Example #1
0
    def shift(self, E, DM):
        r""" Shift the energy density matrix to a common energy by using a reference density matrix

        This is equal to performing this operation:

        .. math::
           \mathfrak E_\sigma = \mathfrak E_\sigma + E \boldsymbol \rho_\sigma

        where :math:`\mathfrak E_\sigma` correspond to the spin diagonal components of the
        energy density matrix and :math:`\boldsymbol \rho_\sigma` is the spin diagonal
        components of the corresponding density matrix.

        Parameters
        ----------
        E : float or (2,)
           the energy (in eV) to shift the energy density matrix, if two values are passed
           the two first spin-components get shifted individually.
        DM : DensityMatrix
           density matrix corresponding to the same geometry
        """
        if not self.spsame(DM):
            raise SislError(self.__class__.__name__ +
                            '.shift requires the input DM to have '
                            'the same sparsity as the shifted object.')

        E = _a.asarrayd(E)
        if E.size == 1:
            E = np.tile(E, 2)

        if np.abs(E).sum() == 0.:
            # When the energy is zero, there is no shift
            return

        for i in range(min(self.spin.spins, 2)):
            self._csr._D[:, i] += DM._csr._D[:, i] * E[i]
Example #2
0
 def find_atom(tag):
     if atoms is None:
         return Atom(tag)
     for atom in atoms:
         if atom.tag == tag:
             return atom
     raise SislError(f'Error when reading the basis for atomic tag: {tag}.')
Example #3
0
    def write_hamiltonian(self, H, **kwargs):
        """ Writes the Hamiltonian to a siesta.TSHS file """
        # Ensure the Hamiltonian is finalized
        H.finalize()

        # Extract the data to pass to the fortran routine

        cell = H.geom.cell * Ang2Bohr
        xyz = H.geom.xyz * Ang2Bohr

        # Pointer to CSR matrix
        csr = H._csr

        # Get H and S
        if H.orthogonal:
            h = (csr._D * eV2Ry).astype(np.float64, 'C', copy=False)
            s = csr.diags(1., dim=1)
            # Ensure all data is correctly formatted (i.e. have the same sparsity pattern
            s.align(csr)
            s.finalize()
            if s.nnz != len(h):
                raise SislError(
                    "The diagonal elements of your orthogonal Hamiltonian have not been defined, "
                    "this is a requirement.")
            s = (s._D[:, 0]).astype(np.float64, 'C', copy=False)
        else:
            h = (csr._D[:, :H.S_idx] * eV2Ry).astype(np.float64,
                                                     'C',
                                                     copy=False)
            s = (csr._D[:, H.S_idx]).astype(np.float64, 'C', copy=False)
        # Ensure shapes (say if only 1 spin)
        h.shape = (-1, len(H.spin))
        s.shape = (-1, )

        # Get shorter variants
        nsc = H.geom.nsc[:]
        isc = H.geom.sc.sc_off[:, :]

        # I can't seem to figure out the usage of f2py
        # Below I get an error if xyz is not transposed and h is transposed,
        # however, they are both in C-contiguous arrays and this is indeed weird... :(
        _siesta.write_tshs_hs(self.file,
                              nsc[0],
                              nsc[1],
                              nsc[2],
                              cell.T,
                              xyz.T,
                              H.geom.firsto,
                              csr.ncol,
                              csr.col + 1,
                              h,
                              s,
                              isc.T,
                              nspin=len(H.spin),
                              na_u=H.geom.na,
                              no_u=H.geom.no,
                              nnz=H.nnz)
