Skip to content

Commit 5a93f08

Browse files
Liu-RXLiuRenxi
andauthored
Rewrite abacus interface on devel branch; add unites for qe/pw/scf's crystal unit atomic positions read in. (#154)
* added unittest for qe/pw/scf's crystal unit atomic positions read in. * Add files via upload * fixed problem in system.py. * deleted .DS_Store files. Co-authored-by: LiuRenxi <[email protected]>
1 parent 19e08df commit 5a93f08

File tree

12 files changed

+1028
-8
lines changed

12 files changed

+1028
-8
lines changed

dpdata/abacus/scf.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import os,sys
2+
import numpy as np
3+
4+
bohr2ang = 0.5291770
5+
ry2ev = 13.605698
6+
kbar2evperang3 = 1e3 / 1.6021892e6
7+
# The consts are cited from $ABACUS_ROOT/source/src_global/constant.h
8+
9+
10+
def get_block (lines, keyword, skip = 0, nlines = None):
11+
ret = []
12+
found = False
13+
if not nlines:
14+
nlines = 1e6
15+
for idx,ii in enumerate(lines) :
16+
if keyword in ii :
17+
found = True
18+
blk_idx = idx + 1 + skip
19+
line_idx = 0
20+
while len(lines[blk_idx]) == 0:
21+
blk_idx += 1
22+
while len(lines[blk_idx]) != 0 and line_idx < nlines and blk_idx != len(lines):
23+
ret.append(lines[blk_idx])
24+
blk_idx += 1
25+
line_idx += 1
26+
break
27+
if not found:
28+
raise RuntimeError("The keyword %s is not found in the script." %keyword)
29+
return ret
30+
31+
def get_geometry_in(fname, inlines):
32+
geometry_path_in = os.path.join(fname, "STRU")
33+
for line in inlines:
34+
if "atom_file" in line and "atom_file"==line.split()[0]:
35+
atom_file = line.split()[1]
36+
geometry_path_in = os.path.join(fname, atom_file)
37+
break
38+
return geometry_path_in
39+
40+
def get_path_out(fname, inlines):
41+
path_out = os.path.join(fname, "OUT.ABACUS/running_scf.log")
42+
for line in inlines:
43+
if "suffix" in line and "suffix"==line.split()[0]:
44+
suffix = line.split()[1]
45+
path_out = os.path.join(fname, "OUT.%s/running_scf.log" % suffix)
46+
break
47+
return path_out
48+
49+
def get_cell(geometry_inlines):
50+
cell_lines = get_block(geometry_inlines, "LATTICE_VECTORS", skip = 0, nlines = 3)
51+
celldm_lines = get_block(geometry_inlines, "LATTICE_CONSTANT", skip=0, nlines=1)
52+
53+
celldm = float(celldm_lines[0].split()[0]) * bohr2ang # lattice const is in Bohr
54+
cell = []
55+
for ii in range(3):
56+
cell.append([float(jj) for jj in cell_lines[ii].split()[0:3]])
57+
cell = celldm*np.array(cell)
58+
return celldm, cell
59+
60+
def get_coords(celldm, cell, geometry_inlines, inlines):
61+
coords_lines = get_block(geometry_inlines, "ATOMIC_POSITIONS", skip=0)
62+
# assuming that ATOMIC_POSITIONS is at the bottom of the STRU file
63+
coord_type = coords_lines[0].split()[0].lower() # cartisan or direct
64+
atom_names = [] # element abbr in periodic table
65+
atom_types = [] # index of atom_names of each atom in the geometry
66+
atom_numbs = [] # of atoms for each element
67+
coords = [] # coordinations of atoms
68+
ntype = 0
69+
for line in inlines:
70+
if "ntype" in line and "ntype"==line.split()[0]:
71+
ntype = int(line.split()[1])
72+
break
73+
if ntype <= 0:
74+
raise RuntimeError('ntype cannot be found in INPUT file.')
75+
line_idx = 1 # starting line of first element
76+
for it in range(ntype):
77+
atom_names.append(coords_lines[line_idx].split()[0])
78+
line_idx+=2
79+
atom_numbs.append(int(coords_lines[line_idx].split()[0]))
80+
line_idx+=1
81+
for iline in range(atom_numbs[it]):
82+
xyz = np.array([float(xx) for xx in coords_lines[line_idx].split()[0:3]])
83+
if coord_type == "cartesian":
84+
xyz = xyz*celldm
85+
elif coord_type == "direct":
86+
tmp = np.matmul(xyz, cell)
87+
xyz = tmp
88+
else:
89+
print("coord_type = %s" % coord_type)
90+
raise RuntimeError("Input coordination type is invalid.\n Only direct and cartesian are accepted.")
91+
coords.append(xyz)
92+
atom_types.append(it)
93+
line_idx += 1
94+
coords = np.array(coords) # need transformation!!!
95+
atom_types = np.array(atom_types)
96+
return atom_names, atom_numbs, atom_types, coords
97+
98+
def get_energy(outlines):
99+
Etot = None
100+
for line in outlines:
101+
if "!FINAL_ETOT_IS" in line:
102+
Etot = float(line.split()[1]) # in eV
103+
break
104+
if not Etot:
105+
not_converge = False
106+
for line in outlines:
107+
if "convergence has NOT been achieved!" in line:
108+
not_converge = True
109+
raise RuntimeError("convergence has NOT been achieved in scf!")
110+
break
111+
if not not_converge:
112+
raise RuntimeError("Final total energy cannot be found in output. Unknown problem.")
113+
return Etot
114+
115+
def get_force (outlines):
116+
force = []
117+
force_inlines = get_block (outlines, "TOTAL-FORCE (eV/Angstrom)", skip = 4)
118+
for line in force_inlines:
119+
force.append([float(f) for f in line.split()[1:4]])
120+
force = np.array(force)
121+
return force
122+
123+
def get_stress(outlines):
124+
stress = []
125+
stress_inlines = get_block(outlines, "TOTAL-STRESS (KBAR)", skip = 3)
126+
for line in stress_inlines:
127+
stress.append([float(f) for f in line.split()])
128+
stress = np.array(stress) * kbar2evperang3
129+
return stress
130+
131+
132+
133+
def get_frame (fname):
134+
if type(fname) == str:
135+
# if the input parameter is only one string, it is assumed that it is the
136+
# base directory containing INPUT file;
137+
path_in = os.path.join(fname, "INPUT")
138+
else:
139+
raise RuntimeError('invalid input')
140+
with open(path_in, 'r') as fp:
141+
inlines = fp.read().split('\n')
142+
143+
geometry_path_in = get_geometry_in(fname, inlines)
144+
path_out = get_path_out(fname, inlines)
145+
146+
with open(geometry_path_in, 'r') as fp:
147+
geometry_inlines = fp.read().split('\n')
148+
with open(path_out, 'r') as fp:
149+
outlines = fp.read().split('\n')
150+
151+
celldm, cell = get_cell(geometry_inlines)
152+
atom_names, natoms, types, coords = get_coords(celldm, cell, geometry_inlines, inlines)
153+
154+
energy = get_energy(outlines)
155+
force = get_force (outlines)
156+
stress = get_stress(outlines) * np.linalg.det(cell)
157+
158+
data = {}
159+
data['atom_names'] = atom_names
160+
data['atom_numbs'] = natoms
161+
data['atom_types'] = types
162+
data['cells'] = cell[np.newaxis, :, :]
163+
data['coords'] = coords[np.newaxis, :, :]
164+
data['energies'] = np.array(energy)[np.newaxis]
165+
data['forces'] = force[np.newaxis, :, :]
166+
data['virials'] = stress[np.newaxis, :, :]
167+
data['orig'] = np.zeros(3)
168+
# print("atom_names = ", data['atom_names'])
169+
# print("natoms = ", data['atom_numbs'])
170+
# print("types = ", data['atom_types'])
171+
# print("cells = ", data['cells'])
172+
# print("coords = ", data['coords'])
173+
# print("energy = ", data['energies'])
174+
# print("force = ", data['forces'])
175+
# print("virial = ", data['virials'])
176+
return data
177+
178+
if __name__ == "__main__":
179+
path = "/home/lrx/work/12_ABACUS_dpgen_interface/dpdata/dpdata/tests/abacus.scf"
180+
data = get_frame(path)

