@open3cl/engine 1.8.55 → 1.8.57
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.
- package/9_conso_ch.js +11 -6
- package/9_conso_ch.spec.js +84 -0
- package/README.md +26 -26
- package/conso.js +16 -3
- package/conso.spec.js +3 -1
- package/engine.js +1 -1
- package/package.json +1 -1
package/9_conso_ch.js
CHANGED
|
@@ -277,10 +277,15 @@ function calc_ch_base_appoint(
|
|
|
277
277
|
const idx_ilpa = ilpa ? 1 : 0;
|
|
278
278
|
|
|
279
279
|
const dh14Saison = mois_liste.reduce((acc, mois) => acc + dh14[idx_ilpa][ca][mois][zc], 0);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
280
|
+
/**
|
|
281
|
+
* Part de l'immeuble desservie par l'installation : S_ratio / Rdim = Sh_chauffée / Stotal
|
|
282
|
+
* (Tribu Calcul_installation.cs l.79-86 : bch et Bch1_mensuel sont proratisés tous les deux).
|
|
283
|
+
* `bch` est déjà proratisé par calc_chauffage ; le besoin mensuel, calculé pour tout le
|
|
284
|
+
* bâtiment, doit l'être avec le même ratio, sinon la part de base de chaque installation est
|
|
285
|
+
* calculée sur le besoin de l'immeuble entier.
|
|
286
|
+
*/
|
|
287
|
+
const ratioSurface = s_chauffee_inst > 0 && Sh > 0 ? s_chauffee_inst / Sh : 1;
|
|
288
|
+
const t = 14 - (pe * dh14Saison) / bch;
|
|
284
289
|
|
|
285
290
|
di.conso_ch = 0;
|
|
286
291
|
for (const mois of mois_liste) {
|
|
@@ -310,10 +315,10 @@ function calc_ch_base_appoint(
|
|
|
310
315
|
if (ratioDh <= 0) {
|
|
311
316
|
bch_base_j = 0;
|
|
312
317
|
} else {
|
|
313
|
-
bch_base_j = besoin_ch_mois[mois] * ratioDh;
|
|
318
|
+
bch_base_j = besoin_ch_mois[mois] * ratioSurface * ratioDh;
|
|
314
319
|
}
|
|
315
320
|
} else {
|
|
316
|
-
bch_base_j = besoin_ch_mois[mois] * 0.5;
|
|
321
|
+
bch_base_j = besoin_ch_mois[mois] * ratioSurface * 0.5;
|
|
317
322
|
}
|
|
318
323
|
bch_base += bch_base_j;
|
|
319
324
|
}
|
package/9_conso_ch.spec.js
CHANGED
|
@@ -462,6 +462,90 @@ describe('conso_ch - installation collective base + appoint', () => {
|
|
|
462
462
|
expect(di.conso_ch).toBeCloseTo(0, 9);
|
|
463
463
|
});
|
|
464
464
|
|
|
465
|
+
/**
|
|
466
|
+
* Immeuble à plusieurs installations base + appoint : le besoin mensuel (calculé pour tout le
|
|
467
|
+
* bâtiment) est proratisé à la surface chauffée par l'installation, comme `bch`.
|
|
468
|
+
* Tribu Calcul_installation.cs l.79-86 : bch = Bch1 × S_ratio / Rdim et
|
|
469
|
+
* Bch1_mensuel × S_ratio / Rdim, avec S_ratio = Sh_chauffée × Rdim / Stotal.
|
|
470
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §9.8
|
|
471
|
+
*/
|
|
472
|
+
describe('installation desservant une partie de l’immeuble (Sh_installation < Sh)', () => {
|
|
473
|
+
function appelPartiel(pos, { pn = 0, Sh = 200, sInst = 100, bch = 25, rdim = 1 } = {}) {
|
|
474
|
+
const args = baseAppointArgs(pos, { pn });
|
|
475
|
+
const [di, de, _pos, cfg, em_list, GV] = args;
|
|
476
|
+
de.rdim = rdim;
|
|
477
|
+
const besoinCouvert = conso_ch(
|
|
478
|
+
di,
|
|
479
|
+
de,
|
|
480
|
+
{},
|
|
481
|
+
_pos,
|
|
482
|
+
cfg,
|
|
483
|
+
em_list,
|
|
484
|
+
GV,
|
|
485
|
+
Sh,
|
|
486
|
+
2.5,
|
|
487
|
+
bch,
|
|
488
|
+
bch,
|
|
489
|
+
-9.5,
|
|
490
|
+
0,
|
|
491
|
+
1,
|
|
492
|
+
1,
|
|
493
|
+
{ Janvier: 3000, Février: 2000 },
|
|
494
|
+
sInst,
|
|
495
|
+
args[16]
|
|
496
|
+
);
|
|
497
|
+
return { di, besoinCouvert };
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
test('pe = 0 : base = moitié du besoin mensuel de la seule part de l’installation', () => {
|
|
501
|
+
// Σ besoin mensuel immeuble = 5 kWh ; part de l'installation = 100 / 200 => base = 1,25 kWh
|
|
502
|
+
const { di, besoinCouvert } = appelPartiel(0);
|
|
503
|
+
expect(besoinCouvert).toBeCloseTo(1.25, 9);
|
|
504
|
+
// conso = base × Int (1) / (Rg 0,9 × Rendement émission 0,9)
|
|
505
|
+
expect(di.conso_ch).toBeCloseTo(1.25 / 0.81, 9);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
test('pe = 0 : appoint = besoin de l’installation moins la base proratisée', () => {
|
|
509
|
+
const { besoinCouvert } = appelPartiel(1);
|
|
510
|
+
expect(besoinCouvert).toBeCloseTo(25 - 1.25, 9);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test('base + appoint = besoin de l’installation, quelle que soit la part de surface', () => {
|
|
514
|
+
for (const sInst of [20, 100, 180]) {
|
|
515
|
+
const bch = (50 * sInst) / 200;
|
|
516
|
+
const base = appelPartiel(0, { sInst, bch }).besoinCouvert;
|
|
517
|
+
const appoint = appelPartiel(1, { sInst, bch }).besoinCouvert;
|
|
518
|
+
expect(base + appoint).toBeCloseTo(bch, 9);
|
|
519
|
+
expect(base).toBeLessThanOrEqual(bch);
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
test('le rdim de l’installation ne modifie pas la part de base (S_ratio / Rdim)', () => {
|
|
524
|
+
const sansRdim = appelPartiel(0, { pn: 5000 }).besoinCouvert;
|
|
525
|
+
const avecRdim = appelPartiel(0, { pn: 5000, rdim: 7 }).besoinCouvert;
|
|
526
|
+
expect(avecRdim).toBeCloseTo(sansRdim, 9);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
test('installation couvrant tout l’immeuble : comportement inchangé', () => {
|
|
530
|
+
// Sh = surface de l'installation => même résultat que le cas nominal
|
|
531
|
+
const { di } = appelPartiel(0, { Sh: 100, sInst: 100, bch: 50 });
|
|
532
|
+
expect(di.conso_ch).toBeCloseTo(3.0864197530864197, 9);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test('surface d’installation absente ou nulle : pas de proratisation du besoin mensuel', () => {
|
|
536
|
+
// Repli sur le besoin mensuel entier (ratio 1) : base = moitié de Σ besoin mensuel = 2,5 kWh
|
|
537
|
+
for (const sInst of [0, -1]) {
|
|
538
|
+
expect(appelPartiel(0, { sInst }).besoinCouvert).toBeCloseTo(2.5, 9);
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
test('surface habitable de l’immeuble nulle : pas de proratisation (aucune division par 0)', () => {
|
|
543
|
+
const { besoinCouvert } = appelPartiel(0, { Sh: 0 });
|
|
544
|
+
expect(Number.isFinite(besoinCouvert)).toBe(true);
|
|
545
|
+
expect(besoinCouvert).toBeCloseTo(2.5, 9);
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
|
|
465
549
|
test('logement collectif ILPA + valeurs par défaut (rdim, rendement de génération)', () => {
|
|
466
550
|
const di = { rg: 1, rg_dep: 1 };
|
|
467
551
|
// pas de rdim => rdim = 1 ; pas de rendement_generation => Ich = 1
|
package/README.md
CHANGED
|
@@ -370,57 +370,57 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
370
370
|
|
|
371
371
|
<!-- CORPUS:START -->
|
|
372
372
|
|
|
373
|
-
> **Version `1.8.
|
|
373
|
+
> **Version `1.8.56`** · branche `main` · généré le 2026-10-13
|
|
374
374
|
> Seuil de tolérance **5%**
|
|
375
375
|
|
|
376
376
|
<table>
|
|
377
377
|
<tr>
|
|
378
378
|
<td align="center"><strong>9</strong><br/><sub>corpus</sub></td>
|
|
379
|
-
<td align="center"><strong>89
|
|
380
|
-
<td align="center"><strong>
|
|
381
|
-
<td align="center"><strong>
|
|
379
|
+
<td align="center"><strong>89 418</strong><br/><sub>DPE analysés</sub></td>
|
|
380
|
+
<td align="center"><strong>66 706</strong><br/><sub>DPE conformes</sub></td>
|
|
381
|
+
<td align="center"><strong>74,60 %</strong><br/><sub>réussite globale</sub></td>
|
|
382
382
|
</tr>
|
|
383
383
|
</table>
|
|
384
384
|
|
|
385
385
|
| | Corpus | Réussite | | DPE conformes |
|
|
386
386
|
| :-: | :---------------------------------------------------------------------------------------------------------------------- | ----------: | :--------------------- | -------------: |
|
|
387
|
-
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **
|
|
387
|
+
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **47,08 %** | `█████████░░░░░░░░░░░` | 4 708 / 9 999 |
|
|
388
388
|
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **92,90 %** | `███████████████████░` | 9 290 / 10 000 |
|
|
389
|
-
| 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **91,
|
|
390
|
-
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **
|
|
391
|
-
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **88,
|
|
392
|
-
|
|
|
393
|
-
| 🟡 | **Immeuble · chauffage
|
|
394
|
-
| 🔴 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **
|
|
395
|
-
| 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **
|
|
389
|
+
| 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **91,72 %** | `██████████████████░░` | 9 172 / 10 000 |
|
|
390
|
+
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **90,47 %** | `██████████████████░░` | 9 040 / 9 992 |
|
|
391
|
+
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **88,90 %** | `██████████████████░░` | 8 889 / 9 999 |
|
|
392
|
+
| 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **85,04 %** | `█████████████████░░░` | 8 346 / 9 814 |
|
|
393
|
+
| 🟡 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **84,58 %** | `█████████████████░░░` | 8 456 / 9 998 |
|
|
394
|
+
| 🔴 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **59,86 %** | `████████████░░░░░░░░` | 5 756 / 9 616 |
|
|
395
|
+
| 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **30,49 %** | `██████░░░░░░░░░░░░░░` | 3 049 / 10 000 |
|
|
396
396
|
|
|
397
397
|
<sub>🟢 ≥ 85 % · 🟡 ≥ 60 % · 🔴 < 60 %</sub>
|
|
398
398
|
|
|
399
399
|
#### Temps d’exécution
|
|
400
400
|
|
|
401
|
-
> Durée de l’appel à `calcul_3cl` par DPE, sur 89
|
|
401
|
+
> Durée de l’appel à `calcul_3cl` par DPE, sur 89 384 calculs.
|
|
402
402
|
> La copie défensive de l’entrée et la lecture du fichier sont exclues de la mesure.
|
|
403
403
|
|
|
404
404
|
<table>
|
|
405
405
|
<tr>
|
|
406
|
-
<td align="center"><strong>5,
|
|
407
|
-
<td align="center"><strong>4,
|
|
408
|
-
<td align="center"><strong>0,
|
|
409
|
-
<td align="center"><strong>
|
|
406
|
+
<td align="center"><strong>5,09 ms</strong><br/><sub>moyenne</sub></td>
|
|
407
|
+
<td align="center"><strong>4,44 ms</strong><br/><sub>médiane</sub></td>
|
|
408
|
+
<td align="center"><strong>0,12 ms</strong><br/><sub>min</sub></td>
|
|
409
|
+
<td align="center"><strong>854,0 ms</strong><br/><sub>max</sub></td>
|
|
410
410
|
</tr>
|
|
411
411
|
</table>
|
|
412
412
|
|
|
413
413
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
414
414
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
415
|
-
| **Généraliste** |
|
|
416
|
-
| **Appartement · chauffage individuel (2025)** |
|
|
417
|
-
| **Appartement · chauffage collectif (2025)** | 4,
|
|
418
|
-
| **Logement individuel (2025)** |
|
|
419
|
-
| **Maison individuelle (2025)** |
|
|
420
|
-
| **Immeuble · chauffage
|
|
421
|
-
| **Immeuble · chauffage
|
|
422
|
-
| **Immeuble · chauffage mixte** |
|
|
423
|
-
| **Individuel généré depuis l'immeuble (2026)** | 6,
|
|
415
|
+
| **Généraliste** | 5,11 ms | 4,71 ms | 0,20 ms | 34,6 ms | 9,00 ms | 12,2 ms |
|
|
416
|
+
| **Appartement · chauffage individuel (2025)** | 3,79 ms | 3,44 ms | 1,48 ms | 33,9 ms | 6,57 ms | 8,40 ms |
|
|
417
|
+
| **Appartement · chauffage collectif (2025)** | 4,45 ms | 4,04 ms | 1,63 ms | 39,4 ms | 7,29 ms | 9,55 ms |
|
|
418
|
+
| **Logement individuel (2025)** | 4,68 ms | 4,12 ms | 1,66 ms | 46,7 ms | 8,62 ms | 12,6 ms |
|
|
419
|
+
| **Maison individuelle (2025)** | 5,24 ms | 4,81 ms | 1,94 ms | 50,8 ms | 8,85 ms | 11,8 ms |
|
|
420
|
+
| **Immeuble · chauffage collectif** | 4,99 ms | 4,28 ms | 0,12 ms | 99,4 ms | 8,96 ms | 14,0 ms |
|
|
421
|
+
| **Immeuble · chauffage individuel** | 5,73 ms | 4,88 ms | 0,12 ms | 98,8 ms | 11,1 ms | 18,4 ms |
|
|
422
|
+
| **Immeuble · chauffage mixte** | 4,87 ms | 3,97 ms | 0,18 ms | 854,0 ms | 9,80 ms | 16,0 ms |
|
|
423
|
+
| **Individuel généré depuis l'immeuble (2026)** | 6,94 ms | 5,65 ms | 1,82 ms | 88,8 ms | 15,2 ms | 25,7 ms |
|
|
424
424
|
|
|
425
425
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
426
426
|
dizaines de fois la médiane : c’est la médiane qui décrit le cas courant, et p95/p99 le cas
|
package/conso.js
CHANGED
|
@@ -317,6 +317,8 @@ function getConso(coef, type_energie, conso) {
|
|
|
317
317
|
* Arrêtés « réseaux de chaleur » et table de valeurs associée (modèle de données de l'observatoire
|
|
318
318
|
* DPE, `modele_donnee/arrete_reseau_chaleur.json`). Le nom de la table ne correspond pas à l'année
|
|
319
319
|
* de l'arrêté (ex. l'arrêté du 05/07/2024 publie la table `reseau_chaleur_2023`).
|
|
320
|
+
* Chaque arrêté remplace l'annexe 7 de l'arrêté du 15/09/2006 et entre en vigueur un mois après
|
|
321
|
+
* sa publication au JO (arrêté du 30/03/2026 : JO du 25/04/2026, applicable au 25/05/2026).
|
|
320
322
|
* - date_arrete : valeur attendue dans `date_arrete_reseau_chaleur` ;
|
|
321
323
|
* - date_application : date à partir de laquelle l'arrêté s'applique (DPE sans date d'arrêté).
|
|
322
324
|
*/
|
|
@@ -326,10 +328,15 @@ export const ARRETES_RESEAU_CHALEUR = [
|
|
|
326
328
|
{ table: 'reseau_chaleur_2022', date_arrete: '2023-03-16', date_application: '2023-04-16' },
|
|
327
329
|
{ table: 'reseau_chaleur_2023', date_arrete: '2024-07-05', date_application: '2024-08-09' },
|
|
328
330
|
{ table: 'reseau_chaleur_2024', date_arrete: '2025-04-11', date_application: '2025-05-26' },
|
|
329
|
-
{ table: 'reseau_chaleur_2025', date_arrete: '2026-
|
|
331
|
+
{ table: 'reseau_chaleur_2025', date_arrete: '2026-03-30', date_application: '2026-05-25' }
|
|
330
332
|
];
|
|
331
333
|
|
|
332
|
-
/**
|
|
334
|
+
/**
|
|
335
|
+
* Facteur « AUTRES RESEAUX DE CHALEUR » (réseau non répertorié ou inconnu), kgCO2/kWh.
|
|
336
|
+
* 0,385 dans les arrêtés des 21/10/2021, 16/03/2023, 05/07/2024, 11/04/2025 et 30/03/2026 : cette
|
|
337
|
+
* ligne peut être utilisée quel que soit le réseau ; le cas échéant, seule elle s'applique (art. 2 de
|
|
338
|
+
* l'arrêté du 21/10/2021, puis annexe 7 § IV). L'arrêté du 12/10/2020 ne contient pas cette ligne.
|
|
339
|
+
*/
|
|
333
340
|
export const COEF_GES_AUTRES_RESEAUX_CHALEUR = 0.385;
|
|
334
341
|
|
|
335
342
|
/**
|
|
@@ -422,7 +429,13 @@ export function getContenuCo2ReseauChaleur(
|
|
|
422
429
|
* Tribu (Calcul_batiment.cs l.1189) applique `contenu_CO2_RCU_ch`, transmis par le logiciel : LICIEL
|
|
423
430
|
* et Pleiades y placent le facteur de l'énergie des chaudières (« Taux CO2 fonction du système réel »).
|
|
424
431
|
* Le bois retient 0,03 (bûches / granulés), valeur exportée par la majorité des DPE LICIEL.
|
|
425
|
-
* Les PAC multi-bâtiment (112 / 77) ne sont pas couvertes
|
|
432
|
+
* Les PAC multi-bâtiment (112 / 77) ne sont volontairement pas couvertes et gardent 0,385
|
|
433
|
+
* (« autres réseaux de chaleur ») : Tribu n'a ni défaut ni table pour elles, le facteur vient du
|
|
434
|
+
* logiciel (`contenu_CO2_RCU_ch`, DPE.DataClasses/batiment.cs l.49). Sur les 44 DPE LICIEL en
|
|
435
|
+
* énergie 8 des corpus, 13 facteurs implicites distincts (0,227, 0, 0,09, 0,079, 0,104, 0,385…) ;
|
|
436
|
+
* 0,079 (électricité ch) n'en concerne que 5, et 0,079 comme 0,385 sortent du même moteur
|
|
437
|
+
* (tribu 1.4.25.1). Passer à 0,079 (PR #281, non fusionnée) gagnait 3 DPE et en cassait 2
|
|
438
|
+
* reproduits à l'identique (2394E2631674Y, 2549E3380026B).
|
|
426
439
|
*/
|
|
427
440
|
const ENERGIE_MULTI_BATIMENT = {
|
|
428
441
|
ch: { 109: 'bois – bûches', 110: 'fioul domestique', 111: 'gaz naturel' },
|
package/conso.spec.js
CHANGED
|
@@ -1277,7 +1277,9 @@ describe('réseaux de chaleur - sélection de l’arrêté et du contenu CO2', (
|
|
|
1277
1277
|
['2024-07-05', 3],
|
|
1278
1278
|
['2024-07-10', 3],
|
|
1279
1279
|
['2025-04-11', 4],
|
|
1280
|
-
['2026-
|
|
1280
|
+
['2026-03-29', 4], // veille de l'arrêté du 30/03/2026
|
|
1281
|
+
['2026-03-30', 5],
|
|
1282
|
+
['2026-04-25', 5], // date de publication au JO
|
|
1281
1283
|
['2030-01-01', 5],
|
|
1282
1284
|
['2019-01-01', 0]
|
|
1283
1285
|
])('date_arrete_reseau_chaleur %s : arrêté n°%i', (dateArrete, index) => {
|
package/engine.js
CHANGED
|
@@ -33,7 +33,7 @@ import getFicheTechnique from './ficheTechnique.js';
|
|
|
33
33
|
import { ProductionENR, ratioProrataPvLogement } from './16.2_production_enr.js';
|
|
34
34
|
import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
35
35
|
|
|
36
|
-
const LIB_VERSION = '1.8.
|
|
36
|
+
const LIB_VERSION = '1.8.57';
|
|
37
37
|
|
|
38
38
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
39
39
|
|