Compare commits

...

12 Commits

Author SHA1 Message Date
Michael Stangl 160a0f5f7b Add CLAUDE.md for Claude Code guidance
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 23:20:24 +02:00
Michael Stangl fc6c2abbb1 Recherche zur 100KW WP 2026-06-15 11:29:19 +02:00
Michael Stangl 6c4f007c3f Recherche für WP und Tauscher dazu 2026-06-15 11:28:57 +02:00
Michael Stangl f392239eb0 Tausender Zahlen mit Einfachhochkomma getrennt. Komma ist verwirrend. COP für Luft und Wasser WP 2026-06-15 11:28:31 +02:00
Michael Stangl 6d3d7bf55c revival zur Nutzung von Wärme für grosse PV Anlagen 2026-06-15 11:27:14 +02:00
Michael Stangl 0a3972a9b4 get_cmd skripte dazu 2026-06-15 09:55:07 +02:00
Michael Stangl 3f59877aa9 schalter --eh-da und --driver dazu 2026-06-15 07:06:48 +02:00
Michael Stangl 06ead41035 .md Report erzeugung in eigene Routinen 2026-06-14 22:45:48 +02:00
Michael Stangl d6e34b5e7c Abschätzung für Containerkosten rein 2026-06-14 21:31:20 +02:00
Michael Stangl 3276ec8d86 Vergleich mit Brennstoffkosten dazu 2026-06-14 09:34:15 +02:00
Michael Stangl 07d7a1191b Readme dazu 2026-06-14 09:34:02 +02:00
Michael Stangl 0748b63a58 Doku dazu 2026-06-14 09:13:30 +02:00
14 changed files with 1867 additions and 135 deletions
+47
View File
@@ -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.
+120
View File
@@ -0,0 +1,120 @@
# Auslegung Wärmerückgewinnung Milchkühlung
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 sich Varianten ohne Codeänderung nachrechnen lassen.
## Konzept
```
Milchabwärme (37→4 °C) ──► Wärmepumpe (Strom aus PV) ──► Latentspeicher (Natriumacetat, 58 °C) ──► Abholung per LKW
```
Da Natriumacetat-Trihydrat erst bei 58 °C schmilzt, die Milchabwärme aber nur bei ~37 °C anfällt, hebt eine Wärmepumpe das Temperaturniveau an. Der Speicher kann unterkühlt und damit isolierungsfrei transportiert werden.
## Voraussetzungen
- Python 3.8+
- Keine externen Abhängigkeiten (nur Standardbibliothek)
## Dateien
| Datei | Zweck |
|---|---|
| `auslegung.py` | Berechnungsprogramm |
| `config.json` | Sämtliche Annahmen (kopieren für Varianten) |
## Verwendung
```bash
# Detailreport für 1000 L Milch/Tag
python auslegung.py 1000
# Kompakte Empfehlung (gerundete Größen + vorgeschlagener Abholzyklus)
python auslegung.py 1000 --suggest-size
# Variante mit eigener Config und Ergebnis-Export als JSON
python auslegung.py 1500 --config meine_variante.json --json ergebnis.json
```
| Argument | Beschreibung |
|---|---|
| `milch_l_tag` | (Pflicht) Milchmenge pro Tag in Litern |
| `--config PFAD` | Pfad zur JSON-Konfiguration (Standard: `config.json`) |
| `--json PFAD` | Ergebnis zusätzlich als JSON schreiben |
| `--suggest-size` | Kompakte Empfehlung statt Detailreport |
## Was berechnet wird
- **Milchkühllast** (kWh/Tag) aus Menge und Temperaturhub
- **Wärmepumpe**: Kälte-, Heiz- und elektrische Leistung, Strombedarf, in den Speicher gelieferte Wärme
- **PV-Anlage**: Leistung (kWp) und Dachfläche für die Jahresbilanz des WP-Strombedarfs
- **Netzbezug**: jährlicher Zukauf-Anteil durch die Wintermonate (Monatsbilanz)
- **Batterie**: nutzbare und Nennkapazität für den Tag-/Nacht-Ausgleich
- **Latentspeicher**: Masse, Volumen, LKW-Auslastung, Energieinhalt einer Ladung
- **Versorgte Haushalte**: Warmwasser (ganzjährig) und Winterfall (Heizung + WW)
- **Brennstoffkosten**: Wert der Wärme als Ersatzkosten für Gas, Heizöl, Pellets
## Konfiguration (`config.json`)
| Abschnitt | Wichtigste Parameter |
|---|---|
| `milch` | Ein-/Austrittstemperatur, Dichte, spez. Wärme |
| `waermepumpe` | `cop`, `laufzeit_h_pro_tag` |
| `pcm` | `modus` (`latent_only` / `latent_plus_hub`), Latentwärme, Dichte, c-Werte, Lade-/Entladetemperatur |
| `abholung` | `zyklus_tage`, LKW-Nutzlast und -Volumen |
| `pv` | spez. Jahresertrag, Fläche je kWp, Monatsprofil |
| `batterie` | Direkt-PV-Stunden, nutzbarer Anteil, Reservefaktor |
| `haushalt` | Personen, Warmwasser je Person, Winter-Heizbedarf |
| `brennstoffe` | Preis (ct/kWh) und Wirkungsgrad für Gas/Öl/Pellets |
### Wichtigste Stellschrauben
- `pcm.modus` `latent_only` (nur Phasenwechsel) oder `latent_plus_hub` (mit Überhitzung): bestimmt Energiedichte und Speichergröße.
- `abholung.zyklus_tage` Abholrhythmus, steuert die Speichergröße direkt.
- `waermepumpe.cop` bestimmt Strombedarf und damit PV-Größe.
- `haushalt.heizbedarf_winter_kWh_tag` stark gebäudeabhängig (saniert ~40, Altbau ~100, 0 = nur Warmwasser).
- `brennstoffe` Preise sind Stand Juni 2026 (DE) und jederzeit überschreibbar.
## Ausgabe
Der Detailreport schreibt einen formatierten Konsolentext mit allen Blöcken. Mit `--json` wird das vollständige Ergebnis zusätzlich als strukturiertes JSON gespeichert (alle Zwischengrößen, im Suggest-Modus zusätzlich der `vorschlag`-Block).
### Beispiel (gekürzt, 1000 L)
```
WAERMEPUMPE (COP 2.60, Laufzeit 18 h/Tag)
Strombedarf ................. 24.0 kWh/Tag
Waerme in den Speicher ...... 62.4 kWh/Tag
PV-ANLAGE
Leistung .................... 8.8 kWp
Dachflaeche ................. 44 m2
NETZBEZUG (durch Wintermonate)
Netzbezug ................... 1941 kWh/a = 22 %
VERSORGTE HAUSHALTE (2 Erw. + 2 Kinder)
-> Warmwasser ganzjaehrig ... 11.1 Haushalte
-> im Winter ................ 1.0 Haushalte
LATENTSPEICHER (Natriumacetat-Trihydrat, Modus: latent_plus_hub)
Energieinhalt voller LKW .... 2185 kWh
WERT DER WAERME BEIM KAEUFER
Gas | 11.6 ct/kWh | 253 EUR/LKW | 2635 EUR/Jahr
Pellets | 8.0 ct/kWh | 174 EUR/LKW | 1810 EUR/Jahr
```
## Modell-Annahmen & Vereinfachungen
- Milch wird thermisch als Wasser gerechnet.
- Der zurückkehrende Speicherbehälter wird als „warm" (nahe Schmelzpunkt) angenommen; sonst müsste die WP ihn zusätzlich aufheizen.
- Der Netzbezug ist eine **Monatsbilanz** und setzt einen Tag-/Nacht-Ausgleich per Batterie voraus. Ohne Batterie liegt der reale Zukauf höher (~5060 %).
- `--suggest-size` optimiert den Abholzyklus auf **Transportauslastung**, nicht auf die Verweildauer der Wärme im Speicher.
- Die Brennstoffkosten sind der **Brutto-Substitutionswert** der Wärme, kein erzielbarer Verkaufspreis (Transport, Strom, Abschreibung noch nicht abgezogen).
## Mögliche Erweiterungen
- Anbieterseite: Transportkosten, WP-Stromkosten und Speicher-Abschreibung → echte Marge bzw. Mindestverkaufspreis je kWh.
- Monatlich aufgelöste PV-Deckung als Diagramm.
+7
View File
@@ -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%"
+11
View File
@@ -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"
+12
View File
@@ -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%" %*
+150 -7
View File
@@ -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": {
@@ -61,10 +64,150 @@
"_kommentar": "Warmwasser je Person/Tag. heizbedarf_winter = max. taeglicher Raumheizbedarf eines Haushalts im tiefsten Winter, stark gebaeudeabhaengig (Neubau/saniert ~40, Altbau ~100). Auf 0 setzen, wenn nur Warmwasser betrachtet werden soll."
},
"brennstoffe": {
"gas": {"preis_ct_pro_kWh": 11.0, "wirkungsgrad": 0.95},
"oel": {"preis_ct_pro_kWh": 11.5, "wirkungsgrad": 0.90},
"pellets": {"preis_ct_pro_kWh": 7.0, "wirkungsgrad": 0.88},
"_kommentar": "Vergleichswert: was der Kaeufer fuer dieselbe Nutzwaerme aus fossilen/Holz-Brennstoffen zahlen wuerde. preis_ct_pro_kWh = Brennstoffpreis je kWh Brennstoffenergie (Stand Juni 2026, Deutschland: Gas ~11, Oel ~11.5 (=115 ct/L bei 10 kWh/L), Pellets ~7 (=350 EUR/t bei 5 kWh/kg)). wirkungsgrad = Kesselwirkungsgrad. Nutzwaermekosten = preis/wirkungsgrad."
"brennstoffe": [
{
"name": "Gas",
"preis_ct_kWh": 11.0,
"wirkungsgrad": 0.95,
"_kommentar": "Erdgas Juni 2026, Brennwertkessel."
},
{
"name": "Heizoel",
"preis_ct_kWh": 11.5,
"wirkungsgrad": 0.90,
"_kommentar": "Heizoel EL Juni 2026 (~115 ct/L bei 10 kWh/L), Brennwertkessel."
},
{
"name": "Pellets",
"preis_ct_kWh": 7.0,
"wirkungsgrad": 0.88,
"_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.",
"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."
}
}
}
+88
View File
@@ -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
+85
View File
@@ -0,0 +1,85 @@
# 1 Energiemengen & Thermodynamik
Grundlagen-Zusammenfassung zur Wärmerückgewinnung aus der Milchkühlung. Referenzfall durchgängig: **1000 L Milch/Tag**.
---
## 1.1 Wärmeinhalt der Milchkühlung
Milch fällt mit Eutertemperatur an und muss zur Haltbarmachung gekühlt werden. Die dabei entzogene Wärme ist die nutzbare Abwärme.
| Größe | Wert |
|---|---|
| Temperaturhub | 37 °C → 4 °C (ΔT = 33 K) |
| Stoffannahme | Milch ≈ Wasser (ρ = 1 kg/L, c = 4,186 kJ/kg·K) |
| Formel | Q = m · c · ΔT |
**Ergebnis:** Q = 1000 · 4,186 · 33 = 138.138 kJ ≈ **38,4 kWh/Tag**
Skalierung: 1500 L → ~57,5 kWh/Tag. Die Abwärme fällt **täglich ganzjährig** an (~14.000 kWh/a).
---
## 1.2 Reinigungs-/Desinfektionswasser
Das Leitungssystem (~100 m, mehrere Pumpvorgänge für ~8 Melkplätze) muss mit heißem Wasser desinfiziert werden.
| Größe | Wert |
|---|---|
| Menge | 200 L |
| Temperaturhub | 50 °C → 100 °C (ΔT = 50 K) |
| Q | 200 · 4,186 · 50 = 41.860 kJ ≈ **11,6 kWh** |
Weil der Tank durch die Milchabwärme bereits bei 50 °C startet, halbiert sich der Heizbedarf gegenüber Kaltwasser (10 °C → ~21 kWh). Die Milchabwärme deckt den Desinfektionsbedarf damit vollständig.
> Noch nicht enthalten: Rohrleitungsverluste (je nach Dämmung +1030 %). Dafür wären Rohrdurchmesser und Dämmung nötig.
---
## 1.3 PV-Panel-Kühlung / PVT-Ertrag
Mit rückseitigen Wassertaschen (PVT) lässt sich die Abwärme der Module nutzen.
**Energiebilanz Modul (Sommerspitze, ~1000 W/m² Einstrahlung):**
- ~1820 % Strom (~180200 W/m²)
- ~800 W/m² Wärme, davon über die Frontseite ein großer Teil verloren
- Unverglaster PVT, niedrige Vorlauftemperatur: **~400500 W/m² thermisch nutzbar**
**Monatlicher Wärmeertrag, 10 m², optimale Südneigung, Raum Augsburg:**
| Monat | kWh | Monat | kWh |
|---|---|---|---|
| Jan | 75 | Jul | 825 |
| Feb | 145 | Aug | 730 |
| Mär | 350 | Sep | 510 |
| Apr | 565 | Okt | 270 |
| Mai | 745 | Nov | 90 |
| Jun | 800 | Dez | 55 |
**Summe ≈ 5.160 kWh/a thermisch** (~516 kWh/m²·a, oberer realistischer Bereich bei ganzjähriger Abnahme) plus ~1.5001.800 kWh/a Strom.
> Vorbehalt: Werte gelten nur bei kaltem Kühlwasser. Steigt die Vorlauftemperatur oder fehlt der Abnehmer, brechen die Sommerwerte ein.
---
## 1.4 Natriumacetat als Latentwärmespeicher (PCM)
### Speichermechanismus
Im **festen** Zustand liegt Natriumacetat-Trihydrat (CH₃COONa·3H₂O) als geordnetes Kristallgitter mit eingebautem Kristallwasser vor. Beim Erwärmen auf ~58 °C bricht das Gitter auf das Salz „löst sich in seinem eigenen Kristallwasser". Die Energie geht in das Aufbrechen der Bindungen statt in Temperaturerhöhung → **latente Wärme**.
Der Schlüssel ist die **Unterkühlbarkeit**: Die Schmelze bleibt bis auf Raumtemperatur metastabil flüssig und hält die Energie nahezu verlustfrei. Erst ein Auslöser (Impfkristall, Erschütterung) startet die Kristallisation und gibt die Wärme schlagartig bei ~58 °C frei. Damit lässt sich der Speicher **isolierungsfrei lagern und transportieren**.
### Energiedichte
| Modus | Beitrag | Energiedichte |
|---|---|---|
| `latent_only` | Phasenwechsel 264 kJ/kg | **0,073 kWh/kg** (~0,095 kWh/L) |
| `latent_plus_hub` | + Überhitzung der Schmelze (z. B. auf 80 °C) | **0,091 kWh/kg** (~0,12 kWh/L) |
| + Unterkühlung Feststoff | bis ~40 °C nutzen | ~0,10 kWh/kg (~0,13 kWh/L) |
Stoffwerte: Dichte ~1,3 kg/L, c_flüssig ≈ 2,9 kJ/kg·K, c_fest ≈ 1,9 kJ/kg·K. Zum Vergleich Wasser (50 K Hub): nur ~0,058 kWh/L.
### Kritischer Punkt: Temperaturniveau
Natriumacetat schmilzt bei **58 °C**, die Milchabwärme fällt aber nur bei max. **37 °C** an. Eine 37-°C-Quelle kann einen 58-°C-Speicher **nicht direkt laden** → es braucht eine **Wärmepumpe** zur Temperaturanhebung (siehe Dokument 2).
+95
View File
@@ -0,0 +1,95 @@
# 2 Systemauslegung
Dimensionierung der Komponenten für den Referenzfall **1000 L Milch/Tag**. Konzept: Milchabwärme → Wärmepumpe (Strom aus PV) → Natriumacetat-Latentspeicher.
---
## 2.1 Wärmepumpe
Die WP entzieht der Milch die Wärme (Verdampfer ~0 °C) und gibt sie bei >58 °C an den Speicher ab (Kondensator ~63 °C). Die Verdichterarbeit landet **mit** im Speicher:
```
Q_Speicher = Q_Milch + W_elektrisch
COP = Q_Speicher / W_elektrisch
W_el = Q_Milch / (COP 1)
```
| COP | Strom/Tag | Wärme in Speicher/Tag |
|---|---|---|
| 2,5 | 25,6 kWh | 64,0 kWh |
| **2,6 (Referenz)** | **24,0 kWh** | **62,4 kWh** |
| 3,0 | 19,2 kWh | 57,6 kWh |
Leistungsauslegung (18 h/Tag Laufzeit): Kälteleistung ~2,1 kW, Heizleistung ~3,5 kW, el. Leistung ~1,3 kW.
> Wichtig: Durch die WP wird **mehr** gespeichert als die reine Milchabwärme (62,4 statt 38,4 kWh/Tag).
---
## 2.2 PV-Anlage
Auslegung auf **Jahresbilanz** des WP-Strombedarfs (Standort Süddeutschland, ~1.000 kWh/kWp·a, ~5 m²/kWp).
| Größe | Wert |
|---|---|
| Jahresstrombedarf WP | ~8.760 kWh/a |
| Leistung | **~8,8 kWp** |
| Dachfläche | **~44 m²** |
Bei einer typischen Stalldachfläche von mehreren hundert m² problemlos verfügbar.
---
## 2.3 Netzbezug durch die Wintermonate
Die WP läuft täglich, die PV liefert saisonal. Monatsbilanz (gerundet):
| | kWh/a |
|---|---|
| Jahresbedarf WP | 8.760 |
| Netzbezug (OktFeb) | ~1.940 |
| Sommerüberschuss (MärSep) | ~1.960 |
**Netzbezug ≈ 22 %** des Jahresbedarfs fällt komplett in die Monate Oktober bis Februar. Der Sommerüberschuss entspricht fast genau dem Winter-Zukauf (bilanziell ausgeglichen, zeitlich verschoben).
> Diese 22 % gelten **mit** Batterie (Tag-/Nacht-Ausgleich). **Ohne** Batterie eher 5060 %, da die WP rund um die Uhr läuft, die PV aber nur tagsüber.
---
## 2.4 Batteriespeicher
Aufgabe: nur **Tag-/Nacht-Versatz** überbrücken (kein saisonaler Ausgleich das ist mit Batterie unmöglich).
| Größe | Wert |
|---|---|
| WP-Last pro Tag | 24 kWh |
| zu überbrückende Nachtlast | ~1416 kWh |
| empfohlen nutzbar | ~1518 kWh |
| Nennkapazität (LFP) | **~20 kWh** |
| Autonomie | ~1 Tag |
Mehr Kapazität senkt den 22-%-Winteranteil **nicht** der kommt aus dem saisonalen PV-Mangel, nicht aus dem Tag-/Nacht-Versatz.
---
## 2.5 Latentwärmespeicher (Natriumacetat)
Speichergröße = Wärme je Abholzyklus ÷ Energiedichte. Beispiel 14-Tage-Zyklus, Modus `latent_plus_hub`:
| Größe | Wert |
|---|---|
| Energie je Zyklus | 62,4 · 14 ≈ **873 kWh** |
| Masse | ~9.600 kg |
| Volumen | ~7,4 m³ |
Skalierung linear mit Zyklusdauer und Milchmenge. Details zu LKW-Auslastung und Abholrhythmus siehe Dokument 3.
---
## 2.6 Saisonaler Grundkonflikt
Wärme**erzeugung** und Wärme**bedarf** laufen gegenläufig:
- Sommer: viel PV-Strom, viel Abwärme aber wenig Heizbedarf.
- Winter: hoher Heizbedarf aber wenig PV (22 % Netzbezug) und gleicher Abwärmeanfall.
Das ist die zentrale Schwäche des Konzepts und der Grund, warum im Winter nur wenige Haushalte beheizt werden können (Dokument 3).
@@ -0,0 +1,88 @@
# 3 Logistik, Haushalte & Wirtschaftlichkeit
Verwertung der gespeicherten Wärme: Transport, Reichweite bei Haushalten, Marktwert. Referenzfall **1000 L Milch/Tag**.
---
## 3.1 Transport im LKW
Grenze: Standard-Sattelauflieger.
| Größe | Wert |
|---|---|
| Nutzlast | ~24 t |
| Laderaum | ~90 m³ |
Natriumacetat (~1,3 kg/L): 24 t ≈ **18,5 m³** → passt locker ins Volumen. **Das Gewicht bindet**, nicht das Volumen (nur ~20 % Laderaum genutzt).
**Energieinhalt eines vollen LKW:**
- `latent_plus_hub`: 24 t · 0,091 ≈ **~2.185 kWh**
- `latent_only`: 24 t · 0,073 ≈ **~1.760 kWh**
**Abholrhythmus:** Aus 62,4 kWh/Tag füllt sich ein voller LKW (Hub-Modus) nach **~35 Tagen**. Optionen:
| Zyklus | Masse | Volumen | LKW-Auslastung |
|---|---|---|---|
| 7 Tage | 4,8 t | 3,7 m³ | 20 % |
| 14 Tage | 9,6 t | 7,4 m³ | 40 % |
| 21 Tage | 14,4 t | 11,1 m³ | 60 % |
| 28 Tage | 19,2 t | 14,7 m³ | 80 % |
| 35 Tage | 24,0 t | 18,4 m³ | 100 % |
Empfehlung: an die volle Ladung koppeln (~monatlich) statt halbleere Fahrten, oder kleineren Wechselbehälter für 2-Wochen-Takt.
---
## 3.2 Versorgbare Haushalte (Familie 2 Erw. + 2 Kinder)
**Warmwasserbedarf:** Erwachsener ~1,8 kWh/Tag, Kind ~1,0 kWh/Tag → Haushalt **~5,6 kWh/Tag** (~2.040 kWh/a).
**Winter-Heizbedarf:** ~60 kWh/Tag/Haushalt (gebäudeabhängig: saniert ~40, Altbau ~100).
| Anwendung | Bedarf/HH/Tag | Versorgbare Haushalte |
|---|---|---|
| Warmwasser (ganzjährig) | 5,6 kWh | **~11 Haushalte** |
| Winter (Heizung + WW) | ~65,6 kWh | **~1 Haushalt** |
Kernaussage: Aus 1000 L Milch lassen sich **~11 Familien mit Warmwasser** versorgen aber sobald die Raumheizung dazukommt, verschlingt schon **ein einziges Haus** fast die komplette Tagesproduktion. Eine LKW-Ladung deckt den Warmwasserbedarf einer Familie für gut ein Jahr.
Die Speicherwärme wird bei 58 °C abgegeben passend für Warmwasser (4560 °C) und Flächenheizung.
> Die Haushaltszahl hängt an der **Produktionsrate**, nicht am LKW-Modus.
---
## 3.3 Wert der Wärme beim Käufer (Ersatzkosten)
Was der Käufer für dieselbe Nutzwärme aus Brennstoffen zahlen würde (Preise Stand Juni 2026, Deutschland; Nutzwärmekosten = Brennstoffpreis ÷ Kesselwirkungsgrad).
| Brennstoff | Nutzwärme | EUR/LKW (~2.185 kWh) | EUR/Jahr (~22.800 kWh) |
|---|---|---|---|
| Gas (11 ct/kWh @ 95 %) | 11,6 ct/kWh | ~253 € | ~2.635 € |
| Heizöl (11,5 @ 90 %) | 12,8 ct/kWh | ~279 € | ~2.908 € |
| Pellets (7,0 @ 88 %) | 8,0 ct/kWh | ~174 € | ~1.810 € |
> Das ist der **Brutto-Substitutionswert**, nicht der erzielbare Verkaufspreis. Davon gehen Transport, WP-/PV-Strom (22 % Netzbezug) und Speicherinvestition ab. Pellets (~8 ct/kWh) sind der härteste Konkurrenzmaßstab.
---
## 3.4 Python-Werkzeug
Zur Nachrechnung beliebiger Varianten: `auslegung.py` + `config.json`.
```
python auslegung.py 1000 # Detailreport
python auslegung.py 1000 --suggest-size # gerundete Empfehlung + Abholzyklus
python auslegung.py 1500 --config variante.json --json ergebnis.json
```
**Berechnet:** Milchkühllast, Wärmepumpe (Kälte-/Heiz-/Stromleistung), PV (kWp + Fläche), Netzbezug, Batterie, Latentspeicher inkl. LKW-Abgleich und Energieinhalt, versorgte Haushalte (WW + Winter), Brennstoffkosten-Vergleich.
**Alle Annahmen** stehen in `config.json` (Milch, WP-COP, PCM-Modus/Stoffwerte, Abholzyklus, LKW-Grenzen, PV-Profil, Batterie, Haushalt, Brennstoffpreise). Variante = Datei kopieren, Werte ändern.
**Bewusste Modell-Vereinfachungen:**
- Zurückkehrender Behälter wird als „warm" (nahe Schmelzpunkt) angenommen, sonst müsste die WP ihn zusätzlich aufheizen.
- Netzbezug ist Monatsbilanz, setzt Tag-/Nacht-Ausgleich per Batterie voraus.
- `--suggest-size` optimiert auf Transportauslastung, nicht auf Speicher-Verweildauer.
**Mögliche nächste Schritte:** Anbieterseite (Transportkosten, WP-Stromkosten, Speicher-Abschreibung) → echte Marge / Mindestverkaufspreis je kWh.
+75
View File
@@ -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.
+254 -128
View File
@@ -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,
}
@@ -215,7 +234,147 @@ def haushalte(q_speicher_tag: float, cfg: dict) -> dict:
}
def rechne(milch_l_tag: float, cfg: dict) -> dict:
def brennstoffkosten(e: dict, cfg: dict) -> dict:
"""Vergleichswert der Waerme gegenueber fossilen Brennstoffen / Pellets.
Nutzwaermekosten = Brennstoffpreis / Kesselwirkungsgrad.
Bewertet werden der Energieinhalt eines vollen LKW und die Jahresmenge.
"""
energie_lkw = e["speicher"]["voller_lkw_energie_kWh"]
energie_jahr = e["waermepumpe"]["speicher_kWh_tag"] * TAGE_PRO_JAHR
vergleich = []
for b in cfg.get("brennstoffe", []):
nw_ct = b["preis_ct_kWh"] / b["wirkungsgrad"]
vergleich.append({
"name": b["name"],
"preis_ct_kWh": b["preis_ct_kWh"],
"wirkungsgrad": b["wirkungsgrad"],
"nutzwaerme_ct_kWh": nw_ct,
"eur_pro_lkw": energie_lkw * nw_ct / 100.0,
"eur_pro_jahr": energie_jahr * nw_ct / 100.0,
})
return {
"energie_lkw_kWh": energie_lkw,
"energie_jahr_kWh": energie_jahr,
"vergleich": vergleich,
}
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)
@@ -223,7 +382,7 @@ def rechne(milch_l_tag: float, cfg: dict) -> dict:
netz = netzbezug(wp["strom_kWh_tag"], pv["kWp"], cfg)
bat = batterie(wp["strom_kWh_tag"], cfg)
hh = haushalte(wp["speicher_kWh_tag"], cfg)
return {
e = {
"eingang": {"milch_l_tag": milch_l_tag},
"milch_kuehllast_kWh_tag": q_milch,
"waermepumpe": wp,
@@ -233,6 +392,11 @@ def rechne(milch_l_tag: float, cfg: dict) -> dict:
"batterie": bat,
"haushalte": hh,
}
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
def runde_auf(wert: float, schritt: float) -> float:
@@ -296,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")
@@ -332,150 +496,108 @@ 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("")
L.append("WERT DER WAERME BEIM KAEUFER (Ersatzkosten fossil/Holz)")
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} | {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 |")
return "\n".join(L)
# report_md wurde nach lib/report_md.py ausgelagert und oben importiert.
def main() -> None:
@@ -495,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()
@@ -525,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)
+449
View File
@@ -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}",
]))
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)
+386
View File
@@ -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()