@open3cl/engine 1.6.1 → 1.6.2
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/10_besoin_fr.spec.js +122 -0
- package/10_clim.spec.js +116 -0
- package/11_besoin_ecs.spec.js +69 -0
- package/11_ecs.spec.js +282 -0
- package/11_nadeq.spec.js +37 -0
- package/12.4_pac.spec.js +131 -0
- package/13.2_generateur_combustion.js +2 -0
- package/13.2_generateur_combustion.spec.js +537 -0
- package/13.2_generateur_combustion_bouilleur.js +4 -1
- package/13.2_generateur_combustion_bouilleur.spec.js +74 -0
- package/13.2_generateur_combustion_ch.spec.js +323 -0
- package/13.2_generateur_combustion_chaudiere.spec.js +85 -0
- package/13.2_generateur_pac.spec.js +64 -0
- package/13_rendement_distribution_ecs.spec.js +38 -0
- package/14_generateur_ecs.js +4 -4
- package/14_generateur_ecs.spec.js +745 -0
- package/15_conso_aux.spec.js +225 -0
- package/16.2_production_enr.spec.js +188 -0
- package/16_conso_eclairage.spec.js +62 -0
- package/2021_04_13_confort_ete.spec.js +159 -0
- package/2021_04_13_qualite_isolation.js +1 -1
- package/2021_04_13_qualite_isolation.spec.js +286 -0
- package/3.1_b.js +1 -1
- package/3.1_b.spec.js +367 -0
- package/3.2.1_mur.spec.js +812 -39
- package/3.2.2_plancher_bas.spec.js +643 -78
- package/3.2.3_plancher_haut.spec.js +452 -0
- package/3.3.1.4_porte.spec.js +114 -0
- package/3.3_baie_vitree.spec.js +449 -295
- package/3.4_pont_thermique.spec.js +890 -0
- package/3_deperdition.spec.js +397 -0
- package/4_ventilation.js +1 -1
- package/4_ventilation.spec.js +406 -0
- package/5_conso_ventilation.spec.js +186 -0
- package/6.2_surface_sud_equivalente.spec.js +138 -0
- package/8_intermittence.spec.js +33 -0
- package/9_besoin_ch.js +2 -2
- package/9_besoin_ch.spec.js +381 -0
- package/9_chauffage.js +4 -0
- package/9_chauffage.spec.js +478 -92
- package/9_conso_ch.spec.js +502 -248
- package/9_emetteur_ch.spec.js +339 -0
- package/9_generateur_ch.spec.js +467 -81
- package/apport_et_besoin.spec.js +126 -0
- package/conso.js +1 -1
- package/conso.spec.js +471 -82
- package/core/assets/domain/add-additionnal-ue-values-tables.spec.js +53 -0
- package/core/assets/domain/synchronize-solicitations-tables.spec.js +25 -0
- package/core/assets/domain/synchronize-valeur-tables.spec.js +48 -0
- package/core/conf/infrastructure/application.config.spec.js +35 -0
- package/core/file/infrastructure/adapter/csv-parser.store.spec.js +31 -13
- package/core/file/infrastructure/adapter/file.store.spec.js +148 -12
- package/core/tv/infrastructure/assets/additional-ue-values.spec.js +36 -0
- package/core/util/infrastructure/object-util.spec.js +20 -0
- package/core/util/logger/log-service.js +16 -7
- package/core/util/logger/log-service.spec.js +101 -0
- package/dpe-sanitizer.service.spec.js +138 -0
- package/engine.js +4 -1
- package/engine.spec.js +854 -0
- package/enums.spec.js +46 -0
- package/features/dpe/domain/models/type-generateur.model.js +1 -2
- package/features/dpe/domain/models/type-generateur.model.spec.js +25 -0
- package/features/dpe/domain/models/type-habitation.model.spec.js +41 -0
- package/features/dpe/domain/models/type-stockage.model.spec.js +18 -0
- package/features/dpe/domain/models/type-ventilation.model.spec.js +19 -0
- package/features/dpe/infrastructure/ch/chTv.store.spec.js +4 -4
- package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.js +14 -2
- package/features/dpe/infrastructure/enveloppe/baieVitreeTv.store.spec.js +16 -0
- package/features/dpe/infrastructure/tv.store.js +28 -9
- package/features/dpe/infrastructure/tv.store.spec.js +44 -0
- package/features/engine/domain/apport_et_besoin/apport-et-besoin.service.spec.js +2 -1
- package/features/engine/domain/apport_et_besoin/ch/besoin-ch.service.spec.js +18 -0
- package/features/engine/domain/apport_et_besoin/ch/perte-ch-recup.service.spec.js +33 -1
- package/features/engine/domain/apport_et_besoin/ecs/besoin-ecs.service.spec.js +21 -1
- package/features/engine/domain/apport_et_besoin/ecs/perte-ecs-recup.service.spec.js +41 -1
- package/features/engine/domain/apport_et_besoin/froid/besoin-froid.service.spec.js +44 -1
- package/features/engine/domain/apport_et_besoin/surface-sud-equivalente.service.spec.js +2 -1
- package/features/engine/domain/ch/generateur-ch.service.js +2 -0
- package/features/engine/domain/ch/generateur-ch.service.spec.js +117 -2
- package/features/engine/domain/ch/installation-ch.service.spec.js +14 -0
- package/features/engine/domain/conso/froid/conso-froid.service.spec.js +82 -1
- package/features/engine/domain/constants.spec.js +14 -0
- package/features/engine/domain/contexte.builder.spec.js +21 -0
- package/features/engine/domain/ecs/generateur-ecs.service.spec.js +11 -0
- package/features/engine/domain/ecs/installation-ecs.service.spec.js +55 -2
- package/features/engine/domain/engine.service.spec.js +227 -4
- package/features/engine/domain/enveloppe/baie_vitree/deperdition-baie-vitree.service.spec.js +124 -1
- package/features/engine/domain/enveloppe/deperdition-enveloppe.service.spec.js +270 -2
- package/features/engine/domain/enveloppe/deperdition.service.spec.js +141 -0
- package/features/engine/domain/enveloppe/espace_tampon/espace-tampon.service.spec.js +2 -1
- package/features/engine/domain/enveloppe/mur/deperdition-mur.service.spec.js +117 -1
- package/features/engine/domain/enveloppe/plancher_bas/deperdition-plancher-bas.service.spec.js +134 -1
- package/features/engine/domain/enveloppe/plancher_haut/deperdition-plancher-haut.service.spec.js +126 -1
- package/features/engine/domain/enveloppe/pont_thermique/deperdition-pont-thermique.service.spec.js +18 -1
- package/features/engine/domain/enveloppe/porte/deperdition-porte.service.spec.js +2 -1
- package/features/engine/domain/enveloppe/ventilation/deperdition-ventilation.service.spec.js +67 -1
- package/features/normalizer/domain/dpe-normalizer.service.js +1 -1
- package/features/normalizer/domain/dpe-normalizer.service.spec.js +53 -2
- package/ficheTechnique.spec.js +26 -0
- package/index.spec.js +54 -0
- package/package.json +5 -2
- package/preprocessor/index.js +20 -0
- package/preprocessor/index.spec.js +15 -0
- package/tv.spec.js +38 -0
- package/utils.js +43 -1
- package/utils.spec.js +518 -2
- package/output.js +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dépendances mockées :
|
|
5
|
+
* - `enums` : orientation et inclinaison des vitrages (détermine la clé de la table c1) ;
|
|
6
|
+
* - `tvs.c1` : coefficient d'ensoleillement mensuel par zone/orientation (valeurs contrôlées) ;
|
|
7
|
+
* - `mois_liste` : réduite à un mois pour rendre l'agrégation annuelle vérifiable.
|
|
8
|
+
*/
|
|
9
|
+
vi.mock('./enums.js', () => ({
|
|
10
|
+
default: {
|
|
11
|
+
orientation: { 1: 'sud', 2: 'horizontal' },
|
|
12
|
+
inclinaison_vitrage: { 3: 'verticale', 4: 'horizontal' }
|
|
13
|
+
}
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
vi.mock('./tv.js', () => ({
|
|
17
|
+
default: {
|
|
18
|
+
c1: {
|
|
19
|
+
h1a: {
|
|
20
|
+
Janvier: { 'sud verticale': 0.5, horizontal: 0.3 }
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock('./utils.js', () => ({
|
|
27
|
+
mois_liste: ['Janvier']
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
const { calc_sse_j, calc_sse } = await import('./6.2_surface_sud_equivalente.js');
|
|
31
|
+
|
|
32
|
+
/** Fabrique une baie vitrée avec les données d'entrée/intermédiaires utiles au calcul. */
|
|
33
|
+
function baie({ adjacence, surface, orientationId = '1', inclinaisonId = '3', sw = 1 }) {
|
|
34
|
+
return {
|
|
35
|
+
donnee_entree: {
|
|
36
|
+
enum_type_adjacence_id: adjacence,
|
|
37
|
+
enum_orientation_id: orientationId,
|
|
38
|
+
enum_inclinaison_vitrage_id: inclinaisonId,
|
|
39
|
+
surface_totale_baie: surface
|
|
40
|
+
},
|
|
41
|
+
donnee_intermediaire: { sw }
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 6.2 Calcul de la surface sud équivalente (SSE)
|
|
47
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §6.2 / §6.3
|
|
48
|
+
*/
|
|
49
|
+
describe('calc_sse_j - surface sud équivalente journalière', () => {
|
|
50
|
+
test("sans espace tampon : somme sur les baies donnant sur l'extérieur", () => {
|
|
51
|
+
// getSsd = surface * c1j * sw = 2 * 0.5 * 0.4
|
|
52
|
+
const bvList = [baie({ adjacence: '1', surface: 2, sw: 0.4 })];
|
|
53
|
+
expect(calc_sse_j(bvList, null, 'ca1', 'h1a', 'Janvier')).toBeCloseTo(0.4, 10);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("les baies non extérieures sont ignorées en l'absence d'espace tampon", () => {
|
|
57
|
+
const bvList = [
|
|
58
|
+
baie({ adjacence: '1', surface: 2, sw: 0.4 }),
|
|
59
|
+
baie({ adjacence: '3', surface: 5, sw: 0.9 }) // adjacence autre => ignorée
|
|
60
|
+
];
|
|
61
|
+
expect(calc_sse_j(bvList, null, 'ca1', 'h1a', 'Janvier')).toBeCloseTo(0.4, 10);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('une baie horizontale utilise la clé "horizontal" de la table c1', () => {
|
|
65
|
+
const bvList = [baie({ adjacence: '1', surface: 2, orientationId: '2', sw: 0.4 })];
|
|
66
|
+
// c1j = 0.3 => 2 * 0.3 * 0.4
|
|
67
|
+
expect(calc_sse_j(bvList, null, 'ca1', 'h1a', 'Janvier')).toBeCloseTo(0.24, 10);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('avec espace tampon solarisé (véranda) : apports directs + indirects', () => {
|
|
71
|
+
const bvList = [
|
|
72
|
+
baie({ adjacence: '10', surface: 3, sw: 0.5 }), // baie logement / véranda
|
|
73
|
+
baie({ adjacence: '1', surface: 2, sw: 0.4 }) // baie extérieure
|
|
74
|
+
];
|
|
75
|
+
const ets = {
|
|
76
|
+
donnee_intermediaire: { bver: 0.5, coef_transparence_ets: 0.8 },
|
|
77
|
+
baie_ets_collection: {
|
|
78
|
+
baie_ets: baie({ adjacence: '1', surface: 4, sw: 1 })
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Ssdj = T * (3*0.5*0.5) = 0.8 * 0.75 = 0.6
|
|
83
|
+
// Sstj = 4 * 0.5 * (0.8*0.8 + 0.024) = 2 * 0.664 = 1.328
|
|
84
|
+
// ssIndj = 1.328 - 0.6 = 0.728 ; SseVeranda = 0.6 + 0.728*0.5 = 0.964
|
|
85
|
+
// sseBaiesExt = 2*0.5*0.4 = 0.4 ; total = 1.364
|
|
86
|
+
expect(calc_sse_j(bvList, ets, 'ca1', 'h1a', 'Janvier')).toBeCloseTo(1.364, 10);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('un espace tampon fourni sous forme de tableau utilise le premier élément', () => {
|
|
90
|
+
const bvList = [baie({ adjacence: '10', surface: 3, sw: 0.5 })];
|
|
91
|
+
const ets = [
|
|
92
|
+
{
|
|
93
|
+
donnee_intermediaire: { bver: 0.5, coef_transparence_ets: 0.8 },
|
|
94
|
+
baie_ets_collection: { baie_ets: baie({ adjacence: '1', surface: 4, sw: 1 }) }
|
|
95
|
+
}
|
|
96
|
+
];
|
|
97
|
+
// pas de baie extérieure : total = SseVeranda = 0.964
|
|
98
|
+
expect(calc_sse_j(bvList, ets, 'ca1', 'h1a', 'Janvier')).toBeCloseTo(0.964, 10);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("baie d'espace tampon sans donnée intermédiaire ni inclinaison : valeurs par défaut", () => {
|
|
102
|
+
// La baie de l'ets n'a ni donnee_intermediaire (=> {} : fe1/fe2 valent 1) ni
|
|
103
|
+
// enum_inclinaison_vitrage_id (=> repli sur l'inclinaison verticale, id 3).
|
|
104
|
+
const ets = {
|
|
105
|
+
donnee_intermediaire: { bver: 0.5, coef_transparence_ets: 0.8 },
|
|
106
|
+
baie_ets_collection: {
|
|
107
|
+
baie_ets: {
|
|
108
|
+
donnee_entree: {
|
|
109
|
+
enum_type_adjacence_id: '1',
|
|
110
|
+
enum_orientation_id: '1',
|
|
111
|
+
surface_totale_baie: 4
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// orientation 'sud' + inclinaison par défaut 'verticale' => clé 'sud verticale' (c1j = 0.5)
|
|
118
|
+
// coeff = 0.8 * T + 0.024 = 0.664 ; Sstj = 4 * 0.5 * 0.664 = 1.328
|
|
119
|
+
// pas de baie véranda => Ssdj = 0 ; ssInd = 1.328 ; SseVeranda = 1.328 * bver = 0.664
|
|
120
|
+
expect(calc_sse_j([], ets, 'ca1', 'h1a', 'Janvier')).toBeCloseTo(0.664, 10);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('les facteurs fe1/fe2 de la baie extérieure sont appliqués au calcul', () => {
|
|
124
|
+
const bv = baie({ adjacence: '1', surface: 2, sw: 0.5 });
|
|
125
|
+
bv.donnee_intermediaire.fe1 = 0.5;
|
|
126
|
+
bv.donnee_intermediaire.fe2 = 0.8;
|
|
127
|
+
// getSsd = 2 * 0.5(c1) * 0.5(sw) * 0.5(fe1) * 0.8(fe2) = 0.2
|
|
128
|
+
expect(calc_sse_j([bv], null, 'ca1', 'h1a', 'Janvier')).toBeCloseTo(0.2, 10);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('calc_sse - surface sud équivalente annuelle', () => {
|
|
133
|
+
test('agrège la surface sud équivalente sur tous les mois', () => {
|
|
134
|
+
const bvList = [baie({ adjacence: '1', surface: 2, sw: 0.4 })];
|
|
135
|
+
// un seul mois mocké => identique au calcul journalier
|
|
136
|
+
expect(calc_sse('ca1', 'h1a', bvList, null)).toBeCloseTo(0.4, 10);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { calc_intermittence } from './8_intermittence.js';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 8. Modélisation de l'intermittence
|
|
6
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §8
|
|
7
|
+
*
|
|
8
|
+
* i = i0 / (1 + 0.1 * (G - 1)) avec G = GV / (Sh * Hsp)
|
|
9
|
+
* (fonction pure, aucune dépendance à mocker).
|
|
10
|
+
*/
|
|
11
|
+
describe("Calcul de l'intermittence (int)", () => {
|
|
12
|
+
test("G > 1 : le facteur d'intermittence est réduit sous i0", () => {
|
|
13
|
+
// GV=300, Sh=100, Hsp=2.5 => G = 1.2 => 0.9 / (1 + 0.1 * 0.2)
|
|
14
|
+
expect(calc_intermittence(300, 100, 2.5, 0.9)).toBeCloseTo(0.8823529411764706, 12);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("G = 1 : le facteur d'intermittence vaut exactement i0", () => {
|
|
18
|
+
// GV = Sh * Hsp => G = 1 => i0 / (1 + 0) = i0
|
|
19
|
+
expect(calc_intermittence(250, 100, 2.5, 0.85)).toBe(0.85);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("le facteur d'intermittence décroît quand G (donc GV) augmente", () => {
|
|
23
|
+
const faibleG = calc_intermittence(250, 100, 2.5, 0.9);
|
|
24
|
+
const fortG = calc_intermittence(500, 100, 2.5, 0.9);
|
|
25
|
+
expect(fortG).toBeLessThan(faibleG);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('le résultat est proportionnel à i0 pour un même G', () => {
|
|
29
|
+
const a = calc_intermittence(300, 100, 2.5, 0.9);
|
|
30
|
+
const b = calc_intermittence(300, 100, 2.5, 0.45);
|
|
31
|
+
expect(a).toBeCloseTo(2 * b, 12);
|
|
32
|
+
});
|
|
33
|
+
});
|
package/9_besoin_ch.js
CHANGED
|
@@ -239,7 +239,7 @@ function calc_qrec(instal_ecs, nadeq, prorataEcs, ilpa, ca, zc, th, nbLogements)
|
|
|
239
239
|
return { Qrec, Qrec_dep, sumNref19, sumNref21 };
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
function calc_Fj(GV, asj, aij, dhj, inertie) {
|
|
242
|
+
export function calc_Fj(GV, asj, aij, dhj, inertie) {
|
|
243
243
|
if (dhj === 0) return 0;
|
|
244
244
|
|
|
245
245
|
let alpha;
|
|
@@ -251,6 +251,6 @@ function calc_Fj(GV, asj, aij, dhj, inertie) {
|
|
|
251
251
|
return (Xj - Xj ** alpha) / (1 - Xj ** alpha);
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
function calc_bvj(GV, Fj) {
|
|
254
|
+
export function calc_bvj(GV, Fj) {
|
|
255
255
|
return GV * (1 - Fj);
|
|
256
256
|
}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dépendances mockées pour isoler `9_besoin_ch.js` :
|
|
5
|
+
* - `enums` : mapping altitude / zone climatique / inertie ;
|
|
6
|
+
* - `tvs` : profils mensuels dh19/dh21/nref19/nref21/e (valeurs contrôlées) ;
|
|
7
|
+
* - apports internes/solaires (`calc_ai_j`, `calc_as_j`) et surface sud
|
|
8
|
+
* équivalente (`calc_sse_j`) : simples fonctions dont on fige le retour ;
|
|
9
|
+
* - besoin ECS journalier (`calc_besoin_ecs_j`) et récupération générateur
|
|
10
|
+
* (`calc_Qrec_gen_j`) : mockés pour piloter les branches de récupération ;
|
|
11
|
+
* - `mois_liste` réduite à un mois et `bug_for_bug_compat` désactivé.
|
|
12
|
+
*/
|
|
13
|
+
vi.mock('./enums.js', () => ({
|
|
14
|
+
default: {
|
|
15
|
+
classe_altitude: { 0: 'ca1' },
|
|
16
|
+
zone_climatique: { 0: 'h1a' },
|
|
17
|
+
classe_inertie: { 0: 'moyenne' }
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
vi.mock('./tv.js', () => ({
|
|
22
|
+
default: {
|
|
23
|
+
dh19: { 0: { ca1: { Janvier: { h1a: 1000 } } } },
|
|
24
|
+
dh21: { 0: { ca1: { Janvier: { h1a: 1200 } } } },
|
|
25
|
+
nref19: { 0: { ca1: { Janvier: { h1a: 100 } } } },
|
|
26
|
+
nref21: { 0: { ca1: { Janvier: { h1a: 120 } } } },
|
|
27
|
+
e: { 0: { ca1: { Janvier: { h1a: 500 } } } }
|
|
28
|
+
}
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
vi.mock('./6.1_apport_gratuit.js', () => ({
|
|
32
|
+
calc_ai_j: vi.fn(),
|
|
33
|
+
calc_as_j: vi.fn()
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
vi.mock('./6.2_surface_sud_equivalente.js', () => ({
|
|
37
|
+
calc_sse_j: vi.fn()
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
vi.mock('./11_besoin_ecs.js', () => ({
|
|
41
|
+
calc_besoin_ecs_j: vi.fn()
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
vi.mock('./9_generateur_ch.js', () => ({
|
|
45
|
+
calc_Qrec_gen_j: vi.fn()
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
const utilState = vi.hoisted(() => ({ bug: false }));
|
|
49
|
+
vi.mock('./utils.js', () => ({
|
|
50
|
+
mois_liste: ['Janvier'],
|
|
51
|
+
get bug_for_bug_compat() {
|
|
52
|
+
return utilState.bug;
|
|
53
|
+
}
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
const { default: calc_besoin_ch, calc_Fj, calc_bvj } = await import('./9_besoin_ch.js');
|
|
57
|
+
const { default: tvsBch } = await import('./tv.js');
|
|
58
|
+
const { calc_ai_j, calc_as_j } = await import('./6.1_apport_gratuit.js');
|
|
59
|
+
const { calc_sse_j } = await import('./6.2_surface_sud_equivalente.js');
|
|
60
|
+
const { calc_besoin_ecs_j } = await import('./11_besoin_ecs.js');
|
|
61
|
+
const { calc_Qrec_gen_j } = await import('./9_generateur_ch.js');
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 9. Besoins de chauffage (Bch)
|
|
65
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §9
|
|
66
|
+
*/
|
|
67
|
+
describe('calc_Fj - fraction des besoins de chauffage couverte par les apports', () => {
|
|
68
|
+
test('retourne 0 lorsque le nombre de degrés-heures est nul', () => {
|
|
69
|
+
expect(calc_Fj(100, 2000, 5000, 0, 'moyenne')).toBe(0);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('valeur de référence de régression (inertie moyenne, alpha = 2.9)', () => {
|
|
73
|
+
expect(calc_Fj(100, 2000, 5000, 1000, 'moyenne')).toBeCloseTo(0.06958364704535852, 9);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('une inertie lourde (alpha = 3.6) couvre une part différente de la légère (alpha = 2.5)', () => {
|
|
77
|
+
const lourde = calc_Fj(100, 2000, 5000, 1000, 'lourde');
|
|
78
|
+
const legere = calc_Fj(100, 2000, 5000, 1000, 'légère');
|
|
79
|
+
// valeurs de référence de régression
|
|
80
|
+
expect(lourde).toBeCloseTo(0.06993530568739618, 9);
|
|
81
|
+
expect(legere).toBeCloseTo(0.06879276604754976, 9);
|
|
82
|
+
expect(lourde).not.toBeCloseTo(legere, 9);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('les inerties "très lourde" et "lourde" partagent le même alpha', () => {
|
|
86
|
+
expect(calc_Fj(100, 2000, 5000, 1000, 'très lourde')).toBeCloseTo(
|
|
87
|
+
calc_Fj(100, 2000, 5000, 1000, 'lourde'),
|
|
88
|
+
12
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('calc_bvj - déperditions corrigées de la fraction récupérée', () => {
|
|
94
|
+
test('bvj = GV * (1 - Fj)', () => {
|
|
95
|
+
expect(calc_bvj(100, 0.3)).toBe(70);
|
|
96
|
+
expect(calc_bvj(100, 0)).toBe(100);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('calc_besoin_ch - agrégation mensuelle du besoin de chauffage', () => {
|
|
101
|
+
beforeEach(() => {
|
|
102
|
+
vi.mocked(calc_ai_j).mockReset();
|
|
103
|
+
vi.mocked(calc_as_j).mockReset();
|
|
104
|
+
vi.mocked(calc_sse_j).mockReset();
|
|
105
|
+
vi.mocked(calc_besoin_ecs_j).mockReset();
|
|
106
|
+
vi.mocked(calc_Qrec_gen_j).mockReset();
|
|
107
|
+
vi.mocked(calc_ai_j).mockReturnValue(5000);
|
|
108
|
+
vi.mocked(calc_as_j).mockReturnValue(2000);
|
|
109
|
+
vi.mocked(calc_sse_j).mockReturnValue(10);
|
|
110
|
+
vi.mocked(calc_besoin_ecs_j).mockReturnValue(10);
|
|
111
|
+
vi.mocked(calc_Qrec_gen_j).mockReturnValue(0);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
/** Appel nominal sans installation (pas de récupération d'énergie). */
|
|
115
|
+
function appelSansRecup() {
|
|
116
|
+
return calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, [], [], [], null, 'maison', 1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
test('valeurs de référence de régression sans récupération (ECS/générateur)', () => {
|
|
120
|
+
const ret = appelSansRecup();
|
|
121
|
+
expect(ret.besoin_ch).toBeCloseTo(93.04163529546415, 9);
|
|
122
|
+
expect(ret.besoin_ch_depensier).toBeCloseTo(113.0298092944794, 9);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('la fraction des apports gratuits est ramenée à la moyenne pondérée par les degrés-heures', () => {
|
|
126
|
+
const ret = appelSansRecup();
|
|
127
|
+
// sur un seul mois : fraction = Fj
|
|
128
|
+
expect(ret.fraction_apport_gratuit_ch).toBeCloseTo(0.06958364704535852, 9);
|
|
129
|
+
expect(ret.fraction_apport_gratuit_depensier_ch).toBeCloseTo(0.05808492254600494, 9);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('sans récupération, toutes les pertes récupérées retournées sont nulles', () => {
|
|
133
|
+
const ret = appelSansRecup();
|
|
134
|
+
expect(ret.pertes_distribution_ecs_recup).toBe(0);
|
|
135
|
+
expect(ret.pertes_stockage_ecs_recup).toBe(0);
|
|
136
|
+
expect(ret.pertes_generateur_ch_recup).toBe(0);
|
|
137
|
+
expect(ret.pertes_generateur_ch_recup_depensier).toBe(0);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('le besoin mensuel est exposé (en Wh) pour chaque mois calculé', () => {
|
|
141
|
+
const ret = appelSansRecup();
|
|
142
|
+
expect(ret.besoin_ch_mois.Janvier).toBeCloseTo(93041.63529546415, 6);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 9.1.1 - Un générateur en volume chauffé avec pertes à l'arrêt (qp0) donne
|
|
147
|
+
* lieu à une récupération d'énergie qui diminue le besoin de chauffage.
|
|
148
|
+
*/
|
|
149
|
+
test('la récupération générateur diminue le besoin et appelle calc_Qrec_gen_j', () => {
|
|
150
|
+
vi.mocked(calc_Qrec_gen_j).mockReturnValue(1000);
|
|
151
|
+
const instal_ch = [
|
|
152
|
+
{
|
|
153
|
+
generateur_chauffage_collection: {
|
|
154
|
+
generateur_chauffage: [
|
|
155
|
+
{
|
|
156
|
+
donnee_intermediaire: { qp0: 1 },
|
|
157
|
+
donnee_entree: { position_volume_chauffe: 1 }
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
];
|
|
163
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, [], instal_ch, [], null, 'maison', 1);
|
|
164
|
+
|
|
165
|
+
expect(calc_Qrec_gen_j).toHaveBeenCalled();
|
|
166
|
+
expect(ret.pertes_generateur_ch_recup).toBeCloseTo(1000, 6);
|
|
167
|
+
// 93041.635 Wh de besoin - 1000 Wh récupérés = 92041.635 Wh
|
|
168
|
+
expect(ret.besoin_ch).toBeCloseTo(92.04163529546415, 6);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("un générateur sans pertes à l'arrêt (qp0) n'est pas retenu pour la récupération", () => {
|
|
172
|
+
vi.mocked(calc_Qrec_gen_j).mockReturnValue(1000);
|
|
173
|
+
const instal_ch = [
|
|
174
|
+
{
|
|
175
|
+
generateur_chauffage_collection: {
|
|
176
|
+
generateur_chauffage: [
|
|
177
|
+
{
|
|
178
|
+
donnee_intermediaire: { qp0: 0 },
|
|
179
|
+
donnee_entree: { position_volume_chauffe: 1 }
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
];
|
|
185
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, [], instal_ch, [], null, 'maison', 1);
|
|
186
|
+
expect(calc_Qrec_gen_j).not.toHaveBeenCalled();
|
|
187
|
+
expect(ret.pertes_generateur_ch_recup).toBe(0);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('le besoin mensuel de chauffage est borné à zéro (Math.max)', () => {
|
|
191
|
+
// récupération générateur démesurée => besoin mensuel négatif ramené à 0
|
|
192
|
+
vi.mocked(calc_Qrec_gen_j).mockReturnValue(1e9);
|
|
193
|
+
const instal_ch = [
|
|
194
|
+
{
|
|
195
|
+
generateur_chauffage_collection: {
|
|
196
|
+
generateur_chauffage: [
|
|
197
|
+
{
|
|
198
|
+
donnee_intermediaire: { qp0: 1 },
|
|
199
|
+
donnee_entree: { position_volume_chauffe: 1 }
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, [], instal_ch, [], null, 'maison', 1);
|
|
206
|
+
expect(ret.besoin_ch).toBe(0);
|
|
207
|
+
expect(ret.besoin_ch_mois.Janvier).toBe(0);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 11.4 - En présence d'une installation ECS, la récupération de distribution
|
|
212
|
+
* ECS (Qrec) diminue le besoin de chauffage et le besoin ECS journalier est
|
|
213
|
+
* sollicité pour chaque mois.
|
|
214
|
+
*/
|
|
215
|
+
test('une installation ECS collective alimente la récupération de distribution', () => {
|
|
216
|
+
const instal_ecs = [
|
|
217
|
+
{
|
|
218
|
+
donnee_entree: { enum_type_installation_id: '2', rdim: 1 },
|
|
219
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
220
|
+
}
|
|
221
|
+
];
|
|
222
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, instal_ecs, [], [], null, 'maison', 1);
|
|
223
|
+
|
|
224
|
+
expect(calc_besoin_ecs_j).toHaveBeenCalled();
|
|
225
|
+
expect(ret.pertes_distribution_ecs_recup).toBeGreaterThan(0);
|
|
226
|
+
// la récupération ECS réduit le besoin sous la valeur sans récupération
|
|
227
|
+
expect(ret.besoin_ch).toBeLessThan(93.04163529546415);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Branches complémentaires : récupération de stockage ECS, cas immeuble,
|
|
233
|
+
* degrés-heures nuls et compatibilité "bug for bug".
|
|
234
|
+
*/
|
|
235
|
+
describe('calc_besoin_ch - branches complémentaires', () => {
|
|
236
|
+
beforeEach(() => {
|
|
237
|
+
utilState.bug = false;
|
|
238
|
+
vi.mocked(calc_ai_j).mockReset().mockReturnValue(5000);
|
|
239
|
+
vi.mocked(calc_as_j).mockReset().mockReturnValue(2000);
|
|
240
|
+
vi.mocked(calc_sse_j).mockReset().mockReturnValue(10);
|
|
241
|
+
vi.mocked(calc_besoin_ecs_j).mockReset().mockReturnValue(10);
|
|
242
|
+
vi.mocked(calc_Qrec_gen_j).mockReset().mockReturnValue(0);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* 11.4 - Plusieurs systèmes d'ECS : le besoin ECS est proratisé (1 / n).
|
|
247
|
+
*/
|
|
248
|
+
test('plusieurs installations ECS : prorata appliqué au besoin ECS', () => {
|
|
249
|
+
const instal_ecs = [
|
|
250
|
+
{
|
|
251
|
+
donnee_entree: { enum_type_installation_id: '2', rdim: 1 },
|
|
252
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
donnee_entree: { enum_type_installation_id: '2', rdim: 1 },
|
|
256
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
257
|
+
}
|
|
258
|
+
];
|
|
259
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, instal_ecs, [], [], null, 'maison', 1);
|
|
260
|
+
expect(ret.pertes_distribution_ecs_recup).toBeGreaterThan(0);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 17.2.1.1 - Installation ECS simple en volume chauffé : récupération de
|
|
265
|
+
* stockage (Qgw) prise en compte, sauf ballon/générateur hors volume chauffé.
|
|
266
|
+
*/
|
|
267
|
+
test('installation ECS simple : récupération de stockage selon la position du ballon', () => {
|
|
268
|
+
const instal_ecs = [
|
|
269
|
+
{
|
|
270
|
+
donnee_entree: { enum_type_installation_id: '1' }, // rdim absent => repli sur 1
|
|
271
|
+
generateur_ecs_collection: {
|
|
272
|
+
generateur_ecs: [
|
|
273
|
+
{
|
|
274
|
+
// en volume chauffé => récupération comptée
|
|
275
|
+
donnee_entree: { position_volume_chauffe_stockage: 1, position_volume_chauffe: 1 },
|
|
276
|
+
donnee_intermediaire: { Qgw: 100 }
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
// stockage hors volume chauffé => ignoré
|
|
280
|
+
donnee_entree: { position_volume_chauffe_stockage: 0, position_volume_chauffe: 1 },
|
|
281
|
+
donnee_intermediaire: { Qgw: 100 }
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
// générateur hors volume chauffé => ignoré
|
|
285
|
+
donnee_entree: { position_volume_chauffe_stockage: 1, position_volume_chauffe: 0 },
|
|
286
|
+
donnee_intermediaire: { Qgw: 100 }
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
// en volume chauffé mais sans Qgw => contribution nulle (Qgw || 0)
|
|
290
|
+
donnee_entree: { position_volume_chauffe_stockage: 1, position_volume_chauffe: 1 },
|
|
291
|
+
donnee_intermediaire: {}
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
];
|
|
297
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, instal_ecs, [], [], null, 'maison', 1);
|
|
298
|
+
// un seul ballon récupérable (Qgw = 100) => pertes de stockage strictement positives
|
|
299
|
+
expect(ret.pertes_stockage_ecs_recup).toBeGreaterThan(0);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* 9.1.1 - Un générateur sans position en volume chauffé renseignée (?? 0)
|
|
304
|
+
* n'est pas retenu pour la récupération de génération.
|
|
305
|
+
*/
|
|
306
|
+
test("générateur sans position en volume chauffé renseignée n'est pas récupéré", () => {
|
|
307
|
+
vi.mocked(calc_Qrec_gen_j).mockReturnValue(1000);
|
|
308
|
+
const instal_ch = [
|
|
309
|
+
{
|
|
310
|
+
generateur_chauffage_collection: {
|
|
311
|
+
generateur_chauffage: [
|
|
312
|
+
{
|
|
313
|
+
donnee_intermediaire: { qp0: 1 },
|
|
314
|
+
donnee_entree: {} // position_volume_chauffe indéfinie => ?? 0 => exclu
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
];
|
|
320
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, [], instal_ch, [], null, 'maison', 1);
|
|
321
|
+
expect(calc_Qrec_gen_j).not.toHaveBeenCalled();
|
|
322
|
+
expect(ret.pertes_generateur_ch_recup).toBe(0);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Immeuble : la récupération ECS est mutualisée (prorata rdim / nombre de
|
|
327
|
+
* logements) et le besoin ECS est agrégé avant application du facteur Qrec.
|
|
328
|
+
*/
|
|
329
|
+
test('cas immeuble : mutualisation de la récupération ECS (rdim / nbLogements)', () => {
|
|
330
|
+
const instal_ecs = [
|
|
331
|
+
{
|
|
332
|
+
donnee_entree: { enum_type_installation_id: '1', rdim: 2 },
|
|
333
|
+
generateur_ecs_collection: { generateur_ecs: [] }
|
|
334
|
+
}
|
|
335
|
+
];
|
|
336
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, instal_ecs, [], [], null, 'immeuble', 4);
|
|
337
|
+
expect(ret.pertes_distribution_ecs_recup).toBeGreaterThan(0);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Un mois sans degrés-heures (dh19/dh21 = 0) donne des déperditions bvj nulles.
|
|
342
|
+
*/
|
|
343
|
+
test('degrés-heures mensuels nuls : besoin nul sur le mois', () => {
|
|
344
|
+
const dh19 = tvsBch.dh19[0].ca1.Janvier.h1a;
|
|
345
|
+
const dh21 = tvsBch.dh21[0].ca1.Janvier.h1a;
|
|
346
|
+
tvsBch.dh19[0].ca1.Janvier.h1a = 0;
|
|
347
|
+
tvsBch.dh21[0].ca1.Janvier.h1a = 0;
|
|
348
|
+
try {
|
|
349
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, [], [], [], null, 'maison', 1);
|
|
350
|
+
expect(ret.besoin_ch).toBe(0);
|
|
351
|
+
expect(ret.besoin_ch_depensier).toBe(0);
|
|
352
|
+
} finally {
|
|
353
|
+
tvsBch.dh19[0].ca1.Janvier.h1a = dh19;
|
|
354
|
+
tvsBch.dh21[0].ca1.Janvier.h1a = dh21;
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* En mode "bug for bug", la récupération de génération est divisée par 1000
|
|
360
|
+
* (reproduction d'une anomalie historique).
|
|
361
|
+
*/
|
|
362
|
+
test('compatibilité "bug for bug" : récupération de génération divisée par 1000', () => {
|
|
363
|
+
utilState.bug = true;
|
|
364
|
+
vi.mocked(calc_Qrec_gen_j).mockReturnValue(1000);
|
|
365
|
+
const instal_ch = [
|
|
366
|
+
{
|
|
367
|
+
generateur_chauffage_collection: {
|
|
368
|
+
generateur_chauffage: [
|
|
369
|
+
{
|
|
370
|
+
donnee_intermediaire: { qp0: 1 },
|
|
371
|
+
donnee_entree: { position_volume_chauffe: 1 }
|
|
372
|
+
}
|
|
373
|
+
]
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
];
|
|
377
|
+
const ret = calc_besoin_ch(0, 0, 0, 0, 100, 100, 3, [], instal_ch, [], null, 'maison', 1);
|
|
378
|
+
// 1000 / 1000 = 1 Wh récupéré sur le seul mois
|
|
379
|
+
expect(ret.pertes_generateur_ch_recup).toBeCloseTo(1, 9);
|
|
380
|
+
});
|
|
381
|
+
});
|
package/9_chauffage.js
CHANGED
|
@@ -139,6 +139,8 @@ export default function calc_chauffage(
|
|
|
139
139
|
Pnominal,
|
|
140
140
|
zc
|
|
141
141
|
);
|
|
142
|
+
/* c8 ignore next 2 -- repli défensif inatteignable : donnee_utilisateur est toujours renseignée
|
|
143
|
+
par la première boucle sur gen_ch ci-dessus */
|
|
142
144
|
(gen.donnee_utilisateur = gen.donnee_utilisateur || {}).nbGenerateurCascade = gen_ch.length;
|
|
143
145
|
|
|
144
146
|
calc_generateur_ch(
|
|
@@ -293,6 +295,8 @@ export function tauxChargeForGenerator(installationChauffage, GV, caId, zcId, th
|
|
|
293
295
|
);
|
|
294
296
|
|
|
295
297
|
installChauffageWithCombustion.forEach((installCh) => {
|
|
298
|
+
/* c8 ignore next 2 -- repli défensif inatteignable : genCombustion est toujours renseignée sur les
|
|
299
|
+
installations ajoutées à installChauffageWithCombustion */
|
|
296
300
|
(installCh.donnee_utilisateur.genCombustion || []).forEach((gen) => {
|
|
297
301
|
const GV_ratio = th === 'immeuble' ? GV * (1 / (installCh.donnee_entree.rdim || 1)) : GV;
|
|
298
302
|
gen.donnee_utilisateur.cdimref = Pn / (GV_ratio * (19 - tbase));
|