Ejemplo n.º 1
0
    def __init__(self,
                 gd,
                 finegd,
                 nspins,
                 setups,
                 timer,
                 xc,
                 world,
                 kptband_comm,
                 vext=None,
                 collinear=True,
                 psolver=None,
                 stencil=3):
        Hamiltonian.__init__(self, gd, finegd, nspins, setups, timer, xc,
                             world, kptband_comm, vext, collinear)

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

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

        self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups],
                        forces=True)
        self.vbar_g = None
Ejemplo n.º 2
0
    def initialize(self, density, hamiltonian, wfs, occupations):
        assert wfs.kd.gamma
        self.xc.initialize(density, hamiltonian, wfs, occupations)
        self.kpt_comm = wfs.kd.comm
        self.nspins = wfs.nspins
        self.setups = wfs.setups
        self.density = density
        self.kpt_u = wfs.kpt_u
        self.exx_s = np.zeros(self.nspins)
        self.ekin_s = np.zeros(self.nspins)
        self.nocc_s = np.empty(self.nspins, int)

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

        assert wfs.kd.gamma
        assert not wfs.gd.pbc_c.any()

        self.wfs = wfs
        self.dtype = float
        self.xc.initialize(density, hamiltonian, wfs, occ)
        self.kpt_comm = wfs.kd.comm
        self.nspins = wfs.nspins
        self.nbands = wfs.bd.nbands

        if self.finegrid:
            self.finegd = density.finegd
            self.ghat = density.ghat
        else:
            self.finegd = density.gd
            self.ghat = LFC(self.finegd,
                            [setup.ghat_l for setup in density.setups],
                            integral=sqrt(4 * pi),
                            forces=True)

        poissonsolver = PoissonSolver(eps=1e-14)
        poissonsolver.set_grid_descriptor(self.finegd)
        poissonsolver.initialize()

        self.spin_s = {}
        for kpt in wfs.kpt_u:
            self.spin_s[kpt.s] = SICSpin(kpt, self.xc, density, hamiltonian,
                                         wfs, poissonsolver, self.ghat,
                                         self.finegd, **self.parameters)
Ejemplo n.º 4
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))
Ejemplo n.º 5
0
    def setUp(self):
        UTBandParallelSetup.setUp(self)
        for virtvar in ['dtype', 'blocking', 'async']:
            assert getattr(self,
                           virtvar) is not None, 'Virtual "%s"!' % virtvar

        # Create randomized atoms
        self.atoms = create_random_atoms(self.gd)

        # 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)

        # Create atomic projector overlaps
        spos_ac = self.atoms.get_scaled_positions() % 1.0
        self.rank_a = self.gd.get_ranks_from_positions(spos_ac)
        self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups],
                      self.kpt_comm,
                      dtype=self.dtype)
        self.pt.set_positions(spos_ac)

        if memstats:
            # Hack to scramble heap usage into steady-state level
            HEAPSIZE = 25 * 1024**2
            for i in range(100):
                data = np.empty(np.random.uniform(0, HEAPSIZE // 8), float)
                del data
            self.mem_pre = record_memory()
            self.mem_alloc = None
            self.mem_test = None

        # Stuff for pseudo wave functions and projections
        if self.dtype == complex:
            self.gamma = 1j**(1.0 / self.nbands)
        else:
            self.gamma = 1.0

        self.psit_nG = None
        self.P_ani = None
        self.Qeff_a = None
        self.Qtotal = None

        self.allocate()
Ejemplo n.º 6
0
    def initialize(self, density, hamiltonian, wfs, occupations):
        assert wfs.kd.gamma
        self.xc.initialize(density, hamiltonian, wfs, occupations)
        self.kpt_comm = wfs.kd.comm
        self.nspins = wfs.nspins
        self.setups = wfs.setups
        self.density = density
        self.kpt_u = wfs.kpt_u
        self.exx_s = np.zeros(self.nspins)
        self.ekin_s = np.zeros(self.nspins)
        self.nocc_s = np.empty(self.nspins, int)

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

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

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

        self.ghat = LFC(self.finegd,
                        [setup.ghat_l for setup in density.setups],
                        integral=np.sqrt(4 * np.pi),
                        forces=True)
        self.poissonsolver.set_grid_descriptor(self.finegd)
        if self.rsf == 'Yukawa':
            omega2 = self.omega**2
            self.screened_poissonsolver = HelmholtzSolver(
                k2=-omega2,
                eps=1e-11,
                nn=3,
                use_charge_center=use_charge_center)
            self.screened_poissonsolver.set_grid_descriptor(self.finegd)
Ejemplo n.º 7
0
 def initialize(self, density, hamiltonian, wfs, occupations):
     assert wfs.gamma
     self.xc.initialize(density, hamiltonian, wfs, occupations)
     self.kpt_comm = wfs.kpt_comm
     self.nspins = wfs.nspins
     self.setups = wfs.setups
     self.density = density
     self.kpt_u = wfs.kpt_u
     self.exx_s = np.zeros(self.nspins)
     self.ekin_s = np.zeros(self.nspins)
     self.nocc_s = np.empty(self.nspins, int)
     
     if self.finegrid:
         self.poissonsolver = hamiltonian.poisson
         self.ghat = density.ghat
         self.interpolator = density.interpolator
         self.restrictor = hamiltonian.restrictor
     else:
         self.poissonsolver = PoissonSolver(eps=1e-11)
         self.poissonsolver.set_grid_descriptor(density.gd)
         self.poissonsolver.initialize()
         self.ghat = LFC(density.gd,
                         [setup.ghat_l for setup in density.setups],
                         integral=np.sqrt(4 * np.pi), forces=True)
     self.gd = density.gd
     self.finegd = self.ghat.gd
Ejemplo n.º 8
0
    def with_compensation_charges(self, finegrid=False):
        """Get pair densisty including the compensation charges"""
        rhot = self.get(finegrid)

        # Determine the compensation charges for each nucleus
        Q_aL = {}
        for a, P_ni in self.P_ani.items():
            # Generate density matrix
            Pi_i = P_ni[self.i]
            Pj_i = P_ni[self.j]
            D_ii = np.outer(Pi_i, Pj_i)
            # allowed to pack as used in the scalar product with
            # the symmetric array Delta_pL
            D_p = pack(D_ii)

            # Determine compensation charge coefficients:
            Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL)

        # Add compensation charges
        if finegrid:
            self.density.ghat.add(rhot, Q_aL)
        else:
            if not hasattr(self.density, 'Ghat'):
                self.density.Ghat = LFC(
                    self.density.gd, [setup.ghat_l for setup in self.setups],
                    integral=sqrt(4 * pi))
                self.density.Ghat.set_positions(self.spos_ac)
            self.density.Ghat.add(rhot, Q_aL)

        return rhot
    def __init__(
        self,
        gd,
        finegd,
        nspins,
        setups,
        timer,
        xc,
        world,
        kptband_comm,
        vext=None,
        collinear=True,
        psolver=None,
        stencil=3,
    ):
        Hamiltonian.__init__(self, gd, finegd, nspins, setups, timer, xc, world, kptband_comm, vext, collinear)

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

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

        self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups], forces=True)
        self.vbar_g = None
Ejemplo n.º 10
0
    def initialize(self, density, hamiltonian, wfs, occ=None):
        
        assert wfs.gamma
        assert not wfs.gd.pbc_c.any()

        self.wfs = wfs
        self.dtype = float
        self.xc.initialize(density, hamiltonian, wfs, occ)
        self.kpt_comm = wfs.kpt_comm
        self.nspins = wfs.nspins
        self.nbands = wfs.bd.nbands
        
        if self.finegrid:
            self.finegd = density.finegd
            self.ghat = density.ghat
        else:
            self.finegd = density.gd
            self.ghat = LFC(self.finegd,
                        [setup.ghat_l for setup in density.setups],
                        integral=sqrt(4 * pi), forces=True)
        
        poissonsolver = PoissonSolver(eps=1e-14)
        poissonsolver.set_grid_descriptor(self.finegd)
        poissonsolver.initialize()
        
        self.spin_s = {}
        for kpt in wfs.kpt_u:
            self.spin_s[kpt.s] = SICSpin(kpt, self.xc,
                                         density, hamiltonian, wfs,
                                         poissonsolver, self.ghat,
                                         self.finegd, **self.parameters)
Ejemplo n.º 11
0
def get_grid_dP_aMix(spos_ac, wfs, q, timer=nulltimer):  # XXXXXX q
    nao = wfs.setups.nao
    C_MM = np.identity(nao, dtype=wfs.dtype)
    # XXX In the future use the New Two-Center integrals
    # to evaluate this
    dP_aMix = {}
    for a, setup in enumerate(wfs.setups):
        ni = 0
        dP_Mix = np.zeros((nao, setup.ni, 3))
        pt = LFC(wfs.gd, [setup.pt_j],
                 wfs.kd.comm,
                 dtype=wfs.dtype,
                 forces=True)
        spos1_ac = [spos_ac[a]]
        pt.set_k_points(wfs.ibzk_qc)
        pt.set_positions(spos1_ac)
        for b, setup_b in enumerate(wfs.setups):
            nao = setup_b.nao
            phi_MG = wfs.gd.zeros(nao, wfs.dtype)
            phi_MG = wfs.gd.collect(phi_MG, broadcast=False)
            wfs.basis_functions.lcao_to_grid(C_MM[ni:ni + nao], phi_MG, q)
            dP_bMix = pt.dict(len(phi_MG), derivative=True)
            pt.derivative(phi_MG, dP_bMix, q=q)
            dP_Mix[ni:ni + nao] = dP_bMix[0]
            ni += nao
            timer.write_now('projector grad. doing atoms (%s, %s) ' % (a, b))

        dP_aMix[a] = dP_Mix
    return dP_aMix
Ejemplo n.º 12
0
Archivo: mgga.py Proyecto: qsnake/gpaw
 def initialize(self, density, hamiltonian, wfs, occupations):
     self.wfs = wfs
     self.tauct = LFC(wfs.gd, [[setup.tauct] for setup in wfs.setups], forces=True, cut=True)
     self.tauct_G = None
     self.dedtaut_sG = None
     self.restrict = hamiltonian.restrictor.apply
     self.interpolate = density.interpolator.apply
     self.taugrad_v = [Gradient(wfs.gd, v, n=self.nn, dtype=wfs.dtype, allocate=True).apply for v in range(3)]
Ejemplo n.º 13
0
    def initialize(self, setups, timer, magmom_av, hund):
        Density.initialize(self, setups, timer, magmom_av, hund)

        # Interpolation function for the density:
        self.interpolator = Transformer(self.gd, self.finegd, self.stencil)
        
        spline_aj = []
        for setup in setups:
            if setup.nct is None:
                spline_aj.append([])
            else:
                spline_aj.append([setup.nct])
        self.nct = LFC(self.gd, spline_aj,
                       integral=[setup.Nct for setup in setups],
                       forces=True, cut=True)
        self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups],
                        integral=sqrt(4 * pi), forces=True)
Ejemplo n.º 14
0
    def initialize(self, setups, timer, magmom_av, hund):
        Density.initialize(self, setups, timer, magmom_av, hund)

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

        spline_aj = []
        for setup in setups:
            if setup.nct is None:
                spline_aj.append([])
            else:
                spline_aj.append([setup.nct])
        self.nct = LFC(self.gd, spline_aj,
                       integral=[setup.Nct for setup in setups],
                       forces=True, cut=True)
        self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups],
                        integral=sqrt(4 * pi), forces=True)
Ejemplo n.º 15
0
    def __init__(self, gd, finegd, nspins, setups, stencil, timer, xc,
                 psolver, vext_g):
        """Create the Hamiltonian."""
        self.gd = gd
        self.finegd = finegd
        self.nspins = nspins
        self.setups = setups
        self.timer = timer
        self.xc = xc
        
        # Solver for the Poisson equation:
        if psolver is None:
            psolver = PoissonSolver(nn=3, relax='J')
        self.poisson = psolver
        self.poisson.set_grid_descriptor(finegd)

        self.dH_asp = None

        # The external potential
        self.vext_g = vext_g

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

        self.rank_a = None

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

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

        self.Ekin0 = None
        self.Ekin = None
        self.Epot = None
        self.Ebar = None
        self.Eext = None
        self.Exc = None
        self.Etot = None
        self.S = None
        self.allocated = False
