@open3cl/engine 1.0.10 → 1.0.12
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/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 +65 -0
- package/features/dpe/infrastructure/ecs/ecsTv.store.spec.js +69 -0
- package/features/dpe/infrastructure/{baieVitreeTv.store.js → enveloppe/baieVitreeTv.store.js} +5 -5
- package/features/dpe/infrastructure/{pontThermiqueTv.store.js → enveloppe/pontThermiqueTv.store.js} +3 -3
- package/features/dpe/infrastructure/froid/frTv.store.js +36 -0
- package/features/dpe/infrastructure/froid/frTv.store.spec.js +52 -0
- package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.js +100 -0
- package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +106 -0
- package/features/engine/domain/apport_et_besoin/apport_gratuit/apport-gratuit.service.js +134 -0
- package/features/engine/domain/apport_et_besoin/apport_gratuit/apport-gratuit.service.spec.js +167 -0
- package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.js +58 -0
- package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +67 -0
- package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.js +123 -0
- package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +205 -0
- package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.js +131 -0
- package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +229 -0
- package/features/engine/domain/{logement → apport_et_besoin}/surface-sud-equivalente.service.js +9 -10
- package/features/engine/domain/{logement → apport_et_besoin}/surface-sud-equivalente.service.spec.js +63 -43
- package/features/engine/domain/contexte.builder.js +53 -6
- package/features/engine/domain/contexte.builder.spec.js +59 -3
- 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 +10 -13
- package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.js +1 -1
- package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +1 -1
- package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.js +1 -1
- package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +1 -1
- package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.js +1 -1
- package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +1 -1
- package/features/engine/domain/logement/nadeq.service.js +8 -7
- package/features/engine/domain/logement/nadeq.service.spec.js +6 -16
- package/features/engine/domain/models/contexte.model.ts +9 -0
- package/package.json +1 -1
- /package/features/dpe/infrastructure/{baieVitreeTv.store.spec.js → enveloppe/baieVitreeTv.store.spec.js} +0 -0
- /package/features/dpe/infrastructure/{pontThermiqueTv.store.spec.js → enveloppe/pontThermiqueTv.store.spec.js} +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
import { SurfaceSudEquivalenteService } from './../surface-sud-equivalente.service.js';
|
|
3
|
+
import { ApportGratuitService } from './apport-gratuit.service.js';
|
|
4
|
+
import { FrTvStore } from '../../../../dpe/infrastructure/froid/frTv.store.js';
|
|
5
|
+
import { mois_liste } from '../../../../../utils.js';
|
|
6
|
+
|
|
7
|
+
/** @type {FrTvStore} **/
|
|
8
|
+
let frTvStore;
|
|
9
|
+
|
|
10
|
+
/** @type {SurfaceSudEquivalenteService} **/
|
|
11
|
+
let surfaceSudEquivalenteService;
|
|
12
|
+
|
|
13
|
+
/** @type {ApportGratuitService} **/
|
|
14
|
+
let service;
|
|
15
|
+
|
|
16
|
+
describe('Calcul des apports gratuits au logement', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
frTvStore = new FrTvStore();
|
|
19
|
+
surfaceSudEquivalenteService = new SurfaceSudEquivalenteService();
|
|
20
|
+
service = new ApportGratuitService(frTvStore, surfaceSudEquivalenteService);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test.each([
|
|
24
|
+
{
|
|
25
|
+
label: 'avec climatisation',
|
|
26
|
+
withClimatisation: true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: 'sans climatisation',
|
|
30
|
+
withClimatisation: false
|
|
31
|
+
}
|
|
32
|
+
])('Determination des apports gratuits pour un logement $label', ({ withClimatisation }) => {
|
|
33
|
+
vi.spyOn(surfaceSudEquivalenteService, 'ssdMois').mockReturnValue(18.5);
|
|
34
|
+
vi.spyOn(frTvStore, 'getData').mockReturnValue(10);
|
|
35
|
+
|
|
36
|
+
/** @type {Contexte} */
|
|
37
|
+
const ctx = {
|
|
38
|
+
zoneClimatique: { value: 'h1a' },
|
|
39
|
+
altitude: { value: '400-800m' },
|
|
40
|
+
inertie: { ilpa: 1 }
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** @type { Logement } **/
|
|
44
|
+
const logement = { enveloppe: { porte_collection: {} } };
|
|
45
|
+
|
|
46
|
+
if (withClimatisation) {
|
|
47
|
+
logement.climatisation_collection = { climatisation: [{}] };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
expect(service.apportSolaire(ctx, logement)).toStrictEqual({
|
|
51
|
+
apport_solaire_ch: 2220000,
|
|
52
|
+
apport_solaire_fr: withClimatisation ? 2220000 : 0
|
|
53
|
+
});
|
|
54
|
+
expect(surfaceSudEquivalenteService.ssdMois).toHaveBeenCalledWith(
|
|
55
|
+
ctx,
|
|
56
|
+
logement.enveloppe,
|
|
57
|
+
'Janvier'
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
for (const mois of mois_liste) {
|
|
61
|
+
expect(frTvStore.getData).toHaveBeenCalledWith(
|
|
62
|
+
'e',
|
|
63
|
+
ctx.altitude.value,
|
|
64
|
+
ctx.zoneClimatique.value,
|
|
65
|
+
mois,
|
|
66
|
+
ctx.inertie.ilpa
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (withClimatisation) {
|
|
70
|
+
expect(frTvStore.getData).toHaveBeenCalledWith(
|
|
71
|
+
'e_fr_28',
|
|
72
|
+
ctx.altitude.value,
|
|
73
|
+
ctx.zoneClimatique.value,
|
|
74
|
+
mois
|
|
75
|
+
);
|
|
76
|
+
} else {
|
|
77
|
+
expect(frTvStore.getData).not.toHaveBeenCalledWith(
|
|
78
|
+
'e_fr_28',
|
|
79
|
+
ctx.altitude.value,
|
|
80
|
+
ctx.zoneClimatique.value,
|
|
81
|
+
mois
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test.each([
|
|
88
|
+
{
|
|
89
|
+
label: 'avec climatisation',
|
|
90
|
+
withClimatisation: true
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: 'sans climatisation',
|
|
94
|
+
withClimatisation: false
|
|
95
|
+
}
|
|
96
|
+
])('Determination des apports internes pour un logement $label', ({ withClimatisation }) => {
|
|
97
|
+
vi.spyOn(frTvStore, 'getData').mockReturnValue(10);
|
|
98
|
+
|
|
99
|
+
/** @type {Contexte} */
|
|
100
|
+
const ctx = {
|
|
101
|
+
zoneClimatique: { value: 'h1a' },
|
|
102
|
+
altitude: { value: '400-800m' },
|
|
103
|
+
inertie: { ilpa: 1 },
|
|
104
|
+
surfaceHabitable: 12,
|
|
105
|
+
nadeq: 1.25
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/** @type { Logement } **/
|
|
109
|
+
const logement = { enveloppe: { porte_collection: {} } };
|
|
110
|
+
|
|
111
|
+
if (withClimatisation) {
|
|
112
|
+
logement.climatisation_collection = { climatisation: [{}] };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
expect(service.apportInterne(ctx, logement)).toStrictEqual({
|
|
116
|
+
apport_interne_ch: 15675.94285714286,
|
|
117
|
+
apport_interne_fr: withClimatisation ? 15675.94285714286 : 0
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
for (const mois of mois_liste) {
|
|
121
|
+
expect(frTvStore.getData).toHaveBeenCalledWith(
|
|
122
|
+
'nref19',
|
|
123
|
+
ctx.altitude.value,
|
|
124
|
+
ctx.zoneClimatique.value,
|
|
125
|
+
mois,
|
|
126
|
+
ctx.inertie.ilpa
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
if (withClimatisation) {
|
|
130
|
+
expect(frTvStore.getData).toHaveBeenCalledWith(
|
|
131
|
+
'nref28',
|
|
132
|
+
ctx.altitude.value,
|
|
133
|
+
ctx.zoneClimatique.value,
|
|
134
|
+
mois
|
|
135
|
+
);
|
|
136
|
+
} else {
|
|
137
|
+
expect(frTvStore.getData).not.toHaveBeenCalledWith(
|
|
138
|
+
'nref28',
|
|
139
|
+
ctx.altitude.value,
|
|
140
|
+
ctx.zoneClimatique.value,
|
|
141
|
+
mois
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('Determination des apports solaires pour un mois donné', () => {
|
|
148
|
+
vi.spyOn(surfaceSudEquivalenteService, 'ssdMois').mockReturnValue(18.5);
|
|
149
|
+
|
|
150
|
+
/** @type {Contexte} */
|
|
151
|
+
const ctx = { zoneClimatique: { id: 1 } };
|
|
152
|
+
|
|
153
|
+
/** @type { Enveloppe } **/
|
|
154
|
+
const enveloppe = {
|
|
155
|
+
porte_collection: {}
|
|
156
|
+
};
|
|
157
|
+
expect(service.apportSolaireMois(ctx, enveloppe, 'Janvier', 18)).toBe(333000);
|
|
158
|
+
expect(surfaceSudEquivalenteService.ssdMois).toHaveBeenCalledWith(ctx, enveloppe, 'Janvier');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('Determination des apports internes dans le logement pour un mois donné', () => {
|
|
162
|
+
/** @type {Contexte} */
|
|
163
|
+
const ctx = { surfaceHabitable: 88, nadeq: 2 };
|
|
164
|
+
|
|
165
|
+
expect(service.apportInterneMois(ctx, 18)).toBeCloseTo(8121.39, 2);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { mois_liste, Njj } from '../../../../../utils.js';
|
|
2
|
+
import { EcsTvStore } from '../../../../dpe/infrastructure/ecs/ecsTv.store.js';
|
|
3
|
+
import { inject } from 'dioma';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Calcul du besoin en eau chaude sanitaire
|
|
7
|
+
* Chapitre 11.1 Calcul du besoin d’ECS
|
|
8
|
+
*
|
|
9
|
+
* Methode_de_calcul_3CL_DPE_2021 - Page 70
|
|
10
|
+
* Octobre 2021
|
|
11
|
+
* @see consolide_anne…arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf
|
|
12
|
+
*/
|
|
13
|
+
export class BesoinEcsService {
|
|
14
|
+
/**
|
|
15
|
+
* @type {EcsTvStore}
|
|
16
|
+
*/
|
|
17
|
+
#tvStore;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param tvStore {EcsTvStore}
|
|
21
|
+
*/
|
|
22
|
+
constructor(tvStore = inject(EcsTvStore)) {
|
|
23
|
+
this.#tvStore = tvStore;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param ctx {Contexte}
|
|
28
|
+
* @return {{besoin_ecs: number, besoin_ecs_depensier: number}}
|
|
29
|
+
*/
|
|
30
|
+
execute(ctx) {
|
|
31
|
+
return mois_liste.reduce(
|
|
32
|
+
(acc, mois) => {
|
|
33
|
+
acc.besoin_ecs += this.besoinEcsMois(ctx, mois, false);
|
|
34
|
+
acc.besoin_ecs_depensier += this.besoinEcsMois(ctx, mois, true);
|
|
35
|
+
return acc;
|
|
36
|
+
},
|
|
37
|
+
{ besoin_ecs: 0, besoin_ecs_depensier: 0 }
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Calcul du besoin ecs pour un mois donné
|
|
43
|
+
*
|
|
44
|
+
* @param ctx {Contexte}
|
|
45
|
+
* @param mois {string}
|
|
46
|
+
* @param depensier {boolean}
|
|
47
|
+
* @returns {number}
|
|
48
|
+
*/
|
|
49
|
+
besoinEcsMois(ctx, mois, depensier) {
|
|
50
|
+
const tefs = this.#tvStore.getTefs(ctx.altitude.value, ctx.zoneClimatique.value, mois);
|
|
51
|
+
|
|
52
|
+
if (depensier) {
|
|
53
|
+
return (1.163 * ctx.nadeq * 79 * (40 - tefs) * Njj[mois]) / 1000;
|
|
54
|
+
} else {
|
|
55
|
+
return (1.163 * ctx.nadeq * 56 * (40 - tefs) * Njj[mois]) / 1000;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import corpus from '../../../../../../test/corpus-sano.json';
|
|
2
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
3
|
+
import { DpeNormalizerService } from '../../../../normalizer/domain/dpe-normalizer.service.js';
|
|
4
|
+
import { ContexteBuilder } from '../../contexte.builder.js';
|
|
5
|
+
import { getAdemeFileJson } from '../../../../../../test/test-helpers.js';
|
|
6
|
+
import { BesoinEcsService } from './besoin-ecs.service.js';
|
|
7
|
+
import { EcsTvStore } from '../../../../dpe/infrastructure/ecs/ecsTv.store.js';
|
|
8
|
+
|
|
9
|
+
/** @type {BesoinEcsService} **/
|
|
10
|
+
let service;
|
|
11
|
+
|
|
12
|
+
/** @type {DpeNormalizerService} **/
|
|
13
|
+
let normalizerService;
|
|
14
|
+
|
|
15
|
+
/** @type {ContexteBuilder} **/
|
|
16
|
+
let contexteBuilder;
|
|
17
|
+
|
|
18
|
+
/** @type {EcsTvStore} **/
|
|
19
|
+
let tvStore;
|
|
20
|
+
|
|
21
|
+
describe('Calcul du besoin en eau chaude sanitaire', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
tvStore = new EcsTvStore();
|
|
24
|
+
service = new BesoinEcsService(tvStore);
|
|
25
|
+
normalizerService = new DpeNormalizerService();
|
|
26
|
+
contexteBuilder = new ContexteBuilder();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('Determination du besoin en eau chaude sanitaire pour un mois donné', () => {
|
|
30
|
+
vi.spyOn(tvStore, 'getTefs').mockReturnValue(0.5);
|
|
31
|
+
|
|
32
|
+
/** @type {Contexte} */
|
|
33
|
+
const ctx = {
|
|
34
|
+
altitude: {
|
|
35
|
+
value: '400-800m'
|
|
36
|
+
},
|
|
37
|
+
zoneClimatique: {
|
|
38
|
+
value: 'h1a'
|
|
39
|
+
},
|
|
40
|
+
nadeq: 2
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
expect(service.besoinEcsMois(ctx, 'Janvier', false)).toBeCloseTo(159.5, 2);
|
|
44
|
+
expect(service.besoinEcsMois(ctx, 'Janvier', true)).toBeCloseTo(225.01, 2);
|
|
45
|
+
expect(tvStore.getTefs).toHaveBeenCalledWith('400-800m', 'h1a', 'Janvier');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("Test d'intégration pour le besoin en eau chaude sanitaire", () => {
|
|
49
|
+
test.each(corpus)('vérification des sorties besoin_ecs pour dpe %s', (ademeId) => {
|
|
50
|
+
/**
|
|
51
|
+
* @type {Dpe}
|
|
52
|
+
*/
|
|
53
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
54
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
55
|
+
|
|
56
|
+
/** @type {Contexte} */
|
|
57
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
58
|
+
|
|
59
|
+
const ecs = service.execute(ctx);
|
|
60
|
+
expect(ecs.besoin_ecs).toBeCloseTo(dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs, 2);
|
|
61
|
+
expect(ecs.besoin_ecs_depensier).toBeCloseTo(
|
|
62
|
+
dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs_depensier,
|
|
63
|
+
2
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
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
|
+
* @param ctx {Contexte}
|
|
33
|
+
* @param logement {Logement}
|
|
34
|
+
* @return {{pertes_distribution_ecs_recup: number, pertes_distribution_ecs_recup_depensier: number, pertes_stockage_ecs_recup: number}}
|
|
35
|
+
*/
|
|
36
|
+
execute(ctx, logement) {
|
|
37
|
+
return {
|
|
38
|
+
pertes_distribution_ecs_recup: this.pertesDistributionEcsRecup(ctx, logement, false),
|
|
39
|
+
pertes_distribution_ecs_recup_depensier: this.pertesDistributionEcsRecup(ctx, logement, true),
|
|
40
|
+
pertes_stockage_ecs_recup: this.pertesStockageEcsRecup(ctx, logement)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pertes récupérées de distribution d’ECS pour le chauffage
|
|
46
|
+
* 9.1.1 Consommation de chauffage
|
|
47
|
+
*
|
|
48
|
+
* @param ctx {Contexte}
|
|
49
|
+
* @param logement {Logement}
|
|
50
|
+
* @param depensier {boolean}
|
|
51
|
+
* @returns {number}
|
|
52
|
+
*/
|
|
53
|
+
pertesDistributionEcsRecup(ctx, logement, depensier) {
|
|
54
|
+
const installationsEcs = logement.installation_ecs_collection?.installation_ecs || [];
|
|
55
|
+
|
|
56
|
+
const pertesDistribution = installationsEcs.reduce((acc, installation) => {
|
|
57
|
+
/** @type {InstallationEcsDU}*/
|
|
58
|
+
const installationEcsDU = installation.donnee_utilisateur;
|
|
59
|
+
|
|
60
|
+
const pertesDistributionIndividuelleVolumeChauffee = depensier
|
|
61
|
+
? installationEcsDU.QdwIndVc.depensier
|
|
62
|
+
: installationEcsDU.QdwIndVc.conventionnel;
|
|
63
|
+
const pertesDistributionCollectiveVolumeChauffee = depensier
|
|
64
|
+
? installationEcsDU.QdwColVc.depensier
|
|
65
|
+
: installationEcsDU.QdwColVc.conventionnel;
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
acc +
|
|
69
|
+
pertesDistributionIndividuelleVolumeChauffee +
|
|
70
|
+
pertesDistributionCollectiveVolumeChauffee
|
|
71
|
+
);
|
|
72
|
+
}, 0);
|
|
73
|
+
|
|
74
|
+
return mois_liste.reduce((acc, mois) => {
|
|
75
|
+
return (
|
|
76
|
+
acc +
|
|
77
|
+
(0.48 *
|
|
78
|
+
pertesDistribution *
|
|
79
|
+
this.#tvStore.getData(
|
|
80
|
+
depensier ? 'nref21' : 'nref19',
|
|
81
|
+
ctx.altitude.value,
|
|
82
|
+
ctx.zoneClimatique.value,
|
|
83
|
+
mois,
|
|
84
|
+
ctx.inertie.ilpa
|
|
85
|
+
)) /
|
|
86
|
+
8760
|
|
87
|
+
);
|
|
88
|
+
}, 0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Pertes récupérées du stockage d’ECS pour le chauffage
|
|
93
|
+
* 9.1.1 Consommation de chauffage
|
|
94
|
+
*
|
|
95
|
+
* @param ctx {Contexte}
|
|
96
|
+
* @param logement {Logement}
|
|
97
|
+
* @returns {number}
|
|
98
|
+
*/
|
|
99
|
+
pertesStockageEcsRecup(ctx, logement) {
|
|
100
|
+
const installationsEcs = logement.installation_ecs_collection?.installation_ecs || [];
|
|
101
|
+
|
|
102
|
+
let pertesStockage = installationsEcs.reduce((acc, installation) => {
|
|
103
|
+
/** @type {InstallationEcsDU}*/
|
|
104
|
+
const installationEcsDU = installation.donnee_utilisateur;
|
|
105
|
+
|
|
106
|
+
return acc + (0.48 * installationEcsDU.QgwRecuperable) / 8760.0;
|
|
107
|
+
}, 0);
|
|
108
|
+
|
|
109
|
+
return mois_liste.reduce((acc, mois) => {
|
|
110
|
+
return (
|
|
111
|
+
acc +
|
|
112
|
+
pertesStockage *
|
|
113
|
+
this.#tvStore.getData(
|
|
114
|
+
'nref19',
|
|
115
|
+
ctx.altitude.value,
|
|
116
|
+
ctx.zoneClimatique.value,
|
|
117
|
+
mois,
|
|
118
|
+
ctx.inertie.ilpa
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
}, 0);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
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.36
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: 'Installation ECS en mode dépensier',
|
|
45
|
+
depensier: true,
|
|
46
|
+
expected: 0.72
|
|
47
|
+
}
|
|
48
|
+
])('Calcul des pertes de distribution récupérées pour $label', ({ depensier, expected }) => {
|
|
49
|
+
vi.spyOn(tvStore, 'getData').mockReturnValue(1.5);
|
|
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, 2);
|
|
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('Calcul des pertes de stockage récupérées pour une installation ECS', () => {
|
|
91
|
+
vi.spyOn(tvStore, 'getData').mockReturnValue(1.5);
|
|
92
|
+
|
|
93
|
+
/** @type {Contexte} */
|
|
94
|
+
const ctx = {
|
|
95
|
+
altitude: { value: '400-800m' },
|
|
96
|
+
zoneClimatique: { value: 'h1a' },
|
|
97
|
+
inertie: { ilpa: 1 }
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/** @type {Logement} */
|
|
101
|
+
const logement = {
|
|
102
|
+
installation_ecs_collection: {
|
|
103
|
+
installation_ecs: [
|
|
104
|
+
{ donnee_utilisateur: { QgwRecuperable: 120 } },
|
|
105
|
+
{ donnee_utilisateur: { QgwRecuperable: 150 } }
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
expect(service.pertesStockageEcsRecup(ctx, logement)).toBeCloseTo(0.27);
|
|
111
|
+
for (const mois of mois_liste) {
|
|
112
|
+
expect(tvStore.getData).toHaveBeenCalledWith('nref19', '400-800m', 'h1a', mois, 1);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("Test d'intégration des installations ECS", () => {
|
|
117
|
+
test.each(corpus)(
|
|
118
|
+
'vérification des pertes de distribution ecs recup des installations ECS pour dpe %s',
|
|
119
|
+
(ademeId) => {
|
|
120
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
121
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
122
|
+
|
|
123
|
+
/** @type {Contexte} */
|
|
124
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
125
|
+
|
|
126
|
+
installationEcsService.execute(ctx, dpeRequest.logement, {
|
|
127
|
+
besoin_ecs: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs,
|
|
128
|
+
besoin_ecs_depensier: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs_depensier
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const pertesStockage = service.execute(ctx, dpeRequest.logement);
|
|
132
|
+
|
|
133
|
+
expect_or(
|
|
134
|
+
() =>
|
|
135
|
+
expect(pertesStockage.pertes_distribution_ecs_recup).toBeCloseTo(
|
|
136
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_distribution_ecs_recup
|
|
137
|
+
),
|
|
138
|
+
() =>
|
|
139
|
+
expect(pertesStockage.pertes_distribution_ecs_recup * 1000).toBeCloseTo(
|
|
140
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_distribution_ecs_recup
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
test.each(corpus)(
|
|
146
|
+
'vérification des pertes de distribution ecs recup depensier des installations ECS pour dpe %s',
|
|
147
|
+
(ademeId) => {
|
|
148
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
149
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
150
|
+
|
|
151
|
+
/** @type {Contexte} */
|
|
152
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
153
|
+
|
|
154
|
+
installationEcsService.execute(ctx, dpeRequest.logement, {
|
|
155
|
+
besoin_ecs: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs,
|
|
156
|
+
besoin_ecs_depensier: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs_depensier
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const pertesStockage = service.execute(ctx, dpeRequest.logement);
|
|
160
|
+
|
|
161
|
+
expect_or(
|
|
162
|
+
() =>
|
|
163
|
+
expect(pertesStockage.pertes_distribution_ecs_recup_depensier).toBeCloseTo(
|
|
164
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_distribution_ecs_recup_depensier
|
|
165
|
+
),
|
|
166
|
+
() =>
|
|
167
|
+
expect(pertesStockage.pertes_distribution_ecs_recup_depensier * 1000).toBeCloseTo(
|
|
168
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_distribution_ecs_recup_depensier
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
test.each(corpus)(
|
|
174
|
+
'vérification des pertes de stockage ecs recup des installations ECS pour dpe %s',
|
|
175
|
+
(ademeId) => {
|
|
176
|
+
let dpeRequest = getAdemeFileJson(ademeId);
|
|
177
|
+
dpeRequest = normalizerService.normalize(dpeRequest);
|
|
178
|
+
|
|
179
|
+
/** @type {Contexte} */
|
|
180
|
+
const ctx = contexteBuilder.fromDpe(dpeRequest);
|
|
181
|
+
|
|
182
|
+
installationEcsService.execute(ctx, dpeRequest.logement, {
|
|
183
|
+
besoin_ecs: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs,
|
|
184
|
+
besoin_ecs_depensier: dpeRequest.logement.sortie.apport_et_besoin.besoin_ecs_depensier
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const pertesStockage = service.execute(ctx, dpeRequest.logement);
|
|
188
|
+
expect_or(
|
|
189
|
+
() =>
|
|
190
|
+
expect(pertesStockage.pertes_stockage_ecs_recup).toBeCloseTo(
|
|
191
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_stockage_ecs_recup
|
|
192
|
+
),
|
|
193
|
+
() =>
|
|
194
|
+
expect(pertesStockage.pertes_stockage_ecs_recup).toBeCloseTo(
|
|
195
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_stockage_ecs_recup * 1000
|
|
196
|
+
),
|
|
197
|
+
() =>
|
|
198
|
+
expect(pertesStockage.pertes_stockage_ecs_recup * 1000).toBeCloseTo(
|
|
199
|
+
dpeRequest.logement.sortie.apport_et_besoin.pertes_stockage_ecs_recup
|
|
200
|
+
)
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
});
|