UNPKG

jalhyd

Version:

JaLHyd, a Javascript Library for Hydraulics

639 lines 25.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PreBarrage = 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"); class PreBarrage extends internal_modules_1.Nub { constructor(prms, dbg = false) { super(prms, dbg); this.setCalculatorType(internal_modules_4.CalculatorType.PreBarrage); this._cloisonsAmont = []; this._bassins = []; //this._intlType = "Cloison"; } /** * paramètres castés au bon type */ get prms() { return this._prms; } /** * enfants castés au bon type */ get children() { return this._children; } get bassins() { return this._bassins; } get cloisonsAmont() { return this._cloisonsAmont; } /** * Removes a child, along with all features (basins or walls) that are * related only to it, and pointers to it */ deleteChild(index) { const item = this._children[index]; if (item instanceof internal_modules_5.PbBassin) { // reconnect walls to river upstream / downstream for (const w of item.cloisonsAmont) { w.bassinAval = undefined; } for (const w of item.cloisonsAval) { w.bassinAmont = undefined; } } else if (item instanceof internal_modules_6.PbCloison) { if (item.bassinAmont !== undefined) { item.bassinAmont.cloisonsAval = item.bassinAmont.cloisonsAval.filter((v) => v !== item); } if (item.bassinAval !== undefined) { item.bassinAval.cloisonsAmont = item.bassinAval.cloisonsAmont.filter((v) => v !== item); } } // remove item super.deleteChild(index); this.updatePointers(); } /** * Returns true if at least one path is connecting river upstream to river downstream, * using at least one basin (direct connection without basin doesn't count) */ hasUpDownConnection(startWall, nbBasins = 0, visited = []) { let ok = false; // starting at river upstream ? if (startWall === undefined) { if (this._cloisonsAmont.length === 0) { return false; } // browse graph downwards for (const ca of this._cloisonsAmont) { ok = ok || this.hasUpDownConnection(ca, 0, visited); } } else { // starting from a wall if (startWall.bassinAval === undefined) { return (nbBasins > 0); } else { // browse graph downwards for (const ca of startWall.bassinAval.cloisonsAval) { // prevent loops @TODO detect loops instead, and throw an error ? if (!visited.includes(ca)) { visited.push(ca); ok = ok || this.hasUpDownConnection(ca, nbBasins + 1, visited); } } } } return ok; } /** * Returns true if at least one basin is lacking upstream or downstream connection (or both) */ hasBasinNotConnected() { for (const b of this._bassins) { if (b.cloisonsAmont.length === 0 || b.cloisonsAval.length === 0) { return true; } } return false; } /** * Returns the child (basin or wall) having the given UID, along with its position * among the _children array (*not* the _bassins array !) */ findChildAndPosition(uid) { const res = { child: undefined, position: undefined }; if (uid !== undefined) { let pos = 0; for (const c of this.children) { if (internal_modules_10.MermaidUtil.isMermaidEqualIds(c.uid, uid)) { res.child = c; res.position = pos; break; } pos++; } } return res; } /** * Returns the child (basin or wall) having the given UID */ findChild(uid) { const { child } = this.findChildAndPosition(uid); return child; } /** * Returns the position of the child (basin or wall) having the given UID, * among the _children array (*not* the _bassins array !) */ findChildPosition(uid) { const { position } = this.findChildAndPosition(uid); return position; } /** * Returns the position of the basin having the given UID, * among the _bassins array (*not* the _children array !) */ findBasinPosition(uid) { let i = 0; for (const b of this._bassins) { if (b.uid === uid) { break; } i++; } return i; } /** * Moves the basin having the given UID, so that it becomes * basin #newBasinNumber, by reordering _children array and * rebuilding _bassins array afterwards */ moveBasin(uid, newBasinNumber) { if (newBasinNumber >= this._bassins.length) { throw new Error(`PreBarrage.moveBasin: cannot make it #${newBasinNumber}, there are only ${this._bassins.length} basins`); } const tmp = []; // find position of basin currently occupying #newBasinNumber, in _children array const newPosition = this.findChildPosition(this._bassins[newBasinNumber].uid); // find basin to move and its current position const { child, position } = this.findChildAndPosition(uid); if (position > newPosition) { // move up: // insert slice [0 - newPosition[ for (let i = 0; i < newPosition; i++) { tmp.push(this._children[i]); } // insert basin tmp.push(child); // insert slice [newPosition - position[ for (let i = newPosition; i < position; i++) { tmp.push(this._children[i]); } // insert slice ]position, end] for (let i = position + 1; i < this._children.length; i++) { tmp.push(this._children[i]); } this._children = tmp; } else if (position < newPosition) { // move down: // @TODO should we also move down all walls connected to the moving basin, so that // they are still declared after it and not before ? // insert slice [0 - position[ for (let i = 0; i < position; i++) { tmp.push(this._children[i]); } // insert slice ]position, newPosition[ for (let i = position + 1; i < newPosition + 1; i++) { tmp.push(this._children[i]); } // insert basin tmp.push(child); // insert slice [newPosition - end] for (let i = newPosition + 1; i < this._children.length; i++) { tmp.push(this._children[i]); } this._children = tmp; } // else already at the right position, do nothing this.updatePointers(); } /** * Detect duplicate walls between same basins pair, and merges * them into one wall containing all the previous walls' structures. */ detectAndMergeDuplicateWalls() { // browse all upstream basins for (const b of this._bassins) { // for each upstream basin, group walls by downstream basin this.mergeDuplicateWalls(b); } // walls between upstream and another basin for (const b of this._cloisonsAmont.map((ca) => ca.bassinAval)) { if (b !== undefined) { this.mergeDuplicateWalls(b, false); } } // walls between river upstream and downstream const udWalls = []; for (const w of this._children) { if (w instanceof internal_modules_6.PbCloison) { if (w.bassinAmont === undefined && w.bassinAval === undefined) { udWalls.push(w); } } } this.mergeStructuresInFirstWall(udWalls); } /** * Finds all walls connected to the given basin's upstream (is upstream is true) or * downstream. Among those walls, finds those who have the same basin at their * opposite connection and merges them into one wall. * @param b * @param upstream */ mergeDuplicateWalls(b, upstream = true) { // group walls attached to b by the other basin they are attached to const wallsByOtherBasin = {}; const linkedWalls = upstream ? b.cloisonsAval : b.cloisonsAmont; for (const w of linkedWalls) { let dbId = "-"; // river upstream / downstream const otherBasin = upstream ? w.bassinAval : w.bassinAmont; if (otherBasin !== undefined) { dbId = otherBasin.uid; } if (!Object.keys(wallsByOtherBasin).includes(dbId)) { wallsByOtherBasin[dbId] = []; } wallsByOtherBasin[dbId].push(w); } // if multiple walls attached to b share the same other basin, merge them for (const dbUid of Object.keys(wallsByOtherBasin)) { const dbWalls = wallsByOtherBasin[dbUid]; this.mergeStructuresInFirstWall(dbWalls); } } /** * Given a list of walls, moves all structures belonging to * structures #2+ into structure #1, and removes structures * #2+ from the current PreBarrage * @param walls */ mergeStructuresInFirstWall(walls) { if (walls.length > 1) { // move all structures of walls > 1 to 1st wall for (let i = 1; i < walls.length; i++) { const dbw = walls[i]; for (const s of dbw.structures) { walls[0].addChild(s); } // delete "merged" wall this.deleteChild(dbw.findPositionInParent()); } } } /** * 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 */ CalcSerie(rInit) { this.detectAndMergeDuplicateWalls(); this._precision = Math.max(5E-4, internal_modules_7.SessionSettings.precision); return super.CalcSerie(rInit); } Calc(sVarCalc, rInit) { // check elevations before calculating const cgResult = this.checkGeometry(); if (cgResult.resultElement.hasErrorMessages()) { this._result.resultElement = cgResult.resultElement; return this.result; } for (const c of this._children) { if (c instanceof internal_modules_6.PbCloison) { c.inhibitSubmergenceError = true; } } const res = super.Calc(sVarCalc, rInit); for (const c of this._children) { if (c instanceof internal_modules_6.PbCloison) { c.inhibitSubmergenceError = false; } } // calculate basins so that they have a proper .result for (const b of this._bassins) { b.Calc(); } // calculate Q on all walls so that their result shows Q and not Z1 for (const c of this._children) { if (c instanceof internal_modules_6.PbCloison) { // sauvegarde des messages générés pendant les itérations de dichotomie const logBackup = c.result.resultElement.log.cloneErrors(); c.finalCalc(); // restitution des messages sauvés c.result.resultElement.log.addLog(logBackup); } } // copy warnings issued by checkGeometry, if any for (const m of cgResult.resultElement.log.messages) { if (m.getSeverity() !== internal_modules_8.MessageSeverity.ERROR) { this._result.resultElement.log.add(m); } } // if an error occurred in any nub, remove all results // except if it's a dichotomy convergence error (and only this error) if (!this.allResultsOk && !this.hasOnlyMessage(internal_modules_8.MessageCode.ERROR_DICHO_CONVERGE)) { for (const c of this.allChildNubIterator) { c.result.resultElement.removeValues(); } } return res; } /** * Calcul de Z1 pour une valeur fixe des paramètres * @param sVarCalc ignoré: Z1 uniquement */ Equation(sVarCalc) { const maxIter = this.maxIterations !== undefined ? this.maxIterations : internal_modules_7.SessionSettings.maxIterations; if (maxIter === undefined || maxIter < 1) { throw new Error("invalid iteration count"); } // Initialisation des cotes sur les bassins et la CL amont if (this.isMeshed()) { for (const b of this.bassins) { const zB = b.getMinZDV(); if (zB !== undefined) { b.Z = Math.max(zB, b.prms.ZF.v) + 1; } else { b.Z = b.prms.ZF.v + 1; } } const Z1 = this.getMinZDV(this._cloisonsAmont); if (Z1 !== undefined) { this.prms.Z1.v = Math.max(Z1, this.bassins[0].Z) + 1; } else { this.prms.Z1.v = this.bassins[0].Z + 1; } } else { // On a le même débit dans tous les bassins et Cloisons for (const child of this.children) { if (child instanceof internal_modules_5.PbBassin) { child.Q = this.prms.Q.v; } else { child.prms.Q.v = this.prms.Q.v; } } } let iter = maxIter; let bConverged; let iterConverged = 1; let z1stat; // const tZ1: IPbBassinZstat[] = []; // const tQ: number[][] = []; while (iter-- > 0) { this._relax = Math.pow(iter / maxIter, 3) * 0.25 + 0.05; this.debug(`***** Iteration n°${iter} relax=${this._relax} *****`); bConverged = true; if (this.isMeshed()) { // Balayage amont-aval: distribution des débits this.debug("*** Calcul des Q amont --> aval ***"); // Répartition cloisons amont du PB // tQ.push(this.CalcQ(this.cloisonsAmont, this.prms.Q.v)); this.CalcQ(this._cloisonsAmont, this.prms.Q.v); // Répartition pour chaque bassin sens amont-aval // TODO s'assurer du sens amont-aval des bassins dans this.children for (const b of this.bassins) { this.debug("Bassin n°" + this.getIndexForChild(b)); this.CalcQ(b.cloisonsAval, b.CalcQ()); } } // Balayage aval-amont: Calcul des cotes amont des cloisons this.debug("*** Calcul des Z aval --> amont ***"); for (let i = this.bassins.length - 1; i >= 0; i--) { this.debug("Bassin " + i); const zStat = this.bassins[i].CalcZ(); bConverged = bConverged && (zStat.max - zStat.min) < this._precision; } this.debug("Cloison amont"); z1stat = this.CalcZ1Cloisons(this._cloisonsAmont); this.prms.Z1.v = z1stat.moy; // tZ1.push(z1stat); bConverged = bConverged && (z1stat.max - z1stat.min) < this._precision; if (bConverged) { iterConverged--; if (iterConverged < 0) break; } else { iterConverged = 1; } } // console.debug(tQ); // console.debug(tZ1); const r = new internal_modules_3.Result(this.prms.Z1.v, this); if (!bConverged) { r.resultElement.addMessage(new internal_modules_8.Message(internal_modules_8.MessageCode.WARNING_PREBARRAGE_NON_CONVERGENCE, { precision: (z1stat.max - z1stat.min) })); } return r; } /** * Checks pass geometry before calculation; adds relevant log messages */ checkGeometry() { // A. error throwing cases: cannot calculate // each basin must have at least one upstream wall and one downstream wall for (const b of this._bassins) { if (b.cloisonsAmont.length === 0 || b.cloisonsAval.length === 0) { throw new Error(`PreBarrage.checkGeometry(): basin ${b.findPositionInParent()} (starting at 0) must have at least one upstream wall (has ${b.cloisonsAmont.length}) and one downstream wall (has ${b.cloisonsAval.length})`); } } // PreBarrage must have an upstream wall and a downstream wall if (this.cloisonsAmont.length === 0 /* || this.cloisonsAval.length === 0 */) { throw new Error(`PreBarrage.checkGeometry(): must have at least one upstream wall (has ${this._cloisonsAmont.length})` /* + ` and one downstream wall (has ${b.cloisonsAval.length})` */); } // PreBarrage must have at least one path from upstream to downstream if (!this.hasUpDownConnection()) { throw new Error("PreBarrage.checkGeometry(): must have at least one path from upstream to downstream"); } // B. messages generating cases: calculation goes on const res = new internal_modules_3.Result(new internal_modules_9.ResultElement(), this); // downstream water elevation > upstream water elevation ? if (this.calculatedParam === this.prms.Q && this.prms.Z2.v > this.prms.Z1.v) { res.resultElement.log.add(new internal_modules_8.Message(internal_modules_8.MessageCode.ERROR_PREBARRAGE_Z2_SUP_Z1)); } // for each basin: is apron elevation > upstream water elevation ? for (const b of this._bassins) { if (b.prms.ZF.v > this.prms.Z1.v) { const m = new internal_modules_8.Message(internal_modules_8.MessageCode.WARNING_PREBARRAGE_BASSIN_ZF_SUP_Z1); m.extraVar.n = String(b.findPositionInParent() + 1); res.resultElement.log.add(m); } } return res; } setParametersCalculability() { this.prms.Z1.calculability = internal_modules_2.ParamCalculability.EQUATION; this.prms.Z2.calculability = internal_modules_2.ParamCalculability.FREE; this.prms.Q.calculability = internal_modules_2.ParamCalculability.DICHO; } adjustChildParameters(child) { this.updatePointers(); } /** Clears basins list then builds it again fom children list */ updatePointers() { this._bassins = []; this._cloisonsAmont = []; for (const c of this.children) { if (c.calcType === internal_modules_4.CalculatorType.PbBassin) { this._bassins.push(c); } else if (c instanceof internal_modules_6.PbCloison) { if (c.bassinAmont === undefined) { this._cloisonsAmont.push(c); } if (c.bassinAmont !== undefined) { if (!c.bassinAmont.cloisonsAval.includes(c)) { c.bassinAmont.cloisonsAval.push(c); } } if (c.bassinAval !== undefined) { if (!c.bassinAval.cloisonsAmont.includes(c)) { c.bassinAval.cloisonsAmont.push(c); } } } } } isMeshed() { if (this._cloisonsAmont.length > 1) { return true; } for (const bassin of this.bassins) { if (bassin.cloisonsAval.length > 1) { return true; } } return false; } CalcQ(cloisons, QT) { // Calculation of repartition regarding actual water elevations let QT2 = 0; for (const c of cloisons) { c.prms.Q.initValue = Math.max(0, c.prms.Q.v); c.Calc("Q"); // Relax! On ne prend pas toute la modification proposée ! if (c.prms.Q.v > 0) { c.prms.Q.v = (1 - this._relax) * c.prms.Q.initValue + this._relax * c.prms.Q.v; QT2 += c.prms.Q.v; } if (this.DBG) { this.debug(`${c.uid} CalcQ: Q=${c.prms.Q.v} Z1=${c.prms.Z1.v} Z2=${c.prms.Z2.v}`); } } if (this.DBG) { this.debug(`CalcQ: QT=${QT} QT2=${QT2}`); } // Adjustement of each Q in order to get the good sum const adjustCoef = QT / QT2; // const tQ: number[] = []; for (const c of cloisons) { if (c.prms.Q.v >= 0) { if (QT2 !== 0) { c.prms.Q.v = c.prms.Q.v * adjustCoef; } else { c.prms.Q.v = QT / cloisons.length; } } // tQ.push(c.prms.Q.v) this.debug("CalcQ: Qc=" + c.prms.Q.v); } // return tQ; } CalcZ1Cloisons(cloisons) { const zStat = { moy: 0, min: Infinity, max: -Infinity }; let n = 0; for (const c of cloisons) { let Z1; if (c.prms.Q.v >= 1E-6) { const r = c.Calc("Z1"); if (r.ok) { Z1 = r.vCalc; } else { Z1 = c.prms.Z2.v; } this.debug(`CalcZ1Cloisons: n°${n} Z1=${Z1} Q=${c.prms.Q.v} Z2=${c.prms.Z2.v}`); } else { // Nul flow in submerged flow: Z1 = Z2 c.updateZ1Z2(); if (c.prms.Z2.v > c.getMinZDV()) { Z1 = c.prms.Z2.v; } else { c.prms.Q.v = 0; } this.debug(`CalcZ1Cloisons: n°${n} Z1=${Z1} Q=${c.prms.Q.v} Z2=${c.prms.Z2.v}`); } if (Z1 !== undefined) { zStat.moy += Z1; n++; zStat.min = Math.min(zStat.min, Z1); zStat.max = Math.max(zStat.max, Z1); } } if (n > 0) { zStat.moy = zStat.moy / n; } else { // Nul flow on all cloisons which are all in free flow => Z1 = ZminZDV zStat.moy = this.getMinZDV(cloisons); zStat.min = zStat.moy; zStat.max = zStat.moy; } this.debug(`CalcZ1Cloisons: Z= ${zStat.moy} [${(zStat.max - zStat.min)}]`); return zStat; } getMinZDV(cloisons) { let minZDV; for (const c of cloisons) { const minZDVCloison = c.getMinZDV(); if (minZDVCloison !== undefined) { if (minZDV === undefined) { minZDV = minZDVCloison; } else { minZDV = Math.min(minZDV, minZDVCloison); } } } return minZDV; } /** * 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 = super.loadObjectRepresentation(obj); // propagate changed UIDs to walls properties for (const c of this._children) { if (c instanceof internal_modules_6.PbCloison) { for (const k of Object.keys(ret.changedUids)) { // find basins having the changed UID if (c.getPropValue("upstreamBasin") === k) { c.bassinAmont = this.findChild(ret.changedUids[k]); } if (c.getPropValue("downstreamBasin") === k) { c.bassinAval = this.findChild(ret.changedUids[k]); } } } } return ret; } } exports.PreBarrage = PreBarrage; //# sourceMappingURL=pre_barrage.js.map