@open3cl/engine 1.8.59 → 1.8.60

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.
@@ -183,6 +183,34 @@ function type_generateur_ecs(di, de, du, usage_generateur) {
183
183
  /** Rendement de génération d'une chaudière électrique (tv rendement_generation, id 30). */
184
184
  export const RG_CHAUDIERE_ELECTRIQUE = 0.97;
185
185
 
186
+ /**
187
+ * Chauffe-eau gaz / GPL à production instantanée 2001-2015 (types 66 et 113) : la méthode donne
188
+ * Pveil = 100 W, mais le moteur de référence (Tribu, Calcul_generateur.cs, Pveil1 = 0,01 kW pour
189
+ * Pn > 10 kW comme pour Pn ≤ 10 kW) applique 10 W. Les DPE exportent alors pveilleuse = 10 (W) ou
190
+ * 0,01 (kW). Ce cas n'est reproduit qu'en mode bug_for_bug_compat.
191
+ * @see Methode_de_calcul_3CL_DPE_2021-338.pdf - §14.1.1
192
+ * @see https://github.com/Open3CL/engine/issues/289
193
+ */
194
+ export const PVEIL_CHAUFFE_EAU_GAZ_INSTANTANE_TRIBU = 10;
195
+ const TYPES_CHAUFFE_EAU_GAZ_INSTANTANE_2001_2015 = ['66', '113'];
196
+ const PVEILLEUSES_EXPORTEES_TRIBU = [10, 0.01];
197
+
198
+ /**
199
+ * Indique si la puissance de veilleuse du générateur doit être ramenée à la valeur Tribu (10 W) :
200
+ * chauffe-eau gaz / GPL instantané 2001-2015, veilleuse de la méthode (100 W) retenue, et
201
+ * pveilleuse exportée par le DPE égale à 10 W ou 0,01 kW.
202
+ * @param typeGenerateurId {string|number} enum_type_generateur_ecs_id
203
+ * @param di {Donnee_intermediaire}
204
+ * @returns {boolean}
205
+ */
206
+ export function isPveilChauffeEauGazInstantaneTribu(typeGenerateurId, di) {
207
+ return (
208
+ TYPES_CHAUFFE_EAU_GAZ_INSTANTANE_2001_2015.includes(String(typeGenerateurId)) &&
209
+ di.pveil === 100 &&
210
+ PVEILLEUSES_EXPORTEES_TRIBU.includes(Number(di.pveilleuse))
211
+ );
212
+ }
213
+
186
214
  export function rg_chauffe_eau_gaz(di, besoin_ecs) {
187
215
  return (
188
216
  1 /
@@ -405,6 +433,13 @@ export default function calc_gen_ecs(dpe, gen_ecs, ecs_di, ecs_de, GV, ca_id, zc
405
433
  di.pveil = 0;
406
434
  }
407
435
 
436
+ if (bug_for_bug_compat && isPveilChauffeEauGazInstantaneTribu(type_generateur_id, di)) {
437
+ console.warn(
438
+ `Correction pveil pour le générateur ECS ${de.description} : ${PVEIL_CHAUFFE_EAU_GAZ_INSTANTANE_TRIBU} W au lieu de ${di.pveil} W (moteur de référence)`
439
+ );
440
+ di.pveil = PVEIL_CHAUFFE_EAU_GAZ_INSTANTANE_TRIBU;
441
+ }
442
+
408
443
  const type_generateur = enums.type_generateur_ecs[type_generateur_id];
409
444
  if (
410
445
  type_generateur.includes('chauffe-eau gaz') ||
@@ -24,6 +24,8 @@ vi.mock('./enums.js', () => ({
24
24
  pac: 'pac double service',
25
25
  autre: 'générateur non implémenté',
26
26
  combautre: 'foyer fermé bois à combustion',
27
+ 66: 'chauffe-eau gaz à production instantanée 2001-2015',
28
+ 113: 'chauffe-eau gpl/propane/butane à production instantanée 2001-2015',
27
29
  84: 'chaudière fioul (système collectif par défaut)'
28
30
  },
29
31
  type_generateur_ch: { chaud_ch: 'chaudière gaz condensation' },
@@ -73,6 +75,8 @@ const {
73
75
  rgrs_chaudiere,
74
76
  rg_accumulateur_gaz,
75
77
  rgrsReseauUrbain,
78
+ isPveilChauffeEauGazInstantaneTribu,
79
+ PVEIL_CHAUFFE_EAU_GAZ_INSTANTANE_TRIBU,
76
80
  RG_CHAUDIERE_ELECTRIQUE,
77
81
  typeGenerateurPertesStockage
78
82
  } = await import('./14_generateur_ecs.js');
@@ -146,6 +150,39 @@ describe('rgrsReseauUrbain - rendement d’un réseau de chaleur', () => {
146
150
  });
147
151
  });
148
152
 
153
+ /**
154
+ * Veilleuse des chauffe-eau gaz / GPL instantanés 2001-2015 : 100 W selon la méthode, 10 W chez Tribu.
155
+ * @see Methode_de_calcul_3CL_DPE_2021-338.pdf - §14.1.1
156
+ * @see https://github.com/Open3CL/engine/issues/289
157
+ */
158
+ describe('isPveilChauffeEauGazInstantaneTribu - veilleuse 10 W du moteur de référence', () => {
159
+ test('valeur Tribu : 10 W', () => {
160
+ expect(PVEIL_CHAUFFE_EAU_GAZ_INSTANTANE_TRIBU).toBe(10);
161
+ });
162
+
163
+ test('types 66 (gaz) et 113 (GPL), pveil 100 W, pveilleuse exportée 10 W ou 0,01 kW : concerné', () => {
164
+ expect(isPveilChauffeEauGazInstantaneTribu('66', { pveil: 100, pveilleuse: 10 })).toBe(true);
165
+ expect(isPveilChauffeEauGazInstantaneTribu('66', { pveil: 100, pveilleuse: 0.01 })).toBe(true);
166
+ expect(isPveilChauffeEauGazInstantaneTribu(113, { pveil: 100, pveilleuse: '10' })).toBe(true);
167
+ });
168
+
169
+ test('autres chauffe-eau gaz instantanés (1990-2000, après 2015) : non concernés', () => {
170
+ expect(isPveilChauffeEauGazInstantaneTribu('65', { pveil: 100, pveilleuse: 10 })).toBe(false);
171
+ expect(isPveilChauffeEauGazInstantaneTribu('67', { pveil: 100, pveilleuse: 10 })).toBe(false);
172
+ });
173
+
174
+ test('pveilleuse exportée conforme à la méthode (100 W / 0,1 kW) ou absente : non concerné', () => {
175
+ expect(isPveilChauffeEauGazInstantaneTribu('66', { pveil: 100, pveilleuse: 100 })).toBe(false);
176
+ expect(isPveilChauffeEauGazInstantaneTribu('66', { pveil: 100, pveilleuse: 0.1 })).toBe(false);
177
+ expect(isPveilChauffeEauGazInstantaneTribu('66', { pveil: 100 })).toBe(false);
178
+ });
179
+
180
+ test('pveil retenue différente de 100 W (sans veilleuse, ou déjà 10 W) : non concerné', () => {
181
+ expect(isPveilChauffeEauGazInstantaneTribu('66', { pveil: 0, pveilleuse: 10 })).toBe(false);
182
+ expect(isPveilChauffeEauGazInstantaneTribu('66', { pveil: 10, pveilleuse: 10 })).toBe(false);
183
+ });
184
+ });
185
+
149
186
  /**
150
187
  * calc_gen_ecs : sélection de la branche de rendement selon le type d'énergie / générateur.
151
188
  */
@@ -271,6 +308,62 @@ describe('calc_gen_ecs - consommation par générateur', () => {
271
308
  expect(g.donnee_intermediaire.conso_ecs).toBeCloseTo(123.81056790123455, 9);
272
309
  });
273
310
 
311
+ describe('chauffe-eau gaz instantané 2001-2015 : veilleuse (#289)', () => {
312
+ function chauffeEau(type, pveilleuse) {
313
+ vi.mocked(tvColumnIDs).mockImplementation((table) =>
314
+ table === 'generateur_combustion' ? [type] : []
315
+ );
316
+ vi.mocked(tv_generateur_combustion).mockImplementation((dpe, di) => {
317
+ di.rpn = 0.84;
318
+ di.qp0 = 0;
319
+ di.pveil = 100;
320
+ di.pveilleuse = pveilleuse;
321
+ di.pn = 21000;
322
+ });
323
+ return gen({
324
+ type_energie: 'gaz naturel',
325
+ enum_type_generateur_ecs_id: type,
326
+ enum_methode_saisie_carac_sys_id: '1',
327
+ presence_veilleuse: 1
328
+ });
329
+ }
330
+
331
+ test('bug_for_bug_compat, pveilleuse exportée 10 W : Pveil = 10 W (Tribu)', () => {
332
+ state.bug = true;
333
+ const g = chauffeEau('66', 10);
334
+ calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
335
+
336
+ expect(g.donnee_intermediaire.pveil).toBe(10);
337
+ // valeur de référence de régression : 1 / (1 / 0.84 + 6970 * 10 / (100 * 1000))
338
+ expect(g.donnee_intermediaire.rendement_generation).toBeCloseTo(0.5298080076696016, 9);
339
+ });
340
+
341
+ test('bug_for_bug_compat, GPL (113), pveilleuse exportée 0,01 kW : Pveil = 10 W', () => {
342
+ state.bug = true;
343
+ const g = chauffeEau('113', 0.01);
344
+ calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
345
+
346
+ expect(g.donnee_intermediaire.pveil).toBe(10);
347
+ });
348
+
349
+ test('hors bug_for_bug_compat : Pveil de la méthode (100 W) conservée', () => {
350
+ const g = chauffeEau('66', 10);
351
+ calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
352
+
353
+ expect(g.donnee_intermediaire.pveil).toBe(100);
354
+ // valeur de référence de régression : 1 / (1 / 0.84 + 6970 * 100 / (100 * 1000))
355
+ expect(g.donnee_intermediaire.rendement_generation).toBeCloseTo(0.12254186847172784, 9);
356
+ });
357
+
358
+ test('bug_for_bug_compat, pveilleuse exportée 100 W : Pveil de la méthode conservée', () => {
359
+ state.bug = true;
360
+ const g = chauffeEau('66', 100);
361
+ calc_gen_ecs({}, g, ecs_di, ecs_de, 0, '1', '1', 'maison');
362
+
363
+ expect(g.donnee_intermediaire.pveil).toBe(100);
364
+ });
365
+ });
366
+
274
367
  test('installation solaire : la couverture solaire réduit la consommation', () => {
275
368
  ecs_de.enum_type_installation_solaire_id = '1';
276
369
  ecs_de.fecs_saisi = 0.5;
package/README.md CHANGED
@@ -375,15 +375,15 @@ 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.58`** · branche `main` · généré le 2026-10-14
378
+ > **Version `1.8.59`** · branche `main` · généré le 2026-10-14
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>69 329</strong><br/><sub>DPE conformes</sub></td>
386
- <td align="center"><strong>77,53 %</strong><br/><sub>réussite globale</sub></td>
385
+ <td align="center"><strong>70 263</strong><br/><sub>DPE conformes</sub></td>
386
+ <td align="center"><strong>78,58 %</strong><br/><sub>réussite globale</sub></td>
387
387
  </tr>
388
388
  </table>
389
389
 
@@ -394,9 +394,9 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
394
394
  | 🟢 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **91,72 %** | `██████████████████░░` | 9 172 / 10 000 |
395
395
  | 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **90,47 %** | `██████████████████░░` | 9 040 / 9 992 |
396
396
  | 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **88,90 %** | `██████████████████░░` | 8 889 / 9 999 |
397
- | 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **85,04 %** | `█████████████████░░░` | 8 346 / 9 814 |
398
- | 🟡 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **84,58 %** | `█████████████████░░░` | 8 456 / 9 998 |
399
- | 🔴 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **59,86 %** | `████████████░░░░░░░░` | 5 756 / 9 616 |
397
+ | 🟢 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **85,07 %** | `█████████████████░░░` | 8 349 / 9 814 |
398
+ | 🟢 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **85,03 %** | `█████████████████░░░` | 8 501 / 9 998 |
399
+ | 🟡 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **69,07 %** | `██████████████░░░░░░` | 6 642 / 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,46 ms</strong><br/><sub>moyenne</sub></td>
412
- <td align="center"><strong>4,80 ms</strong><br/><sub>médiane</sub></td>
411
+ <td align="center"><strong>5,52 ms</strong><br/><sub>moyenne</sub></td>
412
+ <td align="center"><strong>4,86 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>655,4 ms</strong><br/><sub>max</sub></td>
414
+ <td align="center"><strong>690,4 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,56 ms | 4,11 ms | 0,22 ms | 28,4 ms | 8,39 ms | 11,5 ms |
421
- | **Appartement · chauffage individuel (2025)** | 4,13 ms | 3,83 ms | 1,53 ms | 33,1 ms | 6,90 ms | 8,83 ms |
422
- | **Appartement · chauffage collectif (2025)** | 4,69 ms | 4,36 ms | 1,65 ms | 38,2 ms | 7,69 ms | 10,3 ms |
423
- | **Logement individuel (2025)** | 5,25 ms | 4,70 ms | 1,64 ms | 43,6 ms | 9,63 ms | 12,9 ms |
424
- | **Maison individuelle (2025)** | 6,17 ms | 5,56 ms | 1,99 ms | 53,3 ms | 11,0 ms | 14,5 ms |
425
- | **Immeuble · chauffage collectif** | 5,61 ms | 4,85 ms | 0,21 ms | 104,4 ms | 10,3 ms | 16,3 ms |
426
- | **Immeuble · chauffage individuel** | 6,70 ms | 5,71 ms | 0,11 ms | 164,2 ms | 13,6 ms | 21,3 ms |
427
- | **Immeuble · chauffage mixte** | 5,70 ms | 4,65 ms | 0,20 ms | 655,4 ms | 12,0 ms | 19,4 ms |
428
- | **Individuel généré depuis l'immeuble (2026)** | 6,33 ms | 5,45 ms | 1,65 ms | 53,8 ms | 11,9 ms | 19,5 ms |
420
+ | **Généraliste** | 4,67 ms | 4,22 ms | 0,22 ms | 39,6 ms | 8,37 ms | 11,6 ms |
421
+ | **Appartement · chauffage individuel (2025)** | 4,17 ms | 3,85 ms | 1,58 ms | 34,2 ms | 7,08 ms | 9,10 ms |
422
+ | **Appartement · chauffage collectif (2025)** | 4,75 ms | 4,46 ms | 1,67 ms | 37,7 ms | 7,67 ms | 10,1 ms |
423
+ | **Logement individuel (2025)** | 5,23 ms | 4,68 ms | 1,63 ms | 41,1 ms | 9,60 ms | 13,1 ms |
424
+ | **Maison individuelle (2025)** | 6,41 ms | 5,76 ms | 2,10 ms | 54,5 ms | 11,6 ms | 14,9 ms |
425
+ | **Immeuble · chauffage collectif** | 5,67 ms | 4,91 ms | 0,20 ms | 113,1 ms | 10,2 ms | 17,2 ms |
426
+ | **Immeuble · chauffage individuel** | 6,68 ms | 5,68 ms | 0,11 ms | 117,5 ms | 13,8 ms | 21,0 ms |
427
+ | **Immeuble · chauffage mixte** | 5,53 ms | 4,50 ms | 0,19 ms | 690,4 ms | 11,5 ms | 18,4 ms |
428
+ | **Individuel généré depuis l'immeuble (2026)** | 6,59 ms | 5,66 ms | 1,77 ms | 58,0 ms | 12,4 ms | 21,2 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.59';
38
+ const LIB_VERSION = '1.8.60';
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.59",
3
+ "version": "1.8.60",
4
4
  "description": "Open Source 3CL-DPE engine",
5
5
  "main": "index.js",
6
6
  "directories": {