@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
@@ -0,0 +1,74 @@
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * Dépendance mockée : `getFicheTechnique` (import par défaut), qui fournit l'année
5
+ * d'installation du poêle bouilleur. On contrôle sa valeur de retour uniquement.
6
+ */
7
+ vi.mock('./ficheTechnique.js', () => ({
8
+ default: vi.fn()
9
+ }));
10
+
11
+ const { updateGenerateurBouilleurs } = await import('./13.2_generateur_combustion_bouilleur.js');
12
+ const { default: getFicheTechnique } = await import('./ficheTechnique.js');
13
+
14
+ beforeEach(() => {
15
+ vi.mocked(getFicheTechnique).mockReset();
16
+ });
17
+
18
+ /**
19
+ * 13.1 - Les poêles à bois bouilleur sont assimilés à des chaudières bois de la
20
+ * période correspondante.
21
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.1
22
+ */
23
+ describe('updateGenerateurBouilleurs - substitution par une chaudière bois équivalente', () => {
24
+ test('type non concerné : aucune substitution', () => {
25
+ getFicheTechnique.mockReturnValue(undefined);
26
+ const de = { enum_type_generateur_ch_id: '90' };
27
+ updateGenerateurBouilleurs({}, de, 'ch');
28
+ expect(de.enum_type_generateur_ch_id).toBe('90');
29
+ });
30
+
31
+ test('sans fiche technique : période par défaut 2004-2012 (ch, bûche 48 -> 58)', () => {
32
+ getFicheTechnique.mockReturnValue(undefined);
33
+ const de = { enum_type_generateur_ch_id: '48' };
34
+ updateGenerateurBouilleurs({}, de, 'ch');
35
+ // Valeur par défaut : chaudière bois 2004-2012 (values[2004])
36
+ expect(de.enum_type_generateur_ch_id).toBe('58');
37
+ });
38
+
39
+ test('fiche "avant 1948" : chaudière bois la plus ancienne (ch, bûche 48 -> 55)', () => {
40
+ getFicheTechnique.mockReturnValue({ valeur: 'avant 1948' });
41
+ const de = { enum_type_generateur_ch_id: '48' };
42
+ updateGenerateurBouilleurs({}, de, 'ch');
43
+ expect(de.enum_type_generateur_ch_id).toBe('55');
44
+ });
45
+
46
+ test.each([
47
+ ['2020', '61'], // >= 2019
48
+ ['2018', '60'], // >= 2018
49
+ ['2015', '59'], // >= 2013
50
+ ['2005', '58'], // >= 2004
51
+ ['1996', '57'], // >= 1995
52
+ ['1980', '56'], // >= 1978
53
+ ['1950', '55'] // >= 1948
54
+ ])('année %s : seuil de période appliqué (ch, bûche 48 -> %s)', (annee, attendu) => {
55
+ getFicheTechnique.mockReturnValue({ valeur: annee });
56
+ const de = { enum_type_generateur_ch_id: '48' };
57
+ updateGenerateurBouilleurs({}, de, 'ch');
58
+ expect(de.enum_type_generateur_ch_id).toBe(attendu);
59
+ });
60
+
61
+ test('type ECS (granulés 115) : utilise la table ECS des chaudières bois', () => {
62
+ getFicheTechnique.mockReturnValue({ valeur: '2016' });
63
+ const de = { enum_type_generateur_ecs_id: '115' };
64
+ updateGenerateurBouilleurs({}, de, 'ecs');
65
+ // granulés ECS : 2013->33 ; 2016 >= 2013 => 33
66
+ expect(de.enum_type_generateur_ecs_id).toBe('33');
67
+ });
68
+
69
+ test("la fiche technique est interrogée pour un 'bouilleur' dans le domaine '7'", () => {
70
+ getFicheTechnique.mockReturnValue({ valeur: '2000' });
71
+ updateGenerateurBouilleurs({}, { enum_type_generateur_ch_id: '48' }, 'ch');
72
+ expect(getFicheTechnique).toHaveBeenCalledWith({}, '7', 'année', ['bouilleur']);
73
+ });
74
+ });
@@ -0,0 +1,323 @@
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * Dépendances mockées pour isoler la logique de calcul :
5
+ * - `enums` (import par défaut) : mapping type de générateur / type d'énergie, qui
6
+ * sélectionne la branche de calcul du rendement et le coefficient K ;
7
+ * - `tv` : accès aux tables temp_fonc_30 / temp_fonc_100 (lignes contrôlées) ;
8
+ * - `requestInput` / `requestInputID` : passe-plats vers les données d'entrée ;
9
+ * - `bug_for_bug_compat` : désactivé pour isoler le comportement nominal.
10
+ */
11
+ const state = vi.hoisted(() => ({ bug: false }));
12
+
13
+ vi.mock('./enums.js', () => ({
14
+ default: {
15
+ type_generateur_ch: {
16
+ 89: 'chaudière gaz standard 2001-2015',
17
+ 55: 'chaudière bois bûche avant 1978',
18
+ 96: 'chaudière gaz à condensation 2001-2015',
19
+ 70: 'chaudière gaz basse température 2001-2015',
20
+ 12: 'radiateur à gaz',
21
+ 33: 'générateur à air chaud',
22
+ 99: 'type de générateur inconnu'
23
+ },
24
+ type_energie: {
25
+ 2: 'gaz naturel',
26
+ 4: 'bois – bûches'
27
+ }
28
+ }
29
+ }));
30
+
31
+ vi.mock('./utils.js', () => ({
32
+ get bug_for_bug_compat() {
33
+ return state.bug;
34
+ },
35
+ tv: vi.fn(),
36
+ requestInput: vi.fn((de, du, field) => de[field]),
37
+ requestInputID: vi.fn((de, du, field) => de[`enum_${field}_id`])
38
+ }));
39
+
40
+ const { tv_temp_fonc_30_100, calc_generateur_combustion_ch } = await import(
41
+ './13.2_generateur_combustion_ch.js'
42
+ );
43
+ const { tv } = await import('./utils.js');
44
+
45
+ beforeEach(() => {
46
+ vi.mocked(tv).mockReset();
47
+ state.bug = false;
48
+ });
49
+
50
+ /**
51
+ * 13.2.1.1 - Températures de fonctionnement à 30 % et 100 % de charge.
52
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2.1.1
53
+ */
54
+ describe('tv_temp_fonc_30_100 - températures de fonctionnement', () => {
55
+ function emetteur(overrides = {}) {
56
+ return {
57
+ donnee_entree: {
58
+ enum_temp_distribution_ch_id: '3',
59
+ ...overrides
60
+ },
61
+ donnee_utilisateur: {}
62
+ };
63
+ }
64
+
65
+ test('les températures forfaitaires trouvées sont recopiées dans les données intermédiaires', () => {
66
+ tv.mockImplementation((table) =>
67
+ table === 'temp_fonc_30'
68
+ ? { tv_temp_fonc_30_id: '5', temp_fonc_30: '40' }
69
+ : { tv_temp_fonc_100_id: '6', temp_fonc_100: '70' }
70
+ );
71
+ const di = {};
72
+ const de = { enum_type_generateur_ch_id: '89' };
73
+ tv_temp_fonc_30_100(di, de, {}, [emetteur()], 1975);
74
+ expect(di.temp_fonc_30).toBe(40);
75
+ expect(di.temp_fonc_100).toBe(70);
76
+ expect(de.tv_temp_fonc_30_id).toBe('5');
77
+ expect(de.tv_temp_fonc_100_id).toBe('6');
78
+ });
79
+
80
+ test.each([
81
+ [1975, 'avant 1981'],
82
+ [1990, 'entre 1981 et 2000'],
83
+ [2010, 'après 2000']
84
+ ])("période des émetteurs déduite de l'année de construction %s : %s", (ac, periodeAttendue) => {
85
+ tv.mockReturnValue({ tv_temp_fonc_30_id: '1', temp_fonc_30: '40' });
86
+ tv_temp_fonc_30_100({}, { enum_type_generateur_ch_id: '89' }, {}, [emetteur()], ac);
87
+ expect(tv).toHaveBeenCalledWith(
88
+ 'temp_fonc_30',
89
+ expect.objectContaining({ periode_emetteurs: periodeAttendue })
90
+ );
91
+ });
92
+
93
+ test('conserve la température la plus élevée entre plusieurs émetteurs', () => {
94
+ tv.mockImplementation((table, matcher) => {
95
+ if (table !== 'temp_fonc_30') return null;
96
+ // Le premier émetteur renvoie 40, le second 55
97
+ return matcher.enum_temp_distribution_ch_id === '3'
98
+ ? { tv_temp_fonc_30_id: 'a', temp_fonc_30: '40' }
99
+ : { tv_temp_fonc_30_id: 'b', temp_fonc_30: '55' };
100
+ });
101
+ const di = {};
102
+ tv_temp_fonc_30_100(
103
+ di,
104
+ { enum_type_generateur_ch_id: '89' },
105
+ {},
106
+ [emetteur(), emetteur({ enum_temp_distribution_ch_id: '4' })],
107
+ 1975
108
+ );
109
+ expect(di.temp_fonc_30).toBe(55);
110
+ });
111
+
112
+ test('conserve la température à 100 % la plus élevée entre plusieurs émetteurs', () => {
113
+ tv.mockImplementation((table, matcher) => {
114
+ if (table !== 'temp_fonc_100') return null;
115
+ return matcher.enum_temp_distribution_ch_id === '3'
116
+ ? { tv_temp_fonc_100_id: 'a', temp_fonc_100: '60' }
117
+ : { tv_temp_fonc_100_id: 'b', temp_fonc_100: '75' };
118
+ });
119
+ const di = {};
120
+ tv_temp_fonc_30_100(
121
+ di,
122
+ { enum_type_generateur_ch_id: '89' },
123
+ {},
124
+ [emetteur(), emetteur({ enum_temp_distribution_ch_id: '4' })],
125
+ 1975
126
+ );
127
+ // La comparaison `>` conserve la plus grande des deux valeurs
128
+ expect(di.temp_fonc_100).toBe(75);
129
+ });
130
+
131
+ test('aucune ligne forfaitaire trouvée : erreurs signalées pour temp_fonc_30 et temp_fonc_100', () => {
132
+ const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
133
+ tv.mockReturnValue(null);
134
+ const di = {};
135
+ tv_temp_fonc_30_100(di, { enum_type_generateur_ch_id: '89' }, {}, [emetteur()], 1975);
136
+ expect(di.temp_fonc_30).toBeUndefined();
137
+ expect(di.temp_fonc_100).toBeUndefined();
138
+ expect(errSpy).toHaveBeenCalledTimes(2);
139
+ errSpy.mockRestore();
140
+ });
141
+ });
142
+
143
+ /**
144
+ * 13.2.1.2 - Rendement de génération des chaudières (rg PCI).
145
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2.1.2
146
+ */
147
+ describe('calc_generateur_combustion_ch - rendement de génération', () => {
148
+ test('chaudière gaz standard : rendement PCI et rendement dépensier', () => {
149
+ const di = { pn: 25000, rpn: 0.9, rpint: 0.85, qp0: 200, temp_fonc_30: 40, temp_fonc_100: 70 };
150
+ const de = {
151
+ enum_type_generateur_ch_id: '89',
152
+ enum_type_energie_id: '2',
153
+ type_energie: 'gaz naturel',
154
+ presence_regulation_combustion: false,
155
+ description: 't'
156
+ };
157
+ calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
158
+ // valeurs de référence de régression (module réel, enums réels équivalents)
159
+ expect(di.rg).toBeCloseTo(0.8582190936918361, 9);
160
+ expect(di.rg_dep).toBeCloseTo(0.8706053178411902, 9);
161
+ expect(di.rendement_generation).toBeCloseTo(0.8582190936918361, 9);
162
+ });
163
+
164
+ test('chaudière bois : branche de calcul dédiée (QP50 / QP100)', () => {
165
+ const di = { pn: 30000, rpn: 0.75, rpint: 0.7, qp0: 300, temp_fonc_30: 40, temp_fonc_100: 70 };
166
+ const de = {
167
+ enum_type_generateur_ch_id: '55',
168
+ enum_type_energie_id: '4',
169
+ type_energie: 'bois – bûches',
170
+ description: 't'
171
+ };
172
+ calc_generateur_combustion_ch({}, di, de, { cdimref: 0.9, cdimrefDep: 0.7 });
173
+ // valeurs de référence de régression
174
+ expect(di.rg).toBeCloseTo(0.7044775930164003, 9);
175
+ expect(di.rg_dep).toBeCloseTo(0.7138933157828895, 9);
176
+ });
177
+
178
+ test('chaudière à condensation avec régulation : température de fonctionnement à 30 % utilisée', () => {
179
+ const di = { pn: 25000, rpn: 0.95, rpint: 0.92, qp0: 150, temp_fonc_30: 35, temp_fonc_100: 70 };
180
+ const de = {
181
+ enum_type_generateur_ch_id: '96',
182
+ enum_type_energie_id: '2',
183
+ type_energie: 'gaz naturel',
184
+ presence_regulation_combustion: true,
185
+ description: 't'
186
+ };
187
+ calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
188
+ // valeurs de référence de régression
189
+ expect(di.rg).toBeCloseTo(0.9281152399114512, 9);
190
+ expect(di.rg_dep).toBeCloseTo(0.9344861707155592, 9);
191
+ });
192
+
193
+ test('radiateur à gaz : branche de calcul dédiée', () => {
194
+ const di = { pn: 20000, rpn: 0.9, rpint: 0.85, qp0: 200, temp_fonc_30: 40, temp_fonc_100: 70 };
195
+ const de = {
196
+ enum_type_generateur_ch_id: '12',
197
+ enum_type_energie_id: '2',
198
+ type_energie: 'gaz naturel',
199
+ presence_regulation_combustion: false,
200
+ description: 't'
201
+ };
202
+ calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
203
+ // valeurs de référence de régression
204
+ expect(di.rg).toBeCloseTo(0.8851321252668564, 9);
205
+ expect(di.rg_dep).toBeCloseTo(0.8869221230591896, 9);
206
+ });
207
+
208
+ test('générateur à air chaud : branche de calcul dédiée', () => {
209
+ const di = { pn: 20000, rpn: 0.9, rpint: 0.85, qp0: 200, temp_fonc_30: 40, temp_fonc_100: 70 };
210
+ const de = {
211
+ enum_type_generateur_ch_id: '33',
212
+ enum_type_energie_id: '2',
213
+ type_energie: 'gaz naturel',
214
+ presence_regulation_combustion: false,
215
+ description: 't'
216
+ };
217
+ calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
218
+ // valeurs de référence de régression
219
+ expect(di.rg).toBeCloseTo(0.8655833354015402, 9);
220
+ expect(di.rg_dep).toBeCloseTo(0.8746954076850985, 9);
221
+ });
222
+
223
+ test('chaudière basse température : coefficients dédiés (a = 0.1, b = 40)', () => {
224
+ const di = { pn: 20000, rpn: 0.9, rpint: 0.85, qp0: 200, temp_fonc_30: 40, temp_fonc_100: 70 };
225
+ const de = {
226
+ enum_type_generateur_ch_id: '70',
227
+ enum_type_energie_id: '2',
228
+ type_energie: 'gaz naturel',
229
+ presence_regulation_combustion: false,
230
+ description: 't'
231
+ };
232
+ calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
233
+ // valeurs de référence de régression
234
+ expect(di.rg).toBeCloseTo(0.8513271171577527, 9);
235
+ expect(di.rg_dep).toBeCloseTo(0.8655664711109103, 9);
236
+ });
237
+
238
+ test('type de générateur inconnu : aucun rendement calculé (avertissement)', () => {
239
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
240
+ const di = { pn: 20000, rpn: 0.9, rpint: 0.85, qp0: 200, temp_fonc_30: 40, temp_fonc_100: 70 };
241
+ const de = {
242
+ enum_type_generateur_ch_id: '99',
243
+ enum_type_energie_id: '2',
244
+ type_energie: 'gaz naturel',
245
+ description: 't'
246
+ };
247
+ calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
248
+ // QPx renvoie undefined pour un type non reconnu -> rendement NaN
249
+ expect(Number.isNaN(di.rg)).toBe(true);
250
+ expect(warnSpy).toHaveBeenCalled();
251
+ warnSpy.mockRestore();
252
+ });
253
+
254
+ test('cdimref absent (du vide) : valeur de repli 0 -> Tch_xfinal saturé à 1', () => {
255
+ const di = { pn: 20000, rpn: 0.9, rpint: 0.85, qp0: 200, temp_fonc_30: 40, temp_fonc_100: 70 };
256
+ const de = {
257
+ enum_type_generateur_ch_id: '89',
258
+ enum_type_energie_id: '2',
259
+ type_energie: 'gaz naturel',
260
+ presence_regulation_combustion: false,
261
+ description: 't'
262
+ };
263
+ calc_generateur_combustion_ch({}, di, de, {});
264
+ // Cdimref = 0 -> x/0 = Infinity -> min(1, ...) = 1 pour tous les x, nominal = dépensier
265
+ expect(di.rg).toBeCloseTo(0.8967281540325838, 9);
266
+ expect(di.rg_dep).toBeCloseTo(0.8967281540325838, 9);
267
+ });
268
+
269
+ test('bug_for_bug_compat : correction de qp0 exprimé en kW (< 1) vers des W', () => {
270
+ state.bug = true;
271
+ const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
272
+ const di = { pn: 20000, rpn: 0.9, rpint: 0.85, qp0: 0.2, temp_fonc_30: 40, temp_fonc_100: 70 };
273
+ const de = {
274
+ enum_type_generateur_ch_id: '89',
275
+ enum_type_energie_id: '2',
276
+ type_energie: 'gaz naturel',
277
+ presence_regulation_combustion: false,
278
+ description: 't'
279
+ };
280
+ calc_generateur_combustion_ch({}, di, de, { cdimref: 0.8, cdimrefDep: 0.6 });
281
+ // qp0 < 1 -> multiplié par 1000 => 200 (passage kW -> W)
282
+ expect(di.qp0).toBe(200);
283
+ // valeur de référence de régression (chaudière standard, pn = 20000, qp0 corrigé à 200)
284
+ expect(di.rg).toBeCloseTo(0.8566147558351076, 9);
285
+ warnSpy.mockRestore();
286
+ });
287
+
288
+ test('la présence de régulation modifie le rendement (tf30 vs tf100)', () => {
289
+ const base = () => ({
290
+ pn: 25000,
291
+ rpn: 0.95,
292
+ rpint: 0.92,
293
+ qp0: 150,
294
+ temp_fonc_30: 35,
295
+ temp_fonc_100: 70
296
+ });
297
+ const deCommun = {
298
+ enum_type_generateur_ch_id: '96',
299
+ enum_type_energie_id: '2',
300
+ type_energie: 'gaz naturel',
301
+ description: 't'
302
+ };
303
+ const diAvec = base();
304
+ calc_generateur_combustion_ch(
305
+ {},
306
+ diAvec,
307
+ { ...deCommun, presence_regulation_combustion: true },
308
+ {
309
+ cdimref: 0.8,
310
+ cdimrefDep: 0.6
311
+ }
312
+ );
313
+ const diSans = base();
314
+ calc_generateur_combustion_ch(
315
+ {},
316
+ diSans,
317
+ { ...deCommun, presence_regulation_combustion: false },
318
+ { cdimref: 0.8, cdimrefDep: 0.6 }
319
+ );
320
+ // Avec régulation (tf plus basse) le rendement est plus élevé
321
+ expect(diAvec.rg).toBeGreaterThan(diSans.rg);
322
+ });
323
+ });
@@ -0,0 +1,85 @@
1
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * Dépendance mockée : `getFicheTechnique` (import par défaut), qui fournit l'année
5
+ * d'installation utilisée pour choisir le générateur équivalent. On contrôle sa valeur
6
+ * de retour ; son fonctionnement interne n'est pas testé ici.
7
+ */
8
+ vi.mock('./ficheTechnique.js', () => ({
9
+ default: vi.fn()
10
+ }));
11
+
12
+ const { updateGenerateurChaudieres } = await import('./13.2_generateur_combustion_chaudiere.js');
13
+ const { default: getFicheTechnique } = await import('./ficheTechnique.js');
14
+
15
+ beforeEach(() => {
16
+ vi.mocked(getFicheTechnique).mockReset();
17
+ });
18
+
19
+ /**
20
+ * 13.2 - Les "autres systèmes à combustion" sont assimilés à des chaudières standard
21
+ * de la période correspondante.
22
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2
23
+ */
24
+ describe('updateGenerateurChaudieres - substitution par une chaudière équivalente', () => {
25
+ test('type non concerné : aucune substitution, pas de champ previous', () => {
26
+ getFicheTechnique.mockReturnValue(undefined);
27
+ const de = { enum_type_generateur_ch_id: '90' };
28
+ updateGenerateurChaudieres({}, de, 'ch');
29
+ expect(de.enum_type_generateur_ch_id).toBe('90');
30
+ expect(de.previous_enum_type_generateur_ch_id).toBeUndefined();
31
+ });
32
+
33
+ test('sans fiche technique : identifiant conservé mais previous renseigné (ch, gaz 113)', () => {
34
+ getFicheTechnique.mockReturnValue(undefined);
35
+ const de = { enum_type_generateur_ch_id: '113' };
36
+ updateGenerateurChaudieres({}, de, 'ch');
37
+ expect(de.enum_type_generateur_ch_id).toBe('113');
38
+ expect(de.previous_enum_type_generateur_ch_id).toBe('113');
39
+ });
40
+
41
+ test('fiche "avant 1948" : chaudière la plus ancienne de la table (ch, gaz 113 -> 85)', () => {
42
+ getFicheTechnique.mockReturnValue({ valeur: 'avant 1948' });
43
+ const de = { enum_type_generateur_ch_id: '113' };
44
+ updateGenerateurChaudieres({}, de, 'ch');
45
+ expect(de.enum_type_generateur_ch_id).toBe('85');
46
+ expect(de.previous_enum_type_generateur_ch_id).toBe('113');
47
+ });
48
+
49
+ test('année 1985 : seuil 1981 retenu (ch, gaz 113 -> 86)', () => {
50
+ getFicheTechnique.mockReturnValue({ valeur: '1985' });
51
+ const de = { enum_type_generateur_ch_id: '113' };
52
+ updateGenerateurChaudieres({}, de, 'ch');
53
+ expect(de.enum_type_generateur_ch_id).toBe('86');
54
+ });
55
+
56
+ test('année postérieure au dernier seuil : chaudière la plus récente (ch, gaz 113 -> 90)', () => {
57
+ getFicheTechnique.mockReturnValue({ valeur: '2030' });
58
+ const de = { enum_type_generateur_ch_id: '113' };
59
+ updateGenerateurChaudieres({}, de, 'ch');
60
+ expect(de.enum_type_generateur_ch_id).toBe('90');
61
+ });
62
+
63
+ test('type ECS (fioul 79) : utilise la table ECS et le seuil correspondant', () => {
64
+ getFicheTechnique.mockReturnValue({ valeur: '1972' });
65
+ const de = { enum_type_generateur_ecs_id: '79' };
66
+ updateGenerateurChaudieres({}, de, 'ecs');
67
+ // seuils fioul ECS : 1948->35, 1970->36 ... 1972 >= 1970 => 36
68
+ expect(de.enum_type_generateur_ecs_id).toBe('36');
69
+ expect(de.previous_enum_type_generateur_ecs_id).toBe('79');
70
+ });
71
+
72
+ test("la fiche technique est interrogée avec le bon domaine ('7' pour ch, '8' pour ecs)", () => {
73
+ getFicheTechnique.mockReturnValue({ valeur: '2000' });
74
+ updateGenerateurChaudieres({}, { enum_type_generateur_ch_id: '113' }, 'ch');
75
+ expect(getFicheTechnique).toHaveBeenCalledWith({}, '7', 'année', [
76
+ 'Autre système à combustion'
77
+ ]);
78
+
79
+ getFicheTechnique.mockClear();
80
+ updateGenerateurChaudieres({}, { enum_type_generateur_ecs_id: '79' }, 'ecs');
81
+ expect(getFicheTechnique).toHaveBeenCalledWith({}, '8', 'année', [
82
+ 'Autre système à combustion'
83
+ ]);
84
+ });
85
+ });
@@ -0,0 +1,64 @@
1
+ import { describe, expect, test } from 'vitest';
2
+
3
+ /**
4
+ * Ce module ne dépend d'aucune table de valeurs ni d'aucun enum : la logique est
5
+ * entièrement déterminée par les données d'entrée. Aucun mock n'est donc nécessaire.
6
+ */
7
+ const { updateGenerateurPacs } = await import('./13.2_generateur_pac.js');
8
+
9
+ /**
10
+ * 13.2 - Substitution des générateurs "autre système thermodynamique" par un CET
11
+ * sur air ambiant équivalent selon la période d'installation.
12
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2
13
+ */
14
+ describe('updateGenerateurPacs - substitution des générateurs thermodynamiques', () => {
15
+ test("aucune période d'installation renseignée : aucune modification", () => {
16
+ const de = { enum_type_generateur_ecs_id: '82' };
17
+ updateGenerateurPacs({}, de, 'ecs');
18
+ expect(de.enum_type_generateur_ecs_id).toBe('82');
19
+ });
20
+
21
+ test.each([
22
+ ['1', '10'],
23
+ ['2', '11'],
24
+ ['3', '12']
25
+ ])(
26
+ 'ECS type 82 (autre système thermodynamique électrique), période %s : générateur remplacé par %s',
27
+ (periode, attendu) => {
28
+ const de = {
29
+ enum_type_generateur_ecs_id: '82',
30
+ enum_periode_installation_ecs_thermo_id: periode
31
+ };
32
+ updateGenerateurPacs({}, de, 'ecs');
33
+ expect(de.enum_type_generateur_ecs_id).toBe(attendu);
34
+ }
35
+ );
36
+
37
+ test('ECS type 82 avec période inconnue : type conservé tel quel', () => {
38
+ const de = {
39
+ enum_type_generateur_ecs_id: '82',
40
+ enum_periode_installation_ecs_thermo_id: '9'
41
+ };
42
+ updateGenerateurPacs({}, de, 'ecs');
43
+ expect(de.enum_type_generateur_ecs_id).toBe('82');
44
+ });
45
+
46
+ test('ECS type différent de 82 : aucune substitution même avec une période', () => {
47
+ const de = {
48
+ enum_type_generateur_ecs_id: '50',
49
+ enum_periode_installation_ecs_thermo_id: '1'
50
+ };
51
+ updateGenerateurPacs({}, de, 'ecs');
52
+ expect(de.enum_type_generateur_ecs_id).toBe('50');
53
+ });
54
+
55
+ test('type ch avec une période : la substitution ECS ne s’applique pas', () => {
56
+ const de = {
57
+ enum_type_generateur_ch_id: '82',
58
+ enum_periode_installation_ecs_thermo_id: '1'
59
+ };
60
+ updateGenerateurPacs({}, de, 'ch');
61
+ // La branche de substitution est réservée au type 'ecs' : valeur inchangée
62
+ expect(de.enum_type_generateur_ch_id).toBe('82');
63
+ });
64
+ });
@@ -0,0 +1,38 @@
1
+ import { describe, expect, test, vi } from 'vitest';
2
+
3
+ /**
4
+ * Dépendances mockées pour isoler le module :
5
+ * - `enums.type_installation` : libellé du type d'installation dérivé de l'identifiant d'entrée.
6
+ * La fonction actuelle se contente de résoudre le type d'installation sans modifier l'objet ECS ;
7
+ * ces tests figent ce contrat (aucune mutation, retour indéfini) pour détecter toute régression.
8
+ */
9
+ vi.mock('./enums.js', () => ({
10
+ default: {
11
+ type_installation: { 1: 'installation individuelle', 2: 'installation collective' }
12
+ }
13
+ }));
14
+
15
+ const { default: rendement_distribution_ecs } = await import('./13_rendement_distribution_ecs.js');
16
+
17
+ /**
18
+ * 13. Rendement de distribution de l'ECS
19
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13
20
+ */
21
+ describe('rendement_distribution_ecs', () => {
22
+ test('ne retourne aucune valeur (fonction sans effet de sortie)', () => {
23
+ const ecs = { donnee_entree: { enum_type_installation_id: '1' } };
24
+ expect(rendement_distribution_ecs(ecs)).toBeUndefined();
25
+ });
26
+
27
+ test('ne mute pas les données d’entrée de l’installation ECS', () => {
28
+ const de = { enum_type_installation_id: '2' };
29
+ const ecs = { donnee_entree: de };
30
+ rendement_distribution_ecs(ecs);
31
+ expect(ecs.donnee_entree).toEqual({ enum_type_installation_id: '2' });
32
+ });
33
+
34
+ test('accepte un type d’installation inconnu sans lever d’erreur', () => {
35
+ const ecs = { donnee_entree: { enum_type_installation_id: '999' } };
36
+ expect(() => rendement_distribution_ecs(ecs)).not.toThrow();
37
+ });
38
+ });
@@ -165,14 +165,14 @@ function type_generateur_ecs(di, de, du, usage_generateur) {
165
165
  return type_generateur;
166
166
  }
