def calculate_residual_change(self, psit_xG, Htpsit_xG, P_axi, c_axi, n_x): """ """ assert len(n_x) == 1 Htphit_mG = self.Htphit_mG phit_mG = self.phit_mG w_mx = np.zeros((self.nocc, 1), dtype=self.dtype) v_mx = np.zeros((self.nocc, 1), dtype=self.dtype) gemm(self.gd.dv, psit_xG, phit_mG, 0.0, w_mx, 't') gemm(self.gd.dv, psit_xG, Htphit_mG, 0.0, v_mx, 't') # PAW for a, P_mi in self.P_ami.items(): P_xi = P_axi[a] dO_ii = self.setups[a].dO_ii # w_mx += np.dot(P_mi, np.dot(dO_ii, P_xi.T)) for m, dH_p in enumerate(self.dH_amp[a]): dH_ii = unpack(dH_p) v_mx[m] += np.dot(P_mi[m], np.dot(dH_ii, P_xi.T)) # sum over grid-domains self.finegd.comm.sum(w_mx) self.finegd.comm.sum(v_mx) V_mm = 0.5 * (self.V_mm + self.V_mm.T) q_mx = v_mx - np.dot(V_mm, w_mx) if self.stabpot != 0.0: q_mx -= self.stabpot * w_mx gemm(1.0, Htphit_mG, w_mx.T.copy(), 1.0, Htpsit_xG) gemm(1.0, phit_mG, q_mx.T.copy(), 1.0, Htpsit_xG) # PAW for a, P_mi in self.P_ami.items(): c_xi = c_axi[a] ct_mi = P_mi.copy() dO_ii = self.setups[a].dO_ii c_xi += np.dot(q_mx.T, np.dot(P_mi, dO_ii)) for m, dH_p in enumerate(self.dH_amp[a]): dH_ii = unpack(dH_p) ct_mi[m] = np.dot(P_mi[m], dH_ii) c_xi += np.dot(w_mx.T, ct_mi) if self.stabpot != 0.0: Htphit_mG += self.stabpot * psit_xG for a, P_xi in P_axi.items(): dO_ii = self.setups[a].dO_ii c_axi[a] += self.stabpot * np.dot(P_xi, dO_ii)
def calculate_residual(self, psit_nG, Htpsit_nG, P_ani, c_ani): """ Calculate the action of the unified Hamiltonian on an arbitrary state: H_u|Psi> = """ nocc = self.nocc nvirt = psit_nG.shape[0] - nocc # constraint for unoccupied states R_mk = np.zeros((nocc, nvirt), dtype=self.dtype) if nvirt > 0: gemm(self.gd.dv, psit_nG[nocc:], self.Htphit_mG, 0.0, R_mk, 't') # PAW for a, P_mi in self.P_ami.items(): P_ni = P_ani[a] for m, dH_p in enumerate(self.dH_amp[a]): dH_ii = unpack(dH_p) R_mk[m] += np.dot(P_mi[m], np.dot(dH_ii, P_ni[nocc:].T)) self.finegd.comm.sum(R_mk) # self.R_mk = R_mk # R_mk = self.R_mk W_mn = self.W_mn Htphit_mG = self.Htphit_mG phit_mG = self.phit_mG K_mm = 0.5 * (self.V_mm - self.V_mm.T) Q_mn = np.dot(K_mm, W_mn) # Action of unified Hamiltonian on occupied states: if nocc > 0: gemm(1.0, Htphit_mG, W_mn.T.copy(), 1.0, Htpsit_nG[:nocc]) gemm(1.0, phit_mG, Q_mn.T.copy(), 1.0, Htpsit_nG[:nocc]) if nvirt > 0: gemm(1.0, phit_mG, R_mk.T.copy(), 1.0, Htpsit_nG[nocc:]) if self.stabpot != 0.0: Htpsit_nG[nocc:] += self.stabpot * psit_nG[nocc:] # PAW for a, P_mi in self.P_ami.items(): # c_ni = c_ani[a] ct_mi = P_mi.copy() # dO_ii = self.setups[a].dO_ii c_ni[:nocc] += np.dot(Q_mn.T, np.dot(P_mi, dO_ii)) c_ni[nocc:] += np.dot(R_mk.T, np.dot(P_mi, dO_ii)) # for m, dH_p in enumerate(self.dH_amp[a]): dH_ii = unpack(dH_p) ct_mi[m] = np.dot(P_mi[m], dH_ii) c_ni[:nocc] += np.dot(W_mn.T, ct_mi) c_ni[nocc:] += self.stabpot * np.dot(P_ani[a][nocc:], dO_ii)
def calculate_sic_matrixelements(self): # overlap of pseudo wavefunctions Htphit_mG = self.vt_mG * self.phit_mG V_mm = np.zeros((self.nocc, self.nocc), dtype=self.dtype) gemm(self.gd.dv, self.phit_mG, Htphit_mG, 0.0, V_mm, 't') # # PAW for a, P_mi in self.P_ami.items(): for m, dH_p in enumerate(self.dH_amp[a]): dH_ii = unpack(dH_p) V_mm[m, :] += np.dot(P_mi[m], np.dot(dH_ii, P_mi.T)) # accumulate over grid-domains self.gd.comm.sum(V_mm) self.V_mm = V_mm # Symmetrization of V and kappa-matrix: K_mm = 0.5 * (V_mm - V_mm.T.conj()) V_mm = 0.5 * (V_mm + V_mm.T.conj()) # evaluate the kinetic correction self.ekin = -np.trace(V_mm) * (3 - self.nspins) return V_mm, K_mm, np.vdot(K_mm, K_mm).real
def calculate_sic_matrixelements(self): # overlap of pseudo wavefunctions Htphit_mG = self.vt_mG * self.phit_mG V_mm = np.zeros((self.nocc, self.nocc), dtype=self.dtype) gemm(self.gd.dv, self.phit_mG, Htphit_mG, 0.0, V_mm, 't') # # PAW for a, P_mi in self.P_ami.items(): for m, dH_p in enumerate(self.dH_amp[a]): dH_ii = unpack(dH_p) V_mm[m,:] += np.dot(P_mi[m], np.dot(dH_ii, P_mi.T)) # accumulate over grid-domains self.gd.comm.sum(V_mm) self.V_mm = V_mm # Symmetrization of V and kappa-matrix: K_mm = 0.5 * (V_mm - V_mm.T.conj()) V_mm = 0.5 * (V_mm + V_mm.T.conj()) # evaluate the kinetic correction self.ekin = -np.trace(V_mm) * (3 - self.nspins) return V_mm, K_mm, np.vdot(K_mm, K_mm).real
def apply(self, a_xG, b_xG, wfs, kpt, calculate_P_ani=True): """Apply the Hamiltonian operator to a set of vectors. Parameters: a_nG: ndarray Set of vectors to which the overlap operator is applied. b_nG: ndarray, output Resulting S times a_nG vectors. wfs: WaveFunctions Wave-function object defined in wavefunctions.py kpt: KPoint object k-point object defined in kpoint.py. calculate_P_ani: bool When True, the integrals of projector times vectors P_ni = <p_i | a_nG> are calculated. When False, existing P_ani are used """ wfs.kin.apply(a_xG, b_xG, kpt.phase_cd) self.apply_local_potential(a_xG, b_xG, kpt.s) shape = a_xG.shape[:-3] P_axi = wfs.pt.dict(shape) if calculate_P_ani: #TODO calculate_P_ani=False is experimental wfs.pt.integrate(a_xG, P_axi, kpt.q) else: for a, P_ni in kpt.P_ani.items(): P_axi[a][:] = P_ni for a, P_xi in P_axi.items(): dH_ii = unpack(self.dH_asp[a][kpt.s]) P_axi[a] = np.dot(P_xi, dH_ii) wfs.pt.add(b_xG, P_axi, kpt.q)
def apply_to_atomic_matrices(self, dI_asp, P_axi, wfs, kpt, shape=()): self.timer.start('Update two-center projections') nproj = len(self) dI_aa = np.zeros((nproj, nproj), dtype=float) #always float? for a, dI_sp in dI_asp.items(): dI_p = dI_sp[kpt.s] dI_ii = unpack(dI_p) self.assign_atomic_pair_matrix(dI_aa, a, a, dI_ii) self.gd.comm.sum(dI_aa) #TODO too heavy? dM_aa = self.get_rotated_coefficients(dI_aa) Q_axi = wfs.pt.dict(shape, zero=True) for a1 in range(self.natoms): if a1 in Q_axi.keys(): Q_xi = Q_axi[a1] else: # Atom a1 is not in domain so allocate a temporary buffer Q_xi = np.zeros(shape + (self.setups[a1].ni, ), dtype=wfs.pt.dtype) #TODO for a2, P_xi in P_axi.items(): dM_ii = self.extract_atomic_pair_matrix(dM_aa, a1, a2) Q_xi += np.dot(P_xi, dM_ii.T) #sum over a2 and last i in dM_ii self.gd.comm.sum(Q_xi) self.timer.stop('Update two-center projections') return Q_axi
def get_lcao_xc(calc, P_aqMi, bfs=None, spin=0): nq = len(calc.wfs.ibzk_qc) nao = calc.wfs.setups.nao dtype = calc.wfs.dtype if bfs is None: bfs = get_bfs(calc) if calc.density.nt_sg is None: calc.density.interpolate() nt_sg = calc.density.nt_sg vxct_sg = calc.density.finegd.zeros(calc.wfs.nspins) calc.hamiltonian.xc.calculate(calc.density.finegd, nt_sg, vxct_sg) vxct_G = calc.wfs.gd.zeros() calc.hamiltonian.restrict(vxct_sg[spin], vxct_G) Vxc_qMM = np.zeros((nq, nao, nao), dtype) for q, Vxc_MM in enumerate(Vxc_qMM): bfs.calculate_potential_matrix(vxct_G, Vxc_MM, q) tri2full(Vxc_MM, "L") # Add atomic PAW corrections for a, P_qMi in P_aqMi.items(): D_sp = calc.density.D_asp[a][:] H_sp = np.zeros_like(D_sp) calc.wfs.setups[a].xc_correction.calculate(calc.hamiltonian.xc, D_sp, H_sp) H_ii = unpack(H_sp[spin]) for Vxc_MM, P_Mi in zip(Vxc_qMM, P_qMi): Vxc_MM += dots(P_Mi, H_ii, P_Mi.T.conj()) return Vxc_qMM * Hartree
def init_potential(self): #initialization of nonlocal part of pseudopotential # V_NL=|chi_i> V_i <chi_i| spline_aj = [] for setup in self.wfs.setups: spline_aj.append(setup.pt_j) self.lfc = PWLFC(spline_aj, self.pd) self.lfc.set_positions(self.calc.spos_ac) #set position of atoms proj_G = [] proj_r = [] #collect chi_i in real space using FFT for kpt in self.wfs.kpt_u: proj_G.append(self.lfc.expand(kpt.q)) proj = [] n_i = proj_G[-1].shape[1] for i in range(n_i): proj.append(self.pd.ifft(proj_G[-1][:, i].copy(), kpt.q)) proj_r.append(proj) self.chi = np.array(proj_r) / self.gd.dv s = 0 # s=0 because we perform spin-paired calculation V = [] #collect V for a in range(len(self.wfs.setups)): dH_ii = unpack(self.ham.dH_asp[a][s]) V.append(dH_ii.diagonal()) V = np.array(V) self.V = V.ravel() self.norb = self.V.size # number of orbitals in nonlocal potential #initialization of local part of pseudopotential V = self.ham.vbar.pd.zeros() self.ham.vbar.add(V) self.Vloc = self.ham.vbar.pd.ifft(V)
def get_lcao_xc(calc, P_aqMi, bfs=None, spin=0): nq = len(calc.wfs.kd.ibzk_qc) nao = calc.wfs.setups.nao dtype = calc.wfs.dtype if bfs is None: bfs = get_bfs(calc) if calc.density.nt_sg is None: calc.density.interpolate_pseudo_density() nt_sg = calc.density.nt_sg vxct_sg = calc.density.finegd.zeros(calc.wfs.nspins) calc.hamiltonian.xc.calculate(calc.density.finegd, nt_sg, vxct_sg) vxct_G = calc.wfs.gd.zeros() calc.hamiltonian.restrict_and_collect(vxct_sg[spin], vxct_G) Vxc_qMM = np.zeros((nq, nao, nao), dtype) for q, Vxc_MM in enumerate(Vxc_qMM): bfs.calculate_potential_matrix(vxct_G, Vxc_MM, q) tri2full(Vxc_MM, 'L') # Add atomic PAW corrections for a, P_qMi in P_aqMi.items(): D_sp = calc.density.D_asp[a][:] H_sp = np.zeros_like(D_sp) calc.hamiltonian.xc.calculate_paw_correction(calc.wfs.setups[a], D_sp, H_sp) H_ii = unpack(H_sp[spin]) for Vxc_MM, P_Mi in zip(Vxc_qMM, P_qMi): Vxc_MM += dots(P_Mi, H_ii, P_Mi.T.conj()) return Vxc_qMM * Hartree
def calculate_hamiltonian_matrix(self, hamiltonian, wfs, kpt, root=-1): # XXX document parallel stuff, particularly root parameter assert self.has_initialized vt_G = hamiltonian.vt_sG[kpt.s] H_MM = np.empty((wfs.ksl.mynao, wfs.ksl.nao), wfs.dtype) wfs.timer.start('Potential matrix') wfs.basis_functions.calculate_potential_matrix(vt_G, H_MM, kpt.q) wfs.timer.stop('Potential matrix') # Add atomic contribution # # -- a a a* # H += > P dH P # mu nu -- mu i ij nu j # aij # wfs.timer.start('Atomic Hamiltonian') Mstart = wfs.basis_functions.Mstart Mstop = wfs.basis_functions.Mstop for a, P_Mi in kpt.P_aMi.items(): dH_ii = np.asarray(unpack(hamiltonian.dH_asp[a][kpt.s]), wfs.dtype) dHP_iM = np.zeros((dH_ii.shape[1], P_Mi.shape[0]), wfs.dtype) # (ATLAS can't handle uninitialized output array) gemm(1.0, P_Mi, dH_ii, 0.0, dHP_iM, 'c') gemm(1.0, dHP_iM, P_Mi[Mstart:Mstop], 1.0, H_MM) wfs.timer.stop('Atomic Hamiltonian') wfs.timer.start('Distribute overlap matrix') H_MM = wfs.ksl.distribute_overlap_matrix(H_MM, root) wfs.timer.stop('Distribute overlap matrix') H_MM += wfs.T_qMM[kpt.q] return H_MM
def apply_to_atomic_matrices(self, dI_asp, P_axi, wfs, kpt, shape=()): self.timer.start('Update two-center projections') nproj = len(self) dI_aa = np.zeros((nproj, nproj), dtype=float) #always float? for a, dI_sp in dI_asp.items(): dI_p = dI_sp[kpt.s] dI_ii = unpack(dI_p) self.assign_atomic_pair_matrix(dI_aa, a, a, dI_ii) self.gd.comm.sum(dI_aa) #TODO too heavy? dM_aa = self.get_rotated_coefficients(dI_aa) Q_axi = wfs.pt.dict(shape, zero=True) for a1 in range(self.natoms): if a1 in Q_axi.keys(): Q_xi = Q_axi[a1] else: # Atom a1 is not in domain so allocate a temporary buffer Q_xi = np.zeros(shape+(self.setups[a1].ni,), dtype=wfs.pt.dtype) #TODO for a2, P_xi in P_axi.items(): dM_ii = self.extract_atomic_pair_matrix(dM_aa, a1, a2) Q_xi += np.dot(P_xi, dM_ii.T) #sum over a2 and last i in dM_ii self.gd.comm.sum(Q_xi) self.timer.stop('Update two-center projections') return Q_axi
def get_vxc(paw, spin=0, U=None): """Calculate matrix elements of the xc-potential.""" assert not paw.hamiltonian.xc.xcfunc.orbital_dependent, "LDA/GGA's only" assert paw.wfs.dtype == float, 'Complex waves not implemented' if U is not None: # Rotate xc matrix return np.dot(U.T.conj(), np.dot(get_vxc(paw, spin), U)) gd = paw.hamiltonian.gd psit_nG = paw.wfs.kpt_u[spin].psit_nG[:] if paw.density.nt_sg is None: paw.density.interpolate_pseudo_density() nt_g = paw.density.nt_sg[spin] vxct_g = paw.density.finegd.zeros() paw.hamiltonian.xc.get_energy_and_potential(nt_g, vxct_g) vxct_G = gd.empty() paw.hamiltonian.restrict(vxct_g, vxct_G) Vxc_nn = np.zeros((paw.wfs.bd.nbands, paw.wfs.bd.nbands)) # Apply pseudo part r2k(.5 * gd.dv, psit_nG, vxct_G * psit_nG, .0, Vxc_nn) # lower triangle tri2full(Vxc_nn, 'L') # Fill in upper triangle from lower gd.comm.sum(Vxc_nn) # Add atomic PAW corrections for a, P_ni in paw.wfs.kpt_u[spin].P_ani.items(): D_sp = paw.density.D_asp[a][:] H_sp = np.zeros_like(D_sp) paw.wfs.setups[a].xc_correction.calculate_energy_and_derivatives( D_sp, H_sp) H_ii = unpack(H_sp[spin]) Vxc_nn += np.dot(P_ni, np.dot(H_ii, P_ni.T)) return Vxc_nn * Hartree
def calculate_residuals(self, kpt, wfs, hamiltonian, psit_xG, P_axi, eps_x, R_xG, n_x=None, calculate_change=False): """Calculate residual. From R=Ht*psit calculate R=H*psit-eps*S*psit.""" for R_G, eps, psit_G in zip(R_xG, eps_x, psit_xG): axpy(-eps, psit_G, R_G) c_axi = {} for a, P_xi in P_axi.items(): dH_ii = unpack(hamiltonian.dH_asp[a][kpt.s]) dO_ii = hamiltonian.setups[a].dO_ii c_xi = (np.dot(P_xi, dH_ii) - np.dot(P_xi * eps_x[:, np.newaxis], dO_ii)) c_axi[a] = c_xi hamiltonian.xc.add_correction(kpt, psit_xG, R_xG, P_axi, c_axi, n_x, calculate_change) wfs.pt.add(R_xG, c_axi, kpt.q)
def apply(self, a_xG, b_xG, wfs, kpt, calculate_P_ani=True): """Apply the Hamiltonian operator to a set of vectors. Parameters: a_nG: ndarray Set of vectors to which the overlap operator is applied. b_nG: ndarray, output Resulting S times a_nG vectors. wfs: WaveFunctions Wave-function object defined in wavefunctions.py kpt: KPoint object k-point object defined in kpoint.py. calculate_P_ani: bool When True, the integrals of projector times vectors P_ni = <p_i | a_nG> are calculated. When False, existing P_ani are used """ wfs.kin.apply(a_xG, b_xG, kpt.phase_cd) self.apply_local_potential(a_xG, b_xG, kpt.s) shape = a_xG.shape[:-3] P_axi = wfs.pt.dict(shape) if calculate_P_ani: # TODO calculate_P_ani=False is experimental wfs.pt.integrate(a_xG, P_axi, kpt.q) else: for a, P_ni in kpt.P_ani.items(): P_axi[a][:] = P_ni for a, P_xi in P_axi.items(): dH_ii = unpack(self.dH_asp[a][kpt.s]) P_axi[a] = np.dot(P_xi, dH_ii) wfs.pt.add(b_xG, P_axi, kpt.q)
def vxc(paw, xc=None, coredensity=True): """Calculate XC-contribution to eigenvalues.""" ham = paw.hamiltonian dens = paw.density wfs = paw.wfs if xc is None: xc = ham.xc elif isinstance(xc, str): xc = XC(xc) if dens.nt_sg is None: dens.interpolate_pseudo_density() thisisatest = not True if xc.orbital_dependent: paw.get_xc_difference(xc) # Calculate XC-potential: vxct_sg = ham.finegd.zeros(wfs.nspins) xc.calculate(dens.finegd, dens.nt_sg, vxct_sg) vxct_sG = ham.gd.empty(wfs.nspins) ham.restrict(vxct_sg, vxct_sG) if thisisatest: vxct_sG[:] = 1 # ... and PAW corrections: dvxc_asii = {} for a, D_sp in dens.D_asp.items(): dvxc_sp = np.zeros_like(D_sp) xc.calculate_paw_correction(wfs.setups[a], D_sp, dvxc_sp, a=a, addcoredensity=coredensity) dvxc_asii[a] = [unpack(dvxc_p) for dvxc_p in dvxc_sp] if thisisatest: dvxc_asii[a] = [wfs.setups[a].dO_ii] vxc_un = np.empty((wfs.kd.mynks, wfs.bd.mynbands)) for u, vxc_n in enumerate(vxc_un): kpt = wfs.kpt_u[u] vxct_G = vxct_sG[kpt.s] for n in range(wfs.bd.mynbands): psit_G = wfs._get_wave_function_array(u, n, realspace=True) vxc_n[n] = wfs.gd.integrate((psit_G * psit_G.conj()).real, vxct_G, global_integral=False) for a, dvxc_sii in dvxc_asii.items(): P_ni = kpt.P_ani[a] vxc_n += (np.dot(P_ni, dvxc_sii[kpt.s]) * P_ni.conj()).sum(1).real wfs.gd.comm.sum(vxc_un) vxc_skn = wfs.kd.collect(vxc_un) if xc.orbital_dependent: vxc_skn += xc.exx_skn return vxc_skn * Hartree
def apply(self, x_nG, y_nG): """Apply the linear operator in the Sternheimer equation to a vector. For the eigenvalue term the k-point is the one of the state. For the other terms the k-point to be used is the one given by the k+q phase of the first-order of the state. Only for q=0 do the two coincide. Parameters ---------- x_nG: ndarray Vector(s) to which the Sternheimer operator is applied. y_nG: ndarray Resulting vector(s). """ assert x_nG.ndim in (3, 4) assert x_nG.shape == y_nG.shape assert tuple(self.gd.n_c) == x_nG.shape[-3:] assert self.k is not None # k kpt = self.kpt_u[self.k] # k+q kplusqpt = self.kpt_u[self.kplusq] # Kintetic energy # k+q self.kin.apply(x_nG, y_nG, phase_cd=kplusqpt.phase_cd) # Local part of effective potential - no phase !! self.hamiltonian.apply_local_potential(x_nG, y_nG, kpt.s) # Non-local part from projectors (coefficients can not be reused) shape = x_nG.shape[:-3] P_ani = self.pt.dict(shape=shape) # k+q self.pt.integrate(x_nG, P_ani, q=kplusqpt.k) for a, P_ni in P_ani.items(): dH_ii = unpack(self.hamiltonian.dH_asp[a][kpt.s]) P_ani[a] = np.dot(P_ni, dH_ii) # k+q self.pt.add(y_nG, P_ani, q=kplusqpt.k) # XXX Eigenvalue term if self.n is not None: # k y_nG -= kpt.eps_n[self.n] * x_nG else: for n, x_G in enumerate(x_nG): # k y_nG[n] -= kpt.eps_n[n] * x_G # Project out undesired (numerical) components # k+q self.project(y_nG)
def calculate(self, wfs, kpt, dH_asp, H_MM, y): dtype = wfs.dtype M_a = wfs.setups.M_a nM_a = np.array([setup.nao for setup in wfs.setups]) Mstart = wfs.ksl.Mstart Mstop = wfs.ksl.Mstop # Now calculate basis-projector-basis overlap: a1 -> a3 -> a2 # # specifically: # < phi[a1] | p[a3] > * dH[a3] * < p[a3] | phi[a2] > # # This matrix multiplication is semi-sparse. It works by blocks # of atoms, looping only over pairs that do have nonzero # overlaps. But it might be even nicer with scipy sparse. # This we will have to check at some point. # # The projection arrays P_aaim are distributed over the grid, # whereas the Hamiltonian is distributed over the band comm. # One could choose a set of a3 to optimize the load balance. # Right now the load balance will be "random" and probably # not very good. #innerloops = 0 for (a3, a1), P1_im in kpt.P_aaim.items(): a1M1 = M_a[a1] nM1 = nM_a[a1] a1M2 = a1M1 + nM1 if a1M1 > Mstop or a1M2 < Mstart: continue stickout1 = max(0, Mstart - a1M1) stickout2 = max(0, a1M2 - Mstop) P1_mi = np.conj(P1_im.T[stickout1:nM1 - stickout2]) dH_ii = y * np.asarray(unpack(dH_asp[a3][kpt.s]), dtype) H_mM = H_MM[a1M1 + stickout1 - Mstart:a1M2 - stickout2 - Mstart] P1dH_mi = np.dot(P1_mi, dH_ii) assert len(wfs.P_neighbors_a[a3]) > 0 for a2 in wfs.P_neighbors_a[a3]: # We can use symmetry somehow. Since the entire matrix # is symmetrized after the Hamiltonian is constructed, # at least in the non-Gamma-point case, we should do # so conditionally somehow. Right now let's stay out # of trouble. # Humm. The following works with gamma point # but not with kpts. XXX take a look at this. # also, it doesn't work with a2 < a1 for some reason. #if a2 > a1: # continue a2M1 = wfs.setups.M_a[a2] a2M2 = a2M1 + wfs.setups[a2].nao P2_im = kpt.P_aaim[(a3, a2)] P1dHP2_mm = np.dot(P1dH_mi, P2_im) H_mM[:, a2M1:a2M2] += P1dHP2_mm
def get_Fcore(self, q=0, indices=None): if indices is None: Fcore_ww = np.zeros_like(self.H_qww[q]) else: Fcore_ww = np.zeros((len(indices), len(indices))) for a, P_wi in self.get_projections(q, indices).items(): X_ii = unpack(self.calc.wfs.setups[a].X_p) Fcore_ww -= dots(P_wi, X_ii, P_wi.T.conj()) return Fcore_ww * Hartree
def calculate_hamiltonian(self, wfs, kpt, dH_asp, H_MM, yy): avalues = self.get_a_values() dH_aii = dH_asp.partition.arraydict( [setup.dO_ii.shape for setup in wfs.setups], dtype=wfs.dtype) for a in avalues: dH_aii[a][:] = yy * unpack(dH_asp[a][kpt.s]) self.calculate(wfs, kpt.q, dH_aii, H_MM)
def calculate(self, wfs, kpt, dH_asp, H_MM, y): Mstart = wfs.ksl.Mstart Mstop = wfs.ksl.Mstop dtype = wfs.dtype for a, P_Mi in kpt.P_aMi.items(): dH_ii = np.asarray(unpack(dH_asp[a][kpt.s]), dtype) dHP_iM = np.zeros((dH_ii.shape[1], P_Mi.shape[0]), dtype) # (ATLAS can't handle uninitialized output array) gemm(1.0, P_Mi, dH_ii, 0.0, dHP_iM, 'c') gemm(y, dHP_iM, P_Mi[Mstart:Mstop], 1.0, H_MM)
def initialize_paw_exx_corrections(self): for a, atomdata in enumerate(self.calc.wfs.setups): V_sii = [] for D_p in self.calc.density.D_asp[a]: D_ii = unpack2(D_p) V_ii = pawexxvv(atomdata, D_ii) V_sii.append(V_ii) C_ii = unpack(atomdata.X_p) self.V_asii.append(V_sii) self.C_aii.append(C_ii) self.exxcc += atomdata.ExxC
def apply_nonlocal_potential(self, psi_nG, y_nG, wfs, k, kplusq): """Derivate of the non-local PAW potential wrt an atomic displacement. Parameters ---------- k: int Index of the k-point being operated on. kplusq: int Index of the k+q vector. """ assert self.a is not None assert self.v is not None assert psi_nG.ndim in (3, 4) assert tuple(self.gd.n_c) == psi_nG.shape[-3:] if psi_nG.ndim == 3: n = 1 else: n = psi_nG.shape[0] a = self.a v = self.v P_ani = wfs.kpt_u[k].P_ani dP_aniv = wfs.kpt_u[k].dP_aniv pt = wfs.pt # < p_a^i | Psi_nk > P_ni = P_ani[a] # < dp_av^i | Psi_nk > - remember the sign convention of the derivative dP_ni = -1 * dP_aniv[a][...,v] # Expansion coefficients for the projectors on atom a dH_ii = unpack(self.dH_asp[a][0]) # The derivative of the non-local PAW potential has two contributions # 1) Sum over projectors c_ni = np.dot(dP_ni, dH_ii) c_ani = pt.dict(shape=n, zero=True) c_ani[a] = c_ni # k+q !! pt.add(y_nG, c_ani, q=kplusq) # 2) Sum over derivatives of the projectors dc_ni = np.dot(P_ni, dH_ii) dc_ani = pt.dict(shape=n, zero=True) # Take care of sign of derivative in the coefficients dc_ani[a] = -1 * dc_ni # k+q !! pt.add_derivative(a, v, y_nG, dc_ani, q=kplusq)
def calculate_forces(self, hamiltonian, F_av): # Calculate force-contribution from k-points: F_av.fill(0.0) F_aniv = self.pt.dict(self.bd.mynbands, derivative=True) for kpt in self.kpt_u: self.pt.derivative(kpt.psit_nG, F_aniv, kpt.q) for a, F_niv in F_aniv.items(): F_niv = F_niv.conj() F_niv *= kpt.f_n[:, np.newaxis, np.newaxis] dH_ii = unpack(hamiltonian.dH_asp[a][kpt.s]) P_ni = kpt.P_ani[a] F_vii = np.dot(np.dot(F_niv.transpose(), P_ni), dH_ii) F_niv *= kpt.eps_n[:, np.newaxis, np.newaxis] dO_ii = hamiltonian.setups[a].dO_ii F_vii -= np.dot(np.dot(F_niv.transpose(), P_ni), dO_ii) F_av[a] += 2 * F_vii.real.trace(0, 1, 2) # Hack used in delta-scf calculations: if hasattr(kpt, 'c_on'): assert self.bd.comm.size == 1 self.pt.derivative(kpt.psit_nG, F_aniv, kpt.q) #XXX again d_nn = np.zeros((self.bd.mynbands, self.bd.mynbands), dtype=complex) for ne, c_n in zip(kpt.ne_o, kpt.c_on): d_nn += ne * np.outer(c_n.conj(), c_n) for a, F_niv in F_aniv.items(): F_niv = F_niv.conj() dH_ii = unpack(hamiltonian.dH_asp[a][kpt.s]) Q_ni = np.dot(d_nn, kpt.P_ani[a]) F_vii = np.dot(np.dot(F_niv.transpose(), Q_ni), dH_ii) F_niv *= kpt.eps_n[:, np.newaxis, np.newaxis] dO_ii = hamiltonian.setups[a].dO_ii F_vii -= np.dot(np.dot(F_niv.transpose(), Q_ni), dO_ii) F_av[a] += 2 * F_vii.real.trace(0, 1, 2) self.bd.comm.sum(F_av, 0) if self.bd.comm.rank == 0: self.kpt_comm.sum(F_av, 0)
def calculate_hamiltonian_matrix(self, hamiltonian, wfs, kpt, Vt_xMM=None, root=-1): # XXX document parallel stuff, particularly root parameter assert self.has_initialized bf = wfs.basis_functions if Vt_xMM is None: wfs.timer.start('Potential matrix') vt_G = hamiltonian.vt_sG[kpt.s] Vt_xMM = bf.calculate_potential_matrices(vt_G) wfs.timer.stop('Potential matrix') if bf.gamma: y = 1.0 H_MM = Vt_xMM[0] if wfs.dtype == complex: H_MM = H_MM.astype(complex) else: wfs.timer.start('Sum over cells') y = 0.5 k_c = wfs.kd.ibzk_qc[kpt.q] H_MM = (0.5 + 0.0j) * Vt_xMM[0] for sdisp_c, Vt_MM in zip(bf.sdisp_xc, Vt_xMM)[1:]: H_MM += np.exp(2j * np.pi * np.dot(sdisp_c, k_c)) * Vt_MM wfs.timer.stop('Sum over cells') # Add atomic contribution # # -- a a a* # H += > P dH P # mu nu -- mu i ij nu j # aij # wfs.timer.start('Atomic Hamiltonian') Mstart = wfs.basis_functions.Mstart Mstop = wfs.basis_functions.Mstop for a, P_Mi in kpt.P_aMi.items(): dH_ii = np.asarray(unpack(hamiltonian.dH_asp[a][kpt.s]), wfs.dtype) dHP_iM = np.zeros((dH_ii.shape[1], P_Mi.shape[0]), wfs.dtype) # (ATLAS can't handle uninitialized output array) gemm(1.0, P_Mi, dH_ii, 0.0, dHP_iM, 'c') gemm(y, dHP_iM, P_Mi[Mstart:Mstop], 1.0, H_MM) wfs.timer.stop('Atomic Hamiltonian') wfs.timer.start('Distribute overlap matrix') H_MM = wfs.ksl.distribute_overlap_matrix( H_MM, root, add_hermitian_conjugate=(y == 0.5)) wfs.timer.stop('Distribute overlap matrix') H_MM += wfs.T_qMM[kpt.q] return H_MM
def get_vxc(self, density, wfs): """Calculate matrix elements of the xc-potential.""" dtype = wfs.dtype nbands = wfs.nbands nu = len(wfs.kpt_u) if density.nt_sg is None: density.interpolate() # Allocate space for result matrix Vxc_unn = np.empty((nu, nbands, nbands), dtype=dtype) # Get pseudo xc potential on the coarse grid Vxct_sG = self.gd.empty(self.nspins) Vxct_sg = self.finegd.zeros(self.nspins) if nspins == 1: self.xc.get_energy_and_potential(density.nt_sg[0], Vxct_sg[0]) else: self.xc.get_energy_and_potential(density.nt_sg[0], Vxct_sg[0], density.nt_sg[1], Vxct_sg[1]) for Vxct_G, Vxct_g in zip(Vxct_sG, Vxct_sg): self.restrict(Vxct_g, Vxct_G) del Vxct_sg # Get atomic corrections to the xc potential Vxc_asp = {} for a, D_sp in density.D_asp.items(): Vxc_asp[a] = np.zeros_like(D_sp) self.setups[a].xc_correction.calculate_energy_and_derivatives( D_sp, Vxc_asp[a]) # Project potential onto the eigenstates for kpt, Vxc_nn in xip(wfs.kpt_u, Vxc_unn): s, q = kpt.s, kpt.q psit_nG = kpt.psit_nG # Project pseudo part r2k(.5 * self.gd.dv, psit_nG, Vxct_sG[s] * psit_nG, 0.0, Vxc_nn) tri2full(Vxc_nn, 'L') self.gd.comm.sum(Vxc_nn) # Add atomic corrections # H_ij = \int dr phi_i(r) Ĥ phi_j^*(r) # P_ni = \int dr psi_n(r) pt_i^*(r) # Vxc_nm = \int dr phi_n(r) vxc(r) phi_m^*(r) # + sum_ij P_ni H_ij P_mj^* for a, P_ni in kpt.P_ani.items(): Vxc_ii = unpack(Vxc_asp[a][s]) Vxc_nn += np.dot(P_ni, np.inner(H_ii, P_ni).conj()) return Vxc_unn
def soft_pseudo(self, paw, H_nn, h_nn=None, u=0): if h_nn is None: h_nn = H_nn kpt = paw.wfs.kpt_u[u] pd = self.pair_density deg = 2 / self.nspins fmin = 1e-9 Htpsit_nG = np.zeros(kpt.psit_nG.shape, self.dtype) for n1 in range(self.nbands): psit1_G = kpt.psit_nG[n1] f1 = kpt.f_n[n1] / deg for n2 in range(n1, self.nbands): psit2_G = kpt.psit_nG[n2] f2 = kpt.f_n[n2] / deg if f1 < fmin and f2 < fmin: continue pd.initialize(kpt, n1, n2) pd.get_coarse(self.nt_G) pd.add_compensation_charges(self.nt_G, self.rhot_g) self.poisson_solve(self.vt_g, -self.rhot_g, charge=-float(n1 == n2), eps=1e-12, zero_initial_phi=True) self.restrict(self.vt_g, self.vt_G) Htpsit_nG[n1] += f2 * self.vt_G * psit2_G if n1 != n2: Htpsit_nG[n2] += f1 * self.vt_G * psit1_G v_aL = paw.density.ghat.dict() paw.density.ghat.integrate(self.vt_g, v_aL) for a, v_L in v_aL.items(): v_ii = unpack(np.dot(paw.wfs.setups[a].Delta_pL, v_L)) P_ni = kpt.P_ani[a] h_nn[:, n1] += f2 * np.dot(P_ni, np.dot(v_ii, P_ni[n2])) if n1 != n2: h_nn[:, n2] += f1 * np.dot(P_ni, np.dot(v_ii, P_ni[n1])) symmetrize(h_nn) # Grrrr why!!! XXX # Fill in lower triangle r2k(0.5 * self.dv, kpt.psit_nG[:], Htpsit_nG, 1.0, H_nn) # Fill in upper triangle from lower tri2full(H_nn, 'L')
def half_apply(self, kpt, psit, hpsit, calculate_P_ani=True): """Applies the half-difference of the time-dependent Hamiltonian to the wavefunction psit of the k-point kpt. Parameters ---------- kpt: Kpoint the current k-point (kpt_u[index_of_k-point]) psit: List of coarse grid the wavefuntions (on coarse grid) (kpt_u[index_of_k-point].psit_nG[indices_of_wavefunc]) hpsit: List of coarse grid the resulting "operated wavefunctions" (H psit) calculate_P_ani: bool When True, the integrals of projector times vectors P_ni = <p_i | psit> are calculated. When False, existing P_uni are used """ hpsit.fill(0.0) self.half_apply_local_potential(psit, hpsit, kpt.s) # Does exactly the same as last part of Hamiltonian.apply but # uses the difference between dH_asp at time t and t+dt. shape = psit.shape[:-3] P_axi = self.wfs.pt.dict(shape) if calculate_P_ani: self.wfs.pt.integrate(psit, P_axi, kpt.q) else: for a, P_ni in kpt.P_ani.items(): P_axi[a][:] = P_ni for a, P_xi in P_axi.items(): dH_ii = unpack(self.dH_asp[a][kpt.s]) P_axi[a][:] = np.dot(P_xi, dH_ii) self.wfs.pt.add(hpsit, P_axi, kpt.q) if self.td_potential is not None: # FIXME: add half difference here... but maybe it's not important # as this will be used only for getting initial guess. So, should # not affect to the results, only to the speed of convergence. #raise NotImplementedError pass
def hs(self, ham, q=-1, s=0, md=None): assert self.dtype == complex npw = len(self.pd.Q_qG[q]) N = self.pd.tmp_R.size if md is None: H_GG = np.zeros((npw, npw), complex) S_GG = np.zeros((npw, npw), complex) G1 = 0 G2 = npw else: H_GG = md.zeros(dtype=complex) S_GG = md.zeros(dtype=complex) G1, G2 = md.my_blocks(S_GG).next()[:2] H_GG.ravel()[G1::npw + 1] = (0.5 * self.pd.gd.dv / N * self.pd.G2_qG[q][G1:G2]) for G in range(G1, G2): x_G = self.pd.zeros(q=q) x_G[G] = 1.0 H_GG[G - G1] += (self.pd.gd.dv / N * self.pd.fft(ham.vt_sG[s] * self.pd.ifft(x_G, q), q)) S_GG.ravel()[G1::npw + 1] = self.pd.gd.dv / N f_IG = self.pt.expand(q) nI = len(f_IG) dH_II = np.zeros((nI, nI)) dS_II = np.zeros((nI, nI)) I1 = 0 for a in self.pt.my_atom_indices: dH_ii = unpack(ham.dH_asp[a][s]) dS_ii = self.setups[a].dO_ii I2 = I1 + len(dS_ii) dH_II[I1:I2, I1:I2] = dH_ii / N**2 dS_II[I1:I2, I1:I2] = dS_ii / N**2 I1 = I2 H_GG += np.dot(f_IG.T[G1:G2].conj(), np.dot(dH_II, f_IG)) S_GG += np.dot(f_IG.T[G1:G2].conj(), np.dot(dS_II, f_IG)) return H_GG, S_GG
def hs(self, ham, q=-1, s=0, md=None): npw = len(self.pd.Q_qG[q]) N = self.pd.tmp_R.size if md is None: H_GG = np.zeros((npw, npw), complex) S_GG = np.zeros((npw, npw), complex) G1 = 0 G2 = npw else: H_GG = md.zeros(dtype=complex) S_GG = md.zeros(dtype=complex) if S_GG.size == 0: return H_GG, S_GG G1, G2 = next(md.my_blocks(S_GG))[:2] H_GG.ravel()[G1::npw + 1] = (0.5 * self.pd.gd.dv / N * self.pd.G2_qG[q][G1:G2]) for G in range(G1, G2): x_G = self.pd.zeros(q=q) x_G[G] = 1.0 H_GG[G - G1] += (self.pd.gd.dv / N * self.pd.fft(ham.vt_sG[s] * self.pd.ifft(x_G, q), q)) S_GG.ravel()[G1::npw + 1] = self.pd.gd.dv / N f_IG = self.pt.expand(q) nI = len(f_IG) dH_II = np.zeros((nI, nI)) dS_II = np.zeros((nI, nI)) I1 = 0 for a in self.pt.my_atom_indices: dH_ii = unpack(ham.dH_asp[a][s]) dS_ii = self.setups[a].dO_ii I2 = I1 + len(dS_ii) dH_II[I1:I2, I1:I2] = dH_ii / N**2 dS_II[I1:I2, I1:I2] = dS_ii / N**2 I1 = I2 H_GG += np.dot(f_IG.T[G1:G2].conj(), np.dot(dH_II, f_IG)) S_GG += np.dot(f_IG.T[G1:G2].conj(), np.dot(dS_II, f_IG)) return H_GG, S_GG
def H_coulomb_val_core(paw, u=0): """Short description here. :: core * * // -- i(r) k(r') k(r) j (r') H = || drdr' > ---------------------- ij // -- |r - r'| k """ H_nn = np.zeros((paw.wfs.bd.nbands, paw.wfs.bd.nbands), dtype=paw.wfs.dtype) for a, P_ni in paw.wfs.kpt_u[u].P_ani.items(): X_ii = unpack(paw.wfs.setups[a].X_p) H_nn += np.dot(P_ni.conj(), np.dot(X_ii, P_ni.T)) paw.wfs.gd.comm.sum(H_nn) from ase.units import Hartree return H_nn * Hartree
def H_coulomb_val_core(paw, u=0): """Short description here. :: core * * // -- i(r) k(r') k(r) j (r') H = || drdr' > ---------------------- ij // -- |r - r'| k """ H_nn = np.zeros((paw.wfs.nbands, paw.wfs.nbands), dtype=paw.wfs.dtype) for a, P_ni in paw.wfs.kpt_u[u].P_ani.items(): X_ii = unpack(paw.wfs.setups[a].X_p) H_nn += np.dot(P_ni.conj(), np.dot(X_ii, P_ni.T)) paw.wfs.gd.comm.sum(H_nn) from ase.units import Hartree return H_nn * Hartree
def dscf_hamiltonian_elements(paw, kpt): # Copy/paste from gpaw/eigensolvers/eigensolver.py lines 155-170 rev. 4808 operator = paw.wfs.overlap.operator assert operator.nblocks == 1 Htpsit_xG = operator.suggest_temporary_buffer(kpt.psit_nG.dtype) def H(psit_xG): paw.wfs.kin.apply(psit_xG, Htpsit_xG, kpt.phase_cd) paw.hamiltonian.apply_local_potential(psit_xG, Htpsit_xG, kpt.s) paw.hamiltonian.xc.add_non_local_terms(psit_xG, Htpsit_xG, kpt) return Htpsit_xG dH_aii = dict([(a, unpack(dH_sp[kpt.s])) for a, dH_sp \ in paw.hamiltonian.dH_asp.items()]) H_oo, H_nn = dscf_matrix_elements(paw, kpt, H, dH_aii) eps_o = H_oo.real.diagonal() return eps_o, H_oo, H_nn
def add_forces(self, F_av): # Calculate changes in projections deg = 3-self.nspins F_amiv = self.pt.dict(self.nocc, derivative=True) self.pt.derivative(self.phit_mG, F_amiv) for m in range(self.nocc): # Force from compensation charges: dF_aLv = self.ghat.dict(derivative=True) self.ghat.derivative(self.vHt_mg[m], dF_aLv) for a, dF_Lv in dF_aLv.items(): F_av[a] -= deg*self.coulomb_factor * np.dot(self.Q_maL[m][a], dF_Lv) # Force from projectors for a, F_miv in F_amiv.items(): F_vi = F_miv[m].T.conj() dH_ii = unpack(self.dH_amp[a][m]) P_i = self.P_ami[a][m] F_v = np.dot(np.dot(F_vi, dH_ii), P_i) F_av[a] += deg* 2 * F_v.real
def add_forces(self, F_av): # Calculate changes in projections deg = 3 - self.nspins F_amiv = self.pt.dict(self.nocc, derivative=True) self.pt.derivative(self.phit_mG, F_amiv) for m in range(self.nocc): # Force from compensation charges: dF_aLv = self.ghat.dict(derivative=True) self.ghat.derivative(self.vHt_mg[m], dF_aLv) for a, dF_Lv in dF_aLv.items(): F_av[a] -= deg * self.coulomb_factor * np.dot( self.Q_maL[m][a], dF_Lv) # Force from projectors for a, F_miv in F_amiv.items(): F_vi = F_miv[m].T.conj() dH_ii = unpack(self.dH_amp[a][m]) P_i = self.P_ami[a][m] F_v = np.dot(np.dot(F_vi, dH_ii), P_i) F_av[a] += deg * 2 * F_v.real
def iterate(self, hamiltonian, wfs): if not self.initialized: self.initialize(wfs) r = self.gd.r_g h = r[0] N = len(r) lmax = len(self.f_sln[0]) - 1 setup = wfs.setups[0] e_n = np.zeros(N) for s in range(wfs.nspins): dH_ii = unpack(hamiltonian.dH_asp[0][s]) kpt = wfs.kpt_u[s] N1 = 0 for l in range(lmax + 1): H = self.T_l[l] + np.diag(hamiltonian.vt_sg[s]) i1 = 0 for pt1, l1 in zip(self.pt_j, setup.l_j): i2 = 0 for pt2, l2 in zip(self.pt_j, setup.l_j): if l1 == l2 == l: H += (h * dH_ii[i1, i2] * np.outer(pt1 * r, pt2 * r)) i2 += 2 * l2 + 1 i1 += 2 * l1 + 1 general_diagonalize(H, e_n, self.S_l[l].copy()) for n in range(len(self.f_sln[s][l])): N2 = N1 + 2 * l + 1 kpt.eps_n[N1:N2] = e_n[n] kpt.psit_nG[N1:N2] = H[n] / r / sqrt(h) i1 = 0 for pt, ll in zip(self.pt_j, setup.l_j): i2 = i1 + 2 * ll + 1 if ll == l: P = np.dot(kpt.psit_nG[N1], pt * r**2) * h kpt.P_ani[0][N1:N2, i1:i2] = P * np.eye(2 * l + 1) i1 = i2 N1 = N2
def get_component(self, wfs, s, vt_sG, dH_asp, kpt, H_MM): wfs.basis_functions.calculate_potential_matrix(vt_sG[s], H_MM, kpt.q) # Add atomic contribution # # -- a a a* # H += > P dH P # mu nu -- mu i ij nu j # aij # wfs.timer.start('Atomic Hamiltonian') Mstart = wfs.basis_functions.Mstart Mstop = wfs.basis_functions.Mstop wfs.timer.stop('Atomic Hamiltonian') tri2full(H_MM) for a, P_Mi in kpt.P_aMi.items(): dH_ii = np.asarray(unpack(dH_asp[a][s]), wfs.dtype) dHP_iM = np.zeros((dH_ii.shape[1], P_Mi.shape[0]), wfs.dtype) # (ATLAS can't handle uninitialized output array) gemm(1.0, P_Mi, dH_ii, 0.0, dHP_iM, 'c') gemm(1.0, dHP_iM, P_Mi[Mstart:Mstop], 1.0, H_MM)
def get_xc2(calc, w_wG, P_awi, spin=0): if calc.density.nt_sg is None: calc.density.interpolate() nt_g = calc.density.nt_sg[spin] vxct_g = calc.density.finegd.zeros() calc.hamiltonian.xc.get_energy_and_potential(nt_g, vxct_g) vxct_G = calc.wfs.gd.empty() calc.hamiltonian.restrict(vxct_g, vxct_G) # Integrate pseudo part Nw = len(w_wG) xc_ww = np.empty((Nw, Nw)) r2k(0.5 * calc.wfs.gd.dv, w_wG, vxct_G * w_wG, 0.0, xc_ww) tri2full(xc_ww, "L") # Add atomic PAW corrections for a, P_wi in P_awi.items(): D_sp = calc.density.D_asp[a][:] H_sp = np.zeros_like(D_sp) calc.wfs.setups[a].xc_correction.calculate_energy_and_derivatives(D_sp, H_sp) H_ii = unpack(H_sp[spin]) xc_ww += dots(P_wi, H_ii, P_wi.T.conj()) return xc_ww * Hartree
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 get_xc2(calc, w_wG, P_awi, spin=0): if calc.density.nt_sg is None: calc.density.interpolate_pseudo_density() nt_g = calc.density.nt_sg[spin] vxct_g = calc.density.finegd.zeros() calc.hamiltonian.xc.get_energy_and_potential(nt_g, vxct_g) vxct_G = calc.wfs.gd.empty() calc.hamiltonian.restrict_and_collect(vxct_g, vxct_G) # Integrate pseudo part Nw = len(w_wG) xc_ww = np.empty((Nw, Nw)) r2k(.5 * calc.wfs.gd.dv, w_wG, vxct_G * w_wG, .0, xc_ww) tri2full(xc_ww, 'L') # Add atomic PAW corrections for a, P_wi in P_awi.items(): D_sp = calc.density.D_asp[a][:] H_sp = np.zeros_like(D_sp) calc.wfs.setups[a].xc_correction.calculate_energy_and_derivatives( D_sp, H_sp) H_ii = unpack(H_sp[spin]) xc_ww += dots(P_wi, H_ii, P_wi.T.conj()) return xc_ww * Hartree
def get_lcao_projections_HSP(calc, bfs=None, spin=0, projectionsonly=True): """Some title. if projectionsonly is True, return the projections:: V_qnM = <psi_qn | Phi_qM> else, also return the Hamiltonian, overlap, and projector overlaps:: H_qMM = <Phi_qM| H |Phi_qM'> S_qMM = <Phi_qM|Phi_qM'> P_aqMi = <pt^a_qi|Phi_qM> """ if calc.wfs.kpt_comm.size != 1: raise NotImplementedError('Parallelization over spin/kpt not ' 'implemented yet.') spos_ac = calc.atoms.get_scaled_positions() % 1. comm = calc.wfs.gd.comm nq = len(calc.wfs.ibzk_qc) Nk = calc.wfs.nibzkpts nao = calc.wfs.setups.nao dtype = calc.wfs.dtype if bfs is None: bfs = get_bfs(calc) tci = TwoCenterIntegrals(calc.wfs.gd.cell_cv, calc.wfs.gd.pbc_c, calc.wfs.setups, calc.wfs.ibzk_qc, calc.wfs.gamma) # Calculate projector overlaps, and (lower triangle of-) S and T matrices S_qMM = np.zeros((nq, nao, nao), dtype) T_qMM = np.zeros((nq, nao, nao), dtype) P_aqMi = {} for a in bfs.my_atom_indices: ni = calc.wfs.setups[a].ni P_aqMi[a] = np.zeros((nq, nao, ni), dtype) tci.calculate(spos_ac, S_qMM, T_qMM, P_aqMi) add_paw_correction_to_overlap(calc.wfs.setups, P_aqMi, S_qMM) calc.wfs.gd.comm.sum(S_qMM) calc.wfs.gd.comm.sum(T_qMM) # Calculate projections V_qnM = np.zeros((nq, calc.wfs.bd.nbands, nao), dtype) for kpt in calc.wfs.kpt_u: if kpt.s != spin: continue V_nM = V_qnM[kpt.q] #bfs.integrate2(kpt.psit_nG[:], V_nM, kpt.q) # all bands to save time for n, V_M in enumerate(V_nM): # band-by-band to save memory bfs.integrate2(kpt.psit_nG[n][:], V_M, kpt.q) for a, P_ni in kpt.P_ani.items(): dS_ii = calc.wfs.setups[a].dO_ii P_Mi = P_aqMi[a][kpt.q] V_nM += np.dot(P_ni, np.inner(dS_ii, P_Mi).conj()) comm.sum(V_qnM) if projectionsonly: return V_qnM # Determine potential matrix vt_G = calc.hamiltonian.vt_sG[spin] H_qMM = np.zeros((nq, nao, nao), dtype) for q, H_MM in enumerate(H_qMM): bfs.calculate_potential_matrix(vt_G, H_MM, q) # Make Hamiltonian as sum of kinetic (T) and potential (V) matrices # and add atomic corrections for a, P_qMi in P_aqMi.items(): dH_ii = unpack(calc.hamiltonian.dH_asp[a][spin]) for P_Mi, H_MM in zip(P_qMi, H_qMM): H_MM += np.dot(P_Mi, np.inner(dH_ii, P_Mi).conj()) comm.sum(H_qMM) H_qMM += T_qMM # Fill in the upper triangles of H and S for H_MM, S_MM in zip(H_qMM, S_qMM): tri2full(H_MM) tri2full(S_MM) H_qMM *= Hartree return V_qnM, H_qMM, S_qMM, P_aqMi
def iterate_one_k_point(self, hamiltonian, wfs, kpt): """Do Davidson iterations for the kpoint""" niter = self.niter nbands = self.nbands gd = wfs.matrixoperator.gd psit_nG, Htpsit_nG = self.subspace_diagonalize(hamiltonian, wfs, kpt) # Note that psit_nG is now in self.operator.work1_nG and # Htpsit_nG is in kpt.psit_nG! H_2n2n = self.H_2n2n S_2n2n = self.S_2n2n eps_2n = self.eps_2n psit2_nG = reshape(self.Htpsit_nG, psit_nG.shape) self.timer.start("Davidson") R_nG = Htpsit_nG self.calculate_residuals(kpt, wfs, hamiltonian, psit_nG, kpt.P_ani, kpt.eps_n, R_nG) def integrate(a_G, b_G): return np.real(wfs.integrate(a_G, b_G, global_integral=False)) for nit in range(niter): H_2n2n[:] = 0.0 S_2n2n[:] = 0.0 norm_n = np.zeros(nbands) error = 0.0 for n in range(nbands): if kpt.f_n is None: weight = kpt.weight else: weight = kpt.f_n[n] if self.nbands_converge != "occupied": if n < self.nbands_converge: weight = kpt.weight else: weight = 0.0 error += weight * integrate(R_nG[n], R_nG[n]) ekin = self.preconditioner.calculate_kinetic_energy(psit_nG[n : n + 1], kpt) psit2_nG[n] = self.preconditioner(R_nG[n : n + 1], kpt, ekin) if self.normalize: norm_n[n] = integrate(psit2_nG[n], psit2_nG[n]) H_2n2n[n, n] = kpt.eps_n[n] S_2n2n[n, n] = 1.0 if self.normalize: gd.comm.sum(norm_n) for norm, psit2_G in zip(norm_n, psit2_nG): psit2_G *= norm ** -0.5 # Calculate projections P2_ani = wfs.pt.dict(nbands) wfs.pt.integrate(psit2_nG, P2_ani, kpt.q) # Hamiltonian matrix # <psi2 | H | psi> wfs.apply_pseudo_hamiltonian(kpt, hamiltonian, psit2_nG, Htpsit_nG) gd.integrate(psit_nG, Htpsit_nG, global_integral=False, _transposed_result=self.H_nn) # gemm(1.0, psit_nG, Htpsit_nG, 0.0, self.H_nn, 'c') for a, P_ni in kpt.P_ani.items(): P2_ni = P2_ani[a] dH_ii = unpack(hamiltonian.dH_asp[a][kpt.s]) self.H_nn += np.dot(P2_ni, np.dot(dH_ii, P_ni.T.conj())) gd.comm.sum(self.H_nn, 0) H_2n2n[nbands:, :nbands] = self.H_nn # <psi2 | H | psi2> gd.integrate(psit2_nG, Htpsit_nG, global_integral=False, _transposed_result=self.H_nn) # r2k(0.5 * gd.dv, psit2_nG, Htpsit_nG, 0.0, self.H_nn) for a, P2_ni in P2_ani.items(): dH_ii = unpack(hamiltonian.dH_asp[a][kpt.s]) self.H_nn += np.dot(P2_ni, np.dot(dH_ii, P2_ni.T.conj())) gd.comm.sum(self.H_nn, 0) H_2n2n[nbands:, nbands:] = self.H_nn # Overlap matrix # <psi2 | S | psi> gd.integrate(psit_nG, psit2_nG, global_integral=False, _transposed_result=self.S_nn) # gemm(1.0, psit_nG, psit2_nG, 0.0, self.S_nn, 'c') for a, P_ni in kpt.P_ani.items(): P2_ni = P2_ani[a] dO_ii = wfs.setups[a].dO_ii self.S_nn += np.dot(P2_ni, np.inner(dO_ii, P_ni.conj())) gd.comm.sum(self.S_nn, 0) S_2n2n[nbands:, :nbands] = self.S_nn # <psi2 | S | psi2> gd.integrate(psit2_nG, psit2_nG, global_integral=False, _transposed_result=self.S_nn) # rk(gd.dv, psit2_nG, 0.0, self.S_nn) for a, P2_ni in P2_ani.items(): dO_ii = wfs.setups[a].dO_ii self.S_nn += np.dot(P2_ni, np.dot(dO_ii, P2_ni.T.conj())) gd.comm.sum(self.S_nn, 0) S_2n2n[nbands:, nbands:] = self.S_nn if gd.comm.rank == 0: m = 0 if self.smin: s_N, U_NN = np.linalg.eigh(S_2n2n) m = int((s_N < self.smin).sum()) if m == 0: general_diagonalize(H_2n2n, eps_2n, S_2n2n) else: T_Nn = np.dot(U_NN[:, m:], np.diag(s_N[m:] ** -0.5)) H_2n2n[:nbands, nbands:] = H_2n2n[nbands:, :nbands].conj().T eps_2n[:-m], P_nn = np.linalg.eigh(np.dot(np.dot(T_Nn.T.conj(), H_2n2n), T_Nn)) H_2n2n[:-m] = np.dot(T_Nn, P_nn).T gd.comm.broadcast(H_2n2n, 0) gd.comm.broadcast(eps_2n, 0) kpt.eps_n[:] = eps_2n[:nbands] # Rotate psit_nG gd.gemm(1.0, psit_nG, H_2n2n[:nbands, :nbands], 0.0, Htpsit_nG) gd.gemm(1.0, psit2_nG, H_2n2n[:nbands, nbands:], 1.0, Htpsit_nG) psit_nG, Htpsit_nG = Htpsit_nG, psit_nG # Rotate P_uni: for a, P_ni in kpt.P_ani.items(): P2_ni = P2_ani[a] gemm(1.0, P_ni.copy(), H_2n2n[:nbands, :nbands], 0.0, P_ni) gemm(1.0, P2_ni, H_2n2n[:nbands, nbands:], 1.0, P_ni) if nit < niter - 1: wfs.apply_pseudo_hamiltonian(kpt, hamiltonian, psit_nG, Htpsit_nG) R_nG = Htpsit_nG self.calculate_residuals(kpt, wfs, hamiltonian, psit_nG, kpt.P_ani, kpt.eps_n, R_nG) self.timer.stop("Davidson") error = gd.comm.sum(error) return error, psit_nG
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 is_cam = self.is_cam vt_g = self.finegd.empty() if self.gd is not self.finegd: vt_G = self.gd.empty() if self.rsf == 'Yukawa': y_vt_g = self.finegd.empty() # if self.gd is not self.finegd: # y_vt_G = self.gd.empty() nocc = int(ceil(kpt.f_n.sum())) // (3 - self.nspins) if self.excitation is not None: ex_band = nocc - self.excited - 1 if self.excitation == 'singlet': ex_weight = -1 elif self.excitation == 'triplet': ex_weight = +1 else: ex_weight = 0 if self.unocc or self.excitation is not None: 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 # XXXX nbands can be different numbers on different cpus! # That means some will execute the loop and others not. # And deadlocks with augment-grids. # 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 if n1 != n2 and f1 == 0 and f1 == f2: continue # Don't work on double unocc. bands # 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 # XXXXX This will go wrong because we are solving the # Poisson equation on the distribution of gd, not finegd # Or maybe it's fixed now self.poissonsolver.solve(vt_g, -rhot_g, charge=-float(n1 == n2), eps=1e-12, zero_initial_phi=True) vt_g *= hybrid if self.rsf == 'Yukawa': y_vt_g[:] = 0.0 self.screened_poissonsolver.solve(y_vt_g, -rhot_g, charge=-float(n1 == n2), eps=1e-12, zero_initial_phi=True) if is_cam: # Cam like correction y_vt_g *= self.cam_beta else: y_vt_g *= hybrid vt_g -= y_vt_g 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 if self.excitation is not None and n1 == ex_band: Htpsit_nG[nocc:] += f1 * vt_G * psit_nG[nocc:] else: if self.excitation is None or n1 != ex_band \ or n2 < nocc: Htpsit_nG[n2] += f1 * vt_G * psit1_G else: Htpsit_nG[n2] += f1 * ex_weight * 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: if self.excitation is None or n1 != ex_band or \ n2 < nocc: v_ni[n2] += f1 * np.dot(v_ii, P_ni[n1]) else: v_ni[n2] += f1 * ex_weight * \ np.dot(v_ii, P_ni[n1]) else: # XXX Check this: v_nii[n1] = f1 * v_ii if self.excitation is not None and n1 == ex_band: for nuoc in range(nocc, nbands): v_ni[nuoc] += f1 * \ np.dot(v_ii, P_ni[nuoc]) 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) # 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 (dexx, dekin) = calculate_vv(ni, D_ii, setup.M_pp, hybrid) ekin += dekin exx += dexx if self.rsf is not None: Mg_pp = setup.calculate_yukawa_interaction(self.omega) if is_cam: (dexx, dekin) = calculate_vv(ni, D_ii, Mg_pp, self.cam_beta, addme=True) else: (dexx, dekin) = calculate_vv(ni, D_ii, Mg_pp, hybrid, addme=True) ekin -= dekin exx -= dexx # 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) if self.rsf == 'Yukawa' and setup.X_pg is not None: if is_cam: thybrid = self.cam_beta # 0th order else: thybrid = hybrid exx += thybrid * np.dot(D_p, setup.X_pg) if Htpsit_nG is not None: dH_p += thybrid * setup.X_pg ekin -= thybrid * np.dot(D_p, setup.X_pg) elif self.rsf == 'Yukawa' and setup.X_pg is None: thybrid = exp(-3.62e-2 * self.omega) # educated guess if is_cam: thybrid *= self.cam_beta else: thybrid *= hybrid exx += thybrid * np.dot(D_p, setup.X_p) if Htpsit_nG is not None: dH_p += thybrid * setup.X_p ekin -= thybrid * np.dot(D_p, setup.X_p) # Add core-core exchange energy if kpt.s == 0: if self.rsf is None or is_cam: if is_cam: exx += self.cam_alpha * setup.ExxC else: 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 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)