From 996705ee3330a31648dbee9fcb92dd3bfa17b78c Mon Sep 17 00:00:00 2001
From: David Dorchies <david.dorchies@irstea.fr>
Date: Fri, 6 Sep 2019 17:05:20 +0200
Subject: [PATCH] Fix #137 Miscalculation in MacroRugoCompound

---
 spec/macrorugo/macrorugo_compound.spec.ts | 76 ++++++++++++++++++++---
 spec/test_func.ts                         | 23 +++++++
 src/macrorugo/macrorugo_compound.ts       |  2 +-
 src/param/param-definition.ts             |  6 +-
 src/structure/parallel_structure.ts       | 24 +++----
 5 files changed, 103 insertions(+), 28 deletions(-)

diff --git a/spec/macrorugo/macrorugo_compound.spec.ts b/spec/macrorugo/macrorugo_compound.spec.ts
index f2a1ecf4..09653377 100644
--- a/spec/macrorugo/macrorugo_compound.spec.ts
+++ b/spec/macrorugo/macrorugo_compound.spec.ts
@@ -1,10 +1,29 @@
 import { CalculatorType } from "../../src/compute-node";
-import { MacroRugo } from "../../src/macrorugo/macrorugo";
+import { MacroRugo, MacrorugoParams } from "../../src/macrorugo/macrorugo";
 import { MacrorugoCompound } from "../../src/macrorugo/macrorugo_compound";
 import { Props } from "../../src/props";
 import { Session } from "../../src/session";
+import { compareTwoResults } from "../test_func";
 
 let BR: number;
+let mrc2: MacrorugoCompound;
+
+/**
+ * Return default instance of MacroRugo as reference for one apron
+ * @param i Offset of ZF1 (- 0.2 * i) and B (+ i)
+ */
+function getMacroRugoRef(i: number = 0): MacroRugo {
+    const mr = Session.getInstance().createNub(
+        new Props({ calcType: CalculatorType.MacroRugo })
+    ) as MacroRugo;
+    mr.prms.ZF1.singleValue -= 0.21 * i;
+    mr.prms.Y.singleValue += 0.21 * i;
+    mr.prms.B.singleValue += i;
+    mr.prms.L.singleValue = 3 / mr.prms.If.currentValue;
+    mr.prms.Q.setCalculated();
+    mr.CalcSerie();
+    return mr;
+}
 
 describe("MacroRugoCompound", () => {
     describe("Default 1 apron", () => {
@@ -13,9 +32,20 @@ describe("MacroRugoCompound", () => {
                 new Props({ calcType: CalculatorType.MacroRugoCompound })
             ) as MacrorugoCompound;
             mrc.addDefaultChild();
-            const mr = Session.getInstance().createNub(new Props({ calcType: CalculatorType.MacroRugo })) as MacroRugo;
-            mr.prms.Q.setCalculated();
-            expect(mrc.CalcSerie().vCalc).toBeCloseTo(mr.CalcSerie().vCalc, 3);
+            const mr = getMacroRugoRef();
+            expect(mrc.CalcSerie().vCalc).toBeCloseTo(mr.result.vCalc, 3);
+        });
+        it("should return same result as Macrorugo", () => {
+            const mrc = Session.getInstance().createNub(
+                new Props({ calcType: CalculatorType.MacroRugoCompound })
+            ) as MacrorugoCompound;
+            mrc.addDefaultChild();
+            mrc.children[0].prms.L.singleValue = 0.5;
+            mrc.CalcSerie();
+            const mr = getMacroRugoRef();
+            mr.prms.L.singleValue = mrc.children[0].prms.L.v;
+            mr.CalcSerie();
+            compareTwoResults(mrc.children[0].result, mr.result, 3);
         });
     });
     describe("Default inclined but flat apron", () => {
@@ -32,11 +62,9 @@ describe("MacroRugoCompound", () => {
                 ) as MacrorugoCompound;
                 mrc.properties.setPropValue("inclinedApron", true);
                 mrc.prms.BR.singleValue = BR;
-                const mr = Session.getInstance()
-                    .createNub(new Props({ calcType: CalculatorType.MacroRugo })) as MacroRugo;
-                mr.prms.Q.setCalculated();
+                const mr = getMacroRugoRef();
                 expect(mrc.CalcSerie().vCalc)
-                    .toBeCloseTo(mr.CalcSerie().vCalc * mrc.prms.BR.currentValue / mr.prms.B.currentValue, 3);
+                    .toBeCloseTo(mr.result.vCalc * mrc.prms.BR.currentValue / mr.prms.B.currentValue, 3);
                 const ax = mrc.prms.PBD.v / Math.sqrt(mrc.prms.C.v);
                 let B: number = 0;
                 for (const child of mrc.children) {
@@ -49,5 +77,35 @@ describe("MacroRugoCompound", () => {
             });
         }
     });
-
+    describe("Multiple apron", () => {
+        beforeAll(() => {
+            mrc2 = Session.getInstance().createNub(
+                new Props({ calcType: CalculatorType.MacroRugoCompound })
+            ) as MacrorugoCompound;
+            for (let i = 0; i < 3; i++) {
+                mrc2.addChild(
+                    new MacroRugo(
+                        new MacrorugoParams(0, 100, 100, 0.5, 1, 10, 1, 1, 1, 10, 0.5)
+                    )
+                );
+                mrc2.children[i].prms.ZF1.singleValue = 12.5 - 0.21 * i;
+                mrc2.children[i].prms.B.singleValue = 1 + i;
+            }
+            mrc2.CalcSerie();
+        });
+        for (let i = 0; i < 3; i++) {
+            it(`Apron #${i} should return same parameters as Macrorugo`, () => {
+                const mr = getMacroRugoRef(i);
+                for (const p of mrc2.children[i].parameterIterator) {
+                    if (p.symbol !== "Q") {
+                        expect(p.v).toBeCloseTo(mr.getParameter(p.symbol).v, 3);
+                    }
+                }
+            });
+            it(`Apron #${i} should return same result as Macrorugo`, () => {
+                const mr = getMacroRugoRef(i);
+                compareTwoResults(mrc2.children[i].result, mr.result, 3);
+            });
+        }
+    });
 });
