diff --git a/spec/devalaison/jet.spec.ts b/spec/devalaison/jet.spec.ts index 383f06fa77b9ebdf8ea41aa11affe06cbaad77de..e82443f6c04220165770006d7eac6b2c6c2015fb 100644 --- a/spec/devalaison/jet.spec.ts +++ b/spec/devalaison/jet.spec.ts @@ -1,9 +1,10 @@ import { Jet } from "../../src/devalaison/jet"; import { JetParams } from "../../src/devalaison/jet_params"; +import { MessageCode, ParamCalculability } from "../../src/index"; function newJet(): Jet { return new Jet( - new JetParams(5, 0.3, 72.48, 19.0788), + new JetParams(5, 0.3, 77.48, 5, 3.42, 19.0788), false ); } @@ -15,16 +16,18 @@ describe("Class Jet − ", () => { jet = newJet(); }); for (const p of jet.parameterIterator) { - it(`Calc(${p.symbol}) should return ${p.currentValue}`, () => { - jet.calculatedParam = jet.getParameter(p.symbol); - const ref: number = p.currentValue; - jet.calculatedParam.singleValue = jet.calculatedParam.singleValue / 2; - expect(jet.CalcSerie().vCalc).toBeCloseTo(ref, 3); - expect(jet.result.values.t).toBeCloseTo(4, 3); - expect(jet.result.values.Vx).toBeCloseTo(4.7697, 3); - expect(jet.result.values.Vz).toBeCloseTo(-37.74, 3); - expect(jet.result.values.Vt).toBeCloseTo(38.0402, 3); - }); + if ([ ParamCalculability.EQUATION, ParamCalculability.DICHO ].includes(p.calculability)) { + it(`Calc(${p.symbol}) should return ${p.currentValue}`, () => { + jet.calculatedParam = jet.getParameter(p.symbol); + const ref: number = p.currentValue; + jet.calculatedParam.singleValue = jet.calculatedParam.singleValue / 2; + expect(jet.CalcSerie().vCalc).toBeCloseTo(ref, 3); + expect(jet.result.values.t).toBeCloseTo(4, 3); + expect(jet.result.values.Vx).toBeCloseTo(4.770, 3); + expect(jet.result.values.Vz).toBeCloseTo(-37.74, 3); + expect(jet.result.values.Vt).toBeCloseTo(38.04, 3); + }); + } } describe("initial slope − ", () => { @@ -43,4 +46,93 @@ describe("Class Jet − ", () => { }); }); + describe("trajectory − ", () => { + it("changing jet start elevation should change Y of the first point", () => { + jet.calculatedParam = jet.prms.D; + // ZJ = 30 + jet.prms.ZJ.singleValue = 30; + jet.CalcSerie(); + const traj30 = jet.generateTrajectories()[0]; + // ZJ = 25 + jet.prms.ZJ.singleValue = 25; + jet.CalcSerie(); + const traj25 = jet.generateTrajectories()[0]; + // check + expect(traj30[0][1]).toBe(30); + expect(traj25[0][1]).toBe(25); + }); + }); + + describe("errors and warnings − ", () => { + it("water underground (fixed), there should be a warning", () => { + jet.prms.ZJ.singleValue = 30; + jet.prms.ZW.singleValue = 28.2; + jet.prms.ZF.singleValue = 28.5; + jet.calculatedParam = jet.prms.D; + const res = jet.CalcSerie(); + expect(res.log.messages.length).toBe(1); + expect(res.log.messages[0].code).toBe(MessageCode.WARNING_JET_WATER_ELEVATION_UNDERGROUND); + }); + it("water underground (calculated), there should be a warning", () => { + jet.prms.ZJ.singleValue = 30; + jet.prms.D.singleValue = 3.709; + jet.prms.ZF.singleValue = 28.5; + jet.calculatedParam = jet.prms.ZW; + const res = jet.CalcSerie(); + expect(res.log.messages.length).toBe(1); + expect(res.log.messages[0].code).toBe(MessageCode.WARNING_JET_WATER_ELEVATION_UNDERGROUND); + }); + it("jet underground (fixed), there should be two warnings", () => { + jet.prms.ZJ.singleValue = 28; + jet.prms.ZW.singleValue = 27; + jet.prms.ZF.singleValue = 28.5; + jet.calculatedParam = jet.prms.D; + const res = jet.CalcSerie(); + expect(res.log.messages.length).toBe(2); + expect(res.log.messages[0].code).toBe(MessageCode.WARNING_JET_WATER_ELEVATION_UNDERGROUND); + expect(res.log.messages[1].code).toBe(MessageCode.WARNING_JET_START_ELEVATION_UNDERGROUND); + }); + it("jet underground (calculated), there should be two warnings", () => { + jet.prms.D.singleValue = 3.003; + jet.prms.ZW.singleValue = 27; + jet.prms.ZF.singleValue = 28.5; + jet.calculatedParam = jet.prms.ZJ; + const res = jet.CalcSerie(); + expect(res.log.messages.length).toBe(2); + expect(res.log.messages[0].code).toBe(MessageCode.WARNING_JET_WATER_ELEVATION_UNDERGROUND); + expect(res.log.messages[1].code).toBe(MessageCode.WARNING_JET_START_ELEVATION_UNDERGROUND); + }); + it("jet submerged (fixed) with solution, there should be a warning", () => { + jet.prms.ZJ.singleValue = 29.1; + jet.prms.ZW.singleValue = 29.2; + jet.prms.ZF.singleValue = 28.5; + jet.prms.S.singleValue = 0.9; + jet.calculatedParam = jet.prms.D; + const res = jet.CalcSerie(); + expect(res.log.messages.length).toBe(1); + expect(res.log.messages[0].code).toBe(MessageCode.WARNING_JET_START_SUBMERGED); + }); + it("jet submerged (calculated) with solution, there should be a warning", () => { + jet.prms.D.singleValue = 0.950; + jet.prms.ZW.singleValue = 29.2; + jet.prms.ZF.singleValue = 24.5; + jet.prms.S.singleValue = 0.9; + jet.calculatedParam = jet.prms.ZJ; + const res = jet.CalcSerie(); + expect(res.log.messages.length).toBe(1); + expect(res.log.messages[0].code).toBe(MessageCode.WARNING_JET_START_SUBMERGED); + }); + it("jet submerged without solution, there should be an error and a warning", () => { + jet.prms.ZJ.singleValue = 29.1; + jet.prms.ZW.singleValue = 29.2; + jet.prms.ZF.singleValue = 28.5; + jet.prms.S.singleValue = 0; + jet.calculatedParam = jet.prms.D; + const res = jet.CalcSerie(); + expect(res.log.messages.length).toBe(2); + expect(res.log.messages[0].code).toBe(MessageCode.ERROR_JET_SUBMERGED_NO_SOLUTION); + expect(res.log.messages[1].code).toBe(MessageCode.WARNING_JET_START_SUBMERGED); + }); + }); + }); diff --git a/src/devalaison/jet.ts b/src/devalaison/jet.ts index 5b143409cb0332f2f2657e7966a7f2e746199b49..b60e484ae890aeb1ea50f8720e2d985903ce986b 100644 --- a/src/devalaison/jet.ts +++ b/src/devalaison/jet.ts @@ -1,7 +1,8 @@ import { CalculatorType } from "../compute-node"; import { Nub } from "../nub"; -import { ParamCalculability } from "../param/param-definition"; +import { ParamCalculability, ParamFamily } from "../param/param-definition"; import { ParamValueMode } from "../param/param-value-mode"; +import { Message, MessageCode } from "../util/message"; import { Result } from "../util/result"; import { JetParams } from "./jet_params"; @@ -24,38 +25,84 @@ export class Jet extends Nub { public Calc(sVarCalc: string, rInit?: number): Result { this.currentResult = super.Calc(sVarCalc, rInit); + // H: chute + this.result.resultElement.values.H = this.prms.ZJ.v - this.prms.ZW.v; + // Y: profondeur + this.result.resultElement.values.Y = this.prms.ZW.v - this.prms.ZF.v; + // YH: rapport profondeur/chute + this.result.resultElement.values.YH = this.result.resultElement.values.Y / this.result.resultElement.values.H; + // t: temps de vol this.result.resultElement.values.t = this.prms.D.V / Math.cos(this.alpha) / this.prms.V0.V; + // Vx: vitesse horizontale à l'impact this.result.resultElement.values.Vx = this.prms.V0.V * Math.cos(this.alpha); + // Vz: vitesse verticale à l'impact this.result.resultElement.values.Vz = this.prms.V0.V * Math.sin(this.alpha) - this.result.resultElement.values.t * 9.81; + // Vt: vitesse à l'impact this.result.resultElement.values.Vt = Math.sqrt( Math.pow(this.result.resultElement.values.Vx, 2) + Math.pow(this.result.resultElement.values.Vz, 2) ); + + let ZF = this.prms.ZF.v; + let ZW = this.prms.ZW.v; + let ZJ = this.prms.ZJ.v; + if (this.calculatedParam === this.prms.ZF) { + ZF = this.result.resultElement.vCalc; + } + if (this.calculatedParam === this.prms.ZW) { + ZW = this.result.resultElement.vCalc; + } + if (this.calculatedParam === this.prms.ZJ) { + ZJ = this.result.resultElement.vCalc; + } + // y a-t-il de l'eau au dessus du sol ? + if (ZF > ZW) { + this.result.resultElement.log.add(new Message(MessageCode.WARNING_JET_WATER_ELEVATION_UNDERGROUND)); + } + // le jet est-il bien au dessus du sol ? + if (ZF > ZJ) { + this.result.resultElement.log.add(new Message(MessageCode.WARNING_JET_START_ELEVATION_UNDERGROUND)); + } + // le jet est-il bien émergé ? + if (ZW > ZJ) { + this.result.resultElement.log.add(new Message(MessageCode.WARNING_JET_START_SUBMERGED)); + } + return this.result; } public Equation(sVarCalc: string): Result { const g: number = 9.81; - let alpha: number; - if (sVarCalc !== "S") { - // Positive slope for negative angle - alpha = this.alpha; - } let v: number; + let h: number; switch (sVarCalc) { - case ("H"): - v = 0.5 * g * Math.pow(this.prms.D.v, 2) / (Math.pow(Math.cos(alpha), 2) * Math.pow(this.prms.V0.v, 2)) - - Math.tan(alpha) * this.prms.D.v; + case ("ZJ"): + h = this.CalcH(); + v = h + this.prms.ZW.v; + break; + + case ("ZW"): + h = this.CalcH(); + v = this.prms.ZJ.v - h; break; + case ("D"): - v = this.prms.V0.v / g * Math.cos(alpha) + h = (this.prms.ZJ.v - this.prms.ZW.v); + const sqrtArg = Math.pow(this.prms.V0.v * Math.sin(this.alpha), 2) + 2 * g * h; + if (sqrtArg < 0) { + return new Result(new Message(MessageCode.ERROR_JET_SUBMERGED_NO_SOLUTION)); + } + v = this.prms.V0.v / g * Math.cos(this.alpha) * ( - this.prms.V0.v * Math.sin(alpha) - + Math.sqrt(Math.pow(this.prms.V0.v * Math.sin(alpha), 2) + 2 * g * this.prms.H.v) + this.prms.V0.v * Math.sin(this.alpha) + + Math.sqrt(sqrtArg) ); break; + default: + throw new Error("Jet.Equation() : invalid variable name " + sVarCalc); + } return new Result(v); } @@ -71,12 +118,12 @@ export class Jet extends Nub { * for each abscissa (x) between 0 and the impact abscissa (D). * A coordinate pair is a list of 2 numbers [ x, y ]. * If no parameter is varying, result will contain only 1 element. - * Trajectory calculation uses a copy of the current Nub to calculate H from D. + * Trajectory calculation uses a copy of the current Nub to calculate ZW from D. */ public generateTrajectories(): number[][][] { const trajectories: number[][][] = []; - // clone Nub so that H calculation will not impact current state + // clone Nub so that ZW calculation will not impact current state const nub = this.clone(); // is anything varying ? @@ -90,8 +137,8 @@ export class Jet extends Nub { nub.prms.D.valueMode = ParamValueMode.SINGLE; // H will be calculated - // 1. find all extended values lists; ignore H (will be calculated) and D (will be reaffected) - for (const symbol of [ "S", "V0" ]) { + // 1. find all extended values lists; ignore ZW (will be calculated) and D (will be reaffected) + for (const symbol of [ "S", "V0", "ZJ" ]) { const p = this.getParameter(symbol); valuesLists[symbol] = []; if (this.calculatedParam.symbol === symbol) { // calculated @@ -115,9 +162,9 @@ export class Jet extends Nub { for (let i = 0; i < length; i++) { // exclude iteration if calculation has failed if (this.result.resultElements[i].ok) { - // set clone params values; ignore H (will be calculated) + // set clone params values; ignore ZW (will be calculated) // and D (will be reaffected by getDAbscissae) - for (const symbol of [ "S", "V0" ]) { + for (const symbol of [ "S", "V0", "ZJ" ]) { const val = valuesLists[symbol][i]; nub.getParameter(symbol).v = val; } @@ -130,7 +177,7 @@ export class Jet extends Nub { } } else { // nothing is varying - for (const symbol of [ "S", "V0" ]) { + for (const symbol of [ "S", "V0", "ZJ" ]) { // init .v of clone nub.getParameter(symbol).v = nub.getParameter(symbol).singleValue; } @@ -140,6 +187,17 @@ export class Jet extends Nub { return trajectories; } + protected CalcH(): number { + const g: number = 9.81; + return ( + 0.5 * g * Math.pow(this.prms.D.v, 2) + / (Math.pow(Math.cos(this.alpha), 2) * Math.pow(this.prms.V0.v, 2) + ) - Math.tan(this.alpha) * this.prms.D.v); + } + + /** + * Build a trajectory data series for a calculation iteration + */ protected buildSeriesForIteration(nub: Jet, i: number): number[][] { const traj: number[][] = []; const xs = this.getDAbscissae(i); @@ -147,8 +205,8 @@ export class Jet extends Nub { // compute H for D = x nub.prms.D.v = x; // console.log("__computing H for x =", x, nub.prms.D.v); - const h = nub.Calc("H"); - traj.push([ x, - h.vCalc ]); + const h = nub.Calc("ZW"); + traj.push([ x, h.vCalc ]); } return traj; } @@ -192,10 +250,19 @@ export class Jet extends Nub { protected setParametersCalculability() { this.prms.V0.calculability = ParamCalculability.DICHO; this.prms.S.calculability = ParamCalculability.DICHO; - this.prms.H.calculability = ParamCalculability.EQUATION; + this.prms.ZJ.calculability = ParamCalculability.EQUATION; + this.prms.ZW.calculability = ParamCalculability.EQUATION; + this.prms.ZF.calculability = ParamCalculability.FIXED; this.prms.D.calculability = ParamCalculability.EQUATION; } + protected setResultsFamilies() { + this._resultsFamilies = { + H: ParamFamily.TOTALFALLS, + Y: ParamFamily.HEIGHTS + }; + } + private get alpha(): number { return Math.asin(this.prms.S.v); } diff --git a/src/devalaison/jet_params.ts b/src/devalaison/jet_params.ts index 1c928be13b1f1bef4571085c511b31410f756614..f151d3a7f3403b59a4577a64916c6fab782b49b4 100644 --- a/src/devalaison/jet_params.ts +++ b/src/devalaison/jet_params.ts @@ -3,8 +3,15 @@ import { ParamDomain, ParamDomainValue } from "../param/param-domain"; import { ParamsEquation } from "../param/params-equation"; export class JetParams extends ParamsEquation { - /** Fall (m) */ - private _H: ParamDefinition; + + /** Jet start elevation (m) */ + private _ZJ: ParamDefinition; + + /** Water elevation (m) */ + private _ZW: ParamDefinition; + + /** Bottom elevation (m) */ + private _ZF: ParamDefinition; /** Initial speed */ private _V0: ParamDefinition; @@ -15,7 +22,7 @@ export class JetParams extends ParamsEquation { /** Impact distance */ private _D: ParamDefinition; - constructor(V0: number, S: number, H: number, D: number) { + constructor(V0: number, S: number, ZJ: number, ZW: number, ZF: number, D: number) { super(); this._V0 = new ParamDefinition(this, "V0", ParamDomainValue.POS, "m", V0, ParamFamily.SPEEDS); this.addParamDefinition(this._V0); @@ -25,8 +32,12 @@ export class JetParams extends ParamsEquation { "m/m", S, ParamFamily.SLOPES ); this.addParamDefinition(this._S); - this._H = new ParamDefinition(this, "H", ParamDomainValue.POS_NULL, "m", H, ParamFamily.TOTALFALLS); - this.addParamDefinition(this._H); + this._ZJ = new ParamDefinition(this, "ZJ", ParamDomainValue.ANY, "m", ZJ, ParamFamily.ELEVATIONS); + this.addParamDefinition(this._ZJ); + this._ZW = new ParamDefinition(this, "ZW", ParamDomainValue.ANY, "m", ZW, ParamFamily.ELEVATIONS); + this.addParamDefinition(this._ZW); + this._ZF = new ParamDefinition(this, "ZF", ParamDomainValue.ANY, "m", ZF, ParamFamily.ELEVATIONS); + this.addParamDefinition(this._ZF); this._D = new ParamDefinition(this, "D", ParamDomainValue.POS_NULL, "m", D, ParamFamily.LENGTHS); this.addParamDefinition(this._D); } @@ -39,8 +50,16 @@ export class JetParams extends ParamsEquation { return this._S; } - public get H(): ParamDefinition { - return this._H; + public get ZJ(): ParamDefinition { + return this._ZJ; + } + + public get ZW(): ParamDefinition { + return this._ZW; + } + + public get ZF(): ParamDefinition { + return this._ZF; } public get D(): ParamDefinition { diff --git a/src/session.ts b/src/session.ts index 4afbbcc33cdc23719fe6578432503caf7562e741..0a6909708954f94aa3fc3100d5e66dff5010ca19 100644 --- a/src/session.ts +++ b/src/session.ts @@ -540,7 +540,9 @@ export class Session { new JetParams( 5, // V0 0.03, // S - 2, // H + 30, // ZJ + 29.2, // ZW + 28.5, // ZF 3 // D ) ); diff --git a/src/util/message.ts b/src/util/message.ts index 6d2f206eb5df32637a9a74d9727911c66f0dc7f2..15319ad0c79ef5107c52c4ce5c9dcb4db4f4cc53 100644 --- a/src/util/message.ts +++ b/src/util/message.ts @@ -70,6 +70,9 @@ export enum MessageCode { */ ERROR_IN_CALC_CHAIN, + /** Jet submergé, pente trop faible: pas de solution pour calculer l'abscisse de l'impact */ + ERROR_JET_SUBMERGED_NO_SOLUTION, + /** * Something failed in certain steps (but not all), when calculating upstream Nubs with varying parameter */ @@ -343,6 +346,15 @@ export enum MessageCode { */ WARNING_GRILLE_VN_GREATER_THAN_05, + /** La cote de départ du jet est plus basse que la code de l'eau */ + WARNING_JET_START_SUBMERGED, + + /** La cote de départ du jet est plus basse que la code de fond */ + WARNING_JET_START_ELEVATION_UNDERGROUND, + + /** La cote de l'eau est plus basse ou égale à la cote de fond */ + WARNING_JET_WATER_ELEVATION_UNDERGROUND, + /** section : le tirant d'eau dépasse la hauteur de berge */ WARNING_SECTION_OVERFLOW,