UNPKG

jalhyd

Version:

JaLHyd, a Javascript Library for Hydraulics

1,184 lines 67.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Nub = void 0; const internal_modules_1 = require("./internal_modules"); const internal_modules_2 = require("./internal_modules"); const internal_modules_3 = require("./internal_modules"); const internal_modules_4 = require("./internal_modules"); const internal_modules_5 = require("./internal_modules"); const internal_modules_6 = require("./internal_modules"); const internal_modules_7 = require("./internal_modules"); const internal_modules_8 = require("./internal_modules"); const internal_modules_9 = require("./internal_modules"); const internal_modules_10 = require("./internal_modules"); const internal_modules_11 = require("./internal_modules"); const internal_modules_12 = require("./internal_modules"); const internal_modules_13 = require("./internal_modules"); class NubIterator { constructor(n) { this._nubs = []; this.fill(n); this._index = 0; } fill(n) { this._nubs.push(n); for (const c of n.getChildren()) { this.fill(c); } } // interface IterableIterator next() { const i = this._index; if (this._index < this._nubs.length) { this._index = i + 1; return { done: false, value: this._nubs[i] }; } else { return { done: true, value: undefined }; } } [Symbol.iterator]() { return this; } } /** * Classe abstraite de Noeud de calcul dans une session : * classe de base pour tous les calculs */ class Nub extends internal_modules_1.ComputeNode { constructor(prms, dbg = false) { super(prms, dbg); /** paramétrage de la dichotomie */ this.dichoStartIntervalMaxSteps = 100; /** properties describing the Nub type */ this._props = new internal_modules_8.Props(); /** a rough indication of calculation progress, between 0 and 100 */ this._progress = 0; /** allows notifying of progress every X milliseconds only */ this.previousNotificationTimestamp = 0; this.deleteAllChildren(); this._observable = new internal_modules_11.Observable(); this._defaultCalculatedParam = this.getFirstAnalyticalParameter(); this.resetDefaultCalculatedParam(); } /** * Find longest series, BUT: if any varying parameter is a calculation result, * its values can't be extended (could be, but not at the moment), so * the global size in this case will be the min of sizes of linked variated results * @param variated list of variated parameter/valuesList pairs to examinate */ static findVariatedSize(variated) { if (variated.length > 0) { let minLinkedResultParam; let minLinkedResultsSize = Infinity; let longest = 0; for (let i = 0; i < variated.length; i++) { if (variated[i].param.valueMode === internal_modules_6.ParamValueMode.LINK && variated[i].param.referencedValue.isCalculated()) { if (variated[i].values.valuesIterator.count() < minLinkedResultsSize) { minLinkedResultParam = variated[i]; minLinkedResultsSize = minLinkedResultParam.values.valuesIterator.count(); } } if (variated[i].values.valuesIterator.count() > variated[longest].values.valuesIterator.count()) { longest = i; } } return { size: variated[longest].values.valuesIterator.count(), longest, minLinkedResultParam }; } else { return { size: 0, longest: undefined, minLinkedResultParam: undefined }; } } static concatPrms(p1, p2) { const p3 = p1; for (const p of p2) { p3.push(p); } return p3; } get parent() { return this._parent; } setParent(p) { this._parent = p; } get intlType() { return this._intlType; } get result() { return this._result; } /** * Local setter to set result element of Equation() / Solve() / … as current * ResultElement, instead of overwriting the whole Result object * (used by CalcSerie with varying parameters) */ set currentResultElement(r) { if (!this._result) { this.initNewResultElement(); } this._result.resultElement = r.resultElement; } set properties(props) { this.setProperties(props); } /** * return ParamsEquation of all children recursively */ get childrenPrms() { const prms = []; if (this._children.length) { // if called within constructor, default class member value is not set yet for (const child of this._children) { prms.push(child.prms); if (child.getChildren()) { if (child.getChildren().length) { Nub.concatPrms(prms, child.childrenPrms); } } } } return prms; } /** * Returns an array with the calculable parameters */ get calculableParameters() { const calcPrms = []; for (const p of this.parameterIterator) { if ([internal_modules_5.ParamCalculability.DICHO, internal_modules_5.ParamCalculability.EQUATION].includes(p.calculability)) { calcPrms.push(p); } } return calcPrms; } /** * Returns an iterator over : * - own parameters (this._prms) * - children parameters (this._children[*]._prms) */ get parameterIterator() { const prms = []; prms.push(this._prms); if (this._children) { Nub.concatPrms(prms, this.childrenPrms); } return new internal_modules_7.ParamsEquationArrayIterator(prms); } get progress() { return this._progress; } /** * Updates the progress percentage and notifies observers, * at most once per 300ms */ set progress(v) { this._progress = v; const currentTime = new Date().getTime(); if ((currentTime - this.previousNotificationTimestamp) > 30 || v === 100) { // console.log(">> notifying !"); this.notifyProgressUpdated(); this.previousNotificationTimestamp = currentTime; } } get calcType() { return this.getPropValue("calcType"); } /** * set Nub calculator type. * give children the opportunity to react to assignment * @see Structure */ setCalculatorType(ct) { this.setPropValue("calcType", ct); } get calculatedParam() { return this._calculatedParam; } /** * Sets p as the parameter to be computed; sets it to CALC mode */ set calculatedParam(p) { this._calculatedParam = p; this._calculatedParam.valueMode = internal_modules_6.ParamValueMode.CALCUL; } /** * Returns a parameter descriptor compatible with Calc() methods, * ie. a symbol string for a Nub's main parameter, or an object * of the form { uid: , symbol: } for a Nub's sub-Nub parameter * (ex: Structure parameter in ParallelStructure) */ get calculatedParamDescriptor() { if (this.uid === this._calculatedParam.nubUid) { return this._calculatedParam.symbol; } else { return { uid: this._calculatedParam.nubUid, symbol: this._calculatedParam.symbol }; } } // move code out of setter to ease inheritance setProperties(props, resetProps = false) { // copy observers const observers = this._props.getObservers(); // empty props if (resetProps) { this._props.reset(); } // restore observers for (const obs of observers) { this._props.addObserver(obs); } // set new props values let error; for (const p of props.keys) { // try properties one by one so that if an error is thrown, // remaining properties are still copied let oldValue; try { // keep old value to restore it in case of an error, or else it breaks fixTargets() oldValue = this.getPropValue(p); // should always be undefined but who knows this.setPropValue(p, props.getPropValue(p)); } catch (e) { error = e; // no one will notice ^^ this.setPropValue(p, oldValue); } } // throw caught error if any if (error) { throw error; } } /** * Finds the previous calculated parameter and sets its mode to SINGLE */ unsetCalculatedParam(except) { for (const p of this.parameterIterator) { if (p.valueMode === internal_modules_6.ParamValueMode.CALCUL && p !== except) { p.setValueMode(internal_modules_6.ParamValueMode.SINGLE, false); } } } /** * @param excluded parameter to exclude from search * @returns true if one of the parameters (other than excluded if provided) is in calculated mode */ hasCalculedParam(excluded) { for (const p of this._prms) { if (p.valueMode == internal_modules_6.ParamValueMode.CALCUL && (p !== excluded || excluded === undefined)) { return true; } } return false; } /** * Tries to reset the calculated parameter, successively, to : * - the default one if it is in SINGLE mode * - the first SINGLE calculable parameter other than requirer * - the first MINMAX/LISTE calculable parameter other than requirer * * If no default calculated parameter is defined, does nothing. * * @param force checking of non pre-existing calculated parameter */ resetDefaultCalculatedParam(requirer, force = true) { if (this._defaultCalculatedParam) { // if forced, first check there is not already another parameter // in calculated mode (eg. in a sibling nub) other than requirer (if provided) let doReset = true; if (!force) { // children (except for Pab: in this case, child parameters // can be calculated and must not prevent reset) if (!(this instanceof internal_modules_3.Pab)) { for (const kid of this.getChildren()) { doReset = doReset && !kid.hasCalculedParam(requirer); } } doReset = doReset && !this.hasCalculedParam(requirer); } if (doReset) { // if default calculated param is not eligible to CALC mode if (requirer === this._defaultCalculatedParam || this._defaultCalculatedParam.valueMode === internal_modules_6.ParamValueMode.LINK || this._defaultCalculatedParam.valueMode === internal_modules_6.ParamValueMode.LISTE || this._defaultCalculatedParam.valueMode === internal_modules_6.ParamValueMode.MINMAX) { // first SINGLE calculable parameter if any const newCalculatedParam = this.findFirstCalculableParameter(requirer); if (newCalculatedParam) { this.calculatedParam = newCalculatedParam; } else { // @TODO throws when a new linkable nub is added, and all parameters are already linked ! throw Error("resetDefaultCalculatedParam : could not find any SINGLE/MINMAX/LISTE parameter"); } } else { // default one this.calculatedParam = this._defaultCalculatedParam; } } } else { // do nothing (ex: Section Paramétrée) } } /** * Returns the first visible calculable parameter other than otherThan, * that is set to SINGLE, MINMAX or LISTE mode (there might be none) */ findFirstCalculableParameter(otherThan) { for (const p of this.parameterIterator) { if ([internal_modules_5.ParamCalculability.EQUATION, internal_modules_5.ParamCalculability.DICHO].includes(p.calculability) && p.visible && p !== otherThan && [internal_modules_6.ParamValueMode.SINGLE, internal_modules_6.ParamValueMode.MINMAX, internal_modules_6.ParamValueMode.LISTE].includes(p.valueMode)) { return p; } } return undefined; } /** * Calculate and put in cache the symbol of first parameter calculable analytically */ get firstAnalyticalPrmSymbol() { if (this._firstAnalyticalPrmSymbol === undefined) { this._firstAnalyticalPrmSymbol = this.getFirstAnalyticalParameter().symbol; } return this._firstAnalyticalPrmSymbol; } /** * Run Equation with first analytical parameter to compute * Returns the result in number form */ EquationFirstAnalyticalParameter() { const res = this.Equation(this.firstAnalyticalPrmSymbol); if (!res.ok) { this._result = res; throw new Error(this.constructor.name + ".EquationFirstAnalyticalParameter(): fail"); } return res.vCalc; } /** * Calcul d'une équation quelle que soit l'inconnue à calculer; déclenche le calcul en * chaîne des modules en amont si nécessaire * @param sVarCalc nom de la variable à calculer * @param rInit valeur initiale de la variable à calculer dans le cas de la dichotomie */ Calc(sVarCalc, rInit) { let computedVar; // overload calculated param at execution time ? if (sVarCalc) { computedVar = this.getParameter(sVarCalc); } else { computedVar = this.calculatedParam; } if (rInit === undefined) { rInit = computedVar.initValue; } if (computedVar.isAnalytical()) { this.currentResultElement = this.Equation(sVarCalc); this._result.symbol = sVarCalc; return this._result; } const resSolve = this.Solve(sVarCalc, rInit); if (!resSolve.ok) { this.currentResultElement = resSolve; this._result.symbol = sVarCalc; return this._result; } const sAnalyticalPrm = this.getFirstAnalyticalParameter().symbol; computedVar.v = resSolve.vCalc; const res = this.Equation(sAnalyticalPrm); res.vCalc = resSolve.vCalc; this.currentResultElement = res; this._result.symbol = sVarCalc; this.notifyResultUpdated(); return this._result; } /** * Calculates required Nubs so that all input data is available; * uses 50% of the progress * @returns true if everything went OK, false otherwise */ triggerChainCalculation() { const requiredNubs1stLevel = this.getRequiredNubs(); if (requiredNubs1stLevel.length > 0) { const progressStep = Nub.progressPercentageAccordedToChainCalculation / requiredNubs1stLevel.length; for (const rn of requiredNubs1stLevel) { const r = rn.CalcSerie(undefined, false); if (r.hasGlobalError() || r.hasOnlyErrors) { // something has failed in chain return { ok: false, message: new internal_modules_10.Message(internal_modules_10.MessageCode.ERROR_IN_CALC_CHAIN) }; } else if (this.resultHasMultipleValues() && (r.hasErrorMessages() // some steps failed // or upstream Nub has already triggered a warning message; pass it on || r.globalLog.contains(internal_modules_10.MessageCode.WARNING_ERROR_IN_CALC_CHAIN_STEPS))) { // if a parameter varies, errors might have occurred for // certain steps (but not all steps) return { ok: true, message: new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_ERROR_IN_CALC_CHAIN_STEPS) }; } this.progress += progressStep; } // round progress to accorded percentage this.progress = Nub.progressPercentageAccordedToChainCalculation; } return { ok: true, message: undefined }; } /** * Returns a list of parameters that are fixed, either because their valueMode * is SINGLE, or because their valueMode is LINK and the reference is * defined and fixed. Does not take calculated parameters into account. */ findFixedParams() { const fixed = []; for (const p of this.parameterIterator) { if (p.visible && (p.valueMode === internal_modules_6.ParamValueMode.SINGLE || (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined() && !p.referencedValue.hasMultipleValues()))) { fixed.push(p); } } return fixed; } /** * Returns a list of parameters that are variating, either because their valueMode * is LISTE or MINMAX, or because their valueMode is LINK and the reference is * defined and variated. Does not take calculated parameters into account. */ findVariatedParams() { const variated = []; for (const p of this.parameterIterator) { if (p.valueMode === internal_modules_6.ParamValueMode.LISTE || p.valueMode === internal_modules_6.ParamValueMode.MINMAX || (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined() && p.referencedValue.hasMultipleValues())) { variated.push({ param: p, // extract variated values from variated Parameters // (in LINK mode, proxies to target data) values: p.paramValues }); } } return variated; } /** * compute log stats (# of error, warning, info) on all result elements for all nub (source and source's children) */ resultElementsLogStats(stats) { stats = this._result.resultElementsLogStats(0, stats); let sn = 1; const it = this.directChildNubIterator(); let icn = it.next(); while (!icn.done) { stats = icn.value.result.resultElementsLogStats(sn++, stats); icn = it.next(); } return stats; } /** * abstract of iterations logs */ generateAbstractLogMessages() { const stats = this.resultElementsLogStats(); // String()ify numbers below, to avoid decimals formatting on screen (ex: "3.000 errors encoutered...") if (stats.error > 0) { if (stats.error > 1) { this._result.globalLog.add(new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_ERRORS_ABSTRACT_PLUR, { nb: String(stats.error) })); } else { this._result.globalLog.add(new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_ERRORS_ABSTRACT, { nb: String(stats.error) }) // should always be "1" ); } } if (stats.warning > 0) { this._result.globalLog.add(new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_WARNINGS_ABSTRACT, { nb: String(stats.warning) })); } } /** * Effectue une série de calculs sur un paramètre; déclenche le calcul en chaîne * des modules en amont si nécessaire * @param rInit solution approximative du paramètre * @param resetDepending true pour resetResult() sur les Nubs dépendants également */ CalcSerie(rInit, resetDepending = true) { // prepare calculation let extraLogMessage; // potential chain calculation warning to add to result at the end this.progress = 0; this.resetResult([], resetDepending); const ccRes = this.triggerChainCalculation(); if (ccRes.ok) { // might still have a warning log if (ccRes.message !== undefined) { extraLogMessage = ccRes.message; } } else { // something went wrong in the chain this._result = new internal_modules_12.Result(undefined, this); this._result.globalLog.add(ccRes.message); return this._result; } this.copySingleValuesToSandboxValues(); // variated parameters caracteristics const variated = this.findVariatedParams(); // find calculated parameter const computedSymbol = this.findCalculatedParameter(); if (rInit === undefined && this.calculatedParam) { rInit = this.calculatedParam.v; } // reinit Result and children Results this.reinitResult(); if (variated.length === 0) { // no parameter is varying // prepare a new slot to store results this.initNewResultElement(); this.doCalc(computedSymbol, rInit); // résultat dans this.currentResult (resultElement le plus récent) this.progress = 100; } else { // at least one parameter is varying const fvsRes = Nub.findVariatedSize(variated); let size = fvsRes.size; const longest = fvsRes.longest; // if at least one linked variated result was found if (fvsRes.minLinkedResultParam !== undefined) { // if the size limited by linked variated results is shorter // than the size of the longest variating element, limit it if (fvsRes.minLinkedResultParam.values.valuesIterator.count() < size) { size = fvsRes.minLinkedResultParam.values.valuesIterator.count(); // and add a warning const m = new internal_modules_10.Message(internal_modules_10.MessageCode.WARNING_VARIATED_LENGTH_LIMITED_BY_LINKED_RESULT); m.extraVar.symbol = fvsRes.minLinkedResultParam.param.symbol; m.extraVar.size = size; this._result.globalLog.add(m); } } // 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; progressStep = remainingProgress / size; // (re)init all iterators for (const v of variated) { // copy iterators, for linked params @see bug #222 if (v === variated[longest]) { v.iterator = v.values.initValuesIterator(false, undefined, true); } else { v.iterator = v.values.initValuesIterator(false, size, true); } } // iterate over longest series (in fact any series would do) let l = 0; // extra counter if size is limited by linked variated results while (variated[longest].values.hasNext && l < size) { // get next value for all variating parameters for (const v of variated) { const currentIteratorValue = v.iterator.nextValue(); v.param.v = currentIteratorValue.value; } // prepare a new slot to store results this.initNewResultElement(); // calculate this.doCalc(computedSymbol, rInit); // résultat dans this.currentResult (resultElement le plus récent) if (this._result.resultElement.ok) { rInit = this._result.resultElement.vCalc; } // update progress this.progress += progressStep; l++; } // round progress to 100% this.progress = 100; this.generateAbstractLogMessages(); } if (computedSymbol !== undefined) { const realSymbol = (typeof computedSymbol === "string") ? computedSymbol : computedSymbol.symbol; this._result.symbol = realSymbol; } this.notifyResultUpdated(); if (extraLogMessage !== undefined) { this._result.globalLog.add(extraLogMessage); } return this._result; } addChild(child, after) { if (after !== undefined) { this._children.splice(after + 1, 0, child); } else { this._children.push(child); } // add reference to parent collection (this) child.setParent(this); // postprocessing this.adjustChildParameters(child); } getChildren() { return this._children; } /** * @returns iterator on direct child nubs (may include extra nubs, see Pab nub) */ directChildNubIterator() { return this._children[Symbol.iterator](); } /** * @returns iterator on all child nubs (recursively) */ get allChildNubIterator() { return new NubIterator(this); } /** * Returns true if all parameters are valid; used to check validity of * parameters linked to Nub results */ isComputable() { let valid = true; for (const p of this.prms) { valid = valid && p.isValid; } return valid; } /** * @returns true if nub is an orifice structure */ get isOrifice() { if (this._prms.hasParameter("h1") && this._prms.hasParameter("ZDV")) { return !this.prms.get("h1").visible && !this.prms.get("ZDV").visible; } return false; } /** * Liste des valeurs (paramètre, résultat, résultat complémentaire) liables à un paramètre * @param src paramètre auquel lier d'autres valeurs */ getLinkableValues(src) { let res = []; const symbol = src.symbol; // If parameter comes from the same Nub, its parent or any of its children, // no linking is possible at all. // Different Structures in the same parent can get linked to each other. if (!this.isParentOrChildOf(src.nubUid) && ( // check grand-parent for PreBarrage special case @TODO find a generic way // to perform both tests (synthesise .nubUid and .originNub.uid) !(this.calcType === internal_modules_1.CalculatorType.PreBarrage) || !this.isParentOrChildOf(src.originNub.uid))) { // 1. own parameters for (const p of this._prms) { // if symbol and Nub type are identical if ((p.symbol === symbol && this.calcType === src.nubCalcType && p.visible) // or if this is a Section, and src direct parent is a Section too, and symbol is identical || (p.symbol === symbol && this instanceof internal_modules_3.acSection && src.getParentComputeNode(false) instanceof internal_modules_3.acSection && p.visible // different types of sections hide certain parameters ) // or if family is identical || (p.family !== undefined && p.family === src.family && p.visible) // or if one of the families is ANY and target parameter is visible || ((src.family === internal_modules_5.ParamFamily.ANY || p.family === internal_modules_5.ParamFamily.ANY) && p.visible)) { // if variability doesn't cause any problem (a non-variable // parameter cannot be linked to a variating one) if (src.calculability !== internal_modules_5.ParamCalculability.FIXED || !p.hasMultipleValues) { // if it is safe to link p's value to src if (p.isLinkableTo(src)) { // if p is a CALC param of a Structure other than "Q" // (structures always have Q as CALC param and cannot have another) // or a CALC param of a Section, that is not sibling of the target // (to prevent circular dependencies among ParallelStructures), // expose its parent if (((this instanceof internal_modules_3.Structure && p.symbol !== "Q" && !this.isSiblingOf(src.nubUid)) || this instanceof internal_modules_3.acSection) && (p.valueMode === internal_modules_6.ParamValueMode.CALCUL)) { if (this.parent._prms.hasParameter(symbol)) { // trick to expose p as a result of the parent Nub res.push(new internal_modules_4.LinkedValue(this.parent, p, p.symbol)); } else { // in case parent does not hold parameter, inform about real owning nub (this) res.push(new internal_modules_4.LinkedValue(this.parent, p, p.symbol, undefined, this)); } } else { // do not suggest parameters that are already linked to another one if (p.valueMode !== internal_modules_6.ParamValueMode.LINK) { res.push(new internal_modules_4.LinkedValue(this, p, p.symbol)); } } } } } } // 2. extra results if (this._resultsFamilies) { // if I don't depend on your result, you may depend on mine ! if (!this.dependsOnNubResult(src.parentNub)) { const erk = Object.keys(this._resultsFamilies); // browse extra results for (const erSymbol of erk) { const erFamily = this._resultsFamilies[erSymbol]; // if family is identical or ANY, and variability doesn't cause any problem if ((( // both families cannot be undefined erFamily === src.family && erFamily !== undefined) || erFamily === internal_modules_5.ParamFamily.ANY || src.family === internal_modules_5.ParamFamily.ANY) && (src.calculability !== internal_modules_5.ParamCalculability.FIXED || !this.resultHasMultipleValues())) { res.push(new internal_modules_4.LinkedValue(this, undefined, erSymbol)); } } } } } // 3. children Nubs, except for PAB and MRC and PreBarrage if (!(this instanceof internal_modules_3.MacrorugoCompound) && ( // meta-except, if source param in a PAB's Cloison (should only apply to QA) !(this instanceof internal_modules_3.Pab) || (src.originNub instanceof internal_modules_3.Pab && src.symbol === "QA")) && this.calcType !== internal_modules_1.CalculatorType.PreBarrage) { for (const cn of this.getChildren()) { res = res.concat(cn.getLinkableValues(src)); } } return res; } /** * Returns true if the given Nub UID is either of : * - the current Nub UID * - the current Nub's parent Nub UID * - the UID of any of the current Nub's children */ isParentOrChildOf(uid) { if (this.uid === uid) { return true; } if (this._parent && this._parent.uid === uid) { return true; } for (const c of this.getChildren()) { if (c.uid === uid) { return true; } } return false; } /** * Returns true if the given Nub UID : * - is the current Nub UID * - is the UID of any of the current Nub's siblings (children of the same parent) */ isSiblingOf(uid) { if (this.uid === uid) { return true; } if (this._parent) { for (const c of this._parent.getChildren()) { if (c.uid === uid) { return true; } } return true; } return false; } /** * Returns all Nubs whose results are required by the given one, * without following links (stops when it finds a Nub that has to * be calculated). Used to trigger chain calculation. */ getRequiredNubs(visited = []) { let requiredNubs = []; // prevent loops if (!visited.includes(this.uid)) { visited.push(this.uid); // inspect all target Nubs for (const p of this.parameterIterator) { if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined()) { const nub = p.referencedValue.nub; // a Nub is required if I depend on its result if (this.dependsOnNubResult(nub, false)) { requiredNubs.push(nub); } } } } // 1. deduplicate requiredNubs = requiredNubs.filter((element, index, self) => self.findIndex((e) => e.uid === element.uid) === index); // 2. only keep requiredNubs that are not dependencies of others const realRequiredNubs = []; for (const rn of requiredNubs) { let keep = true; // test all combinations for (const rn2 of requiredNubs) { if (rn.uid !== rn2.uid) { keep = (keep && !rn2.dependsOnNubResult(rn, true)); } } if (keep) { realRequiredNubs.push(rn); } } return realRequiredNubs; } /** * Returns all Nubs whose results are required by the given one, * following links. Used by Solveur. */ getRequiredNubsDeep(visited = []) { let requiredNubs = this.getRequiredNubs(visited); for (const rn of requiredNubs) { requiredNubs = requiredNubs.concat(rn.getRequiredNubsDeep(visited)); } requiredNubs = requiredNubs.filter((item, index) => requiredNubs.indexOf(item) === index // deduplicate ); return requiredNubs; } /** * Returns all Nubs whose parameters or results are targetted * by the given one. * (used for dependencies checking at session saving time) */ getTargettedNubs(visited = []) { const targettedNubs = []; // prevent loops if (!visited.includes(this.uid)) { visited.push(this.uid); // inspect all target Nubs for (const p of this.parameterIterator) { if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined()) { targettedNubs.push(p.referencedValue.nub); } } } return targettedNubs; } /** * Returns true if * - this Nub * - any of this Nub's children * - this Nub's parent * depends on * - the result of the given Nub * - the result of any of the given Nub's children * [ note: but NOT the result of the given Nub's parent, or the given Nub might * be recalculated independently, which is not wanted ] * * (ie. "my family depends on the result of your family") * * If followLinksChain is false, will limit the exploration to the first target level only */ dependsOnNubResult(nub, followLinksChain = true) { let thisFamily = [this]; if (this._parent) { thisFamily = thisFamily.concat(this._parent); } thisFamily = thisFamily.concat(this.getChildren()); let yourFamily = []; const you = nub; if (you) { yourFamily = [you].concat(you.getChildren()); } let depends = false; outerloop: for (const t of thisFamily) { // look for a parameter linked to the result of any Nub of yourFamily for (const p of t.prms) { if (p.valueMode === internal_modules_6.ParamValueMode.LINK) { if (p.isLinkedToResultOfNubs(yourFamily, followLinksChain)) { // dependence found ! depends = true; break outerloop; } } } } return depends; } /** * Returns true if this nub (parent and children included) * directly requires (1st level only) * the given parameter */ dependsOnParameter(src) { let thisFamily = [this]; if (this._parent) { thisFamily = thisFamily.concat(this._parent); } thisFamily = thisFamily.concat(this.getChildren()); let depends = false; outerloop: for (const t of thisFamily) { // look for a parameter of thisFamily that is directly linked to src for (const p of t.prms) { if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined() && p.referencedValue.isParameter()) { if (p.referencedValue.nub.uid === src.nubUid && p.referencedValue.symbol === src.symbol) { // dependence found ! depends = true; break outerloop; } } } } return depends; } /** * Returns true if the computation of the current Nub (parent and children * included) directly requires (1st level only), anything (parameter or result) * from the given Nub UID "uid", its parent or any of its children * @param uid * @param symbol symbol of the target parameter whose value change triggered this method; * if current Nub targets this symbol, it will be considered dependent * @param includeValuesLinks if true, even if this Nub targets only non-calculated non-modified * parameters, it will be considered dependent @see jalhyd#98 * @param includeOtherDependencies if true, then: * - if this Nub is a Solveur, also returns true if ${uid} is either the X or the Ytarget's parent * Nub of this Solveur * - if this Nub is a Verificateur, also returns true if ${uid} is either a selected Custom * Species or the Pass to check of this Verificateur */ resultDependsOnNub(uid, visited = [], symbol, includeValuesLinks = false, includeOtherDependencies = false) { var _a, _b; if (uid !== this.uid && !visited.includes(this.uid)) { visited.push(this.uid); // does any of our parameters depend on the target Nub ? for (const p of this._prms) { if (p.valueMode === internal_modules_6.ParamValueMode.LINK) { if (p.dependsOnNubFamily(uid, symbol, includeValuesLinks)) { return true; } } } // does any of our parent's parameters depend on the target Nub ? if (this._parent) { if (this._parent.resultDependsOnNub(uid, visited, symbol, includeValuesLinks, includeOtherDependencies)) { return true; } } // does any of our children' parameters depend on the target Nub ? for (const c of this.getChildren()) { if (c.resultDependsOnNub(uid, visited, symbol, includeValuesLinks, includeOtherDependencies)) { return true; } } // other dependencies if (includeOtherDependencies) { // is this a Solveur if (this instanceof internal_modules_3.Solveur) { return ((((_a = this.searchedParameter) === null || _a === void 0 ? void 0 : _a.nubUid) === uid) || (((_b = this.nubToCalculate) === null || _b === void 0 ? void 0 : _b.uid) === uid)); } if (this instanceof internal_modules_3.Verificateur) { return ((this.nubToVerify !== undefined && this.nubToVerify.uid === uid) || (this.speciesList.includes(uid))); } if (this instanceof internal_modules_3.MacrorugoRemous) { return ((this.nubMacroRugo !== undefined && this.getPropValue("nubMacroRugo") === uid)); } } } return false; } /** * Returns true if the computation of the current Nub has multiple values * (whether it is already computed or not), by detecting if any parameter, * linked or not, is variated */ resultHasMultipleValues() { for (const p of this.parameterIterator) { if (p.valueMode === internal_modules_6.ParamValueMode.MINMAX || p.valueMode === internal_modules_6.ParamValueMode.LISTE) { return true; } else if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined()) { // indirect recursivity if (p.referencedValue.hasMultipleValues()) { return true; } } } return false; } /** * Sets the current result to undefined, as well as the results * of all depending Nubs; also invalidates all fake ParamValues * held by LinkedValues pointing to this result * @param resetDepending true pour resetResult() sur les Nubs dépendants également */ resetResult(visited = [], resetDepending = true) { this._result = undefined; visited.push(this.uid); const dependingNubs = internal_modules_3.Session.getInstance().getDependingNubs(this.uid); // @TODO include Solveur / Verificateur links ? for (const dn of dependingNubs) { if (resetDepending && !visited.includes(dn.uid)) { dn.resetResult(visited); } dn.resetLinkedParamValues(this); } } /** * For all parameters pointing to the result of the given Nub, * invalidates fake ParamValues held by pointed LinkedValue */ resetLinkedParamValues(nub) { for (const p of this.parameterIterator) { if (p.valueMode === internal_modules_6.ParamValueMode.LINK && p.isReferenceDefined()) { if ((p.referencedValue.isResult() || p.referencedValue.isExtraResult()) && p.referencedValue.nub.uid === nub.uid) { p.referencedValue.invalidateParamValues(); } } } } /** * Duplicates the current Nub, but does not register it in the session */ clone() { const serialised = this.serialise(); const clone = internal_modules_3.Session.getInstance().unserialiseSingleNub(serialised, false); return clone.nub; } /** * Returns a JSON representation of the Nub's current state * @param extra extra key-value pairs, for ex. calculator title in GUI * @param nubUidsInSession UIDs of Nubs that will be saved in session along with this one; * useful to determine if linked parameters must be kept as links * or have their value copied (if target is not in UIDs list) */ serialise(extra, nubUidsInSession) { return JSON.stringify(this.objectRepresentation(extra, nubUidsInSession)); } /** * Returns an object representation of the Nub's current state * @param extra extra key-value pairs, for ex. calculator title in GUI * @param nubUidsInSession UIDs of Nubs that will be saved in session along with this one; * useful to determine if linked parameters must be kept as links * or have their value copied (if target is not in UIDs list) */ objectRepresentation(extra, nubUidsInSession) { let ret = { uid: this.uid, props: this.invertedPropertiesEnumAndValues(), }; if (extra) { ret = Object.assign(Object.assign({}, ret), { meta: extra }); // merge properties } ret = Object.assign(Object.assign({}, ret), { children: [], parameters: [] }); // extend here to make "parameters" the down-most key // iterate over local parameters const localParametersIterator = new internal_modules_7.ParamsEquationArrayIterator([this._prms]); for (const p of localParametersIterator) { if (p.visible) { ret.parameters.push(p.objectRepresentation(nubUidsInSession)); } } // iterate over children Nubs for (const child of this._children) { ret.children.push(child.objectRepresentation(undefined, nubUidsInSession)); } return ret; } /** * Fills the current Nub with parameter values, provided an object representation * @param obj object representation of a Nub content (parameters) * @returns the calculated parameter found, if any - used by child Nub to notify * its parent of the calculated parameter to set */ loadObjectRepresentation(obj) { // return value const ret = { p: undefined, hasErrors: false, changedUids: {} }; // set parameter modes and values if (obj.parameters && Array.isArray(obj.parameters)) { // 1st pass: find calculated param // (if calculated param is not the default one, and default one is processed // before new one, prevents changing the former's mode from setting the 1st // param of the Nub to calculated, resetting the mode that had been loaded) for (const p of obj.parameters) { if (internal_modules_6.ParamValueMode[p.mode] === internal_modules_6.ParamValueMode.CALCUL) { this.loadParam(p, ret); } } // define calculated param at Nub level if (ret.p) { this.calculatedParam = ret.p; } // set parameter modes and values - 2nd pass: set non-calculated params for (const p of obj.parameters) { if (internal_modules_6.ParamValueMode[p.mode] !== internal_modules_6.ParamValueMode.CALCUL) { this.loadParam(p, ret); } } } // iterate over children if any if (obj.children && Array.isArray(obj.children)) { for (const s of obj.children) { if (Object.keys(s.props).length > 0) { // decode properties const props = internal_modules_8.Props.invertEnumKeysAndValuesInProperties(s.props, true); // create the Nub const subNub = internal_modules_3.Session.getInstance().createNub(new internal_modules_8.Props(props), this); // try to keep the original ID if (!internal_modules_3.Session.getInstance().uidAlreadyUsed(s.uid)) { subNub.setUid(s.uid); } else { ret.changedUids[s.uid] = subNub.uid; } const childRet = subNub.loadObjectRepresentation(s); // add Structure to parent this.addChild(subNub); // set calculated parameter for child ? if (childRet.p) { this.calculatedParam = childRet.p; } // forward errors if (childRet.hasErrors) { ret.hasErrors = true; } } } } return ret; } /** * Load parameter state from JSON and amend "ret" status variable; * to be used by loadObjectRepresentation() only */ loadParam(p, ret) { const param = this.getParamet