From 68a824db7c426d716f0dfa38de4ff523b0256178 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr>
Date: Sun, 31 Mar 2019 12:25:16 +0200
Subject: [PATCH] fixed analytic, fixed levelset example

---
 examples/scalar_advection/levelset.py         | 89 ++++++++++++++-----
 .../device/opencl/operator/analytic.py        |  2 +-
 .../device/opencl/operator/integrate.py       |  3 +-
 .../backend/host/python/operator/analytic.py  | 16 ++--
 hysop/operator/base/enstrophy.py              |  1 +
 hysop/operator/base/integrate.py              |  3 +-
 hysop/operator/integrate.py                   |  5 +-
 7 files changed, 87 insertions(+), 32 deletions(-)

diff --git a/examples/scalar_advection/levelset.py b/examples/scalar_advection/levelset.py
index 9246d2060..21f3ebec7 100644
--- a/examples/scalar_advection/levelset.py
+++ b/examples/scalar_advection/levelset.py
@@ -3,11 +3,11 @@ import sympy as sm
 pi = np.pi
 
 def compute(args):
-    from hysop import Field, Box, Simulation, Problem, \
+    from hysop import Field, Box, Simulation, Problem, IOParams, \
                       ScalarParameter, MPIParams, CartesianDiscretization, CartesianTopology
     from hysop.constants import Implementation, Backend
     from hysop.operators import DirectionalAdvection, StrangSplitting, Integrate, \
-        AnalyticField, Advection
+        AnalyticField, Advection, HDF_Writer
     from hysop.methods import Remesh, TimeIntegrator, ComputeGranularity, \
                               Interpolation, StrangOrder
     import hysop.numerics.odesolvers.runge_kutta as rk
@@ -51,8 +51,9 @@ def compute(args):
                                                                  device_id=args.cl_device_id)
         extra_op_kwds['cl_env'] = cl_env
         method[OpenClKernelConfig] = args.opencl_kernel_config
+        backend = Backend.OPENCL
     elif (impl in  (Implementation.PYTHON, Implementation.FORTRAN)):
-        pass
+        backend = Backend.HOST
     else:
         msg='Unknown implementation \'{}\'.'.format(impl)
         raise ValueError(msg)
@@ -63,6 +64,11 @@ def compute(args):
     scalar = Field(domain=box, name='S', nb_components=1, dtype=args.dtype)
     vol    = ScalarParameter('volume', dtype=args.dtype)
 
