예제 #1
0
파일: chi0.py 프로젝트: thonmaker/gpaw
                name = 'si.k%d.g%d.c%d.s%d' % (k, gamma, center, bool(sym))
                print(name)
                if 1:
                    calc = a.calc = GPAW(
                        kpts=kpts,
                        eigensolver='rmm-diis',
                        symmetry={'point_group': sym},
                        mode='pw',
                        occupations=FermiDirac(width=0.001),
                        txt=name + '.txt')
                    e = a.get_potential_energy()
                    calc.write(name, 'all')
                    
                calc = GPAW(name, txt=None, communicator=serial_comm)

                chi = Chi0(calc, frequencies=omega, hilbert=False,
                           ecut=100, txt=name + '.log')
                pd, chi0_wGG, _, _ = chi.calculate(q_c)
                
                if not sym and not center:
                    chi00_w = chi0_wGG[:, 0, 0]
                elif -1 not in calc.wfs.kd.bz2bz_ks:
                    assert abs(chi0_wGG[:, 0, 0] - chi00_w).max() < 3e-5
                    
                if not sym:
                    chi00_wGG = chi0_wGG
                elif -1 not in calc.wfs.kd.bz2bz_ks:
                    assert abs(chi0_wGG - chi00_wGG).max() < 2e-5

                q0_c = [0, 1e-7, 1e-7]
                q0_v = np.dot(q0_c, a.get_reciprocal_cell() * 2 * np.pi) * Bohr
                q0 = (q0_v**2).sum()**0.5
예제 #2
0
파일: df.py 프로젝트: Huaguiyuan/gpawDFT
    def __init__(self,
                 calc,
                 name=None,
                 frequencies=None,
                 domega0=0.1,
                 omega2=10.0,
                 omegamax=None,
                 ecut=50,
                 hilbert=True,
                 nbands=None,
                 eta=0.2,
                 ftol=1e-6,
                 threshold=1,
                 intraband=True,
                 nblocks=1,
                 world=mpi.world,
                 txt=sys.stdout,
                 gate_voltage=None,
                 truncation=None,
                 disable_point_group=False,
                 disable_time_reversal=False,
                 use_more_memory=1,
                 unsymmetrized=True,
                 eshift=None):
        """Creates a DielectricFunction object.

        calc: str
            The groundstate calculation file that the linear response
            calculation is based on.
        name: str
            If defined, save the density-density response function to::

                name + '%+d%+d%+d.pckl' % tuple((q_c * kd.N_c).round())

            where q_c is the reduced momentum and N_c is the number of
            kpoints along each direction.
        frequencies: np.ndarray
            Specification of frequency grid. If not set the non-linear
            frequency grid is used.
        domega0: float
            Frequency grid spacing for non-linear frequency grid at omega = 0.
        omega2: float
            Frequency at which the non-linear frequency grid has doubled
            the spacing.
        omegamax: float
            The upper frequency bound for the non-linear frequency grid.
        ecut: float
            Plane-wave cut-off.
        hilbert: bool
            Use hilbert transform.
        nbands: int
            Number of bands from calc.
        eta: float
            Broadening parameter.
        ftol: float
            Threshold for including close to equally occupied orbitals,
            f_ik - f_jk > ftol.
        threshold: float
            Threshold for matrix elements in optical response perturbation
            theory.
        intraband: bool
            Include intraband transitions.
        world: comm
            mpi communicator.
        nblocks: int
            Split matrices in nblocks blocks and distribute them G-vectors or
            frequencies over processes.
        txt: str
            Output file.
        gate_voltage: float
            Shift Fermi level of ground state calculation by the
            specified amount.
        truncation: str
            'wigner-seitz' for Wigner Seitz truncated Coulomb.
            '2D, 1D or 0d for standard analytical truncation schemes.
            Non-periodic directions are determined from k-point grid
        """

        self.chi0 = Chi0(calc,
                         frequencies,
                         domega0=domega0,
                         omega2=omega2,
                         omegamax=omegamax,
                         ecut=ecut,
                         hilbert=hilbert,
                         nbands=nbands,
                         eta=eta,
                         ftol=ftol,
                         threshold=threshold,
                         intraband=intraband,
                         world=world,
                         nblocks=nblocks,
                         txt=txt,
                         gate_voltage=gate_voltage,
                         disable_point_group=disable_point_group,
                         disable_time_reversal=disable_time_reversal,
                         use_more_memory=use_more_memory,
                         unsymmetrized=unsymmetrized,
                         eshift=eshift)

        self.name = name

        self.omega_w = self.chi0.omega_w
        nw = len(self.omega_w)

        world = self.chi0.world
        self.mynw = (nw + world.size - 1) // world.size
        self.w1 = min(self.mynw * world.rank, nw)
        self.w2 = min(self.w1 + self.mynw, nw)
        self.truncation = truncation