dpdata/qe/scf.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,49 @@ def get_cell (lines) :
2828
blk = lines[idx:idx+2]
2929
ibrav = int(blk[0].replace(',','').split('=')[-1])
3030
if ibrav == 0:
31+
for iline in lines:
32+
if 'CELL_PARAMETERS' in iline and 'angstrom' not in iline.lower():
33+
raise RuntimeError("CELL_PARAMETERS must be written in Angstrom. Other units are not supported yet.")
3134
blk = get_block(lines, 'CELL_PARAMETERS')
3235
for ii in blk:
3336
ret.append([float(jj) for jj in ii.split()[0:3]])
3437
ret = np.array(ret)
3538
elif ibrav == 1:
36-
a = float(blk[1].split('=')[-1])
39+
a = None
40+
for iline in lines:
41+
line = iline.replace("=", " ").replace(",", "").split()
42+
if len(line) >= 2 and "a" == line[0]:
43+
#print("line = ", line)
44+
a = float(line[1])
45+
if len(line) >= 2 and "celldm(1)" == line[0]:
46+
a = float(line[1])*bohr2ang
47+
#print("a = ", a)
48+
if not a:
49+
raise RuntimeError("parameter 'a' or 'celldm(1)' cannot be found.")
3750
ret = np.array([[a,0.,0.],[0.,a,0.],[0.,0.,a]])
3851
else:
3952
sys.exit('ibrav > 1 not supported yet.')
4053
return ret
4154

