예제 #1
0
    def initialize_positions(self, atoms=None):
        """Update the positions of the atoms."""
        self.log('Initializing position-dependent things.\n')
        if atoms is None:
            atoms = self.atoms
        else:
            atoms = atoms.copy()
            self._set_atoms(atoms)

        mpi.synchronize_atoms(atoms, self.world)

        rank_a = self.wfs.gd.get_ranks_from_positions(self.spos_ac)
        atom_partition = AtomPartition(self.wfs.gd.comm, rank_a, name='gd')
        self.wfs.set_positions(self.spos_ac, atom_partition)
        self.density.set_positions(self.spos_ac, atom_partition)
        self.hamiltonian.set_positions(self.spos_ac, atom_partition)
예제 #2
0
# Check that atoms object mismatches are detected properly across CPUs.

from ase.build import molecule
from gpaw.mpi import world, synchronize_atoms

system = molecule('H2O')
synchronize_atoms(system, world)

if world.rank == 1:
    system.positions[1, 1] += 1e-8  # fail (above tolerance)
if world.rank == 2:
    system.cell[0, 0] += 1e-15  # fail (zero tolerance)
if world.rank == 3:
    system.positions[1, 1] += 1e-10  # pass (below tolerance)

expected_err_ranks = {1: [], 2: [1]}.get(world.size, [1, 2])

try:
    synchronize_atoms(system, world, tolerance=1e-9)
except ValueError as e:
    assert (expected_err_ranks == e.args[1]).all()
else:
    assert world.size == 1
예제 #3
0
def create_random_atoms(gd, nmolecules=10, name='NH2', mindist=4.5 / Bohr):
    """Create gas-like collection of atoms from randomly placed molecules.
    Applies rigid motions to molecules, translating the COM and/or rotating
    by a given angle around an axis of rotation through the new COM. These
    atomic positions obey the minimum distance requirement to zero-boundaries.

    Warning: This is only intended for testing parallel grid/LFC consistency.
    """
    atoms = Atoms(cell=gd.cell_cv * Bohr, pbc=gd.pbc_c)

    # Store the original state of the random number generator
    randstate = np.random.get_state()
    seed = np.array([
        md5_array(data, numeric=True)
        for data in [nmolecules, gd.cell_cv, gd.pbc_c, gd.N_c]
    ]).astype(int)
    #np.random.seed(seed % 4294967296)
    np.random.seed(seed % 1073741824)

    for m in range(nmolecules):
        amol = molecule(name)
        amol.set_cell(gd.cell_cv * Bohr)

        # Rotate the molecule around COM according to three random angles
        # The rotation axis is given by spherical angles phi and theta
        v, phi, theta = np.random.uniform(0.0, 2 * np.pi,
                                          3)  # theta [0,pi[ really
        axis = np.array(
            [cos(phi) * sin(theta),
             sin(phi) * sin(theta),
             cos(theta)])
        amol.rotate(axis, v)

        # Find the scaled length we must transverse along the given axes such
        # that the resulting displacement vector is `mindist` from the cell
        # face corresponding to that direction (plane with unit normal n_v).
        sdist_c = np.empty(3)
        if not gd.orthogonal:
            for c in range(3):
                n_v = gd.xxxiucell_cv[c] / np.linalg.norm(gd.xxxiucell_cv[c])
                sdist_c[c] = mindist / np.dot(gd.cell_cv[c], n_v)
        else:
            sdist_c[:] = mindist / gd.cell_cv.diagonal()
        assert np.all(sdist_c > 0), 'Displacment vectors must be inside cell.'

        # Scaled dimensions of the smallest possible box centered on the COM
        spos_ac = amol.get_scaled_positions()  # NB! must not do a "% 1.0"
        scom_c = np.dot(gd.icell_cv, amol.get_center_of_mass())
        sbox_c = np.abs(spos_ac - scom_c[np.newaxis, :]).max(axis=0)
        sdelta_c = (1 - np.array(gd.pbc_c)) * (sbox_c + sdist_c)
        assert (sdelta_c <
                1.0 - sdelta_c).all(), 'Box is too tight to fit atoms.'
        scenter_c = [np.random.uniform(d, 1 - d) for d in sdelta_c]
        center_v = np.dot(scenter_c, gd.cell_cv)

        # Translate the molecule such that COM is located at random center
        offset_av = (center_v -
                     amol.get_center_of_mass() / Bohr)[np.newaxis, :]
        amol.set_positions(amol.get_positions() + offset_av * Bohr)
        assert np.linalg.norm(center_v -
                              amol.get_center_of_mass() / Bohr) < 1e-9
        atoms.extend(amol)

    # Restore the original state of the random number generator
    np.random.set_state(randstate)
    synchronize_atoms(atoms, world)
    return atoms
