예제 #1
0
    def __init__(self, calc, xc, ibzq_qc, fd, unit_cells, density_cut, ecut,
                 tag, timer):

        self.calc = calc
        self.gd = calc.density.gd
        self.xc = xc
        self.ibzq_qc = ibzq_qc
        self.fd = fd
        self.unit_cells = unit_cells
        self.density_cut = density_cut
        self.ecut = ecut
        self.tag = tag
        self.timer = timer

        self.A_x = -(3 / 4.) * (3 / np.pi)**(1 / 3.)

        self.n_g = calc.get_all_electron_density(gridrefinement=1)
        self.n_g *= Bohr**3

        if xc[-3:] == 'PBE':
            nf_g = calc.get_all_electron_density(gridrefinement=2)
            nf_g *= Bohr**3
            gdf = self.gd.refine()
            grad_v = [Gradient(gdf, v, n=1).apply for v in range(3)]
            gradnf_vg = gdf.empty(3)
            for v in range(3):
                grad_v[v](nf_g, gradnf_vg[v])
            self.gradn_vg = gradnf_vg[:, ::2, ::2, ::2]

        qd = KPointDescriptor(self.ibzq_qc)
        self.pd = PWDescriptor(ecut / Hartree, self.gd, complex, qd)
예제 #2
0
    def create_kpoint_descriptor(self, nspins):
        par = self.parameters

        bzkpts_kc = kpts2ndarray(par.kpts, self.atoms)
        kpt_refine = par.experimental.get('kpt_refine')
        if kpt_refine is None:
            kd = KPointDescriptor(bzkpts_kc, nspins)

            self.timer.start('Set symmetry')
            kd.set_symmetry(self.atoms, self.symmetry, comm=self.world)
            self.timer.stop('Set symmetry')

        else:
            self.timer.start('Set k-point refinement')
            kd = create_kpoint_descriptor_with_refinement(kpt_refine,
                                                          bzkpts_kc,
                                                          nspins,
                                                          self.atoms,
                                                          self.symmetry,
                                                          comm=self.world,
                                                          timer=self.timer)
            self.timer.stop('Set k-point refinement')
            # Update quantities which might have changed, if symmetry
            # was changed
            self.symmetry = kd.symmetry
            self.setups.set_symmetry(kd.symmetry)

        self.log(kd)

        return kd
예제 #3
0
    def get_ibz_q_points(self, bz_k_points):

        # Get all q-points
        all_qs = []
        for k1 in bz_k_points:
            for k2 in bz_k_points:
                all_qs.append(k1 - k2)
        all_qs = np.array(all_qs)

        # Fold q-points into Brillouin zone
        all_qs[np.where(all_qs > 0.501)] -= 1.
        all_qs[np.where(all_qs < -0.499)] += 1.

        # Make list of non-identical q-points in full BZ
        bz_qs = [all_qs[0]]
        for q_a in all_qs:
            q_in_list = False
            for q_b in bz_qs:
                if (abs(q_a[0] - q_b[0]) < 0.01 and abs(q_a[1] - q_b[1]) < 0.01
                        and abs(q_a[2] - q_b[2]) < 0.01):
                    q_in_list = True
                    break
            if q_in_list == False:
                bz_qs.append(q_a)
        self.bz_q_points = bz_qs

        # Obtain q-points and weights in the irreducible part of the BZ
        kpt_descriptor = KPointDescriptor(bz_qs, self.nspins)
        kpt_descriptor.set_symmetry(self.atoms, self.setups, usesymm=True)
        ibz_q_points = kpt_descriptor.ibzk_kc
        q_weights = kpt_descriptor.weight_k
        return ibz_q_points, q_weights
예제 #4
0
 def get_PWDescriptor(self, q_c, gammacentered=False):
     """Get the planewave descriptor of q_c."""
     qd = KPointDescriptor([q_c])
     pd = PWDescriptor(self.ecut,
                       self.calc.wfs.gd,
                       complex,
                       qd,
                       gammacentered=gammacentered)
     return pd
예제 #5
0
def get_pw_descriptor(q_c, calc, ecut, gammacentered=False):
    """Get the planewave descriptor of q_c."""
    qd = KPointDescriptor([q_c])
    pd = PWDescriptor(ecut,
                      calc.wfs.gd,
                      complex,
                      qd,
                      gammacentered=gammacentered)
    return pd
예제 #6
0
    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))
예제 #7
0
    def setUp(self):
        for virtvar in ['boundaries']:
            assert getattr(self,
                           virtvar) is not None, 'Virtual "%s"!' % virtvar

        # Basic unit cell information:
        res, N_c = shapeopt(100, self.G**3, 3, 0.2)
        #N_c = 4*np.round(np.array(N_c)/4) # makes domain decomposition easier
        cell_cv = self.h * np.diag(N_c)
        pbc_c = {'zero'    : (False,False,False), \
                 'periodic': (True,True,True), \
                 'mixed'   : (True, False, True)}[self.boundaries]

        # Create randomized gas-like atomic configuration on interim grid
        tmpgd = GridDescriptor(N_c, cell_cv, pbc_c)
        self.atoms = create_random_atoms(tmpgd)

        # Create setups
        Z_a = self.atoms.get_atomic_numbers()
        assert 1 == self.nspins
        self.setups = Setups(Z_a, p.setups, p.basis, p.lmax, xc)
        self.natoms = len(self.setups)

        # Decide how many kpoints to sample from the 1st Brillouin Zone
        kpts_c = np.ceil(
            (10 / Bohr) / np.sum(cell_cv**2, axis=1)**0.5).astype(int)
        kpts_c = tuple(kpts_c * pbc_c + 1 - pbc_c)
        self.bzk_kc = kpts2ndarray(kpts_c)

        # Set up k-point descriptor
        self.kd = KPointDescriptor(self.bzk_kc, self.nspins)
        self.kd.set_symmetry(self.atoms, self.setups, p.usesymm)

        # Set the dtype
        if self.kd.gamma:
            self.dtype = float
        else:
            self.dtype = complex

        # Create communicators
        parsize, parsize_bands = self.get_parsizes()
        assert self.nbands % np.prod(parsize_bands) == 0
        domain_comm, kpt_comm, band_comm = distribute_cpus(
            parsize, parsize_bands, self.nspins, self.kd.nibzkpts)

        self.kd.set_communicator(kpt_comm)

        # Set up band descriptor:
        self.bd = BandDescriptor(self.nbands, band_comm)

        # Set up grid descriptor:
        self.gd = GridDescriptor(N_c, cell_cv, pbc_c, domain_comm, parsize)

        # Set up kpoint/spin descriptor (to be removed):
        self.kd_old = KPointDescriptorOld(self.nspins, self.kd.nibzkpts,
                                          kpt_comm, self.kd.gamma, self.dtype)
예제 #8
0
    def ibz2bz(self, atoms):
        """Transform wave functions in IBZ to the full BZ."""

        assert self.kd.comm.size == 1

        # New k-point descriptor for full BZ:
        kd = KPointDescriptor(self.kd.bzk_kc, nspins=self.nspins)
        #kd.set_symmetry(atoms, self.setups, enabled=False)
        kd.set_communicator(serial_comm)

        self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups],
                      kd,
                      dtype=self.dtype)
        self.pt.set_positions(atoms.get_scaled_positions())

        self.initialize_wave_functions_from_restart_file()

        weight = 2.0 / kd.nspins / kd.nbzkpts

        # Build new list of k-points:
        kpt_u = []
        for s in range(self.nspins):
            for k in range(kd.nbzkpts):
                # Index of symmetry related point in the IBZ
                ik = self.kd.bz2ibz_k[k]
                r, u = self.kd.get_rank_and_index(s, ik)
                assert r == 0
                kpt = self.kpt_u[u]

                phase_cd = np.exp(2j * np.pi * self.gd.sdisp_cd *
                                  kd.bzk_kc[k, :, np.newaxis])

                # New k-point:
                kpt2 = KPoint(weight, s, k, k, phase_cd)
                kpt2.f_n = kpt.f_n / kpt.weight / kd.nbzkpts * 2 / self.nspins
                kpt2.eps_n = kpt.eps_n.copy()

                # Transform wave functions using symmetry operation:
                Psit_nG = self.gd.collect(kpt.psit_nG)
                if Psit_nG is not None:
                    Psit_nG = Psit_nG.copy()
                    for Psit_G in Psit_nG:
                        Psit_G[:] = self.kd.transform_wave_function(Psit_G, k)
                kpt2.psit_nG = self.gd.empty(self.bd.nbands, dtype=self.dtype)
                self.gd.distribute(Psit_nG, kpt2.psit_nG)

                # Calculate PAW projections:
                kpt2.P_ani = self.pt.dict(len(kpt.psit_nG))
                self.pt.integrate(kpt2.psit_nG, kpt2.P_ani, k)

                kpt_u.append(kpt2)

        self.kd = kd
        self.kpt_u = kpt_u
예제 #9
0
    def calculate(self, q_c, spin='all', A_x=None):
        wfs = self.calc.wfs

        if spin == 'all':
            spins = range(wfs.nspins)
        else:
            assert spin in range(wfs.nspins)
            spins = [spin]

        q_c = np.asarray(q_c, dtype=float)
        qd = KPointDescriptor([q_c])
        pd = PWDescriptor(self.ecut, wfs.gd, complex, qd)

        self.print_chi(pd)

        if extra_parameters.get('df_dry_run'):
            print('    Dry run exit', file=self.fd)
            raise SystemExit

        nG = pd.ngmax
        nw = len(self.omega_w)
        mynG = (nG + self.blockcomm.size - 1) // self.blockcomm.size
        self.Ga = self.blockcomm.rank * mynG
        self.Gb = min(self.Ga + mynG, nG)
        assert mynG * (self.blockcomm.size - 1) < nG

        if A_x is not None:
            nx = nw * (self.Gb - self.Ga) * nG
            chi0_wGG = A_x[:nx].reshape((nw, self.Gb - self.Ga, nG))
            chi0_wGG[:] = 0.0
        else:
            chi0_wGG = np.zeros((nw, self.Gb - self.Ga, nG), complex)

        if np.allclose(q_c, 0.0):
            chi0_wxvG = np.zeros((len(self.omega_w), 2, 3, nG), complex)
            chi0_wvv = np.zeros((len(self.omega_w), 3, 3), complex)
            self.chi0_vv = np.zeros((3, 3), complex)
        else:
            chi0_wxvG = None
            chi0_wvv = None

        print('Initializing PAW Corrections', file=self.fd)
        self.Q_aGii = self.initialize_paw_corrections(pd)

        # Do all empty bands:
        m1 = self.nocc1
        m2 = self.nbands

        self._calculate(pd, chi0_wGG, chi0_wxvG, chi0_wvv, self.Q_aGii, m1, m2,
                        spins)

        return pd, chi0_wGG, chi0_wxvG, chi0_wvv
예제 #10
0
파일: ut_invops.py 프로젝트: thonmaker/gpaw
    def setUp(self):
        UTDomainParallelSetup.setUp(self)

        for virtvar in ['dtype']:
            assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar

        # Create randomized atoms
        self.atoms = create_random_atoms(self.gd, 5) # also tested: 10xNH3/BDA

        # XXX DEBUG START
        if False:
            from ase import view
            view(self.atoms*(1+2*self.gd.pbc_c))
        # XXX DEBUG END

        # Do we agree on the atomic positions?
        pos_ac = self.atoms.get_positions()
        pos_rac = np.empty((world.size,)+pos_ac.shape, pos_ac.dtype)
        world.all_gather(pos_ac, pos_rac)
        if (pos_rac-pos_rac[world.rank,...][np.newaxis,...]).any():
            raise RuntimeError('Discrepancy in atomic positions detected.')

        # Create setups for atoms
        self.Z_a = self.atoms.get_atomic_numbers()
        self.setups = Setups(self.Z_a, p.setups, p.basis,
                             p.lmax, xc)

        # K-point descriptor
        bzk_kc = np.array([[0, 0, 0]], dtype=float)
        self.kd = KPointDescriptor(bzk_kc, 1)
        self.kd.set_symmetry(self.atoms, self.setups)
        self.kd.set_communicator(self.kpt_comm)

        # Create gamma-point dummy wavefunctions
        self.wfs = FDWFS(self.gd, self.bd, self.kd, self.setups,
                         self.block_comm, self.dtype)

        spos_ac = self.atoms.get_scaled_positions() % 1.0
        self.wfs.set_positions(spos_ac)
        self.pt = self.wfs.pt # XXX shortcut

        ## Also create pseudo partial waveves
        #from gpaw.lfc import LFC
        #self.phit = LFC(self.gd, [setup.phit_j for setup in self.setups], \
        #                self.kpt_comm, dtype=self.dtype)
        #self.phit.set_positions(spos_ac)

        self.r_cG = None
        self.buf_G = None
        self.psit_nG = None

        self.allocate()
