Beregn rett fra en BIM-modell, uten bygning.toml
«enok beregn modell.ifc» leser modellen, fletter inn en eventuell tilleggsfil og skriver bygningsbeskrivelsen den kom fram til, i utdatakatalogen, slik at beregningen kan etterprøves. Importen sier nå hvilke bygningsdeler som mangler U-verdi, ikke bare hvor mange. Vinduer og dører uten U-verdi i modellen får en typisk verdi for byggeåret, slik flatene alltid har fått, i stedet for faste 2,4 og 2,0 W/(m²K). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015GeH3y8fwTaTudaD88P57o
This commit is contained in:
+99
-60
@@ -56,6 +56,9 @@ struct Beregn {
|
||||
/// Beregner bare hovedalternativet, uten scenarioene i filen.
|
||||
#[arg(long)]
|
||||
uten_scenarier: bool,
|
||||
/// Tilleggsfil som flettes inn: det modellen ikke beskriver, og rettelser.
|
||||
#[arg(short, long, value_name = "FIL")]
|
||||
tillegg: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
@@ -102,14 +105,77 @@ fn main() -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Leser en bygningsbeskrivelse eller en BIM-modell, med en valgfri tilleggsfil flettet inn.
|
||||
///
|
||||
/// En `.toml`-fil er bygningsbeskrivelsen selv; andre filtyper importeres. Returnerer også
|
||||
/// merknadene fra importen, og om filen var en modell.
|
||||
fn les_modell(
|
||||
fil: &Path,
|
||||
tillegg: Option<&Path>,
|
||||
) -> Result<(enok::modell::Bygning, Vec<String>, bool)> {
|
||||
let endelse = fil
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
let tillegg = tillegg.map(inndata::les_tillegg).transpose()?;
|
||||
let valg = tillegg
|
||||
.as_ref()
|
||||
.map(|t| t.import.clone())
|
||||
.unwrap_or_default();
|
||||
let (bygning, mut merknader, modell) = match endelse.as_str() {
|
||||
"toml" => (inndata::les_bygning(fil)?, Vec::new(), false),
|
||||
"gbxml" | "xml" => {
|
||||
let r = inndata::gbxml::les(fil)?;
|
||||
(r.bygning, r.merknader, true)
|
||||
}
|
||||
"ifc" => {
|
||||
let r = inndata::ifc::les_med(fil, &valg)?;
|
||||
(r.bygning, r.merknader, true)
|
||||
}
|
||||
"csv" => {
|
||||
let r = inndata::regneark::les(fil)?;
|
||||
(r.bygning, r.merknader, true)
|
||||
}
|
||||
annen => bail!(
|
||||
"ukjent filtype «{annen}». enok leser bygning.toml og importerer .gbxml, .ifc og .csv (regneark fra Vectorworks)"
|
||||
),
|
||||
};
|
||||
if endelse != "ifc" && !valg.uoppvarmede_rom.is_empty() {
|
||||
merknader.push("uoppvarmede_rom i tilleggsfilen brukes bare ved import fra IFC.".into());
|
||||
}
|
||||
let Some(t) = tillegg else {
|
||||
return Ok((bygning, merknader, modell));
|
||||
};
|
||||
let (bygning, bom) = inndata::flett_tillegg(&bygning, &t.endringer)?;
|
||||
for m in bom {
|
||||
merknader.push(format!(
|
||||
"Navnemønsteret «{m}» i tilleggsfilen traff ingen oppføring i modellen."
|
||||
));
|
||||
}
|
||||
Ok((bygning, merknader, modell))
|
||||
}
|
||||
|
||||
fn beregn(a: Beregn, kilde: &Datakilde) -> Result<()> {
|
||||
let bygning = inndata::les_bygning(&a.fil)?;
|
||||
let (bygning, merknader, modell) = les_modell(&a.fil, a.tillegg.as_deref())?;
|
||||
for m in &merknader {
|
||||
eprintln!(" · {m}");
|
||||
}
|
||||
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)?;
|
||||
if modell {
|
||||
// Bygningsbeskrivelsen modellen ga, skrives med, slik at beregningen kan etterprøves.
|
||||
let sti = ut.join("bygning.toml");
|
||||
std::fs::write(
|
||||
&sti,
|
||||
skriv_bygning(&bygning, &merknader, &a.fil, a.tillegg.as_deref())?,
|
||||
)?;
|
||||
eprintln!("Bygningsbeskrivelsen fra modellen: {}", sti.display());
|
||||
}
|
||||
|
||||
eprintln!("Henter klimadata …");
|
||||
let (vaerdata, klimanavn, er_referanse) = kjor::hent_klima(&bygning)?;
|
||||
@@ -215,66 +281,9 @@ fn skriv_sammendrag(r: &kjor::Resultat, scenarier: &[(String, kjor::Resultat)])
|
||||
}
|
||||
|
||||
fn import(a: Import) -> Result<()> {
|
||||
let endelse = a
|
||||
.fil
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
let tillegg = a.tillegg.as_deref().map(inndata::les_tillegg).transpose()?;
|
||||
let valg = tillegg
|
||||
.as_ref()
|
||||
.map(|t| t.import.clone())
|
||||
.unwrap_or_default();
|
||||
let (bygning, mut merknader) = match endelse.as_str() {
|
||||
"gbxml" | "xml" => {
|
||||
let r = inndata::gbxml::les(&a.fil)?;
|
||||
(r.bygning, r.merknader)
|
||||
}
|
||||
"ifc" => {
|
||||
let r = inndata::ifc::les_med(&a.fil, &valg)?;
|
||||
(r.bygning, r.merknader)
|
||||
}
|
||||
"csv" => {
|
||||
let r = inndata::regneark::les(&a.fil)?;
|
||||
(r.bygning, r.merknader)
|
||||
}
|
||||
annen => bail!(
|
||||
"ukjent filtype «{annen}». enok importerer .gbxml, .ifc og .csv (regneark fra Vectorworks)"
|
||||
),
|
||||
};
|
||||
if endelse != "ifc" && !valg.uoppvarmede_rom.is_empty() {
|
||||
merknader.push("uoppvarmede_rom i tilleggsfilen brukes bare ved import fra IFC.".into());
|
||||
}
|
||||
let (bygning, mut innhold) = match (&tillegg, &a.tillegg) {
|
||||
(Some(t), Some(sti)) => {
|
||||
let (b, bom) = inndata::flett_tillegg(&bygning, &t.endringer)?;
|
||||
for m in bom {
|
||||
merknader.push(format!(
|
||||
"Navnemønsteret «{m}» i tilleggsfilen traff ingen oppføring i modellen."
|
||||
));
|
||||
}
|
||||
let hode = format!(
|
||||
"# Generert av enok fra «{}» og tilleggsfilen «{}».\n\
|
||||
# Rett i modellen eller i tilleggsfilen; denne filen skrives på nytt ved hver import.\n\
|
||||
# Merknader fra importen:\n",
|
||||
a.fil.display(),
|
||||
sti.display()
|
||||
);
|
||||
(b, hode)
|
||||
}
|
||||
_ => (
|
||||
bygning,
|
||||
String::from("# Importert med enok. Fyll ut feltene som er merket nedenfor.\n"),
|
||||
),
|
||||
};
|
||||
let (bygning, merknader, _) = les_modell(&a.fil, a.tillegg.as_deref())?;
|
||||
let ut = a.ut.unwrap_or_else(|| a.fil.with_extension("toml"));
|
||||
let tekst = toml::to_string_pretty(&bygning).context("serialiserer bygningen til TOML")?;
|
||||
for m in &merknader {
|
||||
innhold.push_str(&format!("# {m}\n"));
|
||||
}
|
||||
innhold.push('\n');
|
||||
innhold.push_str(&tekst);
|
||||
let innhold = skriv_bygning(&bygning, &merknader, &a.fil, a.tillegg.as_deref())?;
|
||||
std::fs::write(&ut, innhold)?;
|
||||
println!("Skrev {}", ut.display());
|
||||
for m in &merknader {
|
||||
@@ -283,6 +292,36 @@ fn import(a: Import) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Bygningsbeskrivelsen som TOML, med et hode som sier hvor den kommer fra.
|
||||
fn skriv_bygning(
|
||||
bygning: &enok::modell::Bygning,
|
||||
merknader: &[String],
|
||||
fil: &Path,
|
||||
tillegg: Option<&Path>,
|
||||
) -> Result<String> {
|
||||
let tekst = toml::to_string_pretty(bygning).context("serialiserer bygningen til TOML")?;
|
||||
let mut innhold = match tillegg {
|
||||
Some(t) => format!(
|
||||
"# Generert av enok fra «{}» og tilleggsfilen «{}».\n\
|
||||
# Rett i modellen eller i tilleggsfilen; denne filen skrives på nytt ved hver import.\n\
|
||||
# Merknader fra importen:\n",
|
||||
fil.display(),
|
||||
t.display()
|
||||
),
|
||||
None => format!(
|
||||
"# Generert av enok fra «{}».\n\
|
||||
# Merknader fra importen:\n",
|
||||
fil.display()
|
||||
),
|
||||
};
|
||||
for m in merknader {
|
||||
innhold.push_str(&format!("# {m}\n"));
|
||||
}
|
||||
innhold.push('\n');
|
||||
innhold.push_str(&tekst);
|
||||
Ok(innhold)
|
||||
}
|
||||
|
||||
fn klima(a: Klima) -> Result<()> {
|
||||
if a.liste {
|
||||
for k in enok::klima::tmy::kommuner()? {
|
||||
|
||||
Reference in New Issue
Block a user