From 173ffdc881aebdfc50dc332734b7edc62ce03d42 Mon Sep 17 00:00:00 2001 From: "mathias.chouet" <mathias.chouet@irstea.fr> Date: Fri, 24 May 2019 15:34:53 +0200 Subject: [PATCH] Multiple variated parameters: moves extension strategy to ParameterIterator --- spec/value_ref/value_ref.spec.ts | 22 ++++ src/nub.ts | 67 +++-------- src/param/param-definition.ts | 16 ++- src/param/param-value-iterator.ts | 185 ++++++++++++++++++++++-------- src/param/param-values.ts | 16 +-- src/section/section_parametree.ts | 3 + 6 files changed, 206 insertions(+), 103 deletions(-) diff --git a/spec/value_ref/value_ref.spec.ts b/spec/value_ref/value_ref.spec.ts index 937377c6..d6fbf2a8 100644 --- a/spec/value_ref/value_ref.spec.ts +++ b/spec/value_ref/value_ref.spec.ts @@ -245,5 +245,27 @@ describe("référence d'un paramètre à un autre : ", () => { } expect(n).toEqual(input.length); }); + + it("test 5", () => { + // cas de figure : + // nub2.A est lié à nub1.A (valeur variée) + // lecture de nub2.A + + createEnv(); + + const input = [2, 3, 4, 5, 6]; + prm1.A.setValues(input); + + // valeur esclave bidon, doit être masquée par la valeur maître (cad prm1.A, normalement [2,3,4,5,6]) + prm2.A.singleValue = 0; + prm2.A.defineReference(nub1, "A"); + + let ndx = 0; + for (const v of prm2.A.paramValues.getValuesIterator()) { + expect(v).toEqual(input[ndx]); + ndx++; + } + expect(ndx).toEqual(input.length); + }); }); }); diff --git a/src/nub.ts b/src/nub.ts index fb195466..125609f1 100644 --- a/src/nub.ts +++ b/src/nub.ts @@ -3,7 +3,7 @@ import { Dichotomie } from "./dichotomie"; import { acSection, IParamDefinitionIterator, Pab, ParamDefinition, ParamsEquation, ParamsEquationArrayIterator, Session, Structure } from "./index"; import { LinkedValue } from "./linked-value"; -import { ExtensionStrategy, ParamCalculability } from "./param/param-definition"; +import { ParamCalculability } from "./param/param-definition"; import { ParamValueMode } from "./param/param-value-mode"; import { ParamValues } from "./param/param-values"; import { Props } from "./props"; @@ -312,7 +312,7 @@ export abstract class Nub extends ComputeNode implements IObservable { */ public CalcSerie(rInit?: number, sDonnee?: any): Result { // variated parameters caracteristics - const variated: Array<{ param: ParamDefinition, values: ParamValues, extendedValues: number[] }> = []; + const variated: Array<{ param: ParamDefinition, values: ParamValues }> = []; // prepare calculation this.progress = 0; @@ -334,9 +334,7 @@ export abstract class Nub extends ComputeNode implements IObservable { param: p, // extract variated values from variated Parameters // (in LINK mode, proxies to target data) - values: p.paramValues, - // values list by default, will be extended later - extendedValues: p.paramValues.getInferredValuesList() + values: p.paramValues }); } } @@ -360,62 +358,34 @@ export abstract class Nub extends ComputeNode implements IObservable { longest = i; } } - - // for all variating parameters other than the longest one, - // complete the series depending on the strategy (recycle / extend) const size = variated[longest].values.valuesIterator.count(); - for (let i = 0; i < variated.length; i++) { - if (i !== longest) { - const vp = variated[i].param; - const vxv = variated[i].extendedValues; - // if sizes differ - if (vxv.length < size) { - const newValues = [...vxv]; // array copy - // extend values - switch (vp.extensionStrategy) { - case ExtensionStrategy.REPEAT_LAST: - while (newValues.length < size) { - newValues.push(vxv[vxv.length - 1]); - } - break; - - case ExtensionStrategy.RECYCLE: - let j = 0; - while (newValues.length < size) { - newValues.push(vxv[j]); - j ++; - if (j === vxv.length) { - j = 0; - } - } - break; - } - // store extended values - variated[i].extendedValues = newValues; - } - } - } // grant the remaining percentage of the progressbar to local calculation // (should be 50% if any chain calculation occurred, 100% otherwise) let progressStep; const remainingProgress = 100 - this.progress; - const nbValues = variated[longest].extendedValues.length; - progressStep = remainingProgress / nbValues; + progressStep = remainingProgress / size; const res = new Result(undefined, this); - // iterate over longest series @TODO iterate over extendedValues directly ? - variated[longest].values.initValuesIterator(false); - let index = 0; + // (re)init all iterators + for (const v of variated) { + if (v === variated[longest]) { + v.values.initValuesIterator(false); + } else { + v.values.initValuesIterator(false, size); + } + } + + // iterate over longest series (in fact any series would do) while (variated[longest].values.hasNext) { - variated[longest].values.next(); // move on // get next value for all variating parameters for (const v of variated) { - v.param.v = v.extendedValues[index]; // copy to local sandbox value + const currentIteratorValue = v.values.next(); + v.param.v = currentIteratorValue.value; } - - this.doCalc(computedSymbol, rInit); // résultat dans this._result + // calculate + this.doCalc(computedSymbol, rInit); // résultat dans this._result if (this._result.ok) { res.addResultElement(this._result.resultElement); res.addLog(this._result.log); @@ -424,7 +394,6 @@ export abstract class Nub extends ComputeNode implements IObservable { res.globalLog.addLog(this._result.globalLog); // update progress this.progress += progressStep; - index ++; } this._result = res; // round progress to 100% diff --git a/src/param/param-definition.ts b/src/param/param-definition.ts index ee48deda..e015b999 100644 --- a/src/param/param-definition.ts +++ b/src/param/param-definition.ts @@ -69,7 +69,7 @@ export class ParamDefinition implements INamedIterableValues, IObservable { public v: number; /** extension strategy, when multiple parameters vary */ - public extensionStrategy: ExtensionStrategy; + private _extensionStrategy: ExtensionStrategy; /** mode de génération des valeurs : min/max, liste, ... */ private _valueMode: ParamValueMode; @@ -206,6 +206,20 @@ export class ParamDefinition implements INamedIterableValues, IObservable { return this._domain.interval; } + public get extensionStrategy(): ExtensionStrategy { + return this._extensionStrategy; + } + + public set extensionStrategy(strategy: ExtensionStrategy) { + this._extensionStrategy = strategy; + // synchronise with underlying local ParamValues (for iterator), except for links + if ([ ParamValueMode.SINGLE, ParamValueMode.MINMAX, ParamValueMode.LISTE ].includes(this.valueMode)) { + if (this._paramValues) { + this._paramValues.extensionStrategy = strategy; + } + } + } + public get valueMode(): ParamValueMode { return this._valueMode; } diff --git a/src/param/param-value-iterator.ts b/src/param/param-value-iterator.ts index c11d0a32..d01c8ed6 100644 --- a/src/param/param-value-iterator.ts +++ b/src/param/param-value-iterator.ts @@ -1,5 +1,6 @@ import { INamedObject, IObjectWithFamily } from "../jalhyd_object"; import { ArrayReverseIterator } from "../util/iterator"; +import { ExtensionStrategy } from "./param-definition"; import { ParamValueMode } from "./param-value-mode"; import { ParamValues } from "./param-values"; @@ -56,27 +57,32 @@ export interface INamedIterableValues extends INamedObject, IObjectWithFamily, I * itérateur sur les (ou la) valeurs prises par un ParamValues */ export class ParamValueIterator implements INumberIterator { - /** - * paramètre à itérer - */ + + /** paramètre à itérer */ private _param: ParamValues; /** - * true si les valeurs sont fournies de max à min (ParamValueMode.MINMAX) + * true si les valeurs sont fournies de max à min (ParamValueMode.MINMAX); + * utilisé uniquement par Remous */ private _reverse: boolean; - /** - * valeur courante - */ + /** length to extend the values series to, depending on _param ExtensionStrategy */ + private _extendTo: number; + + /** valeur courante */ private _current: number; + /** index sur les valeurs définies (et non sur les valeurs étendues) */ private _index: number; - constructor(prm: ParamValues, reverse: boolean = false) { + /** index sur les valeurs étendues (lorsque plusieurs paramètres varient) */ + private _xindex: number; + + constructor(prm: ParamValues, reverse: boolean = false, extendTo: number) { prm.check(); this._param = prm; - this.reset(reverse); + this.reset(reverse, extendTo); } /** @@ -91,8 +97,10 @@ export class ParamValueIterator implements INumberIterator { return length; } - public reset(reverse: boolean) { + public reset(reverse: boolean, extendTo?: number) { this._reverse = reverse; + this._extendTo = extendTo; + this._xindex = undefined; switch (this._param.valueMode) { case ParamValueMode.SINGLE: @@ -116,27 +124,6 @@ export class ParamValueIterator implements INumberIterator { throw new Error(`ParamValueIterator : mode de génération de valeurs ${ParamValueMode[this._param.valueMode]} incorrect`); } } - - public get hasNext(): boolean { - switch (this._param.valueMode) { - - case ParamValueMode.SINGLE: - return this._index === 0; - - case ParamValueMode.MINMAX: - const end = this._reverse ? - this._index < this._param.min - this._param.step * 1E-7 : - this._index > this._param.max + this._param.step * 1E-7; - return !end; - - case ParamValueMode.LISTE: - return this._index < this._param.valueList.length; - - default: - throw new Error(`ParamValueIterator.hasNext() : erreur interne`); - } - } - public next(): IteratorResult<number> { switch (this._param.valueMode) { @@ -156,8 +143,8 @@ export class ParamValueIterator implements INumberIterator { } case ParamValueMode.MINMAX: - this._current = this._index; - if (this.hasNext) { + if (this.hasNextWithoutExtension) { // default case + this._current = this._index; if (this._reverse) { this._index -= this._param.step; } else { @@ -167,29 +154,94 @@ export class ParamValueIterator implements INumberIterator { done: false, value: this._current }; - } else { - return { - done: true, - value: undefined - }; + } else { // no more real values + if (this._extendTo && this.hasNext) { + // extend + switch (this._param.extensionStrategy) { + case ExtensionStrategy.REPEAT_LAST: + // repeat last real value (do not change this._current) + return { + done: false, + value: this._current + }; + + case ExtensionStrategy.RECYCLE: + // loop over real values + if ( + this._xindex === undefined + || ( // end reached + this._reverse ? + this._xindex < this._param.min - this._param.step * 1E-7 : + this._xindex > this._param.max + this._param.step * 1E-7 + ) + ) { + this._xindex = this._reverse ? this._param.max : this._param.min; + } + this._current = this._index; + if (this._reverse) { + this._xindex -= this._param.step; + } else { + this._xindex += this._param.step; + } + return { + done: false, + value: this._current + }; + } + } else { + return { + done: true, + value: undefined + }; + } } case ParamValueMode.LISTE: - if (this.hasNext) { - this._current = this._param.valueList[this._index++]; + if (this.hasNextWithoutExtension) { // default case + this._current = this._param.valueList[this._index++]; // what about _reverse ? return { done: false, value: this._current }; - } else { - return { - done: true, - value: undefined - }; + } else { // no more real values + /* console.log(`extending LISTE with ${ExtensionStrategy[this._param.extensionStrategy]} ` + + `strategy : ${this._param.valueList} real values, ` + + `${this._extendTo} extended values, index=${this._index}, ` + + `xindex=${this._xindex}, current=${this._current}, hasNext=${this.hasNext}`); */ + if (this._extendTo && this.hasNext) { + // extend + switch (this._param.extensionStrategy) { + case ExtensionStrategy.REPEAT_LAST: + // repeat last real value (do not change this._current) + return { + done: false, + value: this._current + }; + + case ExtensionStrategy.RECYCLE: + // loop over real values + if ( + this._xindex === undefined + || this._xindex === this._param.valueList.length + ) { + this._xindex = 0; + } + this._current = this._param.valueList[this._xindex++]; + return { + done: false, + value: this._current + }; + } + } else { + return { + done: true, + value: undefined + }; + } } default: - throw new Error(`ParamValueIterator.next() : erreur interne`); + throw new Error(`ParamValueIterator.next() : internal error`); } } @@ -202,6 +254,47 @@ export class ParamValueIterator implements INumberIterator { public [Symbol.iterator](): IterableIterator<number> { return this; } + + public get hasNext(): boolean { + return this.hasNextValue(); + } + + protected get hasNextWithoutExtension() { + return this.hasNextValue(true); + } + + /** + * Returns true if iterator has at least one more value + * @param ignoreExtension if true, will consider only real values (those that were + * set up) and ignore extended values (when multiple parameters are varying) + */ + protected hasNextValue(ignoreExtension: boolean = false): boolean { + switch (this._param.valueMode) { + + case ParamValueMode.SINGLE: + return this._index === 0; + + case ParamValueMode.MINMAX: + if (this._extendTo && !ignoreExtension) { + return this._index < this._extendTo; + } else { + const end = this._reverse ? + this._index < this._param.min - this._param.step * 1E-7 : + this._index > this._param.max + this._param.step * 1E-7; + return !end; + } + + case ParamValueMode.LISTE: + if (this._extendTo && !ignoreExtension) { + return this._index < this._extendTo; + } else { + return this._index < this._param.valueList.length; + } + + default: + throw new Error(`ParamValueIterator.hasNext() : internal error`); + } + } } /** diff --git a/src/param/param-values.ts b/src/param/param-values.ts index d4919b3f..9da01d09 100644 --- a/src/param/param-values.ts +++ b/src/param/param-values.ts @@ -1,3 +1,4 @@ +import { ExtensionStrategy } from "./param-definition"; import { INumberIterator, IterableValues, ParamValueIterator } from "./param-value-iterator"; import { ParamValueMode } from "./param-value-mode"; @@ -8,11 +9,12 @@ import { ParamValueMode } from "./param-value-mode"; */ export class ParamValues implements IterableValues { - /** - * usually set by enclosing ParamDefinition - */ + /** usually set by enclosing ParamDefinition */ public valueMode: ParamValueMode; + /** usually set by enclosing ParamDefinition */ + public extensionStrategy: ExtensionStrategy; + /** valeur courante (éventuellement non définie) indépendemment du mode */ public currentValue: number; @@ -130,8 +132,8 @@ export class ParamValues implements IterableValues { * crée un ParamValueIterator * @param reverse true si on veut itérer max->min ou depuis la fin de la liste */ - public getValuesIterator(reverse: boolean = false): INumberIterator { - return new ParamValueIterator(this, reverse); + public getValuesIterator(reverse: boolean = false, extendTo?: number): INumberIterator { + return new ParamValueIterator(this, reverse, extendTo); } // interface IterableValues @@ -156,11 +158,11 @@ export class ParamValues implements IterableValues { } } - public initValuesIterator(reverse: boolean = false): INumberIterator { + public initValuesIterator(reverse: boolean = false, extendTo?: number): INumberIterator { switch (this.valueMode) { case ParamValueMode.LISTE: case ParamValueMode.MINMAX: - this._iterator = this.getValuesIterator(reverse); + this._iterator = this.getValuesIterator(reverse, extendTo); break; default: diff --git a/src/section/section_parametree.ts b/src/section/section_parametree.ts index 4a7da8dd..b88b68ae 100644 --- a/src/section/section_parametree.ts +++ b/src/section/section_parametree.ts @@ -147,6 +147,9 @@ export class SectionParametree extends SectionNub { return this.Calc(); } + // does nothing or else tests break !? + protected copySingleValuesToSandboxValues() { } + // tslint:disable-next-line:no-empty protected setParametersCalculability(): void {} -- GitLab