diff --git a/src/app/calculators/pab/pab.config.json b/src/app/calculators/pab/pab.config.json
new file mode 100644
index 0000000000000000000000000000000000000000..fc2ad396c93cf0525d8459d68a1d4fac32050528
--- /dev/null
+++ b/src/app/calculators/pab/pab.config.json
@@ -0,0 +1,103 @@
+[
+    {
+        "id": "fs_param_hydro",
+        "type": "fieldset",
+        "calcType": "Pab",
+        "option": "cal",
+        "fields": [
+            {
+                "type": "input",
+                "id": "Q",
+                "symbol": "Q",
+                "unit": "m³/s"
+            },
+            {
+                "type": "input",
+                "id": "Z1",
+                "unit": "m"
+            },
+            {
+                "type": "input",
+                "id": "Z2",
+                "unit": "m"
+            }
+        ]
+    },
+    {
+        "id": "fs_bassin",
+        "type": "fieldset_template",
+        "option": "fix",
+        "fields": [
+            {
+                "id": "select_modele_cloisons",
+                "type": "select",
+                "select": [
+                    {
+                        "id": "select_ouvrage_vanne_rect",
+                        "enum": "StructureType.VanneRectangulaire"
+                    },
+                    {
+                        "id": "select_ouvrage_seuil_rect",
+                        "enum": "StructureType.SeuilRectangulaire"
+                    },
+                    {
+                        "id": "select_ouvrage_seuil_triang",
+                        "enum": "StructureType.SeuilTriangulaire"
+                    },
+                    {
+                        "id": "select_ouvrage_seuil_triangtrunc",
+                        "enum": "StructureType.SeuilTriangulaireTrunc"
+                    }
+                ]
+            },
+            {
+                "type": "input",
+                "id": "ZDV",
+                "unit": "m",
+                "nodeType": "StructureRectangle",
+                "dep_exist": [
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_vanne_rect"
+                    },
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_seuil_rect"
+                    },
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_seuil_triang"
+                    },
+                    {
+                        "refid": "select_ouvrage",
+                        "refvalue": "select_ouvrage_seuil_triangtrunc"
+                    }
+                ]
+            }
+        ]
+    },
+    {
+        "id": "bassin_container",
+        "type": "template_container",
+        "templates": [
+            "fs_bassin"
+        ]
+    },
+    {
+        "id": "fs_param_calc",
+        "type": "fieldset",
+        "calcType": "ParallelStructure",
+        "option": "fix",
+        "fields": [
+            {
+                "type": "input",
+                "id": "Pr"
+            }
+        ]
+    },
+    {
+        "type": "options",
+        "modeleCloisonsSelectId": "select_modele_cloisons",
+        "idCal": "Q"
+    }
+]
\ No newline at end of file
diff --git a/src/app/calculators/pab/pab.en.json b/src/app/calculators/pab/pab.en.json
new file mode 100644
index 0000000000000000000000000000000000000000..955f0fb0b24f5959de7fe3e36d90b31319a9e524
--- /dev/null
+++ b/src/app/calculators/pab/pab.en.json
@@ -0,0 +1,9 @@
+{
+    "fs_param_hydro": "Hydraulic parameters",
+    "Q": "Flow",
+    "Z1": "Upstream elevation",
+    "Z2": "Downstream elevation",
+    "fs_bassin": "Basin",
+    "bassin_container": "Basins",
+    "select_modele_cloisons": "Cross walls model"
+}
\ No newline at end of file
diff --git a/src/app/calculators/pab/pab.fr.json b/src/app/calculators/pab/pab.fr.json
new file mode 100644
index 0000000000000000000000000000000000000000..e64d3e88f486d8b13c804f55d91e593b0855a963
--- /dev/null
+++ b/src/app/calculators/pab/pab.fr.json
@@ -0,0 +1,9 @@
+{
+    "fs_param_hydro": "Paramètres hydrauliques",
+    "Q": "Débit",
+    "Z1": "Cote amont",
+    "Z2": "Cote aval",
+    "fs_bassin": "Bassin",
+    "bassin_container": "Bassins",
+    "select_modele_cloisons": "Modèle de cloisons"
+}
\ No newline at end of file
diff --git a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
index 0fc7928626554a2c86fae9bd460a7bb0d1758bd1..3b868359886c34ed418697f8fcadaa132edf0101 100644
--- a/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
+++ b/src/app/components/dialog-generate-pab/dialog-generate-pab.component.ts
@@ -2,7 +2,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material";
 import { Inject, Component } from "@angular/core";
 
 import { I18nService } from "../../services/internationalisation/internationalisation.service";
