diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 496309b0b1b0b144b114045c8495b2bb43d087bf..2e5f7049aa75412f3db8c5a7ee22ea828018a4db 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,28 +4,42 @@ stages:
   - test
   - build
   - clean-stale-branches
-  - deploy
-  - deploy-stable
+  - deploy-dev
+  - deploy-prod
   - releases-nightly
   - releases-version
 
 variables:
+# from Gitlab CI/CD environment variables :
+# - development server :
+#   DEV_HOST : deployment server hostname
+#   DEV_LOGIN : user on deployment server
+#   DEV_PATH : path base on deployment server
+# - production server :
+#   PROD_HOST : deployment server hostname
+#   PROD_LOGIN : user on deployment server
+#   PROD_PASS : password for user on deployment server
+#   PROD_PATH : path base on deployment server
   LC_ALL: C.UTF-8
   ANDROID_HOME: "/usr/local/android-sdk" # should already be defined in the Docker image
   ANDROID_SDK_ROOT: "/usr/local/android-sdk" # should already be defined in the Docker image
-  DEPLOY_HOST_LOGIN: "nghyd@aubes"
-  DEPLOY_URL: "/var/www/html/cassiopee"
-  DEPLOY_STABLE_URL: "/var/www/cassiopee-production"
-  RELEASES_URL: "$DEPLOY_STABLE_URL/cassiopee-releases"
+  RELEASES_PATH: "$PROD_PATH/cassiopee-releases"
 
 before_script:
-  # load private key from GitLab CI variable, to deploy on Aubes server
+  # load private key from GitLab CI variable to deploy on Aubes server
   - eval $(ssh-agent -s)
   - ssh-add <(echo "$AUBES_B64_AUTHORIZED_SSH_PRIVATE_KEY" | base64 -d)
+  # load private key from GitLab CI variable to deploy on OVH server
+  - ssh-add <(echo "$OVH_B64_AUTHORIZED_SSH_PRIVATE_KEY" | base64 -d)
+  # disable console prompt for host checking
   - mkdir -p ~/.ssh
   - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
 
 cache:
+  key:
+    files:
+      - jalhyd_branch
+      - package.json
   paths:
     - node_modules/
 
@@ -91,10 +105,10 @@ clean-stale-branches:
     - tags
     - web
   script:
-    - ./scripts/remove-stale-branches.sh $DEPLOY_HOST_LOGIN $DEPLOY_URL
+    - ./scripts/remove-stale-branches.sh $DEV_LOGIN@$DEV_HOST $DEV_PATH
 
-deploy:
-  stage: deploy
+deploy-dev:
+  stage: deploy-dev
   only:
     - pushes
     - tags
@@ -103,32 +117,28 @@ deploy:
     - build
   script:
     # Copie de la branche / du tag
-    - rsync -a "dist/" "$DEPLOY_HOST_LOGIN:$DEPLOY_URL/$CI_COMMIT_REF_NAME"
+    - ./scripts/deploy-version.sh $CI_COMMIT_REF_NAME $DEV_LOGIN $DEV_HOST $DEV_PATH
 
-deploy-stable:
-  stage: deploy-stable
+deploy-prod:
+  stage: deploy-prod
   only:
     variables:
       - $CI_COMMIT_REF_NAME == "stable"
   dependencies:
     - build
   script:
-    # Copie de la branche production
-    - rsync -a "dist/" "$DEPLOY_HOST_LOGIN:$DEPLOY_STABLE_URL/"
-    # Modification du dossier base href
-    - ssh $DEPLOY_HOST_LOGIN "sed -i 's:/cassiopee/stable/:/:g' $DEPLOY_STABLE_URL/index.html"
+    - ./scripts/deploy-version.sh stable $PROD_LOGIN $PROD_HOST $PROD_PATH $PROD_PASS
 
 releases-nightly:
   stage: releases-nightly
   only:
     - schedules
+  except:
+  # exclude master to apply releases-nightly on devel only
+    - master
   dependencies: []
   script:
-    - npm run release-all
-    - find release -name "fr.irstea.cassiopee_*.deb" -exec scp "{}" $DEPLOY_HOST_LOGIN:$RELEASES_URL/linux-nightly.deb \;
-    - find release -name "Cassio*Setup*.exe" -exec scp "{}" $DEPLOY_HOST_LOGIN:$RELEASES_URL/windows-nightly.exe \;
-    - find release -name "Cassio*-mac.zip" -exec scp "{}" $DEPLOY_HOST_LOGIN:$RELEASES_URL/macos-nightly.zip \;
-    - find release -name "cassiopee-*.apk" -exec scp "{}" $DEPLOY_HOST_LOGIN:$RELEASES_URL/android-nightly.apk \;
+    - ./scripts/release-version.sh nightly $PROD_LOGIN $PROD_HOST $RELEASES_PATH
 
 releases-version:
   stage: releases-version
@@ -137,4 +147,4 @@ releases-version:
       - $CI_COMMIT_REF_NAME =~ /^[0-9]+\.[0-9]+\.[0-9]+$/ # version tag
   dependencies: []
   script:
-    - ./scripts/release-version.sh $CI_COMMIT_REF_NAME $DEPLOY_HOST_LOGIN $RELEASES_URL
+    - ./scripts/release-version.sh $CI_COMMIT_REF_NAME $PROD_LOGIN $PROD_HOST $RELEASES_PATH
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 560298a3045c52d0ab3ab4eaeeec40ee183c8432..1d92791fcaaab47792e582858c23e003bd0b8cb0 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -10,7 +10,10 @@
             "name": "Launch Chrome (local webserver)",
             "url": "http://localhost:4200",
             "webRoot": "${workspaceFolder}",
-            "runtimeExecutable": "/usr/bin/chromium-browser"
+            "runtimeExecutable": "/usr/bin/chromium",
+            "runtimeArgs": [
+                "--preserve-symlinks"
+            ]
         },
         {
             "name": "Launch Firefox (local webserver)",
@@ -19,6 +22,23 @@
             "reAttach": true,
             "url": "http://localhost:4200",
             "webRoot": "${workspaceFolder}"
+        },
+        {
+            "name": "Launch e2e current file",
+            "type": "node",
+            "request": "launch",
+            "stopOnEntry": false,
+            "program": "${workspaceRoot}/node_modules/protractor/bin/protractor",
+            "args": [
+                "${workspaceRoot}/protractor.conf.js",
+                "--specs=${file}"
+            ],
+            "cwd": "${workspaceRoot}",
+            "sourceMaps": true,
+            "outFiles": [
+                "${workspaceRoot}/dist/out-tsc-e2e/*.js"
+            ],
+            "skipSourceMapSupport": true
         }
     ]
-}
\ No newline at end of file
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6977faf6b44138df65d6928c8d73b04f7c481637..8cd5b0cdfb32bdba1cb6b0c679df12c1986b3f39 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,46 @@
 # Historique des versions
 
