From 3c19cedeba8e65cc93604cdf9900dcc58fa75100 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 08:50:07 +0800 Subject: [PATCH 01/33] [pre-commit.ci] pre-commit autoupdate (#696) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.6 → v0.5.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.6...v0.5.7) ## Summary by CodeRabbit - **Chores** - Updated the Ruff pre-commit hook to the latest version (v0.5.7) for improved linting of Python code. Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1bb2fcc0..4f16df29 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.6 + rev: v0.5.7 hooks: - id: ruff args: ["--fix"] From cbcbb92ce959ff08647c5e2587599a54c9cc3663 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 20 Aug 2024 14:15:49 +0800 Subject: [PATCH 02/33] [pre-commit.ci] pre-commit autoupdate (#697) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.5.7 → v0.6.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.5.7...v0.6.1) ## Summary by CodeRabbit - **Chores** - Updated the version of the Ruff pre-commit hook from v0.5.7 to v0.6.1, potentially enhancing linting and formatting during pre-commit checks. Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4f16df29..c255e2b1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.7 + rev: v0.6.1 hooks: - id: ruff args: ["--fix"] From 2fb0804116761aef072021e412b589be551c2f69 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 26 Aug 2024 20:53:38 -0400 Subject: [PATCH 03/33] docs: migrate from m2r2 to myst-parser (#701) ## Summary by CodeRabbit - **Documentation Updates** - Updated documentation generation tools to use `myst_parser`, enhancing Markdown compatibility and features. - Improved documentation processing and rendering capabilities. --- docs/conf.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e3c0b3d4..a21d19d5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,7 +49,7 @@ "sphinx.ext.viewcode", "sphinx.ext.intersphinx", "numpydoc", - "m2r2", + "myst_parser", "sphinxarg.ext", "jupyterlite_sphinx", ] diff --git a/pyproject.toml b/pyproject.toml index 05a6e243..4bf2e64b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,7 @@ docs = [ 'recommonmark', 'sphinx_rtd_theme>=1.0.0rc1', 'numpydoc', - 'm2r2', + 'myst-parser', 'deepmodeling-sphinx>=0.1.1', 'sphinx-argparse<0.5.0', 'rdkit', From 0a50f4d5d62b070b92f708ace0c83b8281476d62 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 27 Aug 2024 01:25:03 +0000 Subject: [PATCH 04/33] [pre-commit.ci] pre-commit autoupdate (#700) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.1 → v0.6.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.1...v0.6.2) ## Summary by CodeRabbit - **Chores** - Updated the version of the Ruff pre-commit hook to enhance performance and reliability. Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c255e2b1..f4abb50c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.1 + rev: v0.6.2 hooks: - id: ruff args: ["--fix"] From 676517aa64b6b2825a6551655c79cd93449a15be Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Wed, 28 Aug 2024 21:34:11 -0400 Subject: [PATCH 05/33] feat: customized dtypes for unlabeled deepmd (#702) ## Summary by CodeRabbit - **New Features** - Enhanced flexibility in data type handling for system data processing based on the presence of labels. - New data type registration for "foo" with updated shape and configuration. - **Bug Fixes** - Improved adaptability and performance in data processing functions by refining dtype selection. - **Refactor** - Streamlined test structure for loading and dumping data types, improving organization and maintainability of test cases. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dpdata/deepmd/comp.py | 76 ++++++++++++--------- dpdata/deepmd/hdf5.py | 13 +++- dpdata/deepmd/raw.py | 76 +++++++++++---------- tests/plugin/dpdata_plugin_test/__init__.py | 4 ++ tests/test_custom_data_type.py | 38 ++++++++--- 5 files changed, 126 insertions(+), 81 deletions(-) diff --git a/dpdata/deepmd/comp.py b/dpdata/deepmd/comp.py index ab004447..d6ccf3ff 100644 --- a/dpdata/deepmd/comp.py +++ b/dpdata/deepmd/comp.py @@ -67,40 +67,43 @@ def to_system_data(folder, type_map=None, labels=True): data["virials"] = np.concatenate(all_virs, axis=0) # allow custom dtypes if labels: - for dtype in dpdata.system.LabeledSystem.DTYPES: - if dtype.name in ( - "atom_numbs", - "atom_names", - "atom_types", - "orig", - "cells", - "coords", - "real_atom_types", - "real_atom_names", - "nopbc", - "energies", - "forces", - "virials", - ): - # skip as these data contains specific rules - continue - if not (len(dtype.shape) and dtype.shape[0] == dpdata.system.Axis.NFRAMES): - warnings.warn( - f"Shape of {dtype.name} is not (nframes, ...), but {dtype.shape}. This type of data will not converted from deepmd/npy format." - ) - continue - natoms = data["coords"].shape[1] - shape = [ - natoms if xx == dpdata.system.Axis.NATOMS else xx - for xx in dtype.shape[1:] - ] - all_data = [] - for ii in sets: - tmp = _cond_load_data(os.path.join(ii, dtype.name + ".npy")) - if tmp is not None: - all_data.append(np.reshape(tmp, [tmp.shape[0], *shape])) - if len(all_data) > 0: - data[dtype.name] = np.concatenate(all_data, axis=0) + dtypes = dpdata.system.LabeledSystem.DTYPES + else: + dtypes = dpdata.system.System.DTYPES + + for dtype in dtypes: + if dtype.name in ( + "atom_numbs", + "atom_names", + "atom_types", + "orig", + "cells", + "coords", + "real_atom_types", + "real_atom_names", + "nopbc", + "energies", + "forces", + "virials", + ): + # skip as these data contains specific rules + continue + if not (len(dtype.shape) and dtype.shape[0] == dpdata.system.Axis.NFRAMES): + warnings.warn( + f"Shape of {dtype.name} is not (nframes, ...), but {dtype.shape}. This type of data will not converted from deepmd/npy format." + ) + continue + natoms = data["coords"].shape[1] + shape = [ + natoms if xx == dpdata.system.Axis.NATOMS else xx for xx in dtype.shape[1:] + ] + all_data = [] + for ii in sets: + tmp = _cond_load_data(os.path.join(ii, dtype.name + ".npy")) + if tmp is not None: + all_data.append(np.reshape(tmp, [tmp.shape[0], *shape])) + if len(all_data) > 0: + data[dtype.name] = np.concatenate(all_data, axis=0) return data @@ -173,6 +176,11 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): with open(os.path.join(folder, "nopbc"), "w") as fw_nopbc: pass # allow custom dtypes + labels = "energies" in data + if labels: + dtypes = dpdata.system.LabeledSystem.DTYPES + else: + dtypes = dpdata.system.System.DTYPES for dtype in dpdata.system.LabeledSystem.DTYPES: if dtype.name in ( "atom_numbs", diff --git a/dpdata/deepmd/hdf5.py b/dpdata/deepmd/hdf5.py index 34ae9dbe..78f4e33f 100644 --- a/dpdata/deepmd/hdf5.py +++ b/dpdata/deepmd/hdf5.py @@ -102,7 +102,11 @@ def to_system_data( }, } # allow custom dtypes - for dtype in dpdata.system.LabeledSystem.DTYPES: + if labels: + dtypes = dpdata.system.LabeledSystem.DTYPES + else: + dtypes = dpdata.system.System.DTYPES + for dtype in dtypes: if dtype.name in ( "atom_numbs", "atom_names", @@ -210,8 +214,13 @@ def dump( "virials": {"fn": "virial", "shape": (nframes, 9), "dump": True}, } + labels = "energies" in data + if labels: + dtypes = dpdata.system.LabeledSystem.DTYPES + else: + dtypes = dpdata.system.System.DTYPES # allow custom dtypes - for dtype in dpdata.system.LabeledSystem.DTYPES: + for dtype in dtypes: if dtype.name in ( "atom_numbs", "atom_names", diff --git a/dpdata/deepmd/raw.py b/dpdata/deepmd/raw.py index e772714a..11a9ff6e 100644 --- a/dpdata/deepmd/raw.py +++ b/dpdata/deepmd/raw.py @@ -64,40 +64,41 @@ def to_system_data(folder, type_map=None, labels=True): data["nopbc"] = True # allow custom dtypes if labels: - for dtype in dpdata.system.LabeledSystem.DTYPES: - if dtype.name in ( - "atom_numbs", - "atom_names", - "atom_types", - "orig", - "cells", - "coords", - "real_atom_types", - "real_atom_names", - "nopbc", - "energies", - "forces", - "virials", - ): - # skip as these data contains specific rules - continue - if not ( - len(dtype.shape) and dtype.shape[0] == dpdata.system.Axis.NFRAMES - ): - warnings.warn( - f"Shape of {dtype.name} is not (nframes, ...), but {dtype.shape}. This type of data will not converted from deepmd/raw format." - ) - continue - natoms = data["coords"].shape[1] - shape = [ - natoms if xx == dpdata.system.Axis.NATOMS else xx - for xx in dtype.shape[1:] - ] - if os.path.exists(os.path.join(folder, f"{dtype.name}.raw")): - data[dtype.name] = np.reshape( - np.loadtxt(os.path.join(folder, f"{dtype.name}.raw")), - [nframes, *shape], - ) + dtypes = dpdata.system.LabeledSystem.DTYPES + else: + dtypes = dpdata.system.System.DTYPES + for dtype in dtypes: + if dtype.name in ( + "atom_numbs", + "atom_names", + "atom_types", + "orig", + "cells", + "coords", + "real_atom_types", + "real_atom_names", + "nopbc", + "energies", + "forces", + "virials", + ): + # skip as these data contains specific rules + continue + if not (len(dtype.shape) and dtype.shape[0] == dpdata.system.Axis.NFRAMES): + warnings.warn( + f"Shape of {dtype.name} is not (nframes, ...), but {dtype.shape}. This type of data will not converted from deepmd/raw format." + ) + continue + natoms = data["coords"].shape[1] + shape = [ + natoms if xx == dpdata.system.Axis.NATOMS else xx + for xx in dtype.shape[1:] + ] + if os.path.exists(os.path.join(folder, f"{dtype.name}.raw")): + data[dtype.name] = np.reshape( + np.loadtxt(os.path.join(folder, f"{dtype.name}.raw")), + [nframes, *shape], + ) return data else: raise RuntimeError("not dir " + folder) @@ -144,7 +145,12 @@ def dump(folder, data): with open(os.path.join(folder, "nopbc"), "w") as fw_nopbc: pass # allow custom dtypes - for dtype in dpdata.system.LabeledSystem.DTYPES: + labels = "energies" in data + if labels: + dtypes = dpdata.system.LabeledSystem.DTYPES + else: + dtypes = dpdata.system.System.DTYPES + for dtype in dtypes: if dtype.name in ( "atom_numbs", "atom_names", diff --git a/tests/plugin/dpdata_plugin_test/__init__.py b/tests/plugin/dpdata_plugin_test/__init__.py index ef26e7c1..4e445eb2 100644 --- a/tests/plugin/dpdata_plugin_test/__init__.py +++ b/tests/plugin/dpdata_plugin_test/__init__.py @@ -10,6 +10,10 @@ DataType("foo", np.ndarray, (Axis.NFRAMES, 2, 4), required=False), labeled=True ) +register_data_type( + DataType("foo", np.ndarray, (Axis.NFRAMES, 3, 3), required=False), labeled=False +) + register_data_type( DataType("bar", np.ndarray, (Axis.NFRAMES, Axis.NATOMS, -1), required=False), labeled=True, diff --git a/tests/test_custom_data_type.py b/tests/test_custom_data_type.py index e94ba5e0..a319af35 100644 --- a/tests/test_custom_data_type.py +++ b/tests/test_custom_data_type.py @@ -9,10 +9,12 @@ from dpdata.data_type import Axis, DataType -class TestDeepmdLoadDumpComp(unittest.TestCase): +class DeepmdLoadDumpCompTest: def setUp(self): - self.system = dpdata.LabeledSystem("poscars/OUTCAR.h2o.md", fmt="vasp/outcar") - self.foo = np.ones((len(self.system), 2, 4)) + self.system = self.cls( + data=dpdata.LabeledSystem("poscars/OUTCAR.h2o.md", fmt="vasp/outcar").data + ) + self.foo = np.ones((len(self.system), *self.shape)) self.system.data["foo"] = self.foo self.system.check_data() @@ -23,7 +25,7 @@ def test_to_deepmd_raw(self): def test_from_deepmd_raw(self): self.system.to_deepmd_raw("data_foo") - x = dpdata.LabeledSystem("data_foo", fmt="deepmd/raw") + x = self.cls("data_foo", fmt="deepmd/raw") np.testing.assert_allclose(x.data["foo"], self.foo) def test_to_deepmd_npy(self): @@ -33,7 +35,7 @@ def test_to_deepmd_npy(self): def test_from_deepmd_npy(self): self.system.to_deepmd_npy("data_foo") - x = dpdata.LabeledSystem("data_foo", fmt="deepmd/npy") + x = self.cls("data_foo", fmt="deepmd/npy") np.testing.assert_allclose(x.data["foo"], self.foo) def test_to_deepmd_hdf5(self): @@ -44,18 +46,34 @@ def test_to_deepmd_hdf5(self): def test_from_deepmd_hdf5(self): self.system.to_deepmd_hdf5("data_foo.h5") - x = dpdata.LabeledSystem("data_foo.h5", fmt="deepmd/hdf5") + x = self.cls("data_foo.h5", fmt="deepmd/hdf5") np.testing.assert_allclose(x.data["foo"], self.foo) def test_duplicated_data_type(self): - dt = DataType("foo", np.ndarray, (Axis.NFRAMES, 2, 4), required=False) - n_dtypes_old = len(dpdata.LabeledSystem.DTYPES) + dt = DataType("foo", np.ndarray, (Axis.NFRAMES, *self.shape), required=False) + n_dtypes_old = len(self.cls.DTYPES) with self.assertWarns(UserWarning): - dpdata.LabeledSystem.register_data_type(dt) - n_dtypes_new = len(dpdata.LabeledSystem.DTYPES) + self.cls.register_data_type(dt) + n_dtypes_new = len(self.cls.DTYPES) self.assertEqual(n_dtypes_old, n_dtypes_new) +class TestDeepmdLoadDumpCompUnlabeled(unittest.TestCase, DeepmdLoadDumpCompTest): + cls = dpdata.System + shape = (3, 3) + + def setUp(self): + DeepmdLoadDumpCompTest.setUp(self) + + +class TestDeepmdLoadDumpCompLabeled(unittest.TestCase, DeepmdLoadDumpCompTest): + cls = dpdata.LabeledSystem + shape = (2, 4) + + def setUp(self): + DeepmdLoadDumpCompTest.setUp(self) + + class TestDeepmdLoadDumpCompAny(unittest.TestCase): def setUp(self): self.system = dpdata.LabeledSystem("poscars/OUTCAR.h2o.md", fmt="vasp/outcar") From f8a1b6b20eb9ac7f1aa6d20ee806a59e6fb00c11 Mon Sep 17 00:00:00 2001 From: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> Date: Sat, 31 Aug 2024 18:33:46 +0800 Subject: [PATCH 06/33] test: support comparison between two multi systems (#705) ## Summary by CodeRabbit - **New Features** - Introduced functions and classes to enhance testing capabilities for multi-system comparisons. - Added validation classes for periodic boundary conditions across multi-system objects. - **Bug Fixes** - Updated test classes to utilize new multi-system handling, improving clarity and functionality. - **Documentation** - Enhanced clarity in variable naming for better alignment with multi-system concepts. --------- Co-authored-by: Han Wang --- tests/comp_sys.py | 84 ++++++++++++++++++++++++++++++++++++++ tests/test_deepmd_mixed.py | 26 +++++++----- 2 files changed, 100 insertions(+), 10 deletions(-) diff --git a/tests/comp_sys.py b/tests/comp_sys.py index 99879af6..a3a916a0 100644 --- a/tests/comp_sys.py +++ b/tests/comp_sys.py @@ -105,6 +105,72 @@ def test_virial(self): ) +def _make_comp_ms_test_func(comp_sys_test_func): + """ + Dynamically generates a test function for multi-system comparisons. + + Args: + comp_sys_test_func (Callable): The original test function for single systems. + + Returns + ------- + Callable: A new test function that can handle comparisons between multi-systems. + """ + + def comp_ms_test_func(iobj): + assert hasattr(iobj, "ms_1") and hasattr( + iobj, "ms_2" + ), "Multi-system objects must be present" + iobj.assertEqual(len(iobj.ms_1), len(iobj.ms_2)) + keys = [ii.formula for ii in iobj.ms_1] + keys_2 = [ii.formula for ii in iobj.ms_2] + assert sorted(keys) == sorted( + keys_2 + ), f"Keys of two MS are not equal: {keys} != {keys_2}" + for kk in keys: + iobj.system_1 = iobj.ms_1[kk] + iobj.system_2 = iobj.ms_2[kk] + comp_sys_test_func(iobj) + del iobj.system_1 + del iobj.system_2 + + return comp_ms_test_func + + +def _make_comp_ms_class(comp_class): + """ + Dynamically generates a test class for multi-system comparisons. + + Args: + comp_class (type): The original test class for single systems. + + Returns + ------- + type: A new test class that can handle comparisons between multi-systems. + """ + + class CompMS: + pass + + test_methods = [ + func + for func in dir(comp_class) + if callable(getattr(comp_class, func)) and func.startswith("test_") + ] + + for func in test_methods: + setattr(CompMS, func, _make_comp_ms_test_func(getattr(comp_class, func))) + + return CompMS + + +# MultiSystems comparison from single System comparison +CompMultiSys = _make_comp_ms_class(CompSys) + +# LabeledMultiSystems comparison from single LabeledSystem comparison +CompLabeledMultiSys = _make_comp_ms_class(CompLabeledSys) + + class MultiSystems: def test_systems_name(self): self.assertEqual(set(self.systems.systems), set(self.system_names)) @@ -127,3 +193,21 @@ class IsNoPBC: def test_is_nopbc(self): self.assertTrue(self.system_1.nopbc) self.assertTrue(self.system_2.nopbc) + + +class MSAllIsPBC: + def test_is_pbc(self): + assert hasattr(self, "ms_1") and hasattr( + self, "ms_2" + ), "Multi-system objects must be present and iterable" + self.assertTrue(all([not ss.nopbc for ss in self.ms_1])) + self.assertTrue(all([not ss.nopbc for ss in self.ms_2])) + + +class MSAllIsNoPBC: + def test_is_nopbc(self): + assert hasattr(self, "ms_1") and hasattr( + self, "ms_2" + ), "Multi-system objects must be present and iterable" + self.assertTrue(all([ss.nopbc for ss in self.ms_1])) + self.assertTrue(all([ss.nopbc for ss in self.ms_2])) diff --git a/tests/test_deepmd_mixed.py b/tests/test_deepmd_mixed.py index 70a09dbe..3c28d1b1 100644 --- a/tests/test_deepmd_mixed.py +++ b/tests/test_deepmd_mixed.py @@ -6,12 +6,18 @@ from glob import glob import numpy as np -from comp_sys import CompLabeledSys, IsNoPBC, MultiSystems +from comp_sys import ( + CompLabeledMultiSys, + CompLabeledSys, + IsNoPBC, + MSAllIsNoPBC, + MultiSystems, +) from context import dpdata class TestMixedMultiSystemsDumpLoad( - unittest.TestCase, CompLabeledSys, MultiSystems, IsNoPBC + unittest.TestCase, CompLabeledMultiSys, MultiSystems, MSAllIsNoPBC ): def setUp(self): self.places = 6 @@ -62,8 +68,8 @@ def setUp(self): self.place_holder_ms.from_deepmd_npy("tmp.deepmd.mixed", fmt="deepmd/npy") self.systems = dpdata.MultiSystems() self.systems.from_deepmd_npy_mixed("tmp.deepmd.mixed", fmt="deepmd/npy/mixed") - self.system_1 = self.ms["C1H4A0B0D0"] - self.system_2 = self.systems["C1H4A0B0D0"] + self.ms_1 = self.ms + self.ms_2 = self.systems mixed_sets = glob("tmp.deepmd.mixed/*/set.*") self.assertEqual(len(mixed_sets), 2) for i in mixed_sets: @@ -112,7 +118,7 @@ def test_str(self): class TestMixedMultiSystemsDumpLoadSetSize( - unittest.TestCase, CompLabeledSys, MultiSystems, IsNoPBC + unittest.TestCase, CompLabeledMultiSys, MultiSystems, MSAllIsNoPBC ): def setUp(self): self.places = 6 @@ -163,8 +169,8 @@ def setUp(self): self.place_holder_ms.from_deepmd_npy("tmp.deepmd.mixed", fmt="deepmd/npy") self.systems = dpdata.MultiSystems() self.systems.from_deepmd_npy_mixed("tmp.deepmd.mixed", fmt="deepmd/npy/mixed") - self.system_1 = self.ms["C1H4A0B0D0"] - self.system_2 = self.systems["C1H4A0B0D0"] + self.ms_1 = self.ms + self.ms_2 = self.systems mixed_sets = glob("tmp.deepmd.mixed/*/set.*") self.assertEqual(len(mixed_sets), 5) for i in mixed_sets: @@ -213,7 +219,7 @@ def test_str(self): class TestMixedMultiSystemsTypeChange( - unittest.TestCase, CompLabeledSys, MultiSystems, IsNoPBC + unittest.TestCase, CompLabeledMultiSys, MultiSystems, MSAllIsNoPBC ): def setUp(self): self.places = 6 @@ -265,8 +271,8 @@ def setUp(self): self.place_holder_ms.from_deepmd_npy("tmp.deepmd.mixed", fmt="deepmd/npy") self.systems = dpdata.MultiSystems(type_map=["TOKEN"]) self.systems.from_deepmd_npy_mixed("tmp.deepmd.mixed", fmt="deepmd/npy/mixed") - self.system_1 = self.ms["TOKEN0C1H4A0B0D0"] - self.system_2 = self.systems["TOKEN0C1H4A0B0D0"] + self.ms_1 = self.ms + self.ms_2 = self.systems mixed_sets = glob("tmp.deepmd.mixed/*/set.*") self.assertEqual(len(mixed_sets), 2) for i in mixed_sets: From 6d082f162dc05c0980567a7544a8024b7c024a78 Mon Sep 17 00:00:00 2001 From: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> Date: Sat, 31 Aug 2024 18:39:10 +0800 Subject: [PATCH 07/33] test: mixed data format: test if the index_map (when type_map is provided) works (#706) consider the PR after #705 ## Summary by CodeRabbit - **New Features** - Enhanced testing framework for multi-system comparisons, allowing for dynamic generation of test functions and classes. - Introduced new test classes to validate properties of multi-system objects regarding periodic boundary conditions. - Added a new test class for handling type mapping in labeled systems. - **Bug Fixes** - Updated existing test classes to improve clarity and consistency in naming conventions for multi-system variables. --------- Co-authored-by: Han Wang --- tests/test_deepmd_mixed.py | 113 +++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/tests/test_deepmd_mixed.py b/tests/test_deepmd_mixed.py index 3c28d1b1..9f0c5ed4 100644 --- a/tests/test_deepmd_mixed.py +++ b/tests/test_deepmd_mixed.py @@ -117,6 +117,119 @@ def test_str(self): ) +class TestMixedMultiSystemsDumpLoadTypeMap( + unittest.TestCase, CompLabeledMultiSys, MultiSystems, MSAllIsNoPBC +): + def setUp(self): + self.places = 6 + self.e_places = 6 + self.f_places = 6 + self.v_places = 6 + + # C1H4 + system_1 = dpdata.LabeledSystem( + "gaussian/methane.gaussianlog", fmt="gaussian/log" + ) + + # C1H3 + system_2 = dpdata.LabeledSystem( + "gaussian/methane_sub.gaussianlog", fmt="gaussian/log" + ) + + tmp_data = system_1.data.copy() + tmp_data["atom_numbs"] = [1, 1, 1, 2] + tmp_data["atom_names"] = ["C", "H", "A", "B"] + tmp_data["atom_types"] = np.array([0, 1, 2, 3, 3]) + # C1H1A1B2 + system_1_modified_type_1 = dpdata.LabeledSystem(data=tmp_data) + + tmp_data = system_1.data.copy() + tmp_data["atom_numbs"] = [1, 1, 2, 1] + tmp_data["atom_names"] = ["C", "H", "A", "B"] + tmp_data["atom_types"] = np.array([0, 1, 2, 2, 3]) + # C1H1A2B1 + system_1_modified_type_2 = dpdata.LabeledSystem(data=tmp_data) + + tmp_data = system_1.data.copy() + tmp_data["atom_numbs"] = [1, 1, 1, 2] + tmp_data["atom_names"] = ["C", "H", "A", "D"] + tmp_data["atom_types"] = np.array([0, 1, 2, 3, 3]) + # C1H1A1C2 + system_1_modified_type_3 = dpdata.LabeledSystem(data=tmp_data) + + self.ms = dpdata.MultiSystems( + system_1, + system_2, + system_1_modified_type_1, + system_1_modified_type_2, + system_1_modified_type_3, + ) + + self.ms.to_deepmd_npy_mixed("tmp.deepmd.mixed") + self.place_holder_ms = dpdata.MultiSystems() + self.place_holder_ms.from_deepmd_npy("tmp.deepmd.mixed", fmt="deepmd/npy") + + new_type_map = ["H", "C", "D", "A", "B"] + self.systems = dpdata.MultiSystems() + self.systems.from_deepmd_npy_mixed( + "tmp.deepmd.mixed", fmt="deepmd/npy/mixed", type_map=new_type_map + ) + for kk in [ii.formula for ii in self.ms]: + # apply type_map to each system + self.ms[kk].apply_type_map(new_type_map) + # revise keys in dict according because the type_map is updated. + tmp_ss = self.ms.systems.pop(kk) + self.ms.systems[tmp_ss.formula] = tmp_ss + + self.ms_1 = self.ms + self.ms_2 = self.systems + mixed_sets = glob("tmp.deepmd.mixed/*/set.*") + self.assertEqual(len(mixed_sets), 2) + for i in mixed_sets: + self.assertEqual( + os.path.exists(os.path.join(i, "real_atom_types.npy")), True + ) + + self.system_names = [ + "H4C1D0A0B0", + "H3C1D0A0B0", + "H1C1D0A1B2", + "H1C1D0A2B1", + "H1C1D2A1B0", + ] + self.system_sizes = { + "H4C1D0A0B0": 1, + "H3C1D0A0B0": 1, + "H1C1D0A1B2": 1, + "H1C1D0A2B1": 1, + "H1C1D2A1B0": 1, + } + self.atom_names = ["H", "C", "D", "A", "B"] + + def tearDown(self): + if os.path.exists("tmp.deepmd.mixed"): + shutil.rmtree("tmp.deepmd.mixed") + + def test_len(self): + self.assertEqual(len(self.ms), 5) + self.assertEqual(len(self.place_holder_ms), 2) + self.assertEqual(len(self.systems), 5) + + def test_get_nframes(self): + self.assertEqual(self.ms.get_nframes(), 5) + self.assertEqual(self.place_holder_ms.get_nframes(), 5) + self.assertEqual(self.systems.get_nframes(), 5) + + def test_str(self): + self.assertEqual(str(self.ms), "MultiSystems (5 systems containing 5 frames)") + self.assertEqual( + str(self.place_holder_ms), "MultiSystems (2 systems containing 5 frames)" + ) + self.assertEqual( + str(self.systems), "MultiSystems (5 systems containing 5 frames)" + ) + + class TestMixedMultiSystemsDumpLoadSetSize( unittest.TestCase, CompLabeledMultiSys, MultiSystems, MSAllIsNoPBC ): From 35fdbb8df25e79fd50c072f17ec2ddf903bcb9d9 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 31 Aug 2024 07:59:26 -0400 Subject: [PATCH 08/33] fix: reuse regular methods for deepmd/mixed (#704) ## Summary by CodeRabbit - **New Features** - Streamlined data loading process with improved handling of atom types. - Enhanced modularization in data dumping, reducing complexity and improving maintainability. - Added a new test for verifying data conversion and loading processes for mixed format systems. - **Bug Fixes** - Improved clarity and efficiency in processing atom names and counts. --------- Signed-off-by: Jinzhe Zeng Co-authored-by: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> --- dpdata/deepmd/comp.py | 2 - dpdata/deepmd/mixed.py | 179 ++++----------------------------- dpdata/deepmd/raw.py | 10 +- tests/test_custom_data_type.py | 10 ++ 4 files changed, 31 insertions(+), 170 deletions(-) diff --git a/dpdata/deepmd/comp.py b/dpdata/deepmd/comp.py index d6ccf3ff..66111016 100644 --- a/dpdata/deepmd/comp.py +++ b/dpdata/deepmd/comp.py @@ -79,7 +79,6 @@ def to_system_data(folder, type_map=None, labels=True): "orig", "cells", "coords", - "real_atom_types", "real_atom_names", "nopbc", "energies", @@ -189,7 +188,6 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): "orig", "cells", "coords", - "real_atom_types", "real_atom_names", "nopbc", "energies", diff --git a/dpdata/deepmd/mixed.py b/dpdata/deepmd/mixed.py index ccf9b2f5..2aff3a28 100644 --- a/dpdata/deepmd/mixed.py +++ b/dpdata/deepmd/mixed.py @@ -1,54 +1,16 @@ from __future__ import annotations import copy -import glob -import os -import shutil import numpy as np - -def load_type(folder): - data = {} - data["atom_names"] = [] - # if find type_map.raw, use it - assert os.path.isfile( - os.path.join(folder, "type_map.raw") - ), "Mixed type system must have type_map.raw!" - with open(os.path.join(folder, "type_map.raw")) as fp: - data["atom_names"] = fp.read().split() - - return data - - -def formula(atom_names, atom_numbs): - """Return the formula of this system, like C3H5O2.""" - return "".join([f"{symbol}{numb}" for symbol, numb in zip(atom_names, atom_numbs)]) - - -def _cond_load_data(fname): - tmp = None - if os.path.isfile(fname): - tmp = np.load(fname) - return tmp - - -def _load_set(folder, nopbc: bool): - coords = np.load(os.path.join(folder, "coord.npy")) - if nopbc: - cells = np.zeros((coords.shape[0], 3, 3)) - else: - cells = np.load(os.path.join(folder, "box.npy")) - eners = _cond_load_data(os.path.join(folder, "energy.npy")) - forces = _cond_load_data(os.path.join(folder, "force.npy")) - virs = _cond_load_data(os.path.join(folder, "virial.npy")) - real_atom_types = np.load(os.path.join(folder, "real_atom_types.npy")) - return cells, coords, eners, forces, virs, real_atom_types +from .comp import dump as comp_dump +from .comp import to_system_data as comp_to_system_data def to_system_data(folder, type_map=None, labels=True): + data = comp_to_system_data(folder, type_map, labels) # data is empty - data = load_type(folder) old_type_map = data["atom_names"].copy() if type_map is not None: assert isinstance(type_map, list) @@ -60,50 +22,16 @@ def to_system_data(folder, type_map=None, labels=True): data["atom_names"] = type_map.copy() else: index_map = None - data["orig"] = np.zeros([3]) - if os.path.isfile(os.path.join(folder, "nopbc")): - data["nopbc"] = True - sets = sorted(glob.glob(os.path.join(folder, "set.*"))) - all_cells = [] - all_coords = [] - all_eners = [] - all_forces = [] - all_virs = [] - all_real_atom_types = [] - for ii in sets: - cells, coords, eners, forces, virs, real_atom_types = _load_set( - ii, data.get("nopbc", False) - ) - nframes = np.reshape(cells, [-1, 3, 3]).shape[0] - all_cells.append(np.reshape(cells, [nframes, 3, 3])) - all_coords.append(np.reshape(coords, [nframes, -1, 3])) - if index_map is None: - all_real_atom_types.append(np.reshape(real_atom_types, [nframes, -1])) - else: - all_real_atom_types.append( - np.reshape(index_map[real_atom_types], [nframes, -1]) - ) - if eners is not None: - eners = np.reshape(eners, [nframes]) - if labels: - if eners is not None and eners.size > 0: - all_eners.append(np.reshape(eners, [nframes])) - if forces is not None and forces.size > 0: - all_forces.append(np.reshape(forces, [nframes, -1, 3])) - if virs is not None and virs.size > 0: - all_virs.append(np.reshape(virs, [nframes, 3, 3])) - all_cells_concat = np.concatenate(all_cells, axis=0) - all_coords_concat = np.concatenate(all_coords, axis=0) - all_real_atom_types_concat = np.concatenate(all_real_atom_types, axis=0) - all_eners_concat = None - all_forces_concat = None - all_virs_concat = None - if len(all_eners) > 0: - all_eners_concat = np.concatenate(all_eners, axis=0) - if len(all_forces) > 0: - all_forces_concat = np.concatenate(all_forces, axis=0) - if len(all_virs) > 0: - all_virs_concat = np.concatenate(all_virs, axis=0) + all_real_atom_types_concat = data.pop("real_atom_types").astype(int) + if index_map is not None: + all_real_atom_types_concat = index_map[all_real_atom_types_concat] + all_cells_concat = data["cells"] + all_coords_concat = data["coords"] + if labels: + all_eners_concat = data.get("energies") + all_forces_concat = data.get("forces") + all_virs_concat = data.get("virials") + data_list = [] while True: if all_real_atom_types_concat.size == 0: @@ -143,20 +71,6 @@ def to_system_data(folder, type_map=None, labels=True): def dump(folder, data, set_size=2000, comp_prec=np.float32, remove_sets=True): - os.makedirs(folder, exist_ok=True) - sets = sorted(glob.glob(os.path.join(folder, "set.*"))) - if len(sets) > 0: - if remove_sets: - for ii in sets: - shutil.rmtree(ii) - else: - raise RuntimeError( - "found " - + str(sets) - + " in " - + folder - + "not a clean deepmd raw dir. please firstly clean set.* then try compress" - ) # if not converted to mixed if "real_atom_types" not in data: from dpdata import LabeledSystem, System @@ -169,69 +83,10 @@ def dump(folder, data, set_size=2000, comp_prec=np.float32, remove_sets=True): else: temp_sys = System(data=data) temp_sys.convert_to_mixed_type() - # dump raw - np.savetxt(os.path.join(folder, "type.raw"), data["atom_types"], fmt="%d") - np.savetxt(os.path.join(folder, "type_map.raw"), data["real_atom_names"], fmt="%s") - # BondOrder System - if "bonds" in data: - np.savetxt( - os.path.join(folder, "bonds.raw"), - data["bonds"], - header="begin_atom, end_atom, bond_order", - ) - if "formal_charges" in data: - np.savetxt(os.path.join(folder, "formal_charges.raw"), data["formal_charges"]) - # reshape frame properties and convert prec - nframes = data["cells"].shape[0] - cells = np.reshape(data["cells"], [nframes, 9]).astype(comp_prec) - coords = np.reshape(data["coords"], [nframes, -1]).astype(comp_prec) - eners = None - forces = None - virials = None - real_atom_types = None - if "energies" in data: - eners = np.reshape(data["energies"], [nframes]).astype(comp_prec) - if "forces" in data: - forces = np.reshape(data["forces"], [nframes, -1]).astype(comp_prec) - if "virials" in data: - virials = np.reshape(data["virials"], [nframes, 9]).astype(comp_prec) - if "atom_pref" in data: - atom_pref = np.reshape(data["atom_pref"], [nframes, -1]).astype(comp_prec) - if "real_atom_types" in data: - real_atom_types = np.reshape(data["real_atom_types"], [nframes, -1]).astype( - np.int64 - ) - # dump frame properties: cell, coord, energy, force and virial - nsets = nframes // set_size - if set_size * nsets < nframes: - nsets += 1 - for ii in range(nsets): - set_stt = ii * set_size - set_end = (ii + 1) * set_size - set_folder = os.path.join(folder, "set.%06d" % ii) - os.makedirs(set_folder) - np.save(os.path.join(set_folder, "box"), cells[set_stt:set_end]) - np.save(os.path.join(set_folder, "coord"), coords[set_stt:set_end]) - if eners is not None: - np.save(os.path.join(set_folder, "energy"), eners[set_stt:set_end]) - if forces is not None: - np.save(os.path.join(set_folder, "force"), forces[set_stt:set_end]) - if virials is not None: - np.save(os.path.join(set_folder, "virial"), virials[set_stt:set_end]) - if real_atom_types is not None: - np.save( - os.path.join(set_folder, "real_atom_types"), - real_atom_types[set_stt:set_end], - ) - if "atom_pref" in data: - np.save(os.path.join(set_folder, "atom_pref"), atom_pref[set_stt:set_end]) - try: - os.remove(os.path.join(folder, "nopbc")) - except OSError: - pass - if data.get("nopbc", False): - with open(os.path.join(folder, "nopbc"), "w") as fw_nopbc: - pass + + data = data.copy() + data["atom_names"] = data.pop("real_atom_names") + comp_dump(folder, data, set_size, comp_prec, remove_sets) def mix_system(*system, type_map, **kwargs): diff --git a/dpdata/deepmd/raw.py b/dpdata/deepmd/raw.py index 11a9ff6e..717c73f9 100644 --- a/dpdata/deepmd/raw.py +++ b/dpdata/deepmd/raw.py @@ -14,9 +14,6 @@ def load_type(folder, type_map=None): int ) ntypes = np.max(data["atom_types"]) + 1 - data["atom_numbs"] = [] - for ii in range(ntypes): - data["atom_numbs"].append(np.count_nonzero(data["atom_types"] == ii)) data["atom_names"] = [] # if find type_map.raw, use it if os.path.isfile(os.path.join(folder, "type_map.raw")): @@ -30,9 +27,10 @@ def load_type(folder, type_map=None): my_type_map = [] for ii in range(ntypes): my_type_map.append("Type_%d" % ii) - assert len(my_type_map) >= len(data["atom_numbs"]) - for ii in range(len(data["atom_numbs"])): - data["atom_names"].append(my_type_map[ii]) + data["atom_names"] = my_type_map + data["atom_numbs"] = [] + for ii, _ in enumerate(data["atom_names"]): + data["atom_numbs"].append(np.count_nonzero(data["atom_types"] == ii)) return data diff --git a/tests/test_custom_data_type.py b/tests/test_custom_data_type.py index a319af35..230475ab 100644 --- a/tests/test_custom_data_type.py +++ b/tests/test_custom_data_type.py @@ -57,6 +57,16 @@ def test_duplicated_data_type(self): n_dtypes_new = len(self.cls.DTYPES) self.assertEqual(n_dtypes_old, n_dtypes_new) + def test_to_deepmd_npy_mixed(self): + ms = dpdata.MultiSystems(self.system) + ms.to_deepmd_npy_mixed("data_foo_mixed") + x = dpdata.MultiSystems().load_systems_from_file( + "data_foo_mixed", + fmt="deepmd/npy/mixed", + labeled=issubclass(self.cls, dpdata.LabeledSystem), + ) + np.testing.assert_allclose(list(x.systems.values())[0].data["foo"], self.foo) + class TestDeepmdLoadDumpCompUnlabeled(unittest.TestCase, DeepmdLoadDumpCompTest): cls = dpdata.System From f4d0adf8ca3aceab813f0bab0fdaeb9b55bf6ed6 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Sat, 31 Aug 2024 08:45:45 -0400 Subject: [PATCH 09/33] docs: reformat README and documentation (#707) Move most of the contents from `README.md` to `docs`. ## Summary by CodeRabbit - **New Features** - Introduced `BondOrderSystem` class for managing chemical bonds and formal charges. - Added `MultiSystems` class for handling multiple system data from files or directories. - Implemented new `deepmd/npy/mixed` format for combining systems with different formulas. - Launched plugin documentation to enhance extensibility for user-created plugins. - **Documentation** - Updated README for clearer installation instructions and streamlined content. - Created dedicated installation guide for user-friendly setup. - Enhanced documentation structure with new entries and improved navigation. --------- Signed-off-by: Jinzhe Zeng --- README.md | 323 +++--------------------------- docs/index.rst | 9 +- docs/installation.md | 13 ++ docs/plugin.md | 9 + docs/systems/bond_order_system.md | 51 +++++ docs/systems/index.rst | 11 + docs/systems/mixed.md | 24 +++ docs/systems/multi.md | 62 ++++++ docs/systems/system.md | 112 +++++++++++ 9 files changed, 312 insertions(+), 302 deletions(-) create mode 100644 docs/installation.md create mode 100644 docs/plugin.md create mode 100644 docs/systems/bond_order_system.md create mode 100644 docs/systems/index.rst create mode 100644 docs/systems/mixed.md create mode 100644 docs/systems/multi.md create mode 100644 docs/systems/system.md diff --git a/README.md b/README.md index ab489a75..6f9246b1 100644 --- a/README.md +++ b/README.md @@ -4,320 +4,47 @@ [![pip install](https://img.shields.io/pypi/dm/dpdata?label=pip%20install&logo=pypi)](https://pypi.org/project/dpdata) [![Documentation Status](https://readthedocs.org/projects/dpdata/badge/)](https://dpdata.readthedocs.io/) -**dpdata** is a python package for manipulating data formats of software in computational science, including DeePMD-kit, VASP, LAMMPS, GROMACS, Gaussian. -dpdata only works with python 3.7 or above. - +**dpdata** is a Python package for manipulating atomistic data of software in computational science. ## Installation -One can download the source code of dpdata by -```bash -git clone https://github.com/deepmodeling/dpdata.git dpdata -``` -then use `pip` to install the module from source -```bash -cd dpdata -pip install . -``` - -`dpdata` can also by install via pip without source -```bash -pip install dpdata -``` - -## Quick start - -This section gives some examples on how dpdata works. Firstly one needs to import the module in a python 3.x compatible code. -```python -import dpdata -``` -The typicall workflow of `dpdata` is - -1. Load data from vasp or lammps or deepmd-kit data files. -2. Manipulate data -3. Dump data to in a desired format - - -### Load data -```python -d_poscar = dpdata.System("POSCAR", fmt="vasp/poscar") -``` -or let dpdata infer the format (`vasp/poscar`) of the file from the file name extension -```python -d_poscar = dpdata.System("my.POSCAR") -``` -The number of atoms, atom types, coordinates are loaded from the `POSCAR` and stored to a data `System` called `d_poscar`. -A data `System` (a concept used by [deepmd-kit](https://github.com/deepmodeling/deepmd-kit)) contains frames that has the same number of atoms of the same type. The order of the atoms should be consistent among the frames in one `System`. -It is noted that `POSCAR` only contains one frame. -If the multiple frames stored in, for example, a `OUTCAR` is wanted, -```python -d_outcar = dpdata.LabeledSystem("OUTCAR") -``` -The labels provided in the `OUTCAR`, i.e. energies, forces and virials (if any), are loaded by `LabeledSystem`. It is noted that the forces of atoms are always assumed to exist. `LabeledSystem` is a derived class of `System`. +DP-GEN only supports Python 3.7 and above. You can [setup a conda/pip environment](https://docs.deepmodeling.com/faq/conda.html), and then use one of the following methods to install DP-GEN: -The `System` or `LabeledSystem` can be constructed from the following file formats with the `format key` in the table passed to argument `fmt`: +- Install via pip: `pip install dpdata` +- Install via conda: `conda install -c conda-forge dpdata` +- Install from source code: `git clone https://github.com/deepmodeling/dpdata && pip install ./dpdata` -| Software| format | multi frames | labeled | class | format key | -| ------- | :--- | :---: | :---: | :--- | :--- | -| vasp | poscar | False | False | System | 'vasp/poscar' | -| vasp | outcar | True | True | LabeledSystem | 'vasp/outcar' | -| vasp | xml | True | True | LabeledSystem | 'vasp/xml' | -| lammps | lmp | False | False | System | 'lammps/lmp' | -| lammps | dump | True | False | System | 'lammps/dump' | -| deepmd | raw | True | False | System | 'deepmd/raw' | -| deepmd | npy | True | False | System | 'deepmd/npy' | -| deepmd | raw | True | True | LabeledSystem | 'deepmd/raw' | -| deepmd | npy | True | True | LabeledSystem | 'deepmd/npy' | -| deepmd | npy | True | True | MultiSystems | 'deepmd/npy/mixed' | -| deepmd | npy | True | False | MultiSystems | 'deepmd/npy/mixed' | -| gaussian| log | False | True | LabeledSystem | 'gaussian/log'| -| gaussian| log | True | True | LabeledSystem | 'gaussian/md' | -| siesta | output | False | True | LabeledSystem | 'siesta/output'| -| siesta | aimd_output | True | True | LabeledSystem | 'siesta/aimd_output' | -| cp2k(deprecated in future) | output | False | True | LabeledSystem | 'cp2k/output' | -| cp2k(deprecated in future) | aimd_output | True | True | LabeledSystem | 'cp2k/aimd_output' | -| cp2k([plug-in](https://github.com/robinzyb/cp2kdata#plug-in-for-dpdata)) | stdout | False | True | LabeledSystem | 'cp2kdata/e_f' | -| cp2k([plug-in](https://github.com/robinzyb/cp2kdata#plug-in-for-dpdata)) | stdout | True | True | LabeledSystem | 'cp2kdata/md' | -| QE | log | False | True | LabeledSystem | 'qe/pw/scf' | -| QE | log | True | False | System | 'qe/cp/traj' | -| QE | log | True | True | LabeledSystem | 'qe/cp/traj' | -| Fhi-aims| output | True | True | LabeledSystem | 'fhi_aims/md' | -| Fhi-aims| output | False | True | LabeledSystem | 'fhi_aims/scf' | -|quip/gap|xyz|True|True|MultiSystems|'quip/gap/xyz'| -| PWmat | atom.config | False | False | System | 'pwmat/atom.config' | -| PWmat | movement | True | True | LabeledSystem | 'pwmat/movement' | -| PWmat | OUT.MLMD | True | True | LabeledSystem | 'pwmat/out.mlmd' | -| Amber | multi | True | True | LabeledSystem | 'amber/md' | -| Amber/sqm | sqm.out | False | False | System | 'sqm/out' | -| Gromacs | gro | True | False | System | 'gromacs/gro' | -| ABACUS | STRU | False | False | System | 'abacus/stru' | -| ABACUS | STRU | False | True | LabeledSystem | 'abacus/scf' | -| ABACUS | cif | True | True | LabeledSystem | 'abacus/md' | -| ABACUS | STRU | True | True | LabeledSystem | 'abacus/relax' | -| ase | structure | True | True | MultiSystems | 'ase/structure' | -| DFTB+ | dftbplus | False | True | LabeledSystem | 'dftbplus' | -| n2p2 | n2p2 | True | True | LabeledSystem | 'n2p2' | - - -The Class `dpdata.MultiSystems` can read data from a dir which may contains many files of different systems, or from single xyz file which contains different systems. - -Use `dpdata.MultiSystems.from_dir` to read from a directory, `dpdata.MultiSystems` will walk in the directory -Recursively and find all file with specific file_name. Supports all the file formats that `dpdata.LabeledSystem` supports. - -Use `dpdata.MultiSystems.from_file` to read from single file. Single-file support is available for the `quip/gap/xyz` and `ase/structure` formats. - -For example, for `quip/gap xyz` files, single .xyz file may contain many different configurations with different atom numbers and atom type. - -The following commands relating to `Class dpdata.MultiSystems` may be useful. -```python -# load data - -xyz_multi_systems = dpdata.MultiSystems.from_file( - file_name="tests/xyz/xyz_unittest.xyz", fmt="quip/gap/xyz" -) -vasp_multi_systems = dpdata.MultiSystems.from_dir( - dir_name="./mgal_outcar", file_name="OUTCAR", fmt="vasp/outcar" -) - -# use wildcard -vasp_multi_systems = dpdata.MultiSystems.from_dir( - dir_name="./mgal_outcar", file_name="*OUTCAR", fmt="vasp/outcar" -) - -# print the multi_system infomation -print(xyz_multi_systems) -print(xyz_multi_systems.systems) # return a dictionaries - -# print the system infomation -print(xyz_multi_systems.systems["B1C9"].data) - -# dump a system's data to ./my_work_dir/B1C9_raw folder -xyz_multi_systems.systems["B1C9"].to_deepmd_raw("./my_work_dir/B1C9_raw") - -# dump all systems -xyz_multi_systems.to_deepmd_raw("./my_deepmd_data/") -``` - -You may also use the following code to parse muti-system: -```python -from dpdata import LabeledSystem, MultiSystems -from glob import glob - -""" -process multi systems -""" -fs = glob("./*/OUTCAR") # remeber to change here !!! -ms = MultiSystems() -for f in fs: - try: - ls = LabeledSystem(f) - except: - print(f) - if len(ls) > 0: - ms.append(ls) - -ms.to_deepmd_raw("deepmd") -ms.to_deepmd_npy("deepmd") -``` +To test if the installation is successful, you may execute -### Access data -These properties stored in `System` and `LabeledSystem` can be accessed by operator `[]` with the key of the property supplied, for example -```python -coords = d_outcar["coords"] -``` -Available properties are (nframe: number of frames in the system, natoms: total number of atoms in the system) - -| key | type | dimension | are labels | description -| --- | --- | --- | --- | --- -| 'atom_names' | list of str | ntypes | False | The name of each atom type -| 'atom_numbs' | list of int | ntypes | False | The number of atoms of each atom type -| 'atom_types' | np.ndarray | natoms | False | Array assigning type to each atom -| 'cells' | np.ndarray | nframes x 3 x 3 | False | The cell tensor of each frame -| 'coords' | np.ndarray | nframes x natoms x 3 | False | The atom coordinates -| 'energies' | np.ndarray | nframes | True | The frame energies -| 'forces' | np.ndarray | nframes x natoms x 3 | True | The atom forces -| 'virials' | np.ndarray | nframes x 3 x 3 | True | The virial tensor of each frame - - -### Dump data -The data stored in `System` or `LabeledSystem` can be dumped in 'lammps/lmp' or 'vasp/poscar' format, for example: -```python -d_outcar.to("lammps/lmp", "conf.lmp", frame_idx=0) -``` -The first frames of `d_outcar` will be dumped to 'conf.lmp' -```python -d_outcar.to("vasp/poscar", "POSCAR", frame_idx=-1) -``` -The last frames of `d_outcar` will be dumped to 'POSCAR'. - -The data stored in `LabeledSystem` can be dumped to deepmd-kit raw format, for example -```python -d_outcar.to("deepmd/raw", "dpmd_raw") -``` -Or a simpler command: -```python -dpdata.LabeledSystem("OUTCAR").to("deepmd/raw", "dpmd_raw") -``` -Frame selection can be implemented by -```python -dpdata.LabeledSystem("OUTCAR").sub_system([0, -1]).to("deepmd/raw", "dpmd_raw") -``` -by which only the first and last frames are dumped to `dpmd_raw`. - - -### replicate -dpdata will create a super cell of the current atom configuration. -```python -dpdata.System("./POSCAR").replicate( - ( - 1, - 2, - 3, - ) -) -``` -tuple(1,2,3) means don't copy atom configuration in x direction, make 2 copys in y direction, make 3 copys in z direction. - - -### perturb -By the following example, each frame of the original system (`dpdata.System('./POSCAR')`) is perturbed to generate three new frames. For each frame, the cell is perturbed by 5% and the atom positions are perturbed by 0.6 Angstrom. `atom_pert_style` indicates that the perturbation to the atom positions is subject to normal distribution. Other available options to `atom_pert_style` are`uniform` (uniform in a ball), and `const` (uniform on a sphere). -```python -perturbed_system = dpdata.System("./POSCAR").perturb( - pert_num=3, - cell_pert_fraction=0.05, - atom_pert_distance=0.6, - atom_pert_style="normal", -) -print(perturbed_system.data) -``` - -### replace -By the following example, Random 8 Hf atoms in the system will be replaced by Zr atoms with the atom postion unchanged. -```python -s = dpdata.System("tests/poscars/POSCAR.P42nmc", fmt="vasp/poscar") -s.replace("Hf", "Zr", 8) -s.to_vasp_poscar("POSCAR.P42nmc.replace") -``` - -## BondOrderSystem -A new class `BondOrderSystem` which inherits from class `System` is introduced in dpdata. This new class contains information of chemical bonds and formal charges (stored in `BondOrderSystem.data['bonds']`, `BondOrderSystem.data['formal_charges']`). Now BondOrderSystem can only read from .mol/.sdf formats, because of its dependency on rdkit (which means rdkit must be installed if you want to use this function). Other formats, such as pdb, must be converted to .mol/.sdf format (maybe with software like open babel). -```python -import dpdata - -system_1 = dpdata.BondOrderSystem( - "tests/bond_order/CH3OH.mol", fmt="mol" -) # read from .mol file -system_2 = dpdata.BondOrderSystem( - "tests/bond_order/methane.sdf", fmt="sdf" -) # read from .sdf file -``` -In sdf file, all molecules must be of the same topology (i.e. conformers of the same molecular configuration). -`BondOrderSystem` also supports initialize from a `rdkit.Chem.rdchem.Mol` object directly. -```python -from rdkit import Chem -from rdkit.Chem import AllChem -import dpdata - -mol = Chem.MolFromSmiles("CC") -mol = Chem.AddHs(mol) -AllChem.EmbedMultipleConfs(mol, 10) -system = dpdata.BondOrderSystem(rdkit_mol=mol) -``` - -### Bond Order Assignment -The `BondOrderSystem` implements a more robust sanitize procedure for rdkit Mol, as defined in `dpdata.rdkit.santizie.Sanitizer`. This class defines 3 level of sanitization process by: low, medium and high. (default is medium). -+ low: use `rdkit.Chem.SanitizeMol()` function to sanitize molecule. -+ medium: before using rdkit, the programm will first assign formal charge of each atom to avoid inappropriate valence exceptions. However, this mode requires the rightness of the bond order information in the given molecule. -+ high: the program will try to fix inappropriate bond orders in aromatic hetreocycles, phosphate, sulfate, carboxyl, nitro, nitrine, guanidine groups. If this procedure fails to sanitize the given molecule, the program will then try to call `obabel` to pre-process the mol and repeat the sanitization procedure. **That is to say, if you wan't to use this level of sanitization, please ensure `obabel` is installed in the environment.** -According to our test, our sanitization procedure can successfully read 4852 small molecules in the PDBBind-refined-set. It is necessary to point out that the in the molecule file (mol/sdf), the number of explicit hydrogens has to be correct. Thus, we recommend to use - `obabel xxx -O xxx -h` to pre-process the file. The reason why we do not implement this hydrogen-adding procedure in dpdata is that we can not ensure its correctness. - -```python -import dpdata - -for sdf_file in glob.glob("bond_order/refined-set-ligands/obabel/*sdf"): - syst = dpdata.BondOrderSystem(sdf_file, sanitize_level="high", verbose=False) -``` -### Formal Charge Assignment -BondOrderSystem implement a method to assign formal charge for each atom based on the 8-electron rule (see below). Note that it only supports common elements in bio-system: B,C,N,O,P,S,As -```python -import dpdata - -syst = dpdata.BondOrderSystem("tests/bond_order/CH3NH3+.mol", fmt="mol") -print(syst.get_formal_charges()) # return the formal charge on each atom -print(syst.get_charge()) # return the total charge of the system +```bash +dpdata --version ``` -If a valence of 3 is detected on carbon, the formal charge will be assigned to -1. Because for most cases (in alkynyl anion, isonitrile, cyclopentadienyl anion), the formal charge on 3-valence carbon is -1, and this is also consisent with the 8-electron rule. +## Supported packages -## Mixed Type Format -The format `deepmd/npy/mixed` is the mixed type numpy format for DeePMD-kit, and can be loaded or dumped through class `dpdata.MultiSystems`. +`dpdata` is aimmed to support different kinds of atomistic packages: -Under this format, systems with the same number of atoms but different formula can be put together -for a larger system, especially when the frame numbers in systems are sparse. +- Atomistic machine learning packages, such as [DeePMD-kit](https://github.com/deepmodeling/deepmd-kit); +- Molecular dynamics packages, such as [LAMMPS](https://github.com/lammps/lammps) and [GROMACS](https://gitlab.com/gromacs/gromacs); +- Quantum chemistry packages, such as [VASP](https://www.vasp.at/), [Gaussian](https://gaussian.com), and [ABACUS](https://github.com/deepmodeling/abacus-develop); +- Atomistic visualization packages, such as [3Dmol.js](https://3dmol.csb.pitt.edu/). +- Other atomistic tools, such as [ASE](https://gitlab.com/ase/ase). +- Common formats such as `xyz`. -This also helps to mixture the type information together for model training with type embedding network. +All supported formats are listed [here](https://docs.deepmodeling.com/projects/dpdata/en/master/formats.html). -Here are examples using `deepmd/npy/mixed` format: +## Quick start -- Dump a MultiSystems into a mixed type numpy directory: -```python -import dpdata +The quickest way to convert a simple file from one format to another one is to use the [command line](https://docs.deepmodeling.com/projects/dpdata/en/master/cli.html). -dpdata.MultiSystems(*systems).to_deepmd_npy_mixed("mixed_dir") +```sh +dpdata OUTCAR -i vasp/outcar -o deepmd/npy -O deepmd_data ``` -- Load a mixed type data into a MultiSystems: -```python -import dpdata - -dpdata.MultiSystems().load_systems_from_file("mixed_dir", fmt="deepmd/npy/mixed") -``` +For advanced usage with Python APIs, [read dpdata documentation](https://docs.deepmodeling.com/projects/dpdata/). ## Plugins -One can follow [a simple example](plugin_example/) to add their own format by creating and installing plugins. It's critical to add the [Format](dpdata/format.py) class to `entry_points['dpdata.plugins']` in [`pyproject.toml`](plugin_example/pyproject.toml): -```toml -[project.entry-points.'dpdata.plugins'] -random = "dpdata_random:RandomFormat" -``` +- [cp2kdata](https://github.com/robinzyb/cp2kdata) adds the latest CP2K support for dpdata. + +For how to create your own plugin packages, [read dpdata documentation](https://docs.deepmodeling.com/projects/dpdata/). diff --git a/docs/index.rst b/docs/index.rst index 07e381de..3d98bd56 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,22 +6,23 @@ Welcome to dpdata's documentation! ================================== +dpdata is a Python package for manipulating atomistic data of software in computational science. + .. toctree:: :maxdepth: 2 :caption: Contents: - Overview + installation + systems/index try_dpdata cli formats drivers minimizers + plugin api/api credits -.. mdinclude:: ../README.md - - Indices and tables ================== diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 00000000..064f9133 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,13 @@ +# Installation + +DP-GEN only supports Python 3.7 and above. You can [setup a conda/pip environment](https://docs.deepmodeling.com/faq/conda.html), and then use one of the following methods to install DP-GEN: + +- Install via pip: `pip install dpdata` +- Install via conda: `conda install -c conda-forge dpdata` +- Install from source code: `git clone https://github.com/deepmodeling/dpdata && pip install ./dpdata` + +To test if the installation is successful, you may execute + +```bash +dpdata --version +``` diff --git a/docs/plugin.md b/docs/plugin.md new file mode 100644 index 00000000..44e8eabf --- /dev/null +++ b/docs/plugin.md @@ -0,0 +1,9 @@ +# Plugins + +One can follow a simple example under `plugin_example/` directory to add their own format by creating and installing plugins. +It's critical to add the :class:`Format` class to `entry_points['dpdata.plugins']` in `pyproject.toml`: + +```toml +[project.entry-points.'dpdata.plugins'] +random = "dpdata_random:RandomFormat" +``` diff --git a/docs/systems/bond_order_system.md b/docs/systems/bond_order_system.md new file mode 100644 index 00000000..443120a7 --- /dev/null +++ b/docs/systems/bond_order_system.md @@ -0,0 +1,51 @@ + +## BondOrderSystem +A new class :class:`BondOrderSystem` which inherits from class :class:`System` is introduced in dpdata. This new class contains information of chemical bonds and formal charges (stored in `BondOrderSystem.data['bonds']`, `BondOrderSystem.data['formal_charges']`). Now BondOrderSystem can only read from .mol/.sdf formats, because of its dependency on rdkit (which means rdkit must be installed if you want to use this function). Other formats, such as pdb, must be converted to .mol/.sdf format (maybe with software like open babel). +```python +import dpdata + +system_1 = dpdata.BondOrderSystem( + "tests/bond_order/CH3OH.mol", fmt="mol" +) # read from .mol file +system_2 = dpdata.BondOrderSystem( + "tests/bond_order/methane.sdf", fmt="sdf" +) # read from .sdf file +``` +In sdf file, all molecules must be of the same topology (i.e. conformers of the same molecular configuration). +`BondOrderSystem` also supports initialize from a :class:`rdkit.Chem.rdchem.Mol` object directly. +```python +from rdkit import Chem +from rdkit.Chem import AllChem +import dpdata + +mol = Chem.MolFromSmiles("CC") +mol = Chem.AddHs(mol) +AllChem.EmbedMultipleConfs(mol, 10) +system = dpdata.BondOrderSystem(rdkit_mol=mol) +``` + +### Bond Order Assignment +The :class:`BondOrderSystem` implements a more robust sanitize procedure for rdkit Mol, as defined in :class:`dpdata.rdkit.santizie.Sanitizer`. This class defines 3 level of sanitization process by: low, medium and high. (default is medium). ++ low: use `rdkit.Chem.SanitizeMol()` function to sanitize molecule. ++ medium: before using rdkit, the programm will first assign formal charge of each atom to avoid inappropriate valence exceptions. However, this mode requires the rightness of the bond order information in the given molecule. ++ high: the program will try to fix inappropriate bond orders in aromatic hetreocycles, phosphate, sulfate, carboxyl, nitro, nitrine, guanidine groups. If this procedure fails to sanitize the given molecule, the program will then try to call `obabel` to pre-process the mol and repeat the sanitization procedure. **That is to say, if you wan't to use this level of sanitization, please ensure `obabel` is installed in the environment.** +According to our test, our sanitization procedure can successfully read 4852 small molecules in the PDBBind-refined-set. It is necessary to point out that the in the molecule file (mol/sdf), the number of explicit hydrogens has to be correct. Thus, we recommend to use + `obabel xxx -O xxx -h` to pre-process the file. The reason why we do not implement this hydrogen-adding procedure in dpdata is that we can not ensure its correctness. + +```python +import dpdata + +for sdf_file in glob.glob("bond_order/refined-set-ligands/obabel/*sdf"): + syst = dpdata.BondOrderSystem(sdf_file, sanitize_level="high", verbose=False) +``` +### Formal Charge Assignment +BondOrderSystem implement a method to assign formal charge for each atom based on the 8-electron rule (see below). Note that it only supports common elements in bio-system: B,C,N,O,P,S,As +```python +import dpdata + +syst = dpdata.BondOrderSystem("tests/bond_order/CH3NH3+.mol", fmt="mol") +print(syst.get_formal_charges()) # return the formal charge on each atom +print(syst.get_charge()) # return the total charge of the system +``` + +If a valence of 3 is detected on carbon, the formal charge will be assigned to -1. Because for most cases (in alkynyl anion, isonitrile, cyclopentadienyl anion), the formal charge on 3-valence carbon is -1, and this is also consisent with the 8-electron rule. diff --git a/docs/systems/index.rst b/docs/systems/index.rst new file mode 100644 index 00000000..dfcbe698 --- /dev/null +++ b/docs/systems/index.rst @@ -0,0 +1,11 @@ +Systems +======= + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + system + multi + bond_order_system + mixed diff --git a/docs/systems/mixed.md b/docs/systems/mixed.md new file mode 100644 index 00000000..69bf80ea --- /dev/null +++ b/docs/systems/mixed.md @@ -0,0 +1,24 @@ +# Mixed Type Format + +The format `deepmd/npy/mixed` is the mixed type numpy format for DeePMD-kit, and can be loaded or dumped through class :class:`dpdata.MultiSystems`. + +Under this format, systems with the same number of atoms but different formula can be put together +for a larger system, especially when the frame numbers in systems are sparse. + +This also helps to mixture the type information together for model training with type embedding network. + +Here are examples using `deepmd/npy/mixed` format: + +- Dump a MultiSystems into a mixed type numpy directory: +```python +import dpdata + +dpdata.MultiSystems(*systems).to_deepmd_npy_mixed("mixed_dir") +``` + +- Load a mixed type data into a MultiSystems: +```python +import dpdata + +dpdata.MultiSystems().load_systems_from_file("mixed_dir", fmt="deepmd/npy/mixed") +``` diff --git a/docs/systems/multi.md b/docs/systems/multi.md new file mode 100644 index 00000000..8e47acf7 --- /dev/null +++ b/docs/systems/multi.md @@ -0,0 +1,62 @@ +# `MultiSystems` + +The Class :class:`dpdata.MultiSystems` can read data from a dir which may contains many files of different systems, or from single xyz file which contains different systems. + +Use :meth:`dpdata.MultiSystems.from_dir` to read from a directory, :class:`dpdata.MultiSystems` will walk in the directory +Recursively and find all file with specific file_name. Supports all the file formats that :class:`dpdata.LabeledSystem` supports. + +Use :meth:`dpdata.MultiSystems.from_file` to read from single file. Single-file support is available for the `quip/gap/xyz` and `ase/structure` formats. + +For example, for `quip/gap xyz` files, single .xyz file may contain many different configurations with different atom numbers and atom type. + +The following commands relating to :class:`dpdata.MultiSystems` may be useful. +```python +# load data + +xyz_multi_systems = dpdata.MultiSystems.from_file( + file_name="tests/xyz/xyz_unittest.xyz", fmt="quip/gap/xyz" +) +vasp_multi_systems = dpdata.MultiSystems.from_dir( + dir_name="./mgal_outcar", file_name="OUTCAR", fmt="vasp/outcar" +) + +# use wildcard +vasp_multi_systems = dpdata.MultiSystems.from_dir( + dir_name="./mgal_outcar", file_name="*OUTCAR", fmt="vasp/outcar" +) + +# print the multi_system infomation +print(xyz_multi_systems) +print(xyz_multi_systems.systems) # return a dictionaries + +# print the system infomation +print(xyz_multi_systems.systems["B1C9"].data) + +# dump a system's data to ./my_work_dir/B1C9_raw folder +xyz_multi_systems.systems["B1C9"].to_deepmd_raw("./my_work_dir/B1C9_raw") + +# dump all systems +xyz_multi_systems.to_deepmd_raw("./my_deepmd_data/") +``` + +You may also use the following code to parse muti-system: +```python +from dpdata import LabeledSystem, MultiSystems +from glob import glob + +""" +process multi systems +""" +fs = glob("./*/OUTCAR") # remeber to change here !!! +ms = MultiSystems() +for f in fs: + try: + ls = LabeledSystem(f) + except: + print(f) + if len(ls) > 0: + ms.append(ls) + +ms.to_deepmd_raw("deepmd") +ms.to_deepmd_npy("deepmd") +``` diff --git a/docs/systems/system.md b/docs/systems/system.md new file mode 100644 index 00000000..0401a311 --- /dev/null +++ b/docs/systems/system.md @@ -0,0 +1,112 @@ +# `System` and `LabeledSystem` + +This section gives some examples on how dpdata works. Firstly one needs to import the module in a python 3.x compatible code. +```python +import dpdata +``` +The typicall workflow of `dpdata` is + +1. Load data from vasp or lammps or deepmd-kit data files. +2. Manipulate data +3. Dump data to in a desired format + + +### Load data +```python +d_poscar = dpdata.System("POSCAR", fmt="vasp/poscar") +``` +or let dpdata infer the format (`vasp/poscar`) of the file from the file name extension +```python +d_poscar = dpdata.System("my.POSCAR") +``` +The number of atoms, atom types, coordinates are loaded from the `POSCAR` and stored to a data :class:`System` called `d_poscar`. +A data :class:`System` (a concept used by [deepmd-kit](https://github.com/deepmodeling/deepmd-kit)) contains frames that has the same number of atoms of the same type. The order of the atoms should be consistent among the frames in one :class:`System`. +It is noted that `POSCAR` only contains one frame. +If the multiple frames stored in, for example, a `OUTCAR` is wanted, +```python +d_outcar = dpdata.LabeledSystem("OUTCAR") +``` +The labels provided in the `OUTCAR`, i.e. energies, forces and virials (if any), are loaded by :class:`LabeledSystem`. It is noted that the forces of atoms are always assumed to exist. :class:`LabeledSystem` is a derived class of :class:`System`. + +The :class:`System` or :class:`LabeledSystem` can be constructed from the following file formats with the `format key` in the table passed to argument `fmt`: + + + +### Access data +These properties stored in :class:`System` and :class:`LabeledSystem` can be accessed by operator `[]` with the key of the property supplied, for example +```python +coords = d_outcar["coords"] +``` +Available properties are (nframe: number of frames in the system, natoms: total number of atoms in the system) + +| key | type | dimension | are labels | description +| --- | --- | --- | --- | --- +| 'atom_names' | list of str | ntypes | False | The name of each atom type +| 'atom_numbs' | list of int | ntypes | False | The number of atoms of each atom type +| 'atom_types' | np.ndarray | natoms | False | Array assigning type to each atom +| 'cells' | np.ndarray | nframes x 3 x 3 | False | The cell tensor of each frame +| 'coords' | np.ndarray | nframes x natoms x 3 | False | The atom coordinates +| 'energies' | np.ndarray | nframes | True | The frame energies +| 'forces' | np.ndarray | nframes x natoms x 3 | True | The atom forces +| 'virials' | np.ndarray | nframes x 3 x 3 | True | The virial tensor of each frame + + +### Dump data +The data stored in :class:`System` or :class:`LabeledSystem` can be dumped in 'lammps/lmp' or 'vasp/poscar' format, for example: +```python +d_outcar.to("lammps/lmp", "conf.lmp", frame_idx=0) +``` +The first frames of `d_outcar` will be dumped to 'conf.lmp' +```python +d_outcar.to("vasp/poscar", "POSCAR", frame_idx=-1) +``` +The last frames of `d_outcar` will be dumped to 'POSCAR'. + +The data stored in `LabeledSystem` can be dumped to deepmd-kit raw format, for example +```python +d_outcar.to("deepmd/raw", "dpmd_raw") +``` +Or a simpler command: +```python +dpdata.LabeledSystem("OUTCAR").to("deepmd/raw", "dpmd_raw") +``` +Frame selection can be implemented by +```python +dpdata.LabeledSystem("OUTCAR").sub_system([0, -1]).to("deepmd/raw", "dpmd_raw") +``` +by which only the first and last frames are dumped to `dpmd_raw`. + + +### replicate +dpdata will create a super cell of the current atom configuration. +```python +dpdata.System("./POSCAR").replicate( + ( + 1, + 2, + 3, + ) +) +``` +tuple(1,2,3) means don't copy atom configuration in x direction, make 2 copys in y direction, make 3 copys in z direction. + + +### perturb +By the following example, each frame of the original system (`dpdata.System('./POSCAR')`) is perturbed to generate three new frames. For each frame, the cell is perturbed by 5% and the atom positions are perturbed by 0.6 Angstrom. `atom_pert_style` indicates that the perturbation to the atom positions is subject to normal distribution. Other available options to `atom_pert_style` are`uniform` (uniform in a ball), and `const` (uniform on a sphere). +```python +perturbed_system = dpdata.System("./POSCAR").perturb( + pert_num=3, + cell_pert_fraction=0.05, + atom_pert_distance=0.6, + atom_pert_style="normal", +) +print(perturbed_system.data) +``` + +### replace +By the following example, Random 8 Hf atoms in the system will be replaced by Zr atoms with the atom postion unchanged. +```python +s = dpdata.System("tests/poscars/POSCAR.P42nmc", fmt="vasp/poscar") +s.replace("Hf", "Zr", 8) +s.to_vasp_poscar("POSCAR.P42nmc.replace") +``` From 5df6acd266589b131c961390414337472e0880d9 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 2 Sep 2024 22:18:50 -0400 Subject: [PATCH 10/33] fix: fix a typo in #702 (#710) ## Summary by CodeRabbit - **Improvements** - Enhanced the flexibility of data handling by modifying how data types are processed, allowing for better adaptability based on context. Signed-off-by: Jinzhe Zeng --- dpdata/deepmd/comp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dpdata/deepmd/comp.py b/dpdata/deepmd/comp.py index 66111016..ad638408 100644 --- a/dpdata/deepmd/comp.py +++ b/dpdata/deepmd/comp.py @@ -180,7 +180,7 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): dtypes = dpdata.system.LabeledSystem.DTYPES else: dtypes = dpdata.system.System.DTYPES - for dtype in dpdata.system.LabeledSystem.DTYPES: + for dtype in dtypes: if dtype.name in ( "atom_numbs", "atom_names", From 6bf41e31b4952e54e7cec6f8b6c9cc3aabdc05cb Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 2 Sep 2024 22:58:22 -0400 Subject: [PATCH 11/33] feat: file object passed to open (#709) ## Summary by CodeRabbit - **New Features** - Introduced a new utility function `open_file` for improved file handling across various modules. - Enhanced type annotations for multiple functions to specify `FileType`, improving code clarity and type safety. - **Bug Fixes** - Improved file handling robustness by replacing the built-in `open` function with the custom `open_file` function in several modules. - **Tests** - Added unit tests for the new `open_file` utility function to ensure reliable functionality. --------- Signed-off-by: Jinzhe Zeng Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dpdata/abacus/md.py | 10 +++++---- dpdata/abacus/relax.py | 8 ++++--- dpdata/abacus/scf.py | 10 +++++---- dpdata/amber/md.py | 7 +++--- dpdata/amber/sqm.py | 14 ++++++++---- dpdata/deepmd/comp.py | 3 ++- dpdata/deepmd/raw.py | 5 +++-- dpdata/dftbplus/output.py | 15 ++++++++++--- dpdata/gaussian/log.py | 11 +++++++-- dpdata/gromacs/gro.py | 10 +++++++-- dpdata/lammps/dump.py | 10 +++++++-- dpdata/openmx/omx.py | 21 +++++++++++------ dpdata/orca/output.py | 13 +++++++++-- dpdata/plugins/abacus.py | 10 +++++++-- dpdata/plugins/amber.py | 3 ++- dpdata/plugins/gaussian.py | 19 ++++++++++------ dpdata/plugins/gromacs.py | 12 ++++++++-- dpdata/plugins/lammps.py | 14 ++++++++---- dpdata/plugins/n2p2.py | 14 ++++++++---- dpdata/plugins/orca.py | 9 ++++++-- dpdata/plugins/psi4.py | 14 ++++++++---- dpdata/plugins/pwmat.py | 14 ++++++++---- dpdata/plugins/vasp.py | 15 ++++++++----- dpdata/plugins/xyz.py | 14 ++++++++---- dpdata/psi4/output.py | 10 +++++++-- dpdata/qe/scf.py | 6 +++-- dpdata/qe/traj.py | 20 +++++++++++------ dpdata/utils.py | 46 +++++++++++++++++++++++++++++++++++++- tests/test_read_file.py | 22 ++++++++++++++++++ 29 files changed, 289 insertions(+), 90 deletions(-) create mode 100644 tests/test_read_file.py diff --git a/dpdata/abacus/md.py b/dpdata/abacus/md.py index fa184177..9e0a416a 100644 --- a/dpdata/abacus/md.py +++ b/dpdata/abacus/md.py @@ -5,6 +5,8 @@ import numpy as np +from dpdata.utils import open_file + from .scf import ( bohr2ang, get_cell, @@ -156,12 +158,12 @@ def get_frame(fname): path_in = os.path.join(fname, "INPUT") else: raise RuntimeError("invalid input") - with open(path_in) as fp: + with open_file(path_in) as fp: inlines = fp.read().split("\n") geometry_path_in = get_geometry_in(fname, inlines) # base dir of STRU path_out = get_path_out(fname, inlines) - with open(geometry_path_in) as fp: + with open_file(geometry_path_in) as fp: geometry_inlines = fp.read().split("\n") celldm, cell = get_cell(geometry_inlines) atom_names, natoms, types, coords = get_coords( @@ -172,11 +174,11 @@ def get_frame(fname): # ndump = int(os.popen("ls -l %s | grep 'md_pos_' | wc -l" %path_out).readlines()[0]) # number of dumped geometry files # coords = get_coords_from_cif(ndump, dump_freq, atom_names, natoms, types, path_out, cell) - with open(os.path.join(path_out, "MD_dump")) as fp: + with open_file(os.path.join(path_out, "MD_dump")) as fp: dumplines = fp.read().split("\n") coords, cells, force, stress = get_coords_from_dump(dumplines, natoms) ndump = np.shape(coords)[0] - with open(os.path.join(path_out, "running_md.log")) as fp: + with open_file(os.path.join(path_out, "running_md.log")) as fp: outlines = fp.read().split("\n") energy = get_energy(outlines, ndump, dump_freq) diff --git a/dpdata/abacus/relax.py b/dpdata/abacus/relax.py index 976243b8..7a4abf35 100644 --- a/dpdata/abacus/relax.py +++ b/dpdata/abacus/relax.py @@ -4,6 +4,8 @@ import numpy as np +from dpdata.utils import open_file + from .scf import ( bohr2ang, collect_force, @@ -174,10 +176,10 @@ def get_frame(fname): path_in = os.path.join(fname, "INPUT") else: raise RuntimeError("invalid input") - with open(path_in) as fp: + with open_file(path_in) as fp: inlines = fp.read().split("\n") geometry_path_in = get_geometry_in(fname, inlines) # base dir of STRU - with open(geometry_path_in) as fp: + with open_file(geometry_path_in) as fp: geometry_inlines = fp.read().split("\n") celldm, cell = get_cell(geometry_inlines) atom_names, natoms, types, coord_tmp = get_coords( @@ -186,7 +188,7 @@ def get_frame(fname): logf = get_log_file(fname, inlines) assert os.path.isfile(logf), f"Error: can not find {logf}" - with open(logf) as f1: + with open_file(logf) as f1: lines = f1.readlines() atomnumber = 0 diff --git a/dpdata/abacus/scf.py b/dpdata/abacus/scf.py index c06f8cd3..93e3d6e1 100644 --- a/dpdata/abacus/scf.py +++ b/dpdata/abacus/scf.py @@ -6,6 +6,8 @@ import numpy as np +from dpdata.utils import open_file + from ..unit import EnergyConversion, LengthConversion, PressureConversion bohr2ang = LengthConversion("bohr", "angstrom").value() @@ -253,7 +255,7 @@ def get_frame(fname): if not CheckFile(path_in): return data - with open(path_in) as fp: + with open_file(path_in) as fp: inlines = fp.read().split("\n") geometry_path_in = get_geometry_in(fname, inlines) @@ -261,9 +263,9 @@ def get_frame(fname): if not (CheckFile(geometry_path_in) and CheckFile(path_out)): return data - with open(geometry_path_in) as fp: + with open_file(geometry_path_in) as fp: geometry_inlines = fp.read().split("\n") - with open(path_out) as fp: + with open_file(path_out) as fp: outlines = fp.read().split("\n") celldm, cell = get_cell(geometry_inlines) @@ -338,7 +340,7 @@ def get_nele_from_stru(geometry_inlines): def get_frame_from_stru(fname): assert isinstance(fname, str) - with open(fname) as fp: + with open_file(fname) as fp: geometry_inlines = fp.read().split("\n") nele = get_nele_from_stru(geometry_inlines) inlines = ["ntype %d" % nele] diff --git a/dpdata/amber/md.py b/dpdata/amber/md.py index f3217fbd..cb4f2d25 100644 --- a/dpdata/amber/md.py +++ b/dpdata/amber/md.py @@ -7,6 +7,7 @@ from dpdata.amber.mask import pick_by_amber_mask from dpdata.unit import EnergyConversion +from dpdata.utils import open_file from ..periodic_table import ELEMENTS @@ -51,7 +52,7 @@ def read_amber_traj( flag_atom_numb = False amber_types = [] atomic_number = [] - with open(parm7_file) as f: + with open_file(parm7_file) as f: for line in f: if line.startswith("%FLAG"): flag_atom_type = line.startswith("%FLAG AMBER_ATOM_TYPE") @@ -101,14 +102,14 @@ def read_amber_traj( # load energy from mden_file or mdout_file energies = [] if mden_file is not None and os.path.isfile(mden_file): - with open(mden_file) as f: + with open_file(mden_file) as f: for line in f: if line.startswith("L6"): s = line.split() if s[2] != "E_pot": energies.append(float(s[2])) elif mdout_file is not None and os.path.isfile(mdout_file): - with open(mdout_file) as f: + with open_file(mdout_file) as f: for line in f: if "EPtot" in line: s = line.split() diff --git a/dpdata/amber/sqm.py b/dpdata/amber/sqm.py index 1be3802a..93e41f9a 100644 --- a/dpdata/amber/sqm.py +++ b/dpdata/amber/sqm.py @@ -1,9 +1,15 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np from dpdata.periodic_table import ELEMENTS from dpdata.unit import EnergyConversion +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType kcal2ev = EnergyConversion("kcal_mol", "eV").value() @@ -14,7 +20,7 @@ READ_FORCES = 7 -def parse_sqm_out(fname): +def parse_sqm_out(fname: FileType): """Read atom symbols, charges and coordinates from ambertools sqm.out file.""" atom_symbols = [] coords = [] @@ -22,7 +28,7 @@ def parse_sqm_out(fname): forces = [] energies = [] - with open(fname) as f: + with open_file(fname) as f: flag = START for line in f: if line.startswith(" Total SCF energy"): @@ -81,7 +87,7 @@ def parse_sqm_out(fname): return data -def make_sqm_in(data, fname=None, frame_idx=0, **kwargs): +def make_sqm_in(data, fname: FileType | None = None, frame_idx=0, **kwargs): symbols = [data["atom_names"][ii] for ii in data["atom_types"]] atomic_numbers = [ELEMENTS.index(ss) + 1 for ss in symbols] charge = kwargs.get("charge", 0) @@ -109,6 +115,6 @@ def make_sqm_in(data, fname=None, frame_idx=0, **kwargs): f"{data['coords'][frame_idx][ii, 2]:.6f}", ) if fname is not None: - with open(fname, "w") as fp: + with open_file(fname, "w") as fp: fp.write(ret) return ret diff --git a/dpdata/deepmd/comp.py b/dpdata/deepmd/comp.py index ad638408..5ba3914e 100644 --- a/dpdata/deepmd/comp.py +++ b/dpdata/deepmd/comp.py @@ -8,6 +8,7 @@ import numpy as np import dpdata +from dpdata.utils import open_file from .raw import load_type @@ -172,7 +173,7 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): except OSError: pass if data.get("nopbc", False): - with open(os.path.join(folder, "nopbc"), "w") as fw_nopbc: + with open_file(os.path.join(folder, "nopbc"), "w") as fw_nopbc: pass # allow custom dtypes labels = "energies" in data diff --git a/dpdata/deepmd/raw.py b/dpdata/deepmd/raw.py index 717c73f9..f8ddcaf3 100644 --- a/dpdata/deepmd/raw.py +++ b/dpdata/deepmd/raw.py @@ -6,6 +6,7 @@ import numpy as np import dpdata +from dpdata.utils import open_file def load_type(folder, type_map=None): @@ -17,7 +18,7 @@ def load_type(folder, type_map=None): data["atom_names"] = [] # if find type_map.raw, use it if os.path.isfile(os.path.join(folder, "type_map.raw")): - with open(os.path.join(folder, "type_map.raw")) as fp: + with open_file(os.path.join(folder, "type_map.raw")) as fp: my_type_map = fp.read().split() # else try to use arg type_map elif type_map is not None: @@ -140,7 +141,7 @@ def dump(folder, data): except OSError: pass if data.get("nopbc", False): - with open(os.path.join(folder, "nopbc"), "w") as fw_nopbc: + with open_file(os.path.join(folder, "nopbc"), "w") as fw_nopbc: pass # allow custom dtypes labels = "energies" in data diff --git a/dpdata/dftbplus/output.py b/dpdata/dftbplus/output.py index 0f10c3ac..49fdd2b1 100644 --- a/dpdata/dftbplus/output.py +++ b/dpdata/dftbplus/output.py @@ -1,9 +1,18 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType + -def read_dftb_plus(fn_1: str, fn_2: str) -> tuple[str, np.ndarray, float, np.ndarray]: +def read_dftb_plus( + fn_1: FileType, fn_2: FileType +) -> tuple[str, np.ndarray, float, np.ndarray]: """Read from DFTB+ input and output. Parameters @@ -29,7 +38,7 @@ def read_dftb_plus(fn_1: str, fn_2: str) -> tuple[str, np.ndarray, float, np.nda symbols = None forces = None energy = None - with open(fn_1) as f: + with open_file(fn_1) as f: flag = 0 for line in f: if flag == 1: @@ -49,7 +58,7 @@ def read_dftb_plus(fn_1: str, fn_2: str) -> tuple[str, np.ndarray, float, np.nda flag += 1 if flag == 7: flag = 0 - with open(fn_2) as f: + with open_file(fn_2) as f: flag = 0 for line in f: if line.startswith("Total Forces"): diff --git a/dpdata/gaussian/log.py b/dpdata/gaussian/log.py index 204cf464..08a65b9d 100644 --- a/dpdata/gaussian/log.py +++ b/dpdata/gaussian/log.py @@ -1,7 +1,14 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType + from ..periodic_table import ELEMENTS from ..unit import EnergyConversion, ForceConversion, LengthConversion @@ -12,7 +19,7 @@ symbols = ["X"] + ELEMENTS -def to_system_data(file_name, md=False): +def to_system_data(file_name: FileType, md=False): """Read Gaussian log file. Parameters @@ -43,7 +50,7 @@ def to_system_data(file_name, md=False): nopbc = True coords = None - with open(file_name) as fp: + with open_file(file_name) as fp: for line in fp: if line.startswith(" SCF Done"): # energies diff --git a/dpdata/gromacs/gro.py b/dpdata/gromacs/gro.py index aca2443b..fe83e0c5 100644 --- a/dpdata/gromacs/gro.py +++ b/dpdata/gromacs/gro.py @@ -2,9 +2,15 @@ from __future__ import annotations import re +from typing import TYPE_CHECKING import numpy as np +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType + from ..unit import LengthConversion nm2ang = LengthConversion("nm", "angstrom").value() @@ -48,9 +54,9 @@ def _get_cell(line): return cell -def file_to_system_data(fname, format_atom_name=True, **kwargs): +def file_to_system_data(fname: FileType, format_atom_name=True, **kwargs): system = {"coords": [], "cells": []} - with open(fname) as fp: + with open_file(fname) as fp: frame = 0 while True: flag = fp.readline() diff --git a/dpdata/lammps/dump.py b/dpdata/lammps/dump.py index f0ade2b0..72fee27d 100644 --- a/dpdata/lammps/dump.py +++ b/dpdata/lammps/dump.py @@ -3,9 +3,15 @@ import os import sys +from typing import TYPE_CHECKING import numpy as np +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType + lib_path = os.path.dirname(os.path.realpath(__file__)) sys.path.append(lib_path) import warnings @@ -169,11 +175,11 @@ def box2dumpbox(orig, box): return bounds, tilt -def load_file(fname, begin=0, step=1): +def load_file(fname: FileType, begin=0, step=1): lines = [] buff = [] cc = -1 - with open(fname) as fp: + with open_file(fname) as fp: while True: line = fp.readline().rstrip("\n") if not line: diff --git a/dpdata/openmx/omx.py b/dpdata/openmx/omx.py index d3afff00..aae9b578 100644 --- a/dpdata/openmx/omx.py +++ b/dpdata/openmx/omx.py @@ -1,8 +1,15 @@ #!/usr/bin/python3 from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType + from ..unit import ( EnergyConversion, ForceConversion, @@ -98,12 +105,12 @@ def load_cells(lines): # load atom_names, atom_numbs, atom_types, cells -def load_param_file(fname, mdname): - with open(fname) as dat_file: +def load_param_file(fname: FileType, mdname: FileType): + with open_file(fname) as dat_file: lines = dat_file.readlines() atom_names, atom_types, atom_numbs = load_atom(lines) - with open(mdname) as md_file: + with open_file(mdname) as md_file: lines = md_file.readlines() cells = load_cells(lines) return atom_names, atom_numbs, atom_types, cells @@ -133,15 +140,15 @@ def load_coords(lines, atom_names, natoms): return coords -def load_data(mdname, atom_names, natoms): - with open(mdname) as md_file: +def load_data(mdname: FileType, atom_names, natoms): + with open_file(mdname) as md_file: lines = md_file.readlines() coords = load_coords(lines, atom_names, natoms) steps = [str(i) for i in range(1, coords.shape[0] + 1)] return coords, steps -def to_system_data(fname, mdname): +def to_system_data(fname: FileType, mdname: FileType): data = {} ( data["atom_names"], @@ -194,7 +201,7 @@ def load_force(lines, atom_names, atom_numbs): # load energy, force def to_system_label(fname, mdname): atom_names, atom_numbs, atom_types, cells = load_param_file(fname, mdname) - with open(mdname) as md_file: + with open_file(mdname) as md_file: lines = md_file.readlines() energy = load_energy(lines) force = load_force(lines, atom_names, atom_numbs) diff --git a/dpdata/orca/output.py b/dpdata/orca/output.py index a23013fd..a0915162 100644 --- a/dpdata/orca/output.py +++ b/dpdata/orca/output.py @@ -1,9 +1,18 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType + -def read_orca_sp_output(fn: str) -> tuple[np.ndarray, np.ndarray, float, np.ndarray]: +def read_orca_sp_output( + fn: FileType, +) -> tuple[np.ndarray, np.ndarray, float, np.ndarray]: """Read from ORCA output. Note that both the energy and the gradient should be printed. @@ -28,7 +37,7 @@ def read_orca_sp_output(fn: str) -> tuple[np.ndarray, np.ndarray, float, np.ndar symbols = None forces = None energy = None - with open(fn) as f: + with open_file(fn) as f: flag = 0 for line in f: if flag in (1, 3, 4): diff --git a/dpdata/plugins/abacus.py b/dpdata/plugins/abacus.py index eb2d7786..e3367b35 100644 --- a/dpdata/plugins/abacus.py +++ b/dpdata/plugins/abacus.py @@ -1,9 +1,15 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import dpdata.abacus.md import dpdata.abacus.relax import dpdata.abacus.scf from dpdata.format import Format +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType @Format.register("abacus/stru") @@ -12,7 +18,7 @@ class AbacusSTRUFormat(Format): def from_system(self, file_name, **kwargs): return dpdata.abacus.scf.get_frame_from_stru(file_name) - def to_system(self, data, file_name, frame_idx=0, **kwargs): + def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs): """Dump the system into ABACUS STRU format file. Parameters @@ -46,7 +52,7 @@ def to_system(self, data, file_name, frame_idx=0, **kwargs): numerical_descriptor=numerical_descriptor, mass=mass, ) - with open(file_name, "w") as fp: + with open_file(file_name, "w") as fp: fp.write(stru_string) diff --git a/dpdata/plugins/amber.py b/dpdata/plugins/amber.py index 42fce552..361e0d8a 100644 --- a/dpdata/plugins/amber.py +++ b/dpdata/plugins/amber.py @@ -8,6 +8,7 @@ import dpdata.amber.sqm from dpdata.driver import Driver, Minimizer from dpdata.format import Format +from dpdata.utils import open_file @Format.register("amber/md") @@ -143,7 +144,7 @@ def label(self, data: dict) -> dict: [*self.sqm_exec.split(), "-O", "-i", inp_fn, "-o", out_fn] ) except sp.CalledProcessError as e: - with open(out_fn) as f: + with open_file(out_fn) as f: raise RuntimeError( "Run sqm failed! Output:\n" + f.read() ) from e diff --git a/dpdata/plugins/gaussian.py b/dpdata/plugins/gaussian.py index b55447b9..55bee5a4 100644 --- a/dpdata/plugins/gaussian.py +++ b/dpdata/plugins/gaussian.py @@ -3,16 +3,21 @@ import os import subprocess as sp import tempfile +from typing import TYPE_CHECKING import dpdata.gaussian.gjf import dpdata.gaussian.log from dpdata.driver import Driver from dpdata.format import Format +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType @Format.register("gaussian/log") class GaussianLogFormat(Format): - def from_labeled_system(self, file_name, md=False, **kwargs): + def from_labeled_system(self, file_name: FileType, md=False, **kwargs): try: return dpdata.gaussian.log.to_system_data(file_name, md=md) except AssertionError: @@ -21,7 +26,7 @@ def from_labeled_system(self, file_name, md=False, **kwargs): @Format.register("gaussian/md") class GaussianMDFormat(Format): - def from_labeled_system(self, file_name, **kwargs): + def from_labeled_system(self, file_name: FileType, **kwargs): return GaussianLogFormat().from_labeled_system(file_name, md=True) @@ -29,7 +34,7 @@ def from_labeled_system(self, file_name, **kwargs): class GaussiaGJFFormat(Format): """Gaussian input file.""" - def from_system(self, file_name: str, **kwargs): + def from_system(self, file_name: FileType, **kwargs): """Read Gaussian input file. Parameters @@ -39,11 +44,11 @@ def from_system(self, file_name: str, **kwargs): **kwargs : dict keyword arguments """ - with open(file_name) as fp: + with open_file(file_name) as fp: text = fp.read() return dpdata.gaussian.gjf.read_gaussian_input(text) - def to_system(self, data: dict, file_name: str, **kwargs): + def to_system(self, data: dict, file_name: FileType, **kwargs): """Generate Gaussian input file. Parameters @@ -56,7 +61,7 @@ def to_system(self, data: dict, file_name: str, **kwargs): Other parameters to make input files. See :meth:`dpdata.gaussian.gjf.make_gaussian_input` """ text = dpdata.gaussian.gjf.make_gaussian_input(data, **kwargs) - with open(file_name, "w") as fp: + with open_file(file_name, "w") as fp: fp.write(text) @@ -110,7 +115,7 @@ def label(self, data: dict) -> dict: try: sp.check_output([*self.gaussian_exec.split(), inp_fn]) except sp.CalledProcessError as e: - with open(out_fn) as f: + with open_file(out_fn) as f: out = f.read() raise RuntimeError("Run gaussian failed! Output:\n" + out) from e labeled_system.append(dpdata.LabeledSystem(out_fn, fmt="gaussian/log")) diff --git a/dpdata/plugins/gromacs.py b/dpdata/plugins/gromacs.py index 12dece71..a7066bbc 100644 --- a/dpdata/plugins/gromacs.py +++ b/dpdata/plugins/gromacs.py @@ -1,7 +1,13 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import dpdata.gromacs.gro from dpdata.format import Format +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType @Format.register("gro") @@ -23,7 +29,9 @@ def from_system(self, file_name, format_atom_name=True, **kwargs): file_name, format_atom_name=format_atom_name, **kwargs ) - def to_system(self, data, file_name=None, frame_idx=-1, **kwargs): + def to_system( + self, data, file_name: FileType | None = None, frame_idx=-1, **kwargs + ): """Dump the system in gromacs .gro format. Parameters @@ -52,5 +60,5 @@ def to_system(self, data, file_name=None, frame_idx=-1, **kwargs): if file_name is None: return gro_str else: - with open(file_name, "w+") as fp: + with open_file(file_name, "w+") as fp: fp.write(gro_str) diff --git a/dpdata/plugins/lammps.py b/dpdata/plugins/lammps.py index 65e7f570..0327d444 100644 --- a/dpdata/plugins/lammps.py +++ b/dpdata/plugins/lammps.py @@ -1,20 +1,26 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import dpdata.lammps.dump import dpdata.lammps.lmp from dpdata.format import Format +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType @Format.register("lmp") @Format.register("lammps/lmp") class LAMMPSLmpFormat(Format): @Format.post("shift_orig_zero") - def from_system(self, file_name, type_map=None, **kwargs): - with open(file_name) as fp: + def from_system(self, file_name: FileType, type_map=None, **kwargs): + with open_file(file_name) as fp: lines = [line.rstrip("\n") for line in fp] return dpdata.lammps.lmp.to_system_data(lines, type_map) - def to_system(self, data, file_name, frame_idx=0, **kwargs): + def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs): """Dump the system in lammps data format. Parameters @@ -30,7 +36,7 @@ def to_system(self, data, file_name, frame_idx=0, **kwargs): """ assert frame_idx < len(data["coords"]) w_str = dpdata.lammps.lmp.from_system_data(data, frame_idx) - with open(file_name, "w") as fp: + with open_file(file_name, "w") as fp: fp.write(w_str) diff --git a/dpdata/plugins/n2p2.py b/dpdata/plugins/n2p2.py index b70d6e6f..28942ff5 100644 --- a/dpdata/plugins/n2p2.py +++ b/dpdata/plugins/n2p2.py @@ -1,8 +1,14 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np from dpdata.format import Format +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType from ..unit import EnergyConversion, ForceConversion, LengthConversion @@ -44,7 +50,7 @@ class N2P2Format(Format): For more information about the n2p2 format, please refer to https://compphysvienna.github.io/n2p2/topics/cfg_file.html """ - def from_labeled_system(self, file_name, **kwargs): + def from_labeled_system(self, file_name: FileType, **kwargs): """Read from n2p2 format. Parameters @@ -67,7 +73,7 @@ def from_labeled_system(self, file_name, **kwargs): natom0 = None natoms0 = None atom_types0 = None - with open(file_name) as file: + with open_file(file_name) as file: for line in file: line = line.strip() # Remove leading/trailing whitespace if line.lower() == "begin": @@ -155,7 +161,7 @@ def from_labeled_system(self, file_name, **kwargs): "forces": forces, } - def to_labeled_system(self, data, file_name, **kwargs): + def to_labeled_system(self, data, file_name: FileType, **kwargs): """Write n2p2 format. By default, LabeledSystem.to will fallback to System.to. @@ -193,5 +199,5 @@ def to_labeled_system(self, data, file_name, **kwargs): buff.append(f"energy {energy:15.6f}") buff.append(f"charge {0:15.6f}") buff.append("end") - with open(file_name, "w") as fp: + with open_file(file_name, "w") as fp: fp.write("\n".join(buff)) diff --git a/dpdata/plugins/orca.py b/dpdata/plugins/orca.py index 9dc32c32..7a0b806c 100644 --- a/dpdata/plugins/orca.py +++ b/dpdata/plugins/orca.py @@ -1,11 +1,16 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np from dpdata.format import Format from dpdata.orca.output import read_orca_sp_output from dpdata.unit import EnergyConversion, ForceConversion +if TYPE_CHECKING: + from dpdata.utils import FileType + energy_convert = EnergyConversion("hartree", "eV").value() force_convert = ForceConversion("hartree/bohr", "eV/angstrom").value() @@ -18,12 +23,12 @@ class ORCASPOutFormat(Format): printed into the output file. """ - def from_labeled_system(self, file_name: str, **kwargs) -> dict: + def from_labeled_system(self, file_name: FileType, **kwargs) -> dict: """Read from ORCA single point energy output. Parameters ---------- - file_name : str + file_name : FileType file name **kwargs keyword arguments diff --git a/dpdata/plugins/psi4.py b/dpdata/plugins/psi4.py index a0cf00e4..2bbfc232 100644 --- a/dpdata/plugins/psi4.py +++ b/dpdata/plugins/psi4.py @@ -1,11 +1,17 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np from dpdata.format import Format from dpdata.psi4.input import write_psi4_input from dpdata.psi4.output import read_psi4_output from dpdata.unit import EnergyConversion, ForceConversion +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType energy_convert = EnergyConversion("hartree", "eV").value() force_convert = ForceConversion("hartree/bohr", "eV/angstrom").value() @@ -19,12 +25,12 @@ class PSI4OutFormat(Format): printed into the output file. """ - def from_labeled_system(self, file_name: str, **kwargs) -> dict: + def from_labeled_system(self, file_name: FileType, **kwargs) -> dict: """Read from Psi4 output. Parameters ---------- - file_name : str + file_name : FileType file name **kwargs keyword arguments @@ -61,7 +67,7 @@ class PSI4InputFormat(Format): def to_system( self, data: dict, - file_name: str, + file_name: FileType, method: str, basis: str, charge: int = 0, @@ -91,7 +97,7 @@ def to_system( keyword arguments """ types = np.array(data["atom_names"])[data["atom_types"]] - with open(file_name, "w") as fout: + with open_file(file_name, "w") as fout: fout.write( write_psi4_input( types=types, diff --git a/dpdata/plugins/pwmat.py b/dpdata/plugins/pwmat.py index 80f219b6..ba3dab16 100644 --- a/dpdata/plugins/pwmat.py +++ b/dpdata/plugins/pwmat.py @@ -1,10 +1,16 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np import dpdata.pwmat.atomconfig import dpdata.pwmat.movement from dpdata.format import Format +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType @Format.register("movement") @@ -47,12 +53,12 @@ def from_labeled_system( @Format.register("pwmat/final.config") class PwmatAtomconfigFormat(Format): @Format.post("rot_lower_triangular") - def from_system(self, file_name, **kwargs): - with open(file_name) as fp: + def from_system(self, file_name: FileType, **kwargs): + with open_file(file_name) as fp: lines = [line.rstrip("\n") for line in fp] return dpdata.pwmat.atomconfig.to_system_data(lines) - def to_system(self, data, file_name, frame_idx=0, *args, **kwargs): + def to_system(self, data, file_name: FileType, frame_idx=0, *args, **kwargs): """Dump the system in pwmat atom.config format. Parameters @@ -70,5 +76,5 @@ def to_system(self, data, file_name, frame_idx=0, *args, **kwargs): """ assert frame_idx < len(data["coords"]) w_str = dpdata.pwmat.atomconfig.from_system_data(data, frame_idx) - with open(file_name, "w") as fp: + with open_file(file_name, "w") as fp: fp.write(w_str) diff --git a/dpdata/plugins/vasp.py b/dpdata/plugins/vasp.py index d0681ceb..0160bde2 100644 --- a/dpdata/plugins/vasp.py +++ b/dpdata/plugins/vasp.py @@ -1,12 +1,17 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np import dpdata.vasp.outcar import dpdata.vasp.poscar import dpdata.vasp.xml from dpdata.format import Format -from dpdata.utils import uniq_atom_names +from dpdata.utils import open_file, uniq_atom_names + +if TYPE_CHECKING: + from dpdata.utils import FileType @Format.register("poscar") @@ -15,14 +20,14 @@ @Format.register("vasp/contcar") class VASPPoscarFormat(Format): @Format.post("rot_lower_triangular") - def from_system(self, file_name, **kwargs): - with open(file_name) as fp: + def from_system(self, file_name: FileType, **kwargs): + with open_file(file_name) as fp: lines = [line.rstrip("\n") for line in fp] data = dpdata.vasp.poscar.to_system_data(lines) data = uniq_atom_names(data) return data - def to_system(self, data, file_name, frame_idx=0, **kwargs): + def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs): """Dump the system in vasp POSCAR format. Parameters @@ -37,7 +42,7 @@ def to_system(self, data, file_name, frame_idx=0, **kwargs): other parameters """ w_str = VASPStringFormat().to_system(data, frame_idx=frame_idx) - with open(file_name, "w") as fp: + with open_file(file_name, "w") as fp: fp.write(w_str) diff --git a/dpdata/plugins/xyz.py b/dpdata/plugins/xyz.py index 322bf77c..d56a8618 100644 --- a/dpdata/plugins/xyz.py +++ b/dpdata/plugins/xyz.py @@ -1,8 +1,14 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np from dpdata.format import Format +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType from dpdata.xyz.quip_gap_xyz import QuipGapxyzSystems from dpdata.xyz.xyz import coord_to_xyz, xyz_to_coord @@ -16,16 +22,16 @@ class XYZFormat(Format): >>> s.to("xyz", "a.xyz") """ - def to_system(self, data, file_name, **kwargs): + def to_system(self, data, file_name: FileType, **kwargs): buff = [] types = np.array(data["atom_names"])[data["atom_types"]] for cc in data["coords"]: buff.append(coord_to_xyz(cc, types)) - with open(file_name, "w") as fp: + with open_file(file_name, "w") as fp: fp.write("\n".join(buff)) - def from_system(self, file_name, **kwargs): - with open(file_name) as fp: + def from_system(self, file_name: FileType, **kwargs): + with open_file(file_name) as fp: coords, types = xyz_to_coord(fp.read()) atom_names, atom_types, atom_numbs = np.unique( types, return_inverse=True, return_counts=True diff --git a/dpdata/psi4/output.py b/dpdata/psi4/output.py index c06eb182..c3594ffb 100644 --- a/dpdata/psi4/output.py +++ b/dpdata/psi4/output.py @@ -1,11 +1,17 @@ from __future__ import annotations +from typing import TYPE_CHECKING + import numpy as np from dpdata.unit import LengthConversion +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType -def read_psi4_output(fn: str) -> tuple[str, np.ndarray, float, np.ndarray]: +def read_psi4_output(fn: FileType) -> tuple[str, np.ndarray, float, np.ndarray]: """Read from Psi4 output. Note that both the energy and the gradient should be printed. @@ -31,7 +37,7 @@ def read_psi4_output(fn: str) -> tuple[str, np.ndarray, float, np.ndarray]: forces = None energy = None length_unit = None - with open(fn) as f: + with open_file(fn) as f: flag = 0 for line in f: if flag in (1, 3, 4, 5, 6): diff --git a/dpdata/qe/scf.py b/dpdata/qe/scf.py index 37e5fbab..f8670860 100755 --- a/dpdata/qe/scf.py +++ b/dpdata/qe/scf.py @@ -5,6 +5,8 @@ import numpy as np +from dpdata.utils import open_file + ry2ev = 13.605693009 bohr2ang = 0.52917721067 kbar2evperang3 = 1e3 / 1.602176621e6 @@ -142,9 +144,9 @@ def get_frame(fname): path_out = fname[1] else: raise RuntimeError("invalid input") - with open(path_out) as fp: + with open_file(path_out) as fp: outlines = fp.read().split("\n") - with open(path_in) as fp: + with open_file(path_in) as fp: inlines = fp.read().split("\n") cell = get_cell(inlines) atom_names, natoms, types, coords = get_coords(inlines, cell) diff --git a/dpdata/qe/traj.py b/dpdata/qe/traj.py index 1fbf0f71..b4be303a 100644 --- a/dpdata/qe/traj.py +++ b/dpdata/qe/traj.py @@ -2,9 +2,15 @@ from __future__ import annotations import warnings +from typing import TYPE_CHECKING import numpy as np +from dpdata.utils import open_file + +if TYPE_CHECKING: + from dpdata.utils import FileType + from ..unit import ( EnergyConversion, ForceConversion, @@ -87,8 +93,8 @@ def load_atom_types(lines, natoms, atom_names): return np.array(ret, dtype=int) -def load_param_file(fname): - with open(fname) as fp: +def load_param_file(fname: FileType): + with open_file(fname) as fp: lines = fp.read().split("\n") natoms = int(load_key(lines, "nat")) ntypes = int(load_key(lines, "ntyp")) @@ -127,11 +133,11 @@ def _load_pos_block(fp, natoms): return blk, ss -def load_data(fname, natoms, begin=0, step=1, convert=1.0): +def load_data(fname: FileType, natoms, begin=0, step=1, convert=1.0): coords = [] steps = [] cc = 0 - with open(fname) as fp: + with open_file(fname) as fp: while True: blk, ss = _load_pos_block(fp, natoms) if blk is None: @@ -147,7 +153,7 @@ def load_data(fname, natoms, begin=0, step=1, convert=1.0): # def load_pos(fname, natoms) : # coords = [] -# with open(fname) as fp: +# with open_file(fname) as fp: # while True: # blk = _load_pos_block(fp, natoms) # # print(blk) @@ -164,7 +170,7 @@ def load_energy(fname, begin=0, step=1): steps = [] for ii in data[begin::step, 0]: steps.append("%d" % ii) - with open(fname) as fp: + with open_file(fname) as fp: while True: line = fp.readline() if not line: @@ -178,7 +184,7 @@ def load_energy(fname, begin=0, step=1): # def load_force(fname, natoms) : # coords = [] -# with open(fname) as fp: +# with open_file(fname) as fp: # while True: # blk = _load_pos_block(fp, natoms) # # print(blk) diff --git a/dpdata/utils.py b/dpdata/utils.py index e008120e..8942bd54 100644 --- a/dpdata/utils.py +++ b/dpdata/utils.py @@ -1,7 +1,10 @@ from __future__ import annotations +import io +import os import sys -from typing import overload +from contextlib import contextmanager +from typing import TYPE_CHECKING, Generator, overload if sys.version_info >= (3, 8): from typing import Literal @@ -129,3 +132,44 @@ def uniq_atom_names(data): def utf8len(s: str) -> int: """Return the byte length of a string.""" return len(s.encode("utf-8")) + + +if TYPE_CHECKING: + FileType = io.IOBase | str | os.PathLike + + +@contextmanager +def open_file(file: FileType, *args, **kwargs) -> Generator[io.IOBase, None, None]: + """A context manager that yields a file object. + + Parameters + ---------- + file : file object or file path + A file object or a file path. + + Yields + ------ + file : io.IOBase + A file object. + *args + parameters to open + **kwargs + other parameters + + Raises + ------ + ValueError + If file is not a file object or a file + + Examples + -------- + >>> with open_file("file.txt") as file: + ... print(file.read()) + """ + if isinstance(file, io.IOBase): + yield file + elif isinstance(file, (str, os.PathLike)): + with open(file, *args, **kwargs) as f: + yield f + else: + raise ValueError("file must be a file object or a file path.") diff --git a/tests/test_read_file.py b/tests/test_read_file.py new file mode 100644 index 00000000..e7dca54e --- /dev/null +++ b/tests/test_read_file.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +import io +import unittest +from pathlib import Path + +from dpdata.utils import open_file + + +class TestReadFile(unittest.TestCase): + def test_open_file_from_string_io(self): + string_io = io.StringIO("Hello, world!") + with open_file(string_io) as file: + self.assertEqual(file.read(), "Hello, world!") + + def test_open_file_from_file_str(self): + with open_file("/dev/null") as file: + self.assertEqual(file.read(), Path("/dev/null").read_text()) + + def test_open_file_from_file_path(self): + with open_file(Path("/dev/null")) as file: + self.assertEqual(file.read(), Path("/dev/null").read_text()) From c311d16ff9db600815acdf23b005fa4ad388607c Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 2 Sep 2024 22:59:20 -0400 Subject: [PATCH 12/33] fix: only apply comp_prec for floating dtypes (#711) Fix #703. ## Summary by CodeRabbit - **New Features** - Improved handling of data types during the reshaping process, ensuring type conversion only occurs for floating-point data. - **Bug Fixes** - Enhanced robustness of data processing by preventing unnecessary type casting for non-floating-point data types. Signed-off-by: Jinzhe Zeng --- dpdata/deepmd/comp.py | 4 +++- dpdata/deepmd/hdf5.py | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dpdata/deepmd/comp.py b/dpdata/deepmd/comp.py index 5ba3914e..3d656b70 100644 --- a/dpdata/deepmd/comp.py +++ b/dpdata/deepmd/comp.py @@ -204,7 +204,9 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): f"Shape of {dtype.name} is not (nframes, ...), but {dtype.shape}. This type of data will not converted to deepmd/npy format." ) continue - ddata = np.reshape(data[dtype.name], [nframes, -1]).astype(comp_prec) + ddata = np.reshape(data[dtype.name], [nframes, -1]) + if np.issubdtype(ddata.dtype, np.floating): + ddata = ddata.astype(comp_prec) for ii in range(nsets): set_stt = ii * set_size set_end = (ii + 1) * set_size diff --git a/dpdata/deepmd/hdf5.py b/dpdata/deepmd/hdf5.py index 78f4e33f..6b587893 100644 --- a/dpdata/deepmd/hdf5.py +++ b/dpdata/deepmd/hdf5.py @@ -252,9 +252,10 @@ def dump( for dt, prop in data_types.items(): if dt in data: if prop["dump"]: - reshaped_data[dt] = np.reshape(data[dt], prop["shape"]).astype( - comp_prec - ) + ddata = np.reshape(data[dt], prop["shape"]) + if np.issubdtype(ddata.dtype, np.floating): + ddata = ddata.astype(comp_prec) + reshaped_data[dt] = ddata # dump frame properties: cell, coord, energy, force and virial nsets = nframes // set_size From bad02850c7cb982f937ce89e296809948081f9c7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Sep 2024 11:00:47 +0800 Subject: [PATCH 13/33] [pre-commit.ci] pre-commit autoupdate (#712) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.2 → v0.6.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.2...v0.6.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f4abb50c..3f43b697 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.2 + rev: v0.6.3 hooks: - id: ruff args: ["--fix"] From e393a8c58f3cfbea48705cb017110d952e0ce991 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 3 Sep 2024 20:15:43 -0400 Subject: [PATCH 14/33] docs: fix directives (#715) The previous syntax is for RST files. ## Summary by CodeRabbit - **New Features** - Introduced the `BondOrderSystem` class for managing chemical bond information with enhanced sanitization capabilities. - **Documentation** - Updated class reference formatting across multiple documentation files for clarity and consistency, changing from colon to curly brace syntax. --- docs/plugin.md | 2 +- docs/systems/bond_order_system.md | 6 +++--- docs/systems/mixed.md | 2 +- docs/systems/multi.md | 10 +++++----- docs/systems/system.md | 12 ++++++------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/plugin.md b/docs/plugin.md index 44e8eabf..bf3266d5 100644 --- a/docs/plugin.md +++ b/docs/plugin.md @@ -1,7 +1,7 @@ # Plugins One can follow a simple example under `plugin_example/` directory to add their own format by creating and installing plugins. -It's critical to add the :class:`Format` class to `entry_points['dpdata.plugins']` in `pyproject.toml`: +It's critical to add the {class}`Format ` class to `entry_points['dpdata.plugins']` in `pyproject.toml`: ```toml [project.entry-points.'dpdata.plugins'] diff --git a/docs/systems/bond_order_system.md b/docs/systems/bond_order_system.md index 443120a7..8d92d724 100644 --- a/docs/systems/bond_order_system.md +++ b/docs/systems/bond_order_system.md @@ -1,6 +1,6 @@ ## BondOrderSystem -A new class :class:`BondOrderSystem` which inherits from class :class:`System` is introduced in dpdata. This new class contains information of chemical bonds and formal charges (stored in `BondOrderSystem.data['bonds']`, `BondOrderSystem.data['formal_charges']`). Now BondOrderSystem can only read from .mol/.sdf formats, because of its dependency on rdkit (which means rdkit must be installed if you want to use this function). Other formats, such as pdb, must be converted to .mol/.sdf format (maybe with software like open babel). +A new class {class}`BondOrderSystem ` which inherits from class {class}`System ` is introduced in dpdata. This new class contains information of chemical bonds and formal charges (stored in `BondOrderSystem.data['bonds']`, `BondOrderSystem.data['formal_charges']`). Now BondOrderSystem can only read from .mol/.sdf formats, because of its dependency on rdkit (which means rdkit must be installed if you want to use this function). Other formats, such as pdb, must be converted to .mol/.sdf format (maybe with software like open babel). ```python import dpdata @@ -12,7 +12,7 @@ system_2 = dpdata.BondOrderSystem( ) # read from .sdf file ``` In sdf file, all molecules must be of the same topology (i.e. conformers of the same molecular configuration). -`BondOrderSystem` also supports initialize from a :class:`rdkit.Chem.rdchem.Mol` object directly. +`BondOrderSystem ` also supports initialize from a {class}`rdkit.Chem.rdchem.Mol` object directly. ```python from rdkit import Chem from rdkit.Chem import AllChem @@ -25,7 +25,7 @@ system = dpdata.BondOrderSystem(rdkit_mol=mol) ``` ### Bond Order Assignment -The :class:`BondOrderSystem` implements a more robust sanitize procedure for rdkit Mol, as defined in :class:`dpdata.rdkit.santizie.Sanitizer`. This class defines 3 level of sanitization process by: low, medium and high. (default is medium). +The {class}`BondOrderSystem ` implements a more robust sanitize procedure for rdkit Mol, as defined in {class}`dpdata.rdkit.santizie.Sanitizer`. This class defines 3 level of sanitization process by: low, medium and high. (default is medium). + low: use `rdkit.Chem.SanitizeMol()` function to sanitize molecule. + medium: before using rdkit, the programm will first assign formal charge of each atom to avoid inappropriate valence exceptions. However, this mode requires the rightness of the bond order information in the given molecule. + high: the program will try to fix inappropriate bond orders in aromatic hetreocycles, phosphate, sulfate, carboxyl, nitro, nitrine, guanidine groups. If this procedure fails to sanitize the given molecule, the program will then try to call `obabel` to pre-process the mol and repeat the sanitization procedure. **That is to say, if you wan't to use this level of sanitization, please ensure `obabel` is installed in the environment.** diff --git a/docs/systems/mixed.md b/docs/systems/mixed.md index 69bf80ea..25837ea6 100644 --- a/docs/systems/mixed.md +++ b/docs/systems/mixed.md @@ -1,6 +1,6 @@ # Mixed Type Format -The format `deepmd/npy/mixed` is the mixed type numpy format for DeePMD-kit, and can be loaded or dumped through class :class:`dpdata.MultiSystems`. +The format `deepmd/npy/mixed` is the mixed type numpy format for DeePMD-kit, and can be loaded or dumped through class {class}`dpdata.MultiSystems`. Under this format, systems with the same number of atoms but different formula can be put together for a larger system, especially when the frame numbers in systems are sparse. diff --git a/docs/systems/multi.md b/docs/systems/multi.md index 8e47acf7..20551c7e 100644 --- a/docs/systems/multi.md +++ b/docs/systems/multi.md @@ -1,15 +1,15 @@ # `MultiSystems` -The Class :class:`dpdata.MultiSystems` can read data from a dir which may contains many files of different systems, or from single xyz file which contains different systems. +The Class {class}`dpdata.MultiSystems` can read data from a dir which may contains many files of different systems, or from single xyz file which contains different systems. -Use :meth:`dpdata.MultiSystems.from_dir` to read from a directory, :class:`dpdata.MultiSystems` will walk in the directory -Recursively and find all file with specific file_name. Supports all the file formats that :class:`dpdata.LabeledSystem` supports. +Use {meth}`dpdata.MultiSystems.from_dir` to read from a directory, {class}`dpdata.MultiSystems` will walk in the directory +Recursively and find all file with specific file_name. Supports all the file formats that {class}`dpdata.LabeledSystem` supports. -Use :meth:`dpdata.MultiSystems.from_file` to read from single file. Single-file support is available for the `quip/gap/xyz` and `ase/structure` formats. +Use {meth}`dpdata.MultiSystems.from_file` to read from single file. Single-file support is available for the `quip/gap/xyz` and `ase/structure` formats. For example, for `quip/gap xyz` files, single .xyz file may contain many different configurations with different atom numbers and atom type. -The following commands relating to :class:`dpdata.MultiSystems` may be useful. +The following commands relating to {class}`dpdata.MultiSystems` may be useful. ```python # load data diff --git a/docs/systems/system.md b/docs/systems/system.md index 0401a311..9f01fc40 100644 --- a/docs/systems/system.md +++ b/docs/systems/system.md @@ -19,21 +19,21 @@ or let dpdata infer the format (`vasp/poscar`) of the file from the file name ex ```python d_poscar = dpdata.System("my.POSCAR") ``` -The number of atoms, atom types, coordinates are loaded from the `POSCAR` and stored to a data :class:`System` called `d_poscar`. -A data :class:`System` (a concept used by [deepmd-kit](https://github.com/deepmodeling/deepmd-kit)) contains frames that has the same number of atoms of the same type. The order of the atoms should be consistent among the frames in one :class:`System`. +The number of atoms, atom types, coordinates are loaded from the `POSCAR` and stored to a data {class}`System ` called `d_poscar`. +A data {class}`System ` (a concept used by [deepmd-kit](https://github.com/deepmodeling/deepmd-kit)) contains frames that has the same number of atoms of the same type. The order of the atoms should be consistent among the frames in one {class}`System `. It is noted that `POSCAR` only contains one frame. If the multiple frames stored in, for example, a `OUTCAR` is wanted, ```python d_outcar = dpdata.LabeledSystem("OUTCAR") ``` -The labels provided in the `OUTCAR`, i.e. energies, forces and virials (if any), are loaded by :class:`LabeledSystem`. It is noted that the forces of atoms are always assumed to exist. :class:`LabeledSystem` is a derived class of :class:`System`. +The labels provided in the `OUTCAR`, i.e. energies, forces and virials (if any), are loaded by {class}`LabeledSystem `. It is noted that the forces of atoms are always assumed to exist. {class}`LabeledSystem ` is a derived class of {class}`System `. -The :class:`System` or :class:`LabeledSystem` can be constructed from the following file formats with the `format key` in the table passed to argument `fmt`: +The {class}`System ` or {class}`LabeledSystem ` can be constructed from the [supported file formats](../formats.rst) with the `format key` in the table passed to argument `fmt`. ### Access data -These properties stored in :class:`System` and :class:`LabeledSystem` can be accessed by operator `[]` with the key of the property supplied, for example +These properties stored in {class}`System ` and {class}`LabeledSystem ` can be accessed by operator `[]` with the key of the property supplied, for example ```python coords = d_outcar["coords"] ``` @@ -52,7 +52,7 @@ Available properties are (nframe: number of frames in the system, natoms: total ### Dump data -The data stored in :class:`System` or :class:`LabeledSystem` can be dumped in 'lammps/lmp' or 'vasp/poscar' format, for example: +The data stored in {class}`System ` or {class}`LabeledSystem ` can be dumped in 'lammps/lmp' or 'vasp/poscar' format, for example: ```python d_outcar.to("lammps/lmp", "conf.lmp", frame_idx=0) ``` From 976cf16ab6ad0c975ee259c17cd7f06f8aa201a8 Mon Sep 17 00:00:00 2001 From: Levi Zhou <31941107+ZhouXY-PKU@users.noreply.github.com> Date: Wed, 4 Sep 2024 21:49:48 +0800 Subject: [PATCH 15/33] Update ase.py: To avoid errors in writing to the .extxyz format from an Atom object coverted from .npy with dpdata. (#717) --- dpdata/plugins/ase.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dpdata/plugins/ase.py b/dpdata/plugins/ase.py index 2ca22926..83a6bcf9 100644 --- a/dpdata/plugins/ase.py +++ b/dpdata/plugins/ase.py @@ -181,7 +181,17 @@ def to_labeled_system(self, data, *args, **kwargs) -> list[ase.Atoms]: # v_pref = 1 * 1e4 / 1.602176621e6 vol = structure.get_volume() # results['stress'] = data["virials"][ii] / (v_pref * vol) - results["stress"] = -data["virials"][ii] / vol + stress33 = -data["virials"][ii] / vol + results["stress"] = np.array( + [ + stress33[0][0], + stress33[1][1], + stress33[2][2], + stress33[1][2], + stress33[0][2], + stress33[0][1], + ] + ) structure.calc = SinglePointCalculator(structure, **results) structures.append(structure) From 1de5acea87683d9d4ac4d0055d55ab6aad931016 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:22:00 +0800 Subject: [PATCH 16/33] [pre-commit.ci] pre-commit autoupdate (#720) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.3 → v0.6.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.3...v0.6.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f43b697..dfe604f3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.3 + rev: v0.6.4 hooks: - id: ruff args: ["--fix"] From fb942bb94cd142d03f52d2def952df954592f42c Mon Sep 17 00:00:00 2001 From: Chenqqian Zhang <100290172+Chengqian-Zhang@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:39:55 +0800 Subject: [PATCH 17/33] Feat: Support specifying proportion of atoms to be perturbed in System (#716) See title. ## Summary by CodeRabbit - **New Features** - Introduced a new parameter for controlled atom perturbation in the perturb function, enhancing flexibility. - **Bug Fixes** - Improved logic for selecting atoms to perturb, ensuring only a specified proportion is affected. - **Tests** - Added a new test class to validate the perturbation functionality for atomic systems, increasing test coverage and reliability. - Introduced a structured representation of a Silicon Carbide crystal for validation in tests. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dpdata/system.py | 13 ++++++++++- tests/poscars/POSCAR.SiC.partpert | 16 +++++++++++++ tests/test_perturb.py | 39 +++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/poscars/POSCAR.SiC.partpert diff --git a/dpdata/system.py b/dpdata/system.py index 2613166a..42b3f4e7 100644 --- a/dpdata/system.py +++ b/dpdata/system.py @@ -849,6 +849,7 @@ def perturb( cell_pert_fraction: float, atom_pert_distance: float, atom_pert_style: str = "normal", + atom_pert_prob: float = 1.0, ): """Perturb each frame in the system randomly. The cell will be deformed randomly, and atoms will be displaced by a random distance in random direction. @@ -877,6 +878,8 @@ def perturb( These points are treated as vector used by atoms to move. Obviously, the max length of the distance atoms move is `atom_pert_distance`. - `'const'`: The distance atoms move will be a constant `atom_pert_distance`. + atom_pert_prob : float + Determine the proportion of the total number of atoms in a frame that are perturbed. Returns ------- @@ -900,7 +903,15 @@ def perturb( tmp_system.data["coords"][0] = np.matmul( tmp_system.data["coords"][0], cell_perturb_matrix ) - for kk in range(len(tmp_system.data["coords"][0])): + pert_natoms = int(atom_pert_prob * len(tmp_system.data["coords"][0])) + pert_atom_id = sorted( + np.random.choice( + range(len(tmp_system.data["coords"][0])), + pert_natoms, + replace=False, + ).tolist() + ) + for kk in pert_atom_id: atom_perturb_vector = get_atom_perturb_vector( atom_pert_distance, atom_pert_style ) diff --git a/tests/poscars/POSCAR.SiC.partpert b/tests/poscars/POSCAR.SiC.partpert new file mode 100644 index 00000000..859de7ca --- /dev/null +++ b/tests/poscars/POSCAR.SiC.partpert @@ -0,0 +1,16 @@ +C4 Si4 +1.0 +4.0354487481064565e+00 1.1027270790560616e-17 2.5642993008475204e-17 +2.0693526054669642e-01 4.1066892997402196e+00 -8.6715682899078028e-18 +4.2891472979598610e-01 5.5796885749827474e-01 4.1100061517204542e+00 +C Si +4 4 +Cartesian + 0.03122504 0.15559669 2.1913045 + 1.93908836 -0.08678864 0.06748919 + 0.13114716 2.15827511 0.06333341 + 2.36161952 1.42824405 2.58837618 +-0.03895165 0.12197669 0.05496244 + 1.79528462 2.48830207 -0.55733221 + 2.11363589 0.09280028 2.0301803 + 0.19221505 2.16245144 2.07930701 diff --git a/tests/test_perturb.py b/tests/test_perturb.py index eea71116..5c2f4a7b 100644 --- a/tests/test_perturb.py +++ b/tests/test_perturb.py @@ -12,6 +12,7 @@ class NormalGenerator: def __init__(self): self.randn_generator = self.get_randn_generator() self.rand_generator = self.get_rand_generator() + self.choice_generator = self.get_choice_generator() def randn(self, number): return next(self.randn_generator) @@ -19,6 +20,9 @@ def randn(self, number): def rand(self, number): return next(self.rand_generator) + def choice(self, total_natoms, pert_natoms, replace): + return next(self.choice_generator)[:pert_natoms] + @staticmethod def get_randn_generator(): data = np.asarray( @@ -44,11 +48,16 @@ def get_rand_generator(): [0.23182233, 0.87106847, 0.68728511, 0.94180274, 0.92860453, 0.69191187] ) + @staticmethod + def get_choice_generator(): + yield np.asarray([5, 3, 7, 6, 2, 1, 4, 0]) + class UniformGenerator: def __init__(self): self.randn_generator = self.get_randn_generator() self.rand_generator = self.get_rand_generator() + self.choice_generator = self.get_choice_generator() def randn(self, number): return next(self.randn_generator) @@ -56,6 +65,9 @@ def randn(self, number): def rand(self, number): return next(self.rand_generator) + def choice(self, total_natoms, pert_natoms, replace): + return next(self.choice_generator) + @staticmethod def get_randn_generator(): data = [ @@ -97,11 +109,16 @@ def get_rand_generator(): yield np.asarray(data[count]) count += 1 + @staticmethod + def get_choice_generator(): + yield np.asarray([5, 3, 7, 6, 2, 1, 4, 0]) + class ConstGenerator: def __init__(self): self.randn_generator = self.get_randn_generator() self.rand_generator = self.get_rand_generator() + self.choice_generator = self.get_choice_generator() def randn(self, number): return next(self.randn_generator) @@ -109,6 +126,9 @@ def randn(self, number): def rand(self, number): return next(self.rand_generator) + def choice(self, total_natoms, pert_natoms, replace): + return next(self.choice_generator) + @staticmethod def get_randn_generator(): data = np.asarray( @@ -135,6 +155,10 @@ def get_rand_generator(): [0.01525907, 0.68387374, 0.39768541, 0.55596047, 0.26557088, 0.60883073] ) + @staticmethod + def get_choice_generator(): + yield np.asarray([5, 3, 7, 6, 2, 1, 4, 0]) + # %% class TestPerturbNormal(unittest.TestCase, CompSys, IsPBC): @@ -142,6 +166,7 @@ class TestPerturbNormal(unittest.TestCase, CompSys, IsPBC): def setUp(self, random_mock): random_mock.rand = NormalGenerator().rand random_mock.randn = NormalGenerator().randn + random_mock.choice = NormalGenerator().choice system_1_origin = dpdata.System("poscars/POSCAR.SiC", fmt="vasp/poscar") self.system_1 = system_1_origin.perturb(1, 0.05, 0.6, "normal") self.system_2 = dpdata.System("poscars/POSCAR.SiC.normal", fmt="vasp/poscar") @@ -153,6 +178,7 @@ class TestPerturbUniform(unittest.TestCase, CompSys, IsPBC): def setUp(self, random_mock): random_mock.rand = UniformGenerator().rand random_mock.randn = UniformGenerator().randn + random_mock.choice = UniformGenerator().choice system_1_origin = dpdata.System("poscars/POSCAR.SiC", fmt="vasp/poscar") self.system_1 = system_1_origin.perturb(1, 0.05, 0.6, "uniform") self.system_2 = dpdata.System("poscars/POSCAR.SiC.uniform", fmt="vasp/poscar") @@ -164,11 +190,24 @@ class TestPerturbConst(unittest.TestCase, CompSys, IsPBC): def setUp(self, random_mock): random_mock.rand = ConstGenerator().rand random_mock.randn = ConstGenerator().randn + random_mock.choice = ConstGenerator().choice system_1_origin = dpdata.System("poscars/POSCAR.SiC", fmt="vasp/poscar") self.system_1 = system_1_origin.perturb(1, 0.05, 0.6, "const") self.system_2 = dpdata.System("poscars/POSCAR.SiC.const", fmt="vasp/poscar") self.places = 6 +class TestPerturbPartAtoms(unittest.TestCase, CompSys, IsPBC): + @patch("numpy.random") + def setUp(self, random_mock): + random_mock.rand = NormalGenerator().rand + random_mock.randn = NormalGenerator().randn + random_mock.choice = NormalGenerator().choice + system_1_origin = dpdata.System("poscars/POSCAR.SiC", fmt="vasp/poscar") + self.system_1 = system_1_origin.perturb(1, 0.05, 0.6, "normal", 0.25) + self.system_2 = dpdata.System("poscars/POSCAR.SiC.partpert", fmt="vasp/poscar") + self.places = 6 + + if __name__ == "__main__": unittest.main() From 480242e3114f711a2d94e99f4b6ab7faaf7ba4ea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 18 Sep 2024 08:35:41 +0800 Subject: [PATCH 18/33] [pre-commit.ci] pre-commit autoupdate (#722) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.4 → v0.6.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.4...v0.6.5) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dfe604f3..8da028de 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.4 + rev: v0.6.5 hooks: - id: ruff args: ["--fix"] From a2fbdd8dae6417dbe3704922c9fc4d9b9ed5b9e1 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Thu, 19 Sep 2024 20:29:21 -0400 Subject: [PATCH 19/33] feat: support data type dumped to a different name (#727) ## Summary by CodeRabbit - **New Features** - Introduced an optional `deepmd_name` parameter in the `DataType` class for enhanced naming flexibility. - Updated data type declarations in the `System` and `LabeledSystem` classes for better integration with the DeepMD framework. - **Bug Fixes** - Removed handling of energy, force, and virial data to simplify data processing and storage. - **Documentation** - Updated documentation for the `DataType` class to clarify the new `deepmd_name` parameter. Signed-off-by: Jinzhe Zeng --- dpdata/data_type.py | 4 +++ dpdata/deepmd/comp.py | 62 ++++--------------------------------------- dpdata/deepmd/hdf5.py | 61 +++++------------------------------------- dpdata/deepmd/raw.py | 45 +++---------------------------- dpdata/system.py | 20 ++++++++++---- 5 files changed, 35 insertions(+), 157 deletions(-) diff --git a/dpdata/data_type.py b/dpdata/data_type.py index bbc7401d..851a65c9 100644 --- a/dpdata/data_type.py +++ b/dpdata/data_type.py @@ -46,6 +46,8 @@ class DataType: represents numbers required : bool, default=True whether this data is required + deepmd_name : str, optional + DeePMD-kit data type name. When not given, it is the same as `name`. """ def __init__( @@ -54,11 +56,13 @@ def __init__( dtype: type, shape: tuple[int | Axis, ...] | None = None, required: bool = True, + deepmd_name: str | None = None, ) -> None: self.name = name self.dtype = dtype self.shape = shape self.required = required + self.deepmd_name = name if deepmd_name is None else deepmd_name def real_shape(self, system: System) -> tuple[int]: """Returns expected real shape of a system.""" diff --git a/dpdata/deepmd/comp.py b/dpdata/deepmd/comp.py index 3d656b70..67eddd9f 100644 --- a/dpdata/deepmd/comp.py +++ b/dpdata/deepmd/comp.py @@ -26,10 +26,7 @@ def _load_set(folder, nopbc: bool): cells = np.zeros((coords.shape[0], 3, 3)) else: cells = np.load(os.path.join(folder, "box.npy")) - eners = _cond_load_data(os.path.join(folder, "energy.npy")) - forces = _cond_load_data(os.path.join(folder, "force.npy")) - virs = _cond_load_data(os.path.join(folder, "virial.npy")) - return cells, coords, eners, forces, virs + return cells, coords def to_system_data(folder, type_map=None, labels=True): @@ -41,31 +38,13 @@ def to_system_data(folder, type_map=None, labels=True): sets = sorted(glob.glob(os.path.join(folder, "set.*"))) all_cells = [] all_coords = [] - all_eners = [] - all_forces = [] - all_virs = [] for ii in sets: - cells, coords, eners, forces, virs = _load_set(ii, data.get("nopbc", False)) + cells, coords = _load_set(ii, data.get("nopbc", False)) nframes = np.reshape(cells, [-1, 3, 3]).shape[0] all_cells.append(np.reshape(cells, [nframes, 3, 3])) all_coords.append(np.reshape(coords, [nframes, -1, 3])) - if eners is not None: - eners = np.reshape(eners, [nframes]) - if labels: - if eners is not None and eners.size > 0: - all_eners.append(np.reshape(eners, [nframes])) - if forces is not None and forces.size > 0: - all_forces.append(np.reshape(forces, [nframes, -1, 3])) - if virs is not None and virs.size > 0: - all_virs.append(np.reshape(virs, [nframes, 3, 3])) data["cells"] = np.concatenate(all_cells, axis=0) data["coords"] = np.concatenate(all_coords, axis=0) - if len(all_eners) > 0: - data["energies"] = np.concatenate(all_eners, axis=0) - if len(all_forces) > 0: - data["forces"] = np.concatenate(all_forces, axis=0) - if len(all_virs) > 0: - data["virials"] = np.concatenate(all_virs, axis=0) # allow custom dtypes if labels: dtypes = dpdata.system.LabeledSystem.DTYPES @@ -82,9 +61,6 @@ def to_system_data(folder, type_map=None, labels=True): "coords", "real_atom_names", "nopbc", - "energies", - "forces", - "virials", ): # skip as these data contains specific rules continue @@ -93,13 +69,13 @@ def to_system_data(folder, type_map=None, labels=True): f"Shape of {dtype.name} is not (nframes, ...), but {dtype.shape}. This type of data will not converted from deepmd/npy format." ) continue - natoms = data["coords"].shape[1] + natoms = data["atom_types"].shape[0] shape = [ natoms if xx == dpdata.system.Axis.NATOMS else xx for xx in dtype.shape[1:] ] all_data = [] for ii in sets: - tmp = _cond_load_data(os.path.join(ii, dtype.name + ".npy")) + tmp = _cond_load_data(os.path.join(ii, dtype.deepmd_name + ".npy")) if tmp is not None: all_data.append(np.reshape(tmp, [tmp.shape[0], *shape])) if len(all_data) > 0: @@ -136,19 +112,6 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): np.savetxt(os.path.join(folder, "formal_charges.raw"), data["formal_charges"]) # reshape frame properties and convert prec nframes = data["cells"].shape[0] - cells = np.reshape(data["cells"], [nframes, 9]).astype(comp_prec) - coords = np.reshape(data["coords"], [nframes, -1]).astype(comp_prec) - eners = None - forces = None - virials = None - if "energies" in data: - eners = np.reshape(data["energies"], [nframes]).astype(comp_prec) - if "forces" in data: - forces = np.reshape(data["forces"], [nframes, -1]).astype(comp_prec) - if "virials" in data: - virials = np.reshape(data["virials"], [nframes, 9]).astype(comp_prec) - if "atom_pref" in data: - atom_pref = np.reshape(data["atom_pref"], [nframes, -1]).astype(comp_prec) # dump frame properties: cell, coord, energy, force and virial nsets = nframes // set_size if set_size * nsets < nframes: @@ -158,16 +121,6 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): set_end = (ii + 1) * set_size set_folder = os.path.join(folder, "set.%03d" % ii) os.makedirs(set_folder) - np.save(os.path.join(set_folder, "box"), cells[set_stt:set_end]) - np.save(os.path.join(set_folder, "coord"), coords[set_stt:set_end]) - if eners is not None: - np.save(os.path.join(set_folder, "energy"), eners[set_stt:set_end]) - if forces is not None: - np.save(os.path.join(set_folder, "force"), forces[set_stt:set_end]) - if virials is not None: - np.save(os.path.join(set_folder, "virial"), virials[set_stt:set_end]) - if "atom_pref" in data: - np.save(os.path.join(set_folder, "atom_pref"), atom_pref[set_stt:set_end]) try: os.remove(os.path.join(folder, "nopbc")) except OSError: @@ -187,13 +140,8 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): "atom_names", "atom_types", "orig", - "cells", - "coords", "real_atom_names", "nopbc", - "energies", - "forces", - "virials", ): # skip as these data contains specific rules continue @@ -211,4 +159,4 @@ def dump(folder, data, set_size=5000, comp_prec=np.float32, remove_sets=True): set_stt = ii * set_size set_end = (ii + 1) * set_size set_folder = os.path.join(folder, "set.%03d" % ii) - np.save(os.path.join(set_folder, dtype.name), ddata[set_stt:set_end]) + np.save(os.path.join(set_folder, dtype.deepmd_name), ddata[set_stt:set_end]) diff --git a/dpdata/deepmd/hdf5.py b/dpdata/deepmd/hdf5.py index 6b587893..cbdaa632 100644 --- a/dpdata/deepmd/hdf5.py +++ b/dpdata/deepmd/hdf5.py @@ -69,38 +69,7 @@ def to_system_data( data["nopbc"] = True sets = globfilter(g.keys(), "set.*") - data_types = { - "cells": { - "fn": "box", - "labeled": False, - "shape": (3, 3), - "required": "nopbc" not in data, - }, - "coords": { - "fn": "coord", - "labeled": False, - "shape": (natoms, 3), - "required": True, - }, - "energies": { - "fn": "energy", - "labeled": True, - "shape": tuple(), - "required": False, - }, - "forces": { - "fn": "force", - "labeled": True, - "shape": (natoms, 3), - "required": False, - }, - "virials": { - "fn": "virial", - "labeled": True, - "shape": (3, 3), - "required": False, - }, - } + data_types = {} # allow custom dtypes if labels: dtypes = dpdata.system.LabeledSystem.DTYPES @@ -112,14 +81,9 @@ def to_system_data( "atom_names", "atom_types", "orig", - "cells", - "coords", "real_atom_types", "real_atom_names", "nopbc", - "energies", - "forces", - "virials", ): # skip as these data contains specific rules continue @@ -133,10 +97,10 @@ def to_system_data( ] data_types[dtype.name] = { - "fn": dtype.name, - "labeled": True, + "fn": dtype.deepmd_name, "shape": shape, - "required": False, + "required": dtype.required + and not (dtype.name == "cells" and data.get("nopbc", False)), } for dt, prop in data_types.items(): @@ -206,13 +170,7 @@ def dump( nopbc = data.get("nopbc", False) reshaped_data = {} - data_types = { - "cells": {"fn": "box", "shape": (nframes, 9), "dump": not nopbc}, - "coords": {"fn": "coord", "shape": (nframes, -1), "dump": True}, - "energies": {"fn": "energy", "shape": (nframes,), "dump": True}, - "forces": {"fn": "force", "shape": (nframes, -1), "dump": True}, - "virials": {"fn": "virial", "shape": (nframes, 9), "dump": True}, - } + data_types = {} labels = "energies" in data if labels: @@ -226,14 +184,9 @@ def dump( "atom_names", "atom_types", "orig", - "cells", - "coords", "real_atom_types", "real_atom_names", "nopbc", - "energies", - "forces", - "virials", ): # skip as these data contains specific rules continue @@ -244,9 +197,9 @@ def dump( continue data_types[dtype.name] = { - "fn": dtype.name, + "fn": dtype.deepmd_name, "shape": (nframes, -1), - "dump": True, + "dump": not (dtype.name == "cells" and nopbc), } for dt, prop in data_types.items(): diff --git a/dpdata/deepmd/raw.py b/dpdata/deepmd/raw.py index f8ddcaf3..81b01cc0 100644 --- a/dpdata/deepmd/raw.py +++ b/dpdata/deepmd/raw.py @@ -49,16 +49,6 @@ def to_system_data(folder, type_map=None, labels=True): data["cells"] = np.loadtxt(os.path.join(folder, "box.raw"), ndmin=2) data["cells"] = np.reshape(data["cells"], [nframes, 3, 3]) data["coords"] = np.reshape(data["coords"], [nframes, -1, 3]) - if labels: - if os.path.exists(os.path.join(folder, "energy.raw")): - data["energies"] = np.loadtxt(os.path.join(folder, "energy.raw")) - data["energies"] = np.reshape(data["energies"], [nframes]) - if os.path.exists(os.path.join(folder, "force.raw")): - data["forces"] = np.loadtxt(os.path.join(folder, "force.raw")) - data["forces"] = np.reshape(data["forces"], [nframes, -1, 3]) - if os.path.exists(os.path.join(folder, "virial.raw")): - data["virials"] = np.loadtxt(os.path.join(folder, "virial.raw")) - data["virials"] = np.reshape(data["virials"], [nframes, 3, 3]) if os.path.isfile(os.path.join(folder, "nopbc")): data["nopbc"] = True # allow custom dtypes @@ -77,9 +67,6 @@ def to_system_data(folder, type_map=None, labels=True): "real_atom_types", "real_atom_names", "nopbc", - "energies", - "forces", - "virials", ): # skip as these data contains specific rules continue @@ -88,14 +75,14 @@ def to_system_data(folder, type_map=None, labels=True): f"Shape of {dtype.name} is not (nframes, ...), but {dtype.shape}. This type of data will not converted from deepmd/raw format." ) continue - natoms = data["coords"].shape[1] + natoms = data["atom_types"].shape[0] shape = [ natoms if xx == dpdata.system.Axis.NATOMS else xx for xx in dtype.shape[1:] ] - if os.path.exists(os.path.join(folder, f"{dtype.name}.raw")): + if os.path.exists(os.path.join(folder, f"{dtype.deepmd_name}.raw")): data[dtype.name] = np.reshape( - np.loadtxt(os.path.join(folder, f"{dtype.name}.raw")), + np.loadtxt(os.path.join(folder, f"{dtype.deepmd_name}.raw")), [nframes, *shape], ) return data @@ -108,10 +95,6 @@ def dump(folder, data): nframes = data["cells"].shape[0] np.savetxt(os.path.join(folder, "type.raw"), data["atom_types"], fmt="%d") np.savetxt(os.path.join(folder, "type_map.raw"), data["atom_names"], fmt="%s") - np.savetxt(os.path.join(folder, "box.raw"), np.reshape(data["cells"], [nframes, 9])) - np.savetxt( - os.path.join(folder, "coord.raw"), np.reshape(data["coords"], [nframes, -1]) - ) # BondOrder System if "bonds" in data: np.savetxt( @@ -121,21 +104,6 @@ def dump(folder, data): ) if "formal_charges" in data: np.savetxt(os.path.join(folder, "formal_charges.raw"), data["formal_charges"]) - # Labeled System - if "energies" in data: - np.savetxt( - os.path.join(folder, "energy.raw"), - np.reshape(data["energies"], [nframes, 1]), - ) - if "forces" in data: - np.savetxt( - os.path.join(folder, "force.raw"), np.reshape(data["forces"], [nframes, -1]) - ) - if "virials" in data: - np.savetxt( - os.path.join(folder, "virial.raw"), - np.reshape(data["virials"], [nframes, 9]), - ) try: os.remove(os.path.join(folder, "nopbc")) except OSError: @@ -155,14 +123,9 @@ def dump(folder, data): "atom_names", "atom_types", "orig", - "cells", - "coords", "real_atom_types", "real_atom_names", "nopbc", - "energies", - "forces", - "virials", ): # skip as these data contains specific rules continue @@ -174,4 +137,4 @@ def dump(folder, data): ) continue ddata = np.reshape(data[dtype.name], [nframes, -1]) - np.savetxt(os.path.join(folder, f"{dtype.name}.raw"), ddata) + np.savetxt(os.path.join(folder, f"{dtype.deepmd_name}.raw"), ddata) diff --git a/dpdata/system.py b/dpdata/system.py index 42b3f4e7..08136cb9 100644 --- a/dpdata/system.py +++ b/dpdata/system.py @@ -91,8 +91,10 @@ class System: DataType("atom_names", list, (Axis.NTYPES,)), DataType("atom_types", np.ndarray, (Axis.NATOMS,)), DataType("orig", np.ndarray, (3,)), - DataType("cells", np.ndarray, (Axis.NFRAMES, 3, 3)), - DataType("coords", np.ndarray, (Axis.NFRAMES, Axis.NATOMS, 3)), + DataType("cells", np.ndarray, (Axis.NFRAMES, 3, 3), deepmd_name="box"), + DataType( + "coords", np.ndarray, (Axis.NFRAMES, Axis.NATOMS, 3), deepmd_name="coord" + ), DataType( "real_atom_types", np.ndarray, (Axis.NFRAMES, Axis.NATOMS), required=False ), @@ -1204,9 +1206,17 @@ class LabeledSystem(System): """ DTYPES: tuple[DataType, ...] = System.DTYPES + ( - DataType("energies", np.ndarray, (Axis.NFRAMES,)), - DataType("forces", np.ndarray, (Axis.NFRAMES, Axis.NATOMS, 3)), - DataType("virials", np.ndarray, (Axis.NFRAMES, 3, 3), required=False), + DataType("energies", np.ndarray, (Axis.NFRAMES,), deepmd_name="energy"), + DataType( + "forces", np.ndarray, (Axis.NFRAMES, Axis.NATOMS, 3), deepmd_name="force" + ), + DataType( + "virials", + np.ndarray, + (Axis.NFRAMES, 3, 3), + required=False, + deepmd_name="virial", + ), DataType("atom_pref", np.ndarray, (Axis.NFRAMES, Axis.NATOMS), required=False), ) From 2648d50c12d9bb5b970ff21a9283c36840047d67 Mon Sep 17 00:00:00 2001 From: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> Date: Fri, 20 Sep 2024 08:29:41 +0800 Subject: [PATCH 20/33] fix: qe input blocks not seperated by empty lines (#724) the qe support only parse input file, in which cell blocks are separated by empty lines, like ``` ATOMIC_SPECIES Na 22.989769 Na_ONCV_PBE-1.0.upf CELL_PARAMETERS {angstrom} 7.171683039200000 0.000000000000000 0.000000000000000 ``` however, the input file is valid when no empty line exists, like the following ``` ATOMIC_SPECIES Na 22.989769 Na_ONCV_PBE-1.0.upf CELL_PARAMETERS {angstrom} 7.171683039200000 0.000000000000000 0.000000000000000 ``` This pr fixes the issue ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Improved handling of Quantum Espresso output data for better parsing and clarity. - Enhanced error handling for missing force and stress data. - Added configuration parameters for self-consistent field (SCF) calculations for sodium. - **Bug Fixes** - Updated functions to prevent unwanted lines in data extraction, ensuring cleaner output. - **Tests** - Introduced a new test class to validate the functionality of the `dpdata.LabeledSystem` class with Quantum Espresso output, enhancing test coverage. --------- Co-authored-by: Han Wang --- dpdata/qe/scf.py | 23 ++- tests/qe.scf/na.in | 44 ++++++ tests/qe.scf/na.out | 343 ++++++++++++++++++++++++++++++++++++++++ tests/test_qe_pw_scf.py | 43 +++++ 4 files changed, 450 insertions(+), 3 deletions(-) create mode 100644 tests/qe.scf/na.in create mode 100644 tests/qe.scf/na.out diff --git a/dpdata/qe/scf.py b/dpdata/qe/scf.py index f8670860..7b6a70aa 100755 --- a/dpdata/qe/scf.py +++ b/dpdata/qe/scf.py @@ -11,15 +11,32 @@ bohr2ang = 0.52917721067 kbar2evperang3 = 1e3 / 1.602176621e6 +_QE_BLOCK_KEYWORDS = [ + "ATOMIC_SPECIES", + "ATOMIC_POSITIONS", + "K_POINTS", + "ADDITIONAL_K_POINTS", + "CELL_PARAMETERS", + "CONSTRAINTS", + "OCCUPATIONS", + "ATOMIC_VELOCITIES", + "ATOMIC_FORCES", + "SOLVENTS", + "HUBBARD", +] + def get_block(lines, keyword, skip=0): ret = [] for idx, ii in enumerate(lines): if keyword in ii: blk_idx = idx + 1 + skip - while len(lines[blk_idx]) == 0: + while len(lines[blk_idx].split()) == 0: blk_idx += 1 - while len(lines[blk_idx]) != 0 and blk_idx != len(lines): + while ( + len(lines[blk_idx].split()) != 0 + and (lines[blk_idx].split()[0] not in _QE_BLOCK_KEYWORDS) + ) and blk_idx != len(lines): ret.append(lines[blk_idx]) blk_idx += 1 break @@ -123,7 +140,7 @@ def get_force(lines, natoms): def get_stress(lines): blk = get_block(lines, "total stress") if len(blk) == 0: - return + return None ret = [] for ii in blk: ret.append([float(jj) for jj in ii.split()[3:6]]) diff --git a/tests/qe.scf/na.in b/tests/qe.scf/na.in new file mode 100644 index 00000000..56a2d2e1 --- /dev/null +++ b/tests/qe.scf/na.in @@ -0,0 +1,44 @@ + +&control + calculation = 'scf' + restart_mode = 'from_scratch', + prefix = 'Na' + outdir = './tmp' + pseudo_dir = './' +/ +&SYSTEM + ibrav=0, + nosym=.true., + nat=3, + ntyp=1, + occupations = 'smearing', + smearing = 'gauss', + degauss = 0.02, + ecutwfc = 100, + ecutrho = 960, + lspinorb = .false., + noncolin = .false., +/ +&ELECTRONS + conv_thr = 1.0d-9, + mixing_beta = 0.7, + electron_maxstep = 200, +/ +&ions +/ +&cell +/ +ATOMIC_SPECIES + Na 22.989769 Na_ONCV_PBE-1.0.upf +CELL_PARAMETERS {angstrom} + 7.171683039200000 0.000000000000000 0.000000000000000 + -4.270578118300000 5.761527588200000 0.000000000000000 + -0.000000045600000 0.000000023000000 12.826457854999999 +ATOMIC_POSITIONS (crystal) + + Na 0.940587444301534 0.397635863676890 0.059472381901808 + Na 0.059413515648061 0.602364552456546 0.559472465518034 + Na 0.602364619812068 0.059413062489401 0.059472381901808 +K_POINTS {automatic} + 8 8 4 0 0 0 + \ No newline at end of file diff --git a/tests/qe.scf/na.out b/tests/qe.scf/na.out new file mode 100644 index 00000000..a1f0042e --- /dev/null +++ b/tests/qe.scf/na.out @@ -0,0 +1,343 @@ + + Program PWSCF v.7.0 starts on 18Sep2024 at 9:37:38 + + This program is part of the open-source Quantum ESPRESSO suite + for quantum simulation of materials; please cite + "P. Giannozzi et al., J. Phys.:Condens. Matter 21 395502 (2009); + "P. Giannozzi et al., J. Phys.:Condens. Matter 29 465901 (2017); + "P. Giannozzi et al., J. Chem. Phys. 152 154105 (2020); + URL http://www.quantum-espresso.org", + in publications or presentations arising from this work. More details at + http://www.quantum-espresso.org/quote + + Parallel version (MPI & OpenMP), running on 32 processor cores + Number of MPI processes: 32 + Threads/MPI process: 1 + + MPI processes distributed on 1 nodes + R & G space division: proc/nbgrp/npool/nimage = 32 + 124265 MiB available memory on the printing compute node when the environment starts + + Waiting for input... + Reading input from standard input +Warning: card &CELL ignored +Warning: card / ignored + + Current dimensions of program PWSCF are: + Max number of different atomic species (ntypx) = 10 + Max number of k-points (npk) = 40000 + Max angular momentum in pseudopotentials (lmaxx) = 4 + Message from routine setup: + no reason to have ecutrho>4*ecutwfc + + Subspace diagonalization in iterative solution of the eigenvalue problem: + a serial algorithm will be used + + + Parallelization info + -------------------- + sticks: dense smooth PW G-vecs: dense smooth PW + Min 352 146 40 56132 15094 2202 + Max 353 147 41 56139 15101 2213 + Sum 11267 4699 1301 1796313 483165 70641 + + Using Slab Decomposition + + + + bravais-lattice index = 0 + lattice parameter (alat) = 13.5525 a.u. + unit-cell volume = 3576.5316 (a.u.)^3 + number of atoms/cell = 3 + number of atomic types = 1 + number of electrons = 540.00 + number of Kohn-Sham states= 324 + kinetic-energy cutoff = 100.0000 Ry + charge density cutoff = 960.0000 Ry + scf convergence threshold = 1.0E-09 + mixing beta = 0.7000 + number of iterations used = 8 plain mixing + Exchange-correlation= PBE + ( 1 4 3 4 0 0 0) + + celldm(1)= 13.552517 celldm(2)= 0.000000 celldm(3)= 0.000000 + celldm(4)= 0.000000 celldm(5)= 0.000000 celldm(6)= 0.000000 + + crystal axes: (cart. coord. in units of alat) + a(1) = ( 1.000000 0.000000 0.000000 ) + a(2) = ( -0.595478 0.803372 0.000000 ) + a(3) = ( -0.000000 0.000000 1.788486 ) + + reciprocal axes: (cart. coord. in units 2 pi/alat) + b(1) = ( 1.000000 0.741223 0.000000 ) + b(2) = ( 0.000000 1.244754 -0.000000 ) + b(3) = ( 0.000000 0.000000 0.559132 ) + + + PseudoPot. # 1 for Na read from file: + ./Na_ONCV_PBE-1.0.upf + MD5 check sum: d7d7bc8e018c79acd8ed72114b17dc6b + Pseudo is Norm-conserving, Zval = 9.0 + Generated using ONCVPSP code by D. R. Hamann + Using radial grid of 602 points, 4 beta functions with: + l(1) = 0 + l(2) = 0 + l(3) = 1 + l(4) = 1 + + atomic species valence mass pseudopotential + Na 9.00 22.98977 Na( 1.00) + + No symmetry found + + + + Cartesian axes + + site n. atom positions (alat units) + 1 Na tau( 1) = ( 0.7038041 0.3194494 0.1063655 ) + 2 Na tau( 2) = ( -0.2992812 0.4839227 1.0006089 ) + 3 Na tau( 3) = ( 0.5669855 0.0477308 0.1063655 ) + + number of k points= 132 Gaussian smearing, width (Ry)= 0.0200 + + Number of k-points >= 100: set verbosity='high' to print them. + + Dense grid: 1796313 G-vectors FFT dimensions: ( 135, 135, 240) + + Smooth grid: 483165 G-vectors FFT dimensions: ( 90, 90, 160) + + Estimated max dynamical RAM per process > 1.32 GB + + Estimated total dynamical RAM > 42.10 GB + + Initial potential from superposition of free atoms + + starting charge 531.7976, renormalised to 540.0000 + Starting wfcs are random + + total cpu time spent up to now is 114.9 secs + + Self-consistent Calculation + + iteration # 1 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 1.00E-02, avg # of iterations = 9.1 + + Threshold (ethr) on eigenvalues was too large: + Diagonalizing with lowered threshold + + Davidson diagonalization with overlap + ethr = 5.78E-04, avg # of iterations = 1.8 + + total cpu time spent up to now is 1816.0 secs + + total energy = -5091.43713858 Ry + estimated scf accuracy < 2.93415295 Ry + + iteration # 2 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 5.43E-04, avg # of iterations = 3.1 + + total cpu time spent up to now is 2304.9 secs + + total energy = -5091.87662866 Ry + estimated scf accuracy < 0.18067264 Ry + + iteration # 3 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 3.35E-05, avg # of iterations = 9.2 + + total cpu time spent up to now is 2929.4 secs + + total energy = -5091.86348124 Ry + estimated scf accuracy < 0.24741291 Ry + + iteration # 4 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 3.35E-05, avg # of iterations = 6.2 + + total cpu time spent up to now is 3422.9 secs + + total energy = -5091.91697077 Ry + estimated scf accuracy < 0.03279724 Ry + + iteration # 5 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 6.07E-06, avg # of iterations = 6.5 + + total cpu time spent up to now is 3919.2 secs + + total energy = -5091.92159622 Ry + estimated scf accuracy < 0.01385226 Ry + + iteration # 6 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 2.57E-06, avg # of iterations = 4.9 + + total cpu time spent up to now is 4362.4 secs + + total energy = -5091.92392162 Ry + estimated scf accuracy < 0.00067159 Ry + + iteration # 7 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + c_bands: 1 eigenvalues not converged + ethr = 1.24E-07, avg # of iterations = 2.4 + + total cpu time spent up to now is 4735.1 secs + + total energy = -5091.92383567 Ry + estimated scf accuracy < 0.00330792 Ry + + iteration # 8 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 1.24E-07, avg # of iterations = 2.0 + + total cpu time spent up to now is 5089.7 secs + + total energy = -5091.92408732 Ry + estimated scf accuracy < 0.00023165 Ry + + iteration # 9 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 4.29E-08, avg # of iterations = 2.0 + + total cpu time spent up to now is 5447.8 secs + + total energy = -5091.92409876 Ry + estimated scf accuracy < 0.00005097 Ry + + iteration # 10 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 9.44E-09, avg # of iterations = 2.0 + + total cpu time spent up to now is 5804.4 secs + + total energy = -5091.92409732 Ry + estimated scf accuracy < 0.00020843 Ry + + iteration # 11 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 9.44E-09, avg # of iterations = 1.8 + + total cpu time spent up to now is 6150.7 secs + + total energy = -5091.92411078 Ry + estimated scf accuracy < 0.00000218 Ry + + iteration # 12 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 4.03E-10, avg # of iterations = 2.0 + + total cpu time spent up to now is 6512.7 secs + + total energy = -5091.92411112 Ry + estimated scf accuracy < 0.00000056 Ry + + iteration # 13 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 1.04E-10, avg # of iterations = 2.0 + + total cpu time spent up to now is 6874.7 secs + + total energy = -5091.92411124 Ry + estimated scf accuracy < 0.00000010 Ry + + iteration # 14 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 1.90E-11, avg # of iterations = 2.0 + + total cpu time spent up to now is 7236.4 secs + + total energy = -5091.92411126 Ry + estimated scf accuracy < 0.00000002 Ry + + iteration # 15 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 3.88E-12, avg # of iterations = 2.0 + + total cpu time spent up to now is 7598.0 secs + + total energy = -5091.92411126 Ry + estimated scf accuracy < 3.1E-09 Ry + + iteration # 16 ecut= 100.00 Ry beta= 0.70 + Davidson diagonalization with overlap + ethr = 5.74E-13, avg # of iterations = 2.0 + + total cpu time spent up to now is 7958.9 secs + + End of self-consistent calculation + + Number of k-points >= 100: set verbosity='high' to print the bands. + + the Fermi energy is 17.2707 ev + +! total energy = -5091.92411126 Ry + estimated scf accuracy < 9.4E-10 Ry + smearing contrib. (-TS) = -0.02510189 Ry + internal energy E=F+TS = -5091.89900937 Ry + + The total energy is F=E-TS. E is the sum of the following terms: + one-electron contribution = -1954.47492763 Ry + hartree contribution = 1190.28239393 Ry + xc contribution = -772.17810332 Ry + ewald contribution = -3555.52837235 Ry + + convergence has been achieved in 16 iterations + + Forces acting on atoms (Ry/au): + + atom 1 type 1 force = -0.0 -0.0 0.0 + atom 2 type 1 force = -0.0 0.0 0.0 + atom 3 type 1 force = -0.0 0.0 -0.0 + + Writing all to output data dir ./tmp/Na.save/ + + init_run : 106.04s CPU 113.67s WALL ( 1 calls) + electrons : 7468.59s CPU 7844.89s WALL ( 1 calls) + + Called by init_run: + wfcinit : 103.82s CPU 111.27s WALL ( 1 calls) + potinit : 0.41s CPU 0.45s WALL ( 1 calls) + hinit0 : 0.77s CPU 0.83s WALL ( 1 calls) + + Called by electrons: + c_bands : 7022.30s CPU 7363.43s WALL ( 17 calls) + sum_band : 444.25s CPU 478.23s WALL ( 17 calls) + v_of_rho : 1.61s CPU 1.93s WALL ( 17 calls) + mix_rho : 0.31s CPU 0.40s WALL ( 17 calls) + + Called by c_bands: + init_us_2 : 22.26s CPU 23.03s WALL ( 4620 calls) + init_us_2:cp : 22.22s CPU 23.01s WALL ( 4620 calls) + cegterg : 6883.35s CPU 7219.17s WALL ( 2244 calls) + + Called by *egterg: + cdiaghg : 1863.67s CPU 1931.50s WALL ( 10175 calls) + h_psi : 2890.75s CPU 3087.99s WALL ( 10439 calls) + g_psi : 14.56s CPU 14.97s WALL ( 8063 calls) + + Called by h_psi: + h_psi:calbec : 479.18s CPU 494.17s WALL ( 10439 calls) + vloc_psi : 1926.22s CPU 2091.42s WALL ( 10439 calls) + add_vuspsi : 470.49s CPU 486.00s WALL ( 10439 calls) + + General routines + calbec : 479.12s CPU 494.13s WALL ( 10439 calls) + fft : 0.84s CPU 1.04s WALL ( 202 calls) + ffts : 0.03s CPU 0.04s WALL ( 34 calls) + fftw : 2059.48s CPU 2242.08s WALL ( 4338058 calls) + interpolate : 0.09s CPU 0.11s WALL ( 17 calls) + + Parallel routines + + PWSCF : 2h 6m CPU 2h19m WALL + + + This run was terminated on: 11:57:29 18Sep2024 + +=------------------------------------------------------------------------------= + JOB DONE. +=------------------------------------------------------------------------------= diff --git a/tests/test_qe_pw_scf.py b/tests/test_qe_pw_scf.py index 8703e7c2..a38e2d05 100644 --- a/tests/test_qe_pw_scf.py +++ b/tests/test_qe_pw_scf.py @@ -188,5 +188,48 @@ def setUp(self): ) +class TestNa(unittest.TestCase): + def test(self): + ss = dpdata.LabeledSystem("qe.scf/na.out", fmt="qe/pw/scf") + self.assertEqual(ss["atom_numbs"], [3]) + self.assertEqual(ss["atom_names"], ["Na"]) + self.assertEqual(ss.get_nframes(), 1) + np.testing.assert_array_equal(ss["atom_types"], [0, 0, 0]) + np.testing.assert_allclose( + ss["coords"][0], + np.array( + [ + 0.940587444301534, + 0.397635863676890, + 0.059472381901808, + 0.059413515648061, + 0.602364552456546, + 0.559472465518034, + 0.602364619812068, + 0.059413062489401, + 0.059472381901808, + ] + ).reshape(3, 3) + @ ss["cells"][0], + ) + np.testing.assert_allclose( + ss["cells"][0], + np.array( + [ + 7.171683039200000, + 0.000000000000000, + 0.000000000000000, + -4.270578118300000, + 5.761527588200000, + 0.000000000000000, + -0.000000045600000, + 0.000000023000000, + 12.826457854999999, + ] + ).reshape(3, 3), + ) + np.testing.assert_allclose(ss["forces"][0], np.zeros([3, 3])) + + if __name__ == "__main__": unittest.main() From 482775f19e11ae77e7ee9835ee23068da11c52cb Mon Sep 17 00:00:00 2001 From: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> Date: Fri, 20 Sep 2024 09:27:41 +0800 Subject: [PATCH 21/33] Fix: qe/pw/scf unit conversion is not consistent with dpdata (#725) ## Summary by CodeRabbit - **New Features** - Introduced a configuration file for sodium SCF calculations, enhancing quantum mechanical simulations. - Added a new test class to verify properties of sodium systems, improving test coverage. - **Bug Fixes** - Improved logic for block extraction and stress block handling to enhance robustness. - **Tests** - Updated energy validation method to use relative comparisons for more accurate results. --------- Signed-off-by: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> Co-authored-by: Han Wang Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dpdata/qe/scf.py | 10 +++++++--- tests/test_qe_pw_scf.py | 6 +++--- tests/test_qe_pw_scf_energy_bug.py | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/dpdata/qe/scf.py b/dpdata/qe/scf.py index 7b6a70aa..341261d2 100755 --- a/dpdata/qe/scf.py +++ b/dpdata/qe/scf.py @@ -7,9 +7,13 @@ from dpdata.utils import open_file -ry2ev = 13.605693009 -bohr2ang = 0.52917721067 -kbar2evperang3 = 1e3 / 1.602176621e6 +from .traj import ( + kbar2evperang3, + ry2ev, +) +from .traj import ( + length_convert as bohr2ang, +) _QE_BLOCK_KEYWORDS = [ "ATOMIC_SPECIES", diff --git a/tests/test_qe_pw_scf.py b/tests/test_qe_pw_scf.py index a38e2d05..7fe803b9 100644 --- a/tests/test_qe_pw_scf.py +++ b/tests/test_qe_pw_scf.py @@ -161,11 +161,11 @@ def test_virial(self): def test_energy(self): ref_energy = -219.74425946528794 - self.assertAlmostEqual(self.system_ch4.data["energies"][0], ref_energy) + self.assertAlmostEqual(self.system_ch4.data["energies"][0] / ref_energy, 1.0) ref_energy = -30007.651851226798 - self.assertAlmostEqual(self.system_h2o.data["energies"][0], ref_energy) + self.assertAlmostEqual(self.system_h2o.data["energies"][0] / ref_energy, 1.0) ref_energy = -219.7153691367526562 - self.assertAlmostEqual(self.system_ch4_2.data["energies"][0], ref_energy) + self.assertAlmostEqual(self.system_ch4_2.data["energies"][0] / ref_energy, 1.0) class TestPWSCFLabeledOutput(unittest.TestCase, TestPWSCFSinglePointEnergy): diff --git a/tests/test_qe_pw_scf_energy_bug.py b/tests/test_qe_pw_scf_energy_bug.py index b66ce924..a513dc7c 100644 --- a/tests/test_qe_pw_scf_energy_bug.py +++ b/tests/test_qe_pw_scf_energy_bug.py @@ -8,7 +8,7 @@ class TestPWSCFSinglePointEnergy: def test_energy(self): ref_energy = -296.08379065679094669 - self.assertAlmostEqual(self.system_al.data["energies"][0], ref_energy) + self.assertAlmostEqual(self.system_al.data["energies"][0] / ref_energy, 1.0) class TestPWSCFLabeledOutput(unittest.TestCase, TestPWSCFSinglePointEnergy): From c1d6c73726b7b3ffe42ba72add54aa03102abfa5 Mon Sep 17 00:00:00 2001 From: Peng Xingliang <91927439+pxlxingliang@users.noreply.github.com> Date: Fri, 20 Sep 2024 15:50:46 +0800 Subject: [PATCH 22/33] feat: support spin for ABACUS (#718) ## Summary by CodeRabbit - **New Features** - Enhanced data retrieval capabilities with the inclusion of magnetic moment and force data in the output. - Streamlined method for handling parameters in the ABACUS plugin, improving usability. - New function added for registering magnetic data types, enhancing functionality. - Comprehensive test suite introduced for validating ABACUS spin simulation functionalities. - **Bug Fixes** - Improved control flow to ensure accurate integration of new magnetic data. - **Tests** - Introduced new test cases to validate the accuracy of ABACUS spin simulation outputs, enhancing overall test coverage. --------- Co-authored-by: root Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> --- dpdata/abacus/md.py | 8 + dpdata/abacus/relax.py | 8 + dpdata/abacus/scf.py | 468 +++++++- dpdata/plugins/abacus.py | 55 +- tests/abacus.relax/INPUT.spin | 35 + tests/abacus.relax/STRU.spin | 21 + tests/abacus.scf/C.orb | 0 tests/abacus.scf/C.upf | 0 tests/abacus.scf/H.orb | 0 tests/abacus.scf/H.upf | 0 tests/abacus.scf/jle.orb | 0 tests/abacus.scf/stru.ref | 29 + tests/abacus.spin/INPUT | 27 + tests/abacus.spin/INPUT.md | 34 + tests/abacus.spin/INPUT.relax | 34 + tests/abacus.spin/INPUT.scf | 27 + tests/abacus.spin/OUT.ABACUS/MD_dump | 44 + tests/abacus.spin/OUT.ABACUS/running_md.log | 997 ++++++++++++++++ .../abacus.spin/OUT.ABACUS/running_relax.log | 1002 +++++++++++++++++ tests/abacus.spin/OUT.ABACUS/running_scf.log | 565 ++++++++++ tests/abacus.spin/STRU | 21 + tests/test_abacus_spin.py | 157 +++ tests/test_abacus_stru_dump.py | 161 ++- 23 files changed, 3647 insertions(+), 46 deletions(-) create mode 100644 tests/abacus.relax/INPUT.spin create mode 100644 tests/abacus.relax/STRU.spin create mode 100644 tests/abacus.scf/C.orb create mode 100644 tests/abacus.scf/C.upf create mode 100644 tests/abacus.scf/H.orb create mode 100644 tests/abacus.scf/H.upf create mode 100644 tests/abacus.scf/jle.orb create mode 100644 tests/abacus.scf/stru.ref create mode 100644 tests/abacus.spin/INPUT create mode 100644 tests/abacus.spin/INPUT.md create mode 100644 tests/abacus.spin/INPUT.relax create mode 100644 tests/abacus.spin/INPUT.scf create mode 100644 tests/abacus.spin/OUT.ABACUS/MD_dump create mode 100644 tests/abacus.spin/OUT.ABACUS/running_md.log create mode 100644 tests/abacus.spin/OUT.ABACUS/running_relax.log create mode 100644 tests/abacus.spin/OUT.ABACUS/running_scf.log create mode 100644 tests/abacus.spin/STRU create mode 100644 tests/test_abacus_spin.py diff --git a/dpdata/abacus/md.py b/dpdata/abacus/md.py index 9e0a416a..bbb02343 100644 --- a/dpdata/abacus/md.py +++ b/dpdata/abacus/md.py @@ -12,6 +12,7 @@ get_cell, get_coords, get_geometry_in, + get_mag_force, kbar2evperang3, ) @@ -199,6 +200,9 @@ def get_frame(fname): stress[iframe] *= np.linalg.det(cells[iframe, :, :].reshape([3, 3])) if np.sum(np.abs(stress[0])) < 1e-10: stress = None + + magmom, magforce = get_mag_force(outlines) + data = {} data["atom_names"] = atom_names data["atom_numbs"] = natoms @@ -213,5 +217,9 @@ def get_frame(fname): if not isinstance(data["virials"], np.ndarray): del data["virials"] data["orig"] = np.zeros(3) + if len(magmom) > 0: + data["spins"] = magmom + if len(magforce) > 0: + data["mag_forces"] = magforce return data diff --git a/dpdata/abacus/relax.py b/dpdata/abacus/relax.py index 7a4abf35..be80bc90 100644 --- a/dpdata/abacus/relax.py +++ b/dpdata/abacus/relax.py @@ -13,6 +13,7 @@ get_cell, get_coords, get_geometry_in, + get_mag_force, kbar2evperang3, ) @@ -198,6 +199,8 @@ def get_frame(fname): lines, atomnumber ) + magmom, magforce = get_mag_force(lines) + data = {} data["atom_names"] = atom_names data["atom_numbs"] = natoms @@ -211,4 +214,9 @@ def get_frame(fname): data["stress"] = stress data["orig"] = np.zeros(3) + if len(magmom) > 0: + data["spins"] = magmom + if len(magforce) > 0: + data["mag_forces"] = magforce + return data diff --git a/dpdata/abacus/scf.py b/dpdata/abacus/scf.py index 93e3d6e1..9919e912 100644 --- a/dpdata/abacus/scf.py +++ b/dpdata/abacus/scf.py @@ -112,6 +112,152 @@ def get_cell(geometry_inlines): return celldm, cell +def parse_stru_pos(pos_line): + """Parses a line from the atom position block in a structure file. + + The content in atom position block can include: + - `m` or NO key word: Three numbers (0 or 1) controlling atom movement in geometry relaxation calculations. + - `v`, `vel`, or `velocity`: Three components of initial velocity of atoms in geometry relaxation calculations. + - `mag` or `magmom`: Start magnetization for each atom. Can be one number (colinear) or three numbers (non-colinear). + - `angle1`: In non-colinear case, angle between c-axis and real spin (in degrees). + - `angle2`: In non-colinear case, angle between a-axis and real spin projection in ab-plane (in degrees). + - `cs` or `constrain`: Three numbers (0 or 1) controlling the spin constraint of the atom. + - `lambda`: Three numbers controlling the lambda of the atom. + + Parameters + ---------- + pos_line : A line from the atom position block. + + Returns + ------- + tuple: A tuple containing: + - pos (list of float): The position coordinates. + - move (list of int or None): Movement control values. + - velocity (list of float or None): Initial velocity components. + - magmom (float, list of float, or None): Magnetization values. + - angle1 (float or None): Angle1 value. + - angle2 (float or None): Angle2 value. + - constrain (list of bool or None): Spin constraint values. + - lambda1 (float, list of float, or None): Lambda values. + + e.g.: + ``` + Fe + 1.0 + 2 + 0.0 0.0 0.0 m 0 0 0 mag 1.0 angle1 90 angle2 0 cs 0 0 0 + 0.5 0.5 0.5 m 1 1 1 mag 1.0 angle1 90 angle2 180 + ``` + """ + pos_line = pos_line.split("#")[0] # remove comments + sline = pos_line.split() + pos = [float(i) for i in sline[:3]] + move = None + velocity = None + magmom = None + angle1 = None + angle2 = None + constrain = None + lambda1 = None + if len(sline) > 3: + mag_list = None + velocity_list = None + move_list = [] + angle1_list = None + angle2_list = None + constrain_list = None + lambda_list = None + label = "move" + for i in range(3, len(sline)): + # firstly read the label + if sline[i] == "m": + label = "move" + elif sline[i] in ["v", "vel", "velocity"]: + label = "velocity" + velocity_list = [] + elif sline[i] in ["mag", "magmom"]: + label = "magmom" + mag_list = [] + elif sline[i] == "angle1": + label = "angle1" + angle1_list = [] + elif sline[i] == "angle2": + label = "angle2" + angle2_list = [] + elif sline[i] in ["constrain", "sc"]: + label = "constrain" + constrain_list = [] + elif sline[i] in ["lambda"]: + label = "lambda" + lambda_list = [] + + # the read the value to the list + elif label == "move": + move_list.append(int(sline[i])) + elif label == "velocity": + velocity_list.append(float(sline[i])) + elif label == "magmom": + mag_list.append(float(sline[i])) + elif label == "angle1": + angle1_list.append(float(sline[i])) + elif label == "angle2": + angle2_list.append(float(sline[i])) + elif label == "constrain": + constrain_list.append(bool(int(sline[i]))) + elif label == "lambda": + lambda_list.append(float(sline[i])) + + if move_list is not None and len(move_list) > 0: + if len(move_list) == 3: + move = move_list + else: + raise RuntimeError(f"Invalid setting of move: {pos_line}") + + if velocity_list is not None: + if len(velocity_list) == 3: + velocity = velocity_list + else: + raise RuntimeError(f"Invalid setting of velocity: {pos_line}") + + if mag_list is not None: + if len(mag_list) == 3: + magmom = mag_list + elif len(mag_list) == 1: + magmom = mag_list[0] + else: + raise RuntimeError(f"Invalid magnetic moment {pos_line}") + + if angle1_list is not None: + if len(angle1_list) == 1: + angle1 = angle1_list[0] + else: + raise RuntimeError(f"Invalid angle1 {pos_line}") + + if angle2_list is not None: + if len(angle2_list) == 1: + angle2 = angle2_list[0] + else: + raise RuntimeError(f"Invalid angle2 {pos_line}") + + if constrain_list is not None: + if len(constrain_list) == 3: + constrain = constrain_list + elif len(constrain_list) == 1: + constrain = constrain_list[0] + else: + raise RuntimeError(f"Invalid constrain {pos_line}") + + if lambda_list is not None: + if len(lambda_list) == 3: + lambda1 = lambda_list + elif len(lambda_list) == 1: + lambda1 = lambda_list[0] + else: + raise RuntimeError(f"Invalid lambda {pos_line}") + + return pos, move, velocity, magmom, angle1, angle2, constrain, lambda1 + + def get_coords(celldm, cell, geometry_inlines, inlines=None): coords_lines = get_stru_block(geometry_inlines, "ATOMIC_POSITIONS") # assuming that ATOMIC_POSITIONS is at the bottom of the STRU file @@ -120,6 +266,14 @@ def get_coords(celldm, cell, geometry_inlines, inlines=None): atom_types = [] # index of atom_names of each atom in the geometry atom_numbs = [] # of atoms for each element coords = [] # coordinations of atoms + move = [] # move flag of each atom + velocity = [] # velocity of each atom + mag = [] # magnetic moment of each atom + angle1 = [] # angle1 of each atom + angle2 = [] # angle2 of each atom + sc = [] # spin constraint flag of each atom + lambda_ = [] # lambda of each atom + ntype = get_nele_from_stru(geometry_inlines) line_idx = 1 # starting line of first element for it in range(ntype): @@ -128,7 +282,10 @@ def get_coords(celldm, cell, geometry_inlines, inlines=None): atom_numbs.append(int(coords_lines[line_idx].split()[0])) line_idx += 1 for iline in range(atom_numbs[it]): - xyz = np.array([float(xx) for xx in coords_lines[line_idx].split()[0:3]]) + pos, imove, ivelocity, imagmom, iangle1, iangle2, iconstrain, ilambda1 = ( + parse_stru_pos(coords_lines[line_idx]) + ) + xyz = np.array(pos) if coord_type == "cartesian": xyz = xyz * celldm elif coord_type == "direct": @@ -141,6 +298,15 @@ def get_coords(celldm, cell, geometry_inlines, inlines=None): ) coords.append(xyz) atom_types.append(it) + + move.append(imove) + velocity.append(ivelocity) + mag.append(imagmom) + angle1.append(iangle1) + angle2.append(iangle2) + sc.append(iconstrain) + lambda_.append(ilambda1) + line_idx += 1 coords = np.array(coords) # need transformation!!! atom_types = np.array(atom_types) @@ -234,15 +400,57 @@ def get_stress(outlines): return np.array(stress[-1]) * kbar2evperang3 # only return the last stress +def get_mag_force(outlines): + """Read atomic magmom and magnetic force from OUT.ABACUS/running_scf.log. + + Returns + ------- + magmom: list of list of atomic magnetic moments (three dimensions: ION_STEP * NATOMS * 1/3) + magforce: list of list of atomic magnetic forces (three dimensions: ION_STEP * NATOMS * 1/3) + e.g.: + ------------------------------------------------------------------------------------------- + Total Magnetism (uB) + ------------------------------------------------------------------------------------------- + Fe 0.0000000001 0.0000000000 3.0000000307 + Fe -0.0000000000 -0.0000000000 3.0000001151 + ------------------------------------------------------------------------------------------- + ------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) + ------------------------------------------------------------------------------------------- + Fe 0.0000000000 0.0000000000 -1.2117698671 + Fe 0.0000000000 0.0000000000 -1.2117928796 + ------------------------------------------------------------------------------------------- + + """ + mags = [] + magforces = [] + for i, line in enumerate(outlines): + if "Total Magnetism (uB)" in line: + j = i + 2 + mag = [] + while "-------------------------" not in outlines[j]: + mag.append([float(ii) for ii in outlines[j].split()[1:]]) + j += 1 + mags.append(mag) + if "Magnetic force (eV/uB)" in line: + j = i + 2 + magforce = [] + while "-------------------------" not in outlines[j]: + magforce.append([float(ii) for ii in outlines[j].split()[1:]]) + j += 1 + magforces.append(magforce) + return np.array(mags), np.array(magforces) + + def get_frame(fname): data = { "atom_names": [], "atom_numbs": [], "atom_types": [], - "cells": [], - "coords": [], - "energies": [], - "forces": [], + "cells": np.array([]), + "coords": np.array([]), + "energies": np.array([]), + "forces": np.array([]), } if isinstance(fname, str): @@ -272,6 +480,12 @@ def get_frame(fname): atom_names, natoms, types, coords = get_coords( celldm, cell, geometry_inlines, inlines ) + magmom, magforce = get_mag_force(outlines) + if len(magmom) > 0: + magmom = magmom[-1:] + if len(magforce) > 0: + magforce = magforce[-1:] + data["atom_names"] = atom_names data["atom_numbs"] = natoms data["atom_types"] = types @@ -291,6 +505,11 @@ def get_frame(fname): if stress is not None: data["virials"] = stress[np.newaxis, :, :] data["orig"] = np.zeros(3) + + if len(magmom) > 0: + data["spins"] = magmom + if len(magforce) > 0: + data["mag_forces"] = magforce # print("atom_names = ", data['atom_names']) # print("natoms = ", data['atom_numbs']) # print("types = ", data['atom_types']) @@ -313,12 +532,9 @@ def get_nele_from_stru(geometry_inlines): ] keyword_sequence = [] keyword_line_index = [] - atom_names = [] - atom_numbs = [] for iline, line in enumerate(geometry_inlines): if line.split() == []: continue - have_key_word = False for keyword in key_words_list: if keyword in line and keyword == line.split()[0]: keyword_sequence.append(keyword) @@ -343,7 +559,7 @@ def get_frame_from_stru(fname): with open_file(fname) as fp: geometry_inlines = fp.read().split("\n") nele = get_nele_from_stru(geometry_inlines) - inlines = ["ntype %d" % nele] + inlines = [f"ntype {nele}"] celldm, cell = get_cell(geometry_inlines) atom_names, natoms, types, coords = get_coords( celldm, cell, geometry_inlines, inlines @@ -366,32 +582,180 @@ def make_unlabeled_stru( numerical_orbital=None, numerical_descriptor=None, mass=None, + move=None, + velocity=None, + mag=None, + angle1=None, + angle2=None, + sc=None, + lambda_=None, + link_file=False, + dest_dir=None, + **kwargs, ): + """Make an unlabeled STRU file from a dictionary. + + Parameters + ---------- + data : dict + System data + frame_idx : int + The index of the frame to dump + pp_file : list of string or dict, optional + List of pseudo potential files, or a dictionary of pseudo potential files for each atomnames + numerical_orbital : list of string or dict, optional + List of orbital files, or a dictionary of orbital files for each atomnames + numerical_descriptor : str, optional + numerical descriptor file + mass : list of float, optional + List of atomic masses + move : list of list of bool, optional + List of the move flag of each xyz direction of each atom + velocity : list of list of float, optional + List of the velocity of each xyz direction of each atom + mag : list of (list of float or float), optional + List of the magnetic moment of each atom, can be a list of three floats or one float + For noncollinear, three floats are the xyz component of the magnetic moment. + For collinear, one float is the norm of the magnetic moment. + angle1 : list of float, optional + List of the angle1 of each atom. For noncollinear calculation, it is the angle between the magnetic moment and the z-axis. + angle2 : list of float, optional + List of the angle2 of each atom. For noncollinear calculation, it is the angle between the projection of magnetic moment on xy plane and the x-axis. + sc : list of (bool or list of 3 bool), optional + List of the spin constraint flag of each atom. Each element can be a bool or a list of three bools or None. + lambda_ : list of (float or list of 3 float), optional + List of the lambda of each atom. Each element can be a float or a list of three floats. + link_file : bool, optional + Whether to link the pseudo potential files and orbital files in the STRU file. + If True, then only filename will be written in the STRU file, and make a soft link to the real file. + For velocity, mag, angle1, angle2, sc, and lambda_, if the value is None, then the corresponding information will not be written. + ABACUS support defining "mag" and "angle1"/"angle2" at the same time, and in this case, the "mag" only define the norm of the magnetic moment, and "angle1" and "angle2" define the direction of the magnetic moment. + If data has spins, then it will be written as mag to STRU file; while if mag is passed at the same time, then mag will be used. + """ + + def _link_file(dest_dir, src_file): + if not os.path.isfile(src_file): + print(f"ERROR: link_file: {src_file} is not a file.") + return False + src_file = os.path.abspath(src_file) + if not os.path.isdir(dest_dir): + os.makedirs(dest_dir) + dest_file = os.path.join(dest_dir, os.path.basename(src_file)) + if os.path.isfile(dest_file): + if os.path.samefile(src_file, dest_file): + return True + else: + os.remove(dest_file) + os.symlink(src_file, dest_file) + return True + + def ndarray2list(i): + if isinstance(i, np.ndarray): + return i.tolist() + else: + return i + + if link_file and dest_dir is None: + print( + "WARNING: make_unlabeled_stru: link_file is True, but dest_dir is None. Will write the filename to STRU but not making soft link." + ) + if dest_dir is not None and dest_dir.strip() == "": + dest_dir = "." + + if mag is None and data.get("spins") is not None and len(data["spins"]) > 0: + mag = data["spins"][frame_idx] + + atom_numbs = sum(data["atom_numbs"]) + for key in [move, velocity, mag, angle1, angle2, sc, lambda_]: + if key is not None: + if ( + not isinstance(ndarray2list(key), (list, tuple)) + and len(key) != atom_numbs + ): + key_name = [name for name, value in locals().items() if value is key][0] + print( + f"ERROR: make_unlabeled_stru: the length of '{key_name}' ({len(key)}) should be equal to the number of atom number ({atom_numbs})." + ) + return "" + + # ATOMIC_SPECIES block out = "ATOMIC_SPECIES\n" + if pp_file is not None: + pp_file = ndarray2list(pp_file) for iele in range(len(data["atom_names"])): + if data["atom_numbs"][iele] == 0: + continue out += data["atom_names"][iele] + " " if mass is not None: out += f"{mass[iele]:.3f} " else: out += "1 " if pp_file is not None: - out += f"{pp_file[iele]}\n" - else: - out += "\n" + if isinstance(pp_file, (list, tuple)): + ipp_file = pp_file[iele] + elif isinstance(pp_file, dict): + if data["atom_names"][iele] not in pp_file: + print( + f"ERROR: make_unlabeled_stru: pp_file does not contain {data['atom_names'][iele]}" + ) + ipp_file = None + else: + ipp_file = pp_file[data["atom_names"][iele]] + else: + ipp_file = None + if ipp_file is not None: + if not link_file: + out += ipp_file + else: + out += os.path.basename(ipp_file.rstrip("/")) + if dest_dir is not None: + _link_file(dest_dir, ipp_file) + + out += "\n" out += "\n" + # NUMERICAL_ORBITAL block if numerical_orbital is not None: assert len(numerical_orbital) == len(data["atom_names"]) + numerical_orbital = ndarray2list(numerical_orbital) out += "NUMERICAL_ORBITAL\n" - for iele in range(len(numerical_orbital)): - out += f"{numerical_orbital[iele]}\n" + for iele in range(len(data["atom_names"])): + if data["atom_numbs"][iele] == 0: + continue + if isinstance(numerical_orbital, (list, tuple)): + inum_orbital = numerical_orbital[iele] + elif isinstance(numerical_orbital, dict): + if data["atom_names"][iele] not in numerical_orbital: + print( + f"ERROR: make_unlabeled_stru: numerical_orbital does not contain {data['atom_names'][iele]}" + ) + inum_orbital = None + else: + inum_orbital = numerical_orbital[data["atom_names"][iele]] + else: + inum_orbital = None + if inum_orbital is not None: + if not link_file: + out += inum_orbital + else: + out += os.path.basename(inum_orbital.rstrip("/")) + if dest_dir is not None: + _link_file(dest_dir, inum_orbital) + out += "\n" out += "\n" + # deepks block if numerical_descriptor is not None: assert isinstance(numerical_descriptor, str) - out += f"NUMERICAL_DESCRIPTOR\n{numerical_descriptor}\n" + if not link_file: + out += f"NUMERICAL_DESCRIPTOR\n{numerical_descriptor}\n" + else: + out += f"NUMERICAL_DESCRIPTOR\n{os.path.basename(numerical_descriptor)}\n" + if dest_dir is not None: + _link_file(dest_dir, numerical_descriptor) out += "\n" + # LATTICE_CONSTANT and LATTICE_VECTORS block out += "LATTICE_CONSTANT\n" out += str(1 / bohr2ang) + "\n\n" @@ -402,24 +766,80 @@ def make_unlabeled_stru( out += "\n" out += "\n" + # ATOMIC_POSITIONS block out += "ATOMIC_POSITIONS\n" out += "Cartesian # Cartesian(Unit is LATTICE_CONSTANT)\n" # ret += "\n" - natom_tot = 0 + natom_tot = 0 # in for loop, it is also the atom index for iele in range(len(data["atom_names"])): + if data["atom_numbs"][iele] == 0: + continue out += data["atom_names"][iele] + "\n" out += "0.0\n" out += str(data["atom_numbs"][iele]) + "\n" for iatom in range(data["atom_numbs"][iele]): iatomtype = np.nonzero(data["atom_types"] == iele)[0][iatom] - out += "%.12f %.12f %.12f %d %d %d\n" % ( - data["coords"][frame_idx][iatomtype, 0], - data["coords"][frame_idx][iatomtype, 1], - data["coords"][frame_idx][iatomtype, 2], - 1, - 1, - 1, - ) + iout = f"{data['coords'][frame_idx][iatomtype, 0]:.12f} {data['coords'][frame_idx][iatomtype, 1]:.12f} {data['coords'][frame_idx][iatomtype, 2]:.12f}" + # add flags for move, velocity, mag, angle1, angle2, and sc + if move is not None: + if ( + isinstance(ndarray2list(move[natom_tot]), (list, tuple)) + and len(move[natom_tot]) == 3 + ): + iout += " " + " ".join( + ["1" if ii else "0" for ii in move[natom_tot]] + ) + elif isinstance(ndarray2list(move[natom_tot]), (int, float, bool)): + iout += " 1 1 1" if move[natom_tot] else " 0 0 0" + else: + iout += " 1 1 1" + + if ( + velocity is not None + and isinstance(ndarray2list(velocity[natom_tot]), (list, tuple)) + and len(velocity[natom_tot]) == 3 + ): + iout += " v " + " ".join([f"{ii:.12f}" for ii in velocity[natom_tot]]) + + if mag is not None: + if isinstance(ndarray2list(mag[natom_tot]), (list, tuple)) and len( + mag[natom_tot] + ) in [1, 3]: + iout += " mag " + " ".join([f"{ii:.12f}" for ii in mag[natom_tot]]) + elif isinstance(ndarray2list(mag[natom_tot]), (int, float)): + iout += " mag " + f"{mag[natom_tot]:.12f}" + + if angle1 is not None and isinstance( + ndarray2list(angle1[natom_tot]), (int, float) + ): + iout += " angle1 " + f"{angle1[natom_tot]:.12f}" + + if angle2 is not None and isinstance( + ndarray2list(angle2[natom_tot]), (int, float) + ): + iout += " angle2 " + f"{angle2[natom_tot]:.12f}" + + if sc is not None: + if isinstance(ndarray2list(sc[natom_tot]), (list, tuple)) and len( + sc[natom_tot] + ) in [1, 3]: + iout += " sc " + " ".join( + ["1" if ii else "0" for ii in sc[natom_tot]] + ) + elif isinstance(ndarray2list(sc[natom_tot]), (int, float, bool)): + iout += " sc " + "1" if sc[natom_tot] else "0" + + if lambda_ is not None: + if isinstance(ndarray2list(lambda_[natom_tot]), (list, tuple)) and len( + lambda_[natom_tot] + ) in [1, 3]: + iout += " lambda " + " ".join( + [f"{ii:.12f}" for ii in lambda_[natom_tot]] + ) + elif isinstance(ndarray2list(lambda_[natom_tot]), (int, float)): + iout += " lambda " + f"{lambda_[natom_tot]:.12f}" + + out += iout + "\n" natom_tot += 1 assert natom_tot == sum(data["atom_numbs"]) return out diff --git a/dpdata/plugins/abacus.py b/dpdata/plugins/abacus.py index e3367b35..cbd1475f 100644 --- a/dpdata/plugins/abacus.py +++ b/dpdata/plugins/abacus.py @@ -1,10 +1,14 @@ from __future__ import annotations +import os from typing import TYPE_CHECKING +import numpy as np + import dpdata.abacus.md import dpdata.abacus.relax import dpdata.abacus.scf +from dpdata.data_type import Axis, DataType from dpdata.format import Format from dpdata.utils import open_file @@ -29,40 +33,49 @@ def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs): The output file name frame_idx : int The index of the frame to dump - pp_file : list of string, optional - List of pseudo potential files - numerical_orbital : list of string, optional - List of orbital files - mass : list of float, optional - List of atomic masses - numerical_descriptor : str, optional - numerical descriptor file **kwargs : dict other parameters """ - pp_file = kwargs.get("pp_file") - numerical_orbital = kwargs.get("numerical_orbital") - mass = kwargs.get("mass") - numerical_descriptor = kwargs.get("numerical_descriptor") stru_string = dpdata.abacus.scf.make_unlabeled_stru( data=data, frame_idx=frame_idx, - pp_file=pp_file, - numerical_orbital=numerical_orbital, - numerical_descriptor=numerical_descriptor, - mass=mass, + dest_dir=os.path.dirname(file_name), + **kwargs, ) with open_file(file_name, "w") as fp: fp.write(stru_string) +def register_mag_data(data): + if "spins" in data: + dt = DataType( + "spins", + np.ndarray, + (Axis.NFRAMES, Axis.NATOMS, 3), + required=False, + deepmd_name="spin", + ) + dpdata.LabeledSystem.register_data_type(dt) + if "mag_forces" in data: + dt = DataType( + "mag_forces", + np.ndarray, + (Axis.NFRAMES, Axis.NATOMS, 3), + required=False, + deepmd_name="force_mag", + ) + dpdata.LabeledSystem.register_data_type(dt) + + @Format.register("abacus/scf") @Format.register("abacus/pw/scf") @Format.register("abacus/lcao/scf") class AbacusSCFFormat(Format): # @Format.post("rot_lower_triangular") def from_labeled_system(self, file_name, **kwargs): - return dpdata.abacus.scf.get_frame(file_name) + data = dpdata.abacus.scf.get_frame(file_name) + register_mag_data(data) + return data @Format.register("abacus/md") @@ -71,7 +84,9 @@ def from_labeled_system(self, file_name, **kwargs): class AbacusMDFormat(Format): # @Format.post("rot_lower_triangular") def from_labeled_system(self, file_name, **kwargs): - return dpdata.abacus.md.get_frame(file_name) + data = dpdata.abacus.md.get_frame(file_name) + register_mag_data(data) + return data @Format.register("abacus/relax") @@ -80,4 +95,6 @@ def from_labeled_system(self, file_name, **kwargs): class AbacusRelaxFormat(Format): # @Format.post("rot_lower_triangular") def from_labeled_system(self, file_name, **kwargs): - return dpdata.abacus.relax.get_frame(file_name) + data = dpdata.abacus.relax.get_frame(file_name) + register_mag_data(data) + return data diff --git a/tests/abacus.relax/INPUT.spin b/tests/abacus.relax/INPUT.spin new file mode 100644 index 00000000..cbdc3fdd --- /dev/null +++ b/tests/abacus.relax/INPUT.spin @@ -0,0 +1,35 @@ +INPUT_PARAMETERS +suffix spin +calculation relax +ecutwfc 100 +scf_thr 1.0e-3 +scf_nmax 200 +smearing_method gauss +smearing_sigma 0.01 +#ks_solver genelpa +basis_type pw +symmetry 0 +noncolin 1 +nspin 4 + +onsite_radius 3 + +relax_nmax 3 + +force_thr 0.00001 + +sc_mag_switch 1 +decay_grad_switch 1 +sc_thr 1e-4 +nsc 100 +nsc_min 2 +alpha_trial 0.01 +sccut 3 + + + +#md_nstep 3 +#md_type nve +#md_dt 1 +#md_tfirst 300 + diff --git a/tests/abacus.relax/STRU.spin b/tests/abacus.relax/STRU.spin new file mode 100644 index 00000000..441b30d1 --- /dev/null +++ b/tests/abacus.relax/STRU.spin @@ -0,0 +1,21 @@ +ATOMIC_SPECIES +Fe 55.845 Fe.upf + +NUMERICAL_ORBITAL +Fe_gga_8au_100Ry_4s2p2d1f.orb + +LATTICE_CONSTANT +1.880277359 +LATTICE_VECTORS + 2.8274254848 0.0000000000 0.0000000000 #latvec1 + 0.0000000000 2.8274254848 0.0000000000 #latvec2 + 0.0000000000 0.0000000000 2.8274254848 #latvec3 + +ATOMIC_POSITIONS +Direct + +Fe #label +1 #magnetism +2 #number of atoms + 0.0000000000 0.0000000000 0.0000000000 mag 1.25 1.25 1.25 sc 1 1 1 + 0.500000000 0.500000000 0.500000000 mag 1.25 1.25 1.25 sc 1 1 1 diff --git a/tests/abacus.scf/C.orb b/tests/abacus.scf/C.orb new file mode 100644 index 00000000..e69de29b diff --git a/tests/abacus.scf/C.upf b/tests/abacus.scf/C.upf new file mode 100644 index 00000000..e69de29b diff --git a/tests/abacus.scf/H.orb b/tests/abacus.scf/H.orb new file mode 100644 index 00000000..e69de29b diff --git a/tests/abacus.scf/H.upf b/tests/abacus.scf/H.upf new file mode 100644 index 00000000..e69de29b diff --git a/tests/abacus.scf/jle.orb b/tests/abacus.scf/jle.orb new file mode 100644 index 00000000..e69de29b diff --git a/tests/abacus.scf/stru.ref b/tests/abacus.scf/stru.ref new file mode 100644 index 00000000..8485de36 --- /dev/null +++ b/tests/abacus.scf/stru.ref @@ -0,0 +1,29 @@ +ATOMIC_SPECIES +C 12.000 C.upf +H 1.000 H.upf + +NUMERICAL_ORBITAL +C.orb +H.orb + +LATTICE_CONSTANT +1.8897261246257702 + +LATTICE_VECTORS +5.291772109029999 0.0 0.0 +0.0 5.291772109029999 0.0 +0.0 0.0 5.291772109029999 + +ATOMIC_POSITIONS +Cartesian # Cartesian(Unit is LATTICE_CONSTANT) +C +0.0 +1 +5.192682633809 4.557725978258 4.436846615358 1 1 1 mag 4.000000000000 angle2 100.000000000000 sc 1 lambda 0.100000000000 0.200000000000 0.300000000000 +H +0.0 +4 +5.416431453540 4.011298860305 3.511161492417 1 1 1 mag 1.000000000000 1.000000000000 1.000000000000 angle2 90.000000000000 sc 1 lambda 0.400000000000 0.500000000000 0.600000000000 +4.131588222365 4.706745191323 4.431136645083 1 1 1 mag 1.000000000000 angle1 100.000000000000 angle2 80.000000000000 sc 1 0 1 lambda 0.700000000000 0.800000000000 0.900000000000 +5.630930319126 5.521640894956 4.450356541303 0 0 0 mag 1.000000000000 angle1 90.000000000000 angle2 70.0000000000000 +5.499851012568 4.003388899277 5.342621842622 0 0 0 mag 1.000000000000 angle1 80.000000000000 sc 1 diff --git a/tests/abacus.spin/INPUT b/tests/abacus.spin/INPUT new file mode 100644 index 00000000..bc0b1df0 --- /dev/null +++ b/tests/abacus.spin/INPUT @@ -0,0 +1,27 @@ +INPUT_PARAMETERS +suffix ABACUS +calculation scf +ecutwfc 100 +scf_thr 1e-07 +scf_nmax 100 +out_chg 0 +smearing_method gauss +smearing_sigma 0.01 +mixing_type broyden +mixing_ndim 10 +ks_solver genelpa +basis_type lcao +symmetry 0 +noncolin 1 +lspinorb 0 +nspin 4 +out_mul true +sc_mag_switch 1 +decay_grad_switch 1 +sc_thr 1e-07 +sc_scf_thr 1e-09 +nsc 100 +nsc_min 2 +alpha_trial 0.01 +sccut 3 +cal_force 1 diff --git a/tests/abacus.spin/INPUT.md b/tests/abacus.spin/INPUT.md new file mode 100644 index 00000000..2be67861 --- /dev/null +++ b/tests/abacus.spin/INPUT.md @@ -0,0 +1,34 @@ +INPUT_PARAMETERS +calculation md +ecutwfc 100 +scf_thr 1.0e-3 +scf_nmax 200 +smearing_method gauss +smearing_sigma 0.01 +#ks_solver genelpa +basis_type pw +symmetry 0 +noncolin 1 +nspin 4 + +onsite_radius 3 + +relax_nmax 3 + +force_thr 0.00001 + +sc_mag_switch 1 +decay_grad_switch 1 +sc_thr 1e-4 +nsc 100 +nsc_min 2 +alpha_trial 0.01 +sccut 3 + + +md_type nvt +md_nstep 3 +md_dt 1 +md_tfirst 300 +md_tfreq 1 + diff --git a/tests/abacus.spin/INPUT.relax b/tests/abacus.spin/INPUT.relax new file mode 100644 index 00000000..9a9466d3 --- /dev/null +++ b/tests/abacus.spin/INPUT.relax @@ -0,0 +1,34 @@ +INPUT_PARAMETERS +calculation relax +ecutwfc 100 +scf_thr 1.0e-3 +scf_nmax 200 +smearing_method gauss +smearing_sigma 0.01 +#ks_solver genelpa +basis_type pw +symmetry 0 +noncolin 1 +nspin 4 + +onsite_radius 3 + +relax_nmax 3 + +force_thr 0.00001 + +sc_mag_switch 1 +decay_grad_switch 1 +sc_thr 1e-4 +nsc 100 +nsc_min 2 +alpha_trial 0.01 +sccut 3 + + + +#md_nstep 3 +#md_type nve +#md_dt 1 +#md_tfirst 300 + diff --git a/tests/abacus.spin/INPUT.scf b/tests/abacus.spin/INPUT.scf new file mode 100644 index 00000000..bc0b1df0 --- /dev/null +++ b/tests/abacus.spin/INPUT.scf @@ -0,0 +1,27 @@ +INPUT_PARAMETERS +suffix ABACUS +calculation scf +ecutwfc 100 +scf_thr 1e-07 +scf_nmax 100 +out_chg 0 +smearing_method gauss +smearing_sigma 0.01 +mixing_type broyden +mixing_ndim 10 +ks_solver genelpa +basis_type lcao +symmetry 0 +noncolin 1 +lspinorb 0 +nspin 4 +out_mul true +sc_mag_switch 1 +decay_grad_switch 1 +sc_thr 1e-07 +sc_scf_thr 1e-09 +nsc 100 +nsc_min 2 +alpha_trial 0.01 +sccut 3 +cal_force 1 diff --git a/tests/abacus.spin/OUT.ABACUS/MD_dump b/tests/abacus.spin/OUT.ABACUS/MD_dump new file mode 100644 index 00000000..a4d41a9b --- /dev/null +++ b/tests/abacus.spin/OUT.ABACUS/MD_dump @@ -0,0 +1,44 @@ +MDSTEP: 0 +LATTICE_CONSTANT: 0.994999532004 Angstrom +LATTICE_VECTORS + 2.827425484800 0.000000000000 0.000000000000 + 0.000000000000 2.827425484800 0.000000000000 + 0.000000000000 0.000000000000 2.827425484800 +INDEX LABEL POSITION (Angstrom) FORCE (eV/Angstrom) VELOCITY (Angstrom/fs) + 0 Fe 0.000000000000 0.000000000000 0.000000000000 -0.000023448027 0.000084873040 0.000395963967 0.001501148908 0.001168257569 -0.001755430621 + 1 Fe 1.406643426095 1.406643426095 1.406643426095 0.000023448027 -0.000084873040 -0.000395963967 -0.001501148908 -0.001168257569 0.001755430621 + + +MDSTEP: 1 +LATTICE_CONSTANT: 0.994999532004 Angstrom +LATTICE_VECTORS + 2.827425484800 0.000000000000 0.000000000000 + 0.000000000000 2.827425484800 0.000000000000 + 0.000000000000 0.000000000000 2.827425484800 +INDEX LABEL POSITION (Angstrom) FORCE (eV/Angstrom) VELOCITY (Angstrom/fs) + 0 Fe 0.001501146882 0.001168264901 2.811531455776 -0.082174998068 -0.064183237637 0.094555758230 0.001495732780 0.001164031434 -0.001749198271 + 1 Fe 1.405142279213 1.405475161194 1.408398822510 0.082174998068 0.064183237637 -0.094555758230 -0.001495732780 -0.001164031434 0.001749198271 + + +MDSTEP: 2 +LATTICE_CONSTANT: 0.994999532004 Angstrom +LATTICE_VECTORS + 2.827425484800 0.000000000000 0.000000000000 + 0.000000000000 2.827425484800 0.000000000000 + 0.000000000000 0.000000000000 2.827425484800 +INDEX LABEL POSITION (Angstrom) FORCE (eV/Angstrom) VELOCITY (Angstrom/fs) + 0 Fe 0.002994040541 0.002330066808 2.809785444314 -0.026325867140 -0.019829200318 0.032814119442 0.001497639352 0.001165552232 -0.001751386047 + 1 Fe 1.403649385554 1.404313359287 1.410144833972 0.026325867140 0.019829200318 -0.032814119442 -0.001497639352 -0.001165552232 0.001751386047 + + +MDSTEP: 3 +LATTICE_CONSTANT: 0.994999532004 Angstrom +LATTICE_VECTORS + 2.827425484800 0.000000000000 0.000000000000 + 0.000000000000 2.827425484800 0.000000000000 + 0.000000000000 0.000000000000 2.827425484800 +INDEX LABEL POSITION (Angstrom) FORCE (eV/Angstrom) VELOCITY (Angstrom/fs) + 0 Fe 0.004498128094 0.003500694358 2.808026692717 -0.044129139592 -0.028138926921 0.059726500602 0.001507997443 0.001174209539 -0.001762617927 + 1 Fe 1.402145298001 1.403142731738 1.411903585568 0.044129139592 0.028138926921 -0.059726500602 -0.001507997443 -0.001174209539 0.001762617927 + + diff --git a/tests/abacus.spin/OUT.ABACUS/running_md.log b/tests/abacus.spin/OUT.ABACUS/running_md.log new file mode 100644 index 00000000..272a44ce --- /dev/null +++ b/tests/abacus.spin/OUT.ABACUS/running_md.log @@ -0,0 +1,997 @@ + + ABACUS v3.7.1 + + Atomic-orbital Based Ab-initio Computation at UStc + + Website: http://abacus.ustc.edu.cn/ + Documentation: https://abacus.deepmodeling.com/ + Repository: https://github.com/abacusmodeling/abacus-develop + https://github.com/deepmodeling/abacus-develop + Commit: 8443c42b5 (Thu Sep 5 14:19:05 2024 +0800) + + Start Time is Fri Sep 6 10:04:51 2024 + + ------------------------------------------------------------------------------------ + + READING GENERAL INFORMATION + global_out_dir = OUT.ABACUS/ + global_in_card = INPUT + pseudo_dir = ./ + orbital_dir = ./ + DRANK = 1 + DSIZE = 1 + DCOLOR = 1 + GRANK = 1 + GSIZE = 1 + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Reading atom information in unitcell: | + | From the input file and the structure file we know the number of | + | different elments in this unitcell, then we list the detail | + | information for each element, especially the zeta and polar atomic | + | orbital number for each element. The total atom number is counted. | + | We calculate the nearest atom distance for each atom and show the | + | Cartesian and Direct coordinates for each atom. We list the file | + | address for atomic orbitals. The volume and the lattice vectors | + | in real and reciprocal space is also shown. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + READING UNITCELL INFORMATION + ntype = 1 + lattice constant (Bohr) = 1.88028 + lattice constant (Angstrom) = 0.995 + + READING ATOM TYPE 1 + atom label = Fe + L=0, number of zeta = 4 + L=1, number of zeta = 2 + L=2, number of zeta = 2 + L=3, number of zeta = 1 + number of atom for this type = 2 + magnetization of element 1 = [ 1.25, 1.25, 1.25 ] + + TOTAL ATOM NUMBER = 2 +DIRECT COORDINATES + atom x y z mag vx vy vz +taud_Fe1 0.0000000000 0.0000000000 0.0000000000 2.1651 0.0000000000 0.0000000000 0.0000000000 +taud_Fe2 0.5000000000 0.5000000000 0.5000000000 2.1651 0.0000000000 0.0000000000 0.0000000000 + + + + Volume (Bohr^3) = 150.259 + Volume (A^3) = 22.266 + + Lattice vectors: (Cartesian coordinate: in unit of a_0) + +2.82743 +0 +0 + +0 +2.82743 +0 + +0 +0 +2.82743 + Reciprocal vectors: (Cartesian coordinate: in unit of 2 pi/a_0) + +0.353679 -0 +0 + -0 +0.353679 -0 + +0 -0 +0.353679 + The esolver type has been set to : ksdft_pw + + RUNNING WITH DEVICE : CPU / Intel(R) Xeon(R) Platinum + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Reading pseudopotentials files: | + | The pseudopotential file is in UPF format. The 'NC' indicates that | + | the type of pseudopotential is 'norm conserving'. Functional of | + | exchange and correlation is decided by 4 given parameters in UPF | + | file. We also read in the 'core correction' if there exists. | + | Also we can read the valence electrons number and the maximal | + | angular momentum used in this pseudopotential. We also read in the | + | trail wave function, trail atomic density and local-pseudopotential| + | on logrithmic grid. The non-local pseudopotential projector is also| + | read in if there is any. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + PAO radial cut off (Bohr) = 15 + + Read in pseudopotential file is Fe.upf + pseudopotential type = NC + exchange-correlation functional = PBE + nonlocal core correction = 1 + valence electrons = 16 + lmax = 2 + number of zeta = 4 + number of projectors = 6 + L of projector = 0 + L of projector = 0 + L of projector = 1 + L of projector = 1 + L of projector = 2 + L of projector = 2 + initial pseudo atomic orbital number = 40 + NLOCAL = 108 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Warning: the number of valence electrons in pseudopotential > 8 for Fe: [Ar] 3d6 4s2 + Pseudopotentials with additional electrons can yield (more) accurate outcomes, but may be less efficient. + If you're confident that your chosen pseudopotential is appropriate, you can safely ignore this warning. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup plane waves of charge/potential: | + | Use the energy cutoff and the lattice vectors to generate the | + | dimensions of FFT grid. The number of FFT grid on each processor | + | is 'nrxx'. The number of plane wave basis in reciprocal space is | + | different for charege/potential and wave functions. We also set | + | the 'sticks' for the parallel of FFT. The number of plane waves | + | is 'npw' in each processor. | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP THE PLANE WAVE BASIS + energy cutoff for charge/potential (unit:Ry) = 400 + fft grid for charge/potential = [ 36, 36, 36 ] + fft grid division = [ 1, 1, 1 ] + big fft grid for charge/potential = [ 36, 36, 36 ] + nbxx = 46656 + nrxx = 46656 + + SETUP PLANE WAVES FOR CHARGE/POTENTIAL + number of plane waves = 20341 + number of sticks = 885 + + PARALLEL PW FOR CHARGE/POTENTIAL + PROC COLUMNS(POT) PW + 1 885 20341 + --------------- sum ------------------- + 1 885 20341 + number of |g| = 241 + max |g| = 35.7753 + min |g| = 0 + +----------- Double Check Mixing Parameters Begin ------------ +mixing_type: broyden +mixing_beta: 0.4 +mixing_gg0: 1 +mixing_gg0_min: 0.1 +mixing_beta_mag: 1.6 +mixing_gg0_mag: 0 +mixing_ndim: 8 +----------- Double Check Mixing Parameters End ------------ + + SETUP THE ELECTRONS NUMBER + electron number of element Fe = 16 + total electron number of element Fe = 32 + AUTOSET number of electrons: = 32 + DONE : SETUP UNITCELL Time : 0.102785 (SEC) + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup K-points | + | We setup the k-points according to input parameters. | + | The reduced k-points are set according to symmetry operations. | + | We treat the spin as another set of k-points. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP K-POINTS + nspin = 4 + Input type of k points = Monkhorst-Pack(Gamma) + nkstot = 1 + nkstot_ibz = 1 +K-POINTS REDUCTION ACCORDING TO SYMMETRY + IBZ DIRECT_X DIRECT_Y DIRECT_Z WEIGHT ibz2bz + 1 0.00000000 0.00000000 0.00000000 1.0000 0 + + nkstot now = 1 +K-POINTS DIRECT COORDINATES + KPOINTS DIRECT_X DIRECT_Y DIRECT_Z WEIGHT + 1 0.00000000 0.00000000 0.00000000 1.0000 + + + k-point number in this process = 1 + minimum distributed K point number = 1 + +K-POINTS CARTESIAN COORDINATES + KPOINTS CARTESIAN_X CARTESIAN_Y CARTESIAN_Z WEIGHT + 1 0.00000000 0.00000000 0.00000000 1.0000 + + +K-POINTS DIRECT COORDINATES + KPOINTS DIRECT_X DIRECT_Y DIRECT_Z WEIGHT + 1 0.00000000 0.00000000 0.00000000 1.0000 + + DONE : INIT K-POINTS Time : 0.112353 (SEC) + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup plane waves of wave functions: | + | Use the energy cutoff and the lattice vectors to generate the | + | dimensions of FFT grid. The number of FFT grid on each processor | + | is 'nrxx'. The number of plane wave basis in reciprocal space is | + | different for charege/potential and wave functions. We also set | + | the 'sticks' for the parallel of FFT. The number of plane wave of | + | each k-point is 'npwk[ik]' in each processor | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP PLANE WAVES FOR WAVE FUNCTIONS + energy cutoff for wavefunc (unit:Ry) = 100 + fft grid for wave functions = [ 36, 36, 36 ] + number of plane waves = 2517 + number of sticks = 221 + + PARALLEL PW FOR WAVE FUNCTIONS + PROC COLUMNS(POT) PW + 1 221 2517 + --------------- sum ------------------- + 1 221 2517 + DONE : INIT PLANEWAVE Time : 0.113487 (SEC) + + occupied bands = 16 + NBANDS = 52 + + SETUP NONLOCAL PSEUDOPOTENTIALS IN PLANE WAVE BASIS + Fe non-local projectors: + projector 1 L=0 + projector 2 L=0 + projector 3 L=1 + projector 4 L=1 + projector 5 L=2 + projector 6 L=2 + TOTAL NUMBER OF NONLOCAL PROJECTORS = 36 + DONE : LOCAL POTENTIAL Time : 0.128854 (SEC) + + + Init Non-Local PseudoPotential table : + Init Non-Local-Pseudopotential done. + DONE : NON-LOCAL POTENTIAL Time : 0.15862 (SEC) + + npwx = 2517 + + Make real space PAO into reciprocal space. + max mesh points in Pseudopotential = 1425 + dq(describe PAO in reciprocal space) = 0.01 + max q = 1204 + + number of pseudo atomic orbitals for Fe is 4 +WARNING: norm of atomic wavefunction # 2 of atomic type Fe is 0.999981, renormalized +WARNING: norm of atomic wavefunction # 3 of atomic type Fe is 0.999997, renormalized + DONE : INIT BASIS Time : 0.330341 (SEC) + + + ------------------------------------------- + STEP OF MOLECULAR DYNAMICS : 0 + ------------------------------------------- + init_chg = atomic + DONE : INIT SCF Time : 0.528545 (SEC) + + + PW ALGORITHM --------------- ION= 1 ELEC= 1-------------------------------- +total magnetism (Bohr mag/cell) 2.30943 2.30938 2.30941 + absolute magnetism (Bohr mag/cell) = 4.01976 + Notice: Threshold on eigenvalues was too large. + hsover_error=0.32 > DRHO=0.0686869 + Origin diag_ethr = 0.01 + New diag_ethr = 0.000214646 +total magnetism (Bohr mag/cell) 2.30943 2.30937 2.3094 + absolute magnetism (Bohr mag/cell) = 4.01964 + + Density error is 0.0548949686243 + Error Threshold = 0.000214646406708 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6632803550 -6825.4790901992 + E_Harris -501.6647758269 -6825.4994371390 + E_Fermi 1.4575316668 19.8307356840 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 2-------------------------------- +total magnetism (Bohr mag/cell) 2.3094424965 2.30934448798 2.30941621651 + absolute magnetism (Bohr mag/cell) = 4.13707180938 + + Density error is 0.00986449919003 + Error Threshold = 0.000171546776951 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6774595966 -6825.6720086784 + E_Harris -499.7039503033 -6798.8210372331 + E_Fermi 1.4964230898 20.3598806394 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 3-------------------------------- +total magnetism (Bohr mag/cell) 2.30944490813 2.30935345105 2.30940487564 + absolute magnetism (Bohr mag/cell) = 4.08853707061 + + Density error is 0.00240445773415 + Error Threshold = 3.08265599688e-05 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6784060189 -6825.6848854146 + E_Harris -501.4802459250 -6822.9887790210 + E_Fermi 1.4994551309 20.4011336751 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 4-------------------------------- +total magnetism (Bohr mag/cell) 2.30944724752 2.30935862214 2.30939736766 + absolute magnetism (Bohr mag/cell) = 4.08848804651 + + Density error is 0.00057214611471 + Error Threshold = 7.51393041921e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6784787714 -6825.6858752634 + E_KS(sigma->0) -501.6746222162 -6825.6334041381 + E_Harris -501.6738375513 -6825.6227282240 + E_band -27.9117558414 -379.7589206280 + E_one_elec -203.4207485774 -2767.6812720784 + E_Hartree 123.6718684724 1682.6420935310 + E_xc -71.4390193374 -971.9777225215 + E_Ewald -350.4828662186 -4768.5640319440 + E_entropy(-TS) -0.0077131104 -0.1049422506 + E_descf 0.0000000000 0.0000000000 + E_exx 0.0000000000 0.0000000000 + E_Fermi 1.5017611709 20.4325089589 +---------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) +------------------------------------------------------------------------------------------- + Fe 0.0000000000 0.0000000000 0.0000000000 + Fe 0.0000000000 0.0000000000 0.0000000000 +------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------------------- +Orbital Charge Analysis Charge Mag(x) Mag(y) Mag(z) +------------------------------------------------------------------------------------------- +Fe1 + s 1.1149 0.0010 0.0010 0.0010 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5931 1.1679 1.1678 1.1678 + Sum 13.7051 1.1691 1.1690 1.1690 +Fe2 + s 1.1150 0.0010 0.0010 0.0010 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5959 1.1671 1.1671 1.1672 + Sum 13.7079 1.1683 1.1683 1.1684 +------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Total Magnetism (uB) +------------------------------------------------------------------------------------------- + Fe 1.1690981855 1.1689596529 1.1689548475 + Fe 1.1682782547 1.1683271615 1.1683689897 +------------------------------------------------------------------------------------------- + + + charge density convergence is achieved + final etot is -6825.6858753 eV + + +------------------------------------------------------------------------------------------ + TOTAL-FORCE (eV/Angstrom) +------------------------------------------------------------------------------------------ + Fe1 -0.0000234480 0.0000848730 0.0003959639 + Fe2 0.0000234480 -0.0000848730 -0.0003959639 +------------------------------------------------------------------------------------------ + + + + ------------------------------------------------------------------------------------------------ + Energy (Ry) Potential (Ry) Kinetic (Ry) Temperature (K) + -501.67563 -501.67848 0.0028501339 300 + ------------------------------------------------------------------------------------------------ + + + + ------------------------------------------- + STEP OF MOLECULAR DYNAMICS : 1 + ------------------------------------------- + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + NEW-OLD atomic charge density approx. for the potential ! + DONE : INIT SCF Time : 4.69025 (SEC) + + + PW ALGORITHM --------------- ION=2 ELEC=1 -------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 7.51393e-06 . +total magnetism (Bohr mag/cell) 2.46069 2.4606 2.46063 + absolute magnetism (Bohr mag/cell) = 4.35476 + Notice: Threshold on eigenvalues was too large. + hsover_error=0.32 > DRHO=0.00192412 + Origin diag_ethr = 0.01 + New diag_ethr = 6.01286e-06 +total magnetism (Bohr mag/cell) 2.46066 2.46058 2.46061 + absolute magnetism (Bohr mag/cell) = 4.36174 + + Density error is 0.00201217065486 + Error Threshold = 6.01286073232e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6717206991 -6825.5939269727 + E_Harris -501.6715817664 -6825.5920366956 + E_Fermi 1.4766917900 20.0914225341 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 2 ELEC= 2-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 6.01286073232e-06 . +total magnetism (Bohr mag/cell) 2.45367664792 2.45369451898 2.45369632661 + absolute magnetism (Bohr mag/cell) = 4.36076913778 + + Density error is 0.00924150911613 + Error Threshold = 6.28803329643e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6713570140 -6825.5889787830 + E_Harris -501.5122751175 -6823.4245585418 + E_Fermi 1.4801602125 20.1386128426 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 2 ELEC= 3-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 6.28803329643e-06 . +total magnetism (Bohr mag/cell) 2.45535480016 2.45535779838 2.45535744914 + absolute magnetism (Bohr mag/cell) = 4.36222821556 + + Density error is 0.00222730537898 + Error Threshold = 6.28803329643e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6723462878 -6825.6024385430 + E_Harris -501.6357961303 -6825.1051481383 + E_Fermi 1.4800078639 20.1365400332 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 2 ELEC= 4-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 6.28803329643e-06 . +total magnetism (Bohr mag/cell) 2.4557794855 2.45578128279 2.45578053957 + absolute magnetism (Bohr mag/cell) = 4.36171007414 + + Density error is 6.47878398594e-06 + Error Threshold = 6.28803329643e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6724876966 -6825.6043625085 + E_KS(sigma->0) -501.6678952474 -6825.5418790320 + E_Harris -501.6633376598 -6825.4798698707 + E_band -27.8834155884 -379.3733317038 + E_one_elec -203.5744480246 -2769.7724603392 + E_Hartree 123.7840339882 1684.1681836654 + E_xc -71.4833250872 -972.5805331721 + E_Ewald -350.4821832426 -4768.5547395789 + E_entropy(-TS) -0.0091848983 -0.1249669530 + E_descf 0.0000000000 0.0000000000 + E_exx 0.0000000000 0.0000000000 + E_Fermi 1.4799614353 20.1359083399 +---------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) +------------------------------------------------------------------------------------------- + Fe -0.1674727502 -0.1674714507 -0.1674677599 + Fe -0.1685388103 -0.1685393463 -0.1685411873 +------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------------------- +Orbital Charge Analysis Charge Mag(x) Mag(y) Mag(z) +------------------------------------------------------------------------------------------- +Fe1 + s 1.1147 0.0019 0.0019 0.0019 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5877 1.2479 1.2479 1.2479 + Sum 13.6994 1.2500 1.2501 1.2501 +Fe2 + s 1.1148 0.0019 0.0019 0.0019 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5911 1.2480 1.2480 1.2480 + Sum 13.7030 1.2502 1.2502 1.2502 +------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Total Magnetism (uB) +------------------------------------------------------------------------------------------- + Fe 1.2500362016 1.2500750113 1.2500654974 + Fe 1.2501907811 1.2501525281 1.2501618807 +------------------------------------------------------------------------------------------- + + + charge density convergence is achieved + final etot is -6825.6043625 eV + + +------------------------------------------------------------------------------------------ + TOTAL-FORCE (eV/Angstrom) +------------------------------------------------------------------------------------------ + Fe1 -0.0821749928 -0.0641832335 0.0945557521 + Fe2 0.0821749928 0.0641832335 -0.0945557521 +------------------------------------------------------------------------------------------ + + + + ------------------------------------------------------------------------------------------------ + Energy (Ry) Potential (Ry) Kinetic (Ry) Temperature (K) + -501.66966 -501.67249 0.0028297442 297.85381 + ------------------------------------------------------------------------------------------------ + + + + ------------------------------------------- + STEP OF MOLECULAR DYNAMICS : 2 + ------------------------------------------- + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + first order charge density extrapolation ! + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + DONE : INIT SCF Time : 20.3341 (SEC) + + + PW ALGORITHM --------------- ION=3 ELEC=1 -------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 6.28803e-06 . +total magnetism (Bohr mag/cell) 2.45486 2.45489 2.45489 + absolute magnetism (Bohr mag/cell) = 4.36543 + Notice: Threshold on eigenvalues was too large. + hsover_error=0.32 > DRHO=0.00210159 + Origin diag_ethr = 0.01 + New diag_ethr = 6.56747e-06 +total magnetism (Bohr mag/cell) 2.45485 2.45488 2.45488 + absolute magnetism (Bohr mag/cell) = 4.36511 + + Density error is 0.00210610440114 + Error Threshold = 6.56747174472e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6717145278 -6825.5938430078 + E_Harris -501.6715861090 -6825.5920957801 + E_Fermi 1.4832199190 20.1802422850 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 3 ELEC= 2-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 6.56747174472e-06 . +total magnetism (Bohr mag/cell) 2.45728332606 2.45728465763 2.45728362282 + absolute magnetism (Bohr mag/cell) = 4.36144612893 + + Density error is 0.0100994423697 + Error Threshold = 6.58157625357e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6712030534 -6825.5868840416 + E_Harris -501.8309806843 -6827.7607702341 + E_Fermi 1.4801826654 20.1389183308 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 3 ELEC= 3-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 6.58157625357e-06 . +total magnetism (Bohr mag/cell) 2.45602003892 2.45602235568 2.45602305001 + absolute magnetism (Bohr mag/cell) = 4.36008011062 + + Density error is 0.0022259696948 + Error Threshold = 6.58157625357e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6723719367 -6825.6027875138 + E_Harris -501.7036416542 -6826.0282338473 + E_Fermi 1.4800402952 20.1369812842 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 3 ELEC= 4-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 6.58157625357e-06 . +total magnetism (Bohr mag/cell) 2.45568827013 2.45568820365 2.45568816609 + absolute magnetism (Bohr mag/cell) = 4.36059357058 + + Density error is 0.000113741995613 + Error Threshold = 6.58157625357e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6725298576 -6825.6049361390 + E_KS(sigma->0) -501.6679841586 -6825.5430887303 + E_Harris -501.6890083521 -6825.8291375583 + E_band -27.8841363403 -379.3831380370 + E_one_elec -203.5689978550 -2769.6983069772 + E_Hartree 123.7762138387 1684.0617850727 + E_xc -71.4816492900 -972.5577327814 + E_Ewald -350.4801490952 -4768.5270635846 + E_entropy(-TS) -0.0090913981 -0.1236948175 + E_descf 0.0000000000 0.0000000000 + E_exx 0.0000000000 0.0000000000 + E_Fermi 1.4800346303 20.1369042091 +---------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) +------------------------------------------------------------------------------------------- + Fe -0.1652181739 -0.1652325578 -0.1652321249 + Fe -0.1654941827 -0.1654786653 -0.1654791319 +------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------------------- +Orbital Charge Analysis Charge Mag(x) Mag(y) Mag(z) +------------------------------------------------------------------------------------------- +Fe1 + s 1.1147 0.0019 0.0019 0.0019 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5895 1.2477 1.2476 1.2476 + Sum 13.7013 1.2499 1.2498 1.2498 +Fe2 + s 1.1147 0.0019 0.0019 0.0019 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5893 1.2478 1.2479 1.2479 + Sum 13.7011 1.2500 1.2500 1.2500 +------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Total Magnetism (uB) +------------------------------------------------------------------------------------------- + Fe 1.2498513779 1.2497690096 1.2497695030 + Fe 1.2499638767 1.2500461778 1.2500456124 +------------------------------------------------------------------------------------------- + + + charge density convergence is achieved + final etot is -6825.6049361 eV + + +------------------------------------------------------------------------------------------ + TOTAL-FORCE (eV/Angstrom) +------------------------------------------------------------------------------------------ + Fe1 -0.0263258654 -0.0198291990 0.0328141173 + Fe2 0.0263258654 0.0198291990 -0.0328141173 +------------------------------------------------------------------------------------------ + + + + ------------------------------------------------------------------------------------------------ + Energy (Ry) Potential (Ry) Kinetic (Ry) Temperature (K) + -501.66969 -501.67253 0.0028369371 298.61093 + ------------------------------------------------------------------------------------------------ + + + + ------------------------------------------- + STEP OF MOLECULAR DYNAMICS : 3 + ------------------------------------------- + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + second order charge density extrapolation ! + alpha = -0.46539717 + beta = 1.4648176 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + Find the file, try to read charge from file. + read in fermi energy = 0 + DONE : INIT SCF Time : 41.017 (SEC) + + + PW ALGORITHM --------------- ION=4 ELEC=1 -------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 6.58158e-06 . +total magnetism (Bohr mag/cell) 2.45395 2.45395 2.45395 + absolute magnetism (Bohr mag/cell) = 4.36621 + Notice: Threshold on eigenvalues was too large. + hsover_error=0.32 > DRHO=0.00327757 + Origin diag_ethr = 0.01 + New diag_ethr = 1.02424e-05 +total magnetism (Bohr mag/cell) 2.45393 2.45393 2.45393 + absolute magnetism (Bohr mag/cell) = 4.36585 + + Density error is 0.00328180767194 + Error Threshold = 1.02424082459e-05 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6714819577 -6825.5906787296 + E_Harris -501.6704871756 -6825.5771440242 + E_Fermi 1.4848945022 20.2030261586 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 4 ELEC= 2-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 1.02424082459e-05 . +total magnetism (Bohr mag/cell) 2.45834495411 2.45824446616 2.45825644792 + absolute magnetism (Bohr mag/cell) = 4.36143824469 + + Density error is 0.00407139541475 + Error Threshold = 1.02556489748e-05 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6718059210 -6825.5950864757 + E_Harris -501.8968017467 -6828.6563117308 + E_Fermi 1.4803720649 20.1414952427 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 4 ELEC= 3-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 1.02556489748e-05 . +total magnetism (Bohr mag/cell) 2.4561430127 2.4561402075 2.45614046704 + absolute magnetism (Bohr mag/cell) = 4.36006713268 + + Density error is 0.00621121698173 + Error Threshold = 1.02556489748e-05 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6723961915 -6825.6031175180 + E_Harris -501.7084280186 -6826.0933556758 + E_Fermi 1.4808558000 20.1480767961 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 4 ELEC= 4-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0 ; where current threshold is: 1.02556489748e-05 . +total magnetism (Bohr mag/cell) 2.45642597595 2.45642438896 2.45642568116 + absolute magnetism (Bohr mag/cell) = 4.36090201409 + + Density error is 0.000907798588431 + Error Threshold = 1.02556489748e-05 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6725094449 -6825.6046584088 + E_KS(sigma->0) -501.6682437690 -6825.5466209109 + E_Harris -501.6882798535 -6825.8192258261 + E_band -27.8806642947 -379.3358984328 + E_one_elec -203.5720364691 -2769.7396494442 + E_Hartree 123.7795681857 1684.1074233046 + E_xc -71.4830945963 -972.5773971831 + E_Ewald -350.4767334207 -4768.4805909482 + E_entropy(-TS) -0.0085313518 -0.1160749959 + E_descf 0.0000000000 0.0000000000 + E_exx 0.0000000000 0.0000000000 + E_Fermi 1.4811684035 20.1523299848 +---------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) +------------------------------------------------------------------------------------------- + Fe -0.1614117188 -0.1614064417 -0.1614126965 + Fe -0.1590151938 -0.1590593187 -0.1590482375 +------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------------------- +Orbital Charge Analysis Charge Mag(x) Mag(y) Mag(z) +------------------------------------------------------------------------------------------- +Fe1 + s 1.1148 0.0020 0.0020 0.0020 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5942 1.2476 1.2477 1.2477 + Sum 13.7061 1.2498 1.2499 1.2499 +Fe2 + s 1.1146 0.0018 0.0018 0.0018 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5847 1.2480 1.2480 1.2480 + Sum 13.6964 1.2501 1.2500 1.2500 +------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Total Magnetism (uB) +------------------------------------------------------------------------------------------- + Fe 1.2498251342 1.2498544518 1.2498533551 + Fe 1.2500507306 1.2500181434 1.2500206507 +------------------------------------------------------------------------------------------- + + + charge density convergence is achieved + final etot is -6825.6046584 eV + + +------------------------------------------------------------------------------------------ + TOTAL-FORCE (eV/Angstrom) +------------------------------------------------------------------------------------------ + Fe1 -0.0441291367 -0.0281389251 0.0597264967 + Fe2 0.0441291367 0.0281389251 -0.0597264967 +------------------------------------------------------------------------------------------ + + + + ------------------------------------------------------------------------------------------------ + Energy (Ry) Potential (Ry) Kinetic (Ry) Temperature (K) + -501.66963 -501.67251 0.0028755884 302.67929 + ------------------------------------------------------------------------------------------------ + + + + + -------------------------------------------- + !FINAL_ETOT_IS -6825.60465840883 eV + -------------------------------------------- + + +TIME STATISTICS +----------------------------------------------------------------------------- + CLASS_NAME NAME TIME/s CALLS AVG/s PER/% +----------------------------------------------------------------------------- + total 59.14 21 2.82 100.00 + Driver reading 0.03 1 0.03 0.05 + Input_Conv Convert 0.00 1 0.00 0.00 + Driver driver_line 59.11 1 59.11 99.95 + UnitCell check_tau 0.00 1 0.00 0.00 + PW_Basis_Sup setuptransform 0.00 1 0.00 0.01 + PW_Basis_Sup distributeg 0.00 1 0.00 0.00 + mymath heapsort 0.00 7 0.00 0.00 + PW_Basis_K setuptransform 0.00 1 0.00 0.00 + PW_Basis_K distributeg 0.00 1 0.00 0.00 + PW_Basis setup_struc_factor 0.01 4 0.00 0.01 + ppcell_vnl init 0.00 1 0.00 0.00 + ppcell_vl init_vloc 0.01 1 0.01 0.01 + ppcell_vnl init_vnl 0.03 1 0.03 0.05 + WF_atomic init_at_1 0.17 1 0.17 0.29 + wavefunc wfcinit 0.00 1 0.00 0.00 + Run_MD md_line 58.80 1 58.80 99.43 + Nose_Hoover setup 4.18 1 4.18 7.06 + MD_func force_virial 58.73 4 14.68 99.31 + ESolver_KS_PW runner 58.35 4 14.59 98.66 + H_Ewald_pw compute_ewald 0.00 4 0.00 0.01 + Charge set_rho_core 0.03 4 0.01 0.05 + PW_Basis_Sup recip2real 0.35 492 0.00 0.60 + PW_Basis_Sup gathers_scatterp 0.06 492 0.00 0.10 + Charge atomic_rho 0.09 8 0.01 0.15 + Potential init_pot 0.28 4 0.07 0.47 + Potential update_from_charge 1.36 20 0.07 2.29 + Potential cal_fixed_v 0.00 4 0.00 0.01 + PotLocal cal_fixed_v 0.00 4 0.00 0.00 + Potential cal_v_eff 1.34 20 0.07 2.27 + H_Hartree_pw v_hartree 0.04 20 0.00 0.06 + PW_Basis_Sup real2recip 0.33 648 0.00 0.56 + PW_Basis_Sup gatherp_scatters 0.03 648 0.00 0.05 + PotXC cal_v_eff 1.30 20 0.06 2.20 + XC_Functional v_xc 1.55 24 0.06 2.63 + Potential interpolate_vrs 0.01 20 0.00 0.01 + OnsiteProj cubspl_tabulate 0.09 1 0.09 0.16 + Charge_Mixing init_mixing 0.00 4 0.00 0.01 + ESolver_KS_PW hamilt2density 10.19 20 0.51 17.24 + HSolverPW solve 16.62 32 0.52 28.10 + pp_cell_vnl getvnl 0.86 330 0.00 1.46 + Structure_Factor get_sk 0.10 340 0.00 0.16 + OnsiteProj getvnl 0.00 330 0.00 0.00 + OnsiteProj tabulate_atomic 0.00 366 0.00 0.00 + WF_atomic atomic_wfc 0.01 1 0.01 0.01 + DiagoIterAssist diagH_subspace_init 0.13 1 0.13 0.23 + Operator hPsi 48.88 4254 0.01 82.65 + Operator EkineticPW 0.25 4254 0.00 0.41 + Operator VeffPW 43.72 4254 0.01 73.93 + PW_Basis_K recip2real 12.72 43840 0.00 21.51 + PW_Basis_K gathers_scatterp 1.93 43840 0.00 3.26 + PW_Basis_K real2recip 11.60 41760 0.00 19.62 + PW_Basis_K gatherp_scatters 0.85 41760 0.00 1.44 + Operator NonlocalPW 3.23 4254 0.00 5.46 + Nonlocal add_nonlocal_pp 1.98 4254 0.00 3.35 + Operator OnsiteProjPW 1.67 4254 0.00 2.82 + OnsiteProj overlap 0.82 4290 0.00 1.39 + FS_Nonlocal_tools cal_becp 0.83 4298 0.00 1.40 + OnsiteProj add_onsite_proj 0.70 4254 0.00 1.19 + DiagoIterAssist diagH_LAPACK 0.36 326 0.00 0.61 + DiagoCG diag_once 12.02 32 0.38 20.33 + DiagoCG_New spsi_func 0.11 7856 0.00 0.19 + DiagoCG_New hpsi_func 10.47 3928 0.00 17.70 + ElecStatePW psiToRho 0.90 20 0.05 1.52 + Charge_Mixing get_drho 0.10 20 0.00 0.17 + Charge_Mixing inner_product_recip_rho 0.00 20 0.00 0.01 + Charge mix_rho 0.14 12 0.01 0.24 + Charge Broyden_mixing 0.02 12 0.00 0.03 + DiagoIterAssist diagH_subspace 3.39 27 0.13 5.73 + Charge_Mixing inner_product_recip_hartree 0.01 24 0.00 0.01 + OnsiteProj cal_occupation 0.01 4 0.00 0.01 + Forces cal_force_loc 0.01 4 0.00 0.01 + Forces cal_force_ew 0.01 4 0.00 0.01 + Forces cal_force_nl 0.03 4 0.01 0.06 + FS_Nonlocal_tools cal_dbecp_f 0.04 24 0.00 0.06 + Forces cal_force_onsite 0.02 4 0.00 0.03 + Forces cal_force_cc 0.29 4 0.07 0.49 + Forces cal_force_scc 0.03 4 0.01 0.05 + Nose_Hoover first_half 0.00 3 0.00 0.00 + SpinConstrain cal_mw_from_lambda 44.69 310 0.14 75.57 + DiagoIterAssist diag_responce 37.41 298 0.13 63.25 + Nose_Hoover second_half 0.00 3 0.00 0.00 + ModuleIO write_istate_info 0.01 1 0.01 0.01 +----------------------------------------------------------------------------- + + + NAME-------------------------|MEMORY(MB)-------- +total 24.43 +Psi_PW 3.994 +DiagSub::temp 3.994 +FFT::grid 1.424 +Chg::rho 1.424 +Chg::rho_save 1.424 +Pot::veff 1.424 +Pot::veff_smooth 1.424 +VNL::vkb 1.383 + ------------- < 1.0 MB has been ignored ---------------- + ---------------------------------------------------------- + + Start Time : Fri Sep 6 10:04:51 2024 + Finish Time : Fri Sep 6 10:05:51 2024 + Total Time : 0 h 1 mins 0 secs diff --git a/tests/abacus.spin/OUT.ABACUS/running_relax.log b/tests/abacus.spin/OUT.ABACUS/running_relax.log new file mode 100644 index 00000000..d94c8f89 --- /dev/null +++ b/tests/abacus.spin/OUT.ABACUS/running_relax.log @@ -0,0 +1,1002 @@ + + ABACUS v3.7.1 + + Atomic-orbital Based Ab-initio Computation at UStc + + Website: http://abacus.ustc.edu.cn/ + Documentation: https://abacus.deepmodeling.com/ + Repository: https://github.com/abacusmodeling/abacus-develop + https://github.com/deepmodeling/abacus-develop + Commit: 8443c42b5 (Thu Sep 5 14:19:05 2024 +0800) + + Start Time is Fri Sep 6 09:29:43 2024 + + ------------------------------------------------------------------------------------ + + READING GENERAL INFORMATION + global_out_dir = OUT.spin/ + global_in_card = INPUT + pseudo_dir = ./ + orbital_dir = ./ + DRANK = 1 + DSIZE = 1 + DCOLOR = 1 + GRANK = 1 + GSIZE = 1 + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Reading atom information in unitcell: | + | From the input file and the structure file we know the number of | + | different elments in this unitcell, then we list the detail | + | information for each element, especially the zeta and polar atomic | + | orbital number for each element. The total atom number is counted. | + | We calculate the nearest atom distance for each atom and show the | + | Cartesian and Direct coordinates for each atom. We list the file | + | address for atomic orbitals. The volume and the lattice vectors | + | in real and reciprocal space is also shown. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + READING UNITCELL INFORMATION + ntype = 1 + lattice constant (Bohr) = 1.88028 + lattice constant (Angstrom) = 0.995 + + READING ATOM TYPE 1 + atom label = Fe + L=0, number of zeta = 4 + L=1, number of zeta = 2 + L=2, number of zeta = 2 + L=3, number of zeta = 1 + number of atom for this type = 2 + magnetization of element 1 = [ 1.25, 1.25, 1.25 ] + + TOTAL ATOM NUMBER = 2 +DIRECT COORDINATES + atom x y z mag vx vy vz +taud_Fe1 0.0000000000 0.0000000000 0.0000000000 2.1651 0.0000000000 0.0000000000 0.0000000000 +taud_Fe2 0.5000000000 0.5000000000 0.5000000000 2.1651 0.0000000000 0.0000000000 0.0000000000 + + + + Volume (Bohr^3) = 150.259 + Volume (A^3) = 22.266 + + Lattice vectors: (Cartesian coordinate: in unit of a_0) + +2.82743 +0 +0 + +0 +2.82743 +0 + +0 +0 +2.82743 + Reciprocal vectors: (Cartesian coordinate: in unit of 2 pi/a_0) + +0.353679 -0 +0 + -0 +0.353679 -0 + +0 -0 +0.353679 + The esolver type has been set to : ksdft_pw + + RUNNING WITH DEVICE : CPU / Intel(R) Xeon(R) Platinum + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Reading pseudopotentials files: | + | The pseudopotential file is in UPF format. The 'NC' indicates that | + | the type of pseudopotential is 'norm conserving'. Functional of | + | exchange and correlation is decided by 4 given parameters in UPF | + | file. We also read in the 'core correction' if there exists. | + | Also we can read the valence electrons number and the maximal | + | angular momentum used in this pseudopotential. We also read in the | + | trail wave function, trail atomic density and local-pseudopotential| + | on logrithmic grid. The non-local pseudopotential projector is also| + | read in if there is any. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + PAO radial cut off (Bohr) = 15 + + Read in pseudopotential file is Fe.upf + pseudopotential type = NC + exchange-correlation functional = PBE + nonlocal core correction = 1 + valence electrons = 16 + lmax = 2 + number of zeta = 4 + number of projectors = 6 + L of projector = 0 + L of projector = 0 + L of projector = 1 + L of projector = 1 + L of projector = 2 + L of projector = 2 + initial pseudo atomic orbital number = 40 + NLOCAL = 108 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Warning: the number of valence electrons in pseudopotential > 8 for Fe: [Ar] 3d6 4s2 + Pseudopotentials with additional electrons can yield (more) accurate outcomes, but may be less efficient. + If you're confident that your chosen pseudopotential is appropriate, you can safely ignore this warning. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup plane waves of charge/potential: | + | Use the energy cutoff and the lattice vectors to generate the | + | dimensions of FFT grid. The number of FFT grid on each processor | + | is 'nrxx'. The number of plane wave basis in reciprocal space is | + | different for charege/potential and wave functions. We also set | + | the 'sticks' for the parallel of FFT. The number of plane waves | + | is 'npw' in each processor. | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP THE PLANE WAVE BASIS + energy cutoff for charge/potential (unit:Ry) = 400 + fft grid for charge/potential = [ 36, 36, 36 ] + fft grid division = [ 1, 1, 1 ] + big fft grid for charge/potential = [ 36, 36, 36 ] + nbxx = 46656 + nrxx = 46656 + + SETUP PLANE WAVES FOR CHARGE/POTENTIAL + number of plane waves = 20341 + number of sticks = 885 + + PARALLEL PW FOR CHARGE/POTENTIAL + PROC COLUMNS(POT) PW + 1 885 20341 + --------------- sum ------------------- + 1 885 20341 + number of |g| = 241 + max |g| = 35.7753 + min |g| = 0 + +----------- Double Check Mixing Parameters Begin ------------ +mixing_type: broyden +mixing_beta: 0.4 +mixing_gg0: 1 +mixing_gg0_min: 0.1 +mixing_beta_mag: 1.6 +mixing_gg0_mag: 0 +mixing_ndim: 8 +----------- Double Check Mixing Parameters End ------------ + + SETUP THE ELECTRONS NUMBER + electron number of element Fe = 16 + total electron number of element Fe = 32 + AUTOSET number of electrons: = 32 + DONE : SETUP UNITCELL Time : 0.0957571 (SEC) + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup K-points | + | We setup the k-points according to input parameters. | + | The reduced k-points are set according to symmetry operations. | + | We treat the spin as another set of k-points. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP K-POINTS + nspin = 4 + Input type of k points = Monkhorst-Pack(Gamma) + nkstot = 1 + nkstot_ibz = 1 +K-POINTS REDUCTION ACCORDING TO SYMMETRY + IBZ DIRECT_X DIRECT_Y DIRECT_Z WEIGHT ibz2bz + 1 0.00000000 0.00000000 0.00000000 1.0000 0 + + nkstot now = 1 +K-POINTS DIRECT COORDINATES + KPOINTS DIRECT_X DIRECT_Y DIRECT_Z WEIGHT + 1 0.00000000 0.00000000 0.00000000 1.0000 + + + k-point number in this process = 1 + minimum distributed K point number = 1 + +K-POINTS CARTESIAN COORDINATES + KPOINTS CARTESIAN_X CARTESIAN_Y CARTESIAN_Z WEIGHT + 1 0.00000000 0.00000000 0.00000000 1.0000 + + +K-POINTS DIRECT COORDINATES + KPOINTS DIRECT_X DIRECT_Y DIRECT_Z WEIGHT + 1 0.00000000 0.00000000 0.00000000 1.0000 + + DONE : INIT K-POINTS Time : 0.105664 (SEC) + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup plane waves of wave functions: | + | Use the energy cutoff and the lattice vectors to generate the | + | dimensions of FFT grid. The number of FFT grid on each processor | + | is 'nrxx'. The number of plane wave basis in reciprocal space is | + | different for charege/potential and wave functions. We also set | + | the 'sticks' for the parallel of FFT. The number of plane wave of | + | each k-point is 'npwk[ik]' in each processor | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP PLANE WAVES FOR WAVE FUNCTIONS + energy cutoff for wavefunc (unit:Ry) = 100 + fft grid for wave functions = [ 36, 36, 36 ] + number of plane waves = 2517 + number of sticks = 221 + + PARALLEL PW FOR WAVE FUNCTIONS + PROC COLUMNS(POT) PW + 1 221 2517 + --------------- sum ------------------- + 1 221 2517 + DONE : INIT PLANEWAVE Time : 0.106715 (SEC) + + occupied bands = 16 + NBANDS = 52 + + SETUP NONLOCAL PSEUDOPOTENTIALS IN PLANE WAVE BASIS + Fe non-local projectors: + projector 1 L=0 + projector 2 L=0 + projector 3 L=1 + projector 4 L=1 + projector 5 L=2 + projector 6 L=2 + TOTAL NUMBER OF NONLOCAL PROJECTORS = 36 + DONE : LOCAL POTENTIAL Time : 0.122677 (SEC) + + + Init Non-Local PseudoPotential table : + Init Non-Local-Pseudopotential done. + DONE : NON-LOCAL POTENTIAL Time : 0.153353 (SEC) + + npwx = 2517 + + Make real space PAO into reciprocal space. + max mesh points in Pseudopotential = 1425 + dq(describe PAO in reciprocal space) = 0.01 + max q = 1204 + + number of pseudo atomic orbitals for Fe is 4 +WARNING: norm of atomic wavefunction # 2 of atomic type Fe is 0.999981, renormalized +WARNING: norm of atomic wavefunction # 3 of atomic type Fe is 0.999997, renormalized + DONE : INIT BASIS Time : 0.325415 (SEC) + + + ------------------------------------------- + STEP OF RELAXATION : 1 + ------------------------------------------- + init_chg = atomic + DONE : INIT SCF Time : 0.524608 (SEC) + + + PW ALGORITHM --------------- ION= 1 ELEC= 1-------------------------------- +total magnetism (Bohr mag/cell) 2.30943 2.30938 2.30941 + absolute magnetism (Bohr mag/cell) = 4.01976 + Notice: Threshold on eigenvalues was too large. + hsover_error=0.32 > DRHO=0.0686869 + Origin diag_ethr = 0.01 + New diag_ethr = 0.000214646 +total magnetism (Bohr mag/cell) 2.30943 2.30937 2.3094 + absolute magnetism (Bohr mag/cell) = 4.01964 + + Density error is 0.0548949686243 + Error Threshold = 0.000214646406708 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6632803550 -6825.4790901992 + E_Harris -501.6647758269 -6825.4994371390 + E_Fermi 1.4575316668 19.8307356840 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 2-------------------------------- +total magnetism (Bohr mag/cell) 2.3094424965 2.30934448798 2.30941621651 + absolute magnetism (Bohr mag/cell) = 4.13707180938 + + Density error is 0.00986449919003 + Error Threshold = 0.000171546776951 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6774595966 -6825.6720086784 + E_Harris -499.7039503033 -6798.8210372331 + E_Fermi 1.4964230898 20.3598806394 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 3-------------------------------- +total magnetism (Bohr mag/cell) 2.30944490813 2.30935345105 2.30940487564 + absolute magnetism (Bohr mag/cell) = 4.08853707061 + + Density error is 0.00240445773415 + Error Threshold = 3.08265599688e-05 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6784060189 -6825.6848854146 + E_Harris -501.4802459250 -6822.9887790210 + E_Fermi 1.4994551309 20.4011336751 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 4-------------------------------- +total magnetism (Bohr mag/cell) 2.30944724752 2.30935862214 2.30939736766 + absolute magnetism (Bohr mag/cell) = 4.08848804651 + + Density error is 0.00057214611471 + Error Threshold = 7.51393041921e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6784787714 -6825.6858752634 + E_KS(sigma->0) -501.6746222162 -6825.6334041381 + E_Harris -501.6738375513 -6825.6227282240 + E_band -27.9117558414 -379.7589206280 + E_one_elec -203.4207485774 -2767.6812720784 + E_Hartree 123.6718684724 1682.6420935310 + E_xc -71.4390193374 -971.9777225215 + E_Ewald -350.4828662186 -4768.5640319440 + E_entropy(-TS) -0.0077131104 -0.1049422506 + E_descf 0.0000000000 0.0000000000 + E_exx 0.0000000000 0.0000000000 + E_Fermi 1.5017611709 20.4325089589 +---------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) +------------------------------------------------------------------------------------------- + Fe 0.0000000000 0.0000000000 0.0000000000 + Fe 0.0000000000 0.0000000000 0.0000000000 +------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------------------- +Orbital Charge Analysis Charge Mag(x) Mag(y) Mag(z) +------------------------------------------------------------------------------------------- +Fe1 + s 1.1149 0.0010 0.0010 0.0010 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5931 1.1679 1.1678 1.1678 + Sum 13.7051 1.1691 1.1690 1.1690 +Fe2 + s 1.1150 0.0010 0.0010 0.0010 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5959 1.1671 1.1671 1.1672 + Sum 13.7079 1.1683 1.1683 1.1684 +------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Total Magnetism (uB) +------------------------------------------------------------------------------------------- + Fe 1.1690981855 1.1689596529 1.1689548475 + Fe 1.1682782547 1.1683271615 1.1683689897 +------------------------------------------------------------------------------------------- + + + charge density convergence is achieved + final etot is -6825.6858753 eV + EFERMI = 20.432508959 eV + + STATE ENERGY(eV) AND OCCUPATIONS NSPIN == 4 + 1/1 kpoint (Cartesian) = 0.0000 0.0000 0.0000 (2517 pws) + 1 -67.4569 1.00000 + 2 -67.2982 1.00000 + 3 -65.2976 1.00000 + 4 -65.1287 1.00000 + 5 -33.6978 1.00000 + 6 -33.6978 1.00000 + 7 -33.6978 1.00000 + 8 -33.3602 1.00000 + 9 -33.3602 1.00000 + 10 -33.3602 1.00000 + 11 -31.4558 1.00000 + 12 -31.4557 1.00000 + 13 -31.4553 1.00000 + 14 -31.0894 1.00000 + 15 -31.0892 1.00000 + 16 -31.0888 1.00000 + 17 10.4705 1.00000 + 18 10.6689 1.00000 + 19 14.7652 1.00000 + 20 14.7654 1.00000 + 21 16.3838 1.00000 + 22 16.3838 1.00000 + 23 17.7210 1.00000 + 24 17.7211 1.00000 + 25 17.7213 1.00000 + 26 19.1329 1.00000 + 27 19.1332 1.00000 + 28 19.5268 1.00000 + 29 19.5272 1.00000 + 30 19.5274 1.00000 + 31 20.3909 0.667123 + 32 20.3910 0.666801 + 33 20.3912 0.666076 + 34 21.6006 0.00000 + 35 21.6007 0.00000 + 36 22.5186 0.00000 + 37 22.5192 0.00000 + 38 22.5194 0.00000 + 39 29.2339 0.00000 + 40 29.2339 0.00000 + 41 29.2339 0.00000 + 42 29.4154 0.00000 + 43 29.4154 0.00000 + 44 29.4155 0.00000 + 45 35.3354 0.00000 + 46 35.3355 0.00000 + 47 36.5789 0.00000 + 48 36.5789 0.00000 + 49 40.4434 0.00000 + 50 41.3886 0.00000 + 51 43.5577 0.00000 + 52 43.5578 0.00000 + + + +------------------------------------------------------------------------------------------ + TOTAL-FORCE (eV/Angstrom) +------------------------------------------------------------------------------------------ + Fe1 -0.0000234480 0.0000848730 0.0003959639 + Fe2 0.0000234480 -0.0000848730 -0.0003959639 +------------------------------------------------------------------------------------------ + + + Relaxation is not converged yet! +DIRECT COORDINATES + atom x y z mag vx vy vz +taud_Fe1 0.9999995833 0.0000015084 0.0000070374 2.1651 0.0000000000 0.0000000000 0.0000000000 +taud_Fe2 0.5000004167 0.4999984916 0.4999929626 2.1651 0.0000000000 0.0000000000 0.0000000000 + + + + ------------------------------------------- + STEP OF RELAXATION : 2 + ------------------------------------------- + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + NEW-OLD atomic charge density approx. for the potential ! + DONE : INIT SCF Time : 4.144735 (SEC) + + + PW ALGORITHM --------------- ION= 2 ELEC= 1-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0.000000 ; where current threshold is: 0.000008 . +total magnetism (Bohr mag/cell) 2.460713 2.460629 2.460667 + absolute magnetism (Bohr mag/cell) = 4.354773 + Notice: Threshold on eigenvalues was too large. + hsover_error=0.320000 > DRHO=0.001904 + Origin diag_ethr = 0.010000 + New diag_ethr = 0.000006 +total magnetism (Bohr mag/cell) 2.460685 2.460607 2.460642 + absolute magnetism (Bohr mag/cell) = 4.361930 + + Density error is 0.001992966717 + Error Threshold = 0.000005950543 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6717345462 -6825.5941153720 + E_Harris -501.6715923037 -6825.5921800627 + E_Fermi 1.4766962002 20.0914825374 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 2 ELEC= 2-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0.000000000000 ; where current threshold is: 0.000005950543 . +total magnetism (Bohr mag/cell) 2.454388903679 2.454380662451 2.454379655944 + absolute magnetism (Bohr mag/cell) = 4.361912797650 + + Density error is 0.009228272161 + Error Threshold = 0.000006228021 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6713440630 -6825.5888025749 + E_Harris -501.5133874565 -6823.4396926906 + E_Fermi 1.4801385565 20.1383181977 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 2 ELEC= 3-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0.000000000000 ; where current threshold is: 0.000006228021 . +total magnetism (Bohr mag/cell) 2.455337730187 2.455339212147 2.455339333866 + absolute magnetism (Bohr mag/cell) = 4.362219771057 + + Density error is 0.002195963526 + Error Threshold = 0.000006228021 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6723554230 -6825.6025628342 + E_Harris -501.6387445550 -6825.1452635146 + E_Fermi 1.4799798703 20.1361591613 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 2 ELEC= 4-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0.000000000000 ; where current threshold is: 0.000006228021 . +total magnetism (Bohr mag/cell) 2.455780448000 2.455781001768 2.455780765168 + absolute magnetism (Bohr mag/cell) = 4.361708226902 + + Density error is 0.000005361548 + Error Threshold = 0.000006228021 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6724951355 -6825.6044637205 + E_KS(sigma->0) -501.6679025517 -6825.5419784114 + E_Harris -501.6617290453 -6825.4579835487 + E_band -27.8843141296 -379.3855569848 + E_one_elec -203.5744655302 -2769.7726985160 + E_Hartree 123.7850811379 1684.1824308678 + E_xc -71.4835967872 -972.5842298408 + E_Ewald -350.4828661766 -4768.5640313736 + E_entropy(-TS) -0.0091851677 -0.1249706181 + E_descf 0.0000000000 0.0000000000 + E_exx 0.0000000000 0.0000000000 + E_Fermi 1.4799376237 20.1355843666 +---------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) +------------------------------------------------------------------------------------------- + Fe -0.1673462642 -0.1673537756 -0.1673561712 + Fe -0.1683646747 -0.1683589722 -0.1683562492 +------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------------------- +Orbital Charge Analysis Charge Mag(x) Mag(y) Mag(z) +------------------------------------------------------------------------------------------- +Fe1 + s 1.1147 0.0019 0.0019 0.0019 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5877 1.2479 1.2479 1.2479 + Sum 13.6995 1.2501 1.2501 1.2500 +Fe2 + s 1.1148 0.0019 0.0019 0.0019 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5911 1.2480 1.2480 1.2480 + Sum 13.7029 1.2502 1.2502 1.2502 +------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Total Magnetism (uB) +------------------------------------------------------------------------------------------- + Fe 1.2500714313 1.2500616663 1.2500458747 + Fe 1.2501576398 1.2501678047 1.2501834374 +------------------------------------------------------------------------------------------- + + + charge density convergence is achieved + final etot is -6825.60446372048 eV + EFERMI = 20.13558436665 eV + + STATE ENERGY(eV) AND OCCUPATIONS NSPIN == 4 + 1/1 kpoint (Cartesian) = 0.00000 0.00000 0.00000 (2517 pws) + 1 -67.494313 1.000000 + 2 -67.340218 1.000000 + 3 -65.146260 1.000000 + 4 -64.972269 1.000000 + 5 -34.011284 1.000000 + 6 -34.011263 1.000000 + 7 -34.011242 1.000000 + 8 -33.673639 1.000000 + 9 -33.673617 1.000000 + 10 -33.673596 1.000000 + 11 -31.014754 1.000000 + 12 -31.014713 1.000000 + 13 -31.014689 1.000000 + 14 -30.647472 1.000000 + 15 -30.647431 1.000000 + 16 -30.647407 1.000000 + 17 10.202179 1.000000 + 18 10.967145 1.000000 + 19 14.534082 1.000000 + 20 14.534103 1.000000 + 21 16.670113 1.000000 + 22 16.670140 1.000000 + 23 17.413528 1.000000 + 24 17.413556 1.000000 + 25 17.413589 1.000000 + 26 18.850839 1.000000 + 27 18.850854 1.000000 + 28 19.969576 0.957785 + 29 19.969617 0.957746 + 30 19.969666 0.957700 + 31 20.082615 0.709038 + 32 20.082645 0.708930 + 33 20.082682 0.708801 + 34 21.999005 0.000000 + 35 21.999021 0.000000 + 36 22.978862 0.000000 + 37 22.978908 0.000000 + 38 22.978962 0.000000 + 39 29.262993 0.000000 + 40 29.263007 0.000000 + 41 29.263018 0.000000 + 42 29.450239 0.000000 + 43 29.450247 0.000000 + 44 29.450271 0.000000 + 45 35.322294 0.000000 + 46 35.322339 0.000000 + 47 36.677233 0.000000 + 48 36.677283 0.000000 + 49 40.233768 0.000000 + 50 41.594113 0.000000 + 51 43.613799 0.000000 + 52 43.613882 0.000000 + + + +------------------------------------------------------------------------------------------ + TOTAL-FORCE (eV/Angstrom) +------------------------------------------------------------------------------------------ + Fe1 0.0000423260 0.0001601470 0.0005255426 + Fe2 -0.0000423260 -0.0001601470 -0.0005255426 +------------------------------------------------------------------------------------------ + + + Relaxation is not converged yet! +DIRECT COORDINATES + atom x y z mag vx vy vz +taud_Fe1 0.9999983331 0.0000060337 0.0000281496 2.1651 0.0000000000 0.0000000000 0.0000000000 +taud_Fe2 0.5000016669 0.4999939663 0.4999718504 2.1651 0.0000000000 0.0000000000 0.0000000000 + + + + ------------------------------------------- + STEP OF RELAXATION : 3 + ------------------------------------------- + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + first order charge density extrapolation ! + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + Find the file, try to read charge from file. + read in fermi energy = 0.000000 + DONE : INIT SCF Time : 19.338131 (SEC) + + + PW ALGORITHM --------------- ION= 3 ELEC= 1-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0.000000 ; where current threshold is: 0.000006 . +total magnetism (Bohr mag/cell) 2.455075 2.455068 2.455069 + absolute magnetism (Bohr mag/cell) = 4.365921 + Notice: Threshold on eigenvalues was too large. + hsover_error=0.320000 > DRHO=0.002000 + Origin diag_ethr = 0.010000 + New diag_ethr = 0.000006 +total magnetism (Bohr mag/cell) 2.455050 2.455043 2.455045 + absolute magnetism (Bohr mag/cell) = 4.365458 + + Density error is 0.002011456982 + Error Threshold = 0.000006249033 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6717775158 -6825.5947000038 + E_Harris -501.6716068292 -6825.5923776932 + E_Fermi 1.4833035949 20.1813807543 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 3 ELEC= 2-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0.000000000000 ; where current threshold is: 0.000006249033 . +total magnetism (Bohr mag/cell) 2.457174616872 2.457187849929 2.457184529753 + absolute magnetism (Bohr mag/cell) = 4.361364469883 + + Density error is 0.008790489237 + Error Threshold = 0.000006285803 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6713261044 -6825.5885582359 + E_Harris -501.8313917187 -6827.7663626437 + E_Fermi 1.4802224904 20.1394601766 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 3 ELEC= 3-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0.000000000000 ; where current threshold is: 0.000006285803 . +total magnetism (Bohr mag/cell) 2.455913724950 2.455916535345 2.455916133793 + absolute magnetism (Bohr mag/cell) = 4.360083340583 + + Density error is 0.002354066573 + Error Threshold = 0.000006285803 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6723623430 -6825.6026569852 + E_Harris -501.7081536771 -6826.0896230689 + E_Fermi 1.4800610576 20.1372637709 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 3 ELEC= 4-------------------------------- +Average iterative diagonalization steps for k-points 0 is: 0.000000000000 ; where current threshold is: 0.000006285803 . +total magnetism (Bohr mag/cell) 2.455552549274 2.455552935684 2.455553065202 + absolute magnetism (Bohr mag/cell) = 4.360588456959 + + Density error is 0.000003562109 + Error Threshold = 0.000006285803 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.6725119610 -6825.6046926422 + E_KS(sigma->0) -501.6679202282 -6825.5422189132 + E_Harris -501.6917485490 -6825.8664198503 + E_band -27.8833131578 -379.3719380647 + E_one_elec -203.5627195010 -2769.6128855894 + E_Hartree 123.7718909794 1684.0029695549 + E_xc -71.4811134180 -972.5504418684 + E_Ewald -350.4828655476 -4768.5640228156 + E_entropy(-TS) -0.0091834655 -0.1249474581 + E_descf 0.0000000000 0.0000000000 + E_exx 0.0000000000 0.0000000000 + E_Fermi 1.4799197855 20.1353416661 +---------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) +------------------------------------------------------------------------------------------- + Fe -0.1657340600 -0.1657462694 -0.1657444988 + Fe -0.1661948902 -0.1661794752 -0.1661827215 +------------------------------------------------------------------------------------------- + + +------------------------------------------------------------------------------------------- +Orbital Charge Analysis Charge Mag(x) Mag(y) Mag(z) +------------------------------------------------------------------------------------------- +Fe1 + s 1.1147 0.0019 0.0019 0.0019 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5891 1.2477 1.2476 1.2476 + Sum 13.7008 1.2498 1.2498 1.2498 +Fe2 + s 1.1147 0.0019 0.0019 0.0019 + p 5.9971 0.0002 0.0002 0.0002 + d 6.5897 1.2478 1.2479 1.2479 + Sum 13.7015 1.2500 1.2500 1.2500 +------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Total Magnetism (uB) +------------------------------------------------------------------------------------------- + Fe 1.2498499353 1.2497710829 1.2497831345 + Fe 1.2499653291 1.2500441039 1.2500320770 +------------------------------------------------------------------------------------------- + + + charge density convergence is achieved + final etot is -6825.60469264220 eV + EFERMI = 20.13534166609 eV + + STATE ENERGY(eV) AND OCCUPATIONS NSPIN == 4 + 1/1 kpoint (Cartesian) = 0.00000 0.00000 0.00000 (2517 pws) + 1 -67.494346 1.000000 + 2 -67.340195 1.000000 + 3 -65.142147 1.000000 + 4 -64.968232 1.000000 + 5 -34.009462 1.000000 + 6 -34.009434 1.000000 + 7 -34.009266 1.000000 + 8 -33.671810 1.000000 + 9 -33.671781 1.000000 + 10 -33.671615 1.000000 + 11 -31.015112 1.000000 + 12 -31.014434 1.000000 + 13 -31.014342 1.000000 + 14 -30.647876 1.000000 + 15 -30.647195 1.000000 + 16 -30.647103 1.000000 + 17 10.203611 1.000000 + 18 10.967020 1.000000 + 19 14.532886 1.000000 + 20 14.532987 1.000000 + 21 16.668995 1.000000 + 22 16.669002 1.000000 + 23 17.413224 1.000000 + 24 17.413591 1.000000 + 25 17.413900 1.000000 + 26 18.850000 1.000000 + 27 18.850124 1.000000 + 28 19.968700 0.958374 + 29 19.969266 0.957847 + 30 19.969947 0.957208 + 31 20.082043 0.710209 + 32 20.082447 0.708773 + 33 20.082779 0.707589 + 34 21.997966 0.000000 + 35 21.997988 0.000000 + 36 22.976716 0.000000 + 37 22.977326 0.000000 + 38 22.978087 0.000000 + 39 29.262086 0.000000 + 40 29.262096 0.000000 + 41 29.262190 0.000000 + 42 29.456009 0.000000 + 43 29.456271 0.000000 + 44 29.456319 0.000000 + 45 35.320363 0.000000 + 46 35.320446 0.000000 + 47 36.677678 0.000000 + 48 36.677685 0.000000 + 49 40.231928 0.000000 + 50 41.593535 0.000000 + 51 43.611156 0.000000 + 52 43.611230 0.000000 + + + +------------------------------------------------------------------------------------------ + TOTAL-FORCE (eV/Angstrom) +------------------------------------------------------------------------------------------ + Fe1 0.0000917195 -0.0005088150 -0.0021920644 + Fe2 -0.0000917195 0.0005088150 0.0021920644 +------------------------------------------------------------------------------------------ + + + Relaxation is not converged yet! +DIRECT COORDINATES + atom x y z mag vx vy vz +taud_Fe1 0.9999984177 0.0000055644 0.0000261275 2.1651 0.0000000000 0.0000000000 0.0000000000 +taud_Fe2 0.5000015823 0.4999944356 0.4999738725 2.1651 0.0000000000 0.0000000000 0.0000000000 + + + + + -------------------------------------------- + !FINAL_ETOT_IS -6825.6046926421959142 eV + -------------------------------------------- + + +TIME STATISTICS +----------------------------------------------------------------------------- + CLASS_NAME NAME TIME/s CALLS AVG/s PER/% +----------------------------------------------------------------------------- + total 38.86 19 2.05 100.00 + Driver reading 0.02 1 0.02 0.05 + Input_Conv Convert 0.00 1 0.00 0.00 + Driver driver_line 38.84 1 38.84 99.95 + UnitCell check_tau 0.00 1 0.00 0.00 + PW_Basis_Sup setuptransform 0.00 1 0.00 0.01 + PW_Basis_Sup distributeg 0.00 1 0.00 0.00 + mymath heapsort 0.00 6 0.00 0.01 + PW_Basis_K setuptransform 0.00 1 0.00 0.00 + PW_Basis_K distributeg 0.00 1 0.00 0.00 + PW_Basis setup_struc_factor 0.00 3 0.00 0.01 + ppcell_vnl init 0.00 1 0.00 0.01 + ppcell_vl init_vloc 0.01 1 0.01 0.01 + ppcell_vnl init_vnl 0.03 1 0.03 0.08 + WF_atomic init_at_1 0.17 1 0.17 0.44 + wavefunc wfcinit 0.00 1 0.00 0.00 + Ions opt_ions 38.53 1 38.53 99.14 + ESolver_KS_PW runner 38.20 3 12.73 98.28 + H_Ewald_pw compute_ewald 0.00 3 0.00 0.01 + Charge set_rho_core 0.02 3 0.01 0.06 + PW_Basis_Sup recip2real 0.22 369 0.00 0.57 + PW_Basis_Sup gathers_scatterp 0.03 369 0.00 0.08 + Charge atomic_rho 0.07 6 0.01 0.18 + Potential init_pot 0.21 3 0.07 0.54 + Potential update_from_charge 1.03 15 0.07 2.64 + Potential cal_fixed_v 0.00 3 0.00 0.01 + PotLocal cal_fixed_v 0.00 3 0.00 0.01 + Potential cal_v_eff 1.02 15 0.07 2.62 + H_Hartree_pw v_hartree 0.02 15 0.00 0.06 + PW_Basis_Sup real2recip 0.25 486 0.00 0.64 + PW_Basis_Sup gatherp_scatters 0.02 486 0.00 0.06 + PotXC cal_v_eff 0.99 15 0.07 2.54 + XC_Functional v_xc 1.18 18 0.07 3.04 + Potential interpolate_vrs 0.01 15 0.00 0.01 + OnsiteProj cubspl_tabulate 0.09 1 0.09 0.24 + Charge_Mixing init_mixing 0.00 3 0.00 0.01 + ESolver_KS_PW hamilt2density 7.41 15 0.49 19.07 + HSolverPW solve 11.45 23 0.50 29.46 + pp_cell_vnl getvnl 0.57 213 0.00 1.46 + Structure_Factor get_sk 0.06 221 0.00 0.16 + OnsiteProj getvnl 0.00 213 0.00 0.00 + OnsiteProj tabulate_atomic 0.00 239 0.00 0.00 + WF_atomic atomic_wfc 0.01 1 0.01 0.01 + DiagoIterAssist diagH_subspace_init 0.14 1 0.14 0.35 + Operator hPsi 31.99 2952 0.01 82.32 + Operator EkineticPW 0.15 2952 0.00 0.39 + Operator VeffPW 28.61 2952 0.01 73.61 + PW_Basis_K recip2real 8.33 28782 0.00 21.42 + PW_Basis_K gathers_scatterp 1.23 28782 0.00 3.16 + PW_Basis_K real2recip 7.66 27222 0.00 19.72 + PW_Basis_K gatherp_scatters 0.58 27222 0.00 1.49 + Operator NonlocalPW 2.14 2952 0.00 5.51 + Nonlocal add_nonlocal_pp 1.30 2952 0.00 3.35 + Operator OnsiteProjPW 1.09 2952 0.00 2.79 + OnsiteProj overlap 0.55 2978 0.00 1.42 + FS_Nonlocal_tools cal_becp 0.56 2984 0.00 1.43 + OnsiteProj add_onsite_proj 0.47 2952 0.00 1.20 + DiagoIterAssist diagH_LAPACK 0.23 209 0.00 0.59 + DiagoCG diag_once 8.26 23 0.36 21.24 + DiagoCG_New spsi_func 0.07 5486 0.00 0.17 + DiagoCG_New hpsi_func 7.20 2743 0.00 18.52 + ElecStatePW psiToRho 0.63 15 0.04 1.62 + Charge_Mixing get_drho 0.07 15 0.00 0.19 + Charge_Mixing inner_product_recip_rho 0.00 15 0.00 0.01 + Charge mix_rho 0.05 9 0.01 0.14 + Charge Broyden_mixing 0.01 9 0.00 0.03 + DiagoIterAssist diagH_subspace 2.30 18 0.13 5.92 + Charge_Mixing inner_product_recip_hartree 0.00 18 0.00 0.01 + OnsiteProj cal_occupation 0.00 3 0.00 0.01 + Forces cal_force_loc 0.01 3 0.00 0.02 + Forces cal_force_ew 0.00 3 0.00 0.01 + Forces cal_force_nl 0.03 3 0.01 0.07 + FS_Nonlocal_tools cal_dbecp_f 0.03 18 0.00 0.07 + Forces cal_force_onsite 0.01 3 0.00 0.04 + Forces cal_force_cc 0.22 3 0.07 0.57 + Forces cal_force_scc 0.02 3 0.01 0.06 + SpinConstrain cal_mw_from_lambda 28.58 198 0.14 73.53 + DiagoIterAssist diag_responce 23.97 190 0.13 61.69 + ModuleIO write_istate_info 0.01 1 0.01 0.02 +----------------------------------------------------------------------------- + + + NAME-------------------------|MEMORY(MB)-------- + total 24.4333 + Psi_PW 3.9943 + DiagSub::temp 3.9943 + FFT::grid 1.4238 + Chg::rho 1.4238 + Chg::rho_save 1.4238 + Pot::veff 1.4238 + Pot::veff_smooth 1.4238 + VNL::vkb 1.3826 + ------------- < 1.0 MB has been ignored ---------------- + ---------------------------------------------------------- + + Start Time : Fri Sep 6 09:29:43 2024 + Finish Time : Fri Sep 6 09:30:22 2024 + Total Time : 0 h 0 mins 39 secs diff --git a/tests/abacus.spin/OUT.ABACUS/running_scf.log b/tests/abacus.spin/OUT.ABACUS/running_scf.log new file mode 100644 index 00000000..2e6e98cc --- /dev/null +++ b/tests/abacus.spin/OUT.ABACUS/running_scf.log @@ -0,0 +1,565 @@ + + ABACUS v3.7.1 + + Atomic-orbital Based Ab-initio Computation at UStc + + Website: http://abacus.ustc.edu.cn/ + Documentation: https://abacus.deepmodeling.com/ + Repository: https://github.com/abacusmodeling/abacus-develop + https://github.com/deepmodeling/abacus-develop + Commit: 1a7a3158b (Fri Aug 23 00:52:25 2024 +0800) + + Start Time is Fri Aug 23 16:06:02 2024 + + ------------------------------------------------------------------------------------ + + READING GENERAL INFORMATION + global_out_dir = OUT.ABACUS/ + global_in_card = INPUT + pseudo_dir = ./ + orbital_dir = ./ + DRANK = 1 + DSIZE = 16 + DCOLOR = 1 + GRANK = 1 + GSIZE = 1 + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Reading atom information in unitcell: | + | From the input file and the structure file we know the number of | + | different elments in this unitcell, then we list the detail | + | information for each element, especially the zeta and polar atomic | + | orbital number for each element. The total atom number is counted. | + | We calculate the nearest atom distance for each atom and show the | + | Cartesian and Direct coordinates for each atom. We list the file | + | address for atomic orbitals. The volume and the lattice vectors | + | in real and reciprocal space is also shown. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + READING UNITCELL INFORMATION + ntype = 1 + lattice constant (Bohr) = 1.88028 + lattice constant (Angstrom) = 0.995 + + READING ATOM TYPE 1 + atom label = Fe + L=0, number of zeta = 4 + L=1, number of zeta = 2 + L=2, number of zeta = 2 + L=3, number of zeta = 1 + number of atom for this type = 2 + magnetization of element 1 = [ 0, 0, 2.4 ] + + TOTAL ATOM NUMBER = 2 +DIRECT COORDINATES + atom x y z mag vx vy vz +taud_Fe1 0.0500000000 0.1000000000 0.1500000000 2.4000 0.0000000000 0.0000000000 0.0000000000 +taud_Fe2 0.5000000000 0.5000000000 0.5000000000 2.4000 0.0000000000 0.0000000000 0.0000000000 + + + + Volume (Bohr^3) = 150.259 + Volume (A^3) = 22.266 + + Lattice vectors: (Cartesian coordinate: in unit of a_0) + +2.82743 +0 +0 + +0 +2.82743 +0 + +0 +0 +2.82743 + Reciprocal vectors: (Cartesian coordinate: in unit of 2 pi/a_0) + +0.353679 -0 +0 + -0 +0.353679 -0 + +0 -0 +0.353679 + The esolver type has been set to : ksdft_pw + + RUNNING WITH DEVICE : CPU / Intel(R) Xeon(R) Platinum 8255C CPU @ 2.50GHz + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Reading pseudopotentials files: | + | The pseudopotential file is in UPF format. The 'NC' indicates that | + | the type of pseudopotential is 'norm conserving'. Functional of | + | exchange and correlation is decided by 4 given parameters in UPF | + | file. We also read in the 'core correction' if there exists. | + | Also we can read the valence electrons number and the maximal | + | angular momentum used in this pseudopotential. We also read in the | + | trail wave function, trail atomic density and local-pseudopotential| + | on logrithmic grid. The non-local pseudopotential projector is also| + | read in if there is any. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + PAO radial cut off (Bohr) = 15 + + Read in pseudopotential file is Fe.upf + pseudopotential type = NC + exchange-correlation functional = PBE + nonlocal core correction = 1 + valence electrons = 16 + lmax = 2 + number of zeta = 6 + number of projectors = 10 + L of projector = 0 + L of projector = 0 + L of projector = 1 + L of projector = 1 + L of projector = 1 + L of projector = 1 + L of projector = 2 + L of projector = 2 + L of projector = 2 + L of projector = 2 + initial pseudo atomic orbital number = 40 + NLOCAL = 108 + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + Warning: the number of valence electrons in pseudopotential > 8 for Fe: [Ar] 3d6 4s2 + Pseudopotentials with additional electrons can yield (more) accurate outcomes, but may be less efficient. + If you're confident that your chosen pseudopotential is appropriate, you can safely ignore this warning. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup plane waves of charge/potential: | + | Use the energy cutoff and the lattice vectors to generate the | + | dimensions of FFT grid. The number of FFT grid on each processor | + | is 'nrxx'. The number of plane wave basis in reciprocal space is | + | different for charege/potential and wave functions. We also set | + | the 'sticks' for the parallel of FFT. The number of plane waves | + | is 'npw' in each processor. | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP THE PLANE WAVE BASIS + energy cutoff for charge/potential (unit:Ry) = 320 + fft grid for charge/potential = [ 32, 32, 32 ] + fft grid division = [ 1, 1, 1 ] + big fft grid for charge/potential = [ 32, 32, 32 ] + nbxx = 2048 + nrxx = 2048 + + SETUP PLANE WAVES FOR CHARGE/POTENTIAL + number of plane waves = 14531 + number of sticks = 725 + + PARALLEL PW FOR CHARGE/POTENTIAL + PROC COLUMNS(POT) PW + 1 45 909 + 2 45 909 + 3 46 908 + 4 46 908 + 5 45 909 + 6 45 909 + 7 45 909 + 8 45 909 + 9 45 909 + 10 46 908 + 11 46 908 + 12 46 908 + 13 45 907 + 14 45 907 + 15 45 907 + 16 45 907 + --------------- sum ------------------- + 16 725 14531 + number of |g| = 165 + max |g| = 28.6453 + min |g| = 0.500354 + +----------- Double Check Mixing Parameters Begin ------------ +mixing_type: broyden +mixing_beta: 0.4 +mixing_gg0: 1 +mixing_gg0_min: 0.1 +mixing_beta_mag: 1.6 +mixing_gg0_mag: 0 +mixing_ndim: 10 +----------- Double Check Mixing Parameters End ------------ + + SETUP THE ELECTRONS NUMBER + electron number of element Fe = 16 + total electron number of element Fe = 32 + AUTOSET number of electrons: = 32 + DONE : SETUP UNITCELL Time : 0.316237 (SEC) + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup K-points | + | We setup the k-points according to input parameters. | + | The reduced k-points are set according to symmetry operations. | + | We treat the spin as another set of k-points. | + | | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP K-POINTS + nspin = 4 + Input type of k points = Monkhorst-Pack(Gamma) + nkstot = 216 +K-POINTS DIRECT COORDINATES + KPOINTS DIRECT_X DIRECT_Y DIRECT_Z WEIGHT + + DONE : INIT K-POINTS Time : 0.376998 (SEC) + + + + + + >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> + | | + | Setup plane waves of wave functions: | + | Use the energy cutoff and the lattice vectors to generate the | + | dimensions of FFT grid. The number of FFT grid on each processor | + | is 'nrxx'. The number of plane wave basis in reciprocal space is | + | different for charege/potential and wave functions. We also set | + | the 'sticks' for the parallel of FFT. The number of plane wave of | + | each k-point is 'npwk[ik]' in each processor | + <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + + + + + SETUP PLANE WAVES FOR WAVE FUNCTIONS + energy cutoff for wavefunc (unit:Ry) = 80 + fft grid for wave functions = [ 32, 32, 32 ] + number of plane waves = 3071 + number of sticks = 253 + + PARALLEL PW FOR WAVE FUNCTIONS + PROC COLUMNS(POT) PW + 1 16 192 + 2 16 192 + 3 16 192 + 4 16 192 + 5 15 191 + 6 15 191 + 7 16 194 + 8 16 192 + 9 16 192 + 10 16 192 + 11 16 192 + 12 16 192 + 13 16 192 + 14 16 192 + 15 16 192 + 16 15 191 + --------------- sum ------------------- + 16 253 3071 + DONE : INIT PLANEWAVE Time : 0.380345 (SEC) + + occupied bands = 32 + NBANDS = 52 + + SETUP NONLOCAL PSEUDOPOTENTIALS IN PLANE WAVE BASIS + Fe non-local projectors: + projector 1 L=0 + projector 2 L=0 + projector 3 L=1 + projector 4 L=1 + projector 5 L=1 + projector 6 L=1 + projector 7 L=2 + projector 8 L=2 + projector 9 L=2 + projector 10 L=2 + TOTAL NUMBER OF NONLOCAL PROJECTORS = 68 + DONE : LOCAL POTENTIAL Time : 0.40963 (SEC) + + + Init Non-Local PseudoPotential table : + Init Non-Local-Pseudopotential done. + DONE : NON-LOCAL POTENTIAL Time : 0.443773 (SEC) + + npwx = 128 + + Warning_Memory_Consuming allocated: Psi_PW 43.875 MB + + Make real space PAO into reciprocal space. + max mesh points in Pseudopotential = 1425 + dq(describe PAO in reciprocal space) = 0.01 + max q = 1078 + + number of pseudo atomic orbitals for Fe is 6 + DONE : INIT BASIS Time : 0.620862 (SEC) + + + ------------------------------------------- + SELF-CONSISTENT + ------------------------------------------- + init_chg = atomic + DONE : INIT SCF Time : 0.761759 (SEC) + + + PW ALGORITHM --------------- ION= 1 ELEC= 1-------------------------------- +total magnetism (Bohr mag/cell) -8.56154e-05 -3.16838e-05 3.87672 + absolute magnetism (Bohr mag/cell) = 3.90219 + Notice: Threshold on eigenvalues was too large. + hsover_error=0.32 > DRHO=0.0810864 + Origin diag_ethr = 0.01 + New diag_ethr = 0.000253395 +total magnetism (Bohr mag/cell) -9.09654e-05 -3.00029e-05 3.87658 + absolute magnetism (Bohr mag/cell) = 3.90286 + + Density error is 0.0818013187196 + Error Threshold = 0.000253394954616 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.1638820263 -6818.6844273569 + E_Harris -501.1650591987 -6818.7004436094 + E_Fermi 1.4203911993 19.3254136999 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 2-------------------------------- +total magnetism (Bohr mag/cell) -2.92622585913e-05 -0.000266812474323 3.65800660509 + absolute magnetism (Bohr mag/cell) = 3.88387266047 + + Density error is 0.0165562104235 + Error Threshold = 0.000255629120999 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.1785311471 -6818.8837388713 + E_Harris -501.4729927600 -6822.8900946492 + E_Fermi 1.4123735299 19.2163277107 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 3-------------------------------- +total magnetism (Bohr mag/cell) -1.41288005953e-05 -0.000388528787066 3.72548472333 + absolute magnetism (Bohr mag/cell) = 3.91023806289 + + Density error is 0.00318633115585 + Error Threshold = 5.17381575736e-05 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.1785047719 -6818.8833800180 + E_Harris -501.0742053019 -6817.4643129270 + E_Fermi 1.4117399370 19.2077072372 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 4-------------------------------- +total magnetism (Bohr mag/cell) 2.26526732328e-05 -0.000450436470697 3.73775099313 + absolute magnetism (Bohr mag/cell) = 3.92308219614 + + Density error is 0.000165938860835 + Error Threshold = 9.95728486202e-06 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.1804562269 -6818.9099309251 + E_Harris -501.0912756169 -6817.6965664782 + E_Fermi 1.4131895976 19.2274308815 +---------------------------------------------------------- + + + PW ALGORITHM --------------- ION= 1 ELEC= 5-------------------------------- + +total magnetism (Bohr mag/cell) -0.000682480084963 -0.000407948602903 4.47635324671 + absolute magnetism (Bohr mag/cell) = 4.82248432181 + + Density error is 6.61858307885e-09 + Error Threshold = 5.40730613116e-10 +---------------------------------------------------------- + Energy Rydberg eV +---------------------------------------------------------- + E_KohnSham -501.1664531630 -6818.7194094666 + E_KS(sigma->0) -501.1658847659 -6818.7116760274 + E_Harris -501.1680065331 -6818.7405441510 + E_band -30.1701507306 -410.4859594555 + E_one_elec -214.9321986489 -2924.3025852934 + E_Hartree 128.2299640744 1744.6581657467 + E_xc -71.3503189041 -970.7708912124 + E_Ewald -343.2422281761 -4670.0500974110 + E_entropy(-TS) -0.0011367942 -0.0154668785 + E_descf 0.0000000000 0.0000000000 + E_exx 0.0000000000 0.0000000000 + E_Fermi 1.4108073605 19.1950188828 +---------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Total Magnetism (uB) +------------------------------------------------------------------------------------------- + Fe -0.0000002724 -0.0000001728 2.4000001004 + Fe -0.0000003180 -0.0000002299 2.3999994597 +------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------- + Magnetic force (eV/uB) +------------------------------------------------------------------------------------------- + Fe -0.0000175013 -0.0000418680 -0.3669618965 + Fe -0.0000161517 -0.0000195198 -0.3669821632 +------------------------------------------------------------------------------------------- + +Charge and Mag of Atom 0 + Orbital 1 Charge: 1.99985926204 Mag: -8.50835272933e-09 -8.84955876115e-09 2.84869655853e-06 + Orbital 2 Charge: 5.99701367096 Mag: -2.18088393737e-07 -2.40618119919e-07 0.000105269162551 + Orbital 3 Charge: 6.37379729477 Mag: -4.581340875e-08 7.66548679323e-08 2.39989198254 +Sum of atom 0 is: 14.3706702278 -2.72410155216e-07 -1.72812810748e-07 2.4000001004 +Charge and Mag of Atom 1 + Orbital 1 Charge: 1.99985925962 Mag: -6.91586723071e-09 -8.93979594505e-09 2.85090960195e-06 + Orbital 2 Charge: 5.99701367924 Mag: -2.16539560943e-07 -2.55979271358e-07 0.000105265428294 + Orbital 3 Charge: 6.37381969542 Mag: -9.45758719447e-08 3.50118887944e-08 2.3998913434 +Sum of atom 1 is: 14.3706926343 -3.18031300118e-07 -2.29907178509e-07 2.39999945974 + + charge density convergence is achieved + final etot is -6818.7194095 eV + EFERMI = 19.195018883 eV + +------------------------------------------------------------------------------------------ + TOTAL-FORCE (eV/Angstrom) +------------------------------------------------------------------------------------------ + Fe1 -0.0000234480 0.0000848730 0.0003959639 + Fe2 0.0000234480 -0.0000848730 -0.0003959639 +------------------------------------------------------------------------------------------ + + -------------------------------------------- + !FINAL_ETOT_IS -6818.719409466637 eV + -------------------------------------------- + + +TIME STATISTICS +---------------------------------------------------------------------------- + CLASS_NAME NAME TIME/s CALLS AVG/s PER/% +---------------------------------------------------------------------------- + total 563.56 15 37.57 100.00 + Driver reading 0.11 1 0.11 0.02 + Input_Conv Convert 0.00 1 0.00 0.00 + Driver driver_line 563.45 1 563.45 99.98 + UnitCell check_tau 0.00 1 0.00 0.00 + PW_Basis_Sup setuptransform 0.06 1 0.06 0.01 + PW_Basis_Sup distributeg 0.00 1 0.00 0.00 + mymath heapsort 0.00 3 0.00 0.00 + PW_Basis_K setuptransform 0.00 1 0.00 0.00 + PW_Basis_K distributeg 0.00 1 0.00 0.00 + PW_Basis setup_struc_factor 0.00 1 0.00 0.00 + ppcell_vnl init 0.00 1 0.00 0.00 + ppcell_vl init_vloc 0.02 1 0.02 0.00 + ppcell_vnl init_vnl 0.03 1 0.03 0.01 + WF_atomic init_at_1 0.18 1 0.18 0.03 + wavefunc wfcinit 0.00 1 0.00 0.00 + Ions opt_ions 562.87 1 562.87 99.88 + ESolver_KS_PW runner 562.87 1 562.87 99.88 + H_Ewald_pw compute_ewald 0.00 1 0.00 0.00 + Charge set_rho_core 0.01 1 0.01 0.00 + PW_Basis_Sup recip2real 0.03 202 0.00 0.00 + PW_Basis_Sup gathers_scatterp 0.01 202 0.00 0.00 + Charge atomic_rho 0.00 1 0.00 0.00 + Potential init_pot 0.01 1 0.01 0.00 + Potential update_from_charge 0.07 13 0.01 0.01 + Potential cal_fixed_v 0.00 1 0.00 0.00 + PotLocal cal_fixed_v 0.00 1 0.00 0.00 + Potential cal_v_eff 0.06 13 0.00 0.01 + H_Hartree_pw v_hartree 0.00 13 0.00 0.00 + PW_Basis_Sup real2recip 0.02 247 0.00 0.00 + PW_Basis_Sup gatherp_scatters 0.02 247 0.00 0.00 + PotXC cal_v_eff 0.06 13 0.00 0.01 + XC_Functional v_xc 0.06 13 0.00 0.01 + Potential interpolate_vrs 0.00 13 0.00 0.00 + OnsiteProj init_k_stage0 0.08 1 0.08 0.01 + Charge_Mixing init_mixing 0.00 2 0.00 0.00 + ESolver_KS_PW hamilt2density 184.77 13 14.21 32.79 + HSolverPW solve 287.77 21 13.70 51.06 + pp_cell_vnl getvnl 4.21 21384 0.00 0.75 + Structure_Factor get_sk 2.26 492264 0.00 0.40 + OnsiteProj getvnl 3.45 21384 0.00 0.61 + OnsiteProj init_k 4.11 26136 0.00 0.73 + OnsiteProj init_k_stage1 1.73 26136 0.00 0.31 + OnsiteProj init_k_stage2 2.37 26136 0.00 0.42 + WF_atomic atomic_wfc 0.10 216 0.00 0.02 + DiagoDavid diag_mock 259.79 4536 0.06 46.10 + DiagoDavid first 83.93 4536 0.02 14.89 + David spsi_func 1.10 1075598 0.00 0.20 + DiagoDavid SchmidtOrth 14.54 537799 0.00 2.58 + David hpsi_func 149.60 14979 0.01 26.55 + Operator hPsi 396.41 31827 0.01 70.34 + Operator EkineticPW 2.03 31827 0.00 0.36 + Operator VeffPW 344.42 31827 0.01 61.12 + PW_Basis_K recip2real 183.20 3119822 0.00 32.51 + PW_Basis_K gathers_scatterp 160.47 3119822 0.00 28.47 + PW_Basis_K real2recip 155.75 2827790 0.00 27.64 + PW_Basis_K gatherp_scatters 136.28 2827790 0.00 24.18 + Operator NonlocalPW 42.43 31827 0.00 7.53 + Nonlocal add_nonlocal_pp 34.57 31827 0.00 6.13 + Operator OnsiteProjPW 7.38 31827 0.00 1.31 + OnsiteProj add_onsite_proj 7.35 31827 0.00 1.30 + OnsiteProj overlap 5.91 36579 0.00 1.05 + DiagoDavid cal_elem 35.38 14979 0.00 6.28 + DiagoDavid diag_zhegvx 51.18 14979 0.00 9.08 + DiagoDavid cal_grad 101.08 10443 0.01 17.94 + DiagoDavid check_update 0.01 10443 0.00 0.00 + DiagoDavid last 1.75 4536 0.00 0.31 + ElecStatePW psiToRho 18.60 13 1.43 3.30 + Charge_Mixing get_drho 0.01 13 0.00 0.00 + Charge_Mixing inner_product_recip_rho 0.00 13 0.00 0.00 + Charge mix_rho 0.01 10 0.00 0.00 + Charge Broyden_mixing 0.01 10 0.00 0.00 + Charge_Mixing inner_product_recip_hartree 0.00 48 0.00 0.00 + SpinConstrain cal_mw_from_lambda 377.05 86 4.38 66.90 + DiagoIterAssist diag_responce 266.63 16848 0.02 47.31 + DiagoIterAssist diagH_LAPACK 12.16 16848 0.00 2.16 + OnsiteProj cal_occupation 0.06 1 0.06 0.01 + ModuleIO write_istate_info 0.07 1 0.07 0.01 +---------------------------------------------------------------------------- + + + NAME-------------------------|MEMORY(MB)-------- + total 786.3 + Psi_PW 668.8 + DAV::basis 12.38 + DAV::hpsi 12.38 + DAV::spsi 12.38 + DAV::hcc 10.56 + DAV::scc 10.56 + DAV::vcc 10.56 + MTransOp 10.56 + PW_B_K::gcar 9.645 + PW_B_K::gk2 3.215 + DiagSub::temp 3.096 + VNL::tab 2.632 + VNL::vkb 2.024 + Nonlocal::becp 1.727 + Nonlocal::ps 1.727 + VNL::tab_at 1.579 + FFT::grid 1.000 + Chg::rho 1.000 + Chg::rho_save 1.000 + Pot::veff 1.000 + Pot::veff_smooth 1.000 + ------------- < 1.0 MB has been ignored ---------------- + ---------------------------------------------------------- + + Start Time : Fri Aug 23 16:06:02 2024 + Finish Time : Fri Aug 23 16:15:04 2024 + Total Time : 0 h 9 mins 2 secs diff --git a/tests/abacus.spin/STRU b/tests/abacus.spin/STRU new file mode 100644 index 00000000..6ec77f1a --- /dev/null +++ b/tests/abacus.spin/STRU @@ -0,0 +1,21 @@ +ATOMIC_SPECIES +Fe 55.845 Fe.upf + +NUMERICAL_ORBITAL +Fe_gga_10au_200.0Ry_4s2p2d1f.orb + +LATTICE_CONSTANT +1.880277359 +LATTICE_VECTORS + 2.8274254848 0.0000000000 0.0000000000 #latvec1 + 0.0000000000 2.8274254848 0.0000000000 #latvec2 + 0.0000000000 0.0000000000 2.8274254848 #latvec3 + +ATOMIC_POSITIONS +Direct + +Fe #label +1 #magnetism +2 #number of atoms + 0.0500000000 0.100000000 0.1500000000 mag 0 0 2.4 sc 1 1 1 + 0.5000000000 0.5000000000 0.5000000000 mag 0 0 2.4 sc 1 1 1 diff --git a/tests/test_abacus_spin.py b/tests/test_abacus_spin.py new file mode 100644 index 00000000..df8ef7d0 --- /dev/null +++ b/tests/test_abacus_spin.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +import os +import shutil +import unittest + +import numpy as np +from context import dpdata + + +class TestABACUSSpin(unittest.TestCase): + def setUp(self): + self.dump_path = "abacus.spin/dump" + os.makedirs(self.dump_path, exist_ok=True) + + def tearDown(self): + if os.path.isdir(self.dump_path): + shutil.rmtree(self.dump_path) + + def test_scf(self): + os.system("cp abacus.spin/INPUT.scf abacus.spin/INPUT") + mysys = dpdata.LabeledSystem("abacus.spin", fmt="abacus/scf") + data = mysys.data + self.assertAlmostEqual(data["energies"][0], -6818.719409466637) + np.testing.assert_almost_equal( + data["spins"][0], + [ + [-0.0000002724, -0.0000001728, 2.4000001004], + [-0.0000003180, -0.0000002299, 2.3999994597], + ], + decimal=8, + ) + np.testing.assert_almost_equal( + data["mag_forces"][0], + [ + [-0.0000175013, -0.0000418680, -0.3669618965], + [-0.0000161517, -0.0000195198, -0.3669821632], + ], + decimal=8, + ) + + # dump to deepmd-npy + mysys.to(file_name=self.dump_path, fmt="deepmd/npy") + self.assertTrue(os.path.isfile(f"{self.dump_path}/set.000/spin.npy")) + self.assertTrue(os.path.isfile(f"{self.dump_path}/set.000/force_mag.npy")) + + sys2 = dpdata.LabeledSystem(self.dump_path, fmt="deepmd/npy") + np.testing.assert_almost_equal(data["spins"], sys2.data["spins"], decimal=8) + np.testing.assert_almost_equal( + data["mag_forces"], sys2.data["mag_forces"], decimal=8 + ) + + def test_relax(self): + os.system("cp abacus.spin/INPUT.relax abacus.spin/INPUT") + mysys = dpdata.LabeledSystem("abacus.spin", fmt="abacus/relax") + data = mysys.data + spins_ref = np.array( + [ + [ + [1.16909819, 1.16895965, 1.16895485], + [1.16827825, 1.16832716, 1.16836899], + ], + [ + [1.25007143, 1.25006167, 1.25004587], + [1.25015764, 1.2501678, 1.25018344], + ], + [ + [1.24984994, 1.24977108, 1.24978313], + [1.24996533, 1.2500441, 1.25003208], + ], + ] + ) + magforces_ref = np.array( + [ + [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], + [ + [-0.16734626, -0.16735378, -0.16735617], + [-0.16836467, -0.16835897, -0.16835625], + ], + [ + [-0.16573406, -0.16574627, -0.1657445], + [-0.16619489, -0.16617948, -0.16618272], + ], + ] + ) + self.assertEqual(len(data["spins"]), 3) + self.assertEqual(len(data["mag_forces"]), 3) + np.testing.assert_almost_equal(data["spins"], spins_ref, decimal=8) + np.testing.assert_almost_equal(data["mag_forces"], magforces_ref, decimal=8) + + # dump to deepmd-npy + mysys.to(file_name=self.dump_path, fmt="deepmd/npy") + self.assertTrue(os.path.isfile(f"{self.dump_path}/set.000/spin.npy")) + self.assertTrue(os.path.isfile(f"{self.dump_path}/set.000/force_mag.npy")) + + sys2 = dpdata.LabeledSystem(self.dump_path, fmt="deepmd/npy") + np.testing.assert_almost_equal(data["spins"], sys2.data["spins"], decimal=8) + np.testing.assert_almost_equal( + data["mag_forces"], sys2.data["mag_forces"], decimal=8 + ) + + def test_md(self): + os.system("cp abacus.spin/INPUT.md abacus.spin/INPUT") + mysys = dpdata.LabeledSystem("abacus.spin", fmt="abacus/md") + data = mysys.data + spins_ref = np.array( + [ + [ + [1.16909819, 1.16895965, 1.16895485], + [1.16827825, 1.16832716, 1.16836899], + ], + [ + [1.2500362, 1.25007501, 1.2500655], + [1.25019078, 1.25015253, 1.25016188], + ], + [ + [1.24985138, 1.24976901, 1.2497695], + [1.24996388, 1.25004618, 1.25004561], + ], + [ + [1.24982513, 1.24985445, 1.24985336], + [1.25005073, 1.25001814, 1.25002065], + ], + ] + ) + magforces_ref = np.array( + [ + [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]], + [ + [-0.16747275, -0.16747145, -0.16746776], + [-0.16853881, -0.16853935, -0.16854119], + ], + [ + [-0.16521817, -0.16523256, -0.16523212], + [-0.16549418, -0.16547867, -0.16547913], + ], + [ + [-0.16141172, -0.16140644, -0.1614127], + [-0.15901519, -0.15905932, -0.15904824], + ], + ] + ) + self.assertEqual(len(data["spins"]), 4) + self.assertEqual(len(data["mag_forces"]), 4) + np.testing.assert_almost_equal(data["spins"], spins_ref, decimal=8) + np.testing.assert_almost_equal(data["mag_forces"], magforces_ref, decimal=8) + + # dump to deepmd-npy + mysys.to(file_name=self.dump_path, fmt="deepmd/npy") + self.assertTrue(os.path.isfile(f"{self.dump_path}/set.000/spin.npy")) + self.assertTrue(os.path.isfile(f"{self.dump_path}/set.000/force_mag.npy")) + + sys2 = dpdata.LabeledSystem(self.dump_path, fmt="deepmd/npy") + np.testing.assert_almost_equal(data["spins"], sys2.data["spins"], decimal=8) + np.testing.assert_almost_equal( + data["mag_forces"], sys2.data["mag_forces"], decimal=8 + ) diff --git a/tests/test_abacus_stru_dump.py b/tests/test_abacus_stru_dump.py index 356aa57f..084a5473 100644 --- a/tests/test_abacus_stru_dump.py +++ b/tests/test_abacus_stru_dump.py @@ -1,16 +1,23 @@ from __future__ import annotations import os +import shutil import unittest from context import dpdata from test_vasp_poscar_dump import myfilecmp +from dpdata.abacus.scf import parse_stru_pos + class TestStruDump(unittest.TestCase): def setUp(self): self.system_ch4 = dpdata.System("abacus.scf/STRU.ch4", fmt="stru") + def tearDown(self): + if os.path.isfile("STRU_tmp"): + os.remove("STRU_tmp") + def test_dump_stru(self): self.system_ch4.to( "stru", @@ -22,9 +29,157 @@ def test_dump_stru(self): ) myfilecmp(self, "abacus.scf/stru_test", "STRU_tmp") - def tearDown(self): - if os.path.isfile("STRU_tmp"): - os.remove("STRU_tmp") + def test_dumpStruLinkFile(self): + os.makedirs("abacus.scf/tmp", exist_ok=True) + self.system_ch4.to( + "stru", + "abacus.scf/tmp/STRU_tmp", + mass=[12, 1], + pp_file=["abacus.scf/C.upf", "abacus.scf/H.upf"], + numerical_orbital=["abacus.scf/C.orb", "abacus.scf/H.orb"], + numerical_descriptor="abacus.scf/jle.orb", + link_file=True, + ) + myfilecmp(self, "abacus.scf/stru_test", "abacus.scf/tmp/STRU_tmp") + + self.assertTrue(os.path.islink("abacus.scf/tmp/C.upf")) + self.assertTrue(os.path.islink("abacus.scf/tmp/H.upf")) + self.assertTrue(os.path.islink("abacus.scf/tmp/C.orb")) + self.assertTrue(os.path.islink("abacus.scf/tmp/H.orb")) + self.assertTrue(os.path.islink("abacus.scf/tmp/jle.orb")) + + if os.path.isdir("abacus.scf/tmp"): + shutil.rmtree("abacus.scf/tmp") + + def test_dump_spinconstrain(self): + self.system_ch4.to( + "stru", + "STRU_tmp", + mass=[12, 1], + pp_file={"C": "C.upf", "H": "H.upf"}, + numerical_orbital={"C": "C.orb", "H": "H.orb"}, + mag=[4, [1, 1, 1], 1, 1, 1], + sc=[True, True, [True, False, True], False, True], + move=[1, 1, 1, 0, 0], + angle1=[None, None, 100, 90, 80], + angle2=[100, 90, 80, 70, None], + lambda_=[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6], [0.7, 0.8, 0.9], None, None], + ) + + assert os.path.isfile("STRU_tmp") + with open("STRU_tmp") as f: + c = f.read() + + with open("abacus.scf/stru.ref") as f: + stru_ref = f.read() + assert c == stru_ref + + def test_dump_spin(self): + sys_tmp = dpdata.System("abacus.scf/stru.ref", fmt="stru") + sys_tmp.data["spins"] = [ + [[1, 2, 3], [4, 5, 6], [1, 1, 1], [2, 2, 2], [3, 3, 3]] + ] + sys_tmp.to( + "stru", + "STRU_tmp", + mass=[12, 1], + pp_file=["C.upf", "H.upf"], + numerical_orbital=["C.orb", "H.orb"], + ) + assert os.path.isfile("STRU_tmp") + with open("STRU_tmp") as f: + c = f.read() + stru_ref = """C +0.0 +1 +5.192682633809 4.557725978258 4.436846615358 1 1 1 mag 1.000000000000 2.000000000000 3.000000000000 +H +0.0 +4 +5.416431453540 4.011298860305 3.511161492417 1 1 1 mag 4.000000000000 5.000000000000 6.000000000000 +4.131588222365 4.706745191323 4.431136645083 1 1 1 mag 1.000000000000 1.000000000000 1.000000000000 +5.630930319126 5.521640894956 4.450356541303 1 1 1 mag 2.000000000000 2.000000000000 2.000000000000 +5.499851012568 4.003388899277 5.342621842622 1 1 1 mag 3.000000000000 3.000000000000 3.000000000000 +""" + self.assertTrue(stru_ref in c) + + +class TestABACUSParseStru(unittest.TestCase): + def test_parse_stru_post(self): + pos, move, velocity, magmom, angle1, angle2, constrain, lambda1 = ( + parse_stru_pos( + "1.0 2.0 3.0 1 1 1 mag 1.0 2.0 3.0 v 1 1 1 angle1 100 angle2 90 sc 1 0 1 lambda 0.1 0.2 0.3" + ) + ) + self.assertEqual(pos, [1.0, 2.0, 3.0]) + self.assertEqual(move, [1, 1, 1]) + self.assertEqual(velocity, [1.0, 1.0, 1.0]) + self.assertEqual(magmom, [1.0, 2.0, 3.0]) + self.assertEqual(angle1, 100) + self.assertEqual(angle2, 90) + self.assertEqual(constrain, [1, 0, 1]) + self.assertEqual(lambda1, [0.1, 0.2, 0.3]) + + pos, move, velocity, magmom, angle1, angle2, constrain, lambda1 = ( + parse_stru_pos("1 2 3 mag 1 sc 1 lambda 0.1") + ) + self.assertEqual(pos, [1, 2, 3]) + self.assertEqual(move, None) + self.assertEqual(velocity, None) + self.assertEqual(magmom, 1.0) + self.assertEqual(angle1, None) + self.assertEqual(angle2, None) + self.assertEqual(constrain, 1) + self.assertEqual(lambda1, 0.1) + + def test_parse_stru_error(self): + line = "1.0 2.0 3.0 1 1" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 mag 1.0 3.0 v 1 1 1" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 mag 1 2 3 4" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 v 1" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 v 1 1" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 v 1 1 1 1" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 1" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 angle1 " + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 angle1 1 2" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 1 1 1 angle2" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 angle2 1 2" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 sc" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 sc 1 2" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 lambda" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 lambda 1 2" + self.assertRaises(RuntimeError, parse_stru_pos, line), line + + line = "1.0 2.0 3.0 lambda 1 2 3 4" + self.assertRaises(RuntimeError, parse_stru_pos, line), line if __name__ == "__main__": From b797acb23214f771e4eb66ff6ae1954e027eed0e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 08:51:26 +0800 Subject: [PATCH 23/33] [pre-commit.ci] pre-commit autoupdate (#731) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.5 → v0.6.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.5...v0.6.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8da028de..d921a886 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.5 + rev: v0.6.7 hooks: - id: ruff args: ["--fix"] From a6ced9fb3f7ec8ba8648363b9a90d4abacd504a3 Mon Sep 17 00:00:00 2001 From: Peng Xingliang <91927439+pxlxingliang@users.noreply.github.com> Date: Tue, 15 Oct 2024 08:08:24 +0800 Subject: [PATCH 24/33] abacus: add checks on pp and orb in construction of STRU (#737) ## Summary by CodeRabbit - **New Features** - Introduced a mandatory parameter for pseudopotential files and an optional parameter to specify the destination directory for symbolic links. - **Improvements** - Enhanced clarity and functionality in handling input parameters for pseudopotential and orbital files. - Streamlined output construction for `pp_file` and `numerical_orbital`. - **Bug Fixes** - Added tests to validate error handling for mismatches between provided pseudopotential files and numerical orbitals, ensuring appropriate exceptions are raised. - **Documentation** - Added comments for better understanding of code functionality. --------- Co-authored-by: root Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dpdata/abacus/scf.py | 90 +++++++++++++++++----------------- tests/test_abacus_stru_dump.py | 43 ++++++++++++++++ 2 files changed, 87 insertions(+), 46 deletions(-) diff --git a/dpdata/abacus/scf.py b/dpdata/abacus/scf.py index 9919e912..b1b2cfed 100644 --- a/dpdata/abacus/scf.py +++ b/dpdata/abacus/scf.py @@ -578,7 +578,7 @@ def get_frame_from_stru(fname): def make_unlabeled_stru( data, frame_idx, - pp_file=None, + pp_file, numerical_orbital=None, numerical_descriptor=None, mass=None, @@ -601,7 +601,7 @@ def make_unlabeled_stru( System data frame_idx : int The index of the frame to dump - pp_file : list of string or dict, optional + pp_file : list of string or dict List of pseudo potential files, or a dictionary of pseudo potential files for each atomnames numerical_orbital : list of string or dict, optional List of orbital files, or a dictionary of orbital files for each atomnames @@ -628,6 +628,8 @@ def make_unlabeled_stru( link_file : bool, optional Whether to link the pseudo potential files and orbital files in the STRU file. If True, then only filename will be written in the STRU file, and make a soft link to the real file. + dest_dir : str, optional + The destination directory to make the soft link of the pseudo potential files and orbital files. For velocity, mag, angle1, angle2, sc, and lambda_, if the value is None, then the corresponding information will not be written. ABACUS support defining "mag" and "angle1"/"angle2" at the same time, and in this case, the "mag" only define the norm of the magnetic moment, and "angle1" and "angle2" define the direction of the magnetic moment. If data has spins, then it will be written as mag to STRU file; while if mag is passed at the same time, then mag will be used. @@ -655,6 +657,23 @@ def ndarray2list(i): else: return i + def process_file_input(file_input, atom_names, input_name): + # For pp_file and numerical_orbital, process the file input, and return a list of file names + # file_input can be a list of file names, or a dictionary of file names for each atom names + if isinstance(file_input, (list, tuple)): + if len(file_input) != len(atom_names): + raise ValueError( + f"{input_name} length is not equal to the number of atom types" + ) + return file_input + elif isinstance(file_input, dict): + for element in atom_names: + if element not in file_input: + raise KeyError(f"{input_name} does not contain {element}") + return [file_input[element] for element in atom_names] + else: + raise ValueError(f"Invalid {input_name}: {file_input}") + if link_file and dest_dir is None: print( "WARNING: make_unlabeled_stru: link_file is True, but dest_dir is None. Will write the filename to STRU but not making soft link." @@ -680,8 +699,8 @@ def ndarray2list(i): # ATOMIC_SPECIES block out = "ATOMIC_SPECIES\n" - if pp_file is not None: - pp_file = ndarray2list(pp_file) + ppfiles = process_file_input(ndarray2list(pp_file), data["atom_names"], "pp_file") + for iele in range(len(data["atom_names"])): if data["atom_numbs"][iele] == 0: continue @@ -690,57 +709,36 @@ def ndarray2list(i): out += f"{mass[iele]:.3f} " else: out += "1 " - if pp_file is not None: - if isinstance(pp_file, (list, tuple)): - ipp_file = pp_file[iele] - elif isinstance(pp_file, dict): - if data["atom_names"][iele] not in pp_file: - print( - f"ERROR: make_unlabeled_stru: pp_file does not contain {data['atom_names'][iele]}" - ) - ipp_file = None - else: - ipp_file = pp_file[data["atom_names"][iele]] - else: - ipp_file = None - if ipp_file is not None: - if not link_file: - out += ipp_file - else: - out += os.path.basename(ipp_file.rstrip("/")) - if dest_dir is not None: - _link_file(dest_dir, ipp_file) + ipp_file = ppfiles[iele] + if not link_file: + out += ipp_file + else: + out += os.path.basename(ipp_file.rstrip("/")) + if dest_dir is not None: + _link_file(dest_dir, ipp_file) out += "\n" out += "\n" # NUMERICAL_ORBITAL block if numerical_orbital is not None: - assert len(numerical_orbital) == len(data["atom_names"]) numerical_orbital = ndarray2list(numerical_orbital) + orbfiles = process_file_input( + numerical_orbital, data["atom_names"], "numerical_orbital" + ) + orbfiles = [ + orbfiles[i] + for i in range(len(data["atom_names"])) + if data["atom_numbs"][i] != 0 + ] out += "NUMERICAL_ORBITAL\n" - for iele in range(len(data["atom_names"])): - if data["atom_numbs"][iele] == 0: - continue - if isinstance(numerical_orbital, (list, tuple)): - inum_orbital = numerical_orbital[iele] - elif isinstance(numerical_orbital, dict): - if data["atom_names"][iele] not in numerical_orbital: - print( - f"ERROR: make_unlabeled_stru: numerical_orbital does not contain {data['atom_names'][iele]}" - ) - inum_orbital = None - else: - inum_orbital = numerical_orbital[data["atom_names"][iele]] + for iorb in orbfiles: + if not link_file: + out += iorb else: - inum_orbital = None - if inum_orbital is not None: - if not link_file: - out += inum_orbital - else: - out += os.path.basename(inum_orbital.rstrip("/")) - if dest_dir is not None: - _link_file(dest_dir, inum_orbital) + out += os.path.basename(iorb.rstrip("/")) + if dest_dir is not None: + _link_file(dest_dir, iorb) out += "\n" out += "\n" diff --git a/tests/test_abacus_stru_dump.py b/tests/test_abacus_stru_dump.py index 084a5473..4549b6d1 100644 --- a/tests/test_abacus_stru_dump.py +++ b/tests/test_abacus_stru_dump.py @@ -51,6 +51,49 @@ def test_dumpStruLinkFile(self): if os.path.isdir("abacus.scf/tmp"): shutil.rmtree("abacus.scf/tmp") + def test_dump_stru_pporb_mismatch(self): + with self.assertRaises(KeyError, msg="pp_file is a dict and lack of pp for H"): + self.system_ch4.to( + "stru", + "STRU_tmp", + mass=[12, 1], + pp_file={"C": "C.upf", "O": "O.upf"}, + numerical_orbital={"C": "C.orb", "H": "H.orb"}, + ) + + with self.assertRaises( + ValueError, msg="pp_file is a list and lack of pp for H" + ): + self.system_ch4.to( + "stru", + "STRU_tmp", + mass=[12, 1], + pp_file=["C.upf"], + numerical_orbital={"C": "C.orb", "H": "H.orb"}, + ) + + with self.assertRaises( + KeyError, msg="numerical_orbital is a dict and lack of orbital for H" + ): + self.system_ch4.to( + "stru", + "STRU_tmp", + mass=[12, 1], + pp_file={"C": "C.upf", "H": "H.upf"}, + numerical_orbital={"C": "C.orb", "O": "O.orb"}, + ) + + with self.assertRaises( + ValueError, msg="numerical_orbital is a list and lack of orbital for H" + ): + self.system_ch4.to( + "stru", + "STRU_tmp", + mass=[12, 1], + pp_file=["C.upf", "H.upf"], + numerical_orbital=["C.orb"], + ) + def test_dump_spinconstrain(self): self.system_ch4.to( "stru", From 373e0d7ec5d080bb97e83152c8173ad82f50d0aa Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Mon, 14 Oct 2024 20:32:05 -0400 Subject: [PATCH 25/33] fix(rtd): pin python to `<3.13` and mamba to `<2` (#735) Fix the RTD build error. Pinning mamba to `<2` is a workaround for https://github.com/jupyterlite/xeus/issues/122. ## Summary by CodeRabbit - **Chores** - Added a constraint specifying that the `python` dependency must be less than version 3.13. - Updated the Python version in the Read the Docs configuration to `mambaforge-23.11`. --- .readthedocs.yaml | 2 +- docs/rtd_environment.yml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 83a36032..79c271f4 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -3,7 +3,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "mambaforge-22.9" + python: "mambaforge-23.11" conda: environment: docs/rtd_environment.yml sphinx: diff --git a/docs/rtd_environment.yml b/docs/rtd_environment.yml index 7eae4aad..50572dca 100644 --- a/docs/rtd_environment.yml +++ b/docs/rtd_environment.yml @@ -2,6 +2,7 @@ name: dpdata channels: - conda-forge dependencies: - - mamba + - python <3.13 + - mamba <2 - pip: - ..[docs] From dccab685734138639ee589ec6c726d865a864bb9 Mon Sep 17 00:00:00 2001 From: "A bot of @njzjz" <48687836+njzjz-bot@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:59:14 -0400 Subject: [PATCH 26/33] chore(ci): use astral-sh/setup-uv action (#734) Generated by the task: https://github.com/njzjz-bot/njzjz-bot/issues/9. ## Summary by CodeRabbit - **Chores** - Updated workflow configurations for benchmarking and testing to improve the setup process for the `uv` tool. - Replaced the previous installation method with a new action that enables caching and specifies cache dependencies. --------- Signed-off-by: Jinzhe Zeng Co-authored-by: Jinzhe Zeng --- .github/workflows/benchmark.yml | 7 ++++++- .github/workflows/test.yml | 8 +++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 1dd68277..20f8dc6d 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -13,7 +13,12 @@ jobs: uses: actions/setup-python@v5 with: python-version: 3.12 - - run: curl -LsSf https://astral.sh/uv/install.sh | sh + - uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: | + **/requirements*.txt + **/pyproject.toml - name: Install dependencies run: uv pip install --system .[test,amber,ase,pymatgen,benchmark] rdkit openbabel-wheel - name: Run benchmarks diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a6d23270..0d855f4a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,13 @@ jobs: uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - run: curl -LsSf https://astral.sh/uv/install.sh | sh + - uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + cache-dependency-glob: | + **/requirements*.txt + **/pyproject.toml + cache-suffix: "py${{ matrix.python-version }}" - name: Install dependencies run: uv pip install --system .[test,amber,ase,pymatgen] coverage ./tests/plugin rdkit openbabel-wheel - name: Test From 74bfcccdb1b92565de64c49de6baf438d741bafc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:59:32 -0400 Subject: [PATCH 27/33] [pre-commit.ci] pre-commit autoupdate (#733) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) - [github.com/astral-sh/ruff-pre-commit: v0.6.7 → v0.6.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.7...v0.6.9) - [github.com/asottile/blacken-docs: 1.18.0 → 1.19.0](https://github.com/asottile/blacken-docs/compare/1.18.0...1.19.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Jinzhe Zeng --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d921a886..898923be 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: # there are many log files in tests # TODO: seperate py files and log files @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.7 + rev: v0.6.9 hooks: - id: ruff args: ["--fix"] @@ -36,7 +36,7 @@ repos: args: ["--write"] # Python inside docs - repo: https://github.com/asottile/blacken-docs - rev: 1.18.0 + rev: 1.19.0 hooks: - id: blacken-docs ci: From d1f1b98238607a479be1b6f2d438b5c24c8b743e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 14:24:48 +0800 Subject: [PATCH 28/33] [pre-commit.ci] pre-commit autoupdate (#741) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 898923be..0bb47d32 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.6.9 + rev: v0.7.0 hooks: - id: ruff args: ["--fix"] From cdbbe1b5c378ed0d418877c610d370617ae3eef6 Mon Sep 17 00:00:00 2001 From: Jinzhe Zeng Date: Tue, 22 Oct 2024 23:40:59 -0400 Subject: [PATCH 29/33] fix: change `project.entry-points.console_scripts` to `project.scripts` in `pyproject.toml` (#743) `project.entry-points.console_scripts` is not allowed per [PEP 621](https://peps.python.org/pep-0621/). ## Summary by CodeRabbit - **New Features** - Updated the command entry point for the `dpdata` tool for improved accessibility. - **Chores** - Organized optional dependencies for better clarity and management. - Maintained existing configurations for tools and package data without functional changes. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4bf2e64b..481e6864 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ Homepage = "https://github.com/deepmodeling/dpdata" documentation = "https://docs.deepmodeling.com/projects/dpdata" repository = "https://github.com/deepmodeling/dpdata" -[project.entry-points.console_scripts] +[project.scripts] dpdata = "dpdata.cli:dpdata_cli" [project.optional-dependencies] From 7853b7b526a5ffbd2b8a08d3a1f3ffa06c228d52 Mon Sep 17 00:00:00 2001 From: Peng Xingliang <91927439+pxlxingliang@users.noreply.github.com> Date: Thu, 24 Oct 2024 03:47:04 +0800 Subject: [PATCH 30/33] add spin for lammps (#738) Reference #728 to add the spin information for lammps. ## Summary by CodeRabbit - **New Features** - Introduced functions to extract and normalize spin data from LAMMPS dump files. - Added support for registering spin data types in the system. - Created new simulation input and output files for LAMMPS. - Added new compute and dump commands for calculating and outputting spin properties in LAMMPS. - **Tests** - Implemented unit tests for verifying the handling of spin configurations in LAMMPS files, including scenarios with zero spin values and incomplete data. --------- Co-authored-by: root Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Han Wang <92130845+wanghan-iapcm@users.noreply.github.com> --- dpdata/lammps/dump.py | 104 ++++++++++++++++++- dpdata/lammps/lmp.py | 73 +++++++++++-- dpdata/plugins/lammps.py | 56 +++++++++- tests/lammps/in.lmp | 2 + tests/lammps/spin.lmp | 12 +++ tests/lammps/traj.dump | 52 ++++++++++ tests/lammps/traj_partial_spin.dump | 52 ++++++++++ tests/test_lammps_spin.py | 153 ++++++++++++++++++++++++++++ 8 files changed, 493 insertions(+), 11 deletions(-) create mode 100644 tests/lammps/in.lmp create mode 100644 tests/lammps/spin.lmp create mode 100644 tests/lammps/traj.dump create mode 100644 tests/lammps/traj_partial_spin.dump create mode 100644 tests/test_lammps_spin.py diff --git a/dpdata/lammps/dump.py b/dpdata/lammps/dump.py index 72fee27d..91f0c48d 100644 --- a/dpdata/lammps/dump.py +++ b/dpdata/lammps/dump.py @@ -197,7 +197,91 @@ def load_file(fname: FileType, begin=0, step=1): buff.append(line) -def system_data(lines, type_map=None, type_idx_zero=True, unwrap=False): +def get_spin_keys(inputfile): + """ + Read input file and get the keys for spin info in dump. + + Parameters + ---------- + inputfile : str + Path to the input file. + + Returns + ------- + list or None + List of spin info keys if found, None otherwise. + """ + if inputfile is None: + return None + + if not os.path.isfile(inputfile): + warnings.warn(f"Input file {inputfile} not found.") + return None + + with open(inputfile) as f: + for line in f.readlines(): + ls = line.split() + if ( + len(ls) > 7 + and ls[0] == "compute" + and all(key in ls for key in ["sp", "spx", "spy", "spz"]) + ): + compute_name = ls[1] + return [ + f"c_{compute_name}[{ls.index(key) - 3}]" + for key in ["sp", "spx", "spy", "spz"] + ] + + return None + + +def get_spin(lines, spin_keys): + """ + Get the spin info from the dump file. + + Parameters + ---------- + lines : list + The content of the dump file. + spin_keys : list + The keys for spin info in dump file. + the spin info is stored in sp, spx, spy, spz or spin_keys, which is the spin norm and the spin vector + 1 1 0.00141160 5.64868599 0.01005602 1.54706291 0.00000000 0.00000000 1.00000000 -1.40772100 -2.03739417 -1522.64797384 -0.00397809 -0.00190426 -0.00743976 + """ + blk, head = _get_block(lines, "ATOMS") + heads = head.split() + + if spin_keys is not None and all(i in heads for i in spin_keys): + key = spin_keys + else: + return None + + try: + idx_id = heads.index("id") - 2 + idx_sp, idx_spx, idx_spy, idx_spz = (heads.index(k) - 2 for k in key) + + norm = [] + vec = [] + atom_ids = [] + for line in blk: + words = line.split() + norm.append([float(words[idx_sp])]) + vec.append( + [float(words[idx_spx]), float(words[idx_spy]), float(words[idx_spz])] + ) + atom_ids.append(int(words[idx_id])) + + spin = np.array(norm) * np.array(vec) + atom_ids, spin = zip(*sorted(zip(atom_ids, spin))) + return np.array(spin) + except (ValueError, IndexError) as e: + warnings.warn(f"Error processing spin data: {str(e)}") + return None + + +def system_data( + lines, type_map=None, type_idx_zero=True, unwrap=False, input_file=None +): array_lines = split_traj(lines) lines = array_lines[0] system = {} @@ -216,6 +300,12 @@ def system_data(lines, type_map=None, type_idx_zero=True, unwrap=False): system["cells"] = [np.array(cell)] system["atom_types"] = get_atype(lines, type_idx_zero=type_idx_zero) system["coords"] = [safe_get_posi(lines, cell, np.array(orig), unwrap)] + spin_keys = get_spin_keys(input_file) + spin = get_spin(lines, spin_keys) + has_spin = False + if spin is not None: + system["spins"] = [spin] + has_spin = True for ii in range(1, len(array_lines)): bounds, tilt = get_dumpbox(array_lines[ii]) orig, cell = dumpbox2box(bounds, tilt) @@ -228,6 +318,18 @@ def system_data(lines, type_map=None, type_idx_zero=True, unwrap=False): system["coords"].append( safe_get_posi(array_lines[ii], cell, np.array(orig), unwrap)[idx] ) + if has_spin: + spin = get_spin(array_lines[ii], spin_keys) + if spin is not None: + system["spins"].append(spin[idx]) + else: + warnings.warn( + f"Warning: spin info is not found in frame {ii}, remove spin info." + ) + system.pop("spins") + has_spin = False + if has_spin: + system["spins"] = np.array(system["spins"]) system["cells"] = np.array(system["cells"]) system["coords"] = np.array(system["coords"]) return system diff --git a/dpdata/lammps/lmp.py b/dpdata/lammps/lmp.py index 604b18d1..04620166 100644 --- a/dpdata/lammps/lmp.py +++ b/dpdata/lammps/lmp.py @@ -127,6 +127,19 @@ def get_posi(lines): return np.array(posis) +def get_spins(lines): + atom_lines = get_atoms(lines) + if len(atom_lines[0].split()) < 8: + return None + spins_ori = [] + spins_norm = [] + for ii in atom_lines: + iis = ii.split() + spins_ori.append([float(jj) for jj in iis[5:8]]) + spins_norm.append([float(iis[-1])]) + return np.array(spins_ori) * np.array(spins_norm) + + def get_lmpbox(lines): box_info = [] tilt = np.zeros(3) @@ -168,6 +181,11 @@ def system_data(lines, type_map=None, type_idx_zero=True): system["coords"] = [get_posi(lines)] system["cells"] = np.array(system["cells"]) system["coords"] = np.array(system["coords"]) + + spins = get_spins(lines) + if spins is not None: + system["spins"] = np.array([spins]) + return system @@ -216,14 +234,55 @@ def from_system_data(system, f_idx=0): + ptr_float_fmt + "\n" ) - for ii in range(natoms): - ret += coord_fmt % ( - ii + 1, - system["atom_types"][ii] + 1, - system["coords"][f_idx][ii][0] - system["orig"][0], - system["coords"][f_idx][ii][1] - system["orig"][1], - system["coords"][f_idx][ii][2] - system["orig"][2], + + if "spins" in system: + coord_fmt = ( + coord_fmt.strip("\n") + + " " + + ptr_float_fmt + + " " + + ptr_float_fmt + + " " + + ptr_float_fmt + + " " + + ptr_float_fmt + + "\n" ) + spins_norm = np.linalg.norm(system["spins"][f_idx], axis=1) + for ii in range(natoms): + if "spins" in system: + if spins_norm[ii] != 0: + ret += coord_fmt % ( + ii + 1, + system["atom_types"][ii] + 1, + system["coords"][f_idx][ii][0] - system["orig"][0], + system["coords"][f_idx][ii][1] - system["orig"][1], + system["coords"][f_idx][ii][2] - system["orig"][2], + system["spins"][f_idx][ii][0] / spins_norm[ii], + system["spins"][f_idx][ii][1] / spins_norm[ii], + system["spins"][f_idx][ii][2] / spins_norm[ii], + spins_norm[ii], + ) + else: + ret += coord_fmt % ( + ii + 1, + system["atom_types"][ii] + 1, + system["coords"][f_idx][ii][0] - system["orig"][0], + system["coords"][f_idx][ii][1] - system["orig"][1], + system["coords"][f_idx][ii][2] - system["orig"][2], + system["spins"][f_idx][ii][0], + system["spins"][f_idx][ii][1], + system["spins"][f_idx][ii][2] + 1, + spins_norm[ii], + ) + else: + ret += coord_fmt % ( + ii + 1, + system["atom_types"][ii] + 1, + system["coords"][f_idx][ii][0] - system["orig"][0], + system["coords"][f_idx][ii][1] - system["orig"][1], + system["coords"][f_idx][ii][2] - system["orig"][2], + ) return ret diff --git a/dpdata/plugins/lammps.py b/dpdata/plugins/lammps.py index 0327d444..c7e5c765 100644 --- a/dpdata/plugins/lammps.py +++ b/dpdata/plugins/lammps.py @@ -2,8 +2,11 @@ from typing import TYPE_CHECKING +import numpy as np + import dpdata.lammps.dump import dpdata.lammps.lmp +from dpdata.data_type import Axis, DataType from dpdata.format import Format from dpdata.utils import open_file @@ -11,6 +14,18 @@ from dpdata.utils import FileType +def register_spin(data): + if "spins" in data: + dt = DataType( + "spins", + np.ndarray, + (Axis.NFRAMES, Axis.NATOMS, 3), + required=False, + deepmd_name="spin", + ) + dpdata.System.register_data_type(dt) + + @Format.register("lmp") @Format.register("lammps/lmp") class LAMMPSLmpFormat(Format): @@ -18,7 +33,9 @@ class LAMMPSLmpFormat(Format): def from_system(self, file_name: FileType, type_map=None, **kwargs): with open_file(file_name) as fp: lines = [line.rstrip("\n") for line in fp] - return dpdata.lammps.lmp.to_system_data(lines, type_map) + data = dpdata.lammps.lmp.to_system_data(lines, type_map) + register_spin(data) + return data def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs): """Dump the system in lammps data format. @@ -45,7 +62,40 @@ def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs): class LAMMPSDumpFormat(Format): @Format.post("shift_orig_zero") def from_system( - self, file_name, type_map=None, begin=0, step=1, unwrap=False, **kwargs + self, + file_name: str, + type_map: list[str] = None, + begin: int = 0, + step: int = 1, + unwrap: bool = False, + input_file: str = None, + **kwargs, ): + """Read the data from a lammps dump file. + + Parameters + ---------- + file_name : str + The dump file name + type_map : List[str], optional + The atom type list + begin : int, optional + The begin step + step : int, optional + The step + unwrap : bool, optional + Whether to unwrap the coordinates + input_file : str, optional + The input file name + + Returns + ------- + dict + The system data + """ lines = dpdata.lammps.dump.load_file(file_name, begin=begin, step=step) - return dpdata.lammps.dump.system_data(lines, type_map, unwrap=unwrap) + data = dpdata.lammps.dump.system_data( + lines, type_map, unwrap=unwrap, input_file=input_file + ) + register_spin(data) + return data diff --git a/tests/lammps/in.lmp b/tests/lammps/in.lmp new file mode 100644 index 00000000..02f655fa --- /dev/null +++ b/tests/lammps/in.lmp @@ -0,0 +1,2 @@ +compute spin all property/atom sp spx spy spz fmx fmy fmz fx fy fz +dump dpgen_dump all custom 10 traj.dump id type x y z c_spin[1] c_spin[2] c_spin[3] c_spin[4] c_spin[5] c_spin[6] c_spin[7] c_spin[8] c_spin[9] c_spin[10] \ No newline at end of file diff --git a/tests/lammps/spin.lmp b/tests/lammps/spin.lmp new file mode 100644 index 00000000..d662cc2c --- /dev/null +++ b/tests/lammps/spin.lmp @@ -0,0 +1,12 @@ + +2 atoms +2 atom types + 0.0000000000 2.5243712000 xlo xhi + 0.0000000000 2.0430257000 ylo yhi + 0.0000000000 2.2254033000 zlo zhi + 1.2621856000 1.2874292000 0.7485898000 xy xz yz + +Atoms # atomic + + 1 1 0.0000000000 0.0000000000 0.0000000000 0.6000000000 0.8000000000 0.0000000000 5.0000000000 + 2 2 1.2621856000 0.7018028000 0.5513885000 0.0000000000 0.8000000000 0.6000000000 5.0000000000 diff --git a/tests/lammps/traj.dump b/tests/lammps/traj.dump new file mode 100644 index 00000000..e1682983 --- /dev/null +++ b/tests/lammps/traj.dump @@ -0,0 +1,52 @@ +ITEM: TIMESTEP +0 +ITEM: NUMBER OF ATOMS +17 +ITEM: BOX BOUNDS xy xz yz pp pp pp +-4.0080511965879438e-02 5.7039029418994556e+00 -5.9179115295410201e-03 +1.4436085788922526e-02 5.6674744441011660e+00 -1.1487414836883500e-02 +7.8239288740356017e-03 5.6734038274259646e+00 6.8277359008788905e-04 +ITEM: ATOMS id type x y z c_spin[1] c_spin[2] c_spin[3] c_spin[4] c_spin[5] c_spin[6] c_spin[7] c_spin[8] c_spin[9] c_spin[10] +1 1 0.00141160 5.64868599 0.01005602 1.54706291 0.00000000 0.00000000 1.00000000 -1.40772100 -2.03739417 -1522.64797384 -0.00397809 -0.00190426 -0.00743976 +2 1 5.65283939 5.57449025 2.84281508 1.54412869 0.00000000 0.00000000 1.00000000 7.75304092 6.48949619 -1512.84926162 -0.00637234 -0.00733168 0.00661107 +3 1 0.00066480 2.78022036 0.01010716 1.54612979 0.00000000 0.00000000 1.00000000 -0.93618575 1.92206111 -1520.80305011 -0.00316673 0.00177893 -0.00744575 +4 1 5.65233666 2.85374747 2.84289453 1.54439093 0.00000000 0.00000000 1.00000000 8.11012818 -6.49922039 -1514.31557088 -0.00569217 0.00741000 0.00640353 +5 1 2.82063515 5.64869321 0.01007552 1.54714250 0.00000000 0.00000000 1.00000000 2.49070852 -2.14456666 -1523.53038650 0.00478410 -0.00213962 -0.00751154 +6 1 2.89579803 5.57439179 2.84287630 1.54415032 0.00000000 0.00000000 1.00000000 -8.03062338 6.63950296 -1513.41291897 0.00440396 -0.00717185 0.00633657 +7 1 2.82151287 2.78010538 0.01016303 1.54619615 0.00000000 0.00000000 1.00000000 2.71859584 1.98482729 -1521.34149633 0.00533453 0.00194532 -0.00745901 +8 1 2.89637049 2.85377083 2.84297332 1.54440023 0.00000000 0.00000000 1.00000000 -7.76758760 -6.67134514 -1514.43304618 0.00505040 0.00743195 0.00630302 +9 1 1.41106492 1.38817482 1.72302072 1.18134529 0.00000000 0.00000000 1.00000000 0.27170165 -0.00426695 -444.22843899 0.00100237 -0.00002725 -0.03385965 +10 1 1.41105247 1.38807861 3.96314606 1.18153407 0.00000000 0.00000000 1.00000000 -0.07722674 0.01368756 -337.08703133 -0.00066982 0.00007487 0.07887183 +11 1 1.41105864 4.21395432 1.43987180 1.71989299 0.00000000 0.00000000 1.00000000 -0.01511106 0.00320081 -1653.34500916 0.00010421 0.00007248 0.00634401 +12 1 1.41104843 4.21387554 4.24576823 1.71989825 0.00000000 0.00000000 1.00000000 -0.71645898 0.05923960 -1640.68070568 -0.00117959 0.00006676 -0.01467806 +13 1 4.27433865 1.38779084 1.43977211 1.72010048 0.00000000 0.00000000 1.00000000 0.45899480 0.03956420 -1653.36356942 0.00051885 0.00002313 0.00911600 +14 1 4.27436799 1.38772964 4.24586490 1.72010133 0.00000000 0.00000000 1.00000000 0.38385331 0.07301994 -1642.06086017 -0.00002034 0.00010335 -0.01688908 +15 1 4.27435427 4.21452597 1.39359689 1.65590121 0.00000000 0.00000000 1.00000000 -0.01658773 -0.06159007 -1659.12744163 0.00006470 -0.00006420 -0.01342935 +16 1 4.27434583 4.21455469 4.29208004 1.65592002 0.00000000 0.00000000 1.00000000 -0.15590720 -0.03252166 -1654.84697132 -0.00066755 -0.00003915 -0.00482188 +17 2 1.41096761 1.38958048 0.01029027 0.00000000 0.00000000 0.00000000 1.00000000 0.00000000 0.00000000 0.00000000 0.00048351 -0.00022876 -0.00645195 +ITEM: TIMESTEP +10 +ITEM: NUMBER OF ATOMS +17 +ITEM: BOX BOUNDS xy xz yz pp pp pp +-4.0080511965879438e-02 5.7039029418994556e+00 -5.9179115295410201e-03 +1.4436085788922526e-02 5.6674744441011660e+00 -1.1487414836883500e-02 +7.8239288740356017e-03 5.6734038274259646e+00 6.8277359008788905e-04 +ITEM: ATOMS id type x y z c_spin[1] c_spin[2] c_spin[3] c_spin[4] c_spin[5] c_spin[6] c_spin[7] c_spin[8] c_spin[9] c_spin[10] +1 1 0.00037565 5.64900783 0.00994919 1.20356102 0.17466098 0.84115562 -0.51181127 -77.61471611 -389.41594243 234.29512368 0.00514290 -0.02481576 0.01063015 +2 1 5.65299480 5.57370279 2.84182058 1.17910451 0.85296110 0.48195380 -0.20044424 -311.75775120 -175.76677913 79.45225558 -0.01239308 -0.00914070 0.01933082 +3 1 0.00076668 2.78053566 0.01181481 1.20779106 0.33415542 0.49831517 -0.80001384 -163.88630094 -248.58823709 387.72415159 -0.01738465 -0.02878227 0.01503087 +4 1 5.65188602 2.85285383 2.84413423 1.20124335 -0.83536303 -0.20314716 0.51078356 399.86863784 90.34522869 -236.39221701 0.02327635 -0.00046572 -0.00138388 +5 1 2.82101290 5.64942265 0.01091135 1.34670883 -0.98528016 0.07078135 -0.15560530 902.73741755 -62.52279896 140.44423419 0.01500524 0.00581151 0.00468238 +6 1 2.89400594 5.57477971 2.84333235 1.25424131 -0.94587492 0.11487066 0.30352161 528.43507318 -60.32699018 -171.89948334 -0.00478280 0.00069273 -0.00496159 +7 1 2.82260306 2.78052696 0.00917962 1.17249564 -0.99589145 0.06282562 -0.06521619 374.56568243 -26.39431071 20.98877908 0.01464486 -0.01010131 -0.00993410 +8 1 2.89632273 2.85545549 2.84070353 1.24297017 -0.44008251 -0.42493729 0.79104721 240.05525392 236.02796206 -448.18443804 0.00137705 0.01258804 -0.01817420 +9 1 1.41117683 1.38867159 1.72266429 1.19059484 0.71251804 -0.69714805 -0.07938914 -309.93474514 293.96860716 19.98886311 -0.03871152 0.00854863 -0.02757569 +10 1 1.41176544 1.38873530 3.96470435 1.17564502 -0.51932602 -0.74875017 0.41191463 181.72443401 263.91689829 -132.94216896 0.00122847 0.01674701 0.02707109 +11 1 1.41085716 4.21342650 1.43850987 1.19874662 -0.51890828 0.82913822 0.20800000 237.52969259 -379.65100512 -93.16140268 0.01185986 -0.01872789 0.00032128 +12 1 1.41088045 4.21340876 4.24487134 1.20157661 -0.86390154 -0.04516556 -0.50163154 388.97171693 21.75492170 227.68580658 0.02074490 0.00756366 0.01937948 +13 1 4.27525485 1.38812593 1.43912039 1.23209806 0.55809649 0.81404794 0.16079259 -335.92026314 -484.87463129 -91.14464759 -0.03675759 -0.03549076 0.00310277 +14 1 4.27483864 1.38696457 4.24782541 1.18431742 0.00519166 -0.92210080 0.38691492 -4.73957478 407.09534135 -171.59043210 -0.00911750 0.04394272 -0.01683249 +15 1 4.27528588 4.21463764 1.39334117 1.17456490 -0.93713453 -0.09927163 0.33455046 397.32993706 40.92599847 -141.68618750 0.01918926 -0.00534149 -0.01906574 +16 1 4.27407834 4.21327842 4.29226033 1.31499905 -0.21350543 -0.97682201 -0.01530327 180.98908307 848.25344747 12.36402507 0.00492895 0.04383813 0.00955221 +17 2 1.40675897 1.38612182 0.01000617 0.00000000 0.00000000 0.00000000 1.00000000 0.00000000 0.00000000 0.00000000 0.00174929 -0.00686653 -0.01117336 diff --git a/tests/lammps/traj_partial_spin.dump b/tests/lammps/traj_partial_spin.dump new file mode 100644 index 00000000..5ee20855 --- /dev/null +++ b/tests/lammps/traj_partial_spin.dump @@ -0,0 +1,52 @@ +ITEM: TIMESTEP +0 +ITEM: NUMBER OF ATOMS +17 +ITEM: BOX BOUNDS xy xz yz pp pp pp +-4.0080511965879438e-02 5.7039029418994556e+00 -5.9179115295410201e-03 +1.4436085788922526e-02 5.6674744441011660e+00 -1.1487414836883500e-02 +7.8239288740356017e-03 5.6734038274259646e+00 6.8277359008788905e-04 +ITEM: ATOMS id type x y z c_spin[1] c_spin[2] c_spin[3] c_spin[4] c_spin[5] c_spin[6] c_spin[7] c_spin[8] c_spin[9] c_spin[10] +1 1 0.00141160 5.64868599 0.01005602 1.54706291 0.00000000 0.00000000 1.00000000 -1.40772100 -2.03739417 -1522.64797384 -0.00397809 -0.00190426 -0.00743976 +2 1 5.65283939 5.57449025 2.84281508 1.54412869 0.00000000 0.00000000 1.00000000 7.75304092 6.48949619 -1512.84926162 -0.00637234 -0.00733168 0.00661107 +3 1 0.00066480 2.78022036 0.01010716 1.54612979 0.00000000 0.00000000 1.00000000 -0.93618575 1.92206111 -1520.80305011 -0.00316673 0.00177893 -0.00744575 +4 1 5.65233666 2.85374747 2.84289453 1.54439093 0.00000000 0.00000000 1.00000000 8.11012818 -6.49922039 -1514.31557088 -0.00569217 0.00741000 0.00640353 +5 1 2.82063515 5.64869321 0.01007552 1.54714250 0.00000000 0.00000000 1.00000000 2.49070852 -2.14456666 -1523.53038650 0.00478410 -0.00213962 -0.00751154 +6 1 2.89579803 5.57439179 2.84287630 1.54415032 0.00000000 0.00000000 1.00000000 -8.03062338 6.63950296 -1513.41291897 0.00440396 -0.00717185 0.00633657 +7 1 2.82151287 2.78010538 0.01016303 1.54619615 0.00000000 0.00000000 1.00000000 2.71859584 1.98482729 -1521.34149633 0.00533453 0.00194532 -0.00745901 +8 1 2.89637049 2.85377083 2.84297332 1.54440023 0.00000000 0.00000000 1.00000000 -7.76758760 -6.67134514 -1514.43304618 0.00505040 0.00743195 0.00630302 +9 1 1.41106492 1.38817482 1.72302072 1.18134529 0.00000000 0.00000000 1.00000000 0.27170165 -0.00426695 -444.22843899 0.00100237 -0.00002725 -0.03385965 +10 1 1.41105247 1.38807861 3.96314606 1.18153407 0.00000000 0.00000000 1.00000000 -0.07722674 0.01368756 -337.08703133 -0.00066982 0.00007487 0.07887183 +11 1 1.41105864 4.21395432 1.43987180 1.71989299 0.00000000 0.00000000 1.00000000 -0.01511106 0.00320081 -1653.34500916 0.00010421 0.00007248 0.00634401 +12 1 1.41104843 4.21387554 4.24576823 1.71989825 0.00000000 0.00000000 1.00000000 -0.71645898 0.05923960 -1640.68070568 -0.00117959 0.00006676 -0.01467806 +13 1 4.27433865 1.38779084 1.43977211 1.72010048 0.00000000 0.00000000 1.00000000 0.45899480 0.03956420 -1653.36356942 0.00051885 0.00002313 0.00911600 +14 1 4.27436799 1.38772964 4.24586490 1.72010133 0.00000000 0.00000000 1.00000000 0.38385331 0.07301994 -1642.06086017 -0.00002034 0.00010335 -0.01688908 +15 1 4.27435427 4.21452597 1.39359689 1.65590121 0.00000000 0.00000000 1.00000000 -0.01658773 -0.06159007 -1659.12744163 0.00006470 -0.00006420 -0.01342935 +16 1 4.27434583 4.21455469 4.29208004 1.65592002 0.00000000 0.00000000 1.00000000 -0.15590720 -0.03252166 -1654.84697132 -0.00066755 -0.00003915 -0.00482188 +17 2 1.41096761 1.38958048 0.01029027 0.00000000 0.00000000 0.00000000 1.00000000 0.00000000 0.00000000 0.00000000 0.00048351 -0.00022876 -0.00645195 +ITEM: TIMESTEP +10 +ITEM: NUMBER OF ATOMS +17 +ITEM: BOX BOUNDS xy xz yz pp pp pp +-4.0080511965879438e-02 5.7039029418994556e+00 -5.9179115295410201e-03 +1.4436085788922526e-02 5.6674744441011660e+00 -1.1487414836883500e-02 +7.8239288740356017e-03 5.6734038274259646e+00 6.8277359008788905e-04 +ITEM: ATOMS id type x y z +1 1 0.00037565 5.64900783 0.00994919 +2 1 5.65299480 5.57370279 2.84182058 +3 1 0.00076668 2.78053566 0.01181481 +4 1 5.65188602 2.85285383 2.84413423 +5 1 2.82101290 5.64942265 0.01091135 +6 1 2.89400594 5.57477971 2.84333235 +7 1 2.82260306 2.78052696 0.00917962 +8 1 2.89632273 2.85545549 2.84070353 +9 1 1.41117683 1.38867159 1.72266429 +10 1 1.41176544 1.38873530 3.96470435 +11 1 1.41085716 4.21342650 1.43850987 +12 1 1.41088045 4.21340876 4.24487134 +13 1 4.27525485 1.38812593 1.43912039 +14 1 4.27483864 1.38696457 4.24782541 +15 1 4.27528588 4.21463764 1.39334117 +16 1 4.27407834 4.21327842 4.29226033 +17 2 1.40675897 1.38612182 0.01000617 diff --git a/tests/test_lammps_spin.py b/tests/test_lammps_spin.py new file mode 100644 index 00000000..d3d58920 --- /dev/null +++ b/tests/test_lammps_spin.py @@ -0,0 +1,153 @@ +from __future__ import annotations + +import os +import shutil +import unittest + +import numpy as np +from context import dpdata + +from dpdata.lammps.dump import get_spin + +TRAJ_NO_ID = """ITEM: TIMESTEP +0 +ITEM: NUMBER OF ATOMS +17 +ITEM: BOX BOUNDS xy xz yz pp pp pp +-4.0080511965879438e-02 5.7039029418994556e+00 -5.9179115295410201e-03 +1.4436085788922526e-02 5.6674744441011660e+00 -1.1487414836883500e-02 +7.8239288740356017e-03 5.6734038274259646e+00 6.8277359008788905e-04 +ITEM: ATOMS type x y z c_spin[1] c_spin[2] c_spin[3] c_spin[4] c_spin[5] c_spin[6] c_spin[7] c_spin[8] c_spin[9] c_spin[10] +1 0.00141160 5.64868599 0.01005602 1.54706291 0.00000000 0.00000000 1.00000000 -1.40772100 -2.03739417 -1522.64797384 -0.00397809 -0.00190426 -0.00743976 +1 5.65283939 5.57449025 2.84281508 1.54412869 0.00000000 0.00000000 1.00000000 7.75304092 6.48949619 -1512.84926162 -0.00637234 -0.00733168 0.00661107 +1 0.00066480 2.78022036 0.01010716 1.54612979 0.00000000 0.00000000 1.00000000 -0.93618575 1.92206111 -1520.80305011 -0.00316673 0.00177893 -0.00744575 +1 5.65233666 2.85374747 2.84289453 1.54439093 0.00000000 0.00000000 1.00000000 8.11012818 -6.49922039 -1514.31557088 -0.00569217 0.00741000 0.00640353 +1 2.82063515 5.64869321 0.01007552 1.54714250 0.00000000 0.00000000 1.00000000 2.49070852 -2.14456666 -1523.53038650 0.00478410 -0.00213962 -0.00751154 +1 2.89579803 5.57439179 2.84287630 1.54415032 0.00000000 0.00000000 1.00000000 -8.03062338 6.63950296 -1513.41291897 0.00440396 -0.00717185 0.00633657 +1 2.82151287 2.78010538 0.01016303 1.54619615 0.00000000 0.00000000 1.00000000 2.71859584 1.98482729 -1521.34149633 0.00533453 0.00194532 -0.00745901 +1 2.89637049 2.85377083 2.84297332 1.54440023 0.00000000 0.00000000 1.00000000 -7.76758760 -6.67134514 -1514.43304618 0.00505040 0.00743195 0.00630302 +1 1.41106492 1.38817482 1.72302072 1.18134529 0.00000000 0.00000000 1.00000000 0.27170165 -0.00426695 -444.22843899 0.00100237 -0.00002725 -0.03385965 + 1 1.41105247 1.38807861 3.96314606 1.18153407 0.00000000 0.00000000 1.00000000 -0.07722674 0.01368756 -337.08703133 -0.00066982 0.00007487 0.07887183 + 1 1.41105864 4.21395432 1.43987180 1.71989299 0.00000000 0.00000000 1.00000000 -0.01511106 0.00320081 -1653.34500916 0.00010421 0.00007248 0.00634401 + 1 1.41104843 4.21387554 4.24576823 1.71989825 0.00000000 0.00000000 1.00000000 -0.71645898 0.05923960 -1640.68070568 -0.00117959 0.00006676 -0.01467806 + 1 4.27433865 1.38779084 1.43977211 1.72010048 0.00000000 0.00000000 1.00000000 0.45899480 0.03956420 -1653.36356942 0.00051885 0.00002313 0.00911600 + 1 4.27436799 1.38772964 4.24586490 1.72010133 0.00000000 0.00000000 1.00000000 0.38385331 0.07301994 -1642.06086017 -0.00002034 0.00010335 -0.01688908 + 1 4.27435427 4.21452597 1.39359689 1.65590121 0.00000000 0.00000000 1.00000000 -0.01658773 -0.06159007 -1659.12744163 0.00006470 -0.00006420 -0.01342935 + 1 4.27434583 4.21455469 4.29208004 1.65592002 0.00000000 0.00000000 1.00000000 -0.15590720 -0.03252166 -1654.84697132 -0.00066755 -0.00003915 -0.00482188 + 2 1.41096761 1.38958048 0.01029027 0.00000000 0.00000000 0.00000000 1.00000000 0.00000000 0.00000000 0.00000000 0.00048351 -0.00022876 -0.00645195""" + + +class TestLmp(unittest.TestCase): + def setUp(self): + self.tmp_system = dpdata.System( + os.path.join("poscars", "conf.lmp"), type_map=["O", "H"] + ) + self.tmp_system.data["spins"] = [[[3, 4, 0], [0, 4, 3]]] + self.lmp_coord_name = "tmp.lmp" + + def tearDown(self): + pass # if os.path.isfile(self.lmp_coord_name):os.remove(self.lmp_coord_name) + + def test_dump_input(self): + self.tmp_system.to("lammps/lmp", self.lmp_coord_name) + self.assertTrue(os.path.isfile(self.lmp_coord_name)) + with open(self.lmp_coord_name) as f: + c = f.read() + + coord_ref = """ 1 1 0.0000000000 0.0000000000 0.0000000000 0.6000000000 0.8000000000 0.0000000000 5.0000000000 + 2 2 1.2621856000 0.7018028000 0.5513885000 0.0000000000 0.8000000000 0.6000000000 5.0000000000""" + self.assertTrue(coord_ref in c) + + def test_dump_input_zero_spin(self): + self.tmp_system.data["spins"] = [[[0, 0, 0], [0, 0, 0]]] + self.tmp_system.to("lammps/lmp", self.lmp_coord_name) + self.assertTrue(os.path.isfile(self.lmp_coord_name)) + with open(self.lmp_coord_name) as f: + c = f.read() + coord_ref = """ 1 1 0.0000000000 0.0000000000 0.0000000000 0.0000000000 0.0000000000 1.0000000000 0.0000000000 + 2 2 1.2621856000 0.7018028000 0.5513885000 0.0000000000 0.0000000000 1.0000000000 0.0000000000""" + self.assertTrue(coord_ref in c) + + def test_read_input(self): + # check if dpdata can read the spins + tmp_system = dpdata.System( + "lammps/spin.lmp", fmt="lammps/lmp", type_map=["O", "H"] + ) + self.assertTrue((tmp_system.data["spins"][0] == [[3, 4, 0], [0, 4, 3]]).all()) + + tmp_system.to(file_name="lammps/dump", fmt="deepmd/npy") + self.assertTrue(os.path.isfile("lammps/dump/set.000/spin.npy")) + + if os.path.isdir("lammps/dump"): + shutil.rmtree("lammps/dump") + + +class TestDump(unittest.TestCase): + def test_read_dump_spin(self): + tmp_system = dpdata.System( + "lammps/traj.dump", + fmt="lammps/dump", + type_map=["O", "H"], + input_file="lammps/in.lmp", + ) + self.assertTrue(len(tmp_system.data["spins"]) == 2) + np.testing.assert_almost_equal( + tmp_system.data["spins"][0][0], [0, 0, 1.54706291], decimal=8 + ) + np.testing.assert_almost_equal( + tmp_system.data["spins"][0][1], [0, 0, 1.54412869], decimal=8 + ) + np.testing.assert_almost_equal( + tmp_system.data["spins"][0][-2], [0, 0, 1.65592002], decimal=8 + ) + np.testing.assert_almost_equal( + tmp_system.data["spins"][0][-1], [0, 0, 0], decimal=8 + ) + + np.testing.assert_almost_equal( + tmp_system.data["spins"][1][0], + [0.21021514724299958, 1.0123821159859323, -0.6159960941686954], + decimal=8, + ) + np.testing.assert_almost_equal( + tmp_system.data["spins"][1][1], + [1.0057302798645609, 0.568273899191638, -0.2363447073875224], + decimal=8, + ) + np.testing.assert_almost_equal( + tmp_system.data["spins"][1][-2], + [-0.28075943761984146, -1.2845200151690905, -0.0201237855118935], + decimal=8, + ) + np.testing.assert_almost_equal( + tmp_system.data["spins"][1][-1], [0, 0, 0], decimal=8 + ) + + tmp_system.to(file_name="lammps/dump", fmt="deepmd/npy") + self.assertTrue(os.path.isfile("lammps/dump/set.000/spin.npy")) + + if os.path.isdir("lammps/dump"): + shutil.rmtree("lammps/dump") + + def test_read_dump_partial_spin(self): + # test if dpdata can read the spins when the spin data is not complete + with self.assertWarns(UserWarning) as cm: + tmp_system = dpdata.System( + "lammps/traj_partial_spin.dump", + fmt="lammps/dump", + type_map=["O", "H"], + input_file="lammps/in.lmp", + ) + self.assertTrue("spins" not in tmp_system.data) + + self.assertIn("Warning: spin info is not found in frame", str(cm.warning)) + + def test_get_spin_failed(self): + with self.assertWarns(UserWarning) as cm: + spin = get_spin( + TRAJ_NO_ID.split("\n"), + ["c_spin[1]", "c_spin[2]", "c_spin[3]", "c_spin[4]"], + ) + self.assertTrue(spin is None) + + self.assertIn("Error processing spin data:", str(cm.warning)) From b908c2b7be49ff6073f3215c0ac982c46715dfc8 Mon Sep 17 00:00:00 2001 From: "A bot of @njzjz" <48687836+njzjz-bot@users.noreply.github.com> Date: Mon, 28 Oct 2024 23:39:05 -0400 Subject: [PATCH 31/33] docs: replace sphinx-rtd-theme with sphinx-book-theme (#745) Compared to `sphinx-rtd-theme`, `sphinx-book-theme` is a clean and modern Sphinx theme. Generated by the task: https://github.com/njzjz-bot/njzjz-bot/issues/11. --- docs/conf.py | 4 ++-- pyproject.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a21d19d5..a40422d3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,7 +44,7 @@ # ones. extensions = [ "deepmodeling_sphinx", - "sphinx_rtd_theme", + "sphinx_book_theme", "sphinx.ext.mathjax", "sphinx.ext.viewcode", "sphinx.ext.intersphinx", @@ -87,7 +87,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "sphinx_rtd_theme" +html_theme = "sphinx_book_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/pyproject.toml b/pyproject.toml index 481e6864..87fa8647 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,10 +55,10 @@ pymatgen = ['pymatgen'] docs = [ 'sphinx', 'recommonmark', - 'sphinx_rtd_theme>=1.0.0rc1', + 'sphinx-book-theme', 'numpydoc', 'myst-parser', - 'deepmodeling-sphinx>=0.1.1', + 'deepmodeling-sphinx>=0.3.0', 'sphinx-argparse<0.5.0', 'rdkit', 'jupyterlite-sphinx', From fba50f1be45389e067257247212610602d08836c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:55:23 +0800 Subject: [PATCH 32/33] [pre-commit.ci] pre-commit autoupdate (#746) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.0 → v0.7.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.0...v0.7.1) - [github.com/asottile/blacken-docs: 1.19.0 → 1.19.1](https://github.com/asottile/blacken-docs/compare/1.19.0...1.19.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0bb47d32..245b376b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # Python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.7.0 + rev: v0.7.1 hooks: - id: ruff args: ["--fix"] @@ -36,7 +36,7 @@ repos: args: ["--write"] # Python inside docs - repo: https://github.com/asottile/blacken-docs - rev: 1.19.0 + rev: 1.19.1 hooks: - id: blacken-docs ci: From d0a786835a00b5b35cbf6ba72cd2c8373f69443c Mon Sep 17 00:00:00 2001 From: AngelJia Date: Tue, 29 Oct 2024 00:53:34 -0500 Subject: [PATCH 33/33] Add support for converting move flags between Abacus and VASP (#744) ## Summary by CodeRabbit ## Release Notes - **New Features** - Enhanced atomic movement handling with the addition of a `move` parameter across multiple functions, enabling more detailed tracking of atom movements. - Introduced selective dynamics support in VASP POSCAR data handling, allowing for better control over atomic movement flags. - Added structured representation for cubic boron nitride crystal in new test files. - **Bug Fixes** - Updated atomic positions and magnetic properties in test structures to reflect accurate simulation data. - **Tests** - Added new test methods to validate the handling of movement flags and ensure the integrity of output structures across various scenarios. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- dpdata/abacus/md.py | 4 ++- dpdata/abacus/relax.py | 4 ++- dpdata/abacus/scf.py | 21 +++++++---- dpdata/plugins/abacus.py | 15 ++++++++ dpdata/plugins/vasp.py | 16 +++++++++ dpdata/vasp/poscar.py | 55 ++++++++++++++++++++++++++--- tests/abacus.scf/STRU.ch4 | 8 ++--- tests/abacus.scf/stru_test | 8 ++--- tests/poscars/POSCAR.oh.err1 | 11 ++++++ tests/poscars/POSCAR.oh.err2 | 11 ++++++ tests/test_abacus_stru_dump.py | 48 +++++++++++++++++++++++-- tests/test_vasp_poscar_dump.py | 15 ++++++++ tests/test_vasp_poscar_to_system.py | 31 ++++++++++++++++ 13 files changed, 224 insertions(+), 23 deletions(-) create mode 100644 tests/poscars/POSCAR.oh.err1 create mode 100644 tests/poscars/POSCAR.oh.err2 diff --git a/dpdata/abacus/md.py b/dpdata/abacus/md.py index bbb02343..aa4215d8 100644 --- a/dpdata/abacus/md.py +++ b/dpdata/abacus/md.py @@ -167,7 +167,7 @@ def get_frame(fname): with open_file(geometry_path_in) as fp: geometry_inlines = fp.read().split("\n") celldm, cell = get_cell(geometry_inlines) - atom_names, natoms, types, coords = get_coords( + atom_names, natoms, types, coords, move = get_coords( celldm, cell, geometry_inlines, inlines ) # This coords is not to be used. @@ -221,5 +221,7 @@ def get_frame(fname): data["spins"] = magmom if len(magforce) > 0: data["mag_forces"] = magforce + if len(move) > 0: + data["move"] = move return data diff --git a/dpdata/abacus/relax.py b/dpdata/abacus/relax.py index be80bc90..63f13678 100644 --- a/dpdata/abacus/relax.py +++ b/dpdata/abacus/relax.py @@ -183,7 +183,7 @@ def get_frame(fname): with open_file(geometry_path_in) as fp: geometry_inlines = fp.read().split("\n") celldm, cell = get_cell(geometry_inlines) - atom_names, natoms, types, coord_tmp = get_coords( + atom_names, natoms, types, coord_tmp, move = get_coords( celldm, cell, geometry_inlines, inlines ) @@ -218,5 +218,7 @@ def get_frame(fname): data["spins"] = magmom if len(magforce) > 0: data["mag_forces"] = magforce + if len(move) > 0: + data["move"] = move return data diff --git a/dpdata/abacus/scf.py b/dpdata/abacus/scf.py index b1b2cfed..43e65f7e 100644 --- a/dpdata/abacus/scf.py +++ b/dpdata/abacus/scf.py @@ -299,7 +299,8 @@ def get_coords(celldm, cell, geometry_inlines, inlines=None): coords.append(xyz) atom_types.append(it) - move.append(imove) + if imove is not None: + move.append(imove) velocity.append(ivelocity) mag.append(imagmom) angle1.append(iangle1) @@ -310,7 +311,8 @@ def get_coords(celldm, cell, geometry_inlines, inlines=None): line_idx += 1 coords = np.array(coords) # need transformation!!! atom_types = np.array(atom_types) - return atom_names, atom_numbs, atom_types, coords + move = np.array(move, dtype=bool) + return atom_names, atom_numbs, atom_types, coords, move def get_energy(outlines): @@ -477,7 +479,7 @@ def get_frame(fname): outlines = fp.read().split("\n") celldm, cell = get_cell(geometry_inlines) - atom_names, natoms, types, coords = get_coords( + atom_names, natoms, types, coords, move = get_coords( celldm, cell, geometry_inlines, inlines ) magmom, magforce = get_mag_force(outlines) @@ -510,6 +512,8 @@ def get_frame(fname): data["spins"] = magmom if len(magforce) > 0: data["mag_forces"] = magforce + if len(move) > 0: + data["move"] = move[np.newaxis, :, :] # print("atom_names = ", data['atom_names']) # print("natoms = ", data['atom_numbs']) # print("types = ", data['atom_types']) @@ -561,7 +565,7 @@ def get_frame_from_stru(fname): nele = get_nele_from_stru(geometry_inlines) inlines = [f"ntype {nele}"] celldm, cell = get_cell(geometry_inlines) - atom_names, natoms, types, coords = get_coords( + atom_names, natoms, types, coords, move = get_coords( celldm, cell, geometry_inlines, inlines ) data = {} @@ -571,6 +575,8 @@ def get_frame_from_stru(fname): data["cells"] = cell[np.newaxis, :, :] data["coords"] = coords[np.newaxis, :, :] data["orig"] = np.zeros(3) + if len(move) > 0: + data["move"] = move[np.newaxis, :, :] return data @@ -609,8 +615,8 @@ def make_unlabeled_stru( numerical descriptor file mass : list of float, optional List of atomic masses - move : list of list of bool, optional - List of the move flag of each xyz direction of each atom + move : list of (list of list of bool), optional + List of the move flag of each xyz direction of each atom for each frame velocity : list of list of float, optional List of the velocity of each xyz direction of each atom mag : list of (list of float or float), optional @@ -684,6 +690,9 @@ def process_file_input(file_input, atom_names, input_name): if mag is None and data.get("spins") is not None and len(data["spins"]) > 0: mag = data["spins"][frame_idx] + if move is None and data.get("move", None) is not None and len(data["move"]) > 0: + move = data["move"][frame_idx] + atom_numbs = sum(data["atom_numbs"]) for key in [move, velocity, mag, angle1, angle2, sc, lambda_]: if key is not None: diff --git a/dpdata/plugins/abacus.py b/dpdata/plugins/abacus.py index cbd1475f..b3e7c98a 100644 --- a/dpdata/plugins/abacus.py +++ b/dpdata/plugins/abacus.py @@ -67,6 +67,18 @@ def register_mag_data(data): dpdata.LabeledSystem.register_data_type(dt) +def register_move_data(data): + if "move" in data: + dt = DataType( + "move", + np.ndarray, + (Axis.NFRAMES, Axis.NATOMS, 3), + required=False, + deepmd_name="move", + ) + dpdata.System.register_data_type(dt) + + @Format.register("abacus/scf") @Format.register("abacus/pw/scf") @Format.register("abacus/lcao/scf") @@ -75,6 +87,7 @@ class AbacusSCFFormat(Format): def from_labeled_system(self, file_name, **kwargs): data = dpdata.abacus.scf.get_frame(file_name) register_mag_data(data) + register_move_data(data) return data @@ -86,6 +99,7 @@ class AbacusMDFormat(Format): def from_labeled_system(self, file_name, **kwargs): data = dpdata.abacus.md.get_frame(file_name) register_mag_data(data) + register_move_data(data) return data @@ -97,4 +111,5 @@ class AbacusRelaxFormat(Format): def from_labeled_system(self, file_name, **kwargs): data = dpdata.abacus.relax.get_frame(file_name) register_mag_data(data) + register_move_data(data) return data diff --git a/dpdata/plugins/vasp.py b/dpdata/plugins/vasp.py index 0160bde2..d25d0c25 100644 --- a/dpdata/plugins/vasp.py +++ b/dpdata/plugins/vasp.py @@ -7,6 +7,7 @@ import dpdata.vasp.outcar import dpdata.vasp.poscar import dpdata.vasp.xml +from dpdata.data_type import Axis, DataType from dpdata.format import Format from dpdata.utils import open_file, uniq_atom_names @@ -14,6 +15,18 @@ from dpdata.utils import FileType +def register_move_data(data): + if "move" in data: + dt = DataType( + "move", + np.ndarray, + (Axis.NFRAMES, Axis.NATOMS, 3), + required=False, + deepmd_name="move", + ) + dpdata.System.register_data_type(dt) + + @Format.register("poscar") @Format.register("contcar") @Format.register("vasp/poscar") @@ -25,6 +38,7 @@ def from_system(self, file_name: FileType, **kwargs): lines = [line.rstrip("\n") for line in fp] data = dpdata.vasp.poscar.to_system_data(lines) data = uniq_atom_names(data) + register_move_data(data) return data def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs): @@ -99,6 +113,7 @@ def from_labeled_system( vol = np.linalg.det(np.reshape(data["cells"][ii], [3, 3])) data["virials"][ii] *= v_pref * vol data = uniq_atom_names(data) + register_move_data(data) return data @@ -135,4 +150,5 @@ def from_labeled_system(self, file_name, begin=0, step=1, **kwargs): vol = np.linalg.det(np.reshape(data["cells"][ii], [3, 3])) data["virials"][ii] *= v_pref * vol data = uniq_atom_names(data) + register_move_data(data) return data diff --git a/dpdata/vasp/poscar.py b/dpdata/vasp/poscar.py index 102e7904..30073e2b 100644 --- a/dpdata/vasp/poscar.py +++ b/dpdata/vasp/poscar.py @@ -4,13 +4,22 @@ import numpy as np -def _to_system_data_lower(lines, cartesian=True): +def _to_system_data_lower(lines, cartesian=True, selective_dynamics=False): + def move_flag_mapper(flag): + if flag == "T": + return True + elif flag == "F": + return False + else: + raise RuntimeError(f"Invalid move flag: {flag}") + """Treat as cartesian poscar.""" system = {} system["atom_names"] = [str(ii) for ii in lines[5].split()] system["atom_numbs"] = [int(ii) for ii in lines[6].split()] scale = float(lines[1]) cell = [] + move_flags = [] for ii in range(2, 5): boxv = [float(jj) for jj in lines[ii].split()] boxv = np.array(boxv) * scale @@ -19,12 +28,21 @@ def _to_system_data_lower(lines, cartesian=True): natoms = sum(system["atom_numbs"]) coord = [] for ii in range(8, 8 + natoms): - tmpv = [float(jj) for jj in lines[ii].split()[:3]] + tmp = lines[ii].split() + tmpv = [float(jj) for jj in tmp[:3]] if cartesian: tmpv = np.array(tmpv) * scale else: tmpv = np.matmul(np.array(tmpv), system["cells"][0]) coord.append(tmpv) + if selective_dynamics: + if len(tmp) == 6: + move_flags.append(list(map(move_flag_mapper, tmp[3:]))) + else: + raise RuntimeError( + f"Invalid move flags, should be 6 columns, got {tmp}" + ) + system["coords"] = [np.array(coord)] system["orig"] = np.zeros(3) atom_types = [] @@ -34,12 +52,18 @@ def _to_system_data_lower(lines, cartesian=True): system["atom_types"] = np.array(atom_types, dtype=int) system["cells"] = np.array(system["cells"]) system["coords"] = np.array(system["coords"]) + if move_flags: + move_flags = np.array(move_flags, dtype=bool) + move_flags = move_flags.reshape((1, natoms, 3)) + system["move"] = np.array(move_flags, dtype=bool) return system def to_system_data(lines): # remove the line that has 'selective dynamics' + selective_dynamics = False if lines[7][0] == "S" or lines[7][0] == "s": + selective_dynamics = True lines.pop(7) is_cartesian = lines[7][0] in ["C", "c", "K", "k"] if not is_cartesian: @@ -47,7 +71,7 @@ def to_system_data(lines): raise RuntimeError( "seem not to be a valid POSCAR of vasp 5.x, may be a POSCAR of vasp 4.x?" ) - return _to_system_data_lower(lines, is_cartesian) + return _to_system_data_lower(lines, is_cartesian, selective_dynamics) def from_system_data(system, f_idx=0, skip_zeros=True): @@ -72,6 +96,10 @@ def from_system_data(system, f_idx=0, skip_zeros=True): continue ret += "%d " % ii ret += "\n" + move = system.get("move", None) + if move is not None and len(move) > 0: + ret += "Selective Dynamics\n" + # should use Cartesian for VESTA software ret += "Cartesian\n" atype = system["atom_types"] @@ -81,9 +109,26 @@ def from_system_data(system, f_idx=0, skip_zeros=True): sort_idx = np.lexsort((np.arange(len(atype)), atype)) atype = atype[sort_idx] posis = posis[sort_idx] + if move is not None and len(move) > 0: + move = move[f_idx][sort_idx] + + if isinstance(move, np.ndarray): + move = move.tolist() + posi_list = [] - for ii in posis: - posi_list.append(f"{ii[0]:15.10f} {ii[1]:15.10f} {ii[2]:15.10f}") + for idx in range(len(posis)): + ii_posi = posis[idx] + line = f"{ii_posi[0]:15.10f} {ii_posi[1]:15.10f} {ii_posi[2]:15.10f}" + if move is not None and len(move) > 0: + move_flags = move[idx] + if not isinstance(move_flags, list) or len(move_flags) != 3: + raise RuntimeError( + f"Invalid move flags: {move_flags}, should be a list of 3 bools" + ) + line += " " + " ".join("T" if flag else "F" for flag in move_flags) + + posi_list.append(line) + posi_list.append("") ret += "\n".join(posi_list) return ret diff --git a/tests/abacus.scf/STRU.ch4 b/tests/abacus.scf/STRU.ch4 index cf97747a..bc33cbe9 100644 --- a/tests/abacus.scf/STRU.ch4 +++ b/tests/abacus.scf/STRU.ch4 @@ -18,11 +18,11 @@ Cartesian #Cartesian(Unit is LATTICE_CONSTANT) C #Name of element 0.0 #Magnetic for this element. 1 #Number of atoms -0.981274803 0.861285385 0.838442496 0 0 0 +0.981274803 0.861285385 0.838442496 1 1 1 H 0.0 4 1.023557202 0.758025625 0.66351336 0 0 0 -0.78075702 0.889445935 0.837363468 0 0 0 -1.064091613 1.043438905 0.840995502 0 0 0 -1.039321214 0.756530859 1.009609207 0 0 0 +0.78075702 0.889445935 0.837363468 1 0 1 +1.064091613 1.043438905 0.840995502 1 0 1 +1.039321214 0.756530859 1.009609207 0 1 1 diff --git a/tests/abacus.scf/stru_test b/tests/abacus.scf/stru_test index 22d619c9..e4036409 100644 --- a/tests/abacus.scf/stru_test +++ b/tests/abacus.scf/stru_test @@ -26,7 +26,7 @@ C H 0.0 4 -5.416431453540 4.011298860305 3.511161492417 1 1 1 -4.131588222365 4.706745191323 4.431136645083 1 1 1 -5.630930319126 5.521640894956 4.450356541303 1 1 1 -5.499851012568 4.003388899277 5.342621842622 1 1 1 +5.416431453540 4.011298860305 3.511161492417 0 0 0 +4.131588222365 4.706745191323 4.431136645083 1 0 1 +5.630930319126 5.521640894956 4.450356541303 1 0 1 +5.499851012568 4.003388899277 5.342621842622 0 1 1 diff --git a/tests/poscars/POSCAR.oh.err1 b/tests/poscars/POSCAR.oh.err1 new file mode 100644 index 00000000..1b7521e6 --- /dev/null +++ b/tests/poscars/POSCAR.oh.err1 @@ -0,0 +1,11 @@ +Cubic BN + 3.57 + 0.00 0.50 0.50 + 0.45 0.00 0.50 + 0.55 0.51 0.00 + O H + 1 1 +Selective dynamics +Cartesian + 0.00 0.00 0.00 T T F + 0.25 0.25 0.25 F F diff --git a/tests/poscars/POSCAR.oh.err2 b/tests/poscars/POSCAR.oh.err2 new file mode 100644 index 00000000..ed52d4d0 --- /dev/null +++ b/tests/poscars/POSCAR.oh.err2 @@ -0,0 +1,11 @@ +Cubic BN + 3.57 + 0.00 0.50 0.50 + 0.45 0.00 0.50 + 0.55 0.51 0.00 + O H + 1 1 +Selective dynamics +Cartesian + 0.00 0.00 0.00 T T F + 0.25 0.25 0.25 a T F diff --git a/tests/test_abacus_stru_dump.py b/tests/test_abacus_stru_dump.py index 4549b6d1..8891dee3 100644 --- a/tests/test_abacus_stru_dump.py +++ b/tests/test_abacus_stru_dump.py @@ -141,8 +141,52 @@ def test_dump_spin(self): 4 5.416431453540 4.011298860305 3.511161492417 1 1 1 mag 4.000000000000 5.000000000000 6.000000000000 4.131588222365 4.706745191323 4.431136645083 1 1 1 mag 1.000000000000 1.000000000000 1.000000000000 -5.630930319126 5.521640894956 4.450356541303 1 1 1 mag 2.000000000000 2.000000000000 2.000000000000 -5.499851012568 4.003388899277 5.342621842622 1 1 1 mag 3.000000000000 3.000000000000 3.000000000000 +5.630930319126 5.521640894956 4.450356541303 0 0 0 mag 2.000000000000 2.000000000000 2.000000000000 +5.499851012568 4.003388899277 5.342621842622 0 0 0 mag 3.000000000000 3.000000000000 3.000000000000 +""" + self.assertTrue(stru_ref in c) + + def test_dump_move_from_vasp(self): + self.system = dpdata.System() + self.system.from_vasp_poscar(os.path.join("poscars", "POSCAR.oh.c")) + self.system.to( + "abacus/stru", + "STRU_tmp", + pp_file={"O": "O.upf", "H": "H.upf"}, + ) + assert os.path.isfile("STRU_tmp") + with open("STRU_tmp") as f: + c = f.read() + + stru_ref = """O +0.0 +1 +0.000000000000 0.000000000000 0.000000000000 1 1 0 +H +0.0 +1 +1.262185604418 0.701802783513 0.551388341420 0 0 0 +""" + self.assertTrue(stru_ref in c) + + self.system.to( + "abacus/stru", + "STRU_tmp", + pp_file={"O": "O.upf", "H": "H.upf"}, + move=[[True, False, True], [False, True, False]], + ) + assert os.path.isfile("STRU_tmp") + with open("STRU_tmp") as f: + c = f.read() + + stru_ref = """O +0.0 +1 +0.000000000000 0.000000000000 0.000000000000 1 0 1 +H +0.0 +1 +1.262185604418 0.701802783513 0.551388341420 0 1 0 """ self.assertTrue(stru_ref in c) diff --git a/tests/test_vasp_poscar_dump.py b/tests/test_vasp_poscar_dump.py index 62f21598..d5522811 100644 --- a/tests/test_vasp_poscar_dump.py +++ b/tests/test_vasp_poscar_dump.py @@ -34,6 +34,21 @@ def setUp(self): self.system = dpdata.System() self.system.from_vasp_poscar("tmp.POSCAR") + def test_dump_move_flags(self): + tmp_system = dpdata.System() + tmp_system.from_vasp_poscar(os.path.join("poscars", "POSCAR.oh.c")) + tmp_system.to_vasp_poscar("tmp.POSCAR") + self.system = dpdata.System() + self.system.from_vasp_poscar("tmp.POSCAR") + with open("tmp.POSCAR") as f: + content = f.read() + + stru_ref = """Cartesian + 0.0000000000 0.0000000000 0.0000000000 T T F + 1.2621856044 0.7018027835 0.5513883414 F F F +""" + self.assertTrue(stru_ref in content) + class TestPOSCARSkipZeroAtomNumb(unittest.TestCase): def tearDown(self): diff --git a/tests/test_vasp_poscar_to_system.py b/tests/test_vasp_poscar_to_system.py index 7457d33d..9e5d7f37 100644 --- a/tests/test_vasp_poscar_to_system.py +++ b/tests/test_vasp_poscar_to_system.py @@ -14,6 +14,37 @@ def setUp(self): self.system = dpdata.System() self.system.from_vasp_poscar(os.path.join("poscars", "POSCAR.oh.c")) + def test_move_flags(self): + expected = np.array([[[True, True, False], [False, False, False]]]) + self.assertTrue(np.array_equal(self.system["move"], expected)) + + +class TestPOSCARMoveFlags(unittest.TestCase): + def setUp(self): + self.tmp_file = "POSCAR.tmp.1" + + def tearDown(self): + if os.path.exists(self.tmp_file): + os.remove(self.tmp_file) + + def test_move_flags_error1(self): + with self.assertRaisesRegex(RuntimeError, "Invalid move flags.*?"): + dpdata.System().from_vasp_poscar(os.path.join("poscars", "POSCAR.oh.err1")) + + def test_move_flags_error2(self): + with self.assertRaisesRegex(RuntimeError, "Invalid move flag: a"): + dpdata.System().from_vasp_poscar(os.path.join("poscars", "POSCAR.oh.err2")) + + def test_move_flags_error3(self): + system = dpdata.System().from_vasp_poscar( + os.path.join("poscars", "POSCAR.oh.c") + ) + system.data["move"] = np.array([[[True, True], [False, False]]]) + with self.assertRaisesRegex( + RuntimeError, "Invalid move flags:.*?should be a list of 3 bools" + ): + system.to_vasp_poscar(self.tmp_file) + class TestPOSCARDirect(unittest.TestCase, TestPOSCARoh): def setUp(self):