UNPKG

jalhyd

Version:

JaLHyd, a Javascript Library for Hydraulics

508 lines 21.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MacroRugo = exports.MacroRugoFlowType = 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"); var MacroRugoFlowType; (function (MacroRugoFlowType) { MacroRugoFlowType[MacroRugoFlowType["EMERGENT"] = 0] = "EMERGENT"; MacroRugoFlowType[MacroRugoFlowType["QUASI_EMERGENT"] = 1] = "QUASI_EMERGENT"; MacroRugoFlowType[MacroRugoFlowType["SUBMERGED"] = 2] = "SUBMERGED"; })(MacroRugoFlowType = exports.MacroRugoFlowType || (exports.MacroRugoFlowType = {})); class MacroRugo extends internal_modules_10.FishPass { constructor(prms, dbg = false) { super(prms, dbg); /** Coefficients used in f_h*(h*) */ this.paramFhStar = [1, 1, 2]; /** Coefficient used in rQ */ this.paramRQ = [0.4, 0.7]; /** Coefficient used in rQ */ this.paramRV = [0.4, 0.7]; /** Maximum value for Cd */ this.paramMaxCd = Infinity; /** true: Cd0 * min(3, fh), false : min(6, Cd0 * fh) */ this.paramCdNewVersion = true; this._cache = {}; this.setCalculatorType(internal_modules_1.CalculatorType.MacroRugo); this._defaultCalculatedParam = this.prms.Q; this._intlType = "MacroRugo"; this.resetDefaultCalculatedParam(); } /** * paramètres castés au bon type */ get prms() { return this._prms; } /** * Calcul du débit total, de la cote amont ou aval ou d'un paramètre d'une structure * @param sVarCalc Nom du paramètre à calculer * @param rInit Valeur initiale */ Calc(sVarCalc, rInit) { // Si on ne force pas à CALCUL, les tests à la 5e décimale ne passent plus (macrorugo.spec.ts) const originalValueMode = this.getParameter(sVarCalc).valueMode; this.getParameter(sVarCalc).valueMode = internal_modules_5.ParamValueMode.CALCUL; const r = super.Calc(sVarCalc, rInit); this.getParameter(sVarCalc).valueMode = originalValueMode; // La largeur de la rampe est-elle adéquate par rapport à la largeur de motif ax ? // (si le parent est une MacroRugoCompound avec radier incliné, on ignore ces problèmes) if (this.parent === undefined || (this.parent instanceof internal_modules_3.MacrorugoCompound && this.parent.getPropValue("inclinedApron") === internal_modules_9.MRCInclination.NOT_INCLINED)) { const ax = this.prms.PBD.v / Math.sqrt(this.prms.C.v); const tol = 0.01; // tolérance avant avertissement (1 cm) if (this.prms.B.v < ax - tol) { // B < ax, with a little tolerance const m = new internal_modules_7.Message(internal_modules_7.MessageCode.WARNING_RAMP_WIDTH_LOWER_THAN_PATTERN_WIDTH); m.extraVar.pattern = ax; r.resultElement.log.add(m); } else if (this.prms.B.v % (ax / 2) > tol && this.prms.B.v % (ax / 2) < ax / 2 - tol) { const m = new internal_modules_7.Message(internal_modules_7.MessageCode.WARNING_RAMP_WIDTH_NOT_MULTIPLE_OF_HALF_PATTERN_WIDTH); m.extraVar.halfPattern = ax / 2; m.extraVar.lower = Math.floor(this.prms.B.v / ax * 2) * (ax / 2); m.extraVar.higher = Math.ceil(this.prms.B.v / ax * 2) * (ax / 2); r.resultElement.log.add(m); } } // La concentration est-elle dans les valeurs admissibles 8-20% (#284) if (this.parent === undefined) { if (this.prms.C.V < 0.08 || this.prms.C.V > 0.2) { r.resultElement.log.add(new internal_modules_7.Message(internal_modules_7.MessageCode.WARNING_MACRORUGO_CONCENTRATION_OUT_OF_BOUNDS)); } } // Ajout des résultats complémentaires // Cote de fond aval r.resultElement.values.ZF2 = this.prms.ZF1.v - this.prms.If.v * this.prms.L.v; // Vitesse débitante let resVdeb = this.prms.Q.V / this.prms.B.v / this.prms.Y.v; if (isNaN(resVdeb)) { resVdeb = 0; } r.resultElement.values.Vdeb = resVdeb; if (this.flowType !== MacroRugoFlowType.SUBMERGED) { // Froude r.resultElement.values.Vg = r.resultElement.values.Vdeb / (1 - Math.sqrt(MacroRugo.fracAxAy * this.prms.C.v)); let resFr = r.resultElement.values.Vg / Math.sqrt(MacroRugo.g * this.prms.Y.v); if (isNaN(resFr)) { // if Y == 0 resFr = 0; } r.resultElement.values.Fr = resFr; // Vitesse maximale r.resultElement.values.Vmax = r.resultElement.values.Vg * Math.sqrt(this.CalcfFr(resVdeb, this.rV)); } // Puissance dissipée r.resultElement.values.PV = 1000 * MacroRugo.g * r.resultElement.values.Vdeb * this.prms.If.v; // Type d'écoulement if (this.prms.Y.v / this.prms.PBH.v < 1) { r.resultElement.values.ENUM_MacroRugoFlowType = MacroRugoFlowType.EMERGENT; } else if (this.prms.Y.v / this.prms.PBH.v < MacroRugo.limitSubmerg) { r.resultElement.values.ENUM_MacroRugoFlowType = MacroRugoFlowType.QUASI_EMERGENT; } else { r.resultElement.values.ENUM_MacroRugoFlowType = MacroRugoFlowType.SUBMERGED; } return r; } Equation(sVarCalc) { const Q = this.prms.Q.v; if (this.prms.Y.v < 1E-4) this.prms.Y.v = 0; const q0 = Math.sqrt(2 * MacroRugo.g * this.prms.If.v * this.prms.PBD.v * (1 - (this.sigma * this.prms.C.v)) / (this.prms.Cd0.v * this.prms.C.v)) * this.prms.Y.v * this.prms.B.v; let r; if (q0 > 0) { this.setFlowType(); const dicho = new internal_modules_2.Dichotomie(this, "Q", false, this.resolveQ); r = dicho.Dichotomie(0, internal_modules_6.SessionSettings.precision, q0); } else { r = new internal_modules_8.Result(0); } this.prms.Q.v = Q; if (this.prms.Y.v > 0 && this.prms.If.v > 0) { const a = this.prms.Y.v * this.prms.B.v; const p = 2 * this.prms.Y.v + this.prms.B.v; r.resultElement.values.Strickler = this.prms.Q.V / (a * Math.pow(a / p, 2 / 3) * Math.pow(this.prms.If.v, 0.5)); } else { r.resultElement.values.Strickler = 0; } return r; } /** * paramétrage de la calculabilité des paramètres */ setParametersCalculability() { this.prms.ZF1.calculability = internal_modules_4.ParamCalculability.FREE; this.prms.L.calculability = internal_modules_4.ParamCalculability.FREE; this.prms.Ks.calculability = internal_modules_4.ParamCalculability.FREE; this.prms.B.calculability = internal_modules_4.ParamCalculability.DICHO; this.prms.If.calculability = internal_modules_4.ParamCalculability.DICHO; this.prms.Q.calculability = internal_modules_4.ParamCalculability.EQUATION; this.prms.Y.calculability = internal_modules_4.ParamCalculability.DICHO; this.prms.C.calculability = internal_modules_4.ParamCalculability.DICHO; this.prms.PBD.calculability = internal_modules_4.ParamCalculability.FREE; this.prms.PBH.calculability = internal_modules_4.ParamCalculability.FREE; this.prms.Cd0.calculability = internal_modules_4.ParamCalculability.FREE; } static resultsUnits() { return MacroRugo._resultsUnits; } exposeResults() { this._resultsFamilies = { ZF2: internal_modules_4.ParamFamily.ELEVATIONS, Vdeb: internal_modules_4.ParamFamily.SPEEDS, Vmax: internal_modules_4.ParamFamily.SPEEDS, Vg: internal_modules_4.ParamFamily.SPEEDS, Fr: undefined, PV: undefined, Strickler: internal_modules_4.ParamFamily.STRICKLERS }; } setFlowType() { const hstar = this.prms.Y.v / this.prms.PBH.v; if (hstar > MacroRugo.limitSubmerg) { this.flowType = MacroRugoFlowType.SUBMERGED; } else if (hstar < 1) { this.flowType = MacroRugoFlowType.EMERGENT; } else { this.flowType = MacroRugoFlowType.QUASI_EMERGENT; } } /** * Equation from Cassan, L., Laurens, P., 2016. Design of emergent and submerged rock-ramp fish passes. * Knowledge & Management of Aquatic Ecosystems 45. * @param sVarCalc Variable à calculer */ resolveQ() { // Reset cached variables depending on Q (or not...) this._cache = {}; switch (this.flowType) { case MacroRugoFlowType.SUBMERGED: return this.resolveQSubmerged(); case MacroRugoFlowType.EMERGENT: return this.resolveQEmergent(); case MacroRugoFlowType.QUASI_EMERGENT: const a = (this.prms.Y.v / this.prms.PBH.v - 1) / (MacroRugo.limitSubmerg - 1); return (1 - a) * this.resolveQEmergent() + a * this.resolveQSubmerged(); } } /** * Averaged velocity (m.s-1) */ get U0() { if (this._cache.U0 === undefined) { this._cache.U0 = this.prms.Q.v / this.prms.B.v / this.prms.Y.v; } return this._cache.U0; } get CdChD() { if (this._cache.CdChD === undefined) { this._cache.CdChD = this.Cd * this.prms.C.v * this.prms.PBH.v / this.prms.PBD.v; } return this._cache.CdChD; } /** * sigma ratio between the block area in the x, y plane and D2 */ get sigma() { if (this._cache.sigma === undefined) { if (this.prms.Cd0.v >= 2) { this._cache.sigma = 1; } else { this._cache.sigma = Math.PI / 4; } } return this._cache.sigma; } get R() { if (this._cache.R === undefined) { this._cache.R = (1 - this.sigma * this.prms.C.v); } return this._cache.R; } /** * Bed friction coefficient Equation (3) (Cassan et al., 2016) * @param Y Water depth (m) */ calcCf(Y) { if (this.prms.Ks.v < 1E-9) { // Between Eq (8) and (9) (Cassan et al., 2016) const reynolds = this.U0 * this.prms.Y.v / MacroRugo.nu; return 0.3164 / 4. * Math.pow(reynolds, -0.25); } else { // Equation (3) (Cassan et al., 2016) return 2 / Math.pow(5.1 * Math.log10(Y / this.prms.Ks.v) + 6, 2); } } /** * Calculation of Cd : drag coefficient of a block under the actual flow conditions */ get Cd() { if (this._cache.Cd === undefined) { if (this.paramCdNewVersion) { this._cache.Cd = this.prms.Cd0.v * Math.min(this.paramMaxCd, (this.paramFhStar[0] + this.paramFhStar[1] / Math.pow(this.prms.Y.v / this.prms.PBD.v, this.paramFhStar[2]))); } else { this._cache.Cd = Math.min(this.paramMaxCd, this.prms.Cd0.v * (this.paramFhStar[0] + this.paramFhStar[1] / Math.pow(this.prms.Y.v / this.prms.PBD.v, this.paramFhStar[2]))); } } return this._cache.Cd; } /** * Calcul de Beta force ratio between drag and turbulent stress (Cassan et al. 2016 eq(8)) * \Beta = (k / alpha_t) (C_d C k / D) / (1 - \sigma C) * @param alpha \alpha_t turbulent length scale (m) within the blocks layer */ calcBeta(alpha) { return Math.min(100, Math.sqrt(this.prms.PBH.v * this.CdChD / alpha / this.R)); } /** * Averaged velocity at a given vertical position (m.s-1) * @param alpha turbulent length scale (m) within the blocks layer * @param z dimensionless vertical position z / k */ calcUz(alpha, z = 1) { const beta = this.calcBeta(alpha); // Equation (9) Cassan et al., 2016 return this.u0 * Math.sqrt(beta * (this.prms.Y.v / this.prms.PBH.v - 1) * Math.sinh(beta * z) / Math.cosh(beta) + 1); } get ustar() { if (this._cache.ustar === undefined) { this._cache.ustar = Math.sqrt(MacroRugo.g * this.prms.If.v * (this.prms.Y.v - this.prms.PBH.v)); } return this._cache.ustar; } resolveAlpha_t(alpha) { /** s: minimum distance between blocks */ const s = this.prms.PBD.v * (1 / Math.sqrt(this.prms.C.v) - 1); /** Equation(11) Cassan et al., 2016 */ const l0 = Math.min(s, 0.15 * this.prms.PBH.v); // Equation(10) Cassan et al., 2016 return alpha * this.calcUz(alpha) - l0 * this.ustar; } resolveQSubmerged() { /** Tirant d'eau (m) */ const h = this.prms.Y.v; /** Concentration de blocs (-) */ const C = this.prms.C.v; /** Paramètre de bloc : Diamètre (m) */ const D = this.prms.PBD.v; /** Paramètre de bloc : Hauteur (m) */ const k = this.prms.PBH.v; /** Slope (m/m) */ const S = this.prms.If.v; /** Accélération gravité (m/s²) */ const g = MacroRugo.g; /** Constante von Karman */ const kappa = 0.41; /** Velocity at the bed §2.3.2 Cassan et al., 2016 */ this.u0 = Math.sqrt(k * 2 * g * S * this.R / (this.Cd * C * k / D + this.calcCf(k) * this.R)); /** turbulent length scale (m) within the blocks layer (alpha_t) */ const alpha = uniroot(this.resolveAlpha_t, this, 1E-3, 10); /** averaged velocity at the top of blocks (m.s-1) */ const uk = this.calcUz(alpha); /** Equation (13) Cassan et al., 2016 */ const d = k - 1 / kappa * alpha * uk / this.ustar; /** Equation (14) Cassan et al., 2016 */ const z0 = (k - d) * Math.exp(-kappa * uk / this.ustar); /** Integral of Equation (12) Cassan et al., 2016 */ // tslint:disable-next-line:variable-name let Qsup; if (z0 > 0) { Qsup = this.ustar / kappa * ((h - d) * (Math.log((h - d) / z0) - 1) - ((k - d) * (Math.log((k - d) / z0) - 1))); } else { Qsup = 0; } // calcul intégrale dans la canopée---- // tslint:disable-next-line:variable-name let Qinf = this.u0; let u = 0; let uOld; const step = 0.01; const zMax = 1 + step / 2; for (let z = step; z < zMax; z += step) { uOld = u; u = this.calcUz(alpha, z); Qinf += (uOld + u); } Qinf = Qinf / 2 * step * k; // Calcul de u moyen return this.U0 - (Qinf + Qsup) / h; } resolveQEmergent() { return this.U0 - uniroot(this.resolveU0Complete, this, 1E-6, 100); } resolveU0Complete(U0) { const alpha = 1 - Math.pow(1 * this.prms.C.v, 0.5) - 0.5 * this.sigma * this.prms.C.v; // tslint:disable-next-line: variable-name const Cd = this.Cd * this.CalcfFr(U0, this.rQ); const N = (alpha * this.calcCf(this.prms.Y.v)) / (this.prms.Y.v / this.prms.PBD.v * Cd * this.prms.C.v); return U0 - Math.sqrt(2 * MacroRugo.g * this.prms.If.v * this.prms.PBD.v * (1 - this.sigma * this.prms.C.v) / (Cd * this.prms.C.v * (1 + N))); } /** * Calcul du ratio entre la vitesse moyenne à l'aval d'un block et la vitesse maximale * r = 1.1 pour un plot circulaire Cd0​=1 et r = 1.5 pour un plot à face plane Cd0​=2 */ get rV() { if (this._cache.rV === undefined) { this._cache.rV = this.paramRV[0] * this.prms.Cd0.v + this.paramRV[1]; } return this._cache.rV; } /** * Perte de charge supplémentaire due à la forme (voir fFr) * r = 1 pour un plot circulaire Cd0​=1 et r = 1.25 pour un plot à face plane Cd0​=2 */ get rQ() { if (this._cache.rQ === undefined) { this._cache.rQ = this.paramRQ[0] * this.prms.Cd0.v + this.paramRQ[1]; } return this._cache.rQ; } /** * Froude correction function (Cassan et al. 2014, Eq. 19) */ get fFr() { if (this._cache.fFr === undefined) { this._cache.fFr = this.CalcfFr(this.U0, this.rQ); } return this._cache.fFr; } /** * Calculation of Froude correction function (Cassan et al. 2014, Eq. 19) */ CalcfFr(U0, r) { // tslint:disable-next-line:variable-name const Fr = U0 / (1 - Math.sqrt(MacroRugo.fracAxAy * this.prms.C.v)) / Math.sqrt(MacroRugo.g * this.prms.Y.v); return Math.max(1, Math.pow(Math.min(r / (1 - Math.pow(Fr, 2) / 4), Math.pow(Fr, -2 / 3)), 2)); } } exports.MacroRugo = MacroRugo; MacroRugo.g = 9.81; /** nu: water kinematic viscosity */ MacroRugo.nu = 1E-6; // Water at 20 °C has a kinematic viscosity of about 10−6 m2·s−1 // (https://en.wikipedia.org/wiki/Viscosity#Kinematic_viscosity,_%CE%BD) /** Ratio between the width (perpendicular to flow) and the length (parallel to flow) of a cell (-) */ MacroRugo.fracAxAy = 1; /** Limit between emergent and submerged flow */ MacroRugo.limitSubmerg = 1.1; /** * { symbol => string } map that defines units for extra results */ MacroRugo._resultsUnits = { PV: "W/m³", Vdeb: "m/s", Vmax: "m/s", Vg: "m/s", ZF2: "m", Strickler: "SI", xCenter: "m" }; /** * Searches the interval from <tt>lowerLimit</tt> to <tt>upperLimit</tt> * for a root (i.e., zero) of the function <tt>func</tt> with respect to * its first argument using Brent's method root-finding algorithm. * * Translated from zeroin.c in http://www.netlib.org/c/brent.shar. * * Copyright (c) 2012 Borgar Thorsteinsson <borgar@borgar.net> * MIT License, http://www.opensource.org/licenses/mit-license.php * * @param {function} func function for which the root is sought. * @param {number} lowerlimit the lower point of the interval to be searched. * @param {number} upperlimit the upper point of the interval to be searched. * @param {number} errorTol the desired accuracy (convergence tolerance). * @param {number} maxIter the maximum number of iterations. * @returns an estimate for the root within accuracy. * */ function uniroot(func, thisArg, lowerLimit, upperLimit, errorTol = 0, maxIter = 1000) { let a = lowerLimit; let b = upperLimit; let c = a; let fa = func.call(thisArg, a); let fb = func.call(thisArg, b); let fc = fa; let tolAct; // Actual tolerance let newStep; // Step at this iteration let prevStep; // Distance from the last but one to the last approximation let p; // Interpolation step is calculated in the form p/q; division is delayed until the last moment let q; while (maxIter-- > 0) { prevStep = b - a; if (Math.abs(fc) < Math.abs(fb)) { // Swap data for b to be the best approximation a = b, b = c, c = a; fa = fb, fb = fc, fc = fa; } tolAct = 1e-15 * Math.abs(b) + errorTol / 2; newStep = (c - b) / 2; if (Math.abs(newStep) <= tolAct || fb === 0) { return b; // Acceptable approx. is found } // Decide if the interpolation can be tried if (Math.abs(prevStep) >= tolAct && Math.abs(fa) > Math.abs(fb)) { // If prev_step was large enough and was in true direction, Interpolatiom may be tried let t1; let cb; let t2; cb = c - b; if (a === c) { // If we have only two distinct points linear interpolation can only be applied t1 = fb / fa; p = cb * t1; q = 1.0 - t1; } else { // Quadric inverse interpolation q = fa / fc, t1 = fb / fc, t2 = fb / fa; p = t2 * (cb * q * (q - t1) - (b - a) * (t1 - 1)); q = (q - 1) * (t1 - 1) * (t2 - 1); } if (p > 0) { q = -q; // p was calculated with the opposite sign; make p positive } else { p = -p; // and assign possible minus to q } if (p < (0.75 * cb * q - Math.abs(tolAct * q) / 2) && p < Math.abs(prevStep * q / 2)) { // If (b + p / q) falls in [b,c] and isn't too large it is accepted newStep = p / q; } // If p/q is too large then the bissection procedure can reduce [b,c] range to more extent } if (Math.abs(newStep) < tolAct) { // Adjust the step to be not less than tolerance newStep = (newStep > 0) ? tolAct : -tolAct; } a = b, fa = fb; // Save the previous approx. b += newStep, fb = func.call(thisArg, b); // Do step to a new approxim. if ((fb > 0 && fc > 0) || (fb < 0 && fc < 0)) { c = a, fc = fa; // Adjust c for it to have a sign opposite to that of b } } return undefined; } //# sourceMappingURL=macrorugo.js.map