+    if (snpts == npts):
+        mask = velo[1]
+    else:
+        mask = scalar.field_like('mask')
+
     # Setup operator method dictionnary
     method.update({
                TimeIntegrator:      args.time_integrator,
@@ -90,9 +96,15 @@ def compute(args):
             data[1][...] = -sin(2.*pi*x)*sin(pi*y)**2*sin(2.*pi*z)*cos(t()*pi/3.)
             (x,y,z) = coords[2]
             data[2][...] = -sin(2.*pi*x)*sin(2.*pi*y)*sin(pi*z)**2*cos(t()*pi/3.)
-        formula = compute_velocity
+        def compute_norm(data, coords):
+            data[0][...] = np.sqrt(data[0]**2 + data[1]**2 + data[2]**2)
+        def compute_volume(data, coords, S):
+            data[0][...] = (S[0] <= 0.5)
     elif (impl is Implementation.OPENCL):
+        from hysop.symbolic.relational import LogicalLE
         sin, cos = sm.sin, sm.cos
+        Vs = velo.s()
+        Ss = scalar.s()
         if dim == 3:
             x, y, z = box.frame.coords
             formula = (2.*sin(pi*x)**2*sin(2.*pi*y)*sin(2.*pi*z)*cos(t.s*pi/3.),
@@ -102,24 +114,13 @@ def compute(args):
             x, y = box.frame.coords
             formula = (-sin(x*pi)**2*sin(y*pi*2)*cos(t.s*pi/3.),
                        sin(y*pi)**2*sin(x*pi*2)*cos(t.s*pi/3.))
+        compute_velocity = formula
+        compute_norm     = (sm.sqrt(np.dot(Vs, Vs)), None, None)
+        compute_volume   = LogicalLE(Ss, 0.5)
     else:
         msg='Unknown implementation \'{}\'.'.format(impl)
         raise ValueError(msg)
 
-
-    analytic = AnalyticField(field=velo, formula=formula,
-                             variables={velo: npts},
-                             extra_input_kwds={'t': simu.t},
-                             implementation=Implementation.PYTHON if (impl is Implementation.FORTRAN) else impl,
-                             **extra_op_kwds)
-
-    volume = Integrate(name='volume',
-                       field=scalar,
-                       variables={scalar: snpts},
-                       parameter=vol,
-                       implementation=impl,
-                       **extra_op_kwds)
-
     if (impl is Implementation.FORTRAN):
         advec = Advection(name='advec',
                           velocity = velo,
@@ -140,12 +141,58 @@ def compute(args):
         advec = StrangSplitting(splitting_dim=dim,
                                   order=args.strang_order)
         advec.push_operators(advec_dir)
+    
+    
+    compute_velocity = AnalyticField(name='compute_velocity', pretty_name='V(t)',
+                             field=velo, formula=compute_velocity,
+                             variables={velo: npts},
+                             extra_input_kwds={'t': simu.t},
+                             implementation=Implementation.PYTHON if (impl is Implementation.FORTRAN) else impl,
+                             **extra_op_kwds)
+    
+    compute_norm = AnalyticField(name='compute_norm', pretty_name='||V||',
+                             field=velo, formula=compute_norm,
+                             variables={velo: npts},
+                             implementation=Implementation.PYTHON if (impl is Implementation.FORTRAN) else impl,
+                             **extra_op_kwds)
+    
+    compute_volume = AnalyticField(name='compute_volume', pretty_name='S<0.5',
+                             field=mask, formula=compute_volume,
+                             variables={mask: snpts, scalar: snpts},
+                             extra_input_kwds={'S': scalar},
+                             implementation=Implementation.PYTHON if (impl is Implementation.FORTRAN) else impl,
+                             **extra_op_kwds)
+
+    # compute the volume (where S<=0.5), use Vy as tmp buffer when possible (ie. npts == snpts)
+    volume = Integrate(name='volume',
+                       field=mask,
+                       variables={mask: snpts},
+                       parameter=vol,
+                       implementation=impl,
+                       allow_ghosts=True,
+                       **extra_op_kwds)
+    
+    # dump scalar and velocity norm
+    if (npts==snpts):
+        io_params = IOParams(filename='fields', frequency=args.dump_freq)
+        dump_fields = HDF_Writer(name='fields', io_params=io_params, 
+                force_backend=backend, variables={velo[0]: snpts, scalar: snpts}, **extra_op_kwds)
+        dumpers = (dump_fields,)
+    else:
+        io_params = IOParams(filename='scalar', frequency=args.dump_freq)
+        dump_scalar = HDF_Writer(name='scalar', io_params=io_params, 
+                force_backend=backend, variables={scalar: snpts}, **extra_op_kwds)
 
+        io_params = IOParams(filename='Vnorm', frequency=args.dump_freq)
+        dump_velocity_norm = HDF_Writer(name='Vnorm', io_params=io_params, 
+                force_backend=backend, variables={velo[0]: npts}, **extra_op_kwds)
+        dumpers = (dump_scalar, dump_velocity_norm)
 
     problem = Problem(method=method)
-    problem.insert(analytic, advec, volume)
-    problem.dump_inputs(fields=scalar, filename='S', frequency=args.dump_freq)
-    problem.build()
+    problem.insert(compute_velocity, advec)
+    problem.insert(compute_norm, *dumpers)
+    problem.insert(compute_volume, volume)
+    problem.build(args=args)
     
     # If a visu_rank was provided, and show_graph was set,
     # display the graph on the given process rank.
diff --git a/hysop/backend/device/opencl/operator/analytic.py b/hysop/backend/device/opencl/operator/analytic.py
index 7ea4fce4e..d490f3654 100644
--- a/hysop/backend/device/opencl/operator/analytic.py
+++ b/hysop/backend/device/opencl/operator/analytic.py
@@ -34,7 +34,7 @@ class OpenClAnalyticField(OpenClCustomSymbolicOperator):
         """
         formula = to_tuple(formula)
         check_instance(field, Field)
-        check_instance(formula, tuple, values=sm.Basic, size=field.nb_components)
+        check_instance(formula, tuple, values=(type(None),sm.Basic), size=field.nb_components)
         check_instance(variables, dict, keys=Field, values=CartesianTopologyDescriptors)
         
         exprs = ()
diff --git a/hysop/backend/device/opencl/operator/integrate.py b/hysop/backend/device/opencl/operator/integrate.py
index d3feef93f..6aa31e79d 100644
--- a/hysop/backend/device/opencl/operator/integrate.py
+++ b/hysop/backend/device/opencl/operator/integrate.py
@@ -13,7 +13,8 @@ class OpenClIntegrate(IntegrateBase, OpenClOperator):
         # force 0 ghosts for the reduction (pyopencl reduction kernel)
         requirements = super(OpenClIntegrate, self).get_field_requirements()
         topo, req = requirements.get_input_requirement(self.field)
-        req.max_ghosts = (0,)*self.field.dim
+        if not self.allow_ghosts:
+            req.max_ghosts = (0,)*self.field.dim
         return requirements
 
     @debug
diff --git a/hysop/backend/host/python/operator/analytic.py b/hysop/backend/host/python/operator/analytic.py
index 4f9ffb5a2..e2969cfe0 100644
--- a/hysop/backend/host/python/operator/analytic.py
+++ b/hysop/backend/host/python/operator/analytic.py
@@ -48,15 +48,17 @@ class PythonAnalyticField(HostOperator):
         assert callable(formula), type(formula)
         check_instance(variables, dict, keys=Field, values=CartesianTopologyDescriptors)
         check_instance(extra_input_kwds, dict, keys=str)
-        
+
         input_fields  = {}
         output_fields = { field: variables[field] }
         input_params  = {}
 
         extra_kwds = {}
+        map_fields = {}
         for (k,v) in extra_input_kwds.iteritems():
             if isinstance(v, Field):
-                input_fields[k] = v
+                input_fields[v] = variables[v]
+                map_fields[v] = k
             elif isinstance(v, Parameter):
                 input_params[k] = v
                 extra_kwds[k] = v
@@ -69,6 +71,7 @@ class PythonAnalyticField(HostOperator):
         self.field = field
         self.formula = formula
         self.extra_kwds = extra_kwds
+        self.map_fields = map_fields
     
     @debug
     def discretize(self):
@@ -77,13 +80,14 @@ class PythonAnalyticField(HostOperator):
         super(PythonAnalyticField, self).discretize()
         dfield = self.get_output_discrete_field(self.field)
         extra_kwds = self.extra_kwds
+        map_fields = self.map_fields
         assert 'data'   not in extra_kwds
         assert 'coords' not in extra_kwds
-        extra_kwds['data']   = dfield.data
-        extra_kwds['coords'] = dfield.get_attributes('mesh', 'local_mesh_coords')
-        for (field, dfield) in self.input_discrete_fields:
+        extra_kwds['data']   = dfield.compute_data
+        extra_kwds['coords'] = dfield.get_attributes('compute_mesh_coords')
+        for (field, dfield) in self.input_discrete_fields.iteritems():
             assert field.name not in extra_kwds, field.name
-            extra_kwds[field.name] = dfield.data
+            extra_kwds[map_fields[field]] = dfield.compute_data
         self.dfield = dfield
 
     @op_apply
diff --git a/hysop/operator/base/enstrophy.py b/hysop/operator/base/enstrophy.py
index 8dd2a558f..1305461ca 100644
--- a/hysop/operator/base/enstrophy.py
+++ b/hysop/operator/base/enstrophy.py
@@ -112,3 +112,4 @@ class EnstrophyBase(object):
     @classmethod
     def supports_mpi(cls):
         return True
+
diff --git a/hysop/operator/base/integrate.py b/hysop/operator/base/integrate.py
index d6c152323..d3b1b9396 100644
--- a/hysop/operator/base/integrate.py
+++ b/hysop/operator/base/integrate.py
@@ -17,7 +17,7 @@ class IntegrateBase(object):
     __metaclass__ = ABCMeta
 
     @debug
-    def __init__(self, field, variables,
+    def __init__(self, field, variables, allow_ghosts=False,
                     name=None, pretty_name=None, cst=1,
                     parameter=None, scaling=None, **kwds):
         """
@@ -89,6 +89,7 @@ class IntegrateBase(object):
         self.scaling   = scaling
         self.cst = cst
         self.scaling_coeff = None
+        self.allow_ghosts = allow_ghosts
 
         super(IntegrateBase, self).__init__(name=name, pretty_name=pretty_name,
                 input_fields=input_fields, output_params=output_params, **kwds)
diff --git a/hysop/operator/integrate.py b/hysop/operator/integrate.py
index 4c0a8e646..34de06ea2 100644
--- a/hysop/operator/integrate.py
+++ b/hysop/operator/integrate.py
@@ -36,7 +36,7 @@ class Integrate(ComputationalGraphNodeFrontend):
     
     @debug
     def __init__(self, field, variables,
-                parameter=None, scaling=None,
+                parameter=None, scaling=None, 
                 implementation=None, base_kwds=None, **kwds):
         """
         Initialize a Integrate operator frontend.
@@ -97,4 +97,5 @@ class Integrate(ComputationalGraphNodeFrontend):
             raise RuntimeError(msg)
         
         super(Integrate, self).__init__(field=field, variables=variables,
-                parameter=parameter, scaling=scaling, base_kwds=base_kwds, **kwds)
+                parameter=parameter, scaling=scaling,
+                base_kwds=base_kwds, **kwds)
-- 
GitLab