예제 #1
0
    def add_potential_correction(self, v_R, alpha):
        dens = self.calc.density
        dens.D_asp.redistribute(dens.atom_partition.as_serial())
        dens.Q_aL.redistribute(dens.atom_partition.as_serial())

        dv_a1 = []
        for a, D_sp in dens.D_asp.items():
            setup = dens.setups[a]
            c = setup.xc_correction
            rgd = c.rgd
            ghat_g = gauss(rgd, 1 / setup.rcgauss**2)
            Z_g = gauss(rgd, alpha) * setup.Z
            D_q = np.dot(D_sp.sum(0), c.B_pqL[:, :, 0])
            dn_g = np.dot(D_q, (c.n_qg - c.nt_qg)) * sqrt(4 * pi)
            dn_g += 4 * pi * (c.nc_g - c.nct_g)
            dn_g -= Z_g
            dn_g -= dens.Q_aL[a][0] * ghat_g * sqrt(4 * pi)
            dv_g = rgd.poisson(dn_g) / sqrt(4 * pi)
            dv_g[1:] /= rgd.r_g[1:]
            dv_g[0] = dv_g[1]
            dv_g[-1] = 0.0
            dv_a1.append([rgd.spline(dv_g, points=POINTS)])

        dens.D_asp.redistribute(dens.atom_partition)
        dens.Q_aL.redistribute(dens.atom_partition)

        if dv_a1:
            dv = LFC(self.gd, dv_a1)
            dv.set_positions(self.calc.spos_ac)
            dv.add(v_R)
        dens.gd.comm.broadcast(v_R, 0)
