diff --git a/hysop/backend/host/python/operator/multiresolution_filter.py b/hysop/backend/host/python/operator/multiresolution_filter.py
index 6ab6fdee6d7466e7e22a2cac6f0918af599994e1..6d7fba0412010eb997070bb3eb64774fe9e64aa2 100755
--- a/hysop/backend/host/python/operator/multiresolution_filter.py
+++ b/hysop/backend/host/python/operator/multiresolution_filter.py
@@ -11,8 +11,9 @@ from hysop.tools.decorators import debug
 
 
 class PythonMultiresolutionFilter(RedistributeOperatorBase):
-    """Interpolation between topologies when topology
-    differs only by global resolution
+    """
+    Interpolation between topologies when topology
+    differs only by global resolution.
 
     Source and target must:
      *be CartesianTopology topologies with a target
diff --git a/hysop/core/graph/computational_node.py b/hysop/core/graph/computational_node.py
index d05ec1619e9064e7e9343af5a0f729ddae7fc1c5..833cbe66ec32e378c4a4a41979b283e2e5bb3e38 100644
--- a/hysop/core/graph/computational_node.py
+++ b/hysop/core/graph/computational_node.py
@@ -275,6 +275,20 @@ class ComputationalGraphNode(OperatorBase):
         """Return True if this node has no input nor output fields."""
         return (not self.input_fields) and (not self.output_fields)
     is_domainless = property(_get_is_domainless)
+
+    @classmethod
+    def expand_tensor_fields(fields):
+        scalar_fields = ()
+        tensor_fields = ()
+        for field in fields:
+            if (field is None):
+                scalar_fields += (None,)
+            elif field.is_tensor:
+                scalar_fields += field.fields
+                tensor_fields += (field,)
+            else:
+                scalar_fields += (field,)
+        return (scalar_fields, tensor_fields)
     
     @debug
     def _setup_method(self, topgraph_method):
