Coverage for /builds/kinetik161/ase/ase/io/octopus/output.py: 48.50%

167 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-12-10 11:04 +0000

1import re 

2 

3import numpy as np 

4 

5from ase.units import Angstrom, Bohr, Debye, Hartree, eV 

6 

7 

8class OctopusIOError(IOError): 

9 pass # Cannot find output files 

10 

11 

12def read_eigenvalues_file(fd): 

13 unit = None 

14 

15 for line in fd: 

16 m = re.match(r'Eigenvalues\s*\[(.+?)\]', line) 

17 if m is not None: 

18 unit = m.group(1) 

19 break 

20 line = next(fd) 

21 assert line.strip().startswith('#st'), line 

22 

23 # fermilevel = None 

24 kpts = [] 

25 eigs = [] 

26 occs = [] 

27 

28 for line in fd: 

29 m = re.match(r'#k.*?\(\s*(.+?),\s*(.+?),\s*(.+?)\)', line) 

30 if m: 

31 k = m.group(1, 2, 3) 

32 kpts.append(np.array(k, float)) 

33 eigs.append({}) 

34 occs.append({}) 

35 else: 

36 m = re.match(r'\s*\d+\s*(\S+)\s*(\S+)\s*(\S+)', line) 

37 if m is None: 

38 m = re.match(r'Fermi energy\s*=\s*(\S+)\s*', line) 

39 assert m is not None 

40 # We can also return the fermilevel but so far we just read 

41 # it from the static/info instead. 

42 # fermilevel = float(m.group(1)) 

43 else: 

44 spin, eig, occ = m.group(1, 2, 3) 

45 

46 if not eigs: 

47 # Only initialized if kpoint header was written 

48 eigs.append({}) 

49 occs.append({}) 

50 

51 eigs[-1].setdefault(spin, []).append(float(eig)) 

52 occs[-1].setdefault(spin, []).append(float(occ)) 

53 

54 nkpts = len(kpts) 

55 nspins = len(eigs[0]) 

56 nbands = len(eigs[0][spin]) 

57 

58 kptsarr = np.array(kpts, float) 

59 eigsarr = np.empty((nkpts, nspins, nbands)) 

60 occsarr = np.empty((nkpts, nspins, nbands)) 

61 

62 arrs = [eigsarr, occsarr] 

63 

64 for arr in arrs: 

65 arr.fill(np.nan) 

66 

67 for k in range(nkpts): 

68 for arr, lst in [(eigsarr, eigs), (occsarr, occs)]: 

69 arr[k, :, :] = [lst[k][sp] for sp 

70 in (['--'] if nspins == 1 else ['up', 'dn'])] 

71 

72 for arr in arrs: 

73 assert not np.isnan(arr).any() 

74 

75 eigsarr *= {'H': Hartree, 'eV': eV}[unit] 

76 return kptsarr, eigsarr, occsarr 

77 

78 

79def read_static_info_stress(fd): 

80 stress_cv = np.empty((3, 3)) 

81 

82 headers = next(fd) 

83 assert headers.strip().startswith('T_{ij}') 

84 for i in range(3): 

85 line = next(fd) 

86 tokens = line.split() 

87 vec = np.array(tokens[1:4]).astype(float) 

88 stress_cv[i] = vec 

89 return stress_cv 

90 

91 

92def read_static_info_kpoints(fd): 

93 for line in fd: 

94 if line.startswith('List of k-points'): 

95 break 

96 

97 tokens = next(fd).split() 

98 assert tokens == ['ik', 'k_x', 'k_y', 'k_z', 'Weight'] 

99 bar = next(fd) 

100 assert bar.startswith('---') 

101 

102 kpts = [] 

103 weights = [] 

104 

105 for line in fd: 

106 # Format: index kx ky kz weight 

107 m = re.match(r'\s*\d+\s*(\S+)\s*(\S+)\s*(\S+)\s*(\S+)', line) 

108 if m is None: 

109 break 

110 kxyz = m.group(1, 2, 3) 

111 weight = m.group(4) 

112 kpts.append(kxyz) 

113 weights.append(weight) 

114 

115 ibz_k_points = np.array(kpts, float) 

116 k_point_weights = np.array(weights, float) 

117 return dict(ibz_k_points=ibz_k_points, k_point_weights=k_point_weights) 

118 

119 

120def read_static_info_eigenvalues(fd, energy_unit): 

121 

122 values_sknx = {} 

123 

124 nbands = 0 

125 fermilevel = None 

126 for line in fd: 

127 line = line.strip() 

128 if line.startswith('#'): 

129 continue 

130 if not line[:1].isdigit(): 

131 m = re.match(r'Fermi energy\s*=\s*(\S+)', line) 

132 if m is not None: 

133 fermilevel = float(m.group(1)) * energy_unit 

134 break 

135 

136 tokens = line.split() 

137 nbands = max(nbands, int(tokens[0])) 

138 energy = float(tokens[2]) * energy_unit 

