Beispiel #1
0
    def run(self, mix=0.4, maxiter=117, dnmax=1e-9):
        if self.channels is None:
            self.initialize()

        dn = self.Z
        pb = ProgressBar(log(dnmax / dn), 0, 53, self.fd)
        self.log()

        for iter in range(maxiter):
            if iter > 1:
                self.vr_sg *= mix
                self.vr_sg += (1 - mix) * vr_old_sg
                dn = self.gd.integrate(abs(self.n_sg - n_old_sg).sum(0))
                pb(log(dnmax / dn))
                if dn <= dnmax:
                    break

            vr_old_sg = self.vr_sg
            n_old_sg = self.n_sg
            self.step()

        self.summary()
        if dn > dnmax:
            raise RuntimeError('Did not converge!')
Beispiel #2
0
    def _calculate(self, pd, chi0_wGG, chi0_wxvG, chi0_wvv, Q_aGii, m1, m2,
                   spins):
        wfs = self.calc.wfs

        if self.keep_occupied_states:
            self.mykpts = [
                self.get_k_point(s, K, n1, n2)
                for s, K, n1, n2 in self.mysKn1n2
            ]

        if self.eta == 0.0:
            update = self.update_hermitian
        elif self.hilbert:
            update = self.update_hilbert
        else:
            update = self.update

        q_c = pd.kd.bzk_kc[0]
        optical_limit = not self.no_optical_limit and np.allclose(q_c, 0.0)

        pb = ProgressBar(self.fd)

        self.timer.start('Loop')
        # kpt1 occupied and kpt2 empty:
        for kn, (s, K, n1, n2) in enumerate(self.mysKn1n2):
            pb.update(kn / len(self.mysKn1n2))
            if self.keep_occupied_states:
                kpt1 = self.mykpts[kn]
            else:
                kpt1 = self.get_k_point(s, K, n1, n2)

            if kpt1.s not in spins:
                continue

            with self.timer('k+q'):
                K2 = wfs.kd.find_k_plus_q(q_c, [kpt1.K])[0]
            with self.timer('get k2'):
                kpt2 = self.get_k_point(kpt1.s, K2, m1, m2, block=True)
            with self.timer('fft-indices'):
                Q_G = self.get_fft_indices(kpt1.K, kpt2.K, q_c, pd,
                                           kpt1.shift_c - kpt2.shift_c)

            for n in range(kpt1.n2 - kpt1.n1):
                eps1 = kpt1.eps_n[n]

                # Only update if there exists deps <= omegamax
                if self.omegamax is not None:
                    m = [
                        m for m, d in enumerate(eps1 - kpt2.eps_n)
                        if abs(d) <= self.omegamax
                    ]
                else:
                    m = range(0, kpt2.n2 - kpt2.n1)

                if not len(m):
                    continue

                deps_m = (eps1 - kpt2.eps_n)[m]
                f1 = kpt1.f_n[n]
                with self.timer('conj'):
                    ut1cc_R = kpt1.ut_nR[n].conj()
                with self.timer('paw'):
                    C1_aGi = [
                        np.dot(Q_Gii, P1_ni[n].conj())
                        for Q_Gii, P1_ni in zip(Q_aGii, kpt1.P_ani)
                    ]
                n_mG = self.calculate_pair_densities(ut1cc_R, C1_aGi, kpt2, pd,
                                                     Q_G)[m]
                df_m = (f1 - kpt2.f_n)[m]

                # This is not quite right for degenerate partially occupied
                # bands, but good enough for now:
                df_m[df_m <= 1e-20] = 0.0

                if optical_limit:
                    self.update_optical_limit(n, m, kpt1, kpt2, deps_m, df_m,
                                              n_mG, chi0_wxvG, chi0_wvv)

                update(n_mG, deps_m, df_m, chi0_wGG)

            if optical_limit and self.intraband:
                # Avoid that more ranks are summing up
                # the intraband contributions
                if kpt1.n1 == 0 and self.blockcomm.rank == 0:
                    assert self.nocc2 <= kpt2.nb, \
                        print('Error: Too few unoccupied bands')
                    self.update_intraband(kpt2)

        self.timer.stop('Loop')

        pb.finish()

        with self.timer('Sum CHI_0'):
            for chi0_GG in chi0_wGG:
                self.kncomm.sum(chi0_GG)

            if optical_limit:
                self.kncomm.sum(chi0_wxvG)
                self.kncomm.sum(chi0_wvv)
                if self.intraband:
                    self.kncomm.sum(self.chi0_vv)

        print('Memory used: {0:.3f} MB / CPU'.format(maxrss() / 1024**2),
              file=self.fd)

        if (self.eta == 0.0 or self.hilbert) and self.blockcomm.size == 1:
            # Fill in upper/lower triangle also:
            nG = pd.ngmax
            il = np.tril_indices(nG, -1)
            iu = il[::-1]
            if self.hilbert:
                for chi0_GG in chi0_wGG:
                    chi0_GG[il] = chi0_GG[iu].conj()
            else:
                for chi0_GG in chi0_wGG:
                    chi0_GG[iu] = chi0_GG[il].conj()

        if self.hilbert:
            with self.timer('Hilbert transform'):
                ht = HilbertTransform(self.omega_w, self.eta, self.timeordered)
                ht(chi0_wGG)
                if optical_limit:
                    ht(chi0_wvv)
                    ht(chi0_wxvG)
            print('Hilbert transform done', file=self.fd)

        if optical_limit and self.intraband:  # Add intraband contribution
            omega_w = self.omega_w.copy()
            if omega_w[0] == 0.0:
                omega_w[0] = 1e-14

            chi0_vv = self.chi0_vv
            self.world.broadcast(chi0_vv, 0)

            chi0_wvv += (
                chi0_vv[np.newaxis] /
                (omega_w[:, np.newaxis, np.newaxis] *
                 (omega_w[:, np.newaxis, np.newaxis] + 1j * self.eta)))

        return pd, chi0_wGG, chi0_wxvG, chi0_wvv