예제 #3
0
파일: bse.py 프로젝트: thonmaker/gpaw
    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)
예제 #4
0
                     scaled_positions=TiO2_basis,
                     cell=rutile_cell,
                     pbc=(1, 1, 1))
data_s = []
for symmetry in ['off', {}]:
    bulk_calc = GPAW(mode=PW(pwcutoff, force_complex_dtype=True),
                     kpts={
                         'size': (k, k, k),
                         'gamma': True
                     },
                     xc='PBE',
                     occupations=FermiDirac(0.00001),
                     parallel={'band': 1},
                     symmetry=symmetry)

    bulk_crystal.set_calculator(bulk_calc)
    e0_bulk_pbe = bulk_crystal.get_potential_energy()
    bulk_calc.write('bulk.gpw', mode='all')
    X = Chi0('bulk.gpw')
    chi_t = X.calculate([1. / 4, 0, 0])[1:]
    data_s.append(list(chi_t))

msg = 'Difference in Chi when turning off symmetries!'

while len(data_s):
    data1 = data_s.pop()
    for data2 in data_s:
        for dat1, dat2 in zip(data1, data2):
            if dat1 is not None:
                equal(np.abs(dat1 - dat2).max(), 0, 1e-5, msg=msg)
예제 #5
0
파일: chi0.py 프로젝트: Huaguiyuan/gpawDFT
                print(name)
                if 1:
                    calc = a.calc = GPAW(kpts=kpts,
                                         eigensolver='rmm-diis',
                                         symmetry={'point_group': sym},
                                         mode='pw',
                                         occupations=FermiDirac(width=0.001),
                                         txt=name + '.txt')
                    e = a.get_potential_energy()
                    calc.write(name, 'all')

                calc = GPAW(name, txt=None, communicator=serial_comm)

                chi = Chi0(calc,
                           omega,
                           hilbert=False,
                           ecut=100,
                           txt=name + '.log')
                pd, chi0_wGG, _, _ = chi.calculate(q_c)

                if not sym and not center:
                    chi00_w = chi0_wGG[:, 0, 0]
                elif -1 not in calc.wfs.kd.bz2bz_ks:
                    assert abs(chi0_wGG[:, 0, 0] - chi00_w).max() < 3e-5

                if not sym:
                    chi00_wGG = chi0_wGG
                elif -1 not in calc.wfs.kd.bz2bz_ks:
                    assert abs(chi0_wGG - chi00_wGG).max() < 2e-5

                q0_c = [0, 1e-7, 1e-7]
