From 1671b4b472b668fb9ea7af08cefabf5ca8df13b3 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr>
Date: Fri, 25 May 2018 17:24:07 +0200
Subject: [PATCH] periodic jet levelset

---
 examples/bubble/periodic_jet_levelset.py | 328 +++++++++++++++++++++++
 1 file changed, 328 insertions(+)
 create mode 100644 examples/bubble/periodic_jet_levelset.py

diff --git a/examples/bubble/periodic_jet_levelset.py b/examples/bubble/periodic_jet_levelset.py
new file mode 100644
index 000000000..9e183b5ab
--- /dev/null
+++ b/examples/bubble/periodic_jet_levelset.py
@@ -0,0 +1,328 @@
+
+## HySoP Example: Periodic jet 2D
+## Osher1995 (second part): 
+##  A Level Set Formulation of Eulerian Interface Capturing Methods for 
+##  Incompressible Fluid flows.
+
+import os
+import numpy as np
+
+def init_vorticity(data, coords):
+    for d in data:
+        d[...] = 0.0
+
+def init_velocity(data, coords):
+    for d in data:
+        d[...] = 0.0
+
+def init_rho(data, coords):
+    data[0][...] = 0.0
+
+def init_phi(data, coords, L):
+    phi = data[0]
+    phi[...] = np.inf
+    Di = np.empty_like(phi)
+    X,Y    = coords 
+    Ly, Lx = L
+    R = Lx/16*(2.5 - 0.75*np.sin(2*np.pi*Y))
+    Di[...] = (X-0.5*Lx)**2 - R**2
+    mask = (np.abs(Di)<np.abs(phi))
+    phi[mask] = Di[mask]
+    assert np.isfinite(phi).all()
+
+def compute(args):
+    from hysop import Box, Simulation, Problem, MPIParams, IOParams
+    from hysop.defaults import VelocityField, VorticityField, \
+                               DensityField, ViscosityField, \
+                               LevelSetField, \
+                               EnstrophyParameter, TimeParameters, \
+                               VolumicIntegrationParameter
+    from hysop.constants import Implementation, AdvectionCriteria
+
+    from hysop.operators import DirectionalAdvection, DirectionalDiffusion,        \
+                                DirectionalStretchingDiffusion,                    \
+                                PoissonRotational, AdaptiveTimeStep,               \
+                                Enstrophy, MinMaxFieldStatistics, StrangSplitting, \
+                                ParameterPlotter, Integrate, HDF_Writer,           \
+                                DirectionalSymbolic
+
+    from hysop.methods import SpaceDiscretization, Remesh, TimeIntegrator, \
+                              ComputeGranularity, Interpolation
+    
+    from hysop.symbolic import sm, space_symbols
+    from hysop.symbolic.base import SymbolicTensor
+    from hysop.symbolic.field import curl
+    from hysop.symbolic.relational import Assignment, LogicalGT, LogicalLT
+    from hysop.symbolic.misc import Select
+    from hysop.symbolic.tmp import TmpScalar
+
+    # Define the domain
+    dim  = args.ndim
+    npts = args.npts
+    box  = Box(origin=args.box_origin, length=args.box_length, dim=dim)
+    
+    # Get default MPI Parameters from domain (even for serial jobs)
+    mpi_params = MPIParams(comm=box.task_comm,
+                           task_id=box.current_task())
+    
+    # Setup usual implementation specific variables
+    impl = args.impl
+    extra_op_kwds = {'mpi_params': mpi_params}
+    if (impl is Implementation.PYTHON):
+        method = {}
+    elif (impl is Implementation.OPENCL):
+        # For the OpenCL implementation we need to setup the compute device
+        # and configure how the code is generated and compiled at runtime.
+                
+        # Create an explicit OpenCL context from user parameters
+        from hysop.backend.device.opencl.opencl_tools import get_or_create_opencl_env
+        cl_env = get_or_create_opencl_env(mpi_params=mpi_params, 
+                                          platform_id=args.cl_platform_id, 
+                                          device_id=args.cl_device_id)
+        
+        # Configure OpenCL kernel generation and tuning (already done by HysopArgParser)
+        from hysop.methods import OpenClKernelConfig
+        method = { OpenClKernelConfig: args.opencl_kernel_config }
+        
+        # Setup opencl specific extra operator keyword arguments
+        extra_op_kwds['cl_env'] = cl_env
+    else:
+        msg='Unknown implementation \'{}\'.'.format(impl)
+        raise ValueError(msg)
+    
+    # Define parameters and field (time, timestep, velocity, vorticity, enstrophy)
+    t, dt = TimeParameters(dtype=args.dtype)
+    velo  = VelocityField(domain=box, dtype=args.dtype)
+    vorti = VorticityField(domain=box, dtype=args.dtype)
+    phi   = LevelSetField(domain=box, dtype=args.dtype)
+    rho   = DensityField(domain=box, dtype=args.dtype) 
+
+    enstrophy = EnstrophyParameter(dtype=args.dtype)
+    rhov = VolumicIntegrationParameter(field=rho)
+    
+    # Symbolic fields
+    frame = rho.domain.frame
+    phis  = phi.s(*frame.vars)
+    rhos  = rho.s(*frame.vars)
+    Ws    = vorti.s(*frame.vars)
+    
+    ### Build the directional operators
+    #> Directional advection 
+    advec = DirectionalAdvection(implementation=impl,
+            name='advection',
+            pretty_name='Adv',
+            velocity = velo,       
+            advected_fields = (vorti, phi),
+            velocity_cfl = args.cfl,
+            variables = {velo: npts, vorti: npts, phi: npts},
+            dt=dt, **extra_op_kwds)
+    #> Recompute density and viscosity from levelset
+    dx = np.max(np.divide(box.length, np.asarray(args.npts)-1))
+    rho1, rho2 = args.rho1, args.rho2
+    pi  = TmpScalar(name='pi', dtype=args.dtype)
+    eps = TmpScalar(name='eps', dtype=args.dtype)
+    x   = TmpScalar(name='x', dtype=args.dtype)
+    H   = TmpScalar(name='H', dtype=args.dtype)
+    smooth_cond = LogicalLT(sm.Abs(x), eps)
+    pos_cond = LogicalGT(x, 0)
+    clamp  = Select(0.0, 1.0, pos_cond)
+    smooth = (x+eps)/(2*eps) + sm.sin(pi*x/eps)/(2*pi)
+    H_eps = Select(clamp, smooth, smooth_cond)
+    e0 = Assignment(pi, np.pi)
+    e1 = Assignment(eps, 2.5*dx)
+    e2 = Assignment(x, phis)
+    e3 = Assignment(H, H_eps) 
+    e4 = Assignment(rhos, rho1 + (rho2-rho1)*H)
+    exprs = (e0,e1,e2,e3,e4)
+    eval_fields = DirectionalSymbolic(name='eval_fields', 
+                                    pretty_name=u'{}({})'.format(
+                                        phi.pretty_name.decode('utf-8'), 
+                                        rho.pretty_name.decode('utf-8')), 
+                                    no_split=True,
+                                    implementation=impl,
+                                    exprs=exprs, dt=dt,
+                                    variables={phi: npts,
+                                               rho: npts},
+                                    **extra_op_kwds)
+    #> Directional stretching + diffusion
+    diffuse = DirectionalDiffusion(implementation=impl,
+             name='diffuse_{}'.format(vorti.name),
+             pretty_name=u'D{}'.format(vorti.pretty_name.decode('utf-8')),
+             coeffs = (args.mu,)*2,
+             fields  = (vorti, phi),
+             variables = {vorti: npts, phi: npts},
+             dt=dt, **extra_op_kwds)
+    
+    #> External force rot(-rho*g)
+    Fext = np.zeros(shape=(dim,), dtype=object).view(SymbolicTensor)
+    Fext[1] = +1
+    Fext *= rhos
+    lhs = Ws.diff(frame.time)
+    rhs = curl(Fext, frame)
+    exprs = Assignment.assign(lhs, rhs)
+    external_force = DirectionalSymbolic(name='Fext', 
+                                    implementation=impl,
+                                    exprs=exprs, dt=dt,
+                                    variables={vorti: npts,
+                                               rho: npts},
+                                    **extra_op_kwds)
+
+    #> Directional splitting operator subgraph
+    splitting = StrangSplitting(splitting_dim=dim, order=args.strang_order)
+    splitting.push_operators(advec, diffuse, eval_fields, external_force)
+    
+    ### Build standard operators
+    #> Poisson operator to recover the velocity from the vorticity
+    poisson = PoissonRotational(name='poisson', velocity=velo, vorticity=vorti, 
+                            variables={velo:npts, vorti: npts}, 
+                            projection=args.reprojection_frequency,
+                            implementation=impl, **extra_op_kwds)
+
+    #> Operators to dump rho
+    io_params = IOParams(filename='fields', frequency=args.dump_freq)
+    dump_fields = HDF_Writer(name='dump',
+                             io_params=io_params,
+                             variables={velo: npts, 
+                                        vorti: npts,    
+                                        phi: npts,
+                                        rho: npts})
+    
+    #> Operator to compute the infinite norm of the velocity
+    min_max_U = MinMaxFieldStatistics(field=velo,
+            Finf=True, implementation=impl, variables={velo:npts},
+            **extra_op_kwds)
+    #> Operator to compute the infinite norm of the vorticity
+    min_max_W = MinMaxFieldStatistics(field=vorti,
+            Finf=True, implementation=impl, variables={vorti:npts},
+            **extra_op_kwds)
+
+    #> Operators to compute the integrated enstrophy, density and viscosity
+    integrate_enstrophy = Enstrophy(vorticity=vorti, enstrophy=enstrophy,
+            variables={vorti:npts}, implementation=impl, **extra_op_kwds)
+    integrate_rho = Integrate(field=rho, variables={rho: npts},
+                                    parameter=rhov, scaling='normalize',
+                                    implementation=impl, **extra_op_kwds)
+
+    ### Adaptive timestep operator
+    adapt_dt = AdaptiveTimeStep(dt, equivalent_CFL=True, max_dt=1e-2,
+                                    name='merge_dt', pretty_name='dt', )
+    dt_cfl   = adapt_dt.push_cfl_criteria(cfl=args.cfl, Finf=min_max_U.Finf,
+                                            equivalent_CFL=True, 
+                                            name='dt_cfl', pretty_name='CFL')
+    dt_advec = adapt_dt.push_advection_criteria(lcfl=args.lcfl, Finf=min_max_W.Finf,
+                                                    criteria=AdvectionCriteria.W_INF,
+                                                 name='dt_lcfl', pretty_name='LCFL')
+    
+    ## Create the problem we want to solve and insert our 
+    # directional splitting subgraph and the standard operators.
+    # The method dictionnary passed to this graph will be dispatched
+    # accross all operators contained in the graph.
+    method.update(
+            { 
+               ComputeGranularity:    args.compute_granularity,
+               SpaceDiscretization:   args.fd_order,
+               TimeIntegrator:        args.time_integrator,
+               Remesh:                args.remesh_kernel,
+               Interpolation:         args.interpolation
+            }
+    )
+    problem = Problem(method=method)
+    problem.insert(poisson, 
+                   splitting, 
+                   dump_fields,
+                   integrate_enstrophy, integrate_rho,
+                   min_max_U, min_max_W, 
+                   adapt_dt)
+    problem.build()
+
+    # If a visu_rank was provided, and show_graph was set,
+    # display the graph on the given process rank.
+    if args.display_graph:
+        problem.display(args.visu_rank)
+    
+    # Create a simulation
+    # (do not forget to specify the t and dt parameters here)
+    simu = Simulation(start=args.tstart, end=args.tend, 
+                      nb_iter=args.nb_iter,
+                      max_iter=args.max_iter,
+                      dt0=args.dt, times_of_interest=args.dump_times,
+                      t=t, dt=dt)
+    simu.write_parameters(t, dt_cfl, dt_advec, dt, 
+            enstrophy, rhov, 
+            min_max_U.Finf, min_max_W.Finf, 
+            adapt_dt.equivalent_CFL,
+            filename='parameters.txt', precision=8)
+    
+    # Initialize vorticity, velocity, viscosity and density on all topologies
+    problem.initialize_field(field=velo,  formula=init_velocity)
+    problem.initialize_field(field=vorti, formula=init_vorticity)
+    problem.initialize_field(field=rho,   formula=init_rho)
+    problem.initialize_field(field=phi,   formula=init_phi, L=box.length) 
+
+    # Finally solve the problem 
+    problem.solve(simu, dry_run=args.dry_run)
+    
+    # Finalize
+    problem.finalize()
+
+
+if __name__=='__main__':
+    from examples.example_utils import HysopArgParser, colors
+
+    class PeriodicJetArgParser(HysopArgParser):
+        def __init__(self):
+            prog_name = 'periodic_jet'
+            default_dump_dir = '{}/hysop_examples/{}'.format(HysopArgParser.tmp_dir(), 
+                    prog_name)
+
+            description=colors.color('HySoP Periodic Bubble Example: ', fg='blue', style='bold')
+            description+=colors.color('[Osher 1995] (second part)', fg='yellow', style='bold')
+            description+=colors.color('\nA Level Set Formulation of Eulerian Interface Capturing Methods for '
+                    +'Incompressible Fluid flows.',
+                    fg='yellow')
+            description+='\n'
+            description+='\nThis example focuses on a validation study for the '
+            description+='hybrid particle-mesh vortex method for varying densities without using a levelset function.'
+    
+            super(PeriodicJetArgParser, self).__init__(
+                 prog_name=prog_name,
+                 description=description,
+                 default_dump_dir=default_dump_dir)
+
+        def _add_main_args(self):
+            args = super(PeriodicJetArgParser, self)._add_main_args()
+            args.add_argument('-rho1', '--bubble-density', type=float,
+                                dest='rho1',
+                                help='Set the density of the bubbles.')
+            args.add_argument('-rho2', '--fluid-density', type=float,
+                                dest='rho2',
+                                help='Set the density of the fluid surrounding the bubbles.')
+            args.add_argument('-mu', '--viscosity', type=float,
+                                dest='mu',
+                                help='Set the viscosity.')
+            return args
+
+        def _check_main_args(self, args):
+            super(PeriodicJetArgParser, self)._check_main_args(args)
+            vars_ = ('rho1', 'rho2', 'mu')
+            self._check_default(args, vars_, float, allow_none=False)
+            self._check_positive(args, vars_, strict=False, allow_none=False)
+            
+        def _setup_parameters(self, args):
+            dim = args.ndim
+            if (dim not in (2,)):
+                msg='Domain should be 2D.'
+                self.error(msg)
+
+
+    parser = PeriodicJetArgParser()
+
+    parser.set_defaults(impl='cl', ndim=2, npts=(129,),
+                        box_origin=(0.0,), box_length=(1.0,), 
+                        tstart=0.0, tend=0.66, 
+                        dt=1e-5, cfl=0.5, lcfl=0.125,
+                        dump_freq=10, 
+                        dump_times=(0.0, 0.1, 0.3, 0.45, 0.55, 0.65),
+                        rho1=10.0, rho2=20.0, mu=0.00025)
+
+    parser.run(compute)
-- 
GitLab