@@ -866,8 +880,8 @@ class ComputationalGraphNode(OperatorBase):
 
     @base_initialized
     def iter_output_fields(self, with_scalars=True, 
-                                         with_tensors=True, 
-                                         as_scalars=False):
+                                 with_tensors=True, 
+                                 as_scalars=False):
         """
         Iterate over all output fields.
         By default iterate over all tensors and scalars unless
diff --git a/hysop/operator/base/spatial_filtering.py b/hysop/operator/base/spatial_filtering.py
new file mode 100644
index 0000000000000000000000000000000000000000..f160158e53735f5d2f18bd0df3055f4521135ef2
--- /dev/null
+++ b/hysop/operator/base/spatial_filtering.py
@@ -0,0 +1,52 @@
+"""
+@file advection.py
+LowpassFilter operator generator.
+"""
+from hysop.constants import Implementation
+from hysop.tools.types import check_instance, to_list, first_not_None
+from hysop.tools.decorators import debug
+from hysop.fields.continuous_field import Field
+from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors
+from hysop.parameters.scalar_parameter import ScalarParameter
+from hysop.core.graph.node_generator import ComputationalGraphNodeGenerator
+from hysop.core.graph.computational_node_fronted import ComputationalGraphNodeFrontend
+
+
+class LowpassFilterBase(object):
+    """
+    Base implementation for lowpass spatial filtering: small grid -> coarse grid
+    """
+    
+    def __init__(self, input_field, output_field,
+                       input_topo,  output_topo,
+                       **kwds):
+
+        check_instance(input_field,  ScalarField)
+        check_instance(output_field, ScalarField)
+        check_instance(input_topo,  CartesianTopologyDescriptors)
+        check_instance(output_topo, CartesianTopologyDescriptors)
+
+        super(LowpassFilterBase, self).__init__(
+                input_fields={input_field: input_topo},
+                output_fields={output_field: output_topo},
+                **kwds)
+
+        self.Fin  = input_field
+        self.Fout = output_field
+
+
+    @debug
+    def discretize(self):
+        if self.discretized:
+            return
+        super(LowpassFilterBase, self).discretize()
+        dFin  = self.get_input_discrete_field(self.Fin)
+        dFout = self.get_output_discrete_field(self.Fout)
+
+        import sys
+        sys.exit(1)
+
+        self.dFin, self.dFout = dFin, dFout
+
+
+
diff --git a/hysop/operator/spatial_filtering.py b/hysop/operator/spatial_filtering.py
new file mode 100644
index 0000000000000000000000000000000000000000..c9b79d3c612b59fa754d7341f6b75916ba86ae80
--- /dev/null
+++ b/hysop/operator/spatial_filtering.py
@@ -0,0 +1,153 @@
+"""
+@file advection.py
+LowpassFilter operator generator.
+"""
+from hysop.constants import Implementation
+from hysop.tools.types import check_instance, to_list, first_not_None
+from hysop.tools.decorators import debug
+from hysop.fields.continuous_field import Field
+from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors
+from hysop.parameters.scalar_parameter import ScalarParameter
+from hysop.core.graph.node_generator import ComputationalGraphNodeGenerator
+from hysop.core.graph.computational_node_fronted import ComputationalGraphNodeFrontend
+
+
+class LowpassFilterFrontend(ComputationalGraphNodeFrontend):
+    """
+    Interface for lowpass spatial filtering: small grid -> coarse grid
+    Available implementations are:
+        *Python using remeshing kernels
+    """
+    @classmethod
+    def implementations(cls):
+        from hysop.backend.host.python.operator.spatial_filtering import \
+            PythonLowpassFilter
+        _implementations = {
+            Implementation.PYTHON: PythonLowpassFilter
+        }
+        return _implementations
+
+    @classmethod
+    def default_implementation(cls):
+        return Implementation.PYTHON
+    
+    def __init__(self, input_variable, output_variable,
+                 implementation=None,
+                 base_kwds=None,
+                 **kwds):
+        """
+        Initialize a LowpassFilter operator.
+
+        Parameters
+        ----------
+        input_variable: ScalarField
+            Input field on fine grid as a tuple (ScalarField, CartesianTopologyDescriptor).
+        output_variable: ScalarField
+            Output field on coarse grid as a tuple (ScalarField, CartesianTopologyDescriptor).
+        implementation: implementation, optional, defaults to None
+            target implementation, should be contained in available_implementations().
+            If None, implementation will be set to default_implementation().
+        base_kwds: dict, optional, defaults to None
+            Base class keywords arguments.
+            If None, an empty dict will be passed.
+        kwds:
+            Extra parameters passed to generated operator.
+
+        Notes
+        -----
+        An implementation should at least support the LowpassFilterBase interface.
+        """
+        check_instance(input_variable, tuple, size=2)
+        check_instance(output_variable, tuple, size=2)
+        input_field, input_topo   = input_variable
+        output_field, output_topo = output_variable
+
+        check_instance(input_field,  ScalarField)
+        check_instance(output_field, ScalarField)
+        check_instance(input_topo,  CartesianTopologyDescriptors)
+        check_instance(output_topo, CartesianTopologyDescriptors)
+        check_instance(base_kwds, dict, keys=str)
+        assert (input_topo != output_topo), "Same topology for input and output."
+
+        super(LowpassFilterFrontend, self).__init__(input_field=input_field, input_topo=input_topo,
+                                                    output_field=output_field, output_topo=output_topo,
+                                                    implementation=implementation, base_kwds=base_kwds, **kwds)
+
+
+
+class LowpassFilter(ComputationalGraphNodeGenerator):
+    """
+    Graphnode generator to lowpass filter multiple fields at once.
+    """
+    @debug
+    def __init__(self, input_variables, output_variables=None,
+                 implementation=None,
+                 base_kwds=None,
+                 **kwds):
+        """
+        Initialize a LowpassFilter operator generator.
+
+        Parameters
+        ----------
+        intput_variables: dict
+            Input fields on fine grid.
+            Dictionary of continuous fields as keys and topologies as values.
+        output_variables: dict
+            Output fields on coarse grid, default to input_fields.
+            Dictionary of continuous fields as keys and topologies as values.
+        implementation: implementation, optional, defaults to None
+            target implementation, should be contained in available_implementations().
+            If None, implementation will be set to default_implementation().
+        base_kwds: dict, optional, defaults to None
+            Base class keywords arguments.
+            If None, an empty dict will be passed.
+        kwds:
+            Extra parameters passed to generated operators.
+        """
+        input_fields = list(self.expand_tensor_fields(input_variables.keys())[0])
+        assert len(set(input_fields)) == len(input_fields)
+        if (output_variables is None):
+            output_variables = input_variables
+        output_fields = list(self.expand_tensor_fields(output_variables.keys())[0])
+        assert len(input_fields) == len(output_fields)
+        for i, field in enumerate(output_fields):
+            if (field is None):
+                output_fields[i] = input_fields[i]
+
+        input_fields  = tuple(input_fields)
+        output_fields = tuple(output_fields)
+        base_kwds = first_not_None(base_kwds, {})
+
+        check_instance(input_fields,  tuple, values=ScalarField)
+        check_instance(output_fields, tuple, values=ScalarField)
+        check_instance(input_variables,  dict, keys=Field, values=CartesianTopologyDescriptors)
+        check_instance(output_variables, dict, keys=Field, values=CartesianTopologyDescriptors)
+        check_instance(base_kwds, dict, keys=str)
+
+        super(LowpassFilter, self).__init__(**base_kwds)
+            
+        self._input_fields     = input_fields
+        self._output_fields    = output_fields
+        self._input_variables  = input_variables
+        self._output_variables = output_variables
+        self._impl             = implementation
+        self._kwds             = kwds
+
+    @debug
+    def _generate(self):
+        nodes = []
+        for (ifield, ofield) in zip(self._input_fields, self._output_fields):
+            stopo = self.get_topo_descriptor(self._input_variables, ifield)
+            ttopo = self.get_topo_descriptor(self._output_variables, ofield)
+            impl  = self._impl
+            kwds  = self._kwds.copy()
+            
+            # if source topology is destination topology there is nothing to be done
+            if (ttopo == stopo):
+                continue
+                
+            # else we build a lowpass filter operator
+            node = LowpassFilterFrontend(input_field=(ifield,stopo) output_field=(ofield,ttopo), 
+                    implementation=impl, **kwds)
+            nodes.append(node)
+        return nodes
diff --git a/hysop/operators.py b/hysop/operators.py
index 3399f0e45e0ce7eadff1856f73c463e294e02b7e..50cd5db0e1d7edec5cc3c0797396a432ae6e0ac4 100644
--- a/hysop/operators.py
+++ b/hysop/operators.py
@@ -23,10 +23,11 @@ from hysop.operator.integrate              import Integrate
 from hysop.operator.penalization           import PenalizeVorticity
 from hysop.operator.flowrate_correction    import FlowRateCorrection
 from hysop.operator.vorticity_absorption   import VorticityAbsorption
-from hysop.operator.multiresolution_filter import MultiresolutionFilter
 from hysop.operator.dummy                  import Dummy
 from hysop.operator.custom                 import CustomOperator
 from hysop.operator.convergence            import Convergence
+from hysop.operator.multiresolution_filter import MultiresolutionFilter
+from hysop.operator.spatial_filtering import LowpassFilter
 
 from hysop.operator.derivative import SpaceDerivative,                  \
                                       SpectralSpaceDerivative,          \