Skip to content

Commit b70e3e0

Browse files
Liu-RXLiuRenxi
andauthored
Add unlabeled abacus STRU read/dump interface (#303)
* adapt abacus/md interface to MD output without stress calculation. * add unit test for abacus md without stress output. * Add files via upload * add unlabeled read and dump interface for ABACUS STRU format. * add assertion failed reason for abacus/md interface * fix open file method * fix typo; introduce np.testing in abacus tests. * remove useless if clause Co-authored-by: LiuRenxi <[email protected]>
1 parent e4919ca commit b70e3e0

File tree

6 files changed

+221
-68
lines changed

6 files changed

+221
-68
lines changed

dpdata/abacus/scf.py

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os,sys
22
import numpy as np
33
from ..unit import EnergyConversion, PressureConversion, LengthConversion
4-
4+
import re
55
bohr2ang = LengthConversion("bohr", "angstrom").value()
66
ry2ev = EnergyConversion("rydberg", "eV").value()
77
kbar2evperang3 = PressureConversion("kbar", "eV/angstrom^3").value()
@@ -16,10 +16,10 @@ def get_block (lines, keyword, skip = 0, nlines = None):
1616
found = True
1717
blk_idx = idx + 1 + skip
1818
line_idx = 0
19-
while len(lines[blk_idx].split("\s+")) == 0:
19+
while len(re.split("\s+", lines[blk_idx])) == 0:
2020
blk_idx += 1
2121
while line_idx < nlines and blk_idx != len(lines):
22-
if len(lines[blk_idx].split("\s+")) == 0 or lines[blk_idx] == "":
22+
if len(re.split("\s+", lines[blk_idx])) == 0 or lines[blk_idx] == "":
2323
blk_idx+=1
2424
continue
2525
ret.append(lines[blk_idx])
@@ -184,6 +184,100 @@ def get_frame (fname):
184184
# print("virial = ", data['virials'])
185185
return data
186186

187+
def get_nele_from_stru(geometry_inlines):
188+
key_words_list = ["ATOMIC_SPECIES", "NUMERICAL_ORBITAL", "LATTICE_CONSTANT", "LATTICE_VECTORS", "ATOMIC_POSITIONS", "NUMERICAL_DESCRIPTOR"]
189+
keyword_sequence = []
190+
keyword_line_index = []
191+
atom_names = []
192+
atom_numbs = []
193+
for iline, line in enumerate(geometry_inlines):
194+
if line.split() == []:
195+
continue
196+
have_key_word = False
197+
for keyword in key_words_list:
198+
if keyword in line and keyword == line.split()[0]:
199+
keyword_sequence.append(keyword)
200+
keyword_line_index.append(iline)
201+
assert(len(keyword_line_index) == len(keyword_sequence))
202+
assert(len(keyword_sequence) > 0)
203+
keyword_line_index.append(len(geometry_inlines))
204+
205+
nele = 0
206+
for idx, keyword in enumerate(keyword_sequence):
207+
if keyword == "ATOMIC_SPECIES":
208+
for iline in range(keyword_line_index[idx]+1, keyword_line_index[idx+1]):
209+
if len(re.split("\s+", geometry_inlines[iline])) >= 3:
210+
nele += 1
211+
return nele
212+
213+
def get_frame_from_stru(fname):
214+
assert(type(fname) == str)
215+
with open(fname, 'r') as fp:
216+
geometry_inlines = fp.read().split('\n')
217+
nele = get_nele_from_stru(geometry_inlines)
218+
inlines = ["ntype %d" %nele]
219+
celldm, cell = get_cell(geometry_inlines)
220+
atom_names, natoms, types, coords = get_coords(celldm, cell, geometry_inlines, inlines)
221+
data = {}
222+
data['atom_names'] = atom_names
223+
data['atom_numbs'] = natoms
224+
data['atom_types'] = types
225+
data['cells'] = cell[np.newaxis, :, :]
226+
data['coords'] = coords[np.newaxis, :, :]
227+
data['orig'] = np.zeros(3)
228+
229+
return data
230+
231+
def make_unlabeled_stru(data, frame_idx, pp_file=None, numerical_orbital=None, numerical_descriptor=None, mass=None):
232+
out = "ATOMIC_SPECIES\n"
233+
for iele in range(len(data['atom_names'])):
234+
out += data['atom_names'][iele] + " "
235+
if mass is not None:
236+
out += "%d "%mass[iele]
237+
else:
238+
out += "1 "
239+
if pp_file is not None:
240+
out += "%s\n"%pp_file[iele]
241+
else:
242+
out += "\n"
243+
out += "\n"
244+
245+
if numerical_orbital is not None:
246+
assert(len(numerical_orbital) == len(data['atom_names']))
247+
out += "NUMERICAL_ORBITAL\n"
248+
for iele in range(len(numerical_orbital)):
249+
out += "%s\n"%numerical_orbital[iele]
250+
out += "\n"
251+
252+
if numerical_descriptor is not None:
253+
assert(type(numerical_descriptor) == str)
254+
out += "NUMERICAL_DESCRIPTOR\n%s\n"%numerical_descriptor
255+
out += "\n"
256+
257+
out += "LATTICE_CONSTANT\n"
258+
out += str(1/bohr2ang) + "\n\n"
259+
260+
out += "LATTICE_VECTORS\n"
261+
for ix in range(3):
262+
for iy in range(3):
263+
out += str(data['cells'][frame_idx][ix][iy]) + " "
264+
out += "\n"
265+
out += "\n"
266+
267+
out += "ATOMIC_POSITIONS\n"
268+
out += "Cartesian # Cartesian(Unit is LATTICE_CONSTANT)\n"
269+
#ret += "\n"
270+
natom_tot = 0
271+
for iele in range(len(data['atom_names'])):
272+
out += data['atom_names'][iele] + "\n"
273+
out += "0.0\n"
274+
out += str(data['atom_numbs'][iele]) + "\n"
275+
for iatom in range(data['atom_numbs'][iele]):
276+
out += "%.12f %.12f %.12f %d %d %d\n" % (data['coords'][frame_idx][natom_tot, 0], data['coords'][frame_idx][natom_tot, 1], data['coords'][frame_idx][natom_tot, 2], 1, 1, 1)
277+
natom_tot += 1
278+
assert(natom_tot == sum(data['atom_numbs']))
279+
return out
280+
187281
#if __name__ == "__main__":
188282
# path = "/home/lrx/work/12_ABACUS_dpgen_interface/dpdata/dpdata/tests/abacus.scf"
189283
# data = get_frame(path)

dpdata/plugins/abacus.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,39 @@
22
import dpdata.abacus.md
33
from dpdata.format import Format
44

5+
@Format.register("abacus/stru")
6+
@Format.register("stru")
7+
class AbacusSTRUFormat(Format):
8+
def from_system(self, file_name, **kwargs):
9+
return dpdata.abacus.scf.get_frame_from_stru(file_name)
10+
11+
def to_system(self, data, file_name, frame_idx=0, **kwargs):
12+
"""
13+
Dump the system into ABACUS STRU format file.
14+
15+
Parameters
16+
----------
17+
file_name : str
18+
The output file name
19+
frame_idx : int
20+
The index of the frame to dump
21+
pp_file: list of string, optional
22+
List of pseudo potential files
23+
numerical_orbital: list of string, optional
24+
List of orbital files
25+
mass: list of float, optional
26+
List of atomic masses
27+
numerical_descriptor: str, optional
28+
numerical descriptor file
29+
"""
30+
31+
pp_file = kwargs.get('pp_file')
32+
numerical_orbital = kwargs.get('numerical_orbital')
33+
mass = kwargs.get('mass')
34+
numerical_descriptor = kwargs.get('numerical_descriptor')
35+
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)
36+
with open(file_name, "w") as fp:
37+
fp.write(stru_string)
538

