From 01ddbea5861b5575abf113dc6730fb652fc9db92 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Thu, 12 Mar 2020 11:40:57 +0100
Subject: [PATCH] Fix #376 - generate SectionPram in Bief: manage variating /
 calculated parameters

---
 .../fixedvar-results/var-results.component.ts |   4 +-
 .../calculator.component.ts                   | 175 ++++++++++++++++--
 .../pab-profile-chart.component.ts            |   4 +-
 .../variable-results-selector.component.ts    |   4 +-
 src/app/formulaire/definition/form-pab.ts     |   4 +-
 src/app/results/var-results.ts                |   4 +-
 src/app/util.ts                               |  22 ++-
 7 files changed, 190 insertions(+), 27 deletions(-)

diff --git a/src/app/components/fixedvar-results/var-results.component.ts b/src/app/components/fixedvar-results/var-results.component.ts
index b4f667694..b5a66d774 100644
--- a/src/app/components/fixedvar-results/var-results.component.ts
+++ b/src/app/components/fixedvar-results/var-results.component.ts
@@ -8,7 +8,7 @@ import { I18nService } from "../../services/internationalisation.service";
 import { ResultsComponentDirective } from "./results.component";
 import { DialogLogEntriesDetailsComponent } from "../dialog-log-entries-details/dialog-log-entries-details.component";
 import { AppComponent } from "../../app.component";
