Skip to content

Commit 8196d9e

Browse files
Liu-RXLiuRenxi
andauthored
adapt dpdata interface to ABACUS 2.2.0, fixed issue#268 about qe/pw/scf (#270)
* adapt dpgen interface to ABACUS 2.2.0, fixed issue#268 about qe/pw/scf * Add files via upload add running_md.log file * removed deprecated code for old ABACUS MD output Co-authored-by: LiuRenxi <[email protected]>
1 parent 5c52fac commit 8196d9e

22 files changed

+2558
-2957
lines changed

dpdata/abacus/md.py

Lines changed: 49 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from ast import dump
12
import os,sys
23
import numpy as np
3-
from .scf import ry2ev, kbar2evperang3, get_block, get_geometry_in, get_cell, get_coords
4+
from .scf import ry2ev, bohr2ang, kbar2evperang3, get_block, get_geometry_in, get_cell, get_coords
5+
import re
46

57
# Read in geometries from an ABACUS MD trajectory.
68
# The atomic coordinates are read in from generated files in OUT.XXXX.
@@ -24,126 +26,49 @@ def get_path_out(fname, inlines):
2426

2527
def get_coord_dump_freq(inlines):
2628
for line in inlines:
27-
if len(line)>0 and "md_dumpmdfred" in line and "md_dumpmdfred" == line.split()[0]:
29+
if len(line)>0 and "md_dumpfreq" in line and "md_dumpfreq" == line.split()[0]:
2830
return int(line.split()[1])
2931
return 1
3032

31-
# set up a cell according to cell info in cif file.
32-
# maybe useful later
33-
'''
34-
def setup_cell(a, b, c, alpha, beta, gamma):
35-
cell = np.zeros(3, 3)
36-
cell[0, 0] = a
37-
cell[1, 0] = b*np.cos(gamma/180*np.pi)
38-
cell[1, 1] = b*np.sin(gamma/180*np.pi)
39-
cell[2, 0] = c*np.cos(beta/180*np.pi)
40-
cell[2, 1] = c*(b*np.cos(alpha/180*np.pi) - cell[1, 0]*np.cos(beta/180*np.pi))/cell[1, 1]
41-
cell[2, 2] = np.sqrt(c**2 - cell[2, 0]**2 - cell[2, 1]**2)
42-
return cell
43-
'''
44-
45-
def get_single_coord_from_cif(pos_file, atom_names, natoms, cell):
46-
assert(len(atom_names) == len(natoms))
47-
nele = len(atom_names)
33+
def get_coords_from_dump(dumplines, natoms):
34+
nlines = len(dumplines)
4835
total_natoms = sum(natoms)
49-
coord = np.zeros([total_natoms, 3])
50-
a = 0
51-
b = 0
52-
c = 0
53-
alpha = 0
54-
beta = 0
55-
gamma = 0
56-
with open(pos_file, "r") as fp:
57-
lines = fp.read().split("\n")
58-
for line in lines:
59-
if "_cell_length_a" in line:
60-
a = float(line.split()[1])
61-
if "_cell_length_b" in line:
62-
b = float(line.split()[1])
63-
if "_cell_length_c" in line:
64-
c = float(line.split()[1])
65-
if "_cell_angle_alpha" in line:
66-
alpha = float(line.split()[1])
67-
if "_cell_angle_beta" in line:
68-
beta = float(line.split()[1])
69-
if "_cell_angle_gamma" in line:
70-
gamma = float(line.split()[1])
71-
assert(a > 0 and b > 0 and c > 0 and alpha > 0 and beta > 0 and gamma > 0)
72-
#cell = setup_cell(a, b, c, alpha, beta, gamma)
73-
coord_lines = get_block(lines=lines, keyword="_atom_site_fract_z", skip=0, nlines = total_natoms)
74-
75-
ia_idx = 0
76-
for it in range(nele):
77-
for ia in range(natoms[it]):
78-
coord_line = coord_lines[ia_idx].split()
79-
assert(coord_line[0] == atom_names[it])
80-
coord[ia_idx, 0] = float(coord_line[1])
81-
coord[ia_idx, 1] = float(coord_line[2])
82-
coord[ia_idx, 2] = float(coord_line[3])
83-
ia_idx+=1
84-
coord = np.matmul(coord, cell)
85-
# important! Coordinates are converted to Cartesian coordinate.
86-
return coord
36+
nframes_dump = int(nlines/(total_natoms + 13))
8737

