From 10b8ece7918702b94ffdfe1239b22cb9508eb28d Mon Sep 17 00:00:00 2001
From: Keck Jean-Baptiste <jean-baptiste.keck@imag.fr>
Date: Tue, 12 Sep 2017 23:59:08 +0200
Subject: [PATCH] fixed exchange ghosts

---
 .gitignore                                    |  1 +
 .../taylor_green/taylor_green_monoscale.py    | 17 ++--
 .../device/codegen/structs/mesh_info.py       |  4 +-
 .../operator/directional/advection_dir.py     | 29 ++++--
 hysop/core/mpi/redistribute.py                |  4 +-
 hysop/fields/cartesian_discrete_field.py      | 97 ++++++++++++++++---
 hysop/mesh/cartesian_mesh.py                  | 78 ++++++++++-----
 hysop/operator/adapt_timestep.py              | 19 ++--
 hysop/operator/hdf_io.py                      |  7 +-
 hysop/problem.py                              |  4 +-
 hysop/tools/io_utils.py                       |  4 +-
 hysop/topology/cartesian_topology.py          |  7 ++
 12 files changed, 202 insertions(+), 69 deletions(-)

diff --git a/.gitignore b/.gitignore
index 10c2abc5b..b86474991 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 *.pyc
+*.pyo
 *.so
 *.log
 hysop.egg-info/
diff --git a/examples/taylor_green/taylor_green_monoscale.py b/examples/taylor_green/taylor_green_monoscale.py
index 820ffed57..1424e7bd1 100644
--- a/examples/taylor_green/taylor_green_monoscale.py
+++ b/examples/taylor_green/taylor_green_monoscale.py
@@ -29,7 +29,7 @@ def init_velocity(data, coords, dim):
     elif dim==2:
         (x,y) = coords
         data[0][...] = +1.0
-        data[1][...] = -1.0
+        data[1][...] = -0.0
     else:
         raise NotImplementedError('dimension {}'.format(dim))
 
@@ -43,11 +43,11 @@ def init_vorticity(data, coords, dim):
         data[2][...] = 2. * sin(x) * sin(y) * cos(z)
     elif dim==2:
         (x,y) = coords
-        data[0][...] = cos(x)*sin(y)
+        data[0][...] = cos(x)*cos(y)
     else:
         raise NotImplementedError('dimension {}'.format(dim))
 
-def do_solve(npts, viscosity=1./1600., lcfl=0.125, cfl=2.0):
+def do_solve(npts, viscosity=1./1600., lcfl=0.125, cfl=0.5):
     autotuner_config = AutotunerConfig(autotuner_flag=AutotunerFlags.ESTIMATE, 
        prune_threshold=1.2, override_cache=True, verbose=0)
     kernel_config = KernelConfig(autotuner_config=autotuner_config)
@@ -68,7 +68,6 @@ def do_solve(npts, viscosity=1./1600., lcfl=0.125, cfl=2.0):
             variables = {velo: d3d, vorti: d3d},
             method = {Remesh: Remesh.L2_1},
         )
-    advec.dump_outputs(directions=1, name_postfix='_X', kill=True)
     if dim==3:
         stretch = DirectionalStretching(
                  name='stretch',
@@ -91,22 +90,20 @@ def do_solve(npts, viscosity=1./1600., lcfl=0.125, cfl=2.0):
                     method={KernelConfig: kernel_config})
     splitting.push_operators(advec)#, stretch)
 
-    simu = Simulation(start=0.0, end=10.0, dt0=2*pi/8.0)
+    simu = Simulation(start=0.0, end=10.0, dt0=0.01)
     adapt_dt = AdaptiveTimeStep(simu.dt, dt_coeff=0.95)
     adapt_dt.push_cfl_criteria(cfl,  velo)
     adapt_dt.push_lcfl_criteria(lcfl, vorti)
 
     problem = Problem(method={ComputeGranularity:0})
-    problem.insert(splitting, diffusion, poisson, adapt_dt)
+    problem.insert(splitting, adapt_dt)
     problem.dump_inputs()
     problem.build()