예제 #2
0
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
예제 #3
0
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 RealSpaceHamiltonian(Hamiltonian):
    def __init__(
        self,
        gd,
        finegd,
        nspins,
        setups,
        timer,
        xc,
        world,
        kptband_comm,
        vext=None,
        collinear=True,
        psolver=None,
        stencil=3,
    ):
        Hamiltonian.__init__(self, gd, finegd, nspins, setups, timer, xc, world, kptband_comm, vext, collinear)

        # Solver for the Poisson equation:
        if psolver is None:
            psolver = PoissonSolver(nn=3, relax="J")
        self.poisson = psolver
        self.poisson.set_grid_descriptor(finegd)

        # Restrictor function for the potential:
        self.restrictor = Transformer(self.finegd, self.gd, stencil)
        self.restrict = self.restrictor.apply

        self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups], forces=True)
        self.vbar_g = None

    def summary(self, fd):
        Hamiltonian.summary(self, fd)

        degree = self.restrictor.nn * 2 - 1
        name = ["linear", "cubic", "quintic", "heptic"][degree // 2]
        fd.write("Interpolation: tri-%s " % name + "(%d. degree polynomial)\n" % degree)

        fd.write("Poisson solver: %s\n" % self.poisson.get_description())

    def set_positions(self, spos_ac, rank_a):
        Hamiltonian.set_positions(self, spos_ac, rank_a)
        if self.vbar_g is None:
            self.vbar_g = self.finegd.empty()
        self.vbar_g[:] = 0.0
        self.vbar.add(self.vbar_g)

    def update_pseudo_potential(self, density):
        self.timer.start("vbar")
        Ebar = self.finegd.integrate(self.vbar_g, density.nt_g, global_integral=False)

        vt_g = self.vt_sg[0]
        vt_g[:] = self.vbar_g
        self.timer.stop("vbar")

        Eext = 0.0
        if self.vext is not None:
            assert self.collinear
            vt_g += self.vext.get_potential(self.finegd)
            Eext = self.finegd.integrate(vt_g, density.nt_g, global_integral=False) - Ebar

        self.vt_sg[1 : self.nspins] = vt_g

        self.vt_sg[self.nspins :] = 0.0

        self.timer.start("XC 3D grid")
        Exc = self.xc.calculate(self.finegd, density.nt_sg, self.vt_sg)
        Exc /= self.gd.comm.size
        self.timer.stop("XC 3D grid")

        self.timer.start("Poisson")
        # npoisson is the number of iterations:
        self.npoisson = self.poisson.solve(self.vHt_g, density.rhot_g, charge=-density.charge)
        self.timer.stop("Poisson")

        self.timer.start("Hartree integrate/restrict")
        Epot = 0.5 * self.finegd.integrate(self.vHt_g, density.rhot_g, global_integral=False)

        Ekin = 0.0
        s = 0
        for s, (vt_g, vt_G, nt_G) in enumerate(zip(self.vt_sg, self.vt_sG, density.nt_sG)):
            if s < self.nspins:
                vt_g += self.vHt_g

            self.restrict(vt_g, vt_G)
            if self.ref_vt_sG is not None:
                vt_G += self.ref_vt_sG[s]

            if s < self.nspins:
                Ekin -= self.gd.integrate(vt_G, nt_G - density.nct_G, global_integral=False)
            else:
                Ekin -= self.gd.integrate(vt_G, nt_G, global_integral=False)
            s += 1

        self.timer.stop("Hartree integrate/restrict")

        # Calculate atomic hamiltonians:
        W_aL = {}
        for a in density.D_asp:
            W_aL[a] = np.empty((self.setups[a].lmax + 1) ** 2)
        density.ghat.integrate(self.vHt_g, W_aL)

        return Ekin, Epot, Ebar, Eext, Exc, W_aL

    def calculate_forces2(self, dens, ghat_aLv, nct_av, vbar_av):
        if self.nspins == 2:
            vt_G = self.vt_sG.mean(0)
        else:
            vt_G = self.vt_sG[0]

        dens.ghat.derivative(self.vHt_g, ghat_aLv)
        dens.nct.derivative(vt_G, nct_av)
        self.vbar.derivative(dens.nt_g, vbar_av)
예제 #5
0
class Hamiltonian:
    """Hamiltonian object.

    Attributes:
     =============== =====================================================
     ``xc``          ``XC3DGrid`` object.
     ``poisson``     ``PoissonSolver``.
     ``gd``          Grid descriptor for coarse grids.
     ``finegd``      Grid descriptor for fine grids.
     ``restrict``    Function for restricting the effective potential.
     =============== =====================================================

    Soft and smooth pseudo functions on uniform 3D grids:
     ========== =========================================
     ``vHt_g``  Hartree potential on the fine grid.
     ``vt_sG``  Effective potential on the coarse grid.
     ``vt_sg``  Effective potential on the fine grid.
     ========== =========================================

    Energy contributions and forces:

    =========== ==========================================
                Description
    =========== ==========================================
    ``Ekin``    Kinetic energy.
    ``Epot``    Potential energy.
    ``Etot``    Total energy.
    ``Exc``     Exchange-Correlation energy.
    ``Eext``    Energy of external potential
    ``Eref``    Reference energy for all-electron atoms.
    ``S``       Entropy.
    ``Ebar``    Should be close to zero!
    =========== ==========================================

    """

    def __init__(self, gd, finegd, nspins, setups, stencil, timer, xc,
                 psolver, vext_g):
        """Create the Hamiltonian."""
        self.gd = gd
        self.finegd = finegd
        self.nspins = nspins
        self.setups = setups
        self.timer = timer
        self.xc = xc
        
        # Solver for the Poisson equation:
        if psolver is None:
            psolver = PoissonSolver(nn=3, relax='J')
        self.poisson = psolver
        self.poisson.set_grid_descriptor(finegd)

        self.dH_asp = None

        # The external potential
        self.vext_g = vext_g

        self.vt_sG = None
        self.vHt_g = None
        self.vt_sg = None
        self.vbar_g = None

        self.rank_a = None

        # Restrictor function for the potential:
        self.restrictor = Transformer(self.finegd, self.gd, stencil,
                                      allocate=False)
        self.restrict = self.restrictor.apply

        self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups],
                        forces=True)

        self.Ekin0 = None
        self.Ekin = None
        self.Epot = None
        self.Ebar = None
        self.Eext = None
        self.Exc = None
        self.Etot = None
        self.S = None
        self.allocated = False

    def allocate(self):
        # TODO We should move most of the gd.empty() calls here
        assert not self.allocated
        self.restrictor.allocate()
        self.allocated = True

    def set_positions(self, spos_ac, rank_a=None):
        self.spos_ac = spos_ac
        if not self.allocated:
            self.allocate()
        self.vbar.set_positions(spos_ac)
        if self.vbar_g is None:
            self.vbar_g = self.finegd.empty()
        self.vbar_g[:] = 0.0
        self.vbar.add(self.vbar_g)

        self.xc.set_positions(spos_ac)
        
        # If both old and new atomic ranks are present, start a blank dict if
        # it previously didn't exist but it will needed for the new atoms.
        if (self.rank_a is not None and rank_a is not None and
            self.dH_asp is None and (rank_a == self.gd.comm.rank).any()):
            self.dH_asp = {}

        if self.rank_a is not None and self.dH_asp is not None:
            self.timer.start('Redistribute')
            requests = []
            flags = (self.rank_a != rank_a)
            my_incoming_atom_indices = np.argwhere(np.bitwise_and(flags, \
                rank_a == self.gd.comm.rank)).ravel()
            my_outgoing_atom_indices = np.argwhere(np.bitwise_and(flags, \
                self.rank_a == self.gd.comm.rank)).ravel()

            for a in my_incoming_atom_indices:
                # Get matrix from old domain:
                ni = self.setups[a].ni
                dH_sp = np.empty((self.nspins, ni * (ni + 1) // 2))
                requests.append(self.gd.comm.receive(dH_sp, self.rank_a[a],
                                                     tag=a, block=False))
                assert a not in self.dH_asp
                self.dH_asp[a] = dH_sp

            for a in my_outgoing_atom_indices:
                # Send matrix to new domain:
                dH_sp = self.dH_asp.pop(a)
                requests.append(self.gd.comm.send(dH_sp, rank_a[a],
                                                  tag=a, block=False))
            self.gd.comm.waitall(requests)
            self.timer.stop('Redistribute')

        self.rank_a = rank_a

    def aoom(self, DM, a, l, scale=1):
        """Atomic Orbital Occupation Matrix.
        
        Determine the Atomic Orbital Occupation Matrix (aoom) for a
        given l-quantum number.
        
        This operation, takes the density matrix (DM), which for
        example is given by unpack2(D_asq[i][spin]), and corrects for
        the overlap between the selected orbitals (l) upon which the
        the density is expanded (ex <p|p*>,<p|p>,<p*|p*> ).

        Returned is only the "corrected" part of the density matrix,
        which represents the orbital occupation matrix for l=2 this is
        a 5x5 matrix.
        """
        S=self.setups[a]
        l_j = S.l_j
        n_j = S.n_j
        lq  = S.lq
        nl  = np.where(np.equal(l_j, l))[0]
        V = np.zeros(np.shape(DM))
        if len(nl) == 2:
            aa = (nl[0])*len(l_j)-((nl[0]-1)*(nl[0])/2)
            bb = (nl[1])*len(l_j)-((nl[1]-1)*(nl[1])/2)
            ab = aa+nl[1]-nl[0]
            
            if(scale==0 or scale=='False' or scale =='false'):
                lq_a  = lq[aa]
                lq_ab = lq[ab]
                lq_b  = lq[bb]
            else:
                lq_a  = 1
                lq_ab = lq[ab]/lq[aa]
                lq_b  = lq[bb]/lq[aa]
 
            # and the correct entrances in the DM
            nn = (2*np.array(l_j)+1)[0:nl[0]].sum()
            mm = (2*np.array(l_j)+1)[0:nl[1]].sum()
            
            # finally correct and add the four submatrices of NC_DM
            A = DM[nn:nn+2*l+1,nn:nn+2*l+1]*(lq_a)
            B = DM[nn:nn+2*l+1,mm:mm+2*l+1]*(lq_ab)
            C = DM[mm:mm+2*l+1,nn:nn+2*l+1]*(lq_ab)
            D = DM[mm:mm+2*l+1,mm:mm+2*l+1]*(lq_b)
            
            V[nn:nn+2*l+1,nn:nn+2*l+1]=+(lq_a)
            V[nn:nn+2*l+1,mm:mm+2*l+1]=+(lq_ab)
            V[mm:mm+2*l+1,nn:nn+2*l+1]=+(lq_ab)
            V[mm:mm+2*l+1,mm:mm+2*l+1]=+(lq_b)
 
            return  A+B+C+D, V
        else:
            nn =(2*np.array(l_j)+1)[0:nl[0]].sum()
            A=DM[nn:nn+2*l+1,nn:nn+2*l+1]*lq[-1]
            V[nn:nn+2*l+1,nn:nn+2*l+1]=+lq[-1]
            return A,V

    def update(self, density):
        """Calculate effective potential.

        The XC-potential and the Hartree potential are evaluated on
        the fine grid, and the sum is then restricted to the coarse
        grid."""

        self.timer.start('Hamiltonian')

        if self.vt_sg is None:
            self.timer.start('Initialize Hamiltonian')
            self.vt_sg = self.finegd.empty(self.nspins)
            self.vHt_g = self.finegd.zeros()
            self.vt_sG = self.gd.empty(self.nspins)
            self.poisson.initialize()
            self.timer.stop('Initialize Hamiltonian')

        self.timer.start('vbar')
        Ebar = self.finegd.integrate(self.vbar_g, density.nt_g,
                                     global_integral=False)

        vt_g = self.vt_sg[0]
        vt_g[:] = self.vbar_g
        self.timer.stop('vbar')

        Eext = 0.0
        if self.vext_g is not None:
            vt_g += self.vext_g.get_potential(self.finegd)
            Eext = self.finegd.integrate(vt_g, density.nt_g,
                                         global_integral=False) - Ebar

        if self.nspins == 2:
            self.vt_sg[1] = vt_g

        self.timer.start('XC 3D grid')
        Exc = self.xc.calculate(self.finegd, density.nt_sg, self.vt_sg)
        Exc /= self.gd.comm.size
        self.timer.stop('XC 3D grid')

        self.timer.start('Poisson')
        # npoisson is the number of iterations:
        self.npoisson = self.poisson.solve(self.vHt_g, density.rhot_g,
                                           charge=-density.charge)
        self.timer.stop('Poisson')

        self.timer.start('Hartree integrate/restrict')
        Epot = 0.5 * self.finegd.integrate(self.vHt_g, density.rhot_g,
                                           global_integral=False)
        Ekin = 0.0
        for vt_g, vt_G, nt_G in zip(self.vt_sg, self.vt_sG, density.nt_sG):
            vt_g += self.vHt_g
            self.restrict(vt_g, vt_G)
            Ekin -= self.gd.integrate(vt_G, nt_G - density.nct_G,
                                      global_integral=False)
        self.timer.stop('Hartree integrate/restrict')
            
        # Calculate atomic hamiltonians:
        self.timer.start('Atomic')
        W_aL = {}
        for a in density.D_asp:
            W_aL[a] = np.empty((self.setups[a].lmax + 1)**2)
        density.ghat.integrate(self.vHt_g, W_aL)
        self.dH_asp = {}
        for a, D_sp in density.D_asp.items():
            W_L = W_aL[a]
            setup = self.setups[a]

            D_p = D_sp.sum(0)
            dH_p = (setup.K_p + setup.M_p +
                    setup.MB_p + 2.0 * np.dot(setup.M_pp, D_p) +
                    np.dot(setup.Delta_pL, W_L))
            Ekin += np.dot(setup.K_p, D_p) + setup.Kc
            Ebar += setup.MB + np.dot(setup.MB_p, D_p)
            Epot += setup.M + np.dot(D_p, (setup.M_p +
                                           np.dot(setup.M_pp, D_p)))

            if self.vext_g is not None:
                vext = self.vext_g.get_taylor(spos_c=self.spos_ac[a, :])
                # Tailor expansion to the zeroth order
                Eext += vext[0][0] * (sqrt(4 * pi) * density.Q_aL[a][0]
                                      + setup.Z)
                dH_p += vext[0][0] * sqrt(4 * pi) * setup.Delta_pL[:, 0]
                if len(vext) > 1:
                    # Tailor expansion to the first order
                    Eext += sqrt(4 * pi / 3) * np.dot(vext[1],
                                                      density.Q_aL[a][1:4])
                    # there must be a better way XXXX
                    Delta_p1 = np.array([setup.Delta_pL[:, 1],
                                          setup.Delta_pL[:, 2],
                                          setup.Delta_pL[:, 3]])
                    dH_p += sqrt(4 * pi / 3) * np.dot(vext[1], Delta_p1)

            self.dH_asp[a] = dH_sp = np.zeros_like(D_sp)
            self.timer.start('XC Correction')
            Exc += setup.xc_correction.calculate(self.xc, D_sp, dH_sp, a)
            self.timer.stop('XC Correction')

            if setup.HubU is not None:
                nspins = len(D_sp)
                
                l_j = setup.l_j
                l   = setup.Hubl
                nl  = np.where(np.equal(l_j,l))[0]
                nn  = (2*np.array(l_j)+1)[0:nl[0]].sum()
                
                for D_p, H_p in zip(D_sp, self.dH_asp[a]):
                    [N_mm,V] =self.aoom(unpack2(D_p),a,l)
                    N_mm = N_mm / 2 * nspins
                     
                    Eorb = setup.HubU / 2. * (N_mm - np.dot(N_mm,N_mm)).trace()
                    Vorb = setup.HubU * (0.5 * np.eye(2*l+1) - N_mm)
                    Exc += Eorb
                    if nspins == 1:
                        # add contribution of other spin manyfold
                        Exc += Eorb
                    
                    if len(nl)==2:
                        mm  = (2*np.array(l_j)+1)[0:nl[1]].sum()
                        
                        V[nn:nn+2*l+1,nn:nn+2*l+1] *= Vorb
                        V[mm:mm+2*l+1,nn:nn+2*l+1] *= Vorb
                        V[nn:nn+2*l+1,mm:mm+2*l+1] *= Vorb
                        V[mm:mm+2*l+1,mm:mm+2*l+1] *= Vorb
                    else:
                        V[nn:nn+2*l+1,nn:nn+2*l+1] *= Vorb
                    
                    Htemp = unpack(H_p)
                    Htemp += V
                    H_p[:] = pack2(Htemp)

            dH_sp += dH_p

            Ekin -= (D_sp * dH_sp).sum()

        self.timer.stop('Atomic')

        # Make corrections due to non-local xc:
        #xcfunc = self.xc.xcfunc
        self.Enlxc = 0.0#XXXxcfunc.get_non_local_energy()
        Ekin += self.xc.get_kinetic_energy_correction() / self.gd.comm.size

        energies = np.array([Ekin, Epot, Ebar, Eext, Exc])
        self.timer.start('Communicate energies')
        self.gd.comm.sum(energies)
        self.timer.stop('Communicate energies')
        (self.Ekin0, self.Epot, self.Ebar, self.Eext, self.Exc) = energies

        #self.Exc += self.Enlxc
        #self.Ekin0 += self.Enlkin

        self.timer.stop('Hamiltonian')

    def get_energy(self, occupations):
        self.Ekin = self.Ekin0 + occupations.e_band
        self.S = occupations.e_entropy

        # Total free energy:
        self.Etot = (self.Ekin + self.Epot + self.Eext +
                     self.Ebar + self.Exc - self.S)

        return self.Etot

    def apply_local_potential(self, psit_nG, Htpsit_nG, s):
        """Apply the Hamiltonian operator to a set of vectors.

        XXX Parameter description is deprecated!
        
        Parameters:

        a_nG: ndarray
            Set of vectors to which the overlap operator is applied.
        b_nG: ndarray, output
            Resulting H times a_nG vectors.
        kpt: KPoint object
            k-point object defined in kpoint.py.
        calculate_projections: bool
            When True, the integrals of projector times vectors
            P_ni = <p_i | a_nG> are calculated.
            When False, existing P_uni are used
        local_part_only: bool
            When True, the non-local atomic parts of the Hamiltonian
            are not applied and calculate_projections is ignored.
        
        """
        vt_G = self.vt_sG[s]
        if psit_nG.ndim == 3:
            Htpsit_nG += psit_nG * vt_G
        else:
            for psit_G, Htpsit_G in zip(psit_nG, Htpsit_nG):
                Htpsit_G += psit_G * vt_G

    def apply(self, a_xG, b_xG, wfs, kpt, calculate_P_ani=True):
        """Apply the Hamiltonian operator to a set of vectors.

        Parameters:

        a_nG: ndarray
            Set of vectors to which the overlap operator is applied.
        b_nG: ndarray, output
            Resulting S times a_nG vectors.
        wfs: WaveFunctions
            Wave-function object defined in wavefunctions.py
        kpt: KPoint object
            k-point object defined in kpoint.py.
        calculate_P_ani: bool
            When True, the integrals of projector times vectors
            P_ni = <p_i | a_nG> are calculated.
            When False, existing P_ani are used
        
        """

        wfs.kin.apply(a_xG, b_xG, kpt.phase_cd)
        self.apply_local_potential(a_xG, b_xG, kpt.s)
        shape = a_xG.shape[:-3]
        P_axi = wfs.pt.dict(shape)

        if calculate_P_ani: #TODO calculate_P_ani=False is experimental
            wfs.pt.integrate(a_xG, P_axi, kpt.q)
        else:
            for a, P_ni in kpt.P_ani.items():
                P_axi[a][:] = P_ni

        for a, P_xi in P_axi.items():
            dH_ii = unpack(self.dH_asp[a][kpt.s])
            P_axi[a] = np.dot(P_xi, dH_ii)
        wfs.pt.add(b_xG, P_axi, kpt.q)

    def get_xc_difference(self, xc, density):
        """Calculate non-selfconsistent XC-energy difference."""
        if density.nt_sg is None:
            density.interpolate()
        nt_sg = density.nt_sg
        if hasattr(xc, 'hybrid'):
            xc.calculate_exx()
        Exc = xc.calculate(density.finegd, nt_sg) / self.gd.comm.size
        for a, D_sp in density.D_asp.items():
            setup = self.setups[a]
            Exc += setup.xc_correction.calculate(xc, D_sp)
        Exc = self.gd.comm.sum(Exc)
        return Exc - self.Exc

    def get_vxc(self, density, wfs):
        """Calculate matrix elements of the xc-potential."""
        dtype = wfs.dtype
        nbands = wfs.nbands
        nu = len(wfs.kpt_u)
        if density.nt_sg is None:
            density.interpolate()

        # Allocate space for result matrix
        Vxc_unn = np.empty((nu, nbands, nbands), dtype=dtype)

        # Get pseudo xc potential on the coarse grid
        Vxct_sG = self.gd.empty(self.nspins)
        Vxct_sg = self.finegd.zeros(self.nspins)
        if nspins == 1:
            self.xc.get_energy_and_potential(density.nt_sg[0], Vxct_sg[0])
        else:
            self.xc.get_energy_and_potential(density.nt_sg[0], Vxct_sg[0],
                                             density.nt_sg[1], Vxct_sg[1])
        for Vxct_G, Vxct_g in zip(Vxct_sG, Vxct_sg):
            self.restrict(Vxct_g, Vxct_G)
        del Vxct_sg

        # Get atomic corrections to the xc potential
        Vxc_asp = {}
        for a, D_sp in density.D_asp.items():
            Vxc_asp[a] = np.zeros_like(D_sp)
            self.setups[a].xc_correction.calculate_energy_and_derivatives(
                D_sp, Vxc_asp[a])

        # Project potential onto the eigenstates
        for kpt, Vxc_nn in xip(wfs.kpt_u, Vxc_unn):
            s, q = kpt.s, kpt.q
            psit_nG = kpt.psit_nG

            # Project pseudo part
            r2k(.5 * self.gd.dv, psit_nG, Vxct_sG[s] * psit_nG, 0.0, Vxc_nn)
            tri2full(Vxc_nn, 'L')
            self.gd.comm.sum(Vxc_nn)

            # Add atomic corrections
            # H_ij = \int dr phi_i(r) Ĥ phi_j^*(r)
            # P_ni = \int dr psi_n(r) pt_i^*(r)
            # Vxc_nm = \int dr phi_n(r) vxc(r) phi_m^*(r)
            #      + sum_ij P_ni H_ij P_mj^*
            for a, P_ni in kpt.P_ani.items():
                Vxc_ii = unpack(Vxc_asp[a][s])
                Vxc_nn += np.dot(P_ni, np.inner(H_ii, P_ni).conj())
        return Vxc_unn

    def estimate_memory(self, mem):
        nbytes = self.gd.bytecount()
        nfinebytes = self.finegd.bytecount()
        arrays = mem.subnode('Arrays', 0)
        arrays.subnode('vHt_g', nfinebytes)
        arrays.subnode('vt_sG', self.nspins * nbytes)
        arrays.subnode('vt_sg', self.nspins * nfinebytes)
        self.restrictor.estimate_memory(mem.subnode('Restrictor'))
        self.xc.estimate_memory(mem.subnode('XC'))
        self.poisson.estimate_memory(mem.subnode('Poisson'))
        self.vbar.estimate_memory(mem.subnode('vbar'))
예제 #6
0
class Density:
    """Density object.
    
    Attributes:
     =============== =====================================================
     ``gd``          Grid descriptor for coarse grids.
     ``finegd``      Grid descriptor for fine grids.
     ``interpolate`` Function for interpolating the electron density.
     ``mixer``       ``DensityMixer`` object.
     =============== =====================================================

    Soft and smooth pseudo functions on uniform 3D grids:
     ========== =========================================
     ``nt_sG``  Electron density on the coarse grid.
     ``nt_sg``  Electron density on the fine grid.
     ``nt_g``   Electron density on the fine grid.
     ``rhot_g`` Charge density on the fine grid.
     ``nct_G``  Core electron-density on the coarse grid.
     ========== =========================================
    """
    
    def __init__(self, gd, finegd, nspins, charge):
        """Create the Density object."""

        self.gd = gd
        self.finegd = finegd
        self.nspins = nspins
        self.charge = float(charge)

        self.charge_eps = 1e-7
        
        self.D_asp = None
        self.Q_aL = None

        self.nct_G = None
        self.nt_sG = None
        self.rhot_g = None
        self.nt_sg = None
        self.nt_g = None

        self.rank_a = None

        self.mixer = BaseMixer()
        self.timer = nulltimer
        self.allocated = False
        
    def initialize(self, setups, stencil, timer, magmom_a, hund):
        self.timer = timer
        self.setups = setups
        self.hund = hund
        self.magmom_a = magmom_a
        
        # Interpolation function for the density:
        self.interpolator = Transformer(self.gd, self.finegd, stencil,
                                        allocate=False)
        
        spline_aj = []
        for setup in setups:
            if setup.nct is None:
                spline_aj.append([])
            else:
                spline_aj.append([setup.nct])
        self.nct = LFC(self.gd, spline_aj,
                       integral=[setup.Nct for setup in setups],
                       forces=True, cut=True)
        self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups],
                        integral=sqrt(4 * pi), forces=True)
        if self.allocated:
            self.allocated = False
            self.allocate()

    def allocate(self):
        assert not self.allocated
        self.interpolator.allocate()
        self.allocated = True

    def reset(self):
        # TODO: reset other parameters?
        self.nt_sG = None

    def set_positions(self, spos_ac, rank_a=None):
        if not self.allocated:
            self.allocate()
        self.nct.set_positions(spos_ac)
        self.ghat.set_positions(spos_ac)
        self.mixer.reset()

        self.nct_G = self.gd.zeros()
        self.nct.add(self.nct_G, 1.0 / self.nspins)
        #self.nt_sG = None
        self.nt_sg = None
        self.nt_g = None
        self.rhot_g = None
        self.Q_aL = None

        # If both old and new atomic ranks are present, start a blank dict if
        # it previously didn't exist but it will needed for the new atoms.
        if (self.rank_a is not None and rank_a is not None and
            self.D_asp is None and (rank_a == self.gd.comm.rank).any()):
            self.D_asp = {}

        if self.rank_a is not None and self.D_asp is not None:
            self.timer.start('Redistribute')
            requests = []
            flags = (self.rank_a != rank_a)
            my_incoming_atom_indices = np.argwhere(np.bitwise_and(flags, \
                rank_a == self.gd.comm.rank)).ravel()
            my_outgoing_atom_indices = np.argwhere(np.bitwise_and(flags, \
                self.rank_a == self.gd.comm.rank)).ravel()

            for a in my_incoming_atom_indices:
                # Get matrix from old domain:
                ni = self.setups[a].ni
                D_sp = np.empty((self.nspins, ni * (ni + 1) // 2))
                requests.append(self.gd.comm.receive(D_sp, self.rank_a[a],
                                                     tag=a, block=False))
                assert a not in self.D_asp
                self.D_asp[a] = D_sp

            for a in my_outgoing_atom_indices:
                # Send matrix to new domain:
                D_sp = self.D_asp.pop(a)
                requests.append(self.gd.comm.send(D_sp, rank_a[a],
                                                  tag=a, block=False))
            self.gd.comm.waitall(requests)
            self.timer.stop('Redistribute')

        self.rank_a = rank_a

    def calculate_pseudo_density(self, wfs):
        """Calculate nt_sG from scratch.

        nt_sG will be equal to nct_G plus the contribution from
        wfs.add_to_density().
        """
        wfs.calculate_density_contribution(self.nt_sG)
        self.nt_sG += self.nct_G

    def update(self, wfs):
        self.timer.start('Density')
        self.timer.start('Pseudo density')
        self.calculate_pseudo_density(wfs)
        self.timer.stop('Pseudo density')
        self.timer.start('Atomic density matrices')
        wfs.calculate_atomic_density_matrices(self.D_asp)
        self.timer.stop('Atomic density matrices')
        self.timer.start('Multipole moments')
        comp_charge = self.calculate_multipole_moments()
        self.timer.stop('Multipole moments')
        
        if isinstance(wfs, LCAOWaveFunctions):
            self.timer.start('Normalize')
            self.normalize(comp_charge)
            self.timer.stop('Normalize')

        self.timer.start('Mix')
        self.mix(comp_charge)
        self.timer.stop('Mix')
        self.timer.stop('Density')

    def normalize(self, comp_charge=None):
        """Normalize pseudo density."""
        if comp_charge is None:
            comp_charge = self.calculate_multipole_moments()
        
        pseudo_charge = self.gd.integrate(self.nt_sG).sum()

        if pseudo_charge + self.charge + comp_charge != 0:
            if pseudo_charge != 0:
                x = -(self.charge + comp_charge) / pseudo_charge
                self.nt_sG *= x
            else:
                # Use homogeneous background:
                self.nt_sG[:] = (self.charge + comp_charge) * self.gd.dv

    def calculate_pseudo_charge(self, comp_charge):
        self.nt_g = self.nt_sg.sum(axis=0)
        self.rhot_g = self.nt_g.copy()
        self.ghat.add(self.rhot_g, self.Q_aL)

        if debug:
            charge = self.finegd.integrate(self.rhot_g) + self.charge
            if abs(charge) > self.charge_eps:
                raise RuntimeError('Charge not conserved: excess=%.9f' %
                                   charge)

    def mix(self, comp_charge):
        if not self.mixer.mix_rho:
            self.mixer.mix(self)
            comp_charge = None
          
        self.interpolate(comp_charge)
        self.calculate_pseudo_charge(comp_charge)

        if self.mixer.mix_rho:
            self.mixer.mix(self)

    def interpolate(self, comp_charge=None):
        """Interpolate pseudo density to fine grid."""
        if comp_charge is None:
            comp_charge = self.calculate_multipole_moments()

        if self.nt_sg is None:
            self.nt_sg = self.finegd.empty(self.nspins)

        for s in range(self.nspins):
            self.interpolator.apply(self.nt_sG[s], self.nt_sg[s])

        # With periodic boundary conditions, the interpolation will
        # conserve the number of electrons.
        if not self.gd.pbc_c.all():
            # With zero-boundary conditions in one or more directions,
            # this is not the case.
            pseudo_charge = -(self.charge + comp_charge)
            if abs(pseudo_charge) > 1.0e-14:
                x = pseudo_charge / self.finegd.integrate(self.nt_sg).sum()
                self.nt_sg *= x

    def calculate_multipole_moments(self):
        """Calculate multipole moments of compensation charges.

        Returns the total compensation charge in units of electron
        charge, so the number will be negative because of the
        dominating contribution from the nuclear charge."""

        comp_charge = 0.0
        self.Q_aL = {}
        for a, D_sp in self.D_asp.items():
            Q_L = self.Q_aL[a] = np.dot(D_sp.sum(0), self.setups[a].Delta_pL)
            Q_L[0] += self.setups[a].Delta0
            comp_charge += Q_L[0]
        return self.gd.comm.sum(comp_charge) * sqrt(4 * pi)

    def initialize_from_atomic_densities(self, basis_functions):
        """Initialize D_asp, nt_sG and Q_aL from atomic densities.

        nt_sG is initialized from atomic orbitals, and will
        be constructed with the specified magnetic moments and
        obeying Hund's rules if ``hund`` is true."""

        # XXX does this work with blacs?  What should be distributed?
        # Apparently this doesn't use blacs at all, so it's serial
        # with respect to the blacs distribution.  That means it works
        # but is not particularly efficient (not that this is a time
        # consuming step)

        f_sM = np.empty((self.nspins, basis_functions.Mmax))
        self.D_asp = {}
        f_asi = {}
        for a in basis_functions.atom_indices:
            c = self.charge / len(self.setups)  # distribute on all atoms
            f_si = self.setups[a].calculate_initial_occupation_numbers(
                    self.magmom_a[a], self.hund, charge=c, nspins=self.nspins)
            if a in basis_functions.my_atom_indices:
                self.D_asp[a] = self.setups[a].initialize_density_matrix(f_si)
            f_asi[a] = f_si

        self.nt_sG = self.gd.zeros(self.nspins)
        basis_functions.add_to_density(self.nt_sG, f_asi)
        self.nt_sG += self.nct_G
        self.calculate_normalized_charges_and_mix()

    def initialize_from_wavefunctions(self, wfs):
        """Initialize D_asp, nt_sG and Q_aL from wave functions."""
        self.nt_sG = self.gd.empty(self.nspins)
        self.calculate_pseudo_density(wfs)
        self.D_asp = {}
        my_atom_indices = np.argwhere(wfs.rank_a == self.gd.comm.rank).ravel()
        for a in my_atom_indices:
            ni = self.setups[a].ni
            self.D_asp[a] = np.empty((self.nspins, ni * (ni + 1) // 2))
        wfs.calculate_atomic_density_matrices(self.D_asp)
        self.calculate_normalized_charges_and_mix()

    def initialize_directly_from_arrays(self, nt_sG, D_asp):
        """Set D_asp and nt_sG directly."""
        self.nt_sG = nt_sG
        self.D_asp = D_asp
        #self.calculate_normalized_charges_and_mix()
        # No calculate multipole moments?  Tests will fail because of
        # improperly initialized mixer

    def calculate_normalized_charges_and_mix(self):
        comp_charge = self.calculate_multipole_moments()
        self.normalize(comp_charge)
        self.mix(comp_charge)

    def set_mixer(self, mixer):
        if mixer is not None:
            if self.nspins == 1 and isinstance(mixer, MixerSum):
                raise RuntimeError('Cannot use MixerSum with nspins==1')
            self.mixer = mixer
        else:
            if self.gd.pbc_c.any():
                beta = 0.1
                weight = 50.0
            else:
                beta = 0.25
                weight = 1.0
                
            if self.nspins == 2:
                self.mixer = MixerSum(beta=beta, weight=weight)
            else:
                self.mixer = Mixer(beta=beta, weight=weight)

        self.mixer.initialize(self)
        
    def estimate_magnetic_moments(self):
        magmom_a = np.zeros_like(self.magmom_a)
        if self.nspins == 2:
            for a, D_sp in self.D_asp.items():
                magmom_a[a] = np.dot(D_sp[0] - D_sp[1], self.setups[a].N0_p)
            self.gd.comm.sum(magmom_a)
        return magmom_a

    def get_correction(self, a, spin):
        """Integrated atomic density correction.

        Get the integrated correction to the pseuso density relative to
        the all-electron density.
        """
        setup = self.setups[a]
        return sqrt(4 * pi) * (
            np.dot(self.D_asp[a][spin], setup.Delta_pL[:, 0])
            + setup.Delta0 / self.nspins)

    def get_density_array(self):
        XXX
        # XXX why not replace with get_spin_density and get_total_density?
        """Return pseudo-density array."""
        if self.nspins == 2:
            return self.nt_sG
        else:
            return self.nt_sG[0]
    
    def get_all_electron_density(self, atoms, gridrefinement=2):
        """Return real all-electron density array."""

        # Refinement of coarse grid, for representation of the AE-density
        if gridrefinement == 1:
            gd = self.gd
            n_sg = self.nt_sG.copy()
        elif gridrefinement == 2:
            gd = self.finegd
            if self.nt_sg is None:
                self.interpolate()
            n_sg = self.nt_sg.copy()
        elif gridrefinement == 4:
            # Extra fine grid
            gd = self.finegd.refine()
            
            # Interpolation function for the density:
            interpolator = Transformer(self.finegd, gd, 3)

            # Transfer the pseudo-density to the fine grid:
            n_sg = gd.empty(self.nspins)
            if self.nt_sg is None:
                self.interpolate()
            for s in range(self.nspins):
                interpolator.apply(self.nt_sg[s], n_sg[s])
        else:
            raise NotImplementedError

        # Add corrections to pseudo-density to get the AE-density
        splines = {}
        phi_aj = []
        phit_aj = []
        nc_a = []
        nct_a = []
        for a, id in enumerate(self.setups.id_a):
            if id in splines:
                phi_j, phit_j, nc, nct = splines[id]
            else:
                # Load splines:
                phi_j, phit_j, nc, nct = self.setups[a].get_partial_waves()[:4]
                splines[id] = (phi_j, phit_j, nc, nct)
            phi_aj.append(phi_j)
            phit_aj.append(phit_j)
            nc_a.append([nc])
            nct_a.append([nct])

        # Create localized functions from splines
        phi = LFC(gd, phi_aj)
        phit = LFC(gd, phit_aj)
        nc = LFC(gd, nc_a)
        nct = LFC(gd, nct_a)
        spos_ac = atoms.get_scaled_positions() % 1.0
        phi.set_positions(spos_ac)
        phit.set_positions(spos_ac)
        nc.set_positions(spos_ac)
        nct.set_positions(spos_ac)

        all_D_asp = []
        for a, setup in enumerate(self.setups):
            D_sp = self.D_asp.get(a)
            if D_sp is None:
                ni = setup.ni
                D_sp = np.empty((self.nspins, ni * (ni + 1) // 2))
            if gd.comm.size > 1:
                gd.comm.broadcast(D_sp, self.rank_a[a])
            all_D_asp.append(D_sp)

        for s in range(self.nspins):
            I_a = np.zeros(len(atoms))
            nc.add1(n_sg[s], 1.0 / self.nspins, I_a)
            nct.add1(n_sg[s], -1.0 / self.nspins, I_a)
            phi.add2(n_sg[s], all_D_asp, s, 1.0, I_a)
            phit.add2(n_sg[s], all_D_asp, s, -1.0, I_a)
            for a, D_sp in self.D_asp.items():
                setup = self.setups[a]
                I_a[a] -= ((setup.Nc - setup.Nct) / self.nspins +
                           sqrt(4 * pi) *
                           np.dot(D_sp[s], setup.Delta_pL[:, 0]))
            gd.comm.sum(I_a)
            N_c = gd.N_c
            g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c
            for I, g_c in zip(I_a, g_ac):
                if (g_c >= 0).all() and (g_c < gd.n_c).all():
                    n_sg[s][tuple(g_c)] -= I / gd.dv

        return n_sg, gd

    def new_get_all_electron_density(self, atoms, gridrefinement=2):
        """Return real all-electron density array."""

        # Refinement of coarse grid, for representation of the AE-density
        if gridrefinement == 1:
            gd = self.gd
            n_sg = self.nt_sG.copy()
        elif gridrefinement == 2:
            gd = self.finegd
            if self.nt_sg is None:
                self.interpolate()
            n_sg = self.nt_sg.copy()
        elif gridrefinement == 4:
            # Extra fine grid
            gd = self.finegd.refine()
            
            # Interpolation function for the density:
            interpolator = Transformer(self.finegd, gd, 3)

            # Transfer the pseudo-density to the fine grid:
            n_sg = gd.empty(self.nspins)
            if self.nt_sg is None:
                self.interpolate()
            for s in range(self.nspins):
                interpolator.apply(self.nt_sg[s], n_sg[s])
        else:
            raise NotImplementedError

        # Add corrections to pseudo-density to get the AE-density
        splines = {}
        phi_aj = []
        phit_aj = []
        nc_a = []
        nct_a = []
        for a, id in enumerate(self.setups.id_a):
            if id in splines:
                phi_j, phit_j, nc, nct = splines[id]
            else:
                # Load splines:
                phi_j, phit_j, nc, nct = self.setups[a].get_partial_waves()[:4]
                splines[id] = (phi_j, phit_j, nc, nct)
            phi_aj.append(phi_j)
            phit_aj.append(phit_j)
            nc_a.append([nc])
            nct_a.append([nct])

        # Create localized functions from splines
        phi = BasisFunctions(gd, phi_aj)
        phit = BasisFunctions(gd, phit_aj)
        nc = LFC(gd, nc_a)
        nct = LFC(gd, nct_a)
        spos_ac = atoms.get_scaled_positions() % 1.0
        phi.set_positions(spos_ac)
        phit.set_positions(spos_ac)
        nc.set_positions(spos_ac)
        nct.set_positions(spos_ac)

        I_sa = np.zeros((self.nspins, len(atoms)))
        a_W =  np.empty(len(phi.M_W), np.int32)
        W = 0
        for a in phi.atom_indices:
            nw = len(phi.sphere_a[a].M_w)
            a_W[W:W + nw] = a
            W += nw
        rho_MM = np.zeros((phi.Mmax, phi.Mmax))
        for s, I_a in enumerate(I_sa):
            M1 = 0
            for a, setup in enumerate(self.setups):
                ni = setup.ni
                D_sp = self.D_asp.get(a)
                if D_sp is None:
                    D_sp = np.empty((self.nspins, ni * (ni + 1) // 2))
                else:
                    I_a[a] = ((setup.Nct - setup.Nc) / self.nspins -
                              sqrt(4 * pi) *
                              np.dot(D_sp[s], setup.Delta_pL[:, 0]))
                if gd.comm.size > 1:
                    gd.comm.broadcast(D_sp, self.rank_a[a])
                M2 = M1 + ni
                rho_MM[M1:M2, M1:M2] = unpack2(D_sp[s])
                M1 = M2

            phi.lfc.ae_valence_density_correction(rho_MM, n_sg[s], a_W, I_a)
            phit.lfc.ae_valence_density_correction(-rho_MM, n_sg[s], a_W, I_a)

        a_W =  np.empty(len(nc.M_W), np.int32)
        W = 0
        for a in nc.atom_indices:
            nw = len(nc.sphere_a[a].M_w)
            a_W[W:W + nw] = a
            W += nw
        scale = 1.0 / self.nspins
        for s, I_a in enumerate(I_sa):
            nc.lfc.ae_core_density_correction(scale, n_sg[s], a_W, I_a)
            nct.lfc.ae_core_density_correction(-scale, n_sg[s], a_W, I_a)
            gd.comm.sum(I_a)
            N_c = gd.N_c
            g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c
            for I, g_c in zip(I_a, g_ac):
                if (g_c >= 0).all() and (g_c < gd.n_c).all():
                    n_sg[s][tuple(g_c)] -= I / gd.dv
        return n_sg, gd

    if extra_parameters.get('usenewlfc', True):
        get_all_electron_density = new_get_all_electron_density
        
    def estimate_memory(self, mem):
        nspins = self.nspins
        nbytes = self.gd.bytecount()
        nfinebytes = self.finegd.bytecount()

        arrays = mem.subnode('Arrays')
        for name, size in [('nt_sG', nbytes * nspins),
                           ('nt_sg', nfinebytes * nspins),
                           ('nt_g', nfinebytes),
                           ('rhot_g', nfinebytes),
                           ('nct_G', nbytes)]:
            arrays.subnode(name, size)

        lfs = mem.subnode('Localized functions')
        for name, obj in [('nct', self.nct),
                          ('ghat', self.ghat)]:
            obj.estimate_memory(lfs.subnode(name))
        self.mixer.estimate_memory(mem.subnode('Mixer'), self.gd)

        # TODO
        # The implementation of interpolator memory use is not very
        # accurate; 20 MiB vs 13 MiB estimated in one example, probably
        # worse for parallel calculations.
        
        self.interpolator.estimate_memory(mem.subnode('Interpolator'))

    def get_spin_contamination(self, atoms, majority_spin=0):
        """Calculate the spin contamination.

        Spin contamination is defined as the integral over the
        spin density difference, where it is negative (i.e. the
        minority spin density is larger than the majority spin density.
        """

        if majority_spin == 0:
            smaj = 0
            smin = 1
        else:
            smaj = 1
            smin = 0
        nt_sg, gd = self.get_all_electron_density(atoms)
        dt_sg = nt_sg[smin] - nt_sg[smaj]
        dt_sg = np.where(dt_sg > 0, dt_sg, 0.0)
        return gd.integrate(dt_sg)
예제 #7
0
파일: hybridq.py 프로젝트: eojons/gpaw-scme
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
예제 #8
0
파일: hybrid.py 프로젝트: robwarm/gpaw-symm
class HybridXC(HybridXCBase):
    def __init__(self, name, hybrid=None, xc=None, 
                 finegrid=False, unocc=False):
        """Mix standard functionals with exact exchange.

        finegrid: boolean
            Use fine grid for energy functional evaluations ?
        unocc: boolean
            Apply vxx also to unoccupied states ?
        """
        self.finegrid = finegrid
        self.unocc = unocc
        HybridXCBase.__init__(self, name, hybrid, xc)
        
    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):
        assert wfs.gamma
        self.xc.initialize(density, hamiltonian, wfs, occupations)
        self.kpt_comm = wfs.kpt_comm
        self.nspins = wfs.nspins
        self.setups = wfs.setups
        self.density = density
        self.kpt_u = wfs.kpt_u
        self.exx_s = np.zeros(self.nspins)
        self.ekin_s = np.zeros(self.nspins)
        self.nocc_s = np.empty(self.nspins, int)
        
        if self.finegrid:
            self.poissonsolver = hamiltonian.poisson
            self.ghat = density.ghat
            self.interpolator = density.interpolator
            self.restrictor = hamiltonian.restrictor
        else:
            self.poissonsolver = PoissonSolver(eps=1e-11)
            self.poissonsolver.set_grid_descriptor(density.gd)
            self.poissonsolver.initialize()
            self.ghat = LFC(density.gd,
                            [setup.ghat_l for setup in density.setups],
                            integral=np.sqrt(4 * np.pi), forces=True)
        self.gd = density.gd
        self.finegd = self.ghat.gd

    def set_positions(self, spos_ac):
        if not self.finegrid:
            self.ghat.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)
        self.ekin = self.kpt_comm.sum(self.ekin_s.sum())
        return exc + self.kpt_comm.sum(self.exx_s.sum())

    def calculate_exx(self):
        for kpt in self.kpt_u:
            self.apply_orbital_dependent_hamiltonian(kpt, kpt.psit_nG)

    def apply_orbital_dependent_hamiltonian(self, kpt, psit_nG,
                                            Htpsit_nG=None, dH_asp=None):
        if kpt.f_n is None:
            return
        
        deg = 2 // self.nspins   # Spin degeneracy
        hybrid = self.hybrid
        
        P_ani = kpt.P_ani
        setups = self.setups

        vt_g = self.finegd.empty()
        if self.gd is not self.finegd:
            vt_G = self.gd.empty()

        nocc = int(kpt.f_n.sum()) // (3 - self.nspins)
        if self.unocc:
            nbands = len(kpt.f_n)
        else:
            nbands = nocc
        self.nocc_s[kpt.s] = nocc

        if Htpsit_nG is not None:
            kpt.vt_nG = self.gd.empty(nbands)
            kpt.vxx_ani = {}
            kpt.vxx_anii = {}
            for a, P_ni in P_ani.items():
                I = P_ni.shape[1]
                kpt.vxx_ani[a] = np.zeros((nbands, I))
                kpt.vxx_anii[a] = np.zeros((nbands, I, I))

        exx = 0.0
        ekin = 0.0

        # Determine pseudo-exchange
        for n1 in range(nbands):
            psit1_G = psit_nG[n1]
            f1 = kpt.f_n[n1] / deg
            for n2 in range(n1, nbands):
                psit2_G = psit_nG[n2]
                f2 = kpt.f_n[n2] / deg

                # Double count factor:
                dc = (1 + (n1 != n2)) * deg
                
                nt_G, rhot_g = self.calculate_pair_density(n1, n2, psit_nG,
                                                           P_ani)
                vt_g[:] = 0.0
                iter = self.poissonsolver.solve(vt_g, -rhot_g,
                                                charge=-float(n1 == n2),
                                                eps=1e-12,
                                                zero_initial_phi=True)
                vt_g *= hybrid

                if self.gd is self.finegd:
                    vt_G = vt_g
                else:
                    self.restrictor.apply(vt_g, vt_G)

                # Integrate the potential on fine and coarse grids
                int_fine = self.finegd.integrate(vt_g * rhot_g)
                int_coarse = self.gd.integrate(vt_G * nt_G)
                if self.gd.comm.rank == 0:  # only add to energy on master CPU
                    exx += 0.5 * dc * f1 * f2 * int_fine
                    ekin -= dc * f1 * f2 * int_coarse
                if Htpsit_nG is not None:
                    Htpsit_nG[n1] += f2 * vt_G * psit2_G
                    if n1 == n2:
                        kpt.vt_nG[n1] = f1 * vt_G
                    else:
                        Htpsit_nG[n2] += f1 * vt_G * psit1_G

                    # Update the vxx_uni and vxx_unii vectors of the nuclei,
                    # used to determine the atomic hamiltonian, and the 
                    # residuals
                    v_aL = self.ghat.dict()
                    self.ghat.integrate(vt_g, v_aL)
                    for a, v_L in v_aL.items():
                        v_ii = unpack(np.dot(setups[a].Delta_pL, v_L))
                        v_ni = kpt.vxx_ani[a]
                        v_nii = kpt.vxx_anii[a]
                        P_ni = P_ani[a]
                        v_ni[n1] += f2 * np.dot(v_ii, P_ni[n2])
                        if n1 != n2:
                            v_ni[n2] += f1 * np.dot(v_ii, P_ni[n1])
                        else:
                            # XXX Check this:
                            v_nii[n1] = f1 * v_ii

        # Apply the atomic corrections to the energy and the Hamiltonian matrix
        for a, P_ni in P_ani.items():
            setup = setups[a]

            if Htpsit_nG is not None:
                # Add non-trivial corrections the Hamiltonian matrix
                h_nn = symmetrize(np.inner(P_ni[:nbands], 
                                           kpt.vxx_ani[a][:nbands]))
                ekin -= np.dot(kpt.f_n[:nbands], h_nn.diagonal())

                dH_p = dH_asp[a][kpt.s]
            
            # Get atomic density and Hamiltonian matrices
            D_p  = self.density.D_asp[a][kpt.s]
            D_ii = unpack2(D_p)
            ni = len(D_ii)
            
            # Add atomic corrections to the valence-valence exchange energy
            # --
            # >  D   C     D
            # --  ii  iiii  ii
            for i1 in range(ni):
                for i2 in range(ni):
                    A = 0.0
                    for i3 in range(ni):
                        p13 = packed_index(i1, i3, ni)
                        for i4 in range(ni):
                            p24 = packed_index(i2, i4, ni)
                            A += setup.M_pp[p13, p24] * D_ii[i3, i4]
                    p12 = packed_index(i1, i2, ni)
                    if Htpsit_nG is not None:
                        dH_p[p12] -= 2 * hybrid / deg * A / ((i1 != i2) + 1)
                    ekin += 2 * hybrid / deg * D_ii[i1, i2] * A
                    exx -= hybrid / deg * D_ii[i1, i2] * A
            
            # Add valence-core exchange energy
            # --
            # >  X   D
            # --  ii  ii
            if setup.X_p is not None:
                exx -= hybrid * np.dot(D_p, setup.X_p)
                if Htpsit_nG is not None:
                    dH_p -= hybrid * setup.X_p
                    ekin += hybrid * np.dot(D_p, setup.X_p)

                # Add core-core exchange energy
                if kpt.s == 0:
                    exx += hybrid * setup.ExxC

        self.exx_s[kpt.s] = self.gd.comm.sum(exx)
        self.ekin_s[kpt.s] = self.gd.comm.sum(ekin)

    def correct_hamiltonian_matrix(self, kpt, H_nn):
        if not hasattr(kpt, 'vxx_ani'):
            return

        if self.gd.comm.rank > 0:
            H_nn[:] = 0.0
            
        nocc = self.nocc_s[kpt.s]
        nbands = len(kpt.vt_nG)
        for a, P_ni in kpt.P_ani.items():
            H_nn[:nbands, :nbands] += symmetrize(np.inner(P_ni[:nbands],
                                                          kpt.vxx_ani[a]))
        self.gd.comm.sum(H_nn)
        
        H_nn[:nocc, nocc:] = 0.0
        H_nn[nocc:, :nocc] = 0.0

    def calculate_pair_density(self, n1, n2, psit_nG, P_ani):
        Q_aL = {}
        for a, P_ni in P_ani.items():
            P1_i = P_ni[n1]
            P2_i = P_ni[n2]
            D_ii = np.outer(P1_i, P2_i.conj()).real
            D_p = pack(D_ii)
            Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL)
            
        nt_G = psit_nG[n1] * psit_nG[n2]

        if self.finegd is self.gd:
            nt_g = nt_G
        else:
            nt_g = self.finegd.empty()
            self.interpolator.apply(nt_G, nt_g)

        rhot_g = nt_g.copy()
        self.ghat.add(rhot_g, Q_aL)

        return nt_G, rhot_g

    def add_correction(self, kpt, psit_xG, Htpsit_xG, P_axi, c_axi, n_x,
                       calculate_change=False):
        if kpt.f_n is None:
            return

        nocc = self.nocc_s[kpt.s]
        
        if calculate_change:
            for x, n in enumerate(n_x):
                if n < nocc:
                    Htpsit_xG[x] += kpt.vt_nG[n] * psit_xG[x]
                    for a, P_xi in P_axi.items():
                        c_axi[a][x] += np.dot(kpt.vxx_anii[a][n], P_xi[x])
        else:
            for a, c_xi in c_axi.items():
                c_xi[:nocc] += kpt.vxx_ani[a][:nocc]
        
    def rotate(self, kpt, U_nn):
        if kpt.f_n is None:
            return

        nocc = self.nocc_s[kpt.s]
        if len(kpt.vt_nG) == nocc:
            U_nn = U_nn[:nocc, :nocc]
        gemm(1.0, kpt.vt_nG.copy(), U_nn, 0.0, kpt.vt_nG)
        for v_ni in kpt.vxx_ani.values():
            gemm(1.0, v_ni.copy(), U_nn, 0.0, v_ni)
        for v_nii in kpt.vxx_anii.values():
            gemm(1.0, v_nii.copy(), U_nn, 0.0, v_nii)
예제 #9
0
파일: hybridk.py 프로젝트: yihsuanliu/gpaw
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
예제 #10
0
class QNA(GGA):
    def __init__(self, atoms, parameters, qna_setup_name='PBE', alpha=2.0,
                 override_atoms=None, stencil=2):
        # override_atoms is only used to test the partial derivatives
        # of xc-functional
        kernel = QNAKernel(self)
        GGA.__init__(self, kernel, stencil=stencil)
        self.atoms = atoms
        self.parameters = parameters
        self.qna_setup_name = qna_setup_name
        self.alpha = alpha
        self.override_atoms = override_atoms
        self.orbital_dependent = False

    def todict(self):
        dct = dict(type='qna-gga',
                   name='QNA',
                   setup_name=self.qna_setup_name,
                   parameters=self.parameters,
                   alpha=self.alpha,
                   orbital_dependent=False)
        return dct

    def set_grid_descriptor(self, gd):
        GGA.set_grid_descriptor(self, gd)
        self.dedmu_g = gd.zeros()
        self.dedbeta_g = gd.zeros()
        # Create gaussian LFC
        l_lim = 1.0e-30
        rcut = 12
        points = 200
        r_i = np.linspace(0, rcut, points + 1)
        rcgauss = 1.2
        g_g = (2 / rcgauss**3 / np.pi *
               np.exp(-((r_i / rcgauss)**2)**self.alpha))

        # Values too close to zero can cause numerical problems especially with
        # forces (some parts of the mu and beta field can become negative)
        g_g[np.where(g_g < l_lim)] = l_lim
        spline = Spline(l=0, rmax=rcut, f_g=g_g)
        spline_j = [[spline]] * len(self.atoms)
        self.Pa = LFC(gd, spline_j)

    def set_positions(self, spos_ac, atom_partition=None):
        self.Pa.set_positions(spos_ac)

    def calculate_spatial_parameters(self, atoms):
        mu_g = self.gd.zeros()
        beta_g = self.gd.zeros()
        denominator = self.gd.zeros()
        mu_a = {}
        beta_a = {}
        eye_a = {}
        for atom in atoms:
            mu, beta = self.parameters[atom.symbol]
            mu_a[atom.index] = np.array([mu])
            beta_a[atom.index] = np.array([beta])
            eye_a[atom.index] = np.array(1.0)
        self.Pa.add(mu_g, mu_a)
        self.Pa.add(beta_g, beta_a)
        self.Pa.add(denominator, eye_a)
        mu_g /= denominator
        beta_g /= denominator
        return mu_g, beta_g

    def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None,
                                 addcoredensity=True, a=None):
        self.current_atom = a
        return GGA.calculate_paw_correction(self, setup, D_sp, dEdD_sp,
                                            addcoredensity, a)

    def get_setup_name(self):
        return self.qna_setup_name

    def get_description(self):
        return 'QNA Parameters: ' + str(self.parameters)

    def add_forces(self, F_av):
        mu_g = self.gd.zeros()
        beta_g = self.gd.zeros()
        denominator = self.gd.zeros()
        mu_a = {}
        beta_a = {}
        eye_a = {}
        for atom in self.atoms:
            mu, beta = self.parameters[atom.symbol]
            mu_a[atom.index] = np.array([mu])
            beta_a[atom.index] = np.array([beta])
            eye_a[atom.index] = np.array(1.0)
        self.Pa.add(mu_g, mu_a)
        self.Pa.add(beta_g, beta_a)
        self.Pa.add(denominator, eye_a)
        mu_g /= denominator
        beta_g /= denominator

        # mu
        part1 = -self.dedmu_g / denominator
        part2 = -part1 * mu_g
        c_axiv = self.Pa.dict(derivative=True)
        self.Pa.derivative(part1, c_axiv)

        for atom in self.atoms:
            F_av[atom.index] -= c_axiv[atom.index][0][:] * mu_a[atom.index][0]
        c_axiv = self.Pa.dict(derivative=True)
        self.Pa.derivative(part2, c_axiv)
        for atom in self.atoms:
            F_av[atom.index] -= c_axiv[atom.index][0][:]

        # beta
        part1 = -self.dedbeta_g / denominator
        part2 = -part1 * beta_g
        c_axiv = self.Pa.dict(derivative=True)
        self.Pa.derivative(part1, c_axiv)
        for atom in self.atoms:
            F_av[atom.index] -= c_axiv[atom.index][0] * beta_a[atom.index][0]
        c_axiv = self.Pa.dict(derivative=True)
        self.Pa.derivative(part2, c_axiv)
        for atom in self.atoms:
            F_av[atom.index] -= c_axiv[atom.index][0][:]
예제 #11
0
파일: hybridk.py 프로젝트: qsnake/gpaw
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
예제 #12
0
class RealSpaceHamiltonian(Hamiltonian):
    def __init__(self,
                 gd,
                 finegd,
                 nspins,
                 setups,
                 timer,
                 xc,
                 world,
                 kptband_comm,
                 vext=None,
                 collinear=True,
                 psolver=None,
                 stencil=3):
        Hamiltonian.__init__(self, gd, finegd, nspins, setups, timer, xc,
                             world, kptband_comm, vext, collinear)

        # Solver for the Poisson equation:
        if psolver is None:
            psolver = PoissonSolver(nn=3, relax='J')
        self.poisson = psolver
        self.poisson.set_grid_descriptor(finegd)

        # Restrictor function for the potential:
        self.restrictor = Transformer(self.finegd, self.gd, stencil)
        self.restrict = self.restrictor.apply

        self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups],
                        forces=True)
        self.vbar_g = None

    def summary(self, fd):
        Hamiltonian.summary(self, fd)

        degree = self.restrictor.nn * 2 - 1
        name = ['linear', 'cubic', 'quintic', 'heptic'][degree // 2]
        fd.write('Interpolation: tri-%s ' % name +
                 '(%d. degree polynomial)\n' % degree)

        fd.write('Poisson solver: %s\n' % self.poisson.get_description())

    def set_positions(self, spos_ac, rank_a):
        Hamiltonian.set_positions(self, spos_ac, rank_a)
        if self.vbar_g is None:
            self.vbar_g = self.finegd.empty()
        self.vbar_g[:] = 0.0
        self.vbar.add(self.vbar_g)

    def update_pseudo_potential(self, density):
        self.timer.start('vbar')
        Ebar = self.finegd.integrate(self.vbar_g,
                                     density.nt_g,
                                     global_integral=False)

        vt_g = self.vt_sg[0]
        vt_g[:] = self.vbar_g
        self.timer.stop('vbar')

        Eext = 0.0
        if self.vext is not None:
            assert self.collinear
            vt_g += self.vext.get_potential(self.finegd)
            Eext = self.finegd.integrate(
                vt_g, density.nt_g, global_integral=False) - Ebar

        self.vt_sg[1:self.nspins] = vt_g

        self.vt_sg[self.nspins:] = 0.0

        self.timer.start('XC 3D grid')
        Exc = self.xc.calculate(self.finegd, density.nt_sg, self.vt_sg)
        Exc /= self.gd.comm.size
        self.timer.stop('XC 3D grid')

        self.timer.start('Poisson')
        # npoisson is the number of iterations:
        self.npoisson = self.poisson.solve(self.vHt_g,
                                           density.rhot_g,
                                           charge=-density.charge)
        self.timer.stop('Poisson')

        self.timer.start('Hartree integrate/restrict')
        Epot = 0.5 * self.finegd.integrate(
            self.vHt_g, density.rhot_g, global_integral=False)

        Ekin = 0.0
        s = 0
        for s, (vt_g, vt_G,
                nt_G) in enumerate(zip(self.vt_sg, self.vt_sG, density.nt_sG)):
            if s < self.nspins:
                vt_g += self.vHt_g

            self.restrict(vt_g, vt_G)
            if self.ref_vt_sG is not None:
                vt_G += self.ref_vt_sG[s]

            if s < self.nspins:
                Ekin -= self.gd.integrate(vt_G,
                                          nt_G - density.nct_G,
                                          global_integral=False)
            else:
                Ekin -= self.gd.integrate(vt_G, nt_G, global_integral=False)
            s += 1

        self.timer.stop('Hartree integrate/restrict')

        # Calculate atomic hamiltonians:
        W_aL = {}
        for a in density.D_asp:
            W_aL[a] = np.empty((self.setups[a].lmax + 1)**2)
        density.ghat.integrate(self.vHt_g, W_aL)

        return Ekin, Epot, Ebar, Eext, Exc, W_aL

    def calculate_forces2(self, dens, ghat_aLv, nct_av, vbar_av):
        if self.nspins == 2:
            vt_G = self.vt_sG.mean(0)
        else:
            vt_G = self.vt_sG[0]

        dens.ghat.derivative(self.vHt_g, ghat_aLv)
        dens.nct.derivative(vt_G, nct_av)
        self.vbar.derivative(dens.nt_g, vbar_av)
예제 #13
0
class PS2AE:
    """Transform PS to AE wave functions.

    Interpolates PS wave functions to a fine grid and adds PAW
    corrections in order to obtain true AE wave functions.
    """
    def __init__(self, calc, h=0.05, n=2):
        """Create transformation object.

        calc: GPAW calculator object
            The calcalator that has the wave functions.
        h: float
            Desired grid-spacing in Angstrom.
        n: int
            Force number of points to be a mulitiple of n.
        """
        self.calc = calc
        gd = calc.wfs.gd

        gd1 = GridDescriptor(gd.N_c, gd.cell_cv, comm=serial_comm)

        # Descriptor for the final grid:
        N_c = h2gpts(h / Bohr, gd.cell_cv)
        N_c = np.array([get_efficient_fft_size(N, n) for N in N_c])
        gd2 = self.gd = GridDescriptor(N_c, gd.cell_cv, comm=serial_comm)
        self.interpolator = Interpolator(gd1, gd2, self.calc.wfs.dtype)

        self.dphi = None  # PAW correction (will be initialized when needed)

    def _initialize_corrections(self):
        if self.dphi is not None:
            return
        splines = {}
        dphi_aj = []
        for setup in self.calc.wfs.setups:
            dphi_j = splines.get(setup)
            if dphi_j is None:
                rcut = max(setup.rcut_j) * 1.1
                gcut = setup.rgd.ceil(rcut)
                dphi_j = []
                for l, phi_g, phit_g in zip(setup.l_j, setup.data.phi_jg,
                                            setup.data.phit_jg):
                    dphi_g = (phi_g - phit_g)[:gcut]
                    dphi_j.append(setup.rgd.spline(dphi_g, rcut, l,
                                                   points=200))
            dphi_aj.append(dphi_j)

        self.dphi = LFC(self.gd,
                        dphi_aj,
                        kd=self.calc.wfs.kd.copy(),
                        dtype=self.calc.wfs.dtype)
        self.dphi.set_positions(self.calc.spos_ac)

    def get_wave_function(self, n, k=0, s=0, ae=True):
        """Interpolate wave function.

        n: int
            Band index.
        k: int
            K-point index.
        s: int
            Spin index.
        ae: bool
            Add PAW correction to get an all-electron wave function.
        """
        psi_r = self.calc.get_pseudo_wave_function(n,
                                                   k,
                                                   s,
                                                   pad=True,
                                                   periodic=True)
        psi_R = self.interpolator.interpolate(psi_r * Bohr**1.5)
        if ae:
            self._initialize_corrections()
            wfs = self.calc.wfs
            P_nI = wfs.collect_projections(k, s)
            if wfs.world.rank == 0:
                P_ai = {}
                I1 = 0
                for a, setup in enumerate(wfs.setups):
                    I2 = I1 + setup.ni
                    P_ai[a] = P_nI[n, I1:I2]
                    I1 = I2
                self.dphi.add(psi_R, P_ai, k)
            wfs.world.broadcast(psi_R, 0)
        return psi_R * Bohr**-1.5

    def get_electrostatic_potential(self, ae=True, rcgauss=0.02):
        """Interpolate electrostatic potential.

        Return value in eV.

        ae: bool
            Add PAW correction to get the all-electron potential.
        rcgauss: float
            Width of gaussian (in Angstrom) used to represent the nuclear
            charge.
        """
        gd = self.calc.hamiltonian.finegd
        v_r = self.calc.get_electrostatic_potential() / Ha
        gd1 = GridDescriptor(gd.N_c, gd.cell_cv, comm=serial_comm)
        interpolator = Interpolator(gd1, self.gd)
        v_R = interpolator.interpolate(v_r)

        if ae:
            alpha = 1 / (rcgauss / Bohr)**2
            self.add_potential_correction(v_R, alpha)

        return v_R * Ha

    def add_potential_correction(self, v_R, alpha):
        dens = self.calc.density
        dens.D_asp.redistribute(dens.atom_partition.as_serial())
        dens.Q_aL.redistribute(dens.atom_partition.as_serial())

        dv_a1 = []
        for a, D_sp in dens.D_asp.items():
            setup = dens.setups[a]
            c = setup.xc_correction
            rgd = c.rgd
            ghat_g = gauss(rgd, 1 / setup.rcgauss**2)
            Z_g = gauss(rgd, alpha) * setup.Z
            D_q = np.dot(D_sp.sum(0), c.B_pqL[:, :, 0])
            dn_g = np.dot(D_q, (c.n_qg - c.nt_qg)) * sqrt(4 * pi)
            dn_g += 4 * pi * (c.nc_g - c.nct_g)
            dn_g -= Z_g
            dn_g -= dens.Q_aL[a][0] * ghat_g * sqrt(4 * pi)
            dv_g = rgd.poisson(dn_g) / sqrt(4 * pi)
            dv_g[1:] /= rgd.r_g[1:]
            dv_g[0] = dv_g[1]
            dv_g[-1] = 0.0
            dv_a1.append([rgd.spline(dv_g, points=POINTS)])

        dens.D_asp.redistribute(dens.atom_partition)
        dens.Q_aL.redistribute(dens.atom_partition)

        if dv_a1:
            dv = LFC(self.gd, dv_a1)
            dv.set_positions(self.calc.spos_ac)
            dv.add(v_R)
        dens.gd.comm.broadcast(v_R, 0)
예제 #14
0
파일: split.py 프로젝트: thonmaker/gpaw
class DensityCollector(Observer):
    def __init__(self, filename, lcao, ranges_str='full'):
        Observer.__init__(self)
        self.lcao = lcao
        self.filename = filename
        self.ranges = None
        print("ranges-str", ranges_str)
        self.ranges_str = ranges_str

    def update(self):
        if self.ranges is None:  # First time
            self.ranges = []
            self.nbands = self.lcao.wfs.bd.nbands
            start = 0
            if self.ranges_str != 'full':
                for rng in self.ranges_str.split(','):
                    print("rng", rng)
                    rng = eval(rng)
                    self.ranges.append(range(start, rng))
                    start = rng
            self.ranges.append(range(start, self.nbands))
            print(self.ranges)
            self.ghat = LFC(
                self.lcao.wfs.gd,
                [setup.ghat_l for setup in self.lcao.density.setups],
                integral=sqrt(4 * pi),
                forces=False)
            self.ghat.set_positions(self.lcao.wfs.spos_ac)

            # Clear files
            for rid, rng in enumerate(self.ranges):
                f = open(self.filename + '.' + str(rid) + '.density', 'w')
                print("# Density file", file=f)
                N_c = self.lcao.wfs.gd.N_c
                print(N_c[0], N_c[1], N_c[2], file=f)
                print(
                    "# This header is 10 lines long, then double precision binary data starts.",
                    file=f)
                for i in range(7):
                    print("#", file=f)
                f.close()

        #self.lcao.timer.start('Dump density')
        for rid, rng in enumerate(self.ranges):
            assert len(self.lcao.wfs.kpt_u) == 1
            f_un = [self.lcao.wfs.kpt_u[0].f_n.copy()]
            for n in range(self.lcao.wfs.bd.nbands):
                band_rank, myn = self.lcao.wfs.bd.who_has(n)
                if self.lcao.wfs.bd.rank == band_rank:
                    if n not in rng:
                        f_un[0][myn] = 0.0
            n_sG = self.lcao.wfs.gd.zeros(1)
            self.lcao.wfs.add_to_density_from_k_point_with_occupation(
                n_sG, self.lcao.wfs.kpt_u[0], f_un[0])

            self.lcao.wfs.kptband_comm.sum(n_sG)

            D_asp = {}
            for a in self.lcao.density.D_asp:
                ni = self.lcao.density.setups[a].ni
                D_asp[a] = np.zeros((1, ni * (ni + 1) // 2))
            self.lcao.wfs.calculate_atomic_density_matrices_with_occupation(
                D_asp, f_un)
            Q_aL = {}
            for a, D_sp in D_asp.items():
                Q_aL[a] = np.dot(D_sp.sum(0),
                                 self.lcao.density.setups[a].Delta_pL)
            self.ghat.add(n_sG, Q_aL)
            n_sg = self.lcao.wfs.gd.collect(n_sG, broadcast=False)
            if world.rank == 0:
                f = open(self.filename + '.' + str(rid) + '.density', 'a+')
                #n_sg.astype(np.float32).tofile(f)
                #print "max", np.max(n_sg), np.min(n_sg)
                n_sg.tofile(f)
                f.close()
                s = n_sg.shape
                f = open(self.filename + '.info', 'w')
                print(s[0], s[1], s[2], file=f)
                f.close()
예제 #15
0
class DensityCollector(Observer):
    def __init__(self, filename, lcao, ranges_str="full"):
        Observer.__init__(self)
        self.lcao = lcao
        self.filename = filename
        self.ranges = None
        print("ranges-str", ranges_str)
        self.ranges_str = ranges_str

    def update(self):
        if self.ranges is None:  # First time
            self.ranges = []
            self.nbands = self.lcao.wfs.bd.nbands
            start = 0
            if self.ranges_str != "full":
                for rng in self.ranges_str.split(","):
                    print("rng", rng)
                    rng = eval(rng)
                    self.ranges.append(range(start, rng))
                    start = rng
            self.ranges.append(range(start, self.nbands))
            print(self.ranges)
            self.ghat = LFC(
                self.lcao.wfs.gd,
                [setup.ghat_l for setup in self.lcao.density.setups],
                integral=sqrt(4 * pi),
                forces=False,
            )
            spos_ac = self.lcao.atoms.get_scaled_positions() % 1.0
            self.ghat.set_positions(spos_ac)

            # Clear files
            for rid, rng in enumerate(self.ranges):
                f = open(self.filename + "." + str(rid) + ".density", "w")
                print("# Density file", file=f)
                N_c = self.lcao.wfs.gd.N_c
                print(N_c[0], N_c[1], N_c[2], file=f)
                print("# This header is 10 lines long, then double precision binary data starts.", file=f)
                for i in range(7):
                    print("#", file=f)
                f.close()

        # self.lcao.timer.start('Dump density')
        for rid, rng in enumerate(self.ranges):
            assert len(self.lcao.wfs.kpt_u) == 1
            f_un = [self.lcao.wfs.kpt_u[0].f_n.copy()]
            for n in range(self.lcao.wfs.bd.nbands):
                band_rank, myn = self.lcao.wfs.bd.who_has(n)
                if self.lcao.wfs.bd.rank == band_rank:
                    if not n in rng:
                        f_un[0][myn] = 0.0
            n_sG = self.lcao.wfs.gd.zeros(1)
            self.lcao.wfs.add_to_density_from_k_point_with_occupation(n_sG, self.lcao.wfs.kpt_u[0], f_un[0])

            self.lcao.wfs.kptband_comm.sum(n_sG)

            D_asp = {}
            for a in self.lcao.density.D_asp:
                ni = self.lcao.density.setups[a].ni
                D_asp[a] = np.zeros((1, ni * (ni + 1) // 2))
            self.lcao.wfs.calculate_atomic_density_matrices_with_occupation(D_asp, f_un)
            Q_aL = {}
            for a, D_sp in D_asp.items():
                Q_aL[a] = np.dot(D_sp.sum(0), self.lcao.density.setups[a].Delta_pL)
            self.ghat.add(n_sG, Q_aL)
            n_sg = self.lcao.wfs.gd.collect(n_sG, broadcast=False)
            if world.rank == 0:
                f = open(self.filename + "." + str(rid) + ".density", "a+")
                # n_sg.astype(np.float32).tofile(f)
                # print "max", np.max(n_sg), np.min(n_sg)
                n_sg.tofile(f)
                f.close()
                s = n_sg.shape
                f = open(self.filename + ".info", "w")
                print(s[0], s[1], s[2], file=f)
                f.close()
예제 #16
0
파일: mgga.py 프로젝트: qsnake/gpaw
class MGGA(GGA):
    orbital_dependent = True

    def __init__(self, kernel, nn=1):
        """Meta GGA functional.

        nn: int
            Number of neighbor grid points to use for FD stencil for
            wave function gradient.
        """
        self.nn = nn
        GGA.__init__(self, kernel)

    def set_grid_descriptor(self, gd):
        GGA.set_grid_descriptor(self, gd)

    def get_setup_name(self):
        return "PBE"

    def initialize(self, density, hamiltonian, wfs, occupations):
        self.wfs = wfs
        self.tauct = LFC(wfs.gd, [[setup.tauct] for setup in wfs.setups], forces=True, cut=True)
        self.tauct_G = None
        self.dedtaut_sG = None
        self.restrict = hamiltonian.restrictor.apply
        self.interpolate = density.interpolator.apply
        self.taugrad_v = [Gradient(wfs.gd, v, n=self.nn, dtype=wfs.dtype, allocate=True).apply for v in range(3)]

    def set_positions(self, spos_ac):
        self.tauct.set_positions(spos_ac)
        if self.tauct_G is None:
            self.tauct_G = self.wfs.gd.empty()
        self.tauct_G[:] = 0.0
        self.tauct.add(self.tauct_G)

    def calculate_gga(self, e_g, nt_sg, v_sg, sigma_xg, dedsigma_xg):
        taut_sG = self.wfs.calculate_kinetic_energy_density(self.tauct, self.taugrad_v)
        taut_sg = np.empty_like(nt_sg)
        for taut_G, taut_g in zip(taut_sG, taut_sg):
            taut_G += 1.0 / self.wfs.nspins * self.tauct_G
            self.interpolate(taut_G, taut_g)
        dedtaut_sg = np.empty_like(nt_sg)
        self.kernel.calculate(e_g, nt_sg, v_sg, sigma_xg, dedsigma_xg, taut_sg, dedtaut_sg)
        self.dedtaut_sG = self.wfs.gd.empty(self.wfs.nspins)
        self.ekin = 0.0
        for s in range(self.wfs.nspins):
            self.restrict(dedtaut_sg[s], self.dedtaut_sG[s])
            self.ekin -= self.wfs.gd.integrate(self.dedtaut_sG[s] * (taut_sG[s] - self.tauct_G / self.wfs.nspins))

    def apply_orbital_dependent_hamiltonian(self, kpt, psit_xG, Htpsit_xG, dH_asp):
        a_G = self.wfs.gd.empty(dtype=psit_xG.dtype)
        for psit_G, Htpsit_G in zip(psit_xG, Htpsit_xG):
            for v in range(3):
                self.taugrad_v[v](psit_G, a_G, kpt.phase_cd)
                self.taugrad_v[v](self.dedtaut_sG[kpt.s] * a_G, a_G, kpt.phase_cd)
                axpy(-0.5, a_G, Htpsit_G)

    def add_forces(self, F_av):
        dF_av = self.tauct.dict(derivative=True)
        self.tauct.derivative(self.dedtaut_sG.sum(0), dF_av)
        for a, dF_v in dF_av.items():
            F_av[a] += dF_v[0]

    def estimate_memory(self, mem):
        bytecount = self.wfs.gd.bytecount()
        mem.subnode("MGGA arrays", (1 + self.wfs.nspins) * bytecount)

    def initialize_kinetic(self, xccorr):
        nii = xccorr.nii
        nn = len(xccorr.rnablaY_nLv)
        ng = len(xccorr.phi_jg[0])

        tau_npg = np.zeros((nn, nii, ng))
        taut_npg = np.zeros((nn, nii, ng))
        self.create_kinetic(xccorr, nn, xccorr.phi_jg, tau_npg)
        self.create_kinetic(xccorr, nn, xccorr.phit_jg, taut_npg)
        return tau_npg, taut_npg

    def create_kinetic(self, x, ny, phi_jg, tau_ypg):
        """Short title here.
        
        kinetic expression is::

                                             __         __
          tau_s = 1/2 Sum_{i1,i2} D(s,i1,i2) \/phi_i1 . \/phi_i2 +tauc_s

        here the orbital dependent part is calculated::

          __         __
          \/phi_i1 . \/phi_i2 =
                      __    __
                      \/YL1.\/YL2 phi_j1 phi_j2 +YL1 YL2 dphi_j1 dphi_j2
                                                         ------  ------
                                                           dr     dr
          __    __
          \/YL1.\/YL2 [y] = Sum_c A[L1,c,y] A[L2,c,y] / r**2
          
        """
        nj = len(phi_jg)
        ni = len(x.jlL)
        nii = ni * (ni + 1) // 2
        dphidr_jg = np.zeros(np.shape(phi_jg))
        for j in range(nj):
            phi_g = phi_jg[j]
            x.rgd.derivative(phi_g, dphidr_jg[j])

        # Second term:
        for y in range(ny):
            i1 = 0
            p = 0
            Y_L = x.Y_nL[y]
            for j1, l1, L1 in x.jlL:
                for j2, l2, L2 in x.jlL[i1:]:
                    c = Y_L[L1] * Y_L[L2]
                    temp = c * dphidr_jg[j1] * dphidr_jg[j2]
                    tau_ypg[y, p, :] += temp
                    p += 1
                i1 += 1
        ##first term
        for y in range(ny):
            i1 = 0
            p = 0
            rnablaY_Lv = x.rnablaY_nLv[y, : x.Lmax]
            Ax_L = rnablaY_Lv[:, 0]
            Ay_L = rnablaY_Lv[:, 1]
            Az_L = rnablaY_Lv[:, 2]
            for j1, l1, L1 in x.jlL:
                for j2, l2, L2 in x.jlL[i1:]:
                    temp = Ax_L[L1] * Ax_L[L2] + Ay_L[L1] * Ay_L[L2] + Az_L[L1] * Az_L[L2]
                    temp *= phi_jg[j1] * phi_jg[j2]
                    temp[1:] /= x.rgd.r_g[1:] ** 2
                    temp[0] = temp[1]
                    tau_ypg[y, p, :] += temp
                    p += 1
                i1 += 1
        tau_ypg *= 0.5

        return
예제 #17
0
class RealSpaceDensity(Density):
    def __init__(self, gd, finegd, nspins, charge, collinear=True,
                 stencil=3):
        Density.__init__(self, gd, finegd, nspins, charge, collinear)
        self.stencil = stencil

    def initialize(self, setups, timer, magmom_av, hund):
        Density.initialize(self, setups, timer, magmom_av, hund)

        # Interpolation function for the density:
        self.interpolator = Transformer(self.gd, self.finegd, self.stencil)
        
        spline_aj = []
        for setup in setups:
            if setup.nct is None:
                spline_aj.append([])
            else:
                spline_aj.append([setup.nct])
        self.nct = LFC(self.gd, spline_aj,
                       integral=[setup.Nct for setup in setups],
                       forces=True, cut=True)
        self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups],
                        integral=sqrt(4 * pi), forces=True)

    def set_positions(self, spos_ac, rank_a=None):
        Density.set_positions(self, spos_ac, rank_a)
        self.nct_G = self.gd.zeros()
        self.nct.add(self.nct_G, 1.0 / self.nspins)

    def interpolate_pseudo_density(self, comp_charge=None):
        """Interpolate pseudo density to fine grid."""
        if comp_charge is None:
            comp_charge = self.calculate_multipole_moments()

        self.nt_sg = self.interpolate(self.nt_sG, self.nt_sg)

        # With periodic boundary conditions, the interpolation will
        # conserve the number of electrons.
        if not self.gd.pbc_c.all():
            # With zero-boundary conditions in one or more directions,
            # this is not the case.
            pseudo_charge = -(self.charge + comp_charge)
            if abs(pseudo_charge) > 1.0e-14:
                x = (pseudo_charge /
                     self.finegd.integrate(self.nt_sg[:self.nspins]).sum())
                self.nt_sg *= x

    def interpolate(self, in_xR, out_xR=None):
        """Interpolate array(s)."""

        # ndim will be 3 in finite-difference mode and 1 when working
        # with the AtomPAW class (spherical atoms and 1d grids)
        ndim = self.gd.ndim

        if out_xR is None:
            out_xR = self.finegd.empty(in_xR.shape[:-ndim])

        a_xR = in_xR.reshape((-1,) + in_xR.shape[-ndim:])
        b_xR = out_xR.reshape((-1,) + out_xR.shape[-ndim:])
        
        for in_R, out_R in zip(a_xR, b_xR):
            self.interpolator.apply(in_R, out_R)

        return out_xR

    def calculate_pseudo_charge(self):
        self.nt_g = self.nt_sg[:self.nspins].sum(axis=0)
        self.rhot_g = self.nt_g.copy()
        self.ghat.add(self.rhot_g, self.Q_aL)

        if debug:
            charge = self.finegd.integrate(self.rhot_g) + self.charge
            if abs(charge) > self.charge_eps:
                raise RuntimeError('Charge not conserved: excess=%.9f' %
                                   charge)

    def get_pseudo_core_kinetic_energy_density_lfc(self):
        return LFC(self.gd,
                   [[setup.tauct] for setup in self.setups],
                   forces=True, cut=True)

    def calculate_dipole_moment(self):
        return self.finegd.calculate_dipole_moment(self.rhot_g)
예제 #18
0
class HybridXC(HybridXCBase):
    def __init__(self,
                 name,
                 hybrid=None,
                 xc=None,
                 finegrid=False,
                 unocc=False,
                 omega=None,
                 excitation=None,
                 excited=0,
                 stencil=2):
        """Mix standard functionals with exact exchange.

        finegrid: boolean
            Use fine grid for energy functional evaluations ?
        unocc: boolean
            Apply vxx also to unoccupied states ?
        omega: float
            RSF mixing parameter
        excitation: string:
            Apply operator for improved virtual orbitals
            to unocc states? Possible modes:
                singlet: excitations to singlets
                triplet: excitations to triplets
                average: average between singlets and tripletts
                see f.e. http://dx.doi.org/10.1021/acs.jctc.8b00238
        excited: number
            Band to excite from - counted from H**O downwards

        """
        self.finegrid = finegrid
        self.unocc = unocc
        self.excitation = excitation
        self.excited = excited
        HybridXCBase.__init__(self,
                              name,
                              hybrid=hybrid,
                              xc=xc,
                              omega=omega,
                              stencil=stencil)

    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):
        assert wfs.kd.gamma
        self.xc.initialize(density, hamiltonian, wfs, occupations)
        self.kpt_comm = wfs.kd.comm
        self.nspins = wfs.nspins
        self.setups = wfs.setups
        self.density = density
        self.kpt_u = wfs.kpt_u
        self.exx_s = np.zeros(self.nspins)
        self.ekin_s = np.zeros(self.nspins)
        self.nocc_s = np.empty(self.nspins, int)

        self.gd = density.gd
        self.redistributor = density.redistributor

        use_charge_center = hamiltonian.poisson.use_charge_center
        # XXX How do we construct a copy of the Poisson solver of the
        # Hamiltonian?  We don't know what class it is, etc., but gd
        # may differ.
        # XXX One might consider using a charged centered compensation
        # charge for the PoissonSolver in the case of EXX as standard
        self.poissonsolver = PoissonSolver('fd',
                                           eps=1e-11,
                                           use_charge_center=use_charge_center)
        # self.poissonsolver = hamiltonian.poisson

        if self.finegrid:
            self.finegd = self.gd.refine()
            # XXX Taking restrictor from Hamiltonian will not work in PW mode,
            # will it?  I think this supports only real-space mode.
            # self.restrictor = hamiltonian.restrictor
            self.restrictor = Transformer(self.finegd, self.gd, 3)
            self.interpolator = Transformer(self.gd, self.finegd, 3)
        else:
            self.finegd = self.gd

        self.ghat = LFC(self.finegd,
                        [setup.ghat_l for setup in density.setups],
                        integral=np.sqrt(4 * np.pi),
                        forces=True)
        self.poissonsolver.set_grid_descriptor(self.finegd)
        if self.rsf == 'Yukawa':
            omega2 = self.omega**2
            self.screened_poissonsolver = HelmholtzSolver(
                k2=-omega2,
                eps=1e-11,
                nn=3,
                use_charge_center=use_charge_center)
            self.screened_poissonsolver.set_grid_descriptor(self.finegd)

    def set_positions(self, spos_ac):
        self.ghat.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)
        # Note that the quantities passed are on the
        # density/Hamiltonian grids!
        # They may be distributed differently from own quantities.
        self.ekin = self.kpt_comm.sum(self.ekin_s.sum())
        return exc + self.kpt_comm.sum(self.exx_s.sum())

    def calculate_exx(self):
        for kpt in self.kpt_u:
            self.apply_orbital_dependent_hamiltonian(kpt, kpt.psit_nG)

    def apply_orbital_dependent_hamiltonian(self,
                                            kpt,
                                            psit_nG,
                                            Htpsit_nG=None,
                                            dH_asp=None):
        if kpt.f_n is None:
            return

        deg = 2 // self.nspins  # Spin degeneracy
        hybrid = self.hybrid
        P_ani = kpt.P_ani
        setups = self.setups
        is_cam = self.is_cam

        vt_g = self.finegd.empty()
        if self.gd is not self.finegd:
            vt_G = self.gd.empty()
        if self.rsf == 'Yukawa':
            y_vt_g = self.finegd.empty()
            # if self.gd is not self.finegd:
            #     y_vt_G = self.gd.empty()

        nocc = int(ceil(kpt.f_n.sum())) // (3 - self.nspins)
        if self.excitation is not None:
            ex_band = nocc - self.excited - 1
            if self.excitation == 'singlet':
                ex_weight = -1
            elif self.excitation == 'triplet':
                ex_weight = +1
            else:
                ex_weight = 0

        if self.unocc or self.excitation is not None:
            nbands = len(kpt.f_n)
        else:
            nbands = nocc
        self.nocc_s[kpt.s] = nocc

        if Htpsit_nG is not None:
            kpt.vt_nG = self.gd.empty(nbands)
            kpt.vxx_ani = {}
            kpt.vxx_anii = {}
            for a, P_ni in P_ani.items():
                I = P_ni.shape[1]
                kpt.vxx_ani[a] = np.zeros((nbands, I))
                kpt.vxx_anii[a] = np.zeros((nbands, I, I))

        exx = 0.0
        ekin = 0.0

        # XXXX nbands can be different numbers on different cpus!
        # That means some will execute the loop and others not.
        # And deadlocks with augment-grids.

        # Determine pseudo-exchange
        for n1 in range(nbands):
            psit1_G = psit_nG[n1]
            f1 = kpt.f_n[n1] / deg
            for n2 in range(n1, nbands):
                psit2_G = psit_nG[n2]
                f2 = kpt.f_n[n2] / deg
                if n1 != n2 and f1 == 0 and f1 == f2:
                    continue  # Don't work on double unocc. bands
                # Double count factor:
                dc = (1 + (n1 != n2)) * deg
                nt_G, rhot_g = self.calculate_pair_density(
                    n1, n2, psit_nG, P_ani)
                vt_g[:] = 0.0
                # XXXXX This will go wrong because we are solving the
                # Poisson equation on the distribution of gd, not finegd
                # Or maybe it's fixed now

                self.poissonsolver.solve(vt_g,
                                         -rhot_g,
                                         charge=-float(n1 == n2),
                                         eps=1e-12,
                                         zero_initial_phi=True)
                vt_g *= hybrid
                if self.rsf == 'Yukawa':
                    y_vt_g[:] = 0.0
                    self.screened_poissonsolver.solve(y_vt_g,
                                                      -rhot_g,
                                                      charge=-float(n1 == n2),
                                                      eps=1e-12,
                                                      zero_initial_phi=True)
                    if is_cam:  # Cam like correction
                        y_vt_g *= self.cam_beta
                    else:
                        y_vt_g *= hybrid
                    vt_g -= y_vt_g
                if self.gd is self.finegd:
                    vt_G = vt_g
                else:
                    self.restrictor.apply(vt_g, vt_G)

                # Integrate the potential on fine and coarse grids
                int_fine = self.finegd.integrate(vt_g * rhot_g)
                int_coarse = self.gd.integrate(vt_G * nt_G)
                if self.gd.comm.rank == 0:  # only add to energy on master CPU
                    exx += 0.5 * dc * f1 * f2 * int_fine
                    ekin -= dc * f1 * f2 * int_coarse
                if Htpsit_nG is not None:
                    Htpsit_nG[n1] += f2 * vt_G * psit2_G
                    if n1 == n2:
                        kpt.vt_nG[n1] = f1 * vt_G
                        if self.excitation is not None and n1 == ex_band:
                            Htpsit_nG[nocc:] += f1 * vt_G * psit_nG[nocc:]
                    else:
                        if self.excitation is None or n1 != ex_band \
                                or n2 < nocc:
                            Htpsit_nG[n2] += f1 * vt_G * psit1_G
                        else:
                            Htpsit_nG[n2] += f1 * ex_weight * vt_G * psit1_G

                    # Update the vxx_uni and vxx_unii vectors of the nuclei,
                    # used to determine the atomic hamiltonian, and the
                    # residuals
                    v_aL = self.ghat.dict()
                    self.ghat.integrate(vt_g, v_aL)
                    for a, v_L in v_aL.items():
                        v_ii = unpack(np.dot(setups[a].Delta_pL, v_L))
                        v_ni = kpt.vxx_ani[a]
                        v_nii = kpt.vxx_anii[a]
                        P_ni = P_ani[a]
                        v_ni[n1] += f2 * np.dot(v_ii, P_ni[n2])
                        if n1 != n2:
                            if self.excitation is None or n1 != ex_band or \
                                    n2 < nocc:
                                v_ni[n2] += f1 * np.dot(v_ii, P_ni[n1])
                            else:
                                v_ni[n2] += f1 * ex_weight * \
                                    np.dot(v_ii, P_ni[n1])
                        else:
                            # XXX Check this:
                            v_nii[n1] = f1 * v_ii
                            if self.excitation is not None and n1 == ex_band:
                                for nuoc in range(nocc, nbands):
                                    v_ni[nuoc] += f1 * \
                                        np.dot(v_ii, P_ni[nuoc])

        def calculate_vv(ni, D_ii, M_pp, weight, addme=False):
            """Calculate the local corrections depending on Mpp."""
            dexx = 0
            dekin = 0
            if not addme:
                addsign = -2.0
            else:
                addsign = 2.0
            for i1 in range(ni):
                for i2 in range(ni):
                    A = 0.0
                    for i3 in range(ni):
                        p13 = packed_index(i1, i3, ni)
                        for i4 in range(ni):
                            p24 = packed_index(i2, i4, ni)
                            A += M_pp[p13, p24] * D_ii[i3, i4]
                    p12 = packed_index(i1, i2, ni)
                    if Htpsit_nG is not None:
                        dH_p[p12] += addsign * weight / \
                            deg * A / ((i1 != i2) + 1)
                    dekin += 2 * weight / deg * D_ii[i1, i2] * A
                    dexx -= weight / deg * D_ii[i1, i2] * A
            return (dexx, dekin)

        # Apply the atomic corrections to the energy and the Hamiltonian
        # matrix
        for a, P_ni in P_ani.items():
            setup = setups[a]

            if Htpsit_nG is not None:
                # Add non-trivial corrections the Hamiltonian matrix
                h_nn = symmetrize(
                    np.inner(P_ni[:nbands], kpt.vxx_ani[a][:nbands]))
                ekin -= np.dot(kpt.f_n[:nbands], h_nn.diagonal())

                dH_p = dH_asp[a][kpt.s]

            # Get atomic density and Hamiltonian matrices
            D_p = self.density.D_asp[a][kpt.s]
            D_ii = unpack2(D_p)
            ni = len(D_ii)

            # Add atomic corrections to the valence-valence exchange energy
            # --
            # >  D   C     D
            # --  ii  iiii  ii
            (dexx, dekin) = calculate_vv(ni, D_ii, setup.M_pp, hybrid)
            ekin += dekin
            exx += dexx
            if self.rsf is not None:
                Mg_pp = setup.calculate_yukawa_interaction(self.omega)
                if is_cam:
                    (dexx, dekin) = calculate_vv(ni,
                                                 D_ii,
                                                 Mg_pp,
                                                 self.cam_beta,
                                                 addme=True)
                else:
                    (dexx, dekin) = calculate_vv(ni,
                                                 D_ii,
                                                 Mg_pp,
                                                 hybrid,
                                                 addme=True)
                ekin -= dekin
                exx -= dexx
            # Add valence-core exchange energy
            # --
            # >  X   D
            # --  ii  ii
            if setup.X_p is not None:
                exx -= hybrid * np.dot(D_p, setup.X_p)
                if Htpsit_nG is not None:
                    dH_p -= hybrid * setup.X_p
                    ekin += hybrid * np.dot(D_p, setup.X_p)

                if self.rsf == 'Yukawa' and setup.X_pg is not None:
                    if is_cam:
                        thybrid = self.cam_beta  # 0th order
                    else:
                        thybrid = hybrid
                    exx += thybrid * np.dot(D_p, setup.X_pg)
                    if Htpsit_nG is not None:
                        dH_p += thybrid * setup.X_pg
                        ekin -= thybrid * np.dot(D_p, setup.X_pg)
                elif self.rsf == 'Yukawa' and setup.X_pg is None:
                    thybrid = exp(-3.62e-2 * self.omega)  # educated guess
                    if is_cam:
                        thybrid *= self.cam_beta
                    else:
                        thybrid *= hybrid
                    exx += thybrid * np.dot(D_p, setup.X_p)
                    if Htpsit_nG is not None:
                        dH_p += thybrid * setup.X_p
                        ekin -= thybrid * np.dot(D_p, setup.X_p)
                # Add core-core exchange energy
                if kpt.s == 0:
                    if self.rsf is None or is_cam:
                        if is_cam:
                            exx += self.cam_alpha * setup.ExxC
                        else:
                            exx += hybrid * setup.ExxC

        self.exx_s[kpt.s] = self.gd.comm.sum(exx)
        self.ekin_s[kpt.s] = self.gd.comm.sum(ekin)

    def correct_hamiltonian_matrix(self, kpt, H_nn):
        if not hasattr(kpt, 'vxx_ani'):
            return

        # if self.gd.comm.rank > 0:
        #    H_nn[:] = 0.0

        nocc = self.nocc_s[kpt.s]
        nbands = len(kpt.vt_nG)
        for a, P_ni in kpt.P_ani.items():
            H_nn[:nbands, :nbands] += symmetrize(
                np.inner(P_ni[:nbands], kpt.vxx_ani[a]))
        # self.gd.comm.sum(H_nn)

        if not self.unocc or self.excitation is not None:
            H_nn[:nocc, nocc:] = 0.0
            H_nn[nocc:, :nocc] = 0.0

    def calculate_pair_density(self, n1, n2, psit_nG, P_ani):
        Q_aL = {}
        for a, P_ni in P_ani.items():
            P1_i = P_ni[n1]
            P2_i = P_ni[n2]
            D_ii = np.outer(P1_i, P2_i.conj()).real
            D_p = pack(D_ii)
            Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL)

        nt_G = psit_nG[n1] * psit_nG[n2]

        if self.finegd is self.gd:
            nt_g = nt_G
        else:
            nt_g = self.finegd.empty()
            self.interpolator.apply(nt_G, nt_g)

        rhot_g = nt_g.copy()
        self.ghat.add(rhot_g, Q_aL)

        return nt_G, rhot_g

    def add_correction(self,
                       kpt,
                       psit_xG,
                       Htpsit_xG,
                       P_axi,
                       c_axi,
                       n_x,
                       calculate_change=False):
        if kpt.f_n is None:
            return

        if self.unocc or self.excitation is not None:
            nocc = len(kpt.vt_nG)
        else:
            nocc = self.nocc_s[kpt.s]

        if calculate_change:
            for x, n in enumerate(n_x):
                if n < nocc:
                    Htpsit_xG[x] += kpt.vt_nG[n] * psit_xG[x]
                    for a, P_xi in P_axi.items():
                        c_axi[a][x] += np.dot(kpt.vxx_anii[a][n], P_xi[x])
        else:
            for a, c_xi in c_axi.items():
                c_xi[:nocc] += kpt.vxx_ani[a][:nocc]

    def rotate(self, kpt, U_nn):
        if kpt.f_n is None:
            return

        U_nn = U_nn.T.copy()
        nocc = self.nocc_s[kpt.s]
        if len(kpt.vt_nG) == nocc:
            U_nn = U_nn[:nocc, :nocc]
        gemm(1.0, kpt.vt_nG.copy(), U_nn, 0.0, kpt.vt_nG)
        for v_ni in kpt.vxx_ani.values():
            gemm(1.0, v_ni.copy(), U_nn, 0.0, v_ni)
        for v_nii in kpt.vxx_anii.values():
            gemm(1.0, v_nii.copy(), U_nn, 0.0, v_nii)
예제 #19
0
class HybridXC(HybridXCBase):
    def __init__(self,
                 name,
                 hybrid=None,
                 xc=None,
                 finegrid=False,
                 unocc=False):
        """Mix standard functionals with exact exchange.

        finegrid: boolean
            Use fine grid for energy functional evaluations ?
        unocc: boolean
            Apply vxx also to unoccupied states ?
        """
        self.finegrid = finegrid
        self.unocc = unocc
        HybridXCBase.__init__(self, name, hybrid, xc)

    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):
        assert wfs.kd.gamma
        self.xc.initialize(density, hamiltonian, wfs, occupations)
        self.kpt_comm = wfs.kd.comm
        self.nspins = wfs.nspins
        self.setups = wfs.setups
        self.density = density
        self.kpt_u = wfs.kpt_u
        self.exx_s = np.zeros(self.nspins)
        self.ekin_s = np.zeros(self.nspins)
        self.nocc_s = np.empty(self.nspins, int)

        if self.finegrid:
            self.poissonsolver = hamiltonian.poisson
            self.ghat = density.ghat
            self.interpolator = density.interpolator
            self.restrictor = hamiltonian.restrictor
        else:
            self.poissonsolver = PoissonSolver(eps=1e-11)
            self.poissonsolver.set_grid_descriptor(density.gd)
            self.poissonsolver.initialize()
            self.ghat = LFC(density.gd,
                            [setup.ghat_l for setup in density.setups],
                            integral=np.sqrt(4 * np.pi),
                            forces=True)
        self.gd = density.gd
        self.finegd = self.ghat.gd

    def set_positions(self, spos_ac):
        if not self.finegrid:
            self.ghat.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)
        self.ekin = self.kpt_comm.sum(self.ekin_s.sum())
        return exc + self.kpt_comm.sum(self.exx_s.sum())

    def calculate_exx(self):
        for kpt in self.kpt_u:
            self.apply_orbital_dependent_hamiltonian(kpt, kpt.psit_nG)

    def apply_orbital_dependent_hamiltonian(self,
                                            kpt,
                                            psit_nG,
                                            Htpsit_nG=None,
                                            dH_asp=None):
        if kpt.f_n is None:
            return

        deg = 2 // self.nspins  # Spin degeneracy
        hybrid = self.hybrid

        P_ani = kpt.P_ani
        setups = self.setups

        vt_g = self.finegd.empty()
        if self.gd is not self.finegd:
            vt_G = self.gd.empty()

        nocc = int(kpt.f_n.sum()) // (3 - self.nspins)
        if self.unocc:
            nbands = len(kpt.f_n)
        else:
            nbands = nocc
        self.nocc_s[kpt.s] = nocc

        if Htpsit_nG is not None:
            kpt.vt_nG = self.gd.empty(nbands)
            kpt.vxx_ani = {}
            kpt.vxx_anii = {}
            for a, P_ni in P_ani.items():
                I = P_ni.shape[1]
                kpt.vxx_ani[a] = np.zeros((nbands, I))
                kpt.vxx_anii[a] = np.zeros((nbands, I, I))

        exx = 0.0
        ekin = 0.0

        # Determine pseudo-exchange
        for n1 in range(nbands):
            psit1_G = psit_nG[n1]
            f1 = kpt.f_n[n1] / deg
            for n2 in range(n1, nbands):
                psit2_G = psit_nG[n2]
                f2 = kpt.f_n[n2] / deg

                # Double count factor:
                dc = (1 + (n1 != n2)) * deg

                nt_G, rhot_g = self.calculate_pair_density(
                    n1, n2, psit_nG, P_ani)
                vt_g[:] = 0.0
                iter = self.poissonsolver.solve(vt_g,
                                                -rhot_g,
                                                charge=-float(n1 == n2),
                                                eps=1e-12,
                                                zero_initial_phi=True)
                vt_g *= hybrid

                if self.gd is self.finegd:
                    vt_G = vt_g
                else:
                    self.restrictor.apply(vt_g, vt_G)

                # Integrate the potential on fine and coarse grids
                int_fine = self.finegd.integrate(vt_g * rhot_g)
                int_coarse = self.gd.integrate(vt_G * nt_G)
                if self.gd.comm.rank == 0:  # only add to energy on master CPU
                    exx += 0.5 * dc * f1 * f2 * int_fine
                    ekin -= dc * f1 * f2 * int_coarse
                if Htpsit_nG is not None:
                    Htpsit_nG[n1] += f2 * vt_G * psit2_G
                    if n1 == n2:
                        kpt.vt_nG[n1] = f1 * vt_G
                    else:
                        Htpsit_nG[n2] += f1 * vt_G * psit1_G

                    # Update the vxx_uni and vxx_unii vectors of the nuclei,
                    # used to determine the atomic hamiltonian, and the
                    # residuals
                    v_aL = self.ghat.dict()
                    self.ghat.integrate(vt_g, v_aL)
                    for a, v_L in v_aL.items():
                        v_ii = unpack(np.dot(setups[a].Delta_pL, v_L))
                        v_ni = kpt.vxx_ani[a]
                        v_nii = kpt.vxx_anii[a]
                        P_ni = P_ani[a]
                        v_ni[n1] += f2 * np.dot(v_ii, P_ni[n2])
                        if n1 != n2:
                            v_ni[n2] += f1 * np.dot(v_ii, P_ni[n1])
                        else:
                            # XXX Check this:
                            v_nii[n1] = f1 * v_ii

        # Apply the atomic corrections to the energy and the Hamiltonian matrix
        for a, P_ni in P_ani.items():
            setup = setups[a]

            if Htpsit_nG is not None:
                # Add non-trivial corrections the Hamiltonian matrix
                h_nn = symmetrize(
                    np.inner(P_ni[:nbands], kpt.vxx_ani[a][:nbands]))
                ekin -= np.dot(kpt.f_n[:nbands], h_nn.diagonal())

                dH_p = dH_asp[a][kpt.s]

            # Get atomic density and Hamiltonian matrices
            D_p = self.density.D_asp[a][kpt.s]
            D_ii = unpack2(D_p)
            ni = len(D_ii)

            # Add atomic corrections to the valence-valence exchange energy
            # --
            # >  D   C     D
            # --  ii  iiii  ii
            for i1 in range(ni):
                for i2 in range(ni):
                    A = 0.0
                    for i3 in range(ni):
                        p13 = packed_index(i1, i3, ni)
                        for i4 in range(ni):
                            p24 = packed_index(i2, i4, ni)
                            A += setup.M_pp[p13, p24] * D_ii[i3, i4]
                    p12 = packed_index(i1, i2, ni)
                    if Htpsit_nG is not None:
                        dH_p[p12] -= 2 * hybrid / deg * A / ((i1 != i2) + 1)
                    ekin += 2 * hybrid / deg * D_ii[i1, i2] * A
                    exx -= hybrid / deg * D_ii[i1, i2] * A

            # Add valence-core exchange energy
            # --
            # >  X   D
            # --  ii  ii
            if setup.X_p is not None:
                exx -= hybrid * np.dot(D_p, setup.X_p)
                if Htpsit_nG is not None:
                    dH_p -= hybrid * setup.X_p
                    ekin += hybrid * np.dot(D_p, setup.X_p)

                # Add core-core exchange energy
                if kpt.s == 0:
                    exx += hybrid * setup.ExxC

        self.exx_s[kpt.s] = self.gd.comm.sum(exx)
        self.ekin_s[kpt.s] = self.gd.comm.sum(ekin)

    def correct_hamiltonian_matrix(self, kpt, H_nn):
        if not hasattr(kpt, 'vxx_ani'):
            return

        if self.gd.comm.rank > 0:
            H_nn[:] = 0.0

        nocc = self.nocc_s[kpt.s]
        nbands = len(kpt.vt_nG)
        for a, P_ni in kpt.P_ani.items():
            H_nn[:nbands, :nbands] += symmetrize(
                np.inner(P_ni[:nbands], kpt.vxx_ani[a]))
        self.gd.comm.sum(H_nn)

        H_nn[:nocc, nocc:] = 0.0
        H_nn[nocc:, :nocc] = 0.0

    def calculate_pair_density(self, n1, n2, psit_nG, P_ani):
        Q_aL = {}
        for a, P_ni in P_ani.items():
            P1_i = P_ni[n1]
            P2_i = P_ni[n2]
            D_ii = np.outer(P1_i, P2_i.conj()).real
            D_p = pack(D_ii)
            Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL)

        nt_G = psit_nG[n1] * psit_nG[n2]

        if self.finegd is self.gd:
            nt_g = nt_G
        else:
            nt_g = self.finegd.empty()
            self.interpolator.apply(nt_G, nt_g)

        rhot_g = nt_g.copy()
        self.ghat.add(rhot_g, Q_aL)

        return nt_G, rhot_g

    def add_correction(self,
                       kpt,
                       psit_xG,
                       Htpsit_xG,
                       P_axi,
                       c_axi,
                       n_x,
                       calculate_change=False):
        if kpt.f_n is None:
            return

        nocc = self.nocc_s[kpt.s]

        if calculate_change:
            for x, n in enumerate(n_x):
                if n < nocc:
                    Htpsit_xG[x] += kpt.vt_nG[n] * psit_xG[x]
                    for a, P_xi in P_axi.items():
                        c_axi[a][x] += np.dot(kpt.vxx_anii[a][n], P_xi[x])
        else:
            for a, c_xi in c_axi.items():
                c_xi[:nocc] += kpt.vxx_ani[a][:nocc]

    def rotate(self, kpt, U_nn):
        if kpt.f_n is None:
            return

        nocc = self.nocc_s[kpt.s]
        if len(kpt.vt_nG) == nocc:
            U_nn = U_nn[:nocc, :nocc]
        gemm(1.0, kpt.vt_nG.copy(), U_nn, 0.0, kpt.vt_nG)
        for v_ni in kpt.vxx_ani.values():
            gemm(1.0, v_ni.copy(), U_nn, 0.0, v_ni)
        for v_nii in kpt.vxx_anii.values():
            gemm(1.0, v_nii.copy(), U_nn, 0.0, v_nii)
