Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e9544dd24
|
||
|
|
cbb1cedc5b
|
||
|
|
1e5d4f05bf
|
||
|
|
133152742f
|
||
|
|
959db648a9
|
Generated
+1
-1
@@ -222,7 +222,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "enok"
|
||||
version = "0.4.4"
|
||||
version = "0.4.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "enok"
|
||||
version = "0.4.4"
|
||||
version = "0.4.8"
|
||||
edition = "2021"
|
||||
description = "Årlig energiberegning og energirapport for boliger etter NS 3031 (timesmetode, ISO 52016-1)"
|
||||
license = "MIT"
|
||||
|
||||
@@ -444,22 +444,54 @@ Motoren er kontrollert mot uavhengige referanser:
|
||||
| Normerte energiposter for småhus | varmtvann 25,1, belysning 11,4, teknisk utstyr 17,5 kWh/(m²·år) |
|
||||
| Faste energiposter for yrkesbyggene | belysning, utstyr og varmtvann treffer energibudsjettene i kildene |
|
||||
|
||||
### Kjent avvik
|
||||
### Kontroll mot de publiserte referansebyggene
|
||||
|
||||
Standard Norges lanseringseksempel for NS 3031:2025 oppgir 46,2 kWh/(m²·år) romoppvarming og
|
||||
106,4 kWh/(m²·år) netto energibehov for TEK17-referansebygget beregnet med Simien 8. enok
|
||||
beregner 27,1 og 86,6 for den samme bygningen.
|
||||
Multiconsult for DiBK, «Grunnlag og forutsetninger for beregnede energirammer» (13.11.2015),
|
||||
gir geometri, komponentverdier og beregnet netto energibehov for bygningskategoriene som
|
||||
ligger til grunn for energirammene i TEK17, beregnet med Simien etter NS 3031:2014. Modellene
|
||||
er gjengitt i `eksempler/referansebygg/`, og `tests/referansebygg_2015.rs` regner dem om igjen:
|
||||
|
||||
Varmetapsbudsjettet, de normerte energipostene, solinnstrålingen og energibalansen stemmer
|
||||
hver for seg, så avviket ligger i hvor stor del av solvarmen som nyttiggjøres. NS 3031:2025
|
||||
Tillegg A er ikke offentlig tilgjengelig, så de normerte solfaktorene, skjermingsforutsetningene
|
||||
og timesprofilene Simien bruker, lar seg ikke etterprøve her. `tests/referansebygg.rs` låser
|
||||
dagens verdier slik at endringer i motoren blir synlige, og oppdateres når standardens
|
||||
forutsetninger er tilgjengelige.
|
||||
| Bygg | enok | Publisert | Avvik |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Småhus | 85,3 | 107,5 | −20,6 % |
|
||||
| Boligblokk | 77,6 | 91,8 | −15,5 % |
|
||||
| Barnehage | 80,1 | 132,0 | −39,3 % |
|
||||
| Kontorbygning | 93,5 | 115,3 | −18,9 % |
|
||||
| Skolebygning | 72,8 | 106,8 | −31,8 % |
|
||||
| Idrettsbygning | 106,7 | 144,3 | −26,0 % |
|
||||
| Kulturbygning | 73,2 | 128,0 | −42,8 % |
|
||||
| **Snitt** | | | **−27,9 %** |
|
||||
|
||||
Bruk derfor tallene til å sammenligne tiltak mot hverandre og til å se hvor varmen forsvinner.
|
||||
Til en energiattest som skal leveres til Enova må resultatet kontrolleres mot et validert
|
||||
beregningsprogram.
|
||||
Netto energibehov, kWh/(m²·år), Oslo-klima. Kjør `cargo test --release --test
|
||||
referansebygg_2015 -- --nocapture` for tabellen.
|
||||
|
||||
**enok regner gjennomgående for lavt.** Avviket er systematisk og går én vei, så det kommer
|
||||
neppe av inndataene for den enkelte kategorien. Dette er kontrollert:
|
||||
|
||||
- Varmetapstallet for småhuset blir 0,70 W/(m²K) mot publisert 0,69, så geometri og
|
||||
U-verdier i modellene er riktig gjengitt.
|
||||
- De faste postene, varmtvann, belysning og teknisk utstyr, treffer de normerte verdiene
|
||||
nøyaktig.
|
||||
- Solinnstrålingen avviker under 1 % fra pvlib, og energibalansen går opp.
|
||||
|
||||
Avviket ligger dermed i den dynamiske delen: romoppvarming og ventilasjonsvarme. De to mest
|
||||
sannsynlige årsakene er hvor stor del av sol- og internvarmen som regnes nyttiggjort, der
|
||||
NS 3031:2025 Tillegg A gir timesprofiler og solfaktorer som ikke er offentlig tilgjengelige,
|
||||
og luftmengdene i NS 3031 tabell B.1, som er anslått for flere yrkesbyggkategorier.
|
||||
|
||||
Standard Norges lanseringseksempel for NS 3031:2025 peker samme vei: det oppgir 46,2
|
||||
kWh/(m²·år) romoppvarming og 106,4 kWh/(m²·år) netto energibehov for et TEK17-småhus, mot
|
||||
enoks 27,1 og 86,6.
|
||||
|
||||
### Hva tallene kan brukes til
|
||||
|
||||
Sammenligning av tiltak mot hverandre, og å se hvor varmen forsvinner. Et systematisk avvik
|
||||
som dette slår omtrent likt ut på alle alternativene, så rekkefølgen mellom tiltak står seg.
|
||||
|
||||
Den absolutte energikarakteren gjør den ikke. En energiattest skal lages av en godkjent
|
||||
ekspert i Enovas system, med et beregningsprogram som er validert for formålet, og enok er
|
||||
ikke validert av noen. Bruk derfor karakteren her som en indikasjon, og kontroller den mot et
|
||||
validert program før den brukes til noe.
|
||||
|
||||
## Datagrunnlag
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
# Barnehage — referansebygg for energirammene i TEK17.
|
||||
#
|
||||
# Geometri og komponentverdier fra Multiconsult for DiBK, «Grunnlag og forutsetninger for
|
||||
# beregnede energirammer» (128298-RIEn-NOT-001, 13. november 2015), tabell 1 og 2. Notatet
|
||||
# oppgir beregnet netto energibehov 132.0 kWh/(m²·år) for denne modellen, beregnet
|
||||
# med Simien etter NS 3031:2014. Filen er enoks gjengivelse av den samme modellen.
|
||||
#
|
||||
# Forenklinger: taket er regnet flatt, og veggarealet er omkrets × etasjehøyde × antall
|
||||
# etasjer minus vindusarealet.
|
||||
|
||||
[bygning]
|
||||
navn = "Barnehage (referansebygg TEK17)"
|
||||
kategori = "barnehage"
|
||||
byggeaar = 2017
|
||||
oppvarmet_bra = 300.0
|
||||
oppvarmet_volum = 960.0
|
||||
|
||||
[klima]
|
||||
kommune = "Oslo"
|
||||
|
||||
[[flate]]
|
||||
navn = "Yttervegger"
|
||||
type = "vegg"
|
||||
areal = 205.0
|
||||
u_verdi = 0.18
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Tak"
|
||||
type = "tak"
|
||||
areal = 300.0
|
||||
u_verdi = 0.13
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Gulv på grunn"
|
||||
type = "gulv"
|
||||
areal = 300.0
|
||||
u_verdi = 0.10
|
||||
mot = "grunn"
|
||||
omkrets = 80.0
|
||||
masseklasse = "tung"
|
||||
kommentar = "U-verdien i notatet er oppgitt inkludert varmemotstanden i grunnen"
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer nord"
|
||||
areal = 22.5
|
||||
orientering = 0
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer øst"
|
||||
areal = 15.0
|
||||
orientering = 90
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer sør"
|
||||
areal = 22.5
|
||||
orientering = 180
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer vest"
|
||||
areal = 15.0
|
||||
orientering = 270
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[kuldebroer]
|
||||
normalisert = 0.07
|
||||
|
||||
[infiltrasjon]
|
||||
n50 = 0.6
|
||||
skjerming = "moderat"
|
||||
|
||||
[ventilasjon]
|
||||
type = "balansert"
|
||||
luftmengde = 8.0
|
||||
gjenvinning = 0.85
|
||||
sfp = 1.5
|
||||
|
||||
[drift]
|
||||
belysning_effekt = 6.4
|
||||
|
||||
[distribusjon]
|
||||
type = "punkt"
|
||||
|
||||
[[oppvarming.kilde]]
|
||||
type = "direkte_el"
|
||||
andel_rom = 1.0
|
||||
andel_tappevann = 1.0
|
||||
@@ -0,0 +1,83 @@
|
||||
# Boligblokk — referansebygg for energirammene i TEK17.
|
||||
#
|
||||
# Geometri og komponentverdier fra Multiconsult for DiBK, «Grunnlag og forutsetninger for
|
||||
# beregnede energirammer» (128298-RIEn-NOT-001, 13. november 2015), tabell 1 og 2. Notatet
|
||||
# oppgir beregnet netto energibehov 91.8 kWh/(m²·år) for denne modellen, beregnet
|
||||
# med Simien etter NS 3031:2014. Filen er enoks gjengivelse av den samme modellen.
|
||||
#
|
||||
# Forenklinger: taket er regnet flatt, og veggarealet er omkrets × etasjehøyde × antall
|
||||
# etasjer minus vindusarealet.
|
||||
|
||||
[bygning]
|
||||
navn = "Boligblokk (referansebygg TEK17)"
|
||||
kategori = "boligblokk"
|
||||
byggeaar = 2017
|
||||
oppvarmet_bra = 900.0
|
||||
oppvarmet_volum = 2430.0
|
||||
|
||||
[klima]
|
||||
kommune = "Oslo"
|
||||
|
||||
[[flate]]
|
||||
navn = "Yttervegger"
|
||||
type = "vegg"
|
||||
areal = 495.0
|
||||
u_verdi = 0.18
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Tak"
|
||||
type = "tak"
|
||||
areal = 300.0
|
||||
u_verdi = 0.13
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Gulv på grunn"
|
||||
type = "gulv"
|
||||
areal = 300.0
|
||||
u_verdi = 0.10
|
||||
mot = "grunn"
|
||||
omkrets = 80.0
|
||||
masseklasse = "tung"
|
||||
kommentar = "U-verdien i notatet er oppgitt inkludert varmemotstanden i grunnen"
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer nord"
|
||||
areal = 112.5
|
||||
orientering = 0
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer sør"
|
||||
areal = 112.5
|
||||
orientering = 180
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[kuldebroer]
|
||||
normalisert = 0.07
|
||||
|
||||
[infiltrasjon]
|
||||
n50 = 0.6
|
||||
skjerming = "moderat"
|
||||
|
||||
[ventilasjon]
|
||||
type = "balansert"
|
||||
luftmengde = 1.5
|
||||
gjenvinning = 0.8
|
||||
sfp = 1.5
|
||||
|
||||
[drift]
|
||||
belysning_effekt = 1.95
|
||||
|
||||
[distribusjon]
|
||||
type = "punkt"
|
||||
|
||||
[[oppvarming.kilde]]
|
||||
type = "direkte_el"
|
||||
andel_rom = 1.0
|
||||
andel_tappevann = 1.0
|
||||
@@ -0,0 +1,104 @@
|
||||
# Idrettsbygning — referansebygg for energirammene i TEK17.
|
||||
#
|
||||
# Geometri og komponentverdier fra Multiconsult for DiBK, «Grunnlag og forutsetninger for
|
||||
# beregnede energirammer» (128298-RIEn-NOT-001, 13. november 2015), tabell 1 og 2. Notatet
|
||||
# oppgir beregnet netto energibehov 144.3 kWh/(m²·år) for denne modellen, beregnet
|
||||
# med Simien etter NS 3031:2014. Filen er enoks gjengivelse av den samme modellen.
|
||||
#
|
||||
# Forenklinger: taket er regnet flatt, og veggarealet er omkrets × etasjehøyde × antall
|
||||
# etasjer minus vindusarealet.
|
||||
|
||||
[bygning]
|
||||
navn = "Idrettsbygning (referansebygg TEK17)"
|
||||
kategori = "idrettsbygg"
|
||||
byggeaar = 2017
|
||||
oppvarmet_bra = 3200.0
|
||||
oppvarmet_volum = 25600.0
|
||||
|
||||
[klima]
|
||||
kommune = "Oslo"
|
||||
|
||||
[[flate]]
|
||||
navn = "Yttervegger"
|
||||
type = "vegg"
|
||||
areal = 1192.0
|
||||
u_verdi = 0.18
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Tak"
|
||||
type = "tak"
|
||||
areal = 3200.0
|
||||
u_verdi = 0.13
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Gulv på grunn"
|
||||
type = "gulv"
|
||||
areal = 3200.0
|
||||
u_verdi = 0.10
|
||||
mot = "grunn"
|
||||
omkrets = 240.0
|
||||
masseklasse = "tung"
|
||||
kommentar = "U-verdien i notatet er oppgitt inkludert varmemotstanden i grunnen"
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer nord"
|
||||
areal = 240.0
|
||||
orientering = 0
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer øst"
|
||||
areal = 160.0
|
||||
orientering = 90
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer sør"
|
||||
areal = 240.0
|
||||
orientering = 180
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer vest"
|
||||
areal = 160.0
|
||||
orientering = 270
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[kuldebroer]
|
||||
normalisert = 0.07
|
||||
|
||||
[infiltrasjon]
|
||||
n50 = 0.6
|
||||
skjerming = "moderat"
|
||||
|
||||
[ventilasjon]
|
||||
type = "balansert"
|
||||
luftmengde = 6.4
|
||||
gjenvinning = 0.85
|
||||
sfp = 1.5
|
||||
kjolebatteri = true
|
||||
|
||||
[drift]
|
||||
belysning_effekt = 6.4
|
||||
|
||||
[distribusjon]
|
||||
type = "radiator"
|
||||
|
||||
[[oppvarming.kilde]]
|
||||
type = "direkte_el"
|
||||
andel_rom = 1.0
|
||||
andel_tappevann = 1.0
|
||||
@@ -0,0 +1,104 @@
|
||||
# Kontorbygning — referansebygg for energirammene i TEK17.
|
||||
#
|
||||
# Geometri og komponentverdier fra Multiconsult for DiBK, «Grunnlag og forutsetninger for
|
||||
# beregnede energirammer» (128298-RIEn-NOT-001, 13. november 2015), tabell 1 og 2. Notatet
|
||||
# oppgir beregnet netto energibehov 115.3 kWh/(m²·år) for denne modellen, beregnet
|
||||
# med Simien etter NS 3031:2014. Filen er enoks gjengivelse av den samme modellen.
|
||||
#
|
||||
# Forenklinger: taket er regnet flatt, og veggarealet er omkrets × etasjehøyde × antall
|
||||
# etasjer minus vindusarealet.
|
||||
|
||||
[bygning]
|
||||
navn = "Kontorbygning (referansebygg TEK17)"
|
||||
kategori = "kontorbygg"
|
||||
byggeaar = 2017
|
||||
oppvarmet_bra = 3600.0
|
||||
oppvarmet_volum = 11520.0
|
||||
|
||||
[klima]
|
||||
kommune = "Oslo"
|
||||
|
||||
[[flate]]
|
||||
navn = "Yttervegger"
|
||||
type = "vegg"
|
||||
areal = 780.0
|
||||
u_verdi = 0.18
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Tak"
|
||||
type = "tak"
|
||||
areal = 1200.0
|
||||
u_verdi = 0.13
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Gulv på grunn"
|
||||
type = "gulv"
|
||||
areal = 1200.0
|
||||
u_verdi = 0.10
|
||||
mot = "grunn"
|
||||
omkrets = 160.0
|
||||
masseklasse = "tung"
|
||||
kommentar = "U-verdien i notatet er oppgitt inkludert varmemotstanden i grunnen"
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer nord"
|
||||
areal = 270.0
|
||||
orientering = 0
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer øst"
|
||||
areal = 180.0
|
||||
orientering = 90
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer sør"
|
||||
areal = 270.0
|
||||
orientering = 180
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer vest"
|
||||
areal = 180.0
|
||||
orientering = 270
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[kuldebroer]
|
||||
normalisert = 0.07
|
||||
|
||||
[infiltrasjon]
|
||||
n50 = 0.6
|
||||
skjerming = "moderat"
|
||||
|
||||
[ventilasjon]
|
||||
type = "balansert"
|
||||
luftmengde = 8.0
|
||||
gjenvinning = 0.85
|
||||
sfp = 1.5
|
||||
kjolebatteri = true
|
||||
|
||||
[drift]
|
||||
belysning_effekt = 6.4
|
||||
|
||||
[distribusjon]
|
||||
type = "radiator"
|
||||
|
||||
[[oppvarming.kilde]]
|
||||
type = "direkte_el"
|
||||
andel_rom = 1.0
|
||||
andel_tappevann = 1.0
|
||||
@@ -0,0 +1,104 @@
|
||||
# Kulturbygning — referansebygg for energirammene i TEK17.
|
||||
#
|
||||
# Geometri og komponentverdier fra Multiconsult for DiBK, «Grunnlag og forutsetninger for
|
||||
# beregnede energirammer» (128298-RIEn-NOT-001, 13. november 2015), tabell 1 og 2. Notatet
|
||||
# oppgir beregnet netto energibehov 128.0 kWh/(m²·år) for denne modellen, beregnet
|
||||
# med Simien etter NS 3031:2014. Filen er enoks gjengivelse av den samme modellen.
|
||||
#
|
||||
# Forenklinger: taket er regnet flatt, og veggarealet er omkrets × etasjehøyde × antall
|
||||
# etasjer minus vindusarealet.
|
||||
|
||||
[bygning]
|
||||
navn = "Kulturbygning (referansebygg TEK17)"
|
||||
kategori = "kulturbygg"
|
||||
byggeaar = 2017
|
||||
oppvarmet_bra = 2400.0
|
||||
oppvarmet_volum = 7680.0
|
||||
|
||||
[klima]
|
||||
kommune = "Oslo"
|
||||
|
||||
[[flate]]
|
||||
navn = "Yttervegger"
|
||||
type = "vegg"
|
||||
areal = 520.0
|
||||
u_verdi = 0.18
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Tak"
|
||||
type = "tak"
|
||||
areal = 1200.0
|
||||
u_verdi = 0.13
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Gulv på grunn"
|
||||
type = "gulv"
|
||||
areal = 1200.0
|
||||
u_verdi = 0.10
|
||||
mot = "grunn"
|
||||
omkrets = 160.0
|
||||
masseklasse = "tung"
|
||||
kommentar = "U-verdien i notatet er oppgitt inkludert varmemotstanden i grunnen"
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer nord"
|
||||
areal = 180.0
|
||||
orientering = 0
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer øst"
|
||||
areal = 120.0
|
||||
orientering = 90
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer sør"
|
||||
areal = 180.0
|
||||
orientering = 180
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer vest"
|
||||
areal = 120.0
|
||||
orientering = 270
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[kuldebroer]
|
||||
normalisert = 0.07
|
||||
|
||||
[infiltrasjon]
|
||||
n50 = 0.6
|
||||
skjerming = "moderat"
|
||||
|
||||
[ventilasjon]
|
||||
type = "balansert"
|
||||
luftmengde = 8.0
|
||||
gjenvinning = 0.85
|
||||
sfp = 1.5
|
||||
kjolebatteri = true
|
||||
|
||||
[drift]
|
||||
belysning_effekt = 6.4
|
||||
|
||||
[distribusjon]
|
||||
type = "radiator"
|
||||
|
||||
[[oppvarming.kilde]]
|
||||
type = "direkte_el"
|
||||
andel_rom = 1.0
|
||||
andel_tappevann = 1.0
|
||||
@@ -0,0 +1,104 @@
|
||||
# Skolebygning — referansebygg for energirammene i TEK17.
|
||||
#
|
||||
# Geometri og komponentverdier fra Multiconsult for DiBK, «Grunnlag og forutsetninger for
|
||||
# beregnede energirammer» (128298-RIEn-NOT-001, 13. november 2015), tabell 1 og 2. Notatet
|
||||
# oppgir beregnet netto energibehov 106.8 kWh/(m²·år) for denne modellen, beregnet
|
||||
# med Simien etter NS 3031:2014. Filen er enoks gjengivelse av den samme modellen.
|
||||
#
|
||||
# Forenklinger: taket er regnet flatt, og veggarealet er omkrets × etasjehøyde × antall
|
||||
# etasjer minus vindusarealet.
|
||||
|
||||
[bygning]
|
||||
navn = "Skolebygning (referansebygg TEK17)"
|
||||
kategori = "skolebygg"
|
||||
byggeaar = 2017
|
||||
oppvarmet_bra = 2400.0
|
||||
oppvarmet_volum = 7680.0
|
||||
|
||||
[klima]
|
||||
kommune = "Oslo"
|
||||
|
||||
[[flate]]
|
||||
navn = "Yttervegger"
|
||||
type = "vegg"
|
||||
areal = 520.0
|
||||
u_verdi = 0.18
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Tak"
|
||||
type = "tak"
|
||||
areal = 1200.0
|
||||
u_verdi = 0.13
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Gulv på grunn"
|
||||
type = "gulv"
|
||||
areal = 1200.0
|
||||
u_verdi = 0.10
|
||||
mot = "grunn"
|
||||
omkrets = 160.0
|
||||
masseklasse = "tung"
|
||||
kommentar = "U-verdien i notatet er oppgitt inkludert varmemotstanden i grunnen"
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer nord"
|
||||
areal = 180.0
|
||||
orientering = 0
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer øst"
|
||||
areal = 120.0
|
||||
orientering = 90
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer sør"
|
||||
areal = 180.0
|
||||
orientering = 180
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer vest"
|
||||
areal = 120.0
|
||||
orientering = 270
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
solskjerming = { type = "automatisk", faktor = 0.125 }
|
||||
|
||||
[kuldebroer]
|
||||
normalisert = 0.07
|
||||
|
||||
[infiltrasjon]
|
||||
n50 = 0.6
|
||||
skjerming = "moderat"
|
||||
|
||||
[ventilasjon]
|
||||
type = "balansert"
|
||||
luftmengde = 8.0
|
||||
gjenvinning = 0.85
|
||||
sfp = 1.5
|
||||
kjolebatteri = true
|
||||
|
||||
[drift]
|
||||
belysning_effekt = 8.0
|
||||
|
||||
[distribusjon]
|
||||
type = "radiator"
|
||||
|
||||
[[oppvarming.kilde]]
|
||||
type = "direkte_el"
|
||||
andel_rom = 1.0
|
||||
andel_tappevann = 1.0
|
||||
@@ -0,0 +1,99 @@
|
||||
# Småhus — referansebygg for energirammene i TEK17.
|
||||
#
|
||||
# Geometri og komponentverdier fra Multiconsult for DiBK, «Grunnlag og forutsetninger for
|
||||
# beregnede energirammer» (128298-RIEn-NOT-001, 13. november 2015), tabell 1 og 2. Notatet
|
||||
# oppgir beregnet netto energibehov 107.5 kWh/(m²·år) for denne modellen, beregnet
|
||||
# med Simien etter NS 3031:2014. Filen er enoks gjengivelse av den samme modellen.
|
||||
#
|
||||
# Forenklinger: taket er regnet flatt, og veggarealet er omkrets × etasjehøyde × antall
|
||||
# etasjer minus vindusarealet.
|
||||
|
||||
[bygning]
|
||||
navn = "Småhus (referansebygg TEK17)"
|
||||
kategori = "smaahus"
|
||||
byggeaar = 2017
|
||||
oppvarmet_bra = 160.0
|
||||
oppvarmet_volum = 459.2
|
||||
|
||||
[klima]
|
||||
kommune = "Oslo"
|
||||
|
||||
[[flate]]
|
||||
navn = "Yttervegger"
|
||||
type = "vegg"
|
||||
areal = 188.2
|
||||
u_verdi = 0.18
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Tak"
|
||||
type = "tak"
|
||||
areal = 80.0
|
||||
u_verdi = 0.13
|
||||
masseklasse = "middels"
|
||||
|
||||
[[flate]]
|
||||
navn = "Gulv på grunn"
|
||||
type = "gulv"
|
||||
areal = 80.0
|
||||
u_verdi = 0.10
|
||||
mot = "grunn"
|
||||
omkrets = 36.0
|
||||
masseklasse = "tung"
|
||||
kommentar = "U-verdien i notatet er oppgitt inkludert varmemotstanden i grunnen"
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer nord"
|
||||
areal = 12.0
|
||||
orientering = 0
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer øst"
|
||||
areal = 8.0
|
||||
orientering = 90
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer sør"
|
||||
areal = 12.0
|
||||
orientering = 180
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[[vindu]]
|
||||
navn = "Vinduer vest"
|
||||
areal = 8.0
|
||||
orientering = 270
|
||||
u_verdi = 0.80
|
||||
g_verdi = 0.40
|
||||
karmandel = 0.20
|
||||
|
||||
[kuldebroer]
|
||||
normalisert = 0.05
|
||||
|
||||
[infiltrasjon]
|
||||
n50 = 0.6
|
||||
skjerming = "moderat"
|
||||
|
||||
[ventilasjon]
|
||||
type = "balansert"
|
||||
luftmengde = 1.2
|
||||
gjenvinning = 0.8
|
||||
sfp = 1.5
|
||||
|
||||
[drift]
|
||||
belysning_effekt = 1.95
|
||||
|
||||
[distribusjon]
|
||||
type = "punkt"
|
||||
|
||||
[[oppvarming.kilde]]
|
||||
type = "direkte_el"
|
||||
andel_rom = 1.0
|
||||
andel_tappevann = 1.0
|
||||
+21
-15
@@ -186,6 +186,8 @@ pub fn fra_tekst_med(tekst: &str, standardnavn: &str, valg: &Importvalg) -> Resu
|
||||
areal: flate.map(|f| f.areal).unwrap_or(0.0),
|
||||
normal: flate.map(|f| f.normal),
|
||||
z: plass.map(|p| p.2).unwrap_or(0.0) + flate.map(|f| f.hoyde).unwrap_or(0.0),
|
||||
topp: plass.map(|p| p.2).unwrap_or(0.0) + flate.map(|f| f.topp).unwrap_or(0.0),
|
||||
omkrets: flate.map(|f| f.omkrets()).unwrap_or(0.0),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -363,12 +365,13 @@ pub fn fra_tekst_med(tekst: &str, standardnavn: &str, valg: &Importvalg) -> Resu
|
||||
}
|
||||
// Terrengmodellen sier hvor høyt bakken ligger akkurat der denne veggen står, så en
|
||||
// skrånende tomt gir forskjellig høyde over bakken på hver side av huset.
|
||||
// Høyden måles fra overkant mur, ikke fra gulvet over: dekket selv står ikke i bakken.
|
||||
let over_bakken = d
|
||||
.plass
|
||||
.and_then(|(x, y, _, _, _)| terreng_ved(&terrengmodell, x, y))
|
||||
.map(|z| (etasjekontekst.forste_over_hoyde.unwrap_or(0.0) - z).clamp(0.0, kjellerdybde))
|
||||
.map(|z| (d.topp - z).clamp(0.0, kjellerdybde))
|
||||
.unwrap_or(kjeller_over_terreng);
|
||||
if i_kjeller && mot == Mot::Grunn {
|
||||
if i_kjeller && mot == Mot::Grunn && typ == FlateType::Vegg {
|
||||
grunnmurshoyder.push(over_bakken);
|
||||
}
|
||||
let mal = if i_kjeller {
|
||||
@@ -389,7 +392,8 @@ pub fn fra_tekst_med(tekst: &str, standardnavn: &str, valg: &Importvalg) -> Resu
|
||||
mot,
|
||||
b_faktor: None,
|
||||
absorptans: None,
|
||||
omkrets: None,
|
||||
omkrets: (typ == FlateType::Gulv && mot == Mot::Grunn && d.omkrets > 0.0)
|
||||
.then_some(d.omkrets),
|
||||
dybde: (i_kjeller && mot == Mot::Grunn).then_some(kjellerdybde - over_bakken),
|
||||
over_terreng: (i_kjeller && mot == Mot::Grunn).then_some(over_bakken),
|
||||
sone,
|
||||
@@ -1316,6 +1320,10 @@ struct Del {
|
||||
normal: Option<[f64; 3]>,
|
||||
/// Delens høyde over byggets nullpunkt, av geometrien, m.
|
||||
z: f64,
|
||||
/// Overkanten av delen over byggets nullpunkt, m. For en kjellervegg er det overkant mur.
|
||||
topp: f64,
|
||||
/// Omkretsen av delens omriss, m. Et gulv mot grunnen trenger den for B′.
|
||||
omkrets: f64,
|
||||
}
|
||||
|
||||
/// Oversetter en fritekstverdi fra modellen til en bygningskategori.
|
||||
@@ -1504,20 +1512,15 @@ fn klassifiser(
|
||||
if d.ytre == Some(true) {
|
||||
return Some((FlateType::Gulv, Mot::Ute));
|
||||
}
|
||||
// Mot grunnen i det nederste planet, eller når navnet sier det. Høyden av geometrien
|
||||
// avgjør, siden etasjetilknytningen i en eksport kan være feil.
|
||||
let paa_grunnen = [
|
||||
"kjellergulv",
|
||||
"on grade",
|
||||
"på grunn",
|
||||
"paa grunn",
|
||||
"såle",
|
||||
"sale",
|
||||
];
|
||||
if (d.z - laveste).abs() < 0.5 || paa_grunnen.iter().any(|o| navn.contains(o)) {
|
||||
// Høyden av geometrien avgjør, siden etasjetilknytningen i en eksport kan være feil.
|
||||
// Et dekke som ligger på bakken sier det i navnet. Ellers er et dekke like under
|
||||
// gulvet i første etasje himlingen over kjelleren, og det nederste dekket ligger mot
|
||||
// grunnen. «Kjellergulv» er ikke nok til å si at dekket ligger på bakken: dekket over
|
||||
// kjelleren bærer ofte det samme navnet.
|
||||
let paa_grunnen = ["on grade", "på grunn", "paa grunn", "såle", "sale"];
|
||||
if paa_grunnen.iter().any(|o| navn.contains(o)) {
|
||||
return Some((FlateType::Gulv, Mot::Grunn));
|
||||
}
|
||||
// Dekket i laveste etasje over en kjeller er grensen mot den uoppvarmede sonen.
|
||||
if e.kjeller.is_some() {
|
||||
let ved_forste_over = e
|
||||
.forste_over_hoyde
|
||||
@@ -1527,6 +1530,9 @@ fn klassifiser(
|
||||
return Some((FlateType::Gulv, Mot::Uoppvarmet));
|
||||
}
|
||||
}
|
||||
if (d.z - laveste).abs() < 0.5 {
|
||||
return Some((FlateType::Gulv, Mot::Grunn));
|
||||
}
|
||||
// Dekker lenger opp er etasjeskillere inne i bygningen.
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ pub struct Hovedflate {
|
||||
/// Midlere høyde over delens eget nullpunkt, m. Sier hvilken etasje delen ligger i når
|
||||
/// plasseringen i filen ikke gjør det.
|
||||
pub hoyde: f64,
|
||||
/// Øverste punkt over delens eget nullpunkt, m. For en kjellervegg er det overkant mur.
|
||||
pub topp: f64,
|
||||
}
|
||||
|
||||
impl Nett {
|
||||
@@ -103,11 +105,18 @@ impl Nett {
|
||||
} else {
|
||||
self.punkter.iter().map(|p| p[2]).sum::<f64>() / self.punkter.len() as f64 * skala
|
||||
};
|
||||
let topp = self
|
||||
.punkter
|
||||
.iter()
|
||||
.map(|p| p[2])
|
||||
.fold(f64::NEG_INFINITY, f64::max)
|
||||
* skala;
|
||||
Some(Hovedflate {
|
||||
areal: sum / 2.0,
|
||||
normal,
|
||||
plan: self.utstrekning(normal, skala),
|
||||
hoyde,
|
||||
topp: if topp.is_finite() { topp } else { 0.0 },
|
||||
})
|
||||
}
|
||||
|
||||
@@ -189,9 +198,14 @@ pub fn samlet_hovedflate(deler: &[Nett], skala: f64) -> Option<Hovedflate> {
|
||||
} else {
|
||||
grupper[0].hoyde
|
||||
};
|
||||
let topp = grupper
|
||||
.iter()
|
||||
.map(|g| g.topp)
|
||||
.fold(f64::NEG_INFINITY, f64::max);
|
||||
Some(Hovedflate {
|
||||
areal,
|
||||
hoyde,
|
||||
topp,
|
||||
..grupper[0]
|
||||
})
|
||||
}
|
||||
@@ -210,6 +224,22 @@ fn overlapper(a: &[f64; 4], b: &[f64; 4]) -> bool {
|
||||
dekning(a[0], a[1], b[0], b[1]) && dekning(a[2], a[3], b[2], b[3])
|
||||
}
|
||||
|
||||
impl Hovedflate {
|
||||
/// Omkretsen av flatens omriss, m, regnet av utstrekningen i flatens eget plan.
|
||||
///
|
||||
/// Et gulv mot grunnen trenger omkretsen for B′ etter NS-EN ISO 13370. Omrisset regnes
|
||||
/// som et rektangel, som stemmer godt for de fleste grunnflater.
|
||||
pub fn omkrets(&self) -> f64 {
|
||||
let bredde = self.plan[1] - self.plan[0];
|
||||
let lengde = self.plan[3] - self.plan[2];
|
||||
if bredde > 0.0 && lengde > 0.0 {
|
||||
2.0 * (bredde + lengde)
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Henter geometrien til et produkt via `Representation`, med én oppføring per geometridel.
|
||||
pub fn netter_for_produkt(fil: &Fil, representasjon: Option<u64>) -> Vec<Nett> {
|
||||
let mut deler = Vec::new();
|
||||
@@ -562,6 +592,9 @@ mod tester {
|
||||
let h = n.hovedflate(1.0).unwrap();
|
||||
assert!((h.areal - 12.0).abs() < 1e-9, "areal ble {}", h.areal);
|
||||
assert!(h.normal[2].abs() > 0.99, "normal ble {:?}", h.normal);
|
||||
// Midlere høyde er halve tykkelsen, og overkanten er tykkelsen.
|
||||
assert!((h.hoyde - z / 2.0).abs() < 1e-9, "høyde ble {}", h.hoyde);
|
||||
assert!((h.topp - z).abs() < 1e-9, "topp ble {}", h.topp);
|
||||
}
|
||||
|
||||
/// Et hull i flaten skal trekkes fra, siden hullet mangler trekanter.
|
||||
|
||||
+246
-11
@@ -2,6 +2,7 @@
|
||||
|
||||
use crate::kjor::Resultat;
|
||||
use crate::merking::skala::BOKSTAVER;
|
||||
use crate::modell::lost::Forutsetning;
|
||||
use crate::modell::{FlateType, Mot};
|
||||
use crate::rapport::svg;
|
||||
use crate::Result;
|
||||
@@ -437,7 +438,10 @@ fn kilde(r: &Resultat, scenarier: &[(String, Resultat)]) -> String {
|
||||
|
||||
// ── inndata ─────────────────────────────────────────────────────
|
||||
s.push_str("#pagebreak()\n\n= Inndata\n\n== Bygningskropp\n\n");
|
||||
s.push_str("#tabell(\n kolonner: (1fr, auto, auto, auto, auto, auto),\n justering: (left, left, right, right, right, left),\n overskrift: (\"Bygningsdel\", \"Type\", \"Areal, m²\", \"U-verdi\", \"Orientering\", \"Mot\"),\n rader: (\n");
|
||||
s.push_str("#tabell(\n kolonner: (1fr, auto, auto, auto, auto, auto, auto),\n justering: (left, left, right, right, right, right, left),\n overskrift: (\"Bygningsdel\", \"Type\", \"Antall\", \"Areal, m²\", \"U-verdi\", \"Orientering\", \"Mot\"),\n rader: (\n");
|
||||
// Like bygningsdeler står på én linje: modellen deler gjerne den samme konstruksjonen i
|
||||
// mange like vinduer eller veggfelt, og da sier antallet mer enn én rad per stykk.
|
||||
let mut samlet: Vec<(String, String, String, String, usize, f64)> = Vec::new();
|
||||
for f in &l.flater {
|
||||
let typ = if f.glass {
|
||||
"Vindu/dør"
|
||||
@@ -453,15 +457,62 @@ fn kilde(r: &Resultat, scenarier: &[(String, Resultat)]) -> String {
|
||||
Mot::Grunn => "grunnen",
|
||||
Mot::Uoppvarmet => "uoppvarmet",
|
||||
};
|
||||
let navn = f
|
||||
.navn
|
||||
.split(" #")
|
||||
.next()
|
||||
.unwrap_or(&f.navn)
|
||||
.trim()
|
||||
.to_string();
|
||||
let nokkel = (
|
||||
navn,
|
||||
typ.to_string(),
|
||||
tall(f.u_verdi, 2),
|
||||
himmelretning(f.orientering, f.helning),
|
||||
mot.to_string(),
|
||||
);
|
||||
match samlet.iter_mut().find(|(n, t, u, o, _, _)| {
|
||||
(n.as_str(), t.as_str(), u.as_str(), o.as_str())
|
||||
== (
|
||||
nokkel.0.as_str(),
|
||||
nokkel.1.as_str(),
|
||||
nokkel.2.as_str(),
|
||||
nokkel.3.as_str(),
|
||||
)
|
||||
}) {
|
||||
Some((_, _, _, _, antall, areal)) => {
|
||||
*antall += 1;
|
||||
*areal += f.areal;
|
||||
}
|
||||
None => samlet.push((nokkel.0, nokkel.1, nokkel.2, nokkel.3, 1, f.areal)),
|
||||
}
|
||||
let _ = nokkel.4;
|
||||
}
|
||||
for (navn, typ, u, orientering, antall, areal) in &samlet {
|
||||
let mot = l
|
||||
.flater
|
||||
.iter()
|
||||
.find(|f| f.navn.split(" #").next().unwrap_or(&f.navn).trim() == navn)
|
||||
.map(|f| match f.mot {
|
||||
Mot::Ute => "det fri",
|
||||
Mot::Grunn => "grunnen",
|
||||
Mot::Uoppvarmet => "uoppvarmet",
|
||||
})
|
||||
.unwrap_or("det fri");
|
||||
s.push_str(&format!(
|
||||
" {}\n",
|
||||
rad(&[
|
||||
e(&f.navn),
|
||||
typ.into(),
|
||||
tall(f.areal, 1),
|
||||
tall(f.u_verdi, 2),
|
||||
himmelretning(f.orientering, f.helning),
|
||||
mot.into()
|
||||
e(navn),
|
||||
typ.clone(),
|
||||
if *antall > 1 {
|
||||
antall.to_string()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
tall(*areal, 1),
|
||||
u.clone(),
|
||||
orientering.clone(),
|
||||
mot.into(),
|
||||
])
|
||||
));
|
||||
}
|
||||
@@ -496,15 +547,28 @@ fn kilde(r: &Resultat, scenarier: &[(String, Resultat)]) -> String {
|
||||
if l.forutsetninger.is_empty() {
|
||||
s.push_str("Alle inndata er oppgitt i bygningsbeskrivelsen.\n\n");
|
||||
} else {
|
||||
// Faste bredder, ellers spiser de to første kolonnene plassen kilden trenger.
|
||||
s.push_str("#tabell(\n kolonner: (1.15fr, 0.85fr, 1.6fr),\n justering: (left, left, left),\n overskrift: (\"Felt\", \"Verdi\", \"Kilde\"),\n rader: (\n");
|
||||
for f in &l.forutsetninger {
|
||||
s.push_str("#tabell(\n kolonner: (1.25fr, 1fr, 1.15fr, auto, auto, auto, auto),\n justering: (left, left, left, center, center, center, left),\n overskrift: (\"Felt\", \"Verdi\", \"Gjelder\", \"Normert\", \"Byggeår\", \"Antatt\", \"Standard\"),\n rader: (\n");
|
||||
for f in grupper_forutsetninger(&l.forutsetninger) {
|
||||
s.push_str(&format!(
|
||||
" {}\n",
|
||||
rad(&[e(&f.felt), e(&f.verdi), e(&f.kilde)])
|
||||
rad(&[
|
||||
e(&f.felt),
|
||||
e(&f.verdi),
|
||||
e(&f.gjelder),
|
||||
kryss(f.normert),
|
||||
kryss(f.byggeaar),
|
||||
kryss(f.antatt),
|
||||
e(&f.standard),
|
||||
])
|
||||
));
|
||||
}
|
||||
s.push_str(" ),\n)\n\n");
|
||||
s.push_str(
|
||||
"#merknad[*Normert* er standardverdier for bygningskategorien, som skal brukes i \
|
||||
energimerket. *Byggeår* er typiske verdier for byggeperioden, brukt der modellen ikke \
|
||||
oppgir noe. *Antatt* er satt av beregningen og bør kontrolleres. *Standard* viser \
|
||||
hvilken standard verdien er regnet etter.]\n\n",
|
||||
);
|
||||
}
|
||||
for m in &r.merknader {
|
||||
s.push_str(&format!("#merknad[{}]\n\n", e(m)));
|
||||
@@ -563,6 +627,177 @@ fn kilde(r: &Resultat, scenarier: &[(String, Resultat)]) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
/// Kilden uten tall, slik at «… av konstruksjonens 3,39 W/(m²K) og 1,29 m dybde» og den
|
||||
/// samme setningen for en annen vegg regnes som samme slags kilde.
|
||||
fn kildenokkel(kilde: &str) -> String {
|
||||
let mut ut = String::with_capacity(kilde.len());
|
||||
let mut i_tall = false;
|
||||
for c in kilde.chars() {
|
||||
if c.is_ascii_digit() {
|
||||
if !i_tall {
|
||||
ut.push('#');
|
||||
}
|
||||
i_tall = true;
|
||||
} else {
|
||||
i_tall = false;
|
||||
ut.push(c);
|
||||
}
|
||||
}
|
||||
ut
|
||||
}
|
||||
|
||||
/// Tallet fremst i en verdi som «1.353 W/(m²K)», når verdien begynner med et tall.
|
||||
fn tallverdi(verdi: &str) -> Option<f64> {
|
||||
let tall: String = verdi
|
||||
.chars()
|
||||
.take_while(|c| c.is_ascii_digit() || *c == '.' || *c == ',' || *c == '-')
|
||||
.collect();
|
||||
tall.replace(',', ".").parse().ok()
|
||||
}
|
||||
|
||||
/// Slår to verdier med samme enhet sammen til et intervall, «1,21–1,39 W/(m²K)».
|
||||
fn slaa_sammen_intervall(a: &str, b: &str) -> String {
|
||||
let enhet = a.trim_start_matches(|c: char| {
|
||||
c.is_ascii_digit() || c == '.' || c == ',' || c == '-' || c == '–'
|
||||
});
|
||||
let mut tall: Vec<f64> = Vec::new();
|
||||
for del in a.split('–').chain(b.split('–')) {
|
||||
if let Some(t) = tallverdi(del.trim()) {
|
||||
tall.push(t);
|
||||
}
|
||||
}
|
||||
let minste = tall.iter().copied().fold(f64::INFINITY, f64::min);
|
||||
let storste = tall.iter().copied().fold(f64::NEG_INFINITY, f64::max);
|
||||
if (storste - minste).abs() < 5e-3 {
|
||||
format!("{minste:.2}{enhet}")
|
||||
} else {
|
||||
format!("{minste:.2}–{storste:.2}{enhet}")
|
||||
}
|
||||
}
|
||||
|
||||
/// Norsk desimaltegn i tall, uten å røre tekst som «ISO 52016-1».
|
||||
fn komma(verdi: &str) -> String {
|
||||
let mut ut = String::with_capacity(verdi.len());
|
||||
let tegn: Vec<char> = verdi.chars().collect();
|
||||
for (i, c) in tegn.iter().enumerate() {
|
||||
let mellom_tall = i > 0
|
||||
&& i + 1 < tegn.len()
|
||||
&& tegn[i - 1].is_ascii_digit()
|
||||
&& tegn[i + 1].is_ascii_digit();
|
||||
ut.push(if *c == '.' && mellom_tall { ',' } else { *c });
|
||||
}
|
||||
ut
|
||||
}
|
||||
|
||||
fn kryss(på: bool) -> String {
|
||||
if på {
|
||||
"✓".into()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// En rad i forutsetningstabellen: like forutsetninger for mange bygningsdeler slås sammen.
|
||||
struct Forutsetningsrad {
|
||||
felt: String,
|
||||
verdi: String,
|
||||
gjelder: String,
|
||||
normert: bool,
|
||||
byggeaar: bool,
|
||||
antatt: bool,
|
||||
standard: String,
|
||||
}
|
||||
|
||||
/// Slår sammen forutsetninger som gjelder flere bygningsdeler, og deler kilden i kolonner.
|
||||
///
|
||||
/// «flate.Yttervegg nord.u_verdi» og «flate.Yttervegg sør.u_verdi» med samme verdi og kilde
|
||||
/// blir til én rad «flate.u_verdi» som gjelder 2 flater.
|
||||
fn grupper_forutsetninger(forutsetninger: &[Forutsetning]) -> Vec<Forutsetningsrad> {
|
||||
let flertall = |prefiks: &str, antall: usize| match prefiks {
|
||||
"flate" => format!("{antall} flater"),
|
||||
"vindu" => format!("{antall} vinduer"),
|
||||
"sone" => format!("{antall} soner"),
|
||||
annet => format!("{antall} × {annet}"),
|
||||
};
|
||||
let mut rader: Vec<(String, String, String, Vec<String>, String)> = Vec::new();
|
||||
for f in forutsetninger {
|
||||
// «prefiks.navn.felt» deles i mønsteret «prefiks.felt» og navnet det gjelder.
|
||||
let (prefiks, resten) = f.felt.split_once('.').unwrap_or(("", f.felt.as_str()));
|
||||
let (navn, felt) = match resten.rsplit_once('.') {
|
||||
Some((navn, felt)) => (navn.to_string(), felt.to_string()),
|
||||
None => (String::new(), resten.to_string()),
|
||||
};
|
||||
let monster = if prefiks.is_empty() {
|
||||
felt.clone()
|
||||
} else {
|
||||
format!("{prefiks}.{felt}")
|
||||
};
|
||||
match rader
|
||||
.iter_mut()
|
||||
.find(|(m, v, k, _, _)| *m == monster && *v == f.verdi && *k == f.kilde)
|
||||
{
|
||||
Some((_, _, _, navn_liste, _)) => navn_liste.push(navn),
|
||||
None => rader.push((
|
||||
monster,
|
||||
f.verdi.clone(),
|
||||
f.kilde.clone(),
|
||||
vec![navn],
|
||||
prefiks.to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
// Verdier som bare skiller seg litt, som en U-verdi mot grunnen regnet for hver vegg,
|
||||
// samles til ett intervall i stedet for én rad per vegg.
|
||||
let mut samlet: Vec<(String, String, String, Vec<String>, String)> = Vec::new();
|
||||
for (felt, verdi, kilde, navn, prefiks) in rader {
|
||||
// Kilden bærer ofte tallene for den enkelte veggen; de maskeres, slik at samme
|
||||
// slags kilde kjennes igjen.
|
||||
let like = samlet
|
||||
.iter_mut()
|
||||
.find(|(f, _, k, _, _)| *f == felt && kildenokkel(k) == kildenokkel(&kilde));
|
||||
match like {
|
||||
Some((_, v, _, n, _)) if tallverdi(v).is_some() && tallverdi(&verdi).is_some() => {
|
||||
*v = slaa_sammen_intervall(v, &verdi);
|
||||
n.extend(navn);
|
||||
}
|
||||
_ => samlet.push((felt, verdi, kilde, navn, prefiks)),
|
||||
}
|
||||
}
|
||||
samlet
|
||||
.into_iter()
|
||||
.map(|(felt, verdi, kilde, navn, prefiks)| {
|
||||
let gjelder = match navn.len() {
|
||||
0 => String::new(),
|
||||
1 if navn[0].is_empty() => String::new(),
|
||||
1 => navn[0].clone(),
|
||||
n => flertall(&prefiks, n),
|
||||
};
|
||||
let lav = kilde.to_lowercase();
|
||||
let standard = [
|
||||
"NS-EN ISO 13370",
|
||||
"NS-EN ISO 13789",
|
||||
"ISO 52016-1",
|
||||
"NS 3031",
|
||||
]
|
||||
.into_iter()
|
||||
.find(|s| kilde.contains(s))
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let normert = lav.contains("normert");
|
||||
let byggeaar = lav.contains("typisk for");
|
||||
Forutsetningsrad {
|
||||
felt,
|
||||
verdi: komma(&verdi),
|
||||
gjelder,
|
||||
normert,
|
||||
byggeaar,
|
||||
antatt: !normert && !byggeaar && standard.is_empty() || lav.contains("antatt"),
|
||||
standard,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Tekst i en Typst-strengliteral: bare omvendt skråstrek og anførselstegn skal ut.
|
||||
fn streng(s: &str) -> String {
|
||||
s.replace('\\', "\\\\").replace('"', "\\\"")
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
//! Kontroll mot de publiserte referansebyggene for energirammene i TEK17.
|
||||
//!
|
||||
//! Multiconsult for DiBK, «Grunnlag og forutsetninger for beregnede energirammer»
|
||||
//! (128298-RIEn-NOT-001, 13. november 2015), gir geometri (tabell 1), komponentverdier
|
||||
//! (tabell 2) og beregnet netto energibehov (tabell 3) for tretten bygningskategorier,
|
||||
//! beregnet med Simien etter NS 3031:2014. Modellene er gjengitt i `eksempler/referansebygg/`.
|
||||
//!
|
||||
//! Dette er den eneste kontrollen der hele beregningen kan sammenlignes med et publisert
|
||||
//! resultat for flere bygningskategorier. Testen låser dagens avvik, slik at det blir synlig
|
||||
//! både når motoren nærmer seg de publiserte tallene og når noe gjør avviket større.
|
||||
//!
|
||||
//! Kjør `cargo test --release referansebygg_2015 -- --nocapture` for hele tabellen.
|
||||
|
||||
use enok::data::Datakilde;
|
||||
use enok::{inndata, kjor};
|
||||
use std::path::Path;
|
||||
|
||||
/// Fil, publisert netto energibehov, og enoks verdi slik den er i dag, kWh/(m²·år).
|
||||
const BYGG: [(&str, f64, f64); 7] = [
|
||||
("smaahus", 107.5, 85.3),
|
||||
("boligblokk", 91.8, 77.6),
|
||||
("barnehage", 132.0, 80.1),
|
||||
("kontorbygg", 115.3, 93.5),
|
||||
("skolebygg", 106.8, 72.8),
|
||||
("idrettsbygg", 144.3, 106.7),
|
||||
("kulturbygg", 128.0, 73.2),
|
||||
];
|
||||
|
||||
fn netto(navn: &str) -> f64 {
|
||||
let sti = format!("eksempler/referansebygg/{navn}.toml");
|
||||
let b = inndata::les_bygning(Path::new(&sti)).unwrap();
|
||||
let r = kjor::kjor(&b, &Datakilde::default()).unwrap();
|
||||
r.budsjett.netto_spesifikk(r.lost.bra)
|
||||
}
|
||||
|
||||
/// Verdiene skal ikke endre seg uten at noen har ment det.
|
||||
#[test]
|
||||
fn referansebyggene_gir_de_samme_tallene() {
|
||||
println!(
|
||||
"\n {:14}{:>10}{:>12}{:>9}",
|
||||
"bygg", "enok", "publisert", "avvik"
|
||||
);
|
||||
let mut avvik = Vec::new();
|
||||
for (navn, publisert, laast) in BYGG {
|
||||
let n = netto(navn);
|
||||
println!(
|
||||
" {navn:14}{n:10.1}{publisert:12.1}{:8.1} %",
|
||||
100.0 * (n - publisert) / publisert
|
||||
);
|
||||
avvik.push(100.0 * (n - publisert) / publisert);
|
||||
assert!(
|
||||
(n - laast).abs() < 1.0,
|
||||
"{navn}: {n:.1} kWh/(m²·år), låst verdi {laast:.1}"
|
||||
);
|
||||
}
|
||||
let snitt = avvik.iter().sum::<f64>() / avvik.len() as f64;
|
||||
println!(" {:14}{:>10}{:>12}{snitt:8.1} %\n", "snitt", "", "");
|
||||
}
|
||||
|
||||
/// Varmetapstallet for småhuset skal treffe den publiserte verdien for TEK17-nivå.
|
||||
///
|
||||
/// Geometri og U-verdier er dermed riktig gjengitt, og avviket i netto energibehov ligger i
|
||||
/// den dynamiske beregningen, ikke i inndataene.
|
||||
#[test]
|
||||
fn varmetapstallet_for_smaahuset_stemmer() {
|
||||
let b = inndata::les_bygning(Path::new("eksempler/referansebygg/smaahus.toml")).unwrap();
|
||||
let r = kjor::kjor(&b, &Datakilde::default()).unwrap();
|
||||
let publisert = 0.69;
|
||||
assert!(
|
||||
(r.varmetap.totalt - publisert).abs() < 0.03,
|
||||
"varmetapstall {:.2}, publisert {publisert:.2}",
|
||||
r.varmetap.totalt
|
||||
);
|
||||
}
|
||||
|
||||
/// De faste postene følger av normerte verdier og skal stemme uavhengig av motoren.
|
||||
#[test]
|
||||
fn faste_poster_i_referansebyggene() {
|
||||
let post = |r: &kjor::Resultat, nr: &str| {
|
||||
r.budsjett
|
||||
.poster
|
||||
.iter()
|
||||
.find(|p| p.nr == nr)
|
||||
.map(|p| p.netto / r.lost.bra)
|
||||
.unwrap_or(0.0)
|
||||
};
|
||||
let b = inndata::les_bygning(Path::new("eksempler/referansebygg/smaahus.toml")).unwrap();
|
||||
let r = kjor::kjor(&b, &Datakilde::default()).unwrap();
|
||||
assert!((post(&r, "2") - 25.1).abs() < 0.2, "varmtvann");
|
||||
assert!((post(&r, "5") - 11.4).abs() < 0.2, "belysning");
|
||||
assert!((post(&r, "6") - 17.5).abs() < 0.2, "teknisk utstyr");
|
||||
}
|
||||
Reference in New Issue
Block a user