Coverage for ase / io / octopus / output.py: 97.67%
172 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 10:20 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-02-04 10:20 +0000
1import re
3import numpy as np
5from ase.units import Angstrom, Bohr, Debye, Hartree, eV
8class OctopusIOError(IOError):
9 pass # Cannot find output files
12def read_eigenvalues_file(fd):
13 unit = None
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
23 # fermilevel = None
24 kpts = []
25 eigs = []
26 occs = []
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)
46 if not eigs:
47 # Only initialized if kpoint header was written
48 eigs.append({})
49 occs.append({})
51 eigs[-1].setdefault(spin, []).append(float(eig))
52 occs[-1].setdefault(spin, []).append(float(occ))
54 nkpts = len(kpts)
55 nspins = len(eigs[0])
56 nbands = len(eigs[0][spin])
58 kptsarr = np.array(kpts, float)
59 eigsarr = np.empty((nkpts, nspins, nbands))
60 occsarr = np.empty((nkpts, nspins, nbands))
62 arrs = [eigsarr, occsarr]
64 for arr in arrs:
65 arr.fill(np.nan)
67 for k in range(nkpts):
68 for arr, lst in [(eigsarr, eigs), (occsarr, occs)]:
69 arr[k, :, :] = [
70 lst[k][sp] for sp in (['--'] if nspins == 1 else ['up', 'dn'])
71 ]
73 for arr in arrs:
74 assert not np.isnan(arr).any()
76 eigsarr *= {'H': Hartree, 'eV': eV}[unit]
77 return kptsarr, eigsarr, occsarr
80def read_static_info_stress(fd):
81 stress_cv = np.empty((3, 3))
83 headers = next(fd)
84 assert headers.strip().startswith('T_{ij}')
85 for i in range(3):
86 line = next(fd)
87 tokens = line.split()
88 vec = np.array(tokens[1:4]).astype(float)
89 stress_cv[i] = vec
90 return stress_cv
93def read_static_info_kpoints(fd):
94 for line in fd:
95 if line.startswith('List of k-points'):
96 break
98 tokens = next(fd).split()
99 assert tokens == ['ik', 'k_x', 'k_y', 'k_z', 'Weight']
100 bar = next(fd)
101 assert bar.startswith('---')
103 kpts = []
104 weights = []
106 for line in fd:
107 # Format: index kx ky kz weight
108 m = re.match(r'\s*\d+\s*(\S+)\s*(\S+)\s*(\S+)\s*(\S+)', line)
109 if m is None:
110 break
111 kxyz = m.group(1, 2, 3)
112 weight = m.group(4)
113 kpts.append(kxyz)
114 weights.append(weight)
116 ibz_kpoints = np.array(kpts, float)
117 kpoint_weights = np.array(weights, float)
118 return dict(ibz_kpoints=ibz_kpoints, kpoint_weights=kpoint_weights)
121def read_static_info_eigenvalues(fd, energy_unit):
122 values_sknx = {}
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
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))
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
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(
157 nspins=nspins,
158 nkpts=nkpts,
159 nbands=nbands,
160 eigenvalues=eps_skn,
161 occupations=occ_skn,
162 )
163 if fermilevel is not None:
164 d.update(fermi_level=fermilevel)
165 return d
168def read_static_info_energy(fd, energy_unit):
169 def get(name):
170 for line in fd:
171 if line.strip().startswith(name):
172 return float(line.split('=')[-1].strip()) * energy_unit
174 return dict(energy=get('Total'), free_energy=get('Free'))
177def read_static_info(fd):
178 results = {}
180 def get_energy_unit(line): # Convert "title [unit]": ---> unit
181 return {'[eV]': eV, '[H]': Hartree}[line.split()[1].rstrip(':')]
183 for line in fd:
184 if line.strip('*').strip().startswith('Brillouin zone'):
185 results.update(read_static_info_kpoints(fd))
186 elif line.startswith('Eigenvalues ['):
187 unit = get_energy_unit(line)
188 results.update(read_static_info_eigenvalues(fd, unit))
189 elif line.startswith('Energy ['):
190 unit = get_energy_unit(line)
191 results.update(read_static_info_energy(fd, unit))
192 elif line.startswith('Total stress tensor ['):
193 if '[H/b^3]' in line:
194 stress = read_static_info_stress(fd)
195 stress *= Hartree / Bohr**3
196 results.update(stress=stress)
197 elif line.startswith('Total Magnetic Moment'):
198 line = next(fd)
199 values = line.split()
200 results['magmom'] = float(values[-1])
201 line = next(fd)
202 assert line.startswith('Local Magnetic Moments')
203 line = next(fd)
204 assert line.split() == ['Ion', 'mz']
205 # Reading Local Magnetic Moments
206 magmoms = []
207 for line in fd:
208 if line == '\n':
209 break # there is no more thing to search for
210 line = line.replace('\n', ' ')
211 values = line.split()
212 magmoms.append(float(values[-1]))
213 results['magmoms'] = np.array(magmoms)
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, '[H/b]': Hartree / Bohr}[
221 forceunitspec
222 ]
223 forces = []
224 line = next(fd)
225 assert line.strip().startswith('Ion')
226 for line in fd:
227 if line.strip().startswith('---'):
228 break
229 tokens = line.split()[-3:]
230 forces.append([float(f) for f in tokens])
231 results['forces'] = np.array(forces) * forceunit
233 return results