From 66bff55f5316e3c621b10670dfa571e3b42eb1b9 Mon Sep 17 00:00:00 2001 From: njzjz-bot Date: Mon, 24 Aug 2026 03:45:40 +0800 Subject: [PATCH] fix: export ABACUS structures for atomic LAMMPS Drop ABACUS spin metadata from per-frame LAMMPS exploration exports because DPGEN2 declares atom_style atomic. Coding-Agent: Codex Codex-Version: codex-cli 0.149.1 Model: gpt-5.6-sol Reasoning-Effort: xhigh --- dpgen2/conf/conf_generator.py | 10 +++++++++- tests/conf/test_file_conf.py | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/dpgen2/conf/conf_generator.py b/dpgen2/conf/conf_generator.py index e5e3b7b0..17eb81e4 100644 --- a/dpgen2/conf/conf_generator.py +++ b/dpgen2/conf/conf_generator.py @@ -59,9 +59,17 @@ def get_file_content( for ii in range(len(ms)): ss = ms[ii] for jj in range(ss.get_nframes()): + frame = ss[jj] + if fmt in {"lmp", "lammps/lmp"}: + # Exploration inputs always declare ``atom_style atomic``. + # dpdata serializes ABACUS magnetic moments as four extra + # atom columns, which belong to LAMMPS's spin atom style and + # make the resulting atomic-style data file unreadable. + # Remove them only from this per-frame export copy. + frame.data.pop("spins", None) with tempfile.NamedTemporaryFile() as ft: tf = Path(ft.name) - ss[jj].to(fmt, tf) + frame.to(fmt, tf) ret.append(tf.read_text()) return ret diff --git a/tests/conf/test_file_conf.py b/tests/conf/test_file_conf.py index ec208522..e2a879da 100644 --- a/tests/conf/test_file_conf.py +++ b/tests/conf/test_file_conf.py @@ -105,6 +105,29 @@ + "Atoms # atomic\n\n 1 2 0.0000000000 0.0000000000 0.0000000000\n" ) +abacus_stru = """ATOMIC_SPECIES +Si 28.085 Si.upf + +NUMERICAL_ORBITAL +Si.orb + +LATTICE_CONSTANT +1.0 + +LATTICE_VECTORS +10.0 0.0 0.0 +0.0 10.0 0.0 +0.0 0.0 10.0 + +ATOMIC_POSITIONS +Cartesian + +Si +0.0 +1 +1.0 2.0 3.0 1 1 1 mag 0.0 +""" + class TestFileConfGenerator(unittest.TestCase): def setUp(self): @@ -205,6 +228,20 @@ def test_deepmd_mixed(self): class TestFileConfGeneratorContent(unittest.TestCase): + def test_abacus_spin_metadata_is_not_written_to_atomic_lammps_data(self): + stru = Path("STRU") + stru.write_text(abacus_stru) + self.addCleanup(stru.unlink, missing_ok=True) + + content = FileConfGenerator(str(stru), fmt="abacus/stru").get_file_content( + type_map=["Si"] + )[0] + atom_section = content.split("Atoms # atomic", maxsplit=1)[1] + atom_line = next(line for line in atom_section.splitlines() if line.strip()) + + # Atomic style accepts only ID, type, and xyz coordinates. + self.assertEqual(len(atom_line.split()), 5) + def test_list_1(self): f0 = Path("f0.POSCAR") f1 = Path("f1.POSCAR")