Files
Milchwaerme/lib/report_md.py
T
2026-06-14 22:45:48 +02:00

278 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Markdown-Reporterzeugung fuer die Waermerueckgewinnung-Auslegung.
Einziger oeffentlicher Einstiegspunkt: report_md(e, cfg, quelldateien).
"""
from __future__ import annotations
import datetime
def _h2(title: str) -> list[str]:
return ["", f"## {title}", ""]
def _h3(title: str) -> list[str]:
return [f"### {title}", ""]
def _tbl_header(cols: list[str]) -> list[str]:
return [" | ".join(cols),
" | ".join(["---"] * len(cols))]
def report_md(e: dict, cfg: dict, quelldateien: list[str] | None = None) -> str:
"""Vollstaendiger Markdown-Report: je Block erst Voraussetzungen, dann Ergebnisse."""
q = e["milch_kuehllast_kWh_tag"]
wp = e["waermepumpe"]
sp = e["speicher"]
pv = e["pv"]
netz = e["netzbezug"]
bat = e["batterie"]
hh = e["haushalte"]
m_cfg = cfg["milch"]
wp_cfg = cfg["waermepumpe"]
pcm_cfg = cfg["pcm"]
abh_cfg = cfg["abholung"]
pv_cfg = cfg["pv"]
bat_cfg = cfg["batterie"]
hh_cfg = cfg["haushalt"]
z = abh_cfg["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}")
if quelldateien:
L.append(f"**Konfiguration:** {', '.join(quelldateien)}")
# ── Milchkühllast ────────────────────────────────────────────────
L.extend(_h2("Milchkuehllast"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| Milchmenge | {milch:.0f} L/Tag |")
L.append(f"| Eingangstemperatur | {m_cfg['temp_ein_C']:.0f} C (Eutertemperatur) |")
L.append(f"| Ausgangstemperatur | {m_cfg['temp_aus_C']:.0f} C (Lagertemperatur) |")
L.append(f"| Dichte | {m_cfg['dichte_kg_pro_L']:.2f} kg/L |")
L.append(f"| Spez. Waerme | {m_cfg['spez_waerme_kJ_pro_kgK']:.3f} kJ/(kg·K) |")
L.append("")
L.extend(_h3("Ergebnisse"))
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
L.append(f"| Taegl. Kuehllast | **{q:.1f} kWh/Tag** |")
# ── Wärmepumpe ───────────────────────────────────────────────────
L.extend(_h2("Waermepumpe"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| COP (Coefficient of Performance) | {wp_cfg['cop']:.2f} |")
L.append(f"| Laufzeit | {wp_cfg['laufzeit_h_pro_tag']:.0f} h/Tag |")
L.append(f"| Kuehllast (aus Milchblock) | {q:.1f} kWh/Tag |")
L.append("")
L.extend(_h3("Ergebnisse"))
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
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** |")
# ── PV-Anlage ────────────────────────────────────────────────────
L.extend(_h2("PV-Anlage (Jahresbilanz)"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| Strombedarf WP (aus WP-Block) | {wp['strom_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| Spez. Jahresertrag | {pv_cfg['spez_ertrag_kWh_pro_kWp_jahr']} kWh/(kWp·a) |")
L.append(f"| Dachflaeche je kWp | {pv_cfg['flaeche_m2_pro_kWp']:.1f} m2/kWp |")
L.append("")
L.extend(_h3("Ergebnisse"))
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
L.append(f"| Jahresstrombedarf WP | {pv['jahresbedarf_kWh']:.0f} kWh/a |")
L.append(f"| PV-Leistung | **{pv['kWp']:.1f} kWp** |")
L.append(f"| Dachflaeche | **{pv['flaeche_m2']:.0f} m2** |")
# ── Netzbezug ────────────────────────────────────────────────────
L.extend(_h2("Netzbezug (Wintermonate, Monatsbilanz)"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| PV-Leistung (aus PV-Block) | {pv['kWp']:.1f} kWp |")
L.append(f"| Strombedarf WP | {wp['strom_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| Monatsprofil | Jan {pv_cfg['monatsertrag_kWh_pro_kWp']['Jan']},"
f" Feb {pv_cfg['monatsertrag_kWh_pro_kWp']['Feb']},"
f" ... Dez {pv_cfg['monatsertrag_kWh_pro_kWp']['Dez']} kWh/kWp |")
L.append("")
L.extend(_h3("Ergebnisse"))
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
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.extend(_tbl_header(["Monat", "Bedarf kWh", "PV kWh", "Zukauf kWh"]))
for monat, werte in netz["monate"].items():
L.append(f"| {monat} | {werte['bedarf_kWh']:.0f} | {werte['pv_kWh']:.0f}"
f" | {werte['zukauf_kWh']:.0f} |")
# ── Batterie ─────────────────────────────────────────────────────
L.extend(_h2("Batterie (Tag-/Nacht-Ueberbrueckung)"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| Strombedarf WP | {wp['strom_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| Direkt-PV-Stunden (WP laeuft direkt aus PV) | {bat_cfg['direkt_pv_stunden']:.0f} h/Tag |")
L.append(f"| Nutzbarer Anteil (Entladetiefe LFP) | {bat_cfg['nutzbar_anteil']*100:.0f} % |")
L.append(f"| Reservefaktor | {bat_cfg['reserve_faktor']:.1f} |")
L.append("")
L.extend(_h3("Ergebnisse"))
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
L.append(f"| Nutzbare Kapazitaet | {bat['nutzbar_kWh']:.1f} kWh |")
L.append(f"| Nennkapazitaet | **{bat['nominal_kWh']:.1f} kWh** |")
# ── Latentspeicher ───────────────────────────────────────────────
passt = "ja" if sp["passt_in_einen_lkw"] else "NEIN - mehrere Behaelter noetig"
L.extend(_h2(f"Latentspeicher ({pcm_cfg['name']})"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| PCM | {pcm_cfg['name']} |")
L.append(f"| Schmelzpunkt | {pcm_cfg['schmelzpunkt_C']:.0f} C |")
L.append(f"| Latentwaerme | {pcm_cfg['latentwaerme_kJ_pro_kg']:.0f} kJ/kg |")
L.append(f"| Dichte | {pcm_cfg['dichte_kg_pro_L']:.1f} kg/L |")
L.append(f"| Modus | {pcm_cfg['modus']} |")
if pcm_cfg["modus"] == "latent_plus_hub":
L.append(f"| Ladetemperatur | {pcm_cfg['ladetemp_C']:.0f} C |")
L.append(f"| Entladetemperatur | {pcm_cfg['entladetemp_C']:.0f} C |")
L.append(f"| cp fluessig | {pcm_cfg['cp_fluessig_kJ_pro_kgK']:.1f} kJ/(kg·K) |")
L.append(f"| cp fest | {pcm_cfg['cp_fest_kJ_pro_kgK']:.1f} kJ/(kg·K) |")
L.append(f"| Abholzyklus | {z} Tage |")
L.append(f"| LKW-Nutzlast | {abh_cfg['lkw_nutzlast_kg']:,} kg |")
L.append(f"| LKW-Volumen | {abh_cfg['lkw_volumen_m3']} m3 |")
L.append(f"| Waerme je Tag (aus WP-Block) | {wp['speicher_kWh_tag']:.1f} kWh/Tag |")
L.append("")
L.extend(_h3("Ergebnisse"))
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
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 |")
# ── Haushalte ────────────────────────────────────────────────────
L.extend(_h2(f"Versorgte Haushalte"
f" ({hh_cfg['erwachsene']} Erw. + {hh_cfg['kinder']} Kinder)"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| Erwachsene je Haushalt | {hh_cfg['erwachsene']} |")
L.append(f"| Kinder je Haushalt | {hh_cfg['kinder']} |")
L.append(f"| WW-Bedarf Erwachsener | {hh_cfg['ww_erwachsener_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| WW-Bedarf Kind | {hh_cfg['ww_kind_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| Heizbedarf Winter | {hh_cfg['heizbedarf_winter_kWh_tag']:.0f} kWh/Tag/HH |")
L.append(f"| Waerme je Tag (aus WP-Block) | {wp['speicher_kWh_tag']:.1f} kWh/Tag |")
L.append("")
L.extend(_h3("Ergebnisse"))
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
L.append(f"| WW-Bedarf je Haushalt | {hh['ww_pro_haushalt_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| Winterbedarf je Haushalt (Heiz+WW) | {hh['winter_pro_haushalt_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| Versorgte HH (Warmwasser, ganzjaehrig) | **{hh['haushalte_warmwasser']:.1f}** |")
L.append(f"| Versorgte HH (Heizung + WW, Winter) | **{hh['haushalte_winter']:.1f}** |")
# ── Wert beim Käufer ─────────────────────────────────────────────
bk = e.get("brennstoffkosten")
if bk and bk["vergleich"]:
L.extend(_h2("Wert der Waerme beim Kaeufer (Ersatzkosten fossil/Holz)"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Brennstoff", "Preis ct/kWh", "Kesselwirkungsgrad"]))
for b in cfg.get("brennstoffe", []):
L.append(f"| {b['name']} | {b['preis_ct_kWh']:.1f} | {b['wirkungsgrad']*100:.0f} % |")
L.append("")
L.append("*Nutzwaermekosten = Brennstoffpreis ÷ Kesselwirkungsgrad*")
L.append("")
L.extend(_h3("Ergebnisse"))
L.append(f"Bewertet: voller LKW **{bk['energie_lkw_kWh']:.0f} kWh**,"
f" Jahreserzeugung **{bk['energie_jahr_kWh']:.0f} kWh/a**")
L.append("")
L.extend(_tbl_header(["Brennstoff", "ct/kWh (Nutzw.)", "EUR/LKW", "EUR/Jahr"]))
for v in bk["vergleich"]:
L.append(f"| {v['name']} | {v['nutzwaerme_ct_kWh']:.1f} |"
f" {v['eur_pro_lkw']:.0f} | {v['eur_pro_jahr']:.0f} |")
# ── Gestehungskosten Anbieter ────────────────────────────────────
ak = e.get("anbieter")
if ak:
inv = cfg["anbieter"]["invest"]
ci = ak["container_invest"]
sd = ak["kosten_strom_detail"]
L.extend(_h2("Waerme-Gestehungskosten Anbieter"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| Transport je Fuhre | {cfg['anbieter']['transport_eur_pro_fuhre']:.0f} EUR |")
L.append(f"| Netzstrompreis | {cfg['anbieter']['netzstrompreis_ct_kWh']:.0f} ct/kWh |")
L.append(f"| PV-Gestehungskosten (LCOE) | {cfg['anbieter']['pv_gestehung_ct_kWh']:.0f} ct/kWh |")
L.append(f"| Wartung pauschal | {cfg['anbieter']['wartung_eur_pro_jahr']:.0f} EUR/a |")
L.append(f"| PV-Invest | {inv['pv_eur_pro_kWp']} EUR/kWp,"
f" {inv['pv_nutzungsdauer_jahre']} J Nutzung |")
L.append(f"| Batterie-Invest | {inv['batterie_eur_pro_kWh']} EUR/kWh,"
f" {inv['batterie_nutzungsdauer_jahre']} J |")
L.append(f"| WP-Invest | {inv['waermepumpe_eur']:,} EUR,"
f" {inv['waermepumpe_nutzungsdauer_jahre']} J |")
L.append(f"| Speicher-Invest | {ci['anzahl_module']} Module,"
f" {inv['speicher_nutzungsdauer_jahre']} J Nutzung |")
L.append("")
L.append("**Aufschluesselung Speicher-Invest**")
L.append("")
L.extend(_tbl_header(["Komponente", "EUR (alle Module)"]))
L.append(f"| PCM ({sp['masse_kg']:.0f} kg × {ci['anzahl_module']} Module)"
f" | {ci['pcm_eur']:,.0f} |")
L.append(f"| Behaelter (isoliert, LKW-tauglich) | {ci['behaelter_eur']:,.0f} |")
L.append(f"| Waermetauscher + Hydraulik | {ci['wt_hydraulik_eur']:,.0f} |")
L.append(f"| Elektronik / Regelung | {ci['elektronik_eur']:,.0f} |")
L.append(f"| Montage + Inbetriebnahme | {ci['montage_eur']:,.0f} |")
L.append(f"| **Gesamt** | **{ci['gesamt_eur']:,.0f}** |")
L.append(f"| entspricht | {ci['eur_pro_kwh_kapazitaet']:.0f} EUR/kWh Speicherkapazitaet |")
L.append("")
L.append("*Abschreibung: linear, kein Kapitalzins.*")
L.append("")
L.extend(_h3("Ergebnisse"))
L.extend(_tbl_header(["Kostenblock", "EUR/Jahr"]))
L.append(f"| Transport ({ak['fuhren_jahr']:.0f} Fuhren/a)"
f" | {ak['kosten_transport_eur']:.0f} |")
L.append(f"| WP-Strom ({sd['netz_kWh']:.0f} kWh Netz"
f" + {sd['pv_kWh']:.0f} kWh PV-Eigen) | {ak['kosten_strom_eur']:.0f} |")
L.append(f"| Wartung | {ak['kosten_wartung_eur']:.0f} |")
L.append(f"| Kapital PV ({pv['kWp']:.1f} kWp) | {ak['kapital_pv_eur']:.0f} |")
L.append(f"| Kapital Batterie ({bat['nominal_kWh']:.0f} kWh)"
f" | {ak['kapital_batterie_eur']:.0f} |")
L.append(f"| Kapital Waermepumpe | {ak['kapital_waermepumpe_eur']:.0f} |")
L.append(f"| Kapital Speicher ({ci['anzahl_module']} Module)"
f" | {ak['kapital_speicher_eur']:.0f} |")
L.append(f"| **Jahreskosten gesamt** | **{ak['jahreskosten_eur']:.0f}** |")
L.append("")
L.append(f"**Jahreserzeugung:** {ak['energie_jahr_kWh']:.0f} kWh/a")
L.append("")
L.append(f"**Mindestverkaufspreis: {ak['gestehungskosten_ct_kWh']:.1f} ct/kWh**")
# ── Marge ────────────────────────────────────────────────────
if ak["marge"]:
L.extend(_h2("Marge gegen Ersatzbrennstoff"))
L.extend(_h3("Voraussetzungen"))
L.extend(_tbl_header(["Parameter", "Wert"]))
L.append(f"| Mindestverkaufspreis (aus Gestehungskosten)"
f" | {ak['gestehungskosten_ct_kWh']:.1f} ct/kWh |")
for m in ak["marge"]:
L.append(f"| Nutzwaerme {m['name']} | {m['nutzwaerme_ct_kWh']:.1f} ct/kWh |")
L.append("")
L.extend(_h3("Ergebnisse"))
L.append("Marge = Nutzwaermekosten Brennstoff Mindestverkaufspreis."
" Positiv = System kostendeckend.")
L.append("")
L.extend(_tbl_header(
["Brennstoff", "ct/kWh (Nutzw.)", "Marge ct/kWh", "Marge EUR/Jahr"]))
for m in ak["marge"]:
L.append(f"| {m['name']} | {m['nutzwaerme_ct_kWh']:.1f} |"
f" {m['marge_ct_kWh']:+.1f} | {m['marge_eur_jahr']:+.0f} |")
return "\n".join(L)