diff --git a/docs/sphinx/users_guide/remeshing_and_interpolation.rst b/docs/sphinx/users_guide/remeshing_and_interpolation.rst
index 723586743c63e113d6d4af802488f77a10051a41..a5d69e721a8901b367dded4e11d4d3c5a9dc3398 100644
--- a/docs/sphinx/users_guide/remeshing_and_interpolation.rst
+++ b/docs/sphinx/users_guide/remeshing_and_interpolation.rst
@@ -4,6 +4,14 @@
 Interpolation and remeshing methods
 ===================================
 
+Interpolation
+-------------
+
+Use :class:`~hysop.numerics.interpolation.Linear to interpolate a field (scalar or vector)
+from one mesh to another.
+
+
+:class:`~hysop.numerics.interpolation.Interpolation
 
 **What**: interpolation of a field, defined as a numpy array from one "grid" to another.
 
@@ -16,7 +24,11 @@ To interpolate a field, the following inputs are required:
 
 
 
+* requirements for position parameter
 
+  * :math:`shape[d] == topo_target.mesh.resolution[d] \forall d \neq idir` where idir is the interpolation direction.
+  * :math:`shape[idir] \leq topo_target.mesh.compute_resolution[d]`
+  
 
 
 Devel notes
diff --git a/hysop/mpi/topology.py b/hysop/mpi/topology.py
index ed8dd66856d6dedd9f2e51903ab2dc8b53127ab6..1ec4759fa007d7b91975c7010173db1067183d23 100644
--- a/hysop/mpi/topology.py
+++ b/hysop/mpi/topology.py
@@ -155,9 +155,17 @@ class Cartesian(object):
         # previous (resp. next) neighbour in direction i
         # (warning : direction in the grid of process).
         self.neighbours = npw.dim_zeros((2, self.dimension))
+        self.neighbours_tmp = []
+        for direction in range(self.domain.dimension):
+            self.neighbours_tmp.append(self.comm.Shift(direction, 1))
+        self.distributed_dirs = tuple([d for d in xrange(self.domain.dimension)
+                                       if self.neighbours_tmp[d][0]
+                                       is not self.rank])
+        self.bc_dirs = tuple([d for d in xrange(self.domain.dimension)
+                              if d not in self.distributed_dirs])
+
         for direction in range(self.dimension):
             self.neighbours[:, direction] = self.comm.Shift(direction, 1)
-
         # ===== 4 - Computation of the local meshes =====
 
         # Local mesh on the current mpi process.
@@ -475,6 +483,8 @@ class Cartesian(object):
         s += str(self._mpis.rank) + '.\n'
         s += '- neighbours coordinates : \n'
         s += str(self.neighbours) + '\n'
+        s += '- FULL neighbours coordinates : \n'
+        s += str(self.neighbours_tmp) + '\n'
         s += str(self.mesh)
         s += '\n=================================\n'
         return s
diff --git a/hysop/numerics/interpolation.py b/hysop/numerics/interpolation.py
index b77f333da4ede19c20b504027158650ab11a9ddb..505d0a4ae0a097394e587357413f9d2b9284c17e 100644
--- a/hysop/numerics/interpolation.py
+++ b/hysop/numerics/interpolation.py
@@ -190,9 +190,9 @@ class Interpolation(object):
             except in direction of interpolation where any number of points
             is allowed, with a max of points equal to the number of grid
             points in this direction (excluding ghosts).
-        work : list of numpy arrays.
-            Used as local buffer. work[0] must be of the same shape
-            as positions. work[0] contains iy in return,
+        work : numpy array.
+            Used as local buffer. work must be of the same shape
+            as positions and contains iy in return,
             i_y = (xp - xg)/dx
 
         Returns
@@ -212,15 +212,16 @@ class Interpolation(object):
         """
         # local origin and space step in current direction
         x0 = self.topo_source.mesh.origin[self.direction]
-        i_y = work[0]
-        assert npw.arrays_share_data(i_y, self._rwork[0])
+        #i_y = work
+        # memory optim: check if input work 'belongs' to rwork
+        #assert npw.arrays_share_data(i_y, work)
         # -- set indices of closest grid points, on the left --
         # Initialized with source grid coordinates
         left_points_indices = list(self._icoords)
         # = [self._icoords[i] for i in xrange(dimension)]
         # pick memory from self._iwork
         iwork_dir = WorkSpaceTools.check_work_array(
-            1, i_y[...].shape, self._iwork, data_type=HYSOP_INTEGER)
+            1, work.shape, self._iwork, data_type=HYSOP_INTEGER)
         # we need an array (filled later) for current direction
         left_points_indices[self.direction] = iwork_dir[0]
         # check memory consumption
@@ -228,13 +229,13 @@ class Interpolation(object):
                                      self._iwork[0])
         # -- compute distance between particles positions and
         # closest grid point, on the left --
-        i_y[...] = (positions[self._target_indices] - x0) *\
+        work[...] = (positions[self._target_indices] - x0) *\
             self._inv_dx[self.direction]
         #self._assert_particles_roundoff(work, self._target_indices)
         # update interpolation point indices (in self._iwork)
         left_points_indices[self.direction][...] = \
-            npw.asintarray(np.floor(i_y[...]))
-        i_y[...] -= left_points_indices[self.direction][...]
+            npw.asintarray(np.floor(work[...]))
+        work[...] -= left_points_indices[self.direction][...]
         nmax = self.topo_source.mesh.resolution[self.direction]
         # Ensure all points belongs to local domain
         # (including ghosts)
@@ -474,8 +475,8 @@ class Linear(Interpolation):
         Notes
         -----
         * since interpolation function is likely to be used
-        as right-hand side of an ode solver, the argument slist must fit with
-        the integrator requirements, which explains the 't' argument
+        as right-hand side of an ode solver, the list of arguments must fit
+        with the integrator requirements, which explains the 't' argument
         unused below but required, or the positions and result as lists.
         """
         assert isinstance(positions, list)
