Ejemplo n.º 1
0
            def __call__(self, parser, ns, value, option_string=None):
                E = ns._E
                Emap = strmap(float, value, E.min(), E.max())
                def Eindex(e):
                    return np.abs(E - e).argmin()

                # Convert to actual indices
                E = []
                for begin, end in Emap:
                    if begin is None and end is None:
                        ns._Erng = None
                        return
                    elif begin is None:
                        E.append(range(Eindex(end)+1))
                    elif end is None:
                        E.append(range(Eindex(begin), len(E)))
                    else:
                        E.append(range(Eindex(begin), Eindex(end)+1))
                # Issuing unique also sorts the entries
                ns._Erng = np.unique(arrayi(E).flatten())
Ejemplo n.º 2
0
    def __call__(self, func, k, *args, **kwargs):
        """ Return a functions return values as the Bloch unfolded equivalent according to this object

        Calling the `Bloch` object is a shorthand for the manual use of the `Bloch.unfold_points` and `Bloch.unfold`
        methods.

        This call structure is a shorthand for:

        >>> bloch = Bloch([2, 1, 2])
        >>> k_unfold = bloch.unfold_points([0] * 3)
        >>> M = [func(*args, k=k) for k in k_unfold]
        >>> bloch.unfold(M, k_unfold)

        Notes
        -----
        The function passed *must* have a keyword argument ``k``.

        Parameters
        ----------
        func : callable
           method called which returns a matrix.
        k : (3, ) of float
           k-point to be unfolded
        *args : list
           arguments passed directly to `func`
        **kwargs: dict
           keyword arguments passed directly to `func`

        Returns
        -------
        M : unfolded Bloch matrix
        """
        K_unfold = self.unfold_points(k)
        M0 = func(*args, k=K_unfold[0, :], **kwargs)
        shape = (K_unfold.shape[0], M0.shape[0], M0.shape[1])
        M = empty(shape, dtype=dtype_real_to_complex(M0.dtype))
        M[0] = M0
        del M0
        for i in range(1, K_unfold.shape[0]):
            M[i] = func(*args, k=K_unfold[i, :], **kwargs)
        return bloch_unfold(_a.arrayi(self._bloch), K_unfold, M)
Ejemplo n.º 3
0
    def set_bc(self, boundary=None, a=None, b=None, c=None):
        """ Set the boundary conditions on the grid

        Parameters
        ----------
        boundary: (3, 2) or (3, ) or int, optional
           boundary condition for all boundaries (or the same for all)
        a: int or list of int, optional
           boundary condition for the first unit-cell vector direction
        b: int or list of int, optional
           boundary condition for the second unit-cell vector direction
        c: int or list of int, optional
           boundary condition for the third unit-cell vector direction

        Raises
        ------
        ValueError : if specifying periodic one one boundary, so must the opposite side.
        """
        if not boundary is None:
            if isinstance(boundary, Integral):
                self.bc = _a.arrayi([[boundary] * 2] * 3)
            else:
                self.bc = _a.asarrayi(boundary)
        if not a is None:
            self.bc[0, :] = a
        if not b is None:
            self.bc[1, :] = b
        if not c is None:
            self.bc[2, :] = c

        # shorthand for bc
        bc = self.bc[:, :]
        for i in range(3):
            if (bc[i, 0] == self.PERIODIC and bc[i, 1] != self.PERIODIC) or \
               (bc[i, 0] != self.PERIODIC and bc[i, 1] == self.PERIODIC):
                raise ValueError(self.__class__.__name__ + '.set_bc has a one non-periodic and '
                                 'one periodic direction. If one direction is periodic, both instances '
                                 'must have that BC.')
Ejemplo n.º 4
0
    def read_supercell_nsc(self):
        """ Reads the supercell number of supercell information """
        # First line contains no no_s
        line = self.readline().split()
        no_s = int(line[1])
        self.readline()
        self.readline()
        nsc = [0] * 3

        def int_abs(i):
            return abs(int(i))

        for _ in range(no_s):
            line = self.readline().split()
            isc = list(map(int_abs, line[12:15]))
            if isc[0] > nsc[0]:
                nsc[0] = isc[0]
            if isc[1] > nsc[1]:
                nsc[1] = isc[1]
            if isc[2] > nsc[2]:
                nsc[2] = isc[2]

        return arrayi([n * 2 + 1 for n in nsc])
