Compare commits
9 Commits
3276ec8d86
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 160a0f5f7b | |||
| fc6c2abbb1 | |||
| 6c4f007c3f | |||
| f392239eb0 | |||
| 6d3d7bf55c | |||
| 0a3972a9b4 | |||
| 3f59877aa9 | |||
| 06ead41035 | |||
| d6e34b5e7c |
@@ -0,0 +1,47 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Projektübersicht
|
||||
|
||||
**Milchwaerme** ist ein Kommandozeilen-Werkzeug zur Auslegung einer Wärmerückgewinnung
|
||||
aus der Milchkühlung. Aus der täglich zu kühlenden Milchmenge werden Wärmepumpe,
|
||||
PV-Anlage, Batterie und Natriumacetat-Latentspeicher dimensioniert — inklusive
|
||||
Abhollogistik, versorgbarer Haushalte und Brennstoffkosten-Vergleich. Alle Annahmen
|
||||
liegen in einer `config.json`, sodass Varianten ohne Codeänderung nachgerechnet werden.
|
||||
|
||||
## Konzept
|
||||
|
||||
```text
|
||||
Milchabwärme (37→4 °C) ─► Wärmepumpe (PV-Strom) ─► Latentspeicher (Natriumacetat, 58 °C) ─► Abholung per LKW
|
||||
```
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```text
|
||||
Milchwaerme/
|
||||
├── bin/ # Start-Skripte (.bat/.sh): setenv, auslegung, revival, get_cmd
|
||||
├── lib/ # Python-Quellcode
|
||||
│ ├── auslegung.py # Berechnungsprogramm
|
||||
│ ├── report_md.py # Markdown-Report
|
||||
│ └── revival.py # Revival-/Zusatzberechnung
|
||||
├── cfg/ # Konfiguration (config.json mit allen Annahmen)
|
||||
├── doc/ # Dokumentation
|
||||
└── out/ # Ausgaben
|
||||
```
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Python 3.8+
|
||||
- Keine externen Abhängigkeiten (nur Standardbibliothek)
|
||||
|
||||
## Nutzung
|
||||
|
||||
```cmd
|
||||
bin\auslegung.bat
|
||||
```
|
||||
```bash
|
||||
bin/auslegung.sh
|
||||
```
|
||||
|
||||
Varianten rechnet man durch, indem man `config.json` kopiert und anpasst.
|
||||
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM TEST - Shell mit gesetzten Umgebungsvariablen oeffnen
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
start cmd /k "echo TEST Umgebung aktiv. && echo DXFMAKRO=%DXFMAKRO%"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# ================================================================
|
||||
# TEST - Shell mit gesetzten Umgebungsvariablen oeffnen
|
||||
# ================================================================
|
||||
# Verwendung: source bin/get_cmd.sh
|
||||
# ================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/setenv.sh"
|
||||
|
||||
exec "$SHELL"
|
||||
@@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
REM Aufruf: revival.bat <kWp> [weitere Optionen]
|
||||
REM Beispiel: revival.bat 100
|
||||
REM revival.bat 250 --sonnenstunden 5.5 -v
|
||||
IF NOT DEFINED MILCH_HOME (
|
||||
SET MILCH_HOME=%~dp0..
|
||||
IF "%MILCH_HOME:~-1%"=="\" SET MILCH_HOME=%MILCH_HOME:~0,-1%
|
||||
)
|
||||
IF NOT DEFINED MILCH_LIB SET MILCH_LIB=%MILCH_HOME%\lib
|
||||
IF NOT DEFINED MILCH_CFG SET MILCH_CFG=%MILCH_HOME%\cfg
|
||||
IF NOT DEFINED MILCH_OUT SET MILCH_OUT=%MILCH_HOME%\out
|
||||
python "%MILCH_LIB%\revival.py" --config "%MILCH_CFG%\config.json" --out "%MILCH_OUT%" %*
|
||||
+131
-3
@@ -11,8 +11,9 @@
|
||||
|
||||
"waermepumpe": {
|
||||
"cop": 2.6,
|
||||
"cop_wasser": 4.0,
|
||||
"laufzeit_h_pro_tag": 18.0,
|
||||
"_kommentar": "COP fuer den Hub von ~0 C (Verdampfer) auf >58 C (Kondensator). Laufzeit bestimmt die Leistungsauslegung (kW)."
|
||||
"_kommentar": "cop = Luft-WP fuer den Hub von ~0 C (Verdampfer) auf >58 C (Kondensator). cop_wasser = Wasser/Wasser-WP mit Grundwasser/Geothermie als Quelle (~10-15 C). Laufzeit bestimmt die Leistungsauslegung (kW)."
|
||||
},
|
||||
|
||||
"pcm": {
|
||||
@@ -42,7 +43,9 @@
|
||||
"Jan": 30, "Feb": 45, "Mae": 85, "Apr": 115, "Mai": 130, "Jun": 130,
|
||||
"Jul": 135, "Aug": 120, "Sep": 95, "Okt": 60, "Nov": 32, "Dez": 25
|
||||
},
|
||||
"_kommentar": "Werte fuer optimal geneigte Suedflaeche, Standort Suedbayern. Summe der Monate ~ spez_ertrag."
|
||||
"einspeiseverguetung_ct_kWh": 8.1,
|
||||
"direktvermarktung_ct_kWh": 7.0,
|
||||
"_kommentar": "Werte fuer optimal geneigte Suedflaeche, Standort Suedbayern. Summe der Monate ~ spez_ertrag. Einspeiseverguetung: EEG 2026 fuer Anlagen >100 kWp (Anzulegender Wert). Direktvermarktung: Boersenstrom abzgl. Vermarktungskosten."
|
||||
},
|
||||
|
||||
"batterie": {
|
||||
@@ -81,5 +84,130 @@
|
||||
"_kommentar": "Holzpellets Juni 2026 (~350 EUR/t bei 5 kWh/kg), moderner Pelletskessel."
|
||||
}
|
||||
],
|
||||
"_kommentar_brennstoffe": "Vergleichswert: Nutzwaermekosten = preis_ct_kWh / wirkungsgrad. Reihenfolge bestimmt die Anzeigereihenfolge im Report."
|
||||
"_kommentar_brennstoffe": "Vergleichswert: Nutzwaermekosten = preis_ct_kWh / wirkungsgrad. Reihenfolge bestimmt die Anzeigereihenfolge im Report.",
|
||||
|
||||
"container_invest": {
|
||||
"_beschreibung": "Kostenbausteine fuer ein Latentspeicher-Modul (LKW-Aufbau). Das Programm skaliert pcm und behaelter mit der berechneten PCM-Masse; alle anderen Positionen sind Pauschalpreise je Modul.",
|
||||
|
||||
"anzahl_module_dauerbetrieb": 2,
|
||||
"_kommentar_module": "Dauerbetrieb erfordert mindestens 2 Module: eines wird auf dem Hof geladen, eines ist unterwegs zum Kaeufer.",
|
||||
|
||||
"pcm_natriumacetat": {
|
||||
"preis_eur_pro_kg": 0.70,
|
||||
"reserve_faktor": 1.05,
|
||||
"_kommentar": "Industriequalitaet (nicht Pharma), Grossgebinde ab 1 t, Stand 06/2026 DE. Reservefaktor deckt Befuell- und Handhabungsverluste ab.",
|
||||
"_quellen": [
|
||||
"Brenntag DE (Grosshaendler Chemierohstoffe): brenntag.com/de-de",
|
||||
"Sasol / Clariant (Hersteller Natriumacetat techn.): sasol.com",
|
||||
"Alibaba Preisvergleich Grossmengen: alibaba.com (Suche: sodium acetate trihydrate bulk)",
|
||||
"Rubitherm GmbH (PCM-Spezialist, Vergleichspreise): rubitherm.eu"
|
||||
]
|
||||
},
|
||||
|
||||
"behaelter": {
|
||||
"preis_eur_pro_kg_pcm": 2.80,
|
||||
"mindestpreis_eur": 12000,
|
||||
"_kommentar": "Isolierter Edelstahlbehaelter (1.4301/1.4404) in Rahmenbauweise fuer LKW-Aufbau, 80 mm PUR-Daemmung, drucklos bis 120 C. Preis linear skaliert je kg PCM-Kapazitaet. Mindestpreis gilt fuer die kleinste sinnvolle Einheit (ca. 3 t PCM / ~2300 L Volumen). Verbindliche Preise nur per Anfrage beim Behaelterbauer.",
|
||||
"_quellen": [
|
||||
"Ziemann Holvrieka (Edelstahlbehaelter Lebensmittel/Industrie): ziemann-holvrieka.com",
|
||||
"Paul Mueller Company (Thermalbehaelter, Milchwirtschaft): paulmueller.com",
|
||||
"GPI Tanks (Industrietanks, Anfrage Sonderanfertigung): gpitanks.com",
|
||||
"Vergleichswert: Isolierter IBC 1000 L (Schuetz/Mauser) ~1.200 EUR -> hoeherer Preis fuer Sonderbehaelter 10-30 t plausibel"
|
||||
]
|
||||
},
|
||||
|
||||
"waermetauscher_ladung": {
|
||||
"typ": "Platten-WT Milchwirtschaft (EHEDG, 1.4404)",
|
||||
"hersteller_beispiel": "Alfa Laval M6 / GEA NT50 / Kelvion NH250",
|
||||
"nennleistung_kW": 5.0,
|
||||
"preis_eur": 2500,
|
||||
"_kommentar": "Handelsueblicher Milch-Platten-WT, EHEDG-zertifiziert und CIP-faehig (identischer Typ wie in der Milchkuehlung des Hofs). Laedt den PCM-Behaelter aus dem WP-Kondensator (Vorlauf 80 C, Ruecklauf ~60 C). Preis inkl. Rahmen, ohne Montage. Gleiche Bauart wird in der Hof-Milchkuehlung bereits eingesetzt.",
|
||||
"_quellen": [
|
||||
"Alfa Laval (M6/M10 Milch-PHE, Preisliste auf Anfrage): alfalaval.com",
|
||||
"GEA Group (NT50/NT100 fuer Dairy): gea.com",
|
||||
"Kelvion (NH250 Plattentauscher): kelvion.com",
|
||||
"Marktpreis Neu 5 kW PHE Milch: 1.800-3.500 EUR (Haendlerauskunft, Stand 2025)"
|
||||
]
|
||||
},
|
||||
|
||||
"waermetauscher_entladung": {
|
||||
"typ": "Platten-WT Industrial (1.4404)",
|
||||
"nennleistung_kW": 20.0,
|
||||
"preis_eur": 1500,
|
||||
"_kommentar": "Entladeseite: PCM-Waerme -> Kundennetz an der LKW-seitigen Uebergabestation. Kein Lebensmittelkontakt, daher Standard-PHE ohne EHEDG-Anforderung.",
|
||||
"_quellen": [
|
||||
"Alfa Laval (CB/AlfaNova Kompakt-PHE): alfalaval.com",
|
||||
"Funke Waermeaustauscher (GPW-Serie): funke.de",
|
||||
"Marktpreis Neu 20 kW Industrial PHE: 900-2.200 EUR (Haendlerauskunft, Stand 2025)"
|
||||
]
|
||||
},
|
||||
|
||||
"hydraulik": {
|
||||
"preis_eur": 1200,
|
||||
"_kommentar": "Schnellanschluesse (Kamlok DN50), Absperrventile (Kugelhahn PN16), Umwaelzpumpe (Grundfos CM5), flexible Hochtemperaturschlaeuche (bis 150 C), Rohrverbinder und Kleinmaterial.",
|
||||
"_quellen": [
|
||||
"Grundfos (CM5-Pumpe, Produktkatalog): grundfos.com",
|
||||
"Georg Fischer (Kugelhahn PN16, 1.4408): georgfischer.com",
|
||||
"Stauff / Parker (Hochtemperaturschlaeuche): stauff.com",
|
||||
"Preise: Industriebedarf-Haendler wie RS Components (rs-online.com) oder Conrad"
|
||||
]
|
||||
},
|
||||
|
||||
"elektronik": {
|
||||
"steuerung_eur": 800,
|
||||
"sensoren_pt100_anzahl": 10,
|
||||
"sensor_preis_eur": 75,
|
||||
"ventile_schaltschrank_eur": 500,
|
||||
"fernueberwachung_4g_eur": 350,
|
||||
"montage_verdrahtung_eur": 800,
|
||||
"_kommentar": "Steuerung: SPS (SIMATIC S7-1200 o.ae.) oder µC-basiert (ESP32 + Industrie-Shield). PT100-Sensoren verteilt im PCM-Volumen (oben/mitte/unten je Zone) fuer Ladestandserkennung ueber Temperaturprofil. Schaltschrank IP54, 4G-Fernueberwachung fuer Betriebsdaten und Alarme.",
|
||||
"_quellen": [
|
||||
"Siemens SIMATIC S7-1200 (SPS, Listenpreis ~600-900 EUR): siemens.com/simatic",
|
||||
"Weidmueller / Phoenix Contact (Schaltschrank-Komponenten): phoenixcontact.com",
|
||||
"PT100-Sensor Industrieausfuehrung (z.B. Jumo, Endress+Hauser): jumo.de / endress.com",
|
||||
"4G-Fernueberwachung (z.B. Teltonika RUT240, ~150 EUR + SIM): teltonika-networks.com",
|
||||
"Alternativ µC-Loesung: ESP32 + Modbus-Shield + Node-RED (Open-Source-Stack)"
|
||||
]
|
||||
},
|
||||
|
||||
"montage_inbetriebnahme_eur": 2000,
|
||||
"_kommentar_montage": "Mechanische Montage aller Komponenten auf dem Containerrahmen, Leitungsverlegung, Dichtigkeitspruefung, Erstbefuellung PCM, Inbetriebnahme Regelung. Schaetzwert auf Basis Montagestundensatz Anlagenbau Suedbayern ~65-80 EUR/h, ca. 25-30 h.",
|
||||
"_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,
|
||||
"fahrer_eur_pro_fuhre": 120,
|
||||
"_kommentar_transport": "transport_eur_pro_fuhre: Gesamtkosten je Fuhre (Treibstoff + Fahrer + Fahrzeugabschreibung). fahrer_eur_pro_fuhre: Fahreranteil daran, wird mit --driver abgezogen wenn der Landwirt selbst faehrt. Richtwert Kurzstrecke bis ~100 km.",
|
||||
|
||||
"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."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# Recherche: Kosten Waermepumpe und Waermetauscher
|
||||
|
||||
Stand: Juni 2026
|
||||
|
||||
Bezugsgroesse: PV-Revival-Szenario mit 100 kWp PV-Anlage
|
||||
-> Waermepumpe ~87 kW elektrisch / ~227 kW thermisch (COP 2.6)
|
||||
-> Waermetauscher ~272 kW (Sicherheitsfaktor 1.2x)
|
||||
|
||||
|
||||
## 1. Industrie-Waermepumpe (~227 kW thermisch / ~87 kW elektrisch)
|
||||
|
||||
In dieser Leistungsklasse handelt es sich um Industrie-/Grosswaermepumpen (>100 kW).
|
||||
Konkrete Listenpreise werden von Herstellern nicht oeffentlich publiziert — es sind
|
||||
immer Projektangebote. Aus den verfuegbaren Quellen lassen sich Richtwerte ableiten:
|
||||
|
||||
| Kennzahl | Richtwert |
|
||||
|-----------------------------------|----------------------------|
|
||||
| Spezifische Kosten (Geraet) | 350-500 EUR/kW thermisch |
|
||||
| Investition WP ~227 kW th. | ca. 80.000-115.000 EUR |
|
||||
| Inkl. Montage, Verrohrung, IBN | ca. 100.000-140.000 EUR |
|
||||
|
||||
Der Installationsaufschlag (Montage, Verrohrung, Inbetriebnahme) betraegt
|
||||
typischerweise ca. +25 % auf den Geraetepreis.
|
||||
|
||||
### Hersteller in dieser Leistungsklasse
|
||||
|
||||
| Hersteller | Land | Serie / Bemerkung |
|
||||
|--------------------------|------|------------------------------------------|
|
||||
| Ochsner | AT | GMLW-Serie, bis 2.500 kW, Industriefokus|
|
||||
| Viessmann / Carrier | DE | Vitocal 350 Pro, Grossanlagen |
|
||||
| Daikin | JP | VRV / Altherma Hochleistung |
|
||||
| MAN Energy Solutions | DE | Hochtemperatur-Industriewaermepumpen |
|
||||
| Combitherm / Friotherm | CH | Spezialisten fuer >200 kW |
|
||||
|
||||
### Referenzwert
|
||||
|
||||
Eine Viessmann Vitocal 250-A mit 48 kW kostet ca. 44.000 EUR (~917 EUR/kW).
|
||||
Bei Grossanlagen sinken die spezifischen Kosten auf 350-500 EUR/kW durch
|
||||
Skaleneffekte.
|
||||
|
||||
|
||||
## 2. Waermetauscher (~272 kW Ladeleistung)
|
||||
|
||||
Plattenwaermetauscher (PHE) in dieser Leistungsklasse sind deutlich guenstiger
|
||||
als die Waermepumpe selbst.
|
||||
|
||||
| Typ | Leistung | Preis |
|
||||
|------------------------------------------|------------|-----------------|
|
||||
| Geloeteter PHE (Standard) | 250-300 kW | 300-800 EUR |
|
||||
| Geschraubter PHE (Edelstahl, zerlegbar) | 300 kW | 1.500-3.000 EUR |
|
||||
| EHEDG/Lebensmittel-PHE (Alfa Laval, GEA)| 300 kW | 3.000-5.000 EUR |
|
||||
|
||||
Fuer die Ladeseite (WP -> Speicher, 80 C) empfiehlt sich ein geschraubter
|
||||
Edelstahl-PHE wegen Wartbarkeit bei hohen Temperaturen: ca. 2.000-3.000 EUR.
|
||||
|
||||
### Anbieter / Shops
|
||||
|
||||
- Satura: geschraubte Edelstahl-PHE bis 300 kW
|
||||
- Inocal: Fernwaerme-PHE PBU10-120, 300 kW
|
||||
- Alfa Laval: CB/AlfaNova Kompakt-PHE (Industrie)
|
||||
- GEA / Kelvion: NT-Serie, NH-Serie
|
||||
- SWEP: B28-Serie, 50-300 kW
|
||||
|
||||
|
||||
## 3. Gesamtkosten-Einordnung (100 kWp Revival-System)
|
||||
|
||||
| Komponente | Richtwert |
|
||||
|--------------------------------------|------------------------|
|
||||
| Waermepumpe ~227 kW th., installiert | 100.000-140.000 EUR |
|
||||
| Waermetauscher Lade + Entlade | 4.000-6.000 EUR |
|
||||
| **Summe WP + WT** | **~105.000-145.000 EUR**|
|
||||
|
||||
Der Waermetauscher ist ein kleiner Posten — die Waermepumpe dominiert die
|
||||
Investition.
|
||||
|
||||
Bei einer Jahres-Waermeproduktion von ~260.000 kWh und Gestehungskosten von
|
||||
3,5 ct/kWh bleibt eine gute Marge gegenueber fossilen Brennstoffen (8-13 ct/kWh).
|
||||
|
||||
|
||||
## Quellen
|
||||
|
||||
- Bosch Grosswaermepumpe: https://www.bosch-homecomfort.com/de/de/wohngebaeude/wissen/heizungsratgeber/waermepumpe/grosswaermepumpe/
|
||||
- Ochsner High Capacity Heat Pumps: https://www.ochsner.com/en/ochsner-products/high-capacity-heat-pumps/
|
||||
- Ochsner Industriewaermepumpen (PDF): https://ochsner-energietechnik.com/wp-content/uploads/2019/08/Industriew%C3%A4rmepumpen_DE_2019_ET06-022019_kl.pdf
|
||||
- Viessmann Community - Spezifische Investitionskosten: https://community.viessmann.de/t5/Waermepumpe-Hybridsysteme/Spezifische-investitionskosten-von-Waermepumpen/td-p/125149
|
||||
- Satura: Geschraubter PHE 300 kW Edelstahl: https://satura-shop.com/geschraubter-Plattenwaermetauscher-aus-Edelstahl-300-kW-bei-70C
|
||||
- Inocal PHE 300 kW Fernwaerme: https://www.inocal.com/c/ola1-282/a/ola1-14841/INOCAL-Plattenwaermetauscher/Inocal-Edelstahl-Plattenwaermetauscher-PBU10-120-fuer-Fernwaerme-300-kW.html
|
||||
- Energie-Experten: WP-Kosten Kennzahlen: https://www.energie-experten.org/heizung/waermepumpe/waermepumpenheizung/kosten
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
# Recherche zu 100-kW-Wärmepumpen: Marktübersicht und Trends
|
||||
|
||||
Da der Markt für Großwärmepumpen stark herstellerspezifisch und oft modular aufgebaut ist, unterteilt man die verfügbaren kommerziellen Systeme am besten nach ihrer Wärmequelle und nennt etablierte Modellreihen namhafter Hersteller:
|
||||
|
||||
## 1. Wasser-Wasser- und Sole-Wasser-Wärmepumpen (Geothermie/Grundwasser/Abwärme)
|
||||
Diese Systeme nutzen das Grundwasser, das Erdreich (über Sonden) oder industrielle Abwärme. Sie sind in dieser Leistungsklasse extrem beliebt, da sie sehr kompakt gebaut werden können (Innenaufstellung) und das ganze Jahr über hohe, konstante Quelltemperaturen nutzen.
|
||||
|
||||
- **Viessmann Vitocal 300-G PRO / 350-G PRO**: Diese Baureihen sind speziell für Gewerbe und Kommunen konzipiert. Die Vitocal 350-G PRO liefert beispielsweise exakt Stufen um die 100 kW (z. B. Modell mit ca. 102 kW) und erreicht Vorlauftemperaturen bis zu 75 °C.
|
||||
|
||||
- **Daikin EWW(H)(L)T-Q**(Modularer Ansatz): Daikin setzt hier auf ein sehr flexibles Plug-and-Play-System. Das kleinste Grundmodul dieser Wasser-Wasser-Wärmepumpen-Serie hat exakt 100 kW Heizleistung. Benötigt man später mehr, lassen sich bis zu 8 Module zu einer Kaskade zusammenschalten.
|
||||
|
||||
- **Ochsner OSWP**(Industrial Baureihe): Ochsner ist bekannt für Großwärmepumpen im High-End-Bereich. Ihre Baureihen im mittleren zweistelligen bis dreistelligen kW-Bereich nutzen hocheffiziente Schrauben- oder Scrollverdichter und eignen sich hervorragend für Grundwasser- und Erdwärmenutzung.
|
||||
|
||||
## 2. Luft-Wasser-Großwärmepumpen (Außenaufstellung)
|
||||
|
||||
Luft-Wasser-Systeme in dieser Größenordnung saugen enorme Mengen an Außenluft an. Sie werden meist im Außenbereich (z. B. auf Flachdächern oder Freiflächen) aufgestellt. Oft arbeiten Hersteller hier mit Kaskaden (Zusammenschaltung mehrerer kleinerer Geräte) oder dedizierten Großmonoblöcken.
|
||||
|
||||
- **Stiebel Eltron WPL 57** (bzw. Kaskadenlösungen): Stiebel Eltron löst den Bedarf von 100 kW im Luft-Bereich häufig über intelligente Kaskadensteuerungen (z. B. zwei oder drei Geräte der größeren WPL-Serien parallel geschaltet). Das erhöht die Ausfallsicherheit, da bei Teillast nur ein Gerät laufen muss.
|
||||
|
||||
- **Wolf Heiztechnik CGB-2 / FHA** (Großgeräte-Portfolio): Wolf bietet im gewerblichen Bereich Luft-Wasser-Wärmepumpen an, die modular zusammengeschaltet werden, um Großobjekte präzise mit 100 kW oder mehr zu versorgen.
|
||||
|
||||
- **Bosch Compress 5000 AW**/ Kaskaden: Bosch nutzt für Industrie und große Wohnkomplexe kaskadierbare Systeme, die flexibel skaliert werden können.
|
||||
|
||||
## 3. Hochtemperatur-Wärmepumpen (Für Industrie & Fernwärme)
|
||||
Wenn die 100 kW Wärme nicht für eine normale Fußbodenheizung (ca. 35 °C) oder Heizkörper (ca. 55-65 °C) benötigt werden, sondern für industrielle Prozesse mit 80 °C bis über 100 °C Vorlauftemperatur, kommen Spezialgeräte zum Einsatz.
|
||||
|
||||
- **Viessmann Vitocal 350-HT PRO**: Diese Hochtemperatur-Wärmepumpe deckt einen Bereich von ca. 56 bis 350 kW ab und liefert Vorlauftemperaturen von bis zu 90 °C, ideal für die Nachrüstung in älteren Fernwärmenetzen oder der Industrie.
|
||||
|
||||
### Wichtige Trends bei 100-kW-Wärmepumpen
|
||||
|
||||
-**Natürliche Kältemittel**: Nahezu alle modernen Hersteller stellen ihre Großwärmepumpen auf zukunftssichere Kältemittel um. Besonders Propan (R290) ist bei Luft-Wasser-Wärmepumpen stark im Kommen, während im reinen Industriebereich auch CO₂ (R744) oder Ammoniak genutzt werden.
|
||||
|
||||
- **Kombiniertes Heizen und Kühlen** (4-Leiter-Systeme): Viele kommerzielle 100-kW-Anlagen (wie die von Daikin oder Carrier) können im Sommer das Gebäude kühlen und die dabei entzogene Wärme gleichzeitig für die Warmwasserbereitung nutzen, was die Effizienz (COP) drastisch nach oben treibt.
|
||||
|
||||
|
||||
Die Kosten für gewerbliche Wärmepumpen mit einer Leistung von ca. 100 kW hängen stark von der gewählten Technologie (Luft-Wasser, Sole-Wasser oder Kaskadensysteme), dem Hersteller und vor allem von der Komplexität der Installation ab.
|
||||
|
||||
Man unterscheidet grundsätzlich zwischen den **reinen Gerätepreisen** und den **Gesamtkosten** (inkl. Zubehör, Planung, hydraulischer Einbindung und Erschließung der Wärmequelle).
|
||||
|
||||
# Kosten
|
||||
## 1. Reine Anschaffungskosten (Nur das Gerät)
|
||||
|
||||
Die reinen Hardware-Preise für Monoblöcke oder industrielle Kompaktgeräte in der 100-kW-Klasse bewegen sich im Online-Handel und bei Großhändlern meist zwischen **25.000 € und 65.000 € nettotarifiert** (je nach Effizienzklasse, Kältemittel und Marke). Extrem günstige, oft asiatische oder im Direktvertrieb angebotene Industrie-Inverter starten teils schon ab ca. 15.000 €, während etablierte europäische Premiummarken im Projektgeschäft deutlich darüber liegen.
|
||||
|
||||
Beispiele für konkrete Marktpreise:
|
||||
|
||||
* **Mittelklasse-Monoblöcke (z. B. Era Master 100 kW mit R290):** Solche Großwärmepumpen, die bereits fertig für Gewerbeprojekte konzipiert sind, liegen als reines Gerät bei ca. **53.000 €**.
|
||||
* Einblick in die Produktdaten bietet [Bioheizer24](https://bioheizer24.de/products/grosswaermepumpe-eramaster-50-100-kw).
|
||||
|
||||
|
||||
* **Industrielle Kompakt-Wärmepumpen (z. B. Renocold HWE100):** Für Systeme, die flexibel für Heizung, Kühlung oder Prozesswärme eingesetzt werden, liegt der Listenpreis im Bereich von rund **38.500 € bis 45.000 €**.
|
||||
* Technische Daten und Anfragemöglichkeiten finden sich bei [Renocold](https://www.renocold.com/waermepumpe/hwa1ah/hen/hwe100).
|
||||
|
||||
|
||||
* **Premium-Hersteller (z. B. Viessmann Vitocal 300-G / 350-G PRO):** Die kleineren Brüder dieser Reihe (bis 85 kW) kosten im Online-Handel bereits um die 41.000 € bis 45.000 € (Netto-Gerätepreis). Für die echten PRO-Modelle mit 100 kW aufwärts werden individuelle Projektangebote erstellt, die für das reine Gerät meist bei **50.000 € bis 70.000 €** starten.
|
||||
* Eine Preisorientierung für die kleineren Stufen der Serie gibt es bei [Heizungsdiscount24](https://www.heizungsdiscount24.de/waermepumpen/viessmann/vitocal-300-g/).
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 2. Gesamtkosten inklusive Installation (Projektpreis)
|
||||
|
||||
Für die betriebsbereite Übergabe einer 100-kW-Anlage müssen Sie mit erheblichen Zusatzkosten rechnen. Als Faustformel gilt in der Branche, dass das Gerät oft nur **30 % bis 50 % der Gesamtinvestition** ausmacht.
|
||||
|
||||
| Kostenpunkt | Luft-Wasser-System (100 kW) | Sole/Wasser- bzw. Wasser/Wasser-System (100 kW) |
|
||||
| --- | --- | --- |
|
||||
| **Wärmepumpen-Gerät** | ca. 25.000 € – 55.000 € | ca. 35.000 € – 70.000 € |
|
||||
| **Erschließung der Quelle** | Gering (Fundamente, Luftkanäle) | Hoch (**ca. 30.000 € – 60.000 €** für Erdsonden-Bohrungen oder Brunnenbau) |
|
||||
| **Zubehör & Hydraulik** | ca. 10.000 € – 20.000 € (Großpufferspeicher, Pumpengruppen) | ca. 10.000 € – 20.000 € (Pufferspeicher, Trennwärmetauscher) |
|
||||
| **Montage & Einbindung** | ca. 15.000 € – 30.000 € | ca. 15.000 € – 30.000 € |
|
||||
| **Gesamtkosten (Richtwert)** | **ca. 50.000 € – 105.000 €** | **ca. 90.000 € – 180.000 €** |
|
||||
|
||||
> **Wichtiger Hinweis zu Förderungen:** In Deutschland und vielen EU-Ländern werden Großwärmepumpen für Unternehmen, Kommunen und Wohnungsbaugesellschaften (z. B. über die Bundesförderung für effiziente Gebäude – BEG oder die KfW) massiv bezuschusst. Je nach Kältemittel und Einsatzbereich können die effektiven Netto-Investitionskosten durch Förderquoten von **30 % bis zu 50 %** drastisch sinken.
|
||||
+217
-147
@@ -24,6 +24,17 @@ import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from report_md import report_md
|
||||
|
||||
def t(wert: float, nachkomma: int = 0) -> str:
|
||||
"""Zahl mit Hochkomma als Tausendertrenner formatieren."""
|
||||
if nachkomma > 0:
|
||||
s = f"{wert:,.{nachkomma}f}"
|
||||
else:
|
||||
s = f"{wert:,.0f}"
|
||||
return s.replace(",", "'")
|
||||
|
||||
|
||||
KJ_PER_KWH = 3600.0
|
||||
TAGE_PRO_JAHR = 365.0
|
||||
|
||||
@@ -124,9 +135,16 @@ def speicher(q_speicher_tag: float, cfg: dict, zyklus: float | None = None) -> d
|
||||
bindend = "Gewicht" if masse_kg / nutzlast >= volumen_m3 / lkw_vol else "Volumen"
|
||||
passt = True
|
||||
|
||||
# Wie oft muesste ein voller LKW (nach Gewicht) getauscht werden?
|
||||
# Tage bis LKW voll (je Grenze, bindende = kleinerer Wert)
|
||||
masse_pro_tag = q_speicher_tag / e_kg
|
||||
voll_lkw_tage = nutzlast / masse_pro_tag if masse_pro_tag > 0 else float("inf")
|
||||
volumen_pro_tag = masse_pro_tag / (p["dichte_kg_pro_L"] * 1000.0)
|
||||
voll_vol_tage = lkw_vol / volumen_pro_tag if volumen_pro_tag > 0 else float("inf")
|
||||
voll_bind_tage = min(voll_lkw_tage, voll_vol_tage)
|
||||
|
||||
# Empfehlung wenn bindende Auslastung < 90 %
|
||||
auslastung_bind = max(masse_kg / nutzlast, volumen_m3 / lkw_vol)
|
||||
empf_zyklus = math.ceil(0.90 * voll_bind_tage) if auslastung_bind < 0.90 else None
|
||||
|
||||
return {
|
||||
"energiedichte_kWh_pro_kg": e_kg,
|
||||
@@ -139,6 +157,7 @@ def speicher(q_speicher_tag: float, cfg: dict, zyklus: float | None = None) -> d
|
||||
"passt_in_einen_lkw": passt,
|
||||
"voller_lkw_tausch_tage": voll_lkw_tage,
|
||||
"voller_lkw_energie_kWh": nutzlast * e_kg,
|
||||
"empf_zyklus_tage": empf_zyklus,
|
||||
}
|
||||
|
||||
|
||||
@@ -241,7 +260,121 @@ def brennstoffkosten(e: dict, cfg: dict) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def rechne(milch_l_tag: float, 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, *, eh_da: bool = False, driver: bool = False) -> dict:
|
||||
"""Jaehrliche Betriebskosten des Anbieters -> Gestehungskosten und Marge.
|
||||
|
||||
eh_da=True : PV und Kuehlung bereits vorhanden -> Kapitalkosten dafuer entfallen.
|
||||
driver=True : Fahrer stellt der Landwirt selbst -> Fahreranteil wird abgezogen.
|
||||
Abschreibung: linear ohne Kapitalzins (konservativ).
|
||||
"""
|
||||
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"]
|
||||
|
||||
fahrer_abzug = a.get("fahrer_eur_pro_fuhre", 0.0) if driver else 0.0
|
||||
kosten_transport = fuhren_jahr * (a["transport_eur_pro_fuhre"] - fahrer_abzug)
|
||||
|
||||
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 = 0.0 if eh_da else 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 = 0.0 if eh_da else 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,
|
||||
"eh_da": eh_da,
|
||||
"driver": driver,
|
||||
"fahrer_abzug_eur_pro_fuhre": fahrer_abzug,
|
||||
}
|
||||
|
||||
|
||||
def rechne(milch_l_tag: float, cfg: dict, *, eh_da: bool = False, driver: bool = False) -> dict:
|
||||
q_milch = milch_kuehllast(milch_l_tag, cfg)
|
||||
wp = waermepumpe(q_milch, cfg)
|
||||
sp = speicher(wp["speicher_kWh_tag"], cfg)
|
||||
@@ -261,6 +394,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, eh_da=eh_da, driver=driver)
|
||||
return e
|
||||
|
||||
|
||||
@@ -325,9 +460,9 @@ def vorschlag_report(e: dict, cfg: dict) -> str:
|
||||
L.append(f"Abholzyklus .......... alle {v['empf_zyklus_tage']:.0f} Tage")
|
||||
L.append("")
|
||||
L.append(f" -> LKW dann zu {sp['auslastung_gewicht']*100:.0f} % ausgelastet "
|
||||
f"({sp['energie_pro_zyklus_kWh']:.0f} kWh je Fuhre)")
|
||||
f"({t(sp['energie_pro_zyklus_kWh'])} kWh je Fuhre)")
|
||||
L.append(f" -> ein voller LKW waere nach {v['voll_lkw_tage']:.0f} Tagen erreicht")
|
||||
L.append(f" -> Energieinhalt eines vollen LKW: ~{sp['voller_lkw_energie_kWh']:.0f} kWh")
|
||||
L.append(f" -> Energieinhalt eines vollen LKW: ~{t(sp['voller_lkw_energie_kWh'])} kWh")
|
||||
L.append("")
|
||||
L.append(f"Versorgte Haushalte (2 Erw. + 2 Kinder):")
|
||||
L.append(f" Warmwasser ganzjaehrig ... ~{hh['haushalte_warmwasser']:.0f} Haushalte")
|
||||
@@ -361,50 +496,50 @@ def report(e: dict, cfg: dict) -> str:
|
||||
L.append(f" Eingang: {e['eingang']['milch_l_tag']:.0f} L Milch/Tag"
|
||||
f" ({cfg['milch']['temp_ein_C']:.0f} -> {cfg['milch']['temp_aus_C']:.0f} C)")
|
||||
L.append("=" * 62)
|
||||
L.append(f"Kuehllast der Milch ........... {q:8.1f} kWh/Tag")
|
||||
L.append(f"Kuehllast der Milch ........... {t(q, 1):>8} kWh/Tag")
|
||||
L.append("")
|
||||
L.append("WAERMEPUMPE (COP {:.2f}, Laufzeit {:.0f} h/Tag)".format(
|
||||
cfg["waermepumpe"]["cop"], cfg["waermepumpe"]["laufzeit_h_pro_tag"]))
|
||||
L.append(f" Kaelteleistung (Verdampfer) . {wp['kaelteleistung_kW']:8.1f} kW")
|
||||
L.append(f" Heizleistung (Kondensator) .. {wp['heizleistung_kW']:8.1f} kW")
|
||||
L.append(f" el. Leistung ................ {wp['elektrische_leistung_kW']:8.1f} kW")
|
||||
L.append(f" Strombedarf ................. {wp['strom_kWh_tag']:8.1f} kWh/Tag")
|
||||
L.append(f" Waerme in den Speicher ...... {wp['speicher_kWh_tag']:8.1f} kWh/Tag")
|
||||
L.append(f" Kaelteleistung (Verdampfer) . {t(wp['kaelteleistung_kW'], 1):>8} kW")
|
||||
L.append(f" Heizleistung (Kondensator) .. {t(wp['heizleistung_kW'], 1):>8} kW")
|
||||
L.append(f" el. Leistung ................ {t(wp['elektrische_leistung_kW'], 1):>8} kW")
|
||||
L.append(f" Strombedarf ................. {t(wp['strom_kWh_tag'], 1):>8} kWh/Tag")
|
||||
L.append(f" Waerme in den Speicher ...... {t(wp['speicher_kWh_tag'], 1):>8} kWh/Tag")
|
||||
L.append("")
|
||||
L.append("PV-ANLAGE (Jahresbilanz)")
|
||||
L.append(f" Jahresstrombedarf WP ........ {pv['jahresbedarf_kWh']:8.0f} kWh/a")
|
||||
L.append(f" Leistung .................... {pv['kWp']:8.1f} kWp")
|
||||
L.append(f" Dachflaeche ................. {pv['flaeche_m2']:8.0f} m2")
|
||||
L.append(f" Jahresstrombedarf WP ........ {t(pv['jahresbedarf_kWh']):>8} kWh/a")
|
||||
L.append(f" Leistung .................... {t(pv['kWp'], 1):>8} kWp")
|
||||
L.append(f" Dachflaeche ................. {t(pv['flaeche_m2']):>8} m2")
|
||||
L.append("")
|
||||
L.append(f"NETZBEZUG (durch Wintermonate, Monatsbilanz)")
|
||||
L.append(f" Netzbezug ................... {netz['netzbezug_kWh']:8.0f} kWh/a"
|
||||
L.append(f" Netzbezug ................... {t(netz['netzbezug_kWh']):>8} kWh/a"
|
||||
f" = {netz['netzbezug_anteil']*100:.0f} %")
|
||||
L.append(f" Sommerueberschuss ........... {netz['ueberschuss_kWh']:8.0f} kWh/a")
|
||||
L.append(f" Sommerueberschuss ........... {t(netz['ueberschuss_kWh']):>8} kWh/a")
|
||||
L.append("")
|
||||
L.append("BATTERIE (Tag-/Nacht-Ueberbbrueckung)")
|
||||
L.append(f" nutzbare Kapazitaet ......... {bat['nutzbar_kWh']:8.1f} kWh")
|
||||
L.append(f" Nennkapazitaet .............. {bat['nominal_kWh']:8.1f} kWh")
|
||||
L.append(f" nutzbare Kapazitaet ......... {t(bat['nutzbar_kWh'], 1):>8} kWh")
|
||||
L.append(f" Nennkapazitaet .............. {t(bat['nominal_kWh'], 1):>8} kWh")
|
||||
L.append("")
|
||||
L.append("VERSORGTE HAUSHALTE ({} Erw. + {} Kinder)".format(
|
||||
cfg["haushalt"]["erwachsene"], cfg["haushalt"]["kinder"]))
|
||||
L.append(f" Warmwasser je Haushalt ...... {hh['ww_pro_haushalt_kWh_tag']:8.1f} kWh/Tag")
|
||||
L.append(f" -> Warmwasser ganzjaehrig ... {hh['haushalte_warmwasser']:8.1f} Haushalte")
|
||||
L.append(f" max. Heizbedarf Winter ...... {hh['heizbedarf_winter_kWh_tag']:8.1f} kWh/Tag/HH")
|
||||
L.append(f" Winterbedarf (Heiz+WW) ...... {hh['winter_pro_haushalt_kWh_tag']:8.1f} kWh/Tag/HH")
|
||||
L.append(f" -> im Winter ................ {hh['haushalte_winter']:8.1f} Haushalte")
|
||||
L.append(f" Warmwasser je Haushalt ...... {t(hh['ww_pro_haushalt_kWh_tag'], 1):>8} kWh/Tag")
|
||||
L.append(f" -> Warmwasser ganzjaehrig ... {t(hh['haushalte_warmwasser'], 1):>8} Haushalte")
|
||||
L.append(f" max. Heizbedarf Winter ...... {t(hh['heizbedarf_winter_kWh_tag'], 1):>8} kWh/Tag/HH")
|
||||
L.append(f" Winterbedarf (Heiz+WW) ...... {t(hh['winter_pro_haushalt_kWh_tag'], 1):>8} kWh/Tag/HH")
|
||||
L.append(f" -> im Winter ................ {t(hh['haushalte_winter'], 1):>8} Haushalte")
|
||||
L.append("")
|
||||
L.append(f"LATENTSPEICHER ({cfg['pcm']['name']}, Modus: {cfg['pcm']['modus']})")
|
||||
L.append(f" Energiedichte ............... {sp['energiedichte_kWh_pro_kg']*1000:8.1f} Wh/kg")
|
||||
L.append(f" Energie je Zyklus ({z:>2} Tage) . {sp['energie_pro_zyklus_kWh']:8.0f} kWh")
|
||||
L.append(f" Masse ....................... {sp['masse_kg']:8.0f} kg")
|
||||
L.append(f" Volumen ..................... {sp['volumen_m3']:8.1f} m3")
|
||||
L.append(f" LKW-Auslastung Gewicht ...... {sp['auslastung_gewicht']*100:8.0f} %")
|
||||
L.append(f" LKW-Auslastung Volumen ...... {sp['auslastung_volumen']*100:8.0f} %")
|
||||
L.append(f" Energiedichte ............... {t(sp['energiedichte_kWh_pro_kg']*1000, 1):>8} Wh/kg")
|
||||
L.append(f" Energie je Zyklus ({z:>2} Tage) . {t(sp['energie_pro_zyklus_kWh']):>8} kWh")
|
||||
L.append(f" Masse ....................... {t(sp['masse_kg']):>8} kg")
|
||||
L.append(f" Volumen ..................... {t(sp['volumen_m3'], 1):>8} m3")
|
||||
L.append(f" LKW-Auslastung Gewicht ...... {t(sp['auslastung_gewicht']*100):>8} %")
|
||||
L.append(f" LKW-Auslastung Volumen ...... {t(sp['auslastung_volumen']*100):>8} %")
|
||||
L.append(f" bindende Grenze ............. {sp['bindende_grenze']:>8}")
|
||||
passt = "ja" if sp["passt_in_einen_lkw"] else "NEIN -> mehrere Behaelter noetig"
|
||||
L.append(f" passt in einen LKW .......... {passt:>8}")
|
||||
L.append(f" voller LKW voll nach ........ {sp['voller_lkw_tausch_tage']:8.1f} Tagen")
|
||||
L.append(f" Energieinhalt voller LKW .... {sp['voller_lkw_energie_kWh']:8.0f} kWh")
|
||||
L.append(f" voller LKW voll nach ........ {t(sp['voller_lkw_tausch_tage'], 1):>8} Tagen")
|
||||
L.append(f" Energieinhalt voller LKW .... {t(sp['voller_lkw_energie_kWh']):>8} kWh")
|
||||
bk = e.get("brennstoffkosten")
|
||||
if bk and bk["vergleich"]:
|
||||
L.append("")
|
||||
@@ -412,126 +547,57 @@ def report(e: dict, cfg: dict) -> str:
|
||||
L.append(f" {'Brennstoff':<10} | {'ct/kWh':>7} | {'EUR/LKW':>9} | {'EUR/Jahr':>9}")
|
||||
L.append(f" {'-'*10}-+-{'-'*7}-+-{'-'*9}-+-{'-'*9}")
|
||||
for v in bk["vergleich"]:
|
||||
L.append(f" {v['name']:<10} | {v['nutzwaerme_ct_kWh']:>7.1f} | "
|
||||
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")
|
||||
L.append(f" {v['name']:<10} | {t(v['nutzwaerme_ct_kWh'], 1):>7} | "
|
||||
f"{t(v['eur_pro_lkw']):>9} | {t(v['eur_pro_jahr']):>9}")
|
||||
L.append(f" Bewertet: voller LKW ~{t(bk['energie_lkw_kWh'])} kWh, "
|
||||
f"Jahreserzeugung ~{t(bk['energie_jahr_kWh'])} 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" {t(ak['kosten_transport_eur']):>8} EUR/a")
|
||||
L.append(f" WP-Strom ({t(sd['netz_kWh'])} kWh Netz"
|
||||
f" @ {sd['netz_ct_kWh']:.0f} ct + {t(sd['pv_kWh'])} kWh PV"
|
||||
f" @ {sd['pv_ct_kWh']:.0f} ct) .. {t(ak['kosten_strom_eur']):>6} EUR/a")
|
||||
L.append(f" Wartung .......................... {t(ak['kosten_wartung_eur']):>8} EUR/a")
|
||||
L.append(f" Kapital PV ({pv['kWp']:.1f} kWp, {inv['pv_nutzungsdauer_jahre']} J) ......"
|
||||
f" {t(ak['kapital_pv_eur']):>8} EUR/a")
|
||||
L.append(f" Kapital Batterie ({bat['nominal_kWh']:.0f} kWh, {inv['batterie_nutzungsdauer_jahre']} J) ."
|
||||
f" {t(ak['kapital_batterie_eur']):>8} EUR/a")
|
||||
L.append(f" Kapital WP ({inv['waermepumpe_nutzungsdauer_jahre']} J) ............."
|
||||
f" {t(ak['kapital_waermepumpe_eur']):>8} EUR/a")
|
||||
L.append(f" Kapital Speicher ({ci['anzahl_module']} Module, {inv['speicher_nutzungsdauer_jahre']} J)"
|
||||
f" {t(ak['kapital_speicher_eur']):>8} EUR/a")
|
||||
L.append(f" {'-'*46}")
|
||||
L.append(f" Jahreskosten gesamt .............. {t(ak['jahreskosten_eur']):>8} EUR/a")
|
||||
L.append(f" Jahreserzeugung .................. {t(ak['energie_jahr_kWh']):>8} kWh/a")
|
||||
L.append(f" -> Mindestverkaufspreis .......... {t(ak['gestehungskosten_ct_kWh'], 1):>8} ct/kWh")
|
||||
L.append(f" Speicher-Invest ({ci['anzahl_module']} Module): {t(ci['gesamt_eur'])} EUR"
|
||||
f" = {t(ci['eur_pro_kwh_kapazitaet'])} EUR/kWh Kapazitaet")
|
||||
L.append(f" PCM {t(ci['pcm_eur'])} | Behaelter {t(ci['behaelter_eur'])}"
|
||||
f" | WT+Hydr. {t(ci['wt_hydraulik_eur'])}"
|
||||
f" | Elektr. {t(ci['elektronik_eur'])} | Montage {t(ci['montage_eur'])}")
|
||||
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 ""
|
||||
vorzeichen_ct = "+" if m['marge_ct_kWh'] >= 0 else ""
|
||||
vorzeichen_eur = "+" if m['marge_eur_jahr'] >= 0 else ""
|
||||
L.append(f" {m['name']:<10} | {t(m['nutzwaerme_ct_kWh'], 1):>7} |"
|
||||
f" {vorzeichen_ct}{t(m['marge_ct_kWh'], 1):>11} |"
|
||||
f" {vorzeichen_eur}{t(m['marge_eur_jahr']):>13}{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:
|
||||
@@ -551,6 +617,10 @@ def main() -> 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("--eh-da", action="store_true",
|
||||
help="PV und Kuehlung bereits vorhanden: Kapitalkosten dafuer entfallen")
|
||||
ap.add_argument("--driver", action="store_true",
|
||||
help="Fahrer stellt der Landwirt selbst: Fahreranteil aus Transportkosten abziehen")
|
||||
ap.add_argument("-v", "--verbose", action="store_true",
|
||||
help="Ausgaben an der Konsole anzeigen")
|
||||
args = ap.parse_args()
|
||||
@@ -581,7 +651,7 @@ def main() -> None:
|
||||
cfg = deep_merge(cfg, lade_config(f))
|
||||
quelldateien.append(f.name)
|
||||
|
||||
ergebnis = rechne(args.milch_l_tag, cfg)
|
||||
ergebnis = rechne(args.milch_l_tag, cfg, eh_da=args.eh_da, driver=args.driver)
|
||||
|
||||
if args.suggest_size:
|
||||
text = vorschlag_report(ergebnis, cfg)
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
"""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} m³",
|
||||
]))
|
||||
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} m³",
|
||||
]))
|
||||
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)
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Revival grosser PV-Anlagen: Strom als Waerme weiterverkaufen.
|
||||
|
||||
Berechnet fuer eine gegebene PV-Anlagengroesse (kWp), welche Waermemenge
|
||||
ueber eine Waermepumpe produziert werden kann und welche Leistung
|
||||
Waermepumpe und Waermetauscher haben muessen, um den taeglichen PV-Strom
|
||||
vollstaendig in Waerme umzuwandeln.
|
||||
|
||||
Aufruf:
|
||||
python revival.py 100
|
||||
python revival.py 250 --config cfg/config.json -v
|
||||
python revival.py 500 --sonnenstunden 5.5
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
def t(wert: float, nachkomma: int = 0) -> str:
|
||||
"""Zahl mit Hochkomma als Tausendertrenner formatieren."""
|
||||
if nachkomma > 0:
|
||||
s = f"{wert:,.{nachkomma}f}"
|
||||
else:
|
||||
s = f"{wert:,.0f}"
|
||||
return s.replace(",", "'")
|
||||
|
||||
|
||||
KJ_PER_KWH = 3600.0
|
||||
TAGE_PRO_JAHR = 365.0
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
|
||||
MONATSTAGE = {
|
||||
"Jan": 31, "Feb": 28, "Mae": 31, "Apr": 30, "Mai": 31, "Jun": 30,
|
||||
"Jul": 31, "Aug": 31, "Sep": 30, "Okt": 31, "Nov": 30, "Dez": 31,
|
||||
}
|
||||
|
||||
|
||||
def lade_config(pfad: Path) -> dict:
|
||||
with pfad.open(encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def monatsberechnung(kwp: float, cfg: dict) -> dict:
|
||||
"""Monatliche PV-Erzeugung, WP-Strom und Waermeproduktion (Luft + Wasser)."""
|
||||
pv = cfg["pv"]
|
||||
cop_luft = cfg["waermepumpe"]["cop"]
|
||||
cop_wasser = cfg["waermepumpe"].get("cop_wasser", 4.0)
|
||||
monats = pv["monatsertrag_kWh_pro_kWp"]
|
||||
detail = {}
|
||||
jahres_pv = 0.0
|
||||
jahres_waerme_luft = 0.0
|
||||
jahres_waerme_wasser = 0.0
|
||||
|
||||
for monat, tage in MONATSTAGE.items():
|
||||
pv_monat_kWh = monats[monat] * kwp
|
||||
pv_tag_kWh = pv_monat_kWh / tage
|
||||
wl_monat = pv_monat_kWh * cop_luft
|
||||
ww_monat = pv_monat_kWh * cop_wasser
|
||||
jahres_pv += pv_monat_kWh
|
||||
jahres_waerme_luft += wl_monat
|
||||
jahres_waerme_wasser += ww_monat
|
||||
detail[monat] = {
|
||||
"tage": tage,
|
||||
"pv_kWh": pv_monat_kWh,
|
||||
"pv_kWh_tag": pv_tag_kWh,
|
||||
"waerme_luft_kWh": wl_monat,
|
||||
"waerme_luft_kWh_tag": wl_monat / tage,
|
||||
"waerme_wasser_kWh": ww_monat,
|
||||
"waerme_wasser_kWh_tag": ww_monat / tage,
|
||||
}
|
||||
return {
|
||||
"monate": detail,
|
||||
"jahres_pv_kWh": jahres_pv,
|
||||
"jahres_waerme_luft_kWh": jahres_waerme_luft,
|
||||
"jahres_waerme_wasser_kWh": jahres_waerme_wasser,
|
||||
"cop_luft": cop_luft,
|
||||
"cop_wasser": cop_wasser,
|
||||
}
|
||||
|
||||
|
||||
def spitzenmonat(monate: dict) -> tuple[str, dict]:
|
||||
"""Monat mit der hoechsten taeglichen PV-Erzeugung."""
|
||||
return max(monate.items(), key=lambda x: x[1]["pv_kWh_tag"])
|
||||
|
||||
|
||||
def waermepumpe_auslegung(kwp: float, cfg: dict, sonnenstunden: float) -> dict:
|
||||
"""Erforderliche WP-Leistung um den PV-Strom tagsueberaufzunehmen.
|
||||
|
||||
sonnenstunden: aequivalente Volllaststunden Sonne pro Tag im Spitzenmonat.
|
||||
Die WP muss den gesamten Tagesstrom in diesen Stunden verarbeiten koennen.
|
||||
"""
|
||||
pv = cfg["pv"]
|
||||
cop = cfg["waermepumpe"]["cop"]
|
||||
|
||||
# Spitzenmonat: maximale taegliche Erzeugung
|
||||
monats = pv["monatsertrag_kWh_pro_kWp"]
|
||||
max_monat, max_ertrag = max(
|
||||
((m, monats[m] / MONATSTAGE[m]) for m in MONATSTAGE),
|
||||
key=lambda x: x[1],
|
||||
)
|
||||
pv_peak_tag_kWh = max_ertrag * kwp
|
||||
|
||||
# WP muss diese Energie in den Sonnenstunden aufnehmen
|
||||
wp_el_kW = pv_peak_tag_kWh / sonnenstunden
|
||||
wp_therm_kW = wp_el_kW * cop
|
||||
wp_kaelte_kW = wp_therm_kW - wp_el_kW # Verdampferseite
|
||||
|
||||
return {
|
||||
"spitzenmonat": max_monat,
|
||||
"pv_peak_tag_kWh": pv_peak_tag_kWh,
|
||||
"sonnenstunden": sonnenstunden,
|
||||
"wp_elektrisch_kW": wp_el_kW,
|
||||
"wp_thermisch_kW": wp_therm_kW,
|
||||
"wp_kaelte_kW": wp_kaelte_kW,
|
||||
}
|
||||
|
||||
|
||||
def waermetauscher_auslegung(wp: dict, sicherheitsfaktor: float = 1.2) -> dict:
|
||||
"""Erforderliche WT-Leistung auf Lade- und Entladeseite."""
|
||||
return {
|
||||
"ladeleistung_kW": wp["wp_thermisch_kW"] * sicherheitsfaktor,
|
||||
"entladeleistung_kW": wp["wp_thermisch_kW"] * sicherheitsfaktor,
|
||||
"sicherheitsfaktor": sicherheitsfaktor,
|
||||
"_hinweis": "Entladeseite kann kleiner sein wenn Entladung ueber laengere Zeit erfolgt",
|
||||
}
|
||||
|
||||
|
||||
def haushalte_versorgung(jahres_waerme_kWh: float, cfg: dict) -> dict:
|
||||
"""Wie viele Haushalte kann die Waerme versorgen?"""
|
||||
h = cfg["haushalt"]
|
||||
ww_tag = h["erwachsene"] * h["ww_erwachsener_kWh_tag"] + h["kinder"] * h["ww_kind_kWh_tag"]
|
||||
winter_tag = ww_tag + h["heizbedarf_winter_kWh_tag"]
|
||||
waerme_tag_schnitt = jahres_waerme_kWh / TAGE_PRO_JAHR
|
||||
return {
|
||||
"waerme_tag_schnitt_kWh": waerme_tag_schnitt,
|
||||
"ww_pro_haushalt_kWh_tag": ww_tag,
|
||||
"winter_pro_haushalt_kWh_tag": winter_tag,
|
||||
"haushalte_warmwasser": waerme_tag_schnitt / ww_tag if ww_tag > 0 else 0.0,
|
||||
"haushalte_winter": waerme_tag_schnitt / winter_tag if winter_tag > 0 else 0.0,
|
||||
}
|
||||
|
||||
|
||||
def wirtschaftlichkeit(kwp: float, jahres_pv_kWh: float,
|
||||
jahres_waerme_kWh: float, cfg: dict) -> dict:
|
||||
"""Erloes der Waerme im Vergleich zu fossilen Brennstoffen und Stromverkauf."""
|
||||
vergleich = []
|
||||
for b in cfg.get("brennstoffe", []):
|
||||
nw_ct = b["preis_ct_kWh"] / b["wirkungsgrad"]
|
||||
erloes_jahr = jahres_waerme_kWh * nw_ct / 100.0
|
||||
vergleich.append({
|
||||
"name": b["name"],
|
||||
"nutzwaerme_ct_kWh": nw_ct,
|
||||
"erloes_jahr_eur": erloes_jahr,
|
||||
})
|
||||
# PV-Gestehungskosten als Vergleich
|
||||
pv_gestehung = cfg.get("anbieter", {}).get("pv_gestehung_ct_kWh", 9.0)
|
||||
cop = cfg["waermepumpe"]["cop"]
|
||||
waerme_gestehung_ct = pv_gestehung / cop
|
||||
|
||||
# Stromverkauf als Alternative (Einspeisung / Direktvermarktung)
|
||||
pv_cfg = cfg.get("pv", {})
|
||||
einspeisung_ct = pv_cfg.get("einspeiseverguetung_ct_kWh", 8.1)
|
||||
direktvermarktung_ct = pv_cfg.get("direktvermarktung_ct_kWh", 7.0)
|
||||
erloes_einspeisung = jahres_pv_kWh * einspeisung_ct / 100.0
|
||||
erloes_direktvermarktung = jahres_pv_kWh * direktvermarktung_ct / 100.0
|
||||
|
||||
return {
|
||||
"pv_gestehung_ct_kWh": pv_gestehung,
|
||||
"waerme_gestehung_ct_kWh": waerme_gestehung_ct,
|
||||
"vergleich": vergleich,
|
||||
"stromverkauf": {
|
||||
"jahres_pv_kWh": jahres_pv_kWh,
|
||||
"einspeiseverguetung_ct_kWh": einspeisung_ct,
|
||||
"direktvermarktung_ct_kWh": direktvermarktung_ct,
|
||||
"erloes_einspeisung_eur": erloes_einspeisung,
|
||||
"erloes_direktvermarktung_eur": erloes_direktvermarktung,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def rechne(kwp: float, cfg: dict, sonnenstunden: float = 5.0) -> dict:
|
||||
mon = monatsberechnung(kwp, cfg)
|
||||
wp = waermepumpe_auslegung(kwp, cfg, sonnenstunden)
|
||||
wt = waermetauscher_auslegung(wp)
|
||||
hh = haushalte_versorgung(mon["jahres_waerme_luft_kWh"], cfg)
|
||||
wi = wirtschaftlichkeit(kwp, mon["jahres_pv_kWh"], mon["jahres_waerme_luft_kWh"], cfg)
|
||||
return {
|
||||
"eingang": {"kwp": kwp, "sonnenstunden": sonnenstunden},
|
||||
"cop_luft": mon["cop_luft"],
|
||||
"cop_wasser": mon["cop_wasser"],
|
||||
"monatsberechnung": mon,
|
||||
"waermepumpe": wp,
|
||||
"waermetauscher": wt,
|
||||
"haushalte": hh,
|
||||
"wirtschaftlichkeit": wi,
|
||||
}
|
||||
|
||||
|
||||
def report(e: dict, cfg: dict) -> str:
|
||||
wp = e["waermepumpe"]
|
||||
wt = e["waermetauscher"]
|
||||
mon = e["monatsberechnung"]
|
||||
hh = e["haushalte"]
|
||||
wi = e["wirtschaftlichkeit"]
|
||||
kwp = e["eingang"]["kwp"]
|
||||
cop_l = e["cop_luft"]
|
||||
cop_w = e["cop_wasser"]
|
||||
L = []
|
||||
|
||||
L.append(f"# PV-REVIVAL: {kwp:.0f} kWp Anlage -> Waermeproduktion")
|
||||
L.append("")
|
||||
L.append(f"Luft-WP COP {cop_l:.1f} / Wasser-WP COP {cop_w:.1f}, "
|
||||
f"{wp['sonnenstunden']:.1f} h aequiv. Sonnenstunden/Tag")
|
||||
|
||||
L.append("")
|
||||
L.append("## Jaehrliche Erzeugung")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert | Einheit |")
|
||||
L.append("|:---|---:|:---|")
|
||||
L.append(f"| PV-Strom | {t(mon['jahres_pv_kWh'])} | kWh/a |")
|
||||
L.append(f"| Waerme Luft-WP (COP {cop_l:.1f}) | {t(mon['jahres_waerme_luft_kWh'])} | kWh/a |")
|
||||
L.append(f"| Waerme Wasser-WP (COP {cop_w:.1f}) | {t(mon['jahres_waerme_wasser_kWh'])} | kWh/a |")
|
||||
L.append(f"| Waerme Tagesmittel (Luft) | {t(hh['waerme_tag_schnitt_kWh'], 1)} | kWh/Tag |")
|
||||
|
||||
L.append("")
|
||||
L.append("## Erforderliche Waermepumpe")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert | Einheit |")
|
||||
L.append("|:---|---:|:---|")
|
||||
L.append(f"| Spitzenmonat | {wp['spitzenmonat']} | |")
|
||||
L.append(f"| PV-Erzeugung Spitzentag | {t(wp['pv_peak_tag_kWh'], 1)} | kWh/Tag |")
|
||||
L.append(f"| Elektr. Leistung WP | {t(wp['wp_elektrisch_kW'], 1)} | kW |")
|
||||
L.append(f"| Thermische Leistung WP | {t(wp['wp_thermisch_kW'], 1)} | kW |")
|
||||
L.append(f"| Kaelteleistung (Verdampfer) | {t(wp['wp_kaelte_kW'], 1)} | kW |")
|
||||
|
||||
L.append("")
|
||||
L.append(f"## Erforderlicher Waermetauscher (Sicherheitsfaktor {wt['sicherheitsfaktor']:.1f}x)")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert | Einheit |")
|
||||
L.append("|:---|---:|:---|")
|
||||
L.append(f"| Ladeleistung | {t(wt['ladeleistung_kW'], 1)} | kW |")
|
||||
L.append(f"| Entladeleistung | {t(wt['entladeleistung_kW'], 1)} | kW |")
|
||||
|
||||
L.append("")
|
||||
L.append("## Monatsuebersicht")
|
||||
L.append("")
|
||||
L.append(f"| Monat | PV kWh | kWh/Tag | Luft kWh (COP {cop_l:.1f}) | Wasser kWh (COP {cop_w:.1f}) |")
|
||||
L.append("|:---|---:|---:|---:|---:|")
|
||||
for monat in MONATSTAGE:
|
||||
m = mon["monate"][monat]
|
||||
L.append(f"| {monat} | {t(m['pv_kWh'])} | {t(m['pv_kWh_tag'], 1)} |"
|
||||
f" {t(m['waerme_luft_kWh'])} | {t(m['waerme_wasser_kWh'])} |")
|
||||
L.append(f"| **Summe** | **{t(mon['jahres_pv_kWh'])}** | |"
|
||||
f" **{t(mon['jahres_waerme_luft_kWh'])}** |"
|
||||
f" **{t(mon['jahres_waerme_wasser_kWh'])}** |")
|
||||
|
||||
# Mermaid-Diagramm
|
||||
L.append("")
|
||||
L.append("## Diagramm: Strom- und Waermeproduktion pro Monat")
|
||||
L.append("")
|
||||
L.append("```mermaid")
|
||||
L.append("%%{init: {'theme': 'base', 'themeVariables': {"
|
||||
"'xyChart': {'plotColorPalette': '#f0c808, #5bc0eb, #1d3557'}"
|
||||
"}}}%%")
|
||||
L.append("xychart-beta")
|
||||
L.append(f" title \"PV-Revival {kwp:.0f} kWp - Monatsproduktion (MWh)\"")
|
||||
L.append(" x-axis [Jan, Feb, Mae, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]")
|
||||
# Werte in MWh fuer bessere Lesbarkeit
|
||||
pv_vals = [f"{mon['monate'][m]['pv_kWh']/1000:.1f}" for m in MONATSTAGE]
|
||||
luft_vals = [f"{mon['monate'][m]['waerme_luft_kWh']/1000:.1f}" for m in MONATSTAGE]
|
||||
wasser_vals = [f"{mon['monate'][m]['waerme_wasser_kWh']/1000:.1f}" for m in MONATSTAGE]
|
||||
L.append(f" y-axis \"MWh\"")
|
||||
L.append(f" bar [{', '.join(pv_vals)}]")
|
||||
L.append(f" line [{', '.join(luft_vals)}]")
|
||||
L.append(f" line [{', '.join(wasser_vals)}]")
|
||||
L.append("```")
|
||||
L.append("")
|
||||
L.append(f"*Balken: PV-Strom | Linie 1: Waerme Luft-WP (COP {cop_l:.1f}) | "
|
||||
f"Linie 2: Waerme Wasser-WP (COP {cop_w:.1f}) -- Werte in MWh*")
|
||||
|
||||
L.append("")
|
||||
L.append(f"## Versorgbare Haushalte ({cfg['haushalt']['erwachsene']} Erw. + "
|
||||
f"{cfg['haushalt']['kinder']} Kinder, Tagesmittel)")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert |")
|
||||
L.append("|:---|---:|")
|
||||
L.append(f"| Warmwasser ganzjaehrig | {t(hh['haushalte_warmwasser'])} Haushalte |")
|
||||
L.append(f"| Winter (Heizung + WW) | {t(hh['haushalte_winter'], 1)} Haushalte |")
|
||||
|
||||
if wi["vergleich"]:
|
||||
L.append("")
|
||||
L.append("## Wirtschaftlichkeit")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert | Einheit |")
|
||||
L.append("|:---|---:|:---|")
|
||||
L.append(f"| PV-Stromgestehung | {t(wi['pv_gestehung_ct_kWh'], 1)} | ct/kWh |")
|
||||
L.append(f"| Waermegestehung (/ COP) | {t(wi['waerme_gestehung_ct_kWh'], 1)} | ct/kWh |")
|
||||
L.append("")
|
||||
L.append("Zum Vergleich: Kosten der anderen Brennstoffe (Nutzwaerme-Preis):")
|
||||
L.append("")
|
||||
L.append("| Brennstoff | ct/kWh | Erloes EUR/a |")
|
||||
L.append("|:---|---:|---:|")
|
||||
for v in wi["vergleich"]:
|
||||
L.append(f"| {v['name']} | {t(v['nutzwaerme_ct_kWh'], 1)} | {t(v['erloes_jahr_eur'])} |")
|
||||
L.append("")
|
||||
L.append("*Erloes = Waerme bewertet zu Ersatzbrennstoffkosten*")
|
||||
|
||||
sv = wi["stromverkauf"]
|
||||
L.append("")
|
||||
L.append("### Alternative: Strom direkt verkaufen")
|
||||
L.append("")
|
||||
L.append("| Variante | ct/kWh | Erloes EUR/a |")
|
||||
L.append("|:---|---:|---:|")
|
||||
L.append(f"| Einspeiseverguetung (EEG) | {t(sv['einspeiseverguetung_ct_kWh'], 1)} |"
|
||||
f" {t(sv['erloes_einspeisung_eur'])} |")
|
||||
L.append(f"| Direktvermarktung | {t(sv['direktvermarktung_ct_kWh'], 1)} |"
|
||||
f" {t(sv['erloes_direktvermarktung_eur'])} |")
|
||||
L.append("")
|
||||
# Vergleich: bester Waerme-Erloes vs. Stromverkauf
|
||||
best_waerme = max(wi["vergleich"], key=lambda v: v["erloes_jahr_eur"])
|
||||
vorteil = best_waerme["erloes_jahr_eur"] - sv["erloes_einspeisung_eur"]
|
||||
if vorteil > 0:
|
||||
L.append(f"*Waermeverkauf (vs. {best_waerme['name']}) bringt "
|
||||
f"{t(vorteil)} EUR/a mehr als Einspeisung.*")
|
||||
else:
|
||||
L.append(f"*Einspeisung bringt {t(-vorteil)} EUR/a mehr als "
|
||||
f"Waermeverkauf (vs. {best_waerme['name']}).*")
|
||||
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("kwp", type=float, help="Groesse der PV-Anlage in kWp")
|
||||
ap.add_argument("--config", type=Path,
|
||||
default=PROJECT_ROOT / "cfg" / "config.json",
|
||||
help="Konfiguration (Standard: cfg/config.json)")
|
||||
ap.add_argument("--sonnenstunden", type=float, default=5.0,
|
||||
help="Aequivalente Volllaststunden Sonne pro Tag im Spitzenmonat "
|
||||
"(Standard: 5.0 h, bestimmt die noetige WP-Spitzenleistung)")
|
||||
ap.add_argument("--out", type=Path, default=None,
|
||||
help="Ausgabeverzeichnis (Standard: $MILCH_OUT oder out/)")
|
||||
ap.add_argument("--json", type=Path, default=None,
|
||||
help="Ergebnis zusaetzlich als JSON schreiben")
|
||||
ap.add_argument("-v", "--verbose", action="store_true",
|
||||
help="Ausgaben auf der Konsole")
|
||||
args = ap.parse_args()
|
||||
|
||||
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)
|
||||
|
||||
cfg = lade_config(args.config)
|
||||
ergebnis = rechne(args.kwp, cfg, sonnenstunden=args.sonnenstunden)
|
||||
text = report(ergebnis, cfg)
|
||||
|
||||
if args.verbose:
|
||||
print(text)
|
||||
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
md_name = f"revival_{args.kwp:.0f}kWp_{ts}.md"
|
||||
md_path = out_ordner / md_name
|
||||
md_path.write_text(text, encoding="utf-8")
|
||||
if args.verbose:
|
||||
print(f"\nReport 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)
|
||||
if args.verbose:
|
||||
print(f"JSON geschrieben: {args.json}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user