From 7bb9bea8373c5a999b39972d6a3965aeac98c8d1 Mon Sep 17 00:00:00 2001
From: Jean-Matthieu Etancelin <jean-matthieu.etancelin@univ-pau.fr>
Date: Tue, 24 Jul 2018 11:24:21 +0200
Subject: [PATCH] Add TaylorGreen Fortran(FFTW+Scales)+Python example

---
 ci/scripts/test.sh                            |   1 +
 .../taylor_green/taylor_green_cpuFortran.py   | 410 ++++++++++++------
 2 files changed, 280 insertions(+), 131 deletions(-)

diff --git a/ci/scripts/test.sh b/ci/scripts/test.sh
index 1d20dbba8..4a24f0291 100755
--- a/ci/scripts/test.sh
+++ b/ci/scripts/test.sh
@@ -89,6 +89,7 @@ scalar_advection/scalar_advection.py
 shear_layer/shear_layer.py
 taylor_green/taylor_green_cpu.py
 taylor_green/taylor_green.py
+taylor_green/taylor_green_cpuFortran.py
 bubble/periodic_bubble.py
 bubble/periodic_bubble_levelset.py
 bubble/periodic_bubble_levelset_penalization.py
diff --git a/examples/taylor_green/taylor_green_cpuFortran.py b/examples/taylor_green/taylor_green_cpuFortran.py
index 4f28f64f3..0bb386d80 100644
--- a/examples/taylor_green/taylor_green_cpuFortran.py
+++ b/examples/taylor_green/taylor_green_cpuFortran.py
@@ -23,136 +23,284 @@ def init_vorticity(data, coords):
     # initial volume averaged enstrophy: 6*pi^3 / (2*pi)^3 = 0.75
 
 
