def extract_basis_functions(self, basis_name='atompaw.sz'):
        """Create BasisFunctions object with pseudo wave functions."""
        from gpaw.basis_data import Basis, BasisFunction
        assert self.wfs.nspins == 1

        basis = Basis(self.symbol, basis_name, readxml=False)
        basis.d = self.wfs.gd.r_g[0]
        basis.ng = self.wfs.gd.N + 1
        basis.generatorattrs = {} # attrs of the setup maybe
        basis.generatordata = 'AtomPAW' # version info too?

        bf_j = basis.bf_j
        for l, n, f, eps, psit_G in self.state_iter():
            phit_g = np.empty(basis.ng)
            phit_g[0] = 0.0
            phit_g[1:] = psit_G
            phit_g *= np.sign(psit_G[-1])

            # If there's no node at zero, we shouldn't set phit_g to zero
            # We'll make an ugly hack
            if abs(phit_g[1]) > 3.0 * abs(phit_g[2] - phit_g[1]):
                phit_g[0] = phit_g[1]
            bf = BasisFunction(l, self.wfs.gd.r_g[-1], phit_g,
                               '%s%d e=%.3f f=%.3f' % ('spdfgh'[l], n, eps, f))
            bf_j.append(bf)
        return basis
Exemple #2
0
    def extract_basis_functions(self, basis_name='atompaw.sz'):
        """Create BasisFunctions object with pseudo wave functions."""
        from gpaw.basis_data import Basis, BasisFunction
        assert self.wfs.nspins == 1

        d = self.wfs.gd.r_g[0]
        ng = self.wfs.gd.N + 1
        rgd = EquidistantRadialGridDescriptor(d, ng)
        basis = Basis(self.symbol, basis_name, readxml=False, rgd=rgd)
        basis.generatorattrs = {}  # attrs of the setup maybe
        basis.generatordata = 'AtomPAW'  # version info too?

        bf_j = basis.bf_j
        for l, n, f, eps, psit_G in self.state_iter():
            phit_g = rgd.empty()
            phit_g[0] = 0.0
            phit_g[1:] = psit_G
            phit_g *= np.sign(psit_G[-1])

            # If there's no node at zero, we shouldn't set phit_g to zero
            # We'll make an ugly hack
            if abs(phit_g[1]) > 3.0 * abs(phit_g[2] - phit_g[1]):
                phit_g[0] = phit_g[1]
            bf = BasisFunction(n, l, self.wfs.gd.r_g[-1], phit_g,
                               '%s%d e=%.3f f=%.3f' % ('spdfgh'[l], n, eps, f))
            bf_j.append(bf)
        return basis
Exemple #3
0
def main():
    parser = build_parser()
    opts, files = parser.parse_args()

    import pylab
    from gpaw.basis_data import Basis, BasisPlotter

    plotter = BasisPlotter(premultiply=not opts.literal,
                           normalize=opts.normalize,
                           show=False,
                           save=opts.save,
                           ext=opts.ext)

    for path in files:
        dir, filename = os.path.split(path)
        splitfilename = filename.split('.')
        symbol = splitfilename[0]
        extension = splitfilename[-1]
        name = '.'.join(splitfilename[1:-1])
        if opts.actual_filenames:
            basis = Basis(symbol, name, False)
            basis.read_xml(path)
        else: # Search GPAW setup dirs
            basis = Basis(symbol, name)        
        plotter.plot(basis)

    if not opts.save:
        pylab.show()
Exemple #4
0
 def build(self, basis):
     if basis is None:
         basis = Basis('Si', 'sz(dzp)')
     elif isinstance(basis, str):
         basis = Basis('Si', basis)
     self.basis = basis
     self.phit_j = self.basis.tosplines()
     self.niAO = self.basis.nao
 def __init__(self, symbol, l_j):
     rgd = EquidistantRadialGridDescriptor(0.02, 160)
     Basis.__init__(self, symbol, 'simple', readxml=False, rgd=rgd)
     self.generatordata = 'simple'
     bf_j = self.bf_j
     rcgauss = rgd.r_g[-1] / 3.0
     gauss_g = np.exp(-(rgd.r_g / rcgauss)**2.0)
     for l in l_j:
         phit_g = rgd.r_g**l * gauss_g
         norm = (rgd.integrate(phit_g**2) / (4 * np.pi))**0.5
         phit_g /= norm
         bf = BasisFunction(None, l, rgd.r_g[-1], phit_g, 'gaussian')
         bf_j.append(bf)
 def __init__(self, symbol, l_j):
     Basis.__init__(self, symbol, 'simple', readxml=False)
     self.generatordata = 'simple'
     self.d = 0.02
     self.ng = 160
     rgd = self.get_grid_descriptor()
     bf_j = self.bf_j
     rcgauss = rgd.r_g[-1] / 3.0
     gauss_g = np.exp(-(rgd.r_g / rcgauss)**2.0)
     for l in l_j:
         phit_g = rgd.r_g**l * gauss_g
         norm = (rgd.integrate(phit_g**2) / (4 * np.pi))**0.5
         phit_g /= norm
         bf = BasisFunction(l, rgd.r_g[-1], phit_g, 'gaussian')
         bf_j.append(bf)
Exemple #7
0
 def __init__(self, symbol, l_j):
     Basis.__init__(self, symbol, 'simple', readxml=False)
     self.generatordata = 'simple'
     self.d = 0.02
     self.ng = 160
     rgd = self.get_grid_descriptor()
     bf_j = self.bf_j
     rcgauss = rgd.rcut / 3.0
     gauss_g = np.exp(-(rgd.r_g / rcgauss)**2.0)
     for l in l_j:
         phit_g = rgd.r_g**l * gauss_g
         norm = np.dot((rgd.r_g * phit_g)**2, rgd.dr_g)**.5
         phit_g /= norm
         bf = BasisFunction(l, rgd.rcut, phit_g, 'gaussian')
         bf_j.append(bf)
Exemple #8
0
 def __init__(self, symbol, l_j):
     Basis.__init__(self, symbol, 'simple', readxml=False)
     self.generatordata = 'simple'
     self.d = 0.02
     self.ng = 160
     rgd = self.get_grid_descriptor()
     bf_j = self.bf_j
     rcgauss = rgd.r_g[-1] / 3.0
     gauss_g = np.exp(-(rgd.r_g / rcgauss)**2.0)
     for l in l_j:
         phit_g = rgd.r_g**l * gauss_g
         norm = (rgd.integrate(phit_g**2) / (4 * np.pi))**0.5
         phit_g /= norm
         bf = BasisFunction(l, rgd.r_g[-1], phit_g, 'gaussian')
         bf_j.append(bf)
Exemple #9
0
def basis_subset(symbol, largebasis, smallbasis):
    """Title.

    Determine which basis function indices from ``largebasis`` are also
    present in smallbasis.
    """
    blarge = Basis(symbol, largebasis)
    zeta_large, pol_large = zeta_pol(blarge)
    
    bsmall = Basis(symbol, smallbasis)
    zeta_small, pol_small = zeta_pol(bsmall)

    assert zeta_small <= zeta_large
    assert pol_small <= pol_large

    insmall = np.zeros(blarge.nao, bool)
    insmall[:zeta_small] = True
    insmall[zeta_large:zeta_large + pol_small] = True
    return insmall
Exemple #10
0
def get_bfi2(symbols, basis, a_list):
    """Same as get_bfi, but does not require an LCAO calc"""
    basis = types2atomtypes(symbols, basis, default='dzp')
    bfs_list = []
    i = 0
    for a, symbol in enumerate(symbols):
        nao = Basis(symbol, basis[a]).nao
        if a in a_list:
            bfs_list += range(i, i + nao)
        i += nao
    return bfs_list
Exemple #11
0
def main():
    parser = build_parser()
    opts, files = parser.parse_args()

    import pylab
    from gpaw.basis_data import Basis, BasisPlotter

    plotter = BasisPlotter(premultiply=not opts.literal,
                           normalize=opts.normalize,
                           show=False,
                           save=opts.save,
                           ext=opts.ext)

    for path in files:
        dir, filename = os.path.split(path)
        splitfilename = filename.split('.')
        symbol = splitfilename[0]
        name = '.'.join(splitfilename[1:-1])
        if opts.actual_filenames:
            basis = Basis(symbol, name, False)
            basis.read_xml(path)
        else:  # Search GPAW setup dirs
            basis = Basis(symbol, name)
        plotter.plot(basis)

    if not opts.save:
        pylab.show()
Exemple #12
0
    def get_stored_basis_functions(self, ):
        states = self.data['states']
        maxlen = max([len(state.values) for state in states])
        orig_r = self.data['r']
        rcut = min(orig_r[maxlen - 1], 12.0)  # XXX hardcoded 12 max radius

        d = 0.02
        ng = int(1 + rcut / d)
        rgd = EquidistantRadialGridDescriptor(d, ng)

        b = Basis(self.symbol, 'upf', readxml=False, rgd=rgd)
        b.generatordata = 'upf-pregenerated'

        for j, state in enumerate(states):
            val = state.values
            phit_g = np.interp(rgd.r_g, orig_r, val)
            phit_g = divrl(phit_g, 1, rgd.r_g)
            icut = len(phit_g) - 1  # XXX correct or off-by-one?
            rcut = rgd.r_g[icut]
            bf = BasisFunction(None, state.l, rcut, phit_g, 'pregenerated')
            b.bf_j.append(bf)
        return b
Exemple #13
0
 def get_stored_basis_functions(self, ):
     b = Basis(self.symbol, 'upf', readxml=False)
     b.generatordata = 'upf-pregenerated'
     
     states = self.data['states']
     maxlen = max([len(state.values) for state in states])
     orig_r = self.data['r']
     rcut = min(orig_r[maxlen - 1], 12.0) # XXX hardcoded 12 max radius
     
     b.d = 0.02
     b.ng = int(1 + rcut / b.d)
     rgd = b.get_grid_descriptor()
     
     for j, state in enumerate(states):
         val = state.values
         phit_g = np.interp(rgd.r_g, orig_r, val)
         phit_g = divrl(phit_g, 1, rgd.r_g)
         icut = len(phit_g) - 1 # XXX correct or off-by-one?
         rcut = rgd.r_g[icut]
         bf = BasisFunction(state.l, rcut, phit_g, 'pregenerated')
         b.bf_j.append(bf)
     return b
Exemple #14
0
def get_bf_centers(atoms, basis=None):
    calc = atoms.get_calculator()
    if calc is None or isinstance(calc, SinglePointCalculator):
        symbols = atoms.get_chemical_symbols()
        basis_a = types2atomtypes(symbols, basis, 'dzp')
        nao_a = [Basis(symbol, type).nao
                 for symbol, type in zip(symbols, basis_a)]
    else:
        if not calc.initialized:
            calc.initialize(atoms)
        nao_a = [calc.wfs.setups[a].nao for a in range(len(atoms))]
    pos_ic = []
    for pos, nao in zip(atoms.get_positions(), nao_a):
        pos_ic.extend(pos[None].repeat(nao, 0))
    return np.array(pos_ic)
Exemple #15
0
    def __init__(self, Z_a, setup_types, basis_sets, lmax, xc,
                 filter=None, world=None):
        list.__init__(self)
        symbols = [chemical_symbols[Z] for Z in Z_a]
        type_a = types2atomtypes(symbols, setup_types, default='paw')
        basis_a = types2atomtypes(symbols, basis_sets, default=None)
        
        # Construct necessary PAW-setup objects:
        self.setups = {}
        natoms = {}
        Mcumulative = 0
        self.M_a = []
        self.id_a = zip(Z_a, type_a, basis_a)
        for id in self.id_a:
            setup = self.setups.get(id)
            if setup is None:
                Z, type, basis = id
                symbol = chemical_symbols[Z]
                setupdata = None
                if not isinstance(type, str):
                    setupdata = type
                # Basis may be None (meaning that the setup decides), a string
                # (meaning we load the basis set now from a file) or an actual
                # pre-created Basis object (meaning we just pass it along)
                if isinstance(basis, str):
                    basis = Basis(symbol, basis, world=world)
                setup = create_setup(symbol, xc, lmax, type,
                                     basis, setupdata=setupdata,
                                     filter=filter, world=world)
                self.setups[id] = setup
                natoms[id] = 0
            natoms[id] += 1
            self.append(setup)
            self.M_a.append(Mcumulative)
            Mcumulative += setup.nao

        # Sum up ...
        self.nvalence = 0       # number of valence electrons
        self.nao = 0            # number of atomic orbitals
        self.Eref = 0.0         # reference energy
        self.core_charge = 0.0  # core hole charge
        for id, setup in self.setups.items():
            n = natoms[id]
            self.Eref += n * setup.E
            self.core_charge += n * (setup.Z - setup.Nv - setup.Nc)
            self.nvalence += n * setup.Nv
            self.nao += n * setup.nao
Exemple #16
0
def get_bfs_atoms(atoms, basis=None, default='dzp', method='extend'):
    """Get ba
    atoms - ase::atoms
    basis - dict of elements with correspondig basis set type
    default - str
    """
    if basis is None:
        basis = {symbol:default for symbol in set(atoms.symbols)}
    symbols = atoms.get_chemical_symbols()
    basis_a = types2atomtypes(symbols, basis, default)

    nao_a = [Basis(symbol, type).nao
             for symbol, type in zip(symbols, basis_a)]

    Mvalues = []
    Mattr = getattr(Mvalues, method)
    i0 = 0
    for nao in nao_a:
        i1 = i0 + nao
        Mattr(list(range(i0,i1)))
        i0 = i1
    return Mvalues