88-
89-
def get_coords_from_cif(ndump, dump_freq, atom_names, natoms, types, path_out, cell):
90-
total_natoms = sum(natoms)
91-
#cell = np.zeros(ndump, 3, 3)
92-
coords = np.zeros([ndump, total_natoms, 3])
93-
pos_file = os.path.join(path_out, "STRU_READIN_ADJUST.cif")
94-
# frame 0 file is different from any other frames
95-
coords[0] = get_single_coord_from_cif(pos_file, atom_names, natoms, cell)
96-
for dump_idx in range(1, ndump):
97-
pos_file = os.path.join(path_out, "md_pos_%d.cif" %(dump_idx*dump_freq))
98-
#print("dump_idx = %s" %dump_idx)
99-
coords[dump_idx] = get_single_coord_from_cif(pos_file, atom_names, natoms, cell)
100-
return coords
38+
cells = np.zeros([nframes_dump, 3, 3])
39+
stresses = np.zeros([nframes_dump, 3, 3])
40+
forces = np.zeros([nframes_dump, total_natoms, 3])
41+
coords = np.zeros([nframes_dump, total_natoms, 3])
42+
iframe = 0
43+
for iline in range(nlines):
44+
if "MDSTEP" in dumplines[iline]:
45+
# read in LATTICE_CONSTANT
46+
celldm = float(dumplines[iline+1].split(" ")[-1])
47+
# read in LATTICE_VECTORS
48+
for ix in range(3):
49+
cells[iframe, ix] = np.array([float(i) for i in re.split('\s+', dumplines[iline+3+ix])[-3:]]) * celldm
50+
stresses[iframe, ix] = np.array([float(i) for i in re.split('\s+', dumplines[iline+7+ix])[-3:]])
51+
for iat in range(total_natoms):
52+
coords[iframe, iat] = np.array([float(i) for i in re.split('\s+', dumplines[iline+11+iat])[-6:-3]])*celldm
53+
forces[iframe, iat] = np.array([float(i) for i in re.split('\s+', dumplines[iline+11+iat])[-3:]])
54+
iframe += 1
55+
assert(iframe == nframes_dump)
56+
cells *= bohr2ang
57+
coords *= bohr2ang
58+
stresses *= kbar2evperang3
59+
return coords, cells, forces, stresses
10160

102-
def get_energy_force_stress(outlines, inlines, dump_freq, ndump, natoms, atom_names):
103-
stress = None
104-
total_natoms = sum(natoms)
105-
for line in inlines:
106-
if len(line)>0 and "stress" in line and "stress" == line.split()[0] and "1" == line.split()[1]:
107-
stress = np.zeros([ndump, 3, 3])
108-
break
109-
if type(stress) != np.ndarray:
110-
print("The ABACUS program has no stress output. Stress will not be read.")
61+
def get_energy(outlines, ndump, dump_freq):
62+
energy = []
11163
nenergy = 0
112-
nforce = 0
113-
nstress = 0
114-
energy = np.zeros(ndump)
115-
force = np.zeros([ndump, total_natoms, 3])
116-
11764
for line_idx, line in enumerate(outlines):
11865
if "final etot is" in line:
11966
if nenergy%dump_freq == 0:
120-
energy[int(nenergy/dump_freq)] = float(line.split()[-2])
67+
energy.append(float(line.split()[-2]))
12168
nenergy+=1
122-
if "TOTAL-FORCE (eV/Angstrom)" in line:
123-
for iatom in range(0, total_natoms):
124-
force_line = outlines[line_idx+5+iatom]
125-
atom_force = [float(i) for i in force_line.split()[1:]]
126-
assert(len(atom_force) == 3)
127-
atom_force = np.array(atom_force)
128-
if nforce%dump_freq == 0:
129-
force[int(nforce/dump_freq), iatom] = atom_force
130-
nforce+=1
131-
assert(nforce==nenergy)
132-
if "TOTAL-STRESS (KBAR)" in line:
133-
for idx in range(0, 3):
134-
stress_line = outlines[line_idx+4+idx]
135-
single_stress = [float(i) for i in stress_line.split()]
136-
if len(single_stress) != 3:
137-
print(single_stress)
138-
assert(len(single_stress) == 3)
139-
single_stress = np.array(single_stress)
140-
if nstress%dump_freq == 0:
141-
stress[int(nstress/dump_freq), idx] = single_stress
142-
nstress+=1
143-
assert(nstress==nforce)
144-
if type(stress) == np.ndarray:
145-
stress *= kbar2evperang3
146-
return energy, force, stress
69+
assert(ndump == len(energy))
70+
energy = np.array(energy)
71+
return energy
14772