-import { FormGroup, FormBuilder, Validators } from "@angular/forms";
+import { FormBuilder } from "@angular/forms";
 
 @Component({
     selector: "dialog-generate-pab",
@@ -11,13 +11,13 @@ import { FormGroup, FormBuilder, Validators } from "@angular/forms";
 })
 export class DialogGeneratePABComponent {
 
-  public debit: number;
+  public debit = 1.5;
 
-  public coteAmont: number;
+  public coteAmont = 102;
 
-  public coteAval: number;
+  public coteAval = 99;
 
-  public nbBassins: number;
+  public nbBassins = 6;
 
   constructor(
     public dialogRef: MatDialogRef<DialogGeneratePABComponent>,
diff --git a/src/app/config.json b/src/app/config.json
index 86e823ea8cf12655218c8722e3bce223aaf2341a..19625acb4c41e2b281b7f1674e1d803057b980ea 100644
--- a/src/app/config.json
+++ b/src/app/config.json
@@ -13,7 +13,7 @@
                 "title": "Passe à poisson sur le Lez, entre Bollène et Suze",
                 "credits": "Hervé Capra / Irstea"
             },
-            "calculators": [ 5, 6, 12, 13, 10, 9 ]
+            "calculators": [ 15, 5, 6, 12, 13, 10, 9 ]
         },
         {
             "name": "PASSE_NATURELLE",
diff --git a/src/app/formulaire/definition/concrete/form-pab.ts b/src/app/formulaire/definition/concrete/form-pab.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ade58a60e42f57b48b491ab49f369b4c14874ed5
--- /dev/null
+++ b/src/app/formulaire/definition/concrete/form-pab.ts
@@ -0,0 +1,269 @@
+import { Structure, Nub, ParallelStructure, StructureProperties, Props, Session } from "jalhyd";
+
+import { FormDefParallelStructures } from "../form-def-parallel-structures";
+import { FormComputeParallelStructures } from "../form-compute-parallel-structures";
+import { FormResultFixedVar } from "../form-result-fixedvar";
+import { FieldsetContainer } from "../../fieldset-container";
+import { FieldSet } from "../../fieldset";
+import { SelectField } from "../../select-field";
+import { NgParameter } from "../../ngparam";
+import { FieldsetTemplate } from "../../fieldset-template";
+import { FormulaireNode } from "../../formulaire-node";
+import { FormulaireBase } from "./form-base";
+
+/**
+ * Formulaire pour les passes à bassins, inspiré du formulaire
+ * pour les structures en parallèle
+ */
+export class FormulairePab extends FormulaireBase {
+
+    private _formParallelStruct: FormDefParallelStructures;
+
+    /** id du select configurant le modèle de cloisons */
+    private __modeleCloisonsSelectId: string;
+
+    constructor() {
+        super();
+        this._formResult = new FormResultFixedVar(this, false);
+        this._formParallelStruct = new FormDefParallelStructures();
+
+        // remove obsolete observer set by super()
+        this.removeObserver(this._formCompute);
+        this._formCompute = new FormComputeParallelStructures(this, this._formParallelStruct, (this._formResult as FormResultFixedVar));
+    }
+
+    private createStructNub(templ: FieldsetTemplate): Nub {
+        // !!! attention !!!
+        // Il doit y avoir cohérence dans le fichier de conf entre les valeurs defaultXXX et les valeurs possibles pour les select
+        // cad valeur par défaut du 1er select (type d'ouvrage), du 2ème (loi de débit).
+        // A terme, il faudrait analyser le fichier de conf (dépendances d'existence) pour déterminer automatiquement ces valeurs
+
+        const params = {};
+        params["calcType"] = templ.calcTypeFromConfig;
+        params["nodeType"] = templ.defaultNodeTypeFromConfig;
+        params["structureType"] = templ.defaultStructTypeFromConfig;
+        params["loiDebit"] = templ.defaultLoiDebitFromConfig;
+
+        return this.createStructure(new Props(params));
+    }
+
+    /**
+     * ajoute un Nub Structure
+     * @param st structure à ajouter
+     * @param after position après laquelle insérer la structure, à la fin sinon
+     */
+    private addStructureNub(st: Structure, after?: number) {
+        this.parallelStructureNub.addChild(st, after);
+    }
+
+    private get parallelStructureNub(): ParallelStructure {
+        return this.currentNub as ParallelStructure;
+    }
+
+    /**
+     * Asks JaLHyd to create a Structure Nub as a child of the current Calculator Module
+     * and return it; does not store it in the Session (for Structures, not for Calculator Modules)
+     * @param p properties for the new Nub
+     */
+    protected createStructure(p: Props): Structure {
+        return Session.getInstance().createNub(p, this.currentNub as ParallelStructure) as Structure;
+    }
+
+    /**
+     * Replaces the current Nub in the current calculator module, with a new one built with properties "params"
+     * @param params properties to build the new Nub (calcType, loiDebit...)
+     */
+    protected replaceNub(sn: Structure, params: Props): Nub {
+        const parent = (this.currentNub as ParallelStructure);
+        const newStructure = this.createStructure(params);
+        parent.replaceChildInplace(sn, newStructure);
+        return newStructure;
+    }
+
+    /**
+     * Deleted the given child Nub in the current calculator module
+     * @param params properties to build the new Nub (calcType, loiDebit...)
+     */
+    protected deleteNub(sn: Structure) {
+        const parent = (this.currentNub as ParallelStructure);
+        parent.deleteChild(parent.getIndexForChild(sn));
+    }
+
+    public createFieldset(parent: FormulaireNode, json: {}, data?: {}, nub?: Nub): FieldSet {
+        if (json["calcType"] === "Structure") {
+            // indice après lequel insérer le nouveau FieldSet
+            const after = data["after"];
+
+            const res: FieldSet = new FieldSet(parent);
+            let sn: Nub;
+            if (nub) { // use existing Nub (build interface based on model)
+                sn = nub;
+            } else {
+                sn = this.createStructNub(data["template"]);
+                this.addStructureNub(sn as Structure, after);
+            }
+            res.setNub(sn, false);
+
+            if (after !== undefined) {
+                parent.kids.splice(after + 1, 0, res);
+            } else {
+                parent.kids.push(res);
+            }
+
+            this.resetResults();
+
+            return res;
+        } else {
+            return super.createFieldset(parent, json, data);
+        }
+    }
+
+    protected parseOptions(json: {}) {
+        super.parseOptions(json);
+
+        // id du select configurant le type d'ouvrage
+        this.__modeleCloisonsSelectId = this.getOption(json, "ouvrageSelectId");
+    }
+
+    public afterParseFieldset(fs: FieldSet) {
+        // si le FieldSet contient le select de type d'ouvrage
+        if (this.__modeleCloisonsSelectId) {
+            const sel = fs.getFormulaireNodeById(this.__modeleCloisonsSelectId);
+            if (sel) {
+                // on abonne le formulaire aux propriétés du FieldSet
+                fs.properties.addObserver(this);
+            }
+        }
+    }
+
+    public moveFieldsetUp(fs: FieldSet) {
+        if (fs.nub instanceof Structure) {
+            // déplacement du nub
+            fs.nub.parent.moveChildUp(fs.nub);
+            // déplacement du fieldset
+            this.fieldsetContainer.moveFieldsetUp(fs);
+
+            this.resetResults();
+        } else {
+            super.moveFieldsetUp(fs);
+        }
+    }
+
+    public moveFieldsetDown(fs: FieldSet) {
+        if (fs.nub instanceof Structure) {
+            // déplacement du nub
+            fs.nub.parent.moveChildDown(fs.nub);
+            // déplacement du fieldset
+            this.fieldsetContainer.moveFieldsetDown(fs);
+
+            this.resetResults();
+        } else { super.moveFieldsetDown(fs); }
+    }
+
+    public removeFieldset(fs: FieldSet) {
+        if (fs.nub instanceof Structure) {
+            // suppression du sous-nub dans le Nub parent
+            this.deleteNub(fs.nub);
+
+            // suppression du fieldset
+            this.fieldsetContainer.removeFieldset(fs);
+
+            this.resetResults();
+        } else { super.removeFieldset(fs); }
+    }
+
+    protected completeParse(json: {}) {
+        this._formParamCalc.parseOptions(json);
+        this.subscribeFieldsetContainer();
+    }
+
+    private get fieldsetContainer(): FieldsetContainer {
+        const n = this.getFormulaireNodeById("bassin_container");
+        if (n === undefined || !(n instanceof FieldsetContainer)) {
+            throw new Error("l'élément 'bassin_container' n'est pas du type FieldsetContainer");
+        }
+        return n as FieldsetContainer;
+    }
+
+    /**
+     * Après une modification, détermine si les propriétés d'un Fieldset sont compatibles
+     * entre elles et les ajuste au besoin
+     * @param props propriétés à vérifier
+     * @param name nom de la propriété qui vient de changer
+     * @param val nouvelle valeur de la propriété
+     */
+    private adjustProperties(props: Props, name: string, val: any) {
+        if (name === "structureType") {
+            if (! StructureProperties.isCompatibleValues(
+                val, props.getPropValue("loiDebit"), this.currentNub as ParallelStructure
+            )) {
+                // currentNub should always be a ParallelStructure here
+                const ld = StructureProperties.findCompatibleLoiDebit(
+                    props.getPropValue("structureType"),
+                    [],
+                    this.currentNub as ParallelStructure
+                );
+                props.setPropValue("loiDebit", ld);
+            }
+        }
+    }
+
+    /**
+     * abonnement en tant qu'observateur du FieldsetContainer
+     */
+    private subscribeFieldsetContainer() {
+        this.fieldsetContainer.addObserver(this);
+    }
+
+    /**
+     * abonnement en tant qu'observateur des NgParameter des FieldSet contenus dans le FieldsetContainer
+     */
+    private subscribeStructureInputFields(fs: FieldSet) {
+        for (const n of fs.allFormElements) {
+            if (n instanceof NgParameter) {
+                n.addObserver(this);
+            }
+        }
+    }
+    /**
+     * abonnement en tant qu'observateur du SelectField des FieldSet contenus dans le FieldsetContainer
+     */
+    private subscribeStructureSelectFields(fs: FieldSet) {
+        for (const n of fs.allFormElements) {
+            if (n instanceof SelectField) {
+                n.addObserver(this);
+            }
+        }
+    }
+
+    // interface Observer
+
+    public update(sender: any, data: any) {
+
+        super.update(sender, data);
+
+        if (sender instanceof FieldsetContainer) {
+            switch (data.action) {
+                case "newFieldset":
+                    this.reset();
+                    this.subscribeStructureInputFields(data["fieldset"]);
+                    this.subscribeStructureSelectFields(data["fieldset"]);
+            }
+        } else if (sender instanceof FieldSet && data.action === "propertyChange") {
+            switch (sender.id) {
+                case "fs_ouvrage":
+                    const props = sender.properties;
+                    // ensure loiDebit is set
+                    props.setPropValue("loiDebit", data.value);
+                    this.adjustProperties(props, data["name"], data["value"]);
+                    // replace Structure Nub
+                    const newNub = this.replaceNub((sender.nub as Structure), props);
+                    sender.setNub(newNub);
+                    // treat the fieldset as new to re-subscribe to Nub properties change events
+                    this.afterParseFieldset(sender);
+                    this.reset();
+                    break;
+            }
+        }
+    }
+}
diff --git a/src/app/formulaire/definition/concrete/form-parallel-structures.ts b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
index 2d703d83205641f1085b9c67dd6990564df10a73..5a45fdaf37eaa1bab5d2e2c69a6f9797e9f9e608 100644
--- a/src/app/formulaire/definition/concrete/form-parallel-structures.ts
+++ b/src/app/formulaire/definition/concrete/form-parallel-structures.ts
@@ -1,4 +1,4 @@
-import { Structure, Nub, ParallelStructure, LoiDebit, StructureProperties, Props, Session, StructureType } from "jalhyd";
+import { Structure, Nub, ParallelStructure, StructureProperties, Props, Session } from "jalhyd";
 
 import { FormDefParallelStructures } from "../form-def-parallel-structures";
 import { FormComputeParallelStructures } from "../form-compute-parallel-structures";
diff --git a/src/app/services/formulaire/formulaire.service.ts b/src/app/services/formulaire/formulaire.service.ts
index 8490ef82890f7d2655bfcd6b4289e45e05f3afd2..2d722c70e7181c673e1e19691fb55a1224c53ef6 100644
--- a/src/app/services/formulaire/formulaire.service.ts
+++ b/src/app/services/formulaire/formulaire.service.ts
@@ -22,6 +22,7 @@ import { NgParameter } from "../../formulaire/ngparam";
 import { FieldsetContainer } from "../..//formulaire/fieldset-container";
 import { ApplicationSetupService } from "../app-setup/app-setup.service";
 import { NotificationsService } from "../notifications/notifications.service";
+import { FormulairePab } from "../../formulaire/definition/concrete/form-pab";
 
 @Injectable()
 export class FormulaireService extends Observable {
@@ -60,6 +61,7 @@ export class FormulaireService extends Observable {
         this.calculatorPaths[CalculatorType.Dever] = "dever";
         this.calculatorPaths[CalculatorType.Cloisons] = "cloisons";
         this.calculatorPaths[CalculatorType.MacroRugo] = "macrorugo";
+        this.calculatorPaths[CalculatorType.Pab] = "pab";
     }
 
     private get _intlService(): I18nService {
@@ -213,6 +215,10 @@ export class FormulaireService extends Observable {
                 f = new FormulaireParallelStructure();
                 break;
 
+            case CalculatorType.Pab:
+                f = new FormulairePab();
+                break;
+
             default:
                 f = new FormulaireBase();
         }
diff --git a/src/locale/messages.en.json b/src/locale/messages.en.json
index fa3305255f91f5472f3bcdf4fa0c7fcb965b4986..9f148f5cd7bc200938db79b1b10161e4398f0878 100644
--- a/src/locale/messages.en.json
+++ b/src/locale/messages.en.json
@@ -167,6 +167,8 @@
     "INFO_OPTION_NONE_F": "None",
     "INFO_OPTION_GENERATE": "Generate",
     "INFO_OUVRAGE": "Structure",
+    "INFO_PAB_TITRE": "Fish ladder",
+    "INFO_PAB_TITRE_COURT": "Fish ladder",
     "INFO_PABCHUTE_TITRE": "Fish ladder: fall",
     "INFO_PABCHUTE_TITRE_COURT": "FL: fall",
     "INFO_PABDIMENSIONS_TITRE": "Fish ladder: dimensions",
diff --git a/src/locale/messages.fr.json b/src/locale/messages.fr.json
index a6ecf9213b9fbc66a24f7f73775cf65979f5e45b..3e0b05940fec7529c9bf432761daabb848bb2c21 100644
--- a/src/locale/messages.fr.json
+++ b/src/locale/messages.fr.json
@@ -167,6 +167,8 @@
     "INFO_OPTION_NONE_F": "Aucune",
     "INFO_OPTION_GENERATE": "Générer",
     "INFO_OUVRAGE": "Ouvrage",
+    "INFO_PAB_TITRE": "Passe à bassins",
+    "INFO_PAB_TITRE_COURT": "PAB",
     "INFO_PABCHUTE_TITRE": "Passe à bassin&nbsp;: chute",
     "INFO_PABCHUTE_TITRE_COURT": "PAB&nbsp;: chute",
     "INFO_PABDIMENSIONS_TITRE": "Passe à bassin&nbsp;: dimensions",