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 do_nao_ngto_basis(atom, xc, naobasis, gbsfname, label, rmax=100.0): # Read Gaussians atomgbs, descriptiongbs, gto_k = read_gbs(gbsfname) assert atom == atomgbs # Generate nao basis assert naobasis == 'sz' # Choose basis sets without semi-core states XXXXXX if atom == 'Ag': label = '11.%s' % label p = parameters_extra else: p = parameters bm = BasisMaker(atom, label, run=False, gtxt=None, xc=xc) bm.generator.run(write_xml=False, use_restart_file=False, **p[atom]) basis = bm.generate(1, 0, txt=None) # Increase basis function max radius h = basis.rgd.dr_g assert isinstance(h, float) assert basis.rgd.r_g[0] == 0.0 N = int(rmax / h) + 1 basis.rgd = EquidistantRadialGridDescriptor(h, N) # Add NGTOs tol = 0.001 description = [] msg = 'Augmented with NGTOs' description.append(msg) description.append('=' * len(msg)) description.append('') msg = 'GTOs from file %s' % os.path.basename(gbsfname) description.append(msg) description.append('-' * len(msg)) description.append(descriptiongbs) description.append('') description.append('NGTO truncation tolerance: %f' % tol) description.append('Functions: NGTO(l,alpha)') for gto in gto_k: l = gto['l'] assert len(gto['alpha_j']) == 1, \ 'Only non-contracted GTOs supported' alpha = gto['alpha_j'][0] ngtolabel = 'NGTO(%s,%.7f)' % ('spdfghi'[l], alpha) description.append(' ' + ngtolabel) add_ngto(basis, l, alpha, tol, ngtolabel) basis.generatordata += '\n\n' + '\n'.join(description) basis.write_xml()
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 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_grid_descriptor(self): return EquidistantRadialGridDescriptor(self.d, self.ng)
def __init__(self, data): # data can be string (filename) # or dict (that's what we are looking for). # Maybe just a symbol would also be fine if we know the # filename to look for. if isinstance(data, basestring): data = parse_upf(data) assert isinstance(data, dict) self.data = data # more or less "raw" data from the file self.name = 'upf' header = data['header'] keys = header.keys() keys.sort() self.symbol = header['element'] self.Z = atomic_numbers[self.symbol] self.Nv = header['z_valence'] self.Nc = self.Z - self.Nv self.Delta0 = -self.Nv / np.sqrt(4.0 * np.pi) # like hgh self.rcut_j = [ data['r'][len(proj.values) - 1] for proj in data['projectors'] ] #beta = 0.1 # XXX nice parameters? #N = 4 * 450 # XXX # This is "stolen" from hgh. Figure out something reasonable #rgd = AERadialGridDescriptor(beta / N, 1.0 / N, N, # default_spline_points=100) from gpaw.atom.radialgd import EquidistantRadialGridDescriptor rgd = EquidistantRadialGridDescriptor(0.02) self.rgd = rgd # Whyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy??? # What abominable part of the code requires the states # to be ordered like this? self.jargs = self.get_jargs() projectors = [data['projectors'][jarg] for jarg in self.jargs] if projectors: self.l_j = [proj.l for proj in projectors] self.pt_jg = [] for proj in projectors: val = proj.values.copy() val /= 2. val[1:] /= data['r'][1:len(val)] val[0] = val[1] pt_g = self._interp(val) #* np.sqrt(4.0 * np.pi) #sqrnorm = (pt_g**2 * self.rgd.dr_g).sum() self.pt_jg.append(pt_g) else: self.l_j = [0] gcut = self.rgd.r2g(1.0) self.pt_jg = [np.zeros(gcut)] # XXX yet another "null" function self.rcgauss = 0.0 # XXX .... what is this used for? self.ni = sum([2 * l + 1 for l in self.l_j]) self.filename = None # remember filename? self.fingerprint = None # XXX hexdigest the file? self.HubU = None # XXX self.lq = None # XXX #if data['states']: # states_lmax = max([state.l for state in data['states']]) #else: # states_lmax = 1 # XXXX if data['states']: states_lmax = max([state.l for state in data['states']]) f_ln = [[] for _ in range(1 + states_lmax)] electroncount = 0.0 for state in data['states']: # Where should the electrons be in the inner list?? # This is probably wrong and will lead to bad initialization f_ln[state.l].append(state.occupation) electroncount += state.occupation # The Cl.pz-hgh.UPF from quantum espresso has only 6 # but should have 7 electrons. Oh well.... #err = abs(electroncount - self.Nv) self.f_j = [state.occupation for state in data['states']] self.n_j = [state.n for state in data['states']] self.l_orb_j = [state.l for state in data['states']] self.f_ln = f_ln else: self.n_j, self.l_orb_j, self.f_j, self.f_ln = \ figure_out_valence_states(self) vlocal_unscreened = data['vlocal'] # The UPF representation of HGH setups should be equal to that # used with setups='hgh'. But the UPF files do not contain # info on the localization of the compensation charges! That # means the screen_potential code will choose the shape of the # compensation charges, resulting in some numerical differences. # # One could hack that it looks up some radii to compare e.g. H2O. # The results of the below comments still have some numerical error. # But it's not huge. #a = None #if self.symbol == 'H': # a = 0.2 #elif self.symbol == 'O': # a = .247621 vbar_g, ghat_g = screen_potential(data['r'], vlocal_unscreened, self.Nv) #, a=a) self.vbar_g = self._interp(vbar_g) * np.sqrt(4.0 * np.pi) self.ghat_lg = [4.0 * np.pi / self.Nv * self._interp(ghat_g)]
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 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()
gd = GridDescriptor([n, n, n], [a, a, a]) r = np.linspace(0, rc, 200) g = np.exp(-(r / rc * b)**2) splines = [Spline(l=l, rmax=rc, f_g=g) for l in range(lmax + 1)] c = LFC(gd, [splines]) c.set_positions([(0, 0, 0)]) psi = gd.zeros(m) d0 = c.dict(m) if 0 in d0: d0[0] = np.identity(m) c.add(psi, d0) d1 = c.dict(m, derivative=True) c.derivative(psi, d1) class TestSetup(Setup): l_j = range(lmax + 1) nj = lmax + 1 ni = m def __init__(self): pass rgd = EquidistantRadialGridDescriptor(r[1], len(r)) g = [np.exp(-(r / rc * b)**2) * r**l for l in range(lmax + 1)] d2 = TestSetup().get_derivative_integrals(rgd, g, np.zeros_like(g)) if 0 in d1: print(abs(d1[0] - d2).max()) assert abs(d1[0] - d2).max() < 2e-6
from __future__ import print_function from math import pi from gpaw.atom.radialgd import EquidistantRadialGridDescriptor from gpaw.grid_descriptor import GridDescriptor from gpaw.xc import XC import numpy as np from gpaw.test import equal rgd = EquidistantRadialGridDescriptor(0.01, 100) for name in ['LDA', 'PBE']: xc = XC(name) for nspins in [1, 2]: n = rgd.zeros(nspins) v = rgd.zeros(nspins) n[:] = np.exp(-rgd.r_g**2) n[-1] *= 2 E = xc.calculate_spherical(rgd, n, v) i = 23 x = v[-1, i] * rgd.dv_g[i] n[-1, i] += 0.000001 Ep = xc.calculate_spherical(rgd, n, v) n[-1, i] -= 0.000002 Em = xc.calculate_spherical(rgd, n, v) x2 = (Ep - Em) / 0.000002 print(name, nspins, E, x, x2, x - x2) equal(x, x2, 1e-9) n[-1, i] += 0.000001 if nspins == 1: ns = rgd.empty(2) ns[:] = n / 2