@open3cl/engine 1.0.0

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 (85) hide show
  1. package/10_besoin_fr.js +76 -0
  2. package/10_clim.js +45 -0
  3. package/11_besoin_ecs.js +25 -0
  4. package/11_ecs.js +95 -0
  5. package/11_nadeq.js +87 -0
  6. package/11_nadeq.spec.js +55 -0
  7. package/12.4_pac.js +54 -0
  8. package/13.2_generateur_combustion.js +295 -0
  9. package/13.2_generateur_combustion_bouilleur.js +173 -0
  10. package/13.2_generateur_combustion_ch.js +195 -0
  11. package/13.2_generateur_combustion_chaudiere.js +151 -0
  12. package/13.2_generateur_pac.js +36 -0
  13. package/13_rendement_distribution_ecs.js +12 -0
  14. package/14_generateur_ecs.js +388 -0
  15. package/15_conso_aux.js +257 -0
  16. package/16.2_production_enr.js +328 -0
  17. package/16.2_production_enr.spec.js +251 -0
  18. package/16_conso_eclairage.js +37 -0
  19. package/2021_04_13_confort_ete.js +61 -0
  20. package/2021_04_13_qualite_isolation.js +174 -0
  21. package/3.1_b.js +141 -0
  22. package/3.2.1_mur.js +331 -0
  23. package/3.2.1_mur.spec.js +46 -0
  24. package/3.2.2_plancher_bas.js +259 -0
  25. package/3.2.2_plancher_bas.spec.js +88 -0
  26. package/3.2.3_plancher_haut.js +158 -0
  27. package/3.3.1.4_porte.js +32 -0
  28. package/3.3_baie_vitree.js +308 -0
  29. package/3.3_baie_vitree.spec.js +333 -0
  30. package/3.4_pont_thermique.js +463 -0
  31. package/3_deperdition.js +258 -0
  32. package/4_ventilation.js +197 -0
  33. package/5_conso_ventilation.js +127 -0
  34. package/6.1_apport_gratuit.js +61 -0
  35. package/6.1_apport_gratuit.spec.js +181 -0
  36. package/6.2_surface_sud_equivalente.js +109 -0
  37. package/7_inertie.js +178 -0
  38. package/7_inertie.spec.js +263 -0
  39. package/8_intermittence.js +5 -0
  40. package/9_besoin_ch.js +198 -0
  41. package/9_chauffage.js +291 -0
  42. package/9_chauffage.spec.js +101 -0
  43. package/9_conso_ch.js +95 -0
  44. package/9_conso_ch.spec.js +255 -0
  45. package/9_emetteur_ch.js +122 -0
  46. package/9_generateur_ch.js +230 -0
  47. package/9_generateur_ch.spec.js +87 -0
  48. package/README.md +43 -0
  49. package/apport_et_besoin.js +55 -0
  50. package/conso.js +529 -0
  51. package/conso.spec.js +90 -0
  52. package/core/assets/domain/add-additionnal-ue-values-tables.js +57 -0
  53. package/core/assets/domain/synchronize-assets.js +29 -0
  54. package/core/assets/domain/synchronize-assets.spec.js +37 -0
  55. package/core/assets/domain/synchronize-c1-tables.js +61 -0
  56. package/core/assets/domain/synchronize-c1-tables.spec.js +35 -0
  57. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.js +73 -0
  58. package/core/assets/domain/synchronize-dpe-ges-limit-values-tables.spec.js +72 -0
  59. package/core/assets/domain/synchronize-enum-tables.js +77 -0
  60. package/core/assets/domain/synchronize-enum-tables.spec.js +31 -0
  61. package/core/assets/domain/synchronize-solicitations-tables.js +72 -0
  62. package/core/assets/domain/synchronize-solicitations-tables.spec.js +47 -0
  63. package/core/assets/domain/synchronize-valeur-tables.js +146 -0
  64. package/core/assets/domain/synchronize-valeur-tables.spec.js +54 -0
  65. package/core/conf/infrastructure/application.config.js +33 -0
  66. package/core/file/infrastructure/adapter/file.store.js +75 -0
  67. package/core/file/infrastructure/adapter/file.store.spec.js +30 -0
  68. package/core/tv/infrastructure/assets/additional-ue-values.js +69 -0
  69. package/core/tv/infrastructure/tvs.store.js +40 -0
  70. package/core/tv/infrastructure/tvs.store.spec.js +34 -0
  71. package/core/util/infrastructure/object-util.js +23 -0
  72. package/core/util/infrastructure/object-util.spec.js +25 -0
  73. package/engine.js +503 -0
  74. package/enums.js +1155 -0
  75. package/ficheTechnique.js +86 -0
  76. package/ficheTechnique.spec.js +181 -0
  77. package/index.js +4 -0
  78. package/package.json +87 -0
  79. package/tv/18.2_sollicitations_ext.ods +0 -0
  80. package/tv/18.5_c1.ods +0 -0
  81. package/tv/dpe_ges_limit_values.ods +0 -0
  82. package/tv.js +80811 -0
  83. package/tvs.d.ts +7 -0
  84. package/utils.js +500 -0
  85. package/utils.spec.js +36 -0
