From 2ed395b98082c49965c7033cc3be8220e2be61c3 Mon Sep 17 00:00:00 2001
From: David Dorchies <david.dorchies@irstea.fr>
Date: Wed, 22 Nov 2017 00:20:15 +0100
Subject: [PATCH] =?UTF-8?q?#6=20Ajout=20de=20l'=C3=A9quation=20CEM88d=20av?=
 =?UTF-8?q?ec=20tests=20OK?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 spec/mock_jasmine.ts                    | 56 +++++++++++++++++++++++++
 spec/structure/structure_cem88d.spec.ts | 46 +++++++++++++++-----
 spec/structure/test_structure_cem88d.ts | 48 +++++++++++++++++++++
 src/base.ts                             |  5 ++-
 src/structure/structure.ts              | 31 ++++++++++++--
 src/structure/structure_cem88d.ts       | 26 ++++++++----
 6 files changed, 188 insertions(+), 24 deletions(-)
 create mode 100644 spec/mock_jasmine.ts
 create mode 100644 spec/structure/test_structure_cem88d.ts

diff --git a/spec/mock_jasmine.ts b/spec/mock_jasmine.ts
new file mode 100644
index 00000000..3a9c2f85
--- /dev/null
+++ b/spec/mock_jasmine.ts
@@ -0,0 +1,56 @@
+/**
+ * Mock fonction describe de Jasmine
+ * @param sTxt Texte de la suite de test
+ * @param fFun Fonction de la suite de test
+ */
+export function describe(sTxt: string, fFun: Function) {
+    //console.group();
+    console.log(sTxt);
+    fFun();
+    //console.groupEnd();
+}
+
+/**
+ * Mock fonction it de jasmine
+ * @param sTxt Texte à afficher pour la spec
+ * @param fFun Function à lancer pour la spec
+ */
+export function it(sTxt: string, fFun: Function) {
+    console.log(sTxt);
+    fFun();
+}
+ 
+/** 
+ * Mock expect et toBeCloseTo de Jasmine. 
+ * Ne fonctionne pas à cause de this = undefined dans le contexte "use strict"
+ */
+export function expect(obj: Object) {
+    this.testResult = obj;
+    
+    this.toBeCloseTo = function(expected: number, precision: number) {
+        var pow = Math.pow(10, precision + 1);
+        var delta = Math.abs(expected - this.testResult);
+        var maxDelta = Math.pow(10, -precision) / 2;
+        if (Math.round(delta * pow) / pow > maxDelta) {
+            console.error("Expected "+this.testResult+" to be close to "+expected+","+precision);
+        }
+        return this;
+    };
+    return this;
+}
+
+/** 
+ * Mock de la function chainée à expect de test d'une valeur à un epsilon près
+ * @param actual Valeur numérique sortie du test
+ * @param expected Valeur numérique attendue
+ * @param precision Précision attendue (nombre de chiffre après la virgule)
+ */
+export function toBeCloseTo (actual: number, expected: number, precision: number) {
+    var pow = Math.pow(10, precision + 1);
+    var delta = Math.abs(expected - actual);
+    var maxDelta = Math.pow(10, -precision) / 2;
+    if (Math.round(delta * pow) / pow > maxDelta) {
+        console.error("Expected "+actual+" to be close to "+expected+","+precision);
+    }
+    return this;
+};
diff --git a/spec/structure/structure_cem88d.spec.ts b/spec/structure/structure_cem88d.spec.ts
index defd90c8..901b8327 100644
--- a/spec/structure/structure_cem88d.spec.ts
+++ b/spec/structure/structure_cem88d.spec.ts
@@ -4,19 +4,43 @@ import { StructureCem88d, RectangularStructureParams } from "../../src/structure
 import { Result } from "../../src/base";
 import { precDigits } from "../nubtest";
 