Exemple #17
0
    def generate(
            self,
            zetacount=2,
            polarizationcount=1,
            tailnorm=(0.16, 0.3, 0.6),
            energysplit=0.1,
            tolerance=1.0e-3,
            referencefile=None,
            referenceindex=None,
            rcutpol_rel=1.0,
            rcutmax=20.0,  #ngaussians=None,
            rcharpol_rel=None,
            vconf_args=(12.0, 0.6),
            txt='-',
            include_energy_derivatives=False,
            lvalues=None):
        """Generate an entire basis set.

        This is a high-level method which will return a basis set
        consisting of several different basis vector types.

        Parameters:

        ===================== =================================================
        ``zetacount``         Number of basis functions per occupied orbital
        ``polarizationcount`` Number of polarization functions
        ``tailnorm``          List of tail norms for split-valence scheme
        ``energysplit``       Energy increase defining confinement radius (eV)
        ``tolerance``         Tolerance of energy split (eV)
        ``referencefile``     gpw-file used to generate polarization function
        ``referenceindex``    Index in reference system of relevant atom
        ``rcutpol_rel``       Polarization rcut relative to largest other rcut
        ``rcutmax``           No cutoff will be greater than this value
        ``vconf_args``        Parameters (alpha, ri/rc) for conf. potential
        ``txt``               Log filename or '-' for stdout
        ===================== =================================================

        Returns a fully initialized Basis object.
        """

        if txt == '-':
            txt = sys.stdout
        elif txt is None:
            txt = devnull

        if isinstance(tailnorm, float):
            tailnorm = (tailnorm, )
        assert 1 + len(tailnorm) >= max(polarizationcount, zetacount), \
               'Needs %d tail norm values, but only %d are specified' % \
               (max(polarizationcount, zetacount) - 1, len(tailnorm))

        textbuffer = StringIO()

        class TeeStream:  # Quick hack to both write and save output
            def __init__(self, out1, out2):
                self.out1 = out1
                self.out2 = out2

            def write(self, string):
                self.out1.write(string)
                self.out2.write(string)

        txt = TeeStream(txt, textbuffer)

        if vconf_args is not None:
            amplitude, ri_rel = vconf_args

        g = self.generator
        rgd = self.rgd

        # Find out all relevant orbitals
        # We'll probably need: s, p and d.
        # The orbitals we want are stored in u_j.
        # Thus we must find the j corresponding to the highest energy of
        # each orbital-type.
        #
        # However not all orbitals in l_j are actually occupied, so we
        # will check the occupations in the generator object's lists
        #
        # ASSUMPTION: The last index of a given value in l_j corresponds
        # exactly to the orbital we want, except those which are not occupied
        #
        # Get (only) one occupied valence state for each l
        # Not including polarization in this list
        if lvalues is None:
            lvalues = np.unique([
                l for l, f in zip(g.l_j[g.njcore:], g.f_j[g.njcore:]) if f > 0
            ])
            if lvalues[0] != 0:  # Always include s-orbital !
                lvalues = np.array([0] + list(lvalues))

        #print energysplit
        if isinstance(energysplit, float):
            energysplit = [energysplit] * (max(lvalues) + 1)
            #print energysplit,'~~~~~~~~'

        title = '%s Basis functions for %s' % (g.xcname, g.symbol)
        print >> txt, title
        print >> txt, '=' * len(title)

        j_l = {}  # index j by l rather than the other way around
        reversed_l_j = list(g.l_j)
        reversed_l_j.reverse()  # the values we want are stored last
        for l in lvalues:
            j = len(reversed_l_j) - reversed_l_j.index(l) - 1
            j_l[l] = j

        singlezetas = []
        energy_derivative_functions = []
        multizetas = [[] for i in range(zetacount - 1)]
        polarization_functions = []

        splitvalencedescr = 'split-valence wave, fixed tail norm'
        derivativedescr = 'derivative of sz wrt. (ri/rc) of potential'

        for l in lvalues:
            # Get one unmodified pseudo-orbital basis vector for each l
            j = j_l[l]
            n = g.n_j[j]
            orbitaltype = str(n) + 'spdf'[l]
            msg = 'Basis functions for l=%d, n=%d' % (l, n)
            print >> txt
            print >> txt, msg + '\n', '-' * len(msg)
            print >> txt
            if vconf_args is None:
                adverb = 'sharply'
            else:
                adverb = 'softly'
            print >> txt, 'Zeta 1: %s confined pseudo wave,' % adverb,

            u, e, de, vconf, rc = self.rcut_by_energy(j,
                                                      energysplit[l],
                                                      tolerance,
                                                      vconf_args=vconf_args)
            if rc > rcutmax:
                rc = rcutmax  # scale things down
                if vconf is not None:
                    vconf = g.get_confinement_potential(
                        amplitude, ri_rel * rc, rc)
                u, e = g.solve_confined(j, rc, vconf)
                print >> txt, 'using maximum cutoff'
                print >> txt, 'rc=%.02f Bohr' % rc
            else:
                print >> txt, 'fixed energy shift'
                print >> txt, 'DE=%.03f eV :: rc=%.02f Bohr' % (de * Hartree,
                                                                rc)
            if vconf is not None:
                print >> txt, ('Potential amp=%.02f :: ri/rc=%.02f' %
                               (amplitude, ri_rel))
            phit_g = self.smoothify(u, l)
            bf = BasisFunction(l, rc, phit_g,
                               '%s-sz confined orbital' % orbitaltype)
            norm = np.dot(g.dr, phit_g * phit_g)**.5
            print >> txt, 'Norm=%.03f' % norm
            singlezetas.append(bf)

            zetacounter = iter(xrange(2, zetacount + 1))

            if include_energy_derivatives:
                assert zetacount > 1
                zeta = zetacounter.next()
                print >> txt, '\nZeta %d: %s' % (zeta, derivativedescr)
                vconf2 = g.get_confinement_potential(amplitude,
                                                     ri_rel * rc * .99, rc)
                u2, e2 = g.solve_confined(j, rc, vconf2)

                phit2_g = self.smoothify(u2, l)
                dphit_g = phit2_g - phit_g
                dphit_norm = np.dot(rgd.dr_g, dphit_g * dphit_g)**.5
                dphit_g /= dphit_norm
                descr = '%s-dz E-derivative of sz' % orbitaltype
                bf = BasisFunction(l, rc, dphit_g, descr)
                energy_derivative_functions.append(bf)

            for i, zeta in enumerate(zetacounter):  # range(zetacount - 1):
                print >> txt, '\nZeta %d: %s' % (zeta, splitvalencedescr)
                # Unresolved issue:  how does the lack of normalization
                # of the first function impact the tail norm scheme?
                # Presumably not much, since most interesting stuff happens
                # close to the core.
                rsplit, norm, splitwave = rsplit_by_norm(
                    rgd, l, phit_g, tailnorm[i]**2.0, txt)
                descr = '%s-%sz split-valence wave' % (orbitaltype,
                                                       '0sdtq56789'[zeta])
                bf = BasisFunction(l, rsplit, phit_g - splitwave, descr)
                multizetas[i].append(bf)

        if polarizationcount > 0:
            # Now make up some properties for the polarization orbital
            # We just use the cutoffs from the previous one times a factor
            rcut = max([bf.rc for bf in singlezetas]) * rcutpol_rel
            rcut = min(rcut, rcutmax)
            # Find 'missing' values in lvalues
            for i, l in enumerate(lvalues):
                if i != l:
                    l_pol = i
                    break
            else:
                l_pol = lvalues[-1] + 1
            msg = 'Polarization function: l=%d, rc=%.02f' % (l_pol, rcut)
            print >> txt, '\n' + msg
            print >> txt, '-' * len(msg)
            # Make a single Gaussian for polarization function.
            #
            # It is known that for given l, the sz cutoff defined
            # by some fixed energy is strongly correlated to the
            # value of the characteristic radius which best reproduces
            # the wave function found by interpolation.
            #
            # We know that for e.g. d orbitals:
            #   rchar ~= .37 rcut[sz](.3eV)
            # Since we don't want to spend a lot of time finding
            # these value for other energies, we just find the energy
            # shift at .3 eV now

            j = max(j_l.values())
            u, e, de, vconf, rc_fixed = self.rcut_by_energy(
                j, .3, 1e-2, 6., (12., .6))

            default_rchar_rel = .25
            # Defaults for each l.  Actually we don't care right now
            rchar_rels = {}

            if rcharpol_rel is None:
                rcharpol_rel = rchar_rels.get(l_pol, default_rchar_rel)
            rchar = rcharpol_rel * rc_fixed
            gaussian = QuasiGaussian(1. / rchar**2, rcut)
            psi_pol = gaussian(rgd.r_g) * rgd.r_g**(l_pol + 1)
            norm = np.dot(rgd.dr_g, psi_pol * psi_pol)**.5
            psi_pol /= norm
            print >> txt, 'Single quasi Gaussian'
            msg = 'Rchar = %.03f*rcut = %.03f Bohr' % (rcharpol_rel, rchar)
            adjective = 'Gaussian'
            print >> txt, msg
            #else:
            #    psi_pol = self.make_polarization_function(rcut, l_pol,
            #                                              referencefile,
            #                                              referenceindex,
            #                                              ngaussians, txt)
            #    adjective = 'interpolated'

            type = '%s-type %s polarization' % ('spdfg'[l_pol], adjective)
            bf_pol = BasisFunction(l_pol, rcut, psi_pol, type)

            polarization_functions.append(bf_pol)
            for i in range(polarizationcount - 1):
                npol = i + 2
                msg = '\n%s: %s' % (['Secondary', 'Tertiary', 'Quaternary', \
                                     'Quintary', 'Sextary', 'Septenary'][i],
                                    splitvalencedescr)
                print >> txt, msg
                rsplit, norm, splitwave = rsplit_by_norm(
                    rgd, l_pol, psi_pol, tailnorm[i], txt)
                descr = ('%s-type split-valence polarization %d' %
                         ('spdfg'[l_pol], npol))
                bf_pol = BasisFunction(l_pol, rsplit, psi_pol - splitwave,
                                       descr)
                polarization_functions.append(bf_pol)

        bf_j = []
        bf_j.extend(singlezetas)
        bf_j.extend(energy_derivative_functions)
        for multizeta_list in multizetas:
            bf_j.extend(multizeta_list)
        bf_j.extend(polarization_functions)

        rcmax = max([bf.rc for bf in bf_j])

        # The non-equidistant grids are really only suited for AE WFs
        d = 1. / 64.
        equidistant_grid = np.arange(0., rcmax + d, d)
        ng = len(equidistant_grid)

        for bf in bf_j:
            # We have been storing phit_g * r, but we just want phit_g
            bf.phit_g = divrl(bf.phit_g, 1, rgd.r_g)

            gcut = min(int(1 + bf.rc / d), ng - 1)

            assert equidistant_grid[gcut] >= bf.rc
            assert equidistant_grid[gcut - 1] <= bf.rc

            bf.rc = equidistant_grid[gcut]
            # Note: bf.rc *must* correspond to a grid point (spline issues)
            bf.ng = gcut + 1
            # XXX all this should be done while building the basis vectors,
            # not here

            # Quick hack to change to equidistant coordinates
            spline = Spline(bf.l,
                            rgd.r_g[rgd.r2g_floor(bf.rc)],
                            bf.phit_g,
                            rgd.r_g,
                            beta=rgd.beta,
                            points=100)
            bf.phit_g = np.array(
                [spline(r) * r**bf.l for r in equidistant_grid[:bf.ng]])
            bf.phit_g[-1] = 0.

        basis = Basis(g.symbol, self.name, False)
        basis.ng = ng
        basis.d = d
        basis.bf_j = bf_j
        basis.generatordata = textbuffer.getvalue().strip()
        basis.generatorattrs = {'version': version}
        textbuffer.close()

        return basis
Exemple #18
0
 def __init__(self, symbol, phit_j):
     Basis.__init__(self, symbol, 'partial-waves', readxml=False)
     self.phit_j = phit_j
Exemple #19
0
    def __init__(self,
                 Z_a,
                 setup_types,
                 basis_sets,
                 xc,
                 filter=None,
                 world=None):
        list.__init__(self)
        symbols = [chemical_symbols[Z] for Z in Z_a]
        type_a = types2atomtypes(symbols, setup_types, default='paw')
        basis_a = types2atomtypes(symbols, basis_sets, default=None)

        for a, _type in enumerate(type_a):
            # Make basis files correspond to setup files.
            #
            # If the setup has a name (i.e. non-default _type), then
            # prepend that name to the basis name.
            #
            # Typically people might specify '11' as the setup but just
            # 'dzp' for the basis set.  Here we adjust to
            # obtain, say, '11.dzp' which loads the correct basis set.
            #
            # There will be no way to obtain the original 'dzp' with
            # a custom-named setup except by loading directly from
            # BasisData.
            #
            # Due to the "szp(dzp)" syntax this is complicated!
            # The name has to go as "szp(name.dzp)".
            basis = basis_a[a]
            if isinstance(basis, basestring):
                if isinstance(_type, basestring):
                    setupname = _type
                else:
                    setupname = _type.name  # _type is an object like SetupData
                # Drop DFT+U specification from type string if it is there:
                if hasattr(setupname, 'swapcase'):
                    setupname = setupname.split(':')[0]

                # Basis names inherit setup names except default setups
                # and ghost atoms.
                if setupname != 'paw' and setupname != 'ghost':
                    if setupname:
                        if '(' in basis:
                            reduced, name = basis.split('(')
                            assert name.endswith(')')
                            name = name[:-1]
                            fullname = '%s(%s.%s)' % (reduced, setupname, name)
                        else:
                            fullname = '%s.%s' % (setupname, basis_a[a])
                        basis_a[a] = fullname

        # Construct necessary PAW-setup objects:
        self.setups = {}
        natoms = {}
        Mcumulative = 0
        self.M_a = []
        self.id_a = list(zip(Z_a, type_a, basis_a))
        for id in self.id_a:
            setup = self.setups.get(id)
            if setup is None:
                Z, type, basis = id
                symbol = chemical_symbols[Z]
                setupdata = None
                if not isinstance(type, basestring):
                    setupdata = type
                # Basis may be None (meaning that the setup decides), a string
                # (meaning we load the basis set now from a file) or an actual
                # pre-created Basis object (meaning we just pass it along)
                if isinstance(basis, basestring):
                    basis = Basis(symbol, basis, world=world)
                setup = create_setup(symbol,
                                     xc,
                                     2,
                                     type,
                                     basis,
                                     setupdata=setupdata,
                                     filter=filter,
                                     world=world)
                self.setups[id] = setup
                natoms[id] = 0
            natoms[id] += 1
            self.append(setup)
            self.M_a.append(Mcumulative)
            Mcumulative += setup.nao

        # Sum up ...
        self.nvalence = 0  # number of valence electrons
        self.nao = 0  # number of atomic orbitals
        self.Eref = 0.0  # reference energy
        self.core_charge = 0.0  # core hole charge
        for id, setup in self.setups.items():
            n = natoms[id]
            self.Eref += n * setup.E
            self.core_charge += n * (setup.Z - setup.Nv - setup.Nc)
            self.nvalence += n * setup.Nv
            self.nao += n * setup.nao
Exemple #20
0
def get_orbitals_by_energy_shift(opts, setup, **kwargs):
    h = opts.grid
    try:
        f_ln = setup.f_ln
    except AttributeError:
        f_ln = []
        for n, l, f in zip(setup.n_j, setup.l_j, setup.f_j):
            if n < 0:
                continue
            if l == len(f_ln):
                f_ln.append([])
            if f > 0 and n > 0:
                f_ln[l].append(f)

    def calculate(rcut, h=h, txt='-'):
        return atompaw(setup, f_ln, rcut, h=h, txt=txt, **kwargs)

    def get_orbital_energy(l0, n0, rcut):
        calc = calculate(rcut, 0.15, txt=None)
        for l, n, f, eps, psit_G in calc.state_iter():
            if l == l0 and n + 1 == n0:  # XXX
                return eps
        raise ValueError('No such valence state: l=%d, n=%d' % (l0, n0))

    calc0 = calculate(2.0, 0.2, txt=None)  # XXX

    def valence_states():
        for l, n, f, eps, psit_G in calc0.state_iter():
            yield l, n + 1  # XXX

    bf_j = []

    for i, (l, n) in enumerate(valence_states()):
        e0 = get_orbital_energy(l, n, 15.0) * 27.211  # 15Ang == infinity
        print('e0', e0)

        def obj(rcut):
            eps = get_orbital_energy(l, n, rcut) * 27.211
            de = eps - opts.energy_shift - e0
            # print rcut, eps, de
            return de

        # Not exactly efficient...
        rcut = bisect(obj, 1.0, 15.0, xtol=0.1)
        calc = calculate(h=h, rcut=rcut, txt=None)
        bfs = calc.extract_basis_functions()
        bf_j.append(bfs.bf_j[i])

    d = bfs.d
    ng = max([bf.ng for bf in bf_j])
    rgd = EquidistantRadialGridDescriptor(d, ng)
    basis = Basis(setup.symbol, 'strange', readxml=False, rgd=rgd)
    basis.bf_j = bf_j
    return basis
    # for (l, n), cutoff in zip(valence_states(), cutoffs):
    #     calculate(

    # return
    # calc = calculate(rcut)
    bfs = calc.extract_basis_functions(basis_name=opts.name)
    ldict = dict([(bf.l, bf) for bf in bfs.bf_j])

    rgd = bfs.get_grid_descriptor()

    def get_rsplit(bf, splitnorm):
        if opts.s_approaches_zero and bf.l == 0:
            l = 1  # create a function phi(r) = A * r + O(r^2)
        else:
            l = bf.l
        return rsplit_by_norm(rgd, l, bf.phit_g * rgd.r_g, splitnorm**2,
                              sys.stdout)

    splitvalence_bfs = []
    for splitnorm in opts.splitnorm:
        splitnorm = float(splitnorm)
        for orbital_bf in bfs.bf_j:
            rsplit, normsqr, phit_g = get_rsplit(orbital_bf, splitnorm)
            phit_g[1:] /= rgd.r_g[1:]
            gcut = rgd.ceil(rsplit)
            #tailnorm = np.dot(rgd.dr_g[gcut:],
            #                  (rgd.r_g[gcut:] * orbital_bf.phit_g[gcut:])**2)**0.5
            #print 'tailnorm', tailnorm
            dphit_g = orbital_bf.phit_g[:gcut + 1] - phit_g[:gcut + 1]
            bf = BasisFunction(l=orbital_bf.l,
                               rc=rgd.r_g[gcut],
                               phit_g=dphit_g,
                               type='%s split-valence' % 'spd'[orbital_bf.l])
            splitvalence_bfs.append(bf)
    bfs.bf_j.extend(splitvalence_bfs)

    #rpol = None
    for l in range(3):
        if l not in ldict:
            lpol = l
            source_bf = ldict[lpol - 1]
            break
    else:
        raise NotImplementedError('f-type polarization not implemented')

    for splitnorm in opts.polarization:
        splitnorm = float(splitnorm)
        rchar, normsqr, phit_g = get_rsplit(source_bf, splitnorm)
        gcut = rgd.ceil(3.5 * rchar)
        rcut = rgd.r_g[gcut]
        phit_g = get_gaussianlike_basis_function(rgd, lpol, rchar, gcut)
        N = len(phit_g)
        x = np.dot(rgd.dr_g[:N], (phit_g * rgd.r_g[:N])**2)**0.5
        print('x', x)
        bf = BasisFunction(None,
                           lpol,
                           rc=rcut,
                           phit_g=phit_g,
                           type='%s polarization' % 'spd'[lpol])
        bf.phit_g
        bfs.bf_j.append(bf)
    bfs.write_xml()
Exemple #21
0
    def create_basis_set(self, tailnorm=0.0005, scale=200.0, splitnorm=0.16):
        rgd = self.rgd
        self.basis = Basis(self.aea.symbol, 'dzp', readxml=False, rgd=rgd)

        # We print text to sdtout and put it in the basis-set file
        txt = 'Basis functions:\n'

        # Bound states:
        for l, waves in enumerate(self.waves_l):
            for i, n in enumerate(waves.n_n):
                if n > 0:
                    tn = tailnorm
                    if waves.f_n[i] == 0:
                        tn = min(0.05, tn * 20)  # no need for long tail
                    phit_g, ronset, rc, de = self.create_basis_function(
                        l, i, tn, scale)
                    bf = BasisFunction(n, l, rc, phit_g, 'bound state')
                    self.basis.append(bf)

                    txt += '%d%s bound state:\n' % (n, 'spdf'[l])
                    txt += ('  cutoff: %.3f to %.3f Bohr (tail-norm=%f)\n' %
                            (ronset, rc, tn))
                    txt += '  eigenvalue shift: %.3f eV\n' % (de * Hartree)

        # Split valence:
        for l, waves in enumerate(self.waves_l):
            # Find the largest n that is occupied:
            n0 = None
            for f, n in zip(waves.f_n, waves.n_n):
                if n > 0 and f > 0:
                    n0 = n
            if n0 is None:
                continue

            for bf in self.basis.bf_j:
                if bf.l == l and bf.n == n0:
                    break

            # Radius and l-value used for polarization function below:
            rcpol = bf.rc
            lpol = l + 1

            phit_g = bf.phit_g

            # Find cutoff radius:
            n_g = np.add.accumulate(phit_g**2 * rgd.r_g**2 * rgd.dr_g)
            norm = n_g[-1]
            gc = (norm - n_g > splitnorm * norm).sum()
            rc = rgd.r_g[gc]

            phit2_g = rgd.pseudize(phit_g, gc, l, 2)[0]  # "split valence"
            bf = BasisFunction(n, l, rc, phit_g - phit2_g, 'split valence')
            self.basis.append(bf)

            txt += '%d%s split valence:\n' % (n0, 'spdf'[l])
            txt += '  cutoff: %.3f Bohr (tail-norm=%f)\n' % (rc, splitnorm)

        # Polarization:
        gcpol = rgd.round(rcpol)
        alpha = 1 / (0.25 * rcpol)**2

        # Gaussian that is continuous and has a continuous derivative at rcpol:
        phit_g = np.exp(-alpha * rgd.r_g**2) * rgd.r_g**lpol
        phit_g -= rgd.pseudize(phit_g, gcpol, lpol, 2)[0]
        phit_g[gcpol:] = 0.0

        bf = BasisFunction(None, lpol, rcpol, phit_g, 'polarization')
        self.basis.append(bf)
        txt += 'l=%d polarization functions:\n' % lpol
        txt += '  cutoff: %.3f Bohr (r^%d exp(-%.3f*r^2))\n' % (rcpol, lpol,
                                                                alpha)

        self.log(txt)

        # Write basis-set file:
        self.basis.generatordata = txt
        self.basis.generatorattrs.update(
            dict(tailnorm=tailnorm, scale=scale, splitnorm=splitnorm))
        self.basis.name = '%de.dzp' % self.nvalence

        return self.basis