diff --git a/spec/test_func.ts b/spec/test_func.ts
index 0c321f78..014b58a7 100644
--- a/spec/test_func.ts
+++ b/spec/test_func.ts
@@ -210,6 +210,29 @@ export function checkResult(result: Result, valRef: number, prec?: number) {
     }
 }
 
+/**
+ * Compare the content of two results objects
+ * @param rTest Result to test
+ * @param rRef Result used as reference
+ * @param prec numbers of digits of the needed precision
+ */
+export function compareTwoResults(rTest: Result, rRef: Result, prec: number = Math.max(0, precDigits - 1)) {
+    expect(rTest.ok).toEqual(rRef.ok, `ok should be $rRef.ok`);
+    if (!rTest.ok) { return; }
+    expect(rTest.resultElements.length).toEqual(rRef.resultElements.length, `resultElements.length should be $rRef.ok`);
+    if (rTest.resultElements.length !== rRef.resultElements.length) { return; }
+    for (let i = 0; i < rRef.resultElements.length; i++) {
+        for (const k of Object.keys(rRef.resultElements[i].values)) {
+            expect(rTest.resultElements[i].values[k])
+            .toBeCloseTo(
+                rRef.resultElements[i].values[k],
+                prec,
+                k
+            );
+        }
+    }
+}
+
 export function checkResultConsistency(nub: Nub, r?: Result) {
     if (r === undefined) {
         r = nub.result;
diff --git a/src/macrorugo/macrorugo_compound.ts b/src/macrorugo/macrorugo_compound.ts
index 35dd6038..b91bab5e 100644
--- a/src/macrorugo/macrorugo_compound.ts
+++ b/src/macrorugo/macrorugo_compound.ts
@@ -124,7 +124,7 @@ export class MacrorugoCompound extends MacroRugo implements Observer {
         for (const child of this.children) {
             // Copy common parameters with MacrorugoCompound
             for (const v of ["Ks", "If", "C", "PBD", "PBH", "Cd0"]) {
-                child.getParameter(v).singleValue = this.getParameter(v).v;
+                child.getParameter(v).v = this.getParameter(v).v;
             }
             // Calculate Length and depth from other parameters
             child.prms.L.v = this.prms.DH.v / this.prms.If.v;
diff --git a/src/param/param-definition.ts b/src/param/param-definition.ts
index c5adb6fb..83687c88 100644
--- a/src/param/param-definition.ts
+++ b/src/param/param-definition.ts
@@ -1100,9 +1100,9 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
      */
     get V(): number {
         if (this.valueMode === ParamValueMode.CALCUL) {
-            if (this.originNub.result !== undefined) {
-                if (this.originNub.result.resultElement.ok) {
-                    return this.originNub.result.vCalc;
+            if (this.parentNub.result !== undefined) {
+                if (this.parentNub.result.resultElement.ok) {
+                    return this.parentNub.result.vCalc;
                 }
             }
         }
diff --git a/src/structure/parallel_structure.ts b/src/structure/parallel_structure.ts
index f42fee6c..d9c7483e 100644
--- a/src/structure/parallel_structure.ts
+++ b/src/structure/parallel_structure.ts
@@ -129,21 +129,13 @@ export class ParallelStructure extends Nub {
                 }
                 // Pour les caractéristiques des ouvrages
                 const structureIndex = this.getIndexForChild(sVarCalc.uid);
-                this.currentResult = this.CalcStructPrm(structureIndex, sVarCalc.symbol, rInit);
-                if (this.result.ok) {
-                    this._children[structureIndex].getParameter(sVarCalc.symbol).v = this.result.resultElement.vCalc;
-                }
-        }
-        if (this.result.ok) {
-            // Recalcul du débit total pour récupérer les résultats des ouvrages dans les résultats complémentaires
-            const resQtot: Result = this.CalcQ();
-            for (const extraResKey in resQtot.extraResults) {
-                if (resQtot.resultElement.values.hasOwnProperty(extraResKey)) {
-                    this.result.resultElement.addExtraResult(
-                        extraResKey, resQtot.extraResults.resultElement.values[extraResKey]
-                    );
+                const r = this.CalcStructPrm(structureIndex, sVarCalc.symbol, rInit);
+                if (r.ok) {
+                    this.result.symbol = r.symbol;
+                    this.result.vCalc = r.vCalc;
+                } else {
+                    this.currentResult = r;
                 }
-            }
         }
 
         return this.result;
@@ -206,7 +198,9 @@ export class ParallelStructure extends Nub {
         this.structures[index].prms.Q.v = this.prms.Q.v - this.CalcQ(index).vCalc;
 
         // Calcul du paramètre de la structure en calcul
-        return this.structures[index].Calc(symbol, rInit);
+        const r: Result = this.structures[index].Calc(symbol, rInit);
+        this.structures[index].result.values.Q = this.structures[index].prms.Q.v;
+        return r;
     }
 
 }
-- 
GitLab