From 4ae039bce797dce1d84c41615d4c58bb28089129 Mon Sep 17 00:00:00 2001
From: "mathias.chouet" <mathias.chouet@irstea.fr>
Date: Wed, 28 Aug 2019 16:24:16 +0200
Subject: [PATCH] MacrorugoCompound: results table

---
 src/app/app.module.ts                         |   8 +-
 .../calculator-results.component.html         |   1 +
 .../calculator-results.component.ts           |  10 +
 .../calculator.component.html                 |  26 +-
 .../calculator.component.ts                   |  16 +-
 ...rugo-compound-results-table.component.html |  32 ++
 ...rugo-compound-results-table.component.scss |  66 ++++
 ...rorugo-compound-results-table.component.ts | 117 +++++++
 .../macrorugo-compound-results.component.html |  25 ++
 .../macrorugo-compound-results.component.scss |   4 +
 .../macrorugo-compound-results.component.ts   | 322 ++++++++++++++++++
 .../pab-results/pab-results.component.html    |   4 +-
 .../pab-results/pab-results.component.ts      |  16 +-
 .../variable-results-selector.component.html} |   2 +-
 .../variable-results-selector.component.scss} |   0
 .../variable-results-selector.component.ts}   |  34 +-
 .../concrete/form-macrorugo-compound.ts       |   8 +-
 .../definition/form-compute-fixedvar.ts       |  23 --
 .../form-compute-macrorugo-compound.ts        |  42 +++
 .../formulaire/definition/form-compute-pab.ts |  17 +-
 src/app/formulaire/definition/form-compute.ts |  24 ++
 .../form-result-macrorugo-compound.ts         |  33 ++
 src/app/results/macrorugo-compound-results.ts | 114 +++++++
 src/app/results/multidimension-results.ts     |  13 +
 src/app/results/pab-results.ts                |  10 +-
 .../plottable-macrorugo-compound-results.ts   | 141 ++++++++
 src/locale/messages.en.json                   |   1 +
 src/locale/messages.fr.json                   |   1 +
 28 files changed, 1016 insertions(+), 94 deletions(-)
 create mode 100644 src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.html
 create mode 100644 src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.scss
 create mode 100644 src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.ts
 create mode 100644 src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.html
 create mode 100644 src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.scss
 create mode 100644 src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.ts
 rename src/app/components/{pab-results/pab-variable-results-selector.component.html => variable-results-selector/variable-results-selector.component.html} (76%)
 rename src/app/components/{pab-results/pab-variable-results-selector.component.scss => variable-results-selector/variable-results-selector.component.scss} (100%)
 rename src/app/components/{pab-results/pab-variable-results-selector.component.ts => variable-results-selector/variable-results-selector.component.ts} (71%)
 create mode 100644 src/app/formulaire/definition/form-compute-macrorugo-compound.ts
 create mode 100644 src/app/formulaire/definition/form-result-macrorugo-compound.ts
 create mode 100644 src/app/results/macrorugo-compound-results.ts
 create mode 100644 src/app/results/multidimension-results.ts
 create mode 100644 src/app/results/plottable-macrorugo-compound-results.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 26158718f..9b3247c78 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -85,8 +85,10 @@ import { ParamLinkComponent } from "./components/param-link/param-link.component
 import { SelectModelFieldLineComponent } from "./components/select-model-field-line/select-model-field-line.component";
 import { PabProfileGraphComponent } from "./components/pab-profile-graph/pab-profile-graph.component";
 import { PabTableComponent } from "./components/pab-table/pab-table.component";
-import { PabVariableResultsSelectorComponent } from "./components/pab-results/pab-variable-results-selector.component";
+import { VariableResultsSelectorComponent } from "./components/variable-results-selector/variable-results-selector.component";
 import { QuicknavComponent } from "./components/quicknav/quicknav.component";
+import { MacrorugoCompoundResultsTableComponent } from "./components/macrorugo-compound-results/macrorugo-compound-results-table.component";
+import { MacrorugoCompoundResultsComponent } from "./components/macrorugo-compound-results/macrorugo-compound-results.component";
 
 import { DialogConfirmEmptySessionComponent } from "./components/dialog-confirm-empty-session/dialog-confirm-empty-session.component";
 import { DialogConfirmCloseCalcComponent } from "./components/dialog-confirm-close-calc/dialog-confirm-close-calc.component";
