6
6
import numpy as np
7
7
8
8
9
- def system_info (lines , type_idx_zero = False ):
9
+ def atom_name_from_potcar_string (instr : str ) -> str :
10
+ """Get atom name from a potcar element name.
11
+
12
+ e.g. Sn_d -> Sn
13
+
14
+ Parameters
15
+ ----------
16
+ instr : str
17
+ input potcar elemenet name
18
+
19
+ Returns
20
+ -------
21
+ name: str
22
+ name of atoms
23
+ """
24
+ if "_" in instr :
25
+ # for case like : TITEL = PAW_PBE Sn_d 06Sep2000
26
+ return instr .split ("_" )[0 ]
27
+ else :
28
+ return instr
29
+
30
+
31
+ def system_info (
32
+ lines : list [str ],
33
+ type_idx_zero : bool = False ,
34
+ ) -> tuple [list [str ], list [int ], np .ndarray , int | None , int | None ]:
35
+ """Get system information from lines of an OUTCAR file.
36
+
37
+ Parameters
38
+ ----------
39
+ lines : list[str]
40
+ the lines of the OUTCAR file
41
+ type_idx_zero : bool
42
+ if true atom types starts from 0 otherwise from 1.
43
+
44
+ Returns
45
+ -------
46
+ atom_names: list[str]
47
+ name of atoms
48
+ atom_numbs: list[int]
49
+ number of atoms that have a certain name. same length as atom_names
50
+ atom_types: np.ndarray
51
+ type of each atom, the array has same lenght as number of atoms
52
+ nelm: optional[int]
53
+ the value of NELM parameter
54
+ nwrite: optional[int]
55
+ the value of NWRITE parameter
56
+ """
10
57
atom_names = []
58
+ atom_names_potcar = []
11
59
atom_numbs = None
12
60
nelm = None
61
+ nwrite = None
13
62
for ii in lines :
14
- ii_word_list = ii .split ()
15
63
if "TITEL" in ii :
16
64
# get atom names from POTCAR info, tested only for PAW_PBE ...
65
+ # for case like : TITEL = PAW_PBE Sn_d 06Sep2000
17
66
_ii = ii .split ()[3 ]
18
- if "_" in _ii :
19
- # for case like : TITEL = PAW_PBE Sn_d 06Sep2000
20
- atom_names .append (_ii .split ("_" )[0 ])
21
- else :
22
- atom_names .append (_ii )
67
+ atom_names .append (atom_name_from_potcar_string (_ii ))
68
+ elif "POTCAR:" in ii :
69
+ # get atom names from POTCAR info, tested only for PAW_PBE ...
70
+ # for case like : POTCAR: PAW_PBE Ti 08Apr2002
71
+ _ii = ii .split ()[2 ]
72
+ atom_names_potcar .append (atom_name_from_potcar_string (_ii ))
23
73
# a stricker check for "NELM"; compatible with distingct formats in different versions(6 and older, newers_expect-to-work) of vasp
24
74
elif nelm is None :
25
75
m = re .search (r"NELM\s*=\s*(\d+)" , ii )
26
76
if m :
27
77
nelm = int (m .group (1 ))
78
+ elif nwrite is None :
79
+ m = re .search (r"NWRITE\s*=\s*(\d+)" , ii )
80
+ if m :
81
+ nwrite = int (m .group (1 ))
28
82
if "ions per type" in ii :
29
83
atom_numbs_ = [int (s ) for s in ii .split ()[4 :]]
30
84
if atom_numbs is None :
31
85
atom_numbs = atom_numbs_
32
86
else :
33
87
assert atom_numbs == atom_numbs_ , "in consistent numb atoms in OUTCAR"
88
+ if len (atom_names ) == 0 :
89
+ # try to use atom_names_potcar
90
+ if len (atom_names_potcar ) == 0 :
91
+ raise ValueError ("cannot get atom names from potcar" )
92
+ nnames = len (atom_names_potcar )
93
+ # the names are repeated. check if it is the case
94
+ assert atom_names_potcar [: nnames // 2 ] == atom_names_potcar [nnames // 2 :]
95
+ atom_names = atom_names_potcar [: nnames // 2 ]
34
96
assert nelm is not None , "cannot find maximum steps for each SC iteration"
35
97
assert atom_numbs is not None , "cannot find ion type info in OUTCAR"
36
98
atom_names = atom_names [: len (atom_numbs )]
@@ -41,7 +103,7 @@ def system_info(lines, type_idx_zero=False):
41
103
atom_types .append (idx )
42
104
else :
43
105
atom_types .append (idx + 1 )
44
- return atom_names , atom_numbs , np .array (atom_types , dtype = int ), nelm
106
+ return atom_names , atom_numbs , np .array (atom_types , dtype = int ), nelm , nwrite
45
107
46
108
47
109
def get_outcar_block (fp , ml = False ):
@@ -57,12 +119,24 @@ def get_outcar_block(fp, ml=False):
57
119
return blk
58
120
59
121
122
+ def check_outputs (coord , cell , force ):
123
+ if len (force ) == 0 :
124
+ raise ValueError ("cannot find forces in OUTCAR block" )
125
+ if len (coord ) == 0 :
126
+ raise ValueError ("cannot find coordinates in OUTCAR block" )
127
+ if len (cell ) == 0 :
128
+ raise ValueError ("cannot find cell in OUTCAR block" )
129
+ return True
130
+
131
+
60
132
# we assume that the force is printed ...
61
133
def get_frames (fname , begin = 0 , step = 1 , ml = False , convergence_check = True ):
62
134
fp = open (fname )
63
135
blk = get_outcar_block (fp )
64
136
65
- atom_names , atom_numbs , atom_types , nelm = system_info (blk , type_idx_zero = True )
137
+ atom_names , atom_numbs , atom_types , nelm , nwrite = system_info (
138
+ blk , type_idx_zero = True
139
+ )
66
140
ntot = sum (atom_numbs )
67
141
68
142
all_coords = []
@@ -78,9 +152,15 @@ def get_frames(fname, begin=0, step=1, ml=False, convergence_check=True):
78
152
coord , cell , energy , force , virial , is_converge = analyze_block (
79
153
blk , ntot , nelm , ml
80
154
)
81
- if len ( coord ) == 0 :
155
+ if energy is None :
82
156
break
83
- if is_converge or not convergence_check :
157
+ if nwrite == 0 :
158
+ has_label = len (force ) > 0 and len (coord ) > 0 and len (cell ) > 0
159
+ if not has_label :
160
+ warnings .warn ("cannot find labels in the frame, ingore" )
161
+ else :
162
+ has_label = check_outputs (coord , cell , force )
163
+ if (is_converge or not convergence_check ) and has_label :
84
164
all_coords .append (coord )
85
165
all_cells .append (cell )
86
166
all_energies .append (energy )
@@ -144,12 +224,6 @@ def analyze_block(lines, ntot, nelm, ml=False):
144
224
is_converge = False
145
225
elif energy_token [ml_index ] in ii :
146
226
energy = float (ii .split ()[energy_index [ml_index ]])
147
- if len (force ) == 0 :
148
- raise ValueError ("cannot find forces in OUTCAR block" )
149
- if len (coord ) == 0 :
150
- raise ValueError ("cannot find coordinates in OUTCAR block" )
151
- if len (cell ) == 0 :
152
- raise ValueError ("cannot find cell in OUTCAR block" )
153
227
return coord , cell , energy , force , virial , is_converge
154
228
elif cell_token [ml_index ] in ii :
155
229
for dd in range (3 ):
0 commit comments