Coverage for gpaw/pair_density.py: 49%

152 statements  

« prev     ^ index     » next       coverage.py v7.7.1, created at 2025-07-08 00:17 +0000

1from math import sqrt, pi 

2import numpy as np 

3 

4from gpaw.utilities import pack_density, unpack_density 

5from gpaw.utilities.tools import pick 

6from gpaw.lfc import LocalizedFunctionsCollection as LFC, BasisFunctions 

7 

8 

9# XXX Document what is the difference between PairDensity2 and 1. 

10class PairDensity2: 

11 def __init__(self, density, spos_ac, finegrid): 

12 """Initialization needs a paw instance, and whether the compensated 

13 pair density should be on the fine grid (boolean)""" 

14 

15 self.density = density 

16 self.finegrid = finegrid 

17 

18 if not finegrid: 

19 density.Ghat = LFC(density.gd, 

20 [setup.ghat_l 

21 for setup in density.setups], 

22 integral=sqrt(4 * pi)) 

23 density.Ghat.set_positions(spos_ac) 

24 

25 def initialize(self, kpt, n1, n2): 

26 """Set wave function indices.""" 

27 self.n1 = n1 

28 self.n2 = n2 

29 self.spin = kpt.s 

30 self.P_ani = kpt.P_ani 

31 self.psit1_G = pick(kpt.psit_nG, n1) 

32 self.psit2_G = pick(kpt.psit_nG, n2) 

33 

34 def get_coarse(self, nt_G): 

35 """Get pair density""" 

36 np.multiply(self.psit1_G.conj(), self.psit2_G, nt_G) 

37 

38 def add_compensation_charges(self, nt_G, rhot_g): 

39 """Add compensation charges to input pair density, which 

40 is interpolated to the fine grid if needed.""" 

41 

42 if self.finegrid: 

43 # interpolate the pair density to the fine grid 

44 self.density.interpolator.apply(nt_G, rhot_g) 

45 else: 

46 # copy values 

47 rhot_g[:] = nt_G 

48 

49 # Determine the compensation charges for each nucleus 

50 Q_aL = {} 

51 for a, P_ni in self.P_ani.items(): 

52 assert P_ni.dtype == float 

53 # Generate density matrix 

54 P1_i = P_ni[self.n1] 

55 P2_i = P_ni[self.n2] 

56 D_ii = np.outer(P1_i.conj(), P2_i) 

57 # allowed to pack as used in the scalar product with 

58 # the symmetric array Delta_pL 

59 D_p = pack_density(D_ii, tolerance=1e30) 

60 

61 # Determine compensation charge coefficients: 

62 Q_aL[a] = np.dot(D_p, self.density.setups[a].Delta_pL) 

63 

64 # Add compensation charges 

65 if self.finegrid: 

66 self.density.ghat.add(rhot_g, Q_aL) 

67 else: 

68 self.density.Ghat.add(rhot_g, Q_aL) 

69 

70 

71class PairDensity: 

72 def __init__(self, paw): 

73 self.set_paw(paw) 

74 

75 def set_paw(self, paw): 

76 """basic initialisation knowing the calculator""" 

77 self.wfs = paw.wfs 

78 self.lcao = paw.wfs.mode == 'lcao' 

79 self.density = paw.density 

80 self.setups = paw.wfs.setups 

81 self.spos_ac = paw.spos_ac 

82 

83 self.spin = 0 

84 self.k = 0 

85 self.weight = 0.0 

86 

87 if self.lcao: 

88 assert paw.wfs.dtype == float 

89 self.wfs = paw.wfs 

90 

91 def initialize(self, kpt, i, j): 

92 """initialize yourself with the wavefunctions""" 

93 self.i = i 

94 self.j = j 

95 

96 if kpt is not None: 

97 self.spin = kpt.s 

98 self.k = kpt.k 

99 self.weight = kpt.weight 

100 self.P_ani = kpt.P_ani 

101 if self.lcao: 

102 self.q = kpt.q 

103 self.wfi_M = kpt.C_nM[i] 

104 self.wfj_M = kpt.C_nM[j] 

105 else: 

106 self.wfi = kpt.psit_nG[i] 

107 self.wfj = kpt.psit_nG[j] 

108 

109 def get_lcao(self, finegrid=False): 

110 """Get pair density""" 

111 # Expand the pair density as density matrix 

112 rho_MM = (0.5 * np.outer(self.wfi_M, self.wfj_M) + 

113 0.5 * np.outer(self.wfj_M, self.wfi_M)) 

114 

115 rho_G = self.density.gd.zeros() 

116 self.wfs.basis_functions.construct_density(rho_MM, rho_G, self.q) 

117 

118 if not finegrid: 

119 return rho_G 

120 

121 # interpolate the pair density to the fine grid 

122 rho_g = self.density.finegd.zeros() 

123 self.density.interpolator.apply(rho_G, rho_g) 

124 

125 return rho_g 

126 

127 def get(self, finegrid=False): 

128 """Get pair density""" 

129 

130 if self.lcao: 

131 return self.get_lcao(finegrid) 

132 

133 nijt_G = self.wfi.conj() * self.wfj 

134 if not finegrid: 

135 return nijt_G 

136 

137 # interpolate the pair density to the fine grid 

138 nijt_g = self.density.finegd.empty(dtype=nijt_G.dtype) 