-    problem.display()
+    #problem.display()
 
     dfields = problem.get_input_discrete_fields()
     dfields[velo].initialize(formula=init_velocity, dim=dim)
     dfields[vorti].initialize(formula=init_vorticity, dim=dim)
-
-    print 'DX=', dfields[velo].mesh.space_step
     
     with printoptions(threshold=10000, linewidth=240, 
                       nanstr='nan', infstr='inf', 
@@ -115,6 +112,6 @@ def do_solve(npts, viscosity=1./1600., lcfl=0.125, cfl=2.0):
         problem.finalize()
 
 if __name__=='__main__':
-    N = 8 + 1
+    N = 64 + 1
     do_solve(N)
 
diff --git a/hysop/backend/device/codegen/structs/mesh_info.py b/hysop/backend/device/codegen/structs/mesh_info.py
index d7473278f..a04104a3d 100644
--- a/hysop/backend/device/codegen/structs/mesh_info.py
+++ b/hysop/backend/device/codegen/structs/mesh_info.py
@@ -229,8 +229,8 @@ class MeshInfoStruct(StructCodeGenerator):
          dx     = mesh.space_step[::-1]
          ghosts = mesh.ghosts[::-1]
 
-         gresolution         = mesh.global_resolution[::-1]
-         gcompute_resolution = mesh.global_resolution[::-1]
+         gresolution         = mesh.grid_resolution[::-1]
+         gcompute_resolution = mesh.grid_resolution[::-1]
          gxmin       = mesh.global_origin[::-1]
          gxmax       = mesh.global_end[::-1]
          gsize       = mesh.global_length[::-1]
diff --git a/hysop/backend/host/python/operator/directional/advection_dir.py b/hysop/backend/host/python/operator/directional/advection_dir.py
index 05bb27aaf..240148e3b 100644
--- a/hysop/backend/host/python/operator/directional/advection_dir.py
+++ b/hysop/backend/host/python/operator/directional/advection_dir.py
@@ -90,7 +90,7 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat
             lidx[...] %= N
         elif __debug__:
             min_lidx, max_lidx = lidx.min(), lidx.max()
-            M = Vin.shape[-1]-1 
+            M = Vin.shape[-1]
             assert min_lidx >= 0,  'index out of bounds: {} < 0'.format(min_lidx)
             assert max_lidx < M-1, 'index out of bounds: {} >= {}'.format(max_lidx, M-1)
         ridx[...] = lidx
@@ -222,9 +222,9 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat
         N = R0.shape[-1]
         
         # print 'IDX=\n{}'.format('\n'.join([str(x[0]) for x in tuple(mesh.iter_compute_axes(cr))]))
-        print 'INPUT'
-        print sinputs.values()[0][0][sinputs.values()[0].compute_slices]
-        print
+        #print 'INPUT'
+        #print sinputs.values()[0][0][sinputs.values()[0].compute_slices]
+        #print
         for (idx,_,_,_) in mesh.iter_compute_axes(cr):
             Pi = pos[idx].lock()
             R0[...]  = Pi
@@ -291,8 +291,25 @@ class PythonDirectionalAdvection(DirectionalAdvectionBase, HostDirectionalOperat
                             sout[...] = Si
                             # print 'SOUT \n', sout
                         sid+=1
-        print 'FINAL'
-        print soutputs.values()[0][0][soutputs.values()[0].compute_slices]
+
+        # exchange and sum remeshed ghosts
+        for sout in soutputs.values():
+            buffers      = sout.buffers
+            outer_ghosts = sout.outer_ghost_slices 
+            inner_ghosts = sout.inner_ghost_slices 
+            nprocs       = sout.mesh.proc_shape
+            
+            for buf in buffers:
+                lsrc, rsrc = inner_ghosts[-1]
+                ldst, rdst = outer_ghosts[-1]
+                if (nprocs[-1] == 1):
+                    buf[lsrc] += buf[rdst]
+                    buf[rsrc] += buf[ldst]
+                    buf[ldst] = buf[rsrc]
+                    buf[rdst] = buf[lsrc]
+                else:
+                    msg='MPI exchange not implemented yet.'
+                    raise RuntimeError(msg)
 
 
     @debug
diff --git a/hysop/core/mpi/redistribute.py b/hysop/core/mpi/redistribute.py
index b940c84bb..cfdaa2dea 100644
--- a/hysop/core/mpi/redistribute.py
+++ b/hysop/core/mpi/redistribute.py
@@ -150,8 +150,8 @@ class RedistributeIntra(RedistributeOperatorBase):
             return False
         
         # source and target must have the same global resolution
-        source_res = tin.mesh.global_resolution
-        target_res = tout.mesh.global_resolution
+        source_res = tin.mesh.grid_resolution
+        target_res = tout.mesh.grid_resolution
         if not npw.array_equal(source_res, target_res):
             return False
 
diff --git a/hysop/fields/cartesian_discrete_field.py b/hysop/fields/cartesian_discrete_field.py
index 999ca562c..1df5c591f 100644
--- a/hysop/fields/cartesian_discrete_field.py
+++ b/hysop/fields/cartesian_discrete_field.py
@@ -84,10 +84,6 @@ class CartesianDiscreteFieldView(DiscreteFieldView):
         for i,var in enumerate(data):
             if backend.can_wrap(var):
                 data[i] = backend.wrap(var)
-        # else:
-                # msg='Unknown data type {} for backend {}.'
-                # msg=msg.format(var.__class__, backend.kind)
-                # raise ValueError(msg)
 
         for idx, _data in zip(components, data):
             if _data.ndim != self.dim:
@@ -212,9 +208,19 @@ class CartesianDiscreteFieldView(DiscreteFieldView):
     def _get_compute_slices(self):
         """Return a tuple of slices indexing the local compute mesh."""
         return self.mesh.local_compute_slices
-    def _get_ghost_slices(self, directions=None, ):
-        """Return a tuple of tuples of slices indexing the ghosts."""
-        return self.mesh.local_ghost_slices
+    def _get_outer_ghost_slices(self):
+        """
+        Return a tuple of tuples of slices indexing the local outer ghosts.
+        Those slices corresponds to local to process ghosts.
+        """
+        return self.mesh.local_outer_ghost_slices
+    def _get_inner_ghost_slices(self):
+        """
+        Return a tuple of tuples of slices indexing the local inner ghosts.
+        Those slices corresponds to neighbour processes overlap on local 
+        compute slices.
+        """
+        return self.mesh.local_inner_ghost_slices
     
     def short_description(self):
         """Short description of this discrete field."""
@@ -308,6 +314,9 @@ CartesianDiscreteFieldView (id={}, tag={})
         # call formula
         formula(**formula_kwds)
 
+        # update ghosts
+        self.exchange_ghosts(data=data)
+
         assert all((d.size  == self.size)     for d in data), 'Array size was altered.'
         assert all((d.dtype == self.dtype)    for d in data), 'Array dtype was altered.'
         assert all(all(d.shape == self.shape) for d in data), 'Array shape was altered.'
@@ -334,6 +343,70 @@ CartesianDiscreteFieldView (id={}, tag={})
         self.topology.cart_comm.Allreduce(result, gresult)
         return gresult ** (1.0/p)
 
+    def print_with_ghosts(self, component=0, compute=None, 
+            inner_ghosts=None, outer_ghosts='X', **print_opts):
+        """
+        Print values with ghosts replaced by symbol.
+        Mainly for debug purposes.
+        """
+        data = self.data[component].get()
+        strarr = np.empty_like(data, dtype=object)
+        strarr[...] = data
+        if (compute is not None):
+            strarr[self.compute_slices] = compute
+        if (inner_ghosts is not None):
+            for lg, rg in self.inner_ghost_slices:
+                strarr[lg] = inner_ghosts
+                strarr[rg] = inner_ghosts
+        if (outer_ghosts is not None):
+            for lg, rg in self.outer_ghost_slices:
+                strarr[lg] = outer_ghosts
+                strarr[rg] = outer_ghosts
+
+        _formatter = {
+                object: lambda x: '{:^6}'.format(x)[:6],
+                float:  lambda x: '{:6.2f}'.format(x)
+        }
+
+        _print_opts = dict(threshold=10000, linewidth=1000, 
+                             nanstr='nan', infstr='inf', 
+                             formatter={'object': lambda x: _formatter.get(type(x),_formatter[object])(x)})
+        _print_opts.update(print_opts)
+    
+        from hysop.tools.contexts import printoptions
+        with printoptions(**_print_opts):
+            print strarr
+
+
+    def exchange_ghosts(self, components=None, directions=None, data=None, 
+                                custom_op=None):
+        """Exchange ghosts on specified components and directions."""
+        from hysop.core.arrays.array import Array
+        components = to_tuple(components or range(self.nb_components))
+        directions = to_tuple(directions or range(self.dim))
+
+        data = data or self.data
+        data = tuple(d.handle if isinstance(d,Array) else d for d in data)
+        # exchange_ghosts currently only supports hosts buffers
+        check_instance(data, tuple, values=np.ndarray)
+        
+        outer_ghosts = self.outer_ghost_slices 
+        inner_ghosts = self.inner_ghost_slices 
+        nprocs = self.mesh.proc_shape
+        
+        for component in components:
+            field = self.data[component]
+            for direction in directions:
+                lsrc, rsrc = inner_ghosts[direction]
+                ldst, rdst = outer_ghosts[direction]
+                if (nprocs[direction] == 1):
+                    field[rdst] = field[lsrc]
+                    field[ldst] = field[rsrc]
+                else:
+                    msg='MPI exchange not implemented yet.'
+                    raise RuntimeError(msg)
+
+
     size               = property(_get_size)
     shape              = property(_get_shape)
     compute_resolution = property(_get_compute_resolution)
@@ -341,7 +414,8 @@ CartesianDiscreteFieldView (id={}, tag={})
     ghosts             = property(_get_ghosts)
 
     compute_slices = property(_get_compute_slices)
-    ghost_slices = property(_get_ghost_slices)
+    inner_ghost_slices = property(_get_inner_ghost_slices)
+    outer_ghost_slices = property(_get_outer_ghost_slices)
     
     axes   = property(_get_axes)
     tstate = property(_get_tstate)   
@@ -352,6 +426,7 @@ CartesianDiscreteFieldView (id={}, tag={})
 
 
 
+
 class CartesianDiscreteField(CartesianDiscreteFieldView, DiscreteField):
     """
     Discrete representation of cartesian scalar or vector fields,
@@ -431,10 +506,8 @@ class CartesianDiscreteField(CartesianDiscreteFieldView, DiscreteField):
             if (vd is not None): 
                 data[i][topology.mesh.local_compute_slices] = vd
             if (vg is not None): 
-                for ls,rs in topology.mesh.local_ghost_slices:
-                    print ls, vg
-                    data[i][ls] = 0
-                    # data[i][rs] = vg
+                for ls,rs in topology.mesh.outer_ghost_slices:
+                    data[i][rs] = vg
             if self.topology_state.is_read_only:
                 npw.set_readonly(data[i])
         
diff --git a/hysop/mesh/cartesian_mesh.py b/hysop/mesh/cartesian_mesh.py
index 1f50fdd88..a8479502f 100644
--- a/hysop/mesh/cartesian_mesh.py
+++ b/hysop/mesh/cartesian_mesh.py
@@ -64,9 +64,17 @@ class CartesianMeshView(MeshView):
         """Return True if the current process has points on the current mesh."""
         return self._mesh._on_proc
     
+    def _get_grid_resolution(self):
+        """
+        Effective resolution of the global mesh.
+        Corresponds to self.global_resolution - topology.is_periodic.
+        See global_resolution for topology global resolution.
+        """
+        return self.__get_transposed_mesh_attr('_grid_resolution')
     def _get_global_resolution(self):
         """
-        Global resolution of the mesh.
+        Global resolution of the associated topology.
+        See grid_resolution for effective global mesh size.
         """
         return self.__get_transposed_mesh_attr('_global_resolution')
     def _get_global_start(self):
@@ -197,14 +205,23 @@ class CartesianMeshView(MeshView):
         This is the local computational view (ie. local indices of the computational points).
         """
         return self.__get_transposed_mesh_attr('_local_compute_slices')
-    def _get_local_ghost_slices(self):
+    def _get_local_inner_ghost_slices(self):
         """
         Return a list of slices defining the ghosts in this arrays as local indices.
-        There are two ghost slices per specified directions.
+        Those slices corresponds to neighbour processes overlap on local 
+        compute slices. 
         For each direction, a pair of tuples of slices is returned, one
-        for left and right ghosts.
+        for left and one for the right ghosts.
         """
-        return self.__get_transposed_mesh_attr('_local_ghost_slices')
+        return self.__get_transposed_mesh_attr('_local_inner_ghost_slices')
+    def _get_local_outer_ghost_slices(self):
+        """
+        Return a list of slices defining the ghosts in this arrays as local indices.
+        Those slices corresponds to local to process ghosts.
+        For each direction, a pair of tuples of slices is returned, one
+        for left and one for the right ghosts.
+        """
+        return self.__get_transposed_mesh_attr('_local_outer_ghost_slices')
     def _get_local_origin(self):
         """
         Origin of the first computational point on the physical box 
@@ -452,6 +469,8 @@ class CartesianMeshView(MeshView):
     topology = property(_get_topology)
     on_proc = property(_get_on_proc)
     
+    grid_resolution=property(_get_grid_resolution)
+
     global_resolution=property(_get_global_resolution)
     global_start=property(_get_global_start)
     global_stop=property(_get_global_stop)
@@ -466,7 +485,8 @@ class CartesianMeshView(MeshView):
     local_start=property(_get_local_start)
     local_stop=property(_get_local_stop)
     local_compute_slices=property(_get_local_compute_slices)
-    local_ghost_slices=property(_get_local_ghost_slices)
+    local_inner_ghost_slices=property(_get_local_inner_ghost_slices)
+    local_outer_ghost_slices=property(_get_local_outer_ghost_slices)
     local_origin=property(_get_local_origin)
     local_end=property(_get_local_end)
     local_length=property(_get_local_length)
@@ -602,6 +622,9 @@ class CartesianMesh(CartesianMeshView, Mesh):
         global_start      = npw.asintegerarray(global_start).copy()
         assert global_start.size == dim
         assert (global_start>=0).all()
+        
+        # Remove 1 point on each periodic axe because of periodicity
+        grid_resolution = global_resolution - domain.periodicity
 
         # ---
         # Attributes relative to the distributed mesh
@@ -610,7 +633,7 @@ class CartesianMesh(CartesianMeshView, Mesh):
         local_resolution = npw.asintegerarray(local_resolution)
         assert local_resolution.size == dim
         assert (local_resolution >  2*ghosts).all()
-        assert (local_resolution <= global_resolution + 2*ghosts).all()
+        assert (local_resolution <= grid_resolution + 2*ghosts).all()
         compute_resolution = local_resolution - 2*ghosts
        
         # deduce all other attributes
@@ -636,14 +659,21 @@ class CartesianMesh(CartesianMeshView, Mesh):
         local_start = ghosts
         local_stop  = local_resolution - ghosts
         local_compute_slices = tuple( slice(i,j) for (i,j) in zip(local_start, local_stop) )
-        local_ghost_slices = []
+        local_inner_ghost_slices = []
+        local_outer_ghost_slices = []
         for i in xrange(dim):
-            lslices = tuple( slice(None) if j!=i else slice(local_start[i]-ghosts[i], 
+            inner_lslices = tuple( slice(None) if j!=i else slice(local_start[i], 
+                local_start[i]+ghosts[i]) for j in xrange(dim) )
+            inner_rslices = tuple( slice(None) if j!=i else slice(local_stop[i]-ghosts[i], 
+                local_stop[i]) for j in xrange(dim) )
+            outer_lslices = tuple( slice(None) if j!=i else slice(local_start[i]-ghosts[i], 
                 local_start[i]) for j in xrange(dim) )
-            rslices = tuple( slice(None) if j!=i else slice(local_stop[i], 
+            outer_rslices = tuple( slice(None) if j!=i else slice(local_stop[i], 
                 local_stop[i]+ghosts[i]) for j in xrange(dim) )
-            local_ghost_slices.append( (lslices,rslices) )
-        local_ghost_slices = tuple(local_ghost_slices)
+            local_inner_ghost_slices.append( (inner_lslices, inner_rslices) )
+            local_outer_ghost_slices.append( (outer_lslices, outer_rslices) )
+        local_inner_ghost_slices = tuple(local_inner_ghost_slices)
+        local_outer_ghost_slices = tuple(local_outer_ghost_slices)
         
         local_indices = tuple(np.arange(Ni, dtype=HYSOP_INTEGER) for Ni in local_resolution)
         local_coords = tuple(
@@ -670,15 +700,13 @@ class CartesianMesh(CartesianMeshView, Mesh):
         rboundaries = np.asarray( [bc if at_right else BoundaryCondition.NONE 
                 for (bc, at_right) in zip(global_boundaries[1], is_at_right_boundary) ])
         local_boundaries = (lboundaries, rboundaries)
-        is_periodic = np.logical_and(lboundaries==rboundaries, 
-                                     lboundaries==BoundaryCondition.PERIODIC)
         
-        npw.set_readonly(global_resolution, global_start, global_stop, 
+        npw.set_readonly(global_resolution, grid_resolution, global_start, global_stop, 
                          global_origin, global_end, global_length,
                          global_boundaries[0], global_boundaries[1],
                          local_resolution, local_start, local_stop, 
                          local_origin, local_end, local_length,
-                         local_boundaries[0], local_boundaries[1], is_periodic,
+                         local_boundaries[0], local_boundaries[1], 
                          compute_resolution, ghosts, 
                          proc_coords, proc_shape,
                          is_at_left_boundary, is_at_right_boundary,
@@ -688,6 +716,7 @@ class CartesianMesh(CartesianMeshView, Mesh):
         npw.set_readonly(*local_compute_indices)
         npw.set_readonly(*local_compute_coords)
 
+        self._grid_resolution       = grid_resolution
         self._global_resolution     = global_resolution
         self._global_start          = global_start
         self._global_stop           = global_stop
@@ -701,8 +730,6 @@ class CartesianMesh(CartesianMeshView, Mesh):
         self._local_resolution      = local_resolution
         self._local_start           = local_start
         self._local_stop            = local_stop
-        self._local_compute_slices  = local_compute_slices
-        self._local_ghost_slices    = local_ghost_slices
         self._local_origin          = local_origin
         self._local_end             = local_end
         self._local_length          = local_length
@@ -712,6 +739,10 @@ class CartesianMesh(CartesianMeshView, Mesh):
         self._local_compute_indices = local_compute_indices
         self._local_compute_coords  = local_compute_coords
         
+        self._local_compute_slices     = local_compute_slices
+        self._local_inner_ghost_slices = local_inner_ghost_slices
+        self._local_outer_ghost_slices = local_outer_ghost_slices
+        
         self._compute_resolution = compute_resolution
         self._ghosts = ghosts
         self._proc_coords = proc_coords
@@ -719,7 +750,6 @@ class CartesianMesh(CartesianMeshView, Mesh):
         self._space_step  = space_step
         self._is_at_left_boundary  = is_at_left_boundary
         self._is_at_right_boundary = is_at_right_boundary
-        self._is_periodic          = is_periodic
         self._on_proc = True
 
         if __debug__:
@@ -729,6 +759,7 @@ class CartesianMesh(CartesianMeshView, Mesh):
         # check variables and properties at the same time
         from hysop.topology.cartesian_topology import CartesianTopologyView
         check_instance(self.topology, CartesianTopologyView)
+        check_instance(self.grid_resolution, np.ndarray, dtype=HYSOP_INTEGER,  shape=(dim,))
         check_instance(self.global_resolution, np.ndarray, dtype=HYSOP_INTEGER,  shape=(dim,))
         check_instance(self.global_start,      np.ndarray, dtype=HYSOP_INTEGER,  shape=(dim,))
         check_instance(self.global_stop,       np.ndarray, dtype=HYSOP_INTEGER,  shape=(dim,))
@@ -751,8 +782,12 @@ class CartesianMesh(CartesianMeshView, Mesh):
         check_instance(self.local_end,        np.ndarray, dtype=HYSOP_REAL, shape=(dim,))
         check_instance(self.local_length,     np.ndarray, dtype=HYSOP_REAL, shape=(dim,))
         check_instance(self.local_compute_slices, tuple, values=slice, size=dim)
-        check_instance(self.local_ghost_slices, tuple, values=tuple, size=dim)
-        for lg,rg in self.local_ghost_slices:
+        check_instance(self.local_inner_ghost_slices, tuple, values=tuple, size=dim)
+        for lg,rg in self.local_inner_ghost_slices:
+            check_instance(lg, tuple, values=slice, size=dim)
+            check_instance(rg, tuple, values=slice, size=dim)
+        check_instance(self.local_outer_ghost_slices, tuple, values=tuple, size=dim)
+        for lg,rg in self.local_outer_ghost_slices:
             check_instance(lg, tuple, values=slice, size=dim)
             check_instance(rg, tuple, values=slice, size=dim)
         check_instance(self.local_boundaries, tuple, values=np.ndarray, size=2)
@@ -794,7 +829,6 @@ class CartesianMesh(CartesianMeshView, Mesh):
         check_instance(self.is_at_left_boundary,  np.ndarray, dtype=bool, shape=(dim,))
         check_instance(self.is_at_right_boundary, np.ndarray, dtype=bool, shape=(dim,))
         check_instance(self.is_at_boundary,       np.ndarray, dtype=bool, shape=(dim,))
-        # check_instance(self.is_periodic,          np.ndarray, dtype=bool, shape=(dim,))
         check_instance(self.on_proc, bool)
 
     def view(self, topology_state):
diff --git a/hysop/operator/adapt_timestep.py b/hysop/operator/adapt_timestep.py
index e41959748..fa218ecd5 100755
--- a/hysop/operator/adapt_timestep.py
+++ b/hysop/operator/adapt_timestep.py
@@ -107,22 +107,25 @@ class CflTimestepCriteria(TimestepCriteria):
 
     def compute_criteria(self, **kwds):
         from hysop.fields.cartesian_discrete_field import CartesianDiscreteFieldView
+
         dv  = self.dvelocity
         cfl = self.cfl
+        dt = 0.0
+
         if isinstance(dv, CartesianDiscreteFieldView):
             dx = dv.dfield.mesh.space_step
-
-            V0_inf = npw.maximum( npw.abs(dv[0].max()), npw.abs(dv[0].min()) )
-            dt = (V0_inf*dx[0]) / cfl
-            for i in xrange(dv.nb_components-1):
-                Vi_inf = npw.maximum( npw.abs(dv[i].max()), npw.abs(dv[i].min()) )
-                dti = (Vi_inf*dx[i]) / cfl
-                dt = npw.maximum(dt, dti)
-            return dt
+            for i in xrange(dv.nb_components):
+                Vi_min, Vi_max =  dv[i].min(), dv[i].max()
+                Vi_inf = max(abs(Vi_min), abs(Vi_max))
+                dti = (dx[i]*cfl) / Vi_inf
+                dt = max(dt, dti)
         else:
             msg='CFL criteria has not been implemented for {} discrete fields.'
             msg=msg.format(type(self.dvelocity))
             raise RuntimeError(msg)
+        
+        assert dt>0.0
+        return dt
 
 
 class MergeTimeStepCriterias(TimestepCriteria):
diff --git a/hysop/operator/hdf_io.py b/hysop/operator/hdf_io.py
index 938654319..f40541064 100755
--- a/hysop/operator/hdf_io.py
+++ b/hysop/operator/hdf_io.py
@@ -8,7 +8,7 @@
 
 """
 from abc import ABCMeta, abstractmethod
-from hysop.deps import h5py
+from hysop.deps import h5py, sys
 from hysop.core.graph.graph import discretized
 from hysop.constants import DirectionLabels, HYSOP_REAL
 from hysop.tools.decorators import debug
@@ -130,7 +130,7 @@ class HDF_IO(ComputationalGraphOperator):
 
         # Global resolution for hdf5 output (warning : this must
         # be the whole domain resolution, not the subset resolution)
-        self._global_resolution = list(refmesh.global_resolution)
+        self._global_resolution = list(refmesh.grid_resolution)
         self._slices = refmesh.local_compute_slices
         if refmesh.on_proc:
             sl = list(refmesh.global_compute_slices)
@@ -235,11 +235,10 @@ class HDF_Writer(HDF_IO):
         if simulation is None:
             raise ValueError("Missing simulation value for monitoring.")
         ite = simulation.current_iteration
-        if ite == -1 or ite % self.io_params.frequency == 0:
+        if (ite == -1) or (ite % self.io_params.frequency == 0):
             self.step(simulation)
             self._count += 1
         if self.kill:
-            import sys
             sys.exit(1)
 
     def createXMFFile(self):
diff --git a/hysop/problem.py b/hysop/problem.py
index 45b9df2fc..4cd748dc0 100644
--- a/hysop/problem.py
+++ b/hysop/problem.py
@@ -26,7 +26,7 @@ class Problem(ComputationalGraph):
         self.setup(work)
 
     @debug
-    def solve(self, simu, **kargs):
+    def solve(self, simu, max_iter=None, **kargs):
         vprint('\nSolving problem...')
         simu.initialize()
         while not simu.is_over:
@@ -34,6 +34,8 @@ class Problem(ComputationalGraph):
              simu.print_state()
              self.apply(simulation=simu, **kargs)
              simu.advance()
+             if (max_iter is not None) and (simu.current_iteration > max_iter):
+                 break
         simu.finalize()
 
     @debug
diff --git a/hysop/tools/io_utils.py b/hysop/tools/io_utils.py
index 83292409e..dcdc35af4 100755
--- a/hysop/tools/io_utils.py
+++ b/hysop/tools/io_utils.py
@@ -461,10 +461,10 @@ class XMF(object):
         # Check substet to find the required grid resolution
         dx = list(topo.mesh.space_step)
         if subset is not None:
-            res = list(subset.mesh[topo].global_resolution)
+            res  = list(subset.mesh[topo].grid_resolution)
             orig = list(subset.real_orig[topo])
         else:
-            res = list(topo.mesh.global_resolution)
+            res  = list(topo.mesh.grid_resolution)
             orig = list(topo.domain.origin)
         resolution = [1,]*3
         origin     = [0.0,]*3
diff --git a/hysop/topology/cartesian_topology.py b/hysop/topology/cartesian_topology.py
index f022fcba8..4ead103ad 100644
--- a/hysop/topology/cartesian_topology.py
+++ b/hysop/topology/cartesian_topology.py
@@ -880,6 +880,13 @@ class CartesianTopology(CartesianTopologyView, Topology):
 
         local_resolution = nbpoints.copy()
         local_resolution += 2 * discretization.ghosts
+        
+        msg='\nLocal compute shape is smaller than the total number of ghosts, ' 
+        msg+='on at least one axis:'
+        msg+='  *compute shape:   {}'.format(nbpoints)
+        msg+='  *ghosts:        2*{}'.format(discretization.ghosts)
+        if not np.all(nbpoints >= 2 * discretization.ghosts):
+            raise RuntimeError(msg)
 
         # Global indices for the local mesh points
         global_start = proc_coords * pts_noghost
-- 
GitLab