Example #4
0
    def write_hamiltonian(self, H, **kwargs):
        """ Writes the Hamiltonian to a siesta.TSHS file """
        csr = H._csr.copy()
        if csr.nnz == 0:
            raise SileError(
                str(self) + '.write_hamiltonian cannot write '
                'a zero element sparse matrix!')

        # Convert to siesta CSR
        _csr_to_siesta(H.geometry, csr)
        # TODO consider removing finalize here!
        # If tbtrans really needs this, then we should definitely do this
        # in tbtrans!
        # I.e. we should probably just do finalize(sort=False)
        csr.finalize()
        _mat_spin_convert(csr, H.spin)

        # Extract the data to pass to the fortran routine
        cell = H.geometry.cell
        xyz = H.geometry.xyz

        # Get H and S
        if H.orthogonal:
            h = csr._D.astype(np.float64, 'C', copy=False)
            s = csr.diags(1., dim=1)
            # Ensure all data is correctly formatted (i.e. have the same sparsity pattern
            s.align(csr)
            s.finalize()
            if s.nnz != len(h):
                raise SislError(
                    'The diagonal elements of your orthogonal Hamiltonian '
                    'have not been defined, this is a requirement.')
            s = (s._D[:, 0]).astype(np.float64, 'C', copy=False)
        else:
            h = csr._D[:, :H.S_idx].astype(np.float64, 'C', copy=False)
            s = csr._D[:, H.S_idx].astype(np.float64, 'C', copy=False)
        # Ensure shapes (say if only 1 spin)
        h.shape = (-1, len(H.spin))
        s.shape = (-1, )

        # Get shorter variants
        nsc = H.geometry.nsc[:].astype(np.int32)
        isc = _siesta.siesta_sc_off(*nsc)

        # I can't seem to figure out the usage of f2py
        # Below I get an error if xyz is not transposed and h is transposed,
        # however, they are both in C-contiguous arrays and this is indeed weird... :(
        _siesta.write_tshs_hs(self.file, nsc[0], nsc[1], nsc[2], cell.T, xyz.T,
                              H.geometry.firsto, csr.ncol, csr.col + 1, h, s,
                              isc)
        _bin_check(self, 'write_hamiltonian',
                   'could not write Hamiltonian and overlap matrix.')
Example #5
0
File: omx.py Project: silsgs/sisl
    def _r_geometry_omx(self, *args, **kwargs):
        """ Returns `Geometry` """
        sc = self.read_supercell(order=['omx'])

        na = self.get('Atoms.Number', default=0)
        conv = self.get('Atoms.SpeciesAndCoordinates.Unit', default='Ang')
        data = self.get('Atoms.SpeciesAndCoordinates')
        if data is None:
            raise SislError('Cannot find key: Atoms.SpeciesAndCoordinates')

        if na == 0:
            # Default to the size of the labels
            na = len(data)

        # Reduce to the number of atoms.
        data = data[:na]

        atoms = self.read_basis(order=['omx'])

        def find_atom(tag):
            if atoms is None:
                return Atom(tag)
            for atom in atoms:
                if atom.tag == tag:
                    return atom
            raise SislError(
                'Error when reading the basis for atomic tag: {}.'.format(tag))

        xyz = []
        atom = []
        for dat in data:
            d = dat.split()
            atom.append(find_atom(d[1]))
            xyz.append(list(map(float, dat.split()[2:5])))
        xyz = _a.arrayd(xyz)

        if conv == 'AU':
            xyz *= units('Bohr', 'Ang')
        elif conv == 'FRAC':
            xyz = np.dot(xyz, sc.cell)

        return Geometry(xyz, atom=atom, sc=sc)
