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