diff --git a/kwant/operator.pyx b/kwant/operator.pyx
index b5ed1b5e7cf0f6612999b83d806ce774565111bc..1dd99ebbb547d754c8ff324f621f3add9fe8bbd0 100644
--- a/kwant/operator.pyx
+++ b/kwant/operator.pyx
@@ -5,7 +5,7 @@
 # http://kwant-project.org/license.  A list of Kwant authors can be found in
 # the file AUTHORS.rst at the top-level directory of this distribution and at
 # http://kwant-project.org/authors.
-r"""Tools for working with operators for acting on wavefunctions."""
+"""Tools for working with operators for acting on wavefunctions."""
 
 __all__ = ['Density', 'Current', 'Source']
 
@@ -379,10 +379,6 @@ cdef class _LocalOperator:
     syst : `~kwant.system.System`
         The system for which this operator is defined. Must have the
         number of orbitals defined for all site families.
-    where : 2D array of `int` or `None`
-        where to evaluate the operator. A list of sites for on-site
-        operators (accessed like `where[n, 0]`), otherwise a list of pairs
-        of sites (accessed like `where[n, 0]` and `where[n, 1]`).
     onsite : complex 2D array, or callable
         If a complex array, then the same onsite is used everywhere.
         Otherwise, function that can be called with a single site (integer) and
@@ -391,18 +387,23 @@ cdef class _LocalOperator:
         same shape as that returned by the system Hamiltonian evaluated on the
         same site.  The extra arguments must be the same as the extra arguments
         to ``syst.hamiltonian``.
+    where : 2D array of `int` or `None`
+        where to evaluate the operator. A list of sites for on-site
+        operators (accessed like `where[n, 0]`), otherwise a list of pairs
+        of sites (accessed like `where[n, 0]` and `where[n, 1]`).
     check_hermiticity : bool
         If True, checks that ``onsite``, as well as any relevant parts
         of the Hamiltonian are hermitian.
     """
 
-    cdef public int check_hermiticity
+    cdef public int check_hermiticity, sum
     cdef public object syst, onsite
     cdef public gint[:, :]  where, _site_ranges
     cdef public BlockSparseMatrix _bound_onsite, _bound_hamiltonian
 
     @cython.embedsignature
-    def __init__(self, syst, onsite, where, check_hermiticity):
+    def __init__(self, syst, onsite, where, *,
+                 check_hermiticity=True, sum=False):
         if syst.site_ranges is None:
             raise ValueError('Number of orbitals not defined.\n'
                              'Declare the number of orbitals using the '
@@ -412,19 +413,24 @@ cdef class _LocalOperator:
         self.syst = syst
         self.onsite = _normalize_onsite(syst, onsite, check_hermiticity)
         self.check_hermiticity = check_hermiticity
+        self.sum = sum
         self._site_ranges = np.asarray(syst.site_ranges, dtype=gint_dtype)
+        self.where = where
         self._bound_onsite = None
         self._bound_hamiltonian = None
-        self.where = None
-        # NOTE: subclasses should populate `where`
 
     @cython.embedsignature
     def __call__(self, bra, ket=None, args=()):
         """Return the matrix elements of the operator.
 
+        For an operator :math:`Q_{iαβ}`, ``bra`` :math:`φ_α`
+        and ``ket`` :math:`ψ_β` this computes
+        :math:`q_i = ∑_{αβ} φ^*_α Q_{iαβ} ψ_β`. if ``self.sum``
+        is False, otherwise computes :math:`q = ∑_{iαβ} φ^*_α Q_{iαβ} ψ_β`.
+
         Parameters
         ----------
-        bra, ket : ``numpy.ndarray``
+        bra, ket : sequence of complex
             Must have the same length as the number of orbitals
             in the system. If only one is provided, both ``bra``
             and ``ket`` are taken as equal.
@@ -434,12 +440,8 @@ cdef class _LocalOperator:
 
         Returns
         -------
