Coverage for gpaw/xc/tb09.py: 98%
91 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-12 00:18 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-12 00:18 +0000
1"""Tran-Blaha potential.
3From:
5 Accurate Band Gaps of Semiconductors and Insulators
6 with a Semilocal Exchange-Correlation Potential
8 Fabien Tran and Peter Blaha
10 PRL 102, 226401 (2009)
12 DOI: 10.1103/PhysRevLett.102.226401
14"""
15import numpy as np
16from ase.utils import seterr
18from gpaw.xc.mgga import MGGA, weight_n
19from gpaw.xc.libxc import LibXC
20from gpaw.fd_operators import Laplace
23class TB09Kernel:
24 name = 'TB09'
25 type = 'MGGA'
26 alpha = -0.012
27 beta = 1.023
29 def __init__(self, c=None):
30 self.tb09 = LibXC('MGGA_X_TB09', provides_laplacian=True).xc.tb09
31 self.ldac = LibXC('LDA_C_PW')
33 self.fixedc = c is not None # calculate c or use fixed value
34 self.c = c # amount of "exact exchange"
35 self.n = 0 # Lebedev quadrature point number (0-49)
36 self.sign = 1.0 # sign of PAW correction: +1 for AE and -1 for PS
37 self.I = None # integral from Eq. (3)
39 def calculate(self, e_g, n_sg, dedn_sg, sigma_xg,
40 dedsigma_xg, tau_sg, dedtau_sg):
41 ns = len(n_sg)
42 n_sg[n_sg < 1e-6] = 1e-6
44 if n_sg.ndim == 4:
45 if not self.fixedc:
46 if self.c is None:
47 # We don't have the integral yet - just use 1.0:
48 self.c = 1.0
49 else:
50 self.I = self.world.sum_scalar(self.I)
51 self.c = (self.alpha + self.beta *
52 (self.I / self.gd.volume)**0.5)
54 # Start calculation of c for use in the next SCF step:
55 if ns == 1:
56 gradn_g = sigma_xg[0]**0.5
57 else:
58 gradn_g = (sigma_xg[0] +
59 2 * sigma_xg[1] +
60 sigma_xg[2])**0.5
61 self.I = self.gd.integrate(gradn_g / n_sg.sum(0))
62 # The domain is not distributed like the PAW corrections:
63 self.I /= self.world.size
65 lapl_sg = self.gd.empty(ns)
66 for n_g, lapl_g in zip(n_sg, lapl_sg):
67 self.lapl.apply(n_g, lapl_g)
69 else:
70 rgd = self.rgd
71 lapl_sg = []
72 for n_Lg in self.n_sLg:
73 lapl_g = rgd.laplace(np.dot(self.Y_L, n_Lg))
74 l = 0
75 L1 = 0
76 while L1 < len(self.Y_L):
77 L2 = L1 + 2 * l + 1
78 n_g = np.dot(self.Y_L[L1:L2], n_Lg[L1:L2])
79 with seterr(divide='ignore', invalid='ignore'):
80 lapl_g -= l * (l + 1) * n_g / rgd.r_g**2
81 lapl_g[0] = 0.0
82 L1 = L2
83 l += 1
84 lapl_sg.append(lapl_g)
86 if not self.fixedc:
87 # PAW corrections to integral:
88 w = self.sign * weight_n[self.n]
89 if ns == 1:
90 gradn_g = sigma_xg[0]**0.5
91 else:
92 gradn_g = (sigma_xg[0] +
93 2 * sigma_xg[1] +
94 sigma_xg[2])**0.5
95 self.I += w * rgd.integrate(gradn_g / n_sg.sum(0))
97 self.n += 1
98 if self.n == len(weight_n):
99 self.n = 0
100 self.sign = -self.sign
102 # dedn_sg[:] = 0.0
103 sigma_xg[sigma_xg < 1e-10] = 1e-10
104 tau_sg[tau_sg < 1e-10] = 1e-10
106 for n_g, sigma_g, lapl_g, tau_g, v_g in zip(n_sg, sigma_xg[::2],
107 lapl_sg, tau_sg, dedn_sg):
108 self.tb09(self.c, n_g.ravel(), sigma_g, lapl_g, tau_g, v_g,
109 dedsigma_xg)
111 self.ldac.calculate(e_g, n_sg, dedn_sg)
112 e_g[:] = 0.0
114 dedsigma_xg[:] = 0.0
115 dedtau_sg[:] = 0.0
118class TB09(MGGA):
119 def __init__(self, c=None, stencil=2):
120 MGGA.__init__(self, TB09Kernel(c), stencil=stencil)
122 def get_setup_name(self):
123 return 'LDA'
125 def initialize(self, dens, ham, wfs):
126 MGGA.initialize(self, dens, ham, wfs)
127 self.kernel.world = wfs.world
128 self.kernel.gd = dens.finegd
129 self.kernel.lapl = Laplace(dens.finegd)
131 def create_mgga_radial_calculator(self):
132 rcalc = MGGA.create_mgga_radial_calculator(self)
134 def f(rgd, n_sLg, Y_L, dndr_sLg, rnablaY_Lv, n):
135 self.kernel.n_sLg = n_sLg
136 self.kernel.Y_L = Y_L
137 self.kernel.rgd = rgd
138 return rcalc(rgd, n_sLg, Y_L, dndr_sLg, rnablaY_Lv, n)
140 return f
142 def apply_orbital_dependent_hamiltonian(self, kpt, psit_xG,
143 Htpsit_xG, dH_asp):
144 pass
146 @property
147 def c(self):
148 """Amount of "exact exchange"."""
149 return self.kernel.c