@open3cl/engine 1.0.11 → 1.0.13
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.
- package/features/dpe/domain/models/dpe.model.ts +9 -0
- package/features/dpe/domain/models/installation-chauffage.model.ts +2 -2
- package/features/dpe/domain/models/installation-ecs.model.ts +15 -2
- package/features/dpe/domain/models/type-habitation.model.js +1 -0
- package/features/dpe/domain/models/type-stockage.model.js +8 -0
- package/features/dpe/infrastructure/ecs/ecsTv.store.js +43 -0
- package/features/dpe/infrastructure/ecs/ecsTv.store.spec.js +36 -0
- package/features/dpe/infrastructure/froid/frTv.store.js +4 -2
- package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.js +69 -3
- package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +123 -1
- package/features/engine/domain/apport_et_besoin/apport_gratuit/apport-gratuit.service.js +18 -2
- package/features/engine/domain/apport_et_besoin/apport_gratuit/apport-gratuit.service.spec.js +13 -2
- package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.js +150 -0
- package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +145 -0
- package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.js +168 -0
- package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +313 -0
- package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.js +127 -0
- package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +220 -0
- package/features/engine/domain/contexte.builder.js +5 -1
- package/features/engine/domain/ecs/generateur-ecs.service.js +106 -0
- package/features/engine/domain/ecs/generateur-ecs.service.spec.js +136 -0
- package/features/engine/domain/ecs/installation-ecs.service.js +156 -0
- package/features/engine/domain/ecs/installation-ecs.service.spec.js +284 -0
- package/features/engine/domain/engine.service.js +13 -2
- package/package.json +1 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import corpus from '../../../../../../test/corpus-sano.json';
|
|
3
|
+
import { expect_or, getAdemeFileJson } from '../../../../../../test/test-helpers.js';
|
|
4
|
+
import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
|
|
5
|
+
import { ContexteBuilder } from '../../contexte.builder.js';
|
|
6
|
+
import { FrTvStore } from '../../../../dpe/infrastructure/froid/frTv.store.js';
|
|
7
|
+
import { mois_liste } from '../../../../../utils.js';
|
|
8
|
+
import { PerteChRecupService } from './perte-ch-recup.service.js';
|
|
9
|
+
import { BesoinChService } from './besoin-ch.service.js';
|
|
10
|
+
import { ApportGratuitService } from '../apport_gratuit/apport-gratuit.service.js';
|
|
11
|
+
import { PRECISION_PERCENT } from '../../../../../../test/constant.js';
|
|
12
|
+
|
|
13
|
+
/** @type {PerteChRecupService} **/
|
|
14
|
+
let service;
|
|
15
|
+
|
|
16
|
+
/** @type {BesoinChService} **/
|
|
17
|
+
let besoinChService;
|
|
18
|
+
|
|
19
|
+
/** @type {ApportGratuitService} **/
|
|
20
|
+
let apportGratuitService;
|
|
21
|
+
|
|
22
|
+
/** @type {FrTvStore} **/
|
|
23
|
+
let tvStore;
|
|
24
|
+
|
|
25
|
+
/** @type {DpeNormalizerService} **/
|
|
26
|
+
let normalizerService;
|
|
27
|
+
|
|
28
|
+
/** @type {ContexteBuilder} **/
|
|
29
|
+
let contexteBuilder;
|
|
30
|
+
|
|
31
|
+
describe('Calcul des pertes de génération de chauffage récupérées', () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
tvStore = new FrTvStore();
|
|
34
|
+
service = new PerteChRecupService(tvStore);
|
|
35
|
+
besoinChService = new BesoinChService();
|
|
36
|
+
apportGratuitService = new ApportGratuitService();
|
|
37
|
+
normalizerService = new DpeNormalizerService();
|
|
38
|
+
contexteBuilder = new ContexteBuilder();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test.each([
|
|
42
|
+
{
|
|
43
|
+
label: 'Installation CH uniquement avec ventouse, utilisation de nref',
|
|
44
|
+
enumUsageGenerateurId: 1,
|
|
45
|
+
presenceVentouse: 1,
|
|
46
|
+
expected: 562.5
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: 'Installation CH uniquement sans ventouse, utilisation de nref',
|
|
50
|
+
enumUsageGenerateurId: 1,
|
|
51
|
+
presenceVentouse: 0,
|
|
52
|
+
expected: 375
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: "Installation CH uniquement sans ventouse, pas d'utilisation de nref",
|
|
56
|
+
enumUsageGenerateurId: 1,
|
|
57
|
+
presenceVentouse: 0,
|
|
58
|
+
nref: 500,
|
|
59
|
+
expected: 7971.08
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
label: 'Installation ECS uniquement avec ventouse',
|
|
63
|
+
enumUsageGenerateurId: 2,
|
|
64
|
+
presenceVentouse: 1,
|
|
65
|
+
expected: 114.94
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: 'Installation ECS uniquement sans ventouse',
|
|
69
|
+
enumUsageGenerateurId: 2,
|
|
70
|
+
presenceVentouse: 0,
|
|
71
|
+
expected: 76.627
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
label: 'Installation ECS + CH avec ventouse, utilisation de nref',
|
|
75
|
+
enumUsageGenerateurId: 3,
|
|
76
|
+
presenceVentouse: 1,
|
|
77
|
+
expected: 562.5
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
label: 'Installation ECS + CH sans ventouse, utilisation de nref',
|
|
81
|
+
enumUsageGenerateurId: 3,
|
|
82
|
+
presenceVentouse: 0,
|
|
83
|
+
expected: 375
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: "Installation ECS + CH sans ventouse, pas d'utilisation de nref",
|
|
87
|
+
enumUsageGenerateurId: 3,
|
|
88
|
+
presenceVentouse: 0,
|
|
89
|
+
nref: 450,
|
|
90
|
+
expected: 10729.642
|
|
91
|
+
}
|
|
92
|
+
])(
|
|
93
|
+
'Calcul des pertes récupérées de génération pour le chauffage sur un mois pour $label',
|
|
94
|
+
({ enumUsageGenerateurId, presenceVentouse, nref = undefined, expected }) => {
|
|
95
|
+
/** @type {GenerateurChauffage[]} */
|
|
96
|
+
const generateurs = [
|
|
97
|
+
{
|
|
98
|
+
donnee_entree: {
|
|
99
|
+
presence_ventouse: presenceVentouse,
|
|
100
|
+
enum_usage_generateur_id: enumUsageGenerateurId
|
|
101
|
+
},
|
|
102
|
+
donnee_intermediaire: { pn: 25000, qp0: 125 }
|
|
103
|
+
}
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
expect(service.Qrec(generateurs, nref ?? 12.5, 1532.9)).toBeCloseTo(expected, 3);
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
test('Filtre des générateurs avec pertes de génération', () => {
|
|
111
|
+
const generateur1 = {
|
|
112
|
+
donnee_entree: {}
|
|
113
|
+
};
|
|
114
|
+
const generateur2 = {
|
|
115
|
+
donnee_entree: { position_volume_chauffe: 0, enum_type_generateur_ch_id: 45 }
|
|
116
|
+
};
|
|
117
|
+
const generateur3 = {
|
|
118
|
+
donnee_entree: { position_volume_chauffe: 0, enum_type_generateur_ch_id: 50 }
|
|
119
|
+
};
|
|
120
|
+
const generateur4 = {
|
|
121
|
+
donnee_entree: { position_volume_chauffe: 1, enum_type_generateur_ch_id: 50 }
|
|
122
|
+
};
|
|
123
|
+
const generateur5 = {
|
|
124
|
+
donnee_entree: { position_volume_chauffe: 1, enum_type_generateur_ch_id: 48 }
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/** @type {Logement} */
|
|
128
|
+
let logement = { installation_chauffage_collection: {} };
|
|
129
|
+
expect(service.generateursWithPertesGeneration(logement)).toStrictEqual([]);
|
|
130
|
+
|
|
131
|
+
logement.installation_chauffage_collection = { installation_chauffage: [] };
|
|
132
|
+
expect(service.generateursWithPertesGeneration(logement)).toStrictEqual([]);
|
|
133
|
+
|
|
134
|
+
logement.installation_chauffage_collection = {
|
|
135
|
+
installation_chauffage: [{ generateur_chauffage_collection: {} }]
|
|
136
|
+
};
|
|
137
|
+
expect(service.generateursWithPertesGeneration(logement)).toStrictEqual([]);
|
|
138
|
+
|
|
139
|
+
logement.installation_chauffage_collection = {
|
|
140
|
+
installation_chauffage: [{ generateur_chauffage_collection: { generateur_chauffage: [] } }]
|
|
141
|
+
};
|
|
142
|
+
expect(service.generateursWithPertesGeneration(logement)).toStrictEqual([]);
|
|
143
|
+
|
|
144
|
+
logement.installation_chauffage_collection = {
|
|
145
|
+
installation_chauffage: [
|
|
146
|
+
{
|
|
147
|
+
generateur_chauffage_collection: {
|
|
148
|
+
generateur_chauffage: [generateur1, generateur2, generateur3]
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{ generateur_chauffage_collection: { generateur_chauffage: [generateur4, generateur5] } }
|
|
152
|
+
]
|
|
153
|
+
};
|
|
154
|
+
expect(service.generateursWithPertesGeneration(logement)).toStrictEqual([generateur5]);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test.each([
|
|
158
|
+
{
|
|
159
|
+
label: 'Installation CH en mode conventionnel',
|
|
160
|
+
depensier: false,
|
|
161
|
+
expected: 288
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
label: 'Installation CH en mode dépensier',
|
|
165
|
+
depensier: true,
|
|
166
|
+
expected: 540
|
|
167
|
+
}
|
|
168
|
+
])('Pertes de chauffage récupérées pour $label', ({ depensier, expected }) => {
|
|
169
|
+
vi.spyOn(tvStore, 'getData').mockReturnValue(depensier ? 1.5 : 0.8);
|
|
170
|
+
|
|
171
|
+
/** @type {Contexte} */
|
|
172
|
+
const ctx = {
|
|
173
|
+
altitude: { value: '400-800m' },
|
|
174
|
+
zoneClimatique: { value: 'h1a' },
|
|
175
|
+
inertie: { ilpa: 1 }
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/** @type {Logement} */
|
|
179
|
+
const logement = {
|
|
180
|
+
installation_chauffage_collection: {
|
|
181
|
+
installation_chauffage: [
|
|
182
|
+
{
|
|
183
|
+
generateur_chauffage_collection: {
|
|
184
|
+
generateur_chauffage: [
|
|
185
|
+
{
|
|
186
|
+
donnee_entree: {
|
|
187
|
+
position_volume_chauffe: 1,
|
|
188
|
+
enum_type_generateur_ch_id: 48,
|
|
189
|
+
enum_usage_generateur_id: 1
|
|
190
|
+
},
|
|
191
|
+
donnee_intermediaire: { pn: 25000, qp0: 125 }
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
donnees_de_calcul: {
|
|
199
|
+
besoinChauffageHP: {
|
|
200
|
+
Janvier: 102.5,
|
|
201
|
+
Février: 102.5,
|
|
202
|
+
Mars: 102.5,
|
|
203
|
+
Avril: 102.5,
|
|
204
|
+
Mai: 102.5,
|
|
205
|
+
Juin: 102.5,
|
|
206
|
+
Juillet: 102.5,
|
|
207
|
+
Aout: 102.5,
|
|
208
|
+
Septembre: 102.5,
|
|
209
|
+
Octobre: 102.5,
|
|
210
|
+
Novembre: 102.5,
|
|
211
|
+
Décembre: 102.5
|
|
212
|
+
},
|
|
213
|
+
besoinChauffageDepensierHP: {
|
|
214
|
+
Janvier: 112.5,
|
|
215
|
+
Février: 112.5,
|
|
216
|
+
Mars: 112.5,
|
|
217
|
+
Avril: 112.5,
|
|
218
|
+
Mai: 112.5,
|
|
219
|
+
Juin: 112.5,
|
|
220
|
+
Juillet: 112.5,
|
|
221
|
+
Aout: 112.5,
|
|
222
|
+
Septembre: 112.5,
|
|
223
|
+
Octobre: 112.5,
|
|
224
|
+
Novembre: 112.5,
|
|
225
|
+
Décembre: 112.5
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
expect(service.pertesGenerateurChRecup(ctx, logement, depensier)).toBeCloseTo(expected, 3);
|
|
231
|
+
for (const mois of mois_liste) {
|
|
232
|
+
expect(tvStore.getData).toHaveBeenCalledWith(
|
|
233
|
+
depensier ? 'nref21' : 'nref19',
|
|
234
|
+
'400-800m',
|
|
235
|
+
'h1a',
|
|
236
|
+
mois,
|
|
237
|
+
1
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("Test d'intégration des installations CH", () => {
|
|
243
|
+
test.each(corpus)('vérification des pertes de génération ch pour dpe %s', (ademeId) => {
|
|
244
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
245
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
246
|
+
dpeRequest.logement.donnees_de_calcul = {
|
|
247
|
+
apportsInterneDepensier: [],
|
|
248
|
+
apportsInterneCh: [],
|
|
249
|
+
apportsSolaire: [],
|
|
250
|
+
besoinChauffageHP: [],
|
|
251
|
+
besoinChauffageDepensierHP: []
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/** @type {Contexte} */
|
|
255
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
256
|
+
apportGratuitService.apportInterne(ctx, dpeRequest.logement);
|
|
257
|
+
apportGratuitService.apportSolaire(ctx, dpeRequest.logement);
|
|
258
|
+
besoinChService.execute(ctx, dpeRequest.logement);
|
|
259
|
+
const pertesGeneration = service.execute(ctx, dpeRequest.logement);
|
|
260
|
+
|
|
261
|
+
const expectedValue = dpeRequest.logement.sortie.apport_et_besoin.pertes_generateur_ch_recup;
|
|
262
|
+
const calculatedValue = pertesGeneration.pertes_generateur_ch_recup;
|
|
263
|
+
|
|
264
|
+
expect_or(
|
|
265
|
+
() =>
|
|
266
|
+
expect(Math.abs(calculatedValue - expectedValue) / (expectedValue || 1)).toBeLessThan(
|
|
267
|
+
PRECISION_PERCENT
|
|
268
|
+
),
|
|
269
|
+
() =>
|
|
270
|
+
expect(
|
|
271
|
+
Math.abs(calculatedValue - expectedValue * 1000) / (expectedValue * 1000 || 1)
|
|
272
|
+
).toBeLessThan(PRECISION_PERCENT)
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test.each(corpus)(
|
|
277
|
+
'vérification des pertes de génération ch depensier des installations CH pour dpe %s',
|
|
278
|
+
(ademeId) => {
|
|
279
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
280
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
281
|
+
dpeRequest.logement.donnees_de_calcul = {
|
|
282
|
+
apportsInterneDepensier: [],
|
|
283
|
+
apportsInterneCh: [],
|
|
284
|
+
apportsSolaire: [],
|
|
285
|
+
besoinChauffageHP: [],
|
|
286
|
+
besoinChauffageDepensierHP: []
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/** @type {Contexte} */
|
|
290
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
291
|
+
apportGratuitService.apportInterne(ctx, dpeRequest.logement);
|
|
292
|
+
apportGratuitService.apportSolaire(ctx, dpeRequest.logement);
|
|
293
|
+
besoinChService.execute(ctx, dpeRequest.logement);
|
|
294
|
+
const pertesGeneration = service.execute(ctx, dpeRequest.logement);
|
|
295
|
+
|
|
296
|
+
const expectedValue =
|
|
297
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_generateur_ch_recup_depensier;
|
|
298
|
+
const calculatedValue = pertesGeneration.pertes_generateur_ch_recup_depensier;
|
|
299
|
+
|
|
300
|
+
expect_or(
|
|
301
|
+
() =>
|
|
302
|
+
expect(Math.abs(calculatedValue - expectedValue) / (expectedValue || 1)).toBeLessThan(
|
|
303
|
+
PRECISION_PERCENT
|
|
304
|
+
),
|
|
305
|
+
() =>
|
|
306
|
+
expect(
|
|
307
|
+
Math.abs(calculatedValue - expectedValue * 1000) / (expectedValue * 1000 || 1)
|
|
308
|
+
).toBeLessThan(PRECISION_PERCENT)
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
});
|
|
313
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { mois_liste } from '../../../../../utils.js';
|
|
2
|
+
import { inject } from 'dioma';
|
|
3
|
+
import { FrTvStore } from '../../../../dpe/infrastructure/froid/frTv.store.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Calcul des pertes récupérées de distribution et de stockage d'ECS
|
|
7
|
+
* Chapitre 9.1.1 Consommation de chauffage
|
|
8
|
+
*
|
|
9
|
+
* Données calculées
|
|
10
|
+
* — pertes_distribution_ecs_recup : pertes de distribution d'ECS récupérées(kWh)
|
|
11
|
+
* — pertes_distribution_ecs_recup_depensier : pertes de distribution d'ECS récupérées(kWh) pour le scénario dépensier
|
|
12
|
+
* — pertes_stockage_ecs_recup : pertes de stockage d'ECS récupérées(kWh)
|
|
13
|
+
*
|
|
14
|
+
* Methode_de_calcul_3CL_DPE_2021 - Page 57
|
|
15
|
+
* Octobre 2021
|
|
16
|
+
* @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
|
|
17
|
+
*/
|
|
18
|
+
export class PerteEcsRecupService {
|
|
19
|
+
/**
|
|
20
|
+
* @type {FrTvStore}
|
|
21
|
+
*/
|
|
22
|
+
#tvStore;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param tvStore {FrTvStore}
|
|
26
|
+
*/
|
|
27
|
+
constructor(tvStore = inject(FrTvStore)) {
|
|
28
|
+
this.#tvStore = tvStore;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Pertes de distribution et de stockage ECS (kWh)
|
|
33
|
+
* @param ctx {Contexte}
|
|
34
|
+
* @param logement {Logement}
|
|
35
|
+
* @return {{pertes_distribution_ecs_recup: number, pertes_distribution_ecs_recup_depensier: number, pertes_stockage_ecs_recup: number, pertes_stockage_ecs_recup_depensier: number}}
|
|
36
|
+
*/
|
|
37
|
+
execute(ctx, logement) {
|
|
38
|
+
return {
|
|
39
|
+
pertes_distribution_ecs_recup: this.pertesDistributionEcsRecup(ctx, logement, false) / 1000,
|
|
40
|
+
pertes_distribution_ecs_recup_depensier:
|
|
41
|
+
this.pertesDistributionEcsRecup(ctx, logement, true) / 1000,
|
|
42
|
+
pertes_stockage_ecs_recup: this.pertesStockageEcsRecup(ctx, logement, false) / 1000,
|
|
43
|
+
pertes_stockage_ecs_recup_depensier: this.pertesStockageEcsRecup(ctx, logement, true) / 1000
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Pertes récupérées de distribution d’ECS pour le chauffage (Wh)
|
|
49
|
+
* 9.1.1 Consommation de chauffage
|
|
50
|
+
*
|
|
51
|
+
* @param ctx {Contexte}
|
|
52
|
+
* @param logement {Logement}
|
|
53
|
+
* @param depensier {boolean}
|
|
54
|
+
* @returns {number}
|
|
55
|
+
*/
|
|
56
|
+
pertesDistributionEcsRecup(ctx, logement, depensier) {
|
|
57
|
+
const installationsEcs = logement.installation_ecs_collection?.installation_ecs || [];
|
|
58
|
+
|
|
59
|
+
const pertesDistribution = installationsEcs.reduce((acc, installation) => {
|
|
60
|
+
/** @type {InstallationEcsDU}*/
|
|
61
|
+
const installationEcsDU = installation.donnee_utilisateur;
|
|
62
|
+
|
|
63
|
+
const pertesDistributionIndividuelleVolumeChauffee = depensier
|
|
64
|
+
? installationEcsDU.QdwIndVc.depensier
|
|
65
|
+
: installationEcsDU.QdwIndVc.conventionnel;
|
|
66
|
+
const pertesDistributionCollectiveVolumeChauffee = depensier
|
|
67
|
+
? installationEcsDU.QdwColVc.depensier
|
|
68
|
+
: installationEcsDU.QdwColVc.conventionnel;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
acc +
|
|
72
|
+
pertesDistributionIndividuelleVolumeChauffee +
|
|
73
|
+
pertesDistributionCollectiveVolumeChauffee
|
|
74
|
+
);
|
|
75
|
+
}, 0);
|
|
76
|
+
|
|
77
|
+
return mois_liste.reduce((acc, mois) => {
|
|
78
|
+
return (
|
|
79
|
+
acc +
|
|
80
|
+
(0.48 *
|
|
81
|
+
pertesDistribution *
|
|
82
|
+
this.#tvStore.getData(
|
|
83
|
+
depensier ? 'nref21' : 'nref19',
|
|
84
|
+
ctx.altitude.value,
|
|
85
|
+
ctx.zoneClimatique.value,
|
|
86
|
+
mois,
|
|
87
|
+
ctx.inertie.ilpa
|
|
88
|
+
)) /
|
|
89
|
+
8760
|
|
90
|
+
);
|
|
91
|
+
}, 0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Pertes récupérées du stockage d’ECS pour le chauffage (Wh)
|
|
96
|
+
* 9.1.1 Consommation de chauffage
|
|
97
|
+
*
|
|
98
|
+
* @param ctx {Contexte}
|
|
99
|
+
* @param logement {Logement}
|
|
100
|
+
* @param depensier {boolean}
|
|
101
|
+
* @returns {number}
|
|
102
|
+
*/
|
|
103
|
+
pertesStockageEcsRecup(ctx, logement, depensier) {
|
|
104
|
+
const installationsEcs = logement.installation_ecs_collection?.installation_ecs || [];
|
|
105
|
+
|
|
106
|
+
let pertesStockage = installationsEcs.reduce((acc, installation) => {
|
|
107
|
+
/** @type {InstallationEcsDU}*/
|
|
108
|
+
const installationEcsDU = installation.donnee_utilisateur;
|
|
109
|
+
|
|
110
|
+
return acc + (0.48 * installationEcsDU.QgwRecuperable) / 8760.0;
|
|
111
|
+
}, 0);
|
|
112
|
+
|
|
113
|
+
return mois_liste.reduce((acc, mois) => {
|
|
114
|
+
return (
|
|
115
|
+
acc +
|
|
116
|
+
pertesStockage *
|
|
117
|
+
this.#tvStore.getData(
|
|
118
|
+
depensier ? 'nref21' : 'nref19',
|
|
119
|
+
ctx.altitude.value,
|
|
120
|
+
ctx.zoneClimatique.value,
|
|
121
|
+
mois,
|
|
122
|
+
ctx.inertie.ilpa
|
|
123
|
+
)
|
|
124
|
+
);
|
|
125
|
+
}, 0);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import corpus from '../../../../../../test/corpus-sano.json';
|
|
3
|
+
import { expect_or, getAdemeFileJson } from '../../../../../../test/test-helpers.js';
|
|
4
|
+
import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
|
|
5
|
+
import { ContexteBuilder } from '../../contexte.builder.js';
|
|
6
|
+
import { PerteEcsRecupService } from './perte-ecs-recup.service.js';
|
|
7
|
+
import { FrTvStore } from '../../../../dpe/infrastructure/froid/frTv.store.js';
|
|
8
|
+
import { mois_liste } from '../../../../../utils.js';
|
|
9
|
+
import { EcsTvStore } from '../../../../dpe/infrastructure/ecs/ecsTv.store.js';
|
|
10
|
+
import { GenerateurEcsService } from '../../ecs/generateur-ecs.service.js';
|
|
11
|
+
import { InstallationEcsService } from '../../ecs/installation-ecs.service.js';
|
|
12
|
+
|
|
13
|
+
/** @type {PerteEcsRecupService} **/
|
|
14
|
+
let service;
|
|
15
|
+
|
|
16
|
+
/** @type {FrTvStore} **/
|
|
17
|
+
let tvStore;
|
|
18
|
+
|
|
19
|
+
/** @type {InstallationEcsService} **/
|
|
20
|
+
let installationEcsService;
|
|
21
|
+
|
|
22
|
+
/** @type {DpeNormalizerService} **/
|
|
23
|
+
let normalizerService;
|
|
24
|
+
|
|
25
|
+
/** @type {ContexteBuilder} **/
|
|
26
|
+
let contexteBuilder;
|
|
27
|
+
|
|
28
|
+
describe('Calcul des pertes de distribution et stockage récupérées', () => {
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
tvStore = new FrTvStore();
|
|
31
|
+
service = new PerteEcsRecupService(tvStore);
|
|
32
|
+
installationEcsService = new InstallationEcsService(new GenerateurEcsService(new EcsTvStore()));
|
|
33
|
+
normalizerService = new DpeNormalizerService();
|
|
34
|
+
contexteBuilder = new ContexteBuilder();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test.each([
|
|
38
|
+
{
|
|
39
|
+
label: 'Installation ECS en mode conventionnel',
|
|
40
|
+
depensier: false,
|
|
41
|
+
expected: 0.192
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: 'Installation ECS en mode dépensier',
|
|
45
|
+
depensier: true,
|
|
46
|
+
expected: 0.725
|
|
47
|
+
}
|
|
48
|
+
])('Calcul des pertes de distribution récupérées pour $label', ({ depensier, expected }) => {
|
|
49
|
+
vi.spyOn(tvStore, 'getData').mockReturnValue(depensier ? 1.5 : 0.8);
|
|
50
|
+
|
|
51
|
+
/** @type {Contexte} */
|
|
52
|
+
const ctx = {
|
|
53
|
+
altitude: { value: '400-800m' },
|
|
54
|
+
zoneClimatique: { value: 'h1a' },
|
|
55
|
+
inertie: { ilpa: 1 }
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** @type {Logement} */
|
|
59
|
+
const logement = {
|
|
60
|
+
installation_ecs_collection: {
|
|
61
|
+
installation_ecs: [
|
|
62
|
+
{
|
|
63
|
+
donnee_utilisateur: {
|
|
64
|
+
QdwIndVc: { conventionnel: 100, depensier: 200 },
|
|
65
|
+
QdwColVc: { conventionnel: 50, depensier: 120 }
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
donnee_utilisateur: {
|
|
70
|
+
QdwIndVc: { conventionnel: 150, depensier: 280 },
|
|
71
|
+
QdwColVc: { conventionnel: 65, depensier: 135 }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
expect(service.pertesDistributionEcsRecup(ctx, logement, depensier)).toBeCloseTo(expected, 3);
|
|
79
|
+
for (const mois of mois_liste) {
|
|
80
|
+
expect(tvStore.getData).toHaveBeenCalledWith(
|
|
81
|
+
depensier ? 'nref21' : 'nref19',
|
|
82
|
+
'400-800m',
|
|
83
|
+
'h1a',
|
|
84
|
+
mois,
|
|
85
|
+
1
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test.each([
|
|
91
|
+
{
|
|
92
|
+
label: 'Installation ECS en mode conventionnel',
|
|
93
|
+
depensier: false,
|
|
94
|
+
expected: 63.209
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
label: 'Installation ECS en mode dépensier',
|
|
98
|
+
depensier: true,
|
|
99
|
+
expected: 118.516
|
|
100
|
+
}
|
|
101
|
+
])('Calcul des pertes de stockage récupérées pour $label', ({ depensier, expected }) => {
|
|
102
|
+
vi.spyOn(tvStore, 'getData').mockReturnValue(depensier ? 1.5 : 0.8);
|
|
103
|
+
|
|
104
|
+
/** @type {Contexte} */
|
|
105
|
+
const ctx = {
|
|
106
|
+
altitude: { value: '400-800m' },
|
|
107
|
+
zoneClimatique: { value: 'h1a' },
|
|
108
|
+
inertie: { ilpa: 1 }
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/** @type {Logement} */
|
|
112
|
+
const logement = {
|
|
113
|
+
installation_ecs_collection: {
|
|
114
|
+
installation_ecs: [
|
|
115
|
+
{ donnee_utilisateur: { QgwRecuperable: 120012 } },
|
|
116
|
+
{ donnee_utilisateur: { QgwRecuperable: 150 } }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
expect(service.pertesStockageEcsRecup(ctx, logement, depensier)).toBeCloseTo(expected, 3);
|
|
122
|
+
for (const mois of mois_liste) {
|
|
123
|
+
expect(tvStore.getData).toHaveBeenCalledWith(
|
|
124
|
+
depensier ? 'nref21' : 'nref19',
|
|
125
|
+
'400-800m',
|
|
126
|
+
'h1a',
|
|
127
|
+
mois,
|
|
128
|
+
1
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe("Test d'intégration des installations ECS", () => {
|
|
134
|
+
test.each(corpus)(
|
|
135
|
+
'vérification des pertes de distribution ecs recup des installations ECS pour dpe %s',
|
|
136
|
+
(ademeId) => {
|
|
137
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
138
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
139
|
+
|
|
140
|
+
/** @type {Contexte} */
|
|
141
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
142
|
+
|
|
143
|
+
installationEcsService.execute(ctx, dpeRequest.logement, {
|
|
144
|
+
besoin_ecs: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs,
|
|
145
|
+
besoin_ecs_depensier: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs_depensier
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const pertesStockage = service.execute(ctx, dpeRequest.logement);
|
|
149
|
+
|
|
150
|
+
expect_or(
|
|
151
|
+
() =>
|
|
152
|
+
expect(pertesStockage.pertes_distribution_ecs_recup).toBeCloseTo(
|
|
153
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_distribution_ecs_recup
|
|
154
|
+
),
|
|
155
|
+
() =>
|
|
156
|
+
expect(pertesStockage.pertes_distribution_ecs_recup * 1000).toBeCloseTo(
|
|
157
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_distribution_ecs_recup
|
|
158
|
+
)
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
test.each(corpus)(
|
|
164
|
+
'vérification des pertes de distribution ecs recup depensier des installations ECS pour dpe %s',
|
|
165
|
+
(ademeId) => {
|
|
166
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
167
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
168
|
+
|
|
169
|
+
/** @type {Contexte} */
|
|
170
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
171
|
+
|
|
172
|
+
installationEcsService.execute(ctx, dpeRequest.logement, {
|
|
173
|
+
besoin_ecs: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs,
|
|
174
|
+
besoin_ecs_depensier: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs_depensier
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const pertesStockage = service.execute(ctx, dpeRequest.logement);
|
|
178
|
+
|
|
179
|
+
expect_or(
|
|
180
|
+
() =>
|
|
181
|
+
expect(pertesStockage.pertes_distribution_ecs_recup_depensier).toBeCloseTo(
|
|
182
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_distribution_ecs_recup_depensier
|
|
183
|
+
),
|
|
184
|
+
() =>
|
|
185
|
+
expect(pertesStockage.pertes_distribution_ecs_recup_depensier * 1000).toBeCloseTo(
|
|
186
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_distribution_ecs_recup_depensier
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
test.each(corpus)(
|
|
193
|
+
'vérification des pertes de stockage ecs recup des installations ECS pour dpe %s',
|
|
194
|
+
(ademeId) => {
|
|
195
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
196
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
197
|
+
|
|
198
|
+
/** @type {Contexte} */
|
|
199
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
200
|
+
|
|
201
|
+
installationEcsService.execute(ctx, dpeRequest.logement, {
|
|
202
|
+
besoin_ecs: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs,
|
|
203
|
+
besoin_ecs_depensier: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs_depensier
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const pertesStockage = service.execute(ctx, dpeRequest.logement);
|
|
207
|
+
expect_or(
|
|
208
|
+
() =>
|
|
209
|
+
expect(pertesStockage.pertes_stockage_ecs_recup).toBeCloseTo(
|
|
210
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_stockage_ecs_recup
|
|
211
|
+
),
|
|
212
|
+
() =>
|
|
213
|
+
expect(pertesStockage.pertes_stockage_ecs_recup * 1000).toBeCloseTo(
|
|
214
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_stockage_ecs_recup
|
|
215
|
+
)
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
@@ -132,8 +132,12 @@ export class ContexteBuilder {
|
|
|
132
132
|
|
|
133
133
|
if ([1, 14, 18].includes(methodeApplication)) {
|
|
134
134
|
return TypeDpe.MAISON;
|
|
135
|
-
} else if (
|
|
135
|
+
} else if (
|
|
136
|
+
[2, 3, 4, 5, 15, 16, 19, 20, 22, 23, 24, 25, 31, 32, 35, 36, 37].includes(methodeApplication)
|
|
137
|
+
) {
|
|
136
138
|
return TypeDpe.APPARTEMENT;
|
|
139
|
+
} else if ([10, 11, 12, 13, 33, 34, 38, 39, 40].includes(methodeApplication)) {
|
|
140
|
+
return TypeDpe.APPARTEMENT_A_PARTIR_IMMEUBLE;
|
|
137
141
|
}
|
|
138
142
|
return TypeDpe.IMMEUBLE;
|
|
139
143
|
}
|