diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000000000000000000000000000000000..7ca0dc5577d8533754f994538ca949512c6368b5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // Utilisez IntelliSense pour en savoir plus sur les attributs possibles. + // Pointez pour afficher la description des attributs existants. + // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Program", + "type": "node", + "request": "launch", + "program": "${file}", + "sourceMaps": true, + "outFiles": [ + "${workspaceRoot}/build/**/*.js" + ], + "cwd": "${workspaceRoot}", + "preLaunchTask": "buildspec" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000000000000000000000000000000000..03efa3f6e6f3976e97516d27e13b8ffb58c89480 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,13 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "buildspec", + "type": "npm", + "script": "buildspec", + "problemMatcher": [] + } + ] +} diff --git a/spec/mock_jasmine.ts b/spec/mock_jasmine.ts index 8f03ee968fd9695c3b0ce6b1619a724d6b577bfb..2ba5f986c0158e410e0ab085db02e0cb5eb59786 100644 --- a/spec/mock_jasmine.ts +++ b/spec/mock_jasmine.ts @@ -98,6 +98,16 @@ class Expect { console.error(message); } } + + public toBeUndefined(message?: string) { + if (this.actual !== undefined) { + if (message === undefined) { + console.error(message); + } else { + console.error(this.actual + " should be undefined"); + } + } + } } /** diff --git a/spec/regime_uniforme/regime_uniforme_puissance.spec.ts b/spec/regime_uniforme/regime_uniforme_puissance.spec.ts index 37ec2a7ef51c66176e8686675f96ac5875c9dac0..f6088e27c8fcbf41c97d7348fb1cdaa4b23be48b 100644 --- a/spec/regime_uniforme/regime_uniforme_puissance.spec.ts +++ b/spec/regime_uniforme/regime_uniforme_puissance.spec.ts @@ -1,12 +1,17 @@ -// tslint:disable-next-line:no-reference -/// <reference path="../../node_modules/@types/jasmine/index.d.ts" /> +/** + * IMPORTANT ! + * Décommenter temporairement la ligne suivante (import { } from "./mock_jasmine") + * Pour exécuter ce code dans le débugger. + * Faire de même avec le fichier test_func.ts + */ +// import { describe, expect, it, xdescribe } from "../mock_jasmine"; -import { Result } from "../../src/util/result"; import { RegimeUniforme } from "../../src/regime_uniforme"; import { cSnPuiss, ParamsSectionPuiss } from "../../src/section/section_puissance"; import { MessageCode } from "../../src/util/message"; -import { equalEpsilon, checkResult } from "../test_func"; -import { precDist, precDigits } from "../test_config"; +import { Result } from "../../src/util/result"; +import { precDigits, precDist } from "../test_config"; +import { checkResult } from "../test_func"; describe("Class RegimeUniforme / section puissance :", () => { describe("pas de débordement :", () => { diff --git a/spec/structure/parallel_structure.spec.ts b/spec/structure/parallel_structure.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4601e2dbbf1b0e6365f7022606fc855dac857cf --- /dev/null +++ b/spec/structure/parallel_structure.spec.ts @@ -0,0 +1,39 @@ +/** + * IMPORTANT ! + * Décommenter temporairement la ligne suivante (import { } from "./mock_jasmine") + * Pour exécuter ce code dans le débugger. + * Faire de même avec le fichier test_func.ts + */ +// import { describe, expect, it, xdescribe } from "../mock_jasmine"; + +import { ParallelStructure } from "../../src/structure/parallel_structure"; +import { ParallelStructureParams } from "../../src/structure/parallel_structure_params"; +import { Result } from "../../src/util/result"; +import { checkResult } from "../test_func"; +import { structTest } from "./structure_test"; + +const pstruct: ParallelStructure = new ParallelStructure( + new ParallelStructureParams(30, 30, 15), // Q = 30, Z1 = 30, Z2 = 15 + false // debug +); + +// Test avec deux structures test identiques +pstruct.addStructure(structTest); +pstruct.addStructure(structTest); + +describe("Class ParallelStructure: ", () => { + describe("Calc()", () => { + itParallelStructure("Q", 30, 15); + itParallelStructure("Z1", 30, 15); + itParallelStructure("Z2", 15, 15); + }); +}); + +function itParallelStructure(sVarCalc: string, rVcalc: number, Q: number) { + it(`${sVarCalc} should be ${rVcalc}`, () => { + const res: Result = pstruct.Calc(sVarCalc); + checkResult(res, rVcalc); + checkResult(res.extractResult(1), Q); + checkResult(res.extractResult(2), Q); + }); +} diff --git a/spec/structure/structure.spec.ts b/spec/structure/structure.spec.ts index 5a976904d4ac85fe929e717d0d896543288606d3..f1c23d0a8339db96b1e55bcffe6c0f9ee313de5e 100644 --- a/spec/structure/structure.spec.ts +++ b/spec/structure/structure.spec.ts @@ -6,42 +6,9 @@ */ // import { describe, expect, it, xdescribe } from "../mock_jasmine"; -import { Structure, StructureFlowMode, StructureFlowRegime, StructureParams } from "../../src/structure/structure"; -import { Result } from "../../src/util/result"; +import { StructureFlowMode, StructureFlowRegime } from "../../src/structure/structure"; import { checkResult } from "../test_func"; - -class StructureTest extends Structure { - - constructor(prms: StructureParams, dbg: boolean = false) { - super(prms, dbg); - } - - /** - * Test of getFlowMode - */ - public testGetFlowMode() { - this.prms.update_h1h2(); - return this.getFlowMode(); - } - - /** - * Test of getFlowRegime - */ - public testGetFlowRegime() { - this.prms.update_h1h2(); - return this.getFlowRegime(); - } - - public Equation(sVarCalc: string): Result { - this.prms.update_h1h2(); - this.CheckEquation(sVarCalc); - return new Result(this.prms.Z1.v - this.prms.Z2.v); - } - -} - -const structTestPrm: StructureParams = new StructureParams(1, 0, 30, 15); -const structTest: StructureTest = new StructureTest(structTestPrm, false); +import { structTest } from "./structure_test"; describe("Class Structure: ", () => { @@ -80,7 +47,7 @@ describe("Class Structure: ", () => { describe("Calc()", () => { const flagsNull = { Mode: StructureFlowMode.NULL, Regime: StructureFlowRegime.NULL }; - it("h1=h2 => Q=0", () => { + it("Z1=Z2 => Q=0", () => { structTest.prms.Z2.v = structTest.prms.Z1.v; checkResult(structTest.Calc("Q"), 0); expect(structTest.Calc("Q").extraResults).toEqual(flagsNull); @@ -92,10 +59,10 @@ describe("Class Structure: ", () => { expect(structTest.Calc("Q").extraResults).toEqual(flagsNull); structTest.prms.W.v = Infinity; }); - it("Q=0 => h1=h2", () => { + it("Q=0 => Z1=Z2", () => { structTest.prms.Q.v = 0; - checkResult(structTest.Calc("h1"), structTest.prms.h2.v); - expect(structTest.Calc("h1").extraResults).toEqual(flagsNull); + checkResult(structTest.Calc("Z1"), structTest.prms.h2.v); + expect(structTest.Calc("Z1").extraResults).toEqual(flagsNull); structTest.prms.Q.v = 1; }); it("Q=0 => W=0", () => { diff --git a/spec/structure/structure_test.ts b/spec/structure/structure_test.ts new file mode 100644 index 0000000000000000000000000000000000000000..c4e582feafa5f8011717b2fe3b84eec917649034 --- /dev/null +++ b/spec/structure/structure_test.ts @@ -0,0 +1,43 @@ +import { Structure, StructureParams } from "../../src/structure/structure"; +import { Result } from "../../src/util/result"; + +class StructureTest extends Structure { + + constructor(prms: StructureParams, dbg: boolean = false) { + super(prms, dbg); + } + + /** + * Test of getFlowMode + */ + public testGetFlowMode() { + this.prms.update_h1h2(); + return this.getFlowMode(); + } + + /** + * Test of getFlowRegime + */ + public testGetFlowRegime() { + this.prms.update_h1h2(); + return this.getFlowRegime(); + } + + public Equation(sVarCalc: string): Result { + this.prms.update_h1h2(); + Structure.CheckEquation(sVarCalc); + const data = this.getResultData(); + + return new Result(this.prms.Z1.v - this.prms.Z2.v, data); + } + +} + +/* Test structure with : + * - ZDV = 0 + * - Z1 = 30 + * - Z2 = 15 + * - expected Q = 15 + */ +export const structTestPrm: StructureParams = new StructureParams(1, 0, 30, 15); +export const structTest: StructureTest = new StructureTest(structTestPrm, false); diff --git a/src/base.ts b/src/base.ts index 4365b942d68073c04d6fc6d7a26abd39a54cf317..f74f97a4a38d112ac8224622d167b277c5de2fd1 100644 --- a/src/base.ts +++ b/src/base.ts @@ -16,6 +16,7 @@ export class Serie { * @note Etendre cette classe pour toutes les classes à debugguer * Dans le constructeur, utiliser super(true) pour debugger la classe, super(false) sinon. */ +// tslint:disable-next-line:max-classes-per-file export abstract class Debug { /** * @param _DBG Flag de débuggage @@ -27,13 +28,13 @@ export abstract class Debug { * @param s Message à afficher dans la console */ public debug(s: any) { - if (this._DBG) console.log(s); + // tslint:disable-next-line:no-console + if (this._DBG) { console.log(s); } } get DBG() { return this._DBG; } } - /** * Méthode simulant l'opérateur booléen xor * @see http://www.howtocreate.co.uk/xor.html @@ -49,16 +50,12 @@ export function BoolIdentity(a: boolean, b: boolean): boolean { return (a && b) || (!a && !b); } - /** * arrondi d'un nombre avec une précision donnée * @param val nombre à arrondir * @param prec nombre de chiffres */ export function round(val: number, prec: number) { - let m = Math.pow(10, prec || 0); + const m = Math.pow(10, prec || 0); return Math.round(val * m) / m; } - -// export class UndefinedError extends Error { -// } diff --git a/src/dichotomie.ts b/src/dichotomie.ts index 55e4b48a2740ff685d6bd1ff703c68ce5adb3ef1..3b79383e8da773e92fc5548fbdd54d32f2573b08 100644 --- a/src/dichotomie.ts +++ b/src/dichotomie.ts @@ -1,12 +1,11 @@ // import { XOR, BoolIdentity, Debug, Result, ResultCode, UndefinedError } from "./base"; -import { XOR, BoolIdentity, Debug } from "./base"; -import { Result } from "./util/result"; -import { Message, MessageCode } from "./util/message"; +import { BoolIdentity, Debug, XOR } from "./base"; import { Nub } from "./nub"; -import { ParamDefinition, ParamDomain, ParamDomainValue } from "./param" -import { Pair } from "./util/pair"; +import { ParamDefinition, ParamDomain, ParamDomainValue } from "./param"; import { Interval } from "./util/interval"; - +import { Message, MessageCode } from "./util/message"; +import { Pair } from "./util/pair"; +import { Result } from "./util/result"; class SearchInterval extends Interval { private _step: number; @@ -17,21 +16,21 @@ class SearchInterval extends Interval { constructor(d: Dichotomie, min: number, max: number, s: number) { super(min, max); - if (s == 0) { - let e = new Message(MessageCode.ERROR_DICHO_NULL_STEP); + if (s === 0) { + const e = new Message(MessageCode.ERROR_DICHO_NULL_STEP); throw e; } this._step = s; this._dicho = d; } - reverse() { + public reverse() { this._step = -this._step; } - growStep(k: number) { - if (k == 0) { - let e = new Message(MessageCode.ERROR_DICHO_INVALID_STEP_GROWTH); + public growStep(k: number) { + if (k === 0) { + const e = new Message(MessageCode.ERROR_DICHO_INVALID_STEP_GROWTH); throw e; } this._step *= k; @@ -42,22 +41,12 @@ class SearchInterval extends Interval { } get targets() { - if (this._targets == undefined) + if (this._targets === undefined) { this._targets = new Pair(undefined, undefined); + } return this._targets; } - private updateTargets() { - let t1 = this.targets.val1; - if (t1 == undefined) - t1 = this._dicho.CalculX(this._val1).vCalc - - let t2 = this.targets.val2; - if (t2 == undefined) - t2 = this._dicho.CalculX(this._val2).vCalc; - this.targets.setValues(t1, t2); - } - /** * get target value for lower bound */ @@ -74,35 +63,49 @@ class SearchInterval extends Interval { return this.targets.val2; } - next() { + public next() { if (this._step > 0) { this._val1 = this._val2; this._val2 += this._step; this.targets.setValues(this.targets.val2, undefined); - } - else { + } else { this._val2 = this._val1; this._val1 += this._step; this.targets.setValues(undefined, this.targets.val1); } } - hasTargetValue(targ: number) { + public hasTargetValue(targ: number) { this.updateTargets(); return this.targets.intervalHasValue(targ); } - toString(): string { + public toString(): string { return super.toString() + " step=" + this._step; } -} + private updateTargets() { + let t1 = this.targets.val1; + if (t1 === undefined) { + t1 = this._dicho.CalculX(this._val1).vCalc; + } + + let t2 = this.targets.val2; + if (t2 === undefined) { + t2 = this._dicho.CalculX(this._val2).vCalc; + } + this.targets.setValues(t1, t2); + } + +} /** * calcul par dichotomie - * principe : y=f(x1,x2,...), on connait une valeur de y, on cherche par ex le x2 correspondant, les autres xi étant fixés. + * principe : y=f(x1,x2,...), on connait une valeur de y, + * on cherche par ex le x2 correspondant, les autres xi étant fixés. * la méthode Equation() calcule analytiquement y=f(xi) */ +// tslint:disable-next-line:max-classes-per-file export class Dichotomie extends Debug { /** * définition de la variable de la fonction @@ -120,18 +123,19 @@ export class Dichotomie extends Debug { private analyticalSymbol: string; /** - * Construction de la classe. - * @param nub Noeud de calcul contenant la méthode de calcul Equation - * @param sVarCalc Nom de la variable à calculer - * @param targetSymbol nom du paramètre calculé analytiquement par Equation() - */ - constructor(private nub: Nub, private sVarCalc: string, dbg: boolean = false, targetSymbol: string = undefined) { + * Construction de la classe. + * @param nub Noeud de calcul contenant la méthode de calcul Equation + * @param sVarCalc Nom de la variable à calculer + * @param targetSymbol nom du paramètre calculé analytiquement par Equation() + */ + constructor(private nub: Nub, private sVarCalc: string, dbg: boolean = false, targetSymbol?: string) { super(dbg); this._paramX = this.nub.getParameter(this.sVarCalc); - if (targetSymbol == undefined) + if (targetSymbol === undefined) { this.analyticalSymbol = this.nub.getFirstAnalyticalParameter().symbol; - else + } else { this.analyticalSymbol = targetSymbol; + } } /** @@ -156,6 +160,40 @@ export class Dichotomie extends Debug { this._startIntervalMaxSteps = n; } + public CalculX(x: number): Result { + this.vX = x; + return this.Calcul(); + } + + /** + * trouve la valeur x pour y=f(x) avec un y donné + * @param rTarget valeur de y + * @param rTol tolérance pour l'arrêt de la recherche + * @param rInit valeur initiale approximative de x + */ + public Dichotomie(rTarget: number, rTol: number, rInit: number): Result { + // console.log("-----"); + // for (let x = 0; x <= 1; x += 0.1) + // console.log(this.CalculX(x).vCalc); + // console.log("-----"); + + // recherche de l'intervalle de départ + + this.debug( + "Dichotomie : valeur cible de " + this.analyticalSymbol + "=" + rTarget + + ", valeur initiale de " + this.sVarCalc + "=" + rInit + ); + + const r = this.getStartInterval(rTarget, rInit); + if (r.ok) { + const interv: SearchInterval = r.intSearch; + // Dichotomie + return this.dichoSearch(rTarget, rTol, interv.min, interv.max, interv.targetLower, interv.targetUpper); + } else { + return new Result(r.res); + } + } + /** * Calcul de l'équation analytique. * @note Wrapper vers this.nub.Equation pour simplifier le code. @@ -163,28 +201,25 @@ export class Dichotomie extends Debug { * Il faudra s'assurer que cette première variable correspond à la méthode de calcul la plus rapide */ private Calcul(): Result { - let r: Result = this.nub.Equation(this.analyticalSymbol); - this.debug("dicho : Calcul(vX=" + this.sVarCalc + "=" + this.vX + ") -> " + this.analyticalSymbol + "=" + r.vCalc); + const r: Result = this.nub.Equation(this.analyticalSymbol); + this.debug( + "dicho : Calcul(vX=" + this.sVarCalc + "=" + this.vX + ") -> " + + this.analyticalSymbol + "=" + r.vCalc); return r; } - CalculX(x: number): Result { - this.vX = x - return this.Calcul(); - } - /** * Détermine si une fonction est croissante ou décroissante. * @param x point auquel on calcul la dérivée * @param dom domaine de définition de la variable */ private isIncreasingFunction(x: number, dom: Interval): boolean { - let epsilon = 1e-8; - let bounds = new Interval(x - epsilon, x + epsilon); + const epsilon = 1e-8; + const bounds = new Interval(x - epsilon, x + epsilon); bounds.setBounds(bounds.intersect(dom)); // au cas où l'on sorte du domaine de la variable de la fonction - let y1 = this.CalculX(bounds.min).vCalc; - let y2 = this.CalculX(bounds.max).vCalc; + const y1 = this.CalculX(bounds.min).vCalc; + const y2 = this.CalculX(bounds.max).vCalc; return y2 > y1; } @@ -200,9 +235,10 @@ export class Dichotomie extends Debug { let n = 0; let ok: boolean = false; do { - var inters: Interval = intSearch.intersect(intMax); - if (inters.length == 0) + const inters: Interval = intSearch.intersect(intMax); + if (inters.length === 0) { break; + } intSearch.setBounds(inters); if (intSearch.hasTargetValue(rTarget)) { ok = true; @@ -223,79 +259,73 @@ export class Dichotomie extends Debug { private getStartInterval(rTarget: number, rInit: number): any { this.debug("intervalle de départ"); - let prmDom: ParamDomain = this._paramX.getDomain(); - let step = 0.001; + const prmDom: ParamDomain = this._paramX.getDomain(); + const step = 0.001; - let min, max; - switch (prmDom.domain) { - case ParamDomainValue.INTERVAL: - min = prmDom.minValue; - max = prmDom.maxValue; - break; - - case ParamDomainValue.NOT_NULL: - if (rInit == 0) - rInit = 1e-8; - // pas de break - - default: - let a = ParamDomain.getDefaultBounds(prmDom.domain); - min = a.min; - max = a.max; + const min: number = prmDom.minValue; + const max: number = prmDom.maxValue; + if (prmDom.domain === ParamDomainValue.NOT_NULL) { + if (rInit === 0) { + rInit = 1e-8; + } } - let intMax: Interval = new Interval(min, max); + const intMax: Interval = new Interval(min, max); try { intMax.checkValue(rInit); - } - catch (m) { - return { ok: false, "res": m }; + } catch (m) { + return { ok: false, res: m }; } // initialisation de l'intervalle de recherche let intSearch: SearchInterval = new SearchInterval(this, rInit - step / 2, rInit + step / 2, step); - intSearch.setBounds(intSearch.intersect(intMax)); // au cas où l'on sorte du domaine de la variable de la fonction + // au cas où l'on sorte du domaine de la variable de la fonction + intSearch.setBounds(intSearch.intersect(intMax)); // sens de variation de la fonction - let inc = this.isIncreasingFunction(rInit, intMax); + const inc = this.isIncreasingFunction(rInit, intMax); - let initTarget = this.CalculX(rInit).vCalc; + const initTarget = this.CalculX(rInit).vCalc; // if (initTarget > rTarget && inc || initTarget < rTarget && !inc) - if (BoolIdentity(initTarget > rTarget, inc)) - intSearch.reverse(); // par ex, la fonction est croissante et la valeur initiale de la variable a une image par la fonction > valeur cible + if (BoolIdentity(initTarget > rTarget, inc)) { + intSearch.reverse(); + // par ex, la fonction est croissante et la valeur initiale + // de la variable a une image par la fonction > valeur cible + } // on cherche dans une première direction let a = this.searchTarget(rTarget, intSearch, intMax); - if (a.ok) + if (a.ok) { return a; + } // il se peut que la fonction ne soit pas monotone et qu'il faille chercher dans l'autre direction - let oldStepSign = intSearch.step > 0 ? 1 : -1; + const oldStepSign = intSearch.step > 0 ? 1 : -1; intSearch = new SearchInterval(this, rInit + step / 2, rInit + step, step * -oldStepSign); - intSearch.setBounds(intSearch.intersect(intMax)); // au cas où l'on sorte du domaine de la variable de la fonction + // au cas où l'on sorte du domaine de la variable de la fonction + intSearch.setBounds(intSearch.intersect(intMax)); a = this.searchTarget(rTarget, intSearch, intMax); - if (a.ok) + if (a.ok) { return a; + } // gestion de l'erreur // la valeur cible de la fonction est elle trouvable ? - - let m; let res: Message; let errDomain = false; switch (prmDom.domain) { case ParamDomainValue.INTERVAL: - let si: SearchInterval = new SearchInterval(this, intMax.min, intMax.max, 1); + const si: SearchInterval = new SearchInterval(this, intMax.min, intMax.max, 1); errDomain = !si.hasTargetValue(rTarget); break; case ParamDomainValue.POS: case ParamDomainValue.POS_NULL: - let y = this.CalculX(1e-8).vCalc; + const y = this.CalculX(1e-8).vCalc; errDomain = inc && (rTarget < y); break; @@ -303,28 +333,28 @@ export class Dichotomie extends Debug { break; default: - throw "unsupported parameter domain value"; + throw new Error("unsupported parameter domain value"); } if (errDomain) { res = new Message(MessageCode.ERROR_DICHO_INIT_DOMAIN); - res.extraVar["targetSymbol"] = this.analyticalSymbol; // symbole de la variable calculée par la fonction - res.extraVar["targetValue"] = rTarget; // valeur cible pour la fonction - res.extraVar["variableInterval"] = intMax.toString(); // intervalle de valeurs pour la variable d'entrée de la fonction - res.extraVar["variableSymbol"] = this._paramX.symbol; // symbole de la variable d'entrée de la fonction - } - else { + res.extraVar.targetSymbol = this.analyticalSymbol; // symbole de la variable calculée par la fonction + res.extraVar.targetValue = rTarget; // valeur cible pour la fonction + // intervalle de valeurs pour la variable d'entrée de la fonction + res.extraVar.variableInterval = intMax.toString(); + res.extraVar.variableSymbol = this._paramX.symbol; // symbole de la variable d'entrée de la fonction + } else { // if (intSearch.step < 0) - if (BoolIdentity(initTarget > rTarget, inc)) + if (BoolIdentity(initTarget > rTarget, inc)) { res = new Message(MessageCode.ERROR_DICHO_INITVALUE_HIGH); - else + } else { res = new Message(MessageCode.ERROR_DICHO_INITVALUE_LOW); - - res.extraVar["variableSymbol"] = this._paramX.symbol; // symbole de la variable de la fonction - res.extraVar["variableInitValue"] = rInit; // valeur initiale de la variable - res.extraVar["targetSymbol"] = this.analyticalSymbol; // symbole de la variable calculée par la fonction - res.extraVar["targetValue"] = rTarget; // valeur cible pour la fonction - res.extraVar["initTarget"] = initTarget; // valeur de la fonction pour la valeur initiale de la variable + } + res.extraVar.variableSymbol = this._paramX.symbol; // symbole de la variable de la fonction + res.extraVar.variableInitValue = rInit; // valeur initiale de la variable + res.extraVar.targetSymbol = this.analyticalSymbol; // symbole de la variable calculée par la fonction + res.extraVar.targetValue = rTarget; // valeur cible pour la fonction + res.extraVar.initTarget = initTarget; // valeur de la fonction pour la valeur initiale de la variable } return { ok: false, res }; @@ -338,56 +368,34 @@ export class Dichotomie extends Debug { * @param Ymin valeur de la fonction pour Xmin * @param Ymax valeur de la fonction pour Xmax */ + // tslint:disable-next-line:variable-name private dichoSearch(Ytarg: number, rTol: number, Xmin: number, Xmax: number, Ymin: number, Ymax: number): Result { this.debug("dichoSearch : yTarget " + Ytarg + " X " + Xmin + "->" + Xmax + " tol " + rTol); - let Xmid = (Xmin + Xmax) / 2; + // tslint:disable-next-line:variable-name + const Xmid = (Xmin + Xmax) / 2; - if (Math.abs(Xmin - Xmax) <= rTol) + if (Math.abs(Xmin - Xmax) <= rTol) { return new Result(Xmid); + } - let Ymid = this.CalculX(Xmid).vCalc; + // tslint:disable-next-line:variable-name + const Ymid = this.CalculX(Xmid).vCalc; if (Ymin < Ymax) { // fonction croissante - if (Ytarg > Ymid) - return this.dichoSearch(Ytarg, rTol, Xmid, Xmax, Ymid, Ymax); // intervalle supérieur + if (Ytarg > Ymid) { + return this.dichoSearch(Ytarg, rTol, Xmid, Xmax, Ymid, Ymax); + } // intervalle supérieur return this.dichoSearch(Ytarg, rTol, Xmin, Xmid, Ymin, Ymid); // intervalle inférieur - } - else { + } else { // fonction décroissante - if (Ytarg > Ymid) - return this.dichoSearch(Ytarg, rTol, Xmin, Xmid, Ymin, Ymid); // intervalle inférieur + if (Ytarg > Ymid) { + return this.dichoSearch(Ytarg, rTol, Xmin, Xmid, Ymin, Ymid); + } // intervalle inférieur return this.dichoSearch(Ytarg, rTol, Xmid, Xmax, Ymid, Ymax); // intervalle supérieur } } - /** - * trouve la valeur x pour y=f(x) avec un y donné - * @param rTarget valeur de y - * @param rTol tolérance pour l'arrêt de la recherche - * @param rInit valeur initiale approximative de x - */ - Dichotomie(rTarget: number, rTol: number, rInit: number): Result { - // console.log("-----"); - // for (let x = 0; x <= 1; x += 0.1) - // console.log(this.CalculX(x).vCalc); - // console.log("-----"); - - // recherche de l'intervalle de départ - - this.debug("Dichotomie : valeur cible de " + this.analyticalSymbol + "=" + rTarget + ", valeur initiale de " + this.sVarCalc + "=" + rInit); - - let r = this.getStartInterval(rTarget, rInit); - if (r.ok) - var interv: SearchInterval = r.intSearch; - else { - return new Result(r.res); - } - - // Dichotomie - - return this.dichoSearch(rTarget, rTol, interv.min, interv.max, interv.targetLower, interv.targetUpper); - } } diff --git a/src/nub.ts b/src/nub.ts index d3822c3ab8c3d24aabe033a8a341c5b483683da4..c88151028dc73ab235e781aebaf6b080e70ca7e1 100644 --- a/src/nub.ts +++ b/src/nub.ts @@ -1,7 +1,7 @@ -import { Debug, Serie } from "./base" +import { Debug, Serie } from "./base"; +import { Dichotomie } from "./dichotomie"; +import { ComputeNode, ParamDefinition, ParamsEquation } from "./param"; import { Result } from "./util/result"; -import { Dichotomie } from "./dichotomie" -import { ComputeNode, ParamDefinition, ParamsEquation } from "./param" /** * Classe abstraite de Noeud de calcul : classe de base pour tous les calculs @@ -14,49 +14,58 @@ export abstract class Nub extends ComputeNode { */ /** - * étapes de recherche de l'intervalle de départ - */ + * étapes de recherche de l'intervalle de départ + */ set dichoStartIntervalMaxSteps(n: number) { this._dichoStartIntervalMaxSteps = n; } - /** + /** * Formule utilisée pour le calcul analytique (solution directe ou méthode de résolution spécifique) */ - abstract Equation(sVarCalc: string): Result; + public abstract Equation(sVarCalc: string): Result; - /** + /** * Calcul d'une équation quelque soit l'inconnue à calculer * @param sVarCalc nom de la variable à calculer * @param rInit valeur initiale de la variable à calculer dans le cas de la dichotomie * @param rPrec précision de calcul */ - Calc(sVarCalc: string, rInit: number = 0, rPrec: number = 0.001): Result { - if (this._prms.map[sVarCalc].isAnalytical()) + public Calc(sVarCalc: string, rInit: number = 0, rPrec: number = 0.001): Result { + if (this._prms.map[sVarCalc].isAnalytical()) { return this.Equation(sVarCalc); + } - return this.Solve(sVarCalc, rInit, rPrec); + const resSolve: Result = this.Solve(sVarCalc, rInit, rPrec); + if (!resSolve.ok) { + return resSolve; + } + const sAnalyticalPrm: string = this.getFirstAnalyticalParameter().symbol; + this._prms.map[sVarCalc].v = resSolve.vCalc; + const res: Result = this.Equation(sAnalyticalPrm); + res.vCalc = resSolve.vCalc; + return res; } - CalcSerie(svarCalc: string, serie: Serie): Result[] { + public CalcSerie(svarCalc: string, serie: Serie): Result[] { /** @todo faire une boucle pour appeler this.Calc avec chaque valeur de serie.values - * + * */ // let results = [new (Result)]; - let results = [new Result(0)]; + const results = [new Result(0)]; return results; } - /** + /** * Résoud l'équation par une méthode numérique * @param sVarCalc nom de la variable à calculer * @param rInit valeur initiale de la variable à calculer dans le cas de la dichotomie * @param rPrec précision de calcul */ - Solve(sVarCalc: string, rInit: number, rPrec: number): Result { - let dicho: Dichotomie = new Dichotomie(this, sVarCalc, this.DBG); + private Solve(sVarCalc: string, rInit: number, rPrec: number): Result { + const dicho: Dichotomie = new Dichotomie(this, sVarCalc, this.DBG); dicho.startIntervalMaxSteps = this._dichoStartIntervalMaxSteps; - var target = this._prms.getFirstAnalyticalParameter(); + const target = this._prms.getFirstAnalyticalParameter(); return dicho.Dichotomie(target.v, rPrec, rInit); } } diff --git a/src/param.ts b/src/param.ts index d8aa8ee04f8ff60a352471ec7c15fd86e6efcd7f..32700fab30f383f1dabd7fd2042fc97cc39de408 100644 --- a/src/param.ts +++ b/src/param.ts @@ -1,8 +1,8 @@ // import { Debug, UndefinedError } from './base'; -import { Debug } from './base'; -import { DefinedNumber } from './util/definedvalue'; +import { Debug } from "./base"; +import { DefinedNumber } from "./util/definedvalue"; +import { Interval } from "./util/interval"; import { Message, MessageCode } from "./util/message"; -import { Interval } from './util/interval'; /** * domaine de définition du paramètre @@ -18,7 +18,6 @@ export enum ParamDomainValue { */ POS_NULL, - /** * > 0 */ @@ -36,13 +35,35 @@ export enum ParamDomainValue { } export class ParamDomain { + + public static getDefaultBounds(d: ParamDomainValue): { min: number, max: number } { + switch (d) { + case ParamDomainValue.INTERVAL: + const e: Message = new Message(MessageCode.ERROR_PARAMDOMAIN_INVALID); + throw e; + + case ParamDomainValue.ANY: + case ParamDomainValue.NOT_NULL: + return { min: -Infinity, max: Infinity }; + + case ParamDomainValue.POS: + return { min: 1e-9, max: Infinity }; + + case ParamDomainValue.POS_NULL: + return { min: 0, max: Infinity }; + + // default: + // throw "valeur de ParamDomainValue" + ParamDomainValue[d] + " non prise en charge"; + } + } + private _domain: ParamDomainValue; private _minValue: number; private _maxValue: number; - constructor(d: ParamDomainValue, min: number = undefined, max: number = undefined) { + constructor(d: ParamDomainValue, min?: number, max?: number) { this.checkValue(d, min, max); this._domain = d; @@ -53,37 +74,13 @@ export class ParamDomain { break; default: - let b = ParamDomain.getDefaultBounds(this._domain); + const b = ParamDomain.getDefaultBounds(this._domain); this._minValue = b.min; this._maxValue = b.max; break; } } - private checkValue(val: number, min: number, max: number) { - switch (val) { - case ParamDomainValue.INTERVAL: - if (min == undefined || max == undefined || min > max) { - // throw "invalid " + min + "/" + max + " min/max boundaries for 'interval' parameter definition domain" - let e: Message = new Message(MessageCode.ERROR_PARAMDOMAIN_INTERVAL_BOUNDS); - e.extraVar["minValue"] = min; - e.extraVar["maxValue"] = max; - throw e; - } - break; - - default: - // en dehors du cas INTERVAL, on ne doit pas fournir de valeur - if (min != undefined || max != undefined) { - let e: Message = new Message(MessageCode.ERROR_PARAMDOMAIN_INTERVAL_BOUNDS); - e.extraVar["minValue"] = min; - e.extraVar["maxValue"] = max; - throw e; - } - break; - } - } - get domain() { return this._domain; } @@ -102,41 +99,44 @@ export class ParamDomain { return new Interval(this._minValue, this._maxValue); default: - let b = ParamDomain.getDefaultBounds(this._domain); - return new Interval(b["min"], b["max"]); + const b = ParamDomain.getDefaultBounds(this._domain); + return new Interval(b.min, b.max); } } - public static getDefaultBounds(d: ParamDomainValue): { min: number, max: number } { - switch (d) { + public clone(): ParamDomain { + switch (this._domain) { case ParamDomainValue.INTERVAL: - let e: Message = new Message(MessageCode.ERROR_PARAMDOMAIN_INVALID); - throw e; - - case ParamDomainValue.ANY: - case ParamDomainValue.NOT_NULL: - return { min: -Infinity, max: Infinity }; - - case ParamDomainValue.POS: - return { min: 1e-9, max: Infinity }; - - case ParamDomainValue.POS_NULL: - return { min: 0, max: Infinity }; + return new ParamDomain(this._domain, this._minValue, this._maxValue); - // default: - // throw "valeur de ParamDomainValue" + ParamDomainValue[d] + " non prise en charge"; + default: + return new ParamDomain(this._domain); } } - public clone(): ParamDomain { - switch (this._domain) { + private checkValue(val: number, min: number, max: number) { + switch (val) { case ParamDomainValue.INTERVAL: - return new ParamDomain(this._domain, this._minValue, this._maxValue); + if (min === undefined || max === undefined || min > max) { + const e: Message = new Message(MessageCode.ERROR_PARAMDOMAIN_INTERVAL_BOUNDS); + e.extraVar.minValue = min; + e.extraVar.maxValue = max; + throw e; + } + break; default: - return new ParamDomain(this._domain); + // en dehors du cas INTERVAL, on ne doit pas fournir de valeur + if (min !== undefined || max !== undefined) { + const e: Message = new Message(MessageCode.ERROR_PARAMDOMAIN_INTERVAL_BOUNDS); + e.extraVar.minValue = min; + e.extraVar.maxValue = max; + throw e; + } + break; } } + } /** @@ -167,6 +167,7 @@ export enum ParamCalculability { /** * paramètre avec symbole et domaine de définition */ +// tslint:disable-next-line:max-classes-per-file export class BaseParam extends DefinedNumber { /** * symbole @@ -178,14 +179,15 @@ export class BaseParam extends DefinedNumber { */ private _domain: ParamDomain; - constructor(symb: string, d: ParamDomain | ParamDomainValue, val: number = undefined) { + constructor(symb: string, d: ParamDomain | ParamDomainValue, val?: number) { super(val); this._symbol = symb; - if (d instanceof ParamDomain) + if (d instanceof ParamDomain) { this._domain = d; - else - this._domain = new ParamDomain(<ParamDomainValue>d); + } else { + this._domain = new ParamDomain(d as ParamDomainValue); + } this.checkValue(val); } @@ -194,7 +196,7 @@ export class BaseParam extends DefinedNumber { return this._symbol; } - getDomain(): ParamDomain { + public getDomain(): ParamDomain { return this._domain; } @@ -202,22 +204,21 @@ export class BaseParam extends DefinedNumber { return this._domain.interval; } - /** * gestion de la valeur */ - getValue(): number { + public getValue(): number { if (!this.isDefined) { - let e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_UNDEFINED); - e.extraVar["symbol"] = this.symbol; + const e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_UNDEFINED); + e.extraVar.symbol = this.symbol; throw e; } return super.getValue(); } - setValue(val: number) { + public setValue(val: number) { this.checkValue(val); super.setValue(val); @@ -225,7 +226,7 @@ export class BaseParam extends DefinedNumber { } public checkValue(v: number) { - let sDomain = ParamDomainValue[this._domain.domain]; + const sDomain = ParamDomainValue[this._domain.domain]; switch (this._domain.domain) { case ParamDomainValue.ANY: @@ -233,47 +234,47 @@ export class BaseParam extends DefinedNumber { case ParamDomainValue.POS: if (v <= 0) { - let e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_POS); - e.extraVar["symbol"] = this.symbol; - e.extraVar["value"] = v; - throw e; + const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_POS); + f.extraVar.symbol = this.symbol; + f.extraVar.value = v; + throw f; } break; case ParamDomainValue.POS_NULL: if (v < 0) { - let e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_POSNULL); - e.extraVar["symbol"] = this.symbol; - e.extraVar["value"] = v; - throw e; + const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_POSNULL); + f.extraVar.symbol = this.symbol; + f.extraVar.value = v; + throw f; } break; case ParamDomainValue.NOT_NULL: - if (v == 0) { - let e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_NULL); - e.extraVar["symbol"] = this.symbol; - throw e; + if (v === 0) { + const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_NULL); + f.extraVar.symbol = this.symbol; + throw f; } break; case ParamDomainValue.INTERVAL: - let min = this._domain.minValue; - let max = this._domain.maxValue; + const min = this._domain.minValue; + const max = this._domain.maxValue; if (v < min || v > max) { - let e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_INTERVAL); - e.extraVar["symbol"] = this.symbol; - e.extraVar["value"] = v; - e.extraVar["minValue"] = min; - e.extraVar["maxValue"] = max; - throw e; + const f = new Message(MessageCode.ERROR_PARAMDEF_VALUE_INTERVAL); + f.extraVar.symbol = this.symbol; + f.extraVar.value = v; + f.extraVar.minValue = min; + f.extraVar.maxValue = max; + throw f; } break; default: - let e = new Message(MessageCode.ERROR_PARAMDOMAIN_INVALID); - e.extraVar["symbol"] = this.symbol; - e.extraVar["domain"] = sDomain; + const e = new Message(MessageCode.ERROR_PARAMDOMAIN_INVALID); + e.extraVar.symbol = this.symbol; + e.extraVar.domain = sDomain; throw e; } } @@ -282,6 +283,7 @@ export class BaseParam extends DefinedNumber { /** * définition d'un paramètre d'un neud de calcul */ +// tslint:disable-next-line:max-classes-per-file export class ParamDefinition extends BaseParam { /** * calculabilité @@ -291,7 +293,7 @@ export class ParamDefinition extends BaseParam { /** * noeud de calcul parent */ - private _computeNodeType: ComputeNodeType + private _computeNodeType: ComputeNodeType; // private static _idGen: number = 0; // A VIRER // private _id: number; // A VIRER @@ -320,9 +322,9 @@ export class ParamDefinition extends BaseParam { } set v(val: number) { - if (this.calculability == ParamCalculability.NONE) { - let e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_FIXED); - e.extraVar["symbol"] = this.symbol; + if (this.calculability === ParamCalculability.NONE) { + const e = new Message(MessageCode.ERROR_PARAMDEF_VALUE_FIXED); + e.extraVar.symbol = this.symbol; throw e; } super.setValue(val); @@ -335,15 +337,15 @@ export class ParamDefinition extends BaseParam { /** * variable calculable par l'équation ? */ - isAnalytical(): boolean { - return this.calculability == ParamCalculability.EQUATION; + public isAnalytical(): boolean { + return this.calculability === ParamCalculability.EQUATION; } get calculability(): ParamCalculability { - if (this._calc == undefined) { + if (this._calc === undefined) { // throw "value of parameter '" + this._symbol + "' calculability is not defined"; - let e = new Message(MessageCode.ERROR_PARAMDEF_CALC_UNDEFINED); - e.extraVar["symbol"] = this.symbol; + const e = new Message(MessageCode.ERROR_PARAMDEF_CALC_UNDEFINED); + e.extraVar.symbol = this.symbol; throw e; } @@ -355,27 +357,61 @@ export class ParamDefinition extends BaseParam { } public clone(): ParamDefinition { - let res = new ParamDefinition(this._computeNodeType, this.symbol, this.getDomain().clone()); + const res = new ParamDefinition(this._computeNodeType, this.symbol, this.getDomain().clone()); res._calc = this._calc; res.value = this.uncheckedValue; return res; } } - /** * liste des paramètres d'une équation */ +// tslint:disable-next-line:max-classes-per-file export abstract class ParamsEquation { + protected _paramMap: { [key: string]: ParamDefinition } = {}; + public hasParameter(name: string): boolean { + for (const ps in this._paramMap) { + const p: ParamDefinition = this._paramMap[ps]; + if (p.symbol === name) { + return true; + } + } + + return false; + } + + public getParameter(name: string): ParamDefinition { + for (const ps in this._paramMap) { + const p: ParamDefinition = this._paramMap[ps]; + if (p.symbol === name) { + return p; + } + } + + throw new Error("ParamsEquation.getParameter() : invalid parameter name " + name); + } + + public getFirstAnalyticalParameter(): ParamDefinition { + for (const ps in this._paramMap) { + const p: ParamDefinition = this._paramMap[ps]; + if (p.isAnalytical()) { + return p; + } + } + return undefined; + } + protected addParamDefinition(p: ParamDefinition) { - if (!this.hasParameter(p.symbol)) + if (!this.hasParameter(p.symbol)) { this._paramMap[p.symbol] = p; + } } protected addParamDefinitions(ps: ParamsEquation) { - for (let pi in ps._paramMap) { + for (const pi in ps._paramMap) { this.addParamDefinition(ps._paramMap[pi]); } } @@ -385,46 +421,20 @@ export abstract class ParamsEquation { } protected checkParametersCalculability() { - let res = []; + const res = []; - for (let ps in this._paramMap) { - let p: ParamDefinition = this._paramMap[ps]; - if (p.calculability == undefined) + for (const ps in this._paramMap) { + const p: ParamDefinition = this._paramMap[ps]; + if (p.calculability === undefined) { res.push(p.symbol); + } } - if (res.length > 0) - throw 'Calculability of parameter(s) ' + res.toString() + ' has not been defined'; - } - - public hasParameter(name: string): boolean { - for (let ps in this._paramMap) { - let p: ParamDefinition = this._paramMap[ps]; - if (p.symbol == name) - return true; - } - - return false; - } - - public getParameter(name: string): ParamDefinition { - for (let ps in this._paramMap) { - let p: ParamDefinition = this._paramMap[ps]; - if (p.symbol == name) - return p; + if (res.length > 0) { + throw new Error("Calculability of parameter(s) " + res.toString() + " has not been defined"); } - - throw 'ParamsEquation.getParameter() : invalid parameter name ' + name; } - public getFirstAnalyticalParameter(): ParamDefinition { - for (let ps in this._paramMap) { - let p: ParamDefinition = this._paramMap[ps]; - if (p.isAnalytical()) - return p; - } - return undefined; - } } /** @@ -446,11 +456,10 @@ export enum ComputeNodeType { /** * noeud de calcul */ +// tslint:disable-next-line:max-classes-per-file export abstract class ComputeNode extends Debug { protected _prms: ParamsEquation; - protected abstract setParametersCalculability(): void; - constructor(prms: ParamsEquation, dbg: boolean = false) { super(dbg); this._prms = prms; @@ -464,4 +473,7 @@ export abstract class ComputeNode extends Debug { public getFirstAnalyticalParameter(): ParamDefinition { return this._prms.getFirstAnalyticalParameter(); } + + protected abstract setParametersCalculability(): void; + } diff --git a/src/structure/parallel_structure.ts b/src/structure/parallel_structure.ts new file mode 100644 index 0000000000000000000000000000000000000000..c692fd4b67dc9d68cd7efa14ad3ad1f69bb20e12 --- /dev/null +++ b/src/structure/parallel_structure.ts @@ -0,0 +1,152 @@ +import { Nub } from "../nub"; +import { ParamCalculability } from "../param"; +import { Message } from "../util/message"; +import { Result } from "../util/result"; +import { Structure } from "./structure"; + +import { ParallelStructureParams } from "./parallel_structure_params"; + +/** + * Calcul de une ou plusieurs structures hydrauliques en parallèles + * reliées par les cotes amont et aval et dont le débit est égal à la + * somme des débits de chaque structure. + */ +export class ParallelStructure extends Nub { + + /** Tableau des structures hydrauliques en parallèle */ + public structures: Structure[] = []; + + /** + * paramètres castés au bon type + */ + get prms(): ParallelStructureParams { + return this._prms as ParallelStructureParams; + } + + /** + * Mise à jour de Z1 pour toutes les structures en parallèle + */ + set Z1(Z1: number) { + this.prms.Z1.v = Z1; + for (const structure of this.structures) { + structure.prms.Z1.v = Z1; + structure.prms.update_h1h2(); + } + } + + /** + * Mise à jour de Z2 pour toutes les structures en parallèle + */ + set Z2(Z2: number) { + this.prms.Z2.v = Z2; + for (const structure of this.structures) { + structure.prms.Z2.v = Z2; + structure.prms.update_h1h2(); + } + } + + /** + * Ajout d'une structure en parallèle + * @param structure La structure à rajouter + */ + public addStructure(structure: Structure) { + this.structures.push(structure); + } + + /** + * Supprime une structure hydraulique + * @param index numéro de la structure dans le tableau + */ + public deleteStructure(index: number) { + if (index > -1) { + this.structures.splice(index, 1); + } else { + throw new Error("ParallelStructure.deleteStructure invalid index=" + index); + } + } + + /** + * Calcul du débit des structures en parallèle (sans détail pour chaque structure) + * @param sVarCalc Variable à calculer (Q uniquement) + */ + public Equation(sVarCalc: string): Result { + Structure.CheckEquation(sVarCalc); + this.Z1 = this.prms.Z1.v; + this.Z2 = this.prms.Z2.v; + return this.CalcQ(); + } + + /** + * Calcul de la somme des débits de chaque structure + * @param iExcept Index de la structure à ne pas additionner (optionel) + */ + public CalcQ(iExcept?: number): Result { + if (iExcept !== undefined) { + if (iExcept < 0 || iExcept >= this.structures.length) { + throw new Error( + "ParallelStructure.CalcQ iExcept not in [0;" + (this.structures.length - 1) + "]", + ); + } + } + const calcRes: Result = new Result(); + let qTot: number = 0; + for (let i = 0 ; i < this.structures.length; i++) { + if (i !== iExcept) { + const res: Result = this.structures[i].Calc("Q"); + calcRes.addResult(res.result); + qTot += res.vCalc; + } + } + // Insert le débit total en premier résultat + calcRes.insertResult(new Result(qTot).result, 0); + return calcRes; + } + + /** + * 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 : + * "Q", "Z1", "Z2" ou "n.X" avec "n" l'index de l'ouvrage et "X" son paramètre + * @param rInit Valeur initiale + * @param rPrec Précision attendue + */ + public Calc(sVarCalc: string, rInit: number = 0, rPrec: number = 0.001): Result { + switch (sVarCalc) { + case "Z1" : + case "Z2" : + case "Q" : + return super.Calc(sVarCalc, rInit, rPrec); + default: + // Pour les caractéristiques des ouvrages + return this.CalcStructPrm(sVarCalc, rInit, rPrec); + } + } + + /** + * paramétrage de la calculabilité des paramètres + */ + protected setParametersCalculability() { + this.prms.Q.calculability = ParamCalculability.EQUATION; + this.prms.Z1.calculability = ParamCalculability.DICHO; + this.prms.Z2.calculability = ParamCalculability.DICHO; + } + + /** + * Calcul du paramètre d'un des ouvrages en parallèle + * @param sVarCalc Nom du paramètre à calculer : "n.X" avec "n" l'index de l'ouvrage et "X" son paramètre + * @param rInit Valeur initiale + * @param rPrec Précision attendue + */ + private CalcStructPrm(sVarCalc: string, rInit: number = 0, rPrec: number = 0.001): Result { + // Détection de la structure où calculer le paramètre + let sIndex: string; + let sPrm: string; + [sIndex, sPrm] = sVarCalc.split("."); + const index = parseInt(sIndex, 10); + + // Le débit restant sur la structure en calcul est : + const qTarget: number = this.prms.Q.v - this.CalcQ(index).vCalc; + + // Calcul du paramètre de la structure en calcul + return this.structures[index].Calc(sPrm, rInit, rPrec); + } +} diff --git a/src/structure/parallel_structure_params.ts b/src/structure/parallel_structure_params.ts new file mode 100644 index 0000000000000000000000000000000000000000..c7d556dbfc0eb1b920e3da8ea900491eecd8023c --- /dev/null +++ b/src/structure/parallel_structure_params.ts @@ -0,0 +1,32 @@ +import { Nub } from "../nub"; +import { ComputeNodeType, ParamDefinition, ParamDomainValue, ParamsEquation } from "../param"; + +/** + * Common parameters of hydraulic structure equations + */ +export class ParallelStructureParams extends ParamsEquation { + /** Débit (m3/s) */ + public Q: ParamDefinition; + + /** Cote de l'eau amont (m) */ + public Z1: ParamDefinition; + + /** Cote de l'eau aval (m) */ + public Z2: ParamDefinition; + + /** + * Paramètres communs à toutes les équations de structure + * @param rQ Débit total (m3/s) + * @param rZ1 Cote de l'eau amont (m) + * @param rZ2 Cote de l'eau aval (m) + */ + constructor(rQ: number, rZ1: number, rZ2: number) { + super(); + this.Q = new ParamDefinition(ComputeNodeType.CondDistri, "Q", ParamDomainValue.ANY, rQ); + this.addParamDefinition(this.Q); + this.Z1 = new ParamDefinition(ComputeNodeType.CondDistri, "Z1", ParamDomainValue.ANY, rZ1); + this.addParamDefinition(this.Z1); + this.Z2 = new ParamDefinition(ComputeNodeType.CondDistri, "Z2", ParamDomainValue.ANY, rZ2); + this.addParamDefinition(this.Z2); + } +} diff --git a/src/structure/rectangular_structure_params.ts b/src/structure/rectangular_structure_params.ts index 4d5d267e1a5f90482132903ea625e6d31aadd612..485d540bdfa2b17d1d4363d1edcac93da1be0ff4 100644 --- a/src/structure/rectangular_structure_params.ts +++ b/src/structure/rectangular_structure_params.ts @@ -10,6 +10,7 @@ export class RectangularStructureParams extends StructureParams { public L: ParamDefinition; /** Discharge coefficient */ + // tslint:disable-next-line:variable-name public Cd: ParamDefinition; /** diff --git a/src/structure/structure.ts b/src/structure/structure.ts index 612c197fb6ebb9ec7c39ec285675904da9135d3e..809ab19b02c5645c081e3dbec810d14c14b6260f 100644 --- a/src/structure/structure.ts +++ b/src/structure/structure.ts @@ -11,12 +11,12 @@ export { StructureParams }; * Flow mode: weir or orifice flow */ export enum StructureFlowMode { - /** Weir flow */ - WEIR, - /** Orifice flow */ - ORIFICE, - /** Zéro flow */ - NULL, + /** Weir flow */ + WEIR, + /** Orifice flow */ + ORIFICE, + /** Zéro flow */ + NULL, } /** @@ -31,13 +31,20 @@ export enum StructureFlowRegime { SUBMERGED, /** Zéro flow */ NULL, - } +} /** * classe de calcul sur la conduite distributrice */ export abstract class Structure extends Nub { + /** + * Test générique si VarCalc="Q" pour l'utilisation de Equation + */ + public static CheckEquation(sVarCalc: string) { + if (sVarCalc !== "Q") { throw new Error("Structure.Equation() : invalid parameter name " + sVarCalc); } + } + /** Constante utile : Racine de 2g */ protected static readonly R2G: number = Math.sqrt(2 * 9.81); @@ -52,11 +59,6 @@ export abstract class Structure extends Nub { return this._prms as StructureParams; } - /** - * Calcul du mode et du régime d'écoulement - */ - public abstract Equation(sVarCalc: string): Result; - /** * Calcul d'une équation quelque soit l'inconnue à calculer. * Gestion du débit nul et de l'inversion de débit @@ -77,23 +79,42 @@ export abstract class Structure extends Nub { } else if (this.prms.Q.v === 0) { // Débit nul <=> tirant d'eau amont = tirant d'eau aval ou tout autre paramètre nul switch (sVarCalc) { - case "h1" : - return new Result(this.prms.h2.v, flagsNull); - case "h2" : - return new Result(this.prms.h1.v, flagsNull); + case "Z1" : + return new Result(this.prms.h2.v, flagsNull); + case "Z2" : + return new Result(this.prms.h1.v, flagsNull); default : - // Est-ce toujours vrai ? Nécessitera peut-être d'étendre la méthode - return new Result(0, flagsNull); + // Est-ce toujours vrai ? Nécessitera peut-être d'étendre la méthode + return new Result(0, flagsNull); } - } else if (this.prms.W.v === 0 && sVarCalc === "h1") { + } else if (this.prms.W.v === 0 && sVarCalc === "Z1") { return new Result(Infinity, flagsNull); // Si la vanne est fermée la cote amont est infinie } + // Gestion du cas d'écoulement impossible Z1 > Z2 et Q <= 0 + if (!(sVarCalc === "Q" || sVarCalc === "Z1" || sVarCalc === "Z2")) { + if ( + (this.prms.Z1.v > this.prms.Z2.v && this.prms.Q.v <= 0) || + (this.prms.Z1.v < this.prms.Z2.v && this.prms.Q.v >= 0) + ) { + // On ferme l'ouvrage et on renvoie un code d'erreur + let rPrm: number; + switch (sVarCalc) { + case "ZDV": + rPrm = Infinity; + default: + rPrm = 0; + } + // TODO Ajouter un message d'erreur + // "Les cotes et le débit ne sont pas cohérents => fermeture de l'ouvrage + return new Result(rPrm, flagsNull); + } + } + // Gestion de l'inversion de débit : on inverse l'amont et l'aval pour le calcul if ((sVarCalc === "Q" && (this.prms.h1.v < this.prms.h2.v)) || (sVarCalc !== "Q" && this.prms.Q.v < 0)) { [this.prms.h1.v, this.prms.h2.v] = [this.prms.h2.v, this.prms.h1.v]; // Swap ES6 fashion - let res: Result; - res = super.Calc(sVarCalc, rInit, rPrec); + const res: Result = super.Calc(sVarCalc, rInit, rPrec); [this.prms.h1.v, this.prms.h2.v] = [this.prms.h2.v, this.prms.h1.v]; // Swap ES6 fashion return res; } @@ -102,7 +123,7 @@ export abstract class Structure extends Nub { return super.Calc(sVarCalc, rInit, rPrec); } - protected defaultResultData() { + protected getResultData() { return { Mode: this.getFlowMode(), Regime: this.getFlowRegime(), @@ -164,11 +185,4 @@ export abstract class Structure extends Nub { return StructureFlowRegime.SUBMERGED; } } - - /** - * Test générique si VarCalc="Q" pour l'utilisation de Equation - */ - protected CheckEquation(sVarCalc: string) { - if (sVarCalc !== "Q") { throw new Error("Structure.Equation() : invalid parameter name " + sVarCalc); } - } } diff --git a/src/structure/structure_cem88d.ts b/src/structure/structure_cem88d.ts index 345e6825f5ca9008efc551a3324ce23c14839b37..27c2405d7bc35b16eb5633bcd088feac5ea74d3d 100644 --- a/src/structure/structure_cem88d.ts +++ b/src/structure/structure_cem88d.ts @@ -15,8 +15,8 @@ export class StructureCem88d extends RectangularStructure { * @param sVarCalc Variable à calculer (doit être "Q") */ public Equation(sVarCalc: string): Result { - super.CheckEquation(sVarCalc); - const data = super.defaultResultData(); + Structure.CheckEquation(sVarCalc); + const data = this.getResultData(); let v: number; const cd: number = this.prms.Cd.v * this.prms.L.v * Structure.R2G; diff --git a/src/structure/structure_cem88v.ts b/src/structure/structure_cem88v.ts index cef783f7cb1adb6d65f57ca65d1bfce705b20c05..c0dc94caaaef79047b945e1fd85517529441c697 100644 --- a/src/structure/structure_cem88v.ts +++ b/src/structure/structure_cem88v.ts @@ -15,8 +15,8 @@ export class StructureCem88v extends RectangularStructure { * @param sVarCalc Variable à calculer (doit être "Q") */ public Equation(sVarCalc: string): Result { - super.CheckEquation(sVarCalc); - const data = super.defaultResultData(); + Structure.CheckEquation(sVarCalc); + const data = this.getResultData(); let v: number; const mu0: number = 2 / 3 * this.prms.Cd.v; diff --git a/src/structure/structure_cunge80.ts b/src/structure/structure_cunge80.ts index bc8b05c40545d4128eb3351d70b42bc434eae8f0..155aa52ccefcc731842e9645cc77b26d3ffe6060 100644 --- a/src/structure/structure_cunge80.ts +++ b/src/structure/structure_cunge80.ts @@ -14,8 +14,8 @@ export class StructureCunge80 extends RectangularStructure { * @param sVarCalc Variable à calculer (doit être égale à Q ici) */ public Equation(sVarCalc: string): Result { - super.CheckEquation(sVarCalc); - const data = super.defaultResultData(); + Structure.CheckEquation(sVarCalc); + const data = this.getResultData(); let v: number; switch (data.Regime) { diff --git a/src/structure/structure_orifice_free.ts b/src/structure/structure_orifice_free.ts index 39272136e8abf5a59581bb2e84ed0a02bcb4a0a3..4d18544dcf38ff1823b7a8b9f9b2856bec36a744 100644 --- a/src/structure/structure_orifice_free.ts +++ b/src/structure/structure_orifice_free.ts @@ -14,14 +14,16 @@ export class StructureOrificeFree extends RectangularStructure { * @param sVarCalc Variable à calculer (doit être égale à Q ici) */ public Equation(sVarCalc: string): Result { - super.CheckEquation(sVarCalc); - const data = super.defaultResultData(); + Structure.CheckEquation(sVarCalc); + const data = this.getResultData(); - // TODO : Warning si les conditions hydrauliques ne correspondent pas à un écoulement dénoyé - data.Regime = StructureFlowRegime.FREE; const v = this.prms.Cd.v * Math.min(this.prms.W.v, this.prms.h1.v) * this.prms.L.v * Structure.R2G * Math.sqrt(this.prms.h1.v); return new Result(v, data); } + + protected getFlowRegime() { + return StructureFlowRegime.FREE; + } } diff --git a/src/structure/structure_orifice_submerged.ts b/src/structure/structure_orifice_submerged.ts index c8a670153bdffe166825e33770b2427b1eecd966..dd91446e475f119e28d99423f07863ba4d8662df 100644 --- a/src/structure/structure_orifice_submerged.ts +++ b/src/structure/structure_orifice_submerged.ts @@ -14,14 +14,16 @@ export class StructureOrificeSubmerged extends RectangularStructure { * @param sVarCalc Variable à calculer (doit être égale à Q ici) */ public Equation(sVarCalc: string): Result { - super.CheckEquation(sVarCalc); - const data = super.defaultResultData(); + Structure.CheckEquation(sVarCalc); + const data = this.getResultData(); - // TODO : Warning si les conditions hydrauliques ne correspondent pas à un écoulement dénoyé - data.Regime = StructureFlowRegime.SUBMERGED; const v = this.prms.Cd.v * Math.min(this.prms.W.v, this.prms.h1.v) * this.prms.L.v * Structure.R2G * Math.sqrt(this.prms.h1.v - this.prms.h2.v); return new Result(v, data); } + + protected getFlowRegime() { + return StructureFlowRegime.SUBMERGED; + } } diff --git a/src/structure/structure_params.ts b/src/structure/structure_params.ts index 5929b5bc684b4804d56edf25b92a05ed895728bf..468fd6a43cb48b9e032dcdb416ea052a0723e533 100644 --- a/src/structure/structure_params.ts +++ b/src/structure/structure_params.ts @@ -38,13 +38,13 @@ export class StructureParams extends ParamsEquation { */ constructor(rQ: number, rZDV: number, rZ1: number, rZ2: number, rW: number = Infinity ) { super(); - this.Q = new ParamDefinition(ComputeNodeType.CondDistri, "Q", ParamDomainValue.POS_NULL, rQ); + this.Q = new ParamDefinition(ComputeNodeType.CondDistri, "Q", ParamDomainValue.ANY, rQ); this.addParamDefinition(this.Q); - this.ZDV = new ParamDefinition(ComputeNodeType.CondDistri, "ZDV", ParamDomainValue.POS_NULL, rZDV); + this.ZDV = new ParamDefinition(ComputeNodeType.CondDistri, "ZDV", ParamDomainValue.ANY, rZDV); this.addParamDefinition(this.ZDV); - this.Z1 = new ParamDefinition(ComputeNodeType.CondDistri, "Z1", ParamDomainValue.POS_NULL, rZ1); + this.Z1 = new ParamDefinition(ComputeNodeType.CondDistri, "Z1", ParamDomainValue.ANY, rZ1); this.addParamDefinition(this.Z1); - this.Z2 = new ParamDefinition(ComputeNodeType.CondDistri, "Z2", ParamDomainValue.POS_NULL, rZ2); + this.Z2 = new ParamDefinition(ComputeNodeType.CondDistri, "Z2", ParamDomainValue.ANY, rZ2); this.addParamDefinition(this.Z2); this.h1 = new ParamDefinition(ComputeNodeType.CondDistri, "h1", ParamDomainValue.POS_NULL, Math.max(0, this.Z1.v - this.ZDV.v)); diff --git a/src/structure/structure_weir_free.ts b/src/structure/structure_weir_free.ts index 4f862723bb97b245245a9775db7402e63d8e88de..444dc37259efab1ba4d2a4305cc5b7273ce756f0 100644 --- a/src/structure/structure_weir_free.ts +++ b/src/structure/structure_weir_free.ts @@ -14,14 +14,19 @@ export class StructureWeirFree extends RectangularStructure { * @param sVarCalc Variable à calculer (doit être "Q") */ public Equation(sVarCalc: string): Result { - super.CheckEquation(sVarCalc); - const data = super.defaultResultData(); + Structure.CheckEquation(sVarCalc); + const data = this.getResultData(); - // TODO : Warning si les conditions hydrauliques ne correspondent pas à un seuil dénoyé - data.Regime = StructureFlowRegime.FREE; - data.Mode = StructureFlowMode.WEIR; const v = this.prms.Cd.v * this.prms.L.v * Structure.R2G * Math.pow(this.prms.h1.v, 1.5); return new Result(v, data); } + + protected getFlowRegime() { + return StructureFlowRegime.FREE; + } + + protected getFlowMode() { + return StructureFlowMode.WEIR; + } } diff --git a/src/util/result.ts b/src/util/result.ts index 2b64d20a6078c2d15e8dc1d2cdd1d7d5b933730c..066a4d72b6c7a8c3a0b5f976804e60658ea53458 100644 --- a/src/util/result.ts +++ b/src/util/result.ts @@ -1,6 +1,6 @@ import { cLog } from "./log"; +import { Message, MessageCode, MessageSeverity } from "./message"; import { ResultElement } from "./resultelement"; -import { MessageSeverity, Message, MessageCode } from "./message"; /** * Résultat global d'un calcul @@ -18,29 +18,31 @@ export class Result { constructor(v?: number | Message | ResultElement, d?: any) { this._globalLog = new cLog(); this._results = []; - if (typeof (v) === "number" || v instanceof Message) + if (typeof (v) === "number" || v instanceof Message) { this._results.push(new ResultElement(v)); - else if (v instanceof ResultElement) + } else if (v instanceof ResultElement) { this._results.push(v); + } - if (d != undefined) { - for (const i in d) { - const re = new ResultElement(); - re.addExtraResult(i, d[i]); - } + if (d !== undefined) { + this.result.extraResults = d; } } /** - * retourne le journal du 1er ResultElement + * retourne le journal de la calculette */ public get globalLog() { return this._globalLog; } - private get result(): ResultElement { - if (this._results.length == 0) + /** + * Retourne le résultat du premier ResultElement + */ + get result(): ResultElement { + if (this._results.length === 0) { throw new Error("appel à la méthode de Result.result() invalide, il n'y a pas au moins un 'ResultElement'"); + } return this._results[0]; } @@ -59,6 +61,13 @@ export class Result { return this.result.vCalc; } + /** + * @return le résultat de calcul du 1er ResultElement + */ + set vCalc(r: number) { + this.result.vCalc = r; + } + /** * retourne le journal du 1er ResultElement */ @@ -75,12 +84,27 @@ export class Result { } /** - * retourne les résultats complémentaires du 1er ResultElement - */ + * retourne les résultats complémentaires du 1er ResultElement + */ get extraResults() { return this.result.extraResults; } + /** + * Extract one ResultElement as a Result + * @param i index of the ResultElement + */ + public extractResult(i: number): Result { + if (i < 0 || i >= this._results.length) { + throw new Error("Result.extractResult index outside [0;" + (this._results.length - 1) + "]"); + } + return new Result(this._results[i]); + } + + /** + * Ajoute un ResultElement au tableau + * @param r ResultElement à ajouter + */ public addResult(r: ResultElement) { this._results.push(r); } @@ -124,7 +148,8 @@ export class Result { default: throw new Error( - "Result.getExtraResult() : il existe plusieurs ResultElement avec un extratresult dont le nom est '" + name + "'", + "Result.getExtraResult() : il existe plusieurs ResultElement avec" + + " un extratresult dont le nom est '" + name + "'", ); } } @@ -182,12 +207,13 @@ export class Result { * @returns true si au moins un message de log comporte un code d'erreur */ public get hasErrorMessages(): boolean { - for (const m of this.log.messages) { + // Test du journal global + for (const m of this._globalLog.messages) { if (m.getSeverity() === MessageSeverity.ERROR) { return true; } } - + // Test des journaux des résultats for (const r of this._results) { if (r.hasErrorMessages) { return true; diff --git a/src/util/resultelement.ts b/src/util/resultelement.ts index 8d58b6f4cbeddd67457517e8ab9868c5818d8c3f..dc301486fdeeabcfc22d9e77a3a37418139c546f 100644 --- a/src/util/resultelement.ts +++ b/src/util/resultelement.ts @@ -34,6 +34,10 @@ export class ResultElement { get vCalc() { return this._vCalc; } + set vCalc(r: number) { + this._vCalc = r; + } + public get log() { return this._log; } @@ -95,10 +99,14 @@ export class ResultElement { * résultats complémentaires */ - public get extraResults() { + get extraResults() { return this._extraResults; } + set extraResults(d: { [key: string]: any }) { + this._extraResults = d; + } + public addExtraResult(name: string, value: any) { this._extraResults[name] = value; } diff --git a/tslint.json b/tslint.json index dfff889c26a98d256d0c7ab79ff413ac5e6e894c..e74a30c408954408442094702dbca063db8137a9 100644 --- a/tslint.json +++ b/tslint.json @@ -4,6 +4,12 @@ "tslint:recommended" ], "jsRules": {}, - "rules": {}, + "rules": { + "variable-name": [true, + "check-format", + "allow-leading-underscore" + ], + "trailing-comma": [false] + }, "rulesDirectory": [] } \ No newline at end of file