jalhyd
Version:
JaLHyd, a Javascript Library for Hydraulics
639 lines • 25.2 kB
JavaScript
"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