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