From f875f421d4ddedef5b3de972747acf555d4515fe Mon Sep 17 00:00:00 2001 From: "francois.grand" <francois.grand@irstea.fr> Date: Mon, 6 Nov 2017 11:51:20 +0100 Subject: [PATCH] =?UTF-8?q?section=20param=C3=A9tr=C3=A9e=20:=20ajout=20de?= =?UTF-8?q?s=20messages=20dans=20le=20journal=20pour=20la=20non=20converge?= =?UTF-8?q?nce=20des=20calculs=20par=20la=20m=C3=A9thode=20de=20Newton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/section_param_rect_conv_newton.spec.ts | 100 ++++++++++++++++++++ src/section/hauteur.ts | 16 ++-- src/section/newton.ts | 15 ++- src/section/section_type.ts | 62 +++++++++--- src/util/message.ts | 30 ++++++ 5 files changed, 200 insertions(+), 23 deletions(-) create mode 100644 spec/section_param_rect_conv_newton.spec.ts diff --git a/spec/section_param_rect_conv_newton.spec.ts b/spec/section_param_rect_conv_newton.spec.ts new file mode 100644 index 00000000..d0c632e8 --- /dev/null +++ b/spec/section_param_rect_conv_newton.spec.ts @@ -0,0 +1,100 @@ +/// <reference path="../node_modules/@types/jasmine/index.d.ts" /> + +import { Result } from "../src/base"; +import { precDist, equalEpsilon } from "./nubtest"; +import { ParamsSectionRectang, cSnRectang } from "../src/section/section_rectang"; +import { cLog } from "../src/util/log"; +import { MessageCode } from "../src/util/message"; + +let paramSection: ParamsSectionRectang; +let sect: cSnRectang; + +function check(val1: number, val2: number) { + expect(equalEpsilon(val1, val2)).toBeTruthy("expected " + val2 + ", got " + val1); +} + +describe('Section paramétrée rectangulaire : ', () => { + beforeEach(() => { + paramSection = new ParamsSectionRectang(0.8, // tirant d'eau + 2.5, // largeur de fond + 40, // Ks=Strickler + 10, // Q=Débit + 0.001, // If=pente du fond + 1e-10, // précision + 1 // YB= hauteur de berge + ); + + sect = new cSnRectang(new cLog(), paramSection); + sect.newtonMaxIter = 5; + }); + + describe('non convergence de la méthode de Newton :', () => { + it('hauteur critique', () => { + sect.Calc("Yc"); + expect(sect.log.messages.length).toEqual(1); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + }); + + it('hauteur normale', () => { + sect.Calc("Yn"); + expect(sect.log.messages.length).toEqual(2); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + expect(sect.log.messages[1].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HNORMALE); + }); + + it('hauteur normale, pente négative', () => { + sect.prms.If.v = -0.001; + sect.Calc("Yn"); + expect(sect.log.messages.length).toEqual(1); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF); + }); + + it('hauteur fluviale, Y < Yc', () => { + sect.Calc("Yf"); + expect(sect.log.messages.length).toEqual(3); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + expect(sect.log.messages[1].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + expect(sect.log.messages[2].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HFLU); + }); + + it('hauteur fluviale, Y > Yc', () => { + sect.prms.Y.v = 2; + sect.Calc("Yf"); + expect(sect.log.messages.length).toEqual(1); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + }); + + it('hauteur torrentielle, Y < Yc', () => { + sect.Calc("Yt"); + expect(sect.log.messages.length).toEqual(1); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + }); + + it('hauteur torrentielle, Y > Yc', () => { + sect.prms.Y.v = 2; + sect.Calc("Yt"); + expect(sect.log.messages.length).toEqual(2); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + expect(sect.log.messages[1].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR); + }); + + it('hauteur conjuguée, Froude < 1', () => { + sect.prms.Y.v = 2; + sect.Calc("Yco"); + console.log(sect.log.toString()); + expect(sect.log.messages.length).toEqual(3); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + expect(sect.log.messages[1].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR); + expect(sect.log.messages[2].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCONJUG); + }); + + it('hauteur conjuguée, Froude > 1', () => { + sect.Calc("Yco"); + expect(sect.log.messages.length).toEqual(4); + expect(sect.log.messages[0].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + expect(sect.log.messages[1].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + expect(sect.log.messages[2].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HFLU); + expect(sect.log.messages[3].code).toEqual(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCONJUG); + }); + }); +}); diff --git a/src/section/hauteur.ts b/src/section/hauteur.ts index 63d181c1..9a3faa0e 100644 --- a/src/section/hauteur.ts +++ b/src/section/hauteur.ts @@ -15,8 +15,8 @@ export class cHautCritique extends acNewton { * Constructeur de la classe * @param Sn Section sur laquelle on fait le calcul */ - constructor(Sn: acSection, dbg: boolean = false) { - super(Sn.prms, dbg); + constructor(Sn: acSection, maxIter: number, dbg: boolean = false) { + super(Sn.prms, maxIter, dbg); this.Sn = Sn.clone(); } @@ -77,8 +77,8 @@ export class cHautNormale extends acNewton { * Constructeur de la classe * @param oSn Section sur laquelle on fait le calcul */ - constructor(Sn: acSection, dbg: boolean = false) { - super(Sn.prms, dbg); + constructor(Sn: acSection, maxIter: number, dbg: boolean = false) { + super(Sn.prms, maxIter, dbg); this.Sn = Sn; this.Q = Sn.prms.Q.v; this.Ks = Sn.prms.Ks.v; @@ -135,8 +135,8 @@ export class cHautCorrespondante extends acNewton { * Constructeur de la classe * @param oSn Section sur laquelle on fait le calcul */ - constructor(Sn: acSection, dbg: boolean = false) { - super(Sn.prms, dbg); + constructor(Sn: acSection, maxIter: number, dbg: boolean = false) { + super(Sn.prms, maxIter, dbg); this.Y = Sn.prms.Y.v; this.rS2 = Math.pow(Sn.Calc("S"), -2); this.Sn = Sn; @@ -186,8 +186,8 @@ export class cHautConjuguee extends acNewton { * Constructeur de la classe * @param oSn Section sur laquelle on fait le calcul */ - constructor(oSn: acSection, dbg: boolean = false) { - super(oSn.prms, dbg); + constructor(oSn: acSection, maxIter: number, dbg: boolean = false) { + super(oSn.prms, maxIter, dbg); this.rQ2 = Math.pow(oSn.prms.Q.v, 2); this.Sn = oSn; this.rS = oSn.Calc("S"); diff --git a/src/section/newton.ts b/src/section/newton.ts index fd1d0813..f3b76d66 100644 --- a/src/section/newton.ts +++ b/src/section/newton.ts @@ -4,8 +4,8 @@ import { cParamsCanal } from "./section_type" export abstract class acNewton extends Debug { protected rTol: number; protected Dx: number; - private iCpt = 0; - private readonly iCptMax = 50; + private iCpt: number = 0; + private iCptMax: number; private rRelax = 1; /// Coefficient de relaxation private rFnPrec = 0; /// Mémorisation du Fn précédent pour détecter le changement de signe private iOscil = 0; /// Nombre de changement de signe de Delta @@ -14,10 +14,11 @@ export abstract class acNewton extends Debug { * Constructeur de la classe * @param prms Paramètres supplémentaires (Débit, précision...) */ - constructor(prms: cParamsCanal, dbg: boolean = false) { + constructor(prms: cParamsCanal, maxIter: number, dbg: boolean = false) { super(dbg); this.rTol = prms.Prec.v; this.Dx = prms.Prec.v / 10; + this.iCptMax = maxIter; } /** @@ -92,4 +93,12 @@ export abstract class acNewton extends Debug { // Echec de la résolution return undefined; } + + /** + * Pour savoir si le Newton a convergé + * @return true si oui, false sinon + */ + public hasConverged(): boolean { + return this.iCpt < this.iCptMax; + } } \ No newline at end of file diff --git a/src/section/section_type.ts b/src/section/section_type.ts index c898f5d8..4cdf97da 100644 --- a/src/section/section_type.ts +++ b/src/section/section_type.ts @@ -1,5 +1,6 @@ import { XOR } from "../base"; import { cLog } from "../util/log"; +import { MessageCode, Message } from "../util/message"; import { ComputeNodeType, ComputeNode, ParamDefinition, ParamDomainValue, ParamCalculability, ParamsEquation } from "../param"; import { cHautCritique, cHautNormale, cHautCorrespondante, cHautConjuguee } from "./hauteur"; @@ -149,10 +150,9 @@ export abstract class acSection extends ComputeNode { private Calc_old = {}; /// Mémorisation des données hydrauliques pour calcul intermédiaire /** - * Nombre de points nécessaires pour le dessin de la section (hors point de berge) - * Valeur de 1 par défaut pour les sections rectangulaires et trapézoïdales + * itérations max pour Newton */ - protected nbDessinPoints = 1; + private _newtonMaxIter: number = 50; /** * Construction de la classe. @@ -188,6 +188,14 @@ export abstract class acSection extends ComputeNode { return Object.create(this); } + public set newtonMaxIter(n: number) { + this._newtonMaxIter = n; + } + + public get log(): cLog { + return this.oLog; + } + /** * Efface toutes les données calculées pour forcer le recalcul * @param bGeo Réinitialise les données de géométrie aussi @@ -500,8 +508,12 @@ export abstract class acSection extends ComputeNode { * @return tirant d'eau critique */ private Calc_Yc(): number { - var hautCritique = new cHautCritique(this, this.DBG); + let hautCritique = new cHautCritique(this, this._newtonMaxIter, this.DBG); this.HautCritique = hautCritique.Newton(this.prms.YB.v); + if (this.HautCritique == undefined || !hautCritique.hasConverged()) { + let m = new Message(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE); + this.oLog.add(m); + } return this.HautCritique; } @@ -511,11 +523,20 @@ export abstract class acSection extends ComputeNode { */ private Calc_Yn(): number { this.debug("in calc_Yn"); - if (this.prms.If.v <= 0) + if (this.prms.If.v <= 0) { + let m = new Message(MessageCode.ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF); + this.oLog.add(m); return undefined; + } + + let oHautNormale = new cHautNormale(this, this._newtonMaxIter, this.DBG); + let res = oHautNormale.Newton(this.CalcGeo("Yc")); + if (res == undefined || !oHautNormale.hasConverged()) { + let m = new Message(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HNORMALE); + this.oLog.add(m); + } - var oHautNormale = new cHautNormale(this, this.DBG); - return oHautNormale.Newton(this.CalcGeo("Yc")); + return res; } /** @@ -526,8 +547,14 @@ export abstract class acSection extends ComputeNode { if (this.prms.Y.v > this.CalcGeo("Yc")) return this.prms.Y.v; - var oHautCorrespondante = new cHautCorrespondante(this, this.DBG); - return oHautCorrespondante.Newton(this.Calc("Yc") * 2); + var oHautCorrespondante = new cHautCorrespondante(this, this._newtonMaxIter, this.DBG); + let res = oHautCorrespondante.Newton(this.Calc("Yc") * 2); + if (res == undefined || !oHautCorrespondante.hasConverged()) { + let m = new Message(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HFLU); + this.oLog.add(m); + } + + return res; } /** @@ -538,8 +565,14 @@ export abstract class acSection extends ComputeNode { if (this.prms.Y.v < this.CalcGeo("Yc")) return this.prms.Y.v; - var oHautCorrespondante = new cHautCorrespondante(this, this.DBG); - return oHautCorrespondante.Newton(this.CalcGeo("Yc") / 2); + var oHautCorrespondante = new cHautCorrespondante(this, this._newtonMaxIter, this.DBG); + let res = oHautCorrespondante.Newton(this.CalcGeo("Yc") / 2); + if (res == undefined || !oHautCorrespondante.hasConverged()) { + let m = new Message(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR); + this.oLog.add(m); + } + + return res; } /** @@ -548,7 +581,7 @@ export abstract class acSection extends ComputeNode { */ private Calc_Yco(): number { this.Swap(true); - var oHautConj = new cHautConjuguee(this, this.DBG); + var oHautConj = new cHautConjuguee(this, this._newtonMaxIter, this.DBG); // Choisir une valeur initiale du bon côté de la courbe if (this.Calc("Fr") < 1) { // Ecoulement fluvial, on cherche la conjuguée à partir du tirant d'eau torrentiel @@ -559,6 +592,11 @@ export abstract class acSection extends ComputeNode { Y0 = this.Calc("Yf"); } let Yco = oHautConj.Newton(Y0); + if (Yco == undefined || !oHautConj.hasConverged()) { + let m = new Message(MessageCode.ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCONJUG); + this.oLog.add(m); + } + this.Swap(false); return Yco; } diff --git a/src/util/message.ts b/src/util/message.ts index d34afb18..a4ee532a 100644 --- a/src/util/message.ts +++ b/src/util/message.ts @@ -157,6 +157,36 @@ export enum MessageCode { * courbe de remous : Condition limite amont > Hauteur critique : pas de calcul possible depuis l'amont */ ERROR_REMOUS_PAS_CALCUL_DEPUIS_AMONT = -510, + + /** + * section : Non convergence du calcul de la hauteur critique (Méthode de Newton) + */ + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCRITIQUE = -600, + + /** + * section : Non convergence du calcul de la hauteur normale (Méthode de Newton) + */ + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HNORMALE = -601, + + /** + * section : Non convergence du calcul de la hauteur conjuguée (Méthode de Newton) + */ + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HCONJUG = -602, + + /** + * section : Non convergence du calcul de la hauteur correspondante (Méthode de Newton) pour le calcul de la hauteur fluviale + */ + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HFLU = -603, + + /** + * section : Non convergence du calcul de la hauteur correspondante (Méthode de Newton) pour le calcul de la hauteur torrentielle + */ + ERROR_SECTION_NON_CONVERGENCE_NEWTON_HTOR = -604, + + /** + * section : La pente est négative ou nulle, la hauteur normale est infinie + */ + ERROR_SECTION_PENTE_NEG_NULLE_HNORMALE_INF = -605, } /** -- GitLab