From 324b52b7f9cf8958956c728a6355099c520fda1c Mon Sep 17 00:00:00 2001 From: NAYAN SHARMA Date: Wed, 4 Nov 2026 05:07:44 +0530 Subject: [PATCH 1/8] Fix LAMMPS DCD writer compatibility with base tests --- package/MDAnalysis/coordinates/LAMMPS.py | 18 ++++ testsuite/MDAnalysisTests/coordinates/base.py | 10 +- .../coordinates/test_lammps.py | 92 ++++++++++++++++++- 3 files changed, 114 insertions(+), 6 deletions(-) diff --git a/package/MDAnalysis/coordinates/LAMMPS.py b/package/MDAnalysis/coordinates/LAMMPS.py index 6e0043321d3..9caf5e3cdb9 100644 --- a/package/MDAnalysis/coordinates/LAMMPS.py +++ b/package/MDAnalysis/coordinates/LAMMPS.py @@ -223,6 +223,24 @@ 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 3a4fa5162ba..1418ab30c3f 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,14 @@ 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 3b203f5bae2..3e525cd80ba 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,77 @@ 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 +486,18 @@ 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") From 95d4df8dd00dfdaff077640b5a6c010243799337 Mon Sep 17 00:00:00 2001 From: NAYAN SHARMA Date: Wed, 4 Nov 2026 06:29:45 +0530 Subject: [PATCH 2/8] Apply Black formatting to LAMMPS coordinates --- package/MDAnalysis/coordinates/LAMMPS.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package/MDAnalysis/coordinates/LAMMPS.py b/package/MDAnalysis/coordinates/LAMMPS.py index 9caf5e3cdb9..1ed12992312 100644 --- a/package/MDAnalysis/coordinates/LAMMPS.py +++ b/package/MDAnalysis/coordinates/LAMMPS.py @@ -228,9 +228,8 @@ def Writer(self, filename, n_atoms=None, **kwargs): if n_atoms is None: n_atoms = self.n_atoms - dt = ( - units.convert(self.ts.dt, "ps", "fs") - / units.convert(1.0, "ps", "AKMA") + dt = units.convert(self.ts.dt, "ps", "fs") / units.convert( + 1.0, "ps", "AKMA" ) return DCD.DCDWriter( From 2bee350436c0dbe92970a45404cd97ea6989d88e Mon Sep 17 00:00:00 2001 From: NAYAN SHARMA Date: Wed, 4 Nov 2026 06:30:00 +0530 Subject: [PATCH 3/8] Add Nayan Sharma to AUTHORS --- package/AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/package/AUTHORS b/package/AUTHORS index ed19d3ac171..bcd0d427d33 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 From e492c5ee55af4a9bc8416cbd6e4631944a6862a8 Mon Sep 17 00:00:00 2001 From: NAYAN SHARMA Date: Wed, 4 Nov 2026 06:35:12 +0530 Subject: [PATCH 4/8] Apply Black formatting to coordinate tests --- package/MDAnalysis/coordinates/LAMMPS.py | 57 +++------ testsuite/MDAnalysisTests/coordinates/base.py | 93 ++++----------- .../coordinates/test_lammps.py | 112 +++++------------- 3 files changed, 70 insertions(+), 192 deletions(-) diff --git a/package/MDAnalysis/coordinates/LAMMPS.py b/package/MDAnalysis/coordinates/LAMMPS.py index 1ed12992312..7f26946c306 100644 --- a/package/MDAnalysis/coordinates/LAMMPS.py +++ b/package/MDAnalysis/coordinates/LAMMPS.py @@ -228,9 +228,7 @@ def Writer(self, filename, n_atoms=None, **kwargs): if n_atoms is None: n_atoms = self.n_atoms - dt = units.convert(self.ts.dt, "ps", "fs") / units.convert( - 1.0, "ps", "AKMA" - ) + dt = units.convert(self.ts.dt, "ps", "fs") / units.convert(1.0, "ps", "AKMA") return DCD.DCDWriter( filename, @@ -270,9 +268,7 @@ def _read_first_frame(self): if self.convert_units: self.convert_pos_from_native(self.ts._pos) # in-place ! try: - self.convert_velocities_from_native( - self.ts._velocities - ) # in-place ! + self.convert_velocities_from_native(self.ts._velocities) # in-place ! except AttributeError: pass @@ -347,9 +343,7 @@ def _write_atoms(self, atoms, data): moltags = data.get("molecule_tag", np.zeros(len(atoms), dtype=int)) if self.convert_units: - coordinates = self.convert_pos_to_native( - atoms.positions, inplace=False - ) + coordinates = self.convert_pos_to_native(atoms.positions, inplace=False) if has_charges: for index, moltag, atype, charge, coords in zip( @@ -374,9 +368,7 @@ def _write_velocities(self, atoms): self.f.write("Velocities\n") self.f.write("\n") indices = atoms.indices + 1 - velocities = self.convert_velocities_to_native( - atoms.velocities, inplace=False - ) + velocities = self.convert_velocities_to_native(atoms.velocities, inplace=False) for index, vel in zip(indices, velocities): self.f.write( "{i:d} {x:.10f} {y:.10f} {z:.10f}\n".format( @@ -393,9 +385,7 @@ def _write_masses(self, atoms): for atype in range(1, max_type + 1): # search entire universe for mass info, not just writing selection masses = set( - atoms.universe.atoms.select_atoms( - "type {:d}".format(atype) - ).masses + atoms.universe.atoms.select_atoms("type {:d}".format(atype)).masses ) if len(masses) == 0: mass_dict[atype] = 1.0 @@ -488,10 +478,7 @@ def write(self, selection, frame=None): try: atoms.types.astype(np.int32) except ValueError: - errmsg = ( - "LAMMPS.DATAWriter: atom types must be convertible to " - "integers" - ) + errmsg = "LAMMPS.DATAWriter: atom types must be convertible to " "integers" raise ValueError(errmsg) from None try: @@ -516,24 +503,18 @@ def write(self, selection, frame=None): for btype, attr_name in attrs: features[btype] = atoms.__getattribute__(attr_name) - self.f.write( - "{:>12d} {}\n".format(len(features[btype]), attr_name) - ) + self.f.write("{:>12d} {}\n".format(len(features[btype]), attr_name)) features[btype] = features[btype].atomgroup_intersection( atoms, strict=True ) self.f.write("\n") self.f.write( - "{:>12d} atom types\n".format( - max(atoms.types.astype(np.int32)) - ) + "{:>12d} atom types\n".format(max(atoms.types.astype(np.int32))) ) for btype, attr in features.items(): - self.f.write( - "{:>12d} {} types\n".format(len(attr.types()), btype) - ) + self.f.write("{:>12d} {} types\n".format(len(attr.types()), btype)) self._write_dimensions(atoms.dimensions) @@ -840,9 +821,7 @@ def _read_next_timestep(self): try: # this will automatically select in order of priority # unscaled, scaled, unwrapped, scaled_unwrapped - self.lammps_coordinate_convention = list(convention_to_col_ix)[ - 0 - ] + self.lammps_coordinate_convention = list(convention_to_col_ix)[0] except IndexError: raise ValueError("No coordinate information detected") elif not self.lammps_coordinate_convention in convention_to_col_ix: @@ -869,9 +848,7 @@ def _read_next_timestep(self): "Some of the additional columns are not present " "in the file, they will be ignored" ) - additional_keys = [ - key for key in self._additional_columns if key in attrs - ] + additional_keys = [key for key in self._additional_columns if key in attrs] else: additional_keys = [] for key in additional_keys: @@ -882,9 +859,7 @@ def _read_next_timestep(self): fields = f.readline().split() if ids: indices[i] = fields[attr_to_col_ix["id"]] - coords = np.array( - [fields[dim] for dim in coord_cols], dtype=np.float32 - ) + coords = np.array([fields[dim] for dim in coord_cols], dtype=np.float32) if self._unwrap: images = coords[3:] @@ -901,9 +876,7 @@ def _read_next_timestep(self): # Collect additional cols for attribute_key in additional_keys: - ts.data[attribute_key][i] = fields[ - attr_to_col_ix[attribute_key] - ] + ts.data[attribute_key][i] = fields[attr_to_col_ix[attribute_key]] order = np.argsort(indices) ts.positions = ts.positions[order] @@ -918,9 +891,7 @@ def _read_next_timestep(self): if self.lammps_coordinate_convention.startswith("scaled"): # if coordinates are given in scaled format, undo that - ts.positions = distances.transform_StoR( - ts.positions, ts.dimensions - ) + ts.positions = distances.transform_StoR(ts.positions, ts.dimensions) # Transform to origin after transformation of scaled variables ts.positions -= np.array([xlo, ylo, zlo])[None, :] diff --git a/testsuite/MDAnalysisTests/coordinates/base.py b/testsuite/MDAnalysisTests/coordinates/base.py index 1418ab30c3f..43f538216e8 100644 --- a/testsuite/MDAnalysisTests/coordinates/base.py +++ b/testsuite/MDAnalysisTests/coordinates/base.py @@ -69,9 +69,7 @@ def test_n_atoms(self): ) def test_numres(self): - assert_equal( - self.universe.atoms.n_residues, 214, "wrong number of residues" - ) + assert_equal(self.universe.atoms.n_residues, 214, "wrong number of residues") def test_n_frames(self): assert_equal( @@ -81,16 +79,13 @@ def test_n_frames(self): ) def test_time(self): - assert_equal( - self.universe.trajectory.time, 0.0, "wrong time of the frame" - ) + assert_equal(self.universe.trajectory.time, 0.0, "wrong time of the frame") def test_frame(self): assert_equal( self.universe.trajectory.frame, 0, - "wrong frame number (0-based, should be 0 for single frame " - "readers)", + "wrong frame number (0-based, should be 0 for single frame " "readers)", ) def test_frame_index_0(self): @@ -206,9 +201,7 @@ def __init__(self): [2**8], # frame 4 = 4ps = step 8 ] self.aux_highf_n_steps = 10 - self.aux_highf_all_data = [ - [2**i] for i in range(self.aux_highf_n_steps) - ] + self.aux_highf_all_data = [[2**i] for i in range(self.aux_highf_n_steps)] self.aux_offset_by = 0.25 @@ -239,9 +232,7 @@ def __init__(self): self.jump_to_frame.aux.lowf = self.aux_lowf_data[3] self.jump_to_frame.aux.highf = self.aux_highf_data[3] - self.dimensions = np.array( - [81.1, 82.2, 83.3, 75, 80, 85], dtype=np.float32 - ) + self.dimensions = np.array([81.1, 82.2, 83.3, 75, 80, 85], dtype=np.float32) self.dimensions_second_frame = np.array( [82.1, 83.2, 84.3, 75.1, 80.1, 85.1], dtype=np.float32 ) @@ -285,16 +276,12 @@ def reader(ref): @pytest.fixture() def transformed(ref): transformed = ref.reader(ref.trajectory) - transformed.add_transformations( - translate([1, 1, 1]), translate([0, 0, 0.33]) - ) + transformed.add_transformations(translate([1, 1, 1]), translate([0, 0, 0.33])) return transformed def test_add_transformations_rejects_non_callable(self): u = mda.Universe.empty(1, trajectory=True) - with pytest.raises( - TypeError, match="All transformations must be callable" - ): + with pytest.raises(TypeError, match="All transformations must be callable"): u.trajectory.add_transformations([1, 2, 3]) def test_n_atoms(self, ref, reader): @@ -305,9 +292,7 @@ def test_n_frames(self, ref, reader): def test_first_frame(self, ref, reader): reader.rewind() - assert_timestep_almost_equal( - reader.ts, ref.first_frame, decimal=ref.prec - ) + assert_timestep_almost_equal(reader.ts, ref.first_frame, decimal=ref.prec) def test_double_close(self, reader): reader.close() @@ -335,9 +320,7 @@ def test_ts_dt_matches_reader(self, reader): assert_equal(reader.ts.dt, reader.dt) def test_total_time(self, ref, reader): - assert_allclose( - reader.totaltime, ref.totaltime, atol=10 ** (-ref.prec), rtol=0 - ) + assert_allclose(reader.totaltime, ref.totaltime, atol=10 ** (-ref.prec), rtol=0) def test_first_dimensions(self, ref, reader): reader.rewind() @@ -408,8 +391,7 @@ def test_iter_auxiliary(self, ref, reader): ref.aux_highf_all_data[i], atol=1e-7, rtol=0, - err_msg="Auxiliary data does not match for " - "step {}".format(i), + err_msg="Auxiliary data does not match for " "step {}".format(i), ) def test_get_aux_attribute(self, ref, reader): @@ -432,8 +414,7 @@ def test_iter_as_aux_cutoff(self, ref, reader): assert_equal( num_frames, 0, - "iter_as_aux should iterate over 0 frames," - " not {}".format(num_frames), + "iter_as_aux should iterate over 0 frames," " not {}".format(num_frames), ) def test_reload_auxiliaries_from_description(self, ref, reader): @@ -496,9 +477,7 @@ def test_transformations_slice(self, ref, transformed): v2 = np.float32((0, 0, 0.33)) for i, ts in enumerate(transformed[2:3:1]): idealcoords = ref.iter_ts(ts.frame).positions + v1 + v2 - assert_allclose( - ts.positions, idealcoords, atol=10 ** (-ref.prec), rtol=0 - ) + assert_allclose(ts.positions, idealcoords, atol=10 ** (-ref.prec), rtol=0) def test_transformations_switch_frame(self, ref, transformed): # This test checks if the transformations are applied and if the coordinates @@ -572,9 +551,7 @@ def test_transformations_copy(self, ref, transformed): ) for i, ts in enumerate(new): ideal_coords = ref.iter_ts(i).positions + v1 + v2 - assert_allclose( - ts.positions, ideal_coords, atol=10 ** (-ref.prec), rtol=0 - ) + assert_allclose(ts.positions, ideal_coords, atol=10 ** (-ref.prec), rtol=0) def test_add_another_transformations_raises_ValueError(self, transformed): # After defining the transformations, the workflow cannot be changed @@ -584,9 +561,7 @@ def test_add_another_transformations_raises_ValueError(self, transformed): def test_pickle_reader(self, reader): reader_p = pickle.loads(pickle.dumps(reader)) assert_equal(len(reader), len(reader_p)) - assert_equal( - reader.ts, reader_p.ts, "Timestep is changed after pickling" - ) + assert_equal(reader.ts, reader_p.ts, "Timestep is changed after pickling") reader_p_p = pickle.loads(pickle.dumps(reader_p)) assert_equal(len(reader), len(reader_p_p)) assert_equal( @@ -620,9 +595,7 @@ def test_frame_collect_all_same(self, reader): for array in collected_ts: assert_allclose(array, collected_ts[0]) - @pytest.mark.parametrize( - "order", ("fac", "fca", "afc", "acf", "caf", "cfa") - ) + @pytest.mark.parametrize("order", ("fac", "fca", "afc", "acf", "caf", "cfa")) def test_timeseries_shape(self, reader, order): timeseries = reader.timeseries(order=order) a_index = order.index("a") @@ -664,9 +637,7 @@ def test_timeseries_empty_asel(self, reader): UserWarning, match="Empty string to select atoms, empty group returned.", ): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( - None - ) + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms(None) with pytest.raises(ValueError, match="Timeseries requires at least"): reader.timeseries(asel=atoms) @@ -675,33 +646,23 @@ def test_timeseries_empty_atomgroup(self, reader): UserWarning, match="Empty string to select atoms, empty group returned.", ): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( - None - ) + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms(None) with pytest.raises(ValueError, match="Timeseries requires at least"): reader.timeseries(atomgroup=atoms) def test_timeseries_asel_warns_deprecation(self, reader): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( - "index 1" - ) + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms("index 1") with pytest.warns(DeprecationWarning, match="asel argument to"): timeseries = reader.timeseries(asel=atoms, order="fac") def test_timeseries_atomgroup(self, reader): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( - "index 1" - ) + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms("index 1") timeseries = reader.timeseries(atomgroup=atoms, order="fac") def test_timeseries_atomgroup_asel_mutex(self, reader): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( - "index 1" - ) + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms("index 1") with pytest.raises(ValueError, match="Cannot provide both"): - timeseries = reader.timeseries( - atomgroup=atoms, asel=atoms, order="fac" - ) + timeseries = reader.timeseries(atomgroup=atoms, asel=atoms, order="fac") class MultiframeReaderTest(BaseReaderTest): @@ -804,9 +765,7 @@ def test_pickle_last_ts_reader(self, reader): len(reader_p), "Last timestep is changed after pickling", ) - assert_equal( - reader.ts, reader_p.ts, "Last timestep is changed after pickling" - ) + assert_equal(reader.ts, reader_p.ts, "Last timestep is changed after pickling") class BaseWriterTest(object): @@ -929,9 +888,7 @@ def test_write_not_changing_ts(self, ref, reader, universe, tmpdir): copy_ts = universe.trajectory.ts.copy() with tmpdir.as_cwd(): - with reader.Writer( - outfile, n_atoms=universe.atoms.n_atoms - ) 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) @@ -1004,9 +961,7 @@ def assert_timestep_almost_equal(A, B, decimal=6, verbose=True): if A.has_forces != B.has_forces: raise AssertionError( "Only one Timestep has forces:" - "A.has_forces = {}, B.has_forces = {}".format( - A.has_forces, B.has_forces - ) + "A.has_forces = {}, B.has_forces = {}".format(A.has_forces, B.has_forces) ) if A.has_forces: assert_allclose( diff --git a/testsuite/MDAnalysisTests/coordinates/test_lammps.py b/testsuite/MDAnalysisTests/coordinates/test_lammps.py index 3e525cd80ba..e9ddd4acd60 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_lammps.py +++ b/testsuite/MDAnalysisTests/coordinates/test_lammps.py @@ -229,18 +229,14 @@ def test_Writer_atoms_types(self, LAMMPSDATAWriter): err_msg="attributes different after writing", ) - @pytest.mark.parametrize( - "attr", ["bonds", "angles", "dihedrals", "impropers"] - ) + @pytest.mark.parametrize("attr", ["bonds", "angles", "dihedrals", "impropers"]) def test_Writer_atoms(self, attr, LAMMPSDATAWriter): u_ref, u_new = LAMMPSDATAWriter ref = getattr(u_ref.atoms, attr) new = getattr(u_new.atoms, attr) assert ref == new, "attributes different after writing" - @pytest.mark.parametrize( - "attr", ["masses", "charges", "velocities", "positions"] - ) + @pytest.mark.parametrize("attr", ["masses", "charges", "velocities", "positions"]) def test_Writer_numerical_attrs(self, attr, LAMMPSDATAWriter): u_ref, u_new = LAMMPSDATAWriter try: @@ -267,9 +263,7 @@ def test_molecule_tag(self, LAMMPSDATAWriter_molecule_tag): ) -@pytest.mark.parametrize( - "filename", ["out.data", "out.data.bz2", "out.data.gz"] -) +@pytest.mark.parametrize("filename", ["out.data", "out.data.bz2", "out.data.gz"]) def test_datawriter_universe(filename, tmpdir): """ Test roundtrip on datawriter, and also checks compressed files @@ -307,9 +301,7 @@ def LAMMPSDATA_partial(tmpdir): return u, u_new - @pytest.mark.parametrize( - "attr", ["masses", "charges", "velocities", "positions"] - ) + @pytest.mark.parametrize("attr", ["masses", "charges", "velocities", "positions"]) def test_Writer_atoms(self, attr, LAMMPSDATA_partial): u_ref, u_new = LAMMPSDATA_partial if hasattr(u_ref.atoms, attr): @@ -370,18 +362,16 @@ def __init__(self): 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] - ]) + 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() @@ -427,9 +417,7 @@ def test_n_frames(self, u): assert_equal(u.trajectory.n_frames, self.n_frames) def test_dimensions(self, u): - mean_dimensions = np.mean( - [ts.dimensions.copy() for ts in u.trajectory], axis=0 - ) + mean_dimensions = np.mean([ts.dimensions.copy() for ts in u.trajectory], axis=0) assert_allclose(mean_dimensions, self.mean_dimensions) def test_dt(self, u): @@ -444,15 +432,11 @@ def test_Timestep_time(self, u): assert_allclose( u.trajectory[iframe].time, iframe * self.dt, - err_msg="Time for frame {0} (dt={1}) is wrong.".format( - iframe, self.dt - ), + err_msg="Time for frame {0} (dt={1}) is wrong.".format(iframe, self.dt), ) def test_LAMMPSDCDReader_set_dt(self, u, dt=1500.0): - u = mda.Universe( - self.topology, self.trajectory, format=self.format, dt=dt - ) + u = mda.Universe(self.topology, self.trajectory, format=self.format, dt=dt) iframe = self.get_frame_from_end(1, u) assert_allclose( u.trajectory[iframe].time, @@ -489,9 +473,7 @@ def wrong_load(unit="GARBAGE"): class TestLAMMPSDCDWriter(BaseWriterTest, RefLAMMPSDataDCD): @pytest.fixture() def universe(self): - return mda.Universe( - self.topology, self.trajectory, format=self.format - ) + return mda.Universe(self.topology, self.trajectory, format=self.format) @staticmethod @pytest.fixture(scope="class") @@ -507,18 +489,14 @@ def u(self): def test_Writer_is_LAMMPS(self, u, tmpdir): ext = os.path.splitext(self.trajectory)[1] outfile = str(tmpdir.join("lammps-writer-test" + ext)) - with mda.Writer( - outfile, n_atoms=u.atoms.n_atoms, format=self.format - ) as W: + with mda.Writer(outfile, n_atoms=u.atoms.n_atoms, format=self.format) as W: assert W.flavor, self.flavor def test_Writer(self, u, tmpdir, n_frames=3): ext = os.path.splitext(self.trajectory)[1] outfile = str(tmpdir.join("lammps-writer-test" + ext)) - with mda.Writer( - outfile, n_atoms=u.atoms.n_atoms, format=self.format - ) as w: + with mda.Writer(outfile, n_atoms=u.atoms.n_atoms, format=self.format) as w: for ts in u.trajectory[:n_frames]: w.write(u) @@ -594,9 +572,7 @@ def test_open(self, tmpdir): def test_wrong_time_unit(self, tmpdir): outfile = str(tmpdir.join("lammps-writer-test.dcd")) with pytest.raises(TypeError): - with mda.coordinates.LAMMPS.DCDWriter( - outfile, n_atoms=10, timeunit="nm" - ): + with mda.coordinates.LAMMPS.DCDWriter(outfile, n_atoms=10, timeunit="nm"): pass def test_wrong_unit(self, tmpdir): @@ -671,9 +647,7 @@ def u(self, tmpdir, request): with gzip.GzipFile(f, "wb") as fout: fout.write(data) - yield mda.Universe( - f, format="LAMMPSDUMP", lammps_coordinate_convention="auto" - ) + yield mda.Universe(f, format="LAMMPSDUMP", lammps_coordinate_convention="auto") @pytest.fixture() def u_additional_columns_true(self): @@ -791,15 +765,9 @@ def test_seeking(self, u, reference_positions): u.trajectory[1] assert_allclose(u.dimensions, reference_positions["box"][1], atol=1e-5) - pos = ( - reference_positions["atom1_pos"][1] - - reference_positions["mins"][1] - ) + pos = reference_positions["atom1_pos"][1] - reference_positions["mins"][1] assert_allclose(u.atoms[0].position, pos, atol=1e-5) - pos = ( - reference_positions["atom13_pos"][1] - - reference_positions["mins"][1] - ) + pos = reference_positions["atom13_pos"][1] - reference_positions["mins"][1] assert_allclose(u.atoms[12].position, pos, atol=1e-5) def test_boxsize(self, u, reference_positions): @@ -830,9 +798,7 @@ def test_additional_columns(self, system, fields, request): u = request.getfixturevalue(system) for field in fields: data = u.trajectory[0].data[field] - assert_allclose( - data, getattr(RefLAMMPSDataAdditionalColumns, field) - ) + assert_allclose(data, getattr(RefLAMMPSDataAdditionalColumns, field)) @pytest.mark.parametrize( "system", @@ -841,9 +807,7 @@ def test_additional_columns(self, system, fields, request): ], ) def test_wrong_format_additional_colums(self, system, request): - with pytest.raises( - ValueError, match="Please provide an iterable containing" - ): + with pytest.raises(ValueError, match="Please provide an iterable containing"): request.getfixturevalue(system) @pytest.mark.parametrize( @@ -857,9 +821,7 @@ def test_warning(self, system, request): request.getfixturevalue(system) -@pytest.mark.parametrize( - "convention", ["unscaled", "unwrapped", "scaled_unwrapped"] -) +@pytest.mark.parametrize("convention", ["unscaled", "unwrapped", "scaled_unwrapped"]) def test_open_absent_convention_fails(convention): with pytest.raises(ValueError, match="No coordinates following"): mda.Universe( @@ -871,9 +833,7 @@ def test_open_absent_convention_fails(convention): def test_open_incorrect_convention_fails(): with pytest.raises(ValueError, match="is not a valid option"): - mda.Universe( - LAMMPSDUMP, format="LAMMPSDUMP", lammps_coordinate_convention="42" - ) + mda.Universe(LAMMPSDUMP, format="LAMMPSDUMP", lammps_coordinate_convention="42") @pytest.mark.parametrize( @@ -975,9 +935,7 @@ def reference_unwrapped_positions(self): ] ) - def test_unwrapped_scaled_reference( - self, universes, reference_unwrapped_positions - ): + def test_unwrapped_scaled_reference(self, universes, reference_unwrapped_positions): atom_340 = universes["unwrapped"].atoms[339] for i, ts_u in enumerate(universes["unwrapped"].trajectory[0:3]): assert_allclose( @@ -986,14 +944,10 @@ def test_unwrapped_scaled_reference( atol=1e-5, ) - def test_unwrapped_scaled_reference( - self, universes, reference_unwrapped_positions - ): + def test_unwrapped_scaled_reference(self, universes, reference_unwrapped_positions): # NOTE use of unscaled positions here due to S->R transform atom_340 = universes["scaled_unwrapped"].atoms[339] - for i, ts_u in enumerate( - universes["scaled_unwrapped"].trajectory[0:3] - ): + for i, ts_u in enumerate(universes["scaled_unwrapped"].trajectory[0:3]): assert_allclose( atom_340.position, reference_unwrapped_positions[i, :], @@ -1073,8 +1027,6 @@ def test_box(self, u_dump, u_data, reference_box): assert_allclose(ts.dimensions, reference_box, rtol=1e-5, atol=0) for ts in u_dump.trajectory: - assert_allclose( - ts.dimensions, u_data.dimensions, rtol=1e-5, atol=0 - ) + assert_allclose(ts.dimensions, u_data.dimensions, rtol=1e-5, atol=0) assert_allclose(u_data.dimensions, reference_box, rtol=1e-5, atol=0) From 16dc2f55f08859bd164eca2f68aeffe55735c4d9 Mon Sep 17 00:00:00 2001 From: NAYAN SHARMA Date: Wed, 4 Nov 2026 06:41:13 +0530 Subject: [PATCH 5/8] Apply Black formatting to LAMMPS coordinates --- package/MDAnalysis/coordinates/LAMMPS.py | 57 ++++++++++++++++++------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/package/MDAnalysis/coordinates/LAMMPS.py b/package/MDAnalysis/coordinates/LAMMPS.py index 7f26946c306..1ed12992312 100644 --- a/package/MDAnalysis/coordinates/LAMMPS.py +++ b/package/MDAnalysis/coordinates/LAMMPS.py @@ -228,7 +228,9 @@ def Writer(self, filename, n_atoms=None, **kwargs): if n_atoms is None: n_atoms = self.n_atoms - dt = units.convert(self.ts.dt, "ps", "fs") / units.convert(1.0, "ps", "AKMA") + dt = units.convert(self.ts.dt, "ps", "fs") / units.convert( + 1.0, "ps", "AKMA" + ) return DCD.DCDWriter( filename, @@ -268,7 +270,9 @@ def _read_first_frame(self): if self.convert_units: self.convert_pos_from_native(self.ts._pos) # in-place ! try: - self.convert_velocities_from_native(self.ts._velocities) # in-place ! + self.convert_velocities_from_native( + self.ts._velocities + ) # in-place ! except AttributeError: pass @@ -343,7 +347,9 @@ def _write_atoms(self, atoms, data): moltags = data.get("molecule_tag", np.zeros(len(atoms), dtype=int)) if self.convert_units: - coordinates = self.convert_pos_to_native(atoms.positions, inplace=False) + coordinates = self.convert_pos_to_native( + atoms.positions, inplace=False + ) if has_charges: for index, moltag, atype, charge, coords in zip( @@ -368,7 +374,9 @@ def _write_velocities(self, atoms): self.f.write("Velocities\n") self.f.write("\n") indices = atoms.indices + 1 - velocities = self.convert_velocities_to_native(atoms.velocities, inplace=False) + velocities = self.convert_velocities_to_native( + atoms.velocities, inplace=False + ) for index, vel in zip(indices, velocities): self.f.write( "{i:d} {x:.10f} {y:.10f} {z:.10f}\n".format( @@ -385,7 +393,9 @@ def _write_masses(self, atoms): for atype in range(1, max_type + 1): # search entire universe for mass info, not just writing selection masses = set( - atoms.universe.atoms.select_atoms("type {:d}".format(atype)).masses + atoms.universe.atoms.select_atoms( + "type {:d}".format(atype) + ).masses ) if len(masses) == 0: mass_dict[atype] = 1.0 @@ -478,7 +488,10 @@ def write(self, selection, frame=None): try: atoms.types.astype(np.int32) except ValueError: - errmsg = "LAMMPS.DATAWriter: atom types must be convertible to " "integers" + errmsg = ( + "LAMMPS.DATAWriter: atom types must be convertible to " + "integers" + ) raise ValueError(errmsg) from None try: @@ -503,18 +516,24 @@ def write(self, selection, frame=None): for btype, attr_name in attrs: features[btype] = atoms.__getattribute__(attr_name) - self.f.write("{:>12d} {}\n".format(len(features[btype]), attr_name)) + self.f.write( + "{:>12d} {}\n".format(len(features[btype]), attr_name) + ) features[btype] = features[btype].atomgroup_intersection( atoms, strict=True ) self.f.write("\n") self.f.write( - "{:>12d} atom types\n".format(max(atoms.types.astype(np.int32))) + "{:>12d} atom types\n".format( + max(atoms.types.astype(np.int32)) + ) ) for btype, attr in features.items(): - self.f.write("{:>12d} {} types\n".format(len(attr.types()), btype)) + self.f.write( + "{:>12d} {} types\n".format(len(attr.types()), btype) + ) self._write_dimensions(atoms.dimensions) @@ -821,7 +840,9 @@ def _read_next_timestep(self): try: # this will automatically select in order of priority # unscaled, scaled, unwrapped, scaled_unwrapped - self.lammps_coordinate_convention = list(convention_to_col_ix)[0] + self.lammps_coordinate_convention = list(convention_to_col_ix)[ + 0 + ] except IndexError: raise ValueError("No coordinate information detected") elif not self.lammps_coordinate_convention in convention_to_col_ix: @@ -848,7 +869,9 @@ def _read_next_timestep(self): "Some of the additional columns are not present " "in the file, they will be ignored" ) - additional_keys = [key for key in self._additional_columns if key in attrs] + additional_keys = [ + key for key in self._additional_columns if key in attrs + ] else: additional_keys = [] for key in additional_keys: @@ -859,7 +882,9 @@ def _read_next_timestep(self): fields = f.readline().split() if ids: indices[i] = fields[attr_to_col_ix["id"]] - coords = np.array([fields[dim] for dim in coord_cols], dtype=np.float32) + coords = np.array( + [fields[dim] for dim in coord_cols], dtype=np.float32 + ) if self._unwrap: images = coords[3:] @@ -876,7 +901,9 @@ def _read_next_timestep(self): # Collect additional cols for attribute_key in additional_keys: - ts.data[attribute_key][i] = fields[attr_to_col_ix[attribute_key]] + ts.data[attribute_key][i] = fields[ + attr_to_col_ix[attribute_key] + ] order = np.argsort(indices) ts.positions = ts.positions[order] @@ -891,7 +918,9 @@ def _read_next_timestep(self): if self.lammps_coordinate_convention.startswith("scaled"): # if coordinates are given in scaled format, undo that - ts.positions = distances.transform_StoR(ts.positions, ts.dimensions) + ts.positions = distances.transform_StoR( + ts.positions, ts.dimensions + ) # Transform to origin after transformation of scaled variables ts.positions -= np.array([xlo, ylo, zlo])[None, :] From 2efe51445ec5113965bc71a444106cb028d1af6f Mon Sep 17 00:00:00 2001 From: NAYAN SHARMA Date: Wed, 4 Nov 2026 06:49:04 +0530 Subject: [PATCH 6/8] Apply Black formatting to LAMMPS coordinates --- package/MDAnalysis/coordinates/LAMMPS.py | 57 ++++++------------------ 1 file changed, 14 insertions(+), 43 deletions(-) diff --git a/package/MDAnalysis/coordinates/LAMMPS.py b/package/MDAnalysis/coordinates/LAMMPS.py index 1ed12992312..7f26946c306 100644 --- a/package/MDAnalysis/coordinates/LAMMPS.py +++ b/package/MDAnalysis/coordinates/LAMMPS.py @@ -228,9 +228,7 @@ def Writer(self, filename, n_atoms=None, **kwargs): if n_atoms is None: n_atoms = self.n_atoms - dt = units.convert(self.ts.dt, "ps", "fs") / units.convert( - 1.0, "ps", "AKMA" - ) + dt = units.convert(self.ts.dt, "ps", "fs") / units.convert(1.0, "ps", "AKMA") return DCD.DCDWriter( filename, @@ -270,9 +268,7 @@ def _read_first_frame(self): if self.convert_units: self.convert_pos_from_native(self.ts._pos) # in-place ! try: - self.convert_velocities_from_native( - self.ts._velocities - ) # in-place ! + self.convert_velocities_from_native(self.ts._velocities) # in-place ! except AttributeError: pass @@ -347,9 +343,7 @@ def _write_atoms(self, atoms, data): moltags = data.get("molecule_tag", np.zeros(len(atoms), dtype=int)) if self.convert_units: - coordinates = self.convert_pos_to_native( - atoms.positions, inplace=False - ) + coordinates = self.convert_pos_to_native(atoms.positions, inplace=False) if has_charges: for index, moltag, atype, charge, coords in zip( @@ -374,9 +368,7 @@ def _write_velocities(self, atoms): self.f.write("Velocities\n") self.f.write("\n") indices = atoms.indices + 1 - velocities = self.convert_velocities_to_native( - atoms.velocities, inplace=False - ) + velocities = self.convert_velocities_to_native(atoms.velocities, inplace=False) for index, vel in zip(indices, velocities): self.f.write( "{i:d} {x:.10f} {y:.10f} {z:.10f}\n".format( @@ -393,9 +385,7 @@ def _write_masses(self, atoms): for atype in range(1, max_type + 1): # search entire universe for mass info, not just writing selection masses = set( - atoms.universe.atoms.select_atoms( - "type {:d}".format(atype) - ).masses + atoms.universe.atoms.select_atoms("type {:d}".format(atype)).masses ) if len(masses) == 0: mass_dict[atype] = 1.0 @@ -488,10 +478,7 @@ def write(self, selection, frame=None): try: atoms.types.astype(np.int32) except ValueError: - errmsg = ( - "LAMMPS.DATAWriter: atom types must be convertible to " - "integers" - ) + errmsg = "LAMMPS.DATAWriter: atom types must be convertible to " "integers" raise ValueError(errmsg) from None try: @@ -516,24 +503,18 @@ def write(self, selection, frame=None): for btype, attr_name in attrs: features[btype] = atoms.__getattribute__(attr_name) - self.f.write( - "{:>12d} {}\n".format(len(features[btype]), attr_name) - ) + self.f.write("{:>12d} {}\n".format(len(features[btype]), attr_name)) features[btype] = features[btype].atomgroup_intersection( atoms, strict=True ) self.f.write("\n") self.f.write( - "{:>12d} atom types\n".format( - max(atoms.types.astype(np.int32)) - ) + "{:>12d} atom types\n".format(max(atoms.types.astype(np.int32))) ) for btype, attr in features.items(): - self.f.write( - "{:>12d} {} types\n".format(len(attr.types()), btype) - ) + self.f.write("{:>12d} {} types\n".format(len(attr.types()), btype)) self._write_dimensions(atoms.dimensions) @@ -840,9 +821,7 @@ def _read_next_timestep(self): try: # this will automatically select in order of priority # unscaled, scaled, unwrapped, scaled_unwrapped - self.lammps_coordinate_convention = list(convention_to_col_ix)[ - 0 - ] + self.lammps_coordinate_convention = list(convention_to_col_ix)[0] except IndexError: raise ValueError("No coordinate information detected") elif not self.lammps_coordinate_convention in convention_to_col_ix: @@ -869,9 +848,7 @@ def _read_next_timestep(self): "Some of the additional columns are not present " "in the file, they will be ignored" ) - additional_keys = [ - key for key in self._additional_columns if key in attrs - ] + additional_keys = [key for key in self._additional_columns if key in attrs] else: additional_keys = [] for key in additional_keys: @@ -882,9 +859,7 @@ def _read_next_timestep(self): fields = f.readline().split() if ids: indices[i] = fields[attr_to_col_ix["id"]] - coords = np.array( - [fields[dim] for dim in coord_cols], dtype=np.float32 - ) + coords = np.array([fields[dim] for dim in coord_cols], dtype=np.float32) if self._unwrap: images = coords[3:] @@ -901,9 +876,7 @@ def _read_next_timestep(self): # Collect additional cols for attribute_key in additional_keys: - ts.data[attribute_key][i] = fields[ - attr_to_col_ix[attribute_key] - ] + ts.data[attribute_key][i] = fields[attr_to_col_ix[attribute_key]] order = np.argsort(indices) ts.positions = ts.positions[order] @@ -918,9 +891,7 @@ def _read_next_timestep(self): if self.lammps_coordinate_convention.startswith("scaled"): # if coordinates are given in scaled format, undo that - ts.positions = distances.transform_StoR( - ts.positions, ts.dimensions - ) + ts.positions = distances.transform_StoR(ts.positions, ts.dimensions) # Transform to origin after transformation of scaled variables ts.positions -= np.array([xlo, ylo, zlo])[None, :] From 713162b2b6c2711ab3a064cd587e1d495816ebff Mon Sep 17 00:00:00 2001 From: NAYAN SHARMA Date: Wed, 4 Nov 2026 06:57:41 +0530 Subject: [PATCH 7/8] Apply Black formatting to LAMMPS coordinates --- package/MDAnalysis/coordinates/LAMMPS.py | 57 ++++++++++++++++++------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/package/MDAnalysis/coordinates/LAMMPS.py b/package/MDAnalysis/coordinates/LAMMPS.py index 7f26946c306..1ed12992312 100644 --- a/package/MDAnalysis/coordinates/LAMMPS.py +++ b/package/MDAnalysis/coordinates/LAMMPS.py @@ -228,7 +228,9 @@ def Writer(self, filename, n_atoms=None, **kwargs): if n_atoms is None: n_atoms = self.n_atoms - dt = units.convert(self.ts.dt, "ps", "fs") / units.convert(1.0, "ps", "AKMA") + dt = units.convert(self.ts.dt, "ps", "fs") / units.convert( + 1.0, "ps", "AKMA" + ) return DCD.DCDWriter( filename, @@ -268,7 +270,9 @@ def _read_first_frame(self): if self.convert_units: self.convert_pos_from_native(self.ts._pos) # in-place ! try: - self.convert_velocities_from_native(self.ts._velocities) # in-place ! + self.convert_velocities_from_native( + self.ts._velocities + ) # in-place ! except AttributeError: pass @@ -343,7 +347,9 @@ def _write_atoms(self, atoms, data): moltags = data.get("molecule_tag", np.zeros(len(atoms), dtype=int)) if self.convert_units: - coordinates = self.convert_pos_to_native(atoms.positions, inplace=False) + coordinates = self.convert_pos_to_native( + atoms.positions, inplace=False + ) if has_charges: for index, moltag, atype, charge, coords in zip( @@ -368,7 +374,9 @@ def _write_velocities(self, atoms): self.f.write("Velocities\n") self.f.write("\n") indices = atoms.indices + 1 - velocities = self.convert_velocities_to_native(atoms.velocities, inplace=False) + velocities = self.convert_velocities_to_native( + atoms.velocities, inplace=False + ) for index, vel in zip(indices, velocities): self.f.write( "{i:d} {x:.10f} {y:.10f} {z:.10f}\n".format( @@ -385,7 +393,9 @@ def _write_masses(self, atoms): for atype in range(1, max_type + 1): # search entire universe for mass info, not just writing selection masses = set( - atoms.universe.atoms.select_atoms("type {:d}".format(atype)).masses + atoms.universe.atoms.select_atoms( + "type {:d}".format(atype) + ).masses ) if len(masses) == 0: mass_dict[atype] = 1.0 @@ -478,7 +488,10 @@ def write(self, selection, frame=None): try: atoms.types.astype(np.int32) except ValueError: - errmsg = "LAMMPS.DATAWriter: atom types must be convertible to " "integers" + errmsg = ( + "LAMMPS.DATAWriter: atom types must be convertible to " + "integers" + ) raise ValueError(errmsg) from None try: @@ -503,18 +516,24 @@ def write(self, selection, frame=None): for btype, attr_name in attrs: features[btype] = atoms.__getattribute__(attr_name) - self.f.write("{:>12d} {}\n".format(len(features[btype]), attr_name)) + self.f.write( + "{:>12d} {}\n".format(len(features[btype]), attr_name) + ) features[btype] = features[btype].atomgroup_intersection( atoms, strict=True ) self.f.write("\n") self.f.write( - "{:>12d} atom types\n".format(max(atoms.types.astype(np.int32))) + "{:>12d} atom types\n".format( + max(atoms.types.astype(np.int32)) + ) ) for btype, attr in features.items(): - self.f.write("{:>12d} {} types\n".format(len(attr.types()), btype)) + self.f.write( + "{:>12d} {} types\n".format(len(attr.types()), btype) + ) self._write_dimensions(atoms.dimensions) @@ -821,7 +840,9 @@ def _read_next_timestep(self): try: # this will automatically select in order of priority # unscaled, scaled, unwrapped, scaled_unwrapped - self.lammps_coordinate_convention = list(convention_to_col_ix)[0] + self.lammps_coordinate_convention = list(convention_to_col_ix)[ + 0 + ] except IndexError: raise ValueError("No coordinate information detected") elif not self.lammps_coordinate_convention in convention_to_col_ix: @@ -848,7 +869,9 @@ def _read_next_timestep(self): "Some of the additional columns are not present " "in the file, they will be ignored" ) - additional_keys = [key for key in self._additional_columns if key in attrs] + additional_keys = [ + key for key in self._additional_columns if key in attrs + ] else: additional_keys = [] for key in additional_keys: @@ -859,7 +882,9 @@ def _read_next_timestep(self): fields = f.readline().split() if ids: indices[i] = fields[attr_to_col_ix["id"]] - coords = np.array([fields[dim] for dim in coord_cols], dtype=np.float32) + coords = np.array( + [fields[dim] for dim in coord_cols], dtype=np.float32 + ) if self._unwrap: images = coords[3:] @@ -876,7 +901,9 @@ def _read_next_timestep(self): # Collect additional cols for attribute_key in additional_keys: - ts.data[attribute_key][i] = fields[attr_to_col_ix[attribute_key]] + ts.data[attribute_key][i] = fields[ + attr_to_col_ix[attribute_key] + ] order = np.argsort(indices) ts.positions = ts.positions[order] @@ -891,7 +918,9 @@ def _read_next_timestep(self): if self.lammps_coordinate_convention.startswith("scaled"): # if coordinates are given in scaled format, undo that - ts.positions = distances.transform_StoR(ts.positions, ts.dimensions) + ts.positions = distances.transform_StoR( + ts.positions, ts.dimensions + ) # Transform to origin after transformation of scaled variables ts.positions -= np.array([xlo, ylo, zlo])[None, :] From 60f6c83db43d4f61ef9e307d85c7dd45066fe6ca Mon Sep 17 00:00:00 2001 From: NAYAN SHARMA Date: Wed, 4 Nov 2026 07:40:08 +0530 Subject: [PATCH 8/8] Apply Black formatting to coordinate tests --- testsuite/MDAnalysisTests/coordinates/base.py | 89 ++++++++++++++----- .../coordinates/test_lammps.py | 86 +++++++++++++----- 2 files changed, 131 insertions(+), 44 deletions(-) diff --git a/testsuite/MDAnalysisTests/coordinates/base.py b/testsuite/MDAnalysisTests/coordinates/base.py index 43f538216e8..2bf50cc776c 100644 --- a/testsuite/MDAnalysisTests/coordinates/base.py +++ b/testsuite/MDAnalysisTests/coordinates/base.py @@ -69,7 +69,9 @@ def test_n_atoms(self): ) def test_numres(self): - assert_equal(self.universe.atoms.n_residues, 214, "wrong number of residues") + assert_equal( + self.universe.atoms.n_residues, 214, "wrong number of residues" + ) def test_n_frames(self): assert_equal( @@ -79,13 +81,16 @@ def test_n_frames(self): ) def test_time(self): - assert_equal(self.universe.trajectory.time, 0.0, "wrong time of the frame") + assert_equal( + self.universe.trajectory.time, 0.0, "wrong time of the frame" + ) def test_frame(self): assert_equal( self.universe.trajectory.frame, 0, - "wrong frame number (0-based, should be 0 for single frame " "readers)", + "wrong frame number (0-based, should be 0 for single frame " + "readers)", ) def test_frame_index_0(self): @@ -201,7 +206,9 @@ def __init__(self): [2**8], # frame 4 = 4ps = step 8 ] self.aux_highf_n_steps = 10 - self.aux_highf_all_data = [[2**i] for i in range(self.aux_highf_n_steps)] + self.aux_highf_all_data = [ + [2**i] for i in range(self.aux_highf_n_steps) + ] self.aux_offset_by = 0.25 @@ -232,7 +239,9 @@ def __init__(self): self.jump_to_frame.aux.lowf = self.aux_lowf_data[3] self.jump_to_frame.aux.highf = self.aux_highf_data[3] - self.dimensions = np.array([81.1, 82.2, 83.3, 75, 80, 85], dtype=np.float32) + self.dimensions = np.array( + [81.1, 82.2, 83.3, 75, 80, 85], dtype=np.float32 + ) self.dimensions_second_frame = np.array( [82.1, 83.2, 84.3, 75.1, 80.1, 85.1], dtype=np.float32 ) @@ -276,12 +285,16 @@ def reader(ref): @pytest.fixture() def transformed(ref): transformed = ref.reader(ref.trajectory) - transformed.add_transformations(translate([1, 1, 1]), translate([0, 0, 0.33])) + transformed.add_transformations( + translate([1, 1, 1]), translate([0, 0, 0.33]) + ) return transformed def test_add_transformations_rejects_non_callable(self): u = mda.Universe.empty(1, trajectory=True) - with pytest.raises(TypeError, match="All transformations must be callable"): + with pytest.raises( + TypeError, match="All transformations must be callable" + ): u.trajectory.add_transformations([1, 2, 3]) def test_n_atoms(self, ref, reader): @@ -292,7 +305,9 @@ def test_n_frames(self, ref, reader): def test_first_frame(self, ref, reader): reader.rewind() - assert_timestep_almost_equal(reader.ts, ref.first_frame, decimal=ref.prec) + assert_timestep_almost_equal( + reader.ts, ref.first_frame, decimal=ref.prec + ) def test_double_close(self, reader): reader.close() @@ -320,7 +335,9 @@ def test_ts_dt_matches_reader(self, reader): assert_equal(reader.ts.dt, reader.dt) def test_total_time(self, ref, reader): - assert_allclose(reader.totaltime, ref.totaltime, atol=10 ** (-ref.prec), rtol=0) + assert_allclose( + reader.totaltime, ref.totaltime, atol=10 ** (-ref.prec), rtol=0 + ) def test_first_dimensions(self, ref, reader): reader.rewind() @@ -391,7 +408,8 @@ def test_iter_auxiliary(self, ref, reader): ref.aux_highf_all_data[i], atol=1e-7, rtol=0, - err_msg="Auxiliary data does not match for " "step {}".format(i), + err_msg="Auxiliary data does not match for " + "step {}".format(i), ) def test_get_aux_attribute(self, ref, reader): @@ -414,7 +432,8 @@ def test_iter_as_aux_cutoff(self, ref, reader): assert_equal( num_frames, 0, - "iter_as_aux should iterate over 0 frames," " not {}".format(num_frames), + "iter_as_aux should iterate over 0 frames," + " not {}".format(num_frames), ) def test_reload_auxiliaries_from_description(self, ref, reader): @@ -477,7 +496,9 @@ def test_transformations_slice(self, ref, transformed): v2 = np.float32((0, 0, 0.33)) for i, ts in enumerate(transformed[2:3:1]): idealcoords = ref.iter_ts(ts.frame).positions + v1 + v2 - assert_allclose(ts.positions, idealcoords, atol=10 ** (-ref.prec), rtol=0) + assert_allclose( + ts.positions, idealcoords, atol=10 ** (-ref.prec), rtol=0 + ) def test_transformations_switch_frame(self, ref, transformed): # This test checks if the transformations are applied and if the coordinates @@ -551,7 +572,9 @@ def test_transformations_copy(self, ref, transformed): ) for i, ts in enumerate(new): ideal_coords = ref.iter_ts(i).positions + v1 + v2 - assert_allclose(ts.positions, ideal_coords, atol=10 ** (-ref.prec), rtol=0) + assert_allclose( + ts.positions, ideal_coords, atol=10 ** (-ref.prec), rtol=0 + ) def test_add_another_transformations_raises_ValueError(self, transformed): # After defining the transformations, the workflow cannot be changed @@ -561,7 +584,9 @@ def test_add_another_transformations_raises_ValueError(self, transformed): def test_pickle_reader(self, reader): reader_p = pickle.loads(pickle.dumps(reader)) assert_equal(len(reader), len(reader_p)) - assert_equal(reader.ts, reader_p.ts, "Timestep is changed after pickling") + assert_equal( + reader.ts, reader_p.ts, "Timestep is changed after pickling" + ) reader_p_p = pickle.loads(pickle.dumps(reader_p)) assert_equal(len(reader), len(reader_p_p)) assert_equal( @@ -595,7 +620,9 @@ def test_frame_collect_all_same(self, reader): for array in collected_ts: assert_allclose(array, collected_ts[0]) - @pytest.mark.parametrize("order", ("fac", "fca", "afc", "acf", "caf", "cfa")) + @pytest.mark.parametrize( + "order", ("fac", "fca", "afc", "acf", "caf", "cfa") + ) def test_timeseries_shape(self, reader, order): timeseries = reader.timeseries(order=order) a_index = order.index("a") @@ -637,7 +664,9 @@ def test_timeseries_empty_asel(self, reader): UserWarning, match="Empty string to select atoms, empty group returned.", ): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms(None) + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( + None + ) with pytest.raises(ValueError, match="Timeseries requires at least"): reader.timeseries(asel=atoms) @@ -646,23 +675,33 @@ def test_timeseries_empty_atomgroup(self, reader): UserWarning, match="Empty string to select atoms, empty group returned.", ): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms(None) + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( + None + ) with pytest.raises(ValueError, match="Timeseries requires at least"): reader.timeseries(atomgroup=atoms) def test_timeseries_asel_warns_deprecation(self, reader): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms("index 1") + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( + "index 1" + ) with pytest.warns(DeprecationWarning, match="asel argument to"): timeseries = reader.timeseries(asel=atoms, order="fac") def test_timeseries_atomgroup(self, reader): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms("index 1") + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( + "index 1" + ) timeseries = reader.timeseries(atomgroup=atoms, order="fac") def test_timeseries_atomgroup_asel_mutex(self, reader): - atoms = mda.Universe(reader.filename, to_guess=()).select_atoms("index 1") + atoms = mda.Universe(reader.filename, to_guess=()).select_atoms( + "index 1" + ) with pytest.raises(ValueError, match="Cannot provide both"): - timeseries = reader.timeseries(atomgroup=atoms, asel=atoms, order="fac") + timeseries = reader.timeseries( + atomgroup=atoms, asel=atoms, order="fac" + ) class MultiframeReaderTest(BaseReaderTest): @@ -765,7 +804,9 @@ def test_pickle_last_ts_reader(self, reader): len(reader_p), "Last timestep is changed after pickling", ) - assert_equal(reader.ts, reader_p.ts, "Last timestep is changed after pickling") + assert_equal( + reader.ts, reader_p.ts, "Last timestep is changed after pickling" + ) class BaseWriterTest(object): @@ -961,7 +1002,9 @@ def assert_timestep_almost_equal(A, B, decimal=6, verbose=True): if A.has_forces != B.has_forces: raise AssertionError( "Only one Timestep has forces:" - "A.has_forces = {}, B.has_forces = {}".format(A.has_forces, B.has_forces) + "A.has_forces = {}, B.has_forces = {}".format( + A.has_forces, B.has_forces + ) ) if A.has_forces: assert_allclose( diff --git a/testsuite/MDAnalysisTests/coordinates/test_lammps.py b/testsuite/MDAnalysisTests/coordinates/test_lammps.py index e9ddd4acd60..25106623f97 100644 --- a/testsuite/MDAnalysisTests/coordinates/test_lammps.py +++ b/testsuite/MDAnalysisTests/coordinates/test_lammps.py @@ -229,14 +229,18 @@ def test_Writer_atoms_types(self, LAMMPSDATAWriter): err_msg="attributes different after writing", ) - @pytest.mark.parametrize("attr", ["bonds", "angles", "dihedrals", "impropers"]) + @pytest.mark.parametrize( + "attr", ["bonds", "angles", "dihedrals", "impropers"] + ) def test_Writer_atoms(self, attr, LAMMPSDATAWriter): u_ref, u_new = LAMMPSDATAWriter ref = getattr(u_ref.atoms, attr) new = getattr(u_new.atoms, attr) assert ref == new, "attributes different after writing" - @pytest.mark.parametrize("attr", ["masses", "charges", "velocities", "positions"]) + @pytest.mark.parametrize( + "attr", ["masses", "charges", "velocities", "positions"] + ) def test_Writer_numerical_attrs(self, attr, LAMMPSDATAWriter): u_ref, u_new = LAMMPSDATAWriter try: @@ -263,7 +267,9 @@ def test_molecule_tag(self, LAMMPSDATAWriter_molecule_tag): ) -@pytest.mark.parametrize("filename", ["out.data", "out.data.bz2", "out.data.gz"]) +@pytest.mark.parametrize( + "filename", ["out.data", "out.data.bz2", "out.data.gz"] +) def test_datawriter_universe(filename, tmpdir): """ Test roundtrip on datawriter, and also checks compressed files @@ -301,7 +307,9 @@ def LAMMPSDATA_partial(tmpdir): return u, u_new - @pytest.mark.parametrize("attr", ["masses", "charges", "velocities", "positions"]) + @pytest.mark.parametrize( + "attr", ["masses", "charges", "velocities", "positions"] + ) def test_Writer_atoms(self, attr, LAMMPSDATA_partial): u_ref, u_new = LAMMPSDATA_partial if hasattr(u_ref.atoms, attr): @@ -417,7 +425,9 @@ def test_n_frames(self, u): assert_equal(u.trajectory.n_frames, self.n_frames) def test_dimensions(self, u): - mean_dimensions = np.mean([ts.dimensions.copy() for ts in u.trajectory], axis=0) + mean_dimensions = np.mean( + [ts.dimensions.copy() for ts in u.trajectory], axis=0 + ) assert_allclose(mean_dimensions, self.mean_dimensions) def test_dt(self, u): @@ -432,11 +442,15 @@ def test_Timestep_time(self, u): assert_allclose( u.trajectory[iframe].time, iframe * self.dt, - err_msg="Time for frame {0} (dt={1}) is wrong.".format(iframe, self.dt), + err_msg="Time for frame {0} (dt={1}) is wrong.".format( + iframe, self.dt + ), ) def test_LAMMPSDCDReader_set_dt(self, u, dt=1500.0): - u = mda.Universe(self.topology, self.trajectory, format=self.format, dt=dt) + u = mda.Universe( + self.topology, self.trajectory, format=self.format, dt=dt + ) iframe = self.get_frame_from_end(1, u) assert_allclose( u.trajectory[iframe].time, @@ -489,14 +503,18 @@ def u(self): def test_Writer_is_LAMMPS(self, u, tmpdir): ext = os.path.splitext(self.trajectory)[1] outfile = str(tmpdir.join("lammps-writer-test" + ext)) - with mda.Writer(outfile, n_atoms=u.atoms.n_atoms, format=self.format) as W: + with mda.Writer( + outfile, n_atoms=u.atoms.n_atoms, format=self.format + ) as W: assert W.flavor, self.flavor def test_Writer(self, u, tmpdir, n_frames=3): ext = os.path.splitext(self.trajectory)[1] outfile = str(tmpdir.join("lammps-writer-test" + ext)) - with mda.Writer(outfile, n_atoms=u.atoms.n_atoms, format=self.format) as w: + with mda.Writer( + outfile, n_atoms=u.atoms.n_atoms, format=self.format + ) as w: for ts in u.trajectory[:n_frames]: w.write(u) @@ -572,7 +590,9 @@ def test_open(self, tmpdir): def test_wrong_time_unit(self, tmpdir): outfile = str(tmpdir.join("lammps-writer-test.dcd")) with pytest.raises(TypeError): - with mda.coordinates.LAMMPS.DCDWriter(outfile, n_atoms=10, timeunit="nm"): + with mda.coordinates.LAMMPS.DCDWriter( + outfile, n_atoms=10, timeunit="nm" + ): pass def test_wrong_unit(self, tmpdir): @@ -647,7 +667,9 @@ def u(self, tmpdir, request): with gzip.GzipFile(f, "wb") as fout: fout.write(data) - yield mda.Universe(f, format="LAMMPSDUMP", lammps_coordinate_convention="auto") + yield mda.Universe( + f, format="LAMMPSDUMP", lammps_coordinate_convention="auto" + ) @pytest.fixture() def u_additional_columns_true(self): @@ -765,9 +787,15 @@ def test_seeking(self, u, reference_positions): u.trajectory[1] assert_allclose(u.dimensions, reference_positions["box"][1], atol=1e-5) - pos = reference_positions["atom1_pos"][1] - reference_positions["mins"][1] + pos = ( + reference_positions["atom1_pos"][1] + - reference_positions["mins"][1] + ) assert_allclose(u.atoms[0].position, pos, atol=1e-5) - pos = reference_positions["atom13_pos"][1] - reference_positions["mins"][1] + pos = ( + reference_positions["atom13_pos"][1] + - reference_positions["mins"][1] + ) assert_allclose(u.atoms[12].position, pos, atol=1e-5) def test_boxsize(self, u, reference_positions): @@ -798,7 +826,9 @@ def test_additional_columns(self, system, fields, request): u = request.getfixturevalue(system) for field in fields: data = u.trajectory[0].data[field] - assert_allclose(data, getattr(RefLAMMPSDataAdditionalColumns, field)) + assert_allclose( + data, getattr(RefLAMMPSDataAdditionalColumns, field) + ) @pytest.mark.parametrize( "system", @@ -807,7 +837,9 @@ def test_additional_columns(self, system, fields, request): ], ) def test_wrong_format_additional_colums(self, system, request): - with pytest.raises(ValueError, match="Please provide an iterable containing"): + with pytest.raises( + ValueError, match="Please provide an iterable containing" + ): request.getfixturevalue(system) @pytest.mark.parametrize( @@ -821,7 +853,9 @@ def test_warning(self, system, request): request.getfixturevalue(system) -@pytest.mark.parametrize("convention", ["unscaled", "unwrapped", "scaled_unwrapped"]) +@pytest.mark.parametrize( + "convention", ["unscaled", "unwrapped", "scaled_unwrapped"] +) def test_open_absent_convention_fails(convention): with pytest.raises(ValueError, match="No coordinates following"): mda.Universe( @@ -833,7 +867,9 @@ def test_open_absent_convention_fails(convention): def test_open_incorrect_convention_fails(): with pytest.raises(ValueError, match="is not a valid option"): - mda.Universe(LAMMPSDUMP, format="LAMMPSDUMP", lammps_coordinate_convention="42") + mda.Universe( + LAMMPSDUMP, format="LAMMPSDUMP", lammps_coordinate_convention="42" + ) @pytest.mark.parametrize( @@ -935,7 +971,9 @@ def reference_unwrapped_positions(self): ] ) - def test_unwrapped_scaled_reference(self, universes, reference_unwrapped_positions): + def test_unwrapped_scaled_reference( + self, universes, reference_unwrapped_positions + ): atom_340 = universes["unwrapped"].atoms[339] for i, ts_u in enumerate(universes["unwrapped"].trajectory[0:3]): assert_allclose( @@ -944,10 +982,14 @@ def test_unwrapped_scaled_reference(self, universes, reference_unwrapped_positio atol=1e-5, ) - def test_unwrapped_scaled_reference(self, universes, reference_unwrapped_positions): + def test_unwrapped_scaled_reference( + self, universes, reference_unwrapped_positions + ): # NOTE use of unscaled positions here due to S->R transform atom_340 = universes["scaled_unwrapped"].atoms[339] - for i, ts_u in enumerate(universes["scaled_unwrapped"].trajectory[0:3]): + for i, ts_u in enumerate( + universes["scaled_unwrapped"].trajectory[0:3] + ): assert_allclose( atom_340.position, reference_unwrapped_positions[i, :], @@ -1027,6 +1069,8 @@ def test_box(self, u_dump, u_data, reference_box): assert_allclose(ts.dimensions, reference_box, rtol=1e-5, atol=0) for ts in u_dump.trajectory: - assert_allclose(ts.dimensions, u_data.dimensions, rtol=1e-5, atol=0) + assert_allclose( + ts.dimensions, u_data.dimensions, rtol=1e-5, atol=0 + ) assert_allclose(u_data.dimensions, reference_box, rtol=1e-5, atol=0)