Files
Milchwaerme/lib/report_md.py
T
2026-06-15 07:06:48 +02:00

450 lines
24 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 _code(lines: list[str]) -> list[str]:
return ["```"] + lines + ["```", ""]
def report_md(e: dict, cfg: dict, quelldateien: list[str] | None = None) -> str:
"""Vollstaendiger Markdown-Report: je Block Voraussetzungen → Formeln → 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("Formeln"))
L.extend(_code([
"Q = V × ρ × c × ΔT / 3600 [kWh/Tag]",
" = (Milchmenge × Dichte) × spez_Waerme × (T_ein T_aus) / 3600",
"",
f" = ({milch:.0f} L/Tag × {m_cfg['dichte_kg_pro_L']:.2f} kg/L)"
f" × {m_cfg['spez_waerme_kJ_pro_kgK']:.3f} kJ/(kg·K)"
f" × ({m_cfg['temp_ein_C']:.0f} {m_cfg['temp_aus_C']:.0f}) K / 3600",
f" = {q:.1f} kWh/Tag",
]))
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("Formeln"))
L.extend(_code([
"Leistungsauslegung aus Kuehllast und Laufzeit:",
" P_Kaelte = Q_Tag / Laufzeit [kW] (Verdampfer)",
" P_Heiz = P_Kaelte × COP / (COP 1) [kW] (Kondensator)",
" P_el = P_Kaelte / (COP 1) [kW] (Antrieb)",
"",
"Tageswerte:",
" W_el = P_el × Laufzeit [kWh/Tag]",
" Q_Speicher = P_Heiz × Laufzeit [kWh/Tag]",
"",
f" P_Kaelte = {q:.1f} / {wp_cfg['laufzeit_h_pro_tag']:.0f}"
f" = {wp['kaelteleistung_kW']:.1f} kW",
f" P_Heiz = {wp['kaelteleistung_kW']:.1f}"
f" × {wp_cfg['cop']:.2f} / ({wp_cfg['cop']:.2f} 1)"
f" = {wp['heizleistung_kW']:.1f} kW",
f" P_el = {wp['kaelteleistung_kW']:.1f}"
f" / ({wp_cfg['cop']:.2f} 1)"
f" = {wp['elektrische_leistung_kW']:.1f} kW",
]))
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("Formeln"))
L.extend(_code([
"Jahresbedarf = W_el_Tag × 365 [kWh/a]",
"kWp = Jahresbedarf / spez_Ertrag [kWp]",
"Flaeche = kWp × Flaeche_je_kWp [m²]",
"",
f"kWp = {pv['jahresbedarf_kWh']:.0f} kWh/a"
f" / {pv_cfg['spez_ertrag_kWh_pro_kWp_jahr']} kWh/(kWp·a)"
f" = {pv['kWp']:.1f} kWp",
]))
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("Formeln"))
L.extend(_code([
"je Monat (n = Tage im Monat):",
" PV_Monat = kWp × Monatsertrag_kWh_pro_kWp [kWh]",
" Bedarf_Monat = W_el_Tag × n [kWh]",
" Zukauf = max(0, Bedarf_Monat PV_Monat) [kWh]",
" Ueberschuss = max(0, PV_Monat Bedarf_Monat) [kWh]",
"",
"Jahreswerte:",
" Netzbezug = Σ Zukauf [kWh/a]",
" Anteil = Netzbezug / Jahresbedarf [%]",
]))
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("Formeln"))
L.extend(_code([
"Pufferbedarf = W_el × (24 Direkt_PV_h) / 24 [kWh]",
" (Anteil des Tages ohne direkten PV-Strom)",
"C_nutzbar = Pufferbedarf × Reservefaktor [kWh]",
"C_nominal = C_nutzbar / Entladetiefe_DoD [kWh]",
"",
f"Pufferbedarf = {wp['strom_kWh_tag']:.1f}"
f" × (24 {bat_cfg['direkt_pv_stunden']:.0f}) / 24"
f" = {wp['strom_kWh_tag'] * (24 - bat_cfg['direkt_pv_stunden']) / 24:.1f} kWh",
f"C_nutzbar = … × {bat_cfg['reserve_faktor']:.1f}"
f" = {bat['nutzbar_kWh']:.1f} kWh",
f"C_nominal = {bat['nutzbar_kWh']:.1f}"
f" / {bat_cfg['nutzbar_anteil']:.1f}"
f" = {bat['nominal_kWh']:.1f} kWh",
]))
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("Formeln"))
if pcm_cfg["modus"] == "latent_plus_hub":
dt_uh = max(0.0, pcm_cfg["ladetemp_C"] - pcm_cfg["schmelzpunkt_C"])
dt_uk = max(0.0, pcm_cfg["schmelzpunkt_C"] - pcm_cfg["entladetemp_C"])
e_kj = (pcm_cfg["latentwaerme_kJ_pro_kg"]
+ pcm_cfg["cp_fluessig_kJ_pro_kgK"] * dt_uh
+ pcm_cfg["cp_fest_kJ_pro_kgK"] * dt_uk)
L.extend(_code([
"Energiedichte (Modus latent_plus_hub):",
" E = (L + cp_fl · ΔT_üh + cp_fest · ΔT_uk) / 3600 [kWh/kg]",
" mit ΔT_üh = max(0, T_Lade T_Schmelz) (Ueberhitzung, fluessig)",
" ΔT_uk = max(0, T_Schmelz T_Entlade) (Unterkuehlung, fest)",
"",
f" ΔT_üh = max(0, {pcm_cfg['ladetemp_C']:.0f} {pcm_cfg['schmelzpunkt_C']:.0f})"
f" = {dt_uh:.0f} K",
f" ΔT_uk = max(0, {pcm_cfg['schmelzpunkt_C']:.0f} {pcm_cfg['entladetemp_C']:.0f})"
f" = {dt_uk:.0f} K",
f" E = ({pcm_cfg['latentwaerme_kJ_pro_kg']:.0f}"
f" + {pcm_cfg['cp_fluessig_kJ_pro_kgK']:.1f}·{dt_uh:.0f}"
f" + {pcm_cfg['cp_fest_kJ_pro_kgK']:.1f}·{dt_uk:.0f}) / 3600"
f" = {e_kj:.1f} / 3600"
f" = {sp['energiedichte_kWh_pro_kg']*1000:.1f} Wh/kg",
"",
"Speichergroesse:",
" E_Zyklus = Q_Speicher_Tag × Zyklus [kWh]",
" m = E_Zyklus / E [kg]",
" V = m / (ρ × 1000) [m³]",
"",
f" E_Zyklus = {wp['speicher_kWh_tag']:.1f} kWh/Tag × {z} Tage"
f" = {sp['energie_pro_zyklus_kWh']:.0f} kWh",
f" m = {sp['energie_pro_zyklus_kWh']:.0f}"
f" / {sp['energiedichte_kWh_pro_kg']:.4f}"
f" = {sp['masse_kg']:.0f} kg",
f" V = {sp['masse_kg']:.0f}"
f" / ({pcm_cfg['dichte_kg_pro_L']:.1f} × 1000)"
f" = {sp['volumen_m3']:.1f}",
]))
else:
L.extend(_code([
"Energiedichte (Modus latent_only):",
" E = L / 3600 [kWh/kg]",
"",
"Speichergroesse:",
" E_Zyklus = Q_Speicher_Tag × Zyklus [kWh]",
" m = E_Zyklus / E [kg]",
" V = m / (ρ × 1000) [m³]",
"",
f" E_Zyklus = {wp['speicher_kWh_tag']:.1f} × {z}"
f" = {sp['energie_pro_zyklus_kWh']:.0f} kWh",
f" m = {sp['energie_pro_zyklus_kWh']:.0f}"
f" / {sp['energiedichte_kWh_pro_kg']:.4f}"
f" = {sp['masse_kg']:.0f} kg",
f" V = {sp['masse_kg']:.0f}"
f" / ({pcm_cfg['dichte_kg_pro_L']:.1f} × 1000)"
f" = {sp['volumen_m3']:.1f}",
]))
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 |")
if sp.get("empf_zyklus_tage") is not None:
L.append("")
L.append(f"**empfohlene Zyklenzahl = {sp['empf_zyklus_tage']} Tage**"
f" (ergibt ≥ 90 % LKW-Auslastung)")
# ── 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("Formeln"))
L.extend(_code([
"WW_je_HH = n_Erw × WW_Erw + n_Kind × WW_Kind [kWh/Tag]",
"HH_WW = Q_Speicher / WW_je_HH [Haushalte, ganzjaehrig]",
"HH_Winter = Q_Speicher / (WW_je_HH + Heiz_Winter) [Haushalte, Winter]",
"",
f"WW_je_HH = {hh_cfg['erwachsene']} × {hh_cfg['ww_erwachsener_kWh_tag']:.1f}"
f" + {hh_cfg['kinder']} × {hh_cfg['ww_kind_kWh_tag']:.1f}"
f" = {hh['ww_pro_haushalt_kWh_tag']:.1f} kWh/Tag",
f"HH_WW = {wp['speicher_kWh_tag']:.1f}"
f" / {hh['ww_pro_haushalt_kWh_tag']:.1f}"
f" = {hh['haushalte_warmwasser']:.1f} HH",
f"HH_Winter = {wp['speicher_kWh_tag']:.1f}"
f" / {hh['winter_pro_haushalt_kWh_tag']:.1f}"
f" = {hh['haushalte_winter']:.1f} HH",
]))
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.extend(_h3("Formeln"))
L.extend(_code([
"Nutzwaermekosten = Preis_ct_kWh / Wirkungsgrad [ct/kWh]",
"EUR_je_LKW = E_LKW_kWh × Nutzwaermekosten / 100 [EUR]",
"EUR_je_Jahr = E_Jahr_kWh × Nutzwaermekosten / 100 [EUR/a]",
]))
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 |")
if ak["eh_da"]:
L.append("| ~~Kapital PV~~ | *eh-da: bereits vorhanden, entfaellt* |")
L.append("| ~~Kapital WP~~ | *eh-da: bereits vorhanden, entfaellt* |")
if ak["driver"]:
L.append(f"| Fahrerabzug | {ak['fahrer_abzug_eur_pro_fuhre']:.0f} EUR/Fuhre"
f" (--driver: Betreiber oder Landwirt faehrt selbst) |")
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.extend(_h3("Formeln"))
L.extend(_code([
"Kapital_X = Invest_X / Nutzungsdauer_X [EUR/a] (lineare AfA)",
"Kosten_Transport = Fuhren_Jahr × Kosten_je_Fuhre [EUR/a]",
"Kosten_Strom = kWh_Netz × p_Netz + kWh_PV × p_LCOE [EUR/a]",
"Jahreskosten = Σ(alle Kostenblöcke) [EUR/a]",
"",
"Gestehungskosten = Jahreskosten / Energie_Jahr × 100 [ct/kWh]",
"",
f"Gestehungskosten = {ak['jahreskosten_eur']:.0f} EUR/a"
f" / {ak['energie_jahr_kWh']:.0f} kWh/a × 100"
f" = {ak['gestehungskosten_ct_kWh']:.1f} ct/kWh",
]))
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("Formeln"))
L.extend(_code([
"Marge_ct = Nutzwaerme_Brennstoff Gestehungskosten [ct/kWh]",
" Positiv = System kostendeckend gegenueber diesem Brennstoff",
"Marge_EUR = Marge_ct × Energie_Jahr / 100 [EUR/a]",
]))
L.extend(_h3("Ergebnisse"))
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)