예제 #11
0
def dscf_load_band(filename, paw, molecule=None):
    """Load and distribute all information for a band from a tar file."""
    if not paw.wfs:
        paw.initialize()
    world, bd, gd, kd = paw.wfs.world, paw.wfs.bd, paw.wfs.gd, \
        KPointDescriptor(paw.wfs.nspins, paw.wfs.nibzkpts, paw.wfs.kpt_comm, \
                         paw.wfs.gamma, paw.wfs.dtype)
    if bd.comm.size != 1:
        raise NotImplementedError('Undefined action for band parallelization.')

    r = Reader(filename)
    assert (r.dimension('nspins') == kd.nspins and \
        r.dimension('nibzkpts') == kd.nibzkpts), 'Incompatible spin/kpoints.'

    # Read wave function for every spin/kpoint owned by this rank
    psit_uG = gd.empty(kd.mynks, kd.dtype)
    for myu, psit_G in enumerate(psit_uG):
        u = kd.global_index(myu)
        s, k = kd.what_is(u)
        if gd.comm.rank == 0:
            big_psit_G = np.array(r.get('PseudoWaveFunction', s, k), kd.dtype)
        else:
            big_psit_G = None
        gd.distribute(big_psit_G, psit_G)

    # Find domain ranks for each atom
    atoms = paw.get_atoms()
    spos_ac = atoms.get_scaled_positions() % 1.0
    rank_a = gd.get_ranks_from_positions(spos_ac)
    #my_atom_indices = np.argwhere(rank_a == gd.comm.rank).ravel()
    #assert np.all(my_atom_indices == paw.wfs.pt.my_atom_indices)
    assert r.dimension('nproj') == sum([setup.ni for setup in paw.wfs.setups])

    if molecule is None:
        molecule = range(len(atoms))

    # Read projections for every spin/kpoint and atom owned by this rank
    P_uai = [{}] * kd.mynks  #[paw.wfs.pt.dict() for myu in range(kd.mynks)]
    for myu, P_ai in enumerate(P_uai):
        u = kd.global_index(myu)
        s, k = kd.what_is(u)
        P_i = r.get('Projection', s, k)
        i1 = 0
        for a in molecule:
            setup = paw.wfs.setups[a]
            i2 = i1 + setup.ni
            if gd.comm.rank == rank_a[a]:
                P_ai[a] = np.array(P_i[i1:i2], kd.dtype)
            i1 = i2

    return psit_uG, P_uai
예제 #12
0
    def check(self, i_cG, shift0_c, N_c, q_c, Q_aGii):
        I0_G = np.ravel_multi_index(i_cG - shift0_c[:, None], N_c, 'wrap')
        qd1 = KPointDescriptor([q_c])
        pd1 = PWDescriptor(self.ecut, self.calc.wfs.gd, complex, qd1)
        G_I = np.empty(N_c.prod(), int)
        G_I[:] = -1
        I1_G = pd1.Q_qG[0]
        G_I[I1_G] = np.arange(len(I0_G))
        G_G = G_I[I0_G]
        assert len(I0_G) == len(I1_G)
        assert (G_G >= 0).all()

        for a, Q_Gii in enumerate(self.initialize_paw_corrections(pd1)):
            e = abs(Q_aGii[a] - Q_Gii[G_G]).max()
            assert e < 1e-12
예제 #13
0
    def calculate_gamma(self, vol, alpha):
        if self.molecule:
            return 0.0

        N_c = self.kd.N_c
        offset_c = (N_c + 1) % 2 * 0.5 / N_c
        bzq_qc = monkhorst_pack(N_c) + offset_c
        qd = KPointDescriptor(bzq_qc)
        pd = PWDescriptor(self.wfs.pd.ecut, self.wfs.gd, kd=qd)
        gamma = (vol / (2 * pi)**2 * sqrt(pi / alpha) * self.kd.nbzkpts)
        for G2_G in pd.G2_qG:
            if G2_G[0] < 1e-7:
                G2_G = G2_G[1:]
            gamma -= np.dot(np.exp(-alpha * G2_G), G2_G**-1)
        return gamma / self.qstride_c.prod()
예제 #14
0
def dscf_save_band(filename, paw, n):
    """Extract and save all information for band `n` to a tar file."""
    world, bd, gd, kd = paw.wfs.world, paw.wfs.bd, paw.wfs.gd, \
        KPointDescriptor(paw.wfs.nspins, paw.wfs.nibzkpts, paw.wfs.kpt_comm, \
                         paw.wfs.gamma, paw.wfs.dtype)
    if world.rank == 0:
        # Minimal amount of information needed:
        w = Writer(filename)
        w.dimension('nspins', kd.nspins)
        w.dimension('nibzkpts', kd.nibzkpts)
        w.dimension('nproj', sum([setup.ni for setup in paw.wfs.setups]))
        ng = gd.get_size_of_global_array()
        w.dimension('ngptsx', ng[0])
        w.dimension('ngptsy', ng[1])
        w.dimension('ngptsz', ng[2])

    # Write projections:
    if world.rank == 0:
        w.add('Projection', ('nspins', 'nibzkpts', 'nproj'), dtype=kd.dtype)
    for s in range(kd.nspins):
        for k in range(kd.nibzkpts):
            all_P_ni = paw.wfs.collect_projections(k, s)  # gets all bands
            if world.rank == 0:
                w.fill(all_P_ni[n])

    # Write wave functions:
    if world.rank == 0:
        w.add('PseudoWaveFunction',
              ('nspins', 'nibzkpts', 'ngptsx', 'ngptsy', 'ngptsz'),
              dtype=kd.dtype)
    for s in range(kd.nspins):
        for k in range(kd.nibzkpts):
            psit_G = paw.wfs.get_wave_function_array(n, k, s)
            if world.rank == 0:
                w.fill(psit_G)

    if world.rank == 0:
        # Close the file here to ensure that the last wave function is
        # written to disk:
        w.close()

    # We don't want the slaves to start reading before the master has
    # finished writing:
    world.barrier()
예제 #15
0
    def setUp(self):
        UTDomainParallelSetup.setUp(self)

        for virtvar in ['dtype']:
            assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar

        # Set up kpoint descriptor:
        self.kd = KPointDescriptor(self.nspins, self.nibzkpts, self.kpt_comm, \
            self.gamma, self.dtype)

        # Choose a sufficiently small width of gaussian test functions
        cell_c = np.sum(self.gd.cell_cv**2, axis=1)**0.5
        self.sigma = np.min((0.1+0.4*self.gd.pbc_c)*cell_c)

        if debug and world.rank == 0:
            print 'sigma=%8.5f Ang' % (self.sigma*Bohr), 'cell_c:', cell_c*Bohr, 'Ang', 'N_c:', self.gd.N_c
        self.atoms = create_random_atoms(self.gd, 4, 'H', 4*self.sigma)
        self.r_vG = None
        self.wf_uG = None
        self.laplace0_uG = None

        self.allocate()
예제 #16
0
    def setUp(self):
        for virtvar in ['equipartition']:
            assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar

        kpts = {'even' : (12,1,2), \
                'prime': (23,1,1)}[self.equipartition]

        #primes = [i for i in xrange(50,1,-1) if ~np.any(i%np.arange(2,i)==0)]
        bzk_kc = kpts2ndarray(kpts)
        assert p.usesymm == None
        self.nibzkpts = len(bzk_kc)

        #parsize, parsize_bands = create_parsize_minbands(self.nbands, world.size)
        parsize, parsize_bands = 1, 1 #XXX
        assert self.nbands % np.prod(parsize_bands) == 0
        domain_comm, kpt_comm, band_comm = distribute_cpus(parsize,
            parsize_bands, self.nspins, self.nibzkpts)

        # Set up band descriptor:
        self.bd = BandDescriptor(self.nbands, band_comm, p.parallel['stridebands'])

        # Set up grid descriptor:
        res, ngpts = shapeopt(300, self.G**3, 3, 0.2)
        cell_c = self.h * np.array(ngpts)
        pbc_c = (True, False, True)
        self.gd = GridDescriptor(ngpts, cell_c, pbc_c, domain_comm, parsize)

        # Create randomized gas-like atomic configuration
        self.atoms = create_random_atoms(self.gd)

        # Create setups
        Z_a = self.atoms.get_atomic_numbers()
        self.setups = Setups(Z_a, p.setups, p.basis, p.lmax, xc)
        self.natoms = len(self.setups)

        # Set up kpoint descriptor:
        self.kd = KPointDescriptor(bzk_kc, self.nspins)
        self.kd.set_symmetry(self.atoms, self.setups, p.usesymm)
        self.kd.set_communicator(kpt_comm)
예제 #17
0
    def calculate_q(self, i, kpt1, kpt2):
        wfs = self.calc.wfs

        q_c = wfs.kd.bzk_kc[kpt2.K] - wfs.kd.bzk_kc[kpt1.K]
        qd = KPointDescriptor([q_c])
        pd = PWDescriptor(self.ecut, wfs.gd, wfs.dtype, kd=qd)

        Q_G = self.get_fft_indices(kpt1.K, kpt2.K, q_c, pd,
                                   kpt1.shift_c - kpt2.shift_c)

        Q_aGii = self.initialize_paw_corrections(pd, soft=True)

        for n in range(kpt1.n2 - kpt1.n1):
            ut1cc_R = kpt1.ut_nR[n].conj()
            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)
            e = self.calculate_n(pd, n, n_mG, kpt2)
            self.exxvv_sin[kpt1.s, i, n] += e
예제 #18
0
파일: phonons.py 프로젝트: yihsuanliu/gpaw
    def __init__(self, atoms, kpts, symmetry):
        """Initialize base class and attributes.

        Parameters
        ----------
        atoms: Atoms
            ASE atoms.
        kpts: tuple or list of tuples
            Shape of Monkhorst-Pack grid or list of k-points used in the dfpt
            calculation.
        symmetry: bool or None
            Symmetry parameter used in dfpt calculation.
            
        """

        # Init base class with ``Atoms`` object
        phonons.Phonons.__init__(atoms)

        # Create k-point descriptor
        self.kd = KPointDescriptor(kpts, 1)
        self.kd.set_symmetry(self.atoms, self.calc.wfs.setups, symmetry)

        # Overwrite ``N_c`` attribute
        self.N_c = tuple(self.kd.N_c)

        # Index of the gamma point -- for the acoustic sum-rule
        self.gamma_index = None
        if self.kd.gamma:
            self.gamma_index = 0
            self.dtype == float
        else:
            self.dtype == comples
            for k, k_c in enumerate(self.kd.ibzk_kc):
                if np.all(k_c == 0.):
                    self.gamma_index = k

        assert self.gamma_index is not None
예제 #19
0
    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)
