@open3cl/engine 1.8.49 → 1.8.51
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/13.2_generateur_combustion.js +99 -2
- package/13.2_generateur_combustion.spec.js +196 -1
- package/13.2_generateur_combustion_pdim.js +212 -0
- package/13.2_generateur_combustion_pdim.spec.js +259 -0
- package/14_generateur_ecs.js +4 -1
- package/README.md +14 -14
- package/conso.js +115 -37
- package/conso.spec.js +188 -10
- package/engine.js +13 -5
- package/engine.spec.js +18 -0
- package/features/engine/domain/ch/generateur-ch.service.js +61 -5
- package/features/engine/domain/ch/generateur-ch.service.spec.js +101 -0
- package/package.json +1 -1
package/conso.spec.js
CHANGED
|
@@ -37,13 +37,21 @@ vi.mock('./tv.js', () => ({
|
|
|
37
37
|
}
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
|
+
/** Mode bug_for_bug_compat piloté par les tests (export « live » du module utils mocké). */
|
|
41
|
+
const utilsState = vi.hoisted(() => ({ bugForBugCompat: false }));
|
|
42
|
+
|
|
43
|
+
/** Lecture des tables par défaut : réseau de chaleur "R1" connu, "R2" inconnu. */
|
|
44
|
+
const tvParDefaut = (table, matcher) => {
|
|
45
|
+
if (matcher?.identifiant_reseau === 'R1') return { contenu_co2_acv: 0.15 };
|
|
46
|
+
return null;
|
|
47
|
+
};
|
|
48
|
+
|
|
40
49
|
vi.mock('./utils.js', () => ({
|
|
41
50
|
set_bug_for_bug_compat: vi.fn(),
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
})
|
|
51
|
+
get bug_for_bug_compat() {
|
|
52
|
+
return utilsState.bugForBugCompat;
|
|
53
|
+
},
|
|
54
|
+
tv: vi.fn((table, matcher) => tvParDefaut(table, matcher))
|
|
47
55
|
}));
|
|
48
56
|
|
|
49
57
|
const {
|
|
@@ -58,8 +66,13 @@ const {
|
|
|
58
66
|
getAuxGenerationEcs,
|
|
59
67
|
DATE_BAREME_COUT_2024,
|
|
60
68
|
appointBijonction,
|
|
61
|
-
coutBijonction
|
|
69
|
+
coutBijonction,
|
|
70
|
+
ARRETES_RESEAU_CHALEUR,
|
|
71
|
+
COEF_GES_AUTRES_RESEAUX_CHALEUR,
|
|
72
|
+
indexArreteReseauChaleur,
|
|
73
|
+
getContenuCo2ReseauChaleur
|
|
62
74
|
} = await import('./conso.js');
|
|
75
|
+
const { tv } = await import('./utils.js');
|
|
63
76
|
|
|
64
77
|
/**
|
|
65
78
|
* Classe énergétique (bilan DPE)
|
|
@@ -432,7 +445,7 @@ describe('calc_conso - agrégation des consommations', () => {
|
|
|
432
445
|
expect(res.emission_ges.emission_ges_ch).toBeCloseTo(150, 9);
|
|
433
446
|
});
|
|
434
447
|
|
|
435
|
-
test('réseau de chaleur inconnu (identifiant sans ligne) : erreur signalée', () => {
|
|
448
|
+
test('réseau de chaleur inconnu (identifiant sans ligne) : erreur signalée, facteur « autres réseaux » 0,385', () => {
|
|
436
449
|
const ch = [
|
|
437
450
|
installCh({ cle_repartition_ch: 1 }, [
|
|
438
451
|
genCh(
|
|
@@ -443,8 +456,10 @@ describe('calc_conso - agrégation des consommations', () => {
|
|
|
443
456
|
])
|
|
444
457
|
];
|
|
445
458
|
|
|
446
|
-
calc_conso(100, 1, 1, [], ch, [], [], 1, 1, DATE_DPE, coef_ep);
|
|
459
|
+
const res = calc_conso(100, 1, 1, [], ch, [], [], 1, 1, DATE_DPE, coef_ep);
|
|
447
460
|
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('R2'));
|
|
461
|
+
// Plus de repli sur 1 kgCO2/kWh (GES = consommation) : 1000 * 0,385
|
|
462
|
+
expect(res.emission_ges.emission_ges_ch).toBeCloseTo(385, 9);
|
|
448
463
|
});
|
|
449
464
|
|
|
450
465
|
test('réseau de chaleur sans identifiant : coefficient GES forfaitaire 0,385', () => {
|
|
@@ -459,8 +474,8 @@ describe('calc_conso - agrégation des consommations', () => {
|
|
|
459
474
|
expect(res.emission_ges.emission_ges_ch).toBeCloseTo(385, 9);
|
|
460
475
|
});
|
|
461
476
|
|
|
462
|
-
test("date de
|
|
463
|
-
// date_arrete_reseau_chaleur = Symbol
|
|
477
|
+
test("date d'arrêté réseau de chaleur invalide : aucune exception", () => {
|
|
478
|
+
// date_arrete_reseau_chaleur = Symbol : ignorée (repli sur la date du DPE)
|
|
464
479
|
const ch = [
|
|
465
480
|
installCh({ cle_repartition_ch: 1 }, [
|
|
466
481
|
genCh(
|
|
@@ -1169,3 +1184,166 @@ describe('appointBijonction / coutBijonction', () => {
|
|
|
1169
1184
|
);
|
|
1170
1185
|
});
|
|
1171
1186
|
});
|
|
1187
|
+
|
|
1188
|
+
/**
|
|
1189
|
+
* Facteur d'émission des réseaux de chaleur urbains : table de l'arrêté utilisé
|
|
1190
|
+
* (modele_donnee/arrete_reseau_chaleur.json de l'observatoire DPE).
|
|
1191
|
+
*/
|
|
1192
|
+
describe('réseaux de chaleur - sélection de l’arrêté et du contenu CO2', () => {
|
|
1193
|
+
let errorSpy;
|
|
1194
|
+
|
|
1195
|
+
beforeEach(() => {
|
|
1196
|
+
errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
1197
|
+
vi.mocked(tv).mockReset();
|
|
1198
|
+
utilsState.bugForBugCompat = false;
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
afterEach(() => {
|
|
1202
|
+
errorSpy.mockRestore();
|
|
1203
|
+
vi.mocked(tv).mockImplementation(tvParDefaut);
|
|
1204
|
+
utilsState.bugForBugCompat = false;
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
/** Tables mockées : identifiant -> { table: ligne } */
|
|
1208
|
+
const mockTables = (lignes) =>
|
|
1209
|
+
vi
|
|
1210
|
+
.mocked(tv)
|
|
1211
|
+
.mockImplementation((table, matcher) => lignes[matcher.identifiant_reseau]?.[table] ?? null);
|
|
1212
|
+
|
|
1213
|
+
test('six arrêtés, dans l’ordre chronologique, table ≠ année de l’arrêté', () => {
|
|
1214
|
+
expect(ARRETES_RESEAU_CHALEUR.map((a) => a.table)).toEqual([
|
|
1215
|
+
'reseau_chaleur_2020',
|
|
1216
|
+
'reseau_chaleur_2021',
|
|
1217
|
+
'reseau_chaleur_2022',
|
|
1218
|
+
'reseau_chaleur_2023',
|
|
1219
|
+
'reseau_chaleur_2024',
|
|
1220
|
+
'reseau_chaleur_2025'
|
|
1221
|
+
]);
|
|
1222
|
+
// L'arrêté du 05/07/2024 publie la table 2023 (cause du bug : 2024 - 1 = 2023 forcé à 2022)
|
|
1223
|
+
expect(ARRETES_RESEAU_CHALEUR[3]).toMatchObject({
|
|
1224
|
+
table: 'reseau_chaleur_2023',
|
|
1225
|
+
date_arrete: '2024-07-05'
|
|
1226
|
+
});
|
|
1227
|
+
const dates = ARRETES_RESEAU_CHALEUR.map((a) => a.date_arrete);
|
|
1228
|
+
expect([...dates].sort()).toEqual(dates);
|
|
1229
|
+
ARRETES_RESEAU_CHALEUR.forEach((a) => expect(a.date_application >= a.date_arrete).toBe(true));
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
test.each([
|
|
1233
|
+
['2020-10-12', 0],
|
|
1234
|
+
['2021-10-21', 1],
|
|
1235
|
+
['2022-01-18', 1], // date hors liste : dernier arrêté publié avant
|
|
1236
|
+
['2023-03-16', 2],
|
|
1237
|
+
['2024-07-05', 3],
|
|
1238
|
+
['2024-07-10', 3],
|
|
1239
|
+
['2025-04-11', 4],
|
|
1240
|
+
['2026-04-25', 5],
|
|
1241
|
+
['2030-01-01', 5],
|
|
1242
|
+
['2019-01-01', 0]
|
|
1243
|
+
])('date_arrete_reseau_chaleur %s : arrêté n°%i', (dateArrete, index) => {
|
|
1244
|
+
// La date du DPE est ignorée quand la date d'arrêté est renseignée
|
|
1245
|
+
expect(indexArreteReseauChaleur(dateArrete, '2030-01-01')).toBe(index);
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
test.each([
|
|
1249
|
+
['2022-01-17', 0],
|
|
1250
|
+
['2022-01-18', 1],
|
|
1251
|
+
['2023-04-15', 1], // arrêté du 16/03/2023 applicable au 16/04/2023 seulement
|
|
1252
|
+
['2023-04-16', 2],
|
|
1253
|
+
['2024-08-09', 3],
|
|
1254
|
+
[undefined, 0],
|
|
1255
|
+
['date invalide', 0]
|
|
1256
|
+
])('sans date d’arrêté, DPE du %s : arrêté applicable n°%i', (dateDpe, index) => {
|
|
1257
|
+
expect(indexArreteReseauChaleur(undefined, dateDpe)).toBe(index);
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
test('date d’arrêté illisible (non chaîne) : repli sur la date du DPE', () => {
|
|
1261
|
+
expect(indexArreteReseauChaleur(Symbol('invalide'), '2024-08-09')).toBe(3);
|
|
1262
|
+
expect(indexArreteReseauChaleur(new Date('2024-07-05'), undefined)).toBe(3);
|
|
1263
|
+
});
|
|
1264
|
+
|
|
1265
|
+
test('arrêté du 05/07/2024 : lecture de reseau_chaleur_2023 et contenu ACV (cas Pau 6403C)', () => {
|
|
1266
|
+
mockTables({
|
|
1267
|
+
'6403C': { reseau_chaleur_2023: { contenu_co2: '0.047', contenu_co2_acv: '0.061' } }
|
|
1268
|
+
});
|
|
1269
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2024-07-05', '2025-01-24', 'Pleiades')).toBe(0.061);
|
|
1270
|
+
expect(tv).toHaveBeenCalledTimes(1);
|
|
1271
|
+
expect(tv).toHaveBeenCalledWith('reseau_chaleur_2023', { identifiant_reseau: '6403C' });
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
test('bug_for_bug_compat + LICIEL + table 2023 : contenu CO2 hors ACV transmis par le logiciel', () => {
|
|
1275
|
+
utilsState.bugForBugCompat = true;
|
|
1276
|
+
mockTables({
|
|
1277
|
+
'6403C': {
|
|
1278
|
+
reseau_chaleur_2023: { contenu_co2: '0.047', contenu_co2_acv: '0.061' },
|
|
1279
|
+
reseau_chaleur_2022: { contenu_co2: '0.1', contenu_co2_acv: '0.2' }
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
const liciel = 'LICIEL Diagnostics v4 [Version XML:305]';
|
|
1283
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2024-07-05', '2025-01-24', liciel)).toBe(0.047);
|
|
1284
|
+
// Autre logiciel : valeur ACV
|
|
1285
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2024-07-05', '2025-01-24', 'DPEWIN V5.2.5')).toBe(
|
|
1286
|
+
0.061
|
|
1287
|
+
);
|
|
1288
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2024-07-05', '2025-01-24', undefined)).toBe(0.061);
|
|
1289
|
+
// LICIEL avec un autre arrêté (16/03/2023 -> table 2022) : valeur ACV
|
|
1290
|
+
expect(getContenuCo2ReseauChaleur('6403C', '2023-03-16', '2023-06-01', liciel)).toBe(0.2);
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
test('sans bug_for_bug_compat, LICIEL lit aussi le contenu ACV', () => {
|
|
1294
|
+
mockTables({
|
|
1295
|
+
R: { reseau_chaleur_2023: { contenu_co2: '0.047', contenu_co2_acv: '0.061' } }
|
|
1296
|
+
});
|
|
1297
|
+
expect(getContenuCo2ReseauChaleur('R', '2024-07-05', undefined, 'LICIEL')).toBe(0.061);
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
test('réseau absent de la table de l’arrêté : table antérieure la plus récente qui le contient', () => {
|
|
1301
|
+
mockTables({
|
|
1302
|
+
R: {
|
|
1303
|
+
reseau_chaleur_2021: { contenu_co2: '0.3', contenu_co2_acv: '0.33' },
|
|
1304
|
+
reseau_chaleur_2020: { contenu_co2: '0.4', contenu_co2_acv: '0.44' }
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
expect(getContenuCo2ReseauChaleur('R', '2024-07-05', undefined, undefined)).toBe(0.33);
|
|
1308
|
+
expect(vi.mocked(tv).mock.calls.map((c) => c[0])).toEqual([
|
|
1309
|
+
'reseau_chaleur_2023',
|
|
1310
|
+
'reseau_chaleur_2022',
|
|
1311
|
+
'reseau_chaleur_2021'
|
|
1312
|
+
]);
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
test('réseau introuvable dans toutes les tables : facteur « autres réseaux de chaleur » 0,385 et erreur', () => {
|
|
1316
|
+
mockTables({});
|
|
1317
|
+
expect(COEF_GES_AUTRES_RESEAUX_CHALEUR).toBe(0.385);
|
|
1318
|
+
expect(getContenuCo2ReseauChaleur('XXXX', '2024-07-05', undefined, undefined)).toBe(0.385);
|
|
1319
|
+
expect(tv).toHaveBeenCalledTimes(4);
|
|
1320
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('XXXX'));
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
test('calc_conso : facteur réseau appliqué au chauffage et à l’ECS, version logiciel transmise', () => {
|
|
1324
|
+
utilsState.bugForBugCompat = true;
|
|
1325
|
+
mockTables({
|
|
1326
|
+
'6403C': { reseau_chaleur_2023: { contenu_co2: '0.047', contenu_co2_acv: '0.061' } }
|
|
1327
|
+
});
|
|
1328
|
+
const de = { identifiant_reseau_chaleur: '6403C', date_arrete_reseau_chaleur: '2024-07-05' };
|
|
1329
|
+
const ch = [
|
|
1330
|
+
installCh({ cle_repartition_ch: 1 }, [
|
|
1331
|
+
genCh('3', { conso_ch: 1000, conso_ch_depensier: 1200 }, de)
|
|
1332
|
+
])
|
|
1333
|
+
];
|
|
1334
|
+
const ecs = [
|
|
1335
|
+
installEcs({ cle_repartition_ecs: 1 }, [
|
|
1336
|
+
genEcs('3', { conso_ecs: 500, conso_ecs_depensier: 600 }, de)
|
|
1337
|
+
])
|
|
1338
|
+
];
|
|
1339
|
+
const args = [100, 1, 1, [], ch, ecs, [], 1, 1, '2025-01-24', coef_ep, 1, 1, {}];
|
|
1340
|
+
|
|
1341
|
+
const licielRes = calc_conso(...args, 'LICIEL Diagnostics v4');
|
|
1342
|
+
// 1000 * 0,047 et 500 * 0,047 (valeur ADEME du DPE 2564E0255485Z) — avant : 1000 * 1
|
|
1343
|
+
expect(licielRes.emission_ges.emission_ges_ch).toBeCloseTo(47, 9);
|
|
1344
|
+
expect(licielRes.emission_ges.emission_ges_ecs).toBeCloseTo(23.5, 9);
|
|
1345
|
+
|
|
1346
|
+
const autreRes = calc_conso(...args);
|
|
1347
|
+
expect(autreRes.emission_ges.emission_ges_ch).toBeCloseTo(61, 9);
|
|
1348
|
+
});
|
|
1349
|
+
});
|
package/engine.js
CHANGED
|
@@ -33,7 +33,7 @@ import getFicheTechnique from './ficheTechnique.js';
|
|
|
33
33
|
import { ProductionENR, ratioProrataPvLogement } from './16.2_production_enr.js';
|
|
34
34
|
import DpeSanitizerService from './dpe-sanitizer.service.js';
|
|
35
35
|
|
|
36
|
-
const LIB_VERSION = '1.8.
|
|
36
|
+
const LIB_VERSION = '1.8.51';
|
|
37
37
|
|
|
38
38
|
const dpeSanitizerService = new DpeSanitizerService();
|
|
39
39
|
|
|
@@ -85,6 +85,7 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
85
85
|
}
|
|
86
86
|
const modele = enums.modele_dpe[dpe.administratif.enum_modele_dpe_id];
|
|
87
87
|
const dateDpe = dpe.administratif.date_etablissement_dpe;
|
|
88
|
+
const versionLogiciel = dpe.administratif.diagnostiqueur?.version_logiciel;
|
|
88
89
|
if (modele !== 'dpe 3cl 2021 méthode logement') {
|
|
89
90
|
console.error('Moteur dpe non implémenté pour le modèle: ' + modele);
|
|
90
91
|
return null;
|
|
@@ -606,7 +607,9 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
606
607
|
dateDpe,
|
|
607
608
|
coef_ep,
|
|
608
609
|
th === 'immeuble' ? Nb_lgt : 1,
|
|
609
|
-
ratioSurfaceCoutCollectif
|
|
610
|
+
ratioSurfaceCoutCollectif,
|
|
611
|
+
{},
|
|
612
|
+
versionLogiciel
|
|
610
613
|
);
|
|
611
614
|
|
|
612
615
|
// Proratisation au logement d'une installation PV d'immeuble (appartement)
|
|
@@ -648,7 +651,8 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
648
651
|
coef_ep,
|
|
649
652
|
th === 'immeuble' ? Nb_lgt : 1,
|
|
650
653
|
ratioSurfaceCoutCollectif,
|
|
651
|
-
facteursAc
|
|
654
|
+
facteursAc,
|
|
655
|
+
versionLogiciel
|
|
652
656
|
);
|
|
653
657
|
conso.emission_ges = consoNette.emission_ges;
|
|
654
658
|
// Ligne électricité de sortie_par_energie nette de l'autoconsommation (issue #34)
|
|
@@ -669,7 +673,9 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
669
673
|
dateDpe,
|
|
670
674
|
coef_ep_1_7,
|
|
671
675
|
th === 'immeuble' ? Nb_lgt : 1,
|
|
672
|
-
ratioSurfaceCoutCollectif
|
|
676
|
+
ratioSurfaceCoutCollectif,
|
|
677
|
+
{},
|
|
678
|
+
versionLogiciel
|
|
673
679
|
);
|
|
674
680
|
|
|
675
681
|
productionENR.calculateEnr(
|
|
@@ -695,7 +701,9 @@ export function calcul_3cl(inputDpe, options) {
|
|
|
695
701
|
dateDpe,
|
|
696
702
|
coef_ep_2_3,
|
|
697
703
|
th === 'immeuble' ? Nb_lgt : 1,
|
|
698
|
-
ratioSurfaceCoutCollectif
|
|
704
|
+
ratioSurfaceCoutCollectif,
|
|
705
|
+
{},
|
|
706
|
+
versionLogiciel
|
|
699
707
|
);
|
|
700
708
|
|
|
701
709
|
productionENR.calculateEnr(
|
package/engine.spec.js
CHANGED
|
@@ -518,6 +518,24 @@ describe('calcul_3cl - run intégral (maison)', () => {
|
|
|
518
518
|
expect(dpe.logement.sortie.cout).toEqual({ cout_5_usages: 2 });
|
|
519
519
|
});
|
|
520
520
|
|
|
521
|
+
test('version du logiciel de saisie transmise à tous les calculs de conso (facteur GES réseau de chaleur)', () => {
|
|
522
|
+
const dpe = makeDpe();
|
|
523
|
+
dpe.administratif.diagnostiqueur = { version_logiciel: 'LICIEL Diagnostics v4' };
|
|
524
|
+
const facteurs = { ch: 0.8 };
|
|
525
|
+
vi.mocked(calculateEnr).mockImplementationOnce((enr, conso, Sh, th, zc, coef, ratio, f) => {
|
|
526
|
+
Object.assign(f, facteurs);
|
|
527
|
+
return { pv: 1 };
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
calcul_3cl(dpe, { sanitize: false });
|
|
531
|
+
|
|
532
|
+
// Lue avant l'écrasement de `diagnostiqueur` par la version du moteur
|
|
533
|
+
expect(calc_conso).toHaveBeenCalledTimes(4);
|
|
534
|
+
calc_conso.mock.calls.forEach((call) => expect(call[14]).toBe('LICIEL Diagnostics v4'));
|
|
535
|
+
expect(calc_conso.mock.calls[0][13]).toEqual({});
|
|
536
|
+
expect(calc_conso.mock.calls[1][13]).toEqual(facteurs);
|
|
537
|
+
});
|
|
538
|
+
|
|
521
539
|
test('bâtiment matériaux anciens => ilpa = 1 ; incohérence inertie => erreur loggée', () => {
|
|
522
540
|
const dpe = makeDpe();
|
|
523
541
|
dpe.logement.meteo.batiment_materiaux_anciens = 1;
|
|
@@ -4,6 +4,11 @@ import { excel_to_js_exec } from '../../../../utils.js';
|
|
|
4
4
|
import { TvStore } from '../../../dpe/infrastructure/tv.store.js';
|
|
5
5
|
import { EmetteurChService } from './emetteur-ch.service.js';
|
|
6
6
|
import { TypeGenerateur } from '../../../dpe/domain/models/type-generateur.model.js';
|
|
7
|
+
import enums from '../../../../enums.js';
|
|
8
|
+
import {
|
|
9
|
+
anneeInstallationGenerateur,
|
|
10
|
+
pnEstimeeDimensionnement
|
|
11
|
+
} from '../../../../13.2_generateur_combustion_pdim.js';
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* Calcul des données de calcul pour chacun des générateurs
|
|
@@ -76,7 +81,8 @@ export class GenerateurChService {
|
|
|
76
81
|
generateurChauffage.donnee_intermediaire.pn = this.pn(
|
|
77
82
|
ctx,
|
|
78
83
|
logement,
|
|
79
|
-
installationChauffage
|
|
84
|
+
installationChauffage,
|
|
85
|
+
generateurChauffage
|
|
80
86
|
);
|
|
81
87
|
}
|
|
82
88
|
|
|
@@ -206,9 +212,10 @@ export class GenerateurChService {
|
|
|
206
212
|
* @param ctx {Contexte}
|
|
207
213
|
* @param logement {Logement}
|
|
208
214
|
* @param installationChauffage {InstallationChauffage}
|
|
215
|
+
* @param generateurChauffage {GenerateurChauffage}
|
|
209
216
|
* @return {number}
|
|
210
217
|
*/
|
|
211
|
-
pn(ctx, logement, installationChauffage) {
|
|
218
|
+
pn(ctx, logement, installationChauffage, generateurChauffage) {
|
|
212
219
|
const Tbase = this.#tvStore.getTempBase(ctx.altitude.id, ctx.zoneClimatique.id);
|
|
213
220
|
// §13.2.2.4 : générateur individuel n'alimentant qu'une partie du logement => prorata Sh_installation / Sh.
|
|
214
221
|
// Un générateur collectif dessert tout le bâtiment : pas de prorata.
|
|
@@ -218,9 +225,58 @@ export class GenerateurChService {
|
|
|
218
225
|
? installationDE.surface_chauffee / ctx.surfaceHabitable
|
|
219
226
|
: 1;
|
|
220
227
|
const prorata = ratio > 0 && ratio < 1 ? ratio : 1;
|
|
221
|
-
|
|
222
|
-
(prorata * 1.2 * logement.sortie.deperdition.deperdition_enveloppe * (19 - Tbase)) /
|
|
223
|
-
|
|
228
|
+
const pch =
|
|
229
|
+
(prorata * 1.2 * logement.sortie.deperdition.deperdition_enveloppe * (19 - Tbase)) /
|
|
230
|
+
0.95 ** 3;
|
|
231
|
+
|
|
232
|
+
// §13.2.2.4 : Pdim = max(Pch ; Pecs) puis table Pdim → Pn des chaudières (générateurs individuels)
|
|
233
|
+
if (String(installationDE?.enum_type_installation_id) !== '1' || !generateurChauffage) {
|
|
234
|
+
return pch;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const generateurDE = generateurChauffage.donnee_entree;
|
|
238
|
+
const usageMixte = String(generateurDE.enum_usage_generateur_id) === '3';
|
|
239
|
+
const libelleTypeGenerateur = enums.type_generateur_ch[generateurDE.enum_type_generateur_ch_id];
|
|
240
|
+
|
|
241
|
+
return pnEstimeeDimensionnement({
|
|
242
|
+
pch,
|
|
243
|
+
volumeStockage: usageMixte ? this.volumeStockageEcsMixte(logement, generateurDE) : undefined,
|
|
244
|
+
usageEcs: usageMixte,
|
|
245
|
+
libelleTypeGenerateur,
|
|
246
|
+
// Fiches techniques non disponibles dans la nouvelle architecture : pose murale inconnue
|
|
247
|
+
murale: undefined,
|
|
248
|
+
anneeInstallation: anneeInstallationGenerateur(
|
|
249
|
+
generateurDE.annee_installation,
|
|
250
|
+
ctx.anneeConstruction,
|
|
251
|
+
libelleTypeGenerateur
|
|
252
|
+
)
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Volume de stockage (L) de la production d'ECS assurée par un générateur de chauffage mixte :
|
|
258
|
+
* générateur ECS mixte de même `reference_generateur_mixte`, sinon générateur ECS mixte unique.
|
|
259
|
+
*
|
|
260
|
+
* @param logement {Logement}
|
|
261
|
+
* @param generateurChauffageDE {GenerateurChauffageDE}
|
|
262
|
+
* @return {number|undefined}
|
|
263
|
+
*/
|
|
264
|
+
volumeStockageEcsMixte(logement, generateurChauffageDE) {
|
|
265
|
+
const generateursEcsMixtes = (logement.installation_ecs_collection?.installation_ecs || [])
|
|
266
|
+
.flatMap((installation) => installation.generateur_ecs_collection?.generateur_ecs || [])
|
|
267
|
+
.filter((generateur) => String(generateur.donnee_entree?.enum_usage_generateur_id) === '3');
|
|
268
|
+
|
|
269
|
+
const reference = generateurChauffageDE.reference_generateur_mixte;
|
|
270
|
+
const jumeau =
|
|
271
|
+
(reference !== undefined &&
|
|
272
|
+
reference !== null &&
|
|
273
|
+
generateursEcsMixtes.find(
|
|
274
|
+
(generateur) =>
|
|
275
|
+
String(generateur.donnee_entree.reference_generateur_mixte) === String(reference)
|
|
276
|
+
)) ||
|
|
277
|
+
(generateursEcsMixtes.length === 1 ? generateursEcsMixtes[0] : undefined);
|
|
278
|
+
|
|
279
|
+
return jumeau?.donnee_entree.volume_stockage;
|
|
224
280
|
}
|
|
225
281
|
|
|
226
282
|
/**
|
|
@@ -128,3 +128,104 @@ describe('Puissance nominale estimée des générateurs de chauffage (issue #264
|
|
|
128
128
|
expect(service.pn(ctx, logement, installation)).toBeCloseTo(PCH_LOGEMENT, 9);
|
|
129
129
|
});
|
|
130
130
|
});
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Puissance nominale estimée par la puissance de dimensionnement : Pdim = max(Pch ; Pecs) puis table
|
|
134
|
+
* Pdim → Pn des chaudières (le calcul de la table est testé dans 13.2_generateur_combustion_pdim.spec.js).
|
|
135
|
+
* @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §13.2.2.4
|
|
136
|
+
*/
|
|
137
|
+
describe('Puissance nominale estimée : Pdim et table Pdim → Pn (issue #271)', () => {
|
|
138
|
+
/** @type {TvStore} */
|
|
139
|
+
let tvStore;
|
|
140
|
+
const ctx = {
|
|
141
|
+
altitude: { id: 1 },
|
|
142
|
+
zoneClimatique: { id: 1 },
|
|
143
|
+
surfaceHabitable: 100,
|
|
144
|
+
anneeConstruction: 1975
|
|
145
|
+
};
|
|
146
|
+
const installation = { donnee_entree: { surface_chauffee: 100, enum_type_installation_id: 1 } };
|
|
147
|
+
const logementAvecEcs = (generateursEcs) => ({
|
|
148
|
+
sortie: { deperdition: { deperdition_enveloppe: 200 } },
|
|
149
|
+
installation_ecs_collection: {
|
|
150
|
+
installation_ecs: [{ generateur_ecs_collection: { generateur_ecs: generateursEcs } }]
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
const generateur = (de) => ({ donnee_entree: de });
|
|
154
|
+
|
|
155
|
+
beforeEach(() => {
|
|
156
|
+
tvStore = new TvStore();
|
|
157
|
+
service = new GenerateurChService(new ChTvStore(), tvStore, new EmetteurChService());
|
|
158
|
+
vi.spyOn(tvStore, 'getTempBase').mockReturnValue(-9);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('chaudière de chauffage seul installée après 2015 : Pch = 7,84 kW → Pn = 10 kW', () => {
|
|
162
|
+
const logement = logementAvecEcs([]);
|
|
163
|
+
const gen = generateur({ enum_type_generateur_ch_id: '97', enum_usage_generateur_id: 1 });
|
|
164
|
+
expect(service.pn(ctx, logement, installation, gen)).toBe(10000);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('chaudière 1991-2000 (pose inconnue, avant 2006) : plancher 18 kW', () => {
|
|
168
|
+
const logement = logementAvecEcs([]);
|
|
169
|
+
const gen = generateur({ enum_type_generateur_ch_id: '88', enum_usage_generateur_id: 1 });
|
|
170
|
+
expect(service.pn(ctx, logement, installation, gen)).toBe(18000);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('chaudière mixte instantanée : Pdim = max(Pch ; 21 kW) → Pn = 24 kW', () => {
|
|
174
|
+
const logement = logementAvecEcs([
|
|
175
|
+
{ donnee_entree: { enum_usage_generateur_id: 2, volume_stockage: 300 } },
|
|
176
|
+
{ donnee_entree: { enum_usage_generateur_id: 3, volume_stockage: 0 } }
|
|
177
|
+
]);
|
|
178
|
+
const gen = generateur({ enum_type_generateur_ch_id: '97', enum_usage_generateur_id: 3 });
|
|
179
|
+
expect(service.pn(ctx, logement, installation, gen)).toBe(24000);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('radiateur gaz : Pn = Pdim (pas de table)', () => {
|
|
183
|
+
const logement = logementAvecEcs([]);
|
|
184
|
+
const gen = generateur({ enum_type_generateur_ch_id: '54', enum_usage_generateur_id: 1 });
|
|
185
|
+
expect(service.pn(ctx, logement, installation, gen)).toBeCloseTo(7837.877241580406, 9);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('installation collective : Pch conservée', () => {
|
|
189
|
+
const logement = logementAvecEcs([]);
|
|
190
|
+
const collective = { donnee_entree: { enum_type_installation_id: 2 } };
|
|
191
|
+
const gen = generateur({ enum_type_generateur_ch_id: '97', enum_usage_generateur_id: 1 });
|
|
192
|
+
expect(service.pn(ctx, logement, collective, gen)).toBeCloseTo(7837.877241580406, 9);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('volume de stockage du générateur ECS mixte jumeau (reference_generateur_mixte)', () => {
|
|
196
|
+
const ecs = (reference, volume) => ({
|
|
197
|
+
donnee_entree: {
|
|
198
|
+
enum_usage_generateur_id: '3',
|
|
199
|
+
reference_generateur_mixte: reference,
|
|
200
|
+
volume_stockage: volume
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
const logement = logementAvecEcs([ecs('A', 0), ecs('B', 200)]);
|
|
204
|
+
expect(service.volumeStockageEcsMixte(logement, { reference_generateur_mixte: 'B' })).toBe(200);
|
|
205
|
+
expect(service.volumeStockageEcsMixte(logement, {})).toBeUndefined();
|
|
206
|
+
expect(service.volumeStockageEcsMixte({}, {})).toBeUndefined();
|
|
207
|
+
expect(
|
|
208
|
+
service.volumeStockageEcsMixte(
|
|
209
|
+
{ installation_ecs_collection: { installation_ecs: [{}] } },
|
|
210
|
+
{ reference_generateur_mixte: 'B' }
|
|
211
|
+
)
|
|
212
|
+
).toBeUndefined();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('execute : le générateur est transmis à l’estimation de pn', () => {
|
|
216
|
+
vi.spyOn(service, 'pn').mockReturnValue(24000);
|
|
217
|
+
vi.spyOn(service, 'caracteristiques').mockReturnValue({});
|
|
218
|
+
const gen = {
|
|
219
|
+
donnee_entree: { enum_type_generateur_ch_id: '97', enum_lien_generateur_emetteur_id: 1 },
|
|
220
|
+
donnee_intermediaire: {}
|
|
221
|
+
};
|
|
222
|
+
const inst = {
|
|
223
|
+
donnee_entree: { enum_type_installation_id: 1 },
|
|
224
|
+
generateur_chauffage_collection: { generateur_chauffage: [gen] }
|
|
225
|
+
};
|
|
226
|
+
const logement = logementAvecEcs([]);
|
|
227
|
+
service.execute(ctx, logement, inst);
|
|
228
|
+
expect(service.pn).toHaveBeenCalledWith(ctx, logement, inst, gen);
|
|
229
|
+
expect(gen.donnee_intermediaire.pn).toBe(24000);
|
|
230
|
+
});
|
|
231
|
+
});
|