Ejemplo n.º 5
0
Archivo: delta.py Proyecto: sjumzw/sisl
    def _read_class(self, cls, **kwargs):
        """ Reads a class model from a file """

        # Ensure that the geometry is written
        geom = self.read_geometry()

        # Determine the type of delta we are storing...
        E = kwargs.get('E', None)

        ilvl, ik, iE = self._get_lvl_k_E(**kwargs)

        # Get the level
        lvl = self._get_lvl(ilvl)

        if iE < 0 and ilvl in [3, 4]:
            raise ValueError(f"Energy {E} eV does not exist in the file.")
        if ik < 0 and ilvl in [2, 4]:
            raise ValueError("k-point requested does not exist in the file.")

        if ilvl == 1:
            sl = [slice(None)] * 2
        elif ilvl == 2:
            sl = [slice(None)] * 3
            sl[0] = ik
        elif ilvl == 3:
            sl = [slice(None)] * 3
            sl[0] = iE
        elif ilvl == 4:
            sl = [slice(None)] * 4
            sl[0] = ik
            sl[1] = iE

        # Now figure out what data-type the delta is.
        if 'Redelta' in lvl.variables:
            # It *must* be a complex valued Hamiltonian
            is_complex = True
            dtype = np.complex128
        elif 'delta' in lvl.variables:
            is_complex = False
            dtype = np.float64

        # Get number of spins
        nspin = len(self.dimensions['spin'])

        # Now create the sparse matrix stuff (we re-create the
        # array, hence just allocate the smallest amount possible)
        C = cls(geom, nspin, nnzpr=1, dtype=dtype, orthogonal=True)

        C._csr.ncol = _a.arrayi(lvl.variables['n_col'][:])
        # Update maximum number of connections (in case future stuff happens)
        C._csr.ptr = np.insert(_a.cumsumi(C._csr.ncol), 0, 0)
        C._csr.col = _a.arrayi(lvl.variables['list_col'][:]) - 1

        # Copy information over
        C._csr._nnz = len(C._csr.col)
        C._csr._D = np.empty([C._csr.ptr[-1], nspin], dtype)
        if is_complex:
            for ispin in range(nspin):
                sl[-2] = ispin
                C._csr._D[:, ispin].real = lvl.variables['Redelta'][sl] * Ry2eV
                C._csr._D[:, ispin].imag = lvl.variables['Imdelta'][sl] * Ry2eV
        else:
            for ispin in range(nspin):
                sl[-2] = ispin
                C._csr._D[:, ispin] = lvl.variables['delta'][sl] * Ry2eV

        _mat_spin_convert(C)

        return C
Ejemplo n.º 6
0
 def __init__(self, bloch):
     """ Create `Bloch` object """
     self._bloch = _a.arrayi(bloch)
     self._bloch = np.where(self._bloch < 1, 1, self._bloch)