639
@Format.register("abacus/scf")
740
@Format.register("abacus/pw/scf")

tests/abacus.scf/stru_test

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
ATOMIC_SPECIES
2+
C 12 C.upf
3+
H 1 H.upf
4+
5+
NUMERICAL_ORBITAL
6+
C.orb
7+
H.orb
8+
9+
NUMERICAL_DESCRIPTOR
10+
jle.orb
11+
12+
LATTICE_CONSTANT
13+
1.8897261246257702
14+
15+
LATTICE_VECTORS
16+
5.291772109029999 0.0 0.0
17+
0.0 5.291772109029999 0.0
18+
0.0 0.0 5.291772109029999
19+
20+
ATOMIC_POSITIONS
21+
Cartesian # Cartesian(Unit is LATTICE_CONSTANT)
22+
C
23+
0.0
24+
1
25+
5.192682633809 4.557725978258 4.436846615358 1 1 1
26+
H
27+
0.0
28+
4
29+
5.416431453540 4.011298860305 3.511161492417 1 1 1
30+
4.131588222365 4.706745191323 4.431136645083 1 1 1
31+
5.630930319126 5.521640894956 4.450356541303 1 1 1
32+
5.499851012568 4.003388899277 5.342621842622 1 1 1

tests/test_abacus_md.py

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,9 @@ def test_cell(self) :
2929
cell = bohr2ang * 28 * np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
3030
cell2 = bohr2ang * 5.1 * np.array([[1, 1, 0], [1, 0, 1], [0, 1, 1]])
3131
for idx in range(np.shape(self.system_water.data['cells'])[0]):
32-
for ii in range(cell.shape[0]) :
33-
for jj in range(cell.shape[1]) :
34-
self.assertAlmostEqual(self.system_water.data['cells'][idx][ii][jj], cell[ii][jj])
32+
np.testing.assert_almost_equal(cell, self.system_water.data['cells'][idx], decimal = 5)
3533
for idx in range(np.shape(self.system_Si.data['cells'])[0]):
36-
for ii in range(cell2.shape[0]) :
37-
for jj in range(cell2.shape[1]) :
38-
self.assertAlmostEqual(self.system_Si.data['cells'][idx][ii][jj], cell2[ii][jj])
34+
np.testing.assert_almost_equal(self.system_Si.data['cells'][idx], cell2, decimal = 5)
3935