Ejemplo n.º 16
0
    def set_setups(self, setups):

        self.pd = PWDescriptor(self.ecut, self.gd, self.kd.ibzk_qc)
        pt = LFC(self.gd, [setup.pt_j for setup in setups],
                 self.kpt_comm,
                 dtype=self.dtype,
                 forces=True)
        self.pt = PWLFC(pt, self.pd)
        FDPWWaveFunctions.set_setups(self, setups)
Ejemplo n.º 17
0
    def set_grid_descriptor(self, gd):
        GGA.set_grid_descriptor(self, gd)
        self.dedmu_g = gd.zeros()
        self.dedbeta_g = gd.zeros()
        # Create gaussian LFC
        l_lim = 1.0e-30
        rcut = 12
        points = 200
        r_i = np.linspace(0, rcut, points + 1)
        rcgauss = 1.2
        g_g = (2 / rcgauss**3 / np.pi *
               np.exp(-((r_i / rcgauss)**2)**self.alpha))

        # Values too close to zero can cause numerical problems especially with
        # forces (some parts of the mu and beta field can become negative)
        g_g[np.where(g_g < l_lim)] = l_lim
        spline = Spline(l=0, rmax=rcut, f_g=g_g)
        spline_j = [[spline]] * len(self.atoms)
        self.Pa = LFC(gd, spline_j)
Ejemplo n.º 18
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
Ejemplo n.º 19
0
    def __init__(self, density, atoms, finegrid):
        """Initialization needs a paw instance, and whether the compensated
        pair density should be on the fine grid (boolean)"""

        self.density = density
        self.finegrid = finegrid

        if not finegrid:
            density.Ghat = LFC(density.gd,
                               [setup.ghat_l for setup in density.setups],
                               integral=sqrt(4 * pi))
            density.Ghat.set_positions(atoms.get_scaled_positions() % 1.0)
Ejemplo n.º 20
0
    def add_potential_correction(self, v_R, alpha):
        dens = self.calc.density
        dens.D_asp.redistribute(dens.atom_partition.as_serial())
        dens.Q_aL.redistribute(dens.atom_partition.as_serial())

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

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

        if dv_a1:
            dv = LFC(self.gd, dv_a1)
            dv.set_positions(self.calc.spos_ac)
            dv.add(v_R)
        dens.gd.comm.broadcast(v_R, 0)
Ejemplo n.º 21
0
 def _initialize_corrections(self):
     if self.dphi is not None:
         return
     splines = {}
     dphi_aj = []
     for setup in self.calc.wfs.setups:
         dphi_j = splines.get(setup)
         if dphi_j is None:
             rcut = max(setup.rcut_j) * 1.1
             gcut = setup.rgd.ceil(rcut)
             dphi_j = []
             for l, phi_g, phit_g in zip(setup.l_j,
                                         setup.data.phi_jg,
                                         setup.data.phit_jg):
                 dphi_g = (phi_g - phit_g)[:gcut]
                 dphi_j.append(setup.rgd.spline(dphi_g, rcut, l,
                                                points=200))
         dphi_aj.append(dphi_j)
         
     self.dphi = LFC(self.gd, dphi_aj, kd=self.calc.wfs.kd.copy(),
                     dtype=self.calc.wfs.dtype)
     self.dphi.set_positions(self.calc.atoms.get_scaled_positions())
Ejemplo n.º 22
0
    def __init__(self,
                 gd,
                 finegd,
                 nspins,
                 setups,
                 timer,
                 xc,
                 world,
                 vext=None,
                 psolver=None,
                 stencil=3,
                 redistributor=None):
        Hamiltonian.__init__(self,
                             gd,
                             finegd,
                             nspins,
                             setups,
                             timer,
                             xc,
                             world,
                             vext=vext,
                             redistributor=redistributor)

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

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

        self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups],
                        forces=True)
        self.vbar_g = None
Ejemplo n.º 23
0
    def initialize(self, density, hamiltonian, wfs, occupations):
        assert wfs.kd.gamma
        self.xc.initialize(density, hamiltonian, wfs, occupations)
        self.kpt_comm = wfs.kd.comm
        self.nspins = wfs.nspins
        self.setups = wfs.setups
        self.density = density
        self.kpt_u = wfs.kpt_u
        self.exx_s = np.zeros(self.nspins)
        self.ekin_s = np.zeros(self.nspins)
        self.nocc_s = np.empty(self.nspins, int)

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

        # XXX How do we construct a copy of the Poisson solver of the
        # Hamiltonian?  We don't know what class it is, etc., but gd
        # may differ.
        self.poissonsolver = PoissonSolver(eps=1e-11)
        #self.poissonsolver = hamiltonian.poisson

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

        self.ghat = LFC(self.finegd,
                        [setup.ghat_l for setup in density.setups],
                        integral=np.sqrt(4 * np.pi),
                        forces=True)
        self.poissonsolver.set_grid_descriptor(self.finegd)
        self.poissonsolver.initialize()
Ejemplo n.º 24
0
    def setUp(self):
        UTBandParallelSetup.setUp(self)
        for virtvar in ['dtype','blocking','async']:
            assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar

        # Create randomized atoms
        self.atoms = create_random_atoms(self.gd)

        # 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)

        # Create atomic projector overlaps
        spos_ac = self.atoms.get_scaled_positions() % 1.0
        self.rank_a = self.gd.get_ranks_from_positions(spos_ac)
        self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups],
                      dtype=self.dtype)
        self.pt.set_positions(spos_ac)

        if memstats:
            # Hack to scramble heap usage into steady-state level
            HEAPSIZE = 25 * 1024**2
            for i in range(100):
                data = np.empty(np.random.uniform(0, HEAPSIZE // 8), float)
                del data
            self.mem_pre = record_memory()
            self.mem_alloc = None
            self.mem_test = None

        # Stuff for pseudo wave functions and projections
        if self.dtype == complex:
            self.gamma = 1j**(1.0/self.nbands)
        else:
            self.gamma = 1.0

        self.psit_nG = None
        self.P_ani = None
        self.Qeff_a = None
        self.Qtotal = None

        self.allocate()
Ejemplo n.º 25
0
    def initialize(self, paw):

        if not paw.initialized:
            raise RuntimeError('PAW instance is not initialized')
        paw.converge_wave_functions()

        self.tauct = LFC(self.gd,
                         [[setup.tauct] for setup in self.density.setups],
                         forces=True,
                         cut=True)
        self.tauct.set_positions(paw.spos_ac)

        self.taut_sg = None
        self.nt_grad2_sG = self.gd.empty(self.nspins)
        self.nt_grad2_sg = None
Ejemplo n.º 26
0
 def initialize(self, setups, stencil, timer, magmom_a, hund):
     self.timer = timer
     self.setups = setups
     self.hund = hund
     self.magmom_a = magmom_a
     
     # Interpolation function for the density:
     self.interpolator = Transformer(self.gd, self.finegd, stencil,
                                     allocate=False)
     
     spline_aj = []
     for setup in setups:
         if setup.nct is None:
             spline_aj.append([])
         else:
             spline_aj.append([setup.nct])
     self.nct = LFC(self.gd, spline_aj,
                    integral=[setup.Nct for setup in setups],
                    forces=True, cut=True)
     self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups],
                     integral=sqrt(4 * pi), forces=True)
     if self.allocated:
         self.allocated = False
         self.allocate()
Ejemplo n.º 27
0
 def __init__(self, gd, spline_j, spos_c, index=None):
     rcut = max([spline.get_cutoff() for spline in spline_j])
     corner_c = np.ceil(spos_c * gd.N_c - rcut / gd.h_c).astype(int)
     size_c = np.ceil(spos_c * gd.N_c + rcut / gd.h_c).astype(int) - corner_c
     smallgd = GridDescriptor(N_c=size_c + 1,
                              cell_cv=gd.h_c * (size_c + 1),
                              pbc_c=False,
                              comm=mpi.serial_comm)
     lfc = LFC(smallgd, [spline_j])
     lfc.set_positions((spos_c[np.newaxis, :] * gd.N_c - corner_c + 1) /
                       smallgd.N_c)
     ni = lfc.Mmax
     f_iG = smallgd.zeros(ni)
     lfc.add(f_iG, {0: np.eye(ni)})
     LocalizedFunctions.__init__(self, gd, f_iG, corner_c, 
                                  index=index)
Ejemplo n.º 28
0
def get_grid2_dP_aMix(spos_ac, wfs, q, *args, **kwargs):  # XXXXXX q
    nao = wfs.setups.nao
    C_MM = np.identity(nao, dtype=wfs.dtype)
    bfs = wfs.basis_functions
    phi_MG = wfs.gd.zeros(nao, wfs.dtype)
    bfs.lcao_to_grid(C_MM, phi_MG, q)
    setups = wfs.setups
    pt = LFC(wfs.gd, [setup.pt_j for setup in setups],
             wfs.kd.comm,
             dtype=wfs.dtype,
             forces=True)
    pt.set_k_points(wfs.ibzk_qc)
    pt.set_positions(spos_ac)
    dP_aMix = pt.dict(len(phi_MG), derivative=True)
    pt.derivative(phi_MG, dP_aMix, q=q)
    return dP_aMix
Ejemplo n.º 29
0
    def initialize(self, paw):

        if not paw.initialized:
            raise RuntimeError('PAW instance is not initialized')

        self.tauct = LFC(self.gd,
                         [[setup.tauct] for setup in self.density.setups],
                         forces=True,
                         cut=True)
        spos_ac = paw.atoms.get_scaled_positions() % 1.0
        self.tauct.set_positions(spos_ac)

        self.taut_sG = self.gd.empty(self.nspins)
        self.taut_sg = None
        self.nt_grad2_sG = self.gd.empty(self.nspins)
        self.nt_grad2_sg = None
Ejemplo n.º 30
0
    def __init__(self, gd, setups, spos_ac, fft=False):
        assert gd.comm.size == 1
        self.rhot1_G = gd.empty()
        self.rhot2_G = gd.empty()
        self.pot_G = gd.empty()
        self.dv = gd.dv
        if fft:
            self.poisson = FFTPoissonSolver()
        else:
            self.poisson = PoissonSolver(name='fd', nn=3)
        self.poisson.set_grid_descriptor(gd)
        self.setups = setups

        # Set coarse ghat
        self.Ghat = LFC(gd, [setup.ghat_l for setup in setups],
                        integral=sqrt(4 * pi))
        self.Ghat.set_positions(spos_ac)
Ejemplo n.º 31
0
def f(n, p):
    N = 2 * n
    gd = GridDescriptor((N, N, N), (L, L, L))
    a = gd.zeros()
    print(a.shape)
    #p = PoissonSolver(nn=1, relax=relax)
    p.set_grid_descriptor(gd)
    cut = N / 2.0 * 0.9
    s = Spline(l=0, rmax=cut, f_g=np.array([1, 0.5, 0.0]))
    c = LFC(gd, [[s], [s]])
    c.set_positions([(0, 0, 0), (0.5, 0.5, 0.5)])
    c.add(a)

    I0 = gd.integrate(a)
    a -= I0 / L**3

    b = gd.zeros()
    p.solve(b, a, charge=0, eps=1e-20)
    return gd.collect(b, broadcast=1)
Ejemplo n.º 32
0
    def get_all_electron_density(self, atoms, gridrefinement=2):
        """Return real all-electron density array."""

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

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

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

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

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

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

        return n_sg, gd
