diff --git a/package/AUTHORS b/package/AUTHORS index ed19d3ac17..bcd0d427d3 100644 --- a/package/AUTHORS +++ b/package/AUTHORS @@ -287,6 +287,7 @@ Chronological list of authors - Francesco Siciliani - Akshit Boora - Sumit Saraswat + - Nayan Sharma External code diff --git a/package/MDAnalysis/coordinates/LAMMPS.py b/package/MDAnalysis/coordinates/LAMMPS.py index 6e0043321d..1ed1299231 100644 --- a/package/MDAnalysis/coordinates/LAMMPS.py +++ b/package/MDAnalysis/coordinates/LAMMPS.py @@ -223,6 +223,23 @@ def __init__(self, dcdfilename, **kwargs): raise ValueError(f"LAMMPS DCDReader: unknown unit {unit}") super(DCDReader, self).__init__(dcdfilename, **kwargs) + def Writer(self, filename, n_atoms=None, **kwargs): + """Return a DCD writer with the correct LAMMPS time conversion.""" + if n_atoms is None: + n_atoms = self.n_atoms + + dt = units.convert(self.ts.dt, "ps", "fs") / units.convert( + 1.0, "ps", "AKMA" + ) + + return DCD.DCDWriter( + filename, + n_atoms=n_atoms, + dt=dt, + convert_units=self.convert_units, + **kwargs, + ) + class DATAReader(base.SingleFrameReaderBase): """Reads a single frame of coordinate information from a LAMMPS DATA file. diff --git a/testsuite/MDAnalysisTests/coordinates/base.py b/testsuite/MDAnalysisTests/coordinates/base.py index 3a4fa5162b..2bf50cc776 100644 --- a/testsuite/MDAnalysisTests/coordinates/base.py +++ b/testsuite/MDAnalysisTests/coordinates/base.py @@ -858,7 +858,7 @@ def test_write_different_box(self, ref, universe, tmpdir): def test_write_trajectory_atomgroup(self, ref, reader, universe, tmpdir): outfile = "write-atoms-test." + ref.ext with tmpdir.as_cwd(): - with ref.writer(outfile, universe.atoms.n_atoms) as w: + with reader.Writer(outfile) as w: for ts in universe.trajectory: w.write(universe.atoms) self._check_copy(outfile, ref, reader) @@ -866,7 +866,7 @@ def test_write_trajectory_atomgroup(self, ref, reader, universe, tmpdir): def test_write_trajectory_universe(self, ref, reader, universe, tmpdir): outfile = "write-uni-test." + ref.ext with tmpdir.as_cwd(): - with ref.writer(outfile, universe.atoms.n_atoms) as w: + with reader.Writer(outfile) as w: for ts in universe.trajectory: w.write(universe) self._check_copy(outfile, ref, reader) @@ -924,12 +924,12 @@ def test_no_container(self, ref, tmpdir): with pytest.raises(TypeError): ref.writer("foo") - def test_write_not_changing_ts(self, ref, universe, tmpdir): + def test_write_not_changing_ts(self, ref, reader, universe, tmpdir): outfile = "write-not-changing-ts." + ref.ext copy_ts = universe.trajectory.ts.copy() with tmpdir.as_cwd(): - with ref.writer(outfile, n_atoms=5) as W: + with reader.Writer(outfile, n_atoms=universe.atoms.n_atoms) as W: W.write(universe) assert_timestep_almost_equal(copy_ts, universe.trajectory.ts) diff --git a/testsuite/MDAnalysisTests/coordinates/test_lammps.py b/testsuite/MDAnalysisTests/coordinates/test_lammps.py index 3b203f5bae..25106623f9 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_lammps.py +++ b/testsuite/MDAnalysisTests/coordinates/test_lammps.py @@ -28,6 +28,8 @@ import MDAnalysis as mda from MDAnalysis import NoDataError +from MDAnalysis.coordinates.LAMMPS import DCDReader +from MDAnalysis.coordinates.DCD import DCDWriter from numpy.testing import assert_equal, assert_allclose @@ -38,6 +40,11 @@ RefLAMMPSDataDCD, RefLAMMPSDataAdditionalColumns, ) +from MDAnalysisTests.coordinates.base import ( + BaseReference, + BaseReaderTest, + BaseWriterTest, +) from MDAnalysisTests.datafiles import ( LAMMPScnt, LAMMPShyd, @@ -328,7 +335,75 @@ def test_n_angles(self, LAMMPSDATA_partial): # need more tests of the LAMMPS DCDReader -class TestLAMMPSDCDReader(RefLAMMPSDataDCD): +class LAMMPSDCDReference(BaseReference): + def __init__(self): + super().__init__() + self.topology = RefLAMMPSDataDCD.topology + self.trajectory = RefLAMMPSDataDCD.trajectory + self.reader = DCDReader + + self.writer = DCDWriter + self.mean_dimensions = RefLAMMPSDataDCD.mean_dimensions + trajectory = self.reader(self.trajectory) + + self.n_atoms = trajectory.n_atoms + self.n_frames = len(trajectory) + self.dt = trajectory.dt + self.totaltime = trajectory.totaltime + self.aux_lowf_data = [ + [2**0], + [np.nan], + [np.nan], + [np.nan], + [2**1], + ] + self.aux_highf_data = [ + [2**0], + [2**1], + [2**2], + [2**3], + [2**4], + ] + self.ext = "dcd" + + self.first_frame = trajectory[0].copy() + self.second_frame = trajectory[1].copy() + self.last_frame = trajectory[-1].copy() + self.jump_to_frame = trajectory[3].copy() + for i, frame in enumerate( + [ + self.first_frame, + self.second_frame, + self.last_frame, + self.jump_to_frame, + ] + ): + frame.aux.lowf = np.array(self.aux_lowf_data[[0, 1, 4, 3][i]]) + frame.aux.highf = np.array(self.aux_highf_data[[0, 1, 4, 3][i]]) + + self.dimensions = self.first_frame.dimensions.copy() + self.dimensions_second_frame = self.second_frame.dimensions.copy() + self.volume = self.first_frame.volume + + trajectory.close() + + def iter_ts(self, i): + trajectory = self.reader(self.trajectory) + ts = trajectory[i].copy() + trajectory.close() + + ts.aux.lowf = np.array(self.aux_lowf_data[i]) + ts.aux.highf = np.array(self.aux_highf_data[i]) + + return ts + + +class TestLAMMPSDCDReader(BaseReaderTest, RefLAMMPSDataDCD): + @staticmethod + @pytest.fixture(scope="class") + def ref(): + return LAMMPSDCDReference() + flavor = "LAMMPS" @pytest.fixture(scope="class") @@ -409,7 +484,16 @@ def wrong_load(unit="GARBAGE"): wrong_load() -class TestLAMMPSDCDWriter(RefLAMMPSDataDCD): +class TestLAMMPSDCDWriter(BaseWriterTest, RefLAMMPSDataDCD): + @pytest.fixture() + def universe(self): + return mda.Universe(self.topology, self.trajectory, format=self.format) + + @staticmethod + @pytest.fixture(scope="class") + def ref(): + return LAMMPSDCDReference() + flavor = "LAMMPS" @pytest.fixture(scope="class")