Si fra om hva beregningen måtte anta
test / test (push) Failing after 4s
Energirapporter / rapporter (release) Failing after 3s
Release enok / bygg (release) Failing after 3s

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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015GeH3y8fwTaTudaD88P57o
This commit is contained in:
bl
2026-09-14 21:20:34 +02:00
co-authored by Claude Opus 5
parent 719f5c672b
commit 98c6223439
4 changed files with 118 additions and 0 deletions
+5
View File
@@ -138,6 +138,11 @@ pub fn kjor_med_klima(
let varmetap = Varmetapsbudsjett::beregn(&lost); let varmetap = Varmetapsbudsjett::beregn(&lost);
let systemer_oppsett = systemer::sett_opp_systemer(b, &lost, kilde)?; let systemer_oppsett = systemer::sett_opp_systemer(b, &lost, kilde)?;
let mut merknader = systemer_oppsett.merknader.clone(); 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 // 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 // reduserer dermed varmetapet gjennom etasjeskilleren. Andelen som avgis i den
+3
View File
@@ -100,6 +100,9 @@ fn main() -> Result<()> {
fn beregn(a: Beregn, kilde: &Datakilde) -> Result<()> { fn beregn(a: Beregn, kilde: &Datakilde) -> Result<()> {
let bygning = inndata::les_bygning(&a.fil)?; let bygning = inndata::les_bygning(&a.fil)?;
for m in enok::modell::kontroll::kontroller(&bygning) {
eprintln!(" ! {}: {}", m.felt, m.melding);
}
let ut = let ut =
a.ut.unwrap_or_else(|| PathBuf::from("ut").join(filnavnvennlig(&bygning.bygning.navn))); a.ut.unwrap_or_else(|| PathBuf::from("ut").join(filnavnvennlig(&bygning.bygning.navn)));
std::fs::create_dir_all(&ut)?; std::fs::create_dir_all(&ut)?;
+109
View File
@@ -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<Mangel> {
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
}
+1
View File
@@ -649,4 +649,5 @@ pub struct Scenario {
pub endringer: toml::Value, pub endringer: toml::Value,
} }
pub mod kontroll;
pub mod lost; pub mod lost;