Exemple #22
0
def get_orbitals_by_energy_shift(opts, setup, **kwargs):
    h = opts.grid
    try:
        f_ln = setup.f_ln
    except AttributeError:
        f_ln = []
        for n, l, f in zip(setup.n_j, setup.l_j, setup.f_j):
            if n < 0:
                continue
            if l == len(f_ln):
                f_ln.append([])
            if f > 0 and n > 0:
                f_ln[l].append(f)
    def calculate(rcut, h=h, txt='-'):
        return atompaw(setup, f_ln, rcut, h=h, txt=txt, **kwargs)

    def get_orbital_energy(l0, n0, rcut):
        calc = calculate(rcut, 0.15, txt=None)
        for l, n, f, eps, psit_G in calc.state_iter():
            if l == l0 and n + 1== n0: # XXX
                return eps
        raise ValueError('No such valence state: l=%d, n=%d' % (l0, n0))

    calc0 = calculate(2.0, 0.2, txt=None) # XXX
    def valence_states():
        for l, n, f, eps, psit_G in calc0.state_iter():
            yield l, n + 1 # XXX

    bf_j = []
    cutoffs = []
    
    for i, (l, n) in enumerate(valence_states()):
        e0 = get_orbital_energy(l, n, 15.0) * 27.211 # 15Ang == infinity
        print 'e0', e0
        def obj(rcut):
            eps = get_orbital_energy(l, n, rcut) * 27.211
            de = eps - opts.energy_shift - e0
            #print rcut, eps, de
            return de
        
        # Not exactly efficient...
        rcut = bisect(obj, 1.0, 15.0, xtol=0.1)
        calc = calculate(h=h, rcut=rcut, txt=None)
        bfs = calc.extract_basis_functions()
        bf_j.append(bfs.bf_j[i])

    basis = Basis(setup.symbol, 'strange', readxml=False)
    basis.d = bfs.d
    basis.ng = max([bf.ng for bf in bf_j])
    basis.bf_j = bf_j
    return basis
    #for (l, n), cutoff in zip(valence_states(), cutoffs):
    #    calculate(
    
    #return
    #calc = calculate(rcut)
    bfs = calc.extract_basis_functions(basis_name=opts.name)
    ldict = dict([(bf.l, bf) for bf in bfs.bf_j])

    rgd = bfs.get_grid_descriptor()

    def get_rsplit(bf, splitnorm):
        if opts.s_approaches_zero and bf.l == 0:
            l = 1 # create a function phi(r) = A * r + O(r^2)
        else:
            l = bf.l
        return rsplit_by_norm(rgd,
                              l,
                              bf.phit_g * rgd.r_g,
                              splitnorm**2,
                              sys.stdout)

    splitvalence_bfs = []
    for splitnorm in opts.splitnorm:
        splitnorm = float(splitnorm)
        for orbital_bf in bfs.bf_j:
            rsplit, normsqr, phit_g = get_rsplit(orbital_bf, splitnorm)
            phit_g[1:] /= rgd.r_g[1:]
            gcut = rgd.r2g_ceil(rsplit)
            #tailnorm = np.dot(rgd.dr_g[gcut:],
            #                  (rgd.r_g[gcut:] * orbital_bf.phit_g[gcut:])**2)**0.5
            #print 'tailnorm', tailnorm
            dphit_g = orbital_bf.phit_g[:gcut+1] - phit_g[:gcut+1]
            bf = BasisFunction(l=orbital_bf.l,
                               rc=rgd.r_g[gcut],
                               phit_g=dphit_g,
                               type='%s split-valence' % 'spd'[orbital_bf.l])
            splitvalence_bfs.append(bf)
    bfs.bf_j.extend(splitvalence_bfs)

    #rpol = None
    for l in range(3):
        if not l in ldict:
            lpol = l
            source_bf = ldict[lpol - 1]
            break
    else:
        raise NotImplementedError('f-type polarization not implemented')
    
    for splitnorm in opts.polarization:
        splitnorm = float(splitnorm)
        rchar, normsqr, phit_g = get_rsplit(source_bf, splitnorm)
        gcut = rgd.r2g_ceil(3.5 * rchar)
        rcut = rgd.r_g[gcut]
        phit_g = get_gaussianlike_basis_function(rgd, lpol, rchar, gcut)
        N = len(phit_g)
        x = np.dot(rgd.dr_g[:N], (phit_g * rgd.r_g[:N])**2)**0.5
        print 'x', x
        bf = BasisFunction(lpol,
                           rc=rcut,
                           phit_g=phit_g,
                           type='%s polarization' % 'spd'[lpol])
        bf.phit_g
        bfs.bf_j.append(bf)
    bfs.write_xml()
Exemple #23
0
    def generate(self, zetacount=2, polarizationcount=1,
                 tailnorm=(0.16, 0.3, 0.6), energysplit=0.1, tolerance=1.0e-3,
                 referencefile=None, referenceindex=None, rcutpol_rel=1.0, 
                 rcutmax=20.0, #ngaussians=None,
                 rcharpol_rel=None,
                 vconf_args=(12.0, 0.6), txt='-',
                 include_energy_derivatives=False,
                 #lvalues=None, # XXX clean up some of these!
                 jvalues=None,
                 l_pol=None
                 ):
        """Generate an entire basis set.

        This is a high-level method which will return a basis set
        consisting of several different basis vector types.

        Parameters:

        ===================== =================================================
        ``zetacount``         Number of basis functions per occupied orbital
        ``polarizationcount`` Number of polarization functions
        ``tailnorm``          List of tail norms for split-valence scheme
        ``energysplit``       Energy increase defining confinement radius (eV)
        ``tolerance``         Tolerance of energy split (eV)
        ``referencefile``     gpw-file used to generate polarization function
        ``referenceindex``    Index in reference system of relevant atom
        ``rcutpol_rel``       Polarization rcut relative to largest other rcut
        ``rcutmax``           No cutoff will be greater than this value
        ``vconf_args``        Parameters (alpha, ri/rc) for conf. potential
        ``txt``               Log filename or '-' for stdout
        ===================== =================================================

        Returns a fully initialized Basis object.
        """

        if txt == '-':
            txt = sys.stdout
        elif txt is None:
            txt = devnull

        if isinstance(tailnorm, float):
            tailnorm = (tailnorm,)
        assert 1 + len(tailnorm) >= max(polarizationcount, zetacount), \
               'Needs %d tail norm values, but only %d are specified' % \
               (max(polarizationcount, zetacount) - 1, len(tailnorm))

        textbuffer = StringIO()
        class TeeStream: # Quick hack to both write and save output
            def __init__(self, out1, out2):
                self.out1 = out1
                self.out2 = out2
            def write(self, string):
                self.out1.write(string)
                self.out2.write(string)
        txt = TeeStream(txt, textbuffer)

        if vconf_args is not None:
            amplitude, ri_rel = vconf_args

        g = self.generator
        rgd = self.rgd

        njcore = g.njcore
        n_j = g.n_j[njcore:]
        l_j = g.l_j[njcore:]
        f_j = g.f_j[njcore:]

        if jvalues is None:
            jvalues = []
            sortkeys = []
            for j in range(len(n_j)):
                if f_j[j] == 0 and l_j[j] != 0:
                    continue
                jvalues.append(j)
                sortkeys.append(l_j[j])
            
            # Now order jvalues by l
            #
            # Use a stable sort so the energy ordering within each
            # angular momentum is guaranteed to be preserved
            args = np.argsort(sortkeys, kind='mergesort')
            jvalues = np.array(jvalues)[args]

        fulljvalues = [njcore + j for j in jvalues]
        
        if isinstance(energysplit, float):
            energysplit = [energysplit] * len(jvalues)
        
        title = '%s Basis functions for %s' % (g.xcname, g.symbol)
        print >> txt, title
        print >> txt, '=' * len(title)
        
        singlezetas = []
        energy_derivative_functions = []
        multizetas = [[] for i in range(zetacount - 1)]
        polarization_functions = []

        splitvalencedescr = 'split-valence wave, fixed tail norm'
        derivativedescr = 'derivative of sz wrt. (ri/rc) of potential'


        for vj, fullj, esplit in zip(jvalues, fulljvalues, energysplit):
            l = l_j[vj]
            n = n_j[vj]
            assert n > 0
            orbitaltype = str(n) + 'spdf'[l]
            msg = 'Basis functions for l=%d, n=%d' % (l, n)
            print >> txt
            print >> txt, msg + '\n', '-' * len(msg)
            print >> txt
            if vconf_args is None:
                adverb = 'sharply'
            else:
                adverb = 'softly'
            print >> txt, 'Zeta 1: %s confined pseudo wave,' % adverb,

            u, e, de, vconf, rc = self.rcut_by_energy(fullj, esplit,
                                                      tolerance,
                                                      vconf_args=vconf_args)
            if rc > rcutmax:
                rc = rcutmax # scale things down
                if vconf is not None:
                    vconf = g.get_confinement_potential(amplitude, ri_rel * rc,
                                                        rc)
                u, e = g.solve_confined(fullj, rc, vconf)
                print >> txt, 'using maximum cutoff'
                print >> txt, 'rc=%.02f Bohr' % rc
            else:
                print >> txt, 'fixed energy shift'    
                print >> txt, 'DE=%.03f eV :: rc=%.02f Bohr' % (de * Hartree,
                                                                rc)
            if vconf is not None:
                print >> txt, ('Potential amp=%.02f :: ri/rc=%.02f' %
                               (amplitude, ri_rel))
            phit_g = self.smoothify(u, l)
            bf = BasisFunction(l, rc, phit_g,
                               '%s-sz confined orbital' % orbitaltype)
            norm = np.dot(g.dr, phit_g * phit_g)**.5
            print >> txt, 'Norm=%.03f' % norm
            singlezetas.append(bf)

            zetacounter = iter(xrange(2, zetacount + 1))

            if include_energy_derivatives:
                assert zetacount > 1
                zeta = zetacounter.next()
                print >> txt, '\nZeta %d: %s' % (zeta, derivativedescr)
                vconf2 = g.get_confinement_potential(amplitude,
                                                     ri_rel * rc * .99, rc)
                u2, e2 = g.solve_confined(fullj, rc, vconf2)
                
                phit2_g = self.smoothify(u2, l)
                dphit_g = phit2_g - phit_g
                dphit_norm = np.dot(rgd.dr_g, dphit_g * dphit_g) ** .5
                dphit_g /= dphit_norm
                descr = '%s-dz E-derivative of sz' % orbitaltype
                bf = BasisFunction(l, rc, dphit_g, descr)
                energy_derivative_functions.append(bf)

            for i, zeta in enumerate(zetacounter): # range(zetacount - 1):
                print >> txt, '\nZeta %d: %s' % (zeta, splitvalencedescr)
                # Unresolved issue:  how does the lack of normalization
                # of the first function impact the tail norm scheme?
                # Presumably not much, since most interesting stuff happens
                # close to the core.
                rsplit, norm, splitwave = rsplit_by_norm(rgd, l, phit_g,
                                                         tailnorm[i]**2.0,
                                                         txt)
                descr = '%s-%sz split-valence wave' % (orbitaltype,
                                                       '0sdtq56789'[zeta])
                bf = BasisFunction(l, rsplit, phit_g - splitwave, descr)
                multizetas[i].append(bf)
            
        if polarizationcount > 0 or l_pol is not None:
            if l_pol is None:
                # Now make up some properties for the polarization orbital
                # We just use the cutoffs from the previous one times a factor
                # Find 'missing' values in lvalues
                lvalues = [l_j[vj] for vj in jvalues]
                for i in range(max(lvalues) + 1):
                    if list(lvalues).count(i) == 0:
                        l_pol = i
                        break
                else:
                        l_pol = max(lvalues) + 1

            # Find the last state with l=l_pol - 1, which will be the state we
            # base the polarization function on
            for vj, fullj, bf in zip(jvalues[::-1], fulljvalues[::-1],
                              singlezetas[::-1]):
                if bf.l == l_pol - 1:
                    vj_pol = vj # index of the state *which* we polarize
                    fullj_pol = fullj
                    rcut = bf.rc * rcutpol_rel
                    break
            else:
                raise ValueError('The requested value l_pol=%d requires l=%d '
                                 'among valence states' % (l_pol, l_pol - 1))
            rcut = min(rcut, rcutmax)
            msg = 'Polarization function: l=%d, rc=%.02f' % (l_pol, rcut)
            print >> txt, '\n' + msg
            print >> txt, '-' * len(msg)
            # Make a single Gaussian for polarization function.
            #
            # It is known that for given l, the sz cutoff defined
            # by some fixed energy is strongly correlated to the
            # value of the characteristic radius which best reproduces
            # the wave function found by interpolation.
            #
            # We know that for e.g. d orbitals:
            #   rchar ~= .37 rcut[sz](.3eV)
            # Since we don't want to spend a lot of time finding
            # these value for other energies, we just find the energy
            # shift at .3 eV now

            u, e, de, vconf, rc_fixed = self.rcut_by_energy(fullj_pol,
                                                            .3, 1e-2,
                                                            6., (12., .6))

            default_rchar_rel = .25
            # Defaults for each l.  Actually we don't care right now
            rchar_rels = {}

            if rcharpol_rel is None:
                rcharpol_rel = rchar_rels.get(l_pol, default_rchar_rel)
            rchar = rcharpol_rel * rc_fixed
            gaussian = QuasiGaussian(1./rchar**2, rcut)
            psi_pol = gaussian(rgd.r_g) * rgd.r_g**(l_pol + 1)
            norm = np.dot(rgd.dr_g, psi_pol * psi_pol) ** .5
            psi_pol /= norm
            print >> txt, 'Single quasi Gaussian'
            msg = 'Rchar = %.03f*rcut = %.03f Bohr' % (rcharpol_rel, rchar)
            adjective = 'Gaussian'
            print >> txt, msg
            type = '%s-type %s polarization' % ('spdfg'[l_pol], adjective)
            bf_pol = BasisFunction(l_pol, rcut, psi_pol, type)
                                   
            polarization_functions.append(bf_pol)
            for i in range(polarizationcount - 1):
                npol = i + 2
                msg = '\n%s: %s' % (['Secondary', 'Tertiary', 'Quaternary', \
                                     'Quintary', 'Sextary', 'Septenary'][i],
                                    splitvalencedescr)
                print >> txt, msg
                rsplit, norm, splitwave = rsplit_by_norm(rgd, l_pol, psi_pol,
                                                         tailnorm[i],
                                                         txt)
                descr = ('%s-type split-valence polarization %d'
                         % ('spdfg'[l_pol], npol))
                bf_pol = BasisFunction(l_pol, rsplit, psi_pol - splitwave,
                                       descr)
                polarization_functions.append(bf_pol)
        
        bf_j = []
        bf_j.extend(singlezetas)
        bf_j.extend(energy_derivative_functions)
        for multizeta_list in multizetas:
            bf_j.extend(multizeta_list)
        bf_j.extend(polarization_functions)
        
        rcmax = max([bf.rc for bf in bf_j])

        # The non-equidistant grids are really only suited for AE WFs
        d = 1./64.
        equidistant_grid = np.arange(0., rcmax + d, d)
        ng = len(equidistant_grid)

        for bf in bf_j:
            # We have been storing phit_g * r, but we just want phit_g
            bf.phit_g = divrl(bf.phit_g, 1, rgd.r_g)
            
            gcut = min(int(1 + bf.rc / d), ng - 1)
            
            assert equidistant_grid[gcut] >= bf.rc
            assert equidistant_grid[gcut - 1] <= bf.rc
            
            bf.rc = equidistant_grid[gcut]
            # Note: bf.rc *must* correspond to a grid point (spline issues)
            bf.ng = gcut + 1
            # XXX all this should be done while building the basis vectors,
            # not here
            
            # Quick hack to change to equidistant coordinates
            spline = rgd.spline(bf.phit_g, rgd.r_g[rgd.floor(bf.rc)], bf.l, 
                                points=100)
            bf.phit_g = np.array([spline(r) * r**bf.l
                                  for r in equidistant_grid[:bf.ng]])
            bf.phit_g[-1] = 0.

        basis = Basis(g.symbol, self.name, False)
        basis.ng = ng
        basis.d = d
        basis.bf_j = bf_j
        basis.generatordata = textbuffer.getvalue().strip()
        basis.generatorattrs = {'version': version}
        textbuffer.close()

        return basis