4036
def test_coord(self) :
4137
with open('abacus.md/water_coord') as fp:
@@ -44,21 +40,15 @@ def test_coord(self) :
4440
coord.append([float(jj) for jj in ii.split()])
4541
coord = np.array(coord)
4642
coord = coord.reshape([5, 3, 3])
47-
for ii in range(coord.shape[0]) :
48-
for jj in range(coord.shape[1]) :
49-
for kk in range(coord.shape[2]):
50-
self.assertAlmostEqual(self.system_water.data['coords'][ii][jj][kk], coord[ii][jj][kk])
43+
np.testing.assert_almost_equal(self.system_water.data['coords'], coord, decimal = 5)
5144

5245
with open('abacus.md.nostress/Si_coord') as fp2:
5346
coord = []
5447
for ii in fp2 :
5548
coord.append([float(jj) for jj in ii.split()])
5649
coord = np.array(coord)
5750
coord = coord.reshape([4, 2, 3])
58-
for ii in range(coord.shape[0]) :
59-
for jj in range(coord.shape[1]) :
60-
for kk in range(coord.shape[2]):
61-
self.assertAlmostEqual(self.system_Si.data['coords'][ii][jj][kk], coord[ii][jj][kk])
51+
np.testing.assert_almost_equal(self.system_Si.data['coords'], coord, decimal = 5)
6252

6353
def test_force(self) :
6454
with open('abacus.md/water_force') as fp:
@@ -67,10 +57,7 @@ def test_force(self) :
6757
force.append([float(jj) for jj in ii.split()])
6858
force = np.array(force)
6959
force = force.reshape([5, 3, 3])
70-
for ii in range(force.shape[0]) :
71-
for jj in range(force.shape[1]) :
72-
for kk in range(force.shape[2]):
73-
self.assertAlmostEqual(self.system_water.data['forces'][ii][jj][kk], force[ii][jj][kk])
60+
np.testing.assert_almost_equal(self.system_water.data['forces'], force, decimal=5)
7461

