Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: IMDReader Integration #4923

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/actions/setup-deps/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ inputs:
default: 'seaborn>=0.7.0'
tidynamics:
default: 'tidynamics>=1.0.0'
imdclient:
default: 'imdclient'
Comment on lines +85 to +86
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's going to take time to get imdclient to a stage where the imdclient package does not actually affect MDAnalysis.

Is there a way that we could temporarily (for initial CI testing) install imdclient from a branch or tarball, e.g., in a pip section? Then we could fairly rapidly create a preliminary (unpublished) imdclient package without IMDReader.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By initial CI testing, do you mean "in this PR"?

There's a pip section just below, which should work if you put in the git location for pip install, but also you can just temporarily modify the CI script to do an additional pip install if it's for "testing within the PR itself".

If it's "after merge", this would require more discussion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for right now to bootstrap the PR.

I don't want to merge without a solid conda-forge imdclient package in place.

# pip-installed min dependencies
coverage:
default: 'coverage'
Expand Down Expand Up @@ -131,6 +133,7 @@ runs:
${{ inputs.distopia }}
${{ inputs.gsd }}
${{ inputs.h5py }}
${{ inputs.imdclient }}
${{ inputs.hole2 }}
${{ inputs.joblib }}
${{ inputs.netcdf4 }}
Expand Down
1 change: 1 addition & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
displayName: 'Install tools'
- script: >-
python -m pip install --only-binary=scipy,h5py
imdclient
cython
hypothesis
h5py>=2.10
Expand Down
1 change: 1 addition & 0 deletions maintainer/conda/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies:
- sphinxcontrib-bibtex
- mdaencore
- waterdynamics
- imdclient
- pip:
- mdahole2
- pathsimanalysis
Expand Down
152 changes: 152 additions & 0 deletions package/MDAnalysis/coordinates/IMD.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"""
MDAnalysis IMDReader
^^^^^^^^^^^^^^^^^^^^

.. autoclass:: IMDReader
:members:
:inherited-members:

"""

from MDAnalysis.coordinates import core
from MDAnalysis.lib.util import store_init_arguments
from MDAnalysis.coordinates.util import parse_host_port
from MDAnalysis.coordinates.base import StreamReaderBase

try:
import imdclient
from imdclient.IMDClient import IMDClient

Check warning on line 18 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L18

Added line #L18 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These need test coverage with mocks

except ImportError:
HAS_IMDCLIENT = False

# Allow building doucmnetation without imdclient
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Allow building doucmnetation without imdclient
# Allow building documentation without imdclient

import types

class MockIMDClient:
pass
imdclient = types.ModuleType("imdclient")
imdclient.IMDClient = MockIMDClient

else:
HAS_IMDCLIENT = True

Check warning on line 31 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L31

Added line #L31 was not covered by tests

import logging

logger = logging.getLogger("imdclient.IMDClient")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to MDAnalysis.coordinates.IMDReader



class IMDReader(StreamReaderBase):
Copy link
Member

