@open3cl/engine 1.6.1 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/10_besoin_fr.spec.js +122 -0
  2. package/10_clim.spec.js +116 -0
  3. package/11_besoin_ecs.spec.js +69 -0
  4. package/11_ecs.spec.js +282 -0
  5. package/11_nadeq.spec.js +37 -0
  6. package/12.4_pac.spec.js +131 -0
  7. package/13.2_generateur_combustion.js +2 -0
  8. package/13.2_generateur_combustion.spec.js +537 -0
  9. package/13.2_generateur_combustion_bouilleur.js +4 -1
  10. package/13.2_generateur_combustion_bouilleur.spec.js +74 -0
  11. package/13.2_generateur_combustion_ch.spec.js +323 -0
  12. package/13.2_generateur_combustion_chaudiere.spec.js +85 -0
  13. package/13.2_generateur_pac.spec.js +64 -0
  14. package/13_rendement_distribution_ecs.spec.js +38 -0
  15. package/14_generateur_ecs.js +4 -4
  16. package/14_generateur_ecs.spec.js +745 -0
  17. package/15_conso_aux.spec.js +225 -0
  18. package/16.2_production_enr.spec.js +188 -0
  19. package/16_conso_eclairage.spec.js +62 -0
  20. package/2021_04_13_confort_ete.spec.js +159 -0
  21. package/2021_04_13_qualite_isolation.js +1 -1
  22. package/2021_04_13_qualite_isolation.spec.js +286 -0
  23. package/3.1_b.js +1 -1
  24. package/3.1_b.spec.js +367 -0
  25. package/3.2.1_mur.spec.js +812 -39
  26. package/3.2.2_plancher_bas.spec.js +643 -78
  27. package/3.2.3_plancher_haut.spec.js +452 -0
  28. package/3.3.1.4_porte.spec.js +114 -0
  29. package/3.3_baie_vitree.spec.js +449 -295
  30. package/3.4_pont_thermique.spec.js +890 -0
  31. package/3_deperdition.spec.js +397 -0
  32. package/4_ventilation.js +1 -1
  33. package/4_ventilation.spec.js +406 -0
  34. package/5_conso_ventilation.spec.js +186 -0
  35. package/6.2_surface_sud_equivalente.spec.js +138 -0
  36. package/8_intermittence.spec.js +33 -0
  37. package/9_besoin_ch.js +2 -2
  38. package/9_besoin_ch.spec.js +381 -0
  39. package/9_chauffage.js +4 -0
  40. package/9_chauffage.spec.js +478 -92
  41. package/9_conso_ch.spec.js +502 -248
  42. package/9_emetteur_ch.spec.js +339 -0
  43. package/9_generateur_ch.spec.js +467 -81
  44. package/apport_et_besoin.spec.js +126 -0
  45. package/conso.js +1 -1
  46. package/conso.spec.js +471 -82
  47. package/core/assets/domain/add-additionnal-ue-values-tables.spec.js +53 -0
  48. package/core/assets/domain/synchronize-solicitations-tables.spec.js +25 -0
  49. package/core/assets/domain/synchronize-valeur-tables.spec.js +48 -0
  50. package/core/conf/infrastructure/application.config.spec.js +35 -0
  51. package/core/file/infrastructure/adapter/csv-parser.store.spec.js +31 -13
  52. package/core/file/infrastructure/adapter/file.store.spec.js +148 -12
  53. package/core/tv/infrastructure/assets/additional-ue-values.spec.js +36 -0
  54. package/core/util/infrastructure/object-util.spec.js +20 -0
  55. package/core/util/logger/log-service.js +16 -7
  56. package/core/util/logger/log-service.spec.js +101 -0
  57. package/dpe-sanitizer.service.spec.js +138 -0
  58. package/engine.js +4 -1
  59. package/engine.spec.js +854 -0
  60. package/enums.spec.js +46 -0
  61. package/features/dpe/domain/models/type-generateur.model.js +1 -2
  62. package/features/dpe/domain/models/type-generateur.model.spec.js +25 -0
  63. package/features/dpe/domain/models/type-habitation.model.spec.js +41 -0
  64. package/features/dpe/domain/models/type-stockage.model.spec.js +18 -0
  65. package/features/dpe/domain/models/type-ventilation.model.spec.js +19 -0
  66. package/features/dpe/infrastructure/ch/chTv.store.spec.js +4 -4
  67. package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.js +14 -2
  68. package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.spec.js +16 -0
  69. package/features/dpe/infrastructure/tv.store.js +28 -9
  70. package/features/dpe/infrastructure/tv.store.spec.js +44 -0
  71. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +2 -1
  72. package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +18 -0
  73. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +33 -1
  74. package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +21 -1
  75. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +41 -1
  76. package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +44 -1
  77. package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +2 -1
  78. package/features/engine/domain/ch/generateur-ch.service.js +2 -0
  79. package/features/engine/domain/ch/generateur-ch.service.spec.js +117 -2
  80. package/features/engine/domain/ch/installation-ch.service.spec.js +14 -0
  81. package/features/engine/domain/conso/froid/conso-froid.service.spec.js +82 -1
  82. package/features/engine/domain/constants.spec.js +14 -0
  83. package/features/engine/domain/contexte.builder.spec.js +21 -0
  84. package/features/engine/domain/ecs/generateur-ecs.service.spec.js +11 -0
  85. package/features/engine/domain/ecs/installation-ecs.service.spec.js +55 -2
  86. package/features/engine/domain/engine.service.spec.js +227 -4
  87. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +124 -1
  88. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +270 -2
  89. package/features/engine/domain/enveloppe/deperdition.service.spec.js +141 -0
  90. package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +2 -1
  91. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +117 -1
  92. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +134 -1
  93. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +126 -1
  94. package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +18 -1
  95. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +2 -1
  96. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +67 -1
  97. package/features/normalizer/domain/dpe-normalizer.service.js +1 -1
  98. package/features/normalizer/domain/dpe-normalizer.service.spec.js +53 -2
  99. package/ficheTechnique.spec.js +26 -0
  100. package/index.spec.js +54 -0
  101. package/package.json +5 -2
  102. package/preprocessor/index.js +20 -0
  103. package/preprocessor/index.spec.js +15 -0
  104. package/tv.spec.js +38 -0
  105. package/utils.js +43 -1
  106. package/utils.spec.js +518 -2
  107. package/output.js +0 -1
