diff --git a/boilerplate/ts/src/boilerplate.ts b/boilerplate/ts/src/boilerplate.ts
index 4c8587c6e14765e139c229a9c02d5b82934252b0..5421d43e8cad7b288b749d5847993efa810fda65 100644
--- a/boilerplate/ts/src/boilerplate.ts
+++ b/boilerplate/ts/src/boilerplate.ts
@@ -7,20 +7,15 @@ import {
     formattedValue,
     LoiDebit,
     Props,
-    Session
+    Session,
+    Cloisons,
+    PabChute,
+    PabNombre,
+    PabPuissance,
+    PabDimension,
+    RectangularStructure
 } from "jalhyd";
 
-/**
- * Not all classes are exported through index.d.ts, prefer importing
- * classes using full path
- */
-import { Cloisons } from "jalhyd/build/pab/cloisons";
-import { PabChute } from "jalhyd/build/pab/pab_chute";
-import { PabDimension } from "jalhyd/build/pab/pab_dimension";
-import { PabNombre } from "jalhyd/build/pab/pab_nombre";
-import { PabPuissance } from "jalhyd/build/pab/pab_puissance";
-import { RectangularStructure } from "jalhyd/build/structure/rectangular_structure";
-
 // ---- example of modules setup and calculation : fish ladder ----
 
 const pabChute = Session.getInstance().createSessionNub(
diff --git a/package-lock.json b/package-lock.json
index 81da99e6e87b129e1a01de03ad740f4ac6ecb94a..3726cb5715387e4d42ea6cae09b51acc3512d829 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
       "license": "LGPL-3.0-or-later",
       "dependencies": {
         "@types/base-64": "^1.0.0",
+        "@types/lodash": "^4.14.191",
         "base-64": "^1.0.0"
       },
       "devDependencies": {
@@ -284,6 +285,11 @@
       "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
       "dev": true
     },
+    "node_modules/@types/lodash": {
+      "version": "4.14.191",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
+      "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
+    },
     "node_modules/@types/node": {
       "version": "18.0.3",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz",
@@ -2777,6 +2783,11 @@
       "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
       "dev": true
     },
+    "@types/lodash": {
+      "version": "4.14.191",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
+      "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
+    },
     "@types/node": {
       "version": "18.0.3",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.3.tgz",
diff --git a/package.json b/package.json
index 01d22ed74231dc3a8f8113a703c00815a64fb564..0b8cfecc6d68330f4db0e94ed0f798ee2a22bf0a 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
   "module": "build/index.js",
   "dependencies": {
     "@types/base-64": "^1.0.0",
+    "@types/lodash": "^4.14.191",
     "base-64": "^1.0.0"
   },
   "devDependencies": {
diff --git a/spec/fuzzing.spec.ts b/spec/fuzzing.spec.ts
index 8dead94243e8e6dd27123927ab82803368df6172..87adb5ba9c87276bfdd1eee41db7a3291d6d60fd 100644
--- a/spec/fuzzing.spec.ts
+++ b/spec/fuzzing.spec.ts
@@ -1,5 +1,5 @@
 import { CalculatorType } from "../src/compute-node";
-import { Grille, ParamDomainValue, Session, SPP, YAXN, YAXNParams, Par, ParSimulation, ParSimulationParams } from "../src/index";
+import { Grille, ParamDomainValue, Session, SPP, YAXN, YAXNParams, Par, ParSimulation, ParSimulationParams, PressureLoss, PL_LechaptCalmonParams, PL_LechaptCalmon } from "../src/index";
 import { MacrorugoCompound } from "../src/macrorugo/macrorugo_compound";
 import { Trigo, TrigoOperation } from "../src/math/trigo";
 import { Nub } from "../src/nub";
@@ -7,7 +7,7 @@ import { SectionNub } from "../src/open-channel/section/section_nub";
 import { CloisonAval } from "../src/pab/cloison_aval";
 import { Pab } from "../src/pab/pab";
 import { ParamDefinition } from "../src/param/param-definition";
-import { PressureLossType } from "../src/pipe_flow/pressureloss_law";
+import { PressureLossLaw, PressureLossType } from "../src/pipe_flow/pressureloss_law";
 import { Props } from "../src/props";
 import { CreateStructure } from "../src/structure/factory_structure";
 import { ParallelStructure } from "../src/structure/parallel_structure";
@@ -46,7 +46,8 @@ const nubsNotTested: CalculatorType[] = [
     CalculatorType.Espece,
     CalculatorType.PbBassin,
     CalculatorType.PbCloison,
-    CalculatorType.PreBarrage // TODO: Add special treatments for creating fuzzy PreBarrage
+    CalculatorType.PreBarrage, // TODO: Add special treatments for creating fuzzy PreBarrage
+    CalculatorType.LechaptCalmon
 ];
 
 const nubsWithStructures: CalculatorType[] = [
@@ -61,6 +62,10 @@ const nubsWithSection: CalculatorType[] = [
     CalculatorType.CourbeRemous
 ];
 
+const nubsWithPressureLossLaw: CalculatorType[] = [
+    CalculatorType.PressureLoss
+]
+
 const calTypes =
     Object.keys(CalculatorType)
         .filter((key: any) =>
@@ -127,6 +132,26 @@ function setRandomSection(sn: SectionNub) {
     sn.setSection(newSect);
 }
 
+function setRandomPressureLossLaw(pl: PressureLoss) {
+    const pressureLossLaws = [CalculatorType.LechaptCalmon];
+    const i = Math.floor(Math.random() * pressureLossLaws.length);
+    let pll: PressureLossLaw;
+    switch (pressureLossLaws[i]) {
+        case CalculatorType.LechaptCalmon:
+            const lcp = new PL_LechaptCalmonParams(1.863, 2, 5.33); // peu importe les valeurs, elles sont randomisées
+            randomizeParameter(lcp.L);
+            randomizeParameter(lcp.M);
+            randomizeParameter(lcp.N);
+            const lc = new PL_LechaptCalmon(lcp);
+            pll = lc;
+            break;
+
+        default:
+            throw new Error("invalid pressure loss law");
+    }
+    pl.setLaw(pll);
+}
+
 function setRandomTrigoOperation(sn: Trigo) {
     const op = Math.floor(Math.random() * 6);
     sn.operation = op;
@@ -169,7 +194,7 @@ function setPab(pab: Pab, nClMax = 30, nStMax = 3) {
 }
 
 function setMacrorugoCompound(n: MacrorugoCompound) {
-    n.properties.setPropValue("inclinedApron", Math.floor(Math.random() * 2));
+    n.setPropValue("inclinedApron", Math.floor(Math.random() * 2));
 }
 
 function setGrille(g: Grille) {
@@ -189,8 +214,11 @@ function CreateTestNub(iCalType: number): Nub {
     if (nubsWithSection.includes(iCalType)) {
         setRandomSection(n as SectionNub);
     }
+    if (nubsWithPressureLossLaw.includes(iCalType)) {
+        setRandomPressureLossLaw(n as PressureLoss);
+    }
     if (iCalType === CalculatorType.CourbeRemous) {
-        n.properties.setPropValue(
+        n.setPropValue(
             "methodeResolution",
             Math.floor(Math.random() * 3) // Euler, RK4, Trapèzes
         );
diff --git a/spec/macrorugo/macrorugo_compound.spec.ts b/spec/macrorugo/macrorugo_compound.spec.ts
index b71376acb302d436b25cd76f991c9b58bdf1ff3e..be2e09c8f6dd3b811316aea450de0453f1ee7f86 100644
--- a/spec/macrorugo/macrorugo_compound.spec.ts
+++ b/spec/macrorugo/macrorugo_compound.spec.ts
@@ -73,7 +73,7 @@ describe("MacroRugoCompound: ", () => {
                 const mrc = Session.getInstance().createNub(
                     new Props({ calcType: CalculatorType.MacroRugoCompound })
                 ) as MacrorugoCompound;
-                mrc.properties.setPropValue("inclinedApron", MRCInclination.INCLINED);
+                mrc.setPropValue("inclinedApron", MRCInclination.INCLINED);
                 mrc.prms.BR.singleValue = BR;
                 const mr = getMacroRugoRef();
                 const res = mrc.CalcSerie();
@@ -97,7 +97,7 @@ describe("MacroRugoCompound: ", () => {
             const mrc = Session.getInstance().createNub(
                 new Props({ calcType: CalculatorType.MacroRugoCompound })
             ) as MacrorugoCompound;
-            mrc.properties.setPropValue("inclinedApron", MRCInclination.INCLINED);
+            mrc.setPropValue("inclinedApron", MRCInclination.INCLINED);
             // width = 4m
             mrc.prms.BR.singleValue = 5;
             // delta Z = 2m
diff --git a/spec/macrorugo/macrorugo_compound_jalhyd174.spec.ts b/spec/macrorugo/macrorugo_compound_jalhyd174.spec.ts
index ffdc327280fc09bc9f45bfb5b127ae7bc3cdce16..f7178f010cb0db619f578fca8eb18af13920cba8 100644
--- a/spec/macrorugo/macrorugo_compound_jalhyd174.spec.ts
+++ b/spec/macrorugo/macrorugo_compound_jalhyd174.spec.ts
@@ -62,7 +62,7 @@ describe("MacroRugoCompound: ", () => {
             nub = Session.getInstance().createNub(
                 new Props({ calcType: CalculatorType.MacroRugoCompound })
             ) as MacrorugoCompound;
-            nub.properties.setPropValue("inclinedApron", MRCInclination.INCLINED);
+            nub.setPropValue("inclinedApron", MRCInclination.INCLINED);
             nub.prms.C.singleValue = 0.09; // easy sqrt
             nub.prms.PBD.singleValue = 0.6; // ax = 2
             nub.prms.BR.singleValue = 3;
@@ -82,7 +82,7 @@ describe("MacroRugoCompound: ", () => {
             nub = Session.getInstance().createNub(
                 new Props({ calcType: CalculatorType.MacroRugoCompound })
             ) as MacrorugoCompound;
-            nub.properties.setPropValue("inclinedApron", MRCInclination.INCLINED);
+            nub.setPropValue("inclinedApron", MRCInclination.INCLINED);
             nub.prms.C.singleValue = 0.09;
             nub.prms.PBD.singleValue = 0.6;
             nub.prms.BR.singleValue = 1.95;
@@ -104,7 +104,7 @@ describe("MacroRugoCompound: ", () => {
             nub = Session.getInstance().createNub(
                 new Props({ calcType: CalculatorType.MacroRugoCompound })
             ) as MacrorugoCompound;
-            nub.properties.setPropValue("inclinedApron", MRCInclination.INCLINED);
+            nub.setPropValue("inclinedApron", MRCInclination.INCLINED);
             nub.prms.C.singleValue = 0.09;
             nub.prms.PBD.singleValue = 0.6;
             nub.prms.BR.singleValue = 5.8;
diff --git a/spec/macrorugo/macrorugo_compound_jalhyd284.spec.ts b/spec/macrorugo/macrorugo_compound_jalhyd284.spec.ts
index 1c6fe7ef7b09779df031972bb911abda0c58da9e..9c170171a7be7a2f1c466038d80af321ac4a98a2 100644
--- a/spec/macrorugo/macrorugo_compound_jalhyd284.spec.ts
+++ b/spec/macrorugo/macrorugo_compound_jalhyd284.spec.ts
@@ -11,7 +11,7 @@ describe("MacroRugoCompound: ", () => {
         nub = Session.getInstance().createNub(
             new Props({ calcType: CalculatorType.MacroRugoCompound })
         ) as MacrorugoCompound;
-        nub.properties.setPropValue("inclinedApron", MRCInclination.INCLINED);
+        nub.setPropValue("inclinedApron", MRCInclination.INCLINED);
     });
 
     describe("jalhyd #284 warnings about block concentration − ", () => {
diff --git a/spec/pipe_flow/lechaptcalmon.spec.ts b/spec/pipe_flow/lechaptcalmon.spec.ts
index a7fa7bc264f73527a6aa74e812556c0c870e74a0..7625d080670cc32636422e63de9f3ab8f83e3d19 100644
--- a/spec/pipe_flow/lechaptcalmon.spec.ts
+++ b/spec/pipe_flow/lechaptcalmon.spec.ts
@@ -1,28 +1,56 @@
+import { PressureLoss, PressureLossParams } from "../../src/internal_modules";
+import { LCMaterial } from "../../src/lc-material";
 import { ParamCalculability } from "../../src/param/param-definition";
 import { PL_LechaptCalmon } from "../../src/pipe_flow/pl_lechaptcalmon";
 import { PL_LechaptCalmonParams } from "../../src/pipe_flow/pl_lechaptcalmon_params";
 import { SessionSettings } from "../../src/session_settings";
 import { MessageCode } from "../../src/util/message";
 
-function getLechapt(): PL_LechaptCalmon {
-    const l = new PL_LechaptCalmon(
+function getLechapt(): PressureLoss {
+    const plParams = new PressureLossParams(
+        0.3, // débit
+        0.5, // diamètre
+        0.1, /// perte de charge
+        20, // longueur du toyo
+        1 // Kloc
+    );
+    const lc = new PL_LechaptCalmon(
         new PL_LechaptCalmonParams(
-            0.3, // débit
-            0.5, // diamètre
-            0.1, /// perte de charge
-            20, // longueur du toyo
-            1, // Ks
             1.863, // paramètre L du matériau
             2, // paramètre M du matériau
             5.33 // paramètre N du matériau
         )
     );
-    l.calculatedParam = l.prms.J;
-    l.prms.J.singleValue = l.CalcSerie().vCalc;
-    return l;
+    const pl: PressureLoss = new PressureLoss(plParams, lc);
+
+    pl.calculatedParam = pl.prms.J;
+    pl.prms.J.singleValue = pl.CalcSerie().vCalc;
+    return pl;
 }
 
-let lechapt: PL_LechaptCalmon = getLechapt();
+function getLechaptWith(Q: number, D: number, Lg: number, Kloc: number, mat: LCMaterial): PressureLoss {
+    const plParams = new PressureLossParams(
+        Q, // débit
+        D, // diamètre
+        0, /// perte de charge
+        Lg, // longueur du toyo
+        Kloc, // coef de perte de charge singulière
+    );
+    const lc = new PL_LechaptCalmon(
+        new PL_LechaptCalmonParams(
+            1.863, // paramètre L du matériau
+            2, // paramètre M du matériau
+            5.33 // paramètre N du matériau
+        )
+    );
+    lc.material = mat;
+    const pl: PressureLoss = new PressureLoss(plParams, lc);
+    pl.calculatedParam = pl.prms.J;
+    pl.prms.J.singleValue = pl.CalcSerie().vCalc;
+    return pl;
+}
+
+let lechapt: PressureLoss = getLechapt();
 
 describe("Class LechaptCalmon : ", () => {
     beforeAll(() => {
@@ -35,16 +63,17 @@ describe("Class LechaptCalmon : ", () => {
         if ([ParamCalculability.EQUATION, ParamCalculability.DICHO].includes(p.calculability) && p.visible) {
             it(`Calc(${p.symbol}) should return ${p.currentValue}`, () => {
                 lechapt.calculatedParam = lechapt.getParameter(p.symbol);
-                const ref: number = p.currentValue;
+                lechapt.CalcSerie();
+                const ref: number = lechapt.result.vCalc;
                 const V: number = lechapt.result.values.V;
-                const Jl: number = lechapt.result.values.Jl;
-                const Kl: number = lechapt.result.values.Kl;
+                const Jlin: number = lechapt.child.result.values.Jlin;
+                const Klin: number = lechapt.result.values.Klin;
                 const fD: number = lechapt.result.values.fD;
                 lechapt.calculatedParam.singleValue = lechapt.calculatedParam.singleValue / 2;
                 expect(lechapt.CalcSerie().vCalc).toBeCloseTo(ref, 2);
                 expect(lechapt.result.values.V).toBeCloseTo(V, 3);
-                expect(lechapt.result.values.Jl).toBeCloseTo(Jl, 3);
-                expect(lechapt.result.values.Kl).toBeCloseTo(Kl, 3);
+                expect(lechapt.child.result.values.Jlin).toBeCloseTo(Jlin, 3);
+                expect(lechapt.result.values.Klin).toBeCloseTo(Klin, 3);
                 expect(lechapt.result.values.fD).toBeCloseTo(fD, 3);
             });
         }
@@ -53,7 +82,7 @@ describe("Class LechaptCalmon : ", () => {
     describe("warning on speed value -", () => {
 
         it("case 1: there should not be any warning", () => {
-            const res = lechapt.CalcSerie();
+            const res = lechapt.result;
             expect(res.log.messages.length).toBe(0);
         });
 
@@ -65,3 +94,77 @@ describe("Class LechaptCalmon : ", () => {
         });
     });
 });
+
+describe("article original", () => {
+    it("", () => {
+        const oldMaxIter = SessionSettings.maxIterations;
+        const oldPrec = SessionSettings.precision;
+
+        try {
+            SessionSettings.maxIterations = 100;
+            SessionSettings.precision = 1e-7;
+
+            const plParams = new PressureLossParams(
+                0.0085, // débit
+                0.175, // diamètre
+                0, /// perte de charge
+                1000, // longueur du toyo
+                0, // Kloc
+            );
+
+            // L,M,N : cf. pl_lechaptcalmon.ts
+            const lechaptPrms = new PL_LechaptCalmonParams(
+                1.863, // paramètre L du matériau
+                2, // paramètre M du matériau
+                5.33, // paramètre N du matériau
+            );
+
+            const lechapt = new PL_LechaptCalmon(lechaptPrms);
+            lechapt.material = LCMaterial.HydraulicallySmoothPipe005D02;
+
+            const pl: PressureLoss = new PressureLoss(plParams, lechapt);
+
+            const res = pl.CalcSerie();
+            expect(res.vCalc).toBeCloseTo(0.784, 3);
+        }
+        finally {
+            SessionSettings.maxIterations = oldMaxIter;
+            SessionSettings.precision = oldPrec;
+        }
+    });
+});
+
+describe("specific values -", () => {
+    it("test 1", () => {
+        const pl: PressureLoss = getLechaptWith(3, 1.2, 100, 0, LCMaterial.HydraulicallySmoothPipe025D1);
+        let r = pl.result;
+        let cr = pl.child.result;
+        expect(r.vCalc).toBeCloseTo(0.295085, 6); // J
+        expect(r.values.V).toBeCloseTo(2.652582, 6);
+        expect(cr.values.Jlin).toBeCloseTo(0.295085, 6);
+        expect(r.values.Klin).toBeCloseTo(0.822826, 6);
+        expect(r.values.fD).toBeCloseTo(0.009874, 6);
+
+        const lc: PL_LechaptCalmon = pl.child as PL_LechaptCalmon;
+        lc.material = LCMaterial.UnlinedCastIronCoarseConcrete;
+        pl.CalcSerie();
+        r = pl.result;
+        cr = pl.child.result;
+        expect(r.vCalc).toBeCloseTo(0.634482, 6); // J
+        expect(r.values.V).toBeCloseTo(2.652582, 6);
+        expect(cr.values.Jlin).toBeCloseTo(0.634482, 6);
+        expect(r.values.Klin).toBeCloseTo(1.769215, 6);
+        expect(r.values.fD).toBeCloseTo(0.021231, 6);
+    });
+
+    it("test 2", () => {
+        const pl: PressureLoss = getLechaptWith(0.05, 0.5, 100, 1, LCMaterial.RolledSteelSmoothConcrete);
+        const r = pl.result;
+        const cr = pl.child.result;
+        expect(r.vCalc).toBeCloseTo(0.015625, 6); // J  
+        expect(r.values.V).toBeCloseTo(0.254648, 6);
+        expect(cr.values.Jlin).toBeCloseTo(0.012320, 6);
+        expect(r.values.Klin).toBeCloseTo(3.727563, 6);
+        expect(r.values.fD).toBeCloseTo(0.023638, 6);
+    });
+});
diff --git a/spec/session/serialisation.spec.ts b/spec/session/serialisation.spec.ts
index e77d7c12838a5b7b036b4d9e1ffd6eca038b81e8..c857a40ddda74516239124dc77be013061c9f4af 100644
--- a/spec/session/serialisation.spec.ts
+++ b/spec/session/serialisation.spec.ts
@@ -522,7 +522,7 @@ describe("PreBarrage - ", () => {
         expect(c1.bassinAmont).toBeUndefined();
         expect(c1.bassinAval).toBeUndefined();
         expect(c1.structures.length).toBe(1);
-        expect(c1.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.WeirSubmergedLarinier);
+        expect(c1.structures[0].getPropValue("loiDebit")).toBe(LoiDebit.WeirSubmergedLarinier);
         const s1 = c1.structures[0].prms as RectangularStructureParams;
         expect(s1.ZDV.singleValue).toBe(101.11);
         expect(s1.L.singleValue).toBe(0.211);
@@ -532,7 +532,7 @@ describe("PreBarrage - ", () => {
         expect(c2.bassinAmont).toBeUndefined();
         expect(c2.bassinAval.uid).toBe("M3AxbT");
         expect(c2.structures.length).toBe(1);
-        expect(c2.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.TriangularWeirBroad);
+        expect(c2.structures[0].getPropValue("loiDebit")).toBe(LoiDebit.TriangularWeirBroad);
         const s2 = c2.structures[0].prms as TriangularStructureParams;
         expect(s2.ZDV.singleValue).toBe(101.22);
         expect(s2.CdT.singleValue).toBe(1.3622);
@@ -542,7 +542,7 @@ describe("PreBarrage - ", () => {
         expect(c3.bassinAmont).toBeUndefined();
         expect(c3.bassinAval.uid).toBe("d2kxcD");
         expect(c3.structures.length).toBe(1);
-        expect(c3.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.OrificeFree);
+        expect(c3.structures[0].getPropValue("loiDebit")).toBe(LoiDebit.OrificeFree);
         const s3 = c3.structures[0].prms as StructureOrificeFreeParams;
         expect(s3.S.singleValue).toBe(0.133);
         expect(s3.CdO.singleValue).toBe(0.733);
@@ -552,7 +552,7 @@ describe("PreBarrage - ", () => {
         expect(c4.bassinAmont.uid).toBe("M3AxbT");
         expect(c4.bassinAval.uid).toBe("d2kxcD");
         expect(c4.structures.length).toBe(1);
-        expect(c4.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.RectangularOrificeSubmerged);
+        expect(c4.structures[0].getPropValue("loiDebit")).toBe(LoiDebit.RectangularOrificeSubmerged);
         const s4 = c4.structures[0].prms as RectangularStructureParams;
         expect(s4.ZDV.singleValue).toBe(101.44);
         expect(s4.L.singleValue).toBe(0.244);
@@ -563,7 +563,7 @@ describe("PreBarrage - ", () => {
         expect(c5.bassinAmont.uid).toBe("d2kxcD");
         expect(c5.bassinAval).toBeUndefined();
         expect(c5.structures.length).toBe(1);
-        expect(c5.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.GateCunge80);
+        expect(c5.structures[0].getPropValue("loiDebit")).toBe(LoiDebit.GateCunge80);
         const s5 = c5.structures[0].prms as RectangularStructureParams;
         expect(s5.ZDV.singleValue).toBe(101.55);
         expect(s5.L.singleValue).toBe(0.255);
diff --git a/spec/structure/functions.ts b/spec/structure/functions.ts
index 0d771331ad79f8d6cab3541309e084f09b399a42..e5c5d2a517bd05ee8ba273c044842611c25d30c9 100644
--- a/spec/structure/functions.ts
+++ b/spec/structure/functions.ts
@@ -158,8 +158,7 @@ export function testParallelStructures(o: { ps: ParallelStructure, ld: number[]
                         beforeEach(() => {
                             originalCalculatedValue = o.ps.calculatedParam.currentValue;
                             if ( // #136 Multiple solutions for GateCem88v ZDV
-                                !(o.ps.calculatedParam.parentNub.properties
-                                    .getPropValue("loiDebit") === LoiDebit.GateCem88v
+                                !(o.ps.calculatedParam.parentNub.getPropValue("loiDebit") === LoiDebit.GateCem88v
                                     && o.ps.calculatedParam.symbol === "ZDV")
                             ) {
                                 // altering value to force looking for the solution
diff --git a/src/devalaison/grille.ts b/src/devalaison/grille.ts
index adf140b643dc5b117425afac7fbe7b2acf0b6598..e03b505d276c82cf6feb726db5472f39385ba616 100644
--- a/src/devalaison/grille.ts
+++ b/src/devalaison/grille.ts
@@ -67,19 +67,19 @@ export class Grille extends Nub implements Observer {
     }
 
     public get gridType(): GrilleType {
-        return this.properties.getPropValue("gridType");
+        return this.getPropValue("gridType");
     }
 
     public get gridProfile(): GrilleProfile {
-        return this.properties.getPropValue("gridProfile");
+        return this.getPropValue("gridProfile");
     }
 
     public set type(type: GrilleType) {
-        this.properties.setPropValue("gridType", type);
+        this.setPropValue("gridType", type);
     }
 
     public set profile(profile: GrilleProfile) {
-        this.properties.setPropValue("gridProfile", profile);
+        this.setPropValue("gridProfile", profile);
     }
 
     /** Coefficient de forme des barreaux a */
diff --git a/src/index.ts b/src/index.ts
index 5b315a80c2b33fc880947344d0928c377cc30bbc..d6c10fa84e35bae89351d186fe3e8fd2004111a6 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -45,6 +45,7 @@ export * from "./structure/structure";
 export * from "./structure/structure_params";
 export * from "./structure/factory_structure";
 export * from "./structure/structure_props";
+export * from "./structure/rectangular_structure";
 export * from "./linked-value";
 export * from "./jalhyd_object";
 export * from "./date_revision";
@@ -61,6 +62,7 @@ export * from "./pab/pab_puissance";
 export * from "./pab/pab_puissance_params";
 export * from "./pab/cloison_aval";
 export * from "./pipe_flow/pl_lechaptcalmon";
+export * from "./pipe_flow/pl_lechaptcalmon_params";
 export * from "./pipe_flow/pressureloss";
 export * from "./pipe_flow/pressureloss_law";
 export * from "./lc-material";
diff --git a/src/internal_modules.ts b/src/internal_modules.ts
index 58b3a9bde01cce55a423aadcf09fbc9e1ae664db..427ef5383fe8ca738753ba3bda70f7d15b3dbaa9 100644
--- a/src/internal_modules.ts
+++ b/src/internal_modules.ts
@@ -10,7 +10,6 @@ export * from "./compute-node";
 export * from "./dichotomie";
 export * from "./nub";
 export * from "./child_nub";
-export * from "./props";
 export * from "./session_settings";
 export * from "./config";
 export * from "./param/param-domain";
@@ -166,5 +165,5 @@ export * from "./verification/espece_params";
 export * from "./verification/fish_species";
 export * from "./verification/verificateur";
 export * from "./verification/verificateur_params";
-
+export * from "./props";
 export * from "./session";
diff --git a/src/macrorugo/macrorugo.ts b/src/macrorugo/macrorugo.ts
index 196fbe4187b60ff8fbc6c7c51e8ba63f7f8ebcf7..db2cd017f3a232dcb056c4d2e430443870552730 100644
--- a/src/macrorugo/macrorugo.ts
+++ b/src/macrorugo/macrorugo.ts
@@ -101,7 +101,7 @@ export class MacroRugo extends FishPass {
             this.parent === undefined
             || (
                 this.parent instanceof MacrorugoCompound
-                && this.parent.properties.getPropValue("inclinedApron") === MRCInclination.NOT_INCLINED
+                && this.parent.getPropValue("inclinedApron") === MRCInclination.NOT_INCLINED
             )
         ) {
             const ax: number = this.prms.PBD.v / Math.sqrt(this.prms.C.v);
diff --git a/src/macrorugo/macrorugo_compound.ts b/src/macrorugo/macrorugo_compound.ts
index 7be71618f58b71da83beb687c51ee6a1959a4603..7744680b562b074a761868af23e028458c7b00b2 100644
--- a/src/macrorugo/macrorugo_compound.ts
+++ b/src/macrorugo/macrorugo_compound.ts
@@ -30,15 +30,15 @@ export class MacrorugoCompound extends MacroRugo implements Observer {
     }
 
     public get inclinedApron(): MRCInclination {
-        return this.properties.getPropValue("inclinedApron");
+        return this.getPropValue("inclinedApron");
     }
 
     public set inclinedApron(i: MRCInclination) {
-        this.properties.setPropValue("inclinedApron", i);
+        this.setPropValue("inclinedApron", i);
     }
 
     public CalcSerie(rInit?: number): Result {
-        if (this.properties.getPropValue("inclinedApron") === MRCInclination.INCLINED) {
+        if (this.getPropValue("inclinedApron") === MRCInclination.INCLINED) {
             // important to regenerate it here, at every iteration of CalcSerie()
             this.generateInclinedFishway();
         }
@@ -94,7 +94,7 @@ export class MacrorugoCompound extends MacroRugo implements Observer {
         this.currentResultElement = this.Equation(sVarCalc);
 
         // lateral inclination for inclined aprons
-        if (this.properties.getPropValue("inclinedApron") === MRCInclination.INCLINED) {
+        if (this.getPropValue("inclinedApron") === MRCInclination.INCLINED) {
             // extraResult : inclination
             this.result.resultElement.values.LIncl = (this.prms.ZRL.v - this.prms.ZRR.v) / this.prms.BR.v;
             // La largeur de la rampe inclinée est-elle adéquate par rapport à la largeur de motif ax ?
diff --git a/src/math/spp.ts b/src/math/spp.ts
index 598fa5b3bea70a4d6e0847ec8221e459c8ee5244..de128ce77aec989b39b711574e9bb2bf8454be36 100644
--- a/src/math/spp.ts
+++ b/src/math/spp.ts
@@ -31,11 +31,11 @@ export class SPP extends Nub {
     }
 
     public get operation(): SPPOperation {
-        return this.properties.getPropValue("sppOperation");
+        return this.getPropValue("sppOperation");
     }
 
     public set operation(o: SPPOperation) {
-        this.properties.setPropValue("sppOperation", o);
+        this.setPropValue("sppOperation", o);
     }
 
     /**
diff --git a/src/math/trigo.ts b/src/math/trigo.ts
index 4050e21c213332a009d2d2b8c8034c10400b939d..286169998997726711b76af3ba71777267f52ea6 100644
--- a/src/math/trigo.ts
+++ b/src/math/trigo.ts
@@ -30,7 +30,7 @@ export class Trigo extends Nub implements Observer {
         this.setCalculatorType(CalculatorType.Trigo);
         this._defaultCalculatedParam = prms.Y;
         this.resetDefaultCalculatedParam();
-        this.properties.addObserver(this);
+        this._props.addObserver(this);
         this.operation = TrigoOperation.COS;
         this.unit = TrigoUnit.DEG;
     }
@@ -41,19 +41,19 @@ export class Trigo extends Nub implements Observer {
     }
 
     public get operation(): TrigoOperation {
-        return this.properties.getPropValue("trigoOperation");
+        return this.getPropValue("trigoOperation");
     }
 
     public set operation(o: TrigoOperation) {
-        this.properties.setPropValue("trigoOperation", o);
+        this.setPropValue("trigoOperation", o);
     }
 
     public get unit(): TrigoUnit {
-        return this.properties.getPropValue("trigoUnit");
+        return this.getPropValue("trigoUnit");
     }
 
     public set unit(u: TrigoUnit) {
-        this.properties.setPropValue("trigoUnit", u);
+        this.setPropValue("trigoUnit", u);
     }
 
     public Equation(sVarCalc: string): Result {
diff --git a/src/nub.ts b/src/nub.ts
index fe913bdaf00358a14d90de0b123639342a4d31ba..e40d9c23efb7f1d431568cf3ff608f3948c9ded7 100644
--- a/src/nub.ts
+++ b/src/nub.ts
@@ -1,4 +1,4 @@
-import { CalculatorType, ComputeNode } from "./internal_modules";
+import { CalculatorType, ComputeNode, IProperties } from "./internal_modules";
 import { Dichotomie } from "./internal_modules";
 import {
     acSection, MacrorugoCompound, Pab, ParamDefinition, ParamsEquation,
@@ -12,7 +12,7 @@ import { ParamsEquationArrayIterator } from "./internal_modules";
 import { Props } from "./internal_modules";
 import { SessionSettings } from "./internal_modules";
 import { Message, MessageCode } from "./internal_modules";
-import { IObservable, Observable, Observer } from "./internal_modules";
+import { Observable, Observer } from "./internal_modules";
 import { Result } from "./internal_modules";
 import { ResultElement } from "./internal_modules";
 import { VariatedDetails } from "./internal_modules";
@@ -21,7 +21,7 @@ import { VariatedDetails } from "./internal_modules";
  * Classe abstraite de Noeud de calcul dans une session :
  * classe de base pour tous les calculs
  */
-export abstract class Nub extends ComputeNode implements IObservable {
+export abstract class Nub extends ComputeNode implements IProperties {
 
     /**
      * Find longest series, BUT: if any varying parameter is a calculation result,
@@ -103,17 +103,6 @@ export abstract class Nub extends ComputeNode implements IObservable {
         return this._result;
     }
 
-    private _calcType: CalculatorType;
-
-    /**
-     * set Nub calculator type.
-     * give children the opportunity to react to assignment
-     * @see Structure
-     */
-    protected setCalculatorType(ct: CalculatorType) {
-        this._calcType = ct;
-    }
-
     /**
      * Local setter to set result element of Equation() / Solve() / …  as current
      * ResultElement, instead of overwriting the whole Result object
@@ -126,13 +115,6 @@ export abstract class Nub extends ComputeNode implements IObservable {
         this._result.resultElement = r.resultElement;
     }
 
-    /** Returns Props object (observable set of key-values) associated to this Nub */
-    public get properties(): Props {
-        // completes props with calcType if not already set
-        this._props.setPropValue("calcType", this.calcType);
-        return this._props;
-    }
-
     public set properties(props: Props) {
         this.setProperties(props);
     }
@@ -204,7 +186,16 @@ export abstract class Nub extends ComputeNode implements IObservable {
     }
 
     public get calcType(): CalculatorType {
-        return this._calcType;
+        return this.getPropValue("calcType");
+    }
+
+    /**
+     * set Nub calculator type.
+     * give children the opportunity to react to assignment
+     * @see Structure
+     */
+    protected setCalculatorType(ct: CalculatorType) {
+        this.setPropValue("calcType", ct);
     }
 
     public get calculatedParam(): ParamDefinition {
@@ -275,7 +266,7 @@ export abstract class Nub extends ComputeNode implements IObservable {
     }
 
     // move code out of setter to ease inheritance
-    public setProperties(props: Props, resetProps: boolean = false) {
+    public setProperties(props: IProperties, resetProps: boolean = false) {
         // copy observers
         const observers = this._props.getObservers();
         // empty props
@@ -288,18 +279,18 @@ export abstract class Nub extends ComputeNode implements IObservable {
         }
         // set new props values
         let error: Error;
-        for (const p of Object.keys(props.props)) {
+        for (const p of props.keys) {
             // try properties one by one so that if an error is thrown,
             // remaining properties are still copied
             let oldValue: any;
             try {
                 // keep old value to restore it in case of an error, or else it breaks fixTargets()
-                oldValue = this._props.getPropValue(p); // should always be undefined but who knows
-                this._props.setPropValue(p, props.getPropValue(p));
+                oldValue = this.getPropValue(p); // should always be undefined but who knows
+                this.setPropValue(p, props.getPropValue(p));
             } catch (e) {
                 error = e;
                 // no one will notice ^^
-                this._props.setPropValue(p, oldValue);
+                this.setPropValue(p, oldValue);
             }
         }
         // throw caught error if any
@@ -745,10 +736,6 @@ export abstract class Nub extends ComputeNode implements IObservable {
         return this._children;
     }
 
-    public getParent(): Nub {
-        return this.parent;
-    }
-
     /**
      * @returns iterator on all child nubs (may include extra nubs, see Pab nub)
      */
@@ -794,7 +781,7 @@ export abstract class Nub extends ComputeNode implements IObservable {
             !this.isParentOrChildOf(src.nubUid)
             && ( // check grand-parent for PreBarrage special case @TODO find a generic way
                 // to perform both tests (synthesise .nubUid and .originNub.uid)
-                !(this._calcType === CalculatorType.PreBarrage)
+                !(this.calcType === CalculatorType.PreBarrage)
                 || !this.isParentOrChildOf(src.originNub.uid)
             )
         ) {
@@ -893,7 +880,7 @@ export abstract class Nub extends ComputeNode implements IObservable {
                     && src.symbol === "QA"
                 )
             )
-            && this._calcType !== CalculatorType.PreBarrage
+            && this.calcType !== CalculatorType.PreBarrage
         ) {
             for (const cn of this.getChildren()) {
                 res = res.concat(cn.getLinkableValues(src));
@@ -913,8 +900,7 @@ export abstract class Nub extends ComputeNode implements IObservable {
         if (this.uid === uid) {
             return true;
         }
-        const parent = this.getParent();
-        if (parent && parent.uid === uid) {
+        if (this._parent && this._parent.uid === uid) {
             return true;
         }
         for (const c of this.getChildren()) {
@@ -934,9 +920,8 @@ export abstract class Nub extends ComputeNode implements IObservable {
         if (this.uid === uid) {
             return true;
         }
-        const parent = this.getParent();
-        if (parent) {
-            for (const c of parent.getChildren()) {
+        if (this._parent) {
+            for (const c of this._parent.getChildren()) {
                 if (c.uid === uid) {
                     return true;
                 }
@@ -1047,9 +1032,8 @@ export abstract class Nub extends ComputeNode implements IObservable {
      */
     public dependsOnNubResult(nub: Nub, followLinksChain: boolean = true): boolean {
         let thisFamily: Nub[] = [this];
-        const tp = this.getParent();
-        if (tp) {
-            thisFamily = thisFamily.concat(tp);
+        if (this._parent) {
+            thisFamily = thisFamily.concat(this._parent);
         }
         thisFamily = thisFamily.concat(this.getChildren());
 
@@ -1085,9 +1069,8 @@ export abstract class Nub extends ComputeNode implements IObservable {
      */
     public dependsOnParameter(src: ParamDefinition) {
         let thisFamily: Nub[] = [this];
-        const tp = this.getParent();
-        if (tp) {
-            thisFamily = thisFamily.concat(tp);
+        if (this._parent) {
+            thisFamily = thisFamily.concat(this._parent);
         }
         thisFamily = thisFamily.concat(this.getChildren());
 
@@ -1151,9 +1134,8 @@ export abstract class Nub extends ComputeNode implements IObservable {
                 }
             }
             // does any of our parent's parameters depend on the target Nub ?
-            const parent = this.getParent();
-            if (parent) {
-                if (parent.resultDependsOnNub(uid, visited, symbol, includeValuesLinks, includeOtherDependencies)) {
+            if (this._parent) {
+                if (this._parent.resultDependsOnNub(uid, visited, symbol, includeValuesLinks, includeOtherDependencies)) {
                     return true;
                 }
             }
@@ -1266,7 +1248,7 @@ export abstract class Nub extends ComputeNode implements IObservable {
     public objectRepresentation(extra?: object, nubUidsInSession?: string[]): object {
         let ret: any = {
             uid: this.uid,
-            props: Session.invertEnumKeysAndValuesInProperties(this.properties.props),
+            props: this.invertedPropertiesEnumAndValues(),
         };
 
         if (extra) {
@@ -1330,7 +1312,7 @@ export abstract class Nub extends ComputeNode implements IObservable {
         if (obj.children && Array.isArray(obj.children)) {
             for (const s of obj.children) {
                 // decode properties
-                const props = Session.invertEnumKeysAndValuesInProperties(s.props, true);
+                const props = Props.invertEnumKeysAndValuesInProperties(s.props, true);
                 // create the Nub
                 const subNub = Session.getInstance().createNub(new Props(props), this);
                 // try to keep the original ID
@@ -1845,4 +1827,40 @@ export abstract class Nub extends ComputeNode implements IObservable {
     //     }
     //     return [];
     // }
+
+    public invertedPropertiesEnumAndValues(forceNumbers: boolean = false) {
+        return this._props.invertEnumKeysAndValues(forceNumbers);
+    }
+
+    public addPropertiesObserver(o: Observer) {
+        this._props.addObserver(o);
+    }
+
+    protected childHasProperty(key: string, nth?: number): boolean {
+        if (this._children.length > 0) {
+            const n = nth ?? 0;
+            if (n < this._children.length) {
+                return this._children[n].hasProperty(key);
+            }
+        }
+        return false;
+    }
+
+    // interface IProperties
+
+    public hasProperty(key: string): boolean {
+        return true;
+    }
+
+    public get keys(): string[] {
+        return this._props.keys;
+    }
+
+    public getPropValue(key: string): any {
+        return this._props.getPropValue(key);
+    }
+
+    public setPropValue(key: string, val: any, sender?: any): boolean {
+        return this._props.setPropValue(key, val, sender);
+    }
 }
diff --git a/src/open-channel/bief.ts b/src/open-channel/bief.ts
index c449b55c2a5829459f4f052254091298ad3ab686..9448017779fb6416ef373a581fc361ba0fb36c4d 100644
--- a/src/open-channel/bief.ts
+++ b/src/open-channel/bief.ts
@@ -40,7 +40,7 @@ export class Bief extends SectionNub implements Observer {
     }
 
     public set regime(regime: BiefRegime) {
-        this.properties.setPropValue("regime", regime);
+        this.setPropValue("regime", regime);
     }
 
     public Calc(sVarCalc?: string, rInit?: number): Result {
@@ -74,13 +74,13 @@ export class Bief extends SectionNub implements Observer {
 
         if (
             this.calculatedParam === this.prms.Z1
-            && this.properties.getPropValue("regime") === BiefRegime.Torrentiel
+            && this.getPropValue("regime") === BiefRegime.Torrentiel
         ) {
             throw new Error("Bief.Equation() : cannot calculate Z1 in Torrential regime");
         }
         if (
             this.calculatedParam === this.prms.Z2
-            && this.properties.getPropValue("regime") === BiefRegime.Fluvial
+            && this.getPropValue("regime") === BiefRegime.Fluvial
         ) {
             throw new Error("Bief.Equation() : cannot calculate Z2 in Fluvial regime");
         }
diff --git a/src/open-channel/remous.ts b/src/open-channel/remous.ts
index 0c043b268252ee5ae5f23b9971a18b77b36c3314..7c20f500f0309008480d50467846ba04980f0b17 100644
--- a/src/open-channel/remous.ts
+++ b/src/open-channel/remous.ts
@@ -46,11 +46,11 @@ export class CourbeRemous extends SectionNub {
     }
 
     public get methodeResolution(): MethodeResolution {
-        return this.properties.getPropValue("methodeResolution");
+        return this.getPropValue("methodeResolution");
     }
 
     public set methodeResolution(m: MethodeResolution) {
-        this.properties.setPropValue("methodeResolution", m);
+        this.setPropValue("methodeResolution", m);
     }
 
     public setSection(s: acSection) {
@@ -528,7 +528,7 @@ export class CourbeRemous extends SectionNub {
      * @param sDonnee éventuel symbole / paire symbole-uid du paramètre à calculer
      */
     public CalcSerie(rInit?: number): Result {
-        const varCalc = this.properties.getPropValue("varCalc");
+        const varCalc = this.getPropValue("varCalc");
         const res = this.calculRemous(varCalc);
         return res;
     }
@@ -834,7 +834,7 @@ export class CourbeRemous extends SectionNub {
         // let funcCalcY = 'Calc_Y_' + Resolution;
         // return this[funcCalcY](Y);
         let res: Result;
-        const methodeResolution: MethodeResolution = this.properties.getPropValue("methodeResolution");
+        const methodeResolution: MethodeResolution = this.getPropValue("methodeResolution");
         switch (methodeResolution) {
             case MethodeResolution.Trapezes:
                 res = this.Calc_Y_Trapez(Y);
diff --git a/src/open-channel/section/section_circulaire.ts b/src/open-channel/section/section_circulaire.ts
index c96857fc761b332d7cb262699f8e1ac37674c97b..a04d2353ecc52bfa9a070ffc4d1a899e29ec4d86 100644
--- a/src/open-channel/section/section_circulaire.ts
+++ b/src/open-channel/section/section_circulaire.ts
@@ -18,7 +18,7 @@ export class cSnCirc extends acSection {
 
     constructor(prms: ParamsSectionCirc, dbg: boolean = false) {
         super(prms, dbg);
-        this._nodeType = SectionType.SectionCercle;
+        this.nodeType = SectionType.SectionCercle;
         // commenté car si D est la variable à déterminer, il peut valoir n'importe
         // quoi... if (prms.YB.v > D) { prms.YB.v = D; } // On place la berge au sommet du cercle
 
diff --git a/src/open-channel/section/section_nub.ts b/src/open-channel/section/section_nub.ts
index bc4dffc5be5ffdfebdd5e4ea9364aa04854bb8e8..f33df5aacd1a94d32a4bfd7baedbca0a04d62d49 100644
--- a/src/open-channel/section/section_nub.ts
+++ b/src/open-channel/section/section_nub.ts
@@ -27,18 +27,6 @@ export abstract class SectionNub extends Nub {
         this._sectionVars = {};
     }
 
-    /** Returns Props object (observable set of key-values) associated to this Nub */
-    public get properties(): Props {
-        // completes props with calcType if not already set
-        this._props.setPropValue("calcType", this.calcType);
-        return this._props;
-    }
-
-    // setter is not inherited from Nub if getter is redefined :/
-    public set properties(props: Props) {
-        super.setProperties(props);
-    }
-
     public getParameter(name: string): ParamDefinition {
         if (typeof name !== "string") {
             // dirty hack because calculated param descriptor for section params is an object { uid: , symbol: }
@@ -104,10 +92,27 @@ export abstract class SectionNub extends Nub {
                 this._children[0] = undefined;
             }
             this.replaceChild(0, s);
+            this._props.removeProp("nodeType"); // because this property belongs to child section and is set as long as section is not defined
             /**
              * Y linkability managed in child classes through 'visible' flag
              * @see Bief,CourbeRemous
              */
         }
     }
+
+    // interface IProperties
+
+    public getPropValue(key: string): any {
+        if (this.childHasProperty(key)) {
+            return this.section.getPropValue(key);
+        }
+        return this._props.getPropValue(key);
+    }
+
+    public setPropValue(key: string, val: any, sender?: any): boolean {
+        if (this.childHasProperty(key)) {
+            return this.section.setPropValue(key, val, sender);
+        }
+        return this._props.setPropValue(key, val, sender);
+    }
 }
diff --git a/src/open-channel/section/section_puissance.ts b/src/open-channel/section/section_puissance.ts
index c54eda30758ed032abe09832b00448e09ae3f168..3e73cc2d0af0722d722f9e4b5a877e3b25f3682f 100644
--- a/src/open-channel/section/section_puissance.ts
+++ b/src/open-channel/section/section_puissance.ts
@@ -13,7 +13,7 @@ export class cSnPuiss extends acSection {
 
     constructor(prms: ParamsSectionPuiss, dbg: boolean = false) {
         super(prms, dbg);
-        this._nodeType = SectionType.SectionPuissance;
+        this.nodeType = SectionType.SectionPuissance;
     }
 
     protected setParametersCalculability() {
diff --git a/src/open-channel/section/section_rectang.ts b/src/open-channel/section/section_rectang.ts
index f801ad556fad44e97f2995f578cb0315b13d8731..83d89747d4019e78ea60cd1a68a4f62d2b9cb6bc 100644
--- a/src/open-channel/section/section_rectang.ts
+++ b/src/open-channel/section/section_rectang.ts
@@ -10,7 +10,7 @@ import { acSection } from "../../internal_modules";
 export class cSnRectang extends acSection {
     constructor(prms: ParamsSectionRectang, dbg: boolean = false) {
         super(prms, dbg);
-        this._nodeType = SectionType.SectionRectangle;
+        this.nodeType = SectionType.SectionRectangle;
     }
 
     get prms(): ParamsSectionRectang {
diff --git a/src/open-channel/section/section_trapez.ts b/src/open-channel/section/section_trapez.ts
index 578842f06e6862de5f099683acc95ecdc31443d6..8dfd4fb4d8b5359a92e818c00bc73c70380f468e 100644
--- a/src/open-channel/section/section_trapez.ts
+++ b/src/open-channel/section/section_trapez.ts
@@ -15,7 +15,7 @@ export class cSnTrapez extends acSection {
     }
     constructor(prms: ParamsSectionTrapez, dbg: boolean = false) {
         super(prms, dbg);
-        this._nodeType = SectionType.SectionTrapeze;
+        this.nodeType = SectionType.SectionTrapeze;
     }
 
     protected setParametersCalculability() {
diff --git a/src/open-channel/section/section_type.ts b/src/open-channel/section/section_type.ts
index 8c7b1983d2d4a45df395f35e1d514f8a4c719783..ae0e8c6e523446807c58a50103a55845809e03ec 100644
--- a/src/open-channel/section/section_type.ts
+++ b/src/open-channel/section/section_type.ts
@@ -52,8 +52,6 @@ export abstract class acSection extends Nub {
     protected bSnFermee: boolean = false;
     protected arCalcGeo: { [key: string]: number } = {}; /// Données ne dépendant pas de la cote de l'eau
 
-    protected _nodeType: SectionType;
-
     private _hautCritique: Result;  // Tirant d'eau critique
 
     /**
@@ -99,22 +97,11 @@ export abstract class acSection extends Nub {
     }
 
     public get nodeType() {
-        return this._nodeType;
-    }
-
-    /** Returns Props object (observable set of key-values) associated to this Nub */
-    public get properties(): Props {
-        // completes props with calcType and nodeType if not already set
-        this._props.setPropValue("calcType", this.calcType);
-        if (this._props.getPropValue("nodeType") === undefined) {
-            this._props.setPropValue("nodeType", this.nodeType);
-        }
-        return this._props;
+        return this._props.getPropValue("nodeType");
     }
 
-    // setter is not inherited from Nub if getter is redefined :/
-    public set properties(props: Props) {
-        super.setProperties(props);
+    protected set nodeType(nt: SectionType) {
+        this._props.setPropValue("nodeType", nt);
     }
 
     /**
@@ -828,4 +815,10 @@ export abstract class acSection extends Nub {
         const v = 1000 * (this.prms.Q.v * rV.vCalc + ParamsSection.G * rSYG.vCalc);
         return new Result(v);
     }
+
+    // interface IProperties
+
+    public hasProperty(key: string): boolean {
+        return key === "nodeType";
+    }
 }
diff --git a/src/pab/cloison_aval.ts b/src/pab/cloison_aval.ts
index 16d4cced82cc63e25064dd839d428f1dcee7fcdb..39c48c4a489c53a0cbe52efaee8a5eedabe10b02 100644
--- a/src/pab/cloison_aval.ts
+++ b/src/pab/cloison_aval.ts
@@ -22,7 +22,7 @@ export class CloisonAval extends ParallelStructure {
     public get indexVanneLevante(): number {
         for (let i = 0; i < this.structures.length; i++) {
             if (loiAdmissiblesCloisonAval.VanneLevante.includes(
-                this.structures[i].properties.getPropValue("loiDebit"))
+                this.structures[i].getPropValue("loiDebit"))
             ) {
                 return i;
             }
@@ -119,7 +119,7 @@ export class CloisonAval extends ParallelStructure {
     public checkVanneLevante() {
         let n: number = 0;
         for (const st of this.structures) {
-            if (loiAdmissiblesCloisonAval.VanneLevante.includes(st.properties.getPropValue("loiDebit"))) {
+            if (loiAdmissiblesCloisonAval.VanneLevante.includes(st.getPropValue("loiDebit"))) {
                 n += 1;
             }
         }
diff --git a/src/pab/cloisons.ts b/src/pab/cloisons.ts
index f113d453571277d5e6aa5596d37890a56303845b..88a036fd7e75064ad62a683cfab6af5c448c7c5d 100644
--- a/src/pab/cloisons.ts
+++ b/src/pab/cloisons.ts
@@ -88,7 +88,7 @@ export class Cloisons extends ParallelStructure {
                 s.result.resultElement.addExtraResult("ZDV", this.prms.Z1.v - s.prms.h1.v);
             }
             // calcul de la pelle
-            if (s.properties.getPropValue("loiDebit") !== LoiDebit.OrificeSubmerged) {
+            if (s.getPropValue("loiDebit") !== LoiDebit.OrificeSubmerged) {
                 const pelle = s.prms.ZDV.v - this.prms.ZRAM.v;
                 s.result.resultElement.values.P = pelle;
                 if (pelle < -1E-7) { // si c'est enfoncé d'un dixième de micron ça va
diff --git a/src/pab/pab.ts b/src/pab/pab.ts
index da52306f500de759a6852683bd0d44b370d3905c..f197cd5425da7a0c41eb3b898d00760de0a54db7 100644
--- a/src/pab/pab.ts
+++ b/src/pab/pab.ts
@@ -292,7 +292,7 @@ export class Pab extends FishPass {
         // load downwall if any
         if (obj.downWall) {
             // decode properties
-            const props = Session.invertEnumKeysAndValuesInProperties(obj.downWall.props, true);
+            const props = Props.invertEnumKeysAndValuesInProperties(obj.downWall.props, true);
             // create the Nub
             const dw = Session.getInstance().createNub(new Props(props), this) as CloisonAval;
             // try to keep the original ID
diff --git a/src/par/par.ts b/src/par/par.ts
index 509575092e1b23454bc4a88e3547b81d298c203c..9821ec1414e947ca80d76932421eb28322a98277 100644
--- a/src/par/par.ts
+++ b/src/par/par.ts
@@ -61,11 +61,11 @@ export class Par extends FishPass implements Observer {
     }
 
     public get parType(): ParType {
-        return this.properties.getPropValue("parType");
+        return this.getPropValue("parType");
     }
 
     public set parType(e: ParType) {
-        this.properties.setPropValue("parType", e);
+        this.setPropValue("parType", e);
     }
 
     public Calc(sVarCalc: string, rInit?: number): Result {
diff --git a/src/par/par_simulation.ts b/src/par/par_simulation.ts
index 39fb57b6fff5bd16946113e8bce79c58ffdd3d16..773107687ca74f729bef2541c08cdd4deb5ec2bd 100644
--- a/src/par/par_simulation.ts
+++ b/src/par/par_simulation.ts
@@ -28,11 +28,11 @@ export class ParSimulation extends Par implements Observer {
     }
 
     public get parType(): ParType {
-        return this.properties.getPropValue("parType");
+        return this.getPropValue("parType");
     }
 
     public set parType(e: ParType) {
-        this.properties.setPropValue("parType", e);
+        this.setPropValue("parType", e);
     }
 
     public Calc(sVarCalc: string, rInit?: number): Result {
diff --git a/src/param/param-definition.ts b/src/param/param-definition.ts
index 18907050c89a3433d5d80df9c62528529c604462..e34e7a31ecb7a17ef62496c07c1f29db297fb90e 100644
--- a/src/param/param-definition.ts
+++ b/src/param/param-definition.ts
@@ -1061,7 +1061,7 @@ export class ParamDefinition implements INamedIterableValues, IObservable {
             // direct, parent or children reference ?
             if (
                 (ref.nub.uid === uid)
-                || (ref.nub.getParent() && ref.nub.getParent().uid === uid)
+                || (ref.nub.parent && ref.nub.parent.uid === uid)
                 || (ref.nub.getChildren().map((c) => c.uid).includes(uid))
             ) {
                 linked = (
diff --git a/src/pipe_flow/pl_lechaptcalmon.ts b/src/pipe_flow/pl_lechaptcalmon.ts
index cc7d9a13ac81c1d8cf28251050f740f818dd574d..bd649cfa01d8baaf7b9d108c343ee8aa11c3d93a 100644
--- a/src/pipe_flow/pl_lechaptcalmon.ts
+++ b/src/pipe_flow/pl_lechaptcalmon.ts
@@ -1,4 +1,4 @@
-import { CalculatorType } from "../internal_modules";
+import { CalculatorType, PressureLossParams } from "../internal_modules";
 import { LCMaterial } from "../internal_modules";
 import { ParamCalculability } from "../internal_modules";
 import { Message, MessageCode } from "../internal_modules";
@@ -72,10 +72,9 @@ export class PL_LechaptCalmon extends PressureLossLaw implements Observer {
     constructor(prms: PL_LechaptCalmonParams, dbg: boolean = false) {
         super(prms, dbg);
         this.setCalculatorType(CalculatorType.LechaptCalmon);
-        this._pressureLossType = PressureLossType.LechaptCalmon;
+        this.pressureLossType = PressureLossType.LechaptCalmon;
         this._props.addObserver(this);
         this.material = LCMaterial.PVCPolyethylene;
-        this.calculatedParam = prms.J;
         this._intlType = "LechaptCalmon";
     }
 
@@ -84,18 +83,35 @@ export class PL_LechaptCalmon extends PressureLossLaw implements Observer {
     }
 
     public get material(): LCMaterial {
-        return this.properties.getPropValue("material");
+        return this.getPropValue("material");
     }
 
     public set material(m: LCMaterial) {
-        this.properties.setPropValue("material", m);
+        this.setPropValue("material", m);
     }
 
-    protected calc_Jlin(r: Result) {
+    public Equation(sVarCalc: string): Result {
+        if (sVarCalc !== undefined && sVarCalc !== "Jlin") {
+            throw new Error("PL_LechaptCalmon.Equation() : invalid variable name " + sVarCalc);
+        }
+        const r: Result = this._result;
+
+        // récupération des paramètres Q,D et Lg dans le parent
+        const parentPrms = this.parent.prms as PressureLossParams;
+        const Q = parentPrms.Q.v;
+        const D = parentPrms.D.v;
+        const Lg = parentPrms.Lg.v;
+
+        // Calcul de la perte de charge linéaire spécifique à chaque module de perte de charge.
+        r.values.Jlin = this.prms.L.v * Math.pow(Q, this.prms.M.v) / Math.pow(D, this.prms.N.v) * (Lg / 1000);
+
+        return r;
+    }
+
+    public finalChecks(r: Result) {
         if (r.values.V < 0.4 || r.values.V > 2) {
             r.resultElement.log.add(new Message(MessageCode.WARNING_LECHAPT_CALMON_SPEED_OUTSIDE_04_2));
         }
-        r.values.Jl = this.prms.L.v * Math.pow(this.prms.Q.v, this.prms.M.v) / Math.pow(this.prms.D.v, this.prms.N.v) * (this.prms.Lg.v / 1000);
     }
 
     /**
@@ -105,14 +121,6 @@ export class PL_LechaptCalmon extends PressureLossLaw implements Observer {
         return this._prms as PL_LechaptCalmonParams;
     }
 
-    public Calc(sVarCalc: string, rInit?: number): Result {
-        const r = super.Calc(sVarCalc, rInit);
-        const V2 = Math.pow(r.values.V, 2);
-        r.values.Kl = 19.62 * r.values.Jl / V2;
-        r.values.fD = this.prms.J.V / this.prms.Lg.V / V2 * 19.62 * this.prms.D.V;
-        return r;
-    }
-
     // interface Observer
 
     public update(sender: any, data: any) {
@@ -125,7 +133,6 @@ export class PL_LechaptCalmon extends PressureLossLaw implements Observer {
      * paramétrage de la calculabilité des paramètres
      */
     protected setParametersCalculability() {
-        super.setParametersCalculability();
         this.prms.L.calculability = ParamCalculability.FIXED;
         this.prms.M.calculability = ParamCalculability.FIXED;
         this.prms.N.calculability = ParamCalculability.FIXED;
@@ -133,10 +140,10 @@ export class PL_LechaptCalmon extends PressureLossLaw implements Observer {
 
     protected exposeResults() {
         this._resultsFamilies = {
-            Kl: undefined,
+            Klin: undefined,
             fD: undefined,
             V: undefined,
-            Jl: undefined
+            Jlin: undefined
         };
     }
 
@@ -145,10 +152,20 @@ export class PL_LechaptCalmon extends PressureLossLaw implements Observer {
      * according to this._materials presets
      */
     private applyMaterialPreset() {
-        const m = this.properties.getPropValue("material");
+        const m = this.getPropValue("material");
         const values = PL_LechaptCalmon._materials[m];
         this.prms.L.singleValue = values.L;
         this.prms.M.singleValue = values.M;
         this.prms.N.singleValue = values.N;
     }
+
+    // interface IProperties
+
+    public hasProperty(key: string): boolean {
+        if (super.hasProperty(key)) {
+            return true;
+        }
+
+        return key === "material";
+    }
 }
diff --git a/src/pipe_flow/pl_lechaptcalmon_params.ts b/src/pipe_flow/pl_lechaptcalmon_params.ts
index b23fba86ddb98287786102fdb83366f607b0394f..040984826f438337effe11d403dcb9286bbdda0f 100644
--- a/src/pipe_flow/pl_lechaptcalmon_params.ts
+++ b/src/pipe_flow/pl_lechaptcalmon_params.ts
@@ -16,8 +16,8 @@ export class PL_LechaptCalmonParams extends PressureLossLawParams {
     /** Paramètre de rugosité N */
     private _N: ParamDefinition;
 
-    constructor(rQ: number, rD: number, rJ: number, rLg: number, rKloc: number, rL: number, rM: number, rN: number, nullParams: boolean = false) {
-        super(rQ, rD, rJ, rLg, rKloc, nullParams);
+    constructor(rL: number, rM: number, rN: number, nullParams: boolean = false) {
+        super();
         this._L = new ParamDefinition(this, "L", new ParamDomain(ParamDomainValue.INTERVAL, 0.8, 2), undefined, rL,
             undefined, false, nullParams);
         this._M = new ParamDefinition(this, "M", new ParamDomain(ParamDomainValue.INTERVAL, 1.5, 2.5), undefined, rM,
diff --git a/src/pipe_flow/pressureloss.ts b/src/pipe_flow/pressureloss.ts
index 53580105c5bb91de481ce4d63f5a9d7865689219..0f5b095c5d25400d15830a38704f9559e452aa99 100644
--- a/src/pipe_flow/pressureloss.ts
+++ b/src/pipe_flow/pressureloss.ts
@@ -1,63 +1,133 @@
-import { Nub } from "../internal_modules";
+import { Nub, ParamCalculability } from "../internal_modules";
 import { Observer } from "../internal_modules";
 import { Result } from "../internal_modules";
 import { CalculatorType } from "../internal_modules";
 import { PressureLossParams } from "../internal_modules";
 import { PressureLossLaw } from "../internal_modules";
-import { PressureLossLawParams } from "../internal_modules";
 
 export class PressureLoss extends Nub {
 
-    constructor(law: PressureLossLaw, dbg: boolean = false) {
-        super(new PressureLossParams(), dbg);
+    /**
+     * { symbol => string } map that defines units for extra results
+     */
+    private static _resultsUnits = {
+        Q: "m³/s",
+        D: "m",
+        J: "m/m",
+        Lg: "m"
+    };
+
+    constructor(prms: PressureLossParams, law: PressureLossLaw, dbg: boolean = false) {
+        super(prms, dbg);
         this.setCalculatorType(CalculatorType.PressureLoss);
-        this.addChild(law);
+        this.calculatedParam = prms.J;
+        this.setLaw(law);
     }
 
     /**
      * paramètres castés au bon type
      */
-    get prms(): PressureLossLawParams {
-        return this.child.prms as PressureLossLawParams;
+    get prms(): PressureLossParams {
+        return this._prms as PressureLossParams;
     }
 
-    private get child(): PressureLossLaw {
+    public get child(): PressureLossLaw {
         return this._children[0] as PressureLossLaw;
     }
 
+    /**
+     * Set/replaces the current section
+     */
+    public setLaw(pll: PressureLossLaw) {
+        if (pll) {
+            // prevent index out of bounds at first time
+            if (this._children.length === 0) {
+                this._children[0] = undefined;
+            }
+            this.replaceChild(0, pll);
+            this._props.removeProp("pressureLossType"); // because this property belongs to child pressure loss law and is set as long as law is not defined
+            /**
+             * Y linkability managed in child classes through 'visible' flag
+             * @see Bief,CourbeRemous
+             */
+        }
+    }
     /**
      * paramétrage de la calculabilité des paramètres
      */
     protected setParametersCalculability() {
-    }
-
-    public CalcSerie(rInit?: number, resetDepending: boolean = true): Result {
-        return this.child.CalcSerie(rInit, resetDepending);
+        this.prms.Q.calculability = ParamCalculability.DICHO;
+        this.prms.D.calculability = ParamCalculability.DICHO;
+        this.prms.J.calculability = ParamCalculability.EQUATION;
+        this.prms.Lg.calculability = ParamCalculability.DICHO;
+        this.prms.Kloc.calculability = ParamCalculability.DICHO;
     }
 
     public Calc(sVarCalc: string, rInit?: number): Result {
-        return this.child.Calc(sVarCalc, rInit);
+        const r: Result = super.Calc(sVarCalc, rInit);
+
+        const V2 = Math.pow(r.values.V, 2);
+        const Jlin = this.child.result.values.Jlin;
+        r.values.Klin = 19.62 * Jlin / V2;
+        r.values.fD = this.prms.J.V / this.prms.Lg.V / V2 * 19.62 * this.prms.D.V;
+
+        this.child.finalChecks(r);
+
+        return r;
     }
 
     public Equation(sVarCalc: string): Result {
-        return this.child.Equation(sVarCalc);
+        if (sVarCalc !== "J") {
+            throw new Error("PressureLoss.Equation() : invalid variable name " + sVarCalc);
+        }
+
+        if (this._result === undefined) {
+            this.initNewResultElement();
+        }
+
+        const r: Result = this._result;
+
+        // vitesse
+        r.values.V = this.prms.Q.v / (Math.PI * Math.pow(this.prms.D.v / 2, 2));
+
+        // perte de charge linéaire propre à chaque module
+        const rc: Result = this.child.Equation("Jlin");
+        if (!rc.ok) {
+            r.addLog(rc.log);
+            return r;
+        }
+
+        // perte de charge totale (J)
+        r.vCalc = rc.values.Jlin + this.prms.Kloc.v / 19.62 * Math.pow(r.values.V, 2);
+
+        return r;
     }
 
     public get result(): Result {
-        return this.child.result;
+        return this._result;  // redéfini (à l'identique) car on a redéfini le setter (et un setter seul fait que le getter n'est pas hérité)
     }
 
     public set result(r: Result) {
         throw new Error("PressureLoss.set result() called!");
     }
 
-    // interface IObservable
+    public static override resultsUnits() {
+        return PressureLoss._resultsUnits;
+    }
+
+    // interface IProperties
 
-    public addObserver(o: Observer) {
-        this.child.addObserver(o);
+    public getPropValue(key: string): any {
+        if (this.childHasProperty(key)) {
+            return this.child.getPropValue(key);
+        }
+        return this._props.getPropValue(key);
     }
 
-    public removeObserver(o: Observer) {
-        this.child.removeObserver(o);
+    public setPropValue(key: string, val: any, sender?: any): boolean {
+        if (this.childHasProperty(key)) {
+            return this.child.setPropValue(key, val, sender);
+        }
+        return this._props.setPropValue(key, val, sender);
     }
 }
diff --git a/src/pipe_flow/pressureloss_law.ts b/src/pipe_flow/pressureloss_law.ts
index 63cfd77d220a66b80bc75af03fde57f2ebd67d8f..ccd36b34388b06ed7032c60831b289432073ea68 100644
--- a/src/pipe_flow/pressureloss_law.ts
+++ b/src/pipe_flow/pressureloss_law.ts
@@ -1,6 +1,5 @@
-import { Nub, ParamCalculability, PressureLossLawParams } from "../internal_modules";
+import { CalculatorType, Nub, PressureLossLawParams } from "../internal_modules";
 import { Result } from "../internal_modules";
-import { Message, MessageCode } from "../internal_modules";
 
 /**
  * Lois de perte de charge
@@ -13,11 +12,19 @@ export enum PressureLossType {
  * generic pressure loss law (analogous to acSection with respect to SectionParametree nub)
  */
 export abstract class PressureLossLaw extends Nub {
-
-    protected _pressureLossType: PressureLossType;
+    /**
+     * correspondance entre les lois de perte de charge et les types de calculette
+     */
+    public static readonly calcTypeFromPressureLossLaw: any = {
+        [PressureLossType.LechaptCalmon]: CalculatorType.LechaptCalmon,
+    };
 
     public get pressureLossType(): PressureLossType {
-        return this._pressureLossType;
+        return this.getPropValue("pressureLossType");
+    }
+
+    public set pressureLossType(plt: PressureLossType) {
+        this.setPropValue("pressureLossType", plt);
     }
 
     /**
@@ -28,37 +35,14 @@ export abstract class PressureLossLaw extends Nub {
     }
 
     /**
-     * Calcul de la perte de charge linéaire spécifique à chaque module de perte de charge.
-     * Le paramètre Result est in-out cad qu'il est modifié avec la charge linéaire.
-     */
-    protected abstract calc_Jlin(r: Result): any;
-
-    /**
-     * paramétrage de la calculabilité des paramètres
+     * vérifications post-calcul
      */
-    protected setParametersCalculability() {
-        this.prms.Q.calculability = ParamCalculability.DICHO;
-        this.prms.D.calculability = ParamCalculability.DICHO;
-        this.prms.J.calculability = ParamCalculability.EQUATION;
-        this.prms.Lg.calculability = ParamCalculability.DICHO;
-        this.prms.Kloc.calculability = ParamCalculability.DICHO;
+    public finalChecks(r: Result) {
     }
 
-    public Equation(sVarCalc: string): Result {
-        if (sVarCalc !== "J") {
-            throw new Error("PressureLossLaw.Equation() : invalid variable name " + sVarCalc);
-        }
-
-        const r: Result = new Result(0, this);
-
-        // la vitesse est générique
-        r.values.V = this.prms.Q.v / (Math.PI * Math.pow(this.prms.D.v / 2, 2));
-
-        // perte de charge linéaire propré à chaque module
-        this.calc_Jlin(r);
-
-        r.vCalc = r.values.Jl + this.prms.Kloc.v / 19.62 * Math.pow(r.values.V, 2); // perte de charge totale
+    // interface IProperties
 
-        return r;
+    public hasProperty(key: string): boolean {
+        return key === "pressureLossType";
     }
 }
diff --git a/src/pipe_flow/pressureloss_law_params.ts b/src/pipe_flow/pressureloss_law_params.ts
index 1aab2e8a52adec4a2ba442c9bcc98c57f1f1011c..154da09bd38a1f44213b43c8aee1878fa8925e50 100644
--- a/src/pipe_flow/pressureloss_law_params.ts
+++ b/src/pipe_flow/pressureloss_law_params.ts
@@ -1,58 +1,7 @@
-import { ParamDefinition, ParamFamily } from "../internal_modules";
-import { ParamsEquation } from "../internal_modules";
-import { ParamDomain, ParamDomainValue } from "../internal_modules";
+import { ParamDefinition, ParamDomain, ParamDomainValue, ParamsEquation } from "../internal_modules";
 
 /**
  * generic pressure loss law parameters
  */
 export class PressureLossLawParams extends ParamsEquation {
-    /** Débit */
-    private _Q: ParamDefinition;
-
-    /** Diamètre */
-    private _D: ParamDefinition;
-
-    /** Perte de charge */
-    private _J: ParamDefinition;
-
-    /** Longueur de la conduite */
-    private _Lg: ParamDefinition;
-
-    /** Perte de charge singulière Kloc */
-    private _Kloc: ParamDefinition;
-
-    constructor(rQ: number, rD: number, rJ: number, rLg: number, rKloc: number, nullParams: boolean = false) {
-        super();
-        this._Q = new ParamDefinition(this, "Q", ParamDomainValue.POS, "m³/s", rQ, ParamFamily.FLOWS, undefined, nullParams);
-        this._D = new ParamDefinition(this, "D", new ParamDomain(ParamDomainValue.INTERVAL, 0, 20), "m", rD, ParamFamily.DIAMETERS, undefined, nullParams);
-        this._J = new ParamDefinition(this, "J", ParamDomainValue.POS_NULL, "m", rJ, undefined, undefined, nullParams);
-        this._Lg = new ParamDefinition(this, "Lg", ParamDomainValue.POS, "m", rLg, ParamFamily.LENGTHS, undefined, nullParams);
-        this._Kloc = new ParamDefinition(this, "Kloc", ParamDomainValue.POS_NULL, undefined, rKloc, undefined, undefined, nullParams);
-
-        this.addParamDefinition(this._Q);
-        this.addParamDefinition(this._D);
-        this.addParamDefinition(this._J);
-        this.addParamDefinition(this._Lg);
-        this.addParamDefinition(this._Kloc);
-    }
-
-    get Q() {
-        return this._Q;
-    }
-
-    get D() {
-        return this._D;
-    }
-
-    get J() {
-        return this._J;
-    }
-
-    get Lg() {
-        return this._Lg;
-    }
-
-    get Kloc() {
-        return this._Kloc;
-    }
 }
diff --git a/src/pipe_flow/pressureloss_params.ts b/src/pipe_flow/pressureloss_params.ts
index b12f1bc23855b3143abdd0d4d6941bf89c8acdf6..53ff48faaca7c28a946b1d7f48ab694ea202ef79 100644
--- a/src/pipe_flow/pressureloss_params.ts
+++ b/src/pipe_flow/pressureloss_params.ts
@@ -1,4 +1,55 @@
+import { ParamDefinition, ParamFamily } from "../internal_modules";
+import { ParamDomain, ParamDomainValue } from "../internal_modules";
 import { ParamsEquation } from "../internal_modules";
 
-// dummy parameters class for code consistency, analogous to SectionParams
-export class PressureLossParams extends ParamsEquation { }
+export class PressureLossParams extends ParamsEquation {
+    /** Débit */
+    private _Q: ParamDefinition;
+
+    /** Diamètre */
+    private _D: ParamDefinition;
+
+    /** Perte de charge */
+    private _J: ParamDefinition;
+
+    /** Longueur de la conduite */
+    private _Lg: ParamDefinition;
+
+    /** Coefficient de perte de charge singulière Kloc */
+    private _Kloc: ParamDefinition;
+
+    constructor(rQ: number, rD: number, rJ: number, rLg: number, rKloc: number, nullParams: boolean = false) {
+        super();
+        this._Q = new ParamDefinition(this, "Q", ParamDomainValue.POS, "m³/s", rQ, ParamFamily.FLOWS, undefined, nullParams);
+        this._D = new ParamDefinition(this, "D", new ParamDomain(ParamDomainValue.INTERVAL, 0, 20), "m", rD, ParamFamily.DIAMETERS, undefined, nullParams);
+        this._J = new ParamDefinition(this, "J", ParamDomainValue.POS_NULL, "m", rJ, undefined, undefined, nullParams);
+        this._Lg = new ParamDefinition(this, "Lg", ParamDomainValue.POS, "m", rLg, ParamFamily.LENGTHS, undefined, nullParams);
+        this._Kloc = new ParamDefinition(this, "Kloc", ParamDomainValue.POS_NULL, undefined, rKloc, undefined, undefined, nullParams);
+
+        this.addParamDefinition(this._Q);
+        this.addParamDefinition(this._D);
+        this.addParamDefinition(this._J);
+        this.addParamDefinition(this._Lg);
+        this.addParamDefinition(this._Kloc);
+    }
+
+    get Q() {
+        return this._Q;
+    }
+
+    get D() {
+        return this._D;
+    }
+
+    get J() {
+        return this._J;
+    }
+
+    get Lg() {
+        return this._Lg;
+    }
+
+    get Kloc() {
+        return this._Kloc;
+    }
+}
diff --git a/src/prebarrage/pb_cloison.ts b/src/prebarrage/pb_cloison.ts
index 6a5f18c26ac2194c114fe128f420b6bedd0c5f15..11db86a6f574575cf997d5c8be0e67f52d418f63 100644
--- a/src/prebarrage/pb_cloison.ts
+++ b/src/prebarrage/pb_cloison.ts
@@ -15,8 +15,8 @@ export class PbCloison extends ParallelStructure {
         this.prms.Q.visible = false;
         this.prms.Z1.visible = false;
         this.prms.Z2.visible = false;
-        this.properties.setPropValue("upstreamBasin", bassinAmont === undefined ? "" : bassinAmont.uid);
-        this.properties.setPropValue("downstreamBasin", bassinAval === undefined ? "" : bassinAval.uid);
+        this.setPropValue("upstreamBasin", bassinAmont === undefined ? "" : bassinAmont.uid);
+        this.setPropValue("downstreamBasin", bassinAval === undefined ? "" : bassinAval.uid);
         this.setCalculatorType(CalculatorType.PbCloison);
         this._intlType = "Cloison";
     }
@@ -45,7 +45,7 @@ export class PbCloison extends ParallelStructure {
         if (b !== undefined) {
             uid = b.uid;
         }
-        this.properties.setPropValue("upstreamBasin", uid);
+        this.setPropValue("upstreamBasin", uid);
         this.parent.updatePointers();
     }
 
@@ -68,7 +68,7 @@ export class PbCloison extends ParallelStructure {
         if (b !== undefined) {
             uid = b.uid;
         }
-        this.properties.setPropValue("downstreamBasin", uid);
+        this.setPropValue("downstreamBasin", uid);
         this.parent.updatePointers();
     }
 
diff --git a/src/prebarrage/pre_barrage.ts b/src/prebarrage/pre_barrage.ts
index 12305ba2ad9b8f231e1eb7b1c799bd7ee331768e..08122a1b53cd5c8fb221620acfe2fce9e5ec317c 100644
--- a/src/prebarrage/pre_barrage.ts
+++ b/src/prebarrage/pre_barrage.ts
@@ -650,10 +650,10 @@ export class PreBarrage extends Nub {
             if (c instanceof PbCloison) {
                 for (const k of Object.keys(ret.changedUids)) {
                     // find basins having the changed UID
-                    if (c.properties.props.upstreamBasin === k) {
+                    if (c.getPropValue("upstreamBasin") === k) {
                         c.bassinAmont = this.findChild(ret.changedUids[k]) as PbBassin;
                     }
-                    if (c.properties.props.downstreamBasin === k) {
+                    if (c.getPropValue("downstreamBasin") === k) {
                         c.bassinAval = this.findChild(ret.changedUids[k]) as PbBassin;
                     }
                 }
diff --git a/src/props.ts b/src/props.ts
index 868e28dfe4c6bec48a7fdffab4fe7cce576023a4..01743005bfdd85440e6dbe2fc701d1a321e2f236 100644
--- a/src/props.ts
+++ b/src/props.ts
@@ -1,70 +1,147 @@
-import { IObservable, Observable, Observer } from "./internal_modules";
+import { cloneDeep } from "lodash";
+
+import { BiefRegime, CalculatorType, DivingJetSupport, FishSpecies, GrilleProfile, GrilleType, IObservable, LCMaterial, LoiDebit, MRCInclination, MethodeResolution, Observable, Observer, ParType, PressureLossType, SPPOperation, SectionType, StructureType, TrigoOperation, TrigoUnit, isNumeric } from "./internal_modules";
 
 /**
- * special property names
+ * get enum numerical value from enum class name and value as a string
+ * @param enumClass enum class name
+ * @param enumValueName enum value as a string
+ * @returns enum numerical value
  */
+export function enumValueFromString(enumClass: string, enumValueName: string): any {
+    // !! property names must be unique throughout JaLHyd !!
+    const enumValues = Props.enumFromProperty[enumClass];
+    if (enumValues) {
+        return enumValues[enumValueName];
+    }
+    throw new Error("unknown enum class ${enumClass}");
+}
 
 /**
  * represents a boolean: true if provided value at parameter creation must be ignore (@see nghyd/enableEmptyFieldsOnFormInit)
  */
 export const Prop_NullParameters: string = "nullparams";
 
+/**
+ * Interface permettant de propager de manière transparente des opérations sur des propriétés en encapsulant celles ci
+ * (implémentée par Props et les classes possédant directement ou indirectement un membre de type Props, par ex Nub)
+ */
+export interface IProperties extends IObservable {
+    /**
+     * get property value
+     * @param key property name
+     */
+    getPropValue(key: string): any;
+
+    /**
+     * set property value
+     * @param key property name
+     * @param val property value to set
+     * @param sender object from which modification originates
+     */
+    setPropValue(key: string, val: any, sender?: any): boolean;
+
+    /**
+     * determine if this object directly or indirectly has a given property
+     * @param key property name
+     */
+    hasProperty(key: string): boolean;
+
+    /**
+     * list of properties keys
+    */
+    readonly keys: string[];
+}
+
 /**
  * gestion d'un ensemble de propriétés (clé/valeur) qui prévient quand
  * l'une d'entre-elles change
  */
-export class Props implements IObservable {
+export class Props implements IProperties {
+
+    /** correspondance entre les noms de propriétés et les enum associés */
+    public static readonly enumFromProperty: any = {
+        calcType: CalculatorType,
+        divingJetSupported: DivingJetSupport,
+        gridProfile: GrilleProfile,
+        gridType: GrilleType,
+        inclinedApron: MRCInclination,
+        loiDebit: LoiDebit,
+        material: LCMaterial,
+        methodeResolution: MethodeResolution,
+        nodeType: SectionType,
+        parType: ParType,
+        pressureLossType: PressureLossType,
+        regime: BiefRegime,
+        species: FishSpecies,
+        sppOperation: SPPOperation,
+        structureType: StructureType,
+        trigoOperation: TrigoOperation,
+        trigoUnit: TrigoUnit
+    };
+
     // implémentation de IObservable par délégation
     private _observable: Observable;
 
-    constructor(private _props: any = {}) {
+    // object literal representing properties keys and values
+    private _map: any = {};
+
+    constructor(prps?: any | IProperties) {
         this._observable = new Observable();
+
+        if (prps !== undefined) {
+            if (Props.implementsIProperties(prps)) {
+                this.copyIProperties(prps);
+            }
+            else
+                this._map = cloneDeep(prps);
+        }
+    }
+
+    /**
+     * @returns true if an object implements the IProperties interface
+     */
+    public static implementsIProperties(o: any): boolean {
+        return "getPropValue" in o && "setPropValue" in o && "hasProperty" in o && "keys" in o;
+    }
+
+    /**
+     * set properties map from an IProperties
+     */
+    private copyIProperties(p: IProperties) {
+        this._map = {};
+        for (const k of p.keys) {
+            this._map[k] = p.getPropValue(k);
+        }
+    }
+
+    public get keys(): string[] {
+        return Object.keys(this._map);
     }
 
     public getPropValue(key: string): any {
-        return this._props[key];
+        return this._map[key];
     }
 
     public setPropValue(key: string, val: any, sender?: any): boolean {
         if (key === undefined) {
             throw new Error("setPropValue : undefined key!");
         }
-        const oldValue = this._props[key];
+        const oldValue = this._map[key];
         const changed = oldValue !== val;
         if (changed) {
-            this._props[key] = val;
+            this._map[key] = val;
             this.notifyPropChange(key, val, sender);
         }
         return changed;
     }
 
-    /**
-     * fixe la valeur de toutes les propriétés
-     * @param props nouvelles valeurs
-     * @param sender objet modificateur
-     */
-    public setProps(props: {}, sender?: any) {
-        let p;
-        if (props instanceof Props) {
-            p = props._props;
-        } else {
-            p = props;
-        }
-        const changed = this.compareObjects(this._props, p);
-        if (changed) {
-            this._props = {};
-            for (const k in p) {
-                if (p.hasOwnProperty(k)) {
-                    this._props[k] = p[k];
-                }
-            }
-
-            this.notifyPropsChange(sender);
-        }
+    public removeProp(key: string) {
+        delete this._map[key];
     }
 
-    public get props() {
-        return this._props;
+    public hasProperty(key: string): boolean {
+        return true;
     }
 
     public getObservers(): Observer[] {
@@ -75,31 +152,24 @@ export class Props implements IObservable {
      * Remove all properties, does not notify of any change
      */
     public reset() {
-        this._props = {};
+        this._map = {};
     }
 
     /**
      * Clones properties into a new Props object
      */
     public clone(): Props {
-        const res = new Props();
-        // copy values
-        for (const k in this._props) {
-            if (this._props.hasOwnProperty(k)) {
-                res._props[k] = this._props[k];
-            }
-        }
-        return res;
+        return new Props(this);
     }
 
     public toString(): string {
         let res = "[";
-        for (const k in this._props) {
-            if (this._props.hasOwnProperty(k)) {
+        for (const k in this._map) {
+            if (this._map.hasOwnProperty(k)) {
                 if (res !== "[") {
                     res += ", ";
                 }
-                res += `${k}:${this._props[k]}`;
+                res += `${k}:${this._map[k]}`;
             }
         }
         res += "]";
@@ -129,16 +199,6 @@ export class Props implements IObservable {
         this._observable.notifyObservers(data, sender);
     }
 
-    /**
-     * notification de changement de la valeur de toutes les propriétés
-     * @param sender objet ayant changé les valeurs
-     */
-    private notifyPropsChange(sender: any) {
-        this.notifyObservers({
-            action: "propertiesChange",
-        }, sender);
-    }
-
     /**
      * notification de changement de la valeur d'une propriété
      * @param prop nom de la propriété modifiée
@@ -154,32 +214,22 @@ export class Props implements IObservable {
     }
 
     /**
-     * compare 2 objets (clés et valeurs)
-     * @return true s'il existe une différence
+     * Returns a copy of given map, inverting enum keys and values
      */
-    private compareObjects(o1: { [key: string]: any }, o2: { [key: string]: any }) {
-        const oldKeys: string[] = Object.keys(o1).sort();
-        const newKeys: string[] = Object.keys(o2).sort();
-
-        // nombre de clés
-        const changed = oldKeys.length !== newKeys.length;
-        if (changed) {
-            return true;
-        }
-        // nom des clés
-        for (const i in oldKeys) {
-            if (oldKeys[i] !== newKeys[i]) {
-                return true;
-            }
-        }
-        // valeurs
-        for (const i in o1) {
-            if (o1[i] !== o2[i]) {
-                return true;
+    public static invertEnumKeysAndValuesInProperties(stringProps: any, forceNumbers: boolean = false) {
+        const res = JSON.parse(JSON.stringify(stringProps)); // clone
+        for (const k in res) {
+            if (!forceNumbers || !isNumeric(res[k])) {
+                if (Object.keys(Props.enumFromProperty).includes(k)) {
+                    const enumClass = Props.enumFromProperty[k];
+                    res[k] = enumClass[res[k]];
+                }
             }
         }
-
-        return false;
+        return res;
     }
 
+    public invertEnumKeysAndValues(forceNumbers: boolean = false) {
+        return Props.invertEnumKeysAndValuesInProperties(this._map, forceNumbers);
+    }
 }
diff --git a/src/session.ts b/src/session.ts
index d1fb86c0b3e0e74f7e06ca2fb6ee56929df537fa..f9f6ef3b7879a1b9b657f8e66affb92e4cac5095 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -1,7 +1,5 @@
-import { isNumeric } from "./internal_modules";
-import { CalculatorType, SectionType } from "./internal_modules";
+import { CalculatorType, IProperties, PressureLossParams, SectionType } from "./internal_modules";
 import { config } from "./internal_modules";
-import { LCMaterial } from "./internal_modules";
 import { LinkedValue } from "./internal_modules";
 import { Nub } from "./internal_modules";
 import { ParamDefinition } from "./internal_modules";
@@ -9,7 +7,7 @@ import { Props, Prop_NullParameters } from "./internal_modules";
 import { SessionSettings } from "./internal_modules";
 
 // Calculettes
-import { Grille, GrilleProfile, GrilleType } from "./internal_modules";
+import { Grille } from "./internal_modules";
 import { GrilleParams } from "./internal_modules";
 import { Jet } from "./internal_modules";
 import { JetParams } from "./internal_modules";
@@ -19,17 +17,16 @@ import { MacroRugo } from "./internal_modules";
 import { MacrorugoCompound } from "./internal_modules";
 import { MacrorugoCompoundParams } from "./internal_modules";
 import { MacrorugoParams } from "./internal_modules";
-import { MRCInclination } from "./internal_modules";
-import { SPP, SPPOperation } from "./internal_modules";
+import { SPP } from "./internal_modules";
 import { SPPParams } from "./internal_modules";
-import { Trigo, TrigoOperation, TrigoUnit } from "./internal_modules";
+import { Trigo } from "./internal_modules";
 import { TrigoParams } from "./internal_modules";
 import { YAXB } from "./internal_modules";
 import { YAXBParams } from "./internal_modules";
 import { YAXN } from "./internal_modules";
 import { YAXNParams } from "./internal_modules";
 import { Bief } from "./internal_modules";
-import { BiefParams, BiefRegime } from "./internal_modules";
+import { BiefParams } from "./internal_modules";
 import { MethodeResolution } from "./internal_modules";
 import { Pente } from "./internal_modules";
 import { PenteParams } from "./internal_modules";
@@ -71,16 +68,14 @@ import { DeverParams } from "./internal_modules";
 import { CreateStructure } from "./internal_modules";
 import { ParallelStructure } from "./internal_modules";
 import { ParallelStructureParams } from "./internal_modules";
-import { LoiDebit, StructureType } from "./internal_modules";
-import { Par, ParType } from "./internal_modules";
+import { LoiDebit } from "./internal_modules";
+import { Par } from "./internal_modules";
 import { ParParams } from "./internal_modules";
 import { ParSimulation } from "./internal_modules";
 import { ParSimulationParams } from "./internal_modules";
-import { FishSpecies } from "./internal_modules";
 import { Espece } from "./internal_modules";
 import { EspeceParams } from "./internal_modules";
 import { Verificateur } from "./internal_modules";
-import { DivingJetSupport } from "./internal_modules";
 import { PreBarrage } from "./internal_modules";
 import { PreBarrageParams } from "./internal_modules";
 import { PbCloison } from "./internal_modules";
@@ -92,27 +87,6 @@ import { PressureLossLaw, PressureLossType } from "./internal_modules";
 
 export class Session {
 
-    /** correspondance entre les noms des propriétés et les enum associés */
-    public static enumFromProperty: any = {
-        loiDebit: LoiDebit,
-        methodeResolution: MethodeResolution,
-        material: LCMaterial,
-        gridProfile: GrilleProfile,
-        gridType: GrilleType,
-        regime: BiefRegime,
-        trigoOperation: TrigoOperation,
-        trigoUnit: TrigoUnit,
-        sppOperation: SPPOperation,
-        nodeType: SectionType,
-        calcType: CalculatorType,
-        structureType: StructureType,
-        inclinedApron: MRCInclination,
-        parType: ParType,
-        species: FishSpecies,
-        divingJetSupported: DivingJetSupport,
-        pressureLossType: PressureLossType
-    };
-
     public static getInstance(): Session {
         if (Session._instance === undefined) {
             Session._instance = new Session();
@@ -120,22 +94,6 @@ export class Session {
         return Session._instance;
     }
 
-    /**
-     * Returns a copy of given map, inverting enum keys and values
-     */
-    public static invertEnumKeysAndValuesInProperties(stringProps: any, forceNumbers: boolean = false) {
-        const res = JSON.parse(JSON.stringify(stringProps)); // clone
-        for (const k in res) {
-            if (!forceNumbers || !isNumeric(res[k])) {
-                if (Object.keys(Session.enumFromProperty).includes(k)) {
-                    const enumClass = Session.enumFromProperty[k];
-                    res[k] = enumClass[res[k]];
-                }
-            }
-        }
-        return res;
-    }
-
     /** instance pour le pattern singleton */
     private static _instance: Session;
 
@@ -364,20 +322,56 @@ export class Session {
         return foundNub;
     }
 
+    private static parameterIndex(obj: any, symbol: string): number {
+        let i;
+        const prms = obj["parameters"];
+        for (i in prms) {
+            if (prms[i]["symbol"] === symbol) {
+                return +i;
+            }
+        }
+        return -1;
+    }
+
     /**
      * Calcule le type de calculette compatible et modifie les propriétés en conséquence.
      * Permet de charger des fichiers session avec une version antérieure.
      * Par ex : Lechapt-Calmon -> PressureLoss
      */
-    private compatibleCalculatorType(props: Props): CalculatorType {
-        const ct: CalculatorType = props.getPropValue("calcType");
-        switch (ct) {
+    private compatibleCalculator(obj: any): any {
+        switch (obj["props"]["calcType"]) {
             case CalculatorType.LechaptCalmon:
-                props.setPropValue("pressureLossType", PressureLossType.LechaptCalmon);
-                return CalculatorType.PressureLoss;
+                // create parent PressureLoss nub
+                const plProps = new Props();
+                plProps.setPropValue("calcType", CalculatorType.PressureLoss);
+                const pl = this.createNub(plProps);
+
+                // JSON representation
+                let res: any = pl.objectRepresentation();
+
+                // set Lechapt-Calmon as child
+                res["children"] = [obj];
+
+                // move PressureLoss parameters from Lechapt-Calmon
+                const movedParams = ["Q", "D", "J", "Lg", "Kloc"];
+                for (const p of movedParams) {
+                    // if child has parameter
+                    const cp = Session.parameterIndex(obj, p);
+                    if (cp !== -1) {
+                        const pp = Session.parameterIndex(res, p);
+                        if (pp === -1) {
+                            res["parameters"].push(obj["parameters"][cp]);
+                        } else {
+                            res["parameters"][pp] = obj["parameters"][cp];
+                        }
+                        // delete obj["parameters"][pp];
+                        obj["parameters"].splice(cp, 1);
+                    }
+                }
+                return res;
 
             default:
-                return ct;
+                return obj;
         }
     }
 
@@ -391,15 +385,14 @@ export class Session {
      *    définies dans le constructeur du Nub créé
      * @param dbg activer débogage
      */
-    public createNub(params: Props, parentNub?: Nub, dbg: boolean = false): Nub {
-        const calcType = this.compatibleCalculatorType(params);
-
+    public createNub(params: IProperties, parentNub?: Nub, dbg: boolean = false): Nub {
         // true if provided values to parameter creation must be ignored
         const nullParams: boolean = params.getPropValue(Prop_NullParameters) === undefined ? false : params.getPropValue(Prop_NullParameters);
 
         let nub: Nub;
         let prms: any;
 
+        const calcType: CalculatorType = params.getPropValue("calcType");
         switch (calcType) {
             case CalculatorType.ConduiteDistributrice:
                 prms = new ConduiteDistribParams(
@@ -415,11 +408,6 @@ export class Session {
 
             case CalculatorType.LechaptCalmon:
                 prms = new PL_LechaptCalmonParams(
-                    3, // débit
-                    1.2, // diamètre
-                    0.6, /// perte de charge
-                    100, // longueur du toyo
-                    0, // Ks Perte de charge singulière
                     1.863, // paramètre L du matériau
                     2, // paramètre M du matériau
                     5.33, // paramètre N du matériau
@@ -821,9 +809,17 @@ export class Session {
                 break;
 
             case CalculatorType.PressureLoss:
+                const plParams = new PressureLossParams(
+                    3, // débit
+                    1.2, // diamètre
+                    0.6, /// perte de charge
+                    100, // longueur du toyo
+                    0, // Kloc Perte de charge singulière
+                    nullParams
+                );
                 const lossType: PressureLossType = params.getPropValue("pressureLossType") ?? PressureLossType.LechaptCalmon;
-                const pl = this.createPressureLossLaw(lossType, dbg, nullParams);
-                nub = new PressureLoss(pl, dbg);
+                nub = new PressureLoss(plParams, undefined, dbg);
+                nub.setPropValue("pressureLossType", lossType);
                 break;
 
             default:
@@ -834,7 +830,7 @@ export class Session {
 
         // propagate properties
         try {
-            nub.properties = params;
+            nub.setProperties(params);
         } catch (e) {
             // loading Solveur properties when unserialising a session might fail because target
             // Nub / param do not exist yet; silent fail in this case, and Solveur.fixTargets()
@@ -1020,27 +1016,21 @@ export class Session {
     /**
      * Crée un Nub de type perte de charge
      */
-    private createPressureLossLaw(plt: PressureLossType, dbg: boolean = false, nullParams: boolean = false): PressureLossLaw {
-        switch (plt) {
-            case PressureLossType.LechaptCalmon:
-                const prms = new PL_LechaptCalmonParams(
-                    3, // débit
-                    1.2, // diamètre
-                    0.6, /// perte de charge
-                    100, // longueur du toyo
-                    0, // Ks Perte de charge singulière
-                    1.863, // paramètre L du matériau
-                    2, // paramètre M du matériau
-                    5.33, // paramètre N du matériau
-                    nullParams
-
-                );
-                return new PL_LechaptCalmon(prms, dbg);
-
-            default:
-                throw new Error(`type de perte de charge ${PressureLossType[plt]} non pris en charge`);
-        }
-    }
+    // private createPressureLossLaw(plt: PressureLossType, dbg: boolean = false, nullParams: boolean = false): PressureLossLaw {
+    //     switch (plt) {
+    //         case PressureLossType.LechaptCalmon:
+    //             const prms = new PL_LechaptCalmonParams(
+    //                 1.863, // paramètre L du matériau
+    //                 2, // paramètre M du matériau
+    //                 5.33, // paramètre N du matériau
+    //                 nullParams
+    //             );
+    //             return new PL_LechaptCalmon(prms, dbg);
+
+    //         default:
+    //             throw new Error(`type de perte de charge ${PressureLossType[plt]} non pris en charge`);
+    //     }
+    // }
 
     /**
      * Creates a Nub from an object representation and adds it to the current session; returns
@@ -1057,8 +1047,10 @@ export class Session {
             meta: undefined,
             hasErrors: false
         };
+        // get upward compatible calculator
+        obj = this.compatibleCalculator(obj);
         // decode properties
-        const props = Session.invertEnumKeysAndValuesInProperties(obj.props, true);
+        const props = Props.invertEnumKeysAndValuesInProperties(obj.props, true);
         // create the Nub
         let newNub;
         if (register) {
diff --git a/src/solveur/solveur.ts b/src/solveur/solveur.ts
index e40c9af1478a75e28bb8f504ad8c27ebd0b5f3e8..7ed087eacda325d4b6efd1fee2714605bc3e512f 100644
--- a/src/solveur/solveur.ts
+++ b/src/solveur/solveur.ts
@@ -77,7 +77,7 @@ export class Solveur extends Nub implements Observer {
         if (n !== undefined) {
             uid = n.uid;
         }
-        this.properties.setPropValue("nubToCalculate", uid);
+        this.setPropValue("nubToCalculate", uid);
     }
 
     public get targettedResult(): string {
@@ -126,7 +126,7 @@ export class Solveur extends Nub implements Observer {
         if (p !== undefined) {
             sp = p.nubUid + "/" + p.symbol;
         }
-        this.properties.setPropValue("searchedParameter", sp);
+        this.setPropValue("searchedParameter", sp);
     }
 
     /**
@@ -255,8 +255,8 @@ export class Solveur extends Nub implements Observer {
         };
         try {
             // do not use setters, to allow setting directly the string UIDs
-            this.properties.setPropValue("nubToCalculate", obj.props.nubToCalculate);
-            this.properties.setPropValue("searchedParameter", obj.props.searchedParameter);
+            this.setPropValue("nubToCalculate", obj.props.nubToCalculate);
+            this.setPropValue("searchedParameter", obj.props.searchedParameter);
         } catch (e) {
             ret.hasErrors = true;
         }
diff --git a/src/structure/factory_structure.ts b/src/structure/factory_structure.ts
index 849ca3ee347a7c8dd318c9b821ed8f89605d7445..31927f6063b25e5b24a2f494c2209286ddef30bf 100755
--- a/src/structure/factory_structure.ts
+++ b/src/structure/factory_structure.ts
@@ -224,7 +224,7 @@ export function CreateStructure(loiDebit: LoiDebit, parentNub?: ParallelStructur
     if (parentNub) {
         ret.setParent(parentNub);
         // Set Structure Type
-        ret.properties.setPropValue("structureType", StructureProperties.findCompatibleStructure(loiDebit, parentNub));
+        ret.setPropValue("structureType", StructureProperties.findCompatibleStructure(loiDebit, parentNub));
     }
 
     return ret;
diff --git a/src/structure/structure.ts b/src/structure/structure.ts
index 78312bee20c9217c15f8cd2352104bf928e0ebba..0363d5133eabb71e30e54a71ff16896f9cdb262c 100644
--- a/src/structure/structure.ts
+++ b/src/structure/structure.ts
@@ -96,23 +96,6 @@ export abstract class Structure extends ChildNub {
         this._intlType = "Ouvrage";
     }
 
-    /** Returns Props object (observable set of key-values) associated to this Nub */
-    public get properties(): Props {
-        return this._props;
-    }
-
-    // overriden to set property once and for all (it's a constant)
-    protected setCalculatorType(ct: CalculatorType): void {
-        super.setCalculatorType(ct);
-        // completes props with calcType
-        this._props.setPropValue("calcType", this.calcType);
-    }
-
-    // setter is not inherited from Nub if getter is redefined :/
-    public set properties(props: Props) {
-        super.setProperties(props);
-    }
-
     public get isZDVcalculable(): boolean {
         return this._isZDVcalculable;
     }
diff --git a/src/units.ts b/src/units.ts
index b1538784ef270bc180e2a2317f893762d01baf65..f072fe21f2024c04a270368b36d2cf3d5fba5f86 100644
--- a/src/units.ts
+++ b/src/units.ts
@@ -1,5 +1,6 @@
 import { CalculatorType } from "./compute-node";
 import { Cloisons, ConcentrationBlocs, Dever, Grille, Jet, MacroRugo, PabNombre, Par, RegimeUniforme } from "./index";
+import { PressureLoss } from "./internal_modules";
 
 /**
  * correspondance type de calculette - classe de Nub
@@ -13,7 +14,8 @@ const nubClasses = new Map<CalculatorType, any>([
     [CalculatorType.Cloisons, Cloisons],
     [CalculatorType.PabNombre, PabNombre],
     [CalculatorType.Par, Par],
-    [CalculatorType.Dever, Dever]
+    [CalculatorType.Dever, Dever],
+    [CalculatorType.PressureLoss, PressureLoss]
 ]);
 
 /**
@@ -29,5 +31,5 @@ export function getNubResultUnit(nubType: CalculatorType, symbol: string) {
         const units = f();
         return units[symbol];
     }
-    return {};
+    return undefined;
 }
diff --git a/src/util/enum.ts b/src/util/enum.ts
index fc4a9cb0fe8f692f1f95cdf0cf5d67c9b90ef06f..d3c7dcbec424152a4ba22b4056f8950b9aa48892 100644
--- a/src/util/enum.ts
+++ b/src/util/enum.ts
@@ -47,19 +47,3 @@ export class EnumEx {
         return Object.keys(e).map((k) => e[k]);
     }
 }
-
-
-/**
- * get enum numerical value from enum class name and value as a string
- * @param enumClass enum class name
- * @param enumValueName enum value as a string
- * @returns enum numerical value
- */
-export function enumValueFromString(enumClass: string, enumValueName: string): any {
-    // !! property names must be unique throughout JaLHyd !!
-    const enumValues = Session.enumFromProperty[enumClass];
-    if (enumValues) {
-        return enumValues[enumValueName];
-    }
-    throw new Error("unknown enum class ${enumClass}");
-}
diff --git a/src/verification/espece.ts b/src/verification/espece.ts
index dcc026235524bdb02f3642b440e20569ee45616e..25d43cad7f07cc1fc8500759f83b5993409ff9b4 100644
--- a/src/verification/espece.ts
+++ b/src/verification/espece.ts
@@ -340,7 +340,7 @@ export class Espece extends Nub implements Observer {
 
     /** Changing the fish species will load adequate predefined values */
     public set species(s: FishSpecies) {
-        this.properties.setPropValue("species", s);
+        this.setPropValue("species", s);
     }
 
     public set passToCheck(p: FishPass) {
@@ -349,11 +349,11 @@ export class Espece extends Nub implements Observer {
     }
 
     public get divingJetSupported(): DivingJetSupport {
-        return this.properties.getPropValue("divingJetSupported");
+        return this.getPropValue("divingJetSupported");
     }
 
     public set divingJetSupported(i: DivingJetSupport) {
-        this.properties.setPropValue("divingJetSupported", i);
+        this.setPropValue("divingJetSupported", i);
     }
 
     /**
@@ -898,7 +898,7 @@ export class Espece extends Nub implements Observer {
         for (const device of wall.structures) {
             let zdv = device.prms.ZDV.singleValue;
             // if device is a regulated weir, get calculated ZDV at current iteration from parent downWall
-            if (loiAdmissiblesCloisonAval.VanneLevante.includes(device.properties.getPropValue("loiDebit"))) {
+            if (loiAdmissiblesCloisonAval.VanneLevante.includes(device.getPropValue("loiDebit"))) {
                 zdv = device.parent.result.resultElements[this.indexToCheck].values.ZDV;
             }
             // do not use device.prms.h1.v, that contains only the latest calculated value if pass is variyng
diff --git a/src/verification/verificateur.ts b/src/verification/verificateur.ts
index 2d0621630af097bfa1aa020f9a5225346e9aacc8..c459b78b5eb628c410a65c9e178d6a4125505036 100644
--- a/src/verification/verificateur.ts
+++ b/src/verification/verificateur.ts
@@ -45,7 +45,7 @@ export class Verificateur extends Nub {
     }
 
     public set speciesList(l: string[]) {
-        this.properties.setPropValue("speciesList", l);
+        this.setPropValue("speciesList", l);
         // (re)create Espece instances
         this.initSpecies();
     }
@@ -67,7 +67,7 @@ export class Verificateur extends Nub {
         if (n !== undefined) {
             uid = n.uid;
         }
-        this.properties.setPropValue("nubToVerify", uid);
+        this.setPropValue("nubToVerify", uid);
     }
 
     /**