@IAlibay IAlibay Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'm getting confused about which PR is for which thing. @orbeckst given our discussion earlier this week, and your comment above which I take to be "IMDClient is still in flux", does it not make sense for the IMDReader to exist upstream and then just import it here? (edit: here my intent is "well then you could make releases and it wouldn't be limited to MDA release frequency").

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to split IMDReader from imdclient and make a version of imdclient without IMDReader (which is in the works Becksteinlab/imdclient#54 ). At the same time we are moving what was split off into coordinates.IMD.

Amru is working on both at the moment.

The way IMDReader depends on imdclient is not the problem, and imdclient itself is also pretty stable, it's just that the tests for imdclient have made use of a lot of MDAnalysis/IMDReader for convenience, and we now have to rewrite some of these tests to use bare-bones python.

"""
Reader for IMD protocol packets.

Parameters
----------
filename : a string of the form "host:port" where host is the hostname
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this format checked on __init__

or IP address of the listening GROMACS server and port
is the port number.
n_atoms : int (optional)
number of atoms in the system. defaults to number of atoms
in the topology. don't set this unless you know what you're doing.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
in the topology. don't set this unless you know what you're doing.
in the topology. Don't set this unless you know what you're doing.

kwargs : dict (optional)
keyword arguments passed to the constructed :class:`IMDClient`
"""

format = "IMD"
one_pass = True

@store_init_arguments
def __init__(
self,
filename,
convert_units=True,
n_atoms=None,
**kwargs,
):
if not HAS_IMDCLIENT:
raise ImportError(

Check warning on line 66 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L66

Added line #L66 was not covered by tests
"IMDReader requires the imdclient package. "
"Please install it with 'pip install imdclient'."
)

super(IMDReader, self).__init__(filename, **kwargs)

Check warning on line 71 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L71

Added line #L71 was not covered by tests

self._imdclient = None
logger.debug("IMDReader initializing")

Check warning on line 74 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L73-L74

Added lines #L73 - L74 were not covered by tests

if n_atoms is None:
raise ValueError("IMDReader: n_atoms must be specified")
self.n_atoms = n_atoms

Check warning on line 78 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L77-L78

Added lines #L77 - L78 were not covered by tests

host, port = parse_host_port(filename)

Check warning on line 80 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L80

Added line #L80 was not covered by tests

# This starts the simulation
self._imdclient = IMDClient(host, port, n_atoms, **kwargs)

Check warning on line 83 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L83

Added line #L83 was not covered by tests

imdsinfo = self._imdclient.get_imdsessioninfo()

Check warning on line 85 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L85

Added line #L85 was not covered by tests
# NOTE: after testing phase, fail out on IMDv2

self.ts = self._Timestep(

Check warning on line 88 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L88

Added line #L88 was not covered by tests
self.n_atoms,
positions=imdsinfo.positions,
velocities=imdsinfo.velocities,
forces=imdsinfo.forces,
**self._ts_kwargs,
)

self._frame = -1

Check warning on line 96 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L96

Added line #L96 was not covered by tests

try:
self._read_next_timestep()
except StopIteration:
raise RuntimeError("IMDReader: No data found in stream")

Check warning on line 101 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L98-L101

Added lines #L98 - L101 were not covered by tests

def _read_frame(self, frame):

try:
imdf = self._imdclient.get_imdframe()
except EOFError as e:
raise e

Check warning on line 108 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L105-L108

Added lines #L105 - L108 were not covered by tests

self._frame = frame
self._load_imdframe_into_ts(imdf)

Check warning on line 111 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L110-L111

Added lines #L110 - L111 were not covered by tests

logger.debug(f"IMDReader: Loaded frame {self._frame}")
return self.ts

Check warning on line 114 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L113-L114

Added lines #L113 - L114 were not covered by tests

def _load_imdframe_into_ts(self, imdf):
self.ts.frame = self._frame

Check warning on line 117 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L117

Added line #L117 was not covered by tests
if imdf.time is not None:
self.ts.time = imdf.time

Check warning on line 119 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L119

Added line #L119 was not covered by tests
# NOTE: timestep.pyx "dt" method is suspicious bc it uses "new" keyword for a float
self.ts.data["dt"] = imdf.dt
self.ts.data["step"] = imdf.step

Check warning on line 122 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L121-L122

Added lines #L121 - L122 were not covered by tests
if imdf.energies is not None:
self.ts.data.update(

Check warning on line 124 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L124

Added line #L124 was not covered by tests
{k: v for k, v in imdf.energies.items() if k != "step"}
)
if imdf.box is not None:
self.ts.dimensions = core.triclinic_box(*imdf.box)

Check warning on line 128 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L128

Added line #L128 was not covered by tests
if imdf.positions is not None:
# must call copy because reference is expected to reset
# see 'test_frame_collect_all_same' in MDAnalysisTests.coordinates.base
self.ts.positions = imdf.positions

Check warning on line 132 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L132

Added line #L132 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use np.copyto here and below

if imdf.velocities is not None:
self.ts.velocities = imdf.velocities

Check warning on line 134 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L134

Added line #L134 was not covered by tests
if imdf.forces is not None:
self.ts.forces = imdf.forces

Check warning on line 136 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L136

Added line #L136 was not covered by tests

@staticmethod
def _format_hint(thing):
try:
parse_host_port(thing)
except:
return False
return HAS_IMDCLIENT and True

Check warning on line 144 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L144

Added line #L144 was not covered by tests

def close(self):
"""Gracefully shut down the reader. Stops the producer thread."""
logger.debug("IMDReader close() called")

Check warning on line 148 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L148

Added line #L148 was not covered by tests
if self._imdclient is not None:
self._imdclient.stop()

Check warning on line 150 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L150

Added line #L150 was not covered by tests
# NOTE: removeme after testing
logger.debug("IMDReader shut down gracefully.")

Check warning on line 152 in package/MDAnalysis/coordinates/IMD.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/IMD.py#L152

Added line #L152 was not covered by tests
1 change: 1 addition & 0 deletions package/MDAnalysis/coordinates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ class can choose an appropriate reader automatically.
from . import DMS
from . import GMS
from . import GRO
from . import IMD
from . import INPCRD
from . import LAMMPS
from . import MOL2
Expand Down
191 changes: 191 additions & 0 deletions package/MDAnalysis/coordinates/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1841,3 +1841,194 @@

def convert(self, obj):
raise NotImplementedError

class StreamReaderBase(ReaderBase):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think coordinates.base is the right place. @hmacdope ??

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep correct!


def __init__(self, filename, convert_units=True, **kwargs):
super(StreamReaderBase, self).__init__(

Check warning on line 1848 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1848

Added line #L1848 was not covered by tests
filename, convert_units=convert_units, **kwargs
)
self._init_scope = True
self._reopen_called = False
self._first_ts = None

Check warning on line 1853 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1851-L1853

Added lines #L1851 - L1853 were not covered by tests

def _read_next_timestep(self):
# No rewinding- to both load the first frame after __init__
# and access it again during iteration, we need to store first ts in mem
if not self._init_scope and self._frame == -1:
self._frame += 1

Check warning on line 1859 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1859

Added line #L1859 was not covered by tests
# can't simply return the same ts again- transformations would be applied twice
# instead, return the pre-transformed copy
return self._first_ts

Check warning on line 1862 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1862

Added line #L1862 was not covered by tests

ts = self._read_frame(self._frame + 1)

Check warning on line 1864 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1864

Added line #L1864 was not covered by tests

if self._init_scope:
self._first_ts = self.ts.copy()
self._init_scope = False

Check warning on line 1868 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1867-L1868

Added lines #L1867 - L1868 were not covered by tests

return ts

Check warning on line 1870 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1870

Added line #L1870 was not covered by tests

@property
def n_frames(self):
"""Changes as stream is processed unlike other readers"""
raise RuntimeError(

Check warning on line 1875 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1875

Added line #L1875 was not covered by tests
"{}: n_frames is unknown".format(self.__class__.__name__)
)

def __len__(self):
raise RuntimeError(

Check warning on line 1880 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1880

Added line #L1880 was not covered by tests
"{} has unknown length".format(self.__class__.__name__)
)

def next(self):
"""Don't rewind after iteration. When _reopen() is called,
an error will be raised
"""
try:
ts = self._read_next_timestep()
except (EOFError, IOError):

Check warning on line 1890 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1888-L1890

Added lines #L1888 - L1890 were not covered by tests
# Don't rewind here like we normally would
raise StopIteration from None

Check warning on line 1892 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1892

Added line #L1892 was not covered by tests
else:
for auxname, reader in self._auxs.items():
ts = self._auxs[auxname].update_ts(ts)

Check warning on line 1895 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1895

Added line #L1895 was not covered by tests

ts = self._apply_transformations(ts)

Check warning on line 1897 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1897

Added line #L1897 was not covered by tests

return ts

Check warning on line 1899 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1899

Added line #L1899 was not covered by tests

def rewind(self):
"""Raise error on rewind"""
raise RuntimeError(

Check warning on line 1903 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1903

Added line #L1903 was not covered by tests
"{}: Stream-based readers can't be rewound".format(
self.__class__.__name__
)
)

# Incompatible methods
def copy(self):
raise NotImplementedError(
"{} does not support copying".format(self.__class__.__name__)
)

def _reopen(self):
if self._reopen_called:
raise RuntimeError(

Check warning on line 1917 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1917

Added line #L1917 was not covered by tests
"{}: Cannot reopen stream".format(self.__class__.__name__)
)
self._frame = -1
self._reopen_called = True

Check warning on line 1921 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1920-L1921

Added lines #L1920 - L1921 were not covered by tests

def __getitem__(self, frame):
"""Return the Timestep corresponding to *frame*.

If `frame` is a integer then the corresponding frame is
returned. Negative numbers are counted from the end.

If frame is a :class:`slice` then an iterator is returned that
allows iteration over that part of the trajectory.

Note
----
*frame* is a 0-based frame index.
"""
if isinstance(frame, slice):
_, _, step = self.check_slice_indices(

Check warning on line 1937 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1937

Added line #L1937 was not covered by tests
frame.start, frame.stop, frame.step
)
if step is None:
return FrameIteratorAll(self)

Check warning on line 1941 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1941

Added line #L1941 was not covered by tests
else:
return StreamFrameIteratorSliced(self, step)

Check warning on line 1943 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1943

Added line #L1943 was not covered by tests
else:
raise TypeError(

Check warning on line 1945 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1945

Added line #L1945 was not covered by tests
"Streamed trajectories must be an indexed using a slice"
)

def check_slice_indices(self, start, stop, step):
if start is not None:
raise ValueError(

Check warning on line 1951 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1951

Added line #L1951 was not covered by tests
"{}: Cannot expect a start index from a stream, 'start' must be None".format(
self.__class__.__name__
)
)
if stop is not None:
raise ValueError(

Check warning on line 1957 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1957

Added line #L1957 was not covered by tests
"{}: Cannot expect a stop index from a stream, 'stop' must be None".format(
self.__class__.__name__
)
)
if step is not None:
if isinstance(step, numbers.Integral):
if step < 1:
raise ValueError(

Check warning on line 1965 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1965

Added line #L1965 was not covered by tests
"{}: Cannot go backwards in a stream, 'step' must be > 0".format(
self.__class__.__name__
)
)
else:
raise ValueError(

Check warning on line 1971 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1971

Added line #L1971 was not covered by tests
"{}: 'step' must be an integer".format(
self.__class__.__name__
)
)

return start, stop, step

Check warning on line 1977 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L1977

Added line #L1977 was not covered by tests

def __getstate__(self):
raise NotImplementedError(
"{} does not support pickling".format(self.__class__.__name__)
)

def __setstate__(self, state: object):
raise NotImplementedError(
"{} does not support pickling".format(self.__class__.__name__)
)

def __repr__(self):
return (
"<{cls} {fname} with continuous stream of {natoms} atoms>"
"".format(
cls=self.__class__.__name__,
fname=self.filename,
natoms=self.n_atoms,
)
)


class StreamFrameIteratorSliced(FrameIteratorBase):

def __init__(self, trajectory, step):
super().__init__(trajectory)
self._step = step

Check warning on line 2004 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2003-L2004

Added lines #L2003 - L2004 were not covered by tests

def __iter__(self):
# Calling reopen tells reader
# it can't be reopened again
self.trajectory._reopen()
return self

Check warning on line 2010 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2009-L2010

Added lines #L2009 - L2010 were not covered by tests

def __next__(self):
try:

Check warning on line 2013 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2013

Added line #L2013 was not covered by tests
# Burn the timesteps until we reach the desired step
# Don't use next() to avoid unnecessary transformations
while self.trajectory._frame + 1 % self.step != 0:
self.trajectory._read_next_timestep()
except (EOFError, IOError):

Check warning on line 2018 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2017-L2018

Added lines #L2017 - L2018 were not covered by tests
# Don't rewind here like we normally would
raise StopIteration from None

Check warning on line 2020 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2020

Added line #L2020 was not covered by tests

return self.trajectory.next()

Check warning on line 2022 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2022

Added line #L2022 was not covered by tests

def __len__(self):
raise RuntimeError(

Check warning on line 2025 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2025

Added line #L2025 was not covered by tests
"{} has unknown length".format(self.__class__.__name__)
)

def __getitem__(self, frame):
raise RuntimeError("Sliced iterator does not support indexing")

Check warning on line 2030 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2030

Added line #L2030 was not covered by tests

@property
def step(self):
return self._step

Check warning on line 2034 in package/MDAnalysis/coordinates/base.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/coordinates/base.py#L2034

Added line #L2034 was not covered by tests
Loading
Loading