From 82d40575bb58f0b96128ea4dda5f94e03ee08c1a Mon Sep 17 00:00:00 2001
From: "francois.grand" <francois.grand@irstea.fr>
Date: Mon, 18 Dec 2017 11:06:37 +0100
Subject: [PATCH] =?UTF-8?q?Stockage=20des=20r=C3=A9sultats=20de=20calcul?=
 =?UTF-8?q?=20dans=20la=20classe=20FormulaireDefinition=20-=20cr=C3=A9atio?=
 =?UTF-8?q?n=20des=20classes=20FixedVarResults,=20SectionResults=20et=20Re?=
 =?UTF-8?q?mousResults=20pour=20stocker=20les=20r=C3=A9sultats=20de=20calc?=
 =?UTF-8?q?ulettes=20dans=20FormulaireDefinition=20-=20CalculatorResultsCo?=
 =?UTF-8?q?mponent=20renomm=C3=A9=20en=20FixedVarResultsComponent=20-=20cr?=
 =?UTF-8?q?=C3=A9ation=20d'un=20composant=20CalculatorResultsComponent=20q?=
 =?UTF-8?q?ui=20inclut=20tous=20les=20composants=20de=20r=C3=A9sultats=20(?=
 =?UTF-8?q?FixedVarResultsComponent,=20SectionResultsComponent,=20RemousRe?=
 =?UTF-8?q?sultsComponent)=20-=20cr=C3=A9ation=20d'un=20service=20de=20par?=
 =?UTF-8?q?am=C3=A9trage=20global=20de=20l'appli=20(ApplicationSetupServic?=
 =?UTF-8?q?e)=20-=20fichiers=20JSON=20de=20configuration=20des=20calculett?=
 =?UTF-8?q?es=20:=20ajout=20d'un=20champ=20sectionSelectId=20pour=20d?=
 =?UTF-8?q?=C3=A9signer=20le=20select=20qui=20d=C3=A9termine=20le=20type?=
 =?UTF-8?q?=20de=20section?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/app/app.component.ts                      |  18 +-
 src/app/app.module.ts                         |   6 +-
 .../generic/calculator.component.html         |   4 +-
 .../generic/calculator.component.ts           | 568 +--------------
 .../regime-uniforme.config.json               |   3 +-
 src/app/calculators/remous/remous.config.json |   3 +-
 .../section-param/section-param.config.json   |   3 +-
 .../calculator-results.component.html         |  50 +-
 .../calculator-results.component.ts           | 189 +----
 src/app/components/canvas/canvas.component.ts |   3 +-
 .../fixedvar-results.component.html           |  51 ++
 .../fixedvar-results.component.ts             | 212 ++++++
 src/app/components/log/log.component.ts       |  36 +-
 .../remous-results.component.html             |   8 +-
 .../remous-results.component.ts               | 121 +---
 .../section-results.component.ts              |  43 +-
 src/app/formulaire/formulaire-definition.ts   | 676 +++++++++++++++++-
 src/app/results/calculator-results.ts         |  28 +
 src/app/results/fixed-var-results.ts          | 137 ++++
 src/app/results/remous-results.ts             | 142 ++++
 src/app/results/section-results.ts            |  54 ++
 .../services/app-setup/app-setup.service.ts   |  26 +
 .../services/formulaire/formulaire.service.ts | 134 +---
 23 files changed, 1498 insertions(+), 1017 deletions(-)
 create mode 100644 src/app/components/fixedvar-results/fixedvar-results.component.html
 create mode 100644 src/app/components/fixedvar-results/fixedvar-results.component.ts
 create mode 100644 src/app/results/calculator-results.ts
 create mode 100644 src/app/results/fixed-var-results.ts
 create mode 100644 src/app/results/remous-results.ts
 create mode 100644 src/app/results/section-results.ts
 create mode 100644 src/app/services/app-setup/app-setup.service.ts

diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 8c93ded30..3fed50b35 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -92,13 +92,17 @@ export class AppComponent implements Observer {
       console.log(data);
     }
     else if (sender instanceof FormulaireService) {
-      const f: FormulaireDefinition = data;
-      this._calculators.push(
-        {
-          "title": CalculatorType[f.calculatorType] + String(f.uid),
-          "uid": String(f.uid)
-        }
-      );
+      switch (data["action"]) {
+        case "create":
+          const f: FormulaireDefinition = data["form"];
+          this._calculators.push(
+            {
+              "title": CalculatorType[f.calculatorType] + String(f.uid),
+              "uid": String(f.uid)
+            }
+          );
+          break;
+      }
     }
   }
 
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index b2ff7e6d1..5acde2f69 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -12,6 +12,7 @@ import { FormulaireService } from "./services/formulaire/formulaire.service";
 import { ParamService } from "./services/param/param.service";
 import { InternationalisationService } from "./services/internationalisation/internationalisation.service";
 import { HttpService } from "./services/http/http.service";
+import { ApplicationSetupService } from "./services/app-setup/app-setup.service";
 import { AppComponent } from './app.component';
 import { ParamInputComponent } from './components/param-input/param-input.component';
 import { FieldSetComponent } from './components/field-set/field-set.component';
@@ -21,6 +22,7 @@ import { CheckFieldLineComponent } from './components/check-field-line/check-fie
 // import { AlertDialog } from './components/alert-dialog/alert-dialog.component';
 import { AppErrorModule } from './error.module';
 import { CalculatorResultsComponent } from './components/calculator-results/calculator-results.component';
+import { FixedVarResultsComponent } from './components/fixedvar-results/fixedvar-results.component';
 import { SectionResultsComponent } from './components/section-results/section-results.component';
 import { GenericCalculatorComponent } from './calculators/generic/calculator.component';
 import { CalcCanvasComponent } from './components/canvas/canvas.component';
@@ -60,12 +62,12 @@ const appRoutes: Routes = [
     CalculatorListComponent,
     GenericCalculatorComponent,
     // AlertDialog,
-    CalculatorResultsComponent, SectionResultsComponent, RemousResultsComponent,
+    CalculatorResultsComponent, FixedVarResultsComponent, SectionResultsComponent, RemousResultsComponent,
     CalcCanvasComponent, SectionCanvasComponent
   ],
   // entryComponents: [AlertDialog],
   providers: [ // services
-    ParamService, InternationalisationService, HttpService, FormulaireService
+    ParamService, InternationalisationService, HttpService, FormulaireService, ApplicationSetupService
   ],
   schemas: [NO_ERRORS_SCHEMA],
   bootstrap: [AppComponent]
diff --git a/src/app/calculators/generic/calculator.component.html b/src/app/calculators/generic/calculator.component.html
index ffc9d4aec..a513d0235 100644
--- a/src/app/calculators/generic/calculator.component.html
+++ b/src/app/calculators/generic/calculator.component.html
@@ -23,6 +23,4 @@
         <p></p>
     </div>
 </div>
-<calc-results [style.display]="getResultsStyleDisplay()"></calc-results>
-<section-results [style.display]="getSectionResultsStyleDisplay()"></section-results>
-<remous-results [style.display]="getRemousResultsStyleDisplay()"></remous-results>
\ No newline at end of file
+<calc-results></calc-results>
\ No newline at end of file
diff --git a/src/app/calculators/generic/calculator.component.ts b/src/app/calculators/generic/calculator.component.ts
index 60f409ef5..df4d9da12 100644
--- a/src/app/calculators/generic/calculator.component.ts
+++ b/src/app/calculators/generic/calculator.component.ts
@@ -1,7 +1,7 @@
 import { Component, Inject, OnInit, DoCheck, ViewChild, Input } from "@angular/core";
 import { ActivatedRoute, ParamMap } from '@angular/router';
 
-import { ComputeNode, ComputeNodeType, ParamsEquation, Nub, acSection, RegimeUniforme, MethodeResolution, CourbeRemousParams, CourbeRemous, cLog, Result } from "jalhyd";
+import { ComputeNodeType } from "jalhyd";
 
 import { ParamService } from "../../services/param/param.service";
 import { HttpService } from "../../services/http/http.service";
@@ -17,11 +17,10 @@ import { ExistenceDependency } from "../../formulaire/existence-dependency";
 import { ValueDependency } from "../../formulaire/value-dependency";
 import { NgParameter, ParamRadioConfig } from "../../formulaire/ngparam";
 import { CalculatorResultsComponent } from "../../components/calculator-results/calculator-results.component";
-import { SectionResultsComponent } from "../../components/section-results/section-results.component";
-import { RemousResultsComponent } from "../../components/remous-results/remous-results.component";
 import { Observer } from "../../services/observer";
 import { Subscription } from "rxjs/Subscription";
 
+
 @Component({
     selector: 'hydrocalc',
     templateUrl: "./calculator.component.html",
@@ -34,61 +33,30 @@ import { Subscription } from "rxjs/Subscription";
     ]
 })
 export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
-    /**
-     * type de noeud de calcul actuel (utilisé pour les sections, à sortir de ce composant générique)
-     */
-    private _nodeType: ComputeNodeType;
-
     /**
      * composant d'affichage des résultats
      */
     @ViewChild(CalculatorResultsComponent)
     private resultsComponent: CalculatorResultsComponent;
 
-    /**
-     * composant d'affichage des résultats des section paramétrées
-     */
-    @ViewChild(SectionResultsComponent)
-    private sectionResultsComponent: SectionResultsComponent;
-
-    /**
-     * composant d'affichage des résultats des section paramétrées
-     */
-    @ViewChild(RemousResultsComponent)
-    private remousResultsComponent: RemousResultsComponent;
-
     /**
      * formulaire affiché
      */
     private _formulaire: FormulaireDefinition;
 
-    /**
-     * flag d'affichage du composant de résultats fixes/variables
-     */
-    private _showResultsFixVar: boolean = false;
-
-    /**
-     * flag d'affichage du composant de résultats du calcul de section
-     */
-    private _showResultsSection: boolean = false;
-
-    /**
-     * flag d'affichage du composant de résultats du calcul des courbes de remous
-     */
-    private _showResultsRemous: boolean = false;
-
     constructor(private paramService: ParamService,
         private httpService: HttpService,
         private intlService: InternationalisationService,
         private formulaireService: FormulaireService,
         private route: ActivatedRoute) {
         this.intlService.addObserver(this);
+        this.formulaireService.addObserver(this);
     }
 
     private get fieldSets(): FieldSet[] {
         if (this._formulaire == undefined)
             return [];
-        return this._formulaire.getFieldSets(this._nodeType);
+        return this._formulaire.getFieldSets();
     }
 
     private get uitextTitre() {
@@ -117,13 +85,6 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         return this.intlService.localizeText("INFO_CALCULATOR_CALCULER");
     }
 
-
-    private applyDependencies() {
-        if (this._formulaire == undefined)
-            return;
-        this._formulaire.applyDependencies();
-    }
-
     private _subscription: Subscription;
 
     ngOnInit() {
@@ -132,9 +93,8 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
             const uid: number = +params['uid'];
 
             this._formulaire = this.formulaireService.getFormulaireFromId(uid);
-            let ssf: SelectField = <SelectField>this._formulaire.getFormulaireElementById("select_section");
-            if (ssf != undefined)
-                this.updateSectionType(ssf.getValue());
+            this.resultsComponent.formulaire = this._formulaire;
+            this._formulaire.updateNodeType();
         });
     }
 
@@ -157,485 +117,20 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
      * envoi d'un message au composant parent
      * cf. https://angular.io/guide/component-interaction#parent-listens-for-child-event
      */
