Skip to content

Commit c72b05d

Browse files
authored
add the support for abacus/relax (#332)
1 parent fe23715 commit c72b05d

File tree

16 files changed

+1832
-1
lines changed

16 files changed

+1832
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ The `System` or `LabeledSystem` can be constructed from the following file forma
8282
| Gromacs | gro | True | False | System | 'gromacs/gro' |
8383
| ABACUS | STRU | False | True | LabeledSystem | 'abacus/scf' |
8484
| ABACUS | cif | True | True | LabeledSystem | 'abacus/md' |
85+
| ABACUS | STRU | True | True | LabeledSystem | 'abacus/relax' |
8586
| ase | structure | True | True | MultiSystems | 'ase/structure' |
8687

8788

dpdata/abacus/relax.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import os,sys
2+
import numpy as np
3+
from .scf import bohr2ang, kbar2evperang3, get_geometry_in, get_cell, get_coords
4+
5+
# Read in geometries from an ABACUS RELAX(CELL-RELAX) trajectory in OUT.XXXX/runnning_relax/cell-relax.log.
6+
7+
def get_log_file(fname, inlines):
8+
suffix = "ABACUS"
9+
calculation = "scf"
10+
for line in inlines:
11+
if "suffix" in line and "suffix"==line.split()[0]:
12+
suffix = line.split()[1]
13+
elif "calculation" in line and "calculation" == line.split()[0]:
14+
calculation = line.split()[1]
15+
logf = os.path.join(fname, "OUT.%s/running_%s.log"%(suffix,calculation))
16+
return logf
17+
18+
def get_coords_from_log(loglines,natoms):
19+
'''
20+
NOTICE: unit of coords and cells is Angstrom
21+
'''
22+
natoms_log = 0
23+
for line in loglines:
24+
if line[13:41] == "number of atom for this type":
25+
natoms_log += int(line.split()[-1])
26+
27+
assert(natoms_log>0 and natoms_log == natoms),"ERROR: detected atom number in log file is %d" % natoms
28+
29+
energy = []
30+
cells = []
31+
coords = []
32+
force = []
33+
stress = []
34+
35+
for i in range(len(loglines)):
36+
line = loglines[i]
37+
if line[18:41] == "lattice constant (Bohr)":
38+
a0 = float(line.split()[-1])
39+
elif len(loglines[i].split()) >=2 and loglines[i].split()[1] == 'COORDINATES':
40+
coords.append([])
41+
direct_coord = False
42+
if loglines[i].split()[0] == 'DIRECT':
43+
direct_coord = True
44+
for k in range(2,2+natoms):
45+
coords[-1].append(list(map(lambda x: float(x),loglines[i+k].split()[1:4])))
46+
elif loglines[i].split()[0] == 'CARTESIAN':
47+
for k in range(2,2+natoms):
48+
coords[-1].append(list(map(lambda x: float(x)*a0,loglines[i+k].split()[1:4])))
49+
else:
50+
assert(False),"Unrecongnized coordinate type, %s, line:%d" % (loglines[i].split()[0],i)
51+
52+
converg = True
53+
for j in range(i):
54+
if loglines[i-j-1][1:36] == 'Ion relaxation is not converged yet':
55+
converg = False
56+
break
57+
elif loglines[i-j-1][1:29] == 'Ion relaxation is converged!':
58+
converg = True
59+
break
60+
61+
if converg:
62+
for j in range(i+1,len(loglines)):
63+
if loglines[j][1:56] == "Lattice vectors: (Cartesian coordinate: in unit of a_0)":
64+
cells.append([])
65+
for k in range(1,4):
66+
cells[-1].append(list(map(lambda x:float(x)*a0,loglines[j+k].split()[0:3])))
67+
break
68+
else:
69+
cells.append(cells[-1])
70+
71+
if direct_coord:
72+
coords[-1] = coords[-1].dot(cells[-1])
73+
74+
elif line[4:15] == "TOTAL-FORCE":
75+
force.append([])
76+
for j in range(5,5+natoms):
77+
force[-1].append(list(map(lambda x:float(x),loglines[i+j].split()[1:4])))
78+
elif line[1:13] == "TOTAL-STRESS":
79+
stress.append([])
80+
for j in range(4,7):
81+
stress[-1].append(list(map(lambda x:float(x),loglines[i+j].split()[0:3])))
82+
elif line[1:14] == "final etot is":
83+
energy.append(float(line.split()[-2]))
84+
85+
assert(len(cells) == len(coords) or len(cells)+1 == len(coords)),"ERROR: detected %d coordinates and %d cells" % (len(coords),len(cells))
86+
if len(cells)+1 == len(coords): del(coords[-1])
87+
88+
energy = np.array(energy)
89+
cells = np.array(cells)
90+
coords = np.array(coords)
91+
stress = np.array(stress)
92+
force = np.array(force)
93+
94+
cells *= bohr2ang
95+
coords *= bohr2ang
96+
97+
virial = np.zeros([len(cells), 3, 3])
98+
for i in range(len(cells)):
99+
volume = np.linalg.det(cells[i, :, :].reshape([3, 3]))
100+
virial[i] = stress[i] * kbar2evperang3 * volume
101+
102+
return energy,cells,coords,force,stress,virial
103+
104+
def get_frame (fname):
105+
if type(fname) == str:
106+
# if the input parameter is only one string, it is assumed that it is the
107+
# base directory containing INPUT file;
108+
path_in = os.path.join(fname, "INPUT")
109+
else:
110+
raise RuntimeError('invalid input')
111+
with open(path_in, 'r') as fp:
112+
inlines = fp.read().split('\n')
113+
geometry_path_in = get_geometry_in(fname, inlines) # base dir of STRU
114+
with open(geometry_path_in, 'r') as fp:
115+
geometry_inlines = fp.read().split('\n')
116+
celldm, cell = get_cell(geometry_inlines)
117+
atom_names, natoms, types, coord_tmp = get_coords(celldm, cell, geometry_inlines, inlines)
118+
119+
logf = get_log_file(fname, inlines)
120+
assert(os.path.isfile(logf)),"Error: can not find %s" % logf
121+
with open(logf) as f1: lines = f1.readlines()
122+
123+
atomnumber = 0
124+
for i in natoms: atomnumber += i
125+
energy,cells,coords,force,stress,virial = get_coords_from_log(lines,atomnumber)
126+
127+
data = {}
128+
data['atom_names'] = atom_names
129+
data['atom_numbs'] = natoms
130+
data['atom_types'] = types
131+
data['cells'] = cells
132+
data['coords'] = coords
133+
data['energies'] = energy
134+
data['forces'] = force
135+
data['virials'] = virial
136+
data['stress'] = stress
137+
data['orig'] = np.zeros(3)
138+
139+
return data

dpdata/plugins/abacus.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import dpdata.abacus.scf
22
import dpdata.abacus.md
3+
import dpdata.abacus.relax
34
from dpdata.format import Format
45

56
@Format.register("abacus/stru")
@@ -51,3 +52,11 @@ class AbacusMDFormat(Format):
5152
#@Format.post("rot_lower_triangular")
5253
def from_labeled_system(self, file_name, **kwargs):
5354
return dpdata.abacus.md.get_frame(file_name)
55+
56+
@Format.register("abacus/relax")
57+
@Format.register("abacus/pw/relax")
58+
@Format.register("abacus/lcao/relax")
59+
class AbacusRelaxFormat(Format):
60+
#@Format.post("rot_lower_triangular")
61+
def from_labeled_system(self, file_name, **kwargs):
62+
return dpdata.abacus.relax.get_frame(file_name)

dpdata/system.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ def __init__ (self,
195195
- ``qe/cp/traj``: Quantum Espresso CP trajectory files. should have: file_name+'.in' and file_name+'.pos'
196196
- ``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.
197197
- ``abacus/scf``: ABACUS pw/lcao scf. The directory containing INPUT file is required.
198-
- ``abacus/md``: ABACUS pw/lcao MD. The directory containing INPUT file is required.
198+
- ``abacus/md``: ABACUS pw/lcao MD. The directory containing INPUT file is required.
199+
- ``abacus/relax``: ABACUS pw/lcao relax or cell-relax. The directory containing INPUT file is required.
199200
- ``siesta/output``: siesta SCF output file
200201
- ``siesta/aimd_output``: siesta aimd output file
201202
- ``pwmat/atom.config``: pwmat atom.config

tests/abacus.relax/INPUT

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
INPUT_PARAMETERS
2+
#Parameters (1.General)
3+
suffix abacus
4+
calculation cell-relax
5+
ntype 2
6+
nbands 6
7+
symmetry 1
8+
9+
#Parameters (2.Iteration)
10+
ecutwfc 50
11+
scf_thr 1e-7
12+
scf_nmax 50
13+
14+
relax_nmax 5
15+
16+
#Parameters (3.Basis)
17+
basis_type pw
18+
19+
#Parameters (4.Smearing)
20+
smearing_method gaussian
21+
smearing_sigma 0.02
22+
23+
#Parameters (5.Mixing)
24+
mixing_type pulay
25+
mixing_beta 0.4
26+
27+
#Parameters (6.Deepks)
28+
cal_force 1
29+
cal_stress 1

tests/abacus.relax/KPT

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
K_POINTS
2+
0
3+
Gamma
4+
1 1 1 0 0 0

0 commit comments

Comments
 (0)