From 6dbd9d682cad56c060c70e127a894f7c46d06606 Mon Sep 17 00:00:00 2001 From: Nick Papior Date: Tue, 26 Nov 2024 13:44:29 +0100 Subject: [PATCH] completely removed dtype in Spin class It does not belong there, and should have been removed ages ago. Signed-off-by: Nick Papior --- CHANGELOG.md | 1 + src/sisl/io/siesta/binaries.py | 4 +- src/sisl/io/siesta/siesta_nc.py | 18 +-- src/sisl/io/tbtrans/delta.py | 6 +- src/sisl/physics/densitymatrix.py | 6 +- src/sisl/physics/sparse.py | 70 +++++------ src/sisl/physics/spin.py | 114 +++++++----------- src/sisl/physics/tests/test_physics_sparse.py | 29 ++--- src/sisl/physics/tests/test_spin.py | 93 +------------- src/sisl/viz/data/pdos.py | 4 +- 10 files changed, 118 insertions(+), 227 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 788bbd81b8..868d719b58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ we hit release version 1.0.0. This yield significant perf. improvements for DFT sparse matrices with *many* edges in the sparse matrix, but a perf. hit for very small TB matrices. +- dtype removed from `Spin` class ## [0.15.2] - 2024-11-06 diff --git a/src/sisl/io/siesta/binaries.py b/src/sisl/io/siesta/binaries.py index fd7224b038..a1c803309b 100644 --- a/src/sisl/io/siesta/binaries.py +++ b/src/sisl/io/siesta/binaries.py @@ -610,7 +610,7 @@ def write_density_matrix(self, DM, **kwargs): dm = csr._D[:, : DM.S_idx] # Ensure shapes (say if only 1 spin) - dm.shape = (-1, len(DM.spin)) + dm.shape = (-1, DM.spin.size(DM.dtype)) nsc = DM.geometry.lattice.nsc.astype(np.int32) @@ -2583,7 +2583,7 @@ def write_header(self, bz, E, mu: float = 0.0, obj=None): """ if obj is None: obj = bz.parent - nspin = len(obj.spin) + nspin = obj.spin.size(obj.dtype) cell = obj.geometry.lattice.cell na_u = obj.geometry.na no_u = obj.geometry.no diff --git a/src/sisl/io/siesta/siesta_nc.py b/src/sisl/io/siesta/siesta_nc.py index 16d795e53d..8c2ef8850e 100644 --- a/src/sisl/io/siesta/siesta_nc.py +++ b/src/sisl/io/siesta/siesta_nc.py @@ -246,7 +246,7 @@ def read_hamiltonian(self, **kwargs) -> Hamiltonian: f"{self}.read_hamiltonian requires the stored matrix to be in Ry!" ) - for i in range(len(H.spin)): + for i in range(H.spin.size(H.dtype)): H._csr._D[:, i] = sp.variables["H"][i, :] * Ry2eV # fix siesta specific notation @@ -281,7 +281,7 @@ def read_density_matrix(self, **kwargs) -> DensityMatrix: DM = self._r_class_spin(DensityMatrix, **kwargs) sp = self.groups["SPARSE"] - for i in range(len(DM.spin)): + for i in range(DM.spin.size(DM.dtype)): DM._csr._D[:, i] = sp.variables["DM"][i, :] # fix siesta specific notation @@ -299,7 +299,7 @@ def read_energy_density_matrix(self, **kwargs) -> EnergyDensityMatrix: Ef = np.tile(Ef, 2) sp = self.groups["SPARSE"] - for i in range(len(EDM.spin)): + for i in range(EDM.spin.size(EDM.dtype)): EDM._csr._D[:, i] = sp.variables["EDM"][i, :] * Ry2eV if i < 2 and "DM" in sp.variables: EDM._csr._D[:, i] -= sp.variables["DM"][i, :] * Ef[i] @@ -629,7 +629,7 @@ def write_hamiltonian(self, H, **kwargs): # Ensure that the geometry is written self.write_geometry(H.geometry) - self._crt_dim(self, "spin", len(H.spin)) + self._crt_dim(self, "spin", H.spin.size(H.dtype)) if H.dkind != "f": raise NotImplementedError( @@ -660,7 +660,7 @@ def write_hamiltonian(self, H, **kwargs): ) v.info = "Hamiltonian" v.unit = "Ry" - for i in range(len(H.spin)): + for i in range(H.spin.size(H.dtype)): v[i, :] = csr._D[:, i] / Ry2eV self._write_settings() @@ -688,7 +688,7 @@ def write_density_matrix(self, DM, **kwargs): # Ensure that the geometry is written self.write_geometry(DM.geometry) - self._crt_dim(self, "spin", len(DM.spin)) + self._crt_dim(self, "spin", DM.spin.size(DM.dtype)) if DM.dkind != "f": raise NotImplementedError( @@ -718,7 +718,7 @@ def write_density_matrix(self, DM, **kwargs): **self._cmp_args, ) v.info = "Density matrix" - for i in range(len(DM.spin)): + for i in range(DM.spin.size(DM.dtype)): v[i, :] = csr._D[:, i] self._write_settings() @@ -746,7 +746,7 @@ def write_energy_density_matrix(self, EDM, **kwargs): # Ensure that the geometry is written self.write_geometry(EDM.geometry) - self._crt_dim(self, "spin", len(EDM.spin)) + self._crt_dim(self, "spin", EDM.spin.size(EDM.dtype)) if EDM.dkind != "f": raise NotImplementedError( @@ -781,7 +781,7 @@ def write_energy_density_matrix(self, EDM, **kwargs): ) v.info = "Energy density matrix" v.unit = "Ry" - for i in range(len(EDM.spin)): + for i in range(EDM.spin.size(EDM.dtype)): v[i, :] = csr._D[:, i] / Ry2eV self._write_settings() diff --git a/src/sisl/io/tbtrans/delta.py b/src/sisl/io/tbtrans/delta.py index 8a0955dc28..23cd0a31b6 100644 --- a/src/sisl/io/tbtrans/delta.py +++ b/src/sisl/io/tbtrans/delta.py @@ -453,7 +453,7 @@ def write_delta(self, delta, **kwargs): # Ensure that the geometry is written self.write_geometry(delta.geometry) - self._crt_dim(self, "spin", len(delta.spin)) + self._crt_dim(self, "spin", delta.spin.size(delta.dtype)) # Determine the type of delta we are storing... k = kwargs.get("k", None) @@ -583,7 +583,7 @@ def write_delta(self, delta, **kwargs): attrs={"info": "Imaginary part of delta", "unit": "Ry"}, **self._cmp_args, ) - for i in range(len(delta.spin)): + for i in range(delta.spin.size(delta.dtype)): sl[-2] = i v1[sl] = csr._D[:, i].real * eV2Ry v2[sl] = csr._D[:, i].imag * eV2Ry @@ -598,7 +598,7 @@ def write_delta(self, delta, **kwargs): attrs={"info": "delta", "unit": "Ry"}, **self._cmp_args, ) - for i in range(len(delta.spin)): + for i in range(delta.spin.size(delta.dtype)): sl[-2] = i v[sl] = csr._D[:, i] * eV2Ry diff --git a/src/sisl/physics/densitymatrix.py b/src/sisl/physics/densitymatrix.py index 4de3f7a7cb..892c036576 100644 --- a/src/sisl/physics/densitymatrix.py +++ b/src/sisl/physics/densitymatrix.py @@ -169,7 +169,7 @@ def close(a, v): out._csr._D[:, [0, 1]] = out._csr._D[:, [1, 0]] else: - spin = Spin("nc", dtype=self.dtype) + spin = Spin("nc") out = self.__class__( self.geometry, dtype=self.dtype, @@ -305,7 +305,7 @@ def spin_align(self, vec: SeqFloat, atoms: AtomsIndex = None): elif self.spin.is_polarized: if vec[:2] @ vec[:2] > 1e-6: - spin = Spin("nc", dtype=self.dtype) + spin = Spin("nc") out = self.__class__( self.geometry, dtype=self.dtype, @@ -768,7 +768,7 @@ def density( DM = _a.emptyz([self.nnz, 2, 2]) idx = _a.array_arange(csr.ptr[:-1], n=csr.ncol) - if self.spin.kind == Spin.NONCOLINEAR: + if self.spin.is_noncolinear: # non-collinear DM[:, 0, 0] = csr._D[idx, 0] DM[:, 0, 1] = csr._D[idx, 2] + 1j * csr._D[idx, 3] diff --git a/src/sisl/physics/sparse.py b/src/sisl/physics/sparse.py index c5e8a79fd4..95b3a59862 100644 --- a/src/sisl/physics/sparse.py +++ b/src/sisl/physics/sparse.py @@ -53,21 +53,21 @@ def _get_spin(M, spin, what: Literal["trace", "box", "vector"] = "box"): if spin.is_polarized: m[..., :2] = 0.0 elif spin.is_noncolinear: - if spin.dkind in ("f", "i"): - m[..., 0] = 2 * M[..., 2] - m[..., 1] = -2 * M[..., 3] - else: + if np.iscomplexobj(M): m[..., 0] = 2 * M[..., 2].real m[..., 1] = -2 * M[..., 2].imag + else: + m[..., 0] = 2 * M[..., 2] + m[..., 1] = -2 * M[..., 3] else: # spin-orbit - if spin.dkind in ("f", "i"): - m[..., 0] = M[..., 2] + M[..., 6] - m[..., 1] = -M[..., 3] + M[..., 7] - else: + if np.iscomplexobj(M): tmp = M[..., 2].conj() + M[..., 3] m[..., 0] = tmp.real m[..., 1] = tmp.imag + else: + m[..., 0] = M[..., 2] + M[..., 6] + m[..., 1] = -M[..., 3] + M[..., 7] return m if what == "box": @@ -82,27 +82,27 @@ def _get_spin(M, spin, what: Literal["trace", "box", "vector"] = "box"): m[..., 0, 0] = M[..., 0] m[..., 1, 1] = M[..., 1] elif spin.is_noncolinear: - if spin.dkind in ("f", "i"): + if np.iscomplexobj(M): + m[..., 0, 0] = M[..., 0] + m[..., 1, 1] = M[..., 1] + m[..., 0, 1] = M[..., 2] + m[..., 1, 0] = M[..., 2].conj() + else: m[..., 0, 0] = M[..., 0] m[..., 1, 1] = M[..., 1] m[..., 0, 1] = M[..., 2] + 1j * M[..., 3] m[..., 1, 0] = m[..., 0, 1].conj() - else: + else: + if np.iscomplexobj(M): m[..., 0, 0] = M[..., 0] m[..., 1, 1] = M[..., 1] m[..., 0, 1] = M[..., 2] - m[..., 1, 0] = M[..., 2].conj() - else: - if spin.dkind in ("f", "i"): + m[..., 1, 0] = M[..., 3] + else: m[..., 0, 0] = M[..., 0] + 1j * M[..., 4] m[..., 1, 1] = M[..., 1] + 1j * M[..., 5] m[..., 0, 1] = M[..., 2] + 1j * M[..., 3] m[..., 1, 0] = M[..., 6] + 1j * M[..., 7] - else: - m[..., 0, 0] = M[..., 0] - m[..., 1, 1] = M[..., 1] - m[..., 0, 1] = M[..., 2] - m[..., 1, 0] = M[..., 3] return m @@ -828,6 +828,8 @@ def __init__( if isinstance(dim, Spin): spin = dim else: + # Back conversion, actually this should depend + # on dtype spin = { 1: Spin.UNPOLARIZED, 2: Spin.POLARIZED, @@ -836,9 +838,9 @@ def __init__( }.get(dim) else: spin = kwargs.pop("spin") - self._spin = Spin(spin, dtype) + self._spin = Spin(spin) - super().__init__(geometry, len(self.spin), self.spin.dtype, nnzpr, **kwargs) + super().__init__(geometry, self.spin.size(dtype), dtype, nnzpr, **kwargs) self._reset() def _reset(self): @@ -846,7 +848,7 @@ def _reset(self): super()._reset() # Update the dtype of the spin - self._spin = Spin(self.spin, dtype=self.dtype) + self._spin = Spin(self.spin) if self.spin.is_unpolarized: self.UP = 0 @@ -865,7 +867,7 @@ def _reset(self): self.dSk = self._dSk elif self.spin.is_noncolinear: - if self.spin.dkind in ("f", "i"): + if self.dkind in ("f", "i"): self.M11 = 0 self.M22 = 1 self.M12r = 2 @@ -882,7 +884,7 @@ def _reset(self): self.ddSk = self._ddSk_non_colinear elif self.spin.is_spinorbit: - if self.spin.dkind in ("f", "i"): + if self.dkind in ("f", "i"): self.SX = np.array([0, 0, 1, 0, 0, 0, 1, 0], self.dtype) self.SY = np.array([0, 0, 0, -1, 0, 0, 0, 1], self.dtype) self.SZ = np.array([1, -1, 0, 0, 0, 0, 0, 0], self.dtype) @@ -1509,7 +1511,7 @@ def transpose(self, hermitian: bool = False, spin: bool = True, sort: bool = Tru if sp.is_spinorbit: if hermitian and spin: # conjugate the imaginary value and transpose spin-box - if sp.dkind in ("f", "i"): + if self.dkind in ("f", "i"): # imaginary components (including transposing) # 12,11,22,21 D[:, [3, 4, 5, 7]] = -D[:, [7, 4, 5, 3]] @@ -1519,7 +1521,7 @@ def transpose(self, hermitian: bool = False, spin: bool = True, sort: bool = Tru D[:, [0, 1, 2, 3]] = np.conj(D[:, [0, 1, 3, 2]]) elif hermitian: # conjugate the imaginary value - if sp.dkind in ("f", "i"): + if self.dkind in ("f", "i"): # imaginary components # 12,11,22,21 D[:, [3, 4, 5, 7]] *= -1.0 @@ -1527,7 +1529,7 @@ def transpose(self, hermitian: bool = False, spin: bool = True, sort: bool = Tru D[:, :] = np.conj(D[:, :]) elif spin: # transpose spin-box, 12 <-> 21 - if sp.dkind in ("f", "i"): + if self.dkind in ("f", "i"): D[:, [2, 3, 6, 7]] = D[:, [6, 7, 2, 3]] else: D[:, [2, 3]] = D[:, [3, 2]] @@ -1544,7 +1546,7 @@ def transpose(self, hermitian: bool = False, spin: bool = True, sort: bool = Tru # So for transposing we should negate the sign # to ensure we put the opposite value in the # correct place. - if sp.dkind in ("f", "i"): + if self.dkind in ("f", "i"): D[:, 3] = -D[:, 3] else: D[:, 2] = np.conj(D[:, 2]) @@ -1568,7 +1570,7 @@ def trs(self): # Apply Pauli-Y on the left and right of each spin-box if sp.is_spinorbit: - if sp.dkind in ("f", "i"): + if self.dkind in ("f", "i"): # [R11, R22, R12, I12, I11, I22, R21, I21] # [R11, R22] = [R22, R11] # [I12, I21] = [I21, I12] (conj + Y @ Y[sign-changes conj]) @@ -1579,7 +1581,7 @@ def trs(self): else: raise NotImplementedError elif sp.is_noncolinear: - if sp.dkind in ("f", "i"): + if self.dkind in ("f", "i"): # [R11, R22, R12, I12] D[:, 2] = -D[:, 2] else: @@ -1648,16 +1650,16 @@ def transform(self, matrix=None, dtype=None, spin=None, orthogonal=None): if spin is None: spin = self.spin else: - spin = Spin(spin, dtype) + spin = Spin(spin) if orthogonal is None: orthogonal = self.orthogonal # get dimensions to check - N = n = self.spin.size + N = n = self.spin.size(self.dtype) if not self.orthogonal: N += 1 - M = m = spin.size + M = m = spin.size(dtype) if not orthogonal: M += 1 @@ -1673,10 +1675,10 @@ def transform(self, matrix=None, dtype=None, spin=None, orthogonal=None): # ensure the overlap matrix is carried over matrix[-1, -1] = 1.0 - if spin.is_unpolarized and self.spin.size > 1: + if spin.is_unpolarized and self.spin.size(self.dtype) > 1: # average up and down components matrix[0, [0, 1]] = 0.5 - elif spin.size > 1 and self.spin.is_unpolarized: + elif spin.size(dtype) > 1 and self.spin.is_unpolarized: # set up and down components to unpolarized value matrix[[0, 1], 0] = 1.0 diff --git a/src/sisl/physics/spin.py b/src/sisl/physics/spin.py index 191c510916..133b6d7983 100644 --- a/src/sisl/physics/spin.py +++ b/src/sisl/physics/spin.py @@ -3,6 +3,8 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. from __future__ import annotations +from typing import Union + import numpy as np from sisl._internal import set_module @@ -36,8 +38,6 @@ class Spin: ---------- kind : str or int, Spin, optional specify the spin kind - dtype : numpy.dtype, optional - the data-type used for the spin-component. Default is ``np.float64`` """ #: Constant for an un-polarized spin configuration @@ -56,22 +56,13 @@ class Spin: #: The :math:`\boldsymbol\sigma_z` Pauli matrix Z = np.array([[1, 0], [0, -1]], np.complex128) - __slots__ = ("_kind", "_dtype") + __slots__ = ("_kind",) - def __init__(self, kind="", dtype=None): + def __init__(self, kind: Union[str, int] = "unpolarized"): if isinstance(kind, Spin): - if dtype is None: - dtype = kind._dtype self._kind = kind._kind - self._dtype = dtype return - if dtype is None: - dtype = np.float64 - - # Copy data-type - self._dtype = dtype - if isinstance(kind, str): kind = kind.lower() @@ -106,84 +97,75 @@ def __init__(self, kind="", dtype=None): # Now assert the checks self._kind = kind - def __str__(self): + def __str__(self) -> str: if self.is_unpolarized: - return f"{self.__class__.__name__}{{unpolarized, kind={self.dkind}}}" + return f"{self.__class__.__name__}{{unpolarized}}" if self.is_polarized: - return f"{self.__class__.__name__}{{polarized, kind={self.dkind}}}" + return f"{self.__class__.__name__}{{polarized}}" if self.is_noncolinear: - return f"{self.__class__.__name__}{{non-colinear, kind={self.dkind}}}" - return f"{self.__class__.__name__}{{spin-orbit, kind={self.dkind}}}" + return f"{self.__class__.__name__}{{non-colinear}}" + return f"{self.__class__.__name__}{{spin-orbit}}" def copy(self): """Create a copy of the spin-object""" - return Spin(self.kind, self.dtype) - - @property - def dtype(self): - """Data-type of the spin configuration""" - return self._dtype + return Spin(self.kind) - @property - def dkind(self): - """Data-type kind""" - return np.dtype(self._dtype).kind + def size(self, dtype: np.dtype) -> int: + """Number of elements to describe the spin-components - @property - def size(self): - """Number of elements to describe the spin-components""" - size = { - "c": { + Parameters + ---------- + dtype: + data-type used to represent the spin-configuration + """ + dkind = np.dtype(dtype).kind + if dkind == "c": + return { self.UNPOLARIZED: 1, self.POLARIZED: 2, self.NONCOLINEAR: 3, self.SPINORBIT: 4, - }, - "i": { - self.UNPOLARIZED: 1, - self.POLARIZED: 2, - self.NONCOLINEAR: 4, - self.SPINORBIT: 8, - }, - "f": { - self.UNPOLARIZED: 1, - self.POLARIZED: 2, - self.NONCOLINEAR: 4, - self.SPINORBIT: 8, - }, - }[self.dkind][self.kind] - return size + }[self.kind] + + return { + self.UNPOLARIZED: 1, + self.POLARIZED: 2, + self.NONCOLINEAR: 4, + self.SPINORBIT: 8, + }[self.kind] @property - def spinor(self): + def spinor(self) -> int: """Number of spinor components (1 or 2)""" - return min(2, self.size) + if self.is_unpolarized: + return 1 + return 2 @property - def kind(self): + def kind(self) -> int: """A unique ID for the kind of spin configuration""" return self._kind @property - def is_unpolarized(self): + def is_unpolarized(self) -> bool: """True if the configuration is not polarized""" # Regardless of data-type return self.kind == Spin.UNPOLARIZED @property - def is_polarized(self): + def is_polarized(self) -> bool: """True if the configuration is polarized""" return self.kind == Spin.POLARIZED is_colinear = is_polarized @property - def is_noncolinear(self): + def is_noncolinear(self) -> bool: """True if the configuration non-collinear""" return self.kind == Spin.NONCOLINEAR @property - def is_diagonal(self): + def is_diagonal(self) -> bool: """Whether the spin-box is only using the diagonal components This will return true for non-polarized and polarized spin configurations. @@ -192,35 +174,31 @@ def is_diagonal(self): return self.kind in (Spin.UNPOLARIZED, Spin.POLARIZED) @property - def is_spinorbit(self): + def is_spinorbit(self) -> bool: """True if the configuration is spin-orbit""" return self.kind == Spin.SPINORBIT - def __len__(self): - return self.size - # Comparisons - def __lt__(self, other): + def __lt__(self, other) -> bool: return self.kind < other.kind - def __le__(self, other): + def __le__(self, other) -> bool: return self.kind <= other.kind - def __eq__(self, other): + def __eq__(self, other) -> bool: return self.kind == other.kind - def __ne__(self, other): + def __ne__(self, other) -> bool: return not self == other - def __gt__(self, other): + def __gt__(self, other) -> bool: return self.kind > other.kind - def __ge__(self, other): + def __ge__(self, other) -> bool: return self.kind >= other.kind - def __getstate__(self): - return {"size": self.size, "kind": self.kind, "dtype": self.dtype} + def __getstate__(self) -> dict: + return {"kind": self.kind} def __setstate__(self, state): self._kind = state["kind"] - self._dtype = state["dtype"] diff --git a/src/sisl/physics/tests/test_physics_sparse.py b/src/sisl/physics/tests/test_physics_sparse.py index 6739de9545..d6ace99ac5 100644 --- a/src/sisl/physics/tests/test_physics_sparse.py +++ b/src/sisl/physics/tests/test_physics_sparse.py @@ -272,7 +272,7 @@ def test_sparse_orbital_bz_spin_orbit_hermitian_not(): def test_sparse_orbital_transform_ortho_unpolarized(): M = SparseOrbitalBZSpin(geom.graphene(), spin="unpolarized") - a = np.arange(M.spin.size) + 0.3 + a = np.arange(M.spin.size(M.dtype)) + 0.3 M.construct(([0.1, 1.44], [a, a + 0.1])) M.finalize() Mcsr = [M.tocsr(i) for i in range(M.shape[2])] @@ -299,7 +299,7 @@ def test_sparse_orbital_transform_ortho_unpolarized(): def test_sparse_orbital_transform_nonortho_unpolarized(): M = SparseOrbitalBZSpin(geom.graphene(), spin="unpolarized", orthogonal=False) - a = np.arange(M.spin.size + 1) + 0.3 + a = np.arange(M.spin.size(M.dtype) + 1) + 0.3 M.construct(([0.1, 1.44], [a, a + 0.1])) M.finalize() Mcsr = [M.tocsr(i) for i in range(M.shape[2])] @@ -328,7 +328,7 @@ def test_sparse_orbital_transform_nonortho_unpolarized(): def test_sparse_orbital_transform_ortho_polarized(): M = SparseOrbitalBZSpin(geom.graphene(), spin="polarized") - a = np.arange(M.spin.size) + 0.3 + a = np.arange(M.spin.size(M.dtype)) + 0.3 M.construct(([0.1, 1.44], [a, a + 0.1])) M.finalize() Mcsr = [M.tocsr(i) for i in range(M.shape[2])] @@ -355,7 +355,7 @@ def test_sparse_orbital_transform_ortho_polarized(): def test_sparse_orbital_transform_ortho_nc(): M = SparseOrbitalBZSpin(geom.graphene(), spin="non-colinear") - a = np.arange(M.spin.size) + 0.3 + a = np.arange(M.spin.size(M.dtype)) + 0.3 M.construct(([0.1, 1.44], [a, a + 0.1])) M.finalize() Mcsr = [M.tocsr(i) for i in range(M.shape[2])] @@ -383,7 +383,7 @@ def test_sparse_orbital_transform_ortho_nc(): @pytest.mark.filterwarnings("ignore", message="*is NOT Hermitian for on-site") def test_sparse_orbital_transform_ortho_so(): M = SparseOrbitalBZSpin(geom.graphene(), spin="so") - a = np.arange(M.spin.size) + 0.3 + a = np.arange(M.spin.size(M.dtype)) + 0.3 M.construct(([0.1, 1.44], [a, a + 0.1])) M.finalize() Mcsr = [M.tocsr(i) for i in range(M.shape[2])] @@ -411,7 +411,7 @@ def test_sparse_orbital_transform_ortho_so(): @pytest.mark.filterwarnings("ignore", message="*is NOT Hermitian for on-site") def test_sparse_orbital_transform_nonortho_so(): M = SparseOrbitalBZSpin(geom.graphene(), spin="so", orthogonal=False) - a = np.arange(M.spin.size + 1) + 0.3 + a = np.arange(M.spin.size(M.dtype) + 1) + 0.3 M.construct(([0.1, 1.44], [a, a + 0.1])) M.finalize() Mcsr = [M.tocsr(i) for i in range(M.shape[2])] @@ -548,18 +548,11 @@ def test_sparse_orbital_transform_fail(): @pytest.mark.parametrize("dtype", [np.float32, np.float64, np.complex64, np.complex128]) -def test_sparseorbital_spin_dtypes(dtype): +@pytest.mark.parametrize( + "spin", ["unpolarized", "polarized", "non-colinear", "spin-orbit"] +) +def test_sparseorbital_spin_dtypes(dtype, spin): gr = geom.graphene() - M = SparseOrbitalBZSpin(gr, spin=Spin("unpolarized", dtype)) - assert M.dtype == dtype - - M = SparseOrbitalBZSpin(gr, spin=Spin("polarized", dtype)) + M = SparseOrbitalBZSpin(gr, spin=Spin(spin), dtype=dtype) assert M.dtype == dtype - - if dtype not in (np.complex64, np.complex128): - M = SparseOrbitalBZSpin(gr, spin=Spin("non-colinear", dtype)) - assert M.dtype == dtype - - M = SparseOrbitalBZSpin(gr, spin=Spin("spin-orbit", dtype)) - assert M.dtype == dtype diff --git a/src/sisl/physics/tests/test_spin.py b/src/sisl/physics/tests/test_spin.py index 25d5983e55..f05bb45ad5 100644 --- a/src/sisl/physics/tests/test_spin.py +++ b/src/sisl/physics/tests/test_spin.py @@ -11,7 +11,7 @@ pytestmark = [pytest.mark.physics, pytest.mark.spin] -def test_spin1(): +def test_spin_init(): for val in [ "unpolarized", "", @@ -32,7 +32,7 @@ def test_spin1(): assert s == s1 -def test_spin2(): +def test_spin_comparisons(): s1 = Spin() s2 = Spin("p") s3 = Spin("nc") @@ -85,95 +85,12 @@ def test_spin2(): assert s4.is_spinorbit -def test_spin3(): +def test_spin_unaccepted_arg(): with pytest.raises(ValueError): s = Spin("satoehus") -def test_spin4(): - s1 = Spin(Spin.UNPOLARIZED) - S1 = Spin(Spin.UNPOLARIZED, np.complex64) - s2 = Spin(Spin.POLARIZED) - S2 = Spin(Spin.POLARIZED, np.complex64) - s3 = Spin(Spin.NONCOLINEAR) - S3 = Spin(Spin.NONCOLINEAR, np.complex64) - s4 = Spin(Spin.SPINORBIT) - S4 = Spin(Spin.SPINORBIT, np.complex64) - assert s1 == S1 - assert s2 == S2 - assert s3 == S3 - assert s4 == S4 - - # real comparison - assert s1 < S2 - assert s1 < S3 - assert s1 < S4 - - assert s2 > S1 - assert s2 < S3 - assert s2 < S4 - - assert s3 > S1 - assert s3 > S2 - assert s3 < S4 - - assert s4 > S1 - assert s4 > S2 - assert s4 > S3 - - # complex complex - assert S1 < S2 - assert S1 < S3 - assert S1 < S4 - - assert S2 > S1 - assert S2 < S3 - assert S2 < S4 - - assert S3 > S1 - assert S3 > S2 - assert S3 < S4 - - assert S4 > S1 - assert S4 > S2 - assert S4 > S3 - - # real comparison - assert S1 < s2 - assert S1 < s3 - assert S1 < s4 - - assert S2 > s1 - assert S2 < s3 - assert S2 < s4 - - assert S3 > s1 - assert S3 > s2 - assert S3 < s4 - - assert S4 > s1 - assert S4 > s2 - assert S4 > s3 - - # complex complex - assert S1 < s2 - assert S1 < s3 - assert S1 < s4 - - assert S2 > s1 - assert S2 < s3 - assert S2 < s4 - - assert S3 > s1 - assert S3 > s2 - assert S3 < s4 - - assert S4 > s1 - assert S4 > s2 - assert S4 > s3 - - -def test_pauli(): +def test_spin_pauli(): # just grab the default spin S = Spin() @@ -201,7 +118,7 @@ def test_pauli(): assert np.allclose(z, (np.conj(W) * np.dot(S.Z, W.T).T).sum(1).real) -def test_pickle(): +def test_spin_pickle(): import pickle as p S = Spin("nc") diff --git a/src/sisl/viz/data/pdos.py b/src/sisl/viz/data/pdos.py index c45bc7b9d4..dc65ceb280 100644 --- a/src/sisl/viz/data/pdos.py +++ b/src/sisl/viz/data/pdos.py @@ -462,8 +462,8 @@ def from_wfsx( sizes = wfsx_sile.read_sizes() # Check that spin sizes of hamiltonian and wfsx file match assert ( - H.spin.size == sizes.nspin - ), f"Hamiltonian has spin size {H.spin.size} while file has spin size {sizes.nspin}" + H.spin.size(H.dtype) == sizes.nspin + ), f"Hamiltonian has spin size {H.spin.size(H.dtype)} while file has spin size {sizes.nspin}" # Get the size of the spin channel. The size returned might be 8 if it is a spin-orbit # calculation, but we need only 4 spin channels (total, x, y and z), same as with non-colinear nspin = min(4, sizes.nspin)