@open3cl/engine 1.7.3 → 1.8.0
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/11_besoin_ecs.js +5 -2
- package/11_ecs.js +99 -2
- package/11_ecs.spec.js +139 -1
- package/README.md +28 -43
- package/conso.js +11 -7
- package/engine.js +3 -2
- package/package.json +3 -2
package/11_besoin_ecs.js
CHANGED
|
@@ -24,11 +24,14 @@ export function calc_besoin_ecs_j(ca, mois, zc, nadeq, depensier) {
|
|
|
24
24
|
export default function calc_besoin_ecs(ca, zc, nadeq) {
|
|
25
25
|
const ret = {
|
|
26
26
|
besoin_ecs: 0,
|
|
27
|
-
besoin_ecs_depensier: 0
|
|
27
|
+
besoin_ecs_depensier: 0,
|
|
28
|
+
besoin_ecs_par_mois: {}
|
|
28
29
|
};
|
|
29
30
|
for (const mois of mois_liste) {
|
|
30
|
-
|
|
31
|
+
const becs_j = calc_besoin_ecs_j(ca, mois, zc, nadeq, false);
|
|
32
|
+
ret.besoin_ecs += becs_j;
|
|
31
33
|
ret.besoin_ecs_depensier += calc_besoin_ecs_j(ca, mois, zc, nadeq, true);
|
|
34
|
+
ret.besoin_ecs_par_mois[mois] = becs_j;
|
|
32
35
|
}
|
|
33
36
|
return ret;
|
|
34
37
|
}
|
package/11_ecs.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import enums from './enums.js';
|
|
2
2
|
import calc_gen_ecs from './14_generateur_ecs.js';
|
|
3
|
-
import { tv, requestInput } from './utils.js';
|
|
3
|
+
import { tv, requestInput, mois_liste, Njj } from './utils.js';
|
|
4
4
|
|
|
5
5
|
function tv_rendement_distribution_ecs(di, de, du, pvc) {
|
|
6
6
|
let matcher = {};
|
|
@@ -49,6 +49,89 @@ function tv_rendement_distribution_ecs(di, de, du, pvc) {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Calcul de la consommation des auxiliaires de distribution ECS - CAS IMMEUBLE
|
|
54
|
+
* Calcul installation par installation pour les réseaux collectifs bouclés ou avec traçage.
|
|
55
|
+
*
|
|
56
|
+
* @param {object} de - donnee_entree de l'installation ECS
|
|
57
|
+
* @param {number} surfaceImmeuble - surface habitable totale de l'immeuble (m²)
|
|
58
|
+
* @param {object} becsParMois - besoin ECS mensuel de l'immeuble {Janvier: X, Février: X, ...} [kWh]
|
|
59
|
+
* @returns {number} conso_auxiliaire_distribution_ecs_installation [Wh]
|
|
60
|
+
*/
|
|
61
|
+
function calc_auxiliaire_distribution_ecs_immeuble(de, surfaceImmeuble, becsParMois) {
|
|
62
|
+
const type_installation = enums.type_installation[de.enum_type_installation_id];
|
|
63
|
+
|
|
64
|
+
// Seulement pour les installations collectives
|
|
65
|
+
if (!type_installation || !type_installation.includes('collective')) {
|
|
66
|
+
return 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const enum_bouclage_id = String(de.enum_bouclage_reseau_ecs_id);
|
|
70
|
+
const surface_installation = de.surface_habitable || 0;
|
|
71
|
+
const surface_immeuble_total = surfaceImmeuble || 1;
|
|
72
|
+
const ratio_surface = surface_installation / surface_immeuble_total;
|
|
73
|
+
|
|
74
|
+
// CAS 1 : réseau non bouclé → 0
|
|
75
|
+
if (enum_bouclage_id === '1') {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// CAS 3 : traçage → 0.14 × BECS_annuel × ratio_surface [Wh]
|
|
80
|
+
if (enum_bouclage_id === '3') {
|
|
81
|
+
const becs_annuel_kwh = Object.values(becsParMois).reduce((acc, v) => acc + v, 0);
|
|
82
|
+
// becs_annuel est en kWh → × 1000 pour obtenir des Wh
|
|
83
|
+
return 0.14 * becs_annuel_kwh * 1000 * ratio_surface;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// CAS 2 : réseau bouclé → calcul complet 9 étapes [Wh]
|
|
87
|
+
if (enum_bouclage_id === '2') {
|
|
88
|
+
const Sh_inst = surface_installation;
|
|
89
|
+
const Niv_inst = Number(de.nombre_niveau_installation_ecs) || 1;
|
|
90
|
+
|
|
91
|
+
// Étape 3 : Calcul de Lb pour l'installation i
|
|
92
|
+
// Lb = 1.2 × (1.1 × Sh_inst/nombre_niveaux + 4 × nombre_niveaux)
|
|
93
|
+
const Lb = 1.2 * (1.1 * (Sh_inst / Niv_inst) + 4 * Niv_inst);
|
|
94
|
+
|
|
95
|
+
// Étape 4 : DeltaPb = 0.2 × Lb + 10
|
|
96
|
+
const DeltaPb = 0.2 * Lb + 10;
|
|
97
|
+
|
|
98
|
+
let Qcirc_annuel = 0;
|
|
99
|
+
|
|
100
|
+
for (const mois of mois_liste) {
|
|
101
|
+
const Njj_mois = Njj[mois];
|
|
102
|
+
const becs_immeuble_j_kwh = becsParMois[mois] || 0;
|
|
103
|
+
|
|
104
|
+
// Étape 1 : Qdwj_i [kWh]
|
|
105
|
+
const Qdwj_i = 0.24 * becs_immeuble_j_kwh * ratio_surface;
|
|
106
|
+
|
|
107
|
+
// Étape 2 : qdwj_i [m³/h]
|
|
108
|
+
// Note: la spec officielle utilise Qdwj en Wh et divise par 1000 ensuite.
|
|
109
|
+
// Le moteur fournit becs en kWh, donc Qdwj est en kWh → on ne divise pas par 1000.
|
|
110
|
+
const qdwj_i = Qdwj_i / (5.815 * 5 * Njj_mois);
|
|
111
|
+
|
|
112
|
+
// Étape 5 : Phyd,j [W]
|
|
113
|
+
// Phyd,j = DeltaPb [kPa] × 1000 × qdwj_i [m³/h] / 3600
|
|
114
|
+
const Phyd_j = (DeltaPb * 1000 * qdwj_i) / 3600;
|
|
115
|
+
|
|
116
|
+
// Étape 6 : Effcirb,j (rendement pompe)
|
|
117
|
+
const Effcirb_j = Phyd_j > 0 ? 0.035 * Math.pow(Phyd_j, 0.45) : 0;
|
|
118
|
+
|
|
119
|
+
// Étape 7 : Pcirb,j [W] = max(Phyd,j / Effcirb,j, 20)
|
|
120
|
+
const Pcirb_j = Effcirb_j > 0 ? Math.max(Phyd_j / Effcirb_j, 20) : 20;
|
|
121
|
+
|
|
122
|
+
// Étape 8 : Qcirb,j [Wh]
|
|
123
|
+
const Qcirb_j = (5 * Pcirb_j + 19 * 20) * Njj_mois;
|
|
124
|
+
|
|
125
|
+
Qcirc_annuel += Qcirb_j;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Étape 9 : conso annuelle en Wh
|
|
129
|
+
return Qcirc_annuel;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
52
135
|
export default function calc_ecs(
|
|
53
136
|
dpe,
|
|
54
137
|
ecs,
|
|
@@ -61,7 +144,8 @@ export default function calc_ecs(
|
|
|
61
144
|
virtualisationECS,
|
|
62
145
|
surfaceImmeuble,
|
|
63
146
|
nombreAppartements,
|
|
64
|
-
isImmeubleSystemEcsIndividuels
|
|
147
|
+
isImmeubleSystemEcsIndividuels,
|
|
148
|
+
becsParMois
|
|
65
149
|
) {
|
|
66
150
|
const de = ecs.donnee_entree;
|
|
67
151
|
const di = {};
|
|
@@ -101,6 +185,19 @@ export default function calc_ecs(
|
|
|
101
185
|
0
|
|
102
186
|
);
|
|
103
187
|
|
|
188
|
+
// Calcul de la consommation des auxiliaires de distribution ECS pour l'immeuble
|
|
189
|
+
// Résultat en Wh, converti en kWh pour cohérence avec le reste du moteur
|
|
190
|
+
if (th === 'immeuble' && becsParMois && Object.keys(becsParMois).length > 0) {
|
|
191
|
+
const conso_aux_wh = calc_auxiliaire_distribution_ecs_immeuble(
|
|
192
|
+
de,
|
|
193
|
+
surfaceImmeuble,
|
|
194
|
+
becsParMois
|
|
195
|
+
);
|
|
196
|
+
di.conso_auxiliaire_distribution_ecs = conso_aux_wh / 1000;
|
|
197
|
+
} else {
|
|
198
|
+
di.conso_auxiliaire_distribution_ecs = 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
104
201
|
ecs.donnee_intermediaire = di;
|
|
105
202
|
ecs.donnee_utilisateur = du;
|
|
106
203
|
}
|
package/11_ecs.spec.js
CHANGED
|
@@ -21,7 +21,10 @@ vi.mock('./enums.js', () => ({
|
|
|
21
21
|
vi.mock('./utils.js', () => ({
|
|
22
22
|
set_bug_for_bug_compat: vi.fn(),
|
|
23
23
|
tv: vi.fn(),
|
|
24
|
-
requestInput: vi.fn()
|
|
24
|
+
requestInput: vi.fn(),
|
|
25
|
+
// Calendrier réduit à deux mois pour le calcul mensuel des auxiliaires de distribution
|
|
26
|
+
mois_liste: ['Janvier', 'Février'],
|
|
27
|
+
Njj: { Janvier: 31, Février: 28 }
|
|
25
28
|
}));
|
|
26
29
|
|
|
27
30
|
vi.mock('./14_generateur_ecs.js', () => ({
|
|
@@ -281,3 +284,138 @@ describe('calc_ecs - agrégation des consommations', () => {
|
|
|
281
284
|
expect(ecs.donnee_intermediaire.conso_ecs_depensier).toBe(28);
|
|
282
285
|
});
|
|
283
286
|
});
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Consommation des auxiliaires de distribution ECS - cas immeuble
|
|
290
|
+
* Seuls les réseaux collectifs bouclés ou avec traçage consomment de l'énergie.
|
|
291
|
+
*/
|
|
292
|
+
describe('calc_ecs - auxiliaires de distribution ECS (immeuble)', () => {
|
|
293
|
+
/** Calcule l'ECS d'un immeuble et retourne la conso des auxiliaires de distribution [kWh]. */
|
|
294
|
+
function consoAuxDistribution(deExtra, becsParMois, surfaceImmeuble = 400, th = 'immeuble') {
|
|
295
|
+
const ecs = makeEcs({ tv_rendement_distribution_ecs_id: '5', ...deExtra });
|
|
296
|
+
calc_ecs(
|
|
297
|
+
{},
|
|
298
|
+
ecs,
|
|
299
|
+
100,
|
|
300
|
+
200,
|
|
301
|
+
1,
|
|
302
|
+
'ca1',
|
|
303
|
+
'h1a',
|
|
304
|
+
th,
|
|
305
|
+
false,
|
|
306
|
+
surfaceImmeuble,
|
|
307
|
+
null,
|
|
308
|
+
false,
|
|
309
|
+
becsParMois
|
|
310
|
+
);
|
|
311
|
+
return ecs.donnee_intermediaire.conso_auxiliaire_distribution_ecs;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
test.each([
|
|
315
|
+
['logement autre qu’un immeuble', 'maison', { Janvier: 1000 }],
|
|
316
|
+
['besoin mensuel absent', 'immeuble', undefined],
|
|
317
|
+
['besoin mensuel vide', 'immeuble', {}]
|
|
318
|
+
])('%s : pas de consommation', (_libelle, th, becsParMois) => {
|
|
319
|
+
expect(
|
|
320
|
+
consoAuxDistribution(
|
|
321
|
+
{ enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '2' },
|
|
322
|
+
becsParMois,
|
|
323
|
+
400,
|
|
324
|
+
th
|
|
325
|
+
)
|
|
326
|
+
).toBe(0);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test.each([
|
|
330
|
+
['type d’installation inconnu', { enum_type_installation_id: '9' }],
|
|
331
|
+
['installation individuelle', { enum_type_installation_id: '1' }],
|
|
332
|
+
[
|
|
333
|
+
'réseau collectif non bouclé',
|
|
334
|
+
{ enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '1' }
|
|
335
|
+
],
|
|
336
|
+
['type de bouclage non renseigné', { enum_type_installation_id: '2' }]
|
|
337
|
+
])('%s : pas de consommation', (_libelle, de) => {
|
|
338
|
+
expect(consoAuxDistribution(de, { Janvier: 1000 })).toBe(0);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test('réseau avec traçage : 0,14 × besoin annuel × ratio de surface', () => {
|
|
342
|
+
const conso = consoAuxDistribution(
|
|
343
|
+
{ enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: 3, surface_habitable: 100 },
|
|
344
|
+
{ Janvier: 100, Février: 50 }
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
// 0,14 × 150 kWh × 1000 × (100 / 400) = 5250 Wh => 5,25 kWh
|
|
348
|
+
expect(conso).toBeCloseTo(5.25, 9);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test('réseau avec traçage sans surfaces renseignées : ratio de surface nul', () => {
|
|
352
|
+
const conso = consoAuxDistribution(
|
|
353
|
+
{ enum_type_installation_id: '3', enum_bouclage_reseau_ecs_id: '3' },
|
|
354
|
+
{ Janvier: 100 },
|
|
355
|
+
null
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
// surface_habitable absente => 0 ; surface de l'immeuble absente => 1
|
|
359
|
+
expect(conso).toBe(0);
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
test('réseau bouclé sans besoin : puissance minimale du circulateur de 20 W', () => {
|
|
363
|
+
const conso = consoAuxDistribution(
|
|
364
|
+
{ enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '2', surface_habitable: 100 },
|
|
365
|
+
{ Janvier: 0 }
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
// Pcirb = 20 W chaque mois : (5 × 20 + 19 × 20) × (31 + 28) jours = 28320 Wh
|
|
369
|
+
expect(conso).toBeCloseTo(28.32, 9);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test('réseau bouclé à faible besoin : puissance du circulateur ramenée au minimum de 20 W', () => {
|
|
373
|
+
const conso = consoAuxDistribution(
|
|
374
|
+
{
|
|
375
|
+
enum_type_installation_id: '2',
|
|
376
|
+
enum_bouclage_reseau_ecs_id: '2',
|
|
377
|
+
surface_habitable: 100,
|
|
378
|
+
nombre_niveau_installation_ecs: 2
|
|
379
|
+
},
|
|
380
|
+
{ Janvier: 1000, Février: 1000 }
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
// Phyd / Effcirb < 20 W => Pcirb = 20 W, identique au cas sans besoin
|
|
384
|
+
expect(conso).toBeCloseTo(28.32, 9);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test('réseau bouclé : calcul complet de la consommation du circulateur', () => {
|
|
388
|
+
const conso = consoAuxDistribution(
|
|
389
|
+
{
|
|
390
|
+
enum_type_installation_id: '2',
|
|
391
|
+
enum_bouclage_reseau_ecs_id: '2',
|
|
392
|
+
surface_habitable: 100,
|
|
393
|
+
nombre_niveau_installation_ecs: '2'
|
|
394
|
+
},
|
|
395
|
+
{ Janvier: 10000 }
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
// Lb = 1,2 × (1,1 × 100 / 2 + 4 × 2) = 75,6 m ; ΔPb = 0,2 × Lb + 10 = 25,12 kPa
|
|
399
|
+
// Janvier : Phyd ≈ 4,645 W => Pcirb ≈ 66,49 W ; Février (besoin absent) : Pcirb = 20 W
|
|
400
|
+
// Référence de régression (calcul des 9 étapes de la méthode) : 35526,3879475995 Wh
|
|
401
|
+
expect(conso).toBeCloseTo(35.526387947599495, 9);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
test('réseau bouclé sans nombre de niveaux : un seul niveau par défaut', () => {
|
|
405
|
+
const avecUnNiveau = consoAuxDistribution(
|
|
406
|
+
{
|
|
407
|
+
enum_type_installation_id: '2',
|
|
408
|
+
enum_bouclage_reseau_ecs_id: '2',
|
|
409
|
+
surface_habitable: 100,
|
|
410
|
+
nombre_niveau_installation_ecs: 1
|
|
411
|
+
},
|
|
412
|
+
{ Janvier: 10000 }
|
|
413
|
+
);
|
|
414
|
+
const sansNiveau = consoAuxDistribution(
|
|
415
|
+
{ enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '2', surface_habitable: 100 },
|
|
416
|
+
{ Janvier: 10000 }
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
expect(sansNiveau).toBe(avecUnNiveau);
|
|
420
|
+
});
|
|
421
|
+
});
|
package/README.md
CHANGED
|
@@ -23,8 +23,6 @@ Implémentation open source du moteur Open3CL de l'ADEME.
|
|
|
23
23
|
·
|
|
24
24
|
<a href="https://github.com/Open3CL/engine/issues/new?labels=enhancement&template=feature-request---.md">Créer une feature</a>
|
|
25
25
|
·
|
|
26
|
-
<a href="https://open3cl.github.io/engine/build/">Démo en ligne</a>
|
|
27
|
-
·
|
|
28
26
|
<a href="https://open3cl.github.io/engine/reports/corpus">Rapports de corpus</a>
|
|
29
27
|
</p>
|
|
30
28
|
</div>
|
|
@@ -57,7 +55,6 @@ Implémentation open source du moteur Open3CL de l'ADEME.
|
|
|
57
55
|
<li><a href="#api-publique">API publique</a></li>
|
|
58
56
|
<li><a href="#options-de-calcul">Options de calcul</a></li>
|
|
59
57
|
<li><a href="#lire-le-résultat">Lire le résultat</a></li>
|
|
60
|
-
<li><a href="#tester-un-dpe-sans-écrire-de-code">Tester un DPE sans écrire de code</a></li>
|
|
61
58
|
</ul>
|
|
62
59
|
</li>
|
|
63
60
|
<li><a href="#-tests-de-corpus">Tests de corpus</a>
|
|
@@ -142,12 +139,11 @@ flowchart LR
|
|
|
142
139
|
|
|
143
140
|
### L'écosystème Open3CL
|
|
144
141
|
|
|
145
|
-
| Ressource | Description
|
|
146
|
-
| :-------------------------------------------------------------------- |
|
|
147
|
-
| [open3cl.fr](https://open3cl.fr) | Le site du projet
|
|
148
|
-
| [
|
|
149
|
-
| [
|
|
150
|
-
| [@open3cl/engine](https://www.npmjs.com/package/@open3cl/engine) | Le paquet npm |
|
|
142
|
+
| Ressource | Description |
|
|
143
|
+
| :-------------------------------------------------------------------- | :-------------------------------------------------------- |
|
|
144
|
+
| [open3cl.fr](https://open3cl.fr) | Le site du projet |
|
|
145
|
+
| [Rapports de corpus](https://open3cl.github.io/engine/reports/corpus) | Le tableau de bord interactif des résultats sur DPE réels |
|
|
146
|
+
| [@open3cl/engine](https://www.npmjs.com/package/@open3cl/engine) | Le paquet npm |
|
|
151
147
|
|
|
152
148
|
<p align="right">(<a href="#readme-top">Retour sommaire</a>)</p>
|
|
153
149
|
|
|
@@ -292,17 +288,6 @@ Le DPE renvoyé est l'objet d'entrée, enrichi de `logement.sortie` :
|
|
|
292
288
|
| `sortie.confort_ete` / `qualite_isolation` | Indicateurs de confort d'été et de qualité d'isolation |
|
|
293
289
|
| `sortie.production_electricite` | Production photovoltaïque |
|
|
294
290
|
|
|
295
|
-
### Tester un DPE sans écrire de code
|
|
296
|
-
|
|
297
|
-
Le [démonstrateur en ligne](https://open3cl.github.io/engine/build/) accepte un DPE au format XML, l'envoie au moteur et
|
|
298
|
-
affiche **côte à côte** les valeurs du DPE d'origine, celles calculées par Open3CL, et le différentiel.
|
|
299
|
-
|
|
300
|
-
C'est le moyen le plus rapide de qualifier un écart : problème dans le DPE d'origine, ou bug dans la librairie ?
|
|
301
|
-
|
|
302
|
-
<p align="right">(<a href="#readme-top">Retour sommaire</a>)</p>
|
|
303
|
-
|
|
304
|
-
---
|
|
305
|
-
|
|
306
291
|
## 🧪 Tests de corpus
|
|
307
292
|
|
|
308
293
|
> Un corpus, c'est une liste de numéros de DPE réels. Le moteur les rejoue **tous**, compare ses sorties à celles du
|
|
@@ -385,28 +370,28 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
385
370
|
|
|
386
371
|
<!-- CORPUS:START -->
|
|
387
372
|
|
|
388
|
-
> **Version `1.6.2`** · branche `
|
|
373
|
+
> **Version `1.6.2`** · branche `main` · généré le 2026-09-18
|
|
389
374
|
> Seuil de tolérance **5%**
|
|
390
375
|
|
|
391
376
|
<table>
|
|
392
377
|
<tr>
|
|
393
378
|
<td align="center"><strong>9</strong><br/><sub>corpus</sub></td>
|
|
394
379
|
<td align="center"><strong>89 996</strong><br/><sub>DPE analysés</sub></td>
|
|
395
|
-
<td align="center"><strong>59
|
|
396
|
-
<td align="center"><strong>66,
|
|
380
|
+
<td align="center"><strong>59 497</strong><br/><sub>DPE conformes</sub></td>
|
|
381
|
+
<td align="center"><strong>66,11 %</strong><br/><sub>réussite globale</sub></td>
|
|
397
382
|
</tr>
|
|
398
383
|
</table>
|
|
399
384
|
|
|
400
385
|
| | Corpus | Réussite | | DPE conformes |
|
|
401
386
|
| :-: | :---------------------------------------------------------------------------------------------------------------------- | ----------: | :--------------------- | -------------: |
|
|
402
387
|
| 🔴 | **Généraliste**<br/><sub>`corpus_dpe.csv`</sub> | **45,97 %** | `█████████░░░░░░░░░░░` | 4 597 / 10 000 |
|
|
403
|
-
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **91,
|
|
404
|
-
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **88,
|
|
405
|
-
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **87,
|
|
406
|
-
| 🟡 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **73,
|
|
407
|
-
| 🟡 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **69,
|
|
408
|
-
| 🟡 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **62,
|
|
409
|
-
| 🔴 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **48,
|
|
388
|
+
| 🟢 | **Appartement · chauffage individuel (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_individuel_2025.csv`</sub> | **91,55 %** | `██████████████████░░` | 9 155 / 10 000 |
|
|
389
|
+
| 🟢 | **Logement individuel (2025)**<br/><sub>`dpe_logement_individuel_2025.csv`</sub> | **88,14 %** | `██████████████████░░` | 8 812 / 9 998 |
|
|
390
|
+
| 🟢 | **Maison individuelle (2025)**<br/><sub>`dpe_maison_individuelle_2025.csv`</sub> | **87,93 %** | `██████████████████░░` | 8 793 / 10 000 |
|
|
391
|
+
| 🟡 | **Immeuble · chauffage individuel**<br/><sub>`dpe_immeuble_chauffage_individuel.csv`</sub> | **73,76 %** | `███████████████░░░░░` | 7 375 / 9 999 |
|
|
392
|
+
| 🟡 | **Appartement · chauffage collectif (2025)**<br/><sub>`dpe_appartement_individuel_chauffage_collectif_2025.csv`</sub> | **69,47 %** | `██████████████░░░░░░` | 6 947 / 10 000 |
|
|
393
|
+
| 🟡 | **Immeuble · chauffage collectif**<br/><sub>`dpe_immeuble_chauffage_collectif.csv`</sub> | **62,27 %** | `████████████░░░░░░░░` | 6 226 / 9 999 |
|
|
394
|
+
| 🔴 | **Immeuble · chauffage mixte**<br/><sub>`dpe_immeuble_chauffage_mixte.csv`</sub> | **48,37 %** | `██████████░░░░░░░░░░` | 4 837 / 10 000 |
|
|
410
395
|
| 🔴 | **Individuel généré depuis l'immeuble (2026)**<br/><sub>`dpe_individuel_a_partir_dpe_immeuble_2026.csv`</sub> | **27,55 %** | `██████░░░░░░░░░░░░░░` | 2 755 / 10 000 |
|
|
411
396
|
|
|
412
397
|
<sub>🟢 ≥ 85 % · 🟡 ≥ 60 % · 🔴 < 60 %</sub>
|
|
@@ -418,24 +403,24 @@ Ces résultats sont **générés automatiquement** à la fin de `npm run test:co
|
|
|
418
403
|
|
|
419
404
|
<table>
|
|
420
405
|
<tr>
|
|
421
|
-
<td align="center"><strong>5,
|
|
422
|
-
<td align="center"><strong>
|
|
423
|
-
<td align="center"><strong>0,
|
|
424
|
-
<td align="center"><strong>
|
|
406
|
+
<td align="center"><strong>5,61 ms</strong><br/><sub>moyenne</sub></td>
|
|
407
|
+
<td align="center"><strong>5,03 ms</strong><br/><sub>médiane</sub></td>
|
|
408
|
+
<td align="center"><strong>0,11 ms</strong><br/><sub>min</sub></td>
|
|
409
|
+
<td align="center"><strong>705,8 ms</strong><br/><sub>max</sub></td>
|
|
425
410
|
</tr>
|
|
426
411
|
</table>
|
|
427
412
|
|
|
428
413
|
| Corpus | Moyenne | Médiane | Min | Max | p95 | p99 |
|
|
429
414
|
| :--------------------------------------------- | ------: | ------: | ------: | -------: | ------: | ------: |
|
|
430
|
-
| **Généraliste** | 4,
|
|
431
|
-
| **Appartement · chauffage individuel (2025)** | 3,
|
|
432
|
-
| **Logement individuel (2025)** |
|
|
433
|
-
| **Maison individuelle (2025)** |
|
|
434
|
-
| **Immeuble · chauffage individuel** |
|
|
435
|
-
| **Appartement · chauffage collectif (2025)** |
|
|
436
|
-
| **Immeuble · chauffage collectif** | 6,
|
|
437
|
-
| **Immeuble · chauffage mixte** |
|
|
438
|
-
| **Individuel généré depuis l'immeuble (2026)** |
|
|
415
|
+
| **Généraliste** | 4,50 ms | 4,09 ms | 0,20 ms | 34,8 ms | 8,12 ms | 11,3 ms |
|
|
416
|
+
| **Appartement · chauffage individuel (2025)** | 3,71 ms | 3,46 ms | 1,32 ms | 26,1 ms | 6,22 ms | 7,99 ms |
|
|
417
|
+
| **Logement individuel (2025)** | 5,30 ms | 4,84 ms | 1,52 ms | 42,5 ms | 9,73 ms | 13,2 ms |
|
|
418
|
+
| **Maison individuelle (2025)** | 6,54 ms | 6,02 ms | 1,71 ms | 54,5 ms | 11,6 ms | 16,4 ms |
|
|
419
|
+
| **Immeuble · chauffage individuel** | 7,17 ms | 6,22 ms | 0,11 ms | 137,7 ms | 14,3 ms | 22,3 ms |
|
|
420
|
+
| **Appartement · chauffage collectif (2025)** | 3,98 ms | 3,76 ms | 1,22 ms | 37,6 ms | 6,35 ms | 8,00 ms |
|
|
421
|
+
| **Immeuble · chauffage collectif** | 6,40 ms | 5,58 ms | 0,15 ms | 101,0 ms | 12,1 ms | 21,2 ms |
|
|
422
|
+
| **Immeuble · chauffage mixte** | 6,01 ms | 5,01 ms | 0,22 ms | 705,8 ms | 12,4 ms | 19,8 ms |
|
|
423
|
+
| **Individuel généré depuis l'immeuble (2026)** | 6,89 ms | 6,27 ms | 1,52 ms | 65,1 ms | 13,0 ms | 19,7 ms |
|
|
439
424
|
|
|
440
425
|
<sub>La moyenne est tirée vers le haut par les DPE collectifs, dont le coût atteint plusieurs
|
|
441
426
|
dizaines de fois la médiane : c’est la médiane qui décrit le cas courant, et p95/p99 le cas
|
package/conso.js
CHANGED
|
@@ -249,6 +249,7 @@ export default function calc_conso(
|
|
|
249
249
|
null,
|
|
250
250
|
prorataECS,
|
|
251
251
|
prorataChauffage,
|
|
252
|
+
ecs,
|
|
252
253
|
conso_aux_distribution_ecs_total
|
|
253
254
|
),
|
|
254
255
|
ep_conso: calc_conso_pond(
|
|
@@ -262,6 +263,7 @@ export default function calc_conso(
|
|
|
262
263
|
coeffEp,
|
|
263
264
|
prorataECS,
|
|
264
265
|
prorataChauffage,
|
|
266
|
+
ecs,
|
|
265
267
|
conso_aux_distribution_ecs_total
|
|
266
268
|
),
|
|
267
269
|
emission_ges: calc_conso_pond(
|
|
@@ -275,6 +277,7 @@ export default function calc_conso(
|
|
|
275
277
|
coef_ges,
|
|
276
278
|
prorataECS,
|
|
277
279
|
prorataChauffage,
|
|
280
|
+
ecs,
|
|
278
281
|
conso_aux_distribution_ecs_total
|
|
279
282
|
),
|
|
280
283
|
cout: calc_conso_pond(
|
|
@@ -288,6 +291,7 @@ export default function calc_conso(
|
|
|
288
291
|
coef_cout,
|
|
289
292
|
prorataECS,
|
|
290
293
|
prorataChauffage,
|
|
294
|
+
ecs,
|
|
291
295
|
conso_aux_distribution_ecs_total
|
|
292
296
|
)
|
|
293
297
|
};
|
|
@@ -343,7 +347,8 @@ export default function calc_conso(
|
|
|
343
347
|
'',
|
|
344
348
|
null,
|
|
345
349
|
prorataECS,
|
|
346
|
-
prorataChauffage
|
|
350
|
+
prorataChauffage,
|
|
351
|
+
type_energie === 'électricité' ? ecs : []
|
|
347
352
|
);
|
|
348
353
|
conso_en = {
|
|
349
354
|
conso_ch: conso_en._ch,
|
|
@@ -474,7 +479,7 @@ function calc_conso_pond(
|
|
|
474
479
|
coef,
|
|
475
480
|
prorataECS,
|
|
476
481
|
prorataChauffage,
|
|
477
|
-
|
|
482
|
+
ecs_installations
|
|
478
483
|
) {
|
|
479
484
|
const ret = {};
|
|
480
485
|
ret.auxiliaire_ventilation = vt_list.reduce((acc, vt) => {
|
|
@@ -538,11 +543,10 @@ function calc_conso_pond(
|
|
|
538
543
|
return acc + getConso(coef, 'électricité auxiliaire', conso);
|
|
539
544
|
}, 0);
|
|
540
545
|
|
|
541
|
-
ret.auxiliaire_distribution_ecs =
|
|
542
|
-
|
|
543
|
-
'électricité auxiliaire',
|
|
544
|
-
|
|
545
|
-
);
|
|
546
|
+
ret.auxiliaire_distribution_ecs = ecs_installations.reduce((acc, inst) => {
|
|
547
|
+
const conso = inst.donnee_intermediaire.conso_auxiliaire_distribution_ecs || 0;
|
|
548
|
+
return acc + getConso(coef, 'électricité auxiliaire', conso);
|
|
549
|
+
}, 0);
|
|
546
550
|
|
|
547
551
|
ret.ecs = getEcsConso(gen_ecs, 'conso_ecs', coef, prorataECS, prefix);
|
|
548
552
|
|
package/engine.js
CHANGED
|
@@ -33,7 +33,7 @@ import getFicheTechnique from './ficheTechnique.js';
|
|
|
33
33
|
import { ProductionENR } from './16.2_production_enr.js';
|
|
34
34
|
import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
35
35
|
|
|
36
|
-
const LIB_VERSION = '1.
|
|
36
|
+
const LIB_VERSION = '1.8.0';
|
|
37
37
|
|
|
38
38
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
39
39
|
|
|
@@ -437,7 +437,8 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
437
437
|
virtualisationECS,
|
|
438
438
|
dpe.logement.caracteristique_generale.surface_habitable_immeuble,
|
|
439
439
|
dpe.logement.caracteristique_generale.nombre_appartement,
|
|
440
|
-
isImmeubleSystemEcsIndividuels
|
|
440
|
+
isImmeubleSystemEcsIndividuels,
|
|
441
|
+
apport_et_besoin.besoin_ecs_par_mois || {}
|
|
441
442
|
);
|
|
442
443
|
|
|
443
444
|
if (!dpeGenereImmeuble) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open3cl/engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Open Source 3CL-DPE engine",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"directories": {
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
"prepare": "husky install",
|
|
29
29
|
"assets:sync": "node scripts/synchronize_assets.js",
|
|
30
30
|
"version:update": "node scripts/update_version.js",
|
|
31
|
+
"reports:charts": "node scripts/generate_corpus_history_chart.js",
|
|
31
32
|
"reports:preview": "npm run http-server dist/reports/corpus -o",
|
|
32
|
-
"reports:readme": "node scripts/generate_corpus_readme.js"
|
|
33
|
+
"reports:readme": "node scripts/generate_corpus_readme.js && npm run reports:charts"
|
|
33
34
|
},
|
|
34
35
|
"repository": {
|
|
35
36
|
"type": "git",
|