Ejemplo n.º 7
0
    def initialize(self):
        """ Initialize the internal data-arrays used for efficient calculation of the real-space quantities

        This method should first be called *after* all options has been specified.

        If the user hasn't specified the ``bz`` value as an option this method will update the internal
        integration Brillouin zone based on the ``dk`` option.
        """
        # Try and guess the directions
        unfold = self._unfold
        nsc = self.parent.nsc.copy()
        axes = self._options['axes']
        if axes is None:
            if nsc[2] == 1:
                axes = _a.arrayi([0, 1])
            elif nsc[1] == 1:
                axes = _a.arrayi([0, 2])
            elif nsc[0] == 1:
                axes = _a.arrayi([1, 2])
            else:
                raise ValueError(self.__class__.__name__ + '.initialize currently only supports a 2D real-space self-energy, hence the MonkhorstPack grid should reflect only 2 periodic directions.')

            self._options['axes'] = axes

        # Check that we have periodicity along the chosen axes
        nsc_sum = nsc[axes].sum()
        if nsc_sum == 1:
            raise ValueError(self.__class__.__name__ + '.initialize found no periodic directions for the chosen integration axes: {} and {}.'.format(*nsc[axes]))
        elif nsc_sum < 6:
            raise ValueError((self.__class__.__name__ + '.initialize found one periodic direction '
                              'out of two for the chosen integration axes: {} and {}. '
                              'For 1D systems the regular surface self-energy method is appropriate.').format(*nsc[axes]))

        if self._options['semi_axis'] is None and self._options['k_axis'] is None:
            # None of the axis has been described
            if nsc[axes[0]] > 3:
                k_ax = axes[0]
                s_ax = axes[1]
            elif nsc[axes[1]] > 3:
                k_ax = axes[1]
                s_ax = axes[0]
            else:
                # Choose the direction of k to be the smallest shortest
                sc = self.parent.sc.tile(unfold[axes[0]], axes[0]).tile(unfold[axes[1]], axes[1])
                rcell = fnorm(sc.rcell)[axes]
                k_ax = axes[np.argmax(rcell)]
                if k_ax == axes[0]:
                    s_ax = axes[1]
                else:
                    s_ax = axes[0]
            self._options['semi_axis'] = s_ax
            self._options['k_axis'] = k_ax

            s_ax = 'ABC'[s_ax]
            k_ax = 'ABC'[k_ax]
            info(self.__class__.__name__ + '.initialize determined the semi-inf- and k-directions to be: {} and {}'.format(s_ax, k_ax))

        elif self._options['k_axis'] is None:
            s_ax = self._options['semi_axis']
            if s_ax == axes[0]:
                k_ax = axes[1]
            else:
                k_ax = axes[0]
            self._options['k_axis'] = k_ax

            k_ax = 'ABC'[k_ax]
            info(self.__class__.__name__ + '.initialize determined the k direction to be: {}'.format(k_ax))

        elif self._options['semi_axis'] is None:
            k_ax = self._options['k_axis']
            if k_ax == axes[0]:
                s_ax = axes[1]
            else:
                s_ax = axes[0]
            self._options['semi_axis'] = s_ax

            s_ax = 'ABC'[s_ax]
            info(self.__class__.__name__ + '.initialize determined the semi-infinite direction to be: {}'.format(s_ax))

        k_ax = self._options['k_axis']
        s_ax = self._options['semi_axis']
        if nsc[s_ax] != 3:
            raise ValueError(self.__class__.__name__ + '.initialize found the self-energy direction to be '
                             'incompatible with the parent object. It *must* have 3 supercells along the '
                             'semi-infinite direction.')

        P0 = self.real_space_parent()
        V_atoms = self.real_space_coupling(True)[1]
        self._calc = {
            # The below algorithm requires the direction to be negative
            # if changed, B, C should be reversed below
            'SE': RecursiveSI(self.parent, '-' + 'ABC'[s_ax], eta=self._options['eta']),
            # Used to calculate the real-space self-energy
            'P0': P0.Pk(), # in sparse format
            'S0': P0.Sk(), # in sparse format
            # Orbitals in the coupling atoms
            'orbs': P0.a2o(V_atoms, True).reshape(-1, 1),
        }

        # Update the BrillouinZone integration grid in case it isn't specified
        if self._options['bz'] is None:
            # Update the integration grid
            # Note this integration grid is based on the big system.
            sc = self.parent.sc.tile(unfold[axes[0]], axes[0]).tile(unfold[axes[1]], axes[1])
            rcell = fnorm(sc.rcell)[k_ax]
            nk = [1] * 3
            nk[k_ax] = int(self._options['dk'] * rcell)
            self._options['bz'] = MonkhorstPack(sc, nk, trs=self._options['trs'])
            info(self.__class__.__name__ + '.initialize determined the number of k-points: {}'.format(nk[k_ax]))
Ejemplo n.º 8
0
    def get_constraints(self, factor=0.95):
        """ Return contraints for the zeta channels """

        # Now we define the constraints of the orbitals.
        def unpack(name):
            try:
                # split at name
                symbol, name, zeta = name.split(".")
                n = int(name[1])
                l = int(name[3])
                zeta = int(zeta[1:])
                return symbol, n, l, zeta
            except:
                return None, None, None, None

        orb_R = {}  # (n,l) = {1: idx-nlzeta=1, 2: idx-nlzeta=2}
        for i, v in enumerate(self.variables):
            symbol, n, l, zeta = unpack(v.name)
            if symbol is None:
                continue
            (orb_R.setdefault(symbol, {}).setdefault((n, l),
                                                     {}).update({zeta: i}))

        def assert_bounds(i1, i2):
            v1 = self.variables[i1]
            v2 = self.variables[i2]
            b1 = v1.bounds
            b2 = v2.bounds
            if not np.allclose(b1, b2):
                raise ValueError(
                    "Bounds for zeta must be the same due to normalization")

        # get two lists of neighbouring zeta's
        # Our constraint is that zeta cutoffs should be descending.
        zeta1, zeta2 = [], []
        # v now contains a dictionary with indices for the zeta orbitals
        for atom in orb_R.values():
            for z_idx in atom.values():
                for i in range(2, max(z_idx.keys()) + 1):
                    # this will request zeta-indices in order (zeta1, zeta2, ...)
                    zeta1.append(z_idx[i - 1])
                    zeta2.append(z_idx[i])
                    assert_bounds(zeta1[-1], zeta2[-1])

        zeta1 = arrayi(zeta1)
        zeta2 = arrayi(zeta2)

        # now create constraint
        def fun_factory(factor, zeta1, zeta2):
            def fun(v):
                # an inequality constraint must return a non-negative
                # zeta1.R * `factor` - zeta2.R >= 0.
                return v[zeta1] * factor - v[zeta2]

            return fun

        def jac_factory(factor, zeta1, zeta2):
            def jac(v):
                out = zerosd([len(zeta1), len(v)])
                idx = arangei(len(zeta1))
                # derivative of
                # zeta1.R * `factor` - zeta2.R >= 0.
                out[idx, zeta1] = factor
                out[idx, zeta2] = -1
                return out

            return jac

        constr = []
        constr.append({
            "type": "ineq",
            "fun": fun_factory(factor, zeta1, zeta2),
            "jac": jac_factory(factor, zeta1, zeta2),
        })

        return constr
