@open3cl/engine 1.0.6 → 1.0.8

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 (72) hide show
  1. package/11_nadeq.spec.js +3 -2
  2. package/16.2_production_enr.spec.js +1 -0
  3. package/3.2.1_mur.spec.js +1 -0
  4. package/3.2.2_plancher_bas.spec.js +1 -0
  5. package/3.3_baie_vitree.spec.js +1 -0
  6. package/6.1_apport_gratuit.spec.js +1 -0
  7. package/7_inertie.spec.js +4 -3
  8. package/9_chauffage.spec.js +1 -0
  9. package/9_conso_ch.spec.js +1 -0
  10. package/9_generateur_ch.spec.js +1 -0
  11. package/conso.spec.js +1 -0
  12. package/core/assets/domain/synchronize-assets.spec.js +9 -9
  13. package/core/assets/domain/synchronize-c1-tables.spec.js +4 -4
  14. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +6 -6
  15. package/core/assets/domain/synchronize-enum-tables.spec.js +10 -10
  16. package/core/assets/domain/synchronize-solicitations-tables.spec.js +6 -6
  17. package/core/assets/domain/synchronize-valeur-tables.spec.js +14 -14
  18. package/core/file/infrastructure/adapter/file.store.spec.js +5 -4
  19. package/core/tv/infrastructure/tvs.store.spec.js +3 -2
  20. package/core/util/infrastructure/object-util.spec.js +2 -1
  21. package/core/util/logger/log-service.js +47 -0
  22. package/features/dpe/domain/models/baie-ets.model.ts +12 -0
  23. package/features/dpe/domain/models/baie-vitree.model.ts +97 -0
  24. package/features/dpe/domain/models/climatisation.model.ts +25 -0
  25. package/features/dpe/domain/models/dpe.model.ts +194 -0
  26. package/features/dpe/domain/models/ets.model.ts +19 -0
  27. package/features/dpe/domain/models/installation-chauffage.model.ts +101 -0
  28. package/features/dpe/domain/models/installation-ecs.model.ts +76 -0
  29. package/features/dpe/domain/models/mur.model.ts +37 -0
  30. package/features/dpe/domain/models/plancher-bas.model.ts +38 -0
  31. package/features/dpe/domain/models/plancher-haut.model.ts +33 -0
  32. package/features/dpe/domain/models/pont-thermique.model.ts +21 -0
  33. package/features/dpe/domain/models/porte.model.ts +31 -0
  34. package/features/dpe/domain/models/production-elec-enr.model.ts +27 -0
  35. package/features/dpe/domain/models/sortie.model.ts +178 -0
  36. package/features/dpe/domain/models/type-habitation.model.js +8 -0
  37. package/features/dpe/domain/models/type-ventilation.model.js +8 -0
  38. package/features/dpe/domain/models/ventilation.model.ts +33 -0
  39. package/features/dpe/infrastructure/baieVitreeTv.store.js +292 -0
  40. package/features/dpe/infrastructure/baieVitreeTv.store.spec.js +352 -0
  41. package/features/dpe/infrastructure/tv.store.js +356 -0
  42. package/features/dpe/infrastructure/tv.store.spec.js +607 -0
  43. package/features/engine/domain/constants.js +1 -0
  44. package/features/engine/domain/contexte.builder.js +140 -0
  45. package/features/engine/domain/contexte.builder.spec.js +152 -0
  46. package/features/engine/domain/engine.service.js +139 -0
  47. package/features/engine/domain/engine.service.spec.js +7 -0
  48. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.js +292 -0
  49. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +484 -0
  50. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.js +278 -0
  51. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +282 -0
  52. package/features/engine/domain/enveloppe/deperdition.service.js +101 -0
  53. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.js +168 -0
  54. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +345 -0
  55. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.js +169 -0
  56. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +200 -0
  57. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.js +136 -0
  58. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +211 -0
  59. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.js +56 -0
  60. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +116 -0
  61. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.js +338 -0
  62. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +442 -0
  63. package/features/engine/domain/models/contexte.model.ts +10 -0
  64. package/features/engine/domain/models/deperdition.model.ts +8 -0
  65. package/features/normalizer/domain/dpe-normalizer.service.js +24 -0
  66. package/features/normalizer/domain/dpe-normalizer.service.spec.js +3 -0
  67. package/ficheTechnique.spec.js +4 -3
  68. package/output.js +1 -0
  69. package/package.json +9 -8
  70. package/tv-v2.js +79121 -0
  71. package/utils.js +2 -2
  72. package/utils.spec.js +4 -3
