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
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
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
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
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))