diff --git a/Documentation_texfol/.gitignore b/Documentation_texfol/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7604ca46acf0379031a19fcf0ec0f4bc4348ab7c
--- /dev/null
+++ b/Documentation_texfol/.gitignore
@@ -0,0 +1,7 @@
+/*.aux
+/*.log
+/*.out
+/*.pdf
+/*.synctex*
+/*.toc
+/auto/
\ No newline at end of file
diff --git a/Documentation_texfol/Graphiques/.gitignore b/Documentation_texfol/Graphiques/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d0f356253e7b577ba9b540ca650a621460cca87d
--- /dev/null
+++ b/Documentation_texfol/Graphiques/.gitignore
@@ -0,0 +1,7 @@
+call_graph.pdf
+degeneracy.pdf
+input_output.pdf
+m3.pdf
+processes.pdf
+regions.pdf
+window.pdf
diff --git a/Documentation_texfol/Graphiques/GNUmakefile b/Documentation_texfol/Graphiques/GNUmakefile
new file mode 100644
index 0000000000000000000000000000000000000000..b6cade49662a5ca170a2d19560289b2cc158793c
--- /dev/null
+++ b/Documentation_texfol/Graphiques/GNUmakefile
@@ -0,0 +1,14 @@
+sources = window processes m3 call_graph input_output degeneracy
+
+objects := $(addsuffix .pdf, ${sources})
+
+%.pdf: %.gv
+	dot -Tpdf -o $@ $<
+
+%.pdf: %.odg
+	unoconv --doctype=graphics $<
+
+all: ${objects}
+
+clean:
+	rm -f ${objects}
diff --git a/Documentation_texfol/Graphiques/call_graph.gv b/Documentation_texfol/Graphiques/call_graph.gv
new file mode 100644
index 0000000000000000000000000000000000000000..0689b3c6bd6e621e6ba313a3aabb2d1e7b500f71
--- /dev/null
+++ b/Documentation_texfol/Graphiques/call_graph.gv
@@ -0,0 +1,9 @@
+digraph call_graph
+{
+	main -> {get_snapshot successive_overlap non_successive_overlap};
+	main -> dispatch_snapshot;
+	non_successive_overlap -> {interpolate_eddy weight};
+	dispatch_snapshot -> write_eddy;
+	interpolate_eddy -> write_eddy;
+	successive_overlap -> weight;
+}
\ No newline at end of file
diff --git a/Documentation_texfol/Graphiques/comparaison_201.pdf b/Documentation_texfol/Graphiques/comparaison_201.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..af99153ff1bfeaed1fd2d53dc91da512f6ccea14
Binary files /dev/null and b/Documentation_texfol/Graphiques/comparaison_201.pdf differ
diff --git a/Documentation_texfol/Graphiques/comparaison_202.pdf b/Documentation_texfol/Graphiques/comparaison_202.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..117a887a8c2b4b5c556b06155d785d85b2ab1d13
Binary files /dev/null and b/Documentation_texfol/Graphiques/comparaison_202.pdf differ
diff --git a/Documentation_texfol/Graphiques/degeneracy.odg b/Documentation_texfol/Graphiques/degeneracy.odg
new file mode 100644
index 0000000000000000000000000000000000000000..67bfc175c74d929776111358f4934e6c7152708d
Binary files /dev/null and b/Documentation_texfol/Graphiques/degeneracy.odg differ
diff --git a/Documentation_texfol/Graphiques/extrema.pdf b/Documentation_texfol/Graphiques/extrema.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..11c52d384a8d315484765e67eb7790c97868a922
Binary files /dev/null and b/Documentation_texfol/Graphiques/extrema.pdf differ
diff --git a/Documentation_texfol/Graphiques/input_output.odg b/Documentation_texfol/Graphiques/input_output.odg
new file mode 100644
index 0000000000000000000000000000000000000000..001bc6e7995dea4372b2484bcac344ed828e94ed
Binary files /dev/null and b/Documentation_texfol/Graphiques/input_output.odg differ
diff --git a/Documentation_texfol/Graphiques/m3.odg b/Documentation_texfol/Graphiques/m3.odg
new file mode 100644
index 0000000000000000000000000000000000000000..3d45c1ea9f203661100066bbb9bfc517068fc70c
Binary files /dev/null and b/Documentation_texfol/Graphiques/m3.odg differ
diff --git a/Documentation_texfol/Graphiques/processes.odg b/Documentation_texfol/Graphiques/processes.odg
new file mode 100644
index 0000000000000000000000000000000000000000..29bf08c3978e29f1e86ccbd41daebf7f8ee05f52
Binary files /dev/null and b/Documentation_texfol/Graphiques/processes.odg differ
diff --git a/Documentation_texfol/Graphiques/regions.py b/Documentation_texfol/Graphiques/regions.py
new file mode 100755
index 0000000000000000000000000000000000000000..223d196f70e6b008063abaff6de19553b5375e7b
--- /dev/null
+++ b/Documentation_texfol/Graphiques/regions.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+import netCDF4
+from mpl_toolkits import basemap
+import jumble
+from matplotlib import pyplot as plt
+import glob
+import itertools
+
+m = basemap.Basemap(llcrnrlon=-30, llcrnrlat=-90, urcrnrlon=60, urcrnrlat = 0)
+plt.figure()
+m.drawcoastlines()
+m.drawmeridians(range(0,361, 15), labels = [0, 0, 0, 1])
+m.drawparallels(range(-90,91, 15), labels = [1, 0, 0, 0])
+colors = itertools.cycle(['blue', 'orange', 'green', 'red', 'purple', 'brown',
+                          'pink', 'gray', 'olive', 'cyan'])
+
+for filename in glob.glob("h_region_*.nc") + ["h_outermost.nc"]:
+    with netCDF4.Dataset(filename) as f:
+        jumble.draw_bbox(f.variables["lon"][0], f.variables["lon"][-1],
+                         f.variables["lat"][0], f.variables["lat"][-1],
+                         colors = [next(colors)], label = filename)
+
+plt.legend(loc = "lower left", bbox_to_anchor = (1, 0.5))
+plt.subplots_adjust(right = 0.7)
+plt.savefig("regions.pdf")
+##plt.show()
diff --git a/Documentation_texfol/Graphiques/test_get_eddy.pdf b/Documentation_texfol/Graphiques/test_get_eddy.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..be29ff9ad0c4aab965b3790c5a86acd26d5a4129
Binary files /dev/null and b/Documentation_texfol/Graphiques/test_get_eddy.pdf differ
diff --git a/Documentation_texfol/Graphiques/test_get_snapshot.pdf b/Documentation_texfol/Graphiques/test_get_snapshot.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..73830a06361538153a4be4f6f5b7d026152f143e
Binary files /dev/null and b/Documentation_texfol/Graphiques/test_get_snapshot.pdf differ
diff --git a/Documentation_texfol/Graphiques/window.odg b/Documentation_texfol/Graphiques/window.odg
new file mode 100644
index 0000000000000000000000000000000000000000..61ff61792cbf4325f84c1494feb0b7c838e2dea7
Binary files /dev/null and b/Documentation_texfol/Graphiques/window.odg differ
diff --git a/Documentation_texfol/documentation.tex b/Documentation_texfol/documentation.tex
new file mode 100644
index 0000000000000000000000000000000000000000..884bb9c7857d4d972bedc2b559643ae8cb574c84
--- /dev/null
+++ b/Documentation_texfol/documentation.tex
@@ -0,0 +1,1332 @@
+\documentclass[a4paper,french]{article}
+
+\usepackage[utf8x]{inputenc}
+
+\usepackage{amsmath}
+
+\usepackage[T1]{fontenc}
+\usepackage{lmodern}
+
+\usepackage{algorithmic}
+\usepackage{babel}
+\usepackage{graphicx}
+\usepackage{xcolor}
+
+\usepackage[np]{numprint}
+
+\usepackage{hyperref} \hypersetup{pdftitle={Documentation du programme
+    en Fortran de détection de tourbillons}, pdfauthor={Lionel Guez},
+  hypertexnames=false}
+
+\usepackage{algorithm}
+
+\renewcommand{\algorithmicfor}{\textbf{pour}}
+\renewcommand{\algorithmicend}{\textbf{fin}}
+\renewcommand{\algorithmicdo}{\textbf{faire}}
+\renewcommand{\algorithmicto}{\textbf{à}}
+\renewcommand{\algorithmicif}{\textbf{si}}
+\renewcommand{\algorithmicthen}{\textbf{alors}}
+\renewcommand{\algorithmicelse}{\textbf{sinon}}
+\renewcommand{\algorithmiccomment}[1]{\textcolor{blue}{(*~#1~*)}}
+\renewcommand{\algorithmicwhile}{\textbf{tant que}}
+\renewcommand{\algorithmicrepeat}{\textbf{répéter}}
+\renewcommand{\algorithmicuntil}{\textbf{jusqu'à}}
+
+                                %---------------
+
+\graphicspath{{Graphiques/}}
+
+\author{Lionel GUEZ}
+
+\title{Documentation du programme en Fortran de détection de
+  tourbillons}
+
+                                %---------------
+
+\begin{document}
+
+\maketitle
+\tableofcontents
+
+\section{Divers}
+
+Pour le critère de fusion ou de division des tourbillons, utiliser la
+vitesse géostrophique. Chaque ligne de niveau de la hauteur correspond
+à une ligne de courant de la vitesse. La norme de la vitesse et la
+composante orthoradiale doivent être à peu près constantes sur une
+ligne, prendre la moyenne sur la ligne de l'une ou l'autre. Chercher
+la ligne de vitesse maximale en partant de l'extremum et en
+s'éloignant. La zone intérieure à cette ligne est le c\oe{}ur du
+tourbillon. Les extremums à deux dates correspondent au même
+tourbillon si les c\oe{}urs se recouvrent.
+
+But du programme : produire une base de données de tourbillons et un
+graphe. Les sommets du graphe sont les tourbillons. Les arcs relient
+les tourbillons dont les c\oe{}urs se recouvrent à deux dates
+successives.
+
+Le graphe est orienté acyclique. L'ordre topologique est tout trouvé :
+numérotation par dates successives. Le graphe non orienté associé est
+une forêt.
+
+Pour comparer les tourbillons à deux dates, il semble coûteux de
+n'utiliser que les listes de tourbillons aux deux dates. Cela implique
+une double boucle sur les listes de tourbillons, à l'intérieur d'une
+boucle sur les dates, soit potentiellement
+$8000 \times 8000 \times 5000$ comparaisons. Pour éviter cette
+comparaison aveugle, on peut conserver l'information sur la position
+des tourbillons dans des cartes aux différentes dates. La conservation
+en mémoire vive à 5000 dates semble aussi trop lourde. Il faut se
+restreindre aux cinq (max\_delta + 1) dates nécessaires à la
+profondeur de vue.
+
+Lorsqu'on compare des tourbillons à distance temporelle delta, on veut
+tester s'ils n'ont pas de prédécesseur ou successeur visible à une
+date intermédiaire. Il faut donc, si on crée un arc entre deux dates
+successives, ou delta arcs à distance delta (par interpolation de
+tourbillons invisibles), enregistrer ce fait en prévision du passage
+au delta supérieur. Mais d'un autre côté, on ne veut pas que le fait
+de mettre des arcs à l'étape delta ait une importance pour la suite de
+l'étape delta. (Et accessoirement, on ne veut pas que l'ordre dans
+lequel on passe les tourbillons à l'étape delta ait une importance.)
+On est donc obligé d'enregistrer, pour chaque tourbillon, non
+seulement s'il a des prédécesseurs visibles et s'il a des successeurs
+visibles, mais aussi à quelle distance temporelle.
+
+On ne cherche des superpositions qu'entres tourbillons visibles, en
+excluant les tourbillons interpolés.
+
+Possibilité de parallélisme dans l'extraction des tourbillons mais au
+prix d'une conservation de ssh, uv, v en mémoire vive à plusieurs
+dates.
+
+On pourrait écrire l'algorithme principal en explicitant les
+get\_snpashot, en explicitant le cas m < n\_proc et en supprimant la
+variable k\_end\_main\_loop. Ce serait mieux du point de vue de la
+performance, mais je pense que ce serait finalement moins clair.
+
+Cf. figure (\ref{fig:call_graph}).
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{call_graph}
+  \caption{Arbre des appels du programme de détection de tourbillons.}
+  \label{fig:call_graph}
+\end{figure}
+
+Cas où les composantes out\_cont ou speed\_cont d'un tourbillon sont
+vides, c'est-à-dire égales à null\_ssh\_contour(). On peut distinguer
+les cas du tableau (\ref{tab:null_ssh_contour}).
+\begin{table}[htbp]
+  \centering
+  \begin{tabular}{lllllll|l}
+    out\_cont & suff\_amp & extr\_map & radius4 & speed\_cont
+    & max\_speed & fraction & note \\
+    \hline
+    null & F & < 0 & 0 & null & \np{e4} & 5 \% & 1 \\
+    $\ne$ null & F & < 0 & 0 & null & \np{e4} & 6 \% \\
+    $\ne$ null & T & > 0 & 1 & null & NaN & 3 \% & 2 \\
+    $\ne$ null & T & > 0 & 1 & null & $\ne \np{e4}$, $\ne$ NaN & 47 \% \\
+    $\ne$ null & T & > 0 & $\ge 2$ & null & $\ne \np{e4}$, $\ne$ NaN & 21 \%
+                                                                 & 3
+  \end{tabular}
+  \caption{Cas où les composantes out\_cont ou speed\_cont d'un tourbillon sont
+    vides. Colonne fraction : pour la région 5, nombre
+    de cas sur 260 extremums au total. Note 1 : pas de bon contour à
+    innermost\_level. Note 2 : out\_cont est près d'une côte, champ de
+    vitesse non défini, max\_speed vaut $10^4$ dans le fichier
+    DBF. Note 3 : pas mieux que out\_cont pour la vitesse maximale.}
+  \label{tab:null_ssh_contour}
+\end{table}
+Sur la région 5, sur 260 extremums, je trouve les nombres de cas
+suivants pour les 5 lignes du tableau (\ref{tab:null_ssh_contour}) :
+12, 15, 7, 122, 54. Au total : 210 cas où les composantes out\_cont ou
+speed\_cont d'un tourbillon sont vides, sur 260 tourbillons, soit 81
+\% des tourbillons.
+
+\section{Calcul de la vitesse azimutale par rapport à l'extremum}
+
+Notons $C$ la position de l'extremum et $M$ un point où l'on veut
+calculer la vitesse azimutale, $\vec k_C$ et $\vec k$ les vecteurs
+verticaux en $C$ et $M$. Le vecteur unitaire azimutal en $M$ par
+rapport à $C$ est :
+\begin{equation*}
+  \vec u = \frac{1}{\| \vec k_C \wedge \vec k \|} \vec k_C \wedge \vec k
+\end{equation*}
+C'est un vecteur orthogonal au grand cercle passant par $C$ et
+$M$. La composante azimutale de la vitesse $\vec V$ est donc
+$\vec V \cdot \vec u$. En pratique, on peut la calculer en projetant
+les trois vecteurs sur la base $(\mathbf{x_2}, \mathbf{y_2},
+\mathbf{z_p})$ (cf. résumé du chapitre 1 de Holton (2004 k0727)) :
+\begin{equation*}
+  \vec V =
+  \begin{bmatrix}
+    - u \sin \lambda - v \sin \phi \cos \lambda \\
+    u \cos \lambda - v \sin \phi \sin \lambda \\
+    v \cos \phi
+  \end{bmatrix},
+  \vec k_C = 
+  \begin{bmatrix}
+    \cos \phi_C \cos \lambda_C \\
+    \cos \phi_C \sin \lambda_C \\
+    \sin \phi_C
+  \end{bmatrix}, 
+  \vec k = 
+  \begin{bmatrix}
+    \cos \phi \cos \lambda \\
+    \cos \phi \sin \lambda \\
+    \sin \phi
+  \end{bmatrix}
+\end{equation*}
+En résumé, il faut calculer un produit vectoriel, normaliser le
+résultat et calculer un produit scalaire. (La permutation circulaire
+du produit mixte ne simplifie rien : il faut toujours calculer le
+produit vectoriel pour la normalisation.) Un inconvénient de cette
+méthode est que, si $C$ et $M$ sont proches,
+$\| \vec k_C \wedge \vec k \| \ll 1$. C'est-à-dire que le produit
+vectoriel doit être calculé par différence petite par rapport aux
+termes de la soustraction, donc avec une mauvaise précision.
+
+Si $C$ et $M$ sont proches, une méthode peut-être plus précise
+consisterait à considérer la projection stéréographique oblique de centre
+$C$. On peut projeter $M$ et projeter $\vec V$, calculer la composante
+azimutale du vecteur projeté dans le plan de projection, et inverser
+la projection à partir de cette composante azimutale. Les calculs
+semblent assez laborieux. La projection de la vitesse fait intervenir
+le jacobien de la projection stéréographique oblique. Je n'ai
+d'ailleurs pas trouvé les formules toutes prêtes.
+
+Dans l'approximation plane, on confond la sphère au voisinage de $C$
+avec le plan tangent en $C$ (le plan de la la projection
+stéréographique oblique de centre $C$). Les formules de la projection
+stéréographique oblique se réduisent, à l'ordre 1 en
+$\delta \phi = \phi - \phi_C$ et
+$\delta \lambda = \lambda - \lambda_C$, à :
+\begin{align*}
+  & x \approx a \cos \phi_C\ \delta \lambda \\
+  & y \approx a\ \delta \phi
+\end{align*}
+Admettons que la vitesse soit identique à sa projection, à ce niveau
+d'approximation. Alors, en notant $\rho$ la
+distance entre l'extremum et $M$, la vitesse azimutale est :
+\begin{equation*}
+  V_\theta = \frac{x v - y u}{\rho}
+\end{equation*}
+
+
+\section{Bons contours}
+
+Soit un extremum de SSH. Un \og bon contour\fg{} de cet extremum, est
+une ligne de niveau de SSH fermée contenant cet extremum et ne
+contenant aucun autre extremum de SSH d'amplitude significative.
+
+Supposons que l'amplitude minimale soit nulle.
+
+Je crois qu'il ne peut exister qu'un bon contour au plus à un niveau
+donné. Je crois aussi, sans l'avoir démontré, que tous les points du
+maillage intérieurs au bon contour ont une SSH comprise entre la SSH
+de l'extremum et la SSH du contour.
+
+Appelons \og chemin de sortie\fg{} un des quatre chemins partant de
+l'extremum, sur le maillage, en ligne droite à longitude constante ou
+à latitude constante (sans rebroussement, sans sauter de point ni
+rester sur le même point), allant jusqu'au premier point du maillage
+strictement extérieur au contour.  Je crois, sans l'avoir démontré,
+que, pour chacun des quatre chemins de sortie, la suite des valeurs de
+SSH aux points rencontrés à l'intérieur du contour (tous les points
+sauf le dernier) varie de façon monotone. J'utilise cette hypothèse
+dans outermost\_possible\_level.
+
+Admettons les propriétés ci-dessus. Considérons l'un des quatre
+chemins de sortie. Indiçons, mettons avec $l$ variant de 1 (pour
+l'extremum) à $n$ (pour le dernier point du chemin, le seul
+strictement à l'extérieur), les points rencontrés sur ce
+chemin. Notons $h_c$ la valeur de SSH sur le contour et $h_l$ la
+valeur de SSH en un point $l$. Supposons :
+\begin{equation}
+  \label{eq:non_degeneracy}
+  \forall l < n, h_l \ne h_{l + 1}
+\end{equation}
+Soit $l < n$ (un point quelconque du chemin sauf le dernier),
+c'est-à-dire que nous supposons le point $l$ à l'intérieur du
+contour. Si par exemple l'extremum est un maximum alors :
+\begin{equation*}
+  h_l \ge h_c
+\end{equation*}
+Si, de plus :
+\begin{equation*}
+  l + 1 = n
+\end{equation*}
+alors le contour passe entre $l$ et $l + 1$ :
+\begin{equation*}
+  h_{l + 1} < h_c \le h_l \lor h_l \le h_c < h_{l + 1}
+\end{equation*}
+On peut ainsi écrire :
+\begin{equation*}
+  l + 1 = n \Rightarrow h_{l + 1} < h_c \lor h_l = h_c < h_{l + 1}
+\end{equation*}
+Par ailleurs :
+\begin{align*}
+  & h_{l + 1} < h_c \Rightarrow l + 1 = n \\
+  & h_l < h_{l + 1} \Rightarrow l + 1 = n
+\end{align*}
+On a donc le critère :
+\begin{equation*}
+  l + 1 = n \Leftrightarrow h_{l + 1} < h_c \lor h_l < h_{l + 1}
+\end{equation*}
+De même, on montre que si l'extremum est un minimum alors :
+\begin{equation*}
+  l + 1 = n \Leftrightarrow h_{l + 1} > h_c \lor h_l > h_{l + 1}
+\end{equation*}
+Utilisation pour la procédure inside.
+
+Sans l'hypothèse (\ref{eq:non_degeneracy}), on peut avoir le cas
+pathologique où $h_c = h_l = h_{l + 1}$.
+\includegraphics{degeneracy}
+Le contour peut passer n'importe où entre $l$ et $l + 1$, cela dépend
+du fonctionnement de \verb+Contour_531+.
+
+Le plus éloigné des bons contours, si on le cherchait avec une
+précision infinie, contiendrait (dans la surface intérieure à ce
+contour) forcément les points voisins de l'extremum. Donc le chemin de
+sortie de ce bon contour le plus éloigné serait de longueur supérieure
+à 3.
+
+\section{Problèmes de dégénérescence}
+
+Possibilité que deux points adajacents aient exactement la même valeur
+de SSH. J. Tierny mentionne ce type de problème dans son
+\href{http://www-pequan.lip6.fr/~tierny/stuff/teaching/tierny_topologicalDataAnalysis.pdf}{cours}
+(§ 1.1.2 Range representation).
+
+J'ai pensé ajouter une valeur aléatoire :
+\begin{verbatim}
+call random_number(harvest)
+ssh(i, j) = ssh(i, j) + (1. + harvest) * 10. * epsilon(0.)
+\end{verbatim}
+Cela n'enlève pas forcément la dégénérescence. En simple précision par
+exemple, la valeur ajoutée est de l'ordre de \np{e-6}. Si par exemple
+pour deux positions successives de même SSH, les valeurs de harvest
+sont \np{0.53} et \np{0.50}, la différence entre ces deux valeurs de
+harvest est perdue par la troncature lors de l'ajout à ssh, si bien
+que la dégénérescence n'est pas levée.
+
+Autre idée : ne modifier la ssh que pour des extremums dégénérés. Par
+exemple, pour un maximum dégénéré, ajouter epsilon. Ou bien, pour un
+maximum dégénéré, le diminuer à la valeur autour la plus proche ?
+
+Il y aurait aussi l'idée (citée par J. Tierny) de ne pas toucher au
+champ de SSH mais de changer la définition de l'ordre. Mais il
+faudrait faire ces changements jusque dans les procédures de recherche
+de contour.
+
+Si j'admets la possibilité de dégénérescence alors les procédures
+impactées sont get\_1\_outerm, local\_extrema, inside et
+outermost\_possible\_level.
+
+Je cherche un bon contour en ne spécifiant qu'un seul point, qui doit
+être intérieur au bon contour, et un ensemble de points qui doivent
+être extérieurs. Si deux points voisins ont la même valeur de SSH et
+sont tous les deux extremums (non stricts donc), et si je les retiens
+tous les deux comme extremums valides, il ne peut exister de bon
+contour autour de l'un des deux. Si ce sont des extremums de signes
+différents, je peux me satisfaire de ne pas trouver de bon contour. Si
+ce sont des extremums de même signe, je voudrais trouver un bon
+contour autour des deux. Il faut donc n'en retenir qu'un des deux
+comme valide. Je peux prendre arbitrairement le premier rencontré dans
+l'ordre de parcours du tableau.
+
+Il faut prévoir dans get\_1\_outerm la possibilité de ne pas
+trouver de bon contour. La logique est impactée. \`A strictement
+parler, la valeur de innermost\_level ne permet plus de prévoir si
+l'amplitude va être suffisante, et on ne sait plus quelle valeur
+intiale de level\_good prendre dans get\_1\_outerm. Je garde
+néanmoins innermost\_level comme valeur intiale de level\_good. La
+conséquence est que je risque de ne pas détecter de contour autour
+d'un extremum dégénéré alors qu'on pourrait peut-être en trouver un en
+prenant une valeur initiale de level\_good plus proche de
+l'extremum. On peut aussi imaginer le cas bizarre de trois maximums
+(par exemple) égaux adjacents. Dans ce cas, l'algorithme choisi
+conserve deux de ces maximaux et il n'y a pas de bon contour possible,
+alors qu'on pourrait en principe en trouver. On se désintéresse de ces
+cas pathologiques.
+
+\section{Entrées et sorties}
+
+Format shapefile. Un type de géométrie par fichier donc il faut
+séparer les contours et les positions des extremums. Par ailleurs, on
+n'enregistre pas de contours pour les tourbillons interpolés. On
+pourrait créer un shapefile séparé pour les extremums interpolés
+(points), avec des méta-données supplémentaires sur les contours
+fictifs associés. En notant $m$ le numéro de processus MPI :
+\begin{itemize}
+\item \verb+extremum_interpolated_$m.shp+ : points
+\item \verb+extremum_interpolated_$m.dbf+ : valeur de SSH à l'extremum,
+  indice de date, indice de tourbillon à cette date, aire du contour
+  de SSH le plus éloigné, valeur de SSH sur le contour de SSH le plus
+  éloigné, aire du contour de vitesse maximale, valeur de vitesse sur
+  le contour de vitesse maximale
+\end{itemize}
+Mais il me semble plus simple et clair d'écrire les tourbillons
+interpolés avec les autres, en utilisant une forme NULL pour le
+polygone correspondant à un contour interpolé.
+\begin{itemize}
+\item \verb+extremum_$m.shp+ : points
+\item \verb+extremum_$m.dbf+
+  : valeur de SSH, indice de date, indice de tourbillon à cette date,
+  interpolé (logique), cyclone (logique), suff\_amp (logique), valeur
+  de vitesse sur le contour de vitesse maximale
+\item \verb+outermost_contour_$m.shp+ : polygones
+\item \verb+outermost_contour_$m.dbf+ : aire, valeur de SSH, indice de
+  date, indice de tourbillon à cette date, twice, radius4
+\item \verb+max_speed_contour_$m.shp+ : polygones
+\item \verb+max_speed_contour_$m.dbf+ : aire, valeur de SSH, indice de date, indice de tourbillon à cette date
+\end{itemize}
+Soit un total de 9 fichiers par processus en comptant les fichiers
+shx.
+
+L'indice de date et l'indice de tourbillon dans
+\verb+outermost_contour_$m.dbf+ et \verb+max_speed_contour_$m.dbf+
+sont en principe inutiles puisqu'ils sont aussi dans
+\verb+extremum_$m.dbf+ et que les tourbillons sont stockés dans le
+même ordre dans les trois shapefiles. La redondance me semble
+préférable pour la solidité et le confort d'utilisation. La
+méta-donnée logique \og interpolé \fg{} dans \verb+extremum_$m.dbf+
+est nécessaire parce qu'il est possible de ne pas trouver de contour
+extérieur autour d'un extremum détecté.
+
+J'avais d'abord mis la valeur de vitesse dans le fichier
+\verb+max_speed_contour_$m.dbf+ mais cette vitesse peut être associée
+au contour extérieur, avec un contour vide dans \verb+max_speed_contour_$m+.
+
+Les réels dans un fichier shp sont stockés en double précision donc
+les polygones occupent chacun : 16 B $\times$ nombre moyen de côtés
+d'un polygone. Soit, en comptant 30 côtés en moyenne par polygone,
+environ \np{0.5} KiB. (Ce nombre de côtés moyen m'a été communiqué par
+Rémi. On peut aussi l'obtenir grossièrement en supposant un rayon
+d'environ 100 km, soit un périmètre d'environ 600 km et une résolution
+de 25 km.) Comme chaque fichier shp peut occuper au maximum 2 GiB, il
+peut contenir au maximum \np{4e6} polygones. On ne peut donc mettre à
+coup sûr les polygones pour 16 ans dans un seul fichier, même en
+séparant cyclones et anticyclones. Faire un fichier par an ?  Séparer
+en outre cyclones et anticyclones ?
+
+Une possibilité simple de stockage de graphe est la liste d'adjacences
+(en texte), c'est-à-dire, pour chaque sommet du graphe, une ligne :
+\begin{verbatim}
+indice date, indice tourbillon, degré sortant d
+\end{verbatim}
+suivie de d lignes :
+\begin{verbatim}
+indice date, indice tourbillon, poids
+\end{verbatim}
+Estimation de la taille du fichier, en comptant 10 caractères par
+entier, 13 caractères par réel, \np{5e7} sommets et deux arcs par
+sommet en moyenne : 5 GiB (au lieu de \np{0.5} GiB en binaire). Mais
+cela implique de connaître tous les arcs sortants d'un sommet au
+moment de l'écriture pour ce sommet. Dans notre algorithme, les arcs
+sortants sont découverts en plusieurs étapes (delta = 1 à 5) et nous
+ne voulons pas stocker le graphe en mémoire vive. Nous choisissons
+donc de stocker le graphe au format liste d'arcs (en texte),
+c'est-à-dire, pour chaque sommet du graphe, une ligne :
+\begin{verbatim}
+indice date, indice tourbillon, indice date, indice tourbillon, poids
+\end{verbatim}
+Fichier \verb+edgelist_$m.txt+. L'estimation de la taille du fichier, avec
+les mêmes hypothèses (en particulier deux arcs par sommet) est de 6
+GiB.
+
+Problème des sommets isolés. L'information est déjà présente, de façon
+implicite, dans les fichiers évoqués ci-dessus. En effet, on a le
+nombre de tourbillons à chaque date d'après la liste des identifiants
+de tourbillons dans les shapefiles, et on a la liste des tourbillons
+non isolés d'après la liste des arcs. Mais la liste des sommets isolés
+n'est donc pas très facilement trouvée. On ajoute par conséquent les
+fichiers de sortie suivants :
+\begin{description}
+\item[number\_eddies\_\$m.csv] Une colonne indice de date, une colonne
+  nombre de tourbillons visibles, une colonne nombre de tourbillons
+  interpolés.
+\item[isolated\_nodes\_\$m.txt] Sur chaque ligne : indice de date, indice
+  de tourbillon. Peut être lu comme une simple liste
+  d'adjacence. Remarque : par construction, les tourbillons interpolés
+  ne sont jamais isolés.
+\end{description}
+
+Les entrées-sorties sont dans : l'algorithme principal directement
+(lecture de longitude et latitude), get\_snapshot (lecture de SSH, u,
+v), successive\_overlap (écriture dans
+\verb+edgelist_$m.txt+, arcs entre k\_begin(m) et k\_end\_main\_loop(m)),
+non\_successive\_overlap directement (écriture dans
+\verb+edgelist_$m.txt+),
+write\_eddy (écriture dans extremum\_\$m, outermost\_contour\_\$m,
+max\_speed\_contour\_\$m), dispatch\_snapshot directement (écriture
+dans isolated\_nodes\_\$m, number\_eddies\_\$m). Les processus
+écrivent dans des fichiers indicés par le numéro de processus donc
+deux processus ne peuvent écrire dans le même fichier. Cf. figure
+(\ref{fig:input_output}).
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{input_output}
+  \caption{Entrées et sorties du programme en Fortran.}
+  \label{fig:input_output}
+\end{figure}
+
+\section{Déclarations pour l'algorithme principal}
+
+On suppose que le maillage est le même à toutes les dates et qu'il est
+régulier.
+
+Type dérivé polyline : défini dans \verb+Contour_531+. Mémoire
+utilisée par un scalaire de ce type :
+\begin{equation*}
+\mathrm{taille}(\mathtt{polyline}) = 2 (\mathtt{n\_points} + 1) \mathrm{mots}  
+\end{equation*}
+soit 8 (n\_points + 1) B.
+
+Je définis un type dérivé ssh\_contour, associé à une ligne de niveau
+de SSH. Mémoire utilisée par un scalaire de ce type :
+\begin{align*}
+  \mathrm{taille}(\mathtt{ssh\_contour})
+  & = \mathrm{taille}(\mathtt{polyline}) + 2\ \mathrm{mots} \\
+  & = 2 (\mathtt{n\_points} + 2) \mathrm{mots}  
+\end{align*}
+soit 8 (n\_points + 2) B. J'ai hésité à ajouter un champ speed,
+scalaire réel, pour une valeur de vitesse associée au contour. La
+vitesse utilisée peut être la composante azimutale par rapport à un
+centre défini indépendamment du contour. De ce point de vue, la
+définition de ce type dérivé serait un peu gênante. Par ailleurs, il
+ne serait pas très satisfaisant de laisser ce champ non défini pour le
+contour le plus extérieur, pour lequel nous n'avons pas besoin de
+calculer une vitesse.
+
+Les tailles ci-dessous sont calculées pour max\_delta = 4, 16 ans,
+soit environ 5000 dates, 8000 tourbillons par date, 30 côtés par
+polygone (sans différence entre contour extérieur et contour de
+vitesse maximale), 720 latitudes par 1440 longitudes.
+\begin{description}
+\item[max\_delta] Scalaire entier. Intervalle maximal d'indices de
+  dates auxquelles on cherche des recouvrements. Normalement 4.
+\item[longitude, latitude] Vecteurs de réels
+\item[ssh, u, v] Tableaux de réels, deux dimensions (nlon,
+  nlat). Environ 4 MiB chacun.
+\item[eddy] La comparaison entre out\_cont\%ssh et ssh\_extr donne la
+  distinction anticyclone -- cyclone mais il est quand même plus
+  confortable d'avoir cette information directement dans les
+  sorties. Pour cela, j'ajoute un champ cyclone. De même, le fait que
+  l'amplitude de ssh soit ou non en dessous du seuil est codé dans
+  extr\_map mais il me semble plus confortable de l'avoir en plus ici,
+  dans le champ suff\_amp. Mémoire utilisée par un scalaire de ce type
+  :
+  \begin{equation*}
+    \mathrm{taille}(\mathtt{eddy})
+    = 11\ \mathrm{mots} + \mathrm{taille}(\mathtt{out\_cont})
+    + \mathrm{taille}(\mathtt{speed\_cont})
+  \end{equation*}
+  Notons $n_o$ le nombre de points dans le contour extérieur
+  (out\_cont\%n\_points) et $n_s$ le nombre de points dans le contour
+  de vitesse maximale (speed\_cont\%n\_points). On obtient :
+  \begin{align*}
+    \mathrm{taille}(\mathtt{eddy})
+    & = [2 (n_o + n_s) + 19]\ \mathrm{mots} \\
+    & = 4 [2 (n_o + n_s) + 19]\ \mathrm{B}    
+  \end{align*}
+soit environ \np{0.5} KiB.
+\item[snapshot\%list\_vis] On a besoin d'encapsuler le vecteur de type
+  eddy dans un scalaire de type snapshot pour pouvoir considérer un
+  vecteur de snapshots. Notons $n_{ve}$ le nombre de tourbillons à une
+  date donnée (number\_vis\_eddies). Mémoire utilisée par ce champ :
+  \begin{equation*}
+    n_{ve} \langle \mathrm{taille}(\mathtt{eddy}) \rangle
+    = n_{ve}
+    [2 (\langle n_o \rangle + \langle n_s \rangle) + 19]\ \mathrm{mots}    
+  \end{equation*}
+  soit environ 4 MiB.
+\item[snapshot\%extr\_map] Notons $n_\lambda$ le nombre de longitudes,
+  $n_\phi$ le nombre de latitudes. Taille : $n_\lambda n_\phi$ mots,
+  soit environ 4 MiB.
+\item[snapshot\%ind\_extr] Taille : $2 n_{ve}$ mots, soit environ
+  \np{0.06} MiB.
+\item[flow] : Vecteur de type snapshot, de taille max\_delta + 1,
+  chaque élément correspondant à une date. Environ 40 MiB.
+\item[dist\_lim] Scalaire entier constant, = 3° / résolution.
+\item[weight\_delta] Scalaire réel.
+\end{description}
+On a :
+\begin{align*}
+  \mathrm{taille}(\mathtt{snapshot})
+  & = n_{ve} \langle \mathrm{taille}(\mathtt{eddy}) \rangle
+    + (n_\lambda n_\phi + 2 n_{ve} + 2)\ \mathrm{mots} \\
+  & = [n_{ve} (2 (\langle n_o \rangle + \langle n_s \rangle) + 21)
+    + n_\lambda n_\phi + 2]\ \mathrm{mots}
+\end{align*}
+D'où la taille d'un scalaire de type snapshot : environ 8 MiB.
+
+J'avais d'abord pensé mettre dans le type eddy les champs ind\_lon et
+ind\_lat. Ce n'est pas très cohérent parce que les indices se réfèrent
+au maillage global, alors que les autres champs sont des propriétés
+intrinsèques. Ainsi, j'étais embêté pour définir un tourbillon à
+partir d'une procédure travaillant sur une section du maillage
+global. J'ai donc choisi donc de remplacer ces champs par un champ
+ind\_extr dans snapshot. Et j'ajoute à la place des champs longitude,
+latitude dans le type eddy.
+
+i désigne un indice de tourbillon, j un indice de position dans la
+fenêtre temporelle (entre 1 et max\_delta + 1) et k un indice de
+date. Cf. figure (\ref{fig:window}) et algorithme
+(\ref{alg:main_sequential}).
+\begin{figure}[htbp]
+  \centering
+  \includegraphics{window}
+  \caption{Indice de position dans la fenêtre glissante et indice de
+    date.}
+  \label{fig:window}
+\end{figure}
+\begin{algorithm}[htbp]
+  \begin{algorithmic}
+    \STATE \COMMENT{\{max\_delta $\ge 1$; n\_dates $\ge$ max\_delta +
+      1\}} 
+
+    \STATE \COMMENT{prologue}
+
+    \STATE entrer(longitude, latitude)
+    \STATE \COMMENT{longitude et latitude dans l'ordre croissant} 
+    \FOR{k = 1 \TO max\_delta + 1}
+
+    \STATE flow(k) = get\_snapshot(k)
+
+    \ENDFOR
+    \FOR{k = 2 \TO max\_delta + 1}
+
+    \STATE appel de successive\_overlap(k, k, flow)
+    \ENDFOR
+    \FOR{delta = 2 \TO max\_delta}
+
+    \FOR{k = delta + 1 \TO max\_delta + 1}
+
+    \STATE appel de non\_successive\_overlap(k, k, delta, flow)
+
+    \ENDFOR
+    \ENDFOR
+
+    \STATE \COMMENT{boucle principale}
+
+    \FOR{k = max\_delta + 2 \TO n\_dates}
+
+    \STATE appel de dispatch\_snapshot(flow(1), k - max\_delta - 1)
+    \STATE flow(:max\_delta) = flow(2:)
+
+    \STATE flow(max\_delta + 1) = get\_snapshot(k)
+
+    \STATE appel de successive\_overlap(max\_delta + 1, k, flow)
+
+    \FOR{delta = 2 \TO max\_delta}
+
+    \STATE appel de non\_successive\_overlap(max\_delta + 1, k, delta,
+    flow)
+
+    \ENDFOR
+    \ENDFOR
+    \STATE \COMMENT{épilogue}
+
+    \FOR{j = 1 \TO max\_delta + 1}
+
+    \STATE appel de dispatch\_snapshot(flow(j), n\_dates - max\_delta - 1
+    + j)
+    \ENDFOR
+  \end{algorithmic}
+  \caption{Algorithme principal, séquentiel}
+  \label{alg:main_sequential}
+\end{algorithm}
+
+\section{Décomposition de domaine}
+
+Cf. figure (\ref{fig:processes}).
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{processes}
+  \caption{Raccordement des processus. Les comparaisons représentées
+    ici en couleurs sont faites par le processus m. En rouge dans la
+    boucle principale, en bleu, vert et marron dans l'épilogue.}
+  \label{fig:processes}
+\end{figure}
+Il faut, pour m < n\_proc :
+\begin{equation}
+  \label{eq:stitching}
+  \mathtt{k\_end}(m)
+  = \mathtt{k\_begin}(m + 1) + \mathtt{max\_delta} - 1
+\end{equation}
+
+k\_end - k\_begin + 1 appels à get\_snapshot, pour les dates k\_begin
+à k\_end. k\_end - k\_begin + 1 appels à dispatch\_snapshot, pour les
+dates k\_begin à k\_end. Si m > 1 alors max\_delta envois. Si m <
+n\_proc alors max\_delta réceptions.
+
+Nombre d'appels à get\_snapshot avec lecture. Si m < n\_proc : k\_end -
+k\_begin - max\_delta + 1. Si m = n\_proc : k\_end - k\_begin + 1.
+
+successive\_overlap est appelé pour tous les indices k compris entre
+k\_begin + 1 et k\_end\_main\_loop, seulement. Nombre d'appels à
+successive\_overlap : k\_end\_main\_loop - k\_begin. Donc si m <
+n\_proc : k\_end - k\_begin - max\_delta + 1 appels. Si m = n\_proc : k\_end
+- k\_begin appels.
+
+Nombre d'appels à non\_successive\_overlap dans le prologue :
+\begin{equation*}
+  \sum_{\delta = 2} ^{\mathtt{max\_delta}}
+  (\mathtt{max\_delta} - \delta +1)
+  = \frac{\mathtt{max\_delta} (\mathtt{max\_delta} - 1)}{2}
+\end{equation*}
+Dans la boucle principale : (max\_delta - 1)(k\_end\_main\_loop -
+k\_begin - max\_delta). Dans l'épilogue, si m < n\_proc :
+$\frac{\mathtt{max\_delta} (\mathtt{max\_delta} - 1)}{2}$. Total si m
+< n\_proc : (max\_delta - 1)(k\_end - k\_begin - max\_delta +
+1). Total si m = n\_proc : (max\_delta - 1)(k\_end - k\_begin -
+$\frac{\mathtt{max\_delta}}{2}$).
+
+Si k\_end - k\_begin était le même pour m = n\_proc et m <
+n\_proc, le processus m=n\_proc aurait des plus grands nombres
+d'appels aux trois procédures. Notons $\Delta k$ = k\_end - k\_begin,
+considéré comme une fonction de m. Si, pour m < n\_proc :
+\begin{equation}
+  \label{eq:charge}
+  \Delta k(m) \approx \Delta k(\mathtt{n\_proc}) + \mathtt{max\_delta}
+\end{equation}
+alors la charge devrait être à peu près équilibrée.
+
+On peut prendre, pour tout m :
+\begin{equation*}
+  \mathtt{k\_begin}(m)
+  = 1 + E[(m - 1) \mathtt{n\_dates} / \mathtt{n\_proc}]
+\end{equation*}
+(On prend la partie entière au lieu de nint pour pouvoir démontrer des
+encadrements.)  D'où, avec la contrainte (\ref{eq:stitching}), pour m
+< n\_proc :
+\begin{equation*}
+  \mathtt{k\_end}(m)
+  = E(m\ \mathtt{n\_dates} / \mathtt{n\_proc}) + \mathtt{max\_delta}
+\end{equation*}
+Alors on peut montrer que $\Delta k(m) - \Delta k(\mathtt{n\_proc})$
+vaut max\_delta ou max\_delta - 1. On a donc l'approximation
+(\ref{eq:charge}). Chaque processus fait aussi le même nombre de
+lectures de ssh, u, v.
+
+Pour n\_proc $\ne 1$ et m < n\_proc, le processus m + 1 ne doit pas
+relire une date déjà lue par le processus m. On doit donc avoir :
+\begin{equation}
+  \label{eq:min_delta_k}
+  \mathtt{k\_begin}(m + 1) > \mathtt{k\_begin}(m) + \mathtt{max\_delta}
+\end{equation}
+Ce qui est équivalent à :
+\begin{equation*}
+  E(m \mathtt{n\_dates} / \mathtt{n\_proc})
+  - E[(m - 1) \mathtt{n\_dates} / \mathtt{n\_proc}]
+  > \mathtt{max\_delta}
+\end{equation*}
+Or on peut montrer que :
+\begin{equation*}
+  E(m \mathtt{n\_dates} / \mathtt{n\_proc})
+  - E[(m - 1) \mathtt{n\_dates} / \mathtt{n\_proc}]
+  > E(\mathtt{n\_dates} / \mathtt{n\_proc}) - 1
+\end{equation*}
+Il suffit donc que :
+\begin{equation*}
+  E(\mathtt{n\_dates} / \mathtt{n\_proc}) \ge \mathtt{max\_delta} + 1
+\end{equation*}
+
+L'équation (\ref{eq:stitching}) et l'inégalité (\ref{eq:min_delta_k})
+impliquent que, pour m < n\_proc :
+\begin{equation}
+  \label{eq:delta_k}
+  \mathtt{k\_begin} + \mathtt{max\_delta}
+  \le \mathtt{k\_end} - \mathtt{max\_delta}
+\end{equation}
+donc les appels de get\_snapshot dans le prologue font des
+lectures. Les appels de get\_snapshot dans l'épilogue font des
+réceptions. L'inégalité (\ref{eq:delta_k}) implique en outre que, pour
+m < n\_proc, la boucle principale fait au moins une itération. Si m <
+n\_proc : tous les appels à get\_snapshot de la boucle principale sauf
+le dernier font des lectures ; le dernier appel à get\_snapshot de la
+boucle principale fait une réception.
+
+Si $m \ge 2$ alors :
+\begin{equation*}
+  \mathtt{k\_end\_main\_loop}(m - 1) = \mathtt{k\_begin}(m)
+\end{equation*}
+
+Exemples : cf. tableaux (\ref{tab:m2}) et (\ref{tab:m3}) et figure
+(\ref{fig:m3}).
+\begin{table}[htbp]
+  \centering
+  \begin{tabular}{llll}
+    m & k\_begin & k\_end & k\_end\_main\_loop \\
+    \hline
+    1 & 1 & 9 & 6 \\
+    2 & 6 & 10 & 10
+  \end{tabular}
+  \caption{max\_delta = 4, n\_dates = 10, n\_proc = 2. m = 1 : lecture
+    dates 1 à 5, écriture date 1, réception date 6, écriture dates 2 à
+    4, réception dates 7 à 9, écriture dates 5 à 9. m = 2 : lecture 6
+    à 10, envoi 6 à 9, écriture 10.}
+  \label{tab:m2}
+\end{table}
+\begin{table}[htbp]
+  \centering
+  \begin{tabular}{llll}
+    m & k\_begin & k\_end & k\_end\_main\_loop \\
+    \hline
+    1 & 1 & 9 & 6 \\
+    2 & 6 & 14 & 11 \\
+    3 & 11 & 15 & 15   
+  \end{tabular}
+  \caption{max\_delta = 4, n\_dates = 15, n\_proc = 3}
+  \label{tab:m3}
+\end{table}
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{m3}
+  \caption{max\_delta = 4, n\_dates = 15, n\_proc = 3. En bleu le
+    prologue, en noir la boucle principale, en rouge l'épilogue. Pour
+    un processus donné, les actions sur une même colonne sont dans une
+    même boucle de l'algorithme principal.}
+  \label{fig:m3}
+\end{figure}
+
+Le dispatch\_snapshot dans la dernière boucle sur j n'est pas
+forcément une écriture. Exemple : n\_proc = 2, max\_delta = 4,
+n\_dates = 10, m = 2.
+
+Si m = 1 alors write\_eddy est appelé pour les dates k\_begin à
+k\_end. Si m > 1 alors write\_eddy est appelé pour les dates
+k\_begin + max\_delta à k\_end. Avec l'équation (\ref{eq:stitching}),
+une date donnée est bien écrite par un seul processus.
+
+\section{Algorithme principal, parallèle}
+
+\begin{algorithmic}[1]
+  \STATE \COMMENT{\{max\_delta $\ge 1$; n\_dates $\ge$ max\_delta + 1;
+    n\_proc
+    $\le E\left(\frac{\mathtt{n\_dates}}{\mathtt{max\_delta} + 1}
+    \right)$\}}
+
+  \STATE \COMMENT{prologue}
+
+  \STATE m = numéro de processus
+
+  \IF{m = 1}
+
+  \STATE entrer(longitude, latitude)
+  \STATE \COMMENT{longitude et latitude dans l'ordre croissant} 
+
+  \STATE envoyer longitude, latitude à tous les processus
+
+  \ELSE
+
+  \STATE recevoir longitude, latitude du processus 1
+  \ENDIF
+
+  \STATE k\_begin = 1 + E[(m - 1) n\_dates / n\_proc]
+
+  \IF{m < n\_proc}
+
+  \STATE k\_end = E(m n\_dates / n\_proc) + max\_delta
+
+  \STATE k\_end\_main\_loop = k\_end - max\_delta + 1
+  \ELSE
+
+  \STATE k\_end = n\_dates
+  \STATE k\_end\_main\_loop = k\_end
+  \ENDIF
+
+  \FOR{k = k\_begin \TO k\_begin + max\_delta}
+
+  \STATE flow(k - k\_begin + 1) = get\_snapshot(k)
+  \COMMENT{lecture}
+
+  \ENDFOR
+  \FOR{k = k\_begin + 1 \TO k\_begin + max\_delta}
+
+  \STATE appel de successive\_overlap(k - k\_begin + 1, k, flow)
+  \ENDFOR
+  \FOR{delta = 2 \TO max\_delta}
+
+  \FOR{k = k\_begin + delta \TO k\_begin + max\_delta}
+
+  \STATE appel de non\_successive\_overlap(k - k\_begin + 1, k, delta,
+  flow)
+
+  \ENDFOR
+  \ENDFOR
+
+  \STATE \COMMENT{boucle principale}
+
+  \FOR{k = k\_begin + max\_delta + 1 \TO k\_end\_main\_loop}
+
+  \STATE appel de dispatch\_snapshot(flow(1), k - max\_delta - 1)
+
+  \STATE flow(:max\_delta) = flow(2:)
+
+  \STATE flow(max\_delta + 1) = get\_snapshot(k)
+
+  \STATE appel de successive\_overlap(max\_delta + 1, k, flow)
+
+  \FOR{delta = 2 \TO max\_delta}
+
+  \STATE appel de non\_successive\_overlap(max\_delta + 1, k, delta,
+  flow)
+
+  \ENDFOR
+  \ENDFOR
+  \STATE \COMMENT{épilogue}
+
+  \FOR{k = k\_end\_main\_loop + 1 \TO k\_end}
+
+  \STATE \COMMENT{m < n\_proc et k $\ge$ k\_end - max\_delta + 2}
+  \STATE appel de dispatch\_snapshot(flow(1), k - max\_delta - 1)
+
+  \STATE flow(:max\_delta) = flow(2:)
+
+  \STATE flow(max\_delta + 1) = get\_snapshot(k)
+  \COMMENT{réception} \STATE \COMMENT{stitching}
+
+  \FOR{delta = k - k\_end + max\_delta \TO max\_delta}
+
+  \STATE appel de non\_successive\_overlap(max\_delta + 1, k, delta,
+  flow)
+
+  \ENDFOR
+  \ENDFOR
+  
+  \FOR{j = 1 \TO max\_delta + 1}
+
+  \STATE appel de dispatch\_snapshot(flow(j), k\_end - max\_delta - 1
+  + j)
+
+  \ENDFOR
+\end{algorithmic}
+
+\section{Résumé du problème de géométrie algorithmique}
+
+\begin{algorithmic}
+  \STATE recherche de tous les extremums locaux
+
+  \FOR{chaque extremum local}
+
+  \STATE recherche du \og bon contour le plus extérieur\fg{}
+  \ENDFOR
+\end{algorithmic}
+
+\og Bon contour\fg{} : contient l'extremum considéré et aucun
+autre. \og Le plus extérieur\fg{} : plus grande différence de niveau
+avec l'extremum considéré, ou encore amplitude maximale.
+
+Recherche du bon contour le plus extérieur : par dichotomie.
+\begin{algorithmic}
+  \STATE $a_1 = 0$
+
+  \STATE $a_2$ = recherche d'une borne supérieure pour
+  l'amplitude en parcourant les points sur un rayon à partir de
+  l'extremum jusqu'à ce que le sens de variation de SSH change
+
+  \WHILE{$a_2 - a_1 >$ précision}
+
+  \STATE recherche du bon contour à l'amplitude
+  $\frac{a_1 + a_2}{2}$
+
+  \IF{bon contour trouvé}
+
+  \STATE $a_1 = \frac{a_1 + a_2}{2}$
+
+  \ELSE
+
+  \STATE $a_2 = \frac{a_1 + a_2}{2}$
+
+  \ENDIF
+
+  \ENDWHILE
+\end{algorithmic}
+
+Recherche d'un bon contour à une amplitude donnée. On cherche tous les
+contours à cette amplitude. Pour chaque contour, pour chaque extremum,
+on regarde si l'extremum est à l'intérieur du contour (test de point
+intérieur à polygone).
+
+\section{Sous-algorithmes}
+
+\subsection{get\_snapshot}
+
+Pour chaque tourbillon du résultat, le contour le plus extérieur doit
+être fermé, doit contenir l'extremum de SSH du tourbillon considéré et
+ne doit contenir aucun autre extremum de SSH avec une amplitude
+significative. Amplitude significative : supérieure à un certain
+minimum, entre l'extremum de SSH et le contour le plus
+lointain.
+
+Dans une première version qui n'envisageait pas d'amplitude minimum,
+j'avais une procédure get\_eddy qui définissait un tourbillon, et qui
+trouvait donc le contour le plus extérieur et le contour de maximum de
+vitesse. Mais avec la possibilité d'une amplitude minimale, il faut
+d'abord se préoccuper de tous les contours extérieurs, qui ne sont
+plus indépendants les uns des autres, pour ne pas chercher inutilement
+des contours de maximum de vitesse. Dans le nouvel algorithme, il n'y
+a donc plus de procédure get\_eddy. Cf. tableau (\ref{tab:outermost_contour}).
+\begin{table}[htbp]
+  \centering
+  \begin{tabular}{l|l|l|l|l|l}
+    flat\_extr & \multicolumn{2}{c|}{F} & \multicolumn{3}{c}{V} \\
+    suff\_amp & \multicolumn{2}{c|}{?} & F & \multicolumn{2}{c}{V} \\
+    extr\_map > 0 & \multicolumn{2}{c|}{V} & F & \multicolumn{2}{c}{V} \\
+    noise\_around & \multicolumn{3}{c|}{?} & F & V \\
+    2\ieme & \multicolumn{2}{c|}{V} & \multicolumn{2}{c|}{F} & V \\
+    suff\_amp & F & V & F & \multicolumn{2}{c}{V} \\
+    extr\_map > 0 & F & V & F & \multicolumn{2}{c}{V}
+  \end{tabular}
+  \caption{Logique pour calculer les contours les plus extérieurs dans
+    get\_snapshot. L'ordre des lignes du tableau suit l'ordre des
+    instructions. suff\_amp n'est d'abord défini que sur une partie
+    des extremums et est donc d'abord différent de extr\_map, mais il
+    contient la même information que extr\_map à la fin de la
+    séquence. noise\_around n'a besoin d'être défini que sur une
+    partie des extremums. Le point d'interrogation 
+    signifie que la valeur n'est pas calculée, elle ne nous intéresse
+    pas. La ligne \og 2\ieme\fg{} donne les cas où le programme passe
+    par la deuxième ligne d'appel à get\_1\_outerm.}
+  \label{tab:outermost_contour}
+\end{table}
+Si un extremum A n'est pas plat, on considère que ce n'est pas du
+bruit, il ne peut pas être englobé dans le contour extérieur d'un
+autre extremum B, même s'il s'avère finalement qu'on ne trouve pas de
+contour extérieur autour de A.
+
+Si le domaine considéré n'est pas global, comment traiter un
+tourbillon au bord du domaine ? La taille du contour le plus extérieur
+peut être artificiellement diminuée. (Ce n'est pas le même cas qu'un
+tourbillon près d'une côte.) C'est-à-dire qu'on pourrait trouver un
+contour plus extérieur avec un domaine élargi. Il pourrait être
+intéressant d'éliminer un tel tourbillon. Comment le détecter ? Le
+fait d'aller jusqu'au bord du domaine dans outermost\_possible\_level
+n'est pas un critère suffisant, parce que outermost\_possible\_level
+ne teste que la direction est. Il faudrait tester après l'appel de
+get\_1\_outerm si le cadre le plus extérieur touche le bord du
+domaine.
+
+\subsection{max\_speed\_contour\_ssh}
+
+Il manque des tests pour lesquels direction vaut 1 et 4.
+
+\subsection{set\_max\_speed}
+
+Notons $d$ la distance dans l'espace des indices entre l'extremum
+cible et le countour extérieur. Par définition de la partie entière,
+les points à distance $E(d)$ de l'extremum cible sont à l'intérieur du
+contour. Certains points à distance $E(d) + 1$ peuvent être à
+l'extérieur. D'où : radius = $E(d) + 1$.
+
+Dans le cas où radius vaut 1, je calcule max\_speed. Ce calcul ne sera
+utile que dans weight si le tourbillon a une intersection avec avec un
+tourbillon à une autre date. Donc, pour certains tourbillons, ce
+calcul est inutile. Difficile d'éviter ces calculs inutiles. Il
+faudrait les reporter à weight mais alors il faudrait avoir accès dans
+weight à u et v, c'est-à-dire qu'il faudrait conserver u, v à
+plusieurs dates en dehors de get\_snpashot. Cela remettrait en
+question tout l'algorithme.
+
+\subsection{successive\_overlap}
+
+j, k, scalaires entiers, données. flow : donnée-résultat. Cherche les
+arcs entre flow(j - 1) et flow(j), correspondant aux dates k - 1 et
+k. Écrit ces arcs dans \verb+graph.txt+. Met à jour flow(j -
+1)\%list\_vis\%delta\_out et
+flow(j)\%list\_vis\%delta\_in. Accélération possible en ne comparant à
+i1 que les trois tourbillons les plus proches (distance entre les
+extremums), comme dans la fonction \verb+Eddy_Intersection+ du
+programme pour Matlab.
+
+On ne peut éviter la copie de polylines due à l'alternative contour
+extérieur -- contour de vitesse maximale. Pour ne pas faire la copie
+dans polygon\_1 et polygon\_2, il faudrait faire des copies de
+out\_cont dans speed\_cont dans get\_snapshot, on ne gagnerait rien en
+quantité de copie, et on perdrait en mémoire vive occupée.
+
+\subsection{non\_successive\_overlap}
+
+j, k, delta $\in \{2, \dots, \mathtt{max\_delta}\}$ : scalaires
+entiers, données. flow : donnée-résultat. Cherche les arcs entre
+flow(j - delta) et flow(j), correspondant aux dates k - delta et
+k. Écrit ces arcs dans \verb+graph.txt+, écrit les tourbillons
+interpolés dans les shapefiles via interpolate\_eddy et
+write\_eddy. Met à jour flow(j - delta)\%list\_vis\%delta\_out,
+flow(j)\%list\_vis\%delta\_in, flow\%number\_eddies. Cf. algorithme
+(\ref{alg:non_successive_overlapping}).
+\begin{algorithm}[htbp]
+  \begin{algorithmic}
+    \STATE \COMMENT{recouvrements à dates non successives}
+
+    \FOR{i1 = 1 \TO flow(j - delta)\%number\_vis\_eddies}
+
+    \IF{flow(j - delta)\%list\_vis(i1)\%suff\_amp}
+
+    \STATE polygon\_1 = merge(flow(j -
+    delta)\%list\_vis(i1)\%speed\_cont\%polyline, flow(j -
+    delta)\%list\_vis(i1)\%out\_cont\%polyline, flow(j -
+    delta)\%list\_vis(i1)\%max\_speed $\ne$ 0)
+
+    \STATE i1\_lon = flow(j - delta)\%ind\_extr(1, i1)
+
+    \STATE i1\_lat = flow(j - delta)\%ind\_extr(2, i1)
+
+    \FOR{i2 > 0 dans abs(flow(j)\%extr\_map(i1\_lon - dist\_lim:i1\_lon
+      + dist\_lim, i1\_lat - dist\_lim:i1\_lat + dist\_lim))}
+
+    \IF{flow(j)\%list\_vis(i2)\%suff\_amp et (flow(j -
+      delta)\%list\_vis(i1)\%delta\_out $\ge$ delta ou
+      flow(j)\%list\_vis(i2)\%delta\_in $\ge$ delta) et flow(j -
+      delta)\%list\_vis(i1)\%cyclone $\Leftrightarrow$
+      flow(j)\%list\_vis(i2)\%cyclone}
+
+    \STATE polygon\_2 =
+    merge(flow(j)\%list\_vis(i2)\%speed\_cont\%polyline,
+    flow(j)\%list\_vis(i2)\%out\_cont\%polyline,
+    flow(j)\%list\_vis(i2)\%max\_speed $\ne$ 0)
+
+    \IF{polygon\_1 et polygon\_2 se recouvrent}
+
+    \STATE weight\_delta = weight(flow(j - delta)\%list\_vis(i1),
+    flow(j)\%list\_vis(i2))
+
+    \STATE flow(j - delta + 1)\%number\_eddies += 1
+
+    \STATE appel de interpolate\_eddy(flow(j -
+    delta)\%list\_vis(i1), flow(j)\%list\_vis(i2), j - delta, j, j - delta + 1,
+    k - delta + 1, flow(j - delta + 1)\%number\_eddies)
+
+    \STATE écrire dans graph.txt : k - delta, i1, k - delta + 1,
+    flow(j - delta + 1)\%number\_eddies, weight\_delta
+
+    \FOR{j\_interp = j - delta + 2 \TO j - 1}
+
+    \STATE flow(j\_interp)\%number\_eddies += 1
+
+    \STATE appel de interpolate\_eddy(flow(j -
+    delta)\%list\_vis(i1), flow(j)\%list\_vis(i2), j - delta, j, j\_interp, k -
+    j + j\_interp, flow(j\_interp)\%number\_eddies)
+
+    \STATE écrire dans graph.txt : k - j + j\_interp - 1,
+    flow(j\_interp - 1)\%number\_eddies, k - j + j\_interp,
+    flow(j\_interp)\%number\_eddies, weight\_delta
+    \ENDFOR
+
+    \STATE écrire dans graph.txt : k - 1, flow(j - 1)\%number\_eddies, k,
+    i2, weight\_delta
+
+    \STATE flow(j - delta)\%list\_vis(i1)\%delta\_out = delta
+  
+    \STATE flow(j)\%list\_vis(i2)\%delta\_in = delta
+    \ENDIF
+    \ENDIF
+    \ENDFOR
+    \ENDIF
+    \ENDFOR
+  \end{algorithmic}
+  \caption{Sous-algorithme non\_successive\_overlap(j, k, delta,
+    flow)}
+  \label{alg:non_successive_overlapping}
+\end{algorithm}
+
+\subsection{local\_extrema}
+
+a(ind\_extr(1, k), ind\_extr(2, k)) est un extremum de a par rapport
+aux huit points voisins. extr\_map vaut 0 en un point où il n'y a pas
+d'extremum, le numéro de l'extremum dans ind\_extr en un point
+extremum. Accélération possible, au prix de tableaux supplémentaires,
+en utilisant le fait que deux maximums ne peuvent être côte à côte et
+deux minimums ne peuvent être côte à côte.
+
+J'avais d'abord codé le sens de l'extremum (maximum ou minimum) par le
+signe dans extr\_map. Mais j'ai besoin de coder dans une carte le fait
+que l'amplitude soit significative ou non. J'ajoute donc un argument
+vecteur cyclone à local\_extrema, qui prend moins de place qu'une
+carte supplémentaire. Pour le sens de l'extremum, je n'ai pas besoin
+que l'information soit sur une carte.
+
+Cf. aussi fontion \verb+skimage.feature.peak_local_max+ dans le paquet
+Python scikit-image. La fonction applique
+\verb+scipy.ndimage.maximum_filter+ et compare le résultat au tableau
+de départ. \verb+scipy.ndimage.maximum_filter+ applique
+\verb+scipy.ndimage.maximum_filter1d+ dans chaque dimension. Cf. aussi
+Douglas (1996 k0968).
+
+\subsection{get\_1\_outerm}
+
+Au lieu d'initialiser l'amplitude maximale à 4 m plus du bruit, comme
+dans le programme pour Matlab :
+\begin{algorithmic}
+   \STATE appel de random\_number(level\_try)
+
+   \STATE level\_try = e\%ssh\_extr $\pm$ [4 + (2 * level\_try -
+   1) * precision] \COMMENT{un peu de bruit sur l'amplitude, autour de
+     4}
+\end{algorithmic}
+et de supposer qu'il n'y a pas de bon contour avec cette amplitude, on
+cherche une amplitude maximale possible. Dans le cas le plus simple où
+noise\_around est faux, on purrait parcourir les points dans une
+direction jusqu'à ce que le sens de variation de SSH change. Dans mes
+tests, le nombre d'itérations dans la recherche par dichotomie est
+typiquement une dizaine, pour une précision de \np{0.5} mm. En
+considérant simplement l'extremum de ssh sur la fenêtre, la taille de
+l'intervalle initial [innermost\_level, level\_try] est plus
+grande. Sur mes tests, elle est plus grande typiquement d'un facteur 2
+environ seulement, donc le nombre d'itérations change peu.
+
+Pour l'amplitude minimale, si je prends simplement l'amplitude
+correspondant à innermost\_level alors l'algorithme échoue lorsque
+l'extremum est à un point du bord et que le point à innermost\_level
+est au bord. Dans ce cas, good\_contour échoue parce que Contour\_531
+ne ferme pas les contours passant par le bord. J'avais essayé, pour
+éviter le problème, de prendre une amplitude minimale légèrement
+inférieure mais cela pose des problèmes de cohérence dans
+l'algorithme.
+
+\subsection{interpolate\_eddy}
+
+Tous les arguments sont des données. e1, e2 : scalaires de type
+eddy. j1, j2, indices de position dans la fenêtre correspondant à e1,
+e2. j : indice de position dans la fenêtre auquel on veut
+interpoler. k, i : indices de date et de tourbillon. Cf. algorithme
+(\ref{alg:interpolate_eddy}).
+\begin{algorithm}[htbp]
+  \begin{algorithmic}
+    \STATE e = interpolation entre e1 et e2
+
+    \STATE appel de write\_eddy(e, k, i)
+  \end{algorithmic}
+  \caption{Sous-algorithme interpolate\_eddy(e1, e2, j1, j2, j, k, i)}
+  \label{alg:interpolate_eddy}
+\end{algorithm}
+On peut mettre le champ suff\_amp à faux pour un tourbillon interpolé,
+en cohérence avec un champ out\_cont vide. Il faut alors un
+champ interpolated pour distinguer les tourbillons interpolés de ceux
+pour lesquels un extremum a été détecté mais sans obtenir de contour
+extérieur.
+
+\subsection{good\_contour}
+
+Trouve, si elle existe, une ligne de niveau à un niveau donné,
+contenant un point donné et ne contenant pas un ensemble de points
+donnés.  Le résultat est un polygone à zéro point s'il n'existe pas de
+ligne de niveau satisfaisante. Correspond à une partie de la fonction
+extraction\_eddies dans le programme pour Matlab.
+
+\subsection{write\_eddy}
+
+On pourrait passer en arguments d'entrée les numéros des
+champs dans les fichiers DBF mais cela ferait 13
+arguments.
+
+\subsection{weight}
+
+D'autant plus proche de 0 que les tourbillons sont
+ressemblants. Comment calculer ce poids ? Comment calculer le poids
+faisant intervenir un ou deux tourbillons interpolés ? Pour faire au
+plus simple, j'ai supposé que l'on utilisait pour les arcs faisant
+intervenir les tourbillons interpolés le poids associé aux tourbillons
+visibles entre lesquels on interpole. Normalement, ce choix doit
+donner un poids plus faible à ces arcs.
+
+\section{Tests}
+
+Pour faire des tests : données au 1\ier{} janvier 2006. Tests sur
+différents domaines. Cf. figure (\ref{fig:regions}).
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{regions}
+  \caption{Régions pour les tests.}
+  \label{fig:regions}
+\end{figure}
+Régions 1, 2 et 3 : 29 $\times$ 17, 57 $\times$ 33; 120 $\times$ 120,
+contenant respectivement 8, 27 et 216 extremums.
+
+Pour la région 1, coin en :
+\begin{equation*}
+  \lambda = \np{5.125}^\circ, \phi = -\np{36.375}^\circ
+\end{equation*}
+soit $(\np[rad]{0.0894}, - \np[rad]{0.6349})$. Indices base 1 du coin
+: 21, 215. Pas de grille : \np{0.25}°, soit \np[rad]{0.0044}. Nombre
+de points de grille : $29 \times 17$. 5 minimums et 3 maximums dans ce
+cadre. Cf. figure (\ref{fig:extrema}).
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{extrema}
+  \caption{Région 1, lignes de niveau de SSH (sans signification
+    particulière). Maximums : points rouges, minimums : points bleus.}
+  \label{fig:extrema}
+\end{figure}
+Minimum cible en :
+\begin{equation*}
+  \lambda = \np{9.625}^\circ, \phi = - \np{33.875}^\circ
+\end{equation*}
+Indices base 1 de ce minimum : 39, 225. SSH en ce mimimum :
+\np{0.2759}. Extremum numéro 6 dans ce cadre. Contour le plus
+intérieur : SSH = \np{0.293300003} m. Contour le plus extérieur : SSH
+= \np{0.460744} m. Contour de vitesse maximale : SSH = \np{0.3815}
+m. Cf. figures (\ref{fig:test_get_eddy}) et (\ref{fig:test_get_snapshot}).
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{test_get_eddy}
+  \caption{Projection stéréographique centrée sur un minimum de
+    SSH. Champ de vitesse, contour de SSH le plus extérieur en bleu,
+    contour de SSH de vitesse maximale en vert. Le minimum de SSH est
+    marqué par un point.}
+  \label{fig:test_get_eddy}
+\end{figure}
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{test_get_snapshot}
+  \caption{Région 1, sans minimum d'amplitude. Contours extérieurs et
+    contours de vitesse maximale. En rouge les anticyclones, en bleu
+    les cyclones. Croix : contour extérieur sans contour de maximum de
+    vitesse distinct. Cercles : contour extérieur et contour de
+    maximum de vitesse distinct. Les contours extérieurs 1, 2 et 8
+    passent près de l'extremum et il n'y a donc pas de contour de
+    vitesse maximale correspondant. Les contours extérieurs 1 et 7
+    touchent le bord et sont donc peut-être artificiellement
+    sous-estimés.}
+  \label{fig:test_get_snapshot}
+\end{figure}
+
+Comparaison sur la région 1 avec le programme Matlab, pour les
+tourbillons 4 et 5. Les coordonnées des extremums sont bien les
+mêmes. Les SSH sur les contours extérieurs sont pratiquement les mêmes
+: différence de l'ordre de \np{0.1} mm. Du coup les contours
+extérieurs sont quasi-identiques. Les rayons des contours extérieurs
+sont presque les mêmes aussi : j'ai \np{87.7} km au lieu de \np{87.5}
+km et \np{90.80} km au lieu de \np{90.81} km. Les SSH sur les contours
+de vitesse max diffèrent de 2 cm environ. Cf. figure
+(\ref{fig:comparaison_Matlab}).
+\begin{figure}[htbp]
+  \centering
+  \includegraphics[width=\textwidth]{comparaison_201}
+  \includegraphics[width=\textwidth]{comparaison_202}
+  \caption{Comparaison avec le programme Matlab. Pour chacun des deux
+    tourbillons, le contour sans marqueur est à la valeur de SSH
+    donnée par le programme Matlab.}
+  \label{fig:comparaison_Matlab}
+\end{figure}
+J'obtiens comme vitesse maximale : $\np[m s^{-1}]{0.29}$ au lieu de
+$\np[m s^{-1}]{0.30}$ et $\np[m s^{-1}]{0.23}$ au lieu de
+$\np[m s^{-1}]{0.26}$.
+
+Test sur la région 3, sans minimum d'amplitude. 14 extremums sans
+contour extérieur (suff\_amp faux), dont 5 sur les bords et 11
+dégénérés. Avec minimum d'amplitude : 34 extremums pour lesquels
+suff\_amp est faux. Je vérifie que, pour chaque extremum, si suff\_amp
+est faux dans le cas sans minimum, suff\_amp reste faux dans le cas
+avec minimum. Il y a donc 20 extremums qui passent à suff\_amp faux à
+cause du minimum d'amplitude. Je vérifie que, pour ces 20 extremums,
+le contour extérieur dans le cas sans minimum est le même que dans le
+cas avec minimum. Entre le cas sans minimum et le cas avec minimum,
+seulement trois contours extérieurs grossissent (je le vois en
+comparant les fichiers outermost\_contour\_1.dbf) : dans le cas avec
+minimum, ils englobent un extremum d'amplitude insuffisante. Dans le
+cas avec minimum, 48 contours extérieurs sont calculés deux fois.
+
+\end{document}