class MGGA(GGA): orbital_dependent = True def __init__(self, kernel, nn=1): """Meta GGA functional. nn: int Number of neighbor grid points to use for FD stencil for wave function gradient. """ self.nn = nn GGA.__init__(self, kernel) def set_grid_descriptor(self, gd): GGA.set_grid_descriptor(self, gd) def get_setup_name(self): return "PBE" def initialize(self, density, hamiltonian, wfs, occupations): self.wfs = wfs self.tauct = LFC(wfs.gd, [[setup.tauct] for setup in wfs.setups], forces=True, cut=True) self.tauct_G = None self.dedtaut_sG = None self.restrict = hamiltonian.restrictor.apply self.interpolate = density.interpolator.apply self.taugrad_v = [Gradient(wfs.gd, v, n=self.nn, dtype=wfs.dtype, allocate=True).apply for v in range(3)] def set_positions(self, spos_ac): self.tauct.set_positions(spos_ac) if self.tauct_G is None: self.tauct_G = self.wfs.gd.empty() self.tauct_G[:] = 0.0 self.tauct.add(self.tauct_G) def calculate_gga(self, e_g, nt_sg, v_sg, sigma_xg, dedsigma_xg): taut_sG = self.wfs.calculate_kinetic_energy_density(self.tauct, self.taugrad_v) taut_sg = np.empty_like(nt_sg) for taut_G, taut_g in zip(taut_sG, taut_sg): taut_G += 1.0 / self.wfs.nspins * self.tauct_G self.interpolate(taut_G, taut_g) dedtaut_sg = np.empty_like(nt_sg) self.kernel.calculate(e_g, nt_sg, v_sg, sigma_xg, dedsigma_xg, taut_sg, dedtaut_sg) self.dedtaut_sG = self.wfs.gd.empty(self.wfs.nspins) self.ekin = 0.0 for s in range(self.wfs.nspins): self.restrict(dedtaut_sg[s], self.dedtaut_sG[s]) self.ekin -= self.wfs.gd.integrate(self.dedtaut_sG[s] * (taut_sG[s] - self.tauct_G / self.wfs.nspins)) def apply_orbital_dependent_hamiltonian(self, kpt, psit_xG, Htpsit_xG, dH_asp): a_G = self.wfs.gd.empty(dtype=psit_xG.dtype) for psit_G, Htpsit_G in zip(psit_xG, Htpsit_xG): for v in range(3): self.taugrad_v[v](psit_G, a_G, kpt.phase_cd) self.taugrad_v[v](self.dedtaut_sG[kpt.s] * a_G, a_G, kpt.phase_cd) axpy(-0.5, a_G, Htpsit_G) def add_forces(self, F_av): dF_av = self.tauct.dict(derivative=True) self.tauct.derivative(self.dedtaut_sG.sum(0), dF_av) for a, dF_v in dF_av.items(): F_av[a] += dF_v[0] def estimate_memory(self, mem): bytecount = self.wfs.gd.bytecount() mem.subnode("MGGA arrays", (1 + self.wfs.nspins) * bytecount) def initialize_kinetic(self, xccorr): nii = xccorr.nii nn = len(xccorr.rnablaY_nLv) ng = len(xccorr.phi_jg[0]) tau_npg = np.zeros((nn, nii, ng)) taut_npg = np.zeros((nn, nii, ng)) self.create_kinetic(xccorr, nn, xccorr.phi_jg, tau_npg) self.create_kinetic(xccorr, nn, xccorr.phit_jg, taut_npg) return tau_npg, taut_npg def create_kinetic(self, x, ny, phi_jg, tau_ypg): """Short title here. kinetic expression is:: __ __ tau_s = 1/2 Sum_{i1,i2} D(s,i1,i2) \/phi_i1 . \/phi_i2 +tauc_s here the orbital dependent part is calculated:: __ __ \/phi_i1 . \/phi_i2 = __ __ \/YL1.\/YL2 phi_j1 phi_j2 +YL1 YL2 dphi_j1 dphi_j2 ------ ------ dr dr __ __ \/YL1.\/YL2 [y] = Sum_c A[L1,c,y] A[L2,c,y] / r**2 """ nj = len(phi_jg) ni = len(x.jlL) nii = ni * (ni + 1) // 2 dphidr_jg = np.zeros(np.shape(phi_jg)) for j in range(nj): phi_g = phi_jg[j] x.rgd.derivative(phi_g, dphidr_jg[j]) # Second term: for y in range(ny): i1 = 0 p = 0 Y_L = x.Y_nL[y] for j1, l1, L1 in x.jlL: for j2, l2, L2 in x.jlL[i1:]: c = Y_L[L1] * Y_L[L2] temp = c * dphidr_jg[j1] * dphidr_jg[j2] tau_ypg[y, p, :] += temp p += 1 i1 += 1 ##first term for y in range(ny): i1 = 0 p = 0 rnablaY_Lv = x.rnablaY_nLv[y, : x.Lmax] Ax_L = rnablaY_Lv[:, 0] Ay_L = rnablaY_Lv[:, 1] Az_L = rnablaY_Lv[:, 2] for j1, l1, L1 in x.jlL: for j2, l2, L2 in x.jlL[i1:]: temp = Ax_L[L1] * Ax_L[L2] + Ay_L[L1] * Ay_L[L2] + Az_L[L1] * Az_L[L2] temp *= phi_jg[j1] * phi_jg[j2] temp[1:] /= x.rgd.r_g[1:] ** 2 temp[0] = temp[1] tau_ypg[y, p, :] += temp p += 1 i1 += 1 tau_ypg *= 0.5 return
class RealSpaceHamiltonian(Hamiltonian): def __init__( self, gd, finegd, nspins, setups, timer, xc, world, kptband_comm, vext=None, collinear=True, psolver=None, stencil=3, ): Hamiltonian.__init__(self, gd, finegd, nspins, setups, timer, xc, world, kptband_comm, vext, collinear) # Solver for the Poisson equation: if psolver is None: psolver = PoissonSolver(nn=3, relax="J") self.poisson = psolver self.poisson.set_grid_descriptor(finegd) # Restrictor function for the potential: self.restrictor = Transformer(self.finegd, self.gd, stencil) self.restrict = self.restrictor.apply self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups], forces=True) self.vbar_g = None def summary(self, fd): Hamiltonian.summary(self, fd) degree = self.restrictor.nn * 2 - 1 name = ["linear", "cubic", "quintic", "heptic"][degree // 2] fd.write("Interpolation: tri-%s " % name + "(%d. degree polynomial)\n" % degree) fd.write("Poisson solver: %s\n" % self.poisson.get_description()) def set_positions(self, spos_ac, rank_a): Hamiltonian.set_positions(self, spos_ac, rank_a) if self.vbar_g is None: self.vbar_g = self.finegd.empty() self.vbar_g[:] = 0.0 self.vbar.add(self.vbar_g) def update_pseudo_potential(self, density): self.timer.start("vbar") Ebar = self.finegd.integrate(self.vbar_g, density.nt_g, global_integral=False) vt_g = self.vt_sg[0] vt_g[:] = self.vbar_g self.timer.stop("vbar") Eext = 0.0 if self.vext is not None: assert self.collinear vt_g += self.vext.get_potential(self.finegd) Eext = self.finegd.integrate(vt_g, density.nt_g, global_integral=False) - Ebar self.vt_sg[1 : self.nspins] = vt_g self.vt_sg[self.nspins :] = 0.0 self.timer.start("XC 3D grid") Exc = self.xc.calculate(self.finegd, density.nt_sg, self.vt_sg) Exc /= self.gd.comm.size self.timer.stop("XC 3D grid") self.timer.start("Poisson") # npoisson is the number of iterations: self.npoisson = self.poisson.solve(self.vHt_g, density.rhot_g, charge=-density.charge) self.timer.stop("Poisson") self.timer.start("Hartree integrate/restrict") Epot = 0.5 * self.finegd.integrate(self.vHt_g, density.rhot_g, global_integral=False) Ekin = 0.0 s = 0 for s, (vt_g, vt_G, nt_G) in enumerate(zip(self.vt_sg, self.vt_sG, density.nt_sG)): if s < self.nspins: vt_g += self.vHt_g self.restrict(vt_g, vt_G) if self.ref_vt_sG is not None: vt_G += self.ref_vt_sG[s] if s < self.nspins: Ekin -= self.gd.integrate(vt_G, nt_G - density.nct_G, global_integral=False) else: Ekin -= self.gd.integrate(vt_G, nt_G, global_integral=False) s += 1 self.timer.stop("Hartree integrate/restrict") # Calculate atomic hamiltonians: W_aL = {} for a in density.D_asp: W_aL[a] = np.empty((self.setups[a].lmax + 1) ** 2) density.ghat.integrate(self.vHt_g, W_aL) return Ekin, Epot, Ebar, Eext, Exc, W_aL def calculate_forces2(self, dens, ghat_aLv, nct_av, vbar_av): if self.nspins == 2: vt_G = self.vt_sG.mean(0) else: vt_G = self.vt_sG[0] dens.ghat.derivative(self.vHt_g, ghat_aLv) dens.nct.derivative(vt_G, nct_av) self.vbar.derivative(dens.nt_g, vbar_av)
class RealSpaceHamiltonian(Hamiltonian): def __init__(self, gd, finegd, nspins, setups, timer, xc, world, kptband_comm, vext=None, collinear=True, psolver=None, stencil=3): Hamiltonian.__init__(self, gd, finegd, nspins, setups, timer, xc, world, kptband_comm, vext, collinear) # Solver for the Poisson equation: if psolver is None: psolver = PoissonSolver(nn=3, relax='J') self.poisson = psolver self.poisson.set_grid_descriptor(finegd) # Restrictor function for the potential: self.restrictor = Transformer(self.finegd, self.gd, stencil) self.restrict = self.restrictor.apply self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups], forces=True) self.vbar_g = None def summary(self, fd): Hamiltonian.summary(self, fd) degree = self.restrictor.nn * 2 - 1 name = ['linear', 'cubic', 'quintic', 'heptic'][degree // 2] fd.write('Interpolation: tri-%s ' % name + '(%d. degree polynomial)\n' % degree) fd.write('Poisson solver: %s\n' % self.poisson.get_description()) def set_positions(self, spos_ac, rank_a): Hamiltonian.set_positions(self, spos_ac, rank_a) if self.vbar_g is None: self.vbar_g = self.finegd.empty() self.vbar_g[:] = 0.0 self.vbar.add(self.vbar_g) def update_pseudo_potential(self, density): self.timer.start('vbar') Ebar = self.finegd.integrate(self.vbar_g, density.nt_g, global_integral=False) vt_g = self.vt_sg[0] vt_g[:] = self.vbar_g self.timer.stop('vbar') Eext = 0.0 if self.vext is not None: assert self.collinear vt_g += self.vext.get_potential(self.finegd) Eext = self.finegd.integrate( vt_g, density.nt_g, global_integral=False) - Ebar self.vt_sg[1:self.nspins] = vt_g self.vt_sg[self.nspins:] = 0.0 self.timer.start('XC 3D grid') Exc = self.xc.calculate(self.finegd, density.nt_sg, self.vt_sg) Exc /= self.gd.comm.size self.timer.stop('XC 3D grid') self.timer.start('Poisson') # npoisson is the number of iterations: self.npoisson = self.poisson.solve(self.vHt_g, density.rhot_g, charge=-density.charge) self.timer.stop('Poisson') self.timer.start('Hartree integrate/restrict') Epot = 0.5 * self.finegd.integrate( self.vHt_g, density.rhot_g, global_integral=False) Ekin = 0.0 s = 0 for s, (vt_g, vt_G, nt_G) in enumerate(zip(self.vt_sg, self.vt_sG, density.nt_sG)): if s < self.nspins: vt_g += self.vHt_g self.restrict(vt_g, vt_G) if self.ref_vt_sG is not None: vt_G += self.ref_vt_sG[s] if s < self.nspins: Ekin -= self.gd.integrate(vt_G, nt_G - density.nct_G, global_integral=False) else: Ekin -= self.gd.integrate(vt_G, nt_G, global_integral=False) s += 1 self.timer.stop('Hartree integrate/restrict') # Calculate atomic hamiltonians: W_aL = {} for a in density.D_asp: W_aL[a] = np.empty((self.setups[a].lmax + 1)**2) density.ghat.integrate(self.vHt_g, W_aL) return Ekin, Epot, Ebar, Eext, Exc, W_aL def calculate_forces2(self, dens, ghat_aLv, nct_av, vbar_av): if self.nspins == 2: vt_G = self.vt_sG.mean(0) else: vt_G = self.vt_sG[0] dens.ghat.derivative(self.vHt_g, ghat_aLv) dens.nct.derivative(vt_G, nct_av) self.vbar.derivative(dens.nt_g, vbar_av)
class QNA(GGA): def __init__(self, atoms, parameters, qna_setup_name='PBE', alpha=2.0, override_atoms=None, stencil=2): # override_atoms is only used to test the partial derivatives # of xc-functional kernel = QNAKernel(self) GGA.__init__(self, kernel, stencil=stencil) self.atoms = atoms self.parameters = parameters self.qna_setup_name = qna_setup_name self.alpha = alpha self.override_atoms = override_atoms self.orbital_dependent = False def todict(self): dct = dict(type='qna-gga', name='QNA', setup_name=self.qna_setup_name, parameters=self.parameters, alpha=self.alpha, orbital_dependent=False) return dct def set_grid_descriptor(self, gd): GGA.set_grid_descriptor(self, gd) self.dedmu_g = gd.zeros() self.dedbeta_g = gd.zeros() # Create gaussian LFC l_lim = 1.0e-30 rcut = 12 points = 200 r_i = np.linspace(0, rcut, points + 1) rcgauss = 1.2 g_g = (2 / rcgauss**3 / np.pi * np.exp(-((r_i / rcgauss)**2)**self.alpha)) # Values too close to zero can cause numerical problems especially with # forces (some parts of the mu and beta field can become negative) g_g[np.where(g_g < l_lim)] = l_lim spline = Spline(l=0, rmax=rcut, f_g=g_g) spline_j = [[spline]] * len(self.atoms) self.Pa = LFC(gd, spline_j) def set_positions(self, spos_ac, atom_partition=None): self.Pa.set_positions(spos_ac) def calculate_spatial_parameters(self, atoms): mu_g = self.gd.zeros() beta_g = self.gd.zeros() denominator = self.gd.zeros() mu_a = {} beta_a = {} eye_a = {} for atom in atoms: mu, beta = self.parameters[atom.symbol] mu_a[atom.index] = np.array([mu]) beta_a[atom.index] = np.array([beta]) eye_a[atom.index] = np.array(1.0) self.Pa.add(mu_g, mu_a) self.Pa.add(beta_g, beta_a) self.Pa.add(denominator, eye_a) mu_g /= denominator beta_g /= denominator return mu_g, beta_g def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): self.current_atom = a return GGA.calculate_paw_correction(self, setup, D_sp, dEdD_sp, addcoredensity, a) def get_setup_name(self): return self.qna_setup_name def get_description(self): return 'QNA Parameters: ' + str(self.parameters) def add_forces(self, F_av): mu_g = self.gd.zeros() beta_g = self.gd.zeros() denominator = self.gd.zeros() mu_a = {} beta_a = {} eye_a = {} for atom in self.atoms: mu, beta = self.parameters[atom.symbol] mu_a[atom.index] = np.array([mu]) beta_a[atom.index] = np.array([beta]) eye_a[atom.index] = np.array(1.0) self.Pa.add(mu_g, mu_a) self.Pa.add(beta_g, beta_a) self.Pa.add(denominator, eye_a) mu_g /= denominator beta_g /= denominator # mu part1 = -self.dedmu_g / denominator part2 = -part1 * mu_g c_axiv = self.Pa.dict(derivative=True) self.Pa.derivative(part1, c_axiv) for atom in self.atoms: F_av[atom.index] -= c_axiv[atom.index][0][:] * mu_a[atom.index][0] c_axiv = self.Pa.dict(derivative=True) self.Pa.derivative(part2, c_axiv) for atom in self.atoms: F_av[atom.index] -= c_axiv[atom.index][0][:] # beta part1 = -self.dedbeta_g / denominator part2 = -part1 * beta_g c_axiv = self.Pa.dict(derivative=True) self.Pa.derivative(part1, c_axiv) for atom in self.atoms: F_av[atom.index] -= c_axiv[atom.index][0] * beta_a[atom.index][0] c_axiv = self.Pa.dict(derivative=True) self.Pa.derivative(part2, c_axiv) for atom in self.atoms: F_av[atom.index] -= c_axiv[atom.index][0][:]
class RealSpaceHamiltonian(Hamiltonian): def __init__(self, gd, finegd, nspins, setups, timer, xc, world, vext=None, psolver=None, stencil=3, redistributor=None): Hamiltonian.__init__(self, gd, finegd, nspins, setups, timer, xc, world, vext=vext, redistributor=redistributor) # Solver for the Poisson equation: if psolver is None: psolver = {} if isinstance(psolver, dict): psolver = create_poisson_solver(**psolver) self.poisson = psolver self.poisson.set_grid_descriptor(self.finegd) # Restrictor function for the potential: self.restrictor = Transformer(self.finegd, self.redistributor.aux_gd, stencil) self.restrict = self.restrictor.apply self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups], forces=True) self.vbar_g = None def restrict_and_collect(self, a_xg, b_xg=None, phases=None): if self.redistributor.enabled: tmp_xg = self.restrictor.apply(a_xg, output=None, phases=phases) b_xg = self.redistributor.collect(tmp_xg, b_xg) else: b_xg = self.restrictor.apply(a_xg, output=b_xg, phases=phases) return b_xg def __str__(self): s = Hamiltonian.__str__(self) degree = self.restrictor.nn * 2 - 1 name = ['linear', 'cubic', 'quintic', 'heptic'][degree // 2] s += (' Interpolation: tri-%s ' % name + '(%d. degree polynomial)\n' % degree) s += ' Poisson solver: %s' % self.poisson.get_description() return s def set_positions(self, spos_ac, rank_a): Hamiltonian.set_positions(self, spos_ac, rank_a) if self.vbar_g is None: self.vbar_g = self.finegd.empty() self.vbar_g[:] = 0.0 self.vbar.add(self.vbar_g) def update_pseudo_potential(self, dens): self.timer.start('vbar') e_zero = self.finegd.integrate(self.vbar_g, dens.nt_g, global_integral=False) vt_g = self.vt_sg[0] vt_g[:] = self.vbar_g self.timer.stop('vbar') e_external = 0.0 if self.vext is not None: vext_g = self.vext.get_potential(self.finegd) vt_g += vext_g e_external = self.finegd.integrate(vext_g, dens.rhot_g, global_integral=False) if self.nspins == 2: self.vt_sg[1] = vt_g self.timer.start('XC 3D grid') e_xc = self.xc.calculate(self.finegd, dens.nt_sg, self.vt_sg) e_xc /= self.finegd.comm.size self.timer.stop('XC 3D grid') self.timer.start('Poisson') # npoisson is the number of iterations: self.npoisson = self.poisson.solve(self.vHt_g, dens.rhot_g, charge=-dens.charge) self.timer.stop('Poisson') self.timer.start('Hartree integrate/restrict') e_coulomb = 0.5 * self.finegd.integrate( self.vHt_g, dens.rhot_g, global_integral=False) for vt_g in self.vt_sg: vt_g += self.vHt_g self.timer.stop('Hartree integrate/restrict') return np.array([e_coulomb, e_zero, e_external, e_xc]) def calculate_kinetic_energy(self, density): # XXX new timer item for kinetic energy? self.timer.start('Hartree integrate/restrict') self.restrict_and_collect(self.vt_sg, self.vt_sG) e_kinetic = 0.0 s = 0 for vt_G, nt_G in zip(self.vt_sG, density.nt_sG): if self.ref_vt_sG is not None: vt_G += self.ref_vt_sG[s] if s < self.nspins: e_kinetic -= self.gd.integrate(vt_G, nt_G - density.nct_G, global_integral=False) else: e_kinetic -= self.gd.integrate(vt_G, nt_G, global_integral=False) s += 1 self.timer.stop('Hartree integrate/restrict') return e_kinetic def calculate_atomic_hamiltonians(self, dens): def getshape(a): return sum(2 * l + 1 for l, _ in enumerate(self.setups[a].ghat_l)), W_aL = ArrayDict(self.atomdist.aux_partition, getshape, float) if self.vext: vext_g = self.vext.get_potential(self.finegd) dens.ghat.integrate(self.vHt_g + vext_g, W_aL) else: dens.ghat.integrate(self.vHt_g, W_aL) return self.atomdist.to_work(self.atomdist.from_aux(W_aL)) def calculate_forces2(self, dens, ghat_aLv, nct_av, vbar_av): if self.nspins == 2: vt_G = self.vt_sG.mean(0) else: vt_G = self.vt_sG[0] self.vbar.derivative(dens.nt_g, vbar_av) if self.vext: vext_g = self.vext.get_potential(self.finegd) dens.ghat.derivative(self.vHt_g + vext_g, ghat_aLv) else: dens.ghat.derivative(self.vHt_g, ghat_aLv) dens.nct.derivative(vt_G, nct_av) def get_electrostatic_potential(self, dens): self.update(dens) v_g = self.finegd.collect(self.vHt_g, broadcast=True) v_g = self.finegd.zero_pad(v_g) if hasattr(self.poisson, 'correction'): assert self.poisson.c == 2 v_g[:, :, 0] = self.poisson.correction return v_g