Exemple #24
0
class PAWSetupGenerator:
    def __init__(self,
                 aea,
                 projectors,
                 scalar_relativistic=False,
                 core_hole=None,
                 fd=None):
        """fd: stream
            Text output."""

        self.aea = aea

        self.fd = fd or sys.stdout

        if core_hole:
            state, occ = core_hole.split(',')
            n = int(state[0])
            l = 'spdf'.find(state[1])
            occ = float(occ)
            aea.add(n, l, -occ)
            self.core_hole = (n, l, occ)
        else:
            self.core_hole = None

        if projectors[-1].isupper():
            self.l0 = 'SPDFG'.find(projectors[-1])
            projectors = projectors[:-2]
        else:
            self.l0 = None

        self.lmax = -1
        self.states = {}
        for s in projectors.split(','):
            l = 'spdf'.find(s[-1])
            if len(s) == 1:
                n = None
            elif '.' in s:
                n = float(s[:-1])
            else:
                n = int(s[:-1])
            if l in self.states:
                self.states[l].append(n)
            else:
                self.states[l] = [n]
            if l > self.lmax:
                self.lmax = l

        # Add empty bound states:
        for l, nn in self.states.items():
            for n in nn:
                if (isinstance(n, int) and
                    (l not in aea.f_lsn or n - l > len(aea.f_lsn[l][0]))):
                    aea.add(n, l, 0)

        aea.initialize()
        aea.run()
        aea.scalar_relativistic = scalar_relativistic
        aea.refine()

        self.rgd = aea.rgd

        self.vtr_g = None

        self.basis = None

        self.log('\nGenerating PAW', aea.xc.name, 'setup for', aea.symbol)

    def construct_shape_function(self, alpha=None, rc=None, eps=1e-10):
        """Build shape-function for compensation charge."""

        self.alpha = alpha

        if self.alpha is None:
            if isinstance(rc, list):
                rc = min(rc)
            rc = 1.5 * rc

            def spillage(alpha):
                """Fraction of gaussian charge outside rc."""
                x = alpha * rc**2
                return 1 - erf(sqrt(x)) + 2 * sqrt(x / pi) * exp(-x)

            def f(alpha):
                return log(spillage(alpha)) - log(eps)

            if LooseVersion(scipy_version) < '0.8':
                self.alpha = fsolve(f, 7.0)
            else:
                self.alpha = fsolve(f, 7.0)[0]

            self.alpha = round(self.alpha, 1)

        self.log('Shape function: exp(-alpha*r^2), alpha=%.1f Bohr^-2' %
                 self.alpha)

        self.ghat_g = (np.exp(-self.alpha * self.rgd.r_g**2) *
                       (self.alpha / pi)**1.5)

    def log(self, *args, **kwargs):
        print(file=self.fd, *args, **kwargs)

    def calculate_core_density(self):
        self.nc_g = self.rgd.zeros()
        self.tauc_g = self.rgd.zeros()
        self.ncore = 0
        self.nvalence = 0
        self.ekincore = 0.0
        for l, ch in enumerate(self.aea.channels):
            for n, f in enumerate(ch.f_n):
                if (l <= self.lmax and any(n + l + 1 == nn
                                           for nn in self.states[l]
                                           if isinstance(nn, int))):
                    self.nvalence += f
                else:
                    self.nc_g += f * ch.calculate_density(n)
                    self.tauc_g += f * ch.calculate_kinetic_energy_density(n)
                    self.ncore += f
                    self.ekincore += f * ch.e_n[n]

        self.ekincore -= self.rgd.integrate(self.nc_g * self.aea.vr_sg[0], -1)

        self.log('Core electrons:', self.ncore)
        self.log('Valence electrons:', self.nvalence)

    def find_local_potential(self, r0, P):
        self.r0 = r0
        self.nderiv0 = P
        if self.l0 is None:
            self.find_polynomial_potential(r0, P)
        else:
            self.match_local_potential(r0, P)

    def find_polynomial_potential(self, r0, P):
        self.log('Constructing smooth local potential for r < %.3f' % r0)
        g0 = self.rgd.ceil(r0)
        self.vtr_g = self.rgd.pseudize(self.aea.vr_sg[0], g0, 1, P)[0]

    def match_local_potential(self, r0, P):
        l0 = self.l0
        self.log('Local potential matching %s-scattering at e=0.0 eV' %
                 'spdfg'[l0] + ' and r=%.2f Bohr' % r0)

        g0 = self.rgd.ceil(r0)
        gc = g0 + 20

        e0 = 0.0

        ch = Channel(l0)
        phi_g = self.rgd.zeros()
        a = ch.integrate_outwards(phi_g, self.rgd, self.aea.vr_sg[0], gc, e0,
                                  self.aea.scalar_relativistic, self.aea.Z)[1]
        phi_g[1:gc] /= self.rgd.r_g[1:gc]
        phi_g[0] = a

        phit_g, c = self.rgd.pseudize(phi_g, g0, l=l0, points=P)

        dgdr_g = 1 / self.rgd.dr_g
        d2gdr2_g = self.rgd.d2gdr2()
        a_g = phit_g.copy()
        a_g[1:] /= self.rgd.r_g[1:]**l0
        a_g[0] = c
        dadg_g = self.rgd.zeros()
        d2adg2_g = self.rgd.zeros()
        dadg_g[1:-1] = 0.5 * (a_g[2:] - a_g[:-2])
        d2adg2_g[1:-1] = a_g[2:] - 2 * a_g[1:-1] + a_g[:-2]
        q_g = (((l0 + 1) * dgdr_g + 0.5 * self.rgd.r_g * d2gdr2_g) * dadg_g +
               0.5 * self.rgd.r_g * d2adg2_g * dgdr_g**2)
        q_g[:g0] /= a_g[:g0]
        q_g += e0 * self.rgd.r_g
        q_g[0] = 0.0

        self.vtr_g = self.aea.vr_sg[0].copy()
        self.vtr_g[0] = 0.0
        self.vtr_g[1:g0] = q_g[1:g0]

    def add_waves(self, rc):
        if isinstance(rc, float):
            radii = [rc]
        else:
            radii = rc

        if self.lmax >= 0:
            radii += [radii[-1]] * (self.lmax + 1 - len(radii))
        del radii[self.lmax + 1:]  # remove unused radii

        self.rcmax = max(radii)

        self.waves_l = []
        for l in range(self.lmax + 1):
            rcut = radii[l]
            waves = PAWWaves(self.rgd, l, rcut)
            e = -1.0
            for n in self.states[l]:
                if isinstance(n, int):
                    # Bound state:
                    ch = self.aea.channels[l]
                    e = ch.e_n[n - l - 1]
                    f = ch.f_n[n - l - 1]
                    phi_g = ch.phi_ng[n - l - 1]
                else:
                    if n is None:
                        e += 1.0
                    else:
                        e = n
                    n = -1
                    f = 0.0
                    phi_g = self.rgd.zeros()
                    gc = self.rgd.round(2.5 * self.rcmax)
                    ch = Channel(l)
                    a = ch.integrate_outwards(phi_g, self.rgd,
                                              self.aea.vr_sg[0], gc, e,
                                              self.aea.scalar_relativistic,
                                              self.aea.Z)[1]
                    phi_g[1:gc + 1] /= self.rgd.r_g[1:gc + 1]
                    phi_g[0] = a
                    phi_g /= (self.rgd.integrate(phi_g**2) / (4 * pi))**0.5

                waves.add(phi_g, n, e, f)
            self.waves_l.append(waves)

    def pseudize(self, type='poly', nderiv=6, rcore=None):
        self.Q = -self.aea.Z + self.ncore

        self.nt_g = self.rgd.zeros()
        for waves in self.waves_l:
            waves.pseudize(type, nderiv, self.vtr_g, self.aea.vr_sg[0],
                           2.0 * self.rcmax)
            self.nt_g += waves.nt_g
            self.Q += waves.Q

        self.construct_pseudo_core_density(rcore)
        self.calculate_potentials()
        self.summarize()

    def construct_pseudo_core_density(self, rcore):
        if rcore is None:
            rcore = self.rcmax * 0.8
        else:
            assert abs(rcore) <= self.rcmax

        if self.ncore == 0:
            self.nct_g = self.rgd.zeros()
            self.tauct_g = self.rgd.zeros()
        elif rcore > 0.0:
            # Make sure pseudo density is monotonically decreasing:
            while True:
                gcore = self.rgd.round(rcore)
                self.nct_g = self.rgd.pseudize(self.nc_g, gcore)[0]
                nt_g = self.nt_g + self.nct_g
                dntdr_g = self.rgd.derivative(nt_g)[:gcore]
                if dntdr_g.max() < 0.0:
                    break
                rcore -= 0.01

            rcore *= 1.2
            gcore = self.rgd.round(rcore)
            self.nct_g = self.rgd.pseudize(self.nc_g, gcore)[0]
            nt_g = self.nt_g + self.nct_g

            self.log('Constructing smooth pseudo core density for r < %.3f' %
                     rcore)
            self.nt_g = nt_g

            self.tauct_g = self.rgd.pseudize(self.tauc_g, gcore)[0]
        else:
            rcore *= -1
            gcore = self.rgd.round(rcore)
            nt_g = self.rgd.pseudize(self.aea.n_sg[0], gcore)[0]
            self.nct_g = nt_g - self.nt_g
            self.nt_g = nt_g

            self.log('Constructing NLCC-style smooth pseudo core density for '
                     'r < %.3f' % rcore)

            self.tauct_g = self.rgd.pseudize(self.tauc_g, gcore)[0]

        self.npseudocore = self.rgd.integrate(self.nct_g)
        self.log('Pseudo core electrons: %.6f' % self.npseudocore)
        self.Q -= self.npseudocore

    def calculate_potentials(self):
        self.rhot_g = self.nt_g + self.Q * self.ghat_g
        self.vHtr_g = self.rgd.poisson(self.rhot_g)

        self.vxct_g = self.rgd.zeros()
        nt_sg = self.nt_g.reshape((1, -1))
        self.exct = self.aea.xc.calculate_spherical(
            self.rgd, nt_sg, self.vxct_g.reshape((1, -1)))

        self.v0r_g = self.vtr_g - self.vHtr_g - self.vxct_g * self.rgd.r_g
        self.v0r_g[self.rgd.round(self.rcmax):] = 0.0

    def summarize(self):
        self.log('\nProjectors:')
        self.log(' state  occ         energy             norm        rcut')
        self.log(' nl            [Hartree]  [eV]      [electrons]   [Bohr]')
        self.log('----------------------------------------------------------')
        for l, waves in enumerate(self.waves_l):
            for n, e, f, ds in zip(waves.n_n, waves.e_n, waves.f_n,
                                   waves.dS_nn.diagonal()):
                if n == -1:
                    self.log('  %s         %10.6f %10.5f   %19.2f' %
                             ('spdf'[l], e, e * Hartree, waves.rcut))
                else:
                    self.log(
                        ' %d%s   %5.2f %10.6f %10.5f      %5.3f  %9.2f' %
                        (n, 'spdf'[l], f, e, e * Hartree, 1 - ds, waves.rcut))
        self.log()

    def construct_projectors(self, rcore):
        for waves in self.waves_l:
            waves.construct_projectors(self.vtr_g, 2.45 * self.rcmax)
            waves.calculate_kinetic_energy_correction(self.aea.vr_sg[0],
                                                      self.vtr_g)

    def check_all(self):
        self.log(('Checking eigenvalues of %s pseudo atom using ' +
                  'a Gaussian basis set:') % self.aea.symbol)
        self.log('                 AE [eV]        PS [eV]      error [eV]')

        ok = True

        for l in range(4):
            try:
                e_b = self.check(l)
            except RuntimeError:
                self.log('Singular overlap matrix!')
                ok = False
                continue

            n0 = self.number_of_core_states(l)

            if l < len(self.aea.channels):
                e0_b = self.aea.channels[l].e_n
                extra = 6
                nae = len(self.aea.channels[l].f_n)
                for n in range(1 + l, nae + 1 + l + extra):
                    if n - 1 - l < nae:
                        f = self.aea.channels[l].f_n[n - 1 - l]
                        self.log('%2d%s  %2d' % (n, 'spdf'[l], f), end='')
                    else:
                        self.log('       ', end='')
                    self.log('  %15.3f' % (e0_b[n - 1 - l] * Hartree), end='')
                    if n - 1 - l - n0 >= 0:
                        self.log('%15.3f' * 2 %
                                 (e_b[n - 1 - l - n0] * Hartree,
                                  (e_b[n - 1 - l - n0] - e0_b[n - 1 - l]) *
                                  Hartree))
                    else:
                        self.log()

                errors = abs(e_b[:nae - n0] - e0_b[n0:nae])
                if (errors > 2e-3).any():
                    self.log('Error in bound %s-states!' % 'spdf'[l])
                    ok = False
                errors = abs(e_b[nae - n0:nae - n0 + extra] -
                             e0_b[nae:nae + extra])
                if (not self.aea.scalar_relativistic
                        and (errors > 2e-2).any()):
                    self.log('Error in %s-states!' % 'spdf'[l])
                    ok = False

        return ok

    def number_of_core_states(self, l):
        n0 = 0
        if l < len(self.waves_l):
            waves = self.waves_l[l]
            if len(waves) > 0:
                n0 = waves.n_n[0] - l - 1
                if n0 < 0 and l < len(self.aea.channels):
                    n0 = (self.aea.channels[l].f_n > 0).sum()
        elif l < len(self.aea.channels):
            n0 = (self.aea.channels[l].f_n > 0).sum()
        return n0

    def check(self, l):
        basis = self.aea.channels[0].basis
        eps = basis.eps
        alpha_B = basis.alpha_B

        basis = GaussianBasis(l, alpha_B, self.rgd, eps)
        H_bb = basis.calculate_potential_matrix(self.vtr_g)
        H_bb += basis.T_bb
        S_bb = np.eye(len(basis))

        if l < len(self.waves_l):
            waves = self.waves_l[l]
            if len(waves) > 0:
                P_bn = self.rgd.integrate(
                    basis.basis_bg[:, None] * waves.pt_ng) / (4 * pi)
                H_bb += np.dot(np.dot(P_bn, waves.dH_nn), P_bn.T)
                S_bb += np.dot(np.dot(P_bn, waves.dS_nn), P_bn.T)

        e_b = np.empty(len(basis))
        general_diagonalize(H_bb, e_b, S_bb)
        return e_b

    def test_convergence(self):
        rgd = self.rgd
        r_g = rgd.r_g
        G_k, nt_k = self.rgd.fft(self.nt_g * r_g)
        rhot_k = self.rgd.fft(self.rhot_g * r_g)[1]
        ghat_k = self.rgd.fft(self.ghat_g * r_g)[1]
        vt_k = self.rgd.fft(self.vtr_g)[1]
        phi_k = self.rgd.fft(self.waves_l[0].phit_ng[0] * r_g)[1]
        eee_k = 0.5 * nt_k**2 * (4 * pi)**2 / (2 * pi)**3
        ecc_k = 0.5 * rhot_k**2 * (4 * pi)**2 / (2 * pi)**3
        egg_k = 0.5 * ghat_k**2 * (4 * pi)**2 / (2 * pi)**3
        ekin_k = 0.5 * phi_k**2 * G_k**4 / (2 * pi)**3
        evt_k = nt_k * vt_k * G_k**2 * 4 * pi / (2 * pi)**3

        eee = 0.5 * rgd.integrate(self.nt_g * rgd.poisson(self.nt_g), -1)
        ecc = 0.5 * rgd.integrate(self.rhot_g * self.vHtr_g, -1)
        egg = 0.5 * rgd.integrate(self.ghat_g * rgd.poisson(self.ghat_g), -1)
        ekin = self.aea.ekin - self.ekincore - self.waves_l[0].dekin_nn[0, 0]
        evt = rgd.integrate(self.nt_g * self.vtr_g, -1)

        import pylab as p

        errors = 10.0**np.arange(-4, 0) / Hartree
        self.log('\nConvergence of energy:')
        self.log('plane-wave cutoff (wave-length) [ev (Bohr)]\n  ', end='')
        for de in errors:
            self.log('%14.4f' % (de * Hartree), end='')
        for label, e_k, e0 in [('e-e', eee_k, eee), ('c-c', ecc_k, ecc),
                               ('g-g', egg_k, egg), ('kin', ekin_k, ekin),
                               ('vt', evt_k, evt)]:
            self.log('\n%3s: ' % label, end='')
            e_k = (np.add.accumulate(e_k) - 0.5 * e_k[0] - 0.5 * e_k) * G_k[1]
            k = len(e_k) - 1
            for de in errors:
                while abs(e_k[k] - e_k[-1]) < de:
                    k -= 1
                G = k * G_k[1]
                ecut = 0.5 * G**2
                h = pi / G
                self.log(' %6.1f (%4.2f)' % (ecut * Hartree, h), end='')
            p.semilogy(G_k, abs(e_k - e_k[-1]) * Hartree, label=label)
        self.log()
        p.axis(xmax=20)
        p.xlabel('G')
        p.ylabel('[eV]')
        p.legend()
        p.show()

    def plot(self):
        import matplotlib.pyplot as plt
        r_g = self.rgd.r_g

        plt.figure()
        plt.plot(r_g, self.vxct_g, label='xc')
        plt.plot(r_g[1:], self.v0r_g[1:] / r_g[1:], label='0')
        plt.plot(r_g[1:], self.vHtr_g[1:] / r_g[1:], label='H')
        plt.plot(r_g[1:], self.vtr_g[1:] / r_g[1:], label='ps')
        plt.plot(r_g[1:], self.aea.vr_sg[0, 1:] / r_g[1:], label='ae')
        plt.axis(xmax=2 * self.rcmax,
                 ymin=self.vtr_g[1] / r_g[1],
                 ymax=max(0, (self.v0r_g[1:] / r_g[1:]).max()))
        plt.xlabel('radius [Bohr]')
        plt.ylabel('potential [Ha]')
        plt.legend()

        plt.figure()
        i = 0
        for l, waves in enumerate(self.waves_l):
            for n, e, phi_g, phit_g in zip(waves.n_n, waves.e_n, waves.phi_ng,
                                           waves.phit_ng):
                if n == -1:
                    gc = self.rgd.ceil(waves.rcut)
                    name = '*%s (%.2f Ha)' % ('spdf'[l], e)
                else:
                    gc = len(self.rgd)
                    name = '%d%s (%.2f Ha)' % (n, 'spdf'[l], e)
                plt.plot(r_g[:gc], (phi_g * r_g)[:gc],
                         color=colors[i],
                         label=name)
                plt.plot(r_g[:gc], (phit_g * r_g)[:gc], '--', color=colors[i])
                i += 1
        plt.axis(xmax=3 * self.rcmax)
        plt.xlabel('radius [Bohr]')
        plt.ylabel(r'$r\phi_{n\ell}(r)$')
        plt.legend()

        plt.figure()
        i = 0
        for l, waves in enumerate(self.waves_l):
            for n, e, pt_g in zip(waves.n_n, waves.e_n, waves.pt_ng):
                if n == -1:
                    name = '*%s (%.2f Ha)' % ('spdf'[l], e)
                else:
                    name = '%d%s (%.2f Ha)' % (n, 'spdf'[l], e)
                plt.plot(r_g, pt_g * r_g, color=colors[i], label=name)
                i += 1
        plt.axis(xmax=self.rcmax)
        plt.legend()

    def create_basis_set(self, tailnorm=0.0005, scale=200.0, splitnorm=0.16):
        rgd = self.rgd
        self.basis = Basis(self.aea.symbol, 'dzp', readxml=False, rgd=rgd)

        # We print text to sdtout and put it in the basis-set file
        txt = 'Basis functions:\n'

        # Bound states:
        for l, waves in enumerate(self.waves_l):
            for i, n in enumerate(waves.n_n):
                if n > 0:
                    tn = tailnorm
                    if waves.f_n[i] == 0:
                        tn = min(0.05, tn * 20)  # no need for long tail
                    phit_g, ronset, rc, de = self.create_basis_function(
                        l, i, tn, scale)
                    bf = BasisFunction(n, l, rc, phit_g, 'bound state')
                    self.basis.append(bf)

                    txt += '%d%s bound state:\n' % (n, 'spdf'[l])
                    txt += ('  cutoff: %.3f to %.3f Bohr (tail-norm=%f)\n' %
                            (ronset, rc, tn))
                    txt += '  eigenvalue shift: %.3f eV\n' % (de * Hartree)

        # Split valence:
        for l, waves in enumerate(self.waves_l):
            # Find the largest n that is occupied:
            n0 = None
            for f, n in zip(waves.f_n, waves.n_n):
                if n > 0 and f > 0:
                    n0 = n
            if n0 is None:
                continue

            for bf in self.basis.bf_j:
                if bf.l == l and bf.n == n0:
                    break

            # Radius and l-value used for polarization function below:
            rcpol = bf.rc
            lpol = l + 1

            phit_g = bf.phit_g

            # Find cutoff radius:
            n_g = np.add.accumulate(phit_g**2 * rgd.r_g**2 * rgd.dr_g)
            norm = n_g[-1]
            gc = (norm - n_g > splitnorm * norm).sum()
            rc = rgd.r_g[gc]

            phit2_g = rgd.pseudize(phit_g, gc, l, 2)[0]  # "split valence"
            bf = BasisFunction(n, l, rc, phit_g - phit2_g, 'split valence')
            self.basis.append(bf)

            txt += '%d%s split valence:\n' % (n0, 'spdf'[l])
            txt += '  cutoff: %.3f Bohr (tail-norm=%f)\n' % (rc, splitnorm)

        # Polarization:
        gcpol = rgd.round(rcpol)
        alpha = 1 / (0.25 * rcpol)**2

        # Gaussian that is continuous and has a continuous derivative at rcpol:
        phit_g = np.exp(-alpha * rgd.r_g**2) * rgd.r_g**lpol
        phit_g -= rgd.pseudize(phit_g, gcpol, lpol, 2)[0]
        phit_g[gcpol:] = 0.0

        bf = BasisFunction(None, lpol, rcpol, phit_g, 'polarization')
        self.basis.append(bf)
        txt += 'l=%d polarization functions:\n' % lpol
        txt += '  cutoff: %.3f Bohr (r^%d exp(-%.3f*r^2))\n' % (rcpol, lpol,
                                                                alpha)

        self.log(txt)

        # Write basis-set file:
        self.basis.generatordata = txt
        self.basis.generatorattrs.update(
            dict(tailnorm=tailnorm, scale=scale, splitnorm=splitnorm))
        self.basis.name = '%de.dzp' % self.nvalence

        return self.basis

    def create_basis_function(self, l, n, tailnorm, scale):
        rgd = self.rgd
        waves = self.waves_l[l]

        # Find cutoff radii:
        n_g = np.add.accumulate(waves.phit_ng[n]**2 * rgd.r_g**2 * rgd.dr_g)
        norm = n_g[-1]
        g2 = (norm - n_g > tailnorm * norm).sum()
        r2 = rgd.r_g[g2]
        r1 = max(0.6 * r2, waves.rcut)
        g1 = rgd.ceil(r1)
        # Set up confining potential:
        r = rgd.r_g[g1:g2]
        vtr_g = self.vtr_g.copy()
        vtr_g[g1:g2] += scale * np.exp((r2 - r1) / (r1 - r)) / (r - r2)**2
        vtr_g[g2:] = np.inf

        # Nonlocal PAW stuff:
        pt_ng = waves.pt_ng
        dH_nn = waves.dH_nn
        dS_nn = waves.dS_nn
        N = len(pt_ng)

        u_g = rgd.zeros()
        u_ng = rgd.zeros(N)
        duodr_n = np.empty(N)
        a_n = np.empty(N)

        e = waves.e_n[n]
        e0 = e
        ch = Channel(l)
        while True:
            duodr, a = ch.integrate_outwards(u_g, rgd, vtr_g, g1, e)

            for n in range(N):
                duodr_n[n], a_n[n] = ch.integrate_outwards(u_ng[n],
                                                           rgd,
                                                           vtr_g,
                                                           g1,
                                                           e,
                                                           pt_g=pt_ng[n])

            A_nn = (dH_nn - e * dS_nn) / (4 * pi)
            B_nn = rgd.integrate(pt_ng[:, None] * u_ng, -1)
            c_n = rgd.integrate(pt_ng * u_g, -1)
            d_n = np.linalg.solve(
                np.dot(A_nn, B_nn) + np.eye(N), np.dot(A_nn, c_n))
            u_g[:g1 + 1] -= np.dot(d_n, u_ng[:, :g1 + 1])
            a -= np.dot(d_n, a_n)
            duodr -= np.dot(duodr_n, d_n)
            uo = u_g[g1]

            duidr = ch.integrate_inwards(u_g, rgd, vtr_g, g1, e, gmax=g2)
            ui = u_g[g1]
            A = duodr / uo - duidr / ui
            u_g[g1:] *= uo / ui
            x = (norm / rgd.integrate(u_g**2, -2) * (4 * pi))**0.5
            u_g *= x
            a *= x

            if abs(A) < 1e-5:
                break

            e += 0.5 * A * u_g[g1]**2

        u_g[1:] /= rgd.r_g[1:]
        u_g[0] = a * 0.0**l
        return u_g, r1, r2, e - e0

    def logarithmic_derivative(self, l, energies, rcut):
        rgd = self.rgd
        ch = Channel(l)
        gcut = rgd.round(rcut)

        N = 0
        if l < len(self.waves_l):
            # Nonlocal PAW stuff:
            waves = self.waves_l[l]
            if len(waves) > 0:
                pt_ng = waves.pt_ng
                dH_nn = waves.dH_nn
                dS_nn = waves.dS_nn
                N = len(pt_ng)

        u_g = rgd.zeros()
        u_ng = rgd.zeros(N)
        dudr_n = np.empty(N)

        logderivs = []
        d0 = 42.0
        offset = 0
        for e in energies:
            dudr = ch.integrate_outwards(u_g, rgd, self.vtr_g, gcut, e)[0]
            u = u_g[gcut]

            if N:
                for n in range(N):
                    dudr_n[n] = ch.integrate_outwards(u_ng[n],
                                                      rgd,
                                                      self.vtr_g,
                                                      gcut,
                                                      e,
                                                      pt_g=pt_ng[n])[0]

                A_nn = (dH_nn - e * dS_nn) / (4 * pi)
                B_nn = rgd.integrate(pt_ng[:, None] * u_ng, -1)
                c_n = rgd.integrate(pt_ng * u_g, -1)
                d_n = np.linalg.solve(
                    np.dot(A_nn, B_nn) + np.eye(N), np.dot(A_nn, c_n))
                u -= np.dot(u_ng[:, gcut], d_n)
                dudr -= np.dot(dudr_n, d_n)

            d1 = np.arctan(dudr / u) / pi + offset
            if d1 > d0:
                offset -= 1
                d1 -= 1
            logderivs.append(d1)
            d0 = d1

        return np.array(logderivs)

    def make_paw_setup(self, tag=None):
        aea = self.aea

        from gpaw.setup_data import SetupData
        setup = SetupData(aea.symbol, aea.xc.name, tag, readxml=False)

        setup.id_j = []

        J = []  # new reordered j and i indices
        I = []

        # Bound states:
        j = 0
        i = 0
        for l, waves in enumerate(self.waves_l):
            for n, f, e, phi_g, phit_g, pt_g in zip(waves.n_n, waves.f_n,
                                                    waves.e_n, waves.phi_ng,
                                                    waves.phit_ng,
                                                    waves.pt_ng):
                if n != -1:
                    setup.append(n, l, f, e, waves.rcut, phi_g, phit_g, pt_g)
                    id = '%d%s' % (n, 'spdf'[l])
                    setup.id_j.append(id)
                    J.append(j)
                    I.extend(range(i, i + 2 * l + 1))
                j += 1
                i += 2 * l + 1

        # Excited states:
        j = 0
        i = 0
        for l, waves in enumerate(self.waves_l):
            ne = 0
            for n, f, e, phi_g, phit_g, pt_g in zip(waves.n_n, waves.f_n,
                                                    waves.e_n, waves.phi_ng,
                                                    waves.phit_ng,
                                                    waves.pt_ng):
                if n == -1:
                    setup.append(n, l, f, e, waves.rcut, phi_g, phit_g, pt_g)
                    ne += 1
                    id = '%s%d' % ('spdf'[l], ne)
                    setup.id_j.append(id)
                    J.append(j)
                    I.extend(range(i, i + 2 * l + 1))
                j += 1
                i += 2 * l + 1

        nj = sum(len(waves) for waves in self.waves_l)
        e_kin_jj = np.zeros((nj, nj))
        j1 = 0
        for waves in self.waves_l:
            j2 = j1 + len(waves)
            e_kin_jj[j1:j2, j1:j2] = waves.dekin_nn
            j1 = j2
        setup.e_kin_jj = e_kin_jj[J][:, J].copy()

        setup.nc_g = self.nc_g * sqrt(4 * pi)
        setup.nct_g = self.nct_g * sqrt(4 * pi)
        setup.e_kinetic_core = self.ekincore
        setup.vbar_g = self.v0r_g * sqrt(4 * pi)
        setup.vbar_g[1:] /= self.rgd.r_g[1:]
        setup.vbar_g[0] = setup.vbar_g[1]
        setup.Z = aea.Z
        setup.Nc = self.ncore
        setup.Nv = self.nvalence
        setup.e_kinetic = aea.ekin
        setup.e_xc = aea.exc
        setup.e_electrostatic = aea.eH + aea.eZ
        setup.e_total = aea.exc + aea.ekin + aea.eH + aea.eZ
        setup.rgd = self.rgd
        setup.rcgauss = 1 / sqrt(self.alpha)

        self.calculate_exx_integrals()
        setup.ExxC = self.exxcc
        setup.X_p = pack2(self.exxcv_ii[I][:, I])

        setup.tauc_g = self.tauc_g * (4 * pi)**0.5
        setup.tauct_g = self.tauct_g * (4 * pi)**0.5

        if self.aea.scalar_relativistic:
            reltype = 'scalar-relativistic'
        else:
            reltype = 'non-relativistic'
        attrs = [('type', reltype), ('version', 2),
                 ('name', 'gpaw-%s' % version)]
        setup.generatorattrs = attrs

        setup.l0 = self.l0
        setup.e0 = 0.0
        setup.r0 = self.r0
        setup.nderiv0 = self.nderiv0

        setup.basis = self.basis

        if self.core_hole:
            n, l, occ = self.core_hole
            phi_g = self.aea.channels[l].phi_ng[n - l - 1]
            setup.ncorehole = n
            setup.lcorehole = l
            setup.fcorehole = occ
            setup.phicorehole_g = phi_g
            setup.has_corehole = True

        return setup

    def calculate_exx_integrals(self):
        # Find core states:
        core = []
        lmax = 0
        for l, ch in enumerate(self.aea.channels):
            for n, phi_g in enumerate(ch.phi_ng):
                if (l >= len(self.waves_l)
                        or (l < len(self.waves_l)
                            and n + l + 1 not in self.waves_l[l].n_n)):
                    core.append((l, phi_g))
                    if l > lmax:
                        lmax = l

        lmax = max(lmax, len(self.waves_l) - 1)
        G_LLL = gaunt(lmax)

        # Calculate core contribution to EXX energy:
        self.exxcc = 0.0
        j1 = 0
        for l1, phi1_g in core:
            f = 1.0
            for l2, phi2_g in core[j1:]:
                n_g = phi1_g * phi2_g
                for l in range((l1 + l2) % 2, l1 + l2 + 1, 2):
                    G = (G_LLL[l1**2:(l1 + 1)**2, l2**2:(l2 + 1)**2,
                               l**2:(l + 1)**2]**2).sum()
                    vr_g = self.rgd.poisson(n_g, l)
                    e = f * self.rgd.integrate(vr_g * n_g, -1) / 4 / pi
                    self.exxcc -= e * G
                f = 2.0
            j1 += 1

        self.log('EXX (core-core):', self.exxcc, 'Hartree')

        # Calculate core-valence contribution to EXX energy:
        ni = sum(
            len(waves) * (2 * l + 1) for l, waves in enumerate(self.waves_l))

        self.exxcv_ii = np.zeros((ni, ni))

        i1 = 0
        for l1, waves1 in enumerate(self.waves_l):
            for phi1_g in waves1.phi_ng:
                i2 = 0
                for l2, waves2 in enumerate(self.waves_l):
                    for phi2_g in waves2.phi_ng:
                        X_mm = self.exxcv_ii[i1:i1 + 2 * l1 + 1,
                                             i2:i2 + 2 * l2 + 1]
                        if (l1 + l2) % 2 == 0:
                            for lc, phi_g in core:
                                n_g = phi1_g * phi_g
                                for l in range((l1 + lc) % 2,
                                               max(l1, l2) + lc + 1, 2):
                                    vr_g = self.rgd.poisson(phi2_g * phi_g, l)
                                    e = (self.rgd.integrate(vr_g * n_g, -1) /
                                         (4 * pi))
                                    for mc in range(2 * lc + 1):
                                        for m in range(2 * l + 1):
                                            G_L = G_LLL[:, lc**2 + mc,
                                                        l**2 + m]
                                            X_mm += np.outer(
                                                G_L[l1**2:(l1 + 1)**2],
                                                G_L[l2**2:(l2 + 1)**2]) * e
                        i2 += 2 * l2 + 1
                i1 += 2 * l1 + 1
