diff --git a/src/base.ts b/src/base.ts
index f74f97a4a38d112ac8224622d167b277c5de2fd1..95f88b73cec1aa1c9a65082e35b2e22592df4ff9 100644
--- a/src/base.ts
+++ b/src/base.ts
@@ -1,16 +1,3 @@
-/**
- * Série de valeurs à calculer définie par le nom de la variable à sérier et le vecteur de valeur
- */
-export class Serie {
-    public name: string;
-    public values: number[];
-
-    constructor(name: string, values: number[]) {
-        this.name = name;
-        this.values = values;
-    }
-}
-
 /**
  * Gestion des messages de debogage dans la console
  * @note Etendre cette classe pour toutes les classes à debugguer
diff --git a/src/compute-node.ts b/src/compute-node.ts
index 92c5ec74cf29d8361221c163561d20416a704589..acc5e5b432f2a038355e24a2a00ba525bf24c1d6 100644
--- a/src/compute-node.ts
+++ b/src/compute-node.ts
@@ -1,6 +1,7 @@
 import { Debug } from "./base";
 import { ParamsEquation } from "./param/params-equation";
 import { ParamDefinition } from "./param/param-definition";
+import { ParamValueMode } from "./param/param-values";
 
 /**
  * type de calculette
@@ -51,5 +52,16 @@ export abstract class ComputeNode extends Debug {
         return this._prms.getFirstAnalyticalParameter();
     }
 
+    public initParametersValueMode(computedParam: ParamDefinition, variatedParam?: ParamDefinition, variatedMode?: ParamValueMode) {
+        for (const k in this._prms.map) {
+            if (k == computedParam.symbol)
+                this._prms.map[k].paramValues.valueMode = ParamValueMode.CALCUL;
+            else if (variatedParam && k == variatedParam.symbol)
+                this._prms.map[k].paramValues.valueMode = variatedMode;
+            else
+                this._prms.map[k].paramValues.valueMode = ParamValueMode.SINGLE;
+        }
+    }
+
     protected abstract setParametersCalculability(): void;
 }
diff --git a/src/nub.ts b/src/nub.ts
index e1471dcef093daf9a4d499d400ea09e33cbb8562..6ed251bdaa9a0f77a2468aeefcfe08fcf9e1c39b 100644
--- a/src/nub.ts
+++ b/src/nub.ts
@@ -1,7 +1,9 @@
-import { Debug, Serie } from "./base";
+import { Debug } from "./base";
 import { Dichotomie } from "./dichotomie";
 import { ComputeNode } from "./compute-node";
 import { Result } from "./util/result";
+import { ParamValues, ParamValueMode } from "./param/param-values";
+import { ParamDefinition } from ".";
 
 /**
  * Classe abstraite de Noeud de calcul : classe de base pour tous les calculs
@@ -9,6 +11,11 @@ import { Result } from "./util/result";
 export abstract class Nub extends ComputeNode {
     private _dichoStartIntervalMaxSteps: number = 100;
 
+    /**
+     * résultat de Calc()/CalcSerie()
+     */
+    protected _result: Result;
+
     /*
      * paramétrage de la dichotomie
      */
@@ -36,27 +43,73 @@ export abstract class Nub extends ComputeNode {
             rInit = this._prms.map[sVarCalc].v;
         }
         if (this._prms.map[sVarCalc].isAnalytical()) {
-            return this.Equation(sVarCalc);
+            this._result = this.Equation(sVarCalc);
+            return this._result;
         }
 
         const resSolve: Result = this.Solve(sVarCalc, rInit, rPrec);
         if (!resSolve.ok) {
-            return resSolve;
+            this._result = resSolve;
+            return this._result;
         }
         const sAnalyticalPrm: string = this.getFirstAnalyticalParameter().symbol;
         this._prms.map[sVarCalc].v = resSolve.vCalc;
         const res: Result = this.Equation(sAnalyticalPrm);
         res.vCalc = resSolve.vCalc;
+        this._result = res;
         return res;
     }
 