14873

14974
def get_frame (fname):
@@ -164,23 +89,27 @@ def get_frame (fname):
16489
atom_names, natoms, types, coords = get_coords(celldm, cell, geometry_inlines, inlines)
16590
# This coords is not to be used.
16691
dump_freq = get_coord_dump_freq(inlines = inlines)
167-
ndump = int(os.popen("ls -l %s | grep 'md_pos_' | wc -l" %path_out).readlines()[0])
92+
#ndump = int(os.popen("ls -l %s | grep 'md_pos_' | wc -l" %path_out).readlines()[0])
16893
# number of dumped geometry files
169-
coords = get_coords_from_cif(ndump, dump_freq, atom_names, natoms, types, path_out, cell)
170-
171-
# TODO: Read in energies, forces and pressures.
94+
#coords = get_coords_from_cif(ndump, dump_freq, atom_names, natoms, types, path_out, cell)
95+
with open(os.path.join(path_out, "MD_dump"), 'r') as fp:
96+
dumplines = fp.read().split('\n')
97+
coords, cells, force, stress = get_coords_from_dump(dumplines, natoms)
98+
ndump = np.shape(coords)[0]
17299
with open(os.path.join(path_out, "running_md.log"), 'r') as fp:
173100
outlines = fp.read().split('\n')
174-
energy, force, stress = get_energy_force_stress(outlines, inlines, dump_freq, ndump, natoms, atom_names)
175-
if type(stress) == np.ndarray:
176-
stress *= np.linalg.det(cell)
101+
energy = get_energy(outlines, ndump, dump_freq)
102+
for iframe in range(ndump):
103+
stress[iframe] *= np.linalg.det(cells[iframe, :, :].reshape([3, 3]))
104+
if np.sum(np.abs(stress[0])) < 1e-10:
105+
stress = None
177106
data = {}
178107
data['atom_names'] = atom_names
179108
data['atom_numbs'] = natoms
180109
data['atom_types'] = types
181-
data['cells'] = np.zeros([ndump, 3, 3])
182-
for idx in range(ndump):
183-
data['cells'][:, :, :] = cell
110+
data['cells'] = cells
111+
#for idx in range(ndump):
112+
# data['cells'][:, :, :] = cell
184113
data['coords'] = coords
185114
data['energies'] = energy
186115
data['forces'] = force

dpdata/abacus/scf.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@ 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]) == 0:
19+
while len(lines[blk_idx].split("\s+")) == 0:
2020
blk_idx += 1
21-
while len(lines[blk_idx]) != 0 and line_idx < nlines and blk_idx != len(lines):
21+
while line_idx < nlines and blk_idx != len(lines):
22+
if len(lines[blk_idx].split("\s+")) == 0 or lines[blk_idx] == "":
23+
blk_idx+=1
24+
continue
2225
ret.append(lines[blk_idx])
2326
blk_idx += 1
2427
line_idx += 1
2528
break
2629
if not found:
27-
raise RuntimeError("The keyword %s is not found in the script." %keyword)
30+
return None
2831
return ret
2932

3033
def get_geometry_in(fname, inlines):
@@ -111,17 +114,21 @@ def get_energy(outlines):
111114
raise RuntimeError("Final total energy cannot be found in output. Unknown problem.")
112115
return Etot
113116

114-
def get_force (outlines):
117+
def get_force (outlines, natoms):
115118
force = []
116-
force_inlines = get_block (outlines, "TOTAL-FORCE (eV/Angstrom)", skip = 4)
119+
force_inlines = get_block (outlines, "TOTAL-FORCE (eV/Angstrom)", skip = 4, nlines=np.sum(natoms))
120+
if force_inlines is None:
121+
raise RuntimeError("TOTAL-FORCE (eV/Angstrom) is not found in running_scf.log. Please check.")
117122
for line in force_inlines:
118123
force.append([float(f) for f in line.split()[1:4]])
119124
force = np.array(force)
120125
return force
121126

