diff --git a/src/base.ts b/src/base.ts index 584799be05a883708bd49ae6f7c8c69cd4acb12e..cb9e20eb161e535ef2df116d24937c7cb4dfad5d 100644 --- a/src/base.ts +++ b/src/base.ts @@ -4,8 +4,10 @@ * Résultat de calcul comprenant la valeur du résultat et des calculs annexes (flag, calculs intermédiaires...) */ export class Result { - public varCalc: number; - public extraVar: {}; /** @todo Comment définit-on un objet dont on ne connait pas le nom des clés mais dont on connait le type ? */ + /** Valeur calculée */ + public vCalc: number; + /** Variables intermédiaires, flags d'erreur */ + public extraVar: {}; } /** @@ -27,17 +29,38 @@ export interface IParametres { } +/** + * Gestion des messages de debogage dans la console + * @note Etendre cette classe pour toutes les classes à debugguer + * Dans le constructeur, utiliser super(true) pour debugger la classe, super(false) sinon. + */ +export abstract class Debug { + /** + * @param DBG Flag de débuggage + */ + constructor(private DBG: boolean) {} + + /** + * Affiche un message dans la console si le flag this.DBG est à true + * @param s Message à afficher dans la console + */ + protected debug(s: any) { + if(this.DBG) console.log(s); + } +} + /** * Classe abstraite de Noeud de calcul : classe de base pour tous les calculs */ -export abstract class Nub { +export abstract class Nub extends Debug { /// Nom des variables calculées par la méthode Equation private _varsEq: string[]; public v: IParametres; constructor(parametres: IParametres) { + super(false); this.v = parametres; } diff --git a/src/dichotomie.ts b/src/dichotomie.ts new file mode 100644 index 0000000000000000000000000000000000000000..f7fd95c6dc0bc55f579ecd64ad82a09fec311615 --- /dev/null +++ b/src/dichotomie.ts @@ -0,0 +1,159 @@ +import { Debug, Nub, Result } from "base"; + +export class Dichotomie extends Debug { + /** Pas de parcours de l'intervalle pour initialisation dichotomie */ + readonly IDEFINT = 100; + /** Nombre d'itérations maximum de la dichotomie */ + readonly IDICMAX = 100; + + + /** + * Construction de la classe. + * @param nub Noeud de calcul contenant la méthode de calcul Equation + * @param sVarCalc Nom de la variable à calculer + */ + constructor(private nub: Nub, private sVarCalc: string) { + super(false); + } + + /** Valeur inconnue à rechercher */ + get vX() { + return this.nub.v[this.sVarCalc]; + } + + /** Valeur inconnue à rechercher */ + set vX(vCalc) { + this.nub.v[this.sVarCalc] = vCalc; + } + + /** + * Méthode simulant l'opérateur booléen xor + * @see http://www.howtocreate.co.uk/xor.html + */ + XOR(a, b) { + return (a || b) && !(a && b); + } + + + /** + * Calcul de l'équation analytique. + * @note Wrapper vers this.nub.Equation pour simplifier le code. + * On utilise la première variable du tableau des variables pourvant être calculées analytiquement + * Il faudra s'assurer que cette première variable correspond à la méthode de calcul la plus rapide + */ + private Calcul() { + /** + * @note + */ + return this.nub.Equation(this.nub.sVarsEq[0]); + } + + + /** + * Calcul à l'ouvrage + * @param sVarCalc Nom de la variable à calculer + * @param rTarget Valeur cible + * @param rtol Précision attendue du résultat + * @param rInit Valeur initiale de l'inconnue à rechercher + */ + Dichotomie(rTarget: number, rTol: number, rInit: number): Result { + let res: Result + + let XminInit = 1E-8; + this.vX = XminInit; + let v1: number = this.Calcul().vCalc; + + let XmaxInit: number = Math.max(1, rInit) * 100; + this.vX = XmaxInit; + let v2 = this.Calcul().vCalc; + + let DX = (XmaxInit - XminInit) / this.IDEFINT; + let nIterMax = Math.floor(Math.max(XmaxInit - rInit, rInit - XminInit) / DX + 1); + let Xmin = rInit; + let Xmax = rInit; + let X1 = rInit; + let X2 = rInit; + this.debug("rInit: " + rInit); + this.vX = rInit; + let v = this.Calcul().vCalc; + + v1 = v; + v2 = v; + this.debug(nIterMax); + //** @todo : Chercher en dehors de l'intervalle en le décalant à droite ou à gauche en fonction de la valeur */ + let nIter: number; + for (nIter = 1; nIter < nIterMax; nIter++) { + //Ouverture de l'intervalle des deux côtés : à droite puis à gauche + Xmax = Xmax + DX; + if (this.XOR(Xmax > XmaxInit, DX <= 0)) Xmax = XmaxInit; + this.vX = Xmax; + v = this.Calcul().vCalc; + if (this.XOR(v1 < v2, v <= v2)) { + v2 = v; + X2 = Xmax; + } + if (this.XOR(v1 < v2, v >= v1)) { + v1 = v; + X1 = Xmax; + } + Xmin = Xmin - DX; + if (this.XOR(Xmin < XminInit, DX <= 0)) { + Xmin = XminInit; + } + + this.vX = Xmin; + v = this.Calcul().vCalc; + if (this.XOR(v1 < v2, v <= v2)) { + v2 = v; + X2 = Xmin; + } + if (this.XOR(v1 < v2, v >= v1)) { + v1 = v; + X1 = Xmin; + } + if (this.XOR(rTarget > v1, rTarget >= v2)) { break; } + } + if (nIter >= this.IDEFINT) { + this.debug("in if"); + // Pas d'intervalle trouvé avec au moins une solution + if (v2 < rTarget && v1 < rTarget) { + // Cote de l'eau trop basse pour passer le débit il faut ouvrir un autre ouvrage + this.vX = XmaxInit; + } + else { + // Cote de l'eau trop grande il faut fermer l'ouvrage + this.vX = XminInit; + } + res = this.Calcul(); + res.extraVar["flag"] = -1; // la valeur cible n'est pas dans l'intervalle de recherche + return res; + } + else { + // Dichotomie + this.debug("in dicho"); + let X = rInit; + for (nIter = 1; nIter <= this.IDICMAX; nIter++) { + this.vX = X; + v = this.Calcul().vCalc; + if (rTarget != 0 && Math.abs(X1 - X2) <= rTol) { break; } + if (this.XOR(rTarget < v, v1 <= v2)) { + // rTarget < IQ et v(X1) > v(X2) ou pareil en inversant les inégalités + X1 = this.vX; + } + else { + // rTarget < IQ et v(X1) < v(X2) ou pareil en inversant les inégalités + X2 = this.vX; + } + X = (X2 + X1) * 0.5; + this.debug((X)); + } + if (nIter == this.IDICMAX) { + res = this.Calcul(); + res.extraVar["flag"] = -1; // la valeur cible n'est pas dans l'intervalle de recherche + return res; + } + } + res.vCalc = v + return ; + } +} \ No newline at end of file diff --git a/src/lechaptcalmon.ts b/src/lechaptcalmon.ts index 79bdda2cb97318e4eb7dd01c01464a497710458f..17dd57bcdfe8dacccc738752ae2f67580452b60e 100644 --- a/src/lechaptcalmon.ts +++ b/src/lechaptcalmon.ts @@ -36,16 +36,16 @@ class LechaptCalmon extends Nub { switch (sVarCalc) { case "Q": - res.varCalc = Math.pow((((this.v.J * Math.pow(this.v.D, this.v.N)) / this.v.L) * (1000 / this.v.Lg)), 1 / this.v.M); + res.vCalc = Math.pow((((this.v.J * Math.pow(this.v.D, this.v.N)) / this.v.L) * (1000 / this.v.Lg)), 1 / this.v.M); break; case "D": - res.varCalc = Math.pow((((this.v.L * Math.pow(this.v.Q, this.v.M)) / this.v.J) * (this.v.Lg / 1000)), 1 / this.v.N); - break; + res.vCalc = Math.pow((((this.v.L * Math.pow(this.v.Q, this.v.M)) / this.v.J) * (this.v.Lg / 1000)), 1 / this.v.N); + break case "J": - res.varCalc = ((this.v.L * Math.pow(this.v.Q, this.v.M)) / Math.pow(this.v.D, this.v.N)) * (this.v.Lg / 1000); + res.vCalc = ((this.v.L * Math.pow(this.v.Q, this.v.M)) / Math.pow(this.v.D, this.v.N)) * (this.v.Lg / 1000); break; case "Lg": - res.varCalc = ((this.v.J * Math.pow(this.v.D, this.v.N)) / (this.v.L * Math.pow(this.v.Q, this.v.M))) * 1000; + res.vCalc = ((this.v.J * Math.pow(this.v.D, this.v.N)) / (this.v.L * Math.pow(this.v.Q, this.v.M))) * 1000; } return res;