7562

7663
with open('abacus.md.nostress/Si_force') as fp2:
@@ -79,10 +66,7 @@ def test_force(self) :
7966
force.append([float(jj) for jj in ii.split()])
8067
force = np.array(force)
8168
force = force.reshape([4, 2, 3])
82-
for ii in range(force.shape[0]) :
83-
for jj in range(force.shape[1]) :
84-
for kk in range(force.shape[2]):
85-
self.assertAlmostEqual(self.system_Si.data['forces'][ii][jj][kk], force[ii][jj][kk])
69+
np.testing.assert_almost_equal(self.system_Si.data['forces'], force, decimal=5)
8670

8771

8872
def test_virial(self) :
@@ -92,19 +76,14 @@ def test_virial(self) :
9276
virial.append([float(jj) for jj in ii.split()])
9377
virial = np.array(virial)
9478
virial = virial.reshape([5, 3, 3])
95-
for ii in range(virial.shape[0]) :
96-
for jj in range(virial.shape[1]) :
97-
for kk in range(virial.shape[2]) :
98-
self.assertAlmostEqual(self.system_water.data['virials'][ii][jj][kk], virial[ii][jj][kk])
79+
np.testing.assert_almost_equal(self.system_water.data['virials'], virial, decimal=5)
9980

10081
def test_energy(self) :
10182
ref_energy = np.array([-466.69285117, -466.69929051, -466.69829826, -466.70364664,
10283
-466.6976083])
10384
ref_energy2 = np.array([-211.77184603, -211.78111966, -211.79681663, -211.79875524])
104-
for ii in range(5):
105-
self.assertAlmostEqual(self.system_water.data['energies'][ii], ref_energy[ii])
106-
for ii in range(4):
107-
self.assertAlmostEqual(self.system_Si.data['energies'][ii], ref_energy2[ii])
85+
np.testing.assert_almost_equal(self.system_water.data['energies'], ref_energy)
86+
np.testing.assert_almost_equal(self.system_Si.data['energies'], ref_energy2)
10887

10988

11089
class TestABACUSMDLabeledOutput(unittest.TestCase, TestABACUSMD):

tests/test_abacus_pw_scf.py

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@ class TestABACUSSinglePointEnergy:
1111
def test_atom_names(self) :
1212
self.assertEqual(self.system_ch4.data['atom_names'], ['C', 'H'])
1313
#self.assertEqual(self.system_h2o.data['atom_names'], ['O','H'])
14-
14+
self.assertEqual(self.system_ch4_unlabeled.data['atom_names'], ['C', 'H'])
1515
def test_atom_numbs(self) :
1616
self.assertEqual(self.system_ch4.data['atom_numbs'], [1, 4])
1717
#self.assertEqual(self.system_h2o.data['atom_numbs'], [64,128])
18-
18+
self.assertEqual(self.system_ch4_unlabeled.data['atom_numbs'], [1, 4])
1919
def test_atom_types(self) :
2020
ref_type = [0,1,1,1,1]
2121
ref_type = np.array(ref_type)
2222
for ii in range(ref_type.shape[0]) :
2323
self.assertEqual(self.system_ch4.data['atom_types'][ii], ref_type[ii])
24-
24+
self.assertEqual(self.system_ch4_unlabeled['atom_types'][ii], ref_type[ii])
2525
# ref_type = [0]*64 + [1]*128
2626
# ref_type = np.array(ref_type)
2727
# for ii in range(ref_type.shape[0]) :
@@ -30,10 +30,8 @@ def test_atom_types(self) :
3030
def test_cell(self) :
3131
# cell = 5.29177 * np.eye(3)
3232
cell = bohr2ang * 10 * np.eye(3)
33-
for ii in range(cell.shape[0]) :
34-
for jj in range(cell.shape[1]) :
35-
self.assertAlmostEqual(self.system_ch4.data['cells'][0][ii][jj], cell[ii][jj])
36-
33+
np.testing.assert_almost_equal(self.system_ch4.data['cells'][0], cell)
34+
np.testing.assert_almost_equal(self.system_ch4_unlabeled.data['cells'][0], cell)
3735
# fp = open('qe.scf/h2o_cell')
3836
# cell = []
3937
# for ii in fp :
@@ -46,15 +44,14 @@ def test_cell(self) :
4644