139 self.density.interpolator.apply(nijt_G, nijt_g) 

140 

141 return nijt_g 

142 

143 def with_compensation_charges(self, finegrid=False): 

144 """Get pair density including the compensation charges""" 

145 rhot_g = self.get(finegrid) 

146 

147 # Determine the compensation charges for each nucleus 

148 Q_aL = {} 

149 for a, P_ni in self.P_ani.items(): 

150 # Generate density matrix 

151 Pi_i = P_ni[self.i].conj() 

152 Pj_i = P_ni[self.j] 

153 D_ii = np.outer(Pi_i, Pj_i) 

154 # allowed to pack as used in the scalar product with 

155 # the symmetric array Delta_pL 

156 D_p = pack_density(D_ii) 

157 

158 # Determine compensation charge coefficients: 

159 Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) 

160 

161 # Add compensation charges 

162 if finegrid: 

163 self.density.ghat.add(rhot_g, Q_aL) 

164 else: 

165 if not hasattr(self.density, 'Ghat'): 

166 self.density.Ghat = LFC(self.density.gd, 

167 [setup.ghat_l 

168 for setup in self.setups], 

169 integral=sqrt(4 * pi)) 

170 self.density.Ghat.set_positions(self.spos_ac) 

171 self.density.Ghat.add(rhot_g, Q_aL) 

172 

173 return rhot_g 

174 

175 def with_ae_corrections(self, finegrid=False): 

176 """Get pair density including the AE corrections""" 

177 nij_g = self.get(finegrid) 

178 

179 # Generate the density matrix 

180 D_ap = {} 

181# D_aii = {} 

182 for a, P_ni in self.P_ani.items(): 

183 Pi_i = P_ni[self.i] 

184 Pj_i = P_ni[self.j] 

185 D_ii = np.outer(Pi_i.conj(), Pj_i) 

186 # Note: D_ii is not symmetric but the products of partial waves are 

187 # so that we can pack 

188 D_ap[a] = pack_density(D_ii) 

189# D_aii[a] = D_ii 

190 

191 # Load partial waves if needed 

192 if ((finegrid and (not hasattr(self, 'phi'))) or 

193 ((not finegrid) and (not hasattr(self, 'Phi')))): 

194 

195 # Splines 

196 splines = {} 

197 phi_aj = [] 

198 phit_aj = [] 

199 for a, id in enumerate(self.setups.id_a): 

200 if id in splines: 

201 phi_j, phit_j = splines[id] 

202 else: 

203 # Load splines: 

204 phi_j, phit_j = self.setups[a].get_partial_waves()[:2] 

205 splines[id] = (phi_j, phit_j) 

206 phi_aj.append(phi_j) 

207 phit_aj.append(phit_j) 

208 

209 # Store partial waves as class variables 

210 if finegrid: 

211 gd = self.density.finegd 

212 self.__class__.phi = BasisFunctions(gd, phi_aj) 

213 self.__class__.phit = BasisFunctions(gd, phit_aj) 

214 self.__class__.phi.set_positions(self.spos_ac) 

215 self.__class__.phit.set_positions(self.spos_ac) 

216 else: 

217 gd = self.density.gd 

218 self.__class__.Phi = BasisFunctions(gd, phi_aj) 

219 self.__class__.Phit = BasisFunctions(gd, phit_aj) 

220 self.__class__.Phi.set_positions(self.spos_ac) 

221 self.__class__.Phit.set_positions(self.spos_ac) 

222 

223 # Add AE corrections 

224 if finegrid: 

225 phi = self.phi 

226 phit = self.phit 

227 gd = self.density.finegd 

228 else: 

229 phi = self.Phi 

230 phit = self.Phit 

231 gd = self.density.gd 

232 

233 rho_MM = np.zeros((phi.Mmax, phi.Mmax)) 

234 M1 = 0 

235 for a, setup in enumerate(self.setups): 

236 ni = setup.ni 

237 D_p = D_ap.get(a) 

238 if D_p is None: 

239 D_p = np.empty(ni * (ni + 1) // 2) 

240 if gd.comm.size > 1: 

241 gd.comm.broadcast(D_p, self.wfs.partition.rank_a[a]) 

242 D_ii = unpack_density(D_p) 

243# D_ii = D_aii.get(a) 

244# if D_ii is None: 

245# D_ii = np.empty((ni, ni)) 

246# if gd.comm.size > 1: 

247# gd.comm.broadcast(D_ii, self.wfs.atom_partition.rank_a[a]) 

248 M2 = M1 + ni 

249 rho_MM[M1:M2, M1:M2] = D_ii 

250 M1 = M2 

251 

252 # construct_density assumes symmetric rho_MM and 

253 # takes only the upper half of it 

254 phi.construct_density(rho_MM, nij_g, q=-1) 

255 phit.construct_density(-rho_MM, nij_g, q=-1) 

256 # TODO: use ae_valence_density_correction 

257# phi.lfc.ae_valence_density_correction( 

258# rho_MM, nij_g, np.zeros(len(phi.M_W), np.intc), np.zeros(self.na)) 

259# phit.lfc.ae_valence_density_correction( 

260# -rho_MM, nij_g, np.zeros(len(phit.M_W), np.intc), 

261# np.zeros(self.na)) 

262 

263 return nij_g