Ejemplo n.º 9
0
    def _r_geometry_multiple(self, steps, ret_data=False, squeeze=False):
        asteps = steps
        steps = dict((step, i) for i, step in enumerate(steps))

        # initialize all things
        cell = [None] * len(steps)
        cell_set = [False] * len(steps)
        xyz_set = [False] * len(steps)
        atom = [None for _ in steps]
        xyz = [None for _ in steps]
        data = [None for _ in steps]
        data_set = [not ret_data for _ in steps]

        line = " "
        all_loaded = False

        while line != '' and not all_loaded:
            line = self.readline()

            if line.isspace():
                continue
            kw = line.split()[0]
            if kw not in ("CONVVEC", "PRIMVEC", "PRIMCOORD"):
                continue

            step = _get_kw_index(line)
            if step != -1 and step not in steps:
                continue

            if step not in steps and step == -1:
                step = idstep = istep = None
            else:
                idstep = steps[step]
                istep = idstep

            if kw == "CONVVEC":
                if step is None:
                    if not any(cell_set):
                        cell_set = [True] * len(cell_set)
                    else:
                        continue
                elif cell_set[istep]:
                    continue
                else:
                    cell_set[istep] = True

                icell = _a.zerosd([3, 3])
                for i in range(3):
                    line = self.readline()
                    icell[i] = line.split()
                if step is None:
                    cell = [icell] * len(cell)
                else:
                    cell[istep] = icell

            elif kw == "PRIMVEC":
                if step is None:
                    cell_set = [True] * len(cell_set)
                else:
                    cell_set[istep] = True

                icell = _a.zerosd([3, 3])
                for i in range(3):
                    line = self.readline()
                    icell[i] = line.split()
                if step is None:
                    cell = [icell] * len(cell)
                else:
                    cell[istep] = icell

            elif kw == "PRIMCOORD":
                if step is None:
                    raise ValueError(f"{self.__class__.__name__}"
                        " contains an unindexed (or somehow malformed) 'PRIMCOORD'"
                        " section but you've asked for a particular index. This"
                        f" shouldn't happen. line:\n {line}"
                    )

                iatom = []
                ixyz = []
                idata = []
                line = self.readline().split()
                for _ in range(int(line[0])):
                    line = self.readline().split()
                    if not xyz_set[istep]:
                        iatom.append(int(line[0]))
                        ixyz.append([float(x) for x in line[1:4]])
                    if ret_data and len(line) > 4:
                        idata.append([float(x) for x in line[4:]])
                if not xyz_set[istep]:
                    atom[istep] = iatom
                    xyz[istep] = ixyz
                    xyz_set[istep] = True
                data[idstep] = idata
                data_set[idstep] = True

            all_loaded = all(xyz_set) and all(cell_set) and all(data_set)

        if not all(xyz_set):
            which = [asteps[i] for i in np.flatnonzero(xyz_set)]
            raise ValueError(f"{self.__class__.__name__} file did not contain atom coordinates for the following requested index: {which}")

        if ret_data:
            data = _a.arrayd(data)
            if data.size == 0:
                data.shape = (len(steps), len(xyz[0]), 0)

        xyz = _a.arrayd(xyz)
        cell = _a.arrayd(cell)
        atom = _a.arrayi(atom)

        geoms = []
        for istep in range(len(steps)):
            if len(atom) == 0:
                geoms.append(si.Geometry(xyz[istep], sc=SuperCell(cell[istep])))
            elif len(atom[0]) == 1 and atom[0][0] == -999:
                # should we perhaps do AtomUnknown?
                geoms.append(None)
            else:
                geoms.append(Geometry(xyz[istep], atoms=atom[istep], sc=SuperCell(cell[istep])))

        if squeeze and len(steps) == 1:
            geoms = geoms[0]
            if ret_data:
                data = data[0]

        if ret_data:
            return geoms, data
        return geoms
Ejemplo n.º 10
0
 def sc_off(self, sc_off):
     """ Set the supercell offset """
     self._sc_off[:, :] = _a.arrayi(sc_off, order='C')
     self._update_isc_off()