예제 #20
0
class RealSpaceDensity(Density):
    def __init__(self,
                 gd,
                 finegd,
                 nspins,
                 charge,
                 redistributor,
                 stencil=3,
                 background_charge=None):
        Density.__init__(self,
                         gd,
                         finegd,
                         nspins,
                         charge,
                         redistributor,
                         background_charge=background_charge)
        self.stencil = stencil

    def initialize(self, setups, timer, magmom_a, hund):
        Density.initialize(self, setups, timer, magmom_a, hund)

        # Interpolation function for the density:
        self.interpolator = Transformer(self.redistributor.aux_gd, self.finegd,
                                        self.stencil)

        spline_aj = []
        for setup in setups:
            if setup.nct is None:
                spline_aj.append([])
            else:
                spline_aj.append([setup.nct])
        self.nct = LFC(self.gd,
                       spline_aj,
                       integral=[setup.Nct for setup in setups],
                       forces=True,
                       cut=True)
        self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups],
                        integral=sqrt(4 * pi),
                        forces=True)

    def set_positions(self, spos_ac, rank_a=None):
        Density.set_positions(self, spos_ac, rank_a)
        self.nct_G = self.gd.zeros()
        self.nct.add(self.nct_G, 1.0 / self.nspins)

    def interpolate_pseudo_density(self, comp_charge=None):
        """Interpolate pseudo density to fine grid."""
        if comp_charge is None:
            comp_charge = self.calculate_multipole_moments()

        self.nt_sg = self.distribute_and_interpolate(self.nt_sG, self.nt_sg)

        # With periodic boundary conditions, the interpolation will
        # conserve the number of electrons.
        if not self.gd.pbc_c.all():
            # With zero-boundary conditions in one or more directions,
            # this is not the case.
            pseudo_charge = (self.background_charge.charge - self.charge -
                             comp_charge)
            if abs(pseudo_charge) > 1.0e-14:
                x = (pseudo_charge / self.finegd.integrate(self.nt_sg).sum())
                self.nt_sg *= x

    def interpolate(self, in_xR, out_xR=None):
        """Interpolate array(s)."""

        # ndim will be 3 in finite-difference mode and 1 when working
        # with the AtomPAW class (spherical atoms and 1d grids)
        ndim = self.gd.ndim

        if out_xR is None:
            out_xR = self.finegd.empty(in_xR.shape[:-ndim])

        a_xR = in_xR.reshape((-1, ) + in_xR.shape[-ndim:])
        b_xR = out_xR.reshape((-1, ) + out_xR.shape[-ndim:])

        for in_R, out_R in zip(a_xR, b_xR):
            self.interpolator.apply(in_R, out_R)

        return out_xR

    def distribute_and_interpolate(self, in_xR, out_xR=None):
        in_xR = self.redistributor.distribute(in_xR)
        return self.interpolate(in_xR, out_xR)

    def calculate_pseudo_charge(self):
        self.nt_g = self.nt_sg.sum(axis=0)
        self.rhot_g = self.nt_g.copy()
        self.ghat.add(self.rhot_g, self.Q_aL)
        self.background_charge.add_charge_to(self.rhot_g)

        if debug:
            charge = self.finegd.integrate(self.rhot_g) + self.charge
            if abs(charge) > self.charge_eps:
                raise RuntimeError('Charge not conserved: excess=%.9f' %
                                   charge)

    def get_pseudo_core_kinetic_energy_density_lfc(self):
        return LFC(self.gd, [[setup.tauct] for setup in self.setups],
                   forces=True,
                   cut=True)

    def calculate_dipole_moment(self):
        return self.finegd.calculate_dipole_moment(self.rhot_g)
