diff --git a/examples/multiresolution/scalar_advection.py b/examples/multiresolution/scalar_advection.py
index fa9d3a58601feb4e77c338d357b1040515ed650f..9acfdebc216f35f383f8996061f39952e4fd944b 100644
--- a/examples/multiresolution/scalar_advection.py
+++ b/examples/multiresolution/scalar_advection.py
@@ -26,9 +26,9 @@ def compute(args):
 
     # Define domain
     dim   = args.ndim
-    npts  = args.npts
-    snpts = args.snpts
-    fnpts = tuple(2*_ for _ in snpts)
+    npts  = args.npts                  # coarse resolution
+    snpts = args.snpts                 # fine resolution
+    fnpts = tuple(3*_ for _ in snpts)  # finest resolution
     lboundaries = (BoxBoundaryCondition.PERIODIC,)*dim
     rboundaries = (BoxBoundaryCondition.PERIODIC,)*dim
     box  = Box(origin=args.box_origin, length=args.box_length, dim=dim,
diff --git a/hysop/backend/device/opencl/operator/spatial_filtering.py b/hysop/backend/device/opencl/operator/spatial_filtering.py
index c01c11a2b428c596c6d69576ae1c61fbeb2885c9..d6f168e6e38e4f1646a18cbd23ed39ba6d064ce9 100644
--- a/hysop/backend/device/opencl/operator/spatial_filtering.py
+++ b/hysop/backend/device/opencl/operator/spatial_filtering.py
@@ -32,17 +32,17 @@ class OpenClPolynomialInterpolationFilter(PolynomialInterpolationFilterBase, Ope
         dim = dFin.dim
         assert dFin.is_scalar
         assert dFout.is_scalar
+        assert self.subgrid_interpolator.gr == gr
         
         ekg = self.elementwise_kernel_generator
-        W   = self.subgrid_interpolator.W
-        s   = self.subgrid_interpolator.s
-        n   = (self.subgrid_interpolator.n,)*dim
-        ghosts = np.asarray((self.subgrid_interpolator.ghosts,)*dim)
+        Wr  = self.subgrid_interpolator.Wr
+        n   = self.subgrid_interpolator.n
+        ghosts = np.asarray(self.subgrid_interpolator.ghosts)
         
         I = np.asarray(local_indices_symbols[:dim][::-1])
         fin, fout = ekg.dfields_to_ndbuffers(dFin, dFout)
         Fin  = ekg.symbolic_tmp_scalars('F', shape=n, dtype=dFin.dtype)
-        Fout_values = W.dot(Fin.ravel()).reshape(s)
+        Fout_values = Wr.dot(Fin.ravel()).reshape(gr)
 
         exprs = ()
         for idx in np.ndindex(*n):
@@ -53,7 +53,7 @@ class OpenClPolynomialInterpolationFilter(PolynomialInterpolationFilterBase, Ope
             exprs += (e,)
         
         interpolate_grid_kernel, _ = ekg.elementwise_kernel('interpolate_grid', 
-                *exprs, compute_resolution=dFin.compute_resolution, debug=False)
+                *exprs, compute_resolution=self.iter_shape, debug=False)
 
         exchange_ghosts = self.dFout.exchange_ghosts(build_launcher=True)
         
diff --git a/hysop/backend/host/python/operator/spatial_filtering.py b/hysop/backend/host/python/operator/spatial_filtering.py
index 24a2fd065f391a26e91b7b47880a2227c3b8cba1..a8268882778ba704c9393adaf7e1c5a0964f49ca 100644
--- a/hysop/backend/host/python/operator/spatial_filtering.py
+++ b/hysop/backend/host/python/operator/spatial_filtering.py
@@ -16,8 +16,7 @@ class PythonPolynomialInterpolationFilter(PolynomialInterpolationFilterBase, Hos
         if self.discretized:
             return
         super(PythonPolynomialInterpolationFilter, self).discretize(**kwds)
-        self.W = self.subgrid_interpolator.W.astype(self.dtype)
-        self.iter_shape = self.dFin.compute_resolution
+        self.Wr = self.subgrid_interpolator.Wr.astype(self.dtype)
 
     @op_apply
     def apply(self, **kwds):
@@ -25,16 +24,17 @@ class PythonPolynomialInterpolationFilter(PolynomialInterpolationFilterBase, Hos
         super(PythonPolynomialInterpolationFilter, self).apply(**kwds)
         fin    = self.fin
         fout   = self.fout
-        shape  = self.iter_shape
+        ishape  = self.dFin.compute_resolution
+        oshape = self.dFout.compute_resolution
+        periodicity = self.dFin.periodicity
         gr     = self.grid_ratio
-        s, n   = self.subgrid_interpolator.s, self.subgrid_interpolator.n
-        oshape = fout.shape
+        gr, n  = self.subgrid_interpolator.gr, self.subgrid_interpolator.n
+        Wr     = self.Wr
         
-        for idx in np.ndindex(*shape):
-            oslc = tuple(slice(j*gr[i], (j+1)*gr[i]+1, 1) for i,j in enumerate(idx)) 
-            islc = tuple(slice(1+j, 1+j+n, 1) for i,j in enumerate(idx))
-            rslc = tuple(slice(0, min(s[i], oshape[i]-j*gr[i]), 1) for i,j in enumerate(idx))
-            fout[oslc] = self.W.dot(fin[islc].ravel()).reshape(s)[rslc]
+        for idx in np.ndindex(*self.iter_shape):
+            oslc = tuple(slice(j*gr[i], (j+1)*gr[i], 1) for i,j in enumerate(idx)) 
+            islc = tuple(slice(periodicity[i]+j, periodicity[i]+j+n[i], 1) for i,j in enumerate(idx))
+            fout[oslc] = Wr.dot(fin[islc].ravel()).reshape(gr)
         self.dFout.exchange_ghosts()
 
 
diff --git a/hysop/numerics/interpolation/polynomial.py b/hysop/numerics/interpolation/polynomial.py
index 8aa98499aad8fd9e4da468830edae4a2848f79de..2e49a40aa22714d837920052535e9238568377b7 100644
--- a/hysop/numerics/interpolation/polynomial.py
+++ b/hysop/numerics/interpolation/polynomial.py
@@ -2,6 +2,7 @@ import itertools as it
 import numpy as np
 import scipy as sp
 import sympy as sm
+import warnings
 
 try:
     import flint
@@ -16,11 +17,12 @@ except ImportError:
     has_flint = False
 
 from hysop.tools.enum import EnumFactory
-from hysop.tools.types import check_instance, InstanceOf
+from hysop.tools.types import check_instance, InstanceOf, to_tuple
 from hysop.tools.cache import update_cache, load_data_from_cache
 from hysop.tools.io_utils import IO
 from hysop.tools.sympy_utils import tensor_xreplace, tensor_symbol
 from hysop.tools.decorators import debug
+from hysop.tools.warning import HysopCacheWarning
 from hysop.numerics.stencil.stencil_generator import CenteredStencilGenerator, MPQ
 from hysop.numerics.interpolation.interpolation import MultiScaleInterpolation
 
@@ -96,7 +98,7 @@ class PolynomialInterpolationMethod(SpaceDiscretizationMethod):
     Operator helper to handle polynomial interpolation method.
     """
     __default_method = {
-        MultiScaleInterpolation: PolynomialInterpolation.NONIC_FDC2,
+        MultiScaleInterpolation: PolynomialInterpolation.CUBIC_FDC2,
     }
 
     __available_methods = {
@@ -168,11 +170,11 @@ class PolynomialInterpolator(object):
         ----------
         dim: int
             Number of dimensions to interpolate.
-        deg: int
+        deg: int or tuple of ints
             Polynomial degree (1=linear, 3=cubic, 5=quintic, 7=septic, ...)
-            Degree should be odd.
-        fd: int
-            Order of centered finite differences used to compute derivatives.
+            Degree should be odd on each axis.
+        fd: int or tuple of ints
+            Order of centered finite differences used to compute derivatives in each direction.
             Will affect the number of ghosts of the method.
             Should be even because this interpolator only use centered dinite differences.
         approximative: bool
@@ -184,20 +186,20 @@ class PolynomialInterpolator(object):
         ----------
         dim: int
             Number of dimensions to interpolate.
-        deg: int
+        deg: tuple of ints
             Polynomial degree (1=linear, 3=cubic, 5=quintic, 7=septic, ...)
             Degree should be odd: deg=2k+1 
-        fd: int
-            Order of centered finite differences stencils used to compute derivatives.
-        p: int
+        fd: tuple of ints
+            Order of centered finite differences stencils used to compute derivatives for each direction.
+        p: tuple of ints
             Corresponds to deg+1.
-            The total number of polynomial coefficients corresponds to P=p**dim.
+            The total number of polynomial coefficients corresponds to P=p0*p1*...*p(dim-1).
         P: int
-            The total number of polynomial coefficients P=p**dim.
-        k: int
-            Max derivative order required to compute the polynomial interpolator coefficients.
+            The total number of polynomial coefficients P=p0*p1*...*p(dim-1)
+        k: tuple of ints
+            Max derivative order required to compute the polynomial interpolator coefficients in each direction.
             Also the regularity of the resulting interpolant. Corresponds to (deg-1)/2.
-        ghosts: int
+        ghosts: tuple of ints
             Return the number of ghosts required by the interpolator on each axis.
             Corresponds to (k>0)*[fd//2 - 1 + (k+1)//2]
                       deg    k   (k+1)/2  | FDC2  FDC4  FDC6
@@ -206,29 +208,29 @@ class PolynomialInterpolator(object):
             quintic:   5     2      1     |  1     2     3
             septic:    7     3      2     |  2     3     4
             nonic:     9     4      2     |  2     3     4
-        n: int
+        n: tuple of ints
             Corresponds to 2*(ghosts+1), the number of required nodes to generate the
             polynomial coefficients (in each direction). 
-            In total we have N=n**dim input nodes.
+            In total we have N=n0*n1*...*n(dim-1) input nodes.
 
-            G     G
+            G1    G1
            <->   <->
             X X X X
             X P P X
             X P P X
             X X X X
             <----->
-               n
+               n1
         N: int
-            Total number of input nodes N=n**dim.
+            Total number of input nodes N=n0*n1*...*n(dim-1).
         M: np.ndarray
             Grid values to polynomial coefficient matrix:
                 M.dot(F.ravel()) will give C.ravel(), coefficients of P(x0,x1,...)
-                 N = n**dim 
+                     N 
                 <--------->
                 X X X X X X ^                         |f0| ^                   |c0| ^
                 X X X X X X |                         |f1| |                   |c1| |
-            M = X X X X X X | P = p**dim          F = |f2| | N       C = M*F = |c2| | P
+            M = X X X X X X | P                   F = |f2| | N       C = M*F = |c2| | P
                 X X X X X X |                         |f3| |                   |c3| |
                 X X X X X X v                         |f4| |                   |c4| v
                                                       |f5| v
@@ -242,23 +244,44 @@ class PolynomialInterpolator(object):
         """
         
         assert dim>0, 'dim<=0'
-        assert deg%2==1, 'deg % 2 != 1'
-        assert fd%2==0, 'fd % 2 != 0'
 
-        dim = dim
-        k = (deg-1)/2
-
-        if (k>0):
-            required_ghosts = (k+1)/2
-            ghosts = (fd/2) - 1 + required_ghosts
-        else:
-            ghosts = 0
-
-        p = deg+1
-        n = 2*(ghosts+1)
+        deg = to_tuple(deg)
+        if len(deg)==1:
+            deg*=dim
+        check_instance(deg, tuple, values=int, size=dim)
         
-        P = p**dim
-        N = n**dim
+        fd = to_tuple(fd)
+        if len(fd)==1:
+            fd*=dim
+        check_instance(fd, tuple, values=int, size=dim)
+
+        p = tuple(degi+1 for degi in deg)
+        k = tuple((degi-1)/2 for degi in deg)
+
+        ghosts = ()
+        n = ()
+        for (fdi,ki) in zip(fd,k):
+            if (ki>0):
+                gi = (fdi/2) - 1 + (ki+1)/2
+            else:
+                gi = 0
+            ni = 2*(gi+1)
+            ghosts += (gi,)
+            n += (ni,)
+        
+        check_instance(deg,   tuple, values=int, size=dim)
+        check_instance(fd,    tuple, values=int, size=dim)
+        check_instance(p,     tuple, values=int, size=dim)
+        check_instance(k,     tuple, values=int, size=dim)
+        check_instance(n,     tuple, values=int, size=dim)
+        check_instance(ghosts,tuple, values=int, size=dim)
+        assert all(fdi%2==0 for fdi in fd), 'fd % 2 != 0'
+        assert all(degi%2==1 for degi in deg), 'deg % 2 != 1'
+        assert all(pi%2==0 for pi in p), 'p % 2 != 0'
+        assert all(ni%2==0 for ni in n), 'n % 2 != 0'
+        
+        P = np.prod(p, dtype=np.int32)
+        N = np.prod(n, dtype=np.int32)
 
         self.dim             = dim
         self.deg             = deg
@@ -278,7 +301,7 @@ class PolynomialInterpolator(object):
         dim     = self.dim
         ghosts  = self.ghosts
         verbose = self.verbose
-        order   = self.fd
+        fd      = self.fd
         approximative = self.approximative
 
         k = self.k
@@ -289,76 +312,89 @@ class PolynomialInterpolator(object):
             print '\nCollecting 1D stencils:'
         
         SG = CenteredStencilGenerator()
-        SG.configure(dim=1, dtype=np.float64, order=order)
-        S = []
-        for i in xrange(k+1):
-            msg='Failed to compute stencil derivative={}, order={}, origin={}'
-            msg=msg.format(i, order, ghosts)
-            try:
-                if approximative:
-                    Si = SG.generate_approximative_stencil(derivative=i)
-                else:
-                    Si = SG.generate_exact_stencil(derivative=i)
-                Si.replace_symbols({Si.dx:1})
-            except:
-                print msg
-                raise
-            msg+=' got {}.'.format(Si.coeffs)
-            assert (not Si.is_symbolic()), msg
-            Si = Si.reshape((self.n-1,))
-            assert Si.origin == ghosts
-            Si = Si.coeffs
-            S.append(Si)
+        SG.configure(dim=1, dtype=np.float64)
+        S = {}
+        for direction in xrange(dim):
             if verbose:
-                print '  {}-th derivative: {}'.format(i,Si)
-        self.S = S
+                print ' Direction {}'.format(direction)
+            Sd  = S.setdefault(direction, [])
+            nd  = n[direction]
+            kd  = k[direction]
+            fdd = fd[direction]
+            gd  = ghosts[direction]
+            for i in xrange(kd+1):
+                msg='Failed to compute stencil derivative={}, order={}, origin={}'
+                msg=msg.format(i, fdd, gd)
+                try:
+                    if approximative:
+                        Si = SG.generate_approximative_stencil(order=fdd, derivative=i)
+                    else:
+                        Si = SG.generate_exact_stencil(order=fdd, derivative=i)
+                    Si.replace_symbols({Si.dx:1})
+                except:
+                    print msg
+                    raise
+                msg+=' got {}.'.format(Si.coeffs)
+                assert (not Si.is_symbolic()), msg
+                Si = Si.reshape((nd-1,))
+                assert Si.origin == gd
+                Si = Si.coeffs
+                Sd.append(Si)
+                if verbose:
+                    print '  {}-th derivative: {}'.format(i,Si)
+        return S
 
     def _build_stencil(self, dvec):
         dvec = np.asarray(dvec)    
+        k = self.k
+        S = self.S
         assert dvec.size == self.dim, 'dvec.size != dim'
-        assert (dvec>=0).all(), 'dvec < 0    => {}'.format(dvec)
-        assert (dvec<=self.k).all(), 'dvec > dmax => {}'.format(dvec)
+        assert all(dvec>=0), 'dvec < 0  => {}'.format(dvec)
+        assert all(di<=ki for (di,ki) in zip(dvec,k)), 'dvec > dmax => {}'.format(dvec)
 
-        Sd = self.S[dvec[0]].copy()
-        for di in dvec[1:]:
-            Sdi = self.S[di]
-            Sd = np.tensordot(Sd, Sdi, axes=0)
+        Sd = S[0][dvec[0]].copy()
+        for (d,i) in enumerate(dvec[1:],1):
+            Sd = np.tensordot(Sd, S[d][i], axes=0)
         return Sd
 
     def _build_interpolator(self):
-        dim    = self.dim
-        deg    = self.deg
-        maxd   = self.k
+        dim = self.dim
+        deg = self.deg
+        k   = self.k
+        n   = self.n
+        p   = self.p
         ghosts = self.ghosts
         approximative = self.approximative
         verbose = self.verbose
 
-        forigin = (ghosts,)*dim
-        fshape  = (2*(ghosts+1),)*dim
-        pshape  = (deg+1,)*dim
-        
-        xvals,  xvars  = tensor_symbol('x',shape=(dim,))
-        fvals,  fvars  = tensor_symbol('F',fshape,forigin)
-        pvals,  pvars  = tensor_symbol('C',pshape)
+        xvals,  xvars  = tensor_symbol('x', shape=(dim,))
+        fvals,  fvars  = tensor_symbol('F', n, ghosts)
+        pvals,  pvars  = tensor_symbol('C', p)
 
         self.xvals,  self.xvars  = xvals, xvars
         self.fvals,  self.fvars  = fvals, fvars
         self.pvals,  self.pvars  = pvals, pvars
         
+        try:
+            data = load_data_from_cache(self.cache_file(), self.key)
+            if (data is not None):
+                (P0, S, M) = data
+                if _check_matrix(M):
+                    self.P0 = P0
+                    self.S  = S
+                    self.M  = M
+                    return
+        except Exception as e:
+            msg='Failed to load data from cache because:\n{}'.format(e)
+            warnings.warn(msg, HysopCacheWarning)
+        
         P0 = 0
-        for idx in it.product(range(0,deg+1), repeat=dim):
-            val = pvals[idx]
-            for i in xrange(dim):
-                val*= pow(xvals[i],idx[i])
-            P0 += val
+        for idx in it.product(*tuple(range(0,pi) for pi in p)):
+            P0 += pvals[idx] * np.prod(np.power(xvals, idx))
         self.P0 = P0
         
-        self._collect_stencils()
-        
-        M = load_data_from_cache(self.cache_file(), self.key)
-        if (M is not None) and _check_matrix(M):
-            self.M = M
-            return
+        S = self._collect_stencils()
+        self.S = S
         
         if verbose:
             print '\nGenerating variables:'
@@ -373,7 +409,7 @@ class PolynomialInterpolator(object):
             print '\nBuilding system...'
         
         eqs = []
-        for dvec in it.product( range(0,maxd+1), repeat=dim ):
+        for dvec in it.product(*tuple(range(0,ki+1) for ki in k)):
             if verbose:
                 print '  => derivative {}'.format(dvec)
             
@@ -386,17 +422,17 @@ class PolynomialInterpolator(object):
                 print '     stencil:'
                 print stencil
 
-            for idx in it.product( range(ghosts,ghosts+2), repeat=dim):
+            for idx in it.product(*tuple(range(gi,gi+2) for gi in ghosts)):
                 if verbose:
                     print '    -> point {}'.format(idx)
                 
                 pos = np.asarray(idx)-ghosts
                 pos = dict(zip(xvals, pos))
                 eq = dP0.xreplace(pos)
-
-                for offset in it.product( range(-ghosts, ghosts+1), repeat=dim):
-                    fidx = tuple(np.asarray(idx)+np.asarray(offset))
-                    sidx = tuple(np.asarray(offset)+ghosts)
+                
+                for offset in it.product(*tuple(range(-gi, gi+1) for gi in ghosts)):
+                    fidx = tuple(np.add(idx, offset))
+                    sidx = tuple(np.add(offset, ghosts))
                     eq -= fvals[fidx]*stencil[sidx]
                 
                 eqs.append(eq)
@@ -434,7 +470,7 @@ class PolynomialInterpolator(object):
             print '\nBuilding matrix...'
         M = Ainv.dot(B)
         self.M = M
-        update_cache(self.cache_file(), self.key, M)
+        update_cache(self.cache_file(), self.key, (P0,S,M))
         
 
     def interpolate(self, fvals):
@@ -458,7 +494,7 @@ class PolynomialSubgridInterpolator(object):
         ----------
         interpolator: PolynomialInterpolator
             Interpolant used to compute weights.
-        grid_ratio: tuple of integers
+        grid_ratio: tuple of int
             Tuple of integers representing the ratio between the coarse and the fine grid.
         dtype: np.dtype
             Force to cast dtype for all matrices (interpolator.M may contain rationals).
@@ -467,14 +503,15 @@ class PolynomialSubgridInterpolator(object):
         ----------
         dim: int
             Number of dimensions to interpolate (same as interpolator.dim).
-        ghosts: int
+        ghosts: tuple of int
             Number of required ghosts.
-        n: int
+        n: tuple of int
             Corresponds to 2*(ghosts+1), the number of required nodes to generate the
             polynomial coefficients (same as interpolator.n). 
         N: int
             Total number of input nodes N including ghosts (same as interpolator.N).
-        s: int
+            N = n0*n1*...*n[dim-1]
+        s: tuple of int
             Corresponds to grid_ratio + 1, number of points of the subgrid in each directions.
             Example for a grid ratio=(3,3), we have s=(4,4): O=coarse grid nodes, X=fine grid nodes
             
@@ -489,10 +526,48 @@ class PolynomialSubgridInterpolator(object):
         S: int
             Represents the number of fine grid points contained in a coarse grid cell.
             S = s0*s1*...*s[dim-1]
+        gr: tuple of int
+            Corresponds to grid_ratio, number of points of the subgrid in each directions, minus one.
+            Example for a grid ratio=(3,3), we have gr=(3,3) and s=(4,4): 
+               O=coarse grid nodes, X=fine grid nodes, -=excluded find grid nodes
+            
+            Coarse grid:               Fine grid:
+
+           ^  O-----O                  ^  O X X O  ^
+           |  |     |              gr0 |  X X X -  | s0
+         1 |  |     |                  v  X X X -  |
+           v  O-----O                     O - - O  v
+              <----->                     <--->
+                 1                         gr1
+        GR: int
+            Represents the number of fine grid points contained in a coarse grid cell exluding right most points.
+            GR = gr0*gr1*...*gr[dim-1]
         W: np.ndarray
-            Pre computed weights to interpolate directly from coarse to fine grid.
+                Pre computed weights to interpolate directly from coarse to fine grid.
+                Let F be the vector of N known coarse grid node values (including required ghosts).
+                Let G be the vector of S unknown fine grid node values.
+                
+                         N                 
+                    <--------->
+                    X X X X X X ^                                         |g0| ^
+                    X X X X X X |                |f0| ^                   |g1| |
+                    X X X X X X |                |f1| |                   |g2| |
+                    X X X X X X |                |f2| |                   |g3| |
+                W = X X X X X X | S           F= |f3| | N       G = W*F = |g4| | S
+                    X X X X X X |                |f4| |                   |g5| |
+                    X X X X X X |                |f5| v                   |g6| |
+                    X X X X X X |                                         |g7| |
+                    X X X X X X v                                         |g8| v
+
+                Will contain the same data type as intepolator.M if dtype is not passed,
+                else W will be computed from user given dtype.
+
+        Wr: np.ndarray
+            Reduced W that exludes rightmost output points of ndimensional output vector.
+                
+            Pre computed weights to interpolate directly from coarse to inner fine grid.
             Let F be the vector of N known coarse grid node values (including required ghosts).
-            Let G be the vector of S unknown fine grid node values.
+            Let G be the vector of GR unknown fine inner grid node values (see gr attribute).
             
                      N                 
                 <--------->
@@ -500,33 +575,33 @@ class PolynomialSubgridInterpolator(object):
                 X X X X X X |                |f0| ^                   |g1| |
                 X X X X X X |                |f1| |                   |g2| |
                 X X X X X X |                |f2| |                   |g3| |
-            W = X X X X X X | S           F= |f3| | N       G = W*F = |g3| | S
-                X X X X X X |                |f4| |                   |g3| |
-                X X X X X X |                |f5| v                   |g3| |
-                X X X X X X |                                         |g3| |
-                X X X X X X v                                         |g4| v
-
-            Will contain the same data type as intepolator.M if dtype is not passed,
-            else W will be computed from user given dtype.
-
-        U: tuple
-            Contains unique values contained in W.
-        I: np.ndarray of np.int8 or np.int16
-            Represents W as integers indexed into U.
+           Wr = X X X X X X | GR          F= |f3| | N       G = W*F = |g4| | GR
+                X X X X X X |                |f4| |                   |g5| |
+                X X X X X X |                |f5| v                   |g6| |
+                X X X X X X |                                         |g7| |
+                X X X X X X v                                         |g8| v
+
+            Same data type as W.
         """
         check_instance(interpolator, PolynomialInterpolator)
         check_instance(grid_ratio, tuple, size=interpolator.dim, minval=1)
 
         p = interpolator.p
-        P = interpolator.P
         n = interpolator.n
+        P = interpolator.P
         N = interpolator.N
+        M = interpolator.M
         ghosts = interpolator.ghosts
+
+        gr = grid_ratio
+        GR = np.prod(gr, dtype=np.int32)
+        del grid_ratio
         
-        s = tuple(gr+1 for gr in grid_ratio)
-        S = np.prod(s, dtype=np.int64)
+        s = tuple(gri+1 for gri in gr)
+        S = np.prod(s, dtype=np.int32)
+
         dim = interpolator.dim
-        key = ('PolynomialSubgridInterpolator', interpolator.key, grid_ratio, str(dtype))
+        key = ('PolynomialSubgridInterpolator', interpolator.key, gr, str(dtype))
         
         self.p = p
         self.P = p
@@ -534,60 +609,48 @@ class PolynomialSubgridInterpolator(object):
         self.S = S
         self.n = n
         self.N = N
+        self.gr = gr
+        self.GR = GR
         self.ghosts = interpolator.ghosts
         self.dim = dim
         self.interpolator = interpolator
         self.key = key
         
         cache_file = interpolator.cache_file()
-        data = load_data_from_cache(cache_file, key)
-        if (data is not None) and _check_matrices(*data):
-            (W,U,I) = data
-        else:
-            X = tuple(np.asarray(tuple(sm.Rational(j,gr) for j in xrange(0,si))) for i,(gr,si) in enumerate(zip(grid_ratio, s)))
-            M = interpolator.M
-            V = np.vander(X[0], N=p, increasing=True)
-            for i in xrange(1, dim):
-                Vi = np.vander(X[i], N=p, increasing=True)
-                V = np.multiply.outer(V,Vi)
-            
-            even_axes = range(0,V.ndim,2)
-            odd_axes  = range(1,V.ndim,2)
-            axes = even_axes + odd_axes
-
-            V = np.transpose(V, axes=axes).copy()
-            assert V.shape[:dim] == s
-            assert V.shape[dim:] == (p,)*dim
-            V = V.reshape((S,P))
-            W = V.dot(M)
-            assert W.shape == (S,N)
-
-            if interpolator.approximative:
-                U = None
-                I = None
-            else:
-                U = list(sorted(set(W.ravel().tolist())))
-                U.remove(0)
-                U.remove(1)
-                U = (0,1,)+tuple(U)
-
-                if len(U)<np.iinfo(np.uint8).max:
-                    itype = np.uint8
-                elif len(U)<np.iinfo(np.uint16).max:
-                    itype = np.uint16
-                else:
-                    raise NotImplementedError(len(U))
-
-                I = np.empty(shape=W.shape, dtype=itype)
-                for idx in np.ndindex(*W.shape):
-                    I[idx] = U.index(W[idx])
-
-            if (dtype is not None):
-                W = W.astype(dtype)
-            update_cache(cache_file, key, (W,U,I))
+        try:
+            data = load_data_from_cache(cache_file, key)
+            if (data is not None):
+                W = data
+                if _check_matrix(W):
+                    self.W = data
+                    return
+        except Exception as e:
+            msg='Failed to load data from cache because:\n{}'.format(e)
+            warnings.warn(msg, HysopCacheWarning)
+
+        X = tuple(np.asarray(tuple(sm.Rational(j,gr) for j in xrange(0,si))) 
+                for i,(gr,si) in enumerate(zip(gr, s)))
+        V = np.vander(X[0], N=p[0], increasing=True)
+        for i in xrange(1, dim):
+            Vi = np.vander(X[i], N=p[i], increasing=True)
+            V = np.multiply.outer(V,Vi)
+        
+        even_axes = range(0,V.ndim,2)
+        odd_axes  = range(1,V.ndim,2)
+        axes = even_axes + odd_axes
+
+        V = np.transpose(V, axes=axes).copy()
+        assert V.shape[:dim] == s
+        assert V.shape[dim:] == p
+        V = V.reshape((S,P))
+        W = V.dot(M)
+        assert W.shape == (S,N)
+
+        if (dtype is not None):
+            W = W.astype(dtype)
+
+        update_cache(cache_file, key, W)
         self.W = W
-        self.U = U
-        self.I = I
 
     def __call__(self, F):
         return self.W.dot(F.ravel()).reshape(self.s)
@@ -595,6 +658,13 @@ class PolynomialSubgridInterpolator(object):
     def generate_subgrid_restrictor(self):
         return PolynomialSubgridRestrictor(subgrid_interpolator=self)
 
+    @property
+    def Wr(self):
+        assert self.W.shape == (self.S, self.N)
+        view = (slice(0,-1,None),)*self.dim + (slice(None, None, None),)*self.dim
+        Wr = self.W.reshape(self.s+self.n)[view].reshape(self.GR, self.N).copy()
+        return Wr
+
 
 class PolynomialSubgridRestrictor(object):
     def __init__(self, subgrid_interpolator):
@@ -677,6 +747,31 @@ if __name__ == '__main__':
     print 'Biquintic (FDC2)'
     print GI1(F)
     print
+    
+    F = [[0,1,1,0], 
+         [0,1,1,0]]
+    F = np.asarray(F)
+    
+    print 'Solving linear/cubic...'
+    PI0 = PolynomialInterpolator(dim=2, deg=(1,3), fd=2, verbose=False)
+    GI0 = PI0.generate_subgrid_interpolator(grid_ratio=grid_ratio, dtype=np.float64)
+    print 'Linear/Cubic (FDC2)'
+    print GI0(F)
+    print
+    
+    F = [[0,0],
+         [1,1], 
+         [1,1],
+         [0,0]]
+    F = np.asarray(F)
+    
+    print 'Solving cubic/linear...'
+    PI0 = PolynomialInterpolator(dim=2, deg=(3,1), fd=2, verbose=False)
+    GI0 = PI0.generate_subgrid_interpolator(grid_ratio=grid_ratio, dtype=np.float64)
+    print 'Cubic/Linear (FDC2)'
+    print GI0(F)
+    print
+    
 
     F = [[0,0,0,0,0,0],
          [0,0,0,0,0,0],
@@ -714,6 +809,20 @@ if __name__ == '__main__':
     print GI3(F)
     print
     
+    print 'Solving septic2/nonic2 ...'
+    PI4 = PolynomialInterpolator(dim=2, deg=(7,9), fd=2, verbose=False, approximative=(not has_flint))
+    GI4 = PI4.generate_subgrid_interpolator(grid_ratio=grid_ratio, dtype=np.float64)
+    print 'septic/nonic (FDC2)'
+    print GI4(F)
+    print
+    
+    print 'Solving septic2/quintic4 ...'
+    PI5 = PolynomialInterpolator(dim=2, deg=(7,5), fd=(2,4), verbose=False, approximative=(not has_flint))
+    GI5 = PI5.generate_subgrid_interpolator(grid_ratio=grid_ratio, dtype=np.float64)
+    print 'septic/nonic (FDC2/FDC4)'
+    print GI5(F)
+    print
+    
     # 3D test
     grid_ratio = (2,2,2)
     print 'Solving trilinear...'
diff --git a/hysop/operator/base/spatial_filtering.py b/hysop/operator/base/spatial_filtering.py
index 401a02e1c51291318d120876447ed5e5affff740..a73b8e975113cbcd89644d4360d11e87ad72f855 100644
--- a/hysop/operator/base/spatial_filtering.py
+++ b/hysop/operator/base/spatial_filtering.py
@@ -2,6 +2,7 @@
 @file advection.py
 RestrictionFilter operator generator.
 """
+import numpy as np
 from hysop.constants import Implementation
 from hysop.methods import Remesh
 from hysop.numerics.remesh.remesh import RemeshKernel
@@ -39,11 +40,17 @@ class SpatialFilterBase(object):
                 input_fields={input_field: input_topo},
                 output_fields={output_field: output_topo},
                 **kwds)
-        assert (input_field.dim == output_field.dim)
-        self.Fin   = input_field
-        self.Fout  = output_field
-        self.dim   = input_field.dim
-        self.dtype = find_common_dtype(input_field.dtype, output_field.dtype)
+
+        Fin = input_field
+        Fout = output_field
+        assert (Fin.dim == Fout.dim)
+        assert (Fin.lboundaries == Fout.lboundaries).all()
+        assert (Fin.rboundaries == Fout.rboundaries).all()
+        assert (Fin.periodicity == Fout.periodicity).all()
+        self.Fin   = Fin
+        self.Fout  = Fout
+        self.dim   = Fin.dim
+        self.dtype = find_common_dtype(Fin.dtype, Fout.dtype)
         self.iratio     = None # will be set in get_field_requirements
         self.grid_ratio = None # will be set in discretize
     
@@ -58,6 +65,7 @@ class SpatialFilterBase(object):
         self.dFin  = dFin
         self.dFout = dFout
         self.grid_ratio = grid_ratio
+        self.iter_shape = self.dFin.compute_resolution + 1 - self.dFin.periodicity
     
     @classmethod
     def supports_multiple_field_topologies(cls):
@@ -381,7 +389,7 @@ class PolynomialInterpolationFilterBase(PolynomialInterpolationMethod, Interpola
     @debug
     def get_field_requirements(self):
         reqs = super(PolynomialInterpolationFilterBase, self).get_field_requirements()
-        required_input_ghosts = (self.polynomial_interpolator.ghosts+1,)*self.dim
+        required_input_ghosts = np.add(self.polynomial_interpolator.ghosts, self.Fin.periodicity)
         Fin_topo, Fin_requirements = reqs.get_input_requirement(self.Fin)
         Fin_requirements.min_ghosts = required_input_ghosts
         self.required_input_ghosts = required_input_ghosts
@@ -392,8 +400,9 @@ class PolynomialInterpolationFilterBase(PolynomialInterpolationMethod, Interpola
             return
         super(PolynomialInterpolationFilterBase, self).discretize()
         dFin, dFout = self.dFin, self.dFout
+        ghosts = self.dFin.topology_state.transposed(self.required_input_ghosts)
         psi = self.polynomial_interpolator.generate_subgrid_interpolator(grid_ratio=self.grid_ratio)
         self.subgrid_interpolator = psi
-        self.fin  = dFin.sdata[dFin.local_slices(ghosts=self.required_input_ghosts)].handle
+        self.fin  = dFin.sdata[dFin.local_slices(ghosts=ghosts)].handle
         self.fout = dFout.sdata[dFout.compute_slices].handle
 
diff --git a/hysop/tools/warning.py b/hysop/tools/warning.py
index 18da7991bf9052b22bed8b5c2ebf7bb06329e547..cb040bf5b18d67b0b8e576daff91d4ad4e0cd136 100644
--- a/hysop/tools/warning.py
+++ b/hysop/tools/warning.py
@@ -17,6 +17,12 @@ class HysopPerformanceWarning(HysopWarning):
     """
     pass
 
+class HysopCacheWarning(HysopWarning):
+    """
+    Custom warning class for hysop caching.
+    """
+    pass
+
 def configure_hysop_warnings(action):
     """ 
     Configure hysop warnings.