139 occupation = float(tokens[3]) 

140 values_sknx.setdefault(tokens[1], []).append((energy, occupation)) 

141 

142 nspins = len(values_sknx) 

143 if nspins == 1: 

144 val = [values_sknx['--']] 

145 else: 

146 val = [values_sknx['up'], values_sknx['dn']] 

147 val = np.array(val, float) 

148 nkpts, remainder = divmod(len(val[0]), nbands) 

149 assert remainder == 0 

150 

151 eps_skn = val[:, :, 0].reshape(nspins, nkpts, nbands) 

152 occ_skn = val[:, :, 1].reshape(nspins, nkpts, nbands) 

153 eps_skn = eps_skn.transpose(1, 0, 2).copy() 

154 occ_skn = occ_skn.transpose(1, 0, 2).copy() 

155 assert eps_skn.flags.contiguous 

156 d = dict(nspins=nspins, 

157 nkpts=nkpts, 

158 nbands=nbands, 

159 eigenvalues=eps_skn, 

160 occupations=occ_skn) 

161 if fermilevel is not None: 

162 d.update(fermi_level=fermilevel) 

163 return d 

164 

165 

166def read_static_info_energy(fd, energy_unit): 

167 def get(name): 

168 for line in fd: 

169 if line.strip().startswith(name): 

170 return float(line.split('=')[-1].strip()) * energy_unit 

171 return dict(energy=get('Total'), free_energy=get('Free')) 

172 

173 

174def read_static_info(fd): 

175 results = {} 

176 

177 def get_energy_unit(line): # Convert "title [unit]": ---> unit 

178 return {'[eV]': eV, '[H]': Hartree}[line.split()[1].rstrip(':')] 

179 

180 for line in fd: 

181 if line.strip('*').strip().startswith('Brillouin zone'): 

182 results.update(read_static_info_kpoints(fd)) 

183 elif line.startswith('Eigenvalues ['): 

184 unit = get_energy_unit(line) 

185 results.update(read_static_info_eigenvalues(fd, unit)) 

186 elif line.startswith('Energy ['): 

187 unit = get_energy_unit(line) 

188 results.update(read_static_info_energy(fd, unit)) 

189 elif line.startswith('Stress tensor'): 

190 assert line.split()[-1] == '[H/b^3]' 

191 stress = read_static_info_stress(fd) 

192 stress *= Hartree / Bohr**3 

193 results.update(stress=stress) 

194 elif line.startswith('Total Magnetic Moment'): 

195 if 0: 

196 line = next(fd) 

197 values = line.split() 

198 results['magmom'] = float(values[-1]) 

199 

200 line = next(fd) 

201 assert line.startswith('Local Magnetic Moments') 

202 line = next(fd) 

203 assert line.split() == ['Ion', 'mz'] 

204 # Reading Local Magnetic Moments 

205 mag_moment = [] 

206 for line in fd: 

207 if line == '\n': 

208 break # there is no more thing to search for 

209 line = line.replace('\n', ' ') 

210 values = line.split() 

211 mag_moment.append(float(values[-1])) 

212 

213 results['magmoms'] = np.array(mag_moment) 

214 elif line.startswith('Dipole'): 

215 assert line.split()[-1] == '[Debye]' 

216 dipole = [float(next(fd).split()[-1]) for i in range(3)] 

217 results['dipole'] = np.array(dipole) * Debye 

218 elif line.startswith('Forces'): 

219 forceunitspec = line.split()[-1] 

220 forceunit = {'[eV/A]': eV / Angstrom, 

221 '[H/b]': Hartree / Bohr}[forceunitspec] 

222 forces = [] 

223 line = next(fd) 

224 assert line.strip().startswith('Ion') 

225 for line in fd: 

226 if line.strip().startswith('---'): 

227 break 

228 tokens = line.split()[-3:] 

229 forces.append([float(f) for f in tokens]) 

230 results['forces'] = np.array(forces) * forceunit 

231 elif line.startswith('Fermi'): 

232 tokens = line.split() 

233 unit = {'eV': eV, 'H': Hartree}[tokens[-1]] 

234 eFermi = float(tokens[-2]) * unit 

235 results['efermi'] = eFermi 

236 

237 if 'ibz_k_points' not in results: 

238 results['ibz_k_points'] = np.zeros((1, 3)) 

239 results['k_point_weights'] = np.ones(1) 

240 if 0: # 'efermi' not in results: 

241 # Find HOMO level. Note: This could be a very bad 

242 # implementation with fractional occupations if the Fermi 

243 # level was not found otherwise. 

244 all_energies = results['eigenvalues'].ravel() 

245 all_occupations = results['occupations'].ravel() 

246 args = np.argsort(all_energies) 

247 for arg in args[::-1]: 

248 if all_occupations[arg] > 0.1: 

249 break 

250 eFermi = all_energies[arg] 

251 results['efermi'] = eFermi 

252 

253 return results