Skip to content

Commit 7853b7b

Browse files
pxlxingliangpre-commit-ci[bot]wanghan-iapcm
authored
add spin for lammps (#738)
Reference #728 to add the spin information for lammps. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: root <pxlxingliang> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Han Wang <[email protected]>
1 parent cdbbe1b commit 7853b7b

File tree

8 files changed

+493
-11
lines changed

8 files changed

+493
-11
lines changed

dpdata/lammps/dump.py

+103-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,91 @@ def load_file(fname: FileType, begin=0, step=1):
197197
buff.append(line)
198198

199199

200-
def system_data(lines, type_map=None, type_idx_zero=True, unwrap=False):
200+
def get_spin_keys(inputfile):
201+
"""
202+
Read input file and get the keys for spin info in dump.
203+
204+
Parameters
205+
----------
206+
inputfile : str
207+
Path to the input file.
208+
209+
Returns
210+
-------
211+
list or None
212+
List of spin info keys if found, None otherwise.
213+
"""
214+
if inputfile is None:
215+
return None
216+
217+
if not os.path.isfile(inputfile):
218+
warnings.warn(f"Input file {inputfile} not found.")
219+
return None
220+
221+
with open(inputfile) as f:
222+
for line in f.readlines():
223+
ls = line.split()
224+
if (
225+
len(ls) > 7
226+
and ls[0] == "compute"
227+
and all(key in ls for key in ["sp", "spx", "spy", "spz"])
228+
):
229+
compute_name = ls[1]
230+
return [
231+
f"c_{compute_name}[{ls.index(key) - 3}]"
232+
for key in ["sp", "spx", "spy", "spz"]
233+
]
234+
235+
return None
236+
237+
238+
def get_spin(lines, spin_keys):
239+
"""
240+
Get the spin info from the dump file.
241+
242+
Parameters
243+
----------
244+
lines : list
245+
The content of the dump file.
246+
spin_keys : list
247+
The keys for spin info in dump file.
248+
the spin info is stored in sp, spx, spy, spz or spin_keys, which is the spin norm and the spin vector
249+
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
250+
"""
251+
blk, head = _get_block(lines, "ATOMS")
252+
heads = head.split()
253+
254+
if spin_keys is not None and all(i in heads for i in spin_keys):
255+
key = spin_keys
256+
else:
257+
return None
258+
259+
try:
260+
idx_id = heads.index("id") - 2
261+
idx_sp, idx_spx, idx_spy, idx_spz = (heads.index(k) - 2 for k in key)
262+
263+
norm = []
264+
vec = []
265+
atom_ids = []
266+
for line in blk:
267+
words = line.split()
268+
norm.append([float(words[idx_sp])])
269+
vec.append(
270+
[float(words[idx_spx]), float(words[idx_spy]), float(words[idx_spz])]
271+
)
272+
atom_ids.append(int(words[idx_id]))
273+
274+
spin = np.array(norm) * np.array(vec)
275+
atom_ids, spin = zip(*sorted(zip(atom_ids, spin)))
276+
return np.array(spin)
277+
except (ValueError, IndexError) as e:
278+
warnings.warn(f"Error processing spin data: {str(e)}")
279+
return None
280+
281+
282+
def system_data(
283+
lines, type_map=None, type_idx_zero=True, unwrap=False, input_file=None
284+
):
201285
array_lines = split_traj(lines)
202286
lines = array_lines[0]
203287
system = {}
@@ -216,6 +300,12 @@ def system_data(lines, type_map=None, type_idx_zero=True, unwrap=False):
216300
system["cells"] = [np.array(cell)]
217301
system["atom_types"] = get_atype(lines, type_idx_zero=type_idx_zero)
218302
system["coords"] = [safe_get_posi(lines, cell, np.array(orig), unwrap)]
303+
spin_keys = get_spin_keys(input_file)
304+
spin = get_spin(lines, spin_keys)
305+
has_spin = False
306+
if spin is not None:
307+
system["spins"] = [spin]
308+
has_spin = True
219309
for ii in range(1, len(array_lines)):
220310
bounds, tilt = get_dumpbox(array_lines[ii])
221311
orig, cell = dumpbox2box(bounds, tilt)
@@ -228,6 +318,18 @@ def system_data(lines, type_map=None, type_idx_zero=True, unwrap=False):
228318
system["coords"].append(
229319
safe_get_posi(array_lines[ii], cell, np.array(orig), unwrap)[idx]
230320
)
321+
if has_spin:
322+
spin = get_spin(array_lines[ii], spin_keys)
323+
if spin is not None:
324+
system["spins"].append(spin[idx])
325+
else:
326+
warnings.warn(
327+
f"Warning: spin info is not found in frame {ii}, remove spin info."
328+
)
329+
system.pop("spins")
330+
has_spin = False
331+
if has_spin:
332+
system["spins"] = np.array(system["spins"])
231333
system["cells"] = np.array(system["cells"])
232334
system["coords"] = np.array(system["coords"])
233335
return system

dpdata/lammps/lmp.py

+66-7
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,19 @@ def get_posi(lines):
127127
return np.array(posis)
128128

129129

130+
def get_spins(lines):
131+
atom_lines = get_atoms(lines)
132+
if len(atom_lines[0].split()) < 8:
133+
return None
134+
spins_ori = []
135+
spins_norm = []
136+
for ii in atom_lines:
137+
iis = ii.split()
138+
spins_ori.append([float(jj) for jj in iis[5:8]])
139+
spins_norm.append([float(iis[-1])])
140+
return np.array(spins_ori) * np.array(spins_norm)
141+
142+
130143
def get_lmpbox(lines):
131144
box_info = []
132145
tilt = np.zeros(3)
@@ -168,6 +181,11 @@ def system_data(lines, type_map=None, type_idx_zero=True):
168181
system["coords"] = [get_posi(lines)]
169182
system["cells"] = np.array(system["cells"])
170183
system["coords"] = np.array(system["coords"])
184+
185+
spins = get_spins(lines)
186+
if spins is not None:
187+
system["spins"] = np.array([spins])
188+
171189
return system
172190

173191

@@ -216,14 +234,55 @@ def from_system_data(system, f_idx=0):
216234
+ ptr_float_fmt
217235
+ "\n"
218236
)
219-
for ii in range(natoms):
220-
ret += coord_fmt % (
221-
ii + 1,
222-
system["atom_types"][ii] + 1,
223-
system["coords"][f_idx][ii][0] - system["orig"][0],
224-
system["coords"][f_idx][ii][1] - system["orig"][1],
225-
system["coords"][f_idx][ii][2] - system["orig"][2],
237+
238+
if "spins" in system:
239+
coord_fmt = (
240+
coord_fmt.strip("\n")
241+
+ " "
242+
+ ptr_float_fmt
243+
+ " "
244+
+ ptr_float_fmt
245+
+ " "
246+
+ ptr_float_fmt
247+
+ " "
248+
+ ptr_float_fmt
249+
+ "\n"
226250
)
251+
spins_norm = np.linalg.norm(system["spins"][f_idx], axis=1)
252+
for ii in range(natoms):
253+
if "spins" in system:
254+
if spins_norm[ii] != 0:
255+
ret += coord_fmt % (
256+
ii + 1,
257+
system["atom_types"][ii] + 1,
258+
system["coords"][f_idx][ii][0] - system["orig"][0],
259+
system["coords"][f_idx][ii][1] - system["orig"][1],
260+
system["coords"][f_idx][ii][2] - system["orig"][2],
261+
system["spins"][f_idx][ii][0] / spins_norm[ii],
262+
system["spins"][f_idx][ii][1] / spins_norm[ii],
263+
system["spins"][f_idx][ii][2] / spins_norm[ii],
264+
spins_norm[ii],
265+
)
266+
else:
267+
ret += coord_fmt % (
268+
ii + 1,
269+
system["atom_types"][ii] + 1,
270+
system["coords"][f_idx][ii][0] - system["orig"][0],
271+
system["coords"][f_idx][ii][1] - system["orig"][1],
272+
system["coords"][f_idx][ii][2] - system["orig"][2],
273+
system["spins"][f_idx][ii][0],
274+
system["spins"][f_idx][ii][1],
275+
system["spins"][f_idx][ii][2] + 1,
276+
spins_norm[ii],
277+
)
278+
else:
279+
ret += coord_fmt % (
280+
ii + 1,
281+
system["atom_types"][ii] + 1,
282+
system["coords"][f_idx][ii][0] - system["orig"][0],
283+
system["coords"][f_idx][ii][1] - system["orig"][1],
284+
system["coords"][f_idx][ii][2] - system["orig"][2],
285+
)
227286
return ret
228287

229288

dpdata/plugins/lammps.py

+53-3
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,40 @@
22

33
from typing import TYPE_CHECKING
44

5+
import numpy as np
6+
57
import dpdata.lammps.dump
68
import dpdata.lammps.lmp
9+
from dpdata.data_type import Axis, DataType
710
from dpdata.format import Format
811
from dpdata.utils import open_file
912

1013
if TYPE_CHECKING:
1114
from dpdata.utils import FileType
1215

1316

17+
def register_spin(data):
18+
if "spins" in data:
19+
dt = DataType(
20+
"spins",
21+
np.ndarray,
22+
(Axis.NFRAMES, Axis.NATOMS, 3),
23+
required=False,
24+
deepmd_name="spin",
25+
)
26+
dpdata.System.register_data_type(dt)
27+
28+
1429
@Format.register("lmp")
1530
@Format.register("lammps/lmp")
1631
class LAMMPSLmpFormat(Format):
1732
@Format.post("shift_orig_zero")
1833
def from_system(self, file_name: FileType, type_map=None, **kwargs):
1934
with open_file(file_name) as fp:
2035
lines = [line.rstrip("\n") for line in fp]
21-
return dpdata.lammps.lmp.to_system_data(lines, type_map)
36+
data = dpdata.lammps.lmp.to_system_data(lines, type_map)
37+
register_spin(data)
38+
return data
2239

2340
def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs):
2441
"""Dump the system in lammps data format.
@@ -45,7 +62,40 @@ def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs):
4562
class LAMMPSDumpFormat(Format):
4663
@Format.post("shift_orig_zero")
4764
def from_system(
48-
self, file_name, type_map=None, begin=0, step=1, unwrap=False, **kwargs
65+
self,
66+
file_name: str,
67+
type_map: list[str] = None,
68+
begin: int = 0,
69+
step: int = 1,
70+
unwrap: bool = False,
71+
input_file: str = None,
72+
**kwargs,
4973
):
74+
"""Read the data from a lammps dump file.
75+
76+
Parameters
77+
----------
78+
file_name : str
79+
The dump file name
80+
type_map : List[str], optional
81+
The atom type list
82+
begin : int, optional
83+
The begin step
84+
step : int, optional
85+
The step
86+
unwrap : bool, optional
87+
Whether to unwrap the coordinates
88+
input_file : str, optional
89+
The input file name
90+
91+
Returns
92+
-------
93+
dict
94+
The system data
95+
"""
5096
lines = dpdata.lammps.dump.load_file(file_name, begin=begin, step=step)
51-
return dpdata.lammps.dump.system_data(lines, type_map, unwrap=unwrap)
97+
data = dpdata.lammps.dump.system_data(
98+
lines, type_map, unwrap=unwrap, input_file=input_file
99+
)
100+
register_spin(data)
101+
return data

tests/lammps/in.lmp

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
compute spin all property/atom sp spx spy spz fmx fmy fmz fx fy fz
2+
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]

tests/lammps/spin.lmp

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
2 atoms
3+
2 atom types
4+
0.0000000000 2.5243712000 xlo xhi
5+
0.0000000000 2.0430257000 ylo yhi
6+
0.0000000000 2.2254033000 zlo zhi
7+
1.2621856000 1.2874292000 0.7485898000 xy xz yz
8+
9+
Atoms # atomic
10+
11+
1 1 0.0000000000 0.0000000000 0.0000000000 0.6000000000 0.8000000000 0.0000000000 5.0000000000
12+
2 2 1.2621856000 0.7018028000 0.5513885000 0.0000000000 0.8000000000 0.6000000000 5.0000000000

tests/lammps/traj.dump

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
ITEM: TIMESTEP
2+
0
3+
ITEM: NUMBER OF ATOMS
4+
17
5+
ITEM: BOX BOUNDS xy xz yz pp pp pp
6+
-4.0080511965879438e-02 5.7039029418994556e+00 -5.9179115295410201e-03
7+
1.4436085788922526e-02 5.6674744441011660e+00 -1.1487414836883500e-02
8+
7.8239288740356017e-03 5.6734038274259646e+00 6.8277359008788905e-04
9+
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]
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
11+
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
12+
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
13+
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
14+
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
15+
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
16+
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
17+
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
18+
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
19+
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
20+
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
21+
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
22+
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
23+
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
24+
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
25+
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
26+
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
27+
ITEM: TIMESTEP
28+
10
29+
ITEM: NUMBER OF ATOMS
30+
17
31+
ITEM: BOX BOUNDS xy xz yz pp pp pp
32+
-4.0080511965879438e-02 5.7039029418994556e+00 -5.9179115295410201e-03
33+
1.4436085788922526e-02 5.6674744441011660e+00 -1.1487414836883500e-02
34+
7.8239288740356017e-03 5.6734038274259646e+00 6.8277359008788905e-04
35+
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]
36+
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
37+
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
38+
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
39+
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
40+
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
41+
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
42+
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
43+
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
44+
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
45+
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
46+
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
47+
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
48+
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
49+
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
50+
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
51+
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
52+
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

0 commit comments

Comments
 (0)