+### 4.15.0 - 2022-05-04 (Salmo trutta)
+
+#### Nouvelles fonctionnalités
+
+* PAB : Variation du débit d'attrait ([nghyd#431](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/431))
+* Ajouter un bouton "Annuler" sur la saisie des paramètres variables ([jalhyd#300](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/300),[nghyd#507](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/507))
+* Prébarrages : mettre les enfants invalides en rouge dans le schéma ([jalhyd#298](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/298),[nghyd#484](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/484))
+
+#### Changements
+
+* Fente Larinier : laisser le coefficient de débit vide ([nghyd#515](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/515))
+* Cloisons : Générer une PAB : vider les champs ([jalhyd#306](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/306),[nghyd#516](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/516))
+
+
+#### Correction de bogues
+
+* Courbe de remous: crash de l'application sur données erronées ([jalhyd#307](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/307),[nghyd#532](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/532))
+  Deux bugs en un, l'appli crashe quand :
+    - la hauteur de berge dépasse une certaine valeur avec des paramètres corrects pour effectuer un calcul (par exemple les valeurs par défaut)
+    - les deux cotes de l'eau se situent sous les cotes de fond amont et aval
+* Sections : non convergence du calcul du tirant d'eau critique ([jalhyd#301](https://gitlab.irstea.fr/cassiopee/jalhyd/-/issues/301),[nghyd#528](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/528))
+* Remettre le paramètre dans son état initial quand le dialogue "Varier" est annulé ([nghyd#508](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/508))
+* Prébarrages: les champs ne sont pas vides lors des ajouts de bassins et cloisons ([nghyd#503](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/503))
+* Mode "champs vides par défaut" : changer le type d'un ouvrage (ex: dans Cloisons) remplit les champs ([nghyd#480](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/480))
+* PréBarrages : perte du focus lorsqu'on édite un paramètre d'un enfant (cloison ou bassin) ([nghyd#469](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/469))
+
+#### Documentation
+
+* Cloisons : il manque l'aide contextuelle pour les lois de débit ([nghyd#529](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/529))
+* Documentation : corrections ([nghyd#498](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/498))
+
+#### Changements internes
+
+* Nightly build: clean folder before installation ([nghyd#495](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/495))
+* Transfert du site de production sur OVH ([nghyd#505](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/505))
+* Plantage des tests e2e sur le chargement des exemples ([nghyd#530](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/530),[nghyd#531](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/531))
+    - Les champs des exemples chargés sont vides lorsque le mode "champ vides" est activé.
+    - Les tests e2e plantent par manque de temporisation
+* CI : les jobs build en schedule de master et devel plantent ([nghyd#527](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/527))
+* CI : affiner la gestion du cache ([nghyd#526](https://gitlab.irstea.fr/cassiopee/nghyd/-/issues/526))
+
 ### 4.14.2 - 2021-03-25
 
 #### Nouvelles fonctionnalités
diff --git a/angular.json b/angular.json
index 0f22c724bcd8a99caff923b27efe8a2309e2db4e..b106949db4e6399d436aa561757dce14c72cd577 100644
--- a/angular.json
+++ b/angular.json
@@ -104,7 +104,12 @@
           "builder": "@angular-devkit/build-angular:dev-server",
           "options": {
             "browserTarget": "ngHyd:build",
-            "aot": true
+            "aot": true,
+            "sourceMap": {
+              "scripts": true,
+              "styles": true,
+              "vendor": true
+            }
           },
           "configurations": {
             "production": {
@@ -193,4 +198,4 @@
   "cli": {
     "analytics": false
   }
-}
\ No newline at end of file
+}
diff --git a/docs/en/calculators/hsl/section_parametree.md b/docs/en/calculators/hsl/section_parametree.md
index e3e8ad8f064eadcfe30824c21e003f4f3f7c680c..7d74e2bd96df9b7aac17d824e30aa65431cacccc 100644
--- a/docs/en/calculators/hsl/section_parametree.md
+++ b/docs/en/calculators/hsl/section_parametree.md
@@ -3,7 +3,7 @@
 This module calculates the hydraulic quantities associated to:
 
 - a section with a defined geometrical shape ([See section types managed by Cassiopée](types_sections.md))
-- a draft \(y\) in m
+- a water depth \(y\) in m
 - a flow \(Q\) in m<sup>3</sup>/s
 - a bottom slope \(I_f\) in m/m
 - a roughness expressed with the Strickler's coefficient \(K\) in m<sup>1/3</sup>/s
@@ -23,7 +23,7 @@ The calculated hydraulic quantities are:
 - Critical depth (m)
 - Critical head (m)
 - Corresponding depth (m)
-- Impulsion (kgâ‹…mâ‹…s<sup>-1</sup>)
+- Impulsion (N)
 - Conjugate depth
 - Tractive force (Pa)
 
@@ -122,15 +122,15 @@ We use Newton's method by posing \(y_{k+1} = y_k - \frac{f(y_k)}{f'(y_k)}\) with
 <div style="position: relative"><div style="position: relative"><a id="la-charge-critique-m" style="position: absolute; top: -100px;" style="position: absolute; top: -60px;"></a></div></div>
 ## Critical head (m)
 
-This is the head calculated for a draft equal to the critical depth. \(H_c = H(y_c)\).
+This is the head calculated for a water depth equal to the critical depth. \(H_c = H(y_c)\).
 
 <div style="position: relative"><a id="le-tirant-deau-correspondant-m" style="position: absolute; top: -60px;"></a></div>
-## Corresponing depth (m)
+## Corresponding depth (m)
 
-For a fluvial (respectively torrential draft) \(y\), corresponding depth is the torrential (respectively fluvial) draft for the which \(H(y) = H(y_{cor})\).
+For a fluvial (respectively torrential water depth) \(y\), corresponding depth is the torrential (respectively fluvial) water depth for which \(H(y) = H(y_{cor})\).
 
-<div style="position: relative"><a id="limpulsion-hydraulique-kgms-1" style="position: absolute; top: -60px;"></a></div>
-## Hydraulic impulsion (kgâ‹…mâ‹…s<sup>-1</sup>)
+<div style="position: relative"><a id="limpulsion-hydraulique-N" style="position: absolute; top: -60px;"></a></div>
+## Hydraulic impulsion (N)
 
 The impulsion \(I\) is the sum of the amount of movement and the resultant of the pressure force in a section:
 
@@ -145,7 +145,7 @@ The distance from the centre of gravity of the section to the free surface \(y_g
 
 $$S.y_g = \int_{0}^{y} (y-z)B(z)dz$$
 
-With \(y\) the depth and \(B(z)\) the width at mirror for a draft \(z\)
+With \(y\) the depth and \(B(z)\) the width at mirror for a water depth \(z\)
 
 Formulas of \(S.y_g\) for the different section shapes are :
 
@@ -157,7 +157,7 @@ Formulas of \(S.y_g\) for the different section shapes are :
 <div style="position: relative"><a id="le-tirant-deau-conjugue-m" style="position: absolute; top: -60px;"></a></div>
 ## Conjugate depth (m)
 
-For a fluvial (respectively torrential draft) \(y\), conjugate depth is the torrential (respectively fluvial) draft for the which \(I(y) = I(y_{con})\).
+For a fluvial (respectively torrential water depth) \(y\), conjugate depth is the torrential (respectively fluvial) water depth for which \(I(y) = I(y_{con})\).
 
 <div style="position: relative"><a id="la-force-tractrice-pa" style="position: absolute; top: -60px;"></a></div>
 ## Tractive force (Pa)
diff --git a/docs/fr/calculators/hsl/section_parametree.md b/docs/fr/calculators/hsl/section_parametree.md
index eef566ac751101ba2f392b1e10a08c6bf8f6385d..18c4bfa59c5a582374d64184fe5bb1bc505f7669 100644
--- a/docs/fr/calculators/hsl/section_parametree.md
+++ b/docs/fr/calculators/hsl/section_parametree.md
@@ -23,7 +23,7 @@ Les grandeurs hydrauliques calculées sont&nbsp;:
 - Le tirant d'eau critique (m)
 - La charge critique (m)
 - Le tirant d'eau correspondant (m)
-- L'impulsion (kgâ‹…mâ‹…s<sup>-1</sup>)
+- L'impulsion (N)
 - Le tirant d'eau conjugué
 - La force tractrice (Pa)
 
@@ -117,7 +117,7 @@ C'est la charge calculée pour un tirant d'eau égal au tirant d'eau critique \(
 
 Pour un tirant d'eau fluvial (respectivement torrentiel) \(y\), le tirant correspondant est le tirant d'eau torrentiel (respectivement fluvial) pour lequel \(H(y) = H(y_{cor})\).
 
-## L'impulsion hydraulique (kgâ‹…mâ‹…s<sup>-1</sup>)
+## L'impulsion hydraulique (N)
 
 L'impulsion \(I\) est la somme de la quantité de mouvement et de la résultante de la force de pression dans une section :
 
diff --git a/docs/fr/calculators/hyd_en_charge/lechapt-calmon.md b/docs/fr/calculators/hyd_en_charge/lechapt-calmon.md
index c423136c149df7f3b67e8bd7662c1fd45f791883..6001e557244786c0b6d2f9890bb159d4460d98e4 100644
--- a/docs/fr/calculators/hyd_en_charge/lechapt-calmon.md
+++ b/docs/fr/calculators/hyd_en_charge/lechapt-calmon.md
@@ -49,7 +49,7 @@ Table: Matériaux et coefficients utilisés dans la formule de Lechapt et Calmon
 
 $$ J_S = K_S \frac{V^2}{2g}$$
 
-Avec&nbsp:
+Avec&nbsp;:
 
 - \(K_S\)&nbsp;: le coefficient de perte de charge singulière
 - \(V\)&nbsp;: la vitesse de l'eau dans la conduite (\(V = 4 Q / \pi / D^2\))
@@ -60,4 +60,4 @@ $$ K_L = \frac{2g J_L}{V^2} $$
 
 ## Coefficient de perte de charge de Darcy
 
-$$ f_D = \frac{2g J D}{l_T V^2}
+$$ f_D = \frac{2g J D}{l_T V^2} $$
diff --git a/e2e/calc-all-examples.e2e-spec.ts b/e2e/calc-all-examples.e2e-spec.ts
index 957364fc618f302066ce5c416ea3f1341d7148f1..4a078b70d549554132d46ef46ae5fe07c7260aa4 100644
--- a/e2e/calc-all-examples.e2e-spec.ts
+++ b/e2e/calc-all-examples.e2e-spec.ts
@@ -2,6 +2,8 @@ import { ListPage } from "./list.po";
 import { CalculatorPage } from "./calculator.po";
 import { Navbar } from "./navbar.po";
 import { browser, by, element } from "protractor";
+import { PreferencesPage } from "./preferences.po";
+import { SideNav } from "./sidenav.po";
 
 /**
  * Calculate all modules of all examples
@@ -11,30 +13,50 @@ describe("ngHyd − example sessions −", async () => {
     let listPage: ListPage;
     let calcPage: CalculatorPage;
     let navbar: Navbar;
+    let prefPage: PreferencesPage;
+    let sideNav: SideNav;
 
     beforeAll(() => {
         jasmine.DEFAULT_TIMEOUT_INTERVAL = 100 * 1000;
-    });
-
-    it("calcul de tous les modules de tous les exemples −", async () => {
         listPage = new ListPage();
         calcPage = new CalculatorPage();
         navbar = new Navbar();
-        listPage = new ListPage();
+        prefPage = new PreferencesPage();
+        sideNav = new SideNav();
+    });
+
+    it("calcul de tous les modules de tous les exemples −", async () => {
+        // disable evil option "empty fields on module creation"
+        await prefPage.navigateTo();
+        await prefPage.disableEvilEmptyFields();
+        await browser.sleep(200);
 
         // for each calculator
         let lastExampleFound = true;
         let i = 0;
         while (lastExampleFound) {
-            await listPage.navigateTo();
+            if (i == 0) {
+                // start page
+                await navbar.clickNewCalculatorButton();
+                await browser.sleep(200);
+            }
+            else {
+                // empty session
+                await navbar.clickMenuButton();
+                await browser.sleep(200);
+                await sideNav.clickNewSessionButton();
+                await browser.sleep(1000);
+            }
 
             const examples = await element.all(by.css("#examples-list .load-example"));
+            await browser.sleep(200);
             if (examples.length > i) {
                 // click example #i
                 await examples[i].click();
-                await browser.sleep(50);
+                await browser.sleep(200);
 
                 const nbModules = await navbar.getCalculatorEntriesCount();
+                await browser.sleep(200);
                 for (let j = 0; j < nbModules; j++) {
                     // select module
                     await navbar.openNthCalculator(j);
diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts
index 1194295c9979387c0b51149569b81ca4ca2d04e2..4be6378862b7589d86a2314e73a9f1a00ffa8b34 100644
--- a/e2e/calculator.po.ts
+++ b/e2e/calculator.po.ts
@@ -67,6 +67,10 @@ export class CalculatorPage {
         return element(by.css("fieldset-container .hyd-window-btns button.add-structure"));
     }
 
+    getCopyStructureButton() {
+        return element(by.css("fieldset-container .hyd-window-btns button.copy-structure"));
+    }
+
     getAllLinkButtons() {
         return element.all(by.css("mat-button-toggle.radio_link"));
     }
diff --git a/e2e/compute-reset-chained-links.e2e-spec.ts b/e2e/compute-reset-chained-links.e2e-spec.ts
index 2767c9d968cf36f944d012f4ccd6212414ef8d4a..65e2e4e954988e3f62a61bd1289cc701e1c36d97 100644
--- a/e2e/compute-reset-chained-links.e2e-spec.ts
+++ b/e2e/compute-reset-chained-links.e2e-spec.ts
@@ -3,6 +3,7 @@ import { CalculatorPage } from "./calculator.po";
 import { Navbar } from "./navbar.po";
 import { SideNav } from "./sidenav.po";
 import { browser } from "protractor";
+import { PreferencesPage } from "./preferences.po";
 
 /**
  * Load a session containing 3 calculators, having linked parameters
@@ -16,12 +17,14 @@ describe("ngHyd − compute then reset chained results − ", () => {
     let calcPage: CalculatorPage;
     let navbar: Navbar;
     let sidenav: SideNav;
+    let prefPage: PreferencesPage;
 
     function init() {
         startPage = new AppPage();
         calcPage = new CalculatorPage();
         navbar = new Navbar();
         sidenav = new SideNav();
+        prefPage = new PreferencesPage();
     }
     beforeEach(init);
 
@@ -128,8 +131,16 @@ describe("ngHyd − compute then reset chained results − ", () => {
     });
 
     it("when loading session-results-invalidation.json, results reset should not be triggered on calculation", async () => {
+        // disable evil option "empty fields on module creation"
+        await prefPage.navigateTo();
+        await prefPage.disableEvilEmptyFields();
+        await browser.sleep(200);
+
+        // start page
+        await navbar.clickNewCalculatorButton();
+        await browser.sleep(200);
+
         // load session file
-        await startPage.navigateTo();
         await navbar.clickMenuButton();
         await browser.sleep(200);
         await sidenav.clickLoadSessionButton();
diff --git a/e2e/examples-empty-fields.e2e-spec.ts b/e2e/examples-empty-fields.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..491ef77c6db5260926874508da87d24dca61378f
--- /dev/null
+++ b/e2e/examples-empty-fields.e2e-spec.ts
@@ -0,0 +1,154 @@
+import { browser, by, element } from "protractor";
+import { CalculatorPage } from "./calculator.po";
+import { ListPage } from "./list.po";
+import { Navbar } from "./navbar.po";
+import { PreferencesPage } from "./preferences.po"
+
+/**
+ * check that fields are empty on creation
+ */
+describe("ngHyd - Check that examples fields are not empty with 'empty fields on calculator creation' is enabled - ", () => {
+    let prefPage: PreferencesPage;
+    let navBar: Navbar;
+    let calcPage: CalculatorPage;
+
+    beforeEach(async () => {
+        prefPage = new PreferencesPage();
+        navBar = new Navbar();
+        calcPage = new CalculatorPage();
+
+        // enable evil option "empty fields on module creation"
+        await prefPage.navigateTo();
+        await browser.sleep(200);
+        await prefPage.enableEvilEmptyFields();
+        await browser.sleep(200);
+    });
+
+    /**
+     * check that a input set is in a given (empty/filled) state
+     * @param inputIds id of the inputs to check
+     * @param emptys empty/not empty state array
+     */
+    async function checkFields(inputIds: string[], emptys: boolean[]) {
+        let n = 0;
+        for (const id of inputIds) {
+            const inp = await calcPage.getInputById(id);
+            const txt = await inp.getAttribute("value");
+            expect(txt === "").toEqual(emptys[n]);
+            n++;
+        }
+    }
+
+    it("when a standard fish ladder calculator is created", async () => {
+        // start page
+        await navBar.clickNewCalculatorButton();
+        await browser.sleep(200);
+
+        // open 1st example
+        const examples = await element.all(by.css("#examples-list .load-example"));
+        await examples[0].click();
+        await browser.sleep(50);
+
+        // select wall module
+        await navBar.openNthCalculator(4);
+        await browser.sleep(50);
+
+        // check fields are not empty
+        const inputIds = ["Z1", "LB", "PB", "0_L", "0_CdWSL"];
+        const emptys = [false, false, false, false, false];
+        await checkFields(inputIds, emptys);
+    });
+
+    it("calculated parameter initial value when discharge law is modified", async () => {
+        // start page
+        await navBar.clickNewCalculatorButton();
+        await browser.sleep(200);
+
+        // open 1st example
+        const examples = await element.all(by.css("#examples-list .load-example"));
+        await examples[0].click();
+        await browser.sleep(50);
+
+        // select wall module
+        await navBar.openNthCalculator(4);
+        await browser.sleep(50);
+
+        // modify 1st structure discharge law
+        const dischargeSelect = calcPage.getSelectById("select_loidebit");
+        await calcPage.changeSelectValue(dischargeSelect, 1);
+        await browser.sleep(200);
+
+        // open initial dialog
+        const initDlgButton = element(by.className("param-computed-more"));
+        await initDlgButton.click();
+        await browser.sleep(200);
+
+        // check input value is not null
+        const input = calcPage.getInputById("initval-input");
+        const underlyingInput = input.element(by.id("0_h1"));
+        const txt = await underlyingInput.getAttribute("value");
+        expect(txt === "").toEqual(false);
+    });
+});
+
+describe("ngHyd - Check that examples work with 'empty fields on calculator creation' enabled - ", () => {
+    let prefPage: PreferencesPage;
+    let navBar: Navbar;
+    let calcPage: CalculatorPage;
+
+    beforeEach(async () => {
+        prefPage = new PreferencesPage();
+        navBar = new Navbar();
+        calcPage = new CalculatorPage();
+
+        // enable evil option "empty fields on module creation"
+        await prefPage.navigateTo();
+        await browser.sleep(200);
+        await prefPage.enableEvilEmptyFields();
+        await browser.sleep(200);
+    });
+
+    it("when calculation is run on a generated fish ladder calculator", async () => {
+        debugger
+        // start page
+        await navBar.clickNewCalculatorButton();
+        await browser.sleep(200);
+
+        // open 1st example
+        const examples = await element.all(by.css("#examples-list .load-example"));
+        await examples[0].click();
+        await browser.sleep(50);
+
+        // select wall module
+        await navBar.openNthCalculator(4);
+        await browser.sleep(50);
+
+        // run calculation
+        const calcButton = calcPage.getCalculateButton();
+        await calcButton.click();
+        await browser.sleep(200);
+
+        // click "generate PAB" button
+        const genButton = calcPage.getGeneratePabButton();
+        await genButton.click();
+        await browser.sleep(200);
+
+        // write "6" in basin count input
+        const nbBassins = calcPage.getInputById("generatePabNbBassins");
+        await nbBassins.sendKeys("6");
+        await browser.sleep(50);
+
+        // click "Generate PAB"
+        await element(by.css("dialog-generate-pab button#do-generate")).click();
+        await browser.sleep(1000);
+
+        // calculate PAB
+        const calcButtonPAB = calcPage.getCalculateButton();
+        await calcButtonPAB.click();
+        await browser.sleep(200);
+
+        // check that result is not empty
+        const hasResults = await calcPage.hasResults();
+        expect(hasResults).toBe(true);
+    });
+});
diff --git a/e2e/load-linked-params.e2e-spec.ts b/e2e/load-linked-params.e2e-spec.ts
index 8e87b5f94762afde2a6fca298d6dd80ba5d00cef..00b262f24c65706e18f94c97edcc308e7e1a6cfd 100644
--- a/e2e/load-linked-params.e2e-spec.ts
+++ b/e2e/load-linked-params.e2e-spec.ts
@@ -34,6 +34,10 @@ describe("ngHyd − load session with multiple linked parameters − ", () => {
         await prefPage.navigateTo();
         await prefPage.changeLanguage(1); // fr
         await browser.sleep(200);
+        // disable evil option "empty fields on module creation"
+        await prefPage.disableEvilEmptyFields();
+        await browser.sleep(200);
+        // start page
         await navbar.clickNewCalculatorButton();
     });
 
diff --git a/e2e/ouvrages-empty-fields.e2e-spec.ts b/e2e/ouvrages-empty-fields.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6eac089d57e978822582a07de47244ede81f3db7
--- /dev/null
+++ b/e2e/ouvrages-empty-fields.e2e-spec.ts
@@ -0,0 +1,166 @@
+import { ListPage } from "./list.po";
+import { browser, by, element } from "protractor";
+import { CalculatorPage } from "./calculator.po";
+import { AppPage } from "./app.po";
+import { PreferencesPage } from "./preferences.po";
+import { Navbar } from "./navbar.po";
+
+/**
+ * Check that created/cloned structures have empty fields when
+ * "empty fields on calculator creation" is enabled
+ */
+describe("ngHyd - check that created/cloned structures have empty fields - ", () => {
+    let prefPage: PreferencesPage;
+    let listPage: ListPage;
+    let calcPage: CalculatorPage;
+    let navBar: Navbar;
+
+    beforeAll(() => {
+        prefPage = new PreferencesPage();
+        listPage = new ListPage();
+        calcPage = new CalculatorPage();
+        navBar = new Navbar();
+        browser.manage().window().setPosition(2000, 30);
+    });
+
+    beforeEach(async () => {
+        // enable evil option "empty fields on module creation"
+        await prefPage.navigateTo();
+        await browser.sleep(200);
+        await prefPage.enableEvilEmptyFields();
+        await browser.sleep(200);
+    });
+
+    async function setup() {
+        // start page
+        await navBar.clickNewCalculatorButton();
+        await browser.sleep(200);
+
+        // open structures calculator
+        await listPage.clickMenuEntryForCalcType(8);
+        await browser.sleep(200);
+    }
+
+    /**
+     * check that a input set is in a given (empty/filled) state
+     * @param inputIds id of the inputs to check
+     * @param emptys empty/not empty state array
+     */
+    async function checkFields(inputIds: string[], emptys: boolean[]) {
+        let n = 0;
+        for (const id of inputIds) {
+            const inp = await calcPage.getInputById(id);
+            const txt = await inp.getAttribute("value");
+            expect(txt === "").toEqual(emptys[n]);
+            n++;
+        }
+    }
+
+    it("when a structure calculator is created", async () => {
+        await setup();
+
+        // check 1st structure empty fields
+        const inputIds = ["Q", "Z1", "Z2", "0_ZDV", "0_L", "0_W", "0_CdGR"];
+        const emptys = [true, true, true, true, true, true, false];
+        await checkFields(inputIds, emptys);
+
+        // change 1st structure type to rectangular weir
+        const structSelect = calcPage.getSelectById("select_structure");
+        await calcPage.changeSelectValue(structSelect, 1);
+        await browser.sleep(200);
+
+        // check 1st structure empty fields
+        const inputIds2 = ["Q", "Z1", "Z2", "0_ZDV", "0_L", "0_CdWR"];
+        const emptys2 = [true, true, true, true, true, false];
+        await checkFields(inputIds2, emptys2);
+    });
+
+    it("when a structure is added", async () => {
+        await setup();
+
+        // add structure
+        const addStruct = calcPage.getAddStructureButton();
+        await addStruct.click();
+
+        // check 2nd structure empty fields
+        const inputIds = ["1_ZDV", "1_L", "1_W", "1_CdGR"];
+        const emptys = [true, true, true, false];
+        await checkFields(inputIds, emptys);
+    });
+
+    it("when a structure is copied", async () => {
+        await setup();
+
+        // copy structure
+        const addStruct = calcPage.getCopyStructureButton();
+        await addStruct.click();
+
+        // check 2nd structure empty fields
+        const inputIds = ["1_ZDV", "1_L", "1_W", "1_CdGR"];
+        const emptys = [true, true, true, false];
+        await checkFields(inputIds, emptys);
+    });
+
+    it("when a modified structure is copied (type)", async () => {
+        await setup();
+
+        // change 1st structure type to rectangular weir
+        const structSelect = calcPage.getSelectById("select_structure");
+        await calcPage.changeSelectValue(structSelect, 1);
+        await browser.sleep(200);
+
+        // copy structure
+        const addStruct = calcPage.getCopyStructureButton();
+        await addStruct.click();
+        await browser.sleep(200);
+
+        // change 2nd structure type to rectangular gate
+        const selects = await element.all(by.css("mat-select#select_structure"));
+        const structSelect2 = selects[1];
+        await calcPage.changeSelectValue(structSelect2, 4);
+        await browser.sleep(200);
+
+        // check empty fields
+        const inputIds = ["1_ZDV", "1_L", "1_W", "1_CdGR"];
+        const emptys = [true, true, true, false];
+        await checkFields(inputIds, emptys);
+    });
+
+    it("when a modified structure is copied (input)", async () => {
+        await setup();
+
+        // fill 
+        const inp = calcPage.getInputById("0_ZDV");
+        await inp.clear();
+        await inp.sendKeys("1");
+
+        // copy structure
+        const addStruct = calcPage.getCopyStructureButton();
+        await addStruct.click();
+        await browser.sleep(200);
+
+        // check empty fields
+        const inputIds = ["1_ZDV", "1_L", "1_W", "1_CdGR"];
+        const emptys = [false, true, true, false];
+        await checkFields(inputIds, emptys);
+    });
+
+    it("except for discharge coefficient when a Larinier weir is used", async () => {
+        await setup();
+
+        // change 1st structure type to rectangular weir
+        const structSelect = calcPage.getSelectById("select_structure");
+        await calcPage.changeSelectValue(structSelect, 1);
+        await browser.sleep(200);
+
+        // change discharge law to Larinier
+        const dischargeSelect = calcPage.getSelectById("select_loidebit");
+        await calcPage.changeSelectValue(dischargeSelect, 3);
+        await browser.sleep(200);
+
+        // check empty fields
+        const inputIds = ["0_ZDV", "0_L", "0_CdWSL"];
+        const emptys = [true, true, true];
+        await checkFields(inputIds, emptys);
+    });
+});
diff --git a/e2e/pab-cloisons-empty-fields.e2e-spec.ts b/e2e/pab-cloisons-empty-fields.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7e99a7de489e6eb9effcb0da652816bed78fbbb4
--- /dev/null
+++ b/e2e/pab-cloisons-empty-fields.e2e-spec.ts
@@ -0,0 +1,135 @@
+import { ListPage } from "./list.po";
+import { PreferencesPage } from "./preferences.po";
+import { browser, by, element } from "protractor";
+import { CalculatorPage } from "./calculator.po";
+import { AppPage } from "./app.po";
+import { Navbar } from "./navbar.po";
+
+/**
+ * enable evil option "empty fields on module creation"
+ */
+async function enableEmptyFieldsOption(prefPage: PreferencesPage) {
+    await prefPage.navigateTo();
+    await browser.sleep(200);
+    await prefPage.enableEvilEmptyFields();
+    await browser.sleep(200);
+}
+
+/**
+ * check that a input set is in a given (empty/filled) state
+ * @param inputIds id of the inputs to check
+ * @param emptys empty/not empty state array
+ */
+async function checkFields(calcPage: CalculatorPage, inputIds: string[], emptys: boolean[]) {
+    let n = 0;
+    for (const id of inputIds) {
+        const inp = await calcPage.getInputById(id);
+        const txt = await inp.getAttribute("value");
+        expect(txt === "").toEqual(emptys[n]);
+        n++;
+    }
+}
+
+async function fillInput(calcPage: CalculatorPage, symbol: string) {
+    const inp = calcPage.getInputById(symbol);
+    await inp.clear();
+    await inp.sendKeys("1");
+}
+
+/**
+ * Check that when "empty fields on calculator creation" is enabled
+ * fields are empty in the "generate fish ladder" dialog of the
+ * cross walls calculator
+ */
+describe("ngHyd - check the cross walls calculator has empty fields - ", () => {
+    let prefPage: PreferencesPage;
+    let listPage: ListPage;
+    let calcPage: CalculatorPage;
+    let navBar: Navbar;
+
+    beforeAll(() => {
+        prefPage = new PreferencesPage();
+        listPage = new ListPage();
+        calcPage = new CalculatorPage();
+        navBar = new Navbar();
+    });
+
+    beforeEach(async () => {
+        await enableEmptyFieldsOption(prefPage);
+    });
+
+    it("in the 'generate fish ladder' dialog", async () => {
+        // open cross walls calculator
+        await navBar.clickNewCalculatorButton();
+        await listPage.clickMenuEntryForCalcType(10);
+        await browser.sleep(200);
+
+        // fill inputs
+        await fillInput(calcPage, "Z1");
+        await fillInput(calcPage, "LB");
+        await fillInput(calcPage, "BB");
+        await fillInput(calcPage, "PB");
+        await fillInput(calcPage, "DH");
+        await fillInput(calcPage, "0_h1");
+        await fillInput(calcPage, "0_L");
+        await fillInput(calcPage, "0_CdWSL");
+
+        // calculate
+        const calcButton = calcPage.getCalculateButton();
+        await calcButton.click();
+        await browser.sleep(200);
+
+        // click "generate PAB" button
+        const genButton = calcPage.getGeneratePabButton();
+        await genButton.click();
+        await browser.sleep(200);
+
+        await checkFields(calcPage, ["generatePabNbBassins"], [true]);
+    });
+});
+
+/**
+ * Check that when "empty fields on calculator creation" is enabled
+ * fields are not empty after calculation
+ */
+describe("ngHyd - check the cross walls calculator has no empty field - ", () => {
+    let prefPage: PreferencesPage;
+    let listPage: ListPage;
+    let calcPage: CalculatorPage;
+    let navBar: Navbar;
+
+    beforeAll(() => {
+        prefPage = new PreferencesPage();
+        listPage = new ListPage();
+        calcPage = new CalculatorPage();
+        navBar = new Navbar();
+    });
+
+    beforeEach(async () => {
+        await enableEmptyFieldsOption(prefPage);
+    });
+
+    it("after calculation", async () => {
+        // open cross walls calculator
+        await navBar.clickNewCalculatorButton();
+        await listPage.clickMenuEntryForCalcType(10);
+        await browser.sleep(200);
+
+        // fill inputs
+        await fillInput(calcPage, "Z1");
+        await fillInput(calcPage, "LB");
+        await fillInput(calcPage, "BB");
+        await fillInput(calcPage, "PB");
+        await fillInput(calcPage, "DH");
+        await fillInput(calcPage, "0_h1");
+        await fillInput(calcPage, "0_L");
+        await fillInput(calcPage, "0_CdWSL");
+
+        // calculate
+        const calcButton = calcPage.getCalculateButton();
+        await calcButton.click();
+        await browser.sleep(200);
+
+        await checkFields(calcPage, ["Z1", "LB", "BB", "PB", "DH", "0_h1", "0_L", "0_CdWSL"], [false, false, false, false, false, false, false, false]);
+    });
+});
diff --git a/e2e/predam-empty-fields.e2e-spec.ts b/e2e/predam-empty-fields.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68db87689b478f2cc5693494404d00bf927575e0
--- /dev/null
+++ b/e2e/predam-empty-fields.e2e-spec.ts
@@ -0,0 +1,81 @@
+import { browser, by, element } from "protractor";
+import { CalculatorPage } from "./calculator.po";
+import { ListPage } from "./list.po";
+import { Navbar } from "./navbar.po";
+import { PreferencesPage } from "./preferences.po"
+
+/**
+ * check that fields are empty on creation
+ */
+describe("ngHyd − check that predam fields are empty on creation", () => {
+    let listPage: ListPage;
+    let prefPage: PreferencesPage;
+    let navBar: Navbar;
+    let calcPage: CalculatorPage;
+
+    beforeEach(async () => {
+        prefPage = new PreferencesPage();
+        listPage = new ListPage();
+        navBar = new Navbar();
+        calcPage = new CalculatorPage();
+
+        // enable evil option "empty fields on module creation"
+        await prefPage.navigateTo();
+        await browser.sleep(200);
+        await prefPage.enableEvilEmptyFields();
+        await browser.sleep(200);
+    });
+
+    it("when a basin is added", async () => {
+        // open predam calculator
+        await navBar.clickNewCalculatorButton();
+        await listPage.clickMenuEntryForCalcType(30);
+        await browser.sleep(200);
+
+        // add basin
+        const addBasinBtn = element(by.id("add-basin"));
+        await addBasinBtn.click();
+        await browser.sleep(200);
+
+        // check "surface" input is empty
+        let inp = calcPage.getInputById("3_S");
+        let txt = await inp.getAttribute("value");
+        expect(txt).toEqual("");
+
+        // check "cote de fond" input is empty
+        inp = calcPage.getInputById("3_ZF");
+        txt = await inp.getAttribute("value");
+        expect(txt).toEqual("");
+    });
+
+    it("when a wall is added", async () => {
+        // open predam calculator
+        await navBar.clickNewCalculatorButton();
+        await listPage.clickMenuEntryForCalcType(30);
+        await browser.sleep(200);
+
+        // add wall
+        const addWallBtn = element(by.id("add-wall"));
+        await addWallBtn.click();
+
+        // connect basins
+        const connectBasinsBtn = element(by.id("validate-connect-basins"));
+        await connectBasinsBtn.click();
+
+        // check ZDV input is empty
+        let inp = calcPage.getInputById("0_ZDV");
+        let txt = await inp.getAttribute("value");
+        expect(txt).toEqual("");
+
+        // check L input is empty
+        inp = calcPage.getInputById("0_L");
+        txt = await inp.getAttribute("value");
+        expect(txt).toEqual("");
+
+        // check CdWSL input is empty (in this case, the structure happens to be a Larinier weir
+        // which discharge coefficient must be empty)
+        inp = calcPage.getInputById("0_CdWSL");
+        txt = await inp.getAttribute("value");
+        expect(txt).toEqual("");
+    });
+});
diff --git a/e2e/preferences.po.ts b/e2e/preferences.po.ts
index 016d2da96c91a27e730c757f5382e60db283b088..ff9ff36aefb1f3b8297e9375816a013bcf7529d4 100644
--- a/e2e/preferences.po.ts
+++ b/e2e/preferences.po.ts
@@ -41,6 +41,14 @@ export class PreferencesPage {
         await option.click();
     }
 
+    async enableEvilEmptyFields() {
+        const cb = this.getEmptyFieldsCheckbox();
+        const underlyingCB = cb.element(by.css(`input.mat-checkbox-input`));
+        if (!underlyingCB.isSelected()) {
+            await cb.click();
+        }
+    }
+
     async disableEvilEmptyFields() {
         const cb = this.getEmptyFieldsCheckbox();
         const underlyingCB = cb.element(by.css(`input.mat-checkbox-input`));
diff --git a/e2e/remous.e2e-spec.ts b/e2e/remous.e2e-spec.ts
index 5c4603946995d87a1e02285d0a1e2198cf8c7877..f79ad021fc64051b3603da1f4ef797663c59e3ff 100644
--- a/e2e/remous.e2e-spec.ts
+++ b/e2e/remous.e2e-spec.ts
@@ -47,4 +47,27 @@ describe("ngHyd − remous", () => {
         // 5. there should still be 6 messages in the log
         expect(await calcPage.nbLogEntries()).toBe(6);
     });
+
+    it("Calculation with large bed width should run successfully", async () => {
+        await navBar.clickNewCalculatorButton();
+
+        // 1. create new Remous
+        await listPage.clickMenuEntryForCalcType(4);
+        await browser.sleep(300);
+
+        // 2. Set to trapezoidal section with bank slope of 2m/m and 20 meter width bed
+        await calcPage.changeSelectValue(calcPage.getSelectById("select_section"), 2);
+        await browser.sleep(300);
+        await calcPage.getInputById("LargeurFond").clear();
+        await browser.sleep(300);
+        await calcPage.getInputById("LargeurFond").sendKeys("20");
+        await calcPage.getInputById("Fruit").clear();
+        await browser.sleep(300);
+        await calcPage.getInputById("Fruit").sendKeys("2");
+
+        // 3. Calculate, the calculation should succeed
+        await calcPage.getCalculateButton().click();
+        const hasResults = await calcPage.hasResults();
+        expect(hasResults).toBe(true);
+    });
 });
diff --git a/e2e/reset-param-mode.e2e-spec.ts b/e2e/reset-param-mode.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7cc73c00746d957dac66f214a2609035e281c27d
--- /dev/null
+++ b/e2e/reset-param-mode.e2e-spec.ts
@@ -0,0 +1,42 @@
+import { ListPage } from "./list.po";
+import { browser, by, element, ElementFinder } from "protractor";
+import { CalculatorPage } from "./calculator.po";
+
+/**
+ * Parameter mode should be set to its previous mode (fixed/var/...) when cancel is pressed
+ * in variable mode edition dialog
+ */
+describe("ngHyd - check parameter mode is set to its previous value - ", () => {
+    let listPage: ListPage;
+
+    beforeAll(async () => {
+        listPage = new ListPage();
+    });
+
+    it("when min/max/list values dialog is cancelled", async () => {
+        // start page
+        await listPage.navigateTo();
+
+        // open PAB chute
+        await listPage.clickMenuEntryForCalcType(12);
+
+        // click "calc" radio on Z1 parameter
+        const z1calcbtn = element(by.id("mat-button-toggle-3"));
+        await z1calcbtn.click();
+
+        // click "var" radio on Z1 parameter
+        const z1varbtn = element(by.id("mat-button-toggle-2"));
+        await z1varbtn.click();
+
+        // click cancel button
+        const cancelbtn = element(by.id("btn-cancel"));
+        await cancelbtn.click();
+        await browser.sleep(200);
+
+        // check Z1 var toggle is disabled
+        expect(await z1varbtn.getAttribute("ng-reflect-checked")).toBe("false");
+
+        // check Z1 calc toggle is enabled
+        expect(await z1calcbtn.getAttribute("ng-reflect-checked")).toBe("true");
+    });
+});
diff --git a/e2e/variable-param-cancel.e2e-spec.ts b/e2e/variable-param-cancel.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abe96d8fe36f4a44466ba12e785352fc6df2d73e
--- /dev/null
+++ b/e2e/variable-param-cancel.e2e-spec.ts
@@ -0,0 +1,30 @@
+import { ListPage } from "./list.po";
+import { by, element } from "protractor";
+
+/**
+ * Check that a cancel button is present in min/max/list edition dialog
+ * for variable parameters
+ */
+describe("ngHyd - check cancel button for variable parameters - ", () => {
+    let listPage: ListPage;
+
+    beforeAll(async () => {
+        listPage = new ListPage();
+    });
+
+    it("when min/max/list values dialog opens, a cancel button should be present", async () => {
+        // start page
+        await listPage.navigateTo();
+
+        // open PAB chute
+        await listPage.clickMenuEntryForCalcType(12);
+
+        // click "var" radio on Z1 parameter
+        const z1btn = element(by.id("mat-button-toggle-2-button"));
+        await z1btn.click();
+
+        // cancel button presence
+        const cancelbtn = element(by.id("btn-cancel"));
+        expect(await cancelbtn.isPresent() && await cancelbtn.isDisplayed()).toBeTruthy();
+    });
+});
diff --git a/jalhyd_branch b/jalhyd_branch
index 1f7391f92b6a3792204e07e99f71f643cc35e7e1..626e97d71d9e3364f9abe16f7e3c8d70dea5a7fa 100644
--- a/jalhyd_branch
+++ b/jalhyd_branch
@@ -1 +1 @@
-master
+devel
\ No newline at end of file
diff --git a/protractor.conf.js b/protractor.conf.js
index 3077d88c4a22b765dc4e56950ea0bc26cf305a45..d474c230b2a8c1592f135dd650a605a8af92ec03 100644
--- a/protractor.conf.js
+++ b/protractor.conf.js
@@ -61,7 +61,7 @@ exports.config = {
     require('ts-node').register({
       project: 'e2e/tsconfig.e2e.json'
     });
-    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: "pretty" } }));
+    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
     browser.manage().window().setSize(1600, 1000);
     browser.driver.sendChromiumCommand('Page.setDownloadBehavior', {
       behavior: 'allow',
diff --git a/scripts/deploy-version.sh b/scripts/deploy-version.sh
new file mode 100755
index 0000000000000000000000000000000000000000..59ed409376a5780a12818a89111dfa511d714303
--- /dev/null
+++ b/scripts/deploy-version.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+set -o errexit
+
+# deploie l'appli sur le serveur $3 (user $2/mot de passe $5) dans le dossier $4 et met à jour le fichier index.html
+# si la version est "stable"
+
+if (( $# < 4 || $# > 5 )); then
+    echo "usage: $0 <version> <login> <server> <path> [<password>]"
+    exit 1
+fi
+
+VERSION="$1"
+LOGIN="$2"
+HOST="$3"
+DIR="$4"
+PASS="$5"
+
+echo "$(basename $0): deploying version $VERSION in $LOGIN@$HOST:$DIR"
+
+if [[ $VERSION == "stable" ]]; then
+  # Copie de la branche production
+  rsync -a --delete --exclude=cassiopee-releases -e "ssh -o StrictHostKeyChecking=no" dist/ ${LOGIN}@${HOST}:${DIR}/
+
+  # Modification du dossier base href
+  echo "updating index.html"
+  ssh $LOGIN@$HOST "sed -i 's:/cassiopee/stable/:/:g' $DIR/index.html"
+else
+  # Copie de la branche / du tag
+  rsync -a --delete --exclude=cassiopee-releases -e "ssh -o StrictHostKeyChecking=no" "dist/" "$LOGIN@$HOST:$DIR/$VERSION"
+fi
diff --git a/scripts/release-version.sh b/scripts/release-version.sh
index 9952d413406c8dcddf72199ad1a83ef9a29b62b7..1f810669edb9478732e78422e6ef76da8e81ad17 100755
--- a/scripts/release-version.sh
+++ b/scripts/release-version.sh
@@ -1,36 +1,50 @@
 #!/bin/bash
 
+set -o errexit
+
 # Fabrique les exécutables electron/cordova pour une version de Cassiopée $1, les
-# distribue sur le serveur $2 dans le dossier $3, et met à jour le fichier releases.json
+# distribue sur le serveur $2@$3 dans le dossier $4, et met à jour le fichier releases.json
+# et les liens symboliques si la version n'est pas nightly
+
+echo "args $@"
 
-if [ "$#" -lt 3 ]; then
-    echo "usage: $0 X.Y.Z login@server /var/www/releases_dir"
+if (( $# < 4 )); then
+    echo "usage: $0 <X.Y.Z|nightly> <login> <server> <releases dir>"
     exit 1
 fi
 
 VERSION="$1"
-HOST_LOGIN="$2"
-RELEASES_DIR="$3"
+LOGIN="$2"
+HOST="$3"
+RELEASES_DIR="$4"
 # RELEASES_DIR="/tmp/cassiopee-releases" # debug
 RELEASES_FILE="$RELEASES_DIR/releases.json"
 TMP_RELEASES_FILE="tmp-releases.json"
+HOST_LOGIN=$LOGIN@$HOST
+
+# update flag for releases file/symlinks
+update_latest=1
+if [[ $VERSION == "nightly" ]]; then
+  update_latest=0
+fi
 
-echo "deploy-version.sh: building release for version $VERSION, deploying in $HOST_LOGIN:$RELEASES_DIR"
+echo "release-version.sh: building release for version $VERSION, deploying in $HOST_LOGIN:$RELEASES_DIR"
 
 # build releases
 npm run release-all
 
 # update existing releases file
+if (( $update_latest )); then
+  echo "updating releases.json"
 
-# fetch current releases file
-scp "$HOST_LOGIN:$RELEASES_FILE" "./$TMP_RELEASES_FILE"
+  # fetch current releases file
+  scp "$HOST_LOGIN:$RELEASES_FILE" "./$TMP_RELEASES_FILE"
 
-grep -P "\"latest\": \"$VERSION\"" "$TMP_RELEASES_FILE"
+  grep -P "\"latest\": \"$VERSION\"" "$TMP_RELEASES_FILE"
 
-if [[ $? == 0 ]]
-then
+  if [[ $? == 0 ]]; then
     echo "$VERSION est déjà la version la plus récente, pas de mise à jour du fichier releases.json"
-else
+  else
     sed -i -E "s/\"latest\": .+,/\"latest\": \"$VERSION\",/" "$TMP_RELEASES_FILE"
     echo -e "\t\"$VERSION\": {
 \t\t\"darwin\": \"Cassiopée-${VERSION}-mac.zip\",
@@ -41,20 +55,34 @@ else
 
     sed -i "/\"latest\": \"$VERSION\",/r releases_patch.tmp" "$TMP_RELEASES_FILE"
     rm releases_patch.tmp
-fi
+  fi
 
-# send updated file, remove local copy
-scp "./$TMP_RELEASES_FILE" "$HOST_LOGIN:$RELEASES_FILE"
-rm "./$TMP_RELEASES_FILE"
+  # send updated file, remove local copy
+  scp "./$TMP_RELEASES_FILE" "$HOST_LOGIN:$RELEASES_FILE"
+  rm "./$TMP_RELEASES_FILE"
+fi
 
 # copy releases to public web directory
-scp "release/Cassiopée Setup $VERSION.exe" "release/fr.irstea.cassiopee_${VERSION}_amd64.deb" "release/cassiopee-$VERSION.apk" "release/Cassiopée-${VERSION}-mac.zip" "$HOST_LOGIN:$RELEASES_DIR/"
+ssh $HOST_LOGIN mkdir -p $RELEASES_DIR
+if [[ $VERSION == nightly ]]; then
+  find release -name "fr.irstea.cassiopee_*.deb" -exec scp "{}" $HOST_LOGIN:$RELEASES_DIR/linux-nightly.deb \;
+  find release -name "Cassio*Setup*.exe" -exec scp "{}" $HOST_LOGIN:$RELEASES_DIR/windows-nightly.exe \;
+  find release -name "Cassio*-mac.zip" -exec scp "{}" $HOST_LOGIN:$RELEASES_DIR/macos-nightly.zip \;
+  find release -name "cassiopee-*.apk" -exec scp "{}" $HOST_LOGIN:$RELEASES_DIR/android-nightly.apk \;
+else
+  scp "release/Cassiopée Setup $VERSION.exe" "release/fr.irstea.cassiopee_${VERSION}_amd64.deb" "release/cassiopee-$VERSION.apk" "release/Cassiopée-${VERSION}-mac.zip" "$HOST_LOGIN:$RELEASES_DIR/"
+fi
 
 # symlink "latest" version for each platform
-ssh $HOST_LOGIN /bin/bash << EOF
+
+if (( $update_latest )); then
+  echo "updating 'latest' symlinks"
+
+  ssh $HOST_LOGIN /bin/bash << EOF
     cd "$RELEASES_DIR"
     ln -sf "Cassiopée Setup $VERSION.exe" "windows-latest.exe"
     ln -sf "fr.irstea.cassiopee_${VERSION}_amd64.deb" "linux-latest.deb"
     ln -sf "Cassiopée-${VERSION}-mac.zip" "macos-latest.zip"
     ln -sf "cassiopee-$VERSION.apk" "android-latest.apk"
 EOF
+fi
diff --git a/src/app/calculators/cloisons/config.json b/src/app/calculators/cloisons/config.json
index 328ffe0616421cc8e4f8d0d9606cb3120e3fc40a..82ccf331ee69f680da4dc26b1dc593fd26d58406 100644
--- a/src/app/calculators/cloisons/config.json
+++ b/src/app/calculators/cloisons/config.json
@@ -31,14 +31,14 @@
                 "property": "loiDebit",
                 "source": "device_loi_debit",
                 "help": {
-                    "Orifice_OrificeSubmerged": "structures/orifice_noye.html",
-                    "SeuilRectangulaire_WeirVillemonte": "structures/kivi.html",
-                    "SeuilRectangulaire_WeirSubmergedLarinier": "structures/fente_noyee.html",
-                    "SeuilRectangulaire_WeirCunge80": "structures/cunge_80.html",
-                    "VanneRectangulaire_GateCunge80": "structures/cunge_80.html",
-                    "SeuilTriangulaire_TriangularWeirFree": "structures/dever_triang.html",
-                    "SeuilTriangulaire_TriangularWeirBroad": "structures/dever_triang.html",
-                    "SeuilTriangulaireTrunc_TriangularTruncWeirFree": "structures/dever_triang_tronque.html"
+                    "OrificeSubmerged": "structures/orifice_noye.html",
+                    "WeirSubmergedLarinier": "structures/fente_noyee.html",
+                    "WeirVillemonte": "structures/kivi.html",
+                    "WeirCem88d": "structures/cem_88_d.html",
+                    "TriangularWeirFree": "structures/dever_triang.html",
+                    "TriangularWeirBroad": "structures/dever_triang.html",
+                    "TriangularTruncWeirFree": "structures/dever_triang_tronque.html",
+                    "GateCem88d": "structures/cem_88_d.html"
                 }
             },
             "h1",
@@ -68,4 +68,4 @@
         "selectIds": [ "select_structure", "select_loidebit" ],
         "help": "pab/cloisons.html"
     }
-]
\ No newline at end of file
+]
diff --git a/src/app/calculators/courberemous/config.json b/src/app/calculators/courberemous/config.json
index 455e9d73bf283f076173b08721b5223c4212a900..272de86872224c219f6fe666b9a56243dab70180 100644
--- a/src/app/calculators/courberemous/config.json
+++ b/src/app/calculators/courberemous/config.json
@@ -89,7 +89,7 @@
                     "Hsc": "hsl/section_parametree.html#la-charge-critique-m",
                     "J": "hsl/section_parametree.html#la-perte-de-charge-mm",
                     "I-J": "hsl/section_parametree.html#la-variation-lineaire-de-lenergie-specifique-mm",
-                    "Imp": "hsl/section_parametree.html#limpulsion-hydraulique-kgms-1",
+                    "Imp": "hsl/section_parametree.html#limpulsion-hydraulique-N",
                     "Tau0": "hsl/section_parametree.html#la-force-tractrice-pa"
                 }
             }
@@ -115,7 +115,7 @@
             "Hsc": "hsl/section_parametree.html#la-charge-critique-m",
             "J": "hsl/section_parametree.html#la-perte-de-charge-mm",
             "I-J": "hsl/section_parametree.html#la-variation-lineaire-de-lenergie-specifique-mm",
-            "Imp": "hsl/section_parametree.html#limpulsion-hydraulique-kgms-1",
+            "Imp": "hsl/section_parametree.html#limpulsion-hydraulique-N",
             "Tau0": "hsl/section_parametree.html#la-force-tractrice-pa"
         }
     }
diff --git a/src/app/calculators/pabdimensions/en.json b/src/app/calculators/pabdimensions/en.json
index df5b11b452d5814c96e3d5dcfd82bb520ec62e39..88df27b9dbd4d3436d02d59fff71d637b4133217 100644
--- a/src/app/calculators/pabdimensions/en.json
+++ b/src/app/calculators/pabdimensions/en.json
@@ -2,6 +2,6 @@
     "fs_dimensions": "Basin dimensions",
     "L": "Length",
     "W": "Width",
-    "Y": "Draft",
+    "Y": "Water depth",
     "V": "Volume"
 }
\ No newline at end of file
diff --git a/src/app/calculators/parallelstructure/config.json b/src/app/calculators/parallelstructure/config.json
index f46051f9d6f6035102324c9380a4684e50aef504..3ac1aef2cfeda5da3ba2b81728bf66fa1960d946 100644
--- a/src/app/calculators/parallelstructure/config.json
+++ b/src/app/calculators/parallelstructure/config.json
@@ -36,14 +36,16 @@
                     "GateCem88d": "structures/cem_88_d.html",
                     "GateCem88v": "structures/cem_88_v.html",
                     "GateCunge80": "structures/cunge_80.html",
-                    "OrificeSubmerged": "structures/noye.html",
-                    "OrificeFree": "structures/denoye.html",
+                    "OrificeSubmerged": "structures/orifice_noye.html",
+                    "OrificeFree": "structures/orifice_denoye.html",
                     "WeirSubmergedLarinier": "structures/fente_noyee.html",
                     "WeirSubmerged": "structures/seuil_noye.html",
                     "WeirFree": "structures/seuil_denoye.html",
                     "TriangularWeirFree": "structures/dever_triang.html",
                     "TriangularWeirBroad": "structures/dever_triang.html",
-                    "TriangularTruncWeirFree": "structures/dever_triang_tronque.html"
+                    "TriangularTruncWeirFree": "structures/dever_triang_tronque.html",
+                    "RectangularOrificeFree": "structures/vanne_denoyee.html",
+                    "RectangularOrificeSubmerged": "structures/vanne_noyee.html"
                 }
             },
             "S",
@@ -82,4 +84,4 @@
             "ENUM_StructureJetType": "structures/lois_ouvrages.html#type-de-jet"
         }
     }
-]
\ No newline at end of file
+]
diff --git a/src/app/calculators/prebarrage/config.json b/src/app/calculators/prebarrage/config.json
index f97c4921f5c1c70386d89a18b36a8a5237edd337..3474300cdb19deeb219ba7d619b6be49817c019c 100644
--- a/src/app/calculators/prebarrage/config.json
+++ b/src/app/calculators/prebarrage/config.json
@@ -63,22 +63,24 @@
                         "property": "loiDebit",
                         "source": "device_loi_debit",
                         "help": {
-                            "SeuilRectangulaire_KIVI": "structures/kivi.html",
-                            "SeuilRectangulaire_WeirVillemonte": "structures/kivi.html",
-                            "SeuilRectangulaire_WeirCem88d": "structures/cem_88_d.html",
-                            "SeuilRectangulaire_WeirCem88v": "structures/cem_88_v.html",
-                            "SeuilRectangulaire_WeirCunge80": "structures/cunge_80.html",
-                            "VanneRectangulaire_GateCem88d": "structures/cem_88_d.html",
-                            "VanneRectangulaire_GateCem88v": "structures/cem_88_v.html",
-                            "VanneRectangulaire_GateCunge80": "structures/cunge_80.html",
-                            "Orifice_OrificeSubmerged": "structures/orifice_noye.html",
-                            "Orifice_OrificeFree": "structures/orifice_denoye.html",
-                            "SeuilRectangulaire_WeirSubmergedLarinier": "structures/fente_noyee.html",
-                            "SeuilRectangulaire_WeirSubmerged": "structures/seuil_noye.html",
-                            "SeuilRectangulaire_WeirFree": "structures/seuil_denoye.html",
-                            "SeuilTriangulaire_TriangularWeirFree": "structures/dever_triang.html",
-                            "SeuilTriangulaire_TriangularWeirBroad": "structures/dever_triang.html",
-                            "SeuilTriangulaireTrunc_TriangularTruncWeirFree": "structures/dever_triang_tronque.html"
+                            "KIVI": "structures/kivi.html",
+                            "WeirVillemonte": "structures/kivi.html",
+                            "WeirCem88d": "structures/cem_88_d.html",
+                            "WeirCem88v": "structures/cem_88_v.html",
+                            "WeirCunge80": "structures/cunge_80.html",
+                            "GateCem88d": "structures/cem_88_d.html",
+                            "GateCem88v": "structures/cem_88_v.html",
+                            "GateCunge80": "structures/cunge_80.html",
+                            "OrificeSubmerged": "structures/orifice_noye.html",
+                            "OrificeFree": "structures/orifice_denoye.html",
+                            "WeirSubmergedLarinier": "structures/fente_noyee.html",
+                            "WeirSubmerged": "structures/seuil_noye.html",
+                            "WeirFree": "structures/seuil_denoye.html",
+                            "TriangularWeirFree": "structures/dever_triang.html",
+                            "TriangularWeirBroad": "structures/dever_triang.html",
+                            "TriangularTruncWeirFree": "structures/dever_triang_tronque.html",
+                            "RectangularOrificeFree": "structures/vanne_denoyee.html",
+                            "RectangularOrificeSubmerged": "structures/vanne_noyee.html"
                         }
                     },
                     {
diff --git a/src/app/calculators/regimeuniforme/en.json b/src/app/calculators/regimeuniforme/en.json
index 4a5a3327a0e2ffbe7f28332ce77ceb2a024a95ac..f1ea50fcc1ea626eb16e607dd10eaf83ae590de8 100644
--- a/src/app/calculators/regimeuniforme/en.json
+++ b/src/app/calculators/regimeuniforme/en.json
@@ -17,5 +17,5 @@
     "If": "Bottom slope",
     "YB": "Embankment elevation",
     "fs_hydraulique": "Hydraulic features",
-    "Y": "Draft"
+    "Y": "Water depth"
 }
\ No newline at end of file
diff --git a/src/app/calculators/sectionparametree/config.json b/src/app/calculators/sectionparametree/config.json
index 63235d9a5b316dbeb7a0041f3ae72cd0c12b6265..f24b88ca8954faf65b6764ecdf60dc8231995ead 100644
--- a/src/app/calculators/sectionparametree/config.json
+++ b/src/app/calculators/sectionparametree/config.json
@@ -67,7 +67,7 @@
             "Hsc": "hsl/section_parametree.html#la-charge-critique-m",
             "J": "hsl/section_parametree.html#la-perte-de-charge-mm",
             "I-J": "hsl/section_parametree.html#la-variation-lineaire-de-lenergie-specifique-mm",
-            "Imp": "hsl/section_parametree.html#limpulsion-hydraulique-kgms-1",
+            "Imp": "hsl/section_parametree.html#limpulsion-hydraulique-N",
             "Tau0": "hsl/section_parametree.html#la-force-tractrice-pa"
         }
     }
diff --git a/src/app/calculators/sectionparametree/en.json b/src/app/calculators/sectionparametree/en.json
index 92a0479a837ab8d6a34e523b10719826aee6a5cc..b13efd538c2c52436eaa99a51dea2d958869c855 100644
--- a/src/app/calculators/sectionparametree/en.json
+++ b/src/app/calculators/sectionparametree/en.json
@@ -17,7 +17,7 @@
     "If": "Bottom slope",
     "YB": "Embankment elevation",
     "fs_hydraulique": "Hydraulic features",
-    "Y": "Draft",
+    "Y": "Water depth",
     "fs_param_calc": "Calculation parameters",
     "Hs": "Specific head",
     "Hsc": "Critical head",
diff --git a/src/app/components/app-setup/app-setup.component.html b/src/app/components/app-setup/app-setup.component.html
index 87f52357afe63aad94befa23b4cbb47f643ba248..bcd3946889ee454fcaa79c7139fcebadcf00051d 100644
--- a/src/app/components/app-setup/app-setup.component.html
+++ b/src/app/components/app-setup/app-setup.component.html
@@ -102,4 +102,4 @@
         </mat-card-content>
 
     </mat-card>
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/components/base-param-input/base-param-input.component.ts b/src/app/components/base-param-input/base-param-input.component.ts
index 25573571b021a64a06672559fa919981d00f8d8a..883fa077213c7a069a3942c163faceecdcf97e41 100644
--- a/src/app/components/base-param-input/base-param-input.component.ts
+++ b/src/app/components/base-param-input/base-param-input.component.ts
@@ -15,7 +15,7 @@ export class NgBaseParam extends Observable {
 
     constructor(symb: string, d: ParamDomain | ParamDomainValue, val: number, unit?: string) {
         super();
-        this._param = new ParamDefinition(null, symb, d, unit, val);
+        this._param = new ParamDefinition(null, symb, d, unit, val, undefined, undefined, false);
     }
 
     public get param() {
diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
index fcc96d90f5b96ff89aa806113a1e161274abdacb..cd37dad302aa6a325990cc70e314b5a7eeccbb69 100644
--- a/src/app/components/calculator-list/calculator-list.component.ts
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -181,10 +181,6 @@ export class CalculatorListComponent implements OnInit {
                 }
             }
         }
-
-        if (this.appSetupService.enableEmptyFieldsOnFormInit) {
-            f.emptyFields();
-        }
     }
 
     public get nbOpenCalculators() {
diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html
index 0ec1a116e403907574eb6db4a0c039b2061bbdb7..9037cd52b516c8eb956399ebcba17197100dff78 100644
--- a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html
+++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.html
@@ -3,11 +3,11 @@
 <form>
 
     <div mat-dialog-content>
-        <ngparam-input [title]="param.title"></ngparam-input>
+        <ngparam-input id="initval-input" [title]="param.title"></ngparam-input>
     </div>
 
     <div mat-dialog-actions [attr.align]="'end'">
-        <button mat-raised-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>
+        <button mat-raised-button color="primary" [mat-dialog-close]="true" (click)="onCancel()" cdkFocusInitial>
             {{ uitextCancel }}
         </button>
         <button mat-raised-button color="warn" (click)="onValidate()">
diff --git a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts
index 4984494eb4220fc5b73ae2b588afb6a7a62ca6bb..473ea11b0633c98a7ed3c0e8eed2e7440fc1e25a 100644
--- a/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts
+++ b/src/app/components/dialog-edit-param-computed/dialog-edit-param-computed.component.ts
@@ -25,12 +25,7 @@ export class DialogEditParamComputedComponent implements OnInit {
         @Inject(MAT_DIALOG_DATA) public data: any
     ) {
         // copy given parameter in a "fake" Ngparameter
-        const nP = data.param as NgParameter;
-        const p = nP.paramDefinition as ParamDefinition;
-        const pDef = new ParamDefinition(undefined, p.symbol, p.domain, p.unit, p.getValue(), p.family, p.visible);
-        this.param = new NgParameter(pDef, undefined);
-        this.param.setLabel(nP.label);
-        this.param.unit = nP.unit;
+        this.param = data.param;
     }
 
     public get uitextCancel(): string {
@@ -46,12 +41,19 @@ export class DialogEditParamComputedComponent implements OnInit {
     }
 
     public ngOnInit() {
+        this._ngParamInputComponent.editInitValue = true;
         this._ngParamInputComponent.model = this.param;
     }
 
+    public onCancel() {
+        this.dialogRef.close({
+            cancel: true
+        });
+    }
+
     public onValidate() {
         this.dialogRef.close({
-            value: this.param.getValue()
+            cancel: false
         });
     }
 }
diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html
index 8a454d5c176463e73a6ebf8b8f54c95be76b6121..c7b8d3803df4042c8549953787439651ba70b098 100644
--- a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html
+++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.html
@@ -1,4 +1,5 @@
-<button mat-icon-button id="show-values-chart" (click)="toggleViewChart()" [title]="uitextShowValuesChart">
+<button mat-icon-button id="show-values-chart" (click)="toggleViewChart()" [title]="uitextShowValuesChart"
+    [disabled]="!viewChart && !isFormValid">
     <mat-icon *ngIf="! viewChart">show_chart</mat-icon>
     <mat-icon *ngIf="viewChart">mode_edit</mat-icon>
 </button>
@@ -119,16 +120,11 @@
 </div>
 
 <div mat-dialog-actions [attr.align]="'end'">
-    <div *ngIf="isMinMax || viewChart">
-        <button mat-raised-button [mat-dialog-close]="true" [disabled]="minMaxFormInvalid" cdkFocusInitial>
-            {{ uitextClose }}
-        </button>
-    </div>
-    <div *ngIf="isListe && ! viewChart">
-        <button mat-raised-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>
+    <div>
+        <button id="btn-cancel" mat-raised-button color="primary" (click)="onCancel()" cdkFocusInitial>
             {{ uitextCancel }}
         </button>
-        <button mat-raised-button color="warn" (click)="onValidate()">
+        <button mat-raised-button color="warn" (click)="onValidate()" [disabled]=" !isFormValid&&!viewChart">
             {{ uitextValidate }}
         </button>
     </div>
diff --git a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts
index 3c419f000945cd25d2e946e2d8518ad516727484..f5200cfd0e09da4212e8b4e9dd1893184cc66c60 100644
--- a/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts
+++ b/src/app/components/dialog-edit-param-values/dialog-edit-param-values.component.ts
@@ -11,6 +11,7 @@ import { sprintf } from "sprintf-js";
 import { ParamValueMode, ExtensionStrategy } from "jalhyd";
 
 import { fv } from "../../util";
+import { ServiceFactory } from "app/services/service-factory";
 
 @Component({
     selector: "dialog-edit-param-values",
@@ -245,42 +246,64 @@ export class DialogEditParamValuesComponent implements OnInit {
         this.param.setValueList(this, vals);
     }
 
-    /** returns true if min/max/step form is invalid */
-    public get minMaxFormInvalid(): boolean {
-        return this.minMaxForm === undefined || ! this.minMaxForm.valid;
-    }
-
     public toggleViewChart() {
-        // validate list values before switching views ?
-        if (! this.viewChart && this.param.valueMode === ParamValueMode.LISTE) {
-            if (this.onValidate(false)) {
+        // validate values before switching views ?
+        if (!this.viewChart && this.isFormValid) {
                 // toggle
-                this.viewChart = ! this.viewChart;
+            this.viewChart = true;
             }
-        } else {
-            // toggle
-            this.viewChart = ! this.viewChart;
-        }
+        else
+            this.viewChart = false;
+
         // refresh chart when displaying it only
         if (this.viewChart) {
             this.drawChart();
         }
     }
 
-    public onValidate(close = true) {
-        const status = this.validateValuesListString(this.valuesListForm.controls.valuesList.value);
+    /** returns true if form is valid */
+    public get isFormValid(): boolean {
+        var ret: boolean = false;
+        switch (this.param.valueMode) {
+            case ParamValueMode.LISTE:
+                const status = this.validateValuesListString(this.valuesListForm.controls.valuesList.value);
+                ret = status.ok;
+                if (ret) {
+                    this.valuesListForm.controls.valuesList.setErrors(null);
+                    this.valuesList = this.valuesListForm.controls.valuesList.value;
+                }
+                else
+                    this.valuesListForm.controls.valuesList.setErrors({ "model": status.message });
+                break;
 
-        if (status.ok) {
-            this.valuesListForm.controls.valuesList.setErrors(null);
-            this.valuesList = this.valuesListForm.controls.valuesList.value;
-            if (close) {
-                this.dialogRef.close();
-            }
-            return true;
-        } else {
-            this.valuesListForm.controls.valuesList.setErrors({ "model": status.message });
-            return false;
+            case ParamValueMode.MINMAX:
+                ret = this.minMaxForm !== undefined && this.minMaxForm.valid;
+                break;
+        }
+        return ret;
+    }
+
+    public onCancel() {
+        this.dialogRef.close({
+            cancelled: true
+        });
+    }
+
+    public onValidate() {
+        switch (this.param.valueMode) {
+            case ParamValueMode.LISTE:
+                this.data.param.setValueList(this, this.param.valueList);
+                break;
+
+            case ParamValueMode.MINMAX:
+                this.data.param.setMinValue(this, this.param.minValue);
+                this.data.param.setMaxValue(this, this.param.maxValue);
+                this.data.param.setStepValue(this, this.param.stepValue);
+                break;
         }
+        this.dialogRef.close({
+            cancelled: false
+        });
     }
 
     /**
@@ -373,28 +396,29 @@ export class DialogEditParamValuesComponent implements OnInit {
     }
 
     private initVariableValues() {
+        const canFill: boolean = !ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit;
         // init min / max / step
         if (this.isMinMax) {
             const pVal = this.param.getValue();
-            if (this.param.minValue === undefined) {
-                this.param.setMinValue(this, pVal !== undefined ? this.param.getValue() / 2 : undefined);
+            if (this.param.minValue === undefined && canFill) {
+                this.param.resetMinValue(this, pVal !== undefined ? this.param.getValue() / 2 : undefined);
             }
-            if (this.param.maxValue === undefined) {
-                this.param.setMaxValue(this, pVal !== undefined ? this.param.getValue() * 2 : undefined);
+            if (this.param.maxValue === undefined && canFill) {
+                this.param.resetMaxValue(this, pVal !== undefined ? this.param.getValue() * 2 : undefined);
             }
             let step = this.param.stepValue;
-            if (step === undefined) {
+            if (step === undefined && canFill) {
                 step = pVal !== undefined ? ((this.param.maxValue - this.param.minValue) / 20) : undefined;
+                this.param.resetStepValue(this, step);
             }
-            this.param.setStepValue(this, step);
         }
         // init values list
         if (this.isListe) {
-            if (this.param.valueList === undefined) {
+            if (this.param.valueList === undefined && canFill) {
                 if (this.param.isDefined) {
-                    this.param.setValueList(this, [ this.param.getValue() ]);
+                    this.param.resetValueList(this, [this.param.getValue()]);
                 } else {
-                    this.param.setValueList(this, []);
+                    this.param.resetValueList(this, []);
                 }
                 // set form control initial value
                 this.valuesListForm.controls.valuesList.setValue(this.valuesList);
@@ -442,10 +466,6 @@ export class DialogEditParamValuesComponent implements OnInit {
         return this.intlService.localizeText("INFO_PARAMFIELD_PASVARIATION");
     }
 
-    public get uitextClose(): string {
-        return this.intlService.localizeText("INFO_OPTION_CLOSE");
-    }
-
     public get uitextCancel(): string {
         return this.intlService.localizeText("INFO_OPTION_CANCEL");
     }
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
index 9634a342bf3e3109f3670613af3adbb00a7cb1ff..202aa1fd89632c45871623cdc91bcbece1f12737 100644
--- a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
@@ -18,8 +18,6 @@ export class DialogGeneratePABComponent {
 
     public coteAmont = 102;
 
-    public chute: number;
-
     public nbBassins = 6;
 
     constructor(
@@ -29,21 +27,18 @@ export class DialogGeneratePABComponent {
         @Inject(MAT_DIALOG_DATA) public data: any
     ) {
         const nDigits = this.appSetupService.displayPrecision;
-        this.coteAmont = round(data.coteAmont, nDigits);
-        this.debit = round(data.debit, nDigits);
-        this.chute = round(data.chute, nDigits);
+        this.coteAmont = data.coteAmont ? round(data.coteAmont, nDigits) : undefined;
+        this.debit = data.debit ? round(data.debit, nDigits) : undefined;
+        this.nbBassins = data.nbBassins;
     }
 
     public generatePAB() {
-        // calculate downstream elevation
-        const coteAval = this.coteAmont - (this.chute * this.nbBassins);
         // create PAB
         this.dialogRef.close({
             generate: true,
-            debit: this.debit,
-            coteAmont: this.coteAmont,
-            coteAval: coteAval,
-            nbBassins: this.nbBassins
+            debit: +this.debit,
+            coteAmont: +this.coteAmont,
+            nbBassins: +this.nbBassins
         });
     }
 
diff --git a/src/app/components/dialog-new-pb-cloison/dialog-new-pb-cloison.component.html b/src/app/components/dialog-new-pb-cloison/dialog-new-pb-cloison.component.html
index a6587c7730d3cf912b851df5f0e55f237fd3bb87..fdded3ee4de8ef7ee55fb9a656e02cbf9bd3246c 100644
--- a/src/app/components/dialog-new-pb-cloison/dialog-new-pb-cloison.component.html
+++ b/src/app/components/dialog-new-pb-cloison/dialog-new-pb-cloison.component.html
@@ -21,7 +21,8 @@
         <button mat-raised-button color="primary" [mat-dialog-close]="true" cdkFocusInitial>
             {{ uitextCancel }}
         </button>
-        <button mat-raised-button color="warn" (click)="onValidate()" [disabled]="! enableValidate">
+        <button mat-raised-button id="validate-connect-basins" color="warn" (click)="onValidate()"
+            [disabled]="! enableValidate">
             {{ uitextValidate }}
         </button>
     </div>
diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts
index 45b87262e5329b8039177d1b4a22fe2014f720b5..d09ee6118fb4ad2208dda48e7e1d43193a9b4de7 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.ts
+++ b/src/app/components/fieldset-container/fieldset-container.component.ts
@@ -74,28 +74,16 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
      * Ajoute un nouveau sous-nub (Structure, PabCloisons, YAXN… selon le cas)
      * dans un nouveau fieldset
      */
-    private addSubNub(after?: FieldSet, clone: boolean = false) {
-        if (after) {
-            const newFs = this._container.addFromTemplate(0, after.indexAsKid());
-            if (this.appSetupService.enableEmptyFieldsOnFormInit && ! clone) {
-                newFs.emptyFields();
-            }
-            if (clone) {
-                // replace in-place to change properties (overkill)
-                // @WTF why only those two ?
-                newFs.setPropValue("structureType", after.properties.getPropValue("structureType"));
-                newFs.setPropValue("loiDebit", after.properties.getPropValue("loiDebit"));
-                // copy param values
-                for (const p of after.nub.prms) {
-                    newFs.nub.getParameter(p.symbol).loadObjectRepresentation(p.objectRepresentation());
-                }
-            }
-        } else {
-            const newFs = this._container.addFromTemplate(0);
-            if (this.appSetupService.enableEmptyFieldsOnFormInit && ! clone) {
-                newFs.emptyFields();
-            }
+    private addSubNub(after: FieldSet, clone: boolean = false) {
+        const prms = after.backupParameters();
+        const newFs = this._container.addFromTemplate(0, after.indexAsKid());
+        if (clone) {
+            // replace in-place to change properties (overkill)
+            // @WTF why only those two ?
+            newFs.setPropValue("structureType", after.properties.getPropValue("structureType"));
+            newFs.setPropValue("loiDebit", after.properties.getPropValue("loiDebit"));
         }
+        newFs.restoreParameters(prms);
     }
 
     private onFieldsetListChange() {
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 718ff08f5a873b395b553155be771e2e4b3d3a69..9650543c6a0add5f27c5b2c5bb7aa61ea0e44f64 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -1,5 +1,7 @@
-import { Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren,
-         QueryList, AfterViewChecked, ElementRef, Inject, forwardRef, isDevMode } from "@angular/core";
+import {
+    Component, OnInit, DoCheck, OnDestroy, ViewChild, ViewChildren,
+    QueryList, AfterViewChecked, ElementRef, Inject, forwardRef, isDevMode
+} from "@angular/core";
 import { ActivatedRoute, Router } from "@angular/router";
 
 import {
@@ -58,6 +60,7 @@ import { MatomoTracker } from "ngx-matomo";
 import { sprintf } from "sprintf-js";
 
 import * as XLSX from "xlsx";
+import { ServiceFactory } from "app/services/service-factory";
 
 @Component({
     selector: "hydrocalc",
@@ -307,7 +310,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     }
 
     public get quicknavItems() {
-        const elts = [ "input", "results" ];
+        const elts = ["input", "results"];
         if (this.isWide && this.hasResults) {
             elts.push("charts");
         }
@@ -328,7 +331,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
      * the UI validity state)
      */
     ngDoCheck() {
-        this.isCalculateDisabled = ! this._isUIValid;
+        this.isCalculateDisabled = !this._isUIValid;
     }
 
     ngOnDestroy() {
@@ -362,7 +365,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     }
 
     public ngAfterViewChecked() {
-        if (! this.firstViewChecked) {
+        if (!this.firstViewChecked) {
             this.firstViewChecked = true;
             this.afterFirstViewChecked();
         }
@@ -393,7 +396,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     }
 
     public doCompute() {
-        if (! isDevMode()) {
+        if (!isDevMode()) {
             this.matomoTracker.trackEvent("userAction", "triggerCalculation", CalculatorType[this._formulaire.currentNub.calcType]);
         }
         this._formulaire.resetResults([]);
@@ -444,7 +447,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         try {
             this.appComponent.scrollToQuicknav("results");
         } catch (e) {
-            const element = document.getElementById ("fake-results-anchor");
+            const element = document.getElementById("fake-results-anchor");
             if (element) {
                 element.scrollIntoView();
             }
@@ -471,7 +474,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
      */
     private updateUIValidity() {
         this._isUIValid = false;
-        if (! this._formulaire.calculateDisabled) {
+        if (!this._formulaire.calculateDisabled) {
             // all fieldsets must be valid
             this._isUIValid = true;
             if (this._fieldsetComponents !== undefined) {
@@ -520,7 +523,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
             }
             if (this._formulaire.currentNub.calcType === CalculatorType.PreBarrage) {
                 const form: FormulairePrebarrage = this._formulaire as FormulairePrebarrage;
-                this._isUIValid = this._isUIValid && form.currentNub.check();
+                this._isUIValid = this._isUIValid && form.checkParameters().length === 0;
             }
         }
     }
@@ -694,14 +697,14 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     private allParamsAreFixed(except: string[] = []) {
         let ret = true;
         for (const p of this._formulaire.currentNub.parameterIterator) {
-            if (! except.includes(p.symbol)) {
+            if (!except.includes(p.symbol)) {
                 if (p.valueMode === ParamValueMode.LINK) {
-                    ret = ret && (! p.hasMultipleValues);
+                    ret = ret && (!p.hasMultipleValues);
                 } else {
                     // avoid calling hasMultipleValues here, because changing parameter mode in GUI
                     // switches valueMode before setting min/max/step or valuesList, and iterator
                     // checker fails to count values that do not exist yet
-                    ret = ret && (! [ ParamValueMode.LISTE, ParamValueMode.MINMAX ].includes(p.valueMode));
+                    ret = ret && (![ParamValueMode.LISTE, ParamValueMode.MINMAX].includes(p.valueMode));
                 }
             }
         }
@@ -716,10 +719,10 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     }
 
     public get uitextGeneratePabTitle() {
-        if (! this.hasResults) {
+        if (!this.hasResults) {
             return this.intlService.localizeText("INFO_CALCULATE_FIRST");
         }
-        if (! this.allParamsAreFixed()) {
+        if (!this.allParamsAreFixed()) {
             return this.intlService.localizeText("INFO_PARAMETRES_FIXES");
         }
         return "";
@@ -731,41 +734,48 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     public generatePAB() {
         // création du dialogue de génération d'une passe à bassin
         const cloisons = (this._formulaire.currentNub as Cloisons);
+        const chute = cloisons.prms.DH.V;
+        const debit = cloisons.prms.Q.V;
+        const zAmont = cloisons.prms.Z1.V;
+        const nbBassins = ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit ? undefined : 6;
         const dialogRef = this.generatePABDialog.open(
-          DialogGeneratePABComponent,
-          {
-            data: {
-                chute: cloisons.prms.DH.V,
-                debit: cloisons.prms.Q.V,
-                coteAmont: cloisons.prms.Z1.V
-            },
-            disableClose: false
-          }
+            DialogGeneratePABComponent,
+            {
+                data: {
+                    debit: debit,
+                    coteAmont: zAmont,
+                    nbBassins: nbBassins
+                },
+                disableClose: false
+            }
         );
         dialogRef.afterClosed().subscribe(async result => {
-          if (result) {
-            if (result.generate) {
-                const f: FormulaireDefinition = await this.formulaireService.createFormulaire(CalculatorType.Pab);
-                const pab = (f.currentNub as Pab);
-                const params = pab.prms;
-                // paramètres hydrauliques
-                params.Q.singleValue = result.debit;
-                params.Z1.singleValue = result.coteAmont;
-                params.Z2.singleValue = result.coteAval;
-                // création des bassins
-                pab.deleteChild(0);
-                pab.addCloisonsFromModel(this._formulaire.currentNub as Cloisons, result.nbBassins);
-                // go to new PAB
-                this.router.navigate(["/calculator", f.uid]);
+            if (result) {
+                if (result.generate) {
+                    const f: FormulaireDefinition = await this.formulaireService.createFormulaire(CalculatorType.Pab);
+                    const pab = (f.currentNub as Pab);
+                    const params = pab.prms;
+                    // calculate downstream elevation
+                    const chute = cloisons.prms.DH.V;
+                    const coteAval = result.coteAmont - (chute * result.nbBassins);
+                    // paramètres hydrauliques
+                    params.Q.singleValue = result.debit;
+                    params.Z1.singleValue = result.coteAmont;
+                    params.Z2.singleValue = coteAval;
+                    // création des bassins
+                    pab.deleteChild(0);
+                    pab.addCloisonsFromModel(this._formulaire.currentNub as Cloisons, result.nbBassins);
+                    // go to new PAB
+                    this.router.navigate(["/calculator", f.uid]);
+                }
             }
-          }
         });
     }
 
     public get generateSPAmontEnabled(): boolean {
         const bief = (this._formulaire.currentNub as Bief);
         if (bief.prms.Z1 === bief.calculatedParam) {
-            return this.hasResults && ! bief.result.hasErrorMessages();
+            return this.hasResults && !bief.result.hasErrorMessages();
         } else {
             // check that linked values are available, if any
             return (
@@ -778,7 +788,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     public get generateSPAvalEnabled(): boolean {
         const bief = (this._formulaire.currentNub as Bief);
         if (bief.prms.Z2 === bief.calculatedParam) {
-            return this.hasResults && ! bief.result.hasErrorMessages();
+            return this.hasResults && !bief.result.hasErrorMessages();
         } else {
             // check that linked values are available, if any
             return (
@@ -789,7 +799,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     }
 
     public get uitextGenerateSPAmontTitle(): string {
-        if (! this.generateSPAmontEnabled) {
+        if (!this.generateSPAmontEnabled) {
             return this.intlService.localizeText("INFO_CALCULATE_FIRST");
         } else {
             return "";
@@ -797,7 +807,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     }
 
     public get uitextGenerateSPAvalTitle(): string {
-        if (! this.generateSPAvalEnabled) {
+        if (!this.generateSPAvalEnabled) {
             return this.intlService.localizeText("INFO_CALCULATE_FIRST");
         } else {
             return "";
@@ -835,7 +845,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         const bief = (this._formulaire.currentNub as Bief);
         return generateValuesCombination(
             bief,
-            [ Z, ZF ],
+            [Z, ZF],
             (nub: Nub, values: { [key: string]: number }): number => {
                 return round(values[Z.symbol] - values[ZF.symbol], 3);
             }
@@ -849,7 +859,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         const bief = (this._formulaire.currentNub as Bief);
         return generateValuesCombination(
             bief,
-            [ bief.prms.ZF1, bief.prms.ZF2, bief.prms.Long ],
+            [bief.prms.ZF1, bief.prms.ZF2, bief.prms.Long],
             (nub: Nub, values: { [key: string]: number }): number => {
                 return round((values["ZF1"] - values["ZF2"]) / values["Long"], 5);
             }
@@ -873,7 +883,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
                 // "If" is hidden in Bief, for ex.
                 bief.section.getParameter(p.symbol).visible
                 // do not link Y and If
-                && ! [ "If", "Y" ].includes(p.symbol)
+                && !["If", "Y"].includes(p.symbol)
             ) {
                 const bP = bief.section.getParameter(p.symbol);
                 if (bP.valueMode === ParamValueMode.LINK) {
@@ -895,11 +905,11 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     }
 
     public get generateRuSpEnabled(): boolean {
-        return this.hasResults && ! this._formulaire.currentNub.result.hasErrorMessages();
+        return this.hasResults && !this._formulaire.currentNub.result.hasErrorMessages();
     }
 
     public get uitextGenerateRuSpTitle(): string {
-        if (! this.generateRuSpEnabled) {
+        if (!this.generateRuSpEnabled) {
             return this.intlService.localizeText("INFO_CALCULATE_FIRST");
         } else {
             return "";
@@ -935,7 +945,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         const parCalage = (this._formulaire.currentNub as Par);
         return (
             this.hasResults
-            && ! parCalage.result.hasErrorMessages()
+            && !parCalage.result.hasErrorMessages()
             && parCalage.prms.Z1.isDefined
             && parCalage.prms.Z2.isDefined
         );
@@ -943,12 +953,12 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
 
     public get uitextGenerateParSimulationTitle(): string {
         const parCalage = (this._formulaire.currentNub as Par);
-        if (! this.hasResults || parCalage.result.hasErrorMessages()) {
+        if (!this.hasResults || parCalage.result.hasErrorMessages()) {
             return this.intlService.localizeText("INFO_CALCULATE_FIRST");
         }
         if (
-            ! parCalage.prms.Z1.isDefined
-            ||  ! parCalage.prms.Z2.isDefined
+            !parCalage.prms.Z1.isDefined
+            || !parCalage.prms.Z2.isDefined
         ) {
             return this.intlService.localizeText("INFO_Z1_Z2_MUST_BE_DEFINED");
         }
@@ -1025,7 +1035,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
 
     public get exportAllPbResultsEnabled(): boolean {
         const pb = (this._formulaire as FormulairePrebarrage).currentNub as PreBarrage;
-        return (pb.result !== undefined && ! pb.result.hasOnlyErrors);
+        return (pb.result !== undefined && !pb.result.hasOnlyErrors);
     }
 
     /**
@@ -1163,11 +1173,11 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
     protected async doGenerateParSimWithValues(v: any) {
         const parCalage = (this._formulaire.currentNub as Par);
         const psim = new ParSimulationParams(
-            round(v.Q, 3),      round(v.Z1, 3),     round(v.Z2, 3),
-            round(v.S, 3),      round(v.P, 3),      round(v.Nb, 3),
-            round(v.ZR1, 3),    round(v.ZD1, 3),    round(v.ZR2, 3),
-            round(v.ZD2, 3),    round(v.L, 3),      round(v.a, 3),
-            round(v.N, 3),      round(v.M, 3)
+            round(v.Q, 3), round(v.Z1, 3), round(v.Z2, 3),
+            round(v.S, 3), round(v.P, 3), round(v.Nb, 3),
+            round(v.ZR1, 3), round(v.ZD1, 3), round(v.ZR2, 3),
+            round(v.ZD2, 3), round(v.L, 3), round(v.a, 3),
+            round(v.N, 3), round(v.M, 3)
         );
         const parSimulation = new ParSimulation(psim);
         parSimulation.parType = parCalage.parType;
@@ -1188,7 +1198,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         const dialogRef = this.loadPredefinedEspeceDialog.open(
             DialogLoadPredefinedEspeceComponent,
             {
-                data: { },
+                data: {},
                 disableClose: false
             }
         );
@@ -1211,7 +1221,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
             DialogConfirmCloseCalcComponent,
             {
                 data: {
-                  uid: this._formulaire.currentNub.uid
+                    uid: this._formulaire.currentNub.uid
                 },
                 disableClose: true
             }
diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts
index 3d9472e3b957f89f234c0c0cf8f9a49a02a28c50..3dc390d50f261a0dab352154f62a34000e46b885 100644
--- a/src/app/components/generic-input/generic-input.component.ts
+++ b/src/app/components/generic-input/generic-input.component.ts
@@ -161,7 +161,7 @@ export abstract class GenericInputComponentDirective implements OnChanges {
         }
     }
 
-    private validateModel() {
+    private validateModel(): boolean {
         let isValid: boolean;
         // specific case for "allowEmpty" params
         if (this.model instanceof NgParameter && this.uiValue === "" && this.model.allowEmpty) {
@@ -174,6 +174,8 @@ export abstract class GenericInputComponentDirective implements OnChanges {
         }
         this.detectChanges();
         this.setModelValid(isValid);
+
+        return isValid;
     }
 
     public validate() {
@@ -211,11 +213,11 @@ export abstract class GenericInputComponentDirective implements OnChanges {
         });
     }
 
-    protected setAndValidateModel(sender: any, v: any) {
+    protected setAndValidateModel(sender: any, v: any): boolean {
         this.setModelValue(sender, v);
         this.emitModelChanged();
 
-        this.validateModel();
+        return this.validateModel();
     }
 
     public set model(v: any) {
diff --git a/src/app/components/ngparam-input/ngparam-input.component.ts b/src/app/components/ngparam-input/ngparam-input.component.ts
index 81db20827dce5fe36d9a3f1aab664f103cd9deaf..2449f691efa1e7b2a0a98f4ab34b284107a801f5 100644
--- a/src/app/components/ngparam-input/ngparam-input.component.ts
+++ b/src/app/components/ngparam-input/ngparam-input.component.ts
@@ -1,6 +1,6 @@
 // cf. https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html
 
-import { Component, ChangeDetectorRef, OnDestroy } from "@angular/core";
+import { Component, ChangeDetectorRef, OnDestroy, Input, ElementRef } from "@angular/core";
 
 import { Message, Observer } from "jalhyd";
 
@@ -17,6 +17,15 @@ import { ApplicationSetupService } from "../../services/app-setup.service";
     ]
 })
 export class NgParamInputComponent extends GenericInputComponentDirective implements Observer, OnDestroy {
+
+    @Input()
+    public captureTabEvents: boolean;
+
+    /**
+     * true : edit parameter initial value (in CALC mode) instead of single value
+     */
+    public editInitValue: boolean;
+
     /**
      * paramètre géré
      */
@@ -33,9 +42,12 @@ export class NgParamInputComponent extends GenericInputComponentDirective implem
     constructor(
         intlService: I18nService,
         appSetupService: ApplicationSetupService,
-        cdRef: ChangeDetectorRef
+        cdRef: ChangeDetectorRef,
+        private element: ElementRef
     ) {
         super(cdRef, intlService, appSetupService);
+        this.captureTabEvents = true;
+        this.editInitValue = false;
     }
 
     /**
@@ -52,7 +64,12 @@ export class NgParamInputComponent extends GenericInputComponentDirective implem
      */
     protected afterSetModel() {
         if (this._paramDef) {
-            this._tmp = this._paramDef.getValue();
+            if (this.editInitValue) {
+                this._tmp = this._paramDef.paramDefinition.initValue;
+            }
+            else {
+                this._tmp = this._paramDef.getValue();
+            }
             this._paramDef.addObserver(this);
         }
     }
@@ -64,7 +81,11 @@ export class NgParamInputComponent extends GenericInputComponentDirective implem
     protected setModelValue(sender: any, v: any) {
         this._tmp = v;
         try {
-            this._paramDef.setValue(sender, v);
+            if (this.editInitValue) {
+                this._paramDef.setInitValue(sender, v);
+            } else {
+                this._paramDef.setValue(sender, v);
+            }
         } catch (e) {
             // géré par validateModelValue()
         }
@@ -95,6 +116,7 @@ export class NgParamInputComponent extends GenericInputComponentDirective implem
     public update(sender: any, data: any): void {
         switch (data["action"]) {
             case "ngparamAfterValue":
+            case "ngparamAfterInitValue":
                 // on ne fait rien au cas où la modif vient de l'interface (on ne remet pas à jour _uiValue ce qui permet
                 // de garder par ex le '.' si on supprime le '2' de '1.2')
                 if (sender !== this) {
@@ -114,6 +136,17 @@ export class NgParamInputComponent extends GenericInputComponentDirective implem
         }
     }
 
+    /**
+     * Renvoie l'événement au composant du dessus
+     */
+    public onTabPressed(event, shift: boolean) {
+        this.tabPressed.emit({ originalEvent: event, shift: shift });
+        // stop event propagation ?
+        if (this.captureTabEvents) {
+            return false;
+        } // else let it bubble !
+    }
+
     public ngOnDestroy() {
         this._paramDef.removeObserver(this);
     }
diff --git a/src/app/components/pab-profile-chart/pab-profile-chart.component.ts b/src/app/components/pab-profile-chart/pab-profile-chart.component.ts
index 5d9220b27bded151a062f9bdd91f1a6bfb813e72..449c050ffc94f34685a9aa280756ced6b9b051fc 100644
--- a/src/app/components/pab-profile-chart/pab-profile-chart.component.ts
+++ b/src/app/components/pab-profile-chart/pab-profile-chart.component.ts
@@ -12,6 +12,7 @@ import { AppComponent } from "../../app.component";
 import { CloisonAval, Cloisons, LoiDebit } from "jalhyd";
 
 import { sprintf } from "sprintf-js";
+import { CalculatorResults } from 'app/results/calculator-results';
 
 @Component({
     selector: "pab-profile-chart",
@@ -453,13 +454,14 @@ export class PabProfileChartComponent extends ResultsComponentDirective implemen
      * @param n index of the variating parameter(s) iteration
      */
     private getLegendForSeries(n: number): string {
-        let i = 0;
+        /* let i = 0;
         return this.varValues.map((vv) => {
             const vp = this._results.variatedParameters[i];
             i++;
             let value = "0";
             value = vv[n];
-            return `${vp.param.symbol} = ${value}`;
-        }).join(", ");
+            return `${vp.symbol} = ${value}`;
+        }).join(", "); */
+        return CalculatorResults.variatingModalityLabel(this.varValues, this._results, n);
     }
 }
diff --git a/src/app/components/pab-table/pab-table.component.html b/src/app/components/pab-table/pab-table.component.html
index 96efe09a17616653a2c7ab330be912aa9af2cc1a..293bd1b077c9dde9b999c953e080508a8b516288 100644
--- a/src/app/components/pab-table/pab-table.component.html
+++ b/src/app/components/pab-table/pab-table.component.html
@@ -96,10 +96,19 @@
                         </mat-option>
                     </mat-select>
 
+                    <div *ngIf="isQA(cell)" class="qaFieldLineContainer">
+                        <div class="qaLabel">
+                            {{ cell.title }}
+                        </div>
+                        <param-field-line class="qaFieldLine" [param]="cell.model" (radio)="inputValueChanged($event, cell)"
+                            (input)="inputValueChanged($event, cell)" [captureTabEvents]="false">
+                        </param-field-line>
+                    </div>
+
                     <span *ngIf="! hasModel(cell)">{{ cellValue(cell) }}</span>
                 </td>
             </tr>
         </ng-template>
     </p-table>
 
-</mat-card-content>
\ No newline at end of file
+</mat-card-content>
diff --git a/src/app/components/pab-table/pab-table.component.scss b/src/app/components/pab-table/pab-table.component.scss
index abd5be33c92a9805a01f4605e3c0621dd8b48478..775e64397f7542fed4b014e0fd908095365b6cdb 100644
--- a/src/app/components/pab-table/pab-table.component.scss
+++ b/src/app/components/pab-table/pab-table.component.scss
@@ -36,10 +36,12 @@ mat-card-content {
     }
     .hyd-window-btns {
         text-align: right;
+        align-items: flex-end;
 
         #add-many-children {
             width: 3em;
             vertical-align: middle;
+            align-items: flex-start;
         }
 
         button.mat-icon-button {
@@ -47,3 +49,17 @@ mat-card-content {
         }
     }
 }
+
+.qaFieldLineContainer {
+    padding: 0 10px;
+    display: flex;
+    flex-wrap: wrap;
+    flex-direction: row;
+    justify-content:space-between;
+}
+
+.qaLabel {
+    margin-top: 13px;
+    font-weight: bold;
+}
+
diff --git a/src/app/components/pab-table/pab-table.component.ts b/src/app/components/pab-table/pab-table.component.ts
index b3c8934ecfc243a544c9a43a4aad2c119288d9b6..486789521a32d7efb0cf3e782f7f1be51bbf30ac 100644
--- a/src/app/components/pab-table/pab-table.component.ts
+++ b/src/app/components/pab-table/pab-table.component.ts
@@ -1,6 +1,6 @@
-import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit } from "@angular/core";
+import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit, AfterViewChecked } from "@angular/core";
 
-import { LoiDebit } from "jalhyd";
+import { LoiDebit, ParamValueMode } from "jalhyd";
 
 import { MatDialog } from "@angular/material/dialog";
 
@@ -27,6 +27,7 @@ import { NotificationsService } from "../../services/notifications.service";
 import { PabTable } from "../../formulaire/elements/pab-table";
 import { DialogEditPabComponent } from "../dialog-edit-pab/dialog-edit-pab.component";
 import { AppComponent } from "../../app.component";
+import { NgParameter, ParamRadioConfig } from "../../formulaire/elements/ngparam";
 
 /**
  * The big editable data grid for calculator type "Pab" (component)
@@ -38,7 +39,7 @@ import { AppComponent } from "../../app.component";
         "./pab-table.component.scss"
     ]
 })
-export class PabTableComponent implements AfterViewInit, OnInit {
+export class PabTableComponent implements AfterViewInit, AfterViewChecked, OnInit {
 
     @Input()
     private pabTable: PabTable;
@@ -85,6 +86,12 @@ export class PabTableComponent implements AfterViewInit, OnInit {
         this.selectedItems = [];
     }
 
+    /** update vary value from pab fish ladder and unable compute Button */
+  ngAfterViewChecked(): void {
+    this.updateValidity();
+  }
+
+
     public get title(): string {
         return this.i18nService.localizeText("INFO_PAB_TABLE");
     }
@@ -101,7 +108,7 @@ export class PabTableComponent implements AfterViewInit, OnInit {
 
     /** returns true if the cell is an editable number */
     public isNumberInput(cell: any): boolean {
-        return this.hasModel(cell) && ! this.isSelect(cell);
+        return this.hasModel(cell) && ! this.isSelect(cell) && ! this.isQA(cell);
     }
 
     /** returns true if the cell is a select box */
@@ -109,6 +116,11 @@ export class PabTableComponent implements AfterViewInit, OnInit {
         return this.hasModel(cell) && (cell.options !== undefined);
     }
 
+    /** returns true if the cell is a QA (Attraction flow) editor */
+    public isQA(cell: any): boolean {
+        return this.hasModel(cell) && cell.qa;
+    }
+
     /** value to display in a cell, if it is not editable */
     public cellValue(cell: any) {
         if (cell === undefined) {
@@ -166,8 +178,12 @@ export class PabTableComponent implements AfterViewInit, OnInit {
      */
     public isInvalid(cell: any): boolean {
         let valid = true;
-        if (this.hasModel(cell) && cell.model instanceof ParamDefinition) {
-            valid = valid && cell.model.isValid;
+        if (this.hasModel(cell)) {
+            if (cell.model instanceof ParamDefinition) {
+                valid = valid && cell.model.isValid;
+            } else if (cell.model instanceof NgParameter) { // for QA (currently has domain ANY but that might change)
+                valid = valid && cell.model.paramDefinition.isValid;
+            }
         }
         if (cell.uiValidity !== undefined) {
             valid = valid && cell.uiValidity;
@@ -443,7 +459,7 @@ export class PabTableComponent implements AfterViewInit, OnInit {
         const nDigits = this.appSetupService.displayPrecision;
         for (const c of this.model.children) {
             for (const p of c.parameterIterator) {
-                if (p.visible) {
+                if (p.visible && p.symbol !== "QA") { // QA might vary !
                     p.singleValue = round(p.singleValue, nDigits);
                 }
             }
@@ -461,7 +477,7 @@ export class PabTableComponent implements AfterViewInit, OnInit {
         bs = bs.concat(this.model.downWall);
         this.headers.push({
             title: this.i18nService.localizeText("INFO_PAB_BASSIN"),
-            colspan: 6,
+            colspan: 5,
             selectable: bs
         });
         // 1 header for each device of the wall having the most devices (including downwall)
@@ -476,7 +492,7 @@ export class PabTableComponent implements AfterViewInit, OnInit {
 
         // A. build columns set
         this.cols = [];
-        // 6 cols for basin
+        // 5 cols for basin
         this.cols.push({
             title: this.i18nService.localizeText("INFO_PAB_NUM_BASSIN"),
             selectable: bs
@@ -489,10 +505,6 @@ export class PabTableComponent implements AfterViewInit, OnInit {
             title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "BB"),
             selectable: bs
         });
-        this.cols.push({
-            title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "QA"),
-            selectable: bs
-        });
         this.cols.push({
             title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "ZRMB"),
             selectable: bs
@@ -523,16 +535,16 @@ export class PabTableComponent implements AfterViewInit, OnInit {
 
         // B. Build rows set
         this.rows = [];
+        // admissible LoiDebit (same for all cloisons)
+        const loisCloisons = this.model.children[0].getLoisAdmissiblesArray().map(l => {
+            return {
+                label: this.localizeLoiDebit(l),
+                value: l
+            };
+        });
         // B.1 many rows for each wall
         let childIndex = 0;
         for (const cloison of this.model.children) {
-            // admissible LoiDebit
-            const loisCloisons = cloison.getLoisAdmissiblesArray().map(l => { // @TODO move up ? (same for all cloisons)
-                return {
-                    label: this.localizeLoiDebit(l),
-                    value: l
-                };
-            });
             // as much rows as the greatest number of parameters among its devices
             const maxNbParams = this.findMaxNumberOfDeviceParameters(cloison);
             for (let i = 0; i < maxNbParams; i++) {
@@ -547,10 +559,10 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                         class: "basin_number",
                         selectable: cloison
                     });
-                    // 4 empty cells
+                    // 3 empty cells
                     deviceParamRow.cells.push({
-                        colspan: 4,
-                        rowspan: maxNbParams ,
+                        colspan: 3,
+                        rowspan: maxNbParams - 1,
                         selectable: cloison
                     });
                     // ZRAM
@@ -559,7 +571,22 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                         title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "ZRAM")
                     });
                 }
-                // 1 empty cell
+                // basin cells on the last but 1 row
+                if (i === maxNbParams - 1) {
+                    deviceParamRow.cells.push({
+                        model: cloison.prms.LB,
+                        title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "LB")
+                    });
+                    deviceParamRow.cells.push({
+                        model: cloison.prms.BB,
+                        title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "BB")
+                    });
+                    deviceParamRow.cells.push({
+                        model: cloison.prms.ZRMB,
+                        title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "ZRMB")
+                    });
+                }
+                // 1 empty cell below ZRAM
                 if (i === 1) {
                     deviceParamRow.cells.push({
                         rowspan: maxNbParams,
@@ -579,7 +606,7 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                             selectable: ouvrage
                         });
                     }
-                    // fill space
+                    // fill space below device type selector
                     if (i === 1) {
                         deviceParamRow.cells.push({
                             rowspan: (maxNbParams - 1),
@@ -626,37 +653,28 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                 // done !
                 this.rows.push(deviceParamRow);
             }
-            // 1 row for the basin after the wall
-            const basinRow: { selectable: any, cells: any[] } = {
-                selectable: cloison,
+            // 1 row for QA editor
+            const qaParam = new NgParameter(cloison.prms.QA, this.pabTable.form);
+            qaParam.radioConfig = ParamRadioConfig.VAR;
+            const qaRow: { selectable: any, cells: any[] } = {
+                selectable: undefined,
                 cells: [
-                    // no cell for basin number (defined by rowspan-n cell above)
-                    {
-                        model: cloison.prms.LB,
-                        title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "LB")
-                    },
                     {
-                        model: cloison.prms.BB,
-                        title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "BB")
-                    },
-                    {
-                        model: cloison.prms.QA,
+                        model: qaParam,
+                        colspan: 3,
+                        qa: true,
                         title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "QA")
-                    },
-                    {
-                        model: cloison.prms.ZRMB,
-                        title: this.formService.expandVariableNameAndUnit(CalculatorType.Pab, "ZRMB")
                     }
                 ]
             };
-            // fill horizontal space
-            for (let i = 0; i < maxNbDevices; i++) {
-                basinRow.cells.push({
-                    colspan: 3
-                });
-            }
+            // as many pairs of columns as the maximum number of devices
+            qaRow.cells.push({
+                colspan: maxNbDevices * 3,
+                // selectable: cloison @TODO oui ou non ?
+            });
             // done !
-            this.rows.push(basinRow);
+            this.rows.push(qaRow);
+
             childIndex ++;
         }
 
@@ -681,9 +699,9 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                     class: "basin_number",
                     selectable: this.model.downWall
                 });
-                // 4 empty cells
+                // 3 empty cells
                 deviceParamRowDW.cells.push({
-                    colspan: 4,
+                    colspan: 3,
                     rowspan: maxNbParamsDW ,
                     selectable: this.model.downWall
                 });
@@ -1033,8 +1051,10 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                 );
                 // copy parameter values
                 for (const p of si.prms) {
-                    newChild.getParameter(p.symbol).singleValue = p.singleValue;
+                    if (p.visible) {
+                        newChild.getParameter(p.symbol).loadObjectRepresentation(p.objectRepresentation());
                 }
+              }
                 // copy children
                 if (si instanceof ParallelStructure) {
                     for (const c of si.getChildren()) {
@@ -1218,7 +1238,8 @@ export class PabTableComponent implements AfterViewInit, OnInit {
             for (const av of availableVariables) {
                 for (const c of this.selectedItems) {
                     for (const p of c.parameterIterator) {
-                        if (p.visible && p.symbol === av.value) {
+                        // @TODO what todo when p varies (QA only) ?
+                        if (p.visible && p.symbol === av.value && ! p.hasMultipleValues) {
                             av.occurrences ++;
                             if (av.first === undefined) {
                                 av.first = p.singleValue;
@@ -1295,6 +1316,10 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                         case "set-value":
                             for (const s of this.selectedItems) {
                                 for (const p of s.parameterIterator) { // deep
+                                    // force single mode (QA only)
+                                    if (p.hasMultipleValues) {
+                                        p.valueMode = ParamValueMode.SINGLE;
+                                    }
                                     if (p.symbol === result.variable) {
                                         p.singleValue = result.value;
                                     }
@@ -1305,6 +1330,10 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                         case "delta":
                             for (const s of this.selectedItems) {
                                 for (const p of s.parameterIterator) { // deep
+                                    // force single mode (QA only)
+                                    if (p.hasMultipleValues) {
+                                        p.valueMode = ParamValueMode.SINGLE;
+                                    }
                                     if (p.symbol === result.variable) {
                                         p.singleValue += result.delta;
                                     }
@@ -1381,6 +1410,10 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                                     // for ZRMB, interpolatedValues length is shorter by 1 element
                                     if (interpolatedValues[idx] !== undefined) {
                                         for (const p of s.parameterIterator) { // deep
+                                            // force single mode (QA only)
+                                            if (p.hasMultipleValues) {
+                                                p.valueMode = ParamValueMode.SINGLE;
+                                            }
                                             if (p.symbol === result.variable) {
                                                 p.singleValue = interpolatedValues[idx];
                                                 idx ++;
@@ -1433,7 +1466,11 @@ export class PabTableComponent implements AfterViewInit, OnInit {
                 const inputs = td.getElementsByTagName("input");
                 if (inputs.length > 0) {
                     const input = inputs[0];
-                    td.innerHTML = input.value;
+                    if (input.id.split("_")[1] === "QA") {
+                        td.innerHTML = NgParameter.preview(this.model.children[input.id.split("_")[0]].prms.QA);
+                    } else {
+                        td.innerHTML = input.value;
+                    }
                 }
             }
         }
diff --git a/src/app/components/param-computed/param-computed.component.ts b/src/app/components/param-computed/param-computed.component.ts
index e1f9010718fe65e683d37fd444984d09757b5795..5525dcf7ab3cc4aa308f25491f006f7e755e4b87 100644
--- a/src/app/components/param-computed/param-computed.component.ts
+++ b/src/app/components/param-computed/param-computed.component.ts
@@ -20,6 +20,11 @@ export class ParamComputedComponent {
     @Input()
     public title: string;
 
+    /**
+     * initial value before opening dialog
+     */
+    private initValueBackup;
+
     /**
      * id de l'input, utilisé notamment pour les tests
      */
@@ -47,6 +52,7 @@ export class ParamComputedComponent {
     }
 
     public openDialog() {
+        this.initValueBackup = this.param.paramDefinition.initValue;
         // modification de la valeur initiale, sans avoir à remettre le mode de paramètre sur "fixé"
         const dialogRef = this.editInitialValueDialog.open(
             DialogEditParamComputedComponent,
@@ -59,9 +65,9 @@ export class ParamComputedComponent {
             }
         );
         dialogRef.afterClosed().subscribe(result => {
-            if (result && result.value !== undefined) {
+            if (result.cancel) {
                 // use setInitValue() and not setValue() to prevent value mode from going back to SINGLE
-                this.param.paramDefinition.setInitValue(result.value);
+                this.param.paramDefinition.setInitValue(this.initValueBackup);
             }
         });
     }
diff --git a/src/app/components/param-field-line/param-field-line.component.html b/src/app/components/param-field-line/param-field-line.component.html
index 1143ac972b73bc22e3b3d8dad26e806443f92ff9..daad0ba4b73516eb51066cd77ed629066571ffe4 100644
--- a/src/app/components/param-field-line/param-field-line.component.html
+++ b/src/app/components/param-field-line/param-field-line.component.html
@@ -4,7 +4,7 @@
     <div fxFlex="1 0 120px">
         <!-- composant pour gérer le cas général (valeur numérique à saisir) -->
         <ngparam-input [title]="param.title" [hidden]="! isRadioFixChecked" (change)="onInputChange($event)"
-            (tabPressed)="onTabPressed($event)">
+            (tabPressed)="onTabPressed($event)" [captureTabEvents]="captureTabEvents">
         </ngparam-input>
 
         <!-- composant pour gérer le cas "paramètre calculé" -->
@@ -52,4 +52,4 @@
         </mat-button-toggle-group>
     </div>
 
-</div>
\ No newline at end of file
+</div>
diff --git a/src/app/components/param-field-line/param-field-line.component.scss b/src/app/components/param-field-line/param-field-line.component.scss
index 16c2b915393123bfb99d0342c04f8a4b15e3648d..4723ca17013a43f00f973db0024ea94a7acb8a56 100644
--- a/src/app/components/param-field-line/param-field-line.component.scss
+++ b/src/app/components/param-field-line/param-field-line.component.scss
@@ -6,3 +6,4 @@ mat-button-toggle-group {
     margin-top: 4px;
     box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
 }
+
diff --git a/src/app/components/param-field-line/param-field-line.component.ts b/src/app/components/param-field-line/param-field-line.component.ts
index 22b7a92ad09a49c78ac0f54cb6ee01e44bc06fbc..1cb5750c7f0e1f33fa6b52b29a0a932d30f5d641 100644
--- a/src/app/components/param-field-line/param-field-line.component.ts
+++ b/src/app/components/param-field-line/param-field-line.component.ts
@@ -4,7 +4,7 @@ import { I18nService } from "../../services/internationalisation.service";
 import { NgParameter, ParamRadioConfig } from "../../formulaire/elements/ngparam";
 import { NgParamInputComponent } from "../ngparam-input/ngparam-input.component";
 import { ServiceFactory } from "../../services/service-factory";
-import { ParamValueMode, ParallelStructure, ParamCalculability } from "jalhyd";
+import { ParamValueMode, ParallelStructure, ParamCalculability, Pab } from "jalhyd";
 import { FormulaireService } from "../../services/formulaire.service";
 import { ParamLinkComponent } from "../param-link/param-link.component";
 import { ParamValuesComponent } from "../param-values/param-values.component";
@@ -26,6 +26,7 @@ export class ParamFieldLineComponent implements OnChanges {
         this._formService = ServiceFactory.formulaireService;
         this.valid = new EventEmitter();
         this.inputChange = new EventEmitter();
+        this.captureTabEvents = true;
     }
 
     public get uitextParamFixe() {
@@ -86,6 +87,9 @@ export class ParamFieldLineComponent implements OnChanges {
     @Input()
     public param: NgParameter;
 
+    @Input()
+    public captureTabEvents: boolean;
+
     @ViewChild(NgParamInputComponent, { static: true })
     private _ngParamInputComponent: NgParamInputComponent;
 
@@ -115,6 +119,12 @@ export class ParamFieldLineComponent implements OnChanges {
 
     private _formService: FormulaireService;
 
+    /**
+     * sauvegarde de certaines propriétés du paramètre avant clic sur le radio "varier", utilisé pour
+     * restaurer le paramètre en cas de cancel du dialogue de modification des valeurs en mode variable
+     */
+    private paramBackup: any;
+
     /*
      * gestion des événements clic sur les radios :
      * envoi d'un message au composant parent
@@ -173,7 +183,7 @@ export class ParamFieldLineComponent implements OnChanges {
                 return this._formService.getLinkableValues(this.param).length > 0;
             }
 
-            // ou un seul module de calcul "ouvrages parallèles"
+            // ou un seul module de calcul "ouvrages parallèles" avec au moins 2 ouvrages
             if (this._formService.formulaires[0].currentNub instanceof ParallelStructure) {
                 const ps: ParallelStructure = this._formService.formulaires[0].currentNub;
                 if (ps.structures.length > 1) {
@@ -181,6 +191,14 @@ export class ParamFieldLineComponent implements OnChanges {
                 }
             }
 
+            // ou un seul module de calcul "PAB" avec au mois 2 ouvrages
+            if (this._formService.formulaires[0].currentNub instanceof Pab) {
+                const pab: Pab = this._formService.formulaires[0].currentNub;
+                if (pab.children.length > 1) {
+                    return this._formService.getLinkableValues(this.param).length > 0;
+                }
+            }
+
         }
         return false;
     }
@@ -224,16 +242,18 @@ export class ParamFieldLineComponent implements OnChanges {
     }
 
     public onRadioClick(option: string) {
-        const oldValue = this.param.valueMode;
+        const oldValueMode = this.param.valueMode;
         switch (option) {
             case "fix":
                 this.param.valueMode = ParamValueMode.SINGLE;
                 break;
 
             case "var":
+                this.paramBackup = this.param.objectRepresentation();
+
                 // prevent setting LISTE mode back to MINMAX if someone clicks "variable" again
                 // after setting variable mode to LISTE
-                if (oldValue !== ParamValueMode.MINMAX && oldValue !== ParamValueMode.LISTE) {
+                if (oldValueMode !== ParamValueMode.MINMAX && oldValueMode !== ParamValueMode.LISTE) {
                     this.param.valueMode = ParamValueMode.MINMAX; // min/max par défaut
                 }
                 if (this._paramValuesComponent) {
@@ -244,15 +264,6 @@ export class ParamFieldLineComponent implements OnChanges {
 
             case "cal":
                 this.param.setCalculated(); // sets mode to CALCUL and more
-                // did some nasty rascal tick the "create module with empty fields" evil option,
-                // then chose to set a DICHO param to CALC mode without setting a value first ?
-                if (
-                    this.param.paramDefinition.calculability === ParamCalculability.DICHO
-                    && this.param.paramDefinition.singleValue === undefined
-                ) {
-                    // restore dedicated initValue
-                    this.param.paramDefinition.singleValue = this.param.paramDefinition.initValue;
-                }
                 break;
 
             case "link":
@@ -261,7 +272,7 @@ export class ParamFieldLineComponent implements OnChanges {
         }
         this.radio.emit({
             "param": this.param,
-            "oldValueMode": oldValue
+            "oldValueMode": oldValueMode
         });
         // MAJ validité
         this.emitValidity();
@@ -294,6 +305,10 @@ export class ParamFieldLineComponent implements OnChanges {
             case "model":
                 this.inputChange.emit(event);
                 break;
+
+            case "cancelvar": // cancel button clicked in DialogEditParamValuesComponent
+                this.param.loadObjectRepresentation(this.paramBackup);
+                break;
         }
     }
 
diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
index 058ed5a280a4b98a87df80aa0866caeba2799cb5..89255ef13580199816bf9f40f4a85a5ce31ea0e9 100644
--- a/src/app/components/param-link/param-link.component.ts
+++ b/src/app/components/param-link/param-link.component.ts
@@ -1,7 +1,7 @@
 import { Component, Input, Output, EventEmitter, OnChanges, OnDestroy } from "@angular/core";
 
 import { NgParameter } from "../../formulaire/elements/ngparam";
-import { LinkedValue, ParamValueMode, Observer, Structure, acSection, ParamDefinition, ChildNub } from "jalhyd";
+import { LinkedValue, ParamValueMode, Observer, acSection, ParamDefinition, ChildNub, Cloisons, Pab } from "jalhyd";
 import { FormulaireService } from "../../services/formulaire.service";
 import { I18nService } from "../../services/internationalisation.service";
 import { FormulaireDefinition } from "../../formulaire/definition/form-definition";
@@ -169,7 +169,14 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
         }
 
         // 1. Paramètre / résultat d'un Nub enfant au sein d'un Nub parent
-        if (i.nub instanceof ChildNub) {
+        if (
+            (i.nub instanceof ChildNub)
+            || (
+                (i.nub instanceof Cloisons)
+                && i.nub.parent !== undefined
+                && (i.nub.parent instanceof Pab)
+            )
+        ) {
             let pos: number;
             pos = i.nub.findPositionInParent();
             return `${preview} - ` + sprintf(
diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts
index 2a0ce5d3c3c9eec2d2764baed3b54c2f0bbec85c..05738ddc4afd5e89d76122fe8d0e5a7af6b7bbee 100644
--- a/src/app/components/param-values/param-values.component.ts
+++ b/src/app/components/param-values/param-values.component.ts
@@ -55,7 +55,7 @@ export class ParamValuesComponent implements AfterViewInit, Observer {
 
     public openDialog() {
         // modification des valeurs variables
-        this.editValuesDialog.open(
+        const dlgRef = this.editValuesDialog.open(
             DialogEditParamValuesComponent,
             {
                 disableClose: true,
@@ -66,6 +66,13 @@ export class ParamValuesComponent implements AfterViewInit, Observer {
                 panelClass: "dialogMinWidth320px"
             }
         );
+        dlgRef.afterClosed().toPromise().then(result => {
+            if (result.cancelled) {
+                this.change.emit({
+                    action: "cancelvar"
+                });
+            }
+        });
     }
 
     public ngAfterViewInit() {
@@ -95,7 +102,10 @@ export class ParamValuesComponent implements AfterViewInit, Observer {
     public update(sender: any, data: any): void {
         if (sender instanceof DialogEditParamValuesComponent) {
             switch (data.action) {
-                case "ngparamAfterValue":
+                case "ngparamAfterMinValue":
+                case "ngparamAfterMaxValue":
+                case "ngparamAfterStepValue":
+                case "ngparamAfterListValue":
                     // tell the form to clear the results
                     this.emitModelChanged();
                     break;
diff --git a/src/app/components/pb-schema/pb-schema.component.ts b/src/app/components/pb-schema/pb-schema.component.ts
index cdbe3fad0932b9ebd36b8fc3ad8c692c166d987a..7ea4ba81d47b6bbaede7cb478cbf84bef38b4b36 100644
--- a/src/app/components/pb-schema/pb-schema.component.ts
+++ b/src/app/components/pb-schema/pb-schema.component.ts
@@ -5,7 +5,7 @@ import * as screenfull from "screenfull";
 import { Screenfull } from "screenfull"; // @see https://github.com/sindresorhus/screenfull.js/issues/126#issuecomment-488796645
 
 import {
-    PreBarrage, PbBassin, PbBassinParams, PbCloison, Observer, IObservable
+    PreBarrage, PbBassin, PbBassinParams, PbCloison, Observer, IObservable, ParamDefinition, ParamValueMode
  } from "jalhyd";
 
 import * as mermaid from "mermaid";
@@ -20,6 +20,8 @@ import { FormulairePrebarrage } from "../../formulaire/definition/form-prebarrag
 import { AppComponent } from "../../app.component";
 
 import { fv } from "app/util";
+import { FormulaireNode } from "app/formulaire/elements/formulaire-node";
+import { ServiceFactory } from "app/services/service-factory";
 
 /**
  * The interactive schema for calculator type "PreBarrage" (component)
@@ -138,6 +140,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         } catch (e) {
             console.error(e);
         }
+        this.highlightErrorItems(null);
     }
 
     /**
@@ -229,6 +232,8 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         def.push("classDef basin fill:#e0f3fb,stroke:#003A80;"); // irstea-ocean 50 / 500
         def.push("classDef basin::first-line color:green,font-size:0.5em;");
         def.push("classDef node-highlighted fill:#4DBBE9;"); // irstea-ocean (material "accent"), 300
+        def.push("classDef node-error fill:#ec7430;"); // irstea-rouille (material "accent"), 400
+        def.push("classDef node-highlighted-error fill:#d92f03;"); // irstea-rouille (material "accent"), 900
 
         const sortedWalls: PbCloison[] = [];
         for (const c of this.model.children) {
@@ -240,6 +245,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
                 sortedWalls.push(c);
             }
         }
+
         // sort then draw walls
         sortedWalls.sort(this.triCloisonsGaucheDroite);
         for (const c of sortedWalls) {
@@ -308,6 +314,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         } else {
             this._selectedItem = this.model.findChild(item.id);
         }
+        this.highlightErrorItems(item.id);
         // show proper form and hide results
         this.nodeSelected.emit({
             node: this._selectedItem
@@ -488,7 +495,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
 
     /** Adds a new lone basin */
     public onAddBasinClick() {
-        const newBasin = new PbBassin(new PbBassinParams(20, 99));
+        const newBasin = new PbBassin(new PbBassinParams(20, 99, ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit));
         this.model.addChild(newBasin);
         this.clearResults();
         this.refreshWithSelection(newBasin.uid);
@@ -649,6 +656,29 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         });
     }
 
+    private highlightErrorItems(selectedUid: string) {
+        this.nativeElement.querySelectorAll("g.node").forEach(item => {
+            item.classList.remove("node-error");
+            item.classList.remove("node-highlighted-error");
+        });
+        const invalidUids: string[] = this.pbSchema.form.checkParameters();
+        this.nativeElement.querySelectorAll("g.node").forEach(item => {
+            let itemId: string;
+            if ([this.upstreamId, this.downstreamId].includes(item.id)) {
+                itemId = this.model.uid;
+            } else {
+                itemId = item.id
+            }
+            if (invalidUids.includes(itemId)) {
+                if (item.id === selectedUid) {
+                    item.classList.add("node-highlighted-error");
+                } else {
+                    item.classList.add("node-error");
+                }
+            }
+        });
+    }
+
     private unselect() {
         // console.debug(`PbSchemaComponent.unselect()`);
         this._selectedItem = undefined;
diff --git a/src/app/components/remous-results/remous-results.component.ts b/src/app/components/remous-results/remous-results.component.ts
index b39505299078fd465949b1f8c178625e459fc299..b753778b459b0fd9db99057765b099cecfc82bc5 100644
--- a/src/app/components/remous-results/remous-results.component.ts
+++ b/src/app/components/remous-results/remous-results.component.ts
@@ -315,33 +315,35 @@ export class RemousResultsComponent extends ResultsComponentDirective implements
 
         // abscisses pour lesquelles on a un résultat
         const rrr = this._remousResults.result;
-        const firstResultAbscissa = rrr.resultElements[0].values.X;
-        const lastResultAbscissa = rrr.resultElements[rrr.resultElements.length - 1].values.X;
-        let resultAbscissaIndex = 0;
-        const itX = this.abscisseIterator;
-
-        // parcours des abscisses une par une
-        while (itX.hasNext) {
-            const x = itX.next().value;
-            // skip abscissa that have no result
-            if (x >= firstResultAbscissa && x <= lastResultAbscissa) {
-                // result element for the given abscissa
-                const re = rrr.resultElements[resultAbscissaIndex];
-                resultAbscissaIndex++;
-
-                const yExtra = re.getValue(this._remousResults.extraParamSymbol);
-                if (yExtra !== undefined) {
-                    lineExtra.setPoint(x, yExtra);
-                }
+        if (rrr.resultElements.length > 0) {
+            const firstResultAbscissa = rrr.resultElements[0].values.X;
+            const lastResultAbscissa = rrr.resultElements[rrr.resultElements.length - 1].values.X;
+            let resultAbscissaIndex = 0;
+            const itX = this.abscisseIterator;
+
+            // parcours des abscisses une par une
+            while (itX.hasNext) {
+                const x = itX.next().value;
+                // skip abscissa that have no result
+                if (x >= firstResultAbscissa && x <= lastResultAbscissa) {
+                    // result element for the given abscissa
+                    const re = rrr.resultElements[resultAbscissaIndex];
+                    resultAbscissaIndex++;
+
+                    const yExtra = re.getValue(this._remousResults.extraParamSymbol);
+                    if (yExtra !== undefined) {
+                        lineExtra.setPoint(x, yExtra);
+                    }
 
-                const yFlu = re.getValue("flu");
-                if (yFlu !== undefined) {
-                    lineFlu.setPoint(x, yFlu);
-                }
+                    const yFlu = re.getValue("flu");
+                    if (yFlu !== undefined) {
+                        lineFlu.setPoint(x, yFlu);
+                    }
 
-                const yTor = re.getValue("tor");
-                if (yTor !== undefined) {
-                    lineTor.setPoint(x, yTor);
+                    const yTor = re.getValue("tor");
+                    if (yTor !== undefined) {
+                        lineTor.setPoint(x, yTor);
+                    }
                 }
             }
         }
diff --git a/src/app/components/variable-results-selector/variable-results-selector.component.ts b/src/app/components/variable-results-selector/variable-results-selector.component.ts
index 9f91c26b0b0a07218563491cabfb93847975a4bd..663f1f0e5215e8875c79aba15edf1d09db0a592c 100644
--- a/src/app/components/variable-results-selector/variable-results-selector.component.ts
+++ b/src/app/components/variable-results-selector/variable-results-selector.component.ts
@@ -4,8 +4,8 @@ import { I18nService } from "../../services/internationalisation.service";
 import { fv, longestVarParam } from "../../util";
 import { MultiDimensionResults } from "../../results/multidimension-results";
 import { PrebarrageResults } from "../../results/prebarrage-results";
-
 import { CalculatorType, PbBassin, PbCloison, Structure, VariatedDetails } from "jalhyd";
+import { CalculatorResults } from "../../results/calculator-results";
 
 @Component({
     selector: "variable-results-selector",
@@ -81,46 +81,7 @@ export class VariableResultsSelectorComponent implements OnChanges {
     }
 
     protected entryLabel(index: number): string {
-        const kv = [];
-        for (let i = 0; i < this.varValues.length; i++) {
-            const vv = this.varValues[i];
-            const vp = this.results.variatedParameters[i];
-            let symbol = vp.param.symbol;
-            // is vp a parameter of a child Nub ?
-            if (
-                vp.param.parentNub
-                && vp.param.parentNub !== vp.param.originNub
-            ) {
-                let childPrefix: string;
-                // prefix the label depending on (grand)children type
-                switch (vp.param.originNub.calcType) {
-                    case CalculatorType.PreBarrage:
-                        const pbRes = this.results as PrebarrageResults;
-                        if (vp.param.parentNub instanceof Structure) {
-                            const struct = vp.param.parentNub as Structure;
-                            const wall = struct.parent as PbCloison;
-                            const posS = struct.findPositionInParent() + 1;
-                            childPrefix = this.intlService.localizeMessage(wall.description);
-                            // there might be multiple walls between the same pair of basins
-                            if (wall.uid in pbRes.wallsSuffixes) {
-                                childPrefix += " (" + pbRes.wallsSuffixes[wall.uid] + ")";
-                            }
-                            childPrefix += "_" + this.intlService.localizeText("INFO_LIB_STRUCTURE_N_COURT") + posS;
-                        } else if (vp.param.parentNub instanceof PbBassin) {
-                            const bassin = vp.param.parentNub as PbBassin;
-                            childPrefix = this.intlService.localizeMessage(bassin.description);
-                        }
-                        break;
-                    case CalculatorType.MacroRugoCompound:
-                        const posMR = vp.param.parentNub.findPositionInParent() + 1;
-                        childPrefix = this.intlService.localizeText("INFO_LIB_RADIER_N_COURT") + posMR;
-                        break;
-                }
-                symbol = childPrefix + "_" + symbol;
-            }
-            kv.push(`${symbol} = ${vv[index]}`);
-        }
-        return kv.join(", ");
+        return CalculatorResults.variatingModalityLabel(this.varValues, this.results, index);
     }
 
     public get selectedValue(): number {
diff --git a/src/app/formulaire/definition/form-definition.ts b/src/app/formulaire/definition/form-definition.ts
index 442a96ef680fe5e41f5bb28cc62b0ac3e64dc6a4..3fd3ee3a274ab1fedb90e0ae8b2c166701210aea 100644
--- a/src/app/formulaire/definition/form-definition.ts
+++ b/src/app/formulaire/definition/form-definition.ts
@@ -9,7 +9,8 @@ import {
     acSection,
     ParamDefinition,
     Result,
-    VariatedDetails
+    VariatedDetails,
+    Prop_NullParameters
 } from "jalhyd";
 
 import { FormulaireElement } from "../elements/formulaire-element";
@@ -110,6 +111,7 @@ export abstract class FormulaireDefinition extends FormulaireNode implements Obs
      */
     public initNub(props?: Props) {
         const p = props ? props : new Props(this.defaultProperties);
+        p.setPropValue(Prop_NullParameters, ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit); // transmit "empty fields" flag
         this.currentNub = Session.getInstance().createSessionNub(p);
 
         if (this.currentNub instanceof SectionNub) {
diff --git a/src/app/formulaire/definition/form-pab.ts b/src/app/formulaire/definition/form-pab.ts
index d88ae78269c05b9aa8139f04b24d951bc98ecbae..af9b7f56d9ea67134a0d682678477961df20f646 100644
--- a/src/app/formulaire/definition/form-pab.ts
+++ b/src/app/formulaire/definition/form-pab.ts
@@ -2,7 +2,7 @@ import { Pab, Result, VariatedDetails } from "jalhyd";
 
 import { FormulaireDefinition } from "./form-definition";
 import { PabResults } from "../../results/pab-results";
-import { NgParameter } from "../elements/ngparam";
+import { NgParameter, ParamRadioConfig } from "../elements/ngparam";
 import { longestVarParam } from "../../util";
 import { CalculatorResults } from "../../results/calculator-results";
 
diff --git a/src/app/formulaire/definition/form-parallel-structures.ts b/src/app/formulaire/definition/form-parallel-structures.ts
index 5e35710f98d06546543fcd44fabc8ca1d6b604c1..9e4dd86d6b05615acc3555d9b8fc6b1441870589 100644
--- a/src/app/formulaire/definition/form-parallel-structures.ts
+++ b/src/app/formulaire/definition/form-parallel-structures.ts
@@ -1,4 +1,4 @@
-import { Structure, Nub, ParallelStructure, StructureProperties, Props, Session, ParamDefinition } from "jalhyd";
+import { Structure, Nub, ParallelStructure, StructureProperties, Props, Session, ParamDefinition, Prop_NullParameters } from "jalhyd";
 
 import { FieldsetContainer } from "../elements/fieldset-container";
 import { FieldSet } from "../elements/fieldset";
@@ -7,6 +7,7 @@ import { NgParameter } from "../elements/ngparam";
 import { FieldsetTemplate } from "../elements/fieldset-template";
 import { FormulaireNode } from "../elements/formulaire-node";
 import { FormulaireRepeatableFieldset } from "./form-repeatable-fieldset";
+import { ServiceFactory } from "app/services/service-factory";
 
 export class FormulaireParallelStructure extends FormulaireRepeatableFieldset {
 
@@ -66,6 +67,7 @@ export class FormulaireParallelStructure extends FormulaireRepeatableFieldset {
         params["nodeType"] = templ.defaultNodeTypeFromConfig;
         params["structureType"] = templ.defaultStructTypeFromConfig;
         params["loiDebit"] = templ.defaultLoiDebitFromConfig;
+        params[Prop_NullParameters] = ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit;
 
         return this.createStructure(new Props(params));
     }
diff --git a/src/app/formulaire/definition/form-prebarrage.ts b/src/app/formulaire/definition/form-prebarrage.ts
index 4400467c8535c95d25367bc66dca6ebf98e6db17..811007d2c1966bc071dc1b9739e3a9485c3ae4ec 100644
--- a/src/app/formulaire/definition/form-prebarrage.ts
+++ b/src/app/formulaire/definition/form-prebarrage.ts
@@ -1,4 +1,4 @@
-import { CalculatorType, PbBassin, PbCloison, IObservable, PreBarrage, VariatedDetails, ParamDefinition } from "jalhyd";
+import { CalculatorType, PbBassin, PbCloison, IObservable, PreBarrage, VariatedDetails, ParamDefinition, IParamDefinitionIterator, ParamDefinitionIterator } from "jalhyd";
 
 import { FormulaireFixedVar } from "./form-fixedvar";
 import { PbSchema } from "../elements/pb-schema";
@@ -10,6 +10,7 @@ import { CalculatorResults } from "../../results/calculator-results";
 import { PrebarrageResults } from "../../results/prebarrage-results";
 import { NgParameter } from "../elements/ngparam";
 import { longestVarParam } from "../../util";
+import { FormulaireNode } from "../elements/formulaire-node";
 
 /**
  * Formulaire pour les PréBarrage
@@ -322,17 +323,32 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
     }
 
     /**
-     * Set value of all single parameters to undefined, except for the given parameter ids
+     *  Check validity of all model parameters
+     *  @param withChildren check parameters of child nub as well
+     *  @return array of uid nubs in error
      */
-    public emptyFields(except: string[] = [ "Cd0", "CdWS", "CdGR", "CdGRS", "CdCunge", "CdWR", "CdO", "CdT", "CdWSL" ]) {
-        // save current calculated param, as setting value on a CALC param will
-        // change its mode and choose another calculated param by default
-        const paramCalculated = this.currentNub.calculatedParam;
-        for (const p of this.currentNub.parameterIterator) {
-            if (! except.includes(p.symbol)) {
-                p.setValue(undefined);
+         public checkParameters(withChildren: boolean = true): string[] {
+            let prmIt: IParamDefinitionIterator;
+            if(withChildren) {
+                prmIt = this.currentNub.parameterIterator;
+            } else {
+                prmIt = new ParamDefinitionIterator(this.currentNub.prms);
             }
+            const uids: string[] = [];
+            for (const p of prmIt) {
+                if (!p.isCalculated && p.visible) {
+                    try {
+                        // will throw an error if no value is defined at all
+                        p.paramValues.check();
+                    } catch (e) {
+                        if (p.parentNub.calcType === CalculatorType.Structure) {
+                            uids.push(p.parentNub.getParent().uid);
+                        } else {
+                            uids.push(p.parentNub.uid);
+                        }
+                    }
+                }
+            }
+            return uids;
         }
-        this.currentNub.calculatedParam = paramCalculated;
-    }
 }
diff --git a/src/app/formulaire/definition/form-section.ts b/src/app/formulaire/definition/form-section.ts
index fd45ceb50c782c50436c52886767f4abbba236c0..c408e572abe092e6454ee1395e052bc0bfdaac18 100644
--- a/src/app/formulaire/definition/form-section.ts
+++ b/src/app/formulaire/definition/form-section.ts
@@ -20,11 +20,6 @@ export class FormulaireSection extends FormulaireFixedVar {
             }
             // show / hide dependent fields
             this.refreshFieldsets();
-            // empty fields ? only those belonging to the specific section type
-            if (ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit) {
-                // "LargeurBerge" is hackily used as LargeurFond in Rectangular and Trapez sections, omit it here
-                this.emptyFields([ "Ks", "Q", "If", "YB", "iPrec", "Y" ]);
-            }
             this.reset();
         }
     }
diff --git a/src/app/formulaire/elements/fieldset.ts b/src/app/formulaire/elements/fieldset.ts
index dce1e17d48ec85a85578f333ccbf908aaca69413..4081f824133ac596b38848c2e926a0793580713e 100644
--- a/src/app/formulaire/elements/fieldset.ts
+++ b/src/app/formulaire/elements/fieldset.ts
@@ -15,6 +15,8 @@ import { FieldsetContainer } from "./fieldset-container";
 import { SelectFieldCustom } from "./select-field-custom";
 import { FormulaireFixedVar } from "../definition/form-fixedvar";
 import { SelectEntry } from "./select-entry";
+import { FormulaireNode } from "./formulaire-node";
+import { ServiceFactory } from "app/services/service-factory";
 
 export class FieldSet extends FormulaireElement implements Observer {
 
@@ -51,6 +53,40 @@ export class FieldSet extends FormulaireElement implements Observer {
         this.kids.push(f);
     }
 
+    /**
+     * backup NgParameters kids
+     * @returns a list of object representation with sybol, value, modification flag
+     */
+    public backupParameters(): any[] {
+        const res: any[] = [];
+        // for (const p of this.allFormElements) {
+        for (const p of this._kids) {
+            if (p instanceof NgParameter) {
+                res.push(p.objectRepresentation());
+            }
+        }
+        return res;
+    }
+
+    /**
+     * restore NgParameters kids from backup list
+     * @param backup list of NgParameter object representation
+     * @see backupParameters
+     */
+    public restoreParameters(backup: any[]) {
+        // for (const p of this.allFormElements) {
+        for (const p of this._kids) {
+            if (p instanceof NgParameter) {
+                for (const bp of backup) {
+                    if (p.symbol === bp.prmDef.symbol) {
+                        p.loadObjectRepresentation(bp);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
     public get hasInputs(): boolean {
         for (const f of this.kids) {
             if (f instanceof NgParameter) {
@@ -353,9 +389,9 @@ export class FieldSet extends FormulaireElement implements Observer {
                             });
                         }
                     } else {
-                        // for all select fields known by the form, apply received value
-                        // to associated property
                         if (this.parentForm instanceof FormulaireFixedVar) {
+                            // for all select fields known by the form, apply received value
+                            // to associated property
                             const selectIds = this.parentForm.selectids;
                             for (const sId of selectIds) {
                                 if (senderId === sId) {
@@ -374,6 +410,7 @@ export class FieldSet extends FormulaireElement implements Observer {
                             }
                         }
                     }
+
                     break; // switch (data.action)
             }
         }
diff --git a/src/app/formulaire/elements/formulaire-node.ts b/src/app/formulaire/elements/formulaire-node.ts
index 7731dfd43029def02f02d3299a9508ad350c653e..b57bec770d2d128fc76e2fcd53a43370f0efd667 100644
--- a/src/app/formulaire/elements/formulaire-node.ts
+++ b/src/app/formulaire/elements/formulaire-node.ts
@@ -140,32 +140,6 @@ export abstract class FormulaireNode implements IObservable {
         return new DeepFormulaireElementIterator(this);
     }
 
-    /**
-     * Set value of all single parameters to undefined, except for the given parameter ids
-     */
-    public emptyFields(except: string[] = [ "Cd0", "CdWS", "CdGR", "CdGRS", "CdCunge", "CdWR", "CdO", "CdT", "CdWSL" ]) {
-        // save current calculated param, as setting value on a CALC param will
-        // change its mode and choose another calculated param by default
-        let calcP: NgParameter;
-        for (const p of this.allFormElements) {
-            if (p instanceof NgParameter) {
-                if (
-                    [ ParamValueMode.SINGLE, ParamValueMode.CALCUL ].includes(p.valueMode)
-                    && ! except.includes(p.id)
-                ) {
-                    if (p.valueMode === ParamValueMode.CALCUL) {
-                        calcP = p;
-                    }
-                    p.setValue(this, undefined);
-                }
-            }
-        }
-        // restore original calculated param
-        if (calcP !== undefined) {
-            calcP.setCalculated();
-        }
-    }
-
     /**
      * notifie un événement aux observateurs
      */
diff --git a/src/app/formulaire/elements/ngparam.ts b/src/app/formulaire/elements/ngparam.ts
index 7943071edcf11c5c0b865d0b76cfd1548c9dc700..43d7cb29815ed0c8e42baa5fa9910fa2b0b810a5 100644
--- a/src/app/formulaire/elements/ngparam.ts
+++ b/src/app/formulaire/elements/ngparam.ts
@@ -1,5 +1,7 @@
-import { Interval, ParamDefinition, ParamDomain, ParamValueMode, INumberIterator,
-    Observer, asObservable, ParamCalculability, LinkedValue } from "jalhyd";
+import {
+    Interval, ParamDefinition, ParamDomain, ParamValueMode, INumberIterator,
+    Observer, asObservable, ParamCalculability, LinkedValue
+} from "jalhyd";
 
 import { sprintf } from "sprintf-js";
 
@@ -89,7 +91,7 @@ export class NgParameter extends InputField implements Observer {
                 valuePreview = i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION");
                 if (p.calculability === ParamCalculability.DICHO) {
                     valuePreview += " (" + i18n.localizeText("INFO_PARAMFIELD_IN_CALCULATION_INITIAL_VALUE")
-                        + ": " + fv(p.getValue()) + ")";
+                        + ": " + fv(p.initValue) + ")";
                 }
                 break;
             case ParamValueMode.LINK:
@@ -132,7 +134,7 @@ export class NgParameter extends InputField implements Observer {
                 }
             } else {
                 // recursive call, compact mode
-                valuePreview =  NgParameter.preview(targetParam, true);
+                valuePreview = NgParameter.preview(targetParam, true);
             }
         } else {
             // was the result already computed ?
@@ -177,6 +179,9 @@ export class NgParameter extends InputField implements Observer {
         return this._paramDef;
     }
 
+    /**
+     * compute radio state from value mode
+     */
     public get radioState() {
         switch (this._paramDef.valueMode) {
             case ParamValueMode.SINGLE:
@@ -338,6 +343,56 @@ export class NgParameter extends InputField implements Observer {
         );
     }
 
+    private notifyMinValueModified(sender: any) {
+        this.notifyObservers(
+            {
+                "action": "ngparamAfterMinValue",
+                "param": this,
+                "value": this._paramDef.min
+            }, sender
+        );
+    }
+
+    private notifyMaxValueModified(sender: any) {
+        this.notifyObservers(
+            {
+                "action": "ngparamAfterMaxValue",
+                "param": this,
+                "value": this._paramDef.max
+            }, sender
+        );
+    }
+
+    private notifyStepValueModified(sender: any) {
+        this.notifyObservers(
+            {
+                "action": "ngparamAfterStepValue",
+                "param": this,
+                "value": this._paramDef.step
+            }, sender
+        );
+    }
+
+    private notifyListValueModified(sender: any) {
+        this.notifyObservers(
+            {
+                "action": "ngparamAfterStepValue",
+                "param": this,
+                "value": this._paramDef.valueList
+            }, sender
+        );
+    }
+
+    private notifyInitValueModified(sender: any) {
+        this.notifyObservers(
+            {
+                "action": "ngparamAfterInitValue",
+                "param": this,
+                "value": this._paramDef.initValue
+            }, sender
+        );
+    }
+
     /**
      * fixe la valeur du paramètre.
      * une notification préalable est envoyée pour laisser l'occasion aux objets liés de préciser le contexte
@@ -346,39 +401,103 @@ export class NgParameter extends InputField implements Observer {
      * @param val
      */
     public setValue(sender: any, val: number) {
-        this._paramDef.setValue(val, sender);
-        this.notifyValueModified(sender);
+        const changed = (this._paramDef.getValue() !== val);
+        if (changed) {
+            this._paramDef.setValue(val, sender);
+            this.notifyValueModified(sender);
+        }
+    }
+
+    public resetMinValue(sender: any, v: number) {
+        const changed = (this._paramDef.min !== v);
+        if (changed) {
+            this._paramDef.min = v;
+            this.notifyMinValueModified(sender);
+        }
     }
 
     public setMinValue(sender: any, v: number) {
         const changed = (this._paramDef.min !== v);
-        this._paramDef.min = v;
         if (changed) {
-            this.notifyValueModified(sender);
+            this._paramDef.min = v;
+            this.notifyMinValueModified(sender);
+        }
+    }
+
+    public resetMaxValue(sender: any, v: number) {
+        const changed = (this._paramDef.max !== v);
+        if (changed) {
+            this._paramDef.max = v;
+            this.notifyMaxValueModified(sender);
         }
     }
 
     public setMaxValue(sender: any, v: number) {
         const changed = (this._paramDef.max !== v);
-        this._paramDef.max = v;
         if (changed) {
-            this.notifyValueModified(sender);
+            this._paramDef.max = v;
+            this.notifyMaxValueModified(sender);
+        }
+    }
+
+    public resetStepValue(sender: any, v: number) {
+        const changed = (this._paramDef.step !== v);
+        if (changed) {
+            this._paramDef.step = v;
+            this.notifyStepValueModified(sender);
         }
     }
 
     public setStepValue(sender: any, v: number) {
         const changed = (this._paramDef.step !== v);
-        this._paramDef.step = v;
         if (changed) {
-            this.notifyValueModified(sender);
+            this._paramDef.step = v;
+            this.notifyStepValueModified(sender);
+        }
+    }
+
+    public resetValueList(sender: any, l: number[]) {
+        const changed = (JSON.stringify(this._paramDef.valueList) !== JSON.stringify(l));
+        if (changed) {
+            this._paramDef.valueList = l;
+            this.notifyListValueModified(sender);
         }
     }
 
     public setValueList(sender: any, l: number[]) {
         const changed = (JSON.stringify(this._paramDef.valueList) !== JSON.stringify(l));
-        this._paramDef.valueList = l;
         if (changed) {
-            this.notifyValueModified(sender);
+            this._paramDef.valueList = l;
+            this.notifyListValueModified(sender);
+        }
+    }
+
+    /**
+     * set initial value for CALC mode
+     */
+    public setInitValue(sender: any, v: number) {
+        const changed = (this._paramDef.initValue !== v);
+        if (changed) {
+            this._paramDef.initValue = v;
+            this.notifyInitValueModified(sender);
+        }
+    }
+
+    public objectRepresentation(): any {
+        return {
+            prmDef: this.paramDefinition.objectRepresentation(),
+            allowEmpty: this._allowEmpty,
+            unit: this.unit,
+            radioConfig: this.radioConfig,
+        }
+    }
+
+    public loadObjectRepresentation(rep: any) {
+        if (this._paramDef.symbol === rep.prmDef.symbol) {
+            this._paramDef.loadObjectRepresentation(rep.prmDef);
+            this._allowEmpty = rep.allowEmpty;
+            this.unit = rep.unit;
+            this.radioConfig = rep.radioConfig;
         }
     }
 
@@ -401,9 +520,9 @@ export class NgParameter extends InputField implements Observer {
     public linkToValue(target: LinkedValue) {
         const changed: boolean =
             // changement de mode
-            ! this._paramDef.isReferenceDefined()
+            !this._paramDef.isReferenceDefined()
             // ou changement de référence
-            || ! this._paramDef.referencedValue.equals(target);
+            || !this._paramDef.referencedValue.equals(target);
 
         if (changed) {
             let o = asObservable(this._paramDef.referencedValue);
diff --git a/src/app/results/calculator-results.ts b/src/app/results/calculator-results.ts
index f573b6c99c7585784791e58f964736e606eb22ba..a205ceddd259c84ec09bd3ee34ed2e78b6f7252e 100644
--- a/src/app/results/calculator-results.ts
+++ b/src/app/results/calculator-results.ts
@@ -1,9 +1,11 @@
-import { Nub, capitalize } from "jalhyd";
+import { CalculatorType, Nub, capitalize, MacrorugoCompound, Pab, Structure, PbCloison, PbBassin } from "jalhyd";
 
 import { NgParameter } from "../formulaire/elements/ngparam";
 import { ServiceFactory } from "../services/service-factory";
 
 import { sprintf } from "sprintf-js";
+import { MultiDimensionResults } from "./multidimension-results";
+import { PrebarrageResults } from "./prebarrage-results";
 
 export abstract class CalculatorResults {
 
@@ -45,6 +47,61 @@ export abstract class CalculatorResults {
         return res;
     }
 
+    /**
+     * Returns a label showing the boundary conditions values of all variating parameters,
+     * for the given iteration
+     * @param varvalues array of values: one element per variating parameter, itself an array of
+     *     values, one per iteration
+     * @param variatedParameters array of variating parameters, in the same order as varvalues
+     * @param n index of the variating parameter(s) iteration
+     */
+    public static variatingModalityLabel(varValues: any[], results: MultiDimensionResults, index: number): string {
+        const kv = [];
+        for (let i = 0; i < varValues.length; i++) {
+            const vv = varValues[i];
+            const vp = results.variatedParameters[i];
+            let symbol = vp.param.symbol;
+            // is vp a parameter of a child Nub ?
+            if (
+                vp.param.parentNub
+                && vp.param.parentNub !== vp.param.originNub
+            ) {
+                let childPrefix: string;
+                // prefix the label depending on (grand)children type
+                switch (vp.param.originNub.calcType) {
+                    case CalculatorType.PreBarrage:
+                        const pbRes = results as PrebarrageResults;
+                        if (vp.param.parentNub instanceof Structure) {
+                            const struct = vp.param.parentNub as Structure;
+                            const wall = struct.parent as PbCloison;
+                            const posS = struct.findPositionInParent() + 1;
+                            childPrefix = ServiceFactory.i18nService.localizeMessage(wall.description);
+                            // there might be multiple walls between the same pair of basins
+                            if (wall.uid in pbRes.wallsSuffixes) {
+                                childPrefix += " (" + pbRes.wallsSuffixes[wall.uid] + ")";
+                            }
+                            childPrefix += "_" + ServiceFactory.i18nService.localizeText("INFO_LIB_STRUCTURE_N_COURT") + posS;
+                        } else if (vp.param.parentNub instanceof PbBassin) {
+                            const bassin = vp.param.parentNub as PbBassin;
+                            childPrefix = ServiceFactory.i18nService.localizeMessage(bassin.description);
+                        }
+                        break;
+                    case CalculatorType.MacroRugoCompound:
+                        const posMR = vp.param.parentNub.findPositionInParent() + 1;
+                        childPrefix = ServiceFactory.i18nService.localizeText("INFO_LIB_RADIER_N_COURT") + posMR;
+                        break;
+                    case CalculatorType.Pab:
+                        const posPAB = vp.param.parentNub.findPositionInParent() + 1;
+                        childPrefix = ServiceFactory.i18nService.localizeText("INFO_LIB_CLOISON_N_COURT") + posPAB;
+                        break;
+                }
+                symbol = childPrefix + "_" + symbol;
+            }
+            kv.push(`${symbol} = ${vv[index]}`);
+        }
+        return kv.join(", ");
+    }
+
     /**
      * remet tous les résultats à zero
      */
diff --git a/src/app/results/remous-results.ts b/src/app/results/remous-results.ts
index dedfd9a6f78aabbd22f97f66bb02d748d5d4c9de..abb20aa69362c07f6dd26dd436b341d33190b0dc 100644
--- a/src/app/results/remous-results.ts
+++ b/src/app/results/remous-results.ts
@@ -90,7 +90,7 @@ export class RemousResults extends CalculatorResults {
         // série de valeurs de X
         this._xValues = new ParamDefinition(
             p,
-            ServiceFactory.i18nService.localizeText("INFO_REMOUSRESULTS_ABSCISSE"),
+            "ABSCISSE",
             ParamDomainValue.POS_NULL
         );
     }
diff --git a/src/app/services/app-setup.service.ts b/src/app/services/app-setup.service.ts
index d1f01b2af7fbc411487f2e8045bd337299b842b6..32b9f517da6c776b8a43fe6df266d80deedefc55 100644
--- a/src/app/services/app-setup.service.ts
+++ b/src/app/services/app-setup.service.ts
@@ -24,7 +24,7 @@ export class ApplicationSetupService extends Observable {
     private _maxIterations = 100; // tied to model
     public enableNotifications = true;
     public enableHotkeys = false;
-    public enableEmptyFieldsOnFormInit = true;
+    private _enableEmptyFieldsOnFormInit = true;
 
     public set computePrecision(p: number) {
         this._computePrecision = p;
@@ -46,6 +46,14 @@ export class ApplicationSetupService extends Observable {
         return this._maxIterations;
     }
 
+    public get enableEmptyFieldsOnFormInit() {
+        return this._enableEmptyFieldsOnFormInit;
+    }
+
+    public set enableEmptyFieldsOnFormInit(b: boolean) {
+        this._enableEmptyFieldsOnFormInit = b;
+    }
+
     /**
      * just stores the current language preference, does not transmit it to I18nService, that is
      * not available here.
@@ -71,6 +79,9 @@ export class ApplicationSetupService extends Observable {
         // related to @HostListener("window:beforeunload") in AppComponent
         this.warnBeforeTabClose = true;
 
+        // by default, create empty fields for new calculators
+        this.enableEmptyFieldsOnFormInit = true;
+
         // load JSON config
         this.readValuesFromConfig().then((data) => {
             const configLanguage = this.language;
diff --git a/src/app/services/formulaire.service.ts b/src/app/services/formulaire.service.ts
index 5d3910bbfc888c573f59c31074b96fc5279095a9..1bfdbe14fe9daced78b2821402b48ab549c00223 100644
--- a/src/app/services/formulaire.service.ts
+++ b/src/app/services/formulaire.service.ts
@@ -50,6 +50,7 @@ import { FormulairePAR } from "../formulaire/definition/form-par";
 import { FormulaireVerificateur } from "../formulaire/definition/form-verificateur";
 import { FormulaireEspece } from "../formulaire/definition/form-espece";
 import { FormulairePrebarrage } from "../formulaire/definition/form-prebarrage";
+import { ServiceFactory } from "./service-factory";
 
 @Injectable()
 export class FormulaireService extends Observable {
@@ -628,6 +629,10 @@ export class FormulaireService extends Observable {
      */
     public async loadSession(f: File, formInfos: any[] = []): Promise<{ hasErrors: boolean, loaded: string[] }> {
         try {
+            // disable "empty fields" flag temporarly
+            const emptyFields = ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit;
+            ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit = false;
+
             const s = await this.readSingleFile(f);
             const uids: string[] = [];
             formInfos.forEach((fi) => {
@@ -646,6 +651,10 @@ export class FormulaireService extends Observable {
                 }
                 await this.createFormulaire(nn.nub.calcType, nn.nub, title); // await guarantees loading order
             }
+
+            // restore "empty fields" flag
+            ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit = emptyFields;
+
             // apply settings
             if (res.settings) {
                 // model based settings
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index b21bd2204fe17d48f765121b1f8c93199f288531..022f3ee461f807220333e6d23369ae20e9ffc99d 100755
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -285,6 +285,7 @@
     "INFO_LIB_CDT": "Discharge coefficient triangular weir",
     "INFO_LIB_CDO": "Discharge coefficient orifice",
     "INFO_LIB_CLOISON": "Cross wall #",
+    "INFO_LIB_CLOISON_N_COURT": "W",
     "INFO_LIB_COTE": "Elevation (m)",
     "INFO_LIB_COTE_VANNE_LEVANTE": "Lift gate elevation",
     "INFO_LIB_CV": "Cv: Velocity coefficient",
@@ -566,7 +567,7 @@
     "INFO_REMOUSRESULTS_ABSCISSE": "Abscissa",
     "INFO_REMOUSRESULTS_BERGE": "Embankment",
     "INFO_REMOUSRESULTS_FOND": "Bottom",
-    "INFO_REMOUSRESULTS_TIRANT": "Draft (m)",
+    "INFO_REMOUSRESULTS_TIRANT": "Water depth (m)",
     "INFO_REMOUSRESULTS_TIRANTCRITIQUE": "Critical water level",
     "INFO_REMOUSRESULTS_TIRANTNORMAL": "Normal water level",
     "INFO_REPORT_BUG_BODY": "This is an issue report.\n\nPlease describe quickly the issue you encoutered, and the steps you followed:\n\n\n\n\n--- Current session state - do not modify text below ---\n------------------------------------------------------------------------\n\n",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index 5169ac6aeb341c19fcea66666e9101a3425cd8bc..6e487527a3f6a9a6e897c53855ef36410d77c039 100755
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -286,6 +286,7 @@
     "INFO_LIB_CDT": "Coefficient de débit seuil triangulaire",
     "INFO_LIB_CDO": "Coefficient de débit orifice",
     "INFO_LIB_CLOISON": "Cloison n°",
+    "INFO_LIB_CLOISON_N_COURT": "C",
     "INFO_LIB_COTE": "Cote (m)",
     "INFO_LIB_COTE_VANNE_LEVANTE": "Cote vanne levante",
     "INFO_LIB_CV": "Cv&nbsp;: Coefficient de vitesse d'approche",
diff --git a/src/styles.scss b/src/styles.scss
index 4f086079ecf28da2a005d9c00a31a5a3c3b2f211..87774a115ad792fd74586403d54677cda31c5e95 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -129,15 +129,64 @@ field-set {
         .mat-select-value, .mat-select-arrow {
             color: #fff;
         }
-        
+
         .mat-form-field-infix {
             border-bottom: 2px solid #fff;
         }
     }
 }
 
+.qaFieldLineContainer {
+
+    .qaFieldLine {
+        .mat-button-toggle-label-content {
+            line-height: 24px !important;
+        }
+    }
+
+    ngparam-input {
+        height: 38px;
+        margin-top: 0;
+    }
+
+    param-values {
+        margin-top: 0;
+    }
+
+    param-link {
+        margin-top: 0;
+
+        .status-icons-container {
+            display: none;
+        }
+    }
+
+    mat-form-field {
+
+        .mat-form-field-wrapper {
+            padding-bottom: 5px;
+        }
+
+        input.form-control.mat-input-element {
+            width: 100%;
+        }
+
+        button.param-values-more {
+            display: none;
+        }
+
+        .mat-form-field-underline {
+            display: none;
+        }
+
+        mat-error.mat-error {
+            display: none;
+        }
+    }
+}
+
 .pab-data-table {
-    
+
     .editable-cell-bg {
         @extend .bg-accent-extralight;
     }
@@ -222,7 +271,7 @@ field-set {
                         > input[type="number"] {
                             -moz-appearance: textfield;
                         }
-                        input[type=number]::-webkit-outer-spin-button, 
+                        input[type=number]::-webkit-outer-spin-button,
                         input[type=number]::-webkit-inner-spin-button {
                             -webkit-appearance: none;
                             margin: 0;
@@ -248,7 +297,7 @@ mat-checkbox.wrapped-checkbox {
 param-computed, param-values {
 
     mat-form-field {
-    
+
         input.mat-input-element {
             width: calc(100% - 40px);
             text-overflow: ellipsis;
@@ -304,7 +353,7 @@ mat-list {
         > .mat-list-item {
             height: auto;
             margin-bottom: .5em;
-    
+
             > .mat-list-item-content {
                 align-items: start;
 
@@ -317,7 +366,7 @@ mat-list {
 }
 
 table.mat-table {
-    
+
     .material-icons {
         font-size: 1.4em;
         vertical-align: bottom;