ein und Ausgabeverzeichnisse hinzu gemacht.
This commit is contained in:
+170
-9
@@ -5,24 +5,30 @@ Eingangsgroesse: zu kuehlende Milchmenge pro Tag (Liter).
|
||||
Ausgabe: erforderliche Groesse von Waermepumpe, PV-Anlage, Latentspeicher
|
||||
(Natriumacetat) und Batterie.
|
||||
|
||||
Alle Annahmen stehen in einer JSON-Datei (Standard: config.json), damit sich
|
||||
Varianten leicht nachrechnen lassen.
|
||||
Alle Annahmen stehen in einer JSON-Datei (Standard: cfg/config.json), damit sich
|
||||
Varianten leicht nachrechnen lassen. Eine Datei im in/-Ordner (oder --file) kann
|
||||
einzelne Werte gezielt ueberschreiben.
|
||||
|
||||
Aufruf:
|
||||
python auslegung.py 1000
|
||||
python auslegung.py 1500 --config variante_winterautark.json
|
||||
python auslegung.py 1000 --json ergebnis.json
|
||||
python auslegung.py 1500 --file in/variante_winter.json
|
||||
python auslegung.py 1000 -v
|
||||
python auslegung.py 1000 --json ergebnis.json -v
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
KJ_PER_KWH = 3600.0
|
||||
TAGE_PRO_JAHR = 365.0
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
|
||||
# Tage je Monat (Schluessel muessen zu pv.monatsertrag_kWh_pro_kWp passen)
|
||||
MONATSTAGE = {
|
||||
"Jan": 31, "Feb": 28, "Mae": 31, "Apr": 30, "Mai": 31, "Jun": 30,
|
||||
@@ -35,6 +41,17 @@ def lade_config(pfad: Path) -> dict:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def deep_merge(base: dict, override: dict) -> dict:
|
||||
"""Rekursives Zusammenfuehren zweier Dicts; override gewinnt bei Konflikten."""
|
||||
result = base.copy()
|
||||
for key, value in override.items():
|
||||
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
||||
result[key] = deep_merge(result[key], value)
|
||||
else:
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
def milch_kuehllast(milch_l_tag: float, cfg: dict) -> float:
|
||||
"""Taeglich der Milch zu entziehende Waerme in kWh/Tag."""
|
||||
m = cfg["milch"]
|
||||
@@ -363,31 +380,175 @@ def report(e: dict, cfg: dict) -> str:
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def report_md(e: dict, cfg: dict, quelldateien: list[str] | None = None) -> str:
|
||||
q = e["milch_kuehllast_kWh_tag"]
|
||||
wp = e["waermepumpe"]
|
||||
sp = e["speicher"]
|
||||
pv = e["pv"]
|
||||
netz = e["netzbezug"]
|
||||
bat = e["batterie"]
|
||||
hh = e["haushalte"]
|
||||
z = cfg["abholung"]["zyklus_tage"]
|
||||
milch = e["eingang"]["milch_l_tag"]
|
||||
ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
L = []
|
||||
L.append("# Auslegung Waermerueckgewinnung Milchkuehlung")
|
||||
L.append("")
|
||||
L.append(f"**Erstellt:** {ts}")
|
||||
L.append(f"**Eingang:** {milch:.0f} L Milch/Tag"
|
||||
f" ({cfg['milch']['temp_ein_C']:.0f} -> {cfg['milch']['temp_aus_C']:.0f} C)")
|
||||
if quelldateien:
|
||||
L.append(f"**Konfiguration:** {', '.join(quelldateien)}")
|
||||
L.append("")
|
||||
L.append(f"**Kuehllast der Milch:** {q:.1f} kWh/Tag")
|
||||
L.append("")
|
||||
|
||||
L.append("## Waermepumpe")
|
||||
L.append(f"COP {cfg['waermepumpe']['cop']:.2f}, Laufzeit {cfg['waermepumpe']['laufzeit_h_pro_tag']:.0f} h/Tag")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert |")
|
||||
L.append("|-------------|------|")
|
||||
L.append(f"| Kaelteleistung (Verdampfer) | {wp['kaelteleistung_kW']:.1f} kW |")
|
||||
L.append(f"| Heizleistung (Kondensator) | {wp['heizleistung_kW']:.1f} kW |")
|
||||
L.append(f"| el. Leistung | {wp['elektrische_leistung_kW']:.1f} kW |")
|
||||
L.append(f"| Strombedarf | {wp['strom_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| Waerme in den Speicher | {wp['speicher_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append("")
|
||||
|
||||
L.append("## PV-Anlage (Jahresbilanz)")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert |")
|
||||
L.append("|-------------|------|")
|
||||
L.append(f"| Jahresstrombedarf WP | {pv['jahresbedarf_kWh']:.0f} kWh/a |")
|
||||
L.append(f"| Leistung | {pv['kWp']:.1f} kWp |")
|
||||
L.append(f"| Dachflaeche | {pv['flaeche_m2']:.0f} m2 |")
|
||||
L.append("")
|
||||
|
||||
L.append("## Netzbezug (Wintermonate, Monatsbilanz)")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert |")
|
||||
L.append("|-------------|------|")
|
||||
L.append(f"| Netzbezug | {netz['netzbezug_kWh']:.0f} kWh/a = {netz['netzbezug_anteil']*100:.0f} % |")
|
||||
L.append(f"| Sommerueberschuss | {netz['ueberschuss_kWh']:.0f} kWh/a |")
|
||||
L.append("")
|
||||
L.append("### Monatsbilanz")
|
||||
L.append("")
|
||||
L.append("| Monat | Bedarf kWh | PV kWh | Zukauf kWh |")
|
||||
L.append("|-------|-----------|--------|-----------|")
|
||||
for monat, werte in netz["monate"].items():
|
||||
L.append(f"| {monat} | {werte['bedarf_kWh']:.0f} | {werte['pv_kWh']:.0f} | {werte['zukauf_kWh']:.0f} |")
|
||||
L.append("")
|
||||
|
||||
L.append("## Batterie (Tag-/Nacht-Ueberbbrueckung)")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert |")
|
||||
L.append("|-------------|------|")
|
||||
L.append(f"| nutzbare Kapazitaet | {bat['nutzbar_kWh']:.1f} kWh |")
|
||||
L.append(f"| Nennkapazitaet | {bat['nominal_kWh']:.1f} kWh |")
|
||||
L.append("")
|
||||
|
||||
L.append(f"## Versorgte Haushalte ({cfg['haushalt']['erwachsene']} Erw. + {cfg['haushalt']['kinder']} Kinder)")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert |")
|
||||
L.append("|-------------|------|")
|
||||
L.append(f"| Warmwasser je Haushalt | {hh['ww_pro_haushalt_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| Warmwasser ganzjaehrig | {hh['haushalte_warmwasser']:.1f} Haushalte |")
|
||||
L.append(f"| max. Heizbedarf Winter | {hh['heizbedarf_winter_kWh_tag']:.1f} kWh/Tag/HH |")
|
||||
L.append(f"| Winterbedarf (Heiz+WW) | {hh['winter_pro_haushalt_kWh_tag']:.1f} kWh/Tag/HH |")
|
||||
L.append(f"| im Winter | {hh['haushalte_winter']:.1f} Haushalte |")
|
||||
L.append("")
|
||||
|
||||
passt = "ja" if sp["passt_in_einen_lkw"] else "NEIN - mehrere Behaelter noetig"
|
||||
L.append(f"## Latentspeicher ({cfg['pcm']['name']}, Modus: {cfg['pcm']['modus']})")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert |")
|
||||
L.append("|-------------|------|")
|
||||
L.append(f"| Energiedichte | {sp['energiedichte_kWh_pro_kg']*1000:.1f} Wh/kg |")
|
||||
L.append(f"| Energie je Zyklus ({z} Tage) | {sp['energie_pro_zyklus_kWh']:.0f} kWh |")
|
||||
L.append(f"| Masse | {sp['masse_kg']:.0f} kg |")
|
||||
L.append(f"| Volumen | {sp['volumen_m3']:.1f} m3 |")
|
||||
L.append(f"| LKW-Auslastung Gewicht | {sp['auslastung_gewicht']*100:.0f} % |")
|
||||
L.append(f"| LKW-Auslastung Volumen | {sp['auslastung_volumen']*100:.0f} % |")
|
||||
L.append(f"| bindende Grenze | {sp['bindende_grenze']} |")
|
||||
L.append(f"| passt in einen LKW | {passt} |")
|
||||
L.append(f"| voller LKW nach | {sp['voller_lkw_tausch_tage']:.1f} Tagen |")
|
||||
L.append(f"| Energieinhalt voller LKW | {sp['voller_lkw_energie_kWh']:.0f} kWh |")
|
||||
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("milch_l_tag", type=float, help="Milchmenge pro Tag in Litern")
|
||||
ap.add_argument("--config", type=Path, default=Path(__file__).with_name("config.json"),
|
||||
help="Pfad zur JSON-Konfiguration (Standard: config.json)")
|
||||
ap.add_argument("--config", type=Path,
|
||||
default=PROJECT_ROOT / "cfg" / "config.json",
|
||||
help="Basis-Konfiguration (Standard: cfg/config.json)")
|
||||
ap.add_argument("--file", type=Path, default=None,
|
||||
help="Override-Datei, die cfg/config.json selektiv ueberschreibt "
|
||||
"(Standard: alle *.json im in/-Ordner)")
|
||||
ap.add_argument("--out", type=Path, default=None,
|
||||
help="Ausgabeverzeichnis fuer die .md-Datei "
|
||||
"(Standard: $MILCH_OUT oder out/)")
|
||||
ap.add_argument("--json", type=Path, default=None,
|
||||
help="Ergebnis zusaetzlich als JSON in diese Datei schreiben")
|
||||
ap.add_argument("--suggest-size", action="store_true",
|
||||
help="Kompakte Empfehlung (gerundete Groessen + Abholzyklus) statt Detailreport")
|
||||
ap.add_argument("-v", "--verbose", action="store_true",
|
||||
help="Ausgaben an der Konsole anzeigen")
|
||||
args = ap.parse_args()
|
||||
|
||||
# Ausgabeverzeichnis bestimmen
|
||||
out_ordner = (
|
||||
args.out
|
||||
or Path(os.environ.get("MILCH_OUT", ""))
|
||||
or PROJECT_ROOT / "out"
|
||||
)
|
||||
if not out_ordner or str(out_ordner) == ".":
|
||||
out_ordner = PROJECT_ROOT / "out"
|
||||
out_ordner = Path(out_ordner)
|
||||
out_ordner.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# In-Ordner bestimmen
|
||||
in_ordner = Path(os.environ.get("MILCH_IN", PROJECT_ROOT / "in"))
|
||||
|
||||
# Konfiguration laden: Basis + Override
|
||||
cfg = lade_config(args.config)
|
||||
quelldateien = [args.config.name]
|
||||
|
||||
if args.file:
|
||||
cfg = deep_merge(cfg, lade_config(args.file))
|
||||
quelldateien.append(args.file.name)
|
||||
elif in_ordner.is_dir():
|
||||
for f in sorted(in_ordner.glob("*.json")):
|
||||
cfg = deep_merge(cfg, lade_config(f))
|
||||
quelldateien.append(f.name)
|
||||
|
||||
ergebnis = rechne(args.milch_l_tag, cfg)
|
||||
|
||||
if args.suggest_size:
|
||||
print(vorschlag_report(ergebnis, cfg))
|
||||
text = vorschlag_report(ergebnis, cfg)
|
||||
ergebnis["vorschlag"] = vorschlag(ergebnis, cfg)
|
||||
else:
|
||||
print(report(ergebnis, cfg))
|
||||
text = report(ergebnis, cfg)
|
||||
|
||||
if args.verbose:
|
||||
print(text)
|
||||
|
||||
# Markdown-Ergebnis in out/ schreiben
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
md_name = f"auslegung_{args.milch_l_tag:.0f}L_{ts}.md"
|
||||
md_path = out_ordner / md_name
|
||||
md_path.write_text(report_md(ergebnis, cfg, quelldateien), encoding="utf-8")
|
||||
if args.verbose:
|
||||
print(f"\nMarkdown geschrieben: {md_path}")
|
||||
|
||||
if args.json:
|
||||
with args.json.open("w", encoding="utf-8") as f:
|
||||
json.dump(ergebnis, f, ensure_ascii=False, indent=2)
|
||||
print(f"\nErgebnis geschrieben: {args.json}")
|
||||
if args.verbose:
|
||||
print(f"JSON geschrieben: {args.json}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user