class PAWSetupGenerator:
    def __init__(self, aea, projectors,
                 scalar_relativistic=False,
                 core_hole=None,
                 fd=None):
        """fd: stream
            Text output."""

        self.aea = aea

        self.fd = fd or sys.stdout

        if core_hole:
            state, occ = core_hole.split(',')
            n = int(state[0])
            l = 'spdf'.find(state[1])
            occ = float(occ)
            aea.add(n, l, -occ)
            self.core_hole = (n, l, occ)
        else:
            self.core_hole = None

        if projectors[-1].isupper():
            self.l0 = 'SPDFG'.find(projectors[-1])
            projectors = projectors[:-2]
        else:
            self.l0 = None

        self.lmax = -1
        self.states = {}
        for s in projectors.split(','):
            l = 'spdf'.find(s[-1])
            if len(s) == 1:
                n = None
            elif '.' in s:
                n = float(s[:-1])
            else:
                n = int(s[:-1])
            if l in self.states:
                self.states[l].append(n)
            else:
                self.states[l] = [n]
            if l > self.lmax:
                self.lmax = l

        # Add empty bound states:
        for l, nn in self.states.items():
            for n in nn:
                if (isinstance(n, int) and
                    (l not in aea.f_lsn or n - l > len(aea.f_lsn[l][0]))):
                    aea.add(n, l, 0)

        aea.initialize()
        aea.run()
        aea.scalar_relativistic = scalar_relativistic
        aea.refine()

        self.rgd = aea.rgd

        self.vtr_g = None

        self.basis = None

        self.log('\nGenerating PAW', aea.xc.name, 'setup for', aea.symbol)

    def construct_shape_function(self, alpha=None, rc=None, eps=1e-10):
        """Build shape-function for compensation charge."""

        self.alpha = alpha

        if self.alpha is None:
            if isinstance(rc, list):
                rc = min(rc)
            rc = 1.5 * rc

            def spillage(alpha):
                """Fraction of gaussian charge outside rc."""
                x = alpha * rc**2
                return 1 - erf(sqrt(x)) + 2 * sqrt(x / pi) * exp(-x)

            def f(alpha):
                return log(spillage(alpha)) - log(eps)

            if scipy_version < '0.8':
                self.alpha = fsolve(f, 7.0)
            else:
                self.alpha = fsolve(f, 7.0)[0]

            self.alpha = round(self.alpha, 1)

        self.log('Shape function: exp(-alpha*r^2), alpha=%.1f Bohr^-2' %
                 self.alpha)

        self.ghat_g = (np.exp(-self.alpha * self.rgd.r_g**2) *
                       (self.alpha / pi)**1.5)

    def log(self, *args, **kwargs):
        print(file=self.fd, *args, **kwargs)

    def calculate_core_density(self):
        self.nc_g = self.rgd.zeros()
        self.tauc_g = self.rgd.zeros()
        self.ncore = 0
        self.nvalence = 0
        self.ekincore = 0.0
        for l, ch in enumerate(self.aea.channels):
            for n, f in enumerate(ch.f_n):
                if (l <= self.lmax and
                    any(n + l + 1 == nn
                        for nn in self.states[l]
                        if isinstance(nn, int))):
                    self.nvalence += f
                else:
                    self.nc_g += f * ch.calculate_density(n)
                    self.tauc_g += f * ch.calculate_kinetic_energy_density(n)
                    self.ncore += f
                    self.ekincore += f * ch.e_n[n]

        self.ekincore -= self.rgd.integrate(self.nc_g * self.aea.vr_sg[0], -1)

        self.log('Core electrons:', self.ncore)
        self.log('Valence electrons:', self.nvalence)

    def add_waves(self, rc):
        if isinstance(rc, float):
            radii = [rc]
        else:
            radii = rc

        self.rcmax = max(radii)

        if self.lmax >= 0:
            radii += [radii[-1]] * (self.lmax + 1 - len(radii))

        self.waves_l = []
        for l in range(self.lmax + 1):
            rcut = radii[l]
            waves = PAWWaves(self.rgd, l, rcut)
            e = -1.0
            for n in self.states[l]:
                if isinstance(n, int):
                    # Bound state:
                    ch = self.aea.channels[l]
                    e = ch.e_n[n - l - 1]
                    f = ch.f_n[n - l - 1]
                    phi_g = ch.phi_ng[n - l - 1]
                else:
                    if n is None:
                        e += 1.0
                    else:
                        e = n
                    n = -1
                    f = 0.0
                    phi_g = self.rgd.zeros()
                    gc = self.rgd.round(1.5 * rcut)
                    ch = Channel(l)
                    a = ch.integrate_outwards(phi_g, self.rgd,
                                              self.aea.vr_sg[0], gc, e,
                                              self.aea.scalar_relativistic,
                                              self.aea.Z)[1]
                    phi_g[1:gc + 1] /= self.rgd.r_g[1:gc + 1]
                    phi_g[0] = a
                    phi_g /= (self.rgd.integrate(phi_g**2) / (4 * pi))**0.5

                waves.add(phi_g, n, e, f)
            self.waves_l.append(waves)

    def pseudize(self, type='poly', nderiv=6, rcore=None):
        self.Q = -self.aea.Z + self.ncore

        self.nt_g = self.rgd.zeros()
        for waves in self.waves_l:
            waves.pseudize(type, nderiv)
            self.nt_g += waves.nt_g
            self.Q += waves.Q

        if rcore is None:
            rcore = self.rcmax * 0.8
        else:
            assert rcore <= self.rcmax

        if self.ncore == 0:
            self.nct_g = self.rgd.zeros()
            self.tauct_g = self.rgd.zeros()
        else:
            # Make sure pseudo density is monotonically decreasing:
            while 1:
                gcore = self.rgd.round(rcore)
                self.nct_g = self.rgd.pseudize(self.nc_g, gcore)[0]
                nt_g = self.nt_g + self.nct_g
                dntdr_g = self.rgd.derivative(nt_g)[:gcore]
                if dntdr_g.max() < 0.0:
                    break
                rcore -= 0.01

            rcore *= 1.2
            gcore = self.rgd.round(rcore)
            self.nct_g = self.rgd.pseudize(self.nc_g, gcore)[0]
            nt_g = self.nt_g + self.nct_g

            self.log('Constructing smooth pseudo core density for r < %.3f' %
                     rcore)
            self.nt_g = nt_g

            self.tauct_g = self.rgd.pseudize(self.tauc_g, gcore)[0]

        self.npseudocore = self.rgd.integrate(self.nct_g)
        self.log('Pseudo core electrons: %.6f' % self.npseudocore)
        self.Q -= self.npseudocore

        self.rhot_g = self.nt_g + self.Q * self.ghat_g
        self.vHtr_g = self.rgd.poisson(self.rhot_g)

        self.vxct_g = self.rgd.zeros()
        nt_sg = self.nt_g.reshape((1, -1))
        self.exct = self.aea.xc.calculate_spherical(
            self.rgd, nt_sg, self.vxct_g.reshape((1, -1)))

        self.v0r_g = self.vtr_g - self.vHtr_g - self.vxct_g * self.rgd.r_g
        self.v0r_g[self.rgd.round(self.rcmax):] = 0.0

        self.log('\nProjectors:')
        self.log(' state  occ         energy             norm        rcut')
        self.log(' nl            [Hartree]  [eV]      [electrons]   [Bohr]')
        self.log('----------------------------------------------------------')
        for l, waves in enumerate(self.waves_l):
            for n, e, f, ds in zip(waves.n_n, waves.e_n, waves.f_n,
                                   waves.dS_nn.diagonal()):
                if n == -1:
                    self.log('  %s         %10.6f %10.5f   %19.2f' %
                             ('spdf'[l], e, e * Hartree, waves.rcut))
                else:
                    self.log(
                        ' %d%s   %5.2f %10.6f %10.5f      %5.3f  %9.2f' %
                        (n, 'spdf'[l], f, e, e * Hartree, 1 - ds,
                         waves.rcut))
        self.log()

    def find_local_potential(self, r0, P):
        self.r0 = r0
        self.nderiv0 = P
        if self.l0 is None:
            self.find_polynomial_potential(r0, P)
        else:
            self.match_local_potential(r0, P)

    def find_polynomial_potential(self, r0, P):
        self.log('Constructing smooth local potential for r < %.3f' % r0)
        g0 = self.rgd.ceil(r0)

        r_g = self.rgd.r_g
        if 1:
            self.vtr_g = self.rgd.pseudize(self.aea.vr_sg[0], g0, 1, P)[0]
        else:
            # Use bessel function:
            r0 = r_g[g0]
            v_g = self.aea.vr_sg[0, g0 - 1:g0 + 2] / r_g[g0 - 1:g0 + 2]
            v0 = v_g[1]
            v1 = (v_g[2] - v_g[0]) / 2 / self.rgd.dr_g[g0]

            def f(x):
                return x / np.tan(x) - 1 - v1 * r0 / v0
            
            q = root(f, 2).x / r0
            A = v0 * r0 / np.sin(q * r0)
            self.vtr_g = self.aea.vr_sg[0].copy()
            self.vtr_g[:g0] = A * np.sin(q * r_g[:g0])

    def match_local_potential(self, r0, P):
        l0 = self.l0
        self.log('Local potential matching %s-scattering at e=0.0 eV' %
                 'spdfg'[l0] + ' and r=%.2f Bohr' % r0)

        g0 = self.rgd.ceil(r0)
        gc = g0 + 20

        e0 = 0.0

        ch = Channel(l0)
        phi_g = self.rgd.zeros()
        a = ch.integrate_outwards(phi_g, self.rgd, self.aea.vr_sg[0], gc, e0,
                                  self.aea.scalar_relativistic, self.aea.Z)[1]
        phi_g[1:gc] /= self.rgd.r_g[1:gc]
        phi_g[0] = a

        phit_g, c = self.rgd.pseudize(phi_g, g0, l=l0, points=P)

        dgdr_g = 1 / self.rgd.dr_g
        d2gdr2_g = self.rgd.d2gdr2()
        a_g = phit_g.copy()
        a_g[1:] /= self.rgd.r_g[1:]**l0
        a_g[0] = c
        dadg_g = self.rgd.zeros()
        d2adg2_g = self.rgd.zeros()
        dadg_g[1:-1] = 0.5 * (a_g[2:] - a_g[:-2])
        d2adg2_g[1:-1] = a_g[2:] - 2 * a_g[1:-1] + a_g[:-2]
        q_g = (((l0 + 1) * dgdr_g + 0.5 * self.rgd.r_g * d2gdr2_g) * dadg_g +
               0.5 * self.rgd.r_g * d2adg2_g * dgdr_g**2)
        q_g[:g0] /= a_g[:g0]
        q_g += e0 * self.rgd.r_g
        q_g[0] = 0.0

        self.vtr_g = self.aea.vr_sg[0].copy()
        self.vtr_g[0] = 0.0
        self.vtr_g[1:g0] = q_g[1:g0]

    def construct_projectors(self):
        for waves in self.waves_l:
            waves.construct_projectors(self.vtr_g, self.rcmax)
            waves.calculate_kinetic_energy_correction(self.aea.vr_sg[0],
                                                      self.vtr_g)

    def check_all(self):
        self.log(('Checking eigenvalues of %s pseudo atom using ' +
                  'a Gaussian basis set:') % self.aea.symbol)
        self.log('                 AE [eV]        PS [eV]      error [eV]')

        ok = True

        for l in range(4):
            try:
                e_b, n0 = self.check(l)
            except RuntimeError:
                self.log('Singular overlap matrix!')
                ok = False
                continue

            nbound = (e_b < -0.002).sum()

            if l < len(self.aea.channels):
                e0_b = self.aea.channels[l].e_n
                nbound0 = (e0_b < -0.002).sum()
                extra = 6
                for n in range(1 + l, nbound0 + 1 + l + extra):
                    if n - 1 - l < len(self.aea.channels[l].f_n):
                        f = self.aea.channels[l].f_n[n - 1 - l]
                        self.log('%2d%s  %2d' % (n, 'spdf'[l], f), end='')
                    else:
                        self.log('       ', end='')
                    self.log('  %15.3f' % (e0_b[n - 1 - l] * Hartree), end='')
                    if n - 1 - l - n0 >= 0:
                        self.log('%15.3f' * 2 %
                                 (e_b[n - 1 - l - n0] * Hartree,
                                  (e_b[n - 1 - l - n0] - e0_b[n - 1 - l]) *
                                  Hartree))
                    else:
                        self.log()

                if nbound != nbound0 - n0:
                    self.log('Wrong number of %s-states!' % 'spdf'[l])
                    ok = False
                elif (nbound > 0 and
                      abs(e_b[:nbound] - e0_b[n0:nbound0]).max() > 1e-3):
                    self.log('Error in bound %s-states!' % 'spdf'[l])
                    ok = False
                elif (abs(e_b[nbound:nbound + extra] -
                          e0_b[nbound0:nbound0 + extra]).max() > 2e-2):
                    self.log('Error in %s-states!' % 'spdf'[l])
                    ok = False
            elif nbound > 0:
                self.log('Wrong number of %s-states!' % 'spdf'[l])
                ok = False

        return ok

    def check(self, l):
        basis = self.aea.channels[0].basis
        eps = basis.eps
        alpha_B = basis.alpha_B

        basis = GaussianBasis(l, alpha_B, self.rgd, eps)
        H_bb = basis.calculate_potential_matrix(self.vtr_g)
        H_bb += basis.T_bb
        S_bb = np.eye(len(basis))

        n0 = 0
        if l < len(self.waves_l):
            waves = self.waves_l[l]
            if len(waves) > 0:
                P_bn = self.rgd.integrate(basis.basis_bg[:, None] *
                                          waves.pt_ng) / (4 * pi)
                H_bb += np.dot(np.dot(P_bn, waves.dH_nn), P_bn.T)
                S_bb += np.dot(np.dot(P_bn, waves.dS_nn), P_bn.T)
                n0 = waves.n_n[0] - l - 1
                if n0 < 0 and l < len(self.aea.channels):
                    n0 = (self.aea.channels[l].f_n > 0).sum()
        elif l < len(self.aea.channels):
            n0 = (self.aea.channels[l].f_n > 0).sum()

        e_b = np.empty(len(basis))
        general_diagonalize(H_bb, e_b, S_bb)
        return e_b, n0

    def test_convergence(self):
        rgd = self.rgd
        r_g = rgd.r_g
        G_k, nt_k = self.rgd.fft(self.nt_g * r_g)
        rhot_k = self.rgd.fft(self.rhot_g * r_g)[1]
        ghat_k = self.rgd.fft(self.ghat_g * r_g)[1]
        vt_k = self.rgd.fft(self.vtr_g)[1]
        phi_k = self.rgd.fft(self.waves_l[0].phit_ng[0] * r_g)[1]
        eee_k = 0.5 * nt_k**2 * (4 * pi)**2 / (2 * pi)**3
        ecc_k = 0.5 * rhot_k**2 * (4 * pi)**2 / (2 * pi)**3
        egg_k = 0.5 * ghat_k**2 * (4 * pi)**2 / (2 * pi)**3
        ekin_k = 0.5 * phi_k**2 * G_k**4 / (2 * pi)**3
        evt_k = nt_k * vt_k * G_k**2 * 4 * pi / (2 * pi)**3

        eee = 0.5 * rgd.integrate(self.nt_g * rgd.poisson(self.nt_g), -1)
        ecc = 0.5 * rgd.integrate(self.rhot_g * self.vHtr_g, -1)
        egg = 0.5 * rgd.integrate(self.ghat_g * rgd.poisson(self.ghat_g), -1)
        ekin = self.aea.ekin - self.ekincore - self.waves_l[0].dekin_nn[0, 0]
        evt = rgd.integrate(self.nt_g * self.vtr_g, -1)

        import pylab as p

        errors = 10.0**np.arange(-4, 0) / Hartree
        self.log('\nConvergence of energy:')
        self.log('plane-wave cutoff (wave-length) [ev (Bohr)]\n  ', end='')
        for de in errors:
            self.log('%14.4f' % (de * Hartree), end='')
        for label, e_k, e0 in [
            ('e-e', eee_k, eee),
            ('c-c', ecc_k, ecc),
            ('g-g', egg_k, egg),
            ('kin', ekin_k, ekin),
            ('vt', evt_k, evt)]:
            self.log('\n%3s: ' % label, end='')
            e_k = (np.add.accumulate(e_k) - 0.5 * e_k[0] - 0.5 * e_k) * G_k[1]
            k = len(e_k) - 1
            for de in errors:
                while abs(e_k[k] - e_k[-1]) < de:
                    k -= 1
                G = k * G_k[1]
                ecut = 0.5 * G**2
                h = pi / G
                self.log(' %6.1f (%4.2f)' % (ecut * Hartree, h), end='')
            p.semilogy(G_k, abs(e_k - e_k[-1]) * Hartree, label=label)
        self.log()
        p.axis(xmax=20)
        p.xlabel('G')
        p.ylabel('[eV]')
        p.legend()
        p.show()

    def plot(self):
        import matplotlib.pyplot as plt
        r_g = self.rgd.r_g

        plt.figure()
        plt.plot(r_g, self.vxct_g, label='xc')
        plt.plot(r_g[1:], self.v0r_g[1:] / r_g[1:], label='0')
        plt.plot(r_g[1:], self.vHtr_g[1:] / r_g[1:], label='H')
        plt.plot(r_g[1:], self.vtr_g[1:] / r_g[1:], label='ps')
        plt.plot(r_g[1:], self.aea.vr_sg[0, 1:] / r_g[1:], label='ae')
        plt.axis(xmax=2 * self.rcmax,
                 ymin=self.vtr_g[1] / r_g[1],
                 ymax=max(0, (self.v0r_g[1:] / r_g[1:]).max()))
        plt.xlabel('radius [Bohr]')
        plt.ylabel('potential [Ha]')
        plt.legend()

        plt.figure()
        i = 0
        for l, waves in enumerate(self.waves_l):
            for n, e, phi_g, phit_g in zip(waves.n_n, waves.e_n,
                                           waves.phi_ng, waves.phit_ng):
                if n == -1:
                    gc = self.rgd.ceil(waves.rcut)
                    name = '*%s (%.2f Ha)' % ('spdf'[l], e)
                else:
                    gc = len(self.rgd)
                    name = '%d%s (%.2f Ha)' % (n, 'spdf'[l], e)
                plt.plot(r_g[:gc], (phi_g * r_g)[:gc], color=colors[i],
                         label=name)
                plt.plot(r_g[:gc], (phit_g * r_g)[:gc], '--', color=colors[i])
                i += 1
        plt.axis(xmax=3 * self.rcmax)
        plt.xlabel('radius [Bohr]')
        plt.ylabel(r'$r\phi_{n\ell}(r)$')
        plt.legend()

        plt.figure()
        i = 0
        for l, waves in enumerate(self.waves_l):
            for n, e, pt_g in zip(waves.n_n, waves.e_n, waves.pt_ng):
                if n == -1:
                    name = '*%s (%.2f Ha)' % ('spdf'[l], e)
                else:
                    name = '%d%s (%.2f Ha)' % (n, 'spdf'[l], e)
                plt.plot(r_g, pt_g * r_g, color=colors[i], label=name)
                i += 1
        plt.axis(xmax=self.rcmax)
        plt.legend()

    def create_basis_set(self, tailnorm=0.0005, scale=200.0, splitnorm=0.16):
        rgd = self.rgd
        self.basis = Basis(self.aea.symbol, readxml=False, rgd=rgd)

        # We print text to sdtout and put it in the basis-set file
        txt = 'Basis functions:\n'

        # Bound states:
        for l, waves in enumerate(self.waves_l):
            for i, n in enumerate(waves.n_n):
                if n > 0:
                    tn = tailnorm
                    if waves.f_n[i] == 0:
                        tn = min(0.05, tn * 20)  # no need for long tail
                    phit_g, ronset, rc, de = self.create_basis_function(
                        l, i, tn, scale)
                    bf = BasisFunction(n, l, rc, phit_g, 'bound state')
                    self.basis.append(bf)

                    txt += '%d%s bound state:\n' % (n, 'spdf'[l])
                    txt += ('  cutoff: %.3f to %.3f Bohr (tail-norm=%f)\n' %
                            (ronset, rc, tn))
                    txt += '  eigenvalue shift: %.3f eV\n' % (de * Hartree)

        # Split valence:
        for l, waves in enumerate(self.waves_l):
            # Find the largest n that is occupied:
            n0 = None
            for f, n in zip(waves.f_n, waves.n_n):
                if n > 0 and f > 0:
                    n0 = n
            if n0 is None:
                continue

            bf = [bf for bf in self.basis.bf_j if bf.l == l and bf.n == n0][0]

            # Radius and l-value used for polarization function below:
            rcpol = bf.rc
            lpol = l + 1

            phit_g = bf.phit_g

            # Find cutoff radius:
            n_g = np.add.accumulate(phit_g**2 * rgd.r_g**2 * rgd.dr_g)
            norm = n_g[-1]
            gc = (norm - n_g > splitnorm * norm).sum()
            rc = rgd.r_g[gc]

            phit2_g = rgd.pseudize(phit_g, gc, l, 2)[0]  # "split valence"
            bf = BasisFunction(n, l, rc, phit_g - phit2_g, 'split valence')
            self.basis.append(bf)

            txt += '%d%s split valence:\n' % (n0, 'spdf'[l])
            txt += '  cutoff: %.3f Bohr (tail-norm=%f)\n' % (rc, splitnorm)

        # Polarization:
        gcpol = rgd.round(rcpol)
        alpha = 1 / (0.25 * rcpol)**2

        # Gaussian that is continuous and has a continuous derivative at rcpol:
        phit_g = np.exp(-alpha * rgd.r_g**2) * rgd.r_g**lpol
        phit_g -= rgd.pseudize(phit_g, gcpol, lpol, 2)[0]
        phit_g[gcpol:] = 0.0

        bf = BasisFunction(None, lpol, rcpol, phit_g, 'polarization')
        self.basis.append(bf)
        txt += 'l=%d polarization functions:\n' % lpol
        txt += '  cutoff: %.3f Bohr (r^%d exp(-%.3f*r^2))\n' % (rcpol, lpol,
                                                                alpha)

        self.log(txt)

        # Write basis-set file:
        self.basis.generatordata = txt
        self.basis.generatorattrs.update(dict(tailnorm=tailnorm,
                                              scale=scale,
                                              splitnorm=splitnorm))
        self.basis.name = '%de.dzp' % self.nvalence

        return self.basis

    def create_basis_function(self, l, n, tailnorm, scale):
        rgd = self.rgd
        waves = self.waves_l[l]

        # Find cutoff radii:
        n_g = np.add.accumulate(waves.phit_ng[n]**2 * rgd.r_g**2 * rgd.dr_g)
        norm = n_g[-1]
        g2 = (norm - n_g > tailnorm * norm).sum()
        r2 = rgd.r_g[g2]
        r1 = max(0.6 * r2, waves.rcut)
        g1 = rgd.ceil(r1)
        # Set up confining potential:
        r = rgd.r_g[g1:g2]
        vtr_g = self.vtr_g.copy()
        vtr_g[g1:g2] += scale * np.exp((r2 - r1) / (r1 - r)) / (r - r2)**2
        vtr_g[g2:] = np.inf

        # Nonlocal PAW stuff:
        pt_ng = waves.pt_ng
        dH_nn = waves.dH_nn
        dS_nn = waves.dS_nn
        N = len(pt_ng)

        u_g = rgd.zeros()
        u_ng = rgd.zeros(N)
        duodr_n = np.empty(N)
        a_n = np.empty(N)

        e = waves.e_n[n]
        e0 = e
        ch = Channel(l)
        while True:
            duodr, a = ch.integrate_outwards(u_g, rgd, vtr_g, g1, e)

            for n in range(N):
                duodr_n[n], a_n[n] = ch.integrate_outwards(u_ng[n], rgd,
                                                           vtr_g, g1, e,
                                                           pt_g=pt_ng[n])

            A_nn = (dH_nn - e * dS_nn) / (4 * pi)
            B_nn = rgd.integrate(pt_ng[:, None] * u_ng, -1)
            c_n = rgd.integrate(pt_ng * u_g, -1)
            d_n = np.linalg.solve(np.dot(A_nn, B_nn) + np.eye(N),
                                  np.dot(A_nn, c_n))
            u_g[:g1 + 1] -= np.dot(d_n, u_ng[:, :g1 + 1])
            a -= np.dot(d_n, a_n)
            duodr -= np.dot(duodr_n, d_n)
            uo = u_g[g1]

            duidr = ch.integrate_inwards(u_g, rgd, vtr_g, g1, e, gmax=g2)
            ui = u_g[g1]
            A = duodr / uo - duidr / ui
            u_g[g1:] *= uo / ui
            x = (norm / rgd.integrate(u_g**2, -2) * (4 * pi))**0.5
            u_g *= x
            a *= x

            if abs(A) < 1e-5:
                break

            e += 0.5 * A * u_g[g1]**2

        u_g[1:] /= rgd.r_g[1:]
        u_g[0] = a * 0.0**l
        return u_g, r1, r2, e - e0

    def logarithmic_derivative(self, l, energies, rcut):
        rgd = self.rgd
        ch = Channel(l)
        gcut = rgd.round(rcut)

        N = 0
        if l < len(self.waves_l):
            # Nonlocal PAW stuff:
            waves = self.waves_l[l]
            if len(waves) > 0:
                pt_ng = waves.pt_ng
                dH_nn = waves.dH_nn
                dS_nn = waves.dS_nn
                N = len(pt_ng)

        u_g = rgd.zeros()
        u_ng = rgd.zeros(N)
        dudr_n = np.empty(N)

        logderivs = []
        for e in energies:
            dudr = ch.integrate_outwards(u_g, rgd, self.vtr_g, gcut, e)[0]
            u = u_g[gcut]

            if N:
                for n in range(N):
                    dudr_n[n] = ch.integrate_outwards(u_ng[n], rgd,
                                                      self.vtr_g, gcut, e,
                                                      pt_g=pt_ng[n])[0]

                A_nn = (dH_nn - e * dS_nn) / (4 * pi)
                B_nn = rgd.integrate(pt_ng[:, None] * u_ng, -1)
                c_n = rgd.integrate(pt_ng * u_g, -1)
                d_n = np.linalg.solve(np.dot(A_nn, B_nn) + np.eye(N),
                                      np.dot(A_nn, c_n))
                u -= np.dot(u_ng[:, gcut], d_n)
                dudr -= np.dot(dudr_n, d_n)

            logderivs.append(dudr / u)

        return logderivs

    def make_paw_setup(self, tag=None):
        aea = self.aea

        from gpaw.setup_data import SetupData
        setup = SetupData(aea.symbol, aea.xc.name, tag, readxml=False)

        setup.id_j = []

        J = []  # new reordered j and i indices
        I = []

        # Bound states:
        j = 0
        i = 0
        for l, waves in enumerate(self.waves_l):
            for n, f, e, phi_g, phit_g, pt_g in zip(waves.n_n, waves.f_n,
                                                    waves.e_n, waves.phi_ng,
                                                    waves.phit_ng,
                                                    waves.pt_ng):
                if n != -1:
                    setup.append(n, l, f, e, waves.rcut, phi_g, phit_g, pt_g)
                    id = '%d%s' % (n, 'spdf'[l])
                    setup.id_j.append(id)
                    J.append(j)
                    I.extend(range(i, i + 2 * l + 1))
                j += 1
                i += 2 * l + 1

        # Excited states:
        j = 0
        i = 0
        for l, waves in enumerate(self.waves_l):
            ne = 0
            for n, f, e, phi_g, phit_g, pt_g in zip(waves.n_n, waves.f_n,
                                                    waves.e_n, waves.phi_ng,
                                                    waves.phit_ng,
                                                    waves.pt_ng):
                if n == -1:
                    setup.append(n, l, f, e, waves.rcut, phi_g, phit_g, pt_g)
                    ne += 1
                    id = '%s%d' % ('spdf'[l], ne)
                    setup.id_j.append(id)
                    J.append(j)
                    I.extend(range(i, i + 2 * l + 1))
                j += 1
                i += 2 * l + 1

        nj = sum(len(waves) for waves in self.waves_l)
        e_kin_jj = np.zeros((nj, nj))
        j1 = 0
        for waves in self.waves_l:
            j2 = j1 + len(waves)
            e_kin_jj[j1:j2, j1:j2] = waves.dekin_nn
            j1 = j2
        setup.e_kin_jj = e_kin_jj[J][:, J].copy()

        setup.nc_g = self.nc_g * sqrt(4 * pi)
        setup.nct_g = self.nct_g * sqrt(4 * pi)
        setup.e_kinetic_core = self.ekincore
        setup.vbar_g = self.v0r_g * sqrt(4 * pi)
        setup.vbar_g[1:] /= self.rgd.r_g[1:]
        setup.vbar_g[0] = setup.vbar_g[1]
        setup.Z = aea.Z
        setup.Nc = self.ncore
        setup.Nv = self.nvalence
        setup.e_kinetic = aea.ekin
        setup.e_xc = aea.exc
        setup.e_electrostatic = aea.eH + aea.eZ
        setup.e_total = aea.exc + aea.ekin + aea.eH + aea.eZ
        setup.rgd = self.rgd
        setup.rcgauss = 1 / sqrt(self.alpha)

        self.calculate_exx_integrals()
        setup.ExxC = self.exxcc
        setup.X_p = pack2(self.exxcv_ii[I][:, I])

        setup.tauc_g = self.tauc_g * (4 * pi)**0.5
        setup.tauct_g = self.tauct_g * (4 * pi)**0.5

        if self.aea.scalar_relativistic:
            reltype = 'scalar-relativistic'
        else:
            reltype = 'non-relativistic'
        attrs = [('type', reltype),
                 ('version', 2),
                 ('name', 'gpaw-%s' % version)]
        setup.generatorattrs = attrs

        setup.l0 = self.l0
        setup.e0 = 0.0
        setup.r0 = self.r0
        setup.nderiv0 = self.nderiv0

        setup.basis = self.basis

        if self.core_hole:
            n, l, occ = self.core_hole
            phi_g = self.aea.channels[l].phi_ng[n - l - 1]
            setup.ncorehole = n
            setup.lcorehole = l
            setup.fcorehole = occ
            setup.phicorehole_g = phi_g
            setup.has_corehole = True

        return setup

    def calculate_exx_integrals(self):
        # Find core states:
        core = []
        lmax = 0
        for l, ch in enumerate(self.aea.channels):
            for n, phi_g in enumerate(ch.phi_ng):
                if (l >= len(self.waves_l) or
                    (l < len(self.waves_l) and
                     n + l + 1 not in self.waves_l[l].n_n)):
                    core.append((l, phi_g))
                    if l > lmax:
                        lmax = l

        lmax = max(lmax, len(self.waves_l) - 1)
        G_LLL = make_gaunt(lmax)

        # Calculate core contribution to EXX energy:
        self.exxcc = 0.0
        j1 = 0
        for l1, phi1_g in core:
            f = 1.0
            for l2, phi2_g in core[j1:]:
                n_g = phi1_g * phi2_g
                for l in range((l1 + l2) % 2, l1 + l2 + 1, 2):
                    G = (G_LLL[l1**2:(l1 + 1)**2,
                               l2**2:(l2 + 1)**2,
                               l**2:(l + 1)**2]**2).sum()
                    vr_g = self.rgd.poisson(n_g, l)
                    e = f * self.rgd.integrate(vr_g * n_g, -1) / 4 / pi
                    self.exxcc -= e * G
                f = 2.0
            j1 += 1

        self.log('EXX (core-core):', self.exxcc, 'Hartree')

        # Calculate core-valence contribution to EXX energy:
        ni = sum(len(waves) * (2 * l + 1)
                 for l, waves in enumerate(self.waves_l))

        self.exxcv_ii = np.zeros((ni, ni))

        i1 = 0
        for l1, waves1 in enumerate(self.waves_l):
            for phi1_g in waves1.phi_ng:
                i2 = 0
                for l2, waves2 in enumerate(self.waves_l):
                    for phi2_g in waves2.phi_ng:
                        X_mm = self.exxcv_ii[i1:i1 + 2 * l1 + 1,
                                             i2:i2 + 2 * l2 + 1]
                        if (l1 + l2) % 2 == 0:
                            for lc, phi_g in core:
                                n_g = phi1_g * phi_g
                                for l in range((l1 + lc) % 2,
                                               max(l1, l2) + lc + 1, 2):
                                    vr_g = self.rgd.poisson(phi2_g * phi_g, l)
                                    e = (self.rgd.integrate(vr_g * n_g, -1) /
                                         (4 * pi))
                                    for mc in range(2 * lc + 1):
                                        for m in range(2 * l + 1):
                                            G_L = G_LLL[:,
                                                        lc**2 + mc,
                                                        l**2 + m]
                                            X_mm += np.outer(
                                                G_L[l1**2:(l1 + 1)**2],
                                                G_L[l2**2:(l2 + 1)**2]) * e
                        i2 += 2 * l2 + 1
                i1 += 2 * l1 + 1