Ejemplo n.º 33
0
class Density:
    """Density object.
    
    Attributes:
     =============== =====================================================
     ``gd``          Grid descriptor for coarse grids.
     ``finegd``      Grid descriptor for fine grids.
     ``interpolate`` Function for interpolating the electron density.
     ``mixer``       ``DensityMixer`` object.
     =============== =====================================================

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

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

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

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

        self.rank_a = None

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

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

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

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

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

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

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

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

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

        self.rank_a = rank_a

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return n_sg, gd

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        if majority_spin == 0:
            smaj = 0
            smin = 1
        else:
            smaj = 1
            smin = 0
        nt_sg, gd = self.get_all_electron_density(atoms)
        dt_sg = nt_sg[smin] - nt_sg[smaj]
        dt_sg = np.where(dt_sg > 0, dt_sg, 0.0)
        return gd.integrate(dt_sg)
Ejemplo n.º 34
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

        N_c = self.gd.N_c
        N = self.gd.N_c.prod()
        vol = self.gd.dv * N
        
        if self.alpha is None:
            self.alpha = 6 * vol**(2 / 3.0) / pi**2
            
        self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) *
                      self.kd.nbzkpts)
        ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max()

        if self.kd.N_c is None:
            self.bzk_kc = np.zeros((1, 3))
            dfghdfgh
        else:
            n = self.kd.N_c * 2 - 1
            bzk_kc = np.indices(n).transpose((1, 2, 3, 0))
            bzk_kc.shape = (-1, 3)
            bzk_kc -= self.kd.N_c - 1
            self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c
        
        self.pwd = PWDescriptor(ecut, self.gd, self.bzk_kc)

        n = 0
        for k_c, Gpk2_G in zip(self.bzk_kc[:], self.pwd.G2_qG):
            if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX???
                if k_c.any():
                    self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G),
                                         Gpk2_G**-1)
                else:
                    self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]),
                                         Gpk2_G[1:]**-1)
                n += 1

        assert n == self.kd.N_c.prod()
        
        self.ghat = LFC(self.gd,
                        [setup.ghat_l for setup in density.setups],
                        dtype=complex
                        )
        self.ghat.set_k_points(self.bzk_kc)
        
        self.fullkd = KPointDescriptor(self.kd.bzk_kc, nspins=1)
        class S:
            id_a = []
            def set_symmetry(self, s): pass
            
        self.fullkd.set_symmetry(Atoms(pbc=True), S(), False)
        self.fullkd.set_communicator(world)
        self.pt = LFC(self.gd, [setup.pt_j for setup in density.setups],
                      dtype=complex)
        self.pt.set_k_points(self.fullkd.ibzk_kc)

        self.interpolator = density.interpolator
Ejemplo n.º 35
0
class HybridXC(XCFunctional):
    orbital_dependent = True
    def __init__(self, name, hybrid=None, xc=None, finegrid=False,
                 alpha=None):
        """Mix standard functionals with exact exchange.

        name: str
            Name of hybrid functional.
        hybrid: float
            Fraction of exact exchange.
        xc: str or XCFunctional object
            Standard DFT functional with scaled down exchange.
        finegrid: boolean
            Use fine grid for energy functional evaluations?
        """

        if name == 'EXX':
            assert hybrid is None and xc is None
            hybrid = 1.0
            xc = XC(XCNull())
        elif name == 'PBE0':
            assert hybrid is None and xc is None
            hybrid = 0.25
            xc = XC('HYB_GGA_XC_PBEH')
        elif name == 'B3LYP':
            assert hybrid is None and xc is None
            hybrid = 0.2
            xc = XC('HYB_GGA_XC_B3LYP')
            
        if isinstance(xc, str):
            xc = XC(xc)

        self.hybrid = hybrid
        self.xc = xc
        self.type = xc.type
        self.alpha = alpha
        self.exx = 0.0
        
        XCFunctional.__init__(self, name)

    def get_setup_name(self):
        return 'PBE'

    def calculate_radial(self, rgd, n_sLg, Y_L, v_sg,
                         dndr_sLg=None, rnablaY_Lv=None,
                         tau_sg=None, dedtau_sg=None):
        return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg,
                                        dndr_sLg, rnablaY_Lv)
    
    def initialize(self, density, hamiltonian, wfs, occupations):
        self.xc.initialize(density, hamiltonian, wfs, occupations)
        self.nspins = wfs.nspins
        self.setups = wfs.setups
        self.density = density
        self.kpt_u = wfs.kpt_u
        
        self.gd = density.gd
        self.kd = wfs.kd
        self.bd = wfs.bd

        N_c = self.gd.N_c
        N = self.gd.N_c.prod()
        vol = self.gd.dv * N
        
        if self.alpha is None:
            self.alpha = 6 * vol**(2 / 3.0) / pi**2
            
        self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) *
                      self.kd.nbzkpts)
        ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max()

        if self.kd.N_c is None:
            self.bzk_kc = np.zeros((1, 3))
            dfghdfgh
        else:
            n = self.kd.N_c * 2 - 1
            bzk_kc = np.indices(n).transpose((1, 2, 3, 0))
            bzk_kc.shape = (-1, 3)
            bzk_kc -= self.kd.N_c - 1
            self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c
        
        self.pwd = PWDescriptor(ecut, self.gd, self.bzk_kc)

        n = 0
        for k_c, Gpk2_G in zip(self.bzk_kc[:], self.pwd.G2_qG):
            if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX???
                if k_c.any():
                    self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G),
                                         Gpk2_G**-1)
                else:
                    self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]),
                                         Gpk2_G[1:]**-1)
                n += 1

        assert n == self.kd.N_c.prod()
        
        self.ghat = LFC(self.gd,
                        [setup.ghat_l for setup in density.setups],
                        dtype=complex
                        )
        self.ghat.set_k_points(self.bzk_kc)
        
        self.fullkd = KPointDescriptor(self.kd.bzk_kc, nspins=1)
        class S:
            id_a = []
            def set_symmetry(self, s): pass
            
        self.fullkd.set_symmetry(Atoms(pbc=True), S(), False)
        self.fullkd.set_communicator(world)
        self.pt = LFC(self.gd, [setup.pt_j for setup in density.setups],
                      dtype=complex)
        self.pt.set_k_points(self.fullkd.ibzk_kc)

        self.interpolator = density.interpolator

    def set_positions(self, spos_ac):
        self.ghat.set_positions(spos_ac)
        self.pt.set_positions(spos_ac)
    
    def calculate(self, gd, n_sg, v_sg=None, e_g=None):
        # Normal XC contribution:
        exc = self.xc.calculate(gd, n_sg, v_sg, e_g)

        # Add EXX contribution:
        return exc + self.exx

    def calculate_exx(self):
        """Non-selfconsistent calculation."""

        kd = self.kd
        K = self.fullkd.nibzkpts
        assert self.nspins == 1
        Q = K // world.size
        assert Q * world.size == K
        parallel = (world.size > self.nspins)
        
        self.exx = 0.0
        self.exx_skn = np.zeros((self.nspins, K, self.bd.nbands))

        kpt_u = []
        for k in range(world.rank * Q, (world.rank + 1) * Q):
            k_c = self.fullkd.ibzk_kc[k]
            for k1, k1_c in enumerate(kd.bzk_kc):
                if abs(k1_c - k_c).max() < 1e-10:
                    break
                
            # Index of symmetry related point in the irreducible BZ
            ik = kd.kibz_k[k1]
            kpt = self.kpt_u[ik]

            # KPoint from ground-state calculation
            phase_cd = np.exp(2j * pi * self.gd.sdisp_cd * k_c[:, np.newaxis])
            kpt2 = KPoint0(kpt.weight, kpt.s, k, None, phase_cd)
            kpt2.psit_nG = np.empty_like(kpt.psit_nG)
            kpt2.f_n = kpt.f_n / kpt.weight / K * 2
            for n, psit_G in enumerate(kpt2.psit_nG):
                psit_G[:] = kd.transform_wave_function(kpt.psit_nG[n], k1)

            kpt2.P_ani = self.pt.dict(len(kpt.psit_nG))
            self.pt.integrate(kpt2.psit_nG, kpt2.P_ani, k)
            kpt_u.append(kpt2)

        for s in range(self.nspins):
            kpt1_q = [KPoint(self.fullkd, kpt) for kpt in kpt_u if kpt.s == s]
            kpt2_q = kpt1_q[:]

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

            # Send rank:
            srank = self.fullkd.get_rank_and_index(s, (kpt1_q[0].k - 1) % K)[0]

            # Receive rank:
            rrank = self.fullkd.get_rank_and_index(s, (kpt1_q[-1].k + 1) % K)[0]

            # Shift k-points K // 2 times:
            for i in range(K // 2 + 1):
                if i < K // 2:
                    if parallel:
                        kpt = kpt2_q[-1].next()
                        kpt.start_receiving(rrank)
                        kpt2_q[0].start_sending(srank)
                    else:
                        kpt = kpt2_q[0]

                for kpt1, kpt2 in zip(kpt1_q, kpt2_q):
                    if 2 * i == K:
                        self.apply(kpt1, kpt2, invert=(kpt1.k > kpt2.k))
                    else:
                        self.apply(kpt1, kpt2)
                        self.apply(kpt1, kpt2, invert=True)

                if i < K // 2:
                    if parallel:
                        kpt.wait()
                        kpt2_q[0].wait()
                    kpt2_q.pop(0)
                    kpt2_q.append(kpt)
            
        self.exx = world.sum(self.exx)
        world.sum(self.exx_skn)
        self.exx += self.calculate_paw_correction()
        
    def apply(self, kpt1, kpt2, invert=False):
        #print world.rank,kpt1.k,kpt2.k,invert
        k1_c = self.fullkd.ibzk_kc[kpt1.k]
        k2_c = self.fullkd.ibzk_kc[kpt2.k]
        if invert:
            k2_c = -k2_c
        k12_c = k1_c - k2_c
        N_c = self.gd.N_c
        eikr_R = np.exp(2j * pi * np.dot(np.indices(N_c).T, k12_c / N_c).T)

        for q, k_c in enumerate(self.bzk_kc):
            if abs(k_c + k12_c).max() < 1e-9:
                q0 = q
                break

        for q, k_c in enumerate(self.bzk_kc):
            if abs(k_c - k12_c).max() < 1e-9:
                q00 = q
                break

        Gpk2_G = self.pwd.G2_qG[q0]
        if Gpk2_G[0] == 0:
            Gpk2_G = Gpk2_G.copy()
            Gpk2_G[0] = 1.0 / self.gamma

        N = N_c.prod()
        vol = self.gd.dv * N
        nspins = self.nspins

        same = (kpt1.k == kpt2.k)
        
        for n1, psit1_R in enumerate(kpt1.psit_nG):
            f1 = kpt1.f_n[n1]
            for n2, psit2_R in enumerate(kpt2.psit_nG):
                if same and n2 > n1:
                    continue
                
                f2 = kpt2.f_n[n2]

                nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, q0,
                                                   invert)
                                                   
                nt_G = self.pwd.fft(nt_R * eikr_R) / N
                vt_G = nt_G.copy()
                vt_G *= -pi * vol / Gpk2_G
                e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid
                if same and n1 == n2:
                    e /= 2
                    
                self.exx += e * f1 * f2
                self.ekin -= 2 * e * f1 * f2
                self.exx_skn[kpt1.s, kpt1.k, n1] += f2 * e
                self.exx_skn[kpt2.s, kpt2.k, n2] += f1 * e

                calculate_potential = not True
                if calculate_potential:
                    vt_R = self.pwd.ifft(vt_G).conj() * eikr_R * N / vol
                    if kpt1 is kpt2 and not invert and n1 == n2:
                        kpt1.vt_nG[n1] = 0.5 * f1 * vt_R

                    if invert:
                        kpt1.Htpsit_nG[n1] += \
                                           f2 * nspins * psit2_R.conj() * vt_R
                    else:
                        kpt1.Htpsit_nG[n1] += f2 * nspins * psit2_R * vt_R

                    if kpt1 is not kpt2:
                        if invert:
                            kpt2.Htpsit_nG[n2] += (f1 * nspins *
                                                   psit1_R.conj() * vt_R)
                        else:
                            kpt2.Htpsit_nG[n2] += (f1 * nspins *
                                                   psit1_R * vt_R.conj())

    def calculate_paw_correction(self):
        exx = 0
        deg = 2 // self.nspins  # spin degeneracy
        for a, D_sp in self.density.D_asp.items():
            setup = self.setups[a]
            for D_p in D_sp:
                D_ii = unpack2(D_p)
                ni = len(D_ii)

                for i1 in range(ni):
                    for i2 in range(ni):
                        A = 0.0
                        for i3 in range(ni):
                            p13 = packed_index(i1, i3, ni)
                            for i4 in range(ni):
                                p24 = packed_index(i2, i4, ni)
                                A += setup.M_pp[p13, p24] * D_ii[i3, i4]
                        p12 = packed_index(i1, i2, ni)
                        exx -= self.hybrid / deg * D_ii[i1, i2] * A

                if setup.X_p is not None:
                    exx -= self.hybrid * np.dot(D_p, setup.X_p)
            exx += self.hybrid * setup.ExxC
        return exx
    
    def calculate_pair_density(self, n1, n2, kpt1, kpt2, q, invert):
        if invert:
            nt_G = kpt1.psit_nG[n1].conj() * kpt2.psit_nG[n2].conj()
        else:
            nt_G = kpt1.psit_nG[n1].conj() * kpt2.psit_nG[n2]

        Q_aL = {}
        for a, P1_ni in kpt1.P_ani.items():
            P1_i = P1_ni[n1]
            P2_i = kpt2.P_ani[a][n2]
            if invert:
                D_ii = np.outer(P1_i.conj(), P2_i.conj())
            else:
                D_ii = np.outer(P1_i.conj(), P2_i)
            D_p = pack(D_ii)
            Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL)

        self.ghat.add(nt_G, Q_aL, q)
        return nt_G
Ejemplo n.º 36
0
class RealSpaceDensity(Density):
    def __init__(self, gd, finegd, nspins, charge, collinear=True,
                 stencil=3):
        Density.__init__(self, gd, finegd, nspins, charge, collinear)
        self.stencil = stencil

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

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

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

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

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

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

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

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

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

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

        return out_xR

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

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

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

    def calculate_dipole_moment(self):
        return self.finegd.calculate_dipole_moment(self.rhot_g)
Ejemplo n.º 37
0
class SIC(XCFunctional):

    orbital_dependent = True
    unitary_invariant = False
    
    def __init__(self, xc='LDA', finegrid=False, **parameters):
        
        """Self-Interaction Corrected Functionals (PZ-SIC).

        finegrid: boolean
            Use fine grid for energy functional evaluations?
        """
        
        if isinstance(xc, str):
            xc = XC(xc)
        self.xc = xc
        self.type = xc.type
        XCFunctional.__init__(self, xc.name + '-PZ-SIC')
        self.finegrid = finegrid
        self.parameters = parameters

    
    def initialize(self, density, hamiltonian, wfs, occ=None):
        
        assert wfs.gamma
        assert not wfs.gd.pbc_c.any()

        self.wfs = wfs
        self.dtype = float
        self.xc.initialize(density, hamiltonian, wfs, occ)
        self.kpt_comm = wfs.kpt_comm
        self.nspins = wfs.nspins
        self.nbands = wfs.bd.nbands
        
        if self.finegrid:
            self.finegd = density.finegd
            self.ghat = density.ghat
        else:
            self.finegd = density.gd
            self.ghat = LFC(self.finegd,
                        [setup.ghat_l for setup in density.setups],
                        integral=sqrt(4 * pi), forces=True)
        
        poissonsolver = PoissonSolver(eps=1e-14)
        poissonsolver.set_grid_descriptor(self.finegd)
        poissonsolver.initialize()
        
        self.spin_s = {}
        for kpt in wfs.kpt_u:
            self.spin_s[kpt.s] = SICSpin(kpt, self.xc,
                                         density, hamiltonian, wfs,
                                         poissonsolver, self.ghat,
                                         self.finegd, **self.parameters)
            
    def get_setup_name(self):
        return self.xc.get_setup_name()

    def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None,
                                 addcoredensity=True, a=None):
        return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp,
                                 addcoredensity, a)
    
    def set_positions(self, spos_ac):
        if not self.finegrid:
            self.ghat.set_positions(spos_ac)
    
    def calculate(self, gd, n_sg, v_sg=None, e_g=None):
        
        self.gd = gd
        
        # Normal XC contribution:
        exc = self.xc.calculate(gd, n_sg, v_sg, e_g)

        # SIC:
        self.esic = 0.0
        self.ekin = 0.0
        for spin in self.spin_s.values():
            if spin.kpt.psit_nG is not None:
                desic, dekin = spin.calculate()
                self.esic += desic
                self.ekin += dekin
        self.esic = self.kpt_comm.sum(self.esic)
        self.ekin = self.kpt_comm.sum(self.ekin)
            
        return exc + self.esic

    def apply_orbital_dependent_hamiltonian(self, kpt, psit_nG,
                                            Htpsit_nG=None, dH_asp=None):
        spin = self.spin_s[kpt.s]
        if spin.W_mn is None:
            return
        spin.apply_orbital_dependent_hamiltonian(psit_nG)

    def correct_hamiltonian_matrix(self, kpt, H_nn):
        spin = self.spin_s[kpt.s]
        if spin.W_mn is None:
            return
        spin.correct_hamiltonian_matrix(H_nn)
    
    def add_correction(self, kpt, psit_xG, Htpsit_xG, P_axi, c_axi, n_x,
                       calculate_change=False):
        spin = self.spin_s[kpt.s]
        if spin.W_mn is None:
            return

        if calculate_change:
            spin.calculate_residual_change(psit_xG, Htpsit_xG, P_axi,
                                           c_axi, n_x)
        else:
            spin.calculate_residual(psit_xG, Htpsit_xG, P_axi, c_axi)
        
    def rotate(self, kpt, U_nn):
        self.spin_s[kpt.s].rotate(U_nn)

    def setup_force_corrections(self, F_av):
       self.dF_av = np.zeros_like(F_av)
       for spin in self.spin_s.values():
           spin.add_forces(self.dF_av)
       self.wfs.kpt_comm.sum(self.dF_av)
        
    def add_forces(self, F_av):
       F_av += self.dF_av

    def summary(self, out=sys.stdout):
        for s in range(self.nspins):
            if s in self.spin_s:
                stabpot = self.spin_s[s].stabpot
                spin = self.spin_s[s]
                pos_mv = spin.get_centers()
                exc_m = spin.exc_m
                ecoulomb_m = spin.ecoulomb_m
                if self.kpt_comm.rank == 1 and self.gd.comm.rank == 0:
                    nocc = self.kpt_comm.sum(spin.nocc)
                    self.kpt_comm.send(pos_mv, 0)
                    self.kpt_comm.send(exc_m, 0)
                    self.kpt_comm.send(ecoulomb_m, 0)
            else:
                if self.kpt_comm.rank == 0 and self.gd.comm.rank == 0:
                    nocc = self.kpt_comm.sum(0)
                    pos_mv = np.zeros((nocc, 3))
                    exc_m = np.zeros(nocc)
                    ecoulomb_m = np.zeros(nocc)
                    self.kpt_comm.receive(pos_mv, 1)
                    self.kpt_comm.receive(exc_m, 1)
                    self.kpt_comm.receive(ecoulomb_m, 1)
            if self.kpt_comm.rank == 0 and self.gd.comm.rank == 0:
                out.write('\nSIC orbital centers and energies:\n')
                out.write('                                %5.2fx   %5.2fx\n' %
                          (self.spin_s[0].xc_factor,
                           self.spin_s[0].coulomb_factor))
                out.write('          x       y       z       XC    Coulomb\n')
                out.write('--------------------------------------------------\n')
                m = 0
                for pos_v, exc, ecoulomb in zip(pos_mv, exc_m, ecoulomb_m):
                    out.write('%3d  (%7.3f,%7.3f,%7.3f): %8.3f %8.3f\n' %
                              ((m,) + tuple(pos_v) +
                               (exc * Hartree, ecoulomb * Hartree)))
                    m += 1
                out.write('--------------------------------------------------\n')
        out.write('\nTotal SIC energy     : %12.5f\n' % (self.esic * Hartree))
        out.write('Stabilizing potential: %12.5f\n' % (stabpot * Hartree))
        


    def read(self, reader):
        xc_factor = reader['SIC_xc_factor']
        coulomb_factor = reader['SIC_coulomb_factor']
        #
        for s in range(self.nspins):
            #
            try:
                npart = reader.dimension('npart'+str(s))
            except KeyError:
                npart = 0
            #
            if npart>0:
                W_mn = reader.get('UnitaryTransformation'+str(s))
            else:
                W_mn = None
            #
            if s in self.spin_s.keys():
                self.spin_s[s].initial_W_mn = W_mn
                self.spin_s[s].xc_factor = xc_factor
                self.spin_s[s].coulomb_factor = coulomb_factor
            

    def write(self, writer, natoms=None):
        #
        for s in self.spin_s.keys():
            spin = self.spin_s[s]
            if self.wfs.world.rank==0:
                writer['SIC_xc_factor'] = spin.xc_factor
                writer['SIC_coulomb_factor'] = spin.coulomb_factor
            break
        #
        for s in range(self.nspins):
            #
            W_mn = self.get_unitary_transformation(s)
            #
            if self.wfs.world.rank == 0:
                if W_mn!=None:
                    writer.dimension('npart'+str(s), W_mn.shape[0])
                    writer.add('UnitaryTransformation'+str(s),
                               ('npart'+str(s),'npart'+str(s)),
                               dtype=self.dtype)
                    writer.fill(W_mn)
    

    def get_unitary_transformation(self, s):
        #
        if s in self.spin_s.keys():
            spin = self.spin_s[s]
            #
            if spin.W_mn==None or spin.finegd.rank!=0:
                n = 0
            else:
                n = spin.W_mn.shape[0]
            #
        else:
            n=0
        #
        n = self.wfs.world.sum(n)
        #
        if n>0:
            W_mn = np.zeros((n,n), dtype=self.dtype)
        else:
            W_mn = None
            return W_mn
        #
        if s in self.spin_s.keys():
            spin = self.spin_s[s]
            #
            if spin.W_mn==None or spin.finegd.rank!=0:
                W_mn[:] = 0.0
            else:
                W_mn[:] = spin.W_mn[:]
            #
        else:
            W_mn[:] = 0.0
        #
        self.wfs.world.sum(W_mn)   
        return W_mn
Ejemplo n.º 38
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)
Ejemplo n.º 39
0
class RealSpaceHamiltonian(Hamiltonian):
    def __init__(
        self,
        gd,
        finegd,
        nspins,
        setups,
        timer,
        xc,
        world,
        kptband_comm,
        vext=None,
        collinear=True,
        psolver=None,
        stencil=3,
    ):
        Hamiltonian.__init__(self, gd, finegd, nspins, setups, timer, xc, world, kptband_comm, vext, collinear)

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

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

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

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

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

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

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

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

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

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

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

        self.vt_sg[self.nspins :] = 0.0

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

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

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

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

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

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

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

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

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

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

        dens.ghat.derivative(self.vHt_g, ghat_aLv)
        dens.nct.derivative(vt_G, nct_av)
        self.vbar.derivative(dens.nt_g, vbar_av)
Ejemplo n.º 40
0
class RealSpaceDensity(Density):
    def __init__(self,
                 gd,
                 finegd,
                 nspins,
                 charge,
                 redistributor,
                 stencil=3,
                 background_charge=None):
        Density.__init__(self,
                         gd,
                         finegd,
                         nspins,
                         charge,
                         redistributor,
                         background_charge=background_charge)
        self.stencil = stencil

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

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

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

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

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

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

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

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

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

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

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

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

        return out_xR

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

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

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

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

    def calculate_dipole_moment(self):
        return self.finegd.calculate_dipole_moment(self.rhot_g)
Ejemplo n.º 41
0
    def get_projections(self, locfun, spin=0):
        """Project wave functions onto localized functions

        Determine the projections of the Kohn-Sham eigenstates
        onto specified localized functions of the format::

          locfun = [[spos_c, l, sigma], [...]]

        spos_c can be an atom index, or a scaled position vector. l is
        the angular momentum, and sigma is the (half-) width of the
        radial gaussian.

        Return format is::

          f_kni = <psi_kn | f_i>

        where psi_kn are the wave functions, and f_i are the specified
        localized functions.

        As a special case, locfun can be the string 'projectors', in which
        case the bound state projectors are used as localized functions.
        """

        wfs = self.wfs

        if locfun == 'projectors':
            f_kin = []
            for kpt in wfs.kpt_u:
                if kpt.s == spin:
                    f_in = []
                    for a, P_ni in kpt.P_ani.items():
                        i = 0
                        setup = wfs.setups[a]
                        for l, n in zip(setup.l_j, setup.n_j):
                            if n >= 0:
                                for j in range(i, i + 2 * l + 1):
                                    f_in.append(P_ni[:, j])
                            i += 2 * l + 1
                    f_kin.append(f_in)
            f_kni = np.array(f_kin).transpose(0, 2, 1)
            return f_kni.conj()

        from gpaw.lfc import LocalizedFunctionsCollection as LFC
        from gpaw.spline import Spline
        from gpaw.utilities import _fact

        nkpts = len(wfs.ibzk_kc)
        nbf = np.sum([2 * l + 1 for pos, l, a in locfun])
        f_kni = np.zeros((nkpts, wfs.nbands, nbf), wfs.dtype)

        spos_ac = self.atoms.get_scaled_positions() % 1.0
        spos_xc = []
        splines_x = []
        for spos_c, l, sigma in locfun:
            if isinstance(spos_c, int):
                spos_c = spos_ac[spos_c]
            spos_xc.append(spos_c)
            alpha = .5 * Bohr**2 / sigma**2
            r = np.linspace(0, 6. * sigma, 500)
            f_g = (_fact[l] * (4 * alpha)**(l + 3 / 2.) *
                   np.exp(-alpha * r**2) /
                   (np.sqrt(4 * np.pi) * _fact[2 * l + 1]))
            splines_x.append([Spline(l, rmax=r[-1], f_g=f_g, points=61)])

        lf = LFC(wfs.gd, splines_x, wfs.kpt_comm, dtype=wfs.dtype)
        if not wfs.gamma:
            lf.set_k_points(wfs.ibzk_qc)
        lf.set_positions(spos_xc)

        k = 0
        f_ani = lf.dict(wfs.nbands)
        for kpt in wfs.kpt_u:
            if kpt.s != spin:
                continue
            lf.integrate(kpt.psit_nG[:], f_ani, kpt.q)
            i1 = 0
            for x, f_ni in f_ani.items():
                i2 = i1 + f_ni.shape[1]
                f_kni[k, :, i1:i2] = f_ni
                i1 = i2
            k += 1

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

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

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

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

            D_asp = {}
            for a in self.lcao.density.D_asp:
                ni = self.lcao.density.setups[a].ni
                D_asp[a] = np.zeros((1, ni * (ni + 1) // 2))
            self.lcao.wfs.calculate_atomic_density_matrices_with_occupation(D_asp, f_un)
            Q_aL = {}
            for a, D_sp in D_asp.items():
                Q_aL[a] = np.dot(D_sp.sum(0), self.lcao.density.setups[a].Delta_pL)
            self.ghat.add(n_sG, Q_aL)
            n_sg = self.lcao.wfs.gd.collect(n_sG, broadcast=False)
            if world.rank == 0:
                f = open(self.filename + "." + str(rid) + ".density", "a+")
                # n_sg.astype(np.float32).tofile(f)
                # print "max", np.max(n_sg), np.min(n_sg)
                n_sg.tofile(f)
                f.close()
                s = n_sg.shape
                f = open(self.filename + ".info", "w")
                print(s[0], s[1], s[2], file=f)
                f.close()
Ejemplo n.º 43
0
    def new_get_all_electron_density(self, atoms, gridrefinement=2):
        """Return real all-electron density array."""

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

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

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

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

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

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

        a_W =  np.empty(len(nc.M_W), np.int32)
        W = 0
        for a in nc.atom_indices:
            nw = len(nc.sphere_a[a].M_w)
            a_W[W:W + nw] = a
            W += nw
        scale = 1.0 / self.nspins
        for s, I_a in enumerate(I_sa):
            nc.lfc.ae_core_density_correction(scale, n_sg[s], a_W, I_a)
            nct.lfc.ae_core_density_correction(-scale, n_sg[s], a_W, I_a)
            gd.comm.sum(I_a)
            N_c = gd.N_c
            g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c
            for I, g_c in zip(I_a, g_ac):
                if (g_c >= 0).all() and (g_c < gd.n_c).all():
                    n_sg[s][tuple(g_c)] -= I / gd.dv
        return n_sg, gd
Ejemplo n.º 44
0
class HybridXC(HybridXCBase):
    def __init__(self, name, hybrid=None, xc=None, 
                 finegrid=False, unocc=False):
        """Mix standard functionals with exact exchange.

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

    def set_positions(self, spos_ac):
        if not self.finegrid:
            self.ghat.set_positions(spos_ac)
    
    def calculate(self, gd, n_sg, v_sg=None, e_g=None):
        # Normal XC contribution:
        exc = self.xc.calculate(gd, n_sg, v_sg, e_g)
        self.ekin = self.kpt_comm.sum(self.ekin_s.sum())
        return exc + self.kpt_comm.sum(self.exx_s.sum())

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

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

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

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

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

        exx = 0.0
        ekin = 0.0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return nt_G, rhot_g

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

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

        nocc = self.nocc_s[kpt.s]
        if len(kpt.vt_nG) == nocc:
            U_nn = U_nn[:nocc, :nocc]
        gemm(1.0, kpt.vt_nG.copy(), U_nn, 0.0, kpt.vt_nG)
        for v_ni in kpt.vxx_ani.values():
            gemm(1.0, v_ni.copy(), U_nn, 0.0, v_ni)
        for v_nii in kpt.vxx_anii.values():
            gemm(1.0, v_nii.copy(), U_nn, 0.0, v_nii)