-        (values, where)
-            both elements of the tuple are arrays of the same length.
-            ``values`` is an array of `float` if ``check_hermiticity`` is True,
-            otherwise it is `complex`. ``where`` is an array of `int` that
-            specifies the sites/hoppings for which the matrix elements
-            are calculated.
+        Array of `float` if ``check_hermiticity`` is True,
+        otherwise an array of `complex`.
         """
         if (self._bound_onsite or self._bound_hamiltonian) and args:
             raise ValueError('Extra arguments are already bound to this '
@@ -469,15 +471,18 @@ cdef class _LocalOperator:
         if self.check_hermiticity:
             assert np.allclose(result.imag, 0)
             result = result.real
-        return (result, where)
+        return np.sum(result) if self.sum else result
 
     @cython.embedsignature
     def act(self, ket, args=()):
         """Act with the operator on a wavefunction.
 
+        For an operator :math:`Q_{iαβ}` and ``ket`` :math:`ψ_β`
+        this computes :math:`∑_{iβ} Q_{iαβ} ψ_β`.
+
         Parameters
         ----------
-        ket : ``numpy.ndarray``
+        ket : sequence of complex
             Wavefunctions defined over all the orbitals of the system.
         args : tuple
             The extra arguments to the Hamiltonian value functions and
@@ -485,8 +490,7 @@ cdef class _LocalOperator:
 
         Returns
         -------
-        ``numpy.ndarray``
-            The result of acting on the wavefunction with the operator
+        Array of `complex`.
         """
         if (self._bound_onsite or self._bound_hamiltonian) and args:
             raise ValueError('Extra arguments are already bound to this '
@@ -580,10 +584,11 @@ cdef class _LocalOperator:
 cdef class Density(_LocalOperator):
     """An operator for calculating general densities.
 
-    Examples of "densities" include charge and spin, and are defined by a
-    square matrix on each site (of the size of the number of orbitals on the
-    site).
-
+    In general, if there is a certain "density" (e.g. charge or spin) that is
+    represented by a square matrix :math:`M_i` associated with each site
+    :math:`i` then an instance of this class represents the tensor
+    :math:`Q_{iαβ}` which is equal to :math:`M_i` when α and β are orbitals on
+    site :math:`i`, and zero otherwise.
 
     Parameters
     ----------
@@ -604,38 +609,16 @@ cdef class Density(_LocalOperator):
         Hermitian, then an error will be raised when the operator is
         evaluated.
 
-    Notes
-    -----
-    When this class is called in the following way::
-
-        Q = kwant.physics.operator.Density(fsyst, M_a)
-        Q(phi, psi)
-
-    the following expression is calculated for each site ``a``
-    in ``where``::
-
-        phi[i:j].conjugate().dot(M_a.dot(psi[i:j]))
-
-    where ``i:j`` is a slice over all the orbitals on site ``a``,
-    and ``M_a`` is a square matrix associated with site ``a``.
-
-    When used in the following way::
-
-        phi = Q.act(psi)
-
-    this is equivalent to performing the following operation
-    for each site ``a`` in ``where``::
-
-        phi[i:j] += M_a.dot(psi[i:j])
-
     .. rubric:: Special Methods
     .. automethod:: __call__
     """
 
     @cython.embedsignature
-    def __init__(self, syst, onsite=1, where=None, check_hermiticity=True):
-        super().__init__(syst, onsite, where, check_hermiticity)
-        self.where = _normalize_site_where(syst, where)
+    def __init__(self, syst, onsite=1, where=None, *,
+                 check_hermiticity=True, sum=False):
+        where = _normalize_site_where(syst, where)
+        super().__init__(syst, onsite, where,
+                        check_hermiticity=check_hermiticity)
 
     @cython.boundscheck(False)
     @cython.wraparound(False)
@@ -683,11 +666,16 @@ cdef class Density(_LocalOperator):
 cdef class Current(_LocalOperator):
     """An operator for calculating general currents.
 
