Skip to content
Merged
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
25 changes: 17 additions & 8 deletions src/15_conso_aux.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,28 @@ function getPuissanceCirculateur(em_ch, de, di, du, surfaceHabitable, GV, Tbase)

const nbNiveauChauffage = de.nombre_niveau_installation_ch || 1;

/**
* Surface desservie par l'installation : le réseau (Lem) et le nombre de circulateurs
* (Sh / 400) sont ceux de l'installation, pas ceux du bâtiment entier (ex. immeuble à
* installations individuelles par logement), comme le moteur de référence CSTB (Tribu).
*
* En DPE immeuble, une installation décrite représente rdim logements identiques : le
* circulateur est dimensionné pour un logement (surface / rdim) puis multiplié par rdim
* (Tribu, Calcul_batiment.cs : Caux_dist_ch += Caux_dist_ch_installation × Rdim).
*/
const rdim = de.rdim_installation_ch || 1;
const surfaceInstallation = (de.surface_chauffee || surfaceHabitable) / rdim;
const nbCirculateurs = Math.max(1, surfaceInstallation / 400);

// Calcul de la longueur du réseau le plus défavorisé
const Lem = 5 * Fcot * (nbNiveauChauffage + (surfaceHabitable / nbNiveauChauffage) ** 0.5);
const Lem = 5 * Fcot * (nbNiveauChauffage + (surfaceInstallation / nbNiveauChauffage) ** 0.5);

// Pertes de charge du réseau (kPa)
const deltaPemnom = 0.15 * Lem + deltaPem;

// Ratio du besoin couvert par l’équipement
const nbGenerateurCascade = du.nbGenerateurCascade || 1;
const ratioSurfaceChauffage =
(de.surface_chauffee || surfaceHabitable) / (surfaceHabitable * nbGenerateurCascade);
const ratioSurfaceChauffage = surfaceInstallation / (surfaceHabitable * nbGenerateurCascade);

// Chute nominale de température de dimensionnement
const deltaDim = getDeltaDim(em_ch);
Expand All @@ -255,11 +267,8 @@ function getPuissanceCirculateur(em_ch, de, di, du, surfaceHabitable, GV, Tbase)

const Qvemnom = (Pnc * ratioSurfaceChauffage) / (1.163 * deltaDim);

return Math.max(
30,
6.44 *
((deltaPemnom * Qvemnom) / Math.max(1, surfaceHabitable / 400)) ** 0.676 *
Math.max(1, surfaceHabitable / 400)
return (
rdim * Math.max(30, 6.44 * ((deltaPemnom * Qvemnom) / nbCirculateurs) ** 0.676 * nbCirculateurs)
);
}

Expand Down
67 changes: 67 additions & 0 deletions src/15_conso_aux.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,73 @@ describe('conso_aux_distribution_ch - auxiliaires de distribution', () => {
// Pcirc ∝ Qvemnom^0,676 et Qvemnom ∝ 1/ΔθDim : diviser ΔθDim par 2 multiplie Pcirc par 2^0,676
expect(toutBasse / bassepuisHaute).toBeCloseTo(2 ** 0.676, 9);
});

/**
* Cas des autotests CSTB IC1-0-1/11/12 : immeuble de 175 m² à installations individuelles,
* l'installation étudiée dessert un logement de 80 m². Lem et le nombre de circulateurs sont
* ceux de l'installation (80 m²), pas ceux de l'immeuble (Tribu : Pcirc = 46,594 W).
*/
test("installation partielle : Lem calculée sur la surface desservie par l'installation", () => {
const calcul = (de, sh) => {
const di = {};
conso_aux_distribution_ch(emCh, de, di, {}, sh, 1, 1, '0', 100000);
return di.conso_auxiliaire_distribution_ch;
};
const partielle = calcul({ surface_chauffee: 80 }, 175);
// valeur de référence de régression
expect(partielle).toBeCloseTo(125.15085008075532, 9);
// Lem dépend de la seule surface de l'installation : même réseau que pour un logement seul
// de 80 m² recevant la même puissance (GV × 80 / 175)
const di = {};
conso_aux_distribution_ch(emCh, {}, di, {}, 80, 1, 1, '0', (100000 * 80) / 175);
expect(partielle).toBeCloseTo(di.conso_auxiliaire_distribution_ch, 9);
});

test("installation de plus de 400 m² : nombre de circulateurs selon la surface de l'installation", () => {
const calcul = (de, sh) => {
const di = {};
conso_aux_distribution_ch(emCh, de, di, {}, sh, 1, 1, '0', 1000000);
return di.conso_auxiliaire_distribution_ch;
};
const partielle = calcul({ surface_chauffee: 800 }, 1600);
const di = {};
conso_aux_distribution_ch(emCh, {}, di, {}, 800, 1, 1, '0', 500000);
expect(partielle).toBeCloseTo(di.conso_auxiliaire_distribution_ch, 9);
});

