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;