Ejemplo n.º 45
0
Archivo: mgga.py Proyecto: qsnake/gpaw
class MGGA(GGA):
    orbital_dependent = True

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

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

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

    def get_setup_name(self):
        return "PBE"

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

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

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

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

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

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

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

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

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

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

        here the orbital dependent part is calculated::

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

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

        return
Ejemplo n.º 46
0
class UTConstantWavefunctionSetup(UTBandParallelSetup):
    __doc__ = UTBandParallelSetup.__doc__ + """
    The pseudo wavefunctions are constants normalized to their band index."""

    allocated = False
    blocking = None
    async = None
    
    def setUp(self):
        UTBandParallelSetup.setUp(self)
        for virtvar in ['dtype','blocking','async']:
            assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar

        # Create randomized atoms
        self.atoms = create_random_atoms(self.gd)

        # 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)

        # Create atomic projector overlaps
        spos_ac = self.atoms.get_scaled_positions() % 1.0
        self.rank_a = self.gd.get_ranks_from_positions(spos_ac)
        self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups],
                      dtype=self.dtype)
        self.pt.set_positions(spos_ac)

        if memstats:
            # Hack to scramble heap usage into steady-state level
            HEAPSIZE = 25 * 1024**2
            for i in range(100):
                data = np.empty(np.random.uniform(0, HEAPSIZE // 8), float)
                del data
            self.mem_pre = record_memory()
            self.mem_alloc = None
            self.mem_test = None

        # Stuff for pseudo wave functions and projections
        if self.dtype == complex:
            self.gamma = 1j**(1.0/self.nbands)
        else:
            self.gamma = 1.0

        self.psit_nG = None
        self.P_ani = None
        self.Qeff_a = None
        self.Qtotal = None

        self.allocate()

    def tearDown(self):
        UTBandParallelSetup.tearDown(self)
        del self.P_ani, self.psit_nG
        del self.pt, self.setups, self.atoms
        if memstats:
            self.print_memory_summary()
            del self.mem_pre, self.mem_alloc, self.mem_test
        self.allocated = False

    def print_memory_summary(self):
        if not memstats:
            raise RuntimeError('No memory statistics were recorded!')

        if world.rank == 0:
            sys.stdout.write('\n')
            sys.stdout.flush()

        dm_r, dminfo = create_memory_info(MemorySingleton(), self.mem_pre)
        if world.rank == 0:
            print('overhead: %s -> %8.4f MB' % (dminfo, dm_r.sum()/1024**2.))

        dm_r, dminfo = create_memory_info(self.mem_pre, self.mem_alloc)
        if world.rank == 0:
            print('allocate: %s -> %8.4f MB' % (dminfo, dm_r.sum()/1024**2.))

        dm_r, dminfo = create_memory_info(self.mem_alloc, self.mem_test)
        if world.rank == 0:
            print('test-use: %s -> %8.4f MB' % (dminfo, dm_r.sum()/1024**2.))

    def allocate(self):
        """
        Allocate constant wavefunctions and their projections according to::

                          /          \     _____    i*phase*(n-m)
           <psi |psi > = ( 1 + Q      ) * V m*n  * e
               n    n'    \     total/

        """
        if self.allocated:
            raise RuntimeError('Already allocated!')

        self.allocate_wavefunctions()
        self.allocate_projections()

        # XXX DEBUG disables projection contributions
        if False:
            for a,P_ni in self.P_ani.items():
                P_ni[:] = 0.
                self.Qeff_a[a] = 0.
            self.Qtotal = 0.
            self.Z_a[:] = 2**63-1
        # XXX DEBUG

        self.Qtotal = np.empty(1, dtype=float)
        self.Qtotal[:] = np.sum([Qeff for Qeff in self.Qeff_a.values()])
        self.gd.comm.sum(self.Qtotal)

        band_indices = np.arange(self.nbands).astype(self.dtype)
        z = self.gamma**band_indices * band_indices**0.5
        self.S0_nn = (1. + self.Qtotal) * np.outer(z.conj(), z)

        self.allocated = True

        if memstats:
            self.mem_alloc = record_memory()

    def allocate_wavefunctions(self):
        """
        Allocate constant pseudo wavefunctions according to::

             ~    ~        _____    i*phase*(n-m)
           <psi |psi > =  V m*n  * e
               n    n'

        """
        if self.allocated:
            raise RuntimeError('Already allocated!')

        # Fill in wave functions
        gpts_c = self.gd.get_size_of_global_array()
        self.psit_nG = self.gd.empty(self.bd.mynbands, self.dtype)
        for myn, psit_G in enumerate(self.psit_nG):
            n = self.bd.global_index(myn)
            # Fill psit_nG: | psit_n > = exp(i*phase*n) * sqrt(n) / sqrt(V)
            psit_G[:] = self.gamma**n * n**0.5 / (self.gd.dv * gpts_c.prod())**0.5

    def allocate_projections(self):
        """
        Construct dummy projection of pseudo wavefunction according to::

           ___
           \     ~   ~a    a   ~a  ~             1     _____    i*phase*(n-m)
            )  <psi |p > dO   <p |psi > =  +/-  --- * V m*n  * e
           /___    n  i    ii'  i'   n'          Z
            ii'                                   a

        """
        if self.allocated:
            raise RuntimeError('Already allocated!')

        # Fill in projector overlaps
        my_band_indices = self.bd.get_band_indices()
        my_atom_indices = np.argwhere(self.gd.comm.rank == self.rank_a).ravel()

        # Holm-Nielsen check:
        natoms = len(self.atoms)
        assert (self.gd.comm.sum(float(sum(my_atom_indices))) ==
                natoms * (natoms - 1) // 2)

        # Check that LFC agrees with us:
        self.assertEqual(len(my_atom_indices), len(self.pt.my_atom_indices))
        for a1, a2 in zip(my_atom_indices, self.pt.my_atom_indices):
            self.assertEqual(a1, a2)

        self.Qeff_a = {}
        self.P_ani = self.pt.dict(self.bd.mynbands)
        for a in my_atom_indices:
            ni = self.setups[a].ni
            # Fill P_ni: <p_i | psit_n > = beta_i * exp(i*phase*n) * sqrt(n)
            #
            #  |  ____                   |
            #  |  \        *    a        |      1
            #  |   )   beta   dO   beta  |  =  ----
            #  |  /___     i    ij     j |      Z
            #  |    ij                   |       a
            #
            # Substitution by linear transformation: beta_i ->  dO_ij alpha_j,
            # where we start out with some initial non-constant vector:
            alpha_i = np.exp(-np.arange(ni).astype(self.dtype)/ni)
            try:
                # Try Cholesky decomposition dO_ii = L_ii * L_ii^dag
                L_ii = np.linalg.cholesky(self.setups[a].dO_ii)
                alpha_i /= np.vdot(alpha_i, alpha_i)**0.5
                beta_i = np.linalg.solve(L_ii.T.conj(), alpha_i)
            except np.linalg.LinAlgError:
                # Eigenvector decomposition dO_ii = V_ii * W_ii * V_ii^dag
                W_i, V_ii = np.linalg.eigh(self.setups[a].dO_ii)
                alpha_i /= np.abs(np.vdot(alpha_i, 
                                          np.dot(np.diag(W_i), alpha_i)))**0.5
                beta_i = np.linalg.solve(V_ii.T.conj(), alpha_i)

            # Normalize according to plus/minus charge
            beta_i /= self.Z_a[a]**0.5
            self.Qeff_a[a] = np.vdot(beta_i, np.dot(self.setups[a].dO_ii, \
                                                    beta_i)).real
            self.P_ani[a][:] = np.outer(self.gamma**my_band_indices \
                                        * my_band_indices**0.5, beta_i)

    def check_and_plot(self, A_nn, A0_nn, digits, keywords=''):
        # Construct fingerprint of input matrices for comparison
        fingerprint = np.array([md5_array(A_nn, numeric=True),
                                md5_array(A0_nn, numeric=True)])

        # Compare fingerprints across all processors
        fingerprints = np.empty((world.size, 2), np.int64)
        world.all_gather(fingerprint, fingerprints)
        if fingerprints.ptp(0).any():
            raise RuntimeError('Distributed matrices are not identical!')

        # If assertion fails, catch temporarily while plotting, then re-raise
        try:
            self.assertAlmostEqual(np.abs(A_nn-A0_nn).max(), 0, digits)
        except AssertionError:
            if world.rank == 0 and mpl is not None:
                from matplotlib.figure import Figure
                fig = Figure()
                ax = fig.add_axes([0.0, 0.1, 1.0, 0.83])
                ax.set_title(self.__class__.__name__)
                im = ax.imshow(np.abs(A_nn-A0_nn), interpolation='nearest')
                fig.colorbar(im)
                fig.text(0.5, 0.05, 'Keywords: ' + keywords, \
                    horizontalalignment='center', verticalalignment='top')

                from matplotlib.backends.backend_agg import FigureCanvasAgg
                img = 'ut_hsops_%s_%s.png' % (self.__class__.__name__, \
                    '_'.join(keywords.split(',')))
                FigureCanvasAgg(fig).print_figure(img.lower(), dpi=90)
            raise

    def get_optimal_number_of_blocks(self, blocking='fast'):
        """Estimate the optimal number of blocks for band parallelization.

        The number of blocks determines how many parallel send/receive 
        operations are performed, as well as the added memory footprint 
        of the required send/receive buffers.

        ``blocking``  ``nblocks``      Description
        ============  =============    ========================================
        'fast'        ``1``            Heavy on memory, more accurate and fast.
        'light'       ``mynbands``     Light on memory, less accurate and slow.
        'intdiv'      ``...``          First integer divisible value 
        'nonintdiv'   ``...``          Some non-integer divisible cases
        """

        #if self.bd.comm.size == 1:
        #    return 1

        if blocking == 'fast':
            return 1
        elif blocking == 'light':
            return self.bd.mynbands
        elif blocking == 'intdiv':
            # Find first value of nblocks that leads to integer
            # divisible mybands / nblock. This is very like to be 
            # 2 but coded here for the general case
            nblocks = 2
            while self.bd.mynbands % nblocks != 0:
                nblocks +=1
            return nblocks
        elif blocking == 'nonintdiv1':
            # Find first value of nblocks that leads to non-integer
            # divisible mynbands / nblock that is less than M
            nblocks = 2
            M = self.bd.mynbands // nblocks
            while self.bd.mynbands % nblocks < M:
                nblocks += 1
                M = self.bd.mynbands // nblocks
            return nblocks
        elif blocking == 'nonintdiv2':
            # Find first value of nblocks that leads to non-integer
            # divisible mynbands / nblock that is less than M
            nblocks = 2
            M = self.bd.mynbands // nblocks
            while self.bd.mynbands % nblocks > M:
                nblocks += 1
                M = self.mynbands // nblocks
            return nblocks
        else:
            nblocks = blocking
            assert self.bd.mynbands // nblocks > 0
            return nblocks

    # =================================

    def test_contents_wavefunction(self):
        # Integrate diagonal brakets of pseudo wavefunctions
        gpts_c = self.gd.get_size_of_global_array()

        intpsit_myn = self.bd.empty(dtype=self.dtype)
        for myn, psit_G in enumerate(self.psit_nG):
            n = self.bd.global_index(myn)
            intpsit_myn[myn] = np.vdot(psit_G, psit_G) * self.gd.dv
        self.gd.comm.sum(intpsit_myn)

        if memstats:
            self.mem_test = record_memory()

        my_band_indices = self.bd.get_band_indices()
        self.assertAlmostEqual(np.abs(intpsit_myn-my_band_indices).max(), 0, 9)

        intpsit_n = self.bd.collect(intpsit_myn, broadcast=True)
        self.assertAlmostEqual(np.abs(intpsit_n-np.arange(self.nbands)).max(), 0, 9)

    def test_contents_projection(self):
        # Distribute inverse effective charges to everybody in domain
        all_Qeff_a = np.empty(len(self.atoms), dtype=float)
        for a,rank in enumerate(self.rank_a):
            if rank == self.gd.comm.rank:
                Qeff = np.array([self.Qeff_a[a]])
            else:
                Qeff = np.empty(1, dtype=float)
            self.gd.comm.broadcast(Qeff, rank)
            all_Qeff_a[a] = Qeff

        # Check absolute values consistency of inverse effective charges
        self.assertAlmostEqual(np.abs(1./self.Z_a-np.abs(all_Qeff_a)).max(), 0, 9)

        # Check sum of inverse effective charges against total
        self.assertAlmostEqual(all_Qeff_a.sum(), self.Qtotal, 9)

        # Make sure that we all agree on inverse effective charges
        fingerprint = np.array([md5_array(all_Qeff_a, numeric=True)])
        all_fingerprints = np.empty(world.size, fingerprint.dtype)
        world.all_gather(fingerprint, all_fingerprints)
        if all_fingerprints.ptp(0).any():
            raise RuntimeError('Distributed eff. charges are not identical!')

    def test_overlaps_hermitian(self):
        # Set up Hermitian overlap operator:
        S = lambda x: x
        dS = lambda a, P_ni: np.dot(P_ni, self.setups[a].dO_ii)
        nblocks = self.get_optimal_number_of_blocks(self.blocking)
        overlap = MatrixOperator(self.ksl, nblocks, self.async, True)
        S_nn = overlap.calculate_matrix_elements(self.psit_nG, \
            self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n>
        tri2full(S_nn, 'U') # upper to lower...

        if self.bd.comm.rank == 0:
            self.gd.comm.broadcast(S_nn, 0)
        self.bd.comm.broadcast(S_nn, 0)

        if memstats:
            self.mem_test = record_memory()

        self.check_and_plot(S_nn, self.S0_nn, 9, 'overlaps,hermitian')

    def test_overlaps_nonhermitian(self):
        alpha = np.random.normal(size=1).astype(self.dtype)
        if self.dtype == complex:
            alpha += 1j*np.random.normal(size=1)
        world.broadcast(alpha, 0)

        # Set up non-Hermitian overlap operator:
        S = lambda x: alpha*x
        dS = lambda a, P_ni: np.dot(alpha*P_ni, self.setups[a].dO_ii)
        nblocks = self.get_optimal_number_of_blocks(self.blocking)
        overlap = MatrixOperator(self.ksl, nblocks, self.async, False)
        S_nn = overlap.calculate_matrix_elements(self.psit_nG, \
            self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n>

        if self.bd.comm.rank == 0:
            self.gd.comm.broadcast(S_nn, 0)
        self.bd.comm.broadcast(S_nn, 0)

        if memstats:
            self.mem_test = record_memory()

        self.check_and_plot(S_nn, alpha*self.S0_nn, 9, 'overlaps,nonhermitian')

    def test_trivial_cholesky(self):
        # Known starting point of SI_nn = <psit_m|S+alpha*I|psit_n>
        I_nn = np.eye(*self.S0_nn.shape)
        alpha = 1e-3 # shift eigenvalues away from zero
        SI_nn = self.S0_nn + alpha * I_nn

        # Try Cholesky decomposition SI_nn = L_nn * L_nn^dag
        L_nn = np.linalg.cholesky(SI_nn)
        # |psit_n> -> C_nn |psit_n> , C_nn^(-1) = L_nn^dag
        # <psit_m|SI|psit_n> -> <psit_m|C_nn^dag SI C_nn|psit_n> = diag(W_n)
        C_nn = np.linalg.inv(L_nn.T.conj())

        # Set up Hermitian overlap operator:
        S = lambda x: x
        dS = lambda a, P_ni: np.dot(P_ni, self.setups[a].dO_ii)
        nblocks = self.get_optimal_number_of_blocks(self.blocking)
        overlap = MatrixOperator(self.ksl, nblocks, self.async, True)
        self.psit_nG = overlap.matrix_multiply(C_nn.T.copy(), self.psit_nG, self.P_ani)
        D_nn = overlap.calculate_matrix_elements(self.psit_nG, \
            self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n>
        tri2full(D_nn, 'U') # upper to lower...

        if self.bd.comm.rank == 0:
            self.gd.comm.broadcast(D_nn, 0)
        self.bd.comm.broadcast(D_nn, 0)

        if memstats:
            self.mem_test = record_memory()

        # D_nn = C_nn^dag * S_nn * C_nn = I_nn - alpha * C_nn^dag * C_nn
        D0_nn = I_nn - alpha * np.dot(C_nn.T.conj(), C_nn)
        self.check_and_plot(D_nn, D0_nn, 6, 'trivial,cholesky') #XXX precision

    def test_trivial_diagonalize(self):
        # Known starting point of S_nn = <psit_m|S|psit_n>
        S_nn = self.S0_nn

        # Eigenvector decomposition S_nn = V_nn * W_nn * V_nn^dag
        # Utilize the fact that they are analytically known (cf. Maple)
        band_indices = np.arange(self.nbands)
        V_nn = np.eye(self.nbands).astype(self.dtype)
        if self.dtype == complex:
            V_nn[1:,1] = np.conj(self.gamma)**band_indices[1:] * band_indices[1:]**0.5
            V_nn[1,2:] = -self.gamma**band_indices[1:-1] * band_indices[2:]**0.5
        else:
            V_nn[2:,1] = band_indices[2:]**0.5
            V_nn[1,2:] = -band_indices[2:]**0.5

        W_n = np.zeros(self.nbands).astype(self.dtype)
        W_n[1] = (1. + self.Qtotal) * self.nbands * (self.nbands - 1) / 2.

        # Find the inverse basis
        Vinv_nn = np.linalg.inv(V_nn)

        # Test analytical eigenvectors for consistency against analytical S_nn
        D_nn = np.dot(Vinv_nn, np.dot(S_nn, V_nn))
        self.assertAlmostEqual(np.abs(D_nn.diagonal()-W_n).max(), 0, 8)
        self.assertAlmostEqual(np.abs(np.tril(D_nn, -1)).max(), 0, 4)
        self.assertAlmostEqual(np.abs(np.triu(D_nn, 1)).max(), 0, 4)
        del Vinv_nn, D_nn

        # Perform Gram Schmidt orthonormalization for diagonalization
        # |psit_n> -> C_nn |psit_n>, using orthonormalized basis Q_nn
        # <psit_m|S|psit_n> -> <psit_m|C_nn^dag S C_nn|psit_n> = diag(W_n)
        # using S_nn = V_nn * W_nn * V_nn^(-1) = Q_nn * W_nn * Q_nn^dag
        C_nn = V_nn.copy()
        gram_schmidt(C_nn)
        self.assertAlmostEqual(np.abs(np.dot(C_nn.T.conj(), C_nn) \
                                      - np.eye(self.nbands)).max(), 0, 6)

        # Set up Hermitian overlap operator:
        S = lambda x: x
        dS = lambda a, P_ni: np.dot(P_ni, self.setups[a].dO_ii)
        nblocks = self.get_optimal_number_of_blocks(self.blocking)
        overlap = MatrixOperator(self.ksl, nblocks, self.async, True)
        self.psit_nG = overlap.matrix_multiply(C_nn.T.copy(), self.psit_nG, self.P_ani)
        D_nn = overlap.calculate_matrix_elements(self.psit_nG, \
            self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n>
        tri2full(D_nn, 'U') # upper to lower...

        if self.bd.comm.rank == 0:
            self.gd.comm.broadcast(D_nn, 0)
        self.bd.comm.broadcast(D_nn, 0)

        if memstats:
            self.mem_test = record_memory()

        # D_nn = C_nn^dag * S_nn * C_nn = W_n since Q_nn^dag = Q_nn^(-1)
        D0_nn = np.dot(C_nn.T.conj(), np.dot(S_nn, C_nn))
        self.assertAlmostEqual(np.abs(D0_nn-np.diag(W_n)).max(), 0, 9)
        self.check_and_plot(D_nn, D0_nn, 9, 'trivial,diagonalize')

    def test_multiply_randomized(self):
        # Known starting point of S_nn = <psit_m|S|psit_n>
        S_nn = self.S0_nn

        if self.dtype == complex:
            C_nn = np.random.uniform(size=self.nbands**2) * \
                np.exp(1j*np.random.uniform(0,2*np.pi,size=self.nbands**2))
        else:
            C_nn = np.random.normal(size=self.nbands**2)
        C_nn = C_nn.reshape((self.nbands,self.nbands)) / np.linalg.norm(C_nn,2)
        world.broadcast(C_nn, 0)

        # Set up Hermitian overlap operator:
        S = lambda x: x
        dS = lambda a, P_ni: np.dot(P_ni, self.setups[a].dO_ii)
        nblocks = self.get_optimal_number_of_blocks(self.blocking)
        overlap = MatrixOperator(self.ksl, nblocks, self.async, True)
        self.psit_nG = overlap.matrix_multiply(C_nn.T.copy(), self.psit_nG, self.P_ani)
        D_nn = overlap.calculate_matrix_elements(self.psit_nG, \
            self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n>
        tri2full(D_nn, 'U') # upper to lower...

        if self.bd.comm.rank == 0:
            self.gd.comm.broadcast(D_nn, 0)
        self.bd.comm.broadcast(D_nn, 0)

        if memstats:
            self.mem_test = record_memory()

        # D_nn = C_nn^dag * S_nn * C_nn
        D0_nn = np.dot(C_nn.T.conj(), np.dot(S_nn, C_nn))
        self.check_and_plot(D_nn, D0_nn, 9, 'multiply,randomized')

    def test_multiply_nonhermitian(self):
        alpha = np.random.normal(size=1).astype(self.dtype)
        if self.dtype == complex:
            alpha += 1j*np.random.normal(size=1)
        world.broadcast(alpha, 0)

        # Known starting point of S_nn = <psit_m|S|psit_n>
        S_nn = alpha*self.S0_nn

        if self.dtype == complex:
            C_nn = np.random.uniform(size=self.nbands**2) * \
                np.exp(1j*np.random.uniform(0,2*np.pi,size=self.nbands**2))
        else:
            C_nn = np.random.normal(size=self.nbands**2)
        C_nn = C_nn.reshape((self.nbands,self.nbands)) / np.linalg.norm(C_nn,2)
        world.broadcast(C_nn, 0)

        # Set up non-Hermitian overlap operator:
        S = lambda x: alpha*x
        dS = lambda a, P_ni: np.dot(alpha*P_ni, self.setups[a].dO_ii)
        nblocks = self.get_optimal_number_of_blocks(self.blocking)
        overlap = MatrixOperator(self.ksl, nblocks, self.async, False)
        self.psit_nG = overlap.matrix_multiply(C_nn.T.copy(), self.psit_nG, self.P_ani)
        D_nn = overlap.calculate_matrix_elements(self.psit_nG, \
            self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n>

        if self.bd.comm.rank == 0:
            self.gd.comm.broadcast(D_nn, 0)
        self.bd.comm.broadcast(D_nn, 0)

        if memstats:
            self.mem_test = record_memory()

        # D_nn = C_nn^dag * S_nn * C_nn
        D0_nn = np.dot(C_nn.T.conj(), np.dot(S_nn, C_nn))
        self.check_and_plot(D_nn, D0_nn, 9, 'multiply,nonhermitian')
Ejemplo n.º 47
0
    def get_all_electron_density(self,
                                 atoms=None,
                                 gridrefinement=2,
                                 spos_ac=None,
                                 skip_core=False):
        """Return real all-electron density array.

           Usage: Either get_all_electron_density(atoms) or
                         get_all_electron_density(spos_ac=spos_ac)

           skip_core=True theoretically returns the
                          all-electron valence density (use with
                          care; will not in general integrate
                          to valence)
        """
        if spos_ac is None:
            spos_ac = atoms.get_scaled_positions() % 1.0

        # Refinement of coarse grid, for representation of the AE-density
        # XXXXXXXXXXXX think about distribution depending on gridrefinement!
        if gridrefinement == 1:
            gd = self.redistributor.aux_gd
            n_sg = self.nt_sG.copy()
            # This will get the density with the same distribution
            # as finegd:
            n_sg = self.redistributor.distribute(n_sg)
        elif gridrefinement == 2:
            gd = self.finegd
            if self.nt_sg is None:
                self.interpolate_pseudo_density()
            n_sg = self.nt_sg.copy()
        elif gridrefinement == 4:
            # Extra fine grid
            gd = self.finegd.refine()

            # Interpolation function for the density:
            interpolator = Transformer(self.finegd, gd, 3)  # XXX grids!

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

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

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

        I_sa = np.zeros((self.nspins, len(spos_ac)))
        a_W = np.empty(len(phi.M_W), np.intc)
        W = 0
        for a in phi.atom_indices:
            nw = len(phi.sphere_a[a].M_w)
            a_W[W:W + nw] = a
            W += nw

        x_W = phi.create_displacement_arrays()[0]
        D_asp = self.D_asp  # XXX really?

        rho_MM = np.zeros((phi.Mmax, phi.Mmax))
        for s, I_a in enumerate(I_sa):
            M1 = 0
            for a, setup in enumerate(self.setups):
                ni = setup.ni
                D_sp = D_asp.get(a)
                if D_sp is None:
                    D_sp = np.empty((self.nspins, ni * (ni + 1) // 2))
                else:
                    I_a[a] = (
                        (setup.Nct) / self.nspins -
                        sqrt(4 * pi) * np.dot(D_sp[s], setup.Delta_pL[:, 0]))

                    if not skip_core:
                        I_a[a] -= setup.Nc / self.nspins

                if gd.comm.size > 1:
                    gd.comm.broadcast(D_sp, D_asp.partition.rank_a[a])
                M2 = M1 + ni
                rho_MM[M1:M2, M1:M2] = unpack2(D_sp[s])
                M1 = M2

            assert np.all(n_sg[s].shape == phi.gd.n_c)
            phi.lfc.ae_valence_density_correction(rho_MM, n_sg[s], a_W, I_a,
                                                  x_W)
            phit.lfc.ae_valence_density_correction(-rho_MM, n_sg[s], a_W, I_a,
                                                   x_W)

        a_W = np.empty(len(nc.M_W), np.intc)
        W = 0
        for a in nc.atom_indices:
            nw = len(nc.sphere_a[a].M_w)
            a_W[W:W + nw] = a
            W += nw
        scale = 1.0 / self.nspins

        for s, I_a in enumerate(I_sa):

            if not skip_core:
                nc.lfc.ae_core_density_correction(scale, n_sg[s], a_W, I_a)

            nct.lfc.ae_core_density_correction(-scale, n_sg[s], a_W, I_a)
            gd.comm.sum(I_a)
            N_c = gd.N_c
            g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c

            if not skip_core:

                for I, g_c in zip(I_a, g_ac):
                    if (g_c >= 0).all() and (g_c < gd.n_c).all():
                        n_sg[s][tuple(g_c)] -= I / gd.dv

        return n_sg, gd
Ejemplo n.º 48
0
class HybridXC(XCFunctional):
    orbital_dependent = True

    def __init__(
        self,
        name,
        hybrid=None,
        xc=None,
        finegrid=False,
        alpha=None,
        skip_gamma=False,
        gygi=False,
        acdf=True,
        qsym=True,
        txt=None,
        ecut=None,
    ):
        """Mix standard functionals with exact exchange.

        name: str
            Name of hybrid functional.
        hybrid: float
            Fraction of exact exchange.
        xc: str or XCFunctional object
            Standard DFT functional with scaled down exchange.
        finegrid: boolean
            Use fine grid for energy functional evaluations?
        """

        if name == "EXX":
            assert hybrid is None and xc is None
            hybrid = 1.0
            xc = XC(XCNull())
        elif name == "PBE0":
            assert hybrid is None and xc is None
            hybrid = 0.25
            xc = XC("HYB_GGA_XC_PBEH")
        elif name == "B3LYP":
            assert hybrid is None and xc is None
            hybrid = 0.2
            xc = XC("HYB_GGA_XC_B3LYP")

        if isinstance(xc, str):
            xc = XC(xc)

        self.hybrid = hybrid
        self.xc = xc
        self.type = xc.type
        self.alpha = alpha
        self.qsym = qsym
        self.skip_gamma = skip_gamma
        self.gygi = gygi
        self.acdf = acdf
        self.exx = None
        self.ecut = ecut
        if txt is None:
            if rank == 0:
                # self.txt = devnull
                self.txt = sys.stdout
            else:
                sys.stdout = devnull
                self.txt = devnull
        else:
            assert type(txt) is str
            from ase.parallel import paropen

            self.txt = paropen(txt, "w")

        XCFunctional.__init__(self, name)

    def get_setup_name(self):
        return "PBE"

    def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None):
        return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv)

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

    def initialize(self, density, hamiltonian, wfs, occupations):
        self.xc.initialize(density, hamiltonian, wfs, occupations)
        self.nspins = wfs.nspins
        self.setups = wfs.setups
        self.density = density
        self.kpt_u = wfs.kpt_u
        self.wfs = wfs

        self.gd = density.gd
        self.kd = wfs.kd
        self.bd = wfs.bd

        N_c = self.gd.N_c
        N = self.gd.N_c.prod()
        vol = self.gd.dv * N

        if self.alpha is None:
            # XXX ?
            self.alpha = 6 * vol ** (2 / 3.0) / pi ** 2

        self.gamma = vol / (2 * pi) ** 2 * sqrt(pi / self.alpha) * self.kd.nbzkpts

        if self.ecut is None:
            self.ecut = 0.5 * pi ** 2 / (self.gd.h_cv ** 2).sum(1).max() * 0.9999

        assert self.kd.N_c is not None
        n = self.kd.N_c * 2 - 1
        bzk_kc = np.indices(n).transpose((1, 2, 3, 0))
        bzk_kc.shape = (-1, 3)
        bzk_kc -= self.kd.N_c - 1
        self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c

        self.bzq_qc = self.kd.get_bz_q_points()
        if self.qsym:
            op_scc = self.kd.symmetry.op_scc
            self.ibzq_qc = self.kd.get_ibz_q_points(self.bzq_qc, op_scc)[0]
            self.q_weights = self.kd.q_weights * len(self.bzq_qc)
        else:
            self.ibzq_qc = self.bzq_qc
            self.q_weights = np.ones(len(self.bzq_qc))

        self.pwd = PWDescriptor(self.ecut, self.gd, complex)
        self.G2_qG = self.pwd.g2(self.bzk_kc)

        n = 0
        for k_c, Gpk2_G in zip(self.bzk_kc[:], self.G2_qG):
            if (k_c > -0.5).all() and (k_c <= 0.5).all():  # XXX???
                if k_c.any():
                    self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G ** -1)
                else:
                    self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:] ** -1)
                n += 1

        assert n == self.kd.N_c.prod()

        self.pwd = PWDescriptor(self.ecut, self.gd, complex)
        self.G2_qG = self.pwd.g2(self.ibzq_qc)

        self.ghat = LFC(
            self.gd, [setup.ghat_l for setup in density.setups], KPointDescriptor(self.bzq_qc), dtype=complex
        )

        # self.interpolator = density.interpolator
        self.print_initialization(hamiltonian.xc.name)

    def set_positions(self, spos_ac):
        self.ghat.set_positions(spos_ac)
        self.spos_ac = spos_ac

    def calculate(self, gd, n_sg, v_sg=None, e_g=None):
        # Normal XC contribution:
        exc = self.xc.calculate(gd, n_sg, v_sg, e_g)

        # Add EXX contribution:
        return exc + self.exx

    def calculate_exx(self):
        """Non-selfconsistent calculation."""
        kd = self.kd
        K = len(kd.bzk_kc)
        W = world.size // self.nspins
        parallel = W > 1

        self.exx = 0.0
        self.exx_kq = np.zeros((K, len(self.ibzq_qc)), float)

        for s in range(self.nspins):
            ibz_kpts = [KPoint(kd, kpt) for kpt in self.kpt_u if kpt.s == s]
            for ik, kpt in enumerate(kd.bzk_kc):
                print >>self.txt, "K %s %s ..." % (ik, kpt)
                for iq, q in enumerate(self.ibzq_qc):
                    kpq = kd.find_k_plus_q(q, kpts_k=[ik])
                    self.apply(ibz_kpts[kd.bz2ibz_k[ik]], ibz_kpts[kd.bz2ibz_k[kpq[0]]], ik, kpq[0], iq)

        self.exx = world.sum(self.exx)
        self.exx += self.calculate_exx_paw_correction()

        exx_q = np.sum(self.exx_kq, 0)

        print >>self.txt
        print >>self.txt, "------------------------------------------------------"
        print >>self.txt
        print >>self.txt, "Contributions: q         w        E_q (eV)"
        for q in range(len(exx_q)):
            print >>self.txt, "[%1.3f %1.3f %1.3f]    %1.3f   %s" % (
                self.ibzq_qc[q][0],
                self.ibzq_qc[q][1],
                self.ibzq_qc[q][2],
                self.q_weights[q] / len(self.bzq_qc),
                exx_q[q] / self.q_weights[q] * len(self.bzq_qc) * Ha,
            )
        print >>self.txt, "E_EXX = %s eV" % (self.exx * Ha)
        print >>self.txt
        print >>self.txt, "Calculation completed at:  ", ctime()
        print >>self.txt
        print >>self.txt, "------------------------------------------------------"
        print >>self.txt

    def apply(self, kpt1, kpt2, ik1, ik2, iq):
        k1_c = self.kd.bzk_kc[ik1]
        k2_c = self.kd.bzk_kc[ik2]
        q = self.ibzq_qc[iq]
        if self.qsym:
            for i, q in enumerate(self.bzq_qc):
                if abs(q - self.ibzq_qc[iq]).max() < 1e-9:
                    bzq_index = i
                    break
        else:
            bzq_index = iq

        N_c = self.gd.N_c
        eikr_R = np.exp(-2j * pi * np.dot(np.indices(N_c).T, q / N_c).T)

        Gamma = abs(q).max() < 1e-9
        if Gamma and self.skip_gamma:
            return

        Gpk2_G = self.G2_qG[iq]
        if Gamma:
            Gpk2_G = Gpk2_G.copy()
            Gpk2_G[0] = 1.0 / self.gamma

        N = N_c.prod()
        vol = self.gd.dv * N
        nspins = self.nspins

        fcut = 1e-10
        for n1, psit1_R in enumerate(kpt1.psit_nG):
            f1 = kpt1.f_n[n1]
            for n2, psit2_R in enumerate(kpt2.psit_nG):
                if self.acdf:
                    if self.gygi and Gamma:
                        # print n2, kpt2.f_n[n2]/kpt2.weight
                        f2 = self.q_weights[iq] * kpt2.weight
                    else:
                        f2 = self.q_weights[iq] * kpt2.weight * (1 - np.sign(kpt2.eps_n[n2] - kpt1.eps_n[n1]))

                else:
                    f2 = kpt2.f_n[n2] * self.q_weights[iq]
                if abs(f1) < fcut or abs(f2) < fcut:
                    continue
                nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, ik1, ik2, bzq_index)
                nt_G = self.pwd.fft(nt_R * eikr_R) / N
                vt_G = nt_G.copy()
                vt_G *= -pi * vol / Gpk2_G
                e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid
                self.exx += f1 * f2 * e
                self.exx_kq[ik1, iq] += f1 * f2 * e

    def calculate_pair_density(self, n1, n2, kpt1, kpt2, ik1, ik2, bzq_index):
        psit1_G = self.kd.transform_wave_function(kpt1.psit_nG[n1], ik1)
        psit2_G = self.kd.transform_wave_function(kpt2.psit_nG[n2], ik2)
        nt_G = psit1_G.conj() * psit2_G

        s1 = self.kd.sym_k[ik1]
        s2 = self.kd.sym_k[ik2]
        t1 = self.kd.time_reversal_k[ik1]
        t2 = self.kd.time_reversal_k[ik2]
        k1_c = self.kd.ibzk_kc[kpt1.k]
        k2_c = self.kd.ibzk_kc[kpt2.k]

        Q_aL = {}
        for a in kpt1.P_ani.keys():
            b1 = self.kd.symmetry.a_sa[s1, a]
            b2 = self.kd.symmetry.a_sa[s2, a]
            S1_c = np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s1]) - self.spos_ac[b1]
            S2_c = np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s2]) - self.spos_ac[b2]
            assert abs(S1_c.round() - S1_c).max() < 1e-13
            assert abs(S2_c.round() - S2_c).max() < 1e-13
            x1 = np.exp(2j * pi * np.dot(k1_c, S1_c))
            x2 = np.exp(2j * pi * np.dot(k2_c, S2_c))
            P1_i = np.dot(self.setups[a].R_sii[s1], kpt1.P_ani[b1][n1]) * x1
            P2_i = np.dot(self.setups[a].R_sii[s2], kpt2.P_ani[b2][n2]) * x2
            if t1:
                P1_i = P1_i.conj()
            if t2:
                P2_i = P2_i.conj()

            D_ii = np.outer(P1_i.conj(), P2_i)
            D_p = pack(D_ii)
            Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL)

        self.ghat.add(nt_G, Q_aL, bzq_index)
        return nt_G

    def calculate_exx_paw_correction(self):
        exx = 0
        deg = 2 // self.nspins  # spin degeneracy
        for a, D_sp in self.density.D_asp.items():
            setup = self.setups[a]
            for D_p in D_sp:
                D_ii = unpack2(D_p)
                ni = len(D_ii)

                for i1 in range(ni):
                    for i2 in range(ni):
                        A = 0.0
                        for i3 in range(ni):
                            p13 = packed_index(i1, i3, ni)
                            for i4 in range(ni):
                                p24 = packed_index(i2, i4, ni)
                                A += setup.M_pp[p13, p24] * D_ii[i3, i4]
                        p12 = packed_index(i1, i2, ni)
                        exx -= self.hybrid / deg * D_ii[i1, i2] * A

                if setup.X_p is not None:
                    exx -= self.hybrid * np.dot(D_p, setup.X_p)
            exx += self.hybrid * setup.ExxC
        return exx

    def print_initialization(self, xc):
        print >>self.txt, "------------------------------------------------------"
        print >>self.txt, "Non-self-consistent HF correlation energy"
        print >>self.txt, "------------------------------------------------------"
        print >>self.txt, "Started at:  ", ctime()
        print >>self.txt
        print >>self.txt, "Ground state XC functional     :   %s" % xc
        print >>self.txt, "Valence electrons              :   %s" % self.setups.nvalence
        print >>self.txt, "Number of Spins                :   %s" % self.nspins
        print >>self.txt, "Plane wave cutoff energy       :   %4.1f eV" % (self.ecut * Ha)
        print >>self.txt, "Gamma q-point excluded         :   %s" % self.skip_gamma
        if not self.skip_gamma:
            print >>self.txt, "Alpha parameter                :   %s" % self.alpha
            print >>self.txt, "Gamma parameter                :   %3.3f" % self.gamma
        print >>self.txt, "ACDF method                    :   %s" % self.acdf
        print >>self.txt, "Number of k-points             :   %s" % len(self.kd.bzk_kc)
        print >>self.txt, "Number of Irreducible k-points :   %s" % len(self.kd.ibzk_kc)
        print >>self.txt, "Number of q-points             :   %s" % len(self.bzq_qc)
        if not self.qsym:
            print >>self.txt, "q-point symmetry               :   %s" % self.qsym
        else:
            print >>self.txt, "Number of Irreducible q-points :   %s" % len(self.ibzq_qc)

        print >>self.txt
        for q, weight in zip(self.ibzq_qc, self.q_weights):
            print >>self.txt, "q: [%1.3f %1.3f %1.3f] - weight: %1.3f" % (q[0], q[1], q[2], weight / len(self.bzq_qc))
        print >>self.txt
        print >>self.txt, "------------------------------------------------------"
        print >>self.txt, "------------------------------------------------------"
        print >>self.txt
        print >>self.txt, "Looping over k-points in the full Brillouin zone"
        print >>self.txt
