diff --git a/e2e/calculator.po.ts b/e2e/calculator.po.ts
index da84adc10448a18c56db41bd2c3e63646ea1a4c1..cb46ead62327610f042b9b62afa357e44d36fb81 100644
--- a/e2e/calculator.po.ts
+++ b/e2e/calculator.po.ts
@@ -64,11 +64,11 @@ export class CalculatorPage {
     }
 
     getAddStructureButton() {
-        return element(by.css("fieldset-container .hyd-window-btns button.add-structure"));
+        return element(by.css("structure-fieldset-container .hyd-window-btns button.add-structure"));
     }
 
     getCopyStructureButton() {
-        return element(by.css("fieldset-container .hyd-window-btns button.copy-structure"));
+        return element(by.css("structure-fieldset-container .hyd-window-btns button.copy-structure"));
     }
 
     getAllLinkButtons() {
diff --git a/jalhyd_branch b/jalhyd_branch
index ba5ab733debaf6478f0d6b583eb80b41848804b7..626e97d71d9e3364f9abe16f7e3c8d70dea5a7fa 100644
--- a/jalhyd_branch
+++ b/jalhyd_branch
@@ -1 +1 @@
-324-pab-ajouter-la-charge-et-l-ennoiement-dans-le-tableau-de-resultat-et-l-export
+devel
\ No newline at end of file
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 7c57cdce8f41945363e18863eb2a1122c4a7b940..c39a646e4ea262213bbe5d71352749971cef5120 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -120,6 +120,9 @@ import { ImmediateErrorStateMatcher } from "./formulaire/immediate-error-state-m
 import { LoadSessionURLComponent } from "./components/load-session-url/load-session-url.component";
 import { DialogShowMessageComponent } from "./components/dialog-show-message/dialog-show-message.component";
 import { DialogConfirmLoadSessionURLComponent } from "./components/dialog-confirm-load-session-url/dialog-confirm-load-session-url.component";
+import { StructureFieldsetContainerComponent } from "./components/structure-fieldset-container/structure-fieldset-container.component";
+import { BasinFieldsetContainerComponent } from "./components/basin-fieldset-container/basin-fieldset-container.component";
+import { PrebarrageService } from "./services/prebarrage.service";
 
 const appRoutes: Routes = [
     { path: "list/search", component: CalculatorListComponent },
@@ -209,6 +212,8 @@ const appRoutes: Routes = [
         DialogConfirmLoadSessionURLComponent,
         FieldSetComponent,
         FieldsetContainerComponent,
+        StructureFieldsetContainerComponent,
+        BasinFieldsetContainerComponent,
         FixedResultsComponent,
         FixedVarResultsComponent,
         FlexGtXxsShowHideDirective,
@@ -261,6 +266,7 @@ const appRoutes: Routes = [
         HttpService,
         I18nService,
         NotificationsService,
+        PrebarrageService,
         {
             provide: ErrorStateMatcher,
             useClass: ImmediateErrorStateMatcher
diff --git a/src/app/calculators/pbbassin/en.json b/src/app/calculators/pbbassin/en.json
index 6566e3fd828cb3b988499fd1881afb6acdbe7f6b..a0c025b65802fd3c6a7c419c30116897cf99ff7e 100644
--- a/src/app/calculators/pbbassin/en.json
+++ b/src/app/calculators/pbbassin/en.json
@@ -1,5 +1,6 @@
 {
-    "fs_basin_params": "Basin parameters",
+    "basin_container": "Basins",
+    "fs_basin": "Basin parameters",
 
     "S": "Surface",
     "ZF": "Bottom elevation"
diff --git a/src/app/calculators/pbbassin/fr.json b/src/app/calculators/pbbassin/fr.json
index 71d54fd771d5d737f4d6e7267777f36fc1876358..29fe43624e0c3c1f9275e1f96ab56e55ed9c7b9f 100644
--- a/src/app/calculators/pbbassin/fr.json
+++ b/src/app/calculators/pbbassin/fr.json
@@ -1,5 +1,6 @@
 {
-    "fs_basin_params": "Paramètres du bassin",
+    "basin_container": "Bassins",
+    "fs_basin": "Paramètres du bassin",
 
     "S": "Surface",
     "ZF": "Cote de fond"
diff --git a/src/app/calculators/prebarrage/config.json b/src/app/calculators/prebarrage/config.json
index 3474300cdb19deeb219ba7d619b6be49817c019c..7dada6df94c09621a3c55d9ba3475b3e2298a9f6 100644
--- a/src/app/calculators/prebarrage/config.json
+++ b/src/app/calculators/prebarrage/config.json
@@ -27,13 +27,21 @@
         "type": "subform",
         "config": [
             {
-                "id": "fs_basin_params",
-                "type": "fieldset",
+                "id": "fs_basin",
+                "type": "fieldset_template",
+                "calcType": "PbBassin",
                 "fields": [
                     "S",
                     "ZF"
                 ]
             },
+            {
+                "id": "basin_container",
+                "type": "template_container",
+                "templates": [
+                    "fs_basin"
+                ]
+            },
             {
                 "type": "options",
                 "selectIds": [ ]
diff --git a/src/app/components/basin-fieldset-container/basin-fieldset-container.component.ts b/src/app/components/basin-fieldset-container/basin-fieldset-container.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fd89b05dd27120941acf22ecc18e7476cde56bf9
--- /dev/null
+++ b/src/app/components/basin-fieldset-container/basin-fieldset-container.component.ts
@@ -0,0 +1,42 @@
+import { Component } from "@angular/core";
+
+import { I18nService } from "../../services/internationalisation.service";
+import { ApplicationSetupService } from "../../services/app-setup.service";
+import { FieldsetContainerComponent } from "../fieldset-container/fieldset-container.component";
+import { PrebarrageService } from "app/services/prebarrage.service";
+import { ServiceFactory } from "app/services/service-factory";
+import { FieldSet } from "app/formulaire/elements/fieldset";
+
+@Component({
+    selector: "basin-fieldset-container",
+    templateUrl: "../fieldset-container/fieldset-container.component.html",
+    styleUrls: [
+        "../fieldset-container/fieldset-container.component.scss"
+    ]
+})
+export class BasinFieldsetContainerComponent extends FieldsetContainerComponent {
+
+    constructor(i18nService: I18nService, appSetupService: ApplicationSetupService, private predamService: PrebarrageService) {
+        super(i18nService, appSetupService);
+    }
+
+    protected onFieldsetListChange() {
+        // disable "add" button (and "how many children" select)
+        setTimeout(() => // setTimeout to avoid ExpressionChangedAfterItHasBeenCheckedError
+            this._fieldsetComponents.forEach(fs => {
+                fs.showAddChildren = false;
+                fs.showMoveArrows = false;
+            }));
+    }
+
+    protected addSubNub(after: FieldSet, clone?: boolean): void {
+        const fsIndex = this._container.kidIndex(after);
+        this.predamService.copyBasinByIndex(fsIndex, ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit);
+    }
+
+    public onRemoveFieldset(fs: FieldSet) {
+        const fsIndex = this._container.kidIndex(fs);
+        super.onRemoveFieldset(fs);
+        this.predamService.deleteBasinByIndex(fsIndex, ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit);
+    }
+}
diff --git a/src/app/components/calculator-list/calculator-list.component.ts b/src/app/components/calculator-list/calculator-list.component.ts
index 3316be022c5f1bed65596a9451b452c8b8a03400..ec0ec059a9df1cea2998e70ecbdbe9f0112580fa 100644
--- a/src/app/components/calculator-list/calculator-list.component.ts
+++ b/src/app/components/calculator-list/calculator-list.component.ts
@@ -149,7 +149,7 @@ export class CalculatorListComponent implements OnInit {
         if (f instanceof FormulaireParallelStructure) {
             for (const e of f.allFormElements) {
                 if (e instanceof FieldsetContainer) {
-                    e.addFromTemplate(0);
+                    e.addFromTemplate();
                     break;
                 }
             }
@@ -158,7 +158,7 @@ export class CalculatorListComponent implements OnInit {
         if (f instanceof FormulairePab) {
             for (const e of f.allFormElements) {
                 if (e instanceof FieldsetContainer) {
-                    e.addFromTemplate(0);
+                    e.addFromTemplate();
                     break;
                 }
             }
@@ -167,7 +167,7 @@ export class CalculatorListComponent implements OnInit {
         if (f instanceof FormulaireMacrorugoCompound) {
             for (const e of f.allFormElements) {
                 if (e instanceof FieldsetContainer) {
-                    e.addFromTemplate(0, 0, f.currentNub.getChildren()[0]);
+                    e.addFromTemplate(0, f.currentNub.getChildren()[0]);
                     break;
                 }
             }
@@ -176,7 +176,7 @@ export class CalculatorListComponent implements OnInit {
         if (f instanceof FormulaireSPP) {
             for (const e of f.allFormElements) {
                 if (e instanceof FieldsetContainer) {
-                    e.addFromTemplate(0);
+                    e.addFromTemplate();
                     break;
                 }
             }
diff --git a/src/app/components/field-set/field-set.component.html b/src/app/components/field-set/field-set.component.html
index 808d883ec6ebc96030ee985ddf107647ff5c820f..88654611e535f4cf3834616084282c15d310ea6b 100644
--- a/src/app/components/field-set/field-set.component.html
+++ b/src/app/components/field-set/field-set.component.html
@@ -4,12 +4,12 @@
     </mat-card-title>
     <div class="hyd-window-btns">
         <span *ngIf="showButtons">
-            <mat-select id="add-many-children" [(value)]="childrenToAdd">
+            <mat-select *ngIf="showAddChildren" id="add-many-children" [(value)]="childrenToAdd">
                 <mat-option *ngFor="let i of addManyOptionsList" [value]="i">
                     {{ i }}
                 </mat-option>
             </mat-select>
-            <button type="button" mat-icon-button (click)="onAddClick()" class="add-structure"
+            <button *ngIf="showAddChildren" type="button" mat-icon-button (click)="onAddClick()" class="add-structure"
                 [title]="uitextAddStructure">
                 <mat-icon>add_box</mat-icon>
             </button>
@@ -22,11 +22,11 @@
                 [title]="uitextRemoveStructure">
                 <mat-icon>delete</mat-icon>
             </button>
-            <button type="button" mat-icon-button [disabled]="! enableUpButton" (click)="onMoveUpClick()"
+            <button *ngIf="showMoveArrows" type="button" mat-icon-button [disabled]="! enableUpButton" (click)="onMoveUpClick()"
                 [title]="uitextMoveStructureUp">
                 <mat-icon>arrow_upward</mat-icon>
             </button>
-            <button type="button" mat-icon-button [disabled]="! enableDownButton" (click)="onMoveDownClick()"
+            <button *ngIf="showMoveArrows" type="button" mat-icon-button [disabled]="! enableDownButton" (click)="onMoveDownClick()"
                 [title]="uitextMoveStructureDown">
                 <mat-icon>arrow_downward</mat-icon>
             </button>
@@ -48,4 +48,4 @@
         <select-field-line *ngIf="isSelectField(p)" [_select]=p>
         </select-field-line>
     </ng-template>
-</mat-card-content>
\ No newline at end of file
+</mat-card-content>
diff --git a/src/app/components/field-set/field-set.component.ts b/src/app/components/field-set/field-set.component.ts
index fa61d21cef01af81516a33cc19f3863aa7fc938a..293fce6e21ae0b137e9a6035b2d20e163739e269 100644
--- a/src/app/components/field-set/field-set.component.ts
+++ b/src/app/components/field-set/field-set.component.ts
@@ -28,6 +28,12 @@ export class FieldSetComponent implements DoCheck {
     /** number of children to add when clicking "add" or "clone" button */
     public childrenToAdd = 1;
 
+    /** flag to show/hide "add" button (and "how many children" select */
+    public showAddChildren: boolean = true;
+
+    /** flag to show/hide "move up" and "move down" buttons */
+    public showMoveArrows: boolean = true;
+
     @Input()
     public set fieldSet(fs: FieldSet) {
         this._fieldSet = fs;
diff --git a/src/app/components/fieldset-container/fieldset-container.component.ts b/src/app/components/fieldset-container/fieldset-container.component.ts
index 9d5664cf7bf0817ad0a61a60a960a24a2d383fae..aa9c6639ddf3c27535e57db4832d5a894f6531d7 100644
--- a/src/app/components/fieldset-container/fieldset-container.component.ts
+++ b/src/app/components/fieldset-container/fieldset-container.component.ts
@@ -32,13 +32,13 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
         return this._isValid.value;
     }
     @Input()
-    private _container: FieldsetContainer;
+    protected _container: FieldsetContainer;
 
     /**
      * liste des composants FieldSet enfants
      */
     @ViewChildren(FieldSetComponent)
-    private _fieldsetComponents: QueryList<FieldSetComponent>;
+    protected _fieldsetComponents: QueryList<FieldSetComponent>;
 
     /**
      * flag de validité des FieldSet enfants
@@ -83,8 +83,8 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
      * Ajoute un nouveau sous-nub (Structure, PabCloisons, YAXN… selon le cas)
      * dans un nouveau fieldset
      */
-    private addSubNub(after: FieldSet, clone: boolean = false) {
-        const newFs = this._container.addFromTemplate(0, after.indexAsKid());
+    protected addSubNub(after: FieldSet, clone: boolean = false) {
+        const newFs = this._container.addFromTemplate(after.indexAsKid());
         if (clone) {
             const prms = after.backupParameters();
             // replace in-place to change properties (overkill)
@@ -106,7 +106,7 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
         }
     }
 
-    private onFieldsetListChange() {
+    protected onFieldsetListChange() {
     }
 
     public ngAfterViewInit() {
@@ -204,7 +204,7 @@ export class FieldsetContainerComponent implements DoCheck, AfterViewInit {
      * clic sur le bouton "ajouter une structure"
      */
     public onAddStructureClick() {
-        this._container.addFromTemplate(0, 0);
+        this._container.addFromTemplate(0);
         this.validChange.emit();
     }
 
diff --git a/src/app/components/fixedvar-results/fixed-results.component.ts b/src/app/components/fixedvar-results/fixed-results.component.ts
index 7fb67ea7f92d6c3ca87749fdad152aaff2fe77b3..ae76c3e1989e993a78305a906f1a5d91c776ca21 100644
--- a/src/app/components/fixedvar-results/fixed-results.component.ts
+++ b/src/app/components/fixedvar-results/fixed-results.component.ts
@@ -95,7 +95,7 @@ export class FixedResultsComponent extends ResultsComponentDirective {
                     let label = this.formattedLabel(fp);
                     const nub = fp.paramDefinition.parentNub;
                     // add child type and position before label
-                    if (nub && nub.parent && nub.parent.childrenType) {
+                    if (nub && nub.parent && nub.intlType) {
                         const pos = nub.findPositionInParent();
                         // label = this.intlService.localizeText("INFO_OUVRAGE") + " n°" + (pos + 1) + ": " + label;
                         const cn = capitalize(this.intlService.childName(nub));
@@ -198,7 +198,7 @@ export class FixedResultsComponent extends ResultsComponentDirective {
             let label = this.formattedLabel(fp);
             const nub = fp.paramDefinition.parentNub;
             // add child type and position before label
-            if (nub && nub.parent && nub.parent.childrenType) {
+            if (nub && nub.parent && nub.intlType) {
                 const pos = nub.findPositionInParent();
                 const cn = capitalize(this.intlService.childName(nub));
                 label = sprintf(this.intlService.localizeText("INFO_STUFF_N"), cn)
diff --git a/src/app/components/generic-calculator/calculator.component.html b/src/app/components/generic-calculator/calculator.component.html
index a560138f592c5d44b3a3b8263fda1e3632cb05ab..a61455db06fc8271a318e1955d57b18ba30bcfee 100644
--- a/src/app/components/generic-calculator/calculator.component.html
+++ b/src/app/components/generic-calculator/calculator.component.html
@@ -95,7 +95,8 @@
                         <div *ngIf="isPB" id="pb-form-container" [hidden]="! showPBInputData"
                             fxFlex.gt-sm="1 0 400px"
                             fxFlex.lt-md="1 0 500px"
-                            fxFlex.lt-sm="1 0 300px">
+                            fxFlex.lt-sm="1 0 300px"
+                            fxLayout="column">
 
                             <ng-template ngFor let-fe [ngForOf]="formElements">
                                 <field-set *ngIf="isFieldset(fe)"
@@ -104,11 +105,18 @@
                                     (tabPressed)="onTabPressed($event)">
                                 </field-set>
 
-                                <fieldset-container *ngIf="isFieldsetContainer(fe)"
+                                <structure-fieldset-container *ngIf="isStructureFieldsetContainer(fe)"
                                     [style.display]="getElementStyleDisplay(fe.id)" [_container]=fe
                                     (radio)=onRadioClick($event) (validChange)=onElementValid() (inputChange)=onInputChange($event)
                                     (tabPressed)="onTabPressed($event)">
-                                </fieldset-container>
+                                </structure-fieldset-container>
+
+                                <basin-fieldset-container *ngIf="isBasinFieldsetContainer(fe)"
+                                    [style.display]="getElementStyleDisplay(fe.id)" [_container]=fe
+                                    (radio)=onRadioClick($event) (validChange)=onElementValid()
+                                    (inputChange)=onInputChange($event) (tabPressed)="onTabPressed($event)"
+                                    fxFlex="1 0 auto">
+                                </basin-fieldset-container>
                             </ng-template>
                         </div>
 
@@ -130,6 +138,13 @@
                                 fxFlex="1 0 auto">
                             </fieldset-container>
 
+                            <structure-fieldset-container *ngIf="isStructureFieldsetContainer(fe)"
+                                [style.display]="getElementStyleDisplay(fe.id)" [_container]=fe
+                                (radio)=onRadioClick($event) (validChange)=onElementValid() (inputChange)=onInputChange($event)
+                                (tabPressed)="onTabPressed($event)"
+                                fxFlex="1 0 auto">
+                            </structure-fieldset-container>
+
                             <pab-table *ngIf="isPabTable(fe)" [pabTable]=fe (radio)=onRadioClick($event)
                                 (validChange)=onElementValid() (inputChange)=onInputChange($event)
                                 fxFlex="1 0 auto">
@@ -206,4 +221,4 @@
 
     </form>
 
-</mat-card>
\ No newline at end of file
+</mat-card>
diff --git a/src/app/components/generic-calculator/calculator.component.ts b/src/app/components/generic-calculator/calculator.component.ts
index 06c2735f6bd9098e41525317badc6a6388942691..901fa13436d5ae5e5bf4f864a65dafa7a1c0e1e0 100644
--- a/src/app/components/generic-calculator/calculator.component.ts
+++ b/src/app/components/generic-calculator/calculator.component.ts
@@ -199,9 +199,35 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         return fe instanceof FieldSet;
     }
 
-    /** détermine si un FormulaireElement est du type FieldsetContainer */
+    /** détermine si un FormulaireElement est du type FieldsetContainer et générique */
     public isFieldsetContainer(fe: any): boolean {
-        return fe instanceof FieldsetContainer;
+        if (fe instanceof FieldsetContainer) {
+            switch (fe.template.calcTypeFromConfig) {
+                case CalculatorType.Structure:
+                case CalculatorType.PbBassin:
+                    return false;
+
+                default:
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /** détermine si un FormulaireElement est du type FieldsetContainer contenant des structures */
+    public isStructureFieldsetContainer(fe: any): boolean {
+        if (fe instanceof FieldsetContainer) {
+            return fe.template.calcTypeFromConfig === CalculatorType.Structure;
+        }
+        return false;
+    }
+
+    /** détermine si un FormulaireElement est du type FieldsetContainer contenant des bassins */
+    public isBasinFieldsetContainer(fe: any): boolean {
+        if( fe instanceof FieldsetContainer){
+            return fe.template.calcTypeFromConfig === CalculatorType.PbBassin;
+        }
+        return false;
     }
 
     /** détermine si un FormulaireElement est du type PabTable */
@@ -480,7 +506,7 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
         if (!this._formulaire.calculateDisabled) {
             // all fieldsets must be valid
             res = true;
-            if (this._fieldsetComponents !== undefined) {
+            if (this._fieldsetComponents?.length > 0) {
                 res = res && this._fieldsetComponents.reduce(
                     // callback
                     (
@@ -496,10 +522,10 @@ export class GenericCalculatorComponent implements OnInit, DoCheck, AfterViewChe
                         return acc && fieldset.isValid;
                     }
                     // valeur initiale
-                    , this._fieldsetComponents.length > 0);
+                    , true);
             }
             // all fieldset containers must be valid
-            if (this._fieldsetContainerComponents !== undefined) {
+            if (this._fieldsetContainerComponents?.length > 0) {
                 res = res && this._fieldsetContainerComponents.reduce<boolean>(
                     // callback
                     (
diff --git a/src/app/components/generic-input/generic-input.component.ts b/src/app/components/generic-input/generic-input.component.ts
index 3154a308fbdf1b9a83750ca2668e046f5bf5f578..d95168143cb67edb5fff3ffe00586e65fe60ad4f 100644
--- a/src/app/components/generic-input/generic-input.component.ts
+++ b/src/app/components/generic-input/generic-input.component.ts
@@ -36,7 +36,7 @@ export abstract class GenericInputComponentDirective implements OnChanges {
                 id = param.symbol;
                 // if inside a child Nub, prefix with child position to disambiguate
                 const nub = param.paramDefinition.parentNub;
-                if (nub && nub.parent && nub.parent.childrenType) {
+                if (nub && nub.parent && nub.intlType) {
                     id = nub.findPositionInParent() + "_" + id;
                 }
             }
diff --git a/src/app/components/param-computed/param-computed.component.ts b/src/app/components/param-computed/param-computed.component.ts
index 5525dcf7ab3cc4aa308f25491f006f7e755e4b87..85d6f4070e3d55302ecb29f89e3b812c587b79ec 100644
--- a/src/app/components/param-computed/param-computed.component.ts
+++ b/src/app/components/param-computed/param-computed.component.ts
@@ -32,7 +32,7 @@ export class ParamComputedComponent {
         let id = "calc_" + this.param.symbol;
         // if inside a child Nub, prefix with child position to disambiguate
         const nub = this.param.paramDefinition.parentNub;
-        if (nub && nub.parent && nub.parent.childrenType) {
+        if (nub && nub.parent && nub.intlType) {
             id = nub.findPositionInParent() + "_" + id;
         }
         return id;
diff --git a/src/app/components/param-link/param-link.component.ts b/src/app/components/param-link/param-link.component.ts
index 2571d9ec7cd0fab288fc147345c7e5ced4352a59..8e03ffeb2f39f59a2654949b2b702cf184f3deff 100644
--- a/src/app/components/param-link/param-link.component.ts
+++ b/src/app/components/param-link/param-link.component.ts
@@ -40,7 +40,7 @@ export class ParamLinkComponent implements OnChanges, Observer, OnDestroy {
         let id = "linked_" + this.param.symbol;
         // if inside a child Nub, prefix with child position to disambiguate
         const nub = this.param.paramDefinition.parentNub;
-        if (nub && nub.parent && nub.parent.childrenType) {
+        if (nub && nub.parent && nub.intlType) {
             id = nub.findPositionInParent() + "_" + id;
         }
         return id;
diff --git a/src/app/components/param-values/param-values.component.ts b/src/app/components/param-values/param-values.component.ts
index eeb36a21d27a6d06cca3f76a449ee6976d181f00..8f3959026a6006caa0f09cf6fcd0778d123f8988 100644
--- a/src/app/components/param-values/param-values.component.ts
+++ b/src/app/components/param-values/param-values.component.ts
@@ -47,7 +47,7 @@ export class ParamValuesComponent implements AfterViewInit, Observer {
         let id = "var_" + this.param.symbol;
         // if inside a child Nub, prefix with child position to disambiguate
         const nub = this.param.paramDefinition.parentNub;
-        if (nub && nub.parent && nub.parent.childrenType) {
+        if (nub && nub.parent && nub.intlType) {
             id = nub.findPositionInParent() + "_" + id;
         }
         return id;
diff --git a/src/app/components/pb-schema/pb-schema.component.ts b/src/app/components/pb-schema/pb-schema.component.ts
index f294c65e1c3933f926c54bbad53077be75cde7df..7ac6b72a7bc7a7ac2c2d6414e1bd11deccce26f7 100644
--- a/src/app/components/pb-schema/pb-schema.component.ts
+++ b/src/app/components/pb-schema/pb-schema.component.ts
@@ -4,8 +4,8 @@ import { MatDialog } from "@angular/material/dialog";
 import screenfull from "screenfull";
 
 import {
-    PreBarrage, PbBassin, PbBassinParams, PbCloison, Observer, IObservable, MermaidUtil
- } from "jalhyd";
+    PbBassin, PbCloison, Observer, IObservable, MermaidUtil
+} from "jalhyd";
 
 import mermaid from "mermaid";
 
@@ -19,9 +19,9 @@ import { FormulairePrebarrage } from "../../formulaire/definition/form-prebarrag
 import { AppComponent } from "../../app.component";
 
 import { fv } from "app/util";
-import { FormulaireNode } from "app/formulaire/elements/formulaire-node";
 import { ServiceFactory } from "app/services/service-factory";
 import { DefinedBoolean } from "app/definedvalue/definedboolean";
+import { PrebarrageService, PrebarrageServiceEvents } from "app/services/prebarrage.service";
 
 /**
  * The interactive schema for calculator type "PreBarrage" (component)
@@ -47,10 +47,6 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
     /** flag de validité du composant */
     private _isValid: DefinedBoolean;
 
-    private upstreamId = "amont";
-
-    private downstreamId = "aval";
-
     /** événément de changement de validité */
     @Output()
     private validChange = new EventEmitter();
@@ -59,9 +55,6 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
     @Output()
     private nodeSelected = new EventEmitter();
 
-    /** underlying PB */
-    private model: PreBarrage;
-
     /** Latest clicked item: a PbCloison, a PbBassin or undefined if river "Upstream" or "Downstream" was clicked */
     private _selectedItem: PbCloison | PbBassin;
 
@@ -72,10 +65,12 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         @Inject(forwardRef(() => GenericCalculatorComponent)) private calculatorComponent: GenericCalculatorComponent,
         private i18nService: I18nService,
         private hotkeysService: HotkeysService,
-        private newPbCloisonDialog: MatDialog
+        private newPbCloisonDialog: MatDialog,
+        private predamService: PrebarrageService
     ) {
         this.hotkeysService.add(new Hotkey("del", AppComponent.onHotkey(this.removeOnHotkey, this)));
         this._isValid = new DefinedBoolean();
+        this.predamService.changeEventEmitter.subscribe(event => this.onPredamServiceEvent(event));
     }
 
     /** tracks the fullscreen state */
@@ -102,10 +97,6 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
     /** called when fullscreen state changes */
     public fullscreenChange(isFullscreen: boolean) { }
 
-    public get selectedItem(): any {
-        return this._selectedItem;
-    }
-
     public ngAfterContentInit(): void {
         mermaid.initialize({
             flowchart: {
@@ -116,7 +107,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         this.nativeElement = this.schema.nativeElement;
         this.render();
         // restore previously selected item
-        this._selectedItem = this.pbSchema.form.selectedItem;
+        this._selectedItem = this.predamService.setSelectedNub(this.pbSchema.form.selectedItem);
         if (this._selectedItem !== undefined) {
             // @WARNING clodo timeout to prevent ExpressionChangedAfterItHasBeenCheckedError
             // and select schema node after schema is refreshed by ngAfterViewInit()
@@ -185,36 +176,38 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         this.pbSchema.wallsSuffixes = {};
         const def: string[] = [ "graph TB" ];
 
+        const pbModel = this.predamService.model;
+
         // river upstream / downstream
         let upstreamLabel = this.i18nService.localizeText("INFO_LIB_AMONT");
         let downstreamLabel = this.i18nService.localizeText("INFO_LIB_AVAL");
         // add result data Z and Q, if any
         if (
-            this.model?.result?.resultElements
-            && this.model.result.resultElements[0]?.ok
+            pbModel.result?.resultElements
+            && pbModel.result.resultElements[0]?.ok
         ) {
             // when a parameter is variating, index of the variating parameter
             // values to build the data from
             const form = this.calculatorComponent.formulaire as FormulairePrebarrage;
             const idx = form.pbResults.variableIndex;
-            const qValue = this.model.prms.Q.isCalculated
-                ? this.model.result.resultElements[idx].vCalc
+            const qValue = pbModel.prms.Q.isCalculated
+                ? pbModel.result.resultElements[idx].vCalc
                 : (
-                    this.model.prms.Q.hasMultipleValues
-                        ? this.model.prms.Q.getInferredValuesList(this.model.variatingLength())[idx]
-                        : this.model.prms.Q.singleValue
+                    pbModel.prms.Q.hasMultipleValues
+                        ? pbModel.prms.Q.getInferredValuesList(pbModel.variatingLength())[idx]
+                        : pbModel.prms.Q.singleValue
                 );
             // upstream
             upstreamLabel += "<br>";
             upstreamLabel += "Q = " + fv(qValue);
             upstreamLabel += "<br>";
             upstreamLabel += "Z = " + fv(
-                this.model.prms.Z1.isCalculated
-                    ? this.model.result.resultElements[idx].vCalc
+                pbModel.prms.Z1.isCalculated
+                    ? pbModel.result.resultElements[idx].vCalc
                     : (
-                        this.model.prms.Z1.hasMultipleValues
-                        ? this.model.prms.Z1.getInferredValuesList(this.model.variatingLength())[idx]
-                            : this.model.prms.Z1.singleValue
+                        pbModel.prms.Z1.hasMultipleValues
+                            ? pbModel.prms.Z1.getInferredValuesList(pbModel.variatingLength())[idx]
+                            : pbModel.prms.Z1.singleValue
                     )
             );
             // downstream
@@ -222,14 +215,14 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
             downstreamLabel += "Q = " + fv(qValue);
             downstreamLabel += "<br>";
             downstreamLabel += "Z = " + fv(
-                this.model.prms.Z2.hasMultipleValues
-                    ? this.model.prms.Z2.getInferredValuesList(this.model.variatingLength())[idx]
-                    : this.model.prms.Z2.singleValue
-                );
+                pbModel.prms.Z2.hasMultipleValues
+                    ? pbModel.prms.Z2.getInferredValuesList(pbModel.variatingLength())[idx]
+                    : pbModel.prms.Z2.singleValue
+            );
         }
         // add to graph definition
-        def.push(`${this.upstreamId}("${upstreamLabel}")`);
-        def.push(`${this.downstreamId}("${downstreamLabel}")`);
+        def.push(`${this.predamService.upstreamId}("${upstreamLabel}")`);
+        def.push(`${this.predamService.downstreamId}("${downstreamLabel}")`);
 
         // styles
         def.push("classDef wall fill:#e8e8e8,stroke-width:0;");
@@ -240,7 +233,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         def.push("classDef node-highlighted-error fill:#d92f03;"); // irstea-rouille (material "accent"), 900
 
         const sortedWalls: PbCloison[] = [];
-        for (const c of this.model.children) {
+        for (const c of pbModel.children) {
             if (c instanceof PbBassin) {
                 def.push(`${c.uid}("${this.itemDescriptionWithResultData(c)}")`); // rounded edges
                 def.push(`class ${c.uid} basin;`);
@@ -253,8 +246,8 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         // sort then draw walls
         sortedWalls.sort(this.triCloisonsGaucheDroite);
         for (const c of sortedWalls) {
-            const upstreamBasinId = c.bassinAmont === undefined ? this.upstreamId : c.bassinAmont.uid;
-            const downstreamBasinId = c.bassinAval === undefined ? this.downstreamId : c.bassinAval.uid;
+            const upstreamBasinId = c.bassinAmont === undefined ? this.predamService.upstreamId : c.bassinAmont.uid;
+            const downstreamBasinId = c.bassinAval === undefined ? this.predamService.downstreamId : c.bassinAval.uid;
             // record this wall
             const basinsPair = upstreamBasinId + "-" + downstreamBasinId;
             if (! (basinsPair in this.existingWalls)) {
@@ -307,22 +300,6 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         return (sommeA <= sommeB ? -1 : 1);
     }
 
-    /**
-     * check if a list of (Mermaid transformed) ids matches a given id
-     * @param ids ids to transform the Mermaid way
-     * @param itemId id to find
-     */
-    private matchMermaidIds(ids: string[], itemId: string): boolean {
-        return ids.find(id => MermaidUtil.isMermaidEqualIds(id, itemId)) !== undefined;
-    }
-
-    /**
-     * return upstream (and downstream) basin objet
-     */
-    private get upstreamBassin(): PbBassin {
-        return (this.model as unknown) as PbBassin;
-    }
-
     /**
      * @param item DOM element
      */
@@ -332,15 +309,11 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         this.clearHighlightedItems();
         item.classList.add("node-highlighted");
         // find what was clicked
-        if (this.matchMermaidIds([this.upstreamId, this.downstreamId], item.id)) {
-            this._selectedItem = this.upstreamBassin;
-        } else {
-            this._selectedItem = this.model.findChild(item.id);
-        }
+        this._selectedItem = this.predamService.setSelectedNub(this.predamService.findFromItemId(item.id));
         this.highlightErrorItems(item.id);
         // show proper form and hide results
         this.nodeSelected.emit({
-            node: this._selectedItem === this.upstreamBassin ? undefined : this._selectedItem
+            node: this._selectedItem === this.predamService.upstreamBassin ? undefined : this._selectedItem
         });
         // exit fullscreen
         this.exitFullscreen();
@@ -445,7 +418,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
             if (!done) {
                 if (MermaidUtil.isMermaidEqualIds("amont", item.id)) {
                     this.selectNode(item);
-                    this._selectedItem = this.upstreamBassin;
+                    this._selectedItem = this.predamService.setSelectedNub(this.predamService.upstreamBassin);
                     done = true;
                 }
             }
@@ -454,7 +427,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
 
     // at this time @Input data is supposed to be already populated
     public ngOnInit() {
-        this.model = this.pbSchema.pb;
+        this.predamService.model = this.pbSchema.pb;
     }
 
     public get enableAddItems(): boolean {
@@ -462,7 +435,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
     }
 
     public get enableRemoveButton() {
-        if (this._selectedItem === this.upstreamBassin) {
+        if (this._selectedItem === this.predamService.upstreamBassin) {
             return false;
         }
         // if deleting a PbCloison would replace it by a new one at
@@ -485,24 +458,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
 
     /** Removes a basin or wall, and all related items */
     public onRemoveClick() {
-        this.model.deleteChild(this._selectedItem.findPositionInParent());
-        // never let an unconnected basin ! (not done in model to prevent unwanted
-        // automatic child addition when clearing children)
-        if (this._selectedItem instanceof PbCloison) {
-            const emptyFields: boolean = ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit;
-            // if no downstream connections remain, connect to river downstream
-            if (this._selectedItem.bassinAmont?.cloisonsAval.length === 0) {
-                this.model.addChild(new PbCloison(this._selectedItem.bassinAmont, undefined, undefined, emptyFields));
-            }
-            // if no upstream connections remain, connect to river upstream
-            if (this._selectedItem.bassinAval?.cloisonsAmont.length === 0) {
-                this.model.addChild(new PbCloison(undefined, this._selectedItem.bassinAval, undefined, emptyFields));
-            }
-        }
-        this.clearResults();
-        this.unselect();
-        this.refreshWithSelection();
-        this.calculatorComponent.showPBInputData = true;
+        this.predamService.deleteSelected(ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit);
     }
 
     public get uitextRemove() {
@@ -517,18 +473,18 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
     }
 
     public get enableCopyButton() {
-        return (this._selectedItem !== undefined && this._selectedItem instanceof PbCloison);
+        // disable copy for upstream/downstream basins
+        return this._selectedItem !== this.predamService.upstreamBassin;
     }
 
-    /** Copies a wall */
+    /** Copies a wall or a basin */
     public onCopyClick() {
-        const wall = this._selectedItem as PbCloison;
-        const wallCopy = new PbCloison(wall.bassinAmont, wall.bassinAval, undefined, ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit);
-        wallCopy.loadObjectRepresentation(wall.objectRepresentation());
-        this.model.addChild(wallCopy);
-        this.clearResults();
-        this.refreshWithSelection(wallCopy.uid);
-        this.calculatorComponent.showPBInputData = true;
+        if (this._selectedItem instanceof PbCloison) {
+            this.predamService.copySelectedWall(ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit);
+        }
+        else {
+            this.predamService.copySelectedBasin(ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit);
+        }
     }
 
     public get uitextCopy() {
@@ -537,11 +493,30 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
 
     /** Adds a new lone basin */
     public onAddBasinClick() {
-        const newBasin = new PbBassin(new PbBassinParams(20, 99, ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit));
-        this.model.addChild(newBasin);
-        this.clearResults();
-        this.refreshWithSelection(newBasin.uid);
-        this.calculatorComponent.showPBInputData = true;
+        this.predamService.addBasin(ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit);
+    }
+
+    /**
+     * process events from PrebarrageService
+     */
+    private onPredamServiceEvent(event: any) {
+        const nub = event.data;
+        switch (event.id) {
+            case PrebarrageServiceEvents.BASIN_WALL_ADDED:
+            case PrebarrageServiceEvents.BASIN_WALL_COPIED:
+            case PrebarrageServiceEvents.MOVE_BASIN:
+                this.clearResults();
+                this.refreshWithSelection(nub.uid);
+                this.calculatorComponent.showPBInputData = true;
+                break;
+
+            case PrebarrageServiceEvents.BASIN_WALL_REMOVED:
+                this.clearResults();
+                this.unselect();
+                this.refreshWithSelection();
+                this.calculatorComponent.showPBInputData = true;
+                break;
+        }
     }
 
     public get uitextAddBasin() {
@@ -549,7 +524,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
     }
 
     public get enableAddWallButton(): boolean {
-        return (this.model.bassins.length > 0);
+        return this.predamService.hasBasins;
     }
 
     /** Adds a new lone wall, opening a modal to choose connected basins */
@@ -559,7 +534,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
             DialogNewPbCloisonComponent,
             {
                 data: {
-                    basins: this.model.bassins
+                    basins: this.predamService.bassins
                 },
                 disableClose: true
             }
@@ -567,16 +542,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         // apply modifications
         dialogRef.afterClosed().subscribe(result => {
             if (result.up !== undefined && result.down !== undefined) {
-                const wall = new PbCloison(
-                    result.up === 0 ? undefined : this.model.bassins[result.up - 1],
-                    result.down === 0 ? undefined : this.model.bassins[result.down - 1],
-                    undefined,
-                    ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit
-                );
-                this.model.addChild(wall);
-                this.clearResults();
-                this.refreshWithSelection(wall.uid);
-                this.calculatorComponent.showPBInputData = true;
+                this.predamService.addWall(result.up, result.down, ServiceFactory.applicationSetupService.enableEmptyFieldsOnFormInit);
             }
         });
     }
@@ -588,19 +554,15 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
     public get enableUpButton() {
         return (
             this._selectedItem instanceof PbBassin
-            && this.model.findBasinPosition(this._selectedItem.uid) !== 0
-            && this.isStandaloneBasin(this._selectedItem)
+            && this.predamService.findBasinPosition(this._selectedItem.uid) !== 0
+            && this.predamService.isStandaloneBasin(this._selectedItem)
         );
     }
 
     public onMoveBasinUpClick() {
         if (this._selectedItem instanceof PbBassin) {
-            this.model.moveBasin(this._selectedItem.uid, this.model.findBasinPosition(this._selectedItem.uid) - 1);
+            this.predamService.moveSelectedBasinUp();
         }
-        const basin = this._selectedItem; // utilité ?
-        this.clearResults();
-        this.refreshWithSelection(this._selectedItem.uid);
-        this.calculatorComponent.showPBInputData = true;
     }
 
     public get uitextMoveBasinUp() {
@@ -610,19 +572,15 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
     public get enableDownButton() {
         return (
             this._selectedItem instanceof PbBassin
-            && this.model.findBasinPosition(this._selectedItem.uid) !== this.model.bassins.length - 1
-            && this.isStandaloneBasin(this._selectedItem)
+            && !this.predamService.isLastBasin(this._selectedItem.uid)
+            && this.predamService.isStandaloneBasin(this._selectedItem)
         );
     }
 
     public onMoveBasinDownClick() {
         if (this._selectedItem instanceof PbBassin) {
-            this.model.moveBasin(this._selectedItem.uid, this.model.findBasinPosition(this._selectedItem.uid) + 1);
+            this.predamService.moveSelectedBasinDown();
         }
-        const basin = this._selectedItem;
-        this.clearResults();
-        this.refreshWithSelection(this._selectedItem.uid);
-        this.calculatorComponent.showPBInputData = true;
     }
 
     public get uitextMoveBasinDown() {
@@ -665,29 +623,13 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         image.src = blobURL;
     }
 
-    /**
-     * Returns true if given basin is either connected to nothing, or only to
-     * river upstream or downstream
-     */
-    private isStandaloneBasin(basin: PbBassin) {
-        return (
-            (
-                basin.cloisonsAmont.length === 0
-                || basin.cloisonsAmont.map(c => c.bassinAmont).every(e => e === undefined)
-            ) && (
-                basin.cloisonsAval.length === 0
-                || basin.cloisonsAval.map(c => c.bassinAval).every(e => e === undefined)
-            )
-        );
-    }
-
     /**
      * Computes the global Pab validity : validity of every cell of every row
      */
     private updateValidity() {
         // check that at least 1 basin is present and a route from river
         // upstream to river downstream exists (2nd check includes 1st)
-        this._isValid.value = this.model.hasUpDownConnection() && !this.model.hasBasinNotConnected();
+        this._isValid.value = this.predamService.isValid();
 
         if (this._isValid.changed) {
             this.validChange.emit();
@@ -707,32 +649,17 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
         });
     }
 
-    /**
-     * turn 'aval', 'amont' and Mermaid ids to nub real uid
-     */
-    private toNubUid(id: string): string {
-        if (id !== undefined && id !== null) {
-            if (this.matchMermaidIds([this.upstreamId, this.downstreamId], id)) {
-                return this.model.uid;
-            }
-            if (id.startsWith("flowchart-")) { // Mermaid style id (ie. flowchart-id-xx)
-                return id.split('-')[1];
-            }
-        }
-        return id;
-    }
-
     private highlightErrorItems(selectedUid: string) {
         this.nativeElement.querySelectorAll("g.node").forEach(item => {
             item.classList.remove("node-error");
             item.classList.remove("node-highlighted-error");
         });
         const invalidUids: string[] = this.pbSchema.form.checkParameters();
-        selectedUid = this.toNubUid(selectedUid);
+        selectedUid = this.predamService.toNubUid(selectedUid);
         if (invalidUids.length > 0) {
             this.nativeElement.querySelectorAll("g.node").forEach(item => {
                 // in this case, item is a HTML node of the SVG schema which id is a nub uid
-                const itemId = this.toNubUid(item.id);
+                const itemId = this.predamService.toNubUid(item.id);
 
                 if (invalidUids.includes(itemId)) {  // if current item is among invalid ones
                     if (selectedUid === itemId) { // if current item is the selected item
@@ -747,7 +674,7 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
 
     private unselect() {
         // console.debug(`PbSchemaComponent.unselect()`);
-        this._selectedItem = undefined;
+        this._selectedItem = this.predamService.setSelectedNub(undefined);
         this.clearHighlightedItems();
         this.nodeSelected.emit({}); // nothing selected
     }
@@ -761,17 +688,17 @@ export class PbSchemaComponent implements AfterViewInit, AfterContentInit, OnIni
      * Refreshes the schema; if uid is given, selects the node having this
      * nub uid, else keeps previous selection
      */
-    private refreshWithSelection(uid ?: string) {
+    private refreshWithSelection(uid?: string) {
         // console.debug(`PbSchemaComponent.refreshWithSelection(${uid})`);
         // remember previously selected node
         const selectedNodeUID = this._selectedItem?.uid;
         this.refresh();
         // select a specific node on the schema
         if (uid !== undefined) {
-            this.selectNodeOnSchema(this.model.findChild(uid));
+            this.selectNodeOnSchema(this.predamService.findChild(uid));
         } else if (selectedNodeUID !== undefined) {
             // re-select previously selected node
-            this.selectNodeOnSchema(this.model.findChild(selectedNodeUID));
+            this.selectNodeOnSchema(this.predamService.findChild(selectedNodeUID));
         }
     }
 
diff --git a/src/app/components/structure-fieldset-container/structure-fieldset-container.component.ts b/src/app/components/structure-fieldset-container/structure-fieldset-container.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..34b9a3bbabc1b14e4ef87fcffa44b47e48d4efd6
--- /dev/null
+++ b/src/app/components/structure-fieldset-container/structure-fieldset-container.component.ts
@@ -0,0 +1,20 @@
+import { Component } from "@angular/core";
+
+import { FieldSet } from "../../formulaire/elements/fieldset";
+import { I18nService } from "../../services/internationalisation.service";
+import { ApplicationSetupService } from "../../services/app-setup.service";
+import { FieldsetContainerComponent } from "../fieldset-container/fieldset-container.component";
+
+@Component({
+    selector: "structure-fieldset-container",
+    templateUrl: "../fieldset-container/fieldset-container.component.html",
+    styleUrls: [
+        "../fieldset-container/fieldset-container.component.scss"
+    ]
+})
+export class StructureFieldsetContainerComponent extends FieldsetContainerComponent {
+
+    constructor(i18nService: I18nService, appSetupService: ApplicationSetupService) {
+        super(i18nService, appSetupService);
+    }
+}
diff --git a/src/app/formulaire/definition/form-macrorugo-compound.ts b/src/app/formulaire/definition/form-macrorugo-compound.ts
index b28b59b7d21e335b8605e9d7b84ce1851eb4836d..ec470e645ca81406ae5e353736467e77427508ba 100644
--- a/src/app/formulaire/definition/form-macrorugo-compound.ts
+++ b/src/app/formulaire/definition/form-macrorugo-compound.ts
@@ -98,7 +98,7 @@ export class FormulaireMacrorugoCompound extends FormulaireRepeatableFieldset {
                 if (elt instanceof FieldsetContainer) {
                     elt.clearKids();
                     for (const c of this.currentNub.getChildren()) {
-                        elt.addFromTemplate(0, undefined, c);
+                        elt.addFromTemplate(undefined, c);
                     }
                 }
             }
diff --git a/src/app/formulaire/definition/form-prebarrage.ts b/src/app/formulaire/definition/form-prebarrage.ts
index 8f317caba59b6b5218a157daccf1b6cc6ffd2884..95e5f65498b3ad929c00730266a3fa0ccb33d221 100644
--- a/src/app/formulaire/definition/form-prebarrage.ts
+++ b/src/app/formulaire/definition/form-prebarrage.ts
@@ -147,6 +147,24 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
                 this.basinForm.preparseConfig(this.basinFormConfig);
                 this.basinForm.parseConfig(this.basinFormConfig);
                 ServiceFactory.formulaireService.updateFormulaireLocalisation(this.basinForm);
+                // add fieldsets for existing basins
+                if (node.parent.bassins.length > 0) {
+                    for (const bassin of node.parent.bassins) {
+                        for (const e of this.basinForm.allFormElements) {
+                            if (e instanceof FieldsetContainer) { // @TODO manage many containers one day ?
+                                e.addFromTemplate(undefined, bassin, { resetResults: false });
+                            }
+                        }
+                    }
+                } else {
+                    // if there was no existing basin, add a default one
+                    for (const e of this.basinForm.allFormElements) {
+                        if (e instanceof FieldsetContainer) {
+                            e.addFromTemplate(undefined, undefined, { resetResults: false });
+                            break;
+                        }
+                    }
+                }
                 this.showFormElements(this.basinForm);
 
             } else if (node instanceof PbCloison) {
@@ -163,7 +181,7 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
                     for (const struct of node.structures) {
                         for (const e of this.wallForm.allFormElements) {
                             if (e instanceof FieldsetContainer) { // @TODO manage many containers one day ?
-                                e.addFromTemplate(0, undefined, struct, { resetResults: false });
+                                e.addFromTemplate(undefined, struct, { resetResults: false });
                             }
                         }
                     }
@@ -171,7 +189,7 @@ export class FormulairePrebarrage extends FormulaireFixedVar {
                     // if there was no existing structure, add a default one
                     for (const e of this.wallForm.allFormElements) {
                         if (e instanceof FieldsetContainer) {
-                            e.addFromTemplate(0, undefined, undefined, { resetResults: false });
+                            e.addFromTemplate(undefined, undefined, { resetResults: false });
                             break;
                         }
                     }
diff --git a/src/app/formulaire/elements/fieldset-container.ts b/src/app/formulaire/elements/fieldset-container.ts
index a8c1ecbe60f98fabcfd8ad96e80465e1f23389c6..23b5488953e9c72500de7ecd8babd9bbacf86948 100644
--- a/src/app/formulaire/elements/fieldset-container.ts
+++ b/src/app/formulaire/elements/fieldset-container.ts
@@ -5,21 +5,23 @@ import { FormulaireNode } from "./formulaire-node";
 import { Nub } from "jalhyd";
 
 export class FieldsetContainer extends FormulaireElement {
-    private _templates: FieldsetTemplate[];
+    private _template: FieldsetTemplate;
 
     public title: string;
 
     constructor(parent: FormulaireNode) {
         super(parent);
-        this._templates = [];
     }
 
-    private addTemplate(fst: FieldsetTemplate) {
-        this._templates.push(new FieldsetTemplate(fst));
+    public get template(): FieldsetTemplate {
+        return this._template;
     }
 
-    public getTemplate(index: number): FieldsetTemplate {
-        return this._templates[index];
+    public set template(fst: FieldsetTemplate) {
+        if (this._template !== undefined) {
+            throw new Error("template already set!");
+        }
+        this._template = new FieldsetTemplate(fst);
     }
 
     public addFieldset(fs: FieldSet) {
@@ -74,10 +76,8 @@ export class FieldsetContainer extends FormulaireElement {
      * @param nub attaches the given Nub to the new FieldSet
      * @param extra extra key-value pairs to add to the "newFieldset" event
      */
-    public addFromTemplate(templateIndex: number, after?: number, nub?: Nub, extra?: any): FieldSet {
-        const templ: FieldsetTemplate = this._templates[templateIndex];
-
-        const inst: FieldSet = templ.instantiateTemplate(this, after, nub, extra);
+    public addFromTemplate(after?: number, nub?: Nub, extra?: any): FieldSet {
+        const inst: FieldSet = this._template.instantiateTemplate(this, after, nub, extra);
 
         this.updateLocalisation();
 
@@ -105,7 +105,7 @@ export class FieldsetContainer extends FormulaireElement {
         for (const t of templateNames) {
             for (const d of templates) {
                 if (d.id === t) {
-                    this.addTemplate(d);
+                    this.template = d;
                 }
             }
         }
diff --git a/src/app/formulaire/elements/formulaire-node.ts b/src/app/formulaire/elements/formulaire-node.ts
index b57bec770d2d128fc76e2fcd53a43370f0efd667..ebefb5e58d26bf1dc33a1144407a3ccb03250f54 100644
--- a/src/app/formulaire/elements/formulaire-node.ts
+++ b/src/app/formulaire/elements/formulaire-node.ts
@@ -95,7 +95,7 @@ export abstract class FormulaireNode implements IObservable {
      * @param kid FormulaireNode enfant dont on cherche l'indice
      * @return -1 si non trouvé, indice commençant à 0 sinon
      */
-    private kidIndex(kid: FormulaireNode): number {
+    public kidIndex(kid: FormulaireNode): number {
         let n = 0;
         for (const k of this._kids) {
             if (k._uid === kid._uid) {
diff --git a/src/app/formulaire/elements/ngparam.ts b/src/app/formulaire/elements/ngparam.ts
index 188f87fd4b451d41b3ff11b9ffe5eb52a98f5579..e4dc902cd4034702c761634d145c30781db189b2 100644
--- a/src/app/formulaire/elements/ngparam.ts
+++ b/src/app/formulaire/elements/ngparam.ts
@@ -212,7 +212,7 @@ export class NgParameter extends InputField implements Observer {
     }
 
     public set radioConfig(rc: ParamRadioConfig) {
-        if (this._radioConfig === undefined) {
+        if (rc === undefined) {
             throw new Error("cannot set radio config to undefined!");
         }
         this._radioConfig = rc;
diff --git a/src/app/services/formulaire.service.ts b/src/app/services/formulaire.service.ts
index 775e6ae0e1cc45f5c442857705dbc88d0f3b3c89..4c9e42baa9e5dc138d02427ff9d4d4ad1520bad8 100644
--- a/src/app/services/formulaire.service.ts
+++ b/src/app/services/formulaire.service.ts
@@ -388,7 +388,7 @@ export class FormulaireService extends Observable {
             for (const struct of f.currentNub.structures) {
                 for (const e of f.allFormElements) {
                     if (e instanceof FieldsetContainer) { // @TODO manage many containers one day ?
-                        e.addFromTemplate(0, undefined, struct);
+                        e.addFromTemplate(undefined, struct);
                     }
                 }
             }
@@ -400,7 +400,7 @@ export class FormulaireService extends Observable {
             for (const c of f.currentNub.getChildren()) {
                 for (const e of f.allFormElements) {
                     if (e instanceof FieldsetContainer) { // @TODO manage many containers one day ?
-                        e.addFromTemplate(0, undefined, c);
+                        e.addFromTemplate(undefined, c);
                     }
                 }
             }
diff --git a/src/app/services/internationalisation.service.ts b/src/app/services/internationalisation.service.ts
index 4b8f545f65dd4ebcdf9b9e7592c50920df7b95db..7dddda2a95c63a280013f2f72e74bdb618e055fb 100644
--- a/src/app/services/internationalisation.service.ts
+++ b/src/app/services/internationalisation.service.ts
@@ -334,8 +334,7 @@ export class I18nService extends Observable implements Observer {
      * @param short if true, will return short name
      */
     public childName(nub: Nub, plural: boolean = false, short: boolean = false) {
-        const type: string = nub.parent.childrenType;
-        let k = "INFO_CHILD_TYPE_" + type.toUpperCase();
+        let k = "INFO_CHILD_TYPE_" + nub.intlType.toUpperCase();
         if (short) {
             k += "_SHORT";
         } else if (plural) {
diff --git a/src/app/services/prebarrage.service.ts b/src/app/services/prebarrage.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..915cc1c61c22714f1f34eb07a7bd678f5ea2e203
--- /dev/null
+++ b/src/app/services/prebarrage.service.ts
@@ -0,0 +1,305 @@
+import { EventEmitter, Injectable } from "@angular/core";
+import { MermaidUtil, PbBassin, PbBassinParams, PbCloison, PreBarrage } from "jalhyd";
+
+export enum PrebarrageServiceEvents {
+    BASIN_WALL_ADDED, // a wall has been added
+    BASIN_WALL_COPIED, // a basin or wall has been copied
+    BASIN_WALL_REMOVED, // a basin or a wall has been removed
+    MOVE_BASIN, // a basin has been moved up or down
+}
+
+/**
+ * service relatif au schéma de prébarrage
+ */
+@Injectable()
+export class PrebarrageService {
+    /** predam data */
+    private _model: PreBarrage;
+
+    public readonly upstreamId = "amont";
+
+    public readonly downstreamId = "aval";
+
+    /** Latest clicked item: a PbCloison, a PbBassin or undefined if river "Upstream" or "Downstream" was clicked */
+    private _selectedNub: PbCloison | PbBassin;
+
+    private _changeEventEmitter = new EventEmitter();
+
+    public get model(): PreBarrage {
+        return this._model;
+    }
+
+    public set model(pb: PreBarrage) {
+        this._model = pb;
+    }
+
+    public get changeEventEmitter() {
+        return this._changeEventEmitter;
+    }
+
+    /**
+     * return upstream (and downstream) basin object
+     */
+    public get upstreamBassin(): PbBassin {
+        return (this._model as unknown) as PbBassin;
+    }
+
+    public get selectedNub(): PbBassin | PbCloison {
+        return this._selectedNub;
+    }
+
+    /**
+     * set selected nub in schema
+     */
+    public setSelectedNub(n: PbBassin | PbCloison): PbBassin | PbCloison {
+        this._selectedNub = n;
+        return n;
+    }
+
+    public deleteSelected(emptyFields: boolean) {
+        this.deleteByIndex(this._selectedNub.findPositionInParent(), emptyFields);
+    }
+
+    /**
+     * remove nth basin
+     * @param bi basin index
+     */
+    public deleteBasinByIndex(bi: number, emptyFields: boolean) {
+        const i = this.nthBasinIndex(bi);
+        this.deleteByIndex(i, emptyFields);
+    }
+
+    private deleteByIndex(i: number, emptyFields: boolean) {
+        this._model.deleteChild(i);
+        // never let an unconnected basin ! (not done in model to prevent unwanted
+        // automatic child addition when clearing children)
+        if (this._selectedNub instanceof PbCloison) {
+            // if no downstream connections remain, connect to river downstream
+            if (this._selectedNub.bassinAmont?.cloisonsAval.length === 0) {
+                this._model.addChild(new PbCloison(this._selectedNub.bassinAmont, undefined, undefined, emptyFields));
+            }
+            // if no upstream connections remain, connect to river upstream
+            if (this._selectedNub.bassinAval?.cloisonsAmont.length === 0) {
+                this._model.addChild(new PbCloison(undefined, this._selectedNub.bassinAval, undefined, emptyFields));
+            }
+        }
+        this._changeEventEmitter.emit(
+            {
+                id: PrebarrageServiceEvents.BASIN_WALL_REMOVED,
+            }
+        );
+    }
+
+    /**
+     * compute index of nth basin
+     * @param bi nth basin
+     * @returns index in child list
+     */
+    private nthBasinIndex(bi): number {
+        let nth: number = 0;
+        let res: number = 0;
+        for (const kid of this._model.children) {
+            if (kid instanceof PbBassin) {
+                if (nth == bi) {
+                    return res;
+                }
+                nth++;
+            }
+            res++;
+        }
+        return -1;
+    }
+
+    private copyWall(wall: PbCloison, emptyFields: boolean): PbCloison {
+        const wallCopy = new PbCloison(wall.bassinAmont, wall.bassinAval, undefined, emptyFields);
+        wallCopy.loadObjectRepresentation(wall.objectRepresentation());
+        this._model.addChild(wallCopy);
+        return wallCopy;
+    }
+
+    public copySelectedWall(emptyFields: boolean) {
+        const wall = this._selectedNub as PbCloison;
+        const wallCopy = new PbCloison(wall.bassinAmont, wall.bassinAval, undefined, emptyFields);
+        wallCopy.loadObjectRepresentation(wall.objectRepresentation());
+        this._model.addChild(wallCopy);
+        this._changeEventEmitter.emit(
+            {
+                id: PrebarrageServiceEvents.BASIN_WALL_COPIED,
+                data: wallCopy
+            }
+        );
+    }
+
+    public addBasin(emptyFields: boolean) {
+        const newBasin = new PbBassin(new PbBassinParams(20, 99, emptyFields));
+        this._model.addChild(newBasin);
+        this._changeEventEmitter.emit(
+            {
+                id: PrebarrageServiceEvents.BASIN_WALL_ADDED,
+                data: newBasin
+            }
+        );
+    }
+
+    public copySelectedBasin(emptyFields: boolean) {
+        this.copyBasin(this._selectedNub as PbBassin, emptyFields);
+    }
+
+    /**
+     * copy nth basin
+     * @param bi basin index
+     */
+    public copyBasinByIndex(bi: number, emptyFields: boolean) {
+        const i = this.nthBasinIndex(bi);
+        this.copyBasin(this._model.children[i] as PbBassin, emptyFields);
+    }
+
+    private copyBasin(basin: PbBassin, emptyFields: boolean) {
+        const basinCopy = new PbBassin(new PbBassinParams(20, 99, emptyFields));
+        basinCopy.loadObjectRepresentation(basin.objectRepresentation());
+        this._model.addChild(basinCopy);
+        this._changeEventEmitter.emit(
+            {
+                id: PrebarrageServiceEvents.BASIN_WALL_COPIED,
+                data: basinCopy
+            }
+        );
+    }
+
+    public get hasBasins(): boolean {
+        return this._model.bassins.length > 0;
+    }
+
+    public get bassins(): PbBassin[] {
+        return this._model.bassins;
+    }
+
+    public addWall(upstreamIndex: number, downstreamIndex: number, emptyFields: boolean) {
+        const wall = new PbCloison(
+            upstreamIndex === 0 ? undefined : this._model.bassins[upstreamIndex - 1],
+            downstreamIndex === 0 ? undefined : this._model.bassins[downstreamIndex - 1],
+            undefined,
+            emptyFields
+        );
+        this._model.addChild(wall);
+        this._changeEventEmitter.emit(
+            {
+                id: PrebarrageServiceEvents.BASIN_WALL_ADDED,
+                data: wall
+            }
+        );
+    }
+
+    /**
+     * Returns true if given basin is either connected to nothing, or only to
+     * river upstream or downstream
+     */
+    public isStandaloneBasin(basin: PbBassin) {
+        return (
+            (
+                basin.cloisonsAmont.length === 0
+                || basin.cloisonsAmont.map(c => c.bassinAmont).every(e => e === undefined)
+            ) && (
+                basin.cloisonsAval.length === 0
+                || basin.cloisonsAval.map(c => c.bassinAval).every(e => e === undefined)
+            )
+        );
+    }
+
+    /**
+     * @param uid nub uid
+     */
+    public findBasinPosition(uid: string): number {
+        return this._model.findBasinPosition(uid);
+    }
+
+    /**
+     * @param uid nub uid
+     */
+    private moveBasinUp(uid: string) {
+        this._model.moveBasin(uid, this._model.findBasinPosition(uid) - 1);
+    }
+
+    public moveSelectedBasinUp() {
+        const uid = this._selectedNub.uid;
+        this.moveBasinUp(uid);
+        this._changeEventEmitter.emit(
+            {
+                id: PrebarrageServiceEvents.MOVE_BASIN,
+                data: this._selectedNub
+            }
+        );
+    }
+
+    /**
+     * @param uid nub uid
+     */
+    private moveBasinDown(uid: string) {
+        this._model.moveBasin(uid, this._model.findBasinPosition(uid) + 1);
+    }
+
+    public moveSelectedBasinDown() {
+        const uid = this._selectedNub.uid;
+        this.moveBasinDown(uid);
+        this._changeEventEmitter.emit(
+            {
+                id: PrebarrageServiceEvents.MOVE_BASIN,
+                data: this._selectedNub
+            }
+        );
+    }
+
+    /**
+     * @param uid nub uid
+     */
+    public isLastBasin(uid: string): boolean {
+        return this._model.findBasinPosition(uid) === this._model.bassins.length - 1
+    }
+
+    public isValid(): boolean {
+        return this._model.hasUpDownConnection() && !this._model.hasBasinNotConnected();
+    }
+
+    /**
+     * @param uid nub uid
+     */
+    public findChild(uid: string): PbBassin | PbCloison {
+        return this._model.findChild(uid);
+    }
+
+    /**
+     * turn 'aval', 'amont' and Mermaid ids to nub real uid
+     */
+    public toNubUid(itemId: string): string {
+        if (itemId !== undefined && itemId !== null) {
+            if (this.matchMermaidIds([this.upstreamId, this.downstreamId], itemId)) {
+                return this.model.uid;
+            }
+            if (itemId.startsWith("flowchart-")) { // Mermaid style id (ie. flowchart-id-xx)
+                return itemId.split('-')[1];
+            }
+        }
+        return itemId;
+    }
+
+    /**
+     * check if a list of (Mermaid transformed) ids matches a given id
+     * @param ids ids to transform the Mermaid way
+     * @param itemId id to find
+     */
+    private matchMermaidIds(ids: string[], itemId: string): boolean {
+        return ids.find(id => MermaidUtil.isMermaidEqualIds(id, itemId)) !== undefined;
+    }
+
+    /**
+     * @param itemId Mermaid id to find
+     * @returns 
+     */
+    public findFromItemId(itemId: string) {
+        if (this.matchMermaidIds([this.upstreamId, this.downstreamId], itemId)) {
+            return this.upstreamBassin;
+        } else {
+            return this.model.findChild(itemId);
+        }
+    }
+}
diff --git a/src/app/services/service-factory.ts b/src/app/services/service-factory.ts
index a566f3aabb97fc5b2db06dcc5388def593fac7b9..e3ea55cae3c7ce504924688dac8ae1517f3268ba 100644
--- a/src/app/services/service-factory.ts
+++ b/src/app/services/service-factory.ts
@@ -3,6 +3,7 @@ import { FormulaireService } from "./formulaire.service";
 import { I18nService } from "./internationalisation.service";
 import { HttpService } from "./http.service";
 import { NotificationsService } from "./notifications.service";
+import { PrebarrageService } from "./prebarrage.service";
 
 /**
  * A "Singleton" the TS way, that holds pointers to all services, to be accessed
@@ -15,10 +16,12 @@ export const ServiceFactory: {
     i18nService: I18nService;
     httpService: HttpService;
     notificationsService: NotificationsService;
+    prebarrageService: PrebarrageService;
 } = {
     applicationSetupService: undefined,
     formulaireService: undefined,
     i18nService: undefined,
     httpService: undefined,
-    notificationsService: undefined
+    notificationsService: undefined,
+    prebarrageService: undefined
 };