def __init__(self, name, hybrid=None, xc=None, finegrid=False): """Mix standard functionals with exact exchange. name: str Name of hybrid functional. hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. finegrid: boolean Use fine grid for energy functional evaluations? """ if name == 'EXX': assert hybrid is None and xc is None hybrid = 1.0 xc = XC(XCNull()) elif name == 'PBE0': assert hybrid is None and xc is None hybrid = 0.25 xc = XC('HYB_GGA_XC_PBEH') elif name == 'B3LYP': assert hybrid is None and xc is None hybrid = 0.2 xc = XC('HYB_GGA_XC_B3LYP') if isinstance(xc, str): xc = XC(xc) self.hybrid = hybrid self.xc = xc self.type = xc.type self.finegrid = finegrid XCFunctional.__init__(self, name)
def initialize(self): self.occupations = self.nlfunc.occupations self.xc = XC(self.functional) # Always 1 spin, no matter what calculation nspins is self.vt_sg = self.nlfunc.finegd.empty(1) self.e_g = self.nlfunc.finegd.empty() #.ravel()
def vxc(paw, xc=None, coredensity=True): """Calculate XC-contribution to eigenvalues.""" ham = paw.hamiltonian dens = paw.density wfs = paw.wfs if xc is None: xc = ham.xc elif isinstance(xc, str): xc = XC(xc) if dens.nt_sg is None: dens.interpolate_pseudo_density() thisisatest = not True if xc.orbital_dependent: paw.get_xc_difference(xc) # Calculate XC-potential: vxct_sg = ham.finegd.zeros(wfs.nspins) xc.calculate(dens.finegd, dens.nt_sg, vxct_sg) vxct_sG = ham.gd.empty(wfs.nspins) ham.restrict(vxct_sg, vxct_sG) if thisisatest: vxct_sG[:] = 1 # ... and PAW corrections: dvxc_asii = {} for a, D_sp in dens.D_asp.items(): dvxc_sp = np.zeros_like(D_sp) xc.calculate_paw_correction(wfs.setups[a], D_sp, dvxc_sp, a=a, addcoredensity=coredensity) dvxc_asii[a] = [unpack(dvxc_p) for dvxc_p in dvxc_sp] if thisisatest: dvxc_asii[a] = [wfs.setups[a].dO_ii] vxc_un = np.empty((wfs.kd.mynks, wfs.bd.mynbands)) for u, vxc_n in enumerate(vxc_un): kpt = wfs.kpt_u[u] vxct_G = vxct_sG[kpt.s] for n in range(wfs.bd.mynbands): psit_G = wfs._get_wave_function_array(u, n, realspace=True) vxc_n[n] = wfs.gd.integrate((psit_G * psit_G.conj()).real, vxct_G, global_integral=False) for a, dvxc_sii in dvxc_asii.items(): P_ni = kpt.P_ani[a] vxc_n += (np.dot(P_ni, dvxc_sii[kpt.s]) * P_ni.conj()).sum(1).real wfs.gd.comm.sum(vxc_un) vxc_skn = wfs.kd.collect(vxc_un) if xc.orbital_dependent: vxc_skn += xc.exx_skn return vxc_skn * Hartree
def create_setup(symbol, xc='LDA', lmax=0, type='paw', basis=None, setupdata=None, filter=None, world=None): if isinstance(xc, str): xc = XC(xc) if isinstance(type, str) and ':' in type: # Parse DFT+U parameters from type-string: # Examples: "type:l,U" or "type:l,U,scale" type, lu = type.split(':') if type == '': type = 'paw' l = 'spdf'.find(lu[0]) assert lu[1] == ',' U = lu[2:] if ',' in U: U, scale = U.split(',') else: scale = True U = float(U) / units.Hartree scale = int(scale) else: U = None if setupdata is None: if type == 'hgh' or type == 'hgh.sc': lmax = 0 from gpaw.hgh import HGHSetupData, setups, sc_setups if type == 'hgh.sc': table = sc_setups else: table = setups parameters = table[symbol] setupdata = HGHSetupData(parameters) elif type == 'ah': from gpaw.ah import AppelbaumHamann ah = AppelbaumHamann() ah.build(basis) return ah elif type == 'ae': from gpaw.ae import HydrogenAllElectronSetup assert symbol == 'H' ae = HydrogenAllElectronSetup() ae.build(basis) return ae elif type == 'ghost': from gpaw.lcao.bsse import GhostSetupData setupdata = GhostSetupData(symbol) else: setupdata = SetupData(symbol, xc.get_setup_name(), type, True, world=world) if hasattr(setupdata, 'build'): setup = LeanSetup(setupdata.build(xc, lmax, basis, filter)) if U is not None: setup.set_hubbard_u(U, l, scale) return setup else: return setupdata
def get_xc_difference(self, xc): if isinstance(xc, str): xc = XC(xc) xc.initialize(self.density, self.hamiltonian, self.wfs, self.occupations) xc.set_positions(self.atoms.get_scaled_positions() % 1.0) if xc.orbital_dependent: self.converge_wave_functions() return self.hamiltonian.get_xc_difference(xc, self.density) * Hartree
def __init__(self, symbol, xc='LDA', spinpol=False, dirac=False, log=sys.stdout): """All-electron calculation for spherically symmetric atom. symbol: str (or int) Chemical symbol (or atomic number). xc: str Name of XC-functional. spinpol: bool If true, do spin-polarized calculation. Default is spin-paired. dirac: bool Solve Dirac equation instead of Schrödinger equation. log: stream Text output.""" if isinstance(symbol, int): symbol = chemical_symbols[symbol] self.symbol = symbol self.Z = atomic_numbers[symbol] self.nspins = 1 + int(bool(spinpol)) self.dirac = bool(dirac) if isinstance(xc, str): self.xc = XC(xc) else: self.xc = xc if log is None: log = devnull self.fd = log self.vr_sg = None # potential * r self.n_sg = 0.0 # density self.gd = None # radial grid descriptor # Energies: self.ekin = None self.eeig = None self.eH = None self.eZ = None self.channels = None self.initialize_configuration() self.log('Z: ', self.Z) self.log('Name: ', atomic_names[self.Z]) self.log('Symbol: ', symbol) self.log('XC-functional: ', self.xc.name) self.log('Equation: ', ['Schrödinger', 'Dirac'][self.dirac])
def linearize_to_xc(self, newxc): """Linearize Hamiltonian to difference XC functional. Used in real time TDDFT to perform calculations with various kernels. """ if isinstance(newxc, str): newxc = XC(newxc) self.log('Linearizing xc-hamiltonian to ' + str(newxc)) newxc.initialize(self.density, self.hamiltonian, self.wfs, self.occupations) self.hamiltonian.linearize_to_xc(newxc, self.density)
def _calculate_fxc(self, gd, n_sG): if self.functional == 'ALDA_x': n_G = np.sum(n_sG, axis=0) fx_G = -1. / 3. * (3. / np.pi)**(1. / 3.) * n_G**(-2. / 3.) return fx_G else: fxc_sG = np.zeros_like(n_sG) xc = XC(self.functional[1:]) xc.calculate_fxc(gd, n_sG, fxc_sG) return fxc_sG[0]
def linearize_to_xc(self, newxc): """Linearize Hamiltonian to difference XC functional. Used in real time TDDFT to perform calculations with various kernels. """ if isinstance(newxc, str): newxc = XC(newxc) self.txt.write('Linearizing xc-hamiltonian to ' + str(newxc)) newxc.initialize(self.density, self.hamiltonian, self.wfs, self.occupations) self.hamiltonian.linearize_to_xc(newxc, self.density)
def _calculate_pol_fxc(self, gd, n_sG, m_G): """ Calculate polarized fxc """ assert np.shape(m_G) == np.shape(n_sG[0]) if self.functional == 'ALDA_x': fx_G = - (6. / np.pi)**(1. / 3.) \ * (n_sG[0]**(1. / 3.) - n_sG[1]**(1. / 3.)) / m_G return fx_G else: v_sG = np.zeros(np.shape(n_sG)) xc = XC(self.functional[1:]) xc.calculate(gd, n_sG, v_sg=v_sG) return (v_sG[0] - v_sG[1]) / m_G
def forced_update(self): """Recalc yourself.""" if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' else: Om = ApmB name = 'LrTDDFThyb' self.kss = KSSingles(calculator=self.calculator, nspins=self.nspins, eps=self.eps, istart=self.istart, jend=self.jend, energy_range=self.energy_range, txt=self.txt) self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name
def paired(): xc = XC('vdW-DF') n = 0.3 * np.ones((1, N, N, N)) n += 0.01 * np.cos(np.arange(N) * 2 * pi / N) v = 0.0 * n E = xc.calculate(gd, n, v) n2 = 1.0 * n i = 1 n2[0, i, i, i] += 0.00002 x = v[0, i, i, i] * gd.dv E2 = xc.calculate(gd, n2, v) n2[0, i, i, i] -= 0.00004 E2 -= xc.calculate(gd, n2, v) x2 = E2 / 0.00004 print i, x, x2, x - x2, x / x2 equal(x, x2, 5e-12)
def initialize(self): self.occupations = self.nlfunc.occupations self.xc = XC(self.functional) # Always 1 spin, no matter what calculation nspins is self.vt_sg = self.nlfunc.finegd.empty(1) self.e_g = self.nlfunc.finegd.empty()#.ravel()
def paired(): xc = XC('vdW-DF') n = 0.3 * np.ones((1, N, N, N)) n += 0.01 * np.cos(np.arange(N) * 2 * pi / N) v = 0.0 * n xc.calculate(gd, n, v) n2 = 1.0 * n i = 1 n2[0, i, i, i] += 0.00002 x = v[0, i, i, i] * gd.dv E2 = xc.calculate(gd, n2, v) n2[0, i, i, i] -= 0.00004 E2 -= xc.calculate(gd, n2, v) x2 = E2 / 0.00004 print(i, x, x2, x - x2, x / x2) equal(x, x2, 2e-11)
def get_xc_difference(self, xc): if isinstance(xc, (str, dict)): xc = XC(xc) xc.set_grid_descriptor(self.density.finegd) xc.initialize(self.density, self.hamiltonian, self.wfs, self.occupations) xc.set_positions(self.spos_ac) if xc.orbital_dependent: self.converge_wave_functions() return self.hamiltonian.get_xc_difference(xc, self.density) * Ha
def polarized(): xc = XC('vdW-DF') n = 0.04 * np.ones((2, N, N, N)) n[1] = 0.3 n[0] += 0.02 * np.sin(np.arange(N) * 2 * pi / N) n[1] += 0.2 * np.cos(np.arange(N) * 2 * pi / N) v = 0.0 * n xc.calculate(gd, n, v) n2 = 1.0 * n i = 1 n2[0, i, i, i] += 0.00002 x = v[0, i, i, i] * gd.dv E2 = xc.calculate(gd, n2, v) n2[0, i, i, i] -= 0.00004 E2 -= xc.calculate(gd, n2, v) x2 = E2 / 0.00004 print(i, x, x2, x - x2, x / x2) equal(x, x2, 1e-10)
def polarized(): xc = XC('vdW-DF') n = 0.04 * np.ones((2, N, N, N)) n[1] = 0.3 n[0] += 0.02 * np.sin(np.arange(N) * 2 * pi / N) n[1] += 0.2 * np.cos(np.arange(N) * 2 * pi / N) v = 0.0 * n E = xc.calculate(gd, n, v) n2 = 1.0 * n i = 1 n2[0, i, i, i] += 0.00002 x = v[0, i, i, i] * gd.dv E2 = xc.calculate(gd, n2, v) n2[0, i, i, i] -= 0.00004 E2 -= xc.calculate(gd, n2, v) x2 = E2 / 0.00004 print i, x, x2, x - x2, x / x2 equal(x, x2, 1e-10)
def __init__(self, symbol, xc='LDA', spinpol=False, dirac=False, log=sys.stdout): """All-electron calculation for spherically symmetric atom. symbol: str (or int) Chemical symbol (or atomic number). xc: str Name of XC-functional. spinpol: bool If true, do spin-polarized calculation. Default is spin-paired. dirac: bool Solve Dirac equation instead of Schrödinger equation. log: stream Text output.""" if isinstance(symbol, int): symbol = chemical_symbols[symbol] self.symbol = symbol self.Z = atomic_numbers[symbol] self.nspins = 1 + int(bool(spinpol)) self.dirac = bool(dirac) self.scalar_relativistic = False if isinstance(xc, str): self.xc = XC(xc) else: self.xc = xc if log is None: log = devnull self.fd = log self.vr_sg = None # potential * r self.n_sg = 0.0 # density self.rgd = None # radial grid descriptor # Energies: self.ekin = None self.eeig = None self.eH = None self.eZ = None self.channels = None self.initialize_configuration() self.log('Z: ', self.Z) self.log('Name: ', atomic_names[self.Z]) self.log('Symbol: ', symbol) self.log('XC-functional: ', self.xc.name) self.log('Equation: ', ['Schrödinger', 'Dirac'][self.dirac]) self.method = 'Gaussian basis-set'
def update(self, calculator=None, nspins=None, eps=0.001, istart=0, jend=None, energy_range=None, xc=None, derivative_level=None, numscale=0.001): changed = False if self.calculator != calculator or \ self.nspins != nspins or \ self.eps != eps or \ self.istart != istart or \ self.jend != jend : changed = True if not changed: return self.calculator = calculator self.nspins = nspins self.eps = eps self.istart = istart self.jend = jend self.xc = xc self.derivative_level = derivative_level self.numscale = numscale self.kss = KSSingles(calculator=calculator, nspins=nspins, eps=eps, istart=istart, jend=jend, energy_range=energy_range, txt=self.txt) if not self.force_ApmB: Om = OmegaMatrix name = 'LrTDDFT' if self.xc: xc = XC(self.xc) if hasattr(xc, 'hybrid') and xc.hybrid > 0.0: Om = ApmB name = 'LrTDDFThyb' else: Om = ApmB name = 'LrTDDFThyb' self.Om = Om(self.calculator, self.kss, self.xc, self.derivative_level, self.numscale, finegrid=self.finegrid, eh_comm=self.eh_comm, txt=self.txt) self.name = name
def get_non_xc_total_energies(self): """Compile non-XC total energy contributions""" if self.e_dft is None: self.e_dft = self.calc.get_potential_energy() if self.e0 is None: from gpaw.xc.kernel import XCNull xc_null = XC(XCNull()) self.e0 = self.e_dft + self.calc.get_xc_difference(xc_null) assert isinstance(self.e_dft, float) assert isinstance(self.e0, float)
def propagate_single(self, dt): # -------------- # Predictor step # -------------- # 1. Calculate H(t) self.save_wfs() # kpt.C2_nM = kpt.C_nM # 2. H_MM(t) = <M|H(t)|H> # Solve Psi(t+dt) from (S_MM - 0.5j*H_MM(t)*dt) Psi(t+dt) = # (S_MM + 0.5j*H_MM(t)*dt) Psi(t) for k, kpt in enumerate(self.wfs.kpt_u): if self.fxc is not None: if self.time == 0.0: kpt.deltaXC_H_MM = self.get_hamiltonian(kpt) self.hamiltonian.xc = XC(self.fxc) self.update_hamiltonian() assert len(self.wfs.kpt_u) == 1 kpt.deltaXC_H_MM -= self.get_hamiltonian(kpt) self.update_hamiltonian() # Call registered callback functions self.call_observers(self.niter) for k, kpt in enumerate(self.wfs.kpt_u): kpt.H0_MM = self.get_hamiltonian(kpt) if self.fxc is not None: kpt.H0_MM += kpt.deltaXC_H_MM self.propagate_wfs(kpt.C_nM, kpt.C_nM, kpt.S_MM, kpt.H0_MM, dt) # --------------- # Propagator step # --------------- # 1. Calculate H(t+dt) self.update_hamiltonian() # 2. Estimate H(t+0.5*dt) ~ H(t) + H(t+dT) for k, kpt in enumerate(self.wfs.kpt_u): kpt.H0_MM *= 0.5 if self.fxc is not None: # Store this to H0_MM and maybe save one extra H_MM of # memory? kpt.H0_MM += 0.5 * (self.get_hamiltonian(kpt) + kpt.deltaXC_H_MM) else: # Store this to H0_MM and maybe save one extra H_MM of # memory? kpt.H0_MM += 0.5 * self.get_hamiltonian(kpt) # 3. Solve Psi(t+dt) from # (S_MM - 0.5j*H_MM(t+0.5*dt)*dt) Psi(t+dt) # = (S_MM + 0.5j*H_MM(t+0.5*dt)*dt) Psi(t) self.propagate_wfs(kpt.C2_nM, kpt.C_nM, kpt.S_MM, kpt.H0_MM, dt) self.niter += 1 self.time += dt
def get_density(self, atom_indices=None, gridrefinement=2): """Get sum of atomic densities from the given atom list. All atoms are taken if the list is not given.""" all_atoms = self.calculator.get_atoms() if atom_indices is None: atom_indices = range(len(all_atoms)) density = self.calculator.density spos_ac = all_atoms.get_scaled_positions() rank_a = self.finegd.get_ranks_from_positions(spos_ac) density.set_positions(all_atoms.get_scaled_positions(), AtomPartition(self.finegd.comm, rank_a)) # select atoms atoms = [] D_asp = {} rank_a = [] all_D_asp = self.calculator.density.D_asp all_rank_a = self.calculator.density.atom_partition.rank_a for a in atom_indices: if a in all_D_asp: D_asp[len(atoms)] = all_D_asp.get(a) atoms.append(all_atoms[a]) rank_a.append(all_rank_a[a]) atoms = Atoms(atoms, cell=all_atoms.get_cell(), pbc=all_atoms.get_pbc()) spos_ac = atoms.get_scaled_positions() Z_a = atoms.get_atomic_numbers() par = self.calculator.parameters setups = Setups(Z_a, par.setups, par.basis, XC(par.xc), self.calculator.wfs.world) # initialize self.initialize(setups, self.calculator.timer, np.zeros(len(atoms)), False) self.set_mixer(None) # FIXME nparray causes partitionong.py test to fail self.set_positions(spos_ac, AtomPartition(self.gd.comm, rank_a)) self.D_asp = D_asp basis_functions = BasisFunctions( self.gd, [setup.phit_j for setup in self.setups], cut=True) basis_functions.set_positions(spos_ac) self.initialize_from_atomic_densities(basis_functions) aed_sg, gd = self.get_all_electron_density(atoms, gridrefinement) return aed_sg.sum(axis=0), gd
def mbeef_exchange_energy_contribs(self): """Legendre polynomial exchange contributions to mBEEF Etot""" self.get_non_xc_total_energies() e_x = np.zeros((self.max_order, self.max_order)) for p1 in range(self.max_order): # alpha for p2 in range(self.max_order): # s2 pars_i = np.array([1, self.trans[0], p2, 1.0]) pars_j = np.array([1, self.trans[1], p1, 1.0]) pars = np.hstack((pars_i, pars_j)) x = XC('2D-MGGA', pars) e_x[p1, p2] = (self.e_dft + self.calc.get_xc_difference(x) - self.e0) del x return e_x
def beefvdw_energy_contribs_x(self): """Legendre polynomial exchange contributions to BEEF-vdW Etot""" self.get_non_xc_total_energies() e_pbe = (self.e_dft + self.calc.get_xc_difference('GGA_C_PBE') - self.e0) exch = np.zeros(30) for p in range(30): pars = [4, 0, p, 1.0] bee = XC('BEE2', pars) exch[p] = (self.e_dft + self.calc.get_xc_difference(bee) - self.e0 - e_pbe) del bee return exch
def __init__(self, xc='LDA', finegrid=False, **parameters): """Self-Interaction Corrected Functionals (PZ-SIC). finegrid: boolean Use fine grid for energy functional evaluations? """ if isinstance(xc, str): xc = XC(xc) self.xc = xc self.type = xc.type XCFunctional.__init__(self, xc.name + '-PZ-SIC') self.finegrid = finegrid self.parameters = parameters
def get_radial_potential(calc, a, ai): """Calculates dV/dr / r for the effective potential. Below, f_g denotes dV/dr = minus the radial force""" rgd = a.xc_correction.rgd r_g = rgd.r_g r_g[0] = 1.0e-12 dr_g = rgd.dr_g Ns = calc.wfs.nspins D_sp = calc.density.D_asp[ai] B_pq = a.xc_correction.B_pqL[:, :, 0] n_qg = a.xc_correction.n_qg D_sq = np.dot(D_sp, B_pq) n_sg = np.dot(D_sq, n_qg) / (4 * np.pi)**0.5 n_sg[:] += a.xc_correction.nc_g / Ns # Coulomb force from nucleus fc_g = a.Z / r_g**2 # Hartree force rho_g = 4 * np.pi * r_g**2 * dr_g * np.sum(n_sg, axis=0) fh_g = -np.array([np.sum(rho_g[:ig]) for ig in range(len(r_g))]) / r_g**2 # xc force xc = XC(calc.get_xc_functional()) v_sg = np.zeros_like(n_sg) xc.calculate_spherical(a.xc_correction.rgd, n_sg, v_sg) fxc_sg = np.array([a.xc_correction.rgd.derivative(v_sg[s]) for s in range(Ns)]) fxc_g = np.sum(fxc_sg, axis=0) / Ns # f_sg = np.tile(fc_g, (Ns, 1)) + np.tile(fh_g, (Ns, 1)) + fxc_sg f_sg = np.tile(fc_g + fh_g + fxc_g, (Ns, 1)) return f_sg[:] / r_g
def get_density(self, atom_indicees=None): """Get sum of atomic densities from the given atom list. All atoms are taken if the list is not given.""" all_atoms = self.calculator.get_atoms() if atom_indicees is None: atom_indicees = range(len(all_atoms)) density = self.calculator.density density.set_positions(all_atoms.get_scaled_positions() % 1.0, self.calculator.wfs.rank_a) # select atoms atoms = [] D_asp = {} rank_a = [] all_D_asp = self.calculator.density.D_asp all_rank_a = self.calculator.density.rank_a for a in atom_indicees: if a in all_D_asp: D_asp[len(atoms)] = all_D_asp.get(a) atoms.append(all_atoms[a]) rank_a.append(all_rank_a[a]) atoms = Atoms(atoms, cell=all_atoms.get_cell()) spos_ac = atoms.get_scaled_positions() % 1.0 Z_a = atoms.get_atomic_numbers() par = self.calculator.input_parameters setups = Setups(Z_a, par.setups, par.basis, par.lmax, XC(par.xc), self.calculator.wfs.world) self.D_asp = D_asp # initialize self.initialize(setups, par.stencils[1], self.calculator.timer, [0] * len(atoms), False) self.set_mixer(None) self.set_positions(spos_ac, rank_a) basis_functions = BasisFunctions( self.gd, [setup.phit_j for setup in self.setups], cut=True) basis_functions.set_positions(spos_ac) self.initialize_from_atomic_densities(basis_functions) aed_sg, gd = Density.get_all_electron_density(self, atoms, gridrefinement=2) return aed_sg[0], gd
def __init__(self, xc='LDA', finegrid=False, **parameters): """Self-Interaction Corrected Functionals (PZ-SIC). finegrid: boolean Use fine grid for energy functional evaluations? """ if isinstance(xc, basestring): xc = XC(xc) if xc.orbital_dependent: raise ValueError('SIC does not support ' + xc.name) self.xc = xc XCFunctional.__init__(self, xc.name + '-PZ-SIC', xc.type) self.finegrid = finegrid self.parameters = parameters
def get_density(self, atom_indices=None, gridrefinement=2): """Get sum of atomic densities from the given atom list. Parameters ---------- atom_indices : list_like All atoms are taken if the list is not given. gridrefinement : 1, 2, 4 Gridrefinement given to get_all_electron_density Returns ------- type spin summed density, grid_descriptor """ all_atoms = self.calculator.get_atoms() if atom_indices is None: atom_indices = range(len(all_atoms)) # select atoms atoms = self.calculator.get_atoms()[atom_indices] spos_ac = atoms.get_scaled_positions() Z_a = atoms.get_atomic_numbers() par = self.calculator.parameters setups = Setups(Z_a, par.setups, par.basis, XC(par.xc), self.calculator.wfs.world) # initialize self.initialize(setups, self.calculator.timer, np.zeros( (len(atoms), 3)), False) self.set_mixer(None) rank_a = self.gd.get_ranks_from_positions(spos_ac) self.set_positions(spos_ac, AtomPartition(self.gd.comm, rank_a)) basis_functions = BasisFunctions( self.gd, [setup.phit_j for setup in self.setups], cut=True) basis_functions.set_positions(spos_ac) self.initialize_from_atomic_densities(basis_functions) aed_sg, gd = self.get_all_electron_density(atoms, gridrefinement) return aed_sg.sum(axis=0), gd
def get_libvdwxc_functional(name, **kwargs): if 'name' in kwargs: name2 = kwargs.pop('name') assert name == name2 funcs = { 'vdW-DF': vdw_df, 'vdW-DF2': vdw_df2, 'vdW-DF-cx': vdw_df_cx, 'optPBE-vdW': vdw_optPBE, 'optB88-vdW': vdw_optB88, 'C09-vdW': vdw_C09, 'BEEF-vdW': vdw_beef, 'mBEEF-vdW': vdw_mbeef } semilocal_xc = kwargs.pop('semilocal_xc', None) if semilocal_xc is not None: from gpaw.xc import XC semilocal_xc = XC(semilocal_xc) func = funcs[name](**kwargs) if semilocal_xc is not None: assert semilocal_xc.name == func.semilocal_xc.name return func
def __init__(self, name, hybrid=None, xc=None, omega=None): """Mix standard functionals with exact exchange. name: str Name of hybrid functional. hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. """ if name == 'EXX': assert hybrid is None and xc is None hybrid = 1.0 xc = XC(XCNull()) elif name == 'PBE0': assert hybrid is None and xc is None hybrid = 0.25 xc = XC('HYB_GGA_XC_PBEH') elif name == 'B3LYP': assert hybrid is None and xc is None hybrid = 0.2 xc = XC('HYB_GGA_XC_B3LYP') elif name == 'HSE03': assert hybrid is None and xc is None and omega is None hybrid = 0.25 omega = 0.106 xc = XC('HYB_GGA_XC_HSE03') elif name == 'HSE06': assert hybrid is None and xc is None and omega is None hybrid = 0.25 omega = 0.11 xc = XC('HYB_GGA_XC_HSE06') if isinstance(xc, str): xc = XC(xc) self.hybrid = float(hybrid) self.xc = xc self.omega = omega self.type = xc.type XCFunctional.__init__(self, name)
def initialize_fxc(self, niter): self.has_fxc = self.fxc_name is not None if not self.has_fxc: return self.timer.start('Initialize fxc') # XXX: Similar functionality is available in # paw.py: PAW.linearize_to_xc(self, newxc) # See test/lcaotddft/fxc_vs_linearize.py get_H_MM = self.get_hamiltonian_matrix # Calculate deltaXC: 1. take current H_MM if niter == 0: self.deltaXC_H_uMM = [None] * len(self.wfs.kpt_u) for u, kpt in enumerate(self.wfs.kpt_u): self.deltaXC_H_uMM[u] = get_H_MM(kpt, addfxc=False) # Update hamiltonian.xc if self.fxc_name == 'RPA': xc_name = 'null' else: xc_name = self.fxc_name # XXX: xc is not written to the gpw file # XXX: so we need to set it always xc = XC(xc_name) xc.initialize(self.density, self.hamiltonian, self.wfs, self.occupations) xc.set_positions(self.hamiltonian.spos_ac) self.hamiltonian.xc = xc self.update() # Calculate deltaXC: 2. update with new H_MM if niter == 0: for u, kpt in enumerate(self.wfs.kpt_u): self.deltaXC_H_uMM[u] -= get_H_MM(kpt, addfxc=False) self.timer.stop('Initialize fxc')
def calculate_Kxc(gd, nt_sG, npw, Gvec_Gc, nG, vol, bcell_cv, R_av, setups, D_asp, functional='ALDA', density_cut=None): """ALDA kernel""" # The soft part #assert np.abs(nt_sG[0].shape - nG).sum() == 0 if functional == 'ALDA_X': x_only = True A_x = -3. / 4. * (3. / np.pi)**(1. / 3.) nspins = len(nt_sG) assert nspins in [1, 2] fxc_sg = nspins**(1. / 3.) * 4. / 9. * A_x * nt_sG**(-2. / 3.) else: assert len(nt_sG) == 1 x_only = False fxc_sg = np.zeros_like(nt_sG) xc = XC(functional[1:]) xc.calculate_fxc(gd, nt_sG, fxc_sg) if density_cut is not None: fxc_sg[np.where(nt_sG * len(nt_sG) < density_cut)] = 0.0 # FFT fxc(r) nG0 = nG[0] * nG[1] * nG[2] tmp_sg = [np.fft.fftn(fxc_sg[s]) * vol / nG0 for s in range(len(nt_sG))] r_vg = gd.get_grid_point_coordinates() Kxc_sGG = np.zeros((len(fxc_sg), npw, npw), dtype=complex) for s in range(len(fxc_sg)): for iG in range(npw): for jG in range(npw): dG_c = Gvec_Gc[iG] - Gvec_Gc[jG] if (nG / 2 - np.abs(dG_c) > 0).all(): index = (dG_c + nG) % nG Kxc_sGG[s, iG, jG] = tmp_sg[s][index[0], index[1], index[2]] else: # not in the fft index dG_v = np.dot(dG_c, bcell_cv) dGr_g = gemmdot(dG_v, r_vg, beta=0.0) Kxc_sGG[s, iG, jG] = gd.integrate(np.exp(-1j * dGr_g) * fxc_sg[s]) # The PAW part KxcPAW_sGG = np.zeros_like(Kxc_sGG) dG_GGv = np.zeros((npw, npw, 3)) for iG in range(npw): for jG in range(npw): dG_c = Gvec_Gc[iG] - Gvec_Gc[jG] dG_GGv[iG, jG] = np.dot(dG_c, bcell_cv) for a, setup in enumerate(setups): if rank == a % size: rgd = setup.xc_correction.rgd n_qg = setup.xc_correction.n_qg nt_qg = setup.xc_correction.nt_qg nc_g = setup.xc_correction.nc_g nct_g = setup.xc_correction.nct_g Y_nL = setup.xc_correction.Y_nL dv_g = rgd.dv_g D_sp = D_asp[a] B_pqL = setup.xc_correction.B_pqL D_sLq = np.inner(D_sp, B_pqL.T) nspins = len(D_sp) f_sg = rgd.empty(nspins) ft_sg = rgd.empty(nspins) n_sLg = np.dot(D_sLq, n_qg) nt_sLg = np.dot(D_sLq, nt_qg) # Add core density n_sLg[:, 0] += np.sqrt(4. * np.pi) / nspins * nc_g nt_sLg[:, 0] += np.sqrt(4. * np.pi) / nspins * nct_g coefatoms_GG = np.exp(-1j * np.inner(dG_GGv, R_av[a])) for n, Y_L in enumerate(Y_nL): w = weight_n[n] f_sg[:] = 0.0 n_sg = np.dot(Y_L, n_sLg) if x_only: f_sg = nspins * (4 / 9.) * A_x * (nspins * n_sg)**(-2 / 3.) else: xc.calculate_fxc(rgd, n_sg, f_sg) ft_sg[:] = 0.0 nt_sg = np.dot(Y_L, nt_sLg) if x_only: ft_sg = nspins * (4 / 9.) * (A_x * (nspins * nt_sg)**(-2 / 3.)) else: xc.calculate_fxc(rgd, nt_sg, ft_sg) for i in range(len(rgd.r_g)): coef_GG = np.exp(-1j * np.inner(dG_GGv, R_nv[n]) * rgd.r_g[i]) for s in range(len(f_sg)): KxcPAW_sGG[s] += w * np.dot(coef_GG, (f_sg[s, i] - ft_sg[s, i]) * dv_g[i]) * coefatoms_GG world.sum(KxcPAW_sGG) Kxc_sGG += KxcPAW_sGG return Kxc_sGG / vol
def calculate_Kxc(gd, nt_sG, npw, Gvec_Gc, nG, vol, bcell_cv, R_av, setups, D_asp): """LDA kernel""" # The soft part assert np.abs(nt_sG[0].shape - nG).sum() == 0 xc = XC('LDA') fxc_sg = np.zeros_like(nt_sG) xc.calculate_fxc(gd, nt_sG, fxc_sg) fxc_g = fxc_sg[0] # FFT fxc(r) nG0 = nG[0] * nG[1] * nG[2] tmp_g = np.fft.fftn(fxc_g) * vol / nG0 r_vg = gd.get_grid_point_coordinates() Kxc_GG = np.zeros((npw, npw), dtype=complex) for iG in range(npw): for jG in range(npw): dG_c = Gvec_Gc[iG] - Gvec_Gc[jG] if (nG / 2 - np.abs(dG_c) > 0).all(): index = (dG_c + nG) % nG Kxc_GG[iG, jG] = tmp_g[index[0], index[1], index[2]] else: # not in the fft index dG_v = np.dot(dG_c, bcell_cv) dGr_g = gemmdot(dG_v, r_vg, beta=0.0) Kxc_GG[iG, jG] = gd.integrate(np.exp(-1j*dGr_g)*fxc_g) KxcPAW_GG = np.zeros_like(Kxc_GG) # The PAW part dG_GGv = np.zeros((npw, npw, 3)) for iG in range(npw): for jG in range(npw): dG_c = Gvec_Gc[iG] - Gvec_Gc[jG] dG_GGv[iG, jG] = np.dot(dG_c, bcell_cv) for a, setup in enumerate(setups): if rank == a % size: rgd = setup.xc_correction.rgd n_qg = setup.xc_correction.n_qg nt_qg = setup.xc_correction.nt_qg nc_g = setup.xc_correction.nc_g nct_g = setup.xc_correction.nct_g Y_nL = setup.xc_correction.Y_nL dv_g = rgd.dv_g D_sp = D_asp[a] B_pqL = setup.xc_correction.B_pqL D_sLq = np.inner(D_sp, B_pqL.T) nspins = len(D_sp) assert nspins == 1 f_sg = rgd.empty(nspins) ft_sg = rgd.empty(nspins) n_sLg = np.dot(D_sLq, n_qg) nt_sLg = np.dot(D_sLq, nt_qg) # Add core density n_sLg[:, 0] += sqrt(4 * pi) / nspins * nc_g nt_sLg[:, 0] += sqrt(4 * pi) / nspins * nct_g coefatoms_GG = np.exp(-1j * np.inner(dG_GGv, R_av[a])) for n, Y_L in enumerate(Y_nL): w = weight_n[n] f_sg[:] = 0.0 n_sg = np.dot(Y_L, n_sLg) xc.calculate_fxc(rgd, n_sg, f_sg) ft_sg[:] = 0.0 nt_sg = np.dot(Y_L, nt_sLg) xc.calculate_fxc(rgd, nt_sg, ft_sg) coef_GGg = np.exp(-1j * np.outer(np.inner(dG_GGv, R_nv[n]), rgd.r_g)).reshape(npw,npw,rgd.ng) KxcPAW_GG += w * np.dot(coef_GGg, (f_sg[0]-ft_sg[0]) * dv_g) * coefatoms_GG world.sum(KxcPAW_GG) Kxc_GG += KxcPAW_GG return Kxc_GG / vol
class AllElectronAtom: def __init__(self, symbol, xc='LDA', spinpol=False, dirac=False, configuration=None, log=None): """All-electron calculation for spherically symmetric atom. symbol: str (or int) Chemical symbol (or atomic number). xc: str Name of XC-functional. spinpol: bool If true, do spin-polarized calculation. Default is spin-paired. dirac: bool Solve Dirac equation instead of Schrödinger equation. configuration: list Electronic configuration for symbol, format as in gpaw.atom.configurations log: stream Text output.""" if isinstance(symbol, int): symbol = chemical_symbols[symbol] self.symbol = symbol self.Z = atomic_numbers[symbol] self.nspins = 1 + int(bool(spinpol)) self.dirac = bool(dirac) if configuration is not None: self.configuration = copy.deepcopy(configuration) else: self.configuration = None self.scalar_relativistic = False if isinstance(xc, str): self.xc = XC(xc) else: self.xc = xc self.fd = log or sys.stdout self.vr_sg = None # potential * r self.n_sg = 0.0 # density self.rgd = None # radial grid descriptor # Energies: self.ekin = None self.eeig = None self.eH = None self.eZ = None self.channels = None self.initialize_configuration(self.configuration) self.log('Z: ', self.Z) self.log('Name: ', atomic_names[self.Z]) self.log('Symbol: ', symbol) self.log('XC-functional: ', self.xc.name) self.log('Equation: ', ['Schrödinger', 'Dirac'][self.dirac]) self.method = 'Gaussian basis-set' def log(self, *args, **kwargs): print(file=self.fd, *args, **kwargs) def initialize_configuration(self, configuration=None): self.f_lsn = {} if configuration is None: configuration = configurations[self.symbol][1] for n, l, f, e in configuration: if l not in self.f_lsn: self.f_lsn[l] = [[] for s in range(self.nspins)] if self.nspins == 1: self.f_lsn[l][0].append(f) else: # Use Hund's rule: f0 = min(f, 2 * l + 1) self.f_lsn[l][0].append(f0) self.f_lsn[l][1].append(f - f0) if 0: n = 2 + len(self.f_lsn[2][0]) if self.f_lsn[0][0][n] == 2: self.f_lsn[0][0][n] = 1 self.f_lsn[2][0][n - 3] += 1 def add(self, n, l, df=+1, s=None): """Add (remove) electrons.""" if s is None: if self.nspins == 1: s = 0 else: self.add(n, l, 0.5 * df, 0) self.add(n, l, 0.5 * df, 1) return if l not in self.f_lsn: self.f_lsn[l] = [[] for x in range(self.nspins)] f_n = self.f_lsn[l][s] if len(f_n) < n - l: f_n.extend([0] * (n - l - len(f_n))) f_n[n - l - 1] += df def initialize(self, ngpts=2000, rcut=50.0, alpha1=0.01, alpha2=None, ngauss=50, eps=1.0e-7): """Initialize basis sets and radial grid. ngpts: int Number of grid points for radial grid. rcut: float Cutoff for radial grid. alpha1: float Smallest exponent for gaussian. alpha2: float Largest exponent for gaussian. ngauss: int Number of gaussians. eps: float Cutoff for eigenvalues of overlap matrix.""" if alpha2 is None: alpha2 = 50.0 * self.Z**2 # Use grid with r(0)=0, r(1)=a and r(ngpts)=rcut: a = 1 / alpha2**0.5 / 20 b = (rcut - a * ngpts) / (rcut * ngpts) b = 1 / round(1 / b) self.rgd = AERadialGridDescriptor(a, b, ngpts) self.log('Grid points: %d (%.5f, %.5f, %.5f, ..., %.3f, %.3f)' % ((self.rgd.N,) + tuple(self.rgd.r_g[[0, 1, 2, -2, -1]]))) # Distribute exponents between alpha1 and alpha2: alpha_B = alpha1 * (alpha2 / alpha1)**np.linspace(0, 1, ngauss) self.log('Exponents: %d (%.3f, %.3f, ..., %.3f, %.3f)' % ((ngauss,) + tuple(alpha_B[[0, 1, -2, -1]]))) # Maximum l value: lmax = max(self.f_lsn.keys()) self.channels = [] nb_l = [] if not self.dirac: for l in range(lmax + 1): basis = GaussianBasis(l, alpha_B, self.rgd, eps) nb_l.append(len(basis)) for s in range(self.nspins): self.channels.append(Channel(l, s, self.f_lsn[l][s], basis)) else: for K in range(1, lmax + 2): leff = (K**2 - (self.Z / c)**2)**0.5 - 1 basis = GaussianBasis(leff, alpha_B, self.rgd, eps) nb_l.append(len(basis)) for k, l in [(-K, K - 1), (K, K)]: if l > lmax: continue f_n = self.f_lsn[l][0] j = abs(k) - 0.5 f_n = (2 * j + 1) / (4 * l + 2) * np.array(f_n) self.channels.append(DiracChannel(k, f_n, basis)) self.log('Basis functions: %s (%s)' % (', '.join([str(nb) for nb in nb_l]), ', '.join('spdf'[:lmax + 1]))) self.vr_sg = self.rgd.zeros(self.nspins) self.vr_sg[:] = -self.Z def solve(self): """Diagonalize Schrödinger equation.""" self.eeig = 0.0 for channel in self.channels: if self.method == 'Gaussian basis-set': channel.solve(self.vr_sg[channel.s]) else: channel.solve2(self.vr_sg[channel.s], self.scalar_relativistic, self.Z) self.eeig += channel.get_eigenvalue_sum() def calculate_density(self): """Calculate elctron density and kinetic energy.""" self.n_sg = self.rgd.zeros(self.nspins) for channel in self.channels: self.n_sg[channel.s] += channel.calculate_density() def calculate_electrostatic_potential(self): """Calculate electrostatic potential and energy.""" n_g = self.n_sg.sum(0) self.vHr_g = self.rgd.poisson(n_g) self.eH = 0.5 * self.rgd.integrate(n_g * self.vHr_g, -1) self.eZ = -self.Z * self.rgd.integrate(n_g, -1) def calculate_xc_potential(self): self.vxc_sg = self.rgd.zeros(self.nspins) self.exc = self.xc.calculate_spherical(self.rgd, self.n_sg, self.vxc_sg) def step(self): self.solve() self.calculate_density() self.calculate_electrostatic_potential() self.calculate_xc_potential() self.vr_sg = self.vxc_sg * self.rgd.r_g self.vr_sg += self.vHr_g self.vr_sg -= self.Z self.ekin = (self.eeig - self.rgd.integrate((self.vr_sg * self.n_sg).sum(0), -1)) def run(self, mix=0.4, maxiter=117, dnmax=1e-9): if self.channels is None: self.initialize() if self.dirac: equation = 'Dirac' elif self.scalar_relativistic: equation = 'scalar-relativistic Schrödinger' else: equation = 'non-relativistic Schrödinger' self.log('\nSolving %s equation using %s:' % (equation, self.method)) dn = self.Z vr_old_sg = None n_old_sg = None for iter in range(maxiter): self.log('.', end='') self.fd.flush() if iter > 0: self.vr_sg *= mix self.vr_sg += (1 - mix) * vr_old_sg dn = self.rgd.integrate(abs(self.n_sg - n_old_sg).sum(0)) if dn <= dnmax: self.log('\nConverged in', iter, 'steps') break vr_old_sg = self.vr_sg n_old_sg = self.n_sg self.step() self.summary() if self.method != 'Gaussian basis-set': for channel in self.channels: assert channel.solve2ok if dn > dnmax: raise RuntimeError('Did not converge!') def refine(self): self.method = 'finite difference' self.run(dnmax=1e-6, mix=0.14, maxiter=200) def summary(self): self.write_states() self.write_energies() def write_states(self): self.log('\n state occupation eigenvalue <r>') if self.dirac: self.log(' nl(j) [Hartree] [eV] [Bohr]') else: self.log(' nl [Hartree] [eV] [Bohr]') self.log('-----------------------------------------------------') states = [] for ch in self.channels: for n, f in enumerate(ch.f_n): states.append((ch.e_n[n], ch, n)) states.sort() for e, ch, n in states: name = str(n + ch.l + 1) + ch.name if self.nspins == 2: name += '(%s)' % '+-'[ch.s] n_g = ch.calculate_density(n) rave = self.rgd.integrate(n_g, 1) self.log(' %-7s %6.3f %13.6f %13.5f %6.3f' % (name, ch.f_n[n], e, e * units.Hartree, rave)) def write_energies(self): self.log('\nEnergies: [Hartree] [eV]') self.log('--------------------------------------------') for text, e in [('kinetic ', self.ekin), ('coulomb (e-e)', self.eH), ('coulomb (e-n)', self.eZ), ('xc ', self.exc), ('total ', self.ekin + self.eH + self.eZ + self.exc)]: self.log(' %s %+13.6f %+13.5f' % (text, e, units.Hartree * e)) self.calculate_exx() self.log('\nExact exchange energy: %.6f Hartree, %.5f eV' % (self.exx, self.exx * units.Hartree)) def get_channel(self, l=None, s=0, k=None): if self.dirac: for channel in self.channels: if channel.k == k: return channel else: for channel in self.channels: if channel.l == l and channel.s == s: return channel raise ValueError def get_orbital(self, n, l=None, s=0, k=None): channel = self.get_channel(l, s, k) return channel.basis.expand(channel.C_nb[n]) def plot_wave_functions(self, rc=4.0): import matplotlib.pyplot as plt for ch in self.channels: for n in range(len(ch.f_n)): fr_g = ch.basis.expand(ch.C_nb[n]) * self.rgd.r_g # fr_g = ch.phi_ng[n] name = str(n + ch.l + 1) + ch.name lw = 2 if self.nspins == 2: name += '(%s)' % '+-'[ch.s] if ch.s == 1: lw = 1 if self.dirac and ch.k > 0: lw = 1 ls = ['-', '--', '-.', ':'][ch.l] n_g = ch.calculate_density(n) rave = self.rgd.integrate(n_g, 1) gave = self.rgd.round(rave) fr_g *= np.sign(fr_g[gave], 0) plt.plot(self.rgd.r_g, fr_g, ls=ls, lw=lw, color=colors[n + ch.l], label=name) plt.legend(loc='best') plt.xlabel('r [Bohr]') plt.ylabel('$r\\phi(r)$') plt.axis(xmax=rc) plt.show() def logarithmic_derivative(self, l, energies, rcut): ch = Channel(l) gcut = self.rgd.round(rcut) u_g = self.rgd.empty() logderivs = [] d0 = 42.0 offset = 0 for e in energies: dudr = ch.integrate_outwards(u_g, self.rgd, self.vr_sg[0], gcut, e, self.scalar_relativistic, self.Z)[0] d1 = np.arctan(dudr / u_g[gcut]) / pi + offset if d1 > d0: offset -= 1 d1 -= 1 logderivs.append(d1) d0 = d1 return np.array(logderivs) def calculate_exx(self, s=None): if s is None: self.exx = sum(self.calculate_exx(s) for s in range(self.nspins)) / self.nspins return self.exx states = [] lmax = 0 for ch in self.channels: l = ch.l for n, phi_g in enumerate(ch.phi_ng): f = ch.f_n[n] if f > 0 and ch.s == s: states.append((l, f * self.nspins / 2.0 / (2 * l + 1), phi_g)) if l > lmax: lmax = l G_LLL = gaunt(lmax) exx = 0.0 j1 = 0 for l1, f1, phi1_g in states: f = 1.0 for l2, f2, phi2_g in states[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 exx -= e * G * f1 * f2 f = 2.0 j1 += 1 return exx
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
# ------------------------------------------------------------------- from gpaw.test.ut_common import ase_svnversion, shapeopt, TestCase, \ TextTestRunner, CustomTextTestRunner, defaultTestLoader, \ initialTestLoader, create_random_atoms, create_parsize_maxbands memstats = False if memstats: # Developer use of this feature requires ASE 3.1.0 svn.rev. 905 or later. assert ase_svnversion >= 905 # wasn't bug-free untill 973! from ase.utils.memory import MemorySingleton, MemoryStatistics # ------------------------------------------------------------------- p = InputParameters(spinpol=False) xc = XC(p.xc) p.setups = dict([(symbol, SetupData(symbol, xc.name)) for symbol in 'HO']) class UTBandParallelSetup(TestCase): """ Setup a simple band parallel calculation.""" # Number of bands nbands = 36 # Spin-paired, single kpoint nspins = 1 nibzkpts = 1 # Strided or blocked groups
def initialize_1d(self): self.ae = self.nlfunc.ae self.xc = XC(self.functional) self.v_g = np.zeros(self.ae.N) self.e_g = np.zeros(self.ae.N)
from __future__ import print_function import numpy as np import numpy.random as ra from gpaw.setup import create_setup from gpaw.xc import XC from gpaw.test import equal x = 0.000001 ra.seed(8) for xc in ['LDA', 'PBE']: print(xc) xc = XC(xc) s = create_setup('N', xc) ni = s.ni nii = ni * (ni + 1) // 2 D_p = 0.1 * ra.random(nii) + 0.2 H_p = np.zeros(nii) E = xc.calculate_paw_correction(s, D_p.reshape(1, -1), H_p.reshape(1, -1)) dD_p = x * ra.random(nii) dE = np.dot(H_p, dD_p) / x D_p += dD_p Ep = xc.calculate_paw_correction(s, D_p.reshape(1, -1)) D_p -= 2 * dD_p Em = xc.calculate_paw_correction(s, D_p.reshape(1, -1)) print(dE, dE - 0.5 * (Ep - Em) / x) equal(dE, 0.5 * (Ep - Em) / x, 1e-6) Ems = xc.calculate_paw_correction(s, np.array([0.5 * D_p, 0.5 * D_p])) print(Em - Ems)
def calculate_Kxc(pd, nt_sG, R_av, setups, D_asp, functional='ALDA', density_cut=None): """ALDA kernel""" gd = pd.gd npw = pd.ngmax nG = pd.gd.N_c vol = pd.gd.volume bcell_cv = np.linalg.inv(pd.gd.cell_cv) G_Gv = pd.get_reciprocal_vectors() # The soft part #assert np.abs(nt_sG[0].shape - nG).sum() == 0 if functional == 'ALDA_X': x_only = True A_x = -3. / 4. * (3. / np.pi)**(1. / 3.) nspins = len(nt_sG) assert nspins in [1, 2] fxc_sg = nspins**(1. / 3.) * 4. / 9. * A_x * nt_sG**(-2. / 3.) else: assert len(nt_sG) == 1 x_only = False fxc_sg = np.zeros_like(nt_sG) xc = XC(functional[1:]) xc.calculate_fxc(gd, nt_sG, fxc_sg) if density_cut is not None: fxc_sg[np.where(nt_sG * len(nt_sG) < density_cut)] = 0.0 # FFT fxc(r) nG0 = nG[0] * nG[1] * nG[2] tmp_sg = [np.fft.fftn(fxc_sg[s]) * vol / nG0 for s in range(len(nt_sG))] r_vg = gd.get_grid_point_coordinates() Kxc_sGG = np.zeros((len(fxc_sg), npw, npw), dtype=complex) for s in range(len(fxc_sg)): for iG, iQ in enumerate(pd.Q_qG[0]): iQ_c = (np.unravel_index(iQ, nG) + nG // 2) % nG - nG // 2 for jG, jQ in enumerate(pd.Q_qG[0]): jQ_c = (np.unravel_index(jQ, nG) + nG // 2) % nG - nG // 2 ijQ_c = (iQ_c - jQ_c) if (abs(ijQ_c) < nG // 2).all(): Kxc_sGG[s, iG, jG] = tmp_sg[s][tuple(ijQ_c)] # The PAW part KxcPAW_sGG = np.zeros_like(Kxc_sGG) dG_GGv = np.zeros((npw, npw, 3)) for v in range(3): dG_GGv[:, :, v] = np.subtract.outer(G_Gv[:, v], G_Gv[:, v]) for a, setup in enumerate(setups): if rank == a % size: rgd = setup.xc_correction.rgd n_qg = setup.xc_correction.n_qg nt_qg = setup.xc_correction.nt_qg nc_g = setup.xc_correction.nc_g nct_g = setup.xc_correction.nct_g Y_nL = setup.xc_correction.Y_nL dv_g = rgd.dv_g D_sp = D_asp[a] B_pqL = setup.xc_correction.B_pqL D_sLq = np.inner(D_sp, B_pqL.T) nspins = len(D_sp) f_sg = rgd.empty(nspins) ft_sg = rgd.empty(nspins) n_sLg = np.dot(D_sLq, n_qg) nt_sLg = np.dot(D_sLq, nt_qg) # Add core density n_sLg[:, 0] += np.sqrt(4. * np.pi) / nspins * nc_g nt_sLg[:, 0] += np.sqrt(4. * np.pi) / nspins * nct_g coefatoms_GG = np.exp(-1j * np.inner(dG_GGv, R_av[a])) for n, Y_L in enumerate(Y_nL): w = weight_n[n] f_sg[:] = 0.0 n_sg = np.dot(Y_L, n_sLg) if x_only: f_sg = nspins * (4 / 9.) * A_x * (nspins * n_sg)**(-2 / 3.) else: xc.calculate_fxc(rgd, n_sg, f_sg) ft_sg[:] = 0.0 nt_sg = np.dot(Y_L, nt_sLg) if x_only: ft_sg = nspins * (4 / 9.) * (A_x * (nspins * nt_sg)**(-2 / 3.)) else: xc.calculate_fxc(rgd, nt_sg, ft_sg) for i in range(len(rgd.r_g)): coef_GG = np.exp(-1j * np.inner(dG_GGv, R_nv[n]) * rgd.r_g[i]) for s in range(len(f_sg)): KxcPAW_sGG[s] += w * np.dot(coef_GG, (f_sg[s, i] - ft_sg[s, i]) * dv_g[i]) * coefatoms_GG world.sum(KxcPAW_sGG) Kxc_sGG += KxcPAW_sGG return Kxc_sGG / vol
class OmegaMatrix: """ Omega matrix in Casidas linear response formalism Parameters - calculator: the calculator object the ground state calculation - kss: the Kohn-Sham singles object - xc: the exchange correlation approx. to use - derivativeLevel: which level i of d^i Exc/dn^i to use - numscale: numeric epsilon for derivativeLevel=0,1 - filehandle: the oject can be read from a filehandle - txt: output stream or file name - finegrid: level of fine grid to use. 0: nothing, 1 for poisson only, 2 everything on the fine grid """ def __init__(self, calculator=None, kss=None, xc=None, derivativeLevel=None, numscale=0.001, filehandle=None, txt=None, finegrid=2, eh_comm=None, ): if not txt and calculator: txt = calculator.txt self.txt, firsttime = initialize_text_stream(txt, mpi.rank) if eh_comm == None: eh_comm = mpi.serial_comm self.eh_comm = eh_comm if filehandle is not None: self.kss = kss self.read(fh=filehandle) return None self.fullkss = kss self.finegrid = finegrid if calculator is None: return self.paw = calculator wfs = self.paw.wfs # handle different grid possibilities self.restrict = None self.poisson = PoissonSolver(nn=self.paw.hamiltonian.poisson.nn) if finegrid: self.poisson.set_grid_descriptor(self.paw.density.finegd) self.poisson.initialize() self.gd = self.paw.density.finegd if finegrid == 1: self.gd = wfs.gd else: self.poisson.set_grid_descriptor(wfs.gd) self.poisson.initialize() self.gd = wfs.gd self.restrict = Transformer(self.paw.density.finegd, wfs.gd, self.paw.input_parameters.stencils[1] ).apply if xc == 'RPA': xc = None # enable RPA as keyword if xc is not None: self.xc = XC(xc) self.xc.initialize(self.paw.density, self.paw.hamiltonian, wfs, self.paw.occupations) # check derivativeLevel if derivativeLevel is None: derivativeLevel= \ self.xc.get_functional().get_max_derivative_level() self.derivativeLevel = derivativeLevel # change the setup xc functional if needed # the ground state calculation may have used another xc if kss.npspins > kss.nvspins: spin_increased = True else: spin_increased = False else: self.xc = None self.numscale = numscale self.singletsinglet = False if kss.nvspins<2 and kss.npspins<2: # this will be a singlet to singlet calculation only self.singletsinglet=True nij = len(kss) self.Om = np.zeros((nij,nij)) self.get_full() def get_full(self): self.paw.timer.start('Omega RPA') self.get_rpa() self.paw.timer.stop() if self.xc is not None: self.paw.timer.start('Omega XC') self.get_xc() self.paw.timer.stop() self.eh_comm.sum(self.Om) self.full = self.Om def get_xc(self): """Add xc part of the coupling matrix""" # shorthands paw = self.paw wfs = paw.wfs gd = paw.density.finegd comm = gd.comm eh_comm = self.eh_comm fg = self.finegrid is 2 kss = self.fullkss nij = len(kss) Om_xc = self.Om # initialize densities # nt_sg is the smooth density on the fine grid with spin index if kss.nvspins==2: # spin polarised ground state calc. nt_sg = paw.density.nt_sg else: # spin unpolarised ground state calc. if kss.npspins==2: # construct spin polarised densities nt_sg = np.array([.5*paw.density.nt_sg[0], .5*paw.density.nt_sg[0]]) else: nt_sg = paw.density.nt_sg # check if D_sp have been changed before D_asp = self.paw.density.D_asp for a, D_sp in D_asp.items(): if len(D_sp) != kss.npspins: if len(D_sp) == 1: D_asp[a] = np.array([0.5 * D_sp[0], 0.5 * D_sp[0]]) else: D_asp[a] = np.array([D_sp[0] + D_sp[1]]) # restrict the density if needed if fg: nt_s = nt_sg else: nt_s = self.gd.zeros(nt_sg.shape[0]) for s in range(nt_sg.shape[0]): self.restrict(nt_sg[s], nt_s[s]) gd = paw.density.gd # initialize vxc or fxc if self.derivativeLevel==0: raise NotImplementedError if kss.npspins==2: v_g=nt_sg[0].copy() else: v_g=nt_sg.copy() elif self.derivativeLevel==1: pass elif self.derivativeLevel==2: fxc_sg = np.zeros(nt_sg.shape) self.xc.calculate_fxc(gd, nt_sg, fxc_sg) else: raise ValueError('derivativeLevel can only be 0,1,2') ## self.paw.my_nuclei = [] ns=self.numscale xc=self.xc print >> self.txt, 'XC',nij,'transitions' for ij in range(eh_comm.rank, nij, eh_comm.size): print >> self.txt,'XC kss['+'%d'%ij+']' timer = Timer() timer.start('init') timer2 = Timer() if self.derivativeLevel >= 1: # vxc is available # We use the numerical two point formula for calculating # the integral over fxc*n_ij. The results are # vvt_s smooth integral # nucleus.I_sp atom based correction matrices (pack2) # stored on each nucleus timer2.start('init v grids') vp_s=np.zeros(nt_s.shape,nt_s.dtype.char) vm_s=np.zeros(nt_s.shape,nt_s.dtype.char) if kss.npspins == 2: # spin polarised nv_s = nt_s.copy() nv_s[kss[ij].pspin] += ns * kss[ij].get(fg) xc.calculate(gd, nv_s, vp_s) nv_s = nt_s.copy() nv_s[kss[ij].pspin] -= ns * kss[ij].get(fg) xc.calculate(gd, nv_s, vm_s) else: # spin unpolarised nv = nt_s + ns * kss[ij].get(fg) xc.calculate(gd, nv, vp_s) nv = nt_s - ns * kss[ij].get(fg) xc.calculate(gd, nv, vm_s) vvt_s = (0.5 / ns) * (vp_s - vm_s) timer2.stop() # initialize the correction matrices timer2.start('init v corrections') I_asp = {} for a, P_ni in wfs.kpt_u[kss[ij].spin].P_ani.items(): # create the modified density matrix Pi_i = P_ni[kss[ij].i] Pj_i = P_ni[kss[ij].j] P_ii = np.outer(Pi_i, Pj_i) # we need the symmetric form, hence we can pack P_p = pack(P_ii) D_sp = self.paw.density.D_asp[a].copy() D_sp[kss[ij].pspin] -= ns * P_p setup = wfs.setups[a] I_sp = np.zeros_like(D_sp) self.xc.calculate_paw_correction(setup, D_sp, I_sp) I_sp *= -1.0 D_sp = self.paw.density.D_asp[a].copy() D_sp[kss[ij].pspin] += ns * P_p self.xc.calculate_paw_correction(setup, D_sp, I_sp) I_sp /= 2.0 * ns I_asp[a] = I_sp timer2.stop() timer.stop() t0 = timer.get_time('init') timer.start(ij) for kq in range(ij,nij): weight = self.weight_Kijkq(ij, kq) if self.derivativeLevel == 0: # only Exc is available if kss.npspins==2: # spin polarised nv_g = nt_sg.copy() nv_g[kss[ij].pspin] += kss[ij].get(fg) nv_g[kss[kq].pspin] += kss[kq].get(fg) Excpp = xc.get_energy_and_potential( nv_g[0], v_g, nv_g[1], v_g) nv_g = nt_sg.copy() nv_g[kss[ij].pspin] += kss[ij].get(fg) nv_g[kss[kq].pspin] -= kss[kq].get(fg) Excpm = xc.get_energy_and_potential(\ nv_g[0],v_g,nv_g[1],v_g) nv_g = nt_sg.copy() nv_g[kss[ij].pspin] -=\ kss[ij].get(fg) nv_g[kss[kq].pspin] +=\ kss[kq].get(fg) Excmp = xc.get_energy_and_potential(\ nv_g[0],v_g,nv_g[1],v_g) nv_g = nt_sg.copy() nv_g[kss[ij].pspin] -= \ kss[ij].get(fg) nv_g[kss[kq].pspin] -=\ kss[kq].get(fg) Excpp = xc.get_energy_and_potential(\ nv_g[0],v_g,nv_g[1],v_g) else: # spin unpolarised nv_g=nt_sg + ns*kss[ij].get(fg)\ + ns*kss[kq].get(fg) Excpp = xc.get_energy_and_potential(nv_g,v_g) nv_g=nt_sg + ns*kss[ij].get(fg)\ - ns*kss[kq].get(fg) Excpm = xc.get_energy_and_potential(nv_g,v_g) nv_g=nt_sg - ns*kss[ij].get(fg)\ + ns*kss[kq].get(fg) Excmp = xc.get_energy_and_potential(nv_g,v_g) nv_g=nt_sg - ns*kss[ij].get(fg)\ - ns*kss[kq].get(fg) Excmm = xc.get_energy_and_potential(nv_g,v_g) Om_xc[ij,kq] += weight *\ 0.25*(Excpp-Excmp-Excpm+Excmm)/(ns*ns) elif self.derivativeLevel == 1: # vxc is available timer2.start('integrate') Om_xc[ij,kq] += weight*\ self.gd.integrate(kss[kq].get(fg)* vvt_s[kss[kq].pspin]) timer2.stop() timer2.start('integrate corrections') Exc = 0. for a, P_ni in wfs.kpt_u[kss[kq].spin].P_ani.items(): # create the modified density matrix Pk_i = P_ni[kss[kq].i] Pq_i = P_ni[kss[kq].j] P_ii = np.outer(Pk_i, Pq_i) # we need the symmetric form, hence we can pack # use pack as I_sp used pack2 P_p = pack(P_ii) Exc += np.dot(I_asp[a][kss[kq].pspin], P_p) Om_xc[ij, kq] += weight * self.gd.comm.sum(Exc) timer2.stop() elif self.derivativeLevel == 2: # fxc is available if kss.npspins==2: # spin polarised Om_xc[ij,kq] += weight *\ gd.integrate(kss[ij].get(fg) * kss[kq].get(fg) * fxc_sg[kss[ij].pspin, kss[kq].pspin]) else: # spin unpolarised Om_xc[ij,kq] += weight *\ gd.integrate(kss[ij].get(fg) * kss[kq].get(fg) * fxc_sg) # XXX still numeric derivatives for local terms timer2.start('integrate corrections') Exc = 0. for a, P_ni in wfs.kpt_u[kss[kq].spin].P_ani.items(): # create the modified density matrix Pk_i = P_ni[kss[kq].i] Pq_i = P_ni[kss[kq].j] P_ii = np.outer(Pk_i, Pq_i) # we need the symmetric form, hence we can pack # use pack as I_sp used pack2 P_p = pack(P_ii) Exc += np.dot(I_asp[a][kss[kq].pspin], P_p) Om_xc[ij, kq] += weight * self.gd.comm.sum(Exc) timer2.stop() if ij != kq: Om_xc[kq,ij] = Om_xc[ij,kq] timer.stop() ## timer2.write() if ij < (nij-1): print >> self.txt,'XC estimated time left',\ self.time_left(timer, t0, ij, nij) def Coulomb_integral_kss(self, kss_ij, kss_kq, phit, rhot, timer=None): # smooth part if timer: timer.start('integrate') I = self.gd.integrate(rhot * phit) if timer: timer.stop() timer.start('integrate corrections 2') wfs = self.paw.wfs Pij_ani = wfs.kpt_u[kss_ij.spin].P_ani Pkq_ani = wfs.kpt_u[kss_kq.spin].P_ani # Add atomic corrections Ia = 0.0 for a, Pij_ni in Pij_ani.items(): Pi_i = Pij_ni[kss_ij.i] Pj_i = Pij_ni[kss_ij.j] Dij_ii = np.outer(Pi_i, Pj_i) Dij_p = pack(Dij_ii) Pk_i = Pkq_ani[a][kss_kq.i] Pq_i = Pkq_ani[a][kss_kq.j] Dkq_ii = np.outer(Pk_i, Pq_i) Dkq_p = pack(Dkq_ii) C_pp = wfs.setups[a].M_pp # ---- # 2 > P P C P P # ---- ip jr prst ks qt # prst Ia += 2.0*np.dot(Dkq_p, np.dot(C_pp, Dij_p)) I += self.gd.comm.sum(Ia) if timer: timer.stop() return I def get_rpa(self): """calculate RPA part of the omega matrix""" # shorthands kss=self.fullkss finegrid=self.finegrid wfs = self.paw.wfs eh_comm = self.eh_comm # calculate omega matrix nij = len(kss) print >> self.txt,'RPA',nij,'transitions' Om = self.Om for ij in range(eh_comm.rank, nij, eh_comm.size): print >> self.txt,'RPA kss['+'%d'%ij+']=', kss[ij] timer = Timer() timer.start('init') timer2 = Timer() # smooth density including compensation charges timer2.start('with_compensation_charges 0') rhot_p = kss[ij].with_compensation_charges( finegrid is not 0) timer2.stop() # integrate with 1/|r_1-r_2| timer2.start('poisson') phit_p = np.zeros(rhot_p.shape, rhot_p.dtype.char) self.poisson.solve(phit_p, rhot_p, charge=None) timer2.stop() timer.stop() t0 = timer.get_time('init') timer.start(ij) if finegrid == 1: rhot = kss[ij].with_compensation_charges() phit = self.gd.zeros() ## print "shapes 0=",phit.shape,rhot.shape self.restrict(phit_p,phit) else: phit = phit_p rhot = rhot_p for kq in range(ij,nij): if kq != ij: # smooth density including compensation charges timer2.start('kq with_compensation_charges') rhot = kss[kq].with_compensation_charges( finegrid is 2) timer2.stop() pre = 2 * sqrt(kss[ij].get_energy() * kss[kq].get_energy() * kss[ij].get_weight() * kss[kq].get_weight() ) I = self.Coulomb_integral_kss(kss[ij], kss[kq], rhot, phit, timer2) Om[ij,kq] = pre * I if ij == kq: Om[ij,kq] += kss[ij].get_energy()**2 else: Om[kq,ij]=Om[ij,kq] timer.stop() ## timer2.write() if ij < (nij-1): t = timer.get_time(ij) # time for nij-ij calculations t = .5*t*(nij-ij) # estimated time for n*(n+1)/2, n=nij-(ij+1) print >> self.txt,'RPA estimated time left',\ self.timestring(t0*(nij-ij-1)+t) def singlets_triplets(self): """Split yourself into singlet and triplet transitions""" assert(self.fullkss.npspins == 2) assert(self.fullkss.nvspins == 1) # strip kss from down spins skss = KSSingles() tkss = KSSingles() map = [] for ij, ks in enumerate(self.fullkss): if ks.pspin == ks.spin: skss.append((ks + ks) / sqrt(2)) tkss.append((ks - ks) / sqrt(2)) map.append(ij) nkss = len(skss) # define the singlet and the triplet omega-matrixes sOm = OmegaMatrix(kss=skss) sOm.full = np.empty((nkss, nkss)) tOm = OmegaMatrix(kss=tkss) tOm.full = np.empty((nkss, nkss)) for ij in range(nkss): for kl in range(nkss): sOm.full[ij, kl] = (self.full[map[ij], map[kl]] + self.full[map[ij], nkss + map[kl]]) tOm.full[ij, kl] = (self.full[map[ij], map[kl]] - self.full[map[ij], nkss + map[kl]]) return sOm, tOm def timestring(self, t): ti = int(t + 0.5) td = ti // 86400 st = '' if td > 0: st += '%d' % td + 'd' ti -= td * 86400 th = ti // 3600 if th > 0: st += '%d' % th + 'h' ti -= th * 3600 tm = ti // 60 if tm > 0: st += '%d' % tm + 'm' ti -= tm * 60 st += '%d' % ti + 's' return st def time_left(self, timer, t0, ij, nij): t = timer.get_time(ij) # time for nij-ij calculations t = .5 * t * (nij - ij) # estimated time for n*(n+1)/2, n=nij-(ij+1) return self.timestring(t0 * (nij - ij - 1) + t) def get_map(self, istart=None, jend=None, energy_range=None): """Return the reduction map for the given requirements""" self.istart = istart self.jend = jend if istart is None and jend is None and energy_range is None: return None, self.fullkss # reduce the matrix print >> self.txt,'# diagonalize: %d transitions original'\ % len(self.fullkss) if energy_range is None: if istart is None: istart = self.kss.istart if self.fullkss.istart > istart: raise RuntimeError('istart=%d has to be >= %d' % (istart, self.kss.istart)) if jend is None: jend = self.kss.jend if self.fullkss.jend < jend: raise RuntimeError('jend=%d has to be <= %d' % (jend, self.kss.jend)) else: try: emin, emax = energy_range except: emax = energy_range emin = 0. emin /= Hartree emax /= Hartree map= [] kss = KSSingles() for ij, k in zip(range(len(self.fullkss)), self.fullkss): if energy_range is None: if k.i >= istart and k.j <= jend: kss.append(k) map.append(ij) else: if k.energy >= emin and k.energy < emax: kss.append(k) map.append(ij) kss.update() print >> self.txt, '# diagonalize: %d transitions now' % len(kss) return map, kss def diagonalize(self, istart=None, jend=None, energy_range=None, TDA=False): """Evaluate Eigenvectors and Eigenvalues:""" if TDA: raise NotImplementedError map, kss = self.get_map(istart, jend, energy_range) nij = len(kss) if map is None: evec = self.full.copy() else: evec = np.zeros((nij,nij)) for ij in range(nij): for kq in range(nij): evec[ij,kq] = self.full[map[ij],map[kq]] assert(len(evec) > 0) self.eigenvectors = evec self.eigenvalues = np.zeros((len(kss))) self.kss = kss diagonalize(self.eigenvectors, self.eigenvalues) def Kss(self, kss=None): """Set and get own Kohn-Sham singles""" if kss is not None: self.fullkss = kss if(hasattr(self,'fullkss')): return self.fullkss else: return None def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: f = open(filename, 'r') else: f = fh f.readline() nij = int(f.readline()) full = np.zeros((nij,nij)) for ij in range(nij): l = f.readline().split() for kq in range(ij,nij): full[ij,kq] = float(l[kq-ij]) full[kq,ij] = full[ij,kq] self.full = full if fh is None: f.close() def write(self, filename=None, fh=None): """Write current state to a file.""" if mpi.rank == mpi.MASTER: if fh is None: f = open(filename, 'w') else: f = fh f.write('# OmegaMatrix\n') nij = len(self.fullkss) f.write('%d\n' % nij) for ij in range(nij): for kq in range(ij,nij): f.write(' %g' % self.full[ij,kq]) f.write('\n') if fh is None: f.close() def weight_Kijkq(self, ij, kq): """weight for the coupling matrix terms""" kss = self.fullkss return 2.*sqrt( kss[ij].get_energy() * kss[kq].get_energy() * kss[ij].get_weight() * kss[kq].get_weight() ) def __str__(self): str='<OmegaMatrix> ' if hasattr(self,'eigenvalues'): str += 'dimension '+ ('%d'%len(self.eigenvalues)) str += "\neigenvalues: " for ev in self.eigenvalues: str += ' ' + ('%f'%(sqrt(ev) * Hartree)) return str
class AllElectronAtom: def __init__(self, symbol, xc='LDA', spinpol=False, dirac=False, log=sys.stdout): """All-electron calculation for spherically symmetric atom. symbol: str (or int) Chemical symbol (or atomic number). xc: str Name of XC-functional. spinpol: bool If true, do spin-polarized calculation. Default is spin-paired. dirac: bool Solve Dirac equation instead of Schrödinger equation. log: stream Text output.""" if isinstance(symbol, int): symbol = chemical_symbols[symbol] self.symbol = symbol self.Z = atomic_numbers[symbol] self.nspins = 1 + int(bool(spinpol)) self.dirac = bool(dirac) self.scalar_relativistic = False if isinstance(xc, str): self.xc = XC(xc) else: self.xc = xc if log is None: log = devnull self.fd = log self.vr_sg = None # potential * r self.n_sg = 0.0 # density self.rgd = None # radial grid descriptor # Energies: self.ekin = None self.eeig = None self.eH = None self.eZ = None self.channels = None self.initialize_configuration() self.log('Z: ', self.Z) self.log('Name: ', atomic_names[self.Z]) self.log('Symbol: ', symbol) self.log('XC-functional: ', self.xc.name) self.log('Equation: ', ['Schrödinger', 'Dirac'][self.dirac]) self.method = 'Gaussian basis-set' def log(self, *args, **kwargs): prnt(file=self.fd, *args, **kwargs) def initialize_configuration(self): self.f_lsn = {} for n, l, f, e in configurations[self.symbol][1]: if l not in self.f_lsn: self.f_lsn[l] = [[] for s in range(self.nspins)] if self.nspins == 1: self.f_lsn[l][0].append(f) else: # Use Hund's rule: f0 = min(f, 2 * l + 1) self.f_lsn[l][0].append(f0) self.f_lsn[l][1].append(f - f0) def add(self, n, l, df=+1, s=None): """Add (remove) electrons.""" if s is None: if self.nspins == 1: s = 0 else: self.add(n, l, 0.5 * df, 0) self.add(n, l, 0.5 * df, 1) return if l not in self.f_lsn: self.f_lsn[l] = [[] for x in range(self.nspins)] f_n = self.f_lsn[l][s] if len(f_n) < n - l: f_n.extend([0] * (n - l - len(f_n))) f_n[n - l - 1] += df def initialize(self, ngpts=2000, rcut=50.0, alpha1=0.01, alpha2=None, ngauss=50, eps=1.0e-7): """Initialize basis sets and radial grid. ngpts: int Number of grid points for radial grid. rcut: float Cutoff for radial grid. alpha1: float Smallest exponent for gaussian. alpha2: float Largest exponent for gaussian. ngauss: int Number of gaussians. eps: float Cutoff for eigenvalues of overlap matrix.""" if alpha2 is None: alpha2 = 50.0 * self.Z**2 # Use grid with r(0)=0, r(1)=a and r(ngpts)=rcut: a = 1 / alpha2**0.5 / 20 b = (rcut - a * ngpts) / (rcut * ngpts) b = 1 / round(1 / b) self.rgd = AERadialGridDescriptor(a, b, ngpts) self.log('Grid points: %d (%.5f, %.5f, %.5f, ..., %.3f, %.3f)' % ((self.rgd.N,) + tuple(self.rgd.r_g[[0, 1, 2, -2, -1]]))) # Distribute exponents between alpha1 and alpha2: alpha_B = alpha1 * (alpha2 / alpha1)**np.linspace(0, 1, ngauss) self.log('Exponents: %d (%.3f, %.3f, ..., %.3f, %.3f)' % ((ngauss,) + tuple(alpha_B[[0, 1, -2, -1]]))) # Maximum l value: lmax = max(self.f_lsn.keys()) self.channels = [] nb_l = [] if not self.dirac: for l in range(lmax + 1): basis = GaussianBasis(l, alpha_B, self.rgd, eps) nb_l.append(len(basis)) for s in range(self.nspins): self.channels.append(Channel(l, s, self.f_lsn[l][s], basis)) else: for K in range(1, lmax + 2): leff = (K**2 - (self.Z / c)**2)**0.5 - 1 basis = GaussianBasis(leff, alpha_B, self.rgd, eps) nb_l.append(len(basis)) for k, l in [(-K, K - 1), (K, K)]: if l > lmax: continue f_n = self.f_lsn[l][0] j = abs(k) - 0.5 f_n = (2 * j + 1) / (4 * l + 2) * np.array(f_n) self.channels.append(DiracChannel(k, f_n, basis)) self.log('Basis functions: %s (%s)' % (', '.join([str(nb) for nb in nb_l]), ', '.join('spdf'[:lmax + 1]))) self.vr_sg = self.rgd.zeros(self.nspins) self.vr_sg[:] = -self.Z def solve(self): """Diagonalize Schrödinger equation.""" self.eeig = 0.0 for channel in self.channels: if self.method == 'Gaussian basis-set': channel.solve(self.vr_sg[channel.s]) else: channel.solve2(self.vr_sg[channel.s], self.scalar_relativistic) self.eeig += channel.get_eigenvalue_sum() def calculate_density(self): """Calculate elctron density and kinetic energy.""" self.n_sg = self.rgd.zeros(self.nspins) for channel in self.channels: self.n_sg[channel.s] += channel.calculate_density() def calculate_electrostatic_potential(self): """Calculate electrostatic potential and energy.""" n_g = self.n_sg.sum(0) self.vHr_g = self.rgd.poisson(n_g) self.eH = 0.5 * self.rgd.integrate(n_g * self.vHr_g, -1) self.eZ = -self.Z * self.rgd.integrate(n_g, -1) def calculate_xc_potential(self): self.vxc_sg = self.rgd.zeros(self.nspins) self.exc = self.xc.calculate_spherical(self.rgd, self.n_sg, self.vxc_sg) def step(self): self.solve() self.calculate_density() self.calculate_electrostatic_potential() self.calculate_xc_potential() self.vr_sg = self.vxc_sg * self.rgd.r_g self.vr_sg += self.vHr_g self.vr_sg -= self.Z self.ekin = (self.eeig - self.rgd.integrate((self.vr_sg * self.n_sg).sum(0), -1)) def run(self, mix=0.4, maxiter=117, dnmax=1e-9): if self.channels is None: self.initialize() if self.dirac: equation = 'Dirac' elif self.scalar_relativistic: equation = 'scalar-relativistic Schrödinger' else: equation = 'non-relativistic Schrödinger' self.log('\nSolving %s equation using %s:' % (equation, self.method)) dn = self.Z for iter in range(maxiter): self.log('.', end='') self.fd.flush() if iter > 0: self.vr_sg *= mix self.vr_sg += (1 - mix) * vr_old_sg dn = self.rgd.integrate(abs(self.n_sg - n_old_sg).sum(0)) if dn <= dnmax: self.log('\nConverged in', iter, 'steps') break vr_old_sg = self.vr_sg n_old_sg = self.n_sg self.step() self.summary() if dn > dnmax: raise RuntimeError('Did not converge!') def refine(self): self.method = 'finite difference' self.run(dnmax=1e-6, mix=0.14, maxiter=200) def summary(self): self.write_states() self.write_energies() def write_states(self): self.log('\n state occupation eigenvalue <r>') if self.dirac: self.log(' nl(j) [Hartree] [eV] [Bohr]') else: self.log(' nl [Hartree] [eV] [Bohr]') self.log('-----------------------------------------------------') states = [] for ch in self.channels: for n, f in enumerate(ch.f_n): states.append((ch.e_n[n], ch, n)) states.sort() for e, ch, n in states: name = str(n + ch.l + 1) + ch.name if self.nspins == 2: name += '(%s)' % '+-'[ch.s] n_g = ch.calculate_density(n) rave = self.rgd.integrate(n_g, 1) self.log(' %-7s %6.3f %13.6f %13.5f %6.3f' % (name, ch.f_n[n], e, e * units.Hartree, rave)) def write_energies(self): self.log('\nEnergies: [Hartree] [eV]') self.log('--------------------------------------------') for text, e in [('kinetic ', self.ekin), ('coulomb (e-e)', self.eH), ('coulomb (e-n)', self.eZ), ('xc ', self.exc), ('total ', self.ekin + self.eH + self.eZ + self.exc)]: self.log(' %s %+13.6f %+13.5f' % (text, e, units.Hartree * e)) self.calculate_exx() self.log('\nExact exchange energy: %.6f Hartree, %.5f eV' % (self.exx, self.exx * units.Hartree)) def get_channel(self, l=None, s=0, k=None): if self.dirac: for channel in self.channels: if channel.k == k: return channel else: for channel in self.channels: if channel.l == l and channel.s == s: return channel raise ValueError def get_orbital(self, n, l=None, s=0, k=None): channel = self.get_channel(l, s, k) return channel.basis.expand(channel.C_nb[n]) def plot_wave_functions(self, rc=4.0): import matplotlib.pyplot as plt for ch in self.channels: for n in range(len(ch.f_n)): fr_g = ch.basis.expand(ch.C_nb[n]) * self.rgd.r_g name = str(n + ch.l + 1) + ch.name lw = 2 if self.nspins == 2: name += '(%s)' % '+-'[ch.s] if ch.s == 1: lw = 1 if self.dirac and ch.k > 0: lw = 1 ls = ['-', '--', '-.', ':'][ch.l] n_g = ch.calculate_density(n) rave = self.rgd.integrate(n_g, 1) gave = self.rgd.round(rave) fr_g *= cmp(fr_g[gave], 0) plt.plot(self.rgd.r_g, fr_g, ls=ls, lw=lw, color=colors[n + ch.l], label=name) plt.legend(loc='best') plt.xlabel('r [Bohr]') plt.ylabel('$r\\phi(r)$') plt.axis(xmax=rc) plt.show() def logarithmic_derivative(self, l, energies, rcut): ch = Channel(l) gcut = self.rgd.round(rcut) u_g = self.rgd.empty() logderivs = [] for e in energies: dudr = ch.integrate_outwards(u_g, self.rgd, self.vr_sg[0], gcut, e, self.scalar_relativistic) logderivs.append(dudr / u_g[gcut]) return logderivs def calculate_exx(self, s=None): if s is None: self.exx = sum(self.calculate_exx(s) for s in range(self.nspins)) / self.nspins return self.exx states = [] lmax = 0 for ch in self.channels: l = ch.l for n, phi_g in enumerate(ch.phi_ng): f = ch.f_n[n] if f > 0 and ch.s == s: states.append((l, f * self.nspins / 2.0 / (2 * l + 1), phi_g)) if l > lmax: lmax = l G_LLL = make_gaunt(lmax) exx = 0.0 j1 = 0 for l1, f1, phi1_g in states: f = 1.0 for l2, f2, phi2_g in states[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 exx -= e * G * f1 * f2 f = 2.0 j1 += 1 return exx
import numpy as np import numpy.random as ra from gpaw.test import equal from gpaw.setup import create_setup from gpaw.grid_descriptor import GridDescriptor from gpaw.localized_functions import create_localized_functions from gpaw.spline import Spline from gpaw.xc import XC from gpaw.utilities import pack from gpaw.mpi import serial_comm ra.seed(8) for name in ['LDA', 'PBE']: xc = XC(name) s = create_setup('N', xc) ni = s.ni niAO = s.niAO wt0_j = s.phit_j rcut = s.xc_correction.rgd.r_g[-1] wt_j = [] for wt0 in wt0_j: data = [wt0(r) for r in np.arange(121) * rcut / 100] data[-1] = 0.0 l = wt0.get_angular_momentum_number() wt_j.append(Spline(l, 1.2 * rcut, data)) a = rcut * 1.2 * 2 + 1.0 ## n = 120 n = 70
def __init__(self, calculator=None, kss=None, xc=None, derivativeLevel=None, numscale=0.001, filehandle=None, txt=None, finegrid=2, eh_comm=None, ): if not txt and calculator: txt = calculator.txt self.txt, firsttime = initialize_text_stream(txt, mpi.rank) if eh_comm == None: eh_comm = mpi.serial_comm self.eh_comm = eh_comm if filehandle is not None: self.kss = kss self.read(fh=filehandle) return None self.fullkss = kss self.finegrid = finegrid if calculator is None: return self.paw = calculator wfs = self.paw.wfs # handle different grid possibilities self.restrict = None self.poisson = PoissonSolver(nn=self.paw.hamiltonian.poisson.nn) if finegrid: self.poisson.set_grid_descriptor(self.paw.density.finegd) self.poisson.initialize() self.gd = self.paw.density.finegd if finegrid == 1: self.gd = wfs.gd else: self.poisson.set_grid_descriptor(wfs.gd) self.poisson.initialize() self.gd = wfs.gd self.restrict = Transformer(self.paw.density.finegd, wfs.gd, self.paw.input_parameters.stencils[1] ).apply if xc == 'RPA': xc = None # enable RPA as keyword if xc is not None: self.xc = XC(xc) self.xc.initialize(self.paw.density, self.paw.hamiltonian, wfs, self.paw.occupations) # check derivativeLevel if derivativeLevel is None: derivativeLevel= \ self.xc.get_functional().get_max_derivative_level() self.derivativeLevel = derivativeLevel # change the setup xc functional if needed # the ground state calculation may have used another xc if kss.npspins > kss.nvspins: spin_increased = True else: spin_increased = False else: self.xc = None self.numscale = numscale self.singletsinglet = False if kss.nvspins<2 and kss.npspins<2: # this will be a singlet to singlet calculation only self.singletsinglet=True nij = len(kss) self.Om = np.zeros((nij,nij)) self.get_full()
from __future__ import print_function import numpy as np import numpy.random as ra from gpaw.setup import create_setup from gpaw.xc.noncollinear import NonCollinearFunctional from gpaw.xc import XC from gpaw.test import equal from gpaw.utilities import pack x = 0.0000001 ra.seed(8) for xc in ['LDA']:#, 'PBE']: print(xc) xc = XC(xc) s = create_setup('N', xc) ni = s.ni D_sp = np.array([pack(np.outer(P_i, P_i)) for P_i in 0.2 * ra.random((2, ni)) - 0.1]) D_sp[1] += D_sp[0] nii = ni * (ni + 1) // 2 E = xc.calculate_paw_correction(s, D_sp) xc = NonCollinearFunctional(xc) Dnc_sp = np.zeros((4, nii)) Dnc_sp[0] = D_sp.sum(0) Dnc_sp[3] = D_sp[0] - D_sp[1] Enc = xc.calculate_paw_correction(s, Dnc_sp)
def initialize(self, atoms=None): """Inexpensive initialization.""" if atoms is None: atoms = self.atoms else: # Save the state of the atoms: self.atoms = atoms.copy() par = self.input_parameters world = par.communicator if world is None: world = mpi.world elif hasattr(world, 'new_communicator'): # Check for whether object has correct type already # # Using isinstance() is complicated because of all the # combinations, serial/parallel/debug... pass else: # world should be a list of ranks: world = mpi.world.new_communicator(np.asarray(world)) self.wfs.world = world self.set_text(par.txt, par.verbose) natoms = len(atoms) cell_cv = atoms.get_cell() / Bohr pbc_c = atoms.get_pbc() Z_a = atoms.get_atomic_numbers() magmom_av = atoms.get_initial_magnetic_moments() # Generate new xc functional only when it is reset by set if self.hamiltonian is None or self.hamiltonian.xc is None: if isinstance(par.xc, str): xc = XC(par.xc) else: xc = par.xc else: xc = self.hamiltonian.xc mode = par.mode if xc.orbital_dependent and mode == 'lcao': raise NotImplementedError('LCAO mode does not support ' 'orbital-dependent XC functionals.') if mode == 'pw': mode = PW() if mode == 'fd' and par.usefractrans: raise NotImplementedError('FD mode does not support ' 'fractional translations.') if mode == 'lcao' and par.usefractrans: raise Warning('Fractional translations have not been tested ' 'with LCAO mode. Use with care!') if par.realspace is None: realspace = not isinstance(mode, PW) else: realspace = par.realspace if isinstance(mode, PW): assert not realspace if par.gpts is not None: N_c = np.array(par.gpts) else: h = par.h if h is not None: h /= Bohr N_c = get_number_of_grid_points(cell_cv, h, mode, realspace) if par.filter is None and not isinstance(mode, PW): gamma = 1.6 hmax = ((np.linalg.inv(cell_cv)**2).sum(0)**-0.5 / N_c).max() def filter(rgd, rcut, f_r, l=0): gcut = np.pi / hmax - 2 / rcut / gamma f_r[:] = rgd.filter(f_r, rcut * gamma, gcut, l) else: filter = par.filter setups = Setups(Z_a, par.setups, par.basis, par.lmax, xc, filter, world) if magmom_av.ndim == 1: collinear = True magmom_av, magmom_a = np.zeros((natoms, 3)), magmom_av magmom_av[:, 2] = magmom_a else: collinear = False magnetic = magmom_av.any() spinpol = par.spinpol if par.hund: if natoms != 1: raise ValueError('hund=True arg only valid for single atoms!') spinpol = True magmom_av[0] = (0, 0, setups[0].get_hunds_rule_moment(par.charge)) if spinpol is None: spinpol = magnetic elif magnetic and not spinpol: raise ValueError('Non-zero initial magnetic moment for a ' + 'spin-paired calculation!') if collinear: nspins = 1 + int(spinpol) ncomp = 1 else: nspins = 1 ncomp = 2 # K-point descriptor bzkpts_kc = kpts2ndarray(par.kpts, self.atoms) kd = KPointDescriptor(bzkpts_kc, nspins, collinear, par.usefractrans) width = par.width if width is None: if pbc_c.any(): width = 0.1 # eV else: width = 0.0 else: assert par.occupations is None if hasattr(self, 'time') or par.dtype == complex: dtype = complex else: if kd.gamma: dtype = float else: dtype = complex ## rbw: If usefractrans=True, kd.set_symmetry might overwrite N_c. ## This is necessary, because N_c must be dividable by 1/(fractional translation), ## f.e. fractional translations of a grid point must land on a grid point. N_c = kd.set_symmetry(atoms, setups, magmom_av, par.usesymm, N_c, world) nao = setups.nao nvalence = setups.nvalence - par.charge M_v = magmom_av.sum(0) M = np.dot(M_v, M_v)**0.5 nbands = par.nbands if nbands is None: nbands = 0 for setup in setups: nbands_from_atom = setup.get_default_nbands() # Any obscure setup errors? if nbands_from_atom < -(-setup.Nv // 2): raise ValueError('Bad setup: This setup requests %d' ' bands but has %d electrons.' % (nbands_from_atom, setup.Nv)) nbands += nbands_from_atom nbands = min(nao, nbands) elif nbands > nao and mode == 'lcao': raise ValueError('Too many bands for LCAO calculation: ' '%d bands and only %d atomic orbitals!' % (nbands, nao)) if nvalence < 0: raise ValueError( 'Charge %f is not possible - not enough valence electrons' % par.charge) if nbands <= 0: nbands = int(nvalence + M + 0.5) // 2 + (-nbands) if nvalence > 2 * nbands: raise ValueError('Too few bands! Electrons: %f, bands: %d' % (nvalence, nbands)) nbands *= ncomp if par.width is not None: self.text('**NOTE**: please start using ' 'occupations=FermiDirac(width).') if par.fixmom: self.text('**NOTE**: please start using ' 'occupations=FermiDirac(width, fixmagmom=True).') if self.occupations is None: if par.occupations is None: # Create object for occupation numbers: self.occupations = occupations.FermiDirac(width, par.fixmom) else: self.occupations = par.occupations self.occupations.magmom = M_v[2] cc = par.convergence if mode == 'lcao': niter_fixdensity = 0 else: niter_fixdensity = None if self.scf is None: self.scf = SCFLoop( cc['eigenstates'] / Hartree**2 * nvalence, cc['energy'] / Hartree * max(nvalence, 1), cc['density'] * nvalence, par.maxiter, par.fixdensity, niter_fixdensity) parsize_kpt = par.parallel['kpt'] parsize_domain = par.parallel['domain'] parsize_bands = par.parallel['band'] if not realspace: pbc_c = np.ones(3, bool) if not self.wfs: if parsize_domain == 'domain only': # XXX this was silly! parsize_domain = world.size parallelization = mpi.Parallelization(world, nspins * kd.nibzkpts) ndomains = None if parsize_domain is not None: ndomains = np.prod(parsize_domain) if isinstance(mode, PW): if ndomains > 1: raise ValueError('Planewave mode does not support ' 'domain decomposition.') ndomains = 1 parallelization.set(kpt=parsize_kpt, domain=ndomains, band=parsize_bands) domain_comm, kpt_comm, band_comm = \ parallelization.build_communicators() #domain_comm, kpt_comm, band_comm = mpi.distribute_cpus( # parsize_domain, parsize_bands, # nspins, kd.nibzkpts, world, par.idiotproof, mode) kd.set_communicator(kpt_comm) parstride_bands = par.parallel['stridebands'] # Unfortunately we need to remember that we adjusted the # number of bands so we can print a warning if it differs # from the number specified by the user. (The number can # be inferred from the input parameters, but it's tricky # because we allow negative numbers) self.nbands_parallelization_adjustment = -nbands % band_comm.size nbands += self.nbands_parallelization_adjustment # I would like to give the following error message, but apparently # there are cases, e.g. gpaw/test/gw_ppa.py, which involve # nbands > nao and are supposed to work that way. #if nbands > nao: # raise ValueError('Number of bands %d adjusted for band ' # 'parallelization %d exceeds number of atomic ' # 'orbitals %d. This problem can be fixed ' # 'by reducing the number of bands a bit.' # % (nbands, band_comm.size, nao)) bd = BandDescriptor(nbands, band_comm, parstride_bands) if (self.density is not None and self.density.gd.comm.size != domain_comm.size): # Domain decomposition has changed, so we need to # reinitialize density and hamiltonian: if par.fixdensity: raise RuntimeError('Density reinitialization conflict ' + 'with "fixdensity" - specify domain decomposition.') self.density = None self.hamiltonian = None # Construct grid descriptor for coarse grids for wave functions: gd = self.grid_descriptor_class(N_c, cell_cv, pbc_c, domain_comm, parsize_domain) # do k-point analysis here? XXX args = (gd, nvalence, setups, bd, dtype, world, kd, self.timer) if par.parallel['sl_auto']: # Choose scalapack parallelization automatically for key, val in par.parallel.items(): if (key.startswith('sl_') and key != 'sl_auto' and val is not None): raise ValueError("Cannot use 'sl_auto' together " "with '%s'" % key) max_scalapack_cpus = bd.comm.size * gd.comm.size nprow = max_scalapack_cpus npcol = 1 # Get a sort of reasonable number of columns/rows while npcol < nprow and nprow % 2 == 0: npcol *= 2 nprow //= 2 assert npcol * nprow == max_scalapack_cpus # ScaLAPACK creates trouble if there aren't at least a few # whole blocks; choose block size so there will always be # several blocks. This will crash for small test systems, # but so will ScaLAPACK in any case blocksize = min(-(-nbands // 4), 64) sl_default = (nprow, npcol, blocksize) else: sl_default = par.parallel['sl_default'] if mode == 'lcao': # Layouts used for general diagonalizer sl_lcao = par.parallel['sl_lcao'] if sl_lcao is None: sl_lcao = sl_default lcaoksl = get_KohnSham_layouts(sl_lcao, 'lcao', gd, bd, dtype, nao=nao, timer=self.timer) if collinear: self.wfs = LCAOWaveFunctions(lcaoksl, *args) else: from gpaw.xc.noncollinear import \ NonCollinearLCAOWaveFunctions self.wfs = NonCollinearLCAOWaveFunctions(lcaoksl, *args) elif mode == 'fd' or isinstance(mode, PW): # buffer_size keyword only relevant for fdpw buffer_size = par.parallel['buffer_size'] # Layouts used for diagonalizer sl_diagonalize = par.parallel['sl_diagonalize'] if sl_diagonalize is None: sl_diagonalize = sl_default diagksl = get_KohnSham_layouts(sl_diagonalize, 'fd', gd, bd, dtype, buffer_size=buffer_size, timer=self.timer) # Layouts used for orthonormalizer sl_inverse_cholesky = par.parallel['sl_inverse_cholesky'] if sl_inverse_cholesky is None: sl_inverse_cholesky = sl_default if sl_inverse_cholesky != sl_diagonalize: message = 'sl_inverse_cholesky != sl_diagonalize ' \ 'is not implemented.' raise NotImplementedError(message) orthoksl = get_KohnSham_layouts(sl_inverse_cholesky, 'fd', gd, bd, dtype, buffer_size=buffer_size, timer=self.timer) # Use (at most) all available LCAO for initialization lcaonbands = min(nbands, nao) try: lcaobd = BandDescriptor(lcaonbands, band_comm, parstride_bands) except RuntimeError: initksl = None else: # Layouts used for general diagonalizer # (LCAO initialization) sl_lcao = par.parallel['sl_lcao'] if sl_lcao is None: sl_lcao = sl_default initksl = get_KohnSham_layouts(sl_lcao, 'lcao', gd, lcaobd, dtype, nao=nao, timer=self.timer) if hasattr(self, 'time'): assert mode == 'fd' from gpaw.tddft import TimeDependentWaveFunctions self.wfs = TimeDependentWaveFunctions(par.stencils[0], diagksl, orthoksl, initksl, gd, nvalence, setups, bd, world, kd, self.timer) elif mode == 'fd': self.wfs = FDWaveFunctions(par.stencils[0], diagksl, orthoksl, initksl, *args) else: # Planewave basis: self.wfs = mode(diagksl, orthoksl, initksl, *args) else: self.wfs = mode(self, *args) else: self.wfs.set_setups(setups) if not self.wfs.eigensolver: # Number of bands to converge: nbands_converge = cc['bands'] if nbands_converge == 'all': nbands_converge = nbands elif nbands_converge != 'occupied': assert isinstance(nbands_converge, int) if nbands_converge < 0: nbands_converge += nbands eigensolver = get_eigensolver(par.eigensolver, mode, par.convergence) eigensolver.nbands_converge = nbands_converge # XXX Eigensolver class doesn't define an nbands_converge property if isinstance(xc, SIC): eigensolver.blocksize = 1 self.wfs.set_eigensolver(eigensolver) if self.density is None: gd = self.wfs.gd if par.stencils[1] != 9: # Construct grid descriptor for fine grids for densities # and potentials: finegd = gd.refine() else: # Special case (use only coarse grid): finegd = gd if realspace: self.density = RealSpaceDensity( gd, finegd, nspins, par.charge + setups.core_charge, collinear, par.stencils[1]) else: self.density = ReciprocalSpaceDensity( gd, finegd, nspins, par.charge + setups.core_charge, collinear) self.density.initialize(setups, self.timer, magmom_av, par.hund) self.density.set_mixer(par.mixer) if self.hamiltonian is None: gd, finegd = self.density.gd, self.density.finegd if realspace: self.hamiltonian = RealSpaceHamiltonian( gd, finegd, nspins, setups, self.timer, xc, par.external, collinear, par.poissonsolver, par.stencils[1], world) else: self.hamiltonian = ReciprocalSpaceHamiltonian( gd, finegd, self.density.pd2, self.density.pd3, nspins, setups, self.timer, xc, par.external, collinear, world) xc.initialize(self.density, self.hamiltonian, self.wfs, self.occupations) self.text() self.print_memory_estimate(self.txt, maxdepth=memory_estimate_depth) self.txt.flush() self.timer.print_info(self) if dry_run: self.dry_run() self.initialized = True
gd.beg_c[2] <= 3 < gd.end_c[2]) if here: x = v[-1, 1, 2, 3] * gd.dv n[-1, 1, 2, 3] += 0.000001 Ep = xc.calculate(gd, n, v) if here: n[-1, 1, 2, 3] -= 0.000002 Em = xc.calculate(gd, n, v) x2 = (Ep - Em) / 0.000002 if here: print(xc.name, E, x, x2, x - x2) equal(x, x2, 1e-11) n[-1, 1, 2, 3] += 0.000001 if 0:#xc.type == 'LDA': xc = XC(NonCollinearLDAKernel()) else: xc = NonCollinearFunctional(xc) n2 = gd.zeros(4) n2[0] = n.sum(0) n2[3] = n[0] - n[1] E2 = xc.calculate(gd, n2) print(E, E2-E) assert abs(E2 - E) < 1e-11 n2[1] = 0.1 * n2[3] n2[2] = 0.2 * n2[3] n2[3] *= (1 - 0.1**2 - 0.2**2)**0.5 v = n2 * 0 E2 = xc.calculate(gd, n2, v) print(E, E2-E)
def initialize(self): self.xc = XC(self.functional) self.vt_sg = self.nlfunc.finegd.empty(self.nlfunc.nspins) self.e_g = self.nlfunc.finegd.empty()
class C_GLLBScr(Contribution): def __init__(self, nlfunc, weight, functional='GGA_X_B88', metallic=False): Contribution.__init__(self, nlfunc, weight) self.functional = functional self.old_coeffs = None self.iter = 0 self.metallic = metallic def get_name(self): return 'SCREENING' def get_desc(self): return '(' + self.functional + ')' # Initialize GLLBScr functional def initialize_1d(self): self.ae = self.nlfunc.ae self.xc = XC(self.functional) self.v_g = np.zeros(self.ae.N) self.e_g = np.zeros(self.ae.N) # Calcualte the GLLB potential and energy 1d def add_xc_potential_and_energy_1d(self, v_g): self.v_g[:] = 0.0 self.e_g[:] = 0.0 self.xc.calculate_spherical(self.ae.rgd, self.ae.n.reshape((1, -1)), self.v_g.reshape((1, -1)), self.e_g) v_g += 2 * self.weight * self.e_g / (self.ae.n + 1e-10) Exc = self.weight * np.sum(self.e_g * self.ae.rgd.dv_g) return Exc def initialize(self): self.occupations = self.nlfunc.occupations self.xc = XC(self.functional) # Always 1 spin, no matter what calculation nspins is self.vt_sg = self.nlfunc.finegd.empty(1) self.e_g = self.nlfunc.finegd.empty()#.ravel() def get_coefficient_calculator(self): return self def f(self, f): return sqrt(f) def get_coefficients(self, e_j, f_j): homo_e = max( [ np.where(f>1e-3, e, -1000) for f,e in zip(f_j, e_j)] ) return [ f * K_G * self.f( max(0, homo_e - e)) for e,f in zip(e_j, f_j) ] def get_coefficients_1d(self, smooth=False, lumo_perturbation = False): homo_e = max( [ np.where(f>1e-3, e, -1000) for f,e in zip(self.ae.f_j, self.ae.e_j)]) if not smooth: if lumo_perturbation: lumo_e = min( [ np.where(f<1e-3, e, 1000) for f,e in zip(self.ae.f_j, self.ae.e_j)]) return np.array([ f * K_G * (self.f( max(0, lumo_e - e)) - self.f(max(0, homo_e -e))) for e,f in zip(self.ae.e_j, self.ae.f_j) ]) else: return np.array([ f * K_G * (self.f( max(0, homo_e - e))) for e,f in zip(self.ae.e_j, self.ae.f_j) ]) else: return [ [ f * K_G * self.f( max(0, homo_e - e)) for e,f in zip(e_n, f_n) ] for e_n, f_n in zip(self.ae.e_ln, self.ae.f_ln) ] def get_coefficients_by_kpt(self, kpt_u, lumo_perturbation=False, homolumo=None, nspins=1): if not hasattr(kpt_u[0],'orbitals_ready'): kpt_u[0].orbitals_ready = True return None #if kpt_u[0].psit_nG is None or isinstance(kpt_u[0].psit_nG, # TarFileReference): # if kpt_u[0].C_nM==None: # return None if homolumo == None: if self.metallic: # For metallic systems, the calculated fermi level represents # the most accurate estimate for reference-energy eref_lumo_s = eref_s = nspins * [ self.occupations.get_fermi_level() ] else: # Find h**o and lumo levels for each spin eref_s = [] eref_lumo_s = [] for s in range(nspins): h**o, lumo = self.occupations.get_homo_lumo_by_spin(self.nlfunc.wfs, s) eref_s.append(h**o) eref_lumo_s.append(lumo) else: eref_s, eref_lumo_s = homolumo if not isinstance(eref_s, (list, tuple)): eref_s = [ eref_s ] eref_lumo_s = [ eref_lumo_s ] # The parameter ee might sometimes be set to small thereshold value to # achieve convergence on small systems with degenerate H**O. if len(kpt_u) > nspins: ee = 0.0 else: ee = 0.05 / 27.21 if lumo_perturbation: return [np.array([ f * K_G * (self.f( np.where(eref_lumo_s[kpt.s] - e>ee, eref_lumo_s[kpt.s]-e,0)) -self.f( np.where(eref_s[kpt.s] - e>ee, eref_s[kpt.s]-e,0))) for e, f in zip(kpt.eps_n, kpt.f_n) ]) for kpt in kpt_u ] else: coeff = [ np.array([ f * K_G * self.f( np.where(eref_s[kpt.s] - e>ee, eref_s[kpt.s]-e,0)) for e, f in zip(kpt.eps_n, kpt.f_n) ]) for kpt in kpt_u ] return coeff def calculate_spinpaired(self, e_g, n_g, v_g): self.e_g[:] = 0.0 self.vt_sg[:] = 0.0 self.xc.calculate(self.nlfunc.finegd, n_g[None, ...], self.vt_sg, self.e_g) self.e_g[:] = np.where(n_g<1e-10, 0, self.e_g) v_g += self.weight * 2 * self.e_g / (n_g + 1e-10) e_g += self.weight * self.e_g def calculate_spinpolarized(self, e_g, n_sg, v_sg): # Calculate spinpolarized exchange screening as two spin-paired calculations n=2*n_s for n, v in [ (n_sg[0], v_sg[0]), (n_sg[1], v_sg[1]) ]: self.e_g[:] = 0.0 self.vt_sg[:] = 0.0 self.xc.calculate(self.nlfunc.finegd, 2*n[None, ...], self.vt_sg, self.e_g) self.e_g[:] = np.where(n<1e-10, 0, self.e_g) v += self.weight * 2 * self.e_g / (2 * n + 1e-9) e_g += self.weight * self.e_g / 2 def calculate_energy_and_derivatives(self, setup, D_sp, H_sp, a, addcoredensity=True): # Get the XC-correction instance c = setup.xc_correction nspins = self.nlfunc.nspins E = 0 for D_p, dEdD_p in zip(D_sp, H_sp): D_Lq = np.dot(c.B_pqL.T, nspins*D_p) n_Lg = np.dot(D_Lq, c.n_qg) if addcoredensity: n_Lg[0] += c.nc_g * sqrt(4 * pi) nt_Lg = np.dot(D_Lq, c.nt_qg) if addcoredensity: nt_Lg[0] += c.nct_g * sqrt(4 * pi) dndr_Lg = np.zeros((c.Lmax, c.ng)) dntdr_Lg = np.zeros((c.Lmax, c.ng)) for L in range(c.Lmax): c.rgd.derivative(n_Lg[L], dndr_Lg[L]) c.rgd.derivative(nt_Lg[L], dntdr_Lg[L]) vt_g = np.zeros(c.ng) v_g = np.zeros(c.ng) e_g = np.zeros(c.ng) deda2_g = np.zeros(c.ng) for y, (w, Y_L) in enumerate(zip(weight_n, c.Y_nL)): # Cut gradient releated coefficient to match the setup's Lmax A_Li = rnablaY_nLv[y, :c.Lmax] # Expand pseudo density nt_g = np.dot(Y_L, nt_Lg) # Expand pseudo density gradient a1x_g = np.dot(A_Li[:, 0], nt_Lg) a1y_g = np.dot(A_Li[:, 1], nt_Lg) a1z_g = np.dot(A_Li[:, 2], nt_Lg) a2_g = a1x_g**2 + a1y_g**2 + a1z_g**2 a2_g[1:] /= c.rgd.r_g[1:]**2 a2_g[0] = a2_g[1] a1_g = np.dot(Y_L, dntdr_Lg) a2_g += a1_g**2 vt_g[:] = 0.0 e_g[:] = 0.0 # Calculate pseudo GGA energy density (potential is discarded) self.xc.kernel.calculate(e_g, nt_g.reshape((1, -1)), vt_g.reshape((1, -1)), a2_g.reshape((1, -1)), deda2_g.reshape((1, -1))) # Calculate pseudo GLLB-potential from GGA-energy density vt_g[:] = 2 * e_g / (nt_g + 1e-10) dEdD_p -= self.weight * w * np.dot(np.dot(c.B_pqL, Y_L), np.dot(c.nt_qg, vt_g * c.rgd.dv_g)) E -= w * np.dot(e_g, c.rgd.dv_g) / nspins # Expand density n_g = np.dot(Y_L, n_Lg) # Expand density gradient a1x_g = np.dot(A_Li[:, 0], n_Lg) a1y_g = np.dot(A_Li[:, 1], n_Lg) a1z_g = np.dot(A_Li[:, 2], n_Lg) a2_g = a1x_g**2 + a1y_g**2 + a1z_g**2 a2_g[1:] /= c.rgd.r_g[1:]**2 a2_g[0] = a2_g[1] a1_g = np.dot(Y_L, dndr_Lg) a2_g += a1_g**2 v_g[:] = 0.0 e_g[:] = 0.0 # Calculate GGA energy density (potential is discarded) self.xc.kernel.calculate(e_g, n_g.reshape((1, -1)), v_g.reshape((1, -1)), a2_g.reshape((1, -1)), deda2_g.reshape((1, -1))) # Calculate GLLB-potential from GGA-energy density v_g[:] = 2 * e_g / (n_g + 1e-10) dEdD_p += self.weight * w * np.dot(np.dot(c.B_pqL, Y_L), np.dot(c.n_qg, v_g * c.rgd.dv_g)) E += w * np.dot(e_g, c.rgd.dv_g) / nspins return E * self.weight def add_smooth_xc_potential_and_energy_1d(self, vt_g): self.v_g[:] = 0.0 self.e_g[:] = 0.0 self.xc.calculate_spherical(self.ae.rgd, self.ae.nt.reshape((1, -1)), self.v_g.reshape((1, -1)), self.e_g) vt_g += 2 * self.weight * self.e_g / (self.ae.nt + 1e-10) return self.weight * np.sum(self.e_g * self.ae.rgd.dv_g) def initialize_from_atomic_orbitals(self, basis_functions): # GLLBScr needs only density which is already initialized pass def add_extra_setup_data(self, dict): # GLLBScr has not any special data pass def read(self, reader): # GLLBScr has no special data to be read pass def write(self, writer, natoms): # GLLBScr has no special data to be written pass
class C_XC(Contribution): def __init__(self, nlfunc, weight, functional = 'LDA'): Contribution.__init__(self, nlfunc, weight) self.functional = functional def get_name(self): return 'XC' def get_desc(self): return "("+self.functional+")" def initialize(self): self.xc = XC(self.functional) self.vt_sg = self.nlfunc.finegd.empty(self.nlfunc.nspins) self.e_g = self.nlfunc.finegd.empty() def initialize_1d(self): self.ae = self.nlfunc.ae self.xc = XC(self.functional) self.v_g = np.zeros(self.ae.N) def calculate_spinpaired(self, e_g, n_g, v_g): self.e_g[:] = 0.0 self.vt_sg[:] = 0.0 self.xc.calculate(self.nlfunc.finegd, n_g[None, ...], self.vt_sg, self.e_g) v_g += self.weight * self.vt_sg[0] e_g += self.weight * self.e_g def calculate_spinpolarized(self, e_g, n_sg, v_sg): self.e_g[:] = 0.0 self.vt_sg[:] = 0.0 self.xc.calculate(self.nlfunc.finegd, n_sg, self.vt_sg, self.e_g) #self.xc.get_energy_and_potential(na_g, self.vt_sg[0], nb_g, self.vt_sg[1], e_g=self.e_g) v_sg[0] += self.weight * self.vt_sg[0] v_sg[1] += self.weight * self.vt_sg[1] e_g += self.weight * self.e_g def calculate_energy_and_derivatives(self, setup, D_sp, H_sp, a, addcoredensity=True): E = self.xc.calculate_paw_correction(setup, D_sp, H_sp, True, a) E += setup.xc_correction.Exc0 print("E", E) return E def add_xc_potential_and_energy_1d(self, v_g): self.v_g[:] = 0.0 Exc = self.xc.calculate_spherical(self.ae.rgd, self.ae.n.reshape((1, -1)), self.v_g.reshape((1, -1))) v_g += self.weight * self.v_g return self.weight * Exc def add_smooth_xc_potential_and_energy_1d(self, vt_g): self.v_g[:] = 0.0 Exc = self.xc.calculate_spherical(self.ae.rgd, self.ae.nt.reshape((1, -1)), self.v_g.reshape((1, -1))) vt_g += self.weight * self.v_g return self.weight * Exc def initialize_from_atomic_orbitals(self, basis_functions): # LDA needs only density, which is already initialized pass def add_extra_setup_data(self, dict): # LDA has not any special data pass def write(self, writer, natoms): # LDA has not any special data to be written pass def read(self, reader): # LDA has not any special data to be read pass
class AllElectron: """Object for doing an atomic DFT calculation.""" def __init__(self, symbol, xcname='LDA', scalarrel=False, corehole=None, configuration=None, nofiles=True, txt='-', gpernode=150, orbital_free=False, tf_coeff=1.): """Do an atomic DFT calculation. Example:: a = AllElectron('Fe') a.run() """ if txt is None: txt = devnull elif txt == '-': txt = sys.stdout elif isinstance(txt, str): txt = open(txt, 'w') self.txt = txt self.symbol = symbol self.xcname = xcname self.scalarrel = scalarrel self.nofiles = nofiles # Get reference state: self.Z, nlfe_j = configurations[symbol] # Collect principal quantum numbers, angular momentum quantum # numbers, occupation numbers and eigenvalues (j is a combined # index for n and l): self.n_j = [n for n, l, f, e in nlfe_j] self.l_j = [l for n, l, f, e in nlfe_j] self.f_j = [f for n, l, f, e in nlfe_j] self.e_j = [e for n, l, f, e in nlfe_j] if configuration is not None: j = 0 for conf in configuration.split(','): if conf[0].isdigit(): n = int(conf[0]) l = 'spdf'.find(conf[1]) if len(conf) == 2: f = 1.0 else: f = float(conf[2:]) #try: assert n == self.n_j[j] assert l == self.l_j[j] self.f_j[j] = f #except IndexError: # self.n_j.append(n) # self.l_j.append(l) # self.f_j.append(f) # self.e_j.append(self.e_j[-1]) j += 1 else: j += {'He': 1, 'Ne': 3, 'Ar': 5, 'Kr': 8, 'Xe': 11}[conf] maxnodes = max([n - l - 1 for n, l in zip(self.n_j, self.l_j)]) self.N = (maxnodes + 1) * gpernode self.beta = 0.4 self.orbital_free = orbital_free self.tf_coeff = tf_coeff if self.orbital_free: self.n_j = [1] self.l_j = [0] self.f_j = [self.Z] self.e_j = [self.e_j[-1]] t = self.text t() if scalarrel: t('Scalar-relativistic atomic ', end='') else: t('Atomic ', end='') t('%s calculation for %s (%s, Z=%d)' % (xcname, symbol, atomic_names[self.Z], self.Z)) if corehole is not None: self.ncorehole, self.lcorehole, self.fcorehole = corehole # Find j for core hole and adjust occupation: for j in range(len(self.f_j)): if (self.n_j[j] == self.ncorehole and self.l_j[j] == self.lcorehole): assert self.f_j[j] == 2 * (2 * self.lcorehole + 1) self.f_j[j] -= self.fcorehole self.jcorehole = j break coreholestate = '%d%s' % (self.ncorehole, 'spdf'[self.lcorehole]) t('Core hole in %s state (%s occupation: %.1f)' % ( coreholestate, coreholestate, self.f_j[self.jcorehole])) else: self.jcorehole = None self.fcorehole = 0 def text(self, *args, **kwargs): self.txt.write(kwargs.get('sep', ' ').join([str(arg) for arg in args]) + kwargs.get('end', '\n')) def initialize_wave_functions(self): r = self.r dr = self.dr # Initialize with Slater function: for l, e, u in zip(self.l_j, self.e_j, self.u_j): if self.symbol in ['Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au']: a = sqrt(-4.0 * e) else: a = sqrt(-2.0 * e) u[:] = r**(1 + l) * np.exp(-a * r) norm = np.dot(u**2, dr) u *= 1.0 / sqrt(norm) def run(self, use_restart_file=True): # beta g # r = ------, g = 0, 1, ..., N - 1 # N - g # # rN # g = -------- # beta + r t = self.text N = self.N beta = self.beta t(N, 'radial gridpoints.') self.rgd = AERadialGridDescriptor(beta / N, 1.0 / N, N) g = np.arange(N, dtype=float) self.r = self.rgd.r_g self.dr = self.rgd.dr_g self.d2gdr2 = self.rgd.d2gdr2() # Number of orbitals: nj = len(self.n_j) # Radial wave functions multiplied by radius: self.u_j = np.zeros((nj, self.N)) # Effective potential multiplied by radius: self.vr = np.zeros(N) # Electron density: self.n = np.zeros(N) # Always spinpaired nspins=1 self.xc = XC(self.xcname) # Initialize for non-local functionals if self.xc.type == 'GLLB': self.xc.pass_stuff_1d(self) self.xc.initialize_1d() n_j = self.n_j l_j = self.l_j f_j = self.f_j e_j = self.e_j Z = self.Z # nuclear charge r = self.r # radial coordinate dr = self.dr # dr/dg n = self.n # electron density vr = self.vr # effective potential multiplied by r vHr = np.zeros(self.N) self.vXC = np.zeros(self.N) restartfile = '%s/%s.restart' % (tempdir, self.symbol) if self.xc.type == 'GLLB' or not use_restart_file: # Do not start from initial guess when doing # non local XC! # This is because we need wavefunctions as well # on the first iteration. fd = None else: try: fd = open(restartfile, 'r') except IOError: fd = None else: try: n[:] = pickle.load(fd) except (ValueError, IndexError): fd = None else: norm = np.dot(n * r**2, dr) * 4 * pi if abs(norm - sum(f_j)) > 0.01: fd = None else: t('Using old density for initial guess.') n *= sum(f_j) / norm if fd is None: self.initialize_wave_functions() n[:] = self.calculate_density() bar = '|------------------------------------------------|' t(bar) niter = 0 nitermax = 117 qOK = log(1e-10) mix = 0.4 # orbital_free needs more iterations and coefficient if self.orbital_free: #qOK = log(1e-14) e_j[0] /= self.tf_coeff mix = 0.01 nitermax = 1000 vrold = None while True: # calculate hartree potential hartree(0, n * r * dr, r, vHr) # add potential from nuclear point charge (v = -Z / r) vHr -= Z # calculated exchange correlation potential and energy self.vXC[:] = 0.0 if self.xc.type == 'GLLB': # Update the potential to self.vXC an the energy to self.Exc Exc = self.xc.get_xc_potential_and_energy_1d(self.vXC) else: Exc = self.xc.calculate_spherical(self.rgd, n.reshape((1, -1)), self.vXC.reshape((1, -1))) # calculate new total Kohn-Sham effective potential and # admix with old version vr[:] = (vHr + self.vXC * r) / self.tf_coeff if niter > 0: vr[:] = mix * vr + (1 - mix) * vrold vrold = vr.copy() # solve Kohn-Sham equation and determine the density change self.solve() dn = self.calculate_density() - n n += dn # estimate error from the square of the density change integrated q = log(np.sum((r * dn)**2)) # print progress bar if niter == 0: q0 = q b0 = 0 else: b = int((q0 - q) / (q0 - qOK) * 50) if b > b0: self.txt.write(bar[b0:min(b, 50)]) self.txt.flush() b0 = b # check if converged and break loop if so if q < qOK: self.txt.write(bar[b0:]) self.txt.flush() break niter += 1 if niter > nitermax: raise RuntimeError('Did not converge!') tau = self.calculate_kinetic_energy_density() t() t('Converged in %d iteration%s.' % (niter, 's'[:niter != 1])) try: fd = open(restartfile, 'w') except IOError: pass else: pickle.dump(n, fd) try: os.chmod(restartfile, 0666) except OSError: pass Ekin = 0 if self.orbital_free: e_j[0] *= self.tf_coeff vr *= self.tf_coeff for f, e in zip(f_j, e_j): Ekin += f * e Epot = 2 * pi * np.dot(n * r * (vHr - Z), dr) Ekin += -4 * pi * np.dot(n * vr * r, dr) t() t('Energy contributions:') t('-------------------------') t('Kinetic: %+13.6f' % Ekin) t('XC: %+13.6f' % Exc) t('Potential: %+13.6f' % Epot) t('-------------------------') t('Total: %+13.6f' % (Ekin + Exc + Epot)) self.ETotal = Ekin + Exc + Epot t() t('state eigenvalue ekin rmax') t('-----------------------------------------------') for m, l, f, e, u in zip(n_j, l_j, f_j, e_j, self.u_j): # Find kinetic energy: k = e - np.sum((np.where(abs(u) < 1e-160, 0, u)**2 * # XXXNumeric! vr * dr)[1:] / r[1:]) # Find outermost maximum: g = self.N - 4 while u[g - 1] >= u[g]: g -= 1 x = r[g - 1:g + 2] y = u[g - 1:g + 2] A = np.transpose(np.array([x**i for i in range(3)])) c, b, a = np.linalg.solve(A, y) assert a < 0.0 rmax = -0.5 * b / a s = 'spdf'[l] t('%d%s^%-4.1f: %12.6f %12.6f %12.3f' % (m, s, f, e, k, rmax)) t('-----------------------------------------------') t('(units: Bohr and Hartree)') for m, l, u in zip(n_j, l_j, self.u_j): self.write(u, 'ae', n=m, l=l) self.write(n, 'n') self.write(vr, 'vr') self.write(vHr, 'vHr') self.write(self.vXC, 'vXC') self.write(tau, 'tau') self.Ekin = Ekin self.Epot = Epot self.Exc = Exc def write(self, array, name=None, n=None, l=None): if self.nofiles: return if name: name = self.symbol + '.' + name else: name = self.symbol if l is not None: assert n is not None if n > 0: # Bound state: name += '.%d%s' % (n, 'spdf'[l]) else: name += '.x%d%s' % (-n, 'spdf'[l]) f = open(name, 'w') for r, a in zip(self.r, array): print(r, a, file=f) def calculate_density(self): """Return the electron charge density divided by 4 pi""" n = np.dot(self.f_j, np.where(abs(self.u_j) < 1e-160, 0, self.u_j)**2) / (4 * pi) n[1:] /= self.r[1:]**2 n[0] = n[1] return n def calculate_kinetic_energy_density(self): """Return the kinetic energy density""" return self.radial_kinetic_energy_density(self.f_j, self.l_j, self.u_j) def radial_kinetic_energy_density(self, f_j, l_j, u_j): """Kinetic energy density from a restricted set of wf's """ shape = np.shape(u_j[0]) dudr = np.zeros(shape) tau = np.zeros(shape) for f, l, u in zip(f_j, l_j, u_j): self.rgd.derivative(u, dudr) # contribution from angular derivatives if l > 0: tau += f * l * (l + 1) * np.where(abs(u) < 1e-160, 0, u)**2 # contribution from radial derivatives dudr = u - self.r * dudr tau += f * np.where(abs(dudr) < 1e-160, 0, dudr)**2 tau[1:] /= self.r[1:]**4 tau[0] = tau[1] return 0.5 * tau / (4 * pi) def calculate_kinetic_energy_density2(self): """Return the kinetic energy density calculation over R(r)=u(r)/r slower convergence with # of radial grid points for Ekin of H than radial_kinetic_energy_density """ shape = self.u_j.shape[1] R = np.zeros(shape) dRdr = np.zeros(shape) tau = np.zeros(shape) for f, l, u in zip(self.f_j, self.l_j, self.u_j): R[1:] = u[1:] / self.r[1:] if l == 0: # estimate value at origin by Taylor series to first order d1 = self.r[1] d2 = self.r[2] R[0] = .5 * (R[1] + R[2] + (R[1] - R[2]) * (d1 + d2) / (d2 - d1)) else: R[0] = 0 self.rgd.derivative(R, dRdr) # contribution from radial derivatives tau += f * np.where(abs(dRdr) < 1e-160, 0, dRdr)**2 # contribution from angular derivatives if l > 0: R[1:] = R[1:] / self.r[1:] if l == 1: R[0] = R[1] else: R[0] = 0 tau += f * l * (l + 1) * np.where(abs(R) < 1e-160, 0, R)**2 return 0.5 * tau / (4 * pi) def solve(self): """Solve the Schrodinger equation :: 2 d u 1 dv du u l(l + 1) - --- - ---- -- (-- - -) + [-------- + 2M(v - e)] u = 0, 2 2 dr dr r 2 dr 2Mc r where the relativistic mass:: 1 M = 1 - --- (v - e) 2 2c and the fine-structure constant alpha = 1/c = 1/137.036 is set to zero for non-scalar-relativistic calculations. On the logaritmic radial grids defined by:: beta g r = ------, g = 0, 1, ..., N - 1 N - g rN g = --------, r = [0; oo[ beta + r the Schrodinger equation becomes:: 2 d u du --- c + -- c + u c = 0 2 2 dg 1 0 dg with the vectors c0, c1, and c2 defined by:: 2 dg 2 c = -r (--) 2 dr 2 2 d g 2 r dg dv c = - --- r - ---- -- -- 1 2 2 dr dr dr 2Mc 2 r dv c = l(l + 1) + 2M(v - e)r + ---- -- 0 2 dr 2Mc """ r = self.r dr = self.dr vr = self.vr c2 = -(r / dr)**2 c10 = -self.d2gdr2 * r**2 # first part of c1 vector if self.scalarrel: self.r2dvdr = np.zeros(self.N) self.rgd.derivative(vr, self.r2dvdr) self.r2dvdr *= r self.r2dvdr -= vr else: self.r2dvdr = None # solve for each quantum state separately for j, (n, l, e, u) in enumerate(zip(self.n_j, self.l_j, self.e_j, self.u_j)): nodes = n - l - 1 # analytically expected number of nodes delta = -0.2 * e nn, A = shoot(u, l, vr, e, self.r2dvdr, r, dr, c10, c2, self.scalarrel) # adjust eigenenergy until u has the correct number of nodes while nn != nodes: diff = cmp(nn, nodes) while diff == cmp(nn, nodes): e -= diff * delta nn, A = shoot(u, l, vr, e, self.r2dvdr, r, dr, c10, c2, self.scalarrel) delta /= 2 # adjust eigenenergy until u is smooth at the turning point de = 1.0 while abs(de) > 1e-9: norm = np.dot(np.where(abs(u) < 1e-160, 0, u)**2, dr) u *= 1.0 / sqrt(norm) de = 0.5 * A / norm x = abs(de / e) if x > 0.1: de *= 0.1 / x e -= de assert e < 0.0 nn, A = shoot(u, l, vr, e, self.r2dvdr, r, dr, c10, c2, self.scalarrel) self.e_j[j] = e u *= 1.0 / sqrt(np.dot(np.where(abs(u) < 1e-160, 0, u)**2, dr)) def solve_confined(self, j, rc, vconf=None): """Solve the Schroedinger equation in a confinement potential. Solves the Schroedinger equation like the solve method, but with a number of differences. Before invoking this method, run solve() to get initial guesses. Parameters: j: solves only for the state given by j rc: solution cutoff. Solution will be zero outside this. vconf: added to the potential (use this as confinement potential) Returns: a tuple containing the solution u and its energy e. Unlike the solve method, this method will not alter any attributes of this object. """ r = self.r dr = self.dr vr = self.vr.copy() if vconf is not None: vr += vconf * r c2 = -(r / dr)**2 c10 = -self.d2gdr2 * r**2 # first part of c1 vector if j is None: n, l, e, u = 3, 2, -0.15, self.u_j[-1].copy() else: n = self.n_j[j] l = self.l_j[j] e = self.e_j[j] u = self.u_j[j].copy() nn, A = shoot_confined(u, l, vr, e, self.r2dvdr, r, dr, c10, c2, self.scalarrel, rc=rc, beta=self.beta) assert nn == n - l - 1 # run() should have been called already # adjust eigenenergy until u is smooth at the turning point de = 1.0 while abs(de) > 1e-9: norm = np.dot(np.where(abs(u) < 1e-160, 0, u)**2, dr) u *= 1.0 / sqrt(norm) de = 0.5 * A / norm x = abs(de / e) if x > 0.1: de *= 0.1 / x e -= de assert e < 0.0 nn, A = shoot_confined(u, l, vr, e, self.r2dvdr, r, dr, c10, c2, self.scalarrel, rc=rc, beta=self.beta) u *= 1.0 / sqrt(np.dot(np.where(abs(u) < 1e-160, 0, u)**2, dr)) return u, e def kin(self, l, u, e=None): # XXX move to Generator r = self.r[1:] dr = self.dr[1:] c0 = 0.5 * l * (l + 1) / r**2 c1 = -0.5 * self.d2gdr2[1:] c2 = -0.5 * dr**-2 if e is not None and self.scalarrel: x = 0.5 * alpha**2 Mr = r * (1.0 + x * e) - x * self.vr[1:] c0 += ((Mr - r) * (self.vr[1:] - e * r) + 0.5 * x * self.r2dvdr[1:] / Mr) / r**2 c1 -= 0.5 * x * self.r2dvdr[1:] / (Mr * dr * r) fp = c2 + 0.5 * c1 fm = c2 - 0.5 * c1 f0 = c0 - 2 * c2 kr = np.zeros(self.N) kr[1:] = f0 * u[1:] + fm * u[:-1] kr[1:-1] += fp[:-1] * u[2:] kr[0] = 0.0 return kr def r2g(self, r): """Convert radius to index of the radial grid.""" return int(r * self.N / (self.beta + r)) def get_confinement_potential(self, alpha, ri, rc): """Create a smooth confinement potential. Returns a (potential) function which is zero inside the radius ri and goes to infinity smoothly at rc, after which point it is nan. The potential is given by:: alpha / rc - ri \ V(r) = -------- exp ( - --------- ) for ri < r < rc rc - r \ r - ri / """ i_ri = self.r2g(ri) i_rc = self.r2g(rc) if self.r[i_rc] == rc: # Avoid division by zero in the odd case that rc coincides # exactly with a grid point (which actually happens sometimes) i_rc -= 1 potential = np.zeros(np.shape(self.r)) r = self.r[i_ri + 1:i_rc + 1] exponent = - (rc - ri) / (r - ri) denom = rc - r value = np.exp(exponent) / denom potential[i_ri + 1:i_rc + 1] = value potential[i_rc + 1:] = np.inf return alpha * potential
'GGA_X_B88+GGA_C_P86': 2.30508027546, 'GGA_X_B88+GGA_C_LYP': 2.28183010548, 'GGA_X_FT97_A+GGA_C_LYP': 2.26846048873, } libxc_set = [ 'LDA_X', 'LDA_X+LDA_C_PW', 'LDA_X+LDA_C_VWN', 'LDA_X+LDA_C_PZ', 'GGA_X_PBE+GGA_C_PBE', 'GGA_X_PBE_R+GGA_C_PBE', 'GGA_X_B88+GGA_C_P86', 'GGA_X_B88+GGA_C_LYP', 'GGA_X_FT97_A+GGA_C_LYP' ] x = 0.000001 for xcname in libxc_set: ra.seed(8) xc = XC(xcname) s = create_setup('N', xc) ni = s.ni nii = ni * (ni + 1) // 2 D_p = 0.1 * ra.random(nii) + 0.4 H_p = np.zeros(nii) E1 = xc.calculate_paw_correction(s, D_p.reshape(1, -1), H_p.reshape(1, -1)) dD_p = x * ra.random(nii) D_p += dD_p dE = np.dot(H_p, dD_p) / x E2 = xc.calculate_paw_correction(s, D_p.reshape(1, -1)) print xcname, dE, (E2 - E1) / x equal(dE, (E2 - E1) / x, 0.003) E2s = xc.calculate_paw_correction(s,
def __init__(self, name, stencil=2, hybrid=None, xc=None, omega=None): """Mix standard functionals with exact exchange. name: str Name of hybrid functional. hybrid: float Fraction of exact exchange. xc: str or XCFunctional object Standard DFT functional with scaled down exchange. """ rsf_functionals = { # Parameters can also be taken from libxc 'CAMY-BLYP': { # Akinaga, Ten-no CPL 462 (2008) 348-351 'alpha': 0.2, 'beta': 0.8, 'omega': 0.44, 'cam': True, 'rsf': 'Yukawa', 'xc': 'HYB_GGA_XC_CAMY_BLYP' }, 'CAMY-B3LYP': { # Seth, Ziegler JCTC 8 (2012) 901-907 'alpha': 0.19, 'beta': 0.46, 'omega': 0.34, 'cam': True, 'rsf': 'Yukawa', 'xc': 'HYB_GGA_XC_CAMY_B3LYP' }, 'LCY-BLYP': { # Seth, Ziegler JCTC 8 (2012) 901-907 'alpha': 0.0, 'beta': 1.0, 'omega': 0.75, 'cam': False, 'rsf': 'Yukawa', 'xc': 'HYB_GGA_XC_LCY_BLYP' }, 'LCY-PBE': { # Seth, Ziegler JCTC 8 (2012) 901-907 'alpha': 0.0, 'beta': 1.0, 'omega': 0.75, 'cam': False, 'rsf': 'Yukawa', 'xc': 'HYB_GGA_XC_LCY_PBE' } } self.omega = None self.cam_alpha = None self.cam_beta = None self.is_cam = False self.rsf = None def _xc(name): return {'name': name, 'stencil': stencil} if name == 'EXX': hybrid = 1.0 xc = XC(XCNull()) elif name == 'PBE0': hybrid = 0.25 xc = XC(_xc('HYB_GGA_XC_PBEH')) elif name == 'B3LYP': hybrid = 0.2 xc = XC(_xc('HYB_GGA_XC_B3LYP')) elif name == 'HSE03': hybrid = 0.25 omega = 0.106 xc = XC(_xc('HYB_GGA_XC_HSE03')) elif name == 'HSE06': hybrid = 0.25 omega = 0.11 xc = XC(_xc('HYB_GGA_XC_HSE06')) elif name in rsf_functionals: rsf_functional = rsf_functionals[name] self.cam_alpha = rsf_functional['alpha'] self.cam_beta = rsf_functional['beta'] self.omega = rsf_functional['omega'] self.is_cam = rsf_functional['cam'] self.rsf = rsf_functional['rsf'] xc = XC(rsf_functional['xc']) hybrid = self.cam_alpha + self.cam_beta if isinstance(xc, (basestring, dict)): xc = XC(xc) self.hybrid = float(hybrid) self.xc = xc if omega is not None: omega = float(omega) if self.omega is not None and self.omega != omega: self.xc.kernel.set_omega(omega) # Needed to tune omega for RSF self.omega = omega XCFunctional.__init__(self, name, xc.type)
def initialize(self, atoms=None): """Inexpensive initialization.""" if atoms is None: atoms = self.atoms else: # Save the state of the atoms: self.atoms = atoms.copy() par = self.input_parameters world = par.communicator if world is None: world = mpi.world elif hasattr(world, 'new_communicator'): # Check for whether object has correct type already # # Using isinstance() is complicated because of all the # combinations, serial/parallel/debug... pass else: # world should be a list of ranks: world = mpi.world.new_communicator(np.asarray(world)) self.wfs.world = world self.set_text(par.txt, par.verbose) natoms = len(atoms) pos_av = atoms.get_positions() / Bohr cell_cv = atoms.get_cell() pbc_c = atoms.get_pbc() Z_a = atoms.get_atomic_numbers() magmom_a = atoms.get_initial_magnetic_moments() magnetic = magmom_a.any() spinpol = par.spinpol if par.hund: if natoms != 1: raise ValueError('hund=True arg only valid for single atoms!') spinpol = True if spinpol is None: spinpol = magnetic elif magnetic and not spinpol: raise ValueError('Non-zero initial magnetic moment for a ' 'spin-paired calculation!') nspins = 1 + int(spinpol) if isinstance(par.xc, str): xc = XC(par.xc) else: xc = par.xc setups = Setups(Z_a, par.setups, par.basis, par.lmax, xc, world) # K-point descriptor kd = KPointDescriptor(par.kpts, nspins) width = par.width if width is None: if kd.gamma: width = 0.0 else: width = 0.1 # eV else: assert par.occupations is None if par.gpts is not None and par.h is None: N_c = np.array(par.gpts) else: if par.h is None: self.text('Using default value for grid spacing.') h = 0.2 else: h = par.h N_c = h2gpts(h, cell_cv) cell_cv /= Bohr if hasattr(self, 'time') or par.dtype==complex: dtype = complex else: if kd.gamma: dtype = float else: dtype = complex kd.set_symmetry(atoms, setups, par.usesymm, N_c) nao = setups.nao nvalence = setups.nvalence - par.charge nbands = par.nbands if nbands is None: nbands = nao elif nbands > nao and par.mode == 'lcao': raise ValueError('Too many bands for LCAO calculation: ' + '%d bands and only %d atomic orbitals!' % (nbands, nao)) if nvalence < 0: raise ValueError( 'Charge %f is not possible - not enough valence electrons' % par.charge) M = magmom_a.sum() if par.hund: f_si = setups[0].calculate_initial_occupation_numbers( magmom=0, hund=True, charge=par.charge, nspins=nspins) Mh = f_si[0].sum() - f_si[1].sum() if magnetic and M != Mh: raise RuntimeError('You specified a magmom that does not' 'agree with hunds rule!') else: M = Mh if nbands <= 0: nbands = int(nvalence + M + 0.5) // 2 + (-nbands) if nvalence > 2 * nbands: raise ValueError('Too few bands! Electrons: %d, bands: %d' % (nvalence, nbands)) if par.width is not None: self.text('**NOTE**: please start using ' 'occupations=FermiDirac(width).') if par.fixmom: self.text('**NOTE**: please start using ' 'occupations=FermiDirac(width, fixmagmom=True).') if self.occupations is None: if par.occupations is None: # Create object for occupation numbers: self.occupations = occupations.FermiDirac(width, par.fixmom) else: self.occupations = par.occupations self.occupations.magmom = M cc = par.convergence if par.mode == 'lcao': niter_fixdensity = 0 else: niter_fixdensity = None if self.scf is None: self.scf = SCFLoop( cc['eigenstates'] * nvalence, cc['energy'] / Hartree * max(nvalence, 1), cc['density'] * nvalence, par.maxiter, par.fixdensity, niter_fixdensity) parsize, parsize_bands = par.parallel['domain'], par.parallel['band'] if parsize_bands is None: parsize_bands = 1 # TODO delete/restructure so all checks are in BandDescriptor if nbands % parsize_bands != 0: raise RuntimeError('Cannot distribute %d bands to %d processors' % (nbands, parsize_bands)) if not self.wfs: if parsize == 'domain only': #XXX this was silly! parsize = world.size domain_comm, kpt_comm, band_comm = mpi.distribute_cpus(parsize, parsize_bands, nspins, kd.nibzkpts, world, par.idiotproof) kd.set_communicator(kpt_comm) parstride_bands = par.parallel['stridebands'] bd = BandDescriptor(nbands, band_comm, parstride_bands) if (self.density is not None and self.density.gd.comm.size != domain_comm.size): # Domain decomposition has changed, so we need to # reinitialize density and hamiltonian: if par.fixdensity: raise RuntimeError("I'm confused - please specify parsize." ) self.density = None self.hamiltonian = None # Construct grid descriptor for coarse grids for wave functions: gd = self.grid_descriptor_class(N_c, cell_cv, pbc_c, domain_comm, parsize) # do k-point analysis here? XXX args = (gd, nvalence, setups, bd, dtype, world, kd, self.timer) if par.mode == 'lcao': # Layouts used for general diagonalizer sl_lcao = par.parallel['sl_lcao'] if sl_lcao is None: sl_lcao = par.parallel['sl_default'] lcaoksl = get_KohnSham_layouts(sl_lcao, 'lcao', gd, bd, dtype, nao=nao, timer=self.timer) self.wfs = LCAOWaveFunctions(lcaoksl, *args) elif par.mode == 'fd' or isinstance(par.mode, PW): # buffer_size keyword only relevant for fdpw buffer_size = par.parallel['buffer_size'] # Layouts used for diagonalizer sl_diagonalize = par.parallel['sl_diagonalize'] if sl_diagonalize is None: sl_diagonalize = par.parallel['sl_default'] diagksl = get_KohnSham_layouts(sl_diagonalize, 'fd', gd, bd, dtype, buffer_size=buffer_size, timer=self.timer) # Layouts used for orthonormalizer sl_inverse_cholesky = par.parallel['sl_inverse_cholesky'] if sl_inverse_cholesky is None: sl_inverse_cholesky = par.parallel['sl_default'] if sl_inverse_cholesky != sl_diagonalize: message = 'sl_inverse_cholesky != sl_diagonalize ' \ 'is not implemented.' raise NotImplementedError(message) orthoksl = get_KohnSham_layouts(sl_inverse_cholesky, 'fd', gd, bd, dtype, buffer_size=buffer_size, timer=self.timer) # Use (at most) all available LCAO for initialization lcaonbands = min(nbands, nao) lcaobd = BandDescriptor(lcaonbands, band_comm, parstride_bands) assert nbands <= nao or bd.comm.size == 1 assert lcaobd.mynbands == min(bd.mynbands, nao) #XXX # Layouts used for general diagonalizer (LCAO initialization) sl_lcao = par.parallel['sl_lcao'] if sl_lcao is None: sl_lcao = par.parallel['sl_default'] initksl = get_KohnSham_layouts(sl_lcao, 'lcao', gd, lcaobd, dtype, nao=nao, timer=self.timer) if par.mode == 'fd': self.wfs = FDWaveFunctions(par.stencils[0], diagksl, orthoksl, initksl, *args) else: # Planewave basis: self.wfs = par.mode(diagksl, orthoksl, initksl, gd, nvalence, setups, bd, world, kd, self.timer) else: self.wfs = par.mode(self, *args) else: self.wfs.set_setups(setups) if not self.wfs.eigensolver: # Number of bands to converge: nbands_converge = cc['bands'] if nbands_converge == 'all': nbands_converge = nbands elif nbands_converge != 'occupied': assert isinstance(nbands_converge, int) if nbands_converge < 0: nbands_converge += nbands eigensolver = get_eigensolver(par.eigensolver, par.mode, par.convergence) eigensolver.nbands_converge = nbands_converge # XXX Eigensolver class doesn't define an nbands_converge property self.wfs.set_eigensolver(eigensolver) if self.density is None: gd = self.wfs.gd if par.stencils[1] != 9: # Construct grid descriptor for fine grids for densities # and potentials: finegd = gd.refine() else: # Special case (use only coarse grid): finegd = gd self.density = Density(gd, finegd, nspins, par.charge + setups.core_charge) self.density.initialize(setups, par.stencils[1], self.timer, magmom_a, par.hund) self.density.set_mixer(par.mixer) if self.hamiltonian is None: gd, finegd = self.density.gd, self.density.finegd self.hamiltonian = Hamiltonian(gd, finegd, nspins, setups, par.stencils[1], self.timer, xc, par.poissonsolver, par.external) xc.initialize(self.density, self.hamiltonian, self.wfs, self.occupations) self.text() self.print_memory_estimate(self.txt, maxdepth=memory_estimate_depth) self.txt.flush() if dry_run: self.dry_run() self.initialized = True
def run(self, use_restart_file=True): # beta g # r = ------, g = 0, 1, ..., N - 1 # N - g # # rN # g = -------- # beta + r t = self.text N = self.N beta = self.beta t(N, 'radial gridpoints.') self.rgd = AERadialGridDescriptor(beta / N, 1.0 / N, N) g = np.arange(N, dtype=float) self.r = self.rgd.r_g self.dr = self.rgd.dr_g self.d2gdr2 = self.rgd.d2gdr2() # Number of orbitals: nj = len(self.n_j) # Radial wave functions multiplied by radius: self.u_j = np.zeros((nj, self.N)) # Effective potential multiplied by radius: self.vr = np.zeros(N) # Electron density: self.n = np.zeros(N) # Always spinpaired nspins=1 self.xc = XC(self.xcname) # Initialize for non-local functionals if self.xc.type == 'GLLB': self.xc.pass_stuff_1d(self) self.xc.initialize_1d() n_j = self.n_j l_j = self.l_j f_j = self.f_j e_j = self.e_j Z = self.Z # nuclear charge r = self.r # radial coordinate dr = self.dr # dr/dg n = self.n # electron density vr = self.vr # effective potential multiplied by r vHr = np.zeros(self.N) self.vXC = np.zeros(self.N) restartfile = '%s/%s.restart' % (tempdir, self.symbol) if self.xc.type == 'GLLB' or not use_restart_file: # Do not start from initial guess when doing # non local XC! # This is because we need wavefunctions as well # on the first iteration. fd = None else: try: fd = open(restartfile, 'r') except IOError: fd = None else: try: n[:] = pickle.load(fd) except (ValueError, IndexError): fd = None else: norm = np.dot(n * r**2, dr) * 4 * pi if abs(norm - sum(f_j)) > 0.01: fd = None else: t('Using old density for initial guess.') n *= sum(f_j) / norm if fd is None: self.initialize_wave_functions() n[:] = self.calculate_density() bar = '|------------------------------------------------|' t(bar) niter = 0 nitermax = 117 qOK = log(1e-10) mix = 0.4 # orbital_free needs more iterations and coefficient if self.orbital_free: #qOK = log(1e-14) e_j[0] /= self.tf_coeff mix = 0.01 nitermax = 1000 vrold = None while True: # calculate hartree potential hartree(0, n * r * dr, r, vHr) # add potential from nuclear point charge (v = -Z / r) vHr -= Z # calculated exchange correlation potential and energy self.vXC[:] = 0.0 if self.xc.type == 'GLLB': # Update the potential to self.vXC an the energy to self.Exc Exc = self.xc.get_xc_potential_and_energy_1d(self.vXC) else: Exc = self.xc.calculate_spherical(self.rgd, n.reshape((1, -1)), self.vXC.reshape((1, -1))) # calculate new total Kohn-Sham effective potential and # admix with old version vr[:] = (vHr + self.vXC * r) / self.tf_coeff if niter > 0: vr[:] = mix * vr + (1 - mix) * vrold vrold = vr.copy() # solve Kohn-Sham equation and determine the density change self.solve() dn = self.calculate_density() - n n += dn # estimate error from the square of the density change integrated q = log(np.sum((r * dn)**2)) # print progress bar if niter == 0: q0 = q b0 = 0 else: b = int((q0 - q) / (q0 - qOK) * 50) if b > b0: self.txt.write(bar[b0:min(b, 50)]) self.txt.flush() b0 = b # check if converged and break loop if so if q < qOK: self.txt.write(bar[b0:]) self.txt.flush() break niter += 1 if niter > nitermax: raise RuntimeError('Did not converge!') tau = self.calculate_kinetic_energy_density() t() t('Converged in %d iteration%s.' % (niter, 's'[:niter != 1])) try: fd = open(restartfile, 'w') except IOError: pass else: pickle.dump(n, fd) try: os.chmod(restartfile, 0666) except OSError: pass Ekin = 0 if self.orbital_free: e_j[0] *= self.tf_coeff vr *= self.tf_coeff for f, e in zip(f_j, e_j): Ekin += f * e Epot = 2 * pi * np.dot(n * r * (vHr - Z), dr) Ekin += -4 * pi * np.dot(n * vr * r, dr) t() t('Energy contributions:') t('-------------------------') t('Kinetic: %+13.6f' % Ekin) t('XC: %+13.6f' % Exc) t('Potential: %+13.6f' % Epot) t('-------------------------') t('Total: %+13.6f' % (Ekin + Exc + Epot)) self.ETotal = Ekin + Exc + Epot t() t('state eigenvalue ekin rmax') t('-----------------------------------------------') for m, l, f, e, u in zip(n_j, l_j, f_j, e_j, self.u_j): # Find kinetic energy: k = e - np.sum((np.where(abs(u) < 1e-160, 0, u)**2 * # XXXNumeric! vr * dr)[1:] / r[1:]) # Find outermost maximum: g = self.N - 4 while u[g - 1] >= u[g]: g -= 1 x = r[g - 1:g + 2] y = u[g - 1:g + 2] A = np.transpose(np.array([x**i for i in range(3)])) c, b, a = np.linalg.solve(A, y) assert a < 0.0 rmax = -0.5 * b / a s = 'spdf'[l] t('%d%s^%-4.1f: %12.6f %12.6f %12.3f' % (m, s, f, e, k, rmax)) t('-----------------------------------------------') t('(units: Bohr and Hartree)') for m, l, u in zip(n_j, l_j, self.u_j): self.write(u, 'ae', n=m, l=l) self.write(n, 'n') self.write(vr, 'vr') self.write(vHr, 'vHr') self.write(self.vXC, 'vXC') self.write(tau, 'tau') self.Ekin = Ekin self.Epot = Epot self.Exc = Exc
class AllElectronAtom: def __init__(self, symbol, xc='LDA', spinpol=False, dirac=False, log=sys.stdout): """All-electron calculation for spherically symmetric atom. symbol: str (or int) Chemical symbol (or atomic number). xc: str Name of XC-functional. spinpol: bool If true, do spin-polarized calculation. Default is spin-paired. dirac: bool Solve Dirac equation instead of Schrödinger equation. log: stream Text output.""" if isinstance(symbol, int): symbol = chemical_symbols[symbol] self.symbol = symbol self.Z = atomic_numbers[symbol] self.nspins = 1 + int(bool(spinpol)) self.dirac = bool(dirac) if isinstance(xc, str): self.xc = XC(xc) else: self.xc = xc if log is None: log = devnull self.fd = log self.vr_sg = None # potential * r self.n_sg = 0.0 # density self.gd = None # radial grid descriptor # Energies: self.ekin = None self.eeig = None self.eH = None self.eZ = None self.channels = None self.initialize_configuration() self.log('Z: ', self.Z) self.log('Name: ', atomic_names[self.Z]) self.log('Symbol: ', symbol) self.log('XC-functional: ', self.xc.name) self.log('Equation: ', ['Schrödinger', 'Dirac'][self.dirac]) def log(self, *args, **kwargs): self.fd.write(kwargs.get('sep', ' ').join([str(arg) for arg in args]) + kwargs.get('end', '\n')) def initialize_configuration(self): self.f_lsn = {} for n, l, f, e in configurations[self.symbol][1]: if l not in self.f_lsn: self.f_lsn[l] = [[] for s in range(self.nspins)] if self.nspins == 1: self.f_lsn[l][0].append(f) else: # Use Hund's rule: f0 = min(f, 2 * l + 1) self.f_lsn[l][0].append(f0) self.f_lsn[l][1].append(f - f0) def add(self, n, l, df=+1, s=None): """Add (remove) electrons.""" if s is None: if self.nspins == 1: s = 0 else: self.add(n, l, 0.5 * df, 0) self.add(n, l, 0.5 * df, 1) return if l not in self.f_lsn: self.f_lsn[l] = [[] for x in range(self.nspins)] f_n = self.f_lsn[l][s] if len(f_n) < n - l: f_n.extend([0] * (n - l - len(f_n))) f_n[n - l - 1] += df def initialize(self, ngpts=1000, rcut=50.0, alpha1=0.01, alpha2=None, ngauss=50, eps=1.0e-7): """Initialize basis sets and radial grid. ngpts: int Number of grid points for radial grid. rcut: float Cutoff for radial grid. alpha1: float Smallest exponent for gaussian. alpha2: float Largest exponent for gaussian. ngauss: int Number of gaussians. eps: float Cutoff for eigenvalues of overlap matrix.""" if alpha2 is None: alpha2 = 50.0 * self.Z**2 self.gd = GridDescriptor(r1=1 / alpha2**0.5 / 50, rN=rcut, N=ngpts) self.log('Grid points: %d (%.5f, %.5f, %.5f, ..., %.3f, %.3f)' % ((self.gd.N,) + tuple(self.gd.r_g[[0, 1, 2, -2, -1]]))) # Distribute exponents between alpha1 and alpha2: alpha_B = alpha1 * (alpha2 / alpha1)**np.linspace(0, 1, ngauss) self.log('Exponents: %d (%.3f, %.3f, ..., %.3f, %.3f)' % ((ngauss,) + tuple(alpha_B[[0, 1, -2, -1]]))) # Maximum l value: lmax = max(self.f_lsn.keys()) self.channels = [] nb_l = [] if not self.dirac: for l in range(lmax + 1): basis = GaussianBasis(l, alpha_B, self.gd, eps) nb_l.append(len(basis)) for s in range(self.nspins): self.channels.append(Channel(l, s, self.f_lsn[l][s], basis)) else: for K in range(1, lmax + 2): leff = (K**2 - (self.Z / c)**2)**0.5 - 1 basis = GaussianBasis(leff, alpha_B, self.gd, eps) nb_l.append(len(basis)) for k, l in [(-K, K - 1), (K, K)]: if l > lmax: continue f_n = self.f_lsn[l][0] j = abs(k) - 0.5 f_n = (2 * j + 1) / (4 * l + 2) * np.array(f_n) self.channels.append(DiracChannel(k, f_n, basis)) self.log('Basis functions: %s (%s)' % (', '.join([str(nb) for nb in nb_l]), ', '.join('spdf'[:lmax + 1]))) self.vr_sg = self.gd.zeros(self.nspins) self.vr_sg[:] = -self.Z def solve(self): """Diagonalize Schrödinger equation.""" self.eeig = 0.0 for channel in self.channels: channel.solve(self.vr_sg[channel.s]) self.eeig += channel.get_eigenvalue_sum() def calculate_density(self): """Calculate elctron density and kinetic energy.""" self.n_sg = self.gd.zeros(self.nspins) for channel in self.channels: self.n_sg[channel.s] += channel.calculate_density() def calculate_electrostatic_potential(self): """Calculate electrostatic potential and energy.""" n_g = self.n_sg.sum(0) self.vHr_g = self.gd.poisson(n_g) self.eH = 0.5 * self.gd.integrate(n_g * self.vHr_g, -1) self.eZ = -self.Z * self.gd.integrate(n_g, -1) def calculate_xc_potential(self): self.vxc_sg = self.gd.zeros(self.nspins) self.exc = self.xc.calculate_spherical(self.gd, self.n_sg, self.vxc_sg) def step(self): self.solve() self.calculate_density() self.calculate_electrostatic_potential() self.calculate_xc_potential() self.vr_sg = self.vxc_sg * self.gd.r_g self.vr_sg += self.vHr_g self.vr_sg -= self.Z self.ekin = (self.eeig - self.gd.integrate((self.vr_sg * self.n_sg).sum(0), -1)) def run(self, mix=0.4, maxiter=117, dnmax=1e-9): if self.channels is None: self.initialize() dn = self.Z pb = ProgressBar(log(dnmax / dn), 0, 53, self.fd) self.log() for iter in range(maxiter): if iter > 1: self.vr_sg *= mix self.vr_sg += (1 - mix) * vr_old_sg dn = self.gd.integrate(abs(self.n_sg - n_old_sg).sum(0)) pb(log(dnmax / dn)) if dn <= dnmax: break vr_old_sg = self.vr_sg n_old_sg = self.n_sg self.step() self.summary() if dn > dnmax: raise RuntimeError('Did not converge!') def summary(self): self.write_states() self.write_energies() def write_states(self): self.log('\n state occupation eigenvalue <r>') if self.dirac: self.log(' nl(j) [Hartree] [eV] [Bohr]') else: self.log(' nl [Hartree] [eV] [Bohr]') self.log('=====================================================') states = [] for ch in self.channels: for n, f in enumerate(ch.f_n): states.append((ch.e_n[n], ch, n)) states.sort() for e, ch, n in states: name = str(n + ch.l + 1) + ch.name if self.nspins == 2: name += '(%s)' % '+-'[ch.s] n_g = ch.calculate_density(n) rave = self.gd.integrate(n_g, 1) self.log(' %-7s %6.3f %13.6f %13.5f %6.3f' % (name, ch.f_n[n], e, e * units.Hartree, rave)) self.log('=====================================================') def write_energies(self): self.log('\nEnergies: [Hartree] [eV]') self.log('============================================') for text, e in [('kinetic ', self.ekin), ('coulomb (e-e)', self.eH), ('coulomb (e-n)', self.eZ), ('xc ', self.exc), ('total ', self.ekin + self.eH + self.eZ + self.exc)]: self.log(' %s %+13.6f %+13.5f' % (text, e, units.Hartree * e)) self.log('============================================') def get_channel(self, l=None, s=0, k=None): if self.dirac: for channel in self.channels: if channel.k == k: return channel else: for channel in self.channels: if channel.l == l and channel.s == s: return channel raise ValueError def get_orbital(self, n, l=None, s=0, k=None): channel = self.get_channel(l, s, k) return channel.basis.expand(channel.C_nb[n]) def plot_wave_functions(self, rc=4.0): import matplotlib.pyplot as plt colors = 'krgbycm' for ch in self.channels: for n in range(len(ch.f_n)): fr_g = ch.basis.expand(ch.C_nb[n]) * self.gd.r_g name = str(n + ch.l + 1) + ch.name lw = 2 if self.nspins == 2: name += '(%s)' % '+-'[ch.s] if ch.s == 1: lw = 1 if self.dirac and ch.k > 0: lw = 1 ls = ['-', '--', '-.', ':'][ch.l] n_g = ch.calculate_density(n) rave = self.gd.integrate(n_g, 1) gave = self.gd.get_index(rave) fr_g *= cmp(fr_g[gave], 0) plt.plot(self.gd.r_g, fr_g, ls=ls, lw=lw, color=colors[n + ch.l], label=name) plt.legend(loc='best') plt.axis(xmax=rc) plt.show() def logarithmic_derivative(self, l, energies, rcut): vr = splrep(self.gd.r_g, self.vr_sg[0]) def v(r): return splev(r, vr) / r def f(y, r, e): if r == 0: return [y[1], -2.0] return [y[1], 2 * (v(r) - e) * y[0]] logderivs = [] for e in energies: u, dudr = odeint(f, [0, 1], [0, rcut], (e,))[1, :] logderivs.append(dudr / u) return logderivs
def __init__(self, symbol, xc='LDA', spinpol=False, dirac=False, configuration=None, log=None): """All-electron calculation for spherically symmetric atom. symbol: str (or int) Chemical symbol (or atomic number). xc: str Name of XC-functional. spinpol: bool If true, do spin-polarized calculation. Default is spin-paired. dirac: bool Solve Dirac equation instead of Schrödinger equation. configuration: list Electronic configuration for symbol, format as in gpaw.atom.configurations log: stream Text output.""" if isinstance(symbol, int): symbol = chemical_symbols[symbol] self.symbol = symbol self.Z = atomic_numbers[symbol] self.nspins = 1 + int(bool(spinpol)) self.dirac = bool(dirac) if configuration is not None: self.configuration = copy.deepcopy(configuration) else: self.configuration = None self.scalar_relativistic = False if isinstance(xc, str): self.xc = XC(xc) else: self.xc = xc self.fd = log or sys.stdout self.vr_sg = None # potential * r self.n_sg = 0.0 # density self.rgd = None # radial grid descriptor # Energies: self.ekin = None self.eeig = None self.eH = None self.eZ = None self.channels = None self.initialize_configuration(self.configuration) self.log('Z: ', self.Z) self.log('Name: ', atomic_names[self.Z]) self.log('Symbol: ', symbol) self.log('XC-functional: ', self.xc.name) self.log('Equation: ', ['Schrödinger', 'Dirac'][self.dirac]) self.method = 'Gaussian basis-set'
class C_XC(Contribution): def __init__(self, nlfunc, weight, functional = 'LDA'): Contribution.__init__(self, nlfunc, weight) self.functional = functional def get_name(self): return 'XC' def get_desc(self): return "("+self.functional+")" def initialize(self): self.xc = XC(self.functional) self.vt_sg = self.nlfunc.finegd.empty(self.nlfunc.nspins) self.e_g = self.nlfunc.finegd.empty() def initialize_1d(self): self.ae = self.nlfunc.ae self.xc = XC(self.functional) self.v_g = np.zeros(self.ae.N) def calculate_spinpaired(self, e_g, n_g, v_g): self.e_g[:] = 0.0 self.vt_sg[:] = 0.0 self.xc.calculate(self.nlfunc.finegd, n_g[None, ...], self.vt_sg, self.e_g) v_g += self.weight * self.vt_sg[0] e_g += self.weight * self.e_g def calculate_spinpolarized(self, e_g, na_g, va_g, nb_g, vb_g): self.e_g[:] = 0.0 self.vt_sg[:] = 0.0 self.xc.get_energy_and_potential(na_g, self.vt_sg[0], nb_g, self.vt_sg[1], e_g=self.e_g) va_g += self.weight * self.vt_sg[0] vb_g += self.weight * self.vt_sg[1] e_g += (self.weight * self.e_g).ravel() def calculate_energy_and_derivatives(self, D_sp, H_sp, a): # Get the XC-correction instance c = self.nlfunc.setups[a].xc_correction assert self.nlfunc.nspins == 1 D_p = D_sp[0] dEdD_p = H_sp[0][:] D_Lq = dot3(c.B_pqL.T, D_p) n_Lg = np.dot(D_Lq, c.n_qg) n_Lg[0] += c.nc_g * sqrt(4 * pi) nt_Lg = np.dot(D_Lq, c.nt_qg) nt_Lg[0] += c.nct_g * sqrt(4 * pi) dndr_Lg = np.zeros((c.Lmax, c.ng)) dntdr_Lg = np.zeros((c.Lmax, c.ng)) for L in range(c.Lmax): c.rgd.derivative(n_Lg[L], dndr_Lg[L]) c.rgd.derivative(nt_Lg[L], dntdr_Lg[L]) E = 0 vt_g = np.zeros(c.ng) v_g = np.zeros(c.ng) e_g = np.zeros(c.ng) y = 0 for w, Y_L in zip(weight_n, c.Y_nL): A_Li = rnablaY_nLv[y, :c.Lmax] a1x_g = np.dot(A_Li[:, 0], n_Lg) a1y_g = np.dot(A_Li[:, 1], n_Lg) a1z_g = np.dot(A_Li[:, 2], n_Lg) a2_g = a1x_g**2 + a1y_g**2 + a1z_g**2 a2_g[1:] /= c.rgd.r_g[1:]**2 a2_g[0] = a2_g[1] a1_g = np.dot(Y_L, dndr_Lg) a2_g += a1_g**2 deda2_g = np.zeros(c.ng) v_g[:] = 0.0 e_g[:] = 0.0 n_g = np.dot(Y_L, n_Lg) self.xc.kernel.calculate(e_g, n_g.reshape((1, -1)), v_g.reshape((1, -1)), a2_g.reshape((1, -1)), deda2_g.reshape((1, -1))) E += w * np.dot(e_g, c.rgd.dv_g) x_g = -2.0 * deda2_g * c.rgd.dv_g * a1_g c.rgd.derivative2(x_g, x_g) x_g += v_g * c.rgd.dv_g dEdD_p += self.weight * w * np.dot(dot3(c.B_pqL, Y_L), np.dot(c.n_qg, x_g)) x_g = 8.0 * pi * deda2_g * c.rgd.dr_g dEdD_p += w * np.dot(dot3(c.B_pqL, A_Li[:, 0]), np.dot(c.n_qg, x_g * a1x_g)) dEdD_p += w * np.dot(dot3(c.B_pqL, A_Li[:, 1]), np.dot(c.n_qg, x_g * a1y_g)) dEdD_p += w * np.dot(dot3(c.B_pqL, A_Li[:, 2]), np.dot(c.n_qg, x_g * a1z_g)) n_g = np.dot(Y_L, nt_Lg) a1x_g = np.dot(A_Li[:, 0], nt_Lg) a1y_g = np.dot(A_Li[:, 1], nt_Lg) a1z_g = np.dot(A_Li[:, 2], nt_Lg) a2_g = a1x_g**2 + a1y_g**2 + a1z_g**2 a2_g[1:] /= c.rgd.r_g[1:]**2 a2_g[0] = a2_g[1] a1_g = np.dot(Y_L, dntdr_Lg) a2_g += a1_g**2 v_g = np.zeros(c.ng) e_g = np.zeros(c.ng) deda2_g = np.zeros(c.ng) v_g[:] = 0.0 e_g[:] = 0.0 self.xc.kernel.calculate(e_g, n_g.reshape((1, -1)), v_g.reshape((1, -1)), a2_g.reshape((1, -1)), deda2_g.reshape((1, -1))) E -= w * np.dot(e_g, c.dv_g) x_g = -2.0 * deda2_g * c.dv_g * a1_g c.rgd.derivative2(x_g, x_g) x_g += v_g * c.dv_g B_Lqp = c.B_pqL.T dEdD_p -= w * np.dot(dot3(c.B_pqL, Y_L), np.dot(c.nt_qg, x_g)) x_g = 8.0 * pi * deda2_g * c.rgd.dr_g dEdD_p -= w * np.dot(dot3(c.B_pqL, A_Li[:, 0]), np.dot(c.nt_qg, x_g * a1x_g)) dEdD_p -= w * np.dot(dot3(c.B_pqL, A_Li[:, 1]), np.dot(c.nt_qg, x_g * a1y_g)) dEdD_p -= w * np.dot(dot3(c.B_pqL, A_Li[:, 2]), np.dot(c.nt_qg, x_g * a1z_g)) y += 1 return (E) * self.weight def add_xc_potential_and_energy_1d(self, v_g): self.v_g[:] = 0.0 Exc = self.xc.calculate_spherical(self.ae.rgd, self.ae.n.reshape((1, -1)), self.v_g.reshape((1, -1))) v_g += self.weight * self.v_g return self.weight * Exc def add_smooth_xc_potential_and_energy_1d(self, vt_g): self.v_g[:] = 0.0 Exc = self.xc.calculate_spherical(self.ae.rgd, self.ae.nt.reshape((1, -1)), self.v_g.reshape((1, -1))) vt_g += self.weight * self.v_g return self.weight * Exc def initialize_from_atomic_orbitals(self, basis_functions): # LDA needs only density, which is already initialized pass def add_extra_setup_data(self, dict): # LDA has not any special data pass def write(self, writer, natoms): # LDA has not any special data to be written pass def read(self, reader): # LDA has not any special data to be read pass