diff --git a/examples/cpr_sns.py b/examples/cpr_sns.py
index f985c55d449e477becca44771b20096cc46277ad..0479b7c819c0f7ad42984fe0031af1c28b3530cb 100644
--- a/examples/cpr_sns.py
+++ b/examples/cpr_sns.py
@@ -66,7 +66,7 @@ def j(T, h, phi, n=15, eta=0.1, alpha_soc=0.1, L=10, W=2):
     res = sol.solve_many(omega=w)
 
     t3 = np.diag([1, 1, -1, -1])
-    J = tr(res.J @ t3)
+    J = tr(res.J.A @ t3)
     J = -1j * pi * (J * a[:, None, None, None]).sum(axis=0)
 
     Jx = (J[:, :, 0] + J[:, :, 2]).real / 2
diff --git a/usadelndsoc/solver.py b/usadelndsoc/solver.py
index a667c3de0ca8c6d50829e473f154ef2fd25c549a..3bafa3a311f96ef84dc062c619df7a69b7d6aa70 100644
--- a/usadelndsoc/solver.py
+++ b/usadelndsoc/solver.py
@@ -119,20 +119,11 @@ def _core_property(name, doc=None):
     return property(fget, fset, doc=doc)
 
 
-def _array_property(name, doc=None, cls=None):
+def _array_property(name, doc=None):
     def fget(self):
-        v = getattr(self, name)
-        if cls is not None:
-            return cls(self, v)
-        else:
-            return v
+        return getattr(self, name)
 
     fget.__name__ = name
-    if doc is None:
-        doc = ""
-    doc = textwrap.dedent(doc)
-    if cls is not None:
-        doc += f"\n\n:Type: :any:`{cls.__name__}`"
     return property(fget, doc=doc)
 
 
@@ -234,7 +225,7 @@ class Current:
         --------
         interpolate_J
         """
-        return interpolate_S(
+        return interpolate_J(
             self.__parent.x, self.__parent.y, self.__data, x, y, method
         )
 
@@ -279,7 +270,6 @@ class Solver:
     Phi = _array_property(
         "_Phi",
         doc=r"""Current saddle-point solution in Riccati parametrization. Array of complex, shape (nx, ny, 2, 2, 2).""",
-        cls=Density,
     )
     J_tot = _array_property(
         "_J_tot",
@@ -287,14 +277,12 @@ class Solver:
         Matsubara-summed matrix current between cells, :math:`J = -i \pi T\sum_{\omega_n} \mathcal{J}(\omega_n)`.
         Array of complex, shape (nx, ny, 4(direction), 4, 4).
         """,
-        cls=Current,
     )
     S_tot = _array_property(
         "_S_tot",
         doc=r"""Matsubara-summed density, :math:`S = \pi T \sum_{\omega_n} [Q(\omega_n) - \tau_3]`.
         Array of complex, shape (nx, ny, 4, 4).
         """,
-        cls=Density,
     )
 
     def reset(self):
@@ -694,7 +682,9 @@ class Solver:
             Phi00,
             **kw,
         )
-        return Result(self, omega=omega, Phi=self._Phi)
+        return Result(
+            self, omega=omega, Phi=self._Phi, J_tot=self._J_tot, S_tot=self._S_tot
+        )
 
     def _get_preconditioner(self, J, preconditioner=None):
         if preconditioner is None:
@@ -754,7 +744,7 @@ class Solver:
         omega0 = np.asarray(omega)
         omega = omega0.ravel()
 
-        Phi = np.zeros(omega.shape + self.Phi.A.shape, dtype=self.Phi.A.dtype)
+        Phi = np.zeros(omega.shape + self._Phi.shape, dtype=self._Phi.dtype)
 
         js = np.lexsort((-abs(omega), np.sign(omega.real)))
 
@@ -778,7 +768,15 @@ class Solver:
 
         Phi = Phi.reshape(omega0.shape + Phi.shape[1:])
 
-        return Result(self, omega=omega0, Phi=Phi)
+        return Result(self, omega=omega0, Phi=Phi, J_tot=self._J_tot, S_tot=self._S_tot)
+
+    def result(self):
+        """
+        Return current values as :any:`Result`.
+        """
+        return Result(
+            self, omega=self.omega, Phi=self._Phi, J_tot=self._J_tot, S_tot=self._S_tot
+        )
 
     def self_consistency(self, T, T_c0, continuation=False, **kw):
         r"""
@@ -848,15 +846,15 @@ class Result:
 
     """
 
-    def __init__(self, parent, omega, Phi, core=None):
+    def __init__(self, parent, omega, Phi, J_tot=None, S_tot=None, core=None):
         self._core = core if core is not None else parent._core
         self.shape = parent.shape
-        self.Lx = parent.Lx
-        self.Ly = parent.Ly
         self.omega = omega
-        self.Omega = parent.Omega
-        self.Phi = Phi
+        self._Omega = parent.Omega
+        self._Phi = Phi
         self.mask = parent.mask