package/3.2.1_mur.js ADDED
@@ -0,0 +1,331 @@
1
+ import enums from './enums.js';
2
+ import {
3
+ tv,
4
+ requestInput,
5
+ requestInputID,
6
+ getKeyByValue,
7
+ bug_for_bug_compat,
8
+ getThicknessFromDescription
9
+ } from './utils.js';
10
+ import b from './3.1_b.js';
11
+
12
+ const scriptName = new URL(import.meta.url).pathname.split('/').pop();
13
+
14
+ function tv_umur0(di, de, du) {
15
+ const matcher = {
16
+ enum_materiaux_structure_mur_id: de.enum_materiaux_structure_mur_id
17
+ };
18
+
19
+ /**
20
+ * Les matériaux 1, 6, 20, 27 ne sont pas concernés par les épaisseurs
21
+ * 1 - inconnu
22
+ * 6 - murs en pan de bois avec remplissage tout venant
23
+ * 20 - cloison de plâtre
24
+ * 27 - murs en ossature bois avec remplissage tout venant
25
+ */
26
+ if (![1, 6, 20, 27].includes(parseInt(de.enum_materiaux_structure_mur_id))) {
27
+ matcher.epaisseur_structure = requestInput(de, du, 'epaisseur_structure', 'float');
28
+ if (!matcher.epaisseur_structure) {
29
+ /**
30
+ * Certains logiciels omettent le champ 'epaisseur_structure'
31
+ * Récupération de cette information si elle existe dans la description via regex "(\d+) cm"
32
+ * @type {number}
33
+ */
34
+ matcher.epaisseur_structure = getThicknessFromDescription(de.description);
35
+
36
+ /**
37
+ * Si on n'a toujours pas de valeur pour 'epaisseur_structure', récupération de la valeur utilisée
38
+ * dans le DPE
39
+ */
40
+ if (bug_for_bug_compat && de.tv_umur0_id && matcher.epaisseur_structure === 0) {
41
+ const rowUmur0 = tv('umur0', {
42
+ tv_umur0_id: de.tv_umur0_id
43
+ });
44
+
45
+ if (rowUmur0 && rowUmur0.epaisseur_structure) {
46
+ matcher.epaisseur_structure = rowUmur0.epaisseur_structure;
47
+
48
+ console.error(
49
+ `Aucune valeur trouvée pour epaisseur_structure (ni saisie, ni présente dans la description) pour le mur '${de.description}'.
50
+ Récupération et utilisation de la valeur ${rowUmur0.epaisseur_structure} depuis tv_umur0_id = ${de.tv_umur0_id}`
51
+ );
52
+ }
53
+ }
54
+ }
55
+ }
56
+ const row = tv('umur0', matcher);
57
+ if (row) {
58
+ di.umur0 = Number(row.umur0);
59
+ de.tv_umur0_id = Number(row.tv_umur0_id);
60
+ } else {
61
+ console.error('!! pas de valeur forfaitaire trouvée pour umur0 !!');
62
+ }
63
+ }
64
+
65
+ function tv_umur(di, de, du, pc_id, zc, effetJoule) {
66
+ if (bug_for_bug_compat && de.tv_umur_id) {
67
+ /**
68
+ * Vérification de la variable effet_joule
69
+ * Certains DPE utilise de manière erronée cette variable. Pour rester cohérent avec le DPE, utilisation de la variable
70
+ * effet_joule telle qu'elle est utilisée dans le DPE
71
+ */
72
+ const rowUmur = tv('umur', {
73
+ tv_umur_id: de.tv_umur_id
74
+ });
75
+
76
+ if (rowUmur && rowUmur.effet_joule !== effetJoule) {
77
+ console.error(
78
+ `La variable effet_joule utilisée dans le DPE pour le mur '${de.description}' est ${rowUmur.effet_joule}.
79
+ Celle-ci devrait être ${effetJoule}. La valeur ${rowUmur.effet_joule} est conservée dans la suite des calculs`
80
+ );
81
+
82
+ effetJoule = rowUmur.effet_joule;
83
+ }
84
+
85
+ if (rowUmur && rowUmur.enum_periode_construction_id.split('|')[0] !== pc_id) {
86
+ console.error(
87
+ `L'année de construction du bâtiment ${pc_id} ne correspond pas à celle utilisée dans le DPE pour le calcul de Umur pour le mur '${de.description}'.
88
+ Celle utilisée ${rowUmur.enum_periode_construction_id}. L'année de construction ${rowUmur.enum_periode_construction_id} est conservée dans la suite des calculs.`
89
+ );
90
+
91
+ pc_id = rowUmur.enum_periode_construction_id.split('|')[0];
92
+ }
93
+ }
94
+
95
+ const matcher = {
96
+ enum_periode_construction_id: pc_id,
97
+ enum_zone_climatique_id: zc,
98
+ effet_joule: effetJoule
99
+ };
100
+ const row = tv('umur', matcher, de);
101
+ if (row) {
102
+ di.umur = Number(row.umur);
103
+ de.tv_umur_id = Number(row.tv_umur_id);
104
+ } else {
105
+ console.error('!! pas de valeur forfaitaire trouvée pour umur !!');
106
+ }
107
+ }
108
+
109
+ function calc_umur0(di, de, du) {
110
+ const umur0_avant = du.umur0_avant;
111
+ const umur_avant = du.umur_avant;
112
+
113
+ const methode_saisie_u0 = requestInput(de, du, 'methode_saisie_u0');
114
+ switch (methode_saisie_u0) {
115
+ case 'type de paroi inconnu (valeur par défaut)':
116
+ /**
117
+ * Si le type de mur est inconnu, Umur0 = Umur_nu = 2.5.
118
+ */
119
+ di.umur0 = '2.5';
120
+ break;
121
+ case 'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire':
122
+ requestInput(de, du, 'materiaux_structure_mur');
123
+ tv_umur0(di, de, du);
124
+ break;
125
+ case 'saisie direct u0 justifiée à partir des documents justificatifs autorisés':
126
+ case "saisie direct u0 correspondant à la performance de la paroi avec son isolation antérieure iti (umur_iti) lorsqu'il y a une surisolation ite réalisée":
127
+ di.umur0 = requestInput(de, du, 'umur0_saisi', 'float');
128
+ break;
129
+ case 'u0 non saisi car le u est saisi connu et justifié.':
130
+ break;
131
+ default:
132
+ console.warn('methode_saisie_u0 inconnue:', methode_saisie_u0);
133
+ }
134
+
135
+ if (de.paroi_ancienne) {
136
+ // le champ `paroi_ancienne` a ete renomme en `enduit_isolant_paroi_ancienne`
137
+ de.enduit_isolant_paroi_ancienne = de.paroi_ancienne;
138
+ }
139
+
140
+ let type_doublage = parseInt(de.enum_type_doublage_id);
141
+
142
+ // Certaines descriptions contiennent des informations sur le type de doublage
143
+ if (bug_for_bug_compat && (type_doublage === 1 || type_doublage === 2)) {
144
+ if (de.description.toLowerCase().indexOf('doublage connu (plâtre, brique') !== -1) {
145
+ type_doublage = 5;
146
+ } else if (de.description.toLowerCase().indexOf('doublage indéterminé avec lame') !== -1) {
147
+ type_doublage = 4;
148
+ } else if (de.description.toLowerCase().indexOf('doublage indéterminé ou lame') !== -1) {
149
+ type_doublage = 3;
150
+ }
151
+ }
152
+
153
+ // Si présence d'un doublage
154
+ if (type_doublage > 2) {
155
+ let umur0Doublage;
156
+
157
+ // 3 - doublage indéterminé ou lame d'air inf 15 mm
158
+ if (type_doublage === 3) {
159
+ umur0Doublage = 1 / (1 / di.umur0 + 0.1);
160
+ } else if (type_doublage === 4 || type_doublage === 5) {
161
+ // 4 - doublage indéterminé avec lame d'air sup 15 mm
162
+ // 5 - doublage connu (plâtre brique bois)
163
+ umur0Doublage = 1 / (1 / di.umur0 + 0.21);
164
+ }
165
+
166
+ /**
167
+ * Certains DPE minorent di.umur0 à 2.5 avant d'appliquer le doublage. On vérifie ici si c'est le cas pour informer que
168
+ * le calcul du doublage se trouvera plus déperditif ici
169
+ */
170
+ if (bug_for_bug_compat) {
171
+ checkDoublageWithMinUmur0(type_doublage, di, de, umur0_avant, umur_avant);
172
+ }
173
+
174
+ di.umur0 = umur0Doublage;
175
+ }
176
+
177
+ if (requestInput(de, du, 'enduit_isolant_paroi_ancienne', 'bool') === 1) {
178
+ const umur0Enduit = 1 / (1 / di.umur0 + 0.7);
179
+ di.umur0Enduit = umur0Enduit;
180
+
181
+ if (
182
+ bug_for_bug_compat &&
183
+ umur0_avant !== undefined &&
184
+ parseFloat(umur0_avant).toFixed(3) === Math.min(2.5, di.umur0).toFixed(3)
185
+ ) {
186
+ console.error(`
187
+ Le mur ${de.description} est une paroi dite 'paroi ancienne avec enduit', la valeur de Umur0 devrait prendre en compte cet enduit et devrait être ${umur0Enduit}.
188
+ Cependant le DPE ne prend pas en compte l'enduit et utilise Umur0=${umur0_avant}. On garde cette valeur ${umur0_avant} dans la suite.
189
+ `);
190
+ di.umur0 = umur0_avant;
191
+ } else {
192
+ di.umur0 = umur0Enduit;
193
+ }
194
+ }
195
+
196
+ di.umur0 = Math.min(2.5, di.umur0);
197
+ }
198
+
199
+ export default function calc_mur(mur, zc, pc_id, effetJoule) {
200
+ const de = mur.donnee_entree;
201
+ const du = {};
202
+ const di = {};
203
+ du.umur0_avant = mur.donnee_intermediaire?.umur0;
204
+ du.umur_avant = mur.donnee_intermediaire?.umur;
205
+
206
+ requestInput(de, du, 'surface_paroi_totale', 'float');
207
+ requestInput(de, du, 'orientation');
208
+
209
+ b(di, de, du, zc);
210
+
211
+ const methode_saisie_u = requestInput(de, du, 'methode_saisie_u');
212
+ switch (methode_saisie_u) {
213
+ case 'non isolé':
214
+ calc_umur0(di, de, du);
215
+ di.umur = Math.min(di.umur0, 2.5);
216
+ break;
217
+ case 'epaisseur isolation saisie justifiée par mesure ou observation':
218
+ case 'epaisseur isolation saisie justifiée à partir des documents justificatifs autorisés': {
219
+ calc_umur0(di, de, du);
220
+ const epaisseurIsolation = requestInput(de, du, 'epaisseur_isolation', 'int') * 0.01;
221
+
222
+ if (epaisseurIsolation) {
223
+ di.umur = 1 / (1 / Number(di.umur0) + epaisseurIsolation / 0.04);
224
+ } else {
225
+ console.error(
226
+ `Le mur ${mur.donnee_entree.description} ne possède pas de donnée d'entrée pour epaisseur_isolation
227
+ alors que methode_saisie_u = 'epaisseur isolation saisie'`
228
+ );
229
+
230
+ di.umur = Math.min(di.umur0, 2.5);
231
+ }
232
+
233
+ break;
234
+ }
235
+ case "resistance isolation saisie justifiée observation de l'isolant installé et mesure de son épaisseur":
236
+ case 'resistance isolation saisie justifiée à partir des documents justificatifs autorisés': {
237
+ calc_umur0(di, de, du);
238
+ const resistanceIsolation = requestInput(de, du, 'resistance_isolation', 'float');
239
+
240
+ if (resistanceIsolation) {
241
+ di.umur = 1 / (1 / Number(di.umur0) + resistanceIsolation);
242
+ } else {
243
+ console.error(
244
+ `Le mur ${mur.donnee_entree.description} ne possède pas de donnée d'entrée pour resistance_isolation
245
+ alors que methode_saisie_u = 'resistance isolation saisie'`
246
+ );
247
+
248
+ di.umur = Math.min(di.umur0, 2.5);
249
+ }
250
+
251
+ break;
252
+ }
253
+ case 'isolation inconnue (table forfaitaire)':
254
+ calc_umur0(di, de, du);
255
+ tv_umur(di, de, du, pc_id, zc, effetJoule);
256
+ di.umur = Math.min(di.umur, di.umur0);
257
+ break;
258
+ case "année d'isolation différente de l'année de construction saisie justifiée (table forfaitaire)": {
259
+ calc_umur0(di, de, du);
260
+ const pi_id = requestInputID(de, du, 'periode_isolation') || pc_id;
261
+ tv_umur(di, de, du, pi_id, zc, effetJoule);
262
+ di.umur = Math.min(di.umur, di.umur0);
263
+ break;
264
+ }
265
+ case 'année de construction saisie (table forfaitaire)': {
266
+ calc_umur0(di, de, du);
267
+ // Si l'année d'isolation est connue, il faut l'utiliser et pas l'année de construction
268
+ let pi_id = de.enum_periode_isolation_id || pc_id;
269
+ if (!de.enum_periode_isolation_id) {
270
+ const pc = enums.periode_construction[pc_id];
271
+ switch (pc) {
272
+ case 'avant 1948':
273
+ case '1948-1974':
274
+ pi_id = parseInt(getKeyByValue(enums.periode_isolation, '1975-1977'), 10);
275
+ break;
276
+ }
277
+ }
278
+ const tv_umur_avant = de.tv_umur_id;
279
+ tv_umur(di, de, du, pi_id, zc, effetJoule);
280
+ if (de.tv_umur_id !== tv_umur_avant && pi_id !== pc_id) {
281
+ console.warn(
282
+ `BUG(${scriptName}) Si année de construction <74 alors Année d'isolation=75-77 (3CL page 13)`
283
+ );
284
+ if (bug_for_bug_compat) tv_umur(di, de, du, pc_id, zc, effetJoule);
285
+ }
286
+ di.umur = Math.min(di.umur, di.umur0);
287
+ break;
288
+ }
289
+ case 'saisie direct u justifiée (à partir des documents justificatifs autorisés)':
290
+ case 'saisie direct u depuis rset/rsee( etude rt2012/re2020)':
291
+ di.umur = requestInput(de, du, 'umur_saisi', 'float');
292
+ di.umur0 = mur.donnee_intermediaire?.umur0;
293
+ break;
294
+ default:
295
+ console.warn('methode_saisie_u inconnue:', methode_saisie_u);
296
+ }
297
+ mur.donnee_utilisateur = du;
298
+ mur.donnee_intermediaire = di;
299
+ }
300
+
301
+ /**
302
+ * Vérification si le calcul des déperditions des murs utilisent, dans le DPE d'origine, une minoration à 2.5 de Umur0
303
+ * pour l'application du doublage
304
+ * @param type_doublage {number}
305
+ * @param di {Donnee_intermediaire}
306
+ * @param de {Donnee_entree}
307
+ * @param umur0_avant {number}
308
+ * @param umur_avant {number}
309
+ */
310
+ function checkDoublageWithMinUmur0(type_doublage, di, de, umur0_avant, umur_avant) {
311
+ let umur0Doublage;
312
+
313
+ // 3 - doublage indéterminé ou lame d'air inf 15 mm
314
+ if (type_doublage === 3) {
315
+ umur0Doublage = 1 / (1 / Math.min(2.5, di.umur0) + 0.1);
316
+ } else if (type_doublage === 4 || type_doublage === 5) {
317
+ // 4 - doublage indéterminé avec lame d'air sup 15 mm
318
+ // 5 - doublage connu (plâtre brique bois)
319
+ umur0Doublage = 1 / (1 / Math.min(2.5, di.umur0) + 0.21);
320
+ }
321
+
322
+ if (
323
+ umur0_avant.toFixed(3) === umur0Doublage.toFixed(3) ||
324
+ umur_avant.toFixed(3) === umur0Doublage.toFixed(3)
325
+ ) {
326
+ console.error(`
327
+ Le calcul Umur0 pour le mur ${de.description} utilise Umur0 minoré à 2.5 pour l'application du doublage alors qu'il est de ${di.umur0}.
328
+ Nous conservons ${di.umur0} pour la suite des calculs, les murs sont donc plus déperditifs que dans le DPE d'origine.
329
+ `);
330
+ }
331
+ }
@@ -0,0 +1,46 @@
1
+ import calc_mur from './3.2.1_mur.js';
2
+
3
+ describe('Recherche de bugs dans le calcul de déperdition des murs', () => {
4
+ /**
5
+ * @see : https://redfroggy.atlassian.net/browse/KAR-119
6
+ */
7
+ test('calcul de déperdition pour les murs de 2213E0696993Z', () => {
8
+ const zc = 8; // H3
9
+ const pc_id = 2; // Période de construction (1948)
10
+ const ej = 0;
11
+ const mur = {
12
+ donnee_entree: {
13
+ description:
14
+ "Mur 2 Est - Inconnu donnant sur des circulations sans ouverture directe sur l'extérieur",
15
+ reference: '2021_08_24_18_02_58_7233440008111783',
16
+ tv_coef_reduction_deperdition_id: 78,
17
+ surface_aiu: 22,
18
+ surface_aue: 15,
19
+ enum_cfg_isolation_lnc_id: '2',
20
+ enum_type_adjacence_id: '14', // Circulation sans ouverture directe sur l'extérieur
21
+ enum_orientation_id: '3', // Est
22
+ surface_paroi_totale: 10.5,
23
+ surface_paroi_opaque: 10.5,
24
+ tv_umur0_id: 1,
25
+ enum_materiaux_structure_mur_id: '1', // Inconnu
26
+ enum_methode_saisie_u0_id: '2', // déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire
27
+ paroi_ancienne: 0,
28
+ enum_type_doublage_id: '2', // absence de doublage
29
+ enum_type_isolation_id: '1', // inconnu
30
+ enum_periode_isolation_id: '2', // 1948-1974
31
+ tv_umur_id: 6, //
32
+ enum_methode_saisie_u_id: '8' // année de construction saisie (table forfaitaire)
33
+ },
34
+ donnee_intermediaire: {
35
+ b: 0.35,
36
+ umur: 2.5,
37
+ umur0: 2.5
38
+ }
39
+ };
40
+ calc_mur(mur, zc, pc_id, ej);
41
+
42
+ expect(mur.donnee_intermediaire.b).toBe(0.35);
43
+ expect(mur.donnee_intermediaire.umur).toBe(2.5);
44
+ expect(mur.donnee_intermediaire.umur0).toBe(2.5);
45
+ });
46
+ });
@@ -0,0 +1,259 @@
1
+ import enums from './enums.js';
2
+ import b from './3.1_b.js';
3
+ import { tv, requestInput, getKeyByValue, bug_for_bug_compat, getRange } from './utils.js';
4
+
5
+ const scriptName = new URL(import.meta.url).pathname.split('/').pop();
6
+
7
+ function tv_upb0(di, de, du) {
8
+ requestInput(de, du, 'type_plancher_bas');
9
+ const matcher = {
10
+ enum_type_plancher_bas_id: de.enum_type_plancher_bas_id
11
+ };
12
+ const row = tv('upb0', matcher, de);
13
+ if (row) {
14
+ di.upb0 = Number(row.upb0);
15
+ de.tv_upb0_id = Number(row.tv_upb0_id);
16
+ } else {
17
+ console.error('!! pas de valeur forfaitaire trouvée pour upb0 !!');
18
+ }
19
+ }
20
+
21
+ function tv_upb(di, de, du, pc_id, zc, effetJoule) {
22
+ if (bug_for_bug_compat && de.tv_upb_id) {
23
+ /**
24
+ * Vérification de la variable effet_joule
25
+ * Certains DPE utilise de manière erronée cette variable. Pour rester cohérent avec le DPE, utilisation de la variable
26
+ * effet_joule telle qu'elle est utilisée dans le DPE
27
+ */
28
+ const rowUpb = tv('upb', {
29
+ tv_upb_id: de.tv_upb_id
30
+ });
31
+
32
+ if (rowUpb && rowUpb.effet_joule !== effetJoule) {
33
+ console.error(
34
+ `La variable effet_joule utilisée dans le DPE pour le plancher bas '${de.description}' est ${rowUpb.effet_joule}.
35
+ Celle-ci devrait être ${effetJoule}. La valeur ${rowUpb.effet_joule} est conservée dans la suite des calculs`
36
+ );
37
+
38
+ effetJoule = rowUpb.effet_joule;
39
+ }
40
+ }
41
+
42
+ const matcher = {
43
+ enum_periode_construction_id: pc_id,
44
+ enum_zone_climatique_id: zc,
45
+ effet_joule: effetJoule
46
+ };
47
+ const row = tv('upb', matcher, de);
48
+ if (row) {
49
+ di.upb = Number(row.upb);
50
+ de.tv_upb_id = Number(row.tv_upb_id);
51
+ } else {
52
+ console.error('!! pas de valeur forfaitaire trouvée pour upb !!');
53
+ }
54
+ }
55
+
56
+ const values_2s_p = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20];
57
+
58
+ function tv_ue(di, de, du, pc_id, pb_list) {
59
+ const type_adjacence = enums.type_adjacence[de.enum_type_adjacence_id];
60
+ let type_adjacence_plancher;
61
+ let upb1, upb2;
62
+ if (type_adjacence === 'terre-plein') {
63
+ if (Number(pc_id) < 7) {
64
+ type_adjacence_plancher = 'terre plein bâtiment construit avant 2001';
65
+ [upb1, upb2] = getRange(di.upb, [0.46, 0.59, 0.85, 1.5, 3.4]);
66
+ } else {
67
+ type_adjacence_plancher = 'terre plein bâtiment construit à partir de 2001';
68
+ [upb1, upb2] = getRange(di.upb, [0.31, 0.37, 0.46, 0.6, 0.85, 1.5, 3.4]);
69
+ }
70
+ } else {
71
+ type_adjacence_plancher = 'plancher sur vide sanitaire ou sous-sol non chauffé';
72
+ [upb1, upb2] = getRange(di.upb, [0.31, 0.34, 0.37, 0.41, 0.45, 0.83, 1.43, 3.33]);
73
+ }
74
+
75
+ /**
76
+ * La surface Ue est la surface de tous les planchers bas ayant le même type d'adjacence
77
+ * Le périmètre Ue est le périmètre de tous les planchers bas ayant le même type d'adjacence
78
+ */
79
+ const { surfaceUe, perimetreUe } = pb_list.reduce(
80
+ (acc, plancherBas) => {
81
+ if (plancherBas.donnee_entree.enum_type_adjacence_id === de.enum_type_adjacence_id) {
82
+ acc.surfaceUe +=
83
+ plancherBas.donnee_entree.surface_ue || plancherBas.donnee_entree.surface_paroi_opaque;
84
+ acc.perimetreUe += plancherBas.donnee_entree.perimetre_ue || 0;
85
+ }
86
+ return acc;
87
+ },
88
+ { surfaceUe: 0, perimetreUe: 0 }
89
+ );
90
+
91
+ const matcher = {
92
+ type_adjacence_plancher,
93
+ '2s_p': Math.round((2 * surfaceUe) / perimetreUe)
94
+ };
95
+ matcher['2s_p'] = values_2s_p.reduce((prev, curr) => {
96
+ return Math.abs(curr - matcher['2s_p']) < Math.abs(prev - matcher['2s_p']) ? curr : prev;
97
+ });
98
+
99
+ matcher['2s_p'] = `^${matcher['2s_p']}$`;
100
+
101
+ const matcher_1 = { ...matcher, ...{ upb: String(upb1) } };
102
+ const matcher_2 = { ...matcher, ...{ upb: String(upb2) } };
103
+ const row_1 = tv('ue', matcher_1);
104
+ const row_2 = tv('ue', matcher_2);
105
+
106
+ const delta_ue = Number(row_2.ue) - Number(row_1.ue);
107
+ const delta_upb = upb2 - upb1;
108
+
109
+ let ue;
110
+ if (delta_upb === 0) {
111
+ ue = Number(row_1.ue);
112
+ } else {
113
+ // Interpolation linéaire si di.upb n'est pas une valeur connue
114
+ ue = Number(row_1.ue) + (delta_ue * (di.upb - upb1)) / delta_upb;
115
+ }
116
+ de.ue = ue;
117
+ }
118
+
119
+ function calc_upb0(di, de, du) {
120
+ const methode_saisie_u0 = requestInput(de, du, 'methode_saisie_u0');
121
+ switch (methode_saisie_u0) {
122
+ case 'type de paroi inconnu (valeur par défaut)':
123
+ case 'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire':
124
+ tv_upb0(di, de, du);
125
+ break;
126
+ case 'saisie direct u0 justifiée à partir des documents justificatifs autorisés':
127
+ case "saisie direct u0 correspondant à la performance de la paroi avec son isolation antérieure iti (umur_iti) lorsqu'il y a une surisolation ite réalisée":
128
+ di.upb0 = requestInput(de, du, 'upb0_saisi', 'float');
129
+ break;
130
+ case 'u0 non saisi car le u est saisi connu et justifié.':
131
+ break;
132
+ default:
133
+ console.warn('methode_saisie_u0 inconnue:', methode_saisie_u0);
134
+ }
135
+ }
136
+
137
+ export default function calc_pb(pb, zc, pc_id, effetJoule, pb_list) {
138
+ const de = pb.donnee_entree;
139
+ const du = {};
140
+ const di = {};
141
+ di.upb0 = pb.donnee_intermediaire.upb0;
142
+
143
+ b(di, de, du, zc);
144
+
145
+ let methode_saisie_u = parseInt(de.enum_methode_saisie_u_id);
146
+
147
+ if (bug_for_bug_compat) {
148
+ // Si la résistance de l'isolation est connue mais que le mode de saisie de u n'est pas
149
+ // 'resistance isolation saisie justifiée'. On force cette méthode de saisie
150
+ if (de.resistance_isolation && ![5, 6].includes(methode_saisie_u)) {
151
+ console.error(`
152
+ La résistance de l'isolation du plancher bas ${de.description} est connue mais la méthode de saisie du facteur u
153
+ n'est pas 'resistance isolation saisie justifiée'. La méthode de saisie est modifiée pour la suite du calcul
154
+ `);
155
+ methode_saisie_u = 5;
156
+ }
157
+ // Si l'épaisseur de l'isolation est connue mais que le mode de saisie de u n'est pas
158
+ // 'epaisseur isolation saisie justifiée' on force cette méthode de saisie
159
+ if (de.epaisseur_isolation && ![3, 4].includes(methode_saisie_u)) {
160
+ console.error(`
161
+ L'épaisseur de l'isolation du plancher bas ${de.description} est connue mais la méthode de saisie du facteur u
162
+ n'est pas 'epaisseur isolation saisie justifiée'. La méthode de saisie est modifiée pour la suite du calcul
163
+ `);
164
+ methode_saisie_u = 3;
165
+ }
166
+ // Si Upb est saisi mais que le mode de saisie de u n'est pas 'saisie direct u' on force cette méthode de saisie.
167
+ if (de.upb_saisi && ![9, 10].includes(methode_saisie_u)) {
168
+ console.error(`
169
+ Upb du plancher bas ${de.description} est saisi mais la méthode de saisie du facteur u
170
+ n'est pas 'saisie direct u'. La méthode de saisie est modifiée pour la suite du calcul
171
+ `);
172
+ methode_saisie_u = 9;
173
+ }
174
+ }
175
+
176
+ switch (methode_saisie_u) {
177
+ case 1:
178
+ // 1 - non isolé
179
+ calc_upb0(di, de, du);
180
+ di.upb = di.upb0;
181
+ break;
182
+ case 3:
183
+ case 4: {
184
+ // 3 - epaisseur isolation saisie justifiée par mesure ou observation
185
+ // 4 - epaisseur isolation saisie justifiée à partir des documents justificatifs autorisés
186
+ const e = parseFloat(de.epaisseur_isolation) * 0.01;
187
+ calc_upb0(di, de, du);
188
+ di.upb = 1 / (1 / di.upb0 + e / 0.042);
189
+ break;
190
+ }
191
+ case 5:
192
+ case 6: {
193
+ // 5 - resistance isolation saisie justifiée observation de l'isolant installé et mesure de son épaisseur
194
+ // 6 - resistance isolation saisie justifiée à partir des documents justificatifs autorisés
195
+ const r = parseFloat(de.resistance_isolation);
196
+ calc_upb0(di, de, du);
197
+ di.upb = 1 / (1 / di.upb0 + r);
198
+ break;
199
+ }
200
+ case 2:
201
+ case 7: {
202
+ // 2 - isolation inconnue (table forfaitaire)
203
+ // 7 - année d'isolation différente de l'année de construction saisie justifiée (table forfaitaire)
204
+ calc_upb0(di, de, du);
205
+ tv_upb(di, de, du, de.enum_periode_isolation_id || pc_id, zc, effetJoule);
206
+ di.upb = Math.min(di.upb, di.upb0);
207
+ break;
208
+ }
209
+ case 8: {
210
+ // 8 - année de construction saisie (table forfaitaire)
211
+ // Si l'année d'isolation est connue, il faut l'utiliser et pas l'année de construction
212
+ let pi_id = de.enum_periode_isolation_id || pc_id;
213
+ if (!de.enum_periode_isolation_id) {
214
+ const pc = enums.periode_construction[pc_id];
215
+ switch (pc) {
216
+ case 'avant 1948':
217
+ case '1948-1974':
218
+ pi_id = getKeyByValue(enums.periode_isolation, '1975-1977');
219
+ break;
220
+ }
221
+ }
222
+ calc_upb0(di, de, du);
223
+ const tv_upb_avant = de.tv_upb_id;
224
+ tv_upb(di, de, du, pi_id, zc, effetJoule);
225
+ if (de.tv_upb_id !== tv_upb_avant && pi_id !== pc_id) {
226
+ console.warn(
227
+ `BUG(${scriptName}) Si année de construction <74 alors Année d'isolation=75-77 (3CL page 17)`
228
+ );
229
+ if (bug_for_bug_compat) tv_upb(di, de, du, pc_id, zc, effetJoule);
230
+ }
231
+ di.upb = Math.min(di.upb, di.upb0);
232
+ break;
233
+ }
234
+ case 9:
235
+ case 10:
236
+ // 9 - saisie direct u justifiée (à partir des documents justificatifs autorisés)
237
+ // 10 - saisie direct u depuis rset/rsee( etude rt2012/re2020)
238
+ di.upb = requestInput(de, du, 'upb_saisi', 'float');
239
+ break;
240
+ default:
241
+ console.warn('methode_saisie_u inconnue:', methode_saisie_u);
242
+ }
243
+
244
+ const type_adjacence = requestInput(de, du, 'type_adjacence');
245
+ switch (type_adjacence) {
246
+ case 'vide sanitaire':
247
+ case 'sous-sol non chauffé':
248
+ case 'terre-plein':
249
+ tv_ue(di, de, du, pc_id, pb_list);
250
+ di.upb_final = de.ue;
251
+ break;
252
+ default:
253
+ di.upb_final = di.upb;
254
+ break;
255
+ }
256
+
257
+ pb.donnee_utilisateur = du;
258
+ pb.donnee_intermediaire = di;
259
+ }