Example #6
0
        def _call(self, *args, **kwargs):
            data_axis = kwargs.pop('data_axis', None)
            grid_unit = kwargs.pop('grid_unit', 'b')

            func = getattr(self.parent, self._bz_attr)
            wrap = allow_kwargs('parent', 'k', 'weight')(kwargs.pop('wrap', _do_nothing))
            eta = tqdm_eta(len(self), self.__class__.__name__ + '.asgrid',
                           'k', kwargs.pop('eta', False))
            parent = self.parent
            k = self.k
            w = self.weight

            # Extract information from the MP grid, these values
            # define the Grid size, etc.
            diag = self._diag.copy()
            if not np.all(self._displ == 0):
                raise SislError(self.__class__.__name__ + '.{} requires the displacement to be 0 for all k-points.'.format(self._bz_attr))
            displ = self._displ.copy()
            size = self._size.copy()
            steps = size / diag
            if self._centered:
                offset = np.where(diag % 2 == 0, steps, steps / 2)
            else:
                offset = np.where(diag % 2 == 0, steps / 2, steps)

            # Instead of doing
            #    _in_primitive(k) + 0.5 - offset
            # we can do it here
            #    _in_primitive(k) + offset'
            offset -= 0.5

            # Check the TRS direction
            trs_axis = self._trs
            _in_primitive = self.in_primitive
            _rint = np.rint
            _int32 = np.int32
            def k2idx(k):
                # In case TRS is applied two indices may be returned
                return _rint((_in_primitive(k) - offset) / steps).astype(_int32)
                # To find the opposite k-point, do this
                #  idx[i] = [diag[i] - idx[i] - 1, idx[i]
                # with i in [0, 1, 2]

            # Create cell from the reciprocal cell.
            if grid_unit == 'b':
                cell = np.diag(self._size)
            else:
                cell = parent.sc.rcell * self._size.reshape(1, -1) / units('Ang', grid_unit)

            # Find the grid origo
            origo = -(cell * 0.5).sum(0)

            # Calculate first k-point (to get size and dtype)
            v = wrap(func(*args, k=k[0], **kwargs), parent=parent, k=k[0], weight=w[0])

            if data_axis is None:
                if v.size != 1:
                    raise SislError(self.__class__.__name__ + '.{} requires one value per-kpoint because of the 3D grid values'.format(self._bz_attr))

            else:

                # Check the weights
                weights = self.grid(diag[data_axis], displ[data_axis], size[data_axis],
                                    centered=self._centered, trs=trs_axis == data_axis)[1]

                # Correct the Grid size
                diag[data_axis] = len(v)
                # Create the orthogonal cell direction to ensure it is orthogonal
                # Since array axis is cyclic for negative numbers, we simply do this
                cell[data_axis, :] = cross(cell[data_axis-1, :], cell[data_axis-2, :])
                # Check whether we should rotate it
                if cart2spher(cell[data_axis, :])[2] > pi / 4:
                    cell[data_axis, :] *= -1

            # Correct cell for the grid
            if trs_axis >= 0:
                origo[trs_axis] = 0.
                # Correct offset since we only have the positive halve
                if self._diag[trs_axis] % 2 == 0 and not self._centered:
                    offset[trs_axis] = steps[trs_axis] / 2
                else:
                    offset[trs_axis] = 0.

                # Find number of points
                if trs_axis != data_axis:
                    diag[trs_axis] = len(self.grid(diag[trs_axis], displ[trs_axis], size[trs_axis],
                                                   centered=self._centered, trs=True)[1])

            # Create the grid in the reciprocal cell
            sc = SuperCell(cell, origo=origo)
            grid = Grid(diag, sc=sc, dtype=v.dtype)
            if data_axis is None:
                grid[k2idx(k[0])] = v
            else:
                idx = k2idx(k[0]).tolist()
                weight = weights[idx[data_axis]]
                idx[data_axis] = slice(None)
                grid[idx] = v * weight

            del v

            # Now perform calculation
            if data_axis is None:
                for i in range(1, len(k)):
                    grid[k2idx(k[i])] = wrap(func(*args, k=k[i], **kwargs),
                                             parent=parent, k=k[i], weight=w[i])
                    eta.update()
            else:
                for i in range(1, len(k)):
                    idx = k2idx(k[i]).tolist()
                    weight = weights[idx[data_axis]]
                    idx[data_axis] = slice(None)
                    grid[idx] = wrap(func(*args, k=k[i], **kwargs),
                                     parent=parent, k=k[i], weight=w[i]) * weight
                    eta.update()
            eta.close()
            return grid
