@open3cl/engine 1.0.5 → 1.0.7

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 (68) 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 +95 -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/tv.store.js +389 -0
  40. package/features/dpe/infrastructure/tv.store.spec.js +690 -0
  41. package/features/engine/domain/constants.js +1 -0
  42. package/features/engine/domain/contexte.builder.js +140 -0
  43. package/features/engine/domain/contexte.builder.spec.js +152 -0
  44. package/features/engine/domain/engine.service.js +139 -0
  45. package/features/engine/domain/engine.service.spec.js +7 -0
  46. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.js +262 -0
  47. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +282 -0
  48. package/features/engine/domain/enveloppe/deperdition.service.js +101 -0
  49. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.js +168 -0
  50. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +345 -0
  51. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.js +169 -0
  52. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +200 -0
  53. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.js +136 -0
  54. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +211 -0
  55. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.js +56 -0
  56. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +116 -0
  57. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.js +338 -0
  58. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +393 -0
  59. package/features/engine/domain/models/contexte.model.ts +10 -0
  60. package/features/engine/domain/models/deperdition.model.ts +8 -0
  61. package/features/normalizer/domain/dpe-normalizer.service.js +24 -0
  62. package/features/normalizer/domain/dpe-normalizer.service.spec.js +3 -0
  63. package/ficheTechnique.spec.js +4 -3
  64. package/output.js +1 -0
  65. package/package.json +9 -8
  66. package/tv-v2.js +79121 -0
  67. package/utils.js +4 -8
  68. package/utils.spec.js +4 -3
