Beispiel #1
0
    def screened_interaction_kernel(self, iq):
        """Calcuate W_GG(w) for a given q.
        if static: return W_GG(w=0)
        is not static: return W_GG(q,w) - Vc_GG
        """

        q = self.ibzq_qc[iq]
        w = self.w_w.copy() * Hartree

        optical_limit = False
        if np.abs(q).sum() < 1e-8:
            q = np.array([1e-12, 0,
                          0])  # arbitrary q, not really need to be calculated
            optical_limit = True

        hilbert_trans = True
        if self.ppa or self.static:
            hilbert_trans = False

        df = DF(calc=self.calc,
                q=q.copy(),
                w=w,
                nbands=self.nbands,
                eshift=None,
                optical_limit=optical_limit,
                hilbert_trans=hilbert_trans,
                xc='RPA',
                time_ordered=True,
                rpad=self.rpad,
                vcut=self.vcut,
                G_plus_q=True,
                eta=self.eta * Hartree,
                ecut=self.ecut.copy() * Hartree,
                txt='df.out',
                comm=self.dfcomm,
                kcommsize=self.kcommsize)

        df.initialize()
        df.e_skn = self.e_skn.copy()
        df.calculate()

        dfinv_wGG = df.get_inverse_dielectric_matrix(xc='RPA')
        assert df.ecut[0] == self.ecut[0]
        if not self.static and not self.ppa:
            assert df.eta == self.eta
            assert df.Nw == self.Nw
            assert df.dw == self.dw

        # calculate Coulomb kernel and use truncation in 2D
        delta_GG = np.eye(df.npw)
        Vq_G = np.diag(
            calculate_Kc(q,
                         df.Gvec_Gc,
                         self.acell_cv,
                         self.bcell_cv,
                         self.pbc,
                         integrate_gamma=True,
                         N_k=self.kd.N_c,
                         vcut=self.vcut))**0.5
        if (self.vcut == '2D' and df.optical_limit) or self.numint:
            for iG in range(len(df.Gvec_Gc)):
                if df.Gvec_Gc[iG, 0] == 0 and df.Gvec_Gc[iG, 1] == 0:
                    v_q, v0_q = calculate_Kc_q(self.acell_cv,
                                               self.bcell_cv,
                                               self.pbc,
                                               self.kd.N_c,
                                               vcut=self.vcut,
                                               q_qc=np.array([q]),
                                               Gvec_c=df.Gvec_Gc[iG])
                    Vq_G[iG] = v_q[0]**0.5
        self.Kc_GG = np.outer(Vq_G, Vq_G)

        if self.ppa:
            dfinv1_GG = dfinv_wGG[0] - delta_GG
            dfinv2_GG = dfinv_wGG[1] - delta_GG
            self.wt_GG = self.E0 * np.sqrt(dfinv2_GG / (dfinv1_GG - dfinv2_GG))
            self.R_GG = -self.wt_GG / 2 * dfinv1_GG
            del dfinv_wGG
            dfinv_wGG = np.array([1j * pi * self.R_GG + delta_GG])

        if self.static:
            assert len(dfinv_wGG) == 1
            W_GG = dfinv_wGG[0] * self.Kc_GG

            return df, W_GG
        else:
            Nw = np.shape(dfinv_wGG)[0]
            W_wGG = np.zeros_like(dfinv_wGG)
            for iw in range(Nw):
                dfinv_wGG[iw] -= delta_GG
                W_wGG[iw] = dfinv_wGG[iw] * self.Kc_GG

            return df, W_wGG
