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

Adding Appending Functionality method to WriterBase #3863

Draft
wants to merge 5 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
14 changes: 9 additions & 5 deletions package/MDAnalysis/coordinates/CRD.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def Writer(self, filename, **kwargs):
return CRDWriter(filename, **kwargs)


class CRDWriter(base.WriterBase):
class CRDWriter(base.SingleFrameWriterBase):
"""CRD writer that implements the CHARMM CRD coordinate format.

It automatically writes the CHARMM EXT extended format if there
Expand Down Expand Up @@ -148,13 +148,16 @@ class CRDWriter(base.WriterBase):
"NUMATOMS": "{0:5d}\n",
}

def __init__(self, filename, **kwargs):
def __init__(self, filename, n_atoms=None, append=False, **kwargs):
"""
Parameters
----------
filename : str or :class:`~MDAnalysis.lib.util.NamedStream`
name of the output file or a stream

n_atoms : int (optional)
number of atoms in the coordinate file;
append : bool (optional)
append to an existing CRD file; default is to overwrite
extended : bool (optional)
By default, noextended CRD format is used [``False``].
However, extended CRD format can be forced by
Expand All @@ -165,13 +168,14 @@ def __init__(self, filename, **kwargs):
.. versionadded:: 2.2.0
"""

self.filename = util.filename(filename, ext='crd')
filename = util.filename(filename, ext='crd')
super().__init__(filename, n_atoms, append)
self.crd = None

# account for explicit crd format, if requested
self.extended = kwargs.pop("extended", False)

def write(self, selection, frame=None):
def _write_next_frame(self, selection, frame=None):
"""Write selection at current trajectory frame to file.

