diff --git a/spec/fuzzing.spec.ts b/spec/fuzzing.spec.ts
index 8dead94243e8e6dd27123927ab82803368df6172..97ff31a336e631272fb75ba93f794fa5cab1aa9c 100644
--- a/spec/fuzzing.spec.ts
+++ b/spec/fuzzing.spec.ts
@@ -169,7 +169,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) {
@@ -190,7 +190,7 @@ function CreateTestNub(iCalType: number): Nub {
         setRandomSection(n as SectionNub);
     }
     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/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/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..2766803f4fded18867181f569cee1ab0d2bf2423 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,
@@ -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,
@@ -105,15 +105,6 @@ export abstract class Nub extends ComputeNode implements IObservable {
 
     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 +117,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);
     }
@@ -207,6 +191,16 @@ export abstract class Nub extends ComputeNode implements IObservable {
         return this._calcType;
     }
 
+    /**
+     * set Nub calculator type.
+     * give children the opportunity to react to assignment
+     * @see Structure
+     */
+    protected setCalculatorType(ct: CalculatorType) {
+        this._calcType = ct;
+        this._props.setPropValue("calcType", this.calcType);
+    }
+
     public get calculatedParam(): ParamDefinition {
         return this._calculatedParam;
     }
@@ -275,7 +269,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,7 +282,7 @@ 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;
@@ -1266,7 +1260,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 +1324,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 +1839,26 @@ 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);
+    }
+
+    // interface IProperties
+
+    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): any {
+        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..988db4c4818cadda6ae684a224e1046cf8464f7e 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: }
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..3f06dca321070d68768872dcce7046d897e9ebdd 100644
--- a/src/open-channel/section/section_type.ts
+++ b/src/open-channel/section/section_type.ts
@@ -52,7 +52,7 @@ 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 _nodeType: SectionType;
 
     private _hautCritique: Result;  // Tirant d'eau critique
 
@@ -102,19 +102,9 @@ export abstract class acSection extends Nub {
         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;
-    }
-
-    // setter is not inherited from Nub if getter is redefined :/
-    public set properties(props: Props) {
-        super.setProperties(props);
+    protected set nodeType(nt: SectionType) {
+        this._nodeType = nt;
+        this._props.setPropValue("nodeType", this.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/pipe_flow/pl_lechaptcalmon.ts b/src/pipe_flow/pl_lechaptcalmon.ts
index cc7d9a13ac81c1d8cf28251050f740f818dd574d..6c621387c2b4db695d36943ec4d2d0d8d201a813 100644
--- a/src/pipe_flow/pl_lechaptcalmon.ts
+++ b/src/pipe_flow/pl_lechaptcalmon.ts
@@ -84,11 +84,11 @@ 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) {
@@ -145,7 +145,7 @@ 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;
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..f84d6f49391769ccce2d572d948cc3bb4020a362 100644
--- a/src/props.ts
+++ b/src/props.ts
@@ -1,19 +1,77 @@
-import { IObservable, Observable, Observer } from "./internal_modules";
+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;
+
+    /**
+     * 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;
 
@@ -21,6 +79,10 @@ export class Props implements IObservable {
         this._observable = new Observable();
     }
 
+    public get keys(): string[] {
+        return Object.keys(this._props);
+    }
+
     public getPropValue(key: string): any {
         return this._props[key];
     }
@@ -63,10 +125,6 @@ export class Props implements IObservable {
         }
     }
 
-    public get props() {
-        return this._props;
-    }
-
     public getObservers(): Observer[] {
         return this._observable.getObservers();
     }
@@ -182,4 +240,23 @@ export class Props implements IObservable {
         return false;
     }
 
+    /**
+     * 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(Props.enumFromProperty).includes(k)) {
+                    const enumClass = Props.enumFromProperty[k];
+                    res[k] = enumClass[res[k]];
+                }
+            }
+        }
+        return res;
+    }
+
+    public invertEnumKeysAndValues(forceNumbers: boolean = false) {
+        return Props.invertEnumKeysAndValuesInProperties(this._props, forceNumbers);
+    }
 }
diff --git a/src/session.ts b/src/session.ts
index d1fb86c0b3e0e74f7e06ca2fb6ee56929df537fa..c7d1763d2d209cde1a5899db7e59c138e6dbdac8 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, 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;
 
@@ -369,7 +327,7 @@ export class Session {
      * Permet de charger des fichiers session avec une version antérieure.
      * Par ex : Lechapt-Calmon -> PressureLoss
      */
-    private compatibleCalculatorType(props: Props): CalculatorType {
+    private compatibleCalculatorType(props: IProperties): CalculatorType {
         const ct: CalculatorType = props.getPropValue("calcType");
         switch (ct) {
             case CalculatorType.LechaptCalmon:
@@ -391,7 +349,7 @@ 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 {
+    public createNub(params: IProperties, parentNub?: Nub, dbg: boolean = false): Nub {
         const calcType = this.compatibleCalculatorType(params);
 
         // true if provided values to parameter creation must be ignored
@@ -834,7 +792,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()
@@ -1058,7 +1016,7 @@ export class Session {
             hasErrors: false
         };
         // 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/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);
     }
 
     /**