Beispiel #3
0
    def diagonalize_full_hamiltonian(self, ham, atoms, occupations, txt,
                                     nbands=None, scalapack=None, expert=False):
        assert self.dtype == complex

        if nbands is None:
            nbands = self.pd.ngmin // self.bd.comm.size * self.bd.comm.size
        else:
            assert nbands <= self.pd.ngmin

        if expert:
            iu = nbands
        else:
            iu = None

        self.bd = bd = BandDescriptor(nbands, self.bd.comm)

        p = functools.partial(print, file=txt)
        p('Diagonalizing full Hamiltonian ({0} lowest bands)'.format(nbands))
        p('Matrix size (min, max): {0}, {1}'.format(self.pd.ngmin,
                                                    self.pd.ngmax))
        mem = 3 * self.pd.ngmax**2 * 16 / bd.comm.size / 1024**2
        p('Approximate memory usage per core: {0:.3f} MB'.format(mem))
        if bd.comm.size > 1:
            if isinstance(scalapack, (list, tuple)):
                nprow, npcol, b = scalapack
            else:
                nprow = int(round(bd.comm.size**0.5))
                while bd.comm.size % nprow != 0:
                    nprow -= 1
                npcol = bd.comm.size // nprow
                b = 64
            p('ScaLapack grid: {0}x{1},'.format(nprow, npcol),
              'block-size:', b)
            bg = BlacsGrid(bd.comm, bd.comm.size, 1)
            bg2 = BlacsGrid(bd.comm, nprow, npcol)
            scalapack = True
        else:
            nprow = npcol = 1
            scalapack = False

        self.pt.set_positions(atoms.get_scaled_positions())
        self.kpt_u[0].P_ani = None
        self.allocate_arrays_for_projections(self.pt.my_atom_indices)

        myslice = bd.get_slice()

        pb = ProgressBar(txt)
        nkpt = len(self.kpt_u)
        
        for u, kpt in enumerate(self.kpt_u):
            pb.update(u / nkpt)
            npw = len(self.pd.Q_qG[kpt.q])
            if scalapack:
                mynpw = -(-npw // bd.comm.size)
                md = BlacsDescriptor(bg, npw, npw, mynpw, npw)
                md2 = BlacsDescriptor(bg2, npw, npw, b, b)
            else:
                md = md2 = MatrixDescriptor(npw, npw)

            with self.timer('Build H and S'):
                H_GG, S_GG = self.hs(ham, kpt.q, kpt.s, md)

            if scalapack:
                r = Redistributor(bd.comm, md, md2)
                H_GG = r.redistribute(H_GG)
                S_GG = r.redistribute(S_GG)

            psit_nG = md2.empty(dtype=complex)
            eps_n = np.empty(npw)

            with self.timer('Diagonalize'):
                if not scalapack:
                    md2.general_diagonalize_dc(H_GG, S_GG, psit_nG, eps_n, 
                                               iu=iu)
                else:
                    md2.general_diagonalize_dc(H_GG, S_GG, psit_nG, eps_n)
            del H_GG, S_GG

            kpt.eps_n = eps_n[myslice].copy()

            if scalapack:
                md3 = BlacsDescriptor(bg, npw, npw, bd.mynbands, npw)
                r = Redistributor(bd.comm, md2, md3)
                psit_nG = r.redistribute(psit_nG)

            kpt.psit_nG = psit_nG[:bd.mynbands].copy()
            del psit_nG

            with self.timer('Projections'):
                self.pt.integrate(kpt.psit_nG, kpt.P_ani, kpt.q)

            kpt.f_n = None

        pb.finish()
        
        occupations.calculate(self)
Beispiel #4
0
    def spectral_function_integration(self, domain=None, integrand=None,
                                      x=None, kwargs=None, out_wxx=None):
        """Integrate response function.

        Assume that the integral has the
        form of a response function. For the linear tetrahedron
        method it is possible calculate frequency dependent weights
        and do a point summation using these weights."""

        if out_wxx is None:
            raise NotImplementedError

        nG = out_wxx.shape[2]
        mynG = (nG + self.blockcomm.size - 1) // self.blockcomm.size
        self.Ga = min(self.blockcomm.rank * mynG, nG)
        self.Gb = min(self.Ga + mynG, nG)
        # assert mynG * (self.blockcomm.size - 1) < nG, \
        #     print('mynG', mynG, 'nG', nG, 'nblocks', self.blockcomm.size)

        # Input domain
        td = self.tesselate(domain[0])
        args = domain[1:]
        get_matrix_element, get_eigenvalues = integrand

        # The kwargs contain any constant
        # arguments provided by the user
        if kwargs is not None:
            get_matrix_element = partial(get_matrix_element,
                                         **kwargs[0])
            get_eigenvalues = partial(get_eigenvalues,
                                      **kwargs[1])

        # Relevant quantities
        bzk_kc = td.points
        nk = len(bzk_kc)

        with self.timer('pts'):
            # Point to simplex
            pts_k = [[] for n in range(nk)]
            for s, K_k in enumerate(td.simplices):
                A_kv = np.append(td.points[K_k],
                                 np.ones(4)[:, np.newaxis], axis=1)

                D_kv = np.append((A_kv[:, :-1]**2).sum(1)[:, np.newaxis],
                                 A_kv, axis=1)
                a = np.linalg.det(D_kv[:, np.arange(5) != 0])

                if np.abs(a) < 1e-10:
                    continue

                for K in K_k:
                    pts_k[K].append(s)

            # Change to numpy arrays:
            for k in range(nk):
                pts_k[k] = np.array(pts_k[k], int)

        with self.timer('neighbours'):
            # Nearest neighbours
            neighbours_k = [None for n in range(nk)]

            for k in range(nk):
                neighbours_k[k] = np.unique(td.simplices[pts_k[k]])

        # Distribute everything
        myterms_t = self.distribute_domain(list(args) +
                                           [list(range(nk))])

        with self.timer('eigenvalues'):
            # Store eigenvalues
            deps_tMk = None  # t for term
            shape = [len(domain_l) for domain_l in args]
            nterms = int(np.prod(shape))

            for t in range(nterms):
                if len(shape) == 0:
                    arguments = ()
                else:
                    arguments = np.unravel_index(t, shape)
                for K in range(nk):
                    k_c = bzk_kc[K]
                    deps_M = -get_eigenvalues(k_c, *arguments)
                    if deps_tMk is None:
                        deps_tMk = np.zeros([nterms] +
                                            list(deps_M.shape) +
                                            [nk], float)
                    deps_tMk[t, :, K] = deps_M

        omega_w = x.get_data()

        # Calculate integrations weight
        pb = ProgressBar(self.fd)
        for _, arguments in pb.enumerate(myterms_t):
            K = arguments[-1]
            if len(shape) == 0:
                t = 0
            else:
                t = np.ravel_multi_index(arguments[:-1], shape)
            deps_Mk = deps_tMk[t]
            teteps_Mk = deps_Mk[:, neighbours_k[K]]
            i0_M, i1_M = x.get_index_range(teteps_Mk.min(1), teteps_Mk.max(1))

            n_MG = get_matrix_element(bzk_kc[K],
                                      *arguments[:-1])
            for n_G, deps_k, i0, i1 in zip(n_MG, deps_Mk,
                                           i0_M, i1_M):
                if i0 == i1:
                    continue

                W_w = self.get_kpoint_weight(K, deps_k,
                                             pts_k, omega_w[i0:i1],
                                             td)

                for iw, weight in enumerate(W_w):
                    if self.blockcomm.size > 1:
                        myn_G = n_G[self.Ga:self.Gb].reshape((-1, 1))
                        gemm(weight, n_G.reshape((-1, 1)), myn_G,
                             1.0, out_wxx[i0 + iw], 'c')
                    else:
                        czher(weight, n_G.conj(), out_wxx[i0 + iw])

        self.kncomm.sum(out_wxx)

        if self.blockcomm.size == 1:
            # Fill in upper/lower triangle also:
            nx = out_wxx.shape[1]
            il = np.tril_indices(nx, -1)
            iu = il[::-1]
            for out_xx in out_wxx:
                out_xx[il] = out_xx[iu].conj()
Beispiel #5
0
    def response_function_integration(self, domain=None, integrand=None,
                                      x=None, kwargs=None, out_wxx=None,
                                      timeordered=False, hermitian=False,
                                      intraband=False, hilbert=False,
                                      wings=False, **extraargs):
        """Integrate a response function over bands and kpoints.

        func: method
        omega_w: ndarray
        out: np.ndarray
        timeordered: Bool
        """
        if out_wxx is None:
            raise NotImplementedError

        nG = out_wxx.shape[2]
        mynG = (nG + self.blockcomm.size - 1) // self.blockcomm.size
        self.Ga = min(self.blockcomm.rank * mynG, nG)
        self.Gb = min(self.Ga + mynG, nG)
        # assert mynG * (self.blockcomm.size - 1) < nG, \
        #     print('mynG', mynG, 'nG', nG, 'nblocks', self.blockcomm.size)

        mydomain_t = self.distribute_domain(domain)
        nbz = len(domain[0])
        get_matrix_element, get_eigenvalues = integrand
        
        prefactor = (2 * np.pi)**3 / self.vol / nbz
        out_wxx /= prefactor

        # The kwargs contain any constant
        # arguments provided by the user
        if kwargs is not None:
            get_matrix_element = partial(get_matrix_element,
                                         **kwargs[0])
            get_eigenvalues = partial(get_eigenvalues,
                                      **kwargs[1])

        # Sum kpoints
        # Calculate integrations weight
        pb = ProgressBar(self.fd)
        for _, arguments in pb.enumerate(mydomain_t):
            n_MG = get_matrix_element(*arguments)
            if n_MG is None:
                continue
            deps_M = get_eigenvalues(*arguments)

            if intraband:
                self.update_intraband(n_MG, out_wxx, **extraargs)
            elif hermitian and not wings:
                self.update_hermitian(n_MG, deps_M, x, out_wxx, **extraargs)
            elif hermitian and wings:
                self.update_optical_limit(n_MG, deps_M, x, out_wxx,
                                          **extraargs)
            elif hilbert and not wings:
                self.update_hilbert(n_MG, deps_M, x, out_wxx, **extraargs)
            elif hilbert and wings:
                self.update_hilbert_optical_limit(n_MG, deps_M, x,
                                                  out_wxx, **extraargs)
            elif wings:
                self.update_optical_limit(n_MG, deps_M, x, out_wxx,
                                          **extraargs)
            else:
                self.update(n_MG, deps_M, x, out_wxx, **extraargs)

        # Sum over
        self.kncomm.sum(out_wxx)

        if (hermitian or hilbert) and self.blockcomm.size == 1 and not wings:
            # Fill in upper/lower triangle also:
            nx = out_wxx.shape[1]
            il = np.tril_indices(nx, -1)
            iu = il[::-1]
            if hilbert:
                for out_xx in out_wxx:
                    out_xx[il] = out_xx[iu].conj()
            else:
                for out_xx in out_wxx:
                    out_xx[iu] = out_xx[il].conj()
        
        out_wxx *= prefactor
Beispiel #6
0
    def diagonalize_full_hamiltonian(self, ham, atoms, occupations, log,
                                     nbands=None, ecut=None, scalapack=None,
                                     expert=False):

        if self.dtype != complex:
            raise ValueError('Your wavefunctions are not complex as '
                             'required by the PW diagonalization routine.\n'
                             'Please supply GPAW(..., dtype=complex, ...) '
                             'as an argument to the calculator to enforce '
                             'complex wavefunctions.')

        if nbands is None and ecut is None:
            nbands = self.pd.ngmin // self.bd.comm.size * self.bd.comm.size
        elif nbands is None:
            ecut /= units.Hartree
            vol = abs(np.linalg.det(self.gd.cell_cv))
            nbands = int(vol * ecut**1.5 * 2**0.5 / 3 / pi**2)
        else:
            assert nbands <= self.pd.ngmin

        if expert:
            iu = nbands
        else:
            iu = None

        self.bd = bd = BandDescriptor(nbands, self.bd.comm)

        log('Diagonalizing full Hamiltonian ({0} lowest bands)'.format(nbands))
        log('Matrix size (min, max): {0}, {1}'.format(self.pd.ngmin,
                                                      self.pd.ngmax))
        mem = 3 * self.pd.ngmax**2 * 16 / bd.comm.size / 1024**2
        log('Approximate memory usage per core: {0:.3f} MB'.format(mem))
        if bd.comm.size > 1:
            if isinstance(scalapack, (list, tuple)):
                nprow, npcol, b = scalapack
            else:
                nprow = int(round(bd.comm.size**0.5))
                while bd.comm.size % nprow != 0:
                    nprow -= 1
                npcol = bd.comm.size // nprow
                b = 64
            log('ScaLapack grid: {0}x{1},'.format(nprow, npcol),
                'block-size:', b)
            bg = BlacsGrid(bd.comm, bd.comm.size, 1)
            bg2 = BlacsGrid(bd.comm, nprow, npcol)
            scalapack = True
        else:
            nprow = npcol = 1
            scalapack = False

        self.set_positions(atoms.get_scaled_positions())
        self.kpt_u[0].P_ani = None
        self.allocate_arrays_for_projections(self.pt.my_atom_indices)

        myslice = bd.get_slice()

        pb = ProgressBar(log.fd)
        nkpt = len(self.kpt_u)

        for u, kpt in enumerate(self.kpt_u):
            pb.update(u / nkpt)
            npw = len(self.pd.Q_qG[kpt.q])
            if scalapack:
                mynpw = -(-npw // bd.comm.size)
                md = BlacsDescriptor(bg, npw, npw, mynpw, npw)
                md2 = BlacsDescriptor(bg2, npw, npw, b, b)
            else:
                md = md2 = MatrixDescriptor(npw, npw)

            with self.timer('Build H and S'):
                H_GG, S_GG = self.hs(ham, kpt.q, kpt.s, md)

            if scalapack:
                r = Redistributor(bd.comm, md, md2)
                H_GG = r.redistribute(H_GG)
                S_GG = r.redistribute(S_GG)

            psit_nG = md2.empty(dtype=complex)
            eps_n = np.empty(npw)

            with self.timer('Diagonalize'):
                if not scalapack:
                    md2.general_diagonalize_dc(H_GG, S_GG, psit_nG, eps_n,
                                               iu=iu)
                else:
                    md2.general_diagonalize_dc(H_GG, S_GG, psit_nG, eps_n)
            del H_GG, S_GG

            kpt.eps_n = eps_n[myslice].copy()

            if scalapack:
                md3 = BlacsDescriptor(bg, npw, npw, bd.mynbands, npw)
                r = Redistributor(bd.comm, md2, md3)
                psit_nG = r.redistribute(psit_nG)

            kpt.psit_nG = psit_nG[:bd.mynbands].copy()
            del psit_nG

            with self.timer('Projections'):
                self.pt.integrate(kpt.psit_nG, kpt.P_ani, kpt.q)

            kpt.f_n = None

        pb.finish()

        occupations.calculate(self)

        return nbands
    def _calculate(self, pd, chi0_wGG, chi0_wxvG, chi0_wvv, Q_aGii,
                   m1, m2, spins):
        wfs = self.calc.wfs

        if self.keep_occupied_states:
            self.mykpts = [self.get_k_point(s, K, n1, n2)
                           for s, K, n1, n2 in self.mysKn1n2]

        if self.eta == 0.0:
            update = self.update_hermitian
        elif self.hilbert:
            update = self.update_hilbert
        else:
            update = self.update

        q_c = pd.kd.bzk_kc[0]
        optical_limit = not self.no_optical_limit and np.allclose(q_c, 0.0)

        pb = ProgressBar(self.fd)

        self.timer.start('Loop')
        # kpt1 occupied and kpt2 empty:
        for kn, (s, K, n1, n2) in enumerate(self.mysKn1n2):
            pb.update(kn / len(self.mysKn1n2))
            if self.keep_occupied_states:
                kpt1 = self.mykpts[kn]
            else:
                kpt1 = self.get_k_point(s, K, n1, n2)

            if kpt1.s not in spins:
                continue

            with self.timer('k+q'):
                K2 = wfs.kd.find_k_plus_q(q_c, [kpt1.K])[0]
            with self.timer('get k2'):
                kpt2 = self.get_k_point(kpt1.s, K2, m1, m2, block=True)
            with self.timer('fft-indices'):
                Q_G = self.get_fft_indices(kpt1.K, kpt2.K, q_c, pd,
                                           kpt1.shift_c - kpt2.shift_c)

            for n in range(kpt1.n2 - kpt1.n1):
                eps1 = kpt1.eps_n[n]
                
                # Only update if there exists deps <= omegamax
                if self.omegamax is not None:
                    m = [m for m, d in enumerate(eps1 - kpt2.eps_n)
                         if abs(d) <= self.omegamax]
                else:
                    m = range(0, kpt2.n2 - kpt2.n1)
                
                if not len(m):
                    continue

                deps_m = (eps1 - kpt2.eps_n)[m]
                f1 = kpt1.f_n[n]
                with self.timer('conj'):
                    ut1cc_R = kpt1.ut_nR[n].conj()
                with self.timer('paw'):
                    C1_aGi = [np.dot(Q_Gii, P1_ni[n].conj())
                              for Q_Gii, P1_ni in zip(Q_aGii, kpt1.P_ani)]
                n_mG = self.calculate_pair_densities(ut1cc_R, C1_aGi, kpt2,
                                                     pd, Q_G)[m]
                df_m = (f1 - kpt2.f_n)[m]

                # This is not quite right for degenerate partially occupied
                # bands, but good enough for now:
                df_m[df_m <= 1e-20] = 0.0

                if optical_limit:
                    self.update_optical_limit(
                        n, m, kpt1, kpt2, deps_m, df_m, n_mG,
                        chi0_wxvG, chi0_wvv)

                update(n_mG, deps_m, df_m, chi0_wGG)

            if optical_limit and self.intraband:
                # Avoid that more ranks are summing up
                # the intraband contributions
                if kpt1.n1 == 0 and self.blockcomm.rank == 0:
                    assert self.nocc2 <= kpt2.nb, \
                        print('Error: Too few unoccupied bands')
                    self.update_intraband(kpt2)

        self.timer.stop('Loop')

        pb.finish()

        with self.timer('Sum CHI_0'):
            for chi0_GG in chi0_wGG:
                self.kncomm.sum(chi0_GG)

            if optical_limit:
                self.kncomm.sum(chi0_wxvG)
                self.kncomm.sum(chi0_wvv)
                if self.intraband:
                    self.kncomm.sum(self.chi0_vv)

        print('Memory used: {0:.3f} MB / CPU'.format(maxrss() / 1024**2),
              file=self.fd)

        if (self.eta == 0.0 or self.hilbert) and self.blockcomm.size == 1:
            # Fill in upper/lower triangle also:
            nG = pd.ngmax
            il = np.tril_indices(nG, -1)
            iu = il[::-1]
            if self.hilbert:
                for chi0_GG in chi0_wGG:
                    chi0_GG[il] = chi0_GG[iu].conj()
            else:
                for chi0_GG in chi0_wGG:
                    chi0_GG[iu] = chi0_GG[il].conj()

        if self.hilbert:
            with self.timer('Hilbert transform'):
                ht = HilbertTransform(self.omega_w, self.eta,
                                      self.timeordered)
                ht(chi0_wGG)
                if optical_limit:
                    ht(chi0_wvv)
                    ht(chi0_wxvG)
            print('Hilbert transform done', file=self.fd)

        if optical_limit and self.intraband:  # Add intraband contribution
            omega_w = self.omega_w.copy()
            if omega_w[0] == 0.0:
                omega_w[0] = 1e-14

            chi0_vv = self.chi0_vv
            self.world.broadcast(chi0_vv, 0)

            chi0_wvv += (chi0_vv[np.newaxis] /
                         (omega_w[:, np.newaxis, np.newaxis] *
                          (omega_w[:, np.newaxis, np.newaxis] +
                           1j * self.eta)))

        return pd, chi0_wGG, chi0_wxvG, chi0_wvv