Example #1
0
    def __init__(self, spgeom, infinite, eta=1e-6, bloch=None):
        """ Create a `SelfEnergy` object from any `SparseGeometry` """
        self.eta = eta
        if bloch is None:
            self.bloch = _a.onesi([3])
        else:
            self.bloch = _a.arrayi(bloch)

        # Determine whether we are in plus/minus direction
        if infinite.startswith('+'):
            self.semi_inf_dir = 1
        elif infinite.startswith('-'):
            self.semi_inf_dir = -1
        else:
            raise ValueError(self.__class__.__name__ + ": infinite keyword does not start with `+` or `-`.")

        # Determine the direction
        INF = infinite.upper()
        if INF.endswith('A'):
            self.semi_inf = 0
        elif INF.endswith('B'):
            self.semi_inf = 1
        elif INF.endswith('C'):
            self.semi_inf = 2

        # Check that the Hamiltonian does have a non-zero V along the semi-infinite direction
        if spgeom.geometry.sc.nsc[self.semi_inf] == 1:
            warn('Creating a semi-infinite self-energy with no couplings along the semi-infinite direction')

        # Finalize the setup by calling the class specific routine
        self._setup(spgeom)
Example #2
0
    def pivot(self, elec=None, in_device=False, sort=False):
        """ Return the pivoting indices for a specific electrode

        Parameters
        ----------
        elec : str or int
           the corresponding electrode to return the self-energy from
        in_device : bool, optional
           If ``True`` the pivoting table will be translated to the device region orbitals
        sort : bool, optional
           Whether the returned indices are sorted. Mostly useful if the self-energies are returned
           sorted as well.

        Examples
        --------
        >>> se = tbtsencSileTBtrans(...) # doctest: +SKIP
        >>> se.pivot() # doctest: +SKIP
        [3, 4, 6, 5, 2]
        >>> se.pivot(sort=True) # doctest: +SKIP
        [2, 3, 4, 5, 6]
        >>> se.pivot(0) # doctest: +SKIP
        [2, 3]
        >>> se.pivot(0, in_device=True) # doctest: +SKIP
        [4, 0]
        >>> se.pivot(0, in_device=True, sort=True) # doctest: +SKIP
        [0, 1]
        >>> se.pivot(0, sort=True) # doctest: +SKIP
        [2, 3]
        """
        if elec is None:
            if in_device and sort:
                return _a.arangei(self.no_d)
            pvt = self._value('pivot') - 1
            if in_device:
                # Count number of elements that we need to subtract from each orbital
                subn = _a.onesi(self.no)
                subn[pvt] = 0
                pvt -= _a.cumsumi(subn)[pvt]
            elif sort:
                pvt = np.sort(pvt)
            return pvt

        if in_device:
            pvt = self._value('pivot') - 1
            if sort:
                pvt = np.sort(pvt)

        # Get electrode pivoting elements
        se_pvt = self._value('pivot', tree=self._elec(elec)) - 1
        if sort:
            # Sort pivoting indices
            # Since we know that pvt is also sorted, then
            # the resulting in_device would also return sorted
            # indices
            se_pvt = np.sort(se_pvt)

        if in_device:
            # translate to the device indices
            se_pvt = in1d(pvt, se_pvt, assume_unique=True).nonzero()[0]
        return se_pvt