-let structPrm: RectangularStructureParams = new RectangularStructureParams(1, undefined, 1, 2, 0.6, 0);
-let structTest: StructureCem88d = new StructureCem88d(structPrm, false);
-
-let resultTesth1FnW: number[] = [Infinity, 3.603062,	1.560872, 1.323331, 1.154102, 1.073632, 1.047438, 1.035118, 1.027086, 1.021649, 1.021305];
+let structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+let structTest: StructureCem88d = new StructureCem88d(structPrm, true);
 
 describe('Class StructureCem88d: ', () => {
-    describe('Calcul h1 avec W croissant: ', () => {
-        for(let iW = 0; iW <= 10; iW++ ) {
-            structTest.prms.W.v = iW / 10 ;
-            it('h1(W='+structTest.prms.W.v+') should be '+resultTesth1FnW[iW], () => {
-                console.log('h1(W='+structTest.prms.W.v+') should be '+resultTesth1FnW[iW]);
-                expect(structTest.Calc('h1').vCalc).toBeCloseTo(resultTesth1FnW[iW],precDigits);
-            });
+    function itCalcQ(structTest:StructureCem88d, h1:number, W:number, Q:number) {
+        it('Q(h1='+h1+',W='+W+') should be '+Q, () => {
+            structTest.prms.h1.v = h1;
+            structTest.prms.W.v = W;
+            expect(structTest.Calc('Q').vCalc).toBeCloseTo(Q,precDigits);
+        });
+    }
+
+    describe('Calcul Q avec W croissant: ', () => {
+        let W: number[] = [0.000000,0.100000,0.200000,0.300000,0.400000,0.500000,0.600000,0.700000,0.800000,0.900000,1.000000,1.100000,1.200000,1.300000];
+        let h1: number = 1.200000;
+        let Q: number[] = [0.000000,0.617586,1.235173,1.852759,2.470345,3.087931,3.705518,4.296608,4.831177,5.302464,5.700445,6.007777,6.175863,6.175863];
+        
+        for(let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);            
+        }
+    });
+    describe('Calcul Q en charge avec h1 croissant: ', () => {
+        let W: number = 0.6;
+        let h1: number[] =[1.100000,1.300000,1.500000];
+        let Q: number[] =[2.620197,4.450866,5.226583];
+
+        for(let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i]);            
+        }
+    });
+    describe('Calcul Q a surface libre avec h1 croissant: ', () => {
+        let W: number = Infinity;
+        let h1: number[] =[1.100000,1.500000];
+        let Q: number[] =[4.366994,9.764896];
+
+        for(let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i]);            
         }
     });
 });
