diff --git a/package-lock.json b/package-lock.json index 40a0a88844caec2751355edbfe23225fea4cb738..68ecfc131657005f9145c7a754a91b8c8669b22b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -110,15 +110,15 @@ "dev": true }, "@types/jasmine": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.13.tgz", - "integrity": "sha512-bVSrTEWdCNH2RHN+E0QlEr4pGPMRA6puKOmL/X13ZeZmUS0q12ZR1rkB9PVvJSX0zi/OXrMDNvUai+PC380+rQ==", + "version": "3.5.14", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.14.tgz", + "integrity": "sha512-Fkgk536sHPqcOtd+Ow+WiUNuk0TSo/BntKkF8wSvcd6M2FvPjeXcUE6Oz/bwDZiUZEaXLslAgw00Q94Pnx6T4w==", "dev": true }, "@types/node": { - "version": "14.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.0.tgz", - "integrity": "sha512-mikldZQitV94akrc4sCcSjtJfsTKt4p+e/s0AGscVA6XArQ9kFclP+ZiYUMnq987rc6QlYxXv/EivqlfSLxpKA==", + "version": "14.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz", + "integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==", "dev": true }, "ansi-regex": { @@ -715,6 +715,29 @@ "is-glob": "^4.0.0", "merge2": "^1.2.3", "micromatch": "^3.1.10" + }, + "dependencies": { + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + } } }, "fill-range": { @@ -833,27 +856,6 @@ "path-is-absolute": "^1.0.0" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, "glob-to-regexp": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", @@ -935,9 +937,9 @@ } }, "highlight.js": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.2.tgz", - "integrity": "sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.0.tgz", + "integrity": "sha512-OryzPiqqNCfO/wtFo619W+nPYALM6u7iCQkum4bqRmmlcTikOkmlL06i009QelynBPAlNByTQU6cBB2cOBQtCw==", "dev": true }, "hosted-git-info": { @@ -2456,42 +2458,51 @@ } }, "typedoc": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.18.0.tgz", - "integrity": "sha512-UgDQwapCGQCCdYhEQzQ+kGutmcedklilgUGf62Vw6RdI29u6FcfAXFQfRTiJEbf16aK3YnkB20ctQK1JusCRbA==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.19.1.tgz", + "integrity": "sha512-EqZpRJQUnkwHA1yBhaDExEXUZIiWKddkrDXhRcfUzpnu6pizxNmVTw5IZ3mu682Noa4zQCniE0YNjaAwHQodrA==", "dev": true, "requires": { "fs-extra": "^9.0.1", "handlebars": "^4.7.6", "highlight.js": "^10.0.0", - "lodash": "^4.17.15", - "lunr": "^2.3.8", + "lodash": "^4.17.20", + "lunr": "^2.3.9", "marked": "^1.1.1", "minimatch": "^3.0.0", "progress": "^2.0.3", + "semver": "^7.3.2", "shelljs": "^0.8.4", - "typedoc-default-themes": "^0.10.2" + "typedoc-default-themes": "^0.11.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } } }, "typedoc-default-themes": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.10.2.tgz", - "integrity": "sha512-zo09yRj+xwLFE3hyhJeVHWRSPuKEIAsFK5r2u47KL/HBKqpwdUSanoaz5L34IKiSATFrjG5ywmIu98hPVMfxZg==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.11.1.tgz", + "integrity": "sha512-1yl8pbhjrLywqGJx9TfT+wzP+ntudPYjgJdpCj+s5ed2etBkqZPOCBMKwpaN9o6pdoFQF195PggqWTLVEkaRQQ==", "dev": true, "requires": { - "lunr": "^2.3.8" + "lunr": "^2.3.9" } }, "typescript": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz", - "integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==", + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, "uglify-js": { - "version": "3.10.2", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.2.tgz", - "integrity": "sha512-GXCYNwqoo0MbLARghYjxVBxDCnU0tLqN7IPLdHHbibCb1NI5zBkU2EPcy/GaVxc0BtTjqyGXJCINe6JMR2Dpow==", + "version": "3.10.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.4.tgz", + "integrity": "sha512-kBFT3U4Dcj4/pJ52vfjCSfyLyvG9VYYuGYPmrPvAxRw/i7xHiT4VvCev+uiEMcEEiu6UNB6KgWmGtSUYIWScbw==", "dev": true, "optional": true }, @@ -2638,9 +2649,9 @@ "dev": true }, "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", + "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", "dev": true, "requires": { "cliui": "^6.0.0", @@ -2653,7 +2664,7 @@ "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "yargs-parser": "^18.1.1" } }, "yargs-parser": { diff --git a/package.json b/package.json index d9a7a35c9727e919cad541098d9a4a4c1d509e53..6bb92b560f32211e0a2bcd1825e5a9b2ca11518b 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,8 @@ "base-64": "^0.1.0" }, "devDependencies": { - "@types/jasmine": "^3.5.13", - "@types/node": "^14.6.0", + "@types/jasmine": "^3.5.14", + "@types/node": "^14.6.4", "buffer": "^5.6.0", "find": "^0.3.0", "jasmine": "^3.6.1", @@ -54,8 +54,8 @@ "rimraf": "^3.0.2", "ts-node": "^8.10.2", "tslint": "^6.1.3", - "typedoc": "^0.18.0", - "typescript": "~3.7.5" + "typedoc": "^0.19.1", + "typescript": "^3.9.7" }, "scripts": { "preprocess": "node scripts/preprocessors.js", diff --git a/spec/fuzzing.spec.ts b/spec/fuzzing.spec.ts index c7ef1746cc42e93ec008fc03ebcce5ca9e69a589..0b8380149d1687c80cb1c5d5a00dc2d7f0eb3c05 100644 --- a/spec/fuzzing.spec.ts +++ b/spec/fuzzing.spec.ts @@ -42,7 +42,10 @@ const nubsNotTested: CalculatorType[] = [ CalculatorType.Solveur, CalculatorType.YAXN, CalculatorType.Verificateur, - CalculatorType.Espece + CalculatorType.Espece, + CalculatorType.PbBassin, + CalculatorType.PbCloison, + CalculatorType.PreBarrage // TODO: Add special treatments for creating fuzzy PreBarrage ]; const nubsWithStructures: CalculatorType[] = [ diff --git a/spec/par/par_simulation.spec.ts b/spec/par/par_simulation.spec.ts index 917b725f9d43043838a2feb905cbc57f843215d0..7cd2eea82232241111c648e62a01d3f1d972524d 100644 --- a/spec/par/par_simulation.spec.ts +++ b/spec/par/par_simulation.spec.ts @@ -106,8 +106,7 @@ describe("Class ParSimulation −", () => { // regime: ParFlowRegime.FREE message: MessageCode.WARNING_PAR_NOT_SUBMERGED }, - { - Z1: 14, // (dans Cv3, "calcul" est "off", sous-entendant que le calcul a raté ? Mais il y a tout de même des valeurs…) + { Z1: 14, // (dans Cv3, "calcul" est "off", sous-entendant que le calcul a raté ? Mais il y a tout de même des valeurs…) /* vCalc: 0.549, h: 0.937, ha: 4.64, diff --git a/spec/pre_barrage/pre-barrage.spec.ts b/spec/pre_barrage/pre-barrage.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..5bc3c51f032b560f9499d16b0791a8fa90abe2cd --- /dev/null +++ b/spec/pre_barrage/pre-barrage.spec.ts @@ -0,0 +1,592 @@ +import { PreBarrage } from "../../src/prebarrage/pre_barrage"; +import { PreBarrageParams } from "../../src/prebarrage/pre_barrage_params"; +import { PbBassin } from "../../src/prebarrage/pb_bassin"; +import { PbBassinParams } from "../../src/prebarrage/pb_bassin_params"; +import { PbCloison } from "../../src/prebarrage/pb_cloison"; +import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params"; +import { StructureWeirVillemonte } from "../../src/structure/structure_weir_villemonte"; +import { StructureWeirCunge80 } from "../../src/structure/structure_weir_cunge80"; +import { MessageCode } from "../../src/index"; + +function createPbCloisonTest(ZDV: number) { + const rectStructPrms = new RectangularStructureParams( + 0, // Q + ZDV, // ZDV + 0, // Z1 entered here for good assignation of h1 + 0, // Z2 + 0.25, // L + 0.4 // Cd pour un seuil rectangulaire + // W = Infinity par défaut pour un seuil + ); + return new StructureWeirVillemonte(rectStructPrms); +} + +function createPreBarrageTest(bMeshed: boolean = false): PreBarrage { + // Pré-barrage Z1 = 101 m, Z2 = 100 m + let Q: number; + if (bMeshed) { + Q = 1.350722; + } else { + Q = 0.601266; + } + const pb = new PreBarrage(new PreBarrageParams(Q, 101, 100), false); + pb.calculatedParam = pb.prms.Z1; + + // 1 bassin intermédiaire à la cote de fond 99 m + pb.addChild(new PbBassin(new PbBassinParams(20, 99))); + + // 1 cloison entre l'amont et le bassin avec une fente cote de radier 99.5 + pb.addChild(new PbCloison(undefined, pb.children[0] as PbBassin)); + pb.children[pb.children.length-1].addChild(createPbCloisonTest(99.5)); + + // 1 cloison entre le bassin et l'aval avec une fente cote de radier 99 + pb.addChild(new PbCloison(pb.children[0] as PbBassin, undefined)); + pb.children[pb.children.length-1].addChild(createPbCloisonTest(99)); + + if (bMeshed) { + // 1 cloison entre l'amont et l'aval avec une fente cote de radier 99.5 + pb.addChild(new PbCloison(undefined, undefined)); + pb.children[pb.children.length-1].addChild(createPbCloisonTest(99.5)); + } + + return pb; +} + +function uidsOfAllPointedNubs(pb: PreBarrage): string[] { + let uids: string[] = []; + for (const c of pb.children) { + if (c instanceof PbCloison) { + if (c.bassinAmont !== undefined) { + uids.push(c.bassinAmont.uid) + } + if (c.bassinAval !== undefined) { + uids.push(c.bassinAval.uid) + } + } else if (c instanceof PbBassin) { + uids = uids.concat(c.cloisonsAval.map(ca => ca.uid)); + uids = uids.concat(c.cloisonsAmont.map(ca => ca.uid)); + } + } + for (const c of pb.cloisonsAmont) { + if (c.bassinAval !== undefined) { + uids.push(c.bassinAval.uid) + } + } + for (const c of pb.bassins) { + uids = uids.concat(c.cloisonsAval.map(ca => ca.uid)); + uids = uids.concat(c.cloisonsAmont.map(ca => ca.uid)); + } + // deduplicate + uids = uids.filter( + (element, index, self) => self.indexOf(element) === index + ); + return uids; +} + +describe("Class PreBarrage:", () => { + + describe("Basic non meshed:", () => { + it("Calc(Z1) should return 101", () => { + const pb = createPreBarrageTest(); + const res = pb.CalcSerie(); + expect(res.vCalc).toBeCloseTo(101, 3); + }); + it("Calc(Q) should return 0.601", () => { + const pb = createPreBarrageTest(); + pb.calculatedParam = pb.prms.Q; + pb.prms.Q.singleValue = 0; + expect(pb.CalcSerie().vCalc).toBeCloseTo(0.601266, 3); + }); + }); + + describe("Basic meshed:", () => { + it("Calc(Z1) should return 101", () => { + const pb = createPreBarrageTest(true); + expect(pb.CalcSerie().vCalc).toBeCloseTo(101, 3); + }); + it("Calc(Q) should return 1.351", () => { + const pb = createPreBarrageTest(true); + pb.calculatedParam = pb.prms.Q; + pb.prms.Q.singleValue = 0; + expect(pb.CalcSerie().vCalc).toBeCloseTo(1.350722, 3); + }); + }); + + describe("Complex meshed:", () => { + let pb: PreBarrage; + beforeEach(() => { + pb = createPreBarrageTest(true); + pb.maxIterations = 100; + pb.prms.Q.singleValue = pb.prms.Q.singleValue + 0.601266; + pb.addChild(new PbBassin(new PbBassinParams(20, 99))); + // 1 cloison entre l'amont et le bassin avec une fente cote de radier 99.5 + pb.addChild(new PbCloison(undefined, pb.bassins[1] as PbBassin)); + pb.children[pb.children.length-1].addChild(createPbCloisonTest(99.5)); + // 1 cloison entre le bassin et l'aval avec une fente cote de radier 99 + pb.addChild(new PbCloison(pb.bassins[1] as PbBassin, undefined)); + pb.children[pb.children.length-1].addChild(createPbCloisonTest(99)); + // 1 cloison entre les 2 bassins avec une fente cote de radier 99 + pb.addChild(new PbCloison(pb.bassins[0] as PbBassin, pb.bassins[1] as PbBassin)); + pb.children[pb.children.length-1].addChild(createPbCloisonTest(99)); + }); + it("Calc(Z1) should return 101", () => { + const r = pb.CalcSerie(); + expect(r.ok).toBe(true); + expect(Math.abs(r.vCalc - 101)).toBeLessThanOrEqual(1E-4); + }); + it("Calc(Q) should return 1.951988", () => { + pb.calculatedParam = pb.prms.Q; + pb.prms.Q.singleValue = 0; + const r = pb.CalcSerie(); + expect(r.ok).toBe(true); + expect(Math.abs(r.vCalc - 1.951988)).toBeLessThanOrEqual(1E-4); + }); + }); + + describe("Real case:", () => { + let pbc: PreBarrage; + beforeEach(()=> { + pbc = new PreBarrage(new PreBarrageParams(0.869, 95.25, 94.45), false); + // add basins + pbc.addChild(new PbBassin(new PbBassinParams(13.80, 95))); + pbc.addChild(new PbBassin(new PbBassinParams(15.40, 94.70))); + pbc.addChild(new PbBassin(new PbBassinParams(16.20, 94.70))); + pbc.addChild(new PbBassin(new PbBassinParams(17.50, 94.40))); + pbc.addChild(new PbBassin(new PbBassinParams(32.10, 94.25))); + pbc.addChild(new PbBassin(new PbBassinParams(35.00, 94.10))); + // add walls + // Wall between upstream and basin 1 + pbc.addChild(new PbCloison(undefined, pbc.children[0] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.30, 0, 0, 0.4, 1.04) + ) + ); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 96.25, 0, 0, 4.40, 1.04) + ) + ); + // Wall between upstream and basin 2 + pbc.addChild(new PbCloison(undefined, pbc.children[1] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 96.00, 0, 0, 1.00, 1.04) + ) + ); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 96.25, 0, 0, 5.00, 0.91) + ) + ); + // Wall between upstream and basin 5 + pbc.addChild(new PbCloison(undefined, pbc.children[4] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 96.25, 0, 0, 3.50, 0.99) + ) + ); + // Wall between upstream and basin 6 + pbc.addChild(new PbCloison(undefined, pbc.children[5] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 96.25, 0, 0, 3.60, 0.99) + ) + ); + // Wall between basin 1 & 3 + pbc.addChild(new PbCloison(pbc.children[0] as PbBassin, pbc.children[2] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.00, 0, 0, 0.40, 1.04) + ) + ); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 96.25, 0, 0, 5.20, 0.99) + ) + ); + // Wall between basin 2 & 3 + pbc.addChild(new PbCloison(pbc.children[1] as PbBassin, pbc.children[2] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.85, 0, 0, 4.38, 0.91) + ) + ); + // Wall between basin 2 & 4 + pbc.addChild(new PbCloison(pbc.children[1] as PbBassin, pbc.children[3] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.85, 0, 0, 3.00, 0.99) + ) + ); + // Wall between basin 2 & 5 + pbc.addChild(new PbCloison(pbc.children[1] as PbBassin, pbc.children[4] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.50, 0, 0, 1.00, 1.04) + ) + ); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.75, 0, 0, 3.00, 0.99) + ) + ); + // Wall between basin 3 & 4 + pbc.addChild(new PbCloison(pbc.children[2] as PbBassin, pbc.children[3] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 94.70, 0, 0, 0.40, 1.04) + ) + ); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.65, 0, 0, 5.74, 0.99) + ) + ); + // Wall between basin 4 & 5 + pbc.addChild(new PbCloison(pbc.children[3] as PbBassin, pbc.children[4] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 94.40, 0, 0, 0.40, 1.04) + ) + ); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.35, 0, 0, 6.00, 0.99) + ) + ); + // Wall between basin 5 & 6 + pbc.addChild(new PbCloison(pbc.children[4] as PbBassin, pbc.children[5] as PbBassin)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 94.25, 0, 0, 0.70, 1.04) + ) + ); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 95.05, 0, 0, 9.50, 0.99) + ) + ); + // Wall between basin 6 & downstream + pbc.addChild(new PbCloison(pbc.children[5] as PbBassin, undefined)); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 94.10, 0, 0, 0.95, 1.04) + ) + ); + pbc.children[pbc.children.length-1].addChild( + new StructureWeirCunge80( + new RectangularStructureParams(0, 94.75, 0, 0, 10.20, 0.99) + ) + ); + }); + it("Calc(Z1) should converge to 96.25", () => { + pbc.calculatedParam = pbc.prms.Z1; + const r = pbc.CalcSerie() + expect(r.ok).toBeTrue(); + expect(r.vCalc).toBeCloseTo(96.25, 2); + }); + it("Calc(Z1, Q=0) should converge to 95.30", () => { + pbc.prms.Q.singleValue = 0; + pbc.calculatedParam = pbc.prms.Z1; + const r = pbc.CalcSerie() + expect(r.ok).toBeTrue(); + expect(r.vCalc).toBeCloseTo(95.30, 1); + }); + it("Calc(Z1, Q=1E-5) should converge to 95.301", () => { + pbc.prms.Q.singleValue = 1E-5; + pbc.calculatedParam = pbc.prms.Z1; + const r = pbc.CalcSerie() + expect(r.ok).toBeTrue(); + expect(r.vCalc).toBeCloseTo(95.301, 3); + }); + it("Calc(Z1, Q=0.1265) should converge to 95.616859", () => { + pbc.prms.Q.singleValue = 0.1265; + pbc.calculatedParam = pbc.prms.Z1; + const r = pbc.CalcSerie() + expect(r.ok).toBeTrue(); + expect(r.vCalc).toBeCloseTo(95.616859, 3); + }); + it("Calc(Q) should converge to 0.869", () => { + pbc.calculatedParam = pbc.prms.Q; + pbc.prms.Z1.singleValue = 96.25; + pbc.prms.Q.singleValue = 0; + const r = pbc.CalcSerie() + expect(r.ok).toBeTrue(); + expect(r.vCalc).toBeCloseTo(0.869, 1); + }); + }); + + describe("Basins and Walls results:", () => { + + it("basins results", () => { + const pb = createPreBarrageTest(); + pb.CalcSerie(); + expect(pb.bassins[0].result).toBeDefined(); + expect(pb.bassins[0].result.resultElements.length).toBeGreaterThan(0); + expect(Object.keys(pb.bassins[0].result.resultElements[0].values).length).toBeGreaterThan(0); + }); + }); + + describe("Add, move and remove children:", () => { + let pb: PreBarrage; + + it("add then remove basin", () => { + pb = createPreBarrageTest(true); + expect(pb.children.length).toBe(4); // 3 walls, 1 basin + expect(pb.bassins.length).toBe(1); + + // add 1 basin with 2 walls + const nb = new PbBassin(new PbBassinParams(15, 94)); + pb.addChild(nb); + pb.addChild(new PbCloison(pb.children[0] as PbBassin, nb)); + pb.children[pb.children.length-1].addChild(createPbCloisonTest(99.5)); + pb.addChild(new PbCloison(nb, undefined)); + pb.children[pb.children.length-1].addChild(createPbCloisonTest(99)); + + expect(pb.children.length).toBe(7); // 5 walls, 2 basins + expect(pb.bassins.length).toBe(2); + + pb.deleteChild(pb.bassins[pb.bassins.length - 1].findPositionInParent()); // newly added basin + + expect(pb.children.length).toBe(6); // 5 walls, 1 basin + expect(pb.bassins.length).toBe(1); + }); + + it("remove basin", () => { + pb = createPreBarrageTest(true); + expect(pb.children.length).toBe(4); // 3 walls, 1 basin + expect(pb.bassins.length).toBe(1); + + pb.deleteChild(0); // basin 1 + + expect(pb.children.length).toBe(3); // 3 walls, 0 basin + expect(pb.bassins.length).toBe(0); + }); + + it("remove walls", () => { + pb = createPreBarrageTest(true); + expect(pb.children.length).toBe(4); // 3 walls, 1 basin + expect(pb.bassins.length).toBe(1); + + pb.deleteChild(1); // wall 1 + + expect(pb.children.length).toBe(3); // 2 walls, 1 basin + expect(pb.bassins.length).toBe(1); + + pb.deleteChild(1); // wall 2 (now 1) + + expect(pb.children.length).toBe(2); // 1 walls, 1 basin + expect(pb.bassins.length).toBe(1); + }); + + it("basin removal in GUI example", () => { + pb = new PreBarrage(new PreBarrageParams(1, 100, 90)); + const b1 = new PbBassin(new PbBassinParams(0.1, 42)); + pb.addChild(b1); + const b2 = new PbBassin(new PbBassinParams(0.15, 38)); + pb.addChild(b2); + pb.addChild(new PbCloison(undefined, b1)); + pb.addChild(new PbCloison(b1, b2)); + pb.addChild(new PbCloison(b2, undefined)); + pb.addChild(new PbCloison(b1, undefined)); + + expect(pb.children.length).toBe(6); // 4 walls, 2 basins + expect(pb.bassins.length).toBe(2); + + pb.deleteChild(0); // remove b1 + + expect(pb.children.length).toBe(5); // 4 walls, 1 basin + expect(pb.bassins.length).toBe(1); + + expect(pb.cloisonsAmont[0].bassinAval).toBeUndefined(); + + expect(uidsOfAllPointedNubs(pb)).not.toContain(b1.uid); + }); + + it("move basin up (1)", () => { + pb = new PreBarrage(new PreBarrageParams(1, 100, 90)); + const b1 = new PbBassin(new PbBassinParams(0.1, 42)); + pb.addChild(b1); + const b2 = new PbBassin(new PbBassinParams(0.15, 38)); + pb.addChild(b2); + pb.addChild(new PbCloison(undefined, b1)); + pb.addChild(new PbCloison(b1, b2)); + pb.addChild(new PbCloison(b2, undefined)); + pb.addChild(new PbCloison(b1, undefined)); + + expect(pb.children.length).toBe(6); // 4 walls, 2 basins + expect(pb.bassins.length).toBe(2); + + // move b2 to become 1st basin (basin #0) + pb.moveBasin(b2.uid, 0); + + expect(pb.children.length).toBe(6); // 4 walls, 2 basins + expect(pb.bassins.length).toBe(2); + + expect(pb.children[0].uid).toBe(b2.uid); + expect(pb.children[1].uid).toBe(b1.uid); + }); + + it("move basin up (2)", () => { + pb = new PreBarrage(new PreBarrageParams(1, 100, 90)); + const b1 = new PbBassin(new PbBassinParams(0.1, 42)); + pb.addChild(b1); + pb.addChild(new PbCloison(undefined, b1)); + pb.addChild(new PbCloison(b1, undefined)); + const b2 = new PbBassin(new PbBassinParams(0.15, 38)); + pb.addChild(b2); + pb.addChild(new PbCloison(b1, b2)); + pb.addChild(new PbCloison(b2, undefined)); + const b3 = new PbBassin(new PbBassinParams(0.17, 35)); + pb.addChild(b3); + pb.addChild(new PbCloison(b1, b3)); + pb.addChild(new PbCloison(b3, undefined)); + + expect(pb.children.length).toBe(9); // 6 walls, 3 basins + expect(pb.bassins.length).toBe(3); + + expect(pb.bassins[0].uid).toBe(b1.uid); + expect(pb.bassins[1].uid).toBe(b2.uid); + expect(pb.bassins[2].uid).toBe(b3.uid); + + // console.log("AVANT", pb.children.map(c => c.uid)); + // move b3 to become 2nd basin (basin #1) + pb.moveBasin(b3.uid, 1); + + expect(pb.children.length).toBe(9); // 6 walls, 3 basins + expect(pb.bassins.length).toBe(3); + + expect(pb.children[0].uid).toBe(b1.uid); + expect(pb.children[3].uid).toBe(b3.uid); + expect(pb.children[4].uid).toBe(b2.uid); + + expect(pb.bassins[0].uid).toBe(b1.uid); + expect(pb.bassins[1].uid).toBe(b3.uid); + expect(pb.bassins[2].uid).toBe(b2.uid); + }); + + it("move basin down (1)", () => { + pb = new PreBarrage(new PreBarrageParams(1, 100, 90)); + const b1 = new PbBassin(new PbBassinParams(0.1, 42)); + pb.addChild(b1); + const b2 = new PbBassin(new PbBassinParams(0.15, 38)); + pb.addChild(b2); + pb.addChild(new PbCloison(undefined, b1)); + pb.addChild(new PbCloison(b1, b2)); + pb.addChild(new PbCloison(b2, undefined)); + pb.addChild(new PbCloison(b1, undefined)); + + expect(pb.children.length).toBe(6); // 4 walls, 2 basins + expect(pb.bassins.length).toBe(2); + + // move b1 to become 2nd basin (basin #1) + pb.moveBasin(b1.uid, 1); + + expect(pb.children.length).toBe(6); // 4 walls, 2 basins + expect(pb.bassins.length).toBe(2); + + expect(pb.children[0].uid).toBe(b2.uid); + expect(pb.children[1].uid).toBe(b1.uid); + }); + + it("move basin down (2)", () => { + pb = new PreBarrage(new PreBarrageParams(1, 100, 90)); + const b1 = new PbBassin(new PbBassinParams(0.1, 42)); + pb.addChild(b1); + pb.addChild(new PbCloison(undefined, b1)); + pb.addChild(new PbCloison(b1, undefined)); + const b2 = new PbBassin(new PbBassinParams(0.15, 38)); + pb.addChild(b2); + pb.addChild(new PbCloison(b1, b2)); + pb.addChild(new PbCloison(b2, undefined)); + const b3 = new PbBassin(new PbBassinParams(0.17, 35)); + pb.addChild(b3); + pb.addChild(new PbCloison(b1, b3)); + pb.addChild(new PbCloison(b3, undefined)); + + expect(pb.children.length).toBe(9); // 6 walls, 3 basins + expect(pb.bassins.length).toBe(3); + + expect(pb.bassins[0].uid).toBe(b1.uid); + expect(pb.bassins[1].uid).toBe(b2.uid); + expect(pb.bassins[2].uid).toBe(b3.uid); + + // console.log("AVANT", pb.children.map(c => c.uid)); + // move b1 to become 3rd basin (basin #2) + pb.moveBasin(b1.uid, 2); + + expect(pb.children.length).toBe(9); // 6 walls, 3 basins + expect(pb.bassins.length).toBe(3); + + expect(pb.children[2].uid).toBe(b2.uid); + expect(pb.children[5].uid).toBe(b3.uid); + expect(pb.children[6].uid).toBe(b1.uid); + + expect(pb.bassins[0].uid).toBe(b2.uid); + expect(pb.bassins[1].uid).toBe(b3.uid); + expect(pb.bassins[2].uid).toBe(b1.uid); + }); + + }); + + describe("error cases −", () => { + + it("downstream water elevation > upstream water elevation", () => { + const pb = createPreBarrageTest(); + pb.prms.Z2.singleValue = pb.prms.Z1.singleValue + 1; + const res = pb.CalcSerie(); + expect(res.ok).toBe(false); + expect(res.resultElement.log.messages.length).toBe(1); + expect(res.resultElement.log.messages[0].code).toBe(MessageCode.ERROR_PREBARRAGE_Z2_SUP_Z1); + }); + + it("basin apron elevation > upstream water elevation", () => { + const pb = createPreBarrageTest(); + pb.prms.Z1.singleValue = pb.bassins[0].prms.ZF.singleValue - 1; + pb.prms.Z2.singleValue = pb.prms.Z1.singleValue - 1; + const res = pb.CalcSerie(); + expect(res.ok).toBe(true); + expect(res.resultElement.log.messages.length).toBe(1); + expect(res.resultElement.log.messages[0].code).toBe(MessageCode.WARNING_PREBARRAGE_BASSIN_ZF_SUP_Z1); + expect(res.resultElement.log.messages[0].extraVar.n).toBe("1"); + }); + + it("device ZDV < ZF of upstream basin", () => { + const pb = createPreBarrageTest(); + pb.bassins[0].cloisonsAval[0].structures[0].prms.ZDV.singleValue = pb.bassins[0].prms.ZF.singleValue - 1; + const res = pb.CalcSerie(); + expect(res.ok).toBe(false); + expect(res.resultElement.log.messages.length).toBe(1); + expect(res.resultElement.log.messages[0].code).toBe(MessageCode.ERROR_PREBARRAGE_STRUCTURE_ZDV_INF_ZF); + expect(res.resultElement.log.messages[0].extraVar.cub).toBe("B1", "wall upstream basin"); + expect(res.resultElement.log.messages[0].extraVar.cdb).toBe("MSG_INFO_LIB_AVAL", "wall downstream basin"); + expect(res.resultElement.log.messages[0].extraVar.ns).toBe("1", "structure number in wall"); + }); + + it("PreBarrage must have at least one path from upstream to downstream", () => { + const pb = createPreBarrageTest(); + pb.bassins[0].cloisonsAval[0].bassinAval = pb.bassins[0]; // nonsense wall having the same basin at upstream and downstream + expect(() => { + pb.CalcSerie(); + }).toThrowError("PreBarrage.checkGeometry(): must have at least one path from upstream to downstream"); + }) + + it("PreBarrage must have at least one upstream wall", () => { + const pb = createPreBarrageTest(); + pb.cloisonsAmont = []; + expect(() => { + pb.CalcSerie(); + }).toThrowError("PreBarrage.checkGeometry(): must have at least one upstream wall (has 0)"); + }) + + it("each basin must have at least one upstream wall and one downstream wall", () => { + const pb = createPreBarrageTest(); + pb.bassins[0].cloisonsAmont = []; + expect(() => { + pb.CalcSerie(); + }).toThrowError("PreBarrage.checkGeometry(): basin 0 (starting at 0) must have at least one upstream wall (has 0) and one downstream wall (has 1)"); + }) + + }) +}); diff --git a/spec/session/serialisation.spec.ts b/spec/session/serialisation.spec.ts index 1aca6700b93b03319da76daf3d2ef1faac802ad8..7bfab78c4d03d16b4ddcf0dbf05032695efec651 100644 --- a/spec/session/serialisation.spec.ts +++ b/spec/session/serialisation.spec.ts @@ -33,6 +33,14 @@ import { RectangularStructure } from "../../src/structure/rectangular_structure" import { RectangularStructureParams } from "../../src/structure/rectangular_structure_params"; import { LoiDebit } from "../../src/structure/structure_props"; import { StructureWeirSubmergedLarinier, } from "../../src/structure/structure_weir_submerged_larinier"; +import { PreBarrage } from "../../src/prebarrage/pre_barrage"; +import { PbCloison } from "../../src/prebarrage/pb_cloison"; +import { PreBarrageParams } from "../../src/prebarrage/pre_barrage_params"; +import { PbBassin } from "../../src/prebarrage/pb_bassin"; +import { PbBassinParams } from "../../src/prebarrage/pb_bassin_params"; +import { TriangularStructureParams } from "../../src/structure/structure_triangular_weir_params"; +import { StructureOrificeFreeParams } from "../../src/structure/structure_orifice_free_params"; + import { TriangularTruncStructureParams } from "../../src/structure/structure_triangular_trunc_weir_params"; import { StructureJetType } from "../../src/structure/structure"; import { CloisonsAvalParams } from "../../src/pab/cloison_aval_params"; @@ -48,6 +56,7 @@ import { sessionSolveurLast } from "./session.solveur.last"; import { sessionSpaghetti } from "./session.spaghetti"; import { sessionVerificateurLast } from "./session.verificateur.last"; import { sessionVerificateurFirst } from "./session.verificateur.first"; +import { Structure } from "../../src/structure/structure"; let dever: Dever; let cloisons: Cloisons; @@ -442,6 +451,166 @@ describe("sessions containing Solveur - ", () => { expect(sp4.symbol).toBe("Z1"); expect(sp4.parentNub.uid).toBe("NjRvcG"); }); + +}); + +describe("PreBarrage - ", () => { + + it("serialise", () => { + const pb = new PreBarrage(new PreBarrageParams(1, 100, 90)); + pb.addChild(new PbCloison(undefined, undefined)); // amont-aval + const b1 = new PbBassin(new PbBassinParams(0.1, 42)); + pb.addChild(b1); + pb.addChild(new PbCloison(undefined, b1)); // amont-B1 + const b2 = new PbBassin(new PbBassinParams(0.15, 38)); + + const c2 = new PbCloison(undefined, b2); // amont-B2 + const s1: Structure = CreateStructure(LoiDebit.WeirCunge80); + s1.prms.ZDV.singleValue = 95.30; + s1.getParameter("L").singleValue = 0.4; + s1.getParameter("CdGR").singleValue = 1.04; + c2.addChild(s1); + pb.addChild(c2); + + pb.addChild(b2); + pb.addChild(new PbCloison(b1, b2)); // B1-B2 + pb.addChild(new PbCloison(b2, undefined)); // B2-aval + + Session.getInstance().clear(); + Session.getInstance().registerNub(pb); + const json = Session.getInstance().serialise(); + expect(json).toContain(`"props":{"calcType":"PbCloison","upstreamBasin":"","downstreamBasin":""}`); + + // check that no parameter is in CALC mode in children + const calcCount = (json.match(/"mode":"CALCUL"/g) || []).length; + expect(calcCount).toBe(1); + }); + + it("unserialise", () => { + Session.getInstance().clear(); + const json = `{ "header": { "source": "jalhyd", "format_version": "1.3", "created": "2020-06-29T07:58:18.237Z" }, "settings": { "precision": 1e-7, "maxIterations": 100, "displayPrecision": 3 }, "documentation": "", "session": [ { "uid": "bG5oYW", "props": { "calcType": "PreBarrage" }, "meta": { "title": "Prébarrages" }, "children": [ { "uid": "a3c1eH", "props": { "calcType": "PbCloison", "upstreamBasin": "", "downstreamBasin": "" }, "children": [ { "uid": "bHdvOW", "props": { "calcType": "Structure", "structureType": "SeuilRectangulaire", "loiDebit": "WeirSubmergedLarinier" }, "children": [], "parameters": [ { "symbol": "ZDV", "mode": "SINGLE", "value": 101.11 }, { "symbol": "L", "mode": "SINGLE", "value": 0.211 }, { "symbol": "CdWSL", "mode": "SINGLE", "value": 0.7511 } ] } ], "parameters": [ { "symbol": "Q", "mode": "SINGLE" }, { "symbol": "Z1", "mode": "SINGLE", "value": 0 }, { "symbol": "Z2", "mode": "SINGLE", "value": 0 } ] }, { "uid": "M3AxbT", "props": { "calcType": "PbBassin" }, "children": [], "parameters": [ { "symbol": "S", "mode": "SINGLE", "value": 0.111 }, { "symbol": "ZF", "mode": "SINGLE", "value": 42.11 } ] }, { "uid": "bjRzNG", "props": { "calcType": "PbCloison", "upstreamBasin": "", "downstreamBasin": "M3AxbT" }, "children": [ { "uid": "MGYycm", "props": { "calcType": "Structure", "structureType": "SeuilTriangulaire", "loiDebit": "TriangularWeirBroad" }, "children": [], "parameters": [ { "symbol": "ZDV", "mode": "SINGLE", "value": 101.22 }, { "symbol": "alpha2", "mode": "SINGLE", "value": 45.22 }, { "symbol": "CdT", "mode": "SINGLE", "value": 1.3622 } ] } ], "parameters": [ { "symbol": "Q", "mode": "SINGLE" }, { "symbol": "Z1", "mode": "SINGLE", "value": 0 }, { "symbol": "Z2", "mode": "SINGLE", "value": 0 } ] }, { "uid": "OTg1en", "props": { "calcType": "PbCloison", "upstreamBasin": "", "downstreamBasin": "d2kxcD" }, "children": [ { "uid": "Ym1qZH", "props": { "calcType": "Structure", "structureType": "Orifice", "loiDebit": "OrificeFree" }, "children": [], "parameters": [ { "symbol": "S", "mode": "SINGLE", "value": 0.133 }, { "symbol": "CdO", "mode": "SINGLE", "value": 0.733 }, { "symbol": "Zco", "mode": "SINGLE", "value": 101.33 } ] } ], "parameters": [ { "symbol": "Q", "mode": "SINGLE" }, { "symbol": "Z1", "mode": "SINGLE", "value": 0 }, { "symbol": "Z2", "mode": "SINGLE", "value": 0 } ] }, { "uid": "d2kxcD", "props": { "calcType": "PbBassin" }, "children": [], "parameters": [ { "symbol": "S", "mode": "SINGLE", "value": 0.1522 }, { "symbol": "ZF", "mode": "SINGLE", "value": 38.22 } ] }, { "uid": "Z3J2cD", "props": { "calcType": "PbCloison", "upstreamBasin": "M3AxbT", "downstreamBasin": "d2kxcD" }, "children": [ { "uid": "aHpubT", "props": { "calcType": "Structure", "structureType": "VanneRectangulaire", "loiDebit": "RectangularOrificeSubmerged" }, "children": [], "parameters": [ { "symbol": "ZDV", "mode": "SINGLE", "value": 101.44 }, { "symbol": "W", "mode": "SINGLE", "value": 0.544 }, { "symbol": "L", "mode": "SINGLE", "value": 0.244 }, { "symbol": "CdGR", "mode": "SINGLE", "value": 0.644 } ] } ], "parameters": [ { "symbol": "Q", "mode": "SINGLE" }, { "symbol": "Z1", "mode": "SINGLE", "value": 0 }, { "symbol": "Z2", "mode": "SINGLE", "value": 0 } ] }, { "uid": "anQ0Zn", "props": { "calcType": "PbCloison", "upstreamBasin": "d2kxcD", "downstreamBasin": "" }, "children": [ { "uid": "ZzJtan", "props": { "calcType": "Structure", "structureType": "VanneRectangulaire", "loiDebit": "GateCunge80" }, "children": [], "parameters": [ { "symbol": "ZDV", "mode": "SINGLE", "value": 101.55 }, { "symbol": "W", "mode": "SINGLE", "value": 0.555 }, { "symbol": "L", "mode": "SINGLE", "value": 0.255 }, { "symbol": "CdCunge", "mode": "SINGLE", "value": 1.55 } ] } ], "parameters": [ { "symbol": "Q", "mode": "SINGLE" }, { "symbol": "Z1", "mode": "SINGLE", "value": 0 }, { "symbol": "Z2", "mode": "SINGLE", "value": 0 } ] } ], "parameters": [ { "symbol": "Q", "mode": "SINGLE", "value": 1.0101 }, { "symbol": "Z1", "mode": "CALCUL" }, { "symbol": "Z2", "mode": "SINGLE", "value": 90.0101 } ] } ] }`; + const res = Session.getInstance().unserialise(json); + expect(res.hasErrors).toBe(false); + + const pb = Session.getInstance().findNubByUid("bG5oYW") as PreBarrage; + expect(pb).toBeDefined(); + expect(pb.children.length).toBe(7); // 2 basins, 5 walls + expect(pb.bassins.length).toBe(2); + + // paramètres de la rivière + expect(pb.prms.Q.singleValue).toBe(1.0101); + expect(pb.prms.Z2.singleValue).toBe(90.0101); + expect(pb.prms.Z1.valueMode).toBe(ParamValueMode.CALCUL); + expect(pb.calculatedParam.symbol).toBe("Z1"); + + const b1: PbBassin = pb.findChild("M3AxbT") as PbBassin; + expect(b1.prms.S.singleValue).toBe(0.111); + expect(b1.prms.ZF.singleValue).toBe(42.11); + const b2: PbBassin = pb.findChild("d2kxcD") as PbBassin; + expect(b2.prms.S.singleValue).toBe(0.1522); + expect(b2.prms.ZF.singleValue).toBe(38.22); + + const c1: PbCloison = pb.findChild("a3c1eH") as PbCloison; // amont-aval + expect(c1.bassinAmont).toBeUndefined(); + expect(c1.bassinAval).toBeUndefined(); + expect(c1.structures.length).toBe(1); + expect(c1.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.WeirSubmergedLarinier); + const s1 = c1.structures[0].prms as RectangularStructureParams; + expect(s1.ZDV.singleValue).toBe(101.11); + expect(s1.L.singleValue).toBe(0.211); + expect(s1.CdWSL.singleValue).toBe(0.7511); + + const c2: PbCloison = pb.findChild("bjRzNG") as PbCloison; // amont-B1 + expect(c2.bassinAmont).toBeUndefined(); + expect(c2.bassinAval.uid).toBe("M3AxbT"); + expect(c2.structures.length).toBe(1); + expect(c2.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.TriangularWeirBroad); + const s2 = c2.structures[0].prms as TriangularStructureParams; + expect(s2.ZDV.singleValue).toBe(101.22); + expect(s2.CdT.singleValue).toBe(1.3622); + expect(s2.alpha2.singleValue).toBe(45.22); + + const c3: PbCloison = pb.findChild("OTg1en") as PbCloison; // amont-B2 + expect(c3.bassinAmont).toBeUndefined(); + expect(c3.bassinAval.uid).toBe("d2kxcD"); + expect(c3.structures.length).toBe(1); + expect(c3.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.OrificeFree); + const s3 = c3.structures[0].prms as StructureOrificeFreeParams; + expect(s3.S.singleValue).toBe(0.133); + expect(s3.CdO.singleValue).toBe(0.733); + expect(s3.Zco.singleValue).toBe(101.33); + + const c4: PbCloison = pb.findChild("Z3J2cD") as PbCloison; // B1-B2 + expect(c4.bassinAmont.uid).toBe("M3AxbT"); + expect(c4.bassinAval.uid).toBe("d2kxcD"); + expect(c4.structures.length).toBe(1); + expect(c4.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.RectangularOrificeSubmerged); + const s4 = c4.structures[0].prms as RectangularStructureParams; + expect(s4.ZDV.singleValue).toBe(101.44); + expect(s4.L.singleValue).toBe(0.244); + expect(s4.W.singleValue).toBe(0.544); + expect(s4.CdGR.singleValue).toBe(0.644); + + const c5: PbCloison = pb.findChild("anQ0Zn") as PbCloison; // B2-aval + expect(c5.bassinAmont.uid).toBe("d2kxcD"); + expect(c5.bassinAval).toBeUndefined(); + expect(c5.structures.length).toBe(1); + expect(c5.structures[0].properties.getPropValue("loiDebit")).toBe(LoiDebit.GateCunge80); + const s5 = c5.structures[0].prms as RectangularStructureParams; + expect(s5.ZDV.singleValue).toBe(101.55); + expect(s5.L.singleValue).toBe(0.255); + expect(s5.W.singleValue).toBe(0.555); + expect(s5.CdCunge.singleValue).toBe(1.55); + + // calculate + const resPb = pb.CalcSerie(); + expect(resPb).toBeDefined(); + }); + + it("unserialise multiple times", () => { + Session.getInstance().clear(); + const json = `{"header":{"source":"jalhyd","format_version":"1.3","created":"2020-07-03T11:58:35.049Z"},"settings":{"precision":1e-7,"maxIterations":100,"displayPrecision":3},"documentation":"","session":[{"uid":"YmZ6eW","props":{"calcType":"PreBarrage"},"meta":{"title":"Prébarrages"},"children":[{"uid":"ZW8yaX","props":{"calcType":"PbBassin"},"children":[],"parameters":[{"symbol":"S","mode":"SINGLE","value":13.8},{"symbol":"ZF","mode":"SINGLE","value":95}]},{"uid":"bXkwZW","props":{"calcType":"PbCloison","upstreamBasin":"","downstreamBasin":"ZW8yaX"},"children":[{"uid":"cmw3eH","props":{"calcType":"Structure","loiDebit":"WeirCunge80","structureType":"SeuilRectangulaire"},"children":[],"parameters":[{"symbol":"ZDV","mode":"SINGLE","value":95.3},{"symbol":"L","mode":"SINGLE","value":0.4},{"symbol":"CdCunge","mode":"SINGLE","value":1}]}],"parameters":[]},{"uid":"OHV2cH","props":{"calcType":"PbCloison","upstreamBasin":"ZW8yaX","downstreamBasin":""},"children":[{"uid":"OGM3bm","props":{"calcType":"Structure","loiDebit":"WeirCunge80","structureType":"SeuilRectangulaire"},"children":[],"parameters":[{"symbol":"ZDV","mode":"SINGLE","value":95.3},{"symbol":"L","mode":"SINGLE","value":0.4},{"symbol":"CdCunge","mode":"SINGLE","value":1}]}],"parameters":[]}],"parameters":[{"symbol":"Q","mode":"SINGLE","value":1},{"symbol":"Z1","mode":"CALCUL"},{"symbol":"Z2","mode":"SINGLE","value":100}]}]}`; + // unserialise + const res = Session.getInstance().unserialise(json); + expect(res.hasErrors).toBe(false); + expect(Session.getInstance().getAllNubs().length).toBe(1); + + const pb = Session.getInstance().findNubByUid("YmZ6eW") as PreBarrage; + expect(pb).toBeDefined(); + expect(pb.children.length).toBe(3); // 1 basins, 2 walls + expect(pb.bassins.length).toBe(1); + + const c1: PbCloison = pb.findChild("bXkwZW") as PbCloison; // amont-B1 + expect(c1.bassinAmont).toBeUndefined(); + expect(c1.bassinAval.uid).toBe("ZW8yaX"); + + const c2: PbCloison = pb.findChild("OHV2cH") as PbCloison; // B1-aval + expect(c2.bassinAmont.uid).toBe("ZW8yaX"); + expect(c2.bassinAval).toBeUndefined(); + + // unserialise the same Nub a second time + const res2 = Session.getInstance().unserialise(json); + expect(res2.hasErrors).toBe(false); + expect(Session.getInstance().getAllNubs().length).toBe(2); + + const pb2 = Session.getInstance().getAllNubs()[1] as PreBarrage; + expect(pb2).toBeDefined(); + expect(pb2.uid).not.toBe("YmZ6eW"); + expect(pb2.children.length).toBe(3); // 1 basins, 2 walls + expect(pb2.bassins.length).toBe(1); + // check that UIDs changed + expect(pb2.uid).not.toBe(pb.uid); + expect(pb2.bassins[0].uid).not.toBe(pb.bassins[0].uid); + + const c12: PbCloison = pb2.getChildren()[1] as PbCloison; // amont-B1 + expect(c12.bassinAmont).toBeUndefined(); + expect(c12.bassinAval.uid).toBe(pb2.bassins[0].uid); + + const c22: PbCloison = pb2.getChildren()[2] as PbCloison; // B1-aval + expect(c22.bassinAmont.uid).toBe(pb2.bassins[0].uid); + expect(c22.bassinAval).toBeUndefined(); + }); + }); describe("sessions containing Verificateur - ", () => { diff --git a/spec/verificateur/verificateur.spec.ts b/spec/verificateur/verificateur.spec.ts index 385366ad4e509faa57bc94e171f159ebc2a43b1a..b790b5ee98152eeb0277b2eda1a05e11c11371e0 100644 --- a/spec/verificateur/verificateur.spec.ts +++ b/spec/verificateur/verificateur.spec.ts @@ -25,10 +25,12 @@ function createPab(): Pab { const pab = Session.getInstance().createSessionNub( new Props({ calcType: CalculatorType.Pab }) ) as Pab; - pab.children.push(Session.getInstance().createNub( + const cl = Session.getInstance().createNub( new Props({ calcType: CalculatorType.Cloisons }) - ) as Cloisons); - pab.children[0].structures[0] = CreateStructure(LoiDebit.WeirSubmergedLarinier); + ) as Cloisons; + cl.parent = pab; + pab.children.push(cl); + pab.children[0].structures[0] = CreateStructure(LoiDebit.WeirSubmergedLarinier, cl); const dw = Session.getInstance().createNub( new Props({ calcType: CalculatorType.CloisonAval }) ) as CloisonAval; @@ -235,6 +237,7 @@ describe("vérificateur de franchissement −", () => { pab.children[4].structures[0] = new StructureWeirVillemonte( // Échancrure (Villemonte 1957) pab.children[4].structures[0].prms as RectangularStructureParams ); + pab.children[4].structures[0].parent = pab.children[4]; pab.children[4].structures[0].getParameter("L").singleValue = 0.35; pab.prms.Z2.singleValue = 30.4; // évite une chute trop importante à la cloison 5 // vérificateur diff --git a/src/compute-node.ts b/src/compute-node.ts index 512ab5d660ab865c7fff00fbbdafeec031ef1cfe..7dc9278b4befd7a58f16e39c610ba92456c29559 100644 --- a/src/compute-node.ts +++ b/src/compute-node.ts @@ -38,6 +38,9 @@ export enum CalculatorType { ConcentrationBlocs, Par, // Passe à ralentisseurs, calage ParSimulation, // Passe à ralentisseurs, simulation + PreBarrage, // Pré-barrage + PbCloison, // Cloison de pré-barrage + PbBassin, // Bassin de pré-barrage Espece, // Critères de vérification pour une espèce de poissons Verificateur // Vérificateur de contraintes sur une passe pour une ou plusieurs espèces } diff --git a/src/dichotomie.ts b/src/dichotomie.ts index 66aaad989513409fa520c2b9add9ed04ceaf0f99..d90fcf869e3742b3631f1205b13c1b235df96ff0 100644 --- a/src/dichotomie.ts +++ b/src/dichotomie.ts @@ -150,13 +150,17 @@ export class Dichotomie extends Debug { * @param dom domaine de définition de la variable */ private isIncreasingFunction(x: number, dom: Interval): boolean { - const epsilon = 1e-8; - const bounds = new Interval(x - epsilon, x + epsilon); - bounds.setInterval(bounds.intersect(dom)); // au cas où l'on sorte du domaine de la variable de la fonction - - const y1 = this.CalculX(bounds.min); - const y2 = this.CalculX(bounds.max); - return y2 > y1; + let epsilon = 1e-8; + for(let i=0; i < 20; i++) { + const bounds = new Interval(x - epsilon, x + epsilon); + bounds.setInterval(bounds.intersect(dom)); // au cas où l'on sorte du domaine de la variable de la fonction + + const y1 = this.CalculX(bounds.min); + const y2 = this.CalculX(bounds.max); + if(Math.abs(y2 - y1) > 1E-6) return y2 > y1; + epsilon *= 10; + } + return true; } /** diff --git a/src/index.ts b/src/index.ts index b0532a82333dd472c991beacc390a1bfdc3af533..41e1307a7fd71c58bb39889a0fd2326322dae388 100644 --- a/src/index.ts +++ b/src/index.ts @@ -93,3 +93,8 @@ export * from "./verification/verificateur_params"; export * from "./verification/fish_species"; export * from "./verification/diving-jet-support"; export * from "./fish_pass"; +export * from "./prebarrage/pre_barrage"; +export * from "./prebarrage/pre_barrage_params"; +export * from "./prebarrage/pb_cloison"; +export * from "./prebarrage/pb_bassin"; +export * from "./prebarrage/pb_bassin_params"; diff --git a/src/macrorugo/macrorugo_params.ts b/src/macrorugo/macrorugo_params.ts index b67f1814f9b081a805f43568e52719ffe53b2f51..348f8e5806a83abf187db45dd1ced426e8010c85 100644 --- a/src/macrorugo/macrorugo_params.ts +++ b/src/macrorugo/macrorugo_params.ts @@ -77,7 +77,7 @@ export class MacrorugoParams extends ParamsEquation { this._Ks = new ParamDefinition(this, "Ks", new ParamDomain(ParamDomainValue.INTERVAL, 0, 1), "m", rRF); this.addParamDefinition(this._Ks); - this._C = new ParamDefinition(this, "C", new ParamDomain(ParamDomainValue.INTERVAL, 0, 1), "-", rCB); + this._C = new ParamDefinition(this, "C", new ParamDomain(ParamDomainValue.INTERVAL, 0, 1), "", rCB); this.addParamDefinition(this._C); this._PBD = new ParamDefinition(this, "PBD", new ParamDomain(ParamDomainValue.INTERVAL, 0, 2), "m", rPBD); diff --git a/src/nub.ts b/src/nub.ts index b9b1b913d9600c6f4c6aa3ff44ba2d29c669fdcd..3b841b290c00a6c6c6ff82b784b368e28ba354de 100644 --- a/src/nub.ts +++ b/src/nub.ts @@ -461,6 +461,28 @@ export abstract class Nub extends ComputeNode implements IObservable { }; } + /** + * Returns a list of parameters that are fixed, either because their valueMode + * is SINGLE, or because their valueMode is LINK and the reference is + * defined and fixed. Does not take calculated parameters into account. + */ + public findFixedParams(): ParamDefinition[] { + const fixed: ParamDefinition[] = []; + for (const p of this.parameterIterator) { + if ( + p.valueMode === ParamValueMode.SINGLE + || ( + p.valueMode === ParamValueMode.LINK + && p.isReferenceDefined() + && ! p.referencedValue.hasMultipleValues() + ) + ) { + fixed.push(p); + } + } + return fixed; + } + /** * Returns a list of parameters that are variating, either because their valueMode * is LISTE or MINMAX, or because their valueMode is LINK and the reference is @@ -493,7 +515,6 @@ export abstract class Nub extends ComputeNode implements IObservable { * Effectue une série de calculs sur un paramètre; déclenche le calcul en chaîne * des modules en amont si nécessaire * @param rInit solution approximative du paramètre - * @param sDonnee éventuel symbole / paire symbole-uid du paramètre à calculer */ public CalcSerie(rInit?: number): Result { // prepare calculation @@ -1033,8 +1054,8 @@ export abstract class Nub extends ComputeNode implements IObservable { // is this a Solveur if (this instanceof Solveur) { return ( - (this.searchedParameter !== undefined && this.searchedParameter.nubUid === uid) - || (this.nubToCalculate !== undefined && this.nubToCalculate.uid === uid) + (this.searchedParameter?.nubUid === uid) + || (this.nubToCalculate?.uid === uid) ); } else if (this instanceof Verificateur) { return ( @@ -1159,16 +1180,17 @@ export abstract class Nub extends ComputeNode implements IObservable { * @returns the calculated parameter found, if any - used by child Nub to notify * its parent of the calculated parameter to set */ - public loadObjectRepresentation(obj: any): { p: ParamDefinition, hasErrors: boolean } { + public loadObjectRepresentation(obj: any): { p: ParamDefinition, hasErrors: boolean, changedUids: { [key: string]: string } } { // return value - const ret: { p: ParamDefinition, hasErrors: boolean } = { + const ret: { p: ParamDefinition, hasErrors: boolean, changedUids: { [key: string]: string } } = { p: undefined, - hasErrors: false + hasErrors: false, + changedUids: {} }; // set parameter modes and values if (obj.parameters && Array.isArray(obj.parameters)) { // 1st pass: find calculated param - // (if calculated param is not the default one, and default one is processes + // (if calculated param is not the default one, and default one is processed // before new one, prevents changing the former's mode from setting the 1st // param of the Nub to calculated, resetting the mode that had been loaded) for (const p of obj.parameters) { @@ -1198,6 +1220,8 @@ export abstract class Nub extends ComputeNode implements IObservable { // try to keep the original ID if (! Session.getInstance().uidAlreadyUsed(s.uid)) { subNub.setUid(s.uid); + } else { + ret.changedUids[s.uid] = subNub.uid; } const childRet = subNub.loadObjectRepresentation(s); // add Structure to parent @@ -1296,7 +1320,7 @@ export abstract class Nub extends ComputeNode implements IObservable { for (const p of this._children[index].parameterIterator) { // if p is also present and visible in new Nub const cp = child.getParameter(p.symbol); - if (cp !== undefined && cp.visible && p.visible) { + if (cp?.visible && p.visible) { parametersState[p.symbol] = p.objectRepresentation([]); } } @@ -1392,9 +1416,7 @@ export abstract class Nub extends ComputeNode implements IObservable { return undefined; } - /** - * Moves a child to first position - */ + /** Moves a child up (1 step) */ public moveChildUp(child: Nub) { let i = 0; for (const s of this._children) { @@ -1408,9 +1430,7 @@ export abstract class Nub extends ComputeNode implements IObservable { } } - /** - * Moves a child to last position - */ + /** Moves a child down (1 step) */ public moveChildDown(child: Nub) { let i = 0; for (const s of this._children) { diff --git a/src/open-channel/remous.ts b/src/open-channel/remous.ts index 9b9b23af27f4484e227db38a975f09bd55865960..0beb4ede2105dd25a833664192a55da7f4ef201c 100644 --- a/src/open-channel/remous.ts +++ b/src/open-channel/remous.ts @@ -179,13 +179,13 @@ export class CourbeRemous extends SectionNub { } let crbFlu; - if (rCourbeFlu !== undefined && rCourbeFlu.trY !== undefined) { + if (rCourbeFlu?.trY !== undefined) { crbFlu = rCourbeFlu.trY; } else { crbFlu = {}; } let crbTor; - if (rCourbeTor !== undefined && rCourbeTor.trY !== undefined) { + if (rCourbeTor?.trY !== undefined) { crbTor = rCourbeTor.trY; } else { crbTor = {}; diff --git a/src/pab/pab.ts b/src/pab/pab.ts index 0df2fa42aaef0f9e12665893b5050e18661a10ed..597d82631b0cac5d4f05d10bd8bd2fb1ff4549bf 100644 --- a/src/pab/pab.ts +++ b/src/pab/pab.ts @@ -245,9 +245,9 @@ export class Pab extends FishPass { * @returns the calculated parameter found, if any - used by child Nub to notify * its parent of the calculated parameter to set */ - public loadObjectRepresentation(obj: any): { p: ParamDefinition, hasErrors: boolean } { + public loadObjectRepresentation(obj: any): { p: ParamDefinition, hasErrors: boolean, changedUids: { [key: string]: string } } { // return value - const ret: { p: ParamDefinition, hasErrors: boolean } = super.loadObjectRepresentation(obj); + const ret: { p: ParamDefinition, hasErrors: boolean, changedUids: { [key: string]: string } } = super.loadObjectRepresentation(obj); // load downwall if any if (obj.downWall) { // decode properties diff --git a/src/par/par_type_chevron.ts b/src/par/par_type_chevron.ts index 49da5bd5909cb4147ed819bceadb27adeb5ccae4..8ffc30cd26d36ca2f459755df3e54612b548334c 100644 --- a/src/par/par_type_chevron.ts +++ b/src/par/par_type_chevron.ts @@ -3,7 +3,6 @@ import { ParTypeSC } from "./par_type_sc"; import { ParParams } from "./par_params"; import { ParType } from "./par"; import { Message, MessageCode } from "../util/message"; -import { ParamDefinition } from "../param/param-definition"; export class ParTypeChevron extends ParTypeSC { diff --git a/src/par/par_type_superactive.ts b/src/par/par_type_superactive.ts index f4315e3c55729ee0c88a644845c471c3190fcdd2..1f9b17a1da6f4e65461de93c8592da31de397395 100644 --- a/src/par/par_type_superactive.ts +++ b/src/par/par_type_superactive.ts @@ -2,8 +2,6 @@ import { Result } from "../util/result"; import { ParTypeSC } from "./par_type_sc"; import { ParType } from "./par"; import { ParParams } from "./par_params"; -import { Message, MessageCode } from "../util/message"; -import { ParamDefinition } from "../param/param-definition"; export class ParTypeSuperactive extends ParTypeSC { diff --git a/src/param/params-equation.ts b/src/param/params-equation.ts index c40a8d540e47bc28818b6fe2b73729535d5e4a15..5e2549fdf4561bf92ddb1e75901ef6fff81da188 100644 --- a/src/param/params-equation.ts +++ b/src/param/params-equation.ts @@ -15,9 +15,6 @@ export abstract class ParamsEquation implements Iterable<ParamDefinition> { protected _paramMap: { [key: string]: ParamDefinition } = {}; - /** Précision de calcul */ - private _Pr: ParamDefinition; - public constructor(parent?: ComputeNode) { this.parent = parent; } diff --git a/src/prebarrage/pb_bassin.ts b/src/prebarrage/pb_bassin.ts new file mode 100644 index 0000000000000000000000000000000000000000..4242e202af6d527eb6c675199260704e9bbd7660 --- /dev/null +++ b/src/prebarrage/pb_bassin.ts @@ -0,0 +1,121 @@ +import { Nub } from "../nub"; +import { Result } from "../util/result"; +import { PbBassinParams } from "./pb_bassin_params"; +import { ParamCalculability } from "../param/param-definition"; +import { PbCloison } from "./pb_cloison"; +import { CalculatorType } from "../compute-node"; +import { PreBarrage } from "./pre_barrage"; +import { Message, MessageCode } from "../util/message"; + +export interface IPbBassinZstat{ + moy: number, + min: number, + max: number +} +export class PbBassin extends Nub { + + /** Liste des cloisons amont */ + public cloisonsAmont: PbCloison[]; + + /** Liste des cloisons aval */ + public cloisonsAval: PbCloison[]; + + public parent: PreBarrage; + + /** Débit transitant dans le bassin en m³/s */ + public Q: number; + + /** Cote de l'eau dans le bassin en m */ + public Z: number; + + constructor(prms: PbBassinParams, dbg: boolean = false) { + super(prms, dbg); + this._calcType = CalculatorType.PbBassin; + this.cloisonsAmont = []; + this.cloisonsAval = []; + } + + /** + * paramètres castés au bon type + */ + get prms(): PbBassinParams { + return this._prms as PbBassinParams; + } + + public Calc(sVarCalc?: string | any, rInit?: number): Result { + // if Calc() is called outside of CalcSerie(), _result might not be initialized + if (! this.result) { + this.initNewResultElement(); + } + const r = this.result; + + // Ajout du calcul de la puissance dissipée + const prms = this.getParamValuesAfterCalc(sVarCalc, r); + let sumQ = 0; // sum of Q in upstream walls + for (const w of this.cloisonsAmont) { + sumQ += w.prms.Q.v; + } + const ro: number = 1000; // masse volumique de l'eau en kg/m3 + const g: number = 9.81; // accélération de la gravité terrestre en m/s2. + r.resultElement.values.PV = ro * g * sumQ * (this.Z - prms.ZF) / (prms.S); + + r.resultElement.values.Z = this.CalcZ().moy; + r.resultElement.values.Q = this.CalcQ(); + r.resultElement.values.YMOY = r.resultElement.values.Z - this.prms.ZF.V; + + return r; + } + + public Equation(sVarCalc: string): Result { + switch(sVarCalc) { + case "Q": + case "Z": + const r = new Result(); + r.values.Q = this.CalcQ(); + r.values.Z = this.CalcZ().moy; + return r; + default: + throw new Error("PbBassin.Equation() : invalid variable name " + sVarCalc); + } + } + + /** + * paramétrage de la calculabilité des paramètres + */ + protected setParametersCalculability() { + this.prms.S.calculability = ParamCalculability.FIXED; + this.prms.ZF.calculability = ParamCalculability.FIXED; + } + + public CalcQ(): number { + this.Q = 0; + for(const c of this.cloisonsAmont) { + this.Q += Math.max(0, c.prms.Q.v); + } + return this.Q; + } + + public CalcZ(): IPbBassinZstat { + const zStat = this.parent.CalcZ1Cloisons(this.cloisonsAval); + this.Z = zStat.moy; + return zStat; + } + + public getMinZDV(): number { + return this.parent.getMinZDV(this.cloisonsAval); + } + + /** Returns order of current basin in parent PreBarrage, starting at 1 */ + public get order(): number { + return this.parent.findBasinPosition(this.uid) + 1; + } + + /** + * Returns a translatable message for basin description, containing order number + */ + public get description(): Message { + return new Message(MessageCode.INFO_PB_BASSIN_DESCRIPTION, { + order: String(this.order) + }); + } +} \ No newline at end of file diff --git a/src/prebarrage/pb_bassin_params.ts b/src/prebarrage/pb_bassin_params.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4e94526d6a17505a4cd16372461343d3670e8f1 --- /dev/null +++ b/src/prebarrage/pb_bassin_params.ts @@ -0,0 +1,20 @@ +import { ParamsEquation } from "../param/params-equation"; +import { ParamDefinition, ParamFamily } from "../param/param-definition"; +import { ParamDomainValue } from "../param/param-domain"; + +export class PbBassinParams extends ParamsEquation { + + /** Surface du bassin en m2 */ + public S: ParamDefinition; + + /** Cote de fond du bassin en m */ + public ZF: ParamDefinition; + + constructor(rS: number, rZF: number) { + super(); + this.S = new ParamDefinition(this, "S", ParamDomainValue.POS, "m²", rS); + this.addParamDefinition(this.S); + this.ZF = new ParamDefinition(this, "ZF", ParamDomainValue.ANY, "m", rZF, ParamFamily.ELEVATIONS); + this.addParamDefinition(this.ZF); + } +} diff --git a/src/prebarrage/pb_cloison.ts b/src/prebarrage/pb_cloison.ts new file mode 100644 index 0000000000000000000000000000000000000000..1be292683fe6295eb07c5a6b7a943169591fd851 --- /dev/null +++ b/src/prebarrage/pb_cloison.ts @@ -0,0 +1,148 @@ +import { ParallelStructure } from "../structure/parallel_structure"; +import { PbBassin } from "./pb_bassin"; +import { PreBarrage } from "./pre_barrage"; +import { ParallelStructureParams } from "../structure/parallel_structure_params"; +import { CalculatorType } from "../compute-node"; +import { Result } from "../util/result"; +import { LoiDebit, loiAdmissiblesOuvrages } from "../structure/structure_props"; +import { Message, MessageCode } from "../util/message"; + +export class PbCloison extends ParallelStructure { + + /** pointer to parent Nub */ + public parent: PreBarrage; + + constructor(bassinAmont: PbBassin, bassinAval: PbBassin, dbg: boolean = false) { + super(new ParallelStructureParams(0,0,0), dbg); + // prevent multiple CALC params in session file + this.prms.Q.visible = false; + this.prms.Z1.visible = false; + this.prms.Z2.visible = false; + this.properties.setPropValue("upstreamBasin", bassinAmont === undefined ? "" : bassinAmont.uid); + this.properties.setPropValue("downstreamBasin", bassinAval === undefined ? "" : bassinAval.uid); + this._calcType = CalculatorType.PbCloison; + } + + /** Bassin à l'amont de la cloison ou undefined pour condition limite amont */ + public get bassinAmont(): PbBassin { + let basin: PbBassin; + const basinUID: string = this._props.getPropValue("upstreamBasin"); + if (basinUID !== undefined && basinUID !== "") { + basin = this.parent.findChild(basinUID) as PbBassin; + if (basin === undefined) { + // silent fail + } + } + return basin; + } + + /** sets the upstream basin by setting property "upstreamBasin" to the UID of the given PbBassin */ + public set bassinAmont(b: PbBassin) { + let uid = ""; // empty value + if (b !== undefined) { + uid = b.uid; + } + this.properties.setPropValue("upstreamBasin", uid); + this.parent.updatePointers(); + } + + /** Bassin à l'aval de la cloison ou undefined pour condition limite aval */ + public get bassinAval(): PbBassin { + let basin: PbBassin; + const basinUID: string = this._props.getPropValue("downstreamBasin"); + if (basinUID !== undefined && basinUID !== "") { + basin = this.parent.findChild(basinUID) as PbBassin; + if (basin === undefined) { + // silent fail + } + } + return basin; + } + + /** sets the downstream basin by setting property "downstreamBasin" to the UID of the given PbBassin */ + public set bassinAval(b: PbBassin) { + let uid = ""; // empty value + if (b !== undefined) { + uid = b.uid; + } + this.properties.setPropValue("downstreamBasin", uid); + this.parent.updatePointers(); + } + + public get Z1(): number { + if(this.bassinAmont !== undefined) { + return this.bassinAmont.Z; + } else { + return this.parent.prms.Z1.v; + } + } + + public get Z2(): number { + if(this.bassinAval !== undefined) { + return this.bassinAval.Z; + } else { + return this.parent.prms.Z2.v; + } + } + + public Calc(sVarCalc?: string, rInit?: number): Result { + this.updateZ1Z2(); + const r = super.Calc(sVarCalc, rInit); + switch(sVarCalc) { + case "Z1": + // Upstream water elevation should be at least equal minZDV + r.vCalc = Math.max(this.getMinZDV(), r.vCalc); + break; + + case "Q": + r.vCalc = Math.max(0, r.vCalc); + } + return r; + } + + public finalCalc() { + this.calculatedParam = this.prms.Q; + this.Calc("Q"); + // add Z1 & Z2 extra results for GUI convenience + this.result.resultElement.values.Z1 = this.prms.Z1.v; + this.result.resultElement.values.Z2 = this.prms.Z2.v; + } + + public updateZ1Z2() { + this.prms.Z1.v = this.Z1; + this.prms.Z2.v = this.Z2; + } + + /** + * Give the minimum ZDV on all structures. Undefined if only orifice without ZDV. + */ + public getMinZDV(): number { + let minZDV: number; + for(const s of this.structures) { + if(s.prms.ZDV.visible) { + if(minZDV === undefined) { + minZDV = s.prms.ZDV.v; + } else { + minZDV = Math.min(minZDV, s.prms.ZDV.v); + } + } + } + return minZDV; + } + + public getLoisAdmissibles(): { [key: string]: LoiDebit[]; } { + return loiAdmissiblesOuvrages; // @TODO loiAdmissiblesCloisons plutôt, mais penser à y intégrer Cunge80 + } + + /** + * Returns a translatable message for wall description, containing order numbers of + * upstream and downstream basin, or "upstream" / "downstream" mention if wall is + * directly connected to the river + */ + public get description(): Message { + return new Message(MessageCode.INFO_PB_CLOISON_DESCRIPTION, { + ub: this.bassinAmont === undefined ? "MSG_INFO_LIB_AMONT" : "B" + String(this.bassinAmont.order), + db: this.bassinAval === undefined ? "MSG_INFO_LIB_AVAL" : "B" + String(this.bassinAval.order), + }); + } +} diff --git a/src/prebarrage/pre_barrage.ts b/src/prebarrage/pre_barrage.ts new file mode 100644 index 0000000000000000000000000000000000000000..317b474f9bab8af5e815b780896ffd62c88555c1 --- /dev/null +++ b/src/prebarrage/pre_barrage.ts @@ -0,0 +1,561 @@ +import { Nub } from "../nub"; +import { ParamCalculability, ParamDefinition } from "../param/param-definition"; +import { Result } from "../util/result"; +import { PreBarrageParams } from "./pre_barrage_params"; +import { CalculatorType } from "../compute-node"; +import { PbBassin, IPbBassinZstat } from "./pb_bassin"; +import { PbCloison } from "./pb_cloison"; +import { SessionSettings } from "../session_settings"; +import { MessageCode, Message, MessageSeverity } from "../util/message"; +import { ResultElement } from "../util/resultelement"; + +export class PreBarrage extends Nub { + + /** Liste des cloisons amont */ + public cloisonsAmont: PbCloison[]; + + public maxIterations: number; + + /** Pointeurs vers les bassins dans this.children */ + private _bassins: PbBassin[]; + + /** Coefficient de relaxation pour la répartition des débits */ + private _relax: number; + + /** Precision du calcul (Min. 1E-4) */ + private _precision: number; + + constructor(prms: PreBarrageParams, dbg: boolean = false) { + super(prms, dbg); + this._calcType = CalculatorType.PreBarrage; + this.cloisonsAmont = []; + this._bassins = []; + this.maxIterations = SessionSettings.maxIterations; + } + + /** + * paramètres castés au bon type + */ + get prms(): PreBarrageParams { + return this._prms as PreBarrageParams; + } + + /** + * enfants castés au bon type + */ + get children(): (PbCloison | PbBassin)[] { + return this._children as (PbCloison | PbBassin)[]; + } + + get bassins(): PbBassin[] { + return this._bassins; + } + + /** + * Removes a child, along with all features (basins or walls) that are + * related only to it, and pointers to it + */ + public deleteChild(index: number) { + const item = this._children[index]; + if (item instanceof PbBassin) { + // reconnect walls to river upstream / downstream + for (const w of item.cloisonsAmont) { + w.bassinAval = undefined; + } + for (const w of item.cloisonsAval) { + w.bassinAmont = undefined; + } + } else if (item instanceof PbCloison) { + if (item.bassinAmont !== undefined) { + item.bassinAmont.cloisonsAval = item.bassinAmont.cloisonsAval.filter((v) => v !== item); + } + if (item.bassinAval !== undefined) { + item.bassinAval.cloisonsAmont = item.bassinAval.cloisonsAmont.filter((v) => v !== item); + } + } + // remove item + super.deleteChild(index); + this.updatePointers(); + } + + /** + * Returns true if at least one path is connecting river upstream to river downstream, + * using at least one basin (direct connection without basin doesn't count) + */ + public hasUpDownConnection(startWall?: PbCloison, nbBasins: number = 0, visited: PbCloison[] = []): boolean { + let ok = false; + // starting at river upstream ? + if (startWall === undefined) { + if (this.cloisonsAmont.length === 0) { + return false; + } + // browse graph downwards + for (const ca of this.cloisonsAmont) { + ok = ok || this.hasUpDownConnection(ca, 0, visited); + } + } else { + // starting from a wall + if (startWall.bassinAval === undefined) { + return (nbBasins > 0); + } else { + // browse graph downwards + for (const ca of startWall.bassinAval.cloisonsAval) { + // prevent loops @TODO detect loops instead, and throw an error ? + if (! visited.includes(ca)) { + visited.push(ca); + ok = ok || this.hasUpDownConnection(ca, nbBasins + 1, visited); + } + } + } + } + return ok; + } + + /** + * Returns true if at least one basin is lacking upstream or downstream connection (or both) + */ + public hasBasinNotConnected(): boolean { + for (const b of this._bassins) { + if (b.cloisonsAmont.length === 0 || b.cloisonsAval.length === 0) { + return true; + } + } + return false; + } + + /** + * Returns the child (basin or wall) having the given UID, along with its position + * among the _children array (*not* the _bassins array !) + */ + protected findChildAndPosition(uid: string): { child: PbBassin | PbCloison, position: number } { + const res: { child: PbBassin | PbCloison, position: number } = { child: undefined, position: undefined }; + let pos = 0; + for (const c of this.children) { + if (c.uid === uid) { + res.child = c; + res.position = pos; + } + pos++; + } + return res; + } + + /** + * Returns the child (basin or wall) having the given UID + */ + public findChild(uid: string): PbBassin | PbCloison { + const { child } = this.findChildAndPosition(uid); + return child; + } + + /** + * Returns the position of the child (basin or wall) having the given UID, + * among the _children array (*not* the _bassins array !) + */ + public findChildPosition(uid: string): number { + const { position } = this.findChildAndPosition(uid); + return position; + } + + /** + * Returns the position of the basin having the given UID, + * among the _bassins array (*not* the _children array !) + */ + public findBasinPosition(uid: string): number { + let i = 0; + for (const b of this._bassins) { + if (b.uid === uid) { + break; + } + i++; + } + return i; + } + + /** + * Moves the basin having the given UID, so that it becomes + * basin #newBasinNumber, by reordering _children array and + * rebuilding _bassins array afterwards + */ + public moveBasin(uid: string, newBasinNumber: number) { + if (newBasinNumber >= this._bassins.length) { + throw new Error(`PreBarrage.moveBasin: cannot make it #${newBasinNumber}, there are only ${this._bassins.length} basins`); + } + const tmp: Nub[] = []; + // find position of basin currently occupying #newBasinNumber, in _children array + const newPosition = this.findChildPosition(this._bassins[newBasinNumber].uid); + // find basin to move and its current position + const { child, position } = this.findChildAndPosition(uid); + if (position > newPosition) { + // move up: + // insert slice [0 - newPosition[ + for (let i = 0; i < newPosition; i++) { + tmp.push(this._children[i]); + } + // insert basin + tmp.push(child); + // insert slice [newPosition - position[ + for (let i = newPosition; i < position; i++) { + tmp.push(this._children[i]); + } + // insert slice ]position, end] + for (let i = position + 1; i < this._children.length; i++) { + tmp.push(this._children[i]); + } + this._children = tmp; + } else if (position < newPosition) { + // move down: + // @TODO should we also move down all walls connected to the moving basin, so that + // they are still declared after it and not before ? + + // insert slice [0 - position[ + for (let i = 0; i < position; i++) { + tmp.push(this._children[i]); + } + // insert slice ]position, newPosition[ + for (let i = position + 1; i < newPosition + 1; i++) { + tmp.push(this._children[i]); + } + // insert basin + tmp.push(child); + // insert slice [newPosition - end] + for (let i = newPosition + 1; i < this._children.length; i++) { + tmp.push(this._children[i]); + } + this._children = tmp; + } // else already at the right position, do nothing + this.updatePointers(); + } + + /** + * Effectue une série de calculs sur un paramètre; déclenche le calcul en chaîne + * des modules en amont si nécessaire + * @param rInit solution approximative du paramètre + */ + public CalcSerie(rInit?: number): Result { + this._precision = Math.max(5E-4, SessionSettings.precision); + return super.CalcSerie(rInit); + } + + public Calc(sVarCalc: string, rInit?: number): Result { + // check elevations before calculating + const cgResult = this.checkGeometry(); + if (cgResult.resultElement.hasErrorMessages()) { + this._result = cgResult; + return this.result; + } + + const res = super.Calc(sVarCalc, rInit); + // calculate basins so that they have a proper .result + for (const b of this._bassins) { + b.Calc(); + } + // calculate Q on all walls so that their result shows Q and not Z1 + for (const c of this._children) { + if (c instanceof PbCloison) { + c.finalCalc(); + } + } + + // copy warnings issued by checkGeometry, if any + for (const m of cgResult.resultElement.log.messages) { + if (m.getSeverity() !== MessageSeverity.ERROR) { + this._result.resultElement.log.add(m); + } + } + + return res; + } + + /** + * Calcul de Z1 pour une valeur fixe des paramètres + * @param sVarCalc ignoré: Z1 uniquement + */ + public Equation(sVarCalc: string): Result { + // Initialisation des cotes sur les bassins et la CL amont + if (this.isMeshed()) { + for(const b of this.bassins) { + const zB = b.getMinZDV(); + if(zB !== undefined) { + b.Z = Math.max(zB, b.prms.ZF.v) + 1; + } else { + b.Z = b.prms.ZF.v + 1; + } + } + const Z1 = this.getMinZDV(this.cloisonsAmont); + if(Z1 !== undefined) { + this.prms.Z1.v = Math.max(Z1, this.bassins[0].Z) + 1; + } else { + this.prms.Z1.v = this.bassins[0].Z + 1; + } + } else { + // On a le même débit dans tous les bassins et Cloisons + for (const child of this.children) { + if (child instanceof PbBassin) { + child.Q = this.prms.Q.v; + } else { + child.prms.Q.v = this.prms.Q.v; + } + } + } + let iter: number = this.maxIterations; + let bConverged; + // const tZ1: IPbBassinZstat[] = []; + // const tQ: number[][] = []; + while (iter-- > 0) { + this._relax = Math.pow(iter / this.maxIterations, 3) * 0.25 + 0.05; + this.debug(`***** Iteration n°${iter} relax=${this._relax} *****`); + bConverged = true; + if (this.isMeshed()) { + // Balayage amont-aval: distribution des débits + this.debug("*** Calcul des Q amont --> aval ***") + // Répartition cloisons amont du PB + // tQ.push(this.CalcQ(this.cloisonsAmont, this.prms.Q.v)); + this.CalcQ(this.cloisonsAmont, this.prms.Q.v); + // Répartition pour chaque bassin sens amont-aval + // TODO s'assurer du sens amont-aval des bassins dans this.children + for (const b of this.bassins) { + this.debug("Bassin n°" + this.getIndexForChild(b)) + this.CalcQ(b.cloisonsAval, b.CalcQ()); + } + } + // Balayage aval-amont: Calcul des cotes amont des cloisons + this.debug("*** Calcul des Z aval --> amont ***") + for (let i = this.bassins.length - 1; i >= 0; i--) { + this.debug("Bassin "+ i); + const zStat = this.bassins[i].CalcZ(); + bConverged = bConverged && (zStat.max - zStat.min) < this._precision; + } + this.debug("Cloison amont"); + const z1stat = this.CalcZ1Cloisons(this.cloisonsAmont); + this.prms.Z1.v = z1stat.moy; + // tZ1.push(z1stat); + bConverged = bConverged && (z1stat.max - z1stat.min) < this._precision + if(bConverged) { + break; + } + } + // console.debug(tQ); + // console.debug(tZ1); + const r = new Result(this.prms.Z1.v); + if(!bConverged) { + r.resultElement.addMessage(new Message(MessageCode.ERROR_PREBARRAGE_NON_CONVERGENCE, { + lastApproximation: this.prms.Z1.v + })); + } + return r; + } + + /** + * Checks pass geometry before calculation; adds relevant log messages + */ + protected checkGeometry(): Result { + // A. error throwing cases: cannot calculate + // each basin must have at least one upstream wall and one downstream wall + for (const b of this._bassins) { + if (b.cloisonsAmont.length === 0 || b.cloisonsAval.length === 0) { + throw new Error( + `PreBarrage.checkGeometry(): basin ${b.findPositionInParent()} (starting at 0) must have at least one upstream wall (has ${b.cloisonsAmont.length}) and one downstream wall (has ${b.cloisonsAval.length})` + ); + } + } + // PreBarrage must have an upstream wall and a downstream wall + if (this.cloisonsAmont.length === 0 /* || this.cloisonsAval.length === 0 */) { + throw new Error( + `PreBarrage.checkGeometry(): must have at least one upstream wall (has ${this.cloisonsAmont.length})` /* + ` and one downstream wall (has ${b.cloisonsAval.length})` */ + ); + } + // PreBarrage must have at least one path from upstream to downstream + if (! this.hasUpDownConnection()) { + throw new Error("PreBarrage.checkGeometry(): must have at least one path from upstream to downstream"); + } + + // B. messages generating cases: calculation goes on + const res = new Result(new ResultElement(), this); + // downstream water elevation > upstream water elevation ? + if (this.prms.Z2.v > this.prms.Z1.v) { + res.resultElement.log.add(new Message(MessageCode.ERROR_PREBARRAGE_Z2_SUP_Z1)); + } + // for each basin: is apron elevation > upstream water elevation ? + for (const b of this._bassins) { + if (b.prms.ZF.v > this.prms.Z1.v) { + const m = new Message(MessageCode.WARNING_PREBARRAGE_BASSIN_ZF_SUP_Z1); + m.extraVar.n = String(b.findPositionInParent() + 1); + res.resultElement.log.add(m); + } + } + // for each device of each wall: is ZDV < ZF of upstream basin ? + for (const b of this.bassins) { + for (const c of b.cloisonsAval) { + for (const s of c.structures) { + if (s.prms.ZDV.v < b.prms.ZF.v) { + const m = new Message(MessageCode.ERROR_PREBARRAGE_STRUCTURE_ZDV_INF_ZF); + m.extraVar.ns = String(s.findPositionInParent() + 1); + const desc = c.description; + m.extraVar.cub = desc.extraVar.ub; + m.extraVar.cdb = desc.extraVar.db; + res.resultElement.log.add(m); + } + } + } + } + return res; + } + + protected setParametersCalculability() { + this.prms.Z1.calculability = ParamCalculability.EQUATION; + this.prms.Z2.calculability = ParamCalculability.FREE; + this.prms.Q.calculability = ParamCalculability.DICHO; + } + + protected adjustChildParameters(child: (PbCloison | PbBassin)) { + this.updatePointers(); + } + + /** Clears basins list then builds it again fom children list */ + public updatePointers() { + this._bassins = []; + this.cloisonsAmont = []; + for (const c of this.children) { + if (c.calcType === CalculatorType.PbBassin) { + this._bassins.push(c as PbBassin); + } else if (c instanceof PbCloison) { + if (c.bassinAmont === undefined) { + this.cloisonsAmont.push(c); + } + if (c.bassinAmont !== undefined) { + if (! c.bassinAmont.cloisonsAval.includes(c)) { + c.bassinAmont.cloisonsAval.push(c); + } + } + if (c.bassinAval !== undefined) { + if (! c.bassinAval.cloisonsAmont.includes(c)) { + c.bassinAval.cloisonsAmont.push(c); + } + } + } + } + } + + private isMeshed(): boolean { + if (this.cloisonsAmont.length > 1) { + return true; + } + for (const bassin of this.bassins) { + if (bassin.cloisonsAval.length > 1) { + return true; + } + } + return false; + } + + private CalcQ(cloisons: PbCloison[], QT: number) { + // Calculation of repartition regarding actual water elevations + let QT2: number = 0; + for (const c of cloisons) { + c.prms.Q.initValue = c.prms.Q.v; + c.Calc("Q"); + // Relax! On ne prend pas toute la modification proposée ! + if(c.prms.Q.v > 0) { + c.prms.Q.v = (1 - this._relax) * c.prms.Q.initValue + this._relax * c.prms.Q.v; + QT2 += c.prms.Q.v; + } + if(this.DBG) {this.debug(`CalcQ: Q=${c.prms.Q.v} Z1=${c.prms.Z1.v} Z2=${c.prms.Z2.v}`);} + } + if(this.DBG) {this.debug(`CalcQ: QT=${QT} QT2=${QT2}`);} + // Adjustement of each Q in order to get the good sum + const adjustCoef = QT / QT2; + // const tQ: number[] = []; + for (const c of cloisons) { + if(c.prms.Q.v >= 0) { + if(QT2 !== 0) { + c.prms.Q.v = c.prms.Q.v * adjustCoef; + } else { + c.prms.Q.v = QT / cloisons.length; + } + } + // tQ.push(c.prms.Q.v) + this.debug("CalcQ: Qc=" + c.prms.Q.v); + } + // return tQ; + } + + public CalcZ1Cloisons(cloisons: PbCloison[]): IPbBassinZstat { + const zStat: IPbBassinZstat = {moy : 0, min: Infinity, max: -Infinity}; + let n: number = 0; + for (const c of cloisons) { + let Z1: number; + if(c.prms.Q.v >= 1E-6) { + Z1 = c.Calc("Z1").vCalc; + zStat.moy += Z1; + n++; + this.debug(`CalcZ1Cloisons: n°${n} Z1=${Z1} Q=${c.prms.Q.v} Z2=${c.prms.Z2.v}`); + } else { + // Nul flow in submerged flow: Z1 = Z2 + c.updateZ1Z2(); + if(c.prms.Z2.v > c.getMinZDV()) { + Z1 = c.prms.Z2.v; + zStat.moy += Z1; + n++; + } else { + c.prms.Q.v = 0; + } + } + if(Z1 !== undefined) { + zStat.min = Math.min(zStat.min, Z1); + zStat.max = Math.max(zStat.max, Z1); + } + } + if(n > 0) { + zStat.moy = zStat.moy / n; + } else { + // Nul flow on all cloisons which are all in free flow => Z1 = ZminZDV + zStat.moy = this.getMinZDV(cloisons) + zStat.min = zStat.moy; + zStat.max = zStat.moy; + } + this.debug(`CalcZ1Cloisons: Z= ${zStat.moy} [${(zStat.max-zStat.min)}]`); + return zStat; + } + + public getMinZDV(cloisons: PbCloison[]): number { + let minZDV: number; + for(const c of cloisons) { + const minZDVCloison = c.getMinZDV(); + if(minZDVCloison !== undefined) { + if(minZDV === undefined) { + minZDV = minZDVCloison + } else { + minZDV = Math.min(minZDV, minZDVCloison); + } + } + } + return minZDV; + } + + /** + * Fills the current Nub with parameter values, provided an object representation + * @param obj object representation of a Nub content (parameters) + * @returns the calculated parameter found, if any - used by child Nub to notify + * its parent of the calculated parameter to set + */ + public loadObjectRepresentation(obj: any): { p: ParamDefinition, hasErrors: boolean, changedUids: { [key: string]: string } } { + // return value + const ret = super.loadObjectRepresentation(obj); + // propagate changed UIDs to walls properties + for (const c of this._children) { + if (c instanceof PbCloison) { + for (const k of Object.keys(ret.changedUids)) { + // find basins having the changed UID + if (c.properties.props.upstreamBasin === k) { + c.bassinAmont = this.findChild(ret.changedUids[k]) as PbBassin; + } + if (c.properties.props.downstreamBasin === k) { + c.bassinAval = this.findChild(ret.changedUids[k]) as PbBassin; + } + } + } + } + return ret; + } +} diff --git a/src/prebarrage/pre_barrage_params.ts b/src/prebarrage/pre_barrage_params.ts new file mode 100644 index 0000000000000000000000000000000000000000..5caa0784ccbf487739e5c8b9945131d5a68eaaa9 --- /dev/null +++ b/src/prebarrage/pre_barrage_params.ts @@ -0,0 +1,25 @@ +import { ParamDefinition, ParamFamily } from "../param/param-definition"; +import { ParamDomainValue } from "../param/param-domain"; +import { ParamsEquation } from "../param/params-equation"; + +export class PreBarrageParams extends ParamsEquation { + + /** Débit entrant à l'amont de la passe (m3/s) */ + public Q: ParamDefinition; + + /** Cote de l'eau amont (m) */ + public Z1: ParamDefinition; + + /** Cote de l'eau aval (m) */ + public Z2: ParamDefinition; + + constructor(rQ: number, rZ1: number, rZ2: number) { + super(); + this.Q = new ParamDefinition(this, "Q", ParamDomainValue.POS_NULL, "m³/s", rQ, ParamFamily.FLOWS); + this.addParamDefinition(this.Q); + this.Z1 = new ParamDefinition(this, "Z1", ParamDomainValue.ANY, "m", rZ1, ParamFamily.ELEVATIONS); + this.addParamDefinition(this.Z1); + this.Z2 = new ParamDefinition(this, "Z2", ParamDomainValue.ANY, "m", rZ2, ParamFamily.ELEVATIONS); + this.addParamDefinition(this.Z2); + } +} diff --git a/src/session.ts b/src/session.ts index 6ca04b6029a8ecea1f85435a39590acfb0746729..dc54dd53b3644558c5035e745bbafe0ab426dc08 100644 --- a/src/session.ts +++ b/src/session.ts @@ -81,6 +81,11 @@ import { Espece } from "./verification/espece"; import { EspeceParams } from "./verification/espece_params"; import { Verificateur } from "./verification/verificateur"; import { DivingJetSupport } from "./verification/diving-jet-support"; +import { PreBarrage } from "./prebarrage/pre_barrage"; +import { PreBarrageParams } from "./prebarrage/pre_barrage_params"; +import { PbCloison } from "./prebarrage/pb_cloison"; +import { PbBassin } from "./prebarrage/pb_bassin"; +import { PbBassinParams } from "./prebarrage/pb_bassin_params"; export class Session { @@ -422,7 +427,7 @@ export class Session { case CalculatorType.Structure: const loiDebit: LoiDebit = params.getPropValue("loiDebit"); - nub = CreateStructure(loiDebit, (parentNub as ParallelStructure)); + nub = CreateStructure(loiDebit, (parentNub as ParallelStructure), dbg); break; case CalculatorType.ParallelStructure: @@ -535,7 +540,7 @@ export class Session { 0.4, // D 0.4, // k 1.2 // Cd0 - ) + ), dbg ); break; @@ -548,7 +553,7 @@ export class Session { 29.2, // ZW 28.5, // ZF 3 // D - ) + ), dbg ); break; @@ -571,7 +576,7 @@ export class Session { 0.5, // Ob 0.1, // OEntH 4 // cIncl - ) + ), dbg ); break; @@ -582,7 +587,7 @@ export class Session { 99.5, // Z2 10, // L 0.15 // I - ) + ), dbg ); break; @@ -656,7 +661,7 @@ export class Session { 5, // Nombre de motifs 4.9, // Largeur de la passe 0.35 // Diamètre des plots - ) + ), dbg ); break; @@ -673,7 +678,7 @@ export class Session { 0.1, // a 1, // N 1 // M - ) + ), dbg ); break; @@ -694,7 +699,17 @@ export class Session { 0.1, // a 1, // N 1 // M - ) + ), dbg + ); + break; + + case CalculatorType.PreBarrage: + nub = new PreBarrage( + new PreBarrageParams( + 1, // Q + 101, // Z1 + 100 // Z2 + ), dbg ); break; @@ -724,6 +739,17 @@ export class Session { nub = new Verificateur(); break; + case CalculatorType.PbBassin: + nub = new PbBassin(new PbBassinParams( + 10, // S + 100 // ZF + ), dbg); + break; + + case CalculatorType.PbCloison: + nub = new PbCloison(undefined, undefined); + break; + default: throw new Error( `Session.createNub() : type de module '${CalculatorType[calcType]}' non pris en charge` @@ -749,19 +775,15 @@ export class Session { * Returns true if given uid is already used by a Nub in this session, * or a Structure nub inside one of them */ - public uidAlreadyUsed(uid: string): boolean { + public uidAlreadyUsed(uid: string, nubs: Nub[] = this._nubs): boolean { let alreadyUsed = false; - outerLoop: - for (const n of this._nubs) { + for (const n of nubs) { if (n.uid === uid) { alreadyUsed = true; - break outerLoop; + break; } - for (const s of n.getChildren()) { - if (s.uid === uid) { - alreadyUsed = true; - break outerLoop; - } + if (! alreadyUsed) { + alreadyUsed = this.uidAlreadyUsed(uid, n.getChildren()); } } return alreadyUsed; diff --git a/src/structure/dever.ts b/src/structure/dever.ts index 764b90a25d96843ef6654a69a8bf6c074235353f..44efef9c86957cc1ae48d18aca6480b52718b040 100644 --- a/src/structure/dever.ts +++ b/src/structure/dever.ts @@ -82,7 +82,7 @@ export class Dever extends ParallelStructure { let r: Result; let i: number = SessionSettings.maxIterations; do { - if (r !== undefined && r.ok) { this.prms.Q.v = r.vCalc; } + if (r?.ok) { this.prms.Q.v = r.vCalc; } r = super.CalcQ(iExcept); i--; } while (i && Math.abs(r.vCalc - this.prms.Q.v) > SessionSettings.precision); diff --git a/src/structure/parallel_structure.ts b/src/structure/parallel_structure.ts index 25d720163a3f7625236296837af856e3698cc1a3..446bc1e0fa0a1d3f89a4ba4671792dc18ff13865 100644 --- a/src/structure/parallel_structure.ts +++ b/src/structure/parallel_structure.ts @@ -7,6 +7,7 @@ import { Result } from "../util/result"; import { ParallelStructureParams } from "./parallel_structure_params"; import { Structure } from "./structure"; import { loiAdmissiblesOuvrages, LoiDebit } from "./structure_props"; +import { MessageCode, Message } from "../util/message"; /** * Calcul de une ou plusieurs structures hydrauliques en parallèles @@ -62,6 +63,21 @@ export class ParallelStructure extends Nub { return lois[Object.keys(lois)[0]][0]; } + /** + * Effectue une série de calculs sur un paramètre; déclenche le calcul en chaîne + * des modules en amont si nécessaire + * Surcharge pour les tests préalables liés à la structure du nub + * @param rInit solution approximative du paramètre + */ + public CalcSerie(rInit?: number): Result { + if (this.structures.length === 0) { + this._result = new Result(undefined, this); + this._result.globalLog.insert(new Message(MessageCode.ERROR_STRUCTURE_AU_MOINS_UNE)); + return this._result; + } + return super.CalcSerie(rInit); + } + /** * Calcul du débit des structures en parallèle (sans détail pour chaque structure) * @param sVarCalc Variable à calculer (Q uniquement) diff --git a/src/structure/structure.ts b/src/structure/structure.ts index 1797f8a421bd09e531498eddd726b2ad5196f068..d222bef99dde983a007e2b2b5a78ca90f00b5947 100644 --- a/src/structure/structure.ts +++ b/src/structure/structure.ts @@ -5,7 +5,8 @@ import { Props } from "../props"; import { Message, MessageCode } from "../util/message"; import { Result } from "../util/result"; import { StructureParams } from "./structure_params"; -import { LoiDebit } from "./structure_props"; +import { LoiDebit, StructureProperties } from "./structure_props"; +import { ParallelStructure } from "./parallel_structure"; /** * Flow mode: weir or orifice flow @@ -75,11 +76,14 @@ export abstract class Structure extends ChildNub { /** Returns Props object (observable set of key-values) associated to this Nub */ public get properties(): Props { - // completes props with calcType and loiDebit if not already set + // completes props with calcType, structureType and loiDebit if not already set this._props.setPropValue("calcType", this.calcType); if (this._props.getPropValue("loiDebit") === undefined) { this._props.setPropValue("loiDebit", this._loiDebit); } + if (this._props.getPropValue("structureType") === undefined) { + this._props.setPropValue("structureType", StructureProperties.findCompatibleStructure(this._props.getPropValue("loiDebit"), this.parent as ParallelStructure)); + } return this._props; } @@ -338,4 +342,7 @@ export abstract class Structure extends ChildNub { }; } + public getFirstAnalyticalParameter() { + return this.prms.Q; + } } diff --git a/src/structure/structure_props.ts b/src/structure/structure_props.ts index 59794f0c0046c11f7175269e62c474bbdd5c42dc..3197ef4b5c891dd3e9f0b6a8b48017d639f07c4c 100644 --- a/src/structure/structure_props.ts +++ b/src/structure/structure_props.ts @@ -134,7 +134,7 @@ export class StructureProperties { /** * @return la 1ère valeur de StructureType compatible avec la loi de débit, dans le contexte - * du module de calcul parentNub + * du module de calcul parentNub @TODO la 1ère ? normalement il n'y en a qu'une ! */ public static findCompatibleStructure(loi: LoiDebit, parentNub: ParallelStructure): StructureType { const loisAdmissibles = parentNub.getLoisAdmissibles(); diff --git a/src/util/message.ts b/src/util/message.ts index 04dc71da0665a7e26ad87d474630f4cd12b57a9c..cef0877ac5c509e48cf9b5760d3988a76b55cdb0 100644 --- a/src/util/message.ts +++ b/src/util/message.ts @@ -343,6 +343,12 @@ export enum MessageCode { */ WARNING_REMOUS_ARRET_CRITIQUE, + /** Baqssin d'un PréBarrage : description (numéro d'ordre %order%) */ + INFO_PB_BASSIN_DESCRIPTION, + + /** Cloison d'un PréBarrage : description (bassin amont %ub%, bassin aval %db%) */ + INFO_PB_CLOISON_DESCRIPTION, + /** * courbe de remous : Condition limite aval >= Hauteur critique : calcul de la partie fluviale à partir de l'aval */ @@ -485,6 +491,11 @@ export enum MessageCode { */ ERROR_STRUCTURE_Z_EGAUX_Q_NON_NUL, + /** + * Il faut au moins un ouvrage dans une structure + */ + ERROR_STRUCTURE_AU_MOINS_UNE, + /** On essaye d'appliquer une puissance non entière à un nombre négatif */ ERROR_NON_INTEGER_POWER_ON_NEGATIVE_NUMBER, @@ -643,7 +654,21 @@ export enum MessageCode { INFO_PARENT_PREFIX, /** downwall : */ - INFO_PARENT_PREFIX_DOWNWALL + INFO_PARENT_PREFIX_DOWNWALL, + + /** + * Pré-barrage : non convergence du calcul + */ + ERROR_PREBARRAGE_NON_CONVERGENCE, + + /** Pré-barrage : cote de l'eau aval supérieure à la cote de l'eau amont */ + ERROR_PREBARRAGE_Z2_SUP_Z1, + + /** Pré-barrage : cote de fond du bassin %n% supérieure à la cote de l'eau amont */ + WARNING_PREBARRAGE_BASSIN_ZF_SUP_Z1, + + /** Pré-barrage : cote de radier de l'ouvrage %ns% inférieure à la cote de fond du bassin amont de la cloison %nc% */ + ERROR_PREBARRAGE_STRUCTURE_ZDV_INF_ZF } /**