@@ -500,16 +501,24 @@ class Linear(Interpolation):
         # pick buffer memory from self._rwork
         # wk[0] shares memory with rwork[0] but data are rearranged
         # according to positions[0].shape.
+        print nb_points, self.direction
         wk = WorkSpaceTools.check_work_array(
             1, positions[0][self._target_indices].shape, self._rwork)
-        wk.append(result[0])
-        left_points_indices = self._compute_iy(positions[0], wk)
+        # memory optim: check if input work 'belongs' to rwork
+        assert npw.arrays_share_data(self._rwork[0], wk[0])
+        left_points_indices = self._compute_iy(positions[0], wk[0])
         assert npw.arrays_share_data(left_points_indices[self.direction],
                                      self._iwork[0])
-        wk[1][self._target_indices] = \
+        print self.interpolated_field[left_points_indices].shape
+        #refsize = positions[0].size
+        #refshape = positions[0].shape
+        #wk.append(result[0][])
+        #res = WorkSpaceTools.check_work_array(1, refshape, result)
+        result[0][self._target_indices] = \
             self.interpolated_field[left_points_indices] * (1. - wk[0][...])
         # compute 'right points' indices
         left_points_indices[self.direction] += 1
-        wk[1][self._target_indices] += \
+
+        result[0][self._target_indices] += \
             self.interpolated_field[left_points_indices] * wk[0][...]
         return result
diff --git a/hysop/numerics/odesolvers.py b/hysop/numerics/odesolvers.py
index 33ab3027f8ba17c23dd58698277fa8252c1b19b0..79741682d402582df901f69bc959aafebcfe3547 100755
--- a/hysop/numerics/odesolvers.py
+++ b/hysop/numerics/odesolvers.py
@@ -31,7 +31,7 @@ class ODESolver(object):
     __metaclass__ = ABCMeta
 
     def __init__(self, nb_components, topo, f=None, indices=None,
-                 rwork=None, optim=None):
+                 rwork=None, optim=None, wk_shape=None, need_synchro=False):
         """
         Parameters
         ----------
@@ -56,6 +56,13 @@ class ODESolver(object):
         optim : int, optional
             the level of optimization (memory management).
             Default = None.
+        wk_shape: tuple, optional
+            shape of the internal work vector. Default = input topo
+            resolution.
+        need_synchro: boolean, optional
+            Set to true if y parameter of the rhs needs an update
+            of its ghost points before each call. This might be
+            the case if rhs is an interpolation. Default=False
 
         Notes
         -----
@@ -65,6 +72,9 @@ class ODESolver(object):
         system is solved. This is the default behavior
         * optim = WITH_GUESS : in that case, work must contain a first
         evaluation of f(t,y) before each call.
+        * wk_shape might be usefull when the advected field shape
+        in advection dir is lower than topo shape (for instance when
+        pushing particles with a threshhold parameter).
 
         """
         # RHS.
@@ -88,19 +98,21 @@ class ODESolver(object):
         #self.output_indices = None
 
         # work arrays
-        self.rwork = self._set_work_arrays(topo, rwork=rwork)
-        self._synchronize = UpdateGhosts(topo, self._nb_components)
+        self.rwork = self._set_work_arrays(topo, wk_shape, rwork=rwork)
+        if need_synchro:
+            self._synchronize = UpdateGhosts(topo, self._nb_components)
+            self.compute_rhs = self._update_rhs_with_synchro
+        else:
+            self.compute_rhs = self.f
 
-    def _set_work_arrays(self, topo, reduce_output_shape=False, rwork=None):
+    def _set_work_arrays(self, topo, wk_shape=False, rwork=None):
         """Check and allocate internal work buffers.
         """
         wk_prop = self.get_work_properties(self._nb_components, topo)['rwork']
         if wk_prop is None:
             return []
-        if reduce_output_shape:
-            subsize = np.asarray([self.in_indices[i].stop -
-                                  self.in_indices[i].start
-                                  for i in xrange(len(self.in_indices))])
+        if wk_shape:
+            subsize = wk_shape
         else:
             subsize = topo.mesh.resolution
 
@@ -118,6 +130,19 @@ class ODESolver(object):
             topology on which the integrator will be applied.
         """
 
+    # def update_rhs_local(self, t, y, result):
+    #     """Return rhs value, no synchronization between mpi domains.
+    #     """
+    #     return self.f(t, y, result)
+
+    def _update_rhs_with_synchro(self, t, y, result):
+        """"Return rhs value, but first
+        synchronize y values between mpi domains
+        (i.e. ghost points update)
+        """
+        self._synchronize(y)
+        return self.f(t, y, result)
+
     def __call__(self, t, y, dt, result):
         """Apply integrator
 
@@ -161,7 +186,7 @@ class ODESolver(object):
         result may be equal to y.
         """
         self.rwork[:self._nb_components] = \
-            self.f(t, y, self.rwork[:self._nb_components])
+            self.compute_rhs(t, y, self.rwork[:self._nb_components])
         return self._core(t, y, dt, result)
 
     @abstractmethod
