Energiberegning etter NS 3031:2025 med timesmetode og energirapport
enok leser en bygningsbeskrivelse i TOML, henter typisk klimaår for kommunen, simulerer 8760 timer etter NS-EN ISO 52016-1 og skriver energirapport, CSV og JSON. Motor - Femnodemodell for opake bygningsdeler, tonodemodell for vinduer, luftnode og strålingsnode per sone, implisitt løsning per time med egen tett LU-løser. - Varme- og kjølebehov ved eksakt interpolasjon mellom to løsninger, siden systemet er lineært. Vinduslufting for bygninger uten kjøleanlegg. - Solberegning etter ISO 52010-1, kontrollert mot pvlib med avvik under 1 prosent. Beregningsgang - Beregningspunktene A til E: netto, brutto, tilført, levert per energibærer og vektet levert energi, med solceller, klimakorrigering og effektbehov. - Energikarakter etter Enovas skala fra 1.1.2026, med arealkorreksjon. Inndata - TOML med scenarier for tiltak, og import fra IFC, gbXML og regneark. - Normerte verdier, karakterskala, vektingsfaktorer og klimadata i data/, som kan overstyres med --data. Kontroll - Stasjonært varmetap mot U·A·ΔT innenfor 2 prosent, energibalansen innenfor 2 prosent, varmetapsbudsjettet for TEK17-referansebygget identisk med publisert tabell. Kjent avvik i romoppvarming mot Simien 8 er dokumentert i README. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GeH3y8fwTaTudaD88P57o
This commit is contained in:
+203
@@ -0,0 +1,203 @@
|
||||
//! Kjører en fullstendig energiberegning: fra bygningsbeskrivelse til energikarakter.
|
||||
|
||||
use crate::data::Datakilde;
|
||||
use crate::klima::{korreksjon, tmy, Vaerdata, REFERANSEKLIMA};
|
||||
use crate::merking::{karakter, Energikarakter};
|
||||
use crate::modell::lost::{Forutsetning, Lost};
|
||||
use crate::modell::Bygning;
|
||||
use crate::motor::{iso52016::Systemtilskudd, simuler, Timeserie, Varmetapsbudsjett};
|
||||
use crate::systemer::{self, Effektbehov, Energibudsjett, Levert, Systemoppsett};
|
||||
use crate::Result;
|
||||
use serde::Serialize;
|
||||
|
||||
/// Alt som kommer ut av én beregning.
|
||||
pub struct Resultat {
|
||||
pub bygning: Bygning,
|
||||
pub lost: Lost,
|
||||
pub vaerdata: Vaerdata,
|
||||
pub klimanavn: String,
|
||||
pub varmetap: Varmetapsbudsjett,
|
||||
pub timeserie: Timeserie,
|
||||
pub systemer: Systemoppsett,
|
||||
pub budsjett: Energibudsjett,
|
||||
pub levert: Levert,
|
||||
pub karakter: Energikarakter,
|
||||
pub effektbehov: Effektbehov,
|
||||
pub solceller: Option<crate::systemer::solcelle::Produksjon>,
|
||||
pub merknader: Vec<String>,
|
||||
}
|
||||
|
||||
/// Kompakt, serialiserbart sammendrag for `resultat.json`.
|
||||
#[derive(Serialize)]
|
||||
pub struct Sammendrag<'a> {
|
||||
pub navn: &'a str,
|
||||
pub kategori: &'a str,
|
||||
pub byggeaar: i32,
|
||||
pub oppvarmet_bra: f64,
|
||||
pub klimasted: &'a str,
|
||||
pub referanseklima: &'a str,
|
||||
pub varmetapsbudsjett: &'a Varmetapsbudsjett,
|
||||
pub energibudsjett: &'a Energibudsjett,
|
||||
pub netto_spesifikk: f64,
|
||||
pub levert: &'a Levert,
|
||||
pub energikarakter: &'a Energikarakter,
|
||||
pub effektbehov: &'a Effektbehov,
|
||||
pub forutsetninger: &'a [Forutsetning],
|
||||
pub merknader: &'a [String],
|
||||
pub maanedsverdier: Maanedsverdier,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Maanedsverdier {
|
||||
pub maaned: [&'static str; 12],
|
||||
pub romoppvarming: [f64; 12],
|
||||
pub ventilasjonsvarme: [f64; 12],
|
||||
pub varmtvann: [f64; 12],
|
||||
pub vifter_pumper: [f64; 12],
|
||||
pub belysning: [f64; 12],
|
||||
pub utstyr: [f64; 12],
|
||||
pub kjoling: [f64; 12],
|
||||
pub utetemperatur: [f64; 12],
|
||||
}
|
||||
|
||||
impl Resultat {
|
||||
pub fn sammendrag(&self) -> Sammendrag<'_> {
|
||||
let m = |s: &[f64]| Timeserie::maaned_kwh(s);
|
||||
let mut vifter_pumper = m(&self.timeserie.vifter);
|
||||
let pumper = m(&self.timeserie.pumper);
|
||||
let mut kjoling = m(&self.timeserie.romkjoling);
|
||||
let ventkjol = m(&self.timeserie.ventilasjonskjoling);
|
||||
for i in 0..12 {
|
||||
vifter_pumper[i] += pumper[i];
|
||||
kjoling[i] += ventkjol[i];
|
||||
}
|
||||
Sammendrag {
|
||||
navn: &self.lost.navn,
|
||||
kategori: self.lost.kategori.nokkel(),
|
||||
byggeaar: self.lost.byggeaar,
|
||||
oppvarmet_bra: self.lost.bra,
|
||||
klimasted: &self.klimanavn,
|
||||
referanseklima: REFERANSEKLIMA,
|
||||
varmetapsbudsjett: &self.varmetap,
|
||||
energibudsjett: &self.budsjett,
|
||||
netto_spesifikk: self.budsjett.netto_spesifikk(self.lost.bra),
|
||||
levert: &self.levert,
|
||||
energikarakter: &self.karakter,
|
||||
effektbehov: &self.effektbehov,
|
||||
forutsetninger: &self.lost.forutsetninger,
|
||||
merknader: &self.merknader,
|
||||
maanedsverdier: Maanedsverdier {
|
||||
maaned: crate::MAANEDER,
|
||||
romoppvarming: m(&self.timeserie.romoppvarming),
|
||||
ventilasjonsvarme: m(&self.timeserie.ventilasjonsvarme),
|
||||
varmtvann: m(&self.timeserie.varmtvann),
|
||||
vifter_pumper,
|
||||
belysning: m(&self.timeserie.belysning),
|
||||
utstyr: m(&self.timeserie.utstyr),
|
||||
kjoling,
|
||||
utetemperatur: self.vaerdata.maanedsmiddeltemperatur(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Henter værdata for bygningen: egen EPW-fil, oppgitt kommune, eller referanseklimaet.
|
||||
pub fn hent_klima(b: &Bygning) -> Result<(Vaerdata, String, bool)> {
|
||||
if let Some(sti) = &b.klima.epw {
|
||||
let v = Vaerdata::fra_fil(sti)?;
|
||||
let navn = v.sted.clone();
|
||||
let er_referanse = crate::data::normaliser_stedsnavn(&navn)
|
||||
== crate::data::normaliser_stedsnavn(REFERANSEKLIMA);
|
||||
return Ok((v, navn, er_referanse));
|
||||
}
|
||||
let sted = b
|
||||
.klima
|
||||
.kommune
|
||||
.clone()
|
||||
.unwrap_or_else(|| REFERANSEKLIMA.to_string());
|
||||
let er_referanse = crate::data::normaliser_stedsnavn(&sted)
|
||||
== crate::data::normaliser_stedsnavn(REFERANSEKLIMA);
|
||||
let v = tmy::for_kommune(&sted)?;
|
||||
Ok((v, sted, er_referanse))
|
||||
}
|
||||
|
||||
pub fn kjor(b: &Bygning, kilde: &Datakilde) -> Result<Resultat> {
|
||||
let lost = Lost::fra(b, kilde)?;
|
||||
let (vaerdata, klimanavn, er_referanse) = hent_klima(b)?;
|
||||
kjor_med_klima(b, kilde, lost, vaerdata, klimanavn, er_referanse)
|
||||
}
|
||||
|
||||
pub fn kjor_med_klima(
|
||||
b: &Bygning,
|
||||
kilde: &Datakilde,
|
||||
lost: Lost,
|
||||
vaerdata: Vaerdata,
|
||||
klimanavn: String,
|
||||
er_referanse: bool,
|
||||
) -> Result<Resultat> {
|
||||
let varmetap = Varmetapsbudsjett::beregn(&lost);
|
||||
let systemer_oppsett = systemer::sett_opp_systemer(b, &lost, kilde)?;
|
||||
let mut merknader = systemer_oppsett.merknader.clone();
|
||||
|
||||
let tilskudd = Systemtilskudd {
|
||||
tappevann: systemer::tappevannstilskudd(&lost, &systemer_oppsett),
|
||||
};
|
||||
let timeserie = simuler(&lost, &vaerdata, tilskudd)?;
|
||||
let budsjett = systemer::energibudsjett(×erie, &systemer_oppsett);
|
||||
|
||||
let solceller = b.solcelle.as_ref().map(|s| {
|
||||
systemer::solcelle::beregn(s, &vaerdata, ×erie, &systemer_oppsett, lost.albedo)
|
||||
});
|
||||
let (egenbruk, eksportert) = solceller
|
||||
.as_ref()
|
||||
.map(|p| (p.egenbruk_kwh, p.eksportert_kwh))
|
||||
.unwrap_or((0.0, 0.0));
|
||||
|
||||
let korrigering = if er_referanse {
|
||||
None
|
||||
} else {
|
||||
let tabell = kilde.klimakorreksjon()?;
|
||||
let klimaavhengig = systemer::klimaavhengig_spesifikk(&budsjett, lost.bra);
|
||||
let k = korreksjon::korriger(&tabell, &klimanavn, lost.kategori, klimaavhengig);
|
||||
if k.is_none() {
|
||||
merknader.push(format!(
|
||||
"Fant ingen klimakorrigeringsfaktor for «{klimanavn}». Energiytelsen er oppgitt i lokalt \
|
||||
klima og er ikke sammenlignbar med energimerkeskalaen."
|
||||
));
|
||||
}
|
||||
k
|
||||
};
|
||||
|
||||
let vekting = kilde.vekting()?;
|
||||
let levert = systemer::levert(
|
||||
&budsjett,
|
||||
&vekting,
|
||||
lost.bra,
|
||||
korrigering,
|
||||
egenbruk,
|
||||
eksportert,
|
||||
);
|
||||
let skala = kilde.skala()?;
|
||||
let karakter = karakter(
|
||||
skala.for_kategori(lost.kategori)?,
|
||||
levert.spesifikk_vektet,
|
||||
lost.skala_areal,
|
||||
);
|
||||
let effektbehov = systemer::effektbehov(×erie, &systemer_oppsett);
|
||||
|
||||
Ok(Resultat {
|
||||
bygning: b.clone(),
|
||||
lost,
|
||||
vaerdata,
|
||||
klimanavn,
|
||||
varmetap,
|
||||
timeserie,
|
||||
systemer: systemer_oppsett,
|
||||
budsjett,
|
||||
levert,
|
||||
karakter,
|
||||
effektbehov,
|
||||
solceller,
|
||||
merknader,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user