コード例 #1
0
    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)
コード例 #2
0
ファイル: ut_gucops.py プロジェクト: qsnake/gpaw
    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()
コード例 #3
0
ファイル: ut_gucops.py プロジェクト: yihsuanliu/gpaw
    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()
コード例 #4
0
ファイル: ut_gucops.py プロジェクト: robwarm/gpaw-symm
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()
コード例 #5
0
ファイル: dscftools.py プロジェクト: qsnake/gpaw
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
コード例 #6
0
ファイル: ut_gucops.py プロジェクト: yihsuanliu/gpaw
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()
コード例 #7
0
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