diff --git a/doc/scilab_tests/Qouvrage.sci b/doc/scilab_tests/Qouvrage.sci
new file mode 100755
index 0000000000000000000000000000000000000000..67759194c7e4d8fb994be629c25aff07869677da
--- /dev/null
+++ b/doc/scilab_tests/Qouvrage.sci
@@ -0,0 +1,262 @@
+function [Q,C,w] = Qouvrage(T,h1,h2,w,hv,L,Cd)
+    // T=1 : Seuil Orifice avec surverse éventuelle (SIC)
+    //   2 : Seuil Vanne avec surverse éventuelle (SIC)
+    //   3 : Classique Seuil
+    //   4 : Classique Orifice
+    //   5 : Classique Noyee
+    //   6 : Cunge avec differentes conditions d'ecoulement
+    // h1  : cote amont (par rapport au radier)
+    // h2  : cote aval (par rapport au radier)
+    // w   : ouverture (par rapport au radier)
+    // hv  : hauteur de la vanne (pour le calcul de la surverse éventuelle)
+    // L   : largeur
+    // Cd  : coefficient de debit
+    // Q   : debit
+    // C   : code couleur correspondant aux conditions d'ecoulement
+    //       C=0; debit nul
+    //       C=1; surface libre denoye
+    //       C=2; surface libre noye
+    //       C=3; charge denoye
+    //       C=4; charge noye partiel
+    //       C=5; charge noye total
+    //       C=C+10; si surverse
+    // Initialisation ---------------------------------------------
+    R2G = sqrt(2*9.81);
+    R32 = 3*sqrt(3)/2;
+    // Tests ------------------------------------------------------
+    Q=0;
+    C=0;
+    partiel="non"
+    if (w==0 & hv==0)
+        Q=0;
+        return
+    end
+    if (h2>h1)
+        Q=0;
+        return
+    end
+    if (h1==0)
+        Q=0;
+        return
+    end
+    // Seuil - Orifice ===========================================
+    if (T==1 & w>0)
+        // Conditions d'ecoulement ------------------------------------
+        if (h1<=w)
+            surfacelibre='oui';
+        else
+            surfacelibre='non';
+        end
+        alfa=2/3;
+        if (h2<=alfa*h1)
+            denoye='oui';
+        else
+            denoye='non';
+            if (h2<=2/3*h1+w/3)
+                partiel='oui';
+            else
+                partiel='non';
+            end
+        end
+        // Seuil - Denoye ---------------------------------------------
+        if (surfacelibre=='oui') & (denoye=='oui')
+            Q=Cd*L*R2G*(h1^1.5);
+            C=1;
+        end
+        // Seuil - Noye -----------------------------------------------
+        if (surfacelibre=='oui') & (denoye=='non')
+            Cs=R32*Cd;
+            Q=Cs*L*R2G*((h1-h2)^0.5)*h2;
+            C=2;
+        end
+        // Orifice - Denoye -------------------------------------------
+        if (surfacelibre=='non') & (denoye=='oui')
+            Q=Cd*L*R2G*(h1^1.5-(h1-w)^1.5);
+            C=3;
+        end
+        // Orifice - Noye ---------------------------------------------
+        if (surfacelibre=='non') & (denoye=='non')
+            //  Ennoyement partiel ---------------------------------
+            if (partiel=='oui')
+                Q=Cd*L*R2G*(R32*h2*(h1-h2)^0.5-(h1-w)^1.5);
+                C=4;
+                //  Ennoyement total -----------------------------------
+            else
+                Cs=R32*Cd;
+                Q=Cs*L*R2G*((h1-h2)^0.5)*w;
+                //~printf("b2= %f  Cd1=%f\n",(h1-h2)^0.5, Cs*L*R2G);
+                C=5;
+            end
+        end
+    end
+    // Seuil - Vanne =============================================
+    if (T==2 & w>0)
+        // Calcul de parametres ---------------------------------------
+        mu0=2/3*Cd;
+        // Conditions d'ecoulement ------------------------------------
+        if (h1<=w)
+            surfacelibre='oui';
+            muf=mu0-0.08;
+            alfa=0.75;
+        else
+            surfacelibre='non';
+            mu =mu0-0.08/(h1/w);
+            mu1=mu0-0.08/(h1/w-1);
+            alfa=1-0.14*h2/w;
+            printf("alfa=%f \n",alfa)
+            if (alfa<0.4)
+                alfa=0.4;
+            end
+            if (alfa>0.75)    
+                alfa=0.75;
+            end
+        end
+        if (h2<=alfa*h1)
+            denoye='oui';
+        else
+            denoye='non';
+            x=sqrt(1-h2/h1);
+            beta1=-2*alfa+2.6;
+            if (x>0.2)
+                KF=1-(1-x/sqrt(1-alfa))^beta1;
+            else
+                KF=5*x*(1-(1-0.2/sqrt(1-alfa))^beta1);
+            end
+            printf("beta1=%f x=%f alfa=%f KF=%f\n",beta1,x,alfa,KF)
+            alfa1=1-0.14*(h2-w)/w;
+            if (alfa1<0.4)
+                alfa1=0.4;
+            end
+            if (alfa1>0.75)
+                alfa1=0.75;
+            end
+            if (h2<=alfa1*h1+(1-alfa1)*w)
+                partiel='oui';
+            else
+                partiel='non';
+            end
+        end
+        // Seuil - Denoye ---------------------------------------------
+        if (surfacelibre=='oui') & (denoye=='oui')
+            Q=muf*L*R2G*(h1^1.5);
+            C=1;
+        end
+        // Seuil - Noye -----------------------------------------------
+        if (surfacelibre=='oui') & (denoye=='non')
+            printf("KF=%f  muf=%f\n", KF, muf);
+            Q=KF*muf*L*R2G*(h1^1.5);
+            C=2;
+        end
+        // Vanne - Denoye ---------------------------------------------
+        if (surfacelibre=='non') & (denoye=='oui')
+            Q=L*R2G*(mu*h1^1.5-mu1*(h1-w)^1.5);
+            C=3;
+        end
+        // Vanne - Noye -----------------------------------------------
+        if (surfacelibre=='non') & (denoye=='non')
+            x1=sqrt(1-(h2-w)/(h1-w));
+            beta1=-2*alfa1+2.6;
+            if (x1>0.2)
+                KF1=1-(1-x1/sqrt(1-alfa1))^beta1;
+            else
+                KF1=5*x1*(1-(1-0.2/sqrt(1-alfa1))^beta1);
+            end
+            printf("beta1=%f x1=%f alfa1=%f KF1=%f\n",beta1,x1,alfa1,KF1)
+            //  Ennoyement partiel ---------------------------------
+            if (partiel=='oui')
+                Q=L*R2G*(KF*mu*(h1^1.5)-mu1*(h1-w)^1.5);
+                C=4;
+                //  Ennoyement total -----------------------------------
+            else
+                printf("%f ",[L,R2G,KF,mu,h1,KF1,mu1,w]')
+                printf("\n")
+                Q=L*R2G*(KF*mu*(h1^1.5)-KF1*mu1*(h1-w)^1.5);
+                printf("Q=%f\n", Q)
+                C=5;
+            end
+        end
+    end
+    // Surverse dans cas 1 et 2 ===================================
+    if hv==0
+        hv=%inf;
+    end;
+    if (T==1) | (T==2)
+        if (h1>w+hv)
+            surverse='oui';
+            alfa=2/3;
+            if (h2-w-hv<=alfa*(h1-w-hv))
+                denoyesurverse='oui';
+            else
+                denoyesurverse='non';
+            end
+        else
+            surverse='non';
+        end
+        // Surverse - Denoye -------------------------------------------
+        if (surverse=='oui')
+            if (denoyesurverse=='oui')
+                Q=Q+0.4*L*R2G*(h1-w-hv)^1.5;
+                C=C+10;
+            else
+                Q=Q+1.04*L*R2G*((h1-h2)^0.5)*(h2-w-hv);
+                C=C+10;
+            end
+        end
+    end
+    // Classique - Seuil ==========================================
+    if (T==3)
+        Q=Cd*L*R2G*h1^1.5;
+        C=1;
+    end
+    // Classique - Orifice ========================================
+    if (T==4)
+        Q=Cd*min(h1,w)*L*R2G*(h1 - min(h1,w)/2)^0.5;
+        if (h1>w)
+            C=3;
+        else
+            C=1;
+        end
+    end
+    // Classique - Noyee ==========================================
+    if (T==5)
+        if (h1>h2)
+            Q=Cd*min(h1,w)*L*R2G*(h1-h2)^0.5;
+            if (h1>w)
+                C=5;
+            else
+                C=2;
+            end
+        else
+            Q=0;
+            C=0;
+        end
+    end
+    // Classique Cunge ===========================================
+    if (T==6)
+        // Conditions d'ecoulement ------------------------------------
+        if (h2<=2/3*h1)
+            denoye='oui';
+            if (w<=2/3*h1)
+                surfacelibre='non';
+                Q=Cd*L*R2G*w*((h1-w)^0.5);
+                C=3;
+            else
+                surfacelibre='oui';
+                Q=Cd*L*R2G/R32*h1^1.5;
+                C=1;
+            end
+        else
+            denoye='non';
+            if (w<=h2)
+                surfacelibre='non';
+                Q=Cd*L*R2G*w*((h1-h2)^0.5);
+                C=5;
+            else
+                surfacelibre='oui';
+                Q=Cd*L*R2G*h2*((h1-h2)^0.5);
+                C=2;
+            end
+        end
+    end
+    //~printf("surface libre=%s  denoye=%s  partiel=%s\n", surfacelibre, denoye, partiel)
+endfunction
diff --git a/doc/scilab_tests/TestQOuvrageLoopW.sci b/doc/scilab_tests/TestQOuvrageLoopW.sci
new file mode 100755
index 0000000000000000000000000000000000000000..fb80ced32f6dfa2b0ae453e9859cca183d6c1546
--- /dev/null
+++ b/doc/scilab_tests/TestQOuvrageLoopW.sci
@@ -0,0 +1,39 @@
+function TestQOuvrageLoop(sTest, T, L, Cd, H1, h2, W)
+    printf("\n")
+    printf("*** %s ***\n", sTest)
+    
+    // Tests des différents régimes
+    sCodeC = ["debit nul","surface libre denoye","surface libre noye","charge denoye","charge noye partiel","charge noye total"];
+    Q = []
+    C = []
+    CS = []
+    for w = W
+        for h1 = H1
+            [q, c] = Qouvrage(T,h1,h2,w,0,L,Cd)
+            Q = [Q q]
+            C = [C c]
+            CS = [CS sCodeC(c+1)]
+        end
+    end
+    
+    printf("W=[")
+    printf("%f,",W')
+    printf("];\n")
+    
+    printf("h1=[")
+    printf("%f,",H1')
+    printf("];\n")
+
+    printf("Q=[")
+    printf("%f,",Q')
+    printf("];\n")
+    
+    printf("C=[")
+    printf("%i,",C')
+    printf("];\n")
+    
+    printf("Code=[")
+    printf("%s,",CS')
+    printf("];\n")
+    
+endfunction
diff --git a/doc/scilab_tests/describe.sci b/doc/scilab_tests/describe.sci
new file mode 100755
index 0000000000000000000000000000000000000000..283e4056dccba63f18da3887e69b69dfb509f7d6
--- /dev/null
+++ b/doc/scilab_tests/describe.sci
@@ -0,0 +1,6 @@
+function describe(sSuite)
+    printf("\n")
+    printf("****************************************\n")
+    printf("* %-36s *\n", sSuite)
+    printf("****************************************\n")
+endfunction
diff --git a/doc/scilab_tests/main_ouvrages.sce b/doc/scilab_tests/main_ouvrages.sce
new file mode 100755
index 0000000000000000000000000000000000000000..d0d7bbb266a86816f8f8e32a0b73582d3682cc4d
--- /dev/null
+++ b/doc/scilab_tests/main_ouvrages.sce
@@ -0,0 +1,29 @@
+// Test des équations d'ouvrages pour JaLHyd
+clear
+sCurrentPath = get_absolute_file_path("main_ouvrages.sce");
+getd(sCurrentPath);
+
+function TestQOuvrageSuite(T)
+    //TestQOuvrageLoop(sTest, T, L, Cd, H1, h2, W)
+    TestQOuvrageLoop("Calcul Q avec W croissant",T, 2, 0.6, 1.2, 1, 0:0.1:1.3);
+    TestQOuvrageLoop("Calcul Q en charge avec h1 croissant",T, 2, 0.6, [1.05,1.3,1.5], 1, 0.8);
+    TestQOuvrageLoop("Calcul Q a surface libre avec h1 croissant",T, 2, 0.6, [1.1,1.5], 1, 2);
+endfunction
+
+describe("CEM88D");
+TestQOuvrageSuite(1);
+
+describe("CEM88V");
+TestQOuvrageSuite(2);
+
+describe("Classique - Seuil dénoyé");
+TestQOuvrageSuite(3);
+
+describe("Classique - Orifice dénoyé");
+TestQOuvrageSuite(4);
+
+describe("Classique - Orifice noyé");
+TestQOuvrageSuite(5);
+
+describe("Cunge 80");
+TestQOuvrageSuite(6);
diff --git a/spec/mock_jasmine.ts b/spec/mock_jasmine.ts
new file mode 100644
index 0000000000000000000000000000000000000000..670e76f69a38bc7390b1c8d8d1ffe9701df48618
--- /dev/null
+++ b/spec/mock_jasmine.ts
@@ -0,0 +1,86 @@
+/**
+ * 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: () => void) {
+    // tslint:disable-next-line:no-console
+    console.log(sTxt);
+    fFun();
+}
+
+/**
+ * Mock fonction xdescribe de Jasmine
+ * @param sTxt Texte de la suite de test
+ * @param fFun Fonction de la suite de test
+ */
+export function xdescribe(sTxt: string, fFun: () => void) {
+    // tslint:disable-next-line:no-console
+    console.log(sTxt + " ignored ***");
+}
+
+/**
+ * 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: () => void) {
+    // tslint:disable-next-line:no-console
+    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) {
+        const pow = Math.pow(10, precision + 1);
+        const delta = Math.abs(expected - this.testResult);
+        const maxDelta = Math.pow(10, -precision) / 2;
+        if (Math.round(delta * pow) / pow > maxDelta) {
+            // tslint:disable-next-line:no-console
+            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) {
+    const pow = Math.pow(10, precision + 1);
+    const delta = Math.abs(expected - actual);
+    const maxDelta = Math.pow(10, -precision) / 2;
+    if (Math.round(delta * pow) / pow > maxDelta) {
+        // tslint:disable-next-line:no-console
+        console.error("Expected " + actual + " to be close to " + expected + "," + precision);
+    }
+    return this;
+}
+
+/**
+ * Mock de la fonction toBe de Jasmine : the actual value to be `===` to the expected value
+ * @param actual Objet à tester
+ * @param expected Objet de référence
+ */
+export function toBe(actual: any, expected: any) {
+    return actual === expected;
+}
+
+/**
+ * Mock de la fonction toBe de Jasmine : the actual value to be `===` to the expected value
+ * @param actual Objet à tester
+ * @param expected Objet de référence
+ */
+export function toEqual(actual: any, expected: any) {
+    return actual === expected;
+}
diff --git a/spec/structure/rectangular_structure.ts b/spec/structure/rectangular_structure.ts
new file mode 100644
index 0000000000000000000000000000000000000000..34b3b6ae4843767b257f7346875e0209a2ba316e
--- /dev/null
+++ b/spec/structure/rectangular_structure.ts
@@ -0,0 +1,50 @@
+// tslint:disable-next-line:no-reference
+/// <reference path="../../node_modules/@types/jasmine/index.d.ts" />
+
+import { Result } from "../../src/base";
+import { RectangularStructure } from "../../src/structure/rectangular_structure";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { precDigits } from "../nubtest";
+
+/**
+ * Récupération du nom de la classe
+ * https://www.stevefenton.co.uk/2013/04/Obtaining-A-Class-Name-At-Runtime-In-TypeScript/
+ */
+export class Describer {
+    /**
+     * Récupère le nom de la classe d'un objet
+     * @param inputClass Objet à tester
+     */
+    public static getName(inputClass: any) {
+        const funcNameRegex = /function (.{1,})\(/;
+        const results = (funcNameRegex).exec((inputClass as any).constructor.toString());
+        return (results && results.length > 1) ? results[1] : "";
+    }
+}
+
+export function itCalcQ(
+    struct: RectangularStructure, h1: number, W: number, Q: number,
+    mode?: StructureFlowMode, regime?: StructureFlowRegime) {
+
+    struct.debug("itCalQ " + Describer.getName(struct) + " h1=" + h1 + " W=" + W + " Q=" + Q);
+
+    struct.prms.h1.v = h1;
+    struct.prms.W.v = W;
+    const res: Result = struct.Calc("Q");
+    struct.debug("struct.Calc(Q)=" + res.vCalc);
+
+    it("Q(h1=" + h1 + ",W=" + W + ") should be " + Q, () => {
+        struct.debug("struct.Calc(Q)=" + res.vCalc);
+        expect(res.vCalc).toBeCloseTo(Q, precDigits);
+    });
+    if (mode !== undefined) {
+        it("Q(h1=" + h1 + ",W=" + W + ") Mode should be " + mode, () => {
+            expect(res.extraVar.Mode).toBe(mode);
+       });
+    }
+    if (regime !== undefined) {
+        it("Q(h1=" + h1 + ",W=" + W + ") Regime should be " + regime, () => {
+            expect(res.extraVar.Regime).toBe(regime);
+        });
+    }
+}
diff --git a/spec/structure/structure.spec.ts b/spec/structure/structure.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f0ba77c6ab37ed73656d6fcac695d423c659b85
--- /dev/null
+++ b/spec/structure/structure.spec.ts
@@ -0,0 +1,112 @@
+// tslint:disable-next-line:no-reference
+/// <reference path="../../node_modules/@types/jasmine/index.d.ts" />
+
+import { Result } from "../../src/base";
+import { Structure, StructureFlowMode, StructureFlowRegime, StructureParams} from "../../src/structure/structure";
+import { equalEpsilon } from "../nubtest";
+
+function check(val1: Result, val2: number) {
+    expect(equalEpsilon(val1.vCalc, val2)).toBe(true);
+}
+
+class StructureTest extends Structure {
+
+    constructor(prms: StructureParams, dbg: boolean = false) {
+        super(prms, dbg);
+    }
+
+    /**
+     * Test of getFlowMode
+     */
+    public testGetFlowMode() {
+        return this.getFlowMode();
+    }
+
+    /**
+     * Test of getFlowRegime
+     */
+    public testGetFlowRegime() {
+        return this.getFlowRegime();
+    }
+
+    public Equation(sVarCalc: string): Result {
+        let v: number;
+        switch (sVarCalc) {
+            case "Q":
+                v = this.prms.h1.v - this.prms.h2.v;
+                break;
+                default:
+                throw new Error("StructureTest.Equation() : invalid parameter name " + sVarCalc);
+        }
+        return new Result(v);
+    }
+
+}
+
+const structTestPrm: StructureParams = new StructureParams(1, 30, 15);
+const structTest: StructureTest = new StructureTest(structTestPrm, false);
+
+describe("Class Structure: ", () => {
+
+    describe("getFlowMode()", () => {
+        it("Flow Mode should be WEIR", () => {
+            expect(structTest.testGetFlowMode()).toBe(StructureFlowMode.WEIR);
+        });
+        it("Flow Mode should be ORIFICE", () => {
+            structTest.prms.W.v = 10;
+            expect(structTest.testGetFlowMode()).toBe(StructureFlowMode.ORIFICE);
+            structTest.prms.W.v = Infinity;
+        });
+    });
+
+    describe("getFlowRegime()", () => {
+        it("Flow Regime should be FREE", () => {
+            expect(structTest.testGetFlowRegime()).toBe(StructureFlowRegime.FREE);
+        });
+        it("Flow Regime should be SUBMERGED (WEIR)", () => {
+            structTest.prms.h2.v = 21;
+            expect(structTest.testGetFlowRegime()).toBe(StructureFlowRegime.SUBMERGED);
+        });
+        it("Flow Regime should be PARTIAL (ORIFICE)", () => {
+            structTest.prms.h2.v = 21;
+            structTest.prms.W.v = 15;
+            expect(structTest.testGetFlowRegime()).toBe(StructureFlowRegime.PARTIAL);
+        });
+        it("Flow Regime should be SUBMERGED (ORIFICE)", () => {
+            structTest.prms.h2.v = 25;
+            structTest.prms.W.v = 15;
+            expect(structTest.testGetFlowRegime()).toBe(StructureFlowRegime.SUBMERGED);
+        });
+        structTest.prms.h2.v = 15;
+        structTest.prms.W.v = Infinity;
+    });
+
+    describe("Calc()", () => {
+        const flagsNull = {Mode: StructureFlowMode.NULL, Regime: StructureFlowRegime.NULL};
+        it("h1=h2 => Q=0", () => {
+            structTest.prms.h2.v = structTest.prms.h1.v;
+            check(structTest.Calc("Q"), 0);
+            expect(structTest.Calc("Q").extraVar).toEqual(flagsNull);
+            structTest.prms.h2.v = 15;
+        });
+        it("W=0 => Q=0", () => {
+            structTest.prms.W.v = 0;
+            check(structTest.Calc("Q"), 0);
+            expect(structTest.Calc("Q").extraVar).toEqual(flagsNull);
+            structTest.prms.W.v = Infinity;
+        });
+        it("Q=0 => h1=h2", () => {
+            structTest.prms.Q.v = 0;
+            check(structTest.Calc("h1"), structTest.prms.h2.v);
+            expect(structTest.Calc("h1").extraVar).toEqual(flagsNull);
+            structTest.prms.Q.v = 1;
+        });
+        it("Q=0 => W=0", () => {
+            structTest.prms.Q.v = 0;
+            check(structTest.Calc("W"), 0);
+            expect(structTest.Calc("W").extraVar).toEqual(flagsNull);
+            structTest.prms.Q.v = 1;
+        });
+        // TODO Test inversion de débit
+    });
+});
diff --git a/spec/structure/structure_cem88d.spec.ts b/spec/structure/structure_cem88d.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1be7f28a43b0e65a3f1207b6986065c5924e2f4b
--- /dev/null
+++ b/spec/structure/structure_cem88d.spec.ts
@@ -0,0 +1,52 @@
+// tslint:disable-next-line:no-reference
+/// <reference path="../../node_modules/@types/jasmine/index.d.ts" />
+
+import { Result } from "../../src/base";
+import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { StructureCem88d } from "../../src/structure/structure_cem88d";
+import { itCalcQ } from "./rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureCem88d = new StructureCem88d(structPrm, false);
+
+describe("Class StructureCem88d: ", () => {
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const 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: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.050000, 1.300000, 1.500000];
+        const Q: number[] = [2.470345, 5.684601, 6.651906];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.PARTIAL, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+    describe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const Q: number[] = [4.366994, 9.764896];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.WEIR, StructureFlowMode.WEIR];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+});
diff --git a/spec/structure/structure_cem88v.spec.ts b/spec/structure/structure_cem88v.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2b3d1daccedac8d333f149c83284a1e54ffa2ca
--- /dev/null
+++ b/spec/structure/structure_cem88v.spec.ts
@@ -0,0 +1,51 @@
+// tslint:disable-next-line:no-reference
+/// <reference path="../../node_modules/@types/jasmine/index.d.ts" />
+
+import { Result } from "../../src/base";
+import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { StructureCem88v } from "../../src/structure/structure_cem88v";
+import { itCalcQ } from "./rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureCem88v = new StructureCem88v(structPrm, false);
+
+describe("Class StructureCem88v: ", () => {
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const Q: number[] = [.000000, 0.328260, 0.641822, 0.823867, 1.117381, 1.720738,
+            2.225472, 2.575336, 2.873893, 3.113250, 3.280545, 3.349403, 3.149324, 3.149324];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);
+        }
+    });
+    describe("Calcul Q en charge avec h1 croissant: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.05, 1.3, 1.5];
+        const Q: number[] = [1.365897, 3.623277, 4.214572];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.PARTIAL, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+    describe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const Q: number[] = [2.086781, 5.207945];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.WEIR, StructureFlowMode.WEIR];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+});
diff --git a/spec/structure/structure_cunge80.spec.ts b/spec/structure/structure_cunge80.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..69cb47d92783e0771efe2d91c160573b60a6094a
--- /dev/null
+++ b/spec/structure/structure_cunge80.spec.ts
@@ -0,0 +1,52 @@
+// tslint:disable-next-line:no-reference
+/// <reference path="../../node_modules/@types/jasmine/index.d.ts" />
+
+import { Result } from "../../src/base";
+import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { StructureCunge80 } from "../../src/structure/structure_cunge80";
+import { itCalcQ } from "./rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureCunge80 = new StructureCunge80(structPrm, false);
+
+describe("Class StructureCunge80: ", () => {
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const Q: number[] = [0.000000, 0.237709, 0.475418, 0.713127, 0.950836, 1.188545,
+            1.426254, 1.663963, 1.901673, 2.139382, 2.377091, 2.377091, 2.377091, 2.377091];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);
+        }
+    });
+    describe("Calcul Q en charge avec h1 croissant: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.050000, 1.300000, 1.500000];
+        const Q: number[] = [0.950836, 2.329064, 3.557704];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.SUBMERGED, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+    describe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const Q: number[] = [1.680857, 3.758510];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.WEIR, StructureFlowMode.WEIR];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+});
diff --git a/spec/structure/structure_orifice_free.spec.ts b/spec/structure/structure_orifice_free.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..db51c1ec0ffa9ac9d4ca20837494efd5beae4518
--- /dev/null
+++ b/spec/structure/structure_orifice_free.spec.ts
@@ -0,0 +1,52 @@
+// tslint:disable-next-line:no-reference
+/// <reference path="../../node_modules/@types/jasmine/index.d.ts" />
+
+import { Result } from "../../src/base";
+import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { StructureOrificeFree } from "../../src/structure/structure_orifice_free";
+import { itCalcQ } from "./rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureOrificeFree = new StructureOrificeFree(structPrm, false);
+
+describe("Class StructureOrificeFree: ", () => {
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const Q: number[] = [0.000000, 0.582266, 1.164532, 1.746798, 2.329064, 2.911330,
+            3.493596, 4.075861, 4.658127, 5.240393, 5.822659, 6.404925, 6.987191, 6.987191];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);
+        }
+    });
+    describe("Calcul Q en charge avec h1 croissant: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.050000, 1.300000, 1.500000];
+        const Q: number[] = [4.357279, 4.848333, 5.207945];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.FREE, StructureFlowRegime.FREE, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+    describe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const Q: number[] = [6.132249, 9.764896];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.WEIR, StructureFlowMode.WEIR];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.FREE, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+});
diff --git a/spec/structure/structure_orifice_submerged.spec.ts b/spec/structure/structure_orifice_submerged.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..050bd6a7dc76407ed57d86d755b7cf5cb75e37a4
--- /dev/null
+++ b/spec/structure/structure_orifice_submerged.spec.ts
@@ -0,0 +1,52 @@
+// tslint:disable-next-line:no-reference
+/// <reference path="../../node_modules/@types/jasmine/index.d.ts" />
+
+import { Result } from "../../src/base";
+import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { StructureOrificeSubmerged } from "../../src/structure/structure_orifice_submerged";
+import { itCalcQ } from "./rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureOrificeSubmerged = new StructureOrificeSubmerged(structPrm, false);
+
+describe("Class StructureOrificeSubmerged: ", () => {
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const Q: number[] = [0.000000, 0.237709, 0.475418, 0.713127, 0.950836, 1.188545,
+            1.426254, 1.663963, 1.901673, 2.139382, 2.377091, 2.614800, 2.852509, 2.852509];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);
+        }
+    });
+    describe("Calcul Q en charge avec h1 croissant: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.050000, 1.300000, 1.500000];
+        const Q: number[] = [0.950836, 2.329064, 3.006808];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.SUBMERGED, StructureFlowRegime.SUBMERGED];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+    describe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const Q: number[] = [1.848943, 5.637766];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.WEIR, StructureFlowMode.WEIR];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.SUBMERGED];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+});
diff --git a/spec/structure/structure_weir_free.spec.ts b/spec/structure/structure_weir_free.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9a75c2d01c581992d620c12c5f41b9cbc612f0ce
--- /dev/null
+++ b/spec/structure/structure_weir_free.spec.ts
@@ -0,0 +1,52 @@
+// tslint:disable-next-line:no-reference
+/// <reference path="../../node_modules/@types/jasmine/index.d.ts" />
+
+import { Result } from "../../src/base";
+import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { StructureWeirFree } from "../../src/structure/structure_weir_free";
+import { itCalcQ } from "./rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureWeirFree = new StructureWeirFree(structPrm, false);
+
+describe("Class StructureWeirFree: ", () => {
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const Q: number[] = [0.000000, 6.987191, 6.987191, 6.987191, 6.987191, 6.987191,
+            6.987191, 6.987191, 6.987191, 6.987191, 6.987191, 6.987191, 6.987191, 6.987191];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);
+        }
+    });
+    describe("Calcul Q en charge avec h1 croissant: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.050000, 1.300000, 1.500000];
+        const Q: number[] = [5.718929, 7.878541, 9.764896];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.WEIR, StructureFlowMode.WEIR, StructureFlowMode.WEIR];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.FREE, StructureFlowRegime.FREE, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+    describe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const Q: number[] = [6.132249, 9.764896];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.WEIR, StructureFlowMode.WEIR];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.FREE, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+});
diff --git a/spec/structure/test_rectangular_structure.ts b/spec/structure/test_rectangular_structure.ts
new file mode 100644
index 0000000000000000000000000000000000000000..603fd06240d11313a8b55354b85167a37c5cb3e0
--- /dev/null
+++ b/spec/structure/test_rectangular_structure.ts
@@ -0,0 +1,33 @@
+import { Result } from "../../src/base";
+import { RectangularStructure } from "../../src/structure/rectangular_structure";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { it, toBe, toBeCloseTo } from "../mock_jasmine";
+import { precDigits } from "../nubtest";
+import { Describer } from "./rectangular_structure";
+
+export function itCalcQ(
+    struct: RectangularStructure, h1: number, W: number, Q: number,
+    mode?: StructureFlowMode, regime?: StructureFlowRegime) {
+
+    struct.debug("itCalQ " + Describer.getName(struct) + " h1=" + h1 + " W=" + W + " Q=" + Q);
+
+    struct.prms.h1.v = h1;
+    struct.prms.W.v = W;
+    const res: Result = struct.Calc("Q");
+    struct.debug("struct.Calc(Q)=" + res.vCalc);
+
+    it("Q(h1=" + h1 + ",W=" + W + ") should be " + Q, () => {
+        struct.debug("struct.Calc(Q)=" + res.vCalc);
+        toBeCloseTo(res.vCalc, Q, precDigits);
+    });
+    if (mode !== undefined) {
+        it("Q(h1=" + h1 + ",W=" + W + ") Mode should be " + mode, () => {
+            toBe(res.extraVar.Mode, mode);
+       });
+    }
+    if (regime !== undefined) {
+        it("Q(h1=" + h1 + ",W=" + W + ") Regime should be " + regime, () => {
+            toBe(res.extraVar.Regime, regime);
+        });
+    }
+}
diff --git a/spec/structure/test_structure_cem88d.ts b/spec/structure/test_structure_cem88d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92c674710f9ba2602dabc00d4d9023ccf6583a7e
--- /dev/null
+++ b/spec/structure/test_structure_cem88d.ts
@@ -0,0 +1,41 @@
+import { Result } from "../../src/base";
+import { RectangularStructureParams, StructureCem88d } from "../../src/structure/structure_cem88d";
+import { describe, xdescribe } from "../mock_jasmine";
+import { precDigits } from "../nubtest";
+import { itCalcQ } from "./test_rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureCem88d = new StructureCem88d(structPrm, true);
+
+describe("Class StructureCem88d: ", () => {
+
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const 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: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.050000, 1.300000, 1.500000];
+        const Q: number[] = [2.470345, 5.684601, 6.651906];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i]);
+        }
+    });
+    describe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const 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_cem88v.ts b/spec/structure/test_structure_cem88v.ts
new file mode 100644
index 0000000000000000000000000000000000000000..20acbfaeeee65dc51f05d1400e21d6ba34729936
--- /dev/null
+++ b/spec/structure/test_structure_cem88v.ts
@@ -0,0 +1,40 @@
+import { Result } from "../../src/base";
+import { RectangularStructureParams, StructureCem88v } from "../../src/structure/structure_cem88v";
+import { describe, xdescribe } from "../mock_jasmine";
+import { precDigits } from "../nubtest";
+import { itCalcQ } from "./test_rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureCem88v = new StructureCem88v(structPrm, true);
+
+describe("Class StructureCem88v: ", () => {
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const Q: number[] = [.000000, 0.328260, 0.641822, 0.823867, 1.117381, 1.720738,
+            2.225472, 2.575336, 2.873893, 3.113250, 3.280545, 3.349403, 3.149324, 3.149324];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);
+        }
+    });
+    xdescribe("Calcul Q en charge avec h1 croissant: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.05, 1.3, 1.5];
+        const Q: number[] = [1.365897, 3.623277, 4.214572];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i]);
+        }
+    });
+    xdescribe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const Q: number[] = [2.086781, 5.207945];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i]);
+        }
+    });
+});
diff --git a/spec/structure/test_structure_cunge80.ts b/spec/structure/test_structure_cunge80.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7cc5df7cf6b296715fb54247ed5b4a4ebdcdc30f
--- /dev/null
+++ b/spec/structure/test_structure_cunge80.ts
@@ -0,0 +1,50 @@
+import { Result } from "../../src/base";
+import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params";
+import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure";
+import { StructureCunge80 } from "../../src/structure/structure_cunge80";
+import { describe, xdescribe } from "../mock_jasmine";
+import { itCalcQ } from "./test_rectangular_structure";
+
+const structPrm: RectangularStructureParams = new RectangularStructureParams(1, 1, 1, 2, 0.6, 0);
+const structTest: StructureCunge80 = new StructureCunge80(structPrm, true);
+
+describe("Class StructureCunge80: ", () => {
+    describe("Calcul Q avec W croissant: ", () => {
+        const 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];
+        const h1: number = 1.200000;
+        const Q: number[] = [0.000000, 0.237709, 0.475418, 0.713127, 0.950836, 1.188545,
+            1.426254, 1.663963, 1.901673, 2.139382, 2.377091, 2.377091, 2.377091, 2.377091];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1, W[i], Q[i]);
+        }
+    });
+    describe("Calcul Q en charge avec h1 croissant: ", () => {
+        const W: number = 0.8;
+        const h1: number[] = [1.050000, 1.300000, 1.500000];
+        const Q: number[] = [0.950836, 2.329064, 3.557704];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE, StructureFlowMode.ORIFICE];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.SUBMERGED, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+    describe("Calcul Q a surface libre avec h1 croissant: ", () => {
+        const W: number = Infinity;
+        const h1: number[] = [1.100000, 1.500000];
+        const Q: number[] = [1.680857, 3.758510];
+        const mode: StructureFlowMode[] = [
+            StructureFlowMode.WEIR, StructureFlowMode.WEIR];
+        const regime: StructureFlowRegime[] = [
+            StructureFlowRegime.SUBMERGED, StructureFlowRegime.FREE];
+
+        for (let i = 0; i < Q.length; i++ ) {
+            itCalcQ(structTest, h1[i], W, Q[i], mode[i], regime[i]);
+        }
+    });
+});
diff --git a/src/base.ts b/src/base.ts
index 9e7c06fed8ec509d9ed578c3282cb0e94c6f1e2d..ad55b28fade5bb9b6959e5e2245d49c8adbfe241 100644
--- a/src/base.ts
+++ b/src/base.ts
@@ -4,17 +4,26 @@ import { Message, MessageCode } from "./util/message";
  * Résultat de calcul comprenant la valeur du résultat et des calculs annexes (flag, calculs intermédiaires...)
  */
 export class Result {
+
+    /** Calculs intermédiaires ou supplémentaires, flags de calcul */
+    public extraVar: { [key: string]: any };
+
     /** Valeur calculée */
-    private _vCalc: number;
+    public vCalc: number;
 
     private _message: Message;
 
-    constructor(v: number, e: Message = undefined) {
-        this._vCalc = v;
-        this._message = e;
-    };
-
-    get vCalc() { return this._vCalc; }
+    /**
+     * Résultat de calcul
+     * @param v Valeur numérique du résultat
+     * @param m Message d'erreur
+     * @param e Calculs intermédiaires ou supplémentaires, flags de calcul
+     */
+    constructor(v: number, e?: { [key: string]: any }, m?: Message) {
+        this.vCalc = v;
+        this.extraVar = e;
+        this._message = m;
+    }
 
     get code() {
         if (this._message == undefined)
@@ -49,11 +58,11 @@ export abstract class Debug {
      */
     constructor(private _DBG: boolean) { }
 
-    /** 
+    /**
      * Affiche un message dans la console si le flag this.DBG est à true
      * @param s Message à afficher dans la console
      */
-    protected debug(s: any) {
+    public debug(s: any) {
         if (this._DBG) console.log(s);
     }
 
diff --git a/src/dichotomie.ts b/src/dichotomie.ts
index 9485690c41128933c5a53034e6b579464b2aad21..d02fd2a010f6de02503138bd719bb0bcfd725890 100644
--- a/src/dichotomie.ts
+++ b/src/dichotomie.ts
@@ -360,7 +360,7 @@ export class Dichotomie extends Debug {
 
         // gestion de l'erreur
 
-        // la valeur cible de la fonction est elle trouvable ?    
+        // la valeur cible de la fonction est elle trouvable ?
 
         let m;
         let res: Message;
@@ -461,7 +461,7 @@ export class Dichotomie extends Debug {
         if (r.ok)
             var interv: SearchInterval = r.intSearch;
         else {
-            let result = new Result(undefined, r.res);
+            let result = new Result(undefined, undefined, r.res);
             return result;
         }
 
diff --git a/src/structure/rectangular_structure.ts b/src/structure/rectangular_structure.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a835b146a9c764c11d7d25eac107557c4761c8c6
--- /dev/null
+++ b/src/structure/rectangular_structure.ts
@@ -0,0 +1,32 @@
+import { ParamCalculability } from "../param";
+import { RectangularStructureParams } from "./rectangular_structure_params";
+import { Structure } from "./structure";
+
+export { RectangularStructureParams };
+
+/**
+ * Classe mère pour toutes les structures ayant une base rectangulaire (vannes, seuils)
+ */
+export abstract class RectangularStructure extends Structure {
+
+    constructor(prms: RectangularStructureParams, dbg: boolean = false) {
+        super(prms, dbg);
+    }
+
+    /**
+     * paramètres castés au bon type
+     */
+    get prms(): RectangularStructureParams {
+        return this._prms as RectangularStructureParams;
+    }
+
+    /**
+     * paramétrage de la calculabilité des paramètres
+     */
+    protected setParametersCalculability() {
+        super.setParametersCalculability();
+        this.prms.L.calculability = ParamCalculability.DICHO;
+        this.prms.Cd.calculability = ParamCalculability.DICHO;
+    }
+
+}
diff --git a/src/structure/rectangular_structure_params.ts b/src/structure/rectangular_structure_params.ts
new file mode 100644
index 0000000000000000000000000000000000000000..edd24882d4056e5269a8d5361f6cb193c92fb18c
--- /dev/null
+++ b/src/structure/rectangular_structure_params.ts
@@ -0,0 +1,22 @@
+import { ComputeNodeType, ParamCalculability, ParamDefinition, ParamDomainValue, ParamsEquation } from "../param";
+import { Structure } from "./structure";
+import { StructureParams } from "./structure_params";
+
+/**
+ * Parameters for rectangular structures (common for all rectangular structure equations)
+ */
+export class RectangularStructureParams extends StructureParams {
+    /** Width of the gate or length of the sill (m) */
+    public L: ParamDefinition;
+
+    /** Discharge coefficient */
+    public Cd: ParamDefinition;
+
+    constructor(rQ: number, rh1: number, rh2: number, rL: number, rCd: number, rW: number = Infinity) {
+        super(rQ, rh1, rh2, rW);
+        this.L = new ParamDefinition(ComputeNodeType.CondDistri, "L", ParamDomainValue.POS, rL);
+        this.addParamDefinition(this.L);
+        this.Cd = new ParamDefinition(ComputeNodeType.CondDistri, "Cd", ParamDomainValue.POS, rCd);
+        this.addParamDefinition(this.Cd);
+    }
+}
diff --git a/src/structure/structure.ts b/src/structure/structure.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f1254a47f068523df82df5f3568d8ebc29d0b0a0
--- /dev/null
+++ b/src/structure/structure.ts
@@ -0,0 +1,167 @@
+import { Result } from "../base";
+import { Nub } from "../nub";
+import { ParamCalculability } from "../param";
+import { Message } from "../util/message";
+
+import { StructureParams } from "./structure_params";
+
+export { StructureParams };
+
+/**
+ * Flow mode: weir or orifice flow
+ */
+export enum StructureFlowMode {
+   /** Weir flow */
+   WEIR,
+   /** Orifice flow */
+   ORIFICE,
+   /** Zéro flow */
+   NULL,
+}
+
+/**
+ * Flow regime: free flow, partially submerged or submerged
+ */
+export enum StructureFlowRegime {
+    /** Free flow (unsubmerged) */
+    FREE,
+    /** Partially submerged flow */
+    PARTIAL,
+    /** Submerged flow */
+    SUBMERGED,
+    /** Zéro flow */
+    NULL,
+ }
+
+/**
+ * classe de calcul sur la conduite distributrice
+ */
+export abstract class Structure extends Nub {
+
+    /** Constante utile : Racine de 2g */
+    protected static readonly R2G: number = Math.sqrt(2 * 9.81);
+
+    constructor(prms: StructureParams, dbg: boolean = false) {
+        super(prms, dbg);
+    }
+
+    /**
+     * paramètres castés au bon type
+     */
+    get prms(): StructureParams {
+        return this._prms as StructureParams;
+    }
+
+    /**
+     * Calcul du mode et du régime d'écoulement
+     */
+    public Equation(sVarCalc: string): Result {
+        this.CheckEquation(sVarCalc);
+        const res: Result = new Result(undefined, {
+                                        Mode:     this.getFlowMode(),
+                                        Regime:   this.getFlowRegime()});
+        return res;
+    }
+
+    /**
+     * Calcul d'une équation quelque soit l'inconnue à calculer.
+     * Gestion du débit nul et de l'inversion de débit
+     * @param sVarCalc nom de la variable à calculer
+     * @param rInit valeur initiale de la variable à calculer dans le cas de la dichotomie
+     * @param rPrec précision de calcul
+     */
+    public Calc(sVarCalc: string, rInit: number = 0, rPrec: number = 0.001): Result {
+        const flagsNull = {Mode: StructureFlowMode.NULL, Regime: StructureFlowRegime.NULL};
+        // Gestion du débit nul
+        if (sVarCalc === "Q") {
+            if (this.prms.h1.v === this.prms.h2.v || this.prms.W.v <= 0) {
+                return new Result(0, flagsNull);
+            }
+        } else if (this.prms.Q.v === 0) {
+            // Débit nul <=> tirant d'eau amont = tirant d'eau aval ou tout autre paramètre nul
+            switch (sVarCalc) {
+                case "h1" :
+                    return new Result(this.prms.h2.v, flagsNull);
+                case "h2" :
+                    return new Result(this.prms.h1.v, flagsNull);
+                default :
+                    // Est-ce toujours vrai ? Nécessitera peut-être d'étendre la méthode
+                    return new Result(0, flagsNull);
+            }
+        } else if (this.prms.W.v === 0 && sVarCalc === "h1") {
+            return new Result(Infinity, flagsNull); // Si la vanne est fermée la cote amont est infinie
+        }
+
+        // Gestion de l'inversion de débit : on inverse l'amont et l'aval pour le calcul
+        if ((sVarCalc === "Q" && (this.prms.h1.v < this.prms.h2.v)) || (sVarCalc !== "Q" && this.prms.Q.v < 0)) {
+            [this.prms.h1.v, this.prms.h2.v] = [this.prms.h2.v, this.prms.h1.v]; // Swap ES6 fashion
+            let res: Result;
+            res = super.Calc(sVarCalc, rInit, rPrec);
+            [this.prms.h1.v, this.prms.h2.v] = [this.prms.h2.v, this.prms.h1.v]; // Swap ES6 fashion
+            return res;
+        }
+
+        return super.Calc(sVarCalc, rInit, rPrec);
+
+    }
+
+    /**
+     * paramétrage de la calculabilité des paramètres
+     */
+    protected setParametersCalculability() {
+        this.prms.Q.calculability = ParamCalculability.EQUATION;
+        this.prms.h1.calculability = ParamCalculability.DICHO;
+        this.prms.h2.calculability = ParamCalculability.DICHO;
+        this.prms.W.calculability = ParamCalculability.DICHO;
+    }
+
+    /**
+     * Give the flow mode : weir or orifice flow
+     */
+    protected getFlowMode(): StructureFlowMode  {
+        if (this.prms.h1.v > this.prms.W.v) {
+            this.debug("Structure.getFlowMode(h1=" + this.prms.h1.v + ",W=" + this.prms.W.v + ")=ORIFICE");
+            return StructureFlowMode.ORIFICE;
+        } else {
+            this.debug("Structure.getFlowMode(h1=" + this.prms.h1.v + ",W=" + this.prms.W.v + ")=WEIR");
+            return StructureFlowMode.WEIR;
+        }
+
+    }
+
+    /**
+     * Give the flow regime for a rectangular section : free, partially submerged or submerged flow
+     */
+    protected getFlowRegime(): StructureFlowRegime  {
+        // Weir have only two flow regimes: free and submerged flow
+        // 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;
+        }
+    }
+
+    /**
+     * Test générique si VarCalc="Q" pour l'utilisation de Equation
+     */
+    private CheckEquation(sVarCalc: string) {
+        if (sVarCalc !== "Q") { throw new Error("Structure.Equation() : invalid parameter name " + sVarCalc); }
+    }
+}
diff --git a/src/structure/structure_cem88d.ts b/src/structure/structure_cem88d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8ddde8744cb93df31c65333fe725ae8583d6f80b
--- /dev/null
+++ b/src/structure/structure_cem88d.ts
@@ -0,0 +1,58 @@
+import { Result } from "../base";
+import { RectangularStructure } from "./rectangular_structure";
+import { RectangularStructureParams } from "./rectangular_structure_params";
+import { Structure, StructureFlowMode, StructureFlowRegime } from "./structure";
+
+export { RectangularStructureParams };
+
+/**
+ * Equation CEM88D : déversoir / orifice (pelle importante) Cemagref 1988
+ */
+export class StructureCem88d extends RectangularStructure {
+
+    /**
+     * Calcul analytique Q = f(Cd, L, h1, h2, W) CEM88D
+     * @param sVarCalc Variable à calculer (doit être "Q")
+     */
+    public Equation(sVarCalc: string): Result {
+        const res: Result = super.Equation(sVarCalc);
+        let v: number;
+        const cd: number = this.prms.Cd.v * this.prms.L.v * Structure.R2G;
+        const b1: number = Math.sqrt(this.prms.h1.v);
+        const b2: number = Math.sqrt(this.prms.h1.v - this.prms.h2.v);
+        const 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 (res.extraVar.Regime) {
+                    case StructureFlowRegime.FREE:
+                        v = cd * this.prms.h1.v * b1;
+                        break;
+                    case StructureFlowRegime.SUBMERGED:
+                        v = cd1 * this.prms.h2.v * b2;
+                        this.debug("StructureCem88d.Equation WEIR SUBMERGED Q=" + v);
+                        break;
+                }
+                break;
+            case StructureFlowMode.ORIFICE:
+                const 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);
+        res.vCalc = v;
+        return res;
+    }
+}
diff --git a/src/structure/structure_cem88v.ts b/src/structure/structure_cem88v.ts
new file mode 100644
index 0000000000000000000000000000000000000000..457f430006452b553bedd14f5b744a29ed1bafff
--- /dev/null
+++ b/src/structure/structure_cem88v.ts
@@ -0,0 +1,127 @@
+import { Result } from "../base";
+import { RectangularStructure } from "./rectangular_structure";
+import { RectangularStructureParams } from "./rectangular_structure_params";
+import { Structure, StructureFlowMode, StructureFlowRegime } from "./structure";
+
+export { RectangularStructureParams };
+
+/**
+ * Equation CEM88V : déversoir / vanne de fond (pelle faible) Cemagref 1988
+ */
+export class StructureCem88v extends RectangularStructure {
+
+    /**
+     * Calcul analytique Q = f(Cd, L, h1, h2, W) CEM88V
+     * @param sVarCalc Variable à calculer (doit être "Q")
+     */
+    public Equation(sVarCalc: string): Result {
+        const res: Result = super.Equation(sVarCalc);
+        let v: number;
+        const mu0: number = 2 / 3 * this.prms.Cd.v;
+        let KF: number;
+        if (res.extraVar.Regime !== StructureFlowRegime.FREE) {
+            KF = this.getKF(Math.sqrt( 1 - this.prms.h2.v / this.prms.h1.v), this.getAlfa(this.prms.h2.v));
+        }
+        switch (res.extraVar.Mode) {
+            case StructureFlowMode.WEIR:
+                const muf: number = mu0 - 0.08;
+                switch (res.extraVar.Regime) {
+                    case StructureFlowRegime.FREE:
+                        v = muf * this.prms.L.v * Structure.R2G * Math.pow(this.prms.h1.v, 1.5);
+                        break;
+                    case StructureFlowRegime.SUBMERGED:
+                        // console.log("KF="+KF+" muf="+muf);
+                        v = KF * muf * this.prms.L.v * Structure.R2G * Math.pow(this.prms.h1.v, 1.5);
+                        break;
+                }
+                break;
+            case StructureFlowMode.ORIFICE:
+                const mu: number  = mu0 - 0.08 / (this.prms.h1.v / this.prms.W.v);
+                const mu1: number = mu0 - 0.08 / (this.prms.h1.v / this.prms.W.v - 1);
+                if (res.extraVar.Regime === StructureFlowRegime.FREE) {
+                    v = this.prms.L.v * Structure.R2G
+                        * (mu * Math.pow(this.prms.h1.v, 1.5)
+                        - mu1 * Math.pow(this.prms.h1.v - this.prms.W.v, 1.5));
+                } else {
+                    if (res.extraVar.Regime === StructureFlowRegime.PARTIAL) {
+                        v = this.prms.L.v * Structure.R2G * (
+                            KF * mu * Math.pow(this.prms.h1.v, 1.5)
+                            - mu1 * Math.pow(this.prms.h1.v - this.prms.W.v, 1.5)
+                        );
+                    } else {
+                        const KF1 = this.getKF(
+                            Math.sqrt( 1 - (this.prms.h2.v - this.prms.W.v) / (this.prms.h1.v - this.prms.W.v)),
+                            this.getAlfa(this.prms.h2.v - this.prms.W.v),
+                        );
+                        v = this.prms.L.v * Structure.R2G * (
+                            KF * mu * Math.pow(this.prms.h1.v, 1.5)
+                            - KF1 * mu1 * Math.pow(this.prms.h1.v - this.prms.W.v, 1.5)
+                        );
+                    }
+                }
+        }
+        this.debug(
+            "StructureCem88v.Equation(h1=" + this.prms.h1.v
+            + ",h2=" + this.prms.h2.v + ",W=" + this.prms.W.v + ") => Q=" + v);
+        res.vCalc = v;
+        return res;
+    }
+
+    /**
+     * Give the flow regime for Equation CEM88V : free, partially submerged or submerged flow
+     */
+    protected getFlowRegime(): StructureFlowRegime  {
+        const mode: StructureFlowMode = this.getFlowMode();
+        // Weir have only two flow regimes: free and submerged flow
+        let alfa: number;
+        switch (mode) {
+            case StructureFlowMode.WEIR:
+                alfa = 0.75;
+                break;
+            case StructureFlowMode.ORIFICE:
+                alfa =  this.getAlfa(this.prms.h2.v);
+                break;
+        }
+        if (this.prms.h2.v <= alfa * this.prms.h1.v) {
+            this.debug(
+                "StructureCem88v.getFlowRegime(h1=" + this.prms.h1.v
+                + ",h2=" + this.prms.h2.v + ",W=" + this.prms.W.v + ")=FREE");
+            return StructureFlowRegime.FREE;
+        } else {
+            alfa = this.getAlfa(this.prms.h2.v - this.prms.W.v);
+            if (
+                mode === StructureFlowMode.ORIFICE
+                && this.prms.h2.v <= alfa * this.prms.h1.v + (1 - alfa) * this.prms.W.v
+            ) {
+                this.debug(
+                    "StructureCem88v.getFlowRegime(h1="  + this.prms.h1.v
+                    + ",h2=" + this.prms.h2.v + ",W=" + this.prms.W.v + ")=PARTIAL");
+                return StructureFlowRegime.PARTIAL;
+            } else {
+                this.debug(
+                    "StructureCem88v.getFlowRegime(h1=" + this.prms.h1.v
+                    + ",h2=" + this.prms.h2.v + ",W=" + this.prms.W.v + ")=SUBMERGED");
+                return StructureFlowRegime.SUBMERGED;
+            }
+        }
+    }
+
+    private getAlfa(h2: number): number {
+        let alfa: number = 1 - 0.14 * (h2) / this.prms.W.v;
+        alfa = Math.min(Math.max(alfa, 0.4), 0.75);
+        // console.log("alfa=" + alfa);
+        return alfa;
+    }
+
+    private getKF(x: number, alfa: number): number {
+        const beta1 = -2 * alfa + 2.6;
+        let KF: number;
+        if (x > 0.2) {
+            KF = 1 - Math.pow(1 - x / Math.sqrt(1 - alfa), beta1);
+        } else {
+            KF = 5 * x * (1 - Math.pow(1 - 0.2 / Math.sqrt(1 - alfa), beta1));
+        }
+        // console.log("KF(x="+x+")="+KF);
+        return KF;
+    }
+}
diff --git a/src/structure/structure_cunge80.ts b/src/structure/structure_cunge80.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d9cc8f448aad3ae941fb0198a1c8d4e375339889
--- /dev/null
+++ b/src/structure/structure_cunge80.ts
@@ -0,0 +1,69 @@
+import { Result } from "../base";
+import { RectangularStructure } from "./rectangular_structure";
+import { RectangularStructureParams } from "./rectangular_structure_params";
+import { Structure, StructureFlowMode, StructureFlowRegime } from "./structure";
+
+export { RectangularStructureParams };
+
+/**
+ * Equation Cunge80
+ */
+export class StructureCunge80 extends RectangularStructure {
+    /**
+     * Calcul du débit avec l'équation Cunge80
+     * @param sVarCalc Variable à calculer (doit être égale à Q ici)
+     */
+    public Equation(sVarCalc: string): Result {
+        const res: Result = super.Equation(sVarCalc);
+        switch (res.extraVar.Regime) {
+            case StructureFlowRegime.FREE:
+                if (res.extraVar.Mode === StructureFlowMode.WEIR) {
+                    const R32: number = 3 * Math.sqrt(3) / 2;
+                    res.vCalc = this.prms.Cd.v * this.prms.L.v * Structure.R2G / R32 * Math.pow(this.prms.h1.v, 1.5);
+                    this.debug("StructureCunge80.Equation WEIR FREE Q=" + res.vCalc);
+                } else {
+                    res.vCalc = this.prms.Cd.v * this.prms.L.v * Structure.R2G
+                        * this.prms.W.v * Math.pow(this.prms.h1.v - this.prms.W.v, 0.5);
+                    this.debug("StructureCunge80.Equation ORIFICE FREE Q=" + res.vCalc);
+                }
+                break;
+            case StructureFlowRegime.SUBMERGED:
+                if (res.extraVar.Mode === StructureFlowMode.WEIR) {
+                    res.vCalc = this.prms.Cd.v * this.prms.L.v * Structure.R2G * this.prms.h2.v
+                        * Math.sqrt(this.prms.h1.v - this.prms.h2.v);
+                    this.debug("StructureCunge80.Equation WEIR SUBMERGED Q=" + res.vCalc);
+                } else {
+                    res.vCalc = this.prms.Cd.v * this.prms.L.v * Structure.R2G
+                        * this.prms.W.v * Math.sqrt(this.prms.h1.v - this.prms.h2.v);
+                    this.debug("StructureCunge80.Equation ORIFICE SUBMERGED Q=" + res.vCalc);
+                }
+        }
+        return res;
+    }
+
+    protected getFlowRegime() {
+        if (this.prms.h1.v >= 1.5 * this.prms.h2.v) {
+            return StructureFlowRegime.FREE;
+        } else {
+            return StructureFlowRegime.SUBMERGED;
+        }
+    }
+
+    protected getFlowMode() {
+        const regime: StructureFlowRegime = this.getFlowRegime();
+        switch (regime) {
+            case StructureFlowRegime.FREE:
+                if (this.prms.W.v <= 2 / 3 * this.prms.h1.v) {
+                    return StructureFlowMode.ORIFICE;
+                } else {
+                    return StructureFlowMode.WEIR;
+                }
+            case StructureFlowRegime.SUBMERGED:
+                if (this.prms.W.v <= this.prms.h2.v) {
+                    return StructureFlowMode.ORIFICE;
+                } else {
+                    return StructureFlowMode.WEIR;
+                }
+        }
+    }
+}
diff --git a/src/structure/structure_orifice_free.ts b/src/structure/structure_orifice_free.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fcb3477baaeaebf07645bc770024b371c9adc367
--- /dev/null
+++ b/src/structure/structure_orifice_free.ts
@@ -0,0 +1,24 @@
+import { Result } from "../base";
+import { RectangularStructure } from "./rectangular_structure";
+import { RectangularStructureParams } from "./rectangular_structure_params";
+import { Structure, StructureFlowMode, StructureFlowRegime } from "./structure";
+
+export { RectangularStructureParams };
+
+/**
+ * Equation classique orifice dénoyé
+ */
+export class StructureOrificeFree extends RectangularStructure {
+    /**
+     * Calcul du débit avec l'équation classique d'un orifice dénoyé
+     * @param sVarCalc Variable à calculer (doit être égale à Q ici)
+     */
+    public Equation(sVarCalc: string): Result {
+        const res: Result = super.Equation(sVarCalc);
+        // TODO : Warning si les conditions hydrauliques ne correspondent pas à un écoulement dénoyé
+        res.extraVar.Regime = StructureFlowRegime.FREE;
+        res.vCalc = this.prms.Cd.v * Math.min(this.prms.W.v, this.prms.h1.v) * this.prms.L.v
+            * Structure.R2G * Math.sqrt(this.prms.h1.v);
+        return res;
+    }
+}
diff --git a/src/structure/structure_orifice_submerged.ts b/src/structure/structure_orifice_submerged.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bfdc4e57c0e68558e28e70945ae42d3036aea2ef
--- /dev/null
+++ b/src/structure/structure_orifice_submerged.ts
@@ -0,0 +1,24 @@
+import { Result } from "../base";
+import { RectangularStructure } from "./rectangular_structure";
+import { RectangularStructureParams } from "./rectangular_structure_params";
+import { Structure, StructureFlowMode, StructureFlowRegime } from "./structure";
+
+export { RectangularStructureParams };
+
+/**
+ * Equation classique orifice noyé
+ */
+export class StructureOrificeSubmerged extends RectangularStructure {
+    /**
+     * Calcul du débit avec l'équation classique d'un orifice noyé
+     * @param sVarCalc Variable à calculer (doit être égale à Q ici)
+     */
+    public Equation(sVarCalc: string): Result {
+        const res: Result = super.Equation(sVarCalc);
+        // TODO : Warning si les conditions hydrauliques ne correspondent pas à un écoulement dénoyé
+        res.extraVar.Regime = StructureFlowRegime.SUBMERGED;
+        res.vCalc = this.prms.Cd.v * Math.min(this.prms.W.v, this.prms.h1.v) * this.prms.L.v
+            * Structure.R2G * Math.sqrt(this.prms.h1.v - this.prms.h2.v);
+        return res;
+    }
+}
diff --git a/src/structure/structure_params.ts b/src/structure/structure_params.ts
new file mode 100644
index 0000000000000000000000000000000000000000..52305ce790126500672211ec1c528b423d480516
--- /dev/null
+++ b/src/structure/structure_params.ts
@@ -0,0 +1,42 @@
+import { Result } from "../base";
+import { Nub } from "../nub";
+import { ComputeNodeType, ParamDefinition, ParamDomain, ParamDomainValue, ParamsEquation } from "../param";
+import { Message } from "../util/message";
+
+/**
+ * Common parameters of hydraulic structure equations
+ */
+export class StructureParams extends ParamsEquation {
+    /** Débit */
+    public Q: ParamDefinition;
+
+    /** Tirant d'eau amont */
+    public h1: ParamDefinition;
+
+    /** Tirant d'eau aval */
+    public h2: ParamDefinition;
+
+    /** Ouverture de la vanne
+     * @note Pour un seuil cette valeur vaut Infinity
+     */
+    public W: ParamDefinition;
+
+    /**
+     * Paramètres communs à toutes les équations de structure
+     * @param rQ Débit (m3/s)
+     * @param rh1 Hauteur amont au dessus de la crête (m)
+     * @param rh2 Hauteur aval au dessus de la crête (m)
+     * @param rW Ouverture de vanne (m) (infinity pour un seuil)
+     */
+    constructor(rQ: number, rh1: number, rh2: number, rW: number = Infinity ) {
+        super();
+        this.Q = new ParamDefinition(ComputeNodeType.CondDistri, "Q", ParamDomainValue.POS_NULL, rQ);
+        this.addParamDefinition(this.Q);
+        this.h1 = new ParamDefinition(ComputeNodeType.CondDistri, "h1", ParamDomainValue.POS_NULL, rh1);
+        this.addParamDefinition(this.h1);
+        this.h2 = new ParamDefinition(ComputeNodeType.CondDistri, "h2", ParamDomainValue.POS_NULL, rh2);
+        this.addParamDefinition(this.h2);
+        this.W = new ParamDefinition(ComputeNodeType.CondDistri, "W", ParamDomainValue.POS_NULL, rW);
+        this.addParamDefinition(this.W);
+    }
+}
diff --git a/src/structure/structure_weir_free.ts b/src/structure/structure_weir_free.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd1246406f35b38b10429ab821e34edc7d8f3831
--- /dev/null
+++ b/src/structure/structure_weir_free.ts
@@ -0,0 +1,24 @@
+import { Result } from "../base";
+import { RectangularStructure } from "./rectangular_structure";
+import { RectangularStructureParams } from "./rectangular_structure_params";
+import { Structure, StructureFlowMode, StructureFlowRegime } from "./structure";
+
+export { RectangularStructureParams };
+
+/**
+ * Equation classique seuil dénoyé
+ */
+export class StructureWeirFree extends RectangularStructure {
+    /**
+     * Calcul analytique Q = f(Cd, L, h1, h2, W) seuil dénoyé
+     * @param sVarCalc Variable à calculer (doit être "Q")
+     */
+    public Equation(sVarCalc: string): Result {
+        const res: Result = super.Equation(sVarCalc);
+        // TODO : Warning si les conditions hydrauliques ne correspondent pas à un seuil dénoyé
+        res.extraVar.Regime = StructureFlowRegime.FREE;
+        res.extraVar.Mode = StructureFlowMode.WEIR;
+        res.vCalc = this.prms.Cd.v * this.prms.L.v * Structure.R2G * Math.pow(this.prms.h1.v, 1.5);
+        return res;
+    }
+}