예제 #20
0
파일: bse.py 프로젝트: thonmaker/gpaw
    def calculate_screened_potential(self, ac):
        """Calculate W_GG(q)"""

        chi0 = Chi0(self.calc,
                    frequencies=[0.0],
                    eta=0.001,
                    ecut=self.ecut,
                    intraband=False,
                    hilbert=False,
                    nbands=self.nbands,
                    txt='chi0.txt',
                    world=world,
                    )

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

        self.Q_qaGii = []
        self.W_qGG = []
        self.pd_q = []

        t0 = time()
        print('Calculating screened potential', file=self.fd)
        for iq, q_c in enumerate(self.qd.ibzk_kc):
            thisqd = KPointDescriptor([q_c])
            pd = PWDescriptor(self.ecut, wfs.gd, complex, thisqd)
            nG = pd.ngmax

            chi0.Ga = self.blockcomm.rank * nG
            chi0.Gb = min(chi0.Ga + nG, nG)
            chi0_wGG = np.zeros((1, nG, nG), complex)
            if np.allclose(q_c, 0.0):
                chi0_wxvG = np.zeros((1, 2, 3, nG), complex)
                chi0_wvv = np.zeros((1, 3, 3), complex)
            else:
                chi0_wxvG = None
                chi0_wvv = None

            chi0._calculate(pd, chi0_wGG, chi0_wxvG, chi0_wvv,
                            0, self.nbands, spins='all', extend_head=False)
            chi0_GG = chi0_wGG[0]

            # Calculate eps^{-1}_GG
            if pd.kd.gamma:
                # Generate fine grid in vicinity of gamma
                kd = self.calc.wfs.kd
                N = 4
                N_c = np.array([N, N, N])
                if self.truncation is not None:
                    # Only average periodic directions if trunction is used
                    N_c[kd.N_c == 1] = 1
                qf_qc = monkhorst_pack(N_c) / kd.N_c
                qf_qc *= 1.0e-6
                U_scc = kd.symmetry.op_scc
                qf_qc = kd.get_ibz_q_points(qf_qc, U_scc)[0]
                weight_q = kd.q_weights
                qf_qv = 2 * np.pi * np.dot(qf_qc, pd.gd.icell_cv)
                a_q = np.sum(np.dot(chi0_wvv[0], qf_qv.T) * qf_qv.T, axis=0)
                a0_qG = np.dot(qf_qv, chi0_wxvG[0, 0])
                a1_qG = np.dot(qf_qv, chi0_wxvG[0, 1])
                einv_GG = np.zeros((nG, nG), complex)
                # W_GG = np.zeros((nG, nG), complex)
                for iqf in range(len(qf_qv)):
                    chi0_GG[0] = a0_qG[iqf]
                    chi0_GG[:, 0] = a1_qG[iqf]
                    chi0_GG[0, 0] = a_q[iqf]
                    sqrV_G = get_coulomb_kernel(pd,
                                                kd.N_c,
                                                truncation=self.truncation,
                                                wstc=self.wstc,
                                                q_v=qf_qv[iqf])**0.5
                    sqrV_G *= ac**0.5  # Multiply by adiabatic coupling
                    e_GG = np.eye(nG) - chi0_GG * sqrV_G * sqrV_G[:,
                                                                  np.newaxis]
                    einv_GG += np.linalg.inv(e_GG) * weight_q[iqf]
                    # einv_GG = np.linalg.inv(e_GG) * weight_q[iqf]
                    # W_GG += (einv_GG * sqrV_G * sqrV_G[:, np.newaxis]
                    #          * weight_q[iqf])
            else:
                sqrV_G = get_coulomb_kernel(pd,
                                            self.kd.N_c,
                                            truncation=self.truncation,
                                            wstc=self.wstc)**0.5
                sqrV_G *= ac**0.5  # Multiply by adiabatic coupling
                e_GG = np.eye(nG) - chi0_GG * sqrV_G * sqrV_G[:, np.newaxis]
                einv_GG = np.linalg.inv(e_GG)
                # W_GG = einv_GG * sqrV_G * sqrV_G[:, np.newaxis]

            # Now calculate W_GG
            if pd.kd.gamma:
                # Reset bare Coulomb interaction
                sqrV_G = get_coulomb_kernel(pd,
                                            self.kd.N_c,
                                            truncation=self.truncation,
                                            wstc=self.wstc)**0.5
            W_GG = einv_GG * sqrV_G * sqrV_G[:, np.newaxis]
            if self.integrate_gamma != 0:
                # Numerical integration of Coulomb interaction at all q-points
                if self.integrate_gamma == 2:
                    reduced = True
                else:
                    reduced = False
                V0, sqrV0 = get_integrated_kernel(pd,
                                                  self.kd.N_c,
                                                  truncation=self.truncation,
                                                  reduced=reduced,
                                                  N=100)
                W_GG[0, 0] = einv_GG[0, 0] * V0
                W_GG[0, 1:] = einv_GG[0, 1:] * sqrV0 * sqrV_G[1:]
                W_GG[1:, 0] = einv_GG[1:, 0] * sqrV_G[1:] * sqrV0
            elif self.integrate_gamma == 0 and pd.kd.gamma:
                # Analytical integration at gamma
                bzvol = (2 * np.pi)**3 / self.vol / self.qd.nbzkpts
                Rq0 = (3 * bzvol / (4 * np.pi))**(1. / 3.)
                V0 = 16 * np.pi**2 * Rq0 / bzvol
                sqrV0 = (4 * np.pi)**(1.5) * Rq0**2 / bzvol / 2
                W_GG[0, 0] = einv_GG[0, 0] * V0
                W_GG[0, 1:] = einv_GG[0, 1:] * sqrV0 * sqrV_G[1:]
                W_GG[1:, 0] = einv_GG[1:, 0] * sqrV_G[1:] * sqrV0
            else:
                pass

            if pd.kd.gamma:
                e = 1 / einv_GG[0, 0].real
                print('    RPA dielectric constant is: %3.3f' % e,
                      file=self.fd)
            self.Q_qaGii.append(chi0.Q_aGii)
            self.pd_q.append(pd)
            self.W_qGG.append(W_GG)

            if iq % (self.qd.nibzkpts // 5 + 1) == 2:
                dt = time() - t0
                tleft = dt * self.qd.nibzkpts / (iq + 1) - dt
                print('  Finished %s q-points in %s - Estimated %s left' %
                      (iq + 1, timedelta(seconds=round(dt)),
                       timedelta(seconds=round(tleft))), file=self.fd)
예제 #21
0
    def bloch_matrix(self,
                     kpts,
                     qpts,
                     c_kn,
                     u_ql,
                     omega_ql=None,
                     kpts_from=None):
        """Calculate el-ph coupling in the Bloch basis for the electrons.

        This function calculates the electron-phonon coupling between the
        specified Bloch states, i.e.::

                      ______ 
            mnl      / hbar               ^
           g    =   /-------  < m k + q | e  . grad V  | n k >
            kq    \/ 2 M w                 ql        q
                          ql

        In case the ``omega_ql`` keyword argument is not given, the bare matrix
        element (in units of eV / Ang) without the sqrt prefactor is returned. 
        
        Parameters
        ----------
        kpts: ndarray or tuple.
            k-vectors of the Bloch states. When a tuple of integers is given, a
            Monkhorst-Pack grid with the specified number of k-points along the
            directions of the reciprocal lattice vectors is generated.
        qpts: ndarray or tuple.
            q-vectors of the phonons.
        c_kn: ndarray
            Expansion coefficients for the Bloch states. The ordering must be
            the same as in the ``kpts`` argument.
        u_ql: ndarray
            Mass-scaled polarization vectors (in units of 1 / sqrt(amu)) of the
            phonons. Again, the ordering must be the same as in the
            corresponding ``qpts`` argument.
        omega_ql: ndarray
            Vibrational frequencies in eV. 
        kpts_from: list of ints or int
            Calculate only the matrix element for the k-vectors specified by
            their index in the ``kpts`` argument (default: all).

        In short, phonon frequencies and mode vectors must be given in ase units.

        """

        assert self.g_xNNMM is not None, "Load supercell matrix."
        assert len(c_kn.shape) == 3
        assert len(u_ql.shape) == 4
        if omega_ql is not None:
            assert np.all(u_ql.shape[:2] == omega_ql.shape[:2])

        # Translate k-points into 1. BZ (required by ``find_k_plus_q``` member
        # function of the ```KPointDescriptor``).
        if isinstance(kpts, np.ndarray):
            assert kpts.shape[1] == 3, "kpts_kc array must be given"
            # XXX This does not seem to cause problems!
            kpts -= kpts.round()

        # Use the KPointDescriptor to keep track of the k and q-vectors
        kd_kpts = KPointDescriptor(kpts)
        kd_qpts = KPointDescriptor(qpts)
        # Check that number of k- and q-points agree with the number of Bloch
        # functions and polarization vectors
        assert kd_kpts.nbzkpts == len(c_kn)
        assert kd_qpts.nbzkpts == len(u_ql)

        # Include all k-point per default
        if kpts_from is None:
            kpts_kc = kd_kpts.bzk_kc
            kpts_k = range(kd_kpts.nbzkpts)
        else:
            kpts_kc = kd_kpts.bzk_kc[kpts_from]
            if isinstance(kpts_from, int):
                kpts_k = list([kpts_from])
            else:
                kpts_k = list(kpts_from)

        # Supercell matrix (real matrix in Hartree / Bohr)
        g_xNNMM = self.g_xNNMM

        # Number of phonon modes and electronic bands
        nmodes = u_ql.shape[1]
        nbands = c_kn.shape[1]
        # Number of atoms displacements and basis functions
        ndisp = np.prod(u_ql.shape[2:])
        assert ndisp == (3 * len(self.indices))
        nao = c_kn.shape[2]
        assert ndisp == g_xNNMM.shape[0]
        assert nao == g_xNNMM.shape[-1]

        # Lattice vectors
        R_cN = self.lattice_vectors()
        # Number of unit cell in supercell
        N = np.prod(self.N_c)

        # Allocate array for couplings
        g_qklnn = np.zeros(
            (kd_qpts.nbzkpts, len(kpts_kc), nmodes, nbands, nbands),
            dtype=complex)

        self.timer.write_now("Calculating coupling matrix elements")
        for q, q_c in enumerate(kd_qpts.bzk_kc):

            # Find indices of k+q for the k-points
            kplusq_k = kd_kpts.find_k_plus_q(q_c, kpts_k=kpts_k)

            # Here, ``i`` is counting from 0 and ``k`` is the global index of
            # the k-point
            for i, (k, k_c) in enumerate(zip(kpts_k, kpts_kc)):

                # Check the wave vectors (adapted to the ``KPointDescriptor`` class)
                kplusq_c = k_c + q_c
                kplusq_c -= kplusq_c.round()
                assert np.allclose(kplusq_c, kd_kpts.bzk_kc[kplusq_k[i]] ), \
                       (i, k, k_c, q_c, kd_kpts.bzk_kc[kplusq_k[i]])

                # Allocate array
                g_xMM = np.zeros((ndisp, nao, nao), dtype=complex)

                # Multiply phase factors
                for m in range(N):
                    for n in range(N):
                        Rm_c = R_cN[:, m]
                        Rn_c = R_cN[:, n]
                        phase = np.exp(
                            2.j * pi *
                            (np.dot(k_c, Rm_c - Rn_c) + np.dot(q_c, Rm_c)))
                        # Sum contributions from different cells
                        g_xMM += g_xNNMM[:, m, n, :, :] * phase

                # LCAO coefficient for Bloch states
                ck_nM = c_kn[k]
                ckplusq_nM = c_kn[kplusq_k[i]]
                # Mass scaled polarization vectors
                u_lx = u_ql[q].reshape(nmodes, 3 * len(self.atoms))

                g_nxn = np.dot(ckplusq_nM.conj(), np.dot(g_xMM, ck_nM.T))
                g_lnn = np.dot(u_lx, g_nxn)

                # Insert value
                g_qklnn[q, i] = g_lnn

                # XXX Temp
                if np.all(q_c == 0.0):
                    # These should be real
                    print(g_qklnn[q].imag.min(), g_qklnn[q].imag.max())

        self.timer.write_now(
            "Finished calculation of coupling matrix elements")

        # Return the bare matrix element if frequencies are not given
        if omega_ql is None:
            # Convert to eV / Ang
            g_qklnn *= units.Hartree / units.Bohr
        else:
            # Multiply prefactor sqrt(hbar / 2 * M * omega) in units of Bohr
            amu = units._amu  # atomic mass unit
            me = units._me  # electron mass
            g_qklnn /= np.sqrt(2 * amu / me / units.Hartree * \
                               omega_ql[:, np.newaxis, :, np.newaxis, np.newaxis])
            # Convert to eV
            g_qklnn *= units.Hartree

        # Return couplings in eV (or eV / Ang)
        return g_qklnn
예제 #22
0
    def calculate_exx(self):
        """Non-selfconsistent calculation."""

        self.timer.start('EXX')
        self.timer.start('Initialization')

        kd = self.kd
        wfs = self.wfs

        if fftw.FFTPlan is fftw.NumpyFFTPlan:
            self.log('NOT USING FFTW !!')

        self.log('Spins:', self.wfs.nspins)

        W = max(1, self.wfs.kd.comm.size // self.wfs.nspins)
        # Are the k-points distributed?
        kparallel = (W > 1)

        # Find number of occupied bands:
        self.nocc_sk = np.zeros((self.wfs.nspins, kd.nibzkpts), int)
        for kpt in self.wfs.kpt_u:
            for n, f in enumerate(kpt.f_n):
                if abs(f) < self.fcut:
                    self.nocc_sk[kpt.s, kpt.k] = n
                    break
            else:
                self.nocc_sk[kpt.s, kpt.k] = self.wfs.bd.nbands
        self.wfs.kd.comm.sum(self.nocc_sk)

        noccmin = self.nocc_sk.min()
        noccmax = self.nocc_sk.max()
        self.log('Number of occupied bands (min, max): %d, %d' %
                 (noccmin, noccmax))

        self.log('Number of valence electrons:', self.wfs.setups.nvalence)

        if self.bandstructure:
            self.log('Calculating eigenvalue shifts.')

            # allocate array for eigenvalue shifts:
            self.exx_skn = np.zeros(
                (self.wfs.nspins, kd.nibzkpts, self.wfs.bd.nbands))

            if self.bands is None:
                noccmax = self.wfs.bd.nbands
            else:
                noccmax = max(max(self.bands) + 1, noccmax)

        N_c = self.kd.N_c

        vol = wfs.gd.dv * wfs.gd.N_c.prod()
        if self.alpha is None:
            alpha = 6 * vol**(2 / 3.0) / pi**2
        else:
            alpha = self.alpha
        if self.gamma_point == 1:
            if alpha == 0.0:
                qvol = (2 * np.pi)**3 / vol / N_c.prod()
                self.gamma = 4 * np.pi * (3 * qvol /
                                          (4 * np.pi))**(1 / 3.) / qvol
            else:
                self.gamma = self.calculate_gamma(vol, alpha)
        else:
            kcell_cv = wfs.gd.cell_cv.copy()
            kcell_cv[0] *= N_c[0]
            kcell_cv[1] *= N_c[1]
            kcell_cv[2] *= N_c[2]
            self.gamma = madelung(kcell_cv) * vol * N_c.prod() / (4 * np.pi)

        self.log('Value of alpha parameter: %.3f Bohr^2' % alpha)
        self.log('Value of gamma parameter: %.3f Bohr^2' % self.gamma)

        # Construct all possible q=k2-k1 vectors:
        Nq_c = (N_c - 1) // self.qstride_c
        i_qc = np.indices(Nq_c * 2 + 1, float).transpose((1, 2, 3, 0)).reshape(
            (-1, 3))
        self.bzq_qc = (i_qc - Nq_c) / N_c * self.qstride_c
        self.q0 = ((Nq_c * 2 + 1).prod() - 1) // 2  # index of q=(0,0,0)
        assert not self.bzq_qc[self.q0].any()

        # Count number of pairs for each q-vector:
        self.npairs_q = np.zeros(len(self.bzq_qc), int)
        for s in range(kd.nspins):
            for k1 in range(kd.nibzkpts):
                for k2 in range(kd.nibzkpts):
                    for K2, q, n1_n, n2 in self.indices(s, k1, k2):
                        self.npairs_q[q] += len(n1_n)

        self.npairs0 = self.npairs_q.sum()  # total number of pairs

        self.log('Number of pairs:', self.npairs0)

        # Distribute q-vectors to Q processors:
        Q = self.world.size // self.wfs.kd.comm.size
        myrank = self.world.rank // self.wfs.kd.comm.size
        rank = 0
        N = 0
        myq = []
        nq = 0
        for q, n in enumerate(self.npairs_q):
            if n > 0:
                nq += 1
                if rank == myrank:
                    myq.append(q)
            N += n
            if N >= (rank + 1.0) * self.npairs0 / Q:
                rank += 1

        assert len(myq) > 0, 'Too few q-vectors for too many processes!'
        self.bzq_qc = self.bzq_qc[myq]
        try:
            self.q0 = myq.index(self.q0)
        except ValueError:
            self.q0 = None

        self.log('%d x %d x %d k-points' % tuple(self.kd.N_c))
        self.log('Distributing %d IBZ k-points over %d process(es).' %
                 (kd.nibzkpts, self.wfs.kd.comm.size))
        self.log('Distributing %d q-vectors over %d process(es).' % (nq, Q))

        # q-point descriptor for my q-vectors:
        qd = KPointDescriptor(self.bzq_qc)

        # Plane-wave descriptor for all wave-functions:
        self.pd = PWDescriptor(wfs.pd.ecut, wfs.gd, dtype=wfs.pd.dtype, kd=kd)

        # Plane-wave descriptor pair-densities:
        self.pd2 = PWDescriptor(self.dens.pd2.ecut,
                                self.dens.gd,
                                dtype=wfs.dtype,
                                kd=qd)

        self.log('Cutoff energies:')
        self.log('    Wave functions:       %10.3f eV' %
                 (self.pd.ecut * Hartree))
        self.log('    Density:              %10.3f eV' %
                 (self.pd2.ecut * Hartree))

        # Calculate 1/|G+q|^2 with special treatment of |G+q|=0:
        G2_qG = self.pd2.G2_qG
        if self.q0 is None:
            if self.omega is None:
                self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG]
            else:
                self.iG2_qG = [
                    (1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2))))
                    for G2_G in G2_qG
                ]
        else:
            G2_qG[self.q0][0] = 117.0  # avoid division by zero
            if self.omega is None:
                self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG]
                self.iG2_qG[self.q0][0] = self.gamma
            else:
                self.iG2_qG = [
                    (1.0 / G2_G * (1 - np.exp(-G2_G / (4 * self.omega**2))))
                    for G2_G in G2_qG
                ]
                self.iG2_qG[self.q0][0] = 1 / (4 * self.omega**2)
            G2_qG[self.q0][0] = 0.0  # restore correct value

        # Compensation charges:
        self.ghat = PWLFC([setup.ghat_l for setup in wfs.setups], self.pd2)
        self.ghat.set_positions(self.spos_ac)

        if self.molecule:
            self.initialize_gaussian()
            self.log('Value of beta parameter: %.3f 1/Bohr^2' % self.beta)

        self.timer.stop('Initialization')

        # Ready ... set ... go:
        self.t0 = time()
        self.npairs = 0
        self.evv = 0.0
        self.evvacdf = 0.0
        for s in range(self.wfs.nspins):
            kpt1_q = [
                KPoint(self.wfs, noccmax).initialize(kpt)
                for kpt in self.wfs.kpt_u if kpt.s == s
            ]
            kpt2_q = kpt1_q[:]

            if len(kpt1_q) == 0:
                # No s-spins on this CPU:
                continue

            # Send and receive ranks:
            srank = self.wfs.kd.get_rank_and_index(s, (kpt1_q[0].k - 1) %
                                                   kd.nibzkpts)[0]
            rrank = self.wfs.kd.get_rank_and_index(s, (kpt1_q[-1].k + 1) %
                                                   kd.nibzkpts)[0]

            # Shift k-points kd.nibzkpts - 1 times:
            for i in range(kd.nibzkpts):
                if i < kd.nibzkpts - 1:
                    if kparallel:
                        kpt = kpt2_q[-1].next(self.wfs)
                        kpt.start_receiving(rrank)
                        kpt2_q[0].start_sending(srank)
                    else:
                        kpt = kpt2_q[0]

                self.timer.start('Calculate')
                for kpt1, kpt2 in zip(kpt1_q, kpt2_q):
                    # Loop over all k-points that k2 can be mapped to:
                    for K2, q, n1_n, n2 in self.indices(s, kpt1.k, kpt2.k):
                        self.apply(K2, q, kpt1, kpt2, n1_n, n2)
                self.timer.stop('Calculate')

                if i < kd.nibzkpts - 1:
                    self.timer.start('Wait')
                    if kparallel:
                        kpt.wait()
                        kpt2_q[0].wait()
                    self.timer.stop('Wait')
                    kpt2_q.pop(0)
                    kpt2_q.append(kpt)

        self.evv = self.world.sum(self.evv)
        self.evvacdf = self.world.sum(self.evvacdf)
        self.calculate_exx_paw_correction()

        if self.method == 'standard':
            self.exx = self.evv + self.devv + self.evc + self.ecc
        elif self.method == 'acdf':
            self.exx = self.evvacdf + self.devv + self.evc + self.ecc
        else:
            1 / 0

        self.log('Exact exchange energy:')
        for txt, e in [('core-core', self.ecc), ('valence-core', self.evc),
                       ('valence-valence (pseudo, acdf)', self.evvacdf),
                       ('valence-valence (pseudo, standard)', self.evv),
                       ('valence-valence (correction)', self.devv),
                       ('total (%s)' % self.method, self.exx)]:
            self.log('    %-36s %14.6f eV' % (txt + ':', e * Hartree))

        self.log('Total time: %10.3f seconds' % (time() - self.t0))

        self.npairs = self.world.sum(self.npairs)
        assert self.npairs == self.npairs0

        self.timer.stop('EXX')
        self.timer.write(self.fd)
