def setUp(self): for virtvar in ['boundaries']: assert getattr(self, virtvar) is not None, 'Virtual "%s"!' % virtvar # Basic unit cell information: res, N_c = shapeopt(100, self.G**3, 3, 0.2) #N_c = 4*np.round(np.array(N_c)/4) # makes domain decomposition easier cell_cv = self.h * np.diag(N_c) pbc_c = {'zero' : (False,False,False), \ 'periodic': (True,True,True), \ 'mixed' : (True, False, True)}[self.boundaries] # Create randomized gas-like atomic configuration on interim grid tmpgd = GridDescriptor(N_c, cell_cv, pbc_c) self.atoms = create_random_atoms(tmpgd) # Create setups Z_a = self.atoms.get_atomic_numbers() assert 1 == self.nspins self.setups = Setups(Z_a, p.setups, p.basis, p.lmax, xc) self.natoms = len(self.setups) # Decide how many kpoints to sample from the 1st Brillouin Zone kpts_c = np.ceil( (10 / Bohr) / np.sum(cell_cv**2, axis=1)**0.5).astype(int) kpts_c = tuple(kpts_c * pbc_c + 1 - pbc_c) self.bzk_kc = kpts2ndarray(kpts_c) # Set up k-point descriptor self.kd = KPointDescriptor(self.bzk_kc, self.nspins) self.kd.set_symmetry(self.atoms, self.setups, p.usesymm) # Set the dtype if self.kd.gamma: self.dtype = float else: self.dtype = complex # Create communicators parsize, parsize_bands = self.get_parsizes() assert self.nbands % np.prod(parsize_bands) == 0 domain_comm, kpt_comm, band_comm = distribute_cpus( parsize, parsize_bands, self.nspins, self.kd.nibzkpts) self.kd.set_communicator(kpt_comm) # Set up band descriptor: self.bd = BandDescriptor(self.nbands, band_comm) # Set up grid descriptor: self.gd = GridDescriptor(N_c, cell_cv, pbc_c, domain_comm, parsize) # Set up kpoint/spin descriptor (to be removed): self.kd_old = KPointDescriptorOld(self.nspins, self.kd.nibzkpts, kpt_comm, self.kd.gamma, self.dtype)
def setUp(self): UTDomainParallelSetup.setUp(self) for virtvar in ["dtype"]: assert getattr(self, virtvar) is not None, 'Virtual "%s"!' % virtvar # Set up kpoint descriptor: self.kd = KPointDescriptor(self.nspins, self.nibzkpts, self.kpt_comm, self.gamma, self.dtype) # Choose a sufficiently small width of gaussian test functions cell_c = np.sum(self.gd.cell_cv ** 2, axis=1) ** 0.5 self.sigma = np.min((0.1 + 0.4 * self.gd.pbc_c) * cell_c) if debug and world.rank == 0: print "sigma=%8.5f Ang" % (self.sigma * Bohr), "cell_c:", cell_c * Bohr, "Ang", "N_c:", self.gd.N_c self.atoms = create_random_atoms(self.gd, 4, "H", 4 * self.sigma) self.r_vG = None self.wf_uG = None self.laplace0_uG = None self.allocate()
def setUp(self): UTDomainParallelSetup.setUp(self) for virtvar in ['dtype']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Set up kpoint descriptor: self.kd = KPointDescriptor(self.nspins, self.nibzkpts, self.kpt_comm, \ self.gamma, self.dtype) # Choose a sufficiently small width of gaussian test functions cell_c = np.sum(self.gd.cell_cv**2, axis=1)**0.5 self.sigma = np.min((0.1+0.4*self.gd.pbc_c)*cell_c) if debug and world.rank == 0: print 'sigma=%8.5f Ang' % (self.sigma*Bohr), 'cell_c:', cell_c*Bohr, 'Ang', 'N_c:', self.gd.N_c self.atoms = create_random_atoms(self.gd, 4, 'H', 4*self.sigma) self.r_vG = None self.wf_uG = None self.laplace0_uG = None self.allocate()
class UTGaussianWavefunctionSetup(UTDomainParallelSetup): __doc__ = UTDomainParallelSetup.__doc__ + """ The pseudo wavefunctions are moving gaussians centered around each atom.""" allocated = False dtype = None def setUp(self): UTDomainParallelSetup.setUp(self) for virtvar in ['dtype']: assert getattr(self,virtvar) is not None, 'Virtual "%s"!' % virtvar # Set up kpoint descriptor: self.kd = KPointDescriptor(self.nspins, self.nibzkpts, self.kpt_comm, \ self.gamma, self.dtype) # Choose a sufficiently small width of gaussian test functions cell_c = np.sum(self.gd.cell_cv**2, axis=1)**0.5 self.sigma = np.min((0.1+0.4*self.gd.pbc_c)*cell_c) if debug and world.rank == 0: print 'sigma=%8.5f Ang' % (self.sigma*Bohr), 'cell_c:', cell_c*Bohr, 'Ang', 'N_c:', self.gd.N_c self.atoms = create_random_atoms(self.gd, 4, 'H', 4*self.sigma) self.r_vG = None self.wf_uG = None self.laplace0_uG = None self.allocate() def tearDown(self): UTDomainParallelSetup.tearDown(self) del self.phase_ucd, self.atoms, self.r_vG, self.wf_uG, self.laplace0_uG self.allocated = False def allocate(self): if self.allocated: raise RuntimeError('Already allocated!') # Calculate complex phase factors: self.phase_ucd = np.ones((self.kd.mynks, 3, 2), complex) if not self.gamma: for myu, phase_cd in enumerate(self.phase_ucd): u = self.kd.global_index(myu) s, k = self.kd.what_is(u) phase_cd[:] = np.exp(2j * np.pi * self.gd.sdisp_cd * \ self.ibzk_kc[k,:,np.newaxis]) assert self.dtype == complex, 'Complex wavefunctions are required.' self.r_vG = self.gd.get_grid_point_coordinates() self.wf_uG = self.gd.zeros(self.kd.mynks, dtype=self.dtype) self.laplace0_uG = self.gd.zeros(self.kd.mynks, dtype=self.dtype) buf_G = self.gd.empty(dtype=self.dtype) sdisp_Ac = [] for a,spos_c in enumerate(self.atoms.get_scaled_positions() % 1.0): for sdisp_x in range(-1*self.gd.pbc_c[0],self.gd.pbc_c[0]+1): for sdisp_y in range(-1*self.gd.pbc_c[1],self.gd.pbc_c[1]+1): for sdisp_z in range(-1*self.gd.pbc_c[2],self.gd.pbc_c[2]+1): sdisp_c = np.array([sdisp_x, sdisp_y, sdisp_z]) if debug and world.rank == 0: print 'a=%d, spos=%s, sdisp_c=%s' % (a,spos_c,sdisp_c) sdisp_Ac.append((a,spos_c,sdisp_c)) for a,spos_c,sdisp_c in sdisp_Ac: if debug and world.rank == 0: print 'Adding gaussian at a=%d, spos=%s, sigma=%8.5f Ang' % (a,spos_c+sdisp_c,self.sigma*Bohr) r0_v = np.dot(spos_c+sdisp_c, self.gd.cell_cv) for myu in range(self.kd.mynks): u = self.kd.global_index(myu) s, k = self.kd.what_is(u) ibzk_v = self.ibzk_kv[k] # f(r) = sum_a A exp(-|r-R^a|^2 / 2sigma^2) exp(i k.r) gaussian_wave(self.r_vG, r0_v, self.sigma, ibzk_v, A=1.0, dtype=self.dtype, out_G=buf_G) self.wf_uG[myu] += buf_G # d^2/dx^2 exp(ikx-(x-x0)^2/2sigma^2) # ((ik-(x-x0)/sigma^2)^2 - 1/sigma^2) exp(ikx-(x-x0)^2/2sigma^2) dr2_G = np.sum((1j*ibzk_v[:,np.newaxis,np.newaxis,np.newaxis] \ - (self.r_vG-r0_v[:,np.newaxis,np.newaxis,np.newaxis]) \ / self.sigma**2)**2, axis=0) self.laplace0_uG[myu] += (dr2_G - 3/self.sigma**2) * buf_G self.allocated = True # ================================= def test_something(self): laplace_uG = np.empty_like(self.laplace0_uG) op = Laplace(self.gd, dtype=self.dtype) for myu, laplace_G in enumerate(laplace_uG): phase_cd = {float:None, complex:self.phase_ucd[myu]}[self.dtype] op.apply(self.wf_uG[myu], laplace_G, phase_cd) print 'myu:', myu, 'diff:', np.std(laplace_G-self.laplace0_uG[myu]), '/', np.abs(laplace_G-self.laplace0_uG[myu]).max()
def dscf_collapse_orbitals(paw, nbands_max='occupied', f_tol=1e-4, verify_density=True, nt_tol=1e-5, D_tol=1e-3): bd = paw.wfs.bd gd = paw.wfs.gd kd = KPointDescriptor(paw.wfs.nspins, paw.wfs.nibzkpts, \ paw.wfs.kpt_comm, paw.wfs.gamma, paw.wfs.dtype) assert paw.wfs.bd.comm.size == 1, 'Band parallelization not implemented.' f_skn = np.empty((kd.nspins, kd.nibzkpts, bd.nbands), dtype=float) for s, f_kn in enumerate(f_skn): for k, f_n in enumerate(f_kn): kpt_rank, myu = kd.get_rank_and_index(s, k) if kd.comm.rank == kpt_rank: f_n[:] = paw.wfs.kpt_u[myu].f_n kd.comm.broadcast(f_n, kpt_rank) # Find smallest band index, from which all bands have negligeble occupations n0 = np.argmax(f_skn<f_tol, axis=-1).max() assert np.all(f_skn[...,n0:]<f_tol) # XXX use f_skn[...,n0:].sum()<f_tol # Read the number of Delta-SCF orbitals norbitals = paw.occupations.norbitals if debug: mpi_debug('n0=%d, norbitals=%d, bd:%d, gd:%d, kd:%d' % (n0,norbitals,bd.comm.size,gd.comm.size,kd.comm.size)) if nbands_max < 0: nbands_max = n0 + norbitals - nbands_max elif nbands_max == 'occupied': nbands_max = n0 + norbitals assert nbands_max >= n0 + norbitals, 'Too few bands to include occupations.' ncut = nbands_max-norbitals if debug: mpi_debug('nbands_max=%d' % nbands_max) paw.wfs.initialize_wave_functions_from_restart_file() # hurts memmory for kpt in paw.wfs.kpt_u: mol = kpt.P_ani.keys() # XXX stupid (f_o, eps_o, wf_oG, P_aoi,) = dscf_reconstruct_orbitals_k_point(paw, norbitals, mol, kpt) assert abs(f_o-1) < 1e-9, 'Orbitals must be properly normalized.' f_o = kpt.ne_o # actual ocupatiion numbers # Crop band-data and inject data for Delta-SCF orbitals kpt.f_n = np.hstack((kpt.f_n[:n0], f_o, kpt.f_n[n0:ncut])) kpt.eps_n = np.hstack((kpt.eps_n[:n0], eps_o, kpt.eps_n[n0:ncut])) for a, P_ni in kpt.P_ani.items(): kpt.P_ani[a] = np.vstack((P_ni[:n0], P_aoi[a], P_ni[n0:ncut])) old_psit_nG = kpt.psit_nG kpt.psit_nG = gd.empty(nbands_max, dtype=kd.dtype) if isinstance(old_psit_nG, TarFileReference): assert old_psit_nG.shape[-3:] == wf_oG.shape[-3:], 'Shape mismatch!' # Read band-by-band to save memory as full psit_nG may be large for n,psit_G in enumerate(kpt.psit_nG): if n < n0: full_psit_G = old_psit_nG[n] elif n in range(n0,n0+norbitals): full_psit_G = wf_oG[n-n0] else: full_psit_G = old_psit_nG[n-norbitals] gd.distribute(full_psit_G, psit_G) else: kpt.psit_nG[:n0] = old_psit_nG[:n0] kpt.psit_nG[n0:n0+norbitals] = wf_oG kpt.psit_nG[n0+norbitals:] = old_psit_nG[n0:ncut] del kpt.ne_o, kpt.c_on, old_psit_nG del paw.occupations.norbitals # Change various parameters related to new number of bands paw.wfs.mynbands = bd.mynbands = nbands_max paw.wfs.nbands = bd.nbands = nbands_max if paw.wfs.eigensolver: paw.wfs.eigensolver.initialized = False # Crop convergence criteria nbands_converge to new number of bands par = paw.input_parameters if 'convergence' in par: cc = par['convergence'] if 'bands' in cc: cc['bands'] = min(nbands_max, cc['bands']) # Replace occupations class with a fixed variant (gets the magmom right) paw.occupations = FermiDiracFixed(paw.occupations.ne, kd.nspins, paw.occupations.width, paw.occupations.fermilevel) paw.occupations.set_communicator(kd.comm, bd.comm) paw.occupations.find_fermi_level(paw.wfs.kpt_u) # just regenerates magmoms # For good measure, self-consistency information should be destroyed paw.scf.reset() if verify_density: paw.initialize_positions() # Re-calculate pseudo density and watch for changes old_nt_sG = paw.density.nt_sG.copy() paw.density.calculate_pseudo_density(paw.wfs) if debug: mpi_debug('delta-density: %g' % np.abs(old_nt_sG-paw.density.nt_sG).max()) assert np.all(np.abs(paw.density.nt_sG-old_nt_sG)<nt_tol), 'Density changed!' # Re-calculate atomic density matrices and watch for changes old_D_asp = {} for a,D_sp in paw.density.D_asp.items(): old_D_asp[a] = D_sp.copy() paw.wfs.calculate_atomic_density_matrices(paw.density.D_asp) if debug: mpi_debug('delta-D_asp: %g' % max([np.abs(D_sp-old_D_asp[a]).max() for a,D_sp in paw.density.D_asp.items()])) for a,D_sp in paw.density.D_asp.items(): assert np.all(np.abs(D_sp-old_D_asp[a])< D_tol), 'Atom %d changed!' % a
def dscf_collapse_orbitals(paw, nbands_max='occupied', f_tol=1e-4, verify_density=True, nt_tol=1e-5, D_tol=1e-3): bd = paw.wfs.bd gd = paw.wfs.gd kd = KPointDescriptor(paw.wfs.nspins, paw.wfs.nibzkpts, \ paw.wfs.kpt_comm, paw.wfs.gamma, paw.wfs.dtype) assert paw.wfs.bd.comm.size == 1, 'Band parallelization not implemented.' f_skn = np.empty((kd.nspins, kd.nibzkpts, bd.nbands), dtype=float) for s, f_kn in enumerate(f_skn): for k, f_n in enumerate(f_kn): kpt_rank, myu = kd.get_rank_and_index(s, k) if kd.comm.rank == kpt_rank: f_n[:] = paw.wfs.kpt_u[myu].f_n kd.comm.broadcast(f_n, kpt_rank) # Find smallest band index, from which all bands have negligeble occupations n0 = np.argmax(f_skn < f_tol, axis=-1).max() assert np.all( f_skn[..., n0:] < f_tol) # XXX use f_skn[...,n0:].sum()<f_tol # Read the number of Delta-SCF orbitals norbitals = paw.occupations.norbitals if debug: mpi_debug('n0=%d, norbitals=%d, bd:%d, gd:%d, kd:%d' % (n0, norbitals, bd.comm.size, gd.comm.size, kd.comm.size)) if nbands_max < 0: nbands_max = n0 + norbitals - nbands_max elif nbands_max == 'occupied': nbands_max = n0 + norbitals assert nbands_max >= n0 + norbitals, 'Too few bands to include occupations.' ncut = nbands_max - norbitals if debug: mpi_debug('nbands_max=%d' % nbands_max) paw.wfs.initialize_wave_functions_from_restart_file() # hurts memmory for kpt in paw.wfs.kpt_u: mol = kpt.P_ani.keys() # XXX stupid ( f_o, eps_o, wf_oG, P_aoi, ) = dscf_reconstruct_orbitals_k_point(paw, norbitals, mol, kpt) assert abs(f_o - 1) < 1e-9, 'Orbitals must be properly normalized.' f_o = kpt.ne_o # actual ocupatiion numbers # Crop band-data and inject data for Delta-SCF orbitals kpt.f_n = np.hstack((kpt.f_n[:n0], f_o, kpt.f_n[n0:ncut])) kpt.eps_n = np.hstack((kpt.eps_n[:n0], eps_o, kpt.eps_n[n0:ncut])) for a, P_ni in kpt.P_ani.items(): kpt.P_ani[a] = np.vstack((P_ni[:n0], P_aoi[a], P_ni[n0:ncut])) old_psit_nG = kpt.psit_nG kpt.psit_nG = gd.empty(nbands_max, dtype=kd.dtype) if isinstance(old_psit_nG, TarFileReference): assert old_psit_nG.shape[-3:] == wf_oG.shape[ -3:], 'Shape mismatch!' # Read band-by-band to save memory as full psit_nG may be large for n, psit_G in enumerate(kpt.psit_nG): if n < n0: full_psit_G = old_psit_nG[n] elif n in range(n0, n0 + norbitals): full_psit_G = wf_oG[n - n0] else: full_psit_G = old_psit_nG[n - norbitals] gd.distribute(full_psit_G, psit_G) else: kpt.psit_nG[:n0] = old_psit_nG[:n0] kpt.psit_nG[n0:n0 + norbitals] = wf_oG kpt.psit_nG[n0 + norbitals:] = old_psit_nG[n0:ncut] del kpt.ne_o, kpt.c_on, old_psit_nG del paw.occupations.norbitals # Change various parameters related to new number of bands paw.wfs.mynbands = bd.mynbands = nbands_max paw.wfs.nbands = bd.nbands = nbands_max if paw.wfs.eigensolver: paw.wfs.eigensolver.initialized = False # Crop convergence criteria nbands_converge to new number of bands par = paw.input_parameters if 'convergence' in par: cc = par['convergence'] if 'bands' in cc: cc['bands'] = min(nbands_max, cc['bands']) # Replace occupations class with a fixed variant (gets the magmom right) paw.occupations = FermiDiracFixed(paw.occupations.ne, kd.nspins, paw.occupations.width, paw.occupations.fermilevel) paw.occupations.set_communicator(kd.comm, bd.comm) paw.occupations.find_fermi_level(paw.wfs.kpt_u) # just regenerates magmoms # For good measure, self-consistency information should be destroyed paw.scf.reset() if verify_density: paw.initialize_positions() # Re-calculate pseudo density and watch for changes old_nt_sG = paw.density.nt_sG.copy() paw.density.calculate_pseudo_density(paw.wfs) if debug: mpi_debug('delta-density: %g' % np.abs(old_nt_sG - paw.density.nt_sG).max()) assert np.all( np.abs(paw.density.nt_sG - old_nt_sG) < nt_tol), 'Density changed!' # Re-calculate atomic density matrices and watch for changes old_D_asp = {} for a, D_sp in paw.density.D_asp.items(): old_D_asp[a] = D_sp.copy() paw.wfs.calculate_atomic_density_matrices(paw.density.D_asp) if debug: mpi_debug('delta-D_asp: %g' % max([ np.abs(D_sp - old_D_asp[a]).max() for a, D_sp in paw.density.D_asp.items() ])) for a, D_sp in paw.density.D_asp.items(): assert np.all( np.abs(D_sp - old_D_asp[a]) < D_tol), 'Atom %d changed!' % a