-    public CalcSerie(svarCalc: string, serie: Serie): Result[] {
-        /** @todo faire une boucle pour appeler this.Calc avec chaque valeur de serie.values
-         *
-         */
-        // let results = [new (Result)];
-        const results = [new Result(0)];
-        return results;
+    public CalcSerie(rPrec: number = 0.001, rInit?: number): Result {
+        const res = new Result();
+        this._result = res;
+
+        let variatedParam: ParamDefinition;
+        let computedParam: ParamDefinition;
+        for (const k in this._prms.map) {
+            const p: ParamDefinition = this._prms.map[k];
+
+            switch (p.valueMode) {
+                case ParamValueMode.LISTE:
+                case ParamValueMode.MINMAX:
+                    if (variatedParam == undefined)
+                        variatedParam = p;
+                    else
+                        throw new Error(`CalcSerie() : il y plusieurs paramètres à varier (au moins ${variatedParam.symbol} et ${p.symbol})`);
+                    break;
+
+                case ParamValueMode.CALCUL:
+                    if (computedParam == undefined)
+                        computedParam = p;
+                    else
+                        throw new Error(`CalcSerie() : il y plusieurs paramètres à calculer (au moins ${computedParam.symbol} et ${p.symbol})`);
+                    break;
+            }
+        }
+
+        if (computedParam == undefined)
+            throw new Error(`CalcSerie() : aucun paramètre à calculer`);
+
+        if (rInit === undefined)
+            rInit = this._prms.map[computedParam.symbol].v;
+
+        if (variatedParam == undefined)
+            this.Calc(computedParam.symbol, rInit, rPrec); // résultat dans this._result
+        else {
+            const res = new Result();
+            variatedParam.paramValues.initIterator();
+            while (variatedParam.paramValues.hasNext) {
+                variatedParam.paramValues.next;
+                this.Calc(computedParam.symbol, rInit, rPrec);  // résultat dans this._result
+                res.addResultElement(this._result.resultElement);
+                res.addLog(this._result.log);
+                if (this._result.ok)
+                    rInit = this._result.resultElement.vCalc;
+            }
+            this._result = res;
+        }
+
+        return this._result;
     }
 
     /**
@@ -71,4 +124,8 @@ export abstract class Nub extends ComputeNode {
         const target = this._prms.getFirstAnalyticalParameter();
         return dicho.Dichotomie(target.v, rPrec, rInit);
     }
+
+    public get result(): Result {
+        return this._result;
+    }
 }
diff --git a/src/param/param-base.ts b/src/param/param-base.ts
index 2a5415e91fb4dcebcb6238c92c875825b86d63dc..b5ee3c26c9f58f70d47e8a1af193afe98f7f80cd 100644
--- a/src/param/param-base.ts
+++ b/src/param/param-base.ts
@@ -4,6 +4,7 @@ import { Message, MessageCode } from "../util/message";
 
 import { JalhydObject } from "../jalhyd_object"
 import { ParamDomain, ParamDomainValue } from "./param-domain";
+import { ParamValues, ParamValueMode } from "./param-values";
 
 /**
  * paramètre avec symbole et domaine de définition
@@ -21,14 +22,16 @@ export class BaseParam extends JalhydObject {
     private _domain: ParamDomain;
 
     /**
-     * valeur numérique (éventuellement non définie)
+     * valeur(s) prise(s)
      */
-    private _value: DefinedNumber;
+    private _paramValues: ParamValues;
 
     constructor(symb: string, d: ParamDomain | ParamDomainValue, val?: number) {
         super();
         this._symbol = symb;
-        this._value = new DefinedNumber(val);
+
+        this._paramValues = new ParamValues();
+        this._paramValues.singleValue = val;
 
         if (d instanceof ParamDomain) {
             this._domain = d;
@@ -51,33 +54,37 @@ export class BaseParam extends JalhydObject {
         return this._domain.interval;
     }
 
+    public get paramValues(): ParamValues {
+        return this._paramValues;
+    }
+
     /**
      * gestion de la valeur
      */
 
     public get isDefined(): boolean {
-        return this._value.isDefined;
+        return this._paramValues.isDefined;
     }
 
     public getValue(): number {
-        if (!this._value.isDefined) {
+        if (!this._paramValues.isDefined) {
             const e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_UNDEFINED);
             e.extraVar.symbol = this.symbol;
             throw e;
         }
 
-        return this._value.value;
+        return this._paramValues.singleValue;
     }
 
     public setValue(val: number) {
         this.checkValue(val);
-        this._value.value = val;
+        this._paramValues.singleValue = val;
 
         //        console.log("setting param " + this._symbol + " id=" + this._id + " to " + val); // A VIRER
     }
 
     public get uncheckedValue(): number {
-        return this._value.uncheckedValue;
+        return this._paramValues.uncheckedValue;
     }
 
     public checkValue(v: number) {
@@ -133,4 +140,8 @@ export class BaseParam extends JalhydObject {
                 throw e;
         }
     }
+
+    public get valueMode() {
+        return this._paramValues.valueMode;
+    }
 }
diff --git a/src/param/param-definition.ts b/src/param/param-definition.ts
index 4f911a6c221357f17df65e82fb440f2aa5d0e903..a3ee7daa6fb9dec90ee2de3c54138950f8111eea 100644
--- a/src/param/param-definition.ts
+++ b/src/param/param-definition.ts
@@ -41,7 +41,6 @@ export class ParamDefinition extends BaseParam {
     constructor(s: string, d: ParamDomain | ParamDomainValue, val?: number) {
         super(s, d, val);
         this._calc = ParamCalculability.FREE;
-        this.checkValue(val);
     }
 
     get v(): number {
diff --git a/src/param/param-values.ts b/src/param/param-values.ts
index bd7eb3ef678657184818bfc5a07103d8d600349e..f05578e86ade4afac04083ec3f9ee5d63402e098 100644
--- a/src/param/param-values.ts
+++ b/src/param/param-values.ts
@@ -1,4 +1,5 @@
 import { Pair } from "../util/pair"
+import { DefinedNumber } from "../util/definedvalue";
 
 /**
  * mode de génération des valeurs
@@ -17,7 +18,12 @@ export enum ParamValueMode {
     /**
      * liste de valeurs discrètes
      */
-    LISTE
+    LISTE,
+
+    /**
+     * la valeur du paramètre est non définie et à calculer
+     */
+    CALCUL
 }
 
 /**
@@ -76,11 +82,32 @@ export class ParamValueIterator implements IterableIterator<number> {
         }
     }
 
+    public get hasNext(): boolean {
+        switch (this._config) {
+            // valeur fixée
+            case 0:
+                return this._index == 0;
+
+            // min/max
+            case 1:
+                const end = this._reverse ? this._index < this._param.min : this._index > this._param.max;
+                return !end;
+
+            // liste
+            case 2:
+                const i = this._index;
+                return this._index >= this._param.valueList.length;
+
+            default:
+                throw new Error(`ParamValueIterator.hasNext() : erreur interne`);
+        }
+    }
+
     public next(): IteratorResult<number> {
         switch (this._config) {
             // valeur fixée
             case 0:
-                if (this._index == 0)
+                if (this.hasNext)
                     return {
                         done: false,
                         value: this._param.singleValue
@@ -94,8 +121,7 @@ export class ParamValueIterator implements IterableIterator<number> {
             // min/max
             case 1:
                 const res = this._index;
-                const end = this._reverse ? this._index < this._param.min : this._index > this._param.max;
-                if (!end) {
+                if (this.hasNext) {
                     if (this._reverse)
                         this._index -= this._param.step;
                     else
@@ -114,7 +140,7 @@ export class ParamValueIterator implements IterableIterator<number> {
             // liste
             case 2:
                 const i = this._index;
-                if (this._index < this._param.valueList.length) {
+                if (this.hasNext) {
                     const res = this._param.valueList[this._index++];
                     return {
                         done: false,
@@ -149,12 +175,12 @@ export class ParamValues {
     /**
      * mode de génération des valeurs : min/max, liste, ...
      */
-    private _valueMode: ParamValueMode = ParamValueMode.MINMAX;
+    private _valueMode: ParamValueMode;
 
     /**
-     * valeur dans le cas ParamValueMode.SINGLE
+     * valeur numérique (éventuellement non définie) dans le mode "valeur unique" (ParamValueMode.SINGLE)
      */
-    private _singleValue: number;
+    private _singleValue: DefinedNumber;
 
     /**
      * valeur min dans le cas ParamValueMode.MINMAX
@@ -176,20 +202,32 @@ export class ParamValues {
      */
     private _valueList: number[];
 
+    /**
+     * itérateur courant
+     */
+    private _iterator: ParamValueIterator;
+
+    constructor() {
+        this._singleValue = new DefinedNumber();
+        this.valueMode = ParamValueMode.CALCUL;
+    }
+
     public setValues(o: number | any, max?: number, step?: number) {
         if (typeof (o) === "number") {
             if (max == undefined) {
-                this._valueMode = ParamValueMode.SINGLE;
-                this._singleValue = o as number;
+                this.valueMode = ParamValueMode.SINGLE;
+                this._singleValue.value = o as number;
             } else {
-                this._valueMode = ParamValueMode.MINMAX;
+                this.valueMode = ParamValueMode.MINMAX;
                 this._minValue = o as number;
                 this._maxValue = max;
                 this._stepValue = step;
             }
         }
-        else if (Array.isArray(o))
+        else if (Array.isArray(o)) {
+            this.valueMode = ParamValueMode.LISTE;
             this._valueList = o;
+        }
         else
             throw new Error(`ParamValues.setValues() :  appel invalide`);
     }
@@ -202,12 +240,26 @@ export class ParamValues {
         this._valueMode = m;
     }
 
+    private checkValueMode(expected: ParamValueMode) {
+        if (this._valueMode != expected)
+            throw new Error(`ParamValues : mode de valeurs ${ParamValueMode[expected]} incorrect`);
+    }
+
     public get singleValue() {
-        return this._singleValue;
+        return this._singleValue.value;
+    }
+
+    public get uncheckedValue() {
+        return this._singleValue.uncheckedValue;
     }
 
     public set singleValue(v: number) {
-        this._singleValue = v;
+        this._singleValue.value = v;
+        this.valueMode = ParamValueMode.SINGLE;
+    }
+
+    public get isDefined() {
+        return this._singleValue.isDefined;
     }
 
     public get min() {
@@ -216,6 +268,7 @@ export class ParamValues {
 
     public set min(v: number) {
         this._minValue = v;
+        this.valueMode = ParamValueMode.MINMAX;
     }
 
     public get max() {
@@ -224,37 +277,81 @@ export class ParamValues {
 
     public set max(v: number) {
         this._maxValue = v;
+        this.valueMode = ParamValueMode.MINMAX;
     }
 
     public get stepRefValue(): Pair {
+        this.checkValueMode(ParamValueMode.MINMAX);
         return new Pair(1e-9, this._maxValue - this._minValue);
     }
 
     public get step() {
+        this.checkValueMode(ParamValueMode.MINMAX);
         return this._stepValue;
     }
 
     public set step(v: number) {
         this._stepValue = v;
+        this.valueMode = ParamValueMode.MINMAX;
     }
 
     public get valueList() {
+        this.checkValueMode(ParamValueMode.LISTE);
         return this._valueList;
     }
 
     public set valueList(l: number[]) {
         this._valueList = l;
+        this.valueMode = ParamValueMode.LISTE;
     }
 
+    /**
+     * crée un iterateur
+     * @param reverse true si on veut itérer max->min ou depuis la fin de la liste
+     */
     public getValuesIterator(reverse: boolean = false): ParamValueIterator {
         return new ParamValueIterator(this, reverse);
     }
 
+    /**
+     * 
+     * @param reverse prépare un itérateur pour parcourir les valeurs
+     */
+    public initIterator(reverse: boolean = false) {
+        switch (this._valueMode) {
+            case ParamValueMode.LISTE:
+            case ParamValueMode.MINMAX:
+                break;
+
+            default:
+                throw new Error(`ParamValues : mode de valeurs ${ParamValueMode[this._valueMode]} incorrect`);
+        }
+
+        this._iterator = this.getValuesIterator(reverse);
+    }
+
+    /**
+     * @return true si il reste des valeurs à parcourir par l'itérateur courant
+     */
+    public get hasNext(): boolean {
+        return this._iterator.hasNext;
+    }
+
+    /**
+     * fixe la valeur courante à la prochaine valeur à parcourir par l'itérateur courant
+     * @return prochaine valeur à parcourir par l'itérateur courant
+     */
+    public get next(): number {
+        this._singleValue.value = this._iterator.next().value;
+        return this._singleValue.value;
+    }
+
     /**
      * copie des membres
      */
     public copyMembers(n: ParamValues) {
         n._valueMode = this.valueMode;
+        n._singleValue = new DefinedNumber(this._singleValue.value);
         n._minValue = this._minValue;
         n._maxValue = this._maxValue;
         n._stepValue = this._stepValue;
diff --git a/src/remous.ts b/src/remous.ts
index 45070fadac9e71a1cbec3912a644aa88c6b962da..3e02d648b599003609f2de6d1cdec9dd54496e1d 100644
--- a/src/remous.ts
+++ b/src/remous.ts
@@ -9,7 +9,7 @@ import { cLog } from "./util/log";
 import { Message, MessageCode } from "./util/message";
 import { Result } from "./util/result";
 import { ResultElement } from "./util/resultelement";
-import { ParamValueIterator, ParamValues } from ".";
+import { ParamValueIterator, ParamValues, BaseParam } from ".";
 
 export enum MethodeResolution {
     Trapezes, EulerExplicite, RungeKutta4
@@ -563,7 +563,8 @@ export class CourbeRemous extends Nub {
                 `Condition limite aval (${this.prms.Yaval.v}) >= ` +
                 `Hauteur critique (${this.Sn.HautCritique}) : calcul de la partie fluviale à partir de l'aval`);
             this.Dx = this.prms.Dx.v;
-            res = this.calcul(this.prms.Yaval.v, xValues.getValuesIterator(true));
+            xValues.initIterator(true);
+            res = this.calcul(this.prms.Yaval.v, xValues);
             res.insertMessage(new Message(MessageCode.INFO_REMOUS_CALCUL_FLUVIAL));
         } else {
             this.debug(
@@ -597,7 +598,8 @@ export class CourbeRemous extends Nub {
                 ") <= Hauteur critique (" + this.Sn.HautCritique +
                 ") : calcul de la partie torrentielle à partir de l'amont");
             this.Dx = -this.prms.Dx.v;
-            res = this.calcul(this.prms.Yamont.v, xValues.getValuesIterator(false));
+            xValues.initIterator(false);
+            res = this.calcul(this.prms.Yamont.v, xValues);
             res.insertMessage(new Message(MessageCode.INFO_REMOUS_CALCUL_TORRENTIEL));
         } else {
             // this._log.add(new Message(MessageCode.ERROR_REMOUS_PAS_CALCUL_DEPUIS_AMONT));
@@ -815,15 +817,16 @@ export class CourbeRemous extends Nub {
      * Calcul d'une courbe de remous en fluvial ou torrentiel
      * @param YCL Condition limite amont (torrentiel) ou aval (fluvial)
      */
-    private calcul(YCL: number, valueIterator: ParamValueIterator): ResultElement {
+    private calcul(YCL: number, varParam: ParamValues): ResultElement {
         const trY: { [key: number]: number; } = {};
         const res = new ResultElement();
 
         let lastY = YCL;
-        trY[round(valueIterator.next().value, this.prmSect.iPrec.v)] = lastY;
+        trY[round(varParam.next, this.prmSect.iPrec.v)] = lastY;
 
         // Boucle de calcul de la courbe de remous
-        for (const x of valueIterator) {
+        while (varParam.hasNext) {
+            const x = varParam.next;
             // this.debug("lastY " + lastY);
             const rY: Result = this.Calc_Y(lastY);
             // this.debug("calcul : x " + x + " y " + rY.vCalc);