Example #1
0
    def fromsp(cls, geom, P, S=None):
        """ Read and return the object with possible overlap """
        # Calculate maximum number of connections per row
        nc = 0

        # Ensure list of csr format (to get dimensions)
        if isspmatrix(P):
            P = [P]

        # Number of dimensions
        dim = len(P)
        # Sort all indices for the passed sparse matrices
        for i in range(dim):
            P[i] = P[i].tocsr()
            P[i].sort_indices()

        # Figure out the maximum connections per
        # row to reduce number of re-allocations to 0
        for i in range(P[0].shape[0]):
            nc = max(nc, P[0][i, :].getnnz())

        # Create the sparse object
        v = cls(geom, dim, P[0].dtype, nc, orthogonal=S is None)

        for i in range(dim):
            for jo, io, vv in ispmatrixd(P[i]):
                v[jo, io, i] = vv

        if not S is None:
            for jo, io, vv in ispmatrixd(S):
                v.S[jo, io] = vv

        return v
Example #2
0
    def _Hk_non_collinear(self, k=(0, 0, 0), dtype=None):
        """ Return the Hamiltonian in a ``scipy.sparse.csr_matrix`` at `k` for a non-collinear
        Hamiltonian.

        Parameters
        ----------
        k: ``array_like``, `[0,0,0]`
           k-point 
        dtype : ``numpy.dtype``
           default to `numpy.complex128`
        """
        if dtype is None:
            dtype = np.complex128

        if np.dtype(dtype).kind != 'c':
            raise ValueError(
                "Non-collinear Hamiltonian setup requires a complex matrix")

        exp = np.exp
        dot = np.dot

        k = np.asarray(k, np.float64)
        k.shape = (-1, )

        no = self.no * 2
        s = (no, no)
        H = csr_matrix(s, dtype=dtype)

        # get back-dimension of the intrinsic sparse matrix
        no = self.no

        # Get the reciprocal lattice vectors dotted with k
        kr = dot(self.rcell, k)
        for si in range(self.sc.n_s):
            isc = self.sc_off[si, :]
            phase = exp(-1j * dot(kr, dot(self.cell, isc)))

            # diagonal elements
            Hf1 = self.tocsr(0)[:, si * no:(si + 1) * no] * phase
            for i, j, h in ispmatrixd(Hf1):
                H[i * 2, j * 2] += h
            Hf1 = self.tocsr(1)[:, si * no:(si + 1) * no] * phase
            for i, j, h in ispmatrixd(Hf1):
                H[1 + i * 2, 1 + j * 2] += h

            # off-diagonal elements
            Hf1 = self.tocsr(2)[:, si * no:(si + 1) * no]
            Hf2 = self.tocsr(3)[:, si * no:(si + 1) * no]
            # We expect Hf1 and Hf2 to be aligned equivalently!
            # TODO CHECK
            for i, j, hr in ispmatrixd(Hf1):
                # get value for the imaginary part
                hi = Hf2[i, j]
                H[i * 2, 1 + j * 2] += (hr - 1j * hi) * phase
                H[1 + i * 2, j * 2] += (hr + 1j * hi) * phase

        del Hf1, Hf2

        return H
Example #3
0
    def sp2HS(cls, geom, H, S=None):
        """ Returns a tight-binding model from a preset H, S and Geometry
        """
        # Calculate number of connections
        nc = 0

        has_S = not S is None

        # Ensure csr format
        H = H.tocsr()
        if has_S:
            S = S.tocsr()
        for i in range(geom.no):
            nc = max(nc, H[i, :].getnnz())
            if has_S:
                nc = max(nc, S[i, :].getnnz())

        # Create the Hamiltonian
        ham = cls(geom, nnzpr=nc, orthogonal=not has_S, dtype=H.dtype)

        # Copy data to the model
        if has_S:
            for jo, io in ispmatrix(H):
                ham.S[jo, io] = S[jo, io]

            # If the Hamiltonian for one reason or the other
            # is zero in the diagonal, then we *must* account for
            # this as it isn't captured in the above loop.
            skip_S = np.all(H.row == S.row)
            skip_S = skip_S and np.all(H.col == S.col)
            skip_S = False
            if not skip_S:
                # Re-convert back to allow index retrieval
                H = H.tocsr()
                for jo, io, s in ispmatrixd(S):
                    ham[jo, io] = (H[jo, io], s)

        else:
            for jo, io, h in ispmatrixd(H):
                ham[jo, io] = h

        return ham
