diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_LmhnaWdub3Jl
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,40 @@
+syntax: glob
+
+*.pyd*
+*.pyc
+*~
+*temp
+*.html
+
+\#*\#
+
+doc/_build/*
+doc/**generated/*
+doc/html.zip
+doc/ipynb/*_files/*.png
+doc/ipynb/*.rst
+
+*.ipynb_checkpoints*
+*Untitled*.ipynb
+
+*.egg-info/*
+
+build/*
+dist/*.tar.gz
+dist*
+
+*.so
+
+*fftw2Dmpi_cylib.c
+*fftw2Dmpi_ccylib.c
+*.o
+
+*setofvariables.c
+*operators.c
+*cyfunc.c
+
+*cy.c
+
+scripts\Experiments\path_working_exp.txt
+
+scripts/Simul/Launch/Profile.prof
\ No newline at end of file
diff --git a/CHANGES.rst b/CHANGES.rst
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Q0hBTkdFUy5yc3Q=
--- /dev/null
+++ b/CHANGES.rst
@@ -0,0 +1,7 @@
+
+0.0.8a
+------
+
+- The fluiddyn package now only contains base files for the FluidDyn
+  project. Other packages (fluidsim, fluidlab, ...) provide other
+  files.
diff --git a/HOWTO.txt b/HOWTO.txt
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_SE9XVE8udHh0
--- /dev/null
+++ b/HOWTO.txt
@@ -0,0 +1,11 @@
+How to
+======
+
+How to upload to PyPI — the Python Package Index
+------------------------------------------------
+
+First, run the tests::
+  python -m unittest discover
+
+With a correct $HOME/.pypirc, run::
+  python setup.py sdist upload
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_TElDRU5TRS50eHQ=
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,518 @@
+
+  CeCILL FREE SOFTWARE LICENSE AGREEMENT
+
+Version 2.1 dated 2013-06-21
+
+
+    Notice
+
+This Agreement is a Free Software license agreement that is the result
+of discussions between its authors in order to ensure compliance with
+the two main principles guiding its drafting:
+
+  * firstly, compliance with the principles governing the distribution
+    of Free Software: access to source code, broad rights granted to users,
+  * secondly, the election of a governing law, French law, with which it
+    is conformant, both as regards the law of torts and intellectual
+    property law, and the protection that it offers to both authors and
+    holders of the economic rights over software.
+
+The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre]) 
+license are: 
+
+Commissariat à l'énergie atomique et aux énergies alternatives - CEA, a
+public scientific, technical and industrial research establishment,
+having its principal place of business at 25 rue Leblanc, immeuble Le
+Ponant D, 75015 Paris, France.
+
+Centre National de la Recherche Scientifique - CNRS, a public scientific
+and technological establishment, having its principal place of business
+at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
+
+Institut National de Recherche en Informatique et en Automatique -
+Inria, a public scientific and technological establishment, having its
+principal place of business at Domaine de Voluceau, Rocquencourt, BP
+105, 78153 Le Chesnay cedex, France.
+
+
+    Preamble
+
+The purpose of this Free Software license agreement is to grant users
+the right to modify and redistribute the software governed by this
+license within the framework of an open source distribution model.
+
+The exercising of this right is conditional upon certain obligations for
+users so as to preserve this status for all subsequent redistributions.
+
+In consideration of access to the source code and the rights to copy,
+modify and redistribute granted by the license, users are provided only
+with a limited warranty and the software's author, the holder of the
+economic rights, and the successive licensors only have limited liability.
+
+In this respect, the risks associated with loading, using, modifying
+and/or developing or reproducing the software by the user are brought to
+the user's attention, given its Free Software status, which may make it
+complicated to use, with the result that its use is reserved for
+developers and experienced professionals having in-depth computer
+knowledge. Users are therefore encouraged to load and test the
+suitability of the software as regards their requirements in conditions
+enabling the security of their systems and/or data to be ensured and,
+more generally, to use and operate it in the same conditions of
+security. This Agreement may be freely reproduced and published,
+provided it is not altered, and that no provisions are either added or
+removed herefrom.
+
+This Agreement may apply to any or all software for which the holder of
+the economic rights decides to submit the use thereof to its provisions.
+
+Frequently asked questions can be found on the official website of the
+CeCILL licenses family (http://www.cecill.info/index.en.html) for any 
+necessary clarification.
+
+
+    Article 1 - DEFINITIONS
+
+For the purpose of this Agreement, when the following expressions
+commence with a capital letter, they shall have the following meaning:
+
+Agreement: means this license agreement, and its possible subsequent
+versions and annexes.
+
+Software: means the software in its Object Code and/or Source Code form
+and, where applicable, its documentation, "as is" when the Licensee
+accepts the Agreement.
+
+Initial Software: means the Software in its Source Code and possibly its
+Object Code form and, where applicable, its documentation, "as is" when
+it is first distributed under the terms and conditions of the Agreement.
+
+Modified Software: means the Software modified by at least one
+Contribution.
+
+Source Code: means all the Software's instructions and program lines to
+which access is required so as to modify the Software.
+
+Object Code: means the binary files originating from the compilation of
+the Source Code.
+
+Holder: means the holder(s) of the economic rights over the Initial
+Software.
+
+Licensee: means the Software user(s) having accepted the Agreement.
+
+Contributor: means a Licensee having made at least one Contribution.
+
+Licensor: means the Holder, or any other individual or legal entity, who
+distributes the Software under the Agreement.
+
+Contribution: means any or all modifications, corrections, translations,
+adaptations and/or new functions integrated into the Software by any or
+all Contributors, as well as any or all Internal Modules.
+
+Module: means a set of sources files including their documentation that
+enables supplementary functions or services in addition to those offered
+by the Software.
+
+External Module: means any or all Modules, not derived from the
+Software, so that this Module and the Software run in separate address
+spaces, with one calling the other when they are run.
+
+Internal Module: means any or all Module, connected to the Software so
+that they both execute in the same address space.
+
+GNU GPL: means the GNU General Public License version 2 or any
+subsequent version, as published by the Free Software Foundation Inc.
+
+GNU Affero GPL: means the GNU Affero General Public License version 3 or
+any subsequent version, as published by the Free Software Foundation Inc.
+
+EUPL: means the European Union Public License version 1.1 or any
+subsequent version, as published by the European Commission.
+
+Parties: mean both the Licensee and the Licensor.
+
+These expressions may be used both in singular and plural form.
+
+
+    Article 2 - PURPOSE
+
+The purpose of the Agreement is the grant by the Licensor to the
+Licensee of a non-exclusive, transferable and worldwide license for the
+Software as set forth in Article 5 <#scope> hereinafter for the whole
+term of the protection granted by the rights over said Software.
+
+
+    Article 3 - ACCEPTANCE
+
+3.1 The Licensee shall be deemed as having accepted the terms and
+conditions of this Agreement upon the occurrence of the first of the
+following events:
+
+  * (i) loading the Software by any or all means, notably, by
+    downloading from a remote server, or by loading from a physical medium;
+  * (ii) the first time the Licensee exercises any of the rights granted
+    hereunder.
+
+3.2 One copy of the Agreement, containing a notice relating to the
+characteristics of the Software, to the limited warranty, and to the
+fact that its use is restricted to experienced users has been provided
+to the Licensee prior to its acceptance as set forth in Article 3.1
+<#accepting> hereinabove, and the Licensee hereby acknowledges that it
+has read and understood it.
+
+
+    Article 4 - EFFECTIVE DATE AND TERM
+
+
+      4.1 EFFECTIVE DATE
+
+The Agreement shall become effective on the date when it is accepted by
+the Licensee as set forth in Article 3.1 <#accepting>.
+
+
+      4.2 TERM
+
+The Agreement shall remain in force for the entire legal term of
+protection of the economic rights over the Software.
+
+
+    Article 5 - SCOPE OF RIGHTS GRANTED
+
+The Licensor hereby grants to the Licensee, who accepts, the following
+rights over the Software for any or all use, and for the term of the
+Agreement, on the basis of the terms and conditions set forth hereinafter.
+
+Besides, if the Licensor owns or comes to own one or more patents
+protecting all or part of the functions of the Software or of its
+components, the Licensor undertakes not to enforce the rights granted by
+these patents against successive Licensees using, exploiting or
+modifying the Software. If these patents are transferred, the Licensor
+undertakes to have the transferees subscribe to the obligations set
+forth in this paragraph.
+
+
+      5.1 RIGHT OF USE
+
+The Licensee is authorized to use the Software, without any limitation
+as to its fields of application, with it being hereinafter specified
+that this comprises:
+
+ 1. permanent or temporary reproduction of all or part of the Software
+    by any or all means and in any or all form.
+
+ 2. loading, displaying, running, or storing the Software on any or all
+    medium.
+
+ 3. entitlement to observe, study or test its operation so as to
+    determine the ideas and principles behind any or all constituent
+    elements of said Software. This shall apply when the Licensee
+    carries out any or all loading, displaying, running, transmission or
+    storage operation as regards the Software, that it is entitled to
+    carry out hereunder.
+
+
+      5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
+
+The right to make Contributions includes the right to translate, adapt,
+arrange, or make any or all modifications to the Software, and the right
+to reproduce the resulting software.
+
+The Licensee is authorized to make any or all Contributions to the
+Software provided that it includes an explicit notice that it is the
+author of said Contribution and indicates the date of the creation thereof.
+
+
+      5.3 RIGHT OF DISTRIBUTION
+
+In particular, the right of distribution includes the right to publish,
+transmit and communicate the Software to the general public on any or
+all medium, and by any or all means, and the right to market, either in
+consideration of a fee, or free of charge, one or more copies of the
+Software by any means.
+
+The Licensee is further authorized to distribute copies of the modified
+or unmodified Software to third parties according to the terms and
+conditions set forth hereinafter.
+
+
+        5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
+
+The Licensee is authorized to distribute true copies of the Software in
+Source Code or Object Code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+ 1. a copy of the Agreement,
+
+ 2. a notice relating to the limitation of both the Licensor's warranty
+    and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the Object Code of the Software is
+redistributed, the Licensee allows effective access to the full Source
+Code of the Software for a period of at least three years from the
+distribution of the Software, it being understood that the additional
+acquisition cost of the Source Code shall not exceed the cost of the
+data transfer.
+
+
+        5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
+
+When the Licensee makes a Contribution to the Software, the terms and
+conditions for the distribution of the resulting Modified Software
+become subject to all the provisions of this Agreement.
+
+The Licensee is authorized to distribute the Modified Software, in
+source code or object code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+ 1. a copy of the Agreement,
+
+ 2. a notice relating to the limitation of both the Licensor's warranty
+    and liability as set forth in Articles 8 and 9,
+
+and, in the event that only the object code of the Modified Software is
+redistributed,
+
+ 3. a note stating the conditions of effective access to the full source
+    code of the Modified Software for a period of at least three years
+    from the distribution of the Modified Software, it being understood
+    that the additional acquisition cost of the source code shall not
+    exceed the cost of the data transfer.
+
+
+        5.3.3 DISTRIBUTION OF EXTERNAL MODULES
+
+When the Licensee has developed an External Module, the terms and
+conditions of this Agreement do not apply to said External Module, that
+may be distributed under a separate license agreement.
+
+
+        5.3.4 COMPATIBILITY WITH OTHER LICENSES
+
+The Licensee can include a code that is subject to the provisions of one
+of the versions of the GNU GPL, GNU Affero GPL and/or EUPL in the
+Modified or unmodified Software, and distribute that entire code under
+the terms of the same version of the GNU GPL, GNU Affero GPL and/or EUPL.
+
+The Licensee can include the Modified or unmodified Software in a code
+that is subject to the provisions of one of the versions of the GNU GPL,
+GNU Affero GPL and/or EUPL and distribute that entire code under the
+terms of the same version of the GNU GPL, GNU Affero GPL and/or EUPL.
+
+
+    Article 6 - INTELLECTUAL PROPERTY
+
+
+      6.1 OVER THE INITIAL SOFTWARE
+
+The Holder owns the economic rights over the Initial Software. Any or
+all use of the Initial Software is subject to compliance with the terms
+and conditions under which the Holder has elected to distribute its work
+and no one shall be entitled to modify the terms and conditions for the
+distribution of said Initial Software.
+
+The Holder undertakes that the Initial Software will remain ruled at
+least by this Agreement, for the duration set forth in Article 4.2 <#term>.
+
+
+      6.2 OVER THE CONTRIBUTIONS
+
+The Licensee who develops a Contribution is the owner of the
+intellectual property rights over this Contribution as defined by
+applicable law.
+
+
+      6.3 OVER THE EXTERNAL MODULES
+
+The Licensee who develops an External Module is the owner of the
+intellectual property rights over this External Module as defined by
+applicable law and is free to choose the type of agreement that shall
+govern its distribution.
+
+
+      6.4 JOINT PROVISIONS
+
+The Licensee expressly undertakes:
+
+ 1. not to remove, or modify, in any manner, the intellectual property
+    notices attached to the Software;
+
+ 2. to reproduce said notices, in an identical manner, in the copies of
+    the Software modified or not.
+
+The Licensee undertakes not to directly or indirectly infringe the
+intellectual property rights on the Software of the Holder and/or
+Contributors, and to take, where applicable, vis-à-vis its staff, any
+and all measures required to ensure respect of said intellectual
+property rights of the Holder and/or Contributors.
+
+
+    Article 7 - RELATED SERVICES
+
+7.1 Under no circumstances shall the Agreement oblige the Licensor to
+provide technical assistance or maintenance services for the Software.
+
+However, the Licensor is entitled to offer this type of services. The
+terms and conditions of such technical assistance, and/or such
+maintenance, shall be set forth in a separate instrument. Only the
+Licensor offering said maintenance and/or technical assistance services
+shall incur liability therefor.
+
+7.2 Similarly, any Licensor is entitled to offer to its licensees, under
+its sole responsibility, a warranty, that shall only be binding upon
+itself, for the redistribution of the Software and/or the Modified
+Software, under terms and conditions that it is free to decide. Said
+warranty, and the financial terms and conditions of its application,
+shall be subject of a separate instrument executed between the Licensor
+and the Licensee.
+
+
+    Article 8 - LIABILITY
+
+8.1 Subject to the provisions of Article 8.2, the Licensee shall be
+entitled to claim compensation for any direct loss it may have suffered
+from the Software as a result of a fault on the part of the relevant
+Licensor, subject to providing evidence thereof.
+
+8.2 The Licensor's liability is limited to the commitments made under
+this Agreement and shall not be incurred as a result of in particular:
+(i) loss due the Licensee's total or partial failure to fulfill its
+obligations, (ii) direct or consequential loss that is suffered by the
+Licensee due to the use or performance of the Software, and (iii) more
+generally, any consequential loss. In particular the Parties expressly
+agree that any or all pecuniary or business loss (i.e. loss of data,
+loss of profits, operating loss, loss of customers or orders,
+opportunity cost, any disturbance to business activities) or any or all
+legal proceedings instituted against the Licensee by a third party,
+shall constitute consequential loss and shall not provide entitlement to
+any or all compensation from the Licensor.
+
+
+    Article 9 - WARRANTY
+
+9.1 The Licensee acknowledges that the scientific and technical
+state-of-the-art when the Software was distributed did not enable all
+possible uses to be tested and verified, nor for the presence of
+possible defects to be detected. In this respect, the Licensee's
+attention has been drawn to the risks associated with loading, using,
+modifying and/or developing and reproducing the Software which are
+reserved for experienced users.
+
+The Licensee shall be responsible for verifying, by any or all means,
+the suitability of the product for its requirements, its good working
+order, and for ensuring that it shall not cause damage to either persons
+or properties.
+
+9.2 The Licensor hereby represents, in good faith, that it is entitled
+to grant all the rights over the Software (including in particular the
+rights set forth in Article 5 <#scope>).
+
+9.3 The Licensee acknowledges that the Software is supplied "as is" by
+the Licensor without any other express or tacit warranty, other than
+that provided for in Article 9.2 <#good-faith> and, in particular,
+without any warranty as to its commercial value, its secured, safe,
+innovative or relevant nature.
+
+Specifically, the Licensor does not warrant that the Software is free
+from any error, that it will operate without interruption, that it will
+be compatible with the Licensee's own equipment and software
+configuration, nor that it will meet the Licensee's requirements.
+
+9.4 The Licensor does not either expressly or tacitly warrant that the
+Software does not infringe any third party intellectual property right
+relating to a patent, software or any other property right. Therefore,
+the Licensor disclaims any and all liability towards the Licensee
+arising out of any or all proceedings for infringement that may be
+instituted in respect of the use, modification and redistribution of the
+Software. Nevertheless, should such proceedings be instituted against
+the Licensee, the Licensor shall provide it with technical and legal
+expertise for its defense. Such technical and legal expertise shall be
+decided on a case-by-case basis between the relevant Licensor and the
+Licensee pursuant to a memorandum of understanding. The Licensor
+disclaims any and all liability as regards the Licensee's use of the
+name of the Software. No warranty is given as regards the existence of
+prior rights over the name of the Software or as regards the existence
+of a trademark.
+
+
+    Article 10 - TERMINATION
+
+10.1 In the event of a breach by the Licensee of its obligations
+hereunder, the Licensor may automatically terminate this Agreement
+thirty (30) days after notice has been sent to the Licensee and has
+remained ineffective.
+
+10.2 A Licensee whose Agreement is terminated shall no longer be
+authorized to use, modify or distribute the Software. However, any
+licenses that it may have granted prior to termination of the Agreement
+shall remain valid subject to their having been granted in compliance
+with the terms and conditions hereof.
+
+
+    Article 11 - MISCELLANEOUS
+
+
+      11.1 EXCUSABLE EVENTS
+
+Neither Party shall be liable for any or all delay, or failure to
+perform the Agreement, that may be attributable to an event of force
+majeure, an act of God or an outside cause, such as defective
+functioning or interruptions of the electricity or telecommunications
+networks, network paralysis following a virus attack, intervention by
+government authorities, natural disasters, water damage, earthquakes,
+fire, explosions, strikes and labor unrest, war, etc.
+
+11.2 Any failure by either Party, on one or more occasions, to invoke
+one or more of the provisions hereof, shall under no circumstances be
+interpreted as being a waiver by the interested Party of its right to
+invoke said provision(s) subsequently.
+
+11.3 The Agreement cancels and replaces any or all previous agreements,
+whether written or oral, between the Parties and having the same
+purpose, and constitutes the entirety of the agreement between said
+Parties concerning said purpose. No supplement or modification to the
+terms and conditions hereof shall be effective as between the Parties
+unless it is made in writing and signed by their duly authorized
+representatives.
+
+11.4 In the event that one or more of the provisions hereof were to
+conflict with a current or future applicable act or legislative text,
+said act or legislative text shall prevail, and the Parties shall make
+the necessary amendments so as to comply with said act or legislative
+text. All other provisions shall remain effective. Similarly, invalidity
+of a provision of the Agreement, for any reason whatsoever, shall not
+cause the Agreement as a whole to be invalid.
+
+
+      11.5 LANGUAGE
+
+The Agreement is drafted in both French and English and both versions
+are deemed authentic.
+
+
+    Article 12 - NEW VERSIONS OF THE AGREEMENT
+
+12.1 Any person is authorized to duplicate and distribute copies of this
+Agreement.
+
+12.2 So as to ensure coherence, the wording of this Agreement is
+protected and may only be modified by the authors of the License, who
+reserve the right to periodically publish updates or new versions of the
+Agreement, each with a separate number. These subsequent versions may
+address new issues encountered by Free Software.
+
+12.3 Any Software distributed under a given version of the Agreement may
+only be subsequently distributed under the same version of the Agreement
+or a subsequent version, subject to the provisions of Article 5.3.4
+<#compatibility>.
+
+
+    Article 13 - GOVERNING LAW AND JURISDICTION
+
+13.1 The Agreement is governed by French law. The Parties agree to
+endeavor to seek an amicable solution to any disagreements or disputes
+that may arise during the performance of the Agreement.
+
+13.2 Failing an amicable solution within two (2) months as from their
+occurrence, and unless emergency proceedings are necessary, the
+disagreements or disputes shall be referred to the Paris Courts having
+jurisdiction, by the more diligent Party.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_TUFOSUZFU1QuaW4=
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,8 @@
+include *.txt
+include *.rst
+recursive-include doc *.rst
+recursive-include fluidsim *.h
+recursive-include fluidsim *.pyx
+recursive-include fluidsim *.pxd
+recursive-include fluidsim *.c
+recursive-include include *.h
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_TWFrZWZpbGU=
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,6 @@
+
+clean_so:
+	find fluidsim -name "*.so" -delete
+
+tests:
+	python -m unittest discover
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_UkVBRE1FLnJzdA==
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,52 @@
+========
+FluidSim
+========
+
+*Framework for studying fluid dynamics by numerical simulation.*
+
+`Package Documentation <http://pythonhosted.org/fluidsim>`__
+
+FluidSim is the numerically oriented package of the `FluidDyn project
+<http://pythonhosted.org/fluiddyn>`__.  The project is still in a
+testing stage so it is still pretty unstable and many of its planned
+features have not yet been implemented.
+
+It has first been developed by `Pierre Augier
+<http://www.legi.grenoble-inp.fr/people/Pierre.Augier/>`_ (CNRS
+researcher at `LEGI <http://www.legi.grenoble-inp.fr>`_, Grenoble) at
+KTH (Stockholm) as a numerical code to solve fluid equations in a
+periodic two-dimensional space with pseudo-spectral methods.
+
+*Key words and ambitions*: fluid dynamics research with Python (2.7 or
+>= 3.3); modular, object-oriented, collaborative, tested and
+documented, free and open-source software.
+
+License
+-------
+
+FluidDyn is distributed under the CeCILL_ License, a GPL compatible
+french license.
+
+.. _CeCILL: http://www.cecill.info/index.en.html
+
+Installation
+------------
+
+You can get the source code from `Bitbucket
+<https://bitbucket.org/fluiddyn/fluidsim>`__ or from `the Python
+Package Index <https://pypi.python.org/pypi/fluidsim/>`__.
+
+The development mode is often useful. From the root directory::
+
+  sudo python setup.py develop
+
+Tests
+-----
+
+From the root directory::
+
+  make tests
+
+Or, from the root directory or from any of the "test" directories::
+
+  python -m unittest discover
diff --git a/fluidsim/__init__.py b/fluidsim/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vX19pbml0X18ucHk=
--- /dev/null
+++ b/fluidsim/__init__.py
@@ -0,0 +1,34 @@
+"""Numerical simulations (:mod:`fluidsim`)
+================================================
+
+.. _simul:
+.. currentmodule:: fluidsim
+
+The package :mod:`fluidsim` **will** provide an object-oriented
+toolkit for doing numerical simulations of different equations
+(incompressible Navier-Stokes, shallow-water, primitive equations,
+with and without the quasi-geostrophic limit, adjoin equations, ...)
+with different simple methods (pseudo-spectral, finite differences)
+and geometries (1D, 2D and 3D periodic, 1 inhomogeneous direction,
+...).
+
+The package is organised in four sub-packages:
+
+.. autosummary::
+   :toctree:
+
+   util
+   base
+   operators
+   solvers
+
+"""
+
+from fluiddyn.io import FLUIDDYN_PATH_SIM as path_dir_results
+
+from fluidsim.util.util import (
+    import_module_solver_from_key,
+    load_sim_for_plot, load_state_phys_file,
+    modif_resolution_from_dir, modif_resolution_all_dir)
+
+from fluidsim.base.params import create_params
diff --git a/fluidsim/_version.py b/fluidsim/_version.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vX3ZlcnNpb24ucHk=
--- /dev/null
+++ b/fluidsim/_version.py
@@ -0,0 +1,14 @@
+"""
+Module where the version is written.
+
+It is executed in setup.py and imported in fluiddyn/__init__.py.
+
+See:
+
+http://en.wikipedia.org/wiki/Software_versioning
+http://legacy.python.org/dev/peps/pep-0386/
+
+'a' or 'alpha' means alpha version (internal testing),
+'b' or 'beta' means beta version (external testing).
+"""
+__version__ = '0.0.1a0'
diff --git a/fluidsim/base/__init__.py b/fluidsim/base/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/fluidsim/base/__init__.py
@@ -0,0 +1,19 @@
+"""Base functionalities for the solvers (:mod:`fluidsim.base`)
+====================================================================
+
+.. currentmodule:: fluidsim.base
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   solvers
+   params
+   state
+   init_fields
+   time_stepping
+   output
+   forcing
+
+"""
diff --git a/fluidsim/base/forcing/__init__.py b/fluidsim/base/forcing/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9mb3JjaW5nL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/base/forcing/__init__.py
@@ -0,0 +1,16 @@
+"""Forcing schemes (:mod:`fluidsim.base.forcing`)
+=======================================================
+
+.. currentmodule:: fluidsim.base.forcing
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   base
+   specific
+
+"""
+
+from .base import ForcingBase, ForcingBasePseudoSpectral
diff --git a/fluidsim/base/forcing/base.py b/fluidsim/base/forcing/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9mb3JjaW5nL2Jhc2UucHk=
--- /dev/null
+++ b/fluidsim/base/forcing/base.py
@@ -0,0 +1,81 @@
+"""Forcing schemes (:mod:`fluidsim.base.forcing.base`)
+============================================================
+
+.. currentmodule:: fluidsim.base.forcing.base
+
+Provides:
+
+.. autoclass:: ForcingBase
+   :members:
+   :private-members:
+
+.. autoclass:: ForcingBasePseudoSpectral
+   :members:
+   :private-members:
+
+"""
+
+
+class ForcingBase(object):
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.Forcing.set_child('classes')
+
+    @staticmethod
+    def _complete_params_with_default(params, info_solver):
+        """This static method is used to complete the *params* container.
+        """
+        params.set_child(
+            'forcing',
+            attribs={'type': 'Random',
+                     'available_types': ['Random', 'Proportional'],
+                     'forcing_rate': 1,
+                     'key_forced': 'rot_fft'})
+        dict_classes = info_solver.classes.Forcing.import_classes()
+        for Class in dict_classes.values():
+            if hasattr(Class, '_complete_params_with_default'):
+                try:
+                    Class._complete_params_with_default(params)
+                except TypeError:
+                    Class._complete_params_with_default(params, info_solver)
+
+    def __init__(self, params, sim):
+        self.type_forcing = params.forcing.type
+
+        dict_classes = sim.info.solver.classes.Forcing.import_classes()
+
+        if self.type_forcing not in dict_classes:
+            raise ValueError('Bad value for parameter forcing.type :' +
+                             self.type_forcing)
+
+        ClassForcing = dict_classes[self.type_forcing]
+
+        self._forcing = ClassForcing(params, sim)
+
+    def compute(self):
+        self._forcing.compute()
+
+    def get_forcing(self):
+        return self._forcing.forcing_phys
+
+
+class ForcingBasePseudoSpectral(ForcingBase):
+
+    @staticmethod
+    def _complete_params_with_default(params, info_solver):
+        """This static method is used to complete the *params* container.
+        """
+        ForcingBase._complete_params_with_default(params, info_solver)
+
+        params.forcing.set_attribs({'nkmax_forcing': 5, 'nkmin_forcing': 4})
+
+    def compute(self):
+        self._forcing.compute()
+
+    def get_forcing(self):
+        return self._forcing.forcing_fft
diff --git a/fluidsim/base/forcing/specific.py b/fluidsim/base/forcing/specific.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9mb3JjaW5nL3NwZWNpZmljLnB5
--- /dev/null
+++ b/fluidsim/base/forcing/specific.py
@@ -0,0 +1,454 @@
+"""Forcing schemes (:mod:`fluidsim.base.forcing.specific`)
+================================================================
+
+.. currentmodule:: fluidsim.base.forcing.specific
+
+Provides:
+
+.. autoclass:: SpecificForcing
+   :members:
+   :private-members:
+
+.. autoclass:: SpecificForcingPseudoSpectral
+   :members:
+   :private-members:
+
+.. autoclass:: NormalizedForcing
+   :members:
+   :private-members:
+
+.. autoclass:: Proportional
+   :members:
+   :private-members:
+
+.. autoclass:: RamdomSimplePseudoSpectral
+   :members:
+   :private-members:
+
+.. autoclass:: TimeCorrelatedRandomPseudoSpectral
+   :members:
+   :private-members:
+
+"""
+import numpy as np
+
+from copy import deepcopy
+
+from fluiddyn.util import mpi
+from fluidsim.operators.operators import OperatorsPseudoSpectral2D
+from fluidsim.operators.setofvariables import SetOfVariables
+
+
+class SpecificForcing(object):
+
+    def __init__(self, params, sim):
+
+        self.sim = sim
+        self.oper = sim.oper
+        self.params = params
+
+
+class SpecificForcingPseudoSpectral(SpecificForcing):
+
+    def __init__(self, params, sim):
+
+        super(SpecificForcingPseudoSpectral, self).__init__(params, sim)
+
+        self.sum_wavenumbers = sim.oper.sum_wavenumbers
+        self.fft2 = sim.oper.fft2
+        self.ifft2 = sim.oper.ifft2
+
+        self.forcing_fft = SetOfVariables(
+            like_this_sov=sim.state.state_fft,
+            name_type_variables='forcing_fft', value=0.)
+        self.forcing_fft.initialize(value=0.)
+
+        self.kmax_forcing = self.oper.deltakx*params.forcing.nkmax_forcing
+        self.kmin_forcing = self.oper.deltakx*params.forcing.nkmin_forcing
+        self.forcing_rate = params.forcing.forcing_rate
+        self.key_forced = params.forcing.key_forced
+
+        i = 0
+        while 2*params.forcing.nkmax_forcing > 2**i:
+            i += 1
+        n = 2**i
+
+        if mpi.rank == 0:
+            params_coarse = deepcopy(params)
+            params_coarse.oper.nx = n
+            params_coarse.oper.ny = n
+            params_coarse.oper.type_fft = 'FFTWPY'
+            params_coarse.oper.coef_dealiasing = 1.
+
+            self.oper_coarse = OperatorsPseudoSpectral2D(
+                SEQUENCIAL=True,
+                params=params_coarse,
+                goal_to_print='coarse resolution for forcing')
+            self.shapeK_loc_coarse = self.oper_coarse.shapeK_loc
+
+            self.COND_NO_F = np.logical_or(
+                self.oper_coarse.KK > self.kmax_forcing,
+                self.oper_coarse.KK < self.kmin_forcing)
+
+            self.nb_forced_modes = (self.COND_NO_F.size -
+                                    np.array(self.COND_NO_F,
+                                             dtype=np.int32).sum())
+            self.ind_forcing = np.logical_not(
+                self.COND_NO_F).flatten().nonzero()[0]
+
+        else:
+            self.shapeK_loc_coarse = None
+
+        if mpi.nb_proc > 1:
+            self.shapeK_loc_coarse = mpi.comm.bcast(
+                self.shapeK_loc_coarse, root=0)
+
+        # if params.forcing.type_forcing == 'WAVES':
+        #     self.compute = self.compute_forcing_waves
+        #     if mpi.rank == 0:
+        #         eta_rms_max = 0.1
+        #         self.eta_cond = eta_rms_max / np.sqrt(self.nb_forced_modes)
+        #         print '    eta_cond =', self.eta_cond
+        # else:
+        #     # self.compute = self.compute_forcing_particular_k
+        #     # self.compute = self.compute_forcing_proportional
+        #     self.compute = self.compute_forcing_2nd_degree_eq
+
+        self.forcingc_fft = SetOfVariables(
+            keys=self.forcing_fft.keys,
+            shape1var=self.shapeK_loc_coarse,
+            dtype=np.complex128,
+            name_type_variables='forcingc_fft',
+            value=0.)
+
+    def compute(self):
+        """compute a forcing normalize with a 2nd degree eq."""
+
+        a_fft = self.sim.state.state_fft[self.key_forced]
+        a_fft = self.oper.coarse_seq_from_fft_loc(a_fft,
+                                                  self.shapeK_loc_coarse)
+
+        if mpi.rank > 0:
+            Fa_fft = np.empty(self.shapeK_loc_coarse,
+                              dtype=np.complex128)
+        else:
+            Fa_fft = self.forcingc_raw_each_time()
+            self.forcingc_fft[self.key_forced] = Fa_fft
+
+        self.put_forcingc_in_forcing()
+
+    def put_forcingc_in_forcing(self):
+        """Copy data from forcingc_fft into forcing_fft."""
+        nKyc = self.shapeK_loc_coarse[0]
+        nKxc = self.shapeK_loc_coarse[1]
+        nb_keys = self.forcing_fft.nb_variables
+
+        ar3Df = self.forcing_fft.data
+        ar3Dfc = self.forcingc_fft.data
+
+        if mpi.nb_proc > 1:
+            nKy = self.oper.shapeK_seq[0]
+
+            for ikey in xrange(nb_keys):
+                fck_fft = ar3Dfc[ikey].transpose()
+
+                for iKxc in xrange(nKxc):
+                    kx = self.oper.deltakx*iKxc
+                    rank_iKx, iKxloc, iKyloc = (
+                        self.oper.where_is_wavenumber(kx, 0.))
+                    fc1D = fck_fft[iKxc]
+                    if rank_iKx != 0:
+                        # message fc1D
+                        fc1D = np.ascontiguousarray(fc1D)
+                        if mpi.rank == 0:
+                            mpi.comm.Send([fc1D, mpi.MPI.COMPLEX],
+                                          dest=rank_iKx, tag=iKxc)
+                        elif mpi.rank == rank_iKx:
+                            mpi.comm.Recv([fc1D, mpi.MPI.COMPLEX],
+                                          source=0, tag=iKxc)
+                    if mpi.rank == rank_iKx:
+                        # copy
+                        for iKyc in xrange(nKyc):
+                            if iKyc <= nKyc/2:
+                                iKy = iKyc
+                            else:
+                                kynodim = iKyc - nKyc
+                                iKy = kynodim + nKy
+                            ar3Df[ikey, iKxloc, iKy] = fc1D[iKyc]
+
+        else:
+            nKy = self.oper.shapeK_seq[0]
+
+            for ikey in xrange(nb_keys):
+                for iKyc in xrange(nKyc):
+                    if iKyc <= nKyc/2:
+                        iKy = iKyc
+                    else:
+                        kynodim = iKyc - nKyc
+                        iKy = kynodim + nKy
+                    for iKxc in xrange(nKxc):
+                        ar3Df[ikey, iKy, iKxc] = ar3Dfc[ikey, iKyc, iKxc]
+
+    def verify_injection_rate(self):
+        """Verify injection rate."""
+        Fa_fft = self.forcing_fft[self.key_forced]
+        a_fft = self.sim.state.state_fft[self.key_forced]
+
+        PZ_forcing1 = abs(Fa_fft)**2/2*self.sim.time_stepping.deltat
+        PZ_forcing2 = np.real(
+            Fa_fft.conj()*a_fft +
+            Fa_fft*a_fft.conj())/2.
+        PZ_forcing1 = self.oper.sum_wavenumbers(PZ_forcing1)
+        PZ_forcing2 = self.oper.sum_wavenumbers(PZ_forcing2)
+        if mpi.rank == 0:
+            print('PZ_f = {0:9.4e} ; PZ_f2 = {1:9.4e};'.format(
+                PZ_forcing1 + PZ_forcing2,
+                PZ_forcing2))
+
+
+class Proportional(SpecificForcingPseudoSpectral):
+
+    def compute(self):
+        """Compute a forcing proportional to the flow."""
+        a_fft = self.sim.state.state_fft[self.key_forced]
+        a_fft = self.oper.coarse_seq_from_fft_loc(a_fft,
+                                                  self.shapeK_loc_coarse)
+
+        if mpi.rank > 0:
+            Fa_fft = np.empty(self.shapeK_loc_coarse,
+                              dtype=np.complex128)
+        else:
+            Fa_fft = self.normalize_forcingc(a_fft)
+            self.forcingc_fft[self.key_forced] = Fa_fft
+
+        self.put_forcingc_in_forcing()
+
+        # # verification
+        # self.verify_injection_rate()
+
+    def normalize_forcingc(self, vc_fft):
+        """Modify the array fvc_fft to fixe the injection rate.
+
+        varc : ndarray
+            a variable at the coarse resolution.
+
+        To be called only with proc 0.
+        """
+        fvc_fft = vc_fft.copy()
+        fvc_fft[self.COND_NO_F] = 0.
+
+        Z_fft = abs(fvc_fft)**2/2
+
+        # # possibly "kill" the largest mode
+        # nb_kill = 0
+        # for ik in xrange(nb_kill):
+        #     imax = Z_fft.argmax()
+        #     Z_fft.flat[imax] = 0.
+        #     fvc_fft.flat[imax] = 0.
+
+        # # possibly add randomness: random kill!
+        # nb_kill = self.nb_forced_modes-10
+        # ind_kill = random.sample(self.ind_forcing,nb_kill)
+        # for ik in ind_kill:
+        #     Z_fft.flat[ik] = 0.
+        #     fvc_fft.flat[ik] = 0.
+
+        Z = self.oper_coarse.sum_wavenumbers(Z_fft)
+        deltat = self.sim.time_stepping.deltat
+        alpha = (np.sqrt(1 + deltat*self.forcing_rate/Z) - 1)/deltat
+        fvc_fft = alpha*fvc_fft
+
+        return fvc_fft
+
+
+class NormalizedForcing(SpecificForcingPseudoSpectral):
+
+    tag = 'normalized_forcing'
+
+    @classmethod
+    def _complete_params_with_default(cls, params):
+        """This static method is used to complete the *params* container.
+        """
+        params.forcing.set_child(
+            cls.tag,
+            {'type_normalize': '2nd_degree_eq'})
+
+    def compute(self):
+        """compute a forcing normalize with a 2nd degree eq."""
+
+        a_fft = self.sim.state.state_fft[self.key_forced]
+        a_fft = self.oper.coarse_seq_from_fft_loc(a_fft,
+                                                  self.shapeK_loc_coarse)
+
+        if mpi.rank > 0:
+            Fa_fft = np.empty(self.shapeK_loc_coarse,
+                              dtype=np.complex128)
+        else:
+            Fa_fft = self.forcingc_raw_each_time()
+            Fa_fft = self.normalize_forcingc(Fa_fft, a_fft)
+            self.forcingc_fft[self.key_forced] = Fa_fft
+
+        self.put_forcingc_in_forcing()
+
+        # # verification
+        # self.verify_injection_rate()
+
+    def normalize_forcingc(self, fvc_fft, vc_fft):
+
+        type_normalize = self.params.forcing.__dict__[self.tag].type_normalize
+
+        if type_normalize == '2nd_degree_eq':
+            return self.normalize_forcingc_2nd_degree_eq(fvc_fft, vc_fft)
+        elif type_normalize == 'particular_k':
+            return self.normalize_forcingc_part_k(fvc_fft, vc_fft)
+        else:
+            ValueError('Bad value for parameter forcing.type_normalize:',
+                       type_normalize)
+
+    def normalize_forcingc_part_k(self, fvc_fft, vc_fft):
+        """Modify the array fvc_fft to fixe the injection rate.
+
+        varc : ndarray
+            a variable at the coarse resolution.
+
+        To be called only with proc 0.
+        """
+        oper_c = self.oper_coarse
+
+        oper_c.project_fft_on_realX(fvc_fft)
+        # fvc_fft[self.COND_NO_F] = 0.
+
+        P_forcing2 = np.real(
+            + fvc_fft.conj()*vc_fft +
+            fvc_fft*vc_fft.conj())/2.
+        P_forcing2 = oper_c.sum_wavenumbers(P_forcing2)
+
+        # we choice randomly a "particular" wavenumber
+        # in the forced space
+        KX_f = oper_c.KX[~self.COND_NO_F].flatten()
+        KY_f = oper_c.KY[~self.COND_NO_F].flatten()
+        nb_wn_f = len(KX_f)
+
+        ipart = np.random.random_integers(0, nb_wn_f-1)
+        kx_part = KX_f[ipart]
+        ky_part = KY_f[ipart]
+        ikx_part = abs(oper_c.kx_loc-kx_part).argmin()
+        iky_part = abs(oper_c.ky_loc-ky_part).argmin()
+
+        ik0_part = iky_part
+        ik1_part = ikx_part
+
+        P_forcing2_part = np.real(
+            fvc_fft[ik0_part, ik1_part].conj() *
+            vc_fft[ik0_part, ik1_part] +
+            fvc_fft[ik0_part, ik1_part] *
+            vc_fft[ik0_part, ik1_part].conj())
+
+        if ikx_part == 0:
+            P_forcing2_part = P_forcing2_part/2
+        P_forcing2_other = P_forcing2 - P_forcing2_part
+        fvc_fft[ik0_part, ik1_part] = \
+            -P_forcing2_other/vc_fft[ik0_part, ik1_part].real
+
+        if ikx_part != 0:
+            fvc_fft[ik0_part, ik1_part] = fvc_fft[ik0_part, ik1_part]/2
+
+        oper_c.project_fft_on_realX(fvc_fft)
+
+        # normalisation to obtain the wanted total forcing rate
+        PZ_nonorm = (oper_c.sum_wavenumbers(abs(fvc_fft)**2) *
+                     self.sim.time_stepping.deltat/2
+                     )
+        fvc_fft = fvc_fft*np.sqrt(self.forcing_rate/PZ_nonorm)
+
+        return fvc_fft
+
+    def normalize_forcingc_2nd_degree_eq(self, fvc_fft, vc_fft):
+        """Modify the array fvc_fft to fixe the injection rate.
+
+        varc : ndarray
+            a variable at the coarse resolution.
+
+        To be called only with proc 0.
+        """
+        oper_c = self.oper_coarse
+
+        deltat = self.sim.time_stepping.deltat
+
+        a = deltat/2*oper_c.sum_wavenumbers(abs(fvc_fft)**2)
+
+        b = oper_c.sum_wavenumbers(
+            (vc_fft.conj()*fvc_fft).real)
+
+        c = -self.forcing_rate
+
+        # print 'max abs vc_fft', np.max(abs(vc_fft))
+        # print 'max abs fvc_fft', np.max(abs(fvc_fft))
+        # print 'in base_forcing:', a, b, c
+
+        Delta = b**2 - 4*a*c
+        alpha = (np.sqrt(Delta) - b)/(2*a)
+
+        fvc_fft = alpha*fvc_fft
+
+        return fvc_fft
+
+    def coef_normalization_from_abc(self, a, b, c):
+        """."""
+        Delta = b**2 - 4*a*c
+        alpha = (np.sqrt(Delta) - b)/(2*a)
+        return alpha
+
+
+class RamdomSimplePseudoSpectral(NormalizedForcing):
+
+    tag = 'random'
+
+    def compute_forcingc_raw(self):
+        """Random coarse forcing.
+
+        To be called only with proc 0.
+        """
+        F_fft = self.oper_coarse.random_arrayK()
+        self.oper_coarse.project_fft_on_realX(F_fft)
+        F_fft[self.COND_NO_F] = 0.
+        return F_fft
+
+    def forcingc_raw_each_time(self):
+        return self.compute_forcingc_raw()
+
+
+class TimeCorrelatedRandomPseudoSpectral(RamdomSimplePseudoSpectral):
+
+    def __init__(self, params, sim):
+
+        super(TimeCorrelatedRandomPseudoSpectral, self).__init__(params, sim)
+
+        if mpi.rank == 0:
+            self.F0 = self.compute_forcingc_raw()
+            self.F1 = self.compute_forcingc_raw()
+            self.period_change_F0F1 = self.forcing_rate**(-1./3)
+            self.t_last_change = self.sim.time_stepping.t
+
+    def forcingc_raw_each_time(self):
+        tsim = self.sim.time_stepping.t
+        if tsim-self.t_last_change >= self.period_change_F0F1:
+            self.t_last_change = tsim
+            self.F0 = self.F1
+            del(self.F1)
+            self.F1 = self.compute_forcingc_raw()
+
+        F_fft = self.forcingc_from_F0F1()
+        return F_fft
+
+    def forcingc_from_F0F1(self):
+        tsim = self.sim.time_stepping.t
+        deltat = self.period_change_F0F1
+        omega = np.pi/deltat
+
+        deltaF = self.F1 - self.F0
+
+        F_fft = self.F1 - 0.5*(
+            np.cos((tsim - self.t_last_change)*omega) + 1)*deltaF
+
+        return F_fft
diff --git a/fluidsim/base/init_fields.py b/fluidsim/base/init_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9pbml0X2ZpZWxkcy5weQ==
--- /dev/null
+++ b/fluidsim/base/init_fields.py
@@ -0,0 +1,367 @@
+"""Initialisation of the fields (:mod:`fluidsim.base.init_fields`)
+========================================================================
+
+.. currentmodule:: fluidsim.base.init_fields
+
+Provides:
+
+.. autoclass:: InitFieldsBase
+   :members:
+   :private-members:
+
+"""
+
+import numpy as np
+import h5py
+
+from copy import deepcopy
+
+from fluiddyn.util import mpi
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+
+class InitFieldsBase(object):
+    """A :class:`InitFieldsBase` object provides functions for
+    initialisation of 2D fields."""
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        attribs = {'type_flow_init': 'NOISE',
+                   'lambda_noise': 1.,
+                   'max_velo_noise': 1.,
+                   # in case type_flow_init == 'LOAD_FILE'
+                   'path_file': ''}
+        params.set_child('init_fields', attribs=attribs)
+
+    implemented_flows = ['NOISE', 'CONSTANT', 'LOAD_FILE']
+
+    def __init__(self, sim=None, oper=None, params=None):
+
+        if sim is not None:
+            self.sim = sim
+            params = sim.params
+            oper = sim.oper
+
+        self.params = params
+        self.oper = oper
+
+    def get_and_check_type_flow_init(self):
+        type_flow_init = self.params.init_fields.type_flow_init
+        if type_flow_init not in self.implemented_flows:
+            raise ValueError(type_flow_init + ' is not an implemented flows.')
+        return type_flow_init
+
+    def __call__(self):
+        sim = self.sim
+
+        type_flow_init = self.get_and_check_type_flow_init()
+
+        if type_flow_init == 'NOISE':
+            rot_fft, ux_fft, uy_fft = self.init_fields_noise()
+            sim.state.state_fft['ux_fft'] = ux_fft
+            sim.state.state_fft['uy_fft'] = uy_fft
+            sim.state.statephys_from_statefft()
+
+        if type_flow_init == 'LOAD_FILE':
+            self.get_state_from_file(self.params.init_fields.path_file)
+
+        elif type_flow_init == 'CONSTANT':
+            sim.state.state_fft.initialize(value=1.)
+            sim.state.state_phys.initialize(value=1.)
+
+    def init_fields_1dipole(self):
+        rot = self.vorticity_shape()
+        rot_fft = self.oper.fft2(rot)
+
+        self.oper.dealiasing(rot_fft)
+        ux_fft, uy_fft = self.oper.vecfft_from_rotfft(rot_fft)
+
+        return rot_fft, ux_fft, uy_fft
+
+    def vorticity_shape(self):
+        xs = self.oper.Lx/2
+        ys = self.oper.Ly/2
+        theta = np.pi/2.3
+        b = 2.5
+        omega = np.zeros(self.oper.shapeX_loc)
+
+        for ip in range(-1, 2):
+            for jp in range(-1, 2):
+                XX_s = (np.cos(theta)*(self.oper.XX-xs-ip*self.oper.Lx)
+                        + np.sin(theta)*(self.oper.YY-ys-jp*self.oper.Ly))
+                YY_s = (np.cos(theta)*(self.oper.YY-ys-jp*self.oper.Ly)
+                        - np.sin(theta)*(self.oper.XX-xs-ip*self.oper.Lx))
+                omega = omega + self.wz_2LO(XX_s, YY_s, b)
+        return omega
+
+    def wz_2LO(self, XX, YY, b):
+        return (- 2*np.exp(-(XX**2 + (YY+b/2)**2))
+                + 2*np.exp(-(XX**2 + (YY-b/2)**2)))
+
+    def init_fields_jet(self):
+        rot = self.vorticity_jet()
+        rot_fft = self.oper.fft2(rot)
+        rot_fft[self.oper.KK == 0] = 0.
+        self.oper.dealiasing(rot_fft)
+        ux_fft, uy_fft = self.oper.vecfft_from_rotfft(rot_fft)
+        rot_fft = self.oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        return rot_fft, ux_fft, uy_fft
+
+    def vorticity_jet(self):
+        Ly = self.oper.Ly
+        a = 0.5
+        b = Ly/2
+        omega0 = 2.
+        # epsilon = 2.
+        omega = omega0*(
+            + np.exp(-((self.oper.YY - Ly/2 + b/2)/a)**2)
+            - np.exp(-((self.oper.YY - Ly/2 - b/2)/a)**2)
+            + np.exp(-((self.oper.YY - Ly/2 + b/2 + Ly)/a)**2)
+            - np.exp(-((self.oper.YY - Ly/2 - b/2 + Ly)/a)**2)
+            + np.exp(-((self.oper.YY - Ly/2 + b/2 - Ly)/a)**2)
+            - np.exp(-((self.oper.YY - Ly/2 - b/2 - Ly)/a)**2)
+            # + epsilon*np.random.random([self.oper.ny_loc, self.oper.nx_loc])
+        )
+        return omega
+
+    def init_fields_noise(self):
+        try:
+            lambda0 = self.params.lambda_noise
+        except AttributeError:
+            lambda0 = self.oper.Lx/4
+        H_smooth = lambda x, delta: (1. + np.tanh(2*np.pi*x/delta))/2
+
+        # to compute always the same field... (for 1 resolution...)
+        np.random.seed(42)  # this does not work for MPI...
+
+        ux_fft = (np.random.random(self.oper.shapeK)
+                  + 1j*np.random.random(self.oper.shapeK) - 0.5 - 0.5j)
+        uy_fft = (np.random.random(self.oper.shapeK)
+                  + 1j*np.random.random(self.oper.shapeK) - 0.5 - 0.5j)
+
+        if mpi.rank == 0:
+            ux_fft[0, 0] = 0.
+            uy_fft[0, 0] = 0.
+
+        self.oper.projection_perp(ux_fft, uy_fft)
+        self.oper.dealiasing(ux_fft, uy_fft)
+
+        k0 = 2*np.pi/lambda0
+        delta_k0 = 1.*k0
+        ux_fft = ux_fft*H_smooth(k0-self.oper.KK, delta_k0)
+        uy_fft = uy_fft*H_smooth(k0-self.oper.KK, delta_k0)
+
+        ux = self.oper.ifft2(ux_fft)
+        uy = self.oper.ifft2(uy_fft)
+        velo_max = np.sqrt(ux**2+uy**2).max()
+        if mpi.nb_proc > 1:
+            velo_max = self.oper.comm.allreduce(velo_max, op=mpi.MPI.MAX)
+        ux = self.params.init_fields.max_velo_noise*ux/velo_max
+        uy = self.params.init_fields.max_velo_noise*uy/velo_max
+        ux_fft = self.oper.fft2(ux)
+        uy_fft = self.oper.fft2(uy)
+
+        rot_fft = self.oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        return rot_fft, ux_fft, uy_fft
+
+    def init_fields_noise_rot(self, lambda0):
+        H_smooth = lambda x, delta: (1. + np.tanh(2*np.pi*x/delta))/2
+        rot_fft = (np.random.random([self.nky, self.nkx])
+                   + 1j*np.random.random([self.nky, self.nkx]) - 0.5 - 0.5j)
+        k0 = 2*np.pi/lambda0
+        delta_k0 = 1*k0
+        rot_fft = rot_fft*H_smooth(k0-self.KK, delta_k0)
+        self.oper.dealiasing(rot_fft)
+        ux_fft, uy_fft = self.oper.vecfft_from_rotfft(rot_fft)
+        ux = self.oper.ifft2(ux_fft)
+        uy = self.oper.ifft2(uy_fft)
+        velo_max = np.sqrt(ux**2+uy**2).max()
+        if mpi.nb_proc > 1:
+            velo_max = self.oper.comm.allreduce(velo_max, op=mpi.MPI.MAX)
+        ux = ux/velo_max
+        uy = uy/velo_max
+        ux_fft = self.oper.fft2(ux)
+        uy_fft = self.oper.fft2(uy)
+
+        return rot_fft, ux_fft, uy_fft
+
+    def init_fields_wave(self):
+        ikx = self.sim.params.ikx
+        eta0 = self.sim.params.eta0
+
+        # BE CARREFUL, THIS WON'T WORK WITH MPI !!!
+        if mpi.rank == 0:
+            print 'init_fields_wave(ikx = {0:4d}, eta0 = {1:7.2e})'.format(
+                ikx, eta0)
+            print 'kx[ikx] = {0:8.2f}'.format(self.oper.kxE[ikx])
+
+        if mpi.nb_proc > 1:
+            raise ValueError('BE CARREFUL, THIS WILL BE WRONG !'
+                             '  DO NOT USE THIS METHOD WITH MPI '
+                             '(or rewrite it :-)')
+
+        eta_fft = self.oper.constant_arrayK(value=0.)
+        ux_fft = self.oper.constant_arrayK(value=0.)
+        uy_fft = self.oper.constant_arrayK(value=0.)
+
+        eta_fft[0, self.sim.params.ikx] = 0.1*eta0
+        # eta_fft[ikx, 0] = 0.1j*eta0
+
+        self.oper.project_fft_on_realX(eta_fft)
+
+#        ux_fft[0,ikx] = 1.j*eta0
+#        uy_fft[0,ikx] = 1.j*eta0
+
+        div_fft = self.oper.constant_arrayK(value=0.)
+        div_fft[ikx, 0] = eta0
+        div_fft[0, ikx] = eta0
+        self.oper.project_fft_on_realX(div_fft)
+        ux_fft, uy_fft = self.oper.vecfft_from_divfft(div_fft)
+
+        return eta_fft, ux_fft, uy_fft
+
+    def get_state_from_file(self, path_file):
+        if mpi.rank == 0:
+            try:
+                f = h5py.File(path_file, 'r')
+            except:
+                raise ValueError('file '+path_file+' is really a hd5 file?')
+
+            print ('Load state from file:\n[...]'+path_file[-75:])
+
+            try:
+                group_oper = f['/info_simul/params/oper']
+            except:
+                raise ValueError(
+                    'file '+path_file+' does not contain a params object')
+
+            try:
+                group_state_phys = f['/state_phys']
+            except:
+                raise ValueError('file ' + path_file +
+                                 ' does not contain a state_phys object')
+
+            nx_file = group_oper.attrs['nx']
+            ny_file = group_oper.attrs['ny']
+            Lx_file = group_oper.attrs['Lx']
+            Ly_file = group_oper.attrs['Ly']
+
+            if isinstance(nx_file, list):
+                nx_file = nx_file.item()
+                ny_file = ny_file.item()
+                Lx_file = Lx_file.item()
+                Ly_file = Ly_file.item()
+
+            if self.params.oper.nx != nx_file:
+                raise ValueError(
+                    'this is not a correct state for this simulation\n'
+                    'self.nx != params_file.nx')
+
+            if self.params.oper.ny != ny_file:
+                raise ValueError(
+                    'this is not a correct state for this simulation\n'
+                    'self.ny != params_file.ny')
+
+            if self.params.oper.Lx != Lx_file:
+                raise ValueError(
+                    'this is not a correct state for this simulation\n'
+                    'self.params.oper.Lx != params_file.Lx')
+
+            if self.params.oper.Ly != Ly_file:
+                raise ValueError(
+                    'this is not a correct state for this simulation\n'
+                    'self.params.oper.Ly != params_file.Ly')
+
+            keys_state_phys_file = group_state_phys.keys()
+        else:
+            keys_state_phys_file = {}
+
+        if mpi.nb_proc > 1:
+            keys_state_phys_file = mpi.comm.bcast(keys_state_phys_file)
+
+        state_phys = self.sim.state.state_phys
+        keys_phys_needed = self.sim.info.solver.classes.State.keys_phys_needed
+        for k in keys_phys_needed:
+            if k in keys_state_phys_file:
+                if mpi.rank == 0:
+                    field_seq = group_state_phys[k][...]
+                else:
+                    field_seq = self.oper.constant_arrayX()
+
+                if mpi.nb_proc > 1:
+                    field_loc = self.oper.scatter_Xspace(field_seq)
+                else:
+                    field_loc = field_seq
+                state_phys[k] = field_loc
+            else:
+                state_phys[k] = self.oper.constant_arrayX(value=0.)
+
+        if mpi.rank == 0:
+            t_file = group_state_phys.attrs['time']
+            f.close()
+        else:
+            t_file = 0.
+
+        if mpi.nb_proc > 1:
+            t_file = mpi.comm.bcast(t_file)
+
+        self.sim.state.statefft_from_statephys()
+        self.sim.state.statephys_from_statefft()
+        self.sim.time_stepping.t = t_file
+
+    def get_state_from_obj_simul(self, sim_in):
+
+        if mpi.nb_proc > 1:
+            raise ValueError('BE CARREFUL, THIS WILL BE WRONG !'
+                             '  DO NOT USE THIS METHOD WITH MPI')
+
+        self.sim.time_stepping.t = sim_in.time_stepping.t
+
+        if (self.params.oper.nx == sim_in.params.oper.nx
+                and self.params.oper.ny == sim_in.params.oper.ny):
+            state_fft = deepcopy(sim_in.state.state_fft)
+        else:
+            # modify resolution
+            # state_fft = SetOfVariables('state_fft')
+            state_fft = SetOfVariables(like_this_sov=self.sim.state.state_fft)
+            keys_state_fft = sim_in.info.solver.classes.State['keys_state_fft']
+            for k in keys_state_fft:
+                field_fft_seq_in = sim_in.state.state_fft[k]
+                field_fft_seq_new_res = \
+                    self.sim.oper.constant_arrayK(value=0.)
+                [nk0_seq, nk1_seq] = field_fft_seq_new_res.shape
+                [nk0_seq_in, nk1_seq_in] = field_fft_seq_in.shape
+
+                nk0_min = min(nk0_seq, nk0_seq_in)
+                nk1_min = min(nk1_seq, nk1_seq_in)
+
+                # it is a little bit complicate to take into account ky
+                for ik1 in xrange(nk1_min):
+                    field_fft_seq_new_res[0, ik1] = field_fft_seq_in[0, ik1]
+                    field_fft_seq_new_res[nk0_min/2, ik1] = \
+                        field_fft_seq_in[nk0_min/2, ik1]
+                for ik0 in xrange(1, nk0_min/2):
+                    for ik1 in xrange(nk1_min):
+                        field_fft_seq_new_res[ik0, ik1] = \
+                            field_fft_seq_in[ik0, ik1]
+                        field_fft_seq_new_res[-ik0, ik1] = \
+                            field_fft_seq_in[-ik0, ik1]
+
+                state_fft[k] = field_fft_seq_new_res
+
+        if self.sim.output.name_solver == sim_in.output.name_solver:
+            self.sim.state.state_fft = state_fft
+        else:  # complicated case... untested solution !
+            # state_fft = SetOfVariables('state_fft')
+            raise ValueError('Not yet implemented...')
+            for k in self.sim.info.solver.classes.State['keys_state_fft']:
+                if k in sim_in.info.solver.classes.State['keys_state_fft']:
+                    self.sim.state.state_fft[k] = state_fft[k]
+                else:
+                    self.sim.state.state_fft[k] = \
+                        self.oper.constant_arrayK(value=0.)
+
+        self.sim.state.statephys_from_statefft()
diff --git a/fluidsim/base/output/__init__.py b/fluidsim/base/output/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvX19pbml0X18ucHk=
--- /dev/null
+++ b/fluidsim/base/output/__init__.py
@@ -0,0 +1,31 @@
+"""Output (:mod:`fluidsim.base.output`)
+=============================================
+
+.. currentmodule:: fluidsim.base.output
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   base
+   prob_dens_func
+   spectra
+   phys_fields
+   spatial_means
+   time_signalsK
+   spatial_means
+   time_signalsK
+   increments
+   print_stdout
+   spect_energy_budget
+
+
+.. autoclass:: OutputBase
+   :members:
+   :private-members:
+
+
+"""
+
+from .base import OutputBase, OutputBasePseudoSpectral
diff --git a/fluidsim/base/output/base.py b/fluidsim/base/output/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvYmFzZS5weQ==
--- /dev/null
+++ b/fluidsim/base/output/base.py
@@ -0,0 +1,443 @@
+"""Base module for the output (:mod:`fluidsim.base.output.base`)
+======================================================================
+
+.. currentmodule:: fluidsim.base.output.base
+
+Provides:
+
+.. autoclass:: OutputBase
+   :members:
+   :private-members:
+
+.. autoclass:: OutputBasePseudoSpectral
+   :members:
+   :private-members:
+
+.. autoclass:: SpecificOutput
+   :members:
+   :private-members:
+
+"""
+
+from __future__ import print_function
+
+import h5py
+import matplotlib.pyplot as plt
+import datetime
+import os
+import shutil
+import numpy as np
+
+
+import fluiddyn
+
+from fluiddyn.util import mpi
+
+from fluiddyn.io import FLUIDDYN_PATH_SIM, FLUIDDYN_PATH_SCRATCH
+
+from fluiddyn.util.util import time_as_str, print_memory_usage
+
+from fluidsim.util.util import load_params_simul
+
+
+class OutputBase(object):
+    """Handle the output."""
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.Output.set_child('classes')
+        classes = info_solver.classes.Output.classes
+
+        classes.set_child(
+            'PrintStdOut',
+            attribs={'module_name': 'fluidsim.base.output.print_stdout',
+                     'class_name': 'PrintStdOutBase'})
+
+        classes.set_child(
+            'PhysFields',
+            attribs={'module_name': 'fluidsim.base.output.phys_fields',
+                     'class_name': 'PhysFieldsBase'})
+
+    @staticmethod
+    def _complete_params_with_default(params, info_solver):
+        """This static method is used to complete the *params* container.
+        """
+        attribs = {'period_show_plot': 1,
+                   'ONLINE_PLOT_OK': True,
+                   'HAS_TO_SAVE': True}
+        params.set_child('output', attribs=attribs)
+
+        params.output.set_child('periods_save')
+        params.output.set_child('periods_print')
+        params.output.set_child('periods_plot')
+
+        dict_classes = info_solver.classes.Output.import_classes()
+        for Class in dict_classes.values():
+            if hasattr(Class, '_complete_params_with_default'):
+                try:
+                    Class._complete_params_with_default(params)
+                except TypeError:
+                    Class._complete_params_with_default(params, info_solver)
+
+    def __init__(self, sim):
+        params = sim.params
+        self.sim = sim
+        self.params = params.output
+
+        self.has_to_save = self.params.HAS_TO_SAVE
+        self.name_solver = sim.info.solver['short_name']
+
+        # initialisation name_run and path_run
+        list_for_name_run = self.create_list_for_name_run()
+        list_for_name_run.append(time_as_str())
+        self.name_run = '_'.join(list_for_name_run)
+
+        self.sim.name_run = self.name_run
+
+        if not params.NEW_DIR_RESULTS:
+            try:
+                self.path_run = params.path_run
+            except AttributeError:
+                params.NEW_DIR_RESULTS = True
+                print('Strange: params.NEW_DIR_RESULTS == False '
+                      'but no params.path_run')
+
+            # if has_to_save, we verify the correspondence between the
+            # resolution of the simulation and the resolution of the
+            # previous simulation saved in this directory
+            if self.has_to_save:
+                if mpi.rank == 0:
+                    try:
+                        params_dir = load_params_simul(path_dir=self.path_run)
+                    except:
+                        raise ValueError(
+                            'Strange, no info_simul.h5 in self.path_run')
+
+                    if (params.oper.nx != params_dir.oper.nx
+                            or params.oper.ny != params_dir.oper.ny):
+                        params.NEW_DIR_RESULTS = True
+                        print("""
+Warning: params.NEW_DIR_RESULTS is False but the resolutions of the simulation
+         and of the simulation in the directory self.path_run are different
+         we put params.NEW_DIR_RESULTS = True""")
+                if mpi.nb_proc > 1:
+                    params.NEW_DIR_RESULTS = \
+                        mpi.comm.bcast(params.NEW_DIR_RESULTS)
+
+        if params.NEW_DIR_RESULTS:
+
+            if FLUIDDYN_PATH_SCRATCH is not None:
+                self.path_run = os.path.join(
+                    FLUIDDYN_PATH_SCRATCH, self.sim.name_run)
+            else:
+                self.path_run = os.path.join(
+                    FLUIDDYN_PATH_SIM, self.sim.name_run)
+
+            if mpi.rank == 0:
+                params._set_attr_xml('path_run', self.path_run)
+                if not os.path.exists(self.path_run):
+                    os.makedirs(self.path_run)
+
+        dico_classes = sim.info.solver.classes.Output.import_classes()
+
+        PrintStdOut = dico_classes['PrintStdOut']
+        self.print_stdout = PrintStdOut(self)
+
+        if not self.params.ONLINE_PLOT_OK:
+            for k in self.params.periods_plot.xml_attrib.keys():
+                self.params.periods_plot[k] = 0.
+
+        if not self.has_to_save:
+            for k in self.params.periods_save.xml_attrib.keys():
+                self.params.periods_save[k] = 0.
+
+    def create_list_for_name_run(self):
+        list_for_name_run = [self.name_solver]
+        if len(self.sim.params.short_name_type_run) > 0:
+            list_for_name_run.append(self.sim.params.short_name_type_run)
+        list_for_name_run.append(self.sim.oper.produce_str_describing_oper())
+
+        return list_for_name_run
+
+    def init_with_oper_and_state(self):
+        sim = self.sim
+
+        self.oper = sim.oper
+
+        if mpi.rank == 0:
+            # print info on the run
+            specifications = (', '+sim.params.time_stepping.type_time_scheme +
+                              # ', '+self.oper.type_fft +
+                              ' and ')
+            if mpi.nb_proc == 1:
+                specifications = specifications+'sequenciel,\n'
+            else:
+                specifications += 'parallel ({0} proc.)\n'.format(mpi.nb_proc)
+            self.print_stdout(
+                '\nsolver ' + self.name_solver + specifications +
+                self.sim.oper.produce_long_str_describing_oper() +
+                'path_run =\n' + self.path_run + '\n' +
+                'type_flow_init = ' + sim.params.init_fields.type_flow_init)
+
+        if mpi.rank == 0 and self.has_to_save and sim.params.NEW_DIR_RESULTS:
+            # save info on the run
+            self.sim.info.solver.xml_save(
+                path_file=self.path_run+'/info_solver.xml',
+                comment=(
+                    'This file has been created by'
+                    ' the Python program FluidDyn ' + fluiddyn.__version__ +
+                    '.\n\nIt should not be modified '
+                    '(except for adding xml comments).'))
+
+            self.sim.params.xml_save(
+                path_file=self.path_run+'/params_simul.xml',
+                comment=(
+                    'This file has been created by'
+                    ' the Python program FluidDyn ' + fluiddyn.__version__ +
+                    '.\n\nIt should not be modified '
+                    '(except for adding xml comments).'))
+
+        if mpi.rank == 0:
+            plt.ion()
+            self.print_stdout('Initialization outputs:')
+
+        self.print_stdout.complete_init_with_state()
+
+        dico_classes = sim.info.solver.classes.Output.import_classes()
+
+        # This class has already been instantiated.
+        dico_classes.pop('PrintStdOut')
+
+        for Class in dico_classes.values():
+            if mpi.rank == 0:
+                print(Class, Class._tag)
+            self.__dict__[Class._tag] = Class(self)
+
+        print_memory_usage(
+            '\nMemory usage at the end of init. (equiv. seq.)')
+
+        try:
+            self.print_size_in_Mo(self.sim.state.state_fft, 'state_fft')
+        except AttributeError:
+            self.print_size_in_Mo(self.sim.state.state_phys, 'state_phys')
+
+    def one_time_step(self):
+
+        for k in self.params.periods_print.xml_attrib.keys():
+            period = self.params.periods_print.__dict__[k]
+            if period != 0:
+                self.__dict__[k].online_print()
+
+        if self.params.ONLINE_PLOT_OK:
+            for k in self.params.periods_plot.xml_attrib.keys():
+                period = self.params.periods_plot.__dict__[k]
+                if period != 0:
+                    self.__dict__[k].online_plot()
+
+        if self.has_to_save:
+            for k in self.params.periods_save.xml_attrib.keys():
+                period = self.params.periods_save.__dict__[k]
+                if period != 0:
+                    self.__dict__[k].online_save()
+
+    def figure_axe(self, numfig=None, size_axe=None):
+        if mpi.rank == 0:
+            if size_axe is None:
+                x_left_axe = 0.12
+                z_bottom_axe = 0.1
+                width_axe = 0.85
+                height_axe = 0.84
+                size_axe = [x_left_axe, z_bottom_axe,
+                            width_axe, height_axe]
+            if numfig is None:
+                fig = plt.figure()
+            else:
+                fig = plt.figure(numfig)
+                fig.clf()
+            axe = fig.add_axes(size_axe)
+            return fig, axe
+
+    def end_of_simul(self, total_time):
+        self.print_stdout(
+            'Computation completed in {0:8.6g} s\n'.format(total_time) +
+            'path_run =\n'+self.path_run)
+        if self.has_to_save:
+            self.phys_fields.save()
+        if mpi.rank == 0 and self.has_to_save:
+            self.print_stdout.close()
+
+            for k in self.params.periods_save.xml_attrib.keys():
+                period = self.params.periods_save.__dict__[k]
+                if period != 0:
+                    if hasattr(self.__dict__[k], 'close_file'):
+                        self.__dict__[k].close_file()
+
+        if (not self.path_run.startswith(FLUIDDYN_PATH_SIM) and mpi.rank == 0):
+            new_path_run = os.path.join(FLUIDDYN_PATH_SIM, self.sim.name_run)
+            print('move result directory in directory:\n'+new_path_run)
+            shutil.move(self.path_run, FLUIDDYN_PATH_SIM)
+            self.path_run = new_path_run
+
+    def compute_energy(self):
+        return 0.
+
+    def print_size_in_Mo(self, arr, string=None):
+        if string is None:
+            string = 'Size of ndarray (equiv. seq.)'
+        else:
+            string = 'Size of '+string+' (equiv. seq.)'
+        mem = arr.nbytes*1.e-6
+        if mpi.nb_proc > 1:
+            mem = mpi.comm.allreduce(mem, op=mpi.MPI.SUM)
+        self.print_stdout(string.ljust(30)+': {0} Mo'.format(mem))
+
+
+class OutputBasePseudoSpectral(OutputBase):
+
+    def init_with_oper_and_state(self):
+
+        oper = self.sim.oper
+        self.sum_wavenumbers = oper.sum_wavenumbers
+        self.fft2 = oper.fft2
+        self.ifft2 = oper.ifft2
+        # really necessary here?
+        self.vecfft_from_rotfft = oper.vecfft_from_rotfft
+        self.rotfft_from_vecfft = oper.rotfft_from_vecfft
+
+        super(OutputBasePseudoSpectral, self).init_with_oper_and_state()
+
+
+class SpecificOutput(object):
+    """Small class for features useful for specific outputs"""
+
+    def __init__(self, output, name_file=None,
+                 period_save=0, period_plot=0,
+                 has_to_plot_saved=False,
+                 dico_arrays_1time=None):
+
+        sim = output.sim
+        params = sim.params
+
+        self.output = output
+        self.sim = sim
+        self.oper = sim.oper
+        self.params = params
+
+        self.period_save = period_save
+        self.period_plot = period_plot
+        self.has_to_plot = has_to_plot_saved
+
+        if not params.output.ONLINE_PLOT_OK:
+            self.period_plot = 0
+            self.has_to_plot = False
+
+        if not has_to_plot_saved:
+            if self.period_plot > 0:
+                self.has_to_plot = True
+            else:
+                self.has_to_plot = False
+
+        self.period_show = params.output.period_show_plot
+        self.t_last_show = 0.
+
+        if name_file is not None:
+            self.path_file = self.output.path_run + '/' + name_file
+        else:
+            self.init_path_files()
+
+        if self.has_to_plot and mpi.rank == 0:
+            self.init_online_plot()
+
+        if not output.has_to_save:
+            self.period_save = 0.
+
+        if self.period_save != 0.:
+            self.init_files(dico_arrays_1time)
+
+    def init_path_files(self):
+        pass
+
+    def init_files(self, dico_arrays_1time=None):
+        if dico_arrays_1time is None:
+            dico_arrays_1time = {}
+        dico_results = self.compute()
+        if mpi.rank == 0:
+            if not os.path.exists(self.path_file):
+                self.create_file_from_dico_arrays(
+                    self.path_file, dico_results, dico_arrays_1time)
+                self.nb_saved_times = 1
+            else:
+                with h5py.File(self.path_file, 'r') as f:
+                    dset_times = f['times']
+                    self.nb_saved_times = dset_times.shape[0]+1
+                self.add_dico_arrays_to_file(self.path_file,
+                                             dico_results)
+        self.t_last_save = self.sim.time_stepping.t
+
+    def online_save(self):
+        """Save the values at one time. """
+        tsim = self.sim.time_stepping.t
+        if (tsim - self.t_last_save >= self.period_save):
+            self.t_last_save = tsim
+            dico_results = self.compute()
+            if mpi.rank == 0:
+                self.add_dico_arrays_to_file(self.path_file,
+                                             dico_results)
+                self.nb_saved_times += 1
+                if self.has_to_plot:
+                    self._online_plot(dico_results)
+                    if (tsim - self.t_last_show >= self.period_show):
+                        self.t_last_show = tsim
+                        self.fig.canvas.draw()
+
+    def create_file_from_dico_arrays(self, path_file,
+                                     dico_arrays, dico_arrays_1time):
+        if os.path.exists(path_file):
+            print('file NOT created since it already exists!')
+        elif mpi.rank == 0:
+            with h5py.File(path_file, 'w') as f:
+                f.attrs['date saving'] = str(datetime.datetime.now())
+                f.attrs['name_solver'] = self.output.name_solver
+                f.attrs['name_run'] = self.output.name_run
+
+                self.sim.info.xml_to_hdf5(hdf5_parent=f)
+
+                times = np.array([self.sim.time_stepping.t])
+                f.create_dataset(
+                    'times', data=times, maxshape=(None,))
+
+                for k, v in dico_arrays_1time.iteritems():
+                    f.create_dataset(k, data=v)
+
+                for k, v in dico_arrays.iteritems():
+                    v.resize([1, v.size])
+                    f.create_dataset(
+                        k, data=v, maxshape=(None, v.size))
+
+    def add_dico_arrays_to_file(self, path_file, dico_arrays):
+        if not os.path.exists(path_file):
+            raise ValueError('can not add dico arrays in nonexisting file!')
+        elif mpi.rank == 0:
+            with h5py.File(path_file, 'r+') as f:
+                dset_times = f['times']
+                nb_saved_times = dset_times.shape[0]
+                dset_times.resize((nb_saved_times+1,))
+                dset_times[nb_saved_times] = self.sim.time_stepping.t
+                for k, v in dico_arrays.iteritems():
+                    dset_k = f[k]
+                    dset_k.resize((nb_saved_times+1, v.size))
+                    dset_k[nb_saved_times] = v
+
+    def add_dico_arrays_to_open_file(self, f, dico_arrays, nb_saved_times):
+        if mpi.rank == 0:
+            dset_times = f['times']
+            dset_times.resize((nb_saved_times+1,))
+            dset_times[nb_saved_times] = self.sim.time_stepping.t
+            for k, v in dico_arrays.iteritems():
+                dset_k = f[k]
+                dset_k.resize((nb_saved_times+1, v.size))
+                dset_k[nb_saved_times] = v
diff --git a/fluidsim/base/output/increments.py b/fluidsim/base/output/increments.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvaW5jcmVtZW50cy5weQ==
--- /dev/null
+++ b/fluidsim/base/output/increments.py
@@ -0,0 +1,909 @@
+
+
+from __future__ import division, print_function
+
+import h5py
+import os
+import numpy as np
+
+from fluiddyn.util import mpi
+
+from .base import SpecificOutput
+
+
+class Increments(SpecificOutput):
+    """A :class:`Increments` object handles the saving of pdf of
+    increments.
+    """
+
+    _tag = 'increments'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        tag = 'increments'
+
+        params.output.periods_save.set_attrib(tag, 0)
+        params.output.set_child(tag,
+                                attribs={'HAS_TO_PLOT_SAVED': False})
+
+    def __init__(self, output):
+        params = output.sim.params
+        self.nx = params.oper.nx
+
+        self.nrx = min(self.nx//16, 128)
+        self.nrx = max(self.nrx, self.nx//2)
+        rmin = 1
+        rmax = int(0.8*self.nx)
+        delta_logr = np.log(rmax/rmin)/(self.nrx-1)
+        logr = np.log(rmin) + delta_logr*np.arange(self.nrx)
+        self.rxs = np.array(np.round(np.exp(logr)), dtype=np.int32)
+
+        for ir in xrange(1, self.nrx):
+            if self.rxs[ir-1] >= self.rxs[ir]:
+                self.rxs[ir] = self.rxs[ir-1] + 1
+
+        self.nbins = 400
+
+        name_file = 'increments.h5'
+        self.path_file = output.path_run+'/'+name_file
+
+        if os.path.exists(self.path_file):
+            if mpi.rank == 0:
+                with h5py.File(self.path_file, 'r') as f:
+                    self.rxs = f['rxs'][...]
+                    self.nbins = f['nbins'][...]
+            if mpi.nb_proc > 1:
+                self.rxs = mpi.comm.bcast(self.rxs)
+                self.nbins = mpi.comm.bcast(self.nbins)
+
+        self.nrx = self.rxs.size
+        dico_arrays_1time = {
+            'rxs': self.rxs,
+            'nbins': self.nbins}
+
+        self.keys_vars_to_compute = list(output.sim.state.state_phys.keys)
+
+        super(Increments, self).__init__(
+            output,
+            name_file=name_file,
+            period_save=params.output.periods_save.increments,
+            has_to_plot_saved=params.output.increments.HAS_TO_PLOT_SAVED,
+            dico_arrays_1time=dico_arrays_1time)
+
+    def init_online_plot(self):
+        self.fig, axe = self.output.figure_axe(numfig=5000000)
+        self.axe = axe
+        axe.set_xlabel('$\delta u_x (x)$')
+        axe.set_ylabel('pdf')
+        axe.set_title(
+            'pdf $\delta u_x (x)$, solver ' + self.output.name_solver +
+            ', nh = {0:5d}'.format(self.nx))
+        axe.hold(True)
+
+    def _online_plot(self, dico_results, key='rot'):
+        """online plot on pdf"""
+        pdf = dico_results['pdf_delta_'+key]
+        pdf = pdf.reshape([self.nrx, self.nbins])
+        valmin = dico_results['valmin_'+key]
+        valmax = dico_results['valmax_'+key]
+
+        for irx, rx in enumerate(self.rxs):
+            values_inc = self.compute_values_inc(
+                valmin[irx], valmax[irx])
+            self.axe.plot(values_inc+irx, pdf[irx])
+
+    def compute(self):
+        """compute the values at one time."""
+        dico_results = {}
+        for key in self.keys_vars_to_compute:
+            var = self.sim.state(key)
+
+            pdf_var = np.empty([self.nrx, self.nbins])
+            valmin = np.empty([self.nrx])
+            valmax = np.empty([self.nrx])
+
+            for irx, rx in enumerate(self.rxs):
+                inc_var = self.oper.compute_increments_dim1(var, rx)
+                (pdf_var[irx], bin_edges_var
+                 ) = self.oper.pdf_normalized(inc_var, self.nbins)
+                valmin[irx] = bin_edges_var[0]
+                valmax[irx] = bin_edges_var[self.nbins]
+
+            dico_results['pdf_delta_'+key] = pdf_var.flatten()
+            dico_results['valmin_'+key] = valmin
+            dico_results['valmax_'+key] = valmax
+
+        return dico_results
+
+    def compute_values_inc(self, valmin, valmax):
+        return (valmin +
+                (valmax-valmin)/self.nbins*np.arange(0.5, self.nbins))
+
+    def load(self):
+        """load the saved pdf and return a dictionary."""
+        f = h5py.File(self.path_file, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+
+        list_base_keys = ['pdf_delta_', 'valmin_', 'valmax_', 'struc_func_']
+
+        dico_results = {'times': times}
+        for key in self.keys_vars_to_compute:
+            for base_key in list_base_keys:
+                dset_pdf = f[base_key+key]
+                result = dset_pdf[...]
+                dico_results[base_key+key] = result
+
+        return dico_results
+
+    def plot(self, tmin=0, tmax=None, delta_t=2, order=2, yscale='log'):
+        """Plot some structure functions."""
+        f = h5py.File(self.path_file, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+        # nt = len(times)
+
+        if tmax is None:
+            tmax = times.max()
+
+        rxs = f['rxs'][...]
+
+        oper = f['/info_simul/params/oper']
+        nx = oper.attrs['nx']
+        Lx = oper.attrs['Lx']
+        deltax = Lx/nx
+
+        rxs = np.array(rxs, dtype=np.float64)*deltax
+
+        # orders = f['orders'][...]
+        # dset_struc_func_ux = f['struc_func_ux']
+        # dset_struc_func_uy = f['struc_func_uy']
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot = 1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        to_print = 'plot(tmin={0}, tmax={1}, delta_t={2:.2f})'.format(
+            tmin, tmax, delta_t)
+        print(to_print)
+
+        to_print = ('plot structure functions\n'
+                    'tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}\n'
+                    'imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}').format(
+                        tmin_plot, tmax_plot, delta_t,
+                        imin_plot, imax_plot, delta_i_plot)
+        print(to_print)
+
+
+        pdf_ux, values_inc_ux, nb_rx_to_plot = self.load_pdf_from_file(
+            tmin=tmin, tmax=tmax, key_var='ux')
+
+        pdf_uy, values_inc_uy, nb_rx_to_plot = self.load_pdf_from_file(
+            tmin=tmin, tmax=tmax, key_var='uy')
+
+        # iorder = self.iorder_from_order(order)
+        order = float(order)
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.56
+        width_axe = 0.85
+        height_axe = 0.37
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('$r_x$')
+        ax1.set_ylabel(r'$\langle \delta u^{' +
+                       '{0}'.format(order) + '} \\rangle$')
+
+        ax1.set_title('struct. functions, solver '+self.output.name_solver+
+                      ', nh = {0:5d}'.format(self.nx))
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale(yscale)
+
+        So_ux = self.strfunc_from_pdf(pdf_ux, values_inc_ux, order)
+        So_uy = self.strfunc_from_pdf(pdf_uy, values_inc_uy, order)
+
+        norm = rxs
+
+        # ax1.set_ylabel('struct. functions, order = {0}'.format(order))
+        # if delta_t != 0.:
+        #     for it in xrange(imin_plot,imax_plot+1,delta_i_plot):
+        #         struc_func_ux = dset_struc_func_ux[it]
+        #         struc_func_ux = struc_func_ux.reshape(
+        #             [self.norders, self.nrx])
+        #         struc_func_uy = dset_struc_func_uy[it]
+        #         struc_func_uy = struc_func_uy.reshape(
+        #             [self.norders, self.nrx])
+
+        #         ax1.plot(rxs, struc_func_ux[iorder], 'c', linewidth=1)
+        #         ax1.plot(rxs, struc_func_uy[iorder], 'm', linewidth=1)
+
+        # struc_func_ux = dset_struc_func_ux[imin_plot:imax_plot+1].mean(0)
+        # struc_func_ux = struc_func_ux.reshape([self.norders, self.nrx])
+        # struc_func_uy = dset_struc_func_uy[imin_plot:imax_plot+1].mean(0)
+        # struc_func_uy = struc_func_uy.reshape([self.norders, self.nrx])
+
+        # ax1.plot(rxs, struc_func_ux[iorder]/norm, 'c', linewidth=2)
+        # ax1.plot(rxs, struc_func_uy[iorder]/norm, 'm', linewidth=2)
+
+        ax1.plot(rxs, So_ux/norm, 'c-.', linewidth=2)
+        ax1.plot(rxs, So_uy/norm, 'm-.', linewidth=2)
+        if order % 2 == 1:
+            ax1.plot(rxs, -So_ux/norm, 'c:', linewidth=2)
+            ax1.plot(rxs, -So_uy/norm, 'm:', linewidth=2)
+
+        # ax1.plot(rxs, abs(struc_func_ux[iorder])/abs(struc_func_uy[iorder]),
+        #          'k', linewidth=1)
+
+        ax1.plot(rxs, abs(So_ux)/abs(So_uy),
+                 'k', linewidth=1)
+
+        # if self.orders[iorder]%2 == 1:
+        #     ax1.plot(rxs, -struc_func_ux[iorder]/norm, '--b', linewidth=2)
+        #     ax1.plot(rxs, -struc_func_uy[iorder]/norm, '--m', linewidth=2)
+
+        cond = rxs < 6*deltax
+        ax1.plot(rxs[cond], 1.e4*rxs[cond]**(order)/norm[cond],
+                 'k', linewidth=2)
+        ax1.plot(rxs, rxs**(order/3)/norm, '--k', linewidth=2)
+
+        ax1.plot(rxs, 1.e0*rxs**(1)/norm, ':k', linewidth=2)
+
+
+        z_bottom_axe = 0.09
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+
+
+        ax2.set_xlabel('$r_x$')
+        ax2.set_ylabel('flatness')
+        ax2.set_xscale('log')
+        ax2.set_yscale('log')
+
+        # iorder4 = self.iorder_from_order(4)
+        # iorder2 = self.iorder_from_order(2)
+
+        # if delta_t != 0.:
+        #     for it in xrange(imin_plot,imax_plot+1,delta_i_plot):
+        #         struc_func_ux = dset_struc_func_ux[it]
+        #         struc_func_ux = struc_func_ux.reshape(
+        #             [self.norders, self.nrx])
+        #         struc_func_uy = dset_struc_func_uy[it]
+        #         struc_func_uy = struc_func_uy.reshape(
+        #             [self.norders, self.nrx])
+
+        #         flatnessL = struc_func_ux[iorder4]/struc_func_ux[iorder2]**2
+        #         flatnessT = struc_func_uy[iorder4]/struc_func_uy[iorder2]**2
+
+        #         ax2.plot(rxs, flatnessL, 'c', linewidth=1)
+        #         ax2.plot(rxs, flatnessT, 'm', linewidth=1)
+
+        # struc_func_ux = dset_struc_func_ux[imin_plot:imax_plot+1].mean(0)
+        # struc_func_ux = struc_func_ux.reshape([self.norders, self.nrx])
+        # struc_func_uy = dset_struc_func_uy[imin_plot:imax_plot+1].mean(0)
+        # struc_func_uy = struc_func_uy.reshape([self.norders, self.nrx])
+
+        # flatnessL = struc_func_ux[iorder4]/struc_func_ux[iorder2]**2
+        # flatnessT = struc_func_uy[iorder4]/struc_func_uy[iorder2]**2
+        # ax2.plot(rxs, flatnessL, 'c', linewidth=2)
+        # ax2.plot(rxs, flatnessT, 'm', linewidth=2)
+
+        S2_ux = self.strfunc_from_pdf(pdf_ux, values_inc_ux, 2)
+        S2_uy = self.strfunc_from_pdf(pdf_uy, values_inc_uy, 2)
+
+        S4_ux = self.strfunc_from_pdf(pdf_ux, values_inc_ux, 4)
+        S4_uy = self.strfunc_from_pdf(pdf_uy, values_inc_uy, 4)
+
+        flatnessL_bis = S4_ux/S2_ux**2
+        flatnessT_bis = S4_uy/S2_uy**2
+
+        ax2.plot(rxs, flatnessL_bis, 'c--', linewidth=2)
+        ax2.plot(rxs, flatnessT_bis, 'm--', linewidth=2)
+
+
+        cond = np.logical_and(rxs < 70*deltax,
+                              rxs > 5*deltax)
+        ax2.plot(rxs[cond], 1e1*rxs[cond]**(-1), ':k', linewidth=2)
+
+        ax2.plot(rxs, 3*np.ones(rxs.shape), 'k--', linewidth=0.5)
+
+
+    def strfunc_from_pdf(self, pdf, values, order, absolute=False):
+        order = float(order)
+        S_order = np.empty(self.rxs.shape)
+        if absolute:
+            values = abs(values)
+        for irx in xrange(self.rxs.size):
+            deltainc = abs(values[irx, 1] - values[irx, 0])
+            S_order[irx] = deltainc*np.sum(
+                pdf[irx]*values[irx]**order)
+        return S_order
+
+
+    def load_pdf_from_file(self, tmin=0, tmax=None, key_var='ux',
+                           irx_to_plot=None):
+        """Plot some pdf."""
+        f = h5py.File(self.path_file, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+        nt = len(times)
+
+        if tmax is None:
+            tmax = times.max()
+
+        rxs = f['rxs'][...]
+
+        oper = f['/info_simul/params/oper']
+        nx = oper.attrs['nx']
+        Lx = oper.attrs['Lx']
+
+        deltax = Lx/nx
+
+        rxs = np.array(rxs, dtype=np.float64)*deltax
+
+        # orders = f['orders'][...]
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_t = delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+
+#         to_print = '''load pdf of the increments
+# tmin = {0:8.6g} ; tmax = {1:8.6g}
+# imin = {2:8d} ; imax = {3:8d}'''.format(
+# tmin_plot, tmax_plot,
+# imin_plot, imax_plot)
+#         print(to_print)
+
+
+        if irx_to_plot is None:
+            irx_to_plot = np.arange(rxs.size)
+
+        nb_rx_to_plot = irx_to_plot.size
+
+        # print 'irx_to_plot', irx_to_plot
+        # print 'self.rxs[irx_to_plot]', self.rxs[irx_to_plot]
+
+        pdf_timemean = np.zeros([nb_rx_to_plot, self.nbins])
+        values_inc_timemean = np.zeros([nb_rx_to_plot, self.nbins])
+
+        valmin_timemean = np.zeros([nb_rx_to_plot])
+        valmax_timemean = np.zeros([nb_rx_to_plot])
+        nb_timemean = 0
+
+        for it in xrange(imin_plot, imax_plot+1):
+            nb_timemean += 1
+            valmin = f['valmin_'+key_var][it]
+            valmax = f['valmax_'+key_var][it]
+
+            for irxp, irx in enumerate(irx_to_plot):
+                valmin_timemean[irxp] += valmin[irx]
+                valmax_timemean[irxp] += valmax[irx]
+
+        valmin_timemean /= nb_timemean
+        valmax_timemean /= nb_timemean
+
+        for irxp, irx in enumerate(irx_to_plot):
+            values_inc_timemean[irxp] = self.compute_values_inc(
+                valmin_timemean[irxp], valmax_timemean[irxp])
+
+        nt = 0
+        for it in xrange(imin_plot, imax_plot+1):
+            nt += 1
+            pdf_dvar2D = f['pdf_delta_'+key_var][it]
+            pdf_dvar2D = pdf_dvar2D.reshape([self.nrx, self.nbins])
+            valmin = f['valmin_'+key_var][it]
+            valmax = f['valmax_'+key_var][it]
+
+            for irxp, irx in enumerate(irx_to_plot):
+                pdf_dvar = pdf_dvar2D[irx]
+                values_inc = self.compute_values_inc(
+                    valmin[irx], valmax[irx])
+
+                pdf_timemean[irxp] += np.interp(
+                    values_inc_timemean[irxp], values_inc, pdf_dvar)
+
+        pdf_timemean /= nt
+
+
+        return pdf_timemean, values_inc_timemean, nb_rx_to_plot
+
+
+
+
+
+    def plot_pdf(self, tmin=0, tmax=None, key_var='ux',
+                 order=0, nb_rx_to_plot=5
+                 ):
+
+        irx_to_plot = np.arange(0, self.rxs.size, self.rxs.size/nb_rx_to_plot)
+        nb_rx_to_plot = irx_to_plot.size
+
+        (pdf_timemean, values_inc_timemean, nb_rx_to_plot
+         ) = self.load_pdf_from_file(tmin=tmin, tmax=tmax, key_var=key_var,
+                                     irx_to_plot=irx_to_plot)
+
+
+        to_print = 'plot_pdf(tmin={0}, tmax={1})'.format(
+            tmin, tmax)
+        print(to_print)
+
+
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_title('pdf increments, solver '+self.output.name_solver+
+', nh = {0:5d}'.format(self.nx))
+# +', c2 = {0:.4g}, f = {1:.4g}'.format(self.c2, self.f))
+        ax1.hold(True)
+        ax1.set_xscale('linear')
+        ax1.set_yscale('linear')
+
+
+        ax1.set_xlabel(key_var)
+        ax1.set_ylabel('PDF x $\delta v^'+repr(order)+'$')
+
+        colors = ['k', 'y', 'r', 'b', 'g', 'm', 'c']
+
+        for irxp, irx in enumerate(irx_to_plot):
+
+            print('color = {0}, rx = {1}'.format(colors[irxp], self.rxs[irx]))
+
+            val_inc = values_inc_timemean[irxp]
+
+            ax1.plot(val_inc, pdf_timemean[irxp]*val_inc**order,
+                     colors[irxp]+'x-', linewidth=1)
+
+
+
+
+
+
+
+    # def iorder_from_order(self, order):
+    #     """Return the indice corresponding to one value of order."""
+    #     iorder = abs(self.orders-order).argmin()
+    #     if self.orders[iorder] != order:
+    #         raise ValueError(
+    #             'Order {0} has not been computed ?'.format(order)
+    #             )
+    #     return iorder
+
+
+
+
+
+
+
+
+
+
+
+
+
+class IncrementsSW1l(Increments):
+    """A :class:`Increments` object handles the saving of pdf of
+    increments.
+    """
+
+    def __init__(self, output):
+        super(IncrementsSW1l, self).__init__(output)
+        params = output.sim.params
+        self.c2 = params.c2
+        self.f = params.f
+
+
+
+    def _online_plot(self, dico_results, key='eta'):
+        """online plot on pdf"""
+        super(IncrementsSW1l, self)._online_plot(dico_results, key=key)
+
+
+
+    def compute(self):
+        dico_results = super(IncrementsSW1l, self).compute()
+
+        ux = self.sim.state('ux')
+        uy = self.sim.state('uy')
+        eta = self.sim.state('eta')
+        Jx = (1+eta)*ux
+
+        S_uL2JL = np.empty([self.nrx])
+        S_uT2JL = np.empty([self.nrx])
+        S_c2h2uL = np.empty([self.nrx])
+        S_uT2uL = np.empty([self.nrx])
+
+        for irx, rx in enumerate(self.rxs):
+            inc_ux = self.oper.compute_increments_dim1(ux, rx)
+            inc_uy = self.oper.compute_increments_dim1(uy, rx)
+            inc_eta = self.oper.compute_increments_dim1(eta, rx)
+            inc_Jx = self.oper.compute_increments_dim1(Jx, rx)
+            inc_uy2 = inc_uy**2
+            S_uL2JL[irx] = np.mean(inc_ux**2*inc_Jx)
+            S_uT2JL[irx] = np.mean(inc_uy2*inc_Jx)
+            S_c2h2uL[irx] = self.params.c2*np.mean(inc_eta**2*inc_ux)
+            S_uT2uL[irx] = np.mean(inc_uy2*inc_ux)
+
+        dico_results['struc_func_uL2JL'] = S_uL2JL
+        dico_results['struc_func_uT2JL'] = S_uT2JL
+        dico_results['struc_func_c2h2uL'] = S_c2h2uL
+        dico_results['struc_func_Kolmo'] = S_uL2JL + S_uT2JL + S_c2h2uL
+
+        dico_results['struc_func_uT2uL'] = S_uT2uL
+
+        return dico_results
+
+
+
+
+    def plot(self, tmin=0, tmax=None, delta_t=2, order=2, yscale='log'):
+        """Plot some structure functions."""
+        f = h5py.File(self.path_file, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+        # nt = len(times)
+
+        if tmax is None:
+            tmax = times.max()
+
+        rxs = f['rxs'][...]
+
+        oper = f['/info_simul/params/oper']
+        nx = oper.attrs['nx']
+        Lx = oper.attrs['Lx']
+        deltax = Lx/nx
+
+        rxs = np.array(rxs, dtype=np.float64)*deltax
+
+        # orders = f['orders'][...]
+        # dset_struc_func_ux = f['struc_func_ux']
+        # dset_struc_func_uy = f['struc_func_uy']
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot=1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        to_print = 'plot(tmin={0}, tmax={1}, delta_t={2:.2f})'.format(
+            tmin, tmax, delta_t)
+        print(to_print)
+
+        to_print = '''plot structure functions
+tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}
+imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}'''.format(
+tmin_plot, tmax_plot, delta_t,
+imin_plot, imax_plot, delta_i_plot)
+        print(to_print)
+
+
+        pdf_eta, values_inc_eta, nb_rx_to_plot = self.load_pdf_from_file(
+            tmin=tmin, tmax=tmax, key_var='eta')
+
+        pdf_ux, values_inc_ux, nb_rx_to_plot = self.load_pdf_from_file(
+            tmin=tmin, tmax=tmax, key_var='ux')
+
+        pdf_uy, values_inc_uy, nb_rx_to_plot = self.load_pdf_from_file(
+            tmin=tmin, tmax=tmax, key_var='uy')
+
+
+        # iorder = self.iorder_from_order(order)
+        order = float(order)
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.56
+        width_axe = 0.85
+        height_axe = 0.37
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('$r_x$')
+        ax1.set_ylabel('$\langle \delta u^{'+'{0}'.format(order)+'} \\rangle$')
+
+
+        ax1.set_title('struct. functions, solver '+self.output.name_solver+
+                      ', nh = {0:5d}'.format(self.nx))
+# +', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f))
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale(yscale)
+
+
+
+
+
+
+
+        # So_eta = self.strfunc_from_pdf(pdf_eta, values_inc_eta, order)
+        So_ux = self.strfunc_from_pdf(pdf_ux, values_inc_ux, order)
+        So_uy = self.strfunc_from_pdf(pdf_uy, values_inc_uy, order)
+
+        norm = rxs
+
+        # ax1.set_ylabel('struct. functions, order = {0}'.format(order))
+        # if delta_t != 0.:
+        #     for it in xrange(imin_plot,imax_plot+1,delta_i_plot):
+        #         struc_func_ux = dset_struc_func_ux[it]
+        #         struc_func_ux = struc_func_ux.reshape(
+        #             [self.norders, self.nrx])
+        #         struc_func_uy = dset_struc_func_uy[it]
+        #         struc_func_uy = struc_func_uy.reshape(
+        #             [self.norders, self.nrx])
+
+        #         ax1.plot(rxs, struc_func_ux[iorder], 'c', linewidth=1)
+        #         ax1.plot(rxs, struc_func_uy[iorder], 'm', linewidth=1)
+
+
+
+        # struc_func_ux = dset_struc_func_ux[imin_plot:imax_plot+1].mean(0)
+        # struc_func_ux = struc_func_ux.reshape([self.norders, self.nrx])
+        # struc_func_uy = dset_struc_func_uy[imin_plot:imax_plot+1].mean(0)
+        # struc_func_uy = struc_func_uy.reshape([self.norders, self.nrx])
+
+        # ax1.plot(rxs, struc_func_ux[iorder]/norm, 'c', linewidth=2)
+        # ax1.plot(rxs, struc_func_uy[iorder]/norm, 'm', linewidth=2)
+
+        ax1.plot(rxs, So_ux/norm, 'c-.', linewidth=2)
+        ax1.plot(rxs, So_uy/norm, 'm-.', linewidth=2)
+        if order % 2 == 1:
+            ax1.plot(rxs, -So_ux/norm, 'c:', linewidth=2)
+            ax1.plot(rxs, -So_uy/norm, 'm:', linewidth=2)
+
+        # ax1.plot(rxs, abs(struc_func_ux[iorder])/abs(struc_func_uy[iorder]),
+        #          'k', linewidth=1)
+
+        ax1.plot(rxs, abs(So_ux)/abs(So_uy),
+                 'k', linewidth=1)
+
+        # if self.orders[iorder]%2 == 1:
+        #     ax1.plot(rxs, -struc_func_ux[iorder]/norm, '--b', linewidth=2)
+        #     ax1.plot(rxs, -struc_func_uy[iorder]/norm, '--m', linewidth=2)
+
+        cond = rxs < 6*deltax
+        ax1.plot(rxs[cond], 1.e4*rxs[cond]**(order)/norm[cond],
+                 'k', linewidth=2)
+        ax1.plot(rxs, rxs**(order/3)/norm, '--k', linewidth=2)
+
+        ax1.plot(rxs, 1.e0*rxs**(1)/norm, ':k', linewidth=2)
+
+
+        z_bottom_axe = 0.09
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+
+
+        ax2.set_xlabel('$r_x$')
+        ax2.set_ylabel('flatness')
+        ax2.set_xscale('log')
+        ax2.set_yscale('log')
+
+        # iorder4 = self.iorder_from_order(4)
+        # iorder2 = self.iorder_from_order(2)
+
+        # if delta_t != 0.:
+        #     for it in xrange(imin_plot,imax_plot+1,delta_i_plot):
+        #         struc_func_ux = dset_struc_func_ux[it]
+        #         struc_func_ux = struc_func_ux.reshape(
+        #             [self.norders, self.nrx])
+        #         struc_func_uy = dset_struc_func_uy[it]
+        #         struc_func_uy = struc_func_uy.reshape(
+        #             [self.norders, self.nrx])
+
+        #         flatnessL = struc_func_ux[iorder4]/struc_func_ux[iorder2]**2
+        #         flatnessT = struc_func_uy[iorder4]/struc_func_uy[iorder2]**2
+
+        #         ax2.plot(rxs, flatnessL, 'c', linewidth=1)
+        #         ax2.plot(rxs, flatnessT, 'm', linewidth=1)
+
+        # struc_func_ux = dset_struc_func_ux[imin_plot:imax_plot+1].mean(0)
+        # struc_func_ux = struc_func_ux.reshape([self.norders, self.nrx])
+        # struc_func_uy = dset_struc_func_uy[imin_plot:imax_plot+1].mean(0)
+        # struc_func_uy = struc_func_uy.reshape([self.norders, self.nrx])
+
+        # flatnessL = struc_func_ux[iorder4]/struc_func_ux[iorder2]**2
+        # flatnessT = struc_func_uy[iorder4]/struc_func_uy[iorder2]**2
+        # ax2.plot(rxs, flatnessL, 'c', linewidth=2)
+        # ax2.plot(rxs, flatnessT, 'm', linewidth=2)
+
+
+        S2_eta = self.strfunc_from_pdf(pdf_eta, values_inc_eta, 2)
+        S2_ux = self.strfunc_from_pdf(pdf_ux, values_inc_ux, 2)
+        S2_uy = self.strfunc_from_pdf(pdf_uy, values_inc_uy, 2)
+
+        S4_eta = self.strfunc_from_pdf(pdf_eta, values_inc_eta, 4)
+        S4_ux = self.strfunc_from_pdf(pdf_ux, values_inc_ux, 4)
+        S4_uy = self.strfunc_from_pdf(pdf_uy, values_inc_uy, 4)
+
+        flatnessL_bis = S4_ux/S2_ux**2
+        flatnessT_bis = S4_uy/S2_uy**2
+        flatness_eta = S4_eta/S2_eta**2
+
+        ax2.plot(rxs, flatnessL_bis, 'c--', linewidth=2)
+        ax2.plot(rxs, flatnessT_bis, 'm--', linewidth=2)
+        ax2.plot(rxs, flatness_eta, 'y--', linewidth=2)
+
+        cond = np.logical_and(rxs < 70*deltax,
+                              rxs > 5*deltax)
+        ax2.plot(rxs[cond], 1e1*rxs[cond]**(-1), ':k', linewidth=2)
+
+        ax2.plot(rxs, 3*np.ones(rxs.shape), 'k--', linewidth=0.5)
+
+
+
+
+    def plot_Kolmo(self, tmin=0, tmax=None):
+        """Plot quantities appearing in the Kolmogorov law."""
+        f = h5py.File(self.path_file, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+
+        if tmax is None:
+            tmax = times.max()
+
+        rxs = f['rxs'][...]
+
+        oper = f['/info_simul/params/oper']
+        nx = oper.attrs['nx']
+        Lx = oper.attrs['Lx']
+        deltax = Lx/nx
+
+        rxs = np.array(rxs, dtype=np.float64)*deltax
+
+
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        to_print = 'plot(tmin={0}, tmax={1})'.format(
+            tmin, tmax)
+        print(to_print)
+
+        to_print = '''plot structure functions
+tmin = {0:8.6g} ; tmax = {1:8.6g}
+imin = {2:8d} ; imax = {3:8d}'''.format(
+tmin_plot, tmax_plot,
+imin_plot, imax_plot)
+        print(to_print)
+
+
+        # dset_struc_func_ux = f['struc_func_ux']
+        # struc_func_ux = dset_struc_func_ux[imin_plot:imax_plot+1].mean(0)
+        # struc_func_ux = struc_func_ux.reshape([self.norders, self.nrx])
+        # order = 3
+        # iorder = self.iorder_from_order(order)
+        # S_ux3 = struc_func_ux[iorder]
+
+        S_uL2JL = f['struc_func_uL2JL'][imin_plot:imax_plot+1].mean(0)
+        S_uT2JL = f['struc_func_uT2JL'][imin_plot:imax_plot+1].mean(0)
+        S_c2h2uL = f['struc_func_c2h2uL'][imin_plot:imax_plot+1].mean(0)
+        S_Kolmo = f['struc_func_Kolmo'][imin_plot:imax_plot+1].mean(0)
+        S_uT2uL = f['struc_func_uT2uL'][imin_plot:imax_plot+1].mean(0)
+
+        S_Kolmo_theo = -4*rxs
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.56
+        width_axe = 0.85
+        height_axe = 0.37
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('$r_x$')
+        title = ('struct. functions, solver '+self.output.name_solver+
+', nh = {0:5d}'.format(self.nx)+
+', c2 = {0:.4g}, f = {1:.4g}'.format(self.c2, self.f)
+)
+        ax1.set_title(title)
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('linear')
+
+        ax1.set_ylabel('struct. functions')
+
+
+        ax1.plot(rxs, S_Kolmo/S_Kolmo_theo, 'k', linewidth=2)
+        ax1.plot(rxs, (S_uL2JL+S_uT2JL)/S_Kolmo_theo, 'r', linewidth=2)
+
+        ax1.plot(rxs, S_c2h2uL/S_Kolmo_theo, 'b', linewidth=2)
+
+        ax1.plot(rxs, S_uL2JL/S_Kolmo_theo, 'r--', linewidth=1)
+        ax1.plot(rxs, S_uT2JL/S_Kolmo_theo, 'r-.', linewidth=1)
+
+        ax1.plot(rxs,
+                 (S_uL2JL+S_uT2JL+S_c2h2uL)/S_Kolmo_theo,
+                 'y', linewidth=1)
+
+
+
+        cond = rxs < 6*deltax
+        ax1.plot(rxs[cond], 1.e0*rxs[cond]**3/S_Kolmo_theo[cond],
+                 'k', linewidth=2)
+
+
+        ax1.plot(rxs, np.ones(rxs.shape), 'k:', linewidth=1)
+
+        ax1.set_ylim([5e-2, 1.5])
+
+
+
+        z_bottom_axe = 0.09
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+
+        ax2.set_xlabel('$r_x$')
+        ax2.set_ylabel('ratio S_ux3/S_uT2uL')
+        ax2.set_xscale('log')
+        ax2.set_yscale('linear')
+
+        # ax2.plot(rxs, S_ux3/S_uT2uL, 'k', linewidth=2)
+
+        ax2.plot(rxs, S_uL2JL/S_uT2JL, 'k--', linewidth=2)
+
+
+        ax2.plot(rxs, 3*np.ones(rxs.shape), 'k:', linewidth=1)
+
+
+        ax2.set_ylim([2, 5])
+
+
+
+
+
+
+if __name__=="__main__":
+
+    from solveq2d import solveq2d
+
+    import glob
+
+    c = 20
+    resol = 240*2**2  # 4
+
+    str_resol = repr(resol)
+    str_to_find_path = (
+        '/scratch/augier/Results_SW1lw'
+        '/Pure_standing_waves_'+
+        str_resol+'*/SE2D*c='+repr(c))+'_*'
+    print(str_to_find_path)
+
+    paths_dir = glob.glob(str_to_find_path)
+
+    sim = solveq2d.create_sim_plot_from_dir(paths_dir[0])
+
+    tmin = sim.output.spatial_means.first_saved_time()
+    tstatio = tmin + 4.
+
+
+    # sim.output.increments.plot(tmin=tmin, tmax=None, delta_t=0.,
+    #                            order=4, yscale='log')
+
+
+    # sim.output.increments.plot_pdf(tmin=tmin, tmax=160.25, key_var='ux',
+    #                                order=4)
+
+    sim.output.increments.plot_Kolmo(tmin=tmin)
+
+
+    solveq2d.show()
diff --git a/fluidsim/base/output/phys_fields.py b/fluidsim/base/output/phys_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvcGh5c19maWVsZHMucHk=
--- /dev/null
+++ b/fluidsim/base/output/phys_fields.py
@@ -0,0 +1,277 @@
+"""Physical fields output (:mod:`fluidsim.base.output.phys_fields`)
+=========================================================================
+
+.. currentmodule:: fluidsim.base.output.phys_fields
+
+Provides:
+
+.. autoclass:: PhysFieldsBase
+   :members:
+   :private-members:
+
+"""
+
+import matplotlib.pyplot as plt
+import numpy as np
+import h5py
+import os
+import datetime
+
+from fluiddyn.util import mpi
+from .base import SpecificOutput
+
+
+class PhysFieldsBase(SpecificOutput):
+    """Manage the output of physical fields."""
+
+    _tag = 'phys_fields'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        tag = 'phys_fields'
+        params.output.set_child(tag,
+                                attribs={'field_to_plot': 'ux'})
+
+        params.output.periods_save.set_attrib(tag, 0)
+        params.output.periods_plot.set_attrib(tag, 0)
+
+    def __init__(self, output):
+        params = output.sim.params
+
+        super(PhysFieldsBase, self).__init__(
+            output,
+            period_save=params.output.periods_save.phys_fields,
+            period_plot=params.output.periods_plot.phys_fields)
+
+        self.field_to_plot = params.output.phys_fields.field_to_plot
+
+        if self.period_save == 0 and self.period_plot == 0:
+            return
+
+        self.t_last_save = self.sim.time_stepping.t
+
+        self.t_last_plot = self.sim.time_stepping.t
+
+    def init_files(self, dico_arrays_1time=None):
+        pass
+
+    def init_online_plot(self):
+        pass
+
+    def online_save(self):
+        """Online save."""
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_save >= self.period_save):
+            self.t_last_save = tsim
+            self.save()
+
+    def online_plot(self):
+        """Online plot."""
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_plot >= self.period_plot):
+            self.t_last_plot = tsim
+            itsim = self.sim.time_stepping.it
+            self.plot(numfig=itsim,
+                      key_field=self.params.output.phys_fields.field_to_plot)
+
+    def save(self, state_phys=None, params=None, time=None,
+             particular_attr=None):
+        if state_phys is None:
+            state_phys = self.sim.state.state_phys
+        if params is None:
+            params = self.params
+        if time is None:
+            time = self.sim.time_stepping.t
+
+        path_run = self.output.path_run
+
+        if mpi.rank == 0 and not os.path.exists(path_run):
+            os.mkdir(path_run)
+
+        if mpi.rank == 0:
+            name_save = \
+                'state_phys_t={0:7.3f}.hd5'.format(time).replace(' ', '0')
+            path_file = path_run+'/'+name_save
+            to_print = 'save state_phys in file '+name_save
+            self.output.print_stdout(to_print)
+
+            f = h5py.File(path_file, 'w')
+            f.attrs['date saving'] = str(datetime.datetime.now())
+            f.attrs['name_solver'] = self.output.name_solver
+            f.attrs['name_run'] = self.output.name_run
+            if particular_attr is not None:
+                f.attrs['particular_attr'] = particular_attr
+
+            self.sim.info.xml_to_hdf5(hdf5_parent=f)
+            gp_info = f['info_simul']
+            gf_params = gp_info['params']
+            gf_params.attrs['SAVE'] = True
+            gf_params.attrs['NEW_DIR_RESULTS'] = True
+
+            group_state_phys = f.create_group("state_phys")
+            group_state_phys.attrs['what'] = 'obj state_phys for solveq2d'
+            group_state_phys.attrs['name_type_variables'] = (
+                state_phys.name_type_variables)
+            group_state_phys.attrs['time'] = time
+
+        for k in state_phys.keys:
+            field_loc = state_phys[k]
+            if mpi.nb_proc > 1:
+                field_seq = self.oper.gather_Xspace(field_loc)
+            else:
+                field_seq = field_loc
+            if mpi.rank == 0:
+                group_state_phys.create_dataset(k, data=field_seq)
+
+        if mpi.rank == 0:
+            f.close()
+
+    def plot(self, numfig=None, field=None, key_field=None,
+             QUIVER=True, vecx='ux', vecy='uy', FIELD_LOC=True,
+             nb_contours=20, type_plot='contourf'):
+
+        x_left_axe = 0.08
+        z_bottom_axe = 0.07
+        width_axe = 0.97
+        height_axe = 0.87
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+
+        keys_state_phys = self.sim.info.solver.classes.State['keys_state_phys']
+        keys_computable = self.sim.info.solver.classes.State['keys_computable']
+
+        if vecx not in keys_state_phys or vecy not in keys_state_phys:
+            QUIVER = False
+
+        if field is None:
+            if key_field is None:
+                field_to_plot = self.params.output.phys_fields.field_to_plot
+                if (field_to_plot in keys_state_phys and
+                        field_to_plot in keys_computable):
+                    key_field = field_to_plot
+                else:
+                    if 'q' in keys_state_phys:
+                        key_field = 'q'
+                    elif 'rot' in keys_state_phys:
+                        key_field = 'rot'
+                    else:
+                        key_field = keys_state_phys[0]
+
+            field_loc = self.sim.state(key_field)
+        else:
+            key_field = 'given field'
+            if FIELD_LOC:
+                field_loc = field
+
+        if mpi.nb_proc > 1 and FIELD_LOC:
+            field = self.oper.gather_Xspace(field_loc)
+        else:
+            field = field_loc
+
+        if mpi.rank == 0:
+            if numfig is None:
+                fig, ax = self.output.figure_axe(size_axe=size_axe)
+            else:
+                fig, ax = self.output.figure_axe(numfig=numfig,
+                                                 size_axe=size_axe)
+            x_seq = self.oper.x_seq
+            y_seq = self.oper.y_seq
+            [XX_seq, YY_seq] = np.meshgrid(x_seq, y_seq)
+
+            if type_plot == 'contourf':
+                contours = ax.contourf(x_seq, y_seq, field,
+                                       nb_contours, cmap=plt.cm.jet)
+                fig.colorbar(contours)
+                fig.contours = contours
+            elif type_plot == 'pcolor':
+                pc = ax.pcolormesh(x_seq, y_seq, field,
+                                   cmap=plt.cm.jet)
+                fig.colorbar(pc)
+
+        if QUIVER:
+            if isinstance(vecx, str):
+                vecx_loc = self.sim.state(vecx)
+                if mpi.nb_proc > 1:
+                    vecx = self.oper.gather_Xspace(vecx_loc)
+                else:
+                    vecx = vecx_loc
+            if isinstance(vecy, str):
+                vecy_loc = self.sim.state(vecy)
+                if mpi.nb_proc > 1:
+                    vecy = self.oper.gather_Xspace(vecy_loc)
+                else:
+                    vecy = vecy_loc
+            pas_vector = np.round(self.oper.nx_seq/48)
+            if pas_vector < 1:
+                pas_vector = 1
+
+            if mpi.rank == 0:
+                ax.quiver(XX_seq[::pas_vector, ::pas_vector],
+                          YY_seq[::pas_vector, ::pas_vector],
+                          vecx[::pas_vector, ::pas_vector],
+                          vecy[::pas_vector, ::pas_vector])
+
+        if mpi.rank == 0:
+            ax.set_xlabel('x')
+            ax.set_ylabel('y')
+
+            title = (key_field +
+                     ', t = {0:.3f}, '.format(self.sim.time_stepping.t) +
+                     self.output.name_solver +
+                     ', nh = {0:d}'.format(self.params.oper.nx))
+
+            if QUIVER:
+                title += r', max(|v|) = {0:.3f}'.format(
+                    np.max(np.sqrt(vecx**2+vecy**2)))
+
+            ax.set_title(title)
+
+            fig.canvas.draw()
+
+
+class PhysFieldsBase1D(PhysFieldsBase):
+
+    def plot(self, numfig=None, field=None, key_field=None):
+
+        # x_left_axe = 0.08
+        # z_bottom_axe = 0.07
+        # width_axe = 0.87
+        # height_axe = 0.87
+        # size_axe = [x_left_axe, z_bottom_axe,
+        #             width_axe, height_axe]
+
+        keys_state_phys = self.sim.info.solver.classes.State['keys_state_phys']
+        keys_computable = self.sim.info.solver.classes.State['keys_computable']
+
+        if field is None:
+            if key_field is None:
+                field_to_plot = self.params.output.phys_fields.field_to_plot
+                if (field_to_plot in keys_state_phys and
+                        field_to_plot in keys_computable):
+                    key_field = field_to_plot
+                else:
+                    if 'q' in keys_state_phys:
+                        key_field = 'q'
+                    elif 'rot' in keys_state_phys:
+                        key_field = 'rot'
+                    else:
+                        key_field = keys_state_phys[0]
+
+            field_loc = self.sim.state(key_field)
+        else:
+            key_field = 'given field'
+
+        if mpi.nb_proc > 1:
+            field = self.oper.gather_Xspace(field_loc)
+        else:
+            field = field_loc
+
+        if mpi.rank == 0:
+            if numfig is None:
+                fig, ax = self.output.figure_axe(size_axe=None)
+            else:
+                fig, ax = self.output.figure_axe(numfig=numfig,
+                                                 size_axe=None)
+            xs = self.oper.xs
+
+            ax.plot(xs, field)
diff --git a/fluidsim/base/output/print_stdout.py b/fluidsim/base/output/print_stdout.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvcHJpbnRfc3Rkb3V0LnB5
--- /dev/null
+++ b/fluidsim/base/output/print_stdout.py
@@ -0,0 +1,87 @@
+
+from __future__ import print_function
+
+import os
+# import numpy as np
+
+# from time import time
+
+from fluiddyn.util import mpi
+
+
+class PrintStdOutBase(object):
+    """A :class:`PrintStdOutBase` object is used to print in both the
+    stdout and the stdout.txt file, and also to print simple info on
+    the current state of the simulation."""
+
+    _tag = 'print_stdout'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        params.output.periods_print.set_attrib('print_stdout', 1.)
+
+    def __init__(self, output):
+        sim = output.sim
+        params = sim.params
+
+        self.output = output
+        self.sim = sim
+        self.params = params
+
+        try:
+            self.c2 = params.c2
+            self.f = params.f
+        except AttributeError:
+            pass
+
+        self.nx = params.oper.nx
+
+        self.period_print = params.output.periods_print.print_stdout
+
+        self.path_file = self.output.path_run+'/stdout.txt'
+
+        if mpi.rank == 0 and self.output.has_to_save:
+                if not os.path.exists(self.path_file):
+                    self.file = open(self.path_file, 'w')
+                else:
+                    self.file = open(self.path_file, 'r+')
+                    self.file.seek(0, 2)  # go to the end of the file
+
+    def complete_init_with_state(self):
+
+        self.energy0 = self.output.compute_energy()
+
+        if self.period_print == 0:
+            return
+
+        self.energy_temp = self.energy0+0.
+        self.t_last_print_info = -self.period_print
+        self.t_real_word_last = 0.
+
+        self.print_stdout = self.__call__
+
+    def __call__(self, to_print, end='\n'):
+        """Print in stdout and if SAVE in the file stdout.txt"""
+        if mpi.rank == 0:
+            print(to_print, end=end)
+            if self.output.has_to_save:
+                self.file.write(to_print+end)
+                self.file.flush()
+                os.fsync(self.file.fileno())
+
+    def online_print(self):
+        """Print simple info on the current state of the simulation"""
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_print_info >= self.period_print):
+            self.t_last_print_info = tsim
+            self.print_stdout(
+                'it = {0:6d} ; t = {1:10.6g} ; deltat  = {2:10.5g}\n'.format(
+                    self.sim.time_stepping.it,
+                    self.sim.time_stepping.t,
+                    self.sim.time_stepping.deltat))
+
+    def close(self):
+        try:
+            self.file.close()
+        except AttributeError:
+            pass
diff --git a/fluidsim/base/output/prob_dens_func.py b/fluidsim/base/output/prob_dens_func.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvcHJvYl9kZW5zX2Z1bmMucHk=
--- /dev/null
+++ b/fluidsim/base/output/prob_dens_func.py
@@ -0,0 +1,169 @@
+
+import h5py
+import numpy as np
+
+from fluidsim.base.output.base import SpecificOutput
+
+
+class ProbaDensityFunc(SpecificOutput):
+    """A :class:`ProbaDensityFunc` object handles the saving of pdf.
+    """
+    _tag = 'pdf'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        tag = 'pdf'
+
+        params.output.periods_save.set_attrib(tag, 0)
+        params.output.set_child(tag,
+                                attribs={'HAS_TO_PLOT_SAVED': False})
+
+    def __init__(self, output):
+        params = output.sim.params
+        self.c2 = params.c2
+        self.f = params.f
+        self.nx = params.oper.nx
+
+        super(ProbaDensityFunc, self).__init__(
+            output,
+            name_file='pdf.h5',
+            period_save=params.output.periods_save.pdf,
+            has_to_plot_saved=params.output.pdf.HAS_TO_PLOT_SAVED)
+
+    def init_online_plot(self):
+        self.fig, axe = self.output.figure_axe(numfig=5000000)
+        self.axe = axe
+        axe.set_xlabel('$\eta$')
+        axe.set_ylabel('pdf')
+        title = ('pdf $\eta$, solver ' + self.output.name_solver +
+                 ', nh = {0:5d}'.format(self.nx) +
+                 ', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f))
+        axe.set_title(title)
+        axe.hold(True)
+
+    def _online_plot(self, dico_pdf):
+        """online plot on pdf"""
+        pdf_eta = dico_pdf['pdf_eta']
+        bin_edges_eta = dico_pdf['bin_edges_eta']
+        self.axe.plot(bin_edges_eta[:-1], pdf_eta, 'k')
+
+    def compute(self):
+        """compute the values at one time."""
+        eta = self.sim.state.state_phys['eta']
+        pdf_eta, bin_edges_eta = self.oper.pdf_normalized(eta)
+
+        ux = self.sim.state.state_phys['ux']
+        uy = self.sim.state.state_phys['uy']
+
+        uxm = ux.mean()
+        uym = uy.mean()
+
+        u_norme = np.sqrt((ux - uxm)**2 + (uy - uym)**2)
+        pdf_u, bin_edges_u = self.oper.pdf_normalized(u_norme)
+
+        dico_pdf = {'pdf_eta': pdf_eta,
+                    'bin_edges_eta': bin_edges_eta,
+                    'pdf_u': pdf_u,
+                    'bin_edges_u': bin_edges_u}
+        return dico_pdf
+
+    def load(self):
+        """load the saved pdf and return a dictionary."""
+        f = h5py.File(self.path_file, 'r')
+        # dset_times = f['times']
+        # times = dset_times[...]
+        # nt = len(times)
+
+        dset_pdf_eta = f['pdf_eta']
+        dset_bin_edges_eta = f['bin_edges_eta']
+
+        pdf_eta = dset_pdf_eta[...]
+        bin_edges_eta = dset_bin_edges_eta[...]
+
+        dset_pdf_u = f['pdf_u']
+        dset_bin_edges_u = f['bin_edges_u']
+
+        pdf_u = dset_pdf_u[...]
+        bin_edges_u = dset_bin_edges_u[...]
+
+        dico_pdf = {'pdf_eta': pdf_eta,
+                    'bin_edges_eta': bin_edges_eta,
+                    'pdf_u': pdf_u,
+                    'bin_edges_u': bin_edges_u}
+        return dico_pdf
+
+    def plot(self, tmin=0, tmax=1000, delta_t=2):
+        """Plot some pdf."""
+        f = h5py.File(self.path_file, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+        # nt = len(times)
+
+        dset_pdf_eta = f['pdf_eta']
+        dset_bin_edges_eta = f['bin_edges_eta']
+        dset_pdf_u = f['pdf_u']
+        dset_bin_edges_u = f['bin_edges_u']
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0:
+            delta_i_plot = 1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        to_print = 'plot(tmin={0}, tmax={1}, delta_t={2:.2f})'.format(
+            tmin, tmax, delta_t)
+        print(to_print)
+
+        to_print = ('plot pdf eta\n'
+                    'tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}'
+                    'imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}').format(
+                        tmin_plot, tmax_plot, delta_t,
+                        imin_plot, imax_plot, delta_i_plot)
+        print(to_print)
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.56
+        width_axe = 0.85
+        height_axe = 0.37
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('$\eta$')
+        ax1.set_ylabel('PDF')
+        ax1.set_title('PDF, solver ' + self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx) +
+                      ', c = {0:.4g}, f = {1:.4g}'.format(
+                          np.sqrt(self.c2), self.f))
+        ax1.hold(True)
+        ax1.set_xscale('linear')
+        ax1.set_yscale('linear')
+
+        for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+            pdf_eta = dset_pdf_eta[it]
+            bin_edges_eta = dset_bin_edges_eta[it]
+
+            bin_edges_eta = (bin_edges_eta[:-1]+bin_edges_eta[1:])/2
+            ax1.plot(bin_edges_eta, pdf_eta, 'c', linewidth=1)
+
+        z_bottom_axe = 0.09
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+
+        ax2.set_xlabel('$ |\\mathbf{u}-\\langle \\mathbf{u} \\rangle | $')
+        ax2.set_ylabel('PDF')
+        ax2.hold(True)
+        ax2.set_xscale('linear')
+        ax2.set_yscale('linear')
+
+        for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+            pdf_u = dset_pdf_u[it]
+            bin_edges_u = dset_bin_edges_u[it]
+
+            bin_edges_u = (bin_edges_u[:-1]+bin_edges_u[1:])/2
+            ax2.plot(bin_edges_u, pdf_u, 'r', linewidth=1)
diff --git a/fluidsim/base/output/spatial_means.py b/fluidsim/base/output/spatial_means.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvc3BhdGlhbF9tZWFucy5weQ==
--- /dev/null
+++ b/fluidsim/base/output/spatial_means.py
@@ -0,0 +1,157 @@
+
+from __future__ import division, print_function
+
+import os
+import numpy as np
+
+
+from fluiddyn.util import mpi
+
+from .base import SpecificOutput
+
+
+def inner_prod(a_fft, b_fft):
+    return np.real(a_fft.conj()*b_fft)
+
+
+class SpatialMeansBase(SpecificOutput):
+    """A :class:`SpatialMean` object handles the saving of .
+
+    This class uses the particular functions defined by some solvers
+    :func:`` and
+    :func``. If the solver doesn't has these
+    functions, this class does nothing.
+    """
+
+    _tag = 'spatial_means'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        tag = 'spatial_means'
+
+        params.output.periods_save.set_attrib(tag, 0)
+        params.output.set_child(tag,
+                                attribs={'HAS_TO_PLOT_SAVED': False})
+
+    def __init__(self, output):
+        params = output.sim.params
+        self.nx = params.oper.nx
+
+        self.sum_wavenumbers = output.sum_wavenumbers
+        self.vecfft_from_rotfft = output.vecfft_from_rotfft
+
+        super(SpatialMeansBase, self).__init__(
+            output,
+            period_save=params.output.periods_save.spatial_means,
+            has_to_plot_saved=params.output.spatial_means.HAS_TO_PLOT_SAVED)
+
+        if self.period_save != 0:
+            self.save_one_time()
+
+    def init_path_files(self):
+        self.path_file = self.output.path_run + '/spatial_means.txt'
+
+    def init_files(self, dico_arrays_1time=None):
+
+        if mpi.rank == 0:
+            if not os.path.exists(self.path_file):
+                self.file = open(self.path_file, 'w')
+            else:
+                self.file = open(self.path_file, 'r+')
+                # to go to the end of the file
+                self.file.seek(0, 2)
+
+    def online_save(self):
+        self()
+
+    def __call__(self):
+        """Save the values at one time. """
+        if (self.sim.time_stepping.t-self.t_last_save >= self.period_save):
+            self.t_last_save = self.sim.time_stepping.t
+            self.save_one_time()
+
+    def save_one_time(self):
+        self.t_last_save = self.sim.time_stepping.t
+
+    def init_online_plot(self):
+        if mpi.rank == 0:
+            width_axe = 0.85
+            height_axe = 0.4
+            x_left_axe = 0.12
+            z_bottom_axe = 0.55
+
+            size_axe = [x_left_axe, z_bottom_axe,
+                        width_axe, height_axe]
+            fig, axe = self.output.figure_axe(size_axe=size_axe,
+                                              numfig=3000000)
+            self.axe_a = axe
+            axe.set_xlabel('$t$')
+            axe.set_ylabel('$E(t)$')
+            title = ('mean quantities, solver ' + self.output.name_solver +
+                     ', nh = {0:5d}'.format(self.nx))
+            axe.set_title(title)
+            axe.hold(True)
+
+            z_bottom_axe = 0.08
+            size_axe[1] = z_bottom_axe
+            axe = fig.add_axes(size_axe)
+            self.axe_b = axe
+            axe.set_xlabel('$t$')
+            axe.set_ylabel('$\epsilon(t)$')
+            axe.hold(True)
+
+    def load(self):
+        dico_results = {}
+        return dico_results
+
+    def plot(self):
+        pass
+
+    def compute_time_means(self, tstatio=0., tmax=None):
+        """compute the temporal means."""
+        dico_results = self.load()
+        if tmax is None:
+            times = dico_results['t']
+            imax_mean = times.size-1
+            tmax = times[imax_mean]
+        else:
+            imax_mean = np.argmin(abs(times-tmax))
+        imin_mean = np.argmin(abs(times-tstatio))
+
+        dico_time_means = {}
+        for key, value in dico_results.iteritems():
+            if isinstance(value, np.ndarray):
+                dico_time_means[key] = np.mean(
+                    value[imin_mean:imax_mean+1]
+                    )
+        return dico_time_means, dico_results
+
+    def close_file(self):
+        try:
+            self.file.close()
+        except AttributeError:
+            pass
+
+    def time_first_saved(self):
+        file_means = open(self.path_file)
+        line = ''
+        while not line.startswith('time ='):
+            line = file_means.readline()
+        file_means.close()
+        words = line.split()
+        return float(words[2])
+
+    def time_last_saved(self):
+        file_means = open(self.path_file)
+        file_means.seek(0, 2)  # go to the end
+        nb_caract = file_means.tell()
+        nb_caract_to_read = min(nb_caract, 1000)
+        file_means.seek(-nb_caract_to_read, 2)
+        line = file_means.readline()
+        while line != '':
+            if line.startswith('time ='):
+                line_time = line
+            line = file_means.readline()
+        file_means.close()
+        words = line_time.split()
+        return float(words[2])
diff --git a/fluidsim/base/output/spect_energy_budget.py b/fluidsim/base/output/spect_energy_budget.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvc3BlY3RfZW5lcmd5X2J1ZGdldC5weQ==
--- /dev/null
+++ b/fluidsim/base/output/spect_energy_budget.py
@@ -0,0 +1,113 @@
+
+import numpy as np
+
+from fluiddyn.util import mpi
+
+from .base import SpecificOutput
+
+
+def cumsum_inv(a):
+    return a[::-1].cumsum()[::-1]
+
+
+def inner_prod(a_fft, b_fft):
+    return np.real(a_fft.conj()*b_fft)
+
+
+class SpectralEnergyBudgetBase(SpecificOutput):
+    """A :class:`Spectra` object handles the saving of .
+
+    This class uses the particular functions defined by some solvers
+    :func:`` and
+    :func``. If the solver doesn't has these
+    functions, this class does nothing.
+    """
+
+    _tag = 'spect_energy_budg'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        tag = 'spect_energy_budg'
+
+        params.output.periods_save.set_attrib(tag, 0)
+        params.output.set_child(tag,
+                                attribs={'HAS_TO_PLOT_SAVED': False})
+
+    def __init__(self, output):
+
+        params = output.sim.params
+        self.nx = params.oper.nx
+
+        self.spectrum2D_from_fft = output.sim.oper.spectrum2D_from_fft
+
+        HAS_TO_PLOT_SAVED = params.output.spect_energy_budg.HAS_TO_PLOT_SAVED
+        super(SpectralEnergyBudgetBase, self).__init__(
+            output,
+            name_file='spectral_energy_budget.h5',
+            period_save=params.output.periods_save.spect_energy_budg,
+            has_to_plot_saved=HAS_TO_PLOT_SAVED,
+            dico_arrays_1time={'khE': output.sim.oper.khE})
+
+    def compute(self):
+        """compute the values at one time."""
+        if mpi.rank == 0:
+            dico_results = {}
+            return dico_results
+
+    def init_online_plot(self):
+        width_axe = 0.85
+        height_axe = 0.37
+        x_left_axe = 0.12
+        z_bottom_axe = 0.56
+
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        self.fig, axe_a = self.output.figure_axe(size_axe=size_axe,
+                                                 numfig=4000000)
+        self.axe_a = axe_a
+        axe_a.set_xlabel('k_h')
+        axe_a.set_ylabel('Pi(k_h) energy')
+        axe_a.set_title('energy flux, solver ' + self.output.name_solver +
+                        ', nh = {0:5d}'.format(self.nx))
+        axe_a.hold(True)
+        axe_a.set_xscale('log')
+
+        z_bottom_axe = 0.08
+        size_axe[1] = z_bottom_axe
+        axe_b = self.fig.add_axes(size_axe)
+        self.axe_b = axe_b
+        axe_b.set_xlabel('k_h')
+        axe_b.set_ylabel('Pi(k_h) energy')
+        axe_b.hold(True)
+        axe_b.set_xscale('log')
+
+    def fnonlinfft_from_uxuy_funcfft(self, ux, uy, f_fft):
+        """Compute a non-linear term."""
+        oper = self.oper
+        px_f_fft, py_f_fft = oper.gradfft_from_fft(f_fft)
+        px_f = oper.ifft2(px_f_fft)
+        py_f = oper.ifft2(py_f_fft)
+        del(px_f_fft, py_f_fft)
+        Fnl = -ux*px_f - uy*py_f
+        del(px_f, py_f)
+        Fnl_fft = oper.fft2(Fnl)
+        oper.dealiasing(Fnl_fft)
+        return Fnl_fft
+
+    def fnonlinfft_from_uruddivfunc(self,
+                                    urx, ury,
+                                    udx, udy, div,
+                                    func_fft, func):
+        """Compute a non-linear term."""
+        oper = self.oper
+        px_func_fft, py_func_fft = oper.gradfft_from_fft(func_fft)
+        px_func = oper.ifft2(px_func_fft)
+        py_func = oper.ifft2(py_func_fft)
+        del(px_func_fft, py_func_fft)
+        Frf = -urx*px_func - ury*py_func
+        Fdf = -udx*px_func - udy*py_func - div*func/2
+        del(px_func, py_func)
+        Frf_fft = oper.fft2(Frf)
+        Fdf_fft = oper.fft2(Fdf)
+        oper.dealiasing(Frf_fft, Fdf_fft)
+        return Frf_fft, Fdf_fft
diff --git a/fluidsim/base/output/spectra.py b/fluidsim/base/output/spectra.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvc3BlY3RyYS5weQ==
--- /dev/null
+++ b/fluidsim/base/output/spectra.py
@@ -0,0 +1,183 @@
+import h5py
+
+import os
+import numpy as np
+
+from fluiddyn.util import mpi
+
+
+from .base import SpecificOutput
+
+
+class Spectra(SpecificOutput):
+    """Used for the saving of spectra.
+
+
+    """
+
+    _tag = 'spectra'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        tag = 'spectra'
+
+        params.output.periods_save.set_attrib(tag, 0)
+        params.output.set_child(tag,
+                                attribs={'HAS_TO_PLOT_SAVED': False})
+
+    def __init__(self, output):
+        params = output.sim.params
+        self.nx = params.oper.nx
+
+        self.spectrum2D_from_fft = output.sim.oper.spectrum2D_from_fft
+        self.spectra1D_from_fft = output.sim.oper.spectra1D_from_fft
+
+        super(Spectra, self).__init__(
+            output,
+            period_save=params.output.periods_save.spectra,
+            has_to_plot_saved=params.output.spectra.HAS_TO_PLOT_SAVED)
+
+    def init_path_files(self):
+        path_run = self.output.path_run
+        self.path_file1D = path_run + '/spectra1D.h5'
+        self.path_file2D = path_run + '/spectra2D.h5'
+
+    def init_files(self, dico_arrays_1time=None):
+        dico_spectra1D, dico_spectra2D = self.compute()
+        if mpi.rank == 0:
+            if not os.path.exists(self.path_file1D):
+                dico_arrays_1time = {'kxE': self.sim.oper.kxE,
+                                     'kyE': self.sim.oper.kyE}
+                self.create_file_from_dico_arrays(
+                    self.path_file1D, dico_spectra1D, dico_arrays_1time)
+                dico_arrays_1time = {'khE': self.sim.oper.khE}
+                self.create_file_from_dico_arrays(
+                    self.path_file2D, dico_spectra2D, dico_arrays_1time)
+                self.nb_saved_times = 1
+            else:
+                with h5py.File(self.path_file1D, 'r') as f:
+                    dset_times = f['times']
+                    self.nb_saved_times = dset_times.shape[0]+1
+                # save the spectra in the file spectra1D.h5
+                self.add_dico_arrays_to_file(self.path_file1D,
+                                             dico_spectra1D)
+                # save the spectra in the file spectra2D.h5
+                self.add_dico_arrays_to_file(self.path_file2D,
+                                             dico_spectra2D)
+
+        self.t_last_save = self.sim.time_stepping.t
+
+    def online_save(self):
+        """Save the values at one time. """
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_save >= self.period_save):
+            self.t_last_save = tsim
+            dico_spectra1D, dico_spectra2D = self.compute()
+            if mpi.rank == 0:
+                # save the spectra in the file spectra1D.h5
+                self.add_dico_arrays_to_file(self.path_file1D,
+                                             dico_spectra1D)
+                # save the spectra in the file spectra2D.h5
+                self.add_dico_arrays_to_file(self.path_file2D,
+                                             dico_spectra2D)
+                self.nb_saved_times += 1
+                if self.has_to_plot:
+                    self._online_plot(dico_spectra1D, dico_spectra2D)
+
+                    if (tsim-self.t_last_show >= self.period_show):
+                        self.t_last_show = tsim
+                        self.axe.get_figure().canvas.draw()
+
+    def compute(self):
+        """compute the values at one time."""
+        if mpi.rank == 0:
+            dico_results = {}
+            return dico_results
+
+    def init_online_plot(self):
+        fig, axe = self.output.figure_axe(numfig=1000000)
+        self.axe = axe
+        axe.set_xlabel('$k_h$')
+        axe.set_ylabel('$E(k_h)$')
+        axe.set_title('spectra, solver '+self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx))
+        axe.hold(True)
+
+    def _online_plot(self):
+        pass
+
+    def load2D_mean(self, tmin=None, tmax=None):
+        f = h5py.File(self.path_file2D, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+        nt = len(times)
+
+        kh = f['khE'][...]
+
+        if tmin is None:
+            imin_plot = 0
+        else:
+            imin_plot = np.argmin(abs(times-tmin))
+
+        if tmax is None:
+            imax_plot = nt-1
+        else:
+            imax_plot = np.argmin(abs(times-tmax))
+
+        tmin = times[imin_plot]
+        tmax = times[imax_plot]
+
+        print('compute mean of 2D spectra\n'
+              ('tmin = {0:8.6g} ; tmax = {1:8.6g}'
+               'imin = {2:8d} ; imax = {3:8d}').format(
+                  tmin, tmax, imin_plot, imax_plot))
+
+        dico_results = {'kh': kh}
+        for key in f.keys():
+            if key.startswith('spectr'):
+                dset_key = f[key]
+                spect = dset_key[imin_plot:imax_plot+1].mean(0)
+                dico_results[key] = spect
+        return dico_results
+
+    def load1D_mean(self, tmin=None, tmax=None):
+        f = h5py.File(self.path_file1D, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+        nt = len(times)
+
+        kx = f['kxE'][...]
+        # ky = f['kyE'][...]
+        kh = kx
+
+        if tmin is None:
+            imin_plot = 0
+        else:
+            imin_plot = np.argmin(abs(times-tmin))
+
+        if tmax is None:
+            imax_plot = nt-1
+        else:
+            imax_plot = np.argmin(abs(times-tmax))
+
+        tmin = times[imin_plot]
+        tmax = times[imax_plot]
+
+        print('compute mean of 1D spectra'
+              ('tmin = {0:8.6g} ; tmax = {1:8.6g}\n'
+               'imin = {2:8d} ; imax = {3:8d}\n').format(
+                   tmin, tmax, imin_plot, imax_plot))
+
+        dico_results = {'kh': kh}
+        for key in f.keys():
+            if key.startswith('spectr'):
+                dset_key = f[key]
+                spect = dset_key[imin_plot:imax_plot+1].mean(0)
+                dico_results[key] = spect
+        return dico_results
+
+    def plot1D(self):
+        pass
+
+    def plot2D(self):
+        pass
diff --git a/fluidsim/base/output/time_signalsK.py b/fluidsim/base/output/time_signalsK.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9vdXRwdXQvdGltZV9zaWduYWxzSy5weQ==
--- /dev/null
+++ b/fluidsim/base/output/time_signalsK.py
@@ -0,0 +1,474 @@
+import h5py
+
+import os
+import numpy as np
+
+from fluiddyn.util import mpi
+
+from fluidsim.base.output.base import SpecificOutput
+
+from fluidsim.operators.fft import easypyfft
+
+
+class TimeSignalsK(SpecificOutput):
+    """A :class:`TimeSignalK` object handles the saving of time signals
+    in spectral space.
+
+    This class uses the particular functions defined by some solvers
+    :func:`linear_eigenmode_from_values_1k` and
+    :func`omega_from_wavenumber`.
+    """
+
+    _tag = 'time_signals_fft'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        tag = 'time_signals_fft'
+
+        params.output.periods_save.set_attrib(tag, 0)
+        params.output.periods_plot.set_attrib(tag, 0)
+
+        params.output.set_child(tag,
+                                attribs={'nb_shells_time_sigK': 4,
+                                         'nb_k_per_shell_time_sigK': 4})
+
+    def __init__(self, output):
+        self.output = output
+        sim = output.sim
+        params = sim.params
+
+        self.params = params
+        self.c2 = params.c2
+        self.f = params.f
+        self.nx = params.oper.nx
+
+        if not params.output.HAS_TO_SAVE:
+            params.output.periods_save.time_signals_fft = False
+
+        if params.output.periods_save.time_signals_fft:
+            self._init_save(sim)
+
+        super(TimeSignalsK, self).__init__(
+            output,
+            period_save=params.output.periods_save.time_signals_fft,
+            period_plot=params.output.periods_plot.time_signals_fft)
+
+    def _init_save(self, sim):
+        params = self.params
+        self.nb_shells = params.output.time_signals_fft.nb_shells_time_sigK
+        self.nb_k_per_shell = \
+            params.output.time_signals_fft.nb_k_per_shell_time_sigK
+        self.nb_k_tot = self.nb_shells*self.nb_k_per_shell
+
+        i_shift = 3
+        deltalogk = np.log(
+            params.oper.nx / 2 *
+            params.oper.coef_dealiasing)/(self.nb_shells+i_shift)
+
+        deltakh = sim.oper.deltakh
+
+        self.kh_shell = deltakh*np.exp(
+            deltalogk*np.arange(i_shift, self.nb_shells+i_shift))
+
+        self.kh_shell = deltakh*np.round(self.kh_shell/deltakh)
+
+        for i_s in xrange(1, self.nb_shells):
+            if self.kh_shell[i_s-1] == self.kh_shell[i_s]:
+                self.kh_shell[i_s] += deltakh
+
+        # if mpi.rank == 0:
+        #     print 'self.kh_shell/deltakh'
+        #     print self.kh_shell/sim.oper.deltakh
+
+        # hypothese dispersion relation only function of the module
+        # of the wavenumber ("shells")
+        self.omega_shell = self.output.omega_from_wavenumber(self.kh_shell)
+
+        kx_array_ik_approx = np.empty([self.nb_k_tot])
+        ky_array_ik_approx = np.empty([self.nb_k_tot])
+
+        delta_angle = np.pi/(self.nb_k_per_shell-1)
+        for ishell, kh_s in enumerate(self.kh_shell):
+            angle = -np.pi/2
+            for ikps in xrange(self.nb_k_per_shell):
+                kx_array_ik_approx[ishell*self.nb_shells+ikps] = \
+                    kh_s*np.cos(angle)
+                ky_array_ik_approx[ishell*self.nb_shells+ikps] = \
+                    kh_s*np.sin(angle)
+                angle += delta_angle
+
+        self.ik0_array_ik = np.empty([self.nb_k_tot], dtype=np.int32)
+        self.ik1_array_ik = np.empty([self.nb_k_tot], dtype=np.int32)
+        if mpi.nb_proc > 1:
+            self.rank_array_ik = np.empty([self.nb_k_tot], dtype=np.int32)
+
+        for ik in xrange(self.nb_k_tot):
+            kx_approx = kx_array_ik_approx[ik]
+            ky_approx = ky_array_ik_approx[ik]
+            rank_ik, ik0, ik1 = \
+                sim.oper.where_is_wavenumber(kx_approx, ky_approx)
+            if mpi.nb_proc > 1:
+                self.rank_array_ik[ik] = rank_ik
+            self.ik0_array_ik[ik] = ik0
+            self.ik1_array_ik[ik] = ik1
+
+        self.kx_array_ik = np.empty([self.nb_k_tot])
+        self.ky_array_ik = np.empty([self.nb_k_tot])
+
+        for ik in xrange(self.nb_k_tot):
+            ik0_ik = self.ik0_array_ik[ik]
+            ik1_ik = self.ik1_array_ik[ik]
+
+            if mpi.nb_proc > 1:
+                rank_ik = self.rank_array_ik[ik]
+            else:
+                rank_ik = 0
+
+            if mpi.rank == rank_ik:
+                kx_1k = sim.oper.KX[ik0_ik, ik1_ik]
+                ky_1k = sim.oper.KY[ik0_ik, ik1_ik]
+
+            if rank_ik != 0:
+                if mpi.rank == rank_ik:
+                    data = np.array([kx_1k, ky_1k])
+                    mpi.comm.Send(
+                        [data, mpi.MPI.DOUBLE],
+                        dest=0, tag=ik)
+                elif mpi.rank == 0:
+                    data = np.empty([2], np.float64)
+                    mpi.comm.Recv(
+                        [data, mpi.MPI.DOUBLE],
+                        source=rank_ik, tag=ik)
+                    kx_1k = data[0]
+                    ky_1k = data[1]
+
+            if mpi.rank == 0:
+                self.kx_array_ik[ik] = kx_1k
+                self.ky_array_ik[ik] = ky_1k
+
+        if mpi.rank == 0:
+            self.kh_array_ik = np.sqrt(self.kx_array_ik**2 +
+                                       self.ky_array_ik**2)
+
+            self.omega_array_ik = self.output.omega_from_wavenumber(
+                self.kh_array_ik)
+
+            self.period_save = np.pi/(8*self.omega_array_ik.max())
+        else:
+            self.period_save = 0.
+
+        if mpi.nb_proc > 1:
+            self.period_save = mpi.comm.bcast(self.period_save)
+
+    def init_path_files(self):
+        self.path_file = self.output.path_run + '/time_sigK.h5'
+
+    def init_files(self, dico_arrays_1time=None):
+        if (not os.path.exists(self.path_file)):
+            dico_results = self.compute()
+            if mpi.rank == 0:
+                dico_arrays_1time = {
+                    'kh_shell': self.kh_shell,
+                    'omega_shell': self.omega_shell,
+                    'kx_array_ik': self.kx_array_ik,
+                    'ky_array_ik': self.ky_array_ik,
+                    'kh_array_ik': self.kh_array_ik,
+                    'omega_array_ik': self.omega_array_ik}
+                self.create_file_from_dico_arrays(
+                    self.path_file, dico_results, dico_arrays_1time)
+
+        if mpi.rank == 0:
+            self.file = h5py.File(self.path_file, 'r+')
+            self.file.attrs['nb_shells'] = self.nb_shells
+            self.file.attrs['nb_k_per_shell'] = self.nb_k_per_shell
+            self.file.attrs['nb_k_tot'] = self.nb_k_tot
+            # the file is kept open during all the simulation
+            self.nb_saved_times = 1
+
+        self.t_last_save = self.sim.time_stepping.t
+
+    def online_save(self):
+        """Save the values at one time. """
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_save >= self.period_save):
+            self.t_last_save = tsim
+            dico_results = self.compute()
+            if mpi.rank == 0:
+                self.add_dico_arrays_to_open_file(self.file,
+                                                  dico_results,
+                                                  self.nb_saved_times)
+                self.nb_saved_times += 1
+
+    def compute(self):
+        """compute the values at one time."""
+
+        ux_fft = self.sim.state('ux_fft')
+        uy_fft = self.sim.state('uy_fft')
+        eta_fft = self.sim.state('eta_fft')
+
+        if mpi.rank == 0:
+            q_array_ik = np.empty([self.nb_k_tot], dtype=np.complex128)
+            d_array_ik = np.empty([self.nb_k_tot], dtype=np.complex128)
+            a_array_ik = np.empty([self.nb_k_tot], dtype=np.complex128)
+
+        for ik in xrange(self.nb_k_tot):
+            ik0_ik = self.ik0_array_ik[ik]
+            ik1_ik = self.ik1_array_ik[ik]
+
+            if mpi.rank == 0:
+                kx_ik = self.kx_array_ik[ik]
+                ky_ik = self.ky_array_ik[ik]
+
+            if mpi.nb_proc > 1:
+                rank_ik = self.rank_array_ik[ik]
+            else:
+                rank_ik = 0
+
+            if mpi.rank == rank_ik:
+                ux_1k = ux_fft[ik0_ik, ik1_ik]
+                uy_1k = uy_fft[ik0_ik, ik1_ik]
+                eta_1k = eta_fft[ik0_ik, ik1_ik]
+
+            if rank_ik != 0:
+                if mpi.rank == rank_ik:
+                    data = np.array([ux_1k, uy_1k, eta_1k])
+                    mpi.comm.Send(
+                        [data, mpi.MPI.COMPLEX],
+                        dest=0, tag=ik)
+                elif mpi.rank == 0:
+                    data = np.empty([3], np.complex128)
+                    mpi.comm.Recv(
+                        [data, mpi.MPI.COMPLEX],
+                        source=rank_ik, tag=ik)
+                    ux_1k = data[0]
+                    uy_1k = data[1]
+                    eta_1k = data[2]
+
+            if mpi.rank == 0:
+                q_1k, d_1k, a_1k = (
+                    self.output.linear_eigenmode_from_values_1k(
+                        ux_1k, uy_1k, eta_1k, kx_ik, ky_ik))
+                q_array_ik[ik] = q_1k
+                d_array_ik[ik] = d_1k
+                a_array_ik[ik] = a_1k
+
+        if mpi.rank == 0:
+            dico_results = {'q_array_ik': q_array_ik,
+                            'd_array_ik': d_array_ik,
+                            'a_array_ik': a_array_ik}
+            return dico_results
+
+    def load(self):
+
+        if (not os.path.exists(self.path_file)):
+            raise ValueError(
+                'no file time_sigK.h5 in\n'+self.output.dir_save_run)
+
+        with h5py.File(self.path_file, 'r+') as f:
+
+            dset_times = f['times']
+            times = dset_times[...]
+
+            dico_results = {}
+            dico_results['times'] = times
+
+            dico_results['nb_shells'] = f.attrs['nb_shells']
+            dico_results['nb_k_per_shell'] = f.attrs['nb_k_per_shell']
+            dico_results['nb_k_tot'] = f.attrs['nb_k_tot']
+
+            keys_1time = [
+                'kh_shell',
+                'omega_shell',
+                'kx_array_ik',
+                'ky_array_ik',
+                'kh_array_ik',
+                'omega_array_ik']
+
+            for key in keys_1time:
+                dset_temp = f[key]
+                dico_results[key] = dset_temp[...]
+
+            keys_linear_eigenmodes = \
+                self.sim.info.solver.classes.State.keys_linear_eigenmodes
+
+            for key in keys_linear_eigenmodes:
+                dset_temp = f[key[:-3]+'array_ik']
+                A = dset_temp[...]
+                dico_results['sig_'+key] = np.ascontiguousarray(A.transpose())
+        return dico_results
+
+    def plot(self):
+        dico_results = self.load()
+
+        t = dico_results['times']
+
+        nb_shells = dico_results['nb_shells']
+        nb_k_per_shell = dico_results['nb_k_per_shell']
+
+        sig_q_fft = dico_results['sig_q_fft']
+        sig_a_fft = dico_results['sig_a_fft']
+        sig_d_fft = dico_results['sig_d_fft']
+
+        kh_shell = dico_results['kh_shell']
+        omega_shell = dico_results['omega_shell']
+        period_shell = 2*np.pi/omega_shell
+
+        for ish in xrange(nb_shells):
+
+            fig, ax1 = self.output.figure_axe()
+            ax1.set_xlabel('$t/T$')
+            ax1.set_ylabel('signals (s$^{-1}$)')
+            title = (
+                'signals eigenmodes, ikh = {0:.2f}, solver '.format(
+                    kh_shell[ish]/sim.oper.deltakh) +
+                self.output.name_solver +
+                ', nh = {0:5d}'.format(self.nx) +
+                ', c2 = {0:.4g}, f = {1:.4g}'.format(self.c2, self.f)
+                )
+            ax1.set_title(title)
+            ax1.hold(True)
+
+            coef_norm_a = self.c2/omega_shell[ish]
+
+            T = period_shell[ish]
+
+            for ikps in xrange(nb_k_per_shell):
+                isig = ish*nb_k_per_shell+ikps
+
+                ax1.plot(t/T, sig_q_fft[isig].real, 'k', linewidth=1)
+                ax1.plot(t/T, coef_norm_a*sig_a_fft[isig].real,
+                         'c', linewidth=1)
+                ax1.plot(t/T, sig_d_fft[isig].real, 'y', linewidth=1)
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('$\omega$')
+        ax1.set_ylabel('kh_shell')
+        ax1.loglog(kh_shell, omega_shell, 'o', linewidth=2)
+
+    def time_spectrum(self, sig_long):
+
+        Nt = sig_long.size
+        stepit0 = int(np.fix(self.nt/2))
+
+        nb_spectra = 0
+        it0 = 0
+        spect = np.zeros([self.nt/2+1])
+        while it0+self.nt < Nt:
+            nb_spectra += 1
+            sig = sig_long[it0:it0+self.nt]
+            spect_raw = (
+                abs(self.opfft1d.fft(self.hann*sig))**2 / 2 / self.deltaomega)
+            spect += spect_raw[:self.nt/2+1]
+            if self.nt % 2 == 0:
+                spect[1:self.nt/2] += spect_raw[self.nt-1:self.nt/2:-1]
+            else:
+                spect[1:self.nt/2+1] += spect_raw[self.nt-1:self.nt/2:-1]
+            it0 += stepit0
+
+        return spect/nb_spectra
+
+    def compute_spectra(self):
+        dico_results = self.load()
+
+        t = dico_results['times']
+        Nt = t.size
+        nt = 2**int(np.fix(np.log2(Nt/10)))
+        # if nt%2 == 1:
+        #     nt -= 1
+        self.nt = nt
+
+        if not hasattr(self, 'opfft1d'):
+            self.opfft1d = easypyfft.FFTW1D(nt)
+
+        T = t[nt-1] - t[0]
+        # deltat = T/nt
+        self.deltaomega = 2*np.pi/T
+        # self.omega = self.deltaomega*np.concatenate(
+        #     (np.arange(nt/2+1), np.arange(-nt/2+1, 0)))
+
+        self.omega = self.deltaomega*np.arange(nt/2+1)
+
+        self.hann = np.hanning(nt)
+
+        nb_shells = dico_results['nb_shells']
+        nb_k_per_shell = dico_results['nb_k_per_shell']
+        # nb_k_tot = dico_results['nb_k_tot']
+
+        sig_q_fft = dico_results['sig_q_fft']
+        sig_a_fft = dico_results['sig_a_fft']
+        sig_d_fft = dico_results['sig_d_fft']
+
+        # kh_shell = dico_results['kh_shell']
+        omega_shell = dico_results['omega_shell']
+        # period_shell = 2*np.pi/omega_shell
+
+        time_spectra_q = np.zeros([nb_shells, nt/2+1])
+        time_spectra_a = np.zeros([nb_shells, nt/2+1])
+        time_spectra_d = np.zeros([nb_shells, nt/2+1])
+
+        for ish in xrange(nb_shells):
+            coef_norm_a = self.c2/omega_shell[ish]
+            for ikps in xrange(nb_k_per_shell):
+                isig = ish*nb_k_per_shell+ikps
+                sig_a_fft[isig] *= coef_norm_a
+                time_spectra_q[ish] += self.time_spectrum(sig_q_fft[isig])
+                time_spectra_a[ish] += self.time_spectrum(sig_a_fft[isig])
+                time_spectra_d[ish] += self.time_spectrum(sig_d_fft[isig])
+
+        time_spectra_q /= nb_k_per_shell
+        time_spectra_a /= nb_k_per_shell
+        time_spectra_d /= nb_k_per_shell
+
+        dico_spectra = {
+            'omega': self.omega,
+            'time_spectra_q': time_spectra_q,
+            'time_spectra_a': time_spectra_a,
+            'time_spectra_d': time_spectra_d}
+        return dico_spectra, dico_results
+
+    def plot_spectra(self):
+        dico_spectra, dico_results = self.compute_spectra()
+
+        omega = dico_spectra['omega']
+        time_spectra_q = dico_spectra['time_spectra_q']
+        time_spectra_a = dico_spectra['time_spectra_a']
+        time_spectra_d = dico_spectra['time_spectra_d']
+        omega_shell = dico_results['omega_shell']
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('r$\omega/\omega_{lin}$')
+        ax1.set_ylabel('r$E(\omega)$)')
+        title = (
+            'time spectra, solver ' +
+            self.output.name_solver +
+            ', nh = {0:5d}'.format(self.nx) +
+            ', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f)
+            )
+        ax1.set_title(title)
+        ax1.hold(True)
+
+        nb_shells = dico_results['nb_shells']
+        for ish in xrange(nb_shells):
+            ax1.loglog(omega/omega_shell[ish],
+                       time_spectra_q[ish], 'k', linewidth=1)
+            ax1.loglog(omega/omega_shell[ish],
+                       time_spectra_a[ish], 'b', linewidth=1)
+            ax1.loglog(omega/omega_shell[ish],
+                       time_spectra_d[ish], 'r', linewidth=1)
+
+    def close_file(self):
+        try:
+            self.file.close()
+        except AttributeError:
+            pass
+
+
+if __name__ == '__main__':
+    path_dir = '/scratch/augier/Results_for_article_SW1l/Waves_standing_256x256/SE2D_SW1lexlin_forcing_L=50.x50._256x256_c2=900_f=0_2013-06-04_12-49-26'
+
+    from solveq2d import solveq2d
+
+    sim = solveq2d.create_sim_plot_from_dir(path_dir)
+
+    sim.output.time_sigK.plot_spectra()
+    solveq2d.show()
diff --git a/fluidsim/base/params.py b/fluidsim/base/params.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9wYXJhbXMucHk=
--- /dev/null
+++ b/fluidsim/base/params.py
@@ -0,0 +1,97 @@
+"""Information on a solver (:mod:`fluidsim.base.params`)
+==============================================================
+
+.. currentmodule:: fluidsim.base.params
+
+Provides:
+
+.. autoclass:: Parameters
+   :members:
+   :private-members:
+
+
+"""
+
+from __future__ import division, print_function
+
+import os
+
+from fluiddyn.util.containerxml import ContainerXML
+from fluiddyn.util.util import import_class
+
+
+class Parameters(ContainerXML):
+    """Contain the parameters."""
+    pass
+
+from fluidsim.base.solvers.info_base import InfoSolverBase
+
+
+def create_params(input_info_solver):
+    """Create a Parameters instance from an InfoSolverBase instance."""
+    if isinstance(input_info_solver, InfoSolverBase):
+        info_solver = input_info_solver
+    elif hasattr(input_info_solver, 'info_solver'):
+        info_solver = input_info_solver.info_solver
+    else:
+        raise ValueError('input_info_solver is not related '
+                         'to a InfoSolver instance.')
+
+    params = Parameters(tag='params')
+    dict_classes = info_solver.import_classes()
+
+    dict_classes['Solver'] = import_class(
+        info_solver.module_name, info_solver.class_name)
+
+    for Class in dict_classes.values():
+        if hasattr(Class, '_complete_params_with_default'):
+            try:
+                Class._complete_params_with_default(params)
+            except TypeError:
+                try:
+                    Class._complete_params_with_default(params, info_solver)
+                except TypeError:
+                    print('TypeError for ', Class)
+                    raise
+    return params
+
+
+def load_params_simul(path_dir=None):
+    """Load the parameters and return a Parameters instance."""
+    if path_dir is None:
+        path_dir = os.getcwd()
+    return Parameters(
+        path_file=os.path.join(path_dir, 'params_simul.xml'))
+
+
+def load_info_solver(path_dir=None):
+    """Load the solver information, return an InfoSolverBase instance.
+
+    """
+    if path_dir is None:
+        path_dir = os.getcwd()
+    return InfoSolverBase(
+        path_file=os.path.join(path_dir, 'info_solver.xml'))
+
+
+# def load_info_simul(path_dir=None):
+#     """Load the data and gather them in a ContainerXML instance."""
+
+#     if path_dir is None:
+#         path_dir = os.getcwd()
+#     info_solver = load_info_solver(path_dir=path_dir)
+#     params = load_params_simul(path_dir=path_dir)
+#     info = ContainerXML(tag='info_simul')
+#     info.set_as_child(info_solver)
+#     info.set_as_child(params)
+#     return info
+
+
+if __name__ == '__main__':
+    info_solver = InfoSolverBase(tag='solver')
+
+    info_solver.complete_with_classes()
+
+    params = create_params(info_solver)
+
+    info = create_info_simul(info_solver, params)
diff --git a/fluidsim/base/solvers/__init__.py b/fluidsim/base/solvers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9zb2x2ZXJzL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/base/solvers/__init__.py
@@ -0,0 +1,15 @@
+"""Base simulations (:mod:`fluidsim.base.solvers`)
+========================================================
+
+.. currentmodule:: fluidsim.base.solvers
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   base
+   pseudo_spect
+   finite_diff
+
+"""
diff --git a/fluidsim/base/solvers/base.py b/fluidsim/base/solvers/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9zb2x2ZXJzL2Jhc2UucHk=
--- /dev/null
+++ b/fluidsim/base/solvers/base.py
@@ -0,0 +1,174 @@
+"""Base solver (:mod:`fluidsim.base.solvers.base`)
+========================================================
+
+.. currentmodule:: fluidsim.base.solvers.base
+
+Provides:
+
+.. autoclass:: InfoSolverBase
+   :members:
+   :private-members:
+
+.. autoclass:: SimulBase
+   :members:
+   :private-members:
+
+"""
+
+import numpy as np
+
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.base.params import Parameters
+
+from fluidsim.base.solvers.info_base import (
+    InfoSolverBase, create_info_simul)
+
+
+class SimulBase(object):
+    """Represent a solver.
+
+    This is the main base class which is inherited by the other
+    simulation classes.
+
+    A :class:`SimulBase` object contains at least one object of the
+    classes:
+
+    - :class:`fluidsim.base.params.Parameters`
+    - :class:`fluidsim.base.time_stepping.TimeSteppingBase`
+    - :class:`fluidsim.operators.operators.Operators`
+    - :class:`fluidsim.base.state.StateBase`
+
+    Parameters
+    ----------
+
+    params : :class:`fluidsim.base.params.Parameters`
+        Parameters for the simulation.
+
+    info_solver : :class:`fluidsim.base.solvers.info_base.InfoSolverBase`
+        Information about the particular solver.
+
+    """
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """A static method used to complete the *params* container."""
+        attribs = {'short_name_type_run': '',
+                   'NEW_DIR_RESULTS': True,
+                   'ONLY_COARSE_OPER': False,
+                   'FORCING': False,
+                   # Physical parameters:
+                   'nu_2': 0.}
+        params.set_attribs(attribs)
+
+    def __init__(self, params, info_solver=None):
+        # np.seterr(invalid='raise')
+        # np.seterr(over='raise')
+        np.seterr(all='warn')
+        np.seterr(under='ignore')
+
+        if info_solver is None:
+            info_solver = InfoSolverBase()
+            info_solver.complete_with_classes()
+        elif not isinstance(info_solver, InfoSolverBase):
+            raise ValueError('info_solver must be an InfoSolverBase object.')
+        dico_classes = info_solver.import_classes()
+
+        if not isinstance(params, Parameters):
+            raise TypeError('params should be a Parameters instance.')
+
+        # params.check_and_modify()
+        self.params = params
+        self.info = create_info_simul(info_solver, params)
+
+        # initialization operators and grid
+        Operators = dico_classes['Operators']
+        self.oper = Operators(params=params)
+
+        # initialization output
+        Output = dico_classes['Output']
+        self.output = Output(self)
+
+        self.output.print_stdout(
+            '*************************************\n' +
+            'Program FluidDyn')
+
+        # output.print_memory_usage(
+        #     'Memory usage after creating operator (equiv. seq.)')
+
+        # initialisation object variables
+        State = dico_classes['State']
+        self.state = State(self, info_solver)
+
+        # initialisation time stepping
+        TimeStepping = dico_classes['TimeStepping']
+        self.time_stepping = TimeStepping(self)
+
+        # initialisation fields (and time if needed)
+        InitFields = dico_classes['InitFields']
+        self.init_fields = InitFields(self)
+        self.init_fields()
+
+        # just for the first output
+        if params.time_stepping.USE_CFL:
+            self.time_stepping._compute_time_increment_CLF()
+
+        # initialisation forcing
+        if params.FORCING:
+            Forcing = dico_classes['Forcing']
+            self.forcing = Forcing(params, self)
+            self.forcing.compute()
+
+        # complete the initialisation of the object output
+        self.output.init_with_oper_and_state()
+
+    def tendencies_nonlin(self, variables=None):
+        """Return a null SetOfVariables object."""
+        tendencies = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+        tendencies.initialize(value=0.)
+        return tendencies
+
+
+Simul = SimulBase
+
+
+if __name__ == "__main__":
+
+    import fluiddyn as fld
+
+    info_solver = InfoSolverBase()
+    info_solver.complete_with_classes()
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 16
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 5.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.output.periods_plot.phys_fields = 0.
+
+    params.output.periods_print.print_stdout = 0.25
+    params.output.periods_save.phys_fields = 2.
+
+    sim = Simul(params)
+
+    sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/base/solvers/finite_diff.py b/fluidsim/base/solvers/finite_diff.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9zb2x2ZXJzL2Zpbml0ZV9kaWZmLnB5
--- /dev/null
+++ b/fluidsim/base/solvers/finite_diff.py
@@ -0,0 +1,28 @@
+
+from fluidsim.base.solvers.base import InfoSolverBase
+
+
+class InfoSolverFiniteDiff(InfoSolverBase):
+    """Contain the information on a solver."""
+
+    def _init_root(self):
+
+        super(InfoSolverFiniteDiff, self)._init_root()
+
+        self.classes.set_child(
+            'State',
+            attribs={'module_name': 'fluidsim.base.state',
+                     'class_name': 'StateBase'})
+
+        self.classes.set_child(
+            'TimeStepping',
+            attribs={'module_name':
+                     'fluidsim.base.time_stepping.finite_diff',
+                     'class_name':
+                     'TimeSteppingFiniteDiffCrankNicolson'})
+
+        self.classes.set_child(
+            'Operators',
+            attribs={'module_name':
+                     'fluidsim.operators.op_finitediff',
+                     'class_name': 'OperatorFiniteDiff1DPeriodic'})
diff --git a/fluidsim/base/solvers/info_base.py b/fluidsim/base/solvers/info_base.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9zb2x2ZXJzL2luZm9fYmFzZS5weQ==
--- /dev/null
+++ b/fluidsim/base/solvers/info_base.py
@@ -0,0 +1,82 @@
+
+from copy import deepcopy
+
+from fluiddyn.util.containerxml import ContainerXML
+from fluiddyn.util.util import import_class
+
+
+def create_info_simul(info_solver, params):
+    """Create a ContainerXML instance gathering info_solver and params."""
+    info = ContainerXML(tag='info_simul')
+    info.set_as_child(info_solver)
+    info.set_as_child(params)
+    return info
+
+
+def _merged_element(el1, el2):
+    result = deepcopy(el1)
+    result.extend(deepcopy(el2))
+    return result
+
+
+class InfoSolverBase(ContainerXML):
+    """Contain the information on a solver."""
+    def __init__(self, **kargs):
+
+        if 'tag' not in kargs:
+            kargs['tag'] = 'solver'
+
+        super(InfoSolverBase, self).__init__(**kargs)
+
+        if kargs['tag'] == 'solver' and 'path_file' not in kargs:
+            self._init_root()
+
+    def _init_root(self):
+
+        self.set_attribs({'module_name': 'fluidsim.base.solvers.base',
+                          'class_name': 'SimulBase',
+                          'short_name': 'Base'})
+
+        self.set_child('classes')
+
+        self.classes.set_child(
+            'InitFields',
+            attribs={'module_name': 'fluidsim.base.init_fields',
+                     'class_name': 'InitFieldsBase'})
+
+        self.classes.set_child(
+            'Forcing',
+            attribs={'module_name': 'fluidsim.base.forcing',
+                     'class_name': 'ForcingBase'})
+
+        self.classes.set_child(
+            'Output',
+            attribs={'module_name': 'fluidsim.base.output.base',
+                     'class_name': 'OutputBase'})
+
+    def import_classes(self):
+        """Import the classes and return a dictionary."""
+        classes = self._elemxml.findall('classes')
+        dict_classes = {}
+        if len(classes) == 0:
+            return dict_classes
+        classes = reduce(_merged_element, classes)
+        for c in classes.getchildren():
+            try:
+                module_name = c.attrib['module_name']
+                class_name = c.attrib['class_name']
+            except KeyError:
+                pass
+            else:
+                Class = import_class(module_name, class_name)
+                dict_classes[c.tag] = Class
+
+        return dict_classes
+
+    def complete_with_classes(self):
+        dict_classes = self.import_classes()
+        for Class in dict_classes.values():
+            if hasattr(Class, '_complete_info_solver'):
+                Class._complete_info_solver(self)
+
+
diff --git a/fluidsim/base/solvers/pseudo_spect.py b/fluidsim/base/solvers/pseudo_spect.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9zb2x2ZXJzL3BzZXVkb19zcGVjdC5weQ==
--- /dev/null
+++ b/fluidsim/base/solvers/pseudo_spect.py
@@ -0,0 +1,140 @@
+"""Base solver (:mod:`fluidsim.base.solvers.pseudo_spect`)
+================================================================
+
+.. currentmodule:: fluidsim.base.solvers.pseudo_spect
+
+Provides:
+
+.. autoclass:: InfoSolverPseudoSpectral
+   :members:
+   :private-members:
+
+.. autoclass:: SimulBasePseudoSpectral
+   :members:
+   :private-members:
+
+"""
+
+import numpy as np
+
+from fluiddyn.util import mpi
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.base.solvers.base import SimulBase, InfoSolverBase
+
+
+class InfoSolverPseudoSpectral(InfoSolverBase):
+    """Contain the information on a solver."""
+
+    def _init_root(self):
+
+        super(InfoSolverPseudoSpectral, self)._init_root()
+
+        self.module_name = 'fluidsim.base.solvers.pseudo_spect'
+        self.class_name = 'SimulBasePseudoSpectral'
+        self.short_name = 'BasePS'
+
+        self.classes.set_child(
+            'State',
+            attribs={'module_name': 'fluidsim.base.state',
+                     'class_name': 'StatePseudoSpectral'})
+
+        self.classes.set_child(
+            'TimeStepping',
+            attribs={'module_name':
+                     'fluidsim.base.time_stepping.pseudo_spect_cy',
+                     'class_name': 'TimeSteppingPseudoSpectral'})
+
+        self.classes.set_child(
+            'Operators',
+            attribs={'module_name': 'fluidsim.operators.operators',
+                     'class_name': 'OperatorsPseudoSpectral2D'})
+
+
+info_solver_ps = InfoSolverPseudoSpectral()
+info_solver_ps.complete_with_classes()
+
+
+class SimulBasePseudoSpectral(SimulBase):
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """A static method used to complete the *params* container."""
+        SimulBase._complete_params_with_default(params)
+
+        attribs = {'nu_8': 0.,
+                   'nu_4': 0.,
+                   'nu_m4': 0.}
+        params.set_attribs(attribs)
+
+    def __init__(self, params, info_solver=info_solver_ps):
+        super(SimulBasePseudoSpectral, self).__init__(params, info_solver)
+
+    def compute_freq_diss(self):
+        if self.params.nu_2 > 0:
+            f_d = self.params.nu_2*self.oper.K2
+        else:
+            f_d = np.zeros_like(self.oper.K2)
+
+        if self.params.nu_4 > 0.:
+            f_d += self.params.nu_4*self.oper.K4
+
+        if self.params.nu_8 > 0.:
+            f_d += self.params.nu_8*self.oper.K8
+
+        if self.params.nu_m4 > 0.:
+            f_d_hypo = self.params.nu_m4/self.oper.K2_not0**2
+            # mode K2 = 0 !
+            if mpi.rank == 0:
+                f_d_hypo[0, 0] = f_d_hypo[0, 1]
+        else:
+            f_d_hypo = np.zeros_like(f_d)
+
+        return f_d, f_d_hypo
+
+    def tendencies_nonlin(self, variables=None):
+        """Return a null SetOfVariables object."""
+        tendencies = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+        tendencies.initialize(value=0.)
+        return tendencies
+
+Simul = SimulBasePseudoSpectral
+
+
+if __name__ == "__main__":
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver_ps)
+
+    params.short_name_type_run = 'test'
+
+    nh = 16
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 5.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.output.periods_plot.phys_fields = 0.
+
+    params.output.periods_print.print_stdout = 0.25
+    params.output.periods_save.phys_fields = 2.
+
+    sim = Simul(params)
+
+    sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/base/state.py b/fluidsim/base/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS9zdGF0ZS5weQ==
--- /dev/null
+++ b/fluidsim/base/state.py
@@ -0,0 +1,161 @@
+"""State of the variables (:mod:`fluidsim.base.state`)
+============================================================
+
+.. currentmodule:: fluidsim.base.state
+
+Provides:
+
+.. autoclass:: StateBase
+   :members:
+   :private-members:
+
+.. autoclass:: StatePseudoSpectral
+   :members:
+   :private-members:
+
+"""
+
+import numpy as np
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+
+class StateBase(object):
+    """Contains the state variables and handles the access to fields."""
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.State.set_attribs(
+            {'keys_state_phys': ['ux', 'uy'],
+             'keys_computable': [],
+             'keys_phys_needed': ['ux', 'uy']})
+
+    def __init__(self, sim, info_solver):
+        self.sim = sim
+        self.params = sim.params
+        self.oper = sim.oper
+
+        # creation of the SetOfVariables state_fft and state_phys
+        self.keys_state_phys = info_solver.classes.State.keys_state_phys
+        self.keys_computable = info_solver.classes.State.keys_computable
+
+        self.state_phys = SetOfVariables(keys=self.keys_state_phys,
+                                         shape1var=self.oper.shapeX_loc,
+                                         dtype=np.float64,
+                                         name_type_variables='state_phys'
+                                         )
+        self.vars_computed = {}
+        self.it_computed = {}
+
+    def compute(self, key):
+        pass
+
+    def clear_computed(self):
+        self.vars_computed.clear()
+
+    def __call__(self, key):
+        if key in self.keys_state_phys:
+            return self.state_phys[key]
+        else:
+            it = self.sim.time_stepping.it
+            if (key in self.vars_computed and it == self.it_computed[key]):
+                return self.vars_computed[key]
+            else:
+                value = self.compute(key)
+                self.vars_computed[key] = value
+                self.it_computed[key] = it
+                return value
+
+    def __setitem__(self, key, value):
+        if key in self.keys_state_phys:
+            self.state_phys[key] = value
+        else:
+            raise ValueError('key "'+key+'" is not known')
+
+    def can_this_key_be_obtained(self, key):
+        return (key in self.keys_state_phys or
+                key in self.keys_computable)
+
+
+class StatePseudoSpectral(StateBase):
+    """Contains the state variables and handles the access to fields.
+
+    This is the general class for the pseudo-spectral solvers.
+
+    """
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+
+        StateBase._complete_info_solver(info_solver)
+
+        info_solver.classes.State.set_attribs(
+            {'keys_state_fft': ['ux_fft', 'uy_fft']})
+
+    def __init__(self, sim, info_solver):
+
+        super(StatePseudoSpectral, self).__init__(sim, info_solver)
+
+        self.keys_state_fft = info_solver.classes.State['keys_state_fft']
+        self.state_fft = SetOfVariables(keys=self.keys_state_fft,
+                                        shape1var=self.oper.shapeK_loc,
+                                        dtype=np.complex128,
+                                        name_type_variables='state_fft')
+
+    def __call__(self, key):
+        """Return the variable corresponding to the given key."""
+        if key in self.keys_state_fft:
+            return self.state_fft[key]
+        elif key in self.keys_state_phys:
+            return self.state_phys[key]
+        else:
+            it = self.sim.time_stepping.it
+            if (key in self.vars_computed and it == self.it_computed[key]):
+                return self.vars_computed[key]
+            else:
+                value = self.compute(key)
+                self.vars_computed[key] = value
+                self.it_computed[key] = it
+                return value
+
+    def __setitem__(self, key, value):
+        if key in self.keys_state_fft:
+            self.state_fft[key] = value
+        elif key in self.keys_state_phys:
+            self.state_phys[key] = value
+        else:
+            raise ValueError('key "'+key+'" is not known')
+
+    def statefft_from_statephys(self):
+        fft2 = self.oper.fft2
+        for ik in xrange(self.state_fft.nb_variables):
+            self.state_fft.data[ik][:] = fft2(self.state_phys.data[ik])
+
+    def statephys_from_statefft(self):
+        ifft2 = self.oper.ifft2
+        for ik in xrange(self.state_fft.nb_variables):
+            self.state_phys.data[ik] = ifft2(self.state_fft.data[ik])
+
+    def return_statephys_from_statefft(self, state_fft=None):
+        """Return the state in physical space."""
+        ifft2 = self.oper.ifft2
+        if state_fft is None:
+            state_fft = self.state_fft
+
+        state_phys = SetOfVariables(like_this_sov=self.state_phys)
+        for ik in xrange(self.state_fft.nb_variables):
+            state_phys.data[ik] = ifft2(state_fft.data[ik])
+        return state_phys
+
+    def can_this_key_be_obtained(self, key):
+        return (key in self.keys_state_phys or
+                key in self.keys_computable or
+                key in self.keys_state_fft)
diff --git a/fluidsim/base/test/__init__.py b/fluidsim/base/test/__init__.py
new file mode 100644
diff --git a/fluidsim/base/test/test_base_solver.py b/fluidsim/base/test/test_base_solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS90ZXN0L3Rlc3RfYmFzZV9zb2x2ZXIucHk=
--- /dev/null
+++ b/fluidsim/base/test/test_base_solver.py
@@ -0,0 +1,42 @@
+
+import unittest
+
+import numpy as np
+
+import fluiddyn as fld
+
+from fluidsim.base.solvers.pseudo_spect import (
+    SimulBasePseudoSpectral, info_solver_ps)
+
+from fluiddyn.io import stdout_redirected
+
+
+class TestBaseSolver(unittest.TestCase):
+    def test_simul(self):
+        """Should be able to run a base experiment."""
+
+    params = fld.simul.create_params(info_solver_ps)
+
+    params.short_name_type_run = 'test'
+
+    nh = 16
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    params.nu_2 = 1.
+
+    params.time_stepping.t_end = 2.
+
+    params.output.periods_plot.phys_fields = 0.
+
+    with stdout_redirected():
+        sim = SimulBasePseudoSpectral(params)
+        sim.time_stepping.start()
+
+    fld.show()
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fluidsim/base/time_stepping/__init__.py b/fluidsim/base/time_stepping/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS90aW1lX3N0ZXBwaW5nL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/base/time_stepping/__init__.py
@@ -0,0 +1,16 @@
+"""Time stepping (:mod:`fluidsim.base.time_stepping`)
+===========================================================
+
+.. currentmodule:: fluidsim.base.time_stepping
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   base
+   pseudo_spect
+   pseudo_spect_cy
+   finite_diff
+
+"""
diff --git a/fluidsim/base/time_stepping/base.py b/fluidsim/base/time_stepping/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS90aW1lX3N0ZXBwaW5nL2Jhc2UucHk=
--- /dev/null
+++ b/fluidsim/base/time_stepping/base.py
@@ -0,0 +1,207 @@
+"""Time stepping (:mod:`fluidsim.base.time_stepping.base`)
+================================================================
+
+.. currentmodule:: fluidsim.base.time_stepping.base
+
+Provides:
+
+.. autoclass:: TimeSteppingBase
+   :members:
+   :private-members:
+
+"""
+
+from time import time
+
+from fluiddyn.util import mpi
+
+
+class TimeSteppingBase(object):
+    """Universal time stepping class used for all solvers.
+
+
+    """
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        attribs = {'USE_T_END': True,
+                   't_end': 10.,
+                   'it_end': 10,
+                   'USE_CFL': True,
+                   'type_time_scheme': 'RK4',
+                   'deltat0': 0.2}
+        params.set_child('time_stepping', attribs=attribs)
+
+    def _init_compute_time_step(self):
+
+        params_ts = self.params.time_stepping
+
+        if params_ts.USE_CFL:
+            if params_ts.type_time_scheme == 'RK2':
+                self.CFL = 0.4
+            elif params_ts.type_time_scheme == 'RK4':
+                self.CFL = 1.0
+            else:
+                raise ValueError('Problem name time_scheme')
+        else:
+            self.deltat = params_ts.deltat0
+
+        self.deltat = params_ts.deltat0
+
+        has_ux = self.sim.state.can_this_key_be_obtained('ux')
+        has_uy = self.sim.state.can_this_key_be_obtained('uy')
+        has_uz = self.sim.state.can_this_key_be_obtained('uz')
+
+        if has_ux and has_uy and has_uz:
+            self._compute_time_increment_CLF = \
+                self._compute_time_increment_CLF_uxuyuz
+        elif has_ux and has_uy:
+            self._compute_time_increment_CLF = \
+                self._compute_time_increment_CLF_uxuy
+        elif has_ux:
+            self._compute_time_increment_CLF = \
+                self._compute_time_increment_CLF_ux
+        else:
+            self._compute_time_increment_CLF = \
+                self._compute_time_increment_CLF_no_ux
+
+        self.deltat_max = 0.2
+
+    def _init_time_scheme(self):
+
+        params_ts = self.params.time_stepping
+
+        if params_ts.type_time_scheme == 'RK2':
+            self._time_step_RK = self._time_step_RK2
+        elif params_ts.type_time_scheme == 'RK4':
+            self._time_step_RK = self._time_step_RK4
+        else:
+            raise ValueError('Problem name time_scheme')
+
+    def start(self):
+        """Loop to run the function :func:`one_time_step`.
+
+        If *self.USE_T_END* is true, run till ``t >= t_end``,
+        otherwise run *self.it_end* time steps.
+        """
+        print_stdout = self.sim.output.print_stdout
+        print_stdout(
+            '*************************************\n' +
+            'Beginning of the computation')
+        if self.sim.output.has_to_save:
+            self.sim.output.phys_fields.save()
+        time_begining_simul = time()
+        if self.params.time_stepping.USE_T_END:
+            print_stdout(
+                '    compute until t = {0:10.6g}'.format(
+                    self.params.time_stepping.t_end))
+            while self.t < self.params.time_stepping.t_end:
+                self.one_time_step()
+        else:
+            print_stdout(
+                '    compute until it = {0:8d}'.format(
+                    self.params.time_stepping.it_end))
+            while self.it < self.params.time_stepping.it_end:
+                self.one_time_step()
+        total_time_simul = time() - time_begining_simul
+        self.sim.output.end_of_simul(total_time_simul)
+
+    def one_time_step(self):
+        if self.params.time_stepping.USE_CFL:
+            self._compute_time_increment_CLF()
+        if self.params.FORCING:
+            self.sim.forcing.compute()
+        self.sim.output.one_time_step()
+        self.one_time_step_computation()
+
+    def _compute_time_increment_CLF_uxuyuz(self):
+        """Compute the time increment deltat with a CLF condition."""
+
+        ux = self.sim.state('ux')
+        uy = self.sim.state('uy')
+        uz = self.sim.state('uz')
+
+        max_ux = abs(ux).max()
+        max_uy = abs(uy).max()
+        max_uz = abs(uz).max()
+        temp = (max_ux/self.sim.oper.deltax +
+                max_uy/self.sim.oper.deltay +
+                max_uz/self.sim.oper.deltaz)
+
+        if mpi.nb_proc > 1:
+            temp = mpi.comm.allreduce(temp, op=mpi.MPI.MAX)
+
+        if temp > 0:
+            deltat_CFL = self.CFL/temp
+        else:
+            deltat_CFL = self.deltat_max
+
+        maybe_new_dt = min(deltat_CFL, self.deltat_max)
+        normalize_diff = abs(self.deltat-maybe_new_dt)/maybe_new_dt
+
+        if normalize_diff > 0.02:
+            self.deltat = maybe_new_dt
+
+    def _compute_time_increment_CLF_uxuy(self):
+        """Compute the time increment deltat with a CLF condition."""
+
+        ux = self.sim.state('ux')
+        uy = self.sim.state('uy')
+
+        max_ux = abs(ux).max()
+        max_uy = abs(uy).max()
+        temp = (max_ux/self.sim.oper.deltax + max_uy/self.sim.oper.deltay)
+
+        if mpi.nb_proc > 1:
+            temp = mpi.comm.allreduce(temp, op=mpi.MPI.MAX)
+
+        if temp > 0:
+            deltat_CFL = self.CFL/temp
+        else:
+            deltat_CFL = self.deltat_max
+
+        maybe_new_dt = min(deltat_CFL, self.deltat_max)
+        normalize_diff = abs(self.deltat-maybe_new_dt)/maybe_new_dt
+
+        if normalize_diff > 0.02:
+            self.deltat = maybe_new_dt
+
+    def _compute_time_increment_CLF_ux(self):
+        """Compute the time increment deltat with a CLF condition."""
+        ux = self.sim.state('ux')
+        max_ux = abs(ux).max()
+        temp = max_ux/self.sim.oper.deltax
+
+        if mpi.nb_proc > 1:
+            temp = mpi.comm.allreduce(temp, op=mpi.MPI.MAX)
+
+        if temp > 0:
+            deltat_CFL = self.CFL/temp
+        else:
+            deltat_CFL = self.deltat_max
+
+        maybe_new_dt = min(deltat_CFL, self.deltat_max)
+        normalize_diff = abs(self.deltat-maybe_new_dt)/maybe_new_dt
+
+        if normalize_diff > 0.02:
+            self.deltat = maybe_new_dt
+
+    def _compute_time_increment_CLF_no_ux(self):
+        """Compute the time increment deltat with a CLF condition."""
+        max_ux = self.params.U
+        temp = max_ux/self.sim.oper.deltax
+
+        if mpi.nb_proc > 1:
+            temp = mpi.comm.allreduce(temp, op=mpi.MPI.MAX)
+
+        if temp > 0:
+            deltat_CFL = self.CFL/temp
+        else:
+            deltat_CFL = self.deltat_max
+
+        maybe_new_dt = min(deltat_CFL, self.deltat_max)
+        normalize_diff = abs(self.deltat-maybe_new_dt)/maybe_new_dt
+
+        if normalize_diff > 0.02:
+            self.deltat = maybe_new_dt
diff --git a/fluidsim/base/time_stepping/finite_diff.py b/fluidsim/base/time_stepping/finite_diff.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS90aW1lX3N0ZXBwaW5nL2Zpbml0ZV9kaWZmLnB5
--- /dev/null
+++ b/fluidsim/base/time_stepping/finite_diff.py
@@ -0,0 +1,144 @@
+"""Time stepping (:mod:`fluidsim.base.time_stepping.finite_diff`)
+=======================================================================
+
+.. currentmodule:: fluidsim.base.time_stepping.finite_diff
+
+Provides:
+
+.. autoclass:: TimeSteppingFiniteDiffCrankNicolson
+   :members:
+   :private-members:
+
+"""
+
+import numpy as np
+import scipy.sparse as sparse
+from scipy.sparse.linalg import spsolve
+
+from copy import deepcopy
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from .base import TimeSteppingBase
+
+
+class TimeSteppingFiniteDiffCrankNicolson(TimeSteppingBase):
+    """
+    Time stepping class for finite-difference solvers.
+
+    """
+    def __init__(self, sim):
+        self.params = sim.params
+        self.sim = sim
+
+        self.it = 0
+        self.t = 0
+
+        # self._init_freq_lin()
+        self._init_compute_time_step()
+        self._init_time_scheme()
+
+        self.L = sim.linear_operator()
+        self.set_of_vars_temp = SetOfVariables(
+            like_this_sov=sim.state.state_phys)
+
+    def one_time_step_computation(self):
+        """One time step"""
+        self._time_step_RK()
+        self.t += self.deltat
+        self.it += 1
+        if np.isnan(np.min(self.sim.state.state_phys.data)):
+            raise ValueError(
+                'nan at it = {0}, t = {1:.4f}'.format(self.it, self.t))
+
+    def _time_step_RK2(self):
+        r"""Advance in time the variables with the Runge-Kutta 2 method.
+
+        .. _rk2timeschemeFiniteDiff:
+
+        Notes
+        -----
+
+        .. Look at Simson KTH documentation...
+           (http://www.mech.kth.se/~mattias/simson-user-guide-v4.0.pdf)
+
+        The Runge-Kutta 2 method computes an approximation of the
+        solution after a time increment :math:`dt`. We denote the
+        initial time :math:`t = 0`.
+
+        For the finite difference schemes, We consider an equation of the form
+
+        .. math:: \p_t S = L S + N(S),
+
+        The linear term can be treated with an implicit method while
+        the nonlinear term have to be treated with an explicit method
+        (see for example `Explicit and implicit methods
+        <http://en.wikipedia.org/wiki/Explicit_and_implicit_methods>`_).
+
+        - Approximation 1:
+
+          For the first step where the nonlinear term is approximated
+          as :math:`N(S) \simeq N(S_0)`, we obtain
+
+          .. math::
+             \left( 1 - \frac{dt}{4} L \right) S_{A1dt/2}
+             \simeq \left( 1 + \frac{dt}{4} L \right) S_0 + N(S_0)dt/2
+
+          Once the right-hand side has been computed, a linear
+          equation has to be solved. It is not efficient to invert the
+          matrix :math:`1 + \frac{dt}{2} L` so other methods have to
+          be used, as the `Thomas algorithm
+          <http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm>`_,
+          or algorithms based on the LU or the QR decompositions.
+
+        - Approximation 2:
+
+            The nonlinear term is then approximated as :math:`N(S)
+            \simeq N(S_{A1dt/2})`, which gives
+
+            .. math::
+               \left( 1 - \frac{dt}{2} L \right) S_{A2dt}
+               \simeq \left( 1 + \frac{dt}{2} L \right) S_0 + N(S_{A1dt/2})dt
+
+        """
+        dt = self.deltat
+        sim = self.sim
+        identity = sparse.identity(sim.state.state_phys.data.size)
+
+        # it seems that there is a bug with the proper RK2 method
+        # (it "goes too fast")
+
+        # # approximation 1 (at t + dt/2 -> "A1dt2"):
+        # tendenciesNL_0 = sim.tendencies_nonlin()
+        # rhs_A1dt2 = self.right_hand_side(sim.state.state_phys,
+        #                                  tendenciesNL_0, dt/2)
+
+        # A_A1dt2 = identity - dt/4*self.L
+        # S_A1dt2 = self.invert_to_get_solution(A_A1dt2, rhs_A1dt2)
+        # del(rhs_A1dt2, A_A1dt2)
+
+        # # approximation 2 (at t + dt -> "A2dt"):
+        # tendenciesNL_1 = sim.tendencies_nonlin(S_A1dt2)
+        # rhs_A2dt = self.right_hand_side(S_A1dt2, tendenciesNL_1, dt)
+        # A_A2dt = identity - dt/2*self.L
+        # sim.state.state_phys = deepcopy(
+        #     self.invert_to_get_solution(A_A2dt, rhs_A2dt))
+
+        # it seems to work with the basic Newton time stepping:
+        tendenciesNL_0 = sim.tendencies_nonlin()
+        rhs_A1dt = self.right_hand_side(sim.state.state_phys,
+                                        tendenciesNL_0, dt)
+        A_A1dt = identity - dt/2*self.L
+        sim.state.state_phys = deepcopy(
+            self.invert_to_get_solution(A_A1dt, rhs_A1dt))
+
+    def right_hand_side(self, S, N, dt):
+        return (S.data.ravel()
+                + dt/2*self.L.dot(S.data.flat)
+                + dt*N.data.ravel())
+
+    def invert_to_get_solution(self, A, b):
+        """Solve the linear system :math:`Ax = b`."""
+        self.set_of_vars_temp.data = spsolve(A, b).reshape(
+            self.set_of_vars_temp.data.shape)
+        return self.set_of_vars_temp
diff --git a/fluidsim/base/time_stepping/pseudo_spect.py b/fluidsim/base/time_stepping/pseudo_spect.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS90aW1lX3N0ZXBwaW5nL3BzZXVkb19zcGVjdC5weQ==
--- /dev/null
+++ b/fluidsim/base/time_stepping/pseudo_spect.py
@@ -0,0 +1,289 @@
+"""Time stepping (:mod:`fluidsim.base.time_stepping.pseudo_spect`)
+========================================================================
+
+.. currentmodule:: fluidsim.base.time_stepping.pseudo_spect
+
+Provides:
+
+.. autoclass:: TimeSteppingPseudoSpectral
+   :members:
+   :private-members:
+
+"""
+
+import numpy as np
+
+from .base import TimeSteppingBase
+
+
+class ExactLinearCoefs(object):
+    """Handle the computation of the exact coefficient for the RK4."""
+
+    def __init__(self, time_stepping):
+        self.time_stepping = time_stepping
+        sim = time_stepping.sim
+        self.shapeK_loc = sim.oper.shapeK_loc
+        self.freq_lin = time_stepping.freq_lin
+
+        self.exact = np.empty_like(self.freq_lin)
+        self.exact2 = np.empty_like(self.freq_lin)
+
+        if sim.params.time_stepping.USE_CFL:
+            self.get_updated_coefs = self.get_updated_coefs_CLF
+            self.dt_old = 0.
+        else:
+            self.compute(time_stepping.deltat)
+            self.get_updated_coefs = self.get_coefs
+
+    def compute(self, dt):
+        f_lin = self.freq_lin
+        self.exact = np.exp(-dt*f_lin)
+        self.exact2 = np.exp(-dt/2*f_lin)
+        self.dt_old = dt
+
+    def get_updated_coefs_CLF(self):
+        dt = self.time_stepping.deltat
+        if self.dt_old != dt:
+            self.compute(dt)
+        return self.exact, self.exact2
+
+    def get_coefs(self):
+        return self.exact, self.exact2
+
+
+class TimeSteppingPseudoSpectral(TimeSteppingBase):
+    """Time stepping class for pseudo-spectral solvers.
+
+    """
+    def __init__(self, sim):
+        self.params = sim.params
+        self.sim = sim
+
+        self.it = 0
+        self.t = 0
+
+        self._init_freq_lin()
+        self._init_compute_time_step()
+        self._init_exact_linear_coef()
+        self._init_time_scheme()
+
+    def _init_freq_lin(self):
+        f_d, f_d_hypo = self.sim.compute_freq_diss()
+        freq_dissip = f_d + f_d_hypo
+
+        if hasattr(self.sim, 'compute_freq_complex'):
+            freq_complex = self._compute_freq_complex()
+            self.freq_lin = freq_dissip + freq_complex
+            freq_max = freq_complex.imag.max()
+            self.deltat_max = 0.78*np.pi/freq_max
+        else:
+            self.freq_lin = freq_dissip
+
+    def _compute_freq_complex(self):
+        state_fft = self.sim.state.state_fft
+        freq_complex = np.empty_like(state_fft.data)
+        for ik, key in enumerate(state_fft.keys):
+            freq_complex[ik] = self.sim.compute_freq_complex(key)
+        return freq_complex
+
+    def _init_exact_linear_coef(self):
+        self.exact_linear_coefs = ExactLinearCoefs(self)
+
+    def one_time_step_computation(self):
+        """One time step"""
+        self._time_step_RK()
+        self.sim.oper.dealiasing(self.sim.state.state_fft)
+        self.sim.state.statephys_from_statefft()
+        self.t += self.deltat
+        self.it += 1
+        if np.isnan(np.min(self.sim.state.state_fft.data[0])):
+            raise ValueError(
+                'nan at it = {0}, t = {1:.4f}'.format(self.it, self.t))
+
+    def _time_step_RK2(self):
+        r"""Advance in time with the Runge-Kutta 2 method.
+
+        .. _rk2timescheme:
+
+        Notes
+        -----
+
+        .. |p| mathmacro:: \partial
+
+        We consider an equation of the form
+
+        .. math:: \p_t S = \sigma S + N(S),
+
+        The Runge-Kutta 2 method computes an approximation of the
+        solution after a time increment :math:`dt`. We denote the
+        initial time :math:`t = 0`.
+
+        - Approximation 1:
+
+          .. math:: \p_t \log S = \sigma + \frac{N(S_0)}{S_0},
+
+          Integrating from :math:`t` to :math:`t+dt/2`, it gives:
+
+          .. |SA1halfdt| mathmacro:: S_{A1dt/2}
+
+          .. math:: \SA1halfdt = (S_0 + N_0 dt/2) e^{\frac{\sigma dt}{2}}.
+
+
+        - Approximation 2:
+
+          .. math::
+             \p_t \log S = \sigma
+             + \frac{N(\SA1halfdt)}{ \SA1halfdt },
+
+          Integrating from :math:`t` to :math:`t+dt` and retaining
+          only the terms in :math:`dt^1` gives:
+
+          .. math::
+             S_{dtA2} = S_0 e^{\sigma dt}
+             + N(\SA1halfdt) dt e^{\frac{\sigma dt}{2}}.
+
+        """
+        dt = self.deltat
+        diss, diss2 = self.exact_linear_coefs.get_updated_coefs()
+
+        tendencies_nonlin = self.sim.tendencies_nonlin
+        state_fft = self.sim.state.state_fft
+
+        tendencies_fft_n = tendencies_nonlin()
+        state_fft_n12 = (state_fft + dt/2*tendencies_fft_n)*diss2
+        tendencies_fft_n12 = tendencies_nonlin(state_fft_n12)
+        self.sim.state.state_fft = (state_fft*diss +
+                                    dt*diss2*tendencies_fft_n12)
+
+    def _time_step_RK4(self):
+        r"""Advance in time with the Runge-Kutta 4 method.
+
+        .. _rk4timescheme:
+
+        We consider an equation of the form
+
+        .. math:: \p_t S = \sigma S + N(S),
+
+        The Runge-Kutta 4 method computes an approximation of the
+        solution after a time increment :math:`dt`. We denote the
+        initial time as :math:`t = 0`. This time scheme uses 4
+        approximations. Only the terms in :math:`dt^1` are retained.
+
+        - Approximation 1:
+
+          .. math:: \p_t \log S = \sigma + \frac{N(S_0)}{S_0},
+
+          Integrating from :math:`t` to :math:`t+dt/2` gives:
+
+          .. math:: \SA1halfdt = (S_0 + N_0 dt/2) e^{\sigma \frac{dt}{2}}.
+
+          Integrating from :math:`t` to :math:`t+dt` gives:
+
+          .. math:: S_{A1dt} = (S_0 + N_0 dt) e^{\sigma dt}.
+
+
+        - Approximation 2:
+
+          .. math::
+             \p_t \log S = \sigma
+             + \frac{N(\SA1halfdt)}{ \SA1halfdt },
+
+          Integrating from :math:`t` to :math:`t+dt/2` gives:
+
+          .. |SA2halfdt| mathmacro:: S_{A2 dt/2}
+
+          .. math::
+             \SA2halfdt = S_0 e^{\sigma \frac{dt}{2}}
+             + N(\SA1halfdt) \frac{dt}{2}.
+
+          Integrating from :math:`t` to :math:`t+dt` gives:
+
+          .. math::
+             S_{A2dt} = S_0 e^{\sigma dt}
+             + N(\SA1halfdt) e^{\sigma \frac{dt}{2}} dt.
+
+
+        - Approximation 3:
+
+          .. math::
+             \p_t \log S = \sigma
+             + \frac{N(\SA2halfdt)}{ \SA2halfdt },
+
+          Integrating from :math:`t` to :math:`t+dt` gives:
+
+          .. math::
+             S_{A3dt} = S_0 e^{\sigma dt}
+             + N(\SA2halfdt) e^{\sigma \frac{dt}{2}} dt.
+
+        - Approximation 4:
+
+          .. math::
+             \p_t \log S = \sigma
+             + \frac{N(S_{A3dt})}{ S_{A3dt} },
+
+          Integrating from :math:`t` to :math:`t+dt` gives:
+
+          .. math::
+             S_{A4dt} = S_0 e^{\sigma dt} + N(S_{A3dt}) dt.
+
+
+        The final result is a pondered average of the results of 4
+        approximations for the time :math:`t+dt`:
+
+          .. math::
+             \frac{1}{3} \left[
+             \frac{1}{2} S_{A1dt}
+             + S_{A2dt} + S_{A3dt}
+             + \frac{1}{2} S_{A4dt}
+             \right],
+
+        which is equal to:
+
+          .. math::
+             S_0 e^{\sigma dt}
+             + \frac{dt}{3} \left[
+             \frac{1}{2} N(S_0) e^{\sigma dt}
+             + N(\SA1halfdt) e^{\sigma \frac{dt}{2}}
+             + N(\SA2halfdt) e^{\sigma \frac{dt}{2}}
+             + \frac{1}{2} N(S_{A3dt})\right].
+
+        """
+
+        dt = self.deltat
+        diss, diss2 = self.exact_linear_coefs.get_updated_coefs()
+
+        tendencies_nonlin = self.sim.tendencies_nonlin
+        state_fft = self.sim.state.state_fft
+
+        tendencies_fft_0 = tendencies_nonlin()
+
+        # based on approximation 1
+        state_fft_temp = (state_fft +
+                          dt/6*tendencies_fft_0)*diss
+        state_fft_np12_approx1 = (state_fft +
+                                  dt/2*tendencies_fft_0)*diss2
+
+        del(tendencies_fft_0)
+        tendencies_fft_1 = tendencies_nonlin(state_fft_np12_approx1)
+        del(state_fft_np12_approx1)
+
+        # based on approximation 2
+        state_fft_temp += dt/3*diss2*tendencies_fft_1
+        state_fft_np12_approx2 = (state_fft*diss2 +
+                                  dt/2*tendencies_fft_1)
+
+        del(tendencies_fft_1)
+        tendencies_fft_2 = tendencies_nonlin(state_fft_np12_approx2)
+        del(state_fft_np12_approx2)
+
+        # based on approximation 3
+        state_fft_temp += dt/3*diss2*tendencies_fft_2
+        state_fft_np1_approx = (state_fft*diss +
+                                dt*diss2*tendencies_fft_2)
+
+        del(tendencies_fft_2)
+        tendencies_fft_3 = tendencies_nonlin(state_fft_np1_approx)
+        del(state_fft_np1_approx)
+
+        # result using the 4 approximations
+        self.sim.state.state_fft = state_fft_temp + dt/6*tendencies_fft_3
diff --git a/fluidsim/base/time_stepping/pseudo_spect_cy.pyx b/fluidsim/base/time_stepping/pseudo_spect_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vYmFzZS90aW1lX3N0ZXBwaW5nL3BzZXVkb19zcGVjdF9jeS5weXg=
--- /dev/null
+++ b/fluidsim/base/time_stepping/pseudo_spect_cy.pyx
@@ -0,0 +1,521 @@
+"""
+Time stepping Cython (:mod:`fluidsim.base.time_stepping.pseudo_spect_cy`)
+=========================================================================
+
+.. currentmodule:: fluidsim.base.time_stepping.pseudo_spect_cy
+
+Provides:
+
+.. autoclass:: ExactLinearCoefs
+   :members:
+   :private-members:
+
+.. autoclass:: TimeSteppingPseudoSpectral
+   :members:
+   :private-members:
+
+"""
+
+cimport numpy as np
+import numpy as np
+np.import_array()
+
+from time import time, sleep
+import datetime
+import os
+import matplotlib.pyplot as plt
+import cython
+
+from libc.math cimport exp
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from pseudo_spect import ExactLinearCoefs as ExactLinearCoefsPurePython
+from pseudo_spect import TimeSteppingPseudoSpectral as \
+    TimeSteppingPseudoSpectralPurePython
+
+
+# we define python and c types for physical and Fourier spaces
+DTYPEb = np.uint8
+ctypedef np.uint8_t DTYPEb_t
+DTYPEi = np.int
+ctypedef np.int_t DTYPEi_t
+DTYPEf = np.float64
+ctypedef np.float64_t DTYPEf_t
+DTYPEc = np.complex128
+ctypedef np.complex128_t DTYPEc_t
+
+# Basically, you use the _t ones when you need to declare a type
+# (e.g. cdef foo_t var, or np.ndarray[foo_t, ndim=...]. Ideally someday
+# we won't have to make this distinction, but currently one is a C type
+# and the other is a python object representing a numpy type (a dtype),
+# and there's currently no way to identify the two without special
+# compiler support.
+# - Robert Bradshaw
+
+
+cdef extern from "complex.h":
+    np.complex128_t cexp(np.complex128_t z) nogil
+
+
+class ExactLinearCoefs(ExactLinearCoefsPurePython):
+    """Handle the computation of the exact coefficient for the RK4."""
+
+    def __init__(self, time_stepping):
+        super(ExactLinearCoefs, self).__init__(time_stepping)
+
+        ndim = self.freq_lin.ndim
+        dtype = self.freq_lin.dtype
+
+        if ndim == 2 and dtype == np.float64:
+            self.compute = self.compute_ndim2_float64
+        else:
+            raise NotImplementedError(
+                'ndim: {} ; dtype {}'.format(ndim, dtype))
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def compute_ndim2_float64(self, double dt):
+        cdef Py_ssize_t i0, i1, n0, n1
+        cdef np.ndarray[double, ndim=2] exact, exact2, f_lin
+
+        exact = self.exact
+        exact2 = self.exact2
+        f_lin = self.freq_lin
+        n0 = exact.shape[0]
+        n1 = exact.shape[1]
+
+        for i0 in xrange(n0):
+            for i1 in xrange(n1):
+                exact[i0, i1] = exp(-dt*f_lin[i0, i1])
+                exact2[i0, i1] = exp(-dt/2*f_lin[i0, i1])
+        self.dt_old = dt
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def compute_ndim3_complex128(self, double dt):
+        cdef Py_ssize_t i0, i1, ik, nk, n0, n1
+        cdef np.ndarray[DTYPEc_t, ndim=3] exact, exact2, f_lin
+
+        nk = self.nk
+        n0 = self.n0
+        n1 = self.n1
+        exact = self.exact
+        exact2 = self.exact2
+        f_lin = self.freq_lin
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    exact[ik, i0, i1] = cexp(-dt*f_lin[ik, i0, i1])
+                    exact2[ik, i0, i1] = cexp(-dt/2*f_lin[ik, i0, i1])
+
+        self.dt_old = dt
+
+
+class TimeSteppingPseudoSpectral(TimeSteppingPseudoSpectralPurePython):
+
+    def _init_time_scheme(self):
+
+        params_ts = self.params.time_stepping
+
+        if params_ts.type_time_scheme not in ['RK2', 'RK4']:
+            raise ValueError('Problem name time_scheme')
+
+        dtype = self.freq_lin.dtype
+        if dtype == np.float64:
+            str_type = 'float'
+        elif dtype == np.complex128:
+            str_type = 'complex'
+        else:
+            raise NotImplementedError('dtype of freq_lin:' + repr(dtype))
+
+        name_function = (
+            '_time_step_' + params_ts.type_time_scheme +
+            '_state_ndim{}_freqlin_ndim{}_'.format(
+                self.sim.state.state_fft.data.ndim, self.freq_lin.ndim) +
+            str_type)
+
+        if not hasattr(self, name_function):
+            raise NotImplementedError(
+                'The function ' + name_function +
+                ' is not implemented.')
+
+        exec('self._time_step_RK = self.' + name_function,
+             globals(), locals())
+
+
+    @cython.embedsignature(True)
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def _time_step_RK4_state_ndim3_freqlin_ndim2_float(self):
+        """Advance in time *sim.state.state_fft* with the Runge-Kutta 4 method.
+
+        See :ref:`the pure python RK4 function <rk4timescheme>` for the
+        presentation of the time scheme.
+
+        For this function, the coefficient :math:`\sigma` is real and
+        represents the dissipation.
+
+        """
+        # cdef DTYPEf_t dt = self.deltat
+        cdef double dt = self.deltat
+
+        cdef Py_ssize_t i0, i1, ik, nk, n0, n1
+
+        # cdef np.ndarray[DTYPEf_t, ndim=2] exact, exact2
+        # This is strange, if I use DTYPEf_t and complex.h => bug
+        cdef np.ndarray[double, ndim=2] exact, exact2
+
+        cdef np.ndarray[DTYPEc_t, ndim=3] datas, datat
+        cdef np.ndarray[DTYPEc_t, ndim=3] datatemp, datatemp2
+
+        tendencies_nonlin = self.sim.tendencies_nonlin
+        state_fft = self.sim.state.state_fft
+
+        nk = state_fft.data.shape[0]
+        n0 = state_fft.data.shape[1]
+        n1 = state_fft.data.shape[2]
+
+        exact, exact2 = self.exact_linear_coefs.get_updated_coefs()
+
+        tendencies_fft_1 = tendencies_nonlin()
+
+        # # alternativelly, this
+        # state_fft_temp = (self.state_fft + dt/6*tendencies_fft_1)*exact
+        # state_fft_np12_approx1 = (
+        #     self.state_fft + dt/2*tendencies_fft_1)*exact2
+        # # or this (slightly faster...)
+
+        datas = state_fft.data
+        datat = tendencies_fft_1.data
+
+        state_fft_temp = SetOfVariables(like_this_sov=state_fft)
+        datatemp = state_fft_temp.data
+
+        state_fft_np12_approx1 = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np12_approx1.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datas[ik, i0, i1] +
+                        dt/6*datat[ik, i0, i1])*exact[i0, i1]
+                    datatemp2[ik, i0, i1] = (
+                        datas[ik, i0, i1] +
+                        dt/2*datat[ik, i0, i1])*exact2[i0, i1]
+
+        del(tendencies_fft_1)
+        tendencies_fft_2 = tendencies_nonlin(state_fft_np12_approx1)
+        del(state_fft_np12_approx1)
+
+        # # alternativelly, this
+        # state_fft_temp += dt/3*exact2*tendencies_fft_2
+        # state_fft_np12_approx2 = (exact2*self.state_fft
+        #                           + dt/2*tendencies_fft_2)
+        # # or this (slightly faster...)
+
+        datat = tendencies_fft_2.data
+
+        state_fft_np12_approx2 = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np12_approx2.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/3*exact2[i0, i1]*datat[ik, i0, i1])
+                    datatemp2[ik, i0, i1] = (
+                        exact2[i0, i1]*datas[ik, i0, i1] +
+                        dt/2*datat[ik, i0, i1])
+
+        del(tendencies_fft_2)
+        tendencies_fft_3 = tendencies_nonlin(state_fft_np12_approx2)
+        del(state_fft_np12_approx2)
+
+        # # alternativelly, this
+        # state_fft_temp += dt/3*exact2*tendencies_fft_3
+        # state_fft_np1_approx = (exact*self.state_fft
+        #                         + dt*exact2*tendencies_fft_3)
+        # # or this (slightly faster...)
+
+        datat = tendencies_fft_3.data
+
+        state_fft_np1_approx = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np1_approx.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/3*exact2[i0, i1]*datat[ik, i0, i1])
+                    datatemp2[ik, i0, i1] = (
+                        exact[i0, i1]*datas[ik, i0, i1] +
+                        dt*exact2[i0, i1]*datat[ik, i0, i1])
+
+        del(tendencies_fft_3)
+        tendencies_fft_4 = tendencies_nonlin(state_fft_np1_approx)
+        del(state_fft_np1_approx)
+
+        # # alternativelly, this
+        # self.state_fft = state_fft_temp + dt/6*tendencies_fft_4
+        # # or this (slightly faster... may be not...)
+
+        datat = tendencies_fft_4.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datas[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/6*datat[ik, i0, i1])
+
+    @cython.embedsignature(True)
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def _time_step_RK4_state_ndim3_freqlin_ndim3_float(self):
+        """Advance in time *sim.state.state_fft* with the Runge-Kutta 4 method.
+
+        See :ref:`the pure python RK4 function <rk4timescheme>` for the
+        presentation of the time scheme.
+
+        For this function, the coefficient :math:`\sigma` is complex.
+
+        """
+        cdef double dt = self.deltat
+        cdef Py_ssize_t i0, i1, ik, nk, n0, n1
+        cdef np.ndarray[double, ndim=3] exact, exact2
+        cdef np.ndarray[DTYPEc_t, ndim=3] datas, datat
+        cdef np.ndarray[DTYPEc_t, ndim=3] datatemp, datatemp2
+
+        tendencies_nonlin = self.sim.tendencies_nonlin
+
+        state_fft = self.sim.state.state_fft
+        datas = state_fft.data
+        nk = datas.shape[0]
+        n0 = datas.shape[1]
+        n1 = datas.shape[2]
+
+        exact, exact2 = self.exact_linear_coefs.get_updated_coefs()
+
+        tendencies_fft_1 = tendencies_nonlin()
+
+        # # alternativelly, this
+        # state_fft_temp = (self.state_fft + dt/6*tendencies_fft_1)*exact
+        # state_fft_np12_approx1 = (self.state_fft
+        #                           + dt/2*tendencies_fft_1)*exact2
+        # # or this (slightly faster...)
+
+        datat = tendencies_fft_1.data
+
+        state_fft_temp = SetOfVariables(like_this_sov=state_fft)
+        datatemp = state_fft_temp.data
+
+        state_fft_np12_approx1 = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np12_approx1.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datas[ik, i0, i1] +
+                        dt/6*datat[ik, i0, i1])*exact[ik, i0, i1]
+                    datatemp2[ik, i0, i1] = (
+                        datas[ik, i0, i1] +
+                        dt/2*datat[ik, i0, i1])*exact2[ik, i0, i1]
+
+        del(tendencies_fft_1)
+        tendencies_fft_2 = tendencies_nonlin(state_fft_np12_approx1)
+        del(state_fft_np12_approx1)
+
+        # # alternativelly, this
+        # state_fft_temp += dt/3*exact2*tendencies_fft_2
+        # state_fft_np12_approx2 = (exact2*self.state_fft
+        #                           + dt/2*tendencies_fft_2)
+        # # or this (slightly faster...)
+
+        datat = tendencies_fft_2.data
+
+        state_fft_np12_approx2 = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np12_approx2.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/3*exact2[ik, i0, i1]*datat[ik, i0, i1])
+                    datatemp2[ik, i0, i1] = (
+                        exact2[ik, i0, i1]*datas[ik, i0, i1] +
+                        dt/2*datat[ik, i0, i1])
+
+        del(tendencies_fft_2)
+        tendencies_fft_3 = tendencies_nonlin(state_fft_np12_approx2)
+        del(state_fft_np12_approx2)
+
+        # # alternativelly, this
+        # state_fft_temp += dt/3*exact2*tendencies_fft_3
+        # state_fft_np1_approx = (exact*self.state_fft
+        #                         + dt*exact2*tendencies_fft_3)
+        # # or this (slightly faster...)
+
+        datat = tendencies_fft_3.data
+
+        state_fft_np1_approx = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np1_approx.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/3*exact2[ik, i0, i1]*datat[ik, i0, i1])
+                    datatemp2[ik, i0, i1] = (
+                        exact[ik, i0, i1]*datas[ik, i0, i1] +
+                        dt*exact2[ik, i0, i1]*datat[ik, i0, i1])
+
+        del(tendencies_fft_3)
+        tendencies_fft_4 = tendencies_nonlin(state_fft_np1_approx)
+        del(state_fft_np1_approx)
+
+        # # alternativelly, this
+        # self.state_fft = state_fft_temp + dt/6*tendencies_fft_4
+        # # or this (slightly faster... may be not...)
+
+        datat = tendencies_fft_4.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datas[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/6*datat[ik, i0, i1])
+
+
+
+    def _time_step_RK2_state_ndim3_freqlin_ndim3_complex(self):
+        raise NotImplementedError
+
+    @cython.embedsignature(True)
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def _time_step_RK4_state_ndim3_freqlin_ndim3_complex(self):
+        """Advance in time *sim.state.state_fft* with the Runge-Kutta 4 method.
+
+        See :ref:`the pure python RK4 function <rk4timescheme>` for the
+        presentation of the time scheme.
+
+        For this function, the coefficient :math:`\sigma` is complex.
+
+        """
+        cdef double dt = self.deltat
+        cdef Py_ssize_t i0, i1, ik, nk, n0, n1
+        cdef np.ndarray[DTYPEc_t, ndim=3] exact, exact2
+        cdef np.ndarray[DTYPEc_t, ndim=3] datas, datat
+        cdef np.ndarray[DTYPEc_t, ndim=3] datatemp, datatemp2
+
+        tendencies_nonlin = self.sim.tendencies_nonlin
+
+        state_fft = self.sim.state.state_fft
+        datas = state_fft.data
+        nk = datas.shape[0]
+        n0 = datas.shape[1]
+        n1 = datas.shape[2]
+
+        exact, exact2 = self.exact_linear_coefs.get_updated_coefs()
+
+        tendencies_fft_1 = tendencies_nonlin()
+
+        # # alternativelly, this
+        # state_fft_temp = (self.state_fft + dt/6*tendencies_fft_1)*exact
+        # state_fft_np12_approx1 = (self.state_fft
+        #                           + dt/2*tendencies_fft_1)*exact2
+        # # or this (slightly faster...)
+
+        datat = tendencies_fft_1.data
+
+        state_fft_temp = SetOfVariables(like_this_sov=state_fft)
+        datatemp = state_fft_temp.data
+
+        state_fft_np12_approx1 = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np12_approx1.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datas[ik, i0, i1] +
+                        dt/6*datat[ik, i0, i1])*exact[ik, i0, i1]
+                    datatemp2[ik, i0, i1] = (
+                        datas[ik, i0, i1] +
+                        dt/2*datat[ik, i0, i1])*exact2[ik, i0, i1]
+
+        del(tendencies_fft_1)
+        tendencies_fft_2 = tendencies_nonlin(state_fft_np12_approx1)
+        del(state_fft_np12_approx1)
+
+        # # alternativelly, this
+        # state_fft_temp += dt/3*exact2*tendencies_fft_2
+        # state_fft_np12_approx2 = (exact2*self.state_fft
+        #                           + dt/2*tendencies_fft_2)
+        # # or this (slightly faster...)
+
+        datat = tendencies_fft_2.data
+
+        state_fft_np12_approx2 = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np12_approx2.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/3*exact2[ik, i0, i1]*datat[ik, i0, i1])
+                    datatemp2[ik, i0, i1] = (
+                        exact2[ik, i0, i1]*datas[ik, i0, i1] +
+                        dt/2*datat[ik, i0, i1])
+
+        del(tendencies_fft_2)
+        tendencies_fft_3 = tendencies_nonlin(state_fft_np12_approx2)
+        del(state_fft_np12_approx2)
+
+        # # alternativelly, this
+        # state_fft_temp += dt/3*exact2*tendencies_fft_3
+        # state_fft_np1_approx = (exact*self.state_fft
+        #                         + dt*exact2*tendencies_fft_3)
+        # # or this (slightly faster...)
+
+        datat = tendencies_fft_3.data
+
+        state_fft_np1_approx = SetOfVariables(like_this_sov=state_fft)
+        datatemp2 = state_fft_np1_approx.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datatemp[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/3*exact2[ik, i0, i1]*datat[ik, i0, i1])
+                    datatemp2[ik, i0, i1] = (
+                        exact[ik, i0, i1]*datas[ik, i0, i1] +
+                        dt*exact2[ik, i0, i1]*datat[ik, i0, i1])
+
+        del(tendencies_fft_3)
+        tendencies_fft_4 = tendencies_nonlin(state_fft_np1_approx)
+        del(state_fft_np1_approx)
+
+        # # alternativelly, this
+        # self.state_fft = state_fft_temp + dt/6*tendencies_fft_4
+        # # or this (slightly faster... may be not...)
+
+        datat = tendencies_fft_4.data
+
+        for ik in xrange(nk):
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    datas[ik, i0, i1] = (
+                        datatemp[ik, i0, i1] +
+                        dt/6*datat[ik, i0, i1])
diff --git a/fluidsim/operators/CySources/operators_cy.pyx b/fluidsim/operators/CySources/operators_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL0N5U291cmNlcy9vcGVyYXRvcnNfY3kucHl4
--- /dev/null
+++ b/fluidsim/operators/CySources/operators_cy.pyx
@@ -0,0 +1,1606 @@
+"""
+Numerical operators (:mod:`fluidsim.operators.operators`)
+===============================================================
+
+.. currentmodule:: fluidsim.operators.operators
+
+This module is written in Cython and provides the classes:
+
+.. autoclass:: Operators
+   :members:
+   :private-members:
+
+.. autoclass:: GridPseudoSpectral2D
+   :members:
+   :private-members:
+
+.. autoclass:: OperatorsPseudoSpectral2D
+   :members:
+   :private-members:
+
+
+"""
+
+# DEF MPI4PY = 0
+
+import sys
+
+cimport numpy as np
+import numpy as np
+np.import_array()
+
+try:
+    from mpi4py import MPI
+except ImportError:
+    nb_proc = 1
+    rank = 0
+else:
+    comm = MPI.COMM_WORLD
+    nb_proc = comm.size
+    rank = comm.Get_rank()
+
+IF MPI4PY:
+    from mpi4py cimport MPI
+    from mpi4py.mpi_c cimport *
+
+    # solve an incompatibility between openmpi and mpi4py versions
+    cdef extern from 'mpi-compat.h': pass
+
+
+from time import time, sleep
+import datetime
+import os
+import matplotlib.pyplot as plt
+import cython
+
+from libc.math cimport exp
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.operators.fft import easypyfft
+
+# we define python and c types for physical and Fourier spaces
+DTYPEb = np.uint8
+ctypedef np.uint8_t DTYPEb_t
+DTYPEi = np.int
+ctypedef np.int_t DTYPEi_t
+DTYPEf = np.float64
+ctypedef np.float64_t DTYPEf_t
+DTYPEc = np.complex128
+ctypedef np.complex128_t DTYPEc_t
+
+# Basically, you use the _t ones when you need to declare a type
+# (e.g. cdef foo_t var, or np.ndarray[foo_t, ndim=...]. Ideally someday
+# we won't have to make this distinction, but currently one is a C type
+# and the other is a python object representing a numpy type (a dtype),
+# and there's currently no way to identify the two without special
+# compiler support.
+# - Robert Bradshaw
+
+
+cdef class Operators(object):
+    pass
+
+
+cdef class GridPseudoSpectral2D(Operators):
+    """Describes a discretisation in spectral and spatial space.
+
+    Parameters
+    ----------
+
+    nx, ny : int
+        Number of colocation points in the x and y directions
+
+    Lx, Lx : float
+        Dimension of the numerical box in the x and y directions
+
+    op_fft2d : :class:`FFT2Dmpi`
+        A instance of the class :class:`OP_FFT2Dmpi`
+
+    SEQUENCIAL : bool
+        If True, the fft is sequencial even though ``nb_proc > 1``
+
+    """
+    # number of nodes, sequenciel case
+    cdef public int nx_seq, ny_seq, nkx_seq, nky_seq
+    # number of nodes locally stored
+    cdef public int nx_loc, ny_loc, nkx_loc, nky_loc
+    cdef public DTYPEf_t Lx, Ly, deltax, deltay,
+    cdef public DTYPEf_t deltakx, deltaky, deltakh, kmax, kymax
+
+    # shape of the arrays in the physical and Fourier spaces,
+    # for the sequential case:
+    cdef public np.ndarray shapeX_seq,  shapeK_seq
+    # and for the parallel case:
+    cdef public np.ndarray shapeX_loc,  shapeK_loc
+    # the names without loc or seq correspond to local (or general) quantities
+    cdef public np.ndarray shapeX,  shapeK
+    # shape K when gathered:
+    cdef public np.ndarray shapeK_gat
+
+    cdef public int idimx, idimy, idimkx, idimky
+
+    # these names without loc or seq correspond to local quantities
+    cdef public np.ndarray XX, YY, RR, KX, KY, KK, K2, K4, K8
+    cdef public np.ndarray kx_loc, ky_loc
+    cdef public np.ndarray x_seq, y_seq
+
+    # the communicator, nb of processus and rank of the processus
+    IF MPI4PY:
+        cdef public MPI.Comm comm
+    cdef public int nb_proc, rank
+    cdef public int iX0loc_start, iKxloc_start, iKyloc_start
+    cdef public int nK0_loc, nK1_loc, dim_kx, dim_ky
+    cdef public np.ndarray iKxloc_start_rank
+
+    cdef public DTYPEb_t TRANSPOSED, SEQUENCIAL
+    cdef public DTYPEb_t SAME_SIZE_IN_ALL_PROC
+
+    # cdef public object where_is_wavenumber
+
+    def __init__(self, int nx, int ny,
+                 DTYPEf_t Lx=2*np.pi, DTYPEf_t Ly=2*np.pi,
+                 op_fft2d=None, SEQUENCIAL=None):
+        if ny % 2 != 0 or nx % 2 != 0:
+            raise ValueError('conditions n0 and n1 even not fulfill')
+
+        # n0 is ny and n1 is ny (see def of n0 and n1 in the fftw doc)
+        self.nx_seq = int(nx)
+        self.ny_seq = int(ny)
+
+        self.Lx = np.float(Lx)
+        self.Ly = np.float(Ly)
+
+        self.deltax = self.Lx/self.nx_seq
+        self.deltay = self.Ly/self.ny_seq
+
+        self.x_seq = self.deltax * np.arange(self.nx_seq)
+        self.y_seq = self.deltay * np.arange(self.ny_seq)
+
+        self.deltakx = 2*np.pi/self.Lx
+        self.deltaky = 2*np.pi/self.Ly
+        self.deltakh = self.deltakx
+
+        self.nkx_seq = int(self.nx_seq/2.+1)
+        self.nky_seq = self.ny_seq
+
+        self.kymax = self.deltaky*self.nky_seq/2.
+
+        # info on MPI
+        self.nb_proc = nb_proc
+        self.rank = rank
+        if self.nb_proc > 1:
+            self.comm = comm
+
+        self.shapeX_seq = np.array([self.ny_seq, self.nx_seq])
+        self.shapeK_seq = np.array([self.nky_seq, self.nkx_seq])
+
+        if self.nb_proc == 1 or SEQUENCIAL:
+            self.SEQUENCIAL = True
+            self.SAME_SIZE_IN_ALL_PROC = True
+            self.shapeX = self.shapeX_seq
+            self.shapeK = self.shapeK_seq
+            self.shapeX_loc = self.shapeX_seq
+            self.shapeK_loc = self.shapeK_seq
+            self.shapeK_gat = self.shapeK_seq
+
+            self.iX0loc_start = 0
+            self.iKxloc_start = 0
+            self.iKyloc_start = 0
+
+            self.nx_loc = self.nx_seq
+            self.ny_loc = self.ny_seq
+            self.nkx_loc = self.nkx_seq
+            self.nky_loc = self.nky_seq
+
+            self.idimx = 1
+            self.idimy = 0
+            self.idimkx = 1
+            self.idimky = 0
+
+            self.TRANSPOSED = False
+
+        else:
+
+            if nx/2+1 < self.nb_proc:
+                raise ValueError('condition nx/2+1 >= nb_proc not fulfill')
+
+            self.SEQUENCIAL = False
+            if op_fft2d is None:
+                raise ValueError(
+                    'for parallel grid, init() needs a op_fft2d object')
+            if not hasattr(op_fft2d, 'shapeX_loc'):
+                raise ValueError(
+                    'The fft operator does not have "shapeX_loc", '
+                    'which seems to indicate that it can not run with mpi.')    
+            self.shapeK_gat = op_fft2d.shapeK_gat
+            self.shapeX_loc = op_fft2d.shapeX_loc
+            self.shapeK_loc = op_fft2d.shapeK_loc
+            self.shapeX = op_fft2d.shapeX_loc
+            self.shapeK = op_fft2d.shapeK_loc
+
+            self.idimkx = op_fft2d.idimkx
+            self.idimky = op_fft2d.idimky
+            self.idimx = op_fft2d.idimx
+            self.idimy = op_fft2d.idimy
+
+            self.nx_loc = self.shapeX_loc[self.idimx]
+            self.ny_loc = self.shapeX_loc[self.idimy]
+            self.nkx_loc = self.shapeK_loc[self.idimkx]
+            self.nky_loc = self.shapeK_loc[self.idimky]
+            self.iX0loc_start = op_fft2d.iX0loc_start
+
+            self.iKxloc_start = op_fft2d.iKxloc_start
+            self.iKyloc_start = op_fft2d.iKyloc_start
+
+            self.iKxloc_start_rank = np.array(
+                comm.allgather(self.iKxloc_start))
+
+            nkx_loc_rank = np.array(comm.allgather(self.nkx_loc))
+            a = nkx_loc_rank
+            self.SAME_SIZE_IN_ALL_PROC = (a >= a.max()).all()
+
+            self.TRANSPOSED = op_fft2d.TRANSPOSED
+
+        self.nK0_loc = self.shapeK_loc[0]
+        self.nK1_loc = self.shapeK_loc[1]
+
+        x_loc = self.deltax * np.arange(self.nx_loc)
+        y_loc = (self.deltay *
+                 np.arange(self.iX0loc_start, self.iX0loc_start+self.ny_loc))
+        [self.XX, self.YY] = np.meshgrid(x_loc, y_loc)
+        self.RR = np.sqrt((self.XX-self.Lx/2)**2 + (self.YY-self.Ly/2)**2)
+
+        self.kx_loc = self.deltakx * np.arange(self.iKxloc_start,
+                                               self.iKxloc_start+self.nkx_loc)
+        self.ky_loc = self.deltaky * np.arange(self.iKyloc_start,
+                                               self.iKyloc_start+self.nky_loc)
+        self.ky_loc[self.ky_loc > self.kymax] = (
+            self.ky_loc[self.ky_loc > self.kymax]
+            - 2*self.kymax)
+
+        if not self.TRANSPOSED:
+            [self.KX, self.KY] = np.meshgrid(self.kx_loc, self.ky_loc)
+            self.dim_kx = 1
+            self.dim_ky = 0
+        else:
+            [self.KY, self.KX] = np.meshgrid(self.ky_loc, self.kx_loc)
+            self.dim_kx = 0
+            self.dim_ky = 1
+
+        self.K2 = self.KX**2 + self.KY**2
+        self.K4 = self.K2**2
+        self.K8 = self.K4**2
+        self.KK = np.sqrt(self.K2)
+
+        self.kmax = np.sqrt((self.deltakx*self.nx_seq)**2
+                            + (self.deltaky*self.ny_seq)**2)/2
+
+    def where_is_wavenumber(self, kx_approx, ky_approx):
+        ikx_seq = np.round(kx_approx/self.deltakh)
+
+        if ikx_seq >= self.nkx_seq:
+            raise ValueError('not good :-) ikx_seq >= self.nkx_seq')
+
+        if self.SEQUENCIAL:
+            rank_k = 0
+            ikx_loc = ikx_seq
+        else:
+            if self.SAME_SIZE_IN_ALL_PROC:
+                rank_k = int(np.floor(float(ikx_seq)/self.nkx_loc))
+            else:
+                rank_k = 0
+                while (rank_k < self.nb_proc-1 and
+                       (not (self.iKxloc_start_rank[rank_k] <= ikx_seq
+                             and ikx_seq < self.iKxloc_start_rank[rank_k+1]))):
+                    rank_k += 1
+
+            ikx_loc = ikx_seq - self.iKxloc_start_rank[rank_k]
+
+        iky_loc = np.round(ky_approx/self.deltaky)
+        if iky_loc < 0:
+            iky_loc = self.nky_loc+iky_loc
+
+        if self.TRANSPOSED:
+            ik0_loc = ikx_loc
+            ik1_loc = iky_loc
+        else:
+            ik0_loc = iky_loc
+            ik1_loc = ikx_loc
+
+        return rank_k, ik0_loc, ik1_loc
+
+
+cdef class OperatorsPseudoSpectral2D(GridPseudoSpectral2D):
+    """Provides fast Fourier transform functions and 2D operators.
+
+    `type_fft='FFTWCY'` :
+    cython wrapper of plans
+    fftw_plan_dft_r2c_2d / fftw_plan_dft_c2r_2d (sequencial case)
+    and
+    fftw_mpi_plan_dft_r2c_2d / fftw_mpi_plan_dft_c2r_2d
+    (parallel case)
+
+    `type_fft='FFTWCCY'` :
+    cython wrapper of a self-written c libray using
+    sequencial fftw plans and MPI_Type. Seems to be faster than
+    the implementation of the mpi FFT by fftw (lib fftw-mpi).
+
+    `type_fft='FFTWPY'` :
+    use of the module :mod:`easypyfft2D` with fftw
+
+    `type_fft='FFTPACK'
+    use of the module :mod:`easypyfft2D` with fftp
+    (bad and slow implementation!)
+
+    """
+
+    cdef public DTYPEf_t coef_dealiasing
+    cdef public object fft2, ifft2
+    cdef public object gather_Xspace,  gather_Kspace,
+    cdef public object scatter_Xspace, scatter_Kspace
+    cdef public object project_fft_on_realX
+    cdef public object params
+    cdef public np.ndarray K2_not0, K4_not0, KX_over_K2, KY_over_K2
+    cdef public np.ndarray Kappa2, Kappa_over_ic, f_over_c2Kappa2
+    cdef public np.ndarray where_dealiased
+
+    cdef public int nkxE, nkyE, nkhE
+    cdef public np.ndarray kxE, kyE, khE
+
+    cdef public str type_fft
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        if nb_proc > 1:
+            type_fft = 'FFTWCCY'
+        else:
+            if not sys.platform == 'win32':
+                type_fft = 'FFTWCY'
+            else:
+                type_fft = 'FFTWPY'
+
+        attribs = {'type_fft': type_fft,
+                   'TRANSPOSED_OK': True,
+                   'coef_dealiasing': 2./3,
+                   'nx': 48,
+                   'ny': 48,
+                   'Lx': 8,
+                   'Ly': 8}
+        params.set_child('oper', attribs=attribs)
+
+    def __init__(self,
+                 SEQUENCIAL=None,
+                 params=None,
+                 goal_to_print=None):
+
+        if not params.ONLY_COARSE_OPER:
+            nx = int(params.oper.nx)
+            ny = int(params.oper.ny)
+        else:
+            nx = 4
+            ny = 4
+
+        Lx = params.oper.Lx
+        Ly = params.oper.Ly
+        type_fft = str(params.oper.type_fft)
+        coef_dealiasing = params.oper.coef_dealiasing
+        TRANSPOSED = params.oper.TRANSPOSED_OK
+
+        if rank == 0:
+            to_print = 'Init. operator'
+            if goal_to_print is not None:
+                to_print += ' ('+goal_to_print+')'
+            print(to_print)
+
+        if params is not None:
+            self.params = params
+
+        list_type_fft = ['FFTWCY', 'FFTWCCY', 'FFTWPY', 'FFTPACK']
+        if type_fft not in list_type_fft:
+            raise ValueError('type_fft should be in '+repr(list_type_fft))
+
+        if type_fft == 'FFTWCCY' and nb_proc == 1:
+            type_fft = 'FFTWCY'
+
+        try:
+            if type_fft == 'FFTWCY':
+                import fluidsim.operators.fft.fftw2dmpicy as fftw2Dmpi
+            elif type_fft == 'FFTWCCY':
+                import fluidsim.operators.fft.fftw2dmpiccy as fftw2Dmpi
+        except ImportError as err:
+            print('ImportError for fftw2Dmpicy and fftw2Dmpiccy')
+            type_fft = 'FFTWPY'
+            if nb_proc > 1 and SEQUENCIAL is None:
+                raise ValueError(
+                    'if nb_proc>1, we need to use one of this library')
+        if type_fft == 'FFTWPY':
+            try:
+                import pyfftw
+            except ImportError as err:
+                print('ImportError for fftw3, we use fftpack (very slow)')
+                type_fft = 'FFTPACK'
+
+        self.type_fft = type_fft
+
+        # Initialization of the fft transforms
+        if not (type_fft == 'FFTWPY' or type_fft == 'FFTPACK'):
+            if not TRANSPOSED and type_fft == 'FFTWCCY':
+                raise ValueError('FFTWCCY does not suport the '
+                                 '(inefficient!) option TRANSPOSED=False')
+
+            if type_fft == 'FFTWCY':
+                op_fft2d = fftw2Dmpi.FFT2Dmpi(ny, nx,
+                                              TRANSPOSED=TRANSPOSED,
+                                              SEQUENCIAL=SEQUENCIAL)
+            else:
+                op_fft2d = fftw2Dmpi.FFT2Dmpi(ny, nx)
+            if op_fft2d.nb_proc > 1:
+                self.gather_Xspace = op_fft2d.gather_Xspace
+                self.gather_Kspace = op_fft2d.gather_Kspace
+                self.scatter_Xspace = op_fft2d.scatter_Xspace
+                self.scatter_Kspace = op_fft2d.scatter_Kspace
+
+        elif type_fft == 'FFTWPY':
+            op_fft2d = easypyfft.FFTW2DReal2Complex(nx, ny)
+        elif type_fft == 'FFTPACK':
+            op_fft2d = easypyfft.fftp2D(nx, ny)
+
+        self.fft2 = op_fft2d.fft2d
+        self.ifft2 = op_fft2d.ifft2d
+
+        GridPseudoSpectral2D.__init__(self, nx, ny, Lx, Ly,
+                                      op_fft2d=op_fft2d, SEQUENCIAL=SEQUENCIAL)
+
+        self.K2_not0 = self.K2.copy()
+        self.K4_not0 = self.K4.copy()
+        if rank == 0 or SEQUENCIAL:
+            self.K2_not0[0, 0] = 10.e-10
+            self.K4_not0[0, 0] = 10.e-10
+
+        self.KX_over_K2 = self.KX/self.K2_not0
+        self.KY_over_K2 = self.KY/self.K2_not0
+
+        try:
+            self.Kappa2 = self.K2 + self.params.kd2
+
+            self.Kappa_over_ic = -1.j*np.sqrt(
+                self.Kappa2/self.params.c2
+                )
+
+            if self.params.f != 0:
+                self.f_over_c2Kappa2 = self.params.f/(
+                    self.params.c2*self.Kappa2
+                    )
+
+        except AttributeError:
+            pass
+
+        # for spectra, we forget the larger wavenumber,
+        # since there is no energy inside because of dealiasing
+        self.nkxE = self.nkx_seq - 1
+        self.nkyE = self.nky_seq/2
+
+        self.kxE = self.deltakx * np.arange(self.nkxE)
+        self.kyE = self.deltaky * np.arange(self.nkyE)
+        self.khE = self.kxE
+        self.nkhE = self.nkxE
+
+        # Initialisation dealiasing
+        self.coef_dealiasing = coef_dealiasing
+        CONDKX = abs(self.KX) > self.coef_dealiasing*self.kxE.max()
+        CONDKY = abs(self.KY) > self.coef_dealiasing*self.kyE.max()
+        where_dealiased = np.logical_or(CONDKX, CONDKY)
+
+        self.where_dealiased = np.array(where_dealiased, dtype=DTYPEb)
+
+        try:
+            self.project_fft_on_realX = op_fft2d.project_fft_on_realX
+        except KeyError:
+            if nb_proc > 1:
+                raise ValueError(
+                    'nb_proc > 1 but no function'
+                    'project_fft_on_realX defined')
+            self.project_fft_on_realX = self.project_fft_on_realX_seq
+
+    def produce_str_describing_oper(self):
+        """Produce a string describing the operator."""
+        if (self.Lx/np.pi).is_integer():
+            str_Lx = repr(int(self.Lx/np.pi)) + 'pi'
+        else:
+            str_Lx = '{:.3f}'.format(self.Lx).rstrip('0')
+        if (self.Ly/np.pi).is_integer():
+            str_Ly = repr(int(self.Ly/np.pi)) + 'pi'
+        else:
+            str_Ly = '{:.3f}'.format(self.Ly).rstrip('0')
+        return ('L='+str_Lx+'x'+str_Ly+'_{}x{}').format(
+            self.nx_seq, self.ny_seq)
+
+    def produce_long_str_describing_oper(self):
+        """Produce a string describing the operator."""
+        if (self.Lx/np.pi).is_integer():
+            str_Lx = repr(int(self.Lx/np.pi)) + '*pi'
+        else:
+            str_Lx = '{:.3f}'.format(self.Lx).rstrip('0')
+        if (self.Ly/np.pi).is_integer():
+            str_Ly = repr(int(self.Ly/np.pi)) + '*pi'
+        else:
+            str_Ly = '{:.3f}'.format(self.Ly).rstrip('0')
+        return (
+            'nx = {0:6d} ; ny = {1:6d}\n'.format(self.nx_seq, self.ny_seq) +
+            'Lx = ' + str_Lx + ' ; Ly = ' + str_Ly + '\n')
+
+    # def rotfft_from_vecfft(self, vecx_fft, vecy_fft):
+    #     """Return the rotational (curl) of a vector in spectral space."""
+    #     return 1j*( self.KX*vecy_fft - self.KY*vecx_fft )
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def rotfft_from_vecfft(self,
+                           np.ndarray[DTYPEc_t, ndim=2] vecx_fft,
+                           np.ndarray[DTYPEc_t, ndim=2] vecy_fft):
+        """Return the rotational of a vector in spectral space."""
+        cdef Py_ssize_t i0, i1, n0, n1
+        cdef np.ndarray[DTYPEc_t, ndim=2] rot_fft
+        cdef np.ndarray[DTYPEf_t, ndim=2] KX, KY
+
+        n0 = self.nK0_loc
+        n1 = self.nK1_loc
+
+        KX = self.KX
+        KY = self.KY
+        rot_fft = np.empty([n0, n1], dtype=np.complex128)
+
+        for i0 in range(n0):
+            for i1 in range(n1):
+                rot_fft[i0, i1] = 1j*(KX[i0, i1]*vecy_fft[i0, i1]
+                                      - KY[i0, i1]*vecx_fft[i0, i1])
+        return rot_fft
+
+    # def divfft_from_vecfft_old(self, vecx_fft, vecy_fft):
+    #     """Return the divergence of a vector in spectral space."""
+    #     return 1j*( self.KX*vecx_fft + self.KY*vecy_fft )
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def divfft_from_vecfft(self,
+                           np.ndarray[DTYPEc_t, ndim=2] vecx_fft,
+                           np.ndarray[DTYPEc_t, ndim=2] vecy_fft):
+        """Return the divergence of a vector in spectral space."""
+        cdef Py_ssize_t i0, i1, n0, n1
+        cdef np.ndarray[DTYPEc_t, ndim=2] div_fft
+        cdef np.ndarray[DTYPEf_t, ndim=2] KX, KY
+
+        n0 = self.nK0_loc
+        n1 = self.nK1_loc
+
+        KX = self.KX
+        KY = self.KY
+        div_fft = np.empty([n0, n1], dtype=np.complex128)
+
+        for i0 in xrange(n0):
+            for i1 in xrange(n1):
+                div_fft[i0, i1] = 1j*(KX[i0, i1]*vecx_fft[i0, i1]
+                                      + KY[i0, i1]*vecy_fft[i0, i1]
+                                      )
+        return div_fft
+
+    def vecfft_from_rotfft(self, rot_fft):
+        """Return the velocity in spectral space computed from the
+        rotational."""
+        ux_fft = 1j * self.KY_over_K2*rot_fft
+        uy_fft = -1j * self.KX_over_K2*rot_fft
+        return ux_fft, uy_fft
+
+    def vecfft_from_divfft(self, div_fft):
+        """Return the velocity in spectral space computed from the
+        divergence."""
+        ux_fft = -1j * self.KX_over_K2*div_fft
+        uy_fft = -1j * self.KY_over_K2*div_fft
+        return ux_fft, uy_fft
+
+
+
+
+    # def gradfft_from_fft_old(self, f_fft):
+    #     """Return the gradient of f_fft in spectral space."""
+    #     px_f_fft = 1j * self.KX*f_fft
+    #     py_f_fft = 1j * self.KY*f_fft
+    #     return px_f_fft, py_f_fft
+
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def pxffft_from_fft(self, f_fft):
+        """Return the gradient of f_fft in spectral space."""
+        cdef Py_ssize_t i0, i1, n0, n1
+        cdef np.ndarray[DTYPEf_t, ndim=2] KX
+        cdef np.ndarray[DTYPEc_t, ndim=2] px_f_fft
+
+        cdef np.ndarray[DTYPEc_t, ndim=2] fc_fft
+        cdef np.ndarray[DTYPEf_t, ndim=2] ff_fft
+
+        n0 = self.nK0_loc
+        n1 = self.nK1_loc
+
+        KX = self.KX
+        KY = self.KY
+
+        px_f_fft = np.empty([n0, n1], dtype=np.complex128)
+
+        if f_fft.dtype == np.float64:
+            ff_fft = f_fft
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    px_f_fft[i0, i1] = 1j * KX[i0, i1]*ff_fft[i0, i1]
+        else:
+            fc_fft = f_fft
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    px_f_fft[i0, i1] = 1j * KX[i0, i1]*fc_fft[i0, i1]
+
+        return px_f_fft
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def gradfft_from_fft(self, f_fft):
+        """Return the gradient of f_fft in spectral space."""
+        cdef Py_ssize_t i0, i1, n0, n1
+        cdef np.ndarray[DTYPEf_t, ndim=2] KX, KY
+        cdef np.ndarray[DTYPEc_t, ndim=2] px_f_fft, py_f_fft
+
+        cdef np.ndarray[DTYPEc_t, ndim=2] fc_fft
+        cdef np.ndarray[DTYPEf_t, ndim=2] ff_fft
+
+        n0 = self.nK0_loc
+        n1 = self.nK1_loc
+
+        KX = self.KX
+        KY = self.KY
+
+        px_f_fft = np.empty([n0, n1], dtype=np.complex128)
+        py_f_fft = np.empty([n0, n1], dtype=np.complex128)
+
+        if f_fft.dtype == np.float64:
+            ff_fft = f_fft
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    px_f_fft[i0, i1] = 1j * KX[i0, i1]*ff_fft[i0, i1]
+                    py_f_fft[i0, i1] = 1j * KY[i0, i1]*ff_fft[i0, i1]
+        else:
+            fc_fft = f_fft
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    px_f_fft[i0, i1] = 1j * KX[i0, i1]*fc_fft[i0, i1]
+                    py_f_fft[i0, i1] = 1j * KY[i0, i1]*fc_fft[i0, i1]
+
+        return px_f_fft, py_f_fft
+
+    def projection_perp(self, fx_fft, fy_fft):
+        KX = self.KX
+        KY = self.KY
+        a = fx_fft - self.KX_over_K2*(KX*fx_fft+KY*fy_fft)
+        b = fy_fft - self.KY_over_K2*(KX*fx_fft+KY*fy_fft)
+        fx_fft[:] = a
+        fy_fft[:] = b
+        return a, b
+
+    def uxuyfft_from_psifft(self, psi_fft):
+        px_psi_fft, py_psi_fft = self.gradfft_from_fft(psi_fft)
+        ux_fft = -py_psi_fft
+        uy_fft = px_psi_fft
+        return ux_fft, uy_fft
+
+    def rotfft_from_psifft(self, psi_fft):
+        rot_fft = -self.K2*psi_fft
+        return rot_fft
+
+    def uxuyetafft_from_qfft(self, q_fft, params=None):
+        """Compute ux, uy and eta in Fourier space."""
+        if params is None:
+            params = self.params
+        K2 = self.K2
+        K2_not0 = self.K2_not0
+        rot_fft = K2*q_fft/(K2_not0+params.kd2)
+        if rank == 0:
+            rot_fft[0, 0] = 0.
+        ux_fft, uy_fft = self.vecfft_from_rotfft(rot_fft)
+
+        if params.f == 0:
+            eta_fft = self.constant_arrayK(value=0)
+        else:
+            eta_fft = -params.f*q_fft/(K2_not0+params.kd2)/params.c2
+        if rank == 0:
+            eta_fft[0, 0] = 0.
+        return ux_fft, uy_fft, eta_fft
+
+    def uxuyetafft_from_afft(self, a_fft, params=None):
+        """Compute ux, uy and eta in Fourier space."""
+        if params is None:
+            params = self.params
+        K2 = self.K2
+        K2_not0 = self.K2_not0
+
+        if params.f == 0:
+            rot_fft = self.constant_arrayK(value=0)
+        else:
+            rot_fft = params.f*a_fft/(K2_not0+params.kd2)
+        if rank == 0:
+            rot_fft[0, 0] = 0.
+        ux_fft, uy_fft = self.vecfft_from_rotfft(rot_fft)
+
+        eta_fft = a_fft/(K2_not0+params.kd2)
+        if rank == 0:
+            eta_fft[0, 0] = 0.
+        return ux_fft, uy_fft, eta_fft
+
+    def rotfft_from_qfft(self, q_fft, params=None):
+        """Compute ux, uy and eta in Fourier space."""
+        if params is None:
+            params = self.params
+        K2 = self.K2
+        K2_not0 = self.K2_not0
+        rot_fft = K2*q_fft/(K2_not0+params.kd2)
+        if rank == 0:
+            rot_fft[0, 0] = 0.
+        return rot_fft
+
+    def rotfft_from_afft(self, a_fft, params=None):
+        """Compute ux, uy and eta in Fourier space."""
+        if params is None:
+            params = self.params
+        K2 = self.K2
+        K2_not0 = self.K2_not0
+        if params.f == 0:
+            rot_fft = self.constant_arrayK(value=0)
+        else:
+            rot_fft = params.f*a_fft/(K2_not0+params.kd2)
+        if rank == 0:
+            rot_fft[0, 0] = 0.
+        return rot_fft
+
+    def afft_from_uxuyetafft(self, ux_fft, uy_fft, eta_fft,
+                             params=None):
+        if params is None:
+            params = self.params
+        rot_fft = self.rotfft_from_vecfft(ux_fft, uy_fft)
+        a_fft = self.K2*eta_fft
+        if params.f != 0:
+            a_fft += params.f/params.c2*rot_fft
+        return a_fft
+
+    def etafft_from_qfft(self, q_fft, params=None):
+        """Compute eta in Fourier space."""
+        if params is None:
+            params = self.params
+        K2_not0 = self.K2_not0
+        if params.f == 0:
+            eta_fft = self.constant_arrayK(value=0)
+        else:
+            eta_fft = -params.f/params.c2*q_fft/(K2_not0+params.kd2)
+        if rank == 0:
+            eta_fft[0, 0] = 0.
+        return eta_fft
+
+    def etafft_from_afft(self, a_fft, params=None):
+        """Compute eta in Fourier space."""
+        if params is None:
+            params = self.params
+        K2_not0 = self.K2_not0
+        eta_fft = a_fft/(K2_not0+params.kd2)
+        if rank == 0:
+            eta_fft[0, 0] = 0.
+        return eta_fft
+
+    def etafft_from_aqfft(self, a_fft, q_fft, params=None):
+        """Compute eta in Fourier space."""
+        if params is None:
+            params = self.params
+        K2_not0 = self.K2_not0
+        if params.f == 0:
+            eta_fft = a_fft/K2_not0
+        else:
+            eta_fft = (a_fft - params.f/params.c2*q_fft)/(
+                K2_not0+params.kd2)
+        if rank == 0:
+            eta_fft[0, 0] = 0.
+        return eta_fft
+
+    def qdafft_from_uxuyetafft(self, ux_fft, uy_fft, eta_fft, params=None):
+        if params is None:
+            params = self.params
+        div_fft = self.divfft_from_vecfft(ux_fft, uy_fft)
+        rot_fft = self.rotfft_from_vecfft(ux_fft, uy_fft)
+        q_fft = rot_fft - params.f*eta_fft
+        ageo_fft = params.f/params.c2*rot_fft + self.K2*eta_fft
+        return q_fft, div_fft, ageo_fft
+
+    def apamfft_from_adfft(self, a_fft, d_fft):
+        """Return the engein modes ap and am."""
+        Delta_a_fft = self.Kappa_over_ic*d_fft
+        ap_fft = 0.5*(a_fft + Delta_a_fft)
+        am_fft = 0.5*(a_fft - Delta_a_fft)
+        return ap_fft, am_fft
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def divfft_from_apamfft(self, ap_fft, am_fft):
+        """Return div from the engein modes ap and am."""
+        cdef Py_ssize_t i0, i1, n0, n1
+        cdef Py_ssize_t rank = self.rank
+        cdef np.ndarray[DTYPEc_t, ndim=2] Kappa_over_ic, Delta_a_fft
+        cdef np.ndarray[DTYPEc_t, ndim=2] d_fft
+
+        Delta_a_fft = ap_fft - am_fft
+        n0 = self.nK0_loc
+        n1 = self.nK1_loc
+        Kappa_over_ic = self.Kappa_over_ic
+        d_fft = np.empty([n0, n1], dtype=np.complex128)
+
+        for i0 in range(n0):
+            for i1 in range(n1):
+                if i0 == 0 and i1 == 0 and rank == 0:
+                    d_fft[i0, i1] = 0.
+                else:
+                    d_fft[i0, i1] = (
+                        Delta_a_fft[i0, i1]/Kappa_over_ic[i0, i1]
+                        )
+        return d_fft
+
+    def qapamfft_from_uxuyetafft_old(self, ux_fft, uy_fft, eta_fft,
+                                     params=None):
+        """ux, uy, eta (fft) ---> q, ap, am (fft)"""
+        if params is None:
+            params = self.params
+        div_fft = self.divfft_from_vecfft(ux_fft, uy_fft)
+        rot_fft = self.rotfft_from_vecfft(ux_fft, uy_fft)
+        q_fft = rot_fft - params.f*eta_fft
+        a_fft = (self.K2*eta_fft
+                 + params.f/params.c2*rot_fft)
+        ap_fft, am_fft = self.apamfft_from_adfft(a_fft, div_fft)
+        if rank == 0:
+            ap_fft[0, 0] = ux_fft[0, 0] + 1.j*uy_fft[0, 0]
+            am_fft[0, 0] = ux_fft[0, 0] - 1.j*uy_fft[0, 0]
+        return q_fft, ap_fft, am_fft
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def qapamfft_from_uxuyetafft(self,
+                                 np.ndarray[DTYPEc_t, ndim=2] ux_fft,
+                                 np.ndarray[DTYPEc_t, ndim=2] uy_fft,
+                                 np.ndarray[DTYPEc_t, ndim=2] eta_fft,
+                                 params=None):
+        """ux, uy, eta (fft) ---> q, ap, am (fft)"""
+        cdef Py_ssize_t i0, i1, n0, n1
+        cdef Py_ssize_t rank = self.rank
+        cdef np.ndarray[DTYPEf_t, ndim=2] KX, KY, K2
+        cdef np.ndarray[DTYPEc_t, ndim=2] Kappa_over_ic
+        cdef np.ndarray[DTYPEc_t, ndim=2] q_fft, ap_fft, am_fft
+        cdef DTYPEc_t rot_fft, a_over2_fft, Deltaa_over2_fft
+        cdef DTYPEf_t freq_Corio, f_over_c2
+
+        if params is None:
+            params = self.params
+
+        n0 = self.nK0_loc
+        n1 = self.nK1_loc
+
+        KX = self.KX
+        KY = self.KY
+        K2 = self.K2
+        Kappa_over_ic = self.Kappa_over_ic
+        KX_over_K2 = self.KX_over_K2
+        KY_over_K2 = self.KY_over_K2
+
+        q_fft = np.empty([n0, n1], dtype=np.complex128)
+        ap_fft = np.empty([n0, n1], dtype=np.complex128)
+        am_fft = np.empty([n0, n1], dtype=np.complex128)
+
+        freq_Corio = params.f
+        f_over_c2 = freq_Corio/params.c2
+
+        if freq_Corio != 0:
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    if i0 == 0 and i1 == 0 and rank == 0:
+                        q_fft[i0, i1] = 0
+                        ap_fft[i0, i1] = ux_fft[0, 0] + 1.j*uy_fft[0, 0]
+                        am_fft[i0, i1] = ux_fft[0, 0] - 1.j*uy_fft[0, 0]
+                    else:
+
+                        rot_fft = 1j*(
+                            KX[i0, i1]*uy_fft[i0, i1] -
+                            KY[i0, i1]*ux_fft[i0, i1])
+
+                        q_fft[i0, i1] = rot_fft - freq_Corio*eta_fft[i0, i1]
+
+                        a_over2_fft = 0.5*(
+                            K2[i0, i1] * eta_fft[i0, i1] +
+                            f_over_c2*rot_fft)
+
+                        Deltaa_over2_fft = 0.5j*Kappa_over_ic[i0, i1]*(
+                            KX[i0, i1]*ux_fft[i0, i1] +
+                            KY[i0, i1]*uy_fft[i0, i1])
+
+                        ap_fft[i0, i1] = a_over2_fft + Deltaa_over2_fft
+                        am_fft[i0, i1] = a_over2_fft - Deltaa_over2_fft
+
+        else:  # (freq_Corio == 0.)
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    if i0 == 0 and i1 == 0 and rank == 0:
+                        q_fft[i0, i1] = 0
+                        ap_fft[i0, i1] = ux_fft[0, 0] + 1.j*uy_fft[0, 0]
+                        am_fft[i0, i1] = ux_fft[0, 0] - 1.j*uy_fft[0, 0]
+                    else:
+                        q_fft[i0, i1] = 1j*(
+                            KX[i0, i1]*uy_fft[i0, i1]
+                            - KY[i0, i1]*ux_fft[i0, i1])
+
+                        a_over2_fft = 0.5*K2[i0, i1]*eta_fft[i0, i1]
+
+                        Deltaa_over2_fft = 0.5j*Kappa_over_ic[i0, i1]*(
+                            KX[i0, i1]*ux_fft[i0, i1]
+                            + KY[i0, i1]*uy_fft[i0, i1])
+
+                        ap_fft[i0, i1] = a_over2_fft + Deltaa_over2_fft
+                        am_fft[i0, i1] = a_over2_fft - Deltaa_over2_fft
+
+        return q_fft, ap_fft, am_fft
+
+    def uxuyetafft_from_qapamfft_old(self, q_fft, ap_fft, am_fft):
+        """q, ap, am (fft) ---> ux, uy, eta (fft)"""
+        a_fft = ap_fft + am_fft
+        if rank == 0:
+            a_fft[0, 0] = 0.
+        div_fft = self.divfft_from_apamfft(ap_fft, am_fft)
+        (uxa_fft, uya_fft, etaa_fft
+         ) = self.uxuyetafft_from_afft(a_fft)
+        (uxq_fft, uyq_fft, etaq_fft
+         ) = self.uxuyetafft_from_qfft(q_fft)
+        uxd_fft, uyd_fft = self.vecfft_from_divfft(div_fft)
+        ux_fft = uxa_fft + uxq_fft + uxd_fft
+        uy_fft = uya_fft + uyq_fft + uyd_fft
+        eta_fft = etaa_fft + etaq_fft
+        if rank == 0:
+            ux_fft[0, 0] = 0.5 * (ap_fft[0, 0] + am_fft[0, 0])
+            uy_fft[0, 0] = 0.5j * (am_fft[0, 0] - ap_fft[0, 0])
+        return ux_fft, uy_fft, eta_fft
+
+    # @cython.boundscheck(False)
+    # @cython.wraparound(False)
+    def uxuyetafft_from_qapamfft(self,
+                                 np.ndarray[DTYPEc_t, ndim=2] q_fft,
+                                 np.ndarray[DTYPEc_t, ndim=2] ap_fft,
+                                 np.ndarray[DTYPEc_t, ndim=2] am_fft,
+                                 params=None):
+        """q, ap, am (fft) ---> ux, uy, eta (fft)"""
+        cdef Py_ssize_t i0, i1, n0, n1
+        cdef Py_ssize_t rank = self.rank
+        cdef np.ndarray[DTYPEf_t, ndim=2] KX, KY, K2
+        cdef np.ndarray[DTYPEc_t, ndim=2] Kappa_over_ic
+        cdef np.ndarray[DTYPEf_t, ndim=2] Kappa2
+        cdef np.ndarray[DTYPEf_t, ndim=2] f_over_c2Kappa2
+        cdef np.ndarray[DTYPEf_t, ndim=2] KX_over_K2, KY_over_K2
+        cdef np.ndarray[DTYPEc_t, ndim=2] eta_fft, ux_fft, uy_fft
+        cdef DTYPEc_t div_fft, rot_fft
+        cdef DTYPEf_t freq_Corio
+
+        if params is None:
+            params = self.params
+
+        n0 = self.nK0_loc
+        n1 = self.nK1_loc
+
+        KX = self.KX
+        KY = self.KY
+        K2 = self.K2
+        Kappa2 = self.Kappa2
+        Kappa_over_ic = self.Kappa_over_ic
+        f_over_c2Kappa2 = self.f_over_c2Kappa2
+        KX_over_K2 = self.KX_over_K2
+        KY_over_K2 = self.KY_over_K2
+
+        eta_fft = np.empty([n0, n1], dtype=np.complex128)
+        ux_fft = np.empty([n0, n1], dtype=np.complex128)
+        uy_fft = np.empty([n0, n1], dtype=np.complex128)
+
+        freq_Corio = params.f
+
+        if freq_Corio != 0:
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    if rank == 0 and i0 == 0 and i1 == 0:
+                        eta_fft[i0, i1] = 0
+                        ux_fft[i0, i1] = 0.5 * (ap_fft[0, 0] + am_fft[0, 0])
+                        uy_fft[i0, i1] = 0.5j * (am_fft[0, 0] - ap_fft[0, 0])
+                    else:
+                        div_fft = (
+                            ap_fft[i0, i1] - am_fft[i0, i1]
+                            )/Kappa_over_ic[i0, i1]
+                        eta_fft[i0, i1] = (
+                            (ap_fft[i0, i1] + am_fft[i0, i1])/Kappa2[i0, i1]
+                            - f_over_c2Kappa2[i0, i1]*q_fft[i0, i1])
+                        rot_fft = (
+                            q_fft[i0, i1]
+                            + freq_Corio*eta_fft[i0, i1])
+                        ux_fft[i0, i1] = (
+                            1j * KY_over_K2[i0, i1]*rot_fft
+                            - 1j * KX_over_K2[i0, i1]*div_fft)
+                        uy_fft[i0, i1] = (
+                            -1j * KX_over_K2[i0, i1]*rot_fft
+                            - 1j * KY_over_K2[i0, i1]*div_fft)
+
+        else:  # (freq_Corio == 0.)
+            for i0 in xrange(n0):
+                for i1 in xrange(n1):
+                    if i0 == 0 and i1 == 0 and rank == 0:
+                        eta_fft[i0, i1] = 0
+                        ux_fft[i0, i1] = 0.5 * (ap_fft[0, 0] + am_fft[0, 0])
+                        uy_fft[i0, i1] = 0.5j * (am_fft[0, 0] - ap_fft[0, 0])
+                    else:
+                        div_fft = (
+                            ap_fft[i0, i1] - am_fft[i0, i1]
+                            )/Kappa_over_ic[i0, i1]
+                        eta_fft[i0, i1] = (
+                            ap_fft[i0, i1] + am_fft[i0, i1]
+                            )/K2[i0, i1]
+                        rot_fft = q_fft[i0, i1]
+                        ux_fft[i0, i1] = (
+                            1j*KY_over_K2[i0, i1]*rot_fft
+                            - 1j*KX_over_K2[i0, i1]*div_fft)
+                        uy_fft[i0, i1] = (
+                            - 1j*KX_over_K2[i0, i1]*rot_fft
+                            - 1j*KY_over_K2[i0, i1]*div_fft)
+
+        return ux_fft, uy_fft, eta_fft
+
+    def dealiasing(self, *arguments):
+        for ii in range(len(arguments)):
+            thing = arguments[ii]
+            if isinstance(thing, np.ndarray):
+                loopKdealiasing(thing, self.where_dealiased,
+                                self.nK0_loc, self.nK1_loc)
+            elif isinstance(thing, SetOfVariables):
+                thing.dealiasing(self.where_dealiased)
+
+    # def sum_wavenumbers_old(self, field_fft):
+    #     S_allkx = np.sum(field_fft)
+    #     if not self.TRANSPOSED:
+    #         S_kx0 = np.sum( field_fft[:,0] )
+    #     else:
+    #         if self.rank==0:
+    #             S_kx0 = np.sum( field_fft[0,:] )
+    #         else:
+    #             S_kx0 = 0.
+    #     S_result = 2*S_allkx-S_kx0
+    #     if self.nb_proc>1:
+    #         S_result = self.comm.allreduce(S_result, op=MPI.SUM)
+    #     return S_result
+
+    def mean_space(self, field):
+
+        mean_field = np.mean(field)
+        if not self.SEQUENCIAL:
+            mean_field = self.comm.allreduce(mean_field, op=MPI.SUM)
+            mean_field /= nb_proc
+        return mean_field
+
+    def sum_wavenumbers(self, np.ndarray[DTYPEf_t, ndim=2] A_fft):
+        """Sum the given array over all wavenumbers."""
+        cdef np.uint32_t ikO, ik1
+        cdef np.uint32_t nk0loc, nk1loc, rank, TRANSPOSED
+        cdef DTYPEf_t A0D, sum_A_fft
+
+        nk0loc = self.shapeK_loc[0]
+        nk1loc = self.shapeK_loc[1]
+
+        rank = self.rank
+
+        if self.TRANSPOSED:
+            TRANSPOSED = 1
+        else:
+            TRANSPOSED = 0
+
+        sum_A_fft = 0.
+
+        for ik0 in range(nk0loc):
+            for ik1 in range(nk1loc):
+                A0D = A_fft[ik0, ik1]
+                if TRANSPOSED == 0:
+                    if ik1 > 0:
+                        A0D = A0D*2
+                else:
+                    if ik0 > 0 or rank > 0:
+                        A0D = A0D*2
+                sum_A_fft += A0D
+
+        # if self.nb_proc>1:
+        if not self.SEQUENCIAL:
+            sum_A_fft = self.comm.allreduce(sum_A_fft, op=MPI.SUM)
+        return sum_A_fft
+
+    def spectra1D_from_fft(self, energy_fft):
+        """Compute the 1D spectra. Return a dictionary."""
+        if self.nb_proc == 1:
+            # In this case, self.dim_ky==0 and self.dim_ky==1
+            # Memory is not shared
+            # note that only the kx>=0 are in the spectral variables
+            # to obtain the spectrum as a function of kx
+            # we sum over all ky
+            # the 2 is here because there are only the kx>=0
+            E_kx = 2.*energy_fft.sum(self.dim_ky)/self.deltakx
+            E_kx[0] = E_kx[0]/2
+            E_kx = E_kx[:self.nkxE]
+            # computation of E_ky
+            E_ky_temp = energy_fft[:, 0].copy()
+            E_ky_temp += 2*energy_fft[:, 1:].sum(1)
+            nkyE = self.nkyE
+            E_ky = E_ky_temp[0:nkyE]
+            E_ky[1:nkyE] = E_ky[1:nkyE] + E_ky_temp[self.nky_seq:nkyE:-1]
+            E_ky = E_ky/self.deltaky
+
+        elif self.TRANSPOSED:
+            # In this case, self.dim_ky==1 and self.dim_ky==0
+            # Memory is shared along kx
+            # note that only the kx>=0 are in the spectral variables
+            # to obtain the spectrum as a function of kx
+            # we sum er.mamover all ky
+            # the 2 is here because there are only the kx>=0
+            E_kx_loc = 2.*energy_fft.sum(self.dim_ky)/self.deltakx
+            if self.rank == 0:
+                E_kx_loc[0] = E_kx_loc[0]/2
+            E_kx = np.empty(self.nkxE)
+            counts = self.comm.allgather(self.nkx_loc)
+            self.comm.Allgatherv(sendbuf=[E_kx_loc, MPI.DOUBLE],
+                                 recvbuf=[E_kx, (counts, None), MPI.DOUBLE])
+            E_kx = E_kx[:self.nkxE]
+            # computation of E_ky
+            if self.rank == 0:
+                E_ky_temp = energy_fft[0, :]+2*energy_fft[1:, :].sum(0)
+            else:
+                E_ky_temp = 2*energy_fft.sum(0)
+            nkyE = self.nkyE
+            E_ky = E_ky_temp[0:nkyE]
+            E_ky[1:nkyE] = E_ky[1:nkyE] + E_ky_temp[self.nky_seq:nkyE:-1]
+            E_ky = E_ky/self.deltaky
+            E_ky = self.comm.allreduce(E_ky, op=MPI.SUM)
+
+        elif not self.TRANSPOSED:
+            # In this case, self.dim_ky==0 and self.dim_ky==1
+            # Memory is shared along ky
+            # note that only the kx>=0 are in the spectral variables
+            # to obtain the spectrum as a function of kx
+            # we sum over all ky
+            # the 2 is here because there are only the kx>=0
+            E_kx = 2.*energy_fft.sum(self.dim_ky)/self.deltakx
+            E_kx[0] = E_kx[0]/2
+            E_kx = self.comm.allreduce(E_kx, op=MPI.SUM)
+            E_kx = E_kx[:self.nkxE]
+            # computation of E_ky
+            E_ky_temp = energy_fft[:, 0].copy()
+            E_ky_temp += 2*energy_fft[:, 1:].sum(1)
+            E_ky_temp = np.ascontiguousarray(E_ky_temp)
+#            print self.rank, 'E_ky_temp', E_ky_temp, E_ky_temp.shape
+            E_ky_long = np.empty(self.nky_seq)
+            counts = self.comm.allgather(self.nky_loc)
+            self.comm.Allgatherv(sendbuf=[E_ky_temp, MPI.DOUBLE],
+                                 recvbuf=[E_ky_long, (counts, None),
+                                          MPI.DOUBLE])
+            nkyE = self.nkyE
+            E_ky = E_ky_long[0:nkyE]
+            E_ky[1:nkyE] = E_ky[1:nkyE] + E_ky_long[self.nky_seq:nkyE:-1]
+            E_ky = E_ky/self.deltaky
+
+####        self.comm.barrier()
+####        sleep(0.1)
+####        print   self.rank,  'E_kx.sum() =', E_kx.sum()*self.deltakx, \
+####                            'E_ky.sum() =', E_ky.sum()*self.deltaky,\
+####                'diff = ', E_kx.sum()*self.deltakx-E_ky.sum()*self.deltaky
+        return E_kx, E_ky
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def spectrum2D_from_fft(self,
+                            np.ndarray[DTYPEf_t, ndim=2] E_fft):
+        """Compute the 2D spectra. Return a dictionary."""
+        cdef np.ndarray[DTYPEf_t, ndim=2] KK
+        cdef np.uint32_t ikO, ik1, ikh, nkh
+        cdef np.uint32_t nk0loc, nk1loc, rank, TRANSPOSED
+        cdef DTYPEf_t E0D, kappa0D, deltakh, coef_share, energy
+        cdef np.ndarray[DTYPEf_t, ndim=1] spectrum2D, khE
+
+        KK = self.KK
+
+        nk0loc = self.shapeK_loc[0]
+        nk1loc = self.shapeK_loc[1]
+
+        rank = self.rank
+
+        if self.TRANSPOSED:
+            TRANSPOSED = 1
+        else:
+            TRANSPOSED = 0
+
+        deltakh = self.deltakh
+
+        khE = self.khE
+        nkh = self.nkhE
+
+        spectrum2D = np.zeros([nkh])
+        for ik0 in xrange(nk0loc):
+            for ik1 in xrange(nk1loc):
+                E0D = E_fft[ik0, ik1]/deltakh
+                kappa0D = KK[ik0, ik1]
+
+                if TRANSPOSED == 0:
+                    if ik1 > 0:
+                        E0D = E0D*2
+                else:
+                    if ik0 > 0 or rank > 0:
+                        E0D = E0D*2
+
+                ikh = int(kappa0D/deltakh)
+
+                if ikh >= nkh-1:
+                    ikh = nkh - 1
+                    spectrum2D[ikh] += E0D
+                else:
+                    coef_share = (kappa0D - khE[ikh])/deltakh
+                    spectrum2D[ikh] += (1-coef_share)*E0D
+                    spectrum2D[ikh+1] += coef_share*E0D
+
+        if nb_proc > 1:
+            spectrum2D = comm.allreduce(spectrum2D, op=MPI.SUM)
+        return spectrum2D
+
+    def pdf_normalized(self, field, nb_bins=100):
+        """Compute the normalized pdf"""
+
+        field_max = field.max()
+        field_min = field.min()
+        # field_mean = field.mean()
+
+        if nb_proc > 1:
+            field_max = comm.allreduce(field_max, op=MPI.MAX)
+            field_min = comm.allreduce(field_min, op=MPI.MIN)
+            # field_mean = comm.allreduce(field_min, op=MPI.SUM)/nb_proc
+
+        # rms = np.sqrt(np.mean( (field-field_mean)**2 ))
+        # range_min = field_mean - 20*rms
+        # range_max = field_mean + 20*rms
+
+        # range_min = max(field_min, range_min)
+        # range_max = min(field_max, range_max)
+
+        range_min = field_min
+        range_max = field_max
+
+        if nb_proc == 1:
+            pdf, bin_edges = np.histogram(field, bins=nb_bins,
+                                          normed=True,
+                                          range=(range_min, range_max))
+        else:
+            hist, bin_edges = np.histogram(field, bins=nb_bins,
+                                           range=(range_min, range_max))
+            hist = comm.allreduce(hist, op=MPI.SUM)
+            pdf = hist/((bin_edges[1]-bin_edges[0])*hist.sum())
+        return pdf, bin_edges
+
+    def compute_increments_dim1(self,
+                                np.ndarray[DTYPEf_t, ndim=2] var,
+                                np.uint32_t irx):
+        """Compute the increments of var over the dim 1."""
+        cdef np.uint32_t iO, i1, n0, n1, n1new
+        cdef np.ndarray[DTYPEf_t, ndim=2] inc_var
+        n0 = var.shape[0]
+        n1 = var.shape[1]
+        n1new = n1 - irx
+        inc_var = np.empty([n0, n1new])
+        for i0 in xrange(n0):
+            for i1 in xrange(n1new):
+                inc_var[i0, i1] = (var[i0, i1+irx] - var[i0, i1])
+        return inc_var
+
+#### functions for initialisation of field
+    def constant_arrayK(self, value=None, dtype=complex, SHAPE='LOC'):
+        """Return a constant array in spectral space."""
+        if SHAPE == 'LOC':
+            shapeK = self.shapeK_loc
+        elif SHAPE == 'SEQ':
+            shapeK = self.shapeK_seq
+        elif SHAPE == 'GAT':
+            shapeK = self.shapeK_gat
+        else:
+            raise ValueError('SHAPE should be "LOC" or "SEQ"')
+        if value is None:
+            field_lm = np.empty(self.shapeK, dtype=dtype)
+        elif value == 0:
+            field_lm = np.zeros(self.shapeK, dtype=dtype)
+        else:
+            field_lm = value*np.ones(self.shapeK, dtype=dtype)
+        return field_lm
+
+    def constant_arrayX(self, value=None, dtype=DTYPEf, SHAPE='LOC'):
+        """Return a constant array in real space."""
+        if SHAPE == 'LOC':
+            shapeX = self.shapeX_loc
+        elif SHAPE == 'SEQ':
+            shapeX = self.shapeX_seq
+        else:
+            raise ValueError('SHAPE should be "LOC" of "SEQ"')
+        if value is None:
+            field = np.empty(shapeX, dtype=dtype)
+        elif value == 0:
+            field = np.zeros(shapeX, dtype=dtype)
+        else:
+            field = value*np.ones(shapeX, dtype=dtype)
+        return field
+
+    def random_arrayK(self, SHAPE='LOC'):
+        """Return a random array in spectral space."""
+        if SHAPE == 'LOC':
+            shapeK = self.shapeK_loc
+        elif SHAPE == 'SEQ':
+            shapeK = self.shapeK_seq
+        elif SHAPE == 'GAT':
+            shapeK = self.shapeK_gat
+        else:
+            raise ValueError('SHAPE should be "LOC", "GAT" or "SEQ"')
+        a_fft = (np.random.random(shapeK)
+                 + 1j*np.random.random(shapeK)
+                 - 0.5 - 0.5j)
+        return a_fft
+
+    def random_arrayX(self, SHAPE='LOC'):
+        """Return a random array in real space."""
+        if SHAPE == 'LOC':
+            shapeX = self.shapeX_loc
+        elif SHAPE == 'SEQ':
+            shapeX = self.shapeX_seq
+        else:
+            raise ValueError('SHAPE should be "LOC" or "SEQ"')
+        return np.random.random(shapeX)
+
+    def project_fft_on_realX_seq(
+            self, np.ndarray[DTYPEc_t, ndim=2] f_fft):
+        """Project the given field in spectral space such as its
+        inverse fft is a real field."""
+        cdef np.uint32_t nky_seq
+        cdef np.uint32_t iky_ky0, iky_kyM, ikx_kx0, ikx_kxM,
+        cdef np.uint32_t ikyp, ikyn
+        cdef DTYPEc_t f_kp_kx0, f_kn_kx0, f_kp_kxM, f_knp_kxM
+
+        nky_seq = self.shapeK_seq[0]
+
+        iky_ky0 = 0
+        iky_kyM = nky_seq/2
+        ikx_kx0 = 0
+        # ikx_kxM = self.nkx_seq-1
+        ikx_kxM = self.shapeK_seq[1]-1
+
+        # first, some values have to be real
+        f_fft[iky_ky0, ikx_kx0] = f_fft[iky_ky0, ikx_kx0].real
+        f_fft[iky_ky0, ikx_kxM] = f_fft[iky_ky0, ikx_kxM].real
+        f_fft[iky_kyM, ikx_kx0] = f_fft[iky_kyM, ikx_kx0].real
+        f_fft[iky_kyM, ikx_kxM] = f_fft[iky_kyM, ikx_kxM].real
+
+        # second, there are relations between some values
+        for ikyp in xrange(1, iky_kyM):
+            ikyn = nky_seq - ikyp
+
+            f_kp_kx0 = f_fft[ikyp, ikx_kx0]
+            f_kn_kx0 = f_fft[ikyn, ikx_kx0]
+
+            f_fft[ikyp, ikx_kx0] = (f_kp_kx0+f_kn_kx0.conjugate()
+                                    )/2
+            f_fft[ikyn, ikx_kx0] = ((f_kp_kx0+f_kn_kx0.conjugate()
+                                     )/2).conjugate()
+
+            f_kp_kxM = f_fft[ikyp, ikx_kxM]
+            f_kn_kxM = f_fft[ikyn, ikx_kxM]
+
+            f_fft[ikyp, ikx_kxM] = (f_kp_kxM+f_kn_kxM.conjugate()
+                                    )/2
+            f_fft[ikyn, ikx_kxM] = ((f_kp_kxM+f_kn_kxM.conjugate()
+                                     )/2).conjugate()
+
+    def coarse_seq_from_fft_loc(self, f_fft, shapeK_loc_coarse):
+        """Return a coarse field in K space."""
+        nKyc = shapeK_loc_coarse[0]
+        nKxc = shapeK_loc_coarse[1]
+
+        if nb_proc > 1:
+            fc_trans = np.empty([nKxc, nKyc], np.complex128)
+            nKy = self.shapeK_seq[0]
+            f1D_temp = np.empty([nKyc], np.complex128)
+
+            for iKxc in xrange(nKxc):
+                kx = self.deltakx*iKxc
+                rank_iKx, iKxloc, iKyloc = self.where_is_wavenumber(kx, 0.)
+                if rank == rank_iKx:
+                    # create f1D_temp
+                    for iKyc in xrange(nKyc):
+                        if iKyc <= nKyc/2:
+                            iKy = iKyc
+                        else:
+                            kynodim = iKyc - nKyc
+                            iKy = kynodim + nKy
+                        f1D_temp[iKyc] = f_fft[iKxloc, iKy]
+
+                if rank_iKx != 0:
+                    # message f1D_temp
+                    if rank == 0:
+                        # print 'f1D_temp', f1D_temp, f1D_temp.dtype
+                        comm.Recv(
+                            [f1D_temp, MPI.DOUBLE_COMPLEX],
+                            source=rank_iKx, tag=iKxc)
+                    elif rank == rank_iKx:
+                        comm.Send(
+                            [f1D_temp, MPI.DOUBLE_COMPLEX],
+                            dest=0, tag=iKxc)
+                if rank == 0:
+                    # copy into fc_trans
+                    fc_trans[iKxc] = f1D_temp.copy()
+            fc_fft = fc_trans.transpose()
+
+        else:
+            nKy = self.shapeK_seq[0]
+            nKx = self.shapeK_seq[1]
+            fc_fft = np.empty([nKyc, nKxc], np.complex128)
+            for iKyc in xrange(nKyc):
+                if iKyc <= nKyc/2:
+                    iKy = iKyc
+                else:
+                    kynodim = iKyc - nKyc
+                    iKy = kynodim + nKy
+                for iKxc in xrange(nKxc):
+                    fc_fft[iKyc, iKxc] = f_fft[iKy, iKxc]
+        return fc_fft
+
+    def fft_loc_from_coarse_seq(self, fc_fft, shapeK_loc_coarse):
+        """Return a large field in K space."""
+        nKyc = shapeK_loc_coarse[0]
+        nKxc = shapeK_loc_coarse[1]
+
+        if nb_proc > 1:
+            nKy = self.shapeK_seq[0]
+            f_fft = self.constant_arrayK(value=0.)
+            fc_trans = fc_fft.transpose()
+
+            for iKxc in xrange(nKxc):
+                kx = self.deltakx*iKxc
+                rank_iKx, iKxloc, iKyloc = self.where_is_wavenumber(kx, 0.)
+                fc1D = fc_trans[iKxc]
+                if rank_iKx != 0:
+                    # message fc1D
+                    fc1D = np.ascontiguousarray(fc1D)
+                    if rank == 0:
+                        comm.Send(fc1D, dest=rank_iKx, tag=iKxc)
+                    elif rank == rank_iKx:
+                        comm.Recv(fc1D, source=0, tag=iKxc)
+                if rank == rank_iKx:
+                    # copy
+                    for iKyc in xrange(nKyc):
+                        if iKyc <= nKyc/2:
+                            iKy = iKyc
+                        else:
+                            kynodim = iKyc - nKyc
+                            iKy = kynodim + nKy
+                        f_fft[iKxloc, iKy] = fc1D[iKyc]
+
+        else:
+            nKy = self.shapeK_seq[0]
+            nKx = self.shapeK_seq[1]
+            f_fft = np.zeros([nKy, nKx], np.complex128)
+            for iKyc in xrange(nKyc):
+                if iKyc <= nKyc/2:
+                    iKy = iKyc
+                else:
+                    kynodim = iKyc - nKyc
+                    iKy = kynodim + nKy
+                for iKxc in xrange(nKxc):
+                    f_fft[iKy, iKxc] = fc_fft[iKyc, iKxc]
+        return f_fft
+
+    def monge_ampere_from_fft(
+            self, DTYPEc_t[:, :] a_fft, DTYPEc_t[:, :] b_fft):
+        cdef Py_ssize_t i0, n0, i1, n1
+        cdef DTYPEc_t[:, :] pxx_afft, pyy_afft, pxy_afft
+        cdef DTYPEc_t[:, :] pxx_bfft, pyy_bfft, pxy_bfft
+        cdef DTYPEf_t[:, :] mamp
+        cdef DTYPEf_t[:, :] KX, KY, pxx_a, pyy_a, pxy_a, pxx_b, pyy_b, pxy_b
+
+        n0 = a_fft.shape[0]
+        n1 = a_fft.shape[1]
+        KX = self.KX
+        KY = self.KY
+
+        pxx_afft = np.empty([n0, n1], dtype=DTYPEc)
+        pyy_afft = np.empty([n0, n1], dtype=DTYPEc)
+        pxy_afft = np.empty([n0, n1], dtype=DTYPEc)
+        pxx_bfft = np.empty([n0, n1], dtype=DTYPEc)
+        pyy_bfft = np.empty([n0, n1], dtype=DTYPEc)
+        pxy_bfft = np.empty([n0, n1], dtype=DTYPEc)
+
+        for i0 in xrange(n0):
+            for i1 in xrange(n1):
+                pxx_afft[i0, i1] = - a_fft[i0, i1] * KX[i0, i1]**2
+                pyy_afft[i0, i1] = - a_fft[i0, i1] * KY[i0, i1]**2
+                pxy_afft[i0, i1] = - a_fft[i0, i1] * KX[i0, i1]*KY[i0, i1]
+                pxx_bfft[i0, i1] = - b_fft[i0, i1] * KX[i0, i1]**2
+                pyy_bfft[i0, i1] = - b_fft[i0, i1] * KY[i0, i1]**2
+                pxy_bfft[i0, i1] = - b_fft[i0, i1] * KX[i0, i1]*KY[i0, i1]
+        pxx_a = self.ifft2(pxx_afft)
+        pyy_a = self.ifft2(pyy_afft)
+        pxy_a = self.ifft2(pxy_afft)
+        pxx_b = self.ifft2(pxx_bfft)
+        pyy_b = self.ifft2(pyy_bfft)
+        pxy_b = self.ifft2(pxy_bfft)
+
+        mamp = np.empty_like(pxx_a)
+        n0 = mamp.shape[0]
+        n1 = mamp.shape[1]
+        for i0 in xrange(n0):
+            for i1 in xrange(n1):
+                mamp[i0, i1] = (pxx_a[i0, i1] * pyy_b[i0, i1] +
+                                pyy_a[i0, i1] * pxx_b[i0, i1] -
+                                2 * pxy_a[i0, i1] * pxy_b[i0, i1])
+        return np.array(mamp)
+
+    def monge_ampere_from_fft_python(self, a_fft, b_fft):
+        KX = self.KX
+        KY = self.KY
+        ifft2 = self.ifft2
+
+        pxx_a = - ifft2(a_fft * KX**2)
+        pyy_a = - ifft2(a_fft * KY**2)
+        pxy_a = - ifft2(a_fft * KX * KY)
+
+        pxx_b = - ifft2(b_fft * KX**2)
+        pyy_b = - ifft2(b_fft * KY**2)
+        pxy_b = - ifft2(b_fft * KX * KY)
+
+        return pxx_a*pyy_b + pyy_a*pxx_b - 2*pxy_a*pxy_b
+
+    def laplacian2_fft(self, DTYPEc_t[:, :] a_fft):
+        cdef Py_ssize_t i0, n0, i1, n1
+        cdef DTYPEc_t[:, :] lap2_afft = np.empty_like(a_fft)
+        cdef DTYPEf_t[:, :] K4 = self.K4
+
+        n0 = a_fft.shape[0]
+        n1 = a_fft.shape[1]
+        for i0 in xrange(n0):
+            for i1 in xrange(n1):
+                lap2_afft[i0, i1] = a_fft[i0, i1] * K4[i0, i1]
+        return np.array(lap2_afft)
+
+    def invlaplacian2_fft(self, DTYPEc_t[:, :] a_fft):
+        cdef Py_ssize_t i0, n0, i1, n1
+        cdef DTYPEc_t[:, :] invlap2_afft = np.empty_like(a_fft)
+        cdef DTYPEf_t[:, :] K4_not0 = self.K4_not0
+
+        n0 = a_fft.shape[0]
+        n1 = a_fft.shape[1]
+
+        for i0 in xrange(n0):
+            for i1 in xrange(n1):
+                invlap2_afft[i0, i1] = a_fft[i0, i1] / K4_not0[i0, i1]
+
+        if rank == 0:
+            invlap2_afft[0, 0] = 0.
+        return np.array(invlap2_afft)
+
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+cdef np.ndarray loopKdealiasing(np.ndarray[DTYPEc_t, ndim=2] ff_fft,
+                                np.ndarray[DTYPEb_t, ndim=2] where_dealiased,
+                                int nK0loc, int nK1loc):
+    cdef np.uint32_t iKO, iK1
+    for iK0 in range(nK0loc):
+        for iK1 in range(nK1loc):
+            if where_dealiased[iK0, iK1]:
+                ff_fft[iK0, iK1] = 0.
diff --git a/fluidsim/operators/CySources/setofvariables_cy.pyx b/fluidsim/operators/CySources/setofvariables_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL0N5U291cmNlcy9zZXRvZnZhcmlhYmxlc19jeS5weXg=
--- /dev/null
+++ b/fluidsim/operators/CySources/setofvariables_cy.pyx
@@ -0,0 +1,252 @@
+"""Variable container (:mod:`fluidsim.operators.setofvariables`)
+======================================================================
+
+This module is written in cython and provides:
+
+.. currentmodule:: fluidsim.operators.setofvariables
+
+Provides:
+
+.. autoclass:: SetOfVariables
+   :members:
+   :private-members:
+
+"""
+
+# DEF MPI4PY = 0
+
+cimport numpy as np
+import numpy as np
+np.import_array()
+
+try:
+    from mpi4py import MPI
+except ImportError:
+    nb_proc = 1
+    rank = 0
+else:
+    comm = MPI.COMM_WORLD
+    nb_proc = comm.size
+    rank = comm.Get_rank()
+
+IF MPI4PY:
+    from mpi4py cimport MPI
+    from mpi4py.mpi_c cimport *
+
+    # solve an incompatibility between openmpi and mpi4py versions
+    cdef extern from 'mpi-compat.h': pass
+
+
+from time import time, sleep
+import datetime
+import os
+import matplotlib.pyplot as plt
+import cython
+
+from libc.math cimport exp
+
+
+# we define python and c types for physical and Fourier spaces
+DTYPEb = np.uint8
+ctypedef np.uint8_t DTYPEb_t
+DTYPEi = np.int
+ctypedef np.int_t DTYPEi_t
+DTYPEf = np.float64
+ctypedef np.float64_t DTYPEf_t
+DTYPEc = np.complex128
+ctypedef np.complex128_t DTYPEc_t
+
+# Basically, you use the _t ones when you need to declare a type
+# (e.g. cdef foo_t var, or np.ndarray[foo_t, ndim=...]. Ideally someday
+# we won't have to make this distinction, but currently one is a C type
+# and the other is a python object representing a numpy type (a dtype),
+# and there's currently no way to identify the two without special
+# compiler support.
+# - Robert Bradshaw
+
+
+class SetOfVariables(object):
+    """Gather a set of variables in a Numpy array.
+
+
+    """
+    __array_priority__ = 100
+
+    @property
+    def nbytes(self):
+        return self.data.nbytes
+
+    def __init__(self,
+                 keys=None, shape1var=None,
+                 dtype=None, name_type_variables=None,
+                 like_this_sov=None, value=None):
+        if like_this_sov is not None:
+            keys = like_this_sov.keys
+            self.nb_variables = like_this_sov.data.shape[0]
+            shape1var = like_this_sov.data.shape[1:]
+            if dtype is None:
+                dtype = like_this_sov.data.dtype
+            if name_type_variables is None:
+                name_type_variables = like_this_sov.name_type_variables
+        else:
+            if dtype is None:
+                dtype = np.float64
+            keys.sort()
+            self.nb_variables = len(keys)
+
+        self.name_type_variables = name_type_variables
+        self.keys = keys
+        shape = [self.nb_variables]
+        shape.extend(shape1var)
+        if value is None:
+            self.data = np.empty(shape, dtype=dtype)
+        elif value == 0:
+            self.data = np.zeros(shape, dtype=dtype)
+        else:
+            self.data = value*np.ones(shape, dtype=dtype)
+
+        dimension_space = len(shape1var)
+        if dimension_space == 1:
+            self.dealiasing = self._dealiasing1d
+        elif dimension_space == 2:
+            self.dealiasing = self._dealiasing2d
+        elif dimension_space == 3:
+            self.dealiasing = self._dealiasing3d
+        else:
+            raise ValueError(
+                'Space dimension {} not implemented in SetOfVariables.'.format(
+                    dimension_space))
+
+    def __getitem__(self, key):
+        ik = self.keys.index(key)
+        return self.data[ik]
+
+    def __setitem__(self, key, value):
+        ik = self.keys.index(key)
+        self.data[ik][:] = value
+
+    def __add__(self, other):
+        if isinstance(other, self.__class__):
+            dtype_new = max_dtype(self.data, other.data)
+            obj_result = self.__class__(like_this_sov=other, dtype=dtype_new)
+            obj_result.data = self.data + other.data
+        elif isinstance(other, (int, float, complex)):
+            dtype_new = max_dtype(self.data, other)
+            obj_result = self.__class__(like_this_sov=self, dtype=dtype_new)
+            obj_result.data = self.data + other
+        return obj_result
+    __radd__ = __add__
+
+    def __iadd__(self, other):
+        if isinstance(other, self.__class__):
+            dtype_new = max_dtype(self.data, other.data)
+            if dtype_new != self.data.dtype:
+                obj_result = self.__class__(
+                    like_this_sov=self, dtype=dtype_new)
+            else:
+                obj_result = self
+            obj_result.data += other.data
+        elif isinstance(other, (int, float, complex)):
+            dtype_new = max_dtype(self.data, other)
+            if dtype_new != self.data.dtype:
+                obj_result = self.__class__(
+                    like_this_sov=self, dtype=dtype_new)
+            else:
+                obj_result = self
+            obj_result.data += other.data
+        return obj_result
+
+    def __sub__(self, other):
+        if isinstance(other, self.__class__):
+            dtype_new = max_dtype(self.data, other.data)
+            obj_result = self.__class__(like_this_sov=other, dtype=dtype_new)
+            obj_result.data = self.data - other.data
+        elif isinstance(other, (int, float, complex)):
+            dtype_new = max_dtype(self.data, other)
+            obj_result = self.__class__(like_this_sov=self, dtype=dtype_new)
+            obj_result.data = self.data - other
+        return obj_result
+
+    def __mul__(self, other):
+        if isinstance(other, (int, float, np.ndarray)):
+            dtype_new = max_dtype(self.data, other)
+            obj_result = self.__class__(like_this_sov=self, dtype=dtype_new)
+            obj_result.data = other*self.data
+        return obj_result
+    __rmul__ = __mul__
+
+    def __div__(self, other):
+        if isinstance(other, (int, float, np.ndarray)):
+            dtype_new = max_dtype(self.data, other)
+            obj_result = self.__class__(like_this_sov=self, dtype=dtype_new)
+            obj_result.data = self.data/other
+        return obj_result
+
+    def initialize(self, value=0.):
+        self.data = value*np.ones(self.data.shape,
+                                  dtype=self.data.dtype)
+
+    def _dealiasing1d(self, DTYPEb_t[:] where_dealiased):
+        cdef DTYPEc_t[:, :] data = self.data
+        cdef Py_ssize_t ik, nk, i0, n0
+
+        nk = self.nb_variables
+        n0 = data.shape[1]
+
+        for i0 in xrange(n0):
+            if where_dealiased[i0]:
+                for ik in xrange(nk):
+                    data[ik, i0] = 0.
+
+    def _dealiasing2d(self, DTYPEb_t[:, :] where_dealiased):
+        cdef DTYPEc_t[:, :, :] data = self.data
+        cdef Py_ssize_t ik, nk, i0, n0, i1, n1
+
+        nk = self.nb_variables
+        n0 = data.shape[1]
+        n1 = data.shape[2]
+
+        for i0 in xrange(n0):
+            for i1 in xrange(n1):
+                if where_dealiased[i0, i1]:
+                    for ik in xrange(nk):
+                        data[ik, i0, i1] = 0.
+
+    def _dealiasing3d(self, DTYPEb_t[:, :, :] where_dealiased):
+        cdef DTYPEc_t[:, :, :, :] data = self.data
+        cdef Py_ssize_t ik, nk, i0, n0, i1, n1, i2, n2
+
+        nk = self.nb_variables
+        n0 = data.shape[1]
+        n1 = data.shape[2]
+        n2 = data.shape[3]
+
+        for i0 in xrange(n0):
+            for i1 in xrange(n1):
+                for i2 in xrange(n2):
+                    if where_dealiased[i0, i1, i2]:
+                        for ik in xrange(nk):
+                            data[ik, i0, i1, i2] = 0.
+
+
+def max_dtype(A, B):
+    '''Return the dtype of the result of an operation involving A and B.'''
+    # it would be better to just use
+    try:
+        # this function is only available in numpy 1.6
+        return np.result_type(A, B)
+    except AttributeError:
+        if isinstance(A, np.ndarray):
+            dtypeA = A.dtype
+        else:
+            dtypeA = np.array(A).dtype
+
+        if isinstance(B, np.ndarray):
+            dtypeB = B.dtype
+        else:
+            dtypeB = np.array(B).dtype
+
+        if dtypeA <= dtypeB:
+            return dtypeB
+        else:
+            return dtypeA
diff --git a/fluidsim/operators/__init__.py b/fluidsim/operators/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/operators/__init__.py
@@ -0,0 +1,27 @@
+"""Numerical operators (:mod:`fluidsim.operators`)
+========================================================
+
+.. currentmodule:: fluidsim.operators
+
+Provides
+
+.. autosummary::
+   :toctree:
+
+   operators
+   setofvariables
+   fft
+
+.. todo:: Make a nice hierarchy of Operators classes.
+
+   - Operators
+   - OperatorsPseudoSpectral1D
+   - OperatorsPseudoSpectral2D
+   - OperatorsPseudoSpectral3D
+   - OperatorsFiniteDiff1D
+   - OperatorsFiniteDiff2D
+   - OperatorsFiniteDiff3D
+   - OperatorsPseudoSpectral1DFiniteDiff1D
+   - OperatorsPseudoSpectral2DFiniteDiff1D
+
+"""
diff --git a/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.c b/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.c
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZGNjeS9mZnQyRHNvbHZlcTJELmM=
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.c
@@ -0,0 +1,443 @@
+/* test_fftw3_2Dmpi_simple program 
+
+export USR_PERSO='/home/pierre/usr'
+export USR_PERSO='/scratch/augier/usr'
+
+Compiled with:
+gcc -O3 fft2Dsolveq2D.c -I$USR_PERSO/include/ \
+$USR_PERSO/lib/libfftw3.so -lm -o fft2Dsolveq2D
+
+Create the library:
+gcc -c -fPIC fft2Dsolveq2D.c -I$USR_PERSO/include -o fft2Dsolveq2D.o
+gcc fft2Dsolveq2D.o -shared -o libfft2Dsolveq2D.so $USR_PERSO/lib/libfftw3.so -lm
+mv libfft2Dsolveq2D.so $USR_PERSO/lib
+
+
+
+And on ferlin:
+gcc -O3 fft2Dsolveq2D.c -I${FFTW_HOME}/double/include \
+-L${FFTW_HOME}/double/lib -lfftw3 -lm -o fft2Dsolveq2D
+
+Create the library:
+gcc -c -fPIC fft2Dsolveq2D.c -I${FFTW_HOME}/double/include -o fft2Dsolveq2D.o
+
+gcc -shared -o libfft2Dsolveq2D.so fft2Dsolveq2D.o \
+${FFTW_HOME}/double/lib/libfftw3.so -lm 
+
+
+mpicc -c -fPIC fft2Dmpisolveq2D.c -I${FFTW_HOME}/double/include \
+-o fft2Dmpisolveq2D.o
+mpicc -shared -o libfft2Dmpisolveq2D.so fft2Dmpisolveq2D.o \
+-L${FFTW_HOME}/double/lib -lm -lfftw3
+
+
+
+
+execute with
+mpirun -np 2 ./fft2Dsolveq2D
+(the 2 after "-np" is the number of processors)
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <complex.h>
+#include <fftw3.h>
+#include <sys/time.h>
+
+
+#include <unistd.h>
+
+#include <math.h>
+
+#include "fft2Dsolveq2D.h"
+
+
+
+
+Util_fft init_Util_fft(int N0, int N1)
+    {
+    Util_fft uf;
+    struct timeval start_time, end_time;
+    double total_usecs;
+    int ii, jj, irank, iX0;
+    int istride = 1, ostride = 1;
+    int howmany, sign;
+
+
+/*    printf("init_util_fft, N0 = %d, N1 = %d\n", N0, N1);*/
+
+    uf.N0 = N0;
+    uf.N1 = N1;
+
+    /* y corresponds to dim 0 in physical space */
+    /* x corresponds to dim 1 in physical space */
+    uf.ny = N0;
+    uf.nx = N1;
+
+    uf.nX0 = N0;
+    uf.nX1 = N1;
+    uf.nX0loc = N0;
+    uf.nXyloc = uf.nX0loc;
+
+    uf.nKx = uf.nx/2;
+    uf.nKxloc = uf.nKx;
+    uf.nKy = uf.ny;
+
+    /* This 2D fft is transposed */
+    uf.nK0 = N1/2;
+    uf.nK0loc = uf.nK0;
+    uf.nK1 = N0;
+
+    uf.coef_norm = N0*N1;
+
+    uf.flags = FFTW_MEASURE;
+/*    flags = FFTW_ESTIMATE;*/
+/*    uf.flags = FFTW_PATIENT;*/
+
+    uf.arrayX    = (double*) fftw_malloc(sizeof(double)*uf.nX0loc*N1);
+    uf.arrayK_pR = (fftw_complex*) fftw_malloc( sizeof(fftw_complex)
+                                                *uf.nX0loc*(uf.nKx+1));
+    uf.arrayK_pC = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*uf.nKxloc*N0);
+
+
+/*    if ((uf.rank)==0) printf("create plans\n");*/
+    gettimeofday(&start_time, NULL);
+/*    plan = fftw_plan_many_dft(int rank, const int *n, int howmany,*/
+/*                                  fftw_complex *in, const int *inembed,*/
+/*                                  int istride, int idist,*/
+/*                                  fftw_complex *out, const int *onembed,*/
+/*                                  int ostride, int odist,*/
+/*                                  int sign, unsigned flags);*/
+
+    howmany = uf.nX0loc;
+    uf.plan_r2c = fftw_plan_many_dft_r2c(   1, &N1, howmany,
+                                            uf.arrayX, NULL,
+                                            istride, N1,
+                                            uf.arrayK_pR, NULL,
+                                            ostride, uf.nKx+1,
+                                            uf.flags);
+
+    uf.plan_c2r = fftw_plan_many_dft_c2r(   1, &N1, howmany,
+                                            uf.arrayK_pR, NULL,
+                                            istride, uf.nKx+1,
+                                            uf.arrayX, NULL,
+                                            ostride, N1,
+                                            uf.flags);
+
+    howmany = uf.nKxloc;
+    sign = FFTW_FORWARD;
+    uf.plan_c2c_fwd = fftw_plan_many_dft(   1, &N0, howmany,
+                                            uf.arrayK_pC, &N0,
+                                            istride, N0,
+                                            uf.arrayK_pC, &N0,
+                                            ostride, N0,
+                                            sign, uf.flags);
+    sign = FFTW_BACKWARD;
+    uf.plan_c2c_bwd = fftw_plan_many_dft(   1, &N0, howmany,
+                                            uf.arrayK_pC, &N0,
+                                            istride, N0,
+                                            uf.arrayK_pC, &N0,
+                                            ostride, N0,
+                                            sign, uf.flags);
+
+    gettimeofday(&end_time, NULL);
+    total_usecs = (end_time.tv_sec-start_time.tv_sec) + 
+        (end_time.tv_usec-start_time.tv_usec)/1000000.;
+/*    printf ("               done in %f s\n", */
+/*            total_usecs);*/
+
+    for (iX0=0;iX0<uf.nX0loc;iX0++)
+        uf.arrayK_pR[iX0*(uf.nKx+1)+uf.nKx] = 0.;
+
+
+
+/*    for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nKx+1; ++jj)*/
+/*    {*/
+/*    printf("%i , uf.arrayK_pR[%i*(uf.nKx+1) + %i] = (%6.4f, %6.4f)\n", */
+/*              uf.rank, ii, jj,*/
+/*             creal(uf.arrayK_pR[ii*(uf.nKx+1) + jj]), */
+/*               cimag(uf.arrayK_pR[ii*(uf.nKx+1) + jj]));*/
+/*                }*/
+
+
+
+
+    return uf;
+}
+
+
+
+void destroy_Util_fft(Util_fft uf)
+    {
+/*    if ((uf.rank)==0) printf("destroy_util_fft\n");*/
+    fftw_destroy_plan(uf.plan_r2c);
+    fftw_destroy_plan(uf.plan_c2c_fwd);
+    fftw_destroy_plan(uf.plan_c2c_bwd);
+    fftw_destroy_plan(uf.plan_c2r);
+    fftw_free(uf.arrayX);
+    fftw_free(uf.arrayK_pR);
+    fftw_free(uf.arrayK_pC);
+}
+
+
+
+
+
+
+
+
+
+void fft2D(Util_fft uf, double *fieldX, fftw_complex *fieldK)
+    {
+    int ii, jj;
+    /*use memcpy(void * destination, void * source, size_t bytes); */
+
+    memcpy(uf.arrayX, fieldX, uf.nX0loc*uf.nX1*sizeof(double));
+
+
+/*       for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.N1; ++jj)*/
+/*            {*/
+/*            printf( "%d , uf.arrayX[%d,%d] = %6.4f\n", */
+/*                    uf.rank, ii, jj, uf.arrayX[ii*uf.N1+jj]);*/
+/*            }*/
+
+
+    fftw_execute(uf.plan_r2c);
+
+/*     printf("print uf.arrayK_pR after alltoall\n");*/
+
+/*     for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nKx+1; ++jj)*/
+/*       {*/
+/*       printf("%i , uf.arrayK_pR[%i*(uf.nKx+1) + %i] = (%6.4f, %6.4f)\n", */
+/*                        uf.rank, ii, jj,*/
+/*                        creal(uf.arrayK_pR[ii*(uf.nKx+1)+jj]), */
+/*                        cimag(uf.arrayK_pR[ii*(uf.nKx+1)+jj]));*/
+/*                }*/
+
+/*    second step: transpose...*/
+    for (ii = 0; ii < uf.nX0; ++ii) for (jj = 0; jj < uf.nKx; ++jj)
+        uf.arrayK_pC[jj*uf.nX0+ii] = 
+          uf.arrayK_pR[ii*(uf.nKx+1)+jj];
+
+
+/*       for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.N0; ++jj)*/
+/*           {*/
+/*        printf("%i , uf.arrayK_pC[%i*uf.N0 + %i] = (%6.4f, %6.4f)\n", */
+/*                        uf.rank, ii, jj,*/
+/*                        creal(uf.arrayK_pC[ii*uf.N0 + jj]), */
+/*                        cimag(uf.arrayK_pC[ii*uf.N0 + jj]));*/
+/*                }*/
+
+
+
+    fftw_execute(uf.plan_c2c_fwd);
+
+
+/*      for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.N0; ++jj)*/
+/*           {*/
+/*         printf("%i , uf.arrayK_pC[%i*uf.N0 + %i] = (%6.4f, %6.4f)\n", */
+/*                        uf.rank, ii, jj,*/
+/*                        creal(uf.arrayK_pC[ii*uf.N0 + jj]), */
+/*                        cimag(uf.arrayK_pC[ii*uf.N0 + jj]));*/
+/*                }*/
+
+
+
+    for (ii=0; ii<uf.nKxloc*uf.nKy; ii++)
+        fieldK[ii]  = uf.arrayK_pC[ii]/uf.coef_norm;
+
+    }
+
+
+
+
+void ifft2D(Util_fft uf, fftw_complex *fieldK, double *fieldX)
+    {
+    int ii, jj, irank;
+    /*use memcpy(void * destination, void * source, size_t bytes); */
+    memcpy(uf.arrayK_pC, fieldK, uf.nKxloc*uf.nKy*sizeof(fftw_complex));
+    fftw_execute(uf.plan_c2c_bwd);
+
+    /* second step: transpose...*/
+    for (ii = 0; ii < uf.nKx; ++ii) for (jj = 0; jj < uf.nX0; ++jj)
+          uf.arrayK_pR[jj*(uf.nKx+1)+ii] =
+            uf.arrayK_pC[ii*uf.nX0+jj];
+
+    /* these modes (nx/2+1=N1/2+1) have to be settled to zero*/
+    for (ii = 0; ii < uf.nX0loc; ++ii) 
+        uf.arrayK_pR[ii*(uf.nKx+1) + uf.nKx] = 0.;
+
+    fftw_execute(uf.plan_c2r);
+    memcpy(fieldX,uf.arrayX, uf.nX0loc*uf.nX1*sizeof(double));
+    }
+
+
+void time_execute(Util_fft uf, double *fieldX, 
+                  fftw_complex *fieldK, int nb_time_execute)
+    {
+    int ii;
+    struct timeval start_time, end_time;
+    double total_usecs;
+
+    if (uf.rank==0) printf("timer...\n");
+    gettimeofday(&start_time, NULL);
+    for (ii=0; ii<nb_time_execute; ii++)
+    {
+        fft2D(uf, fieldX, fieldK);
+        ifft2D(uf, fieldK, fieldX);
+    }
+    gettimeofday(&end_time, NULL);
+
+
+    total_usecs = (end_time.tv_sec-start_time.tv_sec) + 
+        (end_time.tv_usec-start_time.tv_usec)/1000000.;
+    printf ("%d times forward and backward sequencial FFT: %f s\n", 
+            nb_time_execute ,total_usecs); 
+}
+
+
+
+int main(int argc, char **argv)
+    {
+    const int N0 = 4, N1 = 4;
+    Util_fft uf;
+    double * fieldX, * fieldX_0;
+    fftw_complex * fieldK, * fieldK_0;
+    int ii, jj, irank;
+    double energyK, energyX, energy2;
+    int coef;
+
+
+    uf = init_Util_fft(N0,N1);
+
+    srand(time(NULL)+uf.rank*uf.nb_proc);
+
+    printf( "I'm rank (processor number) %i of size %i\n", 
+            uf.rank, uf.nb_proc);
+
+    fieldX = (double *) malloc(uf.nX0loc*uf.nX1 * sizeof(double));
+    fieldX_0 = (double *) malloc(uf.nX0loc*uf.nX1 * sizeof(double));
+    fieldK = (fftw_complex *) malloc(uf.nKxloc*uf.nKy * sizeof(fftw_complex));
+    fieldK_0 = (fftw_complex *) malloc(uf.nKxloc*uf.nKy * sizeof(fftw_complex));
+
+
+/*    time_execute(uf, fieldX, fieldK, 100);*/
+
+
+
+    for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj)
+        {
+/*        fieldX[ii*uf.nX1+jj] = 2.;*/
+        fieldX[ii*uf.nX1+jj] = rand()/(double)RAND_MAX -0.5;
+        }
+
+    for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.nKy; ++jj)
+        {
+        fieldK[ii*uf.nKy+jj] = 0.;
+        }
+
+/* We have to project on the space available for this library */
+    fft2D(uf, fieldX, fieldK);
+    ifft2D(uf, fieldK, fieldX);
+
+
+
+    for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.nKy; ++jj)
+        fieldK_0[ii*uf.nKy+jj] = fieldK[ii*uf.nKy+jj];
+
+    for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj)
+        fieldX_0[ii*uf.nX1+jj] = fieldX[ii*uf.nX1+jj];
+
+
+
+    /* if (uf.rank==0)  */
+    /* { */
+/*    fieldK[0*uf.nKy+0] = 1.;*/
+
+/*    fieldK[0*uf.nKy+1] = 1.I;*/
+/*    fieldK[0*uf.nKy+3] = -1.I;*/
+
+    /* fieldK[0*uf.nKy+2] = 1.; */
+    /* } */
+
+
+/*    if (uf.rank==1) */
+/*    {*/
+/*    fieldK[0*uf.nKy+0] = 1.;*/
+
+/*    fieldK[0*uf.nKy+1] = 1.I;*/
+/*    fieldK[0*uf.nKy+3] = -1.I;*/
+
+/*    fieldK[0*uf.nKy+2] = 1.I;*/
+/*    }*/
+
+
+
+
+
+
+
+
+    time_execute(uf, fieldX, fieldK, 100);
+
+
+
+    for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.nKy; ++jj)
+        {
+        printf("%i , fieldK[%i*nKxloc + %i] = (%6.4f, %6.4f)\n", 
+               uf.rank, ii, jj,
+               creal(fieldK[ii*uf.nKy + jj]),  
+               cimag(fieldK[ii*uf.nKy + jj]));
+        }
+
+
+    for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj)
+        {
+        printf( "%d , fieldX[%d,%d] = %+6.4f\n", 
+                uf.rank, ii, jj, fieldX[ii*uf.nX1+jj]);
+        }
+
+    for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj)
+        {
+         printf("%d , (fieldX - fieldX_0)[%d,%d] = %+6.4f\n", 
+                 uf.rank, ii, jj, 
+                fieldX[ii*uf.nX1+jj]-fieldX_0[ii*uf.nX1+jj]);
+        }
+
+    energyX = 0.;
+    for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj)
+        {
+        energyX += fieldX[ii*uf.nX1+jj]*fieldX[ii*uf.nX1+jj];
+        }
+    energyX = energyX/uf.coef_norm;
+
+    printf( "energyX = %6.4f\n", energyX);
+
+
+    energyK = 0.;
+    for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.nKy; ++jj)
+        {
+        if (ii==0)
+            coef = 1;
+        else
+            coef = 2;
+        energyK += pow(cabs(fieldK[ii*uf.nKy + jj]), 2) *coef;
+        }
+
+     printf( "energyK = %6.4f\n", energyK);
+
+
+
+
+
+
+    free(fieldX);
+    free(fieldX_0);
+    free(fieldK);
+
+    destroy_Util_fft(uf);
+    }
+
+
+
diff --git a/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.h b/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.h
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZGNjeS9mZnQyRHNvbHZlcTJELmg=
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.h
@@ -0,0 +1,26 @@
+
+
+typedef struct 
+{
+  /* X and K denote physical and Fourier spaces. */
+  /* y corresponds to dim 0 in physical space */
+  /* x corresponds to dim 1 in physical space */
+  int N0, N1, nX0, nX1, nX0loc;
+  int ny, nx, nXyloc;
+  /* y corresponds to dim 1 in Fourier space */
+  /* x corresponds to dim 0 in Fourier space */
+  int nK0, nK1, nK0loc; 
+  int nKx, nKy, nKxloc;
+  int coef_norm;
+  fftw_plan plan_r2c, plan_c2c_fwd, plan_c2r, plan_c2c_bwd;
+  double *arrayX;
+  fftw_complex *arrayK_pR, *arrayK_pC;
+  unsigned flags;
+  int rank, nb_proc, irank;
+} Util_fft;
+
+Util_fft init_Util_fft(int N0, int N1);
+void destroy_Util_fft(Util_fft uf);
+void fft2D(Util_fft uf, double *fieldX, fftw_complex *fieldK);
+void ifft2D(Util_fft uf, fftw_complex *fieldK, double *fieldX);
+
diff --git a/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.pxd b/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZGNjeS9mZnQyRHNvbHZlcTJELnB4ZA==
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dccy/fft2Dsolveq2D.pxd
@@ -0,0 +1,34 @@
+
+cdef extern from "complex.h":
+    pass
+
+cdef extern from "fftw3.h":
+    ctypedef struct fftw_plan_s:
+        pass
+    ctypedef fftw_plan_s *fftw_plan
+
+cdef extern from "fft2Dmpisolveq2D.h":
+    ctypedef struct Util_fft:
+        # X and K denote physical and Fourier spaces
+        # y corresponds to dim 0 in physical space
+        # x corresponds to dim 1 in physical space
+        int N0, N1, nX0, nX1, nX0loc
+        int ny, nx, nXyloc
+        # y corresponds to dim 1 in Fourier space
+        # x corresponds to dim 0 in Fourier space
+        int nK0, nK1, nK0loc
+        int nKx, nKy, nKxloc
+        int coef_norm
+        fftw_plan plan_r2c, plan_c2c_fwd, plan_c2r, plan_c2c_bwd
+        double *arrayX
+        complex *arrayK_pR, *arrayK_pC
+        unsigned flags
+
+
+
+
+    Util_fft init_Util_fft(int N0, int N1)
+    void destroy_Util_fft(Util_fft uf)
+    void fft2D(Util_fft uf, double *fieldX, complex *fieldK)
+    void ifft2D(Util_fft uf, complex *fieldK, double *fieldX)
+
diff --git a/fluidsim/operators/fft/Sources_fftw2dmpiccy/fftw2dmpiccy.pyx b/fluidsim/operators/fft/Sources_fftw2dmpiccy/fftw2dmpiccy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZG1waWNjeS9mZnR3MmRtcGljY3kucHl4
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dmpiccy/fftw2dmpiccy.pyx
@@ -0,0 +1,308 @@
+
+
+cimport numpy as np
+import numpy as np
+np.import_array()
+
+from mpi4py import MPI
+from mpi4py cimport MPI
+from mpi4py.mpi_c cimport *
+
+# fix a bug arising when using a recent version of mpi4py
+cdef extern from 'mpi-compat.h': pass
+
+from cpython cimport Py_INCREF
+
+cimport libc
+from libc.stddef cimport ptrdiff_t
+
+from libc.stdlib cimport malloc, free
+
+# this refers to the .pxd file
+cimport libcfftw2dmpi
+
+cdef extern from "numpy/arrayobject.h":
+    object PyArray_SimpleNewFromData(int nd, int* dims,
+                                     int typenum, void* data)
+
+# we define python and c types for physical and Fourier spaces
+DTYPEb = np.uint8
+ctypedef np.uint8_t DTYPEb_t
+DTYPEi = np.int
+ctypedef np.int_t DTYPEi_t
+DTYPEf = np.float64
+ctypedef np.float64_t DTYPEf_t
+DTYPEc = np.complex128
+ctypedef np.complex128_t DTYPEc_t
+
+comm = MPI.COMM_WORLD
+
+cdef class FFT2Dmpi(object):
+    '''A FFT2Dmpi object is a wrapper to a c library
+doing 2D parallele fft which uses MPI and sequencial functions
+of the fftw library.
+'''
+    # number of nodes in the first and second dimensions
+    cdef int N0, N1
+    cdef public DTYPEb_t TRANSPOSED
+
+    cdef libcfftw2dmpi.Util_fft uf
+
+    # shape of the arrays in the physical and Fourier spaces,
+    # for the sequential case:
+    cdef public np.ndarray shapeX_seq, shapeK_seq, shapeK_gat
+    # and for the parallel case:
+    cdef public np.ndarray shapeX_loc, shapeK_loc
+
+    # the communicator, nb of processus and rank of the processus
+    cdef MPI.Comm comm
+    cdef public int nb_proc, rank
+
+    cdef public int iX0loc_start, iK1loc_start, iK0loc_start
+    cdef public int iKxloc_start, iKyloc_start
+
+    cdef public int idimx, idimy, idimkx, idimky
+
+    cdef DTYPEc_t *carrayK
+    cdef DTYPEf_t *carrayX
+
+    def __init__(self, int N0, int N1):
+
+        # info on MPI
+        self.comm = comm
+        self.nb_proc = self.comm.size
+        self.rank = self.comm.Get_rank()
+
+        if N0%2 != 0 or N1%2 != 0:
+            raise ValueError('conditions n0, n1 have to be even')
+
+        if N0%self.nb_proc != 0 or N1/2%self.nb_proc != 0:
+            raise ValueError(
+                'fftw2dmpiccy works only'
+                ' if N0%self.nb_proc==0 and N1/2%self.nb_proc==0')
+
+        self.N0 = N0
+        self.N1 = N1
+
+        # for sequenciel runs (not implemented with this library)
+        # the data in K space is not transposed
+        self.shapeX_seq = np.array([N0, N1])
+        self.shapeK_seq = np.array([N0, N1/2+1])
+
+        self.shapeK_gat = np.array([N1/2, N0])
+
+        # the figures 0 and 1 correspond to the dimension in physical space,
+        # the dimension 0 corresponds to the y-axes.
+        # and the dimension 1 corresponds to the x-axes.
+        self.idimx = 1
+        self.idimy = 0
+        self.idimkx = 0
+        self.idimky = 1
+
+        self.TRANSPOSED = 1
+
+        # initialisation of the fft
+        self.uf = libcfftw2dmpi.init_Util_fft(N0, N1)
+
+        self.shapeX_loc = np.array([self.uf.nX0loc, self.uf.nX1])
+        self.shapeK_loc = np.array([self.uf.nKxloc, self.uf.nKy])
+
+        self.iX0loc_start = self.uf.nX0loc*self.rank
+        self.iK0loc_start = self.uf.nK0loc*self.rank
+
+        self.iK1loc_start = 0
+
+        self.iKxloc_start = self.iK0loc_start
+        self.iKyloc_start = self.iK1loc_start
+
+        # allocation de carray
+        self.carrayX = <DTYPEf_t *> malloc(self.shapeX_loc.prod()
+                                           * sizeof(DTYPEf_t))
+        self.carrayK = <DTYPEc_t *> malloc(self.shapeK_loc.prod()
+                                           * sizeof(DTYPEc_t))
+
+
+    # cpdef fft2d(self, np.ndarray[DTYPEf_t, ndim=2] ffX):
+    cpdef fft2d(self, DTYPEf_t[:, :] ffX):
+        cdef np.ndarray[DTYPEf_t, ndim=2, mode="c"] ffX_cont
+        cdef np.ndarray[DTYPEc_t, ndim=2, mode="c"] ffK_cont
+        ffX_cont = np.ascontiguousarray(ffX, dtype=DTYPEf)
+        ffK_cont = np.empty(self.shapeK_loc, dtype=DTYPEc)
+        libcfftw2dmpi.fft2D(self.uf, &ffX_cont[0,0], &ffK_cont[0,0])
+        return ffK_cont
+
+    # cpdef ifft2d(self, np.ndarray[DTYPEc_t, ndim=2] ffK):
+    cpdef ifft2d(self, DTYPEc_t[:, :] ffK):
+        cdef np.ndarray[DTYPEc_t, ndim=2, mode="c"] ffK_cont
+        cdef np.ndarray[DTYPEf_t, ndim=2, mode="c"] ffX_cont
+        ffK_cont = np.ascontiguousarray(ffK, dtype=DTYPEc)
+        ffX_cont = np.empty(self.shapeX_loc, dtype=DTYPEf)
+        libcfftw2dmpi.ifft2D(self.uf, &ffK_cont[0,0], &ffX_cont[0,0])
+        return ffX_cont
+
+    def __dealloc__(self):
+        libcfftw2dmpi.destroy_Util_fft(self.uf)
+
+    def describe(self):
+        if self.rank == 0:
+            print 'object of class Myfft2Dmpi'
+            print 'N0 =', self.N0, 'N1 =', self.N1
+            print 'nb_proc =', self.nb_proc,
+            if self.nb_proc == 1:
+                print '=> sequenciel version'
+            else:
+                print '=> parallel version (MPI)'
+
+    def gather_Xspace(self, np.ndarray ff_loc,
+                      root=None, type DTYPE=DTYPEf):
+        cdef np.ndarray ff_seq
+
+        # self.shapeX_loc is the same for all processes,
+        # it is safe to use Allgather or Gather
+        if root is None:
+            ff_seq = np.empty(self.shapeX_seq, DTYPE)
+            self.comm.Allgather(ff_loc, ff_seq)
+        elif isinstance(root, int):
+            ff_seq = None
+            if self.rank == root:
+                ff_seq = np.empty(self.shapeX_seq, DTYPE)
+            self.comm.Gather(ff_loc, ff_seq, root=root)
+        else:
+            raise ValueError('root should be an int')
+        return ff_seq
+
+    def scatter_Xspace(self, np.ndarray ff_seq,
+                       int root=0, type DTYPE=DTYPEf):
+        cdef np.ndarray ff_loc
+        ff_loc = np.empty(self.shapeX_loc, dtype=DTYPE)
+        # self.shapeX_loc is the same for all processes,
+        # it is safe to use Scatter
+        if isinstance(root, int):
+            self.comm.Scatter(ff_seq, ff_loc, root=root)
+        else:
+            raise ValueError('root should be an int')
+        return ff_loc
+
+    def gather_Kspace(self, np.ndarray ff_fft_loc, root=None, AS_SEQ=True):
+        cdef np.ndarray ff_fft_seq
+        cdef np.ndarray shapeK_temp
+        rowtype = MPI.COMPLEX16.Create_contiguous(self.shapeK_loc[1])
+        rowtype.Commit()
+        if root is None:
+            ff_fft_gat = np.empty(self.shapeK_gat, dtype=DTYPEc)
+#            counts1 = self.comm.allgather(self.shapeK_loc[0])
+#            print 'counts1 =', counts1
+            counts = np.ones([self.nb_proc], dtype=int)*self.shapeK_loc[0]
+#            print 'counts =', counts
+            self.comm.Allgatherv(sendbuf=[ff_fft_loc, MPI.COMPLEX16],
+                                 recvbuf=[ff_fft_gat, (counts, None), rowtype])
+            if AS_SEQ:
+                ff_fft_gat = ff_fft_gat.transpose()
+                ff_fft_seq = np.empty(self.shapeK_seq, dtype=DTYPEc)
+                ff_fft_seq[:, :-1] = ff_fft_gat
+                ff_fft_seq[:, -1] = 0.
+                result = ff_fft_seq
+            else:
+                result = ff_fft_gat
+        elif isinstance(root, int):
+            ff_fft_gat = None
+            if self.rank == root:
+                ff_fft_gat = np.empty(self.shapeK_gat, dtype=DTYPEc)
+            counts = np.ones([self.nb_proc], dtype=int)*self.shapeK_loc[0]
+            self.comm.Gatherv(sendbuf=[ff_fft_loc, MPI.COMPLEX16],
+                              recvbuf=[ff_fft_gat, (counts, None), rowtype],
+                              root=root)
+            if AS_SEQ:
+                if self.rank == root:
+                    ff_fft_gat = ff_fft_gat.transpose()
+                    ff_fft_seq = np.empty(self.shapeK_seq, dtype=DTYPEc)
+                    ff_fft_seq[:, :-1] = ff_fft_gat
+                    ff_fft_seq[:, -1] = 0.
+                    result = ff_fft_seq
+                else:
+                    result = None
+            else:
+                result = ff_fft_gat
+        else:
+            raise ValueError('root should be an int')
+        rowtype.Free()
+        return result
+
+    def scatter_Kspace(self, np.ndarray ff_fft_seq, int root=0,
+                       AS_SEQ=True, type DTYPE=DTYPEc):
+        cdef np.ndarray ff_fft_loc
+        cdef np.ndarray shapeK_temp
+
+        if not isinstance(root, int):
+            raise ValueError('root should be an int')
+        if AS_SEQ and root == self.rank:
+            ff_fft_seq = ff_fft_seq[:, :-1]
+            ff_fft_seq = ff_fft_seq.transpose()
+
+        ff_fft_loc = np.empty(self.shapeK_loc, dtype=DTYPE)
+        # self.shapeX_loc is the same for all processes,
+        # it is safe to use Scatter
+        self.comm.Scatter(ff_fft_seq, ff_fft_loc, root=root)
+        return ff_fft_loc
+
+    # functions for initialisation of field
+    def constant_arrayK(self, value=None, dtype=complex, SHAPE='LOC'):
+        """Return a constant array in spectral space."""
+        if SHAPE == 'LOC':
+            shapeK = self.shapeK_loc
+        elif SHAPE == 'SEQ':
+            shapeK = self.shapeK_seq
+        else:
+            raise ValueError('SHAPE should be ''LOC'' of ''SEQ''')
+        if value is None:
+            field_lm = np.empty(self.shapeK, dtype=dtype)
+        elif value == 0:
+            field_lm = np.zeros(self.shapeK, dtype=dtype)
+        else:
+            field_lm = value*np.ones(self.shapeK, dtype=dtype)
+        return field_lm
+
+    def constant_arrayX(self, value=None, dtype=DTYPEf, SHAPE='LOC'):
+        """Return a constant array in real space."""
+        if SHAPE == 'LOC':
+            shapeX = self.shapeX_loc
+        elif SHAPE == 'SEQ':
+            shapeX = self.shapeX_seq
+        else:
+            raise ValueError('SHAPE should be ''LOC'' of ''SEQ''')
+        if value is None:
+            field = np.empty(shapeX, dtype=dtype)
+        elif value == 0:
+            field = np.zeros(shapeX, dtype=dtype)
+        else:
+            field = value*np.ones(shapeX, dtype=dtype)
+        return field
+
+    def project_fft_on_realX(self,
+                             np.ndarray[DTYPEc_t, ndim=2] f_fft):
+        """Project the given field in spectral space such as its
+        inverse fft is a real field."""
+        cdef np.uint32_t nky_seq
+        cdef np.uint32_t iky_ky0, iky_kyM,  ikx_kx0, ikx_kxM,
+        cdef np.uint32_t ikyp, ikyn
+        cdef DTYPEc_t f_kp_kx0, f_kn_kx0, f_kp_kxM, f_knp_kxM
+
+        nky_seq = self.shapeK_seq[0]
+        iky_kyM = nky_seq/2
+
+        if self.rank == 0:
+            # first, some values have to be real
+            f_fft[0, 0] = f_fft[0, 0].real
+            f_fft[0, iky_kyM] = f_fft[0, iky_kyM].real
+
+            # second, there are relations between some values
+            for ikyp in xrange(1, iky_kyM):
+                ikyn = nky_seq - ikyp
+
+                f_kp_kx0 = f_fft[0, ikyp]
+                f_kn_kx0 = f_fft[0, ikyn]
+
+                f_fft[0, ikyp] = (f_kp_kx0+f_kn_kx0.conjugate()
+                                  )/2
+                f_fft[0, ikyn] = ((f_kp_kx0+f_kn_kx0.conjugate()
+                                   )/2).conjugate()
diff --git a/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.c b/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.c
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZG1waWNjeS9saWJjZmZ0dzJkbXBpLmM=
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.c
@@ -0,0 +1,585 @@
+/* test_fftw3_2Dmpi_simple program 
+
+export USR_PERSO='/home/pierre/usr'
+export USR_PERSO='/scratch/augier/usr'
+
+Compiled with:
+mpicc -O3 fft2Dmpisolveq2D.c -I$USR_PERSO/include/ $USR_PERSO/lib/libfftw3.so -lm -o fft2Dmpisolveq2D
+
+Create the library:
+mpicc -c -fPIC fft2Dmpisolveq2D.c -I$USR_PERSO/include -o fft2Dmpisolveq2D.o
+mpicc fft2Dmpisolveq2D.o -shared -o libfft2Dmpisolveq2D.so $USR_PERSO/lib/libfftw3.so -lm
+mv libfft2Dmpisolveq2D.so $USR_PERSO/lib
+
+
+
+And on ferlin:
+mpicc -O3 fft2Dmpisolveq2D.c -I${FFTW_HOME}/double/include \
+-L${FFTW_HOME}/double/lib -lfftw3 -lm -o fft2Dmpisolveq2D
+
+Create the library:
+mpicc -c -fPIC fft2Dmpisolveq2D.c -I${FFTW_HOME}/double/include -o fft2Dmpisolveq2D.o
+
+mpicc -shared -o libfft2Dmpisolveq2D.so fft2Dmpisolveq2D.o \
+${FFTW_HOME}/double/lib/libfftw3.so -lm 
+
+
+
+
+
+execute with
+mpirun -np 2 ./fft2Dmpisolveq2D
+(the 2 after "-np" is the number of processors)
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <complex.h>
+#include <fftw3.h>
+#include <time.h>
+#include <sys/time.h>
+#include <mpi.h>
+
+#include <unistd.h>
+
+#include <math.h>
+
+#include "libcfftw2dmpi.h"
+
+
+
+
+Util_fft init_Util_fft(int N0, int N1)
+    {
+    Util_fft uf;
+    struct timeval start_time, end_time;
+    /* double total_usecs; */
+    int iX0;
+/* ii, jj, irank, iX0; */
+    int istride = 1, ostride = 1;
+    int howmany, sign;
+    MPI_Datatype MPI_type_complex;
+
+    /*DETERMINE RANK OF THIS PROCESSOR*/
+    MPI_Comm_rank(MPI_COMM_WORLD, &(uf.rank)); 
+    /*DETERMINE TOTAL NUMBER OF PROCESSORS*/
+    MPI_Comm_size(MPI_COMM_WORLD, &(uf.nb_proc));
+
+
+
+/*    if ((uf.rank)==0) printf("init_util_fft, N0 = %d, N1 = %d\n", N0, N1);*/
+
+    MPI_Barrier(MPI_COMM_WORLD);
+    uf.N0 = N0;
+    uf.N1 = N1;
+
+    /* y corresponds to dim 0 in physical space */
+    /* x corresponds to dim 1 in physical space */
+    uf.ny = N0;
+    uf.nx = N1;
+
+    uf.nX0 = N0;
+    uf.nX1 = N1;
+    uf.nX0loc = N0/uf.nb_proc;
+    uf.nXyloc = uf.nX0loc;
+
+    uf.nKx = uf.nx/2;
+    uf.nKxloc = uf.nKx/uf.nb_proc;
+    uf.nKy = uf.ny;
+
+    /* This 2D fft is transposed */
+    uf.nK0 = N1/2;
+    uf.nK0loc = uf.nK0/uf.nb_proc;
+    uf.nK1 = N0;
+
+    uf.coef_norm = N0*N1;
+
+    uf.flags = FFTW_MEASURE;
+/*    flags = FFTW_ESTIMATE;*/
+/*    uf.flags = FFTW_PATIENT;*/
+
+    uf.arrayX    = (double*) fftw_malloc(sizeof(double)*uf.nX0loc*N1);
+    uf.arrayK_pR = (fftw_complex*) fftw_malloc( sizeof(fftw_complex)
+                                                *uf.nX0loc*(uf.nKx+1));
+    uf.arrayK_pC = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*uf.nKxloc*N0);
+
+
+/*    if ((uf.rank)==0) printf("create plans\n");*/
+    gettimeofday(&start_time, NULL);
+/*    plan = fftw_plan_many_dft(int rank, const int *n, int howmany,*/
+/*                                  fftw_complex *in, const int *inembed,*/
+/*                                  int istride, int idist,*/
+/*                                  fftw_complex *out, const int *onembed,*/
+/*                                  int ostride, int odist,*/
+/*                                  int sign, unsigned flags);*/
+
+    howmany = uf.nX0loc;
+    uf.plan_r2c = fftw_plan_many_dft_r2c(   1, &N1, howmany,
+                                            uf.arrayX, NULL,
+                                            istride, N1,
+                                            uf.arrayK_pR, NULL,
+                                            ostride, uf.nKx+1,
+                                            uf.flags);
+
+    uf.plan_c2r = fftw_plan_many_dft_c2r(   1, &N1, howmany,
+                                            uf.arrayK_pR, NULL,
+                                            istride, uf.nKx+1,
+                                            uf.arrayX, NULL,
+                                            ostride, N1,
+                                            uf.flags);
+
+    howmany = uf.nKxloc;
+    sign = FFTW_FORWARD;
+    uf.plan_c2c_fwd = fftw_plan_many_dft(   1, &N0, howmany,
+                                            uf.arrayK_pC, &N0,
+                                            istride, N0,
+                                            uf.arrayK_pC, &N0,
+                                            ostride, N0,
+                                            sign, uf.flags);
+    sign = FFTW_BACKWARD;
+    uf.plan_c2c_bwd = fftw_plan_many_dft(   1, &N0, howmany,
+                                            uf.arrayK_pC, &N0,
+                                            istride, N0,
+                                            uf.arrayK_pC, &N0,
+                                            ostride, N0,
+                                            sign, uf.flags);
+
+    gettimeofday(&end_time, NULL);
+    /* total_usecs = (end_time.tv_sec-start_time.tv_sec) +  */
+    /*     (end_time.tv_usec-start_time.tv_usec)/1000000.; */
+/*    printf ("               done in %f s\n", */
+/*            total_usecs);*/
+
+    for (iX0=0;iX0<uf.nX0loc;iX0++)
+        uf.arrayK_pR[iX0*(uf.nKx+1)+uf.nKx] = 0.;
+
+
+/*    if ((uf.rank)==0) printf("print uf.arrayK_pR\n");*/
+/*    for (irank = 0; irank<uf.nb_proc; irank++)*/
+/*        {*/
+/*            MPI_Barrier(MPI_COMM_WORLD);*/
+/*            if (uf.rank == irank)*/
+/*            {*/
+/*            for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nKx+1; ++jj)*/
+/*                {*/
+/*                 printf("%i , uf.arrayK_pR[%i*(uf.nKx+1) + %i] = (%6.4f, %6.4f)\n", */
+/*                        uf.rank, ii, jj,*/
+/*                        creal(uf.arrayK_pR[ii*(uf.nKx+1) + jj]), */
+/*                        cimag(uf.arrayK_pR[ii*(uf.nKx+1) + jj]));*/
+/*                }*/
+/*            }*/
+/*            else usleep(200);*/
+/*        }*/
+
+
+
+/*     for (irank = 0; irank<uf.nb_proc; irank++) */
+/*       { */
+/* 	MPI_Barrier(MPI_COMM_WORLD); */
+/* 	if (uf.rank == irank) */
+/* 	  { */
+/* 	    printf( */
+/* "%i, N0:%i, N1:%i, nX0loc:%i, nXyloc:%i, nKx:%i, nK0:%i, nKy:%i, nK1:%i, nK0loc:%i, nKxloc:%i\n" */
+/* , */
+/* 		   uf.rank, */
+/* 		   N0, N1, */
+/* 		   uf.nX0loc, uf.nXyloc, */
+/* 		   uf.nKx, uf.nK0, */
+/* 		   uf.nKy, uf.nK1, */
+/* 		   uf.nK0loc, */
+/* 		   uf.nKxloc */
+/* 		   ); */
+/* 	  } */
+/* 	else usleep(200); */
+/*        } */
+
+
+
+    MPI_Type_contiguous( 2, MPI_DOUBLE, &MPI_type_complex );
+    MPI_Type_commit( &MPI_type_complex );
+
+/*    MPI_Type_vector(int count, int blocklength, int stride, */
+/*                    MPI_Datatype oldtype, MPI_Datatype *newtype);*/
+
+    MPI_Type_vector(uf.nX0loc, 1, uf.nKx+1, 
+                    MPI_type_complex, &(uf.MPI_type_column));
+    MPI_Type_create_resized(uf.MPI_type_column, 0, 
+                            sizeof(fftw_complex), 
+                            &(uf.MPI_type_column));
+    MPI_Type_commit( &(uf.MPI_type_column) );
+
+    MPI_Type_vector(uf.nKxloc, uf.nX0loc, uf.N0, 
+                    MPI_type_complex, &(uf.MPI_type_block));
+    MPI_Type_create_resized(uf.MPI_type_block, 0, 
+                            uf.nX0loc*sizeof(fftw_complex), 
+                            &(uf.MPI_type_block));
+    MPI_Type_commit( &(uf.MPI_type_block) );
+
+
+    return uf;
+}
+
+
+
+void destroy_Util_fft(Util_fft uf)
+    {
+/*    if ((uf.rank)==0) printf("destroy_util_fft\n");*/
+    fftw_destroy_plan(uf.plan_r2c);
+    fftw_destroy_plan(uf.plan_c2c_fwd);
+    fftw_destroy_plan(uf.plan_c2c_bwd);
+    fftw_destroy_plan(uf.plan_c2r);
+    fftw_free(uf.arrayX);
+    fftw_free(uf.arrayK_pR);
+    fftw_free(uf.arrayK_pC);
+    MPI_Type_free(&(uf.MPI_type_column));
+    MPI_Type_free(&(uf.MPI_type_block));
+}
+
+
+
+
+
+
+
+
+
+void fft2D(Util_fft uf, double *fieldX, fftw_complex *fieldK)
+    {
+      int ii;
+/* , jj, irank; */
+    /*use memcpy(void * destination, void * source, size_t bytes); */
+
+    memcpy(uf.arrayX, fieldX, uf.nX0loc*uf.nX1*sizeof(double));
+
+/*    if ((uf.rank)==0) printf("print uf.arrayX\n");*/
+/*    for (irank = 0; irank<uf.nb_proc; irank++)*/
+/*        {*/
+/*            MPI_Barrier(MPI_COMM_WORLD);*/
+/*            if (uf.rank == irank)*/
+/*            {*/
+/*            for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.N1; ++jj)*/
+/*                {*/
+/*                printf( "%d , uf.arrayX[%d,%d] = %6.4f\n", */
+/*                        uf.rank, ii, jj, uf.arrayX[ii*uf.N1+jj]);*/
+/*                }*/
+/*            }*/
+/*            else usleep(200);*/
+/*        }*/
+/*    MPI_Barrier(MPI_COMM_WORLD);*/
+
+    fftw_execute(uf.plan_r2c);
+
+/*    if ((uf.rank)==0) printf("print uf.arrayK_pR after alltoall\n");*/
+/*    for (irank = 0; irank<uf.nb_proc; irank++)*/
+/*        {*/
+/*            MPI_Barrier(MPI_COMM_WORLD);*/
+/*            if (uf.rank == irank)*/
+/*            {*/
+/*            for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nKx+1; ++jj)*/
+/*                {*/
+/*                 printf("%i , uf.arrayK_pR[%i*(uf.nKx+1) + %i] = (%6.4f, %6.4f)\n", */
+/*                        uf.rank, ii, jj,*/
+/*                        creal(uf.arrayK_pR[ii*(uf.nKx+1)+jj]), */
+/*                        cimag(uf.arrayK_pR[ii*(uf.nKx+1)+jj]));*/
+/*                }*/
+/*            }*/
+/*            else usleep(200);*/
+/*            MPI_Barrier(MPI_COMM_WORLD);*/
+/*        }*/
+/*    MPI_Barrier(MPI_COMM_WORLD);*/
+
+/*    second step: alltoall communication...*/
+    MPI_Alltoall(uf.arrayK_pR, uf.nKxloc, uf.MPI_type_column, 
+                 uf.arrayK_pC, 1, uf.MPI_type_block, 
+                 MPI_COMM_WORLD);
+
+
+/*    if ((uf.rank)==0) printf("print uf.arrayK_pC after alltoall\n");*/
+/*    for (irank = 0; irank<uf.nb_proc; irank++)*/
+/*        {*/
+/*            MPI_Barrier(MPI_COMM_WORLD);*/
+/*            if (uf.rank == irank)*/
+/*            {*/
+/*            for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.N0; ++jj)*/
+/*                {*/
+/*                 printf("%i , uf.arrayK_pC[%i*uf.N0 + %i] = (%6.4f, %6.4f)\n", */
+/*                        uf.rank, ii, jj,*/
+/*                        creal(uf.arrayK_pC[ii*uf.N0 + jj]), */
+/*                        cimag(uf.arrayK_pC[ii*uf.N0 + jj]));*/
+/*                }*/
+/*            }*/
+/*            else usleep(200);*/
+/*            MPI_Barrier(MPI_COMM_WORLD);*/
+/*        }*/
+/*    MPI_Barrier(MPI_COMM_WORLD);*/
+
+
+    fftw_execute(uf.plan_c2c_fwd);
+
+/*    if ((uf.rank)==0) printf("print uf.arrayK_pC after fftw_execute\n");*/
+/*    for (irank = 0; irank<uf.nb_proc; irank++)*/
+/*        {*/
+/*            MPI_Barrier(MPI_COMM_WORLD);*/
+/*            if (uf.rank == irank)*/
+/*            {*/
+/*            for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.N0; ++jj)*/
+/*                {*/
+/*                 printf("%i , uf.arrayK_pC[%i*uf.N0 + %i] = (%6.4f, %6.4f)\n", */
+/*                        uf.rank, ii, jj,*/
+/*                        creal(uf.arrayK_pC[ii*uf.N0 + jj]), */
+/*                        cimag(uf.arrayK_pC[ii*uf.N0 + jj]));*/
+/*                }*/
+/*            }*/
+/*            else usleep(200);*/
+/*            MPI_Barrier(MPI_COMM_WORLD);*/
+/*        }*/
+/*    MPI_Barrier(MPI_COMM_WORLD);*/
+
+
+    for (ii=0; ii<uf.nKxloc*uf.nKy; ii++)
+        fieldK[ii]  = uf.arrayK_pC[ii]/uf.coef_norm;
+
+    }
+
+
+
+
+void ifft2D(Util_fft uf, fftw_complex *fieldK, double *fieldX)
+    {
+      int ii;
+/* , jj, irank; */
+    /*use memcpy(void * destination, void * source, size_t bytes); */
+    memcpy(uf.arrayK_pC, fieldK, uf.nKxloc*uf.nKy*sizeof(fftw_complex));
+    fftw_execute(uf.plan_c2c_bwd);
+    MPI_Alltoall(   uf.arrayK_pC, 1, uf.MPI_type_block,
+                    uf.arrayK_pR, uf.nKxloc, uf.MPI_type_column, 
+                    MPI_COMM_WORLD);
+
+    /*These modes (nx/2+1=N1/2+1) have to be settled to zero*/
+    for (ii = 0; ii < uf.nX0loc; ++ii) 
+        uf.arrayK_pR[ii*(uf.nKx+1) + uf.nKx] = 0.;
+
+    fftw_execute(uf.plan_c2r);
+    memcpy(fieldX,uf.arrayX, uf.nX0loc*uf.nX1*sizeof(double));
+    }
+
+
+void time_execute(Util_fft uf, double *fieldX, fftw_complex *fieldK, int nb_time_execute)
+    {
+    int ii;
+    struct timeval start_time, end_time;
+    double total_usecs;
+
+    if (uf.rank==0) printf("timer...\n");
+    gettimeofday(&start_time, NULL);
+    for (ii=0; ii<nb_time_execute; ii++)
+    {
+        fft2D(uf, fieldX, fieldK);
+        ifft2D(uf, fieldK, fieldX);
+    }
+    gettimeofday(&end_time, NULL);
+
+
+    total_usecs = (end_time.tv_sec-start_time.tv_sec) + 
+        (end_time.tv_usec-start_time.tv_usec)/1000000.;
+    printf ("%d times forward and backward sequencial FFT: %f s\n", 
+            nb_time_execute ,total_usecs); 
+}
+
+
+
+int main(int argc, char **argv)
+    {
+    const int N0 = 32*4*2, N1 = 32*4*2;
+    Util_fft uf;
+    double * fieldX;
+/* , * fieldX_0; */
+    fftw_complex * fieldK;
+/* , * fieldK_0; */
+    int ii, jj;
+/* , irank; */
+    double energyK, energyX, energy2;
+    int coef;
+
+
+    MPI_Init(&argc, &argv);
+    uf = init_Util_fft(N0,N1);
+
+    srand(time(NULL)+uf.rank*uf.nb_proc);
+
+    printf( "I'm rank (processor number) %i of size %i\n", 
+            uf.rank, uf.nb_proc);
+
+    fieldX = (double *) malloc(uf.nX0loc*uf.nX1 * sizeof(double));
+    /* fieldX_0 = (double *) malloc(uf.nX0loc*uf.nX1 * sizeof(double)); */
+    fieldK = (fftw_complex *) malloc(uf.nKxloc*uf.nKy * sizeof(fftw_complex));
+    /* fieldK_0 = (fftw_complex *) malloc(uf.nKxloc*uf.nKy * sizeof(fftw_complex)); */
+
+
+/*    time_execute(uf, fieldX, fieldK, 100);*/
+
+
+
+    for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj)
+        {
+/*        fieldX[ii*uf.nX1+jj] = 2.;*/
+        fieldX[ii*uf.nX1+jj] = rand()/(double)RAND_MAX -0.5;
+        }
+
+    for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.nKy; ++jj)
+        {
+        fieldK[ii*uf.nKy+jj] = 0.;
+        }
+
+/* We have to project on the space available for this library */
+    fft2D(uf, fieldX, fieldK);
+    ifft2D(uf, fieldK, fieldX);
+
+
+
+    /* for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.nKy; ++jj) */
+    /*     fieldK_0[ii*uf.nKy+jj] = fieldK[ii*uf.nKy+jj]; */
+
+    /* for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj) */
+    /*     fieldX_0[ii*uf.nX1+jj] = fieldX[ii*uf.nX1+jj]; */
+
+
+
+    /* if (uf.rank==0)  */
+    /* { */
+/*    fieldK[0*uf.nKy+0] = 1.;*/
+
+/*    fieldK[0*uf.nKy+1] = 1.I;*/
+/*    fieldK[0*uf.nKy+3] = -1.I;*/
+
+    /* fieldK[0*uf.nKy+2] = 1.; */
+    /* } */
+
+
+/*    if (uf.rank==1) */
+/*    {*/
+/*    fieldK[0*uf.nKy+0] = 1.;*/
+
+/*    fieldK[0*uf.nKy+1] = 1.I;*/
+/*    fieldK[0*uf.nKy+3] = -1.I;*/
+
+/*    fieldK[0*uf.nKy+2] = 1.I;*/
+/*    }*/
+
+
+
+
+
+
+
+
+    /* time_execute(uf, fieldX, fieldK, 100); */
+
+
+    /* usleep(100); */
+    /* for (irank = 0; irank<uf.nb_proc; irank++) */
+    /*     { */
+    /*         MPI_Barrier(MPI_COMM_WORLD); */
+    /*         if (uf.rank == irank) */
+    /*         { */
+    /*         for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.nKy; ++jj) */
+    /*         { */
+    /*              printf("%i , fieldK[%i*nKxloc + %i] = (%6.4f, %6.4f)\n",  */
+    /*                     uf.rank, ii, jj, */
+    /*                     creal(fieldK[ii*uf.nKy + jj]),  */
+    /*                     cimag(fieldK[ii*uf.nKy + jj])); */
+    /*         } */
+    /*         } */
+    /*         else usleep(100); */
+    /*         MPI_Barrier(MPI_COMM_WORLD); */
+    /*     } */
+    /* MPI_Barrier(MPI_COMM_WORLD); */
+
+
+
+
+
+
+
+    /* for (irank = 0; irank<uf.nb_proc; irank++) */
+    /*     { */
+    /*         MPI_Barrier(MPI_COMM_WORLD); */
+    /*         if (uf.rank == irank) */
+    /*         { */
+    /*         for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj) */
+    /*             { */
+    /*             printf( "%d , fieldX[%d,%d] = %+6.4f\n",  */
+    /*                     uf.rank, ii, jj, fieldX[ii*uf.nX1+jj]); */
+    /*             } */
+    /*         } */
+    /*         MPI_Barrier(MPI_COMM_WORLD); */
+    /*     } */
+    /* MPI_Barrier(MPI_COMM_WORLD); */
+
+    /* for (irank = 0; irank<uf.nb_proc; irank++) */
+    /*     { */
+    /*         MPI_Barrier(MPI_COMM_WORLD); */
+    /*         if (uf.rank == irank) */
+    /*         { */
+    /*         for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj) */
+    /*             { */
+    /*             printf("%d , (fieldX - fieldX_0)[%d,%d] = %+6.4f\n",  */
+    /*                    uf.rank, ii, jj,  */
+    /*                    fieldX[ii*uf.nX1+jj]-fieldX_0[ii*uf.nX1+jj]); */
+    /*             } */
+    /*         } */
+    /*         MPI_Barrier(MPI_COMM_WORLD); */
+    /*     } */
+    /* MPI_Barrier(MPI_COMM_WORLD); */
+
+
+
+
+
+
+
+
+    energyX = 0.;
+    for (ii = 0; ii < uf.nX0loc; ++ii) for (jj = 0; jj < uf.nX1; ++jj)
+        {
+
+        energyX += fieldX[ii*uf.nX1+jj]*fieldX[ii*uf.nX1+jj];
+        }
+    energyX = energyX/uf.coef_norm;
+    energy2 = energyX;
+    MPI_Reduce(&energy2, &energyX, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
+    if (uf.rank==0) printf( "energyX = %8.6f\n", energyX);
+
+
+    energyK = 0.;
+    for (ii = 0; ii < uf.nKxloc; ++ii) for (jj = 0; jj < uf.nKy; ++jj)
+        {
+	  if ((uf.rank==0) & (ii==0))
+            coef = 1;
+        else
+            coef = 2;
+        energyK += pow(cabs(fieldK[ii*uf.nKy + jj]), 2) *coef;
+        }
+    energy2 = energyK;
+
+    MPI_Reduce(&energy2, &energyK, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
+    if (uf.rank==0) printf( "energyK = %8.6f\n", energyK);
+
+
+
+
+
+
+    free(fieldX);
+    /* free(fieldX_0); */
+    free(fieldK);
+
+    destroy_Util_fft(uf);
+    MPI_Finalize();
+
+    return 0;
+    }
+
+
+
diff --git a/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.h b/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.h
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZG1waWNjeS9saWJjZmZ0dzJkbXBpLmg=
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.h
@@ -0,0 +1,28 @@
+
+
+typedef struct 
+{
+  /* X and K denote physical and Fourier spaces. */
+  /* y corresponds to dim 0 in physical space */
+  /* x corresponds to dim 1 in physical space */
+  int N0, N1, nX0, nX1, nX0loc;
+  int ny, nx, nXyloc;
+  /* y corresponds to dim 1 in Fourier space */
+  /* x corresponds to dim 0 in Fourier space */
+  int nK0, nK1, nK0loc; 
+  int nKx, nKy, nKxloc;
+  int coef_norm;
+  fftw_plan plan_r2c, plan_c2c_fwd, plan_c2r, plan_c2c_bwd;
+  double *arrayX;
+  fftw_complex *arrayK_pR, *arrayK_pC;
+
+  unsigned flags;
+  int rank, nb_proc, irank;
+  MPI_Datatype MPI_type_column, MPI_type_block;
+} Util_fft;
+
+Util_fft init_Util_fft(int N0, int N1);
+void destroy_Util_fft(Util_fft uf);
+void fft2D(Util_fft uf, double *fieldX, fftw_complex *fieldK);
+void ifft2D(Util_fft uf, fftw_complex *fieldK, double *fieldX);
+
diff --git a/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.pxd b/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZG1waWNjeS9saWJjZmZ0dzJkbXBpLnB4ZA==
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dmpiccy/libcfftw2dmpi.pxd
@@ -0,0 +1,35 @@
+
+from mpi4py cimport MPI
+
+cdef extern from "complex.h":
+    pass
+
+cdef extern from "fftw3.h":
+    ctypedef struct fftw_plan_s:
+        pass
+    ctypedef fftw_plan_s *fftw_plan
+
+cdef extern from "libcfftw2dmpi.h":
+    ctypedef struct Util_fft:
+        # X and K denote physical and Fourier spaces
+        # y corresponds to dim 0 in physical space
+        # x corresponds to dim 1 in physical space
+        int N0, N1, nX0, nX1, nX0loc
+        int ny, nx, nXyloc
+        # y corresponds to dim 1 in Fourier space
+        # x corresponds to dim 0 in Fourier space
+        int nK0, nK1, nK0loc
+        int nKx, nKy, nKxloc
+        int coef_norm
+        fftw_plan plan_r2c, plan_c2c_fwd, plan_c2r, plan_c2c_bwd
+        double *arrayX
+        complex *arrayK_pR
+        complex *arrayK_pC
+        unsigned flags
+        int rank, nb_proc, irank
+        MPI.MPI_Datatype MPI_type_column, MPI_type_block
+
+    Util_fft init_Util_fft(int N0, int N1)
+    void destroy_Util_fft(Util_fft uf)
+    void fft2D(Util_fft uf, double *fieldX, complex *fieldK)
+    void ifft2D(Util_fft uf, complex *fieldK, double *fieldX)
diff --git a/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw2dmpicy.pyx b/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw2dmpicy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZG1waWN5L2ZmdHcyZG1waWN5LnB5eA==
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw2dmpicy.pyx
@@ -0,0 +1,594 @@
+
+
+from __future__ import division, print_function
+
+from time import sleep
+
+cimport numpy as np
+import numpy as np
+np.import_array()
+
+
+try:
+    from mpi4py import MPI
+except ImportError:
+    nb_proc = 1
+    rank = 0
+else:
+    comm = MPI.COMM_WORLD
+    nb_proc = comm.size
+    rank = comm.Get_rank()
+
+IF MPI4PY:
+    from mpi4py cimport MPI
+    from mpi4py.mpi_c cimport *
+
+    # solve an incompatibility between openmpi and mpi4py versions
+    cdef extern from 'mpi-compat.h':
+        pass
+
+from cpython cimport Py_INCREF
+
+cimport libc
+from libc.stddef cimport ptrdiff_t
+
+cimport fftw3
+from fftw3 cimport fftw_iodim, FFTW_FORWARD, FFTW_BACKWARD, \
+    FFTW_MEASURE, FFTW_DESTROY_INPUT, FFTW_UNALIGNED, \
+    FFTW_CONSERVE_MEMORY, FFTW_EXHAUSTIVE, FFTW_PRESERVE_INPUT, \
+    FFTW_PATIENT, FFTW_ESTIMATE, FFTW_MPI_TRANSPOSED_IN, \
+    FFTW_MPI_TRANSPOSED_OUT, fftw_plan, FFTW_MPI_DEFAULT_BLOCK
+
+IF MPI4PY:
+    cimport fftw3mpi
+
+
+fftw_flags = {'FFTW_CONSERVE_MEMORY': FFTW_CONSERVE_MEMORY,
+              'FFTW_DESTROY_INPUT': FFTW_DESTROY_INPUT,
+              'FFTW_ESTIMATE': FFTW_ESTIMATE,
+              'FFTW_EXHAUSTIVE': FFTW_EXHAUSTIVE,
+              'FFTW_MEASURE': FFTW_MEASURE,
+              'FFTW_MPI_TRANSPOSED_IN': FFTW_MPI_TRANSPOSED_IN,
+              'FFTW_MPI_TRANSPOSED_OUT': FFTW_MPI_TRANSPOSED_OUT,
+              'FFTW_PATIENT': FFTW_PATIENT,
+              'FFTW_PRESERVE_INPUT': FFTW_PRESERVE_INPUT,
+              'FFTW_UNALIGNED': FFTW_UNALIGNED}
+
+from cpython.ref cimport PyTypeObject
+
+cdef extern from "numpy/arrayobject.h":
+    object PyArray_NewFromDescr(PyTypeObject * subtype, np.dtype descr,
+                                int nd, np.npy_intp* dims, np.npy_intp* strides,
+                                void* data, int flags, object obj)
+    object PyArray_SimpleNewFromData(int nd, int* dims, int typenum,void* data)
+
+
+# we define python and c types for physical and Fourier spaces
+DTYPEb = np.uint8
+ctypedef np.uint8_t DTYPEb_t
+DTYPEi = np.int
+ctypedef np.int_t DTYPEi_t
+DTYPEf = np.float64
+ctypedef np.float64_t DTYPEf_t
+DTYPEc = np.complex128
+ctypedef np.complex128_t DTYPEc_t
+
+
+cdef class FFT2Dmpi(object):
+    """The FFT2Dmpi class is a cython wrapper for the 2D fast Fourier
+    transform (sequencial and MPI) of the fftw library."""
+    # number of nodes in the first and second dimensions
+    cdef int n0, n1
+    # flags for fftw
+    cdef int flags
+    cdef public DTYPEb_t TRANSPOSED, SEQUENCIAL
+
+    # coef for normalization
+    cdef int coef_norm
+
+    cdef np.ndarray arrayX, arrayK
+
+    cdef complex *carrayK
+    cdef DTYPEf_t *carrayX
+
+    cdef fftw_plan plan_forward
+    cdef fftw_plan plan_backward
+
+    # shape of the arrays in the physical and Fourier spaces,
+    # for the sequential case:
+    cdef public np.ndarray shapeX_seq, shapeK_seq, shapeK_gat
+    # and for the parallel case:
+    cdef public np.ndarray shapeX_loc, shapeK_loc, shapeX_locpad
+
+    # the communicator, nb of processus and rank of the processus
+    IF MPI4PY:
+        cdef MPI.Comm comm
+
+    cdef public int nb_proc, rank
+
+    cdef public ptrdiff_t iX0loc_start, iKxloc_start, iKyloc_start
+
+    cdef public int idimx, idimy, idimkx, idimky
+
+    cdef size_t n_alloc_local
+
+    def __init__(self, int n0, int n1, flags=['FFTW_MEASURE'],
+                 TRANSPOSED=True, SEQUENCIAL=None):
+
+        if TRANSPOSED is None:
+            TRANSPOSED = True
+
+        if nb_proc == 1 or SEQUENCIAL:
+            self.SEQUENCIAL = True
+            if SEQUENCIAL and rank == 0 and nb_proc > 1:
+                print('    sequencial version even though self.nb_proc > 1')
+        else:
+            self.SEQUENCIAL = False
+
+        # info on MPI
+        self.nb_proc = nb_proc
+        self.rank = rank
+        if self.nb_proc > 1:
+            self.comm = comm
+
+        if n0 % 2 != 0 or n1 % 2 != 0:
+            raise ValueError('conditions n0 and n1 even not fulfill')
+
+        if not self.SEQUENCIAL and n0//2 + 1 < nb_proc:
+            raise ValueError('condition nx//2+1 >= nb_proc not fulfill')
+
+        self.n0 = n0
+        self.n1 = n1
+
+        self.shapeX_seq = np.array([n0, n1])
+        self.shapeK_seq = np.array([n0, n1//2+1])
+
+        # print('shapeX_seq:', shapeX_seq, '\nshapeK_seq:', shapeK_seq)
+
+        if self.nb_proc == 1 or SEQUENCIAL:
+            TRANSPOSED = False
+
+        self.TRANSPOSED = TRANSPOSED
+
+        # we consider that the first dimension corresponds to the x-axes.
+        # and the second dimension corresponds to the y-axes.
+        self.idimx = 1
+        self.idimy = 0
+
+        if self.TRANSPOSED:
+            self.idimkx = 0
+            self.idimky = 1
+            self.shapeK_gat = np.array([n1//2+1, n0])
+        else:
+            self.idimkx = 1
+            self.idimky = 0
+            self.shapeK_gat = np.array([n0, n1//2+1])
+
+        for f in flags:
+            self.flags = self.flags | fftw_flags[f]
+
+        self.coef_norm = n0*n1
+
+        # Allocate the carrays and create the plans
+        # and create the np arrays pointing to the carrays
+        if self.nb_proc == 1 or SEQUENCIAL:
+            self.init_seq()
+        else:
+            self.init_parall()
+
+    cdef init_seq(self):
+        """
+        Allocate the carrays, create the plans (for sequential FFTW)
+        and create np arrays pointing on the carrays
+        """
+
+        # print('init_seq:', self.shapeX_seq, self.shapeK_seq)
+
+        # from fluiddyn.util.debug_with_ipython import ipydebug
+        # ipydebug()
+
+        self.shapeX_loc = self.shapeX_seq
+        self.shapeK_loc = self.shapeK_seq
+        self.iKxloc_start = 0
+        self.iKyloc_start = 0
+        self.n_alloc_local = self.shapeK_loc.prod()
+
+        # print('self.n_alloc_local:', self.n_alloc_local)
+
+        self.carrayK = fftw3.fftw_alloc_complex(<size_t> self.shapeK_loc.prod())
+        self.carrayX = fftw3.fftw_alloc_real(<size_t>  self.shapeX_loc.prod())
+
+        # print('after alloc')
+
+        self.plan_forward = fftw3.fftw_plan_dft_r2c_2d(self.n0, self.n1,
+                                                       <double*> self.carrayX,
+                                                       <complex*> self.carrayK,
+                                                       self.flags)
+        self.plan_backward = fftw3.fftw_plan_dft_c2r_2d(self.n0, self.n1,
+                                                        <complex*> self.carrayK,
+                                                        <double*> self.carrayX,
+                                                        self.flags)
+
+        # print(self.n0, self.n1, self.shapeX_loc.data)
+        # print('after planning, self.flags:', self.flags)
+
+        self.arrayX = PyArray_SimpleNewFromData(
+            <int> 2, <np.npy_intp *> self.shapeX_loc.data,
+            np.NPY_FLOAT64, <void*> self.carrayX)
+        # print('after first PyArray_SimpleNewFromData')
+
+        self.arrayK = PyArray_SimpleNewFromData(
+            <int> 2, <np.npy_intp *> self.shapeK_loc.data,
+            np.NPY_COMPLEX128, <void*> self.carrayK)
+
+        # print('after PyArray_SimpleNewFromData')
+
+    IF MPI4PY:
+
+        cdef init_parall(self):
+            """
+            Allocate the carrays, create the plans (for MPI FFTW)
+            and create the np arrays pointing to the carrays
+            """
+            cdef MPI_Comm c_comm = self.comm.ob_mpi
+            cdef int flags_temp
+            cdef size_t n_alloc_local
+            cdef ptrdiff_t nX0loc, nKxloc
+
+    #        if self.rank==0: print 'self.init_parall()'
+            fftw3mpi.fftw_mpi_init()
+
+            if not self.TRANSPOSED:
+                n_alloc_local = fftw3mpi.fftw_mpi_local_size_2d(
+                    <size_t> self.n0, <size_t> self.n1//2+1,
+                    c_comm,
+                    &nX0loc, &self.iX0loc_start)
+                self.shapeK_loc = np.array([nX0loc, self.n1//2+1])
+                self.iKxloc_start = 0
+                self.iKyloc_start = self.iX0loc_start
+            else:
+                n_alloc_local = fftw3mpi.fftw_mpi_local_size_2d_transposed(
+                    <size_t> self.n0, <size_t> self.n1//2+1,
+                    c_comm,
+                    &nX0loc, &self.iX0loc_start,
+                    &nKxloc, &self.iKxloc_start)
+                self.shapeK_loc = np.array([nKxloc, self.n0])
+                self.iKyloc_start = 0
+
+            self.n_alloc_local = n_alloc_local
+
+            self.shapeX_loc = np.array([nX0loc, self.n1])
+
+            self.shapeX_locpad     = self.shapeX_loc.copy()
+            self.shapeX_locpad[-1] = 2*(self.shapeX_loc[-1]//2+1)
+
+            self.carrayK = fftw3.fftw_alloc_complex(n_alloc_local)
+            self.carrayX = fftw3.fftw_alloc_real(2 * n_alloc_local)
+
+    ####        for r in xrange(self.nb_proc):
+    ####            self.comm.barrier()
+    ####            sleep(0.05)
+    ####            if self.rank==r:
+    ####                print  'rank =', self.rank, 'n_alloc_local =', n_alloc_local,\
+    ####                        'other:', nX0loc, self.iX0loc_start,\
+    ####                         self.iKxloc_start, self.iKyloc_start
+    ####                print  'self.shapeX_locpad =', self.shapeX_locpad
+    ####                print  'self.shapeX_loc    =', self.shapeX_loc
+    ####                print  'self.shapeK_loc    =', self.shapeK_loc
+
+            if self.TRANSPOSED:
+                flags_temp = self.flags | fftw_flags['FFTW_MPI_TRANSPOSED_OUT']
+            else:
+                flags_temp = self.flags
+            self.plan_forward = fftw3mpi.fftw_mpi_plan_dft_r2c_2d(  
+                <size_t> self.n0, <size_t> self.n1,
+                <double*> self.carrayX,
+                <complex*> self.carrayK,
+                c_comm,
+                flags_temp)
+
+            if self.TRANSPOSED:
+                flags_temp = self.flags | fftw_flags['FFTW_MPI_TRANSPOSED_IN']
+            else:
+                flags_temp = self.flags
+            self.plan_backward = fftw3mpi.fftw_mpi_plan_dft_c2r_2d(
+                <size_t> self.n0, <size_t> self.n1,
+                <complex*> self.carrayK,
+                <double*> self.carrayX,
+                c_comm,
+                flags_temp)
+
+            # create the python arrays (arrayX and arrayK) pointing
+            # toward carrayX and carrayK
+            cdef np.dtype npDTYPEf = np.dtype('float64')
+            Py_INCREF(npDTYPEf)
+            cdef np.ndarray[DTYPEi_t, ndim=1] stridesX
+            # stridesX en nb elements
+            stridesX = np.array([self.shapeX_locpad[1], 1])
+            # stridesX en octet
+            stridesX *= npDTYPEf.itemsize
+            cdef int ndim = 2
+            self.arrayX = PyArray_NewFromDescr(
+                <PyTypeObject *> np.ndarray,
+                npDTYPEf, ndim,
+                <np.npy_intp *> self.shapeX_loc.data,
+                <np.npy_intp *> stridesX.data,
+                <void *> self.carrayX,
+                np.NPY_DEFAULT, None)
+
+            self.arrayK = PyArray_SimpleNewFromData(
+                <int> 2, <np.npy_intp *> self.shapeK_loc.data,
+                np.NPY_COMPLEX128, <void*> self.carrayK)
+
+    # cpdef fft2d(self, np.ndarray[DTYPEf_t, ndim=2] ff):
+    cpdef fft2d(self, DTYPEf_t[:, :] ff):
+        self.arrayX[:] = ff
+        fftw3.fftw_execute(self.plan_forward)
+        return self.arrayK/self.coef_norm
+
+    # cpdef ifft2d(self, np.ndarray[DTYPEc_t, ndim=2] ff_fft):
+    cpdef ifft2d(self, DTYPEc_t[:, :] ff_fft):
+        """Inverse Fast Fourier Transform 2D
+
+        This is THE function where most of the time is spent !
+        """
+        # print ff_fft
+        self.arrayK[:] = ff_fft
+        # self.print_carrayK()
+        fftw3.fftw_execute(self.plan_backward)
+        # self.print_carrayX()
+        # print self.arrayX
+#### result = self.arrayX.copy()     # BUG with pelvoux !!! (python 2.6)
+#### result = self.arrayX*1          # works, but maybe slower ?
+        return self.arrayX.copy()
+
+    def __dealloc__(self):
+        if self.nb_proc == 1:
+            fftw3.fftw_destroy_plan(self.plan_forward)
+            fftw3.fftw_destroy_plan(self.plan_backward)
+            fftw3.fftw_free(self.carrayX)
+            fftw3.fftw_free(self.carrayK)
+        else:
+            IF MPI4PY:
+                fftw3mpi.fftw_mpi_cleanup()
+
+    def print_carrayX(self):
+        cdef int ii, r
+        for r in xrange(self.nb_proc):
+            self.comm.barrier()
+            sleep(0.05)
+            if self.rank == r:
+                for ii in xrange(2*self.n_alloc_local):
+                    print(self.rank, ii,
+                          ' self.carrayX[ii] =', self.carrayX[ii])
+
+    def print_carrayK(self):
+        cdef int ii, r
+        for r in xrange(self.nb_proc):
+            self.comm.barrier()
+            sleep(0.05)
+            if self.rank == r:
+                for ii in xrange(self.n_alloc_local):
+                    print(self.rank, ii, ' self.carrayK[ii] =',
+                          self.carrayK[ii])
+
+    def describe(self):
+        if self.rank == 0:
+            print('object of class ', self.__class__,
+                  '\nn0 =', self.n0, 'n1 =', self.n1,
+                  '\nTRANSPOSED =', self.TRANSPOSED,
+                  '\nnb_proc =', self.nb_proc)
+            if self.nb_proc == 1:
+                print('=> sequenciel version')
+            else:
+                print('=> parallel version (MPI)')
+
+    def gather_Xspace(self, np.ndarray ff_loc,
+                      root=None, type DTYPEf=None):
+        cdef np.ndarray ff_seq
+        if DTYPEf is None:
+            DTYPEf = DTYPEf
+        # self.shapeX_loc is the same for all processes,
+        # it is safe to use Allgather or Gather
+        if root is None:
+            ff_seq = np.empty(self.shapeX_seq, DTYPEf)
+            self.comm.Allgather(ff_loc, ff_seq)
+        elif isinstance(root, int):
+            ff_seq = None
+            if self.rank == root:
+                ff_seq = np.empty(self.shapeX_seq, DTYPEf)
+            self.comm.Gather(ff_loc, ff_seq, root=root)
+        else:
+            raise ValueError('root should be an int')
+        return ff_seq
+
+    def scatter_Xspace(self, np.ndarray ff_seq,
+                       int root=0, type DTYPEf=None):
+        cdef np.ndarray ff_loc
+        if DTYPEf is None:
+            DTYPEf = DTYPEf
+        ff_loc = np.empty(self.shapeX_loc, dtype=DTYPEf)
+        # self.shapeX_loc is the same for all processes,
+        # it is safe to use Scatter
+        if isinstance(root, int):
+            self.comm.Scatter(ff_seq, ff_loc, root=root)
+        else:
+            raise ValueError('root should be an int')
+        return ff_loc
+
+    def gather_Kspace(self, np.ndarray ff_fft_loc, root=None):
+        cdef np.ndarray ff_fft_seq
+        cdef np.ndarray shapeK_temp
+
+        if self.TRANSPOSED is True:
+            shapeK_temp = np.empty(2)
+            shapeK_temp[0] = self.shapeK_seq[1]
+            shapeK_temp[1] = self.shapeK_seq[0]
+        else:
+            shapeK_temp = self.shapeK_seq
+        rowtype = MPI.COMPLEX16.Create_contiguous(self.shapeK_loc[1])
+        rowtype.Commit()
+        if root is None:
+            ff_fft_seq = np.empty(shapeK_temp, dtype=DTYPEc)
+            counts = self.comm.allgather(self.shapeK_loc[0])
+            self.comm.Allgatherv(sendbuf=[ff_fft_loc, MPI.COMPLEX16],
+                                 recvbuf=[ff_fft_seq, (counts, None), rowtype])
+            if self.TRANSPOSED is True:
+                ff_fft_seq = ff_fft_seq.transpose()
+        elif isinstance(root, int):
+            ff_fft_seq = None
+            if self.rank == root:
+                ff_fft_seq = np.empty(shapeK_temp, dtype=DTYPEc)
+            counts = self.comm.gather(self.shapeK_loc[0], root=root)
+            self.comm.Gatherv(sendbuf=[ff_fft_loc, MPI.COMPLEX16],
+                              recvbuf=[ff_fft_seq, (counts, None), rowtype],
+                              root=root)
+            if self.rank == root and (self.TRANSPOSED is True):
+                ff_fft_seq = ff_fft_seq.transpose()
+        else:
+            raise ValueError('root should be an int')
+        rowtype.Free()
+        return ff_fft_seq
+
+
+    # ATTENTION C'EST FAUX !!!!!!
+    # C'EST LA VERSION DE CCYLIB !!!!!!
+    # IL FAUDRA ECRIRE CA COMME IL FAUT
+    def scatter_Kspace(self, np.ndarray ff_fft_seq, int root=0,
+                       AS_SEQ=True, type DTYPE=DTYPEc):
+        cdef np.ndarray ff_fft_loc
+        cdef np.ndarray shapeK_temp
+
+        if not isinstance(root, int):
+            raise ValueError('root should be an int')
+
+        if AS_SEQ and root == self.rank:
+            ff_fft_seq = ff_fft_seq[:, :-1]
+            ff_fft_seq = ff_fft_seq.transpose()
+
+        ff_fft_loc = np.empty(self.shapeK_loc, dtype=DTYPE)
+        # self.shapeX_loc is the same for all processes,
+        # it is safe to use Scatter
+
+        # NO !!!!!!!!!!
+
+        self.comm.Scatter(ff_fft_seq, ff_fft_loc, root=root)
+        return ff_fft_loc
+
+    # functions for initialisation of field
+    def constant_arrayK(self, value=None, dtype=complex, SHAPE='LOC'):
+        """Return a constant array in spectral space."""
+        if SHAPE == 'LOC':
+            shapeK = self.shapeK_loc
+        elif SHAPE == 'SEQ':
+            shapeK = self.shapeK_seq
+        else:
+            raise ValueError('SHAPE should be ''LOC'' of ''SEQ''')
+        if value == None:
+            field_lm = np.empty(self.shapeK, dtype=dtype)
+        elif value == 0:
+            field_lm = np.zeros(self.shapeK, dtype=dtype)
+        else:
+            field_lm = value*np.ones(self.shapeK, dtype=dtype)
+        return field_lm
+
+    def constant_arrayX(self, value=None, dtype=DTYPEf, SHAPE='LOC'):
+        """Return a constant array in real space."""
+        if SHAPE == 'LOC':
+            shapeX = self.shapeX_loc
+        elif SHAPE == 'SEQ':
+            shapeX = self.shapeX_seq
+        else:
+            raise ValueError('SHAPE should be ''LOC'' of ''SEQ''')
+        if value is None:
+            field = np.empty(shapeX, dtype=dtype)
+        elif value == 0:
+            field = np.zeros(shapeX, dtype=dtype)
+        else:
+            field = value*np.ones(shapeX, dtype=dtype)
+        return field
+
+    def compute_energy_from_Fourier(self, ff_fft):
+        if self.nb_proc > 1:
+            raise ValueError('not yet implemented for mpi')
+        return (np.sum(abs(ff_fft[:, 0])**2 + abs(ff_fft[:, -1])**2)
+                + 2*np.sum(abs(ff_fft[:, 1:-1])**2))/2
+
+    def compute_energy_from_spatial(self, ff):
+        if self.nb_proc > 1:
+            raise ValueError('not yet implemented for mpi')
+
+        return np.mean(abs(ff)**2)/2
+
+    def project_fft_on_realX(self,
+                             np.ndarray[DTYPEc_t, ndim=2] f_fft):
+        """Project the given field in spectral space such as its
+        inverse fft is a real field."""
+        cdef np.uint32_t nky_seq
+        cdef np.uint32_t iky_ky0, iky_kyM,  ikx_kx0, ikx_kxM,
+        cdef np.uint32_t ikyp, ikyn
+        cdef DTYPEc_t f_kp_kx0, f_kn_kx0, f_kp_kxM, f_knp_kxM
+
+        if self.nb_proc == 1 or self.SEQUENCIAL:
+            nky_seq = self.shapeK_seq[0]
+
+            iky_ky0 = 0
+            iky_kyM = nky_seq//2
+            ikx_kx0 = 0
+            ikx_kxM = self.shapeK_seq[1]-1
+
+            # first, some values have to be real
+            f_fft[iky_ky0, ikx_kx0] = f_fft[iky_ky0, ikx_kx0].real
+            f_fft[iky_ky0, ikx_kxM] = f_fft[iky_ky0, ikx_kxM].real
+            f_fft[iky_kyM, ikx_kx0] = f_fft[iky_kyM, ikx_kx0].real
+            f_fft[iky_kyM, ikx_kxM] = f_fft[iky_kyM, ikx_kxM].real
+
+            # second, there are relations between some values
+            for ikyp in xrange(1, iky_kyM):
+                ikyn = nky_seq - ikyp
+
+                f_kp_kx0 = f_fft[ikyp, ikx_kx0]
+                f_kn_kx0 = f_fft[ikyn, ikx_kx0]
+                f_fft[ikyp, ikx_kx0] = (f_kp_kx0+f_kn_kx0.conjugate()
+                                        )/2
+                f_fft[ikyn, ikx_kx0] = ((f_kp_kx0+f_kn_kx0.conjugate()
+                                         )/2).conjugate()
+
+                f_kp_kxM = f_fft[ikyp, ikx_kxM]
+                f_kn_kxM = f_fft[ikyn, ikx_kxM]
+                f_fft[ikyp, ikx_kxM] = (f_kp_kxM+f_kn_kxM.conjugate()
+                                        )/2
+                f_fft[ikyn, ikx_kxM] = ((f_kp_kxM+f_kn_kxM.conjugate()
+                                         )/2).conjugate()
+        else:
+            nky_seq = self.shapeK_seq[0]
+            iky_kyM = nky_seq // 2
+
+            if self.rank == 0:
+                # first, some values have to be real
+                f_fft[0, 0] = f_fft[0, 0].real
+                f_fft[0, iky_kyM] = f_fft[0, iky_kyM].real
+                # second, there are relations between some values
+                for ikyp in xrange(1, iky_kyM):
+                    ikyn = nky_seq - ikyp
+                    f_kp_kx0 = f_fft[0, ikyp]
+                    f_kn_kx0 = f_fft[0, ikyn]
+                    f_fft[0, ikyp] = (f_kp_kx0+f_kn_kx0.conjugate()
+                                      )/2
+                    f_fft[0, ikyn] = ((f_kp_kx0+f_kn_kx0.conjugate()
+                                       )/2).conjugate()
+
+            if self.rank == self.nb_proc-1:
+                ikx_kxM = f_fft.shape[0]-1
+                # first, some values have to be real
+                f_fft[ikx_kxM, 0] = f_fft[ikx_kxM, 0].real
+                f_fft[ikx_kxM, iky_kyM] = f_fft[ikx_kxM, iky_kyM].real
+                # second, there are relations between some values
+                for ikyp in xrange(1, iky_kyM):
+                    ikyn = nky_seq - ikyp
+                    f_kp_kxM = f_fft[ikx_kxM, ikyp]
+                    f_kn_kxM = f_fft[ikx_kxM, ikyn]
+                    f_fft[ikx_kxM, ikyp] = (
+                        f_kp_kxM+f_kn_kxM.conjugate())/2
+                    f_fft[ikx_kxM, ikyn] = (
+                        (f_kp_kxM+f_kn_kxM.conjugate())/2).conjugate()
diff --git a/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw3.pxd b/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw3.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZG1waWN5L2ZmdHczLnB4ZA==
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw3.pxd
@@ -0,0 +1,54 @@
+
+
+
+
+cdef extern from "complex.h":
+    pass
+
+cdef extern from "stddef.h":
+    ctypedef void ptrdiff_t
+
+cdef extern from "fftw3.h":
+    ctypedef struct fftw_plan_s:
+        pass
+    ctypedef fftw_plan_s *fftw_plan
+    ctypedef struct fftw_iodim:
+        int n
+        int ins "is"
+        int ous "os"
+
+    fftw_plan fftw_plan_dft_r2c_2d(int n0,
+                                   int n1,
+                                   double* in_,
+                                   complex* out_,
+                                   unsigned flags)
+
+    fftw_plan fftw_plan_dft_c2r_2d(int n0,
+                                   int n1,
+                                   complex* in_,
+                                   double* out_,
+                                   unsigned flags)
+
+    double* fftw_alloc_real(size_t n)
+    complex* fftw_alloc_complex(size_t n)
+    void fftw_execute(fftw_plan plan)
+    void fftw_destroy_plan(fftw_plan plan)
+    void fftw_free(void *mem)
+
+
+cdef enum:
+    FFTW_FORWARD = -1
+    FFTW_BACKWARD = +1
+    FFTW_MEASURE = 0
+    FFTW_DESTROY_INPUT =  (1 << 0)
+    FFTW_UNALIGNED = (1 << 1)
+    FFTW_CONSERVE_MEMORY = (1 << 2)
+    FFTW_EXHAUSTIVE = (1 << 3) # /* NO_EXHAUSTIVE is default */
+    FFTW_PRESERVE_INPUT = (1 << 4) # /* cancels FFTW_DESTROY_INPUT */
+    FFTW_PATIENT = (1 << 5) # /* IMPATIENT is default */
+    FFTW_ESTIMATE = (1 << 6)
+    FFTW_MPI_TRANSPOSED_IN = (1U << 29)
+    FFTW_MPI_TRANSPOSED_OUT = (1U << 30)
+    FFTW_MPI_DEFAULT_BLOCK = 0
+
+
diff --git a/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw3mpi.pxd b/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw3mpi.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9Tb3VyY2VzX2ZmdHcyZG1waWN5L2ZmdHczbXBpLnB4ZA==
--- /dev/null
+++ b/fluidsim/operators/fft/Sources_fftw2dmpicy/fftw3mpi.pxd
@@ -0,0 +1,35 @@
+
+
+
+cdef extern from "fftw3.h":
+    ctypedef struct fftw_plan_s:
+        pass
+    ctypedef fftw_plan_s *fftw_plan
+
+
+from mpi4py cimport MPI
+
+cdef extern from "fftw3-mpi.h":
+    size_t fftw_mpi_local_size_2d(size_t n0, size_t n1, MPI.MPI_Comm comm,
+                                  ptrdiff_t *local_n0, ptrdiff_t *local_0_start)
+
+    size_t fftw_mpi_local_size_2d_transposed(
+        size_t n0, size_t n1, MPI.MPI_Comm comm,
+        ptrdiff_t *local_n0, ptrdiff_t *local_0_start,
+        ptrdiff_t *local_n1, ptrdiff_t *local_1_start)
+
+    fftw_plan fftw_mpi_plan_dft_r2c_2d(int n0,
+                                       int n1,
+                                       double *in_,
+                                       complex *out,
+                                       MPI.MPI_Comm comm,
+                                       unsigned flags)
+    fftw_plan fftw_mpi_plan_dft_c2r_2d(int n0,
+                                       int n1,
+                                       complex *in_,
+                                       double *out,
+                                       MPI.MPI_Comm comm,
+                                       unsigned flags)
+
+    void fftw_mpi_init()
+    void fftw_mpi_cleanup()
diff --git a/fluidsim/operators/fft/__init__.py b/fluidsim/operators/fft/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/fluidsim/operators/fft/__init__.py
@@ -0,0 +1,15 @@
+"""Fast Fourier transforms (:mod:`fluidsim.operators.fft`)
+================================================================
+
+.. currentmodule:: fluidsim.operators.fft
+
+Provides
+
+.. autosummary::
+   :toctree:
+
+   easypyfft
+   fftw2dmpiccy
+   fftw2dmpicy
+
+"""
diff --git a/fluidsim/operators/fft/easypyfft.py b/fluidsim/operators/fft/easypyfft.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC9lYXN5cHlmZnQucHk=
--- /dev/null
+++ b/fluidsim/operators/fft/easypyfft.py
@@ -0,0 +1,263 @@
+"""Fast Fourier transforms (:mod:`fluidsim.operators.fft.easypyfft`)
+==========================================================================
+
+.. currentmodule:: fluidsim.operators.fft.easypyfft
+
+Provides classes for performing fft in 1, 2, and 3 dimensions:
+
+.. autoclass:: fftp2D
+   :members:
+
+.. autoclass:: FFTW2DReal2Complex
+   :members:
+
+.. autoclass:: FFTW3DReal2Complex
+   :members:
+
+.. autoclass:: FFTW1D
+   :members:
+
+.. autoclass:: FFTW1DReal2Complex
+   :members:
+
+"""
+
+from __future__ import division, print_function
+
+import numpy as np
+
+import scipy.fftpack as fftp
+
+nthreads = 4
+
+
+class fftp2D:
+    """ A class to use fftp """
+    def __init__(self, nx, ny):
+        if nx % 2 != 0 or ny % 2 != 0:
+            raise ValueError('nx and ny should be even')
+        self.nx = nx
+        self.ny = ny
+        self.shapeX = [ny, nx]
+        self.nkx = int(float(nx)/2+1)
+        self.shapeK = [ny, self.nkx]
+        self.coef_norm = nx*ny
+
+        self.fft2D = self.fftp2D
+        self.ifft2D = self.ifftp2D
+
+    def fftp2D(self, ff):
+        if not (isinstance(ff[0, 0], float)):
+            print('Warning: not array of floats')
+        big_ff_fft = fftp.fft2(ff)/self.coef_norm
+        small_ff_fft = big_ff_fft[:, 0:self.nkx]
+        return small_ff_fft
+
+    def ifftp2D(self, small_ff_fft, ARG_IS_COMPLEX=False):
+        if not (isinstance(small_ff_fft[0, 0], complex)):
+            print('Warning: not array of complexes')
+        print('small_ff_fft\n', small_ff_fft)
+        big_ff_fft = np.empty(self.shapeX, dtype=np.complex128)
+        big_ff_fft[:, 0:self.nkx] = small_ff_fft
+        for iky in range(self.ny):
+            big_ff_fft[iky, self.nkx:] = \
+                small_ff_fft[-iky, self.nkx-2:0:-1].conj()
+
+        print('big_ff_fft final\n', big_ff_fft)
+        result_ifft = fftp.ifft2(big_ff_fft*self.coef_norm)
+        if np.max(np.imag(result_ifft)) > 10**(-8):
+            print ('ifft2: imaginary part of ifft not equal to zero,',
+                   np.max(np.imag(result_ifft)))
+        return np.real(result_ifft)
+
+
+class FFTW2DReal2Complex:
+    """ A class to use fftw """
+    def __init__(self, nx, ny):
+        try:
+            import pyfftw
+        except ImportError as err:
+            raise ImportError(
+                "ImportError {0}. Instead fftpack can be used (?)", err)
+        if nx % 2 != 0 or ny % 2 != 0:
+            raise ValueError('nx and ny should be even')
+        shapeX = [ny, nx]
+        shapeK = [ny, nx//2 + 1]
+
+        self.shapeX = shapeX
+        self.shapeK = shapeK
+
+        self.arrayX = pyfftw.n_byte_align_empty(shapeX, 16, 'float64')
+        self.arrayK = pyfftw.n_byte_align_empty(shapeK, 16, 'complex128')
+
+        self.fftplan = pyfftw.FFTW(input_array=self.arrayX,
+                                   output_array=self.arrayK,
+                                   axes=(0, 1),
+                                   direction='FFTW_FORWARD',
+                                   threads=nthreads)
+        self.ifftplan = pyfftw.FFTW(input_array=self.arrayK,
+                                    output_array=self.arrayX,
+                                    axes=(0, 1),
+                                    direction='FFTW_BACKWARD',
+                                    threads=nthreads)
+
+        self.coef_norm = nx*ny
+
+    def fft2d(self, ff):
+        self.arrayX[:] = ff
+        self.fftplan(normalise_idft=False)
+        return self.arrayK/self.coef_norm
+
+    def ifft2d(self, ff_fft):
+        self.arrayK[:] = ff_fft
+        self.ifftplan(normalise_idft=False)
+        return self.arrayX.copy()
+
+    def compute_energy_from_Fourier(self, ff_fft):
+        return (np.sum(abs(ff_fft[:, 0])**2 + abs(ff_fft[:, -1])**2) +
+                2*np.sum(abs(ff_fft[:, 1:-1])**2))/2
+
+    def compute_energy_from_spatial(self, ff):
+        return np.mean(abs(ff)**2)/2
+
+    def project_fft_on_realX(self, ff_fft):
+        return self.fft2d(self.ifft2d(ff_fft))
+
+
+class FFTW3DReal2Complex:
+    """ A class to use fftw """
+    def __init__(self, nx, ny, nz):
+        try:
+            import pyfftw
+        except ImportError as err:
+            raise ImportError(
+                "ImportError {0}. Instead fftpack can be used (?)", err)
+        if nx % 2 != 0 or ny % 2 != 0 or nz % 2 != 0:
+            raise ValueError('nx, ny and nz should be even')
+        shapeX = [nz, ny, nx]
+        shapeK = [nz, ny, nx//2 + 1]
+
+        self.shapeX = shapeX
+        self.shapeK = shapeK
+
+        self.arrayX = pyfftw.n_byte_align_empty(shapeX, 16, 'float64')
+        self.arrayK = pyfftw.n_byte_align_empty(shapeK, 16, 'complex128')
+
+        self.fftplan = pyfftw.FFTW(input_array=self.arrayX,
+                                   output_array=self.arrayK,
+                                   axes=(0, 1, 2),
+                                   direction='FFTW_FORWARD',
+                                   threads=nthreads)
+        self.ifftplan = pyfftw.FFTW(input_array=self.arrayK,
+                                    output_array=self.arrayX,
+                                    axes=(0, 1, 2),
+                                    direction='FFTW_BACKWARD',
+                                    threads=nthreads)
+
+        self.coef_norm = nx*ny*nz
+
+    def fft3d(self, ff):
+        self.arrayX[:] = ff
+        self.fftplan(normalise_idft=False)
+        return self.arrayK/self.coef_norm
+
+    def ifft3d(self, ff_fft):
+        self.arrayK[:] = ff_fft
+        self.ifftplan(normalise_idft=False)
+        return self.arrayX.copy()
+
+    def compute_energy_from_Fourier(self, ff_fft):
+        return (np.sum(abs(ff_fft[:, :, 0])**2 + abs(ff_fft[:, :, -1])**2) +
+                2*np.sum(abs(ff_fft[:, :, 1:-1])**2))/2
+
+    def compute_energy_from_spatial(self, ff):
+        return np.mean(abs(ff)**2)/2
+
+    def project_fft_on_realX(self, ff_fft):
+        return self.fft2d(self.ifft2d(ff_fft))
+
+
+class FFTW1D:
+    """ A class to use fftw 1D """
+    def __init__(self, n):
+        try:
+            import pyfftw
+        except ImportError as err:
+            raise ImportError("ImportError. Instead fftpack?", err)
+
+        if n % 2 != 0:
+            raise ValueError('n should be even')
+        shapeX = [n]
+        shapeK = [n]
+        self.shapeX = shapeX
+        self.shapeK = shapeK
+        self.arrayX = pyfftw.n_byte_align_empty(shapeX, 16, 'complex128')
+        self.arrayK = pyfftw.n_byte_align_empty(shapeK, 16, 'complex128')
+        self.fftplan = pyfftw.FFTW(input_array=self.arrayX,
+                                   output_array=self.arrayK,
+                                   axes=(-1,),
+                                   direction='FFTW_FORWARD', threads=nthreads)
+        self.ifftplan = pyfftw.FFTW(input_array=self.arrayK,
+                                    output_array=self.arrayX,
+                                    axes=(-1,),
+                                    direction='FFTW_BACKWARD',
+                                    threads=nthreads)
+
+        self.coef_norm = n
+
+    def fft(self, ff):
+        self.arrayX[:] = ff
+        self.fftplan()
+        return self.arrayK/self.coef_norm
+
+    def ifft(self, ff_fft):
+        self.arrayK[:] = ff_fft
+        self.ifftplan()
+        return self.arrayX.copy()
+
+
+class FFTW1DReal2Complex:
+    """ A class to use fftw 1D """
+    def __init__(self, n):
+        try:
+            import pyfftw
+        except ImportError as err:
+            raise ImportError("ImportError. Instead fftpack?", err)
+
+        if n % 2 != 0:
+            raise ValueError('n should be even')
+        shapeX = [n]
+        shapeK = [n//2+1]
+        self.shapeX = shapeX
+        self.shapeK = shapeK
+        self.arrayX = pyfftw.n_byte_align_empty(shapeX, 16, 'float64')
+        self.arrayK = pyfftw.n_byte_align_empty(shapeK, 16, 'complex128')
+        self.fftplan = pyfftw.FFTW(input_array=self.arrayX,
+                                   output_array=self.arrayK,
+                                   axes=(-1,),
+                                   direction='FFTW_FORWARD', threads=nthreads)
+        self.ifftplan = pyfftw.FFTW(input_array=self.arrayK,
+                                    output_array=self.arrayX,
+                                    axes=(-1,),
+                                    direction='FFTW_BACKWARD',
+                                    threads=nthreads)
+
+        self.coef_norm = n
+
+    def fft(self, ff):
+        self.arrayX[:] = ff
+        self.fftplan(normalise_idft=False)
+        return self.arrayK/self.coef_norm
+
+    def ifft(self, ff_fft):
+        self.arrayK[:] = ff_fft
+        self.ifftplan(normalise_idft=False)
+        return self.arrayX.copy()
+
+    def compute_energy_from_Fourier(self, ff_fft):
+        return (abs(ff_fft[0])**2 +
+                2*np.sum(abs(ff_fft[1:-1])**2) +
+                abs(ff_fft[-1])**2)/2
+
+    def compute_energy_from_spatial(self, ff):
+        return np.mean(abs(ff)**2)/2
diff --git a/fluidsim/operators/fft/profile_fft.ods b/fluidsim/operators/fft/profile_fft.ods
new file mode 100644
index 0000000000000000000000000000000000000000..258ce4eb5da5d6578696f3beedf30da104a1209d
GIT binary patch
literal 12484
zc$}451y~%*vc}!rEf6fY1QvG)!69hy#bJS6ToRl>a1D^)u0evkOMu|+65L(#NY2T<
z$+_p;@4nfucc*LW|Ep?xr@MN(mE~dK@SvcOprD4WL&O7Y_(NEsprF2&2N9I5r7aNR
zW)C#7x3{r0F@jiv?AV;`Oj$ujU`sG7$R22C3NmrB1=>Ma!S;?oBU7*i5C~EJ!HI-~
z^n(-Xmjm^on1Jjc-vO?+HhX%ypjlo#@5NGfb;BtN)0gB1@mVcp=tPleo$g|bC^{K7
z8FgV<CLYb+L9uk9;Xrx0uXV)+dXa)bi?uO=*zR}kaUG3_&xw0SNZYq&r7?5G2%;g*
zCZ4X}68Jgw7w;kOrLd6?X$O3zeAVm_wELD9DeQA?RErjfP?HonJCKsI7+P)G{N&<S
zE7y|7^67C36OBB{tIZNRob19i8(1pRG+e(_3YF8AfP?T@W;s!H*sb0R_04dWYS~B=
zYYFvY(+j8;8Ap59a%`k*;`ZyXCfsFD4t*VP>{|Uur^)Q&7s6^Vtfd;@6JYYBC2{?f
zyf90FUB-4Dx6&zUM2fSsvdR)mCZZupZQwQB7{1T6T_zv`#{jcp;>#JkH9MGHrqU@z
z&X`BNq0B4K)#&|GG0%mi#V9At(BsW}J5o(<%TPKCd^Pykwc_z7c{{h-@fsN@fi%<E
zH6^w;FE~n6*f68n7?%?#di?y>phNunR<z59EJQc8M2%Juv5z%ykR{3x5GI-8nT-i}
z?e4bj_g}JiBhTQ(tadz>BbM2k;8f>JR~hw@a`>R4PyE=*z_V4?j&3%E-T}OYRY<q>
zHgH*A6|1`<?X4(IGA10G0b|vfz2I9GIdyo}R)=?V_<Pdi4SgJR;<$LBqU^W`(tb-l
z$8ur1#iiWZRkMn4k_6KA0{gTwEs5P1(@S?E@mgG+M=<aZKXri@tt%t1aTQhiI+~K(
zK24)GbX<>k<X~yo>FmPr5Vu8i&cR&ikw|+H5mm3h(KDs0D0qXUXvQTD%_~9Hv3p*Z
zS2PYOB|>GMBXWW3tBlRMz*ysSSH6EX;@`1Dx@sP4zFSRRXFT$iQ5V%69H_?5Ov|W?
z(}fq3atPHo1)CvxJqLI)bKuXV$nngb68gH#)x%;oxW<f(zT31iX}9j34C)7DbA+6^
zT+W(X4Q>?9T$tHlwoJ;wWY?%H>0a$rZg8;lbN<#MLSPId!&PUBF%s33vS@;UT~7in
zo_WALW!OBlTs91k8wT>`hi7~>rf&nipDs7x>I{-XaKd?O<IMWWdku3~Uwou}yqP$#
zp_eA`s2lpUoraAlkh4|J*SKPEa0|u-yP<Vm31`mfwlD^H0SiN^;&O^IMb<w2Jm?w!
zyA~cIH7*%08v?RdOGqrnO_>@BnJWr7IPu*UjZx+m$u$D4q3zGhv7AHldvnQEF`nWx
zlXb5@3(s>!>l2G<v8UP3$|%Zudy6<0o(#@R(zSaF_`IUb*zY^#-;SM4CvW?4i7DaY
zB!txKt>))Be4n)g!|a@bE6x{Il|9(msf8M9wR?FQou_<=*<B2eE5=NxeM9!eBwws=
z*w=UFZF9EZglO+|K}}gms|YWCoOV`Amt-=)V1G{qc$81PDog9BGCMT7-y~fhdL8J?
z#+Ae}qD&S|EI4c+rSi4*lfzLTbSYX>ye5F`NouPZp3F@|!lzuBb!Gf<tA=emf*kFd
zg(g`?J}C*_?!-lXLa1XG*cV8*ici{+)dt2tKOX8a2IQKOn)T@?3oKALyY(*QY4D}7
zUkMLs6<<Ii_8Ap@&u_LOzl@fd)0ux6o{i+J$b%zV*zRUIK^udephyWKuqT{@W&8jq
zFw*K>57RzIvG5pdn@?JGh{@iF<&WlQ-3_z%d_{Pg_Lx=YTwS@L<*~sgJ0{4xW--K8
zN>ilSE9QLR-tFS(bC431F-LYE)A_fB0rxkRGLMgRlCq>^8|jhv`5PHl8V#mOm5`Zv
zA{$@$kQJG5!i(M><WnCk-g^OhN`0=PBkv>9H&8uJ3O6BuJ2cHD(FCImJ_)^-wHzrd
z4PaG67u4N*qLzDtsmZuidZ_)hBMVePfRbUOX7YrbMLiGY8<nTlRh=Ji&c|I*X@h1W
z$&|z#1-)c7nMUG-Y4N{Gfllmh4iRj0&oYaLBF<%{t_g7Zs_v4;jI;U$!?%%SG9Q&7
zSZ!leQlpRaQ97fYV#xZX`*IUmNgQju_w)i>Sy^3Q)3m9njouKbj#hV9IkHH*53HWf
zi+vV!%<IQ4S#i)zZWpQ|@Q|@O0&^e7cKD}%f+>LFpRv~irCDVadjeoP<p-l`JgjsI
zE|Aj-4Ia-lJ);EOy&ar7OaYjzjgZt=c2P*^>zE`=Ol9*p+qFFWUO}gJgO_8AIW*lA
zUOGMZ8RaPo*K4oii6+yqmPwtMCy^T}KTu3nT-0sWCEXWP-G+ST@F=SDEW)&0W0rm7
z1vE~hK|g&63FBJX0P&^Q-t4vhjR23RfuO6EvHPO9@rM!~XAQ(?30s!J^c5r`E9=8N
z*%D#eA+<rmhYB4dA-vbCDOC>>fUZf%y5STD%MMovZG^~*)6mYNg>b1g?E;U*s_k{n
z#lDNVb<?9x7yg+$5VhSmL}htIq~@;In1qN>P?`_p=Rc39C=Ue;akBw}zYnMLTDA|v
zDb{_H0X0KQ8FUQew4AQ>M_8NoGH3<%ti7gD96H=MS)VL}5rgK{P`pNfPIdY$4pn&1
z=01h6i=b*#qd-D{3$3{7Ag^~Nl2=5Te{SqL>9_WqRKKV9^XG-SAAsD{4t+5aa)~He
z?o7d_Es4ETigveth8*3|eTmD_Jz8`erAUQgE4%>S^xa?w-pRSwvlAgH<9a4xds8d(
zw>=1RpA_MRKl4mz4OiNJII8V3*M=j;#L<7D^@So7EH>R8)~s0&f(>}D7&>azDixk_
z<{ma+Bf)2BKQ4xc5cfD3PZBYPK}RyLD_(NB_LlpT!W;5Fa_oxiRus%~9b_74nEr7m
z{vnmw95sV(PiH6c6XbU>LIf2%1F!3|%b#r~^fB4a#%t}-AKxI%w`W~}zn#~XNb*08
z?{dz123KsxHkvVeo^ldcz+09)Km0OqnWub+<#dbYmNt(j173->4OfpgY^8w=7u_Bk
zMp87qZq2%KIP}A*rypE?jWT+ajdumCQ~c)_^M&P!3kB|<0yk(BWYR^=0}N-|a*)Gg
zQ%V4hyXE)RrlJaR^0lfa919{eLWEzPk<nY7b|)C2nK|K1=-6ZGg6E{2nndAi*|*`6
z0vxOwMI*FnzM7*PaqEU~>$dnc7h@*AZC{RF_J?DXL#b`73QH$G->r!ETy4%C20k}D
zARj&ynrh^!%<3HN`bMIu=e^iIEw;E*GjT~p{}u^_=dqG{A4Wz7lg~@n4qp=FFF-31
zWx6=3np5wf;e@R)!%bz+=#W@82T8TMItP#NNozT|K+FJvdF!F@!u%8}>VU!c_XTAw
zwJqbqd2m_U3@Dl~0`L}xZG}|Xszx~=#O0O1a`SnJE&3kX(3@Q|L00yDOuiuPwgJ&L
z%;(5y7t8M#U9d&(jCkWv{fh6#hm~&JyZB4!y*-KQCO0L;22WtPmxT6$EONZP*cnNC
zJI^O5^7l>mXnP9Iu&#w7A4beRm_EGTR@Q8O`v7tqn6S&D3NEz6nDteq1o$LgZ`$MX
z;z@ONa6MX`L+&<0NK017OPc&Pcm=Ch2%d(F*!Ji&JkrO$Ik=Iy3TKv~m?273IQsTA
zvx#XNubP+bX}A|9Waf`(Y_pHyfyI3NL#o}#9%9!9ND~QH%`!J#7N*_SaiTXV@X?h1
z3rEC4lY`5*v|2mx0X?GWXoThCk}?9wdDpv;ReD1t7oT2)5j>bXxR05damTqKtEKDx
zj3i$1Sx$frS*DuCeuQC3?mH(w+BfcDb3)tK<K~Vy5CP9@4;7!(OEISjh(TPG2n<Q|
zh$g3&sPZx1E(&(uJXrC(tj?R+!Gw4-vPr)U0-C9(^$%UY-r%Y7YV4p1s`WK>&k<lC
zYZ9nieYVAHP=hoHD%q3s%hBcs82B=hg=TWs#|~2wayi!Armiaw?bXPh_0yu2lo+UN
zygT1}8L-hU%9Jw0_5=#IDcnjT#d83jtlV<GqBL6h8!t^2RwZ030v(su;j7G?`^NG{
zy;X~HzuGrA{asR{lXGe3kgbW41^h>0LnDg|Nu_U?E*LNTwNB6X_xB0T5a(sbanm)S
z014r6dT*GQ*!kd!c@<6<Pj%+}a%@X2XY;}*Nl2CRUOGuO%R-|=JmqF&E}nUcO|Oei
zU-L%pm?+?0#~6DoYNccpSV-)iRKrpOq5u+RlxG?3@=lG?C*JeIHIz%f6R&+Av0|{-
zpk-=c=_6v>gddVfQI9+d4?wVMKCx<R5w3fqY1E*@lhxgq+kX%`9vd_xdQwvUvUFBd
z;2d+Df@E>e;x_c_9_9D$xYIv_TN54%s)yibcl>k8ghQO^^%WKh>U;SyWdcHszIVog
zA`f3w&Ok@-!=y=whJ%%zh6-qB0y4F<GZ&)KP?u)mqY)8C5d@i;S(*R=Kj%>_-|<ur
zP&+W-k4r){PL6hfA2Tk1oslgN41kybew}&&e$@v2F!YDS)yC4!T8PF10<j0MvAMXo
zu)1)uf*j4+IQaSb*?wI8scLHSFFkuFN1Gp7rY3Aa8{l^fU^Wg`4z{1V-#z?ao%*i(
zt4kmd=wF7utN-Ej4|AOC?A&aB)PHK5JDQr>{9OPKx?F4z9vVT6Sez|^E{|xa{tW+@
z8_r2X^@9KaaWt|6n}Hl{e@tNi#rNPU_>Y+^%Xe3P5dXpQ5H2U1E7%6Y1~N6{bTl<%
zeW=g~|7@%8K?w`~2+h*Y5@Km&!(#F<`GtTSg_SLVj*dVoF(>nf6G67W)%gSYqxRqq
z%R>-AVNP}qE*5rf77liG4sHN9FMx;pPxSA4f~F>b{{Zj+xYz+)T!L&rFMikh2akTa
z@`oQl(+BVD%vnsG9DmpmR#s<cm*wVC;Nn;N(}n*~`5kHEX7Z4L2y^{K`%m#7y5>MT
zpyLm#N)NYM>EEr+#mY&mVQ1+|^}F1_-VtO4G=Z>jakATT@Y9MrS=yMg@bj?q{AuOi
zgnt<L*Yg90JopHPSej7%kVA}&ZGbE$ASXMB5Dgd2A3_tLjm=LPAIG0UknwkeKgH}c
zY{G(Uzco19-#x0r|D2o=Jrs2dCtG7XBTE}F8|3FHtG%81UV^69sBS_RwhAVU<5$Gk
z3{fm-{@B-Pl@jRw3!!6B)ZD0zs3cjBkcixo)BVJ;uq;QU-1C^I6x<a=VODI0-W^=^
z4G3}(a=FAL$Avatyh^BAxTwxJg^X>nI_hPmrKR~<<`KP~5)*R>Tp@gRIJDe2h>n8t
zf-x~9I?7<f>;+ZI^r@riOV;`4*J(Ri?i{g?DBbqoGA&n!unL)(-D!#2<r2gLHye5n
zsl1GzPgUtK;zseu8@v+9T#u%-%EJ5>_vN@ePYYtSHz41Ydu2lX0a43G_APm6bLrLT
zuJ^^U5M^qqHqoX}SPY?dvH&h`I#Eci9#Eb)M2%QVcE=4ad-fInaBE}7>RvqR=3sVK
zHl00l4=NJgmEF*(HD#EtJBENKd5?~k)>5N0dQ_Tg^da|atiJ2_4f;;!f_ud+e#don
z?eyF>FC7vT(9sEBya?fdUn6ag0|FC(jgbs^+Y5~zii-W9hDjme@dJ#x1zzxS_pFVD
zSdNpRpy1`zRZvjS+1VMJUfuDr3&+KJ3YH_@V(ChvTB`B8cP+NHuf6soK@nKxm<cBR
zeg3jy^!4)^?;7rT7Is!+Vq%Jmiyy!a3E7KBj3A5UPstJ0(kNBsri~|0c|^Q*x8-l<
zzFLpsiH2-8c&+#0uSuN7(9>mR3e_W03h63FMD(uFUIH1PQapE%3U8NCDSWD5Xu~UJ
z@TvOMfJ;{v(mUP*)ejR5IGy&r@L1I()#x-eH3yfBsu%`Rh>g&bU_R0r<5_7-G;B79
z_jwQp2QfM-x@?VWE+B<3DXNeV&eEJ{AUbA~m20(cQT4%Fidd0wDZ+{p3e84q>7uYW
z1=mlWn*9#5*JCY(haT*1RY<Mk-7+*$695>pqC4QVK`{So79p)&Hv|l<rOk`kmWpak
zkv{igGzCQ~TA7bw{@C5mZ7VBG<%mCgb~7)9f8)aa=-k=GC@Cok006LSeP{~N#2K*C
z)pQeoFL3GR{3#sH<R&!IRsYPjsAO=wP!krDmuP`w%HNxw4)v>acgI{+xs~wrC;%sm
z`NMO+)dO$xfvc#K)ebi})Sd86$jiM)Qhjiv6_c)meTxxsJ!@fVW6$shb+sB>$YT@Z
zvRO7YZfj?vq&=J*R06`E7Y=gMs|-1%;5o^a%RcR<jcl>Lwg(&E6sWy&Uv9mg)D<R;
z@v_VSI)>gYy;qMQr!6SLk$}gzd>4uPO`+{xBGD7UASNdZ$8FUbZm=jfLDmf7e4j+U
z=k{i25VfS<<>376EdPG$<yXZT8k4l#Zg3JK;aAvbRTUM7JFodw(D|~MST6m2R6=F<
z;>xAPLek{EHs^W7(eN69T&)6ApNg8=!4a+|0pUAsZMD%+P|wI&<65We3=(IDo2R!#
zH_Nw)GTGcupU!WN(({64_0;ju5HfReCj;8UY!nGS-pSU{^_u{53hNI(4DSMKJB(0K
zgC<KB8{V-j3sNytKUtU_A0MCh@`R{sspAn5@#(%=G-A+1*1LuCbhDNZl0GZnhgOY|
z=-cZ|o+9E^Ntq+$v?Tw+q>jL%soJtMxD#}SupT!yl*c3XA?y_#{~~$+AgSyPIHn^V
zs6^{n{za2pv1e*(YGKfPXB6APiJLc`7Xw3|Qlvb#BB!-|-jdJY=>G2J$Po-wIoG|g
zAt%48n!zf@k9S)0Q($rNqoicV>F8x54e<7_t-TW0OfgEM{bpYvH&bzvnwgnN>^dHZ
zwoOVhz}#(+F*%{^HT>b@>Xjl%xTY+RO7h00-y{C$6?|TnX?p_x%2iVqLC+ddA?dp~
z%7qD+9b7-<cT7gcZ7yK<x2}mhjRK3)6mlXnHkKAscAo8*?$3;3V$%!{c8kK=VzXni
z$WY?<gM~WkUOn5SBEVZ3ob+ZLGu}Cv%4GJ>JTm8qUY}6ddimB=ByM%gQYEHmJ&{|Q
zm-mxp_E{BAO-!ErXZw#;@7@Ihb|Cfk%Ahv|AxSgAiI4bdo12>ng%<~rv#v<+o9vxr
zo}8X$RcU;j=!%TmkKj3?1;7*<fvcnhWW2qb4&jD6-#0ei`jv5+7n7DPDQnyNf!ife
za&bQ@O({c@qavf^PDao(9hjM#wQ*G{87AQ7k-;lTyjO?uz$sms9Zukrmj~>3Vu;8D
zhpx=ktivF6w7=V4GD&UX2tU$0;p9YI8GuzF{&Ze09to=EunRHTm5~DFO`m+dDZtZT
zzKj(Zw!5y<;N9C9s697($#RXQ>rL@^wqnIRi}yM8-V}&G>iEpbSU>mTg21Spr`oS+
z9yiGg#1bbyJN32R*4D^DA|4L6pClnJida8;@EO?AnPR+cYeS2ac=>=0V)W6o9CF0L
z(TmnxPbjCIRk5K8svSiX!JK~QVVcWtQkITlbivrp-HYn0(|3o%(okOk?gs_szEM(D
zRn@6e6!NjQw0t8c8iXlv2|R`*ZXY_W`RsF7nOtKbCf+c7S|%g5=B+MwyyN3zkd<=V
zh<m1|m8|?>WHW*iHn)JBSv(*-#_-V#1iO`lgzD<T7q}mUKMPr}t_QF=q&r%_{Q%sz
zw$2zGo?x{><)6_lz_JaKA>x|~P2j#&Ce2Cu=124HWA3TO`038@R+@nyChrveliUxj
z+CgSYiS(s*OcLJIbCQmsBFFrGM73s9&_x}0t2@gJ&?(Lc(9}$i9)VZ8JU%1I5E8cD
zpM^8ze4&|t)6Sg6+4U?;#$iUht!?VAAS?`BnL69j(k#qKKtRBS)GLE5l%cS&FiKm~
zw2^yhjK~wSf`|<y=2ok#`9#2g^ER!r$fjt(^SB`0F5ENbTa%h!57FcTM|#I$E>)`c
zC6o-h*}NiG!0G#T0(`pm6Wb#HgioLBW`=L|tS;m|Oo2eUND`!P>d!qJFv#w2FK28G
zd2S?E$PGK3{gNqn$aeRDUe!ye5x6*CyJIzS3mLU?ZZrYhfDQ%^q_!4R`TqW=(xEtp
zWw5l2+clTB0jTkPbY-SJ_L^Pf->d}YTW$D>N;=AVs7$oGjj)5`>)jG_qE$%`B~0_;
z1G`Ec-;@#YwYS|0!rl9BCPg||U8e=~H-4ScvhfWmD6l#U&7$qfCy5v;XHYrW?jG&<
zbm{6A=~zlWlm_TYcB(*t<U#46HSy(ozTqOBNml4<X4p}?^&6mxqJ65A8ILL0vzHic
zZ#SXMqP0f3MuEt9@4H?m`_0~<zbBYq-p<m^ZOwPTWCBFWjMJwKok@1FMh$DbH$6<b
z=yTm&A-{Bd@(rpq@`LrB+FM0@wi|XnEe)MI8QPVYr8P|VyD^hN%Bw@^z)VxlS^=N>
zqV?_RX3U(s^#qTLgW0X?<FgZy$KQB{*%`Mzu^ZdoEHL_F`=DSZ_N|sJMJwmT2oc{q
zZS@n@w+%!GcDswraZ==FC@0N_fx(|>9VRJY@VRl5+e(W9IGQZaInh`kW0czx9c#5)
zUy8?TxO8IUi$Xr746w1SDth?0){y}yqT!FlhtPE-XIA%8A{Xy<*bO=3ZN5nG5F=n_
z-srXz=;X$VZIrkzS#6jn!HjFDH%0aK_xBTE=;jee&3zGHdX}kqesR$qXnJ``pc#nT
z;K@$(RzBsi-skKEnis-?I3>lJXA6m23zaMkT5fJ`h8d#OFcqvZNmH*%s54m8o*vW%
z>GVFASDS?6v@AhI-pDg$kxi_(>XBsdV3&@w<isR{VR$m)n1xpuR+OLdr88InhOs$5
zE%#P~Cy*x`He~y)8SzkFTrO7_@ry;dC+P$RjgYNq<WWUeS66p;0K0`HBjXDK0>TIy
zfz`O)r{P{jgM)*CnG&4h;ybUm$?8@`Ci&>I_%yzLikQYF-oN#PHi<|}t3I44rDP98
zK#<GNUzLyu*^|p+>Ayu#)oQG+%h~9h*_)1MIu(_bMWWW7;SyL6B=T4$j3wewO3qgx
zcKifu+X0EdbMtCAF4@VcJvG7(dh&EET@F_tzfly1@`{q9=bIuFZG~n>FTMEVFjF(L
zjg4b6j_YcqZ-|LoSuni?hlWR6v-445`ANaS!4<Kw1YynjLRAKOQ{Motv#8pQXnWkr
zI|f`c8;+czSQz~uye(bgJG#3kV|djgQ*12E%mT&ZN@P*0VaK0yMZrK=@lS1K=;fVl
zJa3`&h$z^QeMF(&FV=!(o<A3<p>8>r#^b_fXZ~`~Uk{eab=`@I!ZxMmBAum@7Qdra
z(eB{@u(GmJFWbK|kk`C8TuLLZku6c}5Dgc{-)P?)(k@C`%(bZMocRI?w7d|rTG}es
z5ihs6BdZu5#`jTy8S2!7h_%8{wMMgn!GbV+c|%`5Jd&HaSX4o{dm3ANpPG(Vdt|fK
zmPutc&tPdpP2EpIUzg(nE{=HUyqWwBh9^8qlLvSa6D&&CdjuuH<S`SW?1qUG5^6P{
zh??vNunW}_QbCJHv8{P@j~<!G#Sd21lpj}V_-n-Eoz3b6649?LL{CH=x)2>Oq?PJt
z8X<C>&I>m!uE8974{&UA3mYKv8E6doL(}Cq%qDvG1vFdUO&rXI&qMr~QGiCUqFqM^
zRDSshtt(?vV^+p*rZ4&A+k>&#V5l#gGAFHsyNdV^+&~$aN}MF3t6$GVYH%Yf5W0r}
zFDzutH*r(HJO!D?<U(@pXbmbpmWS^O^G7D$TNv5B5j}Jufhj?_ORlTK3c?lL5hR!D
zKb4=Odi2%OGAum(y@cRYa)X*+`p{F=258e1ou%Ec$BdFvQYtL4E{OLXXuF-a4Z|@^
z&Lk?g;kOj-gyob)F)1m#<yhHEZL$7^Soehfmas_eyN9?&#;luCPx&b*ZE{NF<C({0
zvD1ZOLx(8m^g<OKK!l6KKBnwb1@GfsEy_wN4_=yqT><YHRO^%+&-ydCGxlzrFF#lv
ze=%q~A3=~v7NqQp6MhP2_utxAAkex?tDyYym3U&!*>4_^TVKJoA!<^mywN7$!VmE(
z^Lcs39V~1OHy``>iZShy+Uq5KGR>(eI9J<Tr@5nOn&Rfk$s^peUR!L!Vc&b5(kb@f
zUX)x|PUrICJ#3x4y39r|qY@V1L~d^G)2&hZ;X@%M`O(h}XJ|$()*f?rU|wN}<mUC)
zRR>)a2dfUmC~6<YXHHIM`L@GEVEcu0qZ~ig52fy&3$vr(*^g{h=an$0HDy6%nvHgB
zIyyS`D3c`t1}HF}Suwf~9Z#f#)2l?i=EI_+aeC!idO&iUqGy>)_DHAp=k9eG_}RtK
z+5D$1m#aQ@2wuX6YXMN{vvQ5G*0ynXxW_%0eudA~8F&SSq(4T9&#k{pYfPT)vc8C5
zMlx@q&)cpX7+`+#Bs{=&-1YVZcyNWWoq1+Ewv<^x_?+oU+WzRJ%fSM)#A1@|yZ4Ea
zFO(CV02h3mjvjR{<ZHgroWrlC$`|D4ze`r?ESI+sWhd>*$pPY)>NBmDulvECC)`kK
z#rLsLz$hw*HpAS6lOy1863^UiwZ0o4*JxV9A0<MN3J$JeQjTjV=hSq$K!GB=f`@<p
z{P}50d2m}`%4`#CX6B7(%dn%<hl(Q=mBLgK+Lc5!heN^XkIL7*dMNQTi-A6m2$NoD
z`vEW><95P%)RsT5wY%v+@RyNPU&AgdGhOp_K7XH|AT{VWeR;t5{)vK1?5w)e=Gd6}
zcJ0c_pn+?&HpgTgK%mOtwj|3qI$XA=g#4X}nR(FqEwm1yKhbc_c8{y3uM-c=b{&<-
zH{9^{J#8Obmv;7W&Foi-^mGzi+{Y(|i0d|865G+V$pju%R%dCv=(kf5vuf*j3$`Zb
z>Tkx_h71{4zp=zBXG$|sfg^*2ZJs=-x@c;Em5WPGuK&2a(PzVqYj!si*0|{6)zF5N
zll}#^j+)n~Lej9GCpH?#k($1m$v^yANw7p*(s0*IakzjsKR<6*mwaI%>n+hTs)Vc6
z{1ffGgLsYTv^)z9@B8|uro_|%ttZR-8|M=)0;Gm8J|P`OFhVgC8-;m91h^8oxz>)E
zXV9G+ZN84hP2{a45oD|rZ0w9rD|Rwz4Yj7Hr17=2w5g>cUah=-&oLVAr}j}b3r0qm
z{k5YRYN&iIE1%qA1LEVys{uNeFOO2f2L1GipVY!aUA#$~?j2vo(mMlV1c9;2d|pR)
zw&rI06+1Rt4CV!Z&)-MwDup(~Ghkf+=;`Snt*+{R^Ll#1KCLoLFR3CDDCS=FYzCAy
zdIr|wHn=<F-uDzuFCKK498QR=%g-}J0*2gbeih5r$9)4Y2YV|nweFx1mm4HQ2dN!N
zQjjfWTF}zosdorF3@Lw6XZWJK_GD9rq|DTUV(QAn1OHm2H9fk$t+$tvhh^__wpy2y
z^Yy~hMj@mi#ZKJ!gvdY)6A6TLlQ!-(YCk0}iByqSk13o%8~z`R5jHk9H?fV+lSDu@
zV<;ir8`KFRh7FC4KJ;KY4=CAZJD?_QyU)nZvaDcE%8=)sixe2E)&yTD>Jm3$**m^`
zRMrZEi5VPSK!w5%A#Cz)tjY3!Nln|++A3y{x;%B{f0jCE=s;zv!;cpootQ^N;^=<L
z!^Vb*6po$8{<<Sa{^k6<mxP>!qpB(nMTAV|5I8ITW{~q-Q;*@r_QvT2>t>l&;WHQY
zl}||1JNS{6N3V#G4}C@1M-W(`X>2&L1D^2TRe|d!s>BDYM9e{-(L(5_L~q*mBGEro
z9IG`fe-M$m=W@M&b=i!*h-b#2j+wORl+M_KnNyIddKF}Q*$n-ConZ8&A9@TI3JOH<
z-<ECZABqIX&dk#M&o`=IPBtSG6QIo>uQb_AoE#nh+#&@F^_})R2<A`l@5>Lr(vtlR
zt-T}2+z|){8yh*Y{WFs2Z;)m-AR`FyzoAI}&K}6#$=()d=k))y_8s#7_YndD+5C4D
z<=>zzZH>%<U^a0}h^>)5`2QK;_xH<AzueBhYhSeQ%kn_T_t)S*7UuV~7nNq&vAh=z
zRMs1_Sc*g?1>zJ*ZEPeIj6@dZ)@F40ntGf``V2(qEwTMg{khGODsu}y-pwbR%wN@C
zuFc!F=rChN(@<*7H-?YH?58VT85_P-5UGqPa%q1`&zGu(Nk-Yo097)B6FZ`2D$lk#
z(i@2Qg0{v_QgzE7=QSjD(-6tCe!z`yRxS*vWJkRI6xKRl{p*x7S+~d0Jw-^sypXW)
zr_nEa>X=t4tOZGZW+L1H!54wi@Wk)Nv`R<$%PPLTu$DOXv38%Z-wE2=!BpZ!C{N|p
zz@lxkw56aQLx|_yDq`rk(60!Y5Yh)L(7op%LZ(tfE8a`f3388g1fpXuP`sZ~GSY|h
zf(C2&&+qU+-M_!K()M2v+P=Va2^4{?(;_Gr*(K97Tf~r^%9#t&Jv_;wVqM6GgoSf_
zED<MK0gH62GNre-kUaLAC*)8N1q<d%3as&GL?qP6LyZ?>1+2=-;w3QTkH{Tn_DtWo
zlU@;BnHwdJy^Z3+JH@D_89*o2Fc=PM_?Ey>>7)3W2rfO(97U+xdncU+k*9SL>b^$}
z0?&Bue<=0F!~LwK+OwZYQ(;hdN={p08oN3x@F3Vd<eAYh)gfDlVSx%s*jKpIq%wwx
zBs#YXTLsEUc-pg*h_81^vGa6Yl<=P?_lRj=+lte1u<;%d3VOQg#iHjrHCkC^<2SF#
z_CC8A%<jhRnR**M!qwn%DbnGL@v);NKc4ex0t}9i7J(hO%~Y;IgX07hF6VZ=f3+6n
zUQ<0MZHI0d+=IMLL>xlK-A)@&*LdGV(83e&In#@0DUOSV=Zs+q_wCT@%Q_JbV_Fv4
zx)CB#k%UE>Y!86_WGmV`2=%KqUgM5++LRT4S@^3kJDLj4C2*~!+*|F$*JC#?3!3Yz
zm>npZwEPc{4y3tKOY)v%FJifJT-Bw=v_{zFLnh~RQPxf|^p((UuD`4x<EWnvVNCnU
zMw<E=9N!srB$k-ysO2f~k>c({b46vQiXBE}`5Q>jsbMo(NG0`Tq4t4F#8Vj<4kpj7
zL;O2r0C-|jZf`^$;anr;#^QebwA-A*krVLx%UiMlYFz_4tb;c+-)0=lb+B(|yZz-@
zoP9b{EyDa$iqS2tC1xfYYS>F6cms|K@73lCU*rXE=)69PMq07-HbE1I=FHa$tIjUX
z7&nkpXkOGSs+n<ESGCA}ZS%}Y$RkM-rQ6>Pd(^i@w2_{<X0Rlz>4-zZlE#aR=S0kq
zJ%Fm{f+l-=#M|*RFySQ>#w*zGU2wos(#sZDC@68V|JJkPKNJNibukt>MQJu$BRfko
zAoxc=UKOWc*T#+&aFIfDEbP!<5P~FIJcc_Ai#ABBb(|__0#^<4eI}=&B8Gpkdvxi+
z_R+jyhOC$ogGkO}GI(=a4)1F$sA`Aw=%W{#L5EqRmbQJJ#L6`%L!ei9Q*M~-mvIVN
zwY?Ua@gghA;VON)(D0qqtFydHeLPM3T=aMeV?q2$EzTUYmK$kV2Xv)fR(hk*;=pJn
zXi{iuqT<&<+-~-^PR=hB&^7RA#N|CC368Ou`vfSU+X?5D%>~QJ5-3-2Qqe|swL;M<
zB&u&rT`(%CdlQWC^<GJ=EgovSf)<?Wl#V>fEf~cZx%r-Z#WHnXUKkDL#z|30F3Ql(
zh&}chbTu4UUkJhrbbRi1_=tZt!stMOx8~FEqh8I=1mZb>%aNY8#|Vm7tn2S>Q=>_l
z>z~!Bs<rur5x)z%I9!-$h;G*JKcoNN6rO5_p0dzTP!@>an*tgJ59*(HF#mr1+|T?I
z^|J|o-ED>X8$uoGw;juW1^&7(0OdDe7UHj4v;OMlUv~}s``|+V_a1`3TKhG-Ao>j@
zhW*cLjelkNuKzQcp#P0Um+pVgOa2<ruQX)8k(U1r>F-&|KQsLv<nG@v{X0wfXBLv*
zSZE&q_pIfA^+xA!c>bNm{I4vLf5Y<ctmdCtD1T$=`Wu%2$a4PI8w5-L-?N_I|4qO4
si)g>`2+;qY1^pHI>tl@n8<L0lA9_(`d3c2H3YZU{{Rg8hPrldw4+@yODF6Tf

diff --git a/fluidsim/operators/fft/test/__init__.py b/fluidsim/operators/fft/test/__init__.py
new file mode 100644
diff --git a/fluidsim/operators/fft/test/test_compare.py b/fluidsim/operators/fft/test/test_compare.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC90ZXN0L3Rlc3RfY29tcGFyZS5weQ==
--- /dev/null
+++ b/fluidsim/operators/fft/test/test_compare.py
@@ -0,0 +1,40 @@
+
+from __future__ import division, print_function
+
+import unittest
+
+import sys
+
+import numpy as np
+
+from fluidsim.operators.fft import easypyfft
+try:
+    from fluidsim.operators.fft import fftw2dmpicy
+    FFTWMPI = True
+except ImportError:
+    FFTWMPI = False
+
+
+@unittest.skipIf(not FFTWMPI, 'fftw2dmpicy fails to be imported.')
+@unittest.skipIf(sys.platform.startswith("win"), "Will fail on Windows")
+class TestFFT2Dmpi(unittest.TestCase):
+
+    def test_fft(self):
+        """Should be able to..."""
+        nx = 8
+        ny = 8
+        n0, n1 = ny, nx
+        op = fftw2dmpicy.FFT2Dmpi(n0, n1, TRANSPOSED=False)
+        op2 = easypyfft.FFTW2DReal2Complex(nx, ny)
+
+        func_fft = (np.random.random(op.shapeK_loc) +
+                    1.j*np.random.random(op.shapeK_loc))
+
+        func = op.ifft2d(func_fft)
+        func2 = op2.ifft2d(func_fft)
+
+        self.assertTrue(np.allclose(func, func2))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fluidsim/operators/fft/test/test_easypyfft.py b/fluidsim/operators/fft/test/test_easypyfft.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC90ZXN0L3Rlc3RfZWFzeXB5ZmZ0LnB5
--- /dev/null
+++ b/fluidsim/operators/fft/test/test_easypyfft.py
@@ -0,0 +1,143 @@
+
+import unittest
+
+import numpy as np
+
+
+from fluidsim.operators.fft import easypyfft
+
+# from fluiddyn.io import stdout_redirected
+
+
+class TestFFTW1DReal2Complex(unittest.TestCase):
+
+    def test_fft(self):
+        """Should be able to..."""
+        nx = 128
+        op = easypyfft.FFTW1DReal2Complex(nx)
+
+        func_fft = np.zeros(op.shapeK, dtype=np.complex128)
+        func_fft[0] = 1
+
+        self.compute_and_check(func_fft, op)
+
+    def compute_and_check(self, func_fft, op):
+
+        func = op.ifft(func_fft)
+        back_fft = op.fft(func)
+        back = op.ifft(back_fft)
+
+        self.assertTrue(np.allclose(func_fft, back_fft))
+        self.assertTrue(np.allclose(func, back))
+
+        energyX = op.compute_energy_from_spatial(func)
+        energyK = op.compute_energy_from_Fourier(func_fft)
+        energyKback = op.compute_energy_from_Fourier(back_fft)
+
+        self.assertAlmostEqual(energyX, energyK)
+        self.assertAlmostEqual(energyK, energyKback)
+
+    def test_fft_random(self):
+        """Should be able to..."""
+        nx = 128
+        op = easypyfft.FFTW1DReal2Complex(nx)
+
+        func_fft = (np.random.random(op.shapeK)
+                    + 1.j*np.random.random(op.shapeK))
+        func = op.ifft(func_fft)
+        func_fft = op.fft(func)
+
+        self.compute_and_check(func_fft, op)
+
+
+class TestFFTW2DReal2Complex(unittest.TestCase):
+
+    def test_fft(self):
+        """Should be able to..."""
+        nx = 4
+        ny = 2
+        op = easypyfft.FFTW2DReal2Complex(nx, ny)
+
+        func_fft = np.zeros(op.shapeK, dtype=np.complex128)
+        func_fft[0, 1] = 1
+
+        self.compute_and_check(func_fft, op)
+
+    def compute_and_check(self, func_fft, op):
+
+        energyK = op.compute_energy_from_Fourier(func_fft)
+
+        func = op.ifft2d(func_fft)
+        energyX = op.compute_energy_from_spatial(func)
+
+        back_fft = op.fft2d(func)
+        energyKback = op.compute_energy_from_Fourier(back_fft)
+        back = op.ifft2d(back_fft)
+
+        self.assertTrue(np.allclose(func_fft, back_fft))
+        self.assertTrue(np.allclose(func, back))
+
+        self.assertAlmostEqual(energyX, energyK)
+        self.assertAlmostEqual(energyK, energyKback)
+
+    def test_fft_random(self):
+        """Should be able to..."""
+        nx = 64
+        ny = 128
+        op = easypyfft.FFTW2DReal2Complex(nx, ny)
+
+        func_fft = (np.random.random(op.shapeK)
+                    + 1.j*np.random.random(op.shapeK))
+        func = op.ifft2d(func_fft)
+        func_fft = op.fft2d(func)
+
+        self.compute_and_check(func_fft, op)
+
+
+class TestFFTW3DReal2Complex(unittest.TestCase):
+
+    def test_fft(self):
+        """Should be able to..."""
+        nx = 4
+        ny = 2
+        nz = 8
+        op = easypyfft.FFTW3DReal2Complex(nx, ny, nz)
+
+        func_fft = np.zeros(op.shapeK, dtype=np.complex128)
+        func_fft[0, 0, 1] = 1
+
+        self.compute_and_check(func_fft, op)
+
+    def compute_and_check(self, func_fft, op):
+
+        energyK = op.compute_energy_from_Fourier(func_fft)
+
+        func = op.ifft3d(func_fft)
+        energyX = op.compute_energy_from_spatial(func)
+
+        back_fft = op.fft3d(func)
+        energyKback = op.compute_energy_from_Fourier(back_fft)
+        back = op.ifft3d(back_fft)
+
+        self.assertTrue(np.allclose(func_fft, back_fft))
+        self.assertTrue(np.allclose(func, back))
+
+        self.assertAlmostEqual(energyX, energyK)
+        self.assertAlmostEqual(energyK, energyKback)
+
+    def test_fft_random(self):
+        """Should be able to..."""
+        nx = 8
+        ny = 8
+        nz = 32
+        op = easypyfft.FFTW3DReal2Complex(nx, ny, nz)
+
+        func_fft = (np.random.random(op.shapeK)
+                    + 1.j*np.random.random(op.shapeK))
+        func = op.ifft3d(func_fft)
+        func_fft = op.fft3d(func)
+
+        self.compute_and_check(func_fft, op)
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fluidsim/operators/fft/test/test_fftw2dmpicy.py b/fluidsim/operators/fft/test/test_fftw2dmpicy.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL2ZmdC90ZXN0L3Rlc3RfZmZ0dzJkbXBpY3kucHk=
--- /dev/null
+++ b/fluidsim/operators/fft/test/test_fftw2dmpicy.py
@@ -0,0 +1,70 @@
+
+import unittest
+import numpy as np
+import sys
+
+try:
+    from fluidsim.operators.fft import fftw2dmpicy
+    FFTWMPI = True
+except ImportError:
+    FFTWMPI = False
+
+
+@unittest.skipIf(not FFTWMPI, 'fftw2dmpicy fails to be imported.')
+@unittest.skipIf(sys.platform.startswith("win"), "Will fail on Windows")
+class TestFFT2Dmpi(unittest.TestCase):
+
+    def test_fft(self):
+        """Should be able to..."""
+        nx = 4
+        ny = 2
+        n0, n1 = ny, nx
+        op = fftw2dmpicy.FFT2Dmpi(n0, n1, TRANSPOSED=False)
+
+        func_fft = np.zeros(op.shapeK_loc, dtype=np.complex128)
+        func_fft[0, 1] = 1
+
+        self.compute_and_check(func_fft, op)
+
+    def compute_and_check(self, func_fft, op):
+
+        energyK = op.compute_energy_from_Fourier(func_fft)
+
+        func = op.ifft2d(func_fft)
+        energyX = op.compute_energy_from_spatial(func)
+
+        back_fft = op.fft2d(func)
+        energyKback = op.compute_energy_from_Fourier(back_fft)
+        back = op.ifft2d(back_fft)
+
+        # mean_fft = op.get_mean_fft(func_fft)
+
+        self.assertTrue(np.allclose(func_fft, back_fft))
+        self.assertTrue(np.allclose(func, back))
+
+        self.assertAlmostEqual(energyX, energyK)
+        self.assertAlmostEqual(energyK, energyKback)
+
+    def test_fft_random(self):
+        """Should be able to..."""
+        nx = 32
+        ny = 64
+        op = fftw2dmpicy.FFT2Dmpi(nx, ny, TRANSPOSED=False)
+
+        func_fft = (np.random.random(op.shapeK_loc)
+                    + 1.j*np.random.random(op.shapeK_loc))
+
+        func = op.ifft2d(func_fft)
+        func_fft = op.fft2d(func)
+
+        self.compute_and_check(func_fft, op)
+
+
+
+
+
+
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fluidsim/operators/op_finitediff.py b/fluidsim/operators/op_finitediff.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL29wX2Zpbml0ZWRpZmYucHk=
--- /dev/null
+++ b/fluidsim/operators/op_finitediff.py
@@ -0,0 +1,246 @@
+"""Operators finite differences (:mod:`fluidsim.operators.op_finitediff`)
+===============================================================================
+
+.. currentmodule:: fluidsim.operators.op_finitediff
+
+Provides:
+
+.. autoclass:: OperatorFiniteDiff1DPeriodic
+   :members:
+   :private-members:
+
+.. autoclass:: OperatorFiniteDiff2DPeriodic
+   :members:
+   :private-members:
+
+"""
+import numpy as np
+import scipy.sparse as sparse
+
+
+class OperatorFiniteDiff1DPeriodic(object):
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        attribs = {'nx': 48, 'Lx': 8.}
+        params.set_child('oper', attribs=attribs)
+
+    def __init__(self, params=None):
+
+        if not params.ONLY_COARSE_OPER:
+            nx = int(params.oper.nx)
+        else:
+            nx = 4
+
+        Lx = float(params.oper.Lx)
+
+        print(nx, Lx)
+        
+        self.nx = nx
+        self.size = nx
+        self.shape = [nx]
+        self.shapeX_loc = self.shape
+        self.Lx = Lx
+        self.deltax = Lx/nx
+        dx = self.deltax
+
+        self.xs = np.linspace(0, Lx, nx)
+
+        self.sparse_px = sparse.diags(
+            diagonals=[-np.ones(nx-1), np.ones(nx-1), -1, 1],
+            offsets=[-1, 1, nx-1, -(nx-1)])
+        self.sparse_px = self.sparse_px/(2*dx)
+
+        self.sparse_pxx = sparse.diags(
+            diagonals=[np.ones(nx-1), -2*np.ones(nx), np.ones(nx-1), 1, 1],
+            offsets=[-1, 0, 1, nx-1, -(nx-1)])
+
+        self.sparse_pxx = self.sparse_pxx/dx**2
+
+    def px(self, a):
+        return self.sparse_px.dot(a.flat)
+
+    def pxx(self, a):
+        return self.sparse_pxx.dot(a.flat)
+
+    def identity(self):
+        return sparse.identity(self.size)
+
+    def produce_str_describing_oper(self):
+        """Produce a string describing the operator."""
+        if (self.Lx/np.pi).is_integer():
+            str_Lx = repr(int(self.Lx/np.pi)) + 'pi'
+        else:
+            str_Lx = '{:.3f}'.format(self.Lx).rstrip('0')
+
+        return ('L='+str_Lx+'_{}').format(self.nx)
+
+    def produce_long_str_describing_oper(self):
+        """Produce a string describing the operator."""
+        if (self.Lx/np.pi).is_integer():
+            str_Lx = repr(int(self.Lx/np.pi)) + 'pi'
+        else:
+            str_Lx = '{:.3f}'.format(self.Lx).rstrip('0')
+        return (
+            'Finite difference operator 1D,\n'
+            'nx = {0:6d}\n'.format(self.nx) +
+            'Lx = ' + str_Lx + '\n')
+
+
+class OperatorFiniteDiff2DPeriodic(OperatorFiniteDiff1DPeriodic):
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+
+        attribs = {'nx': 48,
+                   'ny': 48,
+                   'Lx': 8,
+                   'Ly': 8}
+        params.set_child('oper', attribs=attribs)
+
+    def __init__(self, params=None):
+
+        Lx = float(params.oper.Lx)
+        Ly = float(params.oper.Ly)
+
+        if not params.ONLY_COARSE_OPER:
+            nx = int(params.oper.nx)
+            ny = int(params.oper.ny)
+        else:
+            nx = 4
+            ny = 4
+
+        self.nx = nx
+        self.ny = ny
+        self.shape = [ny, nx]
+        size = nx*ny
+        self.size = size
+        self.Lx = Lx
+        self.Ly = Ly
+        self.deltax = Lx/nx
+        self.deltay = Ly/ny
+        dx = self.deltax
+        dy = self.deltay
+
+        self.xs = np.linspace(0, Lx, nx)
+        self.ys = np.linspace(0, Ly, ny)
+
+        def func_i1_mat(i0_mat, iv):
+            i1 = i0_mat % nx
+            i0 = i0_mat // nx
+            if iv == 0:
+                i1_mat = i0*nx + (i1+1) % nx
+            elif iv == 1:
+                i1_mat = i0*nx + (i1-1) % nx
+            else:
+                raise ValueError('Shouldn''t be here...')
+            return i1_mat
+
+        values = np.array([1, -1])/(2*dx)
+        self.sparse_px = self._create_sparse(values, func_i1_mat)
+
+        def func_i1_mat(i0_mat, iv):
+            i1 = i0_mat % nx
+            i0 = i0_mat // nx
+            if iv == 0:
+                i1_mat = i0_mat
+            elif iv == 1:
+                i1_mat = i0*nx + (i1+1) % nx
+            elif iv == 2:
+                i1_mat = i0*nx + (i1-1) % nx
+            else:
+                raise ValueError('Shouldn''t be here...')
+            return i1_mat
+
+        values = np.array([-2, 1, 1])/dx**2
+        self.sparse_pxx = self._create_sparse(values, func_i1_mat)
+
+        def func_i1_mat(i0_mat, iv):
+            i1 = i0_mat % nx
+            i0 = i0_mat // nx
+            if iv == 0:
+                i1_mat = ((i0+1)*nx) % size + i1
+            elif iv == 1:
+                i1_mat = ((i0-1)*nx) % size + i1
+            else:
+                raise ValueError('Shouldn''t be here...')
+            return i1_mat
+
+        values = np.array([1, -1])/(2*dy)
+        self.sparse_py = self._create_sparse(values, func_i1_mat)
+
+        def func_i1_mat(i0_mat, iv):
+            i1 = i0_mat % nx
+            i0 = i0_mat // nx
+            if iv == 0:
+                i1_mat = i0_mat
+            elif iv == 1:
+                i1_mat = ((i0+1)*nx) % size + i1
+            elif iv == 2:
+                i1_mat = ((i0-1)*nx) % size + i1
+            else:
+                raise ValueError('Shouldn''t be here...')
+            return i1_mat
+
+        values = np.array([-2, 1, 1])/dx**2
+        self.sparse_pyy = self._create_sparse(values, func_i1_mat)
+
+    def _create_sparse(self, values, func_i1_mat):
+        size = self.size
+        nb_values = len(values)
+        data = np.empty(size*nb_values)
+        i0s = np.empty(size*nb_values)
+        i1s = np.empty(size*nb_values)
+
+        for i0_mat in xrange(size):
+            for iv, v in enumerate(values):
+                data[nb_values*i0_mat+iv] = v
+                i0s[nb_values*i0_mat+iv] = i0_mat
+                i1s[nb_values*i0_mat+iv] = func_i1_mat(i0_mat, iv)
+        return sparse.coo_matrix(
+            (data, (i0s, i1s)), shape=(size, size))
+
+    def py(self, a):
+        return self.sparse_py.dot(a.flat)
+
+    def pyy(self, a):
+        return self.sparse_pyy.dot(a.flat)
+
+    def produce_str_describing_oper(self):
+        """Produce a string describing the operator."""
+        if (self.Lx/np.pi).is_integer():
+            str_Lx = repr(int(self.Lx/np.pi)) + 'pi'
+        else:
+            str_Lx = '{:.3f}'.format(self.Lx).rstrip('0')
+        if (self.Ly/np.pi).is_integer():
+            str_Ly = repr(int(self.Ly/np.pi)) + 'pi'
+        else:
+            str_Ly = '{:.3f}'.format(self.Ly).rstrip('0')
+        return ('L='+str_Lx+'x'+str_Ly+'_{}x{}').format(
+            self.nx, self.ny)
+
+    def produce_long_str_describing_oper(self):
+        """Produce a string describing the operator."""
+        if (self.Lx/np.pi).is_integer():
+            str_Lx = repr(int(self.Lx/np.pi)) + 'pi'
+        else:
+            str_Lx = '{:.3f}'.format(self.Lx).rstrip('0')
+        if (self.Ly/np.pi).is_integer():
+            str_Ly = repr(int(self.Ly/np.pi)) + 'pi'
+        else:
+            str_Ly = '{:.3f}'.format(self.Ly).rstrip('0')
+        return (
+            'Finite difference operator 2D,\n'
+            'nx = {0:6d} ; ny = {1:6d}\n'.format(self.nx, self.ny) +
+            'Lx = ' + str_Lx + ' ; Ly = ' + str_Ly + '\n')
+
+
+if __name__ == '__main__':
+    nx = 3
+    ny = 3
+    oper = OperatorFiniteDiff2DPeriodic([ny, nx], [nx/2., ny/2.])
+    a = np.arange(nx*ny).reshape([ny, nx])
diff --git a/fluidsim/operators/test/__init__.py b/fluidsim/operators/test/__init__.py
new file mode 100644
diff --git a/fluidsim/operators/test/test_operators.py b/fluidsim/operators/test/test_operators.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vb3BlcmF0b3JzL3Rlc3QvdGVzdF9vcGVyYXRvcnMucHk=
--- /dev/null
+++ b/fluidsim/operators/test/test_operators.py
@@ -0,0 +1,161 @@
+
+import unittest
+import numpy as np
+import sys
+
+import fluiddyn as fld
+
+from fluiddyn.io import stdout_redirected
+
+from fluidsim.base.solvers.pseudo_spect import info_solver_ps
+
+from fluidsim.operators.operators import OperatorsPseudoSpectral2D
+
+
+def create_oper(type_fft='FFTWCY'):
+
+    params = fld.simul.create_params(info_solver_ps)
+
+    nh = 8
+    params.oper.nx = nh
+    params.oper.ny = nh
+    Lh = 6.
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    params.oper.type_fft = type_fft
+
+    params.oper.coef_dealiasing = 2./3
+
+    with stdout_redirected():
+        oper = OperatorsPseudoSpectral2D(params=params)
+
+    return oper
+
+
+@unittest.skipIf(sys.platform.startswith("win"), "Will fail on Windows")
+class TestOperators(unittest.TestCase):
+    def test_create(self):
+        """Should be able to ..."""
+        oper = create_oper('FFTWCY')
+
+        rot = oper.random_arrayX()
+        rot_fft = oper.fft2(rot)
+        rot_fft[0, 0] = 0.
+
+        ux_fft, uy_fft = oper.vecfft_from_rotfft(rot_fft)
+        rot2_fft = oper.rotfft_from_vecfft(ux_fft, uy_fft)
+
+        self.assertTrue(np.allclose(rot2_fft, rot_fft))
+
+        oper_py = create_oper('FFTWPY')
+
+        ux_fft, uy_fft = oper_py.vecfft_from_rotfft(rot_fft)
+        rot2_fft = oper_py.rotfft_from_vecfft(ux_fft, uy_fft)
+
+        self.assertTrue(np.allclose(rot2_fft, rot_fft))
+
+        px_rot_fft, py_rot_fft = oper.gradfft_from_fft(rot_fft)
+        px_rot2_fft, py_rot2_fft = oper_py.gradfft_from_fft(rot_fft)
+
+        self.assertTrue(np.allclose(px_rot_fft, px_rot2_fft))
+
+        # fld.ipydebug()
+
+    def test_tendency(self):
+
+        oper = create_oper('FFTWCY')
+        rot = oper.random_arrayX()
+        rot_fft = oper.fft2(rot)
+        rot_fft[0, 0] = 0.
+        oper.dealiasing(rot_fft)
+
+        ux_fft, uy_fft = oper.vecfft_from_rotfft(rot_fft)
+        ux = oper.ifft2(ux_fft)
+        uy = oper.ifft2(uy_fft)
+
+        px_rot_fft, py_rot_fft = oper.gradfft_from_fft(rot_fft)
+        px_rot = oper.ifft2(px_rot_fft)
+        py_rot = oper.ifft2(py_rot_fft)
+
+        Frot = -ux*px_rot - uy*py_rot
+        Frot_fft = oper.fft2(Frot)
+        oper.dealiasing(Frot_fft)
+
+        T_rot = np.real(Frot_fft.conj()*rot_fft)
+
+        ratio = (oper.sum_wavenumbers(T_rot) /
+                 oper.sum_wavenumbers(abs(T_rot)))
+
+        self.assertGreater(1e-15, ratio)
+
+        # print ('sum(T_rot) = {0:9.4e} ; '
+        #        'sum(abs(T_rot)) = {1:9.4e}').format(
+        #            oper.sum_wavenumbers(T_rot),
+        #            oper.sum_wavenumbers(abs(T_rot)))
+
+        oper2 = create_oper('FFTWPY')
+
+        ux_fftpy, uy_fftpy = oper2.vecfft_from_rotfft(rot_fft)
+
+        self.assertTrue(np.allclose(ux_fft, ux_fftpy))
+        self.assertTrue(np.allclose(uy_fft, uy_fftpy))
+
+        uxpy = oper2.ifft2(ux_fftpy)
+        uypy = oper2.ifft2(uy_fftpy)
+
+        self.assertTrue(np.allclose(ux_fft, ux_fftpy))
+        self.assertTrue(np.allclose(uy_fft, uy_fftpy))
+
+        self.assertTrue(np.allclose(ux, uxpy))
+        self.assertTrue(np.allclose(uy, uypy))
+
+        px_rot_fftpy, py_rot_fftpy = oper2.gradfft_from_fft(rot_fft)
+        px_rotpy = oper2.ifft2(px_rot_fftpy)
+        py_rotpy = oper2.ifft2(py_rot_fftpy)
+
+        Frotpy = -uxpy*px_rotpy - uypy*py_rotpy
+        Frot_fftpy = oper2.fft2(Frotpy)
+        oper2.dealiasing(Frot_fftpy)
+
+        T_rotpy = np.real(Frot_fftpy.conj()*rot_fft)
+
+        ratio = (oper2.sum_wavenumbers(T_rotpy) /
+                 oper2.sum_wavenumbers(abs(T_rotpy)))
+
+        # print ('sum(T_rot) = {0:9.4e} ; '
+        #        'sum(abs(T_rot)) = {1:9.4e}').format(
+        #            oper2.sum_wavenumbers(T_rotpy),
+        #            oper2.sum_wavenumbers(abs(T_rotpy)))
+
+        self.assertGreater(1e-15, ratio)
+
+    def test_laplacian2(self):
+        oper = create_oper('FFTWCY')
+        ff = oper.random_arrayX()
+        ff_fft = oper.fft2(ff)
+        ff_fft[0, 0] = 0.
+
+        lap_fft = oper.laplacian2_fft(ff_fft)
+        ff_fft_back = oper.invlaplacian2_fft(lap_fft)
+
+        self.assertTrue(np.allclose(ff_fft, ff_fft_back))
+
+    def test_monge_ampere(self):
+        oper = create_oper('FFTWCY')
+        a = oper.random_arrayX()
+        a_fft = oper.fft2(a)
+        a_fft[0, 0] = 0.
+
+        b = oper.random_arrayX()
+        b_fft = oper.fft2(b)
+        b_fft[0, 0] = 0.
+
+        ma_py = oper.monge_ampere_from_fft_python(a_fft, b_fft)
+        ma_cy = oper.monge_ampere_from_fft(a_fft, b_fft)
+
+        self.assertTrue(np.allclose(ma_py, ma_cy))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fluidsim/solvers/__init__.py b/fluidsim/solvers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/fluidsim/solvers/__init__.py
@@ -0,0 +1,16 @@
+"""Particular solvers (:mod:`fluidsim.solvers`)
+
+.. currentmodule:: fluidsim.solvers
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   ns2d
+   sw1l
+   plate2d
+   ad1d
+   waves2d
+
+"""
diff --git a/fluidsim/solvers/ad1d/__init__.py b/fluidsim/solvers/ad1d/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9hZDFkL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/solvers/ad1d/__init__.py
@@ -0,0 +1,14 @@
+"""Advection-diffusion 1D solvers (:mod:`fluidsim.solvers.ad1d`)
+======================================================================
+
+.. currentmodule:: fluidsim.solvers.ad1d
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   solver
+
+
+"""
diff --git a/fluidsim/solvers/ad1d/init_fields.py b/fluidsim/solvers/ad1d/init_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9hZDFkL2luaXRfZmllbGRzLnB5
--- /dev/null
+++ b/fluidsim/solvers/ad1d/init_fields.py
@@ -0,0 +1,32 @@
+
+"""InitFieldsNS2D"""
+
+import numpy as np
+
+from fluidsim.base.init_fields import InitFieldsBase
+
+
+class InitFieldsAD1D(InitFieldsBase):
+    """Init the fields for the solver AD1D."""
+
+    implemented_flows = ['GAUSSIAN', 'COS']
+
+    def __call__(self):
+        """Init the state (in physical and Fourier space) and time"""
+
+        type_flow_init = self.get_and_check_type_flow_init()
+
+        if type_flow_init == 'GAUSSIAN':
+            self.init_fields_gaussian()
+        elif type_flow_init == 'COS':
+            self.init_fields_cos()
+        else:
+            raise ValueError('bad value of params.type_flow_init')
+
+    def init_fields_gaussian(self):
+        s = np.exp(-(10*(self.oper.xs-self.oper.Lx/2))**2)
+        self.sim.state.state_phys.data[0] = s
+
+    def init_fields_cos(self):
+        s = np.cos(2*np.pi*self.oper.xs/self.oper.Lx)
+        self.sim.state.state_phys.data[0] = s
diff --git a/fluidsim/solvers/ad1d/output/__init__.py b/fluidsim/solvers/ad1d/output/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9hZDFkL291dHB1dC9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/fluidsim/solvers/ad1d/output/__init__.py
@@ -0,0 +1,40 @@
+
+
+import numpy as np
+
+from fluidsim.base.output import OutputBase
+
+
+class Output(OutputBase):
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.Output.set_child('classes')
+        classes = info_solver.classes.Output.classes
+
+        base_name_mod = 'fluidsim.solvers.ad1d.output'
+
+        classes.set_child(
+            'PrintStdOut',
+            attribs={'module_name': base_name_mod+'.print_stdout',
+                     'class_name': 'PrintStdOutAD1D'})
+
+        classes.set_child(
+            'PhysFields',
+            attribs={'module_name': 'fluidsim.base.output.phys_fields',
+                     'class_name': 'PhysFieldsBase1D'})
+
+    @staticmethod
+    def _complete_params_with_default(params, info_solver):
+        """This static method is used to complete the *params* container.
+        """
+        OutputBase._complete_params_with_default(params, info_solver)
+
+        params.output.phys_fields.field_to_plot = 's'
+
+    def compute_energy(self):
+        return 0.5*np.mean(self.sim.state.state_phys['s']**2)
diff --git a/fluidsim/solvers/ad1d/output/print_stdout.py b/fluidsim/solvers/ad1d/output/print_stdout.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9hZDFkL291dHB1dC9wcmludF9zdGRvdXQucHk=
--- /dev/null
+++ b/fluidsim/solvers/ad1d/output/print_stdout.py
@@ -0,0 +1,9 @@
+
+from fluidsim.solvers.ns2d.output.print_stdout import PrintStdOutNS2D
+
+
+class PrintStdOutAD1D(PrintStdOutNS2D):
+    """Used to print in both the stdout and the stdout.txt file, and also
+    to print simple info on the current state of the simulation.
+
+    """
diff --git a/fluidsim/solvers/ad1d/solver.py b/fluidsim/solvers/ad1d/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9hZDFkL3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/ad1d/solver.py
@@ -0,0 +1,141 @@
+"""AD1D solver (:mod:`fluidsim.solvers.ad1d.solver`)
+=========================================================
+
+.. currentmodule:: fluidsim.solvers.ad1d.solver
+
+Provides:
+
+.. autoclass:: Simul
+   :members:
+   :private-members:
+
+
+"""
+
+from fluidsim.base.solvers.base import SimulBase
+from fluidsim.operators.setofvariables import SetOfVariables
+from fluidsim.base.solvers.finite_diff import InfoSolverFiniteDiff
+
+
+info_solver = InfoSolverFiniteDiff()
+
+package = 'fluidsim.solvers.ad1d'
+info_solver.module_name = package + '.solver'
+info_solver.class_name = 'Simul'
+info_solver.short_name = 'AD1D'
+
+classes = info_solver.classes
+
+classes.State.module_name = package + '.state'
+classes.State.class_name = 'StateAD1D'
+
+classes.InitFields.module_name = package + '.init_fields'
+classes.InitFields.class_name = 'InitFieldsAD1D'
+
+classes.Output.module_name = package + '.output'
+classes.Output.class_name = 'Output'
+
+# classes.Forcing.module_name = package + '.forcing'
+# classes.Forcing.class_name = 'ForcingAD1D'
+
+
+info_solver.complete_with_classes()
+
+
+class Simul(SimulBase):
+    r"""Advection-diffusion solver 1D.
+
+    Notes
+    -----
+
+    .. |p| mathmacro:: \partial
+
+    We use a finite difference method with the Crank-Nicolson time
+    scheme to solve the equation
+
+    .. math:: \p_t s + U \p_x s = D(s),
+
+    where :math:`d(s)` is the dissipation term and :math:`U` is a
+    constant velocity.
+
+    """
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        SimulBase._complete_params_with_default(params)
+        attribs = {'U': 1.}
+        params.set_attribs(attribs)
+
+    def __init__(self, params):
+        # the common initialization with the AD1D info_solver:
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_phys=None):
+        """Compute the "nonlinear" tendencies."""
+        tendencies = SetOfVariables(
+            like_this_sov=self.state.state_phys,
+            name_type_variables='tendencies', value=0.)
+
+        if self.params.FORCING:
+            tendencies += self.forcing.tendencies
+
+        return tendencies
+
+    def linear_operator(self):
+        """Compute the linear operator as a matrix."""
+
+        return (- self.params.U*self.oper.sparse_px
+                + self.params.nu_2*(self.oper.sparse_pxx))
+
+
+if __name__ == "__main__":
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.U = 1.
+
+    params.short_name_type_run = 'test'
+
+    params.oper.nx = 200
+    params.oper.Lx = 1.
+
+    # params.oper.type_fft = 'FFTWPY'
+
+    params.time_stepping.type_time_scheme = 'RK2'
+
+    # delta_x = params.oper.Lx/params.oper.nx
+    params.nu_2 = 0.01
+
+    params.time_stepping.t_end = 0.4
+    params.time_stepping.USE_CFL = True
+    # params.time_stepping.deltat0 = 0.1
+
+    params.init_fields.type_flow_init = 'GAUSSIAN'
+
+    params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 0.5
+
+    params.output.periods_plot.phys_fields = 0.
+
+    params.output.phys_fields.field_to_plot = 's'
+
+    # params.output.spectra.has_to_plot = 1  # False
+    # params.output.spatial_means.has_to_plot = 1  # False
+    # params.output.spect_energy_budg.has_to_plot = 1  # False
+    # params.output.increments.has_to_plot = 1  # False
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+
+    print 'x of s_max: ', sim.oper.xs[sim.state.state_phys.data.argmax()]
+
+    sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/ad1d/state.py b/fluidsim/solvers/ad1d/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9hZDFkL3N0YXRlLnB5
--- /dev/null
+++ b/fluidsim/solvers/ad1d/state.py
@@ -0,0 +1,54 @@
+"""State for the AD1D solver (:mod:`fluidsim.solvers.ad1d.state`)
+=======================================================================
+"""
+
+
+from fluidsim.base.state import StateBase
+
+from fluiddyn.util import mpi
+
+
+class StateAD1D(StateBase):
+    """Contains the variables corresponding to the state and handles the
+    access to other fields for the solver AD1D.
+
+    """
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.State.set_attribs({
+            'keys_state_phys': ['s'],
+            'keys_computable': [],
+            'keys_phys_needed': ['s'],
+            'keys_linear_eigenmodes': ['s']
+        })
+
+    def compute(self, key, SAVE_IN_DICT=True, RAISE_ERROR=True):
+        it = self.sim.time_stepping.it
+        if (key in self.vars_computed
+                and it == self.it_computed[key]):
+            return self.vars_computed[key]
+
+        if key == 'dx_s':
+            result = self.oper.grad(self.state_phys['s'])
+
+        else:
+            to_print = 'Do not know how to compute "'+key+'".'
+            if RAISE_ERROR:
+                raise ValueError(to_print)
+            else:
+                if mpi.rank == 0:
+                    print(to_print
+                          + '\nreturn an array of zeros.')
+
+                result = self.oper.constant_arrayX(value=0.)
+
+        if SAVE_IN_DICT:
+            self.vars_computed[key] = result
+            self.it_computed[key] = it
+
+        return result
diff --git a/fluidsim/solvers/ns2d/__init__.py b/fluidsim/solvers/ns2d/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/solvers/ns2d/__init__.py
@@ -0,0 +1,13 @@
+"""Navier-Stokes 2D solvers (:mod:`fluidsim.solvers.ns2d`)
+================================================================
+
+.. currentmodule:: fluidsim.solvers.ns2d
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   solver
+
+"""
diff --git a/fluidsim/solvers/ns2d/forcing.py b/fluidsim/solvers/ns2d/forcing.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL2ZvcmNpbmcucHk=
--- /dev/null
+++ b/fluidsim/solvers/ns2d/forcing.py
@@ -0,0 +1,32 @@
+
+
+from fluidsim.base.forcing import ForcingBasePseudoSpectral
+
+from fluidsim.base.forcing.specific import Proportional
+
+from fluidsim.base.forcing.specific import \
+    TimeCorrelatedRandomPseudoSpectral as Random
+
+
+class ForcingNS2D(ForcingBasePseudoSpectral):
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        ForcingBasePseudoSpectral._complete_info_solver(info_solver)
+        classes = info_solver.classes.Forcing.classes
+
+        package = 'fluidsim.solvers.ns2d.forcing'
+
+        classes.set_child(
+            'Random',
+            attribs={'module_name': package,
+                     'class_name': 'Random'})
+
+        classes.set_child(
+            'Proportional',
+            attribs={'module_name': package,
+                     'class_name': 'Proportional'})
diff --git a/fluidsim/solvers/ns2d/init_fields.py b/fluidsim/solvers/ns2d/init_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL2luaXRfZmllbGRzLnB5
--- /dev/null
+++ b/fluidsim/solvers/ns2d/init_fields.py
@@ -0,0 +1,45 @@
+
+"""InitFieldsNS2D"""
+
+from fluiddyn.util import mpi
+
+from fluidsim.base.init_fields import InitFieldsBase
+
+
+
+class InitFieldsNS2D(InitFieldsBase):
+    """Init the fields for the solver NS2D."""
+
+    implemented_flows = ['NOISE', 'CONSTANT', 'LOAD_FILE', 'DIPOLE', 'JET']
+
+    def __call__(self):
+        """Init the state (in physical and Fourier space) and time"""
+        sim = self.sim
+
+        type_flow_init = self.get_and_check_type_flow_init()
+
+        if type_flow_init == 'DIPOLE':
+            rot_fft, ux_fft, uy_fft = self.init_fields_1dipole()
+            tasks_complete_init = ['Fourier_to_phys']
+        elif type_flow_init == 'JET':
+            rot_fft, ux_fft, uy_fft = self.init_fields_jet()
+            tasks_complete_init = ['Fourier_to_phys']
+        elif type_flow_init == 'NOISE':
+            rot_fft, ux_fft, uy_fft = self.init_fields_noise()
+            tasks_complete_init = ['Fourier_to_phys']
+        elif type_flow_init == 'LOAD_FILE':
+            self.get_state_from_file(self.params.init_fields.path_file)
+            tasks_complete_init = []
+        elif type_flow_init == 'CONSTANT':
+            rot_fft = sim.oper.constant_arrayK(value=0.)
+            if mpi.rank == 0:
+                rot_fft[1, 0] = 1.
+            tasks_complete_init = ['Fourier_to_phys']
+        else:
+            raise ValueError('bad value of params.type_flow_init')
+
+        if 'Fourier_to_phys' in tasks_complete_init:
+            sim.oper.dealiasing(rot_fft)
+            sim.state.state_fft['rot_fft'] = rot_fft
+
+            sim.state.statephys_from_statefft()
diff --git a/fluidsim/solvers/ns2d/output/__init__.py b/fluidsim/solvers/ns2d/output/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL291dHB1dC9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/fluidsim/solvers/ns2d/output/__init__.py
@@ -0,0 +1,70 @@
+
+
+import numpy as np
+
+from fluidsim.base.output import OutputBasePseudoSpectral
+
+
+class Output(OutputBasePseudoSpectral):
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+
+        OutputBasePseudoSpectral._complete_info_solver(info_solver)
+
+        classes = info_solver.classes.Output.classes
+
+        base_name_mod = 'fluidsim.solvers.ns2d.output'
+
+        classes.PrintStdOut.module_name = base_name_mod + '.print_stdout'
+        classes.PrintStdOut.class_name = 'PrintStdOutNS2D'
+
+        classes.set_child(
+            'Spectra',
+            attribs={'module_name': base_name_mod + '.spectra',
+                     'class_name': 'SpectraNS2D'})
+
+        classes.set_child(
+            'spatial_means',
+            attribs={'module_name': base_name_mod + '.spatial_means',
+                     'class_name': 'SpatialMeansNS2D'})
+
+        attribs = {
+            'module_name': base_name_mod + '.spect_energy_budget',
+            'class_name': 'SpectralEnergyBudgetNS2D'}
+        classes.set_child('spect_energy_budg', attribs=attribs)
+
+        attribs = {
+            'module_name': 'fluidsim.base.output.increments',
+            'class_name': 'Increments'}
+        classes.set_child('increments', attribs=attribs)
+
+    @staticmethod
+    def _complete_params_with_default(params, info_solver):
+        """This static method is used to complete the *params* container.
+        """
+        OutputBasePseudoSpectral._complete_params_with_default(
+            params, info_solver)
+
+        params.output.phys_fields.field_to_plot = 'rot'
+
+    def compute_energy_fft(self):
+        rot_fft = self.sim.state.state_fft['rot_fft']
+        ux_fft, uy_fft = self.vecfft_from_rotfft(rot_fft)
+        return (np.abs(ux_fft)**2+np.abs(uy_fft)**2)/2
+
+    def compute_enstrophy_fft(self):
+        rot_fft = self.sim.state.state_fft['rot_fft']
+        return np.abs(rot_fft)**2/2
+
+    def compute_energy(self):
+        energy_fft = self.compute_energy_fft()
+        return self.sum_wavenumbers(energy_fft)
+
+    def compute_enstrophy(self):
+        enstrophy_fft = self.compute_enstrophy_fft()
+        return self.sum_wavenumbers(enstrophy_fft)
diff --git a/fluidsim/solvers/ns2d/output/print_stdout.py b/fluidsim/solvers/ns2d/output/print_stdout.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL291dHB1dC9wcmludF9zdGRvdXQucHk=
--- /dev/null
+++ b/fluidsim/solvers/ns2d/output/print_stdout.py
@@ -0,0 +1,130 @@
+
+from __future__ import print_function, division
+
+from time import time
+import numpy as np
+
+from fluidsim.base.output.print_stdout import PrintStdOutBase
+
+from fluiddyn.util import mpi
+
+
+class PrintStdOutNS2D(PrintStdOutBase):
+    """Used to print in both the stdout and the stdout.txt file, and also
+    to print simple info on the current state of the simulation.
+
+    """
+
+    def online_print(self):
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_print_info <= self.period_print):
+            return
+
+        tsim = self.sim.time_stepping.t
+        itsim = self.sim.time_stepping.it
+        deltatsim = self.sim.time_stepping.deltat
+
+        energy = self.output.compute_energy()
+        if mpi.rank == 0:
+            t_real_word = time()
+            if self.t_real_word_last == 0.:
+                duration_left = 0
+            else:
+                if self.params.time_stepping.USE_T_END:
+                    duration_left = int(np.round(
+                        (self.params.time_stepping.t_end - tsim)
+                        * (t_real_word-self.t_real_word_last)
+                        / (tsim - self.t_last_print_info)
+                    ))
+                else:
+                    duration_left = int(np.round(
+                        (self.params.time_stepping.it_end - itsim)
+                        * (t_real_word-self.t_real_word_last)
+                    ))
+            to_print = (
+                'it = {0:6d} ; t      = {1:9.3f} ; deltat       = {2:10.5g}\n'+
+                '              energy = {3:9.3e} ; Delta energy = {4:+9.3e}\n'+
+                '              estimated remaining duration = {5:6d} s')
+            to_print = to_print.format(
+                itsim, tsim, deltatsim,
+                energy, energy-self.energy_temp,
+                duration_left)
+            self.print_stdout(to_print)
+            self.t_real_word_last = t_real_word
+        self.energy_temp = energy
+        self.t_last_print_info = tsim
+
+    def load(self):
+        dico_results = {'name_solver': self.output.name_solver}
+        file_means = open(self.output.path_run+'/stdout.txt')
+        lines = file_means.readlines()
+
+        lines_t = []
+        lines_E = []
+        for il, line in enumerate(lines):
+            if line[0:4] == 'it =':
+                lines_t.append(line)
+            if line[0:22] == '              energy =':
+                lines_E.append(line)
+
+        nt = len(lines_t)
+        if nt > 1:
+            nt -= 1
+
+        it = np.zeros(nt, dtype=np.int)
+        t = np.zeros(nt)
+        deltat = np.zeros(nt)
+
+        E = np.zeros(nt)
+        deltaE = np.zeros(nt)
+
+        for il in xrange(nt):
+            line = lines_t[il]
+            words = line.split()
+            it[il] = int(words[2])
+            t[il] = float(words[6])
+            deltat[il] = float(words[10])
+
+            line = lines_E[il]
+            words = line.split()
+            E[il] = float(words[2])
+            deltaE[il] = float(words[7])
+
+        dico_results['it'] = it
+        dico_results['t'] = t
+        dico_results['deltat'] = deltat
+        dico_results['E'] = E
+        dico_results['deltaE'] = deltaE
+
+        return dico_results
+
+    def plot(self):
+        dico_results = self.load()
+
+        t = dico_results['t']
+        deltat = dico_results['deltat']
+        E = dico_results['E']
+        deltaE = dico_results['deltaE']
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.55
+        width_axe = 0.85
+        height_axe = 0.4
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('deltat(t)')
+
+        ax1.set_title('info stdout, solver '+self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx))
+        ax1.hold(True)
+        ax1.plot(t, deltat, 'k', linewidth=2)
+
+        size_axe[1] = 0.08
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('t')
+        ax2.set_ylabel('E(t), deltaE(t)')
+        ax2.hold(True)
+        ax2.plot(t, E, 'k', linewidth=2)
+        ax2.plot(t, deltaE, 'b', linewidth=2)
diff --git a/fluidsim/solvers/ns2d/output/spatial_means.py b/fluidsim/solvers/ns2d/output/spatial_means.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL291dHB1dC9zcGF0aWFsX21lYW5zLnB5
--- /dev/null
+++ b/fluidsim/solvers/ns2d/output/spatial_means.py
@@ -0,0 +1,257 @@
+
+from __future__ import division, print_function
+
+import os
+import numpy as np
+
+
+from fluiddyn.util import mpi
+
+from fluidsim.base.output.spatial_means import SpatialMeansBase
+
+
+class SpatialMeansNS2D(SpatialMeansBase):
+    """A :class:`SpatialMean` object handles the saving of ."""
+
+    def save_one_time(self):
+        tsim = self.sim.time_stepping.t
+        self.t_last_save = tsim
+
+        energy_fft = self.output.compute_energy_fft()
+        enstrophy_fft = self.output.compute_enstrophy_fft()
+        energy = self.sum_wavenumbers(energy_fft)
+        enstrophy = self.sum_wavenumbers(enstrophy_fft)
+
+        f_d, f_d_hypo = self.sim.compute_freq_diss()
+        epsK = self.sum_wavenumbers(f_d*2*energy_fft)
+        epsK_hypo = self.sum_wavenumbers(f_d_hypo*2*energy_fft)
+        epsZ = self.sum_wavenumbers(f_d*2*enstrophy_fft)
+        epsZ_hypo = self.sum_wavenumbers(f_d_hypo*2*enstrophy_fft)
+
+        if self.sim.params.FORCING:
+            deltat = self.sim.time_stepping.deltat
+            Frot_fft = self.sim.forcing.get_forcing()['rot_fft']
+            Fx_fft, Fy_fft = self.vecfft_from_rotfft(Frot_fft)
+
+            rot_fft = self.sim.state.state_fft['rot_fft']
+            ux_fft, uy_fft = self.vecfft_from_rotfft(rot_fft)
+
+            PZ1_fft = np.real(
+                rot_fft.conj()*Frot_fft +
+                rot_fft*Frot_fft.conj())/2
+            PZ2_fft = (abs(Frot_fft)**2)*deltat/2
+
+            PZ1 = self.sum_wavenumbers(PZ1_fft)
+            PZ2 = self.sum_wavenumbers(PZ2_fft)
+
+            PK1_fft = np.real(
+                ux_fft.conj()*Fx_fft +
+                ux_fft*Fx_fft.conj() +
+                uy_fft.conj()*Fy_fft +
+                uy_fft*Fy_fft.conj())/2
+            PK2_fft = (abs(Fx_fft)**2+abs(Fy_fft)**2)*deltat/2
+
+            PK1 = self.sum_wavenumbers(PK1_fft)
+            PK2 = self.sum_wavenumbers(PK2_fft)
+
+        if mpi.rank == 0:
+            epsK_tot = epsK+epsK_hypo
+
+            self.file.write(
+                '####\ntime = {0:7.3f}\n'.format(tsim))
+            to_print = (
+'E    = {0:11.6e} ; Z         = {1:11.6e} \n'
+'epsK = {2:11.6e} ; epsK_hypo = {3:11.6e} ; epsK_tot = {4:11.6e} \n'
+'epsZ = {5:11.6e} ; epsZ_hypo = {6:11.6e} ; epsZ_tot = {7:11.6e} \n'
+).format(energy, enstrophy,
+         epsK, epsK_hypo, epsK+epsK_hypo,
+         epsZ, epsZ_hypo, epsZ+epsZ_hypo)
+            self.file.write(to_print)
+
+            if self.sim.params.FORCING:
+                PK_tot = PK1+PK2
+                to_print = (
+'PK1  = {0:11.6e} ; PK2       = {1:11.6e} ; PK_tot   = {2:11.6e} \n'
+'PZ1  = {3:11.6e} ; PZ2       = {4:11.6e} ; PZ_tot   = {5:11.6e} \n'
+).format(PK1, PK2, PK1+PK2, PZ1, PZ2, PZ1+PZ2)
+                self.file.write(to_print)
+
+            self.file.flush()
+            os.fsync(self.file.fileno())
+
+        if self.has_to_plot and mpi.rank == 0:
+
+            self.axe_a.plot(tsim, energy, 'k.')
+
+            self.axe_b.plot(tsim, epsK_tot, 'k.')
+            if self.sim.params.FORCING:
+                self.axe_b.plot(tsim, PK_tot, 'm.')
+
+            if (tsim-self.t_last_show >= self.period_show):
+                self.t_last_show = tsim
+                fig = self.axe_a.get_figure()
+                fig.canvas.draw()
+
+    def load(self):
+        dico_results = {'name_solver': self.output.name_solver}
+
+        file_means = open(self.path_file)
+        lines = file_means.readlines()
+
+        lines_t = []
+        lines_E = []
+        lines_PK = []
+        lines_PZ = []
+        lines_epsK = []
+        lines_epsZ = []
+
+        for il, line in enumerate(lines):
+            if line.startswith('time ='):
+                lines_t.append(line)
+            if line.startswith('E    ='):
+                lines_E.append(line)
+            if line.startswith('PK1  ='):
+                lines_PK.append(line)
+            if line.startswith('PZ1  ='):
+                lines_PZ.append(line)
+            if line.startswith('epsK ='):
+                lines_epsK.append(line)
+            if line.startswith('epsZ ='):
+                lines_epsZ.append(line)
+
+        nt = len(lines_t)
+        if nt > 1:
+            nt -= 1
+
+        t = np.empty(nt)
+        E = np.empty(nt)
+        Z = np.empty(nt)
+        PK1 = np.empty(nt)
+        PK2 = np.empty(nt)
+        PK_tot = np.empty(nt)
+        PZ1 = np.empty(nt)
+        PZ2 = np.empty(nt)
+        PZ_tot = np.empty(nt)
+        epsK = np.empty(nt)
+        epsK_hypo = np.empty(nt)
+        epsK_tot = np.empty(nt)
+        epsZ = np.empty(nt)
+        epsZ_hypo = np.empty(nt)
+        epsZ_tot = np.empty(nt)
+
+        for il in xrange(nt):
+            line = lines_t[il]
+            words = line.split()
+            t[il] = float(words[2])
+
+            line = lines_E[il]
+            words = line.split()
+            E[il] = float(words[2])
+            Z[il] = float(words[6])
+
+            if self.sim.params.FORCING:
+                line = lines_PK[il]
+                words = line.split()
+                PK1[il] = float(words[2])
+                PK2[il] = float(words[6])
+                PK_tot[il] = float(words[10])
+
+                line = lines_PZ[il]
+                words = line.split()
+                PZ1[il] = float(words[2])
+                PZ2[il] = float(words[6])
+                PZ_tot[il] = float(words[10])
+
+            line = lines_epsK[il]
+            words = line.split()
+            epsK[il] = float(words[2])
+            epsK_hypo[il] = float(words[6])
+            epsK_tot[il] = float(words[10])
+
+            line = lines_epsZ[il]
+            words = line.split()
+            epsZ[il] = float(words[2])
+            epsZ_hypo[il] = float(words[6])
+            epsZ_tot[il] = float(words[10])
+
+        dico_results['t'] = t
+        dico_results['E'] = E
+        dico_results['Z'] = Z
+
+        dico_results['PK1'] = PK1
+        dico_results['PK2'] = PK2
+        dico_results['PK_tot'] = PK_tot
+
+        dico_results['PZ1'] = PZ1
+        dico_results['PZ2'] = PZ2
+        dico_results['PZ_tot'] = PZ_tot
+
+        dico_results['epsK'] = epsK
+        dico_results['epsK_hypo'] = epsK_hypo
+        dico_results['epsK_tot'] = epsK_tot
+
+        dico_results['epsZ'] = epsZ
+        dico_results['epsZ_hypo'] = epsZ_hypo
+        dico_results['epsZ_tot'] = epsZ_tot
+        return dico_results
+
+    def plot(self):
+        dico_results = self.load()
+
+        t = dico_results['t']
+        E = dico_results['E']
+        Z = dico_results['Z']
+
+        PK_tot = dico_results['PK_tot']
+        PZ_tot = dico_results['PZ_tot']
+
+        epsK = dico_results['epsK']
+        epsK_hypo = dico_results['epsK_hypo']
+        epsK_tot = dico_results['epsK_tot']
+
+        epsZ = dico_results['epsZ']
+        epsZ_hypo = dico_results['epsZ_hypo']
+        epsZ_tot = dico_results['epsZ_tot']
+
+        width_axe = 0.85
+        height_axe = 0.4
+        x_left_axe = 0.12
+        z_bottom_axe = 0.55
+
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('E(t)')
+        ax1.hold(True)
+        ax1.plot(t, E, 'k', linewidth=2)
+
+        z_bottom_axe = 0.08
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('t')
+        ax2.set_ylabel('Z(t)')
+        ax2.hold(True)
+        ax2.plot(t, Z, 'k', linewidth=2)
+
+        z_bottom_axe = 0.55
+        size_axe[1] = z_bottom_axe
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('P_E(t), epsK(t)')
+        ax1.hold(True)
+        ax1.plot(t, PK_tot, 'c', linewidth=2)
+        ax1.plot(t, epsK, 'r', linewidth=2)
+        ax1.plot(t, epsK_hypo, 'g', linewidth=2)
+        ax1.plot(t, epsK_tot, 'k', linewidth=2)
+
+        z_bottom_axe = 0.08
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('t')
+        ax2.set_ylabel('P_Z(t), epsZ(t)')
+        ax2.hold(True)
+        ax2.plot(t, PZ_tot, 'c', linewidth=2)
+        ax2.plot(t, epsZ, 'r', linewidth=2)
+        ax2.plot(t, epsZ_hypo, 'g', linewidth=2)
+        ax2.plot(t, epsZ_tot, 'k', linewidth=2)
diff --git a/fluidsim/solvers/ns2d/output/spect_energy_budget.py b/fluidsim/solvers/ns2d/output/spect_energy_budget.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL291dHB1dC9zcGVjdF9lbmVyZ3lfYnVkZ2V0LnB5
--- /dev/null
+++ b/fluidsim/solvers/ns2d/output/spect_energy_budget.py
@@ -0,0 +1,143 @@
+import numpy as np
+import h5py
+
+
+from fluidsim.base.output.spect_energy_budget import (
+    SpectralEnergyBudgetBase, cumsum_inv)
+
+
+class SpectralEnergyBudgetNS2D(SpectralEnergyBudgetBase):
+    """Save and plot spectra."""
+
+    def compute(self):
+        """compute the spectral energy budget at one time."""
+        oper = self.sim.oper
+
+        ux = self.sim.state.state_phys['ux']
+        uy = self.sim.state.state_phys['uy']
+
+        rot_fft = self.sim.state.state_fft['rot_fft']
+        ux_fft, uy_fft = oper.vecfft_from_rotfft(rot_fft)
+
+        px_rot_fft, py_rot_fft = oper.gradfft_from_fft(rot_fft)
+        px_rot = oper.ifft2(px_rot_fft)
+        py_rot = oper.ifft2(py_rot_fft)
+
+        px_ux_fft, py_ux_fft = oper.gradfft_from_fft(ux_fft)
+        px_ux = oper.ifft2(px_ux_fft)
+        py_ux = oper.ifft2(py_ux_fft)
+
+        px_uy_fft, py_uy_fft = oper.gradfft_from_fft(uy_fft)
+        px_uy = oper.ifft2(px_uy_fft)
+        py_uy = oper.ifft2(py_uy_fft)
+
+        Frot = -ux*px_rot - uy*(py_rot + self.params.beta)
+        Frot_fft = oper.fft2(Frot)
+        oper.dealiasing(Frot_fft)
+
+        Fx = -ux*px_ux - uy*(py_ux)
+        Fx_fft = oper.fft2(Fx)
+        oper.dealiasing(Fx_fft)
+
+        Fy = -ux*px_uy - uy*(py_uy)
+        Fy_fft = oper.fft2(Fy)
+        oper.dealiasing(Fy_fft)
+
+        transferZ_fft = np.real(rot_fft.conj()*Frot_fft +
+                                rot_fft*Frot_fft.conj())/2.
+        # print ('sum(transferZ) = {0:9.4e} ; sum(abs(transferZ)) = {1:9.4e}'
+        #       ).format(self.sum_wavenumbers(transferZ_fft),
+        #                self.sum_wavenumbers(abs(transferZ_fft)))
+
+        transferE_fft = np.real(ux_fft.conj()*Fx_fft
+                                + ux_fft*Fx_fft.conj()
+                                + uy_fft.conj()*Fy_fft
+                                + uy_fft*Fy_fft.conj()
+                                )/2.
+        # print ('sum(transferE) = {0:9.4e} ; sum(abs(transferE)) = {1:9.4e}'
+        #       ).format(self.sum_wavenumbers(transferE_fft),
+        #                self.sum_wavenumbers(abs(transferE_fft)))
+
+        transfer2D_E = self.spectrum2D_from_fft(transferE_fft)
+        transfer2D_Z = self.spectrum2D_from_fft(transferZ_fft)
+
+        dico_results = {
+            'transfer2D_E': transfer2D_E,
+            'transfer2D_Z': transfer2D_Z
+        }
+        return dico_results
+
+    def _online_plot(self, dico_results):
+        transfer2D_E = dico_results['transfer2D_E']
+        transfer2D_Z = dico_results['transfer2D_Z']
+        khE = self.oper.khE
+        PiE = cumsum_inv(transfer2D_E)*self.oper.deltakh
+        PiZ = cumsum_inv(transfer2D_Z)*self.oper.deltakh
+        self.axe_a.plot(khE+khE[1], PiE, 'k')
+        self.axe_b.plot(khE+khE[1], PiZ, 'g')
+
+    def plot(self, tmin=0, tmax=1000, delta_t=2):
+
+        f = h5py.File(self.path_file, 'r')
+        dset_times = f['times']
+        dset_khE = f['khE']
+        khE = dset_khE[...]
+        khE = khE+khE[1]
+
+        dset_transferE = f['transfer2D_E']
+        dset_transferZ = f['transfer2D_Z']
+
+        # nb_spectra = dset_times.shape[0]
+        times = dset_times[...]
+        # nt = len(times)
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot = 1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        to_print = 'plot(tmin={0}, tmax={1}, delta_t={2:.2f})'.format(
+            tmin, tmax, delta_t)
+        print(to_print)
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+        print(
+'''plot spectral energy budget
+tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}
+imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}'''.format(
+    tmin_plot, tmax_plot, delta_t,
+    imin_plot, imax_plot, delta_i_plot))
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('spectra')
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('linear')
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot, delta_i_plot):
+
+                transferE = dset_transferE[it]
+                transferZ = dset_transferZ[it]
+
+                PiE = cumsum_inv(transferE)*self.oper.deltakh
+                PiZ = cumsum_inv(transferZ)*self.oper.deltakh
+
+                ax1.plot(khE, PiE, 'k', linewidth=1)
+                ax1.plot(khE, PiZ, 'g', linewidth=1)
+
+        transferE = dset_transferE[imin_plot:imax_plot].mean(0)
+        transferZ = dset_transferZ[imin_plot:imax_plot].mean(0)
+
+        PiE = cumsum_inv(transferE)*self.oper.deltakh
+        PiZ = cumsum_inv(transferZ)*self.oper.deltakh
+
+        ax1.plot(khE, PiE, 'r', linewidth=2)
+        ax1.plot(khE, PiZ, 'm', linewidth=2)
diff --git a/fluidsim/solvers/ns2d/output/spectra.py b/fluidsim/solvers/ns2d/output/spectra.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL291dHB1dC9zcGVjdHJhLnB5
--- /dev/null
+++ b/fluidsim/solvers/ns2d/output/spectra.py
@@ -0,0 +1,156 @@
+import h5py
+
+import numpy as np
+
+from fluidsim.base.output.spectra import Spectra
+
+
+class SpectraNS2D(Spectra):
+    """Save and plot spectra."""
+
+    def compute(self):
+        """compute the values at one time."""
+        energy_fft = self.output.compute_energy_fft()
+        # compute the spectra 1D
+        spectrum1Dkx_E, spectrum1Dky_E = self.spectra1D_from_fft(energy_fft)
+        dico_spectra1D = {'spectrum1Dkx_E': spectrum1Dkx_E,
+                          'spectrum1Dky_E': spectrum1Dky_E}
+        # compute the spectra 2D
+        spectrum2D_E = self.spectrum2D_from_fft(energy_fft)
+        dico_spectra2D = {'spectrum2D_E': spectrum2D_E}
+        return dico_spectra1D, dico_spectra2D
+
+    def _online_plot(self, dico_spectra1D, dico_spectra2D):
+        if (self.nx == self.params.oper.ny and
+                self.params.oper.Lx == self.params.oper.Ly):
+            spectrum2D = dico_spectra2D['spectrum2D_E']
+            khE = self.oper.khE
+            coef_norm = khE**(3.)
+            self.axe.loglog(khE, spectrum2D*coef_norm, 'k')
+            lin_inf, lin_sup = self.axe.get_ylim()
+            if lin_inf < 10e-6:
+                lin_inf = 10e-6
+            self.axe.set_ylim([lin_inf, lin_sup])
+        else:
+            print('you need to implement the ploting '
+                  'of the spectra for this case')
+
+    def plot1D(self, tmin=0, tmax=1000, delta_t=2,
+               coef_compensate=3):
+
+        f = h5py.File(self.path_file1D, 'r')
+        dset_times = f['times']
+
+        dset_kxE = f['kxE']
+        # dset_kyE = f['kyE']
+        kh = dset_kxE[...]
+
+        dset_spectrum1Dkx = f['spectrum1Dkx_E']
+        dset_spectrum1Dky = f['spectrum1Dky_E']
+        # nb_spectra = dset_times.shape[0]
+        times = dset_times[...]
+        # nt = len(times)
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        delta_t = delta_t_save*delta_i_plot
+        if delta_i_plot == 0:
+            delta_i_plot = 1
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        print(
+            'plot1D(tmin={0}, tmax={1}, delta_t={2:.2f},'.format(
+                tmin, tmax, delta_t) +
+            ' coef_compensate={0:.3f})'.format(coef_compensate))
+
+        print('''plot 1D spectra
+tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}
+imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}'''.format(
+    tmin_plot, tmax_plot, delta_t,
+    imin_plot, imax_plot, delta_i_plot))
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('spectra')
+        ax1.set_title('1D spectra, solver '+self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx))
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('log')
+
+        coef_norm = kh**(coef_compensate)
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+                EK = (dset_spectrum1Dkx[it]+dset_spectrum1Dky[it])
+                EK[EK < 10e-16] = 0.
+                ax1.plot(kh, EK*coef_norm, 'k', linewidth=2)
+
+        EK = (dset_spectrum1Dkx[imin_plot:imax_plot+1] +
+              dset_spectrum1Dky[imin_plot:imax_plot+1]).mean(0)
+
+        ax1.plot(kh, kh**(-3)*coef_norm, 'k', linewidth=1)
+        ax1.plot(kh, 0.01*kh**(-5/3)*coef_norm, 'k--', linewidth=1)
+
+    def plot2D(self, tmin=0, tmax=1000, delta_t=2,
+               coef_compensate=3):
+        f = h5py.File(self.path_file2D, 'r')
+        dset_times = f['times']
+        # nb_spectra = dset_times.shape[0]
+        times = dset_times[...]
+        # nt = len(times)
+
+        kh = f['khE'][...]
+
+        dset_spectrum = f['spectrum2D_E']
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot = 1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        print(
+            'plot2D(tmin={0}, tmax={1}, delta_t={2:.2f},'.format(
+                tmin, tmax, delta_t) +
+            ' coef_compensate={0:.3f})'.format(coef_compensate))
+
+        print('''plot 2D spectra
+tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}
+imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}'''.format(
+tmin_plot, tmax_plot, delta_t,
+imin_plot, imax_plot, delta_i_plot))
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('2D spectra')
+        ax1.set_title('2D spectra, solver ' + self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx))
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('log')
+
+        coef_norm = kh**coef_compensate
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+                EK = dset_spectrum[it]
+                EK[EK < 10e-16] = 0.
+                ax1.plot(kh, EK*coef_norm, 'k', linewidth=1)
+
+        EK = dset_spectrum[imin_plot:imax_plot+1].mean(0)
+        EK[EK < 10e-16] = 0.
+        ax1.plot(kh, EK*coef_norm, 'k', linewidth=2)
+
+        ax1.plot(kh, kh**(-3)*coef_norm, 'k--', linewidth=1)
+        ax1.plot(kh, 0.01*kh**(-5./3)*coef_norm, 'k-.', linewidth=1)
diff --git a/fluidsim/solvers/ns2d/solver.py b/fluidsim/solvers/ns2d/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/ns2d/solver.py
@@ -0,0 +1,159 @@
+"""NS2D solver (:mod:`fluidsim.solvers.ns2d.solver`)
+=========================================================
+
+.. autoclass:: Simul
+   :members:
+   :private-members:
+
+"""
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.base.solvers.pseudo_spect import (
+    SimulBasePseudoSpectral, InfoSolverPseudoSpectral)
+
+
+info_solver = InfoSolverPseudoSpectral()
+
+package = 'fluidsim.solvers.ns2d'
+info_solver.module_name = package + '.solver'
+info_solver.class_name = 'Simul'
+info_solver.short_name = 'NS2D'
+
+classes = info_solver.classes
+
+classes.State.module_name = package + '.state'
+classes.State.class_name = 'StateNS2D'
+
+classes.InitFields.module_name = package + '.init_fields'
+classes.InitFields.class_name = 'InitFieldsNS2D'
+
+classes.Output.module_name = package + '.output'
+classes.Output.class_name = 'Output'
+
+classes.Forcing.module_name = package + '.forcing'
+classes.Forcing.class_name = 'ForcingNS2D'
+
+
+info_solver.complete_with_classes()
+
+
+class Simul(SimulBasePseudoSpectral):
+    """Pseudo-spectral solver 2D incompressible Navier-Stokes equations.
+
+    """
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        SimulBasePseudoSpectral._complete_params_with_default(params)
+        attribs = {'beta': 0.}
+        params.set_attribs(attribs)
+
+    def __init__(self, params):
+        # the common initialization with the NS2D info_solver:
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_fft=None):
+        oper = self.oper
+        fft2 = oper.fft2
+        ifft2 = oper.ifft2
+
+        if state_fft is None:
+            rot_fft = self.state.state_fft['rot_fft']
+            ux = self.state.state_phys['ux']
+            uy = self.state.state_phys['uy']
+        else:
+            rot_fft = state_fft['rot_fft']
+            ux_fft, uy_fft = oper.vecfft_from_rotfft(rot_fft)
+            ux = ifft2(ux_fft)
+            uy = ifft2(uy_fft)
+
+        px_rot_fft, py_rot_fft = oper.gradfft_from_fft(rot_fft)
+        px_rot = ifft2(px_rot_fft)
+        py_rot = ifft2(py_rot_fft)
+
+        if self.params.beta == 0:
+            Frot = -ux*px_rot - uy*py_rot
+        else:
+            Frot = -ux*px_rot - uy*(py_rot + self.params.beta)
+
+        Frot_fft = fft2(Frot)
+        oper.dealiasing(Frot_fft)
+
+        # T_rot = np.real(Frot_fft.conj()*rot_fft
+        #                + Frot_fft*rot_fft.conj())/2.
+        # print ('sum(T_rot) = {0:9.4e} ; sum(abs(T_rot)) = {1:9.4e}'
+        #       ).format(self.oper.sum_wavenumbers(T_rot),
+        #                self.oper.sum_wavenumbers(abs(T_rot)))
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+
+        tendencies_fft['rot_fft'] = Frot_fft
+
+        if self.params.FORCING:
+            tendencies_fft += self.forcing.get_forcing()
+
+        return tendencies_fft
+
+
+if __name__ == "__main__":
+
+    import numpy as np
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 32
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    # params.oper.type_fft = 'FFTWPY'
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 1.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.FORCING = True
+    params.forcing.type = 'Random'
+    # 'Proportional'
+    # params.forcing.type_normalize
+
+    # params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 0.5
+    params.output.periods_save.spectra = 0.5
+    params.output.periods_save.spatial_means = 0.05
+    params.output.periods_save.spect_energy_budg = 0.5
+    params.output.periods_save.increments = 0.5
+
+    params.output.periods_plot.phys_fields = 0.0
+
+    params.output.ONLINE_PLOT_OK = True
+
+    # params.output.spectra.HAS_TO_PLOT_SAVED = True
+    # params.output.spatial_means.HAS_TO_PLOT_SAVED = True
+    # params.output.spect_energy_budg.HAS_TO_PLOT_SAVED = True
+    # params.output.increments.HAS_TO_PLOT_SAVED = True
+
+    params.output.phys_fields.field_to_plot = 'rot'
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    # sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/ns2d/state.py b/fluidsim/solvers/ns2d/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL3N0YXRlLnB5
--- /dev/null
+++ b/fluidsim/solvers/ns2d/state.py
@@ -0,0 +1,83 @@
+"""State for the NS2D solver (:mod:`fluidsim.solvers.ns2d.state`)
+=======================================================================
+"""
+
+
+from fluidsim.base.state import StatePseudoSpectral
+
+from fluiddyn.util import mpi
+
+
+class StateNS2D(StatePseudoSpectral):
+    """Contains the variables corresponding to the state and handles the
+    access to other fields for the solver NS2D.
+
+    """
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.State.set_attribs({
+            'keys_state_fft': ['rot_fft'],
+            'keys_state_phys': ['ux', 'uy', 'rot'],
+            'keys_computable': [],
+            'keys_phys_needed': ['rot'],
+            'keys_linear_eigenmodes': ['rot_fft']})
+
+    def compute(self, key, SAVE_IN_DICT=True, RAISE_ERROR=True):
+        it = self.sim.time_stepping.it
+        if (key in self.vars_computed
+                and it == self.it_computed[key]):
+            return self.vars_computed[key]
+
+        if key == 'ux_fft':
+            result = self.oper.fft2(self.state_phys['ux'])
+        elif key == 'uy_fft':
+            result = self.oper.fft2(self.state_phys['uy'])
+        elif key == 'rot_fft':
+            ux_fft = self.compute('ux_fft')
+            uy_fft = self.compute('uy_fft')
+            result = self.oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        elif key == 'div_fft':
+            ux_fft = self.compute('ux_fft')
+            uy_fft = self.compute('uy_fft')
+            result = self.oper.divfft_from_vecfft(ux_fft, uy_fft)
+        elif key == 'rot':
+            rot_fft = self.compute('rot_fft')
+            result = self.oper.ifft2(rot_fft)
+        elif key == 'div':
+            div_fft = self.compute('div_fft')
+            result = self.oper.ifft2(div_fft)
+        elif key == 'q':
+            rot = self.compute('rot')
+            result = rot
+        else:
+            to_print = 'Do not know how to compute "' + key + '".'
+            if RAISE_ERROR:
+                raise ValueError(to_print)
+            else:
+                if mpi.rank == 0:
+                    print(to_print +
+                          '\nreturn an array of zeros.')
+
+                result = self.oper.constant_arrayX(value=0.)
+
+        if SAVE_IN_DICT:
+            self.vars_computed[key] = result
+            self.it_computed[key] = it
+
+        return result
+
+    def statephys_from_statefft(self):
+        rot_fft = self.state_fft['rot_fft']
+        self.state_phys['rot'] = self.oper.ifft2(rot_fft)
+        ux_fft, uy_fft = self.oper.vecfft_from_rotfft(rot_fft)
+        self.state_phys['ux'] = self.oper.ifft2(ux_fft)
+        self.state_phys['uy'] = self.oper.ifft2(uy_fft)
+
+    def statefft_from_statephys(self):
+        rot = self.state_phys['rot']
+        self.state_fft['rot_fft'] = self.oper.fft2(rot)
diff --git a/fluidsim/solvers/ns2d/strat/__init__.py b/fluidsim/solvers/ns2d/strat/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL3N0cmF0L19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/solvers/ns2d/strat/__init__.py
@@ -0,0 +1,14 @@
+"""Stratified Navier-Stokes 2D (:mod:`fluidsim.solvers.ns2d.strat`)
+=========================================================================
+
+.. currentmodule:: fluidsim.solvers.ns2d.strat
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   solver
+   exact
+
+"""
diff --git a/fluidsim/solvers/ns2d/strat/exact/__init__.py b/fluidsim/solvers/ns2d/strat/exact/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL3N0cmF0L2V4YWN0L19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/solvers/ns2d/strat/exact/__init__.py
@@ -0,0 +1,13 @@
+"""Stratified Navier-Stokes 2D (:mod:`fluidsim.solvers.ns2d.strat.exact`)
+===============================================================================
+
+.. currentmodule:: fluidsim.solvers.ns2d.strat.exact
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   solver
+
+"""
diff --git a/fluidsim/solvers/ns2d/strat/exact/solver.py b/fluidsim/solvers/ns2d/strat/exact/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL3N0cmF0L2V4YWN0L3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/ns2d/strat/exact/solver.py
@@ -0,0 +1,165 @@
+"""Stratified NS2D solver (:mod:`fluidsim.solvers.ns2d.strat.solver`)
+===========================================================================
+
+.. autoclass:: Simul
+   :members:
+   :private-members:
+
+"""
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.base.solvers.pseudo_spect import (
+    SimulBasePseudoSpectral, InfoSolverPseudoSpectral)
+
+
+info_solver = InfoSolverPseudoSpectral()
+
+package = 'fluidsim.solvers.ns2d'
+info_solver.module_name = package + '.solver'
+info_solver.class_name = 'Simul'
+info_solver.short_name = 'NS2D'
+
+classes = info_solver.classes
+
+classes.State.module_name = package + '.state'
+classes.State.class_name = 'StateNS2D'
+
+classes.InitFields.module_name = package + '.init_fields'
+classes.InitFields.class_name = 'InitFieldsNS2D'
+
+classes.Output.module_name = package + '.output'
+classes.Output.class_name = 'Output'
+
+# classes.Forcing.module_name = package + '.forcing'
+# classes.Forcing.class_name = 'ForcingNS2D'
+
+
+info_solver.complete_with_classes()
+
+
+class Simul(SimulBasePseudoSpectral):
+    r"""Pseudo-spectral solver strat. 2D incompressible Navier-Stokes equations.
+
+    
+
+
+    """
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        SimulBasePseudoSpectral._complete_params_with_default(params)
+        attribs = {'N': 1.}
+        params.set_attribs(attribs)
+
+    def __init__(self, params):
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_fft=None):
+        oper = self.oper
+        fft2 = oper.fft2
+        ifft2 = oper.ifft2
+
+        if state_fft is None:
+            rot_fft = self.state.state_fft['rot_fft']
+            b_fft = self.state.state_fft['b_fft']
+            ux = self.state.state_phys['ux']
+            uz = self.state.state_phys['uz']
+        else:
+            rot_fft = state_fft['rot_fft']
+            b_fft = state_fft['b_fft']
+            ux_fft, uz_fft = oper.vecfft_from_rotfft(rot_fft)
+            ux = ifft2(ux_fft)
+            uz = ifft2(uz_fft)
+
+        px_rot_fft, pz_rot_fft = oper.gradfft_from_fft(rot_fft)
+        px_rot = ifft2(px_rot_fft)
+        pz_rot = ifft2(pz_rot_fft)
+
+        px_b_fft = oper.pxffft_ftt(b_fft)
+
+        Frot = - ux*px_rot - uz*pz_rot
+
+        Frot_fft = fft2(Frot) + px_b_fft
+        oper.dealiasing(Frot_fft)
+
+        Fb_fft = self.params.N2*uz_fft
+
+        # T_rot = np.real(Frot_fft.conj()*rot_fft
+        #                + Frot_fft*rot_fft.conj())/2.
+        # print ('sum(T_rot) = {0:9.4e} ; sum(abs(T_rot)) = {1:9.4e}'
+        #       ).format(self.oper.sum_wavenumbers(T_rot),
+        #                self.oper.sum_wavenumbers(abs(T_rot)))
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+
+        tendencies_fft['rot_fft'] = Frot_fft
+        tendencies_fft['b_fft'] = Fb_fft
+
+        if self.params.FORCING:
+            tendencies_fft += self.forcing.get_forcing()
+
+        return tendencies_fft
+
+
+if __name__ == "__main__":
+
+    import numpy as np
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 32
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    # params.oper.type_fft = 'FFTWPY'
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 1.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.FORCING = True
+    params.forcing.type = 'Random'
+    # 'Proportional'
+    # params.forcing.type_normalize
+
+    # params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 0.5
+    params.output.periods_save.spectra = 0.5
+    params.output.periods_save.spatial_means = 0.05
+    params.output.periods_save.spect_energy_budg = 0.5
+    params.output.periods_save.increments = 0.5
+
+    params.output.periods_plot.phys_fields = 0.0
+
+    params.output.ONLINE_PLOT_OK = True
+
+    # params.output.spectra.HAS_TO_PLOT_SAVED = True
+    # params.output.spatial_means.HAS_TO_PLOT_SAVED = True
+    # params.output.spect_energy_budg.HAS_TO_PLOT_SAVED = True
+    # params.output.increments.HAS_TO_PLOT_SAVED = True
+
+    params.output.phys_fields.field_to_plot = 'rot'
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    # sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/ns2d/strat/solver.py b/fluidsim/solvers/ns2d/strat/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL3N0cmF0L3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/ns2d/strat/solver.py
@@ -0,0 +1,159 @@
+"""NS2D solver (:mod:`fluidsim.solvers.ns2d.solver`)
+=========================================================
+
+.. autoclass:: Simul
+   :members:
+   :private-members:
+
+"""
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.base.solvers.pseudo_spect import (
+    SimulBasePseudoSpectral, InfoSolverPseudoSpectral)
+
+
+info_solver = InfoSolverPseudoSpectral()
+
+package = 'fluidsim.solvers.ns2d'
+info_solver.module_name = package + '.solver'
+info_solver.class_name = 'Simul'
+info_solver.short_name = 'NS2D'
+
+classes = info_solver.classes
+
+classes.State.module_name = package + '.state'
+classes.State.class_name = 'StateNS2D'
+
+classes.InitFields.module_name = package + '.init_fields'
+classes.InitFields.class_name = 'InitFieldsNS2D'
+
+classes.Output.module_name = package + '.output'
+classes.Output.class_name = 'Output'
+
+classes.Forcing.module_name = package + '.forcing'
+classes.Forcing.class_name = 'ForcingNS2D'
+
+
+info_solver.complete_with_classes()
+
+
+class Simul(SimulBasePseudoSpectral):
+    """Pseudo-spectral solver 2D incompressible Navier-Stokes equations.
+
+    """
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        SimulBasePseudoSpectral._complete_params_with_default(params)
+        attribs = {'beta': 0.}
+        params.set_attribs(attribs)
+
+    def __init__(self, params):
+        # the common initialization with the NS2D info_solver:
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_fft=None):
+        oper = self.oper
+        fft2 = oper.fft2
+        ifft2 = oper.ifft2
+
+        if state_fft is None:
+            rot_fft = self.state.state_fft['rot_fft']
+            ux = self.state.state_phys['ux']
+            uy = self.state.state_phys['uy']
+        else:
+            rot_fft = state_fft['rot_fft']
+            ux_fft, uy_fft = oper.vecfft_from_rotfft(rot_fft)
+            ux = ifft2(ux_fft)
+            uy = ifft2(uy_fft)
+
+        px_rot_fft, py_rot_fft = oper.gradfft_from_fft(rot_fft)
+        px_rot = ifft2(px_rot_fft)
+        py_rot = ifft2(py_rot_fft)
+
+        if self.params.beta == 0:
+            Frot = -ux*px_rot - uy*py_rot
+        else:
+            Frot = -ux*px_rot - uy*(py_rot + self.params.beta)
+
+        Frot_fft = fft2(Frot)
+        oper.dealiasing(Frot_fft)
+
+        # T_rot = np.real(Frot_fft.conj()*rot_fft
+        #                + Frot_fft*rot_fft.conj())/2.
+        # print ('sum(T_rot) = {0:9.4e} ; sum(abs(T_rot)) = {1:9.4e}'
+        #       ).format(self.oper.sum_wavenumbers(T_rot),
+        #                self.oper.sum_wavenumbers(abs(T_rot)))
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+
+        tendencies_fft['rot_fft'] = Frot_fft
+
+        if self.params.FORCING:
+            tendencies_fft += self.forcing.get_forcing()
+
+        return tendencies_fft
+
+
+if __name__ == "__main__":
+
+    import numpy as np
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 32
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    # params.oper.type_fft = 'FFTWPY'
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 1.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.FORCING = True
+    params.forcing.type = 'Random'
+    # 'Proportional'
+    # params.forcing.type_normalize
+
+    # params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 0.5
+    params.output.periods_save.spectra = 0.5
+    params.output.periods_save.spatial_means = 0.05
+    params.output.periods_save.spect_energy_budg = 0.5
+    params.output.periods_save.increments = 0.5
+
+    params.output.periods_plot.phys_fields = 0.0
+
+    params.output.ONLINE_PLOT_OK = True
+
+    # params.output.spectra.HAS_TO_PLOT_SAVED = True
+    # params.output.spatial_means.HAS_TO_PLOT_SAVED = True
+    # params.output.spect_energy_budg.HAS_TO_PLOT_SAVED = True
+    # params.output.increments.HAS_TO_PLOT_SAVED = True
+
+    params.output.phys_fields.field_to_plot = 'rot'
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    # sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/ns2d/test/__init__.py b/fluidsim/solvers/ns2d/test/__init__.py
new file mode 100644
diff --git a/fluidsim/solvers/ns2d/test/test_solver.py b/fluidsim/solvers/ns2d/test/test_solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9uczJkL3Rlc3QvdGVzdF9zb2x2ZXIucHk=
--- /dev/null
+++ b/fluidsim/solvers/ns2d/test/test_solver.py
@@ -0,0 +1,64 @@
+import unittest
+import shutil
+
+import numpy as np
+
+import fluiddyn as fld
+
+from fluiddyn.io import stdout_redirected
+
+
+class TestSolverNS2D(unittest.TestCase):
+    def test_tendency(self):
+
+        key_solver = 'NS2D'
+        solver = fld.simul.import_module_solver_from_key(key_solver)
+        params = fld.simul.create_params(solver)
+
+        params.short_name_type_run = 'test'
+
+        nh = 64
+        params.oper.nx = nh
+        params.oper.ny = nh
+        Lh = 6.
+        params.oper.Lx = Lh
+        params.oper.Ly = Lh
+
+        params.oper.coef_dealiasing = 2./3
+        params.nu_8 = 2.
+
+        params.oper.type_fft = 'FFTWPY'
+        # params.oper.type_fft = 'FFTWCY'
+
+        params.time_stepping.t_end = 0.5
+
+        params.init_fields.type_flow_init = 'NOISE'
+        params.output.HAS_TO_SAVE = False
+        params.FORCING = False
+
+        with stdout_redirected():
+            sim = solver.Simul(params)
+
+        rot_fft = sim.state('rot_fft')
+
+        tend = sim.tendencies_nonlin(state_fft=sim.state.state_fft)
+        Frot_fft = tend['rot_fft']
+
+        T_rot = np.real(Frot_fft.conj()*rot_fft)
+
+        ratio = (sim.oper.sum_wavenumbers(T_rot) /
+                 sim.oper.sum_wavenumbers(abs(T_rot)))
+
+        self.assertGreater(1e-16, ratio)
+
+        # print ('sum(T_rot) = {0:9.4e} ; '
+        #        'sum(abs(T_rot)) = {1:9.4e}').format(
+        #            sim.oper.sum_wavenumbers(T_rot),
+        #            sim.oper.sum_wavenumbers(abs(T_rot)))
+
+        # clean by removing the directory
+        shutil.rmtree(sim.output.path_run)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fluidsim/solvers/plate2d/__init__.py b/fluidsim/solvers/plate2d/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/solvers/plate2d/__init__.py
@@ -0,0 +1,18 @@
+"""Plate2d solvers (:mod:`fluidsim.solvers.plate2d`)
+==========================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   solver
+   state
+   output
+   init_fields
+   forcing
+   diag
+
+"""
diff --git a/fluidsim/solvers/plate2d/diag/__init__.py b/fluidsim/solvers/plate2d/diag/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL2RpYWcvX19pbml0X18ucHk=
--- /dev/null
+++ b/fluidsim/solvers/plate2d/diag/__init__.py
@@ -0,0 +1,13 @@
+"""Plate2d solvers diagonalized (:mod:`fluidsim.solvers.plate2d.diag`)
+============================================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.diag
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   solver
+
+"""
diff --git a/fluidsim/solvers/plate2d/diag/solver.py b/fluidsim/solvers/plate2d/diag/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL2RpYWcvc29sdmVyLnB5
--- /dev/null
+++ b/fluidsim/solvers/plate2d/diag/solver.py
@@ -0,0 +1,326 @@
+# -*- coding: utf-8 -*-
+
+"""Plate2d solver diag. (:mod:`fluidsim.solvers.plate2d.diag.solver`)
+===========================================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.diag.solver
+
+Provides:
+
+.. autoclass:: Simul
+   :members:
+   :private-members:
+
+"""
+
+from __future__ import print_function
+
+import numpy as np
+
+from fluidsim.operators.setofvariables import SetOfVariables
+from fluidsim.base.solvers.pseudo_spect import (
+    SimulBasePseudoSpectral, InfoSolverPseudoSpectral)
+
+
+info_solver = InfoSolverPseudoSpectral()
+
+package = 'fluidsim.solvers.plate2d'
+info_solver.module_name = package + '.solver'
+info_solver.class_name = 'Simul'
+info_solver.short_name = 'Plate2D'
+
+classes = info_solver.classes
+
+classes.State.module_name = package + '.state'
+classes.State.class_name = 'StatePlate2D'
+
+classes.InitFields.module_name = package + '.init_fields'
+classes.InitFields.class_name = 'InitFieldsPlate2D'
+
+classes.Output.module_name = package + '.output'
+classes.Output.class_name = 'Output'
+
+classes.Forcing.module_name = package + '.forcing'
+classes.Forcing.class_name = 'ForcingPlate2D'
+
+
+info_solver.complete_with_classes()
+
+
+class Simul(SimulBasePseudoSpectral):
+    r"""Pseudo-spectral solver solving the Föppl-von Kármán equations.
+
+    Notes
+    -----
+
+    .. |p| mathmacro:: \partial
+
+    This class is dedicated to solve with a pseudo-spectral method the
+    Föppl-von Kármán equations which describe the dynamics of a rigid
+    plate (see :class:`fluidsim.solvers.plate2d.solver.Simul`).
+
+    In the Fourier space, the governing equations write:
+
+    .. math::
+       \p_t \hat z = \hat w,
+
+       \p_t \hat w = - \Omega(k)^2 \hat z + \widehat{N_w(z)} + \hat F_w
+       - \gamma_w \hat w,
+
+    where :math:`\Omega(k) = k^4`, :math:`k^2 = |\mathbf{k}|^2` and
+    :math:`\gamma_w = \nu_\alpha k^{2\alpha}`. For this solver, we
+    will use variables that diagonalized the linear terms, i.e. that
+    represent propagative waves (see
+    :class:`fluidsim.solvers.waves2d.solver.Simul`). Therefore,
+    all the linear terms can be solved exactly. Applying the equations
+    of :class:`fluidsim.solvers.waves2d.solver.Simul` with
+    :math:`\gamma_f = \gamma_z = 0`, we find that the eigenvalues are
+    :math:`\sigma_\pm = - \gamma_w/2 \pm i \tilde \Omega` with
+    :math:`\tilde \Omega = \Omega \sqrt{1 - (\gamma_w/(2 \Omega))^2}`,
+    and the (not normalized) eigenvectors are
+
+    .. math::
+
+       V_\pm = \begin{pmatrix} 1 \\ \sigma_\pm \end{pmatrix}.
+
+    The state can be represented by a vector :math:`A` that verifies
+    :math:`X = V A`, where :math:`V` is the base matrix
+
+    .. math::
+
+       V = \begin{pmatrix} 1 & 1 \\
+       \sigma_+ & \sigma_- \end{pmatrix}.
+
+    The inverse base matrix is given by
+
+    .. math::
+
+       V^{-1} = \frac{i}{2\tilde \Omega}
+       \begin{pmatrix}
+       \sigma_- & -1 \\
+       -\sigma_+ &  1 \end{pmatrix},
+
+    which gives more explicitly that
+
+    .. math::
+
+       A = \begin{pmatrix} \hat a_+ \\ \hat a_- \end{pmatrix} =
+       \frac{i}{2\tilde \Omega} \begin{pmatrix} \sigma_- \hat z - \hat
+       w \\ -\sigma_+ \hat z + \hat w \end{pmatrix}.
+
+    The governing equations can then be expressed as
+
+    .. math::
+
+       \p_t A = L A + N(A),
+
+    with
+
+
+    .. math::
+
+       L = \begin{pmatrix} \sigma_+ & 0 \\ 0 & \sigma_- \end{pmatrix},
+
+       N = \frac{i}{2\tilde \Omega} \begin{pmatrix} -(\widehat{N_w(z)}
+       + \hat F_w) \\ \widehat{N_w(z)} + \hat F_w \end{pmatrix}.
+
+    """
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        SimulBasePseudoSpectral._complete_params_with_default(params)
+        attribs = {'beta': 0.}
+        params.set_attribs(attribs)
+
+    def __init__(self, params):
+        # the common initialization with the PLATE2D info_solver:
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_fft=None):
+        """Compute the "nonlinear" tendencies."""
+        oper = self.oper
+
+        if state_fft is None:
+            w_fft = self.state.state_fft['w_fft']
+            z_fft = self.state.state_fft['z_fft']
+        else:
+            w_fft = state_fft['w_fft']
+            z_fft = state_fft['z_fft']
+
+        mamp_zz = oper.monge_ampere_from_fft(z_fft, z_fft)
+        chi_fft = - oper.invlaplacian2_fft(oper.fft2(mamp_zz))
+        mamp_zchi = oper.monge_ampere_from_fft(z_fft, chi_fft)
+        Nw_fft = oper.fft2(mamp_zchi)
+
+        if self.params.FORCING:
+            forcing_fft = self.forcing.get_forcing()
+            forcing_w_fft = forcing_fft...
+            Nw_fft += forcing_w_fft
+
+        oper.dealiasing(Nw_fft)
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+
+        tendencies_fft['ap_fft'] = -Nw_fft
+        tendencies_fft['am_fft'] = Nw_fft
+
+        tendencies_fft /= -2j*self._tilde_Omega
+
+        # ratio = self.test_tendencies_nonlin(
+        #     tendencies_fft, w_fft, z_fft, chi_fft)
+        # print('ratio:', ratio)
+
+
+
+        return tendencies_fft
+
+    def compute_freq_diss(self):
+        """Compute the dissipation frequencies with dissipation only for w."""
+        f_d_w, f_d_hypo_w = super(Simul, self).compute_freq_diss()
+        f_d = np.zeros_like(self.state.state_fft.data, dtype=np.float64)
+        f_d_hypo = np.zeros_like(self.state.state_fft.data,
+                                 dtype=np.float64)
+        f_d[0] = f_d_w
+        f_d_hypo[0] = f_d_hypo_w
+        return f_d, f_d_hypo
+
+    # def test_tendencies_nonlin(
+    #         self, tendencies_fft=None,
+    #         w_fft=None, z_fft=None, chi_fft=None):
+    #     r"""Test if the tendencies conserves the total energy.
+
+    #     We consider the conservative Föppl-von Kármán equations
+    #     (without dissipation and forcing) written as
+
+    #     .. math::
+
+    #        \p_t z = F_z
+
+    #        \p_t w = F_w
+
+    #     We have:
+
+    #     .. math::
+
+    #        \p_t E_K(\mathbf{k}) = \mathcal{R} ( \hat F_w \hat w ^* )
+
+    #        \p_t E_L(\mathbf{k}) = k^4 \mathcal{R} ( \hat F_z \hat z ^* )
+
+    #        \p_t E_{NQ}(\mathbf{k}) =
+    #        - \mathcal{R} ( \widehat{\{ F_z, z\}} \hat \chi ^* )
+
+    #     Since the total energy is conserved, we should have
+
+    #     .. math::
+
+    #        \sum_{\mathbf{k}} \p_t E_K(\mathbf{k}) + \p_t E_L(\mathbf{k})
+    #        + \p_t E_{NQ}(\mathbf{k}) = 0
+
+    #     This function computes this quantities.
+
+    #     """
+
+    #     if tendencies_fft is None:
+    #         tendencies_fft = self.tendencies_nonlin()
+    #         w_fft = self.state.state_fft['w_fft']
+    #         z_fft = self.state.state_fft['z_fft']
+    #         chi_fft = self.state.compute('chi_fft')
+
+    #     F_w_fft = tendencies_fft['w_fft']
+    #     F_z_fft = tendencies_fft['z_fft']
+
+    #     K4 = self.oper.K4
+
+    #     dt_E_K = np.real(F_w_fft * w_fft.conj())
+    #     dt_E_L = K4 * np.real(F_z_fft * z_fft.conj())
+
+    #     tmp = self.oper.monge_ampere_from_fft(F_z_fft, z_fft)
+    #     tmp_fft = self.oper.fft2(tmp)
+
+    #     dt_E_NQ = - np.real(tmp_fft * chi_fft.conj())
+
+    #     T = dt_E_K + dt_E_L + dt_E_NQ
+
+    #     norm = self.oper.sum_wavenumbers(abs(T))
+
+    #     if norm < 1e-15:
+    #         print('Only zeros in total energy tendency.')
+    #         # print('(K+L)\n', dt_E_K+dt_E_L)
+    #         # print('NQ\n', dt_E_NQ)
+    #         return 0
+    #     else:
+    #         T = T/norm
+    #         # print('ratio array\n', T)
+    #         # print('(K+L)\n', (dt_E_K+dt_E_L)/norm)
+    #         # print('NQ\n', dt_E_NQ/norm)
+    #         return self.oper.sum_wavenumbers(T)
+
+
+if __name__ == "__main__":
+
+    np.set_printoptions(precision=2)
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 192/2
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+    # params.oper.type_fft = 'FFTWPY'
+    params.oper.coef_dealiasing = 2./3
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-4*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    kmax = np.sqrt(2)*np.pi/delta_x
+
+    params.time_stepping.USE_CFL = False
+    params.time_stepping.deltat0 = 2*np.pi/kmax**2
+    params.time_stepping.USE_T_END = True
+    params.time_stepping.t_end = 50.0
+    params.time_stepping.it_end = 1
+
+    # params.init_fields.type_flow_init = 'HARMONIC'
+    params.init_fields.type_flow_init = 'NOISE'
+    params.init_fields.max_velo_noise = 0.001
+    # params.init_fields.path_file = (
+    #     '/home/users/bonamy2c/Sim_data/PLATE2D_test_L='
+    #     '2pix2pi_256x256_2015-03-04_22-36-37/state_phys_t=000.100.hd5')
+
+    params.FORCING = True
+    params.forcing.forcing_rate = 100.
+    # params.forcing.nkmax_forcing = 5
+    # params.forcing.nkmin_forcing = 4
+
+    params.output.periods_print.print_stdout = 0.5
+
+    params.output.periods_save.phys_fields = 0.0
+    params.output.periods_save.spectra = 0.5
+    # params.output.periods_save.spect_energy_budg = 0.5
+    # params.output.periods_save.increments = 0.5
+
+    params.output.ONLINE_PLOT_OK = False
+    params.output.period_show_plot = 0.5
+    params.output.periods_plot.phys_fields = 0.0
+
+    params.output.phys_fields.field_to_plot = 'z'
+
+    params.output.spectra.HAS_TO_PLOT_SAVED = True
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/plate2d/forcing.py b/fluidsim/solvers/plate2d/forcing.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL2ZvcmNpbmcucHk=
--- /dev/null
+++ b/fluidsim/solvers/plate2d/forcing.py
@@ -0,0 +1,93 @@
+"""
+Plate2d forcing (:mod:`fluidsim.solvers.plate2d.forcing`)
+===============================================================
+
+"""
+
+# import numpy as np
+
+# from fluiddyn.util import mpi
+
+from fluidsim.base.forcing import ForcingBasePseudoSpectral
+
+from fluidsim.base.forcing.specific import \
+    Proportional as ProportionalBase
+
+from fluidsim.base.forcing.specific import \
+    TimeCorrelatedRandomPseudoSpectral as TCRandomPS
+
+
+class ForcingPlate2D(ForcingBasePseudoSpectral):
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        ForcingBasePseudoSpectral._complete_info_solver(info_solver)
+        classes = info_solver.classes.Forcing.classes
+
+        package = 'fluidsim.solvers.plate2d.forcing'
+
+        classes.set_child(
+            'Random',
+            attribs={'module_name': package,
+                     'class_name': 'Random'})
+
+        classes.set_child(
+            'Proportional',
+            attribs={'module_name': package,
+                     'class_name': 'Proportional'})
+
+
+class Random(TCRandomPS):
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        TCRandomPS._complete_params_with_default(params)
+        params.forcing.key_forced = 'w_fft'
+
+
+class Proportional(ProportionalBase):
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        params.forcing.key_forced = 'w_fft'
+
+
+class TimeCorrelatedRandomPseudoSpectralGauss(TCRandomPS):
+    def compute_forcingc_raw(self):
+        Fw_fft = super(TCRandomPS, self).compute_forcingc_raw()
+
+        return Fw_fft
+
+    # def compute_forcing_2nd_degree_eq(self):
+    #     """compute a forcing normalize with a 2nd degree eq."""
+
+    #     w_fft = self.sim.state.state_fft['w_fft']
+    #     vmax = self.sim.params.forcing.vmax
+    #     n0 = w_fft.shape[0]
+    #     n1 = w_fft.shape[1]
+    #     zf = np.zeros((n0, n1), dtype=np.complex128)
+    #     kfSI = 2*np.pi*2.5
+    #     sk = 1.
+    #     kfor = self.sim.oper.Lx*kfSI/(2*np.pi)
+    #     rand = np.random.normal(loc=0.0, scale=1.0, size=(2*n0/3, 2*n1/3))
+    #     for i0 in xrange(2*n0/3):
+    #         for i1 in xrange(2*n1/3):
+    #             zf[i0, i1] = ((1/(2*sk**2)) *
+    #                           (np.exp(-np.sqrt(i0**2+i1**2)-kfor-1)**2) *
+    #                           np.exp(2*np.pi*1j*rand[i0, i1]))
+
+    #             # zIJ(1:(n3+1),1:(n3+1)) =
+    #             # exp(-(sqrt(I.^2+J.^2)-kfor-1).^2/(2*sk^2))
+    #             # .*exp(2*pi*1i*rand(size(I)));
+    #     forcingW_fft = self.oper.nx_loc**2 * vmax / np.sum(zf) * zf
+    #     self.forcingc_fft['w_fft'] = forcingW_fft
+
+    #     self.put_forcingc_in_forcing()
+
+
diff --git a/fluidsim/solvers/plate2d/init_fields.py b/fluidsim/solvers/plate2d/init_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL2luaXRfZmllbGRzLnB5
--- /dev/null
+++ b/fluidsim/solvers/plate2d/init_fields.py
@@ -0,0 +1,102 @@
+"""
+Plate2d InitFields (:mod:`fluidsim.solvers.plate2d.init_fields`)
+======================================================================
+
+
+"""
+
+import numpy as np
+
+from fluiddyn.util import mpi
+from fluidsim.base.init_fields import InitFieldsBase
+
+
+class InitFieldsPlate2D(InitFieldsBase):
+    """Init the fields for the solver PLATE2D."""
+
+    implemented_flows = ['NOISE', 'CONSTANT', 'LOAD_FILE', 'HARMONIC']
+
+    def __call__(self):
+        """Init the state (in physical and Fourier space) and time"""
+        sim = self.sim
+
+        type_flow_init = self.get_and_check_type_flow_init()
+
+        if type_flow_init == 'HARMONIC':
+            w_fft, z_fft = self.init_fields_harmonic()
+            tasks_complete_init = ['Fourier_to_phys']
+        elif type_flow_init == 'NOISE':
+            w_fft, z_fft = self.init_fields_noise()
+            tasks_complete_init = ['Fourier_to_phys']
+        elif type_flow_init == 'LOAD_FILE':
+            self.get_state_from_file(self.params.init_fields.path_file)
+            tasks_complete_init = []
+        elif type_flow_init == 'CONSTANT':
+            #   rot_fft = sim.oper.constant_arrayK(value=0.)
+            #    if mpi.rank == 0:
+            #       rot_fft[1, 0] = 1.
+            tasks_complete_init = ['Fourier_to_phys']
+        else:
+            raise ValueError('bad value of params.type_flow_init')
+
+        if 'Fourier_to_phys' in tasks_complete_init:
+            sim.oper.dealiasing(w_fft)
+            sim.oper.dealiasing(z_fft)
+            sim.state.state_fft['w_fft'] = w_fft
+            sim.state.state_fft['z_fft'] = z_fft
+
+            sim.state.statephys_from_statefft()
+
+    def init_fields_harmonic(self):
+        w_fft = np.zeros(self.sim.oper.shapeK_loc, dtype=np.complex128)
+        z_fft = np.zeros(self.sim.oper.shapeK_loc, dtype=np.complex128)
+        w_fft[20, 25] = 1.
+        z_fft[20, 25] = 1.
+
+        w = self.oper.ifft2(w_fft)
+        z = self.oper.ifft2(z_fft)
+
+        w_fft = self.oper.fft2(w)
+        z_fft = self.oper.fft2(z)
+
+        return w_fft, z_fft
+
+    def init_fields_noise(self):
+        try:
+            lambda0 = self.params.init_fields.lambda_noise
+        except AttributeError:
+            lambda0 = self.oper.Lx/4
+
+        def H_smooth(x, delta):
+            return (1. + np.tanh(2*np.pi*x/delta))/2
+
+        # to compute always the same field... (for 1 resolution...)
+        np.random.seed(42)  # this does not work for MPI...
+
+        w_fft = (np.random.random(self.oper.shapeK) +
+                 1j*np.random.random(self.oper.shapeK) - 0.5 - 0.5j)
+        z_fft = (np.random.random(self.oper.shapeK) +
+                 1j*np.random.random(self.oper.shapeK) - 0.5 - 0.5j)
+
+        if mpi.rank == 0:
+            w_fft[0, 0] = 0.
+            z_fft[0, 0] = 0.
+
+        self.oper.dealiasing(w_fft, z_fft)
+
+        k0 = 2*np.pi/lambda0
+        delta_k0 = 1.*k0
+        w_fft = w_fft*H_smooth(k0-self.oper.KK, delta_k0)
+        z_fft = z_fft*H_smooth(k0-self.oper.KK, delta_k0)
+
+        w = self.oper.ifft2(w_fft)
+        z = self.oper.ifft2(z_fft)
+        velo_max = np.sqrt(w**2+z**2).max()
+        if mpi.nb_proc > 1:
+            velo_max = self.oper.comm.allreduce(velo_max, op=mpi.MPI.MAX)
+        w = self.params.init_fields.max_velo_noise*w/velo_max
+        z = self.params.init_fields.max_velo_noise*z/velo_max
+        w_fft = self.oper.fft2(w)
+        z_fft = self.oper.fft2(z)
+
+        return w_fft, z_fft
diff --git a/fluidsim/solvers/plate2d/output/__init__.py b/fluidsim/solvers/plate2d/output/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL291dHB1dC9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/fluidsim/solvers/plate2d/output/__init__.py
@@ -0,0 +1,88 @@
+"""
+Plate2d output (:mod:`fluidsim.solvers.plate2d.output`)
+=============================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.output
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   print_stdout
+   spatial_means
+   spectra
+   correlations_freq
+
+"""
+
+import numpy as np
+
+
+from fluidsim.base.output import OutputBasePseudoSpectral
+
+
+class Output(OutputBasePseudoSpectral):
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.Output.set_child('classes')
+        classes = info_solver.classes.Output.classes
+
+        package = 'fluidsim.solvers.plate2d.output'
+
+        classes.set_child(
+            'PrintStdOut',
+            attribs={'module_name': package + '.print_stdout',
+                     'class_name': 'PrintStdOutPlate2D'})
+
+        classes.set_child(
+            'PhysFields',
+            attribs={'module_name': 'fluidsim.base.output.phys_fields',
+                     'class_name': 'PhysFieldsBase'})
+
+        classes.set_child(
+            'Spectra',
+            attribs={'module_name': package + '.spectra',
+                     'class_name': 'SpectraPlate2D'})
+
+        classes.set_child(
+            'spatial_means',
+            attribs={'module_name': package + '.spatial_means',
+                     'class_name': 'SpatialMeansPlate2D'})
+
+        # classes.set_child(
+        #     'spatial_means',
+        #     attribs={'module_name': package + '.correlations_freq',
+        #              'class_name': 'CorrelationsFreq'})
+
+    @staticmethod
+    def _complete_params_with_default(params, info_solver):
+        """This static method is used to complete the *params* container.
+        """
+        OutputBasePseudoSpectral._complete_params_with_default(
+            params, info_solver)
+
+        params.output.phys_fields.field_to_plot = 'z'
+
+    def compute_energies_fft(self):
+        w_fft = self.sim.state.state_fft['w_fft']
+        z_fft = self.sim.state.state_fft['z_fft']
+        chi_fft = self.sim.state.compute('chi_fft')
+        Ee_fft = np.abs(
+            0.25*self.sim.oper.laplacian2_fft(np.abs(chi_fft)**2+0j))
+        El_fft = np.abs(0.5*self.sim.oper.laplacian2_fft(np.abs(z_fft)**2+0j))
+        Ek_fft = 0.5*np.abs(w_fft)**2
+        return Ek_fft, El_fft, Ee_fft
+
+    def compute_energy_fft(self):
+        Ek_fft, El_fft, Ee_fft = self.compute_energies_fft()
+        return Ek_fft + El_fft + Ee_fft
+
+    def compute_energy(self):
+        E_fft = self.compute_energy_fft()
+        return self.sum_wavenumbers(E_fft)
diff --git a/fluidsim/solvers/plate2d/output/correl_Mordant.m b/fluidsim/solvers/plate2d/output/correl_Mordant.m
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL291dHB1dC9jb3JyZWxfTW9yZGFudC5t
--- /dev/null
+++ b/fluidsim/solvers/plate2d/output/correl_Mordant.m
@@ -0,0 +1,38 @@
+% interactions 2<->2  F1+F3=F2+F4
+
+% version NMordant (moins rapide)
+for if1=ID2 %boucle F1 fix
+    if1
+    F1(j)=if1;
+    
+    % boucle F2>F1 et F4>F2 car Cor4 est symetrique par echange F2 F4
+    % Cor4(F4,F2,F1)
+    for if2=if1:1:newN
+        
+        afnew3=afnew(:,2*if2-if1:end);
+        afnew4=afnew(:,if2:end+if1-if2);
+    
+        AF1=repmat(afnew(:,if1),[1,newN-2*if2+if1+1]);
+        AF2=repmat(afnew(:,if2),[1,newN-2*if2+if1+1]);
+        
+        Cor4(if2:(newN-if2+if1),if2,j)=Cor4(if2:(newN-if2+if1),if2,j)+ (sum(conj(afnew3.*AF1).*(afnew4.*AF2),1)).';
+    end
+
+    % boucle F2<F1
+    for if2=1:if1
+        
+        afnew3=afnew(:,1:end-if1+if2);
+        afnew4=afnew(:,if1-if2+1:end);
+        
+        AF1=repmat(afnew(:,if1),[1,newN-if1+if2]);
+        AF2=repmat(afnew(:,if2),[1,newN-if1+if2]);
+        
+        Cor4(if1-if2+1:end,if2,j)=Cor4(if1-if2+1:end,if2,j)+ (sum(conj(afnew3.*AF1).*(afnew4.*AF2),1)).';
+    end
+    
+       j=j+1;
+ 
+end
+
+ 
+
diff --git a/fluidsim/solvers/plate2d/output/correlations_freq.py b/fluidsim/solvers/plate2d/output/correlations_freq.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL291dHB1dC9jb3JyZWxhdGlvbnNfZnJlcS5weQ==
--- /dev/null
+++ b/fluidsim/solvers/plate2d/output/correlations_freq.py
@@ -0,0 +1,203 @@
+"""
+Correl freq (:mod:`fluidsim.solvers.plate2d.output.correlations_freq`)
+============================================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.output.correlations_freq
+
+Provides:
+
+.. autoclass:: CorrelationsFreq
+   :members:
+   :private-members:
+
+"""
+import h5py
+
+import os
+import numpy as np
+
+from fluiddyn.util import mpi
+
+from fluidsim.base.output.base import SpecificOutput
+
+
+class CorrelationsFreq(SpecificOutput):
+    """Compute, save, load and plot correlations of frequencies.
+
+    """
+
+    _tag = 'correl'
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        tag = 'correl'
+
+        params.output.periods_save.set_attrib(tag, 0)
+        params.output.set_child(tag,
+                                attribs={
+                                    'HAS_TO_PLOT_SAVED': False,
+                                    'nb_times': 1000})
+
+    def __init__(self, output):
+        params = output.sim.params
+
+        self.nb_times = params.output.correl.nb_times
+        self.nb_omegas = self.nb_times/4
+
+        super(CorrelationsFreq, self).__init__(
+            output,
+            period_save=params.output.periods_save.correl,
+            has_to_plot_saved=params.output.correl.HAS_TO_PLOT_SAVED)
+
+    def init_path_files(self):
+        path_run = self.output.path_run
+        self.path_file = path_run + '/correlations_freq.h5'
+
+    def init_files(self, dico_arrays_1time=None):
+        correlations = self.compute()
+        if mpi.rank == 0:
+            if not os.path.exists(self.path_file):
+                dico_arrays_1time = {'kxE': self.sim.oper.kxE,
+                                     'kyE': self.sim.oper.kyE}
+                self.create_file_from_dico_arrays(
+                    self.path_file, correlations, dico_arrays_1time)
+                self.nb_saved_times = 1
+            else:
+                with h5py.File(self.path_file, 'r') as f:
+                    dset_times = f['times']
+                    self.nb_saved_times = dset_times.shape[0]+1
+                # save the spectra in the file spectra1D.h5
+                self.add_dico_arrays_to_file(self.path_file1D,
+                                             correlations)
+
+        self.t_last_save = self.sim.time_stepping.t
+
+    def online_save(self):
+        """Save the values at one time. """
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_save >= self.period_save):
+            self.t_last_save = tsim
+            correlations = self.compute()
+            if mpi.rank == 0:
+                # save the spectra in the file correlation_freq.h5
+                self.add_dico_arrays_to_file(self.path_file,
+                                             correlations)
+                self.nb_saved_times += 1
+                # if self.has_to_plot:
+                #     self._online_plot(dico_spectra1D, dico_spectra2D)
+
+                #     if (tsim-self.t_last_show >= self.period_show):
+                #         self.t_last_show = tsim
+                #         self.axe.get_figure().canvas.draw()
+
+    def compute(self):
+        """compute the values at one time."""
+        if mpi.rank == 0:
+            dico_results = {}
+            return dico_results
+
+    def init_online_plot(self):
+        fig, axe = self.output.figure_axe(numfig=4000000)
+        self.axe = axe
+        axe.set_xlabel('?')
+        axe.set_ylabel('?')
+        axe.set_title('Correlation, solver '+self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx))
+        axe.hold(True)
+
+    def _online_plot(self):
+        pass
+
+    # def load2D_mean(self, tmin=None, tmax=None):
+    #     f = h5py.File(self.path_file2D, 'r')
+    #     dset_times = f['times']
+    #     times = dset_times[...]
+    #     nt = len(times)
+
+    #     kh = f['khE'][...]
+
+    #     if tmin is None:
+    #         imin_plot = 0
+    #     else:
+    #         imin_plot = np.argmin(abs(times-tmin))
+
+    #     if tmax is None:
+    #         imax_plot = nt-1
+    #     else:
+    #         imax_plot = np.argmin(abs(times-tmax))
+
+    #     tmin = times[imin_plot]
+    #     tmax = times[imax_plot]
+
+    #     print('compute mean of 2D spectra\n'
+    #           ('tmin = {0:8.6g} ; tmax = {1:8.6g}'
+    #            'imin = {2:8d} ; imax = {3:8d}').format(
+    #               tmin, tmax, imin_plot, imax_plot))
+
+    #     dico_results = {'kh': kh}
+    #     for key in f.keys():
+    #         if key.startswith('spectr'):
+    #             dset_key = f[key]
+    #             spect = dset_key[imin_plot:imax_plot+1].mean(0)
+    #             dico_results[key] = spect
+    #     return dico_results
+
+    def plot(self):
+        pass
+
+    def _compute_correl4(self, w):
+        r"""Compute the correlations 4.
+
+        .. math::
+           C_4(\omega_1, \omega_2, \omega_3, \omega_4) =
+           \langle
+           \tilde w(\omega_1, \mathbf{x}) +
+           \tilde w(\omega_2, \mathbf{x}) +
+           \tilde w(\omega_3, \mathbf{x})^* +
+           \tilde w(\omega_4, \mathbf{x})^* +
+           \rangle_\mathbf{x},
+
+        where
+
+        .. math::
+           \omega_2 = \omega_3 + \omega_4 - \omega_1
+
+        and :math:`\omega_1 > 0`, :math:`\omega_3 > 0` and
+        :math:`\omega_4 > 0`. Thus, this function produces an array
+        :math:`C_4(\omega_1, \omega_3, \omega_4)`.
+
+        """
+
+        nt, ny, nx = w.shape
+
+        w_fftt = self.oper_fft1.fft(w).reshape([nt, nx*ny])
+        w_fftt_conj = w_fftt.conj()
+
+        nb_omegas = self.nb_omegas
+
+        iomegas1 = self.iomegas1
+
+        corr4 = np.empty([len(iomegas1), nb_omegas, nb_omegas])
+
+        for i1, io1 in enumerate(iomegas1):
+            # this loop could be parallelized (OMP)
+            for io3 in range(nb_omegas):
+                # we use the symmetry omega_3 <--> omega_4
+                for io4 in range(0, io3+1):
+                    tmp = (w_fftt[io1, :] *
+                           w_fftt_conj[io3, :] *
+                           w_fftt_conj[io4, :])
+                    io2 = io3 + io4 - io1
+                    if io2 < 0:
+                        io2 = abs(io2)
+                        corr4[i1, io3, io4] = np.mean(tmp*w_fftt_conj[io2, :])
+                    else:
+                        corr4[i1, io3, io4] = np.mean(tmp*w_fftt[io3, :])
+                # symmetry omega_3 <--> omega_4:
+                corr4[i1, io4, io3] = corr4[i1, io3, io4]
+
+        # if mpi.nb_proc > 1:
+        #     # reduce for mean:
+        #     mpi.comm.
+
+        return corr4
diff --git a/fluidsim/solvers/plate2d/output/correlations_freq_cy.pyx b/fluidsim/solvers/plate2d/output/correlations_freq_cy.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL291dHB1dC9jb3JyZWxhdGlvbnNfZnJlcV9jeS5weXg=
--- /dev/null
+++ b/fluidsim/solvers/plate2d/output/correlations_freq_cy.pyx
@@ -0,0 +1,83 @@
+"""
+Correl freq (:mod:`fluidsim.solvers.plate2d.output.correlations_freq`)
+============================================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.output.correlations_freq
+
+Provides:
+
+.. autoclass:: CorrelationsFreq
+   :members:
+   :private-members:
+
+"""
+import h5py
+
+import numpy as np
+
+# from fluiddyn.util import mpi
+
+from .correlations_freq import CorrelationsFreq as CorrelationsFreqPython
+
+
+class CorrelationsFreq(CorrelationsFreqPython):
+    """Compute, save, load and plot correlations of frequencies.
+
+    """
+
+    def _compute_correl4(self, w):
+        r"""Compute the correlations 4.
+
+        .. math::
+           C_4(\omega_1, \omega_2, \omega_3, \omega_4) =
+           \langle
+           \tilde w(\omega_1, \mathbf{x}) +
+           \tilde w(\omega_2, \mathbf{x}) +
+           \tilde w(\omega_3, \mathbf{x})^* +
+           \tilde w(\omega_4, \mathbf{x})^* +
+           \rangle_\mathbf{x},
+
+        where
+
+        .. math::
+           \omega_2 = \omega_3 + \omega_4 - \omega_1
+
+        and :math:`\omega_1 > 0`, :math:`\omega_3 > 0` and
+        :math:`\omega_4 > 0`. Thus, this function produces an array
+        :math:`C_4(\omega_1, \omega_3, \omega_4)`.
+
+        """
+
+        nt, ny, nx = w.shape
+
+        w_fftt = self.oper_fft1.fft(w).reshape([nt, nx*ny])
+        w_fftt_conj = w_fftt.conj()
+
+        nb_omegas = self.nb_omegas
+
+        iomegas1 = self.iomegas1
+
+        corr4 = np.empty([len(iomegas1), nb_omegas, nb_omegas])
+
+        for i1, io1 in enumerate(iomegas1):
+            # this loop could be parallelized (OMP)
+            for io3 in range(nb_omegas):
+                # we use the symmetry omega_3 <--> omega_4
+                for io4 in range(0, io3+1):
+                    tmp = (w_fftt[io1, :] *
+                           w_fftt_conj[io3, :] *
+                           w_fftt_conj[io4, :])
+                    io2 = io3 + io4 - io1
+                    if io2 < 0:
+                        io2 = abs(io2)
+                        corr4[i1, io3, io4] = np.mean(tmp*w_fftt_conj[io2, :])
+                    else:
+                        corr4[i1, io3, io4] = np.mean(tmp*w_fftt[io3, :])
+                # symmetry omega_3 <--> omega_4:
+                corr4[i1, io4, io3] = corr4[i1, io3, io4]
+
+        # if mpi.nb_proc > 1:
+        #     # reduce for mean:
+        #     mpi.comm.
+
+        return corr4
diff --git a/fluidsim/solvers/plate2d/output/print_stdout.py b/fluidsim/solvers/plate2d/output/print_stdout.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL291dHB1dC9wcmludF9zdGRvdXQucHk=
--- /dev/null
+++ b/fluidsim/solvers/plate2d/output/print_stdout.py
@@ -0,0 +1,142 @@
+"""Standard output (:mod:`fluidsim.solvers.plate2d.output.print_stdout`)
+==============================================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.output.print_stdout
+
+Provides:
+
+.. autoclass:: PrintStdOutPlate2D
+   :members:
+   :private-members:
+
+"""
+
+from __future__ import print_function, division
+
+from time import time
+import numpy as np
+
+from fluidsim.base.output.print_stdout import PrintStdOutBase
+
+from fluiddyn.util import mpi
+
+
+class PrintStdOutPlate2D(PrintStdOutBase):
+    """Used to print in both the stdout and the stdout.txt file, and also
+    to print simple info on the current state of the simulation.
+
+    """
+
+    def online_print(self):
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_print_info <= self.period_print):
+            return
+
+        tsim = self.sim.time_stepping.t
+        itsim = self.sim.time_stepping.it
+        deltatsim = self.sim.time_stepping.deltat
+
+        energy = self.output.compute_energy()
+        if mpi.rank == 0:
+            t_real_word = time()
+            if self.t_real_word_last == 0.:
+                duration_left = 0
+            else:
+                if self.params.time_stepping.USE_T_END:
+                    duration_left = int(np.round(
+                        (self.params.time_stepping.t_end - tsim)
+                        * (t_real_word-self.t_real_word_last)
+                        / (tsim - self.t_last_print_info)
+                    ))
+                else:
+                    duration_left = int(np.round(
+                        (self.params.time_stepping.it_end - itsim)
+                        * (t_real_word-self.t_real_word_last)
+                    ))
+            to_print = (
+                'it = {0:6d} ; t      = {1:9.3f} ; deltat       = {2:10.5g}\n'+
+                '              energy = {3:9.3e} ; Delta energy = {4:+9.3e}\n'+
+                '              estimated remaining duration = {5:6d} s')
+            to_print = to_print.format(
+                itsim, tsim, deltatsim,
+                energy, energy-self.energy_temp,
+                duration_left)
+            self.print_stdout(to_print)
+            self.t_real_word_last = t_real_word
+        self.energy_temp = energy
+        self.t_last_print_info = tsim
+
+    def load(self):
+        dico_results = {'name_solver': self.output.name_solver}
+        file_means = open(self.output.path_run+'/stdout.txt')
+        lines = file_means.readlines()
+
+        lines_t = []
+        lines_E = []
+        for il, line in enumerate(lines):
+            if line[0:4] == 'it =':
+                lines_t.append(line)
+            if line[0:22] == '              energy =':
+                lines_E.append(line)
+
+        nt = len(lines_t)
+        if nt > 1:
+            nt -= 1
+
+        it = np.zeros(nt, dtype=np.int)
+        t = np.zeros(nt)
+        deltat = np.zeros(nt)
+
+        E = np.zeros(nt)
+        deltaE = np.zeros(nt)
+
+        for il in xrange(nt):
+            line = lines_t[il]
+            words = line.split()
+            it[il] = int(words[2])
+            t[il] = float(words[6])
+            deltat[il] = float(words[10])
+
+            line = lines_E[il]
+            words = line.split()
+            E[il] = float(words[2])
+            deltaE[il] = float(words[7])
+
+        dico_results['it'] = it
+        dico_results['t'] = t
+        dico_results['deltat'] = deltat
+        dico_results['E'] = E
+        dico_results['deltaE'] = deltaE
+
+        return dico_results
+
+    def plot(self):
+        dico_results = self.load()
+
+        t = dico_results['t']
+        deltat = dico_results['deltat']
+        E = dico_results['E']
+        deltaE = dico_results['deltaE']
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.55
+        width_axe = 0.85
+        height_axe = 0.4
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('deltat(t)')
+
+        ax1.set_title('info stdout, solver '+self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx))
+        ax1.hold(True)
+        ax1.plot(t, deltat, 'k', linewidth=2)
+
+        size_axe[1] = 0.08
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('t')
+        ax2.set_ylabel('E(t), deltaE(t)')
+        ax2.hold(True)
+        ax2.plot(t, E, 'k', linewidth=2)
+        ax2.plot(t, deltaE, 'b', linewidth=2)
diff --git a/fluidsim/solvers/plate2d/output/spatial_means.py b/fluidsim/solvers/plate2d/output/spatial_means.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL291dHB1dC9zcGF0aWFsX21lYW5zLnB5
--- /dev/null
+++ b/fluidsim/solvers/plate2d/output/spatial_means.py
@@ -0,0 +1,270 @@
+"""
+Spatial means (:mod:`fluidsim.solvers.plate2d.output.spatial_means`)
+==========================================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.output.spatial_means
+
+Provides:
+
+.. autoclass:: SpatialMeansPlate2D
+   :members:
+   :private-members:
+
+"""
+from __future__ import division, print_function
+
+import os
+import numpy as np
+
+
+from fluiddyn.util import mpi
+
+from fluidsim.base.output.spatial_means import SpatialMeansBase
+
+
+class SpatialMeansPlate2D(SpatialMeansBase):
+    r"""Compute, save, load and plot spatial means.
+
+    .. |p| mathmacro:: \partial
+
+    If only :math:`W` is forced and dissipated, the energy budget is
+    quite simple and can be written as:
+
+    .. math::
+
+       \p_t E_W = - C_{W\rightarrow Z} - C_{W\rightarrow \chi} + P_W - D_W,
+
+       \p_t E_Z = + C_{W\rightarrow Z},
+
+       \p_t E_\chi = + C_{W\rightarrow \chi},
+
+    where
+
+    .. math::
+
+       C_{W\rightarrow Z} = \sum_{\mathbf{k}} k^4\mathcal{R}(\hat W \hat Z^*),
+
+       C_{W\rightarrow \chi} = -\sum_{\mathbf{k}}
+       \mathcal{R}( \widehat{\{ W, Z\}} \hat \chi ^* ),
+
+       P_W = \sum_{\mathbf{k}} \mathcal{R}( \hat F_W \hat W^* )
+
+    and
+
+    .. math::
+
+       D_W = 2 \nu_\alpha \sum_{\mathbf{k}} k^{2\alpha} E_K(k).
+
+"""
+
+    def save_one_time(self):
+        tsim = self.sim.time_stepping.t
+        self.t_last_save = tsim
+
+        energy_fft = self.output.compute_energy_fft()
+        energy = self.sum_wavenumbers(energy_fft)
+
+        f_d, f_d_hypo = self.sim.time_stepping.compute_freq_diss()
+        epsK = self.sum_wavenumbers(f_d*2*energy_fft)
+        epsK_hypo = self.sum_wavenumbers(f_d_hypo*2*energy_fft)
+
+        if self.sim.params.FORCING:
+            deltat = self.sim.time_stepping.deltat
+            Frot_fft = self.sim.forcing.get_forcing()['rot_fft']
+            Fx_fft, Fy_fft = self.vecfft_from_rotfft(Frot_fft)
+
+            rot_fft = self.sim.state.state_fft['rot_fft']
+            ux_fft, uy_fft = self.vecfft_from_rotfft(rot_fft)
+
+            PZ1_fft = np.real(
+                rot_fft.conj()*Frot_fft +
+                rot_fft*Frot_fft.conj())/2
+            PZ2_fft = (abs(Frot_fft)**2)*deltat/2
+
+            PZ1 = self.sum_wavenumbers(PZ1_fft)
+            PZ2 = self.sum_wavenumbers(PZ2_fft)
+
+            PK1_fft = np.real(
+                ux_fft.conj()*Fx_fft +
+                ux_fft*Fx_fft.conj() +
+                uy_fft.conj()*Fy_fft +
+                uy_fft*Fy_fft.conj())/2
+            PK2_fft = (abs(Fx_fft)**2+abs(Fy_fft)**2)*deltat/2
+
+            PK1 = self.sum_wavenumbers(PK1_fft)
+            PK2 = self.sum_wavenumbers(PK2_fft)
+
+        if mpi.rank == 0:
+            epsK_tot = epsK+epsK_hypo
+
+            self.file.write(
+                '####\ntime = {0:7.3f}\n'.format(tsim))
+            to_print = (
+                'E    = {0:11.6e} \n'
+                'epsK = {1:11.6e} ; epsK_hypo = {2:11.6e} ; '
+                'epsK_tot = {3:11.6e} \n').format(
+                    energy, epsK, epsK_hypo, epsK+epsK_hypo)
+            self.file.write(to_print)
+
+            if self.sim.params.FORCING:
+                PK_tot = PK1+PK2
+                to_print = (
+'PK1  = {0:11.6e} ; PK2       = {1:11.6e} ; PK_tot   = {2:11.6e} \n'
+'PZ1  = {3:11.6e} ; PZ2       = {4:11.6e} ; PZ_tot   = {5:11.6e} \n'
+).format(PK1, PK2, PK1+PK2, PZ1, PZ2, PZ1+PZ2)
+                self.file.write(to_print)
+
+            self.file.flush()
+            os.fsync(self.file.fileno())
+
+        if self.sim.params.output.spatial_means.has_to_plot and mpi.rank == 0:
+
+            self.axe_a.plot(tsim, energy, 'k.')
+
+            self.axe_b.plot(tsim, epsK_tot, 'k.')
+            if self.sim.params.FORCING:
+                self.axe_b.plot(tsim, PK_tot, 'm.')
+
+            if (tsim-self.t_last_show >= self.period_show):
+                self.t_last_show = tsim
+                fig = self.axe_a.get_figure()
+                fig.canvas.draw()
+
+    def load(self):
+        dico_results = {'name_solver': self.output.name_solver}
+
+        file_means = open(self.path_file)
+        lines = file_means.readlines()
+
+        lines_t = []
+        lines_E = []
+        lines_PK = []
+        lines_PZ = []
+        lines_epsK = []
+
+        for il, line in enumerate(lines):
+            if line.startswith('time ='):
+                lines_t.append(line)
+            if line.startswith('E    ='):
+                lines_E.append(line)
+            if line.startswith('PK1  ='):
+                lines_PK.append(line)
+            if line.startswith('PZ1  ='):
+                lines_PZ.append(line)
+            if line.startswith('epsK ='):
+                lines_epsK.append(line)
+
+        nt = len(lines_t)
+        if nt > 1:
+            nt -= 1
+
+        t = np.empty(nt)
+        E = np.empty(nt)
+        Z = np.empty(nt)
+        PK1 = np.empty(nt)
+        PK2 = np.empty(nt)
+        PK_tot = np.empty(nt)
+        PZ1 = np.empty(nt)
+        PZ2 = np.empty(nt)
+        PZ_tot = np.empty(nt)
+        epsK = np.empty(nt)
+        epsK_hypo = np.empty(nt)
+        epsK_tot = np.empty(nt)
+
+        for il in xrange(nt):
+            line = lines_t[il]
+            words = line.split()
+            t[il] = float(words[2])
+
+            line = lines_E[il]
+            words = line.split()
+            E[il] = float(words[2])
+            Z[il] = float(words[6])
+
+            if self.sim.params.FORCING:
+                line = lines_PK[il]
+                words = line.split()
+                PK1[il] = float(words[2])
+                PK2[il] = float(words[6])
+                PK_tot[il] = float(words[10])
+
+                line = lines_PZ[il]
+                words = line.split()
+                PZ1[il] = float(words[2])
+                PZ2[il] = float(words[6])
+                PZ_tot[il] = float(words[10])
+
+            line = lines_epsK[il]
+            words = line.split()
+            epsK[il] = float(words[2])
+            epsK_hypo[il] = float(words[6])
+            epsK_tot[il] = float(words[10])
+
+        dico_results['t'] = t
+        dico_results['E'] = E
+        dico_results['Z'] = Z
+
+        dico_results['PK1'] = PK1
+        dico_results['PK2'] = PK2
+        dico_results['PK_tot'] = PK_tot
+
+        dico_results['PZ1'] = PZ1
+        dico_results['PZ2'] = PZ2
+        dico_results['PZ_tot'] = PZ_tot
+
+        dico_results['epsK'] = epsK
+        dico_results['epsK_hypo'] = epsK_hypo
+        dico_results['epsK_tot'] = epsK_tot
+
+        return dico_results
+
+    def plot(self):
+        dico_results = self.load()
+
+        t = dico_results['t']
+        E = dico_results['E']
+        Z = dico_results['Z']
+
+        PK_tot = dico_results['PK_tot']
+
+        epsK = dico_results['epsK']
+        epsK_hypo = dico_results['epsK_hypo']
+        epsK_tot = dico_results['epsK_tot']
+
+        width_axe = 0.85
+        height_axe = 0.4
+        x_left_axe = 0.12
+        z_bottom_axe = 0.55
+
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('E(t)')
+        ax1.hold(True)
+        ax1.plot(t, E, 'k', linewidth=2)
+
+        z_bottom_axe = 0.08
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('t')
+        ax2.set_ylabel('Z(t)')
+        ax2.hold(True)
+        ax2.plot(t, Z, 'k', linewidth=2)
+
+        z_bottom_axe = 0.55
+        size_axe[1] = z_bottom_axe
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('P_E(t), epsK(t)')
+        ax1.hold(True)
+        ax1.plot(t, PK_tot, 'c', linewidth=2)
+        ax1.plot(t, epsK, 'r', linewidth=2)
+        ax1.plot(t, epsK_hypo, 'g', linewidth=2)
+        ax1.plot(t, epsK_tot, 'k', linewidth=2)
+
+
+
+
+
+
diff --git a/fluidsim/solvers/plate2d/output/spectra.py b/fluidsim/solvers/plate2d/output/spectra.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL291dHB1dC9zcGVjdHJhLnB5
--- /dev/null
+++ b/fluidsim/solvers/plate2d/output/spectra.py
@@ -0,0 +1,203 @@
+"""Spectra (:mod:`fluidsim.solvers.plate2d.output.spectra`)
+=================================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.output.spectra
+
+Provides:
+
+.. autoclass:: SpectraPlate2D
+   :members:
+   :private-members:
+
+"""
+
+import h5py
+
+import numpy as np
+
+from fluidsim.base.output.spectra import Spectra
+
+
+class SpectraPlate2D(Spectra):
+    """Compute, save, load and plot spectra."""
+
+    def compute(self):
+        """compute the values at one time."""
+        EK_fft, EL_fft, EE_fft = self.output.compute_energies_fft()
+        # compute the spectra 1D
+        spectrum1Dkx_EK, spectrum1Dky_EK = self.spectra1D_from_fft(EK_fft)
+        spectrum1Dkx_EL, spectrum1Dky_EL = self.spectra1D_from_fft(EL_fft)
+        spectrum1Dkx_EE, spectrum1Dky_EE = self.spectra1D_from_fft(EE_fft)
+
+        dico_spectra1D = {'spectrum1Dkx_EK': spectrum1Dkx_EK,
+                          'spectrum1Dky_EK': spectrum1Dky_EK,
+                          'spectrum1Dkx_EL': spectrum1Dkx_EL,
+                          'spectrum1Dky_EL': spectrum1Dky_EL,
+                          'spectrum1Dkx_EE': spectrum1Dkx_EE,
+                          'spectrum1Dky_EE': spectrum1Dky_EE}
+
+        # compute the spectra 2D
+        spectrum2D_EK = self.spectrum2D_from_fft(EK_fft)
+        spectrum2D_EL = self.spectrum2D_from_fft(EL_fft)
+        spectrum2D_EE = self.spectrum2D_from_fft(EE_fft)
+        dico_spectra2D = {'spectrum2D_EK': spectrum2D_EK,
+                          'spectrum2D_EL': spectrum2D_EL,
+                          'spectrum2D_EE': spectrum2D_EE}
+        return dico_spectra1D, dico_spectra2D
+
+    def _online_plot(self, dico_spectra1D, dico_spectra2D):
+        if (self.nx == self.params.oper.ny and
+                self.params.oper.Lx == self.params.oper.Ly):
+            spectrum2D_EK = dico_spectra2D['spectrum2D_EK']
+            spectrum2D_EL = dico_spectra2D['spectrum2D_EL']
+            spectrum2D_EE = dico_spectra2D['spectrum2D_EE']
+            spectrum2D_Etot = (spectrum2D_EK + spectrum2D_EL + spectrum2D_EE)
+            khE = self.oper.khE
+            coef_norm = khE**(3.)
+            self.axe.loglog(khE, spectrum2D_Etot*coef_norm, 'k', linewidth=2)
+            self.axe.loglog(khE, spectrum2D_EK*coef_norm, 'b--')
+            self.axe.loglog(khE, spectrum2D_EL*coef_norm, 'r--')
+            self.axe.loglog(khE, spectrum2D_EE*coef_norm, 'y--')
+            lin_inf, lin_sup = self.axe.get_ylim()
+            if lin_inf < 10e-6:
+                lin_inf = 10e-6
+            self.axe.set_ylim([lin_inf, lin_sup])
+        else:
+            print('you need to implement the ploting '
+                  'of the spectra for this case')
+
+    def plot1D(self, tmin=0, tmax=1000, delta_t=2,
+               coef_compensate=3):
+
+        f = h5py.File(self.path_file1D, 'r')
+        dset_times = f['times']
+
+        dset_kxE = f['kxE']
+        # dset_kyE = f['kyE']
+        kh = dset_kxE[...]
+
+        dset_spectrum1Dkx_EK = f['spectrum1Dkx_EK']
+        dset_spectrum1Dky_EK = f['spectrum1Dky_EK']
+        # dset_spectrum1Dkx_EL = f['spectrum1Dkx_EL']
+        # dset_spectrum1Dky_EL = f['spectrum1Dky_EL']
+        # dset_spectrum1Dkx_EE = f['spectrum1Dkx_EE']
+        # dset_spectrum1Dky_EE = f['spectrum1Dky_EE']
+
+        times = dset_times[...]
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        delta_t = delta_t_save*delta_i_plot
+        if delta_i_plot == 0:
+            delta_i_plot = 1
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        print(
+            'plot1D(tmin={0}, tmax={1}, delta_t={2:.2f},'.format(
+                tmin, tmax, delta_t) +
+            ' coef_compensate={0:.3f})'.format(coef_compensate))
+
+        print((
+            'plot 1D spectra\n'
+            'tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}\n'
+            'imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}').format(
+                tmin_plot, tmax_plot, delta_t,
+                imin_plot, imax_plot, delta_i_plot))
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('spectra')
+        ax1.set_title('1D spectra, solver '+self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx))
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('log')
+
+        coef_norm = kh**(coef_compensate)
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+                EK = (dset_spectrum1Dkx_EK[it]+dset_spectrum1Dky_EK[it])
+                EK[EK < 10e-16] = 0.
+                ax1.plot(kh, EK*coef_norm, 'b', linewidth=1)
+
+        EK = (dset_spectrum1Dkx_EK[imin_plot:imax_plot+1] +
+              dset_spectrum1Dky_EK[imin_plot:imax_plot+1]).mean(0)
+
+        ax1.plot(kh, kh**(-3)*coef_norm, 'k', linewidth=1)
+        ax1.plot(kh, 0.01*kh**(-5/3)*coef_norm, 'k--', linewidth=1)
+
+    def plot2D(self, tmin=0, tmax=1000, delta_t=2,
+               coef_compensate=3):
+        f = h5py.File(self.path_file2D, 'r')
+        dset_times = f['times']
+        # nb_spectra = dset_times.shape[0]
+        times = dset_times[...]
+        # nt = len(times)
+
+        kh = f['khE'][...]
+
+        dset_spectrum_EK = f['spectrum2D_EK']
+        dset_spectrum_EL = f['spectrum2D_EL']
+        dset_spectrum_EE = f['spectrum2D_EE']
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot = 1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        print(
+            'plot2D(tmin={0}, tmax={1}, delta_t={2:.2f},'.format(
+                tmin, tmax, delta_t) +
+            ' coef_compensate={0:.3f})'.format(coef_compensate))
+
+        print((
+            'plot 2D spectra\n'
+            'tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}\n'
+            'imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}').format(
+                tmin_plot, tmax_plot, delta_t,
+                imin_plot, imax_plot, delta_i_plot))
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('2D spectra')
+        ax1.set_title('2D spectra, solver ' + self.output.name_solver +
+                      ', nh = {0:5d}'.format(self.nx))
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('log')
+
+        coef_norm = kh**coef_compensate
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+                EK = dset_spectrum_EK[it]
+                EK[EK < 10e-16] = 0.
+                EL = dset_spectrum_EL[it]
+                EL[EL < 10e-16] = 0.
+                EE = dset_spectrum_EE[it]
+                EE[EE < 10e-16] = 0.
+                Etot = EK + EL + EE
+
+                ax1.plot(kh, Etot*coef_norm, 'k', linewidth=2)
+                ax1.plot(kh, EK*coef_norm, 'b--', linewidth=1)
+                ax1.plot(kh, EL*coef_norm, 'r--', linewidth=1)
+                ax1.plot(kh, EE*coef_norm, 'y', linewidth=1)
+
+        EK = dset_spectrum_EK[imin_plot:imax_plot+1].mean(0)
+        EK[EK < 10e-16] = 0.
+        ax1.plot(kh, EK*coef_norm, 'b-', linewidth=2)
+
+        ax1.plot(kh, kh**(-3)*coef_norm, 'k--', linewidth=1)
+        ax1.plot(kh, 0.01*kh**(-5./3)*coef_norm, 'k-.', linewidth=1)
diff --git a/fluidsim/solvers/plate2d/solver.py b/fluidsim/solvers/plate2d/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/plate2d/solver.py
@@ -0,0 +1,333 @@
+# -*- coding: utf-8 -*-
+
+"""Plate2d solver (:mod:`fluidsim.solvers.plate2d.solver`)
+================================================================
+
+.. currentmodule:: fluidsim.solvers.plate2d.solver
+
+Provides:
+
+.. autoclass:: Simul
+   :members:
+   :private-members:
+
+.. todo::
+
+   - Compile without fftw-mpi,
+   - bench performances,
+   - output:
+     * spectra,
+     * spatial means (energy, dissipation, forcing),
+     * correlations frequencies,
+   - forcing,
+   - solver solving exactly the linear terms.
+
+"""
+
+from __future__ import print_function
+
+import numpy as np
+
+from fluidsim.operators.setofvariables import SetOfVariables
+from fluidsim.base.solvers.pseudo_spect import (
+    SimulBasePseudoSpectral, InfoSolverPseudoSpectral)
+
+
+info_solver = InfoSolverPseudoSpectral()
+
+package = 'fluidsim.solvers.plate2d'
+info_solver.module_name = package + '.solver'
+info_solver.class_name = 'Simul'
+info_solver.short_name = 'Plate2D'
+
+classes = info_solver.classes
+
+classes.State.module_name = package + '.state'
+classes.State.class_name = 'StatePlate2D'
+
+classes.InitFields.module_name = package + '.init_fields'
+classes.InitFields.class_name = 'InitFieldsPlate2D'
+
+classes.Output.module_name = package + '.output'
+classes.Output.class_name = 'Output'
+
+classes.Forcing.module_name = package + '.forcing'
+classes.Forcing.class_name = 'ForcingPlate2D'
+
+
+info_solver.complete_with_classes()
+
+
+class Simul(SimulBasePseudoSpectral):
+    r"""Pseudo-spectral solver solving the Föppl-von Kármán equations.
+
+    Notes
+    -----
+
+    .. |p| mathmacro:: \partial
+
+    This class is dedicated to solve with a pseudo-spectral method the
+    Föppl-von Kármán equations which describe the dynamics of a rigid
+    plate.  Using the non-dimensional variables displacement :math:`Z`
+    and out of plane velocity :math:`W`:
+
+    .. math::
+       \p_t Z = W,
+
+    .. math::
+       \p_t W = - \Delta^2 Z + N_W(Z) + F f_W - \nu_\alpha (-\Delta)^\alpha W.
+
+    where :math:`\Delta = \p_{xx} + \p_{yy}` is the Laplacian. The
+    first term of the two equations corresponds to the linear part.
+    :math:`F f_W` and :math:`\nu_\alpha \Delta^\alpha W` are the
+    forcing and the dissipation terms, respectively. The nonlinear
+    term is equal to :math:`N_W(Z) = \{ Z, \chi \}`, where :math:`\{
+    \cdot, \cdot \}` is the Monge-Ampère operator
+
+    .. math::
+       \{ a, b \} = \p_{xx} a \p_{yy} b + \p_{yy} a \p_{xx} b
+       - 2 \p_{xy} a \p_{xy} b,
+
+    and
+
+    .. math:: \Delta^2 \chi = -\{ Z, Z \}.
+
+    Taking the Fourier transform, we get:
+
+    .. math::
+       \p_t \hat Z = \hat W,
+
+    .. math::
+       \p_t \hat W = - k^4 \hat Z + \widehat{N_W(Z)} + F \hat f_W
+       - \nu_\alpha k^{2\alpha} \hat W,
+
+    where :math:`k^2 = |\mathbf{k}|^2`. For this simple solver, we
+    will use the variables :math:`Z` and :math:`W` and only the
+    dissipative term will be solve exactly.  Thus, all the other terms
+    are included in the :func:`tendencies_nonlin` function.
+
+    **Energetics**: The total energy can be decomposed in the kinetic energy
+
+    .. math::
+       E_K = \frac{1}{2} \langle W^2 \rangle
+       = \frac{1}{2} \sum_\mathbf{k} |\hat W|^2,
+
+    the flexion energy
+
+    .. math::
+       E_L = \frac{1}{2} \langle (\Delta Z)^2 \rangle
+       = \frac{1}{2} \sum_\mathbf{k} k^4|\hat Z|^2,
+
+    and the non-quadratic extension energy
+
+    .. math::
+       E_E = \frac{1}{4} \langle (\Delta \chi)^2 \rangle
+       = \frac{1}{4} \sum_\mathbf{k} k^4 |\hat \chi|^2.
+
+    The energy injected into the system by the forcing is
+
+    .. math::
+       P = F \langle f W \rangle,
+
+    and the dissipation is
+
+    .. math::
+       D = \nu_\alpha \langle W (-\Delta)^\alpha W \rangle.
+
+    """
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        SimulBasePseudoSpectral._complete_params_with_default(params)
+        attribs = {'beta': 0.}
+        params.set_attribs(attribs)
+
+    def __init__(self, params):
+        # the common initialization with the PLATE2D info_solver:
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_fft=None):
+        """Compute the "nonlinear" tendencies."""
+        oper = self.oper
+
+        if state_fft is None:
+            w_fft = self.state.state_fft['w_fft']
+            z_fft = self.state.state_fft['z_fft']
+        else:
+            w_fft = state_fft['w_fft']
+            z_fft = state_fft['z_fft']
+
+        mamp_zz = oper.monge_ampere_from_fft(z_fft, z_fft)
+        chi_fft = - oper.invlaplacian2_fft(oper.fft2(mamp_zz))
+        mamp_zchi = oper.monge_ampere_from_fft(z_fft, chi_fft)
+        Nw_fft = oper.fft2(mamp_zchi)
+        lap2z_fft = oper.laplacian2_fft(z_fft)
+        F_fft = - lap2z_fft + Nw_fft
+
+        oper.dealiasing(F_fft)
+        oper.dealiasing(w_fft)
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+
+        tendencies_fft['w_fft'] = F_fft
+        tendencies_fft['z_fft'] = w_fft
+
+        # ratio = self.test_tendencies_nonlin(
+        #     tendencies_fft, w_fft, z_fft, chi_fft)
+        # print('ratio:', ratio)
+
+        if self.params.FORCING:
+            tendencies_fft += self.forcing.get_forcing()
+
+        return tendencies_fft
+
+    def compute_freq_diss(self):
+        """Compute the dissipation frequencies with dissipation only for w."""
+        f_d_w, f_d_hypo_w = super(Simul, self).compute_freq_diss()
+        f_d = np.zeros_like(self.state.state_fft.data, dtype=np.float64)
+        f_d_hypo = np.zeros_like(self.state.state_fft.data,
+                                 dtype=np.float64)
+        f_d[0] = f_d_w
+        f_d_hypo[0] = f_d_hypo_w
+        return f_d, f_d_hypo
+
+    def test_tendencies_nonlin(
+            self, tendencies_fft=None,
+            w_fft=None, z_fft=None, chi_fft=None):
+        r"""Test if the tendencies conserves the total energy.
+
+        We consider the conservative Föppl-von Kármán equations
+        (without dissipation and forcing) written as
+
+        .. math::
+
+           \p_t Z = F_Z
+
+           \p_t W = F_W
+
+        We have:
+
+        .. math::
+
+           \p_t E_K(\mathbf{k}) = \mathcal{R} ( \hat F_W \hat W ^* )
+
+           \p_t E_L(\mathbf{k}) = k^4 \mathcal{R} ( \hat F_Z \hat Z ^* )
+
+           \p_t E_{NQ}(\mathbf{k}) =
+           - \mathcal{R} ( \widehat{\{ F_Z, Z\}} \hat \chi ^* )
+
+        Since the total energy is conserved, we should have
+
+        .. math::
+
+           \sum_{\mathbf{k}} \p_t E_K(\mathbf{k}) + \p_t E_L(\mathbf{k})
+           + \p_t E_{NQ}(\mathbf{k}) = 0
+
+        This function computes this quantities.
+
+        """
+
+        if tendencies_fft is None:
+            tendencies_fft = self.tendencies_nonlin()
+            w_fft = self.state.state_fft['w_fft']
+            z_fft = self.state.state_fft['z_fft']
+            chi_fft = self.state.compute('chi_fft')
+
+        F_w_fft = tendencies_fft['w_fft']
+        F_z_fft = tendencies_fft['z_fft']
+
+        K4 = self.oper.K4
+
+        dt_E_K = np.real(F_w_fft * w_fft.conj())
+        dt_E_L = K4 * np.real(F_z_fft * z_fft.conj())
+
+        tmp = self.oper.monge_ampere_from_fft(F_z_fft, z_fft)
+        tmp_fft = self.oper.fft2(tmp)
+
+        dt_E_NQ = - np.real(tmp_fft * chi_fft.conj())
+
+        T = dt_E_K + dt_E_L + dt_E_NQ
+
+        norm = self.oper.sum_wavenumbers(abs(T))
+
+        if norm < 1e-15:
+            print('Only zeros in total energy tendency.')
+            # print('(K+L)\n', dt_E_K+dt_E_L)
+            # print('NQ\n', dt_E_NQ)
+            return 0
+        else:
+            T = T/norm
+            # print('ratio array\n', T)
+            # print('(K+L)\n', (dt_E_K+dt_E_L)/norm)
+            # print('NQ\n', dt_E_NQ/norm)
+            return self.oper.sum_wavenumbers(T)
+
+
+if __name__ == "__main__":
+
+    np.set_printoptions(precision=2)
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 192/2
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+    # params.oper.type_fft = 'FFTWPY'
+    params.oper.coef_dealiasing = 2./3
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-4*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    kmax = np.sqrt(2)*np.pi/delta_x
+
+    params.time_stepping.USE_CFL = False
+    params.time_stepping.deltat0 = 2*np.pi/kmax**2
+    params.time_stepping.USE_T_END = True
+    params.time_stepping.t_end = 50.0
+    params.time_stepping.it_end = 1
+
+    # params.init_fields.type_flow_init = 'HARMONIC'
+    params.init_fields.type_flow_init = 'NOISE'
+    params.init_fields.max_velo_noise = 0.001
+    # params.init_fields.path_file = (
+    #     '/home/users/bonamy2c/Sim_data/PLATE2D_test_L='
+    #     '2pix2pi_256x256_2015-03-04_22-36-37/state_phys_t=000.100.hd5')
+
+    params.FORCING = True
+    params.forcing.forcing_rate = 100.
+    # params.forcing.nkmax_forcing = 5
+    # params.forcing.nkmin_forcing = 4
+
+    params.output.periods_print.print_stdout = 0.5
+
+    params.output.periods_save.phys_fields = 0.0
+    params.output.periods_save.spectra = 0.5
+    # params.output.periods_save.spect_energy_budg = 0.5
+    # params.output.periods_save.increments = 0.5
+
+    params.output.ONLINE_PLOT_OK = False
+    params.output.period_show_plot = 0.5
+    params.output.periods_plot.phys_fields = 0.0
+
+    params.output.phys_fields.field_to_plot = 'z'
+
+    params.output.spectra.HAS_TO_PLOT_SAVED = True
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/plate2d/state.py b/fluidsim/solvers/plate2d/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL3N0YXRlLnB5
--- /dev/null
+++ b/fluidsim/solvers/plate2d/state.py
@@ -0,0 +1,75 @@
+"""Plate2d state (:mod:`fluidsim.solvers.plate2d.state`)
+==============================================================
+"""
+
+from fluidsim.base.state import StatePseudoSpectral
+
+from fluiddyn.util import mpi
+
+
+class StatePlate2D(StatePseudoSpectral):
+    """Contains the variables corresponding to the state and handles the
+    access to other fields for the solver PLATE2D.
+
+    """
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.State.set_attribs({
+            'keys_state_fft': ['w_fft', 'z_fft'],
+            'keys_state_phys': ['w', 'z'],
+            'keys_computable': [],
+            'keys_phys_needed': ['w', 'z'],
+            'keys_linear_eigenmodes': []})
+
+    def compute(self, key, SAVE_IN_DICT=True, RAISE_ERROR=True):
+        oper = self.oper
+        it = self.sim.time_stepping.it
+        if (key in self.vars_computed and
+                it == self.it_computed[key]):
+            return self.vars_computed[key]
+
+        if key == 'w_fft':
+            result = oper.fft2(self.state_phys['w'])
+        elif key == 'z_fft':
+            result = oper.fft2(self.state_phys['z'])
+        elif key == 'chi_fft':
+            mamp_zz = oper.monge_ampere_from_fft(
+                self.state_fft['z_fft'], self.state_fft['z_fft'])
+            result = - oper.invlaplacian2_fft(oper.fft2(mamp_zz))
+        elif key == 'chi':
+            chi_fft = self.compute('chi_fft')
+            result = oper.ifft2(chi_fft)
+        elif key == 'Nw_fft':
+            mamp_zchi = oper.monge_ampere_from_fft(
+                self.state_fft['z_fft'], self.compute('chi_fft'))
+            result = oper.fft2(mamp_zchi)
+        elif key == 'lapz_fft':
+            z_fft = self.compute('z_fft')
+            result = oper.laplacian2_fft(z_fft)
+        else:
+            to_print = 'Do not know how to compute "'+key+'".'
+            if RAISE_ERROR:
+                raise ValueError(to_print)
+            else:
+                if mpi.rank == 0:
+                    print(to_print
+                          + '\nreturn an array of zeros.')
+
+                result = self.oper.constant_arrayX(value=0.)
+
+        if SAVE_IN_DICT:
+            self.vars_computed[key] = result
+            self.it_computed[key] = it
+
+        return result
+
+    def statephys_from_statefft(self):
+        w_fft = self.state_fft['w_fft']
+        z_fft = self.state_fft['z_fft']
+        self.state_phys['w'] = self.oper.ifft2(w_fft)
+        self.state_phys['z'] = self.oper.ifft2(z_fft)
diff --git a/fluidsim/solvers/plate2d/test/__init__.py b/fluidsim/solvers/plate2d/test/__init__.py
new file mode 100644
diff --git a/fluidsim/solvers/plate2d/test/test_solver.py b/fluidsim/solvers/plate2d/test/test_solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9wbGF0ZTJkL3Rlc3QvdGVzdF9zb2x2ZXIucHk=
--- /dev/null
+++ b/fluidsim/solvers/plate2d/test/test_solver.py
@@ -0,0 +1,52 @@
+import unittest
+import shutil
+
+# import numpy as np
+
+import fluiddyn as fld
+
+from fluiddyn.io import stdout_redirected
+
+
+class TestSolverPLATE2D(unittest.TestCase):
+    # @unittest.expectedFailure
+    def test_tendency(self):
+
+        key_solver = 'PLATE2D'
+        solver = fld.simul.import_module_solver_from_key(key_solver)
+        params = fld.simul.create_params(solver)
+
+        params.short_name_type_run = 'test'
+
+        nh = 64
+        params.oper.nx = nh
+        params.oper.ny = nh
+        Lh = 6.
+        params.oper.Lx = Lh
+        params.oper.Ly = Lh
+
+        params.oper.coef_dealiasing = 2./3
+        params.nu_8 = 2.
+
+        params.time_stepping.USE_CFL = False
+        params.time_stepping.deltat0 = 0.005
+        params.time_stepping.t_end = 0.5
+
+        params.init_fields.type_flow_init = 'NOISE'
+        params.output.HAS_TO_SAVE = False
+        params.FORCING = False
+
+        params.output.ONLINE_PLOT_OK = False
+
+        with stdout_redirected():
+            sim = solver.Simul(params)
+
+        ratio = sim.test_tendencies_nonlin()
+
+        self.assertGreater(1e-15, ratio)
+
+        shutil.rmtree(sim.output.path_run)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fluidsim/solvers/sw1l/__init__.py b/fluidsim/solvers/sw1l/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/__init__.py
@@ -0,0 +1,5 @@
+"""1-layer Shallow-Water solvers (:mod:`fluidsim.solvers.sw1l`)
+=====================================================================
+
+
+"""
diff --git a/fluidsim/solvers/sw1l/exactlin/__init__.py b/fluidsim/solvers/sw1l/exactlin/__init__.py
new file mode 100644
diff --git a/fluidsim/solvers/sw1l/exactlin/solver.py b/fluidsim/solvers/sw1l/exactlin/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL2V4YWN0bGluL3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/sw1l/exactlin/solver.py
@@ -0,0 +1,255 @@
+"""SW1l equations solving exactly the linear terms
+==================================================
+
+(:mod:`fluidsim.solvers.sw1l.exactlin.solver`)
+
+
+
+"""
+
+from __future__ import division, print_function
+
+import numpy as np
+
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+
+from fluidsim.solvers.sw1l.solver import InfoSolverSW1l
+from fluidsim.solvers.sw1l.solver import Simul as SimulSW1l
+
+
+from fluiddyn.util import mpi
+
+
+class InfoSolverSW1lExactLin(InfoSolverSW1l):
+    """Information about the solver SW1l."""
+    def __init__(self, **kargs):
+        super(InfoSolverSW1lExactLin, self).__init__(**kargs)
+
+        if 'tag' in kargs and kargs['tag'] == 'solver':
+
+            sw1l = 'fluidsim.solvers.sw1l'
+
+            self.module_name = sw1l+'.exactlin.solver'
+            self.class_name = 'Simul'
+            self.short_name = 'SW1lexlin'
+
+            classes = self.classes
+
+            classes.State.module_name = sw1l+'.exactlin.state'
+            classes.State.class_name = 'StateSW1lExactLin'
+
+            classes.InitFields.class_name = 'InitFieldsSW1lExLin'
+
+            classes.Forcing.class_name = 'ForcingSW1lExactLin'
+
+
+info_solver = InfoSolverSW1lExactLin(tag='solver')
+info_solver.complete_with_classes()
+
+
+class Simul(SimulSW1l):
+    """A solver of the shallow-water 1 layer equations (SW1l)"""
+
+    def __init__(self, params, info_solver=info_solver):
+        super(Simul, self).__init__(params, info_solver)
+
+    # def tendencies_nonlin(self, state_fft=None):
+    #     oper = self.oper
+    #     fft2 = oper.fft2
+
+    #     if state_fft is None:
+    #         state_phys = self.state.state_phys
+    #         state_fft = self.state.state_fft
+    #     else:
+    #         state_phys = self.return_statephys_from_statefft(state_fft)
+
+    #     ux = state_phys['ux']
+    #     uy = state_phys['uy']
+    #     eta = state_phys['eta']
+    #     q = state_phys['q']
+    #     d = state_phys['div']
+
+    #     q_fft = state_fft['q_fft']
+    #     ap_fft = state_fft['ap_fft']
+    #     am_fft = state_fft['am_fft']
+
+    #     a_fft = ap_fft + am_fft
+    #     div_fft = self.divfft_from_apamfft(ap_fft, am_fft)
+
+    #     pxq_fft, pyq_fft = oper.gradfft_from_fft(q_fft)
+    #     pxq = oper.ifft2(pxq_fft)
+    #     pyq = oper.ifft2(pyq_fft)
+
+    #     Fq = -ux*pxq - uy*pyq - q*d
+    #     Fq_fft = fft2(Fq)
+
+    def tendencies_nonlin(self, state_fft=None):
+        oper = self.oper
+        fft2 = oper.fft2
+
+        if state_fft is None:
+            state_phys = self.state.state_phys
+            state_fft = self.state.state_fft
+        else:
+            state_phys = self.state.return_statephys_from_statefft(state_fft)
+
+        ux = state_phys['ux']
+        uy = state_phys['uy']
+        eta = state_phys['eta']
+        rot = state_phys['rot']
+
+        # compute the nonlinear terms for ux, uy and eta
+        N1x = +rot*uy
+        N1y = -rot*ux
+        gradu2_x_fft, gradu2_y_fft = oper.gradfft_from_fft(
+            fft2(ux**2+uy**2)/2)
+
+        Nx_fft = fft2(N1x) - gradu2_x_fft
+        Ny_fft = fft2(N1y) - gradu2_y_fft
+
+        jx_fft = fft2(eta*ux)
+        jy_fft = fft2(eta*uy)
+        Neta_fft = -oper.divfft_from_vecfft(jx_fft, jy_fft)
+
+        # self.verify_tendencies(state_fft, state_phys,
+        #                        Nx_fft, Ny_fft, Neta_fft)
+
+        # compute the nonlinear terms for q, ap and am
+        (Nq_fft, Np_fft, Nm_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(Nx_fft, Ny_fft, Neta_fft)
+
+        # Nq_fft = self.oper.constant_arrayK(value=0)
+        # Np_fft = self.oper.constant_arrayK(value=0)
+        # Nm_fft = self.oper.constant_arrayK(value=0)
+
+        oper.dealiasing(Nq_fft, Np_fft, Nm_fft)
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+        tendencies_fft['q_fft'] = Nq_fft
+        tendencies_fft['ap_fft'] = Np_fft
+        tendencies_fft['am_fft'] = Nm_fft
+
+        if self.params.FORCING:
+            tendencies_fft += self.forcing.get_forcing()
+
+        return tendencies_fft
+
+    def compute_freq_complex(self, key):
+        K2 = self.oper.K2
+        if key == 'q_fft':
+            omega = self.oper.constant_arrayK(value=0)
+        elif key == 'ap_fft':
+            omega = 1.j*np.sqrt(self.params.f**2 + self.params.c2*K2)
+        elif key == 'am_fft':
+            omega = -1.j*np.sqrt(self.params.f**2 + self.params.c2*K2)
+        return omega
+
+    def verify_tendencies(self, state_fft, state_phys,
+                          Nx_fft, Ny_fft, Neta_fft):
+        # for verification conservation energy
+        # compute the linear terms
+        oper = self.oper
+        ux = state_phys['ux']
+        uy = state_phys['uy']
+        eta = state_phys['eta']
+
+        q_fft = state_fft['q_fft']
+        ap_fft = state_fft['ap_fft']
+        am_fft = state_fft['am_fft']
+        a_fft = ap_fft + am_fft
+        div_fft = self.divfft_from_apamfft(ap_fft, am_fft)
+
+        eta_fft = (oper.etafft_from_qfft(q_fft)
+                   + oper.etafft_from_afft(a_fft))
+
+        dx_c2eta_fft, dy_c2eta_fft = oper.gradfft_from_fft(
+            self.params.c2*eta_fft)
+        LCx = self.params.f*uy
+        LCy = -self.params.f*ux
+        Lx_fft = oper.fft2(LCx) - dx_c2eta_fft
+        Ly_fft = oper.fft2(LCy) - dy_c2eta_fft
+        Leta_fft = -div_fft
+
+        # compute the full tendencies
+        Fx_fft = Lx_fft + Nx_fft
+        Fy_fft = Ly_fft + Ny_fft
+        Feta_fft = Leta_fft + Neta_fft
+        oper.dealiasing(Fx_fft, Fy_fft, Feta_fft)
+
+        # test : ux, uy, eta ---> q, ap, am
+        (Fq_fft, Fp_fft, Fm_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(Fx_fft, Fy_fft, Feta_fft)
+        # test : q, ap, am ---> ux, uy, eta
+        (Fx2_fft, Fy2_fft, Feta2_fft
+         ) = self.oper.uxuyetafft_from_qapamfft(Fq_fft, Fp_fft, Fm_fft)
+        print(np.max(abs(Fx2_fft - Fx_fft)))
+        print(np.max(abs(Fy2_fft - Fy_fft)))
+        print(np.max(abs(Feta2_fft - Feta_fft)))
+        Fx_fft = Fx2_fft
+        Fy_fft = Fy2_fft
+        Feta_fft = Feta2_fft
+
+        (Fq2_fft, Fp2_fft, Fm2_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(
+            Fx2_fft, Fy2_fft, Feta2_fft)
+        print(np.max(abs(Fq2_fft - Fq_fft)))
+        print(np.max(abs(Fp2_fft - Fp_fft)))
+        print(np.max(abs(Fm2_fft - Fm_fft)))
+
+        Fx = oper.ifft2(Fx_fft)
+        Fy = oper.ifft2(Fy_fft)
+        Feta = oper.ifft2(Feta_fft)
+        A = (Feta*(ux**2+uy**2)/2
+             + (1+eta)*(ux*Fx+uy*Fy)
+             + self.params.c2*eta*Feta)
+        A_fft = oper.fft2(A)
+        if mpi.rank == 0:
+            print('should be zero =', A_fft[0, 0])
+
+
+if __name__ == "__main__":
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 64
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 2.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 1.
+    params.output.periods_save.spectra = 0.5
+    params.output.periods_save.spect_energy_budg = 0.5
+    params.output.periods_save.increments = 0.5
+    params.output.periods_save.pdf = 0.5
+    params.output.periods_save.time_signals_fft = False
+
+    params.output.periods_plot.phys_fields = 0.
+
+    params.output.phys_fields.field_to_plot = 'div'
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    # sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/sw1l/exactlin/state.py b/fluidsim/solvers/sw1l/exactlin/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL2V4YWN0bGluL3N0YXRlLnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/exactlin/state.py
@@ -0,0 +1,193 @@
+"""State class for the SW1l.exactlin solver
+(:mod:`fluidsim.solvers.sw1l.exactlin.state`)
+===================================================
+
+.. currentmodule:: fluidsim.solvers.sw1l.exactlin.state
+
+Provides:
+
+.. autoclass:: StateSW1lExactLin
+   :members:
+   :private-members:
+
+"""
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.solvers.sw1l.state import StateSW1l
+
+from fluiddyn.util import mpi
+
+
+class StateSW1lExactLin(StateSW1l):
+    """
+    The class :class:`StateSW1lexlin` contains the variables corresponding
+    to the state and handles the access to other fields for the solver
+    SW1l.
+    """
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.State.set_attribs({
+            'keys_state_fft': ['ap_fft', 'am_fft', 'q_fft'],
+            'keys_state_phys': ['ux', 'uy', 'eta', 'rot'],
+            'keys_computable': [],
+            'keys_phys_needed': ['ux', 'uy', 'eta'],
+            'keys_linear_eigenmodes': ['q_fft', 'a_fft', 'd_fft']})
+
+
+
+    def compute(self, key, SAVE_IN_DICT=True, RAISE_ERROR=True):
+        it = self.sim.time_stepping.it
+
+        if (key in self.vars_computed and it == self.it_computed[key]):
+            return self.vars_computed[key]
+
+        if key == 'div_fft':
+            ap_fft = self.state_fft['ap_fft']
+            am_fft = self.state_fft['am_fft']
+            d_fft = self.oper.divfft_from_apamfft(ap_fft, am_fft)
+            result = d_fft
+
+        elif key == 'a_fft':
+            ap_fft = self.state_fft['ap_fft']
+            am_fft = self.state_fft['am_fft']
+            result = ap_fft + am_fft
+
+        elif key == 'rot_fft':
+            q_fft = self.state_fft['q_fft']
+            a_fft = self.compute('a_fft')
+            result = (self.oper.rotfft_from_qfft(q_fft)
+                      + self.oper.rotfft_from_afft(a_fft)
+                      )
+
+        elif key == 'eta_fft':
+            q_fft = self.state_fft['q_fft']
+            a_fft = self.compute('a_fft')
+            result = (self.oper.etafft_from_qfft(q_fft)
+                      + self.oper.etafft_from_afft(a_fft)
+                      )
+
+
+
+
+
+
+
+        elif key == 'ux_fft':
+            rot_fft = self.compute('rot_fft')
+            div_fft = self.compute('div_fft')
+            urx_fft, ury_fft = self.oper.vecfft_from_rotfft(rot_fft)
+            udx_fft, udy_fft = self.oper.vecfft_from_divfft(div_fft)
+            ux_fft = urx_fft + udx_fft
+            if mpi.rank == 0:
+                ap_fft = self.state_fft['ap_fft']
+                ux_fft[0, 0] = ap_fft[0, 0]
+            result = ux_fft
+            if SAVE_IN_DICT:
+                key2 = 'uy_fft'
+                uy_fft = ury_fft + udy_fft
+                if mpi.rank == 0:
+                    am_fft = self.state_fft['am_fft']
+                    uy_fft[0, 0] = am_fft[0, 0]
+
+                self.vars_computed[key2] = uy_fft
+                self.it_computed[key2] = it
+
+        elif key == 'uy_fft':
+            rot_fft = self.compute('rot_fft')
+            div_fft = self.compute('div_fft')
+            urx_fft, ury_fft = self.oper.vecfft_from_rotfft(rot_fft)
+            udx_fft, udy_fft = self.oper.vecfft_from_divfft(div_fft)
+            uy_fft = ury_fft + udy_fft
+            if mpi.rank == 0:
+                am_fft = self.state_fft['am_fft']
+                uy_fft[0, 0] = am_fft[0, 0]
+            result = uy_fft
+            if SAVE_IN_DICT:
+                key2 = 'ux_fft'
+                ux_fft = urx_fft + udx_fft
+                if mpi.rank == 0:
+                    ap_fft = self.state_fft['ap_fft']
+                    ux_fft[0, 0] = ap_fft[0, 0]
+                self.vars_computed[key2] = ux_fft
+                self.it_computed[key2] = it
+
+        else:
+            result = super(StateSW1lExactLin, self).compute(
+                key, SAVE_IN_DICT=SAVE_IN_DICT,
+                RAISE_ERROR=RAISE_ERROR)
+            SAVE_IN_DICT = False
+
+        if SAVE_IN_DICT:
+            self.vars_computed[key] = result
+            self.it_computed[key] = it
+
+        return result
+
+
+
+
+
+    def statefft_from_statephys(self):
+        """Compute the state in Fourier space."""
+        ux = self.state_phys['ux']
+        uy = self.state_phys['uy']
+        eta = self.state_phys['eta']
+
+        eta_fft = self.oper.fft2(eta)
+        ux_fft = self.oper.fft2(ux)
+        uy_fft = self.oper.fft2(uy)
+
+        (q_fft, ap_fft, am_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        self.state_fft['q_fft'] = q_fft
+        self.state_fft['ap_fft'] = ap_fft
+        self.state_fft['am_fft'] = am_fft
+
+
+
+    def statephys_from_statefft(self):
+        """Compute the state in physical space."""
+        ifft2 = self.oper.ifft2
+        q_fft = self.state_fft['q_fft']
+        ap_fft = self.state_fft['ap_fft']
+        am_fft = self.state_fft['am_fft']
+
+        (ux_fft, uy_fft, eta_fft
+         ) = self.oper.uxuyetafft_from_qapamfft(q_fft, ap_fft, am_fft)
+
+        rot_fft = q_fft + self.params.f*eta_fft
+
+        self.state_phys['ux'] = ifft2(ux_fft)
+        self.state_phys['uy'] = ifft2(uy_fft)
+        self.state_phys['eta'] = ifft2(eta_fft)
+        self.state_phys['rot'] = ifft2(rot_fft)
+
+
+    def return_statephys_from_statefft(self, state_fft=None):
+        """Return the state in physical space."""
+        ifft2 = self.oper.ifft2
+        if state_fft is None:
+            state_fft = self.state_fft
+
+        q_fft = state_fft['q_fft']
+        ap_fft = state_fft['ap_fft']
+        am_fft = state_fft['am_fft']
+
+        (ux_fft, uy_fft, eta_fft
+         ) = self.oper.uxuyetafft_from_qapamfft(q_fft, ap_fft, am_fft)
+
+        rot_fft = q_fft + self.params.f*eta_fft
+
+        state_phys = SetOfVariables(like_this_sov=self.state_phys)
+        state_phys['ux'] = ifft2(ux_fft)
+        state_phys['uy'] = ifft2(uy_fft)
+        state_phys['eta'] = ifft2(eta_fft)
+        state_phys['rot'] = ifft2(rot_fft)
+        return state_phys
diff --git a/fluidsim/solvers/sw1l/forcing.py b/fluidsim/solvers/sw1l/forcing.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL2ZvcmNpbmcucHk=
--- /dev/null
+++ b/fluidsim/solvers/sw1l/forcing.py
@@ -0,0 +1,497 @@
+
+
+import numpy as np
+
+from fluiddyn.util import mpi
+
+from fluidsim.base.forcing import ForcingBasePseudoSpectral
+
+from fluidsim.base.forcing.specific import \
+    Proportional as ProportionalBase
+
+from fluidsim.base.forcing.specific import \
+    TimeCorrelatedRandomPseudoSpectral as TCRandomPS
+
+
+class ForcingSW1l(ForcingBasePseudoSpectral):
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        ForcingBasePseudoSpectral._complete_info_solver(info_solver)
+        classes = info_solver.classes.Forcing.classes
+
+        package = 'fluidsim.solvers.plate2d.forcing'
+
+        classes.set_child(
+            'Random',
+            attribs={'module_name': package,
+                     'class_name': 'Random'})
+
+        classes.set_child(
+            'Proportional',
+            attribs={'module_name': package,
+                     'class_name': 'Proportional'})
+
+
+class Random(TCRandomPS):
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        TCRandomPS._complete_params_with_default(params)
+        params.forcing.key_forced = 'q_fft'
+
+
+class Proportional(ProportionalBase):
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        params.forcing.key_forced = 'q_fft'
+
+
+class TimeCorrelatedRandomPseudoSpectralGauss(TCRandomPS):
+    def compute_forcingc_raw(self):
+        Fq_fft = super(TCRandomPS, self).compute_forcingc_raw()
+
+        return Fq_fft
+
+
+
+    
+    # def compute_forcing_proportional(self):
+    #     """Compute a forcing proportional to the flow."""
+    #     shapeK_loc_c = self.shapeK_loc_coarse
+    #     q_fft = self.qfftcoarse_from_setvarfft()
+
+    #     if mpi.rank > 0:
+    #         Fq_fft = np.empty(shapeK_loc_c,
+    #                           dtype=np.complex128)
+    #     else:
+    #         Fq_fft = self.normalize_forcingc_proportional(q_fft)
+    #         # self.verify_injection_rate_opfft(q_fft, Fq_fft,
+    #         #                                  self.oper_coarse)
+    #         self.fill_forcingc_from_Fqfft(Fq_fft)
+
+    #     self.put_forcingc_in_forcing()
+    #     ## verification
+    #     self.verify_injection_rate_from_state()
+
+
+
+    # def compute_forcing_2nd_degree_eq(self):
+    #     """compute a forcing normalized with a 2nd degree eq."""
+    #     shapeK_loc_c = self.shapeK_loc_coarse
+    #     q_fft = self.qfftcoarse_from_setvarfft()
+
+    #     if mpi.rank > 0:
+    #         Fq_fft = np.empty(shapeK_loc_c,
+    #                           dtype=np.complex128)
+    #     else:
+    #         Fq_fft = self.forcingc_raw_each_time()
+    #         Fq_fft = self.normalize_forcingc_2nd_degree_eq(Fq_fft,
+    #                                                        q_fft)
+    #         # self.verify_injection_rate_opfft(q_fft, Fq_fft,
+    #         #                                  self.oper_coarse)
+    #         self.fill_forcingc_from_Fqfft(Fq_fft)
+
+    #     self.put_forcingc_in_forcing()
+    #     ## verification
+    #     # self.verify_injection_rate_from_state()
+
+
+class OldStuff(object):
+
+    def compute_forcing_waves(self):
+        """compute a forcing normalized with a 2nd degree eq."""
+        shapeK_loc_c = self.shapeK_loc_coarse
+        a_fft, eta_fft = self.aetafftcoarse_from_setvarfft()
+        if mpi.rank > 0:
+            Fa_fft = np.empty(shapeK_loc_c,
+                              dtype=np.complex128)
+        else:
+            Fa_fft = self.forcingc_random()
+            self.modify_Ffft_from_eta(Fa_fft, eta_fft)
+
+            if np.max(abs(Fa_fft)) > 0:
+                self.normalize_Fafft_constPquadE(Fa_fft,
+                                                 a_fft)
+
+            self.fill_forcingc_from_Fafft(Fa_fft)
+
+        self.put_forcingc_in_forcing()
+
+
+
+
+
+
+
+
+
+    def compute_forcing_particular_k(self):
+        """compute a forcing "decorralated" from the flow"""
+
+        shapeK_loc_c = self.shapeK_loc_coarse
+        q_fft = self.qfftcoarse_from_setvarfft()
+
+        if mpi.rank > 0:
+            Fq_fft = np.empty(shapeK_loc_c,
+                              dtype=np.complex128)
+        else:
+            Fq_fft = self.forcingc_raw_each_time()
+            Fq_fft = self.normalize_forcingc_part_k(Fq_fft,
+                                                    q_fft)
+            # self.verify_injection_rate_opfft(q_fft, Fq_fft,
+            #                                  self.oper_coarse)
+            self.fill_forcingc_from_Fqfft(Fq_fft)
+
+        self.put_forcingc_in_forcing()
+        ## verification
+        self.verify_injection_rate_from_state()
+
+
+
+
+    def verify_injection_rate_opfft(self, q_fft, Fq_fft, oper):
+        """Verify injection rate."""
+        P_Z_forcing1 = abs(Fq_fft)**2/2*self.sim.time_stepping.deltat
+        P_Z_forcing2 = np.real(Fq_fft.conj()*q_fft)
+        P_Z_forcing1 = oper.sum_wavenumbers(P_Z_forcing1)
+        P_Z_forcing2 = oper.sum_wavenumbers(P_Z_forcing2)
+        if mpi.rank==0:
+            print 'P_Z_f = {0:9.4e} ; P_Z_f2 = {1:9.4e};'.format(
+                P_Z_forcing1+P_Z_forcing2,
+                P_Z_forcing2)
+
+
+
+
+
+
+
+
+
+
+    def verify_injection_rate_from_state(self):
+        """Verify injection rate."""
+
+        ux_fft = self.sim.state.state_fft['ux_fft']
+        uy_fft = self.sim.state.state_fft['uy_fft']
+        eta_fft = self.sim.state.state_fft['eta_fft']
+
+        q_fft, div_fft, ageo_fft = \
+            self.oper.qdafft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        Fux_fft = self.forcing_fft['ux_fft']
+        Fuy_fft = self.forcing_fft['uy_fft']
+        Feta_fft = self.forcing_fft['eta_fft']
+
+        Fq_fft, Fdiv_fft, Fageo_fft = \
+            self.oper.qdafft_from_uxuyetafft(Fux_fft, Fuy_fft, Feta_fft)
+        # print 'Fq_fft', abs(Fq_fft).max()
+        # print 'Fdiv_fft', abs(Fdiv_fft).max()
+        # print 'Fageo_fft', abs(Fageo_fft).max()
+
+        self.verify_injection_rate_opfft(q_fft, Fq_fft, self.oper)
+
+
+
+
+
+
+
+    def qfftcoarse_from_setvarfft(self, set_var_fft=None):
+        if set_var_fft is None:
+            set_var_fft = self.sim.state.state_fft
+        ux_fft = set_var_fft['ux_fft']
+        uy_fft = set_var_fft['uy_fft']
+        eta_fft = set_var_fft['eta_fft']
+        shapeK_loc_c = self.shapeK_loc_coarse
+        ux_fft = self.oper.coarse_seq_from_fft_loc(ux_fft, shapeK_loc_c)
+        uy_fft = self.oper.coarse_seq_from_fft_loc(uy_fft, shapeK_loc_c)
+        eta_fft = self.oper.coarse_seq_from_fft_loc(eta_fft, shapeK_loc_c)
+        if mpi.rank > 0:
+            q_fft = np.empty(shapeK_loc_c,
+                             dtype=np.complex128)
+        else:
+            rot_fft = self.oper_coarse.rotfft_from_vecfft(ux_fft, uy_fft)
+            q_fft = rot_fft-self.params.f*eta_fft
+        return q_fft
+
+
+
+
+
+    def etafftcoarse_from_setvarfft(self, set_var_fft=None):
+        if set_var_fft is None:
+            set_var_fft = self.sim.state.state_fft
+        eta_fft = set_var_fft['eta_fft']
+        shapeK_loc_c = self.shapeK_loc_coarse
+        eta_fft = self.oper.coarse_seq_from_fft_loc(eta_fft, shapeK_loc_c)
+        return eta_fft
+
+
+
+    def aetafftcoarse_from_setvarfft(self, set_var_fft=None):
+        if set_var_fft is None:
+            set_var_fft = self.sim.state.state_fft
+        eta_fft = set_var_fft['eta_fft']
+        ux_fft = set_var_fft['ux_fft']
+        uy_fft = set_var_fft['uy_fft']
+        shapeK_loc_c = self.shapeK_loc_coarse
+        eta_fft = self.oper.coarse_seq_from_fft_loc(eta_fft, shapeK_loc_c)
+        ux_fft = self.oper.coarse_seq_from_fft_loc(ux_fft, shapeK_loc_c)
+        uy_fft = self.oper.coarse_seq_from_fft_loc(uy_fft, shapeK_loc_c)
+
+        if mpi.rank > 0:
+            a_fft = np.empty(shapeK_loc_c,
+                             dtype=np.complex128)
+        else:
+            a_fft = self.oper_coarse.afft_from_uxuyetafft(ux_fft, uy_fft,
+                                                        eta_fft)
+
+        return a_fft, eta_fft
+
+
+    def modify_Ffft_from_eta(self, F_fft, eta_fft):
+        """Put to zero the forcing for the too large modes."""
+        for ik in self.ind_forcing:
+            if abs(eta_fft.flat[ik]) > self.eta_cond:
+                F_fft.flat[ik] = 0.
+
+
+
+
+    def fill_forcingc_from_Fqfft(self, Fq_fft):
+
+        Fux_fft, Fuy_fft, Feta_fft = \
+            self.oper_coarse.uxuyetafft_from_qfft(Fq_fft)
+        self.forcingc_fft['ux_fft'] = Fux_fft
+        self.forcingc_fft['uy_fft'] = Fuy_fft
+        self.forcingc_fft['eta_fft'] = Feta_fft
+
+
+    def fill_forcingc_from_Fetafft(self, Feta_fft):
+
+        self.forcingc_fft['ux_fft'] = \
+            self.oper_coarse.constant_arrayK(value=0.)
+        self.forcingc_fft['uy_fft'] = \
+            self.oper_coarse.constant_arrayK(value=0.)
+        self.forcingc_fft['eta_fft'] = Feta_fft
+
+
+    def fill_forcingc_from_Fafft(self, Fa_fft):
+
+        Fux_fft, Fuy_fft, Feta_fft = \
+            self.oper_coarse.uxuyetafft_from_afft(Fa_fft)
+        self.forcingc_fft['ux_fft'] = Fux_fft
+        self.forcingc_fft['uy_fft'] = Fuy_fft
+        self.forcingc_fft['eta_fft'] = Feta_fft
+
+
+
+
+
+    def get_FxFyFetafft(self):
+
+        Fx_fft = self.forcing_fft['ux_fft']
+        Fy_fft = self.forcing_fft['uy_fft']
+        Feta_fft = self.forcing_fft['eta_fft']
+        return Fx_fft, Fy_fft, Feta_fft
+
+
+
+    def normalize_Fafft_constPquadE(self, Fa_fft, a_fft):
+        """Normalize the forcing Fa_fft such as the forcing rate of
+        quadratic energy is equal to self.forcing_rate."""
+        oper_c = self.oper_coarse
+        params = self.params
+        deltat = self.sim.time_stepping.deltat
+
+        Fux_fft, Fuy_fft, Feta_fft = \
+            oper_c.uxuyetafft_from_afft(Fa_fft)
+        ux_fft, uy_fft, eta_fft = \
+            oper_c.uxuyetafft_from_afft(a_fft)
+
+        ax = deltat/2*oper_c.sum_wavenumbers(abs(Fux_fft)**2)
+        ay = deltat/2*oper_c.sum_wavenumbers(abs(Fuy_fft)**2)
+        aA = params.c2*deltat/2*oper_c.sum_wavenumbers(abs(Feta_fft)**2)
+        a = ax + ay + aA
+
+        bx = oper_c.sum_wavenumbers(
+            (ux_fft.conj()*Fux_fft).real)
+        by = oper_c.sum_wavenumbers(
+            (uy_fft.conj()*Fuy_fft).real)
+        bA = params.c2*oper_c.sum_wavenumbers(
+            (eta_fft.conj()*Feta_fft).real)
+        b = bx + by + bA
+
+        c = -self.forcing_rate
+
+        Delta = b**2 - 4*a*c
+        alpha = (np.sqrt(Delta) - b)/(2*a)
+
+        Fa_fft[:] = alpha*Fa_fft
+
+
+
+
+
+class ForcingSW1lExactLin(ForcingSW1l):
+
+
+    def verify_injection_rate_from_state(self):
+        """Verify injection rate."""
+
+        q_fft = self.sim.state.state_fft['q_fft']
+        Fq_fft = self.forcing_fft['q_fft']
+        # print 'Fq_fft', abs(Fq_fft).max()
+        self.verify_injection_rate_opfft(q_fft, Fq_fft, self.oper)
+
+    def qfftcoarse_from_setvarfft(self, set_var_fft=None):
+        if set_var_fft is None:
+            set_var_fft = self.sim.state.state_fft
+        q_fft = set_var_fft['q_fft']
+        shapeK_loc_c = self.shapeK_loc_coarse
+        q_fft = self.oper.coarse_seq_from_fft_loc(q_fft, shapeK_loc_c)
+        return q_fft
+
+    def fill_forcingc_from_Fqfft(self, Fq_fft):
+
+        self.forcingc_fft['q_fft'] = Fq_fft
+        self.forcingc_fft['ap_fft'] = \
+            self.oper_coarse.constant_arrayK(value=0.)
+        self.forcingc_fft['am_fft'] = \
+            self.oper_coarse.constant_arrayK(value=0.)
+
+
+    def get_FxFyFetafft(self):
+
+        Fq_fft = self.forcing_fft['q_fft']
+        Fp_fft = self.forcing_fft['ap_fft']
+        Fm_fft = self.forcing_fft['am_fft']
+
+        return self.oper.uxuyetafft_from_qapamfft(Fq_fft, Fp_fft, Fm_fft)
+
+
+
+
+
+    def etafftcoarse_from_setvarfft(self, set_var_fft=None):
+        if set_var_fft is None:
+            set_var_fft = self.sim.state.state_fft
+        q_fft = set_var_fft['q_fft']
+        ap_fft = set_var_fft['ap_fft']
+        am_fft = set_var_fft['am_fft']
+        a_fft = ap_fft + am_fft
+        eta_fft = (self.oper_coarse.etafft_from_qfft(q_fft)
+                   + self.oper_coarse.etafft_from_afft(a_fft)
+                   )
+        shapeK_loc_c = self.shapeK_loc_coarse
+        eta_fft = self.oper.coarse_seq_from_fft_loc(eta_fft, shapeK_loc_c)
+        return eta_fft
+
+
+
+    def fill_forcingc_from_Fafft(self, Fa_fft):
+
+        self.forcingc_fft['q_fft'] = \
+            self.oper_coarse.constant_arrayK(value=0.)
+        self.forcingc_fft['ap_fft'] = 0.5*Fa_fft
+        self.forcingc_fft['am_fft'] = 0.5*Fa_fft
+
+
+    def aetafftcoarse_from_setvarfft(self, set_var_fft=None):
+        if set_var_fft is None:
+            set_var_fft = self.sim.state.state_fft
+        q_fft = set_var_fft['q_fft']
+        ap_fft = set_var_fft['ap_fft']
+        am_fft = set_var_fft['am_fft']
+        shapeK_loc_c = self.shapeK_loc_coarse
+        q_fft = self.oper.coarse_seq_from_fft_loc(q_fft, shapeK_loc_c)
+        ap_fft = self.oper.coarse_seq_from_fft_loc(ap_fft, shapeK_loc_c)
+        am_fft = self.oper.coarse_seq_from_fft_loc(am_fft, shapeK_loc_c)
+
+        if mpi.rank > 0:
+            a_fft = np.empty(shapeK_loc_c,
+                             dtype=np.complex128)
+            eta_fft = np.empty(shapeK_loc_c,
+                               dtype=np.complex128)
+        else:
+            a_fft = ap_fft + am_fft
+            eta_fft = self.oper_coarse.etafft_from_aqfft(a_fft, q_fft)
+
+        return a_fft, eta_fft
+
+
+
+
+
+
+
+
+class ForcingSW1lWaves(ForcingSW1l):
+
+
+
+
+    def qfftcoarse_from_setvarfft(self, set_var_fft=None):
+        raise ValueError('This solver does not solve for q.')
+
+
+    def get_FxFyFetafft(self):
+
+        Fq_fft = self.oper.constant_arrayK(value=0)
+        Fp_fft = self.forcing_fft['ap_fft']
+        Fm_fft = self.forcing_fft['am_fft']
+
+        return self.oper.uxuyetafft_from_qapamfft(Fq_fft, Fp_fft, Fm_fft)
+
+
+
+
+
+    def etafftcoarse_from_setvarfft(self, set_var_fft=None):
+        if set_var_fft is None:
+            set_var_fft = self.sim.state.state_fft
+        ap_fft = set_var_fft['ap_fft']
+        am_fft = set_var_fft['am_fft']
+        a_fft = ap_fft + am_fft
+        eta_fft = self.oper_coarse.etafft_from_afft(a_fft)
+        shapeK_loc_c = self.shapeK_loc_coarse
+        eta_fft = self.oper.coarse_seq_from_fft_loc(eta_fft, shapeK_loc_c)
+        return eta_fft
+
+
+
+    def fill_forcingc_from_Fafft(self, Fa_fft):
+
+        self.forcingc_fft['ap_fft'] = 0.5*Fa_fft
+        self.forcingc_fft['am_fft'] = 0.5*Fa_fft
+
+
+    def aetafftcoarse_from_setvarfft(self, set_var_fft=None):
+        if set_var_fft is None:
+            set_var_fft = self.sim.state.state_fft
+        ap_fft = set_var_fft['ap_fft']
+        am_fft = set_var_fft['am_fft']
+        shapeK_loc_c = self.shapeK_loc_coarse
+        ap_fft = self.oper.coarse_seq_from_fft_loc(ap_fft, shapeK_loc_c)
+        am_fft = self.oper.coarse_seq_from_fft_loc(am_fft, shapeK_loc_c)
+
+        if mpi.rank > 0:
+            a_fft = np.empty(shapeK_loc_c,
+                             dtype=np.complex128)
+            eta_fft = np.empty(shapeK_loc_c,
+                               dtype=np.complex128)
+        else:
+            a_fft = ap_fft + am_fft
+            q_fft = self.oper_coarse.constant_arrayK(value=0)
+            eta_fft = self.oper_coarse.etafft_from_aqfft(a_fft, q_fft)
+
+        return a_fft, eta_fft
diff --git a/fluidsim/solvers/sw1l/init_fields.py b/fluidsim/solvers/sw1l/init_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL2luaXRfZmllbGRzLnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/init_fields.py
@@ -0,0 +1,213 @@
+
+"""InitFieldsSW1l"""
+
+import os
+
+from fluiddyn.util import mpi
+
+from fluidsim.base.init_fields import InitFieldsBase
+
+
+class InitFieldsSW1l(InitFieldsBase):
+    """Init the fields for the solver SW1l."""
+
+    implemented_flows = ['NOISE', 'CONSTANT', 'LOAD_FILE',
+                         'DIPOLE', 'JET', 'WAVE']
+
+    def __call__(self):
+        """Initialization initial fields"""
+        sim = self.sim
+
+        sim.time_stepping.t = 0.
+        sim.time_stepping.it = 0
+        oper = sim.oper
+
+        type_flow_init = self.get_and_check_type_flow_init()
+
+        if type_flow_init == 'DIPOLE':
+            rot_fft, ux_fft, uy_fft = self.init_fields_1dipole()
+            self.fill_state_from_uxuyfft(ux_fft, uy_fft)
+        elif type_flow_init == 'JET':
+            rot_fft, ux_fft, uy_fft = self.init_fields_jet()
+            self.fill_state_from_uxuyfft(ux_fft, uy_fft)
+        elif type_flow_init == 'NOISE':
+            rot_fft, ux_fft, uy_fft = self.init_fields_noise()
+            self.fill_state_from_uxuyfft(ux_fft, uy_fft)
+        elif type_flow_init == 'LOAD_FILE':
+            path_file = sim.params.init_fields.path_file
+            if not os.path.exists(path_file):
+                raise ValueError('file \"{0}\" not found'.format(path_file))
+            self.get_state_from_file(path_file)
+        elif type_flow_init == 'CONSTANT':
+            ux_fft = oper.constant_arrayK(value=1.)
+            uy_fft = oper.constant_arrayK(value=0.)
+            self.fill_state_from_uxuyfft(ux_fft, uy_fft)
+        elif type_flow_init == 'WAVE':
+            eta_fft, ux_fft, uy_fft = \
+                self.init_fields_wave()
+            self.fill_state_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+        else:
+            raise ValueError('bad value of params.init_fields.type_flow_init')
+
+    def fill_state_from_uxuyetafft(self, ux_fft, uy_fft, eta_fft):
+        sim = self.sim
+        state_fft = sim.state.state_fft
+        state_fft['ux_fft'] = ux_fft
+        state_fft['uy_fft'] = uy_fft
+        state_fft['eta_fft'] = eta_fft
+
+        sim.oper.dealiasing(state_fft)
+        sim.state.statephys_from_statefft()
+
+    def fill_state_from_uxuyfft(self, ux_fft, uy_fft):
+        sim = self.sim
+        oper = sim.oper
+        ifft2 = oper.ifft2
+
+        oper.projection_perp(ux_fft, uy_fft)
+        oper.dealiasing(ux_fft, uy_fft)
+
+        ux = ifft2(ux_fft)
+        uy = ifft2(uy_fft)
+
+        rot_fft = oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        rot = ifft2(rot_fft)
+
+        eta_fft = self.etafft_no_div(ux, uy, rot)
+        eta = ifft2(eta_fft)
+
+        state_fft = sim.state.state_fft
+        state_fft['ux_fft'] = ux_fft
+        state_fft['uy_fft'] = uy_fft
+        state_fft['eta_fft'] = eta_fft
+
+        state_phys = sim.state.state_phys
+        state_phys['rot'] = rot
+        state_phys['ux'] = ux
+        state_phys['uy'] = uy
+        state_phys['eta'] = eta
+
+    def etafft_no_div(self, ux, uy, rot):
+        K2_not0 = self.oper.K2_not0
+        rot_abs = rot + self.params.f
+
+        tempx_fft = -self.oper.fft2(rot_abs*uy)
+        tempy_fft = +self.oper.fft2(rot_abs*ux)
+
+        uu2_fft = self.oper.fft2(ux**2+uy**2)
+
+        eta_fft = (1.j * self.oper.KX*tempx_fft/K2_not0 +
+                   1.j*self.oper.KY*tempy_fft/K2_not0 -
+                   uu2_fft/2)/self.params.c2
+        if mpi.rank == 0:
+            eta_fft[0, 0] = 0.
+        self.oper.dealiasing(eta_fft)
+
+        return eta_fft
+
+
+class InitFieldsSW1lExLin(InitFieldsSW1l):
+    """Init the fields for the solver SW1lExLin."""
+
+    def fill_state_from_uxuyetafft(self, ux_fft, uy_fft, eta_fft):
+        sim = self.sim
+
+        (q_fft, ap_fft, am_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        # q_fft = self.oper.constant_arrayK(value=0)
+        # ap_fft = self.oper.constant_arrayK(value=0)
+        # am_fft = self.oper.constant_arrayK(value=0)
+
+        # am_fft[0,8] = 1.
+
+        state_fft = sim.state.state_fft
+        state_fft['q_fft'] = q_fft
+        state_fft['ap_fft'] = ap_fft
+        state_fft['am_fft'] = am_fft
+
+        sim.oper.dealiasing(state_fft)
+        sim.state.statephys_from_statefft()
+
+    def fill_state_from_uxuyfft(self, ux_fft, uy_fft):
+        sim = self.sim
+        oper = sim.oper
+        ifft2 = oper.ifft2
+
+        oper.projection_perp(ux_fft, uy_fft)
+        oper.dealiasing(ux_fft, uy_fft)
+
+        ux = ifft2(ux_fft)
+        uy = ifft2(uy_fft)
+
+        rot_fft = oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        rot = ifft2(rot_fft)
+
+        eta_fft = self.etafft_no_div(ux, uy, rot)
+        eta = ifft2(eta_fft)
+
+        (q_fft, ap_fft, am_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        state_fft = sim.state.state_fft
+        state_fft['q_fft'] = q_fft
+        state_fft['ap_fft'] = ap_fft
+        state_fft['am_fft'] = am_fft
+
+        state_phys = sim.state.state_phys
+        state_phys['rot'] = rot
+        state_phys['ux'] = ux
+        state_phys['uy'] = uy
+        state_phys['eta'] = eta
+
+
+class InitFieldsSW1lWaves(InitFieldsSW1l):
+    """Init """
+
+    def fill_state_from_uxuyetafft(self, ux_fft, uy_fft, eta_fft):
+        sim = self.sim
+
+        (q_fft, ap_fft, am_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        # q_fft = self.oper.constant_arrayK(value=0)
+        # ap_fft = self.oper.constant_arrayK(value=0)
+        # am_fft = self.oper.constant_arrayK(value=0)
+
+        # am_fft[0,8] = 1.
+
+        state_fft = sim.state.state_fft
+        state_fft['ap_fft'] = ap_fft
+        state_fft['am_fft'] = am_fft
+
+        sim.oper.dealiasing(state_fft)
+        sim.state.statephys_from_statefft()
+
+    def fill_state_from_uxuyfft(self, ux_fft, uy_fft):
+        sim = self.sim
+        oper = sim.oper
+        ifft2 = oper.ifft2
+
+        oper.projection_perp(ux_fft, uy_fft)
+        oper.dealiasing(ux_fft, uy_fft)
+
+        ux = ifft2(ux_fft)
+        uy = ifft2(uy_fft)
+
+        rot_fft = oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        rot = ifft2(rot_fft)
+
+        eta_fft = self.etafft_no_div(ux, uy, rot)
+        eta = ifft2(eta_fft)
+
+        (q_fft, ap_fft, am_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        state_fft = sim.state.state_fft
+        state_fft['ap_fft'] = ap_fft
+        state_fft['am_fft'] = am_fft
+
+        state_phys = sim.state.state_phys
+        state_phys['ux'] = ux
+        state_phys['uy'] = uy
+        state_phys['eta'] = eta
diff --git a/fluidsim/solvers/sw1l/modified/__init__.py b/fluidsim/solvers/sw1l/modified/__init__.py
new file mode 100644
diff --git a/fluidsim/solvers/sw1l/modified/init_fields.py b/fluidsim/solvers/sw1l/modified/init_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL21vZGlmaWVkL2luaXRfZmllbGRzLnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/modified/init_fields.py
@@ -0,0 +1,39 @@
+
+
+from fluidsim.solvers.sw1l.init_fields import InitFieldsSW1l
+
+
+class InitFieldsSW1lModified(InitFieldsSW1l):
+    """Init """
+
+
+    def fill_state_from_uxuyfft(self, ux_fft, uy_fft):
+        sim = self.sim
+        oper = sim.oper
+        ifft2 = oper.ifft2
+
+        oper.projection_perp(ux_fft, uy_fft)
+        oper.dealiasing(ux_fft, uy_fft)
+
+        ux = ifft2(ux_fft)
+        uy = ifft2(uy_fft)
+
+        rot_fft = oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        rot = ifft2(rot_fft)
+
+        eta_fft = self.etafft_no_div(ux, uy, rot)
+        eta = ifft2(eta_fft)
+
+        state_fft = sim.state.state_fft
+        state_fft['ux_fft'] = ux_fft
+        state_fft['uy_fft'] = uy_fft
+        state_fft['eta_fft'] = eta_fft
+
+        state_phys = sim.state.state_phys
+        state_phys['ux'] = ux
+        state_phys['uy'] = uy
+        state_phys['eta'] = eta
+
+
+
+
diff --git a/fluidsim/solvers/sw1l/modified/output.py b/fluidsim/solvers/sw1l/modified/output.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL21vZGlmaWVkL291dHB1dC5weQ==
--- /dev/null
+++ b/fluidsim/solvers/sw1l/modified/output.py
@@ -0,0 +1,53 @@
+""" """
+
+import numpy as np
+
+from fluidsim.solvers.sw1l.output.output_base import OutputBaseSW1l
+
+
+class OutputSW1lModified(OutputBaseSW1l):
+    """subclass :class:`OutputSW1l`"""
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        OutputBaseSW1l._complete_info_solver(info_solver)
+
+        classes = info_solver.classes.Output.classes
+
+        classes.SpatialMeans.class_name = 'SpatialMeansMSW1l'
+        classes.SpectralEnergyBudget.class_name = 'SpectralEnergyBudgetMSW1l'
+
+    def compute_energies_fft(self):
+        ux_fft = self.sim.state.state_fft['ux_fft']
+        uy_fft = self.sim.state.state_fft['uy_fft']
+        eta_fft = self.sim.state.state_fft['eta_fft']
+        energyA_fft = self.sim.params.c2 * np.abs(eta_fft)**2/2
+        energyK_fft = np.abs(ux_fft)**2/2 + np.abs(uy_fft)**2/2
+        rot_fft = self.rotfft_from_vecfft(ux_fft, uy_fft)
+        uxr_fft, uyr_fft = self.vecfft_from_rotfft(rot_fft)
+        energyKr_fft = np.abs(uxr_fft)**2/2 + np.abs(uyr_fft)**2/2
+        return energyK_fft, energyA_fft, energyKr_fft
+
+    def compute_energiesKA_fft(self):
+        ux_fft = self.sim.state.state_fft['ux_fft']
+        uy_fft = self.sim.state.state_fft['uy_fft']
+        eta_fft = self.sim.state.state_fft['eta_fft']
+        energyA_fft = self.sim.params.c2 * np.abs(eta_fft)**2/2
+        energyK_fft = np.abs(ux_fft)**2/2 + np.abs(uy_fft)**2/2
+        return energyK_fft, energyA_fft
+
+    def compute_PV_fft(self):
+        # compute Ertel and Charney (QG) potential vorticity
+        rot = self.sim.state('rot')
+        eta = self.sim.state.state_phys['eta']
+        ErtelPV_fft = self.fft2((self.sim.params.f+rot)/(1.+eta))
+        ux_fft = self.sim.state.state_fft['ux_fft']
+        uy_fft = self.sim.state.state_fft['uy_fft']
+        rot_fft = self.rotfft_from_vecfft(ux_fft, uy_fft)
+        eta_fft = self.sim.state.state_fft['eta_fft']
+        CharneyPV_fft = rot_fft - self.sim.params.f*eta_fft
+        return ErtelPV_fft, CharneyPV_fft
diff --git a/fluidsim/solvers/sw1l/modified/solver.py b/fluidsim/solvers/sw1l/modified/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL21vZGlmaWVkL3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/sw1l/modified/solver.py
@@ -0,0 +1,191 @@
+"""Modified SW1l equations
+==========================
+
+(:mod:`fluidsim.solvers.sw1l.modified.solver`)
+
+This class is a solver of a modified version of the 1 layer shallow
+water (Saint Venant) equations for which the advection is only
+due to the rotational velocity.
+"""
+
+from __future__ import division, print_function
+
+import numpy as np
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.solvers.sw1l.solver import InfoSolverSW1l
+from fluidsim.solvers.sw1l.solver import Simul as SimulSW1l
+
+
+class InfoSolverSW1lModified(InfoSolverSW1l):
+    """Information about the solver SW1l."""
+    def __init__(self, **kargs):
+        super(InfoSolverSW1lModified, self).__init__(**kargs)
+
+        if 'tag' in kargs and kargs['tag'] == 'solver':
+
+            sw1l = 'fluidsim.solvers.sw1l'
+
+            self.module_name = sw1l+'.modified.solver'
+            self.class_name = 'Simul'
+            self.short_name = 'SW1lmodif'
+
+            classes = self.classes
+
+            classes.State.module_name = sw1l+'.modified.state'
+            classes.State.class_name = 'StateSW1lModified'
+
+            classes.InitFields.module_name = sw1l+'.modified.init_fields'
+            classes.InitFields.class_name = 'InitFieldsSW1lModified'
+
+            classes.Output.module_name = sw1l+'.modified.output'
+            classes.Output.class_name = 'OutputSW1lModified'
+
+
+info_solver = InfoSolverSW1lModified(tag='solver')
+info_solver.complete_with_classes()
+
+
+class Simul(SimulSW1l):
+    """A solver of the shallow-water 1 layer equations (SW1l)"""
+
+    def __init__(self, params, info_solver=info_solver):
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_fft=None):
+        oper = self.oper
+        fft2 = oper.fft2
+        ifft2 = oper.ifft2
+
+        if state_fft is None:
+            state_phys = self.state.state_phys
+            state_fft = self.state.state_fft
+        else:
+            state_phys = self.state.return_statephys_from_statefft(state_fft)
+
+        ux = state_phys['ux']
+        uy = state_phys['uy']
+        # eta = state_phys['eta']
+
+        ux_fft = state_fft['ux_fft']
+        uy_fft = state_fft['uy_fft']
+        eta_fft = state_fft['eta_fft']
+
+        # compute Fx_fft and Fy_fft
+        rot_fft = oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        ux_rot_fft, uy_rot_fft = oper.vecfft_from_rotfft(rot_fft)
+        ux_rot = ifft2(ux_rot_fft)
+        uy_rot = ifft2(uy_rot_fft)
+
+        dxux_fft, dyux_fft = oper.gradfft_from_fft(ux_fft)
+        dxux = ifft2(dxux_fft)
+        dyux = ifft2(dyux_fft)
+        dxuy_fft, dyuy_fft = oper.gradfft_from_fft(uy_fft)
+        dxuy = ifft2(dxuy_fft)
+        dyuy = ifft2(dyuy_fft)
+
+        FNLx = -ux_rot*dxux - uy_rot*dyux
+        FNLy = -ux_rot*dxuy - uy_rot*dyuy
+
+        FCx = +self.params.f*uy
+        FCy = -self.params.f*ux
+
+        Fgradx_fft, Fgrady_fft = oper.gradfft_from_fft(self.params.c2*eta_fft)
+
+        Fx_fft = fft2(FCx+FNLx) - Fgradx_fft
+        Fy_fft = fft2(FCy+FNLy) - Fgrady_fft
+
+        # compute Feta_fft
+        dxeta_fft, dyeta_fft = oper.gradfft_from_fft(eta_fft)
+        dxeta = ifft2(dxeta_fft)
+        dyeta = ifft2(dyeta_fft)
+
+        div_fft = oper.divfft_from_vecfft(ux_fft, uy_fft)
+        Feta_fft = -fft2(ux_rot*dxeta + uy_rot*dyeta) - div_fft
+
+        oper.dealiasing(Fx_fft, Fy_fft, Feta_fft)
+
+        # # for verification conservation energy
+        # T_ux = (ux_fft.conj()*Fx_fft).real
+        # T_uy = (uy_fft.conj()*Fy_fft).real
+        # T_eta = (eta_fft.conj()*Feta_fft).real
+        # T_tot = T_ux + T_uy + T_eta
+        # print 'sum(T_tot) = {0:9.4e} ; sum(abs(T_tot)) = {1:9.4e}'.format(
+        #     self.oper.sum_wavenumbers(T_tot),
+        #     self.oper.sum_wavenumbers(abs(T_tot)))
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+
+        tendencies_fft['ux_fft'] = Fx_fft
+        tendencies_fft['uy_fft'] = Fy_fft
+        tendencies_fft['eta_fft'] = Feta_fft
+
+        if self.params.FORCING:
+            tendencies_fft += self.forcing.get_forcing()
+
+        return tendencies_fft
+
+
+
+
+
+
+
+
+
+
+
+
+if __name__=="__main__":
+
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 64
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 2.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+
+    params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 1.
+    params.output.periods_save.spectra = 0.5
+    params.output.periods_save.spect_energy_budg = 0.5
+    params.output.periods_save.increments = 0.5
+    params.output.periods_save.pdf = 0.5
+    params.output.periods_save.time_signals_fft = False
+
+    params.output.periods_plot.phys_fields = 0.
+
+    params.output.phys_fields.field_to_plot = 'div'
+
+    params.output.spectra.has_to_plot = False
+    params.output.spatial_means.has_to_plot = False
+    params.output.spect_energy_budg.has_to_plot = False
+    params.output.increments.has_to_plot = False
+    params.output.pdf.has_to_plot = False
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    # sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/sw1l/modified/state.py b/fluidsim/solvers/sw1l/modified/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL21vZGlmaWVkL3N0YXRlLnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/modified/state.py
@@ -0,0 +1,88 @@
+"""State class for the sw1l.modified solver
+(:mod:`fluidsim.solvers.sw1l.modified.state`)
+===================================================
+
+.. currentmodule:: fluidsim.solvers.sw1l.modified.state
+
+Provides:
+
+.. autoclass:: StateSW1lModified
+   :members:
+   :private-members:
+
+"""
+
+from fluidsim.base.state import StatePseudoSpectral
+
+from fluiddyn.util import mpi
+
+
+class StateSW1lModified(StatePseudoSpectral):
+    """
+    The class :class:`StateMSW1l` contains the variables corresponding
+    to the state and handles the access to other fields for the solver
+    MSW1l.
+    """
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.State.set_attribs({
+            'keys_state_fft': ['ux_fft', 'uy_fft', 'eta_fft'],
+            'keys_state_phys': ['ux', 'uy', 'eta'],
+            'keys_computable': [],
+            'keys_phys_needed': ['ux', 'uy', 'eta'],
+            'keys_linear_eigenmodes': ['q_fft', 'a_fft', 'd_fft']})
+
+
+    def compute(self, key, SAVE_IN_DICT=True, RAISE_ERROR=True):
+        it = self.sim.time_stepping.it
+
+        if (key in self.vars_computed and it == self.it_computed[key]):
+            return self.vars_computed[key]
+
+        if key == 'ux_fft':
+            result = self.oper.fft2(self.state_phys['ux'])
+        elif key == 'uy_fft':
+            result = self.oper.fft2(self.state_phys['ux'])
+        elif key == 'rot_fft':
+            ux_fft = self.compute('ux_fft')
+            uy_fft = self.compute('uy_fft')
+            result = self.oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        elif key == 'div_fft':
+            ux_fft = self.compute('ux_fft')
+            uy_fft = self.compute('uy_fft')
+            result = self.oper.divfft_from_vecfft(ux_fft, uy_fft)
+        elif key == 'rot':
+            rot_fft = self.compute('rot_fft')
+            result = self.oper.ifft2(rot_fft)
+        elif key == 'div':
+            div_fft = self.compute('div_fft')
+            result = self.oper.ifft2(div_fft)
+        elif key == 'q':
+            rot = self.compute('rot')
+            eta = self.sim.vars.state_phys['eta']
+            result = rot-self.f*eta
+        else:
+            to_print = 'Do not know how to compute "'+key+'".'
+            if RAISE_ERROR:
+                raise ValueError(to_print)
+            else:
+                if mpi.rank == 0:
+                    print(to_print
+                          +'\nreturn an array of zeros.')
+                    
+                result = self.oper.constant_arrayX(value=0.)
+
+        if SAVE_IN_DICT:
+            self.vars_computed[key] = result
+            self.it_computed[key] = it
+
+        return result
+
+
+
+
+
diff --git a/fluidsim/solvers/sw1l/onlywaves/__init__.py b/fluidsim/solvers/sw1l/onlywaves/__init__.py
new file mode 100644
diff --git a/fluidsim/solvers/sw1l/onlywaves/solver.py b/fluidsim/solvers/sw1l/onlywaves/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL29ubHl3YXZlcy9zb2x2ZXIucHk=
--- /dev/null
+++ b/fluidsim/solvers/sw1l/onlywaves/solver.py
@@ -0,0 +1,225 @@
+"""SW1l equations solving exactly the linear terms
+==================================================
+
+(:mod:`fluidsim.solvers.sw1l.onlywaves.solver`)
+
+This class is a solver of the 1 layer shallow water (Saint Venant)
+equations with zeros QG PV.
+"""
+
+from __future__ import division, print_function
+
+import numpy as np
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.solvers.sw1l.exactlin.solver import InfoSolverSW1lExactLin
+from fluidsim.solvers.sw1l.exactlin.solver import \
+    Simul as SimulSW1lExactLin
+
+
+from fluiddyn.util import mpi
+
+
+class InfoSolverSW1lWaves(InfoSolverSW1lExactLin):
+    """Information about the solver SW1l."""
+    def __init__(self, **kargs):
+        super(InfoSolverSW1lWaves, self).__init__(**kargs)
+
+        if 'tag' in kargs and kargs['tag'] == 'solver':
+
+            sw1l = 'fluidsim.solvers.sw1l'
+
+            self.module_name = sw1l+'.onlywaves.solver'
+            self.short_name = 'SW1lwaves'
+
+            classes = self.classes
+
+            classes.State.module_name = sw1l+'.onlywaves.state'
+            classes.State.class_name = 'StateSW1lWaves'
+
+            classes.InitFields.class_name = 'InitFieldsSW1lWaves'
+
+            classes.Forcing.class_name = 'ForcingSW1lWaves'
+
+
+info_solver = InfoSolverSW1lWaves(tag='solver')
+info_solver.complete_with_classes()
+
+
+class Simul(SimulSW1lExactLin):
+    """A solver of the shallow-water 1 layer equations (SW1l)"""
+
+    def __init__(self, params, info_solver=info_solver):
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_fft=None):
+        oper = self.oper
+        fft2 = oper.fft2
+
+        if state_fft is None:
+            state_phys = self.state.state_phys
+            state_fft = self.state.state_fft
+        else:
+            state_phys = self.state.return_statephys_from_statefft(state_fft)
+
+        ux = state_phys['ux']
+        uy = state_phys['uy']
+        eta = state_phys['eta']
+
+        # compute the nonlinear terms for ux, uy and eta
+        gradu2_x_fft, gradu2_y_fft = oper.gradfft_from_fft(
+            fft2(ux**2+uy**2)/2)
+
+        Nx_fft = - gradu2_x_fft
+        Ny_fft = - gradu2_y_fft
+
+        if self.params.f > 0:
+            # this is not very efficient, but this is simple...
+            rot = self.state('rot')
+            N1x = +rot*uy
+            N1y = -rot*ux
+
+            Nx_fft += fft2(N1x)
+            Ny_fft += fft2(N1y)
+
+        jx_fft = fft2(eta*ux)
+        jy_fft = fft2(eta*uy)
+        Neta_fft = -oper.divfft_from_vecfft(jx_fft, jy_fft)
+
+        # self.verify_tendencies(state_fft, state_phys,
+        #                        Nx_fft, Ny_fft, Neta_fft)
+
+        # compute the nonlinear terms for q, ap and am
+        (Nq_fft, Np_fft, Nm_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(Nx_fft, Ny_fft, Neta_fft)
+
+        # Np_fft = self.oper.constant_arrayK(value=0)
+        # Nm_fft = self.oper.constant_arrayK(value=0)
+
+        oper.dealiasing(Np_fft, Nm_fft)
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+        tendencies_fft['ap_fft'] = Np_fft
+        tendencies_fft['am_fft'] = Nm_fft
+
+        if self.params.FORCING:
+            tendencies_fft += self.forcing.get_forcing()
+
+        return tendencies_fft
+
+    def compute_freq_complex(self, key):
+        K2 = self.oper.K2
+        # return self.oper.constant_arrayK(value=0)
+        if key == 'ap_fft':
+            omega = 1.j*np.sqrt(self.params.f**2 + self.params.c2*K2)
+        elif key == 'am_fft':
+            omega = -1.j*np.sqrt(self.params.f**2 + self.params.c2*K2)
+        return omega
+
+    def verify_tendencies(self, state_fft, state_phys,
+                          Nx_fft, Ny_fft, Neta_fft):
+        # for verification conservation energy
+        # compute the linear terms
+        oper = self.oper
+        ux = state_phys['ux']
+        uy = state_phys['uy']
+        eta = state_phys['eta']
+
+        # q_fft = self.oper.constant_arrayK(value=0)
+        ap_fft = state_fft['ap_fft']
+        am_fft = state_fft['am_fft']
+        a_fft = ap_fft + am_fft
+        div_fft = self.divfft_from_apamfft(ap_fft, am_fft)
+
+        eta_fft = oper.etafft_from_afft(a_fft)
+
+        dx_c2eta_fft, dy_c2eta_fft = oper.gradfft_from_fft(
+            self.params.c2*eta_fft)
+        LCx = self.params.f*uy
+        LCy = -self.params.f*ux
+        Lx_fft = oper.fft2(LCx) - dx_c2eta_fft
+        Ly_fft = oper.fft2(LCy) - dy_c2eta_fft
+        Leta_fft = -div_fft
+
+        # compute the full tendencies
+        Fx_fft = Lx_fft + Nx_fft
+        Fy_fft = Ly_fft + Ny_fft
+        Feta_fft = Leta_fft + Neta_fft
+        oper.dealiasing(Fx_fft, Fy_fft, Feta_fft)
+
+        # test : ux, uy, eta ---> q, ap, am
+        (Fq_fft, Fp_fft, Fm_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(Fx_fft, Fy_fft, Feta_fft)
+        # test : q, ap, am ---> ux, uy, eta
+        (Fx2_fft, Fy2_fft, Feta2_fft
+         ) = self.oper.uxuyetafft_from_qapamfft(Fq_fft, Fp_fft, Fm_fft)
+        print(np.max(abs(Fx2_fft - Fx_fft)))
+        print(np.max(abs(Fy2_fft - Fy_fft)))
+        print(np.max(abs(Feta2_fft - Feta_fft)))
+        Fx_fft = Fx2_fft
+        Fy_fft = Fy2_fft
+        Feta_fft = Feta2_fft
+
+        (Fq2_fft, Fp2_fft, Fm2_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(
+            Fx2_fft, Fy2_fft, Feta2_fft)
+        print(np.max(abs(Fq2_fft - Fq_fft)))
+        print(np.max(abs(Fp2_fft - Fp_fft)))
+        print(np.max(abs(Fm2_fft - Fm_fft)))
+
+        Fx = oper.ifft2(Fx_fft)
+        Fy = oper.ifft2(Fy_fft)
+        Feta = oper.ifft2(Feta_fft)
+        A = (Feta*(ux**2+uy**2)/2
+             + (1+eta)*(ux*Fx+uy*Fy)
+             + self.params.c2*eta*Feta)
+        A_fft = oper.fft2(A)
+        if mpi.rank == 0:
+            print('should be zero =', A_fft[0, 0])
+
+
+if __name__ == "__main__":
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 64
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 2.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 1.
+    params.output.periods_save.spectra = 0.5
+    params.output.periods_save.spect_energy_budg = 0.5
+    params.output.periods_save.increments = 0.5
+    params.output.periods_save.pdf = 0.5
+    params.output.periods_save.time_signals_fft = False
+
+    params.output.periods_plot.phys_fields = 0.
+
+    params.output.phys_fields.field_to_plot = 'div'
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    # sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/sw1l/onlywaves/state.py b/fluidsim/solvers/sw1l/onlywaves/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL29ubHl3YXZlcy9zdGF0ZS5weQ==
--- /dev/null
+++ b/fluidsim/solvers/sw1l/onlywaves/state.py
@@ -0,0 +1,194 @@
+"""State class for the SW1l.onlywaves solver
+(:mod:`fluidsim.solvers.sw1l.onlywaves.state`)
+====================================================
+
+.. currentmodule:: fluidsim.solvers.sw1l.onlywaves.state
+
+Provides:
+
+.. autoclass:: StateSW1lWaves
+   :members:
+   :private-members:
+
+"""
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.solvers.sw1l.state import StateSW1l
+
+from fluiddyn.util import mpi
+
+
+
+class StateSW1lWaves(StateSW1l):
+    """
+    The class :class:`StateSW1lwaves` contains the variables corresponding
+    to the state and handles the access to other fields for the solver
+    SW1l.
+    """
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.State.set_attribs({
+            'keys_state_fft': ['ap_fft', 'am_fft'],
+            'keys_state_phys': ['ux', 'uy', 'eta'],
+            'keys_computable': [],
+            'keys_phys_needed': ['ux', 'uy', 'eta'],
+            'keys_linear_eigenmodes': ['q_fft', 'a_fft', 'd_fft']})
+
+
+
+
+    def compute(self, key, SAVE_IN_DICT=True, RAISE_ERROR=True):
+        it = self.sim.time_stepping.it
+        if (key in self.vars_computed and it == self.it_computed[key]):
+            return self.vars_computed[key]
+
+        if key == 'div_fft':
+            ap_fft = self.state_fft['ap_fft']
+            am_fft = self.state_fft['am_fft']
+            d_fft = self.oper.divfft_from_apamfft(ap_fft, am_fft)
+            result = d_fft
+
+        elif key == 'a_fft':
+            ap_fft = self.state_fft['ap_fft']
+            am_fft = self.state_fft['am_fft']
+            result = ap_fft + am_fft
+
+        elif key == 'rot_fft':
+            a_fft = self.compute('a_fft')
+            result = self.oper.rotfft_from_afft(a_fft)
+
+        elif key == 'eta_fft':
+            a_fft = self.compute('a_fft')
+            result = self.oper.etafft_from_afft(a_fft)
+
+
+        elif key == 'ux_fft':
+            rot_fft = self.compute('rot_fft')
+            div_fft = self.compute('div_fft')
+            urx_fft, ury_fft = self.oper.vecfft_from_rotfft(rot_fft)
+            udx_fft, udy_fft = self.oper.vecfft_from_divfft(div_fft)
+            ux_fft = urx_fft + udx_fft
+            if mpi.rank == 0:
+                ap_fft = self.state_fft['ap_fft']
+                ux_fft[0, 0] = ap_fft[0, 0]
+            result = ux_fft
+            if SAVE_IN_DICT:
+                key2 = 'uy_fft'
+                uy_fft = ury_fft + udy_fft
+                if mpi.rank == 0:
+                    am_fft = self.state_fft['am_fft']
+                    uy_fft[0, 0] = am_fft[0, 0]
+
+                self.vars_computed[key2] = uy_fft
+                self.it_computed[key2] = it
+
+        elif key == 'uy_fft':
+            rot_fft = self.compute('rot_fft')
+            div_fft = self.compute('div_fft')
+            urx_fft, ury_fft = self.oper.vecfft_from_rotfft(rot_fft)
+            udx_fft, udy_fft = self.oper.vecfft_from_divfft(div_fft)
+            uy_fft = ury_fft + udy_fft
+            if mpi.rank == 0:
+                am_fft = self.state_fft['am_fft']
+                uy_fft[0, 0] = am_fft[0, 0]
+            result = uy_fft
+            if SAVE_IN_DICT:
+                key2 = 'ux_fft'
+                ux_fft = urx_fft + udx_fft
+                if mpi.rank == 0:
+                    ap_fft = self.state_fft['ap_fft']
+                    ux_fft[0, 0] = ap_fft[0, 0]
+                self.vars_computed[key2] = ux_fft
+                self.it_computed[key2] = it
+
+        elif key == 'rot':
+            rot_fft = self.compute('rot_fft')
+            result = self.oper.ifft2(rot_fft)
+
+        elif key == 'q':
+            result = self.oper.constant_arrayX(value=0)
+
+        else:
+            result = super(StateSW1lWaves, self).compute(
+                key, SAVE_IN_DICT=SAVE_IN_DICT,
+                RAISE_ERROR=RAISE_ERROR)
+            SAVE_IN_DICT = False
+
+            # to_print = 'Do not know how to compute "'+key+'".'
+            # if RAISE_ERROR:
+            #     raise ValueError(to_print)
+            # else:
+            #     if mpi.rank == 0:
+            #         print(to_print
+            #               +'\nreturn an array of zeros.')
+            #     result = self.oper.constant_arrayX(value=0.)
+
+        if SAVE_IN_DICT:
+            self.vars_computed[key] = result
+            self.it_computed[key] = it
+
+        return result
+
+
+
+
+
+    def statefft_from_statephys(self):
+        """Compute the state in Fourier space."""
+        ux = self.state_phys['ux']
+        uy = self.state_phys['uy']
+        eta = self.state_phys['eta']
+
+        eta_fft = self.oper.fft2(eta)
+        ux_fft = self.oper.fft2(ux)
+        uy_fft = self.oper.fft2(uy)
+
+        (q_fft, ap_fft, am_fft
+         ) = self.oper.qapamfft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        self.state_fft['ap_fft'] = ap_fft
+        self.state_fft['am_fft'] = am_fft
+
+
+
+    def statephys_from_statefft(self):
+        """Compute the state in physical space."""
+        ifft2 = self.oper.ifft2
+        q_fft = self.oper.constant_arrayK(value=0)
+        ap_fft = self.state_fft['ap_fft']
+        am_fft = self.state_fft['am_fft']
+
+        (ux_fft, uy_fft, eta_fft
+         ) = self.oper.uxuyetafft_from_qapamfft(q_fft, ap_fft, am_fft)
+
+        self.state_phys['ux'] = ifft2(ux_fft)
+        self.state_phys['uy'] = ifft2(uy_fft)
+        self.state_phys['eta'] = ifft2(eta_fft)
+
+
+
+    def return_statephys_from_statefft(self, state_fft=None):
+        """Return the state in physical space."""
+        ifft2 = self.oper.ifft2
+        if state_fft is None:
+            state_fft = self.state_fft
+
+        q_fft = self.oper.constant_arrayK(value=0)
+        ap_fft = state_fft['ap_fft']
+        am_fft = state_fft['am_fft']
+
+        (ux_fft, uy_fft, eta_fft
+         ) = self.oper.uxuyetafft_from_qapamfft(q_fft, ap_fft, am_fft)
+
+        state_phys = SetOfVariables(like_this_sov=self.state_phys)
+        state_phys['ux'] = ifft2(ux_fft)
+        state_phys['uy'] = ifft2(uy_fft)
+        state_phys['eta'] = ifft2(eta_fft)
+        return state_phys
+
diff --git a/fluidsim/solvers/sw1l/output/__init__.py b/fluidsim/solvers/sw1l/output/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL291dHB1dC9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/fluidsim/solvers/sw1l/output/__init__.py
@@ -0,0 +1,184 @@
+""" """
+
+import numpy as np
+
+from fluidsim.base.output import OutputBasePseudoSpectral
+
+
+class OutputBaseSW1l(OutputBasePseudoSpectral):
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.Output.set_child('classes')
+        classes = info_solver.classes.Output.classes
+
+        package = 'fluidsim.solvers.sw1l.output'
+
+        classes.set_child(
+            'PrintStdOut',
+            attribs={'module_name': package + '.print_stdout',
+                     'class_name': 'PrintStdOutSW1l'})
+
+        classes.set_child(
+            'PhysFields',
+            attribs={'module_name': 'fluidsim.base.output.phys_fields',
+                     'class_name': 'PhysFieldsBase'})
+
+        classes.set_child(
+            'Spectra',
+            attribs={'module_name': package + '.spectra',
+                     'class_name': 'SpectraSW1l'})
+
+        classes.set_child(
+            'SpatialMeans',
+            attribs={'module_name': package + '.spatial_means',
+                     'class_name': 'SpatialMeansSW1l'})
+
+        attribs = {
+            'module_name': package + '.spect_energy_budget',
+            'class_name': 'SpectralEnergyBudgetSW1l'}
+        classes.set_child('SpectralEnergyBudget', attribs=attribs)
+
+        attribs = {
+            'module_name': 'fluidsim.base.output.increments',
+            'class_name': 'IncrementsSW1l'}
+        classes.set_child('Increments', attribs=attribs)
+
+        attribs = {
+            'module_name': 'fluidsim.base.output.prob_dens_func',
+            'class_name': 'ProbaDensityFunc'}
+        classes.set_child('ProbaDensityFunc', attribs=attribs)
+
+        attribs = {
+            'module_name': 'fluidsim.base.output.time_signalsK',
+            'class_name': 'TimeSignalsK'}
+        classes.set_child('TimeSignalsK', attribs=attribs)
+
+    @staticmethod
+    def _complete_params_with_default(params, info_solver):
+        """This static method is used to complete the *params* container.
+        """
+        OutputBasePseudoSpectral._complete_params_with_default(
+            params, info_solver)
+
+        params.output.phys_fields.field_to_plot = 'rot'
+
+    def linear_eigenmode_from_values_1k(self, ux_fft, uy_fft, eta_fft,
+                                        kx, ky):
+        div_fft = 1j*(kx*ux_fft + ky*uy_fft)
+        rot_fft = 1j*(kx*uy_fft - ky*ux_fft)
+        q_fft = rot_fft - self.sim.params.f*eta_fft
+        k2 = kx**2+ky**2
+        ageo_fft = self.sim.params.f*rot_fft/self.sim.params.c2 + k2*eta_fft
+        return q_fft, div_fft, ageo_fft
+
+    def omega_from_wavenumber(self, k):
+        return np.sqrt(self.sim.params.f**2 + self.sim.params.c2*k**2)
+
+    def compute_enstrophy_fft(self):
+        rot_fft = self.sim.state('rot_fft')
+        return np.abs(rot_fft)**2/2
+
+    def compute_PV_fft(self):
+        """Compute Ertel and Charney (QG) potential vorticity."""
+        rot = self.sim.state('rot')
+        eta = self.sim.state.state_phys['eta']
+        ErtelPV_fft = self.fft2((self.sim.params.f+rot)/(1.+eta))
+        rot_fft = self.sim.state('rot_fft')
+        eta_fft = self.sim.state('eta_fft')
+        CharneyPV_fft = rot_fft - self.sim.params.f*eta_fft
+        return ErtelPV_fft, CharneyPV_fft
+
+    def compute_PE_fft(self):
+        ErtelPV_fft, CharneyPV_fft = self.compute_PV_fft()
+        return (abs(ErtelPV_fft)**2/2,
+                abs(CharneyPV_fft)**2/2)
+
+    def compute_CharneyPE_fft(self):
+        # compute Charney (QG) potential vorticity
+        rot_fft = self.sim.state('rot_fft')
+        eta_fft = self.sim.state('eta_fft')
+        CharneyPV_fft = rot_fft - self.sim.params.f*eta_fft
+        return abs(CharneyPV_fft)**2/2
+
+    def compute_energies(self):
+        energyK_fft, energyA_fft, energyKr_fft = self.compute_energies_fft()
+        return (self.sum_wavenumbers(energyK_fft),
+                self.sum_wavenumbers(energyA_fft),
+                self.sum_wavenumbers(energyKr_fft))
+
+    def compute_energiesKA(self):
+        energyK_fft, energyA_fft = self.compute_energiesKA_fft()
+        return (self.sum_wavenumbers(energyK_fft),
+                self.sum_wavenumbers(energyA_fft))
+
+    def compute_energy(self):
+        energyK_fft, energyA_fft = self.compute_energiesKA_fft()
+        return (self.sum_wavenumbers(energyK_fft) +
+                self.sum_wavenumbers(energyA_fft))
+
+    def compute_enstrophy(self):
+        enstrophy_fft = self.compute_enstrophy_fft()
+        return self.sum_wavenumbers(enstrophy_fft)
+
+    def compute_lin_energies_fft(self):
+        """Compute quadratic energies."""
+
+        ux_fft = self.sim.state('ux_fft')
+        uy_fft = self.sim.state('uy_fft')
+        eta_fft = self.sim.state('eta_fft')
+
+        q_fft, div_fft, ageo_fft = \
+            self.oper.qdafft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        udx_fft, udy_fft = self.oper.vecfft_from_divfft(div_fft)
+        energy_dlin_fft = 0.5*(np.abs(udx_fft)**2 + np.abs(udy_fft)**2)
+
+        ugx_fft, ugy_fft, etag_fft = self.oper.uxuyetafft_from_qfft(q_fft)
+        energy_glin_fft = 0.5*(np.abs(ugx_fft)**2 + np.abs(ugy_fft)**2 +
+                               self.sim.params.c2*np.abs(etag_fft)**2)
+
+        uax_fft, uay_fft, etaa_fft = self.oper.uxuyetafft_from_afft(ageo_fft)
+        energy_alin_fft = 0.5*(np.abs(uax_fft)**2 + np.abs(uay_fft)**2 +
+                               self.sim.params.c2*np.abs(etaa_fft)**2)
+
+        return energy_glin_fft, energy_dlin_fft, energy_alin_fft
+
+
+class OutputSW1l(OutputBaseSW1l):
+
+    def compute_energies_fft(self):
+        state = self.sim.state
+        eta_fft = state('eta_fft')
+        energyA_fft = self.sim.params.c2 * np.abs(eta_fft)**2/2
+        Jx_fft = state('Jx_fft')
+        Jy_fft = state('Jy_fft')
+        ux_fft = state('ux_fft')
+        uy_fft = state('uy_fft')
+        energyK_fft = np.real(Jx_fft.conj()*ux_fft +
+                              Jy_fft.conj()*uy_fft)/2
+
+        rot_fft = state('rot_fft')
+        uxr_fft, uyr_fft = self.vecfft_from_rotfft(rot_fft)
+        rotJ_fft = self.rotfft_from_vecfft(Jx_fft, Jy_fft)
+        Jxr_fft, Jyr_fft = self.vecfft_from_rotfft(rotJ_fft)
+        energyKr_fft = np.real(Jxr_fft.conj()*uxr_fft +
+                               Jyr_fft.conj()*uyr_fft)/2
+        return energyK_fft, energyA_fft, energyKr_fft
+
+    def compute_energiesKA_fft(self):
+        state = self.sim.state
+        eta_fft = state('eta_fft')
+        energyA_fft = self.sim.params.c2 * np.abs(eta_fft)**2/2
+        Jx_fft = state('Jx_fft')
+        Jy_fft = state('Jy_fft')
+        ux_fft = state('ux_fft')
+        uy_fft = state('uy_fft')
+        energyK_fft = np.real(Jx_fft.conj()*ux_fft +
+                              Jy_fft.conj()*uy_fft)/2
+
+        return energyK_fft, energyA_fft
diff --git a/fluidsim/solvers/sw1l/output/print_stdout.py b/fluidsim/solvers/sw1l/output/print_stdout.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL291dHB1dC9wcmludF9zdGRvdXQucHk=
--- /dev/null
+++ b/fluidsim/solvers/sw1l/output/print_stdout.py
@@ -0,0 +1,157 @@
+
+from __future__ import print_function, division
+
+from time import time
+import numpy as np
+
+from fluidsim.base.output.print_stdout import PrintStdOutBase
+
+from fluiddyn.util import mpi
+
+
+class PrintStdOutSW1l(PrintStdOutBase):
+    """A :class:`PrintStdOutBase` object is used to print in both the
+    stdout and the stdout.txt file, and also to print simple info on
+    the current state of the simulation."""
+
+    def online_print(self):
+        tsim = self.sim.time_stepping.t
+        if (tsim-self.t_last_print_info <= self.period_print):
+            return
+
+        itsim = self.sim.time_stepping.it
+        deltatsim = self.sim.time_stepping.deltat
+
+        energyK, energyA = self.output.compute_energiesKA()
+        energy = energyK + energyA
+        if mpi.rank==0:
+            t_real_word = time()
+            if self.t_real_word_last == 0.:
+                duration_left = 0
+            else:
+                if self.params.time_stepping.USE_T_END:
+                    duration_left = int(np.round(
+                        (self.params.time_stepping.t_end - tsim)
+                        *(t_real_word-self.t_real_word_last)
+                        /(tsim - self.t_last_print_info)
+                    ))
+                else:
+                    duration_left = int(np.round(
+                        (self.params.time_stepping.it_end - itsim)
+                        *(t_real_word-self.t_real_word_last)
+                    ))
+            to_print = (
+                'it = {0:6d} ; t       = {1:9.3f} ; deltat       = {2:10.3g}\n'
+                '              energy  = {3:8.3e} ; Delta energy = {4:8.3e}\n'
+                '              energyK = {5:8.3e} ; energyA      = {6:8.3e}\n'
+                '              estimated remaining duration = {7:6d} s')
+            to_print = to_print.format(
+                itsim, tsim, deltatsim,
+                energy, energy-self.energy_temp,
+                energyK, energyA,
+                duration_left)
+            self.print_stdout(to_print)
+            self.t_real_word_last = t_real_word
+        self.energy_temp = energy
+        self.t_last_print_info = tsim
+
+    def load(self):
+        dico_results = {'name_solver': self.output.name_solver}
+        file_means = open(self.output.path_run+'/stdout.txt')
+        lines = file_means.readlines()
+
+        lines_t = []
+        lines_E = []
+        lines_E_KA = []
+        for il, line in enumerate(lines):
+            if line[0:4]=='it =':
+                lines_t.append(line)
+            if line[0:23]=='              energy  =':
+                lines_E.append(line)
+            if line[0:23]=='              energyK =':
+                lines_E_KA.append(line)
+        nt = len(lines_t)
+        if nt > 1: 
+            nt -= 1
+
+        it = np.zeros(nt, dtype=np.int)
+        t = np.zeros(nt)
+        deltat = np.zeros(nt)
+
+        E = np.zeros(nt)
+        deltaE = np.zeros(nt)
+
+        E_K = np.zeros(nt)
+        E_A = np.zeros(nt)
+
+        for il in xrange(nt):
+            line = lines_t[il]
+            words = line.split()
+            it[il] = int(words[2])
+            t[il] = float(words[6])
+            deltat[il] = float(words[10])
+
+            line = lines_E[il]
+            words = line.split()
+            E[il] = float(words[2])
+            deltaE[il] = float(words[7])
+
+            line = lines_E_KA[il]
+            words = line.split()
+            E_K[il] = float(words[2])
+            E_A[il] = float(words[6])
+
+        dico_results['it'] = it
+        dico_results['t'] = t
+        dico_results['deltat'] = deltat
+        dico_results['E'] = E
+        dico_results['deltaE'] = deltaE
+        dico_results['E_K'] = E_K
+        dico_results['E_A'] = E_A
+        return dico_results
+
+
+    def plot(self):
+        dico_results = self.load()
+
+        it = dico_results['it']
+        t = dico_results['t']
+        deltat = dico_results['deltat']
+        E = dico_results['E']
+        deltaE = dico_results['deltaE']
+        E_K = dico_results['E_K']
+        E_A = dico_results['E_A']
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.55
+        width_axe = 0.85
+        height_axe = 0.4
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('deltat(t)')
+        title = ('info stdout, solver '+self.output.name_solver+
+                 ', nh = {0:5d}'.format(self.nx))
+
+        try:
+            title = title+', c = {0:.4g}, f = {1:.4g}'.format(
+                np.sqrt(self.c2), self.f)
+        except AttributeError:
+            pass
+
+        ax1.set_title(title)
+        ax1.hold(True)
+        ax1.plot(t, deltat, 'k', linewidth=2 )
+
+        z_bottom_axe = 0.08
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('t')
+        ax2.set_ylabel('E(t), deltaE(t)')
+        ax2.hold(True)
+        ax2.plot(t, E, 'k', linewidth=2 )
+        ax2.plot(t, E_K, 'r', linewidth=2 )
+        ax2.plot(t, E_A, 'b', linewidth=2 )
+        ax2.plot(t, deltaE, 'k--', linewidth=2 )
diff --git a/fluidsim/solvers/sw1l/output/spatial_means.py b/fluidsim/solvers/sw1l/output/spatial_means.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL291dHB1dC9zcGF0aWFsX21lYW5zLnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/output/spatial_means.py
@@ -0,0 +1,706 @@
+
+import os
+import numpy as np
+
+from fluiddyn.util import mpi
+from fluidsim.base.output.spatial_means import (
+    SpatialMeansBase, inner_prod)
+
+
+class SpatialMeansMSW1l(SpatialMeansBase):
+    """A :class:`SpatialMean` object handles the saving of ."""
+
+    def __init__(self, output):
+
+        params = output.sim.params
+        self.c2 = params.c2
+        self.f = params.f
+
+        super(SpatialMeansMSW1l, self).__init__(output)
+
+    def save_one_time(self):
+        tsim = self.sim.time_stepping.t
+        self.t_last_save = tsim
+
+        if mpi.rank == 0:
+            self.file.write('####\ntime = {0:.6e}\n'.format(tsim))
+
+        energyK_fft, energyA_fft, energyKr_fft = \
+            self.output.compute_energies_fft()
+        energyK = self.sum_wavenumbers(energyK_fft)
+        energyA = self.sum_wavenumbers(energyA_fft)
+        energyKr = self.sum_wavenumbers(energyKr_fft)
+        energy = energyK + energyA
+
+        CharneyPE_fft = self.output.compute_CharneyPE_fft()
+        CharneyPE = self.sum_wavenumbers(CharneyPE_fft)
+
+        if mpi.rank == 0:
+            to_print = (
+                'E      = {0:11.6e} ; CPE        = {1:11.6e} \n'
+                'EK     = {2:11.6e} ; EA         = {3:11.6e} ; '
+                'EKr       = {4:11.6e} \n').format(
+                    energy, CharneyPE, energyK, energyA, energyKr)
+            self.file.write(to_print)
+
+        # Compute and save dissipation rates.
+        self.treat_dissipation_rates(energyK_fft, energyA_fft, CharneyPE_fft)
+
+        # Compute and save conversion rates.
+        self.treat_conversion()
+
+        # Compute and save skewness and kurtosis.
+        eta = self.sim.state.state_phys['eta']
+        meaneta2 = 2./self.c2*energyA
+        skew_eta = np.mean(eta**3)/meaneta2**(3./2)
+        kurt_eta = np.mean(eta**4)/meaneta2**(2)
+
+        ux = self.sim.state.state_phys['ux']
+        uy = self.sim.state.state_phys['uy']
+        ux_fft = self.sim.oper.fft2(ux)
+        uy_fft = self.sim.oper.fft2(uy)
+        rot_fft = self.sim.oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        rot = self.sim.oper.ifft2(rot_fft)
+        meanrot2 = self.sum_wavenumbers(abs(rot_fft)**2)
+        skew_rot = np.mean(rot**3)/meanrot2**(3./2)
+        kurt_rot = np.mean(rot**4)/meanrot2**(2)
+
+        if mpi.rank == 0:
+            to_print = (
+                'eta skew = {0:11.6e} ; kurt = {1:11.6e} \n'
+                'rot skew = {2:11.6e} ; kurt = {3:11.6e} \n').format(
+                skew_eta, kurt_eta, skew_rot, kurt_rot)
+            self.file.write(to_print)
+
+        if self.sim.params.FORCING:
+            self.treat_forcing()
+
+        if mpi.rank == 0:
+            self.file.flush()
+            os.fsync(self.file.fileno())
+
+        if self.has_to_plot and mpi.rank == 0:
+            self.axe_a.plot(tsim, energy, 'k.')
+            self.axe_a.plot(tsim, energyK, 'r.')
+            self.axe_a.plot(tsim, energyA, 'b.')
+
+            if tsim-self.t_last_show >= self.period_show:
+                self.t_last_show = tsim
+                fig = self.axe_a.get_figure()
+                fig.canvas.draw()
+
+    def treat_conversion(self):
+        mean_space = self.sim.oper.mean_space
+
+        c2 = self.sim.params.c2
+        eta = self.sim.state('eta')
+        div = self.sim.state('div')
+        h = eta + 1
+
+        Conv = c2/2*mean_space(h**2*div)
+        c2eta1d = c2*mean_space(eta*div)
+        c2eta2d = c2*mean_space(eta**2*div)
+        c2eta3d = c2*mean_space(eta**3*div)
+
+        if mpi.rank == 0:
+            to_print = (
+                'Conv = {0:11.6e} ; c2eta1d = {1:11.6e} ; '
+                'c2eta2d = {2:11.6e} ; c2eta2d = {3:11.6e}\n').format(
+                    Conv, c2eta1d, c2eta2d, c2eta3d)
+            self.file.write(to_print)
+
+    def treat_dissipation_rates(self, energyK_fft, energyA_fft,
+                                CharneyPE_fft):
+        """Compute and save dissipation rates."""
+
+        f_d, f_d_hypo = self.sim.time_stepping.compute_freq_diss()
+
+        dico_eps = self.compute_dissipation_rates(
+            f_d, f_d_hypo,
+            energyK_fft, energyA_fft, CharneyPE_fft)
+
+        self.save_dissipation_rates(dico_eps)
+
+    def compute_dissipation_rates(
+            self, f_d, f_d_hypo,
+            energyK_fft, energyA_fft, CharneyPE_fft):
+
+        epsK = self.sum_wavenumbers(f_d*2*energyK_fft)
+        epsK_hypo = self.sum_wavenumbers(f_d_hypo*2*energyK_fft)
+        epsA = self.sum_wavenumbers(f_d*2*energyA_fft)
+        epsA_hypo = self.sum_wavenumbers(f_d_hypo*2*energyA_fft)
+        epsCPE = self.sum_wavenumbers(f_d*2*CharneyPE_fft)
+        epsCPE_hypo = self.sum_wavenumbers(f_d_hypo*2*CharneyPE_fft)
+
+        dico_eps = {'epsK': epsK,
+                    'epsK_hypo': epsK_hypo,
+                    'epsA': epsA,
+                    'epsA_hypo': epsA_hypo,
+                    'epsCPE': epsCPE,
+                    'epsCPE_hypo': epsCPE_hypo}
+        return dico_eps
+
+    def save_dissipation_rates(self, dico_eps):
+        epsK = dico_eps['epsK']
+        epsK_hypo = dico_eps['epsK_hypo']
+        epsA = dico_eps['epsA']
+        epsA_hypo = dico_eps['epsA_hypo']
+        epsCPE = dico_eps['epsCPE']
+        epsCPE_hypo = dico_eps['epsCPE_hypo']
+
+        if mpi.rank == 0:
+            epsK_tot = epsK+epsK_hypo
+            epsA_tot = epsA+epsA_hypo
+
+            to_print = (
+'epsK   = {0:11.6e} ; epsK_hypo  = {1:11.6e} ; epsK_tot  = {2:11.6e} \n'
+'epsA   = {3:11.6e} ; epsA_hypo  = {4:11.6e} ; epsA_tot  = {5:11.6e} \n'
+'epsCPE = {6:11.6e} ; epsCPEhypo = {7:11.6e} ; epsCPEtot = {8:11.6e} \n'
+).format(epsK,   epsK_hypo,   epsK_tot,
+         epsA,   epsA_hypo,   epsA_tot,
+         epsCPE, epsCPE_hypo, epsCPE+epsCPE_hypo)
+            self.file.write(to_print)
+
+            if self.has_to_plot:
+                tsim = self.sim.time_stepping.t
+                self.axe_b.plot(tsim, epsK_tot+epsA_tot, 'k.')
+
+    def treat_forcing(self):
+        """Save forcing injection rates."""
+        state_fft = self.sim.state.state_fft
+        ux_fft = state_fft['ux_fft']
+        uy_fft = state_fft['uy_fft']
+        eta_fft = state_fft['eta_fft']
+
+        forcing_fft = self.sim.forcing.get_forcing()
+        Fx_fft = forcing_fft['ux_fft']
+        Fy_fft = forcing_fft['uy_fft']
+        Feta_fft = forcing_fft['eta_fft']
+
+        deltat = self.sim.time_stepping.deltat
+
+        PK1_fft = (
+            inner_prod(ux_fft, Fx_fft)
+            + inner_prod(uy_fft, Fy_fft)
+            )
+        PK2_fft = deltat/2*( abs(Fx_fft)**2 + abs(Fy_fft)**2 )
+
+        PK1 = self.sum_wavenumbers(PK1_fft)
+        PK2 = self.sum_wavenumbers(PK2_fft)
+
+        PA1_fft = self.c2*inner_prod(eta_fft, Feta_fft)
+        PA2_fft = deltat/2*self.c2*(abs(Feta_fft)**2)
+
+        PA1 = self.sum_wavenumbers(PA1_fft)
+        PA2 = self.sum_wavenumbers(PA2_fft)
+
+
+        if mpi.rank==0:
+
+            PK_tot = PK1+PK2
+            PA_tot = PA1+PA2
+            to_print =  (
+'PK1    = {0:11.6e} ; PK2        = {1:11.6e} ; PK_tot    = {2:11.6e} \n'
+'PA1    = {3:11.6e} ; PA2        = {4:11.6e} ; PA_tot    = {5:11.6e} \n'
+).format(PK1, PK2, PK_tot, PA1, PA2, PA_tot)
+
+            self.file.write(to_print)
+
+        if self.has_to_plot and mpi.rank == 0:
+            tsim = self.sim.time_stepping.t
+            self.axe_b.plot(tsim, PK_tot+PA_tot, 'c.')
+
+
+
+    def load(self):
+        dico_results = {'name_solver': self.output.name_solver}
+
+        file_means = open(self.path_file)
+        lines = file_means.readlines()
+
+        lines_t = []
+        lines_E = []
+        lines_EK = []
+        lines_epsK = []
+        lines_epsA = []
+        lines_epsCPE = []
+
+        lines_epsK = []
+
+
+        lines_PK = []
+        lines_PA = []
+        lines_etaskew = []
+        lines_rotskew = []
+        lines_Conv = []
+
+        for il, line in enumerate(lines):
+            if line[0:6]=='time =':
+                lines_t.append(line)
+            if line[0:8]=='E      =':
+                lines_E.append(line)
+            if line[0:8]=='EK     =':
+                lines_EK.append(line)
+            if line[0:8]=='epsK   =':
+                lines_epsK.append(line)
+            if line[0:8]=='epsA   =':
+                lines_epsA.append(line)
+            if line[0:8]=='epsCPE =':
+                lines_epsCPE.append(line)
+            if line[0:8]=='PK1    =':
+                lines_PK.append(line)
+            if line[0:8]=='PA1    =':
+                lines_PA.append(line)
+            if line.startswith('eta skew ='):
+                lines_etaskew.append(line)
+            if line.startswith('rot skew ='):
+                lines_rotskew.append(line)
+            if line.startswith('Conv ='):
+                lines_Conv.append(line)
+
+        nt = len(lines_t)
+        if nt>1: 
+            nt -= 1
+
+        t = np.empty(nt)
+
+        E = np.empty(nt)
+        CPE = np.empty(nt)
+        EK = np.empty(nt)
+        EA = np.empty(nt)
+        EKr = np.empty(nt)
+        epsK = np.empty(nt)
+        epsK_hypo = np.empty(nt)
+        epsK_tot = np.empty(nt)
+        epsA = np.empty(nt)
+        epsA_hypo = np.empty(nt)
+        epsA_tot = np.empty(nt)
+        epsCPE = np.empty(nt)
+        epsCPE_hypo = np.empty(nt)
+        epsCPE_tot = np.empty(nt)
+
+
+        if len(lines_PK) == len(lines_t):
+            PK1 = np.empty(nt)
+            PK2 = np.empty(nt)
+            PK_tot = np.empty(nt)
+            PA1 = np.empty(nt)
+            PA2 = np.empty(nt)
+            PA_tot = np.empty(nt)
+
+        if len(lines_rotskew) == len(lines_t):
+            skew_eta = np.empty(nt)
+            kurt_eta = np.empty(nt)
+            skew_rot = np.empty(nt)
+            kurt_rot = np.empty(nt)
+
+        if len(lines_Conv) == len(lines_t):
+            Conv = np.empty(nt)
+            c2eta1d = np.empty(nt)
+            c2eta2d = np.empty(nt)
+            c2eta3d = np.empty(nt)
+
+        for il in xrange(nt):
+            line = lines_t[il]
+            words = line.split()
+            t[il] = float(words[2])
+
+            line = lines_E[il]
+            words = line.split()
+            E[il] = float(words[2])
+            CPE[il] = float(words[6])
+
+            line = lines_EK[il]
+            words = line.split()
+            EK[il] = float(words[2])
+            EA[il] = float(words[6])
+            EKr[il] = float(words[10])
+
+            line = lines_epsK[il]
+            words = line.split()
+            epsK[il] = float(words[2])
+            epsK_hypo[il] = float(words[6])
+            epsK_tot[il] = float(words[10])
+
+            line = lines_epsA[il]
+            words = line.split()
+            epsA[il] = float(words[2])
+            epsA_hypo[il] = float(words[6])
+            epsA_tot[il] = float(words[10])
+
+            line = lines_epsCPE[il]
+            words = line.split()
+            epsCPE[il] = float(words[2])
+            epsCPE_hypo[il] = float(words[6])
+            epsCPE_tot[il] = float(words[10])
+
+
+            if len(lines_PK) == len(lines_t):
+                line = lines_PK[il]
+                words = line.split()
+                PK1[il] = float(words[2])
+                PK2[il] = float(words[6])
+                PK_tot[il] = float(words[10])
+
+                line = lines_PA[il]
+                words = line.split()
+                PA1[il] = float(words[2])
+                PA2[il] = float(words[6])
+                PA_tot[il] = float(words[10])
+
+            if len(lines_rotskew) == len(lines_t):
+                line = lines_etaskew[il]
+                words = line.split()
+                skew_eta[il] = float(words[3])
+                kurt_eta[il] = float(words[7])
+
+                line = lines_rotskew[il]
+                words = line.split()
+                skew_rot[il] = float(words[3])
+                kurt_rot[il] = float(words[7])
+
+            if len(lines_Conv) == len(lines_t):
+                line = lines_Conv[il]
+                words = line.split()
+                Conv[il] = float(words[2])
+                c2eta1d[il] = float(words[6])
+                c2eta2d[il] = float(words[10])
+                c2eta3d[il] = float(words[14])
+
+
+
+
+
+
+        dico_results['t'] = t
+        dico_results['E'] = E
+        dico_results['CPE'] = CPE
+
+        dico_results['EK'] = EK
+        dico_results['EA'] = EA
+        dico_results['EKr'] = EKr
+
+        dico_results['epsK'] = epsK
+        dico_results['epsK_hypo'] = epsK_hypo
+        dico_results['epsK_tot'] = epsK_tot
+
+        dico_results['epsA'] = epsA
+        dico_results['epsA_hypo'] = epsA_hypo
+        dico_results['epsA_tot'] = epsA_tot
+
+        dico_results['epsCPE'] = epsCPE
+        dico_results['epsCPE_hypo'] = epsCPE_hypo
+        dico_results['epsCPE_tot'] = epsCPE_tot
+
+
+        if len(lines_PK) == len(lines_t):
+            dico_results['PK1'] = PK1
+            dico_results['PK2'] = PK2
+            dico_results['PK_tot'] = PK_tot
+            dico_results['PA1'] = PA1
+            dico_results['PA2'] = PA2
+            dico_results['PA_tot'] = PA_tot
+
+        if len(lines_rotskew) == len(lines_t):
+            dico_results['skew_eta'] = skew_eta
+            dico_results['kurt_eta'] = kurt_eta
+            dico_results['skew_rot'] = skew_rot
+            dico_results['kurt_rot'] = kurt_rot
+
+        if len(lines_Conv) == len(lines_t):
+            dico_results['Conv'] = Conv
+            dico_results['c2eta1d'] = c2eta1d
+            dico_results['c2eta2d'] = c2eta2d
+            dico_results['c2eta3d'] = c2eta3d
+
+        return dico_results
+
+
+
+    def plot(self):
+        dico_results = self.load()
+
+        t = dico_results['t']
+
+        E = dico_results['E']
+        CPE = dico_results['CPE']
+
+        EK = dico_results['EK']
+        EA = dico_results['EA']
+        EKr = dico_results['EKr']
+
+        epsK = dico_results['epsK']
+        epsK_hypo = dico_results['epsK_hypo']
+        epsK_tot = dico_results['epsK_tot']
+
+        epsA = dico_results['epsA']
+        epsA_hypo = dico_results['epsA_hypo']
+        epsA_tot = dico_results['epsA_tot']
+
+        epsE      = epsK      + epsA
+        epsE_hypo = epsK_hypo + epsA_hypo
+        epsE_tot  = epsK_tot  + epsA_tot
+
+        epsCPE = dico_results['epsCPE']
+        epsCPE_hypo = dico_results['epsCPE_hypo']
+        epsCPE_tot = dico_results['epsCPE_tot']
+
+        if 'PK_tot' in dico_results:
+            PK_tot = dico_results['PK_tot']
+            PA_tot = dico_results['PA_tot']
+            P_tot = PK_tot + PA_tot
+
+        width_axe = 0.85
+        height_axe = 0.37
+        x_left_axe = 0.12
+        z_bottom_axe = 0.56
+
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('$2E(t)/c^2$')
+        title = ('mean energy, solver '+self.output.name_solver+
+                 ', nh = {0:5d}'.format(self.nx)+
+                 ', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f))
+        ax1.set_title(title)
+        ax1.hold(True)
+        norm = self.c2/2
+        ax1.plot(t, E/norm, 'k', linewidth=2)
+        ax1.plot(t, EK/norm, 'r', linewidth=1)
+        ax1.plot(t, EA/norm, 'b', linewidth=1)
+        ax1.plot(t, EKr/norm, 'r--', linewidth=1)
+        ax1.plot(t, (EK-EKr)/norm, 'r:', linewidth=1)
+
+        z_bottom_axe = 0.07
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('t')
+        ax2.set_ylabel('Charney PE(t)')
+        title = ('mean Charney PE(t)')
+        ax2.set_title(title)
+        ax2.hold(True)
+        ax2.plot(t, CPE, 'k', linewidth=2 )
+
+
+        z_bottom_axe = 0.56
+        size_axe[1] = z_bottom_axe
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('t')
+        ax1.set_ylabel('$P_E(t)$, $\epsilon(t)$')
+        title = ('forcing and dissipation, solver '+self.output.name_solver+
+', nh = {0:5d}'.format(self.nx)+
+', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f)
+)
+        ax1.set_title(title)
+        ax1.hold(True)
+        if 'PK_tot' in dico_results:
+            (l_P_tot,) = ax1.plot(t, P_tot, 'c', linewidth=2 )
+            l_P_tot.set_label('$P_{tot}$')
+
+        (l_epsE,) = ax1.plot(t, epsE, 'k--', linewidth=2 )
+        (l_epsE_hypo,) = ax1.plot(t, epsE_hypo, 'g', linewidth=2 )
+        (l_epsE_tot,) = ax1.plot(t, epsE_tot, 'k', linewidth=2 )
+
+        l_epsE.set_label('$\epsilon$')
+        l_epsE_hypo.set_label('$\epsilon_{hypo}$')
+        l_epsE_tot.set_label('$\epsilon_{tot}$')
+
+        ax1.legend(loc=2)
+
+
+        z_bottom_axe = 0.07
+        size_axe[1] = z_bottom_axe
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('t')
+        ax2.set_ylabel('$\epsilon$ Charney PE(t)')
+        title = ('dissipation Charney PE')
+        ax2.set_title(title)
+        ax2.hold(True)
+        ax2.plot(t, epsCPE, 'k--', linewidth=2 )
+        ax2.plot(t, epsCPE_hypo, 'g', linewidth=2 )
+        ax2.plot(t, epsCPE_tot, 'r', linewidth=2 )
+
+
+
+
+
+
+#         skew_eta = dico_results['skew_eta']
+#         kurt_eta = dico_results['kurt_eta']
+#         skew_rot = dico_results['skew_rot']
+#         kurt_rot = dico_results['kurt_rot']
+
+#         fig, ax1 = self.output.figure_axe()
+
+#         title = ('skewness and kurtosis, solver '+self.output.name_solver+
+# ', nh = {0:5d}'.format(self.nx)+
+# ', c2 = {0:.4g}, f = {1:.4g}'.format(self.c2, self.f)
+# )
+#         ax1.set_title(title)
+#         ax2.set_xlabel('t')
+
+#         ax1.plot(t, skew_eta, 'b', linewidth=2)
+#         ax1.plot(t, kurt_eta, 'b--', linewidth=2)
+#         ax1.plot(t, skew_rot, 'r', linewidth=2)
+#         ax1.plot(t, kurt_rot, 'r--', linewidth=2)
+
+
+
+
+
+
+
+
+
+
+class SpatialMeansSW1l(SpatialMeansMSW1l):
+    """A :class:`SpatialMean` object handles the saving of ."""
+
+    def treat_dissipation_rates(self, energyK_fft, energyA_fft,
+                                CharneyPE_fft):
+        """Compute and save dissipation rates."""
+
+        f_d, f_d_hypo = self.sim.time_stepping.compute_freq_diss()
+
+        dico_eps = super(
+            SpatialMeansSW1l, self
+        ).compute_dissipation_rates(
+            f_d, f_d_hypo, energyK_fft, energyA_fft, CharneyPE_fft)
+
+
+        (epsKsuppl, epsKsuppl_hypo
+         ) = self.compute_epsK(f_d, f_d_hypo, energyK_fft, dico_eps)
+
+        super(SpatialMeansSW1l, self).save_dissipation_rates(dico_eps)
+
+        if mpi.rank == 0:
+            to_print =  (
+'epsKsup= {0:11.6e} ; epsKshypo  = {1:11.6e} ;\n'
+).format(epsKsuppl,   epsKsuppl_hypo)
+            self.file.write(to_print)
+
+    def compute_epsK(self, f_d, f_d_hypo,
+                     energyK_fft, dico_eps):
+
+        ux = self.sim.state.state_phys['ux']
+        uy = self.sim.state.state_phys['uy']
+
+        EKquad = 0.5*(ux**2 + uy**2)
+        EKquad_fft = self.sim.oper.fft2(EKquad)
+
+        eta_fft = self.sim.state('eta_fft')
+
+        epsKsuppl = self.sum_wavenumbers(
+            f_d*inner_prod(EKquad_fft, eta_fft)
+            )
+
+        epsKsuppl_hypo = self.sum_wavenumbers(
+            f_d_hypo*inner_prod(EKquad_fft, eta_fft)
+            )
+
+        dico_eps['epsK'] += epsKsuppl
+        dico_eps['epsK_hypo'] += epsKsuppl_hypo
+
+        return epsKsuppl, epsKsuppl_hypo
+
+
+
+    def load(self):
+
+        dico_results = super(SpatialMeansSW1l, self).load()
+
+        file_means = open(self.path_file)
+        lines = file_means.readlines()
+
+        lines_epsKsuppl = []
+
+        for il, line in enumerate(lines):
+            if line.startswith('epsKsup='):
+                lines_epsKsuppl.append(line)
+
+        t = dico_results['t']
+        nt = len(t)
+        epsKsuppl = np.empty(nt)
+        epsKsuppl_hypo = np.empty(nt)
+
+        for il in xrange(nt):
+            line = lines_epsKsuppl[il]
+            words = line.split()
+            epsKsuppl[il] = float(words[1])
+            epsKsuppl_hypo[il] = float(words[5])
+
+        dico_results['epsKsuppl'] = epsKsuppl
+        dico_results['epsKsuppl_hypo'] = epsKsuppl_hypo
+
+        return dico_results
+
+
+    def treat_forcing(self):
+        """Save forcing injection rates."""
+        state = self.sim.state
+        ux_fft = state('ux_fft')
+        uy_fft = state('uy_fft')
+        eta_fft = state('eta_fft')
+
+        Fx_fft, Fy_fft, Feta_fft = \
+            self.sim.forcing.get_FxFyFetafft()
+
+        deltat = self.sim.time_stepping.deltat
+
+        PA1_fft = self.c2*inner_prod(eta_fft, Feta_fft)
+        PA2_fft = deltat/2*self.c2*(abs(Feta_fft)**2)
+
+        PA1 = self.sum_wavenumbers(PA1_fft)
+        PA2 = self.sum_wavenumbers(PA2_fft)
+
+        Fx = self.sim.oper.ifft2(Fx_fft)
+        Fy = self.sim.oper.ifft2(Fy_fft)
+        Feta = self.sim.oper.ifft2(Feta_fft)
+
+        eta = self.sim.state.state_phys['eta']
+        h = eta + 1.
+
+        ux = self.sim.state.state_phys['ux']
+        uy = self.sim.state.state_phys['uy']
+
+        FetaFx_fft = self.sim.oper.fft2(Feta*Fx)
+        FetaFy_fft = self.sim.oper.fft2(Feta*Fy)
+
+        Jx_fft = self.sim.oper.fft2(h*ux)
+        Jy_fft = self.sim.oper.fft2(h*uy)
+
+        FJx_fft = self.sim.oper.fft2(h*Fx + Feta*ux)
+        FJy_fft = self.sim.oper.fft2(h*Fy + Feta*uy)
+
+        PK1_fft = 0.5*(
+            inner_prod(Jx_fft, Fx_fft)
+            + inner_prod(Jy_fft, Fy_fft)
+            + inner_prod(ux_fft, FJx_fft)
+            + inner_prod(uy_fft, FJy_fft)
+            )
+        PK2_fft = deltat/2*(
+            0.5*(inner_prod(Fx_fft, FJx_fft)
+                 + inner_prod(Fy_fft, FJy_fft)
+                 )
+            + inner_prod(ux_fft, FetaFx_fft)
+            + inner_prod(uy_fft, FetaFy_fft)
+            )
+
+        PK1 = self.sum_wavenumbers(PK1_fft)
+        PK2 = self.sum_wavenumbers(PK2_fft)
+
+        if mpi.rank==0:
+
+            PK_tot = PK1+PK2
+            PA_tot = PA1+PA2
+            to_print =  (
+'PK1    = {0:11.6e} ; PK2        = {1:11.6e} ; PK_tot    = {2:11.6e} \n'
+'PA1    = {3:11.6e} ; PA2        = {4:11.6e} ; PA_tot    = {5:11.6e} \n'
+).format(PK1, PK2, PK_tot, PA1, PA2, PA_tot)
+
+            self.file.write(to_print)
+
+        if self.has_to_plot and mpi.rank == 0:
+            tsim = self.sim.time_stepping.t
+            self.axe_b.plot(tsim, PK_tot+PA_tot, 'c.')
diff --git a/fluidsim/solvers/sw1l/output/spect_energy_budget.py b/fluidsim/solvers/sw1l/output/spect_energy_budget.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL291dHB1dC9zcGVjdF9lbmVyZ3lfYnVkZ2V0LnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/output/spect_energy_budget.py
@@ -0,0 +1,936 @@
+import numpy as np
+import h5py
+
+from fluiddyn.util import mpi
+
+from fluidsim.base.output.spect_energy_budget import (
+    SpectralEnergyBudgetBase, cumsum_inv, inner_prod)
+
+
+class SpectralEnergyBudgetSW1l(SpectralEnergyBudgetBase):
+    """Save and plot spectra."""
+
+    def __init__(self, output):
+
+        params = output.sim.params
+        self.c2 = params.c2
+        self.f = params.f
+
+        super(SpectralEnergyBudgetSW1l, self).__init__(output)
+
+    def compute(self):
+        """compute spectral energy budget the one time."""
+        oper = self.sim.oper
+
+        # print_memory_usage('start function compute seb')
+
+        ux = self.sim.state.state_phys['ux']
+        uy = self.sim.state.state_phys['uy']
+        eta = self.sim.state.state_phys['eta']
+        h = 1.+eta
+
+        Jx = h*ux
+        Jy = h*uy
+        Jx_fft = oper.fft2(Jx)
+        Jy_fft = oper.fft2(Jy)
+        del(Jx, Jy)
+
+        ux_fft = self.sim.state('ux_fft')
+        uy_fft = self.sim.state('uy_fft')
+        eta_fft = self.sim.state('eta_fft')
+        h_fft = eta_fft.copy()
+        if mpi.rank == 0:
+            h_fft[0, 0] = 1.
+
+        rot_fft = oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        urx_fft, ury_fft = oper.vecfft_from_rotfft(rot_fft)
+        del(rot_fft)
+        urx = oper.ifft2(urx_fft)
+        ury = oper.ifft2(ury_fft)
+
+        q_fft, div_fft, a_fft = \
+            self.oper.qdafft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        # if self.params.f != 0:
+        #     udx_fft, udy_fft = oper.vecfft_from_divfft(div_fft)
+        #     ugx_fft, ugy_fft, etag_fft = \
+        #         self.oper.uxuyetafft_from_qfft(q_fft)
+        #     uax_fft, uay_fft, etaa_fft = \
+        #         self.oper.uxuyetafft_from_afft(a_fft)
+        #     del(a_fft)
+        #     # velocity influenced by linear terms
+        #     u_infl_lin_x = udx_fft + uax_fft
+        #     u_infl_lin_y = udy_fft + uay_fft
+
+        udx_fft, udy_fft = oper.vecfft_from_divfft(div_fft)
+        udx = oper.ifft2(udx_fft)
+        udy = oper.ifft2(udy_fft)
+        div = oper.ifft2(div_fft)
+        del(div_fft)
+
+        # print_memory_usage('before starting computing fluxes')
+
+
+        # compute flux of Charney PE
+        Fq_fft = self.fnonlinfft_from_uxuy_funcfft(urx, ury, q_fft)
+        transferCPE_fft = inner_prod(q_fft, Fq_fft)
+        del(q_fft, Fq_fft)
+        transfer2D_CPE = self.spectrum2D_from_fft(transferCPE_fft)
+        del(transferCPE_fft)
+
+#         print(
+# ('sum(transfer2D_CPE) = {0:9.4e} ; sum(abs(transfer2D_CPE)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_CPE),
+# np.sum(abs(transfer2D_CPE)))
+# )
+
+
+
+        px_h_fft, py_h_fft = oper.gradfft_from_fft(eta_fft)
+        px_h = oper.ifft2(px_h_fft)
+        py_h = oper.ifft2(py_h_fft)
+
+        F_rh = -urx*px_h - ury*py_h
+        F_dh = -udx*px_h - udy*py_h - h*div/2
+        F_de = -udx*px_h - udy*py_h - eta*div/2
+        del(px_h, py_h)
+        F_rh_fft = oper.fft2(F_rh)
+        F_dh_fft = oper.fft2(F_dh)
+        F_de_fft = oper.fft2(F_de)
+        del(F_rh, F_dh, F_de)
+        oper.dealiasing(F_rh_fft)
+        oper.dealiasing(F_dh_fft)
+        oper.dealiasing(F_de_fft)
+
+        transferEAr_fft = self.c2*inner_prod(h_fft, F_rh_fft)
+        transferEPd_fft = self.c2*inner_prod(h_fft, F_dh_fft)
+        transferEAd_fft = self.c2*inner_prod(eta_fft, F_de_fft)
+        del(F_rh_fft, F_dh_fft, F_de_fft)
+
+        transfer2D_EAr = self.spectrum2D_from_fft(transferEAr_fft)
+        transfer2D_EPd = self.spectrum2D_from_fft(transferEPd_fft)
+        transfer2D_EAd = self.spectrum2D_from_fft(transferEAd_fft)
+        del(transferEAr_fft, transferEPd_fft, transferEAd_fft)
+
+        # print_memory_usage('after transfer2D_EAr')
+
+#         print(
+# ('sum(transfer2D_EAr) = {0:9.4e} ; sum(abs(transfer2D_EAr)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_EAr),
+# np.sum(abs(transfer2D_EAr)))
+# )
+
+#         print(
+# ('sum(transfer2D_EAd) = {0:9.4e} ; sum(abs(transfer2D_EAd)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_EAd),
+# np.sum(abs(transfer2D_EAd)))
+# )
+
+        hdiv_fft = oper.fft2(h*div)
+        convP_fft = self.c2/2.*inner_prod(h_fft, hdiv_fft)
+        convP2D = self.spectrum2D_from_fft(convP_fft)
+        del(convP_fft, h_fft, hdiv_fft)
+
+        EP = self.c2/2*h*h
+        EP_fft = oper.fft2(EP)
+        del(EP, h)
+        px_EP_fft, py_EP_fft = oper.gradfft_from_fft(EP_fft)
+        del(EP_fft)
+
+        convK_fft = 1./2*(
+            - inner_prod(ux_fft, px_EP_fft)
+            - inner_prod(uy_fft, py_EP_fft)
+            - self.c2*inner_prod(Jx_fft, px_h_fft)
+            - self.c2*inner_prod(Jy_fft, py_h_fft)
+            )
+        del(px_h_fft, py_h_fft, px_EP_fft, py_EP_fft)
+        convK2D = self.spectrum2D_from_fft(convK_fft)
+        del(convK_fft)
+
+        # print_memory_usage('after convK2D')
+
+#         print(
+# ('sum(convP2D-convK2D)*deltakh = {0:9.4e}, sum(convP2D)*deltakh = {1:9.4e}'
+# ).format(
+# np.sum(convP2D-convK2D)*self.oper.deltakh,
+# np.sum(convP2D)*self.oper.deltakh
+# )
+# )
+
+#         print(
+# ('                                           sum(convK2D)*deltakh = {0:9.4e}'
+# ).format(
+# np.sum(convK2D)*self.oper.deltakh
+# )
+# )
+
+
+        Fxrd_fft, Fxdd_fft = self.fnonlinfft_from_uruddivfunc(
+            urx, ury, udx, udy, div, udx_fft, udx)
+        Fyrd_fft, Fydd_fft = self.fnonlinfft_from_uruddivfunc(
+            urx, ury, udx, udy, div, udy_fft, udy)
+        Fxrr_fft, Fxdr_fft = self.fnonlinfft_from_uruddivfunc(
+            urx, ury, udx, udy, div, urx_fft, urx)
+        Fyrr_fft, Fydr_fft = self.fnonlinfft_from_uruddivfunc(
+            urx, ury, udx, udy, div, ury_fft, ury)
+
+
+        transferErrr_fft = (  inner_prod(urx_fft, Fxrr_fft)
+                            + inner_prod(ury_fft, Fyrr_fft)
+                            )
+        transfer2D_Errr = self.spectrum2D_from_fft(transferErrr_fft)
+        del(transferErrr_fft)
+#         print(
+# ('sum(transfer2D_Errr) = {0:9.4e} ; sum(abs(transfer2D_Errr)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Errr),
+# np.sum(abs(transfer2D_Errr)))
+# )
+
+        transferEdrd_fft = (  inner_prod(udx_fft, Fxrd_fft)
+                            + inner_prod(udy_fft, Fyrd_fft)
+                            )
+        transfer2D_Edrd = self.spectrum2D_from_fft(transferEdrd_fft)
+        del(transferEdrd_fft)
+#         print(
+# ('sum(transfer2D_Edrd) = {0:9.4e} ; sum(abs(transfer2D_Edrd)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Edrd),
+# np.sum(abs(transfer2D_Edrd)))
+# )
+        Clfromqq = (  inner_prod(udx_fft, Fxrr_fft)
+                    + inner_prod(udy_fft, Fyrr_fft)
+                      )
+        transferEdrr_rrd_fft = (  Clfromqq
+                                + inner_prod(urx_fft, Fxrd_fft)
+                                + inner_prod(ury_fft, Fyrd_fft)
+                                  )
+        Clfromqq = self.spectrum2D_from_fft(Clfromqq)
+        transfer2D_Edrr_rrd = self.spectrum2D_from_fft(transferEdrr_rrd_fft)
+        del(transferEdrr_rrd_fft)
+#         print(
+# ('sum(transfer2D_Edrr_rrd) = {0:9.4e} ; '
+# 'sum(abs(transfer2D_Edrr_rrd)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Edrr_rrd),
+# np.sum(abs(transfer2D_Edrr_rrd)))
+# )
+
+
+
+        transferErdr_fft = (  inner_prod(urx_fft, Fxdr_fft)
+                            + inner_prod(ury_fft, Fydr_fft)
+                            )
+        transfer2D_Erdr = self.spectrum2D_from_fft(transferErdr_fft)
+        del(transferErdr_fft)
+#         print(
+# ('sum(transfer2D_Erdr) = {0:9.4e} ; sum(abs(transfer2D_Erdr)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Erdr),
+# np.sum(abs(transfer2D_Erdr)))
+# )
+
+        transferEddd_fft = (  inner_prod(udx_fft, Fxdd_fft)
+                            + inner_prod(udy_fft, Fydd_fft)
+                            )
+        transfer2D_Eddd = self.spectrum2D_from_fft(transferEddd_fft)
+        del(transferEddd_fft)
+#         print(
+# ('sum(transfer2D_Eddd) = {0:9.4e} ; sum(abs(transfer2D_Eddd)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Eddd),
+# np.sum(abs(transfer2D_Eddd)))
+# )
+
+        Cqfromll = (  inner_prod(urx_fft, Fxdd_fft)
+                     + inner_prod(ury_fft, Fydd_fft)
+                       )
+
+        transferEddr_rdd_fft = (  Cqfromll
+                                + inner_prod(udx_fft, Fxdr_fft)
+                                + inner_prod(udy_fft, Fydr_fft)
+                                  )
+
+        Cqfromll = self.spectrum2D_from_fft(Cqfromll)
+        transfer2D_Eddr_rdd = self.spectrum2D_from_fft(transferEddr_rdd_fft)
+        del(transferEddr_rdd_fft)
+#         print(
+# ('sum(transfer2D_Eddr_rdd) = {0:9.4e} ; '
+# 'sum(abs(transfer2D_Eddr_rdd)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Eddr_rdd),
+# np.sum(abs(transfer2D_Eddr_rdd)))
+# )
+
+
+        Fx_ru_fft = Fxrr_fft + Fxrd_fft
+        del(Fxrr_fft,  Fxrd_fft)
+        Fy_ru_fft = Fyrr_fft + Fyrd_fft
+        del(Fyrr_fft, Fyrd_fft)
+        Fx_du_fft = Fxdr_fft + Fxdd_fft
+        del(Fxdr_fft,  Fxdd_fft)
+        Fy_du_fft = Fydr_fft + Fydd_fft
+        del(Fydr_fft, Fydd_fft)
+
+
+        # print_memory_usage('after Fy_du_fft')
+
+
+        etaux = eta*ux
+        etauy = eta*uy
+
+        etaux_fft = oper.fft2(etaux)
+        etauy_fft = oper.fft2(etauy)
+        del(etaux, etauy)
+
+        px_etaux_fft, py_etaux_fft = oper.gradfft_from_fft(etaux_fft)
+        px_etauy_fft, py_etauy_fft = oper.gradfft_from_fft(etauy_fft)
+
+        px_etaux = oper.ifft2(px_etaux_fft)
+        del(px_etaux_fft)
+        py_etaux = oper.ifft2(py_etaux_fft)
+        del(py_etaux_fft)
+        px_etauy = oper.ifft2(px_etauy_fft)
+        del(px_etauy_fft)
+        py_etauy = oper.ifft2(py_etauy_fft)
+        del(py_etauy_fft)
+
+        Fx_reu = -urx*px_etaux - ury*py_etaux
+        Fx_reu_fft = oper.fft2(Fx_reu)
+        del(Fx_reu)
+
+        Fx_deu = -udx*px_etaux - udy*py_etaux - 0.5*div*eta*ux
+        del(px_etaux, py_etaux)
+        Fx_deu_fft = oper.fft2(Fx_deu)
+        del(Fx_deu)
+
+        Fy_reu = -urx*px_etauy - ury*py_etauy
+        Fy_reu_fft = oper.fft2(Fy_reu)
+        del(Fy_reu)
+
+        Fy_deu = -udx*px_etauy - udy*py_etauy - 0.5*div*eta*uy
+        del(px_etauy, py_etauy)
+        Fy_deu_fft = oper.fft2(Fy_deu)
+        del(Fy_deu)
+
+
+
+        transferEureu_fft = 0.5*(
+              inner_prod(ux_fft, Fx_reu_fft)
+            + inner_prod(uy_fft, Fy_reu_fft)
+            + inner_prod(etaux_fft, Fx_ru_fft)
+            + inner_prod(etauy_fft, Fy_ru_fft)
+            )
+        del(Fx_reu_fft, Fy_reu_fft, Fx_ru_fft, Fy_ru_fft)
+
+        transfer2D_Eureu = self.spectrum2D_from_fft(transferEureu_fft)
+#         print(
+# ('sum(transferEureu_fft) = {0:9.4e} ; '
+# 'sum(abs(transferEureu_fft)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Eureu),
+# np.sum(abs(transfer2D_Eureu)))
+# )
+
+        transferEudeu_fft = 0.5*(
+              inner_prod(ux_fft, Fx_deu_fft)
+            + inner_prod(uy_fft, Fy_deu_fft)
+            + inner_prod(etaux_fft, Fx_du_fft)
+            + inner_prod(etauy_fft, Fy_du_fft)
+            )
+        del(Fx_deu_fft, Fy_deu_fft, Fx_du_fft, Fy_du_fft)
+        del(etaux_fft, etauy_fft)
+
+        transfer2D_Eudeu = self.spectrum2D_from_fft(transferEudeu_fft)
+        del(transferEudeu_fft)
+#         print(
+# ('sum(transferEudeu_fft) = {0:9.4e} ; '
+# 'sum(abs(transferEudeu_fft)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Eudeu),
+# np.sum(abs(transfer2D_Eudeu)))
+# )
+
+        transfer2D_EKr = (transfer2D_Errr + transfer2D_Edrd
+                          + transfer2D_Edrr_rrd
+                          + transfer2D_Eureu
+                          )
+        transfer2D_EKd = (transfer2D_Erdr + transfer2D_Eddd
+                          + transfer2D_Eddr_rdd
+                          + transfer2D_Eudeu
+                          )
+
+        # print_memory_usage('end of function compute seb')
+
+
+        dico_results = {
+            'transfer2D_EAr': transfer2D_EAr,
+            'transfer2D_EAd': transfer2D_EAd,
+            'transfer2D_EPd': transfer2D_EPd,
+            'transfer2D_EKr': transfer2D_EKr,
+            'transfer2D_EKd': transfer2D_EKd,
+            'transfer2D_Errr': transfer2D_Errr,
+            'transfer2D_Edrd': transfer2D_Edrd,
+            'Clfromqq': Clfromqq,
+            'transfer2D_Edrr_rrd': transfer2D_Edrr_rrd,
+            'transfer2D_Erdr': transfer2D_Erdr,
+            'transfer2D_Eddd': transfer2D_Eddd,
+            'Cqfromll': Cqfromll,
+            'transfer2D_Eddr_rdd': transfer2D_Eddr_rdd,
+            'transfer2D_Eureu': transfer2D_Eureu,
+            'transfer2D_Eudeu': transfer2D_Eudeu,
+            'convP2D': convP2D,
+            'convK2D': convK2D,
+            'transfer2D_CPE': transfer2D_CPE,
+}
+        return dico_results
+
+
+
+
+
+    def _online_plot(self, dico_results):
+
+        transfer2D_CPE = dico_results['transfer2D_CPE']
+        transfer2D_EKr = dico_results['transfer2D_EKr']
+        transfer2D_EKd = dico_results['transfer2D_EKd']
+        transfer2D_EK = transfer2D_EKr + transfer2D_EKd
+        transfer2D_EAr = dico_results['transfer2D_EAr']
+        transfer2D_EAd = dico_results['transfer2D_EAd']
+        transfer2D_EA = transfer2D_EAr + transfer2D_EAd
+        convP2D = dico_results['convP2D']
+        convK2D = dico_results['convK2D']
+        khE = self.oper.khE
+        PiCPE = cumsum_inv(transfer2D_CPE)*self.oper.deltakh
+        PiEK = cumsum_inv(transfer2D_EK)*self.oper.deltakh
+        PiEA = cumsum_inv(transfer2D_EA)*self.oper.deltakh
+        CCP = cumsum_inv(convP2D)*self.oper.deltakh
+        CCK = cumsum_inv(convK2D)*self.oper.deltakh
+
+        self.axe_a.plot(khE+khE[1], PiEK, 'r')
+        self.axe_a.plot(khE+khE[1], PiEA, 'b')
+        self.axe_a.plot(khE+khE[1], CCP, 'y')
+        self.axe_a.plot(khE+khE[1], CCK, 'y--')
+
+        self.axe_b.plot(khE+khE[1], PiCPE, 'g')
+
+
+
+
+    def plot(self, tmin=0, tmax=1000, delta_t=2):
+
+        f = h5py.File(self.path_file, 'r')
+
+        dset_times = f['times']
+        times = dset_times[...]
+        # nt = len(times)
+
+        dset_khE = f['khE']
+        khE = dset_khE[...]
+
+        dset_transfer2D_EKr = f['transfer2D_EKr']
+        dset_transfer2D_EKd = f['transfer2D_EKd']
+        dset_transfer2D_EAr = f['transfer2D_EAr']
+        dset_transfer2D_EAd = f['transfer2D_EAd']
+        dset_transfer2D_EPd = f['transfer2D_EPd']
+        dset_convP2D = f['convP2D']
+        dset_convK2D = f['convK2D']
+        dset_transfer2D_CPE = f['transfer2D_CPE']
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot = 1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+
+        to_print = 'plot(tmin={0}, tmax={1}, delta_t={2:.2f})'.format(
+            tmin, tmax, delta_t)
+        print(to_print)
+
+        to_print = '''plot fluxes 2D
+tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}
+imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}'''.format(
+tmin_plot, tmax_plot, delta_t,
+imin_plot, imax_plot, delta_i_plot)
+        print(to_print)
+
+
+
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.36
+        width_axe = 0.85
+        height_axe = 0.57
+
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('transfers')
+        title = ('energy flux, solver '+self.output.name_solver+
+', nh = {0:5d}'.format(self.nx)+
+', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f)
+)
+        ax1.set_title(title)
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('linear')
+
+
+        khE = khE+1
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot, delta_i_plot):
+                transferEKr = dset_transfer2D_EKr[it]
+                transferEAr = dset_transfer2D_EAr[it]
+                PiEKr = cumsum_inv(transferEKr)*self.oper.deltakh
+                PiEAr = cumsum_inv(transferEAr)*self.oper.deltakh
+                PiE = PiEKr + PiEAr
+                ax1.plot(khE, PiE, 'k', linewidth=1)
+
+                convK = dset_convK2D[it]
+                CCK = cumsum_inv(convK)*self.oper.deltakh
+                ax1.plot(khE, CCK, 'y', linewidth=1)
+
+                convP = dset_convP2D[it]
+                CCP = cumsum_inv(convP)*self.oper.deltakh
+                ax1.plot(khE, CCP, 'y--', linewidth=1)
+
+                # print(convK.sum()*self.oper.deltakh,
+                #       convP.sum()*self.oper.deltakh,
+                #       CCP[0], CCK[0])
+
+
+
+
+        transferEKr = dset_transfer2D_EKr[imin_plot:imax_plot].mean(0)
+        transferEKd = dset_transfer2D_EKd[imin_plot:imax_plot].mean(0)
+        transferEAr = dset_transfer2D_EAr[imin_plot:imax_plot].mean(0)
+        transferEAd = dset_transfer2D_EAd[imin_plot:imax_plot].mean(0)
+        transferEPd = dset_transfer2D_EPd[imin_plot:imax_plot].mean(0)
+
+
+        PiEKr = cumsum_inv(transferEKr)*self.oper.deltakh
+        PiEKd = cumsum_inv(transferEKd)*self.oper.deltakh
+        PiEAr = cumsum_inv(transferEAr)*self.oper.deltakh
+        PiEAd = cumsum_inv(transferEAd)*self.oper.deltakh
+        PiEPd = cumsum_inv(transferEPd)*self.oper.deltakh
+
+        PiEK = PiEKr + PiEKd
+        PiEA = PiEAr + PiEAd
+        PiE = PiEK + PiEA
+
+        ax1.plot(khE, PiE, 'k', linewidth=2)
+        ax1.plot(khE, PiEK, 'r', linewidth=2)
+        ax1.plot(khE, PiEA, 'b', linewidth=2)
+
+
+
+        ax1.plot(khE, PiEKr, 'r--', linewidth=2)
+        ax1.plot(khE, PiEKd, 'r:', linewidth=2)
+
+
+        ax1.plot(khE, PiEAr, 'b--', linewidth=2)
+        ax1.plot(khE, PiEAd, 'b:', linewidth=2)
+        # ax1.plot(khE, PiEPd, 'c:', linewidth=1)
+
+
+
+
+
+        convP = dset_convP2D[imin_plot:imax_plot].mean(0)
+        convK = dset_convK2D[imin_plot:imax_plot].mean(0)
+
+        CCP = cumsum_inv(convP)*self.oper.deltakh
+        CCK = cumsum_inv(convK)*self.oper.deltakh
+
+        ax1.plot(khE, CCP, 'y--', linewidth=2)
+        ax1.plot(khE, CCK, 'y', linewidth=2)
+
+#         print(convK.sum()*self.oper.deltakh,
+#               convP.sum()*self.oper.deltakh,
+#               CCP[0], CCK[0],
+#               CCP[1], CCK[1]
+# )
+
+
+
+        dset_transfer2D_Errr = f['transfer2D_Errr']
+        dset_transfer2D_Edrd = f['transfer2D_Edrd']
+        dset_transfer2D_Edrr_rrd = f['transfer2D_Edrr_rrd']
+
+        transferEdrr_rrd = \
+            dset_transfer2D_Edrr_rrd[imin_plot:imax_plot].mean(0)
+        transferErrr = dset_transfer2D_Errr[imin_plot:imax_plot].mean(0)
+        transferEdrd = dset_transfer2D_Edrd[imin_plot:imax_plot].mean(0)
+
+        Pi_drr_rrd = cumsum_inv(transferEdrr_rrd)*self.oper.deltakh
+        Pi_rrr = cumsum_inv(transferErrr)*self.oper.deltakh
+        Pi_drd = cumsum_inv(transferEdrd)*self.oper.deltakh
+
+        ax1.plot(khE, Pi_drr_rrd, 'm:', linewidth=1)
+        ax1.plot(khE, Pi_rrr, 'm--', linewidth=1)
+        ax1.plot(khE, Pi_drd, 'm-.', linewidth=1)
+
+
+
+        dset_transfer2D_Eddd = f['transfer2D_Eddd']
+        dset_transfer2D_Erdr = f['transfer2D_Erdr']
+        dset_transfer2D_Eddr_rdd = f['transfer2D_Eddr_rdd']
+        dset_transfer2D_Eudeu = f['transfer2D_Eudeu']
+
+
+        transferEddr_rdd = \
+            dset_transfer2D_Eddr_rdd[imin_plot:imax_plot].mean(0)
+        transferEddd = dset_transfer2D_Eddd[imin_plot:imax_plot].mean(0)
+        transferErdr = dset_transfer2D_Erdr[imin_plot:imax_plot].mean(0)
+
+        transferEudeu = dset_transfer2D_Eudeu[imin_plot:imax_plot].mean(0)
+
+        Pi_ddr_rdd = cumsum_inv(transferEddr_rdd)*self.oper.deltakh
+        Pi_ddd = cumsum_inv(transferEddd)*self.oper.deltakh
+        Pi_rdr = cumsum_inv(transferErdr)*self.oper.deltakh
+
+        Pi_udeu = cumsum_inv(transferEudeu)*self.oper.deltakh
+
+
+        ax1.plot(khE, Pi_ddr_rdd, 'c:', linewidth=1)
+        ax1.plot(khE, Pi_ddd, 'c--', linewidth=1)
+        ax1.plot(khE, Pi_rdr, 'c-.', linewidth=1)
+
+        ax1.plot(khE, Pi_udeu, 'g', linewidth=1)
+
+
+
+
+
+        z_bottom_axe = 0.07
+        height_axe = 0.17
+        size_axe[1] = z_bottom_axe
+        size_axe[3] = height_axe
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('$k_h$')
+        ax2.set_ylabel('transfers')
+        title = ('Charney PE flux')
+        ax2.set_title(title)
+        ax2.hold(True)
+        ax2.set_xscale('log')
+        ax2.set_yscale('linear')
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot,imax_plot+1,delta_i_plot):
+                transferCPE = dset_transfer2D_CPE[it]
+                PiCPE = cumsum_inv(transferCPE)*self.oper.deltakh
+                ax2.plot(khE, PiCPE, 'g', linewidth=1)
+
+        transferCPE = dset_transfer2D_CPE[imin_plot:imax_plot].mean(0)
+        PiCPE = cumsum_inv(transferCPE)*self.oper.deltakh
+
+        ax2.plot(khE, PiCPE, 'm', linewidth=2)
+
+        f.close()
+
+
+
+
+class SpectralEnergyBudgetMSW1l(SpectralEnergyBudgetSW1l):
+    """Save and plot spectra."""
+
+
+
+
+    def compute(self):
+        """compute spectral energy budget the one time."""
+        oper = self.sim.oper
+
+        # ux = self.sim.state.state_phys['ux']
+        # uy = self.sim.state.state_phys['uy']
+
+        ux_fft = self.sim.state.state_fft['ux_fft']
+        uy_fft = self.sim.state.state_fft['uy_fft']
+        eta_fft = self.sim.state.state_fft['eta_fft']
+
+        rot_fft = oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        urx_fft, ury_fft = oper.vecfft_from_rotfft(rot_fft)
+        del(rot_fft)
+        urx = oper.ifft2(urx_fft)
+        ury = oper.ifft2(ury_fft)
+
+        q_fft, div_fft, a_fft = \
+            self.oper.qdafft_from_uxuyetafft(ux_fft, uy_fft, eta_fft)
+
+        udx_fft, udy_fft = oper.vecfft_from_divfft(div_fft)
+
+        if self.params.f != 0:
+            ugx_fft, ugy_fft, etag_fft = \
+                self.oper.uxuyetafft_from_qfft(q_fft)
+            uax_fft, uay_fft, etaa_fft = \
+                self.oper.uxuyetafft_from_afft(a_fft)
+            # velocity influenced by linear terms
+            u_infl_lin_x = udx_fft + uax_fft
+            u_infl_lin_y = udy_fft + uay_fft
+
+
+
+
+        # compute flux of Charney PE
+        Fq_fft = self.fnonlinfft_from_uxuy_funcfft(urx, ury, q_fft)
+
+        transferCPE_fft = inner_prod(q_fft, Fq_fft)
+        del(q_fft, Fq_fft)
+
+        transfer2D_CPE = self.spectrum2D_from_fft(transferCPE_fft)
+        del(transferCPE_fft)
+
+#         print(
+# ('sum(transfer2D_CPE) = {0:9.4e} ; sum(abs(transfer2D_CPE)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_CPE),
+# np.sum(abs(transfer2D_CPE)))
+# )
+
+
+        Feta_fft = self.fnonlinfft_from_uxuy_funcfft(urx, ury, eta_fft)
+        transferEA_fft = self.c2*inner_prod(eta_fft, Feta_fft)
+        del(Feta_fft)
+        transfer2D_EA = self.spectrum2D_from_fft(transferEA_fft)
+        del(transferEA_fft)
+
+#         print(
+# ('sum(transfer2D_EA) = {0:9.4e} ; sum(abs(transfer2D_EA)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_EA),
+# np.sum(abs(transfer2D_EA)))
+# )
+
+
+        convA_fft = self.c2*inner_prod(eta_fft, div_fft)
+        convA2D = self.spectrum2D_from_fft(convA_fft)
+        del(convA_fft)
+
+
+        Fxrr_fft = self.fnonlinfft_from_uxuy_funcfft(urx, ury, urx_fft)
+        Fyrr_fft = self.fnonlinfft_from_uxuy_funcfft(urx, ury, ury_fft)
+
+        Fxrd_fft = self.fnonlinfft_from_uxuy_funcfft(urx, ury, udx_fft)
+        Fyrd_fft = self.fnonlinfft_from_uxuy_funcfft(urx, ury, udy_fft)
+
+        transferErrr_fft = (  inner_prod(urx_fft, Fxrr_fft)
+                            + inner_prod(ury_fft, Fyrr_fft)
+                          )
+        transfer2D_Errr = self.spectrum2D_from_fft(transferErrr_fft)
+        del(transferErrr_fft)
+#         print(
+# ('sum(transfer2D_Errr) = {0:9.4e} ; sum(abs(transfer2D_Errr)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Errr),
+# np.sum(abs(transfer2D_Errr)))
+# )
+
+        transferEdrd_fft = (  inner_prod(udx_fft, Fxrd_fft)
+                            + inner_prod(udy_fft, Fyrd_fft)
+                            )
+        transfer2D_Edrd = self.spectrum2D_from_fft(transferEdrd_fft)
+        del(transferEdrd_fft)
+#         print(
+# ('sum(transfer2D_Edrd) = {0:9.4e} ; sum(abs(transfer2D_Edrd)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Edrd),
+# np.sum(abs(transfer2D_Edrd)))
+# )
+
+
+        Clfromqq = (  inner_prod(udx_fft, Fxrr_fft)
+                    + inner_prod(udy_fft, Fyrr_fft)
+                      )
+        transferEdrr_rrd_fft = (  Clfromqq
+                                + inner_prod(urx_fft, Fxrd_fft)
+                                + inner_prod(ury_fft, Fyrd_fft)
+                                  )
+        Clfromqq = self.spectrum2D_from_fft(Clfromqq)
+        transfer2D_Edrr_rrd = self.spectrum2D_from_fft(transferEdrr_rrd_fft)
+        del(transferEdrr_rrd_fft)
+#         print(
+# ('sum(transfer2D_Edrr_rrd) = {0:9.4e} ; '
+# 'sum(abs(transfer2D_Edrr_rrd)) = {1:9.4e}'
+# ).format(
+# np.sum(transfer2D_Edrr_rrd),
+# np.sum(abs(transfer2D_Edrr_rrd)))
+# )
+
+        transfer2D_EK = transfer2D_Errr + transfer2D_Edrd +transfer2D_Edrr_rrd
+
+        dico_results = {
+            'transfer2D_EK': transfer2D_EK,
+            'transfer2D_Errr': transfer2D_Errr,
+            'transfer2D_Edrd': transfer2D_Edrd,
+            'Clfromqq': Clfromqq,
+            'transfer2D_Edrr_rrd': transfer2D_Edrr_rrd,
+            'transfer2D_EA': transfer2D_EA,
+            'convA2D': convA2D,
+            'transfer2D_CPE': transfer2D_CPE}
+        return dico_results
+
+
+
+
+
+    def _online_plot(self, dico_results):
+
+        transfer2D_CPE = dico_results['transfer2D_CPE']
+        transfer2D_EK = dico_results['transfer2D_EK']
+        transfer2D_EA = dico_results['transfer2D_EA']
+        convA2D = dico_results['convA2D']
+        khE = self.oper.khE
+        PiCPE = cumsum_inv(transfer2D_CPE)*self.oper.deltakh
+        PiEK = cumsum_inv(transfer2D_EK)*self.oper.deltakh
+        PiEA = cumsum_inv(transfer2D_EA)*self.oper.deltakh
+        CCA = cumsum_inv(convA2D)*self.oper.deltakh
+        self.axe_a.plot(khE+khE[1], PiEK, 'r')
+        self.axe_a.plot(khE+khE[1], PiEA, 'b')
+        self.axe_a.plot(khE+khE[1], CCA, 'y')
+        self.axe_b.plot(khE+khE[1], PiCPE, 'g')
+
+
+    def plot(self, tmin=0, tmax=1000, delta_t=2):
+
+        f = h5py.File(self.path_file, 'r')
+
+        dset_times = f['times']
+        dset_khE = f['khE']
+        khE = dset_khE[...]
+        # khE = khE+khE[1]
+
+        dset_transfer2D_EK = f['transfer2D_EK']
+        dset_transfer2D_Errr = f['transfer2D_Errr']
+        dset_transfer2D_Edrd = f['transfer2D_Edrd']
+        dset_transfer2D_Edrr_rrd = f['transfer2D_Edrr_rrd']
+        dset_transfer2D_EA = f['transfer2D_EA']
+        dset_convA2D = f['convA2D']
+        dset_transfer2D_CPE = f['transfer2D_CPE']
+
+        times = dset_times[...]
+        nt = len(times)
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot=1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+
+        to_print = 'plot(tmin={0}, tmax={1}, delta_t={2:.2f})'.format(
+            tmin, tmax, delta_t)
+        print(to_print)
+
+        to_print = '''plot fluxes 2D
+tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}
+imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}'''.format(
+tmin_plot, tmax_plot, delta_t,
+imin_plot, imax_plot, delta_i_plot)
+        print(to_print)
+
+
+
+
+        x_left_axe = 0.12
+        z_bottom_axe = 0.36
+        width_axe = 0.85
+        height_axe = 0.57
+
+        size_axe = [x_left_axe, z_bottom_axe,
+                    width_axe, height_axe]
+        fig, ax1 = self.output.figure_axe(size_axe=size_axe)
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('transfers')
+        title = ('energy flux, solver '+self.output.name_solver+
+', nh = {0:5d}'.format(self.nx)+
+', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f)
+)
+        ax1.set_title(title)
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('linear')
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot, delta_i_plot):
+                transferEK = dset_transfer2D_EK[it]
+                transferEA = dset_transfer2D_EA[it]
+                PiEK = cumsum_inv(transferEK)*self.oper.deltakh
+                PiEA = cumsum_inv(transferEA)*self.oper.deltakh
+                PiE = PiEK + PiEA
+                ax1.plot(khE, PiE, 'k', linewidth=1)
+
+        transferEK = dset_transfer2D_EK[imin_plot:imax_plot].mean(0)
+        transferEA = dset_transfer2D_EA[imin_plot:imax_plot].mean(0)
+        PiEK = cumsum_inv(transferEK)*self.oper.deltakh
+        PiEA = cumsum_inv(transferEA)*self.oper.deltakh
+        PiE = PiEK + PiEA
+
+        ax1.plot(khE, PiE, 'k', linewidth=2)
+        ax1.plot(khE, PiEK, 'r', linewidth=2)
+        ax1.plot(khE, PiEA, 'b', linewidth=2)
+
+
+        transferEdrr_rrd = \
+            dset_transfer2D_Edrr_rrd[imin_plot:imax_plot].mean(0)
+        transferErrr = dset_transfer2D_Errr[imin_plot:imax_plot].mean(0)
+        transferEdrd = dset_transfer2D_Edrd[imin_plot:imax_plot].mean(0)
+
+        Pi_drr_rrd = cumsum_inv(transferEdrr_rrd)*self.oper.deltakh
+        Pi_rrr = cumsum_inv(transferErrr)*self.oper.deltakh
+        Pi_drd = cumsum_inv(transferEdrd)*self.oper.deltakh
+
+        ax1.plot(khE, Pi_drr_rrd, 'm:', linewidth=1)
+        ax1.plot(khE, Pi_rrr, 'm--', linewidth=1)
+        ax1.plot(khE, Pi_drd, 'm-.', linewidth=1)
+
+
+
+
+        convA2D = dset_convA2D[imin_plot:imax_plot].mean(0)
+        CCA = cumsum_inv(convA2D)*self.oper.deltakh
+
+        ax1.plot(khE+khE[1], CCA, 'y', linewidth=2)
+
+        z_bottom_axe = 0.07
+        height_axe = 0.17
+        size_axe[1] = z_bottom_axe
+        size_axe[3] = height_axe
+        ax2 = fig.add_axes(size_axe)
+        ax2.set_xlabel('$k_h$')
+        ax2.set_ylabel('transfers')
+        title = ('Charney PE flux')
+        ax2.set_title(title)
+        ax2.hold(True)
+        ax2.set_xscale('log')
+        ax2.set_yscale('linear')
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+                transferCPE = dset_transfer2D_CPE[it]
+                PiCPE = cumsum_inv(transferCPE)*self.oper.deltakh
+                ax2.plot(khE, PiCPE, 'g', linewidth=1)
+
+        transferCPE = dset_transfer2D_CPE[imin_plot:imax_plot].mean(0)
+        PiCPE = cumsum_inv(transferCPE)*self.oper.deltakh
+
+        ax2.plot(khE, PiCPE, 'm', linewidth=2)
+
+
diff --git a/fluidsim/solvers/sw1l/output/spectra.py b/fluidsim/solvers/sw1l/output/spectra.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL291dHB1dC9zcGVjdHJhLnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/output/spectra.py
@@ -0,0 +1,351 @@
+import h5py
+
+import numpy as np
+
+from fluidsim.base.output.spectra import Spectra
+
+
+class SpectraSW1l(Spectra):
+    """Save and plot spectra."""
+
+
+    def __init__(self, output):
+        params = output.sim.params
+        self.c2 = params.c2
+        self.f = params.f
+
+        super(SpectraSW1l, self).__init__(output)
+
+
+
+    def init_online_plot(self):
+        fig, axe = self.output.figure_axe(numfig=1000000)
+        self.axe = axe
+        axe.set_xlabel('k_h')
+        axe.set_ylabel('E(k_h)')
+        title = ('spectra, solver '+self.output.name_solver+
+                 ', nh = {0:5d}'.format(self.nx)+
+                 ', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f)
+                 )
+        axe.set_title(title)
+        axe.hold(True)
+
+
+    def compute(self):
+        """compute the values at one time."""
+        # compute 'quantities_fft'
+        energyK_fft, energyA_fft, energyKr_fft = (
+            self.output.compute_energies_fft())
+        ErtelPE_fft, CharneyPE_fft = self.output.compute_PE_fft()
+
+        energy_glin_fft, energy_dlin_fft, energy_alin_fft = \
+            self.output.compute_lin_energies_fft()
+
+
+
+        # compute the spectra 1D
+        spectrum1Dkx_EK, spectrum1Dky_EK = \
+            self.spectra1D_from_fft(energyK_fft)
+        spectrum1Dkx_EA, spectrum1Dky_EA = \
+            self.spectra1D_from_fft(energyA_fft)
+        spectrum1Dkx_EKr, spectrum1Dky_EKr = \
+            self.spectra1D_from_fft(energyKr_fft)
+        spectrum1Dkx_EPE, spectrum1Dky_EPE = \
+            self.spectra1D_from_fft(ErtelPE_fft)
+        spectrum1Dkx_CPE, spectrum1Dky_CPE = \
+            self.spectra1D_from_fft(CharneyPE_fft)
+        spectrum1Dkx_Eglin, spectrum1Dky_Eglin = \
+            self.spectra1D_from_fft(energy_glin_fft)
+        spectrum1Dkx_Edlin, spectrum1Dky_Edlin = \
+            self.spectra1D_from_fft(energy_dlin_fft)
+        spectrum1Dkx_Ealin, spectrum1Dky_Ealin = \
+            self.spectra1D_from_fft(energy_alin_fft)
+
+        dico_spectra1D = {
+            'spectrum1Dkx_EK': spectrum1Dkx_EK,
+            'spectrum1Dky_EK': spectrum1Dky_EK,
+            'spectrum1Dkx_EA': spectrum1Dkx_EA,
+            'spectrum1Dky_EA': spectrum1Dky_EA,
+            'spectrum1Dkx_EKr': spectrum1Dkx_EKr,
+            'spectrum1Dky_EKr': spectrum1Dky_EKr,
+            'spectrum1Dkx_EPE': spectrum1Dkx_EPE,
+            'spectrum1Dky_EPE': spectrum1Dky_EPE,
+            'spectrum1Dkx_CPE': spectrum1Dkx_CPE,
+            'spectrum1Dky_CPE': spectrum1Dky_CPE,
+            'spectrum1Dkx_Eglin': spectrum1Dkx_Eglin,
+            'spectrum1Dky_Eglin': spectrum1Dky_Eglin,
+            'spectrum1Dkx_Edlin': spectrum1Dkx_Edlin,
+            'spectrum1Dky_Edlin': spectrum1Dky_Edlin,
+            'spectrum1Dkx_Ealin': spectrum1Dkx_Ealin,
+            'spectrum1Dky_Ealin': spectrum1Dky_Ealin}
+
+        # compute the spectra 2D
+        spectrum2D_EK = self.spectrum2D_from_fft(energyK_fft)
+        spectrum2D_EA = self.spectrum2D_from_fft(energyA_fft)
+        spectrum2D_EKr = self.spectrum2D_from_fft(energyKr_fft)
+        spectrum2D_EPE = self.spectrum2D_from_fft(ErtelPE_fft)
+        spectrum2D_CPE = self.spectrum2D_from_fft(CharneyPE_fft)
+        spectrum2D_Eglin = self.spectrum2D_from_fft(energy_glin_fft)
+        spectrum2D_Edlin = self.spectrum2D_from_fft(energy_dlin_fft)
+        spectrum2D_Ealin = self.spectrum2D_from_fft(energy_alin_fft)
+
+        dico_spectra2D = {
+            'spectrum2D_EK': spectrum2D_EK,
+            'spectrum2D_EA': spectrum2D_EA,
+            'spectrum2D_EKr': spectrum2D_EKr,
+            'spectrum2D_EPE': spectrum2D_EPE,
+            'spectrum2D_CPE': spectrum2D_CPE,
+            'spectrum2D_Eglin': spectrum2D_Eglin,
+            'spectrum2D_Edlin': spectrum2D_Edlin,
+            'spectrum2D_Ealin': spectrum2D_Ealin}
+
+        return dico_spectra1D, dico_spectra2D
+
+
+
+    def _online_plot(self, dico_spectra1D, dico_spectra2D):
+        if (self.params.oper.nx==self.params.oper.ny
+                and self.params.oper.Lx==self.params.oper.Ly):
+            spectrum2D_EK = dico_spectra2D['spectrum2D_EK']
+            spectrum2D_EA = dico_spectra2D['spectrum2D_EA']
+            spectrum2D_EKr = dico_spectra2D['spectrum2D_EKr']
+            spectrum2D_E = spectrum2D_EK + spectrum2D_EA
+            spectrum2D_EKd = spectrum2D_EK - spectrum2D_EKr
+            khE = self.oper.khE
+            coef_norm = khE**(3.)
+            self.axe.loglog(khE, spectrum2D_E*coef_norm, 'k')
+            self.axe.loglog(khE, spectrum2D_EK*coef_norm, 'r')
+            self.axe.loglog(khE, spectrum2D_EA*coef_norm, 'b')
+            self.axe.loglog(khE, spectrum2D_EKr*coef_norm, 'r--')
+            self.axe.loglog(khE, spectrum2D_EKd*coef_norm, 'r:')
+            lin_inf, lin_sup = self.axe.get_ylim()
+            if lin_inf<10e-6:
+                lin_inf=10e-6
+            self.axe.set_ylim([lin_inf, lin_sup])
+        else:
+            print('you need to implement the ploting '
+                  'of the spectra for this case')
+
+
+
+
+
+    def plot1D(self, tmin=0, tmax=1000, delta_t=2,
+               coef_compensate=3):
+
+        f = h5py.File(self.path_file1D, 'r')
+        dset_times = f['times']
+        times = dset_times[...]
+        # nb_spectra = times.shape[0]
+
+        dset_kxE = f['kxE']
+        # dset_kyE = f['kyE']
+
+        kh = dset_kxE[...]
+
+        dset_spectrum1Dkx_EK = f['spectrum1Dkx_EK']
+        dset_spectrum1Dky_EK = f['spectrum1Dky_EK']
+        dset_spectrum1Dkx_EA = f['spectrum1Dkx_EA']
+        dset_spectrum1Dky_EA = f['spectrum1Dky_EA']
+
+        dset_spectrum1Dkx_EKr = f['spectrum1Dkx_EKr']
+        dset_spectrum1Dky_EKr = f['spectrum1Dky_EKr']
+
+        nt = len(times)
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot=1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        to_print = (
+'plot1D(tmin={0}, tmax={1}, delta_t={2:.2f},'.format(tmin, tmax, delta_t)
++' coef_compensate={0:.3f})'.format(coef_compensate)
+)
+        print(to_print)
+
+        to_print = '''plot 1D spectra
+tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}
+imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}'''.format(
+tmin_plot, tmax_plot, delta_t,
+imin_plot, imax_plot, delta_i_plot)
+        print(to_print)
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('1D spectra')
+        title = ('1D spectra, solver '+self.output.name_solver+
+', nh = {0:5d}'.format(self.nx)+
+', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f)
+)
+        ax1.set_title(title)
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('log')
+
+        coef_norm = kh**(coef_compensate)
+
+        min_to_plot = 1e-16
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+                E_K = (dset_spectrum1Dkx_EK[it]+dset_spectrum1Dky_EK[it])
+                # E_K[E_K<min_to_plot] = 0.
+                E_A = (dset_spectrum1Dkx_EA[it]+dset_spectrum1Dky_EA[it])
+                # E_A[E_A<min_to_plot] = 0.
+                E_tot = E_K + E_A
+
+                E_Kr = (dset_spectrum1Dkx_EKr[it]+dset_spectrum1Dky_EKr[it])
+                # E_Kr[E_Kr<min_to_plot] = 0.
+                E_Kd = E_K - E_Kr
+
+                ax1.plot(kh, E_tot*coef_norm, 'k', linewidth=2)
+                ax1.plot(kh, E_K*coef_norm, 'r', linewidth=1)
+                ax1.plot(kh, E_A*coef_norm, 'b', linewidth=1)
+                ax1.plot(kh, E_Kr*coef_norm, 'r--', linewidth=1)
+                ax1.plot(kh, E_Kd*coef_norm, 'r:', linewidth=1)
+
+
+        E_K = (dset_spectrum1Dkx_EK[imin_plot:imax_plot+1]
+               +dset_spectrum1Dky_EK[imin_plot:imax_plot+1]).mean(0)
+
+        E_A = (dset_spectrum1Dkx_EA[imin_plot:imax_plot+1]
+               +dset_spectrum1Dky_EA[imin_plot:imax_plot+1]).mean(0)
+
+        ax1.plot(kh, E_K*coef_norm, 'r', linewidth=2)
+        ax1.plot(kh, E_A*coef_norm, 'b', linewidth=2)
+
+        ax1.plot(kh, kh**(-3)*coef_norm, 'k', linewidth=1)
+        ax1.plot(kh, 0.01*kh**(-5./3)*coef_norm, 'k--', linewidth=1)
+
+
+
+
+
+
+    def plot2D(self, tmin=0, tmax=1000, delta_t=2,
+               coef_compensate=3):
+
+        f = h5py.File(self.path_file2D, 'r')
+        dset_times = f['times']
+        nb_spectra = dset_times.shape[0]
+        times = dset_times[...]
+        nt = len(times)
+
+        dset_khE = f['khE']
+        kh = dset_khE[...]
+
+        dset_spectrumEK = f['spectrum2D_EK']
+        dset_spectrumEA = f['spectrum2D_EA']
+        dset_spectrumEKr = f['spectrum2D_EKr']
+
+        delta_t_save = np.mean(times[1:]-times[0:-1])
+        delta_i_plot = int(np.round(delta_t/delta_t_save))
+        if delta_i_plot == 0 and delta_t != 0.:
+            delta_i_plot=1
+        delta_t = delta_i_plot*delta_t_save
+
+        imin_plot = np.argmin(abs(times-tmin))
+        imax_plot = np.argmin(abs(times-tmax))
+
+        tmin_plot = times[imin_plot]
+        tmax_plot = times[imax_plot]
+
+        to_print = (
+'plot2D(tmin={0}, tmax={1}, delta_t={2:.2f},'.format(tmin, tmax, delta_t)
++' coef_compensate={0:.3f})'.format(coef_compensate)
+)
+        print(to_print)
+
+        to_print = '''plot 2D spectra
+tmin = {0:8.6g} ; tmax = {1:8.6g} ; delta_t = {2:8.6g}
+imin = {3:8d} ; imax = {4:8d} ; delta_i = {5:8d}'''.format(
+tmin_plot, tmax_plot, delta_t,
+imin_plot, imax_plot, delta_i_plot)
+        print(to_print)
+
+        fig, ax1 = self.output.figure_axe()
+        ax1.set_xlabel('$k_h$')
+        ax1.set_ylabel('2D spectra')
+        title = ('2D spectra, solver '+self.output.name_solver+
+', nh = {0:5d}'.format(self.nx)+
+', c = {0:.4g}, f = {1:.4g}'.format(np.sqrt(self.c2), self.f)
+)
+        ax1.set_title(title)
+        ax1.hold(True)
+        ax1.set_xscale('log')
+        ax1.set_yscale('log')
+
+        coef_norm = kh**coef_compensate
+
+        if delta_t != 0.:
+            for it in xrange(imin_plot, imax_plot+1, delta_i_plot):
+                EK = dset_spectrumEK[it]
+                EA = dset_spectrumEA[it]
+                EKr = dset_spectrumEKr[it]
+
+                EK[EK<10e-16] = 0.
+                EA[EA<10e-16] = 0.
+                EKr[EKr<10e-16] = 0.
+
+                E_tot = EK + EA
+                EKd = EK - EKr
+
+                ax1.plot(kh, E_tot*coef_norm, 'k', linewidth=1)
+                ax1.plot(kh, EK*coef_norm, 'r', linewidth=1)
+                ax1.plot(kh, EA*coef_norm, 'b', linewidth=1)
+                ax1.plot(kh, EKr*coef_norm, 'r--', linewidth=1)
+                ax1.plot(kh, EKd*coef_norm, 'r:', linewidth=1)
+
+        EK = dset_spectrumEK[imin_plot:imax_plot+1].mean(0)
+        EA = dset_spectrumEA[imin_plot:imax_plot+1].mean(0)
+        EKr = dset_spectrumEKr[imin_plot:imax_plot+1].mean(0)
+
+
+        EK[abs(EK)<10e-16] = 0.
+        EA[abs(EA)<10e-16] = 0.
+        EKr[abs(EKr)<10e-16] = 0.
+
+        E_tot = EK + EA
+        EKd = EK - EKr
+
+
+        ax1.plot(kh, E_tot*coef_norm, 'k', linewidth=4)
+        ax1.plot(kh, EK*coef_norm, 'r', linewidth=2)
+        ax1.plot(kh, EA*coef_norm, 'b', linewidth=2)
+        ax1.plot(kh, EKr*coef_norm, 'r--', linewidth=2)
+        ax1.plot(kh, EKd*coef_norm, 'r:', linewidth=2)
+
+        ax1.plot(kh, -EK*coef_norm, 'm', linewidth=2)
+        ax1.plot(kh, -EKd*coef_norm, 'm:', linewidth=2)
+
+
+
+
+
+        if self.sim.info.solver.short_name.startswith('SW1l'):
+            dset_spectrumEdlin = f['spectrum2D_Edlin']
+            Edlin = dset_spectrumEdlin[imin_plot:imax_plot+1].mean(0)
+            ax1.plot(kh, Edlin*coef_norm, 'y:', linewidth=1)
+
+        if self.params.f != 0:
+            dset_spectrumEglin = f['spectrum2D_Eglin']
+            Eglin = dset_spectrumEglin[imin_plot:imax_plot+1].mean(0)
+            ax1.plot(kh, Eglin*coef_norm, 'c', linewidth=1)
+
+            dset_spectrumEalin = f['spectrum2D_Ealin']
+            Ealin = dset_spectrumEalin[imin_plot:imax_plot+1].mean(0)
+            ax1.plot(kh, Ealin*coef_norm, 'y', linewidth=1)
+
+
+
+
+        ax1.plot(kh, kh**(-3)*coef_norm, 'k--', linewidth=1)
+        ax1.plot(kh, 0.01*kh**(-5./3)*coef_norm, 'k-.', linewidth=1)
diff --git a/fluidsim/solvers/sw1l/solver.py b/fluidsim/solvers/sw1l/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/sw1l/solver.py
@@ -0,0 +1,169 @@
+"""Solver one-layer shallow-water (Saint Venant) equations.
+===========================================================
+
+"""
+
+from fluidsim.operators.setofvariables import SetOfVariables
+from fluidsim.base.solvers.pseudo_spect import (
+    SimulBasePseudoSpectral, InfoSolverPseudoSpectral)
+
+from fluiddyn.util import mpi
+
+
+class InfoSolverSW1l(InfoSolverPseudoSpectral):
+    """Information about the solver SW1l."""
+    def __init__(self, **kargs):
+        super(InfoSolverSW1l, self).__init__(**kargs)
+
+        if 'tag' in kargs and kargs['tag'] == 'solver':
+
+            package = 'fluidsim.solvers.sw1l'
+
+            self.module_name = package + '.solver'
+            self.class_name = 'Simul'
+            self.short_name = 'SW1l'
+
+            classes = self.classes
+
+            classes.State.module_name = package + '.state'
+            classes.State.class_name = 'StateSW1l'
+
+            classes.InitFields.module_name = package + '.init_fields'
+            classes.InitFields.class_name = 'InitFieldsSW1l'
+
+            classes.Output.module_name = package + '.output'
+            classes.Output.class_name = 'OutputSW1l'
+
+            classes.Forcing.module_name = package + '.forcing'
+            classes.Forcing.class_name = 'ForcingSW1l'
+
+
+info_solver = InfoSolverSW1l(tag='solver')
+info_solver.complete_with_classes()
+
+
+class Simul(SimulBasePseudoSpectral):
+    """A solver of the shallow-water 1 layer equations (SW1l)"""
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        SimulBasePseudoSpectral._complete_params_with_default(params)
+
+        attribs = {'f': 0,
+                   'c2': 20,
+                   'kd2': 0}
+        params.set_attribs(attribs)
+
+    def __init__(self, params, info_solver=info_solver):
+        # Parameter(s) specific to this solver
+        params.kd2 = params.f**2/params.c2
+
+        # first, the common initialisations
+        super(Simul, self).__init__(params, info_solver)
+
+        if mpi.rank == 0:
+            self.output.print_stdout(
+                'c2 = {0:6.5g} ; f = {1:6.5g} ; kd2 = {2:6.5g}'.format(
+                    params.c2, params.f, params.kd2))
+
+    def tendencies_nonlin(self, state_fft=None):
+        oper = self.oper
+        fft2 = oper.fft2
+
+        if state_fft is None:
+            state_phys = self.state.state_phys
+        else:
+            state_phys = self.state.return_statephys_from_statefft(state_fft)
+
+        ux = state_phys['ux']
+        uy = state_phys['uy']
+        eta = state_phys['eta']
+        rot = state_phys['rot']
+        rot_abs = rot + self.params.f
+
+        F1x = +rot_abs*uy
+        F1y = -rot_abs*ux
+        gradx_fft, grady_fft = oper.gradfft_from_fft(
+            fft2(self.params.c2*eta + (ux**2+uy**2)/2))
+        oper.dealiasing(gradx_fft, grady_fft)
+        Fx_fft = fft2(F1x) - gradx_fft
+        Fy_fft = fft2(F1y) - grady_fft
+
+        Feta_fft = -oper.divfft_from_vecfft(fft2((eta+1)*ux),
+                                            fft2((eta+1)*uy))
+        oper.dealiasing(Fx_fft, Fy_fft, Feta_fft)
+
+        # # for verification conservation energy
+        # Fx   = oper.ifft2(Fx_fft)
+        # Fy   = oper.ifft2(Fy_fft)
+        # Feta = oper.ifft2(Feta_fft)
+        # A = ( Feta*(ux**2+uy**2)/2
+        #       + (1+eta)*(ux*Fx+uy*Fy)
+        #       + self.params.c2*eta*Feta )
+        # A_fft = fft2(A)
+        # if mpi.rank == 0:
+        #     print 'should be zero =', A_fft[0,0]
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin'
+            )
+        tendencies_fft['ux_fft'] = Fx_fft
+        tendencies_fft['uy_fft'] = Fy_fft
+        tendencies_fft['eta_fft'] = Feta_fft
+
+        if self.params.FORCING:
+            tendencies_fft += self.forcing.get_forcing()
+
+        return tendencies_fft
+
+
+if __name__ == "__main__":
+
+    import numpy as np
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 32
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 2.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.FORCING = False
+    params.forcing.type = 'Random'
+
+    params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 0.5
+    params.output.periods_save.spectra = 0.5
+    params.output.periods_save.spect_energy_budg = 0.5
+    params.output.periods_save.increments = 0.5
+    params.output.periods_save.pdf = 0.5
+    params.output.periods_save.time_signals_fft = True
+
+    params.output.periods_plot.phys_fields = 0.
+
+    params.output.phys_fields.field_to_plot = 'rot'
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    # sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/solvers/sw1l/state.py b/fluidsim/solvers/sw1l/state.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy9zdzFsL3N0YXRlLnB5
--- /dev/null
+++ b/fluidsim/solvers/sw1l/state.py
@@ -0,0 +1,138 @@
+"""
+The module :mod:`stateSW1l` supplies the class :class:`StateSW1l`.
+"""
+
+import numpy as np
+
+from fluidsim.operators.setofvariables import SetOfVariables
+from fluidsim.base.state import StatePseudoSpectral
+
+from fluiddyn.util import mpi
+
+
+class StateSW1l(StatePseudoSpectral):
+    """
+    The class :class:`StateSW1l` contains the variables corresponding
+    to the state and handles the access to other fields for the solver
+    SW1l.
+    """
+
+    @staticmethod
+    def _complete_info_solver(info_solver):
+        """Complete the ContainerXML info_solver.
+
+        This is a static method!
+        """
+        info_solver.classes.State.set_attribs({
+            'keys_state_fft': ['ux_fft', 'uy_fft', 'eta_fft'],
+            'keys_state_phys': ['ux', 'uy', 'eta', 'rot'],
+            'keys_computable': [],
+            'keys_phys_needed': ['ux', 'uy', 'eta'],
+            'keys_linear_eigenmodes': ['q_fft', 'a_fft', 'd_fft']})
+
+
+    def compute(self, key, SAVE_IN_DICT=True, RAISE_ERROR=True):
+        it = self.sim.time_stepping.it
+        if (key in self.vars_computed and it == self.it_computed[key]):
+            return self.vars_computed[key]
+
+        if key == 'Jx':
+            ux = self.state_phys['ux']
+            eta = self.state_phys['eta']
+            h = 1 + eta
+            result = h*ux
+        elif key == 'Jy':
+            uy = self.state_phys['uy']
+            eta = self.state_phys['eta']
+            h = 1 + eta
+            result = h*uy
+        elif key == 'Jx_fft':
+            Jx = self.compute('Jx')
+            result = self.oper.fft2(Jx)
+        elif key == 'Jy_fft':
+            Jy = self.compute('Jy')
+            result = self.oper.fft2(Jy)
+        elif key == 'rot_fft':
+            ux_fft = self.state_fft['ux_fft']
+            uy_fft = self.state_fft['uy_fft']
+            result = self.oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        elif key == 'div_fft':
+            ux_fft = self.state_fft['ux_fft']
+            uy_fft = self.state_fft['uy_fft']
+            result = self.oper.divfft_from_vecfft(ux_fft, uy_fft)
+        elif key == 'div':
+            div_fft = self.compute('div_fft')
+            result = self.oper.ifft2(div_fft)
+        elif key == 'q':
+            rot = self.state_phys['rot']
+            eta = self.state_phys['eta']
+            result = rot-self.param.f*eta
+        elif key == 'h':
+            eta = self.state_phys['eta']
+            result = 1 + eta
+
+        elif key == 'Floc':
+            h = self.compute('h')
+            ux = self.state_phys['ux']
+            uy = self.state_phys['uy']
+            result = np.sqrt((ux**2 + uy**2)/(self.sim.param.c2*h))
+
+        else:
+            to_print = 'Do not know how to compute "'+key+'".'
+            if RAISE_ERROR:
+                raise ValueError(to_print)
+            else:
+                if mpi.rank == 0:
+                    print(to_print
+                          +'\nreturn an array of zeros.')
+                    
+                result = self.oper.constant_arrayX(value=0.)
+
+        if SAVE_IN_DICT:
+            self.vars_computed[key] = result
+            self.it_computed[key] = it
+
+        return result
+
+
+
+
+    def statefft_from_statephys(self):
+        """Compute the state in Fourier space."""
+        ux = self.state_phys['ux']
+        uy = self.state_phys['uy']
+        eta = self.state_phys['eta']
+        self.state_fft['ux_fft'] = self.oper.fft2(ux)
+        self.state_fft['uy_fft'] = self.oper.fft2(uy)
+        self.state_fft['eta_fft'] = self.oper.fft2(eta)
+
+    def statephys_from_statefft(self):
+        """Compute the state in physical space."""
+        ifft2 = self.oper.ifft2
+        ux_fft = self.state_fft['ux_fft']
+        uy_fft = self.state_fft['uy_fft']
+        eta_fft = self.state_fft['eta_fft']
+        self.state_phys['ux'] = ifft2(ux_fft)
+        self.state_phys['uy'] = ifft2(uy_fft)
+        self.state_phys['eta'] = ifft2(eta_fft)
+        rot_fft = self.oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        self.state_phys['rot'] = ifft2(rot_fft)
+
+    def return_statephys_from_statefft(self, state_fft=None):
+        """Return the state in physical space."""
+        ifft2 = self.oper.ifft2
+        if state_fft is None:
+            state_fft = self.state_fft
+        ux_fft = state_fft['ux_fft']
+        uy_fft = state_fft['uy_fft']
+        eta_fft = state_fft['eta_fft']
+        state_phys = SetOfVariables(like_this_sov=self.state_phys)
+        state_phys['ux'] = ifft2(ux_fft)
+        state_phys['uy'] = ifft2(uy_fft)
+        state_phys['eta'] = ifft2(eta_fft)
+
+        rot_fft = self.oper.rotfft_from_vecfft(ux_fft, uy_fft)
+        state_phys['rot'] = ifft2(rot_fft)
+
+        return state_phys
+
diff --git a/fluidsim/solvers/test/__init__.py b/fluidsim/solvers/test/__init__.py
new file mode 100644
diff --git a/fluidsim/solvers/test/test_solvers.py b/fluidsim/solvers/test/test_solvers.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy90ZXN0L3Rlc3Rfc29sdmVycy5weQ==
--- /dev/null
+++ b/fluidsim/solvers/test/test_solvers.py
@@ -0,0 +1,65 @@
+
+import unittest
+import shutil
+
+import fluiddyn as fld
+
+from fluiddyn.io import stdout_redirected
+
+
+def run_mini_simul(key_solver):
+
+    solver = fld.simul.import_module_solver_from_key(key_solver)
+    params = fld.simul.create_params(solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 64
+    params.oper.nx = nh
+    params.oper.ny = nh
+    Lh = 6.
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    params.oper.coef_dealiasing = 2./3
+    params.nu_8 = 2.
+
+    try:
+        params.f = 1.
+        params.c2 = 200.
+    except KeyError:
+        pass
+
+    params.time_stepping.t_end = 0.5
+
+    params.init_fields.type_flow_init = 'DIPOLE'
+    params.output.HAS_TO_SAVE = False
+
+    with stdout_redirected():
+        sim = solver.Simul(params)
+        sim.time_stepping.start()
+
+    # clean by removing the directory
+    shutil.rmtree(sim.output.path_run)
+
+
+class TestSolvers(unittest.TestCase):
+    def test_ns2d(self):
+        """Should be able to run a base experiment."""
+        run_mini_simul('NS2D')
+
+    def test_sw1l(self):
+        """Should be able to run a base experiment."""
+        run_mini_simul('SW1l')
+
+    def test_sw1l_onlywaves(self):
+        """Should be able to run a base experiment."""
+        run_mini_simul('SW1l.onlywaves')
+
+    def test_sw1l_exactlin(self):
+        """Should be able to run a base experiment."""
+        run_mini_simul('SW1l.exactlin')
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fluidsim/solvers/waves2d/__init__.py b/fluidsim/solvers/waves2d/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy93YXZlczJkL19faW5pdF9fLnB5
--- /dev/null
+++ b/fluidsim/solvers/waves2d/__init__.py
@@ -0,0 +1,13 @@
+"""2D waves solvers (:mod:`fluidsim.solvers.waves2d`)
+===========================================================
+
+.. currentmodule:: fluidsim.solvers.waves2d
+
+Provides:
+
+.. autosummary::
+   :toctree:
+
+   solver
+
+"""
diff --git a/fluidsim/solvers/waves2d/solver.py b/fluidsim/solvers/waves2d/solver.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vc29sdmVycy93YXZlczJkL3NvbHZlci5weQ==
--- /dev/null
+++ b/fluidsim/solvers/waves2d/solver.py
@@ -0,0 +1,187 @@
+"""2D waves solver (:mod:`fluidsim.solvers.waves2d.solver`)
+==================================================================
+
+.. autoclass:: Simul
+   :members:
+   :private-members:
+
+"""
+
+from fluidsim.operators.setofvariables import SetOfVariables
+
+from fluidsim.base.solvers.pseudo_spect import (
+    SimulBasePseudoSpectral, InfoSolverPseudoSpectral)
+
+
+info_solver = InfoSolverPseudoSpectral()
+
+package = 'fluidsim.solvers.waves2d'
+info_solver.module_name = package + '.solver'
+info_solver.class_name = 'Simul'
+info_solver.short_name = 'Waves2d'
+
+classes = info_solver.classes
+
+# classes.State.module_name = package + '.state'
+# classes.State.class_name = 'StateWaves'
+
+# classes.InitFields.module_name = package + '.init_fields'
+# classes.InitFields.class_name = 'InitFieldsWaves'
+
+# classes.Output.module_name = package + '.output'
+# classes.Output.class_name = 'Output'
+
+# classes.Forcing.module_name = package + '.forcing'
+# classes.Forcing.class_name = 'ForcingNS2D'
+
+
+info_solver.complete_with_classes()
+
+
+class Simul(SimulBasePseudoSpectral):
+    r"""Pseudo-spectral solver for equations of 2D waves.
+
+    Notes
+    -----
+
+    .. |p| mathmacro:: \partial
+
+    This class is dedicated to solve wave 2D equations:
+
+    .. math::
+       \p_t \hat f = \hat g - \gamma_f \hat f,
+
+       \p_t \hat g = -\Omega^2 \hat f - \gamma_g \hat g,
+
+    This purely linear wave equation can alternatively be written as
+    as :math:`\p_t X = M X`, with
+
+    .. math::
+
+       X = \begin{pmatrix} \hat f \\ \hat g \end{pmatrix}
+       \ \ \text{and}\ \
+       M = \begin{pmatrix} -\gamma_f & 1 \\
+       -\Omega^2 & -\gamma_g \end{pmatrix},
+
+    where the three coefficients usually depend on the wavenumber.
+    The eigenvalues are :math:`\sigma_\pm = - \bar \gamma \pm i \tilde
+    \Omega`, where :math:`\bar \gamma = (\gamma_f + \gamma_g)/2` and
+
+    .. math::
+
+       \tilde \Omega = \Omega \sqrt{1 +
+       \frac{1}{\Omega^2}(\gamma_f\gamma_g - \bar \gamma^2)}.
+
+    The (not normalized) eigenvectors can be expressed as
+
+    .. math::
+
+       V_\pm = \begin{pmatrix} 1 \\ \sigma_\pm + \gamma_f \end{pmatrix}.
+
+    The state can be represented by a vector :math:`A` that verifies
+    :math:`X = V A`, where :math:`V` is the base matrix
+
+    .. math::
+
+       V = \begin{pmatrix} 1 & 1 \\
+       \sigma_+ + \gamma_f & \sigma_- + \gamma_f \end{pmatrix}.
+
+    The inverse base matrix is given by
+
+    .. math::
+
+       V^{-1} = \frac{i}{2\tilde \Omega}
+       \begin{pmatrix}
+       \sigma_- + \gamma_f & -1 \\
+       -\sigma_+ - \gamma_f &  1 \end{pmatrix}.
+
+
+    """
+
+    @staticmethod
+    def _complete_params_with_default(params):
+        """This static method is used to complete the *params* container.
+        """
+        SimulBasePseudoSpectral._complete_params_with_default(params)
+        attribs = {'c2': 1., 'f': 0}
+        params.set_attribs(attribs)
+
+    def __init__(self, params):
+        super(Simul, self).__init__(params, info_solver)
+
+    def tendencies_nonlin(self, state_fft=None):
+
+        tendencies_fft = SetOfVariables(
+            like_this_sov=self.state.state_fft,
+            name_type_variables='tendencies_nonlin')
+
+        tendencies_fft.data[:] = 0.
+
+        return tendencies_fft
+
+    def compute_freq_complex(self, key):
+        if key == 'f_fft':
+            omega = self.oper.constant_arrayK(value=0)
+        elif key == 'g_fft':
+            omega = 1.j*np.sqrt(self.params.f**2 +
+                                self.params.c2*self.oper.K2)
+        return omega
+
+
+if __name__ == "__main__":
+
+    import numpy as np
+
+    import fluiddyn as fld
+
+    params = fld.simul.create_params(info_solver)
+
+    params.short_name_type_run = 'test'
+
+    nh = 32
+    Lh = 2*np.pi
+    params.oper.nx = nh
+    params.oper.ny = nh
+    params.oper.Lx = Lh
+    params.oper.Ly = Lh
+
+    # params.oper.type_fft = 'FFTWPY'
+
+    delta_x = params.oper.Lx/params.oper.nx
+    params.nu_8 = 2.*10e-1*params.forcing.forcing_rate**(1./3)*delta_x**8
+
+    params.time_stepping.t_end = 1.
+
+    params.init_fields.type_flow_init = 'NOISE'
+
+    params.FORCING = True
+    params.forcing.type = 'Random'
+    # 'Proportional'
+    # params.forcing.type_normalize
+
+    # params.output.periods_print.print_stdout = 0.25
+
+    params.output.periods_save.phys_fields = 0.5
+    params.output.periods_save.spectra = 0.5
+    params.output.periods_save.spatial_means = 0.05
+    params.output.periods_save.spect_energy_budg = 0.5
+    params.output.periods_save.increments = 0.5
+
+    params.output.periods_plot.phys_fields = 0.0
+
+    params.output.ONLINE_PLOT_OK = True
+
+    # params.output.spectra.HAS_TO_PLOT_SAVED = True
+    # params.output.spatial_means.HAS_TO_PLOT_SAVED = True
+    # params.output.spect_energy_budg.HAS_TO_PLOT_SAVED = True
+    # params.output.increments.HAS_TO_PLOT_SAVED = True
+
+    params.output.phys_fields.field_to_plot = 'rot'
+
+    sim = Simul(params)
+
+    # sim.output.phys_fields.plot()
+    sim.time_stepping.start()
+    # sim.output.phys_fields.plot()
+
+    fld.show()
diff --git a/fluidsim/util/__init__.py b/fluidsim/util/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vdXRpbC9fX2luaXRfXy5weQ==
--- /dev/null
+++ b/fluidsim/util/__init__.py
@@ -0,0 +1,7 @@
+"""Utilities for the numerical simulations (:mod:`fluidsim.util`)
+=======================================================================
+
+"""
+
+
+
diff --git a/fluidsim/util/util.py b/fluidsim/util/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_Zmx1aWRzaW0vdXRpbC91dGlsLnB5
--- /dev/null
+++ b/fluidsim/util/util.py
@@ -0,0 +1,405 @@
+"""Utilities for the numerical simulations (:mod:`fluidsim.util`)
+=======================================================================
+
+"""
+
+from __future__ import division, print_function
+
+import os as _os
+import glob as _glob
+import numpy as _np
+from copy import deepcopy as _deepcopy
+
+import h5py as _h5py
+
+import operator as _operator
+import numbers as _numbers
+
+from importlib import import_module
+
+
+import fluiddyn as fld
+
+from fluiddyn.util import mpi
+
+from fluidsim import path_dir_results
+
+from fluidsim.base.params import (
+    load_info_solver, load_params_simul, Parameters)
+
+
+def module_solver_from_key(key=None):
+    """Return the string corresponding to a module solver."""
+    key = key.lower()
+    keys = [
+        'ns2d', 'sw1l', 'burgers', 'plate2d',
+        'sw1l.exactlin', 'sw1l.onlywaves', 'sw1l.modified', 'sw1l.etaj']
+
+    if key in keys:
+        part_path = key
+    else:
+        raise ValueError(
+            'You have to give a proper solver key, name solver given: '+key)
+
+    base_solvers = 'fluidsim.solvers'
+    module_solver = base_solvers+'.'+part_path+'.solver'
+
+    return module_solver
+
+
+def import_module_solver_from_key(key=None):
+    """Import and reload the solver.
+
+    Parameters
+    ----------
+
+    key : str
+        The short name of a solver. Can be 'NS2D', 'SW1l' or 'MSW1l'.
+
+    """
+    return import_module(module_solver_from_key(key))
+
+
+def pathdir_from_namedir(name_dir=None):
+    """Return the path if a result directory."""
+    if name_dir is None:
+        return _os.getcwd()
+    if name_dir[0] != '/' and name_dir[0] != '~':
+        name_dir = path_dir_results+'/'+name_dir
+    return _os.path.expanduser(name_dir)
+
+
+class ModulesSolvers(dict):
+    """Dictionary to gather imported solvers."""
+    def __init__(self, names_solvers):
+        for key in names_solvers:
+            self[key] = import_module_solver_from_key(key)
+
+
+def name_file_from_time_approx(path_dir, t_approx=None):
+    """Return the file name whose time is the closest to the given time.
+
+    """
+    list_path_files = _glob.glob(path_dir+'/state_phys_t=*')
+    nb_files = len(list_path_files)
+    if nb_files == 0 and mpi.rank == 0:
+        raise ValueError('No state file in the dir\n'+path_dir)
+    times = _np.empty([nb_files])
+    for ii in xrange(nb_files):
+        times[ii] = float(list_path_files[ii][-11:-4])
+    if t_approx is None:
+        t_approx = times.max()
+    i_file = abs(times-t_approx).argmin()
+    name_file = list_path_files[i_file][-24:]
+    return name_file
+
+
+def load_sim_for_plot(path_dir=None):
+    """Create a object Simul from a dir result."""
+    path_dir = pathdir_from_namedir(path_dir)
+    solver = _import_solver_from_path(path_dir)
+    params = load_params_simul(path_dir=path_dir)
+
+    params.path_run = path_dir
+    params.init_fields.type_flow_init = 'CONSTANT'
+    params.ONLY_COARSE_OPER = True
+    params.FORCING = False
+    params.NEW_DIR_RESULTS = False
+    params.SAVE = False
+    sim = solver.Simul(params)
+    return sim
+
+
+def _import_solver_from_path(path_dir):
+    info_solver = load_info_solver(path_dir=path_dir)
+    solver = import_module(info_solver.module_name)
+    return solver
+
+
+def load_state_phys_file(name_dir=None, t_approx=None):
+    """Create a simulation from a file."""
+
+    path_dir = pathdir_from_namedir(name_dir)
+
+    solver = _import_solver_from_path(path_dir)
+
+    # choose the file with the time closer to t_approx
+    name_file = name_file_from_time_approx(path_dir, t_approx)
+    path_file = _os.path.join(path_dir, name_file)
+
+    with _h5py.File(path_file, 'r') as f:
+        params = Parameters(hdf5_object=f['info_simul']['params'])
+
+    params.path_run = path_dir
+    params.NEW_DIR_RESULTS = False
+    params.SAVE = False
+    params.init_fields.type_flow_init = 'LOAD_FILE'
+    params.init_fields.path_file = path_file
+    sim = solver.Simul(params)
+    return sim
+
+
+def modif_resolution_all_dir(t_approx=None,
+                             coef_modif_resol=2,
+                             dir_base=None):
+    """Save files with a modified resolution."""
+    path_base = pathdir_from_namedir(dir_base)
+    list_dir_results = _glob.glob(path_base+'/SE2D*')
+    for path_dir in list_dir_results:
+        modif_resolution_from_dir(name_dir=path_dir,
+                                  t_approx=t_approx,
+                                  coef_modif_resol=coef_modif_resol,
+                                  PLOT=False)
+
+
+def modif_resolution_from_dir(name_dir=None,
+                              t_approx=None,
+                              coef_modif_resol=2,
+                              PLOT=True):
+    """Save a file with a modified resolution."""
+
+    path_dir = pathdir_from_namedir(name_dir)
+
+    if mpi.nb_proc > 1:
+        raise ValueError('Do NOT use this script with MPI !\n'
+                         'The MPI version of get_state_from_obj_simul()\n'
+                         'is not implemented.')
+
+    solver = _import_solver_from_path(path_dir)
+
+    sim = load_state_phys_file(name_dir, t_approx)
+
+    params2 = _deepcopy(sim.params)
+    params2.oper.nx = sim.params.oper.nx*coef_modif_resol
+    params2.oper.ny = sim.params.oper.ny*coef_modif_resol
+    params2.init_fields.type_flow_init = 'CONSTANT'
+
+    sim2 = solver.Simul(params2)
+    sim2.init_fields.get_state_from_obj_simul(sim)
+
+    print(sim2.params.path_run)
+
+    sim2.output.path_run = path_dir+'/State_phys_{0}x{1}'.format(
+        sim2.params.oper.nx, sim2.params.oper.ny)
+    print('Save file in directory\n'+sim2.output.path_run)
+    sim2.output.phys_fields.save(particular_attr='modif_resolution')
+
+    print('The new file is saved.')
+
+    if PLOT:
+        sim.output.phys_fields.plot(numfig=0)
+        sim2.output.phys_fields.plot(numfig=1)
+        fld.show()
+
+
+def times_start_end_from_path(path):
+    """Return the start and end times from a result directory path.
+
+    """
+
+    path_file = path+'/stdout.txt'
+    if not _os.path.exists(path_file):
+        print('Given path does not exist:\n '+path)
+        return 666, 666
+
+    file_stdout = open(path_file, 'r')
+
+    line = ''
+    while not line.startswith('it ='):
+        line = file_stdout.readline()
+
+    words = line.split()
+    t_s = float(words[6])
+
+    # in order to get the informations at the end of the file,
+    # we do not want to read the full file...
+    file_stdout.seek(0, 2)  # go to the end
+    nb_caract = file_stdout.tell()
+    nb_caract_to_read = min(nb_caract, 1000)
+    file_stdout.seek(-nb_caract_to_read, 2)
+    while line != '':
+        if line.startswith('it ='):
+            line_it = line
+        last_line = line
+        line = file_stdout.readline()
+
+    if last_line.startswith('save state_phys'):
+        word = last_line.replace('=', ' ').split()[-1]
+        t_e = float(word.replace('.hd5', ''))
+    else:
+        words = line_it.split()
+        t_e = float(words[6])
+
+    # print('t_s = {0:.3f}, t_e = {1:.3f}'.format(t_s, t_e))
+
+    file_stdout.close()
+
+    return t_s, t_e
+
+
+class SetOfDirResults(object):
+    """Represent a set of result directories."""
+    def __init__(self, arg):
+        if isinstance(arg, str):
+            dir_base = pathdir_from_namedir(arg)
+            paths_results = _glob.glob(dir_base+'/SE2D_*')
+            if len(paths_results) == 0:
+                print('No result directory in the directory\n'+dir_base)
+        else:
+            paths_results = arg
+            for ind, val in enumerate(arg):
+                paths_results[ind] = pathdir_from_namedir(val)
+            if len(paths_results) == 0:
+                print('paths_results empty')
+
+        self.nb_dirs = len(paths_results)
+
+        self.dico_paths = {}
+        self.dico_params = {}
+
+        keys_values = ['c', 'f', 'name_solver', 'FORCING', 'nh']
+        self.dico_values = {}
+        for k in keys_values:
+            self.dico_values[k] = []
+
+        for path_dir in paths_results:
+            path_file = path_dir+'/param_simul.h5'
+
+            name_run = _os.path.split(path_dir)[1]
+
+            if not _os.path.exists(path_file):
+                print('No file param_simul.h5 in dir\n'+path_dir+
+                      'This directory is skipped...')
+                self.nb_dirs -= 1
+            else:
+                self.dico_paths[name_run] = path_dir
+
+                with _h5py.File(path_file, 'r') as f:
+                    name_run2 = f.attrs['name_run']
+                    name_solver = f.attrs['name_solver']
+
+                if name_run != name_run2:
+                    raise ValueError('name_run != name_run2')
+
+                # old code that have to be modified...
+                params = Params(path_dir=path_dir, VERBOSE=False)
+                self.dico_params[name_run] = params
+
+                params.add_a_param('name_solver', name_solver)
+                params.add_a_param('solver', name_solver)
+                params.add_a_param('name_run', name_run)
+                params.add_a_param('nh', params['nx'])
+
+                if 'c2' in params.__dict__ and 'c' not in params.__dict__:
+                    params.add_a_param('c', _np.sqrt(params['c2']))
+
+                for k in keys_values:
+                    if not params[k] in self.dico_values[k]:
+                        self.dico_values[k].append(params[k])
+
+        if self.nb_dirs > 1:
+            for k, v in self.dico_values.iteritems():
+                v.sort()
+                if isinstance(v[0], _numbers.Number):
+                    self.dico_values[k] = _np.array(v)
+
+        self.paths = self.dico_paths.values()
+
+    def dirs_from_values(self, k_sort='c2', **kwargs):
+        """Return a list of dirs from conditions.
+
+        >>> paths = setofdir.dirs_from_values2(
+        >>>    c2=100, f=('>', 1), nh=('=',1920))
+
+        """
+
+        kdirs_corresp = self.dico_params.keys()
+        for k, v in kwargs.iteritems():
+            if isinstance(v, tuple):
+                str_operator = v[0]
+                value = v[1]
+            else:
+                str_operator = '=='
+                value = v
+
+            if str_operator == '==':
+                cond = _operator.eq
+            elif str_operator == '!=':
+                cond = _operator.ne
+            elif str_operator == '<':
+                cond = _operator.lt
+            elif str_operator == '>':
+                cond = _operator.gt
+            elif str_operator == '>=':
+                cond = _operator.le
+            elif str_operator == '<=':
+                cond = _operator.ge
+            else:
+                raise ValueError(
+                    'Supports only the operators ==, !=, >, <, >=, <=')
+
+            kdirs_corresp_temp = [kdir for kdir, params
+                                  in self.dico_params.iteritems()
+                                  if cond(params[k], value)
+                                  ]
+
+            kdirs_corresp = list(
+                set(kdirs_corresp).intersection(kdirs_corresp_temp))
+
+        if len(kdirs_corresp) == 0 and mpi.rank == 0:
+            print('No result directory corresponds to the criteria.')
+
+        kdirs_corresp.sort(key=lambda key: self.dico_params[key][k_sort])
+
+        return kdirs_corresp
+
+
+    def filter_old(self, solver=None, c2=None, f=None,
+                   FORCING=None, nh=None):
+        """Return a filtered SetOfDirResults."""
+        dirs = self.dirs_from_values(solver=solver, c2=c2, f=f,
+                                     FORCING=FORCING, nh=nh)
+        paths = [self.dico_paths[dir_i] for dir_i in dirs]
+        return SetOfDirResults(paths)
+
+
+    def filter(self, **kwargs):
+        """Return a filtered SetOfDirResults from conditions.
+
+        >>> setofdir2 = setofdir.filter(c2=100, f=('>', 1), nh=('=',1920))
+        """
+        dirs = self.dirs_from_values(**kwargs)
+        paths = [self.dico_paths[dir_i] for dir_i in dirs]
+        return SetOfDirResults(paths)
+
+    def path_larger_t_start(self):
+        """Return the path corresponding to the run with larger *t_start*.
+
+        """
+        if len(self.paths) == 1:
+            path = self.paths[0]
+        else:
+            t_s = -1.
+            for path_temp in self.paths:
+                t_s_temp, t_e = times_start_end_from_path(path_temp)
+                if t_s_temp > t_s:
+                    path = path_temp
+                    t_s = t_s_temp
+        return path
+
+    def one_path_from_values(self, **kwargs):
+        """Return one path from parameter values.
+
+        If there are two corresponding runs, a warning is written and
+        the function returns None.
+        """
+        keys_corresp = self.dirs_from_values(**kwargs)
+        if len(keys_corresp) == 1:
+            return self.dico_paths[keys_corresp[0]]
+        elif len(keys_corresp) == 0:
+            print('No directory corresponds to the given values.')
+        elif len(keys_corresp) > 1:
+            print('More than one directory corresponds to the given value(s).')
+            paths = [self.dico_paths[dir_i] for dir_i in keys_corresp]
+            sod = SetOfDirResults(paths)
+            return sod.path_larger_t_start()
diff --git a/include/mpi-compat.h b/include/mpi-compat.h
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_aW5jbHVkZS9tcGktY29tcGF0Lmg=
--- /dev/null
+++ b/include/mpi-compat.h
@@ -0,0 +1,14 @@
+/* Author: Lisandro Dalcin */
+/* Contact: dalcinl@gmail.com */
+
+#ifndef MPI_COMPAT_H
+#define MPI_COMPAT_H
+
+#include <mpi.h>
+
+#if (MPI_VERSION < 3) && !defined(PyMPI_HAVE_MPI_Message)
+typedef void *PyMPI_MPI_Message;
+#define MPI_Message PyMPI_MPI_Message
+#endif
+
+#endif/*MPI_COMPAT_H*/
diff --git a/pylintrc b/pylintrc
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_cHlsaW50cmM=
--- /dev/null
+++ b/pylintrc
@@ -0,0 +1,361 @@
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+# DEPRECATED
+# include-ids=no
+
+# DEPRECATED
+# symbols=no
+
+
+[MESSAGES CONTROL]
+
+# Only show warnings with the listed confidence levels. Leave empty to show
+# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
+confidence=HIGH, INFERENCE, INFERENCE_FAILURE
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time. See also the "--disable" option for examples.
+#enable=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifiers separated by comma (,) or put this
+# option multiple times (only on the command line, not in the configuration
+# file where it should appear only once).You can also use "--disable=all" to
+# disable everything first and then reenable specific checks. For example, if
+# you want to run only the similarities checker, you can use "--disable=all
+# --enable=similarities". If you want to run only the classes checker, but have
+# no Warning level messages displayed, use"--disable=all --enable=classes
+# --disable=W"
+#disable=
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html. You can also give a reporter class, eg
+# mypackage.mymodule.MyReporterClass.
+output-format=colorized
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells whether to display a full report or only the messages
+reports=yes
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (RP0004).
+comment=no
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details
+# msg-template='{msg_id}:{line:3d},{column}: {obj}: {msg}'
+msg-template='{C}:{line:3d},{column:2d}: {msg} ({symbol})'
+
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis
+ignored-modules=
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed. Python regular
+# expressions are accepted.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input,file
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_,f,ax
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# Colon-delimited sets of names that determine each other's naming style when
+# the name regexes allow several styles.
+name-group=
+
+# Include a hint for the correct naming format with invalid-name
+include-naming-hint=no
+
+# Regular expression matching correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for function names
+function-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct variable names
+variable-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Naming hint for variable names
+variable-name-hint=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression matching correct constant names
+const-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Naming hint for constant names
+const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression matching correct attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for attribute names
+attr-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for argument names
+argument-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression matching correct class attribute names
+class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Naming hint for class attribute names
+class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
+
+# Regular expression matching correct inline iteration names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Naming hint for inline iteration names
+inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
+
+# Regular expression matching correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Naming hint for class names
+class-name-hint=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression matching correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Naming hint for module names
+module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression matching correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Naming hint for method names
+method-name-hint=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match function or class names that do
+# not require a docstring.
+no-docstring-rgx=__.*__
+
+# Minimum line length for functions/classes that require docstrings, shorter
+# ones are exempt.
+docstring-min-length=-1
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+[LOGGING]
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format
+logging-modules=logging
+
+
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+# List of optional constructs for which whitespace checking is disabled
+no-space-check=trailing-comma,dict-separator
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
+
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
+
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
+
+
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching the name of dummy variables (i.e. expectedly
+# not used).
+dummy-variables-rgx=_$|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+[SPELLING]
+
+# Spelling dictionary name. Available dictionaries: en (aspell), en_CA
+# (aspell), en_GB (aspell), en_US (aspell), fr (aspell), fr_CH (aspell), fr_FR
+# (aspell), en_ZA (myspell), en_AU (myspell).
+spelling-dict=
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to indicated private dictionary in
+# --spelling-private-dict-file option instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+# Ignore imports when computing similarities.
+ignore-imports=no
+
+
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branches=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=mcs
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=Exception
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_c2V0dXAuY2Zn
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[flake8]
+ignore = E225,E226,E303,E201,E202, W503
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..49a6353d9e5ff1906540e01fed90d5e523cbcecf_c2V0dXAucHk=
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,187 @@
+
+from setuptools import setup, find_packages
+
+try:
+    from Cython.Distutils.extension import Extension
+    from Cython.Distutils import build_ext
+except ImportError:
+    from setuptools import Extension, build_ext
+    from distutils.command import build_ext
+
+import subprocess
+import numpy as np
+
+import os
+here = os.path.abspath(os.path.dirname(__file__))
+
+import sys
+if sys.version_info[:2] < (2, 7) or (3, 0) <= sys.version_info[0:2] < (3, 2):
+    raise RuntimeError("Python version 2.7 or >= 3.2 required.")
+
+# Get the long description from the relevant file
+with open(os.path.join(here, 'README.rst')) as f:
+    long_description = f.read()
+lines = long_description.splitlines(True)
+long_description = ''.join(lines[8:])
+
+# Get the version from the relevant file
+execfile('fluidsim/_version.py')
+# Get the development status from the version string
+if 'a' in __version__:
+    devstatus = 'Development Status :: 3 - Alpha'
+elif 'b' in __version__:
+    devstatus = 'Development Status :: 4 - Beta'
+else:
+    devstatus = 'Development Status :: 5 - Production/Stable'
+
+ext_modules = []
+
+
+try:
+    import mpi4py
+except ImportError:
+    MPI4PY = False
+    include_dirs_mpi = []
+else:
+    MPI4PY = True
+    os.environ["CC"] = 'mpicc'
+    include_dirs_mpi = [
+        mpi4py.get_include(),
+        here+'/include']
+
+
+if MPI4PY:
+    path_sources = 'fluidsim/operators/fft/Sources_fftw2dmpiccy'
+    include_dirs = [path_sources, np.get_include()] + include_dirs_mpi
+    ext_fftw2dmpiccy = Extension(
+        'fluidsim.operators.fft.fftw2dmpiccy',
+        include_dirs=include_dirs,
+        libraries=['mpi', 'fftw3', 'm'],
+        library_dirs=[],
+        sources=[path_sources+'/libcfftw2dmpi.c',
+                 path_sources+'/fftw2dmpiccy.pyx'])
+    ext_modules.append(ext_fftw2dmpiccy)
+
+
+path_sources = 'fluidsim/operators/fft/Sources_fftw2dmpicy'
+include_dirs = [path_sources, np.get_include()]
+libraries = ['m']
+if MPI4PY:
+    include_dirs.extend(include_dirs_mpi)
+    libraries.append('mpi')
+
+library_dirs = []
+if sys.platform == 'win32':
+    if MPI4PY:
+        raise ValueError(
+            'We have to work on this case with MPI4PY on Windows...')
+    fftw_dir = r'c:\Prog\fftw-3.3.4-dll64'
+    library_dirs.append(fftw_dir)
+    include_dirs.append(fftw_dir)
+    libraries.append('libfftw3-3')
+else:
+    libraries.append('fftw3')
+
+try:
+    subprocess.check_call('ldconfig -p | grep libfftws3_mpi', shell=True)
+    FFTW3MPI = True
+except subprocess.CalledProcessError:
+    FFTW3MPI = False
+
+if FFTW3MPI:
+    libraries.append('fftw3_mpi')
+
+    ext_fftw2dmpicy = Extension(
+        'fluidsim.operators.fft.fftw2dmpicy',
+        include_dirs=include_dirs,
+        libraries=libraries,
+        library_dirs=library_dirs,
+        cython_compile_time_env={'MPI4PY': MPI4PY},
+        sources=[path_sources+'/fftw2dmpicy.pyx'])
+    ext_modules.append(ext_fftw2dmpicy)
+
+path_sources = 'fluidsim/operators/CySources'
+include_dirs = [path_sources, np.get_include()]
+libraries = ['m']
+if MPI4PY:
+    include_dirs.extend(include_dirs_mpi)
+    libraries.extend(['mpi'])
+ext_operators = Extension(
+    'fluidsim.operators.operators',
+    include_dirs=include_dirs,
+    libraries=libraries,
+    library_dirs=[],
+    cython_compile_time_env={'MPI4PY': MPI4PY},
+    sources=[path_sources+'/operators_cy.pyx'])
+
+
+path_sources = 'fluidsim/operators/CySources'
+include_dirs = [path_sources, np.get_include()]
+if MPI4PY:
+    include_dirs.extend(include_dirs_mpi)
+ext_sov = Extension(
+    'fluidsim.operators.setofvariables',
+    include_dirs=include_dirs,
+    libraries=libraries,
+    library_dirs=[],
+    cython_compile_time_env={'MPI4PY': MPI4PY},
+    sources=[path_sources+'/setofvariables_cy.pyx'])
+
+
+path_sources = 'fluidsim/base/time_stepping'
+ext_cyfunc = Extension(
+    'fluidsim.base.time_stepping.pseudo_spect_cy',
+    include_dirs=[
+        path_sources,
+        np.get_include()],
+    libraries=['m'],
+    library_dirs=[],
+    sources=[path_sources+'/pseudo_spect_cy.pyx'])
+
+ext_modules.extend([
+    ext_operators,
+    ext_sov,
+    ext_cyfunc])
+
+
+setup(name='fluidsim',
+      version=__version__,
+      description=('Framework for studying fluid dynamics with simulations.'),
+      long_description=long_description,
+      keywords='Fluid dynamics, research',
+      author='Pierre Augier',
+      author_email='pierre.augier@legi.cnrs.fr',
+      url='https://bitbucket.org/fluiddyn/fluidsim',
+      license='CeCILL',
+      classifiers=[
+          # How mature is this project? Common values are
+          # 3 - Alpha
+          # 4 - Beta
+          # 5 - Production/Stable
+          devstatus,
+          'Intended Audience :: Science/Research',
+          'Intended Audience :: Education',
+          'Topic :: Scientific/Engineering',
+          'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
+          # actually CeCILL License (GPL compatible license for French laws)
+          #
+          # Specify the Python versions you support here. In particular,
+          # ensure that you indicate whether you support Python 2,
+          # Python 3 or both.
+          'Programming Language :: Python',
+          'Programming Language :: Python :: 2',
+          'Programming Language :: Python :: 2.7',
+          # 'Programming Language :: Python :: 3',
+          # 'Programming Language :: Python :: 3.3',
+          # 'Programming Language :: Python :: 3.4',
+          'Programming Language :: Cython',
+          'Programming Language :: C',
+      ],
+      packages=find_packages(exclude=['doc', 'script']),
+      install_requires=['fluiddyn', 'h5py', 'pyfftw'],
+      extras_require=dict(
+          doc=['Sphinx>=1.1', 'numpydoc'],
+          parallel=['mpi4py']),
+      # scripts=['bin/fluiddyn-stop-pumps'],
+      cmdclass={"build_ext": build_ext},
+      ext_modules=ext_modules)