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 initialize(self, density, hamiltonian, wfs, occupations): assert wfs.kd.gamma self.xc.initialize(density, hamiltonian, wfs, occupations) self.kpt_comm = wfs.kd.comm self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.exx_s = np.zeros(self.nspins) self.ekin_s = np.zeros(self.nspins) self.nocc_s = np.empty(self.nspins, int) if self.finegrid: self.poissonsolver = hamiltonian.poisson self.ghat = density.ghat self.interpolator = density.interpolator self.restrictor = hamiltonian.restrictor else: self.poissonsolver = PoissonSolver(eps=1e-11) self.poissonsolver.set_grid_descriptor(density.gd) self.poissonsolver.initialize() self.ghat = LFC(density.gd, [setup.ghat_l for setup in density.setups], integral=np.sqrt(4 * np.pi), forces=True) self.gd = density.gd self.finegd = self.ghat.gd
def initialize(self, density, hamiltonian, wfs, occ=None): assert wfs.kd.gamma assert not wfs.gd.pbc_c.any() self.wfs = wfs self.dtype = float self.xc.initialize(density, hamiltonian, wfs, occ) self.kpt_comm = wfs.kd.comm self.nspins = wfs.nspins self.nbands = wfs.bd.nbands if self.finegrid: self.finegd = density.finegd self.ghat = density.ghat else: self.finegd = density.gd self.ghat = LFC(self.finegd, [setup.ghat_l for setup in density.setups], integral=sqrt(4 * pi), forces=True) poissonsolver = PoissonSolver(eps=1e-14) poissonsolver.set_grid_descriptor(self.finegd) poissonsolver.initialize() self.spin_s = {} for kpt in wfs.kpt_u: self.spin_s[kpt.s] = SICSpin(kpt, self.xc, density, hamiltonian, wfs, poissonsolver, self.ghat, self.finegd, **self.parameters)
def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd if self.bd.comm.size > 1: raise ValueError('Band parallelization not supported by hybridk') self.wfs = wfs self.world = wfs.world self.fd = logfile(self.fd, self.world.rank) N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol**(2 / 3.0) / pi**2 if self.ecut is None: self.ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() * 0.9999 self.bzq_qc = self.kd.get_bz_q_points() qd = KPointDescriptor(self.bzq_qc) q0 = self.kd.where_is_q(np.zeros(3), self.bzq_qc) self.pwd = PWDescriptor(self.ecut, self.gd, complex, kd=qd) G2_qG = self.pwd.G2_qG G2_qG[q0][0] = 117.0 self.iG2_qG = [1.0 / G2_G for G2_G in G2_qG] G2_qG[q0][0] = 0.0 self.iG2_qG[q0][0] = 0.0 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) for q in range(self.kd.nbzkpts): self.gamma -= np.dot(np.exp(-self.alpha * G2_qG[q]), self.iG2_qG[q]) self.iG2_qG[q0][0] = self.gamma self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], qd, dtype=complex) self.log('Value of alpha parameter:', self.alpha) self.log('Value of gamma parameter:', self.gamma) self.log('Cutoff energy:', self.ecut, 'Hartree') self.log('%d x %d x %d k-points' % tuple(self.kd.N_c))
def setUp(self): UTBandParallelSetup.setUp(self) for virtvar in ['dtype', 'blocking', 'async']: assert getattr(self, virtvar) is not None, 'Virtual "%s"!' % virtvar # Create randomized atoms self.atoms = create_random_atoms(self.gd) # Do we agree on the atomic positions? pos_ac = self.atoms.get_positions() pos_rac = np.empty((world.size, ) + pos_ac.shape, pos_ac.dtype) world.all_gather(pos_ac, pos_rac) if (pos_rac - pos_rac[world.rank, ...][np.newaxis, ...]).any(): raise RuntimeError('Discrepancy in atomic positions detected.') # Create setups for atoms self.Z_a = self.atoms.get_atomic_numbers() self.setups = Setups(self.Z_a, p.setups, p.basis, p.lmax, xc) # Create atomic projector overlaps spos_ac = self.atoms.get_scaled_positions() % 1.0 self.rank_a = self.gd.get_ranks_from_positions(spos_ac) self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups], self.kpt_comm, dtype=self.dtype) self.pt.set_positions(spos_ac) if memstats: # Hack to scramble heap usage into steady-state level HEAPSIZE = 25 * 1024**2 for i in range(100): data = np.empty(np.random.uniform(0, HEAPSIZE // 8), float) del data self.mem_pre = record_memory() self.mem_alloc = None self.mem_test = None # Stuff for pseudo wave functions and projections if self.dtype == complex: self.gamma = 1j**(1.0 / self.nbands) else: self.gamma = 1.0 self.psit_nG = None self.P_ani = None self.Qeff_a = None self.Qtotal = None self.allocate()
def initialize(self, density, hamiltonian, wfs, occupations): assert wfs.kd.gamma self.xc.initialize(density, hamiltonian, wfs, occupations) self.kpt_comm = wfs.kd.comm self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.exx_s = np.zeros(self.nspins) self.ekin_s = np.zeros(self.nspins) self.nocc_s = np.empty(self.nspins, int) self.gd = density.gd self.redistributor = density.redistributor use_charge_center = hamiltonian.poisson.use_charge_center # XXX How do we construct a copy of the Poisson solver of the # Hamiltonian? We don't know what class it is, etc., but gd # may differ. # XXX One might consider using a charged centered compensation # charge for the PoissonSolver in the case of EXX as standard self.poissonsolver = PoissonSolver('fd', eps=1e-11, use_charge_center=use_charge_center) # self.poissonsolver = hamiltonian.poisson if self.finegrid: self.finegd = self.gd.refine() # XXX Taking restrictor from Hamiltonian will not work in PW mode, # will it? I think this supports only real-space mode. # self.restrictor = hamiltonian.restrictor self.restrictor = Transformer(self.finegd, self.gd, 3) self.interpolator = Transformer(self.gd, self.finegd, 3) else: self.finegd = self.gd self.ghat = LFC(self.finegd, [setup.ghat_l for setup in density.setups], integral=np.sqrt(4 * np.pi), forces=True) self.poissonsolver.set_grid_descriptor(self.finegd) if self.rsf == 'Yukawa': omega2 = self.omega**2 self.screened_poissonsolver = HelmholtzSolver( k2=-omega2, eps=1e-11, nn=3, use_charge_center=use_charge_center) self.screened_poissonsolver.set_grid_descriptor(self.finegd)
def initialize(self, density, hamiltonian, wfs, occupations): assert wfs.gamma self.xc.initialize(density, hamiltonian, wfs, occupations) self.kpt_comm = wfs.kpt_comm self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.exx_s = np.zeros(self.nspins) self.ekin_s = np.zeros(self.nspins) self.nocc_s = np.empty(self.nspins, int) if self.finegrid: self.poissonsolver = hamiltonian.poisson self.ghat = density.ghat self.interpolator = density.interpolator self.restrictor = hamiltonian.restrictor else: self.poissonsolver = PoissonSolver(eps=1e-11) self.poissonsolver.set_grid_descriptor(density.gd) self.poissonsolver.initialize() self.ghat = LFC(density.gd, [setup.ghat_l for setup in density.setups], integral=np.sqrt(4 * np.pi), forces=True) self.gd = density.gd self.finegd = self.ghat.gd
def with_compensation_charges(self, finegrid=False): """Get pair densisty including the compensation charges""" rhot = self.get(finegrid) # Determine the compensation charges for each nucleus Q_aL = {} for a, P_ni in self.P_ani.items(): # Generate density matrix Pi_i = P_ni[self.i] Pj_i = P_ni[self.j] D_ii = np.outer(Pi_i, Pj_i) # allowed to pack as used in the scalar product with # the symmetric array Delta_pL D_p = pack(D_ii) # Determine compensation charge coefficients: Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) # Add compensation charges if finegrid: self.density.ghat.add(rhot, Q_aL) else: if not hasattr(self.density, 'Ghat'): self.density.Ghat = LFC( self.density.gd, [setup.ghat_l for setup in self.setups], integral=sqrt(4 * pi)) self.density.Ghat.set_positions(self.spos_ac) self.density.Ghat.add(rhot, Q_aL) return rhot
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 initialize(self, density, hamiltonian, wfs, occ=None): assert wfs.gamma assert not wfs.gd.pbc_c.any() self.wfs = wfs self.dtype = float self.xc.initialize(density, hamiltonian, wfs, occ) self.kpt_comm = wfs.kpt_comm self.nspins = wfs.nspins self.nbands = wfs.bd.nbands if self.finegrid: self.finegd = density.finegd self.ghat = density.ghat else: self.finegd = density.gd self.ghat = LFC(self.finegd, [setup.ghat_l for setup in density.setups], integral=sqrt(4 * pi), forces=True) poissonsolver = PoissonSolver(eps=1e-14) poissonsolver.set_grid_descriptor(self.finegd) poissonsolver.initialize() self.spin_s = {} for kpt in wfs.kpt_u: self.spin_s[kpt.s] = SICSpin(kpt, self.xc, density, hamiltonian, wfs, poissonsolver, self.ghat, self.finegd, **self.parameters)
def get_grid_dP_aMix(spos_ac, wfs, q, timer=nulltimer): # XXXXXX q nao = wfs.setups.nao C_MM = np.identity(nao, dtype=wfs.dtype) # XXX In the future use the New Two-Center integrals # to evaluate this dP_aMix = {} for a, setup in enumerate(wfs.setups): ni = 0 dP_Mix = np.zeros((nao, setup.ni, 3)) pt = LFC(wfs.gd, [setup.pt_j], wfs.kd.comm, dtype=wfs.dtype, forces=True) spos1_ac = [spos_ac[a]] pt.set_k_points(wfs.ibzk_qc) pt.set_positions(spos1_ac) for b, setup_b in enumerate(wfs.setups): nao = setup_b.nao phi_MG = wfs.gd.zeros(nao, wfs.dtype) phi_MG = wfs.gd.collect(phi_MG, broadcast=False) wfs.basis_functions.lcao_to_grid(C_MM[ni:ni + nao], phi_MG, q) dP_bMix = pt.dict(len(phi_MG), derivative=True) pt.derivative(phi_MG, dP_bMix, q=q) dP_Mix[ni:ni + nao] = dP_bMix[0] ni += nao timer.write_now('projector grad. doing atoms (%s, %s) ' % (a, b)) dP_aMix[a] = dP_Mix return dP_aMix
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 initialize(self, setups, timer, magmom_av, hund): Density.initialize(self, setups, timer, magmom_av, hund) # Interpolation function for the density: self.interpolator = Transformer(self.gd, self.finegd, self.stencil) spline_aj = [] for setup in setups: if setup.nct is None: spline_aj.append([]) else: spline_aj.append([setup.nct]) self.nct = LFC(self.gd, spline_aj, integral=[setup.Nct for setup in setups], forces=True, cut=True) self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups], integral=sqrt(4 * pi), forces=True)
def __init__(self, gd, finegd, nspins, setups, stencil, timer, xc, psolver, vext_g): """Create the Hamiltonian.""" self.gd = gd self.finegd = finegd self.nspins = nspins self.setups = setups self.timer = timer self.xc = xc # Solver for the Poisson equation: if psolver is None: psolver = PoissonSolver(nn=3, relax='J') self.poisson = psolver self.poisson.set_grid_descriptor(finegd) self.dH_asp = None # The external potential self.vext_g = vext_g self.vt_sG = None self.vHt_g = None self.vt_sg = None self.vbar_g = None self.rank_a = None # Restrictor function for the potential: self.restrictor = Transformer(self.finegd, self.gd, stencil, allocate=False) self.restrict = self.restrictor.apply self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups], forces=True) self.Ekin0 = None self.Ekin = None self.Epot = None self.Ebar = None self.Eext = None self.Exc = None self.Etot = None self.S = None self.allocated = False
def set_setups(self, setups): self.pd = PWDescriptor(self.ecut, self.gd, self.kd.ibzk_qc) pt = LFC(self.gd, [setup.pt_j for setup in setups], self.kpt_comm, dtype=self.dtype, forces=True) self.pt = PWLFC(pt, self.pd) FDPWWaveFunctions.set_setups(self, setups)
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 ibz2bz(self, atoms): """Transform wave functions in IBZ to the full BZ.""" assert self.kd.comm.size == 1 # New k-point descriptor for full BZ: kd = KPointDescriptor(self.kd.bzk_kc, nspins=self.nspins) #kd.set_symmetry(atoms, self.setups, enabled=False) kd.set_communicator(serial_comm) self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups], kd, dtype=self.dtype) self.pt.set_positions(atoms.get_scaled_positions()) self.initialize_wave_functions_from_restart_file() weight = 2.0 / kd.nspins / kd.nbzkpts # Build new list of k-points: kpt_u = [] for s in range(self.nspins): for k in range(kd.nbzkpts): # Index of symmetry related point in the IBZ ik = self.kd.bz2ibz_k[k] r, u = self.kd.get_rank_and_index(s, ik) assert r == 0 kpt = self.kpt_u[u] phase_cd = np.exp(2j * np.pi * self.gd.sdisp_cd * kd.bzk_kc[k, :, np.newaxis]) # New k-point: kpt2 = KPoint(weight, s, k, k, phase_cd) kpt2.f_n = kpt.f_n / kpt.weight / kd.nbzkpts * 2 / self.nspins kpt2.eps_n = kpt.eps_n.copy() # Transform wave functions using symmetry operation: Psit_nG = self.gd.collect(kpt.psit_nG) if Psit_nG is not None: Psit_nG = Psit_nG.copy() for Psit_G in Psit_nG: Psit_G[:] = self.kd.transform_wave_function(Psit_G, k) kpt2.psit_nG = self.gd.empty(self.bd.nbands, dtype=self.dtype) self.gd.distribute(Psit_nG, kpt2.psit_nG) # Calculate PAW projections: kpt2.P_ani = self.pt.dict(len(kpt.psit_nG)) self.pt.integrate(kpt2.psit_nG, kpt2.P_ani, k) kpt_u.append(kpt2) self.kd = kd self.kpt_u = kpt_u
def __init__(self, density, atoms, finegrid): """Initialization needs a paw instance, and whether the compensated pair density should be on the fine grid (boolean)""" self.density = density self.finegrid = finegrid if not finegrid: density.Ghat = LFC(density.gd, [setup.ghat_l for setup in density.setups], integral=sqrt(4 * pi)) density.Ghat.set_positions(atoms.get_scaled_positions() % 1.0)
def add_potential_correction(self, v_R, alpha): dens = self.calc.density dens.D_asp.redistribute(dens.atom_partition.as_serial()) dens.Q_aL.redistribute(dens.atom_partition.as_serial()) dv_a1 = [] for a, D_sp in dens.D_asp.items(): setup = dens.setups[a] c = setup.xc_correction rgd = c.rgd ghat_g = gauss(rgd, 1 / setup.rcgauss**2) Z_g = gauss(rgd, alpha) * setup.Z D_q = np.dot(D_sp.sum(0), c.B_pqL[:, :, 0]) dn_g = np.dot(D_q, (c.n_qg - c.nt_qg)) * sqrt(4 * pi) dn_g += 4 * pi * (c.nc_g - c.nct_g) dn_g -= Z_g dn_g -= dens.Q_aL[a][0] * ghat_g * sqrt(4 * pi) dv_g = rgd.poisson(dn_g) / sqrt(4 * pi) dv_g[1:] /= rgd.r_g[1:] dv_g[0] = dv_g[1] dv_g[-1] = 0.0 dv_a1.append([rgd.spline(dv_g, points=POINTS)]) dens.D_asp.redistribute(dens.atom_partition) dens.Q_aL.redistribute(dens.atom_partition) if dv_a1: dv = LFC(self.gd, dv_a1) dv.set_positions(self.calc.spos_ac) dv.add(v_R) dens.gd.comm.broadcast(v_R, 0)
def _initialize_corrections(self): if self.dphi is not None: return splines = {} dphi_aj = [] for setup in self.calc.wfs.setups: dphi_j = splines.get(setup) if dphi_j is None: rcut = max(setup.rcut_j) * 1.1 gcut = setup.rgd.ceil(rcut) dphi_j = [] for l, phi_g, phit_g in zip(setup.l_j, setup.data.phi_jg, setup.data.phit_jg): dphi_g = (phi_g - phit_g)[:gcut] dphi_j.append(setup.rgd.spline(dphi_g, rcut, l, points=200)) dphi_aj.append(dphi_j) self.dphi = LFC(self.gd, dphi_aj, kd=self.calc.wfs.kd.copy(), dtype=self.calc.wfs.dtype) self.dphi.set_positions(self.calc.atoms.get_scaled_positions())
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 initialize(self, density, hamiltonian, wfs, occupations): assert wfs.kd.gamma self.xc.initialize(density, hamiltonian, wfs, occupations) self.kpt_comm = wfs.kd.comm self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.exx_s = np.zeros(self.nspins) self.ekin_s = np.zeros(self.nspins) self.nocc_s = np.empty(self.nspins, int) self.gd = density.gd self.redistributor = density.redistributor # XXX How do we construct a copy of the Poisson solver of the # Hamiltonian? We don't know what class it is, etc., but gd # may differ. self.poissonsolver = PoissonSolver(eps=1e-11) #self.poissonsolver = hamiltonian.poisson if self.finegrid: self.finegd = self.gd.refine() # XXX Taking restrictor from Hamiltonian will not work in PW mode, # will it? I think this supports only real-space mode. #self.restrictor = hamiltonian.restrictor self.restrictor = Transformer(self.finegd, self.gd, 3) self.interpolator = Transformer(self.gd, self.finegd, 3) else: self.finegd = self.gd self.ghat = LFC(self.finegd, [setup.ghat_l for setup in density.setups], integral=np.sqrt(4 * np.pi), forces=True) self.poissonsolver.set_grid_descriptor(self.finegd) self.poissonsolver.initialize()
def setUp(self): UTBandParallelSetup.setUp(self) for virtvar in ['dtype','blocking','async']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Create randomized atoms self.atoms = create_random_atoms(self.gd) # Do we agree on the atomic positions? pos_ac = self.atoms.get_positions() pos_rac = np.empty((world.size,)+pos_ac.shape, pos_ac.dtype) world.all_gather(pos_ac, pos_rac) if (pos_rac-pos_rac[world.rank,...][np.newaxis,...]).any(): raise RuntimeError('Discrepancy in atomic positions detected.') # Create setups for atoms self.Z_a = self.atoms.get_atomic_numbers() self.setups = Setups(self.Z_a, p.setups, p.basis, p.lmax, xc) # Create atomic projector overlaps spos_ac = self.atoms.get_scaled_positions() % 1.0 self.rank_a = self.gd.get_ranks_from_positions(spos_ac) self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups], dtype=self.dtype) self.pt.set_positions(spos_ac) if memstats: # Hack to scramble heap usage into steady-state level HEAPSIZE = 25 * 1024**2 for i in range(100): data = np.empty(np.random.uniform(0, HEAPSIZE // 8), float) del data self.mem_pre = record_memory() self.mem_alloc = None self.mem_test = None # Stuff for pseudo wave functions and projections if self.dtype == complex: self.gamma = 1j**(1.0/self.nbands) else: self.gamma = 1.0 self.psit_nG = None self.P_ani = None self.Qeff_a = None self.Qtotal = None self.allocate()
def initialize(self, paw): if not paw.initialized: raise RuntimeError('PAW instance is not initialized') paw.converge_wave_functions() self.tauct = LFC(self.gd, [[setup.tauct] for setup in self.density.setups], forces=True, cut=True) self.tauct.set_positions(paw.spos_ac) self.taut_sg = None self.nt_grad2_sG = self.gd.empty(self.nspins) self.nt_grad2_sg = None
def initialize(self, setups, stencil, timer, magmom_a, hund): self.timer = timer self.setups = setups self.hund = hund self.magmom_a = magmom_a # Interpolation function for the density: self.interpolator = Transformer(self.gd, self.finegd, stencil, allocate=False) spline_aj = [] for setup in setups: if setup.nct is None: spline_aj.append([]) else: spline_aj.append([setup.nct]) self.nct = LFC(self.gd, spline_aj, integral=[setup.Nct for setup in setups], forces=True, cut=True) self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups], integral=sqrt(4 * pi), forces=True) if self.allocated: self.allocated = False self.allocate()
def __init__(self, gd, spline_j, spos_c, index=None): rcut = max([spline.get_cutoff() for spline in spline_j]) corner_c = np.ceil(spos_c * gd.N_c - rcut / gd.h_c).astype(int) size_c = np.ceil(spos_c * gd.N_c + rcut / gd.h_c).astype(int) - corner_c smallgd = GridDescriptor(N_c=size_c + 1, cell_cv=gd.h_c * (size_c + 1), pbc_c=False, comm=mpi.serial_comm) lfc = LFC(smallgd, [spline_j]) lfc.set_positions((spos_c[np.newaxis, :] * gd.N_c - corner_c + 1) / smallgd.N_c) ni = lfc.Mmax f_iG = smallgd.zeros(ni) lfc.add(f_iG, {0: np.eye(ni)}) LocalizedFunctions.__init__(self, gd, f_iG, corner_c, index=index)
def get_grid2_dP_aMix(spos_ac, wfs, q, *args, **kwargs): # XXXXXX q nao = wfs.setups.nao C_MM = np.identity(nao, dtype=wfs.dtype) bfs = wfs.basis_functions phi_MG = wfs.gd.zeros(nao, wfs.dtype) bfs.lcao_to_grid(C_MM, phi_MG, q) setups = wfs.setups pt = LFC(wfs.gd, [setup.pt_j for setup in setups], wfs.kd.comm, dtype=wfs.dtype, forces=True) pt.set_k_points(wfs.ibzk_qc) pt.set_positions(spos_ac) dP_aMix = pt.dict(len(phi_MG), derivative=True) pt.derivative(phi_MG, dP_aMix, q=q) return dP_aMix
def initialize(self, paw): if not paw.initialized: raise RuntimeError('PAW instance is not initialized') self.tauct = LFC(self.gd, [[setup.tauct] for setup in self.density.setups], forces=True, cut=True) spos_ac = paw.atoms.get_scaled_positions() % 1.0 self.tauct.set_positions(spos_ac) self.taut_sG = self.gd.empty(self.nspins) self.taut_sg = None self.nt_grad2_sG = self.gd.empty(self.nspins) self.nt_grad2_sg = None
def __init__(self, gd, setups, spos_ac, fft=False): assert gd.comm.size == 1 self.rhot1_G = gd.empty() self.rhot2_G = gd.empty() self.pot_G = gd.empty() self.dv = gd.dv if fft: self.poisson = FFTPoissonSolver() else: self.poisson = PoissonSolver(name='fd', nn=3) self.poisson.set_grid_descriptor(gd) self.setups = setups # Set coarse ghat self.Ghat = LFC(gd, [setup.ghat_l for setup in setups], integral=sqrt(4 * pi)) self.Ghat.set_positions(spos_ac)
def f(n, p): N = 2 * n gd = GridDescriptor((N, N, N), (L, L, L)) a = gd.zeros() print(a.shape) #p = PoissonSolver(nn=1, relax=relax) p.set_grid_descriptor(gd) cut = N / 2.0 * 0.9 s = Spline(l=0, rmax=cut, f_g=np.array([1, 0.5, 0.0])) c = LFC(gd, [[s], [s]]) c.set_positions([(0, 0, 0), (0.5, 0.5, 0.5)]) c.add(a) I0 = gd.integrate(a) a -= I0 / L**3 b = gd.zeros() p.solve(b, a, charge=0, eps=1e-20) return gd.collect(b, broadcast=1)
def get_all_electron_density(self, atoms, gridrefinement=2): """Return real all-electron density array.""" # Refinement of coarse grid, for representation of the AE-density if gridrefinement == 1: gd = self.gd n_sg = self.nt_sG.copy() elif gridrefinement == 2: gd = self.finegd if self.nt_sg is None: self.interpolate() n_sg = self.nt_sg.copy() elif gridrefinement == 4: # Extra fine grid gd = self.finegd.refine() # Interpolation function for the density: interpolator = Transformer(self.finegd, gd, 3) # Transfer the pseudo-density to the fine grid: n_sg = gd.empty(self.nspins) if self.nt_sg is None: self.interpolate() for s in range(self.nspins): interpolator.apply(self.nt_sg[s], n_sg[s]) else: raise NotImplementedError # Add corrections to pseudo-density to get the AE-density splines = {} phi_aj = [] phit_aj = [] nc_a = [] nct_a = [] for a, id in enumerate(self.setups.id_a): if id in splines: phi_j, phit_j, nc, nct = splines[id] else: # Load splines: phi_j, phit_j, nc, nct = self.setups[a].get_partial_waves()[:4] splines[id] = (phi_j, phit_j, nc, nct) phi_aj.append(phi_j) phit_aj.append(phit_j) nc_a.append([nc]) nct_a.append([nct]) # Create localized functions from splines phi = LFC(gd, phi_aj) phit = LFC(gd, phit_aj) nc = LFC(gd, nc_a) nct = LFC(gd, nct_a) spos_ac = atoms.get_scaled_positions() % 1.0 phi.set_positions(spos_ac) phit.set_positions(spos_ac) nc.set_positions(spos_ac) nct.set_positions(spos_ac) all_D_asp = [] for a, setup in enumerate(self.setups): D_sp = self.D_asp.get(a) if D_sp is None: ni = setup.ni D_sp = np.empty((self.nspins, ni * (ni + 1) // 2)) if gd.comm.size > 1: gd.comm.broadcast(D_sp, self.rank_a[a]) all_D_asp.append(D_sp) for s in range(self.nspins): I_a = np.zeros(len(atoms)) nc.add1(n_sg[s], 1.0 / self.nspins, I_a) nct.add1(n_sg[s], -1.0 / self.nspins, I_a) phi.add2(n_sg[s], all_D_asp, s, 1.0, I_a) phit.add2(n_sg[s], all_D_asp, s, -1.0, I_a) for a, D_sp in self.D_asp.items(): setup = self.setups[a] I_a[a] -= ((setup.Nc - setup.Nct) / self.nspins + sqrt(4 * pi) * np.dot(D_sp[s], setup.Delta_pL[:, 0])) gd.comm.sum(I_a) N_c = gd.N_c g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c for I, g_c in zip(I_a, g_ac): if (g_c >= 0).all() and (g_c < gd.n_c).all(): n_sg[s][tuple(g_c)] -= I / gd.dv return n_sg, gd
class Density: """Density object. Attributes: =============== ===================================================== ``gd`` Grid descriptor for coarse grids. ``finegd`` Grid descriptor for fine grids. ``interpolate`` Function for interpolating the electron density. ``mixer`` ``DensityMixer`` object. =============== ===================================================== Soft and smooth pseudo functions on uniform 3D grids: ========== ========================================= ``nt_sG`` Electron density on the coarse grid. ``nt_sg`` Electron density on the fine grid. ``nt_g`` Electron density on the fine grid. ``rhot_g`` Charge density on the fine grid. ``nct_G`` Core electron-density on the coarse grid. ========== ========================================= """ def __init__(self, gd, finegd, nspins, charge): """Create the Density object.""" self.gd = gd self.finegd = finegd self.nspins = nspins self.charge = float(charge) self.charge_eps = 1e-7 self.D_asp = None self.Q_aL = None self.nct_G = None self.nt_sG = None self.rhot_g = None self.nt_sg = None self.nt_g = None self.rank_a = None self.mixer = BaseMixer() self.timer = nulltimer self.allocated = False def initialize(self, setups, stencil, timer, magmom_a, hund): self.timer = timer self.setups = setups self.hund = hund self.magmom_a = magmom_a # Interpolation function for the density: self.interpolator = Transformer(self.gd, self.finegd, stencil, allocate=False) spline_aj = [] for setup in setups: if setup.nct is None: spline_aj.append([]) else: spline_aj.append([setup.nct]) self.nct = LFC(self.gd, spline_aj, integral=[setup.Nct for setup in setups], forces=True, cut=True) self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups], integral=sqrt(4 * pi), forces=True) if self.allocated: self.allocated = False self.allocate() def allocate(self): assert not self.allocated self.interpolator.allocate() self.allocated = True def reset(self): # TODO: reset other parameters? self.nt_sG = None def set_positions(self, spos_ac, rank_a=None): if not self.allocated: self.allocate() self.nct.set_positions(spos_ac) self.ghat.set_positions(spos_ac) self.mixer.reset() self.nct_G = self.gd.zeros() self.nct.add(self.nct_G, 1.0 / self.nspins) #self.nt_sG = None self.nt_sg = None self.nt_g = None self.rhot_g = None self.Q_aL = None # If both old and new atomic ranks are present, start a blank dict if # it previously didn't exist but it will needed for the new atoms. if (self.rank_a is not None and rank_a is not None and self.D_asp is None and (rank_a == self.gd.comm.rank).any()): self.D_asp = {} if self.rank_a is not None and self.D_asp is not None: self.timer.start('Redistribute') requests = [] flags = (self.rank_a != rank_a) my_incoming_atom_indices = np.argwhere(np.bitwise_and(flags, \ rank_a == self.gd.comm.rank)).ravel() my_outgoing_atom_indices = np.argwhere(np.bitwise_and(flags, \ self.rank_a == self.gd.comm.rank)).ravel() for a in my_incoming_atom_indices: # Get matrix from old domain: ni = self.setups[a].ni D_sp = np.empty((self.nspins, ni * (ni + 1) // 2)) requests.append(self.gd.comm.receive(D_sp, self.rank_a[a], tag=a, block=False)) assert a not in self.D_asp self.D_asp[a] = D_sp for a in my_outgoing_atom_indices: # Send matrix to new domain: D_sp = self.D_asp.pop(a) requests.append(self.gd.comm.send(D_sp, rank_a[a], tag=a, block=False)) self.gd.comm.waitall(requests) self.timer.stop('Redistribute') self.rank_a = rank_a def calculate_pseudo_density(self, wfs): """Calculate nt_sG from scratch. nt_sG will be equal to nct_G plus the contribution from wfs.add_to_density(). """ wfs.calculate_density_contribution(self.nt_sG) self.nt_sG += self.nct_G def update(self, wfs): self.timer.start('Density') self.timer.start('Pseudo density') self.calculate_pseudo_density(wfs) self.timer.stop('Pseudo density') self.timer.start('Atomic density matrices') wfs.calculate_atomic_density_matrices(self.D_asp) self.timer.stop('Atomic density matrices') self.timer.start('Multipole moments') comp_charge = self.calculate_multipole_moments() self.timer.stop('Multipole moments') if isinstance(wfs, LCAOWaveFunctions): self.timer.start('Normalize') self.normalize(comp_charge) self.timer.stop('Normalize') self.timer.start('Mix') self.mix(comp_charge) self.timer.stop('Mix') self.timer.stop('Density') def normalize(self, comp_charge=None): """Normalize pseudo density.""" if comp_charge is None: comp_charge = self.calculate_multipole_moments() pseudo_charge = self.gd.integrate(self.nt_sG).sum() if pseudo_charge + self.charge + comp_charge != 0: if pseudo_charge != 0: x = -(self.charge + comp_charge) / pseudo_charge self.nt_sG *= x else: # Use homogeneous background: self.nt_sG[:] = (self.charge + comp_charge) * self.gd.dv def calculate_pseudo_charge(self, comp_charge): self.nt_g = self.nt_sg.sum(axis=0) self.rhot_g = self.nt_g.copy() self.ghat.add(self.rhot_g, self.Q_aL) if debug: charge = self.finegd.integrate(self.rhot_g) + self.charge if abs(charge) > self.charge_eps: raise RuntimeError('Charge not conserved: excess=%.9f' % charge) def mix(self, comp_charge): if not self.mixer.mix_rho: self.mixer.mix(self) comp_charge = None self.interpolate(comp_charge) self.calculate_pseudo_charge(comp_charge) if self.mixer.mix_rho: self.mixer.mix(self) def interpolate(self, comp_charge=None): """Interpolate pseudo density to fine grid.""" if comp_charge is None: comp_charge = self.calculate_multipole_moments() if self.nt_sg is None: self.nt_sg = self.finegd.empty(self.nspins) for s in range(self.nspins): self.interpolator.apply(self.nt_sG[s], self.nt_sg[s]) # With periodic boundary conditions, the interpolation will # conserve the number of electrons. if not self.gd.pbc_c.all(): # With zero-boundary conditions in one or more directions, # this is not the case. pseudo_charge = -(self.charge + comp_charge) if abs(pseudo_charge) > 1.0e-14: x = pseudo_charge / self.finegd.integrate(self.nt_sg).sum() self.nt_sg *= x def calculate_multipole_moments(self): """Calculate multipole moments of compensation charges. Returns the total compensation charge in units of electron charge, so the number will be negative because of the dominating contribution from the nuclear charge.""" comp_charge = 0.0 self.Q_aL = {} for a, D_sp in self.D_asp.items(): Q_L = self.Q_aL[a] = np.dot(D_sp.sum(0), self.setups[a].Delta_pL) Q_L[0] += self.setups[a].Delta0 comp_charge += Q_L[0] return self.gd.comm.sum(comp_charge) * sqrt(4 * pi) def initialize_from_atomic_densities(self, basis_functions): """Initialize D_asp, nt_sG and Q_aL from atomic densities. nt_sG is initialized from atomic orbitals, and will be constructed with the specified magnetic moments and obeying Hund's rules if ``hund`` is true.""" # XXX does this work with blacs? What should be distributed? # Apparently this doesn't use blacs at all, so it's serial # with respect to the blacs distribution. That means it works # but is not particularly efficient (not that this is a time # consuming step) f_sM = np.empty((self.nspins, basis_functions.Mmax)) self.D_asp = {} f_asi = {} for a in basis_functions.atom_indices: c = self.charge / len(self.setups) # distribute on all atoms f_si = self.setups[a].calculate_initial_occupation_numbers( self.magmom_a[a], self.hund, charge=c, nspins=self.nspins) if a in basis_functions.my_atom_indices: self.D_asp[a] = self.setups[a].initialize_density_matrix(f_si) f_asi[a] = f_si self.nt_sG = self.gd.zeros(self.nspins) basis_functions.add_to_density(self.nt_sG, f_asi) self.nt_sG += self.nct_G self.calculate_normalized_charges_and_mix() def initialize_from_wavefunctions(self, wfs): """Initialize D_asp, nt_sG and Q_aL from wave functions.""" self.nt_sG = self.gd.empty(self.nspins) self.calculate_pseudo_density(wfs) self.D_asp = {} my_atom_indices = np.argwhere(wfs.rank_a == self.gd.comm.rank).ravel() for a in my_atom_indices: ni = self.setups[a].ni self.D_asp[a] = np.empty((self.nspins, ni * (ni + 1) // 2)) wfs.calculate_atomic_density_matrices(self.D_asp) self.calculate_normalized_charges_and_mix() def initialize_directly_from_arrays(self, nt_sG, D_asp): """Set D_asp and nt_sG directly.""" self.nt_sG = nt_sG self.D_asp = D_asp #self.calculate_normalized_charges_and_mix() # No calculate multipole moments? Tests will fail because of # improperly initialized mixer def calculate_normalized_charges_and_mix(self): comp_charge = self.calculate_multipole_moments() self.normalize(comp_charge) self.mix(comp_charge) def set_mixer(self, mixer): if mixer is not None: if self.nspins == 1 and isinstance(mixer, MixerSum): raise RuntimeError('Cannot use MixerSum with nspins==1') self.mixer = mixer else: if self.gd.pbc_c.any(): beta = 0.1 weight = 50.0 else: beta = 0.25 weight = 1.0 if self.nspins == 2: self.mixer = MixerSum(beta=beta, weight=weight) else: self.mixer = Mixer(beta=beta, weight=weight) self.mixer.initialize(self) def estimate_magnetic_moments(self): magmom_a = np.zeros_like(self.magmom_a) if self.nspins == 2: for a, D_sp in self.D_asp.items(): magmom_a[a] = np.dot(D_sp[0] - D_sp[1], self.setups[a].N0_p) self.gd.comm.sum(magmom_a) return magmom_a def get_correction(self, a, spin): """Integrated atomic density correction. Get the integrated correction to the pseuso density relative to the all-electron density. """ setup = self.setups[a] return sqrt(4 * pi) * ( np.dot(self.D_asp[a][spin], setup.Delta_pL[:, 0]) + setup.Delta0 / self.nspins) def get_density_array(self): XXX # XXX why not replace with get_spin_density and get_total_density? """Return pseudo-density array.""" if self.nspins == 2: return self.nt_sG else: return self.nt_sG[0] def get_all_electron_density(self, atoms, gridrefinement=2): """Return real all-electron density array.""" # Refinement of coarse grid, for representation of the AE-density if gridrefinement == 1: gd = self.gd n_sg = self.nt_sG.copy() elif gridrefinement == 2: gd = self.finegd if self.nt_sg is None: self.interpolate() n_sg = self.nt_sg.copy() elif gridrefinement == 4: # Extra fine grid gd = self.finegd.refine() # Interpolation function for the density: interpolator = Transformer(self.finegd, gd, 3) # Transfer the pseudo-density to the fine grid: n_sg = gd.empty(self.nspins) if self.nt_sg is None: self.interpolate() for s in range(self.nspins): interpolator.apply(self.nt_sg[s], n_sg[s]) else: raise NotImplementedError # Add corrections to pseudo-density to get the AE-density splines = {} phi_aj = [] phit_aj = [] nc_a = [] nct_a = [] for a, id in enumerate(self.setups.id_a): if id in splines: phi_j, phit_j, nc, nct = splines[id] else: # Load splines: phi_j, phit_j, nc, nct = self.setups[a].get_partial_waves()[:4] splines[id] = (phi_j, phit_j, nc, nct) phi_aj.append(phi_j) phit_aj.append(phit_j) nc_a.append([nc]) nct_a.append([nct]) # Create localized functions from splines phi = LFC(gd, phi_aj) phit = LFC(gd, phit_aj) nc = LFC(gd, nc_a) nct = LFC(gd, nct_a) spos_ac = atoms.get_scaled_positions() % 1.0 phi.set_positions(spos_ac) phit.set_positions(spos_ac) nc.set_positions(spos_ac) nct.set_positions(spos_ac) all_D_asp = [] for a, setup in enumerate(self.setups): D_sp = self.D_asp.get(a) if D_sp is None: ni = setup.ni D_sp = np.empty((self.nspins, ni * (ni + 1) // 2)) if gd.comm.size > 1: gd.comm.broadcast(D_sp, self.rank_a[a]) all_D_asp.append(D_sp) for s in range(self.nspins): I_a = np.zeros(len(atoms)) nc.add1(n_sg[s], 1.0 / self.nspins, I_a) nct.add1(n_sg[s], -1.0 / self.nspins, I_a) phi.add2(n_sg[s], all_D_asp, s, 1.0, I_a) phit.add2(n_sg[s], all_D_asp, s, -1.0, I_a) for a, D_sp in self.D_asp.items(): setup = self.setups[a] I_a[a] -= ((setup.Nc - setup.Nct) / self.nspins + sqrt(4 * pi) * np.dot(D_sp[s], setup.Delta_pL[:, 0])) gd.comm.sum(I_a) N_c = gd.N_c g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c for I, g_c in zip(I_a, g_ac): if (g_c >= 0).all() and (g_c < gd.n_c).all(): n_sg[s][tuple(g_c)] -= I / gd.dv return n_sg, gd def new_get_all_electron_density(self, atoms, gridrefinement=2): """Return real all-electron density array.""" # Refinement of coarse grid, for representation of the AE-density if gridrefinement == 1: gd = self.gd n_sg = self.nt_sG.copy() elif gridrefinement == 2: gd = self.finegd if self.nt_sg is None: self.interpolate() n_sg = self.nt_sg.copy() elif gridrefinement == 4: # Extra fine grid gd = self.finegd.refine() # Interpolation function for the density: interpolator = Transformer(self.finegd, gd, 3) # Transfer the pseudo-density to the fine grid: n_sg = gd.empty(self.nspins) if self.nt_sg is None: self.interpolate() for s in range(self.nspins): interpolator.apply(self.nt_sg[s], n_sg[s]) else: raise NotImplementedError # Add corrections to pseudo-density to get the AE-density splines = {} phi_aj = [] phit_aj = [] nc_a = [] nct_a = [] for a, id in enumerate(self.setups.id_a): if id in splines: phi_j, phit_j, nc, nct = splines[id] else: # Load splines: phi_j, phit_j, nc, nct = self.setups[a].get_partial_waves()[:4] splines[id] = (phi_j, phit_j, nc, nct) phi_aj.append(phi_j) phit_aj.append(phit_j) nc_a.append([nc]) nct_a.append([nct]) # Create localized functions from splines phi = BasisFunctions(gd, phi_aj) phit = BasisFunctions(gd, phit_aj) nc = LFC(gd, nc_a) nct = LFC(gd, nct_a) spos_ac = atoms.get_scaled_positions() % 1.0 phi.set_positions(spos_ac) phit.set_positions(spos_ac) nc.set_positions(spos_ac) nct.set_positions(spos_ac) I_sa = np.zeros((self.nspins, len(atoms))) a_W = np.empty(len(phi.M_W), np.int32) W = 0 for a in phi.atom_indices: nw = len(phi.sphere_a[a].M_w) a_W[W:W + nw] = a W += nw rho_MM = np.zeros((phi.Mmax, phi.Mmax)) for s, I_a in enumerate(I_sa): M1 = 0 for a, setup in enumerate(self.setups): ni = setup.ni D_sp = self.D_asp.get(a) if D_sp is None: D_sp = np.empty((self.nspins, ni * (ni + 1) // 2)) else: I_a[a] = ((setup.Nct - setup.Nc) / self.nspins - sqrt(4 * pi) * np.dot(D_sp[s], setup.Delta_pL[:, 0])) if gd.comm.size > 1: gd.comm.broadcast(D_sp, self.rank_a[a]) M2 = M1 + ni rho_MM[M1:M2, M1:M2] = unpack2(D_sp[s]) M1 = M2 phi.lfc.ae_valence_density_correction(rho_MM, n_sg[s], a_W, I_a) phit.lfc.ae_valence_density_correction(-rho_MM, n_sg[s], a_W, I_a) a_W = np.empty(len(nc.M_W), np.int32) W = 0 for a in nc.atom_indices: nw = len(nc.sphere_a[a].M_w) a_W[W:W + nw] = a W += nw scale = 1.0 / self.nspins for s, I_a in enumerate(I_sa): nc.lfc.ae_core_density_correction(scale, n_sg[s], a_W, I_a) nct.lfc.ae_core_density_correction(-scale, n_sg[s], a_W, I_a) gd.comm.sum(I_a) N_c = gd.N_c g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c for I, g_c in zip(I_a, g_ac): if (g_c >= 0).all() and (g_c < gd.n_c).all(): n_sg[s][tuple(g_c)] -= I / gd.dv return n_sg, gd if extra_parameters.get('usenewlfc', True): get_all_electron_density = new_get_all_electron_density def estimate_memory(self, mem): nspins = self.nspins nbytes = self.gd.bytecount() nfinebytes = self.finegd.bytecount() arrays = mem.subnode('Arrays') for name, size in [('nt_sG', nbytes * nspins), ('nt_sg', nfinebytes * nspins), ('nt_g', nfinebytes), ('rhot_g', nfinebytes), ('nct_G', nbytes)]: arrays.subnode(name, size) lfs = mem.subnode('Localized functions') for name, obj in [('nct', self.nct), ('ghat', self.ghat)]: obj.estimate_memory(lfs.subnode(name)) self.mixer.estimate_memory(mem.subnode('Mixer'), self.gd) # TODO # The implementation of interpolator memory use is not very # accurate; 20 MiB vs 13 MiB estimated in one example, probably # worse for parallel calculations. self.interpolator.estimate_memory(mem.subnode('Interpolator')) def get_spin_contamination(self, atoms, majority_spin=0): """Calculate the spin contamination. Spin contamination is defined as the integral over the spin density difference, where it is negative (i.e. the minority spin density is larger than the majority spin density. """ if majority_spin == 0: smaj = 0 smin = 1 else: smaj = 1 smin = 0 nt_sg, gd = self.get_all_electron_density(atoms) dt_sg = nt_sg[smin] - nt_sg[smaj] dt_sg = np.where(dt_sg > 0, dt_sg, 0.0) return gd.integrate(dt_sg)
def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: self.alpha = 6 * vol**(2 / 3.0) / pi**2 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() if self.kd.N_c is None: self.bzk_kc = np.zeros((1, 3)) dfghdfgh else: n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.pwd = PWDescriptor(ecut, self.gd, self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.pwd.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G**-1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:]**-1) n += 1 assert n == self.kd.N_c.prod() self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], dtype=complex ) self.ghat.set_k_points(self.bzk_kc) self.fullkd = KPointDescriptor(self.kd.bzk_kc, nspins=1) class S: id_a = [] def set_symmetry(self, s): pass self.fullkd.set_symmetry(Atoms(pbc=True), S(), False) self.fullkd.set_communicator(world) self.pt = LFC(self.gd, [setup.pt_j for setup in density.setups], dtype=complex) self.pt.set_k_points(self.fullkd.ibzk_kc) self.interpolator = density.interpolator
class HybridXC(XCFunctional): orbital_dependent = True def __init__(self, name, hybrid=None, xc=None, finegrid=False, alpha=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. 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.alpha = alpha self.exx = 0.0 XCFunctional.__init__(self, name) def get_setup_name(self): return 'PBE' def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: self.alpha = 6 * vol**(2 / 3.0) / pi**2 self.gamma = (vol / (2 * pi)**2 * sqrt(pi / self.alpha) * self.kd.nbzkpts) ecut = 0.5 * pi**2 / (self.gd.h_cv**2).sum(1).max() if self.kd.N_c is None: self.bzk_kc = np.zeros((1, 3)) dfghdfgh else: n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.pwd = PWDescriptor(ecut, self.gd, self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.pwd.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): #XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G**-1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:]**-1) n += 1 assert n == self.kd.N_c.prod() self.ghat = LFC(self.gd, [setup.ghat_l for setup in density.setups], dtype=complex ) self.ghat.set_k_points(self.bzk_kc) self.fullkd = KPointDescriptor(self.kd.bzk_kc, nspins=1) class S: id_a = [] def set_symmetry(self, s): pass self.fullkd.set_symmetry(Atoms(pbc=True), S(), False) self.fullkd.set_communicator(world) self.pt = LFC(self.gd, [setup.pt_j for setup in density.setups], dtype=complex) self.pt.set_k_points(self.fullkd.ibzk_kc) self.interpolator = density.interpolator def set_positions(self, spos_ac): self.ghat.set_positions(spos_ac) self.pt.set_positions(spos_ac) def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx def calculate_exx(self): """Non-selfconsistent calculation.""" kd = self.kd K = self.fullkd.nibzkpts assert self.nspins == 1 Q = K // world.size assert Q * world.size == K parallel = (world.size > self.nspins) self.exx = 0.0 self.exx_skn = np.zeros((self.nspins, K, self.bd.nbands)) kpt_u = [] for k in range(world.rank * Q, (world.rank + 1) * Q): k_c = self.fullkd.ibzk_kc[k] for k1, k1_c in enumerate(kd.bzk_kc): if abs(k1_c - k_c).max() < 1e-10: break # Index of symmetry related point in the irreducible BZ ik = kd.kibz_k[k1] kpt = self.kpt_u[ik] # KPoint from ground-state calculation phase_cd = np.exp(2j * pi * self.gd.sdisp_cd * k_c[:, np.newaxis]) kpt2 = KPoint0(kpt.weight, kpt.s, k, None, phase_cd) kpt2.psit_nG = np.empty_like(kpt.psit_nG) kpt2.f_n = kpt.f_n / kpt.weight / K * 2 for n, psit_G in enumerate(kpt2.psit_nG): psit_G[:] = kd.transform_wave_function(kpt.psit_nG[n], k1) kpt2.P_ani = self.pt.dict(len(kpt.psit_nG)) self.pt.integrate(kpt2.psit_nG, kpt2.P_ani, k) kpt_u.append(kpt2) for s in range(self.nspins): kpt1_q = [KPoint(self.fullkd, kpt) for kpt in kpt_u if kpt.s == s] kpt2_q = kpt1_q[:] if len(kpt1_q) == 0: # No s-spins on this CPU: continue # Send rank: srank = self.fullkd.get_rank_and_index(s, (kpt1_q[0].k - 1) % K)[0] # Receive rank: rrank = self.fullkd.get_rank_and_index(s, (kpt1_q[-1].k + 1) % K)[0] # Shift k-points K // 2 times: for i in range(K // 2 + 1): if i < K // 2: if parallel: kpt = kpt2_q[-1].next() kpt.start_receiving(rrank) kpt2_q[0].start_sending(srank) else: kpt = kpt2_q[0] for kpt1, kpt2 in zip(kpt1_q, kpt2_q): if 2 * i == K: self.apply(kpt1, kpt2, invert=(kpt1.k > kpt2.k)) else: self.apply(kpt1, kpt2) self.apply(kpt1, kpt2, invert=True) if i < K // 2: if parallel: kpt.wait() kpt2_q[0].wait() kpt2_q.pop(0) kpt2_q.append(kpt) self.exx = world.sum(self.exx) world.sum(self.exx_skn) self.exx += self.calculate_paw_correction() def apply(self, kpt1, kpt2, invert=False): #print world.rank,kpt1.k,kpt2.k,invert k1_c = self.fullkd.ibzk_kc[kpt1.k] k2_c = self.fullkd.ibzk_kc[kpt2.k] if invert: k2_c = -k2_c k12_c = k1_c - k2_c N_c = self.gd.N_c eikr_R = np.exp(2j * pi * np.dot(np.indices(N_c).T, k12_c / N_c).T) for q, k_c in enumerate(self.bzk_kc): if abs(k_c + k12_c).max() < 1e-9: q0 = q break for q, k_c in enumerate(self.bzk_kc): if abs(k_c - k12_c).max() < 1e-9: q00 = q break Gpk2_G = self.pwd.G2_qG[q0] if Gpk2_G[0] == 0: Gpk2_G = Gpk2_G.copy() Gpk2_G[0] = 1.0 / self.gamma N = N_c.prod() vol = self.gd.dv * N nspins = self.nspins same = (kpt1.k == kpt2.k) for n1, psit1_R in enumerate(kpt1.psit_nG): f1 = kpt1.f_n[n1] for n2, psit2_R in enumerate(kpt2.psit_nG): if same and n2 > n1: continue f2 = kpt2.f_n[n2] nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, q0, invert) nt_G = self.pwd.fft(nt_R * eikr_R) / N vt_G = nt_G.copy() vt_G *= -pi * vol / Gpk2_G e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid if same and n1 == n2: e /= 2 self.exx += e * f1 * f2 self.ekin -= 2 * e * f1 * f2 self.exx_skn[kpt1.s, kpt1.k, n1] += f2 * e self.exx_skn[kpt2.s, kpt2.k, n2] += f1 * e calculate_potential = not True if calculate_potential: vt_R = self.pwd.ifft(vt_G).conj() * eikr_R * N / vol if kpt1 is kpt2 and not invert and n1 == n2: kpt1.vt_nG[n1] = 0.5 * f1 * vt_R if invert: kpt1.Htpsit_nG[n1] += \ f2 * nspins * psit2_R.conj() * vt_R else: kpt1.Htpsit_nG[n1] += f2 * nspins * psit2_R * vt_R if kpt1 is not kpt2: if invert: kpt2.Htpsit_nG[n2] += (f1 * nspins * psit1_R.conj() * vt_R) else: kpt2.Htpsit_nG[n2] += (f1 * nspins * psit1_R * vt_R.conj()) def calculate_paw_correction(self): exx = 0 deg = 2 // self.nspins # spin degeneracy for a, D_sp in self.density.D_asp.items(): setup = self.setups[a] for D_p in D_sp: D_ii = unpack2(D_p) ni = len(D_ii) for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) exx -= self.hybrid / deg * D_ii[i1, i2] * A if setup.X_p is not None: exx -= self.hybrid * np.dot(D_p, setup.X_p) exx += self.hybrid * setup.ExxC return exx def calculate_pair_density(self, n1, n2, kpt1, kpt2, q, invert): if invert: nt_G = kpt1.psit_nG[n1].conj() * kpt2.psit_nG[n2].conj() else: nt_G = kpt1.psit_nG[n1].conj() * kpt2.psit_nG[n2] Q_aL = {} for a, P1_ni in kpt1.P_ani.items(): P1_i = P1_ni[n1] P2_i = kpt2.P_ani[a][n2] if invert: D_ii = np.outer(P1_i.conj(), P2_i.conj()) else: D_ii = np.outer(P1_i.conj(), P2_i) D_p = pack(D_ii) Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) self.ghat.add(nt_G, Q_aL, q) return nt_G
class RealSpaceDensity(Density): def __init__(self, gd, finegd, nspins, charge, collinear=True, stencil=3): Density.__init__(self, gd, finegd, nspins, charge, collinear) self.stencil = stencil def initialize(self, setups, timer, magmom_av, hund): Density.initialize(self, setups, timer, magmom_av, hund) # Interpolation function for the density: self.interpolator = Transformer(self.gd, self.finegd, self.stencil) spline_aj = [] for setup in setups: if setup.nct is None: spline_aj.append([]) else: spline_aj.append([setup.nct]) self.nct = LFC(self.gd, spline_aj, integral=[setup.Nct for setup in setups], forces=True, cut=True) self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups], integral=sqrt(4 * pi), forces=True) def set_positions(self, spos_ac, rank_a=None): Density.set_positions(self, spos_ac, rank_a) self.nct_G = self.gd.zeros() self.nct.add(self.nct_G, 1.0 / self.nspins) def interpolate_pseudo_density(self, comp_charge=None): """Interpolate pseudo density to fine grid.""" if comp_charge is None: comp_charge = self.calculate_multipole_moments() self.nt_sg = self.interpolate(self.nt_sG, self.nt_sg) # With periodic boundary conditions, the interpolation will # conserve the number of electrons. if not self.gd.pbc_c.all(): # With zero-boundary conditions in one or more directions, # this is not the case. pseudo_charge = -(self.charge + comp_charge) if abs(pseudo_charge) > 1.0e-14: x = (pseudo_charge / self.finegd.integrate(self.nt_sg[:self.nspins]).sum()) self.nt_sg *= x def interpolate(self, in_xR, out_xR=None): """Interpolate array(s).""" # ndim will be 3 in finite-difference mode and 1 when working # with the AtomPAW class (spherical atoms and 1d grids) ndim = self.gd.ndim if out_xR is None: out_xR = self.finegd.empty(in_xR.shape[:-ndim]) a_xR = in_xR.reshape((-1,) + in_xR.shape[-ndim:]) b_xR = out_xR.reshape((-1,) + out_xR.shape[-ndim:]) for in_R, out_R in zip(a_xR, b_xR): self.interpolator.apply(in_R, out_R) return out_xR def calculate_pseudo_charge(self): self.nt_g = self.nt_sg[:self.nspins].sum(axis=0) self.rhot_g = self.nt_g.copy() self.ghat.add(self.rhot_g, self.Q_aL) if debug: charge = self.finegd.integrate(self.rhot_g) + self.charge if abs(charge) > self.charge_eps: raise RuntimeError('Charge not conserved: excess=%.9f' % charge) def get_pseudo_core_kinetic_energy_density_lfc(self): return LFC(self.gd, [[setup.tauct] for setup in self.setups], forces=True, cut=True) def calculate_dipole_moment(self): return self.finegd.calculate_dipole_moment(self.rhot_g)
class SIC(XCFunctional): orbital_dependent = True unitary_invariant = False 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 initialize(self, density, hamiltonian, wfs, occ=None): assert wfs.gamma assert not wfs.gd.pbc_c.any() self.wfs = wfs self.dtype = float self.xc.initialize(density, hamiltonian, wfs, occ) self.kpt_comm = wfs.kpt_comm self.nspins = wfs.nspins self.nbands = wfs.bd.nbands if self.finegrid: self.finegd = density.finegd self.ghat = density.ghat else: self.finegd = density.gd self.ghat = LFC(self.finegd, [setup.ghat_l for setup in density.setups], integral=sqrt(4 * pi), forces=True) poissonsolver = PoissonSolver(eps=1e-14) poissonsolver.set_grid_descriptor(self.finegd) poissonsolver.initialize() self.spin_s = {} for kpt in wfs.kpt_u: self.spin_s[kpt.s] = SICSpin(kpt, self.xc, density, hamiltonian, wfs, poissonsolver, self.ghat, self.finegd, **self.parameters) def get_setup_name(self): return self.xc.get_setup_name() def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp, addcoredensity, a) def set_positions(self, spos_ac): if not self.finegrid: self.ghat.set_positions(spos_ac) def calculate(self, gd, n_sg, v_sg=None, e_g=None): self.gd = gd # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # SIC: self.esic = 0.0 self.ekin = 0.0 for spin in self.spin_s.values(): if spin.kpt.psit_nG is not None: desic, dekin = spin.calculate() self.esic += desic self.ekin += dekin self.esic = self.kpt_comm.sum(self.esic) self.ekin = self.kpt_comm.sum(self.ekin) return exc + self.esic def apply_orbital_dependent_hamiltonian(self, kpt, psit_nG, Htpsit_nG=None, dH_asp=None): spin = self.spin_s[kpt.s] if spin.W_mn is None: return spin.apply_orbital_dependent_hamiltonian(psit_nG) def correct_hamiltonian_matrix(self, kpt, H_nn): spin = self.spin_s[kpt.s] if spin.W_mn is None: return spin.correct_hamiltonian_matrix(H_nn) def add_correction(self, kpt, psit_xG, Htpsit_xG, P_axi, c_axi, n_x, calculate_change=False): spin = self.spin_s[kpt.s] if spin.W_mn is None: return if calculate_change: spin.calculate_residual_change(psit_xG, Htpsit_xG, P_axi, c_axi, n_x) else: spin.calculate_residual(psit_xG, Htpsit_xG, P_axi, c_axi) def rotate(self, kpt, U_nn): self.spin_s[kpt.s].rotate(U_nn) def setup_force_corrections(self, F_av): self.dF_av = np.zeros_like(F_av) for spin in self.spin_s.values(): spin.add_forces(self.dF_av) self.wfs.kpt_comm.sum(self.dF_av) def add_forces(self, F_av): F_av += self.dF_av def summary(self, out=sys.stdout): for s in range(self.nspins): if s in self.spin_s: stabpot = self.spin_s[s].stabpot spin = self.spin_s[s] pos_mv = spin.get_centers() exc_m = spin.exc_m ecoulomb_m = spin.ecoulomb_m if self.kpt_comm.rank == 1 and self.gd.comm.rank == 0: nocc = self.kpt_comm.sum(spin.nocc) self.kpt_comm.send(pos_mv, 0) self.kpt_comm.send(exc_m, 0) self.kpt_comm.send(ecoulomb_m, 0) else: if self.kpt_comm.rank == 0 and self.gd.comm.rank == 0: nocc = self.kpt_comm.sum(0) pos_mv = np.zeros((nocc, 3)) exc_m = np.zeros(nocc) ecoulomb_m = np.zeros(nocc) self.kpt_comm.receive(pos_mv, 1) self.kpt_comm.receive(exc_m, 1) self.kpt_comm.receive(ecoulomb_m, 1) if self.kpt_comm.rank == 0 and self.gd.comm.rank == 0: out.write('\nSIC orbital centers and energies:\n') out.write(' %5.2fx %5.2fx\n' % (self.spin_s[0].xc_factor, self.spin_s[0].coulomb_factor)) out.write(' x y z XC Coulomb\n') out.write('--------------------------------------------------\n') m = 0 for pos_v, exc, ecoulomb in zip(pos_mv, exc_m, ecoulomb_m): out.write('%3d (%7.3f,%7.3f,%7.3f): %8.3f %8.3f\n' % ((m,) + tuple(pos_v) + (exc * Hartree, ecoulomb * Hartree))) m += 1 out.write('--------------------------------------------------\n') out.write('\nTotal SIC energy : %12.5f\n' % (self.esic * Hartree)) out.write('Stabilizing potential: %12.5f\n' % (stabpot * Hartree)) def read(self, reader): xc_factor = reader['SIC_xc_factor'] coulomb_factor = reader['SIC_coulomb_factor'] # for s in range(self.nspins): # try: npart = reader.dimension('npart'+str(s)) except KeyError: npart = 0 # if npart>0: W_mn = reader.get('UnitaryTransformation'+str(s)) else: W_mn = None # if s in self.spin_s.keys(): self.spin_s[s].initial_W_mn = W_mn self.spin_s[s].xc_factor = xc_factor self.spin_s[s].coulomb_factor = coulomb_factor def write(self, writer, natoms=None): # for s in self.spin_s.keys(): spin = self.spin_s[s] if self.wfs.world.rank==0: writer['SIC_xc_factor'] = spin.xc_factor writer['SIC_coulomb_factor'] = spin.coulomb_factor break # for s in range(self.nspins): # W_mn = self.get_unitary_transformation(s) # if self.wfs.world.rank == 0: if W_mn!=None: writer.dimension('npart'+str(s), W_mn.shape[0]) writer.add('UnitaryTransformation'+str(s), ('npart'+str(s),'npart'+str(s)), dtype=self.dtype) writer.fill(W_mn) def get_unitary_transformation(self, s): # if s in self.spin_s.keys(): spin = self.spin_s[s] # if spin.W_mn==None or spin.finegd.rank!=0: n = 0 else: n = spin.W_mn.shape[0] # else: n=0 # n = self.wfs.world.sum(n) # if n>0: W_mn = np.zeros((n,n), dtype=self.dtype) else: W_mn = None return W_mn # if s in self.spin_s.keys(): spin = self.spin_s[s] # if spin.W_mn==None or spin.finegd.rank!=0: W_mn[:] = 0.0 else: W_mn[:] = spin.W_mn[:] # else: W_mn[:] = 0.0 # self.wfs.world.sum(W_mn) return W_mn
def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.wfs = wfs self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol ** (2 / 3.0) / pi ** 2 self.gamma = vol / (2 * pi) ** 2 * sqrt(pi / self.alpha) * self.kd.nbzkpts if self.ecut is None: self.ecut = 0.5 * pi ** 2 / (self.gd.h_cv ** 2).sum(1).max() * 0.9999 assert self.kd.N_c is not None n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.bzq_qc = self.kd.get_bz_q_points() if self.qsym: op_scc = self.kd.symmetry.op_scc self.ibzq_qc = self.kd.get_ibz_q_points(self.bzq_qc, op_scc)[0] self.q_weights = self.kd.q_weights * len(self.bzq_qc) else: self.ibzq_qc = self.bzq_qc self.q_weights = np.ones(len(self.bzq_qc)) self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): # XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G ** -1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:] ** -1) n += 1 assert n == self.kd.N_c.prod() self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.ibzq_qc) self.ghat = LFC( self.gd, [setup.ghat_l for setup in density.setups], KPointDescriptor(self.bzq_qc), dtype=complex ) # self.interpolator = density.interpolator self.print_initialization(hamiltonian.xc.name)
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 RealSpaceDensity(Density): def __init__(self, gd, finegd, nspins, charge, redistributor, stencil=3, background_charge=None): Density.__init__(self, gd, finegd, nspins, charge, redistributor, background_charge=background_charge) self.stencil = stencil def initialize(self, setups, timer, magmom_a, hund): Density.initialize(self, setups, timer, magmom_a, hund) # Interpolation function for the density: self.interpolator = Transformer(self.redistributor.aux_gd, self.finegd, self.stencil) spline_aj = [] for setup in setups: if setup.nct is None: spline_aj.append([]) else: spline_aj.append([setup.nct]) self.nct = LFC(self.gd, spline_aj, integral=[setup.Nct for setup in setups], forces=True, cut=True) self.ghat = LFC(self.finegd, [setup.ghat_l for setup in setups], integral=sqrt(4 * pi), forces=True) def set_positions(self, spos_ac, rank_a=None): Density.set_positions(self, spos_ac, rank_a) self.nct_G = self.gd.zeros() self.nct.add(self.nct_G, 1.0 / self.nspins) def interpolate_pseudo_density(self, comp_charge=None): """Interpolate pseudo density to fine grid.""" if comp_charge is None: comp_charge = self.calculate_multipole_moments() self.nt_sg = self.distribute_and_interpolate(self.nt_sG, self.nt_sg) # With periodic boundary conditions, the interpolation will # conserve the number of electrons. if not self.gd.pbc_c.all(): # With zero-boundary conditions in one or more directions, # this is not the case. pseudo_charge = (self.background_charge.charge - self.charge - comp_charge) if abs(pseudo_charge) > 1.0e-14: x = (pseudo_charge / self.finegd.integrate(self.nt_sg).sum()) self.nt_sg *= x def interpolate(self, in_xR, out_xR=None): """Interpolate array(s).""" # ndim will be 3 in finite-difference mode and 1 when working # with the AtomPAW class (spherical atoms and 1d grids) ndim = self.gd.ndim if out_xR is None: out_xR = self.finegd.empty(in_xR.shape[:-ndim]) a_xR = in_xR.reshape((-1, ) + in_xR.shape[-ndim:]) b_xR = out_xR.reshape((-1, ) + out_xR.shape[-ndim:]) for in_R, out_R in zip(a_xR, b_xR): self.interpolator.apply(in_R, out_R) return out_xR def distribute_and_interpolate(self, in_xR, out_xR=None): in_xR = self.redistributor.distribute(in_xR) return self.interpolate(in_xR, out_xR) def calculate_pseudo_charge(self): self.nt_g = self.nt_sg.sum(axis=0) self.rhot_g = self.nt_g.copy() self.ghat.add(self.rhot_g, self.Q_aL) self.background_charge.add_charge_to(self.rhot_g) if debug: charge = self.finegd.integrate(self.rhot_g) + self.charge if abs(charge) > self.charge_eps: raise RuntimeError('Charge not conserved: excess=%.9f' % charge) def get_pseudo_core_kinetic_energy_density_lfc(self): return LFC(self.gd, [[setup.tauct] for setup in self.setups], forces=True, cut=True) def calculate_dipole_moment(self): return self.finegd.calculate_dipole_moment(self.rhot_g)
def get_projections(self, locfun, spin=0): """Project wave functions onto localized functions Determine the projections of the Kohn-Sham eigenstates onto specified localized functions of the format:: locfun = [[spos_c, l, sigma], [...]] spos_c can be an atom index, or a scaled position vector. l is the angular momentum, and sigma is the (half-) width of the radial gaussian. Return format is:: f_kni = <psi_kn | f_i> where psi_kn are the wave functions, and f_i are the specified localized functions. As a special case, locfun can be the string 'projectors', in which case the bound state projectors are used as localized functions. """ wfs = self.wfs if locfun == 'projectors': f_kin = [] for kpt in wfs.kpt_u: if kpt.s == spin: f_in = [] for a, P_ni in kpt.P_ani.items(): i = 0 setup = wfs.setups[a] for l, n in zip(setup.l_j, setup.n_j): if n >= 0: for j in range(i, i + 2 * l + 1): f_in.append(P_ni[:, j]) i += 2 * l + 1 f_kin.append(f_in) f_kni = np.array(f_kin).transpose(0, 2, 1) return f_kni.conj() from gpaw.lfc import LocalizedFunctionsCollection as LFC from gpaw.spline import Spline from gpaw.utilities import _fact nkpts = len(wfs.ibzk_kc) nbf = np.sum([2 * l + 1 for pos, l, a in locfun]) f_kni = np.zeros((nkpts, wfs.nbands, nbf), wfs.dtype) spos_ac = self.atoms.get_scaled_positions() % 1.0 spos_xc = [] splines_x = [] for spos_c, l, sigma in locfun: if isinstance(spos_c, int): spos_c = spos_ac[spos_c] spos_xc.append(spos_c) alpha = .5 * Bohr**2 / sigma**2 r = np.linspace(0, 6. * sigma, 500) f_g = (_fact[l] * (4 * alpha)**(l + 3 / 2.) * np.exp(-alpha * r**2) / (np.sqrt(4 * np.pi) * _fact[2 * l + 1])) splines_x.append([Spline(l, rmax=r[-1], f_g=f_g, points=61)]) lf = LFC(wfs.gd, splines_x, wfs.kpt_comm, dtype=wfs.dtype) if not wfs.gamma: lf.set_k_points(wfs.ibzk_qc) lf.set_positions(spos_xc) k = 0 f_ani = lf.dict(wfs.nbands) for kpt in wfs.kpt_u: if kpt.s != spin: continue lf.integrate(kpt.psit_nG[:], f_ani, kpt.q) i1 = 0 for x, f_ni in f_ani.items(): i2 = i1 + f_ni.shape[1] f_kni[k, :, i1:i2] = f_ni i1 = i2 k += 1 return f_kni.conj()
def update(self): if self.ranges is None: # First time self.ranges = [] self.nbands = self.lcao.wfs.bd.nbands start = 0 if self.ranges_str != "full": for rng in self.ranges_str.split(","): print("rng", rng) rng = eval(rng) self.ranges.append(range(start, rng)) start = rng self.ranges.append(range(start, self.nbands)) print(self.ranges) self.ghat = LFC( self.lcao.wfs.gd, [setup.ghat_l for setup in self.lcao.density.setups], integral=sqrt(4 * pi), forces=False, ) spos_ac = self.lcao.atoms.get_scaled_positions() % 1.0 self.ghat.set_positions(spos_ac) # Clear files for rid, rng in enumerate(self.ranges): f = open(self.filename + "." + str(rid) + ".density", "w") print("# Density file", file=f) N_c = self.lcao.wfs.gd.N_c print(N_c[0], N_c[1], N_c[2], file=f) print("# This header is 10 lines long, then double precision binary data starts.", file=f) for i in range(7): print("#", file=f) f.close() # self.lcao.timer.start('Dump density') for rid, rng in enumerate(self.ranges): assert len(self.lcao.wfs.kpt_u) == 1 f_un = [self.lcao.wfs.kpt_u[0].f_n.copy()] for n in range(self.lcao.wfs.bd.nbands): band_rank, myn = self.lcao.wfs.bd.who_has(n) if self.lcao.wfs.bd.rank == band_rank: if not n in rng: f_un[0][myn] = 0.0 n_sG = self.lcao.wfs.gd.zeros(1) self.lcao.wfs.add_to_density_from_k_point_with_occupation(n_sG, self.lcao.wfs.kpt_u[0], f_un[0]) self.lcao.wfs.kptband_comm.sum(n_sG) D_asp = {} for a in self.lcao.density.D_asp: ni = self.lcao.density.setups[a].ni D_asp[a] = np.zeros((1, ni * (ni + 1) // 2)) self.lcao.wfs.calculate_atomic_density_matrices_with_occupation(D_asp, f_un) Q_aL = {} for a, D_sp in D_asp.items(): Q_aL[a] = np.dot(D_sp.sum(0), self.lcao.density.setups[a].Delta_pL) self.ghat.add(n_sG, Q_aL) n_sg = self.lcao.wfs.gd.collect(n_sG, broadcast=False) if world.rank == 0: f = open(self.filename + "." + str(rid) + ".density", "a+") # n_sg.astype(np.float32).tofile(f) # print "max", np.max(n_sg), np.min(n_sg) n_sg.tofile(f) f.close() s = n_sg.shape f = open(self.filename + ".info", "w") print(s[0], s[1], s[2], file=f) f.close()
def new_get_all_electron_density(self, atoms, gridrefinement=2): """Return real all-electron density array.""" # Refinement of coarse grid, for representation of the AE-density if gridrefinement == 1: gd = self.gd n_sg = self.nt_sG.copy() elif gridrefinement == 2: gd = self.finegd if self.nt_sg is None: self.interpolate() n_sg = self.nt_sg.copy() elif gridrefinement == 4: # Extra fine grid gd = self.finegd.refine() # Interpolation function for the density: interpolator = Transformer(self.finegd, gd, 3) # Transfer the pseudo-density to the fine grid: n_sg = gd.empty(self.nspins) if self.nt_sg is None: self.interpolate() for s in range(self.nspins): interpolator.apply(self.nt_sg[s], n_sg[s]) else: raise NotImplementedError # Add corrections to pseudo-density to get the AE-density splines = {} phi_aj = [] phit_aj = [] nc_a = [] nct_a = [] for a, id in enumerate(self.setups.id_a): if id in splines: phi_j, phit_j, nc, nct = splines[id] else: # Load splines: phi_j, phit_j, nc, nct = self.setups[a].get_partial_waves()[:4] splines[id] = (phi_j, phit_j, nc, nct) phi_aj.append(phi_j) phit_aj.append(phit_j) nc_a.append([nc]) nct_a.append([nct]) # Create localized functions from splines phi = BasisFunctions(gd, phi_aj) phit = BasisFunctions(gd, phit_aj) nc = LFC(gd, nc_a) nct = LFC(gd, nct_a) spos_ac = atoms.get_scaled_positions() % 1.0 phi.set_positions(spos_ac) phit.set_positions(spos_ac) nc.set_positions(spos_ac) nct.set_positions(spos_ac) I_sa = np.zeros((self.nspins, len(atoms))) a_W = np.empty(len(phi.M_W), np.int32) W = 0 for a in phi.atom_indices: nw = len(phi.sphere_a[a].M_w) a_W[W:W + nw] = a W += nw rho_MM = np.zeros((phi.Mmax, phi.Mmax)) for s, I_a in enumerate(I_sa): M1 = 0 for a, setup in enumerate(self.setups): ni = setup.ni D_sp = self.D_asp.get(a) if D_sp is None: D_sp = np.empty((self.nspins, ni * (ni + 1) // 2)) else: I_a[a] = ((setup.Nct - setup.Nc) / self.nspins - sqrt(4 * pi) * np.dot(D_sp[s], setup.Delta_pL[:, 0])) if gd.comm.size > 1: gd.comm.broadcast(D_sp, self.rank_a[a]) M2 = M1 + ni rho_MM[M1:M2, M1:M2] = unpack2(D_sp[s]) M1 = M2 phi.lfc.ae_valence_density_correction(rho_MM, n_sg[s], a_W, I_a) phit.lfc.ae_valence_density_correction(-rho_MM, n_sg[s], a_W, I_a) a_W = np.empty(len(nc.M_W), np.int32) W = 0 for a in nc.atom_indices: nw = len(nc.sphere_a[a].M_w) a_W[W:W + nw] = a W += nw scale = 1.0 / self.nspins for s, I_a in enumerate(I_sa): nc.lfc.ae_core_density_correction(scale, n_sg[s], a_W, I_a) nct.lfc.ae_core_density_correction(-scale, n_sg[s], a_W, I_a) gd.comm.sum(I_a) N_c = gd.N_c g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c for I, g_c in zip(I_a, g_ac): if (g_c >= 0).all() and (g_c < gd.n_c).all(): n_sg[s][tuple(g_c)] -= I / gd.dv return n_sg, gd
class HybridXC(HybridXCBase): def __init__(self, name, hybrid=None, xc=None, finegrid=False, unocc=False): """Mix standard functionals with exact exchange. finegrid: boolean Use fine grid for energy functional evaluations ? unocc: boolean Apply vxx also to unoccupied states ? """ self.finegrid = finegrid self.unocc = unocc HybridXCBase.__init__(self, name, hybrid, xc) def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp, addcoredensity, a) def initialize(self, density, hamiltonian, wfs, occupations): assert wfs.gamma self.xc.initialize(density, hamiltonian, wfs, occupations) self.kpt_comm = wfs.kpt_comm self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.exx_s = np.zeros(self.nspins) self.ekin_s = np.zeros(self.nspins) self.nocc_s = np.empty(self.nspins, int) if self.finegrid: self.poissonsolver = hamiltonian.poisson self.ghat = density.ghat self.interpolator = density.interpolator self.restrictor = hamiltonian.restrictor else: self.poissonsolver = PoissonSolver(eps=1e-11) self.poissonsolver.set_grid_descriptor(density.gd) self.poissonsolver.initialize() self.ghat = LFC(density.gd, [setup.ghat_l for setup in density.setups], integral=np.sqrt(4 * np.pi), forces=True) self.gd = density.gd self.finegd = self.ghat.gd def set_positions(self, spos_ac): if not self.finegrid: self.ghat.set_positions(spos_ac) def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) self.ekin = self.kpt_comm.sum(self.ekin_s.sum()) return exc + self.kpt_comm.sum(self.exx_s.sum()) def calculate_exx(self): for kpt in self.kpt_u: self.apply_orbital_dependent_hamiltonian(kpt, kpt.psit_nG) def apply_orbital_dependent_hamiltonian(self, kpt, psit_nG, Htpsit_nG=None, dH_asp=None): if kpt.f_n is None: return deg = 2 // self.nspins # Spin degeneracy hybrid = self.hybrid P_ani = kpt.P_ani setups = self.setups vt_g = self.finegd.empty() if self.gd is not self.finegd: vt_G = self.gd.empty() nocc = int(kpt.f_n.sum()) // (3 - self.nspins) if self.unocc: nbands = len(kpt.f_n) else: nbands = nocc self.nocc_s[kpt.s] = nocc if Htpsit_nG is not None: kpt.vt_nG = self.gd.empty(nbands) kpt.vxx_ani = {} kpt.vxx_anii = {} for a, P_ni in P_ani.items(): I = P_ni.shape[1] kpt.vxx_ani[a] = np.zeros((nbands, I)) kpt.vxx_anii[a] = np.zeros((nbands, I, I)) exx = 0.0 ekin = 0.0 # Determine pseudo-exchange for n1 in range(nbands): psit1_G = psit_nG[n1] f1 = kpt.f_n[n1] / deg for n2 in range(n1, nbands): psit2_G = psit_nG[n2] f2 = kpt.f_n[n2] / deg # Double count factor: dc = (1 + (n1 != n2)) * deg nt_G, rhot_g = self.calculate_pair_density(n1, n2, psit_nG, P_ani) vt_g[:] = 0.0 iter = self.poissonsolver.solve(vt_g, -rhot_g, charge=-float(n1 == n2), eps=1e-12, zero_initial_phi=True) vt_g *= hybrid if self.gd is self.finegd: vt_G = vt_g else: self.restrictor.apply(vt_g, vt_G) # Integrate the potential on fine and coarse grids int_fine = self.finegd.integrate(vt_g * rhot_g) int_coarse = self.gd.integrate(vt_G * nt_G) if self.gd.comm.rank == 0: # only add to energy on master CPU exx += 0.5 * dc * f1 * f2 * int_fine ekin -= dc * f1 * f2 * int_coarse if Htpsit_nG is not None: Htpsit_nG[n1] += f2 * vt_G * psit2_G if n1 == n2: kpt.vt_nG[n1] = f1 * vt_G else: Htpsit_nG[n2] += f1 * vt_G * psit1_G # Update the vxx_uni and vxx_unii vectors of the nuclei, # used to determine the atomic hamiltonian, and the # residuals v_aL = self.ghat.dict() self.ghat.integrate(vt_g, v_aL) for a, v_L in v_aL.items(): v_ii = unpack(np.dot(setups[a].Delta_pL, v_L)) v_ni = kpt.vxx_ani[a] v_nii = kpt.vxx_anii[a] P_ni = P_ani[a] v_ni[n1] += f2 * np.dot(v_ii, P_ni[n2]) if n1 != n2: v_ni[n2] += f1 * np.dot(v_ii, P_ni[n1]) else: # XXX Check this: v_nii[n1] = f1 * v_ii # Apply the atomic corrections to the energy and the Hamiltonian matrix for a, P_ni in P_ani.items(): setup = setups[a] if Htpsit_nG is not None: # Add non-trivial corrections the Hamiltonian matrix h_nn = symmetrize(np.inner(P_ni[:nbands], kpt.vxx_ani[a][:nbands])) ekin -= np.dot(kpt.f_n[:nbands], h_nn.diagonal()) dH_p = dH_asp[a][kpt.s] # Get atomic density and Hamiltonian matrices D_p = self.density.D_asp[a][kpt.s] D_ii = unpack2(D_p) ni = len(D_ii) # Add atomic corrections to the valence-valence exchange energy # -- # > D C D # -- ii iiii ii for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) if Htpsit_nG is not None: dH_p[p12] -= 2 * hybrid / deg * A / ((i1 != i2) + 1) ekin += 2 * hybrid / deg * D_ii[i1, i2] * A exx -= hybrid / deg * D_ii[i1, i2] * A # Add valence-core exchange energy # -- # > X D # -- ii ii if setup.X_p is not None: exx -= hybrid * np.dot(D_p, setup.X_p) if Htpsit_nG is not None: dH_p -= hybrid * setup.X_p ekin += hybrid * np.dot(D_p, setup.X_p) # Add core-core exchange energy if kpt.s == 0: exx += hybrid * setup.ExxC self.exx_s[kpt.s] = self.gd.comm.sum(exx) self.ekin_s[kpt.s] = self.gd.comm.sum(ekin) def correct_hamiltonian_matrix(self, kpt, H_nn): if not hasattr(kpt, 'vxx_ani'): return if self.gd.comm.rank > 0: H_nn[:] = 0.0 nocc = self.nocc_s[kpt.s] nbands = len(kpt.vt_nG) for a, P_ni in kpt.P_ani.items(): H_nn[:nbands, :nbands] += symmetrize(np.inner(P_ni[:nbands], kpt.vxx_ani[a])) self.gd.comm.sum(H_nn) H_nn[:nocc, nocc:] = 0.0 H_nn[nocc:, :nocc] = 0.0 def calculate_pair_density(self, n1, n2, psit_nG, P_ani): Q_aL = {} for a, P_ni in P_ani.items(): P1_i = P_ni[n1] P2_i = P_ni[n2] D_ii = np.outer(P1_i, P2_i.conj()).real D_p = pack(D_ii) Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) nt_G = psit_nG[n1] * psit_nG[n2] if self.finegd is self.gd: nt_g = nt_G else: nt_g = self.finegd.empty() self.interpolator.apply(nt_G, nt_g) rhot_g = nt_g.copy() self.ghat.add(rhot_g, Q_aL) return nt_G, rhot_g def add_correction(self, kpt, psit_xG, Htpsit_xG, P_axi, c_axi, n_x, calculate_change=False): if kpt.f_n is None: return nocc = self.nocc_s[kpt.s] if calculate_change: for x, n in enumerate(n_x): if n < nocc: Htpsit_xG[x] += kpt.vt_nG[n] * psit_xG[x] for a, P_xi in P_axi.items(): c_axi[a][x] += np.dot(kpt.vxx_anii[a][n], P_xi[x]) else: for a, c_xi in c_axi.items(): c_xi[:nocc] += kpt.vxx_ani[a][:nocc] def rotate(self, kpt, U_nn): if kpt.f_n is None: return nocc = self.nocc_s[kpt.s] if len(kpt.vt_nG) == nocc: U_nn = U_nn[:nocc, :nocc] gemm(1.0, kpt.vt_nG.copy(), U_nn, 0.0, kpt.vt_nG) for v_ni in kpt.vxx_ani.values(): gemm(1.0, v_ni.copy(), U_nn, 0.0, v_ni) for v_nii in kpt.vxx_anii.values(): gemm(1.0, v_nii.copy(), U_nn, 0.0, v_nii)
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 UTConstantWavefunctionSetup(UTBandParallelSetup): __doc__ = UTBandParallelSetup.__doc__ + """ The pseudo wavefunctions are constants normalized to their band index.""" allocated = False blocking = None async = None def setUp(self): UTBandParallelSetup.setUp(self) for virtvar in ['dtype','blocking','async']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Create randomized atoms self.atoms = create_random_atoms(self.gd) # Do we agree on the atomic positions? pos_ac = self.atoms.get_positions() pos_rac = np.empty((world.size,)+pos_ac.shape, pos_ac.dtype) world.all_gather(pos_ac, pos_rac) if (pos_rac-pos_rac[world.rank,...][np.newaxis,...]).any(): raise RuntimeError('Discrepancy in atomic positions detected.') # Create setups for atoms self.Z_a = self.atoms.get_atomic_numbers() self.setups = Setups(self.Z_a, p.setups, p.basis, p.lmax, xc) # Create atomic projector overlaps spos_ac = self.atoms.get_scaled_positions() % 1.0 self.rank_a = self.gd.get_ranks_from_positions(spos_ac) self.pt = LFC(self.gd, [setup.pt_j for setup in self.setups], dtype=self.dtype) self.pt.set_positions(spos_ac) if memstats: # Hack to scramble heap usage into steady-state level HEAPSIZE = 25 * 1024**2 for i in range(100): data = np.empty(np.random.uniform(0, HEAPSIZE // 8), float) del data self.mem_pre = record_memory() self.mem_alloc = None self.mem_test = None # Stuff for pseudo wave functions and projections if self.dtype == complex: self.gamma = 1j**(1.0/self.nbands) else: self.gamma = 1.0 self.psit_nG = None self.P_ani = None self.Qeff_a = None self.Qtotal = None self.allocate() def tearDown(self): UTBandParallelSetup.tearDown(self) del self.P_ani, self.psit_nG del self.pt, self.setups, self.atoms if memstats: self.print_memory_summary() del self.mem_pre, self.mem_alloc, self.mem_test self.allocated = False def print_memory_summary(self): if not memstats: raise RuntimeError('No memory statistics were recorded!') if world.rank == 0: sys.stdout.write('\n') sys.stdout.flush() dm_r, dminfo = create_memory_info(MemorySingleton(), self.mem_pre) if world.rank == 0: print('overhead: %s -> %8.4f MB' % (dminfo, dm_r.sum()/1024**2.)) dm_r, dminfo = create_memory_info(self.mem_pre, self.mem_alloc) if world.rank == 0: print('allocate: %s -> %8.4f MB' % (dminfo, dm_r.sum()/1024**2.)) dm_r, dminfo = create_memory_info(self.mem_alloc, self.mem_test) if world.rank == 0: print('test-use: %s -> %8.4f MB' % (dminfo, dm_r.sum()/1024**2.)) def allocate(self): """ Allocate constant wavefunctions and their projections according to:: / \ _____ i*phase*(n-m) <psi |psi > = ( 1 + Q ) * V m*n * e n n' \ total/ """ if self.allocated: raise RuntimeError('Already allocated!') self.allocate_wavefunctions() self.allocate_projections() # XXX DEBUG disables projection contributions if False: for a,P_ni in self.P_ani.items(): P_ni[:] = 0. self.Qeff_a[a] = 0. self.Qtotal = 0. self.Z_a[:] = 2**63-1 # XXX DEBUG self.Qtotal = np.empty(1, dtype=float) self.Qtotal[:] = np.sum([Qeff for Qeff in self.Qeff_a.values()]) self.gd.comm.sum(self.Qtotal) band_indices = np.arange(self.nbands).astype(self.dtype) z = self.gamma**band_indices * band_indices**0.5 self.S0_nn = (1. + self.Qtotal) * np.outer(z.conj(), z) self.allocated = True if memstats: self.mem_alloc = record_memory() def allocate_wavefunctions(self): """ Allocate constant pseudo wavefunctions according to:: ~ ~ _____ i*phase*(n-m) <psi |psi > = V m*n * e n n' """ if self.allocated: raise RuntimeError('Already allocated!') # Fill in wave functions gpts_c = self.gd.get_size_of_global_array() self.psit_nG = self.gd.empty(self.bd.mynbands, self.dtype) for myn, psit_G in enumerate(self.psit_nG): n = self.bd.global_index(myn) # Fill psit_nG: | psit_n > = exp(i*phase*n) * sqrt(n) / sqrt(V) psit_G[:] = self.gamma**n * n**0.5 / (self.gd.dv * gpts_c.prod())**0.5 def allocate_projections(self): """ Construct dummy projection of pseudo wavefunction according to:: ___ \ ~ ~a a ~a ~ 1 _____ i*phase*(n-m) ) <psi |p > dO <p |psi > = +/- --- * V m*n * e /___ n i ii' i' n' Z ii' a """ if self.allocated: raise RuntimeError('Already allocated!') # Fill in projector overlaps my_band_indices = self.bd.get_band_indices() my_atom_indices = np.argwhere(self.gd.comm.rank == self.rank_a).ravel() # Holm-Nielsen check: natoms = len(self.atoms) assert (self.gd.comm.sum(float(sum(my_atom_indices))) == natoms * (natoms - 1) // 2) # Check that LFC agrees with us: self.assertEqual(len(my_atom_indices), len(self.pt.my_atom_indices)) for a1, a2 in zip(my_atom_indices, self.pt.my_atom_indices): self.assertEqual(a1, a2) self.Qeff_a = {} self.P_ani = self.pt.dict(self.bd.mynbands) for a in my_atom_indices: ni = self.setups[a].ni # Fill P_ni: <p_i | psit_n > = beta_i * exp(i*phase*n) * sqrt(n) # # | ____ | # | \ * a | 1 # | ) beta dO beta | = ---- # | /___ i ij j | Z # | ij | a # # Substitution by linear transformation: beta_i -> dO_ij alpha_j, # where we start out with some initial non-constant vector: alpha_i = np.exp(-np.arange(ni).astype(self.dtype)/ni) try: # Try Cholesky decomposition dO_ii = L_ii * L_ii^dag L_ii = np.linalg.cholesky(self.setups[a].dO_ii) alpha_i /= np.vdot(alpha_i, alpha_i)**0.5 beta_i = np.linalg.solve(L_ii.T.conj(), alpha_i) except np.linalg.LinAlgError: # Eigenvector decomposition dO_ii = V_ii * W_ii * V_ii^dag W_i, V_ii = np.linalg.eigh(self.setups[a].dO_ii) alpha_i /= np.abs(np.vdot(alpha_i, np.dot(np.diag(W_i), alpha_i)))**0.5 beta_i = np.linalg.solve(V_ii.T.conj(), alpha_i) # Normalize according to plus/minus charge beta_i /= self.Z_a[a]**0.5 self.Qeff_a[a] = np.vdot(beta_i, np.dot(self.setups[a].dO_ii, \ beta_i)).real self.P_ani[a][:] = np.outer(self.gamma**my_band_indices \ * my_band_indices**0.5, beta_i) def check_and_plot(self, A_nn, A0_nn, digits, keywords=''): # Construct fingerprint of input matrices for comparison fingerprint = np.array([md5_array(A_nn, numeric=True), md5_array(A0_nn, numeric=True)]) # Compare fingerprints across all processors fingerprints = np.empty((world.size, 2), np.int64) world.all_gather(fingerprint, fingerprints) if fingerprints.ptp(0).any(): raise RuntimeError('Distributed matrices are not identical!') # If assertion fails, catch temporarily while plotting, then re-raise try: self.assertAlmostEqual(np.abs(A_nn-A0_nn).max(), 0, digits) except AssertionError: if world.rank == 0 and mpl is not None: from matplotlib.figure import Figure fig = Figure() ax = fig.add_axes([0.0, 0.1, 1.0, 0.83]) ax.set_title(self.__class__.__name__) im = ax.imshow(np.abs(A_nn-A0_nn), interpolation='nearest') fig.colorbar(im) fig.text(0.5, 0.05, 'Keywords: ' + keywords, \ horizontalalignment='center', verticalalignment='top') from matplotlib.backends.backend_agg import FigureCanvasAgg img = 'ut_hsops_%s_%s.png' % (self.__class__.__name__, \ '_'.join(keywords.split(','))) FigureCanvasAgg(fig).print_figure(img.lower(), dpi=90) raise def get_optimal_number_of_blocks(self, blocking='fast'): """Estimate the optimal number of blocks for band parallelization. The number of blocks determines how many parallel send/receive operations are performed, as well as the added memory footprint of the required send/receive buffers. ``blocking`` ``nblocks`` Description ============ ============= ======================================== 'fast' ``1`` Heavy on memory, more accurate and fast. 'light' ``mynbands`` Light on memory, less accurate and slow. 'intdiv' ``...`` First integer divisible value 'nonintdiv' ``...`` Some non-integer divisible cases """ #if self.bd.comm.size == 1: # return 1 if blocking == 'fast': return 1 elif blocking == 'light': return self.bd.mynbands elif blocking == 'intdiv': # Find first value of nblocks that leads to integer # divisible mybands / nblock. This is very like to be # 2 but coded here for the general case nblocks = 2 while self.bd.mynbands % nblocks != 0: nblocks +=1 return nblocks elif blocking == 'nonintdiv1': # Find first value of nblocks that leads to non-integer # divisible mynbands / nblock that is less than M nblocks = 2 M = self.bd.mynbands // nblocks while self.bd.mynbands % nblocks < M: nblocks += 1 M = self.bd.mynbands // nblocks return nblocks elif blocking == 'nonintdiv2': # Find first value of nblocks that leads to non-integer # divisible mynbands / nblock that is less than M nblocks = 2 M = self.bd.mynbands // nblocks while self.bd.mynbands % nblocks > M: nblocks += 1 M = self.mynbands // nblocks return nblocks else: nblocks = blocking assert self.bd.mynbands // nblocks > 0 return nblocks # ================================= def test_contents_wavefunction(self): # Integrate diagonal brakets of pseudo wavefunctions gpts_c = self.gd.get_size_of_global_array() intpsit_myn = self.bd.empty(dtype=self.dtype) for myn, psit_G in enumerate(self.psit_nG): n = self.bd.global_index(myn) intpsit_myn[myn] = np.vdot(psit_G, psit_G) * self.gd.dv self.gd.comm.sum(intpsit_myn) if memstats: self.mem_test = record_memory() my_band_indices = self.bd.get_band_indices() self.assertAlmostEqual(np.abs(intpsit_myn-my_band_indices).max(), 0, 9) intpsit_n = self.bd.collect(intpsit_myn, broadcast=True) self.assertAlmostEqual(np.abs(intpsit_n-np.arange(self.nbands)).max(), 0, 9) def test_contents_projection(self): # Distribute inverse effective charges to everybody in domain all_Qeff_a = np.empty(len(self.atoms), dtype=float) for a,rank in enumerate(self.rank_a): if rank == self.gd.comm.rank: Qeff = np.array([self.Qeff_a[a]]) else: Qeff = np.empty(1, dtype=float) self.gd.comm.broadcast(Qeff, rank) all_Qeff_a[a] = Qeff # Check absolute values consistency of inverse effective charges self.assertAlmostEqual(np.abs(1./self.Z_a-np.abs(all_Qeff_a)).max(), 0, 9) # Check sum of inverse effective charges against total self.assertAlmostEqual(all_Qeff_a.sum(), self.Qtotal, 9) # Make sure that we all agree on inverse effective charges fingerprint = np.array([md5_array(all_Qeff_a, numeric=True)]) all_fingerprints = np.empty(world.size, fingerprint.dtype) world.all_gather(fingerprint, all_fingerprints) if all_fingerprints.ptp(0).any(): raise RuntimeError('Distributed eff. charges are not identical!') def test_overlaps_hermitian(self): # Set up Hermitian overlap operator: S = lambda x: x dS = lambda a, P_ni: np.dot(P_ni, self.setups[a].dO_ii) nblocks = self.get_optimal_number_of_blocks(self.blocking) overlap = MatrixOperator(self.ksl, nblocks, self.async, True) S_nn = overlap.calculate_matrix_elements(self.psit_nG, \ self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n> tri2full(S_nn, 'U') # upper to lower... if self.bd.comm.rank == 0: self.gd.comm.broadcast(S_nn, 0) self.bd.comm.broadcast(S_nn, 0) if memstats: self.mem_test = record_memory() self.check_and_plot(S_nn, self.S0_nn, 9, 'overlaps,hermitian') def test_overlaps_nonhermitian(self): alpha = np.random.normal(size=1).astype(self.dtype) if self.dtype == complex: alpha += 1j*np.random.normal(size=1) world.broadcast(alpha, 0) # Set up non-Hermitian overlap operator: S = lambda x: alpha*x dS = lambda a, P_ni: np.dot(alpha*P_ni, self.setups[a].dO_ii) nblocks = self.get_optimal_number_of_blocks(self.blocking) overlap = MatrixOperator(self.ksl, nblocks, self.async, False) S_nn = overlap.calculate_matrix_elements(self.psit_nG, \ self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n> if self.bd.comm.rank == 0: self.gd.comm.broadcast(S_nn, 0) self.bd.comm.broadcast(S_nn, 0) if memstats: self.mem_test = record_memory() self.check_and_plot(S_nn, alpha*self.S0_nn, 9, 'overlaps,nonhermitian') def test_trivial_cholesky(self): # Known starting point of SI_nn = <psit_m|S+alpha*I|psit_n> I_nn = np.eye(*self.S0_nn.shape) alpha = 1e-3 # shift eigenvalues away from zero SI_nn = self.S0_nn + alpha * I_nn # Try Cholesky decomposition SI_nn = L_nn * L_nn^dag L_nn = np.linalg.cholesky(SI_nn) # |psit_n> -> C_nn |psit_n> , C_nn^(-1) = L_nn^dag # <psit_m|SI|psit_n> -> <psit_m|C_nn^dag SI C_nn|psit_n> = diag(W_n) C_nn = np.linalg.inv(L_nn.T.conj()) # Set up Hermitian overlap operator: S = lambda x: x dS = lambda a, P_ni: np.dot(P_ni, self.setups[a].dO_ii) nblocks = self.get_optimal_number_of_blocks(self.blocking) overlap = MatrixOperator(self.ksl, nblocks, self.async, True) self.psit_nG = overlap.matrix_multiply(C_nn.T.copy(), self.psit_nG, self.P_ani) D_nn = overlap.calculate_matrix_elements(self.psit_nG, \ self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n> tri2full(D_nn, 'U') # upper to lower... if self.bd.comm.rank == 0: self.gd.comm.broadcast(D_nn, 0) self.bd.comm.broadcast(D_nn, 0) if memstats: self.mem_test = record_memory() # D_nn = C_nn^dag * S_nn * C_nn = I_nn - alpha * C_nn^dag * C_nn D0_nn = I_nn - alpha * np.dot(C_nn.T.conj(), C_nn) self.check_and_plot(D_nn, D0_nn, 6, 'trivial,cholesky') #XXX precision def test_trivial_diagonalize(self): # Known starting point of S_nn = <psit_m|S|psit_n> S_nn = self.S0_nn # Eigenvector decomposition S_nn = V_nn * W_nn * V_nn^dag # Utilize the fact that they are analytically known (cf. Maple) band_indices = np.arange(self.nbands) V_nn = np.eye(self.nbands).astype(self.dtype) if self.dtype == complex: V_nn[1:,1] = np.conj(self.gamma)**band_indices[1:] * band_indices[1:]**0.5 V_nn[1,2:] = -self.gamma**band_indices[1:-1] * band_indices[2:]**0.5 else: V_nn[2:,1] = band_indices[2:]**0.5 V_nn[1,2:] = -band_indices[2:]**0.5 W_n = np.zeros(self.nbands).astype(self.dtype) W_n[1] = (1. + self.Qtotal) * self.nbands * (self.nbands - 1) / 2. # Find the inverse basis Vinv_nn = np.linalg.inv(V_nn) # Test analytical eigenvectors for consistency against analytical S_nn D_nn = np.dot(Vinv_nn, np.dot(S_nn, V_nn)) self.assertAlmostEqual(np.abs(D_nn.diagonal()-W_n).max(), 0, 8) self.assertAlmostEqual(np.abs(np.tril(D_nn, -1)).max(), 0, 4) self.assertAlmostEqual(np.abs(np.triu(D_nn, 1)).max(), 0, 4) del Vinv_nn, D_nn # Perform Gram Schmidt orthonormalization for diagonalization # |psit_n> -> C_nn |psit_n>, using orthonormalized basis Q_nn # <psit_m|S|psit_n> -> <psit_m|C_nn^dag S C_nn|psit_n> = diag(W_n) # using S_nn = V_nn * W_nn * V_nn^(-1) = Q_nn * W_nn * Q_nn^dag C_nn = V_nn.copy() gram_schmidt(C_nn) self.assertAlmostEqual(np.abs(np.dot(C_nn.T.conj(), C_nn) \ - np.eye(self.nbands)).max(), 0, 6) # Set up Hermitian overlap operator: S = lambda x: x dS = lambda a, P_ni: np.dot(P_ni, self.setups[a].dO_ii) nblocks = self.get_optimal_number_of_blocks(self.blocking) overlap = MatrixOperator(self.ksl, nblocks, self.async, True) self.psit_nG = overlap.matrix_multiply(C_nn.T.copy(), self.psit_nG, self.P_ani) D_nn = overlap.calculate_matrix_elements(self.psit_nG, \ self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n> tri2full(D_nn, 'U') # upper to lower... if self.bd.comm.rank == 0: self.gd.comm.broadcast(D_nn, 0) self.bd.comm.broadcast(D_nn, 0) if memstats: self.mem_test = record_memory() # D_nn = C_nn^dag * S_nn * C_nn = W_n since Q_nn^dag = Q_nn^(-1) D0_nn = np.dot(C_nn.T.conj(), np.dot(S_nn, C_nn)) self.assertAlmostEqual(np.abs(D0_nn-np.diag(W_n)).max(), 0, 9) self.check_and_plot(D_nn, D0_nn, 9, 'trivial,diagonalize') def test_multiply_randomized(self): # Known starting point of S_nn = <psit_m|S|psit_n> S_nn = self.S0_nn if self.dtype == complex: C_nn = np.random.uniform(size=self.nbands**2) * \ np.exp(1j*np.random.uniform(0,2*np.pi,size=self.nbands**2)) else: C_nn = np.random.normal(size=self.nbands**2) C_nn = C_nn.reshape((self.nbands,self.nbands)) / np.linalg.norm(C_nn,2) world.broadcast(C_nn, 0) # Set up Hermitian overlap operator: S = lambda x: x dS = lambda a, P_ni: np.dot(P_ni, self.setups[a].dO_ii) nblocks = self.get_optimal_number_of_blocks(self.blocking) overlap = MatrixOperator(self.ksl, nblocks, self.async, True) self.psit_nG = overlap.matrix_multiply(C_nn.T.copy(), self.psit_nG, self.P_ani) D_nn = overlap.calculate_matrix_elements(self.psit_nG, \ self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n> tri2full(D_nn, 'U') # upper to lower... if self.bd.comm.rank == 0: self.gd.comm.broadcast(D_nn, 0) self.bd.comm.broadcast(D_nn, 0) if memstats: self.mem_test = record_memory() # D_nn = C_nn^dag * S_nn * C_nn D0_nn = np.dot(C_nn.T.conj(), np.dot(S_nn, C_nn)) self.check_and_plot(D_nn, D0_nn, 9, 'multiply,randomized') def test_multiply_nonhermitian(self): alpha = np.random.normal(size=1).astype(self.dtype) if self.dtype == complex: alpha += 1j*np.random.normal(size=1) world.broadcast(alpha, 0) # Known starting point of S_nn = <psit_m|S|psit_n> S_nn = alpha*self.S0_nn if self.dtype == complex: C_nn = np.random.uniform(size=self.nbands**2) * \ np.exp(1j*np.random.uniform(0,2*np.pi,size=self.nbands**2)) else: C_nn = np.random.normal(size=self.nbands**2) C_nn = C_nn.reshape((self.nbands,self.nbands)) / np.linalg.norm(C_nn,2) world.broadcast(C_nn, 0) # Set up non-Hermitian overlap operator: S = lambda x: alpha*x dS = lambda a, P_ni: np.dot(alpha*P_ni, self.setups[a].dO_ii) nblocks = self.get_optimal_number_of_blocks(self.blocking) overlap = MatrixOperator(self.ksl, nblocks, self.async, False) self.psit_nG = overlap.matrix_multiply(C_nn.T.copy(), self.psit_nG, self.P_ani) D_nn = overlap.calculate_matrix_elements(self.psit_nG, \ self.P_ani, S, dS).T.copy() # transpose to get <psit_m|A|psit_n> if self.bd.comm.rank == 0: self.gd.comm.broadcast(D_nn, 0) self.bd.comm.broadcast(D_nn, 0) if memstats: self.mem_test = record_memory() # D_nn = C_nn^dag * S_nn * C_nn D0_nn = np.dot(C_nn.T.conj(), np.dot(S_nn, C_nn)) self.check_and_plot(D_nn, D0_nn, 9, 'multiply,nonhermitian')
def get_all_electron_density(self, atoms=None, gridrefinement=2, spos_ac=None, skip_core=False): """Return real all-electron density array. Usage: Either get_all_electron_density(atoms) or get_all_electron_density(spos_ac=spos_ac) skip_core=True theoretically returns the all-electron valence density (use with care; will not in general integrate to valence) """ if spos_ac is None: spos_ac = atoms.get_scaled_positions() % 1.0 # Refinement of coarse grid, for representation of the AE-density # XXXXXXXXXXXX think about distribution depending on gridrefinement! if gridrefinement == 1: gd = self.redistributor.aux_gd n_sg = self.nt_sG.copy() # This will get the density with the same distribution # as finegd: n_sg = self.redistributor.distribute(n_sg) elif gridrefinement == 2: gd = self.finegd if self.nt_sg is None: self.interpolate_pseudo_density() n_sg = self.nt_sg.copy() elif gridrefinement == 4: # Extra fine grid gd = self.finegd.refine() # Interpolation function for the density: interpolator = Transformer(self.finegd, gd, 3) # XXX grids! # Transfer the pseudo-density to the fine grid: n_sg = gd.empty(self.nspins) if self.nt_sg is None: self.interpolate_pseudo_density() for s in range(self.nspins): interpolator.apply(self.nt_sg[s], n_sg[s]) else: raise NotImplementedError # Add corrections to pseudo-density to get the AE-density splines = {} phi_aj = [] phit_aj = [] nc_a = [] nct_a = [] for a, id in enumerate(self.setups.id_a): if id in splines: phi_j, phit_j, nc, nct = splines[id] else: # Load splines: phi_j, phit_j, nc, nct = self.setups[a].get_partial_waves()[:4] splines[id] = (phi_j, phit_j, nc, nct) phi_aj.append(phi_j) phit_aj.append(phit_j) nc_a.append([nc]) nct_a.append([nct]) # Create localized functions from splines phi = BasisFunctions(gd, phi_aj) phit = BasisFunctions(gd, phit_aj) nc = LFC(gd, nc_a) nct = LFC(gd, nct_a) phi.set_positions(spos_ac) phit.set_positions(spos_ac) nc.set_positions(spos_ac) nct.set_positions(spos_ac) I_sa = np.zeros((self.nspins, len(spos_ac))) a_W = np.empty(len(phi.M_W), np.intc) W = 0 for a in phi.atom_indices: nw = len(phi.sphere_a[a].M_w) a_W[W:W + nw] = a W += nw x_W = phi.create_displacement_arrays()[0] D_asp = self.D_asp # XXX really? rho_MM = np.zeros((phi.Mmax, phi.Mmax)) for s, I_a in enumerate(I_sa): M1 = 0 for a, setup in enumerate(self.setups): ni = setup.ni D_sp = D_asp.get(a) if D_sp is None: D_sp = np.empty((self.nspins, ni * (ni + 1) // 2)) else: I_a[a] = ( (setup.Nct) / self.nspins - sqrt(4 * pi) * np.dot(D_sp[s], setup.Delta_pL[:, 0])) if not skip_core: I_a[a] -= setup.Nc / self.nspins if gd.comm.size > 1: gd.comm.broadcast(D_sp, D_asp.partition.rank_a[a]) M2 = M1 + ni rho_MM[M1:M2, M1:M2] = unpack2(D_sp[s]) M1 = M2 assert np.all(n_sg[s].shape == phi.gd.n_c) phi.lfc.ae_valence_density_correction(rho_MM, n_sg[s], a_W, I_a, x_W) phit.lfc.ae_valence_density_correction(-rho_MM, n_sg[s], a_W, I_a, x_W) a_W = np.empty(len(nc.M_W), np.intc) W = 0 for a in nc.atom_indices: nw = len(nc.sphere_a[a].M_w) a_W[W:W + nw] = a W += nw scale = 1.0 / self.nspins for s, I_a in enumerate(I_sa): if not skip_core: nc.lfc.ae_core_density_correction(scale, n_sg[s], a_W, I_a) nct.lfc.ae_core_density_correction(-scale, n_sg[s], a_W, I_a) gd.comm.sum(I_a) N_c = gd.N_c g_ac = np.around(N_c * spos_ac).astype(int) % N_c - gd.beg_c if not skip_core: for I, g_c in zip(I_a, g_ac): if (g_c >= 0).all() and (g_c < gd.n_c).all(): n_sg[s][tuple(g_c)] -= I / gd.dv return n_sg, gd
class HybridXC(XCFunctional): orbital_dependent = True def __init__( self, name, hybrid=None, xc=None, finegrid=False, alpha=None, skip_gamma=False, gygi=False, acdf=True, qsym=True, txt=None, ecut=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. 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.alpha = alpha self.qsym = qsym self.skip_gamma = skip_gamma self.gygi = gygi self.acdf = acdf self.exx = None self.ecut = ecut if txt is None: if rank == 0: # self.txt = devnull self.txt = sys.stdout else: sys.stdout = devnull self.txt = devnull else: assert type(txt) is str from ase.parallel import paropen self.txt = paropen(txt, "w") XCFunctional.__init__(self, name) def get_setup_name(self): return "PBE" def calculate_radial(self, rgd, n_sLg, Y_L, v_sg, dndr_sLg=None, rnablaY_Lv=None, tau_sg=None, dedtau_sg=None): return self.xc.calculate_radial(rgd, n_sLg, Y_L, v_sg, dndr_sLg, rnablaY_Lv) def calculate_paw_correction(self, setup, D_sp, dEdD_sp=None, addcoredensity=True, a=None): return self.xc.calculate_paw_correction(setup, D_sp, dEdD_sp, addcoredensity, a) def initialize(self, density, hamiltonian, wfs, occupations): self.xc.initialize(density, hamiltonian, wfs, occupations) self.nspins = wfs.nspins self.setups = wfs.setups self.density = density self.kpt_u = wfs.kpt_u self.wfs = wfs self.gd = density.gd self.kd = wfs.kd self.bd = wfs.bd N_c = self.gd.N_c N = self.gd.N_c.prod() vol = self.gd.dv * N if self.alpha is None: # XXX ? self.alpha = 6 * vol ** (2 / 3.0) / pi ** 2 self.gamma = vol / (2 * pi) ** 2 * sqrt(pi / self.alpha) * self.kd.nbzkpts if self.ecut is None: self.ecut = 0.5 * pi ** 2 / (self.gd.h_cv ** 2).sum(1).max() * 0.9999 assert self.kd.N_c is not None n = self.kd.N_c * 2 - 1 bzk_kc = np.indices(n).transpose((1, 2, 3, 0)) bzk_kc.shape = (-1, 3) bzk_kc -= self.kd.N_c - 1 self.bzk_kc = bzk_kc.astype(float) / self.kd.N_c self.bzq_qc = self.kd.get_bz_q_points() if self.qsym: op_scc = self.kd.symmetry.op_scc self.ibzq_qc = self.kd.get_ibz_q_points(self.bzq_qc, op_scc)[0] self.q_weights = self.kd.q_weights * len(self.bzq_qc) else: self.ibzq_qc = self.bzq_qc self.q_weights = np.ones(len(self.bzq_qc)) self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.bzk_kc) n = 0 for k_c, Gpk2_G in zip(self.bzk_kc[:], self.G2_qG): if (k_c > -0.5).all() and (k_c <= 0.5).all(): # XXX??? if k_c.any(): self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G), Gpk2_G ** -1) else: self.gamma -= np.dot(np.exp(-self.alpha * Gpk2_G[1:]), Gpk2_G[1:] ** -1) n += 1 assert n == self.kd.N_c.prod() self.pwd = PWDescriptor(self.ecut, self.gd, complex) self.G2_qG = self.pwd.g2(self.ibzq_qc) self.ghat = LFC( self.gd, [setup.ghat_l for setup in density.setups], KPointDescriptor(self.bzq_qc), dtype=complex ) # self.interpolator = density.interpolator self.print_initialization(hamiltonian.xc.name) def set_positions(self, spos_ac): self.ghat.set_positions(spos_ac) self.spos_ac = spos_ac def calculate(self, gd, n_sg, v_sg=None, e_g=None): # Normal XC contribution: exc = self.xc.calculate(gd, n_sg, v_sg, e_g) # Add EXX contribution: return exc + self.exx def calculate_exx(self): """Non-selfconsistent calculation.""" kd = self.kd K = len(kd.bzk_kc) W = world.size // self.nspins parallel = W > 1 self.exx = 0.0 self.exx_kq = np.zeros((K, len(self.ibzq_qc)), float) for s in range(self.nspins): ibz_kpts = [KPoint(kd, kpt) for kpt in self.kpt_u if kpt.s == s] for ik, kpt in enumerate(kd.bzk_kc): print >>self.txt, "K %s %s ..." % (ik, kpt) for iq, q in enumerate(self.ibzq_qc): kpq = kd.find_k_plus_q(q, kpts_k=[ik]) self.apply(ibz_kpts[kd.bz2ibz_k[ik]], ibz_kpts[kd.bz2ibz_k[kpq[0]]], ik, kpq[0], iq) self.exx = world.sum(self.exx) self.exx += self.calculate_exx_paw_correction() exx_q = np.sum(self.exx_kq, 0) print >>self.txt print >>self.txt, "------------------------------------------------------" print >>self.txt print >>self.txt, "Contributions: q w E_q (eV)" for q in range(len(exx_q)): print >>self.txt, "[%1.3f %1.3f %1.3f] %1.3f %s" % ( self.ibzq_qc[q][0], self.ibzq_qc[q][1], self.ibzq_qc[q][2], self.q_weights[q] / len(self.bzq_qc), exx_q[q] / self.q_weights[q] * len(self.bzq_qc) * Ha, ) print >>self.txt, "E_EXX = %s eV" % (self.exx * Ha) print >>self.txt print >>self.txt, "Calculation completed at: ", ctime() print >>self.txt print >>self.txt, "------------------------------------------------------" print >>self.txt def apply(self, kpt1, kpt2, ik1, ik2, iq): k1_c = self.kd.bzk_kc[ik1] k2_c = self.kd.bzk_kc[ik2] q = self.ibzq_qc[iq] if self.qsym: for i, q in enumerate(self.bzq_qc): if abs(q - self.ibzq_qc[iq]).max() < 1e-9: bzq_index = i break else: bzq_index = iq N_c = self.gd.N_c eikr_R = np.exp(-2j * pi * np.dot(np.indices(N_c).T, q / N_c).T) Gamma = abs(q).max() < 1e-9 if Gamma and self.skip_gamma: return Gpk2_G = self.G2_qG[iq] if Gamma: Gpk2_G = Gpk2_G.copy() Gpk2_G[0] = 1.0 / self.gamma N = N_c.prod() vol = self.gd.dv * N nspins = self.nspins fcut = 1e-10 for n1, psit1_R in enumerate(kpt1.psit_nG): f1 = kpt1.f_n[n1] for n2, psit2_R in enumerate(kpt2.psit_nG): if self.acdf: if self.gygi and Gamma: # print n2, kpt2.f_n[n2]/kpt2.weight f2 = self.q_weights[iq] * kpt2.weight else: f2 = self.q_weights[iq] * kpt2.weight * (1 - np.sign(kpt2.eps_n[n2] - kpt1.eps_n[n1])) else: f2 = kpt2.f_n[n2] * self.q_weights[iq] if abs(f1) < fcut or abs(f2) < fcut: continue nt_R = self.calculate_pair_density(n1, n2, kpt1, kpt2, ik1, ik2, bzq_index) nt_G = self.pwd.fft(nt_R * eikr_R) / N vt_G = nt_G.copy() vt_G *= -pi * vol / Gpk2_G e = np.vdot(nt_G, vt_G).real * nspins * self.hybrid self.exx += f1 * f2 * e self.exx_kq[ik1, iq] += f1 * f2 * e def calculate_pair_density(self, n1, n2, kpt1, kpt2, ik1, ik2, bzq_index): psit1_G = self.kd.transform_wave_function(kpt1.psit_nG[n1], ik1) psit2_G = self.kd.transform_wave_function(kpt2.psit_nG[n2], ik2) nt_G = psit1_G.conj() * psit2_G s1 = self.kd.sym_k[ik1] s2 = self.kd.sym_k[ik2] t1 = self.kd.time_reversal_k[ik1] t2 = self.kd.time_reversal_k[ik2] k1_c = self.kd.ibzk_kc[kpt1.k] k2_c = self.kd.ibzk_kc[kpt2.k] Q_aL = {} for a in kpt1.P_ani.keys(): b1 = self.kd.symmetry.a_sa[s1, a] b2 = self.kd.symmetry.a_sa[s2, a] S1_c = np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s1]) - self.spos_ac[b1] S2_c = np.dot(self.spos_ac[a], self.kd.symmetry.op_scc[s2]) - self.spos_ac[b2] assert abs(S1_c.round() - S1_c).max() < 1e-13 assert abs(S2_c.round() - S2_c).max() < 1e-13 x1 = np.exp(2j * pi * np.dot(k1_c, S1_c)) x2 = np.exp(2j * pi * np.dot(k2_c, S2_c)) P1_i = np.dot(self.setups[a].R_sii[s1], kpt1.P_ani[b1][n1]) * x1 P2_i = np.dot(self.setups[a].R_sii[s2], kpt2.P_ani[b2][n2]) * x2 if t1: P1_i = P1_i.conj() if t2: P2_i = P2_i.conj() D_ii = np.outer(P1_i.conj(), P2_i) D_p = pack(D_ii) Q_aL[a] = np.dot(D_p, self.setups[a].Delta_pL) self.ghat.add(nt_G, Q_aL, bzq_index) return nt_G def calculate_exx_paw_correction(self): exx = 0 deg = 2 // self.nspins # spin degeneracy for a, D_sp in self.density.D_asp.items(): setup = self.setups[a] for D_p in D_sp: D_ii = unpack2(D_p) ni = len(D_ii) for i1 in range(ni): for i2 in range(ni): A = 0.0 for i3 in range(ni): p13 = packed_index(i1, i3, ni) for i4 in range(ni): p24 = packed_index(i2, i4, ni) A += setup.M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) exx -= self.hybrid / deg * D_ii[i1, i2] * A if setup.X_p is not None: exx -= self.hybrid * np.dot(D_p, setup.X_p) exx += self.hybrid * setup.ExxC return exx def print_initialization(self, xc): print >>self.txt, "------------------------------------------------------" print >>self.txt, "Non-self-consistent HF correlation energy" print >>self.txt, "------------------------------------------------------" print >>self.txt, "Started at: ", ctime() print >>self.txt print >>self.txt, "Ground state XC functional : %s" % xc print >>self.txt, "Valence electrons : %s" % self.setups.nvalence print >>self.txt, "Number of Spins : %s" % self.nspins print >>self.txt, "Plane wave cutoff energy : %4.1f eV" % (self.ecut * Ha) print >>self.txt, "Gamma q-point excluded : %s" % self.skip_gamma if not self.skip_gamma: print >>self.txt, "Alpha parameter : %s" % self.alpha print >>self.txt, "Gamma parameter : %3.3f" % self.gamma print >>self.txt, "ACDF method : %s" % self.acdf print >>self.txt, "Number of k-points : %s" % len(self.kd.bzk_kc) print >>self.txt, "Number of Irreducible k-points : %s" % len(self.kd.ibzk_kc) print >>self.txt, "Number of q-points : %s" % len(self.bzq_qc) if not self.qsym: print >>self.txt, "q-point symmetry : %s" % self.qsym else: print >>self.txt, "Number of Irreducible q-points : %s" % len(self.ibzq_qc) print >>self.txt for q, weight in zip(self.ibzq_qc, self.q_weights): print >>self.txt, "q: [%1.3f %1.3f %1.3f] - weight: %1.3f" % (q[0], q[1], q[2], weight / len(self.bzq_qc)) print >>self.txt print >>self.txt, "------------------------------------------------------" print >>self.txt, "------------------------------------------------------" print >>self.txt print >>self.txt, "Looping over k-points in the full Brillouin zone" print >>self.txt
def get_pseudo_core_kinetic_energy_density_lfc(self): return LFC(self.gd, [[setup.tauct] for setup in self.setups], forces=True, cut=True)
class Hamiltonian: """Hamiltonian object. Attributes: =============== ===================================================== ``xc`` ``XC3DGrid`` object. ``poisson`` ``PoissonSolver``. ``gd`` Grid descriptor for coarse grids. ``finegd`` Grid descriptor for fine grids. ``restrict`` Function for restricting the effective potential. =============== ===================================================== Soft and smooth pseudo functions on uniform 3D grids: ========== ========================================= ``vHt_g`` Hartree potential on the fine grid. ``vt_sG`` Effective potential on the coarse grid. ``vt_sg`` Effective potential on the fine grid. ========== ========================================= Energy contributions and forces: =========== ========================================== Description =========== ========================================== ``Ekin`` Kinetic energy. ``Epot`` Potential energy. ``Etot`` Total energy. ``Exc`` Exchange-Correlation energy. ``Eext`` Energy of external potential ``Eref`` Reference energy for all-electron atoms. ``S`` Entropy. ``Ebar`` Should be close to zero! =========== ========================================== """ def __init__(self, gd, finegd, nspins, setups, stencil, timer, xc, psolver, vext_g): """Create the Hamiltonian.""" self.gd = gd self.finegd = finegd self.nspins = nspins self.setups = setups self.timer = timer self.xc = xc # Solver for the Poisson equation: if psolver is None: psolver = PoissonSolver(nn=3, relax='J') self.poisson = psolver self.poisson.set_grid_descriptor(finegd) self.dH_asp = None # The external potential self.vext_g = vext_g self.vt_sG = None self.vHt_g = None self.vt_sg = None self.vbar_g = None self.rank_a = None # Restrictor function for the potential: self.restrictor = Transformer(self.finegd, self.gd, stencil, allocate=False) self.restrict = self.restrictor.apply self.vbar = LFC(self.finegd, [[setup.vbar] for setup in setups], forces=True) self.Ekin0 = None self.Ekin = None self.Epot = None self.Ebar = None self.Eext = None self.Exc = None self.Etot = None self.S = None self.allocated = False def allocate(self): # TODO We should move most of the gd.empty() calls here assert not self.allocated self.restrictor.allocate() self.allocated = True def set_positions(self, spos_ac, rank_a=None): self.spos_ac = spos_ac if not self.allocated: self.allocate() self.vbar.set_positions(spos_ac) if self.vbar_g is None: self.vbar_g = self.finegd.empty() self.vbar_g[:] = 0.0 self.vbar.add(self.vbar_g) self.xc.set_positions(spos_ac) # If both old and new atomic ranks are present, start a blank dict if # it previously didn't exist but it will needed for the new atoms. if (self.rank_a is not None and rank_a is not None and self.dH_asp is None and (rank_a == self.gd.comm.rank).any()): self.dH_asp = {} if self.rank_a is not None and self.dH_asp is not None: self.timer.start('Redistribute') requests = [] flags = (self.rank_a != rank_a) my_incoming_atom_indices = np.argwhere(np.bitwise_and(flags, \ rank_a == self.gd.comm.rank)).ravel() my_outgoing_atom_indices = np.argwhere(np.bitwise_and(flags, \ self.rank_a == self.gd.comm.rank)).ravel() for a in my_incoming_atom_indices: # Get matrix from old domain: ni = self.setups[a].ni dH_sp = np.empty((self.nspins, ni * (ni + 1) // 2)) requests.append(self.gd.comm.receive(dH_sp, self.rank_a[a], tag=a, block=False)) assert a not in self.dH_asp self.dH_asp[a] = dH_sp for a in my_outgoing_atom_indices: # Send matrix to new domain: dH_sp = self.dH_asp.pop(a) requests.append(self.gd.comm.send(dH_sp, rank_a[a], tag=a, block=False)) self.gd.comm.waitall(requests) self.timer.stop('Redistribute') self.rank_a = rank_a def aoom(self, DM, a, l, scale=1): """Atomic Orbital Occupation Matrix. Determine the Atomic Orbital Occupation Matrix (aoom) for a given l-quantum number. This operation, takes the density matrix (DM), which for example is given by unpack2(D_asq[i][spin]), and corrects for the overlap between the selected orbitals (l) upon which the the density is expanded (ex <p|p*>,<p|p>,<p*|p*> ). Returned is only the "corrected" part of the density matrix, which represents the orbital occupation matrix for l=2 this is a 5x5 matrix. """ S=self.setups[a] l_j = S.l_j n_j = S.n_j lq = S.lq nl = np.where(np.equal(l_j, l))[0] V = np.zeros(np.shape(DM)) if len(nl) == 2: aa = (nl[0])*len(l_j)-((nl[0]-1)*(nl[0])/2) bb = (nl[1])*len(l_j)-((nl[1]-1)*(nl[1])/2) ab = aa+nl[1]-nl[0] if(scale==0 or scale=='False' or scale =='false'): lq_a = lq[aa] lq_ab = lq[ab] lq_b = lq[bb] else: lq_a = 1 lq_ab = lq[ab]/lq[aa] lq_b = lq[bb]/lq[aa] # and the correct entrances in the DM nn = (2*np.array(l_j)+1)[0:nl[0]].sum() mm = (2*np.array(l_j)+1)[0:nl[1]].sum() # finally correct and add the four submatrices of NC_DM A = DM[nn:nn+2*l+1,nn:nn+2*l+1]*(lq_a) B = DM[nn:nn+2*l+1,mm:mm+2*l+1]*(lq_ab) C = DM[mm:mm+2*l+1,nn:nn+2*l+1]*(lq_ab) D = DM[mm:mm+2*l+1,mm:mm+2*l+1]*(lq_b) V[nn:nn+2*l+1,nn:nn+2*l+1]=+(lq_a) V[nn:nn+2*l+1,mm:mm+2*l+1]=+(lq_ab) V[mm:mm+2*l+1,nn:nn+2*l+1]=+(lq_ab) V[mm:mm+2*l+1,mm:mm+2*l+1]=+(lq_b) return A+B+C+D, V else: nn =(2*np.array(l_j)+1)[0:nl[0]].sum() A=DM[nn:nn+2*l+1,nn:nn+2*l+1]*lq[-1] V[nn:nn+2*l+1,nn:nn+2*l+1]=+lq[-1] return A,V def update(self, density): """Calculate effective potential. The XC-potential and the Hartree potential are evaluated on the fine grid, and the sum is then restricted to the coarse grid.""" self.timer.start('Hamiltonian') if self.vt_sg is None: self.timer.start('Initialize Hamiltonian') self.vt_sg = self.finegd.empty(self.nspins) self.vHt_g = self.finegd.zeros() self.vt_sG = self.gd.empty(self.nspins) self.poisson.initialize() self.timer.stop('Initialize Hamiltonian') 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_g is not None: vt_g += self.vext_g.get_potential(self.finegd) Eext = self.finegd.integrate(vt_g, density.nt_g, global_integral=False) - Ebar if self.nspins == 2: self.vt_sg[1] = vt_g 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 for vt_g, vt_G, nt_G in zip(self.vt_sg, self.vt_sG, density.nt_sG): vt_g += self.vHt_g self.restrict(vt_g, vt_G) Ekin -= self.gd.integrate(vt_G, nt_G - density.nct_G, global_integral=False) self.timer.stop('Hartree integrate/restrict') # Calculate atomic hamiltonians: self.timer.start('Atomic') 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) self.dH_asp = {} for a, D_sp in density.D_asp.items(): W_L = W_aL[a] setup = self.setups[a] D_p = D_sp.sum(0) dH_p = (setup.K_p + setup.M_p + setup.MB_p + 2.0 * np.dot(setup.M_pp, D_p) + np.dot(setup.Delta_pL, W_L)) Ekin += np.dot(setup.K_p, D_p) + setup.Kc Ebar += setup.MB + np.dot(setup.MB_p, D_p) Epot += setup.M + np.dot(D_p, (setup.M_p + np.dot(setup.M_pp, D_p))) if self.vext_g is not None: vext = self.vext_g.get_taylor(spos_c=self.spos_ac[a, :]) # Tailor expansion to the zeroth order Eext += vext[0][0] * (sqrt(4 * pi) * density.Q_aL[a][0] + setup.Z) dH_p += vext[0][0] * sqrt(4 * pi) * setup.Delta_pL[:, 0] if len(vext) > 1: # Tailor expansion to the first order Eext += sqrt(4 * pi / 3) * np.dot(vext[1], density.Q_aL[a][1:4]) # there must be a better way XXXX Delta_p1 = np.array([setup.Delta_pL[:, 1], setup.Delta_pL[:, 2], setup.Delta_pL[:, 3]]) dH_p += sqrt(4 * pi / 3) * np.dot(vext[1], Delta_p1) self.dH_asp[a] = dH_sp = np.zeros_like(D_sp) self.timer.start('XC Correction') Exc += setup.xc_correction.calculate(self.xc, D_sp, dH_sp, a) self.timer.stop('XC Correction') if setup.HubU is not None: nspins = len(D_sp) l_j = setup.l_j l = setup.Hubl nl = np.where(np.equal(l_j,l))[0] nn = (2*np.array(l_j)+1)[0:nl[0]].sum() for D_p, H_p in zip(D_sp, self.dH_asp[a]): [N_mm,V] =self.aoom(unpack2(D_p),a,l) N_mm = N_mm / 2 * nspins Eorb = setup.HubU / 2. * (N_mm - np.dot(N_mm,N_mm)).trace() Vorb = setup.HubU * (0.5 * np.eye(2*l+1) - N_mm) Exc += Eorb if nspins == 1: # add contribution of other spin manyfold Exc += Eorb if len(nl)==2: mm = (2*np.array(l_j)+1)[0:nl[1]].sum() V[nn:nn+2*l+1,nn:nn+2*l+1] *= Vorb V[mm:mm+2*l+1,nn:nn+2*l+1] *= Vorb V[nn:nn+2*l+1,mm:mm+2*l+1] *= Vorb V[mm:mm+2*l+1,mm:mm+2*l+1] *= Vorb else: V[nn:nn+2*l+1,nn:nn+2*l+1] *= Vorb Htemp = unpack(H_p) Htemp += V H_p[:] = pack2(Htemp) dH_sp += dH_p Ekin -= (D_sp * dH_sp).sum() self.timer.stop('Atomic') # Make corrections due to non-local xc: #xcfunc = self.xc.xcfunc self.Enlxc = 0.0#XXXxcfunc.get_non_local_energy() Ekin += self.xc.get_kinetic_energy_correction() / self.gd.comm.size energies = np.array([Ekin, Epot, Ebar, Eext, Exc]) self.timer.start('Communicate energies') self.gd.comm.sum(energies) self.timer.stop('Communicate energies') (self.Ekin0, self.Epot, self.Ebar, self.Eext, self.Exc) = energies #self.Exc += self.Enlxc #self.Ekin0 += self.Enlkin self.timer.stop('Hamiltonian') def get_energy(self, occupations): self.Ekin = self.Ekin0 + occupations.e_band self.S = occupations.e_entropy # Total free energy: self.Etot = (self.Ekin + self.Epot + self.Eext + self.Ebar + self.Exc - self.S) return self.Etot def apply_local_potential(self, psit_nG, Htpsit_nG, s): """Apply the Hamiltonian operator to a set of vectors. XXX Parameter description is deprecated! Parameters: a_nG: ndarray Set of vectors to which the overlap operator is applied. b_nG: ndarray, output Resulting H times a_nG vectors. kpt: KPoint object k-point object defined in kpoint.py. calculate_projections: bool When True, the integrals of projector times vectors P_ni = <p_i | a_nG> are calculated. When False, existing P_uni are used local_part_only: bool When True, the non-local atomic parts of the Hamiltonian are not applied and calculate_projections is ignored. """ vt_G = self.vt_sG[s] if psit_nG.ndim == 3: Htpsit_nG += psit_nG * vt_G else: for psit_G, Htpsit_G in zip(psit_nG, Htpsit_nG): Htpsit_G += psit_G * vt_G def apply(self, a_xG, b_xG, wfs, kpt, calculate_P_ani=True): """Apply the Hamiltonian operator to a set of vectors. Parameters: a_nG: ndarray Set of vectors to which the overlap operator is applied. b_nG: ndarray, output Resulting S times a_nG vectors. wfs: WaveFunctions Wave-function object defined in wavefunctions.py kpt: KPoint object k-point object defined in kpoint.py. calculate_P_ani: bool When True, the integrals of projector times vectors P_ni = <p_i | a_nG> are calculated. When False, existing P_ani are used """ wfs.kin.apply(a_xG, b_xG, kpt.phase_cd) self.apply_local_potential(a_xG, b_xG, kpt.s) shape = a_xG.shape[:-3] P_axi = wfs.pt.dict(shape) if calculate_P_ani: #TODO calculate_P_ani=False is experimental wfs.pt.integrate(a_xG, P_axi, kpt.q) else: for a, P_ni in kpt.P_ani.items(): P_axi[a][:] = P_ni for a, P_xi in P_axi.items(): dH_ii = unpack(self.dH_asp[a][kpt.s]) P_axi[a] = np.dot(P_xi, dH_ii) wfs.pt.add(b_xG, P_axi, kpt.q) def get_xc_difference(self, xc, density): """Calculate non-selfconsistent XC-energy difference.""" if density.nt_sg is None: density.interpolate() nt_sg = density.nt_sg if hasattr(xc, 'hybrid'): xc.calculate_exx() Exc = xc.calculate(density.finegd, nt_sg) / self.gd.comm.size for a, D_sp in density.D_asp.items(): setup = self.setups[a] Exc += setup.xc_correction.calculate(xc, D_sp) Exc = self.gd.comm.sum(Exc) return Exc - self.Exc def get_vxc(self, density, wfs): """Calculate matrix elements of the xc-potential.""" dtype = wfs.dtype nbands = wfs.nbands nu = len(wfs.kpt_u) if density.nt_sg is None: density.interpolate() # Allocate space for result matrix Vxc_unn = np.empty((nu, nbands, nbands), dtype=dtype) # Get pseudo xc potential on the coarse grid Vxct_sG = self.gd.empty(self.nspins) Vxct_sg = self.finegd.zeros(self.nspins) if nspins == 1: self.xc.get_energy_and_potential(density.nt_sg[0], Vxct_sg[0]) else: self.xc.get_energy_and_potential(density.nt_sg[0], Vxct_sg[0], density.nt_sg[1], Vxct_sg[1]) for Vxct_G, Vxct_g in zip(Vxct_sG, Vxct_sg): self.restrict(Vxct_g, Vxct_G) del Vxct_sg # Get atomic corrections to the xc potential Vxc_asp = {} for a, D_sp in density.D_asp.items(): Vxc_asp[a] = np.zeros_like(D_sp) self.setups[a].xc_correction.calculate_energy_and_derivatives( D_sp, Vxc_asp[a]) # Project potential onto the eigenstates for kpt, Vxc_nn in xip(wfs.kpt_u, Vxc_unn): s, q = kpt.s, kpt.q psit_nG = kpt.psit_nG # Project pseudo part r2k(.5 * self.gd.dv, psit_nG, Vxct_sG[s] * psit_nG, 0.0, Vxc_nn) tri2full(Vxc_nn, 'L') self.gd.comm.sum(Vxc_nn) # Add atomic corrections # H_ij = \int dr phi_i(r) Ĥ phi_j^*(r) # P_ni = \int dr psi_n(r) pt_i^*(r) # Vxc_nm = \int dr phi_n(r) vxc(r) phi_m^*(r) # + sum_ij P_ni H_ij P_mj^* for a, P_ni in kpt.P_ani.items(): Vxc_ii = unpack(Vxc_asp[a][s]) Vxc_nn += np.dot(P_ni, np.inner(H_ii, P_ni).conj()) return Vxc_unn def estimate_memory(self, mem): nbytes = self.gd.bytecount() nfinebytes = self.finegd.bytecount() arrays = mem.subnode('Arrays', 0) arrays.subnode('vHt_g', nfinebytes) arrays.subnode('vt_sG', self.nspins * nbytes) arrays.subnode('vt_sg', self.nspins * nfinebytes) self.restrictor.estimate_memory(mem.subnode('Restrictor')) self.xc.estimate_memory(mem.subnode('XC')) self.poisson.estimate_memory(mem.subnode('Poisson')) self.vbar.estimate_memory(mem.subnode('vbar'))