diff --git a/spec/structure/test_structure_cem88d.ts b/spec/structure/test_structure_cem88d.ts
new file mode 100644
index 00000000..34f24d4a
--- /dev/null
+++ b/spec/structure/test_structure_cem88d.ts
@@ -0,0 +1,48 @@
+import { StructureCem88d, RectangularStructureParams } from "../../src/structure/structure_cem88d";
+import { Result } from "../../src/base";
+import { precDigits } from "../nubtest";
+import { describe, it, toBeCloseTo } from "../mock_jasmine"
+
+//expect(1).toBeCloseTo(1,3);
+
+let structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+let structTest: StructureCem88d = new StructureCem88d(structPrm, true);
+
+describe('Class StructureCem88d: ', () => {
+    function itCalcQ(structTest:StructureCem88d, h1:number, W:number, Q:number) {
+        it('Q(h1='+h1+',W='+W+') should be '+Q, () => {
+            structTest.prms.h1.v = h1;
+            structTest.prms.W.v = W;
+            toBeCloseTo(structTest.Calc('Q').vCalc, Q, precDigits);
+        });
+    }
+
+    describe('Calcul Q avec W croissant: ', () => {
+        let W: number[] = [0.000000,0.100000,0.200000,0.300000,0.400000,0.500000,0.600000,0.700000,0.800000,0.900000,1.000000,1.100000,1.200000,1.300000];
+        let h1: number = 1.200000;
+        let Q: number[] = [0.000000,0.617586,1.235173,1.852759,2.470345,3.087931,3.705518,4.296608,4.831177,5.302464,5.700445,6.007777,6.175863,6.175863];
+        
+        for(let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);            
+        }
+    });
+    describe('Calcul Q en charge avec h1 croissant: ', () => {
+        let W: number = 0.6;
+        let h1: number[] =[1.100000,1.300000,1.500000];
+        let Q: number[] =[2.620197,4.450866,5.226583];
+
+        for(let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i]);            
+        }
+    });
+    describe('Calcul Q a surface libre avec h1 croissant: ', () => {
+        let W: number = Infinity;
+        let h1: number[] =[1.100000,1.500000];
+        let Q: number[] =[4.366994,9.764896];
+
+        for(let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i]);            
+        }
+    });
+});
+
diff --git a/src/base.ts b/src/base.ts
index 8b873195..9930687e 100644
--- a/src/base.ts
+++ b/src/base.ts
@@ -12,9 +12,10 @@ export class Result {
 
     private _message: Message;
 
-    constructor(v: number, e: Message = undefined) {
+    constructor(v: number, m: Message = undefined, e: { [key: string]: any } = {}) {
         this._vCalc = v;
-        this._message = e;
+        this._message = m;
+        this.extraVar = e;
     };
 
     get vCalc() { return this._vCalc; }
diff --git a/src/structure/structure.ts b/src/structure/structure.ts
index 2b3937e1..a8211a1f 100644
--- a/src/structure/structure.ts
+++ b/src/structure/structure.ts
@@ -4,7 +4,7 @@
 import { Result } from "../base";
 import { ComputeNodeType, ParamDefinition, ParamDomain, ParamDomainValue, ParamCalculability, ParamsEquation } from "../param";
 import { Nub } from "../nub";
-
+import { Message } from "../util/message";
 
 /**
  * Common parameters of hydraulic structure equations
@@ -98,8 +98,14 @@ export abstract class Structure extends Nub {
      * Give the flow mode : weir or orifice flow
      */
     protected getFlowMode(): StructureFlowMode  {
-        if (this.prms.h1.v >= this.prms.W.v) return StructureFlowMode.ORIFICE;
-        return StructureFlowMode.WEIR; 
+        if (this.prms.h1.v > this.prms.W.v) {
+            this.debug("Structure.getFlowRegime(h1="+this.prms.h1.v+",W="+this.prms.W.v+")=ORIFICE");
+            return StructureFlowMode.ORIFICE;
+        } else {
+            this.debug("Structure.getFlowRegime(h1="+this.prms.h1.v+",W="+this.prms.W.v+")=WEIR");
+            return StructureFlowMode.WEIR; 
+        }
+        
     }
 
 
@@ -111,12 +117,15 @@ export abstract class Structure extends Nub {
         // Orifice have three flow regimes: free, partially submerged and (totally) submerged
         if(this.prms.h2.v <= 2/3*this.prms.h1.v) {
             // free flow for both weirs and orifices
+            this.debug("Structure.getFlowRegime(h1="+this.prms.h1.v+",h2="+this.prms.h2.v+",W="+this.prms.W.v+")=FREE");
             return StructureFlowRegime.FREE;
         } else if(this.prms.h1.v > this.prms.W.v && this.prms.h2.v < (2*this.prms.h1.v + this.prms.W.v)/3) {
             // Partially submerged only for orifices
+            this.debug("Structure.getFlowRegime(h1="+this.prms.h1.v+",h2="+this.prms.h2.v+",W="+this.prms.W.v+")=PARTIAL");
             return StructureFlowRegime.PARTIAL;
         } else {
             // (Totally) submerged for both weirs and orifices
+            this.debug("Structure.getFlowRegime(h1="+this.prms.h1.v+",h2="+this.prms.h2.v+",W="+this.prms.W.v+")=SUBMERGED");
             return StructureFlowRegime.SUBMERGED;
         }
     }
@@ -170,4 +179,20 @@ export abstract class Structure extends Nub {
         if(sVarCalc!="Q") throw 'Structure.Equation() : invalid parameter name ' + sVarCalc;
     }
 
+    /** 
+     * Calcul du mode et du régime d'écoulement 
+     */
+    Equation(sVarCalc: string): Result {
+        this.CheckEquation(sVarCalc);
+        let res: Result = new Result(
+            undefined, 
+            undefined, 
+            {
+                "Regime":   this.getFlowRegime(),
+                "Mode":     this.getFlowMode()
+            }
+        );
+        return res;
+    }
+
 }
\ No newline at end of file
diff --git a/src/structure/structure_cem88d.ts b/src/structure/structure_cem88d.ts
index 324c7fac..706f9bd6 100644
--- a/src/structure/structure_cem88d.ts
+++ b/src/structure/structure_cem88d.ts
@@ -9,31 +9,41 @@ export { RectangularStructureParams };
 
 export class StructureCem88d extends RectangularStructure {
     Equation(sVarCalc: string): Result {
-        this.CheckEquation(sVarCalc);
+        let res: Result = super.Equation(sVarCalc);
         let v : number;
         let cd : number = this.prms.Cd.v * this.prms.L.v * Structure.R2G;
         let b1 : number = Math.sqrt(this.prms.h1.v);
-        let b2 : number = Math.sqrt(this.prms.h2.v);
-        let cd1 : number = cd * 2.5981;
-        switch(this.getFlowMode()) {
+        let b2 : number = Math.sqrt(this.prms.h1.v-this.prms.h2.v);
+        let cd1 : number = cd * 2.5981; // cd * 3*sqrt(3)/2
+        this.debug("StructureCem88d.Equation b1="+b1+" b2="+b2+" cd1="+cd1);
+        switch(res.extraVar['Mode']) {
             case StructureFlowMode.WEIR:
-                switch(this.getFlowRegime()) {
+                switch(res.extraVar['Regime']) {
                     case StructureFlowRegime.FREE:
                         v = cd * this.prms.h1.v * b1;
+                        break;
                     case StructureFlowRegime.SUBMERGED:
-                        v = cd1 * this.prms.h1.v * b2;
+                        v = cd1 * this.prms.h2.v * b2;
+                        this.debug("StructureCem88d.Equation WEIR SUBMERGED Q="+v);
+                        break;
                 }
+                break;
             case StructureFlowMode.ORIFICE:
-                let b3 : number = this.prms.h1.v - this.prms.W.v;
-                switch(this.getFlowRegime()) {
+                let b3 : number = Math.pow(this.prms.h1.v - this.prms.W.v, 1.5);
+                switch(res.extraVar['Regime']) {
                     case StructureFlowRegime.FREE:
                         v = cd * (this.prms.h1.v * b1 -b3);
+                        break;
                     case StructureFlowRegime.PARTIAL:
                         v = cd1 * b2 * this.prms.h2.v - cd * b3;
+                        break;
                     case StructureFlowRegime.SUBMERGED:
                         v = cd1 * b2 * this.prms.W.v;
+                        this.debug("StructureCem88d.Equation ORIFICE SUBMERGED Q="+v);
+                        break;
                 }
         }
+        this.debug("StructureCem88d.Equation(h1="+this.prms.h1.v+",h2="+this.prms.h2.v+",W="+this.prms.W.v+") => Q="+v);
         return new Result(v);
     }
 }
-- 
GitLab