-
     private onRadioClick(info: string) {
-        this._showResultsFixVar = false;
-        this._showResultsSection = false;
-        this._showResultsRemous = false;
-
         let tmp: string[] = info.split("_");
-        let symbol: string = tmp[0];
-
-        let sourceParam = this._formulaire.getParamFromSymbol(symbol);
-        let oldState: ParamRadioConfig = sourceParam.radioState;
-        let newState: ParamRadioConfig = ParamRadioConfig[tmp[1].toUpperCase()];
-
-        switch (oldState) {
-            case ParamRadioConfig.FIX:
-                switch (newState) {
-                    case ParamRadioConfig.VAR:
-                        this._formulaire.resetOther(sourceParam, ParamRadioConfig.CAL);
-                        break;
-
-                    case ParamRadioConfig.CAL:
-                        this._formulaire.resetOther(sourceParam, ParamRadioConfig.VAR);
-                        break;
-                }
-                break;
-
-            case ParamRadioConfig.VAR:
-                switch (newState) {
-                    case ParamRadioConfig.CAL:
-                        this._formulaire.resetOther(sourceParam, ParamRadioConfig.VAR);
-                        break;
-                }
-                break;
-
-            case ParamRadioConfig.CAL:
-                switch (newState) {
-                    case ParamRadioConfig.FIX:
-                        this._formulaire.setDefault();
-                        break;
-
-                    case ParamRadioConfig.VAR:
-                        this._formulaire.resetOther(sourceParam, ParamRadioConfig.CAL);
-                        this._formulaire.setDefault();
-                        break;
-                }
-        }
+        let symbol: string = tmp[0]; // symbole du paramètre dont vient l'événement radio
+        let option: string = tmp[1]; // nouvel état (radio)
 
-        sourceParam.radioState = newState;
+        this._formulaire.resetRadiosAndResults(symbol, option);
 
         // this.appRef.tick();
         //        this.changeDetectorRef.detectChanges();  // provoque une détection des changements dans les contrôles
-
-        this.resultsComponent.reset(false);
-        this.applyDependencies();
-    }
-
-    protected getParameterValue(symbol: string): number {
-        return this._formulaire.getParameterValue(symbol);
-    }
-
-    protected getNodeParameterValue(nodeType: ComputeNodeType, symbol: string): number {
-        return this._formulaire.getNodeParameterValue(nodeType, symbol);
-    }
-
-    private getComputedParameter(): NgParameter {
-        return this._formulaire.getParamFromState(ParamRadioConfig.CAL);
-    }
-
-    private getVariatedParameter(): NgParameter {
-        return this._formulaire.getParamFromState(ParamRadioConfig.VAR);
-    }
-
-    private isMySectionType(nodeType: ComputeNodeType, strict: boolean) {
-        let res: boolean = nodeType == this._nodeType;
-        if (strict)
-            return res;
-
-        return res || nodeType == ComputeNodeType.SectionParametree
-    }
-
-    private getSectionVariatedParameter(): NgParameter {
-        let res: NgParameter = this._formulaire.getParamFromState(ParamRadioConfig.VAR);
-        if (res != undefined && this.isMySectionType(res.computeNodeType, false))
-            return res;
-        return undefined;
-    }
-
-    private addFixedResults(nDigits: number, displaySymbol: boolean) {
-        for (let p of this._formulaire.getInputParameters())
-            if (p.radioState == ParamRadioConfig.FIX && p.symbol !== "Pr")
-                this.resultsComponent.addFixedResult(p, p.getValue(), nDigits, displaySymbol);
-    }
-
-    private addSectionFixedResults(nDigits: number, displaySymbol: boolean) {
-        for (let p of this._formulaire.getDisplayedInputParameters())
-            if (p.radioState == ParamRadioConfig.FIX && this.isMySectionType(p.computeNodeType, false) && p.symbol !== "Pr")
-                this.resultsComponent.addFixedResult(p, p.getValue(), nDigits, displaySymbol);
-    }
-
-    private addSectionFixedResult(val: number, label: string, nDigits: number, drawLabel: string = undefined) {
-        this.sectionResultsComponent.addResult(val, label, nDigits, drawLabel);
-    }
-
-    private getSectionComputedParam(): { symbol: string, label: string } {
-        let targetSelect: SelectField = <SelectField>this._formulaire.getFieldById("select_target");
-        let targetValue: string = targetSelect.getValue();
-
-        let symbol: string = this.removePrefix(targetValue, "select_target_");
-
-        let label = targetSelect.getLabel();
-        return { symbol, label };
-    }
-
-    private doComputeSectionVar(varParam: NgParameter) {
-        let prec: number = this.getParameterValue("Pr"); // précision
-        let nDigits = -Math.log10(prec);
-
-        this.resultsComponent.reset(false);
-        this.addSectionFixedResults(nDigits, false);
-
-        let computedParam = this.getSectionComputedParam();
-
-        this.resultsComponent.setVariableParamHeaderFromParameter(varParam, false);
-        this.resultsComponent.setVariableResultHeader(computedParam["label"]);
-
-        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField("select_section", CalculatorType.SectionParametree);
-
-        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(this._formulaire, nodeType);
-        let sect: acSection = np[0];
-        let prms: ParamsEquation = np[1];
-
-        let min: number = +varParam.minValue;
-        let max: number = +varParam.maxValue;
-        let step: number = +varParam.stepValue;
-
-        let yField: InputField = <InputField>this._formulaire.getFormulaireElementById("Y");
-        let Y: number = yField.getValue();
-
-        let compSymbol = computedParam["symbol"];
-        for (let val = min; val <= max; val += step) {
-            prms[varParam.symbol].v = val;
-
-            sect.Reset(true);
-            let res = sect.Calc(compSymbol, Y);
-            this.resultsComponent.addVarResult(val, res, nDigits);
-        }
-
-        this.resultsComponent.setGraphTitle(computedParam.symbol + " = f( " + varParam.symbol + " )");
-        this.resultsComponent.generateGraph();
-        this._showResultsFixVar = true;
-    }
-
-    private doComputeSection() {
-        this.sectionResultsComponent.reset();
-
-        let varParam = this.getSectionVariatedParameter();
-        if (varParam != undefined) {
-            this.doComputeSectionVar(varParam);
-            return;
-        }
-
-        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField("select_section", CalculatorType.SectionParametree);
-        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(this._formulaire, nodeType);
-
-        let sect: acSection = np[0];
-        let prms: ParamsEquation = np[1];
-
-        let prec: number = this.getNodeParameterValue(ComputeNodeType.SectionParametree, "Pr"); // précision
-        let nDigits = -Math.log10(prec);
-
-        let Y = prms.map.Y.v; // tirant d'eau original (doit être fourni à acSection.Calc() sous peine d'être modifié par les appels successifs car c'est en même temps un paramètre et une variable temporaire)
-
-        // charge spécifique
-        let Hs = sect.Calc("Hs", Y);
-        this.addSectionFixedResult(Hs, this.intlService.localizeText("INFO_GRANDEUR_HS"), nDigits, "Hs");
-
-        // charge critique
-        let Hsc = sect.Calc("Hsc", Y);
-        this.addSectionFixedResult(Hsc, this.intlService.localizeText("INFO_GRANDEUR_HSC"), nDigits, "Hsc");
-
-        // largeur au miroir
-        let B = sect.Calc("B", Y);
-        this.addSectionFixedResult(B, this.intlService.localizeText("INFO_GRANDEUR_B"), nDigits);
-
-        // périmètre hydraulique
-        let P = sect.Calc("P", Y);
-        this.addSectionFixedResult(P, this.intlService.localizeText("INFO_GRANDEUR_P"), nDigits);
-
-        // surface hydraulique
-        let S = sect.Calc("S", Y);
-        this.addSectionFixedResult(S, this.intlService.localizeText("INFO_GRANDEUR_S"), nDigits);
-
-        // rayon hydraulique
-        let R = sect.Calc("R", Y);
-        this.addSectionFixedResult(R, this.intlService.localizeText("INFO_GRANDEUR_R"), nDigits);
-
-        // vitesse moyenne
-        let V = sect.Calc("V", Y);
-        this.addSectionFixedResult(V, this.intlService.localizeText("INFO_GRANDEUR_V"), nDigits);
-
-        // nombre de Froude
-        let Fr = sect.Calc("Fr", Y);
-        this.addSectionFixedResult(Fr, this.intlService.localizeText("INFO_GRANDEUR_FR"), nDigits);
-
-        // tirant d'eau critique
-        let Yc = sect.Calc("Yc", Y);
-        this.addSectionFixedResult(Yc, this.intlService.localizeText("INFO_GRANDEUR_YC"), nDigits, "Yc");
-
-        // tirant d'eau normal
-        let Yn = sect.Calc("Yn", Y);
-        this.addSectionFixedResult(Yn, this.intlService.localizeText("INFO_GRANDEUR_YN"), nDigits, "Yn");
-
-        // tirant d'eau fluvial
-        let Yf = sect.Calc("Yf", Y);
-        this.addSectionFixedResult(Yf, this.intlService.localizeText("INFO_GRANDEUR_YF"), nDigits, "Yf");
-
-        // tirant d'eau torrentiel
-        let Yt = sect.Calc("Yt", Y);
-        this.addSectionFixedResult(Yt, this.intlService.localizeText("INFO_GRANDEUR_YT"), nDigits, "Yt");
-
-        // tirant d'eau conjugué
-        let Yco = sect.Calc("Yco", Y);
-        this.addSectionFixedResult(Yco, this.intlService.localizeText("INFO_GRANDEUR_YCO"), nDigits, "Yco");
-
-        // perte de charge
-        let J = sect.Calc("J", Y);
-        this.addSectionFixedResult(J, this.intlService.localizeText("INFO_GRANDEUR_J"), nDigits);
-
-        // Variation linéaire de l'énergie spécifique
-        let IJ = sect.Calc("I-J", Y);
-        this.addSectionFixedResult(IJ, this.intlService.localizeText("INFO_GRANDEUR_I-J"), nDigits);
-
-        // impulsion hydraulique
-        let Imp = sect.Calc("Imp", Y);
-        this.addSectionFixedResult(Imp, this.intlService.localizeText("INFO_GRANDEUR_IMP"), nDigits);
-
-        // contrainte de cisaillement
-        let Tau0 = sect.Calc("Tau0", Y);
-        this.addSectionFixedResult(Tau0, this.intlService.localizeText("INFO_GRANDEUR_TAU0"), nDigits);
-
-        this.sectionResultsComponent.section = sect;
-        this._showResultsSection = true;
-    }
-
-    private addRemousResult(x: string, f: number, t: number, e: number, nDigits: number) {
-        let flu = f == undefined ? "" : f.toFixed(nDigits);
-        let tor = t == undefined ? "" : t.toFixed(nDigits);
-        let extra = e == undefined ? "" : e.toFixed(nDigits);
-        this.remousResultsComponent.addResult(x, flu, tor, extra);
-    }
-
-    private doComputeRemous() {
-        this.remousResultsComponent.reset();
-
-        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField("select_section", CalculatorType.CourbeRemous);
-        var np: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(this._formulaire, nodeType, false);
-
-        let sect: acSection = np[0];
-        let prmSect: ParamsEquation = np[1];
-
-        let prec: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Pr"); // précision
-        let nDigits: number = -Math.log10(prec);
-
-        let Yamont: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Yamont"); // tirant amont
-        let Yaval: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Yaval"); // tirant aval
-        let Dx: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Dx"); // pas de discrétisation
-        let Long: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Long"); // longueur du bief
-        let If: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "If"); // pente du fond
-        let YB: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "YB"); // hauteur de berge
-        let Yn: number = sect.Calc("Yn"); // hauteur normale
-        let Yc: number = sect.Calc("Yc"); // hauteur critique
-
-        // méthode de résolution
-
-        let msf: SelectField = <SelectField>this._formulaire.getFormulaireElementById("select_resolution");
-        let smeth: string = msf.getValue();
-        let methRes: MethodeResolution;
-        if (smeth == "select_resolution_trap")
-            methRes = MethodeResolution.Trapezes;
-        else if (smeth == "select_resolution_rk4")
-            methRes = MethodeResolution.RungeKutta4;
-        else if (smeth == "select_resolution_euler")
-            methRes = MethodeResolution.EulerExplicite;
-        else
-            throw "GenericCalculatorComponent.doComputeRemous() : type de méthode de résolution '" + smeth + "' inconnu";
-
-        // paramètre supplémentaire à calculer
-
-        let dsf: SelectField = <SelectField>this._formulaire.getFormulaireElementById("select_target");
-        let extraSymbol: string = this.removePrefix(dsf.getValue(), "select_target_");
-
-        // calcul
-
-        let prmCR: CourbeRemousParams = new CourbeRemousParams(sect, Yamont, Yaval, Long, Dx, methRes);
-        let cr = new CourbeRemous(prmCR);
-        let res = cr.calculRemous(extraSymbol == "none" ? undefined : extraSymbol);
-
-        // affichage du graphe
-
-        this.remousResultsComponent.setHauteurBerge(YB);
-        this.remousResultsComponent.setHauteurNormale(Yn);
-        this.remousResultsComponent.setHauteurCritique(Yc);
-        if (extraSymbol != "none") {
-            this.remousResultsComponent.extraParamLabel = dsf.selectedEntry.label;
-            this.remousResultsComponent.extraGraph = ["Hs", "Hsc", "Yf", "Yt", "Yco"].indexOf(extraSymbol) == -1;
-        }
-        else
-            this.remousResultsComponent.extraGraph = false;
-
-        // affichage du journal
-
-        for (let l of cr.log.messages)
-            this.remousResultsComponent.addLogEntry(l, nDigits);
-
-        // affichage des resultats numériques
-
-        let kFlu = Object.keys(res.flu);
-        let kTor = Object.keys(res.tor);
-        for (let i = 0; i < res.trX.length; i++) {
-            let x: string = res.trX[i];
-            let f = res.flu[x];
-            let t = res.tor[x];
-            let e = res.tRes[x];
-            this.addRemousResult(x, f, t, e, nDigits);
-        }
-
-        this.remousResultsComponent.generateGraph(If);
-        this._showResultsRemous = true;
     }
 
     private doCompute() {
-        this._showResultsFixVar = false;
-        this._showResultsSection = false;
-        this._showResultsRemous = false;
-
-        if (this._formulaire.calculatorType == CalculatorType.SectionParametree) {
-            this.doComputeSection();
-            return;
-        }
-
-        if (this._formulaire.calculatorType == CalculatorType.CourbeRemous) {
-            this.doComputeRemous();
-            return;
-        }
-
-        let np: [Nub, ParamsEquation];
-        let nub: Nub;
-        let prms: ParamsEquation;
-        let rg: boolean = this._formulaire.calculatorType == CalculatorType.RegimeUniforme;
-        if (rg) {
-            let snp: [acSection, ParamsEquation] = this.formulaireService.getSectionNubAndParameters(this._formulaire, this._nodeType);
-            nub = new RegimeUniforme(snp[0]);
-            prms = snp[1];
-        }
-        else {
-            np = this.formulaireService.getNubAndParameters(this._formulaire);
-            nub = np[0];
-            prms = np[1];
-        }
-
-        let prec: number = this.getParameterValue("Pr"); // précision
-        let nDigits: number = -Math.log10(prec);
-
-        let computedParam: NgParameter = this.getComputedParameter();
-
-        let varParam: NgParameter = this.getVariatedParameter();
-        this.resultsComponent.reset(varParam == undefined);
-        if (varParam == undefined) {
-            // pas de paramètre à varier
-
-            let res: Result = nub.Calc(computedParam.symbol, 0, prec);
-            if (res.ok) {
-                this.addFixedResults(nDigits, !rg);
-                this.resultsComponent.addFixedResult(computedParam, res.vCalc, nDigits, !rg);
-            }
-            else {
-                this.resultsComponent.addLogMessages(res.log);
-            }
-        }
-        else {
-            // il y a un paramètre à varier
-
-            this.addFixedResults(nDigits, !rg);
-            this.resultsComponent.setVariableParamHeaderFromParameter(varParam, !rg);
-            this.resultsComponent.setVariableResultHeaderFromParameter(computedParam);
-
-            let min: number = +varParam.minValue;
-            let max: number = +varParam.maxValue;
-            let step: number = +varParam.stepValue;
-
-            for (let val = min; val <= max; val += step) {
-                prms[varParam.symbol].v = val;
-
-                let res = nub.Calc(computedParam.symbol, 0, prec);
-                if (res.ok) {
-                    this.resultsComponent.addVarResult(val, res.vCalc, nDigits);
-                }
-                else {
-                    this.resultsComponent.addLogMessages(res.log);
-                }
-            }
-
-            this.resultsComponent.setGraphTitle(computedParam.symbol + " = f( " + varParam.symbol + " )");
-            this.resultsComponent.generateGraph();
-        }
-        this._showResultsFixVar = true;
-    }
-
-    private getComputeNodeTypeFromSection(sect: string, calc: CalculatorType): ComputeNodeType {
-        switch (calc) {
-            case CalculatorType.SectionParametree:
-                switch (sect) {
-                    case "trapez":
-                        return ComputeNodeType.SectionTrapeze;
-
-                    case "rect":
-                        return ComputeNodeType.SectionRectangle;
-
-                    case "circ":
-                        return ComputeNodeType.SectionCercle;
-
-                    case "puiss":
-                        return ComputeNodeType.SectionPuissance;
-
-                    default:
-                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
-                }
-
-            case CalculatorType.RegimeUniforme:
-                switch (sect) {
-                    case "trapez":
-                        return ComputeNodeType.RegimeUniformeTrapeze;
-
-                    case "rect":
-                        return ComputeNodeType.RegimeUniformeRectangle;
-
-                    case "circ":
-                        return ComputeNodeType.RegimeUniformeCercle;
-
-                    case "puiss":
-                        return ComputeNodeType.RegimeUniformePuissance;
-
-                    default:
-                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
-                }
-
-            case CalculatorType.CourbeRemous:
-                switch (sect) {
-                    case "trapez":
-                        return ComputeNodeType.CourbeRemousTrapeze;
-
-                    case "rect":
-                        return ComputeNodeType.CourbeRemousRectangle;
-
-                    case "circ":
-                        return ComputeNodeType.CourbeRemousCercle;
-
-                    case "puiss":
-                        return ComputeNodeType.CourbeRemousPuissance;
-
-                    default:
-                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
-                }
-
-            default:
-                throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
-        }
-    }
-
-    private getResultsStyleDisplay() {
-        return this._showResultsFixVar ? "block" : "none";
-    }
-
-    private getSectionResultsStyleDisplay() {
-        return this._showResultsSection ? "block" : "none";
-    }
-
-    private getRemousResultsStyleDisplay() {
-        return this._showResultsRemous ? "block" : "none";
+        this._formulaire.doCompute();
+        this.resultsComponent.updateView();
     }
 
     private getFieldsetStyleDisplay(id: string) {
@@ -649,41 +144,22 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, Observer {
         if (sender instanceof InternationalisationService) {
             this.formulaireService.updateLocalisation();
         }
-    }
-
-    private removePrefix(s: string, prefix: string): string {
-        if (s.startsWith(prefix)) {
-            let l = prefix.length;
-            return s.substr(l, s.length - l);
-        }
-        return undefined;
-    }
-
-    private updateSectionType(val: string) {
-        //TODO ici on fait un cas particulier, il faudra sortir ça
-
-        let type: string = this.removePrefix(val, "select_section_");
-        if (type != undefined)
-            this._nodeType = this.getComputeNodeTypeFromSection(type, this._formulaire.calculatorType);
-    }
-
-    private getNodeTypeFromSelectField(selectFieldId: string, calcType: CalculatorType): ComputeNodeType {
-        let ssf: SelectField = <SelectField>this._formulaire.getFormulaireElementById(selectFieldId);
-        let val = ssf.getValue();
-        let type: string = this.removePrefix(val, "select_section_");
-        if (type != undefined)
-            return this.getComputeNodeTypeFromSection(type, calcType);
-
-        return undefined;
+        // else if (sender instanceof FormulaireService) {
+        //     switch (data["action"]) {
+        //         case "create":
+        //             const f: FormulaireDefinition = data["form"];
+        //             this._resultsStatus.addFormulaire(f.uid);
+        //             break;
+        //     }
+        // }
     }
 
     /**
      * réception d'un événement d'un select
      */
     private onSelectChanged(val: string) {
-        this.resultsComponent.reset(false);
-        this.sectionResultsComponent.reset();
-        this.updateSectionType(val);
-        this.applyDependencies();
+        this._formulaire.resetResults();
+        this._formulaire.updateNodeType();
+        this._formulaire.applyDependencies();
     }
 }