@@ -295,7 +320,7 @@ class RK2(ODESolver):
         Note : since result may be equal to y, it can not
         be used as a temporary workspace.
         """
-        ic = self.in_indices
+        i_y = self.in_indices
         for i in xrange(self._nb_components):
             for j in xrange(len(self.rwork)):
                 assert not npw.arrays_share_data(y[i], self.rwork[j])
@@ -308,19 +333,20 @@ class RK2(ODESolver):
         # result = y + dt * k2
 
         # yn
+        #print ic, y[0].shape
         for i in xrange(self._nb_components):
-            np.add(y[i][ic], work0[i] * 0.5 * dt, yn[i])
+            np.add(y[i][i_y], work0[i] * 0.5 * dt, yn[i])
+            #np.add(y[i][ic], work0[i] * 0.5 * dt, yn[i])
 
-        # Update ghosts
-        self._synchronize.apply(yn)
         # k2 in work0
-        work0 = self.f(t + 0.5 * dt, yn, work0)
+        work0 = self.compute_rhs(t + 0.5 * dt, yn, work0)
         # *= dt
         for i in xrange(self._nb_components):
             #np.multiply(work0[i], dt, work0[i])
             work0[i] *= dt
             # result = y + work0
-            np.add(work0[i], y[i][ic], result[i])
+            np.add(work0[i], y[i][i_y], result[i][i_y])
+            #np.add(work0[i], y[i][ic], result[i])
 
         return result
 
@@ -396,18 +422,14 @@ class RK3(ODESolver):
         for i in xrange(self._nb_components):
             np.add(y[i], work0[i] * dt / 3, yn[i])
 
-        # Update ghosts
-        self._synchronize.apply(yn)
         # k2 in kn
-        kn = self.f(t + dt / 3, yn, kn)
+        kn = self.compute_rhs(t + dt / 3, yn, kn)
         # yn
         for i in xrange(self._nb_components):
             np.add(y[i], 2 * dt / 3 * kn[i], yn[i])
 
-        # Update ghosts
-        self._synchronize.apply(yn)
         # k3 in kn
-        kn = self.f(t + 2 * dt / 3, yn, kn)
+        kn = self.compute_rhs(t + 2 * dt / 3, yn, kn)
         # k1 + 3 * k3 in work0
         for i in xrange(self._nb_components):
             np.add(work0[i], 3 * kn[i], work0[i])
@@ -483,10 +505,8 @@ class RK4(ODESolver):
         for i in xrange(self._nb_components):
             np.add(y[i], work0[i] * dt / 2, yn[i])
 
-        # Update ghosts
-        self._synchronize.apply(yn)
         # k2 in kn
-        kn = self.f(t + dt / 2, yn, kn)
+        kn = self.compute_rhs(t + dt / 2, yn, kn)
 
         # k1 + 2 * k2 in work0
         for i in xrange(self._nb_components):
@@ -494,10 +514,8 @@ class RK4(ODESolver):
             # yn
             np.add(y[i], dt / 2 * kn[i], yn[i])
 
-        # Update ghosts
-        self._synchronize.apply(yn)
         # k3 in kn
-        kn = self.f(t + dt / 2, yn, kn)
+        kn = self.compute_rhs(t + dt / 2, yn, kn)
 
         # k1 + 2 * k2 + 2 * k3 in work0
         for i in xrange(self._nb_components):
@@ -505,10 +523,8 @@ class RK4(ODESolver):
             # yn
             np.add(y[i], dt * kn[i], yn[i])
 
-        # Update ghosts
-        self._synchronize.apply(yn)
         # K4 in kn
-        kn = self.f(t + dt, yn, kn)
+        kn = self.compute_rhs(t + dt, yn, kn)
 
         # k1 + 2 * k2 + 2 * k3 + k4
         for i in xrange(self._nb_components):
diff --git a/hysop/numerics/remeshing.py b/hysop/numerics/remeshing.py
index f63f52ecad8ad3c12fd7d33f80d78f27e0430a61..80e17ece4ffcf9840b7c85a27da4bacbfd9f1aa5 100644
--- a/hysop/numerics/remeshing.py
+++ b/hysop/numerics/remeshing.py
@@ -114,7 +114,8 @@ class Remeshing(Interpolation):
         # wk[0] --> iy, wk[1] --> used in weights accumulation below
         wk = WorkSpaceTools.check_work_array(
             2, ppos[self._target_indices].shape, self._rwork)
-        left_points_indices = self._compute_iy(ppos, wk)
+        assert npw.arrays_share_data(self._rwork[0], wk[0])
+        left_points_indices = self._compute_iy(ppos, wk[0])
         assert npw.arrays_share_data(left_points_indices[self.direction],
                                      self._iwork[0])
         # shift iwork, so that iwork[direction][p] is the index
diff --git a/hysop/numerics/tests/test_update_ghosts.py b/hysop/numerics/tests/test_update_ghosts.py
index b4cfaf2ee219b5729c2d26d594a25338f51f99a2..1a4a7fa9be3d7ccb097836593749d25fb7c03354 100755
--- a/hysop/numerics/tests/test_update_ghosts.py
+++ b/hysop/numerics/tests/test_update_ghosts.py
@@ -1,154 +1,267 @@
+"""Test ghost points synchronization
+"""
 from hysop.numerics.update_ghosts import UpdateGhosts, UpdateGhostsFull
 from hysop.domain.box import Box
 from hysop.fields.continuous import Field
 from hysop.tools.parameters import Discretization
+from hysop.mpi import main_rank
+import hysop.tools.numpywrappers as npw
 import numpy as np
 
 
-def _setup(ghosts, topo_dim):
-    dom = Box()
-
+def get_gh_slices(topo):
+    """Return indices of ghost points"""
+    tab = Field(topo.domain, name='temp')
+    tabd = tab.discretize(topo)[0]
+    tabd[...] = 0.
+    tabd[topo.mesh.compute_index] = 1.
+    return np.where(tabd == 0.)
+
+
+def get_ghosts_indices(topo):
+    """Return indices of points inside the ghost layers
+    for a given topology
+
+    gh_s[d] : for points at the beginning of the domain
+    in direction d.
+    gh_e[d] : for points at the end of the domain
+    in direction d.
+
+    Corner points are not included.
+    """
+    gh_s = []
+    gh_e = []
+    ghosts = topo.ghosts()
+    sl_start = [slice(0, ghosts[d]) for d in xrange(topo.domain.dimension)]
+    sl_end = [slice(-ghosts[d], None, None)
+              for d in xrange(topo.domain.dimension)]
+    for j in xrange(topo.domain.dimension):
+        gh_s.append(list(topo.mesh.compute_index))
+        gh_e.append(list(topo.mesh.compute_index))
+        gh_s[j][j] = sl_start[j]
+        gh_e[j][j] = sl_end[j]
+    return gh_s, gh_e
+
+
+def get_corners_indices(topo):
+    """Return indices of points inside the ghost layers
+    for a given topology
+
+    gh_s[d] : for points at the beginning of the domain
+    in direction d.
+    gh_e[d] : for points at the end of the domain
+    in direction d.
+
+    Corner points are included.
+    """
+    gh_s = []
+    gh_e = []
+    ghosts = topo.ghosts()
+    sl_start = [slice(0, ghosts[d]) for d in xrange(topo.domain.dimension)]
+    sl_end = [slice(-ghosts[d], None, None)
+              for d in xrange(topo.domain.dimension)]
+    for j in xrange(topo.domain.dimension):
+        gh_s.append(list(topo.mesh.local_index))
+        gh_e.append(list(topo.mesh.local_index))
+        gh_s[j][j] = sl_start[j]
+        gh_e[j][j] = sl_end[j]
+
+    start, end = get_ghosts_indices(topo)
+    tab = np.zeros((topo.mesh.resolution), dtype=np.int16)
+    tab[topo.mesh.compute_index] = 1
+    for d in xrange(topo.domain.dimension):
+        tab[start[d]] = 1
+        tab[end[d]] = 1
+    corners = np.where(tab == 0)
+
+    corners = [list(topo.mesh.local_index), ] * (2 ** (topo.domain.dimension))
+
+    dimension = topo.domain.dimension
+    corners_size = 2 ** dimension
+    corners = [None, ] * corners_size
+    val = npw.int_zeros(corners_size)
+    if dimension == 1:
+        return [], 0.
+    elif dimension == 2:
+        corners[0] = [sl_start[0], sl_start[1]]
+        corners[1] = [sl_start[0], sl_end[1]]
+        corners[2] = [sl_end[0], sl_start[1]]
+        corners[3] = [sl_end[0], sl_end[1]]
+        val[0] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] - 1, topo.proc_coords[1] - 1])
+        val[1] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] - 1, topo.proc_coords[1] + 1])
+        val[2] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] + 1, topo.proc_coords[1] - 1])
+        val[3] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] + 1, topo.proc_coords[1] + 1])
+    elif dimension == 3:
+        corners[0] = [sl_start[0], sl_start[1], sl_start[2]]
+        corners[1] = [sl_start[0], sl_start[1], sl_end[2]]
+        corners[2] = [sl_start[0], sl_end[1], sl_start[2]]
+        corners[3] = [sl_start[0], sl_end[1], sl_end[2]]
+        corners[4] = [sl_end[0], sl_start[1], sl_start[2]]
+        corners[5] = [sl_end[0], sl_start[1], sl_end[2]]
+        corners[6] = [sl_end[0], sl_end[1], sl_start[2]]
+        corners[7] = [sl_end[0], sl_end[1], sl_end[2]]
+        val[0] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] - 1,
+             topo.proc_coords[1] - 1,
+             topo.proc_coords[2] - 1])
+        val[1] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] - 1,
+             topo.proc_coords[1] - 1,
+             topo.proc_coords[2] + 1])
+        val[2] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] - 1,
+             topo.proc_coords[1] + 1,
+             topo.proc_coords[2] - 1])
+        val[3] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] - 1,
+             topo.proc_coords[1] + 1,
+             topo.proc_coords[2] + 1])
+        val[4] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] + 1,
+             topo.proc_coords[1] - 1,
+             topo.proc_coords[2] - 1])
+        val[5] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] + 1,
+             topo.proc_coords[1] - 1,
+             topo.proc_coords[2] + 1])
+        val[6] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] + 1,
+             topo.proc_coords[1] + 1,
+             topo.proc_coords[2] - 1])
+        val[7] = topo.comm.Get_cart_rank(
+            [topo.proc_coords[0] + 1,
+             topo.proc_coords[1] + 1,
+             topo.proc_coords[2] + 1])
+
+#    print "youou ", corners
+    return corners, val
+
+
+def run_synchro(ghosts, cutdir=None, full=False):
+    """Build domain, fields and topology
+    for a given ghost layer size and
+    distributed directions.
+    Then call synchronization and check.
+    """
+    dimension = len(ghosts)
+    dom = Box(length=[1., ] * dimension)
     f = Field(dom, is_vector=True, name='f')
-    resolTopo = Discretization([33, 33, 33], ghosts=ghosts)
-    topo = dom.create_topology(resolTopo, dim=topo_dim)
-    f.discretize(topo)
-    df = f.discreteFields[topo]
-    for i, dfd in enumerate(df.data):
-        dfd[...] = 0.
-        dfd[topo.mesh.compute_index] = 1. * (i + 1)
-    return df, topo
-
-
-def verify(df, gh):
-    sli = (
-        (slice(0, gh[0]), slice(gh[1], -gh[1]), slice(gh[2], -gh[2])),
-        (slice(-gh[0], None), slice(gh[1], -gh[1]), slice(gh[2], -gh[2])),
-        (slice(gh[0], -gh[0]), slice(0, gh[1]), slice(gh[2], -gh[2])),
-        (slice(gh[0], -gh[0]), slice(-gh[1], None), slice(gh[2], -gh[2])),
-        (slice(gh[0], -gh[0]), slice(gh[1], -gh[1]), slice(0, gh[2])),
-        (slice(gh[0], -gh[0]), slice(gh[1], -gh[1]), slice(-gh[2], None)))
-    slni = (
-        (slice(0, gh[0]), slice(0, gh[1]), slice(None)),
-        (slice(0, gh[0]), slice(None), slice(0, gh[2])),
-        (slice(None), slice(0, gh[1]), slice(0, gh[2])),
-        (slice(-gh[0], None), slice(-gh[1], None), slice(None)),
-        (slice(-gh[0], None), slice(None), slice(-gh[2], None)),
-        (slice(None), slice(-gh[1], None), slice(-gh[2], None)))
-    for i, dfd in enumerate(df.data):
-        for s in sli:
-            assert np.max(dfd[s]) == np.min(dfd[s]) and \
-                np.max(dfd[s]) == 1. * (i + 1)
-        for s in slni:
-            assert np.max(dfd[s]) == np.min(dfd[s]) and \
-                np.max(dfd[s]) == 0.
-
-
-def verify_full(df, gh):
-    for i, dfd in enumerate(df.data):
-        for s in ((slice(0, gh[0]), slice(None), slice(None)),
-                  (slice(-gh[0], None), slice(None), slice(None)),
-                  (slice(None), slice(0, gh[1]), slice(None)),
-                  (slice(None), slice(-gh[1], None), slice(None)),
-                  (slice(None), slice(None), slice(0, gh[2])),
-                  (slice(None), slice(None), slice(-gh[2], None))):
-            assert np.max(dfd[s]) == np.min(dfd[s]) and \
-                np.max(dfd[s]) == 1. * (i + 1)
-        assert np.max(dfd) == np.min(dfd) and \
-            np.max(dfd) == 1. * (i + 1)
-
-
-def test_update_ghosts_simple_1D():
-    gh = [1, 1, 1]
-    df, topo, = _setup(gh, 1)
-    update = UpdateGhosts(topo, 3)
-    update(df.data)
-    verify(df, gh)
-
-
-def test_update_ghosts_simple_2D():
-    gh = [1, 1, 1]
-    df, topo, = _setup(gh, 2)
-    update = UpdateGhosts(topo, 3)
-    update(df.data)
-    verify(df, gh)
-
-
-def test_update_ghosts_simple_3D():
-    gh = [1, 1, 1]
-    df, topo, = _setup(gh, 3)
-    update = UpdateGhosts(topo, 3)
-    update(df.data)
-    verify(df, gh)
-
-
-def test_update_ghosts_1D():
-    gh = [2, 3, 4]
-    df, topo, = _setup(gh, 1)
-    update = UpdateGhosts(topo, 3)
-    update(df.data)
-    verify(df, gh)
-
-
-def test_update_ghosts_2D():
-    gh = [2, 3, 4]
-    df, topo, = _setup(gh, 2)
-    update = UpdateGhosts(topo, 3)
-    update(df.data)
-    verify(df, gh)
-
-
-def test_update_ghosts_3D():
-    gh = [2, 3, 4]
-    df, topo, = _setup(gh, 3)
-    update = UpdateGhosts(topo, 3)
-    update(df.data)
-    verify(df, gh)
-
-
-def test_update_ghosts_full_simple_1D():
-    gh = [1, 1, 1]
-    df, topo, = _setup(gh, 1)
-    update = UpdateGhostsFull(topo, 3)
-    update(df.data)
-    verify_full(df, gh)
-
-
-def test_update_ghosts_full_simple_2D():
-    gh = [1, 1, 1]
-    df, topo, = _setup(gh, 2)
-    update = UpdateGhostsFull(topo, 3)
-    update(df.data)
-    verify_full(df, gh)
-
-
-def test_update_ghosts_full_simple_3D():
-    gh = [1, 1, 1]
-    df, topo, = _setup(gh, 3)
-    update = UpdateGhostsFull(topo, 3)
-    update(df.data)
-    verify_full(df, gh)
-
-
-def test_update_ghosts_full_1D():
-    gh = [2, 3, 4]
-    df, topo, = _setup(gh, 1)
-    update = UpdateGhostsFull(topo, 3)
-    update(df.data)
-    verify_full(df, gh)
-
-
-def test_update_ghosts_full_2D():
-    gh = [2, 3, 4]
-    df, topo, = _setup(gh, 2)
-    update = UpdateGhostsFull(topo, 3)
-    update(df.data)
-    verify_full(df, gh)
-
-
-def test_update_ghosts_full_3D():
-    gh = [2, 3, 4]
-    df, topo, = _setup(gh, 3)
-    update = UpdateGhostsFull(topo, 3)
-    update(df.data)
-    verify_full(df, gh)
+    discr = Discretization([33, ] * dimension, ghosts=ghosts)
+    topo = dom.create_topology(discr, cutdir=cutdir)
+    df = f.discretize(topo)
+    # initialize + 0 in ghost layer
+    nbc = f.nb_components
+    for i in xrange(nbc):
+        df[i][...] = main_rank
+        #df[i][topo.mesh.compute_index] = topo.rank
+
+    for i in xrange(df.nb_components):
+        assert np.allclose(df[i], main_rank)
+
+    if full:
+        sync = UpdateGhostsFull(topo, nbc)
+
+    else:
+        sync = UpdateGhosts(topo, nbc)
+
+    start, end = get_ghosts_indices(topo)
+    corners, val = get_corners_indices(topo)
+
+    sync.apply(df.data)
+    nghb = topo.neighbours_tmp
+    for i in xrange(df.nb_components):
+        assert np.allclose(df[i][topo.mesh.compute_index], main_rank)
+        for d in xrange(topo.domain.dimension):
+            assert np.allclose(df[i][start[d]], nghb[d][0])
+            assert np.allclose(df[i][end[d]], nghb[d][1])
+        if full:
+            for d in xrange(len(corners)):
+                assert np.allclose(df[i][corners[d]], val[d])
+
+
+def test_1_1_simple():
+    """ 1D domain, 1D topo, simple update
+    """
+    run_synchro([1, ])
+
+
+def test_2_1_simple():
+    """ 2D domain, 1D topo, simple update
+    """
+    run_synchro([1, 1], [True, False])
+
+
+def test_2_2_simple():
+    """ 2D domain, 2D topo, simple update
+    """
+    run_synchro([1, 1])
+
+
+def test_3_1_simple():
+    """ 3D domain, 1D topo, simple update
+    """
+    run_synchro([1, 1, 1], [True, False, False])
+
+
+def test_3_2_simple():
+    """ 3D domain, 1D topo, simple update
+    """
+    run_synchro([1, 1, 1], [True, False, True])
+
+
+def test_3_3_simple():
+    """ 3D domain, 1D topo, simple update
+    """
+    run_synchro([1, 1, 1])
+
+
+def test_3_3_bis():
+    """ 3D domain, 3D topo, different ghost size in each dir
+    """
+    run_synchro([1, 3, 2])
+
+
+def test_1_1_full():
+    """ 1D domain, 1D topo, corners update
+    """
+    run_synchro([1, ], full=True)
+
+
+def test_2_1_full():
+    """ 2D domain, 1D topo, corners update
+    """
+    run_synchro([1, 1], [True, False], full=True)
+
+
+def test_2_2_full():
+    """ 2D domain, 2D topo, corners update
+    """
+    run_synchro([1, 1], full=True)
+
+
+def test_3_1_full():
+    """ 3D domain, 1D topo, corners update
+    """
+    run_synchro([1, 1, 1], [True, False, False], full=True)
+
+
+def test_3_2_full():
+    """ 3D domain, 2D topo, corners update
+    """
+    run_synchro([1, 1, 1], [True, False, True], full=True)
+
+
+def test_3_3_full():
+    """ 3D domain, 3D topo, corners update
+    """
+    run_synchro([1, 1, 1], full=True)
+
 
+def test_3_2_bis_full():
+    """ 3D domain, 2D topo, corners update, different ghost size in each dir
+    """
+    run_synchro([1, 3, 2], [True, False, True], full=True)
diff --git a/hysop/numerics/update_ghosts.py b/hysop/numerics/update_ghosts.py
index 7e6df40c2baca2ad51dc5c7da0d24a994ce42148..4f4a254df92b2bf722536917e7dcaccf77f6a85c 100644
--- a/hysop/numerics/update_ghosts.py
+++ b/hysop/numerics/update_ghosts.py
@@ -60,10 +60,12 @@ class UpdateGhosts(object):
         self._g_tonext = []
         # Indices of points to be sent to previous neighbour.
         self._g_toprevious = []
-        # Buffers that contains ghosts points to be sent (list, one per dir)
-        self._sendbuffer = []
-        # Buffers for reception (list, one per dir)
-        self._recvbuffer = []
+        # Buffers that contains ghosts points to be sent
+        self._sendbuffer = None
+        # List of sizes of buffer to be sent/recv in each direction
+        self._buffer_size = []
+        # Buffers for reception
+        self._recvbuffer = None
         # domain dimension
         self._dim = self.topology.domain.dimension
 
@@ -75,30 +77,47 @@ class UpdateGhosts(object):
         self._memoryblocksize = np.prod(self.memshape)
         # Number of numpy arrays that will be updated
         self.nb_elements = nb_elements
+
+        # list of functions, _apply[d] corresponds to the synchronization
+        # used in direction d, either
+        #  - local (non-distributed) --> apply BC
+        #  - distributed --> mpi comm
+        self._apply = [None, ] * self._dim
+        # distributed (mpi) directions
+        self.exchange_dir = []
+        if self.topology.size > 1:
+            self.exchange_dir = [d for d in xrange(self._dim)
+                                 if self.topology.cutdir[d]]
+        for d in self.exchange_dir:
+            self._apply[d] = self._apply_in_dir
+        for d in self.topology.bc_dirs:
+            self._apply[d] = self._apply_bc_in_dir
+
         if self.topology.size > 1:  # else no need to set buffers ...
             # Size computation below assumes that what we send in one
             # dir has the same size as what we receive from process in the
             # same dir ...
-            exchange_dir = [d for d in xrange(self._dim)
-                            if self.topology.cutdir[d]]
             # A temporary array used to calculate slices sizes
             temp = np.zeros(self.memshape, dtype=np.int8)
 
-            for d in exchange_dir:
+            for d in self.exchange_dir:
                 buffsize = 0
                 buffsize += temp[self._g_tonext[d]].size * self.nb_elements
-                self._sendbuffer.append(npw.zeros((buffsize)))
-                self._recvbuffer.append(npw.zeros((buffsize)))
+                self._buffer_size.append(buffsize)
+            self._recvbuffer = npw.zeros(max(self._buffer_size))
+            self._sendbuffer = npw.zeros(max(self._buffer_size))
 
     def _setup_slices(self):
         """Compute slices used to describe what to send
         and receive in ghosts points.