@@ -0,0 +1,345 @@
1
+ import corpus from '../../../../../../test/corpus-sano.json';
2
+ import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
3
+ import { DeperditionMurService } from './deperdition-mur.service.js';
4
+ import { ContexteBuilder } from '../../contexte.builder.js';
5
+ import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
6
+ import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
7
+ import { beforeEach, describe, expect, test } from 'vitest';
8
+
9
+ /** @type {DeperditionMurService} **/
10
+ let service;
11
+
12
+ /** @type {DpeNormalizerService} **/
13
+ let normalizerService;
14
+
15
+ /** @type {ContexteBuilder} **/
16
+ let contexteBuilder;
17
+
18
+ describe('Calcul de déperdition des murs', () => {
19
+ beforeEach(() => {
20
+ service = new DeperditionMurService(new TvStore());
21
+ normalizerService = new DpeNormalizerService();
22
+ contexteBuilder = new ContexteBuilder();
23
+ });
24
+
25
+ describe('Determination de uMur et uMur0', () => {
26
+ test.each([
27
+ {
28
+ label: 'cas 2187E1039187C mur 1',
29
+ enumMateriauxStructureMurId: '2',
30
+ enumMethodeSaisieUId: '8',
31
+ enumMethodeSaisieU0Id: '2',
32
+ paroiAncienne: false,
33
+ epaisseurStructure: 60,
34
+ enumTypeIsolationId: '9',
35
+ enumTypeDoublageId: '2',
36
+ enumPeriodeConstructionId: '2',
37
+ zoneClimatiqueId: '3',
38
+ umurExpected: 1,
39
+ umur0Expected: 1.8
40
+ },
41
+ {
42
+ label:
43
+ "(2287E0577966W) Mur 2 Sud, Est, Ouest (p1) - Mur en blocs de béton creux d'épaisseur ≥ 25 cm avec un doublage rapporté donnant sur l'extérieur",
44
+ enumMateriauxStructureMurId: '12',
45
+ enumMethodeSaisieUId: '7',
46
+ enumMethodeSaisieU0Id: '2',
47
+ paroiAncienne: false,
48
+ enumTypeIsolationId: '1',
49
+ enumTypeDoublageId: '4',
50
+ enumPeriodeConstructionId: '1',
51
+ enumIsolationId: '5',
52
+ epaisseurStructure: 25,
53
+ zoneClimatiqueId: '3',
54
+ umurExpected: 0.8, // 0.7 dans le DPE original, mais certainement lié à une erreur
55
+ umur0Expected: 1.55091
56
+ }
57
+ ])(
58
+ '$label',
59
+ ({
60
+ enumMateriauxStructureMurId,
61
+ enumMethodeSaisieUId,
62
+ enumMethodeSaisieU0Id,
63
+ enumTypeDoublageId,
64
+ enumTypeIsolationId,
65
+ umurSaisi,
66
+ epaisseurStructure,
67
+ paroiAncienne,
68
+ resistanceIsolation,
69
+ umurExpected,
70
+ umur0Expected,
71
+ enumPeriodeConstructionId = 1,
72
+ enumIsolationId,
73
+ effetJoule = false,
74
+ zoneClimatiqueId = '1'
75
+ }) => {
76
+ /** @type {Contexte} */
77
+ const ctx = {
78
+ effetJoule,
79
+ enumPeriodeConstructionId,
80
+ zoneClimatiqueId
81
+ };
82
+
83
+ /** @type {MurDE} */
84
+ const de = {
85
+ enum_materiaux_structure_mur_id: enumMateriauxStructureMurId,
86
+ enum_methode_saisie_u_id: enumMethodeSaisieUId,
87
+ enum_methode_saisie_u0_id: enumMethodeSaisieU0Id,
88
+ umur_saisi: umurSaisi,
89
+ resistance_isolation: resistanceIsolation,
90
+ paroi_ancienne: paroiAncienne,
91
+ epaisseur_structure: epaisseurStructure,
92
+ enum_type_doublage_id: enumTypeDoublageId,
93
+ enum_type_isolation_id: enumTypeIsolationId,
94
+ enum_periode_isolation_id: enumIsolationId
95
+ };
96
+
97
+ const di = service.execute(ctx, de);
98
+ expect(di.umur0).toBe(umur0Expected);
99
+ expect(di.umur).toBeCloseTo(umurExpected);
100
+ }
101
+ );
102
+
103
+ test("Mur 1 Sud, Est, Ouest (p1) - Mur en blocs de béton pleins d'épaisseur ≤ 20 cm avec isolation intérieure (10 cm) donnant sur l'extérieur", () => {
104
+ /** @type {Contexte} */
105
+ const ctx = {
106
+ effetJoule: false,
107
+ enumPeriodeConstructionId: '6',
108
+ zoneClimatiqueId: '3'
109
+ };
110
+ /** @type {MurDE} */
111
+ const de = {
112
+ enum_materiaux_structure_mur_id: '11',
113
+ enum_methode_saisie_u_id: '3',
114
+ enum_methode_saisie_u0_id: '2',
115
+ paroi_ancienne: false,
116
+ epaisseur_structure: 20,
117
+ enum_type_doublage_id: '2',
118
+ enum_type_isolation_id: '3',
119
+ epaisseur_isolation: 10
120
+ };
121
+
122
+ const di = service.execute(ctx, de);
123
+ expect(di.umur0).toBe(2.5);
124
+ expect(di.umur).toBeCloseTo(0.3448275862068969);
125
+ });
126
+
127
+ test('Mur 1 Nord - Mur en pierre de taille et moellons avec remplissage tout venant d'épaisseur 70 cm avec isolation intérieure (10 cm) donnant sur l'extérieur', () => {
128
+ /** @type {Contexte} */
129
+ const ctx = {
130
+ effetJoule: false,
131
+ enumPeriodeConstructionId: '6',
132
+ zoneClimatiqueId: '3'
133
+ };
134
+ /** @type {MurDE} */
135
+ const de = {
136
+ enum_materiaux_structure_mur_id: '3',
137
+ enum_methode_saisie_u_id: '3',
138
+ enum_methode_saisie_u0_id: '2',
139
+ paroi_ancienne: true,
140
+ epaisseur_structure: 70,
141
+ enum_type_doublage_id: '2',
142
+ enum_type_isolation_id: '3',
143
+ epaisseur_isolation: 10
144
+ };
145
+
146
+ const di = service.execute(ctx, de);
147
+ expect(di.umur0).toBe(1.45);
148
+ expect(di.umur).toBeCloseTo(0.31351351351351353);
149
+ });
150
+
151
+ test("(2387E0430619S) Mur 3 Nord, Est (p1) - Mur en placoplatre isolé par l'intérieur (environ 10 cm) avec isolation intérieure donnant sur des circulations avec ouverture directe sur l'extérieur", () => {
152
+ /** @type {Contexte} */
153
+ const ctx = {
154
+ effetJoule: false,
155
+ enumPeriodeConstructionId: '6',
156
+ zoneClimatiqueId: '3'
157
+ };
158
+ /** @type {MurDE} */
159
+ const de = {
160
+ enum_materiaux_structure_mur_id: '23',
161
+ enum_methode_saisie_u_id: '9',
162
+ enum_methode_saisie_u0_id: '5',
163
+ paroi_ancienne: false,
164
+ enum_type_doublage_id: '2',
165
+ enum_type_isolation_id: '3',
166
+ umur_saisi: 0.32
167
+ };
168
+
169
+ const di = service.execute(ctx, de);
170
+ expect(di.umur0).toBeUndefined();
171
+ expect(di.umur).toBeCloseTo(0.32);
172
+ });
173
+
174
+ test("Mur 1 Nord, Sud, Ouest (p1) - Mur en pierre de taille et moellons avec remplissage tout venant d'épaisseur 50 cm non isolé donnant sur l'extérieur", () => {
175
+ /** @type {Contexte} */
176
+ const ctx = {
177
+ effetJoule: false,
178
+ enumPeriodeConstructionId: '6',
179
+ zoneClimatiqueId: '3'
180
+ };
181
+ /** @type {MurDE} */
182
+ const de = {
183
+ enum_materiaux_structure_mur_id: '3',
184
+ enum_methode_saisie_u_id: '1',
185
+ enum_methode_saisie_u0_id: '2',
186
+ enduit_isolant_paroi_ancienne: true,
187
+ epaisseur_structure: 50,
188
+ enum_type_doublage_id: '2',
189
+ enum_type_isolation_id: '2'
190
+ };
191
+
192
+ const di = service.execute(ctx, de);
193
+ expect(di.umur0).toBeCloseTo(0.81545);
194
+ expect(di.umur).toBeCloseTo(0.81545);
195
+ });
196
+
197
+ test("Mur 8 Est - Mur en pierre de taille et moellons avec remplissage tout venant d'épaisseur 50 cm avec un doublage rapporté non isolé donnant sur l'extérieur", () => {
198
+ /** @type {Contexte} */
199
+ const ctx = {
200
+ effetJoule: false,
201
+ enumPeriodeConstructionId: '6',
202
+ zoneClimatiqueId: '3'
203
+ };
204
+ /** @type {MurDE} */
205
+ const de = {
206
+ enum_materiaux_structure_mur_id: '3',
207
+ enum_methode_saisie_u_id: '1',
208
+ enum_methode_saisie_u0_id: '2',
209
+ paroi_ancienne: true,
210
+ epaisseur_structure: 50,
211
+ enum_type_doublage_id: '3',
212
+ enum_type_isolation_id: '2'
213
+ };
214
+
215
+ const di = service.execute(ctx, de);
216
+ expect(di.umur0).toBeCloseTo(1.59664);
217
+ expect(di.umur).toBeCloseTo(1.59664);
218
+ });
219
+
220
+ test('(2287E0577966W) umur 0.7 au lieu de 0.8', () => {
221
+ /** @type {Contexte} */
222
+ const ctx = {
223
+ effetJoule: true,
224
+ enumPeriodeConstructionId: '1',
225
+ zoneClimatiqueId: '3'
226
+ };
227
+ /** @type {MurDE} */
228
+ const de = {
229
+ enum_materiaux_structure_mur_id: '12',
230
+ enum_methode_saisie_u_id: '7',
231
+ enum_methode_saisie_u0_id: '2',
232
+ paroi_ancienne: false,
233
+ epaisseur_structure: 25,
234
+ enum_type_doublage_id: '4',
235
+ enum_type_isolation_id: '1',
236
+ enum_periode_isolation_id: '5'
237
+ };
238
+
239
+ const di = service.execute(ctx, de);
240
+ expect(di.umur0).toBeCloseTo(1.55091);
241
+ expect(di.umur).toBeCloseTo(0.7); // 0.7 dans le DPE d'origine, comme si effet joule
242
+ });
243
+
244
+ test("(2387E0430619S) Mur 3 Nord, Est (p1) - Mur en placoplatre isolé par l'intérieur (environ 10 cm) avec isolation intérieure donnant sur des circulations avec ouverture directe sur l'extérieur", () => {
245
+ /** @type {Contexte} */
246
+ const ctx = {
247
+ effetJoule: true,
248
+ enumPeriodeConstructionId: '1',
249
+ zoneClimatiqueId: '3'
250
+ };
251
+ /** @type {MurDE} */
252
+ const de = {
253
+ enum_materiaux_structure_mur_id: '23',
254
+ enum_methode_saisie_u_id: '9',
255
+ enum_methode_saisie_u0_id: '5',
256
+ paroi_ancienne: false,
257
+ umur_saisi: 0.32,
258
+ epaisseur_structure: 25,
259
+ enum_type_doublage_id: '2',
260
+ enum_type_isolation_id: '3'
261
+ };
262
+
263
+ const di = service.execute(ctx, de);
264
+ expect(di.umur0).toBeUndefined(); // umur saisie directement
265
+ expect(di.umur).toBeCloseTo(0.32);
266
+ });
267
+
268
+ test('(2287E1724516Y) Mur 4 Nord, Sud (p1) - Mur en pan de bois sans remplissage tout venant d'épaisseur 18 cm avec isolation intérieure (R=2.5m².K/W) donnant sur l'extérieur', () => {
269
+ /** @type {Contexte} */
270
+ const ctx = {
271
+ effetJoule: false,
272
+ enumPeriodeConstructionId: '1',
273
+ zoneClimatiqueId: '3'
274
+ };
275
+ /** @type {MurDE} */
276
+ const de = {
277
+ enum_materiaux_structure_mur_id: '5',
278
+ enum_methode_saisie_u_id: '6',
279
+ enum_methode_saisie_u0_id: '2',
280
+ enduit_isolant_paroi_ancienne: true,
281
+ epaisseur_structure: 18,
282
+ resistance_isolation: 2.5,
283
+ enum_type_doublage_id: '2',
284
+ enum_type_isolation_id: '3'
285
+ };
286
+
287
+ const di = service.execute(ctx, de);
288
+ expect(di.umur0).toBeCloseTo(0.82983999999999991);
289
+ expect(di.umur).toBeCloseTo(0.26990177584075975);
290
+ });
291
+
292
+ test('(2187E0982013C) Mur Nord, Sud, Est, Ouest - Mur en pierre de taille et moellons avec remplissage tout venant d'épaisseur 50 cm non isolé donnant sur l'extérieur', () => {
293
+ /** @type {Contexte} */
294
+ const ctx = {
295
+ effetJoule: false,
296
+ enumPeriodeConstructionId: '1',
297
+ zoneClimatiqueId: '3'
298
+ };
299
+ /** @type {MurDE} */
300
+ const de = {
301
+ enum_type_adjacence_id: '1',
302
+ enum_materiaux_structure_mur_id: '3',
303
+ enum_methode_saisie_u_id: '1',
304
+ enum_methode_saisie_u0_id: '2',
305
+ enduit_isolant_paroi_ancienne: true,
306
+ epaisseur_structure: 18,
307
+ resistance_isolation: 2.5,
308
+ enum_type_doublage_id: '2',
309
+ enum_type_isolation_id: '2'
310
+ };
311
+
312
+ const di = service.execute(ctx, de);
313
+ expect(di.umur0).toBeCloseTo(0.81545);
314
+ expect(di.umur).toBeCloseTo(0.81545);
315
+ });
316
+ });
317
+
318
+ describe("Test d'intégration de mur", () => {
319
+ test.each(corpus)('vérification des DI des murs pour dpe %s', (ademeId) => {
320
+ let dpeRequest = getAdemeFileJson(ademeId);
321
+ dpeRequest = normalizerService.normalize(dpeRequest);
322
+
323
+ /** @type {Contexte} */
324
+ const ctx = contexteBuilder.fromDpe(dpeRequest);
325
+
326
+ const murs = dpeRequest.logement.enveloppe.mur_collection?.mur || [];
327
+
328
+ murs.forEach((m) => {
329
+ // dans ces cas de figure, les données paroi_ancienne sont bien traitées
330
+ if (['2187E0982013C', '2287E2336469P', '2387E0045247S'].includes(ademeId)) {
331
+ m.donnee_entree.enduit_isolant_paroi_ancienne = m.donnee_entree.paroi_ancienne;
332
+ }
333
+ const di = service.execute(ctx, m.donnee_entree);
334
+
335
+ if (m.donnee_intermediaire.umur0) {
336
+ expect(di.umur0).toBeCloseTo(m.donnee_intermediaire.umur0, 2);
337
+ } else {
338
+ expect(di.umur0).toBeUndefined();
339
+ }
340
+ expect(di.umur).toBeCloseTo(m.donnee_intermediaire.umur, 2);
341
+ expect(di.b).toBeCloseTo(m.donnee_intermediaire.b, 2);
342
+ });
343
+ });
344
+ });
345
+ });
@@ -0,0 +1,169 @@
1
+ import { inject } from 'dioma';
2
+ import { PRECISION } from '../../constants.js';
3
+ import { DeperditionService } from '../deperdition.service.js';
4
+ import { TvStore } from '../../../../dpe/infrastructure/tv.store.js';
5
+
6
+ /**
7
+ * Calcul des déperditions de l’enveloppe GV
8
+ * Chapitre 3.2.2 Calcul des planchers bas
9
+ *
10
+ * Méthode de calcul 3CL-DPE 2021
11
+ * Octobre 2021
12
+ * @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
13
+ */
14
+ export class DeperditionPlancherBasService extends DeperditionService {
15
+ /**
16
+ * @param tvStore {TvStore}
17
+ */
18
+ constructor(tvStore = inject(TvStore)) {
19
+ super(tvStore);
20
+ }
21
+
22
+ /**
23
+ * @param ctx {Contexte}
24
+ * @param pbDE {PlancherBasDE}
25
+ * @param plancherBas {PlancherBas[]}
26
+ * @return {PlancherBasDI}
27
+ */
28
+ execute(ctx, pbDE, plancherBas) {
29
+ const upb0 = this.#upb0(pbDE);
30
+ const upb = this.#upb(pbDE, upb0, ctx);
31
+ const upb_final = this.#upbFinal(pbDE, upb, ctx, plancherBas);
32
+ const b = this.b({
33
+ enumTypeAdjacenceId: pbDE.enum_type_adjacence_id,
34
+ surfaceAiu: pbDE.surface_aiu,
35
+ surfaceAue: pbDE.surface_aue,
36
+ enumCfgIsolationLncId: pbDE.enum_cfg_isolation_lnc_id,
37
+ zoneClimatiqueId: ctx.zoneClimatiqueId
38
+ });
39
+
40
+ /** @type {PlancherBasDI} */
41
+ return { upb0, upb, upb_final, b };
42
+ }
43
+
44
+ /**
45
+ * @param pbDE {PlancherBasDE}
46
+ * @param upb0 {number}
47
+ * @param ctx {Contexte}
48
+ * @return {number|undefined}
49
+ */
50
+ #upb(pbDE, upb0, ctx) {
51
+ // On determine upb_nu (soit upb0 soit 2 comme valeur minimale forfaitaire)
52
+ const upbNu = Math.min(upb0, 2);
53
+
54
+ const enumPeriodeIsolationId = this.getEnumPeriodeIsolationId(
55
+ pbDE.enum_periode_isolation_id,
56
+ ctx
57
+ );
58
+
59
+ // Selon l'isolation, on applique un calcul au upb nu pour simuler son isolation
60
+ let upb;
61
+ switch (pbDE.enum_methode_saisie_u_id) {
62
+ case '1': // non isolé
63
+ upb = upbNu;
64
+ break;
65
+ case '2': // isolation inconnue (table forfaitaire)
66
+ case '7': // année d'isolation différente de l'année de construction
67
+ case '8': // année de construction saisie
68
+ upb = Math.min(
69
+ upbNu,
70
+ this.tvStore.getUpb(enumPeriodeIsolationId, ctx.zoneClimatiqueId, ctx.effetJoule)
71
+ );
72
+ break;
73
+ case '3': // epaisseur isolation saisie justifiée par mesure ou observation
74
+ case '4': // epaisseur isolation saisie (en cm)
75
+ upb = 1 / (1 / upbNu + (pbDE.epaisseur_isolation * 0.01) / 0.042);
76
+ break;
77
+ case '5':
78
+ case '6': // resistance isolation saisie
79
+ upb = 1 / (1 / upbNu + pbDE.resistance_isolation);
80
+ break;
81
+ default: // saisie direct de la valeur de u
82
+ upb = pbDE.upb_saisi;
83
+ break;
84
+ }
85
+
86
+ return Math.round(parseFloat(upb) * PRECISION) / PRECISION;
87
+ }
88
+
89
+ /**
90
+ * @param pbDE {PlancherBasDE}
91
+ * @return {number|undefined}
92
+ */
93
+ #upb0(pbDE) {
94
+ let upb0;
95
+ switch (pbDE.enum_methode_saisie_u0_id) {
96
+ case '1': // 'type de paroi inconnu (valeur par défaut)'
97
+ case '2': // 'déterminé selon le matériau et épaisseur à partir de la table de valeur forfaitaire'
98
+ upb0 = this.tvStore.getUpb0(pbDE.enum_type_plancher_bas_id);
99
+ break;
100
+ case '5': // 'u0 non saisi car le u est saisi connu et justifié.'
101
+ return;
102
+ default: // Valeur saisie
103
+ return pbDE.upb0_saisi;
104
+ }
105
+
106
+ return upb0;
107
+ }
108
+
109
+ /**
110
+ * Pour les vides sanitaires, les sous-sol non chauffés et terre-plein, le calcul des déperditions se fait avec un coefficient
111
+ * Ue en remplacement de Upb.
112
+ *
113
+ * @param pbDE {PlancherBasDE}
114
+ * @param upb {number}
115
+ * @param ctx {Contexte}
116
+ * @param plancherBas {PlancherBas[]}
117
+ * @return {number|undefined}
118
+ */
119
+ #upbFinal(pbDE, upb, ctx, plancherBas) {
120
+ if (pbDE.calcul_ue === 1) {
121
+ return pbDE.ue;
122
+ }
123
+
124
+ /**
125
+ * 3 - vide sanitaire
126
+ * 5 - terre-plein
127
+ * 6 - sous-sol non chauffé
128
+ */
129
+ if (!['3', '5', '6'].includes(pbDE.enum_type_adjacence_id)) {
130
+ return upb;
131
+ }
132
+
133
+ /**
134
+ * La surface Ue est la surface de tous les planchers bas ayant le même type d'adjacence
135
+ * Le périmètre Ue est le périmètre de tous les planchers bas ayant le même type d'adjacence
136
+ */
137
+ const { surfaceUe, perimetreUe } = plancherBas
138
+ .filter(
139
+ (plancherBas) =>
140
+ pbDE.enum_type_adjacence_id === plancherBas.donnee_entree.enum_type_adjacence_id
141
+ )
142
+ .reduce(
143
+ (acc, plancherBas) => {
144
+ acc.surfaceUe +=
145
+ plancherBas.donnee_entree.surface_ue || plancherBas.donnee_entree.surface_paroi_opaque;
146
+ acc.perimetreUe += plancherBas.donnee_entree.perimetre_ue || 0;
147
+ return acc;
148
+ },
149
+ { surfaceUe: 0, perimetreUe: 0 }
150
+ );
151
+
152
+ const available2sp = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20];
153
+ let dsp = perimetreUe ? Math.round((2 * surfaceUe) / perimetreUe) : 1;
154
+
155
+ // Recherche de la valeur la plus proche dans les valeurs disponibles
156
+ dsp = available2sp.reduce((prev, curr) => {
157
+ return Math.abs(curr - dsp) < Math.abs(prev - dsp) ? curr : prev;
158
+ });
159
+
160
+ const upbFinal = this.tvStore.getUeByUpd(
161
+ pbDE.enum_type_adjacence_id,
162
+ ctx.enumPeriodeConstructionId,
163
+ dsp,
164
+ upb
165
+ );
166
+
167
+ return Math.round(parseFloat(upbFinal) * PRECISION) / PRECISION;
168
+ }
169
+ }