From 3e7292f9dce9a1be1edf71c5a6bed5f9e45e98fe Mon Sep 17 00:00:00 2001 From: abhi-0203 Date: Sun, 5 Jul 2026 04:35:58 +0000 Subject: [PATCH 1/3] fix: skip zero-force fabrication when ABACUS MD dump lacks FORCE columns When an ABACUS MD dump file does not contain FORCE columns (i.e., dump_force=0), the parser previously initialized forces as a zero array and always assigned it to the output data dict. This fabricated misleading zero forces instead of correctly omitting the forces key. Now get_coords_from_dump() returns None for forces when calc_force is False, and get_frame() only includes 'forces' in the data dict when actual force data was parsed. Closes #1000 --- dpdata/formats/abacus/md.py | 10 +++++---- tests/abacus.md.noforce/INPUT | 2 ++ tests/abacus.md.noforce/OUT.autotest/MD_dump | 22 ++++++++++++++++++++ tests/abacus.md.noforce/STRU | 19 +++++++++++++++++ tests/test_abacus_md.py | 13 ++++++++++++ 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 tests/abacus.md.noforce/INPUT create mode 100644 tests/abacus.md.noforce/OUT.autotest/MD_dump create mode 100644 tests/abacus.md.noforce/STRU diff --git a/dpdata/formats/abacus/md.py b/dpdata/formats/abacus/md.py index 8df156c94..eb31059f0 100644 --- a/dpdata/formats/abacus/md.py +++ b/dpdata/formats/abacus/md.py @@ -72,7 +72,7 @@ def get_coords_from_dump(dumplines, natoms): ) cells = np.zeros([nframes_dump, 3, 3]) stresses = np.zeros([nframes_dump, 3, 3]) - forces = np.zeros([nframes_dump, total_natoms, 3]) + forces = np.zeros([nframes_dump, total_natoms, 3]) if calc_force else None coords = np.zeros([nframes_dump, total_natoms, 3]) iframe = 0 for iline in range(nlines): @@ -115,7 +115,7 @@ def get_coords_from_dump(dumplines, natoms): if not newversion: coords[iframe, iat] *= celldm - if calc_force: + if calc_force and forces is not None: forces[iframe, iat] = np.array( [ float(i) @@ -187,7 +187,8 @@ def get_frame(fname): if np.isnan(iene): coords = np.delete(coords, i - ndump, axis=0) cells = np.delete(cells, i - ndump, axis=0) - force = np.delete(force, i - ndump, axis=0) + if force is not None: + force = np.delete(force, i - ndump, axis=0) stress = np.delete(stress, i - ndump, axis=0) energy = np.delete(energy, i - ndump, axis=0) unconv_stru += "%d " % i # noqa: UP031 @@ -207,7 +208,8 @@ def get_frame(fname): # data['cells'][:, :, :] = cell data["coords"] = coords data["energies"] = energy - data["forces"] = force + if force is not None: + data["forces"] = force data["virials"] = stress if not isinstance(data["virials"], np.ndarray): del data["virials"] diff --git a/tests/abacus.md.noforce/INPUT b/tests/abacus.md.noforce/INPUT new file mode 100644 index 000000000..7b3cdc992 --- /dev/null +++ b/tests/abacus.md.noforce/INPUT @@ -0,0 +1,2 @@ +suffix autotest +md_dumpfreq 1 diff --git a/tests/abacus.md.noforce/OUT.autotest/MD_dump b/tests/abacus.md.noforce/OUT.autotest/MD_dump new file mode 100644 index 000000000..b9c9c4057 --- /dev/null +++ b/tests/abacus.md.noforce/OUT.autotest/MD_dump @@ -0,0 +1,22 @@ +MDSTEP: 0 +LATTICE_CONSTANT: 10.200000000000 +LATTICE_VECTORS + 0.500000000000 0.500000000000 0.000000000000 + 0.500000000000 0.000000000000 0.500000000000 + 0.000000000000 0.500000000000 0.500000000000 +INDEX LABEL POSITIONS + 0 Si 0.000000000000 0.000000000000 0.000000000000 + 1 Si 0.241000000000 0.255000000000 0.251000000000 + + +MDSTEP: 1 +LATTICE_CONSTANT: 10.200000000000 +LATTICE_VECTORS + 0.500000000000 0.500000000000 0.000000000000 + 0.500000000000 0.000000000000 0.500000000000 + 0.000000000000 0.500000000000 0.500000000000 +INDEX LABEL POSITIONS + 0 Si 0.010000000000 0.010000000000 0.010000000000 + 1 Si 0.251000000000 0.265000000000 0.261000000000 + + diff --git a/tests/abacus.md.noforce/STRU b/tests/abacus.md.noforce/STRU new file mode 100644 index 000000000..1533a93a6 --- /dev/null +++ b/tests/abacus.md.noforce/STRU @@ -0,0 +1,19 @@ +ATOMIC_SPECIES +Si 1 ../tools/PP_ORB/Si_ONCV_PBE-1.0.upf + +LATTICE_CONSTANT +10.2 + +LATTICE_VECTORS +0.5 0.5 0 #latvec1 +0.5 0 0.5 #latvec2 +0 0.5 0.5 #latvec3 + +ATOMIC_POSITIONS +Cartesian + +Si #label +0 #magnetism +2 #number of atoms +0 0 0 m 1 1 1 v -0 0 0 +0.241 0.255 0.251 m 1 1 1 v 0 -0 -0 diff --git a/tests/test_abacus_md.py b/tests/test_abacus_md.py index ddcb7734a..07a5dfd85 100644 --- a/tests/test_abacus_md.py +++ b/tests/test_abacus_md.py @@ -249,6 +249,19 @@ def test_to_system(self): iline += 1 self.assertEqual(iline, 30) + def test_no_force_columns(self): + """Test that MD dumps without FORCE columns do not fabricate zero forces.""" + system_noforce = dpdata.LabeledSystem( + "abacus.md.noforce", fmt="abacus/md" + ) + # When FORCE is absent from the dump, forces should not be in data + self.assertNotIn("forces", system_noforce.data) + # Other data should still be present + self.assertIn("coords", system_noforce.data) + self.assertIn("energies", system_noforce.data) + self.assertIn("cells", system_noforce.data) + self.assertEqual(len(system_noforce.data["coords"]), 2) + if __name__ == "__main__": unittest.main() From 1cb495861af0bb768be9dd87857afff6ad6b88bc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 5 Jul 2026 04:36:26 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_abacus_md.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_abacus_md.py b/tests/test_abacus_md.py index 07a5dfd85..0852a6096 100644 --- a/tests/test_abacus_md.py +++ b/tests/test_abacus_md.py @@ -251,9 +251,7 @@ def test_to_system(self): def test_no_force_columns(self): """Test that MD dumps without FORCE columns do not fabricate zero forces.""" - system_noforce = dpdata.LabeledSystem( - "abacus.md.noforce", fmt="abacus/md" - ) + system_noforce = dpdata.LabeledSystem("abacus.md.noforce", fmt="abacus/md") # When FORCE is absent from the dump, forces should not be in data self.assertNotIn("forces", system_noforce.data) # Other data should still be present From 19e27fb1f42e067744c7cb1c480aa8fbbc4f0672 Mon Sep 17 00:00:00 2001 From: abhi-0203 Date: Sun, 5 Jul 2026 06:59:53 +0000 Subject: [PATCH 3/3] fix: add missing running_md.log fixture for no-force ABACUS test The test fixture at tests/abacus.md.noforce/ was missing running_md.log, which dpdata/formats/abacus/md.py:get_frame unconditionally opens. This caused FileNotFoundError when running test_no_force_columns. Add a minimal 2-frame running_md.log with matching energies to fix the test. Addresses CodeRabbit review comment on PR #1008 --- tests/abacus.md.noforce/OUT.autotest/running_md.log | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/abacus.md.noforce/OUT.autotest/running_md.log diff --git a/tests/abacus.md.noforce/OUT.autotest/running_md.log b/tests/abacus.md.noforce/OUT.autotest/running_md.log new file mode 100644 index 000000000..92f6915d6 --- /dev/null +++ b/tests/abacus.md.noforce/OUT.autotest/running_md.log @@ -0,0 +1,8 @@ + STEP OF MOLECULAR DYNAMICS : 0 + ------------------------------------------- + final etot is -211.771846025 eV + + ------------------------------------------- + STEP OF MOLECULAR DYNAMICS : 1 + ------------------------------------------- + final etot is -211.781119663 eV