예제 #4
0
    def initialize(self, atoms=None, reading=False):
        """Inexpensive initialization."""

        self.log('Initialize ...\n')

        if atoms is None:
            atoms = self.atoms
        else:
            # Save the state of the atoms:
            self.atoms = atoms.copy()

        par = self.parameters

        natoms = len(atoms)

        cell_cv = atoms.get_cell() / Bohr
        number_of_lattice_vectors = cell_cv.any(axis=1).sum()
        if number_of_lattice_vectors < 3:
            raise ValueError(
                'GPAW requires 3 lattice vectors.  Your system has {0}.'.
                format(number_of_lattice_vectors))

        pbc_c = atoms.get_pbc()
        assert len(pbc_c) == 3
        magmom_a = atoms.get_initial_magnetic_moments()

        mpi.synchronize_atoms(atoms, self.world)

        # Generate new xc functional only when it is reset by set
        # XXX sounds like this should use the _changed_keywords dictionary.
        if self.hamiltonian is None or self.hamiltonian.xc is None:
            if isinstance(par.xc, basestring):
                xc = XC(par.xc)
            else:
                xc = par.xc
        else:
            xc = self.hamiltonian.xc

        mode = par.mode
        if isinstance(mode, basestring):
            mode = {'name': mode}
        if isinstance(mode, dict):
            mode = create_wave_function_mode(**mode)

        if par.dtype == complex:
            warnings.warn('Use mode={0}(..., force_complex_dtype=True) '
                          'instead of dtype=complex'.format(mode.name.upper()))
            mode.force_complex_dtype = True
            del par['dtype']
            par.mode = mode

        if xc.orbital_dependent and mode.name == 'lcao':
            raise ValueError('LCAO mode does not support '
                             'orbital-dependent XC functionals.')

        realspace = (mode.name != 'pw' and mode.interpolation != 'fft')

        if not realspace:
            pbc_c = np.ones(3, bool)

        self.create_setups(mode, xc)

        magnetic = magmom_a.any()

        spinpol = par.spinpol
        if par.hund:
            if natoms != 1:
                raise ValueError('hund=True arg only valid for single atoms!')
            spinpol = True
            magmom_a[0] = self.setups[0].get_hunds_rule_moment(par.charge)

        if spinpol is None:
            spinpol = magnetic
        elif magnetic and not spinpol:
            raise ValueError('Non-zero initial magnetic moment for a ' +
                             'spin-paired calculation!')

        nspins = 1 + int(spinpol)

        if spinpol:
            self.log('Spin-polarized calculation.')
            self.log('Magnetic moment:  {0:.6f}\n'.format(magmom_a.sum()))
        else:
            self.log('Spin-paired calculation\n')

        if isinstance(par.background_charge, dict):
            background = create_background_charge(**par.background_charge)
        else:
            background = par.background_charge

        nao = self.setups.nao
        nvalence = self.setups.nvalence - par.charge
        if par.background_charge is not None:
            nvalence += background.charge
        M = abs(magmom_a.sum())

        nbands = par.nbands

        orbital_free = any(setup.orbital_free for setup in self.setups)
        if orbital_free:
            nbands = 1

        if isinstance(nbands, basestring):
            if nbands[-1] == '%':
                basebands = int(nvalence + M + 0.5) // 2
                nbands = int((float(nbands[:-1]) / 100) * basebands)
            else:
                raise ValueError('Integer expected: Only use a string '
                                 'if giving a percentage of occupied bands')

        if nbands is None:
            nbands = 0
            for setup in self.setups:
                nbands_from_atom = setup.get_default_nbands()

                # Any obscure setup errors?
                if nbands_from_atom < -(-setup.Nv // 2):
                    raise ValueError('Bad setup: This setup requests %d'
                                     ' bands but has %d electrons.' %
                                     (nbands_from_atom, setup.Nv))
                nbands += nbands_from_atom
            nbands = min(nao, nbands)
        elif nbands > nao and mode.name == 'lcao':
            raise ValueError('Too many bands for LCAO calculation: '
                             '%d bands and only %d atomic orbitals!' %
                             (nbands, nao))

        if nvalence < 0:
            raise ValueError(
                'Charge %f is not possible - not enough valence electrons' %
                par.charge)

        if nbands <= 0:
            nbands = int(nvalence + M + 0.5) // 2 + (-nbands)

        if nvalence > 2 * nbands and not orbital_free:
            raise ValueError('Too few bands!  Electrons: %f, bands: %d' %
                             (nvalence, nbands))

        self.create_occupations(magmom_a.sum(), orbital_free)

        if self.scf is None:
            self.create_scf(nvalence, mode)

        self.create_symmetry(magmom_a, cell_cv)

        if not self.wfs:
            self.create_wave_functions(mode, realspace, nspins, nbands, nao,
                                       nvalence, self.setups, magmom_a,
                                       cell_cv, pbc_c)
        else:
            self.wfs.set_setups(self.setups)

        if not self.wfs.eigensolver:
            self.create_eigensolver(xc, nbands, mode)

        if self.density is None and not reading:
            assert not par.fixdensity, 'No density to fix!'

        olddens = None
        if (self.density is not None and
            (self.density.gd.parsize_c != self.wfs.gd.parsize_c).any()):
            # Domain decomposition has changed, so we need to
            # reinitialize density and hamiltonian:
            if par.fixdensity:
                olddens = self.density

            self.density = None
            self.hamiltonian = None

        if self.density is None:
            self.create_density(realspace, mode, background)

        # XXXXXXXXXX if setups change, then setups.core_charge may change.
        # But that parameter was supplied in Density constructor!
        # This surely is a bug!
        self.density.initialize(self.setups, self.timer, magmom_a, par.hund)
        self.density.set_mixer(par.mixer)
        if self.density.mixer.driver.name == 'dummy' or par.fixdensity:
            self.log('No density mixing\n')
        else:
            self.log(self.density.mixer, '\n')
        self.density.fixed = par.fixdensity
        self.density.log = self.log

        if olddens is not None:
            self.density.initialize_from_other_density(olddens,
                                                       self.wfs.kptband_comm)

        if self.hamiltonian is None:
            self.create_hamiltonian(realspace, mode, xc)

        xc.initialize(self.density, self.hamiltonian, self.wfs,
                      self.occupations)

        if xc.name == 'GLLBSC' and olddens is not None:
            xc.heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeelp(olddens)

        self.print_memory_estimate(maxdepth=memory_estimate_depth + 1)

        print_parallelization_details(self.wfs, self.density, self.log)

        self.log('Number of atoms:', natoms)
        self.log('Number of atomic orbitals:', self.wfs.setups.nao)
        if self.nbands_parallelization_adjustment != 0:
            self.log(
                'Adjusting number of bands by %+d to match parallelization' %
                self.nbands_parallelization_adjustment)
        self.log('Number of bands in calculation:', self.wfs.bd.nbands)
        self.log('Bands to converge: ', end='')
        n = par.convergence.get('bands', 'occupied')
        if n == 'occupied':
            self.log('occupied states only')
        elif n == 'all':
            self.log('all')
        else:
            self.log('%d lowest bands' % n)
        self.log('Number of valence electrons:', self.wfs.nvalence)

        self.log(flush=True)

        self.timer.print_info(self)

        if dry_run:
            self.dry_run()

        if (realspace and self.hamiltonian.poisson.get_description()
                == 'FDTD+TDDFT'):
            self.hamiltonian.poisson.set_density(self.density)
            self.hamiltonian.poisson.print_messages(self.log)
            self.log.fd.flush()

        self.initialized = True
        self.log('... initialized\n')