From 68dc481ae3e4967b4464f922c9f84d2025dedf3b Mon Sep 17 00:00:00 2001
From: "francois.grand" <francois.grand@irstea.fr>
Date: Thu, 19 Oct 2017 12:07:09 +0200
Subject: [PATCH] =?UTF-8?q?d=C3=A9but=20d'impl=C3=A9mentation=20des=20cour?=
 =?UTF-8?q?bes=20de=20remous=20(int=C3=A9gration=20par=20la=20m=C3=A9thode?=
 =?UTF-8?q?=20des=20trap=C3=A8zes)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 spec/regime_uniforme_circ.spec.ts      |   4 +-
 spec/regime_uniforme_puissance.spec.ts |   4 +-
 spec/regime_uniforme_trapeze.spec.ts   |   4 +-
 spec/remous_rect.ts                    | 340 ++++++++++++
 spec/remous_trapez.ts                  |  65 +++
 src/base.ts                            |  18 +-
 src/nub.ts                             |   8 +-
 src/param.ts                           |  31 +-
 src/remous.ts                          | 705 +++++++++++++++++++++++++
 src/util/error.ts                      |  10 +
 10 files changed, 1177 insertions(+), 12 deletions(-)
 create mode 100644 spec/remous_rect.ts
 create mode 100644 spec/remous_trapez.ts
 create mode 100644 src/remous.ts

diff --git a/spec/regime_uniforme_circ.spec.ts b/spec/regime_uniforme_circ.spec.ts
index 632c947c..6ac8421c 100644
--- a/spec/regime_uniforme_circ.spec.ts
+++ b/spec/regime_uniforme_circ.spec.ts
@@ -29,7 +29,7 @@ describe('Class RegimeUniforme / section circulaire :', () => {
         });
 
 
