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
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
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()
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)
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)
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
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
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()
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
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
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)
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
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
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
def __init__(self, symbol, phit_j): Basis.__init__(self, symbol, 'partial-waves', readxml=False) self.phit_j = phit_j
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
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()
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 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()
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
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
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
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