@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
@@ -1,256 +1,510 @@
1
- import { conso_ch } from './9_conso_ch.js';
2
- import { describe, expect, test } from 'vitest';
3
-
4
- describe('Recherche de bugs dans le calcul de la consommation du chauffage', () => {
5
- test('calcul de la consommation du chauffage pour 2475E2510509B', () => {
6
- const di = {
7
- rendement_generation: 1,
8
- rg: 1,
9
- rg_dep: 1,
10
- conso_auxiliaire_generation_ch: 0,
11
- conso_auxiliaire_generation_ch_depensier: 0
12
- };
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * Dépendances mockées pour isoler `9_conso_ch.js` :
5
+ * - `utils` : `mois_liste` (réduite) et `requestInputID` (lien générateur/émetteur) ;
6
+ * - `9_emetteur_ch` : `rendement_emission` (produit des rendements de l'émetteur) ;
7
+ * - `8_intermittence` : `calc_intermittence` (coefficient d'intermittence I0) ;
8
+ * - `tv` : profils mensuels dh14 / text / nref19 (valeurs contrôlées) ;
9
+ * - `enums` : mapping altitude / zone climatique.
10
+ *
11
+ * `isNil` (lodash-es) est un utilitaire déterministe conservé tel quel.
12
+ * On pilote les fonctions mockées pour vérifier la logique de composition
13
+ * propre au module (choix des coefficients, prorata de surface, base+appoint).
14
+ */
15
+ vi.mock('./utils.js', () => ({
16
+ mois_liste: ['Janvier', 'Février'],
17
+ requestInputID: vi.fn()
18
+ }));
19
+
20
+ vi.mock('./9_emetteur_ch.js', () => ({
21
+ rendement_emission: vi.fn()
22
+ }));
23
+
24
+ vi.mock('./8_intermittence.js', () => ({
25
+ calc_intermittence: vi.fn()
26
+ }));
27
+
28
+ vi.mock('./tv.js', () => ({
29
+ default: {
30
+ dh14: {
31
+ 0: { ca1: { Janvier: { h1a: 1000 }, Février: { h1a: 800 } } },
32
+ 1: { ca1: { Janvier: { h1a: 1100 }, Février: { h1a: 900 } } }
33
+ },
34
+ text: {
35
+ 0: { ca1: { Janvier: { h1a: 5 }, Février: { h1a: 7 } } },
36
+ 1: { ca1: { Janvier: { h1a: 5 }, Février: { h1a: 7 } } }
37
+ },
38
+ nref19: {
39
+ 0: { ca1: { Janvier: { h1a: 500 }, Février: { h1a: 400 } } },
40
+ 1: { ca1: { Janvier: { h1a: 500 }, Février: { h1a: 400 } } }
41
+ }
42
+ }
43
+ }));
44
+
45
+ vi.mock('./enums.js', () => ({
46
+ default: {
47
+ zone_climatique: { 1: 'h1a' },
48
+ classe_altitude: { 1: 'ca1' }
49
+ }
50
+ }));
51
+
52
+ const { conso_ch } = await import('./9_conso_ch.js');
53
+ const { default: tvs } = await import('./tv.js');
54
+ const { requestInputID } = await import('./utils.js');
55
+ const { rendement_emission } = await import('./9_emetteur_ch.js');
56
+ const { calc_intermittence } = await import('./8_intermittence.js');
57
+
58
+ /** Émetteur minimal avec une valeur d'intermittence I0 propre. */
59
+ function emetteur(i0, surface = 100, lien = '1') {
60
+ return {
61
+ donnee_entree: { enum_lien_generateur_emetteur_id: lien, surface_chauffee: surface },
62
+ donnee_intermediaire: { i0 }
63
+ };
64
+ }
65
+
66
+ /**
67
+ * 9. Consommations de chauffage (Cch)
68
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §9
69
+ */
70
+ describe('conso_ch - configuration par défaut (installation simple)', () => {
71
+ beforeEach(() => {
72
+ vi.mocked(requestInputID).mockReset();
73
+ vi.mocked(rendement_emission).mockReset();
74
+ vi.mocked(calc_intermittence).mockReset();
75
+ // rendement d'émission global = 0.8 -> Ich émetteur = 1 / 0.8 = 1.25
76
+ vi.mocked(rendement_emission).mockReturnValue(0.8);
77
+ // I0 renvoyé tel quel pour rendre le prorata vérifiable
78
+ vi.mocked(calc_intermittence).mockImplementation((_gv, _sh, _hsp, i0) => i0);
79
+ });
80
+
81
+ test('conso = coeff * (I0 / rendement_emission / rg) * bch (générateur électrique, 1 émetteur)', () => {
82
+ const di = { rg: 1, rg_dep: 1 };
83
+ // enum électrique + installation simple => émetteur sélectionné par position
84
+ const de = { enum_type_generateur_ch_id: '98', surface_chauffee: 100 };
85
+ const em = [emetteur(0.9)];
86
+
87
+ conso_ch(
88
+ di,
89
+ de,
90
+ {},
91
+ 0,
92
+ 'installation de chauffage simple',
93
+ em,
94
+ 150,
95
+ 100,
96
+ 2.5,
97
+ 1000,
98
+ 2000,
99
+ -9.5,
100
+ 0,
101
+ 1,
102
+ 1
103
+ );
104
+
105
+ // coeff = 1 ; emetteur_eq = 0.9 * 1.25 = 1.125
106
+ expect(di.conso_ch).toBeCloseTo(1125, 9);
107
+ expect(di.conso_ch_depensier).toBeCloseTo(2250, 9);
108
+ // un seul émetteur => calc_intermittence appelé avec l'I0 de l'émetteur
109
+ expect(calc_intermittence).toHaveBeenCalledWith(150, 100, 2.5, 0.9);
110
+ });
111
+
112
+ test('coefficient de configuration : "chauffage solaire" applique (1 - Fch)', () => {
113
+ const di = { rg: 1, rg_dep: 1 };
114
+ // non électrique => tous les émetteurs filtrés sont retenus
115
+ const de = { enum_type_generateur_ch_id: '10', surface_chauffee: 100, fch: 0.4 };
116
+ const em = [emetteur(1)];
117
+
118
+ conso_ch(
119
+ di,
120
+ de,
121
+ {},
122
+ 0,
123
+ 'installation de chauffage avec chauffage solaire',
124
+ em,
125
+ 150,
126
+ 100,
127
+ 2.5,
128
+ 1000,
129
+ 2000,
130
+ -9.5,
131
+ 0,
132
+ 1,
133
+ 1
134
+ );
135
+
136
+ // coeff = 1 - 0.4 = 0.6 ; Ich = 1 * 1.25 = 1.25
137
+ expect(di.conso_ch).toBeCloseTo(0.6 * 1.25 * 1000, 9);
138
+ });
139
+
140
+ test('sans fch, le coefficient utilise la valeur par défaut 0.5', () => {
141
+ const di = { rg: 1, rg_dep: 1 };
142
+ const de = { enum_type_generateur_ch_id: '10', surface_chauffee: 100 };
143
+ const em = [emetteur(1)];
144
+
145
+ conso_ch(
146
+ di,
147
+ de,
148
+ {},
149
+ 0,
150
+ 'installation de chauffage avec chauffage solaire',
151
+ em,
152
+ 150,
153
+ 100,
154
+ 2.5,
155
+ 1000,
156
+ 2000,
157
+ -9.5,
158
+ 0,
159
+ 1,
160
+ 1
161
+ );
162
+
163
+ // coeff = 1 - 0.5 = 0.5
164
+ expect(di.conso_ch).toBeCloseTo(0.5 * 1.25 * 1000, 9);
165
+ });
166
+
167
+ test('coefficient absent pour la position demandée : repli sur 1', () => {
168
+ const di = { rg: 1, rg_dep: 1 };
169
+ // non électrique pour conserver tous les émetteurs malgré la position 2
170
+ const de = { enum_type_generateur_ch_id: '10', surface_chauffee: 100 };
171
+ const em = [emetteur(0.9)];
172
+
173
+ conso_ch(
174
+ di,
175
+ de,
176
+ {},
177
+ 2,
178
+ 'installation de chauffage simple',
179
+ em,
180
+ 150,
181
+ 100,
182
+ 2.5,
183
+ 1000,
184
+ 2000,
185
+ -9.5,
186
+ 0,
187
+ 1,
188
+ 1
189
+ );
190
+
191
+ // coeff = 1 (aucune clé 2 dans la table) ; Ich = 0.9 * 1.25
192
+ expect(di.conso_ch).toBeCloseTo(0.9 * 1.25 * 1000, 9);
193
+ });
194
+
195
+ test('plusieurs émetteurs : prorata par la surface chauffée de chaque émetteur', () => {
196
+ const di = { rg: 1, rg_dep: 1 };
197
+ // non électrique => les deux émetteurs liés sont conservés
198
+ const de = { enum_type_generateur_ch_id: '10', surface_chauffee: 40 };
199
+ vi.mocked(requestInputID).mockReturnValue('1');
200
+ const em = [emetteur(1, 10, '1'), emetteur(2, 30, '1')];
201
+
202
+ conso_ch(
203
+ di,
204
+ de,
205
+ {},
206
+ 0,
207
+ 'installation de chauffage simple',
208
+ em,
209
+ 150,
210
+ 100,
211
+ 2.5,
212
+ 1000,
213
+ 2000,
214
+ -9.5,
215
+ 0,
216
+ 1,
217
+ 1
218
+ );
219
+
220
+ // emetteur_eq = (10/40)*1*1.25 + (30/40)*2*1.25 = 0.3125 + 1.875 = 2.1875
221
+ expect(di.conso_ch).toBeCloseTo(2187.5, 9);
222
+ expect(rendement_emission).toHaveBeenCalledTimes(2);
223
+ });
224
+
225
+ test('générateur électrique avec position nulle : tous les émetteurs sont conservés (isNil)', () => {
226
+ const di = { rg: 1, rg_dep: 1 };
227
+ const de = { enum_type_generateur_ch_id: '98', surface_chauffee: 40 };
228
+ vi.mocked(requestInputID).mockReturnValue('1');
229
+ const em = [emetteur(1, 10, '1'), emetteur(2, 30, '1')];
230
+
231
+ conso_ch(
232
+ di,
233
+ de,
234
+ {},
235
+ null,
236
+ 'installation de chauffage simple',
237
+ em,
238
+ 150,
239
+ 100,
240
+ 2.5,
241
+ 1000,
242
+ 2000,
243
+ -9.5,
244
+ 0,
245
+ 1,
246
+ 1
247
+ );
248
+
249
+ // les deux émetteurs pris en compte => emetteur_eq = 2.1875
250
+ expect(di.conso_ch).toBeCloseTo(2187.5, 9);
251
+ });
252
+ });
253
+
254
+ describe('conso_ch - convecteurs bi-jonction (individuel + collectif)', () => {
255
+ beforeEach(() => {
256
+ vi.mocked(requestInputID).mockReset();
257
+ vi.mocked(rendement_emission).mockReset();
258
+ vi.mocked(calc_intermittence).mockReset();
259
+ vi.mocked(rendement_emission).mockReturnValue(0.8);
260
+ vi.mocked(calc_intermittence).mockImplementation((_gv, _sh, _hsp, i0) => i0);
261
+ });
262
+
263
+ test('somme des parts individuelle (coeff 0.4) et collective (coeff 0.6, I0 forcé à 1.03)', () => {
264
+ const di = { rg: 1, rg_dep: 1 };
265
+ const de = { enum_type_generateur_ch_id: '10', surface_chauffee: 100, fch: 0.5 };
266
+ const em = [emetteur(0.5)];
267
+
268
+ conso_ch(
269
+ di,
270
+ de,
271
+ {},
272
+ 0,
273
+ 'convecteurs bi-jonction',
274
+ em,
275
+ 150,
276
+ 100,
277
+ 2.5,
278
+ 1000,
279
+ 2000,
280
+ -9.5,
281
+ 0,
282
+ 1,
283
+ 1
284
+ );
285
+
286
+ // individuel : 0.4 * (0.5 / 0.8) * 1000 = 250
287
+ // collectif : 0.6 * (1.03 / 0.8) * 1000 = 772.5
288
+ expect(di.conso_ch).toBeCloseTo(250 + 772.5, 9);
289
+ expect(di.conso_ch_depensier).toBeCloseTo((250 + 772.5) * 2, 9);
290
+ // l'intermittence collective est calculée avec I0 = 1.03
291
+ expect(calc_intermittence).toHaveBeenCalledWith(150, 100, 2.5, 1.03);
292
+ });
293
+ });
13
294
 
14
- const de = {
15
- description: 'Electrique - Convecteur électrique NFC, NF** et NF***',
16
- reference: 'Generateur:2024_07_11_09_30_44_722898200737262#1',
17
- reference_generateur_mixte: '',
18
- ref_produit_generateur_ch: 'Sans Objet',
19
- enum_type_generateur_ch_id: '98',
20
- enum_usage_generateur_id: '1',
21
- enum_type_energie_id: '1',
22
- position_volume_chauffe: 1,
23
- tv_rendement_generation_id: 29,
24
- identifiant_reseau_chaleur: '',
25
- enum_methode_saisie_carac_sys_id: '1',
26
- enum_lien_generateur_emetteur_id: '1'
295
+ describe('conso_ch - installation collective base + appoint', () => {
296
+ beforeEach(() => {
297
+ vi.mocked(requestInputID).mockReset();
298
+ vi.mocked(rendement_emission).mockReset();
299
+ vi.mocked(calc_intermittence).mockReset();
300
+ vi.mocked(rendement_emission).mockReturnValue(0.9);
301
+ vi.mocked(calc_intermittence).mockReturnValue(1);
302
+ });
303
+
304
+ /** Jeu d'installation base + appoint minimal. */
305
+ function baseAppointArgs(pos, { pn = 5000 } = {}) {
306
+ const di = { rg: 1, rg_dep: 1 };
307
+ const de = { surface_chauffee: 100, rdim: 1 };
308
+ const emBase = {
309
+ donnee_entree: { surface_chauffee: 100 },
310
+ donnee_intermediaire: {
311
+ i0: 0.9,
312
+ rendement_distribution: 0.95,
313
+ rendement_regulation: 0.99,
314
+ rendement_emission: 0.95
315
+ }
316
+ };
317
+ const emAppoint = {
318
+ donnee_entree: { surface_chauffee: 100 },
319
+ donnee_intermediaire: { i0: 0.9 }
27
320
  };
321
+ const genBase = { donnee_intermediaire: { pn, rendement_generation: 0.9 } };
322
+ const genAppoint = { donnee_intermediaire: { pn: 0, rendement_generation: 0.9 } };
323
+ const em_list = [emBase, emAppoint];
324
+ const gen_ch_list = [genBase, genAppoint];
325
+ const besoin_ch_mois = { Janvier: 3000, Février: 2000 };
326
+ return [
327
+ di,
328
+ de,
329
+ pos,
330
+ 'installation de chauffage collectif avec base + appoint',
331
+ em_list,
332
+ 150,
333
+ 100,
334
+ 2.5,
335
+ 50,
336
+ 60,
337
+ -9.5,
338
+ 0,
339
+ 1,
340
+ 1,
341
+ besoin_ch_mois,
342
+ 100,
343
+ gen_ch_list
344
+ ];
345
+ }
346
+
347
+ /** Adaptation à la signature réelle de `conso_ch`. */
348
+ function callBaseAppoint(pos, opts) {
349
+ const [
350
+ di,
351
+ de,
352
+ _pos,
353
+ cfg,
354
+ em_list,
355
+ GV,
356
+ Sh,
357
+ hsp,
358
+ bch,
359
+ bch_dep,
360
+ tbase,
361
+ ilpa,
362
+ ca_id,
363
+ zc_id,
364
+ besoin_ch_mois,
365
+ s_chauffee_inst,
366
+ gen_ch_list
367
+ ] = baseAppointArgs(pos, opts);
368
+ conso_ch(
369
+ di,
370
+ de,
371
+ {},
372
+ _pos,
373
+ cfg,
374
+ em_list,
375
+ GV,
376
+ Sh,
377
+ hsp,
378
+ bch,
379
+ bch_dep,
380
+ tbase,
381
+ ilpa,
382
+ ca_id,
383
+ zc_id,
384
+ besoin_ch_mois,
385
+ s_chauffee_inst,
386
+ gen_ch_list
387
+ );
388
+ return di;
389
+ }
390
+
391
+ test('générateur de base (pos 0) : valeur de référence de régression', () => {
392
+ const di = callBaseAppoint(0);
393
+ // référence de régression (calcul mensuel base+appoint avec pn > 0)
394
+ expect(di.conso_ch).toBeCloseTo(6.172839506172839, 9);
395
+ });
28
396
 
29
- const du = {
30
- enum_usage_generateur_id: ['1', '2', '3'],
31
- enum_type_generateur_ch_id: [
32
- '1',
33
- '2',
34
- '3',
35
- '4',
36
- '5',
37
- '6',
38
- '7',
39
- '8',
40
- '9',
41
- '10',
42
- '11',
43
- '12',
44
- '13',
45
- '14',
46
- '15',
47
- '16',
48
- '17',
49
- '18',
50
- '19',
51
- '20',
52
- '21',
53
- '22',
54
- '23',
55
- '24',
56
- '25',
57
- '26',
58
- '27',
59
- '28',
60
- '29',
61
- '30',
62
- '31',
63
- '32',
64
- '33',
65
- '34',
66
- '35',
67
- '36',
68
- '37',
69
- '38',
70
- '39',
71
- '40',
72
- '41',
73
- '42',
74
- '43',
75
- '44',
76
- '45',
77
- '46',
78
- '47',
79
- '48',
80
- '49',
81
- '50',
82
- '51',
83
- '52',
84
- '53',
85
- '54',
86
- '55',
87
- '56',
88
- '57',
89
- '58',
90
- '59',
91
- '60',
92
- '61',
93
- '62',
94
- '63',
95
- '64',
96
- '65',
97
- '66',
98
- '67',
99
- '68',
100
- '69',
101
- '70',
102
- '71',
103
- '72',
104
- '73',
105
- '74',
106
- '75',
107
- '76',
108
- '77',
109
- '78',
110
- '79',
111
- '80',
112
- '81',
113
- '82',
114
- '83',
115
- '84',
116
- '85',
117
- '86',
118
- '87',
119
- '88',
120
- '89',
121
- '90',
122
- '91',
123
- '92',
124
- '93',
125
- '94',
126
- '95',
127
- '96',
128
- '97',
129
- '98',
130
- '99',
131
- '100',
132
- '101',
133
- '102',
134
- '103',
135
- '104',
136
- '105',
137
- '106',
138
- '107',
139
- '108',
140
- '109',
141
- '110',
142
- '111',
143
- '112',
144
- '113',
145
- '114',
146
- '115',
147
- '116',
148
- '117',
149
- '118',
150
- '119',
151
- '120',
152
- '121',
153
- '122',
154
- '123',
155
- '124',
156
- '125',
157
- '126',
158
- '127',
159
- '128',
160
- '129',
161
- '130',
162
- '131',
163
- '132',
164
- '133',
165
- '134',
166
- '135',
167
- '136',
168
- '137',
169
- '138',
170
- '139',
171
- '140',
172
- '141',
173
- '142',
174
- '143',
175
- '144',
176
- '145',
177
- '146',
178
- '147',
179
- '148',
180
- '149',
181
- '150',
182
- '151',
183
- '152',
184
- '153',
185
- '154',
186
- '155',
187
- '156',
188
- '157',
189
- '158',
190
- '159',
191
- '160',
192
- '161',
193
- '162',
194
- '163',
195
- '164',
196
- '165',
197
- '166',
198
- '167',
199
- '168',
200
- '169',
201
- '170',
202
- '171'
203
- ]
397
+ test("générateur d'appoint (pos 1) : valeur de référence de régression", () => {
398
+ const di = callBaseAppoint(1);
399
+ // référence de régression (part d'appoint = bch - besoin de base)
400
+ expect(di.conso_ch).toBeCloseTo(55.55555555555556, 9);
401
+ });
402
+
403
+ test('puissance nulle (pe = 0) : le besoin de base est forfaitairement la moitié du besoin', () => {
404
+ const di = callBaseAppoint(0, { pn: 0 });
405
+ // pe = 0 => bch_base_j = besoin_ch_mois * 0.5 sur chaque mois
406
+ // référence de régression
407
+ expect(di.conso_ch).toBeCloseTo(3.0864197530864197, 9);
408
+ });
409
+
410
+ test('faible puissance (dhtj positif) : le besoin de base peut devenir nul (ratioDh <= 0)', () => {
411
+ // pn faible => t proche de 14 => xj > 0 => dhtj > 0 et supérieur à dh14j
412
+ // => ratioDh <= 0 => bch_base_j forcé à 0 sur les mois concernés
413
+ const di = callBaseAppoint(0, { pn: 100 });
414
+ // référence de régression (dhtj > 0, ratioDh <= 0)
415
+ expect(di.conso_ch).toBeCloseTo(0, 9);
416
+ });
417
+
418
+ test('logement collectif ILPA + valeurs par défaut (rdim, rendement de génération)', () => {
419
+ const di = { rg: 1, rg_dep: 1 };
420
+ // pas de rdim => rdim = 1 ; pas de rendement_generation => Ich = 1
421
+ const de = { surface_chauffee: 100 };
422
+ const emBase = {
423
+ donnee_entree: { surface_chauffee: 100 },
424
+ donnee_intermediaire: {
425
+ i0: 0.9,
426
+ rendement_distribution: 0.95,
427
+ rendement_regulation: 0.99,
428
+ rendement_emission: 0.95
429
+ }
204
430
  };
205
- const _pos = 0;
206
-
207
- const cfg_ch = 'installation de chauffage simple';
208
-
209
- const em_list = [
210
- {
211
- donnee_entree: {
212
- description: '',
213
- reference: 'Emetteur:2024_07_11_09_30_44_722898200737262#1',
214
- surface_chauffee: 22.76,
215
- tv_rendement_emission_id: 1,
216
- tv_rendement_distribution_ch_id: 1,
217
- tv_rendement_regulation_id: 1,
218
- enum_type_emission_distribution_id: '1',
219
- tv_intermittence_id: 138,
220
- reseau_distribution_isole: 0,
221
- enum_equipement_intermittence_id: '4',
222
- enum_type_regulation_id: '2',
223
- enum_periode_installation_emetteur_id: '1',
224
- enum_type_chauffage_id: '1',
225
- enum_temp_distribution_ch_id: '1',
226
- enum_lien_generateur_emetteur_id: '1'
227
- },
228
- donnee_intermediaire: {
229
- rendement_distribution: 1,
230
- rendement_emission: 0.95,
231
- rendement_regulation: 0.99,
232
- i0: 0.86
233
- },
234
- donnee_utilisateur: {}
431
+ const genBase = { donnee_intermediaire: { pn: 5000 } };
432
+ const genAppoint = { donnee_intermediaire: { pn: 0 } };
433
+
434
+ conso_ch(
435
+ di,
436
+ de,
437
+ {},
438
+ 0,
439
+ 'installation de chauffage collectif avec base + appoint',
440
+ [emBase, emBase],
441
+ 150,
442
+ 100,
443
+ 2.5,
444
+ 50,
445
+ 60,
446
+ -9.5,
447
+ 1, // ilpa vrai => idx_ilpa = 1
448
+ 1,
449
+ 1,
450
+ { Janvier: 3000, Février: 2000 },
451
+ 100,
452
+ [genBase, genAppoint]
453
+ );
454
+
455
+ expect(Number.isFinite(di.conso_ch)).toBe(true);
456
+ expect(di.conso_ch).toBeGreaterThanOrEqual(0);
457
+ });
458
+
459
+ test('degrés-heures mensuels nuls (dh14j = 0) : mois neutralisé sans division', () => {
460
+ const initialFev = tvs.dh14[0].ca1.Février.h1a;
461
+ tvs.dh14[0].ca1.Février.h1a = 0;
462
+ try {
463
+ const di = callBaseAppoint(0, { pn: 100 });
464
+ // le mois à dh14j nul est ignoré (branche dh14j > 0 fausse)
465
+ expect(Number.isFinite(di.conso_ch)).toBe(true);
466
+ expect(di.conso_ch).toBeGreaterThanOrEqual(0);
467
+ } finally {
468
+ tvs.dh14[0].ca1.Février.h1a = initialFev;
469
+ }
470
+ });
471
+
472
+ test('besoin de chauffage nul : la consommation reste indéfinie (aucun calcul)', () => {
473
+ const di = { rg: 1, rg_dep: 1 };
474
+ const de = { surface_chauffee: 100, rdim: 1 };
475
+ const emBase = {
476
+ donnee_entree: { surface_chauffee: 100 },
477
+ donnee_intermediaire: {
478
+ i0: 0.9,
479
+ rendement_distribution: 0.95,
480
+ rendement_regulation: 0.99,
481
+ rendement_emission: 0.95
235
482
  }
236
- ];
483
+ };
484
+ const genBase = { donnee_intermediaire: { pn: 5000, rendement_generation: 0.9 } };
485
+
486
+ conso_ch(
487
+ di,
488
+ de,
489
+ {},
490
+ 0,
491
+ 'installation de chauffage collectif avec base + appoint',
492
+ [emBase, emBase],
493
+ 150,
494
+ 100,
495
+ 2.5,
496
+ 0,
497
+ 0,
498
+ -9.5,
499
+ 0,
500
+ 1,
501
+ 1,
502
+ { Janvier: 3000, Février: 2000 },
503
+ 100,
504
+ [genBase, genBase]
505
+ );
237
506
 
238
- const GV = 150.18558182166174;
239
- const Sh = 22.76;
240
- const hsp = 2.5700000000000003;
241
- const bch = 8001.0714;
242
- const bch_dep = 9714.925476692655;
243
-
244
- conso_ch(di, de, du, _pos, cfg_ch, em_list, GV, Sh, hsp, bch, bch_dep);
245
-
246
- expect(di).toStrictEqual({
247
- conso_auxiliaire_generation_ch: 0,
248
- conso_auxiliaire_generation_ch_depensier: 0,
249
- conso_ch: 6324.781622192915,
250
- conso_ch_depensier: 7679.569278179351,
251
- rendement_generation: 1,
252
- rg: 1,
253
- rg_dep: 1
254
- });
507
+ // bch = 0 => la branche de calcul n'est pas exécutée
508
+ expect(di.conso_ch).toBeUndefined();
255
509
  });
256
510
  });