Beispiel #2
0
    def initialize(self):

        self.printtxt('----------------------------------------')
        self.printtxt('Bethe-Salpeter Equation calculation')
        self.printtxt('----------------------------------------')
        self.printtxt('Started at:  %s' % ctime())
        self.printtxt('')
        BASECHI.initialize(self)
        assert self.nspins == 1
        
        calc = self.calc
        self.kd = kd = calc.wfs.kd

        # frequency points init
        self.dw = self.w_w[1] - self.w_w[0]
        assert ((self.w_w[1:] - self.w_w[:-1] - self.dw) < 1e-10).all() # make sure its linear w grid
        assert self.w_w.max() == self.w_w[-1]

        self.dw /= Hartree
        self.w_w  /= Hartree
        self.wmax = self.w_w[-1] 
        self.Nw  = int(self.wmax / self.dw) + 1

        # band init
        if self.nc is None:
            nv = self.nvalence / 2 - 1
            self.nv = np.array([nv, nv+1]) # conduction band start / end
            self.nc = np.array([nv+1, nv+2]) # valence band start / end

        self.printtxt('')
        self.printtxt('Number of electrons      : %d' % (self.nvalence))
        self.printtxt('Valence band included    : (band %d to band %d)' %(self.nv[0], self.nv[1]-1))
        self.printtxt('Conduction band included : (band %d to band %d)' %(self.nc[0], self.nc[1]-1))
        if self.eshift is not None:
            self.printtxt('Scissors operator        : %2.3f eV' % self.eshift)
        self.printtxt('')
            
        # find the pair index and initialized pair energy (e_i - e_j) and occupation(f_i-f_j)
        self.e_S = {}
        focc_s = {}
        self.Sindex_S3 = {}
        iS = 0
        kq_k = self.kq_k
        for k1 in range(self.kd.nbzkpts):
            ibzkpt1 = kd.bz2ibz_k[k1]
            ibzkpt2 = kd.bz2ibz_k[kq_k[k1]]
            for n1 in range(self.nv[0], self.nv[1]): 
                for m1 in range(self.nc[0], self.nc[1]): 
                    focc = self.f_skn[0][ibzkpt1,n1] - self.f_skn[0][ibzkpt2,m1]
                    if self.coupling: # Dont use Tamm-Dancoff Approx.
                        check_ftol = np.abs(focc) > self.ftol
                    else:
                        check_ftol = focc > self.ftol
                    if check_ftol:
                        if self.gw_skn is None:
                            self.e_S[iS] = self.e_skn[0][ibzkpt2,m1] - self.e_skn[0][ibzkpt1,n1]
                        else:
                            self.e_S[iS] = self.gw_skn[0][ibzkpt2,m1] - self.gw_skn[0][ibzkpt1,n1]
                            
                        focc_s[iS] = focc
                        self.Sindex_S3[iS] = (k1, n1, m1)
                        iS += 1
        self.nS = iS
        self.focc_S = np.zeros(self.nS)
        for iS in range(self.nS):
            self.focc_S[iS] = focc_s[iS]

        # q points init
        self.bzq_qc = kd.get_bz_q_points()
        if not self.qsymm:
            self.ibzq_qc = self.bzq_qc
        else:
            (self.ibzq_qc, self.ibzq_q, self.iop_q,
             self.timerev_q, self.diff_qc) = kd.get_ibz_q_points(self.bzq_qc,
                                                                 calc.wfs.symmetry.op_scc)
            if np.abs(self.bzq_qc - kd.bzk_kc).sum() < 1e-8:
                assert np.abs(self.ibzq_qc - kd.ibzk_kc).sum() < 1e-8
        self.nibzq = len(self.ibzq_qc)

        # Parallel initialization 
        # kcomm and wScomm is only to be used when wavefunctions are distributed in parallel.
        self.comm = self.Scomm = world
        self.kcomm = world
        self.wScomm = serial_comm
        self.nS, self.nS_local, self.nS_start, self.nS_end = parallel_partition(
            self.nS, world.rank, world.size, reshape=False)

        self.print_bse()

        if calc.input_parameters['mode'] == 'lcao':
            calc.initialize_positions()

        # Coulomb interaction at q=0 for Hartree coupling
        ### 2D z direction only !!!!!!!!!!!!!!!!!!!!!!
        if self.integrate_coulomb is None:
            self.integrate_coulomb = []
            if self.vcut is None:
                pass
            elif self.vcut == '2D':
                for iG in range(len(self.Gvec_Gc)):
                    if self.Gvec_Gc[iG, 0] == 0 and self.Gvec_Gc[iG, 1] == 0:
                        self.integrate_coulomb.append(iG)
            else:
                raise NotImplementedError
        elif type(self.integrate_coulomb) is int:
            self.integrate_coulomb = range(self.integrate_coulomb)
        elif self.integrate_coulomb == 'all':
            self.integrate_coulomb = range(len(self.Gvec_Gc))
        elif type(self.integrate_coulomb) is list:
            pass
        else:
            raise 'Invalid option for integrate_coulomb'
        
        self.printtxt('')
        self.printtxt('Calculating bare Coulomb kernel')
        if not len(self.integrate_coulomb) == 0:
            self.printtxt('Integrating Coulomb kernel at %s reciprocal lattice vector(s)' % len(self.integrate_coulomb))
        
        # Coulomb interaction at problematic G's for exchange coupling
        if len(self.integrate_coulomb) != 0:
            self.vint_Gq = []
            for iG in self.integrate_coulomb:
                v_q, v0_q = calculate_Kc_q(self.acell_cv,
                                           self.bcell_cv,
                                           self.pbc,
                                           self.kd.N_c,
                                           vcut=self.vcut,
                                           Gvec_c=self.Gvec_Gc[iG],
                                           q_qc=self.ibzq_qc.copy())
                self.vint_Gq.append(v_q)                    
                if self.print_coulomb:
                    self.printtxt('')
                    self.printtxt('Average kernel relative to bare kernel - \int v(q)dq / v(q0): ')
                    self.printtxt('  G: % s' % self.Gvec_Gc[iG])
                    for iq in range(len(v_q)):
                        q_s = '    q = [%1.2f,  %1.2f,  %1.2f]: ' % (self.ibzq_qc[iq,0],
                                                                     self.ibzq_qc[iq,1],
                                                                     self.ibzq_qc[iq,2])
                        v_rel = v_q[iq] / v0_q[iq]
                        self.printtxt(q_s + '%1.3f' % v_rel)
        self.printtxt('')

        self.V_qGG = self.full_bare_interaction()

        return