-from hysop import Box, Simulation, Problem, MPIParams
-from hysop.defaults import VelocityField, VorticityField, \
-                           EnstrophyParameter, TimeParameters
-from hysop.constants import Implementation, AdvectionCriteria, HYSOP_REAL, \
-    StretchingFormulation
-from hysop.operators import Advection, DirectionalStretching, Diffusion, \
-                            PoissonRotational, AdaptiveTimeStep,                  \
-                            Enstrophy, MinMaxFieldStatistics, StrangSplitting,    \
-                            ParameterPlotter
-from hysop.numerics.odesolvers.runge_kutta import RK2
-from hysop.methods import SpaceDiscretization, Remesh, TimeIntegrator, \
-                          ComputeGranularity, Interpolation, StrangOrder
-# Define the domain
-dim  = 3
-npts = (65, )*dim
-box  = Box(origin=(0., )*dim, length=(2*pi,)*dim, dim=dim)
-cfl = 0.5
-lcfl = 0.125
-Re = 1600.
-viscosity = (1.0/Re)
-
-# 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 = Implementation.OPENCL
-extra_op_kwds = {'mpi_params': mpi_params}
-method = {}
-
-# Define parameters and field (time, timestep, velocity, vorticity, enstrophy)
-t, dt = TimeParameters(dtype=HYSOP_REAL)
-velo  = VelocityField(domain=box, dtype=HYSOP_REAL)
-vorti = VorticityField(domain=box, dtype=HYSOP_REAL)
-enstrophy = EnstrophyParameter(dtype=HYSOP_REAL)
-
-### Build the directional operators
-#> Directional advection
-advec = Advection(implementation=Implementation.FORTRAN,
-        name='advec',
-        velocity = velo,
-        advected_fields = (vorti,),
-        #velocity_cfl = cfl,
-        variables = {velo: npts, vorti: npts},
-        dt=dt, **extra_op_kwds)
-#> Directional stretching + diffusion
-stretch = DirectionalStretching(implementation=impl,
-         name='stretch',
-         formulation =StretchingFormulation.CONSERVATIVE,
-         velocity  = velo,
-         vorticity = vorti,
-         variables = {velo: npts, vorti: npts},
-         dt=dt, **extra_op_kwds)
-#> Directional splitting operator subgraph
-splitting = StrangSplitting(splitting_dim=dim, order=StrangOrder.STRANG_SECOND_ORDER)
-splitting.push_operators(stretch)
-
-diffuse = Diffusion(implementation=Implementation.FORTRAN,
-                    name='diffuse',
-                    viscosity = viscosity,
-                    input_field = vorti,
-                    variables = {vorti: npts},
-                    dt=dt, **extra_op_kwds)
-### 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=None,
-                        implementation=Implementation.FORTRAN, **extra_op_kwds)
-#> We ask to dump the outputs of this operator
-#poisson.dump_outputs(fields=(vorti,), frequency=args.dump_freq)
-#poisson.dump_outputs(fields=(velo,),  frequency=args.dump_freq)
-
-#> Operator to compute the infinite norm of the velocity
-min_max_U = MinMaxFieldStatistics(name='min_max_U', 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(name='min_max_W', field=vorti,
-        Finf=True, implementation=impl, variables={vorti:npts},
-        **extra_op_kwds)
-#> Operator to compute the enstrophy
-enstrophy_op = Enstrophy(name='enstrophy', vorticity=vorti, enstrophy=enstrophy,
-        variables={vorti:npts}, implementation=impl, **extra_op_kwds)
-
-### Adaptive timestep operator
-adapt_dt = AdaptiveTimeStep(dt, equivalent_CFL=True)
-dt_cfl   = adapt_dt.push_cfl_criteria(cfl=cfl, Finf=min_max_U.Finf,
-                                        equivalent_CFL=True)
-dt_advec = adapt_dt.push_advection_criteria(lcfl=lcfl, Finf=min_max_W.Finf,
+def compute(args):
+    from hysop import Box, Simulation, Problem, MPIParams, Field
+    from hysop.defaults import VelocityField, VorticityField, \
+                               EnstrophyParameter, TimeParameters
+    from hysop.constants import Implementation, AdvectionCriteria, HYSOP_REAL, \
+        StretchingFormulation
+    from hysop.operators import Advection, StaticDirectionalStretching, Diffusion, \
+                                PoissonRotational, AdaptiveTimeStep,                  \
+                                Enstrophy, MinMaxFieldStatistics, StrangSplitting,    \
+                                ParameterPlotter
+    from hysop.numerics.odesolvers.runge_kutta import RK2
+    from hysop.topology.cartesian_topology import CartesianTopology
+    from hysop.tools.parameters import Discretization
+    from hysop.methods import SpaceDiscretization, Remesh, TimeIntegrator, \
+                              ComputeGranularity, Interpolation, StrangOrder
+    # 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}
+    method = {}
+
+    # Define parameters and field (time, timestep, velocity, vorticity, enstrophy)
+    t, dt = TimeParameters(dtype=HYSOP_REAL)
+    velo  = VelocityField(domain=box, dtype=HYSOP_REAL)
+    vorti = VorticityField(domain=box, dtype=HYSOP_REAL)
+    enstrophy = EnstrophyParameter(dtype=HYSOP_REAL)
+    wdotw = Field(domain=box, dtype=HYSOP_REAL, is_vector=False, name="WdotW")
+
+    # Topologies
+    topo_nogh = CartesianTopology(domain=box,
+                                  discretization=Discretization(npts),
+                                  mpi_params=mpi_params,
+                                  cutdirs=[False, False, True])
+
+
+    ### Build the directional operators
+    #> Directional advection
+    advec = Advection(implementation=Implementation.FORTRAN,
+            name='advec',
+            velocity = velo,
+            advected_fields = (vorti,),
+            variables = {velo: npts, vorti: npts},
+            dt=dt, **extra_op_kwds)
+    #> Directional stretching
+    stretch = StaticDirectionalStretching(implementation=impl,
+             name='stretch',
+             formulation = args.stretching_formulation,
+             velocity  = velo,
+             vorticity = vorti,
+             variables = {velo: npts, vorti: npts},
+             dt=dt, **extra_op_kwds)
+    #> Directional splitting operator subgraph
+    splitting = StrangSplitting(splitting_dim=dim, order=args.strang_order)
+    splitting.push_operators(stretch)
+    #> Diffusion
+    diffuse = Diffusion(implementation=Implementation.FORTRAN,
+                        name='diffuse',
+                        viscosity = (1.0/args.Re),
+                        input_field = vorti,
+                        variables = {vorti: topo_nogh},
+                        dt=dt, **extra_op_kwds)
+    ### Build standard operators
+    #> Poisson operator to recover the velocity from the vorticity
+    poisson = PoissonRotational(name='poisson', velocity=velo, vorticity=vorti,
+                            variables={velo:topo_nogh, vorti: topo_nogh},
+                            projection=args.reprojection_frequency,
+                            implementation=Implementation.FORTRAN, **extra_op_kwds)
+    #> We ask to dump the outputs of this operator
+    poisson.dump_outputs(fields=(vorti,), frequency=args.dump_freq)
+    poisson.dump_outputs(fields=(velo,),  frequency=args.dump_freq)
+
+    #> Operator to compute the infinite norm of the velocity
+    min_max_U = MinMaxFieldStatistics(name='min_max_U', 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(name='min_max_W', field=vorti,
+            Finf=True, implementation=impl, variables={vorti:npts},
+            **extra_op_kwds)
+    #> Operator to compute the enstrophy
+    enstrophy_op = Enstrophy(name='enstrophy', vorticity=vorti, enstrophy=enstrophy,
+            variables={vorti:topo_nogh,wdotw:topo_nogh}, implementation=impl, **extra_op_kwds)
+
+    ### Adaptive timestep operator
+    adapt_dt = AdaptiveTimeStep(dt, equivalent_CFL=True)
+    dt_cfl   = adapt_dt.push_cfl_criteria(cfl=args.cfl, Finf=min_max_U.Finf,
+                                          equivalent_CFL=True)
+    dt_advec = adapt_dt.push_advection_criteria(lcfl=args.lcfl, Finf=min_max_W.Finf,
                                                 criteria=AdvectionCriteria.W_INF)
 
-## 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:    0,
-               SpaceDiscretization:   4,
-               TimeIntegrator:        RK2,
-               Remesh:                Remesh.L4_2,
-               Interpolation:         Interpolation.LINEAR})
-problem = Problem(method=method)
-problem.insert(poisson, advec, splitting, diffuse, enstrophy_op,
-               min_max_U, min_max_W, adapt_dt)
-problem.build()
-
-# Create a simulation
-# (do not forget to specify the t and dt parameters here)
-simu = Simulation(start=0., end=10.01,
-                  #nb_iter=999999,
-                  #max_iter=args.max_iter,
-                  dt0=1e-5, #times_of_interest=args.dump_times,
-                  t=t, dt=dt)
-simu.write_parameters(t, dt_cfl, dt_advec, dt, enstrophy,
-        min_max_U.Finf, min_max_W.Finf, adapt_dt.equivalent_CFL,
-        filename='parameters.txt', precision=8)
-
-# Attach a field debug dumper if requested
-debug_dumper = None
-# from hysop.tools.debug_dumper import DebugDumper
-# debug_dumper = DebugDumper(
-#     path=args.debug_dump_dir,
-#     name=args.debug_dump_target,
-#     force_overwrite=True, enable_on_op_apply=True)
-
-# Initialize only the vorticity
-problem.initialize_field(vorti, formula=init_vorticity)
-
-# Finally solve the problem
-problem.solve(simu, debug_dumper=debug_dumper)
-
-# Finalize
-problem.finalize()
+    #> Custom operator to plot enstrophy
+    if args.plot_enstrophy:
+        class EnstrophyPlotter(ParameterPlotter):
+            """Custom plotting operator for enstrophy."""
+            def __init__(self, **kwds):
+                import matplotlib.pyplot as plt
+                if all(n==npts[0] for n in npts):
+                    snpts='${}^3$'.format(npts[0]-1)
+                else:
+                    snpts='x'.join(str(n-1) for n in npts)
+                tag='hysop-{}'.format(snpts)
+                fig  = plt.figure(figsize=(30,18))
+                axe0 = plt.subplot2grid((3,2), (0,0), rowspan=3, colspan=1)
+                axe1 = plt.subplot2grid((3,2), (0,1), rowspan=2, colspan=1)
+                axe2 = plt.subplot2grid((3,2), (2,1), rowspan=1, colspan=1)
+                axes = (axe0, axe1, axe2)
+                parameters={axe0:{tag:enstrophy},
+                            axe1:{dt_advec.name: dt_advec,
+                                  dt_cfl.name: dt_cfl,
+                                  dt.name: dt},
+                            axe2:{'CFL*': adapt_dt.equivalent_CFL }}
+                super(EnstrophyPlotter, self).__init__(name='enstrophy_dt',
+                        parameters=parameters, fig=fig, axes=axes, **kwds)
+                config='{}  {}  FD{}  PROJECTION_{}  {}'.format(args.time_integrator, args.remesh_kernel,
+                                            args.fd_order, args.reprojection_frequency, args.strang_order)
+                fig = fig.suptitle('HySoP Taylor-Green Example {}\n{}'.format(
+                                                    snpts, config), fontweight='bold')
+                axe0.set_title('Integrated Enstrophy')
+                axe0.set_xlabel('Non-dimensional time', fontweight='bold')
+                axe0.set_ylabel('$\zeta$',
+                        rotation=0, fontweight='bold')
+                axe0.set_xlim(args.tstart, args.tend)
+                axe0.set_ylim(0, 26)
+                datadir = os.path.realpath(
+                    os.path.join(os.getcwd(), os.path.dirname(__file__)))
+                datadir+='/data'
+                for d in (64,128,256,512):
+                    reference=os.path.join(datadir, 'reference_{d}_{d}_{d}.txt'.format(d=d))
+                    data = np.loadtxt(reference, usecols=(0,2), dtype=np.float32)
+                    axe0.plot(data[:,0], data[:,1]*(1+(d==512)), '--',
+                            linewidth=1.0,
+                            label='hysop-origin ${}^3$'.format(d) if (d<512) else 'reference-$512^3$')
+                axe0.legend()
+                axe1.set_title('Timesteps (CFL={}, LCFL={})'.format(args.cfl, args.lcfl))
+                axe1.set_xlabel('Non-dimensional time', fontweight='bold')
+                axe1.set_ylabel('Non-dimensional time steps', fontweight='bold')
+                axe1.set_xlim(args.tstart, args.tend)
+                axe1.set_ylim(1e-5, 1e0)
+                axe1.set_yscale('log')
+                axe1.legend()
+                axe2.set_title('Equivalent CFL')
+                axe2.set_xlabel('Non-dimensional time', fontweight='bold')
+                axe2.set_ylabel('CFL*', fontweight='bold')
+                axe2.set_xlim(args.tstart, args.tend)
+                axe2.axhline(y=args.cfl, color='r', linestyle='--')
+                axe2.set_ylim(0., 1.1*args.cfl)
+        plot = EnstrophyPlotter(update_frequency=args.plot_freq,
+                                visu_rank=args.visu_rank)
+    else:
+        plot = None
+
+    ## 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, advec, splitting, diffuse, enstrophy_op,
+                   min_max_U, min_max_W, adapt_dt, plot)
+    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,
+            min_max_U.Finf, min_max_W.Finf, adapt_dt.equivalent_CFL,
+            filename='parameters.txt', precision=8)
+
+    # Attach a field debug dumper if requested
+    from hysop.tools.debug_dumper import DebugDumper
+    if args.debug_dump_target:
+        debug_dumper = DebugDumper(
+                path=args.debug_dump_dir,
+                name=args.debug_dump_target,
+                force_overwrite=True, enable_on_op_apply=True)
+    else:
+        debug_dumper = None
+
+    # Initialize only the vorticity
+    problem.initialize_field(vorti, formula=init_vorticity)
+
+    # Finally solve the problem
+    problem.solve(simu, debug_dumper=debug_dumper)
+
+    # Finalize
+    problem.finalize()
+
+
+if __name__=='__main__':
+    from examples.example_utils import HysopArgParser, colors
+
+    class TaylorGreenArgParser(HysopArgParser):
+        def __init__(self):
+            prog_name = 'taylor_green'
+            default_dump_dir = '{}/hysop_examples/{}'.format(HysopArgParser.tmp_dir(),
+                    prog_name)
+
+            description=colors.color('HySoP Taylor-Green Example: ', fg='blue', style='bold')
+            description+=colors.color('[Van Rees 2011] (first part)', fg='yellow', style='bold')
+            description+=colors.color('\nA comparison of vortex and pseudo-spectral methods '
+                    +'for the simulation of periodic vortical flows at high Reynolds numbers.',
+                    fg='yellow')
+            description+='\n'
+            description+='\nThis example focuses on a validation study for the '
+            description+='hybrid particle-mesh vortex method at Reynolds 1600 for '
+            description+='the 3D Taylor-Green vortex.'
+            description+='\n'
+            description+='\nSee the original paper at '
+            description+='http://vanreeslab.com/wp-content/papercite-data/pdf/rees-2011.pdf.'
+
+            super(TaylorGreenArgParser, self).__init__(
+                 prog_name=prog_name,
+                 description=description,
+                 default_dump_dir=default_dump_dir)
+
+        def _add_main_args(self):
+            args = super(TaylorGreenArgParser, self)._add_main_args()
+            args.add_argument('-Re', '--reynolds-number', type=float,
+                                dest='Re',
+                                help='Set the simulation Reynolds number.')
+            return args
+
+        def _check_main_args(self, args):
+            super(TaylorGreenArgParser, self)._check_main_args(args)
+            self._check_default(args, 'Re', float, allow_none=False)
+            self._check_positive(args, 'Re', strict=True, allow_none=False)
+
+        def _add_graphical_io_args(self):
+            graphical_io = super(TaylorGreenArgParser, self)._add_graphical_io_args()
+            graphical_io.add_argument('-pe', '--plot-enstrophy', action='store_true',
+                    dest='plot_enstrophy',
+                    help=('Plot the enstrophy component during simulation. '+
+                         'Simulation will stop at each time of interest and '+
+                         'the plot will be updated every specified freq iterations.'))
+            graphical_io.add_argument('-pf', '--plot-freq', type=int, default=10,
+                    dest='plot_freq',
+                    help='Plotting update frequency in terms of iterations.')
+
+        def _check_file_io_args(self, args):
+            super(TaylorGreenArgParser, self)._check_file_io_args(args)
+            self._check_default(args, 'plot_enstrophy', bool, allow_none=False)
+            self._check_default(args, 'plot_freq', int, allow_none=False)
+            self._check_positive(args, 'plot_freq', strict=True, allow_none=False)
+
+        def _setup_parameters(self, args):
+            if (args.ndim != 3):
+                msg='This example only works for 3D domains.'
+                self.error(msg)
+
+    parser = TaylorGreenArgParser()
+
+    parser.set_defaults(impl='PYTHON', ndim=3, npts=(65,),
+                        box_origin=(0.0,), box_length=(2*pi,),
+                        tstart=0.0, tend=10.01,
+                        dt=1e-5,
+                        cfl=0.5, lcfl=0.125,
+                        dump_freq=100, dump_times=(),
+                        Re=1600.0)
+
+    parser.run(compute)
-- 
GitLab