Example #4
0
    def _Sk_non_collinear(self, k=(0, 0, 0), dtype=None):
        """ Return the Hamiltonian in a ``scipy.sparse.csr_matrix`` at `k`.

        Parameters
        ----------
        k: ``array_like``, `[0,0,0]`
           k-point 
        dtype : ``numpy.dtype``
           default to `numpy.complex128`
        """
        if dtype is None:
            dtype = np.complex128

        if not np.allclose(k, 0.):
            if np.dtype(dtype).kind != 'c':
                raise ValueError(
                    "Hamiltonian setup at k different from Gamma requires a complex matrix"
                )

        exp = np.exp
        dot = np.dot

        k = np.asarray(k, np.float64)
        k.shape = (-1, )

        # Get the overlap matrix
        Sf = self.tocsr(self.S_idx)

        no = self.no * 2
        s = (no, no)
        S = csr_matrix(s, dtype=dtype)

        # Get back dimensionality of the intrinsic orbitals
        no = self.no

        # Get the reciprocal lattice vectors dotted with k
        kr = dot(self.rcell, k)
        for si in range(self.sc.n_s):
            isc = self.sc_off[si, :]
            phase = exp(-1j * dot(kr, dot(self.cell, isc)))
            # Setup the overlap for this k-point
            sf = Sf[:, si * no:(si + 1) * no]
            for i, j, s in ispmatrixd(sf):
                S[i * 2, j * 2] += s
                S[1 + i * 2, 1 + j * 2] += s

        del Sf

        return S
