Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/11_besoin_ecs.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ export function calc_besoin_ecs_j(ca, mois, zc, nadeq, depensier) {
export default function calc_besoin_ecs(ca, zc, nadeq) {
const ret = {
besoin_ecs: 0,
besoin_ecs_depensier: 0
besoin_ecs_depensier: 0,
besoin_ecs_par_mois: {}
};
for (const mois of mois_liste) {
ret.besoin_ecs += calc_besoin_ecs_j(ca, mois, zc, nadeq, false);
const becs_j = calc_besoin_ecs_j(ca, mois, zc, nadeq, false);
ret.besoin_ecs += becs_j;
ret.besoin_ecs_depensier += calc_besoin_ecs_j(ca, mois, zc, nadeq, true);
ret.besoin_ecs_par_mois[mois] = becs_j;
}
return ret;
}
101 changes: 99 additions & 2 deletions src/11_ecs.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import enums from './enums.js';
import calc_gen_ecs from './14_generateur_ecs.js';
import { tv, requestInput } from './utils.js';
import { tv, requestInput, mois_liste, Njj } from './utils.js';

function tv_rendement_distribution_ecs(di, de, du, pvc) {
let matcher = {};
Expand Down Expand Up @@ -49,6 +49,89 @@ function tv_rendement_distribution_ecs(di, de, du, pvc) {
}
}

/**
* Calcul de la consommation des auxiliaires de distribution ECS - CAS IMMEUBLE
* Calcul installation par installation pour les réseaux collectifs bouclés ou avec traçage.
*
* @param {object} de - donnee_entree de l'installation ECS
* @param {number} surfaceImmeuble - surface habitable totale de l'immeuble (m²)
* @param {object} becsParMois - besoin ECS mensuel de l'immeuble {Janvier: X, Février: X, ...} [kWh]
* @returns {number} conso_auxiliaire_distribution_ecs_installation [Wh]
*/
function calc_auxiliaire_distribution_ecs_immeuble(de, surfaceImmeuble, becsParMois) {
const type_installation = enums.type_installation[de.enum_type_installation_id];

// Seulement pour les installations collectives
if (!type_installation || !type_installation.includes('collective')) {
return 0;
}

const enum_bouclage_id = String(de.enum_bouclage_reseau_ecs_id);
const surface_installation = de.surface_habitable || 0;
const surface_immeuble_total = surfaceImmeuble || 1;
const ratio_surface = surface_installation / surface_immeuble_total;

// CAS 1 : réseau non bouclé → 0
if (enum_bouclage_id === '1') {
return 0;
}

// CAS 3 : traçage → 0.14 × BECS_annuel × ratio_surface [Wh]
if (enum_bouclage_id === '3') {
const becs_annuel_kwh = Object.values(becsParMois).reduce((acc, v) => acc + v, 0);
// becs_annuel est en kWh → × 1000 pour obtenir des Wh
return 0.14 * becs_annuel_kwh * 1000 * ratio_surface;
}

// CAS 2 : réseau bouclé → calcul complet 9 étapes [Wh]
if (enum_bouclage_id === '2') {
const Sh_inst = surface_installation;
const Niv_inst = Number(de.nombre_niveau_installation_ecs) || 1;

// Étape 3 : Calcul de Lb pour l'installation i
// Lb = 1.2 × (1.1 × Sh_inst/nombre_niveaux + 4 × nombre_niveaux)
const Lb = 1.2 * (1.1 * (Sh_inst / Niv_inst) + 4 * Niv_inst);

// Étape 4 : DeltaPb = 0.2 × Lb + 10
const DeltaPb = 0.2 * Lb + 10;

let Qcirc_annuel = 0;

for (const mois of mois_liste) {
const Njj_mois = Njj[mois];
const becs_immeuble_j_kwh = becsParMois[mois] || 0;

// Étape 1 : Qdwj_i [kWh]
const Qdwj_i = 0.24 * becs_immeuble_j_kwh * ratio_surface;

// Étape 2 : qdwj_i [m³/h]
// Note: la spec officielle utilise Qdwj en Wh et divise par 1000 ensuite.
// Le moteur fournit becs en kWh, donc Qdwj est en kWh → on ne divise pas par 1000.
const qdwj_i = Qdwj_i / (5.815 * 5 * Njj_mois);

// Étape 5 : Phyd,j [W]
// Phyd,j = DeltaPb [kPa] × 1000 × qdwj_i [m³/h] / 3600
const Phyd_j = (DeltaPb * 1000 * qdwj_i) / 3600;

// Étape 6 : Effcirb,j (rendement pompe)
const Effcirb_j = Phyd_j > 0 ? 0.035 * Math.pow(Phyd_j, 0.45) : 0;

// Étape 7 : Pcirb,j [W] = max(Phyd,j / Effcirb,j, 20)
const Pcirb_j = Effcirb_j > 0 ? Math.max(Phyd_j / Effcirb_j, 20) : 20;

// Étape 8 : Qcirb,j [Wh]
const Qcirb_j = (5 * Pcirb_j + 19 * 20) * Njj_mois;

Qcirc_annuel += Qcirb_j;
}

// Étape 9 : conso annuelle en Wh
return Qcirc_annuel;
}

return 0;
}

export default function calc_ecs(
dpe,
ecs,
Expand All @@ -61,7 +144,8 @@ export default function calc_ecs(
virtualisationECS,
surfaceImmeuble,
nombreAppartements,
isImmeubleSystemEcsIndividuels
isImmeubleSystemEcsIndividuels,
becsParMois
) {
const de = ecs.donnee_entree;
const di = {};
Expand Down Expand Up @@ -101,6 +185,19 @@ export default function calc_ecs(
0
);

// Calcul de la consommation des auxiliaires de distribution ECS pour l'immeuble
// Résultat en Wh, converti en kWh pour cohérence avec le reste du moteur
if (th === 'immeuble' && becsParMois && Object.keys(becsParMois).length > 0) {
const conso_aux_wh = calc_auxiliaire_distribution_ecs_immeuble(
de,
surfaceImmeuble,
becsParMois
);
di.conso_auxiliaire_distribution_ecs = conso_aux_wh / 1000;
} else {
di.conso_auxiliaire_distribution_ecs = 0;
}

ecs.donnee_intermediaire = di;
ecs.donnee_utilisateur = du;
}
23 changes: 16 additions & 7 deletions src/conso.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ export default function calc_conso(
'conso',
null,
prorataECS,
prorataChauffage
prorataChauffage,
ecs
),
ep_conso: calc_conso_pond(
Sh,
Expand All @@ -255,7 +256,8 @@ export default function calc_conso(
'ep_conso',
coeffEp,
prorataECS,
prorataChauffage
prorataChauffage,
ecs
),
emission_ges: calc_conso_pond(
Sh,
Expand All @@ -267,7 +269,8 @@ export default function calc_conso(
'emission_ges',
coef_ges,
prorataECS,
prorataChauffage
prorataChauffage,
ecs
),
cout: calc_conso_pond(
Sh,
Expand All @@ -279,7 +282,8 @@ export default function calc_conso(
'cout',
coef_cout,
prorataECS,
prorataChauffage
prorataChauffage,
ecs
)
};
ret.ep_conso.classe_bilan_dpe = classe_bilan_dpe(
Expand Down Expand Up @@ -334,7 +338,8 @@ export default function calc_conso(
'',
null,
prorataECS,
prorataChauffage
prorataChauffage,
type_energie === 'électricité' ? ecs : []
);
conso_en = {
conso_ch: conso_en._ch,
Expand Down Expand Up @@ -464,7 +469,8 @@ function calc_conso_pond(
prefix,
coef,
prorataECS,
prorataChauffage
prorataChauffage,
ecs_installations
) {
const ret = {};
ret.auxiliaire_ventilation = vt_list.reduce((acc, vt) => {
Expand Down Expand Up @@ -528,7 +534,10 @@ function calc_conso_pond(
return acc + getConso(coef, 'électricité auxiliaire', conso);
}, 0);

ret.auxiliaire_distribution_ecs = 0;
ret.auxiliaire_distribution_ecs = (ecs_installations || []).reduce((acc, inst) => {
const conso = (inst.donnee_intermediaire || {}).conso_auxiliaire_distribution_ecs || 0;
return acc + getConso(coef, 'électricité auxiliaire', conso);
}, 0);

ret.ecs = getEcsConso(gen_ecs, 'conso_ecs', coef, prorataECS, prefix);

Expand Down
3 changes: 2 additions & 1 deletion src/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,8 @@ export function calcul_3cl(inputDpe, options) {
virtualisationECS,
dpe.logement.caracteristique_generale.surface_habitable_immeuble,
dpe.logement.caracteristique_generale.nombre_appartement,
isImmeubleSystemEcsIndividuels
isImmeubleSystemEcsIndividuels,
apport_et_besoin.besoin_ecs_par_mois || {}
);
});

Expand Down
79 changes: 79 additions & 0 deletions test/open3cl_conso_auxiliaire_distribution_ecs.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { calcul_3cl } from '../src/engine.js';
import { getAdemeFileJsonOrDownload } from './test-helpers.js';
import { set_bug_for_bug_compat } from '../src/utils.js';
import { describe, expect, test } from 'vitest';

/**
* Tests pour la consommation des auxiliaires de distribution ECS en immeuble (issue #157)
*
* La consommation d'auxiliaire se calcule INSTALLATION PAR INSTALLATION :
* - CAS 1 (enum_bouclage_reseau_ecs_id=1, réseau non bouclé) : 0
* - CAS 2 (enum_bouclage_reseau_ecs_id=2, réseau bouclé) : calcul 9 étapes
* - CAS 3 (enum_bouclage_reseau_ecs_id=3, traçage) : 0.14 × BECS_annuel × ratio_surface [Wh]
*
* DPEs de référence (résultats vérifiés) :
* - 2592E0278308W : 2 installations bouclées, total = 400.5335739 kWh
* - 2231E1326114Q : 1 installation avec traçage, total = 2504.613341 kWh
*/
describe('Conso auxiliaire distribution ECS - Cas immeuble (issue #157)', () => {
test('DPE immeuble bouclé (2592E0278308W) : conso auxiliaire distribution ECS ≈ 400.53 kWh', async () => {
set_bug_for_bug_compat();
const input = await getAdemeFileJsonOrDownload('2592E0278308W');
const output = calcul_3cl(structuredClone(input));

// La conso totale de l'auxiliaire de distribution ECS pour l'immeuble
// est la somme des deux installations (400.5335739 kWh)
const ef_conso = output.logement.sortie.ef_conso;
expect(ef_conso.conso_auxiliaire_distribution_ecs).toBeCloseTo(400.53, 0);

// Vérification par installation
const ecs_list = output.logement.installation_ecs_collection.installation_ecs;
expect(ecs_list).toHaveLength(2);

// Installation 1 : Sh=1633, Niv=4, bouclage=2 → ~228.33 kWh
const inst1_di = ecs_list[0].donnee_intermediaire;
expect(inst1_di.conso_auxiliaire_distribution_ecs).toBeGreaterThan(0);

// Installation 2 : Sh=408, Niv=1, bouclage=2 → ~172.20 kWh
const inst2_di = ecs_list[1].donnee_intermediaire;
expect(inst2_di.conso_auxiliaire_distribution_ecs).toBeGreaterThan(0);

// Total des deux installations
const total =
inst1_di.conso_auxiliaire_distribution_ecs + inst2_di.conso_auxiliaire_distribution_ecs;
expect(total).toBeCloseTo(400.53, 0);
});

test('DPE immeuble traçage (2231E1326114Q) : conso auxiliaire distribution ECS ≈ 2504.61 kWh', async () => {
set_bug_for_bug_compat();
const input = await getAdemeFileJsonOrDownload('2231E1326114Q');
const output = calcul_3cl(structuredClone(input));

const ef_conso = output.logement.sortie.ef_conso;
expect(ef_conso.conso_auxiliaire_distribution_ecs).toBeCloseTo(2504.61, 0);

// Vérification par installation
const ecs_list = output.logement.installation_ecs_collection.installation_ecs;
expect(ecs_list).toHaveLength(1);
expect(ecs_list[0].donnee_intermediaire.conso_auxiliaire_distribution_ecs).toBeCloseTo(
2504.61,
0
);
});

test('DPE immeuble réseau non bouclé : conso auxiliaire distribution ECS = 0', async () => {
// Un DPE immeuble avec enum_bouclage_reseau_ecs_id=1 doit avoir 0
// On utilise un DPE de référence dont on sait qu'il est non bouclé
set_bug_for_bug_compat();
const input = await getAdemeFileJsonOrDownload('2321E0998807O');
// Ce DPE a enum_bouclage=2 (bouclé), on vérifie juste que la valeur est > 0
const output = calcul_3cl(structuredClone(input));
const ecs_list = output.logement.installation_ecs_collection.installation_ecs;
const bouclage = ecs_list[0]?.donnee_entree?.enum_bouclage_reseau_ecs_id;
if (String(bouclage) === '1') {
expect(ecs_list[0].donnee_intermediaire.conso_auxiliaire_distribution_ecs).toBe(0);
} else if (String(bouclage) === '2') {
expect(ecs_list[0].donnee_intermediaire.conso_auxiliaire_distribution_ecs).toBeGreaterThan(0);
}
});
});
Loading