122127
def get_stress(outlines):
123128
stress = []
124-
stress_inlines = get_block(outlines, "TOTAL-STRESS (KBAR)", skip = 3)
129+
stress_inlines = get_block(outlines, "TOTAL-STRESS (KBAR)", skip = 3, nlines=3)
130+
if stress_inlines is None:
131+
return None
125132
for line in stress_inlines:
126133
stress.append([float(f) for f in line.split()])
127134
stress = np.array(stress) * kbar2evperang3
@@ -151,8 +158,10 @@ def get_frame (fname):
151158
atom_names, natoms, types, coords = get_coords(celldm, cell, geometry_inlines, inlines)
152159

153160
energy = get_energy(outlines)
154-
force = get_force (outlines)
155-
stress = get_stress(outlines) * np.linalg.det(cell)
161+
force = get_force (outlines, natoms)
162+
stress = get_stress(outlines)
163+
if stress is not None:
164+
stress *= np.abs(np.linalg.det(cell))
156165

157166
data = {}
158167
data['atom_names'] = atom_names
@@ -162,7 +171,8 @@ def get_frame (fname):
162171
data['coords'] = coords[np.newaxis, :, :]
163172
data['energies'] = np.array(energy)[np.newaxis]
164173
data['forces'] = force[np.newaxis, :, :]
165-
data['virials'] = stress[np.newaxis, :, :]
174+
if stress is not None:
175+
data['virials'] = stress[np.newaxis, :, :]
166176
data['orig'] = np.zeros(3)
167177
# print("atom_names = ", data['atom_names'])
168178
# print("natoms = ", data['atom_numbs'])

dpdata/plugins/abacus.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
@Format.register("abacus/scf")
77
@Format.register("abacus/pw/scf")
8+
@Format.register("abacus/lcao/scf")
89
class AbacusSCFFormat(Format):
910
#@Format.post("rot_lower_triangular")
1011
def from_labeled_system(self, file_name, **kwargs):

dpdata/qe/scf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def get_coords (lines, cell) :
7070
coord.append([float(jj) for jj in ii.split()[1:4]])
7171
atom_symbol_list.append(ii.split()[0])
7272
coord = np.array(coord)
73-
coord = np.matmul(coord, cell.T)
73+
coord = np.matmul(coord, cell)
7474
atom_symbol_list = np.array(atom_symbol_list)
7575
tmp_names, symbol_idx = np.unique(atom_symbol_list, return_index=True)
7676
atom_types = []

tests/abacus.md/INPUT

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,43 @@
11
INPUT_PARAMETERS
2-
#Parameters (General)
3-
suffix autotest
4-
pseudo_dir ./
5-
ntype 1
6-
nbands 8
7-
calculation md
8-
read_file_dir ./
2+
#Parameters (1.General)
3+
suffix abacus
4+
calculation md
5+
ntype 2
6+
nbands 6
7+
symmetry 0
98

10-
#Parameters (Accuracy)
11-
ecutwfc 20
12-
niter 20
9+
#Parameters (2.Iteration)
10+
ecutwfc 50
11+
scf_thr 1e-2
12+
scf_nmax 50
1313

14-
basis_type pw
15-
nstep 21 # number of MD/relaxation steps
14+
#Parameters (3.Basis)
15+
basis_type lcao
16+
gamma_only 1
1617

17-
stress 1
18-
stress_thr 1e-6
19-
force 1
20-
force_thr_ev 1.0e-3
18+
#Parameters (4.Smearing)
19+
smearing_method gaussian
20+
smearing_sigma 0.02
2121

22-
ks_solver cg
23-
mixing_type pulay
24-
mixing_beta 0.7
22+
#Parameters (5.Mixing)
23+
mixing_type pulay
24+
mixing_beta 0.4
2525

26-
md_mdtype 1 # 0 for NVE; 1 for NVT with Nose Hoover; 2 for NVT with velocity rescaling
27-
md_tfirst 10 # temperature, unit: K
28-
md_dt 1 # unit: fs
29-
md_rstmd 0 # 1 for restart
30-
md_dumpmdfred 5
26+
#Parameters (6.Deepks)
27+
#cal_force 1
28+
#test_force 1
29+
#deepks_out_labels 1
30+
#deepks_descriptor_lmax 2
31+
#deepks_scf 1
32+
#deepks_model model.ptg
33+
34+
md_type 1
35+
md_dt 0.1
36+
md_tfirst 300
37+
md_tfreq 0.1
38+
md_restart 0
39+
md_dumpfreq 5
40+
md_nstep 20
41+
out_level m
42+
43+
cal_stress 1

0 commit comments

Comments
 (0)