-    Examples of "currents" include charge currents and spin currents.
-    If there is a certain "density" (e.g. charge or spin) that is
-    represented by the operator ``M``, then the associated current
-    is represented by the off-diagonal part of the commutator between
-    the Hamiltonian and ``M``: ``[H, M]``.
+    In general, if there is a certain "density" (e.g. charge or spin) that is
+    represented by a square matrix :math:`M_i` associated with each site
+    :math:`i` and :math:`H_{ij}` is the hopping Hamiltonian from site :math:`j`
+    to site `i`, then an instance of this class represents the tensor
+    :math:`J_{ijαβ}` which is equal to :math:`(H_{ij})^† M_i - M_i H_{ij}` when
+    α and β are orbitals on sites :math:`i` and :math:`j` respectively, and
+    zero otherwise.
+
+    The tensor :math:`J_{ijαβ}` will also be referred to as :math:`Q_{nαβ}`,
+    where :math:`n` is the index of hopping :math:`(i, j)` in ``where``.
 
     Parameters
     ----------
@@ -710,47 +698,16 @@ cdef class Current(_LocalOperator):
         is not Hermitian, then an error will be raised when the
         operator is evaluated.
 
-    Notes
-    -----
-    Calculates the flux of a quantity ``M_a`` through hoppings.
-
-    When this class is called in the following way::
-
-        J = kwant.physics.operator.Current(fsyst, M_a)
-        J(phi, psi)
-
-    the following expression is calculated for each pair of sites
-    ``(a, b)`` in ``where``::
-
-        1j * (phi[bi:bj].conjugate().dot(
-                H_ab.conjugate().transpose().dot(M_a.dot(psi[ai:aj])))
-              -
-              phi[ai:aj].conjugate().dot(
-                M_a.dot(H_ab.dot(psi[bi:bj])))
-             )
-
-    where ``ai:aj`` and ``bi:bj`` are slices over all the orbitals on sites
-    ``a`` and ``b`` respectively. ``M_a`` is a square matrix and ``H_ab`` is
-    the Hamiltonian matrix element between the sites.
-
-    When used in the following way::
-
-        phi = J.act(psi)
-
-    this is equivalent to performing the following operations
-    for each pair of sites ``(a, b)`` in ``where``::
-
-        phi[bi:bj] += 1j * H_ab.conjugate().transpose().dot(M_a.dot(psi[ai:aj]))
-        phi[ai:aj] += -1j * M_a.dot(H_ab.dot(psi[bi:bj]))
-
     .. rubric:: Special Methods
     .. automethod:: __call__
     """
 
     @cython.embedsignature
-    def __init__(self, syst, onsite=1, where=None, check_hermiticity=True):
-        super().__init__(syst, onsite, where, check_hermiticity)
-        self.where = _normalize_hopping_where(syst, where)
+    def __init__(self, syst, onsite=1, where=None, *,
+                 check_hermiticity=True, sum=False):
+        where = _normalize_hopping_where(syst, where)
+        super().__init__(syst, onsite, where,
+                         check_hermiticity=check_hermiticity)
 
     @cython.embedsignature
     def bind(self, args=()):
@@ -823,11 +780,13 @@ cdef class Current(_LocalOperator):
 cdef class Source(_LocalOperator):
     """An operator for calculating general sources.
 
-    An example of a "source" is a spin torque. In general,
-    if there is a certain "density" (e.g. charge or spin) that is
-    represented by the operator ``M``, then the associated source
-    is represented by the diagonal part of the commutator between
-    the Hamiltonian and ``M``: ``[H, M]``.
+    An example of a "source" is a spin torque. In general, if there is a
+    certain "density" (e.g. charge or spin) that is represented by  a square
+    matrix :math:`M_i` associated with each site :math:`i`, and :math:`H_{i}`
+    is the onsite Hamiltonian on site site `i`, then an instance of this class
+    represents the tensor :math:`Q_{iαβ}` which is equal to :math:`(H_{i})^†
+    M_i - M_i H_{i}` when α and β are orbitals on site :math:`i`, and zero
+    otherwise.
 
     Parameters
     ----------