Example #7
0
    def replace(self, k, mp):
        r""" Replace a k-point with a new set of k-points from a Monkhorst-Pack grid

        This method tries to replace an area corresponding to `mp.size` around the k-point `k`
        such that the k-points are replaced.
        This enables one to zoom in on specific points in the Brillouin zone for detailed analysis.

        Parameters
        ----------
        k : array_like
           k-point in this object to replace
        mp : MonkhorstPack
           object containing the replacement k-points.

        Examples
        --------

        This example creates a zoomed-in view of the :math:`\Gamma`-point by replacing it with
        a 3x3x3 Monkhorst-Pack grid.

        >>> sc = SuperCell(1.)
        >>> mp = MonkhorstPack(sc, [3, 3, 3])
        >>> mp.replace([0, 0, 0], MonkhorstPack(sc, [3, 3, 3], size=1./3))

        This example creates a zoomed-in view of the :math:`\Gamma`-point by replacing it with
        a 4x4x4 Monkhorst-Pack grid.

        >>> sc = SuperCell(1.)
        >>> mp = MonkhorstPack(sc, [3, 3, 3])
        >>> mp.replace([0, 0, 0], MonkhorstPack(sc, [4, 4, 4], size=1./3))

        This example creates a zoomed-in view of the :math:`\Gamma`-point by replacing it with
        a 4x4x1 Monkhorst-Pack grid.

        >>> sc = SuperCell(1.)
        >>> mp = MonkhorstPack(sc, [3, 3, 3])
        >>> mp.replace([0, 0, 0], MonkhorstPack(sc, [4, 4, 1], size=1./3))

        Raises
        ------
        SislError : if the size of the replacement `MonkhorstPack` grid is not compatible with the
                    k-point spacing in this object.
        """
        # First we find all k-points within k +- mp.size
        # Those are the points we wish to remove.
        # Secondly we need to ensure that the k-points we remove are occupying *exactly*
        # the Brillouin zone we wish to replace.
        if not isinstance(mp, MonkhorstPack):
            raise ValueError('Object `mp` is not a MonkhorstPack object')

        # We can easily figure out the BZ that each k-point is averaging
        k_vol = self._size / self._diag
        # Compare against the size of this one
        # Since we can remove more than one k-point, we require that the
        # size of the replacement MP is an integer multiple of the
        # k-point volumes.
        k_int = mp._size / k_vol
        if not np.allclose(np.rint(k_int), k_int):
            raise SislError(self.__class__.__name__ + '.reduce could not replace k-point, BZ '
                            'volume replaced is not equivalent to the inherent k-point volume.')
        k_int = np.rint(k_int)

        # 1. find all k-points
        k = self.in_primitive(k).reshape(1, 3)
        dk = (mp._size / 2).reshape(1, 3)
        # Find all points within [k - dk; k + dk]
        # Since the volume of each k-point is non-zero we know that no k-points will be located
        # on the boundary.
        # This does remove boundary points because we shift everything into the positive
        # plane.
        diff_k = self.in_primitive(self.k % 1. - k % 1.)
        idx = np.logical_and.reduce(np.abs(diff_k) <= dk, axis=1).nonzero()[0]
        if len(idx) == 0:
            raise SislError(self.__class__.__name__ + '.reduce could not find any points to replace.')

        # Now we have the k-points we need to remove
        # Figure out if the total weight is consistent
        total_weight = self.weight[idx].sum()
        replace_weight = mp.weight.sum()
        if abs(total_weight - replace_weight) < 1e-8:
            weight_factor = 1.
        elif abs(total_weight - replace_weight * 2) < 1e-8:
            weight_factor = 2.
            if self._trs < 0:
                info(self.__class__.__name__ + '.reduce assumes that the replaced k-point has double weights.')
        else:
            print('k-point to replace:')
            print(' ', k.ravel())
            print('delta-k:')
            print(' ', dk.ravel())
            print('Found k-indices that will be replaced:')
            print(' ', idx)
            print('k-points replaced:')
            print(self.k[idx, :])
            raise SislError(self.__class__.__name__ + '.reduce could not assert the weights are consistent during replacement.')

        self._k = np.delete(self._k, idx, axis=0)
        self._w = np.delete(self._w, idx)

        # Append the new k-points and weights
        self._k = np.concatenate((self._k, mp._k), axis=0)
        self._w = np.concatenate((self._w, mp._w * weight_factor))