-
         """
+        # all points
         defslice = [slice(None, None, None)] * self._dim
+        # no points
         nogh_slice = [slice(0)] * self._dim
         for d in xrange(self._dim):
             if self.ghosts[d] > 0:
+                # get ghost points indices in current direction ...
                 self._g_fromprevious.append(list(defslice))
                 self._g_fromprevious[d][d] = slice(self.ghosts[d])
                 self._g_fromnext.append(list(defslice))
@@ -111,6 +130,8 @@ class UpdateGhosts(object):
                                              -self.ghosts[d])
 
                 other_dim = self._other_dim(d)
+                # ... and only compute points indices in other directions
+                # i.e. 'corner' points are not taken into account.
                 for d2 in other_dim:
                     self._g_fromprevious[d][d2] = slice(self.ghosts[d2],
                                                         -self.ghosts[d2])
@@ -121,12 +142,29 @@ class UpdateGhosts(object):
                     self._g_tonext[d][d2] = slice(self.ghosts[d2],
                                                   -self.ghosts[d2])
             else:
+                # empty lists of indices
                 self._g_fromprevious.append(list(nogh_slice))
                 self._g_fromnext.append(list(nogh_slice))
                 self._g_toprevious.append(list(nogh_slice))
                 self._g_tonext.append(list(nogh_slice))
 
+        self._g_fromprevious = self._immutable(self._g_fromprevious)
+        self._immutable(self._g_fromnext)
+        self._immutable(self._g_toprevious)
+        self._immutable(self._g_tonext)
+
+    @staticmethod
+    def _immutable(var):
+        """Convert list of lists to immutable tuples
+        """
+        assert isinstance(var, list)
+        for i in xrange(len(var)):
+            var[i] = tuple(var[i])
+        return tuple(var)
+
     def _other_dim(self, d):
+        """utility to get all directions except d
+        """
         return [x for x in xrange(self._dim) if x != d]
 
     def __call__(self, variables):
@@ -145,43 +183,71 @@ class UpdateGhosts(object):
         assert (self.topology.domain.boundaries == PERIODIC).all(),\
             'Only implemented for periodic boundary conditions.'
         assert isinstance(variables, list)
-        dirs = [d for d in xrange(self._dim)
-                if self.topology.shape[d] == 1]
+        dirs = self.topology.bc_dirs
         for d in dirs:
-            self._apply_bc_in_dir(variables, d)
+            self._apply_bc_in_dir(variables, d, 0)
 
-    def _apply_bc_in_dir(self, variables, d):
+    def _apply_bc_in_dir(self, variables, d, i):
         """Apply periodic boundary condition in direction d."""
+        assert (self.topology.domain.boundaries == PERIODIC).all(),\
+            'Only implemented for periodic boundary conditions.'
         for v in variables:
             assert v.shape == self.memshape
             v[self._g_fromprevious[d]] = v[self._g_tonext[d]]
             v[self._g_fromnext[d]] = v[self._g_toprevious[d]]
+        return i
+    # @debug
+    # def apply(self, variables):
+    #     """Update ghosts points values:
+    #     mpi communications and boundary conditions.
+
+    #     Parameters
+    #     ----------
+    #     variables : list of numpy arrays
+    #         arrays that must be synchronized.
+    #     """
+    #     assert isinstance(variables, list)
+    #     # Apply boundary conditions for distributed directions ...
+    #     i = 0
+    #     for d in self.exchange_dir:
+    #         self._apply_in_dir(variables, d, i)
+    #         # update position in buffers list
+    #         i += 1
+    #     #  ... and for non-distributed directions.
+    #     self.apply_bc(variables)
 
     @debug
     def apply(self, variables):
