Coverage for eminus/xc/gga_x_pbe.py: 100.00%

35 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-16 10:16 +0000

1# SPDX-FileCopyrightText: 2021 The eminus developers 

2# SPDX-License-Identifier: Apache-2.0 

3"""Perdew-Burke-Ernzerhof GGA exchange. 

4 

5Reference: Phys. Rev. Lett. 77, 3865. 

6""" 

7 

8import numpy as np 

9from scipy.linalg import norm 

10 

11from .lda_x import lda_x, lda_x_spin 

12 

13 

14def gga_x_pbe(n, mu=0.2195149727645171, dn_spin=None, **kwargs): 

15 """Perdew-Burke-Ernzerhof parametrization of the exchange functional (spin-paired). 

16 

17 Corresponds to the functional with the label GGA_X_PBE and ID 101 in Libxc. 

18 

19 Reference: Phys. Rev. Lett. 77, 3865. 

20 

21 Args: 

22 n: Real-space electronic density. 

23 

24 Keyword Args: 

25 mu: Functional parameter. 

26 dn_spin: Real-space gradient of densities per spin channel. 

27 **kwargs: Throwaway arguments. 

28 

29 Returns: 

30 PBE exchange energy density, potential, and vsigma. 

31 """ 

32 ex, vx, _ = lda_x(n, **kwargs) 

33 gex, gvx, vsigmax = pbe_x_base(n, mu, dn_spin[0], **kwargs) 

34 vx, gvx = vx[0], gvx[0] # Remove spin dimension for the correct shape 

35 return ex + gex / n, np.array([vx + gvx]), np.array([0.5 * vsigmax]) 

36 

37 

38def gga_x_pbe_spin(n, zeta, mu=0.2195149727645171, dn_spin=None, **kwargs): 

39 """Perdew-Burke-Ernzerhof parametrization of the exchange functional (spin-polarized). 

40 

41 Corresponds to the functional with the label GGA_X_PBE and ID 101 in Libxc. 

42 

43 Reference: Phys. Rev. Lett. 77, 3865. 

44 

45 Args: 

46 n: Real-space electronic density. 

47 zeta: Relative spin polarization. 

48 

49 Keyword Args: 

50 mu: Functional parameter. 

51 dn_spin: Real-space gradient of densities per spin channel. 

52 **kwargs: Throwaway arguments. 

53 

54 Returns: 

55 PBE exchange energy density, potential, and vsigma. 

56 """ 

57 # Use the spin-scaling relationship Exc(n_up, n_down)=(Exc(2 n_up)+Exc(2 n_down))/2 

58 zeta = zeta[0] # Getting the non-zero values from zeta adds an extra dimension, remove it here 

59 n_up = zeta * n + n # 2 * n_up 

60 n_dw = -zeta * n + n # 2 * n_down 

61 ex_up, vx_up, vsigma_up = pbe_x_base(n_up, mu, 2 * dn_spin[0], **kwargs) 

62 ex_dw, vx_dw, vsigma_dw = pbe_x_base(n_dw, mu, 2 * dn_spin[1], **kwargs) 

63 vx_up, vx_dw = vx_up[0], vx_dw[0] # Remove spin dimension for the correct shape 

64 

65 ex, vx, _ = lda_x_spin(n, zeta, **kwargs) 

66 

67 vsigmax = np.array([vsigma_up, np.zeros_like(ex), vsigma_dw]) 

68 return ex + 0.5 * (ex_up + ex_dw) / n, np.array([vx[0] + vx_up, vx[1] + vx_dw]), vsigmax 

69 

70 

71def pbe_x_base(n, mu=0.2195149727645171, dn=None, **kwargs): 

72 """Base PBE exchange functional to be used in the spin-paired and -polarized case. 

73 

74 Reference: Phys. Rev. Lett. 77, 3865. 

75 

76 Args: 

77 n: Real-space electronic density. 

78 

79 Keyword Args: 

80 mu: Functional parameter. 

81 dn: Real-space gradient of densities per spin channel. 

82 **kwargs: Throwaway arguments. 

83 

84 Returns: 

85 PBE exchange energy density, potential, and vsigma. 

86 """ 

87 kappa = 0.804 

88 

89 norm_dn = norm(dn, axis=1) 

90 kf = (3 * np.pi**2 * n) ** (1 / 3) 

91 # Handle divisions by zero 

92 # divkf = 1 / kf 

93 divkf = np.divide(1, kf, out=np.zeros_like(kf), where=kf > 0) 

94 # Handle divisions by zero 

95 # s = norm_dn * divkf / (2 * n) 

96 s = np.divide(norm_dn * divkf, 2 * n, out=np.zeros_like(n), where=n > 0) 

97 f1 = 1 + mu * s**2 / kappa 

98 Fx = kappa - kappa / f1 

99 exunif = -3 * kf / (4 * np.pi) 

100 # In Fx a "1 + " is missing, since n * exunif is the Slater exchange that is added later 

101 sx = exunif * Fx 

102 

103 dsdn = -4 / 3 * s 

104 dFxds = 2 * mu * s / f1**2 

105 dexunif = exunif / 3 

106 exunifdFx = exunif * dFxds 

107 vx = sx + dexunif * Fx + exunifdFx * dsdn # dFx/dn = dFx/ds * ds/dn 

108 

109 # Handle divisions by zero 

110 # vsigmax = exunifdFx * divkf / (2 * norm_dn) 

111 vsigmax = np.divide( 

112 exunifdFx * divkf, 2 * norm_dn, out=np.zeros_like(norm_dn), where=norm_dn > 0 

113 ) 

114 return sx * n, np.array([vx]), vsigmax