@open3cl/engine 1.6.2 → 1.7.1

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 (51) hide show
  1. package/10_besoin_fr.spec.js +1 -0
  2. package/10_clim.spec.js +1 -0
  3. package/11_besoin_ecs.spec.js +1 -0
  4. package/11_ecs.spec.js +1 -0
  5. package/12.4_pac.spec.js +1 -0
  6. package/13.2_generateur_combustion.spec.js +3 -3
  7. package/13.2_generateur_combustion_ch.spec.js +3 -3
  8. package/14_generateur_ecs.spec.js +1 -0
  9. package/15_conso_aux.js +121 -1
  10. package/15_conso_aux.spec.js +329 -4
  11. package/2021_04_13_confort_ete.spec.js +1 -0
  12. package/3.1_b.spec.js +1 -0
  13. package/3.2.1_mur.spec.js +1 -0
  14. package/3.2.2_plancher_bas.spec.js +1 -0
  15. package/3.2.3_plancher_haut.spec.js +1 -0
  16. package/3.3.1.4_porte.spec.js +1 -0
  17. package/3.3_baie_vitree.spec.js +1 -0
  18. package/3.4_pont_thermique.spec.js +1 -0
  19. package/4_ventilation.spec.js +1 -0
  20. package/5_conso_ventilation.spec.js +1 -0
  21. package/6.2_surface_sud_equivalente.js +2 -1
  22. package/6.2_surface_sud_equivalente.spec.js +1 -0
  23. package/9_besoin_ch.spec.js +1 -0
  24. package/9_chauffage.spec.js +3 -3
  25. package/9_conso_ch.spec.js +1 -0
  26. package/9_emetteur_ch.spec.js +1 -0
  27. package/9_generateur_ch.spec.js +1 -0
  28. package/README.md +412 -291
  29. package/conso.js +20 -6
  30. package/conso.spec.js +21 -0
  31. package/engine.js +19 -1
  32. package/engine.spec.js +96 -0
  33. package/package.json +15 -14
  34. package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +0 -222
  35. package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +0 -345
  36. package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +0 -87
  37. package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +0 -260
  38. package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +0 -272
  39. package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +0 -231
  40. package/features/engine/domain/ch/generateur-ch.service.spec.js +0 -849
  41. package/features/engine/domain/conso/froid/conso-froid.service.spec.js +0 -233
  42. package/features/engine/domain/ecs/installation-ecs.service.spec.js +0 -337
  43. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +0 -607
  44. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +0 -550
  45. package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +0 -82
  46. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +0 -542
  47. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +0 -430
  48. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +0 -411
  49. package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +0 -653
  50. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +0 -117
  51. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +0 -508
@@ -19,6 +19,7 @@ vi.mock('./tv.js', () => ({
19
19
  }));
20
20
 
21
21
  vi.mock('./utils.js', () => ({
22
+ set_bug_for_bug_compat: vi.fn(),
22
23
  mois_liste: ['Janvier']
23
24
  }));
24
25
 
package/10_clim.spec.js CHANGED
@@ -8,6 +8,7 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
8
8
  * d'installation.
9
9
  */