-        """
-        Compute ghosts values from mpi communications and boundary conditions.
+        """Update ghosts points values:
+        mpi communications and boundary conditions.
+
+        Parameters
+        ----------
+        variables : list of numpy arrays
+            arrays that must be synchronized.
         """
         assert isinstance(variables, list)
-        exchange_dir = []
-        if self.topology.size > 1:
-            exchange_dir = [d for d in xrange(self._dim)
-                            if self.topology.cutdir[d]]
+        # Apply boundary conditions all directions ...
         i = 0
-        for d in exchange_dir:
-            self._apply_in_dir(variables, d, i)
-            # update index in neighbours list
-            i += 1
-            # End of loop through send/recv directions.
-        # Apply boundary conditions for non-distributed directions
-        self.apply_bc(variables)
+        for d in xrange(self._dim):
+            i = self._apply[d](variables, d, i)
 
     def _apply_in_dir(self, variables, d, i):
         """Communicate ghosts values in direction d for neighbour
-        in direction i of the topology"""
+        in direction i of the topology
+
+        Parameters
+        ----------
+        variables : list of numpy arrays
+            arrays to be synchronized
+        d : int
+            direction of synchronization
+        i : int
+            counter, current position in buffers.
+        """
         comm = self.topology.comm
         rank = self.topology.rank
