Compare commits

...

9 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
10 changed files with 1423 additions and 150 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.
+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%" %*
+131 -3
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": {
@@ -81,5 +84,130 @@
"_kommentar": "Holzpellets Juni 2026 (~350 EUR/t bei 5 kWh/kg), moderner Pelletskessel."
}
],
"_kommentar_brennstoffe": "Vergleichswert: Nutzwaermekosten = preis_ct_kWh / wirkungsgrad. Reihenfolge bestimmt die Anzeigereihenfolge im Report."
"_kommentar_brennstoffe": "Vergleichswert: Nutzwaermekosten = preis_ct_kWh / wirkungsgrad. Reihenfolge bestimmt die Anzeigereihenfolge im Report.",
"container_invest": {
"_beschreibung": "Kostenbausteine fuer ein Latentspeicher-Modul (LKW-Aufbau). Das Programm skaliert pcm und behaelter mit der berechneten PCM-Masse; alle anderen Positionen sind Pauschalpreise je Modul.",
"anzahl_module_dauerbetrieb": 2,
"_kommentar_module": "Dauerbetrieb erfordert mindestens 2 Module: eines wird auf dem Hof geladen, eines ist unterwegs zum Kaeufer.",
"pcm_natriumacetat": {
"preis_eur_pro_kg": 0.70,
"reserve_faktor": 1.05,
"_kommentar": "Industriequalitaet (nicht Pharma), Grossgebinde ab 1 t, Stand 06/2026 DE. Reservefaktor deckt Befuell- und Handhabungsverluste ab.",
"_quellen": [
"Brenntag DE (Grosshaendler Chemierohstoffe): brenntag.com/de-de",
"Sasol / Clariant (Hersteller Natriumacetat techn.): sasol.com",
"Alibaba Preisvergleich Grossmengen: alibaba.com (Suche: sodium acetate trihydrate bulk)",
"Rubitherm GmbH (PCM-Spezialist, Vergleichspreise): rubitherm.eu"
]
},
"behaelter": {
"preis_eur_pro_kg_pcm": 2.80,
"mindestpreis_eur": 12000,
"_kommentar": "Isolierter Edelstahlbehaelter (1.4301/1.4404) in Rahmenbauweise fuer LKW-Aufbau, 80 mm PUR-Daemmung, drucklos bis 120 C. Preis linear skaliert je kg PCM-Kapazitaet. Mindestpreis gilt fuer die kleinste sinnvolle Einheit (ca. 3 t PCM / ~2300 L Volumen). Verbindliche Preise nur per Anfrage beim Behaelterbauer.",
"_quellen": [
"Ziemann Holvrieka (Edelstahlbehaelter Lebensmittel/Industrie): ziemann-holvrieka.com",
"Paul Mueller Company (Thermalbehaelter, Milchwirtschaft): paulmueller.com",
"GPI Tanks (Industrietanks, Anfrage Sonderanfertigung): gpitanks.com",
"Vergleichswert: Isolierter IBC 1000 L (Schuetz/Mauser) ~1.200 EUR -> hoeherer Preis fuer Sonderbehaelter 10-30 t plausibel"
]
},
"waermetauscher_ladung": {
"typ": "Platten-WT Milchwirtschaft (EHEDG, 1.4404)",
"hersteller_beispiel": "Alfa Laval M6 / GEA NT50 / Kelvion NH250",
"nennleistung_kW": 5.0,
"preis_eur": 2500,
"_kommentar": "Handelsueblicher Milch-Platten-WT, EHEDG-zertifiziert und CIP-faehig (identischer Typ wie in der Milchkuehlung des Hofs). Laedt den PCM-Behaelter aus dem WP-Kondensator (Vorlauf 80 C, Ruecklauf ~60 C). Preis inkl. Rahmen, ohne Montage. Gleiche Bauart wird in der Hof-Milchkuehlung bereits eingesetzt.",
"_quellen": [
"Alfa Laval (M6/M10 Milch-PHE, Preisliste auf Anfrage): alfalaval.com",
"GEA Group (NT50/NT100 fuer Dairy): gea.com",
"Kelvion (NH250 Plattentauscher): kelvion.com",
"Marktpreis Neu 5 kW PHE Milch: 1.800-3.500 EUR (Haendlerauskunft, Stand 2025)"
]
},
"waermetauscher_entladung": {
"typ": "Platten-WT Industrial (1.4404)",
"nennleistung_kW": 20.0,
"preis_eur": 1500,
"_kommentar": "Entladeseite: PCM-Waerme -> Kundennetz an der LKW-seitigen Uebergabestation. Kein Lebensmittelkontakt, daher Standard-PHE ohne EHEDG-Anforderung.",
"_quellen": [
"Alfa Laval (CB/AlfaNova Kompakt-PHE): alfalaval.com",
"Funke Waermeaustauscher (GPW-Serie): funke.de",
"Marktpreis Neu 20 kW Industrial PHE: 900-2.200 EUR (Haendlerauskunft, Stand 2025)"
]
},
"hydraulik": {
"preis_eur": 1200,
"_kommentar": "Schnellanschluesse (Kamlok DN50), Absperrventile (Kugelhahn PN16), Umwaelzpumpe (Grundfos CM5), flexible Hochtemperaturschlaeuche (bis 150 C), Rohrverbinder und Kleinmaterial.",
"_quellen": [
"Grundfos (CM5-Pumpe, Produktkatalog): grundfos.com",
"Georg Fischer (Kugelhahn PN16, 1.4408): georgfischer.com",
"Stauff / Parker (Hochtemperaturschlaeuche): stauff.com",
"Preise: Industriebedarf-Haendler wie RS Components (rs-online.com) oder Conrad"
]
},
"elektronik": {
"steuerung_eur": 800,
"sensoren_pt100_anzahl": 10,
"sensor_preis_eur": 75,
"ventile_schaltschrank_eur": 500,
"fernueberwachung_4g_eur": 350,
"montage_verdrahtung_eur": 800,
"_kommentar": "Steuerung: SPS (SIMATIC S7-1200 o.ae.) oder µC-basiert (ESP32 + Industrie-Shield). PT100-Sensoren verteilt im PCM-Volumen (oben/mitte/unten je Zone) fuer Ladestandserkennung ueber Temperaturprofil. Schaltschrank IP54, 4G-Fernueberwachung fuer Betriebsdaten und Alarme.",
"_quellen": [
"Siemens SIMATIC S7-1200 (SPS, Listenpreis ~600-900 EUR): siemens.com/simatic",
"Weidmueller / Phoenix Contact (Schaltschrank-Komponenten): phoenixcontact.com",
"PT100-Sensor Industrieausfuehrung (z.B. Jumo, Endress+Hauser): jumo.de / endress.com",
"4G-Fernueberwachung (z.B. Teltonika RUT240, ~150 EUR + SIM): teltonika-networks.com",
"Alternativ µC-Loesung: ESP32 + Modbus-Shield + Node-RED (Open-Source-Stack)"
]
},
"montage_inbetriebnahme_eur": 2000,
"_kommentar_montage": "Mechanische Montage aller Komponenten auf dem Containerrahmen, Leitungsverlegung, Dichtigkeitspruefung, Erstbefuellung PCM, Inbetriebnahme Regelung. Schaetzwert auf Basis Montagestundensatz Anlagenbau Suedbayern ~65-80 EUR/h, ca. 25-30 h.",
"_quellen_montage": [
"Stundensaetze Anlagenbau DE 2025: VDMA-Statistik (vdma.org), regionale Schlossereibetriebe"
]
},
"anbieter": {
"_beschreibung": "Betriebswirtschaftliche Annahmen fuer die Anbieterseite. Ermoeglicht die Berechnung von Waerme-Gestehungskosten (ct/kWh) und Marge gegenueber Ersatzbrennstoff. Abschreibung: linear, kein Kapitalzins (konservativ).",
"transport_eur_pro_fuhre": 300,
"fahrer_eur_pro_fuhre": 120,
"_kommentar_transport": "transport_eur_pro_fuhre: Gesamtkosten je Fuhre (Treibstoff + Fahrer + Fahrzeugabschreibung). fahrer_eur_pro_fuhre: Fahreranteil daran, wird mit --driver abgezogen wenn der Landwirt selbst faehrt. Richtwert Kurzstrecke bis ~100 km.",
"netzstrompreis_ct_kWh": 28.0,
"_kommentar_netzstrom": "Industriestrompreis (Netzbezug), Stand 06/2026 DE. Fuer Landwirtschaft oft Sondertarif moeglich (~22-28 ct/kWh). Quelle: BDEW Strompreisanalyse (bdew.de).",
"pv_gestehung_ct_kWh": 9.0,
"_kommentar_pv_gestehung": "Levelized Cost of Energy (LCOE) der eigenen PV-Anlage, d.h. Abschreibung und Wartung je erzeugter kWh. Typisch 7-11 ct/kWh fuer Neuanlagen 2024-2026 (Fraunhofer ISE: ise.fraunhofer.de).",
"wartung_eur_pro_jahr": 500,
"_kommentar_wartung": "Pauschale Jahreskosten fuer Wartung, Versicherung, Kleinreparaturen der gesamten Anlage (WP, PV, Speicher). Exkl. Speicher-Container (separat ueber container_invest abgebildet).",
"invest": {
"pv_eur_pro_kWp": 1200,
"pv_nutzungsdauer_jahre": 25,
"_kommentar_pv": "Investitionskosten PV inkl. Montage und Wechselrichter, Stand 2025 DE. Quelle: Bundesnetzagentur Marktstammdatenregister, Fraunhofer ISE Photovoltaics Report (ise.fraunhofer.de).",
"batterie_eur_pro_kWh": 400,
"batterie_nutzungsdauer_jahre": 15,
"_kommentar_batterie": "LFP-Hausspeicher inkl. Wechselrichter, Stand 2025 DE (~350-500 EUR/kWh Nennkapazitaet). Quelle: BVES Jahresbericht (bves.de), Haendlerpreise.",
"waermepumpe_eur": 12000,
"waermepumpe_nutzungsdauer_jahre": 20,
"_kommentar_waermepumpe": "Industrie-Waermepumpe 3-5 kW el. inkl. Montage und Inbetriebnahme. Quelle: BWP Branchenstudie (waermepumpe.de), Haendlerangebote.",
"speicher_nutzungsdauer_jahre": 20,
"_kommentar_speicher": "Nutzungsdauer der Container-Module. PCM selbst ist unbegrenzt wiederverwertbar; limitierende Teile sind Behaelter (Korrosion) und Elektronik. Investitionshoehe wird aus container_invest berechnet."
}
}
}
+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
+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.
+217 -147
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,
}
@@ -241,7 +260,121 @@ def brennstoffkosten(e: dict, cfg: dict) -> dict:
}
def rechne(milch_l_tag: float, cfg: dict) -> dict:
def container_invest_total(sp: dict, cfg: dict) -> dict:
"""Investitionssumme aller Container-Module aus der container_invest-Config."""
ci = cfg["container_invest"]
masse = sp["masse_kg"]
energie = sp["energie_pro_zyklus_kWh"]
anzahl = ci["anzahl_module_dauerbetrieb"]
pcm_eur = masse * ci["pcm_natriumacetat"]["preis_eur_pro_kg"] * ci["pcm_natriumacetat"]["reserve_faktor"]
behaelter_eur = max(ci["behaelter"]["mindestpreis_eur"],
masse * ci["behaelter"]["preis_eur_pro_kg_pcm"])
wt_eur = (ci["waermetauscher_ladung"]["preis_eur"]
+ ci["waermetauscher_entladung"]["preis_eur"])
hydraulik_eur = ci["hydraulik"]["preis_eur"]
el = ci["elektronik"]
elektronik_eur = (el["steuerung_eur"]
+ el["sensoren_pt100_anzahl"] * el["sensor_preis_eur"]
+ el["ventile_schaltschrank_eur"]
+ el["fernueberwachung_4g_eur"]
+ el["montage_verdrahtung_eur"])
montage_eur = ci["montage_inbetriebnahme_eur"]
pro_modul = pcm_eur + behaelter_eur + wt_eur + hydraulik_eur + elektronik_eur + montage_eur
gesamt = pro_modul * anzahl
eur_pro_kwh = gesamt / (energie * anzahl) if energie > 0 else 0.0
return {
"anzahl_module": anzahl,
"pcm_eur": pcm_eur * anzahl,
"behaelter_eur": behaelter_eur * anzahl,
"wt_hydraulik_eur": (wt_eur + hydraulik_eur) * anzahl,
"elektronik_eur": elektronik_eur * anzahl,
"montage_eur": montage_eur * anzahl,
"gesamt_eur": gesamt,
"eur_pro_kwh_kapazitaet": eur_pro_kwh,
}
def anbieterkosten(e: dict, cfg: dict, *, eh_da: bool = False, driver: bool = False) -> dict:
"""Jaehrliche Betriebskosten des Anbieters -> Gestehungskosten und Marge.
eh_da=True : PV und Kuehlung bereits vorhanden -> Kapitalkosten dafuer entfallen.
driver=True : Fahrer stellt der Landwirt selbst -> Fahreranteil wird abgezogen.
Abschreibung: linear ohne Kapitalzins (konservativ).
"""
a = cfg["anbieter"]
inv = a["invest"]
wp = e["waermepumpe"]
sp = e["speicher"]
pv_data = e["pv"]
netz = e["netzbezug"]
bat = e["batterie"]
energie_jahr = wp["speicher_kWh_tag"] * TAGE_PRO_JAHR
fuhren_jahr = TAGE_PRO_JAHR / cfg["abholung"]["zyklus_tage"]
fahrer_abzug = a.get("fahrer_eur_pro_fuhre", 0.0) if driver else 0.0
kosten_transport = fuhren_jahr * (a["transport_eur_pro_fuhre"] - fahrer_abzug)
netz_eur = netz["netzbezug_kWh"] * a["netzstrompreis_ct_kWh"] / 100.0
pv_kwh = netz["jahresbedarf_kWh"] - netz["netzbezug_kWh"]
pv_eur = pv_kwh * a["pv_gestehung_ct_kWh"] / 100.0
kosten_strom = netz_eur + pv_eur
kosten_wartung = a["wartung_eur_pro_jahr"]
kapital_pv = 0.0 if eh_da else pv_data["kWp"] * inv["pv_eur_pro_kWp"] / inv["pv_nutzungsdauer_jahre"]
kapital_bat = bat["nominal_kWh"] * inv["batterie_eur_pro_kWh"] / inv["batterie_nutzungsdauer_jahre"]
kapital_wp = 0.0 if eh_da else inv["waermepumpe_eur"] / inv["waermepumpe_nutzungsdauer_jahre"]
ci = container_invest_total(sp, cfg)
kapital_speicher = ci["gesamt_eur"] / inv["speicher_nutzungsdauer_jahre"]
kosten_kapital = kapital_pv + kapital_bat + kapital_wp + kapital_speicher
jahreskosten = kosten_transport + kosten_strom + kosten_wartung + kosten_kapital
gestehung_ct = jahreskosten / energie_jahr * 100.0 if energie_jahr > 0 else 0.0
marge = []
for v in e.get("brennstoffkosten", {}).get("vergleich", []):
m_ct = v["nutzwaerme_ct_kWh"] - gestehung_ct
marge.append({
"name": v["name"],
"nutzwaerme_ct_kWh": v["nutzwaerme_ct_kWh"],
"marge_ct_kWh": m_ct,
"marge_eur_jahr": m_ct / 100.0 * energie_jahr,
})
return {
"fuhren_jahr": fuhren_jahr,
"energie_jahr_kWh": energie_jahr,
"kosten_transport_eur": kosten_transport,
"kosten_strom_eur": kosten_strom,
"kosten_strom_detail": {
"netz_kWh": netz["netzbezug_kWh"],
"netz_ct_kWh": a["netzstrompreis_ct_kWh"],
"netz_eur": netz_eur,
"pv_kWh": pv_kwh,
"pv_ct_kWh": a["pv_gestehung_ct_kWh"],
"pv_eur": pv_eur,
},
"kosten_wartung_eur": kosten_wartung,
"kapital_pv_eur": kapital_pv,
"kapital_batterie_eur": kapital_bat,
"kapital_waermepumpe_eur": kapital_wp,
"kapital_speicher_eur": kapital_speicher,
"kosten_kapital_eur": kosten_kapital,
"jahreskosten_eur": jahreskosten,
"gestehungskosten_ct_kWh": gestehung_ct,
"container_invest": ci,
"marge": marge,
"eh_da": eh_da,
"driver": driver,
"fahrer_abzug_eur_pro_fuhre": fahrer_abzug,
}
def rechne(milch_l_tag: float, cfg: dict, *, eh_da: bool = False, driver: bool = False) -> dict:
q_milch = milch_kuehllast(milch_l_tag, cfg)
wp = waermepumpe(q_milch, cfg)
sp = speicher(wp["speicher_kWh_tag"], cfg)
@@ -261,6 +394,8 @@ def rechne(milch_l_tag: float, cfg: dict) -> dict:
}
if "brennstoffe" in cfg:
e["brennstoffkosten"] = brennstoffkosten(e, cfg)
if "anbieter" in cfg and "container_invest" in cfg:
e["anbieter"] = anbieterkosten(e, cfg, eh_da=eh_da, driver=driver)
return e
@@ -325,9 +460,9 @@ def vorschlag_report(e: dict, cfg: dict) -> str:
L.append(f"Abholzyklus .......... alle {v['empf_zyklus_tage']:.0f} Tage")
L.append("")
L.append(f" -> LKW dann zu {sp['auslastung_gewicht']*100:.0f} % ausgelastet "
f"({sp['energie_pro_zyklus_kWh']:.0f} kWh je Fuhre)")
f"({t(sp['energie_pro_zyklus_kWh'])} kWh je Fuhre)")
L.append(f" -> ein voller LKW waere nach {v['voll_lkw_tage']:.0f} Tagen erreicht")
L.append(f" -> Energieinhalt eines vollen LKW: ~{sp['voller_lkw_energie_kWh']:.0f} kWh")
L.append(f" -> Energieinhalt eines vollen LKW: ~{t(sp['voller_lkw_energie_kWh'])} kWh")
L.append("")
L.append(f"Versorgte Haushalte (2 Erw. + 2 Kinder):")
L.append(f" Warmwasser ganzjaehrig ... ~{hh['haushalte_warmwasser']:.0f} Haushalte")
@@ -361,50 +496,50 @@ def report(e: dict, cfg: dict) -> str:
L.append(f" Eingang: {e['eingang']['milch_l_tag']:.0f} L Milch/Tag"
f" ({cfg['milch']['temp_ein_C']:.0f} -> {cfg['milch']['temp_aus_C']:.0f} C)")
L.append("=" * 62)
L.append(f"Kuehllast der Milch ........... {q:8.1f} kWh/Tag")
L.append(f"Kuehllast der Milch ........... {t(q, 1):>8} kWh/Tag")
L.append("")
L.append("WAERMEPUMPE (COP {:.2f}, Laufzeit {:.0f} h/Tag)".format(
cfg["waermepumpe"]["cop"], cfg["waermepumpe"]["laufzeit_h_pro_tag"]))
L.append(f" Kaelteleistung (Verdampfer) . {wp['kaelteleistung_kW']:8.1f} kW")
L.append(f" Heizleistung (Kondensator) .. {wp['heizleistung_kW']:8.1f} kW")
L.append(f" el. Leistung ................ {wp['elektrische_leistung_kW']:8.1f} kW")
L.append(f" Strombedarf ................. {wp['strom_kWh_tag']:8.1f} kWh/Tag")
L.append(f" Waerme in den Speicher ...... {wp['speicher_kWh_tag']:8.1f} kWh/Tag")
L.append(f" Kaelteleistung (Verdampfer) . {t(wp['kaelteleistung_kW'], 1):>8} kW")
L.append(f" Heizleistung (Kondensator) .. {t(wp['heizleistung_kW'], 1):>8} kW")
L.append(f" el. Leistung ................ {t(wp['elektrische_leistung_kW'], 1):>8} kW")
L.append(f" Strombedarf ................. {t(wp['strom_kWh_tag'], 1):>8} kWh/Tag")
L.append(f" Waerme in den Speicher ...... {t(wp['speicher_kWh_tag'], 1):>8} kWh/Tag")
L.append("")
L.append("PV-ANLAGE (Jahresbilanz)")
L.append(f" Jahresstrombedarf WP ........ {pv['jahresbedarf_kWh']:8.0f} kWh/a")
L.append(f" Leistung .................... {pv['kWp']:8.1f} kWp")
L.append(f" Dachflaeche ................. {pv['flaeche_m2']:8.0f} m2")
L.append(f" Jahresstrombedarf WP ........ {t(pv['jahresbedarf_kWh']):>8} kWh/a")
L.append(f" Leistung .................... {t(pv['kWp'], 1):>8} kWp")
L.append(f" Dachflaeche ................. {t(pv['flaeche_m2']):>8} m2")
L.append("")
L.append(f"NETZBEZUG (durch Wintermonate, Monatsbilanz)")
L.append(f" Netzbezug ................... {netz['netzbezug_kWh']:8.0f} kWh/a"
L.append(f" Netzbezug ................... {t(netz['netzbezug_kWh']):>8} kWh/a"
f" = {netz['netzbezug_anteil']*100:.0f} %")
L.append(f" Sommerueberschuss ........... {netz['ueberschuss_kWh']:8.0f} kWh/a")
L.append(f" Sommerueberschuss ........... {t(netz['ueberschuss_kWh']):>8} kWh/a")
L.append("")
L.append("BATTERIE (Tag-/Nacht-Ueberbbrueckung)")
L.append(f" nutzbare Kapazitaet ......... {bat['nutzbar_kWh']:8.1f} kWh")
L.append(f" Nennkapazitaet .............. {bat['nominal_kWh']:8.1f} kWh")
L.append(f" nutzbare Kapazitaet ......... {t(bat['nutzbar_kWh'], 1):>8} kWh")
L.append(f" Nennkapazitaet .............. {t(bat['nominal_kWh'], 1):>8} kWh")
L.append("")
L.append("VERSORGTE HAUSHALTE ({} Erw. + {} Kinder)".format(
cfg["haushalt"]["erwachsene"], cfg["haushalt"]["kinder"]))
L.append(f" Warmwasser je Haushalt ...... {hh['ww_pro_haushalt_kWh_tag']:8.1f} kWh/Tag")
L.append(f" -> Warmwasser ganzjaehrig ... {hh['haushalte_warmwasser']:8.1f} Haushalte")
L.append(f" max. Heizbedarf Winter ...... {hh['heizbedarf_winter_kWh_tag']:8.1f} kWh/Tag/HH")
L.append(f" Winterbedarf (Heiz+WW) ...... {hh['winter_pro_haushalt_kWh_tag']:8.1f} kWh/Tag/HH")
L.append(f" -> im Winter ................ {hh['haushalte_winter']:8.1f} Haushalte")
L.append(f" Warmwasser je Haushalt ...... {t(hh['ww_pro_haushalt_kWh_tag'], 1):>8} kWh/Tag")
L.append(f" -> Warmwasser ganzjaehrig ... {t(hh['haushalte_warmwasser'], 1):>8} Haushalte")
L.append(f" max. Heizbedarf Winter ...... {t(hh['heizbedarf_winter_kWh_tag'], 1):>8} kWh/Tag/HH")
L.append(f" Winterbedarf (Heiz+WW) ...... {t(hh['winter_pro_haushalt_kWh_tag'], 1):>8} kWh/Tag/HH")
L.append(f" -> im Winter ................ {t(hh['haushalte_winter'], 1):>8} Haushalte")
L.append("")
L.append(f"LATENTSPEICHER ({cfg['pcm']['name']}, Modus: {cfg['pcm']['modus']})")
L.append(f" Energiedichte ............... {sp['energiedichte_kWh_pro_kg']*1000:8.1f} Wh/kg")
L.append(f" Energie je Zyklus ({z:>2} Tage) . {sp['energie_pro_zyklus_kWh']:8.0f} kWh")
L.append(f" Masse ....................... {sp['masse_kg']:8.0f} kg")
L.append(f" Volumen ..................... {sp['volumen_m3']:8.1f} m3")
L.append(f" LKW-Auslastung Gewicht ...... {sp['auslastung_gewicht']*100:8.0f} %")
L.append(f" LKW-Auslastung Volumen ...... {sp['auslastung_volumen']*100:8.0f} %")
L.append(f" Energiedichte ............... {t(sp['energiedichte_kWh_pro_kg']*1000, 1):>8} Wh/kg")
L.append(f" Energie je Zyklus ({z:>2} Tage) . {t(sp['energie_pro_zyklus_kWh']):>8} kWh")
L.append(f" Masse ....................... {t(sp['masse_kg']):>8} kg")
L.append(f" Volumen ..................... {t(sp['volumen_m3'], 1):>8} m3")
L.append(f" LKW-Auslastung Gewicht ...... {t(sp['auslastung_gewicht']*100):>8} %")
L.append(f" LKW-Auslastung Volumen ...... {t(sp['auslastung_volumen']*100):>8} %")
L.append(f" bindende Grenze ............. {sp['bindende_grenze']:>8}")
passt = "ja" if sp["passt_in_einen_lkw"] else "NEIN -> mehrere Behaelter noetig"
L.append(f" passt in einen LKW .......... {passt:>8}")
L.append(f" voller LKW voll nach ........ {sp['voller_lkw_tausch_tage']:8.1f} Tagen")
L.append(f" Energieinhalt voller LKW .... {sp['voller_lkw_energie_kWh']:8.0f} kWh")
L.append(f" voller LKW voll nach ........ {t(sp['voller_lkw_tausch_tage'], 1):>8} Tagen")
L.append(f" Energieinhalt voller LKW .... {t(sp['voller_lkw_energie_kWh']):>8} kWh")
bk = e.get("brennstoffkosten")
if bk and bk["vergleich"]:
L.append("")
@@ -412,126 +547,57 @@ def report(e: dict, cfg: dict) -> str:
L.append(f" {'Brennstoff':<10} | {'ct/kWh':>7} | {'EUR/LKW':>9} | {'EUR/Jahr':>9}")
L.append(f" {'-'*10}-+-{'-'*7}-+-{'-'*9}-+-{'-'*9}")
for v in bk["vergleich"]:
L.append(f" {v['name']:<10} | {v['nutzwaerme_ct_kWh']:>7.1f} | "
f"{v['eur_pro_lkw']:>9.0f} | {v['eur_pro_jahr']:>9.0f}")
L.append(f" Bewertet: voller LKW ~{bk['energie_lkw_kWh']:.0f} kWh, "
f"Jahreserzeugung ~{bk['energie_jahr_kWh']:.0f} kWh")
L.append(f" {v['name']:<10} | {t(v['nutzwaerme_ct_kWh'], 1):>7} | "
f"{t(v['eur_pro_lkw']):>9} | {t(v['eur_pro_jahr']):>9}")
L.append(f" Bewertet: voller LKW ~{t(bk['energie_lkw_kWh'])} kWh, "
f"Jahreserzeugung ~{t(bk['energie_jahr_kWh'])} kWh")
ak = e.get("anbieter")
if ak:
inv = cfg["anbieter"]["invest"]
ci = ak["container_invest"]
sd = ak["kosten_strom_detail"]
L.append("")
L.append("WAERME-GESTEHUNGSKOSTEN ANBIETER (linear, kein Kapitalzins)")
L.append(f" Transport ({ak['fuhren_jahr']:.0f} Fuhren/a) ........"
f" {t(ak['kosten_transport_eur']):>8} EUR/a")
L.append(f" WP-Strom ({t(sd['netz_kWh'])} kWh Netz"
f" @ {sd['netz_ct_kWh']:.0f} ct + {t(sd['pv_kWh'])} kWh PV"
f" @ {sd['pv_ct_kWh']:.0f} ct) .. {t(ak['kosten_strom_eur']):>6} EUR/a")
L.append(f" Wartung .......................... {t(ak['kosten_wartung_eur']):>8} EUR/a")
L.append(f" Kapital PV ({pv['kWp']:.1f} kWp, {inv['pv_nutzungsdauer_jahre']} J) ......"
f" {t(ak['kapital_pv_eur']):>8} EUR/a")
L.append(f" Kapital Batterie ({bat['nominal_kWh']:.0f} kWh, {inv['batterie_nutzungsdauer_jahre']} J) ."
f" {t(ak['kapital_batterie_eur']):>8} EUR/a")
L.append(f" Kapital WP ({inv['waermepumpe_nutzungsdauer_jahre']} J) ............."
f" {t(ak['kapital_waermepumpe_eur']):>8} EUR/a")
L.append(f" Kapital Speicher ({ci['anzahl_module']} Module, {inv['speicher_nutzungsdauer_jahre']} J)"
f" {t(ak['kapital_speicher_eur']):>8} EUR/a")
L.append(f" {'-'*46}")
L.append(f" Jahreskosten gesamt .............. {t(ak['jahreskosten_eur']):>8} EUR/a")
L.append(f" Jahreserzeugung .................. {t(ak['energie_jahr_kWh']):>8} kWh/a")
L.append(f" -> Mindestverkaufspreis .......... {t(ak['gestehungskosten_ct_kWh'], 1):>8} ct/kWh")
L.append(f" Speicher-Invest ({ci['anzahl_module']} Module): {t(ci['gesamt_eur'])} EUR"
f" = {t(ci['eur_pro_kwh_kapazitaet'])} EUR/kWh Kapazitaet")
L.append(f" PCM {t(ci['pcm_eur'])} | Behaelter {t(ci['behaelter_eur'])}"
f" | WT+Hydr. {t(ci['wt_hydraulik_eur'])}"
f" | Elektr. {t(ci['elektronik_eur'])} | Montage {t(ci['montage_eur'])}")
if ak["marge"]:
L.append("")
L.append("MARGE GEGEN ERSATZBRENNSTOFF")
L.append(f" {'Brennstoff':<10} | {'ct/kWh':>7} | {'Marge ct/kWh':>12} | {'Marge EUR/Jahr':>14}")
L.append(f" {'-'*10}-+-{'-'*7}-+-{'-'*12}-+-{'-'*14}")
for m in ak["marge"]:
haertester = " <== haertester Massstab" if m == ak["marge"][-1] else ""
vorzeichen_ct = "+" if m['marge_ct_kWh'] >= 0 else ""
vorzeichen_eur = "+" if m['marge_eur_jahr'] >= 0 else ""
L.append(f" {m['name']:<10} | {t(m['nutzwaerme_ct_kWh'], 1):>7} |"
f" {vorzeichen_ct}{t(m['marge_ct_kWh'], 1):>11} |"
f" {vorzeichen_eur}{t(m['marge_eur_jahr']):>13}{haertester}")
L.append("=" * 62)
return "\n".join(L)
def report_md(e: dict, cfg: dict, quelldateien: list[str] | None = None) -> str:
q = e["milch_kuehllast_kWh_tag"]
wp = e["waermepumpe"]
sp = e["speicher"]
pv = e["pv"]
netz = e["netzbezug"]
bat = e["batterie"]
hh = e["haushalte"]
z = cfg["abholung"]["zyklus_tage"]
milch = e["eingang"]["milch_l_tag"]
ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
L = []
L.append("# Auslegung Waermerueckgewinnung Milchkuehlung")
L.append("")
L.append(f"**Erstellt:** {ts}")
L.append(f"**Eingang:** {milch:.0f} L Milch/Tag"
f" ({cfg['milch']['temp_ein_C']:.0f} -> {cfg['milch']['temp_aus_C']:.0f} C)")
if quelldateien:
L.append(f"**Konfiguration:** {', '.join(quelldateien)}")
L.append("")
L.append(f"**Kuehllast der Milch:** {q:.1f} kWh/Tag")
L.append("")
L.append("## Waermepumpe")
L.append(f"COP {cfg['waermepumpe']['cop']:.2f}, Laufzeit {cfg['waermepumpe']['laufzeit_h_pro_tag']:.0f} h/Tag")
L.append("")
L.append("| Kenngroesse | Wert |")
L.append("|-------------|------|")
L.append(f"| Kaelteleistung (Verdampfer) | {wp['kaelteleistung_kW']:.1f} kW |")
L.append(f"| Heizleistung (Kondensator) | {wp['heizleistung_kW']:.1f} kW |")
L.append(f"| el. Leistung | {wp['elektrische_leistung_kW']:.1f} kW |")
L.append(f"| Strombedarf | {wp['strom_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| Waerme in den Speicher | {wp['speicher_kWh_tag']:.1f} kWh/Tag |")
L.append("")
L.append("## PV-Anlage (Jahresbilanz)")
L.append("")
L.append("| Kenngroesse | Wert |")
L.append("|-------------|------|")
L.append(f"| Jahresstrombedarf WP | {pv['jahresbedarf_kWh']:.0f} kWh/a |")
L.append(f"| Leistung | {pv['kWp']:.1f} kWp |")
L.append(f"| Dachflaeche | {pv['flaeche_m2']:.0f} m2 |")
L.append("")
L.append("## Netzbezug (Wintermonate, Monatsbilanz)")
L.append("")
L.append("| Kenngroesse | Wert |")
L.append("|-------------|------|")
L.append(f"| Netzbezug | {netz['netzbezug_kWh']:.0f} kWh/a = {netz['netzbezug_anteil']*100:.0f} % |")
L.append(f"| Sommerueberschuss | {netz['ueberschuss_kWh']:.0f} kWh/a |")
L.append("")
L.append("### Monatsbilanz")
L.append("")
L.append("| Monat | Bedarf kWh | PV kWh | Zukauf kWh |")
L.append("|-------|-----------|--------|-----------|")
for monat, werte in netz["monate"].items():
L.append(f"| {monat} | {werte['bedarf_kWh']:.0f} | {werte['pv_kWh']:.0f} | {werte['zukauf_kWh']:.0f} |")
L.append("")
L.append("## Batterie (Tag-/Nacht-Ueberbbrueckung)")
L.append("")
L.append("| Kenngroesse | Wert |")
L.append("|-------------|------|")
L.append(f"| nutzbare Kapazitaet | {bat['nutzbar_kWh']:.1f} kWh |")
L.append(f"| Nennkapazitaet | {bat['nominal_kWh']:.1f} kWh |")
L.append("")
L.append(f"## Versorgte Haushalte ({cfg['haushalt']['erwachsene']} Erw. + {cfg['haushalt']['kinder']} Kinder)")
L.append("")
L.append("| Kenngroesse | Wert |")
L.append("|-------------|------|")
L.append(f"| Warmwasser je Haushalt | {hh['ww_pro_haushalt_kWh_tag']:.1f} kWh/Tag |")
L.append(f"| Warmwasser ganzjaehrig | {hh['haushalte_warmwasser']:.1f} Haushalte |")
L.append(f"| max. Heizbedarf Winter | {hh['heizbedarf_winter_kWh_tag']:.1f} kWh/Tag/HH |")
L.append(f"| Winterbedarf (Heiz+WW) | {hh['winter_pro_haushalt_kWh_tag']:.1f} kWh/Tag/HH |")
L.append(f"| im Winter | {hh['haushalte_winter']:.1f} Haushalte |")
L.append("")
passt = "ja" if sp["passt_in_einen_lkw"] else "NEIN - mehrere Behaelter noetig"
L.append(f"## Latentspeicher ({cfg['pcm']['name']}, Modus: {cfg['pcm']['modus']})")
L.append("")
L.append("| Kenngroesse | Wert |")
L.append("|-------------|------|")
L.append(f"| Energiedichte | {sp['energiedichte_kWh_pro_kg']*1000:.1f} Wh/kg |")
L.append(f"| Energie je Zyklus ({z} Tage) | {sp['energie_pro_zyklus_kWh']:.0f} kWh |")
L.append(f"| Masse | {sp['masse_kg']:.0f} kg |")
L.append(f"| Volumen | {sp['volumen_m3']:.1f} m3 |")
L.append(f"| LKW-Auslastung Gewicht | {sp['auslastung_gewicht']*100:.0f} % |")
L.append(f"| LKW-Auslastung Volumen | {sp['auslastung_volumen']*100:.0f} % |")
L.append(f"| bindende Grenze | {sp['bindende_grenze']} |")
L.append(f"| passt in einen LKW | {passt} |")
L.append(f"| voller LKW nach | {sp['voller_lkw_tausch_tage']:.1f} Tagen |")
L.append(f"| Energieinhalt voller LKW | {sp['voller_lkw_energie_kWh']:.0f} kWh |")
bk = e.get("brennstoffkosten")
if bk and bk["vergleich"]:
L.append("")
L.append("## Wert der Waerme beim Kaeufer (Ersatzkosten fossil/Holz)")
L.append("")
L.append(f"Bewertet: voller LKW ~{bk['energie_lkw_kWh']:.0f} kWh, "
f"Jahreserzeugung ~{bk['energie_jahr_kWh']:.0f} kWh")
L.append("")
L.append("| Brennstoff | ct/kWh (Nutzw.) | EUR/LKW | EUR/Jahr |")
L.append("|------------|-----------------|---------|----------|")
for v in bk["vergleich"]:
L.append(f"| {v['name']} | {v['nutzwaerme_ct_kWh']:.1f} | "
f"{v['eur_pro_lkw']:.0f} | {v['eur_pro_jahr']:.0f} |")
L.append("")
L.append("*ct/kWh = Brennstoffpreis ÷ Kesselwirkungsgrad*")
return "\n".join(L)
# report_md wurde nach lib/report_md.py ausgelagert und oben importiert.
def main() -> None:
@@ -551,6 +617,10 @@ def main() -> None:
help="Ergebnis zusaetzlich als JSON in diese Datei schreiben")
ap.add_argument("--suggest-size", action="store_true",
help="Kompakte Empfehlung (gerundete Groessen + Abholzyklus) statt Detailreport")
ap.add_argument("--eh-da", action="store_true",
help="PV und Kuehlung bereits vorhanden: Kapitalkosten dafuer entfallen")
ap.add_argument("--driver", action="store_true",
help="Fahrer stellt der Landwirt selbst: Fahreranteil aus Transportkosten abziehen")
ap.add_argument("-v", "--verbose", action="store_true",
help="Ausgaben an der Konsole anzeigen")
args = ap.parse_args()
@@ -581,7 +651,7 @@ def main() -> None:
cfg = deep_merge(cfg, lade_config(f))
quelldateien.append(f.name)
ergebnis = rechne(args.milch_l_tag, cfg)
ergebnis = rechne(args.milch_l_tag, cfg, eh_da=args.eh_da, driver=args.driver)
if args.suggest_size:
text = vorschlag_report(ergebnis, cfg)
+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()