diff --git a/src/app/calculators/regime-uniforme/regime-uniforme.config.json b/src/app/calculators/regime-uniforme/regime-uniforme.config.json
index defd7d641..d8ff11945 100644
--- a/src/app/calculators/regime-uniforme/regime-uniforme.config.json
+++ b/src/app/calculators/regime-uniforme/regime-uniforme.config.json
@@ -167,6 +167,7 @@
     {
         "id": "options",
         "idCal": "Q",
-        "nodeType": "RegimeUniforme"
+        "nodeType": "RegimeUniforme",
+        "sectionSelectId": "select_section"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/remous/remous.config.json b/src/app/calculators/remous/remous.config.json
index c3e1da174..4399886be 100644
--- a/src/app/calculators/remous/remous.config.json
+++ b/src/app/calculators/remous/remous.config.json
@@ -258,6 +258,7 @@
     },
     {
         "id": "options",
-        "nodeType": "CourbeRemous"
+        "nodeType": "CourbeRemous",
+        "sectionSelectId": "select_section"
     }
 ]
\ No newline at end of file
diff --git a/src/app/calculators/section-param/section-param.config.json b/src/app/calculators/section-param/section-param.config.json
index 912775953..fa20969c3 100644
--- a/src/app/calculators/section-param/section-param.config.json
+++ b/src/app/calculators/section-param/section-param.config.json
@@ -270,6 +270,7 @@
     },
     {
         "id": "options",
-        "nodeType": "SectionParametree"
+        "nodeType": "SectionParametree",
+        "sectionSelectId": "select_section"
     }
 ]
\ No newline at end of file
diff --git a/src/app/components/calculator-results/calculator-results.component.html b/src/app/components/calculator-results/calculator-results.component.html
index 6bcceae97..713002566 100644
--- a/src/app/components/calculator-results/calculator-results.component.html
+++ b/src/app/components/calculator-results/calculator-results.component.html
@@ -1,51 +1,7 @@
 <div class="row">
     <div class="col-12 mx-auto">
-        <log></log>
-    </div>
-</div>
-
-<div class="row">
-    <div class="col-8 mx-auto">
-        <chart *ngIf="showVarResults" [type]="graph_type" [data]="graph_data" [options]="graph_options">
-        </chart>
-    </div>
-</div>
-
-<!-- 
-    classe conditionnelle :
-    [ngClass]="(condition) ? 'classe-si-vrai' : 'classe-si-faux'"
- -->
-
-<div class="row">
-    <!-- table des résultats fixés -->
-    <div [ngClass]="(showVarResults) ? 'col-xs-12 col-sm-10 col-lg-8 col-xl-6 mx-auto':'col-xs-10 col-md-8 col-lg-6 col-xl-5 mx-auto'">
-        <div class="row">
-            <div [ngClass]="(showVarResults) ? 'col-6':'col-12'" *ngIf="showFixedResults">
-                <table class="table" style="border: 1px solid rgb(230,230,230);">
-                    <tr>
-                        <th class="result_center">{{uitextParamFixes}}</th>
-                        <th class="result_center">{{uitextValeurs}}</th>
-                    </tr>
-                    <tr *ngFor="let r of _fixedResults; let i=index">
-                        <td class="result_right {{getFixedResultClass(i)}}">{{r.label}}</td>
-                        <td class="result_center {{getFixedResultClass(i)}}">{{r.value}}</td>
-                    </tr>
-                </table>
-            </div>
-
-            <!-- table des résultats variés -->
-            <div class="col-6" *ngIf="showVarResults">
-                <table class="table table-striped" style="border: 1px solid rgb(230,230,230);">
-                    <tr>
-                        <th class="result_center">{{_variableParamHeader}}</th>
-                        <th class="result_center">{{_variableResultHeader}}</th>
-                    </tr>
-                    <tr *ngFor="let r of _varResults; let i=index">
-                        <td class="result_center">{{r.param}}</td>
-                        <td class="result_center">{{r.result}}</td>
-                    </tr>
-                </table>
-            </div>
-        </div>
+        <fixedvar-results [style.display]="getFixedVarResultsStyleDisplay()"> </fixedvar-results>
+        <section-results [style.display]="getSectionResultsStyleDisplay()"></section-results>
+        <remous-results [style.display]="getRemousResultsStyleDisplay()"></remous-results>
     </div>
 </div>
\ No newline at end of file
diff --git a/src/app/components/calculator-results/calculator-results.component.ts b/src/app/components/calculator-results/calculator-results.component.ts
index 8a6826456..9cb97804f 100644
--- a/src/app/components/calculator-results/calculator-results.component.ts
+++ b/src/app/components/calculator-results/calculator-results.component.ts
@@ -1,188 +1,57 @@
 import { Component, ViewChild } from "@angular/core";
 