10
10
  vi.mock('./utils.js', () => ({
11
+ set_bug_for_bug_compat: vi.fn(),
11
12
  tv: vi.fn(),
12
13
  requestInputID: (de, du, field) => {
13
14
  const enumName = `enum_${field}_id`;
@@ -14,6 +14,7 @@ vi.mock('./tv.js', () => ({
14
14
  }));
15
15
 
16
16
  vi.mock('./utils.js', () => ({
17
+ set_bug_for_bug_compat: vi.fn(),
17
18
  mois_liste: ['Janvier'],
18
19
  Njj: { Janvier: 30 }
19
20
  }));
package/11_ecs.spec.js CHANGED
@@ -19,6 +19,7 @@ vi.mock('./enums.js', () => ({
19
19
  }));
20
20
 
21
21
  vi.mock('./utils.js', () => ({
22
+ set_bug_for_bug_compat: vi.fn(),
22
23
  tv: vi.fn(),
23
24
  requestInput: vi.fn()
24
25
  }));
package/12.4_pac.spec.js CHANGED
@@ -7,6 +7,7 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
7
7
  * Le comportement de ces utilitaires n'est pas testé ici, seulement la logique du module.
8
8
  */
9
9
  vi.mock('./utils.js', () => ({
10
+ set_bug_for_bug_compat: vi.fn(),
10
11
  tv: vi.fn(),
11
12
  requestInputID: vi.fn((de, du, field) => {
12
13
  const enumName = `enum_${field}_id`;
@@ -14,6 +14,7 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
14
14
  const state = vi.hoisted(() => ({ bug: false }));
15
15
 
16
16
  vi.mock('./utils.js', () => ({
17
+ set_bug_for_bug_compat: vi.fn(),
17
18
  get bug_for_bug_compat() {
18
19
  return state.bug;
19
20
  },
@@ -51,9 +52,8 @@ vi.mock('./ficheTechnique.js', () => ({
51
52
  default: vi.fn()
52
53
  }));
53
54
 
54
- const { tv_generateur_combustion, updateGenerateurCombustion } = await import(
55
- './13.2_generateur_combustion.js'
56
- );
55
+ const { tv_generateur_combustion, updateGenerateurCombustion } =
56
+ await import('./13.2_generateur_combustion.js');
57
57
  const { tv, tvColumnLines } = await import('./utils.js');
58
58
  const { updateGenerateurBouilleurs } = await import('./13.2_generateur_combustion_bouilleur.js');
59
59
  const { updateGenerateurChaudieres } = await import('./13.2_generateur_combustion_chaudiere.js');
@@ -29,6 +29,7 @@ vi.mock('./enums.js', () => ({
29
29
  }));
30
30
 
31
31
  vi.mock('./utils.js', () => ({
32
+ set_bug_for_bug_compat: vi.fn(),
32
33
  get bug_for_bug_compat() {
33
34
  return state.bug;
34
35
  },
@@ -37,9 +38,8 @@ vi.mock('./utils.js', () => ({
37
38
  requestInputID: vi.fn((de, du, field) => de[`enum_${field}_id`])
38
39
  }));
39
40
 
40
- const { tv_temp_fonc_30_100, calc_generateur_combustion_ch } = await import(
41
- './13.2_generateur_combustion_ch.js'
42
- );
41
+ const { tv_temp_fonc_30_100, calc_generateur_combustion_ch } =
42
+ await import('./13.2_generateur_combustion_ch.js');
43
43
  const { tv } = await import('./utils.js');
44
44
 
45
45
  beforeEach(() => {
@@ -32,6 +32,7 @@ vi.mock('./enums.js', () => ({
32
32
  }));
33
33
 
34
34
  vi.mock('./utils.js', () => ({
35
+ set_bug_for_bug_compat: vi.fn(),
35
36
  get bug_for_bug_compat() {
36
37
  return state.bug;
37
38
  },
package/15_conso_aux.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import enums from './enums.js';
2
2
  import tvs from './tv.js';
3
- import { mois_liste, Tbase } from './utils.js';
3
+ import { mois_liste, Njj, Tbase } from './utils.js';
4
4
 
5
5
  const G_CHAUDIERE = 20;
6
6
  const G_RADIATEURS_GAZ = 40;
@@ -259,3 +259,123 @@ function getPuissanceCirculateur(em_ch, de, di, du, surfaceHabitable, GV, Tbase)
259
259
  Math.max(1, surfaceHabitable / 400)
260
260
  );
261
261
  }
262
+
263
+ /**
264
+ * 15.2.3 Consommation des auxiliaires de distribution d'ECS
265
+ * Calcul installation par installation pour le cas appartement
266
+ *
267
+ * SI installation individuelle : conso = 0
268
+ * SI installation collective :
269
+ * CAS 1 - enum_bouclage_reseau_ecs_id = 1 (non bouclé) : conso = 0
270
+ * CAS 2 - enum_bouclage_reseau_ecs_id = 2 (bouclé) : calcul selon étapes 1-9
271
+ * CAS 3 - enum_bouclage_reseau_ecs_id = 3 (traçage) : conso = 0.14 * BECS_annuel * Sh_install / Sh_logement
272
+ *
273
+ * @param ecs {object} installation ECS
274
+ * @param de {Donnee_entree} donnée d'entrée de l'installation ECS
275
+ * @param di {Donnee_intermediaire} donnée intermédiaire de l'installation ECS
276
+ * @param Sh_logement {number} surface habitable du logement
277
+ * @param Sh_immeuble {number} surface habitable de l'immeuble
278
+ * @param caId {number} id de la classe d'altitude
279
+ * @param zcId {number} id de la zone climatique
280
+ * @param nadeq {number} nombre d'unités d'équivalence
281
+ * @param nombre_niveau_immeuble {number} nombre de niveaux de l'immeuble
282
+ */
283
+ export function conso_aux_distribution_ecs(
284
+ ecs,
285
+ de,
286
+ di,
287
+ Sh_logement,
288
+ Sh_immeuble,
289
+ caId,
290
+ zcId,
291
+ nadeq,
292
+ nombre_niveau_immeuble
293
+ ) {
294
+ const typeInstallation = parseInt(de.enum_type_installation_id);
295
+
296
+ if (typeInstallation === 1) {
297
+ di.conso_auxiliaire_distribution_ecs = 0;
298
+ return;
299
+ }
300
+
301
+ const enumBouclage = parseInt(de.enum_bouclage_reseau_ecs_id);
302
+
303
+ // CAS 1 - enum_bouclage_reseau_ecs_id =1 (réseau d'ECS non bouclé)
304
+ if (enumBouclage === 1 || isNaN(enumBouclage)) {
305
+ di.conso_auxiliaire_distribution_ecs = 0;
306
+ return;
307
+ }
308
+
309
+ // CAS 3 - enum_bouclage_reseau_ecs_id = 3 (traçage)
310
+ if (enumBouclage === 3) {
311
+ const BECS_annuel = di.besoin_ecs;
312
+ const Sh_install = de.surface_habitable || Sh_logement;
313
+ di.conso_auxiliaire_distribution_ecs = (0.14 * BECS_annuel * Sh_install) / Sh_logement;
314
+ return;
315
+ }
316
+
317
+ // CAS 2 - enum_bouclage_reseau_ecs_id = 2 (réseau bouclé)
318
+ const ca = enums.classe_altitude[caId];
319
+ const zc = enums.zone_climatique[zcId];
320
+
321
+ const ratioVirtu = parseFloat(de.ratio_virtualisation);
322
+ let Sh_immeuble_ratio = Sh_immeuble;
323
+ let Sh_install = de.surface_habitable || Sh_logement;
324
+ let Niv_inst_ecs = de.nombre_niveau_installation_ecs || 1;
325
+
326
+ if (!(Sh_immeuble > Sh_logement) && ratioVirtu > 0 && ratioVirtu < 1) {
327
+ Sh_immeuble_ratio = Sh_logement / ratioVirtu;
328
+ Sh_install = Sh_logement;
329
+ Niv_inst_ecs = nombre_niveau_immeuble || Niv_inst_ecs;
330
+ }
331
+
332
+ // Etape 3: Lb - longueur par défaut du bouclage ECS (m)
333
+ // Sh est la surface habitable des logements desservis par l'installation d'ECS
334
+ // Pour l'appartement, on utilise la surface à l'échelle de l'immeuble
335
+ const Sh = (Sh_install / Sh_logement) * Sh_immeuble_ratio;
336
+ const Lb = 4 * Math.pow(Sh / Niv_inst_ecs, 0.5) + 6 * (Niv_inst_ecs - 0.5);
337
+
338
+ // Etape 4: DeltaPb (kPa) - perte de charge dans le bouclage
339
+ const DeltaPb = 0.2 * Lb + 10;
340
+
341
+ let conso = 0;
342
+
343
+ for (const mois of mois_liste) {
344
+ // Besoin ECS mensuel pour le logement (kWh)
345
+ // spec : https://rt-re-batiment.developpement-durable.gouv.fr/IMG/pdf/consolide_annexe_1_arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
346
+ // page 71-72
347
+ const tefsj = tvs.tefs[ca][mois][zc];
348
+ const njj = Njj[mois];
349
+ const BECS_j = (1.163 * nadeq * 56 * (40 - tefsj) * njj) / 1000;
350
+
351
+ // Etape 1: Qdwj_i (Wh) - pertes de distribution à l'échelle de l'immeuble
352
+ // Qd,w,j = (0.5 * Lvc / Sh + 0.112 + 0.028) * BECS_j
353
+ // Pour Ratecs=1 (bouclé): Lvc = 0.2 * Sh * Ratecs = 0.2 * Sh
354
+ // So Qd,w,j = (0.1 + 0.112 + 0.028) * BECS_j = 0.24 * BECS_j (kWh)
355
+ // Pour l'appartement: Qd,w,j est multiplié par Sh_immeuble_ratio / Sh_logement
356
+ const Qdwj_i =
357
+ (0.24 * BECS_j * 1000 * Sh_install * Sh_immeuble_ratio) / (Sh_logement * Sh_logement);
358
+
359
+ // Etape 2: qdwj_i (m³/h) - débit de distribution ECS
360
+ const Nh_puisage_j = njj * 5;
361
+ const qdwj_i = Qdwj_i / (5.815 * Nh_puisage_j) / 1000;
362
+
363
+ // Etape 5: Phyd,j (W) - puissance hydraulique du bouclage
364
+ const Phyd_j = (qdwj_i * DeltaPb) / 3.6;
365
+
366
+ // Etape 6: Effcirb,j - efficacité du circulateur
367
+ const Effcirb_j = Math.pow(Phyd_j, 0.324) / 15.3;
368
+
369
+ // Etape 7: Pcirb,j (W) - puissance électrique du circulateur
370
+ const Pcirb_j = Math.max(20, Phyd_j / Effcirb_j);
371
+
372
+ // Etape 8: Qcirb,j (Wh) - consommation mensuelle du circulateur
373
+ const Nh_mois_j = njj * 24;
374
+ const Qcirb_j = Nh_puisage_j * Pcirb_j + (Nh_mois_j - Nh_puisage_j) * 20;
375
+
376
+ conso += Qcirb_j;
377
+ }
378
+
379
+ // Etape 9: conso annuelle ramenée à l'appartement (kWh)
380
+ di.conso_auxiliaire_distribution_ecs = (conso * Sh_logement) / Sh_immeuble_ratio / 1000;
381
+ }
@@ -1,10 +1,13 @@
1
1
  import { beforeEach, describe, expect, test, vi } from 'vitest';
2
2
 
3
3
  /**
4
- * Dépendances mockées uniquement pour `conso_aux_distribution_ch` :
4
+ * Dépendances mockées pour `conso_aux_distribution_ch` et `conso_aux_distribution_ecs` :
5
5
  * - `enums` : libellés de classe d'altitude / zone climatique ;
6
6
  * - `tvs.nref19` : nombre d'heures de fonctionnement mensuel ;
7
- * - `mois_liste` / `Tbase` : réduits à des valeurs contrôlées.
7
+ * - `tvs.tefs` : température d'eau froide sanitaire mensuelle ;
8
+ * - `mois_liste` / `Njj` / `Tbase` : réduits à des valeurs contrôlées.
9
+ * L'année est réduite à un seul mois (Janvier, 31 jours) pour rendre les
10
+ * consommations attendues calculables à la main.
8
11
  * `conso_aux_gen` n'utilise aucune de ces dépendances (calcul autonome).
9
12
  */
10
13
  vi.mock('./enums.js', () => ({
@@ -16,16 +19,20 @@ vi.mock('./enums.js', () => ({
16
19
 
17
20
  vi.mock('./tv.js', () => ({
18
21
  default: {
19
- nref19: { 0: { ca1: { Janvier: { h1a: 100 } } } }
22
+ nref19: { 0: { ca1: { Janvier: { h1a: 100 } } } },
23
+ tefs: { ca1: { Janvier: { h1a: 10 } } }
20
24
  }
21
25
  }));
22
26
 
23
27
  vi.mock('./utils.js', () => ({
28
+ set_bug_for_bug_compat: vi.fn(),
24
29
  mois_liste: ['Janvier'],
30
+ Njj: { Janvier: 31 },
25
31
  Tbase: { ca1: { h1: -9 } }
26
32
  }));
27
33
 
28
- const { conso_aux_gen, conso_aux_distribution_ch } = await import('./15_conso_aux.js');
34
+ const { conso_aux_gen, conso_aux_distribution_ch, conso_aux_distribution_ecs } =
35
+ await import('./15_conso_aux.js');
29
36
 
30
37
  /**
31
38
  * 15.1 Consommation des auxiliaires de génération
@@ -223,3 +230,321 @@ describe('conso_aux_distribution_ch - auxiliaires de distribution', () => {
223
230
  expect(di.conso_auxiliaire_distribution_ch).toBeCloseTo(136.51553416032854, 9);
224
231
  });
225
232
  });
233
+
234
+ /**
235
+ * 15.2.3 Consommation des auxiliaires de distribution d'ECS
236
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §15.2.3
237
+ *
238
+ * Les scénarios ci-dessous s'appuient sur l'année réduite à Janvier (31 jours,
239
+ * tefs = 10 °C) définie par les mocks en tête de fichier :
240
+ * - Nh_puisage = 31 × 5 = 155 h
241
+ * - Nh_mois = 31 × 24 = 744 h
242
+ */
243
+ describe("conso_aux_distribution_ecs - auxiliaires de distribution d'ECS", () => {
244
+ // Consommation du circulateur maintenu à 20 W sur toutes les heures du mois (kWh)
245
+ const CONSO_PLANCHER_20W = (31 * 24 * 20) / 1000; // 14,88 kWh
246
+
247
+ test('installation individuelle : consommation nulle', () => {
248
+ const di = {};
249
+ conso_aux_distribution_ecs(
250
+ {},
251
+ { enum_type_installation_id: '1', enum_bouclage_reseau_ecs_id: '2' },
252
+ di,
253
+ 50,
254
+ 2000,
255
+ 1,
256
+ 1,
257
+ 30
258
+ );
259
+ expect(di.conso_auxiliaire_distribution_ecs).toBe(0);
260
+ });
261
+
262
+ test("réseau d'ECS non bouclé (id 1) : consommation nulle", () => {
263
+ const di = {};
264
+ conso_aux_distribution_ecs(
265
+ {},
266
+ { enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '1' },
267
+ di,
268
+ 50,
269
+ 2000,
270
+ 1,
271
+ 1,
272
+ 30
273
+ );
274
+ expect(di.conso_auxiliaire_distribution_ecs).toBe(0);
275
+ });
276
+
277
+ test('type de bouclage non renseigné : consommation nulle', () => {
278
+ const di = {};
279
+ conso_aux_distribution_ecs({}, { enum_type_installation_id: '2' }, di, 50, 2000, 1, 1, 30);
280
+ expect(di.conso_auxiliaire_distribution_ecs).toBe(0);
281
+ });
282
+
283
+ test('réseau avec traçage (id 3) : 0,14 × BECS × Sh_installation / Sh_logement', () => {
284
+ const di = { besoin_ecs: 1000 };
285
+ conso_aux_distribution_ecs(
286
+ {},
287
+ {
288
+ enum_type_installation_id: '2',
289
+ enum_bouclage_reseau_ecs_id: '3',
290
+ surface_habitable: 60
291
+ },
292
+ di,
293
+ 50,
294
+ 2000,
295
+ 1,
296
+ 1,
297
+ 30
298
+ );
299
+ // 0,14 × 1000 × 60 / 50
300
+ expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(168, 10);
301
+ });
302
+
303
+ test("traçage sans surface d'installation : repli sur la surface du logement", () => {
304
+ const di = { besoin_ecs: 1000 };
305
+ conso_aux_distribution_ecs(
306
+ {},
307
+ { enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '3' },
308
+ di,
309
+ 50,
310
+ 2000,
311
+ 1,
312
+ 1,
313
+ 30
314
+ );
315
+ // Sh_install = Sh_logement => 0,14 × 1000
316
+ expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(140, 10);
317
+ });
318
+
319
+ test('réseau bouclé, faible puissance hydraulique : plancher de 20 W', () => {
320
+ const di = {};
321
+ // Immeuble réduit au logement (18 m²) et nadeq faible : Phyd ≈ 0,07 W, donc
322
+ // Pcirb calculée (≈ 2,6 W) passe sous le plancher de 20 W tous les mois.
323
+ conso_aux_distribution_ecs(
324
+ {},
325
+ {
326
+ enum_type_installation_id: '2',
327
+ enum_bouclage_reseau_ecs_id: '2',
328
+ surface_habitable: 18,
329
+ nombre_niveau_installation_ecs: 1
330
+ },
331
+ di,
332
+ 18,
333
+ 18,
334
+ 1,
335
+ 1,
336
+ 1.15
337
+ );
338
+ expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(CONSO_PLANCHER_20W, 10);
339
+ });
340
+
341
+ test('réseau bouclé : consommation ramenée au prorata Sh_logement / Sh_immeuble', () => {
342
+ const di = {};
343
+ // Même régime de plancher (Pcirb ≈ 11 W) : la consommation immeuble (14,88 kWh)
344
+ // est ramenée à l'appartement, soit un dixième ici.
345
+ conso_aux_distribution_ecs(
346
+ {},
347
+ {
348
+ enum_type_installation_id: '2',
349
+ enum_bouclage_reseau_ecs_id: '2',
350
+ surface_habitable: 50,
351
+ nombre_niveau_installation_ecs: 1
352
+ },
353
+ di,
354
+ 50,
355
+ 500,
356
+ 1,
357
+ 1,
358
+ 0.5
359
+ );
360
+ expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo((CONSO_PLANCHER_20W * 50) / 500, 10);
361
+ });
362
+
363
+ test('réseau bouclé, circulateur au-dessus du plancher de 20 W', () => {
364
+ const di = {};
365
+ conso_aux_distribution_ecs(
366
+ {},
367
+ {
368
+ enum_type_installation_id: '2',
369
+ enum_bouclage_reseau_ecs_id: '2',
370
+ surface_habitable: 50,
371
+ nombre_niveau_installation_ecs: 2
372
+ },
373
+ di,
374
+ 50,
375
+ 2000,
376
+ 1,
377
+ 1,
378
+ 30
379
+ );
380
+ // valeur de référence de régression (Pcirb ≈ 549 W pendant les heures de puisage)
381
+ expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(2.4208575056559525, 9);
382
+ });
383
+
384
+ test('plus de niveaux desservis : bouclage plus court, donc consommation plus faible', () => {
385
+ const scenario = (Niv) => {
386
+ const di = {};
387
+ conso_aux_distribution_ecs(
388
+ {},
389
+ {
390
+ enum_type_installation_id: '2',
391
+ enum_bouclage_reseau_ecs_id: '2',
392
+ surface_habitable: 50,
393
+ nombre_niveau_installation_ecs: Niv
394
+ },
395
+ di,
396
+ 50,
397
+ 2000,
398
+ 1,
399
+ 1,
400
+ 30
401
+ );
402
+ return di.conso_auxiliaire_distribution_ecs;
403
+ };
404
+ // Lb = 4 × (Sh / Niv)^0,5 + 6 × (Niv − 0,5) : sur cet immeuble, Lb décroît avec Niv
405
+ expect(scenario(1)).toBeCloseTo(2.767218880573735, 9);
406
+ expect(scenario(4)).toBeCloseTo(2.2222299894932647, 9);
407
+ expect(scenario(4)).toBeLessThan(scenario(1));
408
+ });
409
+
410
+ test("réseau bouclé sans surface ni nombre de niveaux d'installation : valeurs par défaut", () => {
411
+ const diDefaut = {};
412
+ conso_aux_distribution_ecs(
413
+ {},
414
+ { enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '2' },
415
+ diDefaut,
416
+ 50,
417
+ 2000,
418
+ 1,
419
+ 1,
420
+ 30
421
+ );
422
+ const diExplicite = {};
423
+ // Sh_install = Sh_logement et Niv = 1
424
+ conso_aux_distribution_ecs(
425
+ {},
426
+ {
427
+ enum_type_installation_id: '2',
428
+ enum_bouclage_reseau_ecs_id: '2',
429
+ surface_habitable: 50,
430
+ nombre_niveau_installation_ecs: 1
431
+ },
432
+ diExplicite,
433
+ 50,
434
+ 2000,
435
+ 1,
436
+ 1,
437
+ 30
438
+ );
439
+ expect(diDefaut.conso_auxiliaire_distribution_ecs).toBeCloseTo(
440
+ diExplicite.conso_auxiliaire_distribution_ecs,
441
+ 10
442
+ );
443
+ });
444
+
445
+ test("installation desservant tout l'immeuble : Sh_installation étendue à l'immeuble", () => {
446
+ const di = {};
447
+ conso_aux_distribution_ecs(
448
+ {},
449
+ {
450
+ enum_type_installation_id: '2',
451
+ enum_bouclage_reseau_ecs_id: '2',
452
+ surface_habitable: 2000,
453
+ nombre_niveau_installation_ecs: 2
454
+ },
455
+ di,
456
+ 50,
457
+ 2000,
458
+ 1,
459
+ 1,
460
+ 30
461
+ );
462
+ // valeur de référence de régression
463
+ expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(72.84189246242042, 9);
464
+ });
465
+ });
466
+
467
+ /**
468
+ * 15.2.3 - Cas des DPE d'appartement dont `surface_habitable_immeuble` est
469
+ * renseignée avec la surface du logement : l'échelle de l'immeuble est alors
470
+ * reconstruite à partir du ratio de virtualisation de l'installation.
471
+ */
472
+ describe("conso_aux_distribution_ecs - surface d'immeuble incohérente", () => {
473
+ const RATIO_VIRTUALISATION = 0.0211;
474
+ const SH_LOGEMENT = 18;
475
+ // 18 / 0,0211 = 853,08 m²
476
+ const SH_IMMEUBLE_RECONSTRUITE = SH_LOGEMENT / RATIO_VIRTUALISATION;
477
+
478
+ /** @param de {object} @param Sh_immeuble {number} @param nbNiveauImmeuble {number} */
479
+ const consoEcs = (de, Sh_immeuble, nbNiveauImmeuble) => {
480
+ const di = {};
481
+ conso_aux_distribution_ecs(
482
+ {},
483
+ {
484
+ enum_type_installation_id: '2',
485
+ enum_bouclage_reseau_ecs_id: '2',
486
+ surface_habitable: SH_LOGEMENT,
487
+ nombre_niveau_installation_ecs: 1,
488
+ ...de
489
+ },
490
+ di,
491
+ SH_LOGEMENT,
492
+ Sh_immeuble,
493
+ 1,
494
+ 1,
495
+ 1.15,
496
+ nbNiveauImmeuble
497
+ );
498
+ return di.conso_auxiliaire_distribution_ecs;
499
+ };
500
+
501
+ test("surface d'immeuble égale à celle du logement : reconstruite via le ratio de virtualisation", () => {
502
+ const reconstruite = consoEcs(
503
+ { ratio_virtualisation: String(RATIO_VIRTUALISATION) },
504
+ SH_LOGEMENT,
505
+ 1
506
+ );
507
+
508
+ // Sans reconstruction, le prorata vaut 1 et le circulateur reste au plancher de 20 W
509
+ expect(reconstruite).not.toBeCloseTo((31 * 24 * 20) / 1000, 6);
510
+ expect(reconstruite).toBeCloseTo(0.45766695775765603, 9);
511
+
512
+ // Équivalent à une surface d'immeuble correctement renseignée
513
+ const renseignee = consoEcs({}, SH_IMMEUBLE_RECONSTRUITE, 1);
514
+ expect(reconstruite).toBeCloseTo(renseignee, 10);
515
+ });
516
+
517
+ test("reconstruction : le nombre de niveaux de l'immeuble prime sur celui de l'installation", () => {
518
+ // nombre_niveau_installation_ecs = 1 est la valeur du logement, incohérente
519
+ // avec la surface d'immeuble reconstruite
520
+ expect(
521
+ consoEcs({ ratio_virtualisation: String(RATIO_VIRTUALISATION) }, SH_LOGEMENT, 6)
522
+ ).toBeCloseTo(0.4237342712345093, 9);
523
+ });
524
+
525
+ test("reconstruction sans nombre de niveaux d'immeuble : repli sur celui de l'installation", () => {
526
+ expect(
527
+ consoEcs(
528
+ {
529
+ ratio_virtualisation: String(RATIO_VIRTUALISATION),
530
+ nombre_niveau_installation_ecs: 3
531
+ },
532
+ SH_LOGEMENT,
533
+ undefined
534
+ )
535
+ ).toBeCloseTo(0.4253220756819142, 9);
536
+ });
537
+
538
+ test('ratio de virtualisation égal à 1 : aucune reconstruction', () => {
539
+ expect(consoEcs({ ratio_virtualisation: '1' }, SH_LOGEMENT, 6)).toBeCloseTo(
540
+ (31 * 24 * 20) / 1000,
541
+ 10
542
+ );
543
+ });
544
+
545
+ test("surface d'immeuble cohérente : le ratio de virtualisation est ignoré", () => {
546
+ expect(
547
+ consoEcs({ ratio_virtualisation: String(RATIO_VIRTUALISATION) }, SH_IMMEUBLE_RECONSTRUITE, 6)
548
+ ).toBeCloseTo(consoEcs({}, SH_IMMEUBLE_RECONSTRUITE, 6), 10);
549
+ });
550
+ });
@@ -15,6 +15,7 @@ vi.mock('./enums.js', () => ({
15
15
  }));
16
16
 
17
17
  vi.mock('./utils.js', () => ({
18
+ set_bug_for_bug_compat: vi.fn(),
18
19
  getKeyByValue: (object, value) => value,
19
20
  requestInput: (de, du, field) => de[field]
20
21
  }));
package/3.1_b.spec.js CHANGED
@@ -14,6 +14,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
14
14
  const state = vi.hoisted(() => ({ bug: false }));
15
15
 
16
16
  vi.mock('./utils.js', () => ({
17
+ set_bug_for_bug_compat: vi.fn(),
17
18
  tv: vi.fn(),
18
19
  requestInputID: (de, du, field) => {
19
20
  const enumName = `enum_${field}_id`;
package/3.2.1_mur.spec.js CHANGED
@@ -24,6 +24,7 @@ vi.mock('./3.1_b.js', () => ({
24
24
  }));
25
25
 
26
26
  vi.mock('./utils.js', () => ({
27
+ set_bug_for_bug_compat: vi.fn(),
27
28
  tv: vi.fn(),
28
29
  requestInput: (de, du, field) => de[field],
29
30
  requestInputID: (de, du, field) => de[`enum_${field}_id`],
@@ -30,6 +30,7 @@ vi.mock('./3.1_b.js', () => ({
30
30
  }));
31
31
 
32
32
  vi.mock('./utils.js', () => ({
33
+ set_bug_for_bug_compat: vi.fn(),
33
34
  tv: vi.fn(),
34
35
  requestInput: (de, du, field) => de[field],
35
36
  getKeyByValue: (object, value) => Object.keys(object).find((key) => object[key] === value),
@@ -23,6 +23,7 @@ vi.mock('./3.1_b.js', () => ({
23
23
  }));
24
24
 
25
25
  vi.mock('./utils.js', () => ({
26
+ set_bug_for_bug_compat: vi.fn(),
26
27
  tv: vi.fn(),
27
28
  requestInput: (de, du, field) => de[field],
28
29
  getKeyByValue: (object, value) => Object.keys(object).find((key) => object[key] === value),
@@ -11,6 +11,7 @@ vi.mock('./3.1_b.js', () => ({
11
11
  }));
12
12
 
13
13
  vi.mock('./utils.js', () => ({
14
+ set_bug_for_bug_compat: vi.fn(),
14
15
  tv: vi.fn(),
15
16
  requestInput: (de, du, field) => de[field]
16
17
  }));
@@ -17,6 +17,7 @@ vi.mock('./3.1_b.js', () => ({
17
17
  }));
18
18
 
19
19
  vi.mock('./utils.js', () => ({
20
+ set_bug_for_bug_compat: vi.fn(),
20
21
  tv: vi.fn(),
21
22
  requestInput: (de, du, field) => de[field],
22
23
  requestInputID: (de, du, field) => de[`enum_${field}_id`],
@@ -27,6 +27,7 @@ vi.mock('lodash-es', () => ({
27
27
  const state = vi.hoisted(() => ({ bug: false }));
28
28
 
29
29
  vi.mock('./utils.js', () => ({
30
+ set_bug_for_bug_compat: vi.fn(),
30
31
  tv: vi.fn(),
31
32
  requestInput: (de, du, field) => de[field],
32
33
  compareReferences: (a, b) => a === b,
@@ -8,6 +8,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
8
8
  * - `calc_pvent` (5_conso_ventilation.js) : espion, la conso de ventilation est testée ailleurs.
9
9
  */
10
10
  vi.mock('./utils.js', () => ({
11
+ set_bug_for_bug_compat: vi.fn(),
11
12
  tv: vi.fn(),
12
13
  requestInput: (de, du, field) => de[field],
13
14
  requestInputID: (de, du, field) => de[`enum_${field}_id`],
@@ -5,6 +5,7 @@ import { beforeEach, describe, expect, test, vi } from 'vitest';
5
5
  * d'entrée `de` pour le champ demandé. Cela isole `calc_pvent` de la logique de `utils`.
6
6
  */
7
7
  vi.mock('./utils.js', () => ({
8
+ set_bug_for_bug_compat: vi.fn(),
8
9
  requestInput: (de, du, field) => de[field]
9
10
  }));
10
11