-        it('Ks should be 40.002', () => {
+        it('Ks should be 40.003', () => {
             let paramSection = new ParamsSectionCirc(6, // diamètre
                 0.6613, // tirant d'eau
                 undefined, //  Ks=Strickler
@@ -42,7 +42,7 @@ describe('Class RegimeUniforme / section circulaire :', () => {
             let sect = new cSnCirc(undefined, paramSection);
             let ru = new RegimeUniforme(sect);
 
-            check(ru.Calc("Ks", 1e-8), 40.002);
+            check(ru.Calc("Ks", 1e-8), 40.003);
         });
 
 
diff --git a/spec/regime_uniforme_puissance.spec.ts b/spec/regime_uniforme_puissance.spec.ts
index 4c351ee5..da7c835c 100644
--- a/spec/regime_uniforme_puissance.spec.ts
+++ b/spec/regime_uniforme_puissance.spec.ts
@@ -29,7 +29,7 @@ describe('Class RegimeUniforme / section puissance :', () => {
             check(ru.Calc("k"), 0.635);
         });
 
-        it('LargeurBerge should be 3.474', () => {
+        it('LargeurBerge should be 3.473', () => {
             let prms = new ParamsSectionPuiss(0.5, // coef
                 0.8, // tirant d'eau
                 undefined, // largeur de berge
@@ -43,7 +43,7 @@ describe('Class RegimeUniforme / section puissance :', () => {
             let sect = new cSnPuiss(undefined, prms);
             let ru = new RegimeUniforme(sect, false);
 
-            check(ru.Calc("LargeurBerge"), 3.474);
+            check(ru.Calc("LargeurBerge"), 3.473);
         });
 
         it('Strickler should be 33.774', () => {
diff --git a/spec/regime_uniforme_trapeze.spec.ts b/spec/regime_uniforme_trapeze.spec.ts
index bf19a006..f0d75d52 100644
--- a/spec/regime_uniforme_trapeze.spec.ts
+++ b/spec/regime_uniforme_trapeze.spec.ts
@@ -158,7 +158,7 @@ describe('Class RegimeUniforme / section trapèze :', () => {
             check(ru.Calc("Fruit"), 0.56);
         });
 
-        it('Ks should be 5.745', () => {
+        it('Ks should be 5.744', () => {
             let prms = new ParamsSectionTrapez(2.5, // largeur de fond
                 0.56, // fruit
                 2, // tirant d'eau
@@ -172,7 +172,7 @@ describe('Class RegimeUniforme / section trapèze :', () => {
             let sect = new cSnTrapez(undefined, prms);
             let ru = new RegimeUniforme(sect);
 
-            check(ru.Calc("Ks", 1e-8), 5.745);
+            check(ru.Calc("Ks", 1e-8), 5.744);
         });
 
         it('If should be 0.001', () => {
diff --git a/spec/remous_rect.ts b/spec/remous_rect.ts
new file mode 100644
index 00000000..6a4f3f30
--- /dev/null
+++ b/spec/remous_rect.ts
@@ -0,0 +1,340 @@
+import { ParamsSectionRectang, cSnRectang } from "../src/section/section_rectang";
+import { CourbeRemousParams, MethodeResolution, CourbeRemous } from "../src/remous";
+import { precDigits, precDist, equalEpsilon } from "./nubtest";
+import { round } from "../src/base";
+
+/**
+ * compare 2 objets
+ * @param s message
+ * @param objTest objet à tester
+ * @param objValid objet de référence
+ * @param epsilon tolérance pour les comparaisons de nombres
+ */
+function compObject(s: string, objTest: { [key: number]: number }, objValid: { [key: number]: number }, epsilon: number) {
+    let n1 = Object.keys(objTest).length;
+    let n2 = Object.keys(objValid).length;
+    let b: boolean = n1 == n2;
+    expect(b).toBeTruthy(s + ": longueur incorrecte " + n1 + ", devrait etre " + n2);
+    if (!b) return;
+
+    for (let i = 0; i < n1; i++) {
+        let v1: number = objTest[+Object.keys(objTest)[i]];
+        let v2: number = objValid[+Object.keys(objValid)[i]];
+        b = equalEpsilon(v1, v2, epsilon);
+        expect(b).toBeTruthy(s + " : " + i + "ieme valeur incorrecte " + v1 + ", devrait etre " + v2);
+        if (!b) return;
+    }
+}
+
+/**
+ * compare 2 tableaux
+ * @param s message
+ * @param arrTest tableau à tester
+ * @param arrValid tableau de référence
+ */
+function compArray(s: string, arrTest: string[], arrValid: number[]) {
+    let n1 = arrTest.length;
+    let n2 = arrValid.length;
+    let b: boolean = n1 == n2;
+    expect(b).toBeTruthy(s + ": longueur incorrecte " + n1 + ", devrait etre " + n2);
+    if (!b) return;
+
+    for (let i = 0; i < arrTest.length; i++) {
+        let v1: number = +arrTest[i];
+        let v2: number = arrValid[i];
+        b = equalEpsilon(v1, v2);
+        expect(b).toBeTruthy(s + " : " + i + "ieme valeur incorrecte " + v1 + ", devrait etre " + v2);
+        if (!b) return;
+    }
+}
+
+describe('Class Remous / section rectangulaire :', () => {
+    xdescribe('méthode trapèzes :', () => {
+        it("forte pente, ressaut avant l'amont", () => {
+            let prms = new ParamsSectionRectang(undefined, // tirant d'eau
+                2.5, // largeur de fond
+                40, //  Ks=Strickler
+                2, // Q=Débit
+                0.05, // If=pente du fond
+                precDist, // précision
+                1, // YB=hauteur de berge
+                undefined,// YCL=Condition limite en cote à l'amont ou à l'aval
+                5,  // Dx=Pas d'espace
+                100  // Long= Longueur du bief
+            );
+
+            let sect = new cSnRectang(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.15, // Yamont = tirant amont
+                6, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            let f = { 100.000: 6, 95.000: 5.75, 90.000: 5.5, 85.000: 5.25, 80.000: 4.999, 75.000: 4.749, 70.000: 4.499, 65.000: 4.249, 60.000: 3.998, 55.000: 3.748, 50.000: 3.498, 45.000: 3.248, 40.000: 2.997, 35.000: 2.747, 30.000: 2.497, 25.000: 2.246, 20.000: 1.995, 15.000: 1.744, 10.000: 1.491, 5.000: 1.237, 0.000: 0.977 };
+            compObject("Yfluvial", res["flu"], f, 0.03);
+
+            let t = {};
+            compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            let x = [0.000, 5.000, 10.000, 15.000, 20.000, 25.000, 30.000, 35.000, 40.000, 45.000, 50.000, 55.000, 60.000, 65.000, 70.000, 75.000, 80.000, 85.000, 90.000, 95.000, 100.000];
+            compArray("abcisses", res["trX"], x);
+        });
+
+        it("forte pente, ressaut après l'aval", () => {
+            // TODO algo à reprendre
+            let prms = new ParamsSectionRectang(undefined, // tirant d'eau
+                2.5, // largeur de fond
+                40, //  Ks=Strickler
+                2, // Q=Débit
+                0.05, // If=pente du fond
+                precDist, // précision
+                1, // YB=hauteur de berge
+                undefined,// YCL=Condition limite en cote à l'amont ou à l'aval
+                5,  // Dx=Pas d'espace
+                100  // Long= Longueur du bief
+            );
+
+            let sect = new cSnRectang(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.15, // Yamont = tirant amont
+                0.45, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            let f = {};
+            compObject("Yfluvial", res["flu"], f, 0.03);
+
+            // let t = { 0.000: 0.15, 5.000: 0.207, 10.000: 0.235, 15.000: 0.246, 20.000: 0.25, 25.000: 0.252, 30.000: 0.253, 35.000: 0.253, 40.000: 0.253, 45.000: 0.253, 50.000: 0.253, 55.000: 0.253, 60.000: 0.253, 65.000: 0.253, 70.000: 0.253, 75.000: 0.253, 80.000: 0.253, 85.000: 0.253, 90.000: 0.253, 95.000: 0.253, 100.000: 0.253 };
+            let t = { 0.000: 0.15, 5.000: 0.207, 10.000: 0.235, 15.000: 0.246, 20.000: 0.25, 25.000: 0.252, 30.000: 0.253, 35.000: 0.253, 40.000: 0.253, 45.000: 0.253, 50.000: 0.253, 55.000: 0.253, 60.000: 0.253, 65.000: 0.253, 70.000: 0.253, 75.000: 0.253, 80.000: 0.253, 85.000: 0.253, 90.000: 0.253, 95.000: 0.253, 100.000: 0.45 };
+            compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            let x = [0.000, 5.000, 10.000, 15.000, 20.000, 25.000, 30.000, 35.000, 40.000, 45.000, 50.000, 55.000, 60.000, 65.000, 70.000, 75.000, 80.000, 85.000, 90.000, 95.000, 100.000];
+            compArray("abcisses", res["trX"], x);
+        });
+
+        it("forte pente, ressaut (1 point) à l'intérieur du bief", () => {
+            let prms = new ParamsSectionRectang(undefined, // tirant d'eau
+                2.5, // largeur de fond
+                40, //  Ks=Strickler
+                2, // Q=Débit
+                0.05, // If=pente du fond
+                precDist, // précision
+                1, // YB=hauteur de berge
+                undefined,// YCL=Condition limite en cote à l'amont ou à l'aval
+                5,  // Dx=Pas d'espace
+                100  // Long= Longueur du bief
+            );
+
+            let sect = new cSnRectang(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.15, // Yamont = tirant amont
+                1, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            let f = { 100.000: 1, 95.000: 0.729 };
+            compObject("Yfluvial", res["flu"], f, 0.03);
+
+            let t = { 0.000: 0.15, 5.000: 0.207, 10.000: 0.235, 15.000: 0.246, 20.000: 0.25, 25.000: 0.252, 30.000: 0.253, 35.000: 0.253, 40.000: 0.253, 45.000: 0.253, 50.000: 0.253, 55.000: 0.253, 60.000: 0.253, 65.000: 0.253, 70.000: 0.253, 75.000: 0.253, 80.000: 0.253, 85.000: 0.253, 90.000: 0.253, 95.000: 0.729 };
+            compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            let x = [0.000, 5.000, 10.000, 15.000, 20.000, 25.000, 30.000, 35.000, 40.000, 45.000, 50.000, 55.000, 60.000, 65.000, 70.000, 75.000, 80.000, 85.000, 90.000, 95.000, 100.000];
+            compArray("abcisses", res["trX"], x);
+        });
+
+        it("forte pente, ressaut (plusieurs points) à l'intérieur du bief", () => {
+            let prms = new ParamsSectionRectang(undefined, // tirant d'eau
+                2.5, // largeur de fond
+                40, //  Ks=Strickler
+                2, // Q=Débit
+                0.05, // If=pente du fond
+                precDist, // précision
+                1, // YB=hauteur de berge
+                undefined,// YCL=Condition limite en cote à l'amont ou à l'aval
+                0.25,  // Dx=Pas d'espace
+                5.5  // Long= Longueur du bief
+            );
+
+            let sect = new cSnRectang(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.15, // Yamont = tirant amont
+                1, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            let f = { 5.500: 1, 5.250: 0.987, 5.000: 0.974, 4.750: 0.96, 4.500: 0.947, 4.250: 0.933, 4.000: 0.92, 3.750: 0.906, 3.500: 0.893, 3.250: 0.88, 3.000: 0.866, 2.750: 0.853, 2.500: 0.839, 2.250: 0.826, 2.000: 0.812, 1.750: 0.798, 1.500: 0.784, 1.250: 0.77, 1.000: 0.756, 0.750: 0.742, 0.500: 0.727, 0.250: 0.712, 0.000: 0.15 };
+            compObject("Yfluvial", res["flu"], f, 0.03);
+
+            let t = { 0.000: 0.15, 0.250: 0.153, 0.500: 0.156, 0.750: 0.158, 1.000: 0.161, 1.250: 0.163, 1.500: 0.166, 1.750: 0.168, 2.000: 0.17, 2.250: 0.173, 2.500: 0.175, 2.750: 0.177, 3.000: 0.18, 3.250: 0.182, 3.500: 0.184, 3.750: 0.906 };
+            compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            let x = [0.000, 0.250, 0.500, 0.750, 1.000, 1.250, 1.500, 1.750, 2.000, 2.250, 2.500, 2.750, 3.000, 3.250, 3.500, 3.750, 4.000, 4.250, 4.500, 4.750, 5.000, 5.250, 5.500];
+            compArray("abcisses", res["trX"], x);
+        });
+
+        it("faible pente, ressaut avant l'amont", () => {
+            let prms = new ParamsSectionRectang(undefined, // tirant d'eau
+                2.5, // largeur de fond
+                40, //  Ks=Strickler
+                2, // Q=Débit
+                0.001, // If=pente du fond
+                precDist, // précision
+                1, // YB=hauteur de berge
+                undefined,// YCL=Condition limite en cote à l'amont ou à l'aval
+                5,  // Dx=Pas d'espace
+                100  // Long= Longueur du bief
+            );
+
+            let sect = new cSnRectang(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.3, // Yamont = tirant amont
+                0.403, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            let f = { 100.000: 0.403, 95.000: 0.524, 90.000: 0.558, 85.000: 0.584, 80.000: 0.604, 75.000: 0.621, 70.000: 0.637, 65.000: 0.65, 60.000: 0.662, 55.000: 0.673, 50.000: 0.684, 45.000: 0.693, 40.000: 0.701, 35.000: 0.709, 30.000: 0.717, 25.000: 0.725, 20.000: 0.731, 15.000: 0.738, 10.000: 0.744, 5.000: 0.75, 0.000: 0.755 };
+            compObject("Yfluvial", res["flu"], f, 0.03);
+
+            let t = {};
+            compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            let x = [0.000, 5.000, 10.000, 15.000, 20.000, 25.000, 30.000, 35.000, 40.000, 45.000, 50.000, 55.000, 60.000, 65.000, 70.000, 75.000, 80.000, 85.000, 90.000, 95.000, 100.000];
+            compArray("abcisses", res["trX"], x);
+        });
+
+        it("faible pente, ressaut (1 point) à l'intérieur du bief", () => {
+            let prms = new ParamsSectionRectang(undefined, // tirant d'eau
+                2.5, // largeur de fond
+                40, //  Ks=Strickler
+                2, // Q=Débit
+                0.001, // If=pente du fond
+                precDist, // précision
+                1, // YB=hauteur de berge
+                undefined,// YCL=Condition limite en cote à l'amont ou à l'aval
+                5,  // Dx=Pas d'espace
+                100  // Long= Longueur du bief
+            );
+
+            let sect = new cSnRectang(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.15, // Yamont = tirant amont
+                0.403, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            let f = { 100.000: 0.403, 95.000: 0.524, 90.000: 0.558, 85.000: 0.584, 80.000: 0.604, 75.000: 0.621, 70.000: 0.637, 65.000: 0.65, 60.000: 0.662, 55.000: 0.673, 50.000: 0.684, 45.000: 0.693, 40.000: 0.701, 35.000: 0.709, 30.000: 0.717, 25.000: 0.725, 20.000: 0.731, 15.000: 0.738, 10.000: 0.744, 5.000: 0.75, 0.000: 0.15 };
+            compObject("Yfluvial", res["flu"], f, 0.03);
+
+            let t = { 0.000: 0.15, 5.000: 0.75 };
+            compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            let x = [0.000, 5.000, 10.000, 15.000, 20.000, 25.000, 30.000, 35.000, 40.000, 45.000, 50.000, 55.000, 60.000, 65.000, 70.000, 75.000, 80.000, 85.000, 90.000, 95.000, 100.000];
+            compArray("abcisses", res["trX"], x);
+        });
+
+        it("faible pente, ressaut (plusieurs points) à l'intérieur du bief (1)", () => {
+            let prms = new ParamsSectionRectang(undefined, // tirant d'eau
+                2.5, // largeur de fond
+                40, //  Ks=Strickler
+                2, // Q=Débit
+                0.001, // If=pente du fond
+                precDist, // précision
+                1, // YB=hauteur de berge
+                undefined,// YCL=Condition limite en cote à l'amont ou à l'aval
+                // 0.05,  // Dx=Pas d'espace
+                0.25,  // Dx=Pas d'espace
+                5  // Long= Longueur du bief
+            );
+
+            let sect = new cSnRectang(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.01, // Yamont = tirant amont
+                0.403, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            // //dx = 0.05
+            // let f = { 5.000: 0.403, 4.950: 0.414, 4.900: 0.419, 4.850: 0.423, 4.800: 0.426, 4.750: 0.429, 4.700: 0.432, 4.650: 0.434, 4.600: 0.436, 4.550: 0.438, 4.500: 0.439, 4.450: 0.441, 4.400: 0.443, 4.350: 0.445, 4.300: 0.446, 4.250: 0.447, 4.200: 0.449, 4.150: 0.45, 4.100: 0.451, 4.050: 0.452, 4.000: 0.454, 3.950: 0.455, 3.900: 0.456, 3.850: 0.457, 3.800: 0.458, 3.750: 0.46, 3.700: 0.461, 3.650: 0.462, 3.600: 0.463, 3.550: 0.465, 3.500: 0.466, 3.450: 0.467, 3.400: 0.468, 3.350: 0.469, 3.300: 0.469, 3.250: 0.47, 3.200: 0.471, 3.150: 0.471, 3.100: 0.472, 3.050: 0.472, 3.000: 0.473, 2.950: 0.474, 2.900: 0.474, 2.850: 0.475, 2.800: 0.476, 2.750: 0.476, 2.700: 0.477, 2.650: 0.477, 2.600: 0.478, 2.550: 0.479, 2.500: 0.479, 2.450: 0.48, 2.400: 0.48, 2.350: 0.481, 2.300: 0.482, 2.250: 0.482, 2.200: 0.483, 2.150: 0.483, 2.100: 0.484, 2.050: 0.485, 2.000: 0.485, 1.950: 0.486, 1.900: 0.486, 1.850: 0.487, 1.800: 0.488, 1.750: 0.488, 1.700: 0.489, 1.650: 0.49, 1.600: 0.49, 1.550: 0.491, 1.500: 0.491, 1.450: 0.492, 1.400: 0.493, 1.350: 0.493, 1.300: 0.494, 1.250: 0.494, 1.200: 0.495, 1.150: 0.496, 1.100: 0.496, 1.050: 0.497, 1.000: 0.497, 0.950: 0.498, 0.900: 0.499, 0.850: 0.499, 0.800: 0.5, 0.750: 0.501, 0.700: 0.501, 0.650: 0.502, 0.600: 0.502, 0.550: 0.503, 0.500: 0.504, 0.450: 0.504, 0.400: 0.505, 0.350: 0.505, 0.300: 0.506, 0.250: 0.507, 0.200: 0.507, 0.150: 0.508, 0.100: 0.508, 0.050: 0.509, 0.000: 0.01 };
+            // compObject("Yfluvial", res["flu"], f, 0.03);
+
+            // //let t = { 0.000: 0.01, 0.050: 0.011, 0.100: 0.013, 0.150: 0.015, 0.200: 0.016, 0.250: 0.018, 0.300: 0.019, 0.350: 0.02, 0.400: 0.021, 0.450: 0.022, 0.500: 0.022, 0.550: 0.023, 0.600: 0.024, 0.650: 0.025, 0.700: 0.026, 0.750: 0.026, 0.800: 0.027, 0.850: 0.028, 0.900: 0.031750000000000014, 0.95: 0.5042500000000006 }; // ok
+            // let t = { 0.000: 0.01, 0.050: 0.011, 0.100: 0.013, 0.150: 0.015, 0.200: 0.016, 0.250: 0.018, 0.300: 0.019, 0.350: 0.02, 0.400: 0.021, 0.450: 0.022, 0.500: 0.022, 0.550: 0.023, 0.600: 0.024, 0.650: 0.025, 0.700: 0.026, 0.750: 0.026, 0.800: 0.027, 0.850: 0.028, 0.900: 0.499 };
+            // compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            // let x = [0.000, 0.050, 0.100, 0.150, 0.200, 0.250, 0.300, 0.350, 0.400, 0.450, 0.500, 0.550, 0.600, 0.650, 0.700, 0.750, 0.800, 0.850, 0.900, 0.950, 1.000, 1.050, 1.100, 1.150, 1.200, 1.250, 1.300, 1.350, 1.400, 1.450, 1.500, 1.550, 1.600, 1.650, 1.700, 1.750, 1.800, 1.850, 1.900, 1.950, 2.000, 2.050, 2.100, 2.150, 2.200, 2.250, 2.300, 2.350, 2.400, 2.450, 2.500, 2.550, 2.600, 2.650, 2.700, 2.750, 2.800, 2.850, 2.900, 2.950, 3.000, 3.050, 3.100, 3.150, 3.200, 3.250, 3.300, 3.350, 3.400, 3.450, 3.500, 3.550, 3.600, 3.650, 3.700, 3.750, 3.800, 3.850, 3.900, 3.950, 4.000, 4.050, 4.100, 4.150, 4.200, 4.250, 4.300, 4.350, 4.400, 4.450, 4.500, 4.550, 4.600, 4.650, 4.700, 4.750, 4.800, 4.850, 4.900, 4.950, 5.000];
+            // compArray("abcisses", res["trX"], x);
+
+            //dx = 0.25
+            let f = { 5.000: 0.403, 4.750: 0.43, 4.500: 0.44, 4.250: 0.448, 4.000: 0.455, 3.750: 0.46, 3.500: 0.465, 3.250: 0.47, 3.000: 0.474, 2.750: 0.479, 2.500: 0.482, 2.250: 0.486, 2.000: 0.489, 1.750: 0.492, 1.500: 0.495, 1.250: 0.498, 1.000: 0.501, 0.750: 0.504, 0.500: 0.506, 0.250: 0.508, 0.000: 0.01 };
+            compObject("Yfluvial", res["flu"], f, 0.03);
+
+            let t = { 0.000: 0.01, 0.250: 0.022, 0.500: 0.027, 0.750: 0.033, 1.000: 0.501 };
+            compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            let x = [0.000, 0.250, 0.500, 0.750, 1.000, 1.250, 1.500, 1.750, 2.000, 2.250, 2.500, 2.750, 3.000, 3.250, 3.500, 3.750, 4.000, 4.250, 4.500, 4.750, 5.000];
+            compArray("abcisses", res["trX"], x);
+        });
+
+        it("faible pente, ressaut (plusieurs points) à l'intérieur du bief (2)", () => {
+            let prms = new ParamsSectionRectang(undefined, // tirant d'eau
+                2.5, // largeur de fond
+                40, //  Ks=Strickler
+                2, // Q=Débit
+                0.001, // If=pente du fond
+                0.0001, // précision
+                1, // YB=hauteur de berge
+                undefined,// YCL=Condition limite en cote à l'amont ou à l'aval
+                0.05,  // Dx=Pas d'espace
+                5  // Long= Longueur du bief
+            );
+
+            let sect = new cSnRectang(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.01, // Yamont = tirant amont
+                0.403, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            let f = { 5.0000: 0.403, 4.9500: 0.415, 4.9000: 0.42, 4.8500: 0.423, 4.8000: 0.426, 4.7500: 0.429, 4.7000: 0.432, 4.6500: 0.434, 4.6000: 0.436, 4.5500: 0.438, 4.5000: 0.44, 4.4500: 0.441, 4.4000: 0.443, 4.3500: 0.445, 4.3000: 0.446, 4.2500: 0.448, 4.2000: 0.449, 4.1500: 0.45, 4.1000: 0.452, 4.0500: 0.453, 4.0000: 0.454, 3.9500: 0.455, 3.9000: 0.457, 3.8500: 0.458, 3.8000: 0.459, 3.7500: 0.46, 3.7000: 0.461, 3.6500: 0.462, 3.6000: 0.463, 3.5500: 0.464, 3.5000: 0.465, 3.4500: 0.466, 3.4000: 0.467, 3.3500: 0.468, 3.3000: 0.469, 3.2500: 0.47, 3.2000: 0.471, 3.1500: 0.471, 3.1000: 0.472, 3.0500: 0.473, 3.0000: 0.474, 2.9500: 0.475, 2.9000: 0.476, 2.8500: 0.476, 2.8000: 0.477, 2.7500: 0.478, 2.7000: 0.479, 2.6500: 0.479, 2.6000: 0.48, 2.5500: 0.481, 2.5000: 0.482, 2.4500: 0.482, 2.4000: 0.483, 2.3500: 0.484, 2.3000: 0.484, 2.2500: 0.485, 2.2000: 0.486, 2.1500: 0.487, 2.1000: 0.487, 2.0500: 0.488, 2.0000: 0.489, 1.9500: 0.489, 1.9000: 0.49, 1.8500: 0.49, 1.8000: 0.491, 1.7500: 0.492, 1.7000: 0.492, 1.6500: 0.493, 1.6000: 0.494, 1.5500: 0.494, 1.5000: 0.495, 1.4500: 0.495, 1.4000: 0.496, 1.3500: 0.497, 1.3000: 0.497, 1.2500: 0.498, 1.2000: 0.498, 1.1500: 0.499, 1.1000: 0.499, 1.0500: 0.5, 1.0000: 0.5, 0.9500: 0.501, 0.9000: 0.502, 0.8500: 0.502, 0.8000: 0.503, 0.7500: 0.503, 0.7000: 0.504, 0.6500: 0.504, 0.6000: 0.505, 0.5500: 0.505, 0.5000: 0.506, 0.4500: 0.506, 0.4000: 0.507, 0.3500: 0.507, 0.3000: 0.508, 0.2500: 0.508, 0.2000: 0.509, 0.1500: 0.509, 0.1000: 0.51, 0.0500: 0.51, 0.0000: 0.01 };
+            compObject("Yfluvial", res["flu"], f, 0.03);
+
+            let t = { 0.0000: 0.01, 0.0500: 0.011, 0.1000: 0.013, 0.1500: 0.014, 0.2000: 0.015, 0.2500: 0.017, 0.3000: 0.018, 0.3500: 0.019, 0.4000: 0.02, 0.4500: 0.021, 0.5000: 0.022, 0.5500: 0.024, 0.6000: 0.025, 0.6500: 0.026, 0.7000: 0.027, 0.7500: 0.028, 0.8000: 0.029, 0.8500: 0.03, 0.9000: 0.502 };
+            compObject("Ytorrentiel", res["tor"], t, 0.03);
+
+            let x = [0.0000, 0.0500, 0.1000, 0.1500, 0.2000, 0.2500, 0.3000, 0.3500, 0.4000, 0.4500, 0.5000, 0.5500, 0.6000, 0.6500, 0.7000, 0.7500, 0.8000, 0.8500, 0.9000, 0.9500, 1.0000, 1.0500, 1.1000, 1.1500, 1.2000, 1.2500, 1.3000, 1.3500, 1.4000, 1.4500, 1.5000, 1.5500, 1.6000, 1.6500, 1.7000, 1.7500, 1.8000, 1.8500, 1.9000, 1.9500, 2.0000, 2.0500, 2.1000, 2.1500, 2.2000, 2.2500, 2.3000, 2.3500, 2.4000, 2.4500, 2.5000, 2.5500, 2.6000, 2.6500, 2.7000, 2.7500, 2.8000, 2.8500, 2.9000, 2.9500, 3.0000, 3.0500, 3.1000, 3.1500, 3.2000, 3.2500, 3.3000, 3.3500, 3.4000, 3.4500, 3.5000, 3.5500, 3.6000, 3.6500, 3.7000, 3.7500, 3.8000, 3.8500, 3.9000, 3.9500, 4.0000, 4.0500, 4.1000, 4.1500, 4.2000, 4.2500, 4.3000, 4.3500, 4.4000, 4.4500, 4.5000, 4.5500, 4.6000, 4.6500, 4.7000, 4.7500, 4.8000, 4.8500, 4.9000, 4.9500, 5.0000];
+            compArray("abcisses", res["trX"], x);
+        });
+    });
+});
diff --git a/spec/remous_trapez.ts b/spec/remous_trapez.ts
new file mode 100644
index 00000000..bddd8d01
--- /dev/null
+++ b/spec/remous_trapez.ts
@@ -0,0 +1,65 @@
+import { ParamsSectionTrapez, cSnTrapez } from "../src/section/section_trapez";
+import { CourbeRemousParams, MethodeResolution, CourbeRemous } from "../src/remous";
+import { precDigits, precDist, equalEpsilon } from "./nubtest";
+import { round } from "../src/base";
+
+function compObject(s: string, arr1: { [key: number]: number }, arr2: { [key: number]: number }) {
+    expect(Object.keys(arr1).length).toEqual(Object.keys(arr2).length, s + ": longueur incorrecte");
+
+    let arrVals: number[] = [];
+    for (let i in arr1) {
+        let v1: number = arr1[i];
+        let v2: number = arr2[i];
+        expect(equalEpsilon(v1, v2)).toBeTruthy(s + " : " + i + "ieme valeur incorrecte " + v1 + " " + v2);
+    }
+}
+
+function compArray(s: string, arr1: string[], arr2: number[]) {
+    expect(arr1.length).toEqual(arr2.length);
+
+    let arrVals: number[] = [];
+    for (let i = 0; i < arr1.length; i++) {
+        let v1: number = +arr1[i];
+        let v2: number = arr2[i];
+        expect(equalEpsilon(v1, v2)).toBeTruthy(s + " : " + i + "ieme valeur incorrecte " + v1 + " " + v2);
+    }
+}
+
+xdescribe('Class Remous / section trapèze :', () => {
+    describe('méthode trapèzes :', () => {
+        it('test 1', () => {
+            let prms = new ParamsSectionTrapez(2.5, // largeur de fond
+                0.56, // fruit
+                undefined, // tirant d'eau
+                40, //  Ks=Strickler
+                2,  //  Q=Débit
+                0.001, //  If=pente du fond
+                precDist, // précision
+                1, // YB= hauteur de berge
+                undefined, // YCL=Condition limite en cote à l'amont ou à l'aval
+                1,  // Dx=Pas d'espace (positif en partant de l'aval, négatif en partant de l'amont)
+                10  // Long= Longueur du bief
+            );
+
+            let sect = new cSnTrapez(undefined, prms);
+
+            let prem = new CourbeRemousParams(0.15, // Yamont = tirant amont
+                0.4, // Yaval = tirant aval
+                MethodeResolution.Trapezes
+            );
+
+            let rem = new CourbeRemous(sect, prem);
+
+            let res = rem.calculRemous(undefined);
+
+            let f = { 9: 0.278, 10: 0.4 };
+            compObject("Yfluvial", res["flu"], f);
+
+            let t = { 0.000: 0.15, 1.000: 0.16369914454109, 2.000: 0.17743613485223, 3.000: 0.19117312516337, 4.000: 0.20491011547451, 5.000: 0.21864710578565, 6.000: 0.23238409609679, 7.000: 0.24688425253633, 8.000: 0.26214757510426, 9.000: 0.27817406380059, 10.000: 0.4 };
+            compObject("Ytorrentiel", res["tor"], t);
+
+            let x = [0.000, 1.000, 2.000, 3.000, 4.000, 5.000, 6.000, 7.000, 8.000, 9.000, 10.000];
+            compArray("abcisses", res["trX"], x);
+        });
+    });
+});
diff --git a/src/base.ts b/src/base.ts
index c8b79b78..3006c6d1 100644
--- a/src/base.ts
+++ b/src/base.ts
@@ -65,7 +65,6 @@ export class Result {
 
     private _errorMessage: ErrorMessage;
 
-    // constructor(v: number, c: ErrorCode = undefined) {
     constructor(v: number, e: ErrorMessage = undefined) {
         this._vCalc = v;
         this._errorMessage = e;
@@ -73,7 +72,11 @@ export class Result {
 
     get vCalc() { return this._vCalc; }
 
-    get code() { return this._errorMessage.code; }
+    get code() {
+        if (this._errorMessage == undefined)
+            return ErrorCode.ERROR_OK;
+        return this._errorMessage.code;
+    }
 
     get errorMessage() { return this._errorMessage; }
 }
@@ -129,5 +132,16 @@ export function BoolIdentity(a: boolean, b: boolean): boolean {
     return (a && b) || (!a && !b);
 }
 
+
+/**
+ * arrondi d'un nombre avec une précision donnée
+ * @param val nombre à arrondir
+ * @param prec nombre de chiffres
+ */
+export function round(val: number, prec: number) {
+    let m = Math.pow(10, prec || 0);
+    return Math.round(val * m) / m;
+}
+
 // export class UndefinedError extends Error {
 // }
diff --git a/src/nub.ts b/src/nub.ts
index e77c81c9..a5100727 100644
--- a/src/nub.ts
+++ b/src/nub.ts
@@ -72,9 +72,11 @@ export abstract class Nub extends ComputeNode {
 
     public getFirstAnalyticalParameter(): ParamDefinition {
         for (let ps in this._prms) {
-            let p: ParamDefinition = this._prms[ps];
-            if (p.isAnalytical())
-                return p;
+            if (this._prms[ps] instanceof ParamDefinition) {
+                let p: ParamDefinition = this._prms[ps];
+                if (p.isAnalytical())
+                    return p;
+            }
         }
         return undefined;
     }
diff --git a/src/param.ts b/src/param.ts
index 491cccbb..c0747cb8 100644
--- a/src/param.ts
+++ b/src/param.ts
@@ -8,7 +8,7 @@ import { ErrorMessage, ErrorCode } from './util/error';
  */
 export enum ParamDomainValue {
     /**
-     * >0, =0, <0
+     * >0, =0, <0 (-inf -> +inf)
      */
     ANY,
 
@@ -61,6 +61,13 @@ export class ParamDomain {
                 break;
 
             default:
+                // en dehors du cas INTERVAL, on ne doit pas fournir de valeur
+                if (min != undefined || max != undefined) {
+                    let e: ErrorMessage = new ErrorMessage(ErrorCode.ERROR_PARAMDOMAIN_INTERVAL_BOUNDS);
+                    e.extraVar["minValue"] = min;
+                    e.extraVar["maxValue"] = max;
+                    throw e;
+                }
                 break;
         }
     }
@@ -76,6 +83,27 @@ export class ParamDomain {
     get maxValue() {
         return this._maxValue;
     }
+
+    public static getDefaultBounds(d: ParamDomainValue): { min: number, max: number } {
+        switch (d) {
+            case ParamDomainValue.INTERVAL:
+                let e: ErrorMessage = new ErrorMessage(ErrorCode.ERROR_PARAMDOMAIN_INVALID);
+                throw e;
+
+            case ParamDomainValue.ANY:
+            case ParamDomainValue.NOT_NULL:
+                return { min: -Infinity, max: Infinity };
+
+            case ParamDomainValue.POS:
+                return { min: 1e-9, max: Infinity };
+
+            case ParamDomainValue.POS_NULL:
+                return { min: 0, max: Infinity };
+
+            // default:
+            //     throw "valeur de ParamDomainValue" + ParamDomainValue[d] + " non prise en charge";
+        }
+    }
 }
 
 /**
@@ -369,6 +397,7 @@ export enum ComputeNodeType {
     SectionTrapeze, SectionRectangle, SectionCercle, SectionPuissance,
     RegimeUniforme, // paramètres communs à toutes les sections paramétrées
     RegimeUniformeTrapeze, RegimeUniformeRectangle, RegimeUniformeCercle, RegimeUniformePuissance,
+    CourbeRemous,
     Test
 }
 
diff --git a/src/remous.ts b/src/remous.ts
new file mode 100644
index 00000000..050523bc
--- /dev/null
+++ b/src/remous.ts
@@ -0,0 +1,705 @@
+import { ParamsSection, acSection } from "./section/section_type";
+import { XOR, Result, round } from "./base";
+import { IParamsEquation, ParamDefinition, ParamCalculability, ComputeNodeType, ParamDomainValue } from "./param";
+import { Dichotomie } from "./dichotomie";
+import { Nub } from "./nub";
+import { ErrorCode, ErrorMessage } from "./util/error";
+import { } from "./util/";
+
+export enum MethodeResolution {
+	Trapezes, EulerExplicite, RungeKutta4
+}
+
+/**
+ * paramètres pour les courbes de remous
+ */
+export class CourbeRemousParams implements IParamsEquation {
+	/**
+	 * Débit amont
+	 */
+	// private _Qamont: ParamDefinition;
+
+	/**
+	 * Tirant imposé à l'amont
+	 */
+	private _Yamont: ParamDefinition;
+
+	/**
+	 * Tirant imposé à l'aval
+	 */
+	private _Yaval: ParamDefinition;
+
+	/**
+	 * Méthode de résolution de l'équation différentielle
+	 */
+	private _methodeResolution: MethodeResolution;
+
+	// constructor(rQamont: number, rYamont: number, rYAval: number, meth: MethodeResolution) {
+	constructor(rYamont: number, rYAval: number, meth: MethodeResolution) {
+		// this._Qamont = new ParamDefinition(ComputeNodeType.CourbeRemous, 'Qam', ParamDomainValue.POS, rQamont);
+		this._Yamont = new ParamDefinition(ComputeNodeType.CourbeRemous, 'Yam', ParamDomainValue.POS, rYamont);
+		this._Yaval = new ParamDefinition(ComputeNodeType.CourbeRemous, 'Yav', ParamDomainValue.POS, rYAval);
+		this._methodeResolution = meth;
+	}
+
+	// get Qamont() {
+	// 	return this._Qamont;
+	// }
+
+	get Yamont() {
+		return this._Yamont;
+	}
+
+	get Yaval() {
+		return this._Yaval;
+	}
+
+	get methodeResolution() {
+		return this._methodeResolution;
+	}
+}
+
+
+/**
+ * Calcul d'une courbe de remous
+ */
+export class CourbeRemous extends Nub {
+	[key: string]: any; // pour pouvoir faire this['methode]();
+
+	private _debugDicho: boolean = false;
+
+	//const DBG = false; /// Pour loguer les messages de debug de cette classe
+
+	// public $oP; /// Paramètres de la section
+	// public $oSect; /// Section du bief
+	// private $oLog; /// Journal de calcul
+
+	/**
+	 * Pas de discrétisation de l'espace (positif en partant de l'aval, négatif en partant de l'amont)
+	 */
+	// private Dx: number;
+	Dx: number; // TODO à remettre en privé
+
+	// private VarCal: number; /// Variable calculée Y pour la dichotomie (intégration trapèze)
+
+	private Sn: acSection;
+	private prmSect: ParamsSection;
+	private prmCR: CourbeRemousParams;
+
+	//private HautCritique: number;	// Yc de la section
+
+	/**
+	 * dernière erreur rencontrée
+	 */
+	private _lastError: Result;
+
+	// constructor(s: acSection, crp: CourbeRemousParams, log: cLog) {
+	constructor(s: acSection, crp: CourbeRemousParams) {
+		super(s.prms, true);
+		this.Sn = s;
+		this.prmSect = s.prms;
+		this.prmCR = crp;
+		//this.HautCritique = s.Calc("Yc", this.prmSect.Y.v);
+		this.Sn.Calc("Yc");
+	}
+
+	protected setParametersCalculability() {
+		this._prms.Y.calculability = ParamCalculability.DICHO;
+	}
+
+	public Equation(sVarCalc: string): Result {
+		if (sVarCalc == "Hs") {
+			// Equation de l'intégration par la méthode des trapèzes
+			// let res: number = this.Sn.Calc('Hs', this.VarCal) - this.Sn.Calc('J', this.VarCal) / 2 * this.Dx;
+			this.Sn.Reset();  // pour forcer le calcul avec la nouvelle valeur de prmSect.Y
+			let res: number = this.Sn.Calc('Hs') - this.Sn.Calc('J') / 2 * this.Dx;
+			return new Result(res);
+		}
+
+		throw "CourbeRemous.Equation() : paramètre " + sVarCalc + " non pris en charge";
+	}
+
+	public get lastError() {
+		return this._lastError;
+	}
+
+	/**
+	 * Calcul de dy/dx (utilisé par Euler explicite et Runge-Kutta 4)
+	 * @param Y Tirant d'eau initial
+	 */
+	private Calc_dYdX(Y: number): number {
+		// L'appel à Calc('J') avec Y en paramètre réinitialise toutes les données dépendantes de la ligne d'eau
+		return - (this.prmSect.If.v - this.Sn.Calc('J', Y)) / (1 - Math.pow(this.Sn.Calc('Fr', Y), 2));
+	}
+
+	/**
+	 * Calcul du point suivant de la courbe de remous par la méthode Euler explicite.
+	 * @param Y Tirant d'eau initial
+	 * @return Tirant d'eau
+	 */
+	private Calc_Y_EulerExplicite(Y: number): Result {
+		// L'appel à Calc('J') avec Y en paramètre réinitialise toutes les données dépendantes de la ligne d'eau
+		let Y2 = Y + this.Dx * this.Calc_dYdX(Y);
+		if (XOR(this.Dx > 0, !(Y2 < this.Sn.HautCritique)))
+			return new Result(undefined, new ErrorMessage(ErrorCode.ERROR_REMOUS_ARRET_CRITIQUE));
+
+		return new Result(Y2);
+	}
+
+	/**
+	 * Calcul du point suivant de la courbe de remous par la méthode RK4.
+	 * @param Y Tirant d'eau initial
+	 * @return Tirant d'eau
+	 */
+	private Calc_Y_RK4(Y: number): Result {
+		// L'appel à Calc('J') avec Y en paramètre réinitialise toutes les données dépendantes de la ligne d'eau
+		//$rDx = $this ->rDx;
+		let Dx = this.Dx;
+		//$rk1 = $this ->Calc_dYdX($Y);
+		let k1 = this.Calc_dYdX(Y);
+
+		let hc = this.Sn.HautCritique;
+
+		//if ($this ->rDx > 0 xor !($Y + $rDx / 2 * $rk1 < $this ->oSect ->rHautCritique)) { return false; }
+		if (XOR(Dx > 0, !(Y + Dx / 2 * k1 < hc)))
+			return undefined;
+
+		//$rk2 = $this ->Calc_dYdX($Y + $rDx / 2 * $rk1);
+		let k2 = this.Calc_dYdX(Y + Dx / 2 * k1);
+
+		//if ($this ->rDx > 0 xor !($Y + $rDx / 2 * $rk2 < $this ->oSect ->rHautCritique)) { return false; }
+		if (XOR(Dx > 0, !(Y + Dx / 2 * k2 < hc)))
+			return undefined;
+
+		//$rk3 = $this ->Calc_dYdX($Y + $rDx / 2 * $rk2);
+		let k3 = this.Calc_dYdX(Y + Dx / 2 * k2);
+
+		//if ($this ->rDx > 0 xor !($Y + $rDx / 2 * $rk3 < $this ->oSect ->rHautCritique)) { return false; }
+		if (XOR(Dx > 0, !(Y + Dx / 2 * k3 < hc)))
+			return undefined;
+
+		//$rk4 = $this ->Calc_dYdX($Y + $rDx * $rk3);
+		let k4 = this.Calc_dYdX(Y + Dx * k3);
+
+		//$Yout = $Y + $rDx / 6 * ($rk1 + 2 * ($rk2 + $rk3) + $rk4);
+		let Yout = Y + Dx / 6 * (k1 + 2 * (k2 + k3) + k4);
+
+		//if ($this ->rDx > 0 xor !($Yout < $this ->oSect ->rHautCritique)) { return false; }
+		if (XOR(Dx > 0, !(Yout < hc)))
+			return new Result(undefined, new ErrorMessage(ErrorCode.ERROR_REMOUS_ARRET_CRITIQUE));
+
+		return new Result(Yout);
+	}
+
+	/**
+	 * Equation de l'intégration par la méthode des trapèzes
+	 */
+	// private Calc_Y_Trapez_Fn(): number {
+	// 	// return $this ->oSect ->Calc('Hs', $this ->VarCal) - $this ->oSect ->Calc('J', $this ->VarCal) / 2 * $this ->rDx;
+	// 	return this.Sn.Calc('Hs', this.VarCal) - this.Sn.Calc('J', this.VarCal) / 2 * this.Dx;
+	// }
+
+	/**
+	 * Calcul du point suivant de la courbe de remous par la méthode de l'intégration par trapèze
+	 * @param Y Tirant d'eau initial
+	 * @return Tirant d'eau
+	 */
+	private Calc_Y_Trapez(Y: number): Result {
+		//include_spip('hyd_inc/dichotomie.class');
+		//$this ->VarCal = &$Y;
+		// this.VarCal = Y;
+
+		// $oDicho = new cDichotomie($this ->oLog, $this, 'Calc_Y_Trapez_Fn', false);
+		let Dicho = new Dichotomie(this, "Y", this._debugDicho, "Hs");
+
+		// Calcul de H + J * \Delta x / 2
+		// $Trapez_Fn = $this ->oSect ->Calc('Hs', $this ->VarCal) + $this ->oSect ->Calc('J', $this ->VarCal) / 2 * $this ->rDx;
+		// let Trapez_Fn = this.Sn.Calc('Hs', this.VarCal) + this.Sn.Calc('J', this.VarCal) / 2 * this.Dx
+		let Trapez_Fn = this.Sn.Calc('Hs', Y) + this.Sn.Calc('J', Y) / 2 * this.Dx
+
+		// H est la charge totale. On se place dans le référentiel ou Zf de la section à calculer = 0
+		// $Trapez_Fn = $Trapez_Fn - $this ->rDx * $this ->oP ->rIf;
+		Trapez_Fn -= this.Dx * this.prmSect.If.v;
+
+		// list($Y2, $flag) = $oDicho ->calculer($Trapez_Fn, $this ->oP ->rPrec, $this ->oSect ->rHautCritique);
+		// let r: Result = Dicho.Dichotomie(Trapez_Fn, this.prmSect.Prec.v, this.Sn.HautCritique);
+		let r: Result = Dicho.Dichotomie(Trapez_Fn, this.prmSect.Prec.v, Y);
+
+		// if ($flag < 0) {
+		if (r.code != ErrorCode.ERROR_OK)
+			return r;
+
+		let Y2 = r.vCalc;
+		// } elseif($this ->rDx > 0 xor !($Y2 < $this ->oSect ->rHautCritique)) {
+		if (XOR(this.Dx > 0, !(Y2 < this.Sn.HautCritique)))
+			return new Result(undefined, new ErrorMessage(ErrorCode.ERROR_REMOUS_ARRET_CRITIQUE));
+
+		return new Result(Y2);
+	}
+
+	/**
+	 * Calcul du point suivant d'une courbe de remous
+	 * @param Y Tirant d'eau initial
+	 * @return Tirant d'eau
+	 */
+	private Calc_Y(Y: number): Result {
+		// let funcCalcY = 'Calc_Y_' + Resolution;
+		// return this[funcCalcY](Y);
+		switch (this.prmCR.methodeResolution) {
+			case MethodeResolution.Trapezes:
+				return this.Calc_Y_Trapez(Y);
+
+			case MethodeResolution.RungeKutta4:
+				return this.Calc_Y_RK4(Y);
+
+			case MethodeResolution.EulerExplicite:
+				return this.Calc_Y_EulerExplicite(Y);
+
+			// default:
+			// 	throw "CourbeRemous.Calc_Y() : type de méthode de résolution " + MethodeResolution[this.prmCR.methodeResolution] + " non pris en charge";
+		}
+	}
+
+	private size(o: {}): number {
+		let res: number = 0;
+
+		for (let i in o)
+			res++;
+
+		return res;
+	}
+
+	private last(o: any): any {
+		let res: any = undefined;
+
+		for (let i in o)
+			res = o[i];
+
+		return res;
+	}
+
+	// private maxKey(o: { [key: number]: any }): number {
+	// 	let res: number = - 1e+9;
+
+	// 	for (let i in o) {
+	// 		let v: number = +i;
+	// 		if (v > res)
+	// 			res = v;
+	// 	}
+
+	// 	return res;
+	// }
+
+	/**
+	 * Calcul d'une courbe de remous en fluvial ou torrentiel
+	 * @param YCL Condition limite amont (torrentiel) ou aval (fluvial)
+	 */
+	private calcul(YCL: number): { [key: number]: number } {
+		//	$trY = array();
+		let trY: { [key: number]: number; } = {};
+		//let n = -1;
+		// let m: Map<number, number>;
+
+		// if ($this ->rDx > 0) {
+		if (this.Dx > 0) {
+			// Calcul depuis l'aval
+			var Deb: number = this.prmSect.Long.v;
+			var Fin: number = 0;
+		}
+		else {
+			// Calcul depuis l'amont
+			Deb = 0;
+			Fin = this.prmSect.Long.v;
+		}
+		// $dx = - $this ->rDx;
+		let dx = - this.Dx;
+		//	spip_log($this, 'hydraulic', _LOG_DEBUG);
+
+		// $trY[sprintf('%1.'.round($this ->oP ->iPrec).'f', $xDeb)] = (real)$rYCL;
+		let lastY = YCL;
+		trY[round(Deb, this.prmSect.iPrec.v)] = lastY;
+		//n++;
+
+		// Boucle de calcul de la courbe de remous
+		// for ($x = $xDeb + $dx; ($dx > 0 && $x <= $xFin) || ($dx < 0 && $x >= $xFin); $x += $dx) {
+		for (let x = Deb + dx; (dx > 0 && x <= Fin) || (dx < 0 && x >= Fin); x += dx) {
+			// $rY = (real)$this->Calc_Y(end($trY), $sResolution);
+			// this.debug("lastY", lastY);
+			let rY: Result = this.Calc_Y(lastY);
+			// this.debug("calcul : x " + x + " y " + rY.vCalc);
+			// this.debug("trY ");
+			// this.logObject(trY);
+			// this.debug("end trY " + this.last(trY));
+			// this.debug("Yn " + this.Sn.HautNormale);
+
+			// if ($rY) {
+			if (rY.code == ErrorCode.ERROR_OK) {
+				// if (end($trY) > $this ->oSect ->rHautNormale xor $rY > $this ->oSect ->rHautNormale) {
+				if (XOR(lastY > this.Sn.HautNormale, rY.vCalc > this.Sn.HautNormale)) {
+					// 	$this ->oLog ->Add(_T('hydraulic:pente_forte').' '.$x. ' m ('._T('hydraulic:reduire_pas').')', true);
+					console.log("La pente de la ligne d'eau est trop forte à l'abscisse " + x + " m (Il faudrait réduire le pas de discrétisation)");
+				}
+
+				// $trY[sprintf('%1.'.round($this ->oP ->iPrec).'f', $x)] = $rY;
+				trY[round(x, this.prmSect.iPrec.v)] = rY.vCalc;
+				// n++;
+			} else {
+				// $this ->oLog ->Add(_T('hydraulic:arret_calcul').' '.$x. ' m');
+				console.log("Arrêt du calcul : Hauteur critique atteinte à l'abscisse", x, "m");
+				this._lastError = rY;
+				break;
+			}
+			lastY = rY.vCalc;
+		}
+
+		return trY;
+	}
+
+	private logObject(o: { [key: number]: number }) {
+		let ks: string[] = Object.keys(o);
+		ks.sort((a, b) => {
+			if (+a > +b) return 1;
+			if (+a < +b) return -1;
+			return 0;
+		});
+		for (let k of ks)
+			this.debug("[" + (+k).toFixed(3) + "]=" + o[+k]);
+	}
+
+	/**
+	 * @param val_a_cal nom de la variable à calculer
+	 */
+	public calculRemous(val_a_cal: string): {
+		"flu": { [key: number]: number; },
+		"tor": { [key: number]: number; },
+		"trX": string[],
+		"tRes": number[]
+	} {
+		// $this ->creer_section_param();
+
+		// On transforme les champs du tableau des données du formulaire en variables
+		// extract($this ->data, EXTR_OVERWRITE | EXTR_REFS);
+
+		// include_spip('hyd_inc/courbe_remous');
+
+		// $oLog = &$this ->oLog;
+
+		this._lastError = undefined;
+
+		// // On calcule les données pour créer un cache et afficher le résultat
+		// $this ->oLog ->Add(_T('hydraulic:largeur_berge').' = '.format_nombre($this ->oSn ->rLargeurBerge, $this ->oP ->iPrec).' m');
+		// $this ->oLog ->Add(_T('hydraulic:h_critique').' = '.format_nombre($this ->oSn ->CalcGeo('Yc'), $this ->oP ->iPrec).' m');
+		// $this ->oLog ->Add(_T('hydraulic:h_normale').' = '.format_nombre($this ->oSn ->CalcGeo('Yn'), $this ->oP ->iPrec).' m');
+
+		let Yc: number = this.Sn.Calc("Yc");
+		this.debug("largeur berge " + this.Sn.Calc("B"));
+		this.debug("hauteur critique " + Yc);
+		this.Sn.HautNormale = this.Sn.Calc("Yn");
+		this.debug("hauteur normale " + this.Sn.HautNormale);
+
+		// Calcul des courbes de remous
+		// $aC = array(); // deux items (Flu et Tor) composé d'un vecteur avec key=X et value=Y
+		let crbFlu: { [key: number]: number; } = undefined;
+		let crbTor: { [key: number]: number; } = undefined;
+
+		//this.debug("HautCritique ", this.Sn.HautCritique);
+
+		// Calcul depuis l'aval
+		// if ($this ->oSn ->rHautCritique <= $rYaval) {
+		if (this.Sn.HautCritique <= this.prmCR.Yaval.v) {
+			// $this ->oLog ->Add(_T('hydraulic:calcul_fluvial'));
+			this.debug("Condition limite aval (" + this.prmCR.Yaval.v + ") >= Hauteur critique (" + this.Sn.HautCritique + ") : calcul de la partie fluviale à partir de l'aval");
+			// $oCRF = new cCourbeRemous($this ->oLog, $this ->oP, $this ->oSn, $rDx);
+			// $aC['Flu'] = $oCRF ->calcul($rYaval, $rLong, $Methode);
+			this.Dx = this.prmSect.Dx.v;
+			crbFlu = this.calcul(this.prmCR.Yaval.v);
+		}
+		else {
+			// 	$this ->oLog ->Add(_T('hydraulic:pas_calcul_depuis_aval'), true);
+			this.debug("Condition limite aval (" + this.prmCR.Yaval.v + ") < Hauteur critique (" + this.Sn.HautCritique + ") : pas de calcul possible depuis l'aval");
+		}
+
+		this.debug("flu ");
+		this.logObject(crbFlu);
+
+		// Calcul depuis l'amont
+		// if ($this ->oSn ->rHautCritique >= $rYamont) {
+		if (this.Sn.HautCritique >= this.prmCR.Yamont.v) {
+			// $this ->oLog ->Add(_T('hydraulic:calcul_torrentiel'));
+			this.debug("Condition limite amont (" + this.prmCR.Yamont.v + ") <= Hauteur critique (" + this.Sn.HautCritique + ") : calcul de la partie torrentielle à partir de l'amont");
+			// $oCRT = new cCourbeRemous($this ->oLog, $this ->oP, $this ->oSn, -$rDx);
+			// $aC['Tor'] = $oCRT ->calcul($rYamont, $rLong, $Methode);
+			this.Dx = -this.prmSect.Dx.v;
+			crbTor = this.calcul(this.prmCR.Yamont.v);
+		}
+		else {
+			// 	$this ->oLog ->Add(_T('hydraulic:pas_calcul_depuis_amont'), true);
+			this.debug("Condition limite amont (" + this.prmCR.Yamont.v + ") > Hauteur critique (" + this.Sn.HautCritique + ") : pas de calcul possible depuis l'amont");
+		}
+		// spip_log($aC, 'hydraulic', _LOG_DEBUG);
+
+		this.debug("tor");
+		this.logObject(crbTor);
+
+		// Détection du ressaut hydraulique
+		let nFlu: number = this.size(crbFlu);
+		let nTor: number = this.size(crbTor);
+		// $bDetectRessaut = true;
+		// if ($bDetectRessaut && isset($aC['Flu']) && isset($aC['Tor'])) {
+		// if (crbFlu != undefined && crbTor != undefined) {
+		if (nFlu != 0 && nTor != 0) {
+			// if (count($aC['Flu']) > count($aC['Tor']) || (count($aC['Flu']) == count($aC['Tor']) && $this ->oSn ->Calc('Imp', end($aC['Flu'])) > $this ->oSn ->Calc('Imp', end($aC['Tor'])))) {
+			let xMaxFlu = crbFlu[0];
+			let xMaxTor = this.last(crbTor);
+			// this.debug("end flu " + xMaxFlu);
+			// this.debug("end tor " + xMaxTor);
+			// this.debug("nFlu " + nFlu);
+			// this.debug("nTor " + nTor);
+			// this.debug("Imp flu " + this.Sn.Calc('Imp', xMaxFlu));
+			// this.debug("Imp tor " + this.Sn.Calc('Imp', xMaxTor));
+			// if (nFlu > nTor || (nFlu == nTor && this.Sn.Calc('Imp', this.last(crbFlu)) > this.Sn.Calc('Imp', this.last(crbTor)))) {
+			if (nFlu > nTor || (nFlu == nTor && this.Sn.Calc('Imp', xMaxFlu) > this.Sn.Calc('Imp', xMaxTor))) {
+				// La courbe fluviale va jusqu'au bout
+				// $sCC = 'Flu';
+				var crbComplete = crbFlu;  // courbe calculée sur tout le bief
+				// $sCN = 'Tor';
+				var crbPartielle = crbTor;  // courbe calculée sur une partie seulement du bief
+				var iSens = 1; // On cherche l'aval du ressaut
+				//$sSens = _T('hydraulic:amont');
+				var sSens = "amont";
+				this.debug("complete=flu, partielle=tor");
+				// this.debug("complete(flu)");
+				// this.debug(crbComplete);
+				// this.debug("partielle(tor)");
+				// this.debug(crbPartielle);
+			} else {
+				// La courbe torrentielle va jusqu'au bout
+				// $sCC = 'Tor';
+				crbComplete = crbTor;
+				// $sCN = 'Flu';
+				crbPartielle = crbFlu;
+				iSens = -1; // On cherche l'amont du ressaut
+				// $sSens = _T('hydraulic:aval');
+				sSens = "aval";
+				this.debug("complete=tor, partielle=flu");
+				// this.debug("complete(tor)");
+				// this.debug(crbComplete);
+				// this.debug("partielle(flu)");
+				// this.debug(crbPartielle);
+			}
+
+			// $trX = array_reverse(array_keys($aC[$sCN])); // Parcours des sections de la ligne d'eau la plus courte
+			let trX: string[] = Object.keys(crbPartielle);
+			if (iSens == -1)
+				trX.sort((a, b) => {
+					if (+a > +b) return 1;
+					if (+a < +b) return -1;
+					return 0;
+				});
+			else
+				trX.sort((a, b) => {
+					if (+a > +b) return -1;
+					if (+a < +b) return 1;
+					return 0;
+				});
+			let trXr = trX.slice(0); // copie
+			trXr.reverse();
+
+			// this.debug("trX");
+			// this.debug(trX);
+
+			let bRessaut = false;
+			let Dx = this.prmSect.Dx.v;
+
+			// foreach($trX as $rX) {
+			// for (let irX in trX) {
+			// for (let irX = 0; irX < trX.length - 1; irX++) {
+			for (let irX = 0; irX < trX.length; irX++) {
+				let rX: number = +trX[irX];
+				// this.debug("irX=" + irX);
+				// this.debug("rX=" + rX);
+				// this.debug("partielle[" + rX + "]=" + crbPartielle[rX]);
+				// Calcul de l'abscisse de la section dans l'autre régime
+				// $Yco = $this ->oSn ->Calc('Yco', $aC[$sCN][$rX]); // Y conjugué
+				let Yco = this.Sn.Calc('Yco', crbPartielle[rX]); // Y conjugué
+				this.debug("rX=" + rX + " Yco(Ypartiel=" + crbPartielle[rX] + ")=" + Yco);
+
+				// $rLongRst = 5 * abs($aC[$sCN][$rX] - $Yco); // Longueur du ressaut
+				let rLongRst = 5 * Math.abs(crbPartielle[rX] - Yco); // Longueur du ressaut
+				this.debug("longueur ressaut=" + rLongRst);
+
+				// $xRst = $rX + round($iSens * $rLongRst / $rDx) * $rDx; // Abscisse où comparer Yconj et Y
+				let xRst = rX + Math.round(iSens * rLongRst / Dx) * Dx; // Abscisse où comparer Yconj et Y
+				//let rxRst = rX + iSens * rLongRst; // Abscisse réelle du ressaut
+				//this.debug("xRst reel=" + (rX + iSens * rLongRst));
+
+				// $xRst = sprintf('%1.'.round($this ->oP ->iPrec).'f', $xRst);
+				xRst = round(xRst, this.prmSect.iPrec.v);
+
+				this.debug("xRst=" + xRst);
+
+				//spip_log("\nrX=$rX xRst=$xRst Yco=$Yco",'hydraulic',_LOG_DEBUG);
+				// if (isset($aC[$sCC][$xRst])) {
+				if (crbComplete[xRst] != undefined) {
+					// Hauteur décalée de la longueur du ressaut (il faut gérer la pente du fond)
+					// $Ydec = $aC[$sCC][$xRst] + $rLongRst * $this ->oP ->rIf * $iSens;
+					let Ydec: number = crbComplete[xRst] + rLongRst * this.prmSect.If.v * iSens;
+					this.debug("Ydec=" + Ydec);
+					this.debug("imp(Ycomplet[xRst=" + xRst + "]=" + crbComplete[xRst] + ")=" + this.Sn.Calc('Imp', crbComplete[xRst]));
+					this.debug("imp(Ypartiel[rX=" + rX + "]=" + crbPartielle[rX] + ")=" + this.Sn.Calc('Imp', crbPartielle[rX]));
+
+					// spip_log("\nrX=$rX xRst=$xRst Yco=$Yco Ydec=$Ydec", 'hydraulic', _LOG_DEBUG);
+					// if (($Yco - $Ydec) > 0) {
+					// if (iSens == 1 ? Yco > Ydec : Yco < Ydec) {
+					if (Yco > Ydec) {
+						// $this ->oLog ->Add(_T('hydraulic:ressaut_hydrau', array('Xmin'=>min($rX, $xRst), 'Xmax'=>max($rX, $xRst))));
+						this.debug("Ressaut hydraulique détecté entre les abscisses " + Math.min(rX, xRst) + " et " + Math.max(rX, xRst));
+						// spip_log("rX=$rX xRst=$xRst", 'hydraulic', _LOG_DEBUG);
+						// this.debug("rX=" + rX + " xRst=" + xRst);
+						// Modification de la ligne d'eau CC
+						// foreach(array_keys($aC[$sCN]) as $rXCC) {
+						// for (let pi in crbPartielle) {
+						// for (let pi in trXr) {
+						for (let pi of trXr) {
+							let rXCC: number = +pi;
+							// this.debug("rXCC=" + rXCC);
+							// if ($iSens * ($rXCC - $rX) < 0) {
+							if (iSens * (rXCC - rX) < 0) {
+								// unset($aC[$sCC][$rXCC]);
+								delete crbComplete[rXCC];
+								this.debug("Modification de la ligne d'eau complète : suppression de la valeur à rX=" + rXCC);
+								// } elseif($rXCC == $rX) {
+							} else if (rXCC == rX) {
+								// $aC[$sCC][$rXCC] = $aC[$sCN][$rXCC];
+								this.debug("Modification de la ligne d'eau complète : valeur " + crbComplete[rXCC] + " remplacée par " + crbPartielle[rXCC] + " à rX=" + rXCC);
+								crbComplete[rXCC] = crbPartielle[rXCC];
+								this.debug("Fin de la modification de la ligne d'eau complète");
+								break;
+							}
+						}
+
+						// Modification de la ligne d'eau CN
+						// foreach($trX as $rXCN) {
+						for (let xcn of trX) {
+							let rXCN = +xcn;
+							// this.debug("rXCN=" + rXCN);
+							// if ($iSens * ($rXCN - $xRst) > 0) {
+							if (iSens * (rXCN - xRst) > 0) {
+								// unset($aC[$sCN][$rXCN]);
+								this.debug("Modification de la ligne d'eau partielle : suppression de la valeur à rX=" + rXCN);
+								delete crbPartielle[rXCN];
+								// } elseif($rXCN == $xRst) {
+							} else if (rXCN == xRst) {
+								// $aC[$sCN][$rXCN] = $aC[$sCC][$rXCN];
+								this.debug("Modification de la ligne d'eau partielle : valeur " + crbPartielle[rXCN] + " remplacée par " + crbComplete[rXCN] + " à rX=" + rXCN);
+								crbPartielle[rXCN] = crbComplete[rXCN];
+								this.debug("Fin de la modification de la ligne d'eau partielle");
+								break;
+							}
+						}
+						bRessaut = true;
+						break;
+					}
+				}
+			}
+			if (!bRessaut) {
+				// Le ressaut est en dehors du canal
+				//	$this ->oLog ->Add(_T('hydraulic:ressaut_dehors', array('Sens' => $sSens, 'X' => end($trX))));
+				this.debug("Ressaut hydraulique détecté à l'" + sSens + " de l'abscisse " + this.last(trX));
+				// $aC[$sCN] = array();
+				if (iSens == 1)
+					crbTor = {};
+				else
+					crbFlu = {};
+				crbPartielle = {};  // pour le log uniquement, à virer
+			}
+		}
+
+		this.debug("complete (" + (iSens == 1 ? "flu" : "tor") + ") modifiée");
+		this.logObject(crbComplete);
+		this.debug("partielle (" + (iSens == 1 ? "tor" : "flu") + ") modifiée");
+		this.logObject(crbPartielle);
+
+		// Définition des abscisses
+		// $trX = array();
+		let trX: string[] = [];
+
+		// if (isset($aC['Flu'])) $trX = array_merge($trX, array_keys($aC['Flu']));
+		// if (crbFlu != undefined)
+		if (nFlu != 0)
+			trX = Object.keys(crbFlu);
+
+		// if (isset($aC['Tor'])) $trX = array_merge($trX, array_keys($aC['Tor']));
+		// if (crbTor != undefined)
+		if (nTor != 0)
+			trX = trX.concat(Object.keys(crbTor));
+		// this.debug("trX=" + trX);
+
+		// $trX = array_unique($trX, SORT_NUMERIC);
+		// sort($trX, SORT_NUMERIC);
+		trX.sort((a, b) => {
+			if (+a > +b) return 1;
+			if (+a < +b) return -1;
+			return 0;
+		});
+		// this.debug("trX tri=" + trX);
+		trX = trX.filter((elem, index, array) => {
+			if (index > 0)
+				return elem != array[index - 1];
+			return true;
+		});
+		// this.debug("trX unique=" + trX);
+
+		// Calcul de la variable à calculer
+		// $this ->data['ValCal'] = $val_a_cal;
+
+		// $tRes = array();
+		let tRes: number[] = [];
+		// if ($val_a_cal != 'none') {
+		// if (val_a_cal != undefined && crbFlu != undefined && crbTor != undefined) {
+		if (val_a_cal != undefined && nFlu != 0 && nTor != 0) {
+			// foreach($trX as $rX) {
+			for (let rX of trX) {
+				// $rY = false;
+				let rY = undefined;
+
+				// if (isset($aC['Flu'][$rX]) && !isset($aC['Tor'][$rX])) {
+				if (crbFlu[+rX] != undefined && crbTor[+rX] == undefined) {
+					// $rY = $aC['Flu'][$rX];
+					rY = crbFlu[+rX];
+				}
+				// if (isset($aC['Tor'][$rX])) {
+				if (crbTor[+rX] != undefined) {
+					// if (!isset($aC['Flu'][$rX]) || (isset($aC['Flu'][$rX]) && $aC['Flu'][$rX] == $aC['Tor'][$rX])) {
+					if (crbFlu[+rX] == undefined || (crbFlu[+rX] != undefined && crbFlu[+rX] == crbTor[+rX])) {
+						// $rY = $aC['Tor'][$rX];
+						rY = crbTor[+rX];
+					}
+
+					// if ($rY !== false) {
+					if (rY != undefined)
+						// if (!in_array($val_a_cal, array('Yn', 'Yc', 'Hsc'))) {
+						// $tRes[$rX] = $this ->oSn ->Calc($val_a_cal, $rY);
+						// 	}
+						// 	else {
+						// $tRes[$rX] = $this ->oSn ->CalcGeo($val_a_cal, $rY);
+						// 	}
+						// }
+						tRes[+rX] = this.Sn.Calc(val_a_cal, rY);
+				}
+			}
+
+			/*		
+			return array_merge(
+				$aC,
+				array(
+					'trX' => $trX,
+					'tRes' => $tRes
+				)
+			);
+			/* */
+		}
+
+		return { "flu": crbFlu, "tor": crbTor, "trX": trX, "tRes": tRes };
+	}
+}
\ No newline at end of file
diff --git a/src/util/error.ts b/src/util/error.ts
index 35983cab..4ef9c2fd 100644
--- a/src/util/error.ts
+++ b/src/util/error.ts
@@ -1,4 +1,9 @@
 export enum ErrorCode {
+    /**
+     * pas de pb !
+     */
+    ERROR_OK = 0,
+
     /**
      * la dichotomie n'a pas pu trouver automatiquement d'intervalle de départ
      * car la valeur initiale de la variable est trop haute
@@ -97,6 +102,11 @@ export enum ErrorCode {
      * internationalisation : langue non prise en charge
      */
     ERROR_LANG_UNSUPPORTED = -400,
+
+    /**
+     * courbes de remous : arret du calcul car hauteur critique atteinte
+     */
+    ERROR_REMOUS_ARRET_CRITIQUE = -500,
 }
 
 /**
-- 
GitLab