Example #3
0
    def write_header(self, bz, E, mu=0., obj=None):
        """ Write to the binary file the header of the file

        Parameters
        ----------
        bz : BrillouinZone
           contains the k-points, the weights and possibly the parent Hamiltonian (if `obj` is None)s
        E : array_like of cmplx or float
           the energy points. If `obj` is an instance of `SelfEnergy` where an
           associated ``eta`` is defined then `E` may be float, otherwise
           it *has* to be a complex array.
        mu : float, optional
           chemical potential in the file
        obj : ..., optional
           an object that contains the Hamiltonian definitions, defaults to ``bz.parent``
        """
        if obj is None:
            obj = bz.parent
        nspin = len(obj.spin)
        cell = obj.geometry.sc.cell * Ang2Bohr
        na_u = obj.geometry.na
        no_u = obj.geometry.no
        xa = obj.geometry.xyz * Ang2Bohr
        # The lasto in siesta requires lasto(0) == 0
        # and secondly, the Python index to fortran
        # index makes firsto behave like fortran lasto
        lasto = obj.geometry.firsto
        bloch = _a.onesi(3)
        mu = mu * eV2Ry
        NE = len(E)
        if E.dtype not in [np.complex64, np.complex128]:
            E = E + 1j * obj.eta
        Nk = len(bz)
        k = bz.k
        w = bz.weight

        sizes = {
            'na_used': na_u,
            'nkpt': Nk,
            'ne': NE,
        }

        self._nspin = nspin
        self._E = E * eV2Ry
        self._k = np.copy(k)
        if self._nspin > 2:
            self._no_u = no_u * 2
        else:
            self._no_u = no_u

        # Ensure it is open (in write mode)
        self._close_gf()
        self._open_gf('w')

        # Now write to it...
        _siesta.write_gf_header(self._iu, nspin, cell.T, na_u, no_u, no_u,
                                xa.T, lasto, bloch, 0, mu, k.T, w, self._E,
                                **sizes)
        _bin_check(self, 'write_header', 'could not write header information.')
Example #4
0
    def __init__(self, spgeom, infinite, eta=1e-6, bloch=None):
        """ Create a `SelfEnergy` object from any `SparseGeometry`

        This enables the calculation of the self-energy for a semi-infinite chain.

        Parameters
        ----------
        spgeom : SparseGeometry
           any sparse geometry matrix which may return matrices
        infinite : str
           axis specification for the semi-infinite direction (`+A`/`-A`/`+B`/`-B`/`+C`/`-C`)
        eta : float, optional
           the default imaginary part of the self-energy calculation
        bloch : array_like, optional
           Bloch-expansion for each of the lattice vectors (`1` for no expansion)
           The resulting self-energy will have dimension
           equal to `len(obj) * np.product(bloch)`.
        """
        self.eta = eta
        if bloch is None:
            self.bloch = _a.onesi([3])
        else:
            self.bloch = _a.arrayi(bloch)

        # Determine whether we are in plus/minus direction
        if infinite.startswith('+'):
            self.semi_inf_dir = 1
        elif infinite.startswith('-'):
            self.semi_inf_dir = -1
        else:
            raise ValueError(
                self.__class__.__name__ +
                ": infinite keyword does not start with `+` or `-`.")

        # Determine the direction
        INF = infinite.upper()
        if INF.endswith('A'):
            self.semi_inf = 0
        elif INF.endswith('B'):
            self.semi_inf = 1
        elif INF.endswith('C'):
            self.semi_inf = 2

        # Check that the Hamiltonian does have a non-zero V along the semi-infinite direction
        if spgeom.geometry.sc.nsc[self.semi_inf] == 1:
            warn(
                'Creating a semi-infinite self-energy with no couplings along the semi-infinite direction'
            )

        # Finalize the setup by calling the class specific routine
        self._setup(spgeom)
Example #5
0
    def write_header(self, E, bz, obj, mu=0.):
        """ Write to the binary file the header of the file

        Parameters
        ----------
        E : array_like of cmplx or float
           the energy points. If `obj` is an instance of `SelfEnergy` where an
           associated ``eta`` is defined then `E` may be float, otherwise
           it *has* to be a complex array.
        bz : BrillouinZone
           contains the k-points and their weights
        obj : ...
           an object that contains the Hamiltonian definitions
        """
        nspin = len(obj.spin)
        cell = obj.geom.sc.cell * Ang2Bohr
        na_u = obj.geom.na
        no_u = obj.geom.no
        xa = obj.geom.xyz * Ang2Bohr
        # The lasto in siesta requires lasto(0) == 0
        # and secondly, the Python index to fortran
        # index makes firsto behave like fortran lasto
        lasto = obj.geom.firsto
        bloch = _a.onesi(3)
        mu = mu * eV2Ry
        NE = len(E)
        if E.dtype not in [np.complex64, np.complex128]:
            E = E + 1j * obj.eta
        Nk = len(bz)
        k = bz.k
        w = bz.weight

        sizes = {
            'na_used': na_u,
            'nkpt': Nk,
            'ne': NE,
        }

        self._E = np.copy(E) * eV2Ry
        self._k = np.copy(k)

        # Ensure it is open
        self._close_gf()
        self._open_gf()

        # Now write to it...
        _siesta.write_gf_header(self._iu, nspin, cell.T, na_u, no_u, no_u,
                                xa.T, lasto, bloch, 0, mu, k.T, w, self._E,
                                **sizes)