예제 #6
0
    def calculate(self, ecut, nbands=None, spin=False):
        """Calculate RPA correlation energy for one or several cutoffs.

        ecut: float or list of floats
            Plane-wave cutoff(s).
        nbands: int
            Number of bands (defaults to number of plane-waves).
        spin: bool
            Separate spin in response function.
            (Only needed for beyond RPA methods that inherit this function).
        """

        p = functools.partial(print, file=self.fd)

        if isinstance(ecut, (float, int)):
            ecut = ecut * (1 + 0.5 * np.arange(6))**(-2 / 3)
        self.ecut_i = np.asarray(np.sort(ecut)) / Hartree
        ecutmax = max(self.ecut_i)

        if nbands is None:
            p('Response function bands : Equal to number of plane waves')
        else:
            p('Response function bands : %s' % nbands)
        p('Plane wave cutoffs (eV) :', end='')
        for e in self.ecut_i:
            p(' {0:.3f}'.format(e * Hartree), end='')
        p()
        if self.truncation is not None:
            p('Using %s Coulomb truncation' % self.truncation)
        p()
            
        if self.filename and os.path.isfile(self.filename):
            self.read()
            self.world.barrier()

        chi0 = Chi0(self.calc, 1j * Hartree * self.omega_w, eta=0.0,
                    intraband=False, hilbert=False,
                    txt='chi0.txt', timer=self.timer, world=self.world,
                    nblocks=self.nblocks)

        self.blockcomm = chi0.blockcomm
        
        wfs = self.calc.wfs

        if self.truncation == 'wigner-seitz':
            self.wstc = WignerSeitzTruncatedCoulomb(wfs.gd.cell_cv,
                                                    wfs.kd.N_c, self.fd)
        else:
            self.wstc = None

        nq = len(self.energy_qi)
        nw = len(self.omega_w)
        nGmax = max(count_reciprocal_vectors(ecutmax, wfs.gd, q_c)
                    for q_c in self.ibzq_qc[nq:])
        mynGmax = (nGmax + self.nblocks - 1) // self.nblocks
        
        nx = (1 + spin) * nw * mynGmax * nGmax
        A1_x = np.empty(nx, complex)
        if self.nblocks > 1:
            A2_x = np.empty(nx, complex)
        else:
            A2_x = None
        
        self.timer.start('RPA')
        
        for q_c in self.ibzq_qc[nq:]:
            if np.allclose(q_c, 0.0) and self.skip_gamma:
                self.energy_qi.append(len(self.ecut_i) * [0.0])
                self.write()
                p('Not calculating E_c(q) at Gamma')
                p()
                continue

            thisqd = KPointDescriptor([q_c])
            pd = PWDescriptor(ecutmax, wfs.gd, complex, thisqd)
            nG = pd.ngmax
            mynG = (nG + self.nblocks - 1) // self.nblocks
            chi0.Ga = self.blockcomm.rank * mynG
            chi0.Gb = min(chi0.Ga + mynG, nG)
            
            shape = (1 + spin, nw, chi0.Gb - chi0.Ga, nG)
            chi0_swGG = A1_x[:np.prod(shape)].reshape(shape)
            chi0_swGG[:] = 0.0
            
            if np.allclose(q_c, 0.0):
                chi0_swxvG = np.zeros((1 + spin, nw, 2, 3, nG), complex)
                chi0_swvv = np.zeros((1 + spin, nw, 3, 3), complex)
            else:
                chi0_swxvG = None
                chi0_swvv = None

            # First not completely filled band:
            m1 = chi0.nocc1
            p('# %s  -  %s' % (len(self.energy_qi), ctime().split()[-2]))
            p('q = [%1.3f %1.3f %1.3f]' % tuple(q_c))

            energy_i = []
            for ecut in self.ecut_i:
                if ecut == ecutmax:
                    # Nothing to cut away:
                    cut_G = None
                    m2 = nbands or nG
                else:
                    cut_G = np.arange(nG)[pd.G2_qG[0] <= 2 * ecut]
                    m2 = len(cut_G)

                p('E_cut = %d eV / Bands = %d:' % (ecut * Hartree, m2))
                self.fd.flush()

                energy = self.calculate_q(chi0, pd,
                                          chi0_swGG, chi0_swxvG, chi0_swvv,
                                          m1, m2, cut_G, A2_x)

                energy_i.append(energy)
                m1 = m2

                a = 1 / chi0.kncomm.size
                if ecut < ecutmax and a != 1.0:
                    # Chi0 will be summed again over chicomm, so we divide
                    # by its size:
                    chi0_swGG *= a
                    if chi0_swxvG is not None:
                        chi0_swxvG *= a
                        chi0_swvv *= a

            self.energy_qi.append(energy_i)
            self.write()
            p()

        e_i = np.dot(self.weight_q, np.array(self.energy_qi))
        p('==========================================================')
        p()
        p('Total correlation energy:')
        for e_cut, e in zip(self.ecut_i, e_i):
            p('%6.0f:   %6.4f eV' % (e_cut * Hartree, e * Hartree))
        p()

        self.energy_qi = []  # important if another calculation is performed

        if len(e_i) > 1:
            self.extrapolate(e_i)

        p('Calculation completed at: ', ctime())
        p()

        self.timer.stop('RPA')
        self.timer.write(self.fd)
        self.fd.flush()
        
        return e_i * Hartree