-        neighbours = self.topology.neighbours
+        neighbours = self.topology.neighbours_tmp[d]
         # 1 - Fill in buffers
         # Loop through all variables that are distributed
         pos = 0
@@ -189,15 +255,18 @@ class UpdateGhosts(object):
         for v in variables:
             assert v.shape == self.memshape
             nextpos += v[self._g_tonext[d]].size
-            self._sendbuffer[i][pos:nextpos] = v[self._g_tonext[d]].flat
+            self._sendbuffer[pos:nextpos] = v[self._g_tonext[d]].flat
             pos = nextpos
 
         # 2 - Send to next receive from previous
-        dest_rk = neighbours[1, i]
-        from_rk = neighbours[0, i]
-        comm.Sendrecv([self._sendbuffer[i], HYSOP_MPI_REAL],
+        dest_rk = neighbours[1]
+        from_rk = neighbours[0]
+        sendbuffer = self._sendbuffer[:self._buffer_size[i]]
+        recvbuffer = self._recvbuffer[:self._buffer_size[i]]
+
+        comm.Sendrecv([sendbuffer, HYSOP_MPI_REAL],
                       dest=dest_rk, sendtag=rank,
-                      recvbuf=self._recvbuffer[i],
+                      recvbuf=recvbuffer,
                       source=from_rk, recvtag=from_rk)
 
         # 3 - fill variables with recvbuffer and update sendbuffer
@@ -207,17 +276,17 @@ class UpdateGhosts(object):
         for v in variables:
             nextpos += v[self._g_fromprevious[d]].size
             v[self._g_fromprevious[d]].flat = \
-                self._recvbuffer[i][pos:nextpos]
-            self._sendbuffer[i][pos:nextpos] = \
+                self._recvbuffer[pos:nextpos]
+            self._sendbuffer[pos:nextpos] = \
                 v[self._g_toprevious[d]].flat
             pos = nextpos
 
         # 4 -Send to previous and receive from next
-        dest_rk = neighbours[0, i]
-        from_rk = neighbours[1, i]
-        comm.Sendrecv([self._sendbuffer[i], HYSOP_MPI_REAL],
+        dest_rk = neighbours[0]
+        from_rk = neighbours[1]
+        comm.Sendrecv([sendbuffer, HYSOP_MPI_REAL],
                       dest=dest_rk, sendtag=rank,
-                      recvbuf=self._recvbuffer[i],
+                      recvbuf=recvbuffer,
                       source=from_rk, recvtag=from_rk)
         # 5 - fill variables with recvbuffer
         pos = 0
@@ -225,9 +294,12 @@ class UpdateGhosts(object):
         for v in variables:
             nextpos += v[self._g_fromprevious[d]].size
             v[self._g_fromnext[d]].flat = \
-                self._recvbuffer[i][pos:nextpos]
+                self._recvbuffer[pos:nextpos]
             pos = nextpos
 
+        # update position in buffers list
+        return i + 1
+
 
 class UpdateGhostsFull(UpdateGhosts):
     """Ghost points synchronization for a list of numpy arrays
@@ -250,27 +322,22 @@ class UpdateGhostsFull(UpdateGhosts):
         #                if d == 0:
         return [x for x in xrange(self._dim) if x > d]
 
-    @debug
-    def apply(self, variables):
-        """Apply either mpi communications
-        or local boundary conditions to fill ghosts.
-        Loop over directions and switch among local BC or mpi comm.
-        """
-        assert isinstance(variables, list)
-        exchange_dir = []
-        if self.topology.size > 1:
-            exchange_dir = [d for d in xrange(self._dim)
-                            if self.topology.cutdir[d]]
-        local_bc_dir = [d for d in xrange(self._dim)
-                        if self.topology.shape[d] == 1]
-        assert len(exchange_dir) + len(local_bc_dir) == self._dim
-
-        i = 0
-        for d in xrange(self._dim):
-            if d in local_bc_dir:
-                self._apply_bc_in_dir(variables, d)
-            elif d in exchange_dir:
-                self._apply_in_dir(variables, d, i)
-                # update index in neighbours list
-                i += 1
-                # End of loop through send/recv directions.
+    # @debug
+    # def apply(self, variables):
+    #     """Apply either mpi communications
+    #     or local boundary conditions to fill ghosts.
+    #     Loop over directions and switch among local BC or mpi comm.
+    #     """
+    #     assert isinstance(variables, list)
+    #     local_bc_dir = self.topology.bc_dirs
+    #     assert len(self.exchange_dir) + len(local_bc_dir) == self._dim
+
+    #     i = 0
+    #     for d in xrange(self._dim):
+    #         if d in local_bc_dir:
+    #             self._apply_bc_in_dir(variables, d)
+    #         elif d in self.exchange_dir:
+    #             self._apply_in_dir(variables, d, i)
+    #             # update position in buffers list
+    #             i += 1
+    #             # End of loop through send/recv directions.
diff --git a/hysop/operator/discrete/particle_advection.py b/hysop/operator/discrete/particle_advection.py
index a8fb2be0945138cc3073879311e48c9aa5631266..d8cf10f58a5a6b4d3e0fc0d2bc0b13b68de07754 100644
--- a/hysop/operator/discrete/particle_advection.py
+++ b/hysop/operator/discrete/particle_advection.py
@@ -116,7 +116,7 @@ class ParticleAdvection(DiscreteOperator):
             self._rhs_size, rwork=self._rw_integ,
             f=num_interpolate,
             topo=topo_fields,
-            #optim=WITH_GUESS,
+            optim=WITH_GUESS,
             indices=ic)
 
         # -- remesh --
@@ -257,7 +257,7 @@ class ParticleAdvection(DiscreteOperator):
         # - ghost-synchro of the rhs (if needed) is done by the odesolver
         # - interpolation of velocity field from grid to particles is done
         # 'inside' the ode solver (rhs = interpolation scheme)
-        #self.num_advec.rwork[0][...] = self.velocity.data[self.direction][...]
+        self.num_advec.rwork[0][...] = self.velocity.data[self.direction][...]
         self.part_positions = self.num_advec(
             t, self.part_positions, dt, result=self.part_positions)