Ejemplo n.º 49
0
 def get_pseudo_core_kinetic_energy_density_lfc(self):
     return LFC(self.gd, [[setup.tauct] for setup in self.setups],
                forces=True,
                cut=True)
Ejemplo n.º 50
0
class Hamiltonian:
    """Hamiltonian object.

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

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

    Energy contributions and forces:

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

    """

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

        self.dH_asp = None

        # The external potential
        self.vext_g = vext_g

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

        self.rank_a = None

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

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

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

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

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

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

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

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

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

        self.rank_a = rank_a

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

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

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

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

        self.timer.start('Hamiltonian')

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

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

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

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

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

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

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

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

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

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

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

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

            dH_sp += dH_p

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

        self.timer.stop('Atomic')

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

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

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

        self.timer.stop('Hamiltonian')

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

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

        return self.Etot

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

        XXX Parameter description is deprecated!
        
        Parameters:

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

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

        Parameters:

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

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

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

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

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

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

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

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

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

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

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

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

    def estimate_memory(self, mem):
        nbytes = self.gd.bytecount()
        nfinebytes = self.finegd.bytecount()
        arrays = mem.subnode('Arrays', 0)
        arrays.subnode('vHt_g', nfinebytes)
        arrays.subnode('vt_sG', self.nspins * nbytes)
        arrays.subnode('vt_sg', self.nspins * nfinebytes)
        self.restrictor.estimate_memory(mem.subnode('Restrictor'))
        self.xc.estimate_memory(mem.subnode('XC'))
        self.poisson.estimate_memory(mem.subnode('Poisson'))
        self.vbar.estimate_memory(mem.subnode('vbar'))