diff --git a/cfg/config.json b/cfg/config.json index 87fa6b4..d77ea4d 100644 --- a/cfg/config.json +++ b/cfg/config.json @@ -172,5 +172,38 @@ "_quellen_montage": [ "Stundensaetze Anlagenbau DE 2025: VDMA-Statistik (vdma.org), regionale Schlossereibetriebe" ] + }, + + "anbieter": { + "_beschreibung": "Betriebswirtschaftliche Annahmen fuer die Anbieterseite. Ermoeglicht die Berechnung von Waerme-Gestehungskosten (ct/kWh) und Marge gegenueber Ersatzbrennstoff. Abschreibung: linear, kein Kapitalzins (konservativ).", + + "transport_eur_pro_fuhre": 300, + "_kommentar_transport": "Kosten je LKW-Fuhre (Treibstoff, Fahrer, Abschreibung Fahrzeug). Richtwert fuer Kurzstrecke bis ~100 km. Fuer laengere Strecken entsprechend erhoehen.", + + "netzstrompreis_ct_kWh": 28.0, + "_kommentar_netzstrom": "Industriestrompreis (Netzbezug), Stand 06/2026 DE. Fuer Landwirtschaft oft Sondertarif moeglich (~22-28 ct/kWh). Quelle: BDEW Strompreisanalyse (bdew.de).", + + "pv_gestehung_ct_kWh": 9.0, + "_kommentar_pv_gestehung": "Levelized Cost of Energy (LCOE) der eigenen PV-Anlage, d.h. Abschreibung und Wartung je erzeugter kWh. Typisch 7-11 ct/kWh fuer Neuanlagen 2024-2026 (Fraunhofer ISE: ise.fraunhofer.de).", + + "wartung_eur_pro_jahr": 500, + "_kommentar_wartung": "Pauschale Jahreskosten fuer Wartung, Versicherung, Kleinreparaturen der gesamten Anlage (WP, PV, Speicher). Exkl. Speicher-Container (separat ueber container_invest abgebildet).", + + "invest": { + "pv_eur_pro_kWp": 1200, + "pv_nutzungsdauer_jahre": 25, + "_kommentar_pv": "Investitionskosten PV inkl. Montage und Wechselrichter, Stand 2025 DE. Quelle: Bundesnetzagentur Marktstammdatenregister, Fraunhofer ISE Photovoltaics Report (ise.fraunhofer.de).", + + "batterie_eur_pro_kWh": 400, + "batterie_nutzungsdauer_jahre": 15, + "_kommentar_batterie": "LFP-Hausspeicher inkl. Wechselrichter, Stand 2025 DE (~350-500 EUR/kWh Nennkapazitaet). Quelle: BVES Jahresbericht (bves.de), Haendlerpreise.", + + "waermepumpe_eur": 12000, + "waermepumpe_nutzungsdauer_jahre": 20, + "_kommentar_waermepumpe": "Industrie-Waermepumpe 3-5 kW el. inkl. Montage und Inbetriebnahme. Quelle: BWP Branchenstudie (waermepumpe.de), Haendlerangebote.", + + "speicher_nutzungsdauer_jahre": 20, + "_kommentar_speicher": "Nutzungsdauer der Container-Module. PCM selbst ist unbegrenzt wiederverwertbar; limitierende Teile sind Behaelter (Korrosion) und Elektronik. Investitionshoehe wird aus container_invest berechnet." + } } } diff --git a/lib/auslegung.py b/lib/auslegung.py index cf0b7ed..755f254 100644 --- a/lib/auslegung.py +++ b/lib/auslegung.py @@ -24,6 +24,8 @@ import math import os from pathlib import Path +from report_md import report_md + KJ_PER_KWH = 3600.0 TAGE_PRO_JAHR = 365.0 @@ -241,6 +243,115 @@ def brennstoffkosten(e: dict, cfg: dict) -> dict: } +def container_invest_total(sp: dict, cfg: dict) -> dict: + """Investitionssumme aller Container-Module aus der container_invest-Config.""" + ci = cfg["container_invest"] + masse = sp["masse_kg"] + energie = sp["energie_pro_zyklus_kWh"] + anzahl = ci["anzahl_module_dauerbetrieb"] + + pcm_eur = masse * ci["pcm_natriumacetat"]["preis_eur_pro_kg"] * ci["pcm_natriumacetat"]["reserve_faktor"] + behaelter_eur = max(ci["behaelter"]["mindestpreis_eur"], + masse * ci["behaelter"]["preis_eur_pro_kg_pcm"]) + wt_eur = (ci["waermetauscher_ladung"]["preis_eur"] + + ci["waermetauscher_entladung"]["preis_eur"]) + hydraulik_eur = ci["hydraulik"]["preis_eur"] + el = ci["elektronik"] + elektronik_eur = (el["steuerung_eur"] + + el["sensoren_pt100_anzahl"] * el["sensor_preis_eur"] + + el["ventile_schaltschrank_eur"] + + el["fernueberwachung_4g_eur"] + + el["montage_verdrahtung_eur"]) + montage_eur = ci["montage_inbetriebnahme_eur"] + + pro_modul = pcm_eur + behaelter_eur + wt_eur + hydraulik_eur + elektronik_eur + montage_eur + gesamt = pro_modul * anzahl + eur_pro_kwh = gesamt / (energie * anzahl) if energie > 0 else 0.0 + + return { + "anzahl_module": anzahl, + "pcm_eur": pcm_eur * anzahl, + "behaelter_eur": behaelter_eur * anzahl, + "wt_hydraulik_eur": (wt_eur + hydraulik_eur) * anzahl, + "elektronik_eur": elektronik_eur * anzahl, + "montage_eur": montage_eur * anzahl, + "gesamt_eur": gesamt, + "eur_pro_kwh_kapazitaet": eur_pro_kwh, + } + + +def anbieterkosten(e: dict, cfg: dict) -> dict: + """Jaehrliche Betriebskosten des Anbieters -> Gestehungskosten und Marge. + + Abschreibung: linear ohne Kapitalzins (konservativ). + Strombedarf: Netzbezug zum Netzpreis, PV-Eigenanteil zu LCOE-Gestehungskosten. + """ + a = cfg["anbieter"] + inv = a["invest"] + wp = e["waermepumpe"] + sp = e["speicher"] + pv_data = e["pv"] + netz = e["netzbezug"] + bat = e["batterie"] + + energie_jahr = wp["speicher_kWh_tag"] * TAGE_PRO_JAHR + fuhren_jahr = TAGE_PRO_JAHR / cfg["abholung"]["zyklus_tage"] + + kosten_transport = fuhren_jahr * a["transport_eur_pro_fuhre"] + + netz_eur = netz["netzbezug_kWh"] * a["netzstrompreis_ct_kWh"] / 100.0 + pv_kwh = netz["jahresbedarf_kWh"] - netz["netzbezug_kWh"] + pv_eur = pv_kwh * a["pv_gestehung_ct_kWh"] / 100.0 + kosten_strom = netz_eur + pv_eur + + kosten_wartung = a["wartung_eur_pro_jahr"] + + kapital_pv = pv_data["kWp"] * inv["pv_eur_pro_kWp"] / inv["pv_nutzungsdauer_jahre"] + kapital_bat = bat["nominal_kWh"] * inv["batterie_eur_pro_kWh"] / inv["batterie_nutzungsdauer_jahre"] + kapital_wp = inv["waermepumpe_eur"] / inv["waermepumpe_nutzungsdauer_jahre"] + ci = container_invest_total(sp, cfg) + kapital_speicher = ci["gesamt_eur"] / inv["speicher_nutzungsdauer_jahre"] + kosten_kapital = kapital_pv + kapital_bat + kapital_wp + kapital_speicher + + jahreskosten = kosten_transport + kosten_strom + kosten_wartung + kosten_kapital + gestehung_ct = jahreskosten / energie_jahr * 100.0 if energie_jahr > 0 else 0.0 + + marge = [] + for v in e.get("brennstoffkosten", {}).get("vergleich", []): + m_ct = v["nutzwaerme_ct_kWh"] - gestehung_ct + marge.append({ + "name": v["name"], + "nutzwaerme_ct_kWh": v["nutzwaerme_ct_kWh"], + "marge_ct_kWh": m_ct, + "marge_eur_jahr": m_ct / 100.0 * energie_jahr, + }) + + return { + "fuhren_jahr": fuhren_jahr, + "energie_jahr_kWh": energie_jahr, + "kosten_transport_eur": kosten_transport, + "kosten_strom_eur": kosten_strom, + "kosten_strom_detail": { + "netz_kWh": netz["netzbezug_kWh"], + "netz_ct_kWh": a["netzstrompreis_ct_kWh"], + "netz_eur": netz_eur, + "pv_kWh": pv_kwh, + "pv_ct_kWh": a["pv_gestehung_ct_kWh"], + "pv_eur": pv_eur, + }, + "kosten_wartung_eur": kosten_wartung, + "kapital_pv_eur": kapital_pv, + "kapital_batterie_eur": kapital_bat, + "kapital_waermepumpe_eur": kapital_wp, + "kapital_speicher_eur": kapital_speicher, + "kosten_kapital_eur": kosten_kapital, + "jahreskosten_eur": jahreskosten, + "gestehungskosten_ct_kWh": gestehung_ct, + "container_invest": ci, + "marge": marge, + } + + def rechne(milch_l_tag: float, cfg: dict) -> dict: q_milch = milch_kuehllast(milch_l_tag, cfg) wp = waermepumpe(q_milch, cfg) @@ -261,6 +372,8 @@ def rechne(milch_l_tag: float, cfg: dict) -> dict: } if "brennstoffe" in cfg: e["brennstoffkosten"] = brennstoffkosten(e, cfg) + if "anbieter" in cfg and "container_invest" in cfg: + e["anbieter"] = anbieterkosten(e, cfg) return e @@ -416,122 +529,51 @@ def report(e: dict, cfg: dict) -> str: f"{v['eur_pro_lkw']:>9.0f} | {v['eur_pro_jahr']:>9.0f}") L.append(f" Bewertet: voller LKW ~{bk['energie_lkw_kWh']:.0f} kWh, " f"Jahreserzeugung ~{bk['energie_jahr_kWh']:.0f} kWh") + ak = e.get("anbieter") + if ak: + inv = cfg["anbieter"]["invest"] + ci = ak["container_invest"] + sd = ak["kosten_strom_detail"] + L.append("") + L.append("WAERME-GESTEHUNGSKOSTEN ANBIETER (linear, kein Kapitalzins)") + L.append(f" Transport ({ak['fuhren_jahr']:.0f} Fuhren/a) ........" + f" {ak['kosten_transport_eur']:>8.0f} EUR/a") + L.append(f" WP-Strom ({sd['netz_kWh']:.0f} kWh Netz" + f" @ {sd['netz_ct_kWh']:.0f} ct + {sd['pv_kWh']:.0f} kWh PV" + f" @ {sd['pv_ct_kWh']:.0f} ct) .. {ak['kosten_strom_eur']:>6.0f} EUR/a") + L.append(f" Wartung .......................... {ak['kosten_wartung_eur']:>8.0f} EUR/a") + L.append(f" Kapital PV ({pv['kWp']:.1f} kWp, {inv['pv_nutzungsdauer_jahre']} J) ......" + f" {ak['kapital_pv_eur']:>8.0f} EUR/a") + L.append(f" Kapital Batterie ({bat['nominal_kWh']:.0f} kWh, {inv['batterie_nutzungsdauer_jahre']} J) ." + f" {ak['kapital_batterie_eur']:>8.0f} EUR/a") + L.append(f" Kapital WP ({inv['waermepumpe_nutzungsdauer_jahre']} J) ............." + f" {ak['kapital_waermepumpe_eur']:>8.0f} EUR/a") + L.append(f" Kapital Speicher ({ci['anzahl_module']} Module, {inv['speicher_nutzungsdauer_jahre']} J)" + f" {ak['kapital_speicher_eur']:>8.0f} EUR/a") + L.append(f" {'-'*46}") + L.append(f" Jahreskosten gesamt .............. {ak['jahreskosten_eur']:>8.0f} EUR/a") + L.append(f" Jahreserzeugung .................. {ak['energie_jahr_kWh']:>8.0f} kWh/a") + L.append(f" -> Mindestverkaufspreis .......... {ak['gestehungskosten_ct_kWh']:>8.1f} ct/kWh") + L.append(f" Speicher-Invest ({ci['anzahl_module']} Module): {ci['gesamt_eur']:,.0f} EUR" + f" = {ci['eur_pro_kwh_kapazitaet']:.0f} EUR/kWh Kapazitaet") + L.append(f" PCM {ci['pcm_eur']:,.0f} | Behaelter {ci['behaelter_eur']:,.0f}" + f" | WT+Hydr. {ci['wt_hydraulik_eur']:,.0f}" + f" | Elektr. {ci['elektronik_eur']:,.0f} | Montage {ci['montage_eur']:,.0f}") + if ak["marge"]: + L.append("") + L.append("MARGE GEGEN ERSATZBRENNSTOFF") + L.append(f" {'Brennstoff':<10} | {'ct/kWh':>7} | {'Marge ct/kWh':>12} | {'Marge EUR/Jahr':>14}") + L.append(f" {'-'*10}-+-{'-'*7}-+-{'-'*12}-+-{'-'*14}") + for m in ak["marge"]: + haertester = " <== haertester Massstab" if m == ak["marge"][-1] else "" + L.append(f" {m['name']:<10} | {m['nutzwaerme_ct_kWh']:>7.1f} |" + f" {m['marge_ct_kWh']:>+12.1f} |" + f" {m['marge_eur_jahr']:>+14.0f}{haertester}") L.append("=" * 62) 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 |") - - bk = e.get("brennstoffkosten") - if bk and bk["vergleich"]: - L.append("") - L.append("## Wert der Waerme beim Kaeufer (Ersatzkosten fossil/Holz)") - L.append("") - L.append(f"Bewertet: voller LKW ~{bk['energie_lkw_kWh']:.0f} kWh, " - f"Jahreserzeugung ~{bk['energie_jahr_kWh']:.0f} kWh") - L.append("") - L.append("| Brennstoff | ct/kWh (Nutzw.) | EUR/LKW | EUR/Jahr |") - L.append("|------------|-----------------|---------|----------|") - 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} |") - L.append("") - L.append("*ct/kWh = Brennstoffpreis ÷ Kesselwirkungsgrad*") - - return "\n".join(L) +# report_md wurde nach lib/report_md.py ausgelagert und oben importiert. def main() -> None: diff --git a/lib/report_md.py b/lib/report_md.py new file mode 100644 index 0000000..445d305 --- /dev/null +++ b/lib/report_md.py @@ -0,0 +1,277 @@ +"""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)