Example #6
0
    def read_supercell_nsc(self, *args, **kwargs):
        """ Read supercell size using any method available

        Raises
        ------
        SislWarning if none of the files can be read
        """
        order = kwargs.pop('order', ['nc', 'ORB_INDX'])
        for f in order:
            v = getattr(self,
                        '_r_supercell_nsc_{}'.format(f.lower()))(*args,
                                                                 **kwargs)
            if v is not None:
                return v
        warn(
            'number of supercells could not be read from output files. Assuming molecule cell '
            '(no supercell connections)')
        return _a.onesi(3)
Example #7
0
    def pivot(self, in_device=False, sort=False):
        """ Pivoting orbitals for the full system

        Parameters
        ----------
        in_device : bool, optional
           whether the pivoting elements are with respect to the device region
        sort : bool, optional
           whether the pivoting elements are sorted
        """
        if in_device and sort:
            return _a.arangei(self.no_d)
        pvt = self._value('pivot') - 1
        if in_device:
            subn = _a.onesi(self.no)
            subn[pvt] = 0
            pvt -= _a.cumsumi(subn)[pvt]
        elif sort:
            pvt = np.sort(pvt)
        return pvt
Example #8
0
    def __init__(self, cell, nsc=None, origo=None):

        if nsc is None:
            nsc = [1, 1, 1]

        # If the length of cell is 6 it must be cell-parameters, not
        # actual cell coordinates
        self.cell = self.tocell(cell)

        if origo is None:
            self._origo = _a.zerosd(3)
        else:
            self._origo = _a.arrayd(origo)
            if self._origo.size != 3:
                raise ValueError("Origo *must* be 3 numbers.")

        # Set the volume
        self._update_vol()

        self.nsc = _a.onesi(3)
        # Set the super-cell
        self.set_nsc(nsc=nsc)
Example #9
0
    def pivot(self, elec=None, in_device=False, sort=False):
        """ Return the pivoting indices for a specific electrode (in the device region) or the device

        Parameters
        ----------
        elec : str or int
           the corresponding electrode to return the pivoting indices from
        in_device : bool, optional
           If ``True`` the pivoting table will be translated to the device region orbitals.
           If `sort` is also true, this would correspond to the orbitals directly translated
           to the geometry ``self.geometry.sub(self.a_dev)``.
        sort : bool, optional
           Whether the returned indices are sorted. Mostly useful if you want to handle
           the device in a non-pivoted order.

        Examples
        --------
        >>> se = tbtncSileTBtrans(...)
        >>> se.pivot()
        [3, 4, 6, 5, 2]
        >>> se.pivot(sort=True)
        [2, 3, 4, 5, 6]
        >>> se.pivot(0)
        [2, 3]
        >>> se.pivot(0, in_device=True)
        [4, 0]
        >>> se.pivot(0, in_device=True, sort=True)
        [0, 1]
        >>> se.pivot(0, sort=True)
        [2, 3]

        See Also
        --------
        pivot_down : for the pivot table for electrodes down-folding regions
        """
        if elec is None:
            if in_device and sort:
                return _a.arangei(self.no_d)
            pvt = self._value('pivot') - 1
            if in_device:
                # Count number of elements that we need to subtract from each orbital
                subn = _a.onesi(self.no)
                subn[pvt] = 0
                pvt -= _a.cumsumi(subn)[pvt]
            elif sort:
                pvt = npsort(pvt)
            return pvt

        # Get electrode pivoting elements
        se_pvt = self._value('pivot', tree=self._elec(elec)) - 1
        if sort:
            # Sort pivoting indices
            # Since we know that pvt is also sorted, then
            # the resulting in_device would also return sorted
            # indices
            se_pvt = npsort(se_pvt)

        if in_device:
            pvt = self._value('pivot') - 1
            if sort:
                pvt = npsort(pvt)
            # translate to the device indices
            se_pvt = indices(pvt, se_pvt, 0)
        return se_pvt