def calculate_exx_paw_correction(self): exx = 0 deg = 2 // self.nspins # spin degeneracy for a, D_sp in self.density.D_asp.items(): setup = self.setups[a] for D_p in D_sp: D_ii = unpack2(D_p) ni = len(D_ii) for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] exx -= self.hybrid / deg * D_ii[i1, i2] * A if self.core_valence: if setup.X_p is not None: exx -= self.hybrid * np.dot(D_p, setup.X_p) if self.coredensity: exx += self.hybrid * setup.ExxC return exx
def calculate_exx_paw_correction(self): self.timer.start('PAW correction') self.devv = 0.0 self.evc = 0.0 self.ecc = 0.0 deg = 2 // self.wfs.nspins # spin degeneracy for a, D_sp in self.dens.D_asp.items(): setup = self.wfs.setups[a] for D_p in D_sp: D_ii = unpack2(D_p) ni = len(D_ii) for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] self.devv -= D_ii[i1, i2] * A / deg self.evc -= np.dot(D_p, setup.X_p) self.ecc += setup.ExxC if not self.bandstructure: self.timer.stop('PAW correction') return Q = self.world.size // self.wfs.kd.comm.size self.exx_skn *= Q for kpt in self.wfs.kpt_u: for a, D_sp in self.dens.D_asp.items(): setup = self.wfs.setups[a] for D_p in D_sp: D_ii = unpack2(D_p) ni = len(D_ii) P_ni = kpt.P_ani[a] for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] self.exx_skn[kpt.s, kpt.k] -= \ (A * P_ni[:, i1].conj() * P_ni[:, i2]).real p12 = packed_index(i1, i2, ni) self.exx_skn[kpt.s, kpt.k] -= \ (P_ni[:, i1].conj() * setup.X_p[p12] * P_ni[:, i2]).real / self.wfs.nspins self.world.sum(self.exx_skn) self.exx_skn *= self.hybrid / Q self.timer.stop('PAW correction')
def pawexxvv(atomdata, D_ii): """PAW correction for valence-valence EXX energy.""" ni = len(D_ii) V_ii = np.empty((ni, ni)) for i1 in range(ni): for i2 in range(ni): V = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) V += atomdata.M_pp[p13, p24] * D_ii[i3, i4] V_ii[i1, i2] = V return V_ii
def dipole_op(c, state1, state2, k=0, s=0): # Taken from KSSingle, maybe make this accessible in # KSSingle? wfs = c.wfs gd = wfs.gd kpt = None for i in wfs.kpt_u: if i.k == k and i.s == s: kpt = i pd = PairDensity(c) pd.initialize(kpt, state1, state2) # coarse grid contribution # <i|r|j> is the negative of the dipole moment (because of negative # e- charge) me = -gd.calculate_dipole_moment(pd.get()) # augmentation contributions ma = np.zeros(me.shape) pos_av = c.get_atoms().get_positions() / Bohr for a, P_ni in kpt.P_ani.items(): Ra = pos_av[a] Pi_i = P_ni[state1] Pj_i = P_ni[state2] Delta_pL = wfs.setups[a].Delta_pL ni = len(Pi_i) ma0 = 0 ma1 = np.zeros(me.shape) for i in range(ni): for j in range(ni): pij = Pi_i[i] * Pj_i[j] ij = packed_index(i, j, ni) # L=0 term ma0 += Delta_pL[ij, 0] * pij # L=1 terms if wfs.setups[a].lmax >= 1: # see spherical_harmonics.py for # L=1:y L=2:z; L=3:x ma1 += np.array([ Delta_pL[ij, 3], Delta_pL[ij, 1], Delta_pL[ij, 2] ]) * pij ma += sqrt(4 * pi / 3) * ma1 + Ra * sqrt(4 * pi) * ma0 gd.comm.sum(ma) me += ma return me * Bohr
def calculate_vv(ni, D_ii, M_pp, weight, addme=False): """Calculate the local corrections depending on Mpp.""" dexx = 0 dekin = 0 if not addme: addsign = -2.0 else: addsign = 2.0 for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) if Htpsit_nG is not None: dH_p[p12] += addsign * weight / \ deg * A / ((i1 != i2) + 1) dekin += 2 * weight / deg * D_ii[i1, i2] * A dexx -= weight / deg * D_ii[i1, i2] * A return (dexx, dekin)
def dipole_op(c, state1, state2, k=0, s=0): # Taken from KSSingle, maybe make this accessible in # KSSingle? wfs = c.wfs gd = wfs.gd kpt = None for i in wfs.kpt_u: if i.k == k and i.s == s: kpt = i pd = PairDensity(c) pd.initialize(kpt, state1, state2) # coarse grid contribution # <i|r|j> is the negative of the dipole moment (because of negative # e- charge) me = -gd.calculate_dipole_moment(pd.get()) # augmentation contributions ma = np.zeros(me.shape) pos_av = c.get_atoms().get_positions() / Bohr for a, P_ni in kpt.P_ani.items(): Ra = pos_av[a] Pi_i = P_ni[state1] Pj_i = P_ni[state2] Delta_pL = wfs.setups[a].Delta_pL ni = len(Pi_i) ma0 = 0 ma1 = np.zeros(me.shape) for i in range(ni): for j in range(ni): pij = Pi_i[i]*Pj_i[j] ij = packed_index(i, j, ni) # L=0 term ma0 += Delta_pL[ij,0]*pij # L=1 terms if wfs.setups[a].lmax >= 1: # see spherical_harmonics.py for # L=1:y L=2:z; L=3:x ma1 += np.array([Delta_pL[ij,3], Delta_pL[ij,1], Delta_pL[ij,2]]) * pij ma += sqrt(4 * pi / 3) * ma1 + Ra * sqrt(4 * pi) * ma0 gd.comm.sum(ma) me += ma return me * Bohr
def calculate_exx_paw_correction(self): exx = 0 deg = 2 // self.nspins # spin degeneracy for a, D_sp in self.density.D_asp.items(): setup = self.setups[a] for D_p in D_sp: D_ii = unpack2(D_p) ni = len(D_ii) for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) exx -= self.hybrid / deg * D_ii[i1, i2] * A if setup.X_p is not None: exx -= self.hybrid * np.dot(D_p, setup.X_p) exx += self.hybrid * setup.ExxC return exx
def atomic_val_val(self, paw, H_nn, u=0): deg = 2 / self.nspins kpt = paw.wfs.kpt_u[u] for a, P_ni in kpt.P_ani.items(): # Add atomic corrections to the valence-valence exchange energy # -- # > D C D # -- ii iiii ii setup = paw.wfs.setups[a] D_p = paw.density.D_asp[a][kpt.s] H_p = np.zeros_like(D_p) D_ii = unpack2(D_p) ni = len(D_ii) for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) H_p[p12] -= 2 / deg * A / ((i1 != i2) + 1) H_nn += np.dot(P_ni, np.inner(unpack(H_p), P_ni.conj()))
def _nuc_corr(self, i_m, j_d, k_m, k_d): ma = 0 for a, P_ni_m in self.c_m.wfs.kpt_u[k_m].P_ani.items(): P_ni_d = self.c_d.wfs.kpt_u[k_d].P_ani[a] Pi_i = P_ni_m[i_m] Pj_i = P_ni_d[j_d] Delta_pL = self.c_m.wfs.setups[a].Delta_pL for i in range(len(Pi_i)): for j in range(len(Pj_i)): pij = Pi_i[i] * Pj_i[j] ij = packed_index(i, j, len(Pi_i)) ma += Delta_pL[ij, 0] * pij self.gd.comm.sum(ma) return sqrt(4 * pi) * ma
def _nuc_corr(self, i_m, j_d, k_m, k_d): ma = 0.0 for a, P_ni_m in self.c_m.wfs.kpt_u[k_m].P_ani.items(): P_ni_d = self.c_d.wfs.kpt_u[k_d].P_ani[a] Pi_i = P_ni_m[i_m] Pj_i = P_ni_d[j_d] Delta_pL = self.c_m.wfs.setups[a].Delta_pL for i in range(len(Pi_i)): for j in range(len(Pj_i)): pij = Pi_i[i] * Pj_i[j] ij = packed_index(i, j, len(Pi_i)) ma += Delta_pL[ij, 0] * pij ma = self.gd.comm.sum(ma) return sqrt(4 * pi) * ma
def full(self, other, myspin=0, otherspin=0): """Overlap of Kohn-Sham states including local terms. Parameter --------- other: gpaw gpaw-object containing wave functions Returns ------- out: array u_kij = \int dx mypsi_ki^*(x) otherpsi_kj(x) """ ov_knn = self.pseudo(other, normalize=False) for k in range(self.nk): # XXX what if parallelized over spin or kpoints ? kpt_rank, u = self.kd.get_rank_and_index(myspin, k) assert self.kd.comm.rank == kpt_rank kpt_rank, uo = other.wfs.kd.get_rank_and_index(otherspin, k) assert self.kd.comm.rank == kpt_rank mkpt = self.calc.wfs.kpt_u[u] okpt = other.wfs.kpt_u[uo] aov_nn = np.zeros_like(ov_knn[k]) for a, mP_ni in mkpt.P_ani.items(): oP_ni = okpt.P_ani[a] Delta_p = (np.sqrt(4 * np.pi) * self.calc.wfs.setups[a].Delta_pL[:, 0]) for n0, mP_i in enumerate(mP_ni): for n1, oP_i in enumerate(oP_ni): ni = len(mP_i) assert (len(oP_i) == ni) for i, mP in enumerate(mP_i): for j, oP in enumerate(oP_i): ij = packed_index(i, j, ni) aov_nn[n0, n1] += Delta_p[ij] * mP.conj() * oP self.calc.wfs.gd.comm.sum(aov_nn) ov_knn[k] += aov_nn return ov_knn
def __init__(self, iidx=None, jidx=None, pspin=None, kpt=None, paw=None, string=None, fijscale=1): if string is not None: self.fromstring(string) return None # normal entry PairDensity.__init__(self, paw) wfs = paw.wfs PairDensity.initialize(self, kpt, iidx, jidx) self.pspin = pspin f = kpt.f_n self.fij = (f[iidx] - f[jidx]) * fijscale e = kpt.eps_n self.energy = e[jidx] - e[iidx] # calculate matrix elements ----------- gd = wfs.gd self.gd = gd # length form .......................... # course grid contribution # <i|r|j> is the negative of the dipole moment (because of negative # e- charge) me = -gd.calculate_dipole_moment(self.get()) # augmentation contributions ma = np.zeros(me.shape) pos_av = paw.atoms.get_positions() / Bohr for a, P_ni in kpt.P_ani.items(): Ra = pos_av[a] Pi_i = P_ni[self.i] Pj_i = P_ni[self.j] Delta_pL = wfs.setups[a].Delta_pL ni = len(Pi_i) ma0 = 0 ma1 = np.zeros(me.shape) for i in range(ni): for j in range(ni): pij = Pi_i[i] * Pj_i[j] ij = packed_index(i, j, ni) # L=0 term ma0 += Delta_pL[ij, 0] * pij # L=1 terms if wfs.setups[a].lmax >= 1: # see spherical_harmonics.py for # L=1:y L=2:z; L=3:x ma1 += np.array([ Delta_pL[ij, 3], Delta_pL[ij, 1], Delta_pL[ij, 2] ]) * pij ma += sqrt(4 * pi / 3) * ma1 + Ra * sqrt(4 * pi) * ma0 gd.comm.sum(ma) ## print '<KSSingle> i,j,me,ma,fac=',self.i,self.j,\ ## me, ma,sqrt(self.energy*self.fij) # print 'l: me, ma', me, ma self.me = sqrt(self.energy * self.fij) * (me + ma) self.mur = -(me + ma) ## print '<KSSingle> mur=',self.mur,-self.fij *me # velocity form ............................. self.muv = np.zeros(me.shape) # does not work XXX
def __init__(self, iidx=None, jidx=None, pspin=None, kpt=None, paw=None, string=None, fijscale=1): if string is not None: self.fromstring(string) return None # normal entry PairDensity.__init__(self, paw) wfs = paw.wfs PairDensity.initialize(self, kpt, iidx, jidx) self.pspin=pspin f = kpt.f_n self.fij = (f[iidx] - f[jidx]) * fijscale e = kpt.eps_n self.energy = e[jidx] - e[iidx] # calculate matrix elements ----------- gd = wfs.gd self.gd = gd # length form .......................... # course grid contribution # <i|r|j> is the negative of the dipole moment (because of negative # e- charge) me = - gd.calculate_dipole_moment(self.get()) # augmentation contributions ma = np.zeros(me.shape) pos_av = paw.atoms.get_positions() / Bohr for a, P_ni in kpt.P_ani.items(): Ra = pos_av[a] Pi_i = P_ni[self.i] Pj_i = P_ni[self.j] Delta_pL = wfs.setups[a].Delta_pL ni=len(Pi_i) ma0 = 0 ma1 = np.zeros(me.shape) for i in range(ni): for j in range(ni): pij = Pi_i[i]*Pj_i[j] ij = packed_index(i, j, ni) # L=0 term ma0 += Delta_pL[ij,0]*pij # L=1 terms if wfs.setups[a].lmax >= 1: # see spherical_harmonics.py for # L=1:y L=2:z; L=3:x ma1 += np.array([Delta_pL[ij,3], Delta_pL[ij,1], Delta_pL[ij,2]]) * pij ma += sqrt(4 * pi / 3) * ma1 + Ra * sqrt(4 * pi) * ma0 gd.comm.sum(ma) self.me = sqrt(self.energy * self.fij) * ( me + ma ) self.mur = - ( me + ma ) # velocity form ............................. me = np.zeros(self.mur.shape) # get derivatives dtype = self.wfj.dtype dwfj_cg = gd.empty((3), dtype=dtype) if not hasattr(gd, 'ddr'): gd.ddr = [Gradient(gd, c, dtype=dtype).apply for c in range(3)] for c in range(3): gd.ddr[c](self.wfj, dwfj_cg[c], kpt.phase_cd) me[c] = gd.integrate(self.wfi * dwfj_cg[c]) if 0: me2 = np.zeros(self.mur.shape) for c in range(3): gd.ddr[c](self.wfi, dwfj_cg[c], kpt.phase_cd) me2[c] = gd.integrate(self.wfj * dwfj_cg[c]) print me, -me2, me2+me # augmentation contributions ma = np.zeros(me.shape) for a, P_ni in kpt.P_ani.items(): Pi_i = P_ni[self.i] Pj_i = P_ni[self.j] nabla_iiv = paw.wfs.setups[a].nabla_iiv for c in range(3): for i1, Pi in enumerate(Pi_i): for i2, Pj in enumerate(Pj_i): ma[c] += Pi * Pj * nabla_iiv[i1, i2, c] gd.comm.sum(ma) self.muv = - (me + ma) / self.energy ## print self.mur, self.muv, self.mur - self.muv # magnetic transition dipole ................ magn = np.zeros(me.shape) r_cg, r2_g = coordinates(gd) wfi_g = self.wfi for ci in range(3): cj = (ci + 1) % 3 ck = (ci + 2) % 3 magn[ci] = gd.integrate(wfi_g * r_cg[cj] * dwfj_cg[ck] - wfi_g * r_cg[ck] * dwfj_cg[cj] ) # augmentation contributions ma = np.zeros(magn.shape) for a, P_ni in kpt.P_ani.items(): Pi_i = P_ni[self.i] Pj_i = P_ni[self.j] rnabla_iiv = paw.wfs.setups[a].rnabla_iiv for c in range(3): for i1, Pi in enumerate(Pi_i): for i2, Pj in enumerate(Pj_i): ma[c] += Pi * Pj * rnabla_iiv[i1, i2, c] gd.comm.sum(ma) self.magn = -alpha / 2. * (magn + ma)
def apply_orbital_dependent_hamiltonian(self, kpt, psit_nG, Htpsit_nG=None, dH_asp=None): if kpt.f_n is None: return deg = 2 // self.nspins # Spin degeneracy hybrid = self.hybrid P_ani = kpt.P_ani setups = self.setups vt_g = self.finegd.empty() if self.gd is not self.finegd: vt_G = self.gd.empty() nocc = int(kpt.f_n.sum()) // (3 - self.nspins) if self.unocc: nbands = len(kpt.f_n) else: nbands = nocc self.nocc_s[kpt.s] = nocc if Htpsit_nG is not None: kpt.vt_nG = self.gd.empty(nbands) kpt.vxx_ani = {} kpt.vxx_anii = {} for a, P_ni in P_ani.items(): I = P_ni.shape[1] kpt.vxx_ani[a] = np.zeros((nbands, I)) kpt.vxx_anii[a] = np.zeros((nbands, I, I)) exx = 0.0 ekin = 0.0 # Determine pseudo-exchange for n1 in range(nbands): psit1_G = psit_nG[n1] f1 = kpt.f_n[n1] / deg for n2 in range(n1, nbands): psit2_G = psit_nG[n2] f2 = kpt.f_n[n2] / deg # Double count factor: dc = (1 + (n1 != n2)) * deg nt_G, rhot_g = self.calculate_pair_density(n1, n2, psit_nG, P_ani) vt_g[:] = 0.0 iter = self.poissonsolver.solve(vt_g, -rhot_g, charge=-float(n1 == n2), eps=1e-12, zero_initial_phi=True) vt_g *= hybrid if self.gd is self.finegd: vt_G = vt_g else: self.restrictor.apply(vt_g, vt_G) # Integrate the potential on fine and coarse grids int_fine = self.finegd.integrate(vt_g * rhot_g) int_coarse = self.gd.integrate(vt_G * nt_G) if self.gd.comm.rank == 0: # only add to energy on master CPU exx += 0.5 * dc * f1 * f2 * int_fine ekin -= dc * f1 * f2 * int_coarse if Htpsit_nG is not None: Htpsit_nG[n1] += f2 * vt_G * psit2_G if n1 == n2: kpt.vt_nG[n1] = f1 * vt_G else: Htpsit_nG[n2] += f1 * vt_G * psit1_G # Update the vxx_uni and vxx_unii vectors of the nuclei, # used to determine the atomic hamiltonian, and the # residuals v_aL = self.ghat.dict() self.ghat.integrate(vt_g, v_aL) for a, v_L in v_aL.items(): v_ii = unpack(np.dot(setups[a].Delta_pL, v_L)) v_ni = kpt.vxx_ani[a] v_nii = kpt.vxx_anii[a] P_ni = P_ani[a] v_ni[n1] += f2 * np.dot(v_ii, P_ni[n2]) if n1 != n2: v_ni[n2] += f1 * np.dot(v_ii, P_ni[n1]) else: # XXX Check this: v_nii[n1] = f1 * v_ii # Apply the atomic corrections to the energy and the Hamiltonian matrix for a, P_ni in P_ani.items(): setup = setups[a] if Htpsit_nG is not None: # Add non-trivial corrections the Hamiltonian matrix h_nn = symmetrize(np.inner(P_ni[:nbands], kpt.vxx_ani[a][:nbands])) ekin -= np.dot(kpt.f_n[:nbands], h_nn.diagonal()) dH_p = dH_asp[a][kpt.s] # Get atomic density and Hamiltonian matrices D_p = self.density.D_asp[a][kpt.s] D_ii = unpack2(D_p) ni = len(D_ii) # Add atomic corrections to the valence-valence exchange energy # -- # > D C D # -- ii iiii ii for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) if Htpsit_nG is not None: dH_p[p12] -= 2 * hybrid / deg * A / ((i1 != i2) + 1) ekin += 2 * hybrid / deg * D_ii[i1, i2] * A exx -= hybrid / deg * D_ii[i1, i2] * A # Add valence-core exchange energy # -- # > X D # -- ii ii if setup.X_p is not None: exx -= hybrid * np.dot(D_p, setup.X_p) if Htpsit_nG is not None: dH_p -= hybrid * setup.X_p ekin += hybrid * np.dot(D_p, setup.X_p) # Add core-core exchange energy if kpt.s == 0: exx += hybrid * setup.ExxC self.exx_s[kpt.s] = self.gd.comm.sum(exx) self.ekin_s[kpt.s] = self.gd.comm.sum(ekin)
def calculate_exx(self): """Non-selfconsistent calculation.""" kd = self.kd K = kd.nibzkpts W = self.world.size // self.nspins parallel = (W > 1) self.log("%d CPU's used for %d IBZ k-points" % (W, K)) self.log('Spins:', self.nspins) if self.etotflag and not self.gygi: self.nbandstmp = 0 for s in range(self.nspins): kpt1_k = [KPoint(kd, kpt) for kpt in self.kpt_u if kpt.s == s] for kpt1 in kpt1_k: for n1 in range(self.bd.nbands): f_n = kpt1.f_n[n1] if np.abs(f_n) < 1e-10: self.nbandstmp = max(self.nbandstmp, n1) break else: self.nbandstmp = self.bd.nbands tmp = np.zeros(kd.comm.size, dtype=int) kd.comm.all_gather(np.array([self.nbandstmp]), tmp) self.nbands = tmp.max() else: self.nbands = self.bd.nbands B = self.nbands self.log('Number of bands calculated:', B) self.log('Number of valence electrons:', self.setups.nvalence) E = B - self.setups.nvalence / 2.0 # empty bands self.npairs = (K * kd.nbzkpts - 0.5 * K**2) * (B**2 - E**2) self.log('Approximate number of pairs:', self.npairs) if not self.etotflag: self.exx_skn = np.zeros((self.nspins, K, B)) self.debug_skn = np.zeros((self.nspins, K, B)) for s in range(self.nspins): kpt1_q = [KPoint(kd, kpt) for kpt in self.kpt_u if kpt.s == s] kpt2_q = kpt1_q[:] if len(kpt1_q) == 0: # No s-spins on this CPU: continue # Send rank: srank = kd.get_rank_and_index(s, (kpt1_q[0].k - 1) % K)[0] # Receive rank: rrank = kd.get_rank_and_index(s, (kpt1_q[-1].k + 1) % K)[0] # Shift k-points K - 1 times: for i in range(K): if i < K - 1: if parallel: kpt = kpt2_q[-1].next() kpt.start_receiving(rrank) kpt2_q[0].start_sending(srank) else: kpt = kpt2_q[0] for kpt1, kpt2 in zip(kpt1_q, kpt2_q): for k, ik in enumerate(kd.bz2ibz_k): if ik == kpt2.k: self.apply(kpt1, kpt2, k) if i < K - 1: if parallel: kpt.wait() kpt2_q[0].wait() kpt2_q.pop(0) kpt2_q.append(kpt) if self.etotflag: if self.acdf: self.exxacdf = self.world.sum(self.exxacdf[0]) self.exx = self.exxacdf else: self.exx = self.world.sum(self.exx) self.exx += self.calculate_exx_paw_correction() else: for kpt in self.kpt_u: for a, D_sp in self.density.D_asp.items(): setup = self.setups[a] for D_p in D_sp: D_ii = unpack2(D_p) ni = len(D_ii) P_ni = kpt.P_ani[a] for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] self.exx_skn[kpt.s, kpt.k] -= \ (self.hybrid * A * P_ni[:, i1].conj() * P_ni[:, i2]).real p12 = packed_index(i1, i2, ni) if self.core_valence: if setup.X_p is not None: self.exx_skn[kpt.s, kpt.k] -= self.hybrid * \ (P_ni[:, i1].conj() * setup.X_p[p12] * P_ni[:, i2]).real / self.nspins self.world.sum(self.exx_skn) self.exx = 0.0 for kpt in self.kpt_u: self.exx += 0.5 * np.dot(kpt.f_n, self.exx_skn[kpt.s, kpt.k]) self.exx = self.world.sum(self.exx) for a, D_sp in self.density.D_asp.items(): setup = self.setups[a] if self.coredensity: self.exx += self.hybrid * setup.ExxC if self.core_valence: self.exx -= self.hybrid * 0.5 * np.dot( D_sp.sum(0), setup.X_p) self.world.sum(self.debug_skn) assert (self.debug_skn == self.kd.nbzkpts * B).all()
def apply_orbital_dependent_hamiltonian(self, kpt, psit_nG, Htpsit_nG=None, dH_asp=None): if kpt.f_n is None: return deg = 2 // self.nspins # Spin degeneracy hybrid = self.hybrid P_ani = kpt.P_ani setups = self.setups vt_g = self.finegd.empty() if self.gd is not self.finegd: vt_G = self.gd.empty() nocc = int(kpt.f_n.sum()) // (3 - self.nspins) if self.unocc: nbands = len(kpt.f_n) else: nbands = nocc self.nocc_s[kpt.s] = nocc if Htpsit_nG is not None: kpt.vt_nG = self.gd.empty(nbands) kpt.vxx_ani = {} kpt.vxx_anii = {} for a, P_ni in P_ani.items(): I = P_ni.shape[1] kpt.vxx_ani[a] = np.zeros((nbands, I)) kpt.vxx_anii[a] = np.zeros((nbands, I, I)) exx = 0.0 ekin = 0.0 # Determine pseudo-exchange for n1 in range(nbands): psit1_G = psit_nG[n1] f1 = kpt.f_n[n1] / deg for n2 in range(n1, nbands): psit2_G = psit_nG[n2] f2 = kpt.f_n[n2] / deg # Double count factor: dc = (1 + (n1 != n2)) * deg nt_G, rhot_g = self.calculate_pair_density( n1, n2, psit_nG, P_ani) vt_g[:] = 0.0 iter = self.poissonsolver.solve(vt_g, -rhot_g, charge=-float(n1 == n2), eps=1e-12, zero_initial_phi=True) vt_g *= hybrid if self.gd is self.finegd: vt_G = vt_g else: self.restrictor.apply(vt_g, vt_G) # Integrate the potential on fine and coarse grids int_fine = self.finegd.integrate(vt_g * rhot_g) int_coarse = self.gd.integrate(vt_G * nt_G) if self.gd.comm.rank == 0: # only add to energy on master CPU exx += 0.5 * dc * f1 * f2 * int_fine ekin -= dc * f1 * f2 * int_coarse if Htpsit_nG is not None: Htpsit_nG[n1] += f2 * vt_G * psit2_G if n1 == n2: kpt.vt_nG[n1] = f1 * vt_G else: Htpsit_nG[n2] += f1 * vt_G * psit1_G # Update the vxx_uni and vxx_unii vectors of the nuclei, # used to determine the atomic hamiltonian, and the # residuals v_aL = self.ghat.dict() self.ghat.integrate(vt_g, v_aL) for a, v_L in v_aL.items(): v_ii = unpack(np.dot(setups[a].Delta_pL, v_L)) v_ni = kpt.vxx_ani[a] v_nii = kpt.vxx_anii[a] P_ni = P_ani[a] v_ni[n1] += f2 * np.dot(v_ii, P_ni[n2]) if n1 != n2: v_ni[n2] += f1 * np.dot(v_ii, P_ni[n1]) else: # XXX Check this: v_nii[n1] = f1 * v_ii # Apply the atomic corrections to the energy and the Hamiltonian matrix for a, P_ni in P_ani.items(): setup = setups[a] if Htpsit_nG is not None: # Add non-trivial corrections the Hamiltonian matrix h_nn = symmetrize( np.inner(P_ni[:nbands], kpt.vxx_ani[a][:nbands])) ekin -= np.dot(kpt.f_n[:nbands], h_nn.diagonal()) dH_p = dH_asp[a][kpt.s] # Get atomic density and Hamiltonian matrices D_p = self.density.D_asp[a][kpt.s] D_ii = unpack2(D_p) ni = len(D_ii) # Add atomic corrections to the valence-valence exchange energy # -- # > D C D # -- ii iiii ii for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) if Htpsit_nG is not None: dH_p[p12] -= 2 * hybrid / deg * A / ((i1 != i2) + 1) ekin += 2 * hybrid / deg * D_ii[i1, i2] * A exx -= hybrid / deg * D_ii[i1, i2] * A # Add valence-core exchange energy # -- # > X D # -- ii ii if setup.X_p is not None: exx -= hybrid * np.dot(D_p, setup.X_p) if Htpsit_nG is not None: dH_p -= hybrid * setup.X_p ekin += hybrid * np.dot(D_p, setup.X_p) # Add core-core exchange energy if kpt.s == 0: exx += hybrid * setup.ExxC self.exx_s[kpt.s] = self.gd.comm.sum(exx) self.ekin_s[kpt.s] = self.gd.comm.sum(ekin)
def __init__(self, iidx=None, jidx=None, pspin=None, kpt=None, paw=None, string=None, fijscale=1, dtype=float): if string is not None: self.fromstring(string, dtype) return None # normal entry PairDensity.__init__(self, paw) PairDensity.initialize(self, kpt, iidx, jidx) self.pspin = pspin self.energy = 0.0 self.fij = 0.0 self.me = np.zeros((3), dtype=dtype) self.mur = np.zeros((3), dtype=dtype) self.muv = np.zeros((3), dtype=dtype) self.magn = np.zeros((3), dtype=dtype) self.kpt_comm = paw.wfs.kd.comm # leave empty if not my kpt if kpt is None: return wfs = paw.wfs gd = wfs.gd self.energy = kpt.eps_n[jidx] - kpt.eps_n[iidx] self.fij = (kpt.f_n[iidx] - kpt.f_n[jidx]) * fijscale # calculate matrix elements ----------- # length form .......................... # course grid contribution # <i|r|j> is the negative of the dipole moment (because of negative # e- charge) me = -gd.calculate_dipole_moment(self.get()) # augmentation contributions ma = np.zeros(me.shape, dtype=dtype) pos_av = paw.atoms.get_positions() / Bohr for a, P_ni in kpt.P_ani.items(): Ra = pos_av[a] Pi_i = P_ni[self.i].conj() Pj_i = P_ni[self.j] Delta_pL = wfs.setups[a].Delta_pL ni = len(Pi_i) ma0 = 0 ma1 = np.zeros(me.shape, dtype=me.dtype) for i in range(ni): for j in range(ni): pij = Pi_i[i] * Pj_i[j] ij = packed_index(i, j, ni) # L=0 term ma0 += Delta_pL[ij, 0] * pij # L=1 terms if wfs.setups[a].lmax >= 1: # see spherical_harmonics.py for # L=1:y L=2:z; L=3:x ma1 += np.array([ Delta_pL[ij, 3], Delta_pL[ij, 1], Delta_pL[ij, 2] ]) * pij ma += sqrt(4 * pi / 3) * ma1 + Ra * sqrt(4 * pi) * ma0 gd.comm.sum(ma) self.me = sqrt(self.energy * self.fij) * (me + ma) self.mur = -(me + ma) # velocity form ............................. if self.lcao: # XXX Velocity form not supported in LCAO return me = np.zeros(self.mur.shape, dtype=dtype) # get derivatives dtype = self.wfj.dtype dwfj_cg = gd.empty((3), dtype=dtype) if not hasattr(gd, 'ddr'): gd.ddr = [Gradient(gd, c, dtype=dtype).apply for c in range(3)] for c in range(3): gd.ddr[c](self.wfj, dwfj_cg[c], kpt.phase_cd) me[c] = gd.integrate(self.wfi.conj() * dwfj_cg[c]) if 0: me2 = np.zeros(self.mur.shape) for c in range(3): gd.ddr[c](self.wfi, dwfj_cg[c], kpt.phase_cd) me2[c] = gd.integrate(self.wfj * dwfj_cg[c]) print(me, -me2, me2 + me) # augmentation contributions ma = np.zeros(me.shape, dtype=me.dtype) for a, P_ni in kpt.P_ani.items(): Pi_i = P_ni[self.i].conj() Pj_i = P_ni[self.j] nabla_iiv = paw.wfs.setups[a].nabla_iiv for c in range(3): for i1, Pi in enumerate(Pi_i): for i2, Pj in enumerate(Pj_i): ma[c] += Pi * Pj * nabla_iiv[i1, i2, c] gd.comm.sum(ma) self.muv = -(me + ma) / self.energy # magnetic transition dipole ................ r_cg, r2_g = coordinates(gd) magn = np.zeros(me.shape, dtype=dtype) wfi_g = self.wfi.conj() for ci in range(3): cj = (ci + 1) % 3 ck = (ci + 2) % 3 magn[ci] = gd.integrate(wfi_g * r_cg[cj] * dwfj_cg[ck] - wfi_g * r_cg[ck] * dwfj_cg[cj]) # augmentation contributions ma = np.zeros(magn.shape, dtype=magn.dtype) for a, P_ni in kpt.P_ani.items(): Pi_i = P_ni[self.i].conj() Pj_i = P_ni[self.j] rnabla_iiv = paw.wfs.setups[a].rnabla_iiv for c in range(3): for i1, Pi in enumerate(Pi_i): for i2, Pj in enumerate(Pj_i): ma[c] += Pi * Pj * rnabla_iiv[i1, i2, c] gd.comm.sum(ma) self.magn = -alpha / 2. * (magn + ma)