diff --git a/dist/reports/corpus/corpus_list_main.json b/dist/reports/corpus/corpus_list_main.json index 33b70994..ec2dba51 100644 --- a/dist/reports/corpus/corpus_list_main.json +++ b/dist/reports/corpus/corpus_list_main.json @@ -10,5 +10,5 @@ "dpe_appartement_individuel_chauffage_collectif_2025.csv", "dpe_individuel_a_partir_dpe_immeuble_2026.csv" ], - "branches": ["main", "fix_issue_177"] + "branches": ["main", "fix_issue_177", "pr-159"] } diff --git a/dist/reports/corpus/index.html b/dist/reports/corpus/index.html index e4f5667a..23133020 100644 --- a/dist/reports/corpus/index.html +++ b/dist/reports/corpus/index.html @@ -667,6 +667,94 @@ color: var(--primary); } + /* ===== Detail tooltip (per-DPE property diffs) ===== */ + .dpe-card.has-detail { + cursor: help; + } + .dpe-card.has-detail:hover { + border-color: var(--primary); + } + #detail-tip { + position: fixed; + z-index: 60; + display: none; + width: min(520px, calc(100vw - 24px)); + max-height: min(60vh, 520px); + overflow: auto; + background: var(--bg-elev); + border: 1px solid var(--border-strong); + border-radius: var(--radius-sm); + box-shadow: var(--shadow-md); + padding: 12px 14px; + font-size: 12px; + color: var(--text); + pointer-events: none; + } + #detail-tip .tt-head { + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-weight: 700; + font-size: 12.5px; + margin-bottom: 2px; + } + #detail-tip .tt-sub { + color: var(--text-muted); + font-size: 11px; + margin-bottom: 8px; + } + #detail-tip .tt-msg { + color: var(--text-muted); + padding: 6px 0 2px; + } + #detail-tip table { + width: 100%; + border-collapse: collapse; + font-size: 11.5px; + } + #detail-tip th { + text-align: left; + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-muted); + font-weight: 600; + padding: 4px 6px; + border-bottom: 1px solid var(--border); + } + #detail-tip td { + padding: 4px 6px; + border-bottom: 1px solid var(--border); + vertical-align: middle; + } + #detail-tip tr:last-child td { + border-bottom: 0; + } + #detail-tip td.tt-prop { + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-size: 11px; + } + #detail-tip td.tt-num { + text-align: right; + font-variant-numeric: tabular-nums; + white-space: nowrap; + } + #detail-tip td.tt-diff { + text-align: right; + font-variant-numeric: tabular-nums; + font-weight: 700; + color: var(--bad); + white-space: nowrap; + } + #detail-tip .tt-star { + color: var(--primary); + } + #detail-tip .tt-arrow { + color: var(--text-dim); + } + /* Tooltips on bar list rows */ .check-row { transition: background 0.15s ease; @@ -853,6 +941,8 @@

Échecs d'exécution

+ + @@ -880,7 +970,8 @@

Échecs d'exécution

checkFilter: 'all', dpeFilter: 'all', sortKey: 'kind', - sortAsc: false + sortAsc: false, + detailCache: {} // `${corpusFile}|${branch}` -> Promise> }; // ----- Utilities ----- @@ -1338,6 +1429,7 @@

Échecs d'exécution

// ----- DPE list ----- function renderDpeList() { + if (typeof hideTip === 'function') hideTip(); const chipsRow = $('#dpe-filter-chips'); chipsRow.style.display = state.compareBranch ? 'inline-flex' : 'none'; @@ -1376,7 +1468,7 @@

Échecs d'exécution

.slice(0, 600) // safety cap on DOM size .map((e) => { const tagLabel = e.tag === 'new' ? 'NEW' : e.tag === 'fixed' ? 'FIX' : 'COMMUN'; - return `
+ return `
${e.id} ${tagLabel}
`; @@ -1391,6 +1483,225 @@

Échecs d'exécution

