@open3cl/engine 1.8.61 → 1.8.62

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.
@@ -252,7 +252,10 @@ export function calc_generateur_ch(
252
252
  /**
253
253
  * 15 Calcul des consommations d’auxiliaires des installations de chauffage (Caux_ch) et d’ECS (Caux_ecs)
254
254
  */
255
- if (hasConsoForAuxDistribution(de.enum_type_generateur_ch_id)) {
255
+ if (
256
+ hasConsoForAuxDistribution(de.enum_type_generateur_ch_id) ||
257
+ hasEmetteurAerauliqueLie(em_ch, de)
258
+ ) {
256
259
  conso_aux_distribution_ch(em_ch, de, di, du, Sh, zc_id, ca_id, ilpa, GV);
257
260
  }
258
261
  const paroi_ancienne = isParoisAncienneInertieLourde(dpe);
@@ -316,6 +319,34 @@ export function hasConsoForAuxDistribution(enum_type_generateur_ch_id) {
316
319
  );
317
320
  }
318
321
 
322
+ /** enum_type_emission_distribution_id 5 : réseau aéraulique (air soufflé) */
323
+ const TYPE_EMISSION_DISTRIBUTION_AERAULIQUE = 5;
324
+
325
+ /**
326
+ * 15.2 Auxiliaires de distribution d'un réseau aéraulique (émetteur 5).
327
+ *
328
+ * Le moteur de référence calcule les auxiliaires de distribution à partir du réseau desservi
329
+ * (ΔθDim = 15, Fcot = 0,802), et non du générateur : une PAC air/air (1 à 3) sur réseau
330
+ * aéraulique a donc des auxiliaires de distribution, bien que hasConsoForAuxDistribution l'exclue.
331
+ * L'émetteur est rattaché au générateur s'il est seul, ou par enum_lien_generateur_emetteur_id.
332
+ *
333
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §15.2
334
+ * @see Tribu Calcul_installation.cs (calcul_Cauxiliaires), Calcul_batiment.cs (Calcul_Cauxiliaires)
335
+ * @see https://github.com/Open3CL/engine/issues/291
336
+ * @param em_ch {EmetteurChauffageItem[]}
337
+ * @param de {Donnee_entree} donnée d'entrée du générateur de chauffage
338
+ * @returns {boolean}
339
+ */
340
+ export function hasEmetteurAerauliqueLie(em_ch, de) {
341
+ return (em_ch || []).some(
342
+ (em) =>
343
+ Number(em.donnee_entree.enum_type_emission_distribution_id) ===
344
+ TYPE_EMISSION_DISTRIBUTION_AERAULIQUE &&
345
+ (em_ch.length === 1 ||
346
+ em.donnee_entree.enum_lien_generateur_emetteur_id === de.enum_lien_generateur_emetteur_id)
347
+ );
348
+ }
349
+
319
350
  /**
320
351
  * 18.3 Cas des bâtiments à inertie lourde, constitués de parois anciennes
321
352
  *
@@ -68,7 +68,8 @@ const {
68
68
  type_generateur_ch,
69
69
  checkForGeneratorType,
70
70
  calc_generateur_ch,
71
- hasConsoForAuxDistribution
71
+ hasConsoForAuxDistribution,
72
+ hasEmetteurAerauliqueLie
72
73
  } = await import('./9_generateur_ch.js');
73
74
  const { requestInput, requestInputID, tv, tvColumnIDs } = await import('./utils.js');
74
75
  const { conso_aux_distribution_ch, conso_aux_gen } = await import('./15_conso_aux.js');
@@ -413,6 +414,47 @@ describe('hasConsoForAuxDistribution - éligibilité aux auxiliaires de distribu
413
414
  });
414
415
  });
415
416
 
417
+ /**
418
+ * 15.2 Réseau aéraulique (émetteur 5) rattaché au générateur : auxiliaires de distribution calculés
419
+ * quel que soit le générateur, comme le moteur de référence (#291).
420
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §15.2
421
+ */
422
+ describe('hasEmetteurAerauliqueLie - réseau aéraulique rattaché au générateur', () => {
423
+ const em = (ed, lien) => ({
424
+ donnee_entree: {
425
+ enum_type_emission_distribution_id: ed,
426
+ enum_lien_generateur_emetteur_id: lien
427
+ }
428
+ });
429
+
430
+ test('émetteur 5 seul : rattaché au générateur quel que soit le lien', () => {
431
+ expect(
432
+ hasEmetteurAerauliqueLie([em('5', '2')], { enum_lien_generateur_emetteur_id: '1' })
433
+ ).toBe(true);
434
+ });
435
+
436
+ test('identifiant numérique ou chaîne : même règle', () => {
437
+ expect(hasEmetteurAerauliqueLie([em(5)], {})).toBe(true);
438
+ });
439
+
440
+ test('émetteur 5 parmi plusieurs : rattaché si même lien générateur/émetteur', () => {
441
+ const de = { enum_lien_generateur_emetteur_id: '1' };
442
+ expect(hasEmetteurAerauliqueLie([em('1', '2'), em('5', '1')], de)).toBe(true);
443
+ });
444
+
445
+ test('émetteur 5 relié à un autre générateur : non rattaché', () => {
446
+ const de = { enum_lien_generateur_emetteur_id: '1' };
447
+ expect(hasEmetteurAerauliqueLie([em('1', '1'), em('5', '2')], de)).toBe(false);
448
+ });
449
+
450
+ test('aucun émetteur aéraulique ou liste absente : faux', () => {
451
+ expect(hasEmetteurAerauliqueLie([em('1'), em('3')], {})).toBe(false);
452
+ expect(hasEmetteurAerauliqueLie([em('4')], {})).toBe(false);
453
+ expect(hasEmetteurAerauliqueLie([], {})).toBe(false);
454
+ expect(hasEmetteurAerauliqueLie(undefined, {})).toBe(false);
455
+ });
456
+ });
457
+
416
458
  /**
417
459
  * calc_generateur_ch : aiguillage du rendement puis délégations.
418
460
  */
@@ -562,6 +604,33 @@ describe('calc_generateur_ch - orchestration du calcul générateur', () => {
562
604
  expect(conso_aux_distribution_ch).not.toHaveBeenCalled();
563
605
  });
564
606
 
607
+ /**
608
+ * PAC air/air (non éligible par son type) reliée à un réseau aéraulique : auxiliaires calculés (#291).
609
+ * @see https://github.com/Open3CL/engine/issues/291
610
+ */
611
+ test('PAC air/air sur réseau aéraulique (émetteur 5) : conso_aux_distribution_ch appelé', () => {
612
+ const gen = generateur({ enum_type_generateur_ch_id: '3' }, { isPacGenerator: true });
613
+ const em = { donnee_entree: { enum_type_emission_distribution_id: '5' } };
614
+ appel(gen, [em]);
615
+ expect(conso_aux_distribution_ch).toHaveBeenCalledWith(
616
+ [em],
617
+ gen.donnee_entree,
618
+ gen.donnee_intermediaire,
619
+ gen.donnee_utilisateur,
620
+ 100,
621
+ 1,
622
+ 1,
623
+ 0,
624
+ 150
625
+ );
626
+ });
627
+
628
+ test('PAC air/air sur émetteur non aéraulique : conso_aux_distribution_ch ignoré', () => {
629
+ const gen = generateur({ enum_type_generateur_ch_id: '3' }, { isPacGenerator: true });
630
+ appel(gen, [{ donnee_entree: { enum_type_emission_distribution_id: '1' } }]);
631
+ expect(conso_aux_distribution_ch).not.toHaveBeenCalled();
632
+ });
633
+
565
634
  test('bâtiment à parois anciennes et inertie lourde transmis à conso_ch', () => {
566
635
  vi.mocked(requestInputID).mockReturnValue('1');
567
636
  vi.mocked(tv).mockReturnValue({ rg: '0.9', tv_rendement_generation_id: '1' });
package/README.md CHANGED
@@ -375,28 +375,28 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
375
375
 
376
376
  <!-- CORPUS:START -->
377
377
 
378
- > **Version `1.8.60`** · branche `main` · généré le 2026-10-14
378
+ > **Version `1.8.61`** · branche `main` · généré le 2026-10-15
379
379
  > Seuil de tolérance **5%**
380
380
 
381
381
  <table>
382
382
  <tr>
383
383
  <td align="center"><strong>9</strong><br/><sub>corpus</sub></td>
384
384
  <td align="center"><strong>89 418</strong><br/><sub>DPE analysés</sub></td>
385
- <td align="center"><strong>70 429</strong><br/><sub>DPE conformes</sub></td>
386
- <td align="center"><strong>78,76 %</strong><br/><sub>réussite globale</sub></td>
385
+ <td align="center"><strong>70 789</strong><br/><sub>DPE conformes</sub></td>
386
+ <td align="center"><strong>79,17 %</strong><br/><sub>réussite globale</sub></td>
387
387
  </tr>
388
388
  </table>
389
389
 
390
390
  | | Corpus | Réussite | | DPE conformes |
391
391
  | :-: | :---------------------------------------------------------------------------------------------------------------------- | ----------: | :--------------------- | -------------: |
392
- | 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **47,68 %** | `██████████░░░░░░░░░░` | 4 768 / 9 999 |
392
+ | 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **47,71 %** | `██████████░░░░░░░░░░` | 4 771 / 9 999 |
393
+ | 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **93,28 %** | `███████████████████░` | 9 328 / 10 000 |
393
394
  | 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **92,96 %** | `███████████████████░` | 9 296 / 10 000 |
394
- | 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **92,11 %** | `██████████████████░░` | 9 211 / 10 000 |
395
- | 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **90,54 %** | `██████████████████░░` | 9 047 / 9 992 |
396
- | 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **88,91 %** | `██████████████████░░` | 8 890 / 9 999 |
397
- | 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **86,00 %** | `█████████████████░░░` | 8 440 / 9 814 |
395
+ | 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **90,69 %** | `██████████████████░░` | 9 062 / 9 992 |
396
+ | 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **88,92 %** | `██████████████████░░` | 8 891 / 9 999 |
397
+ | 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **88,19 %** | `██████████████████░░` | 8 655 / 9 814 |
398
398
  | 🟢 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **85,05 %** | `█████████████████░░░` | 8 503 / 9 998 |
399
- | 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **69,27 %** | `██████████████░░░░░░` | 6 661 / 9 616 |
399
+ | 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **69,36 %** | `██████████████░░░░░░` | 6 670 / 9 616 |
400
400
  | 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **56,13 %** | `███████████░░░░░░░░░` | 5 613 / 10 000 |
401
401
 
402
402
  <sub>🟢 ≥ 85 % · 🟡 ≥ 60 % · 🔴 < 60 %</sub>
@@ -408,24 +408,24 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
408
408
 
409
409
  <table>
410
410
  <tr>
411
- <td align="center"><strong>5,49 ms</strong><br/><sub>moyenne</sub></td>
412
- <td align="center"><strong>4,83 ms</strong><br/><sub>médiane</sub></td>
411
+ <td align="center"><strong>5,18 ms</strong><br/><sub>moyenne</sub></td>
412
+ <td align="center"><strong>4,55 ms</strong><br/><sub>médiane</sub></td>
413
413
  <td align="center"><strong>0,11 ms</strong><br/><sub>min</sub></td>
414
- <td align="center"><strong>626,5 ms</strong><br/><sub>max</sub></td>
414
+ <td align="center"><strong>994,3 ms</strong><br/><sub>max</sub></td>
415
415
  </tr>
416
416
  </table>
417
417
 
418
418
  | Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
419
419
  | :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
420
- | **Généraliste** | 4,75 ms | 4,25 ms | 0,22 ms | 31,6 ms | 8,80 ms | 12,7 ms |
421
- | **Appartement · chauffage individuel (2025)** | 4,03 ms | 3,72 ms | 1,44 ms | 27,8 ms | 6,78 ms | 8,70 ms |
422
- | **Appartement · chauffage collectif (2025)** | 4,43 ms | 4,04 ms | 1,64 ms | 36,9 ms | 7,23 ms | 9,29 ms |
423
- | **Logement individuel (2025)** | 5,40 ms | 4,87 ms | 1,68 ms | 34,9 ms | 9,89 ms | 13,4 ms |
424
- | **Maison individuelle (2025)** | 6,27 ms | 5,71 ms | 1,95 ms | 39,2 ms | 11,0 ms | 14,4 ms |
425
- | **Immeuble · chauffage collectif** | 5,76 ms | 5,03 ms | 0,19 ms | 117,5 ms | 10,5 ms | 17,5 ms |
426
- | **Immeuble · chauffage individuel** | 6,91 ms | 5,88 ms | 0,11 ms | 100,8 ms | 14,0 ms | 22,1 ms |
427
- | **Immeuble · chauffage mixte** | 5,58 ms | 4,54 ms | 0,21 ms | 626,5 ms | 11,5 ms | 19,7 ms |
428
- | **Individuel généré depuis l'immeuble (2026)** | 6,29 ms | 5,39 ms | 1,77 ms | 64,7 ms | 11,8 ms | 19,6 ms |
420
+ | **Généraliste** | 4,55 ms | 4,10 ms | 0,21 ms | 40,1 ms | 8,24 ms | 11,1 ms |
421
+ | **Appartement · chauffage collectif (2025)** | 4,51 ms | 4,14 ms | 1,59 ms | 31,4 ms | 7,33 ms | 9,53 ms |
422
+ | **Appartement · chauffage individuel (2025)** | 4,03 ms | 3,71 ms | 1,49 ms | 34,3 ms | 6,83 ms | 8,45 ms |
423
+ | **Logement individuel (2025)** | 4,85 ms | 4,34 ms | 1,66 ms | 30,8 ms | 8,92 ms | 11,8 ms |
424
+ | **Maison individuelle (2025)** | 5,54 ms | 5,04 ms | 1,83 ms | 50,4 ms | 9,68 ms | 12,7 ms |
425
+ | **Immeuble · chauffage collectif** | 5,54 ms | 4,80 ms | 0,12 ms | 92,2 ms | 10,1 ms | 15,7 ms |
426
+ | **Immeuble · chauffage individuel** | 6,45 ms | 5,48 ms | 0,11 ms | 120,4 ms | 12,9 ms | 20,5 ms |
427
+ | **Immeuble · chauffage mixte** | 5,36 ms | 4,35 ms | 0,18 ms | 994,3 ms | 11,0 ms | 18,0 ms |
428
+ | **Individuel généré depuis l'immeuble (2026)** | 5,80 ms | 4,96 ms | 1,77 ms | 53,2 ms | 10,8 ms | 17,9 ms |
429
429
 
430
430
  <sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
431
431
  dizaines de fois la médiane : c’est la médiane qui décrit le cas courant, et p95/p99 le cas
package/engine.js CHANGED
@@ -35,7 +35,7 @@ import DpeSanitizerService from './dpe-sanitizer.service.js';
35
35
  import { reconstituerRdimDpeGenereImmeuble } from './17.2_rdim_dpe_genere_immeuble.js';
36
36
  import { reconstituerRdimEcsIndividuelleUnique } from './11_rdim_ecs_individuelle_unique.js';
37
37
 
38
- const LIB_VERSION = '1.8.61';
38
+ const LIB_VERSION = '1.8.62';
39
39
 
40
40
  const dpeSanitizerService = new DpeSanitizerService();
41
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open3cl/engine",
3
- "version": "1.8.61",
3
+ "version": "1.8.62",
4
4
  "description": "Open Source 3CL-DPE engine",
5
5
  "main": "index.js",
6
6
  "directories": {