package/index.spec.js ADDED
@@ -0,0 +1,54 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * `index.js` est un simple point d'entrée qui ré-exporte des fonctions/valeurs
5
+ * provenant de `engine.js` et `3_deperdition.js`. On mocke ces deux modules avec
6
+ * des identités reconnaissables afin de vérifier que chaque symbole est bien
7
+ * ré-exporté tel quel (même référence).
8
+ */
9
+ vi.mock('./engine.js', () => ({
10
+ calcul_3cl: vi.fn(),
11
+ calcul_3cl_xml: vi.fn(),
12
+ get_classe_ges_dpe: vi.fn(),
13
+ get_conso_coeff_1_9_2026: vi.fn(),
14
+ getVersion: vi.fn()
15
+ }));
16
+
17
+ vi.mock('./3_deperdition.js', () => ({
18
+ default: vi.fn(),
19
+ Umur: vi.fn(),
20
+ Uph: vi.fn(),
21
+ Upb: vi.fn(),
22
+ Uporte: vi.fn(),
23
+ Ubv: vi.fn(),
24
+ Upt: vi.fn()
25
+ }));
26
+
27
+ const index = await import('./index.js');
28
+ const engine = await import('./engine.js');
29
+ const deperdition = await import('./3_deperdition.js');
30
+
31
+ describe('index - ré-exports depuis engine.js', () => {
32
+ test.each(['calcul_3cl', 'calcul_3cl_xml', 'get_classe_ges_dpe', 'getVersion'])(
33
+ "ré-exporte %s à l'identique depuis engine.js",
34
+ (nom) => {
35
+ expect(index[nom]).toBe(engine[nom]);
36
+ expect(typeof index[nom]).toBe('function');
37
+ }
38
+ );
39
+ });
40
+
41
+ describe('index - ré-exports depuis 3_deperdition.js', () => {
42
+ test.each(['Umur', 'Uph', 'Upb', 'Uporte', 'Ubv', 'Upt'])(
43
+ "ré-exporte %s à l'identique depuis 3_deperdition.js",
44
+ (nom) => {
45
+ expect(index[nom]).toBe(deperdition[nom]);
46
+ expect(typeof index[nom]).toBe('function');
47
+ }
48
+ );
49
+
50
+ test("ne ré-exporte pas l'export par défaut de 3_deperdition.js", () => {
51
+ // index.js n'expose que les exports nommés Umur, Uph, ...
52
+ expect(index.default).toBeUndefined();
53
+ });
54
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open3cl/engine",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "Open Source 3CL-DPE engine",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -14,7 +14,10 @@
14
14
  "scripts": {
15
15
  "http-server": "http-server",
16
16
  "test": "vitest run",
17
- "test:ci": "vitest run --coverage",
17
+ "test:unit": "vitest run src",
18
+ "test:unit:ci": "vitest run src --coverage",
19
+ "test:int": "vitest run test",
20
+ "test:int:ci": "vitest run test --coverage",
18
21
  "test:corpus": "node test/corpus/run_all_corpus_checks.js",
19
22
  "test:corpus:all": "node test/corpus/run_all_corpus_checks.js",
20
23
  "test:corpus:all+database": "npm run test:corpus:all && node test/corpus/corpus_database.js",
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Pré-traitement du DPE d'entrée.
3
+ *
4
+ * Placeholder minimal : la migration vers la nouvelle architecture `features/` prévoit un
5
+ * pré-processeur chargé de redresser / compléter les données d'entrée avant calcul. Tant que
6
+ * cette étape n'est pas implémentée, `preprocess` est un no-op qui laisse le DPE inchangé.
7
+ *
8
+ * Ce module existe pour que {@link DpeNormalizerService} soit chargeable (son import était cassé,
9
+ * pointant vers un fichier inexistant). Il n'est pas câblé dans le moteur legacy (`engine.js`).
10
+ */
11
+ export class DpePreProcessor {
12
+ /**
13
+ * @param dpe {Dpe} DPE à pré-traiter (modifié sur place)
14
+ * @return {void}
15
+ */
16
+ preprocess(dpe) {
17
+ // no-op : voir la description du module
18
+ return dpe;
19
+ }
20
+ }
@@ -0,0 +1,15 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { DpePreProcessor } from './index.js';
3
+
4
+ describe('Pré-processeur du DPE (DpePreProcessor)', () => {
5
+ test('preprocess est un no-op : renvoie le DPE inchangé (même référence)', () => {
6
+ const preProcessor = new DpePreProcessor();
7
+ const dpe = { logement: { enveloppe: {} } };
8
+
9
+ const resultat = preProcessor.preprocess(dpe);
10
+
11
+ // Placeholder : le DPE est renvoyé tel quel, sans copie ni mutation.
12
+ expect(resultat).toBe(dpe);
13
+ expect(resultat).toEqual({ logement: { enveloppe: {} } });
14
+ });
15
+ });
package/tv.spec.js ADDED
@@ -0,0 +1,38 @@
1
+ import { describe, expect, test } from 'vitest';
2
+
3
+ /**
4
+ * `tv.js` ne contient que des données : le catalogue des tables de valeurs
5
+ * du DPE 3CL. On vérifie ici uniquement la structure des exports (export nommé
6
+ * `tvs` + export par défaut identique) et quelques invariants de forme, sans
7
+ * dépendre de la valeur métier des tables.
8
+ */
9
+ const { tvs, default: tvsDefault } = await import('./tv.js');
10
+
11
+ describe('tv - structure des exports', () => {
12
+ test('exporte un objet non vide `tvs`', () => {
13
+ expect(tvs).toBeTypeOf('object');
14
+ expect(tvs).not.toBeNull();
15
+ expect(Object.keys(tvs).length).toBeGreaterThan(0);
16
+ });
17
+
18
+ test("l'export par défaut est la même référence que l'export nommé", () => {
19
+ expect(tvsDefault).toBe(tvs);
20
+ });
21
+ });
22
+
23
+ describe('tv - forme des tables', () => {
24
+ test('la table coef_reduction_deperdition est un tableau de lignes indexées', () => {
25
+ const table = tvs.coef_reduction_deperdition;
26
+ expect(Array.isArray(table)).toBe(true);
27
+ expect(table.length).toBeGreaterThan(0);
28
+ // chaque ligne possède son identifiant de table de valeur
29
+ expect(table[0]).toHaveProperty('tv_coef_reduction_deperdition_id');
30
+ });
31
+
32
+ test('toutes les valeurs de premier niveau sont des objets (tableaux ou dictionnaires)', () => {
33
+ for (const [nom, table] of Object.entries(tvs)) {
34
+ expect(table, `table ${nom}`).toBeTypeOf('object');
35
+ expect(table, `table ${nom}`).not.toBeNull();
36
+ }
37
+ });
38
+ });
package/utils.js CHANGED
@@ -2,6 +2,7 @@ import enums from './enums.js';
2
2
  import tvs from './tv.js';
3
3
  import { set } from 'lodash-es';
4
4
  import { XMLParser } from 'fast-xml-parser';
5
+ import { evaluate } from 'mathjs';
5
6
 
6
7
  export const xmlParser = new XMLParser({
7
8
  // We want to make sure collections of length 1 are still parsed as arrays
@@ -473,6 +474,44 @@ export function containsAnySubstring(mainString, substrings) {
473
474
  );
474
475
  }
475
476
 
477
+ /**
478
+ * Évalue une formule de type tableur issue des tables de valeurs des générateurs à combustion
479
+ * (ex. `Pn * (E + F * logPn) / 100`, `84 + 2 logPn`).
480
+ *
481
+ * Une valeur numérique est retournée telle quelle. Sinon la formule est évaluée avec les
482
+ * variables suivantes :
483
+ * - `Pn` : puissance nominale exprimée en kW (l'argument `pn` est fourni en W, donc divisé
484
+ * par 1000),
485
+ * - `logPn` : logarithme décimal de `Pn`,
486
+ * - `E`, `F`: coefficients forfaitaires (présence ou non d'une ventouse).
487
+ *
488
+ * @param formulaOrValue {string|number} Formule tableur ou valeur numérique.
489
+ * @param pn {number} Puissance nominale en W.
490
+ * @param E {number} [Coefficient forfaitaire E]
491
+ * @param F {number} [Coefficient forfaitaire F]
492
+ * @returns {number} Résultat de l'évaluation.
493
+ */
494
+ export function excel_to_js_exec(formulaOrValue, pn, E, F) {
495
+ // Les formules des tables utilisent la virgule comme séparateur décimal (ex. `0,085`),
496
+ // à convertir en point pour être évaluées.
497
+ const formula =
498
+ typeof formulaOrValue === 'string'
499
+ ? formulaOrValue.replace(/(\d),(\d)/g, '$1.$2')
500
+ : formulaOrValue;
501
+
502
+ if (!isNaN(formula)) {
503
+ return Number(formula);
504
+ }
505
+
506
+ const Pn = pn / 1000;
507
+ return evaluate(formula, {
508
+ Pn,
509
+ logPn: Math.log10(Pn),
510
+ E,
511
+ F
512
+ });
513
+ }
514
+
476
515
  /**
477
516
  * Conversion des expressions du type `70 < Pn <= 400` en (70 < Pn) && (Pn <= 400)
478
517
  * @param expression {string}
@@ -504,7 +543,7 @@ export function getRange(inputNumber, ranges) {
504
543
 
505
544
  ranges.sort();
506
545
 
507
- if (inputNumber <= ranges[0]) {
546
+ if (inputNumber < ranges[0]) {
508
547
  result.push(ranges[0]);
509
548
  result.push(ranges[1]);
510
549
  return result;
@@ -519,11 +558,14 @@ export function getRange(inputNumber, ranges) {
519
558
  } else {
520
559
  ranges.find((range, index) => {
521
560
  if (inputNumber < range) {
561
+ /* c8 ignore start -- branche défensive inatteignable : inputNumber < ranges[0] provoque
562
+ déjà le retour anticipé en amont, donc index vaut toujours > 0 ici. */
522
563
  if (index > 0) {
523
564
  result.push(ranges[index - 1]);
524
565
  } else {
525
566
  result.push(ranges[index]);
526
567
  }
568
+ /* c8 ignore stop */
527
569
  result.push(ranges[index]);
528
570
  return true;
529
571
  }