@@ -0,0 +1,352 @@
1
+ import { beforeEach, describe, expect, test } from 'vitest';
2
+ import { BaieVitreeTvStore } from './baieVitreeTv.store.js';
3
+
4
+ /** @type {BaieVitreeTvStore} **/
5
+ let tvStore;
6
+
7
+ describe('Lecture des tables de valeurs', () => {
8
+ beforeEach(() => {
9
+ tvStore = new BaieVitreeTvStore();
10
+ });
11
+
12
+ describe('lecture des valeurs de ug', () => {
13
+ test.each([
14
+ {
15
+ label: 'Simple vitrage',
16
+ enumTypeVitrageId: '1',
17
+ expected: 5.8
18
+ },
19
+ {
20
+ label: "Survitrage non traité avec lame d'air 6mm",
21
+ enumTypeVitrageId: '4',
22
+ epaisseurLame: 6,
23
+ expected: 3.4
24
+ },
25
+ {
26
+ label: "Survitrage non traité avec lame d'air 20mm",
27
+ enumTypeVitrageId: '4',
28
+ epaisseurLame: 20,
29
+ expected: 2.8
30
+ },
31
+ {
32
+ label: "Double vitrage vertical traité avec lame d'air argon 14mm",
33
+ enumTypeVitrageId: '2',
34
+ epaisseurLame: 14,
35
+ enumTypeGazLameId: '2',
36
+ enumInclinaisonVitrageId: '1',
37
+ vitrageVir: '1',
38
+ expected: 1.2
39
+ },
40
+ {
41
+ label: "Double vitrage vertical non traité avec lame d'air argon 14mm",
42
+ enumTypeVitrageId: '2',
43
+ epaisseurLame: 14,
44
+ enumTypeGazLameId: '2',
45
+ enumInclinaisonVitrageId: '1',
46
+ vitrageVir: '0',
47
+ expected: 2.8
48
+ },
49
+ {
50
+ label: "Double vitrage horizontal non traité avec lame d'air inconnu 15mm",
51
+ enumTypeVitrageId: '2',
52
+ epaisseurLame: 15,
53
+ enumTypeGazLameId: '3',
54
+ enumInclinaisonVitrageId: '4',
55
+ vitrageVir: '0',
56
+ expected: 2.9
57
+ },
58
+ {
59
+ label: "Double vitrage horizontal non traité avec lame d'air inconnu 12mm",
60
+ enumTypeVitrageId: '2',
61
+ epaisseurLame: 12,
62
+ enumTypeGazLameId: '3',
63
+ enumInclinaisonVitrageId: '4',
64
+ vitrageVir: '0',
65
+ expected: 3.1
66
+ }
67
+ ])(
68
+ `ug pour baie vitrée $label`,
69
+ ({
70
+ enumTypeVitrageId,
71
+ enumTypeGazLameId = undefined,
72
+ enumInclinaisonVitrageId = undefined,
73
+ vitrageVir = undefined,
74
+ epaisseurLame = undefined,
75
+ expected
76
+ }) => {
77
+ expect(
78
+ tvStore.getUg(
79
+ enumTypeVitrageId,
80
+ enumTypeGazLameId,
81
+ enumInclinaisonVitrageId,
82
+ vitrageVir,
83
+ epaisseurLame
84
+ )
85
+ ).toBe(expected);
86
+ }
87
+ );
88
+
89
+ test('Récupération des valeurs des épaisseurs disponibles pour les valeurs de ug', () => {
90
+ expect(tvStore.getEpaisseurAvailableForUg()).toStrictEqual([
91
+ 0, 6, 8, 10, 12, 14, 15, 16, 18, 20
92
+ ]);
93
+ });
94
+
95
+ test('pas de valeur de ug', () => {
96
+ const ug = tvStore.getUg('0', '0', '0', false, 0);
97
+ expect(ug).toBeUndefined();
98
+ });
99
+ });
100
+
101
+ describe('lecture des valeurs de sw', () => {
102
+ test.each([
103
+ {
104
+ label: 'Paroi en brique de verre pleine',
105
+ enumTypeBaieId: '1',
106
+ enumTypeMateriauxMenuiserieId: '1',
107
+ expected: 0.4
108
+ },
109
+ {
110
+ label: 'Paroi en polycarbonnate',
111
+ enumTypeBaieId: '3',
112
+ enumTypeMateriauxMenuiserieId: '2',
113
+ expected: 0.4
114
+ },
115
+ {
116
+ label: 'Fenêtres battantes',
117
+ enumTypeBaieId: '4',
118
+ enumTypeMateriauxMenuiserieId: '3',
119
+ enumTypeVitrageId: '1',
120
+ expected: 0.58
121
+ },
122
+ {
123
+ label: 'Fenêtres coulissantes',
124
+ enumTypeBaieId: '5',
125
+ enumTypeMateriauxMenuiserieId: '3',
126
+ enumTypeVitrageId: '3',
127
+ enumTypePoseId: '2',
128
+ expected: 0.41
129
+ },
130
+ {
131
+ label: 'Fenêtres coulissantes',
132
+ enumTypeBaieId: '5',
133
+ enumTypeMateriauxMenuiserieId: '3',
134
+ enumTypeVitrageId: '3',
135
+ enumTypePoseId: '2',
136
+ vitrageVir: 1,
137
+ expected: 0.37
138
+ },
139
+ {
140
+ label: 'Fenêtres coulissantes',
141
+ enumTypeBaieId: '5',
142
+ enumTypeMateriauxMenuiserieId: '3',
143
+ enumTypeVitrageId: '3',
144
+ enumTypePoseId: '2',
145
+ vitrageVir: '1',
146
+ expected: 0.37
147
+ }
148
+ ])(
149
+ `sw pour baie vitrée $label`,
150
+ ({
151
+ enumTypeVitrageId = undefined,
152
+ enumTypeBaieId,
153
+ enumTypeMateriauxMenuiserieId,
154
+ vitrageVir = undefined,
155
+ enumTypePoseId = undefined,
156
+ expected
157
+ }) => {
158
+ expect(
159
+ tvStore.getSw(
160
+ enumTypeVitrageId,
161
+ enumTypeBaieId,
162
+ enumTypeMateriauxMenuiserieId,
163
+ vitrageVir,
164
+ enumTypePoseId
165
+ )
166
+ ).toBe(expected);
167
+ }
168
+ );
169
+
170
+ test('pas de valeur de sw', () => {
171
+ const ug = tvStore.getSw('0', '0', '0', '0', '0');
172
+ expect(ug).toBeUndefined();
173
+ });
174
+ });
175
+
176
+ describe('lecture des valeurs de uw', () => {
177
+ test.each([
178
+ {
179
+ label: 'Paroi en brique de verre pleine',
180
+ enumTypeBaieId: '1',
181
+ enumTypeMateriauxMenuiserieId: '1',
182
+ expected: 3.5
183
+ },
184
+ {
185
+ label: 'Paroi en polycarbonnate',
186
+ enumTypeBaieId: '3',
187
+ enumTypeMateriauxMenuiserieId: '2',
188
+ expected: 3
189
+ },
190
+ {
191
+ label: 'uw non extrapolé pour Fenêtres battantes',
192
+ enumTypeBaieId: '4',
193
+ enumTypeMateriauxMenuiserieId: '6',
194
+ ug: 0.5,
195
+ expected: 1.3
196
+ },
197
+ {
198
+ label: 'uw extrapolé pour Fenêtres coulissantes',
199
+ enumTypeBaieId: '5',
200
+ enumTypeMateriauxMenuiserieId: '6',
201
+ ug: '2.45',
202
+ expected: 3.05
203
+ },
204
+ {
205
+ label: 'uw extrapolé pour Fenêtres coulissantes',
206
+ enumTypeBaieId: '5',
207
+ enumTypeMateriauxMenuiserieId: '6',
208
+ ug: '8',
209
+ expected: 8
210
+ }
211
+ ])(
212
+ `uw pour baie vitrée $label`,
213
+ ({ enumTypeBaieId, enumTypeMateriauxMenuiserieId, ug = undefined, expected }) => {
214
+ expect(tvStore.getUw(enumTypeBaieId, enumTypeMateriauxMenuiserieId, ug)).toBeCloseTo(
215
+ expected,
216
+ 2
217
+ );
218
+ }
219
+ );
220
+
221
+ test('pas de valeur de uw', () => {
222
+ const ug = tvStore.getSw('0', '0', 0);
223
+ expect(ug).toBeUndefined();
224
+ });
225
+ });
226
+
227
+ describe('lecture des valeurs de ujn', () => {
228
+ test.each([
229
+ {
230
+ label: 'ujn non extrapolé',
231
+ enumTypeFermetureId: '2',
232
+ uw: 0.8,
233
+ expected: '0.8'
234
+ },
235
+ {
236
+ label: 'ujn extrapolé',
237
+ enumTypeFermetureId: '2',
238
+ uw: 0.85,
239
+ expected: '0.85'
240
+ },
241
+ {
242
+ label: 'ujn extrapolé > à la borne maximale des valeurs connues',
243
+ enumTypeFermetureId: '2',
244
+ uw: 7,
245
+ expected: '5.9'
246
+ }
247
+ ])(`ujn pour baie vitrée $label`, ({ enumTypeFermetureId, uw, expected }) => {
248
+ expect(tvStore.getUjn(enumTypeFermetureId, uw)).toBeCloseTo(expected, 2);
249
+ });
250
+
251
+ test('pas de valeur de ujn', () => {
252
+ const ug = tvStore.getUjn('0', 0);
253
+ expect(ug).toBeUndefined();
254
+ });
255
+ });
256
+
257
+ describe('lecture des valeurs de deltar', () => {
258
+ test.each([
259
+ {
260
+ label: 'Jalousie accordéon',
261
+ enumTypeFermetureId: '2',
262
+ expected: 0.08
263
+ },
264
+ {
265
+ label: 'Fermeture isolée sans ajours',
266
+ enumTypeFermetureId: '8',
267
+ expected: 0.25
268
+ }
269
+ ])(`deltar pour baie vitrée $label`, ({ enumTypeFermetureId, expected }) => {
270
+ expect(tvStore.getDeltar(enumTypeFermetureId)).toBe(expected);
271
+ });
272
+
273
+ test('pas de valeur de deltar', () => {
274
+ const ug = tvStore.getDeltar('0');
275
+ expect(ug).toBeUndefined();
276
+ });
277
+ });
278
+
279
+ describe('lecture des valeurs des masques lointains non homogènes', () => {
280
+ test.each([
281
+ {
282
+ label: '2 secteurs lateraux',
283
+ tvCoeffMasqueLointainNonHomogeneId: '1',
284
+ expected: 0
285
+ },
286
+ {
287
+ label: '2 secteurs lateraux',
288
+ tvCoeffMasqueLointainNonHomogeneId: '4',
289
+ expected: 15
290
+ }
291
+ ])(`ombre pour baie vitrée $label`, ({ tvCoeffMasqueLointainNonHomogeneId, expected }) => {
292
+ expect(tvStore.getOmbre(tvCoeffMasqueLointainNonHomogeneId)).toBe(expected);
293
+ });
294
+
295
+ test('pas de valeur de masque lointain non homogène', () => {
296
+ const ug = tvStore.getOmbre('0');
297
+ expect(ug).toBeUndefined();
298
+ });
299
+ });
300
+
301
+ describe('lecture des valeurs des masques proches', () => {
302
+ test.each([
303
+ {
304
+ label: 'Baie en fond de balcon ou fond et flanc de loggias',
305
+ tvCoefMasqueProcheId: '1',
306
+ expected: 0.4
307
+ },
308
+ {
309
+ label: 'Baie sous un balcon ou auvent',
310
+ tvCoefMasqueProcheId: '13',
311
+ expected: 0.8
312
+ }
313
+ ])(`incidence masque proche pour baie vitrée $label`, ({ tvCoefMasqueProcheId, expected }) => {
314
+ expect(tvStore.getMasqueProche(tvCoefMasqueProcheId)).toBe(expected);
315
+ });
316
+
317
+ test('pas de valeur de masque proche', () => {
318
+ const ug = tvStore.getMasqueProche('0');
319
+ expect(ug).toBeUndefined();
320
+ });
321
+ });
322
+
323
+ describe('lecture des valeurs des masques lointains homogènes', () => {
324
+ test.each([
325
+ {
326
+ label: 'Orientation nord, hauteur 15 ≤….< 30',
327
+ tvCoefMasqueLointainHomogeneId: '2',
328
+ expected: 0.82
329
+ },
330
+ {
331
+ label: 'Orientation sud, hauteur 60 ≤….< 90',
332
+ tvCoefMasqueLointainHomogeneId: '8',
333
+ expected: 0.1
334
+ }
335
+ ])(
336
+ `incidence masque proche pour baie vitrée $label`,
337
+ ({ tvCoefMasqueLointainHomogeneId, expected }) => {
338
+ expect(tvStore.getMasqueLointainHomogene(tvCoefMasqueLointainHomogeneId)).toBe(expected);
339
+ }
340
+ );
341
+
342
+ test('pas de valeur de masque lointain homogène', () => {
343
+ const ug = tvStore.getMasqueLointainHomogene('0');
344
+ expect(ug).toBeUndefined();
345
+ });
346
+ });
347
+
348
+ test('lecture des épaisseurs disponibles pour les coefficients de transmission thermique ug', () => {
349
+ const ug = tvStore.getEpaisseurAvailableForUg();
350
+ expect(ug).toStrictEqual([0, 6, 8, 10, 12, 14, 15, 16, 18, 20]);
351
+ });
352
+ });
@@ -0,0 +1,356 @@
1
+ import { tvs as tv } from '../../../tv-v2.js';
2
+ import { getRange } from '../../../utils.js';
3
+ import { logger } from '../../../core/util/logger/log-service.js';
4
+ import { TypeHabitation } from '../domain/models/type-habitation.model.js';
5
+
6
+ /**
7
+ * Accès aux données des tables de valeurs
8
+ *
9
+ * /!\ Les tableaux des valeurs doivent souvent être ordonnés (ex: epaisseur_structure pour umur0)
10
+ */
11
+ export class TvStore {
12
+ /**
13
+ * Coefficients U des portes
14
+ * Si le coefficient U des portes est connu et justifié, le saisir directement. Sinon, prendre les valeurs forfaitaires
15
+ *
16
+ * @see Chapitre 3.3.4
17
+ *
18
+ * @param enumTypePorteId {string} Identifiant du type de porte
19
+ * @return {number|undefined} Uporte si trouvé, sinon undefined
20
+ */
21
+ getUPorte(enumTypePorteId) {
22
+ const uPorte = tv['uporte'].find((v) =>
23
+ v.enum_type_porte_id.split('|').includes(enumTypePorteId)
24
+ )?.uporte;
25
+
26
+ if (!uPorte) {
27
+ logger.error(`Pas de valeur forfaitaire uPorte pour enumTypePorteId:${enumTypePorteId}`);
28
+ return;
29
+ }
30
+
31
+ logger.debug(`uPorte pour type ${enumTypePorteId} = ${uPorte}`);
32
+ return parseFloat(uPorte);
33
+ }
34
+
35
+ /**
36
+ * Coefficient de réduction des déperditions b
37
+ * @param enumTypeAdjacenceId {string}
38
+ * @param uVue {number|undefined}
39
+ * @param enumCfgIsolationLncId {string|undefined}
40
+ * @param rAiuAue {number|undefined}
41
+ * @param zc {string|undefined}
42
+ * @return {number|undefined}
43
+ */
44
+ getB(
45
+ enumTypeAdjacenceId,
46
+ uVue = undefined,
47
+ enumCfgIsolationLncId = undefined,
48
+ rAiuAue = undefined,
49
+ zc = undefined
50
+ ) {
51
+ const b = tv['coef_reduction_deperdition'].find(
52
+ (v) =>
53
+ (!enumTypeAdjacenceId ||
54
+ v.enum_type_adjacence_id.split('|').includes(enumTypeAdjacenceId)) &&
55
+ (!uVue || !v.uvue || parseFloat(v.uvue) === uVue) &&
56
+ (!enumCfgIsolationLncId ||
57
+ !v.enum_cfg_isolation_lnc_id ||
58
+ v.enum_cfg_isolation_lnc_id === enumCfgIsolationLncId) &&
59
+ (!zc ||
60
+ !v.zone_climatique ||
61
+ zc.toLowerCase().startsWith(v.zone_climatique.toLowerCase())) &&
62
+ (!rAiuAue ||
63
+ ((!v.aiu_aue_min || v.aiu_aue_min < rAiuAue) &&
64
+ (!v.aiu_aue_max || v.aiu_aue_max >= rAiuAue)))
65
+ )?.b;
66
+
67
+ if (!b) {
68
+ logger.error(`Pas de valeur forfaitaire b pour enumTypeAdjacenceId:${enumTypeAdjacenceId}`);
69
+ return;
70
+ }
71
+
72
+ logger.debug(`b pour enumTypeAdjacenceId ${enumTypeAdjacenceId} = ${b}`);
73
+ return parseFloat(b);
74
+ }
75
+
76
+ /**
77
+ * Coefficient surfacique équivalent
78
+ * @param enumTypeAdjacenceId {string}
79
+ * @return {number|undefined}
80
+ */
81
+ getUVue(enumTypeAdjacenceId) {
82
+ const uvue = tv['uvue'].find((v) => v.enum_type_adjacence_id === enumTypeAdjacenceId)?.uvue;
83
+
84
+ if (!uvue) {
85
+ logger.error(
86
+ `Pas de valeur forfaitaire uVue pour enumTypeAdjacenceId:${enumTypeAdjacenceId}`
87
+ );
88
+ return;
89
+ }
90
+
91
+ logger.debug(`uvue pour enumTypeAdjacenceId ${enumTypeAdjacenceId} = ${uvue}`);
92
+ return parseFloat(uvue);
93
+ }
94
+
95
+ /**
96
+ * Coefficient de transmission thermique du mur non isolé
97
+ * @param enumMateriauxStructureMurId {string}
98
+ * @param epaisseurStructure {number|undefined}
99
+ * @return {number|undefined}
100
+ */
101
+ getUmur0(enumMateriauxStructureMurId, epaisseurStructure = undefined) {
102
+ const umur0 = tv['umur0']
103
+ .filter((v) => v.enum_materiaux_structure_mur_id === enumMateriauxStructureMurId)
104
+ .find(
105
+ (v, idx, items) =>
106
+ !v.epaisseur_structure ||
107
+ !epaisseurStructure ||
108
+ !items[idx + 1] ||
109
+ epaisseurStructure < parseFloat(items[idx + 1].epaisseur_structure)
110
+ )?.umur0;
111
+
112
+ if (!umur0) {
113
+ logger.error(
114
+ `Pas de valeur forfaitaire umur0 pour enumMateriauxStructureMurId:${enumMateriauxStructureMurId}`
115
+ );
116
+ return;
117
+ }
118
+
119
+ logger.debug(
120
+ `umur0 pour enumMateriauxStructureMurId ${enumMateriauxStructureMurId} = ${umur0}`
121
+ );
122
+ return parseFloat(umur0);
123
+ }
124
+
125
+ /**
126
+ * Coefficient de transmission thermique du mur
127
+ * @param enumPeriodeConstructionId {string}
128
+ * @param enumZoneClimatiqueId {string}
129
+ * @param effetJoule {boolean}
130
+ * @return {number|undefined}
131
+ */
132
+ getUmur(enumPeriodeConstructionId, enumZoneClimatiqueId, effetJoule = false) {
133
+ const umur = tv['umur'].find(
134
+ (v) =>
135
+ v.enum_periode_construction_id.split('|').includes(enumPeriodeConstructionId) &&
136
+ v.enum_zone_climatique_id.split('|').includes(enumZoneClimatiqueId) &&
137
+ effetJoule === (parseInt(v.effet_joule) === 1)
138
+ )?.umur;
139
+
140
+ if (!umur) {
141
+ logger.error(
142
+ `Pas de valeur forfaitaire umur pour enumPeriodeConstructionId:${enumPeriodeConstructionId}, enumPeriodeConstructionId:${enumPeriodeConstructionId}`
143
+ );
144
+ return;
145
+ }
146
+
147
+ logger.debug(
148
+ `umur pour enumPeriodeConstructionId:${enumPeriodeConstructionId}, enumPeriodeConstructionId:${enumPeriodeConstructionId} = ${umur}`
149
+ );
150
+ return parseFloat(umur);
151
+ }
152
+
153
+ /**
154
+ * Coefficient de transmission thermique du plancher bas
155
+ * @param enumTypePlancherBasId {string}
156
+ * @return {number|undefined}
157
+ */
158
+ getUpb0(enumTypePlancherBasId) {
159
+ const upbO = tv['upb0'].find(
160
+ (v) => v.enum_type_plancher_bas_id === enumTypePlancherBasId
161
+ )?.upb0;
162
+
163
+ if (!upbO) {
164
+ logger.error(
165
+ `Pas de valeur forfaitaire upbO pour enumTypePlancherBasId:${enumTypePlancherBasId}`
166
+ );
167
+ return;
168
+ }
169
+
170
+ logger.debug(`upbO pour enumTypePlancherBasId ${enumTypePlancherBasId} = ${upbO}`);
171
+ return parseFloat(upbO);
172
+ }
173
+
174
+ /**
175
+ * Coefficient de transmission thermique du plancher bas
176
+ * @param enumPeriodeConstructionId {string}
177
+ * @param enumZoneClimatiqueId {string}
178
+ * @param effetJoule {boolean}
179
+ * @return {number|undefined}
180
+ */
181
+ getUpb(enumPeriodeConstructionId, enumZoneClimatiqueId, effetJoule = false) {
182
+ const upb = tv['upb'].find(
183
+ (v) =>
184
+ v.enum_zone_climatique_id.split('|').includes(enumZoneClimatiqueId) &&
185
+ v.enum_periode_construction_id.split('|').includes(enumPeriodeConstructionId) &&
186
+ effetJoule === (parseInt(v.effet_joule) === 1)
187
+ )?.upb;
188
+
189
+ if (!upb) {
190
+ logger.error(
191
+ `Pas de valeur forfaitaire upb pour enumPeriodeConstructionId:${enumPeriodeConstructionId}, enumPeriodeConstructionId:${enumPeriodeConstructionId}`
192
+ );
193
+ return;
194
+ }
195
+
196
+ logger.debug(
197
+ `upb pour enumPeriodeConstructionId:${enumPeriodeConstructionId}, enumPeriodeConstructionId:${enumPeriodeConstructionId} = ${upb}`
198
+ );
199
+ return parseFloat(upb);
200
+ }
201
+
202
+ /**
203
+ * Retourne la valeur UE la plus proche
204
+ * @param enumTypeAdjacenceId {string}
205
+ * @param enumPeriodeConstructionId {string}
206
+ * @param dsp {number} Valeur entière la plus proche de 2S/P
207
+ * @param upb {number} Valeur de upb
208
+ * @return {number|undefined}
209
+ */
210
+ getUeByUpd(enumTypeAdjacenceId, enumPeriodeConstructionId, dsp, upb) {
211
+ const ueValues =
212
+ tv['ue'].filter(
213
+ (v) =>
214
+ parseInt(v['2s_p']) === dsp &&
215
+ v.enum_type_adjacence_id.split('|').includes(enumTypeAdjacenceId) &&
216
+ v.enum_periode_construction_id.split('|').includes(enumPeriodeConstructionId)
217
+ ) || [];
218
+
219
+ const ueRange = [...new Set(ueValues.map((value) => value.upb).sort())];
220
+ let ue;
221
+ let upb1, upb2;
222
+
223
+ if (ueValues.length) {
224
+ [upb1, upb2] = getRange(upb, ueRange);
225
+
226
+ const ue1 = ueValues.find((value) => value.upb === upb1)?.ue || 0;
227
+ const ue2 = ueValues.find((value) => value.upb === upb2)?.ue || 0;
228
+
229
+ const delta_ue = Number(ue2) - Number(ue1);
230
+ const delta_upb = upb2 - upb1 || 0;
231
+
232
+ if (delta_upb === 0) {
233
+ ue = Number(ue1);
234
+ } else {
235
+ // Interpolation linéaire si upb n'est pas une valeur connue
236
+ ue = Number(ue1) + (delta_ue * (upb - upb1)) / delta_upb;
237
+ }
238
+ }
239
+
240
+ if (ue === undefined) {
241
+ logger.error(
242
+ `Pas de valeur forfaitaire ue pour enumTypeAdjacenceId:${enumTypeAdjacenceId}, enumPeriodeConstructionId:${enumPeriodeConstructionId}, 2S/P:${dsp}`
243
+ );
244
+ return;
245
+ }
246
+
247
+ logger.debug(
248
+ `ue pour enumTypeAdjacenceId:${enumTypeAdjacenceId}, enumPeriodeConstructionId:${enumPeriodeConstructionId}, 2S/P:${dsp} = ${ue}`
249
+ );
250
+ return parseFloat(ue);
251
+ }
252
+
253
+ /**
254
+ * Coefficient de transmission thermique du plancher haut
255
+ * @param enumTypePlancherHautId {string}
256
+ * @return {number|undefined}
257
+ */
258
+ getUph0(enumTypePlancherHautId) {
259
+ const uph0 = tv['uph0'].find((v) =>
260
+ v.enum_type_plancher_haut_id.split('|').includes(enumTypePlancherHautId)
261
+ )?.uph0;
262
+
263
+ if (!uph0) {
264
+ logger.error(
265
+ `Pas de valeur forfaitaire uph0 pour enumTypePlancherHautId:${enumTypePlancherHautId}`
266
+ );
267
+ return;
268
+ }
269
+
270
+ logger.debug(`upbO pour enumTypePlancherHautId ${enumTypePlancherHautId} = ${uph0}`);
271
+ return parseFloat(uph0);
272
+ }
273
+
274
+ /**
275
+ * Coefficient de transmission thermique du plancher bas
276
+ * @param enumPeriodeConstructionId {string}
277
+ * @param typeToiture {('combles'|'terrasse')}
278
+ * @param enumZoneClimatiqueId {string}
279
+ * @param effetJoule {boolean}
280
+ * @return {number|undefined}
281
+ */
282
+ getUph(enumPeriodeConstructionId, typeToiture, enumZoneClimatiqueId, effetJoule = false) {
283
+ const uph = tv['uph'].find(
284
+ (v) =>
285
+ v.enum_periode_construction_id.split('|').includes(enumPeriodeConstructionId) &&
286
+ v.enum_zone_climatique_id.split('|').includes(enumZoneClimatiqueId) &&
287
+ v.type_toiture === typeToiture &&
288
+ effetJoule === (parseInt(v.effet_joule) === 1)
289
+ )?.uph;
290
+
291
+ if (!uph) {
292
+ logger.error(
293
+ `Pas de valeur forfaitaire uph pour enumPeriodeConstructionId:${enumPeriodeConstructionId}, enumPeriodeConstructionId:${enumPeriodeConstructionId}, typeToiture:${typeToiture}`
294
+ );
295
+ return;
296
+ }
297
+
298
+ logger.debug(
299
+ `uph pour enumPeriodeConstructionId:${enumPeriodeConstructionId}, enumPeriodeConstructionId:${enumPeriodeConstructionId}, typeToiture:${typeToiture} = ${uph}`
300
+ );
301
+ return parseFloat(uph);
302
+ }
303
+
304
+ /**
305
+ * Débits de la ventilation
306
+ * @param typeVentilation {number}
307
+ * @return {object|undefined}
308
+ */
309
+ getDebitsVentilation(typeVentilation) {
310
+ const debitsVentilation = tv['debits_ventilation'].find((v) =>
311
+ v.enum_type_ventilation_id.split('|').includes(typeVentilation)
312
+ );
313
+
314
+ if (!debitsVentilation) {
315
+ logger.error(
316
+ `Pas de valeur forfaitaire debits_ventilation pour enumTypeVentilationId: ${typeVentilation}`
317
+ );
318
+ return;
319
+ }
320
+
321
+ return debitsVentilation;
322
+ }
323
+
324
+ /**
325
+ * Valeur conventionnelle de la perméabilité sous 4Pa
326
+ * @see Chapitre 4 - Calcul des déperditions par renouvellement d’air
327
+ *
328
+ * @param periodConstruction {string}
329
+ * @param typeHabitation {TypeHabitation}
330
+ * @param isolationSurface {string | undefined}
331
+ * @param presenceJointsMenuiserie {string | undefined}
332
+ *
333
+ * @return {object|undefined}
334
+ */
335
+ getQ4paConv(periodConstruction, typeHabitation, isolationSurface, presenceJointsMenuiserie) {
336
+ const q4paConv = tv['q4pa_conv'].find(
337
+ (v) =>
338
+ v.enum_periode_construction_id.split('|').includes(periodConstruction) &&
339
+ v.type_habitation.split('/').includes(TypeHabitation[typeHabitation].toLowerCase()) &&
340
+ (isolationSurface === undefined || v.isolation_surfaces === isolationSurface) &&
341
+ (presenceJointsMenuiserie === undefined ||
342
+ v.presence_joints_menuiserie === presenceJointsMenuiserie)
343
+ );
344
+
345
+ if (!q4paConv) {
346
+ logger.error(
347
+ `Pas de valeur forfaitaire q4pa_conv pour periodConstruction: ${periodConstruction},
348
+ typeHabitation: ${typeHabitation}, isolationSurface: ${isolationSurface},
349
+ presenceJointsMenuiserie: ${presenceJointsMenuiserie}`
350
+ );
351
+ return;
352
+ }
353
+
354
+ return q4paConv;
355
+ }
356
+ }