42-
def get_coords (lines) :
55+
def get_coords (lines, cell) :
4356
coord = []
4457
atom_symbol_list = []
45-
blk = get_block(lines, 'ATOMIC_POSITIONS')
46-
for ii in blk:
47-
coord.append([float(jj) for jj in ii.split()[1:4]])
48-
atom_symbol_list.append(ii.split()[0])
49-
coord = np.array(coord)
58+
for iline in lines:
59+
if 'ATOMIC_POSITIONS' in iline and ('angstrom' not in iline.lower() and 'crystal' not in iline.lower()):
60+
raise RuntimeError("ATOMIC_POSITIONS must be written in Angstrom or crystal. Other units are not supported yet.")
61+
if 'ATOMIC_POSITIONS' in iline and 'angstrom' in iline.lower():
62+
blk = get_block(lines, 'ATOMIC_POSITIONS')
63+
for ii in blk:
64+
coord.append([float(jj) for jj in ii.split()[1:4]])
65+
atom_symbol_list.append(ii.split()[0])
66+
coord = np.array(coord)
67+
elif 'ATOMIC_POSITIONS' in iline and 'crystal' in iline.lower():
68+
blk = get_block(lines, 'ATOMIC_POSITIONS')
69+
for ii in blk:
70+
coord.append([float(jj) for jj in ii.split()[1:4]])
71+
atom_symbol_list.append(ii.split()[0])
72+
coord = np.array(coord)
73+
coord = np.matmul(coord, cell.T)
5074
atom_symbol_list = np.array(atom_symbol_list)
5175
tmp_names, symbol_idx = np.unique(atom_symbol_list, return_index=True)
5276
atom_types = []
@@ -104,8 +128,8 @@ def get_frame (fname):
104128
outlines = fp.read().split('\n')
105129
with open(path_in, 'r') as fp:
106130
inlines = fp.read().split('\n')
107-
atom_names, natoms, types, coords = get_coords(inlines)
108131
cell = get_cell (inlines)
132+
atom_names, natoms, types, coords = get_coords(inlines, cell)
109133
energy = get_energy(outlines)
110134
force = get_force (outlines)
111135
stress = get_stress(outlines) * np.linalg.det(cell)

dpdata/system.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import dpdata.deepmd.comp
1313
import dpdata.qe.traj
1414
import dpdata.qe.scf
15+
import dpdata.abacus.scf
1516
import dpdata.siesta.output
1617
import dpdata.siesta.aiMD_output
1718
import dpdata.md.pbc
@@ -96,6 +97,8 @@ def __init__ (self,
9697
- ``deepmd/npy``: deepmd-kit compressed format (numpy binary)
9798
- ``vasp/poscar``: vasp POSCAR
9899
- ``qe/cp/traj``: Quantum Espresso CP trajectory files. should have: file_name+'.in' and file_name+'.pos'
100+
- ``qe/pw/scf``: Quantum Espresso PW single point calculations. Both input and output files are required. If file_name is a string, it denotes the output file name. Input file name is obtained by replacing 'out' by 'in' from file_name. Or file_name is a list, with the first element being the input file name and the second element being the output filename.
101+
- ``abacus/scf``: ABACUS plane wave scf. The directory containing INPUT file is required.
99102
- ``siesta/output``: siesta SCF output file
100103
- ``siesta/aimd_output``: siesta aimd output file
101104
- ``pwmat/atom.config``: pwmat atom.config
@@ -1366,6 +1369,11 @@ def from_qe_pw_scf(self, file_name) :
13661369
self.data['virials'], \
13671370
= dpdata.qe.scf.get_frame(file_name)
13681371
self.rot_lower_triangular()
1372+
1373+
@register_from_funcs.register_funcs('abacus/scf')
1374+
def from_abacus_pw_scf(self, file_name) :
1375+
self.data = dpdata.abacus.scf.get_frame(file_name)
1376+
self.rot_lower_triangular()
13691377

13701378
@register_from_funcs.register_funcs('siesta/output')
13711379
def from_siesta_output(self, file_name) :

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
'dpdata/amber',
3939
'dpdata/fhi_aims',
4040
'dpdata/gromacs',
41+
'dpdata/abacus',
4142
'dpdata/rdkit'
4243
],
4344
package_data={'dpdata':['*.json']},

tests/abacus.scf/INPUT

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
INPUT_PARAMETERS
2+
#Parameters (General)
3+
suffix ch4
4+
atom_file STRU.ch4 #the filename of file containing atom positions
5+
kpoint_file KPT.ch4 #the name of file containing k points
6+
pseudo_dir ./
7+
ntype 2
8+
nbands 8
9+
#Parameters (Accuracy)
10+
ecutwfc 100
11+
symmetry 1
12+
niter 50
13+
smearing gauss #type of smearing: gauss; fd; fixed; mp; mp2; mv
14+
sigma 0.01
15+
mixing_beta 0.5
16+
mixing_type plain
17+
force 1
18+
stress 1

0 commit comments

Comments
 (0)