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);