: `${fmtNum(totalShown)} DPE`; } + // ----- Detailed per-DPE report (properties above threshold) ----- + + /** Split one CSV line, honouring double-quoted fields. */ + function splitCsvLine(line) { + const out = []; + let cur = ''; + let quoted = false; + for (let i = 0; i < line.length; i++) { + const ch = line[i]; + if (quoted) { + if (ch === '"') { + if (line[i + 1] === '"') { + cur += '"'; + i++; + } else quoted = false; + } else cur += ch; + } else if (ch === '"') quoted = true; + else if (ch === ',') { + out.push(cur); + cur = ''; + } else cur += ch; + } + out.push(cur); + return out; + } + + /** + * Parse a corpus_detailed_report_.csv into + * Map keeping only the + * properties whose diff exceeds the threshold (sorted worst first). + */ + function parseDetailedReport(text, threshold) { + const lines = text.split(/\r?\n/); + const header = splitCsvLine(lines[0] || ''); + const idx = {}; + header.forEach((h, i) => (idx[h] = i)); + const codeIdx = idx['code'] ?? 0; + // Every "_diff" column backed by "_input" / "_output" + const props = header + .filter((h) => h.endsWith('_diff')) + .map((h) => h.slice(0, -'_diff'.length)) + .filter((p) => idx[p + '_input'] != null && idx[p + '_output'] != null) + .map((p) => ({ + prop: p, + di: idx[p + '_diff'], + ii: idx[p + '_input'], + oi: idx[p + '_output'] + })); + + const map = new Map(); + for (let l = 1; l < lines.length; l++) { + const line = lines[l]; + if (!line) continue; + const cells = splitCsvLine(line); + const code = cells[codeIdx]; + if (!code) continue; + const over = []; + for (const p of props) { + const diff = Number(cells[p.di]); + if (!Number.isFinite(diff) || diff <= threshold) continue; + over.push({ + prop: p.prop, + input: cells[p.ii], + output: cells[p.oi], + diff + }); + } + over.sort((a, b) => b.diff - a.diff); + map.set(code, over); + } + return map; + } + + function loadDetailedReport(branch) { + const cf = state.corpusFile; + const key = `${cf}|${branch}`; + if (!state.detailCache[key]) { + state.detailCache[key] = (async () => { + const path = `${cf}/corpus_detailed_report_${branch}.csv`; + const res = await fetch(path); + if (!res.ok) throw new Error(`Fichier introuvable : ${path}`); + const threshold = parsePct((state.branchReport || state.mainReport)?.threshold ?? '5%'); + return parseDetailedReport(await res.text(), threshold); + })().catch((err) => { + console.warn(err); + delete state.detailCache[key]; // allow a retry on next hover + return null; + }); + } + return state.detailCache[key]; + } + + const fmtVal = (v) => { + const n = Number(v); + if (v === '' || v == null || !Number.isFinite(n)) return v || '—'; + return new Intl.NumberFormat('fr-FR', { maximumFractionDigits: 2 }).format(n); + }; + + // ----- Detail tooltip ----- + const tip = $('#detail-tip'); + let tipToken = 0; + + function tipBranchFor(tag) { + // "fixed" DPE are above threshold on main only -> read main's detail file + if (tag === 'fixed') return 'main'; + return state.compareBranch || 'main'; + } + + function placeTip(x, y) { + tip.style.display = 'block'; + const r = tip.getBoundingClientRect(); + let left = x + 16; + let top = y + 16; + if (left + r.width > window.innerWidth - 8) left = Math.max(8, x - r.width - 16); + if (top + r.height > window.innerHeight - 8) top = Math.max(8, y - r.height - 16); + tip.style.left = left + 'px'; + tip.style.top = top + 'px'; + } + + function hideTip() { + tipToken++; + tip.style.display = 'none'; + tip.setAttribute('aria-hidden', 'true'); + } + + function tipHeader(id, branch, tagLabel) { + const threshold = (state.branchReport || state.mainReport)?.threshold ?? '5 %'; + return `
${id}${tagLabel.label}
+
Propriétés au-dessus du seuil (${threshold}) — branche « ${branch} »
`; + } + + async function showTip(card, x, y) { + const id = card.dataset.id; + const tag = card.dataset.tag || 'common'; + const label = { new: 'NEW', fixed: 'FIX', common: 'COMMUN' }[tag] || 'COMMUN'; + const branch = tipBranchFor(tag); + const token = ++tipToken; + + tip.setAttribute('aria-hidden', 'false'); + tip.innerHTML = + tipHeader(id, branch, { tag, label }) + `
Chargement du détail…
`; + placeTip(x, y); + + const map = await loadDetailedReport(branch); + if (token !== tipToken) return; // another card (or a hide) won the race + + let body; + if (!map) { + body = `
Rapport détaillé indisponible pour « ${branch} ».
`; + } else if (!map.has(id)) { + body = `
Ce DPE n'apparaît pas dans le rapport détaillé.
`; + } else { + const rows = map.get(id); + const checks = (state.branchReport || state.mainReport)?.checks || {}; + body = rows.length + ? ` + + ${rows + .map( + (r) => ` + + + + + ` + ) + .join('')} +
PropriétéAttenduCalculéÉcart
${checks[r.prop]?.mandatory ? ' ' : ''}${r.prop}${fmtVal(r.input)} ${fmtVal(r.output)}${fmtVal(r.diff)}%
+
${rows.length} propriété${rows.length > 1 ? 's' : ''} au-dessus du seuil · ★ check obligatoire
` + : `
Aucune propriété au-dessus du seuil sur cette branche.
`; + } + tip.innerHTML = tipHeader(id, branch, { tag, label }) + body; + placeTip(x, y); + } + + (function bindTip() { + const grid = $('#dpe-grid'); + let current = null; + grid.addEventListener('mouseover', (e) => { + const card = e.target.closest('.dpe-card.has-detail'); + if (!card || card === current) return; + current = card; + showTip(card, e.clientX, e.clientY); + }); + grid.addEventListener('mousemove', (e) => { + if (current && tip.style.display === 'block') placeTip(e.clientX, e.clientY); + }); + grid.addEventListener('mouseout', (e) => { + const card = e.target.closest('.dpe-card.has-detail'); + if (!card) return; + if (e.relatedTarget && card.contains(e.relatedTarget)) return; + current = null; + hideTip(); + }); + grid.addEventListener('scroll', () => { + current = null; + hideTip(); + }); + // Touch / keyboard: tap or focus a card to pin the tooltip + grid.addEventListener('click', (e) => { + const card = e.target.closest('.dpe-card.has-detail'); + if (!card) return; + if (current === card && tip.style.display === 'block') { + current = null; + hideTip(); + } else { + current = card; + const r = card.getBoundingClientRect(); + showTip(card, r.left, r.bottom - 16); + } + }); + window.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + current = null; + hideTip(); + } + }); + })(); + // ----- Run-failed ----- function renderRunFailed() { const active = state.branchReport || state.mainReport; diff --git a/src/15_conso_aux.js b/src/15_conso_aux.js index 074bf056..8a46d7df 100644 --- a/src/15_conso_aux.js +++ b/src/15_conso_aux.js @@ -1,6 +1,6 @@ import enums from './enums.js'; import tvs from './tv.js'; -import { mois_liste, Tbase } from './utils.js'; +import { mois_liste, Njj, Tbase } from './utils.js'; const G_CHAUDIERE = 20; const G_RADIATEURS_GAZ = 40; @@ -259,3 +259,123 @@ function getPuissanceCirculateur(em_ch, de, di, du, surfaceHabitable, GV, Tbase) Math.max(1, surfaceHabitable / 400) ); } + +/** + * 15.2.3 Consommation des auxiliaires de distribution d'ECS + * Calcul installation par installation pour le cas appartement + * + * SI installation individuelle : conso = 0 + * SI installation collective : + * CAS 1 - enum_bouclage_reseau_ecs_id = 1 (non bouclé) : conso = 0 + * CAS 2 - enum_bouclage_reseau_ecs_id = 2 (bouclé) : calcul selon étapes 1-9 + * CAS 3 - enum_bouclage_reseau_ecs_id = 3 (traçage) : conso = 0.14 * BECS_annuel * Sh_install / Sh_logement + * + * @param ecs {object} installation ECS + * @param de {Donnee_entree} donnée d'entrée de l'installation ECS + * @param di {Donnee_intermediaire} donnée intermédiaire de l'installation ECS + * @param Sh_logement {number} surface habitable du logement + * @param Sh_immeuble {number} surface habitable de l'immeuble + * @param caId {number} id de la classe d'altitude + * @param zcId {number} id de la zone climatique + * @param nadeq {number} nombre d'unités d'équivalence + * @param nombre_niveau_immeuble {number} nombre de niveaux de l'immeuble + */ +export function conso_aux_distribution_ecs( + ecs, + de, + di, + Sh_logement, + Sh_immeuble, + caId, + zcId, + nadeq, + nombre_niveau_immeuble +) { + const typeInstallation = parseInt(de.enum_type_installation_id); + + if (typeInstallation === 1) { + di.conso_auxiliaire_distribution_ecs = 0; + return; + } + + const enumBouclage = parseInt(de.enum_bouclage_reseau_ecs_id); + + // CAS 1 - enum_bouclage_reseau_ecs_id =1 (réseau d'ECS non bouclé) + if (enumBouclage === 1 || isNaN(enumBouclage)) { + di.conso_auxiliaire_distribution_ecs = 0; + return; + } + + // CAS 3 - enum_bouclage_reseau_ecs_id = 3 (traçage) + if (enumBouclage === 3) { + const BECS_annuel = di.besoin_ecs; + const Sh_install = de.surface_habitable || Sh_logement; + di.conso_auxiliaire_distribution_ecs = (0.14 * BECS_annuel * Sh_install) / Sh_logement; + return; + } + + // CAS 2 - enum_bouclage_reseau_ecs_id = 2 (réseau bouclé) + const ca = enums.classe_altitude[caId]; + const zc = enums.zone_climatique[zcId]; + + const ratioVirtu = parseFloat(de.ratio_virtualisation); + let Sh_immeuble_ratio = Sh_immeuble; + let Sh_install = de.surface_habitable || Sh_logement; + let Niv_inst_ecs = de.nombre_niveau_installation_ecs || 1; + + if (!(Sh_immeuble > Sh_logement) && ratioVirtu > 0 && ratioVirtu < 1) { + Sh_immeuble_ratio = Sh_logement / ratioVirtu; + Sh_install = Sh_logement; + Niv_inst_ecs = nombre_niveau_immeuble || Niv_inst_ecs; + } + + // Etape 3: Lb - longueur par défaut du bouclage ECS (m) + // Sh est la surface habitable des logements desservis par l'installation d'ECS + // Pour l'appartement, on utilise la surface à l'échelle de l'immeuble + const Sh = (Sh_install / Sh_logement) * Sh_immeuble_ratio; + const Lb = 4 * Math.pow(Sh / Niv_inst_ecs, 0.5) + 6 * (Niv_inst_ecs - 0.5); + + // Etape 4: DeltaPb (kPa) - perte de charge dans le bouclage + const DeltaPb = 0.2 * Lb + 10; + + let conso = 0; + + for (const mois of mois_liste) { + // Besoin ECS mensuel pour le logement (kWh) + // spec : https://rt-re-batiment.developpement-durable.gouv.fr/IMG/pdf/consolide_annexe_1_arrete_du_31_03_2021_relatif_aux_methodes_et_procedures_applicables.pdf + // page 71-72 + const tefsj = tvs.tefs[ca][mois][zc]; + const njj = Njj[mois]; + const BECS_j = (1.163 * nadeq * 56 * (40 - tefsj) * njj) / 1000; + + // Etape 1: Qdwj_i (Wh) - pertes de distribution à l'échelle de l'immeuble + // Qd,w,j = (0.5 * Lvc / Sh + 0.112 + 0.028) * BECS_j + // Pour Ratecs=1 (bouclé): Lvc = 0.2 * Sh * Ratecs = 0.2 * Sh + // So Qd,w,j = (0.1 + 0.112 + 0.028) * BECS_j = 0.24 * BECS_j (kWh) + // Pour l'appartement: Qd,w,j est multiplié par Sh_immeuble_ratio / Sh_logement + const Qdwj_i = + (0.24 * BECS_j * 1000 * Sh_install * Sh_immeuble_ratio) / (Sh_logement * Sh_logement); + + // Etape 2: qdwj_i (m³/h) - débit de distribution ECS + const Nh_puisage_j = njj * 5; + const qdwj_i = Qdwj_i / (5.815 * Nh_puisage_j) / 1000; + + // Etape 5: Phyd,j (W) - puissance hydraulique du bouclage + const Phyd_j = (qdwj_i * DeltaPb) / 3.6; + + // Etape 6: Effcirb,j - efficacité du circulateur + const Effcirb_j = Math.pow(Phyd_j, 0.324) / 15.3; + + // Etape 7: Pcirb,j (W) - puissance électrique du circulateur + const Pcirb_j = Math.max(20, Phyd_j / Effcirb_j); + + // Etape 8: Qcirb,j (Wh) - consommation mensuelle du circulateur + const Nh_mois_j = njj * 24; + const Qcirb_j = Nh_puisage_j * Pcirb_j + (Nh_mois_j - Nh_puisage_j) * 20; + + conso += Qcirb_j; + } + + // Etape 9: conso annuelle ramenée à l'appartement (kWh) + di.conso_auxiliaire_distribution_ecs = (conso * Sh_logement) / Sh_immeuble_ratio / 1000; +} diff --git a/src/15_conso_aux.spec.js b/src/15_conso_aux.spec.js index f521dfce..8c70c32a 100644 --- a/src/15_conso_aux.spec.js +++ b/src/15_conso_aux.spec.js @@ -1,10 +1,13 @@ import { beforeEach, describe, expect, test, vi } from 'vitest'; /** - * Dépendances mockées uniquement pour `conso_aux_distribution_ch` : + * Dépendances mockées pour `conso_aux_distribution_ch` et `conso_aux_distribution_ecs` : * - `enums` : libellés de classe d'altitude / zone climatique ; * - `tvs.nref19` : nombre d'heures de fonctionnement mensuel ; - * - `mois_liste` / `Tbase` : réduits à des valeurs contrôlées. + * - `tvs.tefs` : température d'eau froide sanitaire mensuelle ; + * - `mois_liste` / `Njj` / `Tbase` : réduits à des valeurs contrôlées. + * L'année est réduite à un seul mois (Janvier, 31 jours) pour rendre les + * consommations attendues calculables à la main. * `conso_aux_gen` n'utilise aucune de ces dépendances (calcul autonome). */ vi.mock('./enums.js', () => ({ @@ -16,16 +19,20 @@ vi.mock('./enums.js', () => ({ vi.mock('./tv.js', () => ({ default: { - nref19: { 0: { ca1: { Janvier: { h1a: 100 } } } } + nref19: { 0: { ca1: { Janvier: { h1a: 100 } } } }, + tefs: { ca1: { Janvier: { h1a: 10 } } } } })); vi.mock('./utils.js', () => ({ mois_liste: ['Janvier'], + Njj: { Janvier: 31 }, Tbase: { ca1: { h1: -9 } } })); -const { conso_aux_gen, conso_aux_distribution_ch } = await import('./15_conso_aux.js'); +const { conso_aux_gen, conso_aux_distribution_ch, conso_aux_distribution_ecs } = await import( + './15_conso_aux.js' +); /** * 15.1 Consommation des auxiliaires de génération @@ -223,3 +230,321 @@ describe('conso_aux_distribution_ch - auxiliaires de distribution', () => { expect(di.conso_auxiliaire_distribution_ch).toBeCloseTo(136.51553416032854, 9); }); }); + +/** + * 15.2.3 Consommation des auxiliaires de distribution d'ECS + * @see : Methode_de_calcul_3CL_DPE_2021-338.pdf - §15.2.3 + * + * Les scénarios ci-dessous s'appuient sur l'année réduite à Janvier (31 jours, + * tefs = 10 °C) définie par les mocks en tête de fichier : + * - Nh_puisage = 31 × 5 = 155 h + * - Nh_mois = 31 × 24 = 744 h + */ +describe("conso_aux_distribution_ecs - auxiliaires de distribution d'ECS", () => { + // Consommation du circulateur maintenu à 20 W sur toutes les heures du mois (kWh) + const CONSO_PLANCHER_20W = (31 * 24 * 20) / 1000; // 14,88 kWh + + test('installation individuelle : consommation nulle', () => { + const di = {}; + conso_aux_distribution_ecs( + {}, + { enum_type_installation_id: '1', enum_bouclage_reseau_ecs_id: '2' }, + di, + 50, + 2000, + 1, + 1, + 30 + ); + expect(di.conso_auxiliaire_distribution_ecs).toBe(0); + }); + + test("réseau d'ECS non bouclé (id 1) : consommation nulle", () => { + const di = {}; + conso_aux_distribution_ecs( + {}, + { enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '1' }, + di, + 50, + 2000, + 1, + 1, + 30 + ); + expect(di.conso_auxiliaire_distribution_ecs).toBe(0); + }); + + test('type de bouclage non renseigné : consommation nulle', () => { + const di = {}; + conso_aux_distribution_ecs({}, { enum_type_installation_id: '2' }, di, 50, 2000, 1, 1, 30); + expect(di.conso_auxiliaire_distribution_ecs).toBe(0); + }); + + test('réseau avec traçage (id 3) : 0,14 × BECS × Sh_installation / Sh_logement', () => { + const di = { besoin_ecs: 1000 }; + conso_aux_distribution_ecs( + {}, + { + enum_type_installation_id: '2', + enum_bouclage_reseau_ecs_id: '3', + surface_habitable: 60 + }, + di, + 50, + 2000, + 1, + 1, + 30 + ); + // 0,14 × 1000 × 60 / 50 + expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(168, 10); + }); + + test("traçage sans surface d'installation : repli sur la surface du logement", () => { + const di = { besoin_ecs: 1000 }; + conso_aux_distribution_ecs( + {}, + { enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '3' }, + di, + 50, + 2000, + 1, + 1, + 30 + ); + // Sh_install = Sh_logement => 0,14 × 1000 + expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(140, 10); + }); + + test('réseau bouclé, faible puissance hydraulique : plancher de 20 W', () => { + const di = {}; + // Immeuble réduit au logement (18 m²) et nadeq faible : Phyd ≈ 0,07 W, donc + // Pcirb calculée (≈ 2,6 W) passe sous le plancher de 20 W tous les mois. + conso_aux_distribution_ecs( + {}, + { + enum_type_installation_id: '2', + enum_bouclage_reseau_ecs_id: '2', + surface_habitable: 18, + nombre_niveau_installation_ecs: 1 + }, + di, + 18, + 18, + 1, + 1, + 1.15 + ); + expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(CONSO_PLANCHER_20W, 10); + }); + + test('réseau bouclé : consommation ramenée au prorata Sh_logement / Sh_immeuble', () => { + const di = {}; + // Même régime de plancher (Pcirb ≈ 11 W) : la consommation immeuble (14,88 kWh) + // est ramenée à l'appartement, soit un dixième ici. + conso_aux_distribution_ecs( + {}, + { + enum_type_installation_id: '2', + enum_bouclage_reseau_ecs_id: '2', + surface_habitable: 50, + nombre_niveau_installation_ecs: 1 + }, + di, + 50, + 500, + 1, + 1, + 0.5 + ); + expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo((CONSO_PLANCHER_20W * 50) / 500, 10); + }); + + test('réseau bouclé, circulateur au-dessus du plancher de 20 W', () => { + const di = {}; + conso_aux_distribution_ecs( + {}, + { + enum_type_installation_id: '2', + enum_bouclage_reseau_ecs_id: '2', + surface_habitable: 50, + nombre_niveau_installation_ecs: 2 + }, + di, + 50, + 2000, + 1, + 1, + 30 + ); + // valeur de référence de régression (Pcirb ≈ 549 W pendant les heures de puisage) + expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(2.4208575056559525, 9); + }); + + test('plus de niveaux desservis : bouclage plus court, donc consommation plus faible', () => { + const scenario = (Niv) => { + const di = {}; + conso_aux_distribution_ecs( + {}, + { + enum_type_installation_id: '2', + enum_bouclage_reseau_ecs_id: '2', + surface_habitable: 50, + nombre_niveau_installation_ecs: Niv + }, + di, + 50, + 2000, + 1, + 1, + 30 + ); + return di.conso_auxiliaire_distribution_ecs; + }; + // Lb = 4 × (Sh / Niv)^0,5 + 6 × (Niv − 0,5) : sur cet immeuble, Lb décroît avec Niv + expect(scenario(1)).toBeCloseTo(2.767218880573735, 9); + expect(scenario(4)).toBeCloseTo(2.2222299894932647, 9); + expect(scenario(4)).toBeLessThan(scenario(1)); + }); + + test("réseau bouclé sans surface ni nombre de niveaux d'installation : valeurs par défaut", () => { + const diDefaut = {}; + conso_aux_distribution_ecs( + {}, + { enum_type_installation_id: '2', enum_bouclage_reseau_ecs_id: '2' }, + diDefaut, + 50, + 2000, + 1, + 1, + 30 + ); + const diExplicite = {}; + // Sh_install = Sh_logement et Niv = 1 + conso_aux_distribution_ecs( + {}, + { + enum_type_installation_id: '2', + enum_bouclage_reseau_ecs_id: '2', + surface_habitable: 50, + nombre_niveau_installation_ecs: 1 + }, + diExplicite, + 50, + 2000, + 1, + 1, + 30 + ); + expect(diDefaut.conso_auxiliaire_distribution_ecs).toBeCloseTo( + diExplicite.conso_auxiliaire_distribution_ecs, + 10 + ); + }); + + test("installation desservant tout l'immeuble : Sh_installation étendue à l'immeuble", () => { + const di = {}; + conso_aux_distribution_ecs( + {}, + { + enum_type_installation_id: '2', + enum_bouclage_reseau_ecs_id: '2', + surface_habitable: 2000, + nombre_niveau_installation_ecs: 2 + }, + di, + 50, + 2000, + 1, + 1, + 30 + ); + // valeur de référence de régression + expect(di.conso_auxiliaire_distribution_ecs).toBeCloseTo(72.84189246242042, 9); + }); +}); + +/** + * 15.2.3 - Cas des DPE d'appartement dont `surface_habitable_immeuble` est + * renseignée avec la surface du logement : l'échelle de l'immeuble est alors + * reconstruite à partir du ratio de virtualisation de l'installation. + */ +describe("conso_aux_distribution_ecs - surface d'immeuble incohérente", () => { + const RATIO_VIRTUALISATION = 0.0211; + const SH_LOGEMENT = 18; + // 18 / 0,0211 = 853,08 m² + const SH_IMMEUBLE_RECONSTRUITE = SH_LOGEMENT / RATIO_VIRTUALISATION; + + /** @param de {object} @param Sh_immeuble {number} @param nbNiveauImmeuble {number} */ + const consoEcs = (de, Sh_immeuble, nbNiveauImmeuble) => { + const di = {}; + conso_aux_distribution_ecs( + {}, + { + enum_type_installation_id: '2', + enum_bouclage_reseau_ecs_id: '2', + surface_habitable: SH_LOGEMENT, + nombre_niveau_installation_ecs: 1, + ...de + }, + di, + SH_LOGEMENT, + Sh_immeuble, + 1, + 1, + 1.15, + nbNiveauImmeuble + ); + return di.conso_auxiliaire_distribution_ecs; + }; + + test("surface d'immeuble égale à celle du logement : reconstruite via le ratio de virtualisation", () => { + const reconstruite = consoEcs( + { ratio_virtualisation: String(RATIO_VIRTUALISATION) }, + SH_LOGEMENT, + 1 + ); + + // Sans reconstruction, le prorata vaut 1 et le circulateur reste au plancher de 20 W + expect(reconstruite).not.toBeCloseTo((31 * 24 * 20) / 1000, 6); + expect(reconstruite).toBeCloseTo(0.45766695775765603, 9); + + // Équivalent à une surface d'immeuble correctement renseignée + const renseignee = consoEcs({}, SH_IMMEUBLE_RECONSTRUITE, 1); + expect(reconstruite).toBeCloseTo(renseignee, 10); + }); + + test("reconstruction : le nombre de niveaux de l'immeuble prime sur celui de l'installation", () => { + // nombre_niveau_installation_ecs = 1 est la valeur du logement, incohérente + // avec la surface d'immeuble reconstruite + expect( + consoEcs({ ratio_virtualisation: String(RATIO_VIRTUALISATION) }, SH_LOGEMENT, 6) + ).toBeCloseTo(0.4237342712345093, 9); + }); + + test("reconstruction sans nombre de niveaux d'immeuble : repli sur celui de l'installation", () => { + expect( + consoEcs( + { + ratio_virtualisation: String(RATIO_VIRTUALISATION), + nombre_niveau_installation_ecs: 3 + }, + SH_LOGEMENT, + undefined + ) + ).toBeCloseTo(0.4253220756819142, 9); + }); + + test('ratio de virtualisation égal à 1 : aucune reconstruction', () => { + expect(consoEcs({ ratio_virtualisation: '1' }, SH_LOGEMENT, 6)).toBeCloseTo( + (31 * 24 * 20) / 1000, + 10 + ); + }); + + test("surface d'immeuble cohérente : le ratio de virtualisation est ignoré", () => { + expect( + consoEcs({ ratio_virtualisation: String(RATIO_VIRTUALISATION) }, SH_IMMEUBLE_RECONSTRUITE, 6) + ).toBeCloseTo(consoEcs({}, SH_IMMEUBLE_RECONSTRUITE, 6), 10); + }); +}); diff --git a/src/6.2_surface_sud_equivalente.js b/src/6.2_surface_sud_equivalente.js index 86954f54..74122e39 100644 --- a/src/6.2_surface_sud_equivalente.js +++ b/src/6.2_surface_sud_equivalente.js @@ -6,7 +6,8 @@ export function calc_sse_j(bv_list, ets, ca, zc, mois) { const baiesAdjVeranda = bv_list.filter((bv) => bv.donnee_entree.enum_type_adjacence_id === '10'); const baiesAdjExt = bv_list.filter((bv) => bv.donnee_entree.enum_type_adjacence_id === '1'); - if (!ets) { + //ets peut etre un tableau vide ou undefined selon les DPE, d'où la vérification ajoutée sur le tableau et sa longueur + if (!ets || (Array.isArray(ets) && ets.length === 0)) { return baiesAdjExt.reduce((acc, bv) => { return acc + getSsd(bv, zc, mois, bv.donnee_intermediaire.sw); }, 0); diff --git a/src/conso.js b/src/conso.js index 5e096dab..6685f2d7 100644 --- a/src/conso.js +++ b/src/conso.js @@ -218,6 +218,11 @@ export default function calc_conso( return acc.concat(generateur_chauffage); }, []); + ecs = Array.isArray(ecs) ? ecs : []; + + const conso_aux_distribution_ecs_total = ecs.reduce((acc, ecs) => { + return acc + (ecs.donnee_intermediaire.conso_auxiliaire_distribution_ecs || 0); + }, 0); const gen_ecs = ecs.reduce((acc, ecs) => { const generateur_ecs = ecs.generateur_ecs_collection.generateur_ecs; if (prorataECS === 1) { @@ -243,7 +248,8 @@ export default function calc_conso( 'conso', null, prorataECS, - prorataChauffage + prorataChauffage, + conso_aux_distribution_ecs_total ), ep_conso: calc_conso_pond( Sh, @@ -255,7 +261,8 @@ export default function calc_conso( 'ep_conso', coeffEp, prorataECS, - prorataChauffage + prorataChauffage, + conso_aux_distribution_ecs_total ), emission_ges: calc_conso_pond( Sh, @@ -267,7 +274,8 @@ export default function calc_conso( 'emission_ges', coef_ges, prorataECS, - prorataChauffage + prorataChauffage, + conso_aux_distribution_ecs_total ), cout: calc_conso_pond( Sh, @@ -279,7 +287,8 @@ export default function calc_conso( 'cout', coef_cout, prorataECS, - prorataChauffage + prorataChauffage, + conso_aux_distribution_ecs_total ) }; ret.ep_conso.classe_bilan_dpe = classe_bilan_dpe( @@ -464,7 +473,8 @@ function calc_conso_pond( prefix, coef, prorataECS, - prorataChauffage + prorataChauffage, + conso_aux_distribution_ecs_total ) { const ret = {}; ret.auxiliaire_ventilation = vt_list.reduce((acc, vt) => { @@ -528,7 +538,11 @@ function calc_conso_pond( return acc + getConso(coef, 'électricité auxiliaire', conso); }, 0); - ret.auxiliaire_distribution_ecs = 0; + ret.auxiliaire_distribution_ecs = getConso( + coef, + 'électricité auxiliaire', + conso_aux_distribution_ecs_total || 0 + ); ret.ecs = getEcsConso(gen_ecs, 'conso_ecs', coef, prorataECS, prefix); diff --git a/src/conso.spec.js b/src/conso.spec.js index 3bbac5fb..463c2925 100644 --- a/src/conso.spec.js +++ b/src/conso.spec.js @@ -188,6 +188,7 @@ function genEcs(energieId, di, deExtra = {}) { function installEcs(deExtra, gens) { return { donnee_entree: { ...deExtra }, + donnee_intermediaire: {}, generateur_ecs_collection: { generateur_ecs: gens } }; } @@ -434,6 +435,25 @@ describe('calc_conso - agrégation des consommations', () => { expect(res.ep_conso.ep_conso_ecs).toBeCloseTo(190, 9); }); + test.each([ + ['absente', undefined], + ['nulle', null], + ['non tabulaire', { installation_ecs: [] }] + ])('collection ECS %s : traitée comme une liste vide', (_libelle, ecs) => { + const chFixture = () => [ + installCh({ cle_repartition_ch: 1 }, [ + genCh('1', { conso_ch: 1000, conso_ch_depensier: 1200 }) + ]) + ]; + + const res = calc_conso(100, 1, 1, [], chFixture(), ecs, [], 1, 1, DATE_DPE, coef_ep); + + // Identique à un appel avec une collection ECS vide : aucune conso ni auxiliaire d'ECS + expect(res).toEqual(calc_conso(100, 1, 1, [], chFixture(), [], [], 1, 1, DATE_DPE, coef_ep)); + expect(res.ef_conso.conso_ecs).toBe(0); + expect(res.ef_conso.conso_auxiliaire_distribution_ecs).toBe(0); + }); + test('ventilation et froid : auxiliaires et froid agrégés par énergie', () => { const vt = [ { diff --git a/src/engine.js b/src/engine.js index 5fb6bd31..92eae64b 100644 --- a/src/engine.js +++ b/src/engine.js @@ -3,6 +3,7 @@ import calc_deperdition from './3_deperdition.js'; import calc_apport_et_besoin from './apport_et_besoin.js'; import calc_clim from './10_clim.js'; import calc_ecs from './11_ecs.js'; +import { conso_aux_distribution_ecs } from './15_conso_aux.js'; import calc_besoin_ch from './9_besoin_ch.js'; import calc_chauffage, { tauxChargeForGenerator } from './9_chauffage.js'; import calc_confort_ete from './2021_04_13_confort_ete.js'; @@ -90,6 +91,7 @@ export function calcul_3cl(inputDpe, options) { const cg = logement.caracteristique_generale; const map_id = cg.enum_methode_application_dpe_log_id; + const dpeGenereImmeuble = ['10', '11', '12', '13', '33', '34', '38', '39', '40'].includes(map_id); const th = calc_th(map_id); if (logement.enveloppe === undefined) { @@ -435,6 +437,20 @@ export function calcul_3cl(inputDpe, options) { dpe.logement.caracteristique_generale.nombre_appartement, isImmeubleSystemEcsIndividuels ); + + if (!dpeGenereImmeuble) { + conso_aux_distribution_ecs( + ecs, + ecs.donnee_entree, + ecs.donnee_intermediaire, + Sh, + cg.surface_habitable_immeuble, + ca_id, + zc_id, + apport_et_besoin.nadeq, + dpe.logement.caracteristique_generale.nombre_niveau_immeuble + ); + } }); /** diff --git a/src/engine.spec.js b/src/engine.spec.js index 5a2147c6..f16b8230 100644 --- a/src/engine.spec.js +++ b/src/engine.spec.js @@ -65,6 +65,7 @@ vi.mock('./10_clim.js', () => ({ default: vi.fn() })); vi.mock('./11_ecs.js', () => ({ default: vi.fn() })); vi.mock('./9_besoin_ch.js', () => ({ default: vi.fn() })); vi.mock('./9_chauffage.js', () => ({ default: vi.fn(), tauxChargeForGenerator: vi.fn() })); +vi.mock('./15_conso_aux.js', () => ({ conso_aux_distribution_ecs: vi.fn() })); vi.mock('./2021_04_13_confort_ete.js', () => ({ default: vi.fn() })); vi.mock('./2021_04_13_qualite_isolation.js', () => ({ default: vi.fn() })); vi.mock('./ficheTechnique.js', () => ({ default: vi.fn() })); @@ -102,6 +103,7 @@ const calc_clim = (await import('./10_clim.js')).default; const calc_ecs = (await import('./11_ecs.js')).default; const calc_besoin_ch = (await import('./9_besoin_ch.js')).default; const { default: calc_chauffage, tauxChargeForGenerator } = await import('./9_chauffage.js'); +const { conso_aux_distribution_ecs } = await import('./15_conso_aux.js'); const calc_confort_ete = (await import('./2021_04_13_confort_ete.js')).default; const calc_qualite_isolation = (await import('./2021_04_13_qualite_isolation.js')).default; const getFicheTechnique = (await import('./ficheTechnique.js')).default; @@ -172,6 +174,7 @@ function makeDpe() { surface_habitable_logement: 80, surface_habitable_immeuble: 200, nombre_appartement: 4, + nombre_niveau_immeuble: 3, hsp: 2.5, annee_construction: 1990 }, @@ -192,6 +195,7 @@ function makeDpe() { installation_ecs: [ { donnee_entree: { enum_type_installation_id: '1' }, + donnee_intermediaire: {}, generateur_ecs_collection: { generateur_ecs: [] } } ] @@ -455,6 +459,22 @@ describe('calcul_3cl - run intégral (maison)', () => { expect(calc_ecs.mock.calls[0][2]).toBe(100); expect(calc_ecs.mock.calls[0][11]).toBe(false); + // Auxiliaires de distribution ECS calculés pour l'installation (maison => map hors + // liste des DPE générés à partir des données d'immeuble). + const ecs = dpe.logement.installation_ecs_collection.installation_ecs[0]; + expect(conso_aux_distribution_ecs).toHaveBeenCalledTimes(1); + expect(conso_aux_distribution_ecs).toHaveBeenCalledWith( + ecs, + ecs.donnee_entree, + ecs.donnee_intermediaire, + 80, // Sh (maison) + 200, // surface_habitable_immeuble + 'ca1', + 'zc1', + 2, // nadeq + 3 // nombre_niveau_immeuble + ); + // Climatisation présente : calc_clim appelé. expect(calc_clim).toHaveBeenCalledTimes(1); }); @@ -484,6 +504,7 @@ describe('calcul_3cl - run intégral (maison)', () => { expect(calc_apport_et_besoin.mock.calls[0][5]).toBe(1); // Pas d'ECS ni de clim à traiter. expect(calc_ecs).not.toHaveBeenCalled(); + expect(conso_aux_distribution_ecs).not.toHaveBeenCalled(); expect(calc_clim).not.toHaveBeenCalled(); }); }); @@ -538,6 +559,9 @@ describe('calcul_3cl - surfaces immeuble / appartement et prorata', () => { // isImmeubleSystemEcsIndividuels = true, besoin non divisé. expect(calc_ecs.mock.calls[0][11]).toBe(true); expect(calc_ecs.mock.calls[0][2]).toBe(100); + // Auxiliaires ECS calculés par installation, avec Sh = surface immeuble. + expect(conso_aux_distribution_ecs).toHaveBeenCalledTimes(2); + expect(conso_aux_distribution_ecs.mock.calls[0][3]).toBe(200); }); test('immeuble : systèmes ECS non tous individuels => besoin divisé par deux', () => { @@ -593,6 +617,77 @@ describe('calcul_3cl - surfaces immeuble / appartement et prorata', () => { expect(calc_conso.mock.calls[0][0]).toBe(80); expect(calc_conso.mock.calls[0][7]).toBeCloseTo(0.4, 9); // prorataECS expect(calc_conso.mock.calls[0][8]).toBeCloseTo(0.4, 9); // prorataChauffage + // Auxiliaires ECS : Sh = surface logement, ramenée à l'immeuble dans le calcul. + expect(conso_aux_distribution_ecs.mock.calls[0][3]).toBe(80); + expect(conso_aux_distribution_ecs.mock.calls[0][4]).toBe(200); + }); +}); + +describe("calcul_3cl - auxiliaires de distribution d'ECS", () => { + // Méthodes d'application « appartement généré à partir des données DPE immeuble » : + // les auxiliaires de distribution d'ECS ne sont pas calculés pour ces DPE. + const MAP_IDS_GENERES_IMMEUBLE = ['10', '11', '12', '13', '33', '34', '38', '39', '40']; + + test.each(MAP_IDS_GENERES_IMMEUBLE)( + "DPE appartement généré à partir des données d'immeuble (map %s) : auxiliaires non calculés", + (mapId) => { + enums.methode_application_dpe_log = { + [mapId]: 'dpe appartement généré à partir des données dpe immeuble chauffage collectif' + }; + const dpe = makeDpe(); + dpe.logement.caracteristique_generale.enum_methode_application_dpe_log_id = mapId; + + calcul_3cl(dpe, { sanitize: false }); + + // L'installation est bien traitée par ailleurs, seul le calcul des auxiliaires est ignoré. + expect(calc_ecs).toHaveBeenCalledTimes(1); + expect(conso_aux_distribution_ecs).not.toHaveBeenCalled(); + } + ); + + test('appartement non généré depuis un immeuble (map 15) : auxiliaires calculés', () => { + enums.methode_application_dpe_log = { 15: 'dpe appartement collectif' }; + const dpe = makeDpe(); + dpe.logement.caracteristique_generale.enum_methode_application_dpe_log_id = '15'; + + calcul_3cl(dpe, { sanitize: false }); + + expect(conso_aux_distribution_ecs).toHaveBeenCalledTimes(1); + }); + + test('une installation ECS par appel : chaque installation est traitée', () => { + const dpe = makeDpe(); + const installations = [ + { + donnee_entree: { enum_type_installation_id: '1' }, + donnee_intermediaire: { tag: 'ecs1' }, + generateur_ecs_collection: { generateur_ecs: [] } + }, + { + donnee_entree: { enum_type_installation_id: '2' }, + donnee_intermediaire: { tag: 'ecs2' }, + generateur_ecs_collection: { generateur_ecs: [] } + } + ]; + dpe.logement.installation_ecs_collection.installation_ecs = installations; + + calcul_3cl(dpe, { sanitize: false }); + + expect(conso_aux_distribution_ecs).toHaveBeenCalledTimes(2); + installations.forEach((ecs, i) => { + expect(conso_aux_distribution_ecs.mock.calls[i][0]).toBe(ecs); + expect(conso_aux_distribution_ecs.mock.calls[i][1]).toBe(ecs.donnee_entree); + expect(conso_aux_distribution_ecs.mock.calls[i][2]).toBe(ecs.donnee_intermediaire); + }); + }); + + test("nombre de niveaux de l'immeuble absent : transmis tel quel", () => { + const dpe = makeDpe(); + dpe.logement.caracteristique_generale.nombre_niveau_immeuble = undefined; + + calcul_3cl(dpe, { sanitize: false }); + + expect(conso_aux_distribution_ecs.mock.calls[0][8]).toBeUndefined(); }); }); diff --git a/test/corpus-sano.json b/test/corpus-sano.json index 9d2b011d..c432ee27 100755 --- a/test/corpus-sano.json +++ b/test/corpus-sano.json @@ -80,5 +80,6 @@ "2387E2603968B", "2432E0658897O", "2444E2616926V", - "2475E2510509B" + "2475E2510509B", + "2674E0057251A" ] diff --git a/test/fixtures/2674E0057251A.xml b/test/fixtures/2674E0057251A.xml new file mode 100644 index 00000000..ded0921e --- /dev/null +++ b/test/fixtures/2674E0057251A.xml @@ -0,0 +1,1825 @@ + + + 2674E0057251A + ACTIF + + + QC LEMELER 06894 11/25 + + + 2.6 + 2025-12-10 + 2026-01-09 + 1 + + 26615 + LICIEL Diagnostics v4 [Version XML:319] + BBS_Slama_2025.11.1.0 + + + + 74H2N5X51JQG + + + + + + + 30 Avenue du Giffre + 74100 + Annemasse, France + 30 Avenue du Giffre 74100 Annemasse, France + 30 Avenue du Giffre 74100 Annemasse, France + 1 + 2025-12-10 + 74012_0620_00030 + + 30 Avenue du Giffre 74100 Annemasse + 30 + Avenue du Giffre + 74012 + 74100 + Annemasse + housenumber + 0.65498 + 949772.96 + 6571256.18 + + QC LEMELER 06894 11/25 + 0 + + + + + + 2 + 2026-01-09T12:05:47+01:00 + + + + 2 + 5 + 29.23 + 1 + 2.49 + 8920 + 1 + + + 3 + 437 + 2 + 0 + + + + 1 + 1 + 1 + 1 + + + + + Mur 1 Est - Murs en ossature bois sans remplissage d'épaisseur 10 cm avec isolation répartie donnant sur l'extérieur + 2025_12_10_09_29_57_1231469004952055 + + 1 + 1 + 3 + 4.93 + 4.93 + 0 + 139 + 10 + 25 + 2 + 0 + 2 + 5 + 2 + 2 + 8 + + + 1 + 2.5 + 2.5 + + + + + Mur 2 Ouest - Mur donnant sur des circulations avec ouverture directe sur l'extérieur + 2025_12_10_09_42_10_0707166007343036 + LNC2025_12_10_09_42_10_0707166007343036 + 99 + 28.94 + 6.52 + 2 + 15 + 4 + 6.05 + 6.05 + 0 + 1 + 1 + 2 + 0 + 2 + 2 + 1 + + + 0.2 + 2.5 + 2.5 + + + + + Mur 3 Nord, Sud, Ouest (p1) - Mur donnant sur un local chauffé + 2025_12_10_09_37_51_9796861009650037 + + 283 + 22 + 2 + 13.07 + 13.07 + 1 + 1 + 1 + 2 + 0 + 2 + 1 + 2 + 2 + 8 + + + 0 + 2.5 + 2.5 + + + + + Mur 3 Nord, Sud, Ouest (p2) - Mur donnant sur un local chauffé + 2025_12_10_09_37_51_9796861009650037(p2) + + 283 + 22 + 1 + 13.07 + 13.07 + 1 + 1 + 1 + 2 + 0 + 2 + 1 + 2 + 2 + 8 + + + 0 + 2.5 + 2.5 + + + + + Mur 3 Nord, Sud, Ouest (p3) - Mur donnant sur un local chauffé + 2025_12_10_09_37_51_9796861009650037(p3) + + 283 + 22 + 4 + 7.58 + 7.58 + 1 + 1 + 1 + 2 + 0 + 2 + 1 + 2 + 2 + 8 + + + 0 + 2.5 + 2.5 + + + + + + + Plancher - Dalle béton donnant sur un local chauffé + 2025_12_10_09_44_57_69884000007763189 + + 283 + 22 + 29.23 + 1 + 9 + 9 + 2 + 1 + 2 + 2 + 8 + 0 + + + 0 + 2 + 2 + 2 + + + + + + + Plafond - Dalle béton donnant sur un local chauffé + 2025_12_10_09_44_43_1707805008240784 + + 283 + 22 + 29.23 + 1 + 8 + 8 + 2 + 1 + 2 + 50 + 8 + + + 0 + 2.5 + 2.5 + + + + + + + Porte-fenêtre 1 Est - Portes-fenêtres battantes pvc, orientées Est, double vitrage avec lame d'argon 20 mm à isolation renforcée et volets roulants aluminium + 2025_12_10_09_47_57_1423016001540166 + 2025_12_10_09_29_57_1231469004952055 + + 1 + 1 + 5.99 + 1 + 37 + 2 + 3 + 2 + 20 + 1 + 1 + 411 + 5 + 7 + 0 + 1.3 + 0.39 + 2 + 66 + 3 + 0 + 0 + 0 + 5 + 126 + 2 + 3 + 9 + 9 + + + + 1 + 1.1 + 1.3 + 1.2 + 1.2 + 0.39 + 0.45 + 1 + + + + + + Porte-fenêtre 2 Est - Portes-fenêtres battantes pvc, orientées Est, double vitrage avec lame d'argon 20 mm à isolation renforcée et volets roulants aluminium + 2025_12_10_09_52_38_160198700453001 + 2025_12_10_09_29_57_1231469004952055 + + 1 + 1 + 5.99 + 1 + 37 + 2 + 3 + 2 + 20 + 1 + 1 + 411 + 5 + 7 + 0 + 1.3 + 0.39 + 2 + 66 + 3 + 0 + 0 + 0 + 5 + 126 + 2 + 3 + 9 + 9 + + + + 1 + 1.1 + 1.3 + 1.2 + 1.2 + 0.39 + 0.45 + 1 + + + + + + + + Porte - Porte(s) bois opaque pleine + 2025_12_10_09_45_53_2434755002439531 + 2025_12_10_09_42_10_0707166007343036 + LNC2025_12_10_09_45_53_2434755002439531 + 2 + 15 + 99 + 28.94 + 6.52 + 2.54 + 1 + 1 + 1 + 1 + 10 + 0 + 0 + 2 + + + 3.5 + 0.2 + + + + + + + + Mur 2 Ouest / Porte + PT_1 + 2025_12_10_09_45_53_2434755002439531 + 2025_12_10_09_42_10_0707166007343036 + 76 + 1 + 6.98 + 1 + 5 + + + 0.25 + + + + + Mur 1 Est / Porte-fenêtre 1 Est + PT_2 + 2025_12_10_09_47_57_1423016001540166 + 2025_12_10_09_29_57_1231469004952055 + 101 + 1 + 9.86 + 1 + 5 + + + 0.2 + + + + + Mur 1 Est / Porte-fenêtre 2 Est + PT_3 + 2025_12_10_09_52_38_160198700453001 + 2025_12_10_09_29_57_1231469004952055 + 101 + 1 + 9.86 + 1 + 5 + + + 0.2 + + + + + + + + 29.23 + Ventilation par entrées d'air hautes et basses + 2025_12_10_09_55_08_1599425001411088 + 0 + 2 + 1 + 2 + 2 + 0 + + + + 2 + 0 + 0.25918 + 22.16219 + + + + + + + + + Combiné au système de chauffage + 2025_12_10_09_55_22_0826104003958533 + 1 + 2 + 2 + 0.00328 + 29.23 + 1 + 1 + 7 + 6 + 2 + 1 + + + 0.55 + 938.48493834095 + 1323.93410944527 + 1779.15376577636 + 2504.62140114879 + + + + + Gaz Naturel - Chaudière gaz à condensation installée à partir de 2016 + Generateur:2025_12_10_09_55_22_0826104003958533 + + Generateur:2025_12_10_09_53_58_1231166003776604#1 + 57 + + 3 + 2 + 13 + 1 + + 1 + 0 + 1 + 0 + 0 + + + 1310.76 + 3.93228 + 0.966021 + 1 + 0.959072 + 1779.15376577636 + 2504.62140114879 + + + + + + + + + Chaudière collective gaz à condensation installée à partir de 2016 régulée, avec équipement d'intermittence central collectif, réseau isolé. Emetteur(s): radiateur bitube sans robinet thermostatique + 2025_12_10_09_53_58_1231166003776604 + 29.23 + 1 + 7 + 1 + 0.00328 + 2 + 2 + + + 2509.28018793419 + 3089.80293515762 + 3300.33162411974 + 4076.25327938611 + + + + + + Emetteur:2025_12_10_09_53_58_1231166003776604#1 + 29.23 + 7 + 8 + 11 + 32 + 183 + 1 + 6 + 1 + 1 + 2 + 4 + 1 + + + 0.95 + 0.95 + 0.87 + 0.9 + + + + + + + Gaz Naturel - Chaudière gaz à condensation installée à partir de 2016 + Generateur:2025_12_10_09_53_58_1231166003776604#1 + + Generateur:2025_12_10_09_55_22_0826104003958533 + Sans Objet + 97 + 3 + 2 + 0 + 13 + + 0 + 1 + 1 + 1 + + + 1310.76 + 3.93228 + 38 + 80 + 0.966021 + 1.06301 + 0.988916 + 3300.33162411974 + 4076.25327938611 + + + + + + + + 22.162186000000002 + 0.25917609185699558 + 22.421362091857 + 15.35 + 0 + 0 + 14.376 + 1.778 + 5.689 + 59.614362091857 + + + 18.375763 + 0 + 0 + 949.872603654 + 1226.4609610000002 + 0.40251320709557686 + 0.36987532858353483 + 67.155410996066479 + 97.843736247104076 + 0 + 0 + 0 + 1.3605625000000001 + 76.1915 + 107.48443750000001 + 2509.280187934186 + 3089.8029351576224 + 938.48493834094984 + 1323.9341094452689 + 0 + 0 + + + 3300.3316241197372 + 4076.2532793861133 + 1779.1537657763593 + 2504.6214011487923 + 54.360784800000005 + 4.1403125332915467 + 9.2384876511391862 + 184.8 + 1.5485002317406782 + 2.1844913983484573 + 2.2492952069933319 + 0 + 192.73810797202557 + 0 + 0 + 5326.5842826681228 + 182 + + + 3300.3316241197372 + 3300.3316241197372 + 1779.1537657763593 + 1779.1537657763593 + 103.28549112 + 7.8665938132539379 + 7.8665938132539379 + 351.12 + 2.9421504403072882 + 2.9421504403072882 + 4.27366089328733 + 0 + 366.20240514684855 + 0 + 0 + 5548.9732861629454 + 189 + D + + + 749.17527867518038 + 749.17527867518038 + 403.86790483123355 + 403.86790483123355 + 3.7508941512000007 + 0.264980002130659 + 0.264980002130659 + 11.827200000000001 + 0.099104014831403409 + 0.099104014831403409 + 0.14395489324757324 + 0 + 12.335238910209638 + 0 + 0 + 1169.1293165678237 + 39 + D + + + 306.68594376251104 + 306.68594376251104 + 165.32928017537677 + 165.32928017537677 + 18.874608090408003 + 0.83359456404354471 + 0.83359456404354471 + 37.206919573479333 + 0.3117690670015667 + 0.3117690670015667 + 0.45286442566890395 + 0 + 38.805147630193346 + 0 + 0 + 529.69497965848927 + + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + + + 2 + 3300.3316241197372 + 1779.1537657763593 + 5079.4853898960964 + 749.17527867518038 + 403.86790483123355 + 1153.0431835064139 + 306.68594376251104 + 165.32928017537677 + 472.01522393788781 + + + 1 + 0 + 0 + 247.09889277202558 + 0 + 0 + 16.08613306140964 + 0 + 0 + 57.679755720601349 + + + + 0 + 1 + 0 + 0 + 1 + 2 + + + 1.459 + 4 + 4 + 1 + 1 + 2 + + + + + + + Murs en ossature bois sans remplissage d'épaisseur 10 cm avec isolation répartie donnant sur l'extérieur + 1 + + + Mur donnant sur des circulations avec ouverture directe sur l'extérieur + 1 + + + Mur donnant sur un local chauffé + 1 + + + Dalle béton donnant sur un local chauffé + 3 + + + Dalle béton donnant sur un local chauffé + 2 + + + Porte(s) bois opaque pleine + 4 + + + Portes-fenêtres battantes pvc, orientées Est, double vitrage avec lame d'argon 20 mm à isolation renforcée et volets roulants aluminium + 4 + + + Portes-fenêtres battantes pvc, orientées Est, double vitrage avec lame d'argon 20 mm à isolation renforcée et volets roulants aluminium + 4 + + + Chaudière collective gaz à condensation installée à partir de 2016 régulée, avec équipement d'intermittence central collectif, réseau isolé. Emetteur(s): radiateur bitube sans robinet thermostatique + 5 + + + Chaudière collective gaz à condensation installée à partir de 2016 régulée, avec équipement d'intermittence central collectif, réseau isolé. Emetteur(s): radiateur bitube sans robinet thermostatique + 9 + + + Ventilation par entrées d'air hautes et basses + 8 + + + Combiné au système de chauffage + 6 + + + + + 11 + + + Département: 74 Haute Savoie + 74 Haute Savoie + + 2 + + + Altitude: 437 m + 437 m + + 4 + + + Type de bien: Appartement + Appartement + + 2 + + + Année de construction: 1948 - 1974 + 1948 - 1974 + + 5 + + + Surface de référence du logement: 29,23 m² + 29,23 m² + + 2 + + + Surface de référence de l'immeuble: 8920 m² + 8920 m² + + 2 + + + Nombre de niveaux du logement: 1 + 1 + + 2 + + + Hauteur moyenne sous plafond: 2,49 m + 2,49 m + + 2 + + + + + 1 + + + Surface du mur: 4,93 m² + 4,93 m² + + 2 + + + Type d'adjacence: l'extérieur + l'extérieur + + 2 + + + Matériau mur: Murs en ossature bois sans remplissage + Murs en ossature bois sans remplissage + + 2 + + + Epaisseur mur: 10 cm + 10 cm + + 2 + + + Isolation: oui + oui + + 2 + + + Année de construction/rénovation: 1948 - 1974 + 1948 - 1974 + + 1 + + + + + 1 + + + Surface du mur: 6,05 m² + 6,05 m² + + 2 + + + Type d'adjacence: des circulations avec ouverture directe sur l'extérieur + des circulations avec ouverture directe sur l'extérieur + + 2 + + + Surface Aiu: 28.94 m² + 28.94 m² + + 2 + + + Etat isolation des parois Aiu: non isolé + non isolé + + 2 + + + Surface Aue: 6.52 m² + 6.52 m² + + 2 + + + Etat isolation des parois Aue: non isolé + non isolé + + 2 + + + Matériau mur: Inconnu (à structure lourde) + Inconnu (à structure lourde) + + 2 + + + Isolation: non + non + + 2 + + + Umur0 (paroi inconnue): 2,5 W/m².K + 2,5 W/m².K + + 1 + + + + + 1 + + + Surface du mur: 33,72 m² + 33,72 m² + + 2 + + + Type d'adjacence: un local chauffé + un local chauffé + + 2 + + + Matériau mur: Inconnu (à structure lourde) + Inconnu (à structure lourde) + + 2 + + + Isolation: inconnue + inconnue + + 2 + + + Année de construction/rénovation: 1948 - 1974 + 1948 - 1974 + + 1 + + + Umur0 (paroi inconnue): 2,5 W/m².K + 2,5 W/m².K + + 1 + + + + + 2 + + + Surface de plancher bas: 29,23 m² + 29,23 m² + + 2 + + + Type d'adjacence: un local chauffé + un local chauffé + + 2 + + + Type de pb: Dalle béton + Dalle béton + + 2 + + + Isolation : oui / non / inconnue: inconnue + inconnue + + 2 + + + Année de construction/rénovation: 1948 - 1974 + 1948 - 1974 + + 1 + + + + + 3 + + + Surface de plancher haut: 29,23 m² + 29,23 m² + + 2 + + + Type d'adjacence: un local chauffé + un local chauffé + + 2 + + + Type de ph: Dalle béton + Dalle béton + + 2 + + + Isolation: inconnue + inconnue + + 2 + + + Année de construction/rénovation: 1948 - 1974 + 1948 - 1974 + + 1 + + + + + 4 + + + Surface de baies: 5,99 m² + 5,99 m² + + 2 + + + Placement: Mur 1 Est + Mur 1 Est + + 2 + + + Orientation des baies: Est + Est + + 2 + + + Inclinaison vitrage: vertical + vertical + + 2 + + + Type ouverture: Portes-fenêtres battantes + Portes-fenêtres battantes + + 2 + + + Type menuiserie: PVC + PVC + + 2 + + + Type de vitrage: double vitrage + double vitrage + + 2 + + + Epaisseur lame air: 20 mm + 20 mm + + 2 + + + Présence couche peu émissive: oui + oui + + 2 + + + Gaz de remplissage: Argon / Krypton + Argon / Krypton + + 2 + + + Longueur Pont Thermique: 9,86 m + 9,86 m + + 2 + + + Positionnement de la menuiserie: au nu intérieur + au nu intérieur + + 2 + + + Largeur du dormant menuiserie: Lp: 5 cm + Lp: 5 cm + + 2 + + + Type volets: Volets roulants aluminium + Volets roulants aluminium + + 2 + + + Type de masques proches: Baie en fond de balcon + Baie en fond de balcon + + 2 + + + Avancée l (profondeur des masques proches): < 1m + < 1m + + 2 + + + Type de masques lointains: Masque homogène + Masque homogène + + 2 + + + Hauteur a (°): 0 - 15° + 0 - 15° + + 2 + + + U Fenêtre (calculé): 1,2 + 1,2 + + 2 + + + + + 4 + + + Surface de baies: 5,99 m² + 5,99 m² + + 2 + + + Placement: Mur 1 Est + Mur 1 Est + + 2 + + + Orientation des baies: Est + Est + + 2 + + + Inclinaison vitrage: vertical + vertical + + 2 + + + Type ouverture: Portes-fenêtres battantes + Portes-fenêtres battantes + + 2 + + + Type menuiserie: PVC + PVC + + 2 + + + Type de vitrage: double vitrage + double vitrage + + 2 + + + Epaisseur lame air: 20 mm + 20 mm + + 2 + + + Présence couche peu émissive: oui + oui + + 2 + + + Gaz de remplissage: Argon / Krypton + Argon / Krypton + + 2 + + + Longueur Pont Thermique: 9,86 m + 9,86 m + + 2 + + + Positionnement de la menuiserie: au nu intérieur + au nu intérieur + + 2 + + + Largeur du dormant menuiserie: Lp: 5 cm + Lp: 5 cm + + 2 + + + Type volets: Volets roulants aluminium + Volets roulants aluminium + + 2 + + + Type de masques proches: Baie en fond de balcon + Baie en fond de balcon + + 2 + + + Avancée l (profondeur des masques proches): < 1m + < 1m + + 2 + + + Type de masques lointains: Masque homogène + Masque homogène + + 2 + + + Hauteur a (°): 0 - 15° + 0 - 15° + + 2 + + + U Fenêtre (calculé): 1,2 + 1,2 + + 2 + + + + + 5 + + + Surface de porte: 2,54 m² + 2,54 m² + + 2 + + + Placement: Mur 2 Ouest + Mur 2 Ouest + + 2 + + + Type d'adjacence: des circulations avec ouverture directe sur l'extérieur + des circulations avec ouverture directe sur l'extérieur + + 2 + + + Surface Aiu: 28.94 m² + 28.94 m² + + 2 + + + Etat isolation des parois Aiu: non isolé + non isolé + + 2 + + + Surface Aue: 6.52 m² + 6.52 m² + + 2 + + + Etat isolation des parois Aue: non isolé + non isolé + + 2 + + + Nature de la menuiserie: Porte simple en bois + Porte simple en bois + + 2 + + + Type de porte: Porte opaque pleine + Porte opaque pleine + + 2 + + + Présence de joints d'étanchéité: non + non + + 2 + + + Longueur Pont Thermique: 6,98 m + 6,98 m + + 2 + + + Positionnement de la menuiserie: au nu intérieur + au nu intérieur + + 2 + + + Largeur du dormant menuiserie: Lp: 10 cm + Lp: 10 cm + + 2 + + + + + 6 + + + Type de pont thermique: Mur 2 Ouest / Porte + Mur 2 Ouest / Porte + + 2 + + + Type isolation: non isolé + non isolé + + 2 + + + Longueur du PT: 7 m + 7 m + + 2 + + + Largeur du dormant menuiserie Lp: Lp: 10 cm + Lp: 10 cm + + 2 + + + Position menuiseries: au nu intérieur + au nu intérieur + + 2 + + + + + 6 + + + Type de pont thermique: Mur 1 Est / Porte-fenêtre 1 Est + Mur 1 Est / Porte-fenêtre 1 Est + + 2 + + + Type isolation: ITR + ITR + + 2 + + + Longueur du PT: 9,9 m + 9,9 m + + 2 + + + Largeur du dormant menuiserie Lp: Lp: 5 cm + Lp: 5 cm + + 2 + + + Position menuiseries: au nu intérieur + au nu intérieur + + 2 + + + + + 6 + + + Type de pont thermique: Mur 1 Est / Porte-fenêtre 2 Est + Mur 1 Est / Porte-fenêtre 2 Est + + 2 + + + Type isolation: ITR + ITR + + 2 + + + Longueur du PT: 9,9 m + 9,9 m + + 2 + + + Largeur du dormant menuiserie Lp: Lp: 5 cm + Lp: 5 cm + + 2 + + + Position menuiseries: au nu intérieur + au nu intérieur + + 2 + + + + + 10 + + + Type de ventilation: Ventilation par entrées d'air hautes et basses + Ventilation par entrées d'air hautes et basses + + 2 + + + Façades exposées: une + une + + 2 + + + Logement Traversant: non + non + + 2 + + + + + 7 + + + Type d'installation de chauffage: Installation de chauffage simple + Installation de chauffage simple + + 2 + + + Surface chauffée: 29,23 m² + 29,23 m² + + 2 + + + Nombre de niveaux desservis: 7 + 7 + + 2 + + + Type générateur: Gaz Naturel - Chaudière gaz à condensation installée à partir de 2016 + Gaz Naturel - Chaudière gaz à condensation installée à partir de 2016 + + 2 + + + Année installation générateur: 2021 (estimée en fonction de la marque et du modèle) + 2021 (estimée en fonction de la marque et du modèle) + + 2 + + + Energie utilisée: Gaz Naturel + Gaz Naturel + + 2 + + + Cper (présence d'une ventouse): non + non + + 2 + + + Présence d'une veilleuse: non + non + + 2 + + + Chaudière murale: non + non + + 2 + + + Présence d'une régulation/Ajust,T° Fonctionnement: oui + oui + + 2 + + + Présence ventilateur / dispositif circulation air dans circuit combustion: non + non + + 2 + + + Type émetteur: Radiateur bitube sans robinet thermostatique + Radiateur bitube sans robinet thermostatique + + 2 + + + Température de distribution: supérieur à 65°C + supérieur à 65°C + + 2 + + + Année installation émetteur: Inconnue + Inconnue + + 2 + + + Type de chauffage: central + central + + 2 + + + Equipement d'intermittence: Avec intermittence centrale collectif + Avec intermittence centrale collectif + + 2 + + + Présence comptage: 1 + 1 + + 2 + + + + + 8 + + + Nombre de niveaux desservis: 7 + 7 + + 2 + + + Type générateur: Gaz Naturel - Chaudière gaz à condensation installée à partir de 2016 + Gaz Naturel - Chaudière gaz à condensation installée à partir de 2016 + + 2 + + + Année installation générateur: 2021 (estimée en fonction de la marque et du modèle) + 2021 (estimée en fonction de la marque et du modèle) + + 2 + + + Energie utilisée: Gaz Naturel + Gaz Naturel + + 2 + + + Type production ECS: Chauffage et ECS + Chauffage et ECS + + 2 + + + Présence d'une veilleuse: non + non + + 2 + + + Chaudière murale: non + non + + 2 + + + Présence d'une régulation/Ajust,T° Fonctionnement: oui + oui + + 2 + + + Présence ventilateur / dispositif circulation air dans circuit combustion: non + non + + 2 + + + Type de distribution: Réseau collectif isolé bouclé sans traçage, majorité des logements avec pièces alimentées contiguës + Réseau collectif isolé bouclé sans traçage, majorité des logements avec pièces alimentées contiguës + + 2 + + + Bouclage pour ECS: oui + oui + + 2 + + + Type de production: instantanée + instantanée + + 2 + + + + + + + Plans du logement + 1 + + + Notices techniques des équipements + 10 + + + + + Eteindre les lumières lorsque personne n'utilise la pièce. + 4 + Eclairage + + + Faire vérifier les isolants et les compléter tous les 20 ans. + 13 + Isolation + + + Laisser les robinets thermostatiques en position ouverte en fin de saison de chauffe. + 3 + Radiateur + + + Ne jamais placer un meuble devant un émetteur de chaleur. + 3 + Radiateur + + + Purger les radiateurs s'il y a de l'air. + 3 + Radiateur + + + Nettoyer régulièrement les bouches. + 1 + Ventilation + + + Veiller à ouvrir les fenêtres de chaque pièce très régulièrement + 1 + Ventilation + + + + + + 1 + 136 + 27 + 100 + 150 + + + Mise en place d'une isolation par l'intérieur des murs +avec dans l'idéal, des retours d'isolants au niveau des tableaux des baies quand cela est possible. Le choix de l'isolant est essentiel pour ne + 1 + + R > 3,7 m².K/W + + + Mettre à jour le système d'intermittence / régulation (programmateur, robinets thermostatiques, isolation réseau). Afin de contrôler précisément la température au plus proche du besoin, optimiser la r + 5 + + + + + Système actualisé en même temps que le chauffage + 6 + + + + + + + 3 + 108 + 21 + 100 + 150 + + + Mettre en place un système Solaire + 6 + + + + + + + + + \ No newline at end of file diff --git a/test/fixtures/2675E0118640E.xml b/test/fixtures/2675E0118640E.xml new file mode 100644 index 00000000..dd0ac3f9 --- /dev/null +++ b/test/fixtures/2675E0118640E.xml @@ -0,0 +1,26 @@ + + 2675E0118640E + ACTIF +2.62025-12-172026-01-151log 2.51 (bld 625.36301)152574.1.164W4HJS8JBNZ75114000CG003724 rue Morère75014PARIS24 rue Morère 75014 PARIS24 rue Morère 75014 PARIS12025-12-1675114_6551_0002424 Rue Morère 75014 Paris24Rue Morère7511475014Parishousenumber0.883130909090909650166.186858535.541Droite12026-01-15T17:01:59+01:00190012135.3612.71110004Mur 1pc Nord77febb38-3920-435d-8b07-637460d1e8c7101230.783.2415212.9611.025011102210.12.52.5Mur 2extsejcha Sud7c06a287-0a44-4da4-8705-a0fa9cd94af411116.211.0414834820534310.6666666666666672Mur 3extcui Nord-Est82d7610f-31e6-4f5e-8fc7-d5efb50cd36e1125.8743.9190111022112.52.5Mur 4extwc Nord105e1852-1ddd-4402-acfa-442552dbfae41122.26951.919501110534310.7142857142857142.5Planchere4e66d2e-6a4a-4db3-9437-cbcd1b6ce4272832235.36022221001.451.451.45Plafond9f1a640c-17cc-455b-bb10-710671d029e22832235.3602222101.451.45Fenêtre 1sejchab9ece877-4cd0-432e-a17c-09e9f9d8c5d67c06a287-0a44-4da4-8705-a0fa9cd94af4115.162102312001346540119200151102119267312.72.62.42.40.4410.340000003576279Fenêtre 2cui5f014e49-81fe-45ca-9622-274f37ad40d382d7610f-31e6-4f5e-8fc7-d5efb50cd36e111.9551102312001346540100151102219412.72.62.60.4410.3Fenêtre 3wcf648e36b-ab73-4241-861b-4f1d0f424557105e1852-1ddd-4402-acfa-442552dbfae4110.351102312001346540100151102219412.72.62.60.4410.3Porteefd53339-bce5-4277-9ec4-e1b9bf65d4a077febb38-3920-435d-8b07-637460d1e8c7101230.783.24151.935111150023.50.1Mur 2extsejcha Sud (vers le haut)-e239d952-730b-4c36-a745-451d996118787c06a287-0a44-4da4-8705-a0fa9cd94af4306120.50.92Mur 2extsejcha Sud (vers le bas)-24091bf8-ec3b-4611-8940-19507cead4317c06a287-0a44-4da4-8705-a0fa9cd94af4306120.50.92Mur 2extsejcha Sud (à gauche du refend)-9db75c0b-c36a-46ae-8f0d-deb03268fead7c06a287-0a44-4da4-8705-a0fa9cd94af4652.7140.50.82Mur 2extsejcha Sud (à droite du refend)-8a4e49c6-40cf-40ac-827c-dc9a60fcfb747c06a287-0a44-4da4-8705-a0fa9cd94af4652.7140.50.82Fenêtre 1sejcha-Mur 2extsejcha Sudd937338b-1163-47c9-89cd-d5c0fe7098cbb9ece877-4cd0-432e-a17c-09e9f9d8c5d67c06a287-0a44-4da4-8705-a0fa9cd94af48713.41510Fenêtre 2cui-Mur 3extcui Nord-Est0cb0ca84-d9f8-471e-b97b-5ab708b72dec5f014e49-81fe-45ca-9622-274f37ad40d382d7610f-31e6-4f5e-8fc7-d5efb50cd36e755.71510.38Fenêtre 3wc-Mur 4extwc Norde92049d5-fd6d-481d-b9b0-d7dd47944884f648e36b-ab73-4241-861b-4f1d0f424557105e1852-1ddd-4402-acfa-442552dbfae4872.41510Porte-Mur 1pc Nord555f3470-bc2d-4b27-812e-7bf31bb37ee2efd53339-bce5-4277-9ec4-e1b9bf65d4a077febb38-3920-435d-8b07-637460d1e8c7755.2151035.36a788e834-b015-46bc-89ba-17708900cfad112166102.517.9042866685309715.869568Chauffe-eau vertical ElectriqueEcs_efa69e06-491f-49d2-b9f0-b12e46031f0211135.36111110.93965912.6027461362626.707445251321.346201877421747.92050800565Chauffe-eau verticalEefa69e06-491f-49d2-b9f0-b12e46031f0270211321651111321.346201877421747.920508005650.786028446142059Chauffage sans solaireInstallation 135.36111112853.576917891023555.732739880322614.291894672063257.56885094212Radiateur électrique NF**Emet_2b832ee6-abf0-4a51-bd81-5476d4be6ac435.3621231384021110.860.9710.99Radiateur électrique NF**G27345bce-c950-4d17-8497-318e0263f440100111291112614.291894672063257.568850942127.9042866685309715.86956823.77385466853121.28482142857140018.3770.677259.974.01285466853110.9275343200368112.52564481199630.53210.3022856239216520.26869041561086627749.478225738539751.367618841281224.9869315069001.475582.628116.56452853.576917891023555.73273988032965.9126027461362.62670744525002614.291894672063257.568850942121321.346201877421747.9205080056566.83040000000102.218688102.218688004104.68718454948116.0827823684814967.154537547346189.380739123542510.557752063753321.04896521073126.9777584066390000000194.215504762917194.215504762917007798.90565064401220.557286500114D206.529069184353257.34795106856885.8874999716974113.6148288534.6112974844813300000006.541996342728146.5419963427281400303.569862983268.59B596718301385150000000232300935000000002614.291894672061321.346201877424104.687184549481206.52906918435385.8874999716974303.569862983265963019350110021.34676562329464443Mur 2extsejcha Sud Briques pleines simples donnant sur Extérieur, isolé +Mur 1pc Nord Inconnu donnant sur Circulations communes, non isolé +Mur 3extcui Nord-Est Inconnu donnant sur Extérieur, non isolé1Pas de plancher déperditif2Pas de plafond déperditif3Fenêtres battantes, Menuiserie PVC - double vitrage vertical (e = 20 mm) avec Fermeture +Porte Bois Opaque pleine4Radiateur électrique NF** Electrique, installation en 2025, individuel5Chauffe-eau vertical Electrique installation en 2025, individuel, production par semi-accumulation 6VMC SF Auto réglable après 20128Radiateur électrique NF** : +avec régulation pièce par pièce, intermittence par pièce avec minimum de température91Umur0 (saisie directe ou matériau mur inconnu)2,5 W/m²K1Surface11,02 m²2Isolation : oui / non / inconnueNon2Bâtiment construit en matériaux anciens Non2Inertie Légère1Type d'adjacenceCirculations communes avec ouverture directe sur l'extérieur2Surface Aiu30,78 m²2Surface Aue3,24 m²2Etat isolation des parois du local non chaufféNon3Doublageabsence de doublage2OrientationNord21Bâtiment construit en matériaux anciens Oui2Inertie Lourde2Type d'adjacenceExtérieur2Doublageconnu (plâtre brique bois)2OrientationSud2Surface11,04 m²2Matériau murBriques pleines simples2Epaisseur mur34 cm2Isolation : oui / non / inconnueOui2Epaisseur isolant4 cm21Umur0 (saisie directe ou matériau mur inconnu)2,5 W/m²K1Surface3,92 m²2Isolation : oui / non / inconnueNon2Bâtiment construit en matériaux anciens Oui2Inertie Légère1Type d'adjacenceExtérieur2Doublageabsence de doublage2OrientationNord21Doublageconnu (plâtre brique bois)2OrientationNord2Umur0 (saisie directe ou matériau mur inconnu)2,5 W/m²K1Surface1,92 m²2Isolation : oui / non / inconnueOui2Epaisseur isolant4 cm2Bâtiment construit en matériaux anciens Oui2Inertie Légère1Type d'adjacenceExtérieur22Surface35,36 m²2Type de plancher basInconnu avec ou sans remplissage2Isolation : oui / non / inconnueNon2Inertie Légère2Type d'adjacenceLocal chauffé23Surface35,36 m²2TypeInconnu avec ou sans remplissage2Isolation : oui / non / inconnueNon2Inertie Légère2Type d'adjacenceLocal chauffé24Surface de baies5,16 m²2Type de vitrageDouble vitrage vertical2Epaisseur lame air20 mm2Présence couche peu émissiveNon2Gaz de remplissageAir2Double fenêtreNon2Inclinaison vitrageVerticale (Inclinaison ≥ 75°)2Type menuiserieMenuiserie PVC2Positionnement de la menuiserieNu intérieur2Type ouvertureFenêtres battantes2Type voletsJalousie accordéon, fermeture à lames orientables y compris les vénitiens extérieurs tout métal, volets battants ou persiennes avec ajours fixes2Orientation des baiesSud2Type de masque prochesAbsence de masque proche2Type de masques lointains Non Homogène2Hauteur moyenne α, β (Latéral est , 22,5) (Central est , 22,5) (Central ouest , 45) (Latéral ouest , 45)2Présence de jointsOui2Type d'adjacenceExtérieur2Largeur approximative du dormant5 cm24Surface de baies1,95 m²2Type de vitrageDouble vitrage vertical2Epaisseur lame air20 mm2Présence couche peu émissiveNon2Gaz de remplissageAir2Double fenêtreNon2Inclinaison vitrageVerticale (Inclinaison ≥ 75°)2Type menuiserieMenuiserie PVC2Positionnement de la menuiserieNu intérieur2Type ouvertureFenêtres battantes2Type voletsSans2Orientation des baiesNord2Type de masque prochesAbsence de masque proche2Type de masques lointains Homogène2Hauteur α75 °2Présence de jointsOui2Type d'adjacenceExtérieur2Largeur approximative du dormant5 cm24Type de masques lointains Homogène2Hauteur α75 °2Présence de jointsOui2Type d'adjacenceExtérieur2Largeur approximative du dormant5 cm2Surface de baies0,35 m²2Type de vitrageDouble vitrage vertical2Epaisseur lame air20 mm2Présence couche peu émissiveNon2Gaz de remplissageAir2Double fenêtreNon2Inclinaison vitrageVerticale (Inclinaison ≥ 75°)2Type menuiserieMenuiserie PVC2Positionnement de la menuiserieNu intérieur2Type ouvertureFenêtres battantes2Type voletsSans2Orientation des baiesNord2Type de masque prochesAbsence de masque proche25Type de menuiserieBois2Type de porteOpaque pleine2Surface1,94 m²2Présence de jointsNon2Type d'adjacenceCirculations communes avec ouverture directe sur l'extérieur2Largeur approximative du dormant5 cm26Type de pont thermiquePlancher intermédiaire lourd - Mur lourd2Type isolation ITI2Longueur du pont thermique6 m26Type de pont thermiquePlancher intermédiaire lourd - Mur lourd2Type isolation ITI2Longueur du pont thermique6 m26Type de pont thermiqueRefend - Mur2Type isolation ITI2Longueur du pont thermique2,7 m26Type de pont thermiqueRefend - Mur2Type isolation ITI2Longueur du pont thermique2,7 m26Type de pont thermiqueMenuiseries - Mur2Type isolation ITI2Longueur du pont thermique13,4 m2Largeur du dormant menuiserie Lp5 cm2Retour isolation autour menuiserieNon2Position menuiseriesNu intérieur26Type de pont thermiqueMenuiseries - Mur2Longueur du pont thermique5,7 m2Largeur du dormant menuiserie Lp5 cm2Retour isolation autour menuiserieNon2Position menuiseriesNu intérieur26Type de pont thermiqueMenuiseries - Mur2Type isolation ITI2Longueur du pont thermique2,4 m2Largeur du dormant menuiserie Lp5 cm2Retour isolation autour menuiserieNon2Position menuiseriesNu intérieur26Type de pont thermiqueMenuiseries - Mur2Longueur du pont thermique5,2 m2Largeur du dormant menuiserie Lp5 cm2Retour isolation autour menuiserieNon2Position menuiseriesNu intérieur27Energie utiliséeElectricité2Présence d'une ventouseNon2Présence d'une veilleuseNon2Type émetteurRadiateur électrique NF**2Surface chauffée par émetteur35,36 m²2Type de chauffageDivisé avec régulation pièce par pièce2Equipement d'intermittencePar pièce avec minimum de température2Présence de comptageNon2Type de distributionRadiateur électrique NF** (35,36m²): Pas de réseau de distribution2Type d’installation de chauffageInstallation de chauffage sans solaire2Type générateurRadiateur électrique NF**2Surface chauffée35,36 m²2Année d'installation202528Production en volume habitableOui2Volume de stockage65 L2Type de ballonChauffe-eau vertical2Catégorie de ballonB ou 2 étoiles2Type générateurChauffe-eau vertical Electrique2Année installation20252Energie utiliséeElectricité2Type production ECSIndividuel2Présence d'une veilleuseNon1Isolation du réseau de distributionNon2Pièces alimentées contiguësOui210Type de ventilationVMC SF Auto réglable après 2012 (Electricité)2Année installation20253Plusieurs façades exposéesOui2Menuiseries avec jointsOui211Département 75 - Paris2Altitude404Type de bienAppartement2Année de construction19005Surface de référence du logement35,363Nombre de niveaux du logement12Hauteur moyenne sous plafond2,724Eteindre les lumières lorsque personne n'utilise la pièce.éclairage3Ne jamais placer un meuble devant un émetteur de chaleurradiateurDes travaux peuvent vous permettre d'améliorer significativement l’efficacité énergétique de votre logement et ainsi de faire des économies d’énergie, d’améliorer son confort, de le valoriser et de le rendre plus écologique. Le pack de travaux vous permet de réaliser les travaux prioritaires, et le pack d’aller vers un logement très performant.2111.271019277296529090811Isolation par l'intérieur des Murs en contact avec un volume non chauffé : Isolation des murs en contact avec un volume non chauffé. +Mise en place d'un isolant permettant d'atteindre pour l'ensemble paroi + isolant 3.7 m²k/wSupprimer les travaux antérieurs inadaptés avant de mettre en place un nouvel isolant, supprimer l'isolant en mauvais état ou mal posé.R ≥ 3.7 m² K/W 1Isolation des murs par l’intérieur (donne sur extérieur) : Isolation des murs par l'intérieur. Les performances thermiques minimales à respecter sont en fonction du type de paroi opaque et à la zone climatique. +Pour aller plus loin dans les recommandations, le label Effinergie impose à minima : R ≥ 4,5 m² K/W pour tout type d’isolation. +En 2025, pour bénéficier de MaPrimRénov', elle est de R ≥ 3,7 m² K/W pour l’isolation des murs, peu importe la technique d’isolationPour les murs non étanches à la vapeur d’eau, isoler avec des matériaux perméables à la vapeur d’eau.R ≥ 3,7 m² K/W 1Isolation des murs par l’intérieur (donne sur extérieur) : Isolation des murs par l'intérieur. Les performances thermiques minimales à respecter sont en fonction du type de paroi opaque et à la zone climatique. +Pour aller plus loin dans les recommandations, le label Effinergie impose à minima : R ≥ 4,5 m² K/W pour tout type d’isolation. +En 2025, pour bénéficier de MaPrimRénov', elle est de R ≥ 3,7 m² K/W pour l’isolation des murs, peu importe la technique d’isolationPour les murs non étanches à la vapeur d’eau, isoler avec des matériaux perméables à la vapeur d’eau.R ≥ 3,7 m² K/W 1Isolation des murs par l’intérieur (donne sur extérieur) : Isolation des murs par l'intérieur. Les performances thermiques minimales à respecter sont en fonction du type de paroi opaque et à la zone climatique. +Pour aller plus loin dans les recommandations, le label Effinergie impose à minima : R ≥ 4,5 m² K/W pour tout type d’isolation. +En 2025, pour bénéficier de MaPrimRénov', elle est de R ≥ 3,7 m² K/W pour l’isolation des murs, peu importe la technique d’isolationPour les murs non étanches à la vapeur d’eau, isoler avec des matériaux perméables à la vapeur d’eau.R ≥ 3,7 m² K/W 5PAC Air / Air monobloc : Installation de pompe à chaleur Air / Air monobloc (SCOP =3) +Le prix est indiqué par unité. +Travaux à réaliser en lien avec la copropriété3111.271019277296529090811Isolation par l'intérieur des Murs en contact avec un volume non chauffé : Isolation des murs en contact avec un volume non chauffé. +Mise en place d'un isolant permettant d'atteindre pour l'ensemble paroi + isolant 3.7 m²k/wSupprimer les travaux antérieurs inadaptés avant de mettre en place un nouvel isolant, supprimer l'isolant en mauvais état ou mal posé.R ≥ 3.7 m² K/W 1Isolation des murs par l’intérieur (donne sur extérieur) : Isolation des murs par l'intérieur. Les performances thermiques minimales à respecter sont en fonction du type de paroi opaque et à la zone climatique. +Pour aller plus loin dans les recommandations, le label Effinergie impose à minima : R ≥ 4,5 m² K/W pour tout type d’isolation. +En 2025, pour bénéficier de MaPrimRénov', elle est de R ≥ 3,7 m² K/W pour l’isolation des murs, peu importe la technique d’isolationPour les murs non étanches à la vapeur d’eau, isoler avec des matériaux perméables à la vapeur d’eau.R ≥ 3,7 m² K/W 1Isolation des murs par l’intérieur (donne sur extérieur) : Isolation des murs par l'intérieur. Les performances thermiques minimales à respecter sont en fonction du type de paroi opaque et à la zone climatique. +Pour aller plus loin dans les recommandations, le label Effinergie impose à minima : R ≥ 4,5 m² K/W pour tout type d’isolation. +En 2025, pour bénéficier de MaPrimRénov', elle est de R ≥ 3,7 m² K/W pour l’isolation des murs, peu importe la technique d’isolationPour les murs non étanches à la vapeur d’eau, isoler avec des matériaux perméables à la vapeur d’eau.R ≥ 3,7 m² K/W 1Isolation des murs par l’intérieur (donne sur extérieur) : Isolation des murs par l'intérieur. Les performances thermiques minimales à respecter sont en fonction du type de paroi opaque et à la zone climatique. +Pour aller plus loin dans les recommandations, le label Effinergie impose à minima : R ≥ 4,5 m² K/W pour tout type d’isolation. +En 2025, pour bénéficier de MaPrimRénov', elle est de R ≥ 3,7 m² K/W pour l’isolation des murs, peu importe la technique d’isolationPour les murs non étanches à la vapeur d’eau, isoler avec des matériaux perméables à la vapeur d’eau.R ≥ 3,7 m² K/W 5PAC Air / Air monobloc : Installation de pompe à chaleur Air / Air monobloc (SCOP =3) +Le prix est indiqué par unité. +Travaux à réaliser en lien avec la copropriété \ No newline at end of file diff --git a/test/open3cl_ecs_auxiliaires.spec.js b/test/open3cl_ecs_auxiliaires.spec.js new file mode 100644 index 00000000..1a59aeae --- /dev/null +++ b/test/open3cl_ecs_auxiliaires.spec.js @@ -0,0 +1,18 @@ +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'; + +describe('Ecs auxiliaires unit tests', () => { + test('should calculate distribution auxiliaire ecs', async () => { + set_bug_for_bug_compat(); + let input = await getAdemeFileJsonOrDownload('2674E0057251A'); + + /** @type {FullDpe} **/ + let output = calcul_3cl(structuredClone(input)); + + expect(input.logement.sortie.ef_conso.conso_auxiliaire_distribution_ecs).toBeCloseTo( + output.logement.sortie.ef_conso.conso_auxiliaire_distribution_ecs + ); + }); +});