-import { NgParameter } from "../../formulaire/ngparam";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
-import { LogComponent } from '../../components/log/log.component';
-import { cLog } from "jalhyd";
+import { FixedVarResultsComponent } from "../../components/fixedvar-results/fixedvar-results.component";
+import { SectionResultsComponent } from "../../components/section-results/section-results.component";
+import { RemousResultsComponent } from "../../components/remous-results/remous-results.component";
+import { FormulaireDefinition } from "../../formulaire/formulaire-definition";
 
 @Component({
     selector: "calc-results",
     templateUrl: "./calculator-results.component.html",
-    styles: [`
-    .result_right {
-        text-align: right;
-    }
-    .result_center {
-        text-align: center;
-    }
-    .result_id_0 {
-        background-color: #f0f0f0;
-    }
-    .result_id_1 {
-        background-color: #ffffff;
-    }
-    .result_id_2 {
-        font-weight: bolder;
-        background-color: #00d0ff;
-    }
-    `
-    ]
 })
 export class CalculatorResultsComponent {
-    /**
-     * résultats fixed (true) ou variés (false)
-     */
-    private _isFixed: boolean;
-
-    private _fixedResults: Object[] = [];
-
-    /**
-     * affichage de la table des résultats fixés
-     */
-    private get showFixedResults(): boolean {
-        return this._fixedResults.length > 0;
-    }
-
-    /**
-     * affichage de la table des résultats variés
-     */
-    private get showVarResults(): boolean {
-        return this._varResults.length > 0;
-    }
-
-    private _variableParamHeader: string;
-
-    private _variableResultHeader: string;
+    private _formulaire: FormulaireDefinition;
 
     /**
-     * tableau de valeurs de la table des résultats variés
+     * composant d'affichage des résultats de paramètres fixés/variables
      */
-    private _varResults: Object[] = [];
+    @ViewChild(FixedVarResultsComponent)
+    private fixedVarResultsComponent: FixedVarResultsComponent;
 
     /**
-     * tableau de valeurs du graphe des résultats variés
+     * composant d'affichage des résultats des sections paramétrées
      */
-    private _varGraph: number[] = [];
-
-    /*
-    * config du graphe
-    */
-    private graph_type = "line";
-    private graph_data = {};
-    private graph_options = {
-        responsive: true,
-        maintainAspectRatio: true,
-        animation: {
-            duration: 0
-        },
-        legend: {
-            display: false
-        },
-        title: {
-            display: true,
-            text: ""
-        }
-    };
+    @ViewChild(SectionResultsComponent)
+    private sectionResultsComponent: SectionResultsComponent;
 
     /**
-     * composant journal
+     * composant d'affichage des résultats des courbes de remous
      */
-    @ViewChild(LogComponent)
-    private logComponent: LogComponent;
-
-    constructor(private intlService: InternationalisationService) {
-    }
-
-    private get uitextParamFixes() {
-        return this.intlService.localizeText("INFO_CALCULATOR_PARAMFIXES");
-    }
-
-    private get uitextValeurs() {
-        return this.intlService.localizeText("INFO_CALCULATOR_VALEURS");
-    }
-
-    public addFixedResult(p: NgParameter, v: number, fixedPrec: number, displaySymbol: boolean) {
-        this._fixedResults.push({ "label": this.paramLabel(p, displaySymbol), "value": v.toFixed(fixedPrec) });
-    }
-
-    public addVarResult(paramVal: number, resVal: number, fixedPrec: number) {
-        this._varResults.push({ "param": paramVal.toFixed(fixedPrec), "result": resVal.toFixed(fixedPrec) });
-        this._varGraph.push(resVal);
-    }
-
-    public addLogMessages(l: cLog) {
-        this.logComponent.addLogEntries(l);
-    }
-
-    public generateGraph() {
-        let labs = [];
-        let dat = [];
-        for (let i in this._varResults) {
-            let r = this._varResults[i];
-            labs.push(r["param"]);
-            dat.push(this._varGraph[i]);
-        }
-
-        this.graph_data = {
-            labels: labs,
-            datasets: [
-                {
-                    label: "",
-                    data: dat
-                }
-            ]
-        };
-    }
-
-    public reset(fixed: boolean) {
-        this._isFixed = fixed;
-        this._fixedResults = [];
-        this._varResults = [];
-        this._varGraph = [];
-        this._variableParamHeader = undefined;
-        this._variableResultHeader = undefined;
-        this.graph_options.title.text = undefined;
-        this.logComponent.reset();
-    }
-
-    public setVariableParamHeader(h: string) {
-        this._variableParamHeader = h;
-    }
-
-    public setVariableParamHeaderFromParameter(p: NgParameter, displaySymbol: boolean) {
-        this._variableParamHeader = this.paramLabel(p, displaySymbol);
-    }
+    @ViewChild(RemousResultsComponent)
+    private remousResultsComponent: RemousResultsComponent;
 
-    public setVariableResultHeader(h: string) {
-        this._variableResultHeader = h;
+    public set formulaire(f: FormulaireDefinition) {
+        this._formulaire = f;
+        this.fixedVarResultsComponent.results = f.fixVarResults;
+        this.sectionResultsComponent.results = f.sectionResults;
+        this.remousResultsComponent.results = f.remousResults;
     }
 
-    public setVariableResultHeaderFromParameter(p: NgParameter) {
-        this._variableResultHeader = this.paramLabel(p, true);
+    public updateView() {
+        this.fixedVarResultsComponent.updateView();
+        this.sectionResultsComponent.updateView();
+        this.remousResultsComponent.updateView();
     }
 
-    public setGraphTitle(t: string) {
-        this.graph_options.title.text = t;
+    private getFixedVarResultsStyleDisplay() {
+        return this._formulaire != undefined && this._formulaire.hasFixVarResults() ? "block" : "none";
     }
 
-    private paramLabel(p: NgParameter, displaySymbol: boolean): string {
-        let res = "";
-        if (displaySymbol)
-            res += p.symbol;
-        if (p.label != undefined && p.label != "")
-            if (p.symbol != p.label || !displaySymbol) {
-                if (res.length > 0)
-                    res += ":";
-                res += p.label;
-            }
-        if (p.unit != undefined && p.unit != "")
-            res += " (" + p.unit + ")";
-        return res;
+    private getSectionResultsStyleDisplay() {
+        return this._formulaire != undefined && this._formulaire.hasSectionResults() ? "block" : "none";
     }
 
-    private getFixedResultClass(i: number) {
-        if (this._isFixed && i == this._fixedResults.length - 1)
-            return "font-weight-bold";
-        return "result_id_" + String(i & 1);
+    private getRemousResultsStyleDisplay() {
+        return this._formulaire != undefined && this._formulaire.hasRemousResults() ? "block" : "none";
     }
 }
diff --git a/src/app/components/canvas/canvas.component.ts b/src/app/components/canvas/canvas.component.ts
index 540b5401e..7191256dc 100644
--- a/src/app/components/canvas/canvas.component.ts
+++ b/src/app/components/canvas/canvas.component.ts
@@ -45,7 +45,8 @@ export class CalcCanvasComponent implements AfterViewInit {
     }
 
     public clear() {
-        this._context2d.clearRect(0, 0, this.width, this.height);
+        if (this._context2d != undefined)
+            this._context2d.clearRect(0, 0, this.width, this.height);
     }
 
     public setStrokeColor(r: number, g: number, b: number) {
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.html b/src/app/components/fixedvar-results/fixedvar-results.component.html
new file mode 100644
index 000000000..974963c29
--- /dev/null
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.html
@@ -0,0 +1,51 @@
+<div class="row">
+    <div class="col-12 mx-auto">
+        <log></log>
+    </div>
+</div>
+
+<div class="row">
+    <div class="col-8 mx-auto">
+        <chart *ngIf="showVarResults" [type]="graph_type" [data]="graph_data" [options]="graph_options">
+        </chart>
+    </div>
+</div>
+
+<!-- 
+    classe conditionnelle :
+    [ngClass]="(condition) ? 'classe-si-vrai' : 'classe-si-faux'"
+ -->
+
+<div class="row">
+    <!-- table des résultats fixés -->
+    <div [ngClass]="(showVarResults) ? 'col-xs-12 col-sm-10 col-lg-8 col-xl-6 mx-auto':'col-xs-10 col-md-8 col-lg-6 col-xl-5 mx-auto'">
+        <div class="row">
+            <div [ngClass]="(showVarResults) ? 'col-6':'col-12'" *ngIf="showFixedResults">
+                <table class="table" style="border: 1px solid rgb(230,230,230);">
+                    <tr>
+                        <th class="result_center">{{uitextParamFixes}}</th>
+                        <th class="result_center">{{uitextValeurs}}</th>
+                    </tr>
+                    <tr *ngFor="let r of _fixedResults; let i=index">
+                        <td class="result_right {{getFixedResultClass(i)}}">{{r.label}}</td>
+                        <td class="result_center {{getFixedResultClass(i)}}">{{r.value}}</td>
+                    </tr>
+                </table>
+            </div>
+
+            <!-- table des résultats variés -->
+            <div class="col-6" *ngIf="showVarResults">
+                <table class="table table-striped" style="border: 1px solid rgb(230,230,230);">
+                    <tr>
+                        <th class="result_center">{{_results.variableParamHeader}}</th>
+                        <th class="result_center">{{_results.variableResultHeader}}</th>
+                    </tr>
+                    <tr *ngFor="let r of _varResults; let i=index">
+                        <td class="result_center">{{r.param}}</td>
+                        <td class="result_center">{{r.result}}</td>
+                    </tr>
+                </table>
+            </div>
+        </div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/src/app/components/fixedvar-results/fixedvar-results.component.ts b/src/app/components/fixedvar-results/fixedvar-results.component.ts
new file mode 100644
index 000000000..e72a63855
--- /dev/null
+++ b/src/app/components/fixedvar-results/fixedvar-results.component.ts
@@ -0,0 +1,212 @@
+import { Component, ViewChild } from "@angular/core";
+
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { ApplicationSetupService } from '../../services/app-setup/app-setup.service';
+import { LogComponent } from '../../components/log/log.component';
+import { cLog } from "jalhyd";
+import { FixedVarResults } from "../../results/fixed-var-results";
+
+@Component({
+    selector: "fixedvar-results",
+    templateUrl: "./fixedvar-results.component.html",
+    styles: [`
+    .result_right {
+        text-align: right;
+    }
+    .result_center {
+        text-align: center;
+    }
+    .result_id_0 {
+        background-color: #f0f0f0;
+    }
+    .result_id_1 {
+        background-color: #ffffff;
+    }
+    .result_id_2 {
+        font-weight: bolder;
+        background-color: #00d0ff;
+    }
+    `
+    ]
+})
+export class FixedVarResultsComponent {
+    /**
+     * résultats non mis en forme
+     */
+    private _results: FixedVarResults;
+
+    /**
+     * résultats fixed mis en forme
+     */
+    private _fixedResults: Object[];
+
+    /**
+     * résultats fixed mis en forme
+     */
+    private _varResults: Object[];
+
+    /*
+    * config du graphe
+    */
+    private graph_type = "line";
+    private graph_data = {};
+    private graph_options = {
+        responsive: true,
+        maintainAspectRatio: true,
+        animation: {
+            duration: 0
+        },
+        legend: {
+            display: false
+        },
+        title: {
+            display: true,
+            text: ""
+        }
+    };
+
+    /**
+     * composant journal
+     */
+    @ViewChild(LogComponent)
+    private logComponent: LogComponent;
+
+    constructor(
+        private intlService: InternationalisationService,
+        private appSetupService: ApplicationSetupService
+    ) { }
+
+    public set results(r: FixedVarResults) {
+        this._results = r;
+        this.updateView();
+    }
+
+    public updateView() {
+        this._fixedResults = [];
+        this._varResults = [];
+        this.logComponent.log = undefined;
+        if (this._results != undefined)
+            this.generateResults();
+    }
+
+    private generateResults() {
+        const nDigits = this.appSetupService.displayDigits;
+        const prec = this.appSetupService.displayPrecision;
+
+        for (let r of this._results.fixedResults) {
+            let v = r["value"];
+            if (v > prec)
+                this._fixedResults.push({ "label": r["label"], "value": v.toFixed(nDigits) });
+            else
+                this._fixedResults.push({ "label": r["label"], "value": v });
+        }
+
+        for (let r of this._results.varResults)
+            this._varResults.push({ "param": r["param"].toFixed(nDigits), "result": r["result"].toFixed(nDigits) });
+
+        this.logComponent.log = this._results.log;
+        this.generateGraph();
+    }
+    /**
+     * affichage de la table des résultats fixés
+     */
+    private get showFixedResults(): boolean {
+        return this._results != undefined && this._results.hasFixedResults();
+    }
+
+    /**
+     * affichage de la table des résultats variés
+     */
+    private get showVarResults(): boolean {
+        return this._results != undefined && this._results.hasVarResults();
+    }
+
+    private get uitextParamFixes() {
+        return this.intlService.localizeText("INFO_CALCULATOR_PARAMFIXES");
+    }
+
+    private get uitextValeurs() {
+        return this.intlService.localizeText("INFO_CALCULATOR_VALEURS");
+    }
+
+    // public addFixedResult(p: NgParameter, v: number, fixedPrec: number, displaySymbol: boolean) {
+    //     this._fixedResults.push({ "label": this.paramLabel(p, displaySymbol), "value": v.toFixed(fixedPrec) });
+    // }
+
+    // public addVarResult(paramVal: number, resVal: number, fixedPrec: number) {
+    //     this._varResults.push({ "param": paramVal.toFixed(fixedPrec), "result": resVal.toFixed(fixedPrec) });
+    //     this._varGraph.push(resVal);
+    // }
+
+    // public addLogMessages(l: cLog) {
+    //     this.logComponent.addLogEntries(l);
+    // }
+
+    private generateGraph() {
+        let labs = [];
+        let dat = [];
+        for (let i in this._results.varResults) {
+            let r = this._results.varResults[i];
+            labs.push(r["param"]);
+            dat.push(this._results.varGraph[i]);
+        }
+
+        this.graph_options.title.text = this._results.graphTitle;
+
+        this.graph_data = {
+            labels: labs,
+            datasets: [
+                {
+                    label: "",
+                    data: dat
+                }
+            ]
+        };
+    }
+
+    // public reset(fixed: boolean) {
+    //     this._results.reset();
+    //     this.logComponent.reset();
+    // }
+
+    // public setVariableParamHeader(h: string) {
+    //     this._variableParamHeader = h;
+    // }
+
+    // public setVariableParamHeaderFromParameter(p: NgParameter, displaySymbol: boolean) {
+    //     this._variableParamHeader = this.paramLabel(p, displaySymbol);
+    // }
+
+    // public setVariableResultHeader(h: string) {
+    //     this._variableResultHeader = h;
+    // }
+
+    // public setVariableResultHeaderFromParameter(p: NgParameter) {
+    //     this._variableResultHeader = this.paramLabel(p, true);
+    // }
+
+    // public setGraphTitle(t: string) {
+    //     this.graph_options.title.text = t;
+    // }
+
+    // private paramLabel(p: NgParameter, displaySymbol: boolean): string {
+    //     let res = "";
+    //     if (displaySymbol)
+    //         res += p.symbol;
+    //     if (p.label != undefined && p.label != "")
+    //         if (p.symbol != p.label || !displaySymbol) {
+    //             if (res.length > 0)
+    //                 res += ":";
+    //             res += p.label;
+    //         }
+    //     if (p.unit != undefined && p.unit != "")
+    //         res += " (" + p.unit + ")";
+    //     return res;
+    // }
+
+    private getFixedResultClass(i: number) {
+        if (this._results.isFixed && i == this._results.fixedResults.length - 1)
+            return "font-weight-bold";
+        return "result_id_" + String(i & 1);
+    }
+}
diff --git a/src/app/components/log/log.component.ts b/src/app/components/log/log.component.ts
index 51770ede9..43fb04717 100644
--- a/src/app/components/log/log.component.ts
+++ b/src/app/components/log/log.component.ts
@@ -4,6 +4,7 @@ import { Message, cLog } from 'jalhyd';
 
 import { NgParameter } from "../../formulaire/ngparam";
 import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
+import { ApplicationSetupService } from '../../services/app-setup/app-setup.service';
 
 
 @Component({
@@ -32,32 +33,39 @@ import { InternationalisationService } from "../../services/internationalisation
 })
 export class LogComponent {
     /**
-    * entrées du journal
+     * journal
+     */
+    private _log: cLog;
+
+    /**
+    * entrées du journal mises en forme
     */
-    private _logEntries: Object[];
+    private _logEntries: string[];
 
-    constructor(private intlService: InternationalisationService) {
-        this.reset();
-    }
+    constructor(
+        private intlService: InternationalisationService,
+        private appSetupService: ApplicationSetupService
+    ) { }
 
     private get uitextTitreJournal() {
         return this.intlService.localizeText("INFO_REMOUSRESULTS_TITREJOURNAL")
     }
 
     private get hasEntries(): boolean {
-        return this._logEntries.length != 0;
+        return this._logEntries != undefined && this._logEntries.length != 0;
     }
 
-    public reset() {
+    private generateEntries() {
         this._logEntries = [];
+        if (this._log != undefined) {
+            const nDigits = this.appSetupService.displayDigits;
+            for (let m of this._log.messages)
+                this._logEntries.push(this.intlService.localizeMessage(m, nDigits));
+        }
     }
 
-    public addLogEntry(m: Message, nDigits: number) {
-        this._logEntries.push(this.intlService.localizeMessage(m, nDigits));
-    }
-
-    public addLogEntries(l: cLog) {
-        for (let m of l.messages)
-            this._logEntries.push(this.intlService.localizeMessage(m));
+    public set log(log: cLog) {
+        this._log = log;
+        this.generateEntries();
     }
 }
diff --git a/src/app/components/remous-results/remous-results.component.html b/src/app/components/remous-results/remous-results.component.html
index 12d9846d0..6dd379021 100644
--- a/src/app/components/remous-results/remous-results.component.html
+++ b/src/app/components/remous-results/remous-results.component.html
@@ -5,7 +5,7 @@
 </div>
 <div class="row">
     <div class="col-10 mx-auto">
-        <chart *ngIf="_extraGraph" [type]="graph2_type" [data]="graph2_data" [options]="graph2_options"></chart>
+        <chart *ngIf="_results.extraGraph" [type]="graph2_type" [data]="graph2_data" [options]="graph2_options"></chart>
     </div>
 </div>
 
@@ -31,12 +31,12 @@
                 <tr>
                     <th>{{uitextAbscisse}}</th>
                     <th>{{uitextTirant}}</th>
-                    <th>{{_extraParamLabel}}</th>
+                    <th>{{_results.extraParamLabel}}</th>
                     <th>{{uitextTirant}}</th>
-                    <th>{{_extraParamLabel}}</th>
+                    <th>{{_results.extraParamLabel}}</th>
                 </tr>
             </thead>
-            <tr *ngFor="let r of _results; let i=index" [class]="getResultClass(i)">
+            <tr *ngFor="let r of _results.series; let i=index" [class]="getResultClass(i)">
                 <td>{{r.abs}}</td>
                 <td>{{r.flu}}</td>
                 <td>{{r.extraFlu}}</td>
diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts
index 2acb0ed44..49fd4c59e 100644
--- a/src/app/components/remous-results/remous-results.component.ts
+++ b/src/app/components/remous-results/remous-results.component.ts
@@ -4,6 +4,7 @@ import { Message } from 'jalhyd';
 
 import { InternationalisationService } from '../../services/internationalisation/internationalisation.service';
 import { LogComponent } from '../../components/log/log.component';
+import { RemousResults } from "../../results/remous-results";
 
 @Component({
     selector: 'remous-results',
@@ -30,36 +31,7 @@ import { LogComponent } from '../../components/log/log.component';
     ]
 })
 export class RemousResultsComponent {
-
-    /**
-     * hauteur de berge
-     */
-    private _hautBerge: number;
-
-    /**
-     * hauteur normale
-     */
-    private _hautNormale: number;
-
-    /**
-     * hauteur critique
-     */
-    private _hautCritique: number;
-
-    /**
-    * résultats numériques
-    */
-    private _results: Object[] = [];
-
-    /**
-     * présence d'un paramètre supplémentaire
-     */
-    private _hasExtra: boolean;
-
-    /**
-     * titre de la colonne du paramètre supplémentaire
-     */
-    private _extraParamLabel: string;
+    private _results: RemousResults;
 
     /*
     * config du graphe principal
@@ -75,18 +47,12 @@ export class RemousResultsComponent {
     private graph2_data = {};
     private graph2_options = {};
 
-    /**
-     * le paramètre supplémentaire est affiché dans un graphe séparé
-     */
-    private _extraGraph: boolean;
-
     /**
      * composant journal
      */
     @ViewChild(LogComponent)
     private logComponent: LogComponent;
 
-
     constructor(private intlService: InternationalisationService) {
     }
 
@@ -122,20 +88,21 @@ export class RemousResultsComponent {
         return this.intlService.localizeText("INFO_REMOUSRESULTS_TIRANTCRITIQUE")
     }
 
-    public reset() {
-        this._results = [];
-        this.logComponent.reset();
-        this._extraParamLabel = undefined;
-        this._hasExtra = false;
-        this._extraGraph = false;
+    public set results(r: RemousResults) {
+        this._results = r;
+        this.updateView();
+    }
+
+    public updateView() {
         this.graph1_data = {};
         this.graph1_options = {};
         this.graph2_data = {};
         this.graph2_options = {};
-    }
-
-    public set extraParamLabel(l: string) {
-        this._extraParamLabel = l;
+        this.logComponent.log = undefined;
+        if (this._results != undefined) {
+            this.logComponent.log = this._results.log;
+            this.generateGraph();
+        }
     }
 
     private searchObjectInArray(array: any[], key: string, value: any) {
@@ -159,7 +126,7 @@ export class RemousResultsComponent {
 
         let minXflu; // abscisse de début de la courbe fluviale
         for (let x of tX) {
-            let r = this.searchObjectInArray(this._results, "abs", x);
+            let r = this.searchObjectInArray(this._results.series, "abs", x);
             if (r["flu"] != "") {
                 minXflu = x;
                 break;
@@ -178,7 +145,7 @@ export class RemousResultsComponent {
         let maxXtor; // abscisse de fin de la courbe torrentielle
         tX.reverse();
         for (let x of tX) {
-            let r = this.searchObjectInArray(this._results, "abs", x);
+            let r = this.searchObjectInArray(this._results.series, "abs", x);
             if (r["tor"] != "") {
                 maxXtor = x;
                 break;
@@ -196,15 +163,17 @@ export class RemousResultsComponent {
         }
     }
 
-    public generateGraph(penteFond: number) {
+    private generateGraph() {
         // http://www.chartjs.org/docs/latest/charts/line.html
         // le dernier dataset de la liste datasets est dessiné en 1er
 
+        const penteFond: number = this._results.penteFond;
+
         // abscisses
 
         let labs: number[] = [];
         let xmax = -1e8;
-        for (let r of this._results) {
+        for (let r of this._results.series) {
             let x = +r["abs"];
             labs.push(x);
             xmax = Math.max(x, xmax);
@@ -213,33 +182,33 @@ export class RemousResultsComponent {
         // init graphiques
 
         let gr1 = new GraphData(labs, penteFond, xmax);
-        if (this._extraGraph)
+        if (this._results.extraGraph)
             var gr2 = new GraphData(labs, 0, xmax);
 
         // ligne de fond
         gr1.drawLine(0, 0, 3, "#753F00", this.uitextFond, "#753F00");
 
         // ligne de berge
-        gr1.drawLine(this._hautBerge, this._hautBerge, 4, "#C58F50", this.uitextBerge);
+        gr1.drawLine(this._results.hautBerge, this._results.hautBerge, 4, "#C58F50", this.uitextBerge);
 
         // hauteur normale
-        gr1.drawLine(this._hautNormale, this._hautNormale, 5, "#A4C537", this.uitextTirantNormal);
+        gr1.drawLine(this._results.hautNormale, this._results.hautNormale, 5, "#A4C537", this.uitextTirantNormal);
 
         // hauteur critique
-        gr1.drawLine(this._hautCritique, this._hautCritique, 6, "#FF0000", this.uitextTirantCritique);
+        gr1.drawLine(this._results.hautCritique, this._results.hautCritique, 6, "#FF0000", this.uitextTirantCritique);
 
         // lignes d'eau torrentielle et fluviale
 
         let lineFlu = gr1.newLine(0);
         let lineTor = gr1.newLine(1);
-        if (this._hasExtra) {
-            if (this._extraGraph)
+        if (this._results.hasExtra) {
+            if (this._results.extraGraph)
                 var lineExtra = gr2.newLine(2);
             else
                 lineExtra = gr1.newLine(2);
         }
 
-        for (let r of this._results) {
+        for (let r of this._results.series) {
             let x: number = +r["abs"];
             let yFlu: string = r["flu"];
             let yTor: string = r["tor"];
@@ -256,11 +225,11 @@ export class RemousResultsComponent {
                 lineExtra.mapPoint(x, +yExtra);
         }
 
-        if (this._hasExtra) {
-            if (this._extraGraph)
-                lineExtra.data = { label: this._extraParamLabel, tension: 0, spanGaps: true, borderColor: "#0093BD", pointRadius: 4 };
+        if (this._results.hasExtra) {
+            if (this._results.extraGraph)
+                lineExtra.data = { label: this._results.extraParamLabel, tension: 0, spanGaps: true, borderColor: "#0093BD", pointRadius: 4 };
             else
-                lineExtra.data = { label: this._extraParamLabel, tension: 0, fill: false, spanGaps: true, borderColor: "#C17AF0", pointRadius: 4 };
+                lineExtra.data = { label: this._results.extraParamLabel, tension: 0, fill: false, spanGaps: true, borderColor: "#C17AF0", pointRadius: 4 };
         }
 
         // raccordement ligne fluviale -> torrentielle pour dessiner le ressaut
@@ -291,7 +260,7 @@ export class RemousResultsComponent {
             }
         };
 
-        if (this._extraGraph) {
+        if (this._results.extraGraph) {
             this.graph2_data = gr2.data;
 
             this.graph2_options = {
@@ -313,34 +282,6 @@ export class RemousResultsComponent {
         }
     }
 
-    public addResult(x: string, flu: string, tor: string, extra: string) {
-        let extraFlu = (flu == "" ? "" : extra);
-        let extraTor = (tor == "" ? "" : extra);
-        if (extraFlu != "" || extraTor != "")
-            this._hasExtra = true;
-        this._results.push({ "abs": x, "flu": flu, "extraFlu": extraFlu, "tor": tor, "extraTor": extraTor });
-    }
-
-    public addLogEntry(m: Message, nDigits: number) {
-        this.logComponent.addLogEntry(m, nDigits);
-    }
-
-    public setHauteurBerge(v: number) {
-        this._hautBerge = v;
-    }
-
-    public setHauteurNormale(v: number) {
-        this._hautNormale = v;
-    }
-
-    public setHauteurCritique(v: number) {
-        this._hautCritique = v;
-    }
-
-    public set extraGraph(b: boolean) {
-        this._extraGraph = b;
-    }
-
     private getResultClass(i: number) {
         return "result_id_" + String(i & 1);
     }
diff --git a/src/app/components/section-results/section-results.component.ts b/src/app/components/section-results/section-results.component.ts
index 3ee4b7526..0ec90da17 100644
--- a/src/app/components/section-results/section-results.component.ts
+++ b/src/app/components/section-results/section-results.component.ts
@@ -3,6 +3,8 @@ import { Component, ViewChild } from '@angular/core';
 import { acSection } from 'jalhyd';
 
 import { SectionCanvasComponent } from '../section-canvas/section-canvas.component';
+import { SectionResults } from '../../results/section-results';
+import { ApplicationSetupService } from '../../services/app-setup/app-setup.service';
 
 @Component({
     selector: 'section-results',
@@ -30,9 +32,16 @@ import { SectionCanvasComponent } from '../section-canvas/section-canvas.compone
 })
 export class SectionResultsComponent {
     /**
-    * résultats
+    * résultats non mis en forme
     */
-    private _results: Object[] = [];
+    private _sectionResults: SectionResults;
+
+    /**
+     * résultats mis en forme
+     */
+    private _results: Object[];
+
+    constructor(private appSetupService: ApplicationSetupService) { }
 
     private static labelColors: { [key: string]: any; } = {
         "Hs": { r: 255, g: 0, b: 0 },
@@ -47,19 +56,33 @@ export class SectionResultsComponent {
     @ViewChild("sectionCanvas")
     private _sectionCanvas: SectionCanvasComponent;
 
-    public reset() {
+    public set results(r: SectionResults) {
+        this._sectionResults = r;
+        this.updateView();
+    }
+
+    public updateView() {
         this._results = [];
         this._sectionCanvas.reset();
+        if (this._sectionResults != undefined)
+            this.formatResults();
     }
 
-    public set section(section: acSection) {
-        this._sectionCanvas.section = section;
-    }
+    private formatResults() {
+        if (this._sectionResults.results.length > 0) {
+            const nDigits = this.appSetupService.displayDigits;
+
+            for (let r of this._sectionResults.results) {
+                const v = r["value"];
+                const l = r["label"];
+
+                this._results.push({ "label": l, "value": v.toFixed(nDigits) });
 
-    public addResult(v: number, l: string, fixedPrec: number, drawLabel: string) {
-        this._results.push({ "label": l, "value": v.toFixed(fixedPrec) });
-        if (drawLabel != undefined) {
-            this._sectionCanvas.addLevel(v, drawLabel + " = " + v.toFixed(fixedPrec), SectionResultsComponent.labelColors[drawLabel]);
+                const drawLabel = r["drawLabel"];
+                if (drawLabel != undefined)
+                    this._sectionCanvas.addLevel(v, drawLabel + " = " + v.toFixed(nDigits), SectionResultsComponent.labelColors[drawLabel]);
+            }
+            this._sectionCanvas.section = this._sectionResults.section;
         }
     }
 
diff --git a/src/app/formulaire/formulaire-definition.ts b/src/app/formulaire/formulaire-definition.ts
index 782a500a3..94154fb25 100644
--- a/src/app/formulaire/formulaire-definition.ts
+++ b/src/app/formulaire/formulaire-definition.ts
@@ -1,8 +1,13 @@
-import { ComputeNodeType } from "jalhyd";
+import { ComputeNodeType, ParamsEquation, Nub, acSection, RegimeUniforme, MethodeResolution, CourbeRemousParams, CourbeRemous } from "jalhyd";
+import { ParamsSectionRectang, cSnRectang, ParamsSectionCirc, cSnCirc, ParamsSectionPuiss, cSnPuiss, Result } from "jalhyd";
+import { ConduiteDistrib, ConduiteDistribParams, LechaptCalmon, LechaptCalmonParams, ParamsSectionTrapez, cSnTrapez } from "jalhyd";
+
 
 import { ParamService } from "../services/param/param.service";
+import { InternationalisationService } from "../services/internationalisation/internationalisation.service";
 import { Field } from "./field";
 import { NgParameter, ParamRadioConfig } from "./ngparam";
+import { InputField } from "./input-field";
 import { CheckField } from "./check-field";
 import { SelectField } from "./select-field";
 import { SelectEntry } from "./select-entry";
@@ -13,6 +18,9 @@ import { FormulaireElement } from "./formulaire-element";
 import { ValueDependency } from "./value-dependency";
 import { ValueDependencyCondition } from "./value-dependency-condition";
 import { ExistenceDependency } from "./existence-dependency";
+import { FixedVarResults } from "../results/fixed-var-results";
+import { SectionResults } from "../results/section-results";
+import { RemousResults } from "../results/remous-results";
 
 
 export enum CalculatorType {
@@ -35,13 +43,45 @@ export class FormulaireDefinition {
 
     private _dependencies: Dependency[] = [];
 
+    /**
+     * id du SelectField configurant le type de section
+     */
+    private _sectionSelectFieldId: string;
+
     /**
      * type de noeud de calcul par défaut
      */
     private _defaultNodeType: ComputeNodeType;
 
-    constructor(private paramService: ParamService, private _calcType: CalculatorType) {
+    /**
+     * type de noeud de calcul actuel (utilisé pour les sections)
+     */
+    private _nodeType: ComputeNodeType;
+
+    /**
+     * résultats fixes/variables
+     */
+    private _fixVarResults: FixedVarResults;
+
+    /**
+     * résultats de section paramétrée
+     */
+    private _sectionResults: SectionResults;
+
+    /**
+     * résultats de courbes de remous
+     */
+    private _remousResults: RemousResults;
+
+    constructor(
+        private _calcType: CalculatorType,
+        private paramService: ParamService,
+        private intlService: InternationalisationService
+    ) {
         this._uid = FormulaireDefinition.uidGenerator();
+        this._fixVarResults = new FixedVarResults();
+        this._sectionResults = new SectionResults();
+        this._remousResults = new RemousResults();
     }
 
     public get calculatorType(): CalculatorType {
@@ -65,11 +105,27 @@ export class FormulaireDefinition {
         return this._uid;
     }
 
-    public getFieldSets(nodeType: ComputeNodeType): FieldSet[] {
+    /**
+     * /id unique
+     */
+
+    public get fixVarResults() {
+        return this._fixVarResults;
+    }
+
+    public get sectionResults() {
+        return this._sectionResults;
+    }
+
+    public get remousResults() {
+        return this._remousResults;
+    }
+
+    public getFieldSets(): FieldSet[] {
         let res: FieldSet[] = [];
 
         for (let fs of this._fieldSets)
-            if (fs.computeNodeType == nodeType || fs.computeNodeType == this._defaultNodeType)
+            if (fs.computeNodeType == this._nodeType || fs.computeNodeType == this._defaultNodeType)
                 res.push(fs);
 
         return res;
@@ -110,10 +166,95 @@ export class FormulaireDefinition {
     }
 
 
+    private removePrefix(s: string, prefix: string): string {
+        if (s.startsWith(prefix)) {
+            let l = prefix.length;
+            return s.substr(l, s.length - l);
+        }
+        return undefined;
+    }
+
+    public updateNodeType() {
+        this._nodeType = this.getNodeTypeFromSelectField(this._calcType);
+    }
+
+    private getNodeTypeFromSelectField(calcType: CalculatorType): ComputeNodeType {
+        if (this._sectionSelectFieldId != undefined) {
+            let ssf: SelectField = <SelectField>this.getFormulaireElementById(this._sectionSelectFieldId);
+            let val = ssf.getValue();
+            let type: string = this.removePrefix(val, this._sectionSelectFieldId + "_");
+            if (type != undefined)
+                return this.getComputeNodeTypeFromSection(type, calcType);
+        }
+
+        return undefined;
+    }
+
+    private getComputeNodeTypeFromSection(sect: string, calc: CalculatorType): ComputeNodeType {
+        switch (calc) {
+            case CalculatorType.SectionParametree:
+                switch (sect) {
+                    case "trapez":
+                        return ComputeNodeType.SectionTrapeze;
+
+                    case "rect":
+                        return ComputeNodeType.SectionRectangle;
+
+                    case "circ":
+                        return ComputeNodeType.SectionCercle;
+
+                    case "puiss":
+                        return ComputeNodeType.SectionPuissance;
+
+                    default:
+                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
+                }
+
+            case CalculatorType.RegimeUniforme:
+                switch (sect) {
+                    case "trapez":
+                        return ComputeNodeType.RegimeUniformeTrapeze;
+
+                    case "rect":
+                        return ComputeNodeType.RegimeUniformeRectangle;
+
+                    case "circ":
+                        return ComputeNodeType.RegimeUniformeCercle;
+
+                    case "puiss":
+                        return ComputeNodeType.RegimeUniformePuissance;
+
+                    default:
+                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
+                }
+
+            case CalculatorType.CourbeRemous:
+                switch (sect) {
+                    case "trapez":
+                        return ComputeNodeType.CourbeRemousTrapeze;
+
+                    case "rect":
+                        return ComputeNodeType.CourbeRemousRectangle;
+
+                    case "circ":
+                        return ComputeNodeType.CourbeRemousCercle;
+
+                    case "puiss":
+                        return ComputeNodeType.CourbeRemousPuissance;
+
+                    default:
+                        throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
+                }
+
+            default:
+                throw "getComputeNodeTypeFromSection() : section " + sect + " / type de calculette " + CalculatorType[calc] + " non pris en charge"
+        }
+    }
+
     /**
-     * remet tous les paramètres à FIX sauf "me" et ceux (celui) à l'état "except"
+     * remet les radios de tous les paramètres à FIX sauf "me" et ceux (celui) à l'état "except"
      */
-    public resetOther(me: NgParameter, except: ParamRadioConfig) {
+    private resetOtherRadio(me: NgParameter, except: ParamRadioConfig) {
         for (let fs of this._fieldSets) {
             for (let p of fs.fields) {
                 if (p instanceof NgParameter)
@@ -123,15 +264,72 @@ export class FormulaireDefinition {
         }
     }
 
+    /**
+     * modifie les boutons radio "fix", "var", "cal" de tous les paramètres
+     * en fonction de la modification de l'état d'un des paramètres
+     * @param symbol symbole du paramètre source
+     * @param option nouvel état "fix", "var" ou "cal" du paramètre source
+     */
+    public resetRadiosAndResults(symbol: string, option: string) {
+        let sourceParam = this.getParamFromSymbol(symbol);
+        let oldState: ParamRadioConfig = sourceParam.radioState;
+        let newState: ParamRadioConfig = ParamRadioConfig[option.toUpperCase()];
+
+        switch (oldState) {
+            case ParamRadioConfig.FIX:
+                switch (newState) {
+                    case ParamRadioConfig.VAR:
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
+                        break;
+
+                    case ParamRadioConfig.CAL:
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
+                        break;
+                }
+                break;
+
+            case ParamRadioConfig.VAR:
+                switch (newState) {
+                    case ParamRadioConfig.CAL:
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.VAR);
+                        break;
+                }
+                break;
+
+            case ParamRadioConfig.CAL:
+                switch (newState) {
+                    case ParamRadioConfig.FIX:
+                        this.setDefault();
+                        break;
+
+                    case ParamRadioConfig.VAR:
+                        this.resetOtherRadio(sourceParam, ParamRadioConfig.CAL);
+                        this.setDefault();
+                        break;
+                }
+        }
+
+        sourceParam.radioState = newState;
+
+        this.resetResults();
+        this.applyDependencies();
+    }
+
+    public resetResults() {
+        this._fixVarResults.reset();
+        this._sectionResults.reset();
+        this._remousResults.reset();
+    }
+
     /**
      * met le paramètre par défaut à CAL
      */
-    public setDefault() {
+    private setDefault() {
         let defaultParamCal = this.getParamFromSymbol(this._defaultCalculatedParam);
         defaultParamCal.radioState = ParamRadioConfig.CAL;
     }
 
-    public getInputParameters(): NgParameter[] {
+    private getInputParameters(): NgParameter[] {
         let res = [];
         for (let fs of this._fieldSets)
             for (let p of fs.fields)
@@ -140,7 +338,7 @@ export class FormulaireDefinition {
         return res;
     }
 
-    public getDisplayedInputParameters(): NgParameter[] {
+    private getDisplayedInputParameters(): NgParameter[] {
         let res = [];
         for (let fs of this._fieldSets)
             if (fs.isDisplayed)
@@ -150,7 +348,7 @@ export class FormulaireDefinition {
         return res;
     }
 
-    public getNodeParameterValue(nodeType: ComputeNodeType, symbol: string): number {
+    private getNodeParameterValue(nodeType: ComputeNodeType, symbol: string): number {
         for (let fs of this._fieldSets) {
             for (let p of fs.fields) {
                 if (p instanceof NgParameter)
@@ -174,10 +372,20 @@ export class FormulaireDefinition {
         throw "Formulaire.getNodeParameterValue() : pas de paramètre " + symbol + " trouvé pour le noeud " + ComputeNodeType[nodeType] + "(" + nodeType + ")";
     }
 
-    public getParameterValue(symbol: string): number {
+    private getParameterValue(symbol: string): number {
         return this.getNodeParameterValue(this._defaultNodeType, symbol);
     }
 
+    private getSectionComputedParam(): { symbol: string, label: string } {
+        let targetSelect: SelectField = <SelectField>this.getFieldById("select_target");
+        let targetValue: string = targetSelect.getValue();
+
+        let symbol: string = this.removePrefix(targetValue, "select_target_");
+
+        let label = targetSelect.getLabel();
+        return { symbol, label };
+    }
+
     public getFormulaireElementById(id: string): FormulaireElement {
         for (let fs of this._fieldSets) {
             if (fs.id == id)
@@ -190,7 +398,7 @@ export class FormulaireDefinition {
         return undefined;
     }
 
-    public getFieldById(id: string): Field {
+    private getFieldById(id: string): Field {
         let res = this.getFormulaireElementById(id);
         if (res instanceof Field)
             return res;
@@ -364,12 +572,16 @@ export class FormulaireDefinition {
             // options globales
             else if (conf_id === "options") {
                 // id du paramètre à calculer par défaut
+
                 this._defaultCalculatedParam = conf["idCal"];
                 if (this._defaultCalculatedParam != undefined) {
                     let p = this.getParamFromSymbol(this._defaultCalculatedParam);
                     p.isDefault = true;
                     p.radioState = ParamRadioConfig.CAL;
                 }
+
+                // id du SelectField configurant le type de section
+                this._sectionSelectFieldId = this.getOption("sectionSelectId");
             }
         }
 
@@ -430,6 +642,434 @@ export class FormulaireDefinition {
         }
     }
 
+    private isMySectionType(nodeType: ComputeNodeType, strict: boolean) {
+        let res: boolean = nodeType == this._nodeType;
+        if (strict)
+            return res;
+
+        return res || nodeType == ComputeNodeType.SectionParametree
+    }
+
+    private addFixedResults(displaySymbol: boolean) {
+        for (let p of this.getInputParameters())
+            if (p.radioState == ParamRadioConfig.FIX && p.symbol !== "Pr")
+                this._fixVarResults.addFixedResult(p, p.getValue(), displaySymbol);
+    }
+
+    private addSectionFixedResult(val: number, label: string, drawLabel: string = undefined) {
+        this._sectionResults.addResult(val, label, drawLabel);
+    }
+
+    private addSectionFixedResults(displaySymbol: boolean) {
+        for (let p of this.getDisplayedInputParameters())
+            if (p.radioState == ParamRadioConfig.FIX && this.isMySectionType(p.computeNodeType, false) && p.symbol !== "Pr")
+                this._fixVarResults.addFixedResult(p, p.getValue(), displaySymbol);
+    }
+
+    private getSectionVariatedParameter(): NgParameter {
+        let res: NgParameter = this.getParamFromState(ParamRadioConfig.VAR);
+        if (res != undefined && this.isMySectionType(res.computeNodeType, false))
+            return res;
+        return undefined;
+    }
+
+    private doComputeSectionVar(varParam: NgParameter) {
+        let computePrec: number = this.getParameterValue("Pr"); // précision de calcul
+        let nDigits = -Math.log10(computePrec);
+
+        this.addSectionFixedResults(false);
+
+        let computedParam = this.getSectionComputedParam();
+
+        this._fixVarResults.setVariableParamHeaderFromParameter(varParam, false);
+        this._fixVarResults.setVariableResultHeader(computedParam["label"]);
+
+        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField(CalculatorType.SectionParametree);
+
+        var np: [acSection, ParamsEquation] = this.getSectionNubAndParameters(nodeType);
+        let sect: acSection = np[0];
+        let prms: ParamsEquation = np[1];
+
+        this._sectionResults.section = sect;
+
+        let min: number = +varParam.minValue;
+        let max: number = +varParam.maxValue;
+        let step: number = +varParam.stepValue;
+
+        let yField: InputField = <InputField>this.getFormulaireElementById("Y");
+        let Y: number = yField.getValue();
+
+        let compSymbol = computedParam["symbol"];
+        for (let val = min; val <= max; val += step) {
+            prms[varParam.symbol].v = val;
+
+            sect.Reset(true);
+            let res = sect.Calc(compSymbol, Y);
+            this._fixVarResults.addVarResult(val, res);
+        }
+
+        this._fixVarResults.graphTitle = computedParam.symbol + " = f( " + varParam.symbol + " )";
+    }
+
+    private doComputeSection() {
+        let varParam = this.getSectionVariatedParameter();
+        if (varParam != undefined) {
+            this.doComputeSectionVar(varParam);
+            return;
+        }
+
+        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField(CalculatorType.SectionParametree);
+        var np: [acSection, ParamsEquation] = this.getSectionNubAndParameters(nodeType);
+
+        let sect: acSection = np[0];
+        let prms: ParamsEquation = np[1];
+
+        this._sectionResults.section = sect;
+
+        let computePrec: number = this.getNodeParameterValue(ComputeNodeType.SectionParametree, "Pr"); // précision de calcul
+        let nDigits = -Math.log10(computePrec);
+
+        let Y = prms.map.Y.v; // tirant d'eau original (doit être fourni à acSection.Calc() sous peine d'être modifié par les appels successifs car c'est en même temps un paramètre et une variable temporaire)
+
+        // charge spécifique
+        let Hs = sect.Calc("Hs", Y);
+        this.addSectionFixedResult(Hs, this.intlService.localizeText("INFO_GRANDEUR_HS"), "Hs");
+
+        // charge critique
+        let Hsc = sect.Calc("Hsc", Y);
+        this.addSectionFixedResult(Hsc, this.intlService.localizeText("INFO_GRANDEUR_HSC"), "Hsc");
+
+        // largeur au miroir
+        let B = sect.Calc("B", Y);
+        this.addSectionFixedResult(B, this.intlService.localizeText("INFO_GRANDEUR_B"));
+
+        // périmètre hydraulique
+        let P = sect.Calc("P", Y);
+        this.addSectionFixedResult(P, this.intlService.localizeText("INFO_GRANDEUR_P"));
+
+        // surface hydraulique
+        let S = sect.Calc("S", Y);
+        this.addSectionFixedResult(S, this.intlService.localizeText("INFO_GRANDEUR_S"));
+
+        // rayon hydraulique
+        let R = sect.Calc("R", Y);
+        this.addSectionFixedResult(R, this.intlService.localizeText("INFO_GRANDEUR_R"));
+
+        // vitesse moyenne
+        let V = sect.Calc("V", Y);
+        this.addSectionFixedResult(V, this.intlService.localizeText("INFO_GRANDEUR_V"));
+
+        // nombre de Froude
+        let Fr = sect.Calc("Fr", Y);
+        this.addSectionFixedResult(Fr, this.intlService.localizeText("INFO_GRANDEUR_FR"), );
+
+        // tirant d'eau critique
+        let Yc = sect.Calc("Yc", Y);
+        this.addSectionFixedResult(Yc, this.intlService.localizeText("INFO_GRANDEUR_YC"), "Yc");
+
+        // tirant d'eau normal
+        let Yn = sect.Calc("Yn", Y);
+        this.addSectionFixedResult(Yn, this.intlService.localizeText("INFO_GRANDEUR_YN"), "Yn");
+
+        // tirant d'eau fluvial
+        let Yf = sect.Calc("Yf", Y);
+        this.addSectionFixedResult(Yf, this.intlService.localizeText("INFO_GRANDEUR_YF"), "Yf");
+
+        // tirant d'eau torrentiel
+        let Yt = sect.Calc("Yt", Y);
+        this.addSectionFixedResult(Yt, this.intlService.localizeText("INFO_GRANDEUR_YT"), "Yt");
+
+        // tirant d'eau conjugué
+        let Yco = sect.Calc("Yco", Y);
+        this.addSectionFixedResult(Yco, this.intlService.localizeText("INFO_GRANDEUR_YCO"), "Yco");
+
+        // perte de charge
+        let J = sect.Calc("J", Y);
+        this.addSectionFixedResult(J, this.intlService.localizeText("INFO_GRANDEUR_J"));
+
+        // Variation linéaire de l'énergie spécifique
+        let IJ = sect.Calc("I-J", Y);
+        this.addSectionFixedResult(IJ, this.intlService.localizeText("INFO_GRANDEUR_I-J"));
+
+        // impulsion hydraulique
+        let Imp = sect.Calc("Imp", Y);
+        this.addSectionFixedResult(Imp, this.intlService.localizeText("INFO_GRANDEUR_IMP"));
+
+        // contrainte de cisaillement
+        let Tau0 = sect.Calc("Tau0", Y);
+        this.addSectionFixedResult(Tau0, this.intlService.localizeText("INFO_GRANDEUR_TAU0"));
+    }
+
+    private addRemousResult(x: string, f: number, t: number, e: number, nDigits: number) {
+        let flu = f == undefined ? "" : f.toFixed(nDigits);
+        let tor = t == undefined ? "" : t.toFixed(nDigits);
+        let extra = e == undefined ? "" : e.toFixed(nDigits);
+        this._remousResults.addResult(x, flu, tor, extra);
+    }
+
+    private doComputeRemous() {
+        let nodeType: ComputeNodeType = this.getNodeTypeFromSelectField(CalculatorType.CourbeRemous);
+        var np: [acSection, ParamsEquation] = this.getSectionNubAndParameters(nodeType, false);
+
+        let sect: acSection = np[0];
+        let prmSect: ParamsEquation = np[1];
+
+        let computePrec: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Pr"); // précision de calcul
+        let nDigits: number = -Math.log10(computePrec);
+
+        let Yamont: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Yamont"); // tirant amont
+        let Yaval: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Yaval"); // tirant aval
+        let Dx: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Dx"); // pas de discrétisation
+        let Long: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "Long"); // longueur du bief
+        let If: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "If"); // pente du fond
+        let YB: number = this.getNodeParameterValue(ComputeNodeType.CourbeRemous, "YB"); // hauteur de berge
+        let Yn: number = sect.Calc("Yn"); // hauteur normale
+        let Yc: number = sect.Calc("Yc"); // hauteur critique
+
+        this._remousResults.penteFond = If;
+
+        // méthode de résolution
+
+        let msf: SelectField = <SelectField>this.getFormulaireElementById("select_resolution");
+        let smeth: string = msf.getValue();
+        let methRes: MethodeResolution;
+        if (smeth == "select_resolution_trap")
+            methRes = MethodeResolution.Trapezes;
+        else if (smeth == "select_resolution_rk4")
+            methRes = MethodeResolution.RungeKutta4;
+        else if (smeth == "select_resolution_euler")
+            methRes = MethodeResolution.EulerExplicite;
+        else
+            throw "GenericCalculatorComponent.doComputeRemous() : type de méthode de résolution '" + smeth + "' inconnu";
+
+        // paramètre supplémentaire à calculer
+
+        let dsf: SelectField = <SelectField>this.getFormulaireElementById("select_target");
+        let extraSymbol: string = this.removePrefix(dsf.getValue(), "select_target_");
+
+        // calcul
+
+        let prmCR: CourbeRemousParams = new CourbeRemousParams(sect, Yamont, Yaval, Long, Dx, methRes);
+        let cr = new CourbeRemous(prmCR);
+        let res = cr.calculRemous(extraSymbol == "none" ? undefined : extraSymbol);
+
+        // affichage du graphe
+
+        this._remousResults.hauteurBerge = YB;
+        this._remousResults.hauteurNormale = Yn;
+        this._remousResults.hauteurCritique = Yc;
+        if (extraSymbol != "none") {
+            this._remousResults.extraParamLabel = dsf.selectedEntry.label;
+            this._remousResults.extraGraph = ["Hs", "Hsc", "Yf", "Yt", "Yco"].indexOf(extraSymbol) == -1;
+        }
+        else
+            this._remousResults.extraGraph = false;
+
+        // journal
+
+        this._remousResults.log = cr.log;
+
+        // résultats numériques
+
+        let kFlu = Object.keys(res.flu);
+        let kTor = Object.keys(res.tor);
+        for (let i = 0; i < res.trX.length; i++) {
+            let x: string = res.trX[i];
+            let f = res.flu[x];
+            let t = res.tor[x];
+            let e = res.tRes[x];
+            this.addRemousResult(x, f, t, e, nDigits);
+        }
+    }
+
+    private getVariatedParameter(): NgParameter {
+        return this.getParamFromState(ParamRadioConfig.VAR);
+    }
+
+    private getComputedParameter(): NgParameter {
+        return this.getParamFromState(ParamRadioConfig.CAL);
+    }
+
+    private getNubAndParameters(): [Nub, ParamsEquation] {
+        switch (this.calculatorType) {
+            case CalculatorType.ConduiteDistributrice:
+                {
+                    let Q: number = this.getParameterValue("Q"); // débit Q
+                    let D: number = this.getParameterValue("D"); // diamètre D
+                    let J: number = this.getParameterValue("J"); // perte de charge J
+                    let Lg: number = this.getParameterValue("Lg"); // Longueur de la conduite Lg
+                    let Nu: number = this.getParameterValue("Nu"); // viscosité dynamique 
+                    let prms = new ConduiteDistribParams(Q, D, J, Lg, Nu);
+                    let nub = new ConduiteDistrib(prms);
+                    return [nub, prms];
+                }
+
+            case CalculatorType.LechaptCalmon:
+                {
+                    let Q: number = this.getParameterValue("Q"); // débit Q
+                    let D: number = this.getParameterValue("D"); // diamètre D
+                    let J: number = this.getParameterValue("J"); // perte de charge J
+                    let Lg: number = this.getParameterValue("Lg"); // Longueur de la conduite Lg
+                    let L: number = this.getParameterValue("L"); // paramètre de matériau 1
+                    let M: number = this.getParameterValue("M"); // paramètre de matériau 2
+                    let N: number = this.getParameterValue("N"); // paramètre de matériau 3
+                    let prms = new LechaptCalmonParams(Q, D, J, Lg, L, M, N);
+                    let nub = new LechaptCalmon(prms);
+                    return [nub, prms];
+                }
+
+            default:
+                throw "FormulaireService.getNubAndParameters() : valeur de CalculatorType " + this.calculatorType + " non implémentée"
+        }
+    }
+
+    private getSectionNubAndParameters(nt: ComputeNodeType, getY: boolean = true): [acSection, ParamsEquation] {
+        // bief
+        let Ks = this.getParameterValue("Ks"); // Strickler
+        let If: number = this.getParameterValue("If"); // Pente du fond
+        let YB: number = this.getParameterValue("YB"); // Hauteur de berge
+
+        // caractéristiques hydro
+        let Q: number = this.getParameterValue("Q"); // débit Q
+        if (getY)
+            var Y: number = this.getParameterValue("Y"); // tirant d'eau
+        else
+            Y = undefined;
+
+        let Prec = this.getParameterValue("Pr"); // précision calcul/affichage
+
+        // ??
+        // let YCL: number = f.getParameterValue("YCL"); // Condition limite en cote à l'amont ou à l'aval
+        // let Dx: number = f.getParameterValue("Dx"); // Pas d'espace (positif en partant de l'aval, négatif en partant de l'amont)
+        // let Long: number = f.getParameterValue("Long"); // Longueur du bief
+
+        switch (nt) {
+            case ComputeNodeType.SectionTrapeze:
+            case ComputeNodeType.RegimeUniformeTrapeze:
+            case ComputeNodeType.CourbeRemousTrapeze:
+                {
+                    let LargeurFond = this.getNodeParameterValue(nt, "LargeurFond"); // Largeur au fond
+                    let Fruit = this.getNodeParameterValue(nt, "Fruit"); // Fruit des berges
+                    let prms = new ParamsSectionTrapez(LargeurFond, Fruit, Y, Ks, Q, If, Prec, YB);
+                    let cn = new cSnTrapez(prms);
+                    return [cn, prms];
+                }
+
+            case ComputeNodeType.SectionRectangle:
+            case ComputeNodeType.RegimeUniformeRectangle:
+            case ComputeNodeType.CourbeRemousRectangle:
+                {
+                    let LargeurFond = this.getNodeParameterValue(nt, "LargeurBerge"); // Largeur au fond
+                    let prms = new ParamsSectionRectang(Y, LargeurFond, Ks, Q, If, Prec, YB);
+                    let cn = new cSnRectang(prms);
+                    return [cn, prms];
+                }
+
+            case ComputeNodeType.SectionCercle:
+            case ComputeNodeType.RegimeUniformeCercle:
+            case ComputeNodeType.CourbeRemousCercle:
+                {
+                    let D = this.getNodeParameterValue(nt, "D"); // Largeur au fond
+                    let prms = new ParamsSectionCirc(D, Y, Ks, Q, If, Prec, YB);
+                    let cn = new cSnCirc(prms);
+                    return [cn, prms];
+                }
+
+            case ComputeNodeType.SectionPuissance:
+            case ComputeNodeType.RegimeUniformePuissance:
+            case ComputeNodeType.CourbeRemousPuissance:
+                {
+                    let k = this.getNodeParameterValue(nt, "k"); // coefficient
+                    let LargeurBerge = this.getNodeParameterValue(nt, "LargeurBerge"); // Largeur au niveau des berges
+                    let prms = new ParamsSectionPuiss(k, Y, LargeurBerge, Ks, Q, If, Prec, YB);
+                    let cn = new cSnPuiss(prms);
+                    return [cn, prms];
+                }
+
+            default:
+                throw "FormulaireDefinition.getSectionNubAndParameters() : valeur de ComputeNodeType " + nt + " non implémentée"
+        }
+    }
+
+    private doComputeFixedVar() {
+        let np: [Nub, ParamsEquation];
+        let nub: Nub;
+        let prms: ParamsEquation;
+        let rg: boolean = this.calculatorType == CalculatorType.RegimeUniforme;
+        if (rg) {
+            let snp: [acSection, ParamsEquation] = this.getSectionNubAndParameters(this._nodeType);
+            nub = new RegimeUniforme(snp[0]);
+            prms = snp[1];
+        }
+        else {
+            np = this.getNubAndParameters();
+            nub = np[0];
+            prms = np[1];
+        }
+
+        let computePrec: number = this.getParameterValue("Pr"); // précision de calcul
+        let nDigits: number = -Math.log10(computePrec);
+
+        let computedParam: NgParameter = this.getComputedParameter();
+
+        let varParam: NgParameter = this.getVariatedParameter();
+        if (varParam == undefined) {
+            // pas de paramètre à varier
+
+            let res: Result = nub.Calc(computedParam.symbol, 0, computePrec);
+            if (res.ok) {
+                this.addFixedResults(!rg);
+                this._fixVarResults.addFixedResult(computedParam, res.vCalc, !rg);
+            }
+            else {
+                this._fixVarResults.addLogMessages(res.log);
+            }
+        }
+        else {
+            // il y a un paramètre à varier
+
+            this.addFixedResults(!rg);
+            this._fixVarResults.setVariableParamHeaderFromParameter(varParam, !rg);
+            this._fixVarResults.setVariableResultHeaderFromParameter(computedParam);
+
+            let min: number = +varParam.minValue;
+            let max: number = +varParam.maxValue;
+            let step: number = +varParam.stepValue;
+
+            for (let val = min; val <= max; val += step) {
+                prms[varParam.symbol].v = val;
+
+                let res = nub.Calc(computedParam.symbol, 0, computePrec);
+                if (res.ok) {
+                    this._fixVarResults.addVarResult(val, res.vCalc);
+                }
+                else {
+                    this._fixVarResults.addLogMessages(res.log);
+                }
+            }
+
+            this._fixVarResults.graphTitle = computedParam.symbol + " = f( " + varParam.symbol + " )";
+        }
+    }
+
+    public doCompute() {
+        this.resetResults();
+
+        switch (this.calculatorType) {
+            case CalculatorType.SectionParametree:
+                this.doComputeSection();
+                break;
+
+            case CalculatorType.CourbeRemous:
+                this.doComputeRemous();
+                break;
+
+            default:
+                this.doComputeFixedVar();
+                break;
+        }
+    }
+
     public printDependencies() {
         for (let d of this.dependencies)
             console.log(d.toString());
@@ -438,4 +1078,16 @@ export class FormulaireDefinition {
     public isDisplayed(id: string) {
         return this.getFormulaireElementById(id).isDisplayed;
     }
+
+    public hasFixVarResults(): boolean {
+        return this._fixVarResults.hasResults();
+    }
+
+    public hasSectionResults(): boolean {
+        return this._sectionResults.hasResults();
+    }
+
+    public hasRemousResults(): boolean {
+        return this._remousResults.hasResults();
+    }
 }
diff --git a/src/app/results/calculator-results.ts b/src/app/results/calculator-results.ts
new file mode 100644
index 000000000..dc319ff31
--- /dev/null
+++ b/src/app/results/calculator-results.ts
@@ -0,0 +1,28 @@
+import { NgParameter } from "../formulaire/ngparam";
+
+export abstract class CalculatorResults {
+    protected paramLabel(p: NgParameter, displaySymbol: boolean): string {
+        let res = "";
+        if (displaySymbol)
+            res += p.symbol;
+        if (p.label != undefined && p.label != "")
+            if (p.symbol != p.label || !displaySymbol) {
+                if (res.length > 0)
+                    res += ":";
+                res += p.label;
+            }
+        if (p.unit != undefined && p.unit != "")
+            res += " (" + p.unit + ")";
+        return res;
+    }
+
+    /**
+     * remet tous les résultats à zero
+     */
+    public abstract reset();
+
+    /**
+     * indique si il existe des résultats à afficher
+     */
+    public abstract hasResults(): boolean;
+}
\ No newline at end of file
diff --git a/src/app/results/fixed-var-results.ts b/src/app/results/fixed-var-results.ts
new file mode 100644
index 000000000..33b49bb07
--- /dev/null
+++ b/src/app/results/fixed-var-results.ts
@@ -0,0 +1,137 @@
+import { cLog } from "jalhyd";
+
+import { NgParameter } from "../formulaire/ngparam";
+import { CalculatorResults } from "./calculator-results";
+
+export class FixedVarResults extends CalculatorResults {
+    /**
+     * résultats fixed (true) ou variés (false)
+     */
+    private _isFixed: boolean;
+
+    /**
+     * liste des résultats avec tous les paramètres fixés
+     */
+    private _fixedResults: Object[];
+
+    /**
+     * liste des résultats avec un paramètre variable
+     */
+    private _varResults: Object[];
+
+    /**
+     * titre de la 1ère colonne des résultats variés
+     */
+    private _variableParamHeader: string;
+
+    /**
+     * titre de la 2ème colonne des résultats variés
+     */
+    private _variableResultHeader: string;
+
+    /**
+     * titre du graphe des résultats variés
+     */
+    private _graphTitle: string;
+
+    /**
+     * tableau de valeurs du graphe des résultats variés
+     */
+    private _varGraph: number[] = [];
+
+    /**
+     * journal de calcul
+     */
+    private _log: cLog;
+
+    constructor() {
+        super();
+        this.reset();
+    }
+
+    public get log() {
+        return this._log;
+    }
+
+    public get fixedResults() {
+        return this._fixedResults;
+    }
+
+    public get varResults() {
+        return this._varResults;
+    }
+
+    public get varGraph() {
+        return this._varGraph;
+    }
+
+    public get isFixed() {
+        return this._isFixed;
+    }
+
+    public reset() {
+        this._isFixed = true;
+        this._fixedResults = [];
+        this._varResults = [];
+        this._varGraph = [];
+        this._variableParamHeader = undefined;
+        this._variableResultHeader = undefined;
+        this._graphTitle = undefined;
+        this._log = new cLog();
+    }
+
+    public addFixedResult(p: NgParameter, v: number, displaySymbol: boolean) {
+        this._fixedResults.push({ "label": this.paramLabel(p, displaySymbol), "value": v });
+    }
+
+    public addVarResult(paramVal: number, resVal: number) {
+        this._isFixed = false;
+        this._varResults.push({ "param": paramVal, "result": resVal });
+        this._varGraph.push(resVal);
+    }
+
+    public addLogMessages(l: cLog) {
+        for (let m of l.messages)
+            this._log.add(m);
+    }
+
+    public get variableParamHeader() {
+        return this._variableParamHeader;
+    }
+
+    public setVariableParamHeaderFromParameter(p: NgParameter, displaySymbol: boolean) {
+        this._variableParamHeader = this.paramLabel(p, displaySymbol);
+    }
+
+    public get variableResultHeader() {
+        return this._variableResultHeader;
+    }
+
+    public setVariableResultHeader(h: string) {
+        this._variableResultHeader = h;
+    }
+
+    public setVariableResultHeaderFromParameter(p: NgParameter) {
+        this._variableResultHeader = this.paramLabel(p, true);
+    }
+
+    public get graphTitle() {
+        return this._graphTitle;
+    }
+
+    public set graphTitle(t: string) {
+        this._graphTitle = t;
+    }
+
+    public hasFixedResults(): boolean {
+        return this._fixedResults.length > 0;
+    }
+
+    public hasVarResults(): boolean {
+        return this._varResults.length > 0;
+    }
+
+    public hasResults(): boolean {
+        return this.hasFixedResults() || this.hasVarResults();
+    }
+}
diff --git a/src/app/results/remous-results.ts b/src/app/results/remous-results.ts
new file mode 100644
index 000000000..e3af08034
--- /dev/null
+++ b/src/app/results/remous-results.ts
@@ -0,0 +1,142 @@
+import { CalculatorResults } from "./calculator-results";
+import { cLog } from "jalhyd";
+
+export class RemousResults extends CalculatorResults {
+    /**
+     * hauteur de berge
+     */
+    private _hautBerge: number;
+
+    /**
+     * pente du fond
+     */
+    private _penteFond: number;
+
+    /**
+     * hauteur normale
+     */
+    private _hautNormale: number;
+
+    /**
+     * hauteur critique
+     */
+    private _hautCritique: number;
+
+    /**
+    * résultats numériques
+    */
+    private _series: Object[] = [];
+
+    /**
+     * présence d'un paramètre supplémentaire
+     */
+    private _hasExtra: boolean;
+
+    /**
+     * le paramètre supplémentaire est affiché dans un graphe séparé
+     */
+    private _extraGraph: boolean;
+
+    /**
+     * titre de la colonne du paramètre supplémentaire
+     */
+    private _extraParamLabel: string;
+
+    /**
+     * journal de calcul
+     */
+    private _log: cLog;
+
+    constructor() {
+        super();
+        this.reset();
+    }
+
+    public reset() {
+        this._series = [];
+        this._log = new cLog();
+        this._extraParamLabel = undefined;
+        this._hasExtra = false;
+        this._extraGraph = false;
+    }
+
+    public get log() {
+        return this._log;
+    }
+
+    public set log(l: cLog) {
+        this._log = l;
+    }
+
+    public get series() {
+        return this._series;
+    }
+
+    public addResult(x: string, flu: string, tor: string, extra: string) {
+        let extraFlu = (flu == "" ? "" : extra);
+        let extraTor = (tor == "" ? "" : extra);
+        if (extraFlu != "" || extraTor != "")
+            this._hasExtra = true;
+        this._series.push({ "abs": x, "flu": flu, "extraFlu": extraFlu, "tor": tor, "extraTor": extraTor });
+    }
+
+    public get extraParamLabel() {
+        return this._extraParamLabel;
+    }
+
+    public set extraParamLabel(l: string) {
+        this._extraParamLabel = l;
+    }
+
+    // public addLogEntry(m: Message, nDigits: number) {
+    //     this.logComponent.addLogEntry(m, nDigits);
+    // }
+
+    public get hautBerge() {
+        return this._hautBerge;
+    }
+
+    public set hauteurBerge(v: number) {
+        this._hautBerge = v;
+    }
+
+    public get penteFond() {
+        return this._penteFond;
+    }
+
+    public set penteFond(v: number) {
+        this._penteFond = v;
+    }
+
+    public get hautNormale() {
+        return this._hautNormale;
+    }
+
+    public set hauteurNormale(v: number) {
+        this._hautNormale = v;
+    }
+
+    public get hautCritique() {
+        return this._hautCritique;
+    }
+
+    public set hauteurCritique(v: number) {
+        this._hautCritique = v;
+    }
+
+    public get hasExtra() {
+        return this._hasExtra;
+    }
+
+    public get extraGraph() {
+        return this._extraGraph;
+    }
+
+    public set extraGraph(b: boolean) {
+        this._extraGraph = b;
+    }
+
+    public hasResults(): boolean {
+        return this._series.length > 0;
+    }
+}
\ No newline at end of file
diff --git a/src/app/results/section-results.ts b/src/app/results/section-results.ts
new file mode 100644
index 000000000..12abf4b36
--- /dev/null
+++ b/src/app/results/section-results.ts
@@ -0,0 +1,54 @@
+import { acSection } from "jalhyd";
+
+import { CalculatorResults } from "./calculator-results";
+import { NgParameter } from "../formulaire/ngparam";
+
+export class SectionResults extends CalculatorResults {
+    private _results: Object[];
+
+    private _section: acSection;
+
+    constructor() {
+        super();
+        this.reset();
+    }
+
+    public reset() {
+        this._section = undefined;
+        this._results = [];
+    }
+
+    // public addResult(v: number, l: string, fixedPrec: number, drawLabel: string) {
+    //     this._results.push({ "label": l, "value": v.toFixed(fixedPrec) });
+    //     if (drawLabel != undefined) {
+    //         this._sectionCanvas.addLevel(v, drawLabel + " = " + v.toFixed(fixedPrec), SectionResultsComponent.labelColors[drawLabel]);
+    //     }
+    // }
+    public addResult(v: number, l: string, drawLabel: string) {
+        this._results.push({
+            "label": l,
+            "value": v,
+            "drawLabel": drawLabel
+        });
+    }
+
+    // public addFixedResult(p: NgParameter, v: number, fixedPrec: number, displaySymbol: boolean) {
+    //     this._fixedResults.push({ "label": this.paramLabel(p, displaySymbol), "value": v.toFixed(fixedPrec) });
+    // }
+
+    public get results() {
+        return this._results;
+    }
+
+    public get section() {
+        return this._section;
+    }
+
+    public set section(s: acSection) {
+        this._section = s;
+    }
+
+    public hasResults(): boolean {
+        return this._section != undefined && this._results.length > 0;
+    }
+}
\ No newline at end of file
diff --git a/src/app/services/app-setup/app-setup.service.ts b/src/app/services/app-setup/app-setup.service.ts
new file mode 100644
index 000000000..25a9f8d70
--- /dev/null
+++ b/src/app/services/app-setup/app-setup.service.ts
@@ -0,0 +1,26 @@
+export class ApplicationSetupService {
+    private _displayPrecision: number;
+
+    private _computePrecision: number;
+
+    constructor() {
+        this.defaults();
+    }
+
+    public defaults() {
+        this._displayPrecision = 0.001;
+        this._computePrecision = 0.001;
+    }
+
+    public get displayPrecision() {
+        return this._displayPrecision;
+    }
+
+    public get displayDigits() {
+        return -Math.log10(this._displayPrecision);
+    }
+
+    public get computePrecision() {
+        return this._computePrecision;
+    }
+}
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index 0c408308a..c7e946d7c 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -3,18 +3,14 @@ import { Response } from "@angular/http";
 import { Observable as rxObservable } from "rxjs/Observable";
 import "rxjs/add/operator/toPromise";
 
-import { ComputeNodeType, ParamsEquation, acSection, Nub, ConduiteDistrib, ConduiteDistribParams } from "jalhyd";
-import { LechaptCalmon, LechaptCalmonParams, ParamsSectionTrapez, cSnTrapez } from "jalhyd";
-import { ParamsSectionRectang, cSnRectang, ParamsSectionCirc, cSnCirc, ParamsSectionPuiss, cSnPuiss } from "jalhyd";
-
 import { ParamService } from "../param/param.service";
 import { HttpService } from "../../services/http/http.service";
+import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { FormulaireDefinition, CalculatorType } from "../../formulaire/formulaire-definition";
 import { FormulaireElement } from "../../formulaire/formulaire-element";
 import { SelectField } from "../../formulaire/select-field";
 import { CheckField } from "../../formulaire/check-field";
 import { StringMap } from "../../stringmap";
-import { InternationalisationService } from "../../services/internationalisation/internationalisation.service";
 import { EnumEx } from "../../util";
 import { Observable } from "../observer";
 
@@ -93,7 +89,7 @@ export class FormulaireService extends Observable {
         if (ct == undefined)
             throw "FormulaireService.createFormulaire() : invalid undefined CalculatorType"
 
-        let f = new FormulaireDefinition(this.paramService, ct);
+        let f = new FormulaireDefinition(ct, this.paramService, this.intlService);
         this._formulaires.push(f);
         let prom: Promise<Response> = this.loadConfig(f, ct);
         return prom.then(_ => {
@@ -105,7 +101,11 @@ export class FormulaireService extends Observable {
             f.applyDependencies();
             return f;
         }).then(f => {
-            this.notifyObservers(f);
+            this.notifyObservers(
+                {
+                    "action": "create",
+                    "form": f
+                });
             return f;
         });
     }
@@ -118,12 +118,15 @@ export class FormulaireService extends Observable {
         throw "FormulaireService.getFormulaire() : unkown form id " + uid;
     }
 
-    private getFormulaireElementById(formId: number, elemId: string): FormulaireElement {
+    public getCheckField(formId: number, elemId: string): CheckField {
         for (let f of this._formulaires)
             if (f.uid == formId) {
                 let s = f.getFormulaireElementById(elemId);
-                if (s != undefined)
-                    return s;
+                if (s != undefined) {
+                    if (!(s instanceof CheckField))
+                        throw "Form element with id '" + elemId + "' is not a checkbox";
+                    return <CheckField>s;
+                }
             }
         return undefined;
     }
@@ -141,121 +144,16 @@ export class FormulaireService extends Observable {
         return undefined;
     }
 
-    public getCheckField(formId: number, elemId: string): CheckField {
+    private getFormulaireElementById(formId: number, elemId: string): FormulaireElement {
         for (let f of this._formulaires)
             if (f.uid == formId) {
                 let s = f.getFormulaireElementById(elemId);
-                if (s != undefined) {
-                    if (!(s instanceof CheckField))
-                        throw "Form element with id '" + elemId + "' is not a checkbox";
-                    return <CheckField>s;
-                }
+                if (s != undefined)
+                    return s;
             }
         return undefined;
     }
 
-    public getNubAndParameters(f: FormulaireDefinition): [Nub, ParamsEquation] {
-        switch (f.calculatorType) {
-            case CalculatorType.ConduiteDistributrice:
-                {
-                    let Q: number = f.getParameterValue("Q"); // débit Q
-                    let D: number = f.getParameterValue("D"); // diamètre D
-                    let J: number = f.getParameterValue("J"); // perte de charge J
-                    let Lg: number = f.getParameterValue("Lg"); // Longueur de la conduite Lg
-                    let Nu: number = f.getParameterValue("Nu"); // viscosité dynamique 
-                    let prms = new ConduiteDistribParams(Q, D, J, Lg, Nu);
-                    let nub = new ConduiteDistrib(prms);
-                    return [nub, prms];
-                }
-
-            case CalculatorType.LechaptCalmon:
-                {
-                    let Q: number = f.getParameterValue("Q"); // débit Q
-                    let D: number = f.getParameterValue("D"); // diamètre D
-                    let J: number = f.getParameterValue("J"); // perte de charge J
-                    let Lg: number = f.getParameterValue("Lg"); // Longueur de la conduite Lg
-                    let L: number = f.getParameterValue("L"); // paramètre de matériau 1
-                    let M: number = f.getParameterValue("M"); // paramètre de matériau 2
-                    let N: number = f.getParameterValue("N"); // paramètre de matériau 3
-                    let prms = new LechaptCalmonParams(Q, D, J, Lg, L, M, N);
-                    let nub = new LechaptCalmon(prms);
-                    return [nub, prms];
-                }
-
-            default:
-                throw "FormulaireService.getNubAndParameters() : valeur de CalculatorType " + f.calculatorType + " non implémentée"
-        }
-    }
-
-    public getSectionNubAndParameters(f: FormulaireDefinition, nt: ComputeNodeType, getY: boolean = true): [acSection, ParamsEquation] {
-
-        // bief
-        let Ks = f.getParameterValue("Ks"); // Strickler
-        let If: number = f.getParameterValue("If"); // Pente du fond
-        let YB: number = f.getParameterValue("YB"); // Hauteur de berge
-
-        // caractéristiques hydro
-        let Q: number = f.getParameterValue("Q"); // débit Q
-        if (getY)
-            var Y: number = f.getParameterValue("Y"); // tirant d'eau
-        else
-            Y = undefined;
-
-        let Prec = f.getParameterValue("Pr"); // précision calcul/affichage
-
-        // ??
-        // let YCL: number = f.getParameterValue("YCL"); // Condition limite en cote à l'amont ou à l'aval
-        // let Dx: number = f.getParameterValue("Dx"); // Pas d'espace (positif en partant de l'aval, négatif en partant de l'amont)
-        // let Long: number = f.getParameterValue("Long"); // Longueur du bief
-
-        switch (nt) {
-            case ComputeNodeType.SectionTrapeze:
-            case ComputeNodeType.RegimeUniformeTrapeze:
-            case ComputeNodeType.CourbeRemousTrapeze:
-                {
-                    let LargeurFond = f.getNodeParameterValue(nt, "LargeurFond"); // Largeur au fond
-                    let Fruit = f.getNodeParameterValue(nt, "Fruit"); // Fruit des berges
-                    let prms = new ParamsSectionTrapez(LargeurFond, Fruit, Y, Ks, Q, If, Prec, YB);
-                    let cn = new cSnTrapez(prms);
-                    return [cn, prms];
-                }
-
-            case ComputeNodeType.SectionRectangle:
-            case ComputeNodeType.RegimeUniformeRectangle:
-            case ComputeNodeType.CourbeRemousRectangle:
-                {
-                    let LargeurFond = f.getNodeParameterValue(nt, "LargeurBerge"); // Largeur au fond
-                    let prms = new ParamsSectionRectang(Y, LargeurFond, Ks, Q, If, Prec, YB);
-                    let cn = new cSnRectang(prms);
-                    return [cn, prms];
-                }
-
-            case ComputeNodeType.SectionCercle:
-            case ComputeNodeType.RegimeUniformeCercle:
-            case ComputeNodeType.CourbeRemousCercle:
-                {
-                    let D = f.getNodeParameterValue(nt, "D"); // Largeur au fond
-                    let prms = new ParamsSectionCirc(D, Y, Ks, Q, If, Prec, YB);
-                    let cn = new cSnCirc(prms);
-                    return [cn, prms];
-                }
-
-            case ComputeNodeType.SectionPuissance:
-            case ComputeNodeType.RegimeUniformePuissance:
-            case ComputeNodeType.CourbeRemousPuissance:
-                {
-                    let k = f.getNodeParameterValue(nt, "k"); // coefficient
-                    let LargeurBerge = f.getNodeParameterValue(nt, "LargeurBerge"); // Largeur au niveau des berges
-                    let prms = new ParamsSectionPuiss(k, Y, LargeurBerge, Ks, Q, If, Prec, YB);
-                    let cn = new cSnPuiss(prms);
-                    return [cn, prms];
-                }
-
-            default:
-                throw "FormulaireService.getSectionNubAndParameters() : valeur de SectionType " + nt + " non implémentée"
-        }
-    }
-
     private getConfigPathPrefix(ct: CalculatorType): string {
         if (ct == undefined)
             throw "FormulaireService.getConfigPathPrefix() : invalid undefined CalculatorType"
-- 
GitLab