Coverage for /builds/hweiske/ase/ase/dft/bandgap.py: 85.71%
84 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-22 11:22 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2024-04-22 11:22 +0000
1import warnings
3import numpy as np
6def get_band_gap(calc, direct=False, spin=None):
7 warnings.warn('Please use ase.dft.bandgap.bandgap() instead!')
8 gap, (s1, k1, n1), (s2, k2, n2) = bandgap(calc, direct, spin)
9 ns = calc.get_number_of_spins()
10 if ns == 2 and spin is None:
11 return gap, (s1, k1), (s2, k2)
12 return gap, k1, k2
15def bandgap(calc=None, direct=False, spin=None, eigenvalues=None, efermi=None,
16 output=None, kpts=None):
17 """Calculates the band-gap.
19 Parameters:
21 calc: Calculator object
22 Electronic structure calculator object.
23 direct: bool
24 Calculate direct band-gap.
25 spin: int or None
26 For spin-polarized systems, you can use spin=0 or spin=1 to look only
27 at a single spin-channel.
28 eigenvalues: ndarray of shape (nspin, nkpt, nband) or (nkpt, nband)
29 Eigenvalues.
30 efermi: float
31 Fermi level (defaults to 0.0).
33 Returns a (gap, p1, p2) tuple where p1 and p2 are tuples of indices of the
34 valence and conduction points (s, k, n).
36 Example:
38 >>> gap, p1, p2 = bandgap(silicon.calc)
39 Gap: 1.2 eV
40 Transition (v -> c):
41 [0.000, 0.000, 0.000] -> [0.500, 0.500, 0.000]
42 >>> print(gap, p1, p2)
43 1.2 (0, 0, 3), (0, 5, 4)
44 >>> gap, p1, p2 = bandgap(silicon.calc, direct=True)
45 Direct gap: 3.4 eV
46 Transition at: [0.000, 0.000, 0.000]
47 >>> print(gap, p1, p2)
48 3.4 (0, 0, 3), (0, 0, 4)
49 """
51 if calc:
52 kpts = calc.get_ibz_k_points()
53 nk = len(kpts)
54 ns = calc.get_number_of_spins()
55 eigenvalues = np.array([[calc.get_eigenvalues(kpt=k, spin=s)
56 for k in range(nk)]
57 for s in range(ns)])
58 if efermi is None:
59 efermi = calc.get_fermi_level()
61 efermi = efermi or 0.0
63 e_skn = eigenvalues - efermi
64 if eigenvalues.ndim == 2:
65 e_skn = e_skn[np.newaxis] # spinors
67 if not np.isfinite(e_skn).all():
68 raise ValueError('Bad eigenvalues!')
70 gap, (s1, k1, n1), (s2, k2, n2) = _bandgap(e_skn, spin, direct)
72 if eigenvalues.ndim != 3:
73 p1 = (k1, n1)
74 p2 = (k2, n2)
75 else:
76 p1 = (s1, k1, n1)
77 p2 = (s2, k2, n2)
79 return gap, p1, p2
82def _bandgap(e_skn, spin, direct):
83 """Helper function."""
84 ns, nk, nb = e_skn.shape
85 s1 = s2 = k1 = k2 = n1 = n2 = None
87 N_sk = (e_skn < 0.0).sum(2) # number of occupied bands
89 # Check for bands crossing the fermi-level
90 if ns == 1:
91 if np.ptp(N_sk[0]) > 0:
92 return 0.0, (None, None, None), (None, None, None)
93 elif spin is None:
94 if (np.ptp(N_sk, axis=1) > 0).any():
95 return 0.0, (None, None, None), (None, None, None)
96 elif np.ptp(N_sk[spin]) > 0:
97 return 0.0, (None, None, None), (None, None, None)
99 if (N_sk == 0).any() or (N_sk == nb).any():
100 raise ValueError('Too few bands!')
102 e_skn = np.array([[e_skn[s, k, N_sk[s, k] - 1:N_sk[s, k] + 1]
103 for k in range(nk)]
104 for s in range(ns)])
105 ev_sk = e_skn[:, :, 0] # valence band
106 ec_sk = e_skn[:, :, 1] # conduction band
108 if ns == 1:
109 s1 = 0
110 s2 = 0
111 gap, k1, k2 = find_gap(ev_sk[0], ec_sk[0], direct)
112 n1 = N_sk[0, 0] - 1
113 n2 = n1 + 1
114 return gap, (0, k1, n1), (0, k2, n2)
116 if spin is None:
117 gap, k1, k2 = find_gap(ev_sk.ravel(), ec_sk.ravel(), direct)
118 if direct:
119 # Check also spin flips:
120 for s in [0, 1]:
121 g, k, _ = find_gap(ev_sk[s], ec_sk[1 - s], direct)
122 if g < gap:
123 gap = g
124 k1 = k + nk * s
125 k2 = k + nk * (1 - s)
127 if gap > 0.0:
128 s1, k1 = divmod(k1, nk)
129 s2, k2 = divmod(k2, nk)
130 n1 = N_sk[s1, k1] - 1
131 n2 = N_sk[s2, k2]
132 return gap, (s1, k1, n1), (s2, k2, n2)
133 return 0.0, (None, None, None), (None, None, None)
135 gap, k1, k2 = find_gap(ev_sk[spin], ec_sk[spin], direct)
136 s1 = spin
137 s2 = spin
138 n1 = N_sk[s1, k1] - 1
139 n2 = n1 + 1
140 return gap, (s1, k1, n1), (s2, k2, n2)
143def find_gap(ev_k, ec_k, direct):
144 """Helper function."""
145 if direct:
146 gap_k = ec_k - ev_k
147 k = gap_k.argmin()
148 return gap_k[k], k, k
149 kv = ev_k.argmax()
150 kc = ec_k.argmin()
151 return ec_k[kc] - ev_k[kv], kv, kc