Exemple #26
0
 def __init__(self, symbol, phit_j):
     Basis.__init__(self, symbol, 'partial-waves', readxml=False)
     self.phit_j = phit_j
Exemple #27
0
    def generate(self, zetacount=2, polarizationcount=1,
                 tailnorm=(0.16, 0.3, 0.6), energysplit=0.1, tolerance=1.0e-3,
                 referencefile=None, referenceindex=None, rcutpol_rel=1.0, 
                 rcutmax=20.0, #ngaussians=None,
                 rcharpol_rel=None,
                 vconf_args=(12.0, 0.6), txt='-',
                 include_energy_derivatives=False,
                 lvalues=None):
        """Generate an entire basis set.

        This is a high-level method which will return a basis set
        consisting of several different basis vector types.

        Parameters:

        ===================== =================================================
        ``zetacount``         Number of basis functions per occupied orbital
        ``polarizationcount`` Number of polarization functions
        ``tailnorm``          List of tail norms for split-valence scheme
        ``energysplit``       Energy increase defining confinement radius (eV)
        ``tolerance``         Tolerance of energy split (eV)
        ``referencefile``     gpw-file used to generate polarization function
        ``referenceindex``    Index in reference system of relevant atom
        ``rcutpol_rel``       Polarization rcut relative to largest other rcut
        ``rcutmax``           No cutoff will be greater than this value
        ``vconf_args``        Parameters (alpha, ri/rc) for conf. potential
        ``txt``               Log filename or '-' for stdout
        ===================== =================================================

        Returns a fully initialized Basis object.
        """

        if txt == '-':
            txt = sys.stdout
        elif txt is None:
            txt = devnull

        if isinstance(tailnorm, float):
            tailnorm = (tailnorm,)
        assert 1 + len(tailnorm) >= max(polarizationcount, zetacount), \
               'Needs %d tail norm values, but only %d are specified' % \
               (max(polarizationcount, zetacount) - 1, len(tailnorm))

        textbuffer = StringIO()
        class TeeStream: # Quick hack to both write and save output
            def __init__(self, out1, out2):
                self.out1 = out1
                self.out2 = out2
            def write(self, string):
                self.out1.write(string)
                self.out2.write(string)
        txt = TeeStream(txt, textbuffer)

        if vconf_args is not None:
            amplitude, ri_rel = vconf_args

        g = self.generator
        rgd = self.rgd

        # Find out all relevant orbitals
        # We'll probably need: s, p and d.
        # The orbitals we want are stored in u_j.
        # Thus we must find the j corresponding to the highest energy of
        # each orbital-type.
        #
        # However not all orbitals in l_j are actually occupied, so we
        # will check the occupations in the generator object's lists
        #
        # ASSUMPTION: The last index of a given value in l_j corresponds
        # exactly to the orbital we want, except those which are not occupied
        #
        # Get (only) one occupied valence state for each l
        # Not including polarization in this list
        if lvalues is None:
            lvalues = np.unique([l for l, f in zip(g.l_j[g.njcore:], 
                                                    g.f_j[g.njcore:])
                                  if f > 0])
            if lvalues[0] != 0: # Always include s-orbital !
                lvalues = np.array([0] + list(lvalues))

        #print energysplit
        if isinstance(energysplit,float):
            energysplit=[energysplit]*(max(lvalues)+1)
            #print energysplit,'~~~~~~~~'
            
            
        title = '%s Basis functions for %s' % (g.xcname, g.symbol)
        print >> txt, title
        print >> txt, '=' * len(title)
        
        j_l = {} # index j by l rather than the other way around
        reversed_l_j = list(g.l_j)
        reversed_l_j.reverse() # the values we want are stored last
        for l in lvalues:
            j = len(reversed_l_j) - reversed_l_j.index(l) - 1
            j_l[l] = j

        singlezetas = []
        energy_derivative_functions = []
        multizetas = [[] for i in range(zetacount - 1)]
        polarization_functions = []

        splitvalencedescr = 'split-valence wave, fixed tail norm'
        derivativedescr = 'derivative of sz wrt. (ri/rc) of potential'

        for l in lvalues:
            # Get one unmodified pseudo-orbital basis vector for each l
            j = j_l[l]
            n = g.n_j[j]
            orbitaltype = str(n) + 'spdf'[l]
            msg = 'Basis functions for l=%d, n=%d' % (l, n)
            print >> txt
            print >> txt, msg + '\n', '-' * len(msg)
            print >> txt
            if vconf_args is None:
                adverb = 'sharply'
            else:
                adverb = 'softly'
            print >> txt, 'Zeta 1: %s confined pseudo wave,' % adverb,

            u, e, de, vconf, rc = self.rcut_by_energy(j, energysplit[l],
                                                      tolerance,
                                                      vconf_args=vconf_args)
            if rc > rcutmax:
                rc = rcutmax # scale things down
                if vconf is not None:
                    vconf = g.get_confinement_potential(amplitude, ri_rel * rc,
                                                        rc)
                u, e = g.solve_confined(j, rc, vconf)
                print >> txt, 'using maximum cutoff'
                print >> txt, 'rc=%.02f Bohr' % rc
            else:
                print >> txt, 'fixed energy shift'    
                print >> txt, 'DE=%.03f eV :: rc=%.02f Bohr' % (de * Hartree,
                                                                rc)
            if vconf is not None:
                print >> txt, ('Potential amp=%.02f :: ri/rc=%.02f' %
                               (amplitude, ri_rel))
            phit_g = self.smoothify(u, l)
            bf = BasisFunction(l, rc, phit_g,
                               '%s-sz confined orbital' % orbitaltype)
            norm = np.dot(g.dr, phit_g * phit_g)**.5
            print >> txt, 'Norm=%.03f' % norm
            singlezetas.append(bf)

            zetacounter = iter(xrange(2, zetacount + 1))

            if include_energy_derivatives:
                assert zetacount > 1
                zeta = zetacounter.next()
                print >> txt, '\nZeta %d: %s' % (zeta, derivativedescr)
                vconf2 = g.get_confinement_potential(amplitude,
                                                     ri_rel * rc * .99, rc)
                u2, e2 = g.solve_confined(j, rc, vconf2)
                
                phit2_g = self.smoothify(u2, l)
                dphit_g = phit2_g - phit_g
                dphit_norm = np.dot(rgd.dr_g, dphit_g * dphit_g) ** .5
                dphit_g /= dphit_norm
                descr = '%s-dz E-derivative of sz' % orbitaltype
                bf = BasisFunction(l, rc, dphit_g, descr)
                energy_derivative_functions.append(bf)

            for i, zeta in enumerate(zetacounter): # range(zetacount - 1):
                print >> txt, '\nZeta %d: %s' % (zeta, splitvalencedescr)
                # Unresolved issue:  how does the lack of normalization
                # of the first function impact the tail norm scheme?
                # Presumably not much, since most interesting stuff happens
                # close to the core.
                rsplit, norm, splitwave = rsplit_by_norm(rgd, l, phit_g,
                                                         tailnorm[i]**2.0,
                                                         txt)
                descr = '%s-%sz split-valence wave' % (orbitaltype,
                                                       '0sdtq56789'[zeta])
                bf = BasisFunction(l, rsplit, phit_g - splitwave, descr)
                multizetas[i].append(bf)
            
        if polarizationcount > 0:
            # Now make up some properties for the polarization orbital
            # We just use the cutoffs from the previous one times a factor
            rcut = max([bf.rc for bf in singlezetas]) * rcutpol_rel
            rcut = min(rcut, rcutmax)
            # Find 'missing' values in lvalues
            for i, l in enumerate(lvalues):
                if i != l:
                    l_pol = i
                    break
            else:
                l_pol = lvalues[-1] + 1
            msg = 'Polarization function: l=%d, rc=%.02f' % (l_pol, rcut)
            print >> txt, '\n' + msg
            print >> txt, '-' * len(msg)
            # Make a single Gaussian for polarization function.
            #
            # It is known that for given l, the sz cutoff defined
            # by some fixed energy is strongly correlated to the
            # value of the characteristic radius which best reproduces
            # the wave function found by interpolation.
            #
            # We know that for e.g. d orbitals:
            #   rchar ~= .37 rcut[sz](.3eV)
            # Since we don't want to spend a lot of time finding
            # these value for other energies, we just find the energy
            # shift at .3 eV now

            j = max(j_l.values())
            u, e, de, vconf, rc_fixed = self.rcut_by_energy(j, .3, 1e-2,
                                                            6., (12., .6))

            default_rchar_rel = .25
            # Defaults for each l.  Actually we don't care right now
            rchar_rels = {}

            if rcharpol_rel is None:
                rcharpol_rel = rchar_rels.get(l_pol, default_rchar_rel)
            rchar = rcharpol_rel * rc_fixed
            gaussian = QuasiGaussian(1./rchar**2, rcut)
            psi_pol = gaussian(rgd.r_g) * rgd.r_g**(l_pol + 1)
            norm = np.dot(rgd.dr_g, psi_pol * psi_pol) ** .5
            psi_pol /= norm
            print >> txt, 'Single quasi Gaussian'
            msg = 'Rchar = %.03f*rcut = %.03f Bohr' % (rcharpol_rel, rchar)
            adjective = 'Gaussian'
            print >> txt, msg
            #else:
            #    psi_pol = self.make_polarization_function(rcut, l_pol,
            #                                              referencefile,
            #                                              referenceindex,
            #                                              ngaussians, txt)
            #    adjective = 'interpolated'

            type = '%s-type %s polarization' % ('spdfg'[l_pol], adjective)
            bf_pol = BasisFunction(l_pol, rcut, psi_pol, type)
                                   
            polarization_functions.append(bf_pol)
            for i in range(polarizationcount - 1):
                npol = i + 2
                msg = '\n%s: %s' % (['Secondary', 'Tertiary', 'Quaternary', \
                                     'Quintary', 'Sextary', 'Septenary'][i],
                                    splitvalencedescr)
                print >> txt, msg
                rsplit, norm, splitwave = rsplit_by_norm(rgd, l_pol, psi_pol,
                                                         tailnorm[i],
                                                         txt)
                descr = ('%s-type split-valence polarization %d'
                         % ('spdfg'[l_pol], npol))
                bf_pol = BasisFunction(l_pol, rsplit, psi_pol - splitwave,
                                       descr)
                polarization_functions.append(bf_pol)
        
        bf_j = []
        bf_j.extend(singlezetas)
        bf_j.extend(energy_derivative_functions)
        for multizeta_list in multizetas:
            bf_j.extend(multizeta_list)
        bf_j.extend(polarization_functions)
        
        rcmax = max([bf.rc for bf in bf_j])

        # The non-equidistant grids are really only suited for AE WFs
        d = 1./64.
        equidistant_grid = np.arange(0., rcmax + d, d)
        ng = len(equidistant_grid)

        for bf in bf_j:
            # We have been storing phit_g * r, but we just want phit_g
            bf.phit_g = divrl(bf.phit_g, 1, rgd.r_g)
            
            gcut = min(int(1 + bf.rc / d), ng - 1)
            
            assert equidistant_grid[gcut] >= bf.rc
            assert equidistant_grid[gcut - 1] <= bf.rc
            
            bf.rc = equidistant_grid[gcut]
            # Note: bf.rc *must* correspond to a grid point (spline issues)
            bf.ng = gcut + 1
            # XXX all this should be done while building the basis vectors,
            # not here
            
            # Quick hack to change to equidistant coordinates
            spline = Spline(bf.l, rgd.r_g[rgd.r2g_floor(bf.rc)],
                            bf.phit_g,
                            rgd.r_g, beta=rgd.beta, points=100)
            bf.phit_g = np.array([spline(r) * r**bf.l
                                   for r in equidistant_grid[:bf.ng]])
            bf.phit_g[-1] = 0.

        basis = Basis(g.symbol, self.name, False)
        basis.ng = ng
        basis.d = d
        basis.bf_j = bf_j
        basis.generatordata = textbuffer.getvalue().strip()
        basis.generatorattrs = {'version' : version}
        textbuffer.close()

        return basis
