From 2313f125751ee19478126a177b6948827768c82c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Chlo=C3=A9=20Mimeau?= <Chloe.Mimeau@imag.fr>
Date: Tue, 27 Oct 2015 15:19:01 +0100
Subject: [PATCH] add SFD stuff (ie: method to get the filtered solution (=the
 base flow) needed to perform global linear stability analysis)

---
 Examples/FlowAroundSphere_linearized.py       | 498 ++++++++++++++++++
 HySoP/hysop/operator/discrete/forcing.py      | 124 +++++
 .../hysop/operator/discrete/low_pass_filt.py  | 123 +++++
 .../operator/discrete/monitoringPoints.py     | 134 +++++
 HySoP/hysop/operator/discrete/residual.py     | 117 ++++
 HySoP/hysop/operator/forcing.py               | 155 ++++++
 HySoP/hysop/operator/low_pass_filt.py         | 153 ++++++
 HySoP/hysop/operator/monitoringPoints.py      |  68 +++
 HySoP/hysop/operator/residual.py              |  53 ++
 9 files changed, 1425 insertions(+)
 create mode 100644 Examples/FlowAroundSphere_linearized.py
 create mode 100644 HySoP/hysop/operator/discrete/forcing.py
 create mode 100644 HySoP/hysop/operator/discrete/low_pass_filt.py
 create mode 100644 HySoP/hysop/operator/discrete/monitoringPoints.py
 create mode 100644 HySoP/hysop/operator/discrete/residual.py
 create mode 100644 HySoP/hysop/operator/forcing.py
 create mode 100644 HySoP/hysop/operator/low_pass_filt.py
 create mode 100644 HySoP/hysop/operator/monitoringPoints.py
 create mode 100644 HySoP/hysop/operator/residual.py

