From 98c622343900a13e453c72136e4e7f9cacfed531 Mon Sep 17 00:00:00 2001 From: Bendik Lynghaug Date: Mon, 14 Sep 2026 21:20:34 +0200 Subject: [PATCH] =?UTF-8?q?Si=20fra=20om=20hva=20beregningen=20m=C3=A5tte?= =?UTF-8?q?=20anta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit En modell importert fra IFC, gbXML eller regneark beskriver geometrien, men ingen av formatene sier noe om drift, tetthet eller energiforsyning. Beregningen gikk likevel gjennom uten et ord om at byggeåret var satt til 2020, at lekkasjetallet fulgte av det, at hele varmebehovet ble regnet som direkte elektrisk, og at klimaet var Oslo. For et hus fra 1948 er det forskjellen mellom 0,6 og 8 luftvekslinger per time. Nå listes slike utfyllingsvalg både på skjermen før beregningen og som merknader i rapporten. Kontrollen ser også etter en kjeller som er tatt med i klimaskjermen i stedet for å være beskrevet som egen uoppvarmet sone. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_015GeH3y8fwTaTudaD88P57o --- src/kjor.rs | 5 ++ src/main.rs | 3 ++ src/modell/kontroll.rs | 109 +++++++++++++++++++++++++++++++++++++++++ src/modell/mod.rs | 1 + 4 files changed, 118 insertions(+) create mode 100644 src/modell/kontroll.rs diff --git a/src/kjor.rs b/src/kjor.rs index c4b5bcc..6277a27 100644 --- a/src/kjor.rs +++ b/src/kjor.rs @@ -138,6 +138,11 @@ pub fn kjor_med_klima( let varmetap = Varmetapsbudsjett::beregn(&lost); let systemer_oppsett = systemer::sett_opp_systemer(b, &lost, kilde)?; let mut merknader = systemer_oppsett.merknader.clone(); + merknader.extend( + crate::modell::kontroll::kontroller(b) + .into_iter() + .map(|m| m.melding), + ); // Tap fra varmerør og bereder som står i en uoppvarmet sone varmer den sonen, og // reduserer dermed varmetapet gjennom etasjeskilleren. Andelen som avgis i den diff --git a/src/main.rs b/src/main.rs index a06711c..2c4eb3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -100,6 +100,9 @@ fn main() -> Result<()> { fn beregn(a: Beregn, kilde: &Datakilde) -> Result<()> { let bygning = inndata::les_bygning(&a.fil)?; + for m in enok::modell::kontroll::kontroller(&bygning) { + eprintln!(" ! {}: {}", m.felt, m.melding); + } let ut = a.ut.unwrap_or_else(|| PathBuf::from("ut").join(filnavnvennlig(&bygning.bygning.navn))); std::fs::create_dir_all(&ut)?; diff --git a/src/modell/kontroll.rs b/src/modell/kontroll.rs new file mode 100644 index 0000000..c687889 --- /dev/null +++ b/src/modell/kontroll.rs @@ -0,0 +1,109 @@ +//! Kontroll av hvor komplett en bygningsbeskrivelse er. +//! +//! En modell importert fra IFC, gbXML eller regneark beskriver geometrien, men ingen av +//! formatene sier noe om drift, tetthet eller energiforsyning. Beregningen går likevel +//! gjennom, med normerte verdier og verdier utledet av byggeåret. Denne modulen finner de +//! stedene der et slikt utfyllingsvalg har stor betydning for resultatet, slik at de blir +//! sagt tydelig fra om både på skjermen og i rapporten. + +use crate::modell::{Bygning, Mot, VentilasjonType}; + +/// En mangel som er verdt å nevne, med hva som ble antatt i stedet. +pub struct Mangel { + pub felt: &'static str, + pub melding: String, +} + +/// Byggeåret importørene setter når modellen ikke oppgir noe. +pub const IMPORTERT_BYGGEAAR: i32 = 2020; + +pub fn kontroller(b: &Bygning) -> Vec { + let mut ut = Vec::new(); + let importert = b + .bygning + .beskrivelse + .as_deref() + .map(|s| s.starts_with("Importert fra")) + .unwrap_or(false); + + if b.bygning.byggeaar == IMPORTERT_BYGGEAAR && importert { + ut.push(Mangel { + felt: "bygning.byggeaar", + melding: format!( + "Byggeåret er ikke oppgitt i modellen og står som {IMPORTERT_BYGGEAAR}. \ + Byggeåret styrer antatt lekkasjetall og U-verdier for bygningsdeler som mangler dem, \ + så et feil år kan endre resultatet kraftig." + ), + }); + } + + if b.infiltrasjon.n50.is_none() { + ut.push(Mangel { + felt: "infiltrasjon.n50", + melding: format!( + "Lekkasjetallet er ikke oppgitt og utledes av byggeåret {}. \ + For et eldre hus er forskjellen mellom 0,6 og 8 luftvekslinger per time stor.", + b.bygning.byggeaar + ), + }); + } + + if b.oppvarming.kilde.is_empty() { + ut.push(Mangel { + felt: "oppvarming.kilde", + melding: "Ingen varmekilde er oppgitt, så hele varmebehovet regnes dekket av direkte \ + elektrisk oppvarming. Varmepumpe, vedovn, kjel eller fjernvarme endrer både \ + levert energi og vektingen." + .into(), + }); + } + + if b.klima.kommune.is_none() && b.klima.epw.is_none() { + ut.push(Mangel { + felt: "klima.kommune", + melding: "Kommune er ikke oppgitt, så beregningen bruker referanseklimaet Oslo. \ + Det er riktig for selve energikarakteren, men gir ikke et bilde av \ + energibruken på stedet." + .into(), + }); + } + + if matches!(b.ventilasjon.typ, VentilasjonType::Naturlig) && b.ventilasjon.luftmengde.is_none() + { + ut.push(Mangel { + felt: "ventilasjon", + melding: "Ventilasjonen er ikke beskrevet, så det regnes med naturlig ventilasjon og \ + normert luftmengde. Balansert ventilasjon med varmegjenvinning gir vesentlig \ + lavere varmetap." + .into(), + }); + } + + // En uoppvarmet kjeller som er tatt med i klimaskjermen i stedet for som egen sone. + let kjellervegger: f64 = b + .flate + .iter() + .filter(|f| f.mot == Mot::Grunn && matches!(f.typ, crate::modell::FlateType::Vegg)) + .map(|f| f.areal) + .sum(); + if b.sone.is_empty() && kjellervegger > 0.25 * b.bygning.oppvarmet_bra { + ut.push(Mangel { + felt: "sone", + melding: format!( + "{kjellervegger:.0} m² kjellervegg mot grunn er regnet som en del av den oppvarmede \ + klimaskjermen. Er kjelleren uoppvarmet, bør den beskrives som en egen [[sone]], \ + slik at temperaturen der beregnes av varmebalansen." + ), + }); + } + + if importert { + ut.push(Mangel { + felt: "bygning.beskrivelse", + melding: "Modellen er importert og ikke gjennomgått. Kontroller arealer, \ + himmelretninger og hvilke rom som er oppvarmet før tallene brukes." + .into(), + }); + } + ut +} diff --git a/src/modell/mod.rs b/src/modell/mod.rs index 491865c..8e44e9f 100644 --- a/src/modell/mod.rs +++ b/src/modell/mod.rs @@ -649,4 +649,5 @@ pub struct Scenario { pub endringer: toml::Value, } +pub mod kontroll; pub mod lost;