@@ -196,7 +198,9 @@ const appRoutes: Routes = [
     PabResultsComponent,
     PabResultsTableComponent,
     PabTableComponent,
-    PabVariableResultsSelectorComponent,
+    VariableResultsSelectorComponent,
+    MacrorugoCompoundResultsComponent,
+    MacrorugoCompoundResultsTableComponent,
     ParamComputedComponent,
     ParamFieldLineComponent,
     ParamLinkComponent,
diff --git a/src/app/components/calculator-results/calculator-results.component.html b/src/app/components/calculator-results/calculator-results.component.html
index be99ed81b..93748fe04 100644
--- a/src/app/components/calculator-results/calculator-results.component.html
+++ b/src/app/components/calculator-results/calculator-results.component.html
@@ -2,5 +2,6 @@
     <section-results></section-results>
     <remous-results></remous-results>
     <pab-results></pab-results>
+    <macrorugo-compound-results></macrorugo-compound-results>
     <fixedvar-results></fixedvar-results>
 </div>
diff --git a/src/app/components/calculator-results/calculator-results.component.ts b/src/app/components/calculator-results/calculator-results.component.ts
index cf74d342c..9dd4576c5 100644
--- a/src/app/components/calculator-results/calculator-results.component.ts
+++ b/src/app/components/calculator-results/calculator-results.component.ts
@@ -4,6 +4,7 @@ import { FixedVarResultsComponent } from "../../components/fixedvar-results/fixe
 import { SectionResultsComponent } from "../../components/section-results/section-results.component";
 import { RemousResultsComponent } from "../../components/remous-results/remous-results.component";
 import { PabResultsComponent } from "../../components/pab-results/pab-results.component";
+import { MacrorugoCompoundResultsComponent } from "../macrorugo-compound-results/macrorugo-compound-results.component";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
 
 @Component({
@@ -37,6 +38,12 @@ export class CalculatorResultsComponent implements AfterViewChecked {
     @ViewChild(PabResultsComponent, { static: true })
     private pabResultsComponent: PabResultsComponent;
 
+    /**
+     * composant d'affichage des résultats des passes à macrorugosités complexes
+     */
+    @ViewChild(MacrorugoCompoundResultsComponent, { static: true })
+    private mrcResultsComponent: PabResultsComponent;
+
     /**
      * événement émis à la fin du dessin de la vue
      */
@@ -50,11 +57,13 @@ export class CalculatorResultsComponent implements AfterViewChecked {
             this.sectionResultsComponent.results = undefined;
             this.remousResultsComponent.results = undefined;
             this.pabResultsComponent.results = undefined;
+            this.mrcResultsComponent.results = undefined;
         } else {
             this.fixedVarResultsComponent.results = f.results;
             this.sectionResultsComponent.results = f.results;
             this.remousResultsComponent.results = f.results;
             this.pabResultsComponent.results = f.results;
+            this.mrcResultsComponent.results = f.results;
         }
     }
 
@@ -63,6 +72,7 @@ export class CalculatorResultsComponent implements AfterViewChecked {
         this.sectionResultsComponent.updateView();
         this.remousResultsComponent.updateView();
         this.pabResultsComponent.updateView();
+        this.mrcResultsComponent.updateView();
     }
 
     public ngAfterViewChecked() {
diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html
index 3fff1f268..7770790c0 100644
--- a/src/app/components/generic-calculator/calculator.component.html
+++ b/src/app/components/generic-calculator/calculator.component.html
@@ -29,7 +29,7 @@
 
     </mat-card-header>
 
-    <quicknav [fxHide.gt-sm]="! isPAB" [items]="quicknavItems" [currentItem]="'input'" [align]="'left'"></quicknav>
+    <quicknav [fxHide.gt-sm]="! isWide" [items]="quicknavItems" [currentItem]="'input'" [align]="'left'"></quicknav>
 
     <form>
 
@@ -39,15 +39,15 @@
             <calc-name id="calculator-name" [title]="uitextCalculatorName"></calc-name>
 
             <div id="calc-cards-container" class="container"
-              [fxLayout]="isPAB ? 'column' : 'row wrap'"
-              [fxLayoutAlign]="isPAB ? 'space-around stretch' : 'space-around start'">
+              [fxLayout]="isWide ? 'column' : 'row wrap'"
+              [fxLayoutAlign]="isWide ? 'space-around stretch' : 'space-around start'">
 
                 <!-- chapitres -->
                 <mat-card id="calc-card-field-sets"
-                  [class.pab-field-sets]="isPAB"
-                  [fxFlex.gt-sm]="isPAB ? '1 0 auto' : '1 0 400px'"
-                  [fxFlex.lt-md]="isPAB ? '1 0 auto' : '1 0 500px'"
-                  [fxFlex.lt-sm]="isPAB ? '1 0 auto' : '1 0 300px'">
+                  [class.pab-field-sets]="isWide"
+                  [fxFlex.gt-sm]="isWide ? '1 0 auto' : '1 0 400px'"
+                  [fxFlex.lt-md]="isWide ? '1 0 auto' : '1 0 500px'"
+                  [fxFlex.lt-sm]="isWide ? '1 0 auto' : '1 0 300px'">
 
                     <ng-template ngFor let-fe [ngForOf]="formElements">
                         <field-set *ngIf="isFieldset(fe)" [style.display]="getElementStyleDisplay(fe.id)" [fieldSet]=fe
@@ -75,16 +75,16 @@
 
                 <!-- résultats -->
                 <mat-card id="calc-card-results"
-                  [class.pab-results]="isPAB"
-                  [fxFlex.gt-sm]="isPAB ? '1 0 auto' : '1 0 400px'"
-                  [fxFlex.lt-md]="isPAB ? '1 0 auto' : '1 0 500px'"
-                  [fxFlex.lt-sm]="isPAB ? '1 0 auto' : '1 0 300px'">
+                  [class.pab-results]="isWide"
+                  [fxFlex.gt-sm]="isWide ? '1 0 auto' : '1 0 400px'"
+                  [fxFlex.lt-md]="isWide ? '1 0 auto' : '1 0 500px'"
+                  [fxFlex.lt-sm]="isWide ? '1 0 auto' : '1 0 300px'">
 
                     <div id="fake-results-anchor"></div>
 
-                    <quicknav [ngClass.lt-xs]="'extraSmall'" [fxHide.gt-sm]="! isPAB" [items]="quicknavItems" [currentItem]="'results'" [align]="'left'"></quicknav>
+                    <quicknav [ngClass.lt-xs]="'extraSmall'" [fxHide.gt-sm]="! isWide" [items]="quicknavItems" [currentItem]="'results'" [align]="'left'"></quicknav>
 
-                    <mat-card-header *ngIf="! isPAB" [fxHide.lt-md]="! isPAB">
+                    <mat-card-header *ngIf="! isWide" [fxHide.lt-md]="! isWide">
                         <mat-card-title>
                             <h1 [innerHTML]="uitextResultsTitle"></h1>
                         </mat-card-title>
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index a686bdf08..19e8926e4 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -205,7 +205,7 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
 
     public get quicknavItems() {
         const elts = [ "input", "results" ];
-        if (this.isPAB && this.hasResults) {
+        if (this.isWide && this.hasResults) {
             elts.push("charts");
         }
         return elts;
@@ -470,6 +470,11 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
     }
 
     // for "one wide column" layout
+    public get isWide() {
+        return (this.isPAB || this.isMRC);
+    }
+
+    // true if current Nub is PAB
     public get isPAB() {
         return (
             this._formulaire
@@ -478,6 +483,15 @@ export class GenericCalculatorComponent extends BaseComponent implements OnInit,
         );
     }
 
+    // true if current Nub is MacroRugoCompound
+    public get isMRC() {
+        return (
+            this._formulaire
+            && this._formulaire.currentNub
+            && this._formulaire.currentNub.calcType === CalculatorType.MacroRugoCompound
+        );
+    }
+
     // for "generate PAB" button
     public get isPABCloisons() {
         return (
diff --git a/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.html b/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.html
new file mode 100644
index 000000000..1e1df2713
--- /dev/null
+++ b/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.html
@@ -0,0 +1,32 @@
+<!-- @TODO copied from var-results.component.html > merge ?-->
+<div class="macrorugo-compound-results-table-container" #mrcResultsTable fxLayout="row wrap" fxLayoutAlign="center center">
+    <div fxFlex="1 1 100%">
+        <div class="macrorugo-compound-results-table-buttons">
+            <button mat-icon-button (click)="exportAsSpreadsheet()" [title]="uitextExportAsSpreadsheet">
+                <mat-icon color="primary">file_download</mat-icon>
+            </button>
+            <button mat-icon-button *ngIf="! isFullscreen" (click)="setFullscreen(mrcResultsTable)" [title]="uitextEnterFSTitle">
+                <mat-icon color="primary" class="scaled12">fullscreen</mat-icon>
+            </button>
+            <button mat-icon-button *ngIf="isFullscreen" (click)="exitFullscreen()" [title]="uitextExitFSTitle">
+                <mat-icon color="primary" class="scaled12">fullscreen_exit</mat-icon>
+            </button>
+        </div>
+
+        <div class="macrorugo-compound-results-table-scrollable-container" [ngClass]="{'full-height': isFullscreen}">
+            <!-- scrollable -->
+            <div class="macrorugo-compound-results-table-inner-container" #tableContainer>
+
+                <table mat-table [dataSource]="dataSet">
+                    <ng-container *ngFor="let h of headers; let i = index" [matColumnDef]="h">
+                        <th mat-header-cell *matHeaderCellDef>{{ h }}</th>
+                        <td mat-cell *matCellDef="let element" [innerHTML]="element[i]"></td>
+                    </ng-container>
+
+                    <tr mat-header-row *matHeaderRowDef="headers"></tr>
+                    <tr mat-row *matRowDef="let row; columns: headers;"></tr>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
diff --git a/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.scss b/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.scss
new file mode 100644
index 000000000..14c20aa01
--- /dev/null
+++ b/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.scss
@@ -0,0 +1,66 @@
+:host {
+    display: block;
+    margin-bottom: 1.5em;
+}
+
+.macrorugo-compound-results-table-container {
+    background-color: white;
+}
+
+.macrorugo-compound-results-table-buttons {
+    padding-right: 4px;
+    padding-top: 4px;
+    text-align: right;
+    background-color: white;
+
+    button {
+        margin-left: 3px;
+        width: auto;
+
+        mat-icon {
+            &.scaled12 {
+                transform: scale(1.2)
+            }
+        }
+    }
+}
+
+.macrorugo-compound-results-table-scrollable-container {
+    overflow-x: scroll;
+    border: solid #ccc 1px;
+
+    &.full-height {
+        height: calc(100vh - 40px); // rend le mode plein-écran scrollable verticalement, sinon ça dépasse
+    }
+}
+
+table.mat-table {
+
+    .mat-header-row {
+        height: 40px;
+    }
+
+    .mat-row {
+        height: 32px;
+
+        &:nth-child(odd) {
+            background-color: #f4f4f4;
+        }
+    }
+
+    ::ng-deep .mat-cell {
+        padding: 5px;
+
+        .inner-cell-line {
+            display: block;
+            white-space: nowrap;
+            line-height: 20px;
+        }
+    }
+
+    ::ng-deep .mat-header-cell {
+        font-size: 1em;
+        color: black;
+        padding: 5px;
+    }
+}
diff --git a/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.ts b/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.ts
new file mode 100644
index 000000000..9102984c0
--- /dev/null
+++ b/src/app/components/macrorugo-compound-results/macrorugo-compound-results-table.component.ts
@@ -0,0 +1,117 @@
+import { Component, ViewChild, ElementRef } from "@angular/core";
+
+import { MacroRugo } from "jalhyd";
+
+import * as XLSX from "xlsx";
+
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { ResultsComponent } from "../fixedvar-results/results.component";
+import { MacrorugoCompoundResults } from "../../results/macrorugo-compound-results";
+
+@Component({
+    selector: "macrorugo-compound-results-table",
+    templateUrl: "./macrorugo-compound-results-table.component.html",
+    styleUrls: [
+        "./macrorugo-compound-results-table.component.scss"
+    ]
+})
+export class MacrorugoCompoundResultsTableComponent extends ResultsComponent {
+
+    /** résultats non mis en forme */
+    private _mrcResults: MacrorugoCompoundResults;
+
+    /** entêtes des colonnes */
+    private _headers: string[];
+
+    /** résultats mis en forme */
+    private _dataSet: any[];
+
+    @ViewChild("tableContainer", { static: false })
+    table: ElementRef;
+
+    constructor(
+        protected appSetupService: ApplicationSetupService,
+        protected intlService: I18nService
+    ) {
+        super();
+    }
+
+    public set results(r: MacrorugoCompoundResults) {
+        this._mrcResults = r;
+
+        this._dataSet = [];
+        if (
+            this._mrcResults
+            && this._mrcResults.childrenResults
+            && this._mrcResults.childrenResults.length > 0
+            && ! this._mrcResults.hasOnlyErrors()
+        ) {
+            const pr = this._mrcResults;
+            const nDigits = this.appSetupService.displayDigits;
+            // when a parameter is variating, index of the variating parameter
+            // values to build the data from
+            const vi = pr.variableIndex;
+
+            // refresh headers here if language changed
+            this._headers = pr.headers;
+
+            // lines 1 - n-1
+            for (let i = 0; i < pr.childrenResults.length; i++) {
+                if (
+                    pr.childrenResults[i].resultElements[vi].vCalc
+                ) {
+                    const res = pr.childrenResults[i].resultElements[vi].values;
+                    const nub = (pr.childrenResults[i].sourceNub as MacroRugo);
+                    this._dataSet.push([
+                        i + 1, // n° radier
+                        nub.prms.ZF1.singleValue.toFixed(nDigits), // @TODO what if ZF1 or B varies ?
+                        nub.prms.B.singleValue.toFixed(nDigits),
+                        res.Q.toFixed(nDigits),
+                        res.ZF2.toFixed(nDigits),
+                        res.Vdeb.toFixed(nDigits),
+                        res.Fr.toFixed(nDigits),
+                        res.Vmax.toFixed(nDigits),
+                        res.PV.toFixed(nDigits),
+                        this.intlService.localizeText("INFO_ENUM_MACRORUGOFLOWTYPE_" + res.ENUM_MacroRugoFlowType),
+                        res.Q_GuideTech.toFixed(nDigits),
+                        (res.V_GuideTech !== undefined ? res.V_GuideTech.toFixed(nDigits) : "-"),
+                        res.xCenter.toFixed(nDigits)
+                    ]);
+                }
+            }
+        }
+    }
+
+    public get headers() {
+        return this._headers;
+    }
+
+    /**
+     * Returns a combination of parameters and results for mat-table
+     */
+    public get dataSet() {
+        return this._dataSet;
+    }
+
+    public exportAsSpreadsheet() {
+        // automagic <table> extraction
+        const ws: XLSX.WorkSheet = XLSX.utils.table_to_sheet(this.table.nativeElement);
+        const wb: XLSX.WorkBook = XLSX.utils.book_new();
+        XLSX.utils.book_append_sheet(wb, ws, "default");
+        // save and download
+        XLSX.writeFile(wb, "MacrorugoCompoundResults.xlsx");
+    }
+
+    public get uitextExportAsSpreadsheet() {
+        return this.intlService.localizeText("INFO_RESULTS_EXPORT_AS_SPREADSHEET");
+    }
+
+    public get uitextEnterFSTitle() {
+        return this.intlService.localizeText("INFO_GRAPH_BUTTON_TITLE_ENTER_FS");
+    }
+
+    public get uitextExitFSTitle() {
+        return this.intlService.localizeText("INFO_GRAPH_BUTTON_TITLE_EXIT_FS");
+    }
+}
diff --git a/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.html b/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.html
new file mode 100644
index 000000000..9d29b23ea
--- /dev/null
+++ b/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.html
@@ -0,0 +1,25 @@
+<div class="container">
+
+    <log #generalLog [logTitle]="uitextGeneralLogTitle">log général</log>
+
+    <variable-results-selector [results]="mrcResults" (indexChange)="variableIndexChanged()">
+    </variable-results-selector>
+
+    <log #iterationLog></log>
+
+    <div>
+        <!-- tableau de résultats -->
+        <macrorugo-compound-results-table *ngIf="hasDisplayableResults" [results]="mrcResults"></macrorugo-compound-results-table>
+    </div>
+
+    <quicknav *ngIf="hasDisplayableResults" [items]="[ 'input', 'results', 'charts' ]"
+        [currentItem]="'charts'" [align]="'left'"></quicknav>
+
+    <div id="macrorugo-compound-graphs-container" class="container" fxLayout="row wrap" fxLayoutAlign="space-around start">
+        <!-- <pab-profile-graph *ngIf="hasDisplayableResults" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
+        </pab-profile-graph> -->
+        <!-- <results-graph *ngIf="hasDisplayableResults" fxFlex.gt-xs="1 0 400px" fxFlex.lt-sm="1 0 300px">
+        </results-graph> -->
+    </div>
+
+</div>
diff --git a/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.scss b/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.scss
new file mode 100644
index 000000000..6f5eb10d5
--- /dev/null
+++ b/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.scss
@@ -0,0 +1,4 @@
+results-graph {
+    margin-left: 1em;
+    margin-right: 1em;
+}
diff --git a/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.ts b/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.ts
new file mode 100644
index 000000000..db564243e
--- /dev/null
+++ b/src/app/components/macrorugo-compound-results/macrorugo-compound-results.component.ts
@@ -0,0 +1,322 @@
+import { Component, ViewChild, DoCheck } from "@angular/core";
+
+import { Result, cLog, Message, MessageCode, MessageSeverity } from "jalhyd";
+
+import { LogComponent } from "../../components/log/log.component";
+import { CalculatorResults } from "../../results/calculator-results";
+import { NgParameter } from "../../formulaire/ngparam";
+import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
+import { PlottableData } from "../../results/plottable-data";
+import { ResultsGraphComponent } from "../results-graph/results-graph.component";
+import { I18nService } from "../../services/internationalisation/internationalisation.service";
+import { VariableResultsSelectorComponent } from "../variable-results-selector/variable-results-selector.component";
+import { MacrorugoCompoundResultsTableComponent } from "./macrorugo-compound-results-table.component";
+import { MacrorugoCompoundResults } from "../../results/macrorugo-compound-results";
+import { PlottableMacrorugoCompoundResults } from "../../results/plottable-macrorugo-compound-results";
+
+@Component({
+    selector: "macrorugo-compound-results",
+    templateUrl: "./macrorugo-compound-results.component.html",
+    styleUrls: [
+        "./macrorugo-compound-results.component.scss"
+    ]
+})
+export class MacrorugoCompoundResultsComponent implements DoCheck {
+
+    /** résultats non mis en forme */
+    private _mrcResults: MacrorugoCompoundResults;
+
+    /** résultats mis en forme pour le graphique de données (classique) */
+    private _plottableResults: PlottableMacrorugoCompoundResults;
+
+    /** true si les résultats doiventt être remis à jour */
+    private _doUpdate = false;
+
+    @ViewChild(MacrorugoCompoundResultsTableComponent, { static: false })
+    private mrcResultsTableComponent: MacrorugoCompoundResultsTableComponent;
+
+    @ViewChild(VariableResultsSelectorComponent, { static: false })
+    private variableResultsSelectorComponent: VariableResultsSelectorComponent;
+
+    @ViewChild("generalLog", { static: false })
+    private generalLogComponent: LogComponent;
+
+    @ViewChild("iterationLog", { static: false })
+    private iterationLogComponent: LogComponent;
+
+    @ViewChild(ResultsGraphComponent, { static: false })
+    private resultsGraphComponent: ResultsGraphComponent;
+
+    constructor(
+        private appSetupService: ApplicationSetupService,
+        private i18nService: I18nService,
+    ) {
+        this._plottableResults = new PlottableMacrorugoCompoundResults();
+    }
+
+    public set results(rs: CalculatorResults[]) {
+        this._mrcResults = undefined;
+        if (rs.length > 0 && rs[0] instanceof MacrorugoCompoundResults) {
+            this._mrcResults = rs[0] as MacrorugoCompoundResults;
+        }
+        this.updateView();
+    }
+
+    /**
+     * update results table and chart when the variable index changed (event sent by
+     * VariableResultsSelectorComponent); variable index is already set in
+     * mrcResults at this time
+     */
+    public variableIndexChanged() {
+        this.updateView();
+    }
+
+    public updateView() {
+        if (this.iterationLogComponent) {
+            this.iterationLogComponent.log = undefined;
+        }
+        if (this.generalLogComponent) {
+            this.generalLogComponent.log = undefined;
+        }
+        if (this.mrcResultsTableComponent) {
+            this.mrcResultsTableComponent.results = undefined;
+        }
+        if (this.variableResultsSelectorComponent) {
+            this.variableResultsSelectorComponent.results = undefined;
+        }
+        if (this.resultsGraphComponent) {
+            this.resultsGraphComponent.results = undefined;
+        }
+        // set _doUpdate flag so that results are rebuilt on the next Angular display cycle
+        this._doUpdate = false;
+        if (this._mrcResults !== undefined) {
+            this._doUpdate = this._doUpdate || this._mrcResults.hasResults || this._mrcResults.hasLog;
+        }
+    }
+
+    public ngDoCheck() {
+        if (this._doUpdate) {
+            this._doUpdate = !this.updateResults();
+        }
+    }
+
+    private mergeGlobalLog(result: Result, log: cLog) {
+        if (result) {
+            if (result.hasGlobalLog()) {
+                log.addLog(result.globalLog);
+            }
+            // if no parameter is varying, 1st element log is considered "global"
+            if (this.mrcResults.variatedParameters.length === 0) {
+                if (result.hasResultElements() && result.resultElement.hasLog()) {
+                    log.addLog(result.log);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the number of errors, warnings, infos among children logs
+     */
+    private logStats(): any {
+        const ret = {
+            info: 0,
+            warning: 0,
+            error: 0
+        };
+        if (this._mrcResults.result && this._mrcResults.result.hasLog()) {
+            for (const re of this._mrcResults.result.resultElements) {
+                if (re.hasLog()) {
+                    for (const m of re.log.messages) {
+                        const s = m.getSeverity();
+                        switch (s) {
+                            case MessageSeverity.INFO:
+                                ret.info ++;
+                                break;
+                            case MessageSeverity.WARNING:
+                                ret.warning ++;
+                                break;
+                            case MessageSeverity.ERROR:
+                                ret.error ++;
+                                break;
+                        }
+                    }
+                }
+            }
+        }
+        for (const cr of this._mrcResults.childrenResults) {
+            if (cr && cr.hasLog()) {
+                for (const re of cr.resultElements) {
+                    if (re.hasLog()) {
+                        for (const m of re.log.messages) {
+                            const s = m.getSeverity();
+                            switch (s) {
+                                case MessageSeverity.INFO:
+                                    ret.info ++;
+                                    break;
+                                case MessageSeverity.WARNING:
+                                    ret.warning ++;
+                                    break;
+                                case MessageSeverity.ERROR:
+                                    ret.error ++;
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return ret;
+    }
+
+    /*
+     * Retourne les logs à afficher dans le composant de log global, au dessus
+     * du sélecteur d'itération : messages globaux et / ou résumé des messages
+     * spécifiques à chaque ResultElement
+     */
+    private get globalLog(): cLog {
+        const l = new cLog();
+        if (this._mrcResults && this.mrcResults.variatedParameters.length > 0) {
+            this.mergeGlobalLog(this._mrcResults.result, l);
+            // un problème avec la MRC en général / les cloisons, à une étape quelconque ?
+            if (
+                (this.mrcResults.hasLog)
+                && l.messages.length === 0 // existing global messages make generic message below useless
+            ) {
+                const logStats = this.logStats();
+                const m = new Message(MessageCode.WARNING_PROBLEMS_ENCOUNTERED);
+                m.extraVar.info = "" + logStats.info; // to avoid displaying fixed number of digits
+                m.extraVar.warning = "" + logStats.warning;
+                m.extraVar.error = "" + logStats.error;
+                l.add(m);
+                // l.add(new Message(MessageCode.WARNING_PROBLEMS_ENCOUNTERED));
+            }
+        } // sinon pas de log global (aucun paramètre ne varie)
+        return l;
+    }
+
+    /**
+     * Retourne les logs à afficher dans le composant de log global, au dessus
+     * du sélecteur d'itération : messages globaux et / ou résumé des messages
+     * spécifiques à chaque ResultElement
+     */
+    private get iterationLog(): cLog {
+        const l = new cLog();
+        if (this._mrcResults) {
+            if (this.mrcResults.variatedParameters.length > 0) {
+                // A. si un paramètre varie
+                const vi = this._mrcResults.variableIndex;
+                // log de la MRC pour l'itération en cours
+                if (
+                    this._mrcResults.result
+                    && this._mrcResults.result.hasResultElements()
+                    && this._mrcResults.result.resultElements[vi]
+                    && this._mrcResults.result.resultElements[vi].hasLog()
+                ) {
+                    l.addLog(this._mrcResults.result.resultElements[vi].log);
+                }
+                // logs des enfants pour l'itération en cours
+                for (const cr of this._mrcResults.childrenResults) {
+                    if (cr && cr.hasResultElements() && cr.resultElements[vi].hasLog()) {
+                        l.addLog(cr.resultElements[vi].log);
+                    }
+                }
+            } else {
+                // B. si aucun paramètre ne varie
+                this.mergeGlobalLog(this._mrcResults.result, l); // faut bien mettre le log global quelque part
+                // logs des enfants
+                for (const cr of this._mrcResults.childrenResults) {
+                    if (cr && cr.hasResultElements() && cr.resultElement.hasLog()) {
+                        l.addLog(cr.resultElement.log);
+                    }
+                }
+            }
+        }
+        return l;
+    }
+
+    /**
+     * met à jour l'affichage des résultats
+     * @returns true si les résultats ont pu être mis à jour
+     */
+    private updateResults() {
+        let mrcUpdated: boolean;
+        let resultsGraphUpdated: boolean;
+        let profileGraphUpdated: boolean;
+        let selectorUpdated: boolean;
+
+        // results or not, there might be a log
+        const logUpdated = (this.iterationLogComponent !== undefined || this.generalLogComponent !== undefined); // gne ?
+        if (logUpdated) {
+            // order of logs is important !
+            this.iterationLogComponent.log = this.iterationLog;
+            this.generalLogComponent.log = this.globalLog;
+        }
+
+        console.log("update ====>", this._mrcResults);
+        if (this.hasResults) {
+            mrcUpdated = this.mrcResultsTableComponent !== undefined;
+            if (mrcUpdated) {
+                this.mrcResultsTableComponent.results = this._mrcResults;
+            }
+            selectorUpdated = this.variableResultsSelectorComponent !== undefined;
+            if (selectorUpdated) {
+                this.variableResultsSelectorComponent.results = this._mrcResults;
+            }
+            resultsGraphUpdated = this.resultsGraphComponent !== undefined;
+            if (resultsGraphUpdated) {
+                this.resultsGraphComponent.results = this.plottableResults;
+                this.resultsGraphComponent.updateView();
+            }
+        } else {
+            mrcUpdated = true;
+            resultsGraphUpdated = true;
+            profileGraphUpdated = true;
+            selectorUpdated = true;
+        }
+
+        return mrcUpdated && logUpdated && resultsGraphUpdated && profileGraphUpdated && selectorUpdated;
+    }
+
+    public get mrcResults() {
+        return this._mrcResults;
+    }
+
+    public formattedLabel(p: NgParameter): string {
+        return CalculatorResults.paramLabel(p, false);
+    }
+
+    public formattedValue(p: NgParameter): string {
+        const nDigits = this.appSetupService.displayDigits;
+        return p.getValue().toFixed(nDigits);
+    }
+
+    public get hasResults(): boolean {
+        return this._mrcResults && this._mrcResults.hasResults;
+    }
+
+    public get hasDisplayableResults(): boolean {
+        let ret = this._mrcResults && this._mrcResults.hasResults;
+        if (
+            this._mrcResults
+            && this._mrcResults.variatedParameters
+            && this._mrcResults.variatedParameters.length > 0
+        ) {
+            ret = ret
+            && this._mrcResults.variableIndex !== undefined
+            && this._mrcResults.result.resultElements[this._mrcResults.variableIndex] !== undefined
+            && this._mrcResults.result.resultElements[this._mrcResults.variableIndex].ok;
+        }
+        return ret;
+    }
+
+    public get uitextGeneralLogTitle(): string {
+        return this.i18nService.localizeText("INFO_TITREJOURNAL_GLOBAL");
+    }
+
+    /** builds a set of PlottableData from MacrorugoCompoundResults, to feed the graph */
+    protected get plottableResults(): PlottableData {
+        this._plottableResults.setMrcResults(this.mrcResults);
+        return this._plottableResults;
+    }
+
+}
diff --git a/src/app/components/pab-results/pab-results.component.html b/src/app/components/pab-results/pab-results.component.html
index 2e31dc126..d601347bd 100644
--- a/src/app/components/pab-results/pab-results.component.html
+++ b/src/app/components/pab-results/pab-results.component.html
@@ -2,8 +2,8 @@
 
     <log #generalLog [logTitle]="uitextGeneralLogTitle">log général</log>
 
-    <pab-variable-results-selector [results]="pabResults" (indexChange)="variableIndexChanged()">
-    </pab-variable-results-selector>
+    <variable-results-selector [results]="pabResults" (indexChange)="variableIndexChanged()">
+    </variable-results-selector>
 
     <log #iterationLog></log>
 
diff --git a/src/app/components/pab-results/pab-results.component.ts b/src/app/components/pab-results/pab-results.component.ts
index 510867ca6..fefa3a36b 100644
--- a/src/app/components/pab-results/pab-results.component.ts
+++ b/src/app/components/pab-results/pab-results.component.ts
@@ -8,12 +8,13 @@ import { NgParameter } from "../../formulaire/ngparam";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
 import { PabResultsTableComponent } from "./pab-results-table.component";
 import { PabResults } from "../../results/pab-results";
-import { PabVariableResultsSelectorComponent } from "./pab-variable-results-selector.component";
+import { VariableResultsSelectorComponent } from "../variable-results-selector/variable-results-selector.component";
 import { PlottableData } from "../../results/plottable-data";
 import { PlottablePabResults } from "../../results/plottable-pab-results";
 import { ResultsGraphComponent } from "../results-graph/results-graph.component";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { PabProfileGraphComponent } from "../pab-profile-graph/pab-profile-graph.component";
+import { VarResults } from "../../results/var-results";
 
 @Component({
     selector: "pab-results",
@@ -36,8 +37,8 @@ export class PabResultsComponent implements DoCheck {
     @ViewChild(PabResultsTableComponent, { static: false })
     private pabResultsTableComponent: PabResultsTableComponent;
 
-    @ViewChild(PabVariableResultsSelectorComponent, { static: false })
-    private pabVariableResultsSelectorComponent: PabVariableResultsSelectorComponent;
+    @ViewChild(VariableResultsSelectorComponent, { static: false })
+    private variableResultsSelectorComponent: VariableResultsSelectorComponent;
 
     @ViewChild("generalLog", { static: false })
     private generalLogComponent: LogComponent;
@@ -85,8 +86,8 @@ export class PabResultsComponent implements DoCheck {
         if (this.pabResultsTableComponent) {
             this.pabResultsTableComponent.results = undefined;
         }
-        if (this.pabVariableResultsSelectorComponent) {
-            this.pabVariableResultsSelectorComponent.results = undefined;
+        if (this.variableResultsSelectorComponent) {
+            this.variableResultsSelectorComponent.results = undefined;
         }
         if (this.resultsGraphComponent) {
             this.resultsGraphComponent.results = undefined;
@@ -272,6 +273,7 @@ export class PabResultsComponent implements DoCheck {
      * @returns true si les résultats ont pu être mis à jour
      */
     private updateResults() {
+        console.log("UPD RES PAB", this._pabResults);
         let pabUpdated: boolean;
         let resultsGraphUpdated: boolean;
         let profileGraphUpdated: boolean;
@@ -290,9 +292,9 @@ export class PabResultsComponent implements DoCheck {
             if (pabUpdated) {
                 this.pabResultsTableComponent.results = this._pabResults;
             }
-            selectorUpdated = this.pabVariableResultsSelectorComponent !== undefined;
+            selectorUpdated = this.variableResultsSelectorComponent !== undefined;
             if (selectorUpdated) {
-                this.pabVariableResultsSelectorComponent.results = this._pabResults;
+                this.variableResultsSelectorComponent.results = this._pabResults;
             }
             resultsGraphUpdated = this.resultsGraphComponent !== undefined;
             if (resultsGraphUpdated) {
diff --git a/src/app/components/pab-results/pab-variable-results-selector.component.html b/src/app/components/variable-results-selector/variable-results-selector.component.html
similarity index 76%
rename from src/app/components/pab-results/pab-variable-results-selector.component.html
rename to src/app/components/variable-results-selector/variable-results-selector.component.html
index c02043692..757776ab8 100644
--- a/src/app/components/pab-results/pab-variable-results-selector.component.html
+++ b/src/app/components/variable-results-selector/variable-results-selector.component.html
@@ -1,4 +1,4 @@
-<div class="pab-variable-results-selector" *ngIf="hasVariableResults" fxLayout="row wrap" fxLayoutAlign="center center">
+<div class="pabvariable-results-selector" *ngIf="hasVariableResults" fxLayout="row wrap" fxLayoutAlign="center center">
     <div fxFlex="1 1 100%">
         <mat-form-field>
             <mat-select id="pab-variating-element" [placeholder]="label" [(value)]="selectedValue">
diff --git a/src/app/components/pab-results/pab-variable-results-selector.component.scss b/src/app/components/variable-results-selector/variable-results-selector.component.scss
similarity index 100%
rename from src/app/components/pab-results/pab-variable-results-selector.component.scss
rename to src/app/components/variable-results-selector/variable-results-selector.component.scss
diff --git a/src/app/components/pab-results/pab-variable-results-selector.component.ts b/src/app/components/variable-results-selector/variable-results-selector.component.ts
similarity index 71%
rename from src/app/components/pab-results/pab-variable-results-selector.component.ts
rename to src/app/components/variable-results-selector/variable-results-selector.component.ts
index 4986bfbf8..901c77ebb 100644
--- a/src/app/components/pab-results/pab-variable-results-selector.component.ts
+++ b/src/app/components/variable-results-selector/variable-results-selector.component.ts
@@ -1,20 +1,20 @@
 import { Component, Output, EventEmitter } from "@angular/core";
 
-import { PabResults } from "../../results/pab-results";
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
 import { ApplicationSetupService } from "../../services/app-setup/app-setup.service";
+import { MultiDimensionResults } from "../../results/multidimension-results";
 
 @Component({
-    selector: "pab-variable-results-selector",
-    templateUrl: "./pab-variable-results-selector.component.html",
+    selector: "variable-results-selector",
+    templateUrl: "./variable-results-selector.component.html",
     styleUrls: [
-        "./pab-variable-results-selector.component.scss"
+        "./variable-results-selector.component.scss"
     ]
 })
-export class PabVariableResultsSelectorComponent {
+export class VariableResultsSelectorComponent {
 
     /** résultats non mis en forme */
-    private _pabResults: PabResults;
+    private _results: MultiDimensionResults;
 
     private _selectedValue: number;
 
@@ -34,23 +34,23 @@ export class PabVariableResultsSelectorComponent {
         this._selectedValue = 0;
     }
 
-    public set results(r: PabResults) {
-        this._pabResults = r;
+    public set results(r: MultiDimensionResults) {
+        this._results = r;
 
-        if (this._pabResults) {
+        if (this._results) {
             // pre-extract variable parameters values
             this.varValues = [];
             const nDigits = this.appSetupService.displayDigits;
             // find longest list
             this.size = 0;
-            for (let i = 0; i < this._pabResults.variatedParameters.length; i++) {
-                const vs = this._pabResults.variatedParameters[i].valuesIterator.count();
+            for (let i = 0; i < this._results.variatedParameters.length; i++) {
+                const vs = this._results.variatedParameters[i].valuesIterator.count();
                 if (vs > this.size) {
                     this.size = vs;
                 }
             }
             // get extended values lists for each variable parameter
-            for (const v of this._pabResults.variatedParameters) {
+            for (const v of this._results.variatedParameters) {
                 const vv = [];
                 const iter = v.getExtendedValuesIterator(this.size);
                 while (iter.hasNext) {
@@ -64,9 +64,9 @@ export class PabVariableResultsSelectorComponent {
 
     public get hasVariableResults(): boolean {
         return (
-            this._pabResults
-            && this._pabResults.hasResults
-            && this._pabResults.variatedParameters.length > 0
+            this._results
+            && this._results.hasResults
+            && this._results.variatedParameters.length > 0
         );
     }
 
@@ -81,7 +81,7 @@ export class PabVariableResultsSelectorComponent {
     protected entryLabel(index: number): string {
         let i = 0;
         return this.varValues.map((vv) => {
-            const vp = this._pabResults.variatedParameters[i];
+            const vp = this._results.variatedParameters[i];
             i++;
             let value = "0";
             value = vv[index];
@@ -94,7 +94,7 @@ export class PabVariableResultsSelectorComponent {
     }
 
     public set selectedValue(v: number) {
-        this._pabResults.variableIndex = v;
+        this._results.variableIndex = v;
         this.indexChange.emit();
     }
 
diff --git a/src/app/formulaire/definition/concrete/form-macrorugo-compound.ts b/src/app/formulaire/definition/concrete/form-macrorugo-compound.ts
index 3d62b3b50..7f495c4d7 100644
--- a/src/app/formulaire/definition/concrete/form-macrorugo-compound.ts
+++ b/src/app/formulaire/definition/concrete/form-macrorugo-compound.ts
@@ -1,13 +1,13 @@
 import { IObservable, MacroRugo, MacrorugoCompound, Nub, Props, Session } from "jalhyd";
 
 import { FormulaireBase } from "./form-base";
-import { FormResultFixedVar } from "../form-result-fixedvar";
-import { FormComputeFixedVar } from "../form-compute-fixedvar";
 import { FieldSet } from "../../fieldset";
 import { FieldsetContainer } from "../../fieldset-container";
 import { FormulaireNode } from "../../formulaire-node";
 import { FieldsetTemplate } from "../../fieldset-template";
 import { NgParameter } from "../../ngparam";
+import { FormResultMacrorugoCompound } from "../form-result-macrorugo-compound";
+import { FormComputeMacrorugoCompound } from "../form-compute-macrorugo-compound";
 
 /**
  * Formulaire pour les passes à macrorugosités complexes
@@ -19,14 +19,14 @@ export class FormulaireMacrorugoCompound extends FormulaireBase {
 
     constructor() {
         super();
-        this._formResult = new FormResultFixedVar(this);
+        this._formResult = new FormResultMacrorugoCompound(this);
 
         // default properties
         this._props["inclinedApron"] = false;
 
         // remove obsolete observer set by super()
         this.removeObserver(this._formCompute);
-        this._formCompute = new FormComputeFixedVar(this, (this._formResult as FormResultFixedVar));
+        this._formCompute = new FormComputeMacrorugoCompound(this, (this._formResult as FormResultMacrorugoCompound));
     }
 
     public get mrcNub(): MacrorugoCompound {
diff --git a/src/app/formulaire/definition/form-compute-fixedvar.ts b/src/app/formulaire/definition/form-compute-fixedvar.ts
index ede113a8f..ba74fda4f 100644
--- a/src/app/formulaire/definition/form-compute-fixedvar.ts
+++ b/src/app/formulaire/definition/form-compute-fixedvar.ts
@@ -14,29 +14,6 @@ export class FormComputeFixedVar extends FormCompute {
         return this._formResult as FormResultFixedVar;
     }
 
-    private getVariatedParameters(): NgParameter[] {
-        let res: NgParameter[] = [];
-        // find variated local parameters
-        res = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.VAR);
-        // add variated linked parameters
-        const pms = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.LINK);
-        for (const p of pms) {
-            if (p.paramDefinition.hasMultipleValues) {
-                res.push(p);
-            }
-        }
-        return res;
-    }
-
-    private getComputedParameter(): NgParameter {
-        const cpd = this._formBase.currentNub.calculatedParam;
-        let ngparam = this._formBase.getParamFromSymbol(cpd.symbol);
-        if (ngparam === undefined) { // calculated parameter is not displayed on screen
-            ngparam = new NgParameter(cpd, this._formBase);
-        }
-        return ngparam;
-    }
-
     protected compute() {
         this.runNubCalc(this._formBase.currentNub);
         this.reaffectResultComponents();
diff --git a/src/app/formulaire/definition/form-compute-macrorugo-compound.ts b/src/app/formulaire/definition/form-compute-macrorugo-compound.ts
new file mode 100644
index 000000000..4b6de1561
--- /dev/null
+++ b/src/app/formulaire/definition/form-compute-macrorugo-compound.ts
@@ -0,0 +1,42 @@
+import { Result, MacrorugoCompound } from "jalhyd";
+
+import { FormulaireDefinition } from "./form-definition";
+import { FormCompute } from "./form-compute";
+import { FormResultMacrorugoCompound } from "./form-result-macrorugo-compound";
+import { NgParameter, ParamRadioConfig } from "../ngparam";
+
+export class FormComputeMacrorugoCompound extends FormCompute {
+
+    constructor(formBase: FormulaireDefinition, formResult: FormResultMacrorugoCompound) {
+        super(formBase, formResult);
+    }
+
+    protected get formResult(): FormResultMacrorugoCompound {
+        return this._formResult as FormResultMacrorugoCompound;
+    }
+
+    protected compute() {
+        this.runNubCalc(this._formBase.currentNub);
+        this.reaffectResultComponents();
+    }
+
+    protected reaffectResultComponents() {
+        const mrc: MacrorugoCompound = (this._formBase.currentNub as MacrorugoCompound);
+        const computedParam: NgParameter = this.getComputedParameter();
+        const varParams: NgParameter[] = this.getVariatedParameters();
+
+        // résultat de calcul de la passe à macrorugo complexe
+        const mrcr = this.formResult.mrcResults;
+        mrcr.calculatedParameter = computedParam;
+        mrcr.result = mrc.result;
+        if (varParams) {
+            mrcr.variatedParameters = varParams;
+        }
+        // résultat de chaque enfant
+        const cr: Result[] = [];
+        for (const c of mrc.children) {
+            cr.push(c.result);
+        }
+        mrcr.childrenResults = cr;
+    }
+}
diff --git a/src/app/formulaire/definition/form-compute-pab.ts b/src/app/formulaire/definition/form-compute-pab.ts
index bcde3d155..445ad0b5d 100644
--- a/src/app/formulaire/definition/form-compute-pab.ts
+++ b/src/app/formulaire/definition/form-compute-pab.ts
@@ -15,17 +15,6 @@ export class FormComputePab extends FormCompute {
         return this._formResult as FormResultPab;
     }
 
-    private getVariatedParameters(): NgParameter[] {
-        const res = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.VAR);
-        const pms = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.LINK);
-        for (const p of pms) {
-            if (p.paramDefinition.hasMultipleValues) {
-                res.push(p);
-            }
-        }
-        return res;
-    }
-
     protected compute() {
         this.runNubCalc(this._formBase.currentNub);
         this.reaffectResultComponents();
@@ -46,7 +35,7 @@ export class FormComputePab extends FormCompute {
         for (const c of pab.children) {
             cr.push(c.result);
         }
-        pabr.cloisonsResults = cr,
+        pabr.cloisonsResults = cr;
         // résultat de la cloison aval
         pabr.cloisonAvalResults = pab.downWall.result;
 
@@ -74,8 +63,4 @@ export class FormComputePab extends FormCompute {
             pabr.variatedParameters = varParams;
         }
     }
-
-    private getComputedParameter(): NgParameter {
-        return this._formBase.getDisplayedParamFromState(ParamRadioConfig.CAL);
-    }
 }
diff --git a/src/app/formulaire/definition/form-compute.ts b/src/app/formulaire/definition/form-compute.ts
index af5c587cd..14a21c5d6 100644
--- a/src/app/formulaire/definition/form-compute.ts
+++ b/src/app/formulaire/definition/form-compute.ts
@@ -2,6 +2,7 @@ import { Nub, Result, ParamDomainValue, Observer, ParamDefinition } from "jalhyd
 
 import { FormResult } from "./form-result";
 import { FormulaireDefinition } from "./form-definition";
+import { NgParameter, ParamRadioConfig } from "../ngparam";
 
 export abstract class FormCompute implements Observer {
 
@@ -91,6 +92,29 @@ export abstract class FormCompute implements Observer {
         return nub.CalcSerie(init, this.getParameterRefid(computedParam));
     }
 
+    protected getComputedParameter(): NgParameter {
+        const cpd = this._formBase.currentNub.calculatedParam;
+        let ngparam = this._formBase.getParamFromSymbol(cpd.symbol);
+        if (ngparam === undefined) { // calculated parameter is not displayed on screen
+            ngparam = new NgParameter(cpd, this._formBase);
+        }
+        return ngparam;
+    }
+
+    protected getVariatedParameters(): NgParameter[] {
+        let res: NgParameter[] = [];
+        // find variated local parameters
+        res = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.VAR);
+        // add variated linked parameters
+        const pms = this._formBase.getDisplayedParamListFromState(ParamRadioConfig.LINK);
+        for (const p of pms) {
+            if (p.paramDefinition.hasMultipleValues) {
+                res.push(p);
+            }
+        }
+        return res;
+    }
+
     /**
      * Triggers computation of the Nub, updates form results
      */
diff --git a/src/app/formulaire/definition/form-result-macrorugo-compound.ts b/src/app/formulaire/definition/form-result-macrorugo-compound.ts
new file mode 100644
index 000000000..73ba7e9d2
--- /dev/null
+++ b/src/app/formulaire/definition/form-result-macrorugo-compound.ts
@@ -0,0 +1,33 @@
+import { FormulaireDefinition } from "./form-definition";
+import { FormResult } from "./form-result";
+import { CalculatorResults } from "../../results/calculator-results";
+import { MacrorugoCompoundResults } from "../../results/macrorugo-compound-results";
+
+export class FormResultMacrorugoCompound extends FormResult {
+
+    protected _formBase: FormulaireDefinition;
+
+    protected _mrcResults: MacrorugoCompoundResults;
+
+    constructor(base: FormulaireDefinition) {
+        super();
+        this._formBase = base;
+        this._mrcResults = new MacrorugoCompoundResults();
+    }
+
+    public get mrcResults() {
+        return this._mrcResults;
+    }
+
+    public resetResults() {
+        this._mrcResults.reset();
+    }
+
+    public get results(): CalculatorResults[] {
+        return [ this._mrcResults ];
+    }
+
+    public get hasResults(): boolean {
+        return this._mrcResults.hasResults;
+    }
+}
diff --git a/src/app/results/macrorugo-compound-results.ts b/src/app/results/macrorugo-compound-results.ts
new file mode 100644
index 000000000..ec1275b92
--- /dev/null
+++ b/src/app/results/macrorugo-compound-results.ts
@@ -0,0 +1,114 @@
+import { Result } from "jalhyd";
+
+import { ServiceFactory } from "../services/service-factory";
+import { MultiDimensionResults } from "./multidimension-results";
+
+export class MacrorugoCompoundResults extends MultiDimensionResults {
+
+    /** résultats des modules MacroRugo enfants */
+    public childrenResults: Result[];
+
+    /** symboles des colonnes de résultat */
+    protected _columns: string[];
+
+    public constructor() {
+        super();
+        this.reset();
+        // standard headers
+        this._columns = [
+            "RADIER_N",
+            "ZF1",
+            "B",
+            "Q",
+            "ZF2",
+            "Vdeb",
+            "Fr",
+            "Vmax",
+            "PV",
+            "ENUM_MacroRugoFlowType",
+            "Q_GuideTech",
+            "V_GuideTech",
+            "xCenter"
+        ];
+    }
+
+    /** headers symbols */
+    public get columns() {
+        return this._columns;
+    }
+
+    /** translated headers texts */
+    public get headers() {
+        return this._columns.map((h) => {
+            // calculator type for translation
+            const sn = this.result.sourceNub;
+            let ct = sn.calcType;
+            if (sn.parent) {
+                ct = sn.parent.calcType;
+            }
+            return ServiceFactory.instance.formulaireService.expandVariableNameAndUnit(ct , h);
+        });
+    }
+
+    public reset() {
+        super.reset();
+        this.childrenResults = [];
+    }
+
+    /**
+     * Returns true if at least one log message is present in the PAB result or any
+     * of the children results
+     */
+    public get hasLog(): boolean {
+        if (this.childrenResults) {
+            for (const cr of this.childrenResults) {
+                if (cr && cr.hasLog()) {
+                    return true;
+                }
+            }
+        }
+        return (this.result && this.result.hasLog());
+    }
+
+    // do not test result.ok else log messages will prevent partial results from being displayed
+    public get hasResults(): boolean {
+        return this.result !== undefined && ! this.result.hasOnlyErrors;
+    }
+
+    /** retourne true si au moins un calcul a échoué (le log a un code négatif) */
+    public hasError(): boolean {
+        let err = false;
+        // log principal
+        err = (err || this.result.hasErrorMessages());
+        // logs des enfants
+        for (const c of this.childrenResults) {
+            err = (err || c.hasErrorMessages());
+        }
+
+        return err;
+    }
+
+    /** retourne true si le calcul à l'itération i a échoué */
+    public iterationHasError(i: number): boolean {
+        let err = this.result.resultElements[i].hasErrorMessages();
+        // logs des cloisons
+        for (const c of this.childrenResults) {
+            err = (err || c.resultElements[i].hasErrorMessages());
+        }
+
+        return err;
+    }
+
+    /** retourne true si tous les calculs ont échoué */
+    public hasOnlyErrors(): boolean {
+        let err = true;
+        // log principal
+        err = (err && this.result.hasOnlyErrors);
+        // logs des cloisons
+        for (const c of this.childrenResults) {
+            err = (err && c.hasOnlyErrors);
+        }
+
+        return err;
+    }
+}
diff --git a/src/app/results/multidimension-results.ts b/src/app/results/multidimension-results.ts
new file mode 100644
index 000000000..86c42164c
--- /dev/null
+++ b/src/app/results/multidimension-results.ts
@@ -0,0 +1,13 @@
+import { CalculatedParamResults } from "./param-calc-results";
+import { PlottableData } from "./plottable-data";
+import { NgParameter } from "../formulaire/ngparam";
+
+
+export class MultiDimensionResults extends CalculatedParamResults/*  implements PlottableData */ {
+
+    /** paramètres variés */
+    public variatedParameters: NgParameter[];
+
+    /** index de la valeur du paramètre varié à afficher dans les résultats */
+    public variableIndex = 0;
+}
diff --git a/src/app/results/pab-results.ts b/src/app/results/pab-results.ts
index a81d37158..d407ae8a4 100644
--- a/src/app/results/pab-results.ts
+++ b/src/app/results/pab-results.ts
@@ -1,10 +1,10 @@
 import { Result } from "jalhyd";
 
-import { CalculatedParamResults } from "./param-calc-results";
 import { NgParameter } from "../formulaire/ngparam";
 import { ServiceFactory } from "../services/service-factory";
+import { MultiDimensionResults } from "./multidimension-results";
 
-export class PabResults extends CalculatedParamResults {
+export class PabResults extends MultiDimensionResults {
 
     /** résultats des modules Cloisons avant chaque bassin */
     public cloisonsResults: Result[];
@@ -18,12 +18,6 @@ export class PabResults extends CalculatedParamResults {
      * */
     public Z2: number[];
 
-    /** paramètres variés */
-    public variatedParameters: NgParameter[];
-
-    /** index de la valeur du paramètre varié à afficher dans les résultats */
-    public variableIndex = 0;
-
     /** symboles des colonnes de résultat */
     protected _columns: string[];
 
diff --git a/src/app/results/plottable-macrorugo-compound-results.ts b/src/app/results/plottable-macrorugo-compound-results.ts
new file mode 100644
index 000000000..0be1264f1
--- /dev/null
+++ b/src/app/results/plottable-macrorugo-compound-results.ts
@@ -0,0 +1,141 @@
+import { PlottableData } from "./plottable-data";
+import { GraphType } from "./graph-type";
+import { ServiceFactory } from "../services/service-factory";
+import { MacrorugoCompoundResults } from "./macrorugo-compound-results";
+
+export class PlottableMacrorugoCompoundResults implements PlottableData {
+
+    public graphType: GraphType = GraphType.Scatter;
+    public chartX: string;
+    public chartY: string;
+
+    protected mrcResults: MacrorugoCompoundResults;
+
+    public constructor(mrcResults?: MacrorugoCompoundResults) {
+        if (mrcResults) {
+            this.setMrcResults(mrcResults);
+        }
+        // axes par défaut
+        this.chartX = this.chartX || "CLOISON";
+        this.chartY = this.chartY || "YMOY";
+    }
+
+    /** reaffect mrcResults, for ex. when objet was contructed with empty mrcResults */
+    public setMrcResults(mrcResults: MacrorugoCompoundResults) {
+        this.mrcResults = mrcResults;
+    }
+
+    /**
+     * Returns the label to display, for an element of getAvailableChartAxis()
+     * @param symbol parameter / result symbol (ex: "Q")
+     */
+    public getChartAxisLabel(symbol: string): string {
+        if (symbol === "x") { // specific case for wall abscissa
+            return ServiceFactory.instance.i18nService.localizeText("INFO_LIB_ABSCISSE_CLOISON");
+        } else {
+            return this.mrcResults.headers[this.mrcResults.columns.indexOf(symbol)];
+        }
+    }
+
+    public expandLabelFromSymbol(symbol: string): string {
+        return symbol;
+    }
+
+    /**
+     * Returns a list of plottable parameters / result elements, that can be defined
+     * as X or Y chart axis
+     */
+    public getAvailableChartAxis(): string[] {
+        const axis = [];
+        axis.push("x"); // wall abscissa
+        for (const c of this.mrcResults.columns) {
+            if (c.indexOf("ENUM_") === -1) { // ENUM variables are not plottable
+                axis.push(c);
+            }
+        }
+        return axis;
+    }
+
+    public getAvailableXAxis(): string[] {
+        return this.getAvailableChartAxis();
+    }
+
+    public getAvailableYAxis(): string[] {
+        return this.getAvailableChartAxis();
+    }
+
+    // just to implement interface
+    public getVariatingParametersSymbols(): string[] {
+        return [];
+    }
+
+    /**
+     * Returns the series of values for the required symbol
+     * @param symbol parameter / result symbol (ex: "Q")
+     */
+    public getValuesSeries(symbol: string): number[] {
+        const data: number[] = [];
+        const pr = this.mrcResults;
+        const l = this.mrcResults.childrenResults.length;
+        // when a parameter is variating, index of the variating parameter
+        // values to build the data from
+        const vi = this.mrcResults.variableIndex;
+
+        if (this.mrcResults.iterationHasError(vi)) {
+            return [];
+        }
+
+        switch (symbol) {
+            case "RADIER_N":
+                data.push(undefined);
+                for (let i = 0; i <= l; i++) { // <= for one extra step (downwall)
+                    data.push(i + 1);
+                }
+                break;
+
+            case "DH":
+            case "ZRAM":
+            /* case "Q":
+                data.push(undefined);
+                for (let i = 0; i < l; i++) {
+                    const er = pr.cloisonsResults[i].resultElements[vi].getValue(symbol);
+                    data.push(er);
+                }
+                const zrAval = pr.cloisonAvalResults.resultElements[vi].getValue(symbol);
+                data.push(zrAval);
+                break;
+
+            case "Z":
+                for (let i = 0; i < l; i++) {
+                    data.push(pr.cloisonsResults[i].resultElements[vi].vCalc);
+                }
+                data.push(pr.cloisonAvalResults.resultElements[vi].vCalc);
+                data.push(pr.Z2[vi]);
+                break; */
+
+            case "PV":
+            case "YMOY":
+            case "ZRMB":
+            case "QA":
+                data.push(undefined);
+                for (let i = 0; i < l; i++) {
+                    const er = pr.childrenResults[i].resultElements[vi].getValue(symbol);
+                    data.push(er);
+                }
+                data.push(undefined);
+                break;
+
+            /* case "x": // wall abscissa
+                data.push(undefined);
+                for (let i = 0; i < l; i++) {
+                    const er = pr.cloisonsResults[i].resultElements[vi].getValue(symbol);
+                    data.push(er);
+                }
+                const erXdw = pr.cloisonAvalResults.resultElements[vi].getValue(symbol);
+                data.push(erXdw);
+                break; */
+        }
+
+        return data;
+    }
+}
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index 2a22bfec4..adcc569ba 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -217,6 +217,7 @@
     "INFO_LIB_R": "Hydraulic radius",
     "INFO_LIB_S": "Orifice area",
     "INFO_LIB_RADIER": "Basin bottom",
+    "INFO_LIB_RADIER_N": "Apron #",
     "INFO_LIB_SELECT_LOIDEBIT": "Stage-discharge law",
     "INFO_LIB_SELECT_LOIDEBIT1_KIVI": "Kindsvater-Carter and Villemonte",
     "INFO_LIB_SELECT_LOIDEBIT1": "Stage-discharge law",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 7155a166c..e5c5de4bf 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -215,6 +215,7 @@
     "INFO_LIB_QA": "Débit d'attrait",
     "INFO_LIB_R": "Rayon hydraulique",
     "INFO_LIB_RADIER": "Radier",
+    "INFO_LIB_RADIER_N": "Radier n°",
     "INFO_LIB_S": "Surface de l'orifice",
     "INFO_LIB_SELECT_LOIDEBIT": "Loi de débit",
     "INFO_LIB_SELECT_LOIDEBIT1_KIVI": "Kindsvater-Carter et Villemonte",
-- 
GitLab