Skip to content

Commit

Permalink
Start of Issue #239
Browse files Browse the repository at this point in the history
  • Loading branch information
richardjgowers committed Aug 3, 2016
1 parent 89f094e commit 1b7ed8c
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 6 deletions.
11 changes: 6 additions & 5 deletions package/MDAnalysis/coordinates/XYZ.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def write_next_timestep(self, ts=None):
"".format(atom, x, y, z))


class XYZReader(base.Reader):
class XYZReader(base.NewReader):
"""Reads from an XYZ file
:Data:
Expand Down Expand Up @@ -295,7 +295,7 @@ def __init__(self, filename, **kwargs):
# coordinates::core.py so the last file extension will tell us if it is
# bzipped or not
root, ext = os.path.splitext(self.filename)
self.xyzfile = util.anyopen(self.filename, "r")
#self.xyzfile = util.anyopen(self.filename, "r")
self.compression = ext[1:] if ext[1:] != "xyz" else None
self._cache = dict()

Expand All @@ -304,7 +304,7 @@ def __init__(self, filename, **kwargs):
# etc.
# (Also cannot just use seek() or reset() because that would break
# with urllib2.urlopen() streams)
self._read_next_timestep()
self.next()

@property
@cached('n_atoms')
Expand Down Expand Up @@ -367,8 +367,9 @@ def _read_next_timestep(self, ts=None):
raise EOFError(err)

def _reopen(self):
self.close()
self.open_trajectory()
"""Reposition (the virtual fh) to just before first frame"""
self._update_last_fh_position(0)
self.ts.frame = -1

def open_trajectory(self):
if self.xyzfile is not None:
Expand Down
105 changes: 104 additions & 1 deletion package/MDAnalysis/coordinates/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@

from six.moves import range
import six

from contextlib import contextmanager
import itertools
import os.path
import warnings
Expand All @@ -133,11 +133,46 @@
)
from ..core import flags
from .. import units
from ..lib import util
from ..lib.util import asiterable
from . import core
from .. import NoDataError


class FH(object):
"""Smart file handle object
Remembers last known position as `last_pos`
Provides two context managers:
- from_start
- from_last_position
"""
def __init__(self, filename):
self.filename = filename
# last known position of the file handle
self.last_pos = 0
# where we store the file handle
self.fh = None

@contextmanager
def from_last_position(self):
fh = self.fh = util.anyopen(self.filename, 'r')
fh.seek(self.last_pos)
try:
yield fh
finally:
fh.close()

@contextmanager
def from_start(self):
fh = self.fh = util.anyopen(self.filename, 'r')
try:
yield fh
finally:
fh.close()


class Timestep(object):
"""Timestep data for one frame
Expand Down Expand Up @@ -1330,6 +1365,74 @@ def __del__(self):
self.close()


class NewReader(ProtoReader):
"""Super top secret magical class that fixes all known (and unknown) bugs"""
def __init__(self, filename, convert_units=None, **kwargs):
self.filename = filename
self._fh = FH(filename)

if convert_units is None:
convert_units = flags['convert_lengths']
self.convert_units = convert_units

ts_kwargs = {}
for att in ('dt', 'time_offset'):
try:
val = kwargs[att]
except KeyError:
pass
else:
ts_kwargs[att] = val

self._ts_kwargs = ts_kwargs

def _update_last_fh_position(self, pos):
"""Update the last known position of the file handle"""
self._fh.last_pos = pos

def rewind(self):
self._reopen()
self.next()

def _full_iter(self):
with self._fh.from_start() as self.xyzfile:
while True:
try:
yield self._read_next_timestep()
except (EOFError, IOError):
self.rewind()
raise StopIteration

def _sliced_iter(self, frames):
with self._fh.from_start() as self.xyzfile:
for f in frames:
yield self._read_frame(f)
self.rewind()
raise StopIteration

def _goto_frame(self, i):
with self._fh.from_start() as self.xyzfile:
return self._read_frame(i)

def __iter__(self):
return self._full_iter()

def __getitem__(self, item):
if isinstance(item, int):
return self._goto_frame(item)
elif isinstance(item, (list, np.ndarray)):
return self._sliced_iter(item)
elif isinstance(item, slice): # TODO Fix me!
return self._sliced_iter(item)

def __next__(self):
return self.next()

def next(self):
with self._fh.from_last_position() as self.xyzfile:
return self._read_next_timestep()


class ChainReader(ProtoReader):
"""Reader that concatenates multiple trajectories on the fly.
Expand Down

0 comments on commit 1b7ed8c

Please sign in to comment.