Beispiel #3
0
    def screened_interaction_kernel(self, iq):
        """Calcuate W_GG(w) for a given q.
        if static: return W_GG(w=0)
        is not static: return W_GG(q,w) - Vc_GG
        """

        q = self.ibzq_qc[iq]
        w = self.w_w.copy()*Hartree

        optical_limit = False
        if np.abs(q).sum() < 1e-8:
            q = np.array([1e-12, 0, 0]) # arbitrary q, not really need to be calculated
            optical_limit = True

        hilbert_trans = True
        if self.ppa or self.static:
            hilbert_trans = False

        df = DF(calc=self.calc, q=q.copy(), w=w, nbands=self.nbands, eshift=None,
                optical_limit=optical_limit, hilbert_trans=hilbert_trans, xc='RPA', time_ordered=True,
                rpad=self.rpad, vcut=self.vcut, G_plus_q=True,
                eta=self.eta*Hartree, ecut=self.ecut.copy()*Hartree,
                txt='df.out', comm=self.dfcomm, kcommsize=self.kcommsize)

        df.initialize()
        df.e_skn = self.e_skn.copy()
        df.calculate()

        dfinv_wGG = df.get_inverse_dielectric_matrix(xc='RPA')
        assert df.ecut[0] == self.ecut[0]
        if not self.static and not self.ppa:
            assert df.eta == self.eta
            assert df.Nw == self.Nw
            assert df.dw == self.dw

        # calculate Coulomb kernel and use truncation in 2D
        delta_GG = np.eye(df.npw)
        Vq_G = np.diag(calculate_Kc(q,
                                    df.Gvec_Gc,
                                    self.acell_cv,
                                    self.bcell_cv,
                                    self.pbc,
                                    integrate_gamma=True,
                                    N_k=self.kd.N_c,
                                    vcut=self.vcut))**0.5
        if (self.vcut == '2D' and df.optical_limit) or self.numint:
            for iG in range(len(df.Gvec_Gc)):
                if df.Gvec_Gc[iG, 0] == 0 and df.Gvec_Gc[iG, 1] == 0:
                    v_q, v0_q = calculate_Kc_q(self.acell_cv,
                                               self.bcell_cv,
                                               self.pbc,
                                               self.kd.N_c,
                                               vcut=self.vcut,
                                               q_qc=np.array([q]),
                                               Gvec_c=df.Gvec_Gc[iG])
                    Vq_G[iG] = v_q[0]**0.5
        self.Kc_GG = np.outer(Vq_G, Vq_G)

        if self.ppa:
            dfinv1_GG = dfinv_wGG[0] - delta_GG
            dfinv2_GG = dfinv_wGG[1] - delta_GG
            self.wt_GG = self.E0 * np.sqrt(dfinv2_GG / (dfinv1_GG - dfinv2_GG))
            self.R_GG = - self.wt_GG / 2 * dfinv1_GG
            del dfinv_wGG
            dfinv_wGG = np.array([1j*pi*self.R_GG + delta_GG])

        if self.static:
            assert len(dfinv_wGG) == 1
            W_GG = dfinv_wGG[0] * self.Kc_GG

            return df, W_GG
        else:
            Nw = np.shape(dfinv_wGG)[0]
            W_wGG = np.zeros_like(dfinv_wGG)
            for iw in range(Nw):
                dfinv_wGG[iw] -= delta_GG
                W_wGG[iw] = dfinv_wGG[iw] * self.Kc_GG

            return df, W_wGG