예제 #7
0
    def calculate_screened_potential(self):
        """Calculates the screened potential for each q-point in the 1st BZ.
        Since many q-points are related by symmetry, the actual calculation is
        only done for q-points in the IBZ and the rest are obtained by symmetry
        transformations. Results are returned as a generator to that it is not
        necessary to store a huge matrix for each q-point in the memory."""
        # The decorator $timer('W') doesn't work for generators, do we will
        # have to manually start and stop the timer here:
        self.timer.start('W')
        print('Calculating screened Coulomb potential', file=self.fd)
        if self.wstc:
            print('Using Wigner-Seitz truncated Coloumb potential',
                  file=self.fd)
            
        if self.ppa:
            print('Using Godby-Needs plasmon-pole approximation:',
                  file=self.fd)
            print('    Fitting energy: i*E0, E0 = %.3f Hartee' % self.E0,
                  file=self.fd)

            # use small imaginary frequency to avoid dividing by zero:
            frequencies = [1e-10j, 1j * self.E0 * Hartree]
            
            parameters = {'eta': 0,
                          'hilbert': False,
                          'timeordered': False,
                          'frequencies': frequencies}
        else:
            print('Using full frequency integration:', file=self.fd)
            print('  domega0: {0:g}'.format(self.domega0 * Hartree),
                  file=self.fd)
            print('  omega2: {0:g}'.format(self.omega2 * Hartree),
                  file=self.fd)

            parameters = {'eta': self.eta * Hartree,
                          'hilbert': True,
                          'timeordered': True,
                          'domega0': self.domega0 * Hartree,
                          'omega2': self.omega2 * Hartree}
        
        chi0 = Chi0(self.calc,
                    nbands=self.nbands,
                    ecut=self.ecut * Hartree,
                    intraband=False,
                    real_space_derivatives=False,
                    txt=self.filename + '.w.txt',
                    timer=self.timer,
                    keep_occupied_states=True,
                    nblocks=self.blockcomm.size,
                    no_optical_limit=self.wstc,
                    **parameters)

        if self.wstc:
            wstc = WignerSeitzTruncatedCoulomb(
                self.calc.wfs.gd.cell_cv,
                self.calc.wfs.kd.N_c,
                chi0.fd)
        else:
            wstc = None
        
        self.omega_w = chi0.omega_w
        self.omegamax = chi0.omegamax
        
        htp = HilbertTransform(self.omega_w, self.eta, gw=True)
        htm = HilbertTransform(self.omega_w, -self.eta, gw=True)

        # Find maximum size of chi-0 matrices:
        gd = self.calc.wfs.gd
        nGmax = max(count_reciprocal_vectors(self.ecut, gd, q_c)
                    for q_c in self.qd.ibzk_kc)
        nw = len(self.omega_w)
        
        size = self.blockcomm.size
        mynGmax = (nGmax + size - 1) // size
        mynw = (nw + size - 1) // size
        
        # Allocate memory in the beginning and use for all q:
        A1_x = np.empty(nw * mynGmax * nGmax, complex)
        A2_x = np.empty(max(mynw * nGmax, nw * mynGmax) * nGmax, complex)
        
        # Need to pause the timer in between iterations
        self.timer.stop('W')
        for iq, q_c in enumerate(self.qd.ibzk_kc):
            self.timer.start('W')
            if self.savew:
                wfilename = self.filename + '.w.q%d.pckl' % iq
                fd = opencew(wfilename)
            if self.savew and fd is None:
                # Read screened potential from file
                with open(wfilename) as fd:
                    pd, W = pickle.load(fd)
            else:
                # First time calculation
                pd, W = self.calculate_w(chi0, q_c, htp, htm, wstc, A1_x, A2_x)
                if self.savew:
                    pickle.dump((pd, W), fd, pickle.HIGHEST_PROTOCOL)

            self.timer.stop('W')
            # Loop over all k-points in the BZ and find those that are related
            # to the current IBZ k-point by symmetry
            Q1 = self.qd.ibz2bz_k[iq]
            done = set()
            for s, Q2 in enumerate(self.qd.bz2bz_ks[Q1]):
                if Q2 >= 0 and Q2 not in done:
                    s = self.qd.sym_k[Q2]
                    self.s = s
                    self.U_cc = self.qd.symmetry.op_scc[s]
                    time_reversal = self.qd.time_reversal_k[Q2]
                    self.sign = 1 - 2 * time_reversal
                    Q_c = self.qd.bzk_kc[Q2]
                    d_c = self.sign * np.dot(self.U_cc, q_c) - Q_c
                    assert np.allclose(d_c.round(), d_c)
                    yield pd, W, Q_c
                    done.add(Q2)