167
167
 
168
- function rg_chauffe_eau_gaz(di, besoin_ecs) {
168
+ export function rg_chauffe_eau_gaz(di, besoin_ecs) {
169
169
  return (
170
170
  1 /
171
171
  (1 / di.rpn + 1790 * (di.qp0 / (besoin_ecs * 1000)) + 6970 * (di.pveil / (besoin_ecs * 1000)))
172
172
  );
173
173
  }
174
174
 
175
- function rgrs_chaudiere(di, besoin_ecs) {
175
+ export function rgrs_chaudiere(di, besoin_ecs) {
176
176
  return (
177
177
  1 /
178
178
  (1 / di.rpn +
@@ -181,7 +181,7 @@ function rgrs_chaudiere(di, besoin_ecs) {
181
181
  );
182
182
  }
183
183
 
184
- function rg_accumulateur_gaz(di, besoin_ecs) {
184
+ export function rg_accumulateur_gaz(di, besoin_ecs) {
185
185
  return (
186
186
  1 /
187
187
  (1 / di.rpn +
@@ -190,7 +190,7 @@ function rg_accumulateur_gaz(di, besoin_ecs) {
190
190
  );
191
191
  }
192
192
 
193
- function rgrsReseauUrbain(de, ecs_de) {
193
+ export function rgrsReseauUrbain(de, ecs_de) {
194
194
  /**
195
195
  * Le rendement de génération d'un réseau de chaleur dépend de son caractère isolé ou non.
196
196
  * D'après la spécification 3CL-DPE, le type de RCU est porté par enum_type_generateur_ecs_id :