def __init__(self, calc, xc, ibzq_qc, fd, unit_cells, density_cut, ecut, tag, timer): self.calc = calc self.gd = calc.density.gd self.xc = xc self.ibzq_qc = ibzq_qc self.fd = fd self.unit_cells = unit_cells self.density_cut = density_cut self.ecut = ecut self.tag = tag self.timer = timer self.A_x = -(3 / 4.) * (3 / np.pi)**(1 / 3.) self.n_g = calc.get_all_electron_density(gridrefinement=1) self.n_g *= Bohr**3 if xc[-3:] == 'PBE': nf_g = calc.get_all_electron_density(gridrefinement=2) nf_g *= Bohr**3 gdf = self.gd.refine() grad_v = [Gradient(gdf, v, n=1).apply for v in range(3)] gradnf_vg = gdf.empty(3) for v in range(3): grad_v[v](nf_g, gradnf_vg[v]) self.gradn_vg = gradnf_vg[:, ::2, ::2, ::2] qd = KPointDescriptor(self.ibzq_qc) self.pd = PWDescriptor(ecut / Hartree, self.gd, complex, qd)
def __init__(self, name=None, calc=None, M=None, spinorbit=None): self.name = name self.calc = GPAW(calc, txt=None, communicator=mpi.serial_comm) self.M = np.array(M, dtype=float) self.spinorbit = spinorbit self.gd = self.calc.wfs.gd.new_descriptor() self.kd = self.calc.wfs.kd if self.calc.wfs.mode is 'pw': self.pd = self.calc.wfs.pd else: self.pd = PWDescriptor(ecut=None, gd=self.gd, kd=self.kd, dtype=complex) self.acell_cv = self.gd.cell_cv self.bcell_cv = 2 * np.pi * self.gd.icell_cv self.vol = self.gd.volume self.BZvol = (2 * np.pi)**3 / self.vol self.nb = self.calc.get_number_of_bands() self.v_Knm = None if spinorbit: if mpi.world.rank == 0: print('Calculating spinorbit Corrections') self.nb = 2 * self.calc.get_number_of_bands() self.e_mK, self.v_Knm = get_spinorbit_eigenvalues(self.calc, return_wfs=True) if mpi.world.rank == 0: print('Done with the spinorbit Corrections')
def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd if self.bd.comm.size > 1: raise ValueError('Band parallelization not supported by hybridk') self.wfs = wfs self.world = wfs.world self.fd = logfile(self.fd, self.world.rank) N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol**(2 / 3.0) / pi**2 if self.ecut is None: self.ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() * 0.9999 self.bzq_qc = self.kd.get_bz_q_points() qd = KPointDescriptor(self.bzq_qc) q0 = self.kd.where_is_q(np.zeros(3), self.bzq_qc) self.pwd = PWDescriptor(self.ecut, self.gd, complex, kd=qd) G2_qG = self.pwd.G2_qG G2_qG[q0][0] = 117.0 self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] G2_qG[q0][0] = 0.0 self.iG2_qG[q0][0] = 0.0 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) for q in range(self.kd.nbzkpts): self.gamma -= np.dot(np.exp(-self.alpha * G2_qG[q]), self.iG2_qG[q]) self.iG2_qG[q0][0] = self.gamma self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], qd, dtype=complex) self.log('Value of alpha parameter:', self.alpha) self.log('Value of gamma parameter:', self.gamma) self.log('Cutoff energy:', self.ecut, 'Hartree') self.log('%d x %d x %d k-points' % tuple(self.kd.N_c))
class Interpolator: def __init__(self, gd1, gd2, dtype=float): self.pd1 = PWDescriptor(0.0, gd1, dtype) self.pd2 = PWDescriptor(0.0, gd2, dtype) def interpolate(self, a_r): return self.pd1.interpolate(a_r, self.pd2)[0]
def __init__(self, calc, xc, ibzq_qc, fd, unit_cells, density_cut, ecut, tag): self.calc = calc self.gd = calc.density.gd self.xc = xc self.ibzq_qc = ibzq_qc self.fd = fd self.unit_cells = unit_cells self.density_cut = density_cut self.ecut = ecut self.tag = tag self.A_x = -(3 / 4.) * (3 / np.pi)**(1 / 3.) self.n_g = calc.get_all_electron_density(gridrefinement=1) self.n_g *= Bohr**3 if xc[-3:] == 'PBE': nf_g = calc.get_all_electron_density(gridrefinement=2) nf_g *= Bohr**3 gdf = self.gd.refine() grad_v = [Gradient(gdf, v, n=1).apply for v in range(3)] gradnf_vg = gdf.empty(3) for v in range(3): grad_v[v](nf_g, gradnf_vg[v]) self.gradn_vg = gradnf_vg[:, ::2, ::2, ::2] qd = KPointDescriptor(self.ibzq_qc) self.pd = PWDescriptor(ecut / Hartree, self.gd, complex, qd)
def get_pw_descriptor(q_c, calc, ecut, gammacentered=False): """Get the planewave descriptor of q_c.""" qd = KPointDescriptor([q_c]) pd = PWDescriptor(ecut, calc.wfs.gd, complex, qd, gammacentered=gammacentered) return pd
def get_PWDescriptor(self, q_c, gammacentered=False): """Get the planewave descriptor of q_c.""" qd = KPointDescriptor([q_c]) pd = PWDescriptor(self.ecut, self.calc.wfs.gd, complex, qd, gammacentered=gammacentered) return pd
def calculate(self, q_c, spin='all', A_x=None): wfs = self.calc.wfs if spin == 'all': spins = range(wfs.nspins) else: assert spin in range(wfs.nspins) spins = [spin] q_c = np.asarray(q_c, dtype=float) qd = KPointDescriptor([q_c]) pd = PWDescriptor(self.ecut, wfs.gd, complex, qd) self.print_chi(pd) if extra_parameters.get('df_dry_run'): print(' Dry run exit', file=self.fd) raise SystemExit nG = pd.ngmax nw = len(self.omega_w) mynG = (nG + self.blockcomm.size - 1) // self.blockcomm.size self.Ga = self.blockcomm.rank * mynG self.Gb = min(self.Ga + mynG, nG) assert mynG * (self.blockcomm.size - 1) < nG if A_x is not None: nx = nw * (self.Gb - self.Ga) * nG chi0_wGG = A_x[:nx].reshape((nw, self.Gb - self.Ga, nG)) chi0_wGG[:] = 0.0 else: chi0_wGG = np.zeros((nw, self.Gb - self.Ga, nG), complex) if np.allclose(q_c, 0.0): chi0_wxvG = np.zeros((len(self.omega_w), 2, 3, nG), complex) chi0_wvv = np.zeros((len(self.omega_w), 3, 3), complex) self.chi0_vv = np.zeros((3, 3), complex) else: chi0_wxvG = None chi0_wvv = None print('Initializing PAW Corrections', file=self.fd) self.Q_aGii = self.initialize_paw_corrections(pd) # Do all empty bands: m1 = self.nocc1 m2 = self.nbands self._calculate(pd, chi0_wGG, chi0_wxvG, chi0_wvv, self.Q_aGii, m1, m2, spins) return pd, chi0_wGG, chi0_wxvG, chi0_wvv
def calculate_gamma(self, vol, alpha): if self.molecule: return 0.0 N_c = self.kd.N_c offset_c = (N_c + 1) % 2 * 0.5 / N_c bzq_qc = monkhorst_pack(N_c) + offset_c qd = KPointDescriptor(bzq_qc) pd = PWDescriptor(self.wfs.pd.ecut, self.wfs.gd, kd=qd) gamma = (vol / (2 * pi)**2 * sqrt(pi / alpha) * self.kd.nbzkpts) for G2_G in pd.G2_qG: if G2_G[0] < 1e-7: G2_G = G2_G[1:] gamma -= np.dot(np.exp(-alpha * G2_G), G2_G**-1) return gamma / self.qstride_c.prod()
def check(self, i_cG, shift0_c, N_c, q_c, Q_aGii): I0_G = np.ravel_multi_index(i_cG - shift0_c[:, None], N_c, 'wrap') qd1 = KPointDescriptor([q_c]) pd1 = PWDescriptor(self.ecut, self.calc.wfs.gd, complex, qd1) G_I = np.empty(N_c.prod(), int) G_I[:] = -1 I1_G = pd1.Q_qG[0] G_I[I1_G] = np.arange(len(I0_G)) G_G = G_I[I0_G] assert len(I0_G) == len(I1_G) assert (G_G >= 0).all() for a, Q_Gii in enumerate(self.initialize_paw_corrections(pd1)): e = abs(Q_aGii[a] - Q_Gii[G_G]).max() assert e < 1e-12
def calculate_q(self, i, kpt1, kpt2): wfs = self.calc.wfs q_c = wfs.kd.bzk_kc[kpt2.K] - wfs.kd.bzk_kc[kpt1.K] qd = KPointDescriptor([q_c]) pd = PWDescriptor(self.ecut, wfs.gd, wfs.dtype, kd=qd) Q_G = self.get_fft_indices(kpt1.K, kpt2.K, q_c, pd, kpt1.shift_c - kpt2.shift_c) Q_aGii = self.initialize_paw_corrections(pd, soft=True) for n in range(kpt1.n2 - kpt1.n1): ut1cc_R = kpt1.ut_nR[n].conj() C1_aGi = [ np.dot(Q_Gii, P1_ni[n].conj()) for Q_Gii, P1_ni in zip(Q_aGii, kpt1.P_ani) ] n_mG = self.calculate_pair_densities(ut1cc_R, C1_aGi, kpt2, pd, Q_G) e = self.calculate_n(pd, n, n_mG, kpt2) self.exxvv_sin[kpt1.s, i, n] += e
def calculate(self, optical=True, ac=1.0): if self.spinors: """Calculate spinors. Here m is index of eigenvalues with SOC and n is the basis of eigenstates withour SOC. Below m is used for unoccupied states and n is used for occupied states so be careful!""" print('Diagonalizing spin-orbit Hamiltonian', file=self.fd) param = self.calc.parameters if not param['symmetry'] == 'off': print('Calculating KS wavefunctions without symmetry ' + 'for spin-orbit', file=self.fd) if not op.isfile('gs_nosym.gpw'): calc_so = GPAW(**param) calc_so.set(symmetry='off', fixdensity=True, txt='gs_nosym.txt') calc_so.atoms = self.calc.atoms calc_so.density = self.calc.density calc_so.get_potential_energy() calc_so.write('gs_nosym.gpw') calc_so = GPAW('gs_nosym.gpw', txt=None, communicator=serial_comm) e_mk, v_knm = get_spinorbit_eigenvalues(calc_so, return_wfs=True, scale=self.scale) del calc_so else: e_mk, v_knm = get_spinorbit_eigenvalues(self.calc, return_wfs=True, scale=self.scale) e_mk /= Hartree # Parallelization stuff nK = self.kd.nbzkpts myKrange, myKsize, mySsize = self.parallelisation_sizes() # Calculate exchange interaction qd0 = KPointDescriptor([self.q_c]) pd0 = PWDescriptor(self.ecut, self.calc.wfs.gd, complex, qd0) ikq_k = self.kd.find_k_plus_q(self.q_c) v_G = get_coulomb_kernel(pd0, self.kd.N_c, truncation=self.truncation, wstc=self.wstc) if optical: v_G[0] = 0.0 self.pair = PairDensity(self.calc, self.ecut, world=serial_comm, txt='pair.txt') # Calculate direct (screened) interaction and PAW corrections if self.mode == 'RPA': Q_aGii = self.pair.initialize_paw_corrections(pd0) else: self.get_screened_potential(ac=ac) if (self.qd.ibzk_kc - self.q_c < 1.0e-6).all(): iq0 = self.qd.bz2ibz_k[self.kd.where_is_q(self.q_c, self.qd.bzk_kc)] Q_aGii = self.Q_qaGii[iq0] else: Q_aGii = self.pair.initialize_paw_corrections(pd0) # Calculate pair densities, eigenvalues and occupations so = self.spinors + 1 Nv, Nc = so * self.nv, so * self.nc Ns = self.spins rhoex_KsmnG = np.zeros((nK, Ns, Nv, Nc, len(v_G)), complex) # rhoG0_Ksmn = np.zeros((nK, Ns, Nv, Nc), complex) df_Ksmn = np.zeros((nK, Ns, Nv, Nc), float) # -(ev - ec) deps_ksmn = np.zeros((myKsize, Ns, Nv, Nc), float) # -(fv - fc) if np.allclose(self.q_c, 0.0): optical_limit = True else: optical_limit = False get_pair = self.pair.get_kpoint_pair get_rho = self.pair.get_pair_density if self.spinors: # Get all pair densities to allow for SOC mixing # Use twice as many no-SOC states as BSE bands to allow mixing vi_s = [2 * self.val_sn[0, 0] - self.val_sn[0, -1] - 1] vf_s = [2 * self.con_sn[0, -1] - self.con_sn[0, 0] + 2] if vi_s[0] < 0: vi_s[0] = 0 ci_s, cf_s = vi_s, vf_s ni, nf = vi_s[0], vf_s[0] mvi = 2 * self.val_sn[0, 0] mvf = 2 * (self.val_sn[0, -1] + 1) mci = 2 * self.con_sn[0, 0] mcf = 2 * (self.con_sn[0, -1] + 1) else: vi_s, vf_s = self.val_sn[:, 0], self.val_sn[:, -1] + 1 ci_s, cf_s = self.con_sn[:, 0], self.con_sn[:, -1] + 1 for ik, iK in enumerate(myKrange): for s in range(Ns): pair = get_pair(pd0, s, iK, vi_s[s], vf_s[s], ci_s[s], cf_s[s]) m_m = np.arange(vi_s[s], vf_s[s]) n_n = np.arange(ci_s[s], cf_s[s]) if self.gw_skn is not None: iKq = self.calc.wfs.kd.find_k_plus_q(self.q_c, [iK])[0] epsv_m = self.gw_skn[s, iK, :self.nv] epsc_n = self.gw_skn[s, iKq, self.nv:] deps_ksmn[ik] = -(epsv_m[:, np.newaxis] - epsc_n) elif self.spinors: iKq = self.calc.wfs.kd.find_k_plus_q(self.q_c, [iK])[0] epsv_m = e_mk[mvi:mvf, iK] epsc_n = e_mk[mci:mcf, iKq] deps_ksmn[ik, s] = -(epsv_m[:, np.newaxis] - epsc_n) else: deps_ksmn[ik, s] = -pair.get_transition_energies(m_m, n_n) df_mn = pair.get_occupation_differences(self.val_sn[s], self.con_sn[s]) rho_mnG = get_rho(pd0, pair, m_m, n_n, optical_limit=optical_limit, direction=self.direction, Q_aGii=Q_aGii, extend_head=False) if self.spinors: if optical_limit: deps0_mn = -pair.get_transition_energies(m_m, n_n) rho_mnG[:, :, 0] *= deps0_mn df_Ksmn[iK, s, ::2, ::2] = df_mn df_Ksmn[iK, s, ::2, 1::2] = df_mn df_Ksmn[iK, s, 1::2, ::2] = df_mn df_Ksmn[iK, s, 1::2, 1::2] = df_mn vecv0_nm = v_knm[iK][::2][ni:nf, mvi:mvf] vecc0_nm = v_knm[iKq][::2][ni:nf, mci:mcf] rho_0mnG = np.dot(vecv0_nm.T.conj(), np.dot(vecc0_nm.T, rho_mnG)) vecv1_nm = v_knm[iK][1::2][ni:nf, mvi:mvf] vecc1_nm = v_knm[iKq][1::2][ni:nf, mci:mcf] rho_1mnG = np.dot(vecv1_nm.T.conj(), np.dot(vecc1_nm.T, rho_mnG)) rhoex_KsmnG[iK, s] = rho_0mnG + rho_1mnG if optical_limit: rhoex_KsmnG[iK, s, :, :, 0] /= deps_ksmn[ik, s] else: df_Ksmn[iK, s] = pair.get_occupation_differences(m_m, n_n) rhoex_KsmnG[iK, s] = rho_mnG if self.eshift is not None: deps_ksmn[np.where(df_Ksmn[myKrange] > 1.0e-3)] += self.eshift deps_ksmn[np.where(df_Ksmn[myKrange] < -1.0e-3)] -= self.eshift world.sum(df_Ksmn) world.sum(rhoex_KsmnG) self.rhoG0_S = np.reshape(rhoex_KsmnG[:, :, :, :, 0], -1) if hasattr(self, 'H_sS'): return # Calculate Hamiltonian t0 = time() print('Calculating %s matrix elements at q_c = %s' % (self.mode, self.q_c), file=self.fd) H_ksmnKsmn = np.zeros((myKsize, Ns, Nv, Nc, nK, Ns, Nv, Nc), complex) for ik1, iK1 in enumerate(myKrange): for s1 in range(Ns): kptv1 = self.pair.get_k_point(s1, iK1, vi_s[s1], vf_s[s1]) kptc1 = self.pair.get_k_point(s1, ikq_k[iK1], ci_s[s1], cf_s[s1]) rho1_mnG = rhoex_KsmnG[iK1, s1] #rhoG0_Ksmn[iK1, s1] = rho1_mnG[:, :, 0] rho1ccV_mnG = rho1_mnG.conj()[:, :] * v_G for s2 in range(Ns): for Q_c in self.qd.bzk_kc: iK2 = self.kd.find_k_plus_q(Q_c, [kptv1.K])[0] rho2_mnG = rhoex_KsmnG[iK2, s2] rho2_mGn = np.swapaxes(rho2_mnG, 1, 2) H_ksmnKsmn[ik1, s1, :, :, iK2, s2, :, :] += ( np.dot(rho1ccV_mnG, rho2_mGn)) if not self.mode == 'RPA' and s1 == s2: ikq = ikq_k[iK2] kptv2 = self.pair.get_k_point(s1, iK2, vi_s[s1], vf_s[s1]) kptc2 = self.pair.get_k_point(s1, ikq, ci_s[s1], cf_s[s1]) rho3_mmG, iq = self.get_density_matrix(kptv1, kptv2) rho4_nnG, iq = self.get_density_matrix(kptc1, kptc2) if self.spinors: vec0_nm = v_knm[iK1][::2][ni:nf, mvi:mvf] vec1_nm = v_knm[iK1][1::2][ni:nf, mvi:mvf] vec2_nm = v_knm[iK2][::2][ni:nf, mvi:mvf] vec3_nm = v_knm[iK2][1::2][ni:nf, mvi:mvf] rho_0mnG = np.dot(vec0_nm.T.conj(), np.dot(vec2_nm.T, rho3_mmG)) rho_1mnG = np.dot(vec1_nm.T.conj(), np.dot(vec3_nm.T, rho3_mmG)) rho3_mmG = rho_0mnG + rho_1mnG vec0_nm = v_knm[ikq_k[iK1]][::2][ni:nf, mci:mcf] vec1_nm = v_knm[ikq_k[iK1]][1::2][ni:nf,mci:mcf] vec2_nm = v_knm[ikq][::2][ni:nf, mci:mcf] vec3_nm = v_knm[ikq][1::2][ni:nf, mci:mcf] rho_0mnG = np.dot(vec0_nm.T.conj(), np.dot(vec2_nm.T, rho4_nnG)) rho_1mnG = np.dot(vec1_nm.T.conj(), np.dot(vec3_nm.T, rho4_nnG)) rho4_nnG = rho_0mnG + rho_1mnG rho3ccW_mmG = np.dot(rho3_mmG.conj(), self.W_qGG[iq]) W_mmnn = np.dot(rho3ccW_mmG, np.swapaxes(rho4_nnG, 1, 2)) W_mnmn = np.swapaxes(W_mmnn, 1, 2) * Ns * so H_ksmnKsmn[ik1, s1, :, :, iK2, s1] -= 0.5 * W_mnmn if iK1 % (myKsize // 5 + 1) == 0: dt = time() - t0 tleft = dt * myKsize / (iK1 + 1) - dt print(' Finished %s pair orbitals in %s - Estimated %s left' % ((iK1 + 1) * Nv * Nc * Ns * world.size, timedelta(seconds=round(dt)), timedelta(seconds=round(tleft))), file=self.fd) #if self.mode == 'BSE': # del self.Q_qaGii, self.W_qGG, self.pd_q H_ksmnKsmn /= self.vol mySsize = myKsize * Nv * Nc * Ns if myKsize > 0: iS0 = myKrange[0] * Nv * Nc * Ns #world.sum(rhoG0_Ksmn) #self.rhoG0_S = np.reshape(rhoG0_Ksmn, -1) self.df_S = np.reshape(df_Ksmn, -1) if not self.td: self.excludef_S = np.where(np.abs(self.df_S) < 0.001)[0] # multiply by 2 when spin-paired and no SOC self.df_S *= 2.0 / nK / Ns / so self.deps_s = np.reshape(deps_ksmn, -1) H_sS = np.reshape(H_ksmnKsmn, (mySsize, self.nS)) for iS in range(mySsize): # Multiply by occupations and adiabatic coupling H_sS[iS] *= self.df_S[iS0 + iS] * ac # add bare transition energies H_sS[iS, iS0 + iS] += self.deps_s[iS] self.H_sS = H_sS if self.write_h: self.par_save('H_SS.ulm', 'H_SS', self.H_sS)
def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: self.alpha = 6 * vol**(2 / 3.0) / pi**2 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() if self.kd.N_c is None: self.bzk_kc = np.zeros((1, 3)) dfghdfgh else: n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.pwd = PWDescriptor(ecut, self.gd, self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.pwd.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G**-1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:]**-1) n += 1 assert n == self.kd.N_c.prod() self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], dtype=complex) self.ghat.set_k_points(self.bzk_kc) self.fullkd = KPointDescriptor(self.kd.bzk_kc, nspins=1) class S: id_a = [] def set_symmetry(self, s): pass self.fullkd.set_symmetry(Atoms(pbc=True), S(), False) self.fullkd.set_communicator(world) self.pt = LFC(self.gd, [setup.pt_j for setup in density.setups], dtype=complex) self.pt.set_k_points(self.fullkd.ibzk_kc) self.interpolator = density.interpolator
def __call__(self, pd, calc, functional): assert functional in self.permitted_functionals self.functional = functional add_fxc = self.add_fxc # class methods not within the scope of call vol = pd.gd.volume npw = pd.ngmax if self.RSrep == 'grid': print("\tFinding all-electron density", file=self.fd) n_sG, gd = calc.density.get_all_electron_density(atoms=calc.atoms, gridrefinement=1) qd = pd.kd lpd = PWDescriptor(self.ecut, gd, complex, qd, gammacentered=pd.gammacentered) print("\tCalculating fxc on real space grid using" + " all-electron density", file=self.fd) fxc_G = np.zeros(np.shape(n_sG[0])) add_fxc(gd, n_sG, fxc_G) else: nt_sG = calc.density.nt_sG gd, lpd = pd.gd, pd print("\tCalculating fxc on real space grid using smooth density", file=self.fd) fxc_G = np.zeros(np.shape(nt_sG[0])) add_fxc(gd, nt_sG, fxc_G) print("\tFourier transforming into reciprocal space", file=self.fd) nG = gd.N_c nG0 = nG[0] * nG[1] * nG[2] tmp_g = np.fft.fftn(fxc_G) * vol / nG0 Kxc_GG = np.zeros((npw, npw), dtype=complex) for iG, iQ in enumerate(lpd.Q_qG[0]): iQ_c = (np.unravel_index(iQ, nG) + nG // 2) % nG - nG // 2 for jG, jQ in enumerate(lpd.Q_qG[0]): jQ_c = (np.unravel_index(jQ, nG) + nG // 2) % nG - nG // 2 ijQ_c = (iQ_c - jQ_c) if (abs(ijQ_c) < nG // 2).all(): Kxc_GG[iG, jG] = tmp_g[tuple(ijQ_c)] if self.RSrep == 'gpaw': print("\tCalculating PAW corrections to the kernel", file=self.fd) G_Gv = pd.get_reciprocal_vectors() R_av = calc.atoms.positions / Bohr setups = calc.wfs.setups D_asp = calc.density.D_asp KxcPAW_GG = np.zeros_like(Kxc_GG) dG_GGv = np.zeros((npw, npw, 3)) for v in range(3): dG_GGv[:, :, v] = np.subtract.outer(G_Gv[:, v], G_Gv[:, v]) # Distribute computation of PAW correction equally among processes p_r = self.distribute_correction(setups, self.world.size) apdone = 0 npdone = 0 pdone = 0 pdonebefore = np.sum(p_r[:self.world.rank]) pdonenow = pdonebefore + p_r[self.world.rank] for a, setup in enumerate(setups): # PAW correction is evaluated on a radial grid Y_nL = setup.xc_correction.Y_nL rgd = setup.xc_correction.rgd # Continue if computation has been done already nn = len(Y_nL) ng = len(rgd.r_g) apdone += nn * ng if pdonebefore >= apdone or pdone >= pdonenow: npdone += nn * ng pdone += nn * ng continue n_qg = setup.xc_correction.n_qg nt_qg = setup.xc_correction.nt_qg nc_g = setup.xc_correction.nc_g nct_g = setup.xc_correction.nct_g dv_g = rgd.dv_g D_sp = D_asp[a] B_pqL = setup.xc_correction.B_pqL D_sLq = np.inner(D_sp, B_pqL.T) nspins = len(D_sp) f_g = rgd.zeros() ft_g = rgd.zeros() n_sLg = np.dot(D_sLq, n_qg) nt_sLg = np.dot(D_sLq, nt_qg) # Add core density n_sLg[:, 0] += np.sqrt(4. * np.pi) / nspins * nc_g nt_sLg[:, 0] += np.sqrt(4. * np.pi) / nspins * nct_g coefatoms_GG = np.exp(-1j * np.inner(dG_GGv, R_av[a])) for n, Y_L in enumerate(Y_nL): # Continue if computation has been done already npdone += ng if pdonebefore >= npdone or pdone >= pdonenow: pdone += ng continue w = weight_n[n] f_g[:] = 0. n_sg = np.dot(Y_L, n_sLg) add_fxc(rgd, n_sg, f_g) ft_g[:] = 0. nt_sg = np.dot(Y_L, nt_sLg) add_fxc(rgd, nt_sg, ft_g) dG_GG = np.inner(dG_GGv, R_nv[n]) for i in range(len(rgd.r_g)): # Continue if previous ranks already did computation pdone += 1 if pdonebefore >= pdone: continue # Do computation if needed if pdone <= pdonenow: coef_GG = np.exp(-1j * dG_GG * rgd.r_g[i]) KxcPAW_GG += w * coefatoms_GG\ * np.dot(coef_GG, (f_g[i] - ft_g[i]) * dv_g[i]) self.world.sum(KxcPAW_GG) Kxc_GG += KxcPAW_GG return Kxc_GG / vol
class HybridXC(XCFunctional): orbital_dependent = True def __init__(self, name, hybrid=None, xc=None, finegrid=False, alpha=None): """Mix standard functionals with exact exchange. name: str Name of hybrid functional. hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. finegrid: boolean Use fine grid for energy functional evaluations? """ if name == 'EXX': assert hybrid is None and xc is None hybrid = 1.0 xc = XC(XCNull()) elif name == 'PBE0': assert hybrid is None and xc is None hybrid = 0.25 xc = XC('HYB_GGA_XC_PBEH') elif name == 'B3LYP': assert hybrid is None and xc is None hybrid = 0.2 xc = XC('HYB_GGA_XC_B3LYP') if isinstance(xc, str): xc = XC(xc) self.hybrid = hybrid self.xc = xc self.type = xc.type self.alpha = alpha self.exx = 0.0 XCFunctional.__init__(self, name) def get_setup_name(self): return 'PBE' def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: self.alpha = 6 * vol**(2 / 3.0) / pi**2 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() if self.kd.N_c is None: self.bzk_kc = np.zeros((1, 3)) dfghdfgh else: n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.pwd = PWDescriptor(ecut, self.gd, self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.pwd.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G**-1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:]**-1) n += 1 assert n == self.kd.N_c.prod() self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], dtype=complex ) self.ghat.set_k_points(self.bzk_kc) self.fullkd = KPointDescriptor(self.kd.bzk_kc, nspins=1) class S: id_a = [] def set_symmetry(self, s): pass self.fullkd.set_symmetry(Atoms(pbc=True), S(), False) self.fullkd.set_communicator(world) self.pt = LFC(self.gd, [setup.pt_j for setup in density.setups], dtype=complex) self.pt.set_k_points(self.fullkd.ibzk_kc) self.interpolator = density.interpolator def set_positions(self, spos_ac): self.ghat.set_positions(spos_ac) self.pt.set_positions(spos_ac) def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx def calculate_exx(self): """Non-selfconsistent calculation.""" kd = self.kd K = self.fullkd.nibzkpts assert self.nspins == 1 Q = K // world.size assert Q * world.size == K parallel = (world.size > self.nspins) self.exx = 0.0 self.exx_skn = np.zeros((self.nspins, K, self.bd.nbands)) kpt_u = [] for k in range(world.rank * Q, (world.rank + 1) * Q): k_c = self.fullkd.ibzk_kc[k] for k1, k1_c in enumerate(kd.bzk_kc): if abs(k1_c - k_c).max() < 1e-10: break # Index of symmetry related point in the irreducible BZ ik = kd.kibz_k[k1] kpt = self.kpt_u[ik] # KPoint from ground-state calculation phase_cd = np.exp(2j * pi * self.gd.sdisp_cd * k_c[:, np.newaxis]) kpt2 = KPoint0(kpt.weight, kpt.s, k, None, phase_cd) kpt2.psit_nG = np.empty_like(kpt.psit_nG) kpt2.f_n = kpt.f_n / kpt.weight / K * 2 for n, psit_G in enumerate(kpt2.psit_nG): psit_G[:] = kd.transform_wave_function(kpt.psit_nG[n], k1) kpt2.P_ani = self.pt.dict(len(kpt.psit_nG)) self.pt.integrate(kpt2.psit_nG, kpt2.P_ani, k) kpt_u.append(kpt2) for s in range(self.nspins): kpt1_q = [KPoint(self.fullkd, kpt) for kpt in 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 = self.fullkd.get_rank_and_index(s, (kpt1_q[0].k - 1) % K)[0] # Receive rank: rrank = self.fullkd.get_rank_and_index(s, (kpt1_q[-1].k + 1) % K)[0] # Shift k-points K // 2 times: for i in range(K // 2 + 1): if i < K // 2: 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): if 2 * i == K: self.apply(kpt1, kpt2, invert=(kpt1.k > kpt2.k)) else: self.apply(kpt1, kpt2) self.apply(kpt1, kpt2, invert=True) if i < K // 2: if parallel: kpt.wait() kpt2_q[0].wait() kpt2_q.pop(0) kpt2_q.append(kpt) self.exx = world.sum(self.exx) world.sum(self.exx_skn) self.exx += self.calculate_paw_correction() def apply(self, kpt1, kpt2, invert=False): #print world.rank,kpt1.k,kpt2.k,invert k1_c = self.fullkd.ibzk_kc[kpt1.k] k2_c = self.fullkd.ibzk_kc[kpt2.k] if invert: k2_c = -k2_c k12_c = k1_c - k2_c N_c = self.gd.N_c eikr_R = np.exp(2j * pi * np.dot(np.indices(N_c).T, k12_c / N_c).T) for q, k_c in enumerate(self.bzk_kc): if abs(k_c + k12_c).max() < 1e-9: q0 = q break for q, k_c in enumerate(self.bzk_kc): if abs(k_c - k12_c).max() < 1e-9: q00 = q break Gpk2_G = self.pwd.G2_qG[q0] if Gpk2_G[0] == 0: Gpk2_G = Gpk2_G.copy() Gpk2_G[0] = 1.0 / self.gamma N = N_c.prod() vol = self.gd.dv * N nspins = self.nspins same = (kpt1.k == kpt2.k) for n1, psit1_R in enumerate(kpt1.psit_nG): f1 = kpt1.f_n[n1] for n2, psit2_R in enumerate(kpt2.psit_nG): if same and n2 > n1: continue f2 = kpt2.f_n[n2] nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, q0, invert) nt_G = self.pwd.fft(nt_R * eikr_R) / N vt_G = nt_G.copy() vt_G *= -pi * vol / Gpk2_G e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid if same and n1 == n2: e /= 2 self.exx += e * f1 * f2 self.ekin -= 2 * e * f1 * f2 self.exx_skn[kpt1.s, kpt1.k, n1] += f2 * e self.exx_skn[kpt2.s, kpt2.k, n2] += f1 * e calculate_potential = not True if calculate_potential: vt_R = self.pwd.ifft(vt_G).conj() * eikr_R * N / vol if kpt1 is kpt2 and not invert and n1 == n2: kpt1.vt_nG[n1] = 0.5 * f1 * vt_R if invert: kpt1.Htpsit_nG[n1] += \ f2 * nspins * psit2_R.conj() * vt_R else: kpt1.Htpsit_nG[n1] += f2 * nspins * psit2_R * vt_R if kpt1 is not kpt2: if invert: kpt2.Htpsit_nG[n2] += (f1 * nspins * psit1_R.conj() * vt_R) else: kpt2.Htpsit_nG[n2] += (f1 * nspins * psit1_R * vt_R.conj()) def calculate_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 calculate_pair_density(self, n1, n2, kpt1, kpt2, q, invert): if invert: nt_G = kpt1.psit_nG[n1].conj() * kpt2.psit_nG[n2].conj() else: nt_G = kpt1.psit_nG[n1].conj() * kpt2.psit_nG[n2] Q_aL = {} for a, P1_ni in kpt1.P_ani.items(): P1_i = P1_ni[n1] P2_i = kpt2.P_ani[a][n2] if invert: D_ii = np.outer(P1_i.conj(), P2_i.conj()) else: D_ii = np.outer(P1_i.conj(), P2_i) D_p = pack(D_ii) Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) self.ghat.add(nt_G, Q_aL, q) return nt_G
class HybridXC(HybridXCBase): orbital_dependent = True def __init__(self, name, hybrid=None, xc=None, alpha=None, gamma_point=1, method='standard', bandstructure=False, logfilename='-', bands=None, fcut=1e-10, molecule=False, qstride=1, world=None): """Mix standard functionals with exact exchange. name: str Name of functional: EXX, PBE0, HSE03, HSE06 hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. method: str Use 'standard' standard formula and 'acdf for adiabatic-connection dissipation fluctuation formula. alpha: float XXX describe gamma_point: bool 0: Skip k2-k1=0 interactions. 1: Use the alpha method. 2: Integrate the gamma point. bandstructure: bool Calculate bandstructure instead of just the total energy. bands: list of int List of bands to calculate bandstructure for. Default is all bands. molecule: bool Decouple electrostatic interactions between periodically repeated images. fcut: float Threshold for empty band. """ self.alpha = alpha self.fcut = fcut self.gamma_point = gamma_point self.method = method self.bandstructure = bandstructure self.bands = bands self.fd = logfilename self.write_timing_information = True HybridXCBase.__init__(self, name, hybrid, xc) # EXX energies: self.exx = None # total self.evv = None # valence-valence (pseudo part) self.evvacdf = None # valence-valence (pseudo part) self.devv = None # valence-valence (PAW correction) self.evc = None # valence-core self.ecc = None # core-core self.exx_skn = None # bandstructure self.qlatest = None if world is None: world = mpi.world self.world = world self.molecule = molecule if isinstance(qstride, int): qstride = [qstride] * 3 self.qstride_c = np.asarray(qstride) self.timer = Timer() def log(self, *args, **kwargs): prnt(file=self.fd, *args, **kwargs) self.fd.flush() def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp, addcoredensity, a) def initialize(self, dens, ham, wfs, occupations): assert wfs.bd.comm.size == 1 self.xc.initialize(dens, ham, wfs, occupations) self.dens = dens self.wfs = wfs # Make a k-point descriptor that is not distributed # (self.kd.comm is serial_comm): self.kd = wfs.kd.copy() self.fd = logfile(self.fd, self.world.rank) wfs.initialize_wave_functions_from_restart_file() def set_positions(self, spos_ac): self.spos_ac = spos_ac def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx * self.hybrid def calculate_exx(self): """Non-selfconsistent calculation.""" self.timer.start('EXX') self.timer.start('Initialization') kd = self.kd wfs = self.wfs if fftw.FFTPlan is fftw.NumpyFFTPlan: self.log('NOT USING FFTW !!') self.log('Spins:', self.wfs.nspins) W = max(1, self.wfs.kd.comm.size // self.wfs.nspins) # Are the k-points distributed? kparallel = (W > 1) # Find number of occupied bands: self.nocc_sk = np.zeros((self.wfs.nspins, kd.nibzkpts), int) for kpt in self.wfs.kpt_u: for n, f in enumerate(kpt.f_n): if abs(f) < self.fcut: self.nocc_sk[kpt.s, kpt.k] = n break else: self.nocc_sk[kpt.s, kpt.k] = self.wfs.bd.nbands self.wfs.kd.comm.sum(self.nocc_sk) noccmin = self.nocc_sk.min() noccmax = self.nocc_sk.max() self.log('Number of occupied bands (min, max): %d, %d' % (noccmin, noccmax)) self.log('Number of valence electrons:', self.wfs.setups.nvalence) if self.bandstructure: self.log('Calculating eigenvalue shifts.') # allocate array for eigenvalue shifts: self.exx_skn = np.zeros( (self.wfs.nspins, kd.nibzkpts, self.wfs.bd.nbands)) if self.bands is None: noccmax = self.wfs.bd.nbands else: noccmax = max(max(self.bands) + 1, noccmax) N_c = self.kd.N_c vol = wfs.gd.dv * wfs.gd.N_c.prod() if self.alpha is None: alpha = 6 * vol**(2 / 3.0) / pi**2 else: alpha = self.alpha if self.gamma_point == 1: if alpha == 0.0: qvol = (2 * np.pi)**3 / vol / N_c.prod() self.gamma = 4 * np.pi * (3 * qvol / (4 * np.pi))**(1 / 3.) / qvol else: self.gamma = self.calculate_gamma(vol, alpha) else: kcell_cv = wfs.gd.cell_cv.copy() kcell_cv[0] *= N_c[0] kcell_cv[1] *= N_c[1] kcell_cv[2] *= N_c[2] self.gamma = madelung(kcell_cv) * vol * N_c.prod() / (4 * np.pi) self.log('Value of alpha parameter: %.3f Bohr^2' % alpha) self.log('Value of gamma parameter: %.3f Bohr^2' % self.gamma) # Construct all possible q=k2-k1 vectors: Nq_c = (N_c - 1) // self.qstride_c i_qc = np.indices(Nq_c * 2 + 1, float).transpose((1, 2, 3, 0)).reshape( (-1, 3)) self.bzq_qc = (i_qc - Nq_c) / N_c * self.qstride_c self.q0 = ((Nq_c * 2 + 1).prod() - 1) // 2 # index of q=(0,0,0) assert not self.bzq_qc[self.q0].any() # Count number of pairs for each q-vector: self.npairs_q = np.zeros(len(self.bzq_qc), int) for s in range(kd.nspins): for k1 in range(kd.nibzkpts): for k2 in range(kd.nibzkpts): for K2, q, n1_n, n2 in self.indices(s, k1, k2): self.npairs_q[q] += len(n1_n) self.npairs0 = self.npairs_q.sum() # total number of pairs self.log('Number of pairs:', self.npairs0) # Distribute q-vectors to Q processors: Q = self.world.size // self.wfs.kd.comm.size myrank = self.world.rank // self.wfs.kd.comm.size rank = 0 N = 0 myq = [] nq = 0 for q, n in enumerate(self.npairs_q): if n > 0: nq += 1 if rank == myrank: myq.append(q) N += n if N >= (rank + 1.0) * self.npairs0 / Q: rank += 1 assert len(myq) > 0, 'Too few q-vectors for too many processes!' self.bzq_qc = self.bzq_qc[myq] try: self.q0 = myq.index(self.q0) except ValueError: self.q0 = None self.log('%d x %d x %d k-points' % tuple(self.kd.N_c)) self.log('Distributing %d IBZ k-points over %d process(es).' % (kd.nibzkpts, self.wfs.kd.comm.size)) self.log('Distributing %d q-vectors over %d process(es).' % (nq, Q)) # q-point descriptor for my q-vectors: qd = KPointDescriptor(self.bzq_qc) # Plane-wave descriptor for all wave-functions: self.pd = PWDescriptor(wfs.pd.ecut, wfs.gd, dtype=wfs.pd.dtype, kd=kd) # Plane-wave descriptor pair-densities: self.pd2 = PWDescriptor(self.dens.pd2.ecut, self.dens.gd, dtype=wfs.dtype, kd=qd) self.log('Cutoff energies:') self.log(' Wave functions: %10.3f eV' % (self.pd.ecut * Hartree)) self.log(' Density: %10.3f eV' % (self.pd2.ecut * Hartree)) # Calculate 1/|G+q|^2 with special treatment of |G+q|=0: G2_qG = self.pd2.G2_qG if self.q0 is None: if self.omega is None: self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] else: self.iG2_qG = [ (1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2)))) for G2_G in G2_qG ] else: G2_qG[self.q0][0] = 117.0 # avoid division by zero if self.omega is None: self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] self.iG2_qG[self.q0][0] = self.gamma else: self.iG2_qG = [ (1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2)))) for G2_G in G2_qG ] self.iG2_qG[self.q0][0] = 1 / (4 * self.omega**2) G2_qG[self.q0][0] = 0.0 # restore correct value # Compensation charges: self.ghat = PWLFC([setup.ghat_l for setup in wfs.setups], self.pd2) self.ghat.set_positions(self.spos_ac) if self.molecule: self.initialize_gaussian() self.log('Value of beta parameter: %.3f 1/Bohr^2' % self.beta) self.timer.stop('Initialization') # Ready ... set ... go: self.t0 = time() self.npairs = 0 self.evv = 0.0 self.evvacdf = 0.0 for s in range(self.wfs.nspins): kpt1_q = [ KPoint(self.wfs, noccmax).initialize(kpt) for kpt in self.wfs.kpt_u if kpt.s == s ] kpt2_q = kpt1_q[:] if len(kpt1_q) == 0: # No s-spins on this CPU: continue # Send and receive ranks: srank = self.wfs.kd.get_rank_and_index(s, (kpt1_q[0].k - 1) % kd.nibzkpts)[0] rrank = self.wfs.kd.get_rank_and_index(s, (kpt1_q[-1].k + 1) % kd.nibzkpts)[0] # Shift k-points kd.nibzkpts - 1 times: for i in range(kd.nibzkpts): if i < kd.nibzkpts - 1: if kparallel: kpt = kpt2_q[-1].next(self.wfs) kpt.start_receiving(rrank) kpt2_q[0].start_sending(srank) else: kpt = kpt2_q[0] self.timer.start('Calculate') for kpt1, kpt2 in zip(kpt1_q, kpt2_q): # Loop over all k-points that k2 can be mapped to: for K2, q, n1_n, n2 in self.indices(s, kpt1.k, kpt2.k): self.apply(K2, q, kpt1, kpt2, n1_n, n2) self.timer.stop('Calculate') if i < kd.nibzkpts - 1: self.timer.start('Wait') if kparallel: kpt.wait() kpt2_q[0].wait() self.timer.stop('Wait') kpt2_q.pop(0) kpt2_q.append(kpt) self.evv = self.world.sum(self.evv) self.evvacdf = self.world.sum(self.evvacdf) self.calculate_exx_paw_correction() if self.method == 'standard': self.exx = self.evv + self.devv + self.evc + self.ecc elif self.method == 'acdf': self.exx = self.evvacdf + self.devv + self.evc + self.ecc else: 1 / 0 self.log('Exact exchange energy:') for txt, e in [('core-core', self.ecc), ('valence-core', self.evc), ('valence-valence (pseudo, acdf)', self.evvacdf), ('valence-valence (pseudo, standard)', self.evv), ('valence-valence (correction)', self.devv), ('total (%s)' % self.method, self.exx)]: self.log(' %-36s %14.6f eV' % (txt + ':', e * Hartree)) self.log('Total time: %10.3f seconds' % (time() - self.t0)) self.npairs = self.world.sum(self.npairs) assert self.npairs == self.npairs0 self.timer.stop('EXX') self.timer.write(self.fd) def calculate_gamma(self, vol, alpha): if self.molecule: return 0.0 N_c = self.kd.N_c offset_c = (N_c + 1) % 2 * 0.5 / N_c bzq_qc = monkhorst_pack(N_c) + offset_c qd = KPointDescriptor(bzq_qc) pd = PWDescriptor(self.wfs.pd.ecut, self.wfs.gd, kd=qd) gamma = (vol / (2 * pi)**2 * sqrt(pi / alpha) * self.kd.nbzkpts) for G2_G in pd.G2_qG: if G2_G[0] < 1e-7: G2_G = G2_G[1:] gamma -= np.dot(np.exp(-alpha * G2_G), G2_G**-1) return gamma / self.qstride_c.prod() def indices(self, s, k1, k2): """Generator for (K2, q, n1, n2) indices for (k1, k2) pair. s: int Spin index. k1: int Index of k-point in the IBZ. k2: int Index of k-point in the IBZ. Returns (K, q, n1_n, n2), where K then index of the k-point in the BZ that k2 is mapped to, q is the index of the q-vector between K and k1, and n1_n is a list of bands that should be combined with band n2.""" for K, k in enumerate(self.kd.bz2ibz_k): if k == k2: for K, q, n1_n, n2 in self._indices(s, k1, k2, K): yield K, q, n1_n, n2 def _indices(self, s, k1, k2, K2): k1_c = self.kd.ibzk_kc[k1] k2_c = self.kd.bzk_kc[K2] q_c = k2_c - k1_c q = abs(self.bzq_qc - q_c).sum(1).argmin() if abs(self.bzq_qc[q] - q_c).sum() > 1e-7: return if self.gamma_point == 0 and q == self.q0: return nocc1 = self.nocc_sk[s, k1] nocc2 = self.nocc_sk[s, k2] # Is k2 in the IBZ? is_ibz2 = (self.kd.ibz2bz_k[k2] == K2) for n2 in range(self.wfs.bd.nbands): # Find range of n1's (from n1a to n1b-1): if is_ibz2: # We get this combination twice, so let's only do half: if k1 >= k2: n1a = n2 else: n1a = n2 + 1 else: n1a = 0 n1b = self.wfs.bd.nbands if self.bandstructure: if n2 >= nocc2: n1b = min(n1b, nocc1) else: if n2 >= nocc2: break n1b = min(n1b, nocc1) if self.bands is not None: assert self.bandstructure n1_n = [] for n1 in range(n1a, n1b): if (n1 in self.bands and n2 < nocc2 or is_ibz2 and n2 in self.bands and n1 < nocc1): n1_n.append(n1) n1_n = np.array(n1_n) else: n1_n = np.arange(n1a, n1b) if len(n1_n) == 0: continue yield K2, q, n1_n, n2 def apply(self, K2, q, kpt1, kpt2, n1_n, n2): k20_c = self.kd.ibzk_kc[kpt2.k] k2_c = self.kd.bzk_kc[K2] if k2_c.any(): self.timer.start('Initialize plane waves') eik2r_R = self.wfs.gd.plane_wave(k2_c) eik20r_R = self.wfs.gd.plane_wave(k20_c) self.timer.stop('Initialize plane waves') else: eik2r_R = 1.0 eik20r_R = 1.0 w1 = self.kd.weight_k[kpt1.k] w2 = self.kd.weight_k[kpt2.k] # Is k2 in the 1. BZ? is_ibz2 = (self.kd.ibz2bz_k[kpt2.k] == K2) e_n = self.calculate_interaction(n1_n, n2, kpt1, kpt2, q, K2, eik20r_R, eik2r_R, is_ibz2) e_n *= 1.0 / self.kd.nbzkpts / self.wfs.nspins * self.qstride_c.prod() if q == self.q0: e_n[n1_n == n2] *= 0.5 f1_n = kpt1.f_n[n1_n] eps1_n = kpt1.eps_n[n1_n] f2 = kpt2.f_n[n2] eps2 = kpt2.eps_n[n2] s_n = np.sign(eps2 - eps1_n) evv = (f1_n * f2 * e_n).sum() evvacdf = 0.5 * (f1_n * (1 - s_n) * e_n + f2 * (1 + s_n) * e_n).sum() self.evv += evv * w1 self.evvacdf += evvacdf * w1 if is_ibz2: self.evv += evv * w2 self.evvacdf += evvacdf * w2 if self.bandstructure: x = self.wfs.nspins self.exx_skn[kpt1.s, kpt1.k, n1_n] += x * f2 * e_n if is_ibz2: self.exx_skn[kpt2.s, kpt2.k, n2] += x * np.dot(f1_n, e_n) def calculate_interaction(self, n1_n, n2, kpt1, kpt2, q, k, eik20r_R, eik2r_R, is_ibz2): """Calculate Coulomb interactions. For all n1 in the n1_n list, calculate interaction with n2.""" # number of plane waves: ng1 = self.wfs.ng_k[kpt1.k] ng2 = self.wfs.ng_k[kpt2.k] # Transform to real space and apply symmetry operation: self.timer.start('IFFT1') if is_ibz2: u2_R = self.pd.ifft(kpt2.psit_nG[n2, :ng2], kpt2.k) else: psit2_R = self.pd.ifft(kpt2.psit_nG[n2, :ng2], kpt2.k) * eik20r_R self.timer.start('Symmetry transform') u2_R = self.kd.transform_wave_function(psit2_R, k) / eik2r_R self.timer.stop() self.timer.stop() # Calculate pair densities: nt_nG = self.pd2.zeros(len(n1_n), q=q) for n1, nt_G in zip(n1_n, nt_nG): self.timer.start('IFFT2') u1_R = self.pd.ifft(kpt1.psit_nG[n1, :ng1], kpt1.k) self.timer.stop() nt_R = u1_R.conj() * u2_R self.timer.start('FFT') nt_G[:] = self.pd2.fft(nt_R, q) self.timer.stop() s = self.kd.sym_k[k] time_reversal = self.kd.time_reversal_k[k] k2_c = self.kd.ibzk_kc[kpt2.k] self.timer.start('Compensation charges') Q_anL = {} # coefficients for shape functions for a, P1_ni in kpt1.P_ani.items(): P1_ni = P1_ni[n1_n] if is_ibz2: P2_i = kpt2.P_ani[a][n2] else: b = self.kd.symmetry.a_sa[s, a] S_c = (np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s]) - self.spos_ac[b]) assert abs(S_c.round() - S_c).max() < 1e-5 if self.ghat.dtype == complex: x = np.exp(2j * pi * np.dot(k2_c, S_c)) else: x = 1.0 P2_i = np.dot(self.wfs.setups[a].R_sii[s], kpt2.P_ani[b][n2]) * x if time_reversal: P2_i = P2_i.conj() D_np = [] for P1_i in P1_ni: D_ii = np.outer(P1_i.conj(), P2_i) D_np.append(pack(D_ii)) Q_anL[a] = np.dot(D_np, self.wfs.setups[a].Delta_pL) self.timer.start('Expand') if q != self.qlatest: self.f_IG = self.ghat.expand(q) self.qlatest = q self.timer.stop('Expand') # Add compensation charges: self.ghat.add(nt_nG, Q_anL, q, self.f_IG) self.timer.stop('Compensation charges') if self.molecule and n2 in n1_n: nn = (n1_n == n2).nonzero()[0][0] nt_nG[nn] -= self.ngauss_G else: nn = None iG2_G = self.iG2_qG[q] # Calculate energies: e_n = np.empty(len(n1_n)) for n, nt_G in enumerate(nt_nG): e_n[n] = -4 * pi * np.real(self.pd2.integrate(nt_G, nt_G * iG2_G)) self.npairs += 1 if nn is not None: e_n[nn] -= 2 * (self.pd2.integrate(nt_nG[nn], self.vgauss_G) + (self.beta / 2 / pi)**0.5) if self.write_timing_information: t = (time() - self.t0) / len(n1_n) self.log('Time for first pair-density: %10.3f seconds' % t) self.log('Estimated total time: %10.3f seconds' % (t * self.npairs0 / self.world.size)) self.write_timing_information = False return e_n 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 initialize_gaussian(self): """Calculate gaussian compensation charge and its potential. Used to decouple electrostatic interactions between periodically repeated images for molecular calculations. Charge containing one electron:: (beta/pi)^(3/2)*exp(-beta*r^2), its Fourier transform:: exp(-G^2/(4*beta)), and its potential:: erf(beta^0.5*r)/r. """ gd = self.wfs.gd # Set exponent of exp-function to -19 on the boundary: self.beta = 4 * 19 * (gd.icell_cv**2).sum(1).max() # Calculate gaussian: G_Gv = self.pd2.get_reciprocal_vectors() G2_G = self.pd2.G2_qG[0] C_v = gd.cell_cv.sum(0) / 2 # center of cell self.ngauss_G = np.exp(-1.0 / (4 * self.beta) * G2_G + 1j * np.dot(G_Gv, C_v)) / gd.dv # Calculate potential from gaussian: R_Rv = gd.get_grid_point_coordinates().transpose((1, 2, 3, 0)) r_R = ((R_Rv - C_v)**2).sum(3)**0.5 if (gd.N_c % 2 == 0).all(): r_R[tuple(gd.N_c // 2)] = 1.0 # avoid dividing by zero v_R = erf(self.beta**0.5 * r_R) / r_R if (gd.N_c % 2 == 0).all(): v_R[tuple(gd.N_c // 2)] = (4 * self.beta / pi)**0.5 self.vgauss_G = self.pd2.fft(v_R) # Compare self-interaction to analytic result: assert abs(0.5 * self.pd2.integrate(self.ngauss_G, self.vgauss_G) - (self.beta / 2 / pi)**0.5) < 1e-6
x = 2.0 rc = 3.5 r = np.linspace(0, rc, 100) n = 40 a = 8.0 cell_cv = np.array([[a, 0.5, -1], [0, a, 2], [-1, 0, a + 1]]) gd = GridDescriptor((n, n, n), cell_cv, comm=mpi.serial_comm) a_R = gd.empty() z = np.linspace(0, n, n, endpoint=False) a_R[:] = 2 + np.sin(2 * np.pi * z / n) spos_ac = np.array([(0.15, 0.45, 0.95)]) pd = PWDescriptor(45, gd) a_G = pd.fft(a_R) s = Spline(0, rc, 2 * x**1.5 / np.pi * np.exp(-x * r**2)) p = Spline(1, rc, 2 * x**1.5 / np.pi * np.exp(-x * r**2)) d = Spline(2, rc, 2 * x**1.5 / np.pi * np.exp(-x * r**2)) lfc = PWLFC([[s, p, d]], pd) lfc.set_positions(spos_ac) b_LG = pd.zeros(9) lfc.add(b_LG, {0: np.eye(9)}) e1 = pd.integrate(a_G, b_LG) assert abs(lfc.integrate(a_G)[0] - e1).max() < 1e-11 s1 = [] for i in range(9):
class Kernel: def __init__(self, calc, xc, ibzq_qc, fd, unit_cells, density_cut, ecut, tag): self.calc = calc self.gd = calc.density.gd self.xc = xc self.ibzq_qc = ibzq_qc self.fd = fd self.unit_cells = unit_cells self.density_cut = density_cut self.ecut = ecut self.tag = tag self.A_x = -(3 / 4.) * (3 / np.pi)**(1 / 3.) self.n_g = calc.get_all_electron_density(gridrefinement=1) self.n_g *= Bohr**3 if xc[-3:] == 'PBE': nf_g = calc.get_all_electron_density(gridrefinement=2) nf_g *= Bohr**3 gdf = self.gd.refine() grad_v = [Gradient(gdf, v, n=1).apply for v in range(3)] gradnf_vg = gdf.empty(3) for v in range(3): grad_v[v](nf_g, gradnf_vg[v]) self.gradn_vg = gradnf_vg[:, ::2, ::2, ::2] qd = KPointDescriptor(self.ibzq_qc) self.pd = PWDescriptor(ecut / Hartree, self.gd, complex, qd) def calculate_fhxc(self): prnt('Calculating %s kernel at %d eV cutoff' % (self.xc, self.ecut), file=self.fd) if self.xc[0] == 'r': self.calculate_rkernel() else: assert self.xc[0] == 'A' self.calculate_local_kernel() def calculate_rkernel(self): gd = self.gd ng_c = gd.N_c cell_cv = gd.cell_cv icell_cv = 2 * np.pi * np.linalg.inv(cell_cv) vol = np.linalg.det(cell_cv) ns = self.calc.wfs.nspins n_g = self.n_g # density on rough grid fx_g = ns * self.get_fxc_g(n_g) # local exchange kernel qc_g = (-4 * np.pi * ns / fx_g)**0.5 # cutoff functional flocal_g = qc_g**3 * fx_g / (6 * np.pi**2) # ren. x-kernel for r=r' Vlocal_g = 2 * qc_g / np.pi # ren. Hartree kernel for r=r' ng = np.prod(ng_c) # number of grid points r_vg = gd.get_grid_point_coordinates() rx_g = r_vg[0].flatten() ry_g = r_vg[1].flatten() rz_g = r_vg[2].flatten() prnt(' %d grid points and %d plane waves at the Gamma point' % (ng, self.pd.ngmax), file=self.fd) # Unit cells R_Rv = [] weight_R = [] nR_v = self.unit_cells nR = np.prod(nR_v) for i in range(-nR_v[0] + 1, nR_v[0]): for j in range(-nR_v[1] + 1, nR_v[1]): for h in range(-nR_v[2] + 1, nR_v[2]): R_Rv.append(i * cell_cv[0] + j * cell_cv[1] + h * cell_cv[2]) weight_R.append((nR_v[0] - abs(i)) * (nR_v[1] - abs(j)) * (nR_v[2] - abs(h)) / float(nR)) if nR > 1: # with more than one unit cell only the exchange kernel is # calculated on the grid. The bare Coulomb kernel is added # in PW basis and Vlocal_g only the exchange part dv = self.calc.density.gd.dv gc = (3 * dv / 4 / np.pi)**(1 / 3.) Vlocal_g -= 2 * np.pi * gc**2 / dv prnt(' Lattice point sampling: ' + '(%s x %s x %s)^2 ' % (nR_v[0], nR_v[1], nR_v[2]) + ' Reduced to %s lattice points' % len(R_Rv), file=self.fd) l_g_size = -(-ng // mpi.world.size) l_g_range = range(mpi.world.rank * l_g_size, min((mpi.world.rank+1) * l_g_size, ng)) fhxc_qsGr = {} for iq in range(len(self.ibzq_qc)): fhxc_qsGr[iq] = np.zeros((ns, len(self.pd.G2_qG[iq]), len(l_g_range)), dtype=complex) inv_error = np.seterr() np.seterr(invalid='ignore') np.seterr(divide='ignore') t0 = time() # Loop over Lattice points for i, R_v in enumerate(R_Rv): # Loop over r'. f_rr and V_rr are functions of r (dim. as r_vg[0]) if i == 1: prnt(' Finished 1 cell in %s seconds' % int(time() - t0) + ' - estimated %s seconds left' % int((len(R_Rv) - 1) * (time() - t0)), file=self.fd) self.fd.flush() if len(R_Rv) > 5: if (i+1) % (len(R_Rv) / 5 + 1) == 0: prnt(' Finished %s cells in %s seconds' % (i, int(time() - t0)) + ' - estimated %s seconds left' % int((len(R_Rv) - i) * (time() - t0) / i), file=self.fd) self.fd.flush() for g in l_g_range: rx = rx_g[g] + R_v[0] ry = ry_g[g] + R_v[1] rz = rz_g[g] + R_v[2] # |r-r'-R_i| rr = ((r_vg[0] - rx)**2 + (r_vg[1] - ry)**2 + (r_vg[2] - rz)**2)**0.5 n_av = (n_g + n_g.flatten()[g]) / 2. fx_g = ns * self.get_fxc_g(n_av, index=g) qc_g = (-4 * np.pi * ns / fx_g)**0.5 x = qc_g * rr osc_x = np.sin(x) - x*np.cos(x) f_rr = fx_g * osc_x / (2 * np.pi**2 * rr**3) if nR > 1: # include only exchange part of the kernel here V_rr = (sici(x)[0] * 2 / np.pi - 1) / rr else: # include the full kernel (also hartree part) V_rr = (sici(x)[0] * 2 / np.pi) / rr # Terms with r = r' if (np.abs(R_v) < 0.001).all(): tmp_flat = f_rr.flatten() tmp_flat[g] = flocal_g.flatten()[g] f_rr = tmp_flat.reshape(ng_c) tmp_flat = V_rr.flatten() tmp_flat[g] = Vlocal_g.flatten()[g] V_rr = tmp_flat.reshape(ng_c) del tmp_flat f_rr[np.where(n_av < self.density_cut)] = 0.0 V_rr[np.where(n_av < self.density_cut)] = 0.0 f_rr *= weight_R[i] V_rr *= weight_R[i] # r-r'-R_i r_r = np.array([r_vg[0] - rx, r_vg[1] - ry, r_vg[2] - rz]) # Fourier transform of r for iq, q in enumerate(self.ibzq_qc): q_v = np.dot(q, icell_cv) e_q = np.exp(-1j * gemmdot(q_v, r_r, beta=0.0)) f_q = self.pd.fft((f_rr + V_rr) * e_q, iq) * vol / ng fhxc_qsGr[iq][0, :, g - l_g_range[0]] += f_q if ns == 2: f_q = self.pd.fft(V_rr * e_q, iq) * vol / ng fhxc_qsGr[iq][1, :, g - l_g_range[0]] += f_q mpi.world.barrier() np.seterr(**inv_error) for iq, q in enumerate(self.ibzq_qc): npw = len(self.pd.G2_qG[iq]) fhxc_sGsG = np.zeros((ns * npw, ns * npw), complex) l_pw_size = -(-npw // mpi.world.size) # parallelize over PW below l_pw_range = range(mpi.world.rank * l_pw_size, min((mpi.world.rank + 1) * l_pw_size, npw)) if mpi.world.size > 1: # redistribute grid and plane waves in fhxc_qsGr[iq] bg1 = BlacsGrid(mpi.world, 1, mpi.world.size) bg2 = BlacsGrid(mpi.world, mpi.world.size, 1) bd1 = bg1.new_descriptor(npw, ng, npw, - (-ng / mpi.world.size)) bd2 = bg2.new_descriptor(npw, ng, -(-npw / mpi.world.size), ng) fhxc_Glr = np.zeros((len(l_pw_range), ng), dtype=complex) if ns == 2: Koff_Glr = np.zeros((len(l_pw_range), ng), dtype=complex) r = Redistributor(bg1.comm, bd1, bd2) r.redistribute(fhxc_qsGr[iq][0], fhxc_Glr, npw, ng) if ns == 2: r.redistribute(fhxc_qsGr[iq][1], Koff_Glr, npw, ng) else: fhxc_Glr = fhxc_qsGr[iq][0] if ns == 2: Koff_Glr = fhxc_qsGr[iq][1] # Fourier transform of r' for iG in range(len(l_pw_range)): f_g = fhxc_Glr[iG].reshape(ng_c) f_G = self.pd.fft(f_g.conj(), iq) * vol / ng fhxc_sGsG[l_pw_range[0] + iG, :npw] = f_G.conj() if ns == 2: v_g = Koff_Glr[iG].reshape(ng_c) v_G = self.pd.fft(v_g.conj(), iq) * vol / ng fhxc_sGsG[npw + l_pw_range[0] + iG, :npw] = v_G.conj() if ns == 2: # f_00 = f_11 and f_01 = f_10 fhxc_sGsG[:npw, npw:] = fhxc_sGsG[npw:, :npw] fhxc_sGsG[npw:, npw:] = fhxc_sGsG[:npw, :npw] mpi.world.sum(fhxc_sGsG) fhxc_sGsG /= vol if mpi.rank == 0: w = Writer('fhxc_%s_%s_%s_%s.gpw' % (self.tag, self.xc, self.ecut, iq)) w.dimension('sG', ns * npw) w.add('fhxc_sGsG', ('sG', 'sG'), dtype=complex) if nR > 1: # add Hartree kernel evaluated in PW basis Gq2_G = self.pd.G2_qG[iq] if (q == 0).all(): Gq2_G[0] = 1. vq_G = 4 * np.pi / Gq2_G fhxc_sGsG += np.tile(np.eye(npw) * vq_G, (ns, ns)) w.fill(fhxc_sGsG) w.close() mpi.world.barrier() prnt(file=self.fd) def calculate_local_kernel(self): # Standard ALDA exchange kernel # Use with care. Results are very difficult to converge # Sensitive to density_cut ns = self.calc.wfs.nspins gd = self.gd pd = self.pd cell_cv = gd.cell_cv icell_cv = 2 * np.pi * np.linalg.inv(cell_cv) vol = np.linalg.det(cell_cv) fxc_sg = ns * self.get_fxc_g(ns * self.n_g) fxc_sg[np.where(self.n_g < self.density_cut)] = 0.0 r_vg = gd.get_grid_point_coordinates() for iq in range(len(self.ibzq_qc)): Gvec_Gc = np.dot(pd.G_Qv[pd.Q_qG[iq]], cell_cv / (2 * np.pi)) npw = len(Gvec_Gc) l_pw_size = -(-npw // mpi.world.size) l_pw_range = range(mpi.world.rank * l_pw_size, min((mpi.world.rank + 1) * l_pw_size, npw)) fhxc_sGsG = np.zeros((ns * npw, ns * npw), dtype=complex) for s in range(ns): for iG in l_pw_range: for jG in range(npw): fxc = fxc_sg[s].copy() dG_c = Gvec_Gc[iG] - Gvec_Gc[jG] dG_v = np.dot(dG_c, icell_cv) dGr_g = gemmdot(dG_v, r_vg, beta=0.0) ft_fxc = gd.integrate(np.exp(-1j * dGr_g) * fxc) fhxc_sGsG[s * npw + iG, s * npw + jG] = ft_fxc mpi.world.sum(fhxc_sGsG) fhxc_sGsG /= vol Gq2_G = self.pd.G2_qG[iq] if (self.ibzq_qc[iq] == 0).all(): Gq2_G[0] = 1. vq_G = 4 * np.pi / Gq2_G fhxc_sGsG += np.tile(np.eye(npw) * vq_G, (ns, ns)) if mpi.rank == 0: w = Writer('fhxc_%s_%s_%s_%s.gpw' % (self.tag, self.xc, self.ecut, iq)) w.dimension('sG', ns * npw) w.add('fhxc_sGsG', ('sG', 'sG'), dtype=complex) w.fill(fhxc_sGsG) w.close() mpi.world.barrier() prnt(file=self.fd) def get_fxc_g(self, n_g, index=None): if self.xc[-3:] == 'LDA': return self.get_lda_g(n_g) elif self.xc[-3:] == 'PBE': return self.get_pbe_g(n_g, index=index) else: raise '%s kernel not recognized' % self.xc def get_lda_g(self, n_g): return (4. / 9.) * self.A_x * n_g**(-2./3.) def get_pbe_g(self, n_g, index=None): if index is None: gradn_vg = self.gradn_vg else: gradn_vg = self.calc.density.gd.empty(3) for v in range(3): gradn_vg[v] = (self.gradn_vg[v] + self.gradn_vg[v].flatten()[index]) / 2 kf_g = (3. * np.pi**2 * n_g)**(1 / 3.) s2_g = np.zeros_like(n_g) for v in range(3): axpy(1.0, gradn_vg[v]**2, s2_g) s2_g /= 4 * kf_g**2 * n_g**2 e_g = self.A_x * n_g**(4 / 3.) v_g = (4 / 3.) * e_g / n_g f_g = (1 / 3.) * v_g / n_g kappa = 0.804 mu = 0.2195149727645171 denom_g = (1 + mu * s2_g / kappa) F_g = 1. + kappa - kappa / denom_g Fn_g = -mu / denom_g**2 * 8 * s2_g / (3 * n_g) Fnn_g = -11 * Fn_g / (3 * n_g) - 2 * Fn_g**2 / kappa fxc_g = f_g * F_g fxc_g += 2 * v_g * Fn_g fxc_g += e_g * Fnn_g # Contributions from varying the gradient #Fgrad_vg = np.zeros_like(gradn_vg) #Fngrad_vg = np.zeros_like(gradn_vg) #for v in range(3): # axpy(1.0, mu / den_g**2 * gradn_vg[v] / (2 * kf_g**2 * n_g**2), # Fgrad_vg[v]) # axpy(-8.0, Fgrad_vg[v] / (3 * n_g), Fngrad_vg[v]) # axpy(-2.0, Fgrad_vg[v] * Fn_g / kappa, Fngrad_vg[v]) #tmp = np.zeros_like(fxc_g) #tmp1 = np.zeros_like(fxc_g) #for v in range(3): #self.grad_v[v](Fgrad_vg[v], tmp) #axpy(-2.0, tmp * v_g, fxc_g) #for u in range(3): #self.grad_v[u](Fgrad_vg[u] * tmp, tmp1) #axpy(-4.0/kappa, tmp1 * e_g, fxc_g) #self.grad_v[v](Fngrad_vg[v], tmp) #axpy(-2.0, tmp * e_g, fxc_g) #self.laplace(mu / den_g**2 / (2 * kf_g**2 * n_g**2), tmp) #axpy(1.0, tmp * e_g, fxc_g) return fxc_g def get_fxc_libxc_g(self, n_g): ### NOT USED AT THE MOMENT gd = self.calc.density.gd.refine() xc = XC('GGA_X_' + self.xc[2:]) #xc = XC('LDA_X') #sigma = np.zeros_like(n_g).flat[:] xc.set_grid_descriptor(gd) sigma_xg, gradn_svg = xc.calculate_sigma(np.array([n_g])) dedsigma_xg = np.zeros_like(sigma_xg) e_g = np.zeros_like(n_g) v_sg = np.array([np.zeros_like(n_g)]) xc.calculate_gga(e_g, np.array([n_g]), v_sg, sigma_xg, dedsigma_xg) sigma = sigma_xg[0].flat[:] gradn_vg = gradn_svg[0] dedsigma_g = dedsigma_xg[0] libxc = LibXC('GGA_X_' + self.xc[2:]) #libxc = LibXC('LDA_X') libxc.initialize(1) libxc_fxc = libxc.xc.calculate_fxc_spinpaired fxc_g = np.zeros_like(n_g).flat[:] d2edndsigma_g = np.zeros_like(n_g).flat[:] d2ed2sigma_g = np.zeros_like(n_g).flat[:] libxc_fxc(n_g.flat[:], fxc_g, sigma, d2edndsigma_g, d2ed2sigma_g) fxc_g = fxc_g.reshape(np.shape(n_g)) d2edndsigma_g = d2edndsigma_g.reshape(np.shape(n_g)) d2ed2sigma_g = d2ed2sigma_g.reshape(np.shape(n_g)) tmp = np.zeros_like(fxc_g) tmp1 = np.zeros_like(fxc_g) #for v in range(3): #self.grad_v[v](d2edndsigma_g * gradn_vg[v], tmp) #axpy(-4.0, tmp, fxc_g) #for u in range(3): #for v in range(3): #self.grad_v[v](d2ed2sigma_g * gradn_vg[u] * gradn_vg[v], tmp) #self.grad_v[u](tmp, tmp1) #axpy(4.0, tmp1, fxc_g) #self.laplace(dedsigma_g, tmp) #axpy(2.0, tmp, fxc_g) return fxc_g[::2, ::2, ::2] def get_numerical_fxc_sg(self, n_sg): ### NOT USED AT THE MOMENT gd = self.calc.density.gd.refine() delta = 1.e-4 if self.xc[2:] == 'LDA': xc = XC('LDA_X') v1xc_sg = np.zeros_like(n_sg) v2xc_sg = np.zeros_like(n_sg) xc.calculate(gd, (1 + delta) * n_sg, v1xc_sg) xc.calculate(gd, (1 - delta) * n_sg, v2xc_sg) fxc_sg = (v1xc_sg - v2xc_sg) / (2 * delta * n_sg) else: fxc_sg = np.zeros_like(n_sg) xc = XC('GGA_X_' + self.xc[2:]) vxc_sg = np.zeros_like(n_sg) xc.calculate(gd, n_sg, vxc_sg) for s in range(len(n_sg)): for x in range(len(n_sg[0])): for y in range(len(n_sg[0, 0])): for z in range(len(n_sg[0, 0, 0])): v1xc_sg = np.zeros_like(n_sg) n1_sg = n_sg.copy() n1_sg[s, x, y, z] *= (1 + delta) xc.calculate(gd, n1_sg, v1xc_sg) num = v1xc_sg[s, x, y, z] - vxc_sg[s, x, y, z] den = delta * n_sg[s, x, y, z] fxc_sg[s, x, y, z] = num / den return fxc_sg[:, ::2, ::2, ::2]
class HybridXC(XCFunctional): orbital_dependent = True def __init__(self, name, hybrid=None, xc=None, finegrid=False, alpha=None): """Mix standard functionals with exact exchange. name: str Name of hybrid functional. hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. finegrid: boolean Use fine grid for energy functional evaluations? """ if name == 'EXX': assert hybrid is None and xc is None hybrid = 1.0 xc = XC(XCNull()) elif name == 'PBE0': assert hybrid is None and xc is None hybrid = 0.25 xc = XC('HYB_GGA_XC_PBEH') elif name == 'B3LYP': assert hybrid is None and xc is None hybrid = 0.2 xc = XC('HYB_GGA_XC_B3LYP') if isinstance(xc, str): xc = XC(xc) self.hybrid = hybrid self.xc = xc self.type = xc.type self.alpha = alpha self.exx = 0.0 XCFunctional.__init__(self, name) def get_setup_name(self): return 'PBE' def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: self.alpha = 6 * vol**(2 / 3.0) / pi**2 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() if self.kd.N_c is None: self.bzk_kc = np.zeros((1, 3)) dfghdfgh else: n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.pwd = PWDescriptor(ecut, self.gd, self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.pwd.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G**-1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:]**-1) n += 1 assert n == self.kd.N_c.prod() self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], dtype=complex) self.ghat.set_k_points(self.bzk_kc) self.fullkd = KPointDescriptor(self.kd.bzk_kc, nspins=1) class S: id_a = [] def set_symmetry(self, s): pass self.fullkd.set_symmetry(Atoms(pbc=True), S(), False) self.fullkd.set_communicator(world) self.pt = LFC(self.gd, [setup.pt_j for setup in density.setups], dtype=complex) self.pt.set_k_points(self.fullkd.ibzk_kc) self.interpolator = density.interpolator def set_positions(self, spos_ac): self.ghat.set_positions(spos_ac) self.pt.set_positions(spos_ac) def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx def calculate_exx(self): """Non-selfconsistent calculation.""" kd = self.kd K = self.fullkd.nibzkpts assert self.nspins == 1 Q = K // world.size assert Q * world.size == K parallel = (world.size > self.nspins) self.exx = 0.0 self.exx_skn = np.zeros((self.nspins, K, self.bd.nbands)) kpt_u = [] for k in range(world.rank * Q, (world.rank + 1) * Q): k_c = self.fullkd.ibzk_kc[k] for k1, k1_c in enumerate(kd.bzk_kc): if abs(k1_c - k_c).max() < 1e-10: break # Index of symmetry related point in the irreducible BZ ik = kd.kibz_k[k1] kpt = self.kpt_u[ik] # KPoint from ground-state calculation phase_cd = np.exp(2j * pi * self.gd.sdisp_cd * k_c[:, np.newaxis]) kpt2 = KPoint0(kpt.weight, kpt.s, k, None, phase_cd) kpt2.psit_nG = np.empty_like(kpt.psit_nG) kpt2.f_n = kpt.f_n / kpt.weight / K * 2 for n, psit_G in enumerate(kpt2.psit_nG): psit_G[:] = kd.transform_wave_function(kpt.psit_nG[n], k1) kpt2.P_ani = self.pt.dict(len(kpt.psit_nG)) self.pt.integrate(kpt2.psit_nG, kpt2.P_ani, k) kpt_u.append(kpt2) for s in range(self.nspins): kpt1_q = [KPoint(self.fullkd, kpt) for kpt in 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 = self.fullkd.get_rank_and_index(s, (kpt1_q[0].k - 1) % K)[0] # Receive rank: rrank = self.fullkd.get_rank_and_index(s, (kpt1_q[-1].k + 1) % K)[0] # Shift k-points K // 2 times: for i in range(K // 2 + 1): if i < K // 2: 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): if 2 * i == K: self.apply(kpt1, kpt2, invert=(kpt1.k > kpt2.k)) else: self.apply(kpt1, kpt2) self.apply(kpt1, kpt2, invert=True) if i < K // 2: if parallel: kpt.wait() kpt2_q[0].wait() kpt2_q.pop(0) kpt2_q.append(kpt) self.exx = world.sum(self.exx) world.sum(self.exx_skn) self.exx += self.calculate_paw_correction() def apply(self, kpt1, kpt2, invert=False): #print world.rank,kpt1.k,kpt2.k,invert k1_c = self.fullkd.ibzk_kc[kpt1.k] k2_c = self.fullkd.ibzk_kc[kpt2.k] if invert: k2_c = -k2_c k12_c = k1_c - k2_c N_c = self.gd.N_c eikr_R = np.exp(2j * pi * np.dot(np.indices(N_c).T, k12_c / N_c).T) for q, k_c in enumerate(self.bzk_kc): if abs(k_c + k12_c).max() < 1e-9: q0 = q break for q, k_c in enumerate(self.bzk_kc): if abs(k_c - k12_c).max() < 1e-9: q00 = q break Gpk2_G = self.pwd.G2_qG[q0] if Gpk2_G[0] == 0: Gpk2_G = Gpk2_G.copy() Gpk2_G[0] = 1.0 / self.gamma N = N_c.prod() vol = self.gd.dv * N nspins = self.nspins same = (kpt1.k == kpt2.k) for n1, psit1_R in enumerate(kpt1.psit_nG): f1 = kpt1.f_n[n1] for n2, psit2_R in enumerate(kpt2.psit_nG): if same and n2 > n1: continue f2 = kpt2.f_n[n2] nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, q0, invert) nt_G = self.pwd.fft(nt_R * eikr_R) / N vt_G = nt_G.copy() vt_G *= -pi * vol / Gpk2_G e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid if same and n1 == n2: e /= 2 self.exx += e * f1 * f2 self.ekin -= 2 * e * f1 * f2 self.exx_skn[kpt1.s, kpt1.k, n1] += f2 * e self.exx_skn[kpt2.s, kpt2.k, n2] += f1 * e calculate_potential = not True if calculate_potential: vt_R = self.pwd.ifft(vt_G).conj() * eikr_R * N / vol if kpt1 is kpt2 and not invert and n1 == n2: kpt1.vt_nG[n1] = 0.5 * f1 * vt_R if invert: kpt1.Htpsit_nG[n1] += \ f2 * nspins * psit2_R.conj() * vt_R else: kpt1.Htpsit_nG[n1] += f2 * nspins * psit2_R * vt_R if kpt1 is not kpt2: if invert: kpt2.Htpsit_nG[n2] += (f1 * nspins * psit1_R.conj() * vt_R) else: kpt2.Htpsit_nG[n2] += (f1 * nspins * psit1_R * vt_R.conj()) def calculate_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 calculate_pair_density(self, n1, n2, kpt1, kpt2, q, invert): if invert: nt_G = kpt1.psit_nG[n1].conj() * kpt2.psit_nG[n2].conj() else: nt_G = kpt1.psit_nG[n1].conj() * kpt2.psit_nG[n2] Q_aL = {} for a, P1_ni in kpt1.P_ani.items(): P1_i = P1_ni[n1] P2_i = kpt2.P_ani[a][n2] if invert: D_ii = np.outer(P1_i.conj(), P2_i.conj()) else: D_ii = np.outer(P1_i.conj(), P2_i) D_p = pack(D_ii) Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) self.ghat.add(nt_G, Q_aL, q) return nt_G
def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.wfs = wfs self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol**(2 / 3.0) / pi**2 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) if self.ecut is None: self.ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() * 0.9999 assert self.kd.N_c is not None n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.bzq_qc = self.kd.get_bz_q_points() if self.qsym: op_scc = self.kd.symmetry.op_scc self.ibzq_qc = self.kd.get_ibz_q_points(self.bzq_qc, op_scc)[0] self.q_weights = self.kd.q_weights * len(self.bzq_qc) else: self.ibzq_qc = self.bzq_qc self.q_weights = np.ones(len(self.bzq_qc)) self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G**-1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:]**-1) n += 1 assert n == self.kd.N_c.prod() self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.ibzq_qc) self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], KPointDescriptor(self.bzq_qc), dtype=complex) #self.interpolator = density.interpolator self.print_initialization(hamiltonian.xc.name)
class TDDFT(object): """ Time-dependent DFT+Hartree-Fock in Kohn-Sham orbitals basis: calc: GPAW calculator (setups='sg15') nbands (int): number of bands in calculation """ def __init__(self, calc, nbands=None, Fock=False): self.calc = calc self.Fock = Fock self.K = calc.get_ibz_k_points() # reduced Brillioun zone self.NK = self.K.shape[0] self.wk = calc.get_k_point_weights( ) # weight of reduced Brillioun zone if nbands is None: self.nbands = calc.get_number_of_bands() else: self.nbands = nbands self.nvalence = int(calc.get_number_of_electrons() / 2) self.EK = [ calc.get_eigenvalues(k)[:self.nbands] for k in range(self.NK) ] # bands energy self.EK = np.array(self.EK) / Hartree self.shape = tuple( calc.get_number_of_grid_points()) # shape of real space grid self.density = calc.get_pseudo_density( ) * Bohr**3 # density at zero time # array of u_nk (periodic part of Kohn-Sham orbitals,only reduced Brillion zone) self.ukn = np.zeros(( self.NK, self.nbands, ) + self.shape, dtype=np.complex) for k in range(self.NK): kpt = calc.wfs.kpt_u[k] for n in range(self.nbands): psit_G = kpt.psit_nG[n] psit_R = calc.wfs.pd.ifft(psit_G, kpt.q) self.ukn[k, n] = psit_R self.icell = 2.0 * np.pi * calc.wfs.gd.icell_cv # inverse cell self.cell = calc.wfs.gd.cell_cv # cell self.r = calc.wfs.gd.get_grid_point_coordinates() for i in range(3): self.r[i] -= self.cell[i, i] / 2. self.volume = np.abs(np.linalg.det( calc.wfs.gd.cell_cv)) # volume of cell self.norm = calc.wfs.gd.dv # self.Fermi = calc.get_fermi_level() / Hartree #Fermi level #desriptors at q=gamma for Hartree self.kdH = KPointDescriptor([[0, 0, 0]]) self.pdH = PWDescriptor(ecut=calc.wfs.pd.ecut, gd=calc.wfs.gd, kd=self.kdH, dtype=complex) #desriptors at q=gamma for Fock self.kdF = KPointDescriptor([[0, 0, 0]]) self.pdF = PWDescriptor(ecut=calc.wfs.pd.ecut / 4., gd=calc.wfs.gd, kd=self.kdF, dtype=complex) #Fermi-Dirac temperature self.temperature = calc.occupations.width #calculate pair-density matrices if Fock: self.M = np.zeros((self.nbands, self.nbands, self.NK, self.NK, self.pdF.get_reciprocal_vectors().shape[0]), dtype=np.complex) indexes = [(n, k) for n, k in product(range(self.nbands), range(self.NK))] for i1 in range(len(indexes)): n1, k1 = indexes[i1] for i2 in range(i1, len(indexes)): n2, k2 = indexes[i1] self.M[n1, n2, k1, k2] = self.pdF.fft( self.ukn[k1, n1].conj() * self.ukn[k2, n2]) self.M[n2, n1, k2, k1] = self.M[n1, n2, k1, k2].conj() self.M *= calc.wfs.gd.dv #Fermi-Dirac distribution self.f = 1 / (1 + np.exp((self.EK - self.Fermi) / self.temperature)) self.Hartree_elements = np.zeros( (self.NK, self.nbands, self.NK, self.nbands, self.nbands), dtype=np.complex) self.LDAx_elements = np.zeros( (self.NK, self.nbands, self.NK, self.nbands, self.nbands), dtype=np.complex) self.LDAc_elements = np.zeros( (self.NK, self.nbands, self.NK, self.nbands, self.nbands), dtype=np.complex) G = self.pdH.get_reciprocal_vectors() G2 = np.linalg.norm(G, axis=1)**2 G2[G2 == 0] = np.inf matrix = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) for k in tqdm(range(self.NK)): for n in range(self.nbands): density = 2 * np.abs(self.ukn[k, n])**2 operator = xc.VLDAx(density) self.LDAx_elements[k, n] = operator_matrix_periodic( matrix, operator, self.ukn.conj(), self.ukn) * self.norm operator = xc.VLDAc(density) self.LDAc_elements[k, n] = operator_matrix_periodic( matrix, operator, self.ukn.conj(), self.ukn) * self.norm density = self.pdH.fft(density) operator = 4 * np.pi * self.pdH.ifft(density / G2) self.Hartree_elements[k, n] = operator_matrix_periodic( matrix, operator, self.ukn.conj(), self.ukn) * self.norm self.wavefunction = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) self.Kinetic = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) self.dipole = self.get_dipole_matrix() for k in range(self.NK): self.wavefunction[k] = np.eye(self.nbands) self.Kinetic[k] = np.diag(self.EK[k]) self.VH0 = self.get_Hartree_matrix(self.wavefunction) self.VLDAc0 = self.get_LDA_correlation_matrix(self.wavefunction) self.VLDAx0 = self.get_LDA_exchange_matrix(self.wavefunction) self.Full_BZ = calc.get_bz_k_points() self.IBZ_map = calc.get_bz_to_ibz_map() def get_dipole_matrix(self, direction=[1, 0, 0]): """ return two-dimensional numpy complex array of dipole matrix elements( """ direction /= np.linalg.norm(direction) r = np.sum(direction[:, None, None, None] * self.r, axis=0) dipole = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) dipole = operator_matrix_periodic( dipole, r, self.ukn.conj(), self.ukn) * self.norm #!!!!!! no direction return dipole def get_density(self, wavefunction): """ return numpy array of electron density in real space at each k-point of full Brillioun zone wavefunction: numpy array [N_kpoint X N_band X N_band] of wavefunction in basis of Kohn-Sham orbital """ if wavefunction is None: return self.density density = np.zeros(self.shape, dtype=np.float) for k in range(self.NK): for n in range(self.nbands): for m in range(self.nbands): density += 2 * self.wk[k] * self.f[k, n] * np.abs( wavefunction[k, m, n] * self.ukn[k, m])**2 return density def get_Hartree_potential(self, wavefunction): """ return numpy array of Hartree potential in real space at each k-point of full Brillioun zone wavefunction: numpy array [N_kpoint X N_band X N_band] of wavefunction in basis of Kohn-Sham orbital """ density = self.get_density(wavefunction) VH = np.zeros(self.shape) G = self.pdH.get_reciprocal_vectors() G2 = np.linalg.norm(G, axis=1)**2 G2[G2 == 0] = np.inf nG = self.pdH.fft(density) return -4 * np.pi * self.pdH.ifft(nG / G2) def get_coloumb_potential(self, q): """ return coloumb potential in plane wave space V= 4 pi /(|q+G|**2) q: [qx,qy,qz] vector in units of reciprocal space """ G = self.pdF.get_reciprocal_vectors() + np.dot(q, self.icell) G2 = np.linalg.norm(G, axis=1)**2 G2[G2 == 0] = np.inf return 4 * np.pi / G2 def get_Hartree_matrix(self, wavefunction=None): """ return numpy array [N_kpoint X N_band X N_band] of Hartree potential matrix elements wavefunction: numpy array [N_kpoint X N_band X N_band] of wavefunction in basis of Kohn-Sham orbital """ VH = self.get_Hartree_potential(wavefunction) VH_matrix = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) VH_matrix = operator_matrix_periodic(VH_matrix, VH, self.ukn.conj(), self.ukn) * self.norm return VH_matrix def get_Fock_matrix(self, wavefunction=None): """ return numpy array [N_kpoint X N_band X N_band] of Fock potential matrix elements wavefunction: numpy array [N_kpoint X N_band X N_band] of wavefunction in basis of Kohn-Sham orbital """ VF_matrix = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) if self.Fock: if wavefunction is None: wavefunction = np.zeros((self.NK, self.nbands, self.nbands)) for k in range(self.NK): wavefunction[k] = np.eye(self.nbands) K = self.Full_BZ NK = K.shape[0] NG = self.pdF.get_reciprocal_vectors().shape[0] V = np.zeros((self.NK, NK, NG)) for k in range(self.NK): for q in range(NK): kq = K[q] - self.K[k] V[k, q] = self.get_coloumb_potential(kq) VF_matrix = Fock_matrix(VF_matrix, V, self.M.conj(), self.M, self.IBZ_map, self.nvalence) return VF_matrix / self.volume def get_LDA_exchange_matrix(self, wavefunction=None): """ return numpy array [N_kpoint X N_band X N_band] of LDA exchange potential matrix elements wavefunction: numpy array [N_kpoint X N_band X N_band] of wavefunction in basis of Kohn-Sham orbital """ density = self.get_density(wavefunction) exchange = xc.VLDAx(density) LDAx_matrix = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) LDAx_matrix = operator_matrix_periodic( LDAx_matrix, exchange, self.ukn.conj(), self.ukn) * self.norm return LDAx_matrix def get_LDA_correlation_matrix(self, wavefunction=None): """ return numpy array [N_kpoint X N_band X N_band] of LDA correlation potential matrix elements wavefunction: numpy array [N_kpoint X N_band X N_band] of wavefunction in basis of Kohn-Sham orbital """ density = self.get_density(wavefunction) correlation = xc.VLDAc(density) LDAc_matrix = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) LDAc_matrix = operator_matrix_periodic( LDAc_matrix, correlation, self.ukn.conj(), self.ukn) * self.norm return LDAc_matrix def occupation(self, wavefunction): return 2 * np.sum(self.wk[:, None, None] * self.f[:, None, :] * np.abs(wavefunction)**2, axis=2) def fast_Hartree_matrix(self, wavefunction): return np.einsum('kn,knqij->qij', self.occupation(wavefunction), self.Hartree_elements) - self.VH0 def fast_LDA_correlation_matrix(self, wavefunction): return np.einsum('kn,knqij->qij', self.occupation(wavefunction), self.LDAc_elements) - self.VLDAc0 def fast_LDA_exchange_matrix(self, wavefunction): return np.einsum('kn,knqij->qij', self.occupation(wavefunction), self.LDAx_elements) - self.VLDAx0 def propagate(self, dt, steps, E, operator, corrections=10): self.wavefunction = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) for k in range(self.NK): self.wavefunction[k] = np.eye(self.nbands) H = np.copy(self.Kinetic) + E[0] * self.dipole operator_macro = np.array( [operator[k].diagonal() for k in range(self.NK)]) self.macro_dipole = np.zeros(steps, dtype=np.complex) for t in tqdm(range(steps)): wavefunction_next = np.copy(self.wavefunction) error = np.inf while error > 1e-8: wavefunction_check = np.copy(wavefunction_next) H_next = self.Kinetic + E[t] * self.dipole H_next += self.fast_Hartree_matrix(wavefunction_next) H_next += self.fast_LDA_correlation_matrix(wavefunction_next) H_next += self.fast_LDA_exchange_matrix(wavefunction_next) H_mid = 0.5 * (H + H_next) for k in range(self.NK): H_left = np.eye(self.nbands) + 0.5j * dt * H_mid[k] H_right = np.eye(self.nbands) - 0.5j * dt * H_mid[k] wavefunction_next[k] = linalg.solve( H_left, H_right @ self.wavefunction[k]) error = np.abs(np.sum(wavefunction_next - wavefunction_check)) self.wavefunction = np.copy(wavefunction_next) H = np.copy(H_next) self.macro_dipole[t] = np.sum( self.occupation(self.wavefunction) * operator_macro)
from gpaw.kpt_descriptor import KPointDescriptor x = 2.0 rc = 3.5 r = np.linspace(0, rc, 100) n = 40 a = 8.0 gd = GridDescriptor((n, n, n), (a, a, a), comm=mpi.serial_comm) kpts = np.array([(0.25, 0.25, 0.0)]) kd = KPointDescriptor(kpts) spos_ac = np.array([(0.15, 0.5, 0.95)]) pd = PWDescriptor(45, gd, complex, kd) eikr = np.ascontiguousarray(np.exp(2j * np.pi * np.dot(np.indices(gd.N_c).T, (kpts / gd.N_c).T).T)[0]) from gpaw.fftw import FFTPlan print(FFTPlan) for l in range(3): print(l) s = Spline(l, rc, 2 * x**1.5 / np.pi * np.exp(-x * r**2)) lfc1 = LFC(gd, [[s]], kd, dtype=complex) lfc2 = PWLFC([[s]], pd) c_axi = {0: np.zeros((1, 2 * l + 1), complex)}
def write(self, calc, ecut=40 * Hartree, spacegroup=1): #sg = Spacegroup(spacegroup) #print sg wfs = calc.wfs setups = wfs.setups bd = wfs.bd kd = wfs.kd atoms = calc.atoms natoms = len(atoms) if wfs.symmetry is None: op_scc = np.eye(3, dtype=int).reshape((1, 3, 3)) else: op_scc = wfs.symmetry.op_scc pwd = PWDescriptor(ecut / Hartree, wfs.gd, kd.ibzk_kc) N_c = pwd.gd.N_c i_Qc = np.indices(N_c, np.int32).transpose((1, 2, 3, 0)) i_Qc += N_c // 2 i_Qc %= N_c i_Qc -= N_c // 2 i_Qc.shape = (-1, 3) i_Gc = i_Qc[pwd.Q_G] B_cv = 2.0 * np.pi * wfs.gd.icell_cv G_Qv = np.dot(i_Gc, B_cv).reshape((-1, 3)) G2_Q = (G_Qv**2).sum(axis=1) specie_a = np.empty(natoms, np.int32) nspecies = 0 species = {} names = [] symbols = [] numbers = [] charges = [] for a, id in enumerate(setups.id_a): if id not in species: species[id] = nspecies nspecies += 1 names.append(setups[a].symbol) symbols.append(setups[a].symbol) numbers.append(setups[a].Z) charges.append(setups[a].Nv) specie_a[a] = species[id] dimensions = [('character_string_length', 80), ('max_number_of_coefficients', len(i_Gc)), ('max_number_of_states', bd.nbands), ('number_of_atoms', len(atoms)), ('number_of_atom_species', nspecies), ('number_of_cartesian_directions', 3), ('number_of_components', 1), ('number_of_grid_points_vector1', N_c[0]), ('number_of_grid_points_vector2', N_c[1]), ('number_of_grid_points_vector3', N_c[2]), ('number_of_kpoints', kd.nibzkpts), ('number_of_reduced_dimensions', 3), ('number_of_spinor_components', 1), ('number_of_spins', wfs.nspins), ('number_of_symmetry_operations', len(op_scc)), ('number_of_vectors', 3), ('real_or_complex_coefficients', 2), ('symbol_length', 2)] for name, size in dimensions: print('%-34s %d' % (name, size)) self.nc.createDimension(name, size) var = self.add_variable var('space_group', (), np.array(spacegroup, dtype=int)) var('primitive_vectors', ('number_of_vectors', 'number_of_cartesian_directions'), wfs.gd.cell_cv, units='atomic units') var('reduced_symmetry_matrices', ('number_of_symmetry_operations', 'number_of_reduced_dimensions', 'number_of_reduced_dimensions'), op_scc.astype(np.int32), symmorphic='yes') var('reduced_symmetry_translations', ('number_of_symmetry_operations', 'number_of_reduced_dimensions'), np.zeros((len(op_scc), 3), dtype=np.int32)) var('atom_species', ('number_of_atoms', ), specie_a + 1) var('reduced_atom_positions', ('number_of_atoms', 'number_of_reduced_dimensions'), atoms.get_scaled_positions()) var('atomic_numbers', ('number_of_atom_species', ), np.array(numbers, dtype=float)) var('valence_charges', ('number_of_atom_species', ), np.array(charges, dtype=float)) var('atom_species_names', ('number_of_atom_species', 'character_string_length'), names) var('chemical_symbols', ('number_of_atom_species', 'symbol_length'), symbols) var('pseudopotential_types', ('number_of_atom_species', 'character_string_length'), ['HGH'] * nspecies) var('fermi_energy', (), calc.occupations.fermilevel, units='atomic units') var('smearing_scheme', ('character_string_length', ), 'fermi-dirac') var('smearing_width', (), calc.occupations.width, units='atomic units') var('number_of_states', ('number_of_spins', 'number_of_kpoints'), np.zeros((wfs.nspins, kd.nibzkpts), np.int32) + bd.nbands, k_dependent='no') var('eigenvalues', ('number_of_spins', 'number_of_kpoints', 'max_number_of_states'), np.array([[ calc.get_eigenvalues(k, s) / Hartree for k in range(kd.nibzkpts) ] for s in range(wfs.nspins)]), units='atomic units') var( 'occupations', ('number_of_spins', 'number_of_kpoints', 'max_number_of_states'), np.array([[ calc.get_occupation_numbers(k, s) / kd.weight_k[k] for k in range(kd.nibzkpts) ] for s in range(wfs.nspins)])) var('reduced_coordinates_of_kpoints', ('number_of_kpoints', 'number_of_reduced_dimensions'), kd.ibzk_kc) var('kpoint_weights', ('number_of_kpoints', ), kd.weight_k) var('basis_set', ('character_string_length', ), 'plane_waves') var('kinetic_energy_cutoff', (), 1.0 * ecut, units='atomic units') var('number_of_coefficients', ('number_of_kpoints', ), np.zeros(kd.nibzkpts, np.int32) + len(i_Gc), k_dependent='no') var('reduced_coordinates_of_plane_waves', ('max_number_of_coefficients', 'number_of_reduced_dimensions'), i_Gc[np.argsort(G2_Q)], k_dependent='no') var('number_of_electrons', (), np.array(wfs.nvalence, dtype=np.int32)) #var('exchange_functional', ('character_string_length',), # calc.hamiltonian.xc.name) #var('correlation_functional', ('character_string_length',), # calc.hamiltonian.xc.name) psit_skn1G2 = var( 'coefficients_of_wavefunctions', ('number_of_spins', 'number_of_kpoints', 'max_number_of_states', 'number_of_spinor_components', 'max_number_of_coefficients', 'real_or_complex_coefficients')) x = atoms.get_volume()**0.5 / N_c.prod() psit_Gx = np.empty((len(i_Gc), 2)) for s in range(wfs.nspins): for k in range(kd.nibzkpts): for n in range(bd.nbands): psit_G = pwd.fft(calc.get_pseudo_wave_function( n, k, s))[np.argsort(G2_Q)] psit_G *= x psit_Gx[:, 0] = psit_G.real psit_Gx[:, 1] = psit_G.imag psit_skn1G2[s, k, n, 0] = psit_Gx self.nc.close()
class HybridXC(XCFunctional): orbital_dependent = True def __init__( self, name, hybrid=None, xc=None, finegrid=False, alpha=None, skip_gamma=False, gygi=False, acdf=True, qsym=True, txt=None, ecut=None, ): """Mix standard functionals with exact exchange. name: str Name of hybrid functional. hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. finegrid: boolean Use fine grid for energy functional evaluations? """ if name == "EXX": assert hybrid is None and xc is None hybrid = 1.0 xc = XC(XCNull()) elif name == "PBE0": assert hybrid is None and xc is None hybrid = 0.25 xc = XC("HYB_GGA_XC_PBEH") elif name == "B3LYP": assert hybrid is None and xc is None hybrid = 0.2 xc = XC("HYB_GGA_XC_B3LYP") if isinstance(xc, str): xc = XC(xc) self.hybrid = hybrid self.xc = xc self.type = xc.type self.alpha = alpha self.qsym = qsym self.skip_gamma = skip_gamma self.gygi = gygi self.acdf = acdf self.exx = None self.ecut = ecut if txt is None: if rank == 0: # self.txt = devnull self.txt = sys.stdout else: sys.stdout = devnull self.txt = devnull else: assert type(txt) is str from ase.parallel import paropen self.txt = paropen(txt, "w") XCFunctional.__init__(self, name) def get_setup_name(self): return "PBE" def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp, addcoredensity, a) def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.wfs = wfs self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol ** (2 / 3.0) / pi ** 2 self.gamma = vol / (2 * pi) ** 2 * sqrt(pi / self.alpha) * self.kd.nbzkpts if self.ecut is None: self.ecut = 0.5 * pi ** 2 / (self.gd.h_cv ** 2).sum(1).max() * 0.9999 assert self.kd.N_c is not None n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.bzq_qc = self.kd.get_bz_q_points() if self.qsym: op_scc = self.kd.symmetry.op_scc self.ibzq_qc = self.kd.get_ibz_q_points(self.bzq_qc, op_scc)[0] self.q_weights = self.kd.q_weights * len(self.bzq_qc) else: self.ibzq_qc = self.bzq_qc self.q_weights = np.ones(len(self.bzq_qc)) self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): # XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G ** -1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:] ** -1) n += 1 assert n == self.kd.N_c.prod() self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.ibzq_qc) self.ghat = LFC( self.gd, [setup.ghat_l for setup in density.setups], KPointDescriptor(self.bzq_qc), dtype=complex ) # self.interpolator = density.interpolator self.print_initialization(hamiltonian.xc.name) def set_positions(self, spos_ac): self.ghat.set_positions(spos_ac) self.spos_ac = spos_ac def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx def calculate_exx(self): """Non-selfconsistent calculation.""" kd = self.kd K = len(kd.bzk_kc) W = world.size // self.nspins parallel = W > 1 self.exx = 0.0 self.exx_kq = np.zeros((K, len(self.ibzq_qc)), float) for s in range(self.nspins): ibz_kpts = [KPoint(kd, kpt) for kpt in self.kpt_u if kpt.s == s] for ik, kpt in enumerate(kd.bzk_kc): print >>self.txt, "K %s %s ..." % (ik, kpt) for iq, q in enumerate(self.ibzq_qc): kpq = kd.find_k_plus_q(q, kpts_k=[ik]) self.apply(ibz_kpts[kd.bz2ibz_k[ik]], ibz_kpts[kd.bz2ibz_k[kpq[0]]], ik, kpq[0], iq) self.exx = world.sum(self.exx) self.exx += self.calculate_exx_paw_correction() exx_q = np.sum(self.exx_kq, 0) print >>self.txt print >>self.txt, "------------------------------------------------------" print >>self.txt print >>self.txt, "Contributions: q w E_q (eV)" for q in range(len(exx_q)): print >>self.txt, "[%1.3f %1.3f %1.3f] %1.3f %s" % ( self.ibzq_qc[q][0], self.ibzq_qc[q][1], self.ibzq_qc[q][2], self.q_weights[q] / len(self.bzq_qc), exx_q[q] / self.q_weights[q] * len(self.bzq_qc) * Ha, ) print >>self.txt, "E_EXX = %s eV" % (self.exx * Ha) print >>self.txt print >>self.txt, "Calculation completed at: ", ctime() print >>self.txt print >>self.txt, "------------------------------------------------------" print >>self.txt def apply(self, kpt1, kpt2, ik1, ik2, iq): k1_c = self.kd.bzk_kc[ik1] k2_c = self.kd.bzk_kc[ik2] q = self.ibzq_qc[iq] if self.qsym: for i, q in enumerate(self.bzq_qc): if abs(q - self.ibzq_qc[iq]).max() < 1e-9: bzq_index = i break else: bzq_index = iq N_c = self.gd.N_c eikr_R = np.exp(-2j * pi * np.dot(np.indices(N_c).T, q / N_c).T) Gamma = abs(q).max() < 1e-9 if Gamma and self.skip_gamma: return Gpk2_G = self.G2_qG[iq] if Gamma: Gpk2_G = Gpk2_G.copy() Gpk2_G[0] = 1.0 / self.gamma N = N_c.prod() vol = self.gd.dv * N nspins = self.nspins fcut = 1e-10 for n1, psit1_R in enumerate(kpt1.psit_nG): f1 = kpt1.f_n[n1] for n2, psit2_R in enumerate(kpt2.psit_nG): if self.acdf: if self.gygi and Gamma: # print n2, kpt2.f_n[n2]/kpt2.weight f2 = self.q_weights[iq] * kpt2.weight else: f2 = self.q_weights[iq] * kpt2.weight * (1 - np.sign(kpt2.eps_n[n2] - kpt1.eps_n[n1])) else: f2 = kpt2.f_n[n2] * self.q_weights[iq] if abs(f1) < fcut or abs(f2) < fcut: continue nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, ik1, ik2, bzq_index) nt_G = self.pwd.fft(nt_R * eikr_R) / N vt_G = nt_G.copy() vt_G *= -pi * vol / Gpk2_G e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid self.exx += f1 * f2 * e self.exx_kq[ik1, iq] += f1 * f2 * e def calculate_pair_density(self, n1, n2, kpt1, kpt2, ik1, ik2, bzq_index): psit1_G = self.kd.transform_wave_function(kpt1.psit_nG[n1], ik1) psit2_G = self.kd.transform_wave_function(kpt2.psit_nG[n2], ik2) nt_G = psit1_G.conj() * psit2_G s1 = self.kd.sym_k[ik1] s2 = self.kd.sym_k[ik2] t1 = self.kd.time_reversal_k[ik1] t2 = self.kd.time_reversal_k[ik2] k1_c = self.kd.ibzk_kc[kpt1.k] k2_c = self.kd.ibzk_kc[kpt2.k] Q_aL = {} for a in kpt1.P_ani.keys(): b1 = self.kd.symmetry.a_sa[s1, a] b2 = self.kd.symmetry.a_sa[s2, a] S1_c = np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s1]) - self.spos_ac[b1] S2_c = np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s2]) - self.spos_ac[b2] assert abs(S1_c.round() - S1_c).max() < 1e-13 assert abs(S2_c.round() - S2_c).max() < 1e-13 x1 = np.exp(2j * pi * np.dot(k1_c, S1_c)) x2 = np.exp(2j * pi * np.dot(k2_c, S2_c)) P1_i = np.dot(self.setups[a].R_sii[s1], kpt1.P_ani[b1][n1]) * x1 P2_i = np.dot(self.setups[a].R_sii[s2], kpt2.P_ani[b2][n2]) * x2 if t1: P1_i = P1_i.conj() if t2: P2_i = P2_i.conj() D_ii = np.outer(P1_i.conj(), P2_i) D_p = pack(D_ii) Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) self.ghat.add(nt_G, Q_aL, bzq_index) return nt_G 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 print_initialization(self, xc): print >>self.txt, "------------------------------------------------------" print >>self.txt, "Non-self-consistent HF correlation energy" print >>self.txt, "------------------------------------------------------" print >>self.txt, "Started at: ", ctime() print >>self.txt print >>self.txt, "Ground state XC functional : %s" % xc print >>self.txt, "Valence electrons : %s" % self.setups.nvalence print >>self.txt, "Number of Spins : %s" % self.nspins print >>self.txt, "Plane wave cutoff energy : %4.1f eV" % (self.ecut * Ha) print >>self.txt, "Gamma q-point excluded : %s" % self.skip_gamma if not self.skip_gamma: print >>self.txt, "Alpha parameter : %s" % self.alpha print >>self.txt, "Gamma parameter : %3.3f" % self.gamma print >>self.txt, "ACDF method : %s" % self.acdf print >>self.txt, "Number of k-points : %s" % len(self.kd.bzk_kc) print >>self.txt, "Number of Irreducible k-points : %s" % len(self.kd.ibzk_kc) print >>self.txt, "Number of q-points : %s" % len(self.bzq_qc) if not self.qsym: print >>self.txt, "q-point symmetry : %s" % self.qsym else: print >>self.txt, "Number of Irreducible q-points : %s" % len(self.ibzq_qc) print >>self.txt for q, weight in zip(self.ibzq_qc, self.q_weights): print >>self.txt, "q: [%1.3f %1.3f %1.3f] - weight: %1.3f" % (q[0], q[1], q[2], weight / len(self.bzq_qc)) print >>self.txt print >>self.txt, "------------------------------------------------------" print >>self.txt, "------------------------------------------------------" print >>self.txt print >>self.txt, "Looping over k-points in the full Brillouin zone" print >>self.txt
def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.wfs = wfs self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol ** (2 / 3.0) / pi ** 2 self.gamma = vol / (2 * pi) ** 2 * sqrt(pi / self.alpha) * self.kd.nbzkpts if self.ecut is None: self.ecut = 0.5 * pi ** 2 / (self.gd.h_cv ** 2).sum(1).max() * 0.9999 assert self.kd.N_c is not None n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.bzq_qc = self.kd.get_bz_q_points() if self.qsym: op_scc = self.kd.symmetry.op_scc self.ibzq_qc = self.kd.get_ibz_q_points(self.bzq_qc, op_scc)[0] self.q_weights = self.kd.q_weights * len(self.bzq_qc) else: self.ibzq_qc = self.bzq_qc self.q_weights = np.ones(len(self.bzq_qc)) self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): # XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G ** -1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:] ** -1) n += 1 assert n == self.kd.N_c.prod() self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.ibzq_qc) self.ghat = LFC( self.gd, [setup.ghat_l for setup in density.setups], KPointDescriptor(self.bzq_qc), dtype=complex ) # self.interpolator = density.interpolator self.print_initialization(hamiltonian.xc.name)
from gpaw.lfc import LocalizedFunctionsCollection as LFC from gpaw.wavefunctions.pw import PWDescriptor, PWLFC from gpaw.kpt_descriptor import KPointDescriptor x = 2.0 rc = 3.5 r = np.linspace(0, rc, 100) n = 40 a = 8.0 gd = GridDescriptor((n, n, n), (a, a, a), comm=mpi.serial_comm) spos_ac = np.array([(0.15, 0.45, 0.95)]) pd = PWDescriptor(45, gd, complex) pdr = PWDescriptor(45, gd) from gpaw.fftw import FFTPlan print(FFTPlan) for l in range(4): print(l) s = Spline(l, rc, 2 * x**1.5 / np.pi * np.exp(-x * r**2)) lfc = PWLFC([[s]], pd) lfcr = PWLFC([[s]], pdr) c_axi = {0: np.zeros((1, 2 * l + 1), complex)} c_axi[0][0, 0] = 1.9 cr_axi = {0: np.zeros((1, 2 * l + 1))}
def __init__(self, calc, nbands=None, Fock=False): self.calc = calc self.Fock = Fock self.K = calc.get_ibz_k_points() # reduced Brillioun zone self.NK = self.K.shape[0] self.wk = calc.get_k_point_weights( ) # weight of reduced Brillioun zone if nbands is None: self.nbands = calc.get_number_of_bands() else: self.nbands = nbands self.nvalence = int(calc.get_number_of_electrons() / 2) self.EK = [ calc.get_eigenvalues(k)[:self.nbands] for k in range(self.NK) ] # bands energy self.EK = np.array(self.EK) / Hartree self.shape = tuple( calc.get_number_of_grid_points()) # shape of real space grid self.density = calc.get_pseudo_density( ) * Bohr**3 # density at zero time # array of u_nk (periodic part of Kohn-Sham orbitals,only reduced Brillion zone) self.ukn = np.zeros(( self.NK, self.nbands, ) + self.shape, dtype=np.complex) for k in range(self.NK): kpt = calc.wfs.kpt_u[k] for n in range(self.nbands): psit_G = kpt.psit_nG[n] psit_R = calc.wfs.pd.ifft(psit_G, kpt.q) self.ukn[k, n] = psit_R self.icell = 2.0 * np.pi * calc.wfs.gd.icell_cv # inverse cell self.cell = calc.wfs.gd.cell_cv # cell self.r = calc.wfs.gd.get_grid_point_coordinates() for i in range(3): self.r[i] -= self.cell[i, i] / 2. self.volume = np.abs(np.linalg.det( calc.wfs.gd.cell_cv)) # volume of cell self.norm = calc.wfs.gd.dv # self.Fermi = calc.get_fermi_level() / Hartree #Fermi level #desriptors at q=gamma for Hartree self.kdH = KPointDescriptor([[0, 0, 0]]) self.pdH = PWDescriptor(ecut=calc.wfs.pd.ecut, gd=calc.wfs.gd, kd=self.kdH, dtype=complex) #desriptors at q=gamma for Fock self.kdF = KPointDescriptor([[0, 0, 0]]) self.pdF = PWDescriptor(ecut=calc.wfs.pd.ecut / 4., gd=calc.wfs.gd, kd=self.kdF, dtype=complex) #Fermi-Dirac temperature self.temperature = calc.occupations.width #calculate pair-density matrices if Fock: self.M = np.zeros((self.nbands, self.nbands, self.NK, self.NK, self.pdF.get_reciprocal_vectors().shape[0]), dtype=np.complex) indexes = [(n, k) for n, k in product(range(self.nbands), range(self.NK))] for i1 in range(len(indexes)): n1, k1 = indexes[i1] for i2 in range(i1, len(indexes)): n2, k2 = indexes[i1] self.M[n1, n2, k1, k2] = self.pdF.fft( self.ukn[k1, n1].conj() * self.ukn[k2, n2]) self.M[n2, n1, k2, k1] = self.M[n1, n2, k1, k2].conj() self.M *= calc.wfs.gd.dv #Fermi-Dirac distribution self.f = 1 / (1 + np.exp((self.EK - self.Fermi) / self.temperature)) self.Hartree_elements = np.zeros( (self.NK, self.nbands, self.NK, self.nbands, self.nbands), dtype=np.complex) self.LDAx_elements = np.zeros( (self.NK, self.nbands, self.NK, self.nbands, self.nbands), dtype=np.complex) self.LDAc_elements = np.zeros( (self.NK, self.nbands, self.NK, self.nbands, self.nbands), dtype=np.complex) G = self.pdH.get_reciprocal_vectors() G2 = np.linalg.norm(G, axis=1)**2 G2[G2 == 0] = np.inf matrix = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) for k in tqdm(range(self.NK)): for n in range(self.nbands): density = 2 * np.abs(self.ukn[k, n])**2 operator = xc.VLDAx(density) self.LDAx_elements[k, n] = operator_matrix_periodic( matrix, operator, self.ukn.conj(), self.ukn) * self.norm operator = xc.VLDAc(density) self.LDAc_elements[k, n] = operator_matrix_periodic( matrix, operator, self.ukn.conj(), self.ukn) * self.norm density = self.pdH.fft(density) operator = 4 * np.pi * self.pdH.ifft(density / G2) self.Hartree_elements[k, n] = operator_matrix_periodic( matrix, operator, self.ukn.conj(), self.ukn) * self.norm self.wavefunction = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) self.Kinetic = np.zeros((self.NK, self.nbands, self.nbands), dtype=np.complex) self.dipole = self.get_dipole_matrix() for k in range(self.NK): self.wavefunction[k] = np.eye(self.nbands) self.Kinetic[k] = np.diag(self.EK[k]) self.VH0 = self.get_Hartree_matrix(self.wavefunction) self.VLDAc0 = self.get_LDA_correlation_matrix(self.wavefunction) self.VLDAx0 = self.get_LDA_exchange_matrix(self.wavefunction) self.Full_BZ = calc.get_bz_k_points() self.IBZ_map = calc.get_bz_to_ibz_map()
def calculate_screened_potential(self, ac): """Calculate W_GG(q)""" chi0 = Chi0(self.calc, frequencies=[0.0], eta=0.001, ecut=self.ecut, intraband=False, hilbert=False, nbands=self.nbands, txt='chi0.txt', world=world, ) self.blockcomm = chi0.blockcomm wfs = self.calc.wfs self.Q_qaGii = [] self.W_qGG = [] self.pd_q = [] t0 = time() print('Calculating screened potential', file=self.fd) for iq, q_c in enumerate(self.qd.ibzk_kc): thisqd = KPointDescriptor([q_c]) pd = PWDescriptor(self.ecut, wfs.gd, complex, thisqd) nG = pd.ngmax chi0.Ga = self.blockcomm.rank * nG chi0.Gb = min(chi0.Ga + nG, nG) chi0_wGG = np.zeros((1, nG, nG), complex) if np.allclose(q_c, 0.0): chi0_wxvG = np.zeros((1, 2, 3, nG), complex) chi0_wvv = np.zeros((1, 3, 3), complex) else: chi0_wxvG = None chi0_wvv = None chi0._calculate(pd, chi0_wGG, chi0_wxvG, chi0_wvv, 0, self.nbands, spins='all', extend_head=False) chi0_GG = chi0_wGG[0] # Calculate eps^{-1}_GG if pd.kd.gamma: # Generate fine grid in vicinity of gamma kd = self.calc.wfs.kd N = 4 N_c = np.array([N, N, N]) if self.truncation is not None: # Only average periodic directions if trunction is used N_c[kd.N_c == 1] = 1 qf_qc = monkhorst_pack(N_c) / kd.N_c qf_qc *= 1.0e-6 U_scc = kd.symmetry.op_scc qf_qc = kd.get_ibz_q_points(qf_qc, U_scc)[0] weight_q = kd.q_weights qf_qv = 2 * np.pi * np.dot(qf_qc, pd.gd.icell_cv) a_q = np.sum(np.dot(chi0_wvv[0], qf_qv.T) * qf_qv.T, axis=0) a0_qG = np.dot(qf_qv, chi0_wxvG[0, 0]) a1_qG = np.dot(qf_qv, chi0_wxvG[0, 1]) einv_GG = np.zeros((nG, nG), complex) # W_GG = np.zeros((nG, nG), complex) for iqf in range(len(qf_qv)): chi0_GG[0] = a0_qG[iqf] chi0_GG[:, 0] = a1_qG[iqf] chi0_GG[0, 0] = a_q[iqf] sqrV_G = get_coulomb_kernel(pd, kd.N_c, truncation=self.truncation, wstc=self.wstc, q_v=qf_qv[iqf])**0.5 sqrV_G *= ac**0.5 # Multiply by adiabatic coupling e_GG = np.eye(nG) - chi0_GG * sqrV_G * sqrV_G[:, np.newaxis] einv_GG += np.linalg.inv(e_GG) * weight_q[iqf] # einv_GG = np.linalg.inv(e_GG) * weight_q[iqf] # W_GG += (einv_GG * sqrV_G * sqrV_G[:, np.newaxis] # * weight_q[iqf]) else: sqrV_G = get_coulomb_kernel(pd, self.kd.N_c, truncation=self.truncation, wstc=self.wstc)**0.5 sqrV_G *= ac**0.5 # Multiply by adiabatic coupling e_GG = np.eye(nG) - chi0_GG * sqrV_G * sqrV_G[:, np.newaxis] einv_GG = np.linalg.inv(e_GG) # W_GG = einv_GG * sqrV_G * sqrV_G[:, np.newaxis] # Now calculate W_GG if pd.kd.gamma: # Reset bare Coulomb interaction sqrV_G = get_coulomb_kernel(pd, self.kd.N_c, truncation=self.truncation, wstc=self.wstc)**0.5 W_GG = einv_GG * sqrV_G * sqrV_G[:, np.newaxis] if self.integrate_gamma != 0: # Numerical integration of Coulomb interaction at all q-points if self.integrate_gamma == 2: reduced = True else: reduced = False V0, sqrV0 = get_integrated_kernel(pd, self.kd.N_c, truncation=self.truncation, reduced=reduced, N=100) W_GG[0, 0] = einv_GG[0, 0] * V0 W_GG[0, 1:] = einv_GG[0, 1:] * sqrV0 * sqrV_G[1:] W_GG[1:, 0] = einv_GG[1:, 0] * sqrV_G[1:] * sqrV0 elif self.integrate_gamma == 0 and pd.kd.gamma: # Analytical integration at gamma bzvol = (2 * np.pi)**3 / self.vol / self.qd.nbzkpts Rq0 = (3 * bzvol / (4 * np.pi))**(1. / 3.) V0 = 16 * np.pi**2 * Rq0 / bzvol sqrV0 = (4 * np.pi)**(1.5) * Rq0**2 / bzvol / 2 W_GG[0, 0] = einv_GG[0, 0] * V0 W_GG[0, 1:] = einv_GG[0, 1:] * sqrV0 * sqrV_G[1:] W_GG[1:, 0] = einv_GG[1:, 0] * sqrV_G[1:] * sqrV0 else: pass if pd.kd.gamma: e = 1 / einv_GG[0, 0].real print(' RPA dielectric constant is: %3.3f' % e, file=self.fd) self.Q_qaGii.append(chi0.Q_aGii) self.pd_q.append(pd) self.W_qGG.append(W_GG) if iq % (self.qd.nibzkpts // 5 + 1) == 2: dt = time() - t0 tleft = dt * self.qd.nibzkpts / (iq + 1) - dt print(' Finished %s q-points in %s - Estimated %s left' % (iq + 1, timedelta(seconds=round(dt)), timedelta(seconds=round(tleft))), file=self.fd)
bG_v = np.dot(bG_c, icell_cv) u2_nG = u_knG[ik2] * np.exp( -1j * np.inner(r_g.T, bG_v).T) S[ik1, i, j, :, :] = get_overlap( calc, bands, np.reshape(u1_nG, (len(u1_nG), Ng)), np.reshape(u2_nG, (len(u2_nG), Ng)), P_kani[ik1], P_kani[ik2], dO_aii, bG_v)[:nb, :nb] np.save(output_directory + case + ".gpaw.S.npy", S) pair = PairDensity(calc=calc) momentum = np.zeros((nkpts, nb, nb, 3), dtype=np.complex) for i in range(nkpts): #k = b1*calc.wfs.kd.bzk_kc[i][0] + b2*calc.wfs.kd.bzk_kc[i][1] +b3*calc.wfs.kd.bzk_kc[i][2] q_c = [0.0, 0.0, 0.0] qd = KPointDescriptor([q_c]) pd = PWDescriptor(pair.ecut, calc.wfs.gd, complex, qd) kptpair = pair.get_kpoint_pair(pd, s=0, K=i, n1=0, n2=nb, m1=0, m2=nb) ol = np.allclose(q_c, 0.0) n_nmvG = pair.get_pair_momentum(pd, kptpair, np.arange(0, nb), np.arange(0, nb)) momentum[i, :, :, :] = n_nmvG[..., 0][:nb, :nb, :] np.save(output_directory + case + ".gpaw.momentum.npy", momentum) hdf5 = h5py.File(output_directory.rstrip('/') + '.hdf5', 'w') dset_energy = hdf5.create_dataset("energy", data=energy)
class TDDFT(object): """ Time-dependent DFT+Hartree-Fock in Kohn-Sham orbitals basis: calc: GPAW calculator (setups='sg15') nbands (int): number of bands in calculation """ def __init__(self,calc,nbands=None): self.calc=calc self.K=calc.get_ibz_k_points() # reduced Brillioun zone self.NK=self.K.shape[0] self.wk=calc.get_k_point_weights() # weight of reduced Brillioun zone if nbands is None: self.nbands=calc.get_number_of_bands() else: self.nbands=nbands self.nvalence=int(calc.get_number_of_electrons()/2) self.EK=[calc.get_eigenvalues(k)[:self.nbands] for k in range(self.NK)] # bands energy self.EK=np.array(self.EK)/Hartree self.shape=tuple(calc.get_number_of_grid_points()) # shape of real space grid self.density=calc.get_pseudo_density()*Bohr**3 # density at zero time # array of u_nk (periodic part of Kohn-Sham orbitals,only reduced Brillion zone) self.ukn=np.zeros((self.NK,self.nbands,)+self.shape,dtype=np.complex) for k in range(self.NK): kpt = calc.wfs.kpt_u[k] for n in range(self.nbands): psit_G = kpt.psit_nG[n] psit_R = calc.wfs.pd.ifft(psit_G, kpt.q) self.ukn[k,n]=psit_R self.icell=2.0 * np.pi * calc.wfs.gd.icell_cv # inverse cell self.cell = calc.wfs.gd.cell_cv # cell self.r=calc.wfs.gd.get_grid_point_coordinates() for i in range(3): self.r[i]-=self.cell[i,i]/2. self.volume = np.abs(np.linalg.det(calc.wfs.gd.cell_cv)) # volume of cell self.norm=calc.wfs.gd.dv # self.Fermi=calc.get_fermi_level()/Hartree #Fermi level #desriptors at q=gamma for Hartree self.kd=KPointDescriptor([[0,0,0]]) self.pd=PWDescriptor(ecut=calc.wfs.pd.ecut,gd=calc.wfs.gd,kd=self.kd,dtype=complex) #Fermi-Dirac temperature self.temperature=calc.occupations.width #Fermi-Dirac distribution self.f=1/(1+np.exp((self.EK-self.Fermi)/self.temperature)) self.Hartree_elements=np.zeros((self.NK,self.nbands,self.NK,self.nbands,self.nbands),dtype=np.complex) self.LDAx_elements=np.zeros((self.NK,self.nbands,self.NK,self.nbands,self.nbands),dtype=np.complex) self.LDAc_elements=np.zeros((self.NK,self.nbands,self.NK,self.nbands,self.nbands),dtype=np.complex) G=self.pd.get_reciprocal_vectors() G2=np.linalg.norm(G,axis=1)**2;G2[G2==0]=np.inf matrix=np.zeros((self.NK,self.nbands,self.nbands),dtype=np.complex) for k in tqdm(range(self.NK)): for n in range(self.nbands): density=self.norm*np.abs(self.ukn[k,n])**2 operator=xc.VLDAx(density) self.LDAx_elements[k,n]=operator_matrix_periodic(matrix,operator,self.ukn.conj(),self.ukn)*self.norm operator=xc.VLDAc(density) self.LDAc_elements[k,n]=operator_matrix_periodic(matrix,operator,self.ukn.conj(),self.ukn)*self.norm density=self.pd.fft(density) operator=4*np.pi*self.pd.ifft(density/G2) self.Hartree_elements[k,n]=operator_matrix_periodic(matrix,operator,self.ukn.conj(),self.ukn)*self.norm self.wavefunction=np.zeros((self.NK,self.nbands,self.nbands),dtype=np.complex) self.Kinetic=np.zeros((self.NK,self.nbands,self.nbands),dtype=np.complex) for k in range(self.NK): self.wavefunction[k]=np.eye(self.nbands) self.Kinetic[k]=np.diag(self.EK[k]) self.VH0=np.einsum('kn,knqij->qij',self.occupation(self.wavefunction),self.Hartree_elements) self.VLDAc0=np.einsum('kn,knqij->qij',self.occupation(self.wavefunction),self.LDAc_elements) self.VLDAx0=np.einsum('kn,knqij->qij',self.occupation(self.wavefunction),self.LDAx_elements) self.Full_BZ=calc.get_bz_k_points() self.IBZ_map=calc.get_bz_to_ibz_map() def get_transition_matrix(self,direction): direction/=np.linalg.norm(direction) self.dipole=np.zeros((self.NK,self.nbands,self.nbands),dtype=np.complex) for k in range(self.NK): kpt = self.calc.wfs.kpt_u[k] G=self.calc.wfs.pd.get_reciprocal_vectors(q=k,add_q=True) G=np.sum(G*direction[None,:],axis=1) for n in range(self.nvalence): for m in range(self.nvalence,self.nbands): wfn=kpt.psit_nG[n];wfm=kpt.psit_nG[m] self.dipole[k,n,m]=self.calc.wfs.pd.integrate(wfm,G*wfn)/(self.EK[k,n]-self.EK[k,m]) self.dipole[k,m,n]=self.dipole[k,n,m].conj() return self.dipole def occupation(self,wavefunction): return 2*np.sum(self.wk[:,None,None]*self.f[:,None,:]*np.abs(wavefunction)**2,axis=2) def fast_Hartree_matrix(self,wavefunction): return np.einsum('kn,knqij->qij',self.occupation(wavefunction),self.Hartree_elements)-self.VH0 def fast_LDA_correlation_matrix(self,wavefunction): return np.einsum('kn,knqij->qij',self.occupation(wavefunction),self.LDAc_elements)-self.VLDAc0 def fast_LDA_exchange_matrix(self,wavefunction): return np.einsum('kn,knqij->qij',self.occupation(wavefunction),self.LDAx_elements)-self.VLDAx0 def propagate(self,dt,steps,E,direction,corrections=10): dipole=self.get_transition_matrix(direction) self.time_occupation=np.zeros((steps,self.nbands),dtype=np.complex) self.polarization=np.zeros(steps,dtype=np.complex) self.time_occupation[0]=np.sum(self.occupation(self.wavefunction),axis=0) for k in range(self.NK): operator=np.linalg.multi_dot([self.wavefunction[k].T.conj(),dipole[k],self.wavefunction[k]]) self.polarization[0]+=self.wk[k]*np.sum(operator.diagonal()) for t in tqdm(range(1,steps)): H = self.Kinetic+E[t]*self.dipole H+= self.fast_Hartree_matrix(self.wavefunction) H+= self.fast_LDA_correlation_matrix(self.wavefunction) H+= self.fast_LDA_exchange_matrix(self.wavefunction) for k in range(self.NK): H_left = np.eye(self.nbands)+0.5j*dt*H[k] H_right= np.eye(self.nbands)-0.5j*dt*H[k] self.wavefunction[k]=linalg.solve(H_left, [email protected][k]) operator=np.linalg.multi_dot([self.wavefunction[k].T.conj(),dipole[k],self.wavefunction[k]]) self.polarization[t]+=self.wk[k]*np.sum(operator.diagonal()) self.time_occupation[t]=np.sum(self.occupation(self.wavefunction),axis=0)
a2[R2] = 1 y = pd2.restrict(a2, pd1)[0][R1] * a2.size / a1.size equal(x, y, 1e-9) return x if world.size == 1: for size1, size2 in [[(3, 3, 3), (8, 8, 8)], [(4, 4, 4), (9, 9, 9)], [(2, 4, 4), (5, 9, 9)], [(2, 3, 4), (5, 6, 9)], [(2, 3, 4), (5, 6, 8)], [(4, 4, 4), (8, 8, 8)], [(2, 4, 4), (4, 8, 8)], [(2, 4, 2), (4, 8, 4)]]: print(size1, size2) gd1 = GridDescriptor(size1, size1) gd2 = GridDescriptor(size2, size1) pd1 = PWDescriptor(1, gd1, complex) pd2 = PWDescriptor(1, gd2, complex) pd1r = PWDescriptor(1, gd1) pd2r = PWDescriptor(1, gd2) for R1, R2 in [[(0, 0, 0), (0, 0, 0)], [(0, 0, 0), (0, 0, 1)]]: x = test(gd1, gd2, pd1, pd2, R1, R2) y = test(gd1, gd2, pd1r, pd2r, R1, R2) equal(x, y, 1e-9) a1 = np.random.random(size1) a2 = pd1r.interpolate(a1, pd2r)[0] c2 = pd1.interpolate(a1 + 0.0j, pd2)[0] d2 = pd1.interpolate(a1 * 1.0j, pd2)[0] equal(abs(c2.imag).max(), 0, 1e-14) equal(abs(d2.real).max(), 0, 1e-14) equal(gd1.integrate(a1), gd2.integrate(a2), 1e-13)
class HybridXC(XCFunctional): orbital_dependent = True def __init__(self, name, hybrid=None, xc=None, finegrid=False, alpha=None, skip_gamma=False, gygi=False, acdf=True, qsym=True, txt=None, ecut=None): """Mix standard functionals with exact exchange. name: str Name of hybrid functional. hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. finegrid: boolean Use fine grid for energy functional evaluations? """ if name == 'EXX': assert hybrid is None and xc is None hybrid = 1.0 xc = XC(XCNull()) elif name == 'PBE0': assert hybrid is None and xc is None hybrid = 0.25 xc = XC('HYB_GGA_XC_PBEH') elif name == 'B3LYP': assert hybrid is None and xc is None hybrid = 0.2 xc = XC('HYB_GGA_XC_B3LYP') if isinstance(xc, str): xc = XC(xc) self.hybrid = hybrid self.xc = xc self.type = xc.type self.alpha = alpha self.qsym = qsym self.skip_gamma = skip_gamma self.gygi = gygi self.acdf = acdf self.exx = None self.ecut = ecut if txt is None: if rank == 0: #self.txt = devnull self.txt = sys.stdout else: sys.stdout = devnull self.txt = devnull else: assert type(txt) is str from ase.parallel import paropen self.txt = paropen(txt, 'w') XCFunctional.__init__(self, name) def get_setup_name(self): return 'PBE' def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp, addcoredensity, a) def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.wfs = wfs self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol**(2 / 3.0) / pi**2 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) if self.ecut is None: self.ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() * 0.9999 assert self.kd.N_c is not None n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.bzq_qc = self.kd.get_bz_q_points() if self.qsym: op_scc = self.kd.symmetry.op_scc self.ibzq_qc = self.kd.get_ibz_q_points(self.bzq_qc, op_scc)[0] self.q_weights = self.kd.q_weights * len(self.bzq_qc) else: self.ibzq_qc = self.bzq_qc self.q_weights = np.ones(len(self.bzq_qc)) self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G**-1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:]**-1) n += 1 assert n == self.kd.N_c.prod() self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.ibzq_qc) self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], KPointDescriptor(self.bzq_qc), dtype=complex) #self.interpolator = density.interpolator self.print_initialization(hamiltonian.xc.name) def set_positions(self, spos_ac): self.ghat.set_positions(spos_ac) self.spos_ac = spos_ac def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx def calculate_exx(self): """Non-selfconsistent calculation.""" kd = self.kd K = len(kd.bzk_kc) W = world.size // self.nspins parallel = (W > 1) self.exx = 0.0 self.exx_kq = np.zeros((K, len(self.ibzq_qc)), float) for s in range(self.nspins): ibz_kpts = [KPoint(kd, kpt) for kpt in self.kpt_u if kpt.s == s] for ik, kpt in enumerate(kd.bzk_kc): print('K %s %s ...' % (ik, kpt), file=self.txt) for iq, q in enumerate(self.ibzq_qc): kpq = kd.find_k_plus_q(q, kpts_k=[ik]) self.apply(ibz_kpts[kd.bz2ibz_k[ik]], ibz_kpts[kd.bz2ibz_k[kpq[0]]], ik, kpq[0], iq) self.exx = world.sum(self.exx) self.exx += self.calculate_exx_paw_correction() exx_q = np.sum(self.exx_kq, 0) print(file=self.txt) print('------------------------------------------------------', file=self.txt) print(file=self.txt) print('Contributions: q w E_q (eV)', file=self.txt) for q in range(len(exx_q)): print('[%1.3f %1.3f %1.3f] %1.3f %s' % \ (self.ibzq_qc[q][0], self.ibzq_qc[q][1], self.ibzq_qc[q][2], self.q_weights[q]/len(self.bzq_qc), exx_q[q]/self.q_weights[q]*len(self.bzq_qc)*Ha), file=self.txt) print('E_EXX = %s eV' % (self.exx * Ha), file=self.txt) print(file=self.txt) print('Calculation completed at: ', ctime(), file=self.txt) print(file=self.txt) print('------------------------------------------------------', file=self.txt) print(file=self.txt) def apply(self, kpt1, kpt2, ik1, ik2, iq): k1_c = self.kd.bzk_kc[ik1] k2_c = self.kd.bzk_kc[ik2] q = self.ibzq_qc[iq] if self.qsym: for i, q in enumerate(self.bzq_qc): if abs(q - self.ibzq_qc[iq]).max() < 1e-9: bzq_index = i break else: bzq_index = iq N_c = self.gd.N_c eikr_R = np.exp(-2j * pi * np.dot(np.indices(N_c).T, q / N_c).T) Gamma = abs(q).max() < 1e-9 if Gamma and self.skip_gamma: return Gpk2_G = self.G2_qG[iq] if Gamma: Gpk2_G = Gpk2_G.copy() Gpk2_G[0] = 1.0 / self.gamma N = N_c.prod() vol = self.gd.dv * N nspins = self.nspins fcut = 1e-10 for n1, psit1_R in enumerate(kpt1.psit_nG): f1 = kpt1.f_n[n1] for n2, psit2_R in enumerate(kpt2.psit_nG): if self.acdf: if self.gygi and Gamma: #print n2, kpt2.f_n[n2]/kpt2.weight f2 = (self.q_weights[iq] * kpt2.weight) else: f2 = (self.q_weights[iq] * kpt2.weight * (1 - np.sign(kpt2.eps_n[n2] - kpt1.eps_n[n1]))) else: f2 = kpt2.f_n[n2] * self.q_weights[iq] if abs(f1) < fcut or abs(f2) < fcut: continue nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, ik1, ik2, bzq_index) nt_G = self.pwd.fft(nt_R * eikr_R) / N vt_G = nt_G.copy() vt_G *= -pi * vol / Gpk2_G e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid self.exx += f1 * f2 * e self.exx_kq[ik1, iq] += f1 * f2 * e def calculate_pair_density(self, n1, n2, kpt1, kpt2, ik1, ik2, bzq_index): psit1_G = self.kd.transform_wave_function(kpt1.psit_nG[n1], ik1) psit2_G = self.kd.transform_wave_function(kpt2.psit_nG[n2], ik2) nt_G = psit1_G.conj() * psit2_G s1 = self.kd.sym_k[ik1] s2 = self.kd.sym_k[ik2] t1 = self.kd.time_reversal_k[ik1] t2 = self.kd.time_reversal_k[ik2] k1_c = self.kd.ibzk_kc[kpt1.k] k2_c = self.kd.ibzk_kc[kpt2.k] Q_aL = {} for a in kpt1.P_ani.keys(): b1 = self.kd.symmetry.a_sa[s1, a] b2 = self.kd.symmetry.a_sa[s2, a] S1_c = (np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s1]) - self.spos_ac[b1]) S2_c = (np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s2]) - self.spos_ac[b2]) assert abs(S1_c.round() - S1_c).max() < 1e-13 assert abs(S2_c.round() - S2_c).max() < 1e-13 x1 = np.exp(2j * pi * np.dot(k1_c, S1_c)) x2 = np.exp(2j * pi * np.dot(k2_c, S2_c)) P1_i = np.dot(self.setups[a].R_sii[s1], kpt1.P_ani[b1][n1]) * x1 P2_i = np.dot(self.setups[a].R_sii[s2], kpt2.P_ani[b2][n2]) * x2 if t1: P1_i = P1_i.conj() if t2: P2_i = P2_i.conj() D_ii = np.outer(P1_i.conj(), P2_i) D_p = pack(D_ii) Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) self.ghat.add(nt_G, Q_aL, bzq_index) return nt_G 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 print_initialization(self, xc): print('------------------------------------------------------', file=self.txt) print('Non-self-consistent HF correlation energy', file=self.txt) print('------------------------------------------------------', file=self.txt) print('Started at: ', ctime(), file=self.txt) print(file=self.txt) print('Ground state XC functional : %s' % xc, file=self.txt) print('Valence electrons : %s' % self.setups.nvalence, file=self.txt) print('Number of Spins : %s' % self.nspins, file=self.txt) print('Plane wave cutoff energy : %4.1f eV' % (self.ecut * Ha), file=self.txt) print('Gamma q-point excluded : %s' % self.skip_gamma, file=self.txt) if not self.skip_gamma: print('Alpha parameter : %s' % self.alpha, file=self.txt) print('Gamma parameter : %3.3f' % self.gamma, file=self.txt) print('ACDF method : %s' % self.acdf, file=self.txt) print('Number of k-points : %s' % len(self.kd.bzk_kc), file=self.txt) print('Number of Irreducible k-points : %s' % len(self.kd.ibzk_kc), file=self.txt) print('Number of q-points : %s' % len(self.bzq_qc), file=self.txt) if not self.qsym: print('q-point symmetry : %s' % self.qsym, file=self.txt) else: print('Number of Irreducible q-points : %s' % len(self.ibzq_qc), file=self.txt) print(file=self.txt) for q, weight in zip(self.ibzq_qc, self.q_weights): print('q: [%1.3f %1.3f %1.3f] - weight: %1.3f' % \ (q[0],q[1],q[2], weight/len(self.bzq_qc)), file=self.txt) print(file=self.txt) print('------------------------------------------------------', file=self.txt) print('------------------------------------------------------', file=self.txt) print(file=self.txt) print('Looping over k-points in the full Brillouin zone', file=self.txt) print(file=self.txt)
class HybridXC(HybridXCBase): orbital_dependent = True def __init__(self, name, hybrid=None, xc=None, alpha=None, gamma_point=1, method='standard', bandstructure=False, logfilename='-', bands=None, fcut=1e-10, molecule=False, qstride=1, world=None): """Mix standard functionals with exact exchange. name: str Name of functional: EXX, PBE0, HSE03, HSE06 hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. method: str Use 'standard' standard formula and 'acdf for adiabatic-connection dissipation fluctuation formula. alpha: float XXX describe gamma_point: bool 0: Skip k2-k1=0 interactions. 1: Use the alpha method. 2: Integrate the gamma point. bandstructure: bool Calculate bandstructure instead of just the total energy. bands: list of int List of bands to calculate bandstructure for. Default is all bands. molecule: bool Decouple electrostatic interactions between periodically repeated images. fcut: float Threshold for empty band. """ self.alpha = alpha self.fcut = fcut self.gamma_point = gamma_point self.method = method self.bandstructure = bandstructure self.bands = bands self.fd = logfilename self.write_timing_information = True HybridXCBase.__init__(self, name, hybrid, xc) # EXX energies: self.exx = None # total self.evv = None # valence-valence (pseudo part) self.evvacdf = None # valence-valence (pseudo part) self.devv = None # valence-valence (PAW correction) self.evc = None # valence-core self.ecc = None # core-core self.exx_skn = None # bandstructure self.qlatest = None if world is None: world = mpi.world self.world = world self.molecule = molecule if isinstance(qstride, int): qstride = [qstride] * 3 self.qstride_c = np.asarray(qstride) self.timer = Timer() def log(self, *args, **kwargs): prnt(file=self.fd, *args, **kwargs) self.fd.flush() def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp, addcoredensity, a) def initialize(self, dens, ham, wfs, occupations): assert wfs.bd.comm.size == 1 self.xc.initialize(dens, ham, wfs, occupations) self.dens = dens self.wfs = wfs # Make a k-point descriptor that is not distributed # (self.kd.comm is serial_comm): self.kd = wfs.kd.copy() self.fd = logfile(self.fd, self.world.rank) wfs.initialize_wave_functions_from_restart_file() def set_positions(self, spos_ac): self.spos_ac = spos_ac def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx * self.hybrid def calculate_exx(self): """Non-selfconsistent calculation.""" self.timer.start('EXX') self.timer.start('Initialization') kd = self.kd wfs = self.wfs if fftw.FFTPlan is fftw.NumpyFFTPlan: self.log('NOT USING FFTW !!') self.log('Spins:', self.wfs.nspins) W = max(1, self.wfs.kd.comm.size // self.wfs.nspins) # Are the k-points distributed? kparallel = (W > 1) # Find number of occupied bands: self.nocc_sk = np.zeros((self.wfs.nspins, kd.nibzkpts), int) for kpt in self.wfs.kpt_u: for n, f in enumerate(kpt.f_n): if abs(f) < self.fcut: self.nocc_sk[kpt.s, kpt.k] = n break else: self.nocc_sk[kpt.s, kpt.k] = self.wfs.bd.nbands self.wfs.kd.comm.sum(self.nocc_sk) noccmin = self.nocc_sk.min() noccmax = self.nocc_sk.max() self.log('Number of occupied bands (min, max): %d, %d' % (noccmin, noccmax)) self.log('Number of valence electrons:', self.wfs.setups.nvalence) if self.bandstructure: self.log('Calculating eigenvalue shifts.') # allocate array for eigenvalue shifts: self.exx_skn = np.zeros((self.wfs.nspins, kd.nibzkpts, self.wfs.bd.nbands)) if self.bands is None: noccmax = self.wfs.bd.nbands else: noccmax = max(max(self.bands) + 1, noccmax) N_c = self.kd.N_c vol = wfs.gd.dv * wfs.gd.N_c.prod() if self.alpha is None: alpha = 6 * vol**(2 / 3.0) / pi**2 else: alpha = self.alpha if self.gamma_point == 1: if alpha == 0.0: qvol = (2*np.pi)**3 / vol / N_c.prod() self.gamma = 4*np.pi * (3*qvol / (4*np.pi))**(1/3.) / qvol else: self.gamma = self.calculate_gamma(vol, alpha) else: kcell_cv = wfs.gd.cell_cv.copy() kcell_cv[0] *= N_c[0] kcell_cv[1] *= N_c[1] kcell_cv[2] *= N_c[2] self.gamma = madelung(kcell_cv) * vol * N_c.prod() / (4 * np.pi) self.log('Value of alpha parameter: %.3f Bohr^2' % alpha) self.log('Value of gamma parameter: %.3f Bohr^2' % self.gamma) # Construct all possible q=k2-k1 vectors: Nq_c = (N_c - 1) // self.qstride_c i_qc = np.indices(Nq_c * 2 + 1, float).transpose( (1, 2, 3, 0)).reshape((-1, 3)) self.bzq_qc = (i_qc - Nq_c) / N_c * self.qstride_c self.q0 = ((Nq_c * 2 + 1).prod() - 1) // 2 # index of q=(0,0,0) assert not self.bzq_qc[self.q0].any() # Count number of pairs for each q-vector: self.npairs_q = np.zeros(len(self.bzq_qc), int) for s in range(kd.nspins): for k1 in range(kd.nibzkpts): for k2 in range(kd.nibzkpts): for K2, q, n1_n, n2 in self.indices(s, k1, k2): self.npairs_q[q] += len(n1_n) self.npairs0 = self.npairs_q.sum() # total number of pairs self.log('Number of pairs:', self.npairs0) # Distribute q-vectors to Q processors: Q = self.world.size // self.wfs.kd.comm.size myrank = self.world.rank // self.wfs.kd.comm.size rank = 0 N = 0 myq = [] nq = 0 for q, n in enumerate(self.npairs_q): if n > 0: nq += 1 if rank == myrank: myq.append(q) N += n if N >= (rank + 1.0) * self.npairs0 / Q: rank += 1 assert len(myq) > 0, 'Too few q-vectors for too many processes!' self.bzq_qc = self.bzq_qc[myq] try: self.q0 = myq.index(self.q0) except ValueError: self.q0 = None self.log('%d x %d x %d k-points' % tuple(self.kd.N_c)) self.log('Distributing %d IBZ k-points over %d process(es).' % (kd.nibzkpts, self.wfs.kd.comm.size)) self.log('Distributing %d q-vectors over %d process(es).' % (nq, Q)) # q-point descriptor for my q-vectors: qd = KPointDescriptor(self.bzq_qc) # Plane-wave descriptor for all wave-functions: self.pd = PWDescriptor(wfs.pd.ecut, wfs.gd, dtype=wfs.pd.dtype, kd=kd) # Plane-wave descriptor pair-densities: self.pd2 = PWDescriptor(self.dens.pd2.ecut, self.dens.gd, dtype=wfs.dtype, kd=qd) self.log('Cutoff energies:') self.log(' Wave functions: %10.3f eV' % (self.pd.ecut * Hartree)) self.log(' Density: %10.3f eV' % (self.pd2.ecut * Hartree)) # Calculate 1/|G+q|^2 with special treatment of |G+q|=0: G2_qG = self.pd2.G2_qG if self.q0 is None: if self.omega is None: self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] else: self.iG2_qG = [(1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2)))) for G2_G in G2_qG] else: G2_qG[self.q0][0] = 117.0 # avoid division by zero if self.omega is None: self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] self.iG2_qG[self.q0][0] = self.gamma else: self.iG2_qG = [(1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2)))) for G2_G in G2_qG] self.iG2_qG[self.q0][0] = 1 / (4 * self.omega**2) G2_qG[self.q0][0] = 0.0 # restore correct value # Compensation charges: self.ghat = PWLFC([setup.ghat_l for setup in wfs.setups], self.pd2) self.ghat.set_positions(self.spos_ac) if self.molecule: self.initialize_gaussian() self.log('Value of beta parameter: %.3f 1/Bohr^2' % self.beta) self.timer.stop('Initialization') # Ready ... set ... go: self.t0 = time() self.npairs = 0 self.evv = 0.0 self.evvacdf = 0.0 for s in range(self.wfs.nspins): kpt1_q = [KPoint(self.wfs, noccmax).initialize(kpt) for kpt in self.wfs.kpt_u if kpt.s == s] kpt2_q = kpt1_q[:] if len(kpt1_q) == 0: # No s-spins on this CPU: continue # Send and receive ranks: srank = self.wfs.kd.get_rank_and_index( s, (kpt1_q[0].k - 1) % kd.nibzkpts)[0] rrank = self.wfs.kd.get_rank_and_index( s, (kpt1_q[-1].k + 1) % kd.nibzkpts)[0] # Shift k-points kd.nibzkpts - 1 times: for i in range(kd.nibzkpts): if i < kd.nibzkpts - 1: if kparallel: kpt = kpt2_q[-1].next(self.wfs) kpt.start_receiving(rrank) kpt2_q[0].start_sending(srank) else: kpt = kpt2_q[0] self.timer.start('Calculate') for kpt1, kpt2 in zip(kpt1_q, kpt2_q): # Loop over all k-points that k2 can be mapped to: for K2, q, n1_n, n2 in self.indices(s, kpt1.k, kpt2.k): self.apply(K2, q, kpt1, kpt2, n1_n, n2) self.timer.stop('Calculate') if i < kd.nibzkpts - 1: self.timer.start('Wait') if kparallel: kpt.wait() kpt2_q[0].wait() self.timer.stop('Wait') kpt2_q.pop(0) kpt2_q.append(kpt) self.evv = self.world.sum(self.evv) self.evvacdf = self.world.sum(self.evvacdf) self.calculate_exx_paw_correction() if self.method == 'standard': self.exx = self.evv + self.devv + self.evc + self.ecc elif self.method == 'acdf': self.exx = self.evvacdf + self.devv + self.evc + self.ecc else: 1 / 0 self.log('Exact exchange energy:') for txt, e in [ ('core-core', self.ecc), ('valence-core', self.evc), ('valence-valence (pseudo, acdf)', self.evvacdf), ('valence-valence (pseudo, standard)', self.evv), ('valence-valence (correction)', self.devv), ('total (%s)' % self.method, self.exx)]: self.log(' %-36s %14.6f eV' % (txt + ':', e * Hartree)) self.log('Total time: %10.3f seconds' % (time() - self.t0)) self.npairs = self.world.sum(self.npairs) assert self.npairs == self.npairs0 self.timer.stop('EXX') self.timer.write(self.fd) def calculate_gamma(self, vol, alpha): if self.molecule: return 0.0 N_c = self.kd.N_c offset_c = (N_c + 1) % 2 * 0.5 / N_c bzq_qc = monkhorst_pack(N_c) + offset_c qd = KPointDescriptor(bzq_qc) pd = PWDescriptor(self.wfs.pd.ecut, self.wfs.gd, kd=qd) gamma = (vol / (2 * pi)**2 * sqrt(pi / alpha) * self.kd.nbzkpts) for G2_G in pd.G2_qG: if G2_G[0] < 1e-7: G2_G = G2_G[1:] gamma -= np.dot(np.exp(-alpha * G2_G), G2_G**-1) return gamma / self.qstride_c.prod() def indices(self, s, k1, k2): """Generator for (K2, q, n1, n2) indices for (k1, k2) pair. s: int Spin index. k1: int Index of k-point in the IBZ. k2: int Index of k-point in the IBZ. Returns (K, q, n1_n, n2), where K then index of the k-point in the BZ that k2 is mapped to, q is the index of the q-vector between K and k1, and n1_n is a list of bands that should be combined with band n2.""" for K, k in enumerate(self.kd.bz2ibz_k): if k == k2: for K, q, n1_n, n2 in self._indices(s, k1, k2, K): yield K, q, n1_n, n2 def _indices(self, s, k1, k2, K2): k1_c = self.kd.ibzk_kc[k1] k2_c = self.kd.bzk_kc[K2] q_c = k2_c - k1_c q = abs(self.bzq_qc - q_c).sum(1).argmin() if abs(self.bzq_qc[q] - q_c).sum() > 1e-7: return if self.gamma_point == 0 and q == self.q0: return nocc1 = self.nocc_sk[s, k1] nocc2 = self.nocc_sk[s, k2] # Is k2 in the IBZ? is_ibz2 = (self.kd.ibz2bz_k[k2] == K2) for n2 in range(self.wfs.bd.nbands): # Find range of n1's (from n1a to n1b-1): if is_ibz2: # We get this combination twice, so let's only do half: if k1 >= k2: n1a = n2 else: n1a = n2 + 1 else: n1a = 0 n1b = self.wfs.bd.nbands if self.bandstructure: if n2 >= nocc2: n1b = min(n1b, nocc1) else: if n2 >= nocc2: break n1b = min(n1b, nocc1) if self.bands is not None: assert self.bandstructure n1_n = [] for n1 in range(n1a, n1b): if (n1 in self.bands and n2 < nocc2 or is_ibz2 and n2 in self.bands and n1 < nocc1): n1_n.append(n1) n1_n = np.array(n1_n) else: n1_n = np.arange(n1a, n1b) if len(n1_n) == 0: continue yield K2, q, n1_n, n2 def apply(self, K2, q, kpt1, kpt2, n1_n, n2): k20_c = self.kd.ibzk_kc[kpt2.k] k2_c = self.kd.bzk_kc[K2] if k2_c.any(): self.timer.start('Initialize plane waves') eik2r_R = self.wfs.gd.plane_wave(k2_c) eik20r_R = self.wfs.gd.plane_wave(k20_c) self.timer.stop('Initialize plane waves') else: eik2r_R = 1.0 eik20r_R = 1.0 w1 = self.kd.weight_k[kpt1.k] w2 = self.kd.weight_k[kpt2.k] # Is k2 in the 1. BZ? is_ibz2 = (self.kd.ibz2bz_k[kpt2.k] == K2) e_n = self.calculate_interaction(n1_n, n2, kpt1, kpt2, q, K2, eik20r_R, eik2r_R, is_ibz2) e_n *= 1.0 / self.kd.nbzkpts / self.wfs.nspins * self.qstride_c.prod() if q == self.q0: e_n[n1_n == n2] *= 0.5 f1_n = kpt1.f_n[n1_n] eps1_n = kpt1.eps_n[n1_n] f2 = kpt2.f_n[n2] eps2 = kpt2.eps_n[n2] s_n = np.sign(eps2 - eps1_n) evv = (f1_n * f2 * e_n).sum() evvacdf = 0.5 * (f1_n * (1 - s_n) * e_n + f2 * (1 + s_n) * e_n).sum() self.evv += evv * w1 self.evvacdf += evvacdf * w1 if is_ibz2: self.evv += evv * w2 self.evvacdf += evvacdf * w2 if self.bandstructure: x = self.wfs.nspins self.exx_skn[kpt1.s, kpt1.k, n1_n] += x * f2 * e_n if is_ibz2: self.exx_skn[kpt2.s, kpt2.k, n2] += x * np.dot(f1_n, e_n) def calculate_interaction(self, n1_n, n2, kpt1, kpt2, q, k, eik20r_R, eik2r_R, is_ibz2): """Calculate Coulomb interactions. For all n1 in the n1_n list, calculate interaction with n2.""" # number of plane waves: ng1 = self.wfs.ng_k[kpt1.k] ng2 = self.wfs.ng_k[kpt2.k] # Transform to real space and apply symmetry operation: self.timer.start('IFFT1') if is_ibz2: u2_R = self.pd.ifft(kpt2.psit_nG[n2, :ng2], kpt2.k) else: psit2_R = self.pd.ifft(kpt2.psit_nG[n2, :ng2], kpt2.k) * eik20r_R self.timer.start('Symmetry transform') u2_R = self.kd.transform_wave_function(psit2_R, k) / eik2r_R self.timer.stop() self.timer.stop() # Calculate pair densities: nt_nG = self.pd2.zeros(len(n1_n), q=q) for n1, nt_G in zip(n1_n, nt_nG): self.timer.start('IFFT2') u1_R = self.pd.ifft(kpt1.psit_nG[n1, :ng1], kpt1.k) self.timer.stop() nt_R = u1_R.conj() * u2_R self.timer.start('FFT') nt_G[:] = self.pd2.fft(nt_R, q) self.timer.stop() s = self.kd.sym_k[k] time_reversal = self.kd.time_reversal_k[k] k2_c = self.kd.ibzk_kc[kpt2.k] self.timer.start('Compensation charges') Q_anL = {} # coefficients for shape functions for a, P1_ni in kpt1.P_ani.items(): P1_ni = P1_ni[n1_n] if is_ibz2: P2_i = kpt2.P_ani[a][n2] else: b = self.kd.symmetry.a_sa[s, a] S_c = (np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s]) - self.spos_ac[b]) assert abs(S_c.round() - S_c).max() < 1e-5 if self.ghat.dtype == complex: x = np.exp(2j * pi * np.dot(k2_c, S_c)) else: x = 1.0 P2_i = np.dot(self.wfs.setups[a].R_sii[s], kpt2.P_ani[b][n2]) * x if time_reversal: P2_i = P2_i.conj() D_np = [] for P1_i in P1_ni: D_ii = np.outer(P1_i.conj(), P2_i) D_np.append(pack(D_ii)) Q_anL[a] = np.dot(D_np, self.wfs.setups[a].Delta_pL) self.timer.start('Expand') if q != self.qlatest: self.f_IG = self.ghat.expand(q) self.qlatest = q self.timer.stop('Expand') # Add compensation charges: self.ghat.add(nt_nG, Q_anL, q, self.f_IG) self.timer.stop('Compensation charges') if self.molecule and n2 in n1_n: nn = (n1_n == n2).nonzero()[0][0] nt_nG[nn] -= self.ngauss_G else: nn = None iG2_G = self.iG2_qG[q] # Calculate energies: e_n = np.empty(len(n1_n)) for n, nt_G in enumerate(nt_nG): e_n[n] = -4 * pi * np.real(self.pd2.integrate(nt_G, nt_G * iG2_G)) self.npairs += 1 if nn is not None: e_n[nn] -= 2 * (self.pd2.integrate(nt_nG[nn], self.vgauss_G) + (self.beta / 2 / pi)**0.5) if self.write_timing_information: t = (time() - self.t0) / len(n1_n) self.log('Time for first pair-density: %10.3f seconds' % t) self.log('Estimated total time: %10.3f seconds' % (t * self.npairs0 / self.world.size)) self.write_timing_information = False return e_n 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 initialize_gaussian(self): """Calculate gaussian compensation charge and its potential. Used to decouple electrostatic interactions between periodically repeated images for molecular calculations. Charge containing one electron:: (beta/pi)^(3/2)*exp(-beta*r^2), its Fourier transform:: exp(-G^2/(4*beta)), and its potential:: erf(beta^0.5*r)/r. """ gd = self.wfs.gd # Set exponent of exp-function to -19 on the boundary: self.beta = 4 * 19 * (gd.icell_cv**2).sum(1).max() # Calculate gaussian: G_Gv = self.pd2.get_reciprocal_vectors() G2_G = self.pd2.G2_qG[0] C_v = gd.cell_cv.sum(0) / 2 # center of cell self.ngauss_G = np.exp(-1.0 / (4 * self.beta) * G2_G + 1j * np.dot(G_Gv, C_v)) / gd.dv # Calculate potential from gaussian: R_Rv = gd.get_grid_point_coordinates().transpose((1, 2, 3, 0)) r_R = ((R_Rv - C_v)**2).sum(3)**0.5 if (gd.N_c % 2 == 0).all(): r_R[tuple(gd.N_c // 2)] = 1.0 # avoid dividing by zero v_R = erf(self.beta**0.5 * r_R) / r_R if (gd.N_c % 2 == 0).all(): v_R[tuple(gd.N_c // 2)] = (4 * self.beta / pi)**0.5 self.vgauss_G = self.pd2.fft(v_R) # Compare self-interaction to analytic result: assert abs(0.5 * self.pd2.integrate(self.ngauss_G, self.vgauss_G) - (self.beta / 2 / pi)**0.5) < 1e-6
class HybridXC(HybridXCBase): orbital_dependent = True def __init__(self, name, hybrid=None, xc=None, gygi=False, alpha=None, skip_gamma=False, ecut=None, etotflag=False, acdf=False, coredensity=True, logfilename='-', bands=None, core_valence=True): """Mix standard functionals with exact exchange. bands: list or None List of bands to calculate energy for. Default is None meaning do all bands. """ self.alpha = alpha self.skip_gamma = skip_gamma self.gygi = gygi self.exx = 0.0 self.etotflag = etotflag self.ecut = ecut self.fd = logfilename self.write_timing_information = True self.bands = bands self.acdf = acdf # adiabatic-connection dissipation fluctuation for RPA correlation energy self.coredensity = coredensity self.core_valence = core_valence if self.acdf: self.exxacdf = 0.0 self.etotflag = True print('etotflag is True') HybridXCBase.__init__(self, name, hybrid, xc) def log(self, *args, **kwargs): prnt(file=self.fd, *args, **kwargs) self.fd.flush() def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): addcoredensity = self.coredensity # XXX overwrites input return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp, addcoredensity, a) def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd if self.bd.comm.size > 1: raise ValueError('Band parallelization not supported by hybridk') self.wfs = wfs self.world = wfs.world self.fd = logfile(self.fd, self.world.rank) N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol**(2 / 3.0) / pi**2 if self.ecut is None: self.ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() * 0.9999 self.bzq_qc = self.kd.get_bz_q_points() qd = KPointDescriptor(self.bzq_qc) q0 = self.kd.where_is_q(np.zeros(3), self.bzq_qc) self.pwd = PWDescriptor(self.ecut, self.gd, complex, kd=qd) G2_qG = self.pwd.G2_qG G2_qG[q0][0] = 117.0 self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] G2_qG[q0][0] = 0.0 self.iG2_qG[q0][0] = 0.0 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) for q in range(self.kd.nbzkpts): self.gamma -= np.dot(np.exp(-self.alpha * G2_qG[q]), self.iG2_qG[q]) self.iG2_qG[q0][0] = self.gamma self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], qd, dtype=complex) self.log('Value of alpha parameter:', self.alpha) self.log('Value of gamma parameter:', self.gamma) self.log('Cutoff energy:', self.ecut, 'Hartree') self.log('%d x %d x %d k-points' % tuple(self.kd.N_c)) def set_positions(self, spos_ac): self.ghat.set_positions(spos_ac) self.spos_ac = spos_ac def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx 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(self, kpt1, kpt2, k): k1_c = self.kd.ibzk_kc[kpt1.k] k20_c = self.kd.ibzk_kc[kpt2.k] k2_c = self.kd.bzk_kc[k] q_c = k2_c - k1_c N_c = self.gd.N_c q = self.kd.where_is_q(q_c, self.bzq_qc) q_c = self.bzq_qc[q] eik1r_R = np.exp(2j * pi * np.dot(np.indices(N_c).T, k1_c / N_c).T) eik2r_R = np.exp(2j * pi * np.dot(np.indices(N_c).T, k20_c / N_c).T) eiqr_R = np.exp(2j * pi * np.dot(np.indices(N_c).T, q_c / N_c).T) same = abs(k1_c - k2_c).max() < 1e-9 iG2_G = self.iG2_qG[q] N = N_c.prod() vol = self.gd.dv * N nspins = self.nspins fcut = 1e-10 is_ibz2 = abs(k2_c - self.kd.ibzk_kc[kpt2.k]).max() < 1e-9 for n1 in range(self.nbands): f1 = kpt1.f_n[n1] e1 = kpt1.eps_n[n1] for n2 in range(self.nbands): if same: assert is_ibz2 if n2 > n1: continue elif is_ibz2: if kpt1.k > kpt2.k: if n2 > n1: continue else: if n2 >= n1: continue f2 = kpt2.f_n[n2] e2 = kpt2.eps_n[n2] x = 1.0 if same and n1 == n2: x = 0.5 if not self.etotflag: self.debug_skn[kpt1.s, kpt1.k, n1] += x if is_ibz2: self.debug_skn[kpt2.s, kpt2.k, n2] += x if self.etotflag and not self.gygi: if abs(f1) < fcut or abs(f2) < fcut: continue else: if abs(f1) < fcut and abs(f2) < fcut: continue if self.bands is not None: if not (n1 in self.bands or is_ibz2 and n2 in self.bands): continue if self.skip_gamma and same: continue t0 = time() nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, q, k, eik1r_R, eik2r_R, eiqr_R, is_ibz2) nt_G = self.pwd.fft(nt_R, q) / N vt_G = nt_G.copy() vt_G *= -pi * vol * iG2_G e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid * x if self.etotflag: if self.acdf: if self.gygi and same: self.exxacdf += f2 * e * kpt1.weight else: self.exxacdf += 0.5 * ( f1 * (1 - np.sign(e2 - e1)) * e + f2 * (1 - np.sign(e1 - e2)) * e) * kpt1.weight else: self.exx += f2 * e * kpt1.weight[ 0] * f1 * self.kd.nbzkpts * nspins / 2 else: self.exx_skn[kpt1.s, kpt1.k, n1] += 2 * f2 * e if is_ibz2: if self.etotflag: if self.acdf: if self.gygi and same: self.exxacdf += f1 * e * kpt2.weight else: self.exxacdf += 0.5 * ( f1 * (1 - np.sign(e2 - e1)) * e + f2 * (1 - np.sign(e1 - e2)) * e) * kpt2.weight else: self.exx += f1 * e * kpt2.weight[ 0] * f2 * self.kd.nbzkpts * nspins / 2 else: self.exx_skn[kpt2.s, kpt2.k, n2] += 2 * f1 * e if self.write_timing_information: t = time() - t0 self.log('Time for first pair-density:', t, 'seconds') self.log('Estimated total time', t * self.npairs / self.world.size, 'seconds') self.write_timing_information = False 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_pair_density(self, n1, n2, kpt1, kpt2, q, k, eik1r_R, eik2r_R, eiqr_R, ibz2): if isinstance(self.wfs, PWWaveFunctions): psit1_R = self.wfs.pd.ifft(kpt1.psit_nG[n1]) * eik1r_R psit2_R = self.wfs.pd.ifft(kpt2.psit_nG[n2]) * eik2r_R else: psit1_R = kpt1.psit_nG[n1] psit2_R = kpt2.psit_nG[n2] if ibz2: psit2_R = psit2_R else: psit2_R = np.asarray(self.kd.transform_wave_function(psit2_R, k), complex) nt_R = psit1_R.conj() * psit2_R s = self.kd.sym_k[k] time_reversal = self.kd.time_reversal_k[k] k2_c = self.kd.ibzk_kc[kpt2.k] Q_aL = {} for a, P1_ni in kpt1.P_ani.items(): P1_i = P1_ni[n1] b = self.kd.symmetry.a_sa[s, a] S_c = (np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s]) - self.spos_ac[b]) assert abs(S_c.round() - S_c).max() < 1e-13 x = np.exp(2j * pi * np.dot(k2_c, S_c)) P2_i = np.dot(self.setups[a].R_sii[s], kpt2.P_ani[b][n2]) * x if time_reversal: P2_i = P2_i.conj() if ibz2: P2_i = kpt2.P_ani[a][n2] D_ii = np.outer(P1_i.conj(), P2_i) D_p = pack(D_ii) Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) self.ghat.add(nt_R, Q_aL, q) return nt_R / eiqr_R
def calculate_exx(self): """Non-selfconsistent calculation.""" self.timer.start('EXX') self.timer.start('Initialization') kd = self.kd wfs = self.wfs if fftw.FFTPlan is fftw.NumpyFFTPlan: self.log('NOT USING FFTW !!') self.log('Spins:', self.wfs.nspins) W = max(1, self.wfs.kd.comm.size // self.wfs.nspins) # Are the k-points distributed? kparallel = (W > 1) # Find number of occupied bands: self.nocc_sk = np.zeros((self.wfs.nspins, kd.nibzkpts), int) for kpt in self.wfs.kpt_u: for n, f in enumerate(kpt.f_n): if abs(f) < self.fcut: self.nocc_sk[kpt.s, kpt.k] = n break else: self.nocc_sk[kpt.s, kpt.k] = self.wfs.bd.nbands self.wfs.kd.comm.sum(self.nocc_sk) noccmin = self.nocc_sk.min() noccmax = self.nocc_sk.max() self.log('Number of occupied bands (min, max): %d, %d' % (noccmin, noccmax)) self.log('Number of valence electrons:', self.wfs.setups.nvalence) if self.bandstructure: self.log('Calculating eigenvalue shifts.') # allocate array for eigenvalue shifts: self.exx_skn = np.zeros( (self.wfs.nspins, kd.nibzkpts, self.wfs.bd.nbands)) if self.bands is None: noccmax = self.wfs.bd.nbands else: noccmax = max(max(self.bands) + 1, noccmax) N_c = self.kd.N_c vol = wfs.gd.dv * wfs.gd.N_c.prod() if self.alpha is None: alpha = 6 * vol**(2 / 3.0) / pi**2 else: alpha = self.alpha if self.gamma_point == 1: if alpha == 0.0: qvol = (2 * np.pi)**3 / vol / N_c.prod() self.gamma = 4 * np.pi * (3 * qvol / (4 * np.pi))**(1 / 3.) / qvol else: self.gamma = self.calculate_gamma(vol, alpha) else: kcell_cv = wfs.gd.cell_cv.copy() kcell_cv[0] *= N_c[0] kcell_cv[1] *= N_c[1] kcell_cv[2] *= N_c[2] self.gamma = madelung(kcell_cv) * vol * N_c.prod() / (4 * np.pi) self.log('Value of alpha parameter: %.3f Bohr^2' % alpha) self.log('Value of gamma parameter: %.3f Bohr^2' % self.gamma) # Construct all possible q=k2-k1 vectors: Nq_c = (N_c - 1) // self.qstride_c i_qc = np.indices(Nq_c * 2 + 1, float).transpose((1, 2, 3, 0)).reshape( (-1, 3)) self.bzq_qc = (i_qc - Nq_c) / N_c * self.qstride_c self.q0 = ((Nq_c * 2 + 1).prod() - 1) // 2 # index of q=(0,0,0) assert not self.bzq_qc[self.q0].any() # Count number of pairs for each q-vector: self.npairs_q = np.zeros(len(self.bzq_qc), int) for s in range(kd.nspins): for k1 in range(kd.nibzkpts): for k2 in range(kd.nibzkpts): for K2, q, n1_n, n2 in self.indices(s, k1, k2): self.npairs_q[q] += len(n1_n) self.npairs0 = self.npairs_q.sum() # total number of pairs self.log('Number of pairs:', self.npairs0) # Distribute q-vectors to Q processors: Q = self.world.size // self.wfs.kd.comm.size myrank = self.world.rank // self.wfs.kd.comm.size rank = 0 N = 0 myq = [] nq = 0 for q, n in enumerate(self.npairs_q): if n > 0: nq += 1 if rank == myrank: myq.append(q) N += n if N >= (rank + 1.0) * self.npairs0 / Q: rank += 1 assert len(myq) > 0, 'Too few q-vectors for too many processes!' self.bzq_qc = self.bzq_qc[myq] try: self.q0 = myq.index(self.q0) except ValueError: self.q0 = None self.log('%d x %d x %d k-points' % tuple(self.kd.N_c)) self.log('Distributing %d IBZ k-points over %d process(es).' % (kd.nibzkpts, self.wfs.kd.comm.size)) self.log('Distributing %d q-vectors over %d process(es).' % (nq, Q)) # q-point descriptor for my q-vectors: qd = KPointDescriptor(self.bzq_qc) # Plane-wave descriptor for all wave-functions: self.pd = PWDescriptor(wfs.pd.ecut, wfs.gd, dtype=wfs.pd.dtype, kd=kd) # Plane-wave descriptor pair-densities: self.pd2 = PWDescriptor(self.dens.pd2.ecut, self.dens.gd, dtype=wfs.dtype, kd=qd) self.log('Cutoff energies:') self.log(' Wave functions: %10.3f eV' % (self.pd.ecut * Hartree)) self.log(' Density: %10.3f eV' % (self.pd2.ecut * Hartree)) # Calculate 1/|G+q|^2 with special treatment of |G+q|=0: G2_qG = self.pd2.G2_qG if self.q0 is None: if self.omega is None: self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] else: self.iG2_qG = [ (1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2)))) for G2_G in G2_qG ] else: G2_qG[self.q0][0] = 117.0 # avoid division by zero if self.omega is None: self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] self.iG2_qG[self.q0][0] = self.gamma else: self.iG2_qG = [ (1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2)))) for G2_G in G2_qG ] self.iG2_qG[self.q0][0] = 1 / (4 * self.omega**2) G2_qG[self.q0][0] = 0.0 # restore correct value # Compensation charges: self.ghat = PWLFC([setup.ghat_l for setup in wfs.setups], self.pd2) self.ghat.set_positions(self.spos_ac) if self.molecule: self.initialize_gaussian() self.log('Value of beta parameter: %.3f 1/Bohr^2' % self.beta) self.timer.stop('Initialization') # Ready ... set ... go: self.t0 = time() self.npairs = 0 self.evv = 0.0 self.evvacdf = 0.0 for s in range(self.wfs.nspins): kpt1_q = [ KPoint(self.wfs, noccmax).initialize(kpt) for kpt in self.wfs.kpt_u if kpt.s == s ] kpt2_q = kpt1_q[:] if len(kpt1_q) == 0: # No s-spins on this CPU: continue # Send and receive ranks: srank = self.wfs.kd.get_rank_and_index(s, (kpt1_q[0].k - 1) % kd.nibzkpts)[0] rrank = self.wfs.kd.get_rank_and_index(s, (kpt1_q[-1].k + 1) % kd.nibzkpts)[0] # Shift k-points kd.nibzkpts - 1 times: for i in range(kd.nibzkpts): if i < kd.nibzkpts - 1: if kparallel: kpt = kpt2_q[-1].next(self.wfs) kpt.start_receiving(rrank) kpt2_q[0].start_sending(srank) else: kpt = kpt2_q[0] self.timer.start('Calculate') for kpt1, kpt2 in zip(kpt1_q, kpt2_q): # Loop over all k-points that k2 can be mapped to: for K2, q, n1_n, n2 in self.indices(s, kpt1.k, kpt2.k): self.apply(K2, q, kpt1, kpt2, n1_n, n2) self.timer.stop('Calculate') if i < kd.nibzkpts - 1: self.timer.start('Wait') if kparallel: kpt.wait() kpt2_q[0].wait() self.timer.stop('Wait') kpt2_q.pop(0) kpt2_q.append(kpt) self.evv = self.world.sum(self.evv) self.evvacdf = self.world.sum(self.evvacdf) self.calculate_exx_paw_correction() if self.method == 'standard': self.exx = self.evv + self.devv + self.evc + self.ecc elif self.method == 'acdf': self.exx = self.evvacdf + self.devv + self.evc + self.ecc else: 1 / 0 self.log('Exact exchange energy:') for txt, e in [('core-core', self.ecc), ('valence-core', self.evc), ('valence-valence (pseudo, acdf)', self.evvacdf), ('valence-valence (pseudo, standard)', self.evv), ('valence-valence (correction)', self.devv), ('total (%s)' % self.method, self.exx)]: self.log(' %-36s %14.6f eV' % (txt + ':', e * Hartree)) self.log('Total time: %10.3f seconds' % (time() - self.t0)) self.npairs = self.world.sum(self.npairs) assert self.npairs == self.npairs0 self.timer.stop('EXX') self.timer.write(self.fd)
if world.size == 1: for size1, size2 in [ [(3, 3, 3), (8, 8, 8)], [(4, 4, 4), (9, 9, 9)], [(2, 4, 4), (5, 9, 9)], [(2, 3, 4), (5, 6, 9)], [(2, 3, 4), (5, 6, 8)], [(4, 4, 4), (8, 8, 8)], [(2, 4, 4), (4, 8, 8)], [(2, 4, 2), (4, 8, 4)] ]: print(size1, size2) gd1 = GridDescriptor(size1, size1) gd2 = GridDescriptor(size2, size1) pd1 = PWDescriptor(1, gd1, complex) pd2 = PWDescriptor(1, gd2, complex) pd1r = PWDescriptor(1, gd1) pd2r = PWDescriptor(1, gd2) for R1, R2 in [[(0,0,0), (0,0,0)], [(0,0,0), (0,0,1)]]: x = test(gd1, gd2, pd1, pd2, R1, R2) y = test(gd1, gd2, pd1r, pd2r ,R1, R2) equal(x, y, 1e-9) a1 = np.random.random(size1) a2 = pd1r.interpolate(a1, pd2r)[0] c2 = pd1.interpolate(a1 + 0.0j, pd2)[0] d2 = pd1.interpolate(a1 * 1.0j, pd2)[0] equal(gd1.integrate(a1), gd2.integrate(a2), 1e-13) equal(abs(c2 - a2).max(), 0, 1e-14)
def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: self.alpha = 6 * vol**(2 / 3.0) / pi**2 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() if self.kd.N_c is None: self.bzk_kc = np.zeros((1, 3)) dfghdfgh else: n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.pwd = PWDescriptor(ecut, self.gd, self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.pwd.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G**-1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:]**-1) n += 1 assert n == self.kd.N_c.prod() self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], dtype=complex ) self.ghat.set_k_points(self.bzk_kc) self.fullkd = KPointDescriptor(self.kd.bzk_kc, nspins=1) class S: id_a = [] def set_symmetry(self, s): pass self.fullkd.set_symmetry(Atoms(pbc=True), S(), False) self.fullkd.set_communicator(world) self.pt = LFC(self.gd, [setup.pt_j for setup in density.setups], dtype=complex) self.pt.set_k_points(self.fullkd.ibzk_kc) self.interpolator = density.interpolator
def calculate_exx(self): """Non-selfconsistent calculation.""" self.timer.start('EXX') self.timer.start('Initialization') kd = self.kd wfs = self.wfs if fftw.FFTPlan is fftw.NumpyFFTPlan: self.log('NOT USING FFTW !!') self.log('Spins:', self.wfs.nspins) W = max(1, self.wfs.kd.comm.size // self.wfs.nspins) # Are the k-points distributed? kparallel = (W > 1) # Find number of occupied bands: self.nocc_sk = np.zeros((self.wfs.nspins, kd.nibzkpts), int) for kpt in self.wfs.kpt_u: for n, f in enumerate(kpt.f_n): if abs(f) < self.fcut: self.nocc_sk[kpt.s, kpt.k] = n break else: self.nocc_sk[kpt.s, kpt.k] = self.wfs.bd.nbands self.wfs.kd.comm.sum(self.nocc_sk) noccmin = self.nocc_sk.min() noccmax = self.nocc_sk.max() self.log('Number of occupied bands (min, max): %d, %d' % (noccmin, noccmax)) self.log('Number of valence electrons:', self.wfs.setups.nvalence) if self.bandstructure: self.log('Calculating eigenvalue shifts.') # allocate array for eigenvalue shifts: self.exx_skn = np.zeros((self.wfs.nspins, kd.nibzkpts, self.wfs.bd.nbands)) if self.bands is None: noccmax = self.wfs.bd.nbands else: noccmax = max(max(self.bands) + 1, noccmax) N_c = self.kd.N_c vol = wfs.gd.dv * wfs.gd.N_c.prod() if self.alpha is None: alpha = 6 * vol**(2 / 3.0) / pi**2 else: alpha = self.alpha if self.gamma_point == 1: if alpha == 0.0: qvol = (2*np.pi)**3 / vol / N_c.prod() self.gamma = 4*np.pi * (3*qvol / (4*np.pi))**(1/3.) / qvol else: self.gamma = self.calculate_gamma(vol, alpha) else: kcell_cv = wfs.gd.cell_cv.copy() kcell_cv[0] *= N_c[0] kcell_cv[1] *= N_c[1] kcell_cv[2] *= N_c[2] self.gamma = madelung(kcell_cv) * vol * N_c.prod() / (4 * np.pi) self.log('Value of alpha parameter: %.3f Bohr^2' % alpha) self.log('Value of gamma parameter: %.3f Bohr^2' % self.gamma) # Construct all possible q=k2-k1 vectors: Nq_c = (N_c - 1) // self.qstride_c i_qc = np.indices(Nq_c * 2 + 1, float).transpose( (1, 2, 3, 0)).reshape((-1, 3)) self.bzq_qc = (i_qc - Nq_c) / N_c * self.qstride_c self.q0 = ((Nq_c * 2 + 1).prod() - 1) // 2 # index of q=(0,0,0) assert not self.bzq_qc[self.q0].any() # Count number of pairs for each q-vector: self.npairs_q = np.zeros(len(self.bzq_qc), int) for s in range(kd.nspins): for k1 in range(kd.nibzkpts): for k2 in range(kd.nibzkpts): for K2, q, n1_n, n2 in self.indices(s, k1, k2): self.npairs_q[q] += len(n1_n) self.npairs0 = self.npairs_q.sum() # total number of pairs self.log('Number of pairs:', self.npairs0) # Distribute q-vectors to Q processors: Q = self.world.size // self.wfs.kd.comm.size myrank = self.world.rank // self.wfs.kd.comm.size rank = 0 N = 0 myq = [] nq = 0 for q, n in enumerate(self.npairs_q): if n > 0: nq += 1 if rank == myrank: myq.append(q) N += n if N >= (rank + 1.0) * self.npairs0 / Q: rank += 1 assert len(myq) > 0, 'Too few q-vectors for too many processes!' self.bzq_qc = self.bzq_qc[myq] try: self.q0 = myq.index(self.q0) except ValueError: self.q0 = None self.log('%d x %d x %d k-points' % tuple(self.kd.N_c)) self.log('Distributing %d IBZ k-points over %d process(es).' % (kd.nibzkpts, self.wfs.kd.comm.size)) self.log('Distributing %d q-vectors over %d process(es).' % (nq, Q)) # q-point descriptor for my q-vectors: qd = KPointDescriptor(self.bzq_qc) # Plane-wave descriptor for all wave-functions: self.pd = PWDescriptor(wfs.pd.ecut, wfs.gd, dtype=wfs.pd.dtype, kd=kd) # Plane-wave descriptor pair-densities: self.pd2 = PWDescriptor(self.dens.pd2.ecut, self.dens.gd, dtype=wfs.dtype, kd=qd) self.log('Cutoff energies:') self.log(' Wave functions: %10.3f eV' % (self.pd.ecut * Hartree)) self.log(' Density: %10.3f eV' % (self.pd2.ecut * Hartree)) # Calculate 1/|G+q|^2 with special treatment of |G+q|=0: G2_qG = self.pd2.G2_qG if self.q0 is None: if self.omega is None: self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] else: self.iG2_qG = [(1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2)))) for G2_G in G2_qG] else: G2_qG[self.q0][0] = 117.0 # avoid division by zero if self.omega is None: self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] self.iG2_qG[self.q0][0] = self.gamma else: self.iG2_qG = [(1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2)))) for G2_G in G2_qG] self.iG2_qG[self.q0][0] = 1 / (4 * self.omega**2) G2_qG[self.q0][0] = 0.0 # restore correct value # Compensation charges: self.ghat = PWLFC([setup.ghat_l for setup in wfs.setups], self.pd2) self.ghat.set_positions(self.spos_ac) if self.molecule: self.initialize_gaussian() self.log('Value of beta parameter: %.3f 1/Bohr^2' % self.beta) self.timer.stop('Initialization') # Ready ... set ... go: self.t0 = time() self.npairs = 0 self.evv = 0.0 self.evvacdf = 0.0 for s in range(self.wfs.nspins): kpt1_q = [KPoint(self.wfs, noccmax).initialize(kpt) for kpt in self.wfs.kpt_u if kpt.s == s] kpt2_q = kpt1_q[:] if len(kpt1_q) == 0: # No s-spins on this CPU: continue # Send and receive ranks: srank = self.wfs.kd.get_rank_and_index( s, (kpt1_q[0].k - 1) % kd.nibzkpts)[0] rrank = self.wfs.kd.get_rank_and_index( s, (kpt1_q[-1].k + 1) % kd.nibzkpts)[0] # Shift k-points kd.nibzkpts - 1 times: for i in range(kd.nibzkpts): if i < kd.nibzkpts - 1: if kparallel: kpt = kpt2_q[-1].next(self.wfs) kpt.start_receiving(rrank) kpt2_q[0].start_sending(srank) else: kpt = kpt2_q[0] self.timer.start('Calculate') for kpt1, kpt2 in zip(kpt1_q, kpt2_q): # Loop over all k-points that k2 can be mapped to: for K2, q, n1_n, n2 in self.indices(s, kpt1.k, kpt2.k): self.apply(K2, q, kpt1, kpt2, n1_n, n2) self.timer.stop('Calculate') if i < kd.nibzkpts - 1: self.timer.start('Wait') if kparallel: kpt.wait() kpt2_q[0].wait() self.timer.stop('Wait') kpt2_q.pop(0) kpt2_q.append(kpt) self.evv = self.world.sum(self.evv) self.evvacdf = self.world.sum(self.evvacdf) self.calculate_exx_paw_correction() if self.method == 'standard': self.exx = self.evv + self.devv + self.evc + self.ecc elif self.method == 'acdf': self.exx = self.evvacdf + self.devv + self.evc + self.ecc else: 1 / 0 self.log('Exact exchange energy:') for txt, e in [ ('core-core', self.ecc), ('valence-core', self.evc), ('valence-valence (pseudo, acdf)', self.evvacdf), ('valence-valence (pseudo, standard)', self.evv), ('valence-valence (correction)', self.devv), ('total (%s)' % self.method, self.exx)]: self.log(' %-36s %14.6f eV' % (txt + ':', e * Hartree)) self.log('Total time: %10.3f seconds' % (time() - self.t0)) self.npairs = self.world.sum(self.npairs) assert self.npairs == self.npairs0 self.timer.stop('EXX') self.timer.write(self.fd)
class Unfold: """This Class is used to Unfold the Bands of a supercell (SC) calculations into a the primitive cell (PC). As a convention (when possible) capital letters variables are related to the SC while lowercase ones to the PC """ def __init__(self, name=None, calc=None, M=None, spinorbit=None): self.name = name self.calc = GPAW(calc, txt=None, communicator=mpi.serial_comm) self.M = np.array(M, dtype=float) self.spinorbit = spinorbit self.gd = self.calc.wfs.gd.new_descriptor() self.kd = self.calc.wfs.kd if self.calc.wfs.mode is 'pw': self.pd = self.calc.wfs.pd else: self.pd = PWDescriptor(ecut=None, gd=self.gd, kd=self.kd, dtype=complex) self.acell_cv = self.gd.cell_cv self.bcell_cv = 2 * np.pi * self.gd.icell_cv self.vol = self.gd.volume self.BZvol = (2 * np.pi)**3 / self.vol self.nb = self.calc.get_number_of_bands() self.v_Knm = None if spinorbit: if mpi.world.rank == 0: print('Calculating spinorbit Corrections') self.nb = 2 * self.calc.get_number_of_bands() self.e_mK, self.v_Knm = get_spinorbit_eigenvalues(self.calc, return_wfs=True) if mpi.world.rank == 0: print('Done with the spinorbit Corrections') def get_K_index(self, K): """Find the index of a given K.""" K = np.array([K]) bzKG = to1bz(K, self.acell_cv)[0] iK = self.kd.where_is_q(bzKG, self.kd.bzk_kc) return iK def get_g(self, iK): """Not all the G vectors are relevant for the bands unfolding, but only the ones that match the PC reciprocal vectors. This function finds the relevant ones.""" G_Gv_temp = self.pd.get_reciprocal_vectors(q=iK, add_q=False) G_Gc_temp = np.dot(G_Gv_temp, np.linalg.inv(self.bcell_cv)) iG_list = [] g_list = [] for iG, G in enumerate(G_Gc_temp): a = np.dot(G, np.linalg.inv(self.M).T) check = np.abs(a) % 1 < 1e-5 check2 = np.abs((np.abs(a[np.where(~check)]) % 1) - 1) < 1e-5 if all(check) or all(check2): iG_list.append(iG) g_list.append(G) return np.array(iG_list), np.array(g_list) def get_G_index(self, iK, G, G_list): """Find the index of a given G.""" G_list -= G sumG = np.sum(abs(G_list), axis=1) iG = np.where(sumG < 1e-5)[0] return iG def get_eigenvalues(self, iK): """Get the list of eigenvalues for a given iK.""" if not self.spinorbit: e_m = self.calc.get_eigenvalues(kpt=iK, spin=0) / Hartree else: e_m = self.e_mK[:, iK] / Hartree return np.array(e_m) def get_pw_wavefunctions_k(self, iK): """Get the list of Fourier coefficients of the WaveFunction for a given iK. For spinors the number of bands is doubled and a spin dimension is added.""" psi_mgrid = get_rs_wavefunctions_k(self.calc, iK, self.spinorbit, self.v_Knm) if not self.spinorbit: psi_list_mG = [] for i in range(len(psi_mgrid)): psi_list_mG.append(self.pd.fft(psi_mgrid[i], iK)) psi_mG = np.array(psi_list_mG) return psi_mG else: u0_list_mG = [] u1_list_mG = [] for i in range(psi_mgrid.shape[0]): u0_list_mG.append(self.pd.fft(psi_mgrid[i, 0], iK)) u1_list_mG.append(self.pd.fft(psi_mgrid[i, 1], iK)) u0_mG = np.array(u0_list_mG) u1_mG = np.array(u1_list_mG) u_mG = np.zeros((len(u0_mG), 2, u0_mG.shape[1]), complex) u_mG[:, 0] = u0_mG u_mG[:, 1] = u1_mG return u_mG def get_spectral_weights_k(self, k_t): """Returns the spectral weights for a given k in the PC: P_mK(k_t) = \sum_n |<Km|k_t n>|**2 which can be shown to be equivalent to: P_mK(k_t) = \sum_g |C_Km(g+k_t-K)|**2 """ K_c, G_t = find_K_from_k(k_t, self.M) iK = self.get_K_index(K_c) iG_list, g_list = self.get_g(iK) gG_t_list = g_list + G_t G_Gv = self.pd.get_reciprocal_vectors(q=iK, add_q=False) G_Gc = np.dot(G_Gv, np.linalg.inv(self.bcell_cv)) igG_t_list = [] for g in gG_t_list: igG_t_list.append(self.get_G_index(iK, g.copy(), G_Gc.copy())) C_mG = self.get_pw_wavefunctions_k(iK) P_m = [] if not self.spinorbit: for m in range(self.nb): P = 0. norm = np.sum(np.linalg.norm(C_mG[m, :])**2) for iG in igG_t_list: P += np.linalg.norm(C_mG[m, iG])**2 P_m.append(P / norm) else: for m in range(self.nb): P = 0. norm = np.sum( np.linalg.norm(C_mG[m, 0, :])**2 + np.linalg.norm(C_mG[m, 1, :])**2) for iG in igG_t_list: P += (np.linalg.norm(C_mG[m, 0, iG])**2 + np.linalg.norm(C_mG[m, 1, iG])**2) P_m.append(P / norm) return np.array(P_m) def get_spectral_weights(self, kpoints, filename=None): """Collect the spectral weights for the k points in the kpoints list. This function is parallelized over k's.""" Nk = len(kpoints) Nb = self.nb world = mpi.world if filename is None: try: e_mK, P_mK = pickle.load( open('weights_' + self.name + '.pckl', 'rb')) except IOError: e_Km = [] P_Km = [] if world.rank == 0: print('Getting EigenValues and Weights') e_Km = np.zeros((Nk, Nb)) P_Km = np.zeros((Nk, Nb)) myk = range(0, Nk)[world.rank::world.size] for ik in myk: k = kpoints[ik] print('kpoint: %s' % k) K_c, G_c = find_K_from_k(k, self.M) iK = self.get_K_index(K_c) e_Km[ik] = self.get_eigenvalues(iK) P_Km[ik] = self.get_spectral_weights_k(k) world.barrier() world.sum(e_Km) world.sum(P_Km) e_mK = np.array(e_Km).T P_mK = np.array(P_Km).T if world.rank == 0: pickle.dump((e_mK, P_mK), open('weights_' + self.name + '.pckl', 'wb')) else: e_mK, P_mK = pickle.load(open(filename, 'rb')) return e_mK, P_mK def spectral_function(self, kpts, x, X, points_name, width=0.002, npts=10000, filename=None): """Returns the spectral function for all the ks in kpoints: eta / pi A_k(e) = \sum_m P_mK(k) x --------------------- (e - e_mk)**2 + eta**2 at each k-points defined on npts energy points in the range [emin, emax]. The width keyword is FWHM = 2 * eta.""" Nk = len(kpts) A_ke = np.zeros((Nk, npts), float) world = mpi.world e_mK, P_mK = self.get_spectral_weights(kpts, filename) if world.rank == 0: print('Calculating the Spectral Function') emin = np.min(e_mK) - 5 * width emax = np.max(e_mK) + 5 * width e = np.linspace(emin, emax, npts) for ik in range(Nk): for ie in range(len(e_mK[:, ik])): e0 = e_mK[ie, ik] D = (width / 2 / np.pi) / ((e - e0)**2 + (width / 2)**2) A_ke[ik] += P_mK[ie, ik] * D if world.rank == 0: pickle.dump((e * Hartree, A_ke, x, X, points_name), open('sf_' + self.name + '.pckl', 'wb')) print('Spectral Function calculation completed!') return
a = Atoms('H', cell=(3 * np.eye(3)), pbc=True) calc = GPAW(mode=PW(600), kpts=[[0, 0, 0], [0.25, 0, 0]]) a.calc = calc a.get_potential_energy() calc.diagonalize_full_hamiltonian(nbands=nb, expert=True) calc.write('a.gpw', 'all') pair = PairDensity('a.gpw', ecut=100) # Check continuity eq. for q_c in [[0, 0, 0], [1. / 4, 0, 0]]: ol = np.allclose(q_c, 0.0) qd = KPointDescriptor([q_c]) pd = PWDescriptor(pair.ecut, calc.wfs.gd, complex, qd) kptpair = pair.get_kpoint_pair(pd, 0, [0, 0, 0], 0, nb, 0, nb) deps_nm = kptpair.get_transition_energies(np.arange(0, nb), np.arange(0, nb)) n_nmG = pair.get_pair_density(pd, kptpair, np.arange(0, nb), np.arange(0, nb), optical_limit=ol) n_nmvG = pair.get_pair_momentum(pd, kptpair, np.arange(0, nb), np.arange(0, nb)) if ol: n2_nmv = np.zeros((nb, nb, 3), complex)
def write(self, calc, ecut=40 * Hartree, spacegroup=1): #sg = Spacegroup(spacegroup) #print sg wfs = calc.wfs setups = wfs.setups bd = wfs.bd kd = wfs.kd atoms = calc.atoms natoms = len(atoms) if wfs.symmetry is None: op_scc = np.eye(3, dtype=int).reshape((1, 3, 3)) else: op_scc = wfs.symmetry.op_scc pwd = PWDescriptor(ecut / Hartree, wfs.gd, kd.ibzk_kc) N_c = pwd.gd.N_c i_Qc = np.indices(N_c, np.int32).transpose((1, 2, 3, 0)) i_Qc += N_c // 2 i_Qc %= N_c i_Qc -= N_c // 2 i_Qc.shape = (-1, 3) i_Gc = i_Qc[pwd.Q_G] B_cv = 2.0 * np.pi * wfs.gd.icell_cv G_Qv = np.dot(i_Gc, B_cv).reshape((-1, 3)) G2_Q = (G_Qv**2).sum(axis=1) specie_a = np.empty(natoms, np.int32) nspecies = 0 species = {} names = [] symbols = [] numbers = [] charges = [] for a, id in enumerate(setups.id_a): if id not in species: species[id] = nspecies nspecies += 1 names.append(setups[a].symbol) symbols.append(setups[a].symbol) numbers.append(setups[a].Z) charges.append(setups[a].Nv) specie_a[a] = species[id] dimensions = [ ('character_string_length', 80), ('max_number_of_coefficients', len(i_Gc)), ('max_number_of_states', bd.nbands), ('number_of_atoms', len(atoms)), ('number_of_atom_species', nspecies), ('number_of_cartesian_directions', 3), ('number_of_components', 1), ('number_of_grid_points_vector1', N_c[0]), ('number_of_grid_points_vector2', N_c[1]), ('number_of_grid_points_vector3', N_c[2]), ('number_of_kpoints', kd.nibzkpts), ('number_of_reduced_dimensions', 3), ('number_of_spinor_components', 1), ('number_of_spins', wfs.nspins), ('number_of_symmetry_operations', len(op_scc)), ('number_of_vectors', 3), ('real_or_complex_coefficients', 2), ('symbol_length', 2)] for name, size in dimensions: print('%-34s %d' % (name, size)) self.nc.createDimension(name, size) var = self.add_variable var('space_group', (), np.array(spacegroup, dtype=int)) var('primitive_vectors', ('number_of_vectors', 'number_of_cartesian_directions'), wfs.gd.cell_cv, units='atomic units') var('reduced_symmetry_matrices', ('number_of_symmetry_operations', 'number_of_reduced_dimensions', 'number_of_reduced_dimensions'), op_scc.astype(np.int32), symmorphic='yes') var('reduced_symmetry_translations', ('number_of_symmetry_operations', 'number_of_reduced_dimensions'), np.zeros((len(op_scc), 3), dtype=np.int32)) var('atom_species', ('number_of_atoms',), specie_a + 1) var('reduced_atom_positions', ('number_of_atoms', 'number_of_reduced_dimensions'), atoms.get_scaled_positions()) var('atomic_numbers', ('number_of_atom_species',), np.array(numbers, dtype=float)) var('valence_charges', ('number_of_atom_species',), np.array(charges, dtype=float)) var('atom_species_names', ('number_of_atom_species', 'character_string_length'), names) var('chemical_symbols', ('number_of_atom_species', 'symbol_length'), symbols) var('pseudopotential_types', ('number_of_atom_species', 'character_string_length'), ['HGH'] * nspecies) var('fermi_energy', (), calc.occupations.fermilevel, units='atomic units') var('smearing_scheme', ('character_string_length',), 'fermi-dirac') var('smearing_width', (), calc.occupations.width, units='atomic units') var('number_of_states', ('number_of_spins', 'number_of_kpoints'), np.zeros((wfs.nspins, kd.nibzkpts), np.int32) + bd.nbands, k_dependent='no') var('eigenvalues', ('number_of_spins', 'number_of_kpoints', 'max_number_of_states'), np.array([[calc.get_eigenvalues(k, s) / Hartree for k in range(kd.nibzkpts)] for s in range(wfs.nspins)]), units='atomic units') var('occupations', ('number_of_spins', 'number_of_kpoints', 'max_number_of_states'), np.array([[calc.get_occupation_numbers(k, s) / kd.weight_k[k] for k in range(kd.nibzkpts)] for s in range(wfs.nspins)])) var('reduced_coordinates_of_kpoints', ('number_of_kpoints', 'number_of_reduced_dimensions'), kd.ibzk_kc) var('kpoint_weights', ('number_of_kpoints',), kd.weight_k) var('basis_set', ('character_string_length',), 'plane_waves') var('kinetic_energy_cutoff', (), 1.0 * ecut, units='atomic units') var('number_of_coefficients', ('number_of_kpoints',), np.zeros(kd.nibzkpts, np.int32) + len(i_Gc), k_dependent='no') var('reduced_coordinates_of_plane_waves', ('max_number_of_coefficients', 'number_of_reduced_dimensions'), i_Gc[np.argsort(G2_Q)], k_dependent='no') var('number_of_electrons', (), np.array(wfs.nvalence, dtype=np.int32)) #var('exchange_functional', ('character_string_length',), # calc.hamiltonian.xc.name) #var('correlation_functional', ('character_string_length',), # calc.hamiltonian.xc.name) psit_skn1G2 = var('coefficients_of_wavefunctions', ('number_of_spins', 'number_of_kpoints', 'max_number_of_states', 'number_of_spinor_components', 'max_number_of_coefficients', 'real_or_complex_coefficients')) x = atoms.get_volume()**0.5 / N_c.prod() psit_Gx = np.empty((len(i_Gc), 2)) for s in range(wfs.nspins): for k in range(kd.nibzkpts): for n in range(bd.nbands): psit_G = pwd.fft(calc.get_pseudo_wave_function(n, k, s))[np.argsort(G2_Q)] psit_G *= x psit_Gx[:, 0] = psit_G.real psit_Gx[:, 1] = psit_G.imag psit_skn1G2[s, k, n, 0] = psit_Gx self.nc.close()