Parameters
Expand Down
7 changes: 5 additions & 2 deletions package/MDAnalysis/coordinates/DCD.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ class DCDWriter(base.WriterBase):
def __init__(self,
filename,
n_atoms,
append=False,
convert_units=True,
step=1,
dt=1,
Expand All @@ -351,6 +352,9 @@ def __init__(self,
filename of trajectory
n_atoms : int
number of atoms to be written
append : bool (optional)
append to an existing trajectory if True, default is to
overwrite an existing file: ``False``
convert_units : bool (optional)
convert from MDAnalysis units to format specific units
step : int (optional)
Expand All @@ -376,11 +380,10 @@ def __init__(self,
General writer arguments

"""
self.filename = filename
self._convert_units = convert_units
if n_atoms is None:
raise ValueError("n_atoms argument is required")
self.n_atoms = n_atoms
super().__init__(filename, n_atoms, append)
self._file = DCDFile(self.filename, 'w')
self.step = step
self.dt = dt
Expand Down
9 changes: 5 additions & 4 deletions package/MDAnalysis/coordinates/FHIAIMS.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def Writer(self, filename, n_atoms=None, **kwargs):
return FHIAIMSWriter(filename, n_atoms=n_atoms, **kwargs)


class FHIAIMSWriter(base.WriterBase):
class FHIAIMSWriter(base.SingleFrameWriterBase):
"""FHI-AIMS Writer.

Single frame writer for the `FHI-AIMS`_ format. Writes geometry (3D and
Expand All @@ -228,7 +228,8 @@ class FHIAIMSWriter(base.WriterBase):
'box_triclinic': "lattice_vector {box[0]:12.8f} {box[1]:12.8f} {box[2]:12.8f}\nlattice_vector {box[3]:12.8f} {box[4]:12.8f} {box[5]:12.8f}\nlattice_vector {box[6]:12.8f} {box[7]:12.8f} {box[8]:12.8f}\n"
}

def __init__(self, filename, convert_units=True, n_atoms=None, **kwargs):
def __init__(self, filename, convert_units=True, n_atoms=None,
append=False, **kwargs):
"""Set up the FHI-AIMS Writer

Parameters
Expand All @@ -239,8 +240,8 @@ def __init__(self, filename, convert_units=True, n_atoms=None, **kwargs):
number of atoms

"""
self.filename = util.filename(filename, ext='.in', keep=True)
self.n_atoms = n_atoms
filename = util.filename(filename, ext='.in', keep=True)
super().__init__(filename, n_atoms, append)

def _write_next_frame(self, obj):
"""Write selection at current trajectory frame to file.
Expand Down
15 changes: 9 additions & 6 deletions package/MDAnalysis/coordinates/GRO.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def Writer(self, filename, n_atoms=None, **kwargs):
return GROWriter(filename, n_atoms=n_atoms, **kwargs)


class GROWriter(base.WriterBase):
class GROWriter(base.SingleFrameWriterBase):
"""GRO Writer that conforms to the Trajectory API.

Will attempt to write the following information from the topology:
Expand Down Expand Up @@ -311,7 +311,8 @@ class GROWriter(base.WriterBase):
}
fmt['xyz_v'] = fmt['xyz'][:-1] + "{vel[0]:8.4f}{vel[1]:8.4f}{vel[2]:8.4f}\n"

def __init__(self, filename, convert_units=True, n_atoms=None, **kwargs):
def __init__(self, filename, convert_units=True, n_atoms=None,
append=False, **kwargs):
"""Set up a GROWriter with a precision of 3 decimal places.

Parameters
Expand All @@ -326,7 +327,9 @@ def __init__(self, filename, convert_units=True, n_atoms=None, **kwargs):
By default, all the atoms were reindexed to have a atom id starting
from 1. [``True``] However, this behaviour can be turned off by
specifying `reindex` ``=False``.

append : bool (optional)
If ``True``, open file in append mode. [``False``]

Note
----
To use the reindex keyword, user can follow the two examples given
Expand All @@ -344,13 +347,13 @@ def __init__(self, filename, convert_units=True, n_atoms=None, **kwargs):
w.write(u.atoms)

"""
self.filename = util.filename(filename, ext='gro', keep=True)
self.n_atoms = n_atoms
filename = util.filename(filename, ext='gro', keep=True)
super().__init__(filename, n_atoms, append)
self.reindex = kwargs.pop('reindex', True)

self.convert_units = convert_units # convert length and time to base units

def write(self, obj):
def _write_next_frame(self, obj):
"""Write selection at current trajectory frame to file.

Parameters
Expand Down
6 changes: 3 additions & 3 deletions package/MDAnalysis/coordinates/H5MD.py
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,8 @@ class H5MDWriter(base.WriterBase):
@due.dcite(Doi("10.1016/j.cpc.2014.01.018"),
description="Specifications of the H5MD standard",
path=__name__, version='1.1')
def __init__(self, filename, n_atoms, n_frames=None, driver=None,
def __init__(self, filename, n_atoms,
append=False, n_frames=None, driver=None,
convert_units=True, chunks=None, compression=None,
compression_opts=None, positions=True, velocities=True,
forces=True, timeunit=None, lengthunit=None,
Expand All @@ -1060,14 +1061,13 @@ def __init__(self, filename, n_atoms, n_frames=None, driver=None,

if not HAS_H5PY:
raise RuntimeError("H5MDWriter: Please install h5py")
self.filename = filename
if n_atoms == 0:
raise ValueError("H5MDWriter: no atoms in output trajectory")
super().__init__(filename, n_atoms, append)
self._driver = driver
if self._driver == 'mpio':
raise ValueError("H5MDWriter: parallel writing with MPI I/O "
"is not currently supported.")
self.n_atoms = n_atoms
self.n_frames = n_frames
self.chunks = (1, n_atoms, 3) if chunks is None else chunks
if self.chunks is False and self.n_frames is None:
Expand Down
8 changes: 6 additions & 2 deletions package/MDAnalysis/coordinates/MOL2.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,17 +287,21 @@ class MOL2Writer(base.WriterBase):
multiframe = True
units = {'time': None, 'length': 'Angstrom'}

def __init__(self, filename, n_atoms=None, convert_units=True):
def __init__(self, filename, n_atoms=None, append=False,
convert_units=True):
"""Create a new MOL2Writer

Parameters
----------
filename: str
name of output file
append: bool (optional)
If ``True``, append to an existing file. If ``False``, overwrite
the file. Default is ``False``.
convert_units: bool (optional)
units are converted to the MDAnalysis base format; [``True``]
"""
self.filename = filename
super().__init__(filename, n_atoms, append)
self.convert_units = convert_units # convert length and time to base units

self.frames_written = 0
Expand Down
7 changes: 4 additions & 3 deletions package/MDAnalysis/coordinates/NAMDBIN.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def Writer(self, filename, **kwargs):
return NAMDBINWriter(filename, **kwargs)


class NAMDBINWriter(base.WriterBase):
class NAMDBINWriter(base.SingleFrameWriterBase):
"""Writer for NAMD binary coordinate files.


Expand All @@ -100,7 +100,7 @@ class NAMDBINWriter(base.WriterBase):
format = ['COOR', 'NAMDBIN']
units = {'time': None, 'length': 'Angstrom'}

def __init__(self, filename, n_atoms=None, **kwargs):
def __init__(self, filename, n_atoms=None, append=False, **kwargs):
"""
Parameters
----------
Expand All @@ -109,7 +109,8 @@ def __init__(self, filename, n_atoms=None, **kwargs):
n_atoms : int
number of atoms for the output coordinate
"""
self.filename = util.filename(filename)
filename = util.filename(filename)
super().__init__(filename, n_atoms, append)

def _write_next_frame(self, obj):
"""Write information associated with ``obj`` at current frame into
Expand Down
78 changes: 39 additions & 39 deletions package/MDAnalysis/coordinates/PDB.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ def close(self):
self._pdbfile.close()


class PDBWriter(base.WriterBase):
class PDBWriter(base.SingleFrameWriterBase):
"""PDB writer that implements a subset of the `PDB 3.3 standard`_ .

PDB format as used by NAMD/CHARMM: 4-letter resnames and segID are allowed,
Expand Down Expand Up @@ -612,7 +612,8 @@ class PDBWriter(base.WriterBase):

def __init__(self, filename, bonds="conect", n_atoms=None, start=0, step=1,
remarks="Created by PDBWriter",
convert_units=True, multiframe=None, reindex=True):
convert_units=True, multiframe=None, reindex=True,
append=False):
"""Create a new PDBWriter

Parameters
Expand Down Expand Up @@ -643,15 +644,17 @@ def __init__(self, filename, bonds="conect", n_atoms=None, start=0, step=1,
reindex: bool (optional)
If ``True`` (default), the atom serial is set to be consecutive
numbers starting at 1. Else, use the atom id.

append: bool (optional)
If ``True``, append to an existing file. If ``False``, overwrite
the file. [``False``]
"""
# n_atoms = None : dummy keyword argument
# (not used, but Writer() always provides n_atoms as the second argument)

# TODO: - remarks should be a list of lines and written to REMARK
# - additional title keyword could contain line for TITLE

self.filename = filename
super().__init__(filename, n_atoms, append)
# convert length and time to base units
self.convert_units = convert_units
self._multiframe = self.multiframe if multiframe is None else multiframe
Expand Down Expand Up @@ -895,32 +898,7 @@ def _update_frame(self, obj):
# hack...
self.obj = obj
self.ts = obj.universe.trajectory.ts

def write(self, obj):
"""Write object *obj* at current trajectory frame to file.

*obj* can be a selection (i.e. a
:class:`~MDAnalysis.core.groups.AtomGroup`) or a whole
:class:`~MDAnalysis.core.universe.Universe`.

The last letter of the :attr:`~MDAnalysis.core.groups.Atom.segid` is
used as the PDB chainID (but see :meth:`~PDBWriter.ATOM` for
details).

Parameters
----------
obj
The :class:`~MDAnalysis.core.groups.AtomGroup` or
:class:`~MDAnalysis.core.universe.Universe` to write.
"""

self._update_frame(obj)
self._write_pdb_header()
# Issue 105: with write() ONLY write a single frame; use
# write_all_timesteps() to dump everything in one go, or do the
# traditional loop over frames
self._write_next_frame(self.ts, multiframe=self._multiframe)
# END and CONECT records are written when file is being close()d


def write_all_timesteps(self, obj):
"""Write all timesteps associated with *obj* to the PDB file.
Expand Down Expand Up @@ -965,15 +943,15 @@ def write_all_timesteps(self, obj):

for framenumber in range(start, len(traj), step):
traj[framenumber]
self._write_next_frame(self.ts, multiframe=True)
self._write_next_frame(obj, multiframe=True)

# CONECT record is written when the file is being close()d
self.close()

# Set the trajectory to the starting position
traj[start]

def _write_next_frame(self, ts=None, **kwargs):
def _write_next_frame(self, obj, **kwargs):
'''write a new timestep to the PDB file

:Keywords:
Expand All @@ -994,13 +972,17 @@ def _write_next_frame(self, ts=None, **kwargs):
.. versionchanged:: 1.0.0
Renamed from `write_next_timestep` to `_write_next_frame`.
'''
if ts is None:
try:
ts = self.ts
except AttributeError:
errmsg = ("PBDWriter: no coordinate data to write to "
"trajectory file")
raise NoDataError(errmsg) from None

self._update_frame(obj)
self._write_pdb_header()

try:
ts = self.ts
except AttributeError:
errmsg = ("PBDWriter: no coordinate data to write to "
"trajectory file")
raise NoDataError(errmsg) from None

self._check_pdb_coordinates()
self._write_timestep(ts, **kwargs)

Expand Down Expand Up @@ -1360,3 +1342,21 @@ class MultiPDBWriter(PDBWriter):
format = ['PDB', 'ENT']
multiframe = True # For Writer registration
singleframe = False

def write(self, obj, **kwargs):
"""Write current timestep, using the supplied `obj`.

Parameters
----------
obj : :class:`~MDAnalysis.core.groups.AtomGroup` or :class:`~MDAnalysis.core.universe.Universe`
write coordinate information associate with `obj`

Note
----
The size of the `obj` must be the same as the number of atoms provided
when setting up the trajectory.
"""

# overwrite the default behavior of PDBWriter
self._n_frames_written += 1
return self._write_next_frame(obj, multiframe=True, **kwargs)
9 changes: 5 additions & 4 deletions package/MDAnalysis/coordinates/PDBQT.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def Writer(self, filename, **kwargs):
return PDBQTWriter(filename, **kwargs)


class PDBQTWriter(base.WriterBase):
class PDBQTWriter(base.SingleFrameWriterBase):
"""PDBQT writer that implements a subset of the PDB_ 3.2 standard and the PDBQT_ spec.

.. _PDB: http://www.wwpdb.org/documentation/file-format-content/format32/v3.2.html
Expand All @@ -212,14 +212,15 @@ class PDBQTWriter(base.WriterBase):
units = {'time': None, 'length': 'Angstrom'}
pdb_coor_limits = {"min": -999.9995, "max": 9999.9995}

def __init__(self, filename, **kwargs):
self.filename = util.filename(filename, ext='pdbqt')
def __init__(self, filename, n_atoms=None, append=False, **kwargs):
filename = util.filename(filename, ext='pdbqt')
super().__init__(filename, n_atoms, append)
self.pdb = util.anyopen(self.filename, 'w')

def close(self):
self.pdb.close()

def write(self, selection, frame=None):
def _write_next_frame(self, selection, frame=None):
"""Write selection at current trajectory frame to file.

Parameters
Expand Down
Loading