4745

4846
def test_coord(self) :
49-
fp = open('abacus.scf/ch4_coord')
50-
coord = []
51-
for ii in fp :
52-
coord.append([float(jj) for jj in ii.split()])
53-
coord = np.array(coord)
54-
for ii in range(coord.shape[0]) :
55-
for jj in range(coord.shape[1]) :
56-
self.assertAlmostEqual(self.system_ch4.data['coords'][0][ii][jj], coord[ii][jj], places=5)
57-
fp.close()
47+
with open('abacus.scf/ch4_coord') as fp:
48+
coord = []
49+
for ii in fp :
50+
coord.append([float(jj) for jj in ii.split()])
51+
coord = np.array(coord)
52+
np.testing.assert_almost_equal(self.system_ch4.data['coords'][0], coord, decimal=5)
53+
np.testing.assert_almost_equal(self.system_ch4_unlabeled.data['coords'][0], coord, decimal=5)
54+
5855

5956
# fp = open('qe.scf/h2o_coord')
6057
# coord = []
@@ -67,15 +64,13 @@ def test_coord(self) :
6764
# fp.close()
6865

6966
def test_force(self) :
70-
fp = open('abacus.scf/ch4_force')
71-
force = []
72-
for ii in fp :
73-
force.append([float(jj) for jj in ii.split()])
74-
force = np.array(force)
75-
for ii in range(force.shape[0]) :
76-
for jj in range(force.shape[1]) :
77-
self.assertAlmostEqual(self.system_ch4.data['forces'][0][ii][jj], force[ii][jj])
78-
fp.close()
67+
with open('abacus.scf/ch4_force') as fp:
68+
force = []
69+
for ii in fp :
70+
force.append([float(jj) for jj in ii.split()])
71+
force = np.array(force)
72+
np.testing.assert_almost_equal(self.system_ch4.data['forces'][0], force)
73+
7974

8075
# fp = open('qe.scf/h2o_force')
8176
# force = []
@@ -88,15 +83,13 @@ def test_force(self) :
8883
# fp.close()
8984

9085
def test_virial(self) :
91-
fp = open('abacus.scf/ch4_virial')
92-
virial = []
93-
for ii in fp :
94-
virial.append([float(jj) for jj in ii.split()])
95-
virial = np.array(virial)
96-
for ii in range(virial.shape[0]) :
97-
for jj in range(virial.shape[1]) :
98-
self.assertAlmostEqual(self.system_ch4.data['virials'][0][ii][jj], virial[ii][jj], places = 3)
99-
fp.close()
86+
with open('abacus.scf/ch4_virial') as fp:
87+
virial = []
88+
for ii in fp :
89+
virial.append([float(jj) for jj in ii.split()])
90+
virial = np.array(virial)
91+
np.testing.assert_almost_equal(self.system_ch4.data['virials'][0], virial, decimal = 3)
92+
10093

10194
# fp = open('qe.scf/h2o_virial')
10295
# virial = []
@@ -121,7 +114,7 @@ class TestABACUSLabeledOutput(unittest.TestCase, TestABACUSSinglePointEnergy):
121114
def setUp(self):
122115
self.system_ch4 = dpdata.LabeledSystem('abacus.scf',fmt='abacus/scf')
123116
# self.system_h2o = dpdata.LabeledSystem('qe.scf/02.out',fmt='qe/pw/scf')
124-
117+
self.system_ch4_unlabeled = dpdata.System('abacus.scf/STRU.ch4', fmt='abacus/stru')
125118

126119
if __name__ == '__main__':
127120
unittest.main()

0 commit comments

Comments
 (0)