+        self._J_tot = J_tot
+        self._S_tot = S_tot
 
     def _density(self, v):
         if np.asarray(self.omega).ndim == 0:
@@ -872,6 +870,21 @@ class Result:
             r = np.moveaxis(v, 0, 3)
         return Current(self, r)
 
+    Lx = _core_property("Lx")
+    Ly = _core_property("Ly")
+    x = _core_property("x")
+    y = _core_property("y")
+
+    @property
+    def Phi(self):
+        """
+        Solution Riccati parameters.
+
+        :Type: :any:`Density`
+        :Shape: (nx, ny, 2, 2, 2)
+        """
+        return Density(self, self._Phi)
+
     @property
     def Delta(self):
         """
@@ -880,7 +893,17 @@ class Result:
         :Type: :any:`Density`
         :Shape: (nx, ny, 2, 2)
         """
-        return Density(self, _DeltaArray(self.Omega).A)
+        return Density(self, _DeltaArray(self._Omega).A)
+
+    @property
+    def Omega(self):
+        """
+        Local energy matrix.
+
+        :Type: :any:`Density`
+        :Shape: (nx, ny, 4, 4)
+        """
+        return Density(self, self._Omega)
 
     @property
     def G(self):
@@ -890,8 +913,8 @@ class Result:
         :Type: :any:`Density`
         :Shape: (nx, ny, [nw,] 2, 2)
         """
-        g = self.Phi[..., 0, :, :]
-        gt = self.Phi[..., 1, :, :]
+        g = self._Phi[..., 0, :, :]
+        gt = self._Phi[..., 1, :, :]
         I = np.eye(2)
         s = _bcast_left(np.sign(self.omega), g.shape)
         with np.errstate(invalid="ignore"):
@@ -906,8 +929,8 @@ class Result:
         :Type: :any:`Density`
         :Shape: (nx, ny, [nw,] 2, 2)
         """
-        g = self.Phi[..., 0, :, :]
-        gt = self.Phi[..., 1, :, :]
+        g = self._Phi[..., 0, :, :]
+        gt = self._Phi[..., 1, :, :]
         I = np.eye(2)
         s = _bcast_left(np.sign(self.omega), g.shape)
         with np.errstate(invalid="ignore"):
@@ -922,8 +945,8 @@ class Result:
         :Type: :any:`Density`
         :Shape: (nx, ny, [nw,] 2, 2)
         """
-        g = self.Phi[..., 0, :, :]
-        gt = self.Phi[..., 1, :, :]
+        g = self._Phi[..., 0, :, :]
+        gt = self._Phi[..., 1, :, :]
         I = np.eye(2)
         s = _bcast_left(np.sign(self.omega), g.shape)
         with np.errstate(invalid="ignore"):
@@ -938,8 +961,8 @@ class Result:
         :Type: :any:`Density`
         :Shape: (nx, ny, [nw,] 2, 2)
         """
-        g = self.Phi[..., 0, :, :]
-        gt = self.Phi[..., 1, :, :]
+        g = self._Phi[..., 0, :, :]
+        gt = self._Phi[..., 1, :, :]
         I = np.eye(2)
         s = _bcast_left(np.sign(self.omega), g.shape)
         with np.errstate(invalid="ignore"):
@@ -991,10 +1014,11 @@ class Result:
         :Shape: (nx, ny, [nw,] 4, 4)
         """
         if np.asarray(self.omega).ndim == 0:
-            r = self._get_S(self.omega, self.Phi)
+            r = self._get_S(self.omega, self._Phi)
         else:
             r = np.array(
-                [self._get_S(w, P) for w, P in zip(self.omega, self.Phi)], dtype=complex
+                [self._get_S(w, P) for w, P in zip(self.omega, self._Phi)],
+                dtype=complex,
             )
         return self._density(r)
 
@@ -1014,13 +1038,52 @@ class Result:
         :Shape: (nx, ny, 4(direction), [nw,] 4, 4)
         """
         if np.asarray(self.omega).ndim == 0:
-            r = self._get_J(self.omega, self.Phi)
+            r = self._get_J(self.omega, self._Phi)
         else:
             r = np.array(
-                [self._get_J(w, P) for w, P in zip(self.omega, self.Phi)], dtype=complex
+                [self._get_J(w, P) for w, P in zip(self.omega, self._Phi)],
+                dtype=complex,
             )
         return self._current(r)
 