Exemple #28
0
    def generate(
            self,
            zetacount=2,
            polarizationcount=1,
            tailnorm=(0.16, 0.3, 0.6),
            energysplit=0.1,
            tolerance=1.0e-3,
            referencefile=None,
            referenceindex=None,
            rcutpol_rel=1.0,
            rcutmax=20.0,
            rcharpol_rel=None,
            vconf_args=(12.0, 0.6),
            txt='-',
            include_energy_derivatives=False,
            # lvalues=None, # XXX clean up some of these!
            jvalues=None,
            l_pol=None):
        """Generate an entire basis set.

        This is a high-level method which will return a basis set
        consisting of several different basis vector types.

        Parameters:

        ===================== =================================================
        ``zetacount``         Number of basis functions per occupied orbital
        ``polarizationcount`` Number of polarization functions
        ``tailnorm``          List of tail norms for split-valence scheme
        ``energysplit``       Energy increase defining confinement radius (eV)
        ``tolerance``         Tolerance of energy split (eV)
        ``referencefile``     gpw-file used to generate polarization function
        ``referenceindex``    Index in reference system of relevant atom
        ``rcutpol_rel``       Polarization rcut relative to largest other rcut
        ``rcutmax``           No cutoff will be greater than this value
        ``vconf_args``        Parameters (alpha, ri/rc) for conf. potential
        ``txt``               Log filename or '-' for stdout
        ===================== =================================================

        Returns a fully initialized Basis object.
        """

        if txt == '-':
            txt = sys.stdout
        elif txt is None:
            txt = devnull

        if isinstance(tailnorm, float):
            tailnorm = (tailnorm, )
        if 1 + len(tailnorm) < max(polarizationcount, zetacount):
            raise ValueError(
                'Needs %d tail norm values, but only %d are specified' %
                (max(polarizationcount, zetacount) - 1, len(tailnorm)))

        textbuffer = StringIO()

        class TeeStream:  # quick hack to both write and save output
            def __init__(self, out1, out2):
                self.out1 = out1
                self.out2 = out2

            def write(self, string):
                self.out1.write(string)
                self.out2.write(string)

        txt = TeeStream(txt, textbuffer)

        if vconf_args is not None:
            amplitude, ri_rel = vconf_args

        g = self.generator
        rgd = self.rgd

        njcore = g.njcore
        n_j = g.n_j[njcore:]
        l_j = g.l_j[njcore:]
        f_j = g.f_j[njcore:]

        if jvalues is None:
            jvalues = []
            sortkeys = []
            for j in range(len(n_j)):
                if f_j[j] == 0 and l_j[j] != 0:
                    continue
                jvalues.append(j)
                sortkeys.append(l_j[j])

            # Now order jvalues by l
            #
            # Use a stable sort so the energy ordering within each
            # angular momentum is guaranteed to be preserved
            args = np.argsort(sortkeys, kind='mergesort')
            jvalues = np.array(jvalues)[args]

        fulljvalues = [njcore + j for j in jvalues]

        if isinstance(energysplit, float):
            energysplit = [energysplit] * len(jvalues)

        title = '%s Basis functions for %s' % (g.xcname, g.symbol)
        print(title, file=txt)
        print('=' * len(title), file=txt)

        singlezetas = []
        energy_derivative_functions = []
        multizetas = [[] for i in range(zetacount - 1)]
        polarization_functions = []

        splitvalencedescr = 'split-valence wave, fixed tail norm'
        derivativedescr = 'derivative of sz wrt. (ri/rc) of potential'

        for vj, fullj, esplit in zip(jvalues, fulljvalues, energysplit):
            l = l_j[vj]
            n = n_j[vj]
            assert n > 0
            orbitaltype = str(n) + 'spdf'[l]
            msg = 'Basis functions for l=%d, n=%d' % (l, n)
            print(file=txt)
            print(msg + '\n', '-' * len(msg), file=txt)
            print(file=txt)
            if vconf_args is None:
                adverb = 'sharply'
            else:
                adverb = 'softly'
            print('Zeta 1: %s confined pseudo wave,' % adverb,
                  end=' ',
                  file=txt)

            u, e, de, vconf, rc = self.rcut_by_energy(fullj,
                                                      esplit,
                                                      tolerance,
                                                      vconf_args=vconf_args)
            if rc > rcutmax:
                rc = rcutmax  # scale things down
                if vconf is not None:
                    vconf = g.get_confinement_potential(
                        amplitude, ri_rel * rc, rc)
                u, e = g.solve_confined(fullj, rc, vconf)
                print('using maximum cutoff', file=txt)
                print('rc=%.02f Bohr' % rc, file=txt)
            else:
                print('fixed energy shift', file=txt)
                print('DE=%.03f eV :: rc=%.02f Bohr' % (de * Hartree, rc),
                      file=txt)

            if vconf is not None:
                print('Potential amp=%.02f :: ri/rc=%.02f' %
                      (amplitude, ri_rel),
                      file=txt)
            phit_g = self.smoothify(u, l)
            bf = BasisFunction(n, l, rc, phit_g,
                               '%s-sz confined orbital' % orbitaltype)
            norm = np.dot(g.dr, phit_g * phit_g)**.5
            print('Norm=%.03f' % norm, file=txt)
            singlezetas.append(bf)

            zetacounter = iter(range(2, zetacount + 1))

            if include_energy_derivatives:
                assert zetacount > 1
                zeta = next(zetacounter)
                print('\nZeta %d: %s' % (zeta, derivativedescr), file=txt)
                vconf2 = g.get_confinement_potential(amplitude,
                                                     ri_rel * rc * .99, rc)
                u2, e2 = g.solve_confined(fullj, rc, vconf2)

                phit2_g = self.smoothify(u2, l)
                dphit_g = phit2_g - phit_g
                dphit_norm = np.dot(rgd.dr_g, dphit_g * dphit_g)**.5
                dphit_g /= dphit_norm
                descr = '%s-dz E-derivative of sz' % orbitaltype
                bf = BasisFunction(None, l, rc, dphit_g, descr)
                energy_derivative_functions.append(bf)

            for i, zeta in enumerate(zetacounter):
                print('\nZeta %d: %s' % (zeta, splitvalencedescr), file=txt)
                # Unresolved issue:  how does the lack of normalization
                # of the first function impact the tail norm scheme?
                # Presumably not much, since most interesting stuff happens
                # close to the core.
                rsplit, norm, splitwave = rsplit_by_norm(
                    rgd, l, phit_g, tailnorm[i]**2.0, txt)
                descr = '%s-%sz split-valence wave' % (orbitaltype,
                                                       '0sdtq56789'[zeta])
                bf = BasisFunction(None, l, rsplit, phit_g - splitwave, descr)
                multizetas[i].append(bf)

        if polarizationcount > 0 or l_pol is not None:
            if l_pol is None:
                # Now make up some properties for the polarization orbital
                # We just use the cutoffs from the previous one times a factor
                # Find 'missing' values in lvalues
                lvalues = [l_j[vj] for vj in jvalues]
                for i in range(max(lvalues) + 1):
                    if list(lvalues).count(i) == 0:
                        l_pol = i
                        break
                else:
                    l_pol = max(lvalues) + 1

            # Find the last state with l=l_pol - 1, which will be the state we
            # base the polarization function on
            for vj, fullj, bf in zip(jvalues[::-1], fulljvalues[::-1],
                                     singlezetas[::-1]):
                if bf.l == l_pol - 1:
                    fullj_pol = fullj
                    rcut = bf.rc * rcutpol_rel
                    break
            else:
                raise ValueError('The requested value l_pol=%d requires l=%d '
                                 'among valence states' % (l_pol, l_pol - 1))
            rcut = min(rcut, rcutmax)
            msg = 'Polarization function: l=%d, rc=%.02f' % (l_pol, rcut)
            print('\n' + msg, file=txt)
            print('-' * len(msg), file=txt)
            # Make a single Gaussian for polarization function.
            #
            # It is known that for given l, the sz cutoff defined
            # by some fixed energy is strongly correlated to the
            # value of the characteristic radius which best reproduces
            # the wave function found by interpolation.
            #
            # We know that for e.g. d orbitals:
            #   rchar ~= .37 rcut[sz](.3eV)
            # Since we don't want to spend a lot of time finding
            # these value for other energies, we just find the energy
            # shift at .3 eV now

            u, e, de, vconf, rc_fixed = self.rcut_by_energy(
                fullj_pol, .3, 1e-2, 6., (12., .6))

            default_rchar_rel = .25
            # Defaults for each l.  Actually we don't care right now
            rchar_rels = {}

            if rcharpol_rel is None:
                rcharpol_rel = rchar_rels.get(l_pol, default_rchar_rel)
            rchar = rcharpol_rel * rc_fixed
            gaussian = QuasiGaussian(1.0 / rchar**2, rcut)
            psi_pol = gaussian(rgd.r_g) * rgd.r_g**(l_pol + 1)
            norm = np.dot(rgd.dr_g, psi_pol * psi_pol)**.5
            psi_pol /= norm
            print('Single quasi Gaussian', file=txt)
            msg = 'Rchar = %.03f*rcut = %.03f Bohr' % (rcharpol_rel, rchar)
            adjective = 'Gaussian'
            print(msg, file=txt)
            type = '%s-type %s polarization' % ('spdfg'[l_pol], adjective)
            bf_pol = BasisFunction(None, l_pol, rcut, psi_pol, type)

            polarization_functions.append(bf_pol)
            for i in range(polarizationcount - 1):
                npol = i + 2
                msg = '\n%s: %s' % ([
                    'Secondary', 'Tertiary', 'Quaternary', 'Quintary',
                    'Sextary', 'Septenary'
                ][i], splitvalencedescr)
                print(msg, file=txt)
                rsplit, norm, splitwave = rsplit_by_norm(
                    rgd, l_pol, psi_pol, tailnorm[i], txt)
                descr = ('%s-type split-valence polarization %d' %
                         ('spdfg'[l_pol], npol))
                bf_pol = BasisFunction(None, l_pol, rsplit,
                                       psi_pol - splitwave, descr)
                polarization_functions.append(bf_pol)

        bf_j = []
        bf_j.extend(singlezetas)
        bf_j.extend(energy_derivative_functions)
        for multizeta_list in multizetas:
            bf_j.extend(multizeta_list)
        bf_j.extend(polarization_functions)

        rcmax = max([bf.rc for bf in bf_j])

        # The non-equidistant grids are really only suited for AE WFs
        d = 1.0 / 64
        equidistant_grid = np.arange(0.0, rcmax + d, d)
        ng = len(equidistant_grid)

        for bf in bf_j:
            # We have been storing phit_g * r, but we just want phit_g
            bf.phit_g = divrl(bf.phit_g, 1, rgd.r_g)

            gcut = min(int(1 + bf.rc / d), ng - 1)

            assert equidistant_grid[gcut] >= bf.rc
            assert equidistant_grid[gcut - 1] <= bf.rc

            bf.rc = equidistant_grid[gcut]
            # Note: bf.rc *must* correspond to a grid point (spline issues)
            bf.ng = gcut + 1
            # XXX all this should be done while building the basis vectors,
            # not here

            # Quick hack to change to equidistant coordinates
            spline = rgd.spline(bf.phit_g,
                                rgd.r_g[rgd.floor(bf.rc)],
                                bf.l,
                                points=100)
            bf.phit_g = np.array(
                [spline(r) * r**bf.l for r in equidistant_grid[:bf.ng]])
            bf.phit_g[-1] = 0.

        basistype = get_basis_name(zetacount, polarizationcount)
        if self.name is None:
            compound_name = basistype
        else:
            compound_name = '%s.%s' % (self.name, basistype)

        basis = Basis(g.symbol, compound_name, False,
                      EquidistantRadialGridDescriptor(d, ng))
        basis.bf_j = bf_j
        basis.generatordata = textbuffer.getvalue().strip()
        basis.generatorattrs = {'version': version}
        textbuffer.close()

        return basis
    def create_basis_set(self, tailnorm=0.0005, scale=200.0, splitnorm=0.16):
        rgd = self.rgd
        self.basis = Basis(self.aea.symbol, readxml=False, rgd=rgd)

        # We print text to sdtout and put it in the basis-set file
        txt = 'Basis functions:\n'

        # Bound states:
        for l, waves in enumerate(self.waves_l):
            for i, n in enumerate(waves.n_n):
                if n > 0:
                    tn = tailnorm
                    if waves.f_n[i] == 0:
                        tn = min(0.05, tn * 20)  # no need for long tail
                    phit_g, ronset, rc, de = self.create_basis_function(
                        l, i, tn, scale)
                    bf = BasisFunction(n, l, rc, phit_g, 'bound state')
                    self.basis.append(bf)

                    txt += '%d%s bound state:\n' % (n, 'spdf'[l])
                    txt += ('  cutoff: %.3f to %.3f Bohr (tail-norm=%f)\n' %
                            (ronset, rc, tn))
                    txt += '  eigenvalue shift: %.3f eV\n' % (de * Hartree)

        # Split valence:
        for l, waves in enumerate(self.waves_l):
            # Find the largest n that is occupied:
            n0 = None
            for f, n in zip(waves.f_n, waves.n_n):
                if n > 0 and f > 0:
                    n0 = n
            if n0 is None:
                continue

            bf = [bf for bf in self.basis.bf_j if bf.l == l and bf.n == n0][0]

            # Radius and l-value used for polarization function below:
            rcpol = bf.rc
            lpol = l + 1

            phit_g = bf.phit_g

            # Find cutoff radius:
            n_g = np.add.accumulate(phit_g**2 * rgd.r_g**2 * rgd.dr_g)
            norm = n_g[-1]
            gc = (norm - n_g > splitnorm * norm).sum()
            rc = rgd.r_g[gc]

            phit2_g = rgd.pseudize(phit_g, gc, l, 2)[0]  # "split valence"
            bf = BasisFunction(n, l, rc, phit_g - phit2_g, 'split valence')
            self.basis.append(bf)

            txt += '%d%s split valence:\n' % (n0, 'spdf'[l])
            txt += '  cutoff: %.3f Bohr (tail-norm=%f)\n' % (rc, splitnorm)

        # Polarization:
        gcpol = rgd.round(rcpol)
        alpha = 1 / (0.25 * rcpol)**2

        # Gaussian that is continuous and has a continuous derivative at rcpol:
        phit_g = np.exp(-alpha * rgd.r_g**2) * rgd.r_g**lpol
        phit_g -= rgd.pseudize(phit_g, gcpol, lpol, 2)[0]
        phit_g[gcpol:] = 0.0

        bf = BasisFunction(None, lpol, rcpol, phit_g, 'polarization')
        self.basis.append(bf)
        txt += 'l=%d polarization functions:\n' % lpol
        txt += '  cutoff: %.3f Bohr (r^%d exp(-%.3f*r^2))\n' % (rcpol, lpol,
                                                                alpha)

        self.log(txt)

        # Write basis-set file:
        self.basis.generatordata = txt
        self.basis.generatorattrs.update(dict(tailnorm=tailnorm,
                                              scale=scale,
                                              splitnorm=splitnorm))
        self.basis.name = '%de.dzp' % self.nvalence

        return self.basis