def calc_es( mater, # make sure high symmetry points contain ecut=800, nbands=nbs, vacuum=12): mol = read(traj(mater)) mol.cell[-1][-1] = vacuum mol.center() calc = GPAW(mode=PW(ecut=ecut), xc="PBE", kpts=dict(gamma=True, size=(12, 12, 1)), occupations=FermiDirac(0.01), poissonsolver=dict(dipolelayer="xy")) calc.atoms = mol calc.get_potential_energy() calc.write(gs_wfs(mater), mode="all") # ES calc = GPAW(restart=gs_wfs(mater), fixdensity=True, nbands=nbands, kpts=dict(gamma=True, size=(12, 12, 1)), convergence=dict(bands=-5), parallel=dict(kpt=1)) calc.get_potential_energy() calc.diagonalize_full_hamiltonian(nbands=nbands) calc.write(es_wfs(mater), mode="all") return True
def test_bs(self): sb = StructureBuilder() atoms, *_ = sb.get_structure("C", "diamond") # print(atoms) base_dir = os.path.join(os.path.dirname(__file__), "../../tmp/C-class/") m_calc = MaterCalc(atoms=atoms, base_dir=base_dir) self.assertTrue(m_calc.relax(fmax=0.002)) # Very tight limit! self.assertTrue(m_calc.ground_state()) # get the PBE BS lattice_type = get_cellinfo(m_calc.atoms.cell).lattice self.assertTrue(lattice_type in special_paths.keys()) kpts_bs = dict(path=special_paths[lattice_type], npoints=120) # HSE06 base generate gs_file = os.path.join(base_dir, "gs.gpw") _calc = GPAW(restart=gs_file) atoms = _calc.atoms.copy() calc = GPAW(**_calc.parameters) calc.set(kpts=dict(gamma=True, density=4)) # low density calculations calc.atoms = atoms del _calc calc.get_potential_energy() calc.write(os.path.join(base_dir, "hse.gpw"), mode="all") calc = GPAW(restart=os.path.join(base_dir, "hse.gpw"), txt=None) ns = calc.get_number_of_spins() nk = len(calc.get_ibz_k_points()) nbands = calc.get_number_of_bands() eigen_pbe = numpy.array([[calc.get_eigenvalues(spin=s, kpt=k) \ for k in range(nk)]\ for s in range(ns)]) parprint("EIGEN_PBE", eigen_pbe.shape) vxc_pbe = vxc(calc, "PBE") parprint("VXC_PBE", vxc_pbe.shape) # world.barrier() # HSE06 now calc_hse = EXX(os.path.join(base_dir, "hse.gpw"), xc="HSE06", bands=[0, nbands]) calc_hse.calculate() vxc_hse = calc_hse.get_eigenvalue_contributions() parprint(vxc_hse.shape) parprint(vxc_hse) eigen_hse = eigen_pbe - vxc_pbe + vxc_hse # HSE bandgap from just kpts bg_hse_min, *_ = bandgap(eigenvalues=eigen_hse, efermi=calc.get_fermi_level(), direct=False) bg_hse_dir, *_ = bandgap(eigenvalues=eigen_hse, efermi=calc.get_fermi_level(), direct=True) parprint("HSE: E_min \t E_dir") parprint("{:.3f}\t{:.3f}".format(bg_hse_min, bg_hse_dir)) """
def test_relax_single(self): sb = StructureBuilder() atoms, *_ = sb.get_structure("C", "diamond") # print(atoms) m_calc = MaterCalc(atoms=atoms, base_dir="../../tmp/C-class/") m_calc.relax(fmax=0.002) # Very tight limit! m_calc.ground_state() # self.assertTrue(res) base_dir = "../../tmp/C-class/" gpw_name = os.path.join(base_dir, "gs.gpw") self.assertTrue(os.path.exists(gpw_name)) calc = GPAW(restart=gpw_name, txt="gp.txt") # PBE bandgap bg_pbe_min, *_ = bandgap(calc, direct=False) bg_pbe_dir, *_ = bandgap(calc, direct=True) # gllbsc calc_gllb = GPAW(**calc.parameters) calc_gllb.atoms = calc.atoms calc_gllb.set(xc="GLLBSC", txt="gllb.txt") calc_gllb.get_potential_energy() # SC calculation response = calc_gllb.hamiltonian.xc.xcs["RESPONSE"] response.calculate_delta_xc() EKs, Dxc = response.calculate_delta_xc_perturbation() gllb_gap = EKs + Dxc parprint("Eg-PBE-min, Eg-PBE-dir, Eg-gllb") parprint(bg_pbe_min, bg_pbe_dir, gllb_gap) # Use kpts? ibz_kpts = calc.get_ibz_k_points() e_kn = numpy.array([calc.get_eigenvalues(kpt=k) \ for k in range(len(ibz_kpts))]) efermi = calc.get_fermi_level() e_kn[e_kn > efermi] += Dxc gllb_gap_min = bandgap(eigenvalues=e_kn, efermi=efermi, direct=False) gllb_gap_dir = bandgap(eigenvalues=e_kn, efermi=efermi, direct=True) parprint("Efermi", efermi) parprint("Eg-gllb-min, Eg-gllb-dir") parprint(gllb_gap_min, gllb_gap_dir)
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 test_bs(self): sb = StructureBuilder() atoms, *_ = sb.get_structure("Si", "diamond") # print(atoms) base_dir = os.path.join(os.path.dirname(__file__), "../../tmp/Si-class/") m_calc = MaterCalc(atoms=atoms, base_dir=base_dir) self.assertTrue(m_calc.relax(fmax=0.002)) # Very tight limit! self.assertTrue(m_calc.ground_state()) # get the PBE BS lattice_type = get_cellinfo(m_calc.atoms.cell).lattice self.assertTrue(lattice_type in special_paths.keys()) kpts_bs = dict(path=special_paths[lattice_type], npoints=120) gpw_name = os.path.join(base_dir, "gs.gpw") self.assertTrue(os.path.exists(gpw_name)) calc_bs = GPAW(restart=gpw_name, kpts=kpts_bs, fixdensity=True, symmetry='off', txt=os.path.join(base_dir, "pbe-bs.txt")) calc_bs.get_potential_energy() # PBE bandgap bg_pbe_min, *_ = bandgap(calc_bs, direct=False) bg_pbe_dir, *_ = bandgap(calc_bs, direct=True) calc_bs.write(os.path.join(base_dir, "pbe-bs.gpw")) bs_pbe = calc_bs.band_structure() bs_pbe.plot(emin=-10, emax=10, filename=os.path.join(base_dir, "pbe-bs.png")) # get the gllbsc by steps calc_ = GPAW(restart=gpw_name) calc_gllb = GPAW(**calc_.parameters) calc_gllb.set(xc="GLLBSC", txt=os.path.join(base_dir, "gllb-gs.txt")) calc_gllb.atoms = calc_.atoms del calc_ calc_gllb.get_potential_energy() # SC calculation calc_gllb.write("gllb-gs.gpw") calc_gllb_bs = GPAW(restart="gllb-gs.gpw", kpts=kpts_bs, fixdensity=True, symmetry="off", txt=os.path.join(base_dir, "gllb-bs.txt")) world.barrier() calc_gllb_bs.get_potential_energy() homolumo = calc_gllb_bs.get_homo_lumo() bg_gllb_ks = homolumo[1] - homolumo[0] response = calc_gllb_bs.hamiltonian.xc.xcs["RESPONSE"] response.calculate_delta_xc(homolumo / Ha) EKs, Dxc = response.calculate_delta_xc_perturbation() bg_gllb_deltaxc = EKs + Dxc ibz_kpts = calc_gllb_bs.get_ibz_k_points() e_kn = numpy.array([calc_gllb_bs.get_eigenvalues(kpt=k) \ for k in range(len(ibz_kpts))]) efermi = calc_gllb_bs.get_fermi_level() e_kn[e_kn > efermi] += Dxc bg_gllb_min, *_ = bandgap(eigenvalues=e_kn, efermi=efermi, direct=False) bg_gllb_dir, *_ = bandgap(eigenvalues=e_kn, efermi=efermi, direct=True) parprint("PBE: E_min \t E_dir") parprint("{:.3f}\t{:.3f}".format(bg_pbe_min, bg_pbe_dir)) parprint("Gllb: EKS \t E_deltaxc") parprint("{:.3f}\t{:.3f}".format(bg_gllb_ks, bg_gllb_deltaxc)) parprint("Gllb: E_min \t E_dir") parprint("{:.3f}\t{:.3f}".format(bg_gllb_min, bg_gllb_dir)) bs_gllb = calc_gllb_bs.band_structure() bs_gllb.energies[bs_gllb.energies > bs_gllb.reference] += Dxc bs_gllb.plot(emin=-10, emax=10, filename=os.path.join(base_dir, "gllb-bs.png")) calc_gllb_bs.write(os.path.join(base_dir, "gllb-bs.gpw"))