+    def _S_s(self, S, msummed=False):
+        Sx = tr(np.kron(S_z, S_x) @ S).imag
+        Sy = tr(np.kron(S_z, S_y) @ S).imag
+        Sz = tr(np.kron(S_z, S_z) @ S).imag
+        ss = np.array([Sx, Sy, Sz])
+        if msummed or np.asarray(self.omega).ndim == 0:
+            r = np.array([jx, jy, jz]).transpose(1, 2, 0)
+        else:
+            r = np.array([jx, jy, jz]).transpose(1, 2, 3, 0)
+        return Density(self, r)
+
+    def _J_c(self, J):
+        t3 = np.diag([1, 1, -1, -1])
+        r = tr(J @ t3).real
+        return Current(self, r)
+
+    def _J_s(self, J, msummed=False):
+        jx = tr(J @ np.kron(S_0, S_x)).real
+        jy = tr(J @ np.kron(S_0, S_y)).real
+        jz = tr(J @ np.kron(S_0, S_z)).real
+        if msummed or np.asarray(self.omega).ndim == 0:
+            r = np.array([jx, jy, jz]).transpose(1, 2, 3, 0)
+        else:
+            r = np.array([jx, jy, jz]).transpose(1, 2, 3, 4, 0)
+        return Current(self, r)
+
+    @property
+    def S_s(self):
+        """
+        Spectral spin density.
+
+        Direction index has same meaning as for :any:`J`.
+
+        :Type: :any:`Current`
+        :Shape: (nx, ny, [nw,] 3(spin-direction))
+        """
+        return self._S_s(self.S.A)
+
     @property
     def J_c(self):
         """
@@ -1029,11 +1092,9 @@ class Result:
         Direction index has same meaning as for :any:`J`.
 
         :Type: :any:`Current`
-        :Shape: ([nw,] nx, ny, 4(direction))
+        :Shape: (nx, ny, [nw,] 4(direction))
         """
-        t3 = np.diag([1, 1, -1, -1])
-        r = tr(self.J.A @ t3)
-        return Current(self, r)
+        return self._J_c(self.J.A)
 
     @property
     def J_s(self):
@@ -1047,14 +1108,74 @@ class Result:
         :Type: :any:`Current`
         :Shape: (nx, ny, 4(direction), [nw,] 3(spin-direction))
         """
-        jx = tr(self.J.A @ np.kron(s0, sx))
-        jy = tr(self.J.A @ np.kron(s0, sy))
-        jz = tr(self.J.A @ np.kron(s0, sz))
-        if np.asarray(self.omega).ndim == 0:
-            r = np.array([jx, jy, jz]).transpose(1, 2, 3, 0)
-        else:
-            r = np.array([jx, jy, jz]).transpose(1, 2, 3, 4, 0)
-        return Current(self, r)
+        return self._J_s(self.J.A)
+
+    @property
+    def J_tot(self):
+        r"""
+        Matsubara-summed matrix current between cells, :math:`J = -i \pi T\sum_{\omega_n} \mathcal{J}(\omega_n)`.
+
+        :Type: :any:`Current`
+        :Shape: (nx, ny, 4(direction), 4, 4)
+        """
+        if self._J_tot is None:
+            raise ValueError("J_tot not computed")
+        return Current(self, self._J_tot)
+
+    @property
+    def J_tot_c(self):
+        """
+        Charge current.
+
+        Direction index has same meaning as for :any:`J`.
+
+        :Type: :any:`Current`
+        :Shape: (nx, ny, 4(direction))
+        """
+        if self._J_tot is None:
+            raise ValueError("J_tot not computed")
+        return self._J_c(self._J_tot)
+
+    @property
+    def J_tot_s(self):
+        """
+        Spin current.
+
+        The current direction index has same meaning as for :any:`J`.
+
+        The spin direction index means (x, y, z).
+
+        :Type: :any:`Current`
+        :Shape: (nx, ny, 4(direction), 3(spin-direction))
+        """
+        if self._J_tot is None:
+            raise ValueError("J_tot not computed")
+        return self._J_s(self._J_tot, msummed=True)
+
+    @property
+    def S_tot(self):
+        """Matsubara-summed density, :math:`S = \pi T \sum_{\omega_n} [Q(\omega_n) - \tau_3]`.
+
+        :Type: :any:`Density`
+        :Shape: (nx, ny, 4, 4)
+        """
+        if self._S_tot is None:
+            raise ValueError("S_tot not computed")
+        return Density(self, self._S_tot)
+
+    @property
+    def S_s(self):
+        """
+        Spin density.
+
+        Direction index has same meaning as for :any:`J`.
+
+        :Type: :any:`Current`
+        :Shape: (nx, ny, 3(spin-direction))
+        """
+        if self._S_tot is None:
+            raise ValueError("S_tot not computed")
+        return self._S_s(self._S_tot, msummed=True)
 
 
 class UmfpackILU(LinearOperator):
@@ -1674,10 +1795,10 @@ def cpr(
         state.dss.append(float(np.squeeze(ds_cur)))
         state.Deltas.append(Delta.copy())
         state.phis.append(float(np.squeeze(phi)))
-        state.Js.append(solver.J_tot.A.copy())
+        state.Js.append(solver.J_tot.copy())
 
         t3 = np.diag([1, 1, -1, -1])
-        J_avg = np.mean(tr(solver.J_tot.A[:, :, 0].real @ t3))
+        J_avg = np.mean(tr(solver.J_tot[:, :, 0].real @ t3))
         _log.info(f"CPR #{j}: phi = {phi}, J = {J_avg}")
 
         if filename is not None: