@open3cl/engine 1.0.9 → 1.0.10

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 (28) hide show
  1. package/features/dpe/domain/models/type-habitation.model.js +8 -0
  2. package/features/dpe/infrastructure/baieVitreeTv.store.js +70 -0
  3. package/features/dpe/infrastructure/baieVitreeTv.store.spec.js +106 -0
  4. package/features/engine/domain/contexte.builder.js +59 -63
  5. package/features/engine/domain/contexte.builder.spec.js +41 -62
  6. package/features/engine/domain/engine.service.js +18 -1
  7. package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.js +1 -1
  8. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.js +19 -0
  9. package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +3 -3
  10. package/features/engine/domain/enveloppe/deperdition.service.js +2 -3
  11. package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.js +44 -0
  12. package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +81 -0
  13. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.js +3 -3
  14. package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +30 -10
  15. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.js +2 -2
  16. package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +18 -6
  17. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.js +2 -2
  18. package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +21 -7
  19. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.js +1 -1
  20. package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +2 -2
  21. package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.js +1 -1
  22. package/features/engine/domain/logement/nadeq.service.js +62 -0
  23. package/features/engine/domain/logement/nadeq.service.spec.js +71 -0
  24. package/features/engine/domain/logement/surface-sud-equivalente.service.js +169 -0
  25. package/features/engine/domain/logement/surface-sud-equivalente.service.spec.js +210 -0
  26. package/features/engine/domain/models/contexte.model.ts +10 -5
  27. package/features/engine/domain/models/deperdition.model.ts +1 -1
  28. package/package.json +1 -1
@@ -99,7 +99,7 @@ describe('Calcul des déperditions', () => {
99
99
  enumTypeAdjacenceId: '10',
100
100
  surfaceAiu: 8.14,
101
101
  surfaceAue: 22.8,
102
- zoneClimatiqueId: '5',
102
+ zoneClimatique: 'h2c',
103
103
  enumCfgIsolationLncId: '9',
104
104
  label: 'espace tampon solarisé (véranda,loggia fermée)',
105
105
  bExpected: 0.85
@@ -197,7 +197,7 @@ describe('Calcul des déperditions', () => {
197
197
  enumTypeAdjacenceId,
198
198
  surfaceAiu = undefined,
199
199
  surfaceAue = undefined,
200
- zoneClimatiqueId = undefined,
200
+ zoneClimatique = undefined,
201
201
  enumCfgIsolationLncId = undefined,
202
202
  bExpected
203
203
  }) => {
@@ -206,7 +206,7 @@ describe('Calcul des déperditions', () => {
206
206
  surfaceAiu,
207
207
  surfaceAue,
208
208
  enumCfgIsolationLncId,
209
- zoneClimatiqueId
209
+ zoneClimatique
210
210
  };
211
211
 
212
212
  const b = deperditionMurService.b(data);
@@ -1,4 +1,3 @@
1
- import enums from '../../../../enums.js';
2
1
  import { logger } from '../../../../core/util/logger/log-service.js';
3
2
  import { inject } from 'dioma';
4
3
  import { TvStore } from '../../../dpe/infrastructure/tv.store.js';
@@ -71,13 +70,13 @@ export class DeperditionService {
71
70
  * Prise en compte de la zone climatique
72
71
  */
73
72
  if (['10'].includes(enumTypeAdjacenceId)) {
74
- if (!d.zoneClimatiqueId) {
73
+ if (!d.zoneClimatique) {
75
74
  logger.warn(
76
75
  `impossible de calculer b pour TypeAdjacenceId:${enumTypeAdjacenceId} sans zone climatique`
77
76
  );
78
77
  return;
79
78
  }
80
- zc = enums.zone_climatique[parseInt(d.zoneClimatiqueId)];
79
+ zc = d.zoneClimatique;
81
80
  }
82
81
 
83
82
  return this.tvStore.getB(enumTypeAdjacenceId, uVue, d.enumCfgIsolationLncId, rAiuAue, zc);
@@ -0,0 +1,44 @@
1
+ import { DeperditionService } from '../deperdition.service.js';
2
+ import { inject } from 'dioma';
3
+ import { BaieVitreeTvStore } from '../../../../dpe/infrastructure/baieVitreeTv.store.js';
4
+
5
+ /**
6
+ * Calcul des déperditions des baies vitrées
7
+ * Chapitre 3.3 Calcul des parois vitrées
8
+ *
9
+ * Méthode de calcul 3CL-DPE 2021
10
+ * Octobre 2021
11
+ * @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
12
+ */
13
+ export class EspaceTamponService extends DeperditionService {
14
+ /**
15
+ * @param tvStore {BaieVitreeTvStore}
16
+ */
17
+ constructor(tvStore = inject(BaieVitreeTvStore)) {
18
+ super(tvStore);
19
+ }
20
+
21
+ /**
22
+ * @param ctx {Contexte}
23
+ * @param ets {Ets}
24
+ * @return {EtsDI}
25
+ */
26
+ execute(ctx, ets) {
27
+ const bvDE = ets.donnee_entree;
28
+
29
+ const bver = this.tvStore.getBver(
30
+ ctx.zoneClimatique.value,
31
+ parseInt(bvDE.enum_cfg_isolation_lnc_id)
32
+ );
33
+
34
+ const coef_transparence_ets = this.tvStore.getCoefTransparenceEts(
35
+ parseInt(bvDE.tv_coef_transparence_ets_id)
36
+ );
37
+
38
+ /** @type {EtsDI} */
39
+ return {
40
+ bver,
41
+ coef_transparence_ets
42
+ };
43
+ }
44
+ }
@@ -0,0 +1,81 @@
1
+ import { ContexteBuilder } from '../../contexte.builder.js';
2
+ import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
3
+ import { beforeEach, describe, expect, test, vi } from 'vitest';
4
+ import { BaieVitreeTvStore } from '../../../../dpe/infrastructure/baieVitreeTv.store.js';
5
+ import { EspaceTamponService } from './espace-tampon.service.js';
6
+ import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
7
+
8
+ /** @type {EspaceTamponService} **/
9
+ let service;
10
+
11
+ /** @type {DpeNormalizerService} **/
12
+ let normalizerService;
13
+
14
+ /** @type {BaieVitreeTvStore} **/
15
+ let tvStore;
16
+
17
+ /** @type {ContexteBuilder} **/
18
+ let contexteBuilder;
19
+
20
+ describe('Calcul de déperdition des baies vitrées', () => {
21
+ beforeEach(() => {
22
+ tvStore = new BaieVitreeTvStore();
23
+ service = new EspaceTamponService(tvStore);
24
+ normalizerService = new DpeNormalizerService();
25
+ contexteBuilder = new ContexteBuilder();
26
+ });
27
+
28
+ describe('Determination des données intermédiaires', () => {
29
+ test('Doit retourner bver et directement', () => {
30
+ vi.spyOn(tvStore, 'getBver').mockReturnValue(0.55);
31
+ vi.spyOn(tvStore, 'getCoefTransparenceEts').mockReturnValue(0.62);
32
+
33
+ /**
34
+ * @type {Contexte}
35
+ */
36
+ const ctx = {
37
+ zoneClimatique: { value: 'h1a' }
38
+ };
39
+
40
+ /**
41
+ * @type {Ets}
42
+ */
43
+ const ets = {
44
+ donnee_entree: {
45
+ enum_cfg_isolation_lnc_id: '10',
46
+ tv_coef_transparence_ets_id: '2'
47
+ }
48
+ };
49
+
50
+ let etsDI = service.execute(ctx, ets);
51
+ expect(tvStore.getBver).toHaveBeenCalledWith('h1a', 10);
52
+ expect(tvStore.getCoefTransparenceEts).toHaveBeenCalledWith(2);
53
+ expect(etsDI).toStrictEqual({ bver: 0.55, coef_transparence_ets: 0.62 });
54
+ });
55
+ });
56
+
57
+ describe("Test d'intégration des ets", () => {
58
+ test.each(['2273E0303205P', '2364E0984413P', '2283E0131604Y'])(
59
+ 'vérification des DI des ets pour dpe %s',
60
+ (ademeId) => {
61
+ let dpeRequest = getAdemeFileJson(ademeId);
62
+ dpeRequest = normalizerService.normalize(dpeRequest);
63
+
64
+ /** @type {Contexte} */
65
+ const ctx = contexteBuilder.fromDpe(dpeRequest);
66
+
67
+ let ets = dpeRequest.logement.enveloppe.ets_collection?.ets || {};
68
+ if (ets && Array.isArray(ets)) {
69
+ ets = ets[0];
70
+ }
71
+
72
+ const di = service.execute(ctx, ets);
73
+ expect(di.bver).toBeCloseTo(ets.donnee_intermediaire.bver, 2);
74
+ expect(di.coef_transparence_ets).toBeCloseTo(
75
+ ets.donnee_intermediaire.coef_transparence_ets,
76
+ 2
77
+ );
78
+ }
79
+ );
80
+ });
81
+ });
@@ -34,7 +34,7 @@ export class DeperditionMurService extends DeperditionService {
34
34
  surfaceAue: murDE.surface_aue,
35
35
  enumCfgIsolationLncId: murDE.enum_cfg_isolation_lnc_id,
36
36
  tvCoefReductionDeperditionId: murDE.tv_coef_reduction_deperdition_id,
37
- zoneClimatiqueId: ctx.zoneClimatiqueId
37
+ zoneClimatique: ctx.zoneClimatique.value
38
38
  });
39
39
 
40
40
  /** @type {MurDI} */
@@ -65,14 +65,14 @@ export class DeperditionMurService extends DeperditionService {
65
65
  case '2': // isolation inconnue (table forfaitaire)
66
66
  umur = Math.min(
67
67
  umurNu,
68
- this.tvStore.getUmur(ctx.enumPeriodeConstructionId, ctx.zoneClimatiqueId, ctx.effetJoule)
68
+ this.tvStore.getUmur(ctx.enumPeriodeConstructionId, ctx.zoneClimatique.id, ctx.effetJoule)
69
69
  );
70
70
  break;
71
71
  case '7': // année d'isolation différente de l'année de construction
72
72
  case '8': // année de construction saisie
73
73
  umur = Math.min(
74
74
  umurNu,
75
- this.tvStore.getUmur(enumPeriodeIsolationId, ctx.zoneClimatiqueId, ctx.effetJoule)
75
+ this.tvStore.getUmur(enumPeriodeIsolationId, ctx.zoneClimatique.id, ctx.effetJoule)
76
76
  );
77
77
  break;
78
78
  case '3': // epaisseur isolation saisie justifiée par mesure ou observation
@@ -77,7 +77,9 @@ describe('Calcul de déperdition des murs', () => {
77
77
  const ctx = {
78
78
  effetJoule,
79
79
  enumPeriodeConstructionId,
80
- zoneClimatiqueId
80
+ zoneClimatique: {
81
+ id: zoneClimatiqueId
82
+ }
81
83
  };
82
84
 
83
85
  /** @type {MurDE} */
@@ -105,7 +107,9 @@ describe('Calcul de déperdition des murs', () => {
105
107
  const ctx = {
106
108
  effetJoule: false,
107
109
  enumPeriodeConstructionId: '6',
108
- zoneClimatiqueId: '3'
110
+ zoneClimatique: {
111
+ id: '3'
112
+ }
109
113
  };
110
114
  /** @type {MurDE} */
111
115
  const de = {
@@ -129,7 +133,9 @@ describe('Calcul de déperdition des murs', () => {
129
133
  const ctx = {
130
134
  effetJoule: false,
131
135
  enumPeriodeConstructionId: '6',
132
- zoneClimatiqueId: '3'
136
+ zoneClimatique: {
137
+ id: '3'
138
+ }
133
139
  };
134
140
  /** @type {MurDE} */
135
141
  const de = {
@@ -153,7 +159,9 @@ describe('Calcul de déperdition des murs', () => {
153
159
  const ctx = {
154
160
  effetJoule: false,
155
161
  enumPeriodeConstructionId: '6',
156
- zoneClimatiqueId: '3'
162
+ zoneClimatique: {
163
+ id: '3'
164
+ }
157
165
  };
158
166
  /** @type {MurDE} */
159
167
  const de = {
@@ -176,7 +184,9 @@ describe('Calcul de déperdition des murs', () => {
176
184
  const ctx = {
177
185
  effetJoule: false,
178
186
  enumPeriodeConstructionId: '6',
179
- zoneClimatiqueId: '3'
187
+ zoneClimatique: {
188
+ id: '3'
189
+ }
180
190
  };
181
191
  /** @type {MurDE} */
182
192
  const de = {
@@ -199,7 +209,9 @@ describe('Calcul de déperdition des murs', () => {
199
209
  const ctx = {
200
210
  effetJoule: false,
201
211
  enumPeriodeConstructionId: '6',
202
- zoneClimatiqueId: '3'
212
+ zoneClimatique: {
213
+ id: '3'
214
+ }
203
215
  };
204
216
  /** @type {MurDE} */
205
217
  const de = {
@@ -222,7 +234,9 @@ describe('Calcul de déperdition des murs', () => {
222
234
  const ctx = {
223
235
  effetJoule: true,
224
236
  enumPeriodeConstructionId: '1',
225
- zoneClimatiqueId: '3'
237
+ zoneClimatique: {
238
+ id: '3'
239
+ }
226
240
  };
227
241
  /** @type {MurDE} */
228
242
  const de = {
@@ -246,7 +260,9 @@ describe('Calcul de déperdition des murs', () => {
246
260
  const ctx = {
247
261
  effetJoule: true,
248
262
  enumPeriodeConstructionId: '1',
249
- zoneClimatiqueId: '3'
263
+ zoneClimatique: {
264
+ id: 3
265
+ }
250
266
  };
251
267
  /** @type {MurDE} */
252
268
  const de = {
@@ -270,7 +286,9 @@ describe('Calcul de déperdition des murs', () => {
270
286
  const ctx = {
271
287
  effetJoule: false,
272
288
  enumPeriodeConstructionId: '1',
273
- zoneClimatiqueId: '3'
289
+ zoneClimatique: {
290
+ id: 3
291
+ }
274
292
  };
275
293
  /** @type {MurDE} */
276
294
  const de = {
@@ -294,7 +312,9 @@ describe('Calcul de déperdition des murs', () => {
294
312
  const ctx = {
295
313
  effetJoule: false,
296
314
  enumPeriodeConstructionId: '1',
297
- zoneClimatiqueId: '3'
315
+ zoneClimatique: {
316
+ id: 3
317
+ }
298
318
  };
299
319
  /** @type {MurDE} */
300
320
  const de = {
@@ -34,7 +34,7 @@ export class DeperditionPlancherBasService extends DeperditionService {
34
34
  surfaceAiu: pbDE.surface_aiu,
35
35
  surfaceAue: pbDE.surface_aue,
36
36
  enumCfgIsolationLncId: pbDE.enum_cfg_isolation_lnc_id,
37
- zoneClimatiqueId: ctx.zoneClimatiqueId
37
+ zoneClimatique: ctx.zoneClimatique.value
38
38
  });
39
39
 
40
40
  /** @type {PlancherBasDI} */
@@ -67,7 +67,7 @@ export class DeperditionPlancherBasService extends DeperditionService {
67
67
  case '8': // année de construction saisie
68
68
  upb = Math.min(
69
69
  upbNu,
70
- this.tvStore.getUpb(enumPeriodeIsolationId, ctx.zoneClimatiqueId, ctx.effetJoule)
70
+ this.tvStore.getUpb(enumPeriodeIsolationId, ctx.zoneClimatique.id, ctx.effetJoule)
71
71
  );
72
72
  break;
73
73
  case '3': // epaisseur isolation saisie justifiée par mesure ou observation
@@ -28,7 +28,9 @@ describe('Calcul de déperdition des planchers bas', () => {
28
28
  const ctx = {
29
29
  effetJoule: false,
30
30
  enumPeriodeConstructionId: '6',
31
- zoneClimatiqueId: '3'
31
+ zoneClimatique: {
32
+ id: '3'
33
+ }
32
34
  };
33
35
  /** @type {PlancherBasDE} */
34
36
  const de = {
@@ -53,7 +55,9 @@ describe('Calcul de déperdition des planchers bas', () => {
53
55
  const ctx = {
54
56
  effetJoule: false,
55
57
  enumPeriodeConstructionId: '6',
56
- zoneClimatiqueId: '3'
58
+ zoneClimatique: {
59
+ id: '3'
60
+ }
57
61
  };
58
62
  /** @type {PlancherBasDE} */
59
63
  const de = {
@@ -80,7 +84,9 @@ describe('Calcul de déperdition des planchers bas', () => {
80
84
  const ctx = {
81
85
  effetJoule: false,
82
86
  enumPeriodeConstructionId: '6',
83
- zoneClimatiqueId: '3'
87
+ zoneClimatique: {
88
+ id: '3'
89
+ }
84
90
  };
85
91
  /** @type {PlancherBasDE} */
86
92
  const de = {
@@ -103,7 +109,9 @@ describe('Calcul de déperdition des planchers bas', () => {
103
109
  const ctx = {
104
110
  effetJoule: false,
105
111
  enumPeriodeConstructionId: '6',
106
- zoneClimatiqueId: '3'
112
+ zoneClimatique: {
113
+ id: '3'
114
+ }
107
115
  };
108
116
  /** @type {PlancherBasDE} */
109
117
  const de = {
@@ -127,7 +135,9 @@ describe('Calcul de déperdition des planchers bas', () => {
127
135
  const ctx = {
128
136
  effetJoule: false,
129
137
  enumPeriodeConstructionId: '6',
130
- zoneClimatiqueId: '3'
138
+ zoneClimatique: {
139
+ id: '3'
140
+ }
131
141
  };
132
142
  /** @type {PlancherBasDE} */
133
143
  const de = {
@@ -150,7 +160,9 @@ describe('Calcul de déperdition des planchers bas', () => {
150
160
  const ctx = {
151
161
  effetJoule: false,
152
162
  enumPeriodeConstructionId: '6',
153
- zoneClimatiqueId: '3'
163
+ zoneClimatique: {
164
+ id: '3'
165
+ }
154
166
  };
155
167
  /** @type {PlancherBasDE} */
156
168
  const de = {
@@ -32,7 +32,7 @@ export class DeperditionPlancherHautService extends DeperditionService {
32
32
  surfaceAiu: phDE.surface_aiu,
33
33
  surfaceAue: phDE.surface_aue,
34
34
  enumCfgIsolationLncId: phDE.enum_cfg_isolation_lnc_id,
35
- zoneClimatiqueId: ctx.zoneClimatiqueId
35
+ zoneClimatique: ctx.zoneClimatique.value
36
36
  });
37
37
 
38
38
  /** @type {PlancherHautDI} */
@@ -69,7 +69,7 @@ export class DeperditionPlancherHautService extends DeperditionService {
69
69
  this.tvStore.getUph(
70
70
  enumPeriodeIsolationId,
71
71
  this.#getTypeAdjacence(phDE),
72
- ctx.zoneClimatiqueId,
72
+ ctx.zoneClimatique.id,
73
73
  ctx.effetJoule
74
74
  )
75
75
  );
@@ -28,7 +28,9 @@ describe('Calcul de déperdition des planchers haut', () => {
28
28
  const ctx = {
29
29
  effetJoule: false,
30
30
  enumPeriodeConstructionId: '2',
31
- zoneClimatiqueId: '3'
31
+ zoneClimatique: {
32
+ id: '3'
33
+ }
32
34
  };
33
35
  /** @type {PlancherHautDE} */
34
36
  const de = {
@@ -50,7 +52,9 @@ describe('Calcul de déperdition des planchers haut', () => {
50
52
  const ctx = {
51
53
  effetJoule: false,
52
54
  enumPeriodeConstructionId: '1',
53
- zoneClimatiqueId: '3'
55
+ zoneClimatique: {
56
+ id: '3'
57
+ }
54
58
  };
55
59
  /** @type {PlancherHautDE} */
56
60
  const de = {
@@ -73,7 +77,9 @@ describe('Calcul de déperdition des planchers haut', () => {
73
77
  const ctx = {
74
78
  effetJoule: true,
75
79
  enumPeriodeConstructionId: '1',
76
- zoneClimatiqueId: '3'
80
+ zoneClimatique: {
81
+ id: '3'
82
+ }
77
83
  };
78
84
  /** @type {PlancherHautDE} */
79
85
  const de = {
@@ -96,7 +102,9 @@ describe('Calcul de déperdition des planchers haut', () => {
96
102
  const ctx = {
97
103
  effetJoule: true,
98
104
  enumPeriodeConstructionId: '1',
99
- zoneClimatiqueId: '3'
105
+ zoneClimatique: {
106
+ id: '3'
107
+ }
100
108
  };
101
109
  /** @type {PlancherHautDE} */
102
110
  const de = {
@@ -119,7 +127,9 @@ describe('Calcul de déperdition des planchers haut', () => {
119
127
  const ctx = {
120
128
  effetJoule: false,
121
129
  enumPeriodeConstructionId: '6',
122
- zoneClimatiqueId: '3'
130
+ zoneClimatique: {
131
+ id: '3'
132
+ }
123
133
  };
124
134
  /** @type {PlancherHautDE} */
125
135
  const de = {
@@ -141,7 +151,9 @@ describe('Calcul de déperdition des planchers haut', () => {
141
151
  const ctx = {
142
152
  effetJoule: false,
143
153
  enumPeriodeConstructionId: '6',
144
- zoneClimatiqueId: '3'
154
+ zoneClimatique: {
155
+ id: '3'
156
+ }
145
157
  };
146
158
  /** @type {PlancherHautDE} */
147
159
  const de = {
@@ -164,7 +176,9 @@ describe('Calcul de déperdition des planchers haut', () => {
164
176
  const ctx = {
165
177
  effetJoule: false,
166
178
  enumPeriodeConstructionId: '6',
167
- zoneClimatiqueId: '3'
179
+ zoneClimatique: {
180
+ id: '3'
181
+ }
168
182
  };
169
183
  /** @type {PlancherHautDE} */
170
184
  const de = {
@@ -48,7 +48,7 @@ export class DeperditionPorteService extends DeperditionService {
48
48
  surfaceAiu: porteDE.surface_aiu,
49
49
  surfaceAue: porteDE.surface_aue,
50
50
  enumCfgIsolationLncId: porteDE.enum_cfg_isolation_lnc_id,
51
- zoneClimatiqueId: ctx.zoneClimatiqueId
51
+ zoneClimatique: ctx.zoneClimatique.value
52
52
  });
53
53
 
54
54
  return di;
@@ -14,7 +14,7 @@ describe('Calcul de déperdition des portes', () => {
14
14
 
15
15
  describe('Determination de uPorte', () => {
16
16
  /** @type {Contexte} */
17
- const ctx = { zoneClimatiqueId: '1' };
17
+ const ctx = { zoneClimatique: { id: 1 } };
18
18
 
19
19
  test.each([
20
20
  { type: '1', label: 'porte simple en bois porte opaque pleine', uPorteExpected: 3.5 },
@@ -103,7 +103,7 @@ describe('Calcul de déperdition des portes', () => {
103
103
  test.each(corpus)('vérification des DI des portes pour dpe %s', (ademeId) => {
104
104
  const dpeRequest = getAdemeFileJson(ademeId);
105
105
  /** @type {Contexte} */
106
- const ctx = { zoneClimatiqueId: dpeRequest.logement.meteo.enum_zone_climatique_id };
106
+ const ctx = { zoneClimatique: { id: dpeRequest.logement.meteo.enum_zone_climatique_id } };
107
107
  const portes = dpeRequest.logement.enveloppe.porte_collection?.porte || [];
108
108
 
109
109
  portes.forEach((p) => {
@@ -110,7 +110,7 @@ export class DeperditionVentilationService extends DeperditionService {
110
110
 
111
111
  /**
112
112
  * Déperdition thermique par renouvellement d’air due au système de ventilation
113
- * @param surfaceHabitable {string}
113
+ * @param surfaceHabitable {number}
114
114
  * @param debitsVentilation {{qvarep_conv?: number, qvasouf_conv?: number, smea_conv?: number}}
115
115
  * @return number
116
116
  */
@@ -0,0 +1,62 @@
1
+ import { TypeDpe } from '../../../dpe/domain/models/type-habitation.model.js';
2
+
3
+ /**
4
+ * Calcul du nombre d’adultes équivalent Nadeq
5
+ * Chapitre 11.1 Calcul du besoin d’ECS
6
+ *
7
+ * Methode_de_calcul_3CL_DPE_2021 - Page 70
8
+ * Octobre 2021
9
+ * @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
10
+ */
11
+ export class NadeqService {
12
+ /**
13
+ * @param ctx {Contexte}
14
+ * @param bv {BaieVitree}
15
+ * @return {number}
16
+ */
17
+ execute(ctx) {
18
+ if (ctx.typeDpe === TypeDpe.MAISON) {
19
+ return this.#calculateIndividualNadeq(ctx.surfaceHabitable);
20
+ }
21
+
22
+ return this.#calculateCollectiveNadeq(
23
+ ctx.surfaceHabitable,
24
+ ctx.typeDpe === TypeDpe.APPARTEMENT ? 1 : ctx.nombreAppartement
25
+ );
26
+ }
27
+
28
+ /**
29
+ * Calcul de nadeq pour une maison ou un logement individuel
30
+ * @param surfaceHabitableLogement {number}
31
+ * @returns {number}
32
+ */
33
+ #calculateIndividualNadeq(surfaceHabitableLogement) {
34
+ let Nmax;
35
+
36
+ if (surfaceHabitableLogement < 30) Nmax = 1;
37
+ else if (surfaceHabitableLogement < 70) Nmax = 1.75 - 0.01875 * (70 - surfaceHabitableLogement);
38
+ else Nmax = 0.025 * surfaceHabitableLogement;
39
+
40
+ if (Nmax < 1.75) return Nmax;
41
+ else return 1.75 + 0.3 * (Nmax - 1.75);
42
+ }
43
+
44
+ /**
45
+ * Calcul de nadeq pour un logement collectif
46
+ *
47
+ * @param surfaceHabitableImmeuble {number}
48
+ * @param nombreAppartement {number}
49
+ * @returns {number}
50
+ */
51
+ #calculateCollectiveNadeq(surfaceHabitableImmeuble, nombreAppartement) {
52
+ const Shmoy = surfaceHabitableImmeuble / nombreAppartement;
53
+
54
+ let Nmax;
55
+ if (Shmoy < 10) Nmax = 1;
56
+ else if (Shmoy < 50) Nmax = 1.75 - 0.01875 * (50 - Shmoy);
57
+ else Nmax = 0.035 * Shmoy;
58
+
59
+ if (Nmax < 1.75) return nombreAppartement * Nmax;
60
+ else return nombreAppartement * (1.75 + 0.3 * (Nmax - 1.75));
61
+ }
62
+ }
@@ -0,0 +1,71 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import { NadeqService } from './nadeq.service.js';
3
+ import { TypeDpe } from '../../../dpe/domain/models/type-habitation.model.js';
4
+
5
+ describe('Nadeq unit tests', () => {
6
+ /**
7
+ * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf Page 70
8
+ */
9
+ const service = new NadeqService();
10
+
11
+ test.each([
12
+ { surfaceHabitable: 8, expectedNadeq: 1 },
13
+ { surfaceHabitable: 28, expectedNadeq: 1 },
14
+ { surfaceHabitable: 45, expectedNadeq: 1.28125 },
15
+ { surfaceHabitable: 51, expectedNadeq: 1.39375 },
16
+ { surfaceHabitable: 70, expectedNadeq: 1.75 },
17
+ { surfaceHabitable: 75, expectedNadeq: 1.7875 }
18
+ ])(
19
+ `Typologie MAISON : Nombre d'adultes équivalent $expectedNadeq pour une surface de logement $surfaceHabitable`,
20
+ ({ expectedNadeq, surfaceHabitable }) => {
21
+ const ctx = {
22
+ surfaceHabitable: surfaceHabitable,
23
+ typeDpe: TypeDpe.MAISON
24
+ };
25
+ expect(service.execute(ctx)).toBe(expectedNadeq);
26
+ }
27
+ );
28
+
29
+ test.each([
30
+ { surfaceHabitable: 8, expectedNadeq: 1 },
31
+ { surfaceHabitable: 28, expectedNadeq: 1.3375 },
32
+ { surfaceHabitable: 45, expectedNadeq: 1.65625 },
33
+ { surfaceHabitable: 51, expectedNadeq: 1.7605 },
34
+ { surfaceHabitable: 70, expectedNadeq: 1.96 },
35
+ { surfaceHabitable: 75, expectedNadeq: 2.0125 }
36
+ ])(
37
+ `Typologie APPARTEMENT : Nombre d'adultes équivalent $expectedNadeq pour une surface de logement $surfaceHabitable`,
38
+ ({ expectedNadeq, surfaceHabitable }) => {
39
+ const ctx = {
40
+ surfaceHabitable: surfaceHabitable,
41
+ typeDpe: TypeDpe.APPARTEMENT
42
+ };
43
+ expect(service.execute(ctx)).toBeCloseTo(expectedNadeq, 2);
44
+ }
45
+ );
46
+
47
+ test.each([
48
+ { surfaceHabitable: 8, expectedNadeq: 1, nombreAppartement: 1 },
49
+ { surfaceHabitable: 8, expectedNadeq: 2, nombreAppartement: 2 },
50
+ { surfaceHabitable: 28, expectedNadeq: 1.3375, nombreAppartement: 1 },
51
+ { surfaceHabitable: 28, expectedNadeq: 2.15, nombreAppartement: 2 },
52
+ { surfaceHabitable: 45, expectedNadeq: 1.65625, nombreAppartement: 1 },
53
+ { surfaceHabitable: 45, expectedNadeq: 2.46875, nombreAppartement: 2 },
54
+ { surfaceHabitable: 51, expectedNadeq: 1.7605, nombreAppartement: 1 },
55
+ { surfaceHabitable: 51, expectedNadeq: 2.58125, nombreAppartement: 2 },
56
+ { surfaceHabitable: 70, expectedNadeq: 1.96, nombreAppartement: 1 },
57
+ { surfaceHabitable: 70, expectedNadeq: 2.9375, nombreAppartement: 2 },
58
+ { surfaceHabitable: 75, expectedNadeq: 2.0125, nombreAppartement: 1 },
59
+ { surfaceHabitable: 75, expectedNadeq: 3.03125, nombreAppartement: 2 }
60
+ ])(
61
+ `Typologie IMMEUBLE : Nombre d'adultes équivalent $expectedNadeq pour une surface de logement $surfaceHabitable et un nombre d'apartement $nombreAppartement`,
62
+ ({ expectedNadeq, surfaceHabitable, nombreAppartement }) => {
63
+ const ctx = {
64
+ surfaceHabitable: surfaceHabitable,
65
+ nombreAppartement: nombreAppartement,
66
+ typeDpe: TypeDpe.IMMEUBLE
67
+ };
68
+ expect(service.execute(ctx)).toBeCloseTo(expectedNadeq, 2);
69
+ }
70
+ );
71
+ });