From 7af769c69b93ac4108df8fe85923143647f2ce31 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Fri, 27 Jul 2018 22:10:00 -0500 Subject: [PATCH 01/14] fixed Issue #2001 - support for Tinker periodic boundary box --- package/CHANGELOG | 3 ++- package/MDAnalysis/coordinates/TXYZ.py | 14 ++++++++++- package/MDAnalysis/topology/TXYZParser.py | 13 ++++++++-- .../MDAnalysisTests/coordinates/test_txyz.py | 23 ++++++++++++++++-- .../data/coordinates/new_hexane.arc | 24 +++++++++++++++++++ testsuite/MDAnalysisTests/datafiles.py | 3 ++- 6 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 testsuite/MDAnalysisTests/data/coordinates/new_hexane.arc diff --git a/package/CHANGELOG b/package/CHANGELOG index 05e9b078d1c..3742ad94e2f 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -14,7 +14,7 @@ The rules for this file: ------------------------------------------------------------------------------ ??/??/18 tylerjereddy, richardjgowers, palnabarun, orbeckst, kain88-de, zemanj, - VOD555, davidercruz, jbarnoud, ayushsuhane, hfmull + VOD555, davidercruz, jbarnoud, ayushsuhane, hfmull, micaela-matta * 0.18.1 @@ -68,6 +68,7 @@ Fixes * PDBWriter now properly sets start value * Zero length TopologyGroup now return proper shape array via to_indices (Issue #1974) + * Added periodic boundary box support to the Tinker file reader (Issue #2001) Changes * TopologyAttrs are now statically typed (Issue #1876) diff --git a/package/MDAnalysis/coordinates/TXYZ.py b/package/MDAnalysis/coordinates/TXYZ.py index 260d11d6ead..41580b46692 100644 --- a/package/MDAnalysis/coordinates/TXYZ.py +++ b/package/MDAnalysis/coordinates/TXYZ.py @@ -74,7 +74,15 @@ def __init__(self, filename, **kwargs): root, ext = os.path.splitext(self.filename) self.xyzfile = util.anyopen(self.filename) self._cache = dict() - + with util.openany(self.filename) as inp: + inp.readline() + line=inp.readline() + try: + float(line.split()[1]) + except ValueError: + self.periodic=False + else: + self.periodic=True self.ts = self._Timestep(self.n_atoms, **self._ts_kwargs) # Haven't quite figured out where to start with all the self._reopen() # etc. @@ -103,6 +111,8 @@ def _read_xyz_n_frames(self): # the number of lines in the XYZ file will be 1 greater than the # number of atoms linesPerFrame = self.n_atoms + 1 + if self.periodic: + linesPerFrame +=1 counter = 0 offsets = [] @@ -134,6 +144,8 @@ def _read_next_timestep(self, ts=None): try: # we assume that there is only one header line per frame f.readline() + if self.periodic: + ts.dimensions=f.readline().split() # convert all entries at the end once for optimal speed tmp_buf = [] for i in range(self.n_atoms): diff --git a/package/MDAnalysis/topology/TXYZParser.py b/package/MDAnalysis/topology/TXYZParser.py index 63698602d06..7cd8f93f902 100644 --- a/package/MDAnalysis/topology/TXYZParser.py +++ b/package/MDAnalysis/topology/TXYZParser.py @@ -43,6 +43,7 @@ """ from __future__ import absolute_import +import itertools import numpy as np from . import guessers @@ -88,9 +89,17 @@ def parse(self, **kwargs): names = np.zeros(natoms, dtype=object) types = np.zeros(natoms, dtype=np.int) bonds = [] + #Checks whether file contains periodic boundary box info + fline = inf.readline() + try: + float(fline.split()[1]) + except: + pass + else: + fline = inf.readline() # Can't infinitely read as XYZ files can be multiframe - for i in range(natoms): - line = inf.readline().split() + for i, line in zip(range(natoms),itertools.chain([fline],inf)): + line = line.split() atomids[i]= line[0] names[i] = line[1] types[i] = line[5] diff --git a/testsuite/MDAnalysisTests/coordinates/test_txyz.py b/testsuite/MDAnalysisTests/coordinates/test_txyz.py index 6334352645d..e95a105f9e7 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_txyz.py +++ b/testsuite/MDAnalysisTests/coordinates/test_txyz.py @@ -24,7 +24,7 @@ import pytest from numpy.testing import assert_almost_equal -from MDAnalysisTests.datafiles import TXYZ, ARC +from MDAnalysisTests.datafiles import TXYZ, ARC, ARC_PBC import MDAnalysis as mda @@ -39,6 +39,11 @@ def ARC_U(): return mda.Universe(ARC) +@pytest.fixture +def ARC_PBC_U(): + return mda.Universe(ARC_PBC) + + def test_txyz_positions(TXYZ_U): assert_almost_equal(TXYZ_U.atoms.positions[0], [-6.553398, -1.854369, 0.000000]) @@ -56,5 +61,19 @@ def test_arc_positions_frame_2(ARC_U): [-0.231579, -0.350841, -0.037475]) -def test_arc_traj_legnth(ARC_U): +def test_arc_traj_length(ARC_U): assert len(ARC_U.trajectory) == 2 + + +def test_arcpbc_traj_length(ARC_PBC_U): + assert len(ARC_PBC_U.trajectory) == 3 + + +def test_pbc_boxsize(ARC_PBC_U): + ref_dimensions=[[ 38.860761, 38.860761, 38.860761, 90.000000, 90.000000, 90.000000], + [ 39.860761, 39.860761, 39.860761, 90.000000, 90.000000, 90.000000], + [ 40.860761, 40.860761, 40.860761, 90.000000, 90.000000, 90.000000]] + + for ref_box, ts in zip(ref_dimensions, ARC_PBC_U.trajectory): + assert_almost_equal(ref_box, ts.dimensions, decimal=5) + diff --git a/testsuite/MDAnalysisTests/data/coordinates/new_hexane.arc b/testsuite/MDAnalysisTests/data/coordinates/new_hexane.arc new file mode 100644 index 00000000000..6705ae636a9 --- /dev/null +++ b/testsuite/MDAnalysisTests/data/coordinates/new_hexane.arc @@ -0,0 +1,24 @@ + 6 Hexane (267 OPLS-UA in 38.8 Ang Box; JPC, 100, 14511 '96) + 38.860761 38.860761 38.860761 90.000000 90.000000 90.000000 + 1 CH3 -0.533809 8.893843 -17.718901 83 2 + 2 CH2 -0.492066 8.734009 -16.201869 86 1 3 + 3 CH2 0.609320 7.723360 -15.894925 86 2 4 + 4 CH2 0.723163 7.301395 -14.432851 86 3 5 + 5 CH2 1.923300 6.401842 -14.151512 86 4 6 + 6 CH3 1.576645 4.928146 -14.343148 83 5 + 6 Hexane (test file for MDA) + 39.860761 39.860761 39.860761 90.000000 90.000000 90.000000 + 1 CH3 0.039519 9.187867 -17.899888 83 2 + 2 CH2 -0.171280 8.486514 -16.561104 86 1 3 + 3 CH2 0.988507 7.632303 -16.057222 86 2 4 + 4 CH2 0.630146 7.086837 -14.677831 86 3 5 + 5 CH2 1.729866 6.240985 -14.042356 86 4 6 + 6 CH3 2.065117 4.899261 -14.687383 83 5 + 6 Hexane + 40.860761 40.860761 40.860761 90.000000 90.000000 90.000000 + 1 CH3 -0.533809 8.893843 -17.718901 83 2 + 2 CH2 -0.492066 8.734009 -16.201869 86 1 3 + 3 CH2 0.609320 7.723360 -15.894925 86 2 4 + 4 CH2 0.723163 7.301395 -14.432851 86 3 5 + 5 CH2 1.923300 6.401842 -14.151512 86 4 6 + 6 CH3 1.576645 4.928146 -14.343148 83 5 diff --git a/testsuite/MDAnalysisTests/datafiles.py b/testsuite/MDAnalysisTests/datafiles.py index 3109f100527..c0b79224a08 100644 --- a/testsuite/MDAnalysisTests/datafiles.py +++ b/testsuite/MDAnalysisTests/datafiles.py @@ -85,7 +85,7 @@ "XTC_sub_sol", "XYZ", "XYZ_psf", "XYZ_bz2", "XYZ_mini", "XYZ_five", # 3 and 5 atoms xyzs for an easy topology - "TXYZ", "ARC", # Tinker files + "TXYZ", "ARC", "ARC_PDB", # Tinker files "PRM", "TRJ", "TRJ_bz2", # Amber (no periodic box) "INPCRD", "PRMpbc", "TRJpbc_bz2", # Amber (periodic box) @@ -294,6 +294,7 @@ XYZ_five = resource_filename(__name__, 'data/five.xyz') TXYZ = resource_filename(__name__, 'data/coordinates/test.txyz') ARC = resource_filename(__name__, 'data/coordinates/test.arc') +ARC_PBC = resource_filename(__name__, 'data/coordinates/new_hexane.arc') PRM = resource_filename(__name__, 'data/Amber/ache.prmtop') TRJ = resource_filename(__name__, 'data/Amber/ache.mdcrd') From 776be8302eed8c51bde74be9b06192c5045990f0 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sat, 28 Jul 2018 16:42:42 -0500 Subject: [PATCH 02/14] w.i.p. - tinker writer --- package/MDAnalysis/coordinates/TXYZ.py | 191 +++++++++++++++++++++++-- 1 file changed, 177 insertions(+), 14 deletions(-) diff --git a/package/MDAnalysis/coordinates/TXYZ.py b/package/MDAnalysis/coordinates/TXYZ.py index 41580b46692..6dcc0e806f6 100644 --- a/package/MDAnalysis/coordinates/TXYZ.py +++ b/package/MDAnalysis/coordinates/TXYZ.py @@ -26,7 +26,8 @@ Coordinate reader for Tinker_ xyz files .txyz and trajectory .arc files. Differences between Tinker format_ and normal xyz files: -- there is only one header line containing both the number of atoms and a comment +- the 1st header line contains both the number of atoms and a comment +- the 2nd header line (optional) has periodic boundary conditions information - column 1 contains atom numbers (starting from 1) - column 6 contains atoms types - the following columns indicate connectivity (atoms to which that particular atom is @@ -39,6 +40,10 @@ Classes ------- +.. autoclass:: TXYZWriter + :members: + :inherited-members: + .. autoclass:: TXYZReader :members: :inherited-members: @@ -51,10 +56,168 @@ import os import errno -from ..lib import util from . import base +from ..core import flags +from ..lib import util from ..lib.util import openany, cached +from ..exceptions import NoDataError +from ..version import __version__ + + +class TXYZWriter(base.WriterBase): + """Writes a TXYZ file (single frame) + or an ARC file (multiple frames) + """ + + format = 'TXYZ', 'ARC' + multiframe = True + # these are assumed! + units = {'time': 'ps', 'length': 'Angstrom'} + + def __init__(self, filename, n_atoms=None, atoms=None, convert_units=None, + remark=None, **kwargs): + """Initialize the TXYZ trajectory writer + + Parameters + ---------- + filename: str + filename of trajectory file. If it ends with "gz" then the file + will be gzip-compressed; if it ends with "bz2" it will be bzip2 + compressed. + n_atoms: int (optional) + Number of atoms in trajectory. By default assume that this is None + and that this file is used to store several different models + instead of a single trajectory. If a number is provided each + written TimeStep has to contain the same number of atoms. + atoms: str | list (optional) + Provide atom names: This can be a list of names or an + :class:`AtomGroup`. If none is provided, atoms will + be called 'X' in the output. These atom names will be + used when a trajectory is written from raw + :class:`Timestep` objects which do not contain atom + information. If you write a :class:`AtomGroup` with + :meth:`XYZWriter.write` then atom information is taken + at each step and *atoms* is ignored. + remark: str (optional) + text following n_atoms ("molecule name"). By default writes MDAnalysis + version + """ + self.filename = filename + self.n_atoms = n_atoms + if convert_units is not None: + self.convert_units = convert_units + else: + self.convert_units = flags['convert_lengths'] + self.atomnames = self._get_atomnames(atoms) + default_remark = "Written by {0} (release {1})".format( + self.__class__.__name__, __version__) + self.remark = default_remark if remark is None else remark + # can also be gz, bz2 + self._txyz = util.anyopen(self.filename, 'wt') + + def _get_atomnames(self, atoms): + """Return a list of atom names""" + # Default case + if atoms is None: + return itertools.cycle(('X',)) + # Single atom name provided + elif isinstance(atoms, six.string_types): + return itertools.cycle((atoms,)) + # List of atom names providded + elif isinstance(atoms, list): + return atoms + # AtomGroup or Universe, grab the names else default + # (AtomGroup.atoms just returns AtomGroup) + try: + return atoms.atoms.names + except (AttributeError, NoDataError): + return itertools.cycle(('X',)) + + def close(self): + """Close the trajectory file and finalize the writing""" + if self._txyz is not None: + self._txyz.write("\n") + self._txyz.close() + self._txyz = None + + def write(self, obj): + """Write object `obj` at current trajectory frame to file. + + Atom names in the output are taken from the `obj` or default + to the value of the `atoms` keyword supplied to the + :class:`TXYZWriter` constructor. + + Parameters + ---------- + obj : Universe or AtomGroup + The :class:`~MDAnalysis.core.groups.AtomGroup` or + :class:`~MDAnalysis.core.universe.Universe` to write. + """ + # prepare the Timestep and extract atom names if possible + # (The way it is written it should be possible to write + # trajectories with frames that differ in atom numbers + # but this is not tested.) + try: + atoms = obj.atoms + except AttributeError: + if isinstance(obj, base.Timestep): + ts = obj + else: + raise TypeError("No Timestep found in obj argument") + else: + if hasattr(obj, 'universe'): + # For AtomGroup and children (Residue, ResidueGroup, Segment) + ts_full = obj.universe.trajectory.ts + if ts_full.n_atoms == atoms.n_atoms: + ts = ts_full + else: + # Only populate a time step with the selected atoms. + ts = ts_full.copy_slice(atoms.indices) + elif hasattr(obj, 'trajectory'): + # For Universe only --- get everything + ts = obj.trajectory.ts + # update atom names + self.atomnames = self._get_atomnames(atoms) + + self.write_next_timestep(ts) + + def write_next_timestep(self, ts=None): + """Write coordinate information in *ts* to the trajectory""" + if ts is None: + if not hasattr(self, 'ts'): + raise NoDataError('TXYZWriter: no coordinate data to write to ' + 'trajectory file') + else: + ts = self.ts + + if self.n_atoms is not None: + if self.n_atoms != ts.n_atoms: + raise ValueError('n_atoms keyword was specified indicating ' + 'that this should be a trajectory of the ' + 'same model. But the provided TimeStep has a ' + 'different number ({}) then expected ({})' + ''.format(ts.n_atoms, self.n_atoms)) + else: + if (not isinstance(self.atomnames, itertools.cycle) and + len(self.atomnames) != ts.n_atoms): + logger.info('Trying to write a TimeStep with unkown atoms. ' + 'Expected {}, got {}. Try using "write" if you are ' + 'using "write_next_timestep" directly'.format( + len(self.atomnames), ts.n_atoms)) + self.atomnames = np.array([self.atomnames[0]] * ts.n_atoms) + + if self.convert_units: + coordinates = self.convert_pos_to_native( + ts.positions, inplace=False) + else: + coordinates = ts.positions + self._xyz.write("{0:d} frame {1}\n".format(ts.n_atoms, ts.frame)) + self._xyz.write("{0:10.5f} {1:10.5f} {2:10.5f} {3:10.5f} + {4:10.5f} {5:10.5f} {6:10.5f}\n".format(ts.dimensions)) + for atom, (x, y, z), typ in zip(self.atomnames, coordinates, self.atomtypes): + self._xyz.write("{0!s:>8} {1:10.5f} {2:10.5f} {3:10.5f} {4}" + "".format(atom, x, y, z, typ)) class TXYZReader(base.ReaderBase): """Reads from a TXYZ file""" @@ -72,7 +235,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) + self.txyzfile = util.anyopen(self.filename) self._cache = dict() with util.openany(self.filename) as inp: inp.readline() @@ -103,11 +266,11 @@ def n_atoms(self): @cached('n_frames') def n_frames(self): try: - return self._read_xyz_n_frames() + return self._read_txyz_n_frames() except IOError: return 0 - def _read_xyz_n_frames(self): + def _read_txyz_n_frames(self): # the number of lines in the XYZ file will be 1 greater than the # number of atoms linesPerFrame = self.n_atoms + 1 @@ -130,7 +293,7 @@ def _read_xyz_n_frames(self): return n_frames def _read_frame(self, frame): - self.xyzfile.seek(self._offsets[frame]) + self.txyzfile.seek(self._offsets[frame]) self.ts.frame = frame - 1 # gets +1'd in next return self._read_next_timestep() @@ -139,13 +302,13 @@ def _read_next_timestep(self, ts=None): if ts is None: ts = self.ts - f = self.xyzfile + f = self.txyzfile try: # we assume that there is only one header line per frame f.readline() if self.periodic: - ts.dimensions=f.readline().split() + ts.dimensions=f.readline().split() # convert all entries at the end once for optimal speed tmp_buf = [] for i in range(self.n_atoms): @@ -161,21 +324,21 @@ def _reopen(self): self.open_trajectory() def open_trajectory(self): - if self.xyzfile is not None: + if self.txyzfile is not None: raise IOError( errno.EALREADY, 'TXYZ file already opened', self.filename) - self.xyzfile = util.anyopen(self.filename) + self.txyzfile = util.anyopen(self.filename) # reset ts ts = self.ts ts.frame = -1 - return self.xyzfile + return self.txyzfile def close(self): """Close arc trajectory file if it was open.""" - if self.xyzfile is None: + if self.txyzfile is None: return - self.xyzfile.close() - self.xyzfile = None + self.txyzfile.close() + self.txyzfile = None From af62d15073fe71dad576ecdcfe81d415e42dfef1 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 11:42:47 -0500 Subject: [PATCH 03/14] added tinker writer --- package/MDAnalysis/coordinates/TXYZ.py | 112 ++++++------------------- 1 file changed, 27 insertions(+), 85 deletions(-) diff --git a/package/MDAnalysis/coordinates/TXYZ.py b/package/MDAnalysis/coordinates/TXYZ.py index 6dcc0e806f6..82a89b7861f 100644 --- a/package/MDAnalysis/coordinates/TXYZ.py +++ b/package/MDAnalysis/coordinates/TXYZ.py @@ -84,20 +84,7 @@ def __init__(self, filename, n_atoms=None, atoms=None, convert_units=None, filename of trajectory file. If it ends with "gz" then the file will be gzip-compressed; if it ends with "bz2" it will be bzip2 compressed. - n_atoms: int (optional) - Number of atoms in trajectory. By default assume that this is None - and that this file is used to store several different models - instead of a single trajectory. If a number is provided each - written TimeStep has to contain the same number of atoms. - atoms: str | list (optional) - Provide atom names: This can be a list of names or an - :class:`AtomGroup`. If none is provided, atoms will - be called 'X' in the output. These atom names will be - used when a trajectory is written from raw - :class:`Timestep` objects which do not contain atom - information. If you write a :class:`AtomGroup` with - :meth:`XYZWriter.write` then atom information is taken - at each step and *atoms* is ignored. + remark: str (optional) text following n_atoms ("molecule name"). By default writes MDAnalysis version @@ -108,31 +95,13 @@ def __init__(self, filename, n_atoms=None, atoms=None, convert_units=None, self.convert_units = convert_units else: self.convert_units = flags['convert_lengths'] - self.atomnames = self._get_atomnames(atoms) + default_remark = "Written by {0} (release {1})".format( self.__class__.__name__, __version__) self.remark = default_remark if remark is None else remark # can also be gz, bz2 self._txyz = util.anyopen(self.filename, 'wt') - def _get_atomnames(self, atoms): - """Return a list of atom names""" - # Default case - if atoms is None: - return itertools.cycle(('X',)) - # Single atom name provided - elif isinstance(atoms, six.string_types): - return itertools.cycle((atoms,)) - # List of atom names providded - elif isinstance(atoms, list): - return atoms - # AtomGroup or Universe, grab the names else default - # (AtomGroup.atoms just returns AtomGroup) - try: - return atoms.atoms.names - except (AttributeError, NoDataError): - return itertools.cycle(('X',)) - def close(self): """Close the trajectory file and finalize the writing""" if self._txyz is not None: @@ -153,58 +122,26 @@ def write(self, obj): The :class:`~MDAnalysis.core.groups.AtomGroup` or :class:`~MDAnalysis.core.universe.Universe` to write. """ - # prepare the Timestep and extract atom names if possible - # (The way it is written it should be possible to write - # trajectories with frames that differ in atom numbers - # but this is not tested.) - try: - atoms = obj.atoms - except AttributeError: - if isinstance(obj, base.Timestep): - ts = obj - else: - raise TypeError("No Timestep found in obj argument") + # information needed: + # timestep if writing a trajectory + # atom numbers, names, positions, types, connectivity + + atoms = obj.atoms + if not hasattr(atoms, 'bonds'): + raise NoDataError('TXYZWriter requires bond information! ' + 'provide a topology file or use guess_bonds ' + 'before writing the current trajectory in ' + 'Tinker format.' + + if self.n_atoms is None: + self.n_atoms = atoms.n_atoms else: - if hasattr(obj, 'universe'): - # For AtomGroup and children (Residue, ResidueGroup, Segment) - ts_full = obj.universe.trajectory.ts - if ts_full.n_atoms == atoms.n_atoms: - ts = ts_full - else: - # Only populate a time step with the selected atoms. - ts = ts_full.copy_slice(atoms.indices) - elif hasattr(obj, 'trajectory'): - # For Universe only --- get everything - ts = obj.trajectory.ts - # update atom names - self.atomnames = self._get_atomnames(atoms) - - self.write_next_timestep(ts) - - def write_next_timestep(self, ts=None): - """Write coordinate information in *ts* to the trajectory""" - if ts is None: - if not hasattr(self, 'ts'): - raise NoDataError('TXYZWriter: no coordinate data to write to ' - 'trajectory file') - else: - ts = self.ts - - if self.n_atoms is not None: - if self.n_atoms != ts.n_atoms: + if self.n_atoms != atoms.n_atoms: raise ValueError('n_atoms keyword was specified indicating ' 'that this should be a trajectory of the ' 'same model. But the provided TimeStep has a ' 'different number ({}) then expected ({})' ''.format(ts.n_atoms, self.n_atoms)) - else: - if (not isinstance(self.atomnames, itertools.cycle) and - len(self.atomnames) != ts.n_atoms): - logger.info('Trying to write a TimeStep with unkown atoms. ' - 'Expected {}, got {}. Try using "write" if you are ' - 'using "write_next_timestep" directly'.format( - len(self.atomnames), ts.n_atoms)) - self.atomnames = np.array([self.atomnames[0]] * ts.n_atoms) if self.convert_units: coordinates = self.convert_pos_to_native( @@ -212,12 +149,17 @@ def write_next_timestep(self, ts=None): else: coordinates = ts.positions - self._xyz.write("{0:d} frame {1}\n".format(ts.n_atoms, ts.frame)) - self._xyz.write("{0:10.5f} {1:10.5f} {2:10.5f} {3:10.5f} - {4:10.5f} {5:10.5f} {6:10.5f}\n".format(ts.dimensions)) - for atom, (x, y, z), typ in zip(self.atomnames, coordinates, self.atomtypes): - self._xyz.write("{0!s:>8} {1:10.5f} {2:10.5f} {3:10.5f} {4}" - "".format(atom, x, y, z, typ)) + self._txyz.write("{0:d} frame {1} {2}\n".format( + n_atoms, ts.frame, default_remark)) + self._txyz.write("{0:10.5f} {1:10.5f} {2:10.5f} " + "{3:10.5f} {4:10.5f} {5:10.5f} \n".format(ts.dimensions)) + + + for atom, (x, y, z) in zip(self.atoms, coordinates): + self._txyz.write("{0} {1!s:>8} {2:10.5f} {3:10.5f} {4:10.5f} {5}" + .format(atom.ix+1, atom.name, x, y, z, atom.type) + b = " ".join(str(item) for item in (atom.bonded_atoms).indices + 1)) + class TXYZReader(base.ReaderBase): """Reads from a TXYZ file""" From 0c15e5d2da3115ce7501ed7cef8da4e7d620e9aa Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 12:10:52 -0500 Subject: [PATCH 04/14] tests for txyz writer --- .../MDAnalysisTests/coordinates/test_txyz.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/testsuite/MDAnalysisTests/coordinates/test_txyz.py b/testsuite/MDAnalysisTests/coordinates/test_txyz.py index e95a105f9e7..4f83143d15b 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_txyz.py +++ b/testsuite/MDAnalysisTests/coordinates/test_txyz.py @@ -77,3 +77,18 @@ def test_pbc_boxsize(ARC_PBC_U): for ref_box, ts in zip(ref_dimensions, ARC_PBC_U.trajectory): assert_almost_equal(ref_box, ts.dimensions, decimal=5) + +def test_txyz_writer(TXYZ_U): + outfile = os.path.join(str(tmpdir), 'test_write.txyz') + with mda.coordinates.TXYZWriter(outfile, len(TXYZ_U.atoms)) as W: + W.write(TXYZ_U.atoms) + + utest = mda.Universe(outfile) + assert_array_equal(TXYZ_U.atoms.indices, + utest.atoms.indices) + assert_almost_equal(TXYZ_U.atoms.positions, + utest.atoms.positions, 3) + assert_array_equal(TXYZ_U.atoms.types, + utest.atoms.types) + assert_array_equal(TXYZ_U.atoms.bonds.to_indices(), + utest.atoms.bonds.to_indices()) From e8d0af451559bf3d58b0e4a819029604a5586954 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 12:12:17 -0500 Subject: [PATCH 05/14] fix to txyz tests --- testsuite/MDAnalysisTests/coordinates/test_txyz.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testsuite/MDAnalysisTests/coordinates/test_txyz.py b/testsuite/MDAnalysisTests/coordinates/test_txyz.py index 4f83143d15b..3d19cc0e185 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_txyz.py +++ b/testsuite/MDAnalysisTests/coordinates/test_txyz.py @@ -20,9 +20,10 @@ # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # from __future__ import absolute_import - +import os import pytest from numpy.testing import assert_almost_equal +from numpy.testing import assert_array_equal from MDAnalysisTests.datafiles import TXYZ, ARC, ARC_PBC From 2ddf3e1b75215758f8e6adc7e8041af2e01fc8da Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 12:13:18 -0500 Subject: [PATCH 06/14] fix imports --- testsuite/MDAnalysisTests/coordinates/test_txyz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testsuite/MDAnalysisTests/coordinates/test_txyz.py b/testsuite/MDAnalysisTests/coordinates/test_txyz.py index 3d19cc0e185..c5b34f374e8 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_txyz.py +++ b/testsuite/MDAnalysisTests/coordinates/test_txyz.py @@ -25,6 +25,7 @@ from numpy.testing import assert_almost_equal from numpy.testing import assert_array_equal +from MDAnalysisTests import tempdir from MDAnalysisTests.datafiles import TXYZ, ARC, ARC_PBC import MDAnalysis as mda From 663c73f073de089602fda3e3060fee8506515f00 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 12:17:01 -0500 Subject: [PATCH 07/14] tempdir fix --- testsuite/MDAnalysisTests/coordinates/test_txyz.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/testsuite/MDAnalysisTests/coordinates/test_txyz.py b/testsuite/MDAnalysisTests/coordinates/test_txyz.py index c5b34f374e8..bf34e17f685 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_txyz.py +++ b/testsuite/MDAnalysisTests/coordinates/test_txyz.py @@ -22,8 +22,9 @@ from __future__ import absolute_import import os import pytest -from numpy.testing import assert_almost_equal -from numpy.testing import assert_array_equal +from numpy.testing import (assert_equal, + assert_array_almost_equal, + assert_almost_equal) from MDAnalysisTests import tempdir from MDAnalysisTests.datafiles import TXYZ, ARC, ARC_PBC @@ -81,7 +82,8 @@ def test_pbc_boxsize(ARC_PBC_U): def test_txyz_writer(TXYZ_U): - outfile = os.path.join(str(tmpdir), 'test_write.txyz') + with tempdir.TempDir() as tmpdir: + outfile = tmpdir + 'test_write.txyz' with mda.coordinates.TXYZWriter(outfile, len(TXYZ_U.atoms)) as W: W.write(TXYZ_U.atoms) From 4992fc01be1568ca93128ed6f6479ef67352642b Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 12:33:04 -0500 Subject: [PATCH 08/14] fix syntax errors --- package/MDAnalysis/coordinates/TXYZ.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package/MDAnalysis/coordinates/TXYZ.py b/package/MDAnalysis/coordinates/TXYZ.py index 82a89b7861f..5d1db87e4e2 100644 --- a/package/MDAnalysis/coordinates/TXYZ.py +++ b/package/MDAnalysis/coordinates/TXYZ.py @@ -131,7 +131,7 @@ def write(self, obj): raise NoDataError('TXYZWriter requires bond information! ' 'provide a topology file or use guess_bonds ' 'before writing the current trajectory in ' - 'Tinker format.' + 'Tinker format.') if self.n_atoms is None: self.n_atoms = atoms.n_atoms @@ -157,8 +157,8 @@ def write(self, obj): for atom, (x, y, z) in zip(self.atoms, coordinates): self._txyz.write("{0} {1!s:>8} {2:10.5f} {3:10.5f} {4:10.5f} {5}" - .format(atom.ix+1, atom.name, x, y, z, atom.type) - b = " ".join(str(item) for item in (atom.bonded_atoms).indices + 1)) + .format(atom.ix+1, atom.name, x, y, z, atom.type, + b = " ".join(str(item) for item in (atom.bonded_atoms).indices + 1))) class TXYZReader(base.ReaderBase): From 28bd1a1e827633f788bd303df32431114a76ae6f Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 12:38:39 -0500 Subject: [PATCH 09/14] fix path --- testsuite/MDAnalysisTests/coordinates/test_txyz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/MDAnalysisTests/coordinates/test_txyz.py b/testsuite/MDAnalysisTests/coordinates/test_txyz.py index bf34e17f685..de5f07d49a7 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_txyz.py +++ b/testsuite/MDAnalysisTests/coordinates/test_txyz.py @@ -84,7 +84,7 @@ def test_pbc_boxsize(ARC_PBC_U): def test_txyz_writer(TXYZ_U): with tempdir.TempDir() as tmpdir: outfile = tmpdir + 'test_write.txyz' - with mda.coordinates.TXYZWriter(outfile, len(TXYZ_U.atoms)) as W: + with mda.coordinates.TXYZ.TXYZWriter(outfile, len(TXYZ_U.atoms)) as W: W.write(TXYZ_U.atoms) utest = mda.Universe(outfile) From ab7362135263264943e9c4cfcda5fd95c00a0ad3 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 12:54:10 -0500 Subject: [PATCH 10/14] declared ts --- package/MDAnalysis/coordinates/TXYZ.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/coordinates/TXYZ.py b/package/MDAnalysis/coordinates/TXYZ.py index 5d1db87e4e2..047ba99c371 100644 --- a/package/MDAnalysis/coordinates/TXYZ.py +++ b/package/MDAnalysis/coordinates/TXYZ.py @@ -127,6 +127,7 @@ def write(self, obj): # atom numbers, names, positions, types, connectivity atoms = obj.atoms + ts = atoms.ts if not hasattr(atoms, 'bonds'): raise NoDataError('TXYZWriter requires bond information! ' 'provide a topology file or use guess_bonds ' @@ -141,7 +142,8 @@ def write(self, obj): 'that this should be a trajectory of the ' 'same model. But the provided TimeStep has a ' 'different number ({}) then expected ({})' - ''.format(ts.n_atoms, self.n_atoms)) + ''.format(atoms.n_atoms, self.n_atoms)) + if self.convert_units: coordinates = self.convert_pos_to_native( @@ -154,7 +156,6 @@ def write(self, obj): self._txyz.write("{0:10.5f} {1:10.5f} {2:10.5f} " "{3:10.5f} {4:10.5f} {5:10.5f} \n".format(ts.dimensions)) - for atom, (x, y, z) in zip(self.atoms, coordinates): self._txyz.write("{0} {1!s:>8} {2:10.5f} {3:10.5f} {4:10.5f} {5}" .format(atom.ix+1, atom.name, x, y, z, atom.type, From 80ddaad232f083a6f4d6a496e3db31f6eec5b345 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 13:02:38 -0500 Subject: [PATCH 11/14] fix n_atoms and def remark --- package/MDAnalysis/coordinates/TXYZ.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/MDAnalysis/coordinates/TXYZ.py b/package/MDAnalysis/coordinates/TXYZ.py index 047ba99c371..77e0806a4ac 100644 --- a/package/MDAnalysis/coordinates/TXYZ.py +++ b/package/MDAnalysis/coordinates/TXYZ.py @@ -152,7 +152,7 @@ def write(self, obj): coordinates = ts.positions self._txyz.write("{0:d} frame {1} {2}\n".format( - n_atoms, ts.frame, default_remark)) + self.n_atoms, ts.frame, self.remark)) self._txyz.write("{0:10.5f} {1:10.5f} {2:10.5f} " "{3:10.5f} {4:10.5f} {5:10.5f} \n".format(ts.dimensions)) From 9260ce9b820aa6d833595015e58c944af5aecb27 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 13:07:58 -0500 Subject: [PATCH 12/14] fix bonds and pbc format --- package/MDAnalysis/coordinates/TXYZ.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package/MDAnalysis/coordinates/TXYZ.py b/package/MDAnalysis/coordinates/TXYZ.py index 77e0806a4ac..15f408bb4dc 100644 --- a/package/MDAnalysis/coordinates/TXYZ.py +++ b/package/MDAnalysis/coordinates/TXYZ.py @@ -154,10 +154,10 @@ def write(self, obj): self._txyz.write("{0:d} frame {1} {2}\n".format( self.n_atoms, ts.frame, self.remark)) self._txyz.write("{0:10.5f} {1:10.5f} {2:10.5f} " - "{3:10.5f} {4:10.5f} {5:10.5f} \n".format(ts.dimensions)) + "{3:10.5f} {4:10.5f} {5:10.5f}\n".format(*ts.dimensions)) for atom, (x, y, z) in zip(self.atoms, coordinates): - self._txyz.write("{0} {1!s:>8} {2:10.5f} {3:10.5f} {4:10.5f} {5}" + self._txyz.write("{0} {1!s:>8} {2:10.5f} {3:10.5f} {4:10.5f} {5} {b}\n" .format(atom.ix+1, atom.name, x, y, z, atom.type, b = " ".join(str(item) for item in (atom.bonded_atoms).indices + 1))) From df81f0af630345f422f6fe52199937e3b09e2ae7 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 13:10:06 -0500 Subject: [PATCH 13/14] fix atoms --- package/MDAnalysis/coordinates/TXYZ.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package/MDAnalysis/coordinates/TXYZ.py b/package/MDAnalysis/coordinates/TXYZ.py index 15f408bb4dc..9b0d6003276 100644 --- a/package/MDAnalysis/coordinates/TXYZ.py +++ b/package/MDAnalysis/coordinates/TXYZ.py @@ -156,7 +156,7 @@ def write(self, obj): self._txyz.write("{0:10.5f} {1:10.5f} {2:10.5f} " "{3:10.5f} {4:10.5f} {5:10.5f}\n".format(*ts.dimensions)) - for atom, (x, y, z) in zip(self.atoms, coordinates): + for atom, (x, y, z) in zip(atoms, coordinates): self._txyz.write("{0} {1!s:>8} {2:10.5f} {3:10.5f} {4:10.5f} {5} {b}\n" .format(atom.ix+1, atom.name, x, y, z, atom.type, b = " ".join(str(item) for item in (atom.bonded_atoms).indices + 1))) From 9bc985636aad2f3b4d73b4f8781d4ecb09793ac6 Mon Sep 17 00:00:00 2001 From: Micaela Matta Date: Sun, 29 Jul 2018 13:13:12 -0500 Subject: [PATCH 14/14] assert_equal fix --- testsuite/MDAnalysisTests/coordinates/test_txyz.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testsuite/MDAnalysisTests/coordinates/test_txyz.py b/testsuite/MDAnalysisTests/coordinates/test_txyz.py index de5f07d49a7..e37131e16e4 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_txyz.py +++ b/testsuite/MDAnalysisTests/coordinates/test_txyz.py @@ -88,11 +88,11 @@ def test_txyz_writer(TXYZ_U): W.write(TXYZ_U.atoms) utest = mda.Universe(outfile) - assert_array_equal(TXYZ_U.atoms.indices, + assert_equal(TXYZ_U.atoms.indices, utest.atoms.indices) assert_almost_equal(TXYZ_U.atoms.positions, utest.atoms.positions, 3) - assert_array_equal(TXYZ_U.atoms.types, + assert_equal(TXYZ_U.atoms.types, utest.atoms.types) - assert_array_equal(TXYZ_U.atoms.bonds.to_indices(), + assert_equal(TXYZ_U.atoms.bonds.to_indices(), utest.atoms.bonds.to_indices())