예제 #21
0
class RealSpaceHamiltonian(Hamiltonian):
    def __init__(self,
                 gd,
                 finegd,
                 nspins,
                 setups,
                 timer,
                 xc,
                 world,
                 vext=None,
                 psolver=None,
                 stencil=3,
                 redistributor=None):
        Hamiltonian.__init__(self,
                             gd,
                             finegd,
                             nspins,
                             setups,
                             timer,
                             xc,
                             world,
                             vext=vext,
                             redistributor=redistributor)

        # Solver for the Poisson equation:
        if psolver is None:
            psolver = {}
        if isinstance(psolver, dict):
            psolver = create_poisson_solver(**psolver)
        self.poisson = psolver
        self.poisson.set_grid_descriptor(self.finegd)

        # Restrictor function for the potential:
        self.restrictor = Transformer(self.finegd, self.redistributor.aux_gd,
                                      stencil)
        self.restrict = self.restrictor.apply

        self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups],
                        forces=True)
        self.vbar_g = None

    def restrict_and_collect(self, a_xg, b_xg=None, phases=None):
        if self.redistributor.enabled:
            tmp_xg = self.restrictor.apply(a_xg, output=None, phases=phases)
            b_xg = self.redistributor.collect(tmp_xg, b_xg)
        else:
            b_xg = self.restrictor.apply(a_xg, output=b_xg, phases=phases)
        return b_xg

    def __str__(self):
        s = Hamiltonian.__str__(self)

        degree = self.restrictor.nn * 2 - 1
        name = ['linear', 'cubic', 'quintic', 'heptic'][degree // 2]
        s += ('  Interpolation: tri-%s ' % name +
              '(%d. degree polynomial)\n' % degree)
        s += '  Poisson solver: %s' % self.poisson.get_description()
        return s

    def set_positions(self, spos_ac, rank_a):
        Hamiltonian.set_positions(self, spos_ac, rank_a)
        if self.vbar_g is None:
            self.vbar_g = self.finegd.empty()
        self.vbar_g[:] = 0.0
        self.vbar.add(self.vbar_g)

    def update_pseudo_potential(self, dens):
        self.timer.start('vbar')
        e_zero = self.finegd.integrate(self.vbar_g,
                                       dens.nt_g,
                                       global_integral=False)

        vt_g = self.vt_sg[0]
        vt_g[:] = self.vbar_g
        self.timer.stop('vbar')

        e_external = 0.0
        if self.vext is not None:
            vext_g = self.vext.get_potential(self.finegd)
            vt_g += vext_g
            e_external = self.finegd.integrate(vext_g,
                                               dens.rhot_g,
                                               global_integral=False)

        if self.nspins == 2:
            self.vt_sg[1] = vt_g

        self.timer.start('XC 3D grid')
        e_xc = self.xc.calculate(self.finegd, dens.nt_sg, self.vt_sg)
        e_xc /= self.finegd.comm.size
        self.timer.stop('XC 3D grid')

        self.timer.start('Poisson')
        # npoisson is the number of iterations:
        self.npoisson = self.poisson.solve(self.vHt_g,
                                           dens.rhot_g,
                                           charge=-dens.charge)
        self.timer.stop('Poisson')

        self.timer.start('Hartree integrate/restrict')
        e_coulomb = 0.5 * self.finegd.integrate(
            self.vHt_g, dens.rhot_g, global_integral=False)

        for vt_g in self.vt_sg:
            vt_g += self.vHt_g

        self.timer.stop('Hartree integrate/restrict')
        return np.array([e_coulomb, e_zero, e_external, e_xc])

    def calculate_kinetic_energy(self, density):
        # XXX new timer item for kinetic energy?
        self.timer.start('Hartree integrate/restrict')
        self.restrict_and_collect(self.vt_sg, self.vt_sG)

        e_kinetic = 0.0
        s = 0
        for vt_G, nt_G in zip(self.vt_sG, density.nt_sG):
            if self.ref_vt_sG is not None:
                vt_G += self.ref_vt_sG[s]

            if s < self.nspins:
                e_kinetic -= self.gd.integrate(vt_G,
                                               nt_G - density.nct_G,
                                               global_integral=False)
            else:
                e_kinetic -= self.gd.integrate(vt_G,
                                               nt_G,
                                               global_integral=False)
            s += 1
        self.timer.stop('Hartree integrate/restrict')
        return e_kinetic

    def calculate_atomic_hamiltonians(self, dens):
        def getshape(a):
            return sum(2 * l + 1 for l, _ in enumerate(self.setups[a].ghat_l)),

        W_aL = ArrayDict(self.atomdist.aux_partition, getshape, float)
        if self.vext:
            vext_g = self.vext.get_potential(self.finegd)
            dens.ghat.integrate(self.vHt_g + vext_g, W_aL)
        else:
            dens.ghat.integrate(self.vHt_g, W_aL)

        return self.atomdist.to_work(self.atomdist.from_aux(W_aL))

    def calculate_forces2(self, dens, ghat_aLv, nct_av, vbar_av):
        if self.nspins == 2:
            vt_G = self.vt_sG.mean(0)
        else:
            vt_G = self.vt_sG[0]

        self.vbar.derivative(dens.nt_g, vbar_av)
        if self.vext:
            vext_g = self.vext.get_potential(self.finegd)
            dens.ghat.derivative(self.vHt_g + vext_g, ghat_aLv)
        else:
            dens.ghat.derivative(self.vHt_g, ghat_aLv)
        dens.nct.derivative(vt_G, nct_av)

    def get_electrostatic_potential(self, dens):
        self.update(dens)

        v_g = self.finegd.collect(self.vHt_g, broadcast=True)
        v_g = self.finegd.zero_pad(v_g)
        if hasattr(self.poisson, 'correction'):
            assert self.poisson.c == 2
            v_g[:, :, 0] = self.poisson.correction
        return v_g