-import { longestVarParam } from "../../../app/util";
+import { longestVarNgParam } from "../../../app/util";
 
 @Component({
     selector: "var-results",
@@ -68,7 +68,7 @@ export class VarResultsComponent extends ResultsComponentDirective {
             // C. pre-extract variable parameters values
             const varValues = [];
             // find longest list
-            const lvp = longestVarParam(this._varResults.variatedParameters);
+            const lvp = longestVarNgParam(this._varResults.variatedParameters);
             this.size = lvp.size;
             // get extended values lists for each variable parameter
             for (const v of this._varResults.variatedParameters) {
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index a113c4f6e..073c1fea9 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -2,7 +2,22 @@ import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren,
          QueryList, AfterViewChecked, ElementRef, Inject, forwardRef } from "@angular/core";
 import { ActivatedRoute, Router } from "@angular/router";
 
-import { Observer, Session, Cloisons, Pab, ParamValueMode, CalculatorType, Bief, SectionParametree, acSection, round } from "jalhyd";
+import {
+    Observer,
+    Session,
+    Cloisons,
+    Pab,
+    ParamValueMode,
+    CalculatorType,
+    Bief,
+    SectionParametree,
+    acSection,
+    ParamDefinition,
+    round,
+    VariatedDetails
+} from "jalhyd";
+
+import { longestVarParam } from "../../util";
 
 import { AppComponent } from "../../app.component";
 import { FormulaireService } from "../../services/formulaire.service";
@@ -645,7 +660,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     public get generateSPAmontEnabled(): boolean {
         const bief = (this._formulaire.currentNub as Bief);
         if (bief.prms.Z1 === bief.calculatedParam) {
-            return this.hasResults && bief.result.ok;
+            return this.hasResults && ! bief.result.hasErrorMessages();
         } else {
             return true;
         }
@@ -654,7 +669,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     public get generateSPAvalEnabled(): boolean {
         const bief = (this._formulaire.currentNub as Bief);
         if (bief.prms.Z2 === bief.calculatedParam) {
-            return this.hasResults && bief.result.ok;
+            return this.hasResults && ! bief.result.hasErrorMessages();
         } else {
             return true;
         }
@@ -681,7 +696,10 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
      */
     public generateSPAmont() {
         const bief = (this._formulaire.currentNub as Bief);
-        this.generateSP(round(bief.prms.Z1.singleValue - bief.prms.ZF1.singleValue, 3));
+        this.generateSP(
+            this.generateYValuesForSP(bief.prms.Z1, bief.prms.ZF1),
+            this.generateIfValuesForSP()
+        );
     }
 
     /**
@@ -689,19 +707,150 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
      */
     public generateSPAval() {
         const bief = (this._formulaire.currentNub as Bief);
-        this.generateSP(round(bief.prms.Z2.singleValue - bief.prms.ZF2.singleValue, 3));
+        this.generateSP(
+            this.generateYValuesForSP(bief.prms.Z2, bief.prms.ZF2),
+            this.generateIfValuesForSP()
+        );
+    }
+
+    /**
+     * Génère une liste de valeurs de tirant d'eau pour un couple Z1/ZF1 ou Z2/ZF2 donné, que ces
+     * paramètres soient fixés, variés et/ou calculés
+     * @TODO factoriser avec generateIfValuesForSP() la partie "générer des combinaisons de valeurs depuis N paramètres"
+     * @param Z cote de leau (Z1 ou Z2)
+     * @param ZF cote de fond (ZF1 ou ZF2)
+     */
+    private generateYValuesForSP(Z: ParamDefinition, ZF: ParamDefinition): number | number[] {
+        const bief = (this._formulaire.currentNub as Bief);
+        if (
+            (Z.isCalculated && bief.resultHasMultipleValues())
+            || Z.hasMultipleValues
+            || ZF.hasMultipleValues
+        ) {
+            // find longest values list with standard Nub method
+            const { param, index, size } = longestVarParam(bief.findVariatedParams());
+
+            console.log(`Z: ${Z.symbol}, isCalculated=${Z.isCalculated}, hasMultipleValues=${Z.hasMultipleValues}, count=${Z.count()}`);
+            // tslint:disable-next-line:max-line-length
+            console.log(`ZF: ${ZF.symbol}, isCalculated=${ZF.isCalculated}, hasMultipleValues=${ZF.hasMultipleValues}, count=${ZF.count()}`);
+            console.log(`longest = ${param.symbol}, size = ${size}`);
+
+            // extend values list for Z / Z1 if they vary
+            let ZVals: number[];
+            if (Z.isCalculated) {
+                ZVals = bief.result.getCalculatedValues();
+            } else {
+                if (Z.hasMultipleValues) {
+                    ZVals = Z.getInferredValuesList(size);
+                } else {
+                    ZVals = [ Z.singleValue ];
+                }
+            }
+            let ZFVals: number[];
+            if (ZF.hasMultipleValues) {
+                ZFVals = ZF.getInferredValuesList(size);
+            } else {
+                ZFVals = [ ZF.singleValue ];
+            }
+            console.log("ZVals", ZVals);
+            console.log("ZFVals", ZFVals);
+
+            // calculate Y values
+            const Ys: number[] = [];
+            for (let i = 0; i < size; i++) {
+                // using "%" because one of the two lists might have only 1 value
+                Ys.push(round(ZVals[i % ZVals.length] - ZFVals[i % ZFVals.length], 3));
+            }
+            console.log("Ys", Ys);
+            return Ys;
+
+        } else {
+            // .V returns calculated value or latest .v; no problem since no parameter is varying
+            // @TODO check that .v is not polluted by any DICHO calc ?
+            const Y = round(Z.V - ZF.V, 3);
+            console.log("Y", Y);
+            return Y;
+        }
+    }
+
+    /**
+     * Génère une liste de valeurs de pente en fonction de ZF1, ZF2 et Long,
+     * que ces paramètres soient fixés, variés et/ou calculés
+     */
+    private generateIfValuesForSP(): number | number[] {
+        const bief = (this._formulaire.currentNub as Bief);
+        const ZF1 = bief.prms.ZF1;
+        const ZF2 = bief.prms.ZF2;
+        const Long = bief.prms.Long;
+        if (ZF1.hasMultipleValues || ZF2.hasMultipleValues || Long.hasMultipleValues) {
+            // find longest values list
+            const variated: VariatedDetails[] = [
+                {
+                    param: ZF1,
+                    values: ZF1.paramValues
+                },
+                {
+                    param: ZF2,
+                    values: ZF2.paramValues
+                },
+                {
+                    param: Long,
+                    values: Long.paramValues
+                }
+            ];
+            const { param, index, size } = longestVarParam(variated);
+
+            console.log(`ZF1: hasMultipleValues=${ZF1.hasMultipleValues}, count=${ZF1.count()}`);
+            console.log(`ZF2: hasMultipleValues=${ZF2.hasMultipleValues}, count=${ZF2.count()}`);
+            console.log(`Long: hasMultipleValues=${Long.hasMultipleValues}, count=${Long.count()}`);
+            console.log(`longest = ${param.symbol}, size = ${size}`);
+
+            // extend values list for ZF1 / ZF2 / Long if they vary
+            let ZF1Vals: number[];
+            if (ZF1.hasMultipleValues) {
+                ZF1Vals = ZF1.getInferredValuesList(size);
+            } else {
+                ZF1Vals = [ ZF1.singleValue ];
+            }
+            let ZF2Vals: number[];
+            if (ZF2.hasMultipleValues) {
+                ZF2Vals = ZF2.getInferredValuesList(size);
+            } else {
+                ZF2Vals = [ ZF2.singleValue ];
+            }
+            let LongVals: number[];
+            if (Long.hasMultipleValues) {
+                LongVals = Long.getInferredValuesList(size);
+            } else {
+                LongVals = [ Long.singleValue ];
+            }
+            console.log("ZF1Vals", ZF1Vals);
+            console.log("ZF2Vals", ZF2Vals);
+            console.log("LongVals", LongVals);
+
+            // calculate If values
+            const Ifs: number[] = [];
+            for (let i = 0; i < size; i++) {
+                // using "%" because one of the three lists might have only 1 value
+                Ifs.push(round((ZF1Vals[i % ZF1Vals.length] - ZF2Vals[i % ZF2Vals.length]) / LongVals[i % LongVals.length], 5));
+            }
+            console.log("Ifs", Ifs);
+            return Ifs;
+
+        } else {
+            const If = round((ZF1.singleValue - ZF2.singleValue) / Long.singleValue, 5);
+            console.log("If", If);
+            return If;
+        }
     }
 
     /**
      * Génère une SectionParametree à partir du module Bief en cours
-     * @param Y tirant d'eau
+     * @param Ys tirant(s) d'eau
+     * @param Ifs pente(s)
      */
-    private generateSP(Y: number) {
+    private generateSP(Ys: number | number[], Ifs: number | number[]) {
         const bief = (this._formulaire.currentNub as Bief);
-        const pente = round(
-            (bief.prms.ZF1.singleValue - bief.prms.ZF2.singleValue) / bief.prms.Long.singleValue,
-            5 // ça ou autre chose…
-        );
         const serialisedSection = bief.section.serialise();
         const sectionCopy = Session.getInstance().unserialiseSingleNub(serialisedSection, false).nub;
         const secParam = new SectionParametree(sectionCopy as acSection);
@@ -710,8 +859,8 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         this.formulaireService.createFormulaire(CalculatorType.SectionParametree, secParam)
             .then((f: FormulaireDefinition) => {
                 const sp = (f.currentNub as SectionParametree);
-                sp.section.prms.Y.singleValue = Y;
-                sp.section.prms.If.singleValue = pente;
+                sp.section.prms.Y.setValues(Ys);
+                sp.section.prms.If.setValues(Ifs);
                 // calculate
                 f.doCompute();
                 // go to new SP
diff --git a/src/app/components/pab-profile-chart/pab-profile-chart.component.ts b/src/app/components/pab-profile-chart/pab-profile-chart.component.ts
index 25dd4bfbb..b54fe19b1 100644
--- a/src/app/components/pab-profile-chart/pab-profile-chart.component.ts
+++ b/src/app/components/pab-profile-chart/pab-profile-chart.component.ts
@@ -6,7 +6,7 @@ import { I18nService } from "../../services/internationalisation.service";
 import { ResultsComponentDirective } from "../fixedvar-results/results.component";
 import { PabResults } from "../../results/pab-results";
 import { IYSeries } from "../../results/y-series";
-import { fv, longestVarParam } from "../../util";
+import { fv, longestVarNgParam } from "../../util";
 import { AppComponent } from "../../app.component";
 
 import { CloisonAval, Cloisons, LoiDebit } from "jalhyd";
@@ -132,7 +132,7 @@ export class PabProfileChartComponent extends ResultsComponentDirective {
         if (this._results) {
             this.varValues = [];
             // find longest list
-            const lvp = longestVarParam(this._results.variatedParameters);
+            const lvp = longestVarNgParam(this._results.variatedParameters);
             this.size = lvp.size;
             // get extended values lists for each variable parameter
             for (const v of this._results.variatedParameters) {
diff --git a/src/app/components/variable-results-selector/variable-results-selector.component.ts b/src/app/components/variable-results-selector/variable-results-selector.component.ts
index fd6e26362..af8dfe927 100644
--- a/src/app/components/variable-results-selector/variable-results-selector.component.ts
+++ b/src/app/components/variable-results-selector/variable-results-selector.component.ts
@@ -1,7 +1,7 @@
 import { Component, Output, EventEmitter } from "@angular/core";
 
 import { I18nService } from "../../services/internationalisation.service";
-import { fv, longestVarParam } from "../../util";
+import { fv, longestVarNgParam } from "../../util";
 import { MultiDimensionResults } from "../../results/multidimension-results";
 
 @Component({
@@ -40,7 +40,7 @@ export class VariableResultsSelectorComponent {
             // pre-extract variable parameters values
             this.varValues = [];
             // find longest list
-            const lvp = longestVarParam(this._results.variatedParameters);
+            const lvp = longestVarNgParam(this._results.variatedParameters);
             this.size = lvp.size;
             // get extended values lists for each variable parameter
             for (const v of this._results.variatedParameters) {
diff --git a/src/app/formulaire/definition/form-pab.ts b/src/app/formulaire/definition/form-pab.ts
index f4ba51958..723d0713a 100644
--- a/src/app/formulaire/definition/form-pab.ts
+++ b/src/app/formulaire/definition/form-pab.ts
@@ -3,7 +3,7 @@ import { Pab, Result } from "jalhyd";
 import { FormulaireDefinition } from "./form-definition";
 import { PabResults } from "../../results/pab-results";
 import { NgParameter } from "../elements/ngparam";
-import { longestVarParam } from "../../util";
+import { longestVarNgParam } from "../../util";
 import { CalculatorResults } from "../../results/calculator-results";
 
 /**
@@ -55,7 +55,7 @@ export class FormulairePab extends FormulaireDefinition {
         pabr.Z2 = [];
         if (varParams.length > 0) {
             // find longest list
-            const lvp = longestVarParam(varParams);
+            const lvp = longestVarNgParam(varParams);
             const longest = lvp.size;
             // get extended values lists for Z2
             if (pab.prms.Z2.hasMultipleValues) {
diff --git a/src/app/results/var-results.ts b/src/app/results/var-results.ts
index 79dbca9fd..66642d8f8 100644
--- a/src/app/results/var-results.ts
+++ b/src/app/results/var-results.ts
@@ -8,7 +8,7 @@ import { ChartType } from "./chart-type";
 
 import { sprintf } from "sprintf-js";
 
-import { longestVarParam } from "../util";
+import { longestVarNgParam } from "../util";
 
 export class VarResults extends CalculatedParamResults implements PlottableData {
     /**
@@ -335,7 +335,7 @@ export class VarResults extends CalculatedParamResults implements PlottableData
         });
 
         // liste la plus longue
-        const lvp = longestVarParam(this._variatedParams);
+        const lvp = longestVarNgParam(this._variatedParams);
         this.size = lvp.size;
         this.longest = lvp.index;
 
diff --git a/src/app/util.ts b/src/app/util.ts
index f4a315c75..5339bad8d 100644
--- a/src/app/util.ts
+++ b/src/app/util.ts
@@ -1,7 +1,7 @@
 import { NgParameter } from "./formulaire/elements/ngparam";
 import { ServiceFactory } from "./services/service-factory";
 
-import { formattedValue, Nub, ParamDefinition, ParamValues } from "jalhyd";
+import { formattedValue, Nub, VariatedDetails, ParamDefinition } from "jalhyd";
 
 export function logObject(obj: {}, m?: string) {
     // évite le message "Value below was evaluated just now" dans le debugger de Chrome
@@ -47,14 +47,28 @@ export function decodeHtml(html: string): string {
  * values, its index in the list, and the number of values it contains
  * @param varParams
  */
-export function longestVarParam(varParams: NgParameter[]): { param: NgParameter, index: number, size: number } {
-    const variated: { param: ParamDefinition, values: ParamValues }[] = [];
+export function longestVarNgParam(varParams: NgParameter[]): { param: NgParameter, index: number, size: number } {
+    const variated: VariatedDetails[] = [];
     for (const vp of varParams) {
         variated.push({
             param: vp.paramDefinition,
             values: vp.paramDefinition.paramValues
         });
     }
+    const { param, index, size } = longestVarParam(variated);
+    return {
+        param: varParams[index],
+        index,
+        size
+    };
+}
+
+/**
+ * Given a list of variated ParamDefinition, returns the parameter having the most
+ * values, its index in the list, and the number of values it contains
+ * @param variated
+ */
+export function longestVarParam(variated: VariatedDetails[]): { param: ParamDefinition, index: number, size: number } {
     const { size, longest, minLinkedResultParam } = Nub.findVariatedSize(variated);
     let realSize = size;
     // if at least one linked variated result was found
@@ -66,7 +80,7 @@ export function longestVarParam(varParams: NgParameter[]): { param: NgParameter,
         }
     }
     return {
-        param: varParams[longest],
+        param: longest !== undefined ? variated[longest].param : undefined,
         index: longest,
         size: realSize
     };
-- 
GitLab