Example #5
0
    def write_hamiltonian(self, ham, hermitian=True, **kwargs):
        """ Writes the Hamiltonian model to the file

        Writes a Hamiltonian model to the intrinsic Hamiltonian file format.
        The file can be constructed by the implict force of Hermiticity,
        or without.

        Utilizing the Hermiticity we reduce the file-size by approximately
        50%.

        Parameters
        ----------
        ham : `Hamiltonian` model
        hermitian : boolean=True
            whether the stored data is halved using the Hermitian property

        """
        ham.finalize()

        # We use the upper-triangular form of the Hamiltonian
        # and the overlap matrix for hermitian problems

        geom = ham.geometry

        # First write the geometry
        self.write_geometry(geom, **kwargs)

        # We default to the advanced layuot if we have more than one
        # orbital on any one atom
        advanced = kwargs.get(
            'advanced',
            np.any(np.array([a.no for a in geom.atom.atom], np.int32) > 1))

        fmt = kwargs.get('fmt', 'g')
        if advanced:
            fmt1_str = ' {{0:d}}[{{1:d}}] {{2:d}}[{{3:d}}] {{4:{0}}}\n'.format(
                fmt)
            fmt2_str = ' {{0:d}}[{{1:d}}] {{2:d}}[{{3:d}}] {{4:{0}}} {{5:{0}}}\n'.format(
                fmt)
        else:
            fmt1_str = ' {{0:d}} {{1:d}} {{2:{0}}}\n'.format(fmt)
            fmt2_str = ' {{0:d}} {{1:d}} {{2:{0}}} {{3:{0}}}\n'.format(fmt)

        # We currently force the model to be finalized
        # before we can write it
        # This should be easily circumvented
        H = ham.tocsr(0)
        if not ham.orthogonal:
            S = ham.tocsr(ham.S_idx)

        # If the model is Hermitian we can
        # do with writing out half the entries
        if hermitian:
            herm_acc = kwargs.get('herm_acc', 1e-6)
            # We check whether it is Hermitian (not S)
            for i, isc in enumerate(geom.sc.sc_off):
                oi = i * geom.no
                oj = geom.sc_index(-isc) * geom.no
                # get the difference between the ^\dagger elements
                diff = H[:, oi:oi + geom.no] - \
                    H[:, oj:oj + geom.no].transpose()
                diff.eliminate_zeros()
                if np.any(np.abs(diff.data) > herm_acc):
                    amax = np.amax(np.abs(diff.data))
                    warn(
                        SileWarning(
                            'The model could not be asserted to be Hermitian '
                            'within the accuracy required ({0}).'.format(
                                amax)))
                    hermitian = False
                del diff

        if hermitian:
            # Remove all double stuff
            for i, isc in enumerate(geom.sc.sc_off):
                if np.any(isc < 0):
                    # We have ^\dagger element, remove it
                    o = i * geom.no
                    # Ensure that we remove all nullified quantities
                    # (setting elements to zero will add them internally
                    #  :(, hence this actually constructs the full matrix
                    # Therefore we do it on a row basis, to limit memory
                    # requirements
                    for j in range(geom.no):
                        H[j, o:o + geom.no] = 0.
                        H.eliminate_zeros()
                        if not ham.orthogonal:
                            S[j, o:o + geom.no] = 0.
                            S.eliminate_zeros()
            o = geom.sc_index(np.zeros([3], np.int32))
            # Get upper-triangular matrix of the unit-cell H and S
            ut = triu(H[:, o:o + geom.no], k=0).tocsr()
            for j in range(geom.no):
                H[j, o:o + geom.no] = 0.
                H[j, o:o + geom.no] = ut[j, :]
                H.eliminate_zeros()
            if not ham.orthogonal:
                ut = triu(S[:, o:o + geom.no], k=0).tocsr()
                for j in range(geom.no):
                    S[j, o:o + geom.no] = 0.
                    S[j, o:o + geom.no] = ut[j, :]
                    S.eliminate_zeros()

                # Ensure that S and H have the same sparsity pattern
                for jo, io in ispmatrix(S):
                    H[jo, io] = H[jo, io]

            del ut

        # Start writing of the model
        # We loop on all super-cells
        for i, isc in enumerate(geom.sc.sc_off):
            # Check that we have any contributions in this
            # sub-section
            Hsub = H[:, i * geom.no:(i + 1) * geom.no]
            if not ham.orthogonal:
                Ssub = S[:, i * geom.no:(i + 1) * geom.no]
            if Hsub.getnnz() == 0:
                continue
            # We have a contribution, write out the information
            self._write('\nbegin matrix {0:d} {1:d} {2:d}\n'.format(*isc))
            if advanced:
                for jo, io, h in ispmatrixd(Hsub):
                    o = np.array([jo, io], np.int32)
                    a = geom.o2a(o)
                    o = o - geom.a2o(a)
                    if not ham.orthogonal:
                        s = Ssub[jo, io]
                    elif jo == io:
                        s = 1.
                    else:
                        s = 0.
                    if s == 0.:
                        self._write(fmt1_str.format(a[0], o[0], a[1], o[1], h))
                    else:
                        self._write(
                            fmt2_str.format(a[0], o[0], a[1], o[1], h, s))
            else:
                for jo, io, h in ispmatrixd(Hsub):
                    if not ham.orthogonal:
                        s = Ssub[jo, io]
                    elif jo == io:
                        s = 1.
                    else:
                        s = 0.
                    if s == 0.:
                        self._write(fmt1_str.format(jo, io, h))
                    else:
                        self._write(fmt2_str.format(jo, io, h, s))
            self._write('end matrix {0:d} {1:d} {2:d}\n'.format(*isc))