Beispiel #4
0
    def initialize(self):

        self.printtxt('----------------------------------------')
        self.printtxt('Bethe-Salpeter Equation calculation')
        self.printtxt('----------------------------------------')
        self.printtxt('Started at:  %s' % ctime())
        self.printtxt('')
        BASECHI.initialize(self)
        assert self.nspins == 1

        calc = self.calc
        self.kd = kd = calc.wfs.kd

        # frequency points init
        self.dw = self.w_w[1] - self.w_w[0]
        assert ((self.w_w[1:] - self.w_w[:-1] - self.dw) <
                1e-10).all()  # make sure its linear w grid
        assert self.w_w.max() == self.w_w[-1]

        self.dw /= Hartree
        self.w_w /= Hartree
        self.wmax = self.w_w[-1]
        self.Nw = int(self.wmax / self.dw) + 1

        # band init
        if self.nc is None:
            nv = self.nvalence / 2 - 1
            self.nv = np.array([nv, nv + 1])  # conduction band start / end
            self.nc = np.array([nv + 1, nv + 2])  # valence band start / end

        self.printtxt('')
        self.printtxt('Number of electrons      : %d' % (self.nvalence))
        self.printtxt('Valence band included    : (band %d to band %d)' %
                      (self.nv[0], self.nv[1] - 1))
        self.printtxt('Conduction band included : (band %d to band %d)' %
                      (self.nc[0], self.nc[1] - 1))
        if self.eshift is not None:
            self.printtxt('Scissors operator        : %2.3f eV' % self.eshift)
        self.printtxt('')

        # find the pair index and initialized pair energy (e_i - e_j) and occupation(f_i-f_j)
        self.e_S = {}
        focc_s = {}
        self.Sindex_S3 = {}
        iS = 0
        kq_k = self.kq_k
        for k1 in range(self.kd.nbzkpts):
            ibzkpt1 = kd.bz2ibz_k[k1]
            ibzkpt2 = kd.bz2ibz_k[kq_k[k1]]
            for n1 in range(self.nv[0], self.nv[1]):
                for m1 in range(self.nc[0], self.nc[1]):
                    focc = self.f_skn[0][ibzkpt1, n1] - self.f_skn[0][ibzkpt2,
                                                                      m1]
                    if self.coupling:  # Dont use Tamm-Dancoff Approx.
                        check_ftol = np.abs(focc) > self.ftol
                    else:
                        check_ftol = focc > self.ftol
                    if check_ftol:
                        if self.gw_skn is None:
                            self.e_S[iS] = self.e_skn[0][
                                ibzkpt2, m1] - self.e_skn[0][ibzkpt1, n1]
                        else:
                            self.e_S[iS] = self.gw_skn[0][
                                ibzkpt2, m1] - self.gw_skn[0][ibzkpt1, n1]

                        focc_s[iS] = focc
                        self.Sindex_S3[iS] = (k1, n1, m1)
                        iS += 1
        self.nS = iS
        self.focc_S = np.zeros(self.nS)
        for iS in range(self.nS):
            self.focc_S[iS] = focc_s[iS]

        # q points init
        self.bzq_qc = kd.get_bz_q_points()
        if not self.qsymm:
            self.ibzq_qc = self.bzq_qc
        else:
            (self.ibzq_qc, self.ibzq_q, self.iop_q, self.timerev_q,
             self.diff_qc) = kd.get_ibz_q_points(self.bzq_qc,
                                                 calc.wfs.kd.symmetry.op_scc)
            if np.abs(self.bzq_qc - kd.bzk_kc).sum() < 1e-8:
                assert np.abs(self.ibzq_qc - kd.ibzk_kc).sum() < 1e-8
        self.nibzq = len(self.ibzq_qc)

        # Parallel initialization
        # kcomm and wScomm is only to be used when wavefunctions are distributed in parallel.
        self.comm = self.Scomm = world
        self.kcomm = world
        self.wScomm = serial_comm
        self.nS, self.nS_local, self.nS_start, self.nS_end = parallel_partition(
            self.nS, world.rank, world.size, reshape=False)

        self.print_bse()

        if calc.input_parameters['mode'] == 'lcao':
            calc.initialize_positions()

        # Coulomb interaction at q=0 for Hartree coupling
        ### 2D z direction only !!!!!!!!!!!!!!!!!!!!!!
        if self.integrate_coulomb is None:
            self.integrate_coulomb = []
            if self.vcut is None:
                pass
            elif self.vcut == '2D':
                for iG in range(len(self.Gvec_Gc)):
                    if self.Gvec_Gc[iG, 0] == 0 and self.Gvec_Gc[iG, 1] == 0:
                        self.integrate_coulomb.append(iG)
            else:
                raise NotImplementedError
        elif type(self.integrate_coulomb) is int:
            self.integrate_coulomb = range(self.integrate_coulomb)
        elif self.integrate_coulomb == 'all':
            self.integrate_coulomb = range(len(self.Gvec_Gc))
        elif type(self.integrate_coulomb) is list:
            pass
        else:
            raise 'Invalid option for integrate_coulomb'

        self.printtxt('')
        self.printtxt('Calculating bare Coulomb kernel')
        if not len(self.integrate_coulomb) == 0:
            self.printtxt(
                'Integrating Coulomb kernel at %s reciprocal lattice vector(s)'
                % len(self.integrate_coulomb))

        # Coulomb interaction at problematic G's for exchange coupling
        if len(self.integrate_coulomb) != 0:
            self.vint_Gq = []
            for iG in self.integrate_coulomb:
                v_q, v0_q = calculate_Kc_q(self.acell_cv,
                                           self.bcell_cv,
                                           self.pbc,
                                           self.kd.N_c,
                                           vcut=self.vcut,
                                           Gvec_c=self.Gvec_Gc[iG],
                                           q_qc=self.ibzq_qc.copy())
                self.vint_Gq.append(v_q)
                if self.print_coulomb:
                    self.printtxt('')
                    self.printtxt(
                        'Average kernel relative to bare kernel - \int v(q)dq / v(q0): '
                    )
                    self.printtxt('  G: % s' % self.Gvec_Gc[iG])
                    for iq in range(len(v_q)):
                        q_s = '    q = [%1.2f,  %1.2f,  %1.2f]: ' % (
                            self.ibzq_qc[iq, 0], self.ibzq_qc[iq, 1],
                            self.ibzq_qc[iq, 2])
                        v_rel = v_q[iq] / v0_q[iq]
                        self.printtxt(q_s + '%1.3f' % v_rel)
        self.printtxt('')

        self.V_qGG = self.full_bare_interaction()