diff --git a/Examples/FlowAroundSphere_linearized.py b/Examples/FlowAroundSphere_linearized.py
new file mode 100644
index 000000000..d41cf782e
--- /dev/null
+++ b/Examples/FlowAroundSphere_linearized.py
@@ -0,0 +1,498 @@
+#!/usr/bin/python
+
+"""
+Taylor Green 3D : see paper van Rees 2011.
+
+All parameters are set and defined in python module dataTG.
+
+"""
+
+from hysop import Box
+from hysop.f2py import fftw2py
+import numpy as np
+import cPickle
+#from scitools.NumPyDB import NumPyDB_cPickle as hysopPickle
+from hysop.fields.continuous import Field
+from hysop.fields.variable_parameter import VariableParameter
+from hysop.mpi.topology import Cartesian
+from hysop.operator.advection import Advection
+from hysop.operator.stretching import Stretching
+from hysop.operator.absorption_BC import AbsorptionBC
+from hysop.operator.poisson import Poisson
+from hysop.operator.diffusion import Diffusion
+from hysop.operator.adapt_timestep import AdaptTimeStep
+from hysop.operator.redistribute_intra import RedistributeIntra
+from hysop.operator.hdf_io import HDF_Writer
+from hysop.operator.energy_enstrophy import EnergyEnstrophy
+from hysop.operator.profiles import Profiles
+from hysop.operator.monitoringPoints import MonitoringPoints
+from hysop.operator.residual import Residual
+from hysop.problem.simulation import Simulation
+from hysop.methods_keys import Scales, TimeIntegrator, Interpolation,\
+    Remesh, Support, Splitting, dtCrit, SpaceDiscretisation
+from hysop.numerics.integrators.runge_kutta2 import RK2 as RK2
+from hysop.numerics.integrators.runge_kutta3 import RK3 as RK3
+from hysop.numerics.integrators.runge_kutta4 import RK4 as RK4
+from hysop.numerics.finite_differences import FD_C_4, FD_C_2
+from hysop.numerics.interpolation import Linear
+from hysop.numerics.remeshing import L6_4 as rmsh
+import hysop.tools.io_utils as io
+import hysop.tools.numpywrappers as npw
+from hysop.mpi import main_rank, MPI
+from hysop.tools.parameters import Discretization, IOParams
+
+print " ========= Start Navier-Stokes 3D (Taylor Green benchmark) ========="
+
+# ====== pi constant and trigonometric functions ======
+pi = np.pi
+cos = np.cos
+sin = np.sin
+
+# ====== Flow constants =======
+VISCOSITY = 1. / 300.
+
+# ======= Domain =======
+dim = 3
+Nx = 129
+Ny = Nz = 65
+#Nx = 257
+#Ny = Nz = 129
+#Nx = 513
+#Ny = Nz = 257
+#Nx = 1025
+#Ny = Nz = 513
+g = 2
+boxlength = npw.asrealarray([10.24, 5.12, 5.12])
+boxorigin = npw.asrealarray([-2.0, -2.56, -2.56])
+box = Box(length=boxlength, origin=boxorigin)
+
+# A global discretization with ghost points
+d3dg = Discretization([Nx, Ny, Nz], [g, g, g])
+# A global discretization, without ghost points
+d3d = Discretization([Nx, Ny, Nz])
+
+# ====== Sphere inside the domain ======
+RADIUS = 0.5
+pos = [0., 0., 0.]
+from hysop.domain.subsets import Sphere, HemiSphere
+sphere = Sphere(origin=pos, radius=RADIUS, parent=box)
+
+
+# ======= Function to set initial velocity BF =======
+def setZeroVel(res, x, y, z, t):
+    res[0][...] = 0.
+    res[1][...] = 0.
+    res[2][...] = 0.
+    return res
+
+
+# ======= Function to set initial vorticity BF =======
+def setZeroVort(res, x, y, z, t):
+    res[0][...] = 0.
+    res[1][...] = 0.
+    res[2][...] = 0.
+    return res
+
+#  ====== Time-dependant required-flowrate (Variable Parameter) ======
+def computeFlowrate(simu):
+    # === Time-dependant flow rate ===
+#    t = simu.tk
+#    Tstart = 3.0
+#    flowrate = np.zeros(3)
+#    flowrate[0] = uinf * box.length[1] * box.length[2]
+#    if t >= Tstart and t <= Tstart + 1.0:
+#        flowrate[1] = sin(pi * (t - Tstart)) * \
+#                      box.length[1] * box.length[2]
+    # === Constant flow rate ===
+    flowrate = np.zeros(3)
+#    flowrate[0] = uinf * box.length[1] * box.length[2]
+    return flowrate
+
+
+# ======= Fields =======
+# Base flow
+veloBF = Field(domain=box, formula=setZeroVel,
+               name='Velocity', is_vector=True)
+vortiBF = Field(domain=box, formula=setZeroVort,
+                name='Vorticity', is_vector=True)
+# Small perturbation whose stability is analyzed
+velo = Field(domain=box, formula=setZeroVel,
+               name='Velocity_fluc', is_vector=True)
+vorti = Field(domain=box, formula=setZeroVort,
+                name='Vorticity_fluc', is_vector=True)
+
+# ========= Simulation setup =========
+simu = Simulation(tinit=0.0, tend=2000.0, timeStep=0.0125, iterMax=10000000)
+
+
+# Adaptative timestep method : dt = min(values(dtCrit))
+# where dtCrit is a list of criterions on which the computation
+# of the adaptative time step is based
+# ex : dtCrit = ['gradU', 'cfl', 'stretch'], means :
+# dt = min (dtAdv, dtCfl, dtStretch), where dtAdv is equal to LCFL / |gradU|
+# For dtAdv, the possible choices are the following:
+# 'vort' (infinite norm of vorticity) : dtAdv = LCFL / |vort|
+# 'gradU' (infinite norm of velocity gradient), dtAdv = LCFL / |gradU|
+# 'deform' (infinite norm of deformation tensor),
+# dtAdv = LCFL / (0.5(gradU + gradU^T))
+op = {}
+iop = IOParams("time_step")
+# Default topology (i.e. 3D, with ghosts)
+topo_with_ghosts = box.create_topology(d3dg)
+
+
+op['dtAdapt'] = AdaptTimeStep(velo, vorti, simulation=simu,
+                              discretization=topo_with_ghosts,
+                              method={TimeIntegrator: RK3,
+                                      SpaceDiscretisation: FD_C_4,
+                                      dtCrit: ['gradU', 'stretch']},
+                              io_params=iop,
+                              lcfl=0.125,
+                              cfl=0.5)
+
+op['advection1'] = Advection(velo, vortiBF,
+                             discretization=d3d,
+                             method={Scales: 'p_M6',
+                                     Splitting: 'classic'}
+                             )
+
+op['advection2'] = Advection(veloBF, vorti,
+                             discretization=d3d,
+                             method={Scales: 'p_M6',
+                             Splitting: 'classic'}
+                             )
+
+op['stretching1'] = Stretching(veloBF, vorti,
+                               discretization=topo_with_ghosts)
+
+op['stretching2'] = Stretching(velo, vortiBF,
+                               discretization=topo_with_ghosts)
+
+op['diffusion'] = Diffusion(viscosity=VISCOSITY, vorticity=vorti,
+                            discretization=d3d)
+
+rate = VariableParameter(formula=computeFlowrate)
+op['poisson'] = Poisson(velo, vorti, discretization=d3d, flowrate=rate)
+
+# ===== Discretization of computational operators ======
+for ope in op.values():
+    ope.discretize()
+
+topofft = op['poisson'].discreteFields[vorti].topology
+topoadvec = op['advection2'].discreteFields[vorti].topology
+
+# =====  Smooth vorticity absorption at the outlet =====
+op['vort_absorption'] = AbsorptionBC(velo, vorti, discretization=topofft, 
+                                     req_flowrate=rate, 
+                                     x_coords_absorp=[7.24, 8.24])
+#                                     x_coords_absorp=[1.56, 2.56])
+op['vort_absorption'].discretize()
+
+# =====  Penalization of the vorticity on a sphere inside the domain =====
+from hysop.operator.penalization import PenalizeVorticity
+op['penalVort'] = PenalizeVorticity(velocity=velo, vorticity=vorti,
+                                    discretization=topo_with_ghosts,
+                                    obstacles=[sphere], coeff=1e8,
+                                    method={SpaceDiscretisation: FD_C_4})
+op['penalVort'].discretize()
+
+# ==== Operators to map data between the different computational operators ===
+# (i.e. between topologies)
+distr = {}
+distr['advec22str1'] = RedistributeIntra(source=op['advection2'],
+                                        target=op['stretching1'],
+                                        variables=[veloBF])
+distr['advec12str2'] = RedistributeIntra(source=op['advection1'],
+                                         target=op['stretching2'],
+                                         variables=[vortiBF])
+
+distr['fft2str1'] = RedistributeIntra(source=op['poisson'],
+                                     target=op['stretching1'],
+                                     variables=[vorti])
+distr['fft2str2'] = RedistributeIntra(source=op['poisson'],
+                                      target=op['stretching2'],
+                                      variables=[velo])
+distr['str12fft'] = RedistributeIntra(source=op['stretching1'],
+                                     target=op['poisson'],
+                                     variables=[vorti])
+distr['str22fft'] = RedistributeIntra(source=op['stretching2'],
+                                      target=op['poisson'],
+                                      variables=[velo])
+
+distr['fft2advec1'] = RedistributeIntra(source=op['poisson'],
+                                       target=op['advection1'],
+                                       variables=[velo])
+distr['fft2advec2'] = RedistributeIntra(source=op['poisson'],
+                                        target=op['advection2'],
+                                        variables=[vorti])
+distr['advec12fft'] = RedistributeIntra(source=op['advection1'],
+                                       target=op['poisson'],
+                                       variables=[velo])
+distr['advec22fft'] = RedistributeIntra(source=op['advection2'],
+                                        target=op['poisson'],
+                                        variables=[vorti])
+
+distr['fft2penal'] = RedistributeIntra(source=op['poisson'],
+                                       target=op['penalVort'],
+                                       variables=[velo, vorti])
+
+# ========= Monitoring operators =========
+monitors = {}
+#iop = IOParams('fields', frequency=100)
+#monitors['writer'] = HDF_Writer(variables={velo: topofft, vorti: topofft},
+#                                io_params=iop)
+
+io_ener = IOParams('energy_enstrophy')
+monitors['energy'] = EnergyEnstrophy(velo, vorti, discretization=topofft,
+                                     io_params=io_ener, is_normalized=False)
+
+rk = 0
+if (0.0 in topofft.mesh.coords[2]):
+    rk = main_rank
+io_prof = IOParams('profile_X_axis', frequency=8400, io_leader=rk)
+monitors['profile'] = Profiles(velo, vorti, discretization=topofft,
+                               io_params=io_prof, prof_coords=[0.0, 0.0],
+                               direction=0, beginMeanComput=25.0)
+
+coordsMonit = [4.0, 0.0, 0.0]
+rk = 0
+if (coordsMonit[2] in topofft.mesh.coords[2]):
+    rk = main_rank
+io_monit = IOParams('monit', frequency=1, io_leader=rk)
+monitors['monit'] = MonitoringPoints(velo, vorti, discretization=topofft,
+                                     io_params=io_monit,
+                                     monitPt_coords=coordsMonit)
+
+io_resi = IOParams('residual')
+monitors['residual'] = Residual(vorti, discretization=topofft,
+                                io_params=io_resi)
+
+from hysop.domain.control_box import ControlBox
+from hysop.operator.drag_and_lift import MomentumForces, NocaForces
+ref_step = topo_with_ghosts.mesh.space_step
+#cbpos = npw.zeros(dim)
+#cblength = npw.zeros(dim)
+#cbpos[...] = boxorigin[...]
+#cbpos +=  15 * ref_step
+#cblength[...] = boxlength[...]
+#cblength -= 30 * ref_step
+#cb = ControlBox(parent=box, origin=cbpos, length=cblength)
+#coeffForce = 1. / (0.5 * uinf ** 2 * pi * RADIUS ** 2)
+
+#io_forces=IOParams('drag_and_lift_NocaII')
+#monitors['forcesNoca'] = NocaForces(velo, vorti,
+#                                    discretization=topo_with_ghosts,
+#                                    nu=VISCOSITY, 
+#                                    volume_of_control=cb,
+#                                    normalization=coeffForce,
+#                                    obstacles=[sphere], 
+#                                    io_params=io_forces)
+
+#io_forcesPenal=IOParams('drag_and_lift_Mom')
+#monitors['forcesMom'] = MomentumForces(velocity=velo, 
+#                                       discretization=topo_with_ghosts,
+#                                       normalization=coeffForce,
+#                                       obstacles=[sphere], 
+#                                       penalisation_coeff=[1e8],
+#                                       io_params=io_forcesPenal)
+
+#io_forcesPenal=IOParams('drag_and_lift_penal')
+#monitors['forcesPenal'] = DragAndLiftPenal(velo, vorti, coeffForce,
+#                                           discretization=topofft,
+#                                           obstacles=[sphere], factor=[1e8],
+#                                           io_params=io_forcesPenal)
+
+step_dir = ref_step[0]
+io_sliceXY = IOParams('sliceXY', frequency=2000)
+thickSliceXY = ControlBox(parent=box, origin=[-2.0, -2.56, -2.0 * step_dir], 
+                          length=[10.24- step_dir, 5.12- step_dir, 4.0 * step_dir])
+#thickSliceXY = ControlBox(parent=box, origin=[-2.56, -2.56, -2.0 * step_dir], 
+#                          length=[5.12 - step_dir, 5.12 - step_dir, 4.0 * step_dir])
+monitors['writerSliceXY'] = HDF_Writer(variables={velo: topofft, vorti: topofft},
+                                      io_params=io_sliceXY, subset=thickSliceXY, 
+                                      xmfalways=True)
+
+io_sliceXZ = IOParams('sliceXZ', frequency=2000)
+thickSliceXZ = ControlBox(parent=box, origin=[-2.0, -2.0 * step_dir, -2.56], 
+                          length=[10.24- step_dir, 4.0 * step_dir, 5.12- step_dir])
+monitors['writerSliceXZ'] = HDF_Writer(variables={velo: topofft, vorti: topofft},
+                                       io_params=io_sliceXZ, subset=thickSliceXZ, 
+                                       xmfalways=True)
+
+io_subBox = IOParams('subBox', frequency=5000)
+subBox = ControlBox(parent=box, origin=[-0.7, -2.0, -2.0], length=[8.0, 4.0, 4.0])
+monitors['writerSubBox'] = HDF_Writer(variables={velo: topofft, vorti: topofft},
+                                      io_params=io_subBox, subset=subBox, 
+                                      xmfalways=True)
+
+# ========= Setup for all declared operators/monitors =========
+time_setup = MPI.Wtime()
+for ope in op.values():
+    ope.setup()
+for ope in distr.values():
+    ope.setup()
+
+for monit in monitors.values():
+    monit.discretize()
+for monit in monitors.values():
+    monit.setup()
+
+print '[', main_rank, '] total time for setup:', MPI.Wtime() - time_setup
+
+# ========= Fields initialization =========
+# - initialize (veloBF, vortiBF) on topofft
+# - initialize (veloBF, vortiBF) and (velo, vort) on topostr
+# - penalize vorti from velo on topostr
+# - redistribute topostr --> topofft
+
+time_init = MPI.Wtime()
+ind = sphere.discretize(topofft)
+def initFields():
+    veloBF.initialize(topo=topofft)
+    vortiBF.initialize(topo=topofft)
+    veloBF.initialize(topo=topo_with_ghosts)
+    vortiBF.initialize(topo=topo_with_ghosts)
+    velo.initialize(topo=topo_with_ghosts)
+    vorti.initialize(topo=topo_with_ghosts)
+    op['penalVort'].apply(simu)
+    distr['str2fft'].apply(simu)
+    distr['str2fft'].wait()
+
+# /!\ CAREFULL /!\ : INITIALIZATION COMMENTED OUT !!!!!!!!!!!!!!!!!
+#initFields()
+print '[', main_rank, '] total time for init :', MPI.Wtime() - time_init
+
+fullseq = []
+
+def run(sequence):
+    op['vort_absorption'].apply(simu)
+    op['poisson'].apply(simu)               # Poisson + correction
+    #    monitors['forcesMom'].apply(simu)       # Forces Heloise
+    distr['fft2penal'].apply(simu)
+    distr['fft2penal'].wait()
+    op['penalVort'].apply(simu)             # Vorticity penalization
+    op['stretching1'].apply(simu)            # Stretching 1
+    op['stretching2'].apply(simu)            # Stretching 2
+    distr['str12fft'].apply(simu)
+    distr['str12fft'].wait()
+    distr['str22fft'].apply(simu)
+    distr['str22fft'].wait()
+    op['diffusion'].apply(simu)             # Diffusion
+    distr['fft2advec1'].apply(simu)
+    distr['fft2advec1'].wait()
+    distr['fft2advec2'].apply(simu)
+    distr['fft2advec2'].wait()
+    op['advection1'].apply(simu)             # Advection 1 (scales)
+    op['advection2'].apply(simu)             # Advection 2 (scales)
+    distr['advec12fft'].apply(simu)
+    distr['advec12fft'].wait()
+    distr['advec22fft'].apply(simu)
+    distr['advec22fft'].wait()
+    monitors['writerSliceXY'].apply(simu)
+    monitors['writerSliceXZ'].apply(simu)
+    monitors['writerSubBox'].apply(simu)
+    monitors['energy'].apply(simu)          # Energy/enstrophy
+    monitors['profile'].apply(simu)         # Profile
+    monitors['monit'].apply(simu)           # Monitoring points in the wake
+    monitors['residual'].apply(simu)        # Vorticity residual
+    distr['fft2penal'].apply(simu)
+    distr['fft2penal'].wait()
+    op['dtAdapt'].apply(simu)               # Update timestep
+    op['dtAdapt'].wait()
+
+# ==== Serialize the simulation data of the problem to a "restart" file ====
+def dump(filename):
+    """
+    Serialize some data of the problem to file
+    (only data required for a proper restart, namely fields in self.input
+    and simulation).
+    @param filename : prefix for output file. Real name = filename_rk_N,
+    N being current process number. If None use default value from problem
+    parameters (self.filename)
+    """
+    if filename is not None:
+        filedump = filename + '_rk_' + str(main_rank)
+    db = open(filedump, 'wb')
+    cPickle.dump(simu, db)
+
+# ====== Load the simulation data of the problem from a "restart" file ======
+def restart(filename):
+    """
+    Load serialized data to restart from a previous state.
+    self.input variables and simulation are loaded.
+    @param  filename : prefix for downloaded file.
+    Real name = filename_rk_N, N being current process number.
+    If None use default value from problem
+    parameters (self.filename)
+    """
+    if filename is not None:
+        filedump = filename + '_rk_' + str(main_rank)
+    db = open(filedump, 'r')
+    simu = cPickle.load(db)
+    simu.start = simu.time - simu.timeStep
+    ite = simu.currentIteration
+    simu.initialize()
+    simu.currentIteration = ite
+    print 'simu', simu
+    print ("load ...", filename)
+    return simu
+
+seq = fullseq
+
+simu.initialize()
+doDump = True
+doRestart = True
+dumpFreq = 8000
+io_default=IOParams('restart')
+dump_filename = io.Writer(io_params=io_default).filename
+#===== Restart (if needed) =====
+if doRestart:
+    # Load data from dumped files
+    simu = restart(dump_filename)
+    iop_vel = IOParams('velo_00000.h5')
+    veloBF.hdf_load(topofft, io_params=iop_vel)
+    iop_vort = IOParams('vorti_00000.h5')
+    vortiBF.hdf_load(topofft, io_params=iop_vort)
+    # Initialize velo and vorti to a small perturbation
+    # equal to baseFlow * 1e-8
+    # (cf Florian Guiho's thesis p.30 eq. (2.13))
+    velo.initialize(topo=topofft)
+    vorti.initialize(topo=topofft)
+    velo.discreteFields[topofft].data[0][...] = \
+        veloBF.discreteFields[topofft].data[0][...] * 1e-8
+    vorti.discreteFields[topofft].data[0][...] = \
+        vortiBF.discreteFields[topofft].data[0][...] * 1e-8
+    # Set up for monitors and redistribute
+    for ope in distr.values():
+        ope.setup()
+    for monit in monitors.values():
+        monit.setup()
+    # Redistribute veloBF and vortBF on topoGhosts
+    distr['advec22str1'].apply(simu)
+    distr['advec22str1'].wait()
+    distr['advec12str2'].apply(simu)
+    distr['advec12str2'].wait()
+
+# ======= Time loop =======
+time_run = MPI.Wtime()
+while not simu.isOver:
+    if topofft.rank == 0:
+        simu.printState()
+    run(seq)
+    simu.advance()
+    testdump = simu.currentIteration % dumpFreq is 0
+    if doDump and testdump:
+        print 'dump ...'
+        dump(dump_filename)
+        iop_vel = IOParams('veloFluc')
+        velo.hdf_dump(topofft, io_params=iop_vel)
+        iop_vort = IOParams('vortiFluc')
+        vorti.hdf_dump(topofft, io_params=iop_vort)
+print '[', main_rank, '] total time for run :', MPI.Wtime() - time_run
+
+# ======= Finalize =======
+fftw2py.clean_fftw_solver(box.dimension)
+for ope in distr.values():
+    ope.finalize()
+for monit in monitors.values():
+    monit.finalize()
diff --git a/HySoP/hysop/operator/discrete/forcing.py b/HySoP/hysop/operator/discrete/forcing.py
new file mode 100644
index 000000000..55a00fa9e
--- /dev/null
+++ b/HySoP/hysop/operator/discrete/forcing.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+"""DOperator implementing the forcing term in the NS,
+    depending on the filtered field
+    (--> computation of base flow).
+    
+.. currentmodule:: hysop.operator.discrete.forcing
+
+"""
+from hysop.constants import debug
+from hysop.operator.discrete.discrete import DiscreteOperator
+from hysop.tools.profiler import profile
+
+class Forcing(DiscreteOperator):
+    """Discretized forcing operator.
+    i.e. solve (with an implicit Euler scheme)
+    \f{eqnarray*}
+    \frac{\partial \omega}{\partial t} &=& -\chi(\omega - \bar{\omega}))
+    \Leftrightarrow \omega^{n+1} &=&
+    \frac{\omega^n + \Delta t \chi \bar{\omega^{n+1}}}{1+\Delta t \chi}
+    \f}
+    """
+
+    @debug
+    def __init__(self, strength=None, **kwds):
+        """
+        Parameters
+        ----------
+        strength : strength of the forcing, chosen in the order
+        of the amplification rate related to the unstable flow.
+        **kwds : extra parameters for parent class.
+
+        """
+        super(Forcing, self).__init__(**kwds)
+        topo = self.variables[0].topology
+        for v in self.variables:
+            msg = 'Multiresolution not implemented for penalization.'
+            assert v.topology == topo, msg
+        
+        ## variable
+        self.var = self.variables[0]
+        ## forced variable
+        self.varFiltered = self.variables[1]
+        ## strength of the forcing
+        self.strength = strength
+
+    def _apply(self, dt):
+        nbc = self.var.nb_components
+        for d in xrange(nbc):
+            self.var[d][...] += self.varFiltered[d][...] * \
+                                    (dt * self.strength)
+            self.var[d][...] *= 1.0 / (1.0 + dt * self.strength)
+        print 'forcing: non conservative formulation'
+
+    def apply(self, simulation=None):
+        assert simulation is not None, \
+            "Simulation parameter is required."
+        dt = simulation.timeStep
+        self._apply(dt)
+
+
+class ForcingConserv(Forcing):
+    """Discretized forcing operator.
+        i.e. solve (with an implicit Euler scheme and a CONSERVATIVE formulation)
+        \f{eqnarray*}
+        \frac{\partial \omega}{\partial t} &=& -\chi(\omega - \bar{\omega}))
+        \Leftrightarrow \omega^{n+1} &=&
+        \frac{\omega^n + \Delta t \chi \bar{\omega^{n+1}}}{1+\Delta t \chi}
+        \f}
+        """
+
+    @debug
+    def __init__(self, vorticity, velocity, velocityFilt, curl, **kwds):
+        """
+        Parameters
+        ----------
+        velocity, vorticity, velocityFilt: :class:`~hysop.fields.continuous.Field`
+        curl : :class:`~hysop.operator.differential`
+        internal operator to compute the curl of the forced velocity
+        **kwds : extra parameters for parent class.
+            
+        Notes
+        -----
+        velocity and velocityFilt are not modified by this operator.
+        vorticity is an in-out parameter.
+        input and ouput variables of the curl are some local buffers.
+        """
+
+        assert 'variables' not in kwds, 'variables parameter is useless.'
+        super(ForcingConserv, self).__init__(variables=[vorticity,
+                                                            velocity,
+                                                            velocityFilt],
+                                             **kwds)
+        # Input vector fields
+        self.velocity = velocity
+        self.vorticity = vorticity
+        self.velocityFilt = velocityFilt
+        # warning : a buffer is added for invar variable in curl
+        topo = self.velocity.topology
+        msg = 'Multiresolution not implemented for vort forcing.'
+        assert self.vorticity.topology == topo, msg
+        self._curl = curl
+            
+    def _apply(self, dt):
+        # Vorticity forcing
+        # warning : the buff0 array ensures "invar" to be 0
+        # outside the obstacle for the curl evaluation
+        invar = self._curl.invar
+        nbc = invar.nb_components
+        for d in xrange(nbc):
+            invar.data[d][...] = 0.0
+        coeff = (dt * self.strength) / (1.0 + dt * self.strength)
+        for d in xrange(nbc):
+            invar.data[d][...] = \
+                (self.velocityFilt[d][...] -
+                 self.velocity[d][...]) * coeff
+        self._curl.apply()
+        for d in xrange(self.vorticity.nb_components):
+            self.vorticity[d][...] += self._curl.outvar[d][...]
+        print 'forcing: conservative formulation'
+
+
+
+
+
diff --git a/HySoP/hysop/operator/discrete/low_pass_filt.py b/HySoP/hysop/operator/discrete/low_pass_filt.py
new file mode 100644
index 000000000..45104de99
--- /dev/null
+++ b/HySoP/hysop/operator/discrete/low_pass_filt.py
@@ -0,0 +1,123 @@
+# -*- coding: utf-8 -*-
+"""Discrete operator for vorticity low-pass filtering
+    (--> computation of base flow).
+.. currentmodule:: hysop.operator.discrete.low_pass_filt_vort
+
+"""
+from hysop.constants import debug
+from hysop.operator.discrete.discrete import DiscreteOperator
+from hysop.tools.profiler import profile
+
+class LowPassFilt(DiscreteOperator):
+    """Discretized vorticity filtering operator.
+    i.e. solve (with an explicit Euler scheme)
+    \f{eqnarray*}
+    \frac{\partial \bar{\omega}}{\partial t} &=& \Omega_c(\omega - \bar{\omega}))
+    \Leftrightarrow \bar{\omega^{n+1}} &=& \bar{\omega^{n}} +
+    \Delta t \Omega_c (\omega^n - \bar{\omega^n})
+    \Leftrightarrow \bar{\omega^{n+1}} &=& 
+    \bar{\omega^{n}} (1 - \Delta t \Omega_c) + 
+    \Delta t \Omega_c \omega^n
+    \f}
+    """
+
+    @debug
+    def __init__(self, cutFreq=None, **kwds):
+        """
+        Parameters
+        ----------
+        cutFreq : cutting circular frequency corresponding to the half of 
+        the eigenfrequency of the flow instability
+        **kwds : extra parameters for parent class.
+
+        """
+        super(LowPassFilt, self).__init__(**kwds)
+        topo = self.variables[0].topology
+        for v in self.variables:
+            msg = 'Multiresolution not implemented for penalization.'
+            assert v.topology == topo, msg
+        
+        ## variable
+        self.var = self.variables[0]
+        ## filtered variable
+        self.varFiltered = self.variables[1]
+        ## cutting circular frequency
+        self.cutFreq = cutFreq
+            
+    def _apply(self, dt):
+        nbc = self.varFiltered.nb_components
+        coeff = dt * self.cutFreq
+        for d in xrange(nbc):
+            self.varFiltered[d][...] *= (1.0 - coeff)
+            self.varFiltered[d][...] += self.var[d][...] * coeff
+        print 'filtering: non conservative formulation'
+
+    def apply(self, simulation=None):
+        assert simulation is not None, \
+            "Simulation parameter is required."
+        dt = simulation.timeStep
+        self._apply(dt)
+
+
+class LowPassFiltConserv(LowPassFilt):
+    """Discretized vorticity filtering operator.
+    i.e. solve (with an explicit Euler scheme and a CONSERVATIVE formulation)
+    \f{eqnarray*}
+    \frac{\partial \bar{\omega}}{\partial t} &=& \Omega_c(\omega - \bar{\omega}))
+    \Leftrightarrow \bar{\omega^{n+1}} &=& \bar{\omega^{n}} +
+    \nabla \times (\Delta t \Omega_c (u^n - \bar{u^n}))
+    \f}
+    """
+    
+    @debug
+    def __init__(self, vorticityFilt, velocity, velocityFilt, curl, **kwds):
+        """
+        Parameters
+        ----------
+        vorticityFilt, vorticity, velocityFilt: :class:`~hysop.fields.continuous.Field`
+        curl : :class:`~hysop.operator.differential`
+        internal operator to compute the curl of the forced velocity
+        **kwds : extra parameters for parent class.
+            
+        Notes
+        -----
+        velocity and velocityFilt are not modified by this operator.
+        vorticityFilt is an in-out parameter.
+        input and ouput variables of the curl are some local buffers.
+        """
+
+        assert 'variables' not in kwds, 'variables parameter is useless.'
+        super(LowPassFiltConserv, self).__init__(variables=[vorticityFilt,
+                                                            velocity,
+                                                            velocityFilt],
+                                                 **kwds)
+        # Input vector fields
+        self.vorticityFilt = vorticityFilt
+        self.velocity = velocity
+        self.velocityFilt = velocityFilt
+        # warning : a buffer is added for invar variable in curl
+        topo = self.velocity.topology
+        msg = 'Multiresolution not implemented for vort forcing.'
+        assert self.vorticityFilt.topology == topo, msg
+        assert self.velocityFilt.topology == topo, msg
+        self._curl = curl
+
+    def _apply(self, dt):
+        # Vorticity filtering
+        # warning : the buff0 array ensures "invar" to be 0
+        # outside the obstacle for the curl evaluation
+        invar = self._curl.invar
+        nbc = invar.nb_components
+        for d in xrange(nbc):
+            invar.data[d][...] = 0.0
+        coeff = dt * self.cutFreq
+        for d in xrange(nbc):
+            invar.data[d][...] = \
+                (self.velocity[d][...] -
+                 self.velocityFilt[d][...]) * coeff
+        self._curl.apply()
+        for d in xrange(self.vorticityFilt.nb_components):
+            self.vorticityFilt[d][...] += self._curl.outvar[d][...]
+        print 'filtering: conservative formulation'
+
+
diff --git a/HySoP/hysop/operator/discrete/monitoringPoints.py b/HySoP/hysop/operator/discrete/monitoringPoints.py
new file mode 100644
index 000000000..eac5f9154
--- /dev/null
+++ b/HySoP/hysop/operator/discrete/monitoringPoints.py
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+"""
+@file monitoringPoints.py
+Print time evolution of flow variables (velo, vorti)
+at a particular monitoring point in the wake
+"""
+from hysop.constants import debug
+from hysop.tools.profiler import profile
+import hysop.tools.numpywrappers as npw
+import scitools.filetable as ft
+import numpy as np
+from hysop.operator.discrete.discrete import DiscreteOperator
+
+
+class MonitoringPoints(DiscreteOperator):
+    """
+        Print time evolution of flow variables at a given position in the wake.
+    """
+    def __init__(self, velocity, vorticity, monitPt_coords, **kwds):
+        """
+        Constructor.
+        @param velocity field
+        @param vorticity field
+        @param monitPt_coords : list of coordinates corresponding
+            to the space location of the different monitoring points in the wake
+        """
+        ## velocity field
+        self.velocity = velocity
+        ## vorticity field
+        self.vorticity = vorticity
+        ## Monitoring point coordinates
+        self.monitPt_coords = monitPt_coords
+
+        assert 'variables' not in kwds, 'variables parameter is useless.'
+        super(MonitoringPoints, self).__init__(variables=[velocity, vorticity],
+                                       **kwds)
+        topo_v = self.velocity.topology
+        self.shape_v = self.velocity.data[0][topo_v.mesh.iCompute].shape
+        self.space_step = topo_v.mesh.space_step
+        self.length = topo_v.domain.length
+        self.origin = topo_v.domain.origin
+        self.coords = topo_v.mesh.coords
+        self.nbIter = 0
+        ## Normalized flow values at monitoring position (velNorm, vortNorm)
+        self.velNorm = 0.0
+        self.vortNorm = 0.0
+
+        # Is current processor working ? (Is monitPt_coords(z) in z-coords ?)
+        self.is_rk_computing = False
+        s = self._dim - 1
+        if (self.monitPt_coords[s] in self.coords[s]):
+            self.is_rk_computing = True
+
+    def _set_work_arrays(self, rwork=None, iwork=None):
+
+        v_ind = self.velocity.topology.mesh.iCompute
+        w_ind = self.vorticity.topology.mesh.iCompute
+        shape_v = self.velocity.data[0][v_ind].shape
+        shape_w = self.velocity.data[0][w_ind].shape
+        # setup for rwork, iwork is useless.
+        if rwork is None:
+            # ---  Local allocation ---
+            if shape_v == shape_w:
+                self._rwork = [npw.zeros(shape_v)]
+            else:
+                self._rwork = [npw.zeros(shape_v), npw.zeros(shape_w)]
+        else:
+            assert isinstance(rwork, list), 'rwork must be a list.'
+            # --- External rwork ---
+            self._rwork = rwork
+            if shape_v == shape_w:
+                assert len(self._rwork) == 1
+                assert self._rwork[0].shape == shape_v
+            else:
+                assert len(self._rwork) == 2
+                assert self._rwork[0].shape == shape_v
+                assert self._rwork[1].shape == shape_w
+
+    def get_work_properties(self):
+
+        v_ind = self.velocity.topology.mesh.iCompute
+        w_ind = self.vorticity.topology.mesh.iCompute
+        shape_v = self.velocity.data[0][v_ind].shape
+        shape_w = self.velocity.data[0][w_ind].shape
+        if shape_v == shape_w:
+            return {'rwork': [shape_v], 'iwork': None}
+        else:
+            return {'rwork': [shape_v, shape_w], 'iwork': None}
+
+    @debug
+    @profile
+    def apply(self, simulation=None):
+        assert simulation is not None, \
+            "Missing simulation value for computation."
+
+        time = simulation.time
+        ite = simulation.currentIteration
+        filename = self._writer.filename  #+ '_ite' + format(ite)
+
+        if self.is_rk_computing :
+            self.nbIter += 1
+            vd = self.velocity.data
+            vortd = self.vorticity.data
+            nbc = self.velocity.nb_components
+            tab = [self.monitPt_coords[0], self.monitPt_coords[1],
+                   self.monitPt_coords[2]]
+
+            ind = []
+            for d in xrange(nbc):
+                cond = np.where(abs(self.coords[d] - tab[d])
+                                < (self.space_step[d] * 0.5))
+                if cond[0].size > 0:
+                    ind.append(cond[d][0])
+                else:
+                    raise ValueError("Wrong set of coordinates.")
+
+
+            for i in xrange (self.shape_v[0]):
+                self.velNorm = np.sqrt(vd[0][ind[0],ind[1],ind[2]] ** 2 +
+                                       vd[1][ind[0],ind[1],ind[2]] ** 2 +
+                                       vd[2][ind[0],ind[1],ind[2]] ** 2)
+                self.vortNorm = np.sqrt(vortd[0][ind[0],ind[1],ind[2]] ** 2 +
+                                        vortd[1][ind[0],ind[1],ind[2]] ** 2 +
+                                        vortd[2][ind[0],ind[1],ind[2]] ** 2)
+
+
+            if self._writer is not None and self._writer.do_write(ite) :
+                self._writer.buffer[0, 0] = time
+                self._writer.buffer[0, 1] = self.velNorm
+                self._writer.buffer[0, 2] = self.vortNorm
+                self._writer.write()
+
+
+
diff --git a/HySoP/hysop/operator/discrete/residual.py b/HySoP/hysop/operator/discrete/residual.py
new file mode 100644
index 000000000..498924fd0
--- /dev/null
+++ b/HySoP/hysop/operator/discrete/residual.py
@@ -0,0 +1,117 @@
+# -*- coding: utf-8 -*-
+"""
+@file residual.py
+Compute and print the time evolution of the residual
+"""
+from hysop.constants import debug
+from hysop.tools.profiler import profile
+import hysop.tools.numpywrappers as npw
+import scitools.filetable as ft
+import numpy as np
+from hysop.operator.discrete.discrete import DiscreteOperator
+
+
+class Residual(DiscreteOperator):
+    """
+        Compute and print the residual as a function of time
+    """
+    def __init__(self, vorticity, **kwds):
+        """
+        Constructor.
+        @param vorticity field
+        """
+        ## vorticity field
+        self.vorticity = vorticity
+
+        assert 'variables' not in kwds, 'variables parameter is useless.'
+        super(Residual, self).__init__(variables=[vorticity], **kwds)
+        topo_w = self.vorticity.topology
+        self.shape_w = self.vorticity.data[0][topo_w.mesh.iCompute].shape
+        self.space_step = topo_w.mesh.space_step
+        self.length = topo_w.domain.length
+        self.origin = topo_w.domain.origin
+        self.coords = topo_w.mesh.coords
+        self.nbIter = 0
+        ## Global residual
+        self.residual = 0.0
+        # Time stem of the previous iteration
+        self._old_dt = None
+        # Define array to store vorticity field at previous iteration
+        self._vortPrev = [npw.zeros_like(d) for d in self.vorticity.data]
+
+    def _set_work_arrays(self, rwork=None, iwork=None):
+
+        w_ind = self.vorticity.topology.mesh.iCompute
+        shape_w = self.vorticity.data[0][w_ind].shape
+        # setup for rwork, iwork is useless.
+        if rwork is None:
+            # ---  Local allocation ---
+            self._rwork = [npw.zeros(shape_w)]
+        else:
+            assert isinstance(rwork, list), 'rwork must be a list.'
+            # --- External rwork ---
+            self._rwork = rwork
+            assert len(self._rwork) == 1
+            assert self._rwork[0].shape == shape_w
+
+    def get_work_properties(self):
+
+        w_ind = self.vorticity.topology.mesh.iCompute
+        shape_w = self.vorticity.data[0][w_ind].shape
+        return {'rwork': [shape_w], 'iwork': None}
+
+    def initialize_vortPrev(self):
+        
+        w_ind = self.vorticity.topology.mesh.iCompute
+        for d in xrange(self.vorticity.dimension):
+            self._vortPrev[d][w_ind] = self.vorticity[d][w_ind]
+
+    @debug
+    @profile
+    def apply(self, simulation=None):
+        assert simulation is not None, \
+            "Missing simulation value for computation."
+
+        time = simulation.time
+        ite = simulation.currentIteration
+        dt = simulation.timeStep
+        if self._old_dt is None:
+            self._old_dt = dt
+        
+        filename = self._writer.filename
+        
+        # Compute on local proc (w^(n+1)-w^n) ** 2
+        local_res = 0.
+        # get the list of computation points (no ghosts)
+        wd = self.vorticity
+        nbc = wd.nb_components
+        w_ind = self.vorticity.topology.mesh.iCompute
+        for i in xrange(nbc):
+            self._rwork[0][...] = (wd[i][w_ind] -
+                                   self._vortPrev[i][w_ind]) ** 2
+            local_res += npw.real_sum(self._rwork[0])
+
+        # --- Reduce local_res over all proc ---
+        sendbuff = npw.zeros((1))
+        recvbuff = npw.zeros((1))
+        sendbuff[:] = [local_res]
+        #
+        self.vorticity.topology.comm.Allreduce(sendbuff, recvbuff)
+        
+        # Update global residual
+        self.residual = np.sqrt(recvbuff[0])
+
+        # Print results, if required
+        if self._writer is not None and self._writer.do_write(ite) :
+            self._writer.buffer[0, 0] = time
+            self._writer.buffer[0, 1] = ite
+            self._writer.buffer[0, 2] = self.residual
+            self._writer.write()
+
+        # update vort(n-1) for next iteration
+        for i in xrange(nbc):
+            self._vortPrev[i][w_ind] = self.vorticity.data[i][w_ind]
+        self._old_dt = dt
+
+
+
diff --git a/HySoP/hysop/operator/forcing.py b/HySoP/hysop/operator/forcing.py
new file mode 100644
index 000000000..bfe03b4e5
--- /dev/null
+++ b/HySoP/hysop/operator/forcing.py
@@ -0,0 +1,155 @@
+# -*- coding: utf-8 -*-
+"""Operator implementing the forcing term in the NS, 
+   depending on the filtered field
+   (--> computation of base flow).
+
+.. currentmodule:: hysop.operator.forcing
+
+"""
+
+from hysop.operator.computational import Computational
+from hysop.operator.discrete.forcing import Forcing as DForcing
+from hysop.operator.discrete.forcing import ForcingConserv as DForcingCsv
+from hysop.constants import debug
+from hysop.operator.continuous import opsetup
+import hysop.default_methods as default
+from hysop.methods_keys import SpaceDiscretisation
+from hysop.numerics.finite_differences import FD_C_4,\
+    FD_C_2
+from hysop.operator.differential import Curl
+from hysop.fields.continuous import Field
+
+
+class Forcing(Computational):
+    """
+    Integrate a forcing term in the right hand side 
+    of the NS equations, depending on the filtered field.
+    i.e. solve
+    \f{eqnarray*}
+    \frac{\partial \omega}{\partial t} &=& -\chi(\omega - \bar{\omega}))
+    \f}
+    The strength of the forcing is chosen in the order
+    of the amplification rate related to the unstable flow.
+
+    """
+
+    @debug
+    def __init__(self, strength=None, **kwds):
+        """
+        Parameters
+        ----------
+        @param strength : strength of the filter
+        **kwds : extra parameters for parent class
+
+        """
+        super(Forcing, self).__init__(**kwds)
+
+        ## strength of the filter
+        self.strength = strength
+        
+        self.input = self.output = self.variables
+#        self.output = [self.variables[0]]
+
+    def discretize(self):
+        super(Forcing, self)._standard_discretize()
+        # all variables must have the same resolution
+        assert self._single_topo, 'multi-resolution case not allowed.'
+
+    @debug
+    def setup(self, rwork=None, iwork=None):
+        self.discrete_op = DForcing(
+            variables=self.discreteFields.values(),
+            strength=self.strength,
+            rwork=rwork, iwork=iwork)
+
+        self._is_uptodate = True
+
+
+class ForcingConserv(Forcing):
+    """
+    Integrate a forcing term in the right hand side
+    of the NS equations, depending on the filtered vorticity.
+    i.e. solve
+    \f{eqnarray*}
+    \frac{\partial \omega}{\partial t} &=& -\chi(\omega - \bar{\omega}))
+    \f}
+    The equation is solved using a CONSERVATIVE formulation.
+    The strength of the forcing is chosen in the order
+    of the amplification rate related to the unstable flow.
+        
+    """
+    @debug
+    def __init__(self, velocity, vorticity, velocityFilt, **kwds):
+        """
+        Parameters
+        ----------
+        @param[in] velocity, vorticity, velocityFilt fields
+        @param[in, out] forced vorticity field
+        @param strength : strength of the filter
+        **kwds : extra parameters for parent class
+            
+        Notes
+        -----
+        velocity and velocityFilt are not modified by this operator.
+        vorticity is an in-out parameter.
+        """
+        assert 'variables' not in kwds, 'variables parameter is useless.'
+        super(ForcingConserv, self).__init__(
+            variables=[velocity, vorticity, velocityFilt], **kwds)
+        ## velocity variable
+        self.velocity = velocity
+        ## vorticity variable
+        self.vorticity = vorticity
+        ## filtered velocity variable
+        self.velocityFilt = velocityFilt
+        # A method is required to set how the curl will be computed.
+        if self.method is None:
+            self.method = default.DIFFERENTIAL
+        # operator to compute buffer = curl(penalised velocity)
+        self._curl = None
+
+        self.input = [self.velocity, self.vorticity, self.velocityFilt]
+        self.output = [self.vorticity]
+
+    def discretize(self):
+    
+        if self.method[SpaceDiscretisation] is FD_C_4:
+            # Finite differences method
+            # Minimal number of ghost points
+            nb_ghosts = 2
+        elif self.method[SpaceDiscretisation] is FD_C_2:
+            nb_ghosts = 1
+        else:
+            raise ValueError("Unknown method for space discretization of the\
+                differential operator in penalization.")
+        super(ForcingConserv, self)._standard_discretize(nb_ghosts)
+        # all variables must have the same resolution
+        assert self._single_topo, 'multi-resolution case not allowed.'
+        topo = self.variables[self.velocity]
+        invar = Field(domain=self.velocity.domain,
+                      name='curl_in', is_vector=True)
+        dimension = self.domain.dimension
+        outvar = Field(domain=self.velocity.domain,
+                       name='curl_out',
+                       is_vector=dimension == 3)
+        self._curl = Curl(invar=invar, outvar=outvar,
+                          discretization=topo, method=self.method)
+        self._curl.discretize()
+
+    def get_work_properties(self):
+        return self._curl.get_work_properties()
+
+    @debug
+    @opsetup
+    def setup(self, rwork=None, iwork=None):
+        self._curl.setup(rwork, iwork)
+        self.discrete_op = DForcingCsv(
+            vorticity=self.discreteFields[self.vorticity],
+            velocity=self.discreteFields[self.velocity],
+            velocityFilt=self.discreteFields[self.velocityFilt],
+            curl=self._curl.discrete_op,
+            strength=self.strength,
+            rwork=rwork, iwork=iwork)
+        self._is_uptodate = True
+
+
diff --git a/HySoP/hysop/operator/low_pass_filt.py b/HySoP/hysop/operator/low_pass_filt.py
new file mode 100644
index 000000000..91f03f28b
--- /dev/null
+++ b/HySoP/hysop/operator/low_pass_filt.py
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+"""Operator for any field low-pass filtering
+   (--> computation of base flow).
+
+.. currentmodule:: hysop.operator.low_pass_filt
+
+"""
+
+from hysop.operator.computational import Computational
+from hysop.operator.discrete.low_pass_filt import LowPassFilt as DFilt
+from hysop.operator.discrete.low_pass_filt import LowPassFiltConserv as DFiltCsv
+from hysop.constants import debug
+from hysop.operator.continuous import opsetup
+import hysop.default_methods as default
+from hysop.methods_keys import SpaceDiscretisation
+from hysop.numerics.finite_differences import FD_C_4,\
+    FD_C_2
+from hysop.operator.differential import Curl
+from hysop.fields.continuous import Field
+
+
+class LowPassFilt(Computational):
+    """
+    Provides a filtered field by low-pass filtering
+    the flow with the frequency of the instability divided by 2.
+    i.e. solve
+    \f{eqnarray*}
+    \frac{\partial \bar{\omega}}{\partial t} &=& \Omega_c(\omega - \bar{\omega}))
+    \f}
+
+    """
+
+    @debug
+    def __init__(self, cutFreq=None, **kwds):
+        """
+        Parameters
+        ----------
+        @param cutFreq : cutting circular frequency corresponding to the half of
+        the eigenfrequency of the flow instability
+        **kwds : extra parameters for parent class
+
+        """
+        super(LowPassFilt, self).__init__(**kwds)
+
+        ## cutting circular frequency
+        self.cutFreq = cutFreq
+        
+        self.input = self.output = self.variables
+
+    def discretize(self):
+        super(LowPassFilt, self)._standard_discretize()
+        # all variables must have the same resolution
+        assert self._single_topo, 'multi-resolution case not allowed.'
+
+    @debug
+    def setup(self, rwork=None, iwork=None):
+        self.discrete_op = DFilt(
+            variables=self.discreteFields.values(),
+            cutFreq=self.cutFreq,
+            rwork=rwork, iwork=iwork)
+
+        self._is_uptodate = True
+
+
+class LowPassFiltConserv(LowPassFilt):
+    """
+        Provides a filtered field by low-pass filtering
+        the flow with the frequency of the instability divided by 2.
+        i.e. solve
+        \f{eqnarray*}
+        \frac{\partial \bar{\omega}}{\partial t} &=& \Omega_c(\omega - \bar{\omega}))
+        \f}
+        The equation is solved using a CONSERVATIVE formulation.
+        
+        """
+    
+    @debug
+    def __init__(self, velocity, vorticityFilt, velocityFilt, **kwds):
+        """
+        Parameters
+        ----------
+        @param[in] velocity, vorticityFilt, velocityFilt fields
+        @param[in, out] vorticityFilt field (i.e. filtered vorticity field)
+        @param strength : strength of the filter
+        **kwds : extra parameters for parent class
+            
+        Notes
+        -----
+        velocity and velocityFilt are not modified by this operator.
+        vorticityFilt is an in-out parameter.
+        """
+        assert 'variables' not in kwds, 'variables parameter is useless.'
+        super(LowPassFiltConserv, self).__init__(
+            variables=[velocity, vorticityFilt, velocityFilt], **kwds)
+
+        ## velocity variable
+        self.velocity = velocity
+        ## filtered vorticity variable
+        self.vorticityFilt = vorticityFilt
+        ## filtered velocity variable
+        self.velocityFilt = velocityFilt
+        # A method is required to set how the curl will be computed.
+        if self.method is None:
+            self.method = default.DIFFERENTIAL
+        # operator to compute buffer = curl(penalised velocity)
+        self._curl = None
+
+        self.input = [self.velocity, self.vorticityFilt, self.velocityFilt]
+        self.output = [self.vorticityFilt]
+
+    def discretize(self):
+    
+        if self.method[SpaceDiscretisation] is FD_C_4:
+            # Finite differences method
+            # Minimal number of ghost points
+            nb_ghosts = 2
+        elif self.method[SpaceDiscretisation] is FD_C_2:
+            nb_ghosts = 1
+        else:
+            raise ValueError("Unknown method for space discretization of the\
+                differential operator in penalization.")
+        super(LowPassFiltConserv, self)._standard_discretize(nb_ghosts)
+        # all variables must have the same resolution
+        assert self._single_topo, 'multi-resolution case not allowed.'
+        topo = self.variables[self.velocity]
+        invar = Field(domain=self.velocity.domain,
+                      name='curl_in', is_vector=True)
+        dimension = self.domain.dimension
+        outvar = Field(domain=self.velocity.domain,
+                       name='curl_out',
+                       is_vector=dimension == 3)
+        self._curl = Curl(invar=invar, outvar=outvar,
+                          discretization=topo, method=self.method)
+        self._curl.discretize()
+
+    def get_work_properties(self):
+        return self._curl.get_work_properties()
+    
+    @debug
+    @opsetup
+    def setup(self, rwork=None, iwork=None):
+        self._curl.setup(rwork, iwork)
+        self.discrete_op = DFiltCsv(
+            vorticityFilt=self.discreteFields[self.vorticityFilt],
+            velocity=self.discreteFields[self.velocity],
+            velocityFilt=self.discreteFields[self.velocityFilt],
+            curl=self._curl.discrete_op,
+            cutFreq=self.cutFreq,
+            rwork=rwork, iwork=iwork)
+        self._is_uptodate = True
+
+
+
diff --git a/HySoP/hysop/operator/monitoringPoints.py b/HySoP/hysop/operator/monitoringPoints.py
new file mode 100644
index 000000000..3066fda28
--- /dev/null
+++ b/HySoP/hysop/operator/monitoringPoints.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""
+@file monitoringPoints.py
+Print time evolution of flow variables (velo, vorti)
+at a particular monitoring point in the wake
+"""
+from hysop.operator.discrete.monitoringPoints import MonitoringPoints as MonitD
+from hysop.operator.computational import Computational
+from hysop.operator.continuous import opsetup
+
+
+class MonitoringPoints(Computational):
+    """
+    Compute and print velo/vorti profiles
+    """
+
+    def __init__(self, velocity, vorticity, monitPt_coords, **kwds):
+        """
+        Constructor.
+        @param velocity field
+        @param vorticity field
+        @param monitPts_coords : coordinates corresponding
+            to the space location of the monitoring point in the wake
+
+        Default file name = 'monit.dat'
+        See hysop.tools.io_utils.Writer for details
+        """
+        assert 'variables' not in kwds, 'variables parameter is useless.'
+        super(MonitoringPoints, self).__init__(variables=
+                                               [velocity, vorticity],
+                                               **kwds)
+        ## velocity field
+        self.velocity = velocity
+        ## vorticity field
+        self.vorticity = vorticity
+        ## coordinates of the monitoring point
+        self.monitPt_coords = monitPt_coords
+        self.input = [velocity, vorticity]
+        self.output = []
+
+    def get_work_properties(self):
+        if not self._is_discretized:
+            msg = 'The operator must be discretized '
+            msg += 'before any call to this function.'
+            raise RuntimeError(msg)
+        vd = self.discreteFields[self.velocity]
+        wd = self.discreteFields[self.vorticity]
+        v_ind = vd.topology.mesh.iCompute
+        w_ind = wd.topology.mesh.iCompute
+        shape_v = vd[0][v_ind].shape
+        shape_w = wd[0][w_ind].shape
+        if shape_v == shape_w:
+            return {'rwork': [shape_v], 'iwork': None}
+        else:
+            return {'rwork': [shape_v, shape_w], 'iwork': None}
+
+    @opsetup
+    def setup(self, rwork=None, iwork=None):
+        if not self._is_uptodate:
+
+            self.discrete_op = MonitD(self.discreteFields[self.velocity],
+                                      self.discreteFields[self.vorticity],
+                                      self.monitPt_coords, rwork=rwork)
+            # Output setup
+            self._set_io('monit', (1, 3))
+            self.discrete_op.setWriter(self._writer)
+            self._is_uptodate = True
+
diff --git a/HySoP/hysop/operator/residual.py b/HySoP/hysop/operator/residual.py
new file mode 100644
index 000000000..1a5de67ae
--- /dev/null
+++ b/HySoP/hysop/operator/residual.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+"""
+@file residual.py
+Compute and print the time evolution of the residual
+"""
+from hysop.operator.discrete.residual import Residual as ResD
+from hysop.operator.computational import Computational
+from hysop.operator.continuous import opsetup
+
+
+class Residual(Computational):
+    """
+    Compute and print the residual time evolution
+    """
+
+    def __init__(self, vorticity, **kwds):
+        """
+        Constructor.
+        @param vorticity field
+
+        Default file name = 'residual.dat'
+        See hysop.tools.io_utils.Writer for details
+        """
+        assert 'variables' not in kwds, 'variables parameter is useless.'
+        super(Residual, self).__init__(variables=[vorticity], **kwds)
+        ## vorticity field
+        self.vorticity = vorticity
+        self.input = [vorticity]
+        self.output = []
+
+    def get_work_properties(self):
+        if not self._is_discretized:
+            msg = 'The operator must be discretized '
+            msg += 'before any call to this function.'
+            raise RuntimeError(msg)
+        wd = self.discreteFields[self.vorticity]
+        w_ind = wd.topology.mesh.iCompute
+        shape_w = wd[0][w_ind].shape
+        return {'rwork': [shape_w], 'iwork': None}
+
+    @opsetup
+    def setup(self, rwork=None, iwork=None):
+        if not self._is_uptodate:
+
+            self.discrete_op = ResD(self.discreteFields[self.vorticity],
+                                    rwork=rwork)
+            # Initialization of w^(n-1) vorticity value
+            self.discrete_op.initialize_vortPrev()
+            # Output setup
+            self._set_io('residual', (1, 3))
+            self.discrete_op.setWriter(self._writer)
+            self._is_uptodate = True
+
-- 
GitLab