예제 #23
0
    def initialize(self, atoms=None):
        """Inexpensive initialization."""

        if atoms is None:
            atoms = self.atoms
        else:
            # Save the state of the atoms:
            self.atoms = atoms.copy()

        par = self.input_parameters

        world = par.communicator
        if world is None:
            world = mpi.world
        elif hasattr(world, 'new_communicator'):
            # Check for whether object has correct type already
            #
            # Using isinstance() is complicated because of all the
            # combinations, serial/parallel/debug...
            pass
        else:
            # world should be a list of ranks:
            world = mpi.world.new_communicator(np.asarray(world))
        self.wfs.world = world

        if 'txt' in self._changed_keywords:
            self.set_txt(par.txt)
        self.verbose = par.verbose

        natoms = len(atoms)

        cell_cv = atoms.get_cell() / Bohr
        pbc_c = atoms.get_pbc()
        Z_a = atoms.get_atomic_numbers()
        magmom_av = atoms.get_initial_magnetic_moments()

        self.check_atoms()

        # Generate new xc functional only when it is reset by set
        # XXX sounds like this should use the _changed_keywords dictionary.
        if self.hamiltonian is None or self.hamiltonian.xc is None:
            if isinstance(par.xc, str):
                xc = XC(par.xc)
            else:
                xc = par.xc
        else:
            xc = self.hamiltonian.xc

        mode = par.mode

        if mode == 'fd':
            mode = FD()
        elif mode == 'pw':
            mode = pw.PW()
        elif mode == 'lcao':
            mode = LCAO()
        else:
            assert hasattr(mode, 'name'), str(mode)

        if xc.orbital_dependent and mode.name == 'lcao':
            raise NotImplementedError('LCAO mode does not support '
                                      'orbital-dependent XC functionals.')

        if par.realspace is None:
            realspace = (mode.name != 'pw')
        else:
            realspace = par.realspace
            if mode.name == 'pw':
                assert not realspace

        if par.filter is None and mode.name != 'pw':
            gamma = 1.6
            if par.gpts is not None:
                h = ((np.linalg.inv(cell_cv)**2).sum(0)**-0.5 / par.gpts).max()
            else:
                h = (par.h or 0.2) / Bohr

            def filter(rgd, rcut, f_r, l=0):
                gcut = np.pi / h - 2 / rcut / gamma
                f_r[:] = rgd.filter(f_r, rcut * gamma, gcut, l)
        else:
            filter = par.filter

        setups = Setups(Z_a, par.setups, par.basis, par.lmax, xc, filter,
                        world)

        if magmom_av.ndim == 1:
            collinear = True
            magmom_av, magmom_a = np.zeros((natoms, 3)), magmom_av
            magmom_av[:, 2] = magmom_a
        else:
            collinear = False

        magnetic = magmom_av.any()

        spinpol = par.spinpol
        if par.hund:
            if natoms != 1:
                raise ValueError('hund=True arg only valid for single atoms!')
            spinpol = True
            magmom_av[0] = (0, 0, setups[0].get_hunds_rule_moment(par.charge))

        if spinpol is None:
            spinpol = magnetic
        elif magnetic and not spinpol:
            raise ValueError('Non-zero initial magnetic moment for a ' +
                             'spin-paired calculation!')

        if collinear:
            nspins = 1 + int(spinpol)
            ncomp = 1
        else:
            nspins = 1
            ncomp = 2

        if par.usesymm != 'default':
            warnings.warn('Use "symmetry" keyword instead of ' +
                          '"usesymm" keyword')
            par.symmetry = usesymm2symmetry(par.usesymm)

        symm = par.symmetry
        if symm == 'off':
            symm = {'point_group': False, 'time_reversal': False}

        bzkpts_kc = kpts2ndarray(par.kpts, self.atoms)
        kd = KPointDescriptor(bzkpts_kc, nspins, collinear)
        m_av = magmom_av.round(decimals=3)  # round off
        id_a = zip(setups.id_a, *m_av.T)
        symmetry = Symmetry(id_a, cell_cv, atoms.pbc, **symm)
        kd.set_symmetry(atoms, symmetry, comm=world)
        setups.set_symmetry(symmetry)

        if par.gpts is not None:
            N_c = np.array(par.gpts)
        else:
            h = par.h
            if h is not None:
                h /= Bohr
            N_c = get_number_of_grid_points(cell_cv, h, mode, realspace,
                                            kd.symmetry)

        symmetry.check_grid(N_c)

        width = par.width
        if width is None:
            if pbc_c.any():
                width = 0.1  # eV
            else:
                width = 0.0
        else:
            assert par.occupations is None

        if hasattr(self, 'time') or par.dtype == complex:
            dtype = complex
        else:
            if kd.gamma:
                dtype = float
            else:
                dtype = complex

        nao = setups.nao
        nvalence = setups.nvalence - par.charge
        M_v = magmom_av.sum(0)
        M = np.dot(M_v, M_v)**0.5

        nbands = par.nbands

        orbital_free = any(setup.orbital_free for setup in setups)
        if orbital_free:
            nbands = 1

        if isinstance(nbands, basestring):
            if nbands[-1] == '%':
                basebands = int(nvalence + M + 0.5) // 2
                nbands = int((float(nbands[:-1]) / 100) * basebands)
            else:
                raise ValueError('Integer Expected: Only use a string '
                                 'if giving a percentage of occupied bands')

        if nbands is None:
            nbands = 0
            for setup in setups:
                nbands_from_atom = setup.get_default_nbands()

                # Any obscure setup errors?
                if nbands_from_atom < -(-setup.Nv // 2):
                    raise ValueError('Bad setup: This setup requests %d'
                                     ' bands but has %d electrons.' %
                                     (nbands_from_atom, setup.Nv))
                nbands += nbands_from_atom
            nbands = min(nao, nbands)
        elif nbands > nao and mode.name == 'lcao':
            raise ValueError('Too many bands for LCAO calculation: '
                             '%d bands and only %d atomic orbitals!' %
                             (nbands, nao))

        if nvalence < 0:
            raise ValueError(
                'Charge %f is not possible - not enough valence electrons' %
                par.charge)

        if nbands <= 0:
            nbands = int(nvalence + M + 0.5) // 2 + (-nbands)

        if nvalence > 2 * nbands and not orbital_free:
            raise ValueError('Too few bands!  Electrons: %f, bands: %d' %
                             (nvalence, nbands))

        nbands *= ncomp

        if par.width is not None:
            self.text('**NOTE**: please start using '
                      'occupations=FermiDirac(width).')
        if par.fixmom:
            self.text('**NOTE**: please start using '
                      'occupations=FermiDirac(width, fixmagmom=True).')

        if self.occupations is None:
            if par.occupations is None:
                # Create object for occupation numbers:
                if orbital_free:
                    width = 0.0  # even for PBC
                    self.occupations = occupations.TFOccupations(
                        width, par.fixmom)
                else:
                    self.occupations = occupations.FermiDirac(
                        width, par.fixmom)
            else:
                self.occupations = par.occupations

            # If occupation numbers are changed, and we have wave functions,
            # recalculate the occupation numbers
            if self.wfs is not None and not isinstance(self.wfs,
                                                       EmptyWaveFunctions):
                self.occupations.calculate(self.wfs)

        self.occupations.magmom = M_v[2]

        cc = par.convergence

        if mode.name == 'lcao':
            niter_fixdensity = 0
        else:
            niter_fixdensity = None

        if self.scf is None:
            force_crit = cc['forces']
            if force_crit is not None:
                force_crit /= Hartree / Bohr
            self.scf = SCFLoop(cc['eigenstates'] / Hartree**2 * nvalence,
                               cc['energy'] / Hartree * max(nvalence, 1),
                               cc['density'] * nvalence, par.maxiter,
                               par.fixdensity, niter_fixdensity, force_crit)

        parsize_kpt = par.parallel['kpt']
        parsize_domain = par.parallel['domain']
        parsize_bands = par.parallel['band']

        if not realspace:
            pbc_c = np.ones(3, bool)

        if not self.wfs:
            if parsize_domain == 'domain only':  # XXX this was silly!
                parsize_domain = world.size

            parallelization = mpi.Parallelization(world, nspins * kd.nibzkpts)
            ndomains = None
            if parsize_domain is not None:
                ndomains = np.prod(parsize_domain)
            if mode.name == 'pw':
                if ndomains > 1:
                    raise ValueError('Planewave mode does not support '
                                     'domain decomposition.')
                ndomains = 1
            parallelization.set(kpt=parsize_kpt,
                                domain=ndomains,
                                band=parsize_bands)
            comms = parallelization.build_communicators()
            domain_comm = comms['d']
            kpt_comm = comms['k']
            band_comm = comms['b']
            kptband_comm = comms['D']
            domainband_comm = comms['K']

            self.comms = comms
            kd.set_communicator(kpt_comm)

            parstride_bands = par.parallel['stridebands']

            # Unfortunately we need to remember that we adjusted the
            # number of bands so we can print a warning if it differs
            # from the number specified by the user.  (The number can
            # be inferred from the input parameters, but it's tricky
            # because we allow negative numbers)
            self.nbands_parallelization_adjustment = -nbands % band_comm.size
            nbands += self.nbands_parallelization_adjustment

            # I would like to give the following error message, but apparently
            # there are cases, e.g. gpaw/test/gw_ppa.py, which involve
            # nbands > nao and are supposed to work that way.
            #if nbands > nao:
            #    raise ValueError('Number of bands %d adjusted for band '
            #                     'parallelization %d exceeds number of atomic '
            #                     'orbitals %d.  This problem can be fixed '
            #                     'by reducing the number of bands a bit.'
            #                     % (nbands, band_comm.size, nao))
            bd = BandDescriptor(nbands, band_comm, parstride_bands)

            if (self.density is not None
                    and self.density.gd.comm.size != domain_comm.size):
                # Domain decomposition has changed, so we need to
                # reinitialize density and hamiltonian:
                if par.fixdensity:
                    raise RuntimeError(
                        'Density reinitialization conflict ' +
                        'with "fixdensity" - specify domain decomposition.')
                self.density = None
                self.hamiltonian = None

            # Construct grid descriptor for coarse grids for wave functions:
            gd = self.grid_descriptor_class(N_c, cell_cv, pbc_c, domain_comm,
                                            parsize_domain)

            # do k-point analysis here? XXX
            args = (gd, nvalence, setups, bd, dtype, world, kd, kptband_comm,
                    self.timer)

            if par.parallel['sl_auto']:
                # Choose scalapack parallelization automatically

                for key, val in par.parallel.items():
                    if (key.startswith('sl_') and key != 'sl_auto'
                            and val is not None):
                        raise ValueError("Cannot use 'sl_auto' together "
                                         "with '%s'" % key)
                max_scalapack_cpus = bd.comm.size * gd.comm.size
                nprow = max_scalapack_cpus
                npcol = 1

                # Get a sort of reasonable number of columns/rows
                while npcol < nprow and nprow % 2 == 0:
                    npcol *= 2
                    nprow //= 2
                assert npcol * nprow == max_scalapack_cpus

                # ScaLAPACK creates trouble if there aren't at least a few
                # whole blocks; choose block size so there will always be
                # several blocks.  This will crash for small test systems,
                # but so will ScaLAPACK in any case
                blocksize = min(-(-nbands // 4), 64)
                sl_default = (nprow, npcol, blocksize)
            else:
                sl_default = par.parallel['sl_default']

            if mode.name == 'lcao':
                # Layouts used for general diagonalizer
                sl_lcao = par.parallel['sl_lcao']
                if sl_lcao is None:
                    sl_lcao = sl_default
                lcaoksl = get_KohnSham_layouts(sl_lcao,
                                               'lcao',
                                               gd,
                                               bd,
                                               domainband_comm,
                                               dtype,
                                               nao=nao,
                                               timer=self.timer)

                self.wfs = mode(collinear, lcaoksl, *args)

            elif mode.name == 'fd' or mode.name == 'pw':
                # buffer_size keyword only relevant for fdpw
                buffer_size = par.parallel['buffer_size']
                # Layouts used for diagonalizer
                sl_diagonalize = par.parallel['sl_diagonalize']
                if sl_diagonalize is None:
                    sl_diagonalize = sl_default
                diagksl = get_KohnSham_layouts(
                    sl_diagonalize,
                    'fd',  # XXX
                    # choice of key 'fd' not so nice
                    gd,
                    bd,
                    domainband_comm,
                    dtype,
                    buffer_size=buffer_size,
                    timer=self.timer)

                # Layouts used for orthonormalizer
                sl_inverse_cholesky = par.parallel['sl_inverse_cholesky']
                if sl_inverse_cholesky is None:
                    sl_inverse_cholesky = sl_default
                if sl_inverse_cholesky != sl_diagonalize:
                    message = 'sl_inverse_cholesky != sl_diagonalize ' \
                        'is not implemented.'
                    raise NotImplementedError(message)
                orthoksl = get_KohnSham_layouts(sl_inverse_cholesky,
                                                'fd',
                                                gd,
                                                bd,
                                                domainband_comm,
                                                dtype,
                                                buffer_size=buffer_size,
                                                timer=self.timer)

                # Use (at most) all available LCAO for initialization
                lcaonbands = min(nbands, nao)

                try:
                    lcaobd = BandDescriptor(lcaonbands, band_comm,
                                            parstride_bands)
                except RuntimeError:
                    initksl = None
                else:
                    # Layouts used for general diagonalizer
                    # (LCAO initialization)
                    sl_lcao = par.parallel['sl_lcao']
                    if sl_lcao is None:
                        sl_lcao = sl_default
                    initksl = get_KohnSham_layouts(sl_lcao,
                                                   'lcao',
                                                   gd,
                                                   lcaobd,
                                                   domainband_comm,
                                                   dtype,
                                                   nao=nao,
                                                   timer=self.timer)

                if hasattr(self, 'time'):
                    assert mode.name == 'fd'
                    from gpaw.tddft import TimeDependentWaveFunctions
                    self.wfs = TimeDependentWaveFunctions(
                        par.stencils[0], diagksl, orthoksl, initksl, gd,
                        nvalence, setups, bd, world, kd, kptband_comm,
                        self.timer)
                elif mode.name == 'fd':
                    self.wfs = mode(par.stencils[0], diagksl, orthoksl,
                                    initksl, *args)
                else:
                    assert mode.name == 'pw'
                    self.wfs = mode(diagksl, orthoksl, initksl, *args)
            else:
                self.wfs = mode(self, *args)
        else:
            self.wfs.set_setups(setups)

        if not self.wfs.eigensolver:
            # Number of bands to converge:
            nbands_converge = cc['bands']
            if nbands_converge == 'all':
                nbands_converge = nbands
            elif nbands_converge != 'occupied':
                assert isinstance(nbands_converge, int)
                if nbands_converge < 0:
                    nbands_converge += nbands
            eigensolver = get_eigensolver(par.eigensolver, mode,
                                          par.convergence)
            eigensolver.nbands_converge = nbands_converge
            # XXX Eigensolver class doesn't define an nbands_converge property

            if isinstance(xc, SIC):
                eigensolver.blocksize = 1
            self.wfs.set_eigensolver(eigensolver)

        if self.density is None:
            gd = self.wfs.gd
            if par.stencils[1] != 9:
                # Construct grid descriptor for fine grids for densities
                # and potentials:
                finegd = gd.refine()
            else:
                # Special case (use only coarse grid):
                finegd = gd

            if realspace:
                self.density = RealSpaceDensity(
                    gd, finegd, nspins, par.charge + setups.core_charge,
                    collinear, par.stencils[1])
            else:
                self.density = pw.ReciprocalSpaceDensity(
                    gd, finegd, nspins, par.charge + setups.core_charge,
                    collinear)

        self.density.initialize(setups, self.timer, magmom_av, par.hund)
        self.density.set_mixer(par.mixer)

        if self.hamiltonian is None:
            gd, finegd = self.density.gd, self.density.finegd
            if realspace:
                self.hamiltonian = RealSpaceHamiltonian(
                    gd, finegd, nspins, setups, self.timer, xc, world,
                    self.wfs.kptband_comm, par.external, collinear,
                    par.poissonsolver, par.stencils[1])
            else:
                self.hamiltonian = pw.ReciprocalSpaceHamiltonian(
                    gd, finegd, self.density.pd2, self.density.pd3, nspins,
                    setups, self.timer, xc, world, self.wfs.kptband_comm,
                    par.external, collinear)

        xc.initialize(self.density, self.hamiltonian, self.wfs,
                      self.occupations)

        self.text()
        self.print_memory_estimate(self.txt, maxdepth=memory_estimate_depth)
        self.txt.flush()

        self.timer.print_info(self)

        if dry_run:
            self.dry_run()

        if realspace and \
                self.hamiltonian.poisson.get_description() == 'FDTD+TDDFT':
            self.hamiltonian.poisson.set_density(self.density)
            self.hamiltonian.poisson.print_messages(self.text)
            self.txt.flush()

        self.initialized = True
        self._changed_keywords.clear()
예제 #24
0
파일: TDDFT.py 프로젝트: drgulevich/TDDFT
    def __init__(self, calc, nbands=None, Fock=False):
        self.calc = calc
        self.Fock = Fock
        self.K = calc.get_ibz_k_points()  # reduced Brillioun zone
        self.NK = self.K.shape[0]

        self.wk = calc.get_k_point_weights(
        )  # weight of reduced Brillioun zone
        if nbands is None:
            self.nbands = calc.get_number_of_bands()
        else:
            self.nbands = nbands
        self.nvalence = int(calc.get_number_of_electrons() / 2)

        self.EK = [
            calc.get_eigenvalues(k)[:self.nbands] for k in range(self.NK)
        ]  # bands energy
        self.EK = np.array(self.EK) / Hartree
        self.shape = tuple(
            calc.get_number_of_grid_points())  # shape of real space grid
        self.density = calc.get_pseudo_density(
        ) * Bohr**3  # density at zero time

        # array of u_nk (periodic part of Kohn-Sham orbitals,only reduced Brillion zone)
        self.ukn = np.zeros((
            self.NK,
            self.nbands,
        ) + self.shape,
                            dtype=np.complex)
        for k in range(self.NK):
            kpt = calc.wfs.kpt_u[k]
            for n in range(self.nbands):
                psit_G = kpt.psit_nG[n]
                psit_R = calc.wfs.pd.ifft(psit_G, kpt.q)
                self.ukn[k, n] = psit_R

        self.icell = 2.0 * np.pi * calc.wfs.gd.icell_cv  # inverse cell
        self.cell = calc.wfs.gd.cell_cv  # cell
        self.r = calc.wfs.gd.get_grid_point_coordinates()
        for i in range(3):
            self.r[i] -= self.cell[i, i] / 2.
        self.volume = np.abs(np.linalg.det(
            calc.wfs.gd.cell_cv))  # volume of cell
        self.norm = calc.wfs.gd.dv  #
        self.Fermi = calc.get_fermi_level() / Hartree  #Fermi level

        #desriptors at q=gamma for Hartree
        self.kdH = KPointDescriptor([[0, 0, 0]])
        self.pdH = PWDescriptor(ecut=calc.wfs.pd.ecut,
                                gd=calc.wfs.gd,
                                kd=self.kdH,
                                dtype=complex)

        #desriptors at q=gamma for Fock
        self.kdF = KPointDescriptor([[0, 0, 0]])
        self.pdF = PWDescriptor(ecut=calc.wfs.pd.ecut / 4.,
                                gd=calc.wfs.gd,
                                kd=self.kdF,
                                dtype=complex)

        #Fermi-Dirac temperature
        self.temperature = calc.occupations.width

        #calculate pair-density matrices
        if Fock:
            self.M = np.zeros((self.nbands, self.nbands, self.NK, self.NK,
                               self.pdF.get_reciprocal_vectors().shape[0]),
                              dtype=np.complex)
            indexes = [(n, k)
                       for n, k in product(range(self.nbands), range(self.NK))]
            for i1 in range(len(indexes)):
                n1, k1 = indexes[i1]
                for i2 in range(i1, len(indexes)):
                    n2, k2 = indexes[i1]
                    self.M[n1, n2, k1, k2] = self.pdF.fft(
                        self.ukn[k1, n1].conj() * self.ukn[k2, n2])
                    self.M[n2, n1, k2, k1] = self.M[n1, n2, k1, k2].conj()
            self.M *= calc.wfs.gd.dv

        #Fermi-Dirac distribution
        self.f = 1 / (1 + np.exp((self.EK - self.Fermi) / self.temperature))

        self.Hartree_elements = np.zeros(
            (self.NK, self.nbands, self.NK, self.nbands, self.nbands),
            dtype=np.complex)
        self.LDAx_elements = np.zeros(
            (self.NK, self.nbands, self.NK, self.nbands, self.nbands),
            dtype=np.complex)
        self.LDAc_elements = np.zeros(
            (self.NK, self.nbands, self.NK, self.nbands, self.nbands),
            dtype=np.complex)
        G = self.pdH.get_reciprocal_vectors()
        G2 = np.linalg.norm(G, axis=1)**2
        G2[G2 == 0] = np.inf
        matrix = np.zeros((self.NK, self.nbands, self.nbands),
                          dtype=np.complex)
        for k in tqdm(range(self.NK)):
            for n in range(self.nbands):
                density = 2 * np.abs(self.ukn[k, n])**2
                operator = xc.VLDAx(density)
                self.LDAx_elements[k, n] = operator_matrix_periodic(
                    matrix, operator, self.ukn.conj(), self.ukn) * self.norm
                operator = xc.VLDAc(density)
                self.LDAc_elements[k, n] = operator_matrix_periodic(
                    matrix, operator, self.ukn.conj(), self.ukn) * self.norm

                density = self.pdH.fft(density)
                operator = 4 * np.pi * self.pdH.ifft(density / G2)
                self.Hartree_elements[k, n] = operator_matrix_periodic(
                    matrix, operator, self.ukn.conj(), self.ukn) * self.norm

        self.wavefunction = np.zeros((self.NK, self.nbands, self.nbands),
                                     dtype=np.complex)
        self.Kinetic = np.zeros((self.NK, self.nbands, self.nbands),
                                dtype=np.complex)
        self.dipole = self.get_dipole_matrix()
        for k in range(self.NK):
            self.wavefunction[k] = np.eye(self.nbands)
            self.Kinetic[k] = np.diag(self.EK[k])
        self.VH0 = self.get_Hartree_matrix(self.wavefunction)
        self.VLDAc0 = self.get_LDA_correlation_matrix(self.wavefunction)
        self.VLDAx0 = self.get_LDA_exchange_matrix(self.wavefunction)

        self.Full_BZ = calc.get_bz_k_points()
        self.IBZ_map = calc.get_bz_to_ibz_map()
예제 #25
0
    def __init__(self,
                 calc,
                 gamma=True,
                 symmetry=False,
                 e_ph=False,
                 communicator=serial_comm):
        """Inititialize class with a list of atoms.

        The atoms object must contain a converged ground-state calculation.

        The set of q-vectors in which the dynamical matrix will be calculated
        is determined from the ``symmetry`` kwarg. For now, only time-reversal
        symmetry is used to generate the irrecducible BZ.

        Add a little note on parallelization strategy here.

        Parameters
        ----------
        calc: str or Calculator
            Calculator containing a ground-state calculation.
        gamma: bool
            Gamma-point calculation with respect to the q-vector of the
            dynamical matrix. When ``False``, the Monkhorst-Pack grid from the
            ground-state calculation is used.
        symmetry: bool
            Use symmetries to reduce the q-vectors of the dynamcial matrix
            (None, False or True). The different options are equivalent to the
            old style options in a ground-state calculation (see usesymm).
        e_ph: bool
            Save the derivative of the effective potential.
        communicator: Communicator
            Communicator for parallelization over k-points and real-space
            domain.
        """

        # XXX
        assert symmetry in [None, False], "Spatial symmetries not allowed yet"

        if isinstance(calc, str):
            self.calc = GPAW(calc, communicator=serial_comm, txt=None)
        else:
            self.calc = calc

        cell_cv = self.calc.atoms.get_cell()
        setups = self.calc.wfs.setups
        # XXX - no clue how to get magmom - ignore it for the moment
        # m_av = magmom_av.round(decimals=3)  # round off
        # id_a = zip(setups.id_a, *m_av.T)
        id_a = setups.id_a

        if symmetry is None:
            self.symmetry = Symmetry(id_a,
                                     cell_cv,
                                     point_group=False,
                                     time_reversal=False)
        else:
            self.symmetry = Symmetry(id_a,
                                     cell_cv,
                                     point_group=False,
                                     time_reversal=True)

        # Make sure localized functions are initialized
        self.calc.set_positions()
        # Note that this under some circumstances (e.g. when called twice)
        # allocates a new array for the P_ani coefficients !!

        # Store useful objects
        self.atoms = self.calc.get_atoms()
        # Get rid of ``calc`` attribute
        self.atoms.calc = None

        # Boundary conditions
        pbc_c = self.calc.atoms.get_pbc()

        if np.all(pbc_c == False):
            self.gamma = True
            self.dtype = float
            kpts = None
            # Multigrid Poisson solver
            poisson_solver = PoissonSolver()
        else:
            if gamma:
                self.gamma = True
                self.dtype = float
                kpts = None
            else:
                self.gamma = False
                self.dtype = complex
                # Get k-points from ground-state calculation
                kpts = self.calc.input_parameters.kpts

            # FFT Poisson solver
            poisson_solver = FFTPoissonSolver(dtype=self.dtype)

        # K-point descriptor for the q-vectors of the dynamical matrix
        # Note, no explicit parallelization here.
        self.kd = KPointDescriptor(kpts, 1)
        self.kd.set_symmetry(self.atoms, self.symmetry)
        self.kd.set_communicator(serial_comm)

        # Number of occupied bands
        nvalence = self.calc.wfs.nvalence
        nbands = nvalence // 2 + nvalence % 2
        assert nbands <= self.calc.wfs.bd.nbands

        # Extract other useful objects
        # Ground-state k-point descriptor - used for the k-points in the
        # ResponseCalculator
        # XXX replace communicators when ready to parallelize
        kd_gs = self.calc.wfs.kd
        gd = self.calc.density.gd
        kpt_u = self.calc.wfs.kpt_u
        setups = self.calc.wfs.setups
        dtype_gs = self.calc.wfs.dtype

        # WaveFunctions
        wfs = WaveFunctions(nbands, kpt_u, setups, kd_gs, gd, dtype=dtype_gs)

        # Linear response calculator
        self.response_calc = ResponseCalculator(self.calc,
                                                wfs,
                                                dtype=self.dtype)

        # Phonon perturbation
        self.perturbation = PhononPerturbation(self.calc,
                                               self.kd,
                                               poisson_solver,
                                               dtype=self.dtype)

        # Dynamical matrix
        self.dyn = DynamicalMatrix(self.atoms, self.kd, dtype=self.dtype)

        # Electron-phonon couplings
        if e_ph:
            self.e_ph = ElectronPhononCoupling(self.atoms,
                                               gd,
                                               self.kd,
                                               dtype=self.dtype)
        else:
            self.e_ph = None

        # Initialization flag
        self.initialized = False

        # Parallel communicator for parallelization over kpts and domain
        self.comm = communicator
예제 #26
0
    def create_wave_functions(self, mode, realspace, nspins, nbands, nao,
                              nvalence, setups, magmom_a, cell_cv, pbc_c):
        par = self.parameters

        bzkpts_kc = kpts2ndarray(par.kpts, self.atoms)
        kd = KPointDescriptor(bzkpts_kc, nspins)

        self.timer.start('Set symmetry')
        kd.set_symmetry(self.atoms, self.symmetry, comm=self.world)
        self.timer.stop('Set symmetry')

        self.log(kd)

        parallelization = mpi.Parallelization(self.world, nspins * kd.nibzkpts)

        parsize_kpt = self.parallel['kpt']
        parsize_domain = self.parallel['domain']
        parsize_bands = self.parallel['band']

        ndomains = None
        if parsize_domain is not None:
            ndomains = np.prod(parsize_domain)
        if mode.name == 'pw':
            if ndomains is not None and ndomains > 1:
                raise ValueError('Planewave mode does not support '
                                 'domain decomposition.')
            ndomains = 1
        parallelization.set(kpt=parsize_kpt,
                            domain=ndomains,
                            band=parsize_bands)
        comms = parallelization.build_communicators()
        domain_comm = comms['d']
        kpt_comm = comms['k']
        band_comm = comms['b']
        kptband_comm = comms['D']
        domainband_comm = comms['K']

        self.comms = comms

        if par.gpts is not None:
            if par.h is not None:
                raise ValueError("""You can't use both "gpts" and "h"!""")
            N_c = np.array(par.gpts)
        else:
            h = par.h
            if h is not None:
                h /= Bohr
            N_c = get_number_of_grid_points(cell_cv, h, mode, realspace,
                                            kd.symmetry)

        self.symmetry.check_grid(N_c)

        kd.set_communicator(kpt_comm)

        parstride_bands = self.parallel['stridebands']

        # Unfortunately we need to remember that we adjusted the
        # number of bands so we can print a warning if it differs
        # from the number specified by the user.  (The number can
        # be inferred from the input parameters, but it's tricky
        # because we allow negative numbers)
        self.nbands_parallelization_adjustment = -nbands % band_comm.size
        nbands += self.nbands_parallelization_adjustment

        bd = BandDescriptor(nbands, band_comm, parstride_bands)

        # Construct grid descriptor for coarse grids for wave functions:
        gd = self.create_grid_descriptor(N_c, cell_cv, pbc_c, domain_comm,
                                         parsize_domain)

        if hasattr(self, 'time') or mode.force_complex_dtype:
            dtype = complex
        else:
            if kd.gamma:
                dtype = float
            else:
                dtype = complex

        wfs_kwargs = dict(gd=gd,
                          nvalence=nvalence,
                          setups=setups,
                          bd=bd,
                          dtype=dtype,
                          world=self.world,
                          kd=kd,
                          kptband_comm=kptband_comm,
                          timer=self.timer)

        if self.parallel['sl_auto']:
            # Choose scalapack parallelization automatically

            for key, val in self.parallel.items():
                if (key.startswith('sl_') and key != 'sl_auto'
                        and val is not None):
                    raise ValueError("Cannot use 'sl_auto' together "
                                     "with '%s'" % key)
            max_scalapack_cpus = bd.comm.size * gd.comm.size
            nprow = max_scalapack_cpus
            npcol = 1

            # Get a sort of reasonable number of columns/rows
            while npcol < nprow and nprow % 2 == 0:
                npcol *= 2
                nprow //= 2
            assert npcol * nprow == max_scalapack_cpus

            # ScaLAPACK creates trouble if there aren't at least a few
            # whole blocks; choose block size so there will always be
            # several blocks.  This will crash for small test systems,
            # but so will ScaLAPACK in any case
            blocksize = min(-(-nbands // 4), 64)
            sl_default = (nprow, npcol, blocksize)
        else:
            sl_default = self.parallel['sl_default']

        if mode.name == 'lcao':
            # Layouts used for general diagonalizer
            sl_lcao = self.parallel['sl_lcao']
            if sl_lcao is None:
                sl_lcao = sl_default
            lcaoksl = get_KohnSham_layouts(sl_lcao,
                                           'lcao',
                                           gd,
                                           bd,
                                           domainband_comm,
                                           dtype,
                                           nao=nao,
                                           timer=self.timer)

            self.wfs = mode(lcaoksl, **wfs_kwargs)

        elif mode.name == 'fd' or mode.name == 'pw':
            # buffer_size keyword only relevant for fdpw
            buffer_size = self.parallel['buffer_size']
            # Layouts used for diagonalizer
            sl_diagonalize = self.parallel['sl_diagonalize']
            if sl_diagonalize is None:
                sl_diagonalize = sl_default
            diagksl = get_KohnSham_layouts(
                sl_diagonalize,
                'fd',  # XXX
                # choice of key 'fd' not so nice
                gd,
                bd,
                domainband_comm,
                dtype,
                buffer_size=buffer_size,
                timer=self.timer)

            # Layouts used for orthonormalizer
            sl_inverse_cholesky = self.parallel['sl_inverse_cholesky']
            if sl_inverse_cholesky is None:
                sl_inverse_cholesky = sl_default
            if sl_inverse_cholesky != sl_diagonalize:
                message = 'sl_inverse_cholesky != sl_diagonalize ' \
                    'is not implemented.'
                raise NotImplementedError(message)
            orthoksl = get_KohnSham_layouts(sl_inverse_cholesky,
                                            'fd',
                                            gd,
                                            bd,
                                            domainband_comm,
                                            dtype,
                                            buffer_size=buffer_size,
                                            timer=self.timer)

            # Use (at most) all available LCAO for initialization
            lcaonbands = min(nbands, nao // band_comm.size * band_comm.size)

            try:
                lcaobd = BandDescriptor(lcaonbands, band_comm, parstride_bands)
            except RuntimeError:
                initksl = None
            else:
                # Layouts used for general diagonalizer
                # (LCAO initialization)
                sl_lcao = self.parallel['sl_lcao']
                if sl_lcao is None:
                    sl_lcao = sl_default
                initksl = get_KohnSham_layouts(sl_lcao,
                                               'lcao',
                                               gd,
                                               lcaobd,
                                               domainband_comm,
                                               dtype,
                                               nao=nao,
                                               timer=self.timer)

            self.wfs = mode(diagksl, orthoksl, initksl, **wfs_kwargs)
        else:
            self.wfs = mode(self, **wfs_kwargs)

        self.log(self.wfs, '\n')
예제 #27
0
파일: bse.py 프로젝트: thonmaker/gpaw
    def __init__(self,
                 calc=None,
                 spinors=False,
                 ecut=10.,
                 scale=1.0,
                 nbands=None,
                 valence_bands=None,
                 conduction_bands=None,
                 eshift=None,
                 gw_skn=None,
                 truncation=None,
                 integrate_gamma=1,
                 txt=sys.stdout,
                 mode='BSE',
                 wfile=None,
                 write_h=False,
                 write_v=False):

        """Creates the BSE object

        calc: str or calculator object
            The string should refer to the .gpw file contaning KS orbitals
        ecut: float
            Plane wave cutoff energy (eV)
        nbands: int
            Number of bands used for the screened interaction
        valence_bands: list
            Valence bands used in the BSE Hamiltonian
        conduction_bands: list
            Conduction bands used in the BSE Hamiltonian
        eshift: float
            Scissors operator opening the gap (eV)
        gw_skn: list / array
            List or array defining the gw quasiparticle energies used in
            the BSE Hamiltonian. Should match spin, k-points and
            valence/conduction bands
        truncation: str
            Coulomb truncation scheme. Can be either wigner-seitz,
            2D, 1D, or 0D
        integrate_gamma: int
            Method to integrate the Coulomb interaction. 1 is a numerical
            integration at all q-points with G=[0,0,0] - this breaks the
            symmetry slightly. 0 is analytical integration at q=[0,0,0] only -
            this conserves the symmetry. integrate_gamma=2 is the same as 1,
            but the average is only carried out in the non-periodic directions.
        txt: str
            txt output
        mode: str
            Theory level used. can be RPA TDHF or BSE. Only BSE is screened.
        wfile: str
            File for saving screened interaction and some other stuff
            needed later
        write_h: bool
            If True, write the BSE Hamiltonian to H_SS.ulm.
        write_v: bool
            If True, write eigenvalues and eigenstates to v_TS.ulm
        """

        # Calculator
        if isinstance(calc, str):
            calc = GPAW(calc, txt=None, communicator=serial_comm)
        self.calc = calc
        self.spinors = spinors
        self.scale = scale

        assert mode in ['RPA', 'TDHF', 'BSE']
        # assert calc.wfs.kd.nbzkpts % world.size == 0

        # txt file
        if world.rank != 0:
            txt = devnull
        elif isinstance(txt, str):
            txt = open(txt, 'w', 1)
        self.fd = txt

        self.ecut = ecut / Hartree
        self.nbands = nbands
        self.mode = mode
        self.truncation = truncation
        if integrate_gamma == 0 and truncation is not None:
            print('***WARNING*** Analytical Coulomb integration is ' +
                  'not expected to work with Coulomb truncation. ' +
                  'Use integrate_gamma=1', file=self.fd)
        self.integrate_gamma = integrate_gamma
        self.wfile = wfile
        self.write_h = write_h
        self.write_v = write_v

        # Find q-vectors and weights in the IBZ:
        self.kd = calc.wfs.kd
        if -1 in self.kd.bz2bz_ks:
            print('***WARNING*** Symmetries may not be right ' +
                  'Use gamma-centered grid to be sure', file=self.fd)
        offset_c = 0.5 * ((self.kd.N_c + 1) % 2) / self.kd.N_c
        bzq_qc = monkhorst_pack(self.kd.N_c) + offset_c
        self.qd = KPointDescriptor(bzq_qc)
        self.qd.set_symmetry(self.calc.atoms, self.kd.symmetry)
        self.vol = abs(np.linalg.det(calc.wfs.gd.cell_cv))

        # bands
        self.spins = self.calc.wfs.nspins
        if self.spins == 2:
            if self.spinors:
                self.spinors = False
                print('***WARNING*** Presently the spinor version' +
                      'does not work for spin-polarized calculations.' +
                      'Performing scalar calculation', file=self.fd)
            assert len(valence_bands[0]) == len(valence_bands[1])
            assert len(conduction_bands[0]) == len(conduction_bands[1])
        if valence_bands is None:
            nv = self.calc.wfs.setups.nvalence
            valence_bands = [[nv // 2 - 1]]
            if self.spins == 2:
                valence_bands *= 2
        if conduction_bands is None:
            conduction_bands = [[valence_bands[-1] + 1]]
            if self.spins == 2:
                conduction_bands *= 2

        self.val_sn = np.array(valence_bands)
        if len(np.shape(self.val_sn)) == 1:
            self.val_sn = np.array([self.val_sn])
        self.con_sn = np.array(conduction_bands)
        if len(np.shape(self.con_sn)) == 1:
            self.con_sn = np.array([self.con_sn])

        self.td = True
        for n in self.val_sn[0]:
            if n in self.con_sn[0]:
                self.td = False
        if len(self.val_sn) == 2:
            for n in self.val_sn[1]:
                if n in self.con_sn[1]:
                    self.td = False

        self.nv = len(self.val_sn[0])
        self.nc = len(self.con_sn[0])
        if eshift is not None:
            eshift /= Hartree
        if gw_skn is not None:
            assert self.nv + self.nc == len(gw_skn[0, 0])
            assert self.kd.nibzkpts == len(gw_skn[0])
            gw_skn = gw_skn[:, self.kd.bz2ibz_k]
            # assert self.kd.nbzkpts == len(gw_skn[0])
            gw_skn /= Hartree
        self.gw_skn = gw_skn
        self.eshift = eshift

        # Number of pair orbitals
        self.nS = self.kd.nbzkpts * self.nv * self.nc * self.spins
        self.nS *= (self.spinors + 1)**2

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

        self.print_initialization(self.td, self.eshift, self.gw_skn)
예제 #28
0
def create_kpoint_descriptor(bzkpts_kc, nspins, atoms, symmetry, comm):
    kd = KPointDescriptor(bzkpts_kc, nspins)
    # self.timer.start('Set symmetry')
    kd.set_symmetry(atoms, symmetry, comm=comm)
    # self.timer.stop('Set symmetry')
    return kd
예제 #29
0
nb = 6

a = Atoms('H', cell=(3 * np.eye(3)), pbc=True)

calc = GPAW(mode=PW(600), kpts=[[0, 0, 0], [0.25, 0, 0]])
a.calc = calc
a.get_potential_energy()
calc.diagonalize_full_hamiltonian(nbands=nb, expert=True)
calc.write('a.gpw', 'all')

pair = PairDensity('a.gpw', ecut=100)

# Check continuity eq.
for q_c in [[0, 0, 0], [1. / 4, 0, 0]]:
    ol = np.allclose(q_c, 0.0)
    qd = KPointDescriptor([q_c])
    pd = PWDescriptor(pair.ecut, calc.wfs.gd, complex, qd)
    kptpair = pair.get_kpoint_pair(pd, 0, [0, 0, 0], 0, nb, 0, nb)
    deps_nm = kptpair.get_transition_energies(np.arange(0, nb),
                                              np.arange(0, nb))

    n_nmG = pair.get_pair_density(pd,
                                  kptpair,
                                  np.arange(0, nb),
                                  np.arange(0, nb),
                                  optical_limit=ol)

    n_nmvG = pair.get_pair_momentum(pd, kptpair, np.arange(0, nb),
                                    np.arange(0, nb))

    if ol:
예제 #30
0
파일: bse.py 프로젝트: thonmaker/gpaw
    def calculate(self, optical=True, ac=1.0):

        if self.spinors:
            """Calculate spinors. Here m is index of eigenvalues with SOC
            and n is the basis of eigenstates withour SOC. Below m is used
            for unoccupied states and n is used for occupied states so be
            careful!"""

            print('Diagonalizing spin-orbit Hamiltonian', file=self.fd)
            param = self.calc.parameters
            if not param['symmetry'] == 'off':
                print('Calculating KS wavefunctions without symmetry ' +
                      'for spin-orbit', file=self.fd)
                if not op.isfile('gs_nosym.gpw'):
                    calc_so = GPAW(**param)
                    calc_so.set(symmetry='off',
                                fixdensity=True,
                                txt='gs_nosym.txt')
                    calc_so.atoms = self.calc.atoms
                    calc_so.density = self.calc.density
                    calc_so.get_potential_energy()
                    calc_so.write('gs_nosym.gpw')
                calc_so = GPAW('gs_nosym.gpw', txt=None,
                               communicator=serial_comm)
                e_mk, v_knm = get_spinorbit_eigenvalues(calc_so,
                                                        return_wfs=True,
                                                        scale=self.scale)
                del calc_so
            else:
                e_mk, v_knm = get_spinorbit_eigenvalues(self.calc,
                                                        return_wfs=True,
                                                        scale=self.scale)
            e_mk /= Hartree

        # Parallelization stuff
        nK = self.kd.nbzkpts
        myKrange, myKsize, mySsize = self.parallelisation_sizes()

        # Calculate exchange interaction
        qd0 = KPointDescriptor([self.q_c])
        pd0 = PWDescriptor(self.ecut, self.calc.wfs.gd, complex, qd0)
        ikq_k = self.kd.find_k_plus_q(self.q_c)
        v_G = get_coulomb_kernel(pd0, self.kd.N_c, truncation=self.truncation,
                                 wstc=self.wstc)
        if optical:
            v_G[0] = 0.0

        self.pair = PairDensity(self.calc, self.ecut, world=serial_comm,
                                txt='pair.txt')

        # Calculate direct (screened) interaction and PAW corrections
        if self.mode == 'RPA':
            Q_aGii = self.pair.initialize_paw_corrections(pd0)
        else:
            self.get_screened_potential(ac=ac)
            if (self.qd.ibzk_kc - self.q_c < 1.0e-6).all():
                iq0 = self.qd.bz2ibz_k[self.kd.where_is_q(self.q_c,
                                                          self.qd.bzk_kc)]
                Q_aGii = self.Q_qaGii[iq0]
            else:
                Q_aGii = self.pair.initialize_paw_corrections(pd0)

        # Calculate pair densities, eigenvalues and occupations
        so = self.spinors + 1
        Nv, Nc = so * self.nv, so * self.nc
        Ns = self.spins
        rhoex_KsmnG = np.zeros((nK, Ns, Nv, Nc, len(v_G)), complex)
        # rhoG0_Ksmn = np.zeros((nK, Ns, Nv, Nc), complex)
        df_Ksmn = np.zeros((nK, Ns, Nv, Nc), float) # -(ev - ec)
        deps_ksmn = np.zeros((myKsize, Ns, Nv, Nc), float) # -(fv - fc)
        if np.allclose(self.q_c, 0.0):
            optical_limit = True
        else:
            optical_limit = False
        get_pair = self.pair.get_kpoint_pair
        get_rho = self.pair.get_pair_density
        if self.spinors:
            # Get all pair densities to allow for SOC mixing
            # Use twice as many no-SOC states as BSE bands to allow mixing
            vi_s = [2 * self.val_sn[0, 0] - self.val_sn[0, -1] - 1]
            vf_s = [2 * self.con_sn[0, -1] - self.con_sn[0, 0] + 2]
            if vi_s[0] < 0:
                vi_s[0] = 0
            ci_s, cf_s = vi_s, vf_s
            ni, nf = vi_s[0], vf_s[0]
            mvi = 2 * self.val_sn[0, 0]
            mvf = 2 * (self.val_sn[0, -1] + 1)
            mci = 2 * self.con_sn[0, 0]
            mcf = 2 * (self.con_sn[0, -1] + 1)
        else:
            vi_s, vf_s = self.val_sn[:, 0], self.val_sn[:, -1] + 1
            ci_s, cf_s = self.con_sn[:, 0], self.con_sn[:, -1] + 1
        for ik, iK in enumerate(myKrange):
            for s in range(Ns):
                pair = get_pair(pd0, s, iK,
                                vi_s[s], vf_s[s], ci_s[s], cf_s[s])
                m_m = np.arange(vi_s[s], vf_s[s])
                n_n = np.arange(ci_s[s], cf_s[s])
                if self.gw_skn is not None:
                    iKq = self.calc.wfs.kd.find_k_plus_q(self.q_c, [iK])[0]
                    epsv_m = self.gw_skn[s, iK, :self.nv]
                    epsc_n = self.gw_skn[s, iKq, self.nv:]
                    deps_ksmn[ik] = -(epsv_m[:, np.newaxis] - epsc_n)
                elif self.spinors:
                    iKq = self.calc.wfs.kd.find_k_plus_q(self.q_c, [iK])[0]
                    epsv_m = e_mk[mvi:mvf, iK]
                    epsc_n = e_mk[mci:mcf, iKq]
                    deps_ksmn[ik, s] = -(epsv_m[:, np.newaxis] - epsc_n)
                else:
                    deps_ksmn[ik, s] = -pair.get_transition_energies(m_m, n_n)

                df_mn = pair.get_occupation_differences(self.val_sn[s],
                                                        self.con_sn[s])
                rho_mnG = get_rho(pd0, pair,
                                  m_m, n_n,
                                  optical_limit=optical_limit,
                                  direction=self.direction,
                                  Q_aGii=Q_aGii,
                                  extend_head=False)
                if self.spinors:
                    if optical_limit:
                        deps0_mn = -pair.get_transition_energies(m_m, n_n)
                        rho_mnG[:, :, 0] *= deps0_mn
                    df_Ksmn[iK, s, ::2, ::2] = df_mn
                    df_Ksmn[iK, s, ::2, 1::2] = df_mn
                    df_Ksmn[iK, s, 1::2, ::2] = df_mn
                    df_Ksmn[iK, s, 1::2, 1::2] = df_mn
                    vecv0_nm = v_knm[iK][::2][ni:nf, mvi:mvf]
                    vecc0_nm = v_knm[iKq][::2][ni:nf, mci:mcf]
                    rho_0mnG = np.dot(vecv0_nm.T.conj(),
                                      np.dot(vecc0_nm.T, rho_mnG))
                    vecv1_nm = v_knm[iK][1::2][ni:nf, mvi:mvf]
                    vecc1_nm = v_knm[iKq][1::2][ni:nf, mci:mcf]
                    rho_1mnG = np.dot(vecv1_nm.T.conj(),
                                      np.dot(vecc1_nm.T, rho_mnG))
                    rhoex_KsmnG[iK, s] = rho_0mnG + rho_1mnG
                    if optical_limit:
                        rhoex_KsmnG[iK, s, :, :, 0] /= deps_ksmn[ik, s]
                else:
                    df_Ksmn[iK, s] = pair.get_occupation_differences(m_m, n_n)
                    rhoex_KsmnG[iK, s] = rho_mnG

        if self.eshift is not None:
            deps_ksmn[np.where(df_Ksmn[myKrange] > 1.0e-3)] += self.eshift
            deps_ksmn[np.where(df_Ksmn[myKrange] < -1.0e-3)] -= self.eshift

        world.sum(df_Ksmn)
        world.sum(rhoex_KsmnG)

        self.rhoG0_S = np.reshape(rhoex_KsmnG[:, :, :, :, 0], -1)

        if hasattr(self, 'H_sS'):
            return

        # Calculate Hamiltonian
        t0 = time()
        print('Calculating %s matrix elements at q_c = %s'
              % (self.mode, self.q_c), file=self.fd)
        H_ksmnKsmn = np.zeros((myKsize, Ns, Nv, Nc, nK, Ns, Nv, Nc), complex)
        for ik1, iK1 in enumerate(myKrange):
            for s1 in range(Ns):
                kptv1 = self.pair.get_k_point(s1, iK1, vi_s[s1], vf_s[s1])
                kptc1 = self.pair.get_k_point(s1, ikq_k[iK1], ci_s[s1],
                                              cf_s[s1])
                rho1_mnG = rhoex_KsmnG[iK1, s1]

                #rhoG0_Ksmn[iK1, s1] = rho1_mnG[:, :, 0]
                rho1ccV_mnG = rho1_mnG.conj()[:, :] * v_G
                for s2 in range(Ns):
                    for Q_c in self.qd.bzk_kc:
                        iK2 = self.kd.find_k_plus_q(Q_c, [kptv1.K])[0]
                        rho2_mnG = rhoex_KsmnG[iK2, s2]
                        rho2_mGn = np.swapaxes(rho2_mnG, 1, 2)
                        H_ksmnKsmn[ik1, s1, :, :, iK2, s2, :, :] += (
                            np.dot(rho1ccV_mnG, rho2_mGn))
                        if not self.mode == 'RPA' and s1 == s2:
                            ikq = ikq_k[iK2]
                            kptv2 = self.pair.get_k_point(s1, iK2, vi_s[s1],
                                                          vf_s[s1])
                            kptc2 = self.pair.get_k_point(s1, ikq, ci_s[s1],
                                                          cf_s[s1])
                            rho3_mmG, iq = self.get_density_matrix(kptv1,
                                                                   kptv2)
                            rho4_nnG, iq = self.get_density_matrix(kptc1,
                                                                   kptc2)
                            if self.spinors:
                                vec0_nm = v_knm[iK1][::2][ni:nf, mvi:mvf]
                                vec1_nm = v_knm[iK1][1::2][ni:nf, mvi:mvf]
                                vec2_nm = v_knm[iK2][::2][ni:nf, mvi:mvf]
                                vec3_nm = v_knm[iK2][1::2][ni:nf, mvi:mvf]
                                rho_0mnG = np.dot(vec0_nm.T.conj(),
                                                  np.dot(vec2_nm.T, rho3_mmG))
                                rho_1mnG = np.dot(vec1_nm.T.conj(),
                                                  np.dot(vec3_nm.T, rho3_mmG))
                                rho3_mmG = rho_0mnG + rho_1mnG
                                vec0_nm = v_knm[ikq_k[iK1]][::2][ni:nf, mci:mcf]
                                vec1_nm = v_knm[ikq_k[iK1]][1::2][ni:nf,mci:mcf]
                                vec2_nm = v_knm[ikq][::2][ni:nf, mci:mcf]
                                vec3_nm = v_knm[ikq][1::2][ni:nf, mci:mcf]
                                rho_0mnG = np.dot(vec0_nm.T.conj(),
                                                  np.dot(vec2_nm.T, rho4_nnG))
                                rho_1mnG = np.dot(vec1_nm.T.conj(),
                                                  np.dot(vec3_nm.T, rho4_nnG))
                                rho4_nnG = rho_0mnG + rho_1mnG

                            rho3ccW_mmG = np.dot(rho3_mmG.conj(),
                                                 self.W_qGG[iq])
                            W_mmnn = np.dot(rho3ccW_mmG,
                                            np.swapaxes(rho4_nnG, 1, 2))
                            W_mnmn = np.swapaxes(W_mmnn, 1, 2) * Ns * so
                            H_ksmnKsmn[ik1, s1, :, :, iK2, s1] -= 0.5 * W_mnmn
            if iK1 % (myKsize // 5 + 1) == 0:
                dt = time() - t0
                tleft = dt * myKsize / (iK1 + 1) - dt
                print('  Finished %s pair orbitals in %s - Estimated %s left' %
                      ((iK1 + 1) * Nv * Nc * Ns * world.size,
                       timedelta(seconds=round(dt)),
                       timedelta(seconds=round(tleft))), file=self.fd)

        #if self.mode == 'BSE':
        #    del self.Q_qaGii, self.W_qGG, self.pd_q

        H_ksmnKsmn /= self.vol

        mySsize = myKsize * Nv * Nc * Ns
        if myKsize > 0:
            iS0 = myKrange[0] *  Nv * Nc * Ns

        #world.sum(rhoG0_Ksmn)
        #self.rhoG0_S = np.reshape(rhoG0_Ksmn, -1)
        self.df_S = np.reshape(df_Ksmn, -1)
        if not self.td:
            self.excludef_S = np.where(np.abs(self.df_S) < 0.001)[0]
        # multiply by 2 when spin-paired and no SOC
        self.df_S *= 2.0 / nK / Ns / so
        self.deps_s = np.reshape(deps_ksmn, -1)
        H_sS = np.reshape(H_ksmnKsmn, (mySsize, self.nS))
        for iS in range(mySsize):
            # Multiply by occupations and adiabatic coupling
            H_sS[iS] *= self.df_S[iS0 + iS] * ac
            # add bare transition energies
            H_sS[iS, iS0 + iS] += self.deps_s[iS]

        self.H_sS = H_sS

        if self.write_h:
            self.par_save('H_SS.ulm', 'H_SS', self.H_sS)