/**
* DPE immeuble : une installation décrite représente rdim logements identiques. Tribu
* dimensionne le circulateur d'un logement (surface / rdim) puis multiplie la consommation
* par rdim (Calcul_batiment.cs : Caux_dist_ch += Caux_dist_ch_installation × Rdim).
* Cas réel 2437E4022989X (LICIEL) : 4 installations de 281,5 m², rdim 4 → 2 699,04 kWh.
*/
test('immeuble, installation représentant rdim logements : circulateur par logement × rdim', () => {
const calcul = (de, sh, gv) => {
const di = {};
conso_aux_distribution_ch(emCh, de, di, {}, sh, 1, 1, '0', gv);
return di.conso_auxiliaire_distribution_ch;
};
const avecRdim = calcul({ surface_chauffee: 800, rdim_installation_ch: 4 }, 1600, 1000000);
// un logement de 200 m² recevant la puissance de 200 m² sur 1 600 m², multiplié par 4
const logement = calcul({}, 200, (1000000 * 200) / 1600);
expect(avecRdim).toBeCloseTo(4 * logement, 9);
// valeur de référence de régression
expect(avecRdim).toBeCloseTo(1114.9777138082252, 9);
// sans rdim, l'installation de 800 m² aurait un seul réseau de 800 m²
expect(calcul({ surface_chauffee: 800 }, 1600, 1000000)).not.toBeCloseTo(avecRdim, 3);
});

test('rdim égal à 1 : aucun effet', () => {
const calcul = (de) => {
const di = {};
conso_aux_distribution_ch(emCh, de, di, {}, 175, 1, 1, '0', 100000);
return di.conso_auxiliaire_distribution_ch;
};
expect(calcul({ surface_chauffee: 80, rdim_installation_ch: 1 })).toBeCloseTo(
calcul({ surface_chauffee: 80 }),
12
);
});
});

/**
Expand Down
11 changes: 11 additions & 0 deletions src/9_chauffage.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ import enums from './enums.js';
/**
* @param dpe {FullDpe}
*/
/**
* DPE immeuble (même règle que calc_th du moteur : « appartement » ou « maison » priment).
* @param map_id {string|number} enum_methode_application_dpe_log_id
* @return {boolean}
*/
export function isImmeuble(map_id) {
const map = enums.methode_application_dpe_log?.[map_id] || '';
return map.includes('immeuble') && !map.includes('appartement') && !map.includes('maison');
}

export default function calc_chauffage(
dpe,
ch,
Expand Down Expand Up @@ -60,6 +70,7 @@ export default function calc_chauffage(
genChDe.ratio_virtualisation = de.ratio_virtualisation || 1;
genChDe.cle_repartition_ch = de.cle_repartition_ch || 1;
genChDe.surface_chauffee = de.surface_chauffee || Sh;
genChDe.rdim_installation_ch = isImmeuble(map_id) ? Number(de.rdim) || 1 : 1;
genChDe.nombre_niveau_installation_ch = de.nombre_niveau_installation_ch || 1;
genChDe.fch = Fch || 0.5;

Expand Down
46 changes: 45 additions & 1 deletion src/9_chauffage.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,20 @@ vi.mock('./13.2_generateur_combustion_ch.js', () => ({
vi.mock('./enums.js', () => ({
default: {
classe_altitude: { 1: 'ca1', 2: 'ca2' },
methode_application_dpe_log: {
2: 'dpe appartement individuel chauffage individuel ecs individuel',
6: 'dpe immeuble collectif chauffage individuel ecs individuel',
10: 'dpe appartement généré à partir des données dpe immeuble chauffage individuel ecs individuel'
},
zone_climatique: { 1: 'h1a', 2: 'h2b', 3: 'h3c' }
}
}));

const { default: calc_chauffage, tauxChargeForGenerator } = await import('./9_chauffage.js');
const {
default: calc_chauffage,
tauxChargeForGenerator,
isImmeuble
} = await import('./9_chauffage.js');
const { requestInput, tv, tvColumnIDs } = await import('./utils.js');
const { calc_emetteur_ch } = await import('./9_emetteur_ch.js');
const { calc_generateur_ch, checkForGeneratorType, hasConsoForAuxDistribution } =
Expand Down Expand Up @@ -234,6 +243,41 @@ describe('calc_chauffage - mise à l’échelle du besoin selon la surface', ()
});
});

describe('calc_chauffage - rdim de l’installation transmis au générateur', () => {
const rdimGen = (ch) =>
ch.generateur_chauffage_collection.generateur_chauffage[0].donnee_entree.rdim_installation_ch;

test('DPE immeuble : rdim de l’installation repris', () => {
const ch = installation([generateur()], [emetteur('1')], { rdim: '4' });
appel(ch, { map_id: 6 });
expect(rdimGen(ch)).toBe(4);
});

test('DPE immeuble sans rdim : 1', () => {
const ch = installation([generateur()], [emetteur('1')]);
appel(ch, { map_id: 6 });
expect(rdimGen(ch)).toBe(1);
});

test('DPE appartement (y compris issu d’un DPE immeuble) : rdim ignoré', () => {
const ch = installation([generateur()], [emetteur('1')], { rdim: '4' });
appel(ch, { map_id: 10 });
expect(rdimGen(ch)).toBe(1);
appel(ch, { map_id: 2 });
expect(rdimGen(ch)).toBe(1);
});
});

describe('isImmeuble', () => {
test('immeuble uniquement, pas appartement issu d’un immeuble ni méthode inconnue', () => {
expect(isImmeuble(6)).toBe(true);
expect(isImmeuble('6')).toBe(true);
expect(isImmeuble(10)).toBe(false);
expect(isImmeuble(2)).toBe(false);
expect(isImmeuble(999)).toBe(false);
});
});

describe('calc_chauffage - facteur de couverture solaire (Fch)', () => {
test('facteur saisi directement pris tel quel', () => {
const ch = installation([generateur()], [emetteur('1')]);
Expand Down
Loading