Skip to content

Commit 7c9be86

Browse files
add spin for abacus/stru (#751)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced data retrieval to include magnetic moment information in atomic coordinates. - Introduced a new function for transforming magnetic moment data into Cartesian coordinates. - Added structured input format for iron simulations, detailing atomic and magnetic properties. - **Bug Fixes** - Improved handling of magnetic data registration in various format classes. - **Tests** - Added a new test method to validate the reading of spin data from the structured input file. - Updated atomic positions in tests to include magnetic moment information. <!-- 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>
1 parent 32f832b commit 7c9be86

File tree

7 files changed

+113
-18
lines changed

7 files changed

+113
-18
lines changed

dpdata/abacus/md.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def get_frame(fname):
167167
with open_file(geometry_path_in) as fp:
168168
geometry_inlines = fp.read().split("\n")
169169
celldm, cell = get_cell(geometry_inlines)
170-
atom_names, natoms, types, coords, move = get_coords(
170+
atom_names, natoms, types, coords, move, magmom = get_coords(
171171
celldm, cell, geometry_inlines, inlines
172172
)
173173
# This coords is not to be used.

dpdata/abacus/relax.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def get_frame(fname):
183183
with open_file(geometry_path_in) as fp:
184184
geometry_inlines = fp.read().split("\n")
185185
celldm, cell = get_cell(geometry_inlines)
186-
atom_names, natoms, types, coord_tmp, move = get_coords(
186+
atom_names, natoms, types, coord_tmp, move, magmom = get_coords(
187187
celldm, cell, geometry_inlines, inlines
188188
)
189189

dpdata/abacus/scf.py

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,51 @@ def parse_stru_pos(pos_line):
258258
return pos, move, velocity, magmom, angle1, angle2, constrain, lambda1
259259

260260

261+
def get_atom_mag_cartesian(atommag, angle1, angle2):
262+
"""Transform atommag, angle1, angle2 to magmom in cartesian coordinates.
263+
264+
Parameters
265+
----------
266+
atommag : float/list of float/None
267+
Atom magnetic moment.
268+
angle1 : float/None
269+
value of angle1.
270+
angle2 : float/None
271+
value of angle2.
272+
ABACUS support defining mag, angle1, angle2 at the same time.
273+
angle1 is the angle between z-axis and real spin (in degrees).
274+
angle2 is the angle between x-axis and real spin projection in xy-plane (in degrees).
275+
If only mag is defined, then transfer it to magmom directly.
276+
And if mag, angle1, angle2 are defined, then mag is only the norm of magmom, and the direction is defined by angle1 and angle2.
277+
"""
278+
if atommag is None:
279+
return None
280+
if not (isinstance(atommag, list) or isinstance(atommag, float)):
281+
raise RuntimeError(f"Invalid atommag: {atommag}")
282+
283+
if angle1 is None and angle2 is None:
284+
if isinstance(atommag, list):
285+
return atommag
286+
else:
287+
return [0, 0, atommag]
288+
else:
289+
a1 = 0
290+
a2 = 0
291+
if angle1 is not None:
292+
a1 = angle1
293+
if angle2 is not None:
294+
a2 = angle2
295+
if isinstance(atommag, list):
296+
mag_norm = np.linalg.norm(atommag)
297+
else:
298+
mag_norm = atommag
299+
return [
300+
mag_norm * np.sin(np.radians(a1)) * np.cos(np.radians(a2)),
301+
mag_norm * np.sin(np.radians(a1)) * np.sin(np.radians(a2)),
302+
mag_norm * np.cos(np.radians(a1)),
303+
]
304+
305+
261306
def get_coords(celldm, cell, geometry_inlines, inlines=None):
262307
coords_lines = get_stru_block(geometry_inlines, "ATOMIC_POSITIONS")
263308
# assuming that ATOMIC_POSITIONS is at the bottom of the STRU file
@@ -268,16 +313,15 @@ def get_coords(celldm, cell, geometry_inlines, inlines=None):
268313
coords = [] # coordinations of atoms
269314
move = [] # move flag of each atom
270315
velocity = [] # velocity of each atom
271-
mag = [] # magnetic moment of each atom
272-
angle1 = [] # angle1 of each atom
273-
angle2 = [] # angle2 of each atom
316+
mags = [] # magnetic moment of each atom
274317
sc = [] # spin constraint flag of each atom
275318
lambda_ = [] # lambda of each atom
276319

277320
ntype = get_nele_from_stru(geometry_inlines)
278321
line_idx = 1 # starting line of first element
279322
for it in range(ntype):
280323
atom_names.append(coords_lines[line_idx].split()[0])
324+
atom_type_mag = float(coords_lines[line_idx + 1].split()[0])
281325
line_idx += 2
282326
atom_numbs.append(int(coords_lines[line_idx].split()[0]))
283327
line_idx += 1
@@ -302,17 +346,20 @@ def get_coords(celldm, cell, geometry_inlines, inlines=None):
302346
if imove is not None:
303347
move.append(imove)
304348
velocity.append(ivelocity)
305-
mag.append(imagmom)
306-
angle1.append(iangle1)
307-
angle2.append(iangle2)
308349
sc.append(iconstrain)
309350
lambda_.append(ilambda1)
310351

352+
# calculate the magnetic moment in cartesian coordinates
353+
mag = get_atom_mag_cartesian(imagmom, iangle1, iangle2)
354+
if mag is None:
355+
mag = [0, 0, atom_type_mag]
356+
mags.append(mag)
357+
311358
line_idx += 1
312359
coords = np.array(coords) # need transformation!!!
313360
atom_types = np.array(atom_types)
314361
move = np.array(move, dtype=bool)
315-
return atom_names, atom_numbs, atom_types, coords, move
362+
return atom_names, atom_numbs, atom_types, coords, move, mags
316363

317364

318365
def get_energy(outlines):
@@ -479,9 +526,12 @@ def get_frame(fname):
479526
outlines = fp.read().split("\n")
480527

481528
celldm, cell = get_cell(geometry_inlines)
482-
atom_names, natoms, types, coords, move = get_coords(
483-
celldm, cell, geometry_inlines, inlines
529+
atom_names, natoms, types, coords, move, magmom = (
530+
get_coords( # here the magmom is the initial magnetic moment in STRU
531+
celldm, cell, geometry_inlines, inlines
532+
)
484533
)
534+
485535
magmom, magforce = get_mag_force(outlines)
486536
if len(magmom) > 0:
487537
magmom = magmom[-1:]
@@ -565,7 +615,7 @@ def get_frame_from_stru(fname):
565615
nele = get_nele_from_stru(geometry_inlines)
566616
inlines = [f"ntype {nele}"]
567617
celldm, cell = get_cell(geometry_inlines)
568-
atom_names, natoms, types, coords, move = get_coords(
618+
atom_names, natoms, types, coords, move, magmom = get_coords(
569619
celldm, cell, geometry_inlines, inlines
570620
)
571621
data = {}
@@ -575,6 +625,7 @@ def get_frame_from_stru(fname):
575625
data["cells"] = cell[np.newaxis, :, :]
576626
data["coords"] = coords[np.newaxis, :, :]
577627
data["orig"] = np.zeros(3)
628+
data["spins"] = np.array([magmom])
578629
if len(move) > 0:
579630
data["move"] = move[np.newaxis, :, :]
580631

dpdata/plugins/abacus.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
@Format.register("stru")
2121
class AbacusSTRUFormat(Format):
2222
def from_system(self, file_name, **kwargs):
23-
return dpdata.abacus.scf.get_frame_from_stru(file_name)
23+
data = dpdata.abacus.scf.get_frame_from_stru(file_name)
24+
register_mag_data(data)
25+
return data
2426

2527
def to_system(self, data, file_name: FileType, frame_idx=0, **kwargs):
2628
"""Dump the system into ABACUS STRU format file.
@@ -55,6 +57,7 @@ def register_mag_data(data):
5557
required=False,
5658
deepmd_name="spin",
5759
)
60+
dpdata.System.register_data_type(dt)
5861
dpdata.LabeledSystem.register_data_type(dt)
5962
if "force_mags" in data:
6063
dt = DataType(
@@ -64,6 +67,7 @@ def register_mag_data(data):
6467
required=False,
6568
deepmd_name="force_mag",
6669
)
70+
dpdata.System.register_data_type(dt)
6771
dpdata.LabeledSystem.register_data_type(dt)
6872

6973

tests/abacus.scf/stru_test

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ Cartesian # Cartesian(Unit is LATTICE_CONSTANT)
2222
C
2323
0.0
2424
1
25-
5.192682633809 4.557725978258 4.436846615358 1 1 1
25+
5.192682633809 4.557725978258 4.436846615358 1 1 1 mag 0.000000000000 0.000000000000 0.000000000000
2626
H
2727
0.0
2828
4
29-
5.416431453540 4.011298860305 3.511161492417 0 0 0
30-
4.131588222365 4.706745191323 4.431136645083 1 0 1
31-
5.630930319126 5.521640894956 4.450356541303 1 0 1
32-
5.499851012568 4.003388899277 5.342621842622 0 1 1
29+
5.416431453540 4.011298860305 3.511161492417 0 0 0 mag 0.000000000000 0.000000000000 0.000000000000
30+
4.131588222365 4.706745191323 4.431136645083 1 0 1 mag 0.000000000000 0.000000000000 0.000000000000
31+
5.630930319126 5.521640894956 4.450356541303 1 0 1 mag 0.000000000000 0.000000000000 0.000000000000
32+
5.499851012568 4.003388899277 5.342621842622 0 1 1 mag 0.000000000000 0.000000000000 0.000000000000

tests/abacus.spin/STRU.spin

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
ATOMIC_SPECIES
2+
Fe 55.845 Fe.upf
3+
4+
NUMERICAL_ORBITAL
5+
Fe_gga_10au_200.0Ry_4s2p2d1f.orb
6+
7+
LATTICE_CONSTANT
8+
1.880277359
9+
LATTICE_VECTORS
10+
2.8274254848 0.0000000000 0.0000000000 #latvec1
11+
0.0000000000 2.8274254848 0.0000000000 #latvec2
12+
0.0000000000 0.0000000000 2.8274254848 #latvec3
13+
14+
ATOMIC_POSITIONS
15+
Direct
16+
17+
Fe #label
18+
1 #magnetism
19+
4 #number of atoms
20+
0.0000000000 0.000000000 0.000000000 mag 0 0 2
21+
0.1000000000 0.1000000000 0.1000000000 mag 3
22+
0.2000000000 0.2000000000 0.2000000000 mag 3 angle1 90
23+
0.3000000000 0.3000000000 0.3000000000 mag 3 4 0 angle1 90 angle2 90
24+

tests/test_abacus_spin.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,19 @@ def test_md(self):
155155
np.testing.assert_almost_equal(
156156
data["force_mags"], sys2.data["force_mags"], decimal=8
157157
)
158+
159+
def test_read_stru_spin(self):
160+
mysys = dpdata.System("abacus.spin/STRU.spin", fmt="abacus/stru")
161+
self.assertTrue("spins" in mysys.data)
162+
print(mysys.data["spins"])
163+
164+
"""
165+
0.0000000000 0.000000000 0.000000000 mag 0 0 2
166+
0.1000000000 0.1000000000 0.1000000000 mag 3
167+
0.2000000000 0.2000000000 0.2000000000 mag 3 angle1 90
168+
0.3000000000 0.3000000000 0.3000000000 mag 3 4 0 angle1 90 angle2 90
169+
"""
170+
np.testing.assert_almost_equal(mysys.data["spins"][0][0], [0, 0, 2], decimal=8)
171+
np.testing.assert_almost_equal(mysys.data["spins"][0][1], [0, 0, 3], decimal=8)
172+
np.testing.assert_almost_equal(mysys.data["spins"][0][2], [3, 0, 0], decimal=8)
173+
np.testing.assert_almost_equal(mysys.data["spins"][0][3], [0, 5, 0], decimal=8)

0 commit comments

Comments
 (0)