def initialize(self, density, hamiltonian, wfs, occ=None): assert wfs.kd.gamma assert not wfs.gd.pbc_c.any() assert wfs.bd.comm.size == 1 # band parallelization unsupported 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 self.finegd = density.gd.refine() if self.finegrid else density.gd self.ghat = LFC(self.finegd, [setup.ghat_l for setup in density.setups], integral=np.sqrt(4 * np.pi), forces=True) poissonsolver = PoissonSolver(eps=1e-14) poissonsolver.set_grid_descriptor(self.finegd) 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 load(self, method): """Make sure all necessary attributes have been initialized""" assert method in ('real', 'recip_gauss', 'recip_ewald'),\ str(method) + ' is an invalid method name,\n' +\ 'use either real, recip_gauss, or recip_ewald' if method.startswith('recip'): if self.gd.comm.size > 1: raise RuntimeError("Cannot do parallel FFT, use method='real'") if not hasattr(self, 'k2'): self.k2, self.N3 = construct_reciprocal(self.gd) if method.endswith('ewald') and not hasattr(self, 'ewald'): # cutoff radius assert self.gd.orthogonal rc = 0.5 * np.average(self.gd.cell_cv.diagonal()) # ewald potential: 1 - cos(k rc) self.ewald = (np.ones(self.gd.n_c) - np.cos(np.sqrt(self.k2) * rc)) # lim k -> 0 ewald / k2 self.ewald[0, 0, 0] = 0.5 * rc**2 elif method.endswith('gauss') and not hasattr(self, 'ng'): gauss = Gaussian(self.gd) self.ng = gauss.get_gauss(0) / sqrt(4 * pi) self.vg = gauss.get_gauss_pot(0) / sqrt(4 * pi) else: # method == 'real' if not hasattr(self, 'solve'): if self.poisson is not None: self.solve = self.poisson.solve else: solver = PoissonSolver('fd', nn=2) solver.set_grid_descriptor(self.gd) #solver.initialize() self.solve = solver.solve
def load(self, method): """Make sure all necessary attributes have been initialized""" assert method in ('real', 'recip_gauss', 'recip_ewald'),\ str(method) + ' is an invalid method name,\n' +\ 'use either real, recip_gauss, or recip_ewald' if method.startswith('recip'): if self.gd.comm.size > 1: raise RuntimeError("Cannot do parallel FFT, use method='real'") if not hasattr(self, 'k2'): self.k2, self.N3 = construct_reciprocal(self.gd) if method.endswith('ewald') and not hasattr(self, 'ewald'): # cutoff radius assert self.gd.orthogonal rc = 0.5 * np.average(self.gd.cell_cv.diagonal()) # ewald potential: 1 - cos(k rc) self.ewald = (np.ones(self.gd.n_c) - np.cos(np.sqrt(self.k2) * rc)) # lim k -> 0 ewald / k2 self.ewald[0, 0, 0] = 0.5 * rc**2 elif method.endswith('gauss') and not hasattr(self, 'ng'): gauss = Gaussian(self.gd) self.ng = gauss.get_gauss(0) / sqrt(4 * pi) self.vg = gauss.get_gauss_pot(0) / sqrt(4 * pi) else: # method == 'real' if not hasattr(self, 'solve'): if self.poisson is not None: self.solve = self.poisson.solve else: solver = PoissonSolver(nn=2) solver.set_grid_descriptor(self.gd) solver.initialize(load_gauss=True) self.solve = solver.solve
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.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 calculate_field( gd, rho_g, bgef_v, phi_g, ef_vg, fe_g, # preallocated numpy arrays nv=3, poisson_nn=3, poisson_relax='J', gradient_n=3, poisson_eps=1e-20): dtype = rho_g.dtype yes_complex = dtype == complex phi_g[:] = 0.0 ef_vg[:] = 0.0 fe_g[:] = 0.0 tmp_g = gd.zeros(dtype=float) # Poissonsolver poissonsolver = PoissonSolver(nn=poisson_nn, relax=poisson_relax, eps=poisson_eps) poissonsolver.set_grid_descriptor(gd) poissonsolver.initialize() # Potential, real part poissonsolver.solve(tmp_g, rho_g.real.copy()) phi_g += tmp_g # Potential, imag part if yes_complex: tmp_g[:] = 0.0 poissonsolver.solve(tmp_g, rho_g.imag.copy()) phi_g += 1.0j * tmp_g # Gradient gradient = [Gradient(gd, v, scale=1.0, n=gradient_n) for v in range(nv)] for v in range(nv): # Electric field, real part gradient[v].apply(-phi_g.real, tmp_g) ef_vg[v] += tmp_g # Electric field, imag part if yes_complex: gradient[v].apply(-phi_g.imag, tmp_g) ef_vg[v] += 1.0j * tmp_g # Electric field enhancement tmp_g[:] = 0.0 # total electric field norm bgefnorm = 0.0 # background electric field norm for v in range(nv): tmp_g += np.absolute(bgef_v[v] + ef_vg[v])**2 bgefnorm += np.absolute(bgef_v[v])**2 tmp_g = np.sqrt(tmp_g) bgefnorm = np.sqrt(bgefnorm) fe_g[:] = tmp_g / bgefnorm
def __init__(self, z1, z2, **kwargs): """Put the positive background charge where z1 < z < z2. z1: float Position of lower surface in Angstrom units. z2: float Position of upper surface in Angstrom units.""" PoissonSolver.__init__(self, **kwargs) self.z1 = (z1 - 0.0001) / Bohr self.z2 = (z2 - 0.0001) / Bohr
def solve(self): hamiltonian = self.calculator.hamiltonian self.poisson = PoissonSolver('fd', nn=hamiltonian.poisson.nn) self.poisson.set_grid_descriptor(self.gd) self.poisson.initialize() corrt_G = self.gd.empty() self.poisson.solve(corrt_G, self.vt_G, charge=None) corrt_G /= (2 * pi)**(5. / 2) self.corrt_G = corrt_G
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)
class CoulombNEW: 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 calculate(self, nt1_G, nt2_G, P1_ap, P2_ap): I = 0.0 self.rhot1_G[:] = nt1_G self.rhot2_G[:] = nt2_G Q1_aL = {} Q2_aL = {} for a, P1_p in P1_ap.items(): P2_p = P2_ap[a] setup = self.setups[a] # Add atomic corrections to integral I += 2 * np.dot(P1_p, np.dot(setup.M_pp, P2_p)) # Add compensation charges to pseudo densities Q1_aL[a] = np.dot(P1_p, setup.Delta_pL) Q2_aL[a] = np.dot(P2_p, setup.Delta_pL) self.Ghat.add(self.rhot1_G, Q1_aL) self.Ghat.add(self.rhot2_G, Q2_aL) # Add coulomb energy of compensated pseudo densities to integral self.poisson.solve(self.pot_G, self.rhot2_G, charge=None, eps=1e-12, zero_initial_phi=True) I += np.vdot(self.rhot1_G, self.pot_G) * self.dv return I * Hartree
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.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 __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 prepare(setups): calc = GPAW(basis={'H' : b}, mode='lcao', setups=setups, h=0.2, poissonsolver=PoissonSolver('M', relax='GS', eps=1e-5), spinpol=False, nbands=1) system.set_calculator(calc) return calc
def get_calculator(): calc = GPAW(h=0.2, mode = 'lcao', basis = 'szp(dzp)', mixer=Mixer(beta=0.1, nmaxold=5, weight=50.0), poissonsolver=PoissonSolver(nn='M', relax='GS'), txt='C5H12.txt') return calc
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(nn=3) self.poisson.set_grid_descriptor(gd) self.poisson.initialize() 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 solve(self): hamiltonian = self.calculator.hamiltonian self.poisson = PoissonSolver(nn=hamiltonian.poisson.nn) self.poisson.set_grid_descriptor(self.gd) self.poisson.initialize() corrt_G = self.gd.empty() self.poisson.solve(corrt_G, self.vt_G, charge=None) corrt_G /= (2 * pi)**(5. / 2) self.corrt_G = corrt_G
def initialize(self, density, hamiltonian, wfs, occ=None): assert wfs.gamma 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.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_calculator(): calc = GPAW(gpts=(64, 64, 64), #h=0.18, gives 64x60x60 mode='lcao', basis='szp(dzp)', nbands=-5, xc='LDA', width=0.1, mixer=Mixer(beta=0.1, nmaxold=5, weight=50.0), poissonsolver=PoissonSolver(nn='M', relax='GS'), convergence={'energy': 1e-4, 'bands': -3}, stencils=(3, 3), txt='nanoparticle.txt') return calc
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 get_calculator(): basis = 'szp(dzp)' calc = GPAW( gpts=(64, 64, 128), # ca h=0.25 width=0.1, nbands=-5, xc='LDA', mode='lcao', txt='C2_Cu100.txt', mixer=Mixer(beta=0.1, nmaxold=5, weight=50.0), poissonsolver=PoissonSolver(nn='M', relax='GS'), stencils=(3, 3), maxiter=400, basis=basis) return calc
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 calc_eng_homo_lumo(atoms): # set gpaw calculator calc = GPAW( h=0.16, kpts=(1, 1, 1), xc='B3LYP', occupations=FermiDirac(width=0.1), # eigensolver=Davidson(3), eigensolver=RMMDIIS(), poissonsolver=PoissonSolver(eps=1e-12), maxiter=2000) atoms.calc = calc energy = atoms.get_potential_energy() print(energy) (h**o, lumo) = calc.get_homo_lumo() print("h**o: {} and LUMO: {}".format(h**o, lumo)) return h**o, lumo
class ZerothOrder1(State): def __init__(self, calculator): self.calculator = calculator State.__init__(self, calculator.gd) self.pw = PlaneWave(self.gd) # get effective potential hamiltonian = self.calculator.hamiltonian if 1: self.vt_G = hamiltonian.vt_sG[0] # XXX treat spin else: self.vt_G = np.where(hamiltonian.vt_sG[0] > 0, 0.0, hamiltonian.vt_sG[0]) self.intvt = self.gd.integrate(self.vt_G) print('# int(vt_G)=', self.intvt, np.sometrue(self.vt_G > 0)) self.solve() def get_grid(self, k_c, r0): print('Correction:', self.gd.integrate(self.corrt_G)) if not hasattr(self, 'written'): print('r0', r0, r0 * Bohr) dR = 0.3 AI = AngularIntegral(r0 * Bohr, self.gd, dR=dR) r_R = AI.radii() psi_R = AI.average(self.corrt_G) v_R = AI.average(self.vt_G) f = open('radial_dR' + str(dR) + '.dat', 'w') print('# R v(R) psi(R)', file=f) for r, psi, v in zip(r_R, psi_R, v_R): print(r, psi, v, file=f) f.close() self.written = True return self.pw.get_grid(k_c, r0) - 1e6 * self.corrt_G def solve(self): hamiltonian = self.calculator.hamiltonian self.poisson = PoissonSolver(nn=hamiltonian.poisson.nn) self.poisson.set_grid_descriptor(self.gd) self.poisson.initialize() corrt_G = self.gd.empty() self.poisson.solve(corrt_G, self.vt_G, charge=None) corrt_G /= (2 * pi) ** (5. / 2) self.corrt_G = corrt_G
class ZerothOrder1(State): def __init__(self, calculator): self.calculator = calculator State.__init__(self, calculator.gd) self.pw = PlaneWave(self.gd) # get effective potential hamiltonian = self.calculator.hamiltonian if 1: self.vt_G = hamiltonian.vt_sG[0] # XXX treat spin else: self.vt_G = np.where(hamiltonian.vt_sG[0] > 0, 0.0, hamiltonian.vt_sG[0]) self.intvt = self.gd.integrate(self.vt_G) print('# int(vt_G)=', self.intvt, np.sometrue(self.vt_G > 0)) self.solve() def get_grid(self, k_c, r0): print('Correction:', self.gd.integrate(self.corrt_G)) if not hasattr(self, 'written'): print('r0', r0, r0 * Bohr) dR = 0.3 AI = AngularIntegral(r0 * Bohr, self.gd, dR=dR) r_R = AI.radii() psi_R = AI.average(self.corrt_G) v_R = AI.average(self.vt_G) f = open('radial_dR' + str(dR) + '.dat', 'w') print('# R v(R) psi(R)', file=f) for r, psi, v in zip(r_R, psi_R, v_R): print(r, psi, v, file=f) f.close() self.written = True return self.pw.get_grid(k_c, r0) - 1e6 * self.corrt_G def solve(self): hamiltonian = self.calculator.hamiltonian self.poisson = PoissonSolver('fd', nn=hamiltonian.poisson.nn) self.poisson.set_grid_descriptor(self.gd) self.poisson.initialize() corrt_G = self.gd.empty() self.poisson.solve(corrt_G, self.vt_G, charge=None) corrt_G /= (2 * pi)**(5. / 2) self.corrt_G = corrt_G
class CoulombNEW: 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(nn=3) self.poisson.set_grid_descriptor(gd) self.poisson.initialize() 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 calculate(self, nt1_G, nt2_G, P1_ap, P2_ap): I = 0.0 self.rhot1_G[:] = nt1_G self.rhot2_G[:] = nt2_G Q1_aL = {} Q2_aL = {} for a, P1_p in P1_ap.items(): P2_p = P2_ap[a] setup = self.setups[a] # Add atomic corrections to integral I += 2 * np.dot(P1_p, np.dot(setup.M_pp, P2_p)) # Add compensation charges to pseudo densities Q1_aL[a] = np.dot(P1_p, setup.Delta_pL) Q2_aL[a] = np.dot(P2_p, setup.Delta_pL) self.Ghat.add(self.rhot1_G, Q1_aL) self.Ghat.add(self.rhot2_G, Q2_aL) # Add coulomb energy of compensated pseudo densities to integral self.poisson.solve(self.pot_G, self.rhot2_G, charge=None, eps=1e-12, zero_initial_phi=True) I += np.vdot(self.rhot1_G, self.pot_G) * self.dv return I * Hartree
basis = BasisMaker('Na').generate(1, 1, energysplit=0.3) atoms = Atoms('Na12', pbc=(1, 1, 1), cell=[L, L, 12 * a]) atoms.positions[:12, 2] = [i * a for i in range(12)] atoms.positions[:, :2] = L / 2. atoms.center() pl_atoms1 = range(4) pl_atoms2 = range(8, 12) pl_cell1 = (L, L, 4 * a) pl_cell2 = pl_cell1 t = Transport(h=0.3, xc='LDA', basis={'Na': basis}, kpts=(2, 2, 1), occupations=FermiDirac(0.1), mode='lcao', poissonsolver=PoissonSolver(nn=2, relax='GS'), txt='Na_lcao.txt', mixer=Mixer(0.1, 5, weight=100.0), guess_steps=10, pl_atoms=[pl_atoms1, pl_atoms2], pl_cells=[pl_cell1, pl_cell2], pl_kpts=(2, 2, 15), analysis_data_list=['tc'], edge_atoms=[[0, 3], [0, 11]], mol_atoms=range(4, 8)) atoms.set_calculator(t) t.calculate_iv(0.5, 2)
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 OmegaMatrix: """ Omega matrix in Casidas linear response formalism Parameters - calculator: the calculator object the ground state calculation - kss: the Kohn-Sham singles object - xc: the exchange correlation approx. to use - derivativeLevel: which level i of d^i Exc/dn^i to use - numscale: numeric epsilon for derivativeLevel=0,1 - filehandle: the oject can be read from a filehandle - txt: output stream or file name - finegrid: level of fine grid to use. 0: nothing, 1 for poisson only, 2 everything on the fine grid """ def __init__(self, calculator=None, kss=None, xc=None, derivativeLevel=None, numscale=0.001, filehandle=None, txt=None, finegrid=2, eh_comm=None, ): if not txt and calculator: txt = calculator.txt self.txt, firsttime = initialize_text_stream(txt, mpi.rank) if eh_comm == None: eh_comm = mpi.serial_comm self.eh_comm = eh_comm if filehandle is not None: self.kss = kss self.read(fh=filehandle) return None self.fullkss = kss self.finegrid = finegrid if calculator is None: return self.paw = calculator wfs = self.paw.wfs # handle different grid possibilities self.restrict = None self.poisson = PoissonSolver(nn=self.paw.hamiltonian.poisson.nn) if finegrid: self.poisson.set_grid_descriptor(self.paw.density.finegd) self.poisson.initialize() self.gd = self.paw.density.finegd if finegrid == 1: self.gd = wfs.gd else: self.poisson.set_grid_descriptor(wfs.gd) self.poisson.initialize() self.gd = wfs.gd self.restrict = Transformer(self.paw.density.finegd, wfs.gd, self.paw.input_parameters.stencils[1] ).apply if xc == 'RPA': xc = None # enable RPA as keyword if xc is not None: self.xc = XC(xc) self.xc.initialize(self.paw.density, self.paw.hamiltonian, wfs, self.paw.occupations) # check derivativeLevel if derivativeLevel is None: derivativeLevel= \ self.xc.get_functional().get_max_derivative_level() self.derivativeLevel = derivativeLevel # change the setup xc functional if needed # the ground state calculation may have used another xc if kss.npspins > kss.nvspins: spin_increased = True else: spin_increased = False else: self.xc = None self.numscale = numscale self.singletsinglet = False if kss.nvspins<2 and kss.npspins<2: # this will be a singlet to singlet calculation only self.singletsinglet=True nij = len(kss) self.Om = np.zeros((nij,nij)) self.get_full() def get_full(self): self.paw.timer.start('Omega RPA') self.get_rpa() self.paw.timer.stop() if self.xc is not None: self.paw.timer.start('Omega XC') self.get_xc() self.paw.timer.stop() self.eh_comm.sum(self.Om) self.full = self.Om def get_xc(self): """Add xc part of the coupling matrix""" # shorthands paw = self.paw wfs = paw.wfs gd = paw.density.finegd comm = gd.comm eh_comm = self.eh_comm fg = self.finegrid is 2 kss = self.fullkss nij = len(kss) Om_xc = self.Om # initialize densities # nt_sg is the smooth density on the fine grid with spin index if kss.nvspins==2: # spin polarised ground state calc. nt_sg = paw.density.nt_sg else: # spin unpolarised ground state calc. if kss.npspins==2: # construct spin polarised densities nt_sg = np.array([.5*paw.density.nt_sg[0], .5*paw.density.nt_sg[0]]) else: nt_sg = paw.density.nt_sg # check if D_sp have been changed before D_asp = self.paw.density.D_asp for a, D_sp in D_asp.items(): if len(D_sp) != kss.npspins: if len(D_sp) == 1: D_asp[a] = np.array([0.5 * D_sp[0], 0.5 * D_sp[0]]) else: D_asp[a] = np.array([D_sp[0] + D_sp[1]]) # restrict the density if needed if fg: nt_s = nt_sg else: nt_s = self.gd.zeros(nt_sg.shape[0]) for s in range(nt_sg.shape[0]): self.restrict(nt_sg[s], nt_s[s]) gd = paw.density.gd # initialize vxc or fxc if self.derivativeLevel==0: raise NotImplementedError if kss.npspins==2: v_g=nt_sg[0].copy() else: v_g=nt_sg.copy() elif self.derivativeLevel==1: pass elif self.derivativeLevel==2: fxc_sg = np.zeros(nt_sg.shape) self.xc.calculate_fxc(gd, nt_sg, fxc_sg) else: raise ValueError('derivativeLevel can only be 0,1,2') ## self.paw.my_nuclei = [] ns=self.numscale xc=self.xc print >> self.txt, 'XC',nij,'transitions' for ij in range(eh_comm.rank, nij, eh_comm.size): print >> self.txt,'XC kss['+'%d'%ij+']' timer = Timer() timer.start('init') timer2 = Timer() if self.derivativeLevel >= 1: # vxc is available # We use the numerical two point formula for calculating # the integral over fxc*n_ij. The results are # vvt_s smooth integral # nucleus.I_sp atom based correction matrices (pack2) # stored on each nucleus timer2.start('init v grids') vp_s=np.zeros(nt_s.shape,nt_s.dtype.char) vm_s=np.zeros(nt_s.shape,nt_s.dtype.char) if kss.npspins == 2: # spin polarised nv_s = nt_s.copy() nv_s[kss[ij].pspin] += ns * kss[ij].get(fg) xc.calculate(gd, nv_s, vp_s) nv_s = nt_s.copy() nv_s[kss[ij].pspin] -= ns * kss[ij].get(fg) xc.calculate(gd, nv_s, vm_s) else: # spin unpolarised nv = nt_s + ns * kss[ij].get(fg) xc.calculate(gd, nv, vp_s) nv = nt_s - ns * kss[ij].get(fg) xc.calculate(gd, nv, vm_s) vvt_s = (0.5 / ns) * (vp_s - vm_s) timer2.stop() # initialize the correction matrices timer2.start('init v corrections') I_asp = {} for a, P_ni in wfs.kpt_u[kss[ij].spin].P_ani.items(): # create the modified density matrix Pi_i = P_ni[kss[ij].i] Pj_i = P_ni[kss[ij].j] P_ii = np.outer(Pi_i, Pj_i) # we need the symmetric form, hence we can pack P_p = pack(P_ii) D_sp = self.paw.density.D_asp[a].copy() D_sp[kss[ij].pspin] -= ns * P_p setup = wfs.setups[a] I_sp = np.zeros_like(D_sp) self.xc.calculate_paw_correction(setup, D_sp, I_sp) I_sp *= -1.0 D_sp = self.paw.density.D_asp[a].copy() D_sp[kss[ij].pspin] += ns * P_p self.xc.calculate_paw_correction(setup, D_sp, I_sp) I_sp /= 2.0 * ns I_asp[a] = I_sp timer2.stop() timer.stop() t0 = timer.get_time('init') timer.start(ij) for kq in range(ij,nij): weight = self.weight_Kijkq(ij, kq) if self.derivativeLevel == 0: # only Exc is available if kss.npspins==2: # spin polarised nv_g = nt_sg.copy() nv_g[kss[ij].pspin] += kss[ij].get(fg) nv_g[kss[kq].pspin] += kss[kq].get(fg) Excpp = xc.get_energy_and_potential( nv_g[0], v_g, nv_g[1], v_g) nv_g = nt_sg.copy() nv_g[kss[ij].pspin] += kss[ij].get(fg) nv_g[kss[kq].pspin] -= kss[kq].get(fg) Excpm = xc.get_energy_and_potential(\ nv_g[0],v_g,nv_g[1],v_g) nv_g = nt_sg.copy() nv_g[kss[ij].pspin] -=\ kss[ij].get(fg) nv_g[kss[kq].pspin] +=\ kss[kq].get(fg) Excmp = xc.get_energy_and_potential(\ nv_g[0],v_g,nv_g[1],v_g) nv_g = nt_sg.copy() nv_g[kss[ij].pspin] -= \ kss[ij].get(fg) nv_g[kss[kq].pspin] -=\ kss[kq].get(fg) Excpp = xc.get_energy_and_potential(\ nv_g[0],v_g,nv_g[1],v_g) else: # spin unpolarised nv_g=nt_sg + ns*kss[ij].get(fg)\ + ns*kss[kq].get(fg) Excpp = xc.get_energy_and_potential(nv_g,v_g) nv_g=nt_sg + ns*kss[ij].get(fg)\ - ns*kss[kq].get(fg) Excpm = xc.get_energy_and_potential(nv_g,v_g) nv_g=nt_sg - ns*kss[ij].get(fg)\ + ns*kss[kq].get(fg) Excmp = xc.get_energy_and_potential(nv_g,v_g) nv_g=nt_sg - ns*kss[ij].get(fg)\ - ns*kss[kq].get(fg) Excmm = xc.get_energy_and_potential(nv_g,v_g) Om_xc[ij,kq] += weight *\ 0.25*(Excpp-Excmp-Excpm+Excmm)/(ns*ns) elif self.derivativeLevel == 1: # vxc is available timer2.start('integrate') Om_xc[ij,kq] += weight*\ self.gd.integrate(kss[kq].get(fg)* vvt_s[kss[kq].pspin]) timer2.stop() timer2.start('integrate corrections') Exc = 0. for a, P_ni in wfs.kpt_u[kss[kq].spin].P_ani.items(): # create the modified density matrix Pk_i = P_ni[kss[kq].i] Pq_i = P_ni[kss[kq].j] P_ii = np.outer(Pk_i, Pq_i) # we need the symmetric form, hence we can pack # use pack as I_sp used pack2 P_p = pack(P_ii) Exc += np.dot(I_asp[a][kss[kq].pspin], P_p) Om_xc[ij, kq] += weight * self.gd.comm.sum(Exc) timer2.stop() elif self.derivativeLevel == 2: # fxc is available if kss.npspins==2: # spin polarised Om_xc[ij,kq] += weight *\ gd.integrate(kss[ij].get(fg) * kss[kq].get(fg) * fxc_sg[kss[ij].pspin, kss[kq].pspin]) else: # spin unpolarised Om_xc[ij,kq] += weight *\ gd.integrate(kss[ij].get(fg) * kss[kq].get(fg) * fxc_sg) # XXX still numeric derivatives for local terms timer2.start('integrate corrections') Exc = 0. for a, P_ni in wfs.kpt_u[kss[kq].spin].P_ani.items(): # create the modified density matrix Pk_i = P_ni[kss[kq].i] Pq_i = P_ni[kss[kq].j] P_ii = np.outer(Pk_i, Pq_i) # we need the symmetric form, hence we can pack # use pack as I_sp used pack2 P_p = pack(P_ii) Exc += np.dot(I_asp[a][kss[kq].pspin], P_p) Om_xc[ij, kq] += weight * self.gd.comm.sum(Exc) timer2.stop() if ij != kq: Om_xc[kq,ij] = Om_xc[ij,kq] timer.stop() ## timer2.write() if ij < (nij-1): print >> self.txt,'XC estimated time left',\ self.time_left(timer, t0, ij, nij) def Coulomb_integral_kss(self, kss_ij, kss_kq, phit, rhot, timer=None): # smooth part if timer: timer.start('integrate') I = self.gd.integrate(rhot * phit) if timer: timer.stop() timer.start('integrate corrections 2') wfs = self.paw.wfs Pij_ani = wfs.kpt_u[kss_ij.spin].P_ani Pkq_ani = wfs.kpt_u[kss_kq.spin].P_ani # Add atomic corrections Ia = 0.0 for a, Pij_ni in Pij_ani.items(): Pi_i = Pij_ni[kss_ij.i] Pj_i = Pij_ni[kss_ij.j] Dij_ii = np.outer(Pi_i, Pj_i) Dij_p = pack(Dij_ii) Pk_i = Pkq_ani[a][kss_kq.i] Pq_i = Pkq_ani[a][kss_kq.j] Dkq_ii = np.outer(Pk_i, Pq_i) Dkq_p = pack(Dkq_ii) C_pp = wfs.setups[a].M_pp # ---- # 2 > P P C P P # ---- ip jr prst ks qt # prst Ia += 2.0*np.dot(Dkq_p, np.dot(C_pp, Dij_p)) I += self.gd.comm.sum(Ia) if timer: timer.stop() return I def get_rpa(self): """calculate RPA part of the omega matrix""" # shorthands kss=self.fullkss finegrid=self.finegrid wfs = self.paw.wfs eh_comm = self.eh_comm # calculate omega matrix nij = len(kss) print >> self.txt,'RPA',nij,'transitions' Om = self.Om for ij in range(eh_comm.rank, nij, eh_comm.size): print >> self.txt,'RPA kss['+'%d'%ij+']=', kss[ij] timer = Timer() timer.start('init') timer2 = Timer() # smooth density including compensation charges timer2.start('with_compensation_charges 0') rhot_p = kss[ij].with_compensation_charges( finegrid is not 0) timer2.stop() # integrate with 1/|r_1-r_2| timer2.start('poisson') phit_p = np.zeros(rhot_p.shape, rhot_p.dtype.char) self.poisson.solve(phit_p, rhot_p, charge=None) timer2.stop() timer.stop() t0 = timer.get_time('init') timer.start(ij) if finegrid == 1: rhot = kss[ij].with_compensation_charges() phit = self.gd.zeros() ## print "shapes 0=",phit.shape,rhot.shape self.restrict(phit_p,phit) else: phit = phit_p rhot = rhot_p for kq in range(ij,nij): if kq != ij: # smooth density including compensation charges timer2.start('kq with_compensation_charges') rhot = kss[kq].with_compensation_charges( finegrid is 2) timer2.stop() pre = 2 * sqrt(kss[ij].get_energy() * kss[kq].get_energy() * kss[ij].get_weight() * kss[kq].get_weight() ) I = self.Coulomb_integral_kss(kss[ij], kss[kq], rhot, phit, timer2) Om[ij,kq] = pre * I if ij == kq: Om[ij,kq] += kss[ij].get_energy()**2 else: Om[kq,ij]=Om[ij,kq] timer.stop() ## timer2.write() if ij < (nij-1): t = timer.get_time(ij) # time for nij-ij calculations t = .5*t*(nij-ij) # estimated time for n*(n+1)/2, n=nij-(ij+1) print >> self.txt,'RPA estimated time left',\ self.timestring(t0*(nij-ij-1)+t) def singlets_triplets(self): """Split yourself into singlet and triplet transitions""" assert(self.fullkss.npspins == 2) assert(self.fullkss.nvspins == 1) # strip kss from down spins skss = KSSingles() tkss = KSSingles() map = [] for ij, ks in enumerate(self.fullkss): if ks.pspin == ks.spin: skss.append((ks + ks) / sqrt(2)) tkss.append((ks - ks) / sqrt(2)) map.append(ij) nkss = len(skss) # define the singlet and the triplet omega-matrixes sOm = OmegaMatrix(kss=skss) sOm.full = np.empty((nkss, nkss)) tOm = OmegaMatrix(kss=tkss) tOm.full = np.empty((nkss, nkss)) for ij in range(nkss): for kl in range(nkss): sOm.full[ij, kl] = (self.full[map[ij], map[kl]] + self.full[map[ij], nkss + map[kl]]) tOm.full[ij, kl] = (self.full[map[ij], map[kl]] - self.full[map[ij], nkss + map[kl]]) return sOm, tOm def timestring(self, t): ti = int(t + 0.5) td = ti // 86400 st = '' if td > 0: st += '%d' % td + 'd' ti -= td * 86400 th = ti // 3600 if th > 0: st += '%d' % th + 'h' ti -= th * 3600 tm = ti // 60 if tm > 0: st += '%d' % tm + 'm' ti -= tm * 60 st += '%d' % ti + 's' return st def time_left(self, timer, t0, ij, nij): t = timer.get_time(ij) # time for nij-ij calculations t = .5 * t * (nij - ij) # estimated time for n*(n+1)/2, n=nij-(ij+1) return self.timestring(t0 * (nij - ij - 1) + t) def get_map(self, istart=None, jend=None, energy_range=None): """Return the reduction map for the given requirements""" self.istart = istart self.jend = jend if istart is None and jend is None and energy_range is None: return None, self.fullkss # reduce the matrix print >> self.txt,'# diagonalize: %d transitions original'\ % len(self.fullkss) if energy_range is None: if istart is None: istart = self.kss.istart if self.fullkss.istart > istart: raise RuntimeError('istart=%d has to be >= %d' % (istart, self.kss.istart)) if jend is None: jend = self.kss.jend if self.fullkss.jend < jend: raise RuntimeError('jend=%d has to be <= %d' % (jend, self.kss.jend)) else: try: emin, emax = energy_range except: emax = energy_range emin = 0. emin /= Hartree emax /= Hartree map= [] kss = KSSingles() for ij, k in zip(range(len(self.fullkss)), self.fullkss): if energy_range is None: if k.i >= istart and k.j <= jend: kss.append(k) map.append(ij) else: if k.energy >= emin and k.energy < emax: kss.append(k) map.append(ij) kss.update() print >> self.txt, '# diagonalize: %d transitions now' % len(kss) return map, kss def diagonalize(self, istart=None, jend=None, energy_range=None, TDA=False): """Evaluate Eigenvectors and Eigenvalues:""" if TDA: raise NotImplementedError map, kss = self.get_map(istart, jend, energy_range) nij = len(kss) if map is None: evec = self.full.copy() else: evec = np.zeros((nij,nij)) for ij in range(nij): for kq in range(nij): evec[ij,kq] = self.full[map[ij],map[kq]] assert(len(evec) > 0) self.eigenvectors = evec self.eigenvalues = np.zeros((len(kss))) self.kss = kss diagonalize(self.eigenvectors, self.eigenvalues) def Kss(self, kss=None): """Set and get own Kohn-Sham singles""" if kss is not None: self.fullkss = kss if(hasattr(self,'fullkss')): return self.fullkss else: return None def read(self, filename=None, fh=None): """Read myself from a file""" if fh is None: f = open(filename, 'r') else: f = fh f.readline() nij = int(f.readline()) full = np.zeros((nij,nij)) for ij in range(nij): l = f.readline().split() for kq in range(ij,nij): full[ij,kq] = float(l[kq-ij]) full[kq,ij] = full[ij,kq] self.full = full if fh is None: f.close() def write(self, filename=None, fh=None): """Write current state to a file.""" if mpi.rank == mpi.MASTER: if fh is None: f = open(filename, 'w') else: f = fh f.write('# OmegaMatrix\n') nij = len(self.fullkss) f.write('%d\n' % nij) for ij in range(nij): for kq in range(ij,nij): f.write(' %g' % self.full[ij,kq]) f.write('\n') if fh is None: f.close() def weight_Kijkq(self, ij, kq): """weight for the coupling matrix terms""" kss = self.fullkss return 2.*sqrt( kss[ij].get_energy() * kss[kq].get_energy() * kss[ij].get_weight() * kss[kq].get_weight() ) def __str__(self): str='<OmegaMatrix> ' if hasattr(self,'eigenvalues'): str += 'dimension '+ ('%d'%len(self.eigenvalues)) str += "\neigenvalues: " for ev in self.eigenvalues: str += ' ' + ('%f'%(sqrt(ev) * Hartree)) return str
import numpy as np from ase import Atoms from gpaw import GPAW from gpaw.tddft import TDDFT from gpaw.inducedfield.inducedfield_base import BaseInducedField from gpaw.inducedfield.inducedfield_tddft import TDDFTInducedField from gpaw.poisson import PoissonSolver from gpaw.test import equal do_print_values = False # Use this for printing the reference values poisson_eps = 1e-12 density_eps = 1e-6 # PoissonSolver poissonsolver = PoissonSolver('fd', eps=poisson_eps) # Na2 cluster atoms = Atoms(symbols='Na2', positions=[(0, 0, 0), (3.0, 0, 0)], pbc=False) atoms.center(vacuum=3.0) # Standard ground state calculation calc = GPAW(nbands=2, h=0.6, setups={'Na': '1'}, poissonsolver=poissonsolver, convergence={'density': density_eps}) atoms.set_calculator(calc) energy = atoms.get_potential_energy() calc.write('na2_gs.gpw', mode='all') # Standard time-propagation initialization time_step = 10.0
from ase.io import read from gpaw import GPAW, ConvergenceError from gpaw.poisson import PoissonSolver from gpaw.dipole_correction import DipoleCorrection atoms = read('Pt_H2O.xyz') atoms.set_cell([[ 8.527708, 0., 0., ], [ 0., 4.923474, 0., ], [ 0., 0., 16., ]], scale_atoms=False) atoms.center(axis=2) atoms.pbc = (True, True, False) atoms.calc = GPAW(h=0.20, kpts=(2,4,1), xc='RPBE', poissonsolver=DipoleCorrection(PoissonSolver(),2), basis='dzp', maxiter=200, width=0.1, txt='Pt_H2O.txt', ) ncpus = 8
def initialize(self): PoissonSolver.initialize(self) r_gv = self.gd.get_grid_point_coordinates().transpose((1, 2, 3, 0)) self.mask_g = self.get_mask(r_gv).astype(float) self.volume = self.gd.comm.sum(self.mask_g.sum()) * self.gd.dv
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 -= gd.integrate(a) / L**3 I = gd.integrate(a) b = gd.zeros() p.solve(b, a, charge=0) #, eps=1e-20) return gd.collect(b, broadcast=1) b1 = f(8, PoissonSolver(nn=1, relax='J')) b2 = f(8, PoissonSolver(nn=1, relax='GS')) err1 = abs(b1[0, 0, 0] - b1[8, 8, 8]) err2 = abs(b2[0, 0, 0] - b2[8, 8, 8]) print err1 print err2 assert err1 < 6e-16 assert err2 < 3e-6 # XXX Shouldn't this be better? from gpaw.mpi import size if size == 1: b3 = f(8, FFTPoissonSolver()) err3 = abs(b3[0, 0, 0] - b3[8, 8, 8])
def __init__(self, k2=0.0, nn='M', relax='GS', eps=2e-10): assert k2 <= 0, 'Currently only defined for k^2<=0' PoissonSolver.__init__(self, nn, relax, eps) self.k2 = k2
from gpaw.grid_descriptor import GridDescriptor from gpaw.test import equal from gpaw.mpi import world from gpaw.poisson import PoissonSolver def norm(a): return np.sqrt(np.sum(a.ravel() ** 2)) / len(a.ravel()) # Initialize classes a = 20 # Size of cell N = 48 # Number of grid points Nc = (N, N, N) # Number of grid points along each axis gd = GridDescriptor(Nc, (a, a, a), 0) # Grid-descriptor object solver = PoissonSolver(nn=3) # Numerical poisson solver solver.set_grid_descriptor(gd) solver.initialize() solve = solver.solve xyz, r2 = coordinates(gd) # Matrix with the square of the radial coordinate print(r2.shape) r = np.sqrt(r2) # Matrix with the values of the radial coordinate nH = np.exp(-2 * r) / pi # Density of the hydrogen atom gauss = Gaussian(gd) # An instance of Gaussian # /------------------------------------------------\ # | Check if Gaussian densities are made correctly | # \------------------------------------------------/ for gL in range(2, 9): g = gauss.get_gauss(gL) # a gaussian of gL'th order print("\nGaussian of order", gL)
from gpaw.mpi import world from gpaw.test import equal # Atoms atoms = molecule('Na2') atoms.center(vacuum=4.0) # Ground-state calculation calc = GPAW(nbands=2, h=0.4, setups=dict(Na='1'), basis='sz(dzp)', mode='lcao', xc='oldLDA', poissonsolver=PoissonSolver(eps=1e-16), convergence={'density': 1e-8}, txt='gs.out') atoms.set_calculator(calc) energy = atoms.get_potential_energy() calc.write('gs.gpw', mode='all') # Time-propagation calculation td_calc = LCAOTDDFT('gs.gpw', txt='td.out') DipoleMomentWriter(td_calc, 'dm.dat') td_calc.absorption_kick([0, 0, 1e-5]) td_calc.propagate(30, 150) photoabsorption_spectrum('dm.dat', 'spec.dat', e_max=10, width=0.5,
class HybridXC(HybridXCBase): def __init__(self, name, hybrid=None, xc=None, finegrid=False, unocc=False, omega=None, excitation=None, excited=0, stencil=2): """Mix standard functionals with exact exchange. finegrid: boolean Use fine grid for energy functional evaluations ? unocc: boolean Apply vxx also to unoccupied states ? omega: float RSF mixing parameter excitation: string: Apply operator for improved virtual orbitals to unocc states? Possible modes: singlet: excitations to singlets triplet: excitations to triplets average: average between singlets and tripletts see f.e. http://dx.doi.org/10.1021/acs.jctc.8b00238 excited: number Band to excite from - counted from H**O downwards """ self.finegrid = finegrid self.unocc = unocc self.excitation = excitation self.excited = excited HybridXCBase.__init__(self, name, hybrid=hybrid, xc=xc, omega=omega, stencil=stencil) 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.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 set_positions(self, spos_ac): 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) # Note that the quantities passed are on the # density/Hamiltonian grids! # They may be distributed differently from own quantities. 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 is_cam = self.is_cam vt_g = self.finegd.empty() if self.gd is not self.finegd: vt_G = self.gd.empty() if self.rsf == 'Yukawa': y_vt_g = self.finegd.empty() # if self.gd is not self.finegd: # y_vt_G = self.gd.empty() nocc = int(ceil(kpt.f_n.sum())) // (3 - self.nspins) if self.excitation is not None: ex_band = nocc - self.excited - 1 if self.excitation == 'singlet': ex_weight = -1 elif self.excitation == 'triplet': ex_weight = +1 else: ex_weight = 0 if self.unocc or self.excitation is not None: 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 # XXXX nbands can be different numbers on different cpus! # That means some will execute the loop and others not. # And deadlocks with augment-grids. # 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 if n1 != n2 and f1 == 0 and f1 == f2: continue # Don't work on double unocc. bands # 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 # XXXXX This will go wrong because we are solving the # Poisson equation on the distribution of gd, not finegd # Or maybe it's fixed now self.poissonsolver.solve(vt_g, -rhot_g, charge=-float(n1 == n2), eps=1e-12, zero_initial_phi=True) vt_g *= hybrid if self.rsf == 'Yukawa': y_vt_g[:] = 0.0 self.screened_poissonsolver.solve(y_vt_g, -rhot_g, charge=-float(n1 == n2), eps=1e-12, zero_initial_phi=True) if is_cam: # Cam like correction y_vt_g *= self.cam_beta else: y_vt_g *= hybrid vt_g -= y_vt_g 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 if self.excitation is not None and n1 == ex_band: Htpsit_nG[nocc:] += f1 * vt_G * psit_nG[nocc:] else: if self.excitation is None or n1 != ex_band \ or n2 < nocc: Htpsit_nG[n2] += f1 * vt_G * psit1_G else: Htpsit_nG[n2] += f1 * ex_weight * 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: if self.excitation is None or n1 != ex_band or \ n2 < nocc: v_ni[n2] += f1 * np.dot(v_ii, P_ni[n1]) else: v_ni[n2] += f1 * ex_weight * \ np.dot(v_ii, P_ni[n1]) else: # XXX Check this: v_nii[n1] = f1 * v_ii if self.excitation is not None and n1 == ex_band: for nuoc in range(nocc, nbands): v_ni[nuoc] += f1 * \ np.dot(v_ii, P_ni[nuoc]) def calculate_vv(ni, D_ii, M_pp, weight, addme=False): """Calculate the local corrections depending on Mpp.""" dexx = 0 dekin = 0 if not addme: addsign = -2.0 else: addsign = 2.0 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 += M_pp[p13, p24] * D_ii[i3, i4] p12 = packed_index(i1, i2, ni) if Htpsit_nG is not None: dH_p[p12] += addsign * weight / \ deg * A / ((i1 != i2) + 1) dekin += 2 * weight / deg * D_ii[i1, i2] * A dexx -= weight / deg * D_ii[i1, i2] * A return (dexx, dekin) # 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 (dexx, dekin) = calculate_vv(ni, D_ii, setup.M_pp, hybrid) ekin += dekin exx += dexx if self.rsf is not None: Mg_pp = setup.calculate_yukawa_interaction(self.omega) if is_cam: (dexx, dekin) = calculate_vv(ni, D_ii, Mg_pp, self.cam_beta, addme=True) else: (dexx, dekin) = calculate_vv(ni, D_ii, Mg_pp, hybrid, addme=True) ekin -= dekin exx -= dexx # 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) if self.rsf == 'Yukawa' and setup.X_pg is not None: if is_cam: thybrid = self.cam_beta # 0th order else: thybrid = hybrid exx += thybrid * np.dot(D_p, setup.X_pg) if Htpsit_nG is not None: dH_p += thybrid * setup.X_pg ekin -= thybrid * np.dot(D_p, setup.X_pg) elif self.rsf == 'Yukawa' and setup.X_pg is None: thybrid = exp(-3.62e-2 * self.omega) # educated guess if is_cam: thybrid *= self.cam_beta else: thybrid *= hybrid exx += thybrid * np.dot(D_p, setup.X_p) if Htpsit_nG is not None: dH_p += thybrid * setup.X_p ekin -= thybrid * np.dot(D_p, setup.X_p) # Add core-core exchange energy if kpt.s == 0: if self.rsf is None or is_cam: if is_cam: exx += self.cam_alpha * setup.ExxC else: 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) if not self.unocc or self.excitation is not None: 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 if self.unocc or self.excitation is not None: nocc = len(kpt.vt_nG) else: 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 U_nn = U_nn.T.copy() 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)
def __init__(self, calculator=None, kss=None, xc=None, derivativeLevel=None, numscale=0.001, filehandle=None, txt=None, finegrid=2, eh_comm=None, ): if not txt and calculator: txt = calculator.txt self.txt, firsttime = initialize_text_stream(txt, mpi.rank) if eh_comm == None: eh_comm = mpi.serial_comm self.eh_comm = eh_comm if filehandle is not None: self.kss = kss self.read(fh=filehandle) return None self.fullkss = kss self.finegrid = finegrid if calculator is None: return self.paw = calculator wfs = self.paw.wfs # handle different grid possibilities self.restrict = None self.poisson = PoissonSolver(nn=self.paw.hamiltonian.poisson.nn) if finegrid: self.poisson.set_grid_descriptor(self.paw.density.finegd) self.poisson.initialize() self.gd = self.paw.density.finegd if finegrid == 1: self.gd = wfs.gd else: self.poisson.set_grid_descriptor(wfs.gd) self.poisson.initialize() self.gd = wfs.gd self.restrict = Transformer(self.paw.density.finegd, wfs.gd, self.paw.input_parameters.stencils[1] ).apply if xc == 'RPA': xc = None # enable RPA as keyword if xc is not None: self.xc = XC(xc) self.xc.initialize(self.paw.density, self.paw.hamiltonian, wfs, self.paw.occupations) # check derivativeLevel if derivativeLevel is None: derivativeLevel= \ self.xc.get_functional().get_max_derivative_level() self.derivativeLevel = derivativeLevel # change the setup xc functional if needed # the ground state calculation may have used another xc if kss.npspins > kss.nvspins: spin_increased = True else: spin_increased = False else: self.xc = None self.numscale = numscale self.singletsinglet = False if kss.nvspins<2 and kss.npspins<2: # this will be a singlet to singlet calculation only self.singletsinglet=True nij = len(kss) self.Om = np.zeros((nij,nij)) self.get_full()
def set_grid_descriptor(self, qmgd): if not self.has_subsystems: return self.qm.gd = qmgd # Create quantum Poisson solver self.qm.poisson_solver = PoissonSolver( name='fd', nn=self.nn, eps=self.eps, relax=self.relax, remove_moment=self.remove_moment_qm) self.qm.poisson_solver.set_grid_descriptor(self.qm.gd) #self.qm.poisson_solver.initialize() self.qm.phi = self.qm.gd.zeros() self.qm.rho = self.qm.gd.zeros() # Set quantum grid descriptor self.qm.poisson_solver.set_grid_descriptor(qmgd) # Create classical PoissonSolver self.cl.poisson_solver = PoissonSolver( name='fd', nn=self.nn, eps=self.eps, relax=self.relax, remove_moment=self.remove_moment_cl) self.cl.poisson_solver.set_grid_descriptor(self.cl.gd) #self.cl.poisson_solver.initialize() # Initialize classical material, # its Poisson solver was generated already self.cl.poisson_solver.set_grid_descriptor(self.cl.gd) self.classical_material.initialize(self.cl.gd) self.cl.extrapolated_qm_phi = self.cl.gd.zeros() self.cl.phi = self.cl.gd.zeros() self.cl.extrapolated_qm_phi = self.cl.gd.empty() msg = self.messages.append msg('\nFDTDPoissonSolver/grid descriptors and coupler:') msg(' Domain parallelization with %i processes.' % self.cl.gd.comm.size) if self.cl.gd.comm == serial_comm: msg(' Communicator for domain parallelization: serial_comm') elif self.cl.gd.comm == world: msg(' Communicator for domain parallelization: world') elif self.cl.gd.comm == self.qm.gd.comm: msg(' Communicator for domain parallelization: dft_domain_comm') else: msg(' Communicator for domain parallelization: %s' % self.cl.gd.comm) # Initialize potential coupler if self.potential_coupling_scheme == 'Multipoles': msg('Classical-quantum coupling by multipole expansion ' + 'with maxL: %i' % (self.remove_moment_qm)) self.potential_coupler = MultipolesPotentialCoupler( qm=self.qm, cl=self.cl, index_offset_1=self.shift_indices_1, index_offset_2=self.shift_indices_2, extended_index_offset_1=self.extended_shift_indices_1, extended_index_offset_2=self.extended_shift_indices_2, extended_delta_index=self.extended_deltaIndex, num_refinements=self.num_refinements, remove_moment_qm=self.remove_moment_qm, remove_moment_cl=self.remove_moment_cl, rank=self.rank) else: msg('Classical-quantum coupling by coarsening/refining') self.potential_coupler = RefinerPotentialCoupler( qm=self.qm, cl=self.cl, index_offset_1=self.shift_indices_1, index_offset_2=self.shift_indices_2, extended_index_offset_1=self.extended_shift_indices_1, extended_index_offset_2=self.extended_shift_indices_2, extended_delta_index=self.extended_deltaIndex, num_refinements=self.num_refinements, remove_moment_qm=self.remove_moment_qm, remove_moment_cl=self.remove_moment_cl, rank=self.rank) self.phi_tot_clgd = self.cl.gd.empty() self.phi_tot_qmgd = self.qm.gd.empty()
def read(self, reader): """Read state from file.""" if isinstance(reader, str): reader = gpaw.io.open(reader, 'r') r = reader version = r['version'] assert version >= 0.3 self.xc = r['XCFunctional'] self.nbands = r.dimension('nbands') self.spinpol = (r.dimension('nspins') == 2) if r.has_array('NBZKPoints'): self.kpts = r.get('NBZKPoints') else: self.kpts = r.get('BZKPoints') self.usesymm = r['UseSymmetry'] try: self.basis = r['BasisSet'] except KeyError: pass self.gpts = ((r.dimension('ngptsx') + 1) // 2 * 2, (r.dimension('ngptsy') + 1) // 2 * 2, (r.dimension('ngptsz') + 1) // 2 * 2) self.lmax = r['MaximumAngularMomentum'] self.setups = r['SetupTypes'] self.fixdensity = r['FixDensity'] if version <= 0.4: # Old version: XXX print('# Warning: Reading old version 0.3/0.4 restart files ' + 'will be disabled some day in the future!') self.convergence['eigenstates'] = r['Tolerance'] else: self.convergence = { 'density': r['DensityConvergenceCriterion'], 'energy': r['EnergyConvergenceCriterion'] * Hartree, 'eigenstates': r['EigenstatesConvergenceCriterion'], 'bands': r['NumberOfBandsToConverge'] } if version <= 0.6: mixer = 'Mixer' weight = r['MixMetric'] elif version <= 0.7: mixer = r['MixClass'] weight = r['MixWeight'] metric = r['MixMetric'] if metric is None: weight = 1.0 else: mixer = r['MixClass'] weight = r['MixWeight'] if mixer == 'Mixer': from gpaw.mixer import Mixer elif mixer == 'MixerSum': from gpaw.mixer import MixerSum as Mixer elif mixer == 'MixerSum2': from gpaw.mixer import MixerSum2 as Mixer elif mixer == 'MixerDif': from gpaw.mixer import MixerDif as Mixer elif mixer == 'DummyMixer': from gpaw.mixer import DummyMixer as Mixer else: Mixer = None if Mixer is None: self.mixer = None else: self.mixer = Mixer(r['MixBeta'], r['MixOld'], weight) if version == 0.3: # Old version: XXX print('# Warning: Reading old version 0.3 restart files is ' + 'dangerous and will be disabled some day in the future!') self.stencils = (2, 3) self.charge = 0.0 fixmom = False else: self.stencils = (r['KohnShamStencil'], r['InterpolationStencil']) if r['PoissonStencil'] == 999: self.poissonsolver = FFTPoissonSolver() else: self.poissonsolver = PoissonSolver(nn=r['PoissonStencil']) self.charge = r['Charge'] fixmom = r['FixMagneticMoment'] self.occupations = FermiDirac(r['FermiWidth'] * Hartree, fixmagmom=fixmom) try: self.mode = r['Mode'] except KeyError: self.mode = 'fd' try: dtype = r['DataType'] if dtype == 'Float': self.dtype = float else: self.dtype = complex except KeyError: self.dtype = float
def calculate_induced_field(self, from_density='comp', gridrefinement=2, extend_N_cd=None, deextend=False, poissonsolver=None, gradient_n=3): if self.has_field and \ from_density == self.field_from_density and \ self.Frho_wg is not None: Frho_wg = self.Frho_wg gd = self.fieldgd else: Frho_wg, gd = self.get_induced_density(from_density, gridrefinement) # Always extend a bit to get field without jumps if extend_N_cd is None: extend_N = max(8, 2**int(np.ceil(np.log(gradient_n) / np.log(2.)))) extend_N_cd = extend_N * np.ones(shape=(3, 2), dtype=np.int) deextend = True # Extend grid oldgd = gd egd, cell_cv, move_c = \ extended_grid_descriptor(gd, extend_N_cd=extend_N_cd) Frho_we = egd.zeros((self.nw, ), dtype=self.dtype) for w in range(self.nw): extend_array(gd, egd, Frho_wg[w], Frho_we[w]) Frho_wg = Frho_we gd = egd if not deextend: # TODO: this will make atoms unusable with original grid self.atoms.set_cell(cell_cv, scale_atoms=False) move_atoms(self.atoms, move_c) # Allocate arrays Fphi_wg = gd.zeros((self.nw, ), dtype=self.dtype) Fef_wvg = gd.zeros(( self.nw, self.nv, ), dtype=self.dtype) Ffe_wg = gd.zeros((self.nw, ), dtype=float) # Poissonsolver if poissonsolver is None: poissonsolver = PoissonSolver(name='fd', eps=1e-20) poissonsolver.set_grid_descriptor(gd) for w in range(self.nw): # TODO: better output of progress # parprint('%d' % w) calculate_field(gd, Frho_wg[w], self.Fbgef_v, Fphi_wg[w], Fef_wvg[w], Ffe_wg[w], poissonsolver=poissonsolver, nv=self.nv, gradient_n=gradient_n) # De-extend grid if deextend: Frho_wo = oldgd.zeros((self.nw, ), dtype=self.dtype) Fphi_wo = oldgd.zeros((self.nw, ), dtype=self.dtype) Fef_wvo = oldgd.zeros(( self.nw, self.nv, ), dtype=self.dtype) Ffe_wo = oldgd.zeros((self.nw, ), dtype=float) for w in range(self.nw): deextend_array(oldgd, gd, Frho_wo[w], Frho_wg[w]) deextend_array(oldgd, gd, Fphi_wo[w], Fphi_wg[w]) deextend_array(oldgd, gd, Ffe_wo[w], Ffe_wg[w]) for v in range(self.nv): deextend_array(oldgd, gd, Fef_wvo[w][v], Fef_wvg[w][v]) Frho_wg = Frho_wo Fphi_wg = Fphi_wo Fef_wvg = Fef_wvo Ffe_wg = Ffe_wo gd = oldgd # Store results self.has_field = True self.field_from_density = from_density self.fieldgd = gd self.Frho_wg = Frho_wg self.Fphi_wg = Fphi_wg self.Fef_wvg = Fef_wvg self.Ffe_wg = Ffe_wg
def compare(phi1_g, phi2_g, val, eps=np.sqrt(poissoneps)): big_phi1_g = gd.collect(phi1_g) big_phi2_g = gd.collect(phi2_g) if gd.comm.rank == 0: diff = np.max(np.absolute(big_phi1_g - big_phi2_g)) else: diff = 0.0 diff = np.array([diff]) gd.comm.broadcast(diff, 0) equal(diff[0], val, eps) # Get reference from default poissonsolver poisson = PoissonSolver('fd', eps=poissoneps) phiref_g, npoisson = poisson_init_solve(gd, rho_g, poisson) # Test agreement with default poisson = ExtraVacuumPoissonSolver(N_c, PoissonSolver('fd', eps=poissoneps)) phi_g, npoisson = poisson_init_solve(gd, rho_g, poisson) compare(phi_g, phiref_g, 0.0, 1e-24) # New reference with extra vacuum gpts = N_c * 4 poisson = ExtraVacuumPoissonSolver(gpts, PoissonSolver('fd', eps=poissoneps)) phi_g, npoisson = poisson_init_solve(gd, rho_g, poisson) # print poisson.get_description() compare(phi_g, phiref_g, 2.6485385144e-02) phiref_g = phi_g
from gpaw.utilities.gauss import Gaussian from gpaw.grid_descriptor import GridDescriptor from gpaw.test import equal from gpaw.poisson import PoissonSolver def norm(a): return np.sqrt(np.sum(a.ravel()**2)) / len(a.ravel()) # Initialize classes a = 20 # Size of cell N = 48 # Number of grid points Nc = (N, N, N) # Number of grid points along each axis gd = GridDescriptor(Nc, (a, a, a), 0) # Grid-descriptor object solver = PoissonSolver(nn=3) # Numerical poisson solver solver.set_grid_descriptor(gd) solve = solver.solve xyz, r2 = coordinates(gd) # Matrix with the square of the radial coordinate print(r2.shape) r = np.sqrt(r2) # Matrix with the values of the radial coordinate nH = np.exp(-2 * r) / pi # Density of the hydrogen atom gauss = Gaussian(gd) # An instance of Gaussian # /------------------------------------------------\ # | Check if Gaussian densities are made correctly | # \------------------------------------------------/ for gL in range(2, 9): g = gauss.get_gauss(gL) # a gaussian of gL'th order print('\nGaussian of order', gL) for mL in range(9):