Пример #1
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

        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
Пример #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

        basis = Basis(self.symbol, basis_name, readxml=False)
        basis.d = self.wfs.gd.h
        basis.ng = self.wfs.gd.ng + 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.rcut, phit_g,
                               '%s%d e=%.3f f=%.3f' % ('spdfgh'[l], n, eps, f))
            bf_j.append(bf)
        return basis
Пример #3
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
Пример #4
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
Пример #5
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()
Пример #6
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
Пример #7
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
Пример #8
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
Пример #9
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()
Пример #10
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(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(xrange(2, zetacount + 1))

            if include_energy_derivatives:
                assert zetacount > 1
                zeta = zetacounter.next()
                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(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(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(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(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.

        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