diff --git a/spec/param/param_var.spec.ts b/spec/param/param_var.spec.ts index a431107784bfb617930bf1043ab4329c546cee90..99e803834ada60b30d87ead40225711489200906 100644 --- a/spec/param/param_var.spec.ts +++ b/spec/param/param_var.spec.ts @@ -20,7 +20,7 @@ describe("when a parameter is varying, ", () => { expect(res.resultElements[4].log.messages.length).toBe(1); // check global log expect(res.globalLog.messages.length).toBe(1); - expect(res.globalLog.messages[0].code).toBe(MessageCode.ERROR_ABSTRACT); + expect(res.globalLog.messages[0].code).toBe(MessageCode.WARNING_ERRORS_ABSTRACT); expect(res.globalLog.messages[0].extraVar.nb).toBe("3"); // number is a string here }); }); diff --git a/spec/solveur/solveur.spec.ts b/spec/solveur/solveur.spec.ts index 680d44a9207675b32e22c77f4c09475e69fb94a6..639f21f328fcab4b4c6f3e8a9433b51b12357a75 100644 --- a/spec/solveur/solveur.spec.ts +++ b/spec/solveur/solveur.spec.ts @@ -19,9 +19,7 @@ describe("solveur multi-modules", () => { const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); pp.calculatedParam = pp.prms.PV; Session.getInstance().clear(); - Session.getInstance().registerNub(pc); - Session.getInstance().registerNub(pn); - Session.getInstance().registerNub(pp); + Session.getInstance().registerNubs([ pc, pn, pp ]); pn.prms.DHT.defineReference(pc, "DH"); pp.prms.DH.defineReference(pn, "DH"); // solveur @@ -42,9 +40,7 @@ describe("solveur multi-modules", () => { const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); pp.calculatedParam = pp.prms.PV; Session.getInstance().clear(); - Session.getInstance().registerNub(pc); - Session.getInstance().registerNub(pn); - Session.getInstance().registerNub(pp); + Session.getInstance().registerNubs([ pc, pn, pp ]); pn.prms.DHT.defineReference(pc, "DH"); pp.prms.DH.defineReference(pn, "DH"); // solveur @@ -64,9 +60,7 @@ describe("solveur multi-modules", () => { const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); pp.calculatedParam = pp.prms.PV; Session.getInstance().clear(); - Session.getInstance().registerNub(pc); - Session.getInstance().registerNub(pn); - Session.getInstance().registerNub(pp); + Session.getInstance().registerNubs([ pc, pn, pp ]); // solveur const s = new Solveur(new SolveurParams(324.907), false); s.searchedParameter = pc.prms.Z1; @@ -84,9 +78,7 @@ describe("solveur multi-modules", () => { const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); pp.calculatedParam = pp.prms.PV; Session.getInstance().clear(); - Session.getInstance().registerNub(pc); - Session.getInstance().registerNub(pn); - Session.getInstance().registerNub(pp); + Session.getInstance().registerNubs([ pc, pn, pp ]); pn.prms.DHT.defineReference(pc, "DH"); pp.prms.DH.defineReference(pn, "DH"); // solveur @@ -110,9 +102,7 @@ describe("solveur multi-modules", () => { const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); pp.calculatedParam = pp.prms.PV; Session.getInstance().clear(); - Session.getInstance().registerNub(pc); - Session.getInstance().registerNub(pn); - Session.getInstance().registerNub(pp); + Session.getInstance().registerNubs([ pc, pn, pp ]); pn.prms.DHT.defineReference(pc, "DH"); pp.prms.DH.defineReference(pn, "DH"); // solveur @@ -137,9 +127,7 @@ describe("solveur multi-modules", () => { const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); pp.calculatedParam = pp.prms.PV; Session.getInstance().clear(); - Session.getInstance().registerNub(pc); - Session.getInstance().registerNub(pn); - Session.getInstance().registerNub(pp); + Session.getInstance().registerNubs([ pc, pn, pp ]); pn.prms.DHT.defineReference(pc, "DH"); pp.prms.DH.defineReference(pn, "DH"); // check lists @@ -153,4 +141,67 @@ describe("solveur multi-modules", () => { expect(searchedParamsPn.map((p) => p.symbol)).toEqual([ "Z1", "Z2" ]); }); + it("test 7: suppression des extraResult", () => { + // contexte + const pc = new PabChute(new PabChuteParams(2, 0.5, 666)); + pc.calculatedParam = pc.prms.DH; + const pn = new PabNombre(new PabNombreParams(666, 10, 666)); + pn.calculatedParam = pn.prms.DH; + Session.getInstance().clear(); + Session.getInstance().registerNubs([ pc, pn ]); + pn.prms.DHT.defineReference(pc, "DH"); + // solveur + const s = new Solveur(new SolveurParams(12.5), false); + s.nubToCalculate = pn; + s.searchedParameter = pc.prms.Z1; + const res = s.CalcSerie(); + expect(res.vCalc).toBeCloseTo(125.5, 1); + expect(Object.keys(res.resultElement.values).length).toBe(1); + expect(res.resultElement.vCalcSymbol).toBe("X"); + }); + + it("test 8: vCalcSymbol du Nub calculé", () => { + // contexte + const pc = new PabChute(new PabChuteParams(2, 0.5, 666)); + pc.calculatedParam = pc.prms.DH; + const pn = new PabNombre(new PabNombreParams(666, 10, 666)); + pn.calculatedParam = pn.prms.DH; + Session.getInstance().clear(); + Session.getInstance().registerNubs([ pc, pn ]); + pn.prms.DHT.defineReference(pc, "DH"); + // solveur + const s = new Solveur(new SolveurParams(12.5), false); + s.nubToCalculate = pn; + s.searchedParameter = pc.prms.Z1; + const res = s.CalcSerie(); + expect(res.resultElement.vCalcSymbol).toBe("X"); + expect(pn.result.resultElement.vCalcSymbol).toBe("DH"); + }); + + // @TODO + xit("test 9: remonter les logs en cas d'erreur dans la chaîne de calcul", () => { + // contexte + const pc = new PabChute(new PabChuteParams(2, 0.5, 666)); + pc.calculatedParam = pc.prms.DH; + const pn = new PabNombre(new PabNombreParams(666, 10, 666)); + pn.calculatedParam = pn.prms.DH; + const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); + pp.calculatedParam = pp.prms.PV; + Session.getInstance().clear(); + Session.getInstance().registerNubs([ pc, pn, pp ]); + pn.prms.DHT.defineReference(pc, "DH"); + pp.prms.DH.defineReference(pn, "DH"); + // solveur + const s = new Solveur(new SolveurParams(1), false); + s.nubToCalculate = pp; + s.searchedParameter = pc.prms.Z1; + const res = s.CalcSerie(); + expect(res.vCalc).toBeCloseTo(2.156, 3); + }); + + // @TODO si la dicho ne converge pas, indiquer la dernière valeur testée + + // @TODO chargement de session avec Solveur (2 passes) + + // @TODO calcul en chaîne: si une étape foire, interrompre et faire remonter l'erreur }); diff --git a/spec/value_ref/value_ref_chained_computation.spec.ts b/spec/value_ref/value_ref_chained_computation.spec.ts index 21a320f1ccfdb07622023fa92b75da27f7ec4532..d5fee01c636d19746f5b2244f2e577ffa1d2ef33 100644 --- a/spec/value_ref/value_ref_chained_computation.spec.ts +++ b/spec/value_ref/value_ref_chained_computation.spec.ts @@ -1,5 +1,5 @@ import { Cloisons, CreateStructure, cSnRectang, LoiDebit, - SectionParametree, Session } from "../../src/index"; + MessageCode, SectionParametree, Session } from "../../src/index"; import { CloisonsParams } from "../../src/pab/cloisons_params"; import { PabChute } from "../../src/pab/pab_chute"; import { PabChuteParams } from "../../src/pab/pab_chute_params"; @@ -136,3 +136,76 @@ describe("chained computation of linked Nubs : ", () => { expect(r1).not.toBe(r2); }); }); + +describe("failure in calc chain", () => { + + it("calc chain with no failure", () => { + const pc = new PabChute(new PabChuteParams(1, 0.5, 666)); + pc.calculatedParam = pc.prms.DH; + const pn = new PabNombre(new PabNombreParams(666, 10, 666)); + pn.calculatedParam = pn.prms.DH; + const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); + pp.calculatedParam = pp.prms.PV; + Session.getInstance().clear(); + Session.getInstance().registerNubs([ pc, pn, pp ]); + pn.prms.DHT.defineReference(pc, "DH"); + pp.prms.DH.defineReference(pn, "DH"); + const res = pp.CalcSerie(); + expect(res.globalLog.messages.length).toBe(0); + expect(res.resultElement.log.messages.length).toBe(0); + }); + + it("calc chain with a failure", () => { + const pc = new PabChute(new PabChuteParams(1, 0.5, 666)); + pc.calculatedParam = pc.prms.DH; + const pn = new PabNombre(new PabNombreParams(666, 10, 666)); + pn.calculatedParam = pn.prms.DH; + const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); + pp.calculatedParam = pp.prms.PV; + Session.getInstance().clear(); + Session.getInstance().registerNubs([ pc, pn, pp ]); + pn.prms.DHT.defineReference(pc, "DH"); + pp.prms.DH.defineReference(pn, "DH"); + pc.prms.Z1.singleValue = 0.1; // Z1 < Z2 : error + const res = pp.CalcSerie(); + expect(res.globalLog.messages.length).toBe(1); + expect(res.globalLog.messages[0].code).toBe(MessageCode.ERROR_IN_CALC_CHAIN); + }); + + it("calc chain with variation, some failures", () => { + const pc = new PabChute(new PabChuteParams(1, 0.5, 666)); + pc.calculatedParam = pc.prms.DH; + const pn = new PabNombre(new PabNombreParams(666, 10, 666)); + pn.calculatedParam = pn.prms.DH; + const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); + pp.calculatedParam = pp.prms.PV; + Session.getInstance().clear(); + Session.getInstance().registerNubs([ pc, pn, pp ]); + pn.prms.DHT.defineReference(pc, "DH"); + pp.prms.DH.defineReference(pn, "DH"); + pc.prms.Z1.singleValue = 1; + pc.prms.Z2.setValues(0.4, 1.9, 0.5); // 2 first steps succeed, 2 last steps fail (Z1 < Z2) + const res = pp.CalcSerie(); + expect(res.globalLog.messages.length).toBe(1); + expect(res.globalLog.messages[0].code).toBe(MessageCode.WARNING_ERROR_IN_CALC_CHAIN_STEPS); + }); + + it("calc chain with variation, only failures", () => { + const pc = new PabChute(new PabChuteParams(1, 0.5, 666)); + pc.calculatedParam = pc.prms.DH; + const pn = new PabNombre(new PabNombreParams(666, 10, 666)); + pn.calculatedParam = pn.prms.DH; + const pp = new PabPuissance(new PabPuissanceParams(666, 0.1, 0.5, 666)); + pp.calculatedParam = pp.prms.PV; + Session.getInstance().clear(); + Session.getInstance().registerNubs([ pc, pn, pp ]); + pn.prms.DHT.defineReference(pc, "DH"); + pp.prms.DH.defineReference(pn, "DH"); + pc.prms.Z1.singleValue = 1; + pc.prms.Z2.setValues(1.4, 2.9, 0.5); + const res = pp.CalcSerie(); + expect(res.globalLog.messages.length).toBe(1); + expect(res.globalLog.messages[0].code).toBe(MessageCode.ERROR_IN_CALC_CHAIN); + }); + +}); diff --git a/src/nub.ts b/src/nub.ts index e644b1af74281716166b8d946131822d08e992ff..07ff8a76a33184807389226ef2436a0b17b3ced2 100644 --- a/src/nub.ts +++ b/src/nub.ts @@ -367,18 +367,44 @@ export abstract class Nub extends ComputeNode implements IObservable { /** * Calculates required Nubs so that all input data is available; * uses 50% of the progress + * @returns true if everything went OK, false otherwise */ - public triggerChainCalculation() { + public triggerChainCalculation(): { ok: boolean, message: Message } { const requiredNubs1stLevel = this.getRequiredNubs(); if (requiredNubs1stLevel.length > 0) { const progressStep = Nub.progressPercentageAccordedToChainCalculation / requiredNubs1stLevel.length; for (const rn of requiredNubs1stLevel) { - rn.CalcSerie(); + const r = rn.CalcSerie(); + if (r.hasGlobalError() || r.hasOnlyErrors) { + // something has failed in chain + return { + ok: false, + message: new Message(MessageCode.ERROR_IN_CALC_CHAIN) + }; + } else if ( + this.resultHasMultipleValues + && ( + r.hasErrorMessages() // some steps failed + // or upstream Nub has already triggered a warning message; pass it on + || r.globalLog.contains(MessageCode.WARNING_ERROR_IN_CALC_CHAIN_STEPS) + ) + ) { + // if a parameter varies, errors might have occurred for + // certain steps (but not all steps) + return { + ok: true, + message: new Message(MessageCode.WARNING_ERROR_IN_CALC_CHAIN_STEPS) + }; + } this.progress += progressStep; } // round progress to accorded percentage this.progress = Nub.progressPercentageAccordedToChainCalculation; } + return { + ok: true, + message: undefined + }; } /** @@ -392,9 +418,21 @@ export abstract class Nub extends ComputeNode implements IObservable { const variated: Array<{ param: ParamDefinition, values: ParamValues }> = []; // prepare calculation + let extraLogMessage: Message; // potential chain calculation warning to add to result at the end this.progress = 0; this.resetResult(); - this.triggerChainCalculation(); + const ccRes = this.triggerChainCalculation(); + if (ccRes.ok) { + // might still have a warning log + if (ccRes.message !== undefined) { + extraLogMessage = ccRes.message; + } + } else { + // something went wrong in the chain + this._result = new Result(undefined, this); + this._result.globalLog.add(ccRes.message); + return this._result; + } this.copySingleValuesToSandboxValues(); // check which values are variating, if any @@ -492,10 +530,14 @@ export abstract class Nub extends ComputeNode implements IObservable { } // String()ify numbers below, to avoid decimals formatting on screen (ex: "3.000 errors encoutered...") if (errors > 0) { - this._result.globalLog.add(new Message(MessageCode.ERROR_ABSTRACT, { nb: String(errors) })); + this._result.globalLog.add( + new Message(MessageCode.WARNING_ERRORS_ABSTRACT, { nb: String(errors) }) + ); } if (warnings > 0) { - this._result.globalLog.add(new Message(MessageCode.WARNING_ABSTRACT, { nb: String(warnings) })); + this._result.globalLog.add( + new Message(MessageCode.WARNING_WARNINGS_ABSTRACT, { nb: String(warnings) }) + ); } } @@ -506,6 +548,10 @@ export abstract class Nub extends ComputeNode implements IObservable { this.notifyResultUpdated(); + if (extraLogMessage !== undefined) { + this._result.globalLog.add(extraLogMessage); + } + return this._result; } diff --git a/src/session.ts b/src/session.ts index 9bb601eab6575f84aa884ddf933dbd55922a619b..50cb9f90c0328cf744bc65d79dcd53962e01eb2b 100644 --- a/src/session.ts +++ b/src/session.ts @@ -151,6 +151,15 @@ export class Session { this._nubs.push(n); } + /** + * Adds many existing Nubs to the session + */ + public registerNubs(nubs: Nub[]) { + for (const n of nubs) { + this.registerNub(n); + } + } + /** * Removes all Nubs from the Session */ diff --git a/src/util/log.ts b/src/util/log.ts index 67cd8b4a9d411865e72f26346e5a15521c475b24..35eb67d36c71a4eb998c24d18dfdf984240336b0 100644 --- a/src/util/log.ts +++ b/src/util/log.ts @@ -1,4 +1,4 @@ -import { Message } from "./message"; +import { Message, MessageCode } from "./message"; // tslint:disable-next-line:class-name export class cLog { @@ -43,4 +43,17 @@ export class cLog { public toString(): string { return this._messages.join("\n"); } + + /** + * @param mc message code you're looking for + * @returns true if log contains at least one occurrence of given message code + */ + public contains(mc: MessageCode): boolean { + for (const m of this.messages) { + if (m.code === mc) { + return true; + } + } + return false; + } } diff --git a/src/util/message.ts b/src/util/message.ts index 3c128937b7457859ae3de325ae931a61e5785ebb..4495f7b183714ec984261b551e39da5d30978ad5 100644 --- a/src/util/message.ts +++ b/src/util/message.ts @@ -5,7 +5,7 @@ export enum MessageCode { ERROR_OK, /** abstract showing number of error messages encountered in an iterative calculation */ - ERROR_ABSTRACT, + WARNING_ERRORS_ABSTRACT, /** calculation of Z1 in Fluvial regime has failed (upstream abscissa not present in results) */ ERROR_BIEF_Z1_CALC_FAILED, @@ -57,6 +57,16 @@ export enum MessageCode { */ ERROR_ELEVATION_ZI_LOWER_THAN_Z2, + /** + * Something failed when calculating upstream Nubs + */ + ERROR_IN_CALC_CHAIN, + + /** + * Something failed in certain steps (but not all), when calculating upstream Nubs with varying parameter + */ + WARNING_ERROR_IN_CALC_CHAIN_STEPS, + /** * les bornes de l'intervalle d'un ParamDomain sont incorrectes */ @@ -288,7 +298,7 @@ export enum MessageCode { ERROR_STRUCTURE_Z_EGAUX_Q_NON_NUL, /** abstract showing number of warning messages encountered in an iterative calculation */ - WARNING_ABSTRACT, + WARNING_WARNINGS_ABSTRACT, /** La cote de fond aval est plus élevée que la code de l'eau aval */ WARNING_DOWNSTREAM_BOTTOM_HIGHER_THAN_WATER, diff --git a/src/util/result.ts b/src/util/result.ts index 83db9cbb484bf2af1c87ae0f28d10195720efe36..773328b38388034045dd4e3c210eac801f1ddd97 100644 --- a/src/util/result.ts +++ b/src/util/result.ts @@ -172,16 +172,26 @@ export class Result extends JalhydObject { } /** - * @returns true if at least one of the log messages (general log or result elements logs) - * has an error-level message, i.e. true if at least one calculation has failed + * @returns true if globalLog has at least one message with level ERROR */ - public hasErrorMessages(): boolean { - // global log + public hasGlobalError(): boolean { for (const m of this._globalLog.messages) { if (m.getSeverity() === MessageSeverity.ERROR) { return true; } } + return false; + } + + /** + * @returns true if at least one of the log messages (general log or result elements logs) + * has an error-level message, i.e. true if at least one calculation has failed + */ + public hasErrorMessages(): boolean { + // global log + if (this.hasGlobalError()) { + return true; + } // result elements logs for (const r of this._resultElements) { if (r.hasErrorMessages()) {