@@ -849,46 +808,16 @@ cdef class Source(_LocalOperator):
         Hermitian, then an error will be raised when the operator is
         evaluated.
 
-    Notes
-    -----
-    Calculates the source of a quantity ``M_a`` on different sites.
-
-    When this class is called in the following way::
-
-        K = kwant.physics.operator.Source(fsyst, M_a)
-        K(phi, psi)
-
-    the following expression is calculated for each site ``a``
-    in ``where``::
-
-        1j * (phi[i:j].conjugate().dot(
-                H_aa.conjugate().transpose().dot(M_a.dot(psi[i:j])))
-              -
-              phi[i:j].conjugate().dot(
-                M_a.dot(H_aa.dot(psi[i:j])))
-             )
-
-    where ``i:j`` is a slice over all the orbitals on site ``a`` ``M_a`` is a
-    square matrix and ``H_aa`` is the onsite Hamiltonian matrix element for
-    site ``a``.
-
-    When used in the following way::
-
-        phi = K.act(psi)
-
-    this is equivalent to performing the following operation
-    for each element in ``where``::
-
-        phi[i:j] += M_a.dot(psi[i:j])
-
     .. rubric:: Special Methods
     .. automethod:: __call__
     """
 
     @cython.embedsignature
-    def __init__(self, syst, onsite=1, where=None, check_hermiticity=True):
-        super().__init__(syst, onsite, where, check_hermiticity)
-        self.where = _normalize_site_where(syst, where)
+    def __init__(self, syst, onsite=1, where=None, *,
+                 check_hermiticity=True, sum=False):
+        where = _normalize_site_where(syst, where)
+        super().__init__(syst, onsite, where,
+                         check_hermiticity=check_hermiticity)
 
     @cython.embedsignature
     def bind(self, args=()):
diff --git a/kwant/tests/test_operator.py b/kwant/tests/test_operator.py
index cf12f8c6814e4f8f867d001aae7b8af114eb4daf..02ee61f565d78df258a0eca9dff4ec18005068ce 100644
--- a/kwant/tests/test_operator.py
+++ b/kwant/tests/test_operator.py
@@ -49,7 +49,7 @@ def _perfect_lead(N, norbs=1):
     return  lat, syst
 
 
-def test_opservables_construction():
+def test_operator_construction():
     lat, syst = _random_square_system(3)
     fsyst = syst.finalized()
     N = len(fsyst.sites)
@@ -141,17 +141,25 @@ def test_opservables_construction():
 
 def _test(A, bra, ket=None, per_el_val=None, reduced_val=None, args=()):
     if per_el_val is not None:
-        val, where = A(bra, ket, args=args)
+        val = A(bra, ket, args=args)
+        print(A.sum)
         assert np.allclose(val, per_el_val)
         # with bound args
-        val, where = A.bind(args)(bra, ket)
+        val = A.bind(args)(bra, ket)
         assert np.allclose(val, per_el_val)
     # test that inner products give the same thing
     ket = bra if ket is None else ket
     act_val = np.dot(bra.conj(), A.act(ket, args=args))
-    inner_val, where = A(bra, ket, args=args)
-    inner_val = np.sum(inner_val)
+    inner_val = np.sum(A(bra, ket, args=args))
+    # check also when sum is done internally by operator
+    A.sum = True
+    try:
+        sum_inner_val = A(bra, ket, args=args)
+    finally:
+        A.sum = False
+
     assert np.isclose(act_val, inner_val)
+    assert np.isclose(sum_inner_val, inner_val)
     if reduced_val is not None:
         assert np.isclose(inner_val, reduced_val)