Coverage for gpaw/xc/pawcorrection.py: 100%
78 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
1# Copyright (C) 2003 CAMP
2# Please see the accompanying LICENSE file for further information.
4from math import pi, sqrt
6import numpy as np
8from gpaw.gaunt import gaunt
9# load points and weights for the angular integration
10from gpaw.sphere.lebedev import R_nv, Y_nL, weight_n
11from gpaw.spherical_harmonics import nablarlYL
13r"""
14 3
15 __ dn __ __ dY
16 __ 2 \ L 2 \ \ L 2
17 (\/n) = ( ) Y --- ) + ) ( ) n --- )
18 /__ L dr /__ /__ L dr
19 v
20 L v=1 L
23 dY
24 L
25 A = --- r
26 Lv dr
27 v
29"""
30# A_nvL is defined as above, n is an expansion point index (50 Lebedev points).
31rnablaY_nLv = np.empty((len(R_nv), 25, 3))
32for rnablaY_Lv, Y_L, R_v in zip(rnablaY_nLv, Y_nL, R_nv):
33 for l in range(5):
34 for L in range(l**2, (l + 1)**2):
35 rnablaY_Lv[L] = nablarlYL(L, R_v) - l * R_v * Y_L[L]
38class PAWXCCorrection:
39 def __init__(self,
40 w_jg, # all-lectron partial waves
41 wt_jg, # pseudo partial waves
42 nc_g, # core density
43 nct_g, # smooth core density
44 rgd, # radial grid descriptor
45 jl, # ?
46 lmax, # maximal angular momentum to consider
47 e_xc0, # xc energy of reference atom
48 phicorehole_g, # ?
49 fcorehole, # ?
50 tauc_g, # kinetic core energy array
51 tauct_g): # pseudo kinetic core energy array
52 self.e_xc0 = e_xc0
53 self.Lmax = (lmax + 1)**2
54 self.rgd = rgd
55 self.dv_g = rgd.dv_g
56 self.Y_nL = Y_nL[:, :self.Lmax]
57 self.rnablaY_nLv = rnablaY_nLv[:, :self.Lmax]
58 self.ng = ng = len(nc_g)
59 self.phi_jg = w_jg
60 self.phit_jg = wt_jg
62 self.jlL = [(j, l, l**2 + m) for j, l in jl for m in range(2 * l + 1)]
63 self.ni = ni = len(self.jlL)
64 self.nj = nj = len(jl)
65 self.nii = nii = ni * (ni + 1) // 2
66 njj = nj * (nj + 1) // 2
68 self.tauc_g = tauc_g
69 self.tauct_g = tauct_g
70 self.tau_npg = None
71 self.taut_npg = None
73 G_LLL = gaunt(max(l for j, l in jl))[:, :, :self.Lmax]
74 LGcut = G_LLL.shape[2]
75 B_Lqp = np.zeros((self.Lmax, njj, nii))
76 p = 0
77 i1 = 0
78 for j1, l1, L1 in self.jlL:
79 for j2, l2, L2 in self.jlL[i1:]:
80 if j1 < j2:
81 q = j2 + j1 * nj - j1 * (j1 + 1) // 2
82 else:
83 q = j1 + j2 * nj - j2 * (j2 + 1) // 2
84 B_Lqp[:LGcut, q, p] = G_LLL[L1, L2]
85 p += 1
86 i1 += 1
87 self.B_pqL = B_Lqp.T.copy()
89 #
90 self.n_qg = np.zeros((njj, ng))
91 self.nt_qg = np.zeros((njj, ng))
92 q = 0
93 for j1, l1 in jl:
94 for j2, l2 in jl[j1:]:
95 self.n_qg[q] = w_jg[j1] * w_jg[j2]
96 self.nt_qg[q] = wt_jg[j1] * wt_jg[j2]
97 q += 1
99 self.nc_g = nc_g
100 self.nct_g = nct_g
102 if fcorehole != 0.0:
103 self.nc_corehole_g = fcorehole * phicorehole_g**2 / (4 * pi)
104 else:
105 self.nc_corehole_g = None
107 def four_phi_integrals(self, D_sp, fxc):
108 """Calculate four-phi integrals.
110 The density is given by the density matrix ``D_sp`` in packed(pack)
111 form, and the resulting rank-four tensor is also returned in
112 packed format. ``fxc`` is a radial object???
113 """
115 ns, nii = D_sp.shape
117 assert ns == 1
119 D_p = D_sp[0]
120 D_Lq = np.dot(self.B_pqL.T, D_p)
122 # Expand all-electron density in spherical harmonics:
123 n_qg = self.n_qg
124 n_Lg = np.dot(D_Lq, n_qg)
125 n_Lg[0] += self.nc_g * sqrt(4 * pi)
127 # Expand pseudo electron density in spherical harmonics:
128 nt_qg = self.nt_qg
129 nt_Lg = np.dot(D_Lq, nt_qg)
130 nt_Lg[0] += self.nct_g * sqrt(4 * pi)
132 # Allocate array for result:
133 J_pp = np.zeros((nii, nii))
135 # Loop over 50 points on the sphere surface:
136 for w, Y_L in zip(weight_n, self.Y_nL):
137 B_pq = np.dot(self.B_pqL, Y_L)
139 fxcdv = fxc(np.dot(Y_L, n_Lg)) * self.dv_g
140 dn2_qq = np.inner(n_qg * fxcdv, n_qg)
142 fxctdv = fxc(np.dot(Y_L, nt_Lg)) * self.dv_g
143 dn2_qq -= np.inner(nt_qg * fxctdv, nt_qg)
145 J_pp += w * np.dot(B_pq, np.inner(dn2_qq, B_pq))
147 return J_pp