Compare commits
12 Commits
fb111a613d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 160a0f5f7b | |||
| fc6c2abbb1 | |||
| 6c4f007c3f | |||
| f392239eb0 | |||
| 6d3d7bf55c | |||
| 0a3972a9b4 | |||
| 3f59877aa9 | |||
| 06ead41035 | |||
| d6e34b5e7c | |||
| 3276ec8d86 | |||
| 07d7a1191b | |||
| 0748b63a58 |
@@ -0,0 +1,47 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Projektübersicht
|
||||
|
||||
**Milchwaerme** ist ein Kommandozeilen-Werkzeug zur Auslegung einer Wärmerückgewinnung
|
||||
aus der Milchkühlung. Aus der täglich zu kühlenden Milchmenge werden Wärmepumpe,
|
||||
PV-Anlage, Batterie und Natriumacetat-Latentspeicher dimensioniert — inklusive
|
||||
Abhollogistik, versorgbarer Haushalte und Brennstoffkosten-Vergleich. Alle Annahmen
|
||||
liegen in einer `config.json`, sodass Varianten ohne Codeänderung nachgerechnet werden.
|
||||
|
||||
## Konzept
|
||||
|
||||
```text
|
||||
Milchabwärme (37→4 °C) ─► Wärmepumpe (PV-Strom) ─► Latentspeicher (Natriumacetat, 58 °C) ─► Abholung per LKW
|
||||
```
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```text
|
||||
Milchwaerme/
|
||||
├── bin/ # Start-Skripte (.bat/.sh): setenv, auslegung, revival, get_cmd
|
||||
├── lib/ # Python-Quellcode
|
||||
│ ├── auslegung.py # Berechnungsprogramm
|
||||
│ ├── report_md.py # Markdown-Report
|
||||
│ └── revival.py # Revival-/Zusatzberechnung
|
||||
├── cfg/ # Konfiguration (config.json mit allen Annahmen)
|
||||
├── doc/ # Dokumentation
|
||||
└── out/ # Ausgaben
|
||||
```
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Python 3.8+
|
||||
- Keine externen Abhängigkeiten (nur Standardbibliothek)
|
||||
|
||||
## Nutzung
|
||||
|
||||
```cmd
|
||||
bin\auslegung.bat
|
||||
```
|
||||
```bash
|
||||
bin/auslegung.sh
|
||||
```
|
||||
|
||||
Varianten rechnet man durch, indem man `config.json` kopiert und anpasst.
|
||||
@@ -0,0 +1,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 (~50–60 %).
|
||||
- `--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.
|
||||
@@ -0,0 +1,7 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM TEST - Shell mit gesetzten Umgebungsvariablen oeffnen
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
start cmd /k "echo TEST Umgebung aktiv. && echo DXFMAKRO=%DXFMAKRO%"
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
# ================================================================
|
||||
# TEST - Shell mit gesetzten Umgebungsvariablen oeffnen
|
||||
# ================================================================
|
||||
# Verwendung: source bin/get_cmd.sh
|
||||
# ================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/setenv.sh"
|
||||
|
||||
exec "$SHELL"
|
||||
@@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
REM Aufruf: revival.bat <kWp> [weitere Optionen]
|
||||
REM Beispiel: revival.bat 100
|
||||
REM revival.bat 250 --sonnenstunden 5.5 -v
|
||||
IF NOT DEFINED MILCH_HOME (
|
||||
SET MILCH_HOME=%~dp0..
|
||||
IF "%MILCH_HOME:~-1%"=="\" SET MILCH_HOME=%MILCH_HOME:~0,-1%
|
||||
)
|
||||
IF NOT DEFINED MILCH_LIB SET MILCH_LIB=%MILCH_HOME%\lib
|
||||
IF NOT DEFINED MILCH_CFG SET MILCH_CFG=%MILCH_HOME%\cfg
|
||||
IF NOT DEFINED MILCH_OUT SET MILCH_OUT=%MILCH_HOME%\out
|
||||
python "%MILCH_LIB%\revival.py" --config "%MILCH_CFG%\config.json" --out "%MILCH_OUT%" %*
|
||||
+150
-7
@@ -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."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
# Recherche: Kosten Waermepumpe und Waermetauscher
|
||||
|
||||
Stand: Juni 2026
|
||||
|
||||
Bezugsgroesse: PV-Revival-Szenario mit 100 kWp PV-Anlage
|
||||
-> Waermepumpe ~87 kW elektrisch / ~227 kW thermisch (COP 2.6)
|
||||
-> Waermetauscher ~272 kW (Sicherheitsfaktor 1.2x)
|
||||
|
||||
|
||||
## 1. Industrie-Waermepumpe (~227 kW thermisch / ~87 kW elektrisch)
|
||||
|
||||
In dieser Leistungsklasse handelt es sich um Industrie-/Grosswaermepumpen (>100 kW).
|
||||
Konkrete Listenpreise werden von Herstellern nicht oeffentlich publiziert — es sind
|
||||
immer Projektangebote. Aus den verfuegbaren Quellen lassen sich Richtwerte ableiten:
|
||||
|
||||
| Kennzahl | Richtwert |
|
||||
|-----------------------------------|----------------------------|
|
||||
| Spezifische Kosten (Geraet) | 350-500 EUR/kW thermisch |
|
||||
| Investition WP ~227 kW th. | ca. 80.000-115.000 EUR |
|
||||
| Inkl. Montage, Verrohrung, IBN | ca. 100.000-140.000 EUR |
|
||||
|
||||
Der Installationsaufschlag (Montage, Verrohrung, Inbetriebnahme) betraegt
|
||||
typischerweise ca. +25 % auf den Geraetepreis.
|
||||
|
||||
### Hersteller in dieser Leistungsklasse
|
||||
|
||||
| Hersteller | Land | Serie / Bemerkung |
|
||||
|--------------------------|------|------------------------------------------|
|
||||
| Ochsner | AT | GMLW-Serie, bis 2.500 kW, Industriefokus|
|
||||
| Viessmann / Carrier | DE | Vitocal 350 Pro, Grossanlagen |
|
||||
| Daikin | JP | VRV / Altherma Hochleistung |
|
||||
| MAN Energy Solutions | DE | Hochtemperatur-Industriewaermepumpen |
|
||||
| Combitherm / Friotherm | CH | Spezialisten fuer >200 kW |
|
||||
|
||||
### Referenzwert
|
||||
|
||||
Eine Viessmann Vitocal 250-A mit 48 kW kostet ca. 44.000 EUR (~917 EUR/kW).
|
||||
Bei Grossanlagen sinken die spezifischen Kosten auf 350-500 EUR/kW durch
|
||||
Skaleneffekte.
|
||||
|
||||
|
||||
## 2. Waermetauscher (~272 kW Ladeleistung)
|
||||
|
||||
Plattenwaermetauscher (PHE) in dieser Leistungsklasse sind deutlich guenstiger
|
||||
als die Waermepumpe selbst.
|
||||
|
||||
| Typ | Leistung | Preis |
|
||||
|------------------------------------------|------------|-----------------|
|
||||
| Geloeteter PHE (Standard) | 250-300 kW | 300-800 EUR |
|
||||
| Geschraubter PHE (Edelstahl, zerlegbar) | 300 kW | 1.500-3.000 EUR |
|
||||
| EHEDG/Lebensmittel-PHE (Alfa Laval, GEA)| 300 kW | 3.000-5.000 EUR |
|
||||
|
||||
Fuer die Ladeseite (WP -> Speicher, 80 C) empfiehlt sich ein geschraubter
|
||||
Edelstahl-PHE wegen Wartbarkeit bei hohen Temperaturen: ca. 2.000-3.000 EUR.
|
||||
|
||||
### Anbieter / Shops
|
||||
|
||||
- Satura: geschraubte Edelstahl-PHE bis 300 kW
|
||||
- Inocal: Fernwaerme-PHE PBU10-120, 300 kW
|
||||
- Alfa Laval: CB/AlfaNova Kompakt-PHE (Industrie)
|
||||
- GEA / Kelvion: NT-Serie, NH-Serie
|
||||
- SWEP: B28-Serie, 50-300 kW
|
||||
|
||||
|
||||
## 3. Gesamtkosten-Einordnung (100 kWp Revival-System)
|
||||
|
||||
| Komponente | Richtwert |
|
||||
|--------------------------------------|------------------------|
|
||||
| Waermepumpe ~227 kW th., installiert | 100.000-140.000 EUR |
|
||||
| Waermetauscher Lade + Entlade | 4.000-6.000 EUR |
|
||||
| **Summe WP + WT** | **~105.000-145.000 EUR**|
|
||||
|
||||
Der Waermetauscher ist ein kleiner Posten — die Waermepumpe dominiert die
|
||||
Investition.
|
||||
|
||||
Bei einer Jahres-Waermeproduktion von ~260.000 kWh und Gestehungskosten von
|
||||
3,5 ct/kWh bleibt eine gute Marge gegenueber fossilen Brennstoffen (8-13 ct/kWh).
|
||||
|
||||
|
||||
## Quellen
|
||||
|
||||
- Bosch Grosswaermepumpe: https://www.bosch-homecomfort.com/de/de/wohngebaeude/wissen/heizungsratgeber/waermepumpe/grosswaermepumpe/
|
||||
- Ochsner High Capacity Heat Pumps: https://www.ochsner.com/en/ochsner-products/high-capacity-heat-pumps/
|
||||
- Ochsner Industriewaermepumpen (PDF): https://ochsner-energietechnik.com/wp-content/uploads/2019/08/Industriew%C3%A4rmepumpen_DE_2019_ET06-022019_kl.pdf
|
||||
- Viessmann Community - Spezifische Investitionskosten: https://community.viessmann.de/t5/Waermepumpe-Hybridsysteme/Spezifische-investitionskosten-von-Waermepumpen/td-p/125149
|
||||
- Satura: Geschraubter PHE 300 kW Edelstahl: https://satura-shop.com/geschraubter-Plattenwaermetauscher-aus-Edelstahl-300-kW-bei-70C
|
||||
- Inocal PHE 300 kW Fernwaerme: https://www.inocal.com/c/ola1-282/a/ola1-14841/INOCAL-Plattenwaermetauscher/Inocal-Edelstahl-Plattenwaermetauscher-PBU10-120-fuer-Fernwaerme-300-kW.html
|
||||
- Energie-Experten: WP-Kosten Kennzahlen: https://www.energie-experten.org/heizung/waermepumpe/waermepumpenheizung/kosten
|
||||
@@ -0,0 +1,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 +10–30 %). 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):**
|
||||
- ~18–20 % Strom (~180–200 W/m²)
|
||||
- ~800 W/m² Wärme, davon über die Frontseite ein großer Teil verloren
|
||||
- Unverglaster PVT, niedrige Vorlauftemperatur: **~400–500 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.500–1.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).
|
||||
@@ -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 (Okt–Feb) | ~1.940 |
|
||||
| Sommerüberschuss (Mär–Sep) | ~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 50–60 %, 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 | ~14–16 kWh |
|
||||
| empfohlen nutzbar | ~15–18 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 (45–60 °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.
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -0,0 +1,449 @@
|
||||
"""Markdown-Reporterzeugung fuer die Waermerueckgewinnung-Auslegung.
|
||||
|
||||
Einziger oeffentlicher Einstiegspunkt: report_md(e, cfg, quelldateien).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
def _h2(title: str) -> list[str]:
|
||||
return ["", f"## {title}", ""]
|
||||
|
||||
|
||||
def _h3(title: str) -> list[str]:
|
||||
return [f"### {title}", ""]
|
||||
|
||||
|
||||
def _tbl_header(cols: list[str]) -> list[str]:
|
||||
return [" | ".join(cols),
|
||||
" | ".join(["---"] * len(cols))]
|
||||
|
||||
|
||||
def _code(lines: list[str]) -> list[str]:
|
||||
return ["```"] + lines + ["```", ""]
|
||||
|
||||
|
||||
def report_md(e: dict, cfg: dict, quelldateien: list[str] | None = None) -> str:
|
||||
"""Vollstaendiger Markdown-Report: je Block Voraussetzungen → Formeln → Ergebnisse."""
|
||||
q = e["milch_kuehllast_kWh_tag"]
|
||||
wp = e["waermepumpe"]
|
||||
sp = e["speicher"]
|
||||
pv = e["pv"]
|
||||
netz = e["netzbezug"]
|
||||
bat = e["batterie"]
|
||||
hh = e["haushalte"]
|
||||
m_cfg = cfg["milch"]
|
||||
wp_cfg = cfg["waermepumpe"]
|
||||
pcm_cfg = cfg["pcm"]
|
||||
abh_cfg = cfg["abholung"]
|
||||
pv_cfg = cfg["pv"]
|
||||
bat_cfg = cfg["batterie"]
|
||||
hh_cfg = cfg["haushalt"]
|
||||
z = abh_cfg["zyklus_tage"]
|
||||
milch = e["eingang"]["milch_l_tag"]
|
||||
ts = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
L = []
|
||||
L.append("# Auslegung Waermerueckgewinnung Milchkuehlung")
|
||||
L.append("")
|
||||
L.append(f"**Erstellt:** {ts}")
|
||||
if quelldateien:
|
||||
L.append(f"**Konfiguration:** {', '.join(quelldateien)}")
|
||||
|
||||
# ── Milchkühllast ────────────────────────────────────────────────
|
||||
L.extend(_h2("Milchkuehllast"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| Milchmenge | {milch:.0f} L/Tag |")
|
||||
L.append(f"| Eingangstemperatur | {m_cfg['temp_ein_C']:.0f} C (Eutertemperatur) |")
|
||||
L.append(f"| Ausgangstemperatur | {m_cfg['temp_aus_C']:.0f} C (Lagertemperatur) |")
|
||||
L.append(f"| Dichte | {m_cfg['dichte_kg_pro_L']:.2f} kg/L |")
|
||||
L.append(f"| Spez. Waerme | {m_cfg['spez_waerme_kJ_pro_kgK']:.3f} kJ/(kg·K) |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"Q = V × ρ × c × ΔT / 3600 [kWh/Tag]",
|
||||
" = (Milchmenge × Dichte) × spez_Waerme × (T_ein − T_aus) / 3600",
|
||||
"",
|
||||
f" = ({milch:.0f} L/Tag × {m_cfg['dichte_kg_pro_L']:.2f} kg/L)"
|
||||
f" × {m_cfg['spez_waerme_kJ_pro_kgK']:.3f} kJ/(kg·K)"
|
||||
f" × ({m_cfg['temp_ein_C']:.0f} − {m_cfg['temp_aus_C']:.0f}) K / 3600",
|
||||
f" = {q:.1f} kWh/Tag",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
|
||||
L.append(f"| Taegl. Kuehllast | **{q:.1f} kWh/Tag** |")
|
||||
|
||||
# ── Wärmepumpe ───────────────────────────────────────────────────
|
||||
L.extend(_h2("Waermepumpe"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| COP (Coefficient of Performance) | {wp_cfg['cop']:.2f} |")
|
||||
L.append(f"| Laufzeit | {wp_cfg['laufzeit_h_pro_tag']:.0f} h/Tag |")
|
||||
L.append(f"| Kuehllast (aus Milchblock) | {q:.1f} kWh/Tag |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"Leistungsauslegung aus Kuehllast und Laufzeit:",
|
||||
" P_Kaelte = Q_Tag / Laufzeit [kW] (Verdampfer)",
|
||||
" P_Heiz = P_Kaelte × COP / (COP − 1) [kW] (Kondensator)",
|
||||
" P_el = P_Kaelte / (COP − 1) [kW] (Antrieb)",
|
||||
"",
|
||||
"Tageswerte:",
|
||||
" W_el = P_el × Laufzeit [kWh/Tag]",
|
||||
" Q_Speicher = P_Heiz × Laufzeit [kWh/Tag]",
|
||||
"",
|
||||
f" P_Kaelte = {q:.1f} / {wp_cfg['laufzeit_h_pro_tag']:.0f}"
|
||||
f" = {wp['kaelteleistung_kW']:.1f} kW",
|
||||
f" P_Heiz = {wp['kaelteleistung_kW']:.1f}"
|
||||
f" × {wp_cfg['cop']:.2f} / ({wp_cfg['cop']:.2f} − 1)"
|
||||
f" = {wp['heizleistung_kW']:.1f} kW",
|
||||
f" P_el = {wp['kaelteleistung_kW']:.1f}"
|
||||
f" / ({wp_cfg['cop']:.2f} − 1)"
|
||||
f" = {wp['elektrische_leistung_kW']:.1f} kW",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
|
||||
L.append(f"| Kaelteleistung (Verdampfer) | {wp['kaelteleistung_kW']:.1f} kW |")
|
||||
L.append(f"| Heizleistung (Kondensator) | {wp['heizleistung_kW']:.1f} kW |")
|
||||
L.append(f"| el. Leistung | {wp['elektrische_leistung_kW']:.1f} kW |")
|
||||
L.append(f"| Strombedarf | **{wp['strom_kWh_tag']:.1f} kWh/Tag** |")
|
||||
L.append(f"| Waerme in den Speicher | **{wp['speicher_kWh_tag']:.1f} kWh/Tag** |")
|
||||
|
||||
# ── PV-Anlage ────────────────────────────────────────────────────
|
||||
L.extend(_h2("PV-Anlage (Jahresbilanz)"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| Strombedarf WP (aus WP-Block) | {wp['strom_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| Spez. Jahresertrag | {pv_cfg['spez_ertrag_kWh_pro_kWp_jahr']} kWh/(kWp·a) |")
|
||||
L.append(f"| Dachflaeche je kWp | {pv_cfg['flaeche_m2_pro_kWp']:.1f} m2/kWp |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"Jahresbedarf = W_el_Tag × 365 [kWh/a]",
|
||||
"kWp = Jahresbedarf / spez_Ertrag [kWp]",
|
||||
"Flaeche = kWp × Flaeche_je_kWp [m²]",
|
||||
"",
|
||||
f"kWp = {pv['jahresbedarf_kWh']:.0f} kWh/a"
|
||||
f" / {pv_cfg['spez_ertrag_kWh_pro_kWp_jahr']} kWh/(kWp·a)"
|
||||
f" = {pv['kWp']:.1f} kWp",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
|
||||
L.append(f"| Jahresstrombedarf WP | {pv['jahresbedarf_kWh']:.0f} kWh/a |")
|
||||
L.append(f"| PV-Leistung | **{pv['kWp']:.1f} kWp** |")
|
||||
L.append(f"| Dachflaeche | **{pv['flaeche_m2']:.0f} m2** |")
|
||||
|
||||
# ── Netzbezug ────────────────────────────────────────────────────
|
||||
L.extend(_h2("Netzbezug (Wintermonate, Monatsbilanz)"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| PV-Leistung (aus PV-Block) | {pv['kWp']:.1f} kWp |")
|
||||
L.append(f"| Strombedarf WP | {wp['strom_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| Monatsprofil | Jan {pv_cfg['monatsertrag_kWh_pro_kWp']['Jan']},"
|
||||
f" Feb {pv_cfg['monatsertrag_kWh_pro_kWp']['Feb']},"
|
||||
f" ... Dez {pv_cfg['monatsertrag_kWh_pro_kWp']['Dez']} kWh/kWp |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"je Monat (n = Tage im Monat):",
|
||||
" PV_Monat = kWp × Monatsertrag_kWh_pro_kWp [kWh]",
|
||||
" Bedarf_Monat = W_el_Tag × n [kWh]",
|
||||
" Zukauf = max(0, Bedarf_Monat − PV_Monat) [kWh]",
|
||||
" Ueberschuss = max(0, PV_Monat − Bedarf_Monat) [kWh]",
|
||||
"",
|
||||
"Jahreswerte:",
|
||||
" Netzbezug = Σ Zukauf [kWh/a]",
|
||||
" Anteil = Netzbezug / Jahresbedarf [%]",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
|
||||
L.append(f"| Netzbezug | **{netz['netzbezug_kWh']:.0f} kWh/a = {netz['netzbezug_anteil']*100:.0f} %** |")
|
||||
L.append(f"| Sommerueberschuss | {netz['ueberschuss_kWh']:.0f} kWh/a |")
|
||||
L.append("")
|
||||
L.append("#### Monatsbilanz")
|
||||
L.append("")
|
||||
L.extend(_tbl_header(["Monat", "Bedarf kWh", "PV kWh", "Zukauf kWh"]))
|
||||
for monat, werte in netz["monate"].items():
|
||||
L.append(f"| {monat} | {werte['bedarf_kWh']:.0f} | {werte['pv_kWh']:.0f}"
|
||||
f" | {werte['zukauf_kWh']:.0f} |")
|
||||
|
||||
# ── Batterie ─────────────────────────────────────────────────────
|
||||
L.extend(_h2("Batterie (Tag-/Nacht-Ueberbrueckung)"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| Strombedarf WP | {wp['strom_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| Direkt-PV-Stunden (WP laeuft direkt aus PV) | {bat_cfg['direkt_pv_stunden']:.0f} h/Tag |")
|
||||
L.append(f"| Nutzbarer Anteil (Entladetiefe LFP) | {bat_cfg['nutzbar_anteil']*100:.0f} % |")
|
||||
L.append(f"| Reservefaktor | {bat_cfg['reserve_faktor']:.1f} |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"Pufferbedarf = W_el × (24 − Direkt_PV_h) / 24 [kWh]",
|
||||
" (Anteil des Tages ohne direkten PV-Strom)",
|
||||
"C_nutzbar = Pufferbedarf × Reservefaktor [kWh]",
|
||||
"C_nominal = C_nutzbar / Entladetiefe_DoD [kWh]",
|
||||
"",
|
||||
f"Pufferbedarf = {wp['strom_kWh_tag']:.1f}"
|
||||
f" × (24 − {bat_cfg['direkt_pv_stunden']:.0f}) / 24"
|
||||
f" = {wp['strom_kWh_tag'] * (24 - bat_cfg['direkt_pv_stunden']) / 24:.1f} kWh",
|
||||
f"C_nutzbar = … × {bat_cfg['reserve_faktor']:.1f}"
|
||||
f" = {bat['nutzbar_kWh']:.1f} kWh",
|
||||
f"C_nominal = {bat['nutzbar_kWh']:.1f}"
|
||||
f" / {bat_cfg['nutzbar_anteil']:.1f}"
|
||||
f" = {bat['nominal_kWh']:.1f} kWh",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
|
||||
L.append(f"| Nutzbare Kapazitaet | {bat['nutzbar_kWh']:.1f} kWh |")
|
||||
L.append(f"| Nennkapazitaet | **{bat['nominal_kWh']:.1f} kWh** |")
|
||||
|
||||
# ── Latentspeicher ───────────────────────────────────────────────
|
||||
passt = "ja" if sp["passt_in_einen_lkw"] else "NEIN - mehrere Behaelter noetig"
|
||||
L.extend(_h2(f"Latentspeicher ({pcm_cfg['name']})"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| PCM | {pcm_cfg['name']} |")
|
||||
L.append(f"| Schmelzpunkt | {pcm_cfg['schmelzpunkt_C']:.0f} C |")
|
||||
L.append(f"| Latentwaerme | {pcm_cfg['latentwaerme_kJ_pro_kg']:.0f} kJ/kg |")
|
||||
L.append(f"| Dichte | {pcm_cfg['dichte_kg_pro_L']:.1f} kg/L |")
|
||||
L.append(f"| Modus | {pcm_cfg['modus']} |")
|
||||
if pcm_cfg["modus"] == "latent_plus_hub":
|
||||
L.append(f"| Ladetemperatur | {pcm_cfg['ladetemp_C']:.0f} C |")
|
||||
L.append(f"| Entladetemperatur | {pcm_cfg['entladetemp_C']:.0f} C |")
|
||||
L.append(f"| cp fluessig | {pcm_cfg['cp_fluessig_kJ_pro_kgK']:.1f} kJ/(kg·K) |")
|
||||
L.append(f"| cp fest | {pcm_cfg['cp_fest_kJ_pro_kgK']:.1f} kJ/(kg·K) |")
|
||||
L.append(f"| Abholzyklus | {z} Tage |")
|
||||
L.append(f"| LKW-Nutzlast | {abh_cfg['lkw_nutzlast_kg']:,} kg |")
|
||||
L.append(f"| LKW-Volumen | {abh_cfg['lkw_volumen_m3']} m3 |")
|
||||
L.append(f"| Waerme je Tag (aus WP-Block) | {wp['speicher_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
if pcm_cfg["modus"] == "latent_plus_hub":
|
||||
dt_uh = max(0.0, pcm_cfg["ladetemp_C"] - pcm_cfg["schmelzpunkt_C"])
|
||||
dt_uk = max(0.0, pcm_cfg["schmelzpunkt_C"] - pcm_cfg["entladetemp_C"])
|
||||
e_kj = (pcm_cfg["latentwaerme_kJ_pro_kg"]
|
||||
+ pcm_cfg["cp_fluessig_kJ_pro_kgK"] * dt_uh
|
||||
+ pcm_cfg["cp_fest_kJ_pro_kgK"] * dt_uk)
|
||||
L.extend(_code([
|
||||
"Energiedichte (Modus latent_plus_hub):",
|
||||
" E = (L + cp_fl · ΔT_üh + cp_fest · ΔT_uk) / 3600 [kWh/kg]",
|
||||
" mit ΔT_üh = max(0, T_Lade − T_Schmelz) (Ueberhitzung, fluessig)",
|
||||
" ΔT_uk = max(0, T_Schmelz − T_Entlade) (Unterkuehlung, fest)",
|
||||
"",
|
||||
f" ΔT_üh = max(0, {pcm_cfg['ladetemp_C']:.0f} − {pcm_cfg['schmelzpunkt_C']:.0f})"
|
||||
f" = {dt_uh:.0f} K",
|
||||
f" ΔT_uk = max(0, {pcm_cfg['schmelzpunkt_C']:.0f} − {pcm_cfg['entladetemp_C']:.0f})"
|
||||
f" = {dt_uk:.0f} K",
|
||||
f" E = ({pcm_cfg['latentwaerme_kJ_pro_kg']:.0f}"
|
||||
f" + {pcm_cfg['cp_fluessig_kJ_pro_kgK']:.1f}·{dt_uh:.0f}"
|
||||
f" + {pcm_cfg['cp_fest_kJ_pro_kgK']:.1f}·{dt_uk:.0f}) / 3600"
|
||||
f" = {e_kj:.1f} / 3600"
|
||||
f" = {sp['energiedichte_kWh_pro_kg']*1000:.1f} Wh/kg",
|
||||
"",
|
||||
"Speichergroesse:",
|
||||
" E_Zyklus = Q_Speicher_Tag × Zyklus [kWh]",
|
||||
" m = E_Zyklus / E [kg]",
|
||||
" V = m / (ρ × 1000) [m³]",
|
||||
"",
|
||||
f" E_Zyklus = {wp['speicher_kWh_tag']:.1f} kWh/Tag × {z} Tage"
|
||||
f" = {sp['energie_pro_zyklus_kWh']:.0f} kWh",
|
||||
f" m = {sp['energie_pro_zyklus_kWh']:.0f}"
|
||||
f" / {sp['energiedichte_kWh_pro_kg']:.4f}"
|
||||
f" = {sp['masse_kg']:.0f} kg",
|
||||
f" V = {sp['masse_kg']:.0f}"
|
||||
f" / ({pcm_cfg['dichte_kg_pro_L']:.1f} × 1000)"
|
||||
f" = {sp['volumen_m3']:.1f} m³",
|
||||
]))
|
||||
else:
|
||||
L.extend(_code([
|
||||
"Energiedichte (Modus latent_only):",
|
||||
" E = L / 3600 [kWh/kg]",
|
||||
"",
|
||||
"Speichergroesse:",
|
||||
" E_Zyklus = Q_Speicher_Tag × Zyklus [kWh]",
|
||||
" m = E_Zyklus / E [kg]",
|
||||
" V = m / (ρ × 1000) [m³]",
|
||||
"",
|
||||
f" E_Zyklus = {wp['speicher_kWh_tag']:.1f} × {z}"
|
||||
f" = {sp['energie_pro_zyklus_kWh']:.0f} kWh",
|
||||
f" m = {sp['energie_pro_zyklus_kWh']:.0f}"
|
||||
f" / {sp['energiedichte_kWh_pro_kg']:.4f}"
|
||||
f" = {sp['masse_kg']:.0f} kg",
|
||||
f" V = {sp['masse_kg']:.0f}"
|
||||
f" / ({pcm_cfg['dichte_kg_pro_L']:.1f} × 1000)"
|
||||
f" = {sp['volumen_m3']:.1f} m³",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
|
||||
L.append(f"| Energiedichte | {sp['energiedichte_kWh_pro_kg']*1000:.1f} Wh/kg |")
|
||||
L.append(f"| Energie je Zyklus ({z} Tage) | {sp['energie_pro_zyklus_kWh']:.0f} kWh |")
|
||||
L.append(f"| Masse | **{sp['masse_kg']:.0f} kg** |")
|
||||
L.append(f"| Volumen | **{sp['volumen_m3']:.1f} m3** |")
|
||||
L.append(f"| LKW-Auslastung Gewicht | {sp['auslastung_gewicht']*100:.0f} % |")
|
||||
L.append(f"| LKW-Auslastung Volumen | {sp['auslastung_volumen']*100:.0f} % |")
|
||||
L.append(f"| Bindende Grenze | {sp['bindende_grenze']} |")
|
||||
L.append(f"| Passt in einen LKW | {passt} |")
|
||||
L.append(f"| Voller LKW nach | {sp['voller_lkw_tausch_tage']:.1f} Tagen |")
|
||||
L.append(f"| Energieinhalt voller LKW | {sp['voller_lkw_energie_kWh']:.0f} kWh |")
|
||||
if sp.get("empf_zyklus_tage") is not None:
|
||||
L.append("")
|
||||
L.append(f"**empfohlene Zyklenzahl = {sp['empf_zyklus_tage']} Tage**"
|
||||
f" (ergibt ≥ 90 % LKW-Auslastung)")
|
||||
|
||||
# ── Haushalte ────────────────────────────────────────────────────
|
||||
L.extend(_h2(f"Versorgte Haushalte"
|
||||
f" ({hh_cfg['erwachsene']} Erw. + {hh_cfg['kinder']} Kinder)"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| Erwachsene je Haushalt | {hh_cfg['erwachsene']} |")
|
||||
L.append(f"| Kinder je Haushalt | {hh_cfg['kinder']} |")
|
||||
L.append(f"| WW-Bedarf Erwachsener | {hh_cfg['ww_erwachsener_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| WW-Bedarf Kind | {hh_cfg['ww_kind_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| Heizbedarf Winter | {hh_cfg['heizbedarf_winter_kWh_tag']:.0f} kWh/Tag/HH |")
|
||||
L.append(f"| Waerme je Tag (aus WP-Block) | {wp['speicher_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"WW_je_HH = n_Erw × WW_Erw + n_Kind × WW_Kind [kWh/Tag]",
|
||||
"HH_WW = Q_Speicher / WW_je_HH [Haushalte, ganzjaehrig]",
|
||||
"HH_Winter = Q_Speicher / (WW_je_HH + Heiz_Winter) [Haushalte, Winter]",
|
||||
"",
|
||||
f"WW_je_HH = {hh_cfg['erwachsene']} × {hh_cfg['ww_erwachsener_kWh_tag']:.1f}"
|
||||
f" + {hh_cfg['kinder']} × {hh_cfg['ww_kind_kWh_tag']:.1f}"
|
||||
f" = {hh['ww_pro_haushalt_kWh_tag']:.1f} kWh/Tag",
|
||||
f"HH_WW = {wp['speicher_kWh_tag']:.1f}"
|
||||
f" / {hh['ww_pro_haushalt_kWh_tag']:.1f}"
|
||||
f" = {hh['haushalte_warmwasser']:.1f} HH",
|
||||
f"HH_Winter = {wp['speicher_kWh_tag']:.1f}"
|
||||
f" / {hh['winter_pro_haushalt_kWh_tag']:.1f}"
|
||||
f" = {hh['haushalte_winter']:.1f} HH",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(["Kenngroesse", "Wert"]))
|
||||
L.append(f"| WW-Bedarf je Haushalt | {hh['ww_pro_haushalt_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| Winterbedarf je Haushalt (Heiz+WW) | {hh['winter_pro_haushalt_kWh_tag']:.1f} kWh/Tag |")
|
||||
L.append(f"| Versorgte HH (Warmwasser, ganzjaehrig) | **{hh['haushalte_warmwasser']:.1f}** |")
|
||||
L.append(f"| Versorgte HH (Heizung + WW, Winter) | **{hh['haushalte_winter']:.1f}** |")
|
||||
|
||||
# ── Wert beim Käufer ─────────────────────────────────────────────
|
||||
bk = e.get("brennstoffkosten")
|
||||
if bk and bk["vergleich"]:
|
||||
L.extend(_h2("Wert der Waerme beim Kaeufer (Ersatzkosten fossil/Holz)"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Brennstoff", "Preis ct/kWh", "Kesselwirkungsgrad"]))
|
||||
for b in cfg.get("brennstoffe", []):
|
||||
L.append(f"| {b['name']} | {b['preis_ct_kWh']:.1f} | {b['wirkungsgrad']*100:.0f} % |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"Nutzwaermekosten = Preis_ct_kWh / Wirkungsgrad [ct/kWh]",
|
||||
"EUR_je_LKW = E_LKW_kWh × Nutzwaermekosten / 100 [EUR]",
|
||||
"EUR_je_Jahr = E_Jahr_kWh × Nutzwaermekosten / 100 [EUR/a]",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.append(f"Bewertet: voller LKW **{bk['energie_lkw_kWh']:.0f} kWh**,"
|
||||
f" Jahreserzeugung **{bk['energie_jahr_kWh']:.0f} kWh/a**")
|
||||
L.append("")
|
||||
L.extend(_tbl_header(["Brennstoff", "ct/kWh (Nutzw.)", "EUR/LKW", "EUR/Jahr"]))
|
||||
for v in bk["vergleich"]:
|
||||
L.append(f"| {v['name']} | {v['nutzwaerme_ct_kWh']:.1f} |"
|
||||
f" {v['eur_pro_lkw']:.0f} | {v['eur_pro_jahr']:.0f} |")
|
||||
|
||||
# ── Gestehungskosten Anbieter ────────────────────────────────────
|
||||
ak = e.get("anbieter")
|
||||
if ak:
|
||||
inv = cfg["anbieter"]["invest"]
|
||||
ci = ak["container_invest"]
|
||||
sd = ak["kosten_strom_detail"]
|
||||
L.extend(_h2("Waerme-Gestehungskosten Anbieter"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| Transport je Fuhre | {cfg['anbieter']['transport_eur_pro_fuhre']:.0f} EUR |")
|
||||
L.append(f"| Netzstrompreis | {cfg['anbieter']['netzstrompreis_ct_kWh']:.0f} ct/kWh |")
|
||||
L.append(f"| PV-Gestehungskosten (LCOE) | {cfg['anbieter']['pv_gestehung_ct_kWh']:.0f} ct/kWh |")
|
||||
L.append(f"| Wartung pauschal | {cfg['anbieter']['wartung_eur_pro_jahr']:.0f} EUR/a |")
|
||||
L.append(f"| PV-Invest | {inv['pv_eur_pro_kWp']} EUR/kWp,"
|
||||
f" {inv['pv_nutzungsdauer_jahre']} J Nutzung |")
|
||||
L.append(f"| Batterie-Invest | {inv['batterie_eur_pro_kWh']} EUR/kWh,"
|
||||
f" {inv['batterie_nutzungsdauer_jahre']} J |")
|
||||
L.append(f"| WP-Invest | {inv['waermepumpe_eur']:,} EUR,"
|
||||
f" {inv['waermepumpe_nutzungsdauer_jahre']} J |")
|
||||
L.append(f"| Speicher-Invest | {ci['anzahl_module']} Module,"
|
||||
f" {inv['speicher_nutzungsdauer_jahre']} J Nutzung |")
|
||||
if ak["eh_da"]:
|
||||
L.append("| ~~Kapital PV~~ | *eh-da: bereits vorhanden, entfaellt* |")
|
||||
L.append("| ~~Kapital WP~~ | *eh-da: bereits vorhanden, entfaellt* |")
|
||||
if ak["driver"]:
|
||||
L.append(f"| Fahrerabzug | {ak['fahrer_abzug_eur_pro_fuhre']:.0f} EUR/Fuhre"
|
||||
f" (--driver: Betreiber oder Landwirt faehrt selbst) |")
|
||||
L.append("")
|
||||
L.append("**Aufschluesselung Speicher-Invest**")
|
||||
L.append("")
|
||||
L.extend(_tbl_header(["Komponente", "EUR (alle Module)"]))
|
||||
L.append(f"| PCM ({sp['masse_kg']:.0f} kg × {ci['anzahl_module']} Module)"
|
||||
f" | {ci['pcm_eur']:,.0f} |")
|
||||
L.append(f"| Behaelter (isoliert, LKW-tauglich) | {ci['behaelter_eur']:,.0f} |")
|
||||
L.append(f"| Waermetauscher + Hydraulik | {ci['wt_hydraulik_eur']:,.0f} |")
|
||||
L.append(f"| Elektronik / Regelung | {ci['elektronik_eur']:,.0f} |")
|
||||
L.append(f"| Montage + Inbetriebnahme | {ci['montage_eur']:,.0f} |")
|
||||
L.append(f"| **Gesamt** | **{ci['gesamt_eur']:,.0f}** |")
|
||||
L.append(f"| entspricht | {ci['eur_pro_kwh_kapazitaet']:.0f} EUR/kWh Speicherkapazitaet |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"Kapital_X = Invest_X / Nutzungsdauer_X [EUR/a] (lineare AfA)",
|
||||
"Kosten_Transport = Fuhren_Jahr × Kosten_je_Fuhre [EUR/a]",
|
||||
"Kosten_Strom = kWh_Netz × p_Netz + kWh_PV × p_LCOE [EUR/a]",
|
||||
"Jahreskosten = Σ(alle Kostenblöcke) [EUR/a]",
|
||||
"",
|
||||
"Gestehungskosten = Jahreskosten / Energie_Jahr × 100 [ct/kWh]",
|
||||
"",
|
||||
f"Gestehungskosten = {ak['jahreskosten_eur']:.0f} EUR/a"
|
||||
f" / {ak['energie_jahr_kWh']:.0f} kWh/a × 100"
|
||||
f" = {ak['gestehungskosten_ct_kWh']:.1f} ct/kWh",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(["Kostenblock", "EUR/Jahr"]))
|
||||
L.append(f"| Transport ({ak['fuhren_jahr']:.0f} Fuhren/a)"
|
||||
f" | {ak['kosten_transport_eur']:.0f} |")
|
||||
L.append(f"| WP-Strom ({sd['netz_kWh']:.0f} kWh Netz"
|
||||
f" + {sd['pv_kWh']:.0f} kWh PV-Eigen) | {ak['kosten_strom_eur']:.0f} |")
|
||||
L.append(f"| Wartung | {ak['kosten_wartung_eur']:.0f} |")
|
||||
L.append(f"| Kapital PV ({pv['kWp']:.1f} kWp) | {ak['kapital_pv_eur']:.0f} |")
|
||||
L.append(f"| Kapital Batterie ({bat['nominal_kWh']:.0f} kWh)"
|
||||
f" | {ak['kapital_batterie_eur']:.0f} |")
|
||||
L.append(f"| Kapital Waermepumpe | {ak['kapital_waermepumpe_eur']:.0f} |")
|
||||
L.append(f"| Kapital Speicher ({ci['anzahl_module']} Module)"
|
||||
f" | {ak['kapital_speicher_eur']:.0f} |")
|
||||
L.append(f"| **Jahreskosten gesamt** | **{ak['jahreskosten_eur']:.0f}** |")
|
||||
L.append("")
|
||||
L.append(f"**Jahreserzeugung:** {ak['energie_jahr_kWh']:.0f} kWh/a")
|
||||
L.append("")
|
||||
L.append(f"**Mindestverkaufspreis: {ak['gestehungskosten_ct_kWh']:.1f} ct/kWh**")
|
||||
|
||||
# ── Marge ────────────────────────────────────────────────────
|
||||
if ak["marge"]:
|
||||
L.extend(_h2("Marge gegen Ersatzbrennstoff"))
|
||||
L.extend(_h3("Voraussetzungen"))
|
||||
L.extend(_tbl_header(["Parameter", "Wert"]))
|
||||
L.append(f"| Mindestverkaufspreis (aus Gestehungskosten)"
|
||||
f" | {ak['gestehungskosten_ct_kWh']:.1f} ct/kWh |")
|
||||
for m in ak["marge"]:
|
||||
L.append(f"| Nutzwaerme {m['name']} | {m['nutzwaerme_ct_kWh']:.1f} ct/kWh |")
|
||||
L.append("")
|
||||
L.extend(_h3("Formeln"))
|
||||
L.extend(_code([
|
||||
"Marge_ct = Nutzwaerme_Brennstoff − Gestehungskosten [ct/kWh]",
|
||||
" Positiv = System kostendeckend gegenueber diesem Brennstoff",
|
||||
"Marge_EUR = Marge_ct × Energie_Jahr / 100 [EUR/a]",
|
||||
]))
|
||||
L.extend(_h3("Ergebnisse"))
|
||||
L.extend(_tbl_header(
|
||||
["Brennstoff", "ct/kWh (Nutzw.)", "Marge ct/kWh", "Marge EUR/Jahr"]))
|
||||
for m in ak["marge"]:
|
||||
L.append(f"| {m['name']} | {m['nutzwaerme_ct_kWh']:.1f} |"
|
||||
f" {m['marge_ct_kWh']:+.1f} | {m['marge_eur_jahr']:+.0f} |")
|
||||
|
||||
return "\n".join(L)
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Revival grosser PV-Anlagen: Strom als Waerme weiterverkaufen.
|
||||
|
||||
Berechnet fuer eine gegebene PV-Anlagengroesse (kWp), welche Waermemenge
|
||||
ueber eine Waermepumpe produziert werden kann und welche Leistung
|
||||
Waermepumpe und Waermetauscher haben muessen, um den taeglichen PV-Strom
|
||||
vollstaendig in Waerme umzuwandeln.
|
||||
|
||||
Aufruf:
|
||||
python revival.py 100
|
||||
python revival.py 250 --config cfg/config.json -v
|
||||
python revival.py 500 --sonnenstunden 5.5
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
def t(wert: float, nachkomma: int = 0) -> str:
|
||||
"""Zahl mit Hochkomma als Tausendertrenner formatieren."""
|
||||
if nachkomma > 0:
|
||||
s = f"{wert:,.{nachkomma}f}"
|
||||
else:
|
||||
s = f"{wert:,.0f}"
|
||||
return s.replace(",", "'")
|
||||
|
||||
|
||||
KJ_PER_KWH = 3600.0
|
||||
TAGE_PRO_JAHR = 365.0
|
||||
|
||||
PROJECT_ROOT = Path(__file__).parent.parent
|
||||
|
||||
MONATSTAGE = {
|
||||
"Jan": 31, "Feb": 28, "Mae": 31, "Apr": 30, "Mai": 31, "Jun": 30,
|
||||
"Jul": 31, "Aug": 31, "Sep": 30, "Okt": 31, "Nov": 30, "Dez": 31,
|
||||
}
|
||||
|
||||
|
||||
def lade_config(pfad: Path) -> dict:
|
||||
with pfad.open(encoding="utf-8") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def monatsberechnung(kwp: float, cfg: dict) -> dict:
|
||||
"""Monatliche PV-Erzeugung, WP-Strom und Waermeproduktion (Luft + Wasser)."""
|
||||
pv = cfg["pv"]
|
||||
cop_luft = cfg["waermepumpe"]["cop"]
|
||||
cop_wasser = cfg["waermepumpe"].get("cop_wasser", 4.0)
|
||||
monats = pv["monatsertrag_kWh_pro_kWp"]
|
||||
detail = {}
|
||||
jahres_pv = 0.0
|
||||
jahres_waerme_luft = 0.0
|
||||
jahres_waerme_wasser = 0.0
|
||||
|
||||
for monat, tage in MONATSTAGE.items():
|
||||
pv_monat_kWh = monats[monat] * kwp
|
||||
pv_tag_kWh = pv_monat_kWh / tage
|
||||
wl_monat = pv_monat_kWh * cop_luft
|
||||
ww_monat = pv_monat_kWh * cop_wasser
|
||||
jahres_pv += pv_monat_kWh
|
||||
jahres_waerme_luft += wl_monat
|
||||
jahres_waerme_wasser += ww_monat
|
||||
detail[monat] = {
|
||||
"tage": tage,
|
||||
"pv_kWh": pv_monat_kWh,
|
||||
"pv_kWh_tag": pv_tag_kWh,
|
||||
"waerme_luft_kWh": wl_monat,
|
||||
"waerme_luft_kWh_tag": wl_monat / tage,
|
||||
"waerme_wasser_kWh": ww_monat,
|
||||
"waerme_wasser_kWh_tag": ww_monat / tage,
|
||||
}
|
||||
return {
|
||||
"monate": detail,
|
||||
"jahres_pv_kWh": jahres_pv,
|
||||
"jahres_waerme_luft_kWh": jahres_waerme_luft,
|
||||
"jahres_waerme_wasser_kWh": jahres_waerme_wasser,
|
||||
"cop_luft": cop_luft,
|
||||
"cop_wasser": cop_wasser,
|
||||
}
|
||||
|
||||
|
||||
def spitzenmonat(monate: dict) -> tuple[str, dict]:
|
||||
"""Monat mit der hoechsten taeglichen PV-Erzeugung."""
|
||||
return max(monate.items(), key=lambda x: x[1]["pv_kWh_tag"])
|
||||
|
||||
|
||||
def waermepumpe_auslegung(kwp: float, cfg: dict, sonnenstunden: float) -> dict:
|
||||
"""Erforderliche WP-Leistung um den PV-Strom tagsueberaufzunehmen.
|
||||
|
||||
sonnenstunden: aequivalente Volllaststunden Sonne pro Tag im Spitzenmonat.
|
||||
Die WP muss den gesamten Tagesstrom in diesen Stunden verarbeiten koennen.
|
||||
"""
|
||||
pv = cfg["pv"]
|
||||
cop = cfg["waermepumpe"]["cop"]
|
||||
|
||||
# Spitzenmonat: maximale taegliche Erzeugung
|
||||
monats = pv["monatsertrag_kWh_pro_kWp"]
|
||||
max_monat, max_ertrag = max(
|
||||
((m, monats[m] / MONATSTAGE[m]) for m in MONATSTAGE),
|
||||
key=lambda x: x[1],
|
||||
)
|
||||
pv_peak_tag_kWh = max_ertrag * kwp
|
||||
|
||||
# WP muss diese Energie in den Sonnenstunden aufnehmen
|
||||
wp_el_kW = pv_peak_tag_kWh / sonnenstunden
|
||||
wp_therm_kW = wp_el_kW * cop
|
||||
wp_kaelte_kW = wp_therm_kW - wp_el_kW # Verdampferseite
|
||||
|
||||
return {
|
||||
"spitzenmonat": max_monat,
|
||||
"pv_peak_tag_kWh": pv_peak_tag_kWh,
|
||||
"sonnenstunden": sonnenstunden,
|
||||
"wp_elektrisch_kW": wp_el_kW,
|
||||
"wp_thermisch_kW": wp_therm_kW,
|
||||
"wp_kaelte_kW": wp_kaelte_kW,
|
||||
}
|
||||
|
||||
|
||||
def waermetauscher_auslegung(wp: dict, sicherheitsfaktor: float = 1.2) -> dict:
|
||||
"""Erforderliche WT-Leistung auf Lade- und Entladeseite."""
|
||||
return {
|
||||
"ladeleistung_kW": wp["wp_thermisch_kW"] * sicherheitsfaktor,
|
||||
"entladeleistung_kW": wp["wp_thermisch_kW"] * sicherheitsfaktor,
|
||||
"sicherheitsfaktor": sicherheitsfaktor,
|
||||
"_hinweis": "Entladeseite kann kleiner sein wenn Entladung ueber laengere Zeit erfolgt",
|
||||
}
|
||||
|
||||
|
||||
def haushalte_versorgung(jahres_waerme_kWh: float, cfg: dict) -> dict:
|
||||
"""Wie viele Haushalte kann die Waerme versorgen?"""
|
||||
h = cfg["haushalt"]
|
||||
ww_tag = h["erwachsene"] * h["ww_erwachsener_kWh_tag"] + h["kinder"] * h["ww_kind_kWh_tag"]
|
||||
winter_tag = ww_tag + h["heizbedarf_winter_kWh_tag"]
|
||||
waerme_tag_schnitt = jahres_waerme_kWh / TAGE_PRO_JAHR
|
||||
return {
|
||||
"waerme_tag_schnitt_kWh": waerme_tag_schnitt,
|
||||
"ww_pro_haushalt_kWh_tag": ww_tag,
|
||||
"winter_pro_haushalt_kWh_tag": winter_tag,
|
||||
"haushalte_warmwasser": waerme_tag_schnitt / ww_tag if ww_tag > 0 else 0.0,
|
||||
"haushalte_winter": waerme_tag_schnitt / winter_tag if winter_tag > 0 else 0.0,
|
||||
}
|
||||
|
||||
|
||||
def wirtschaftlichkeit(kwp: float, jahres_pv_kWh: float,
|
||||
jahres_waerme_kWh: float, cfg: dict) -> dict:
|
||||
"""Erloes der Waerme im Vergleich zu fossilen Brennstoffen und Stromverkauf."""
|
||||
vergleich = []
|
||||
for b in cfg.get("brennstoffe", []):
|
||||
nw_ct = b["preis_ct_kWh"] / b["wirkungsgrad"]
|
||||
erloes_jahr = jahres_waerme_kWh * nw_ct / 100.0
|
||||
vergleich.append({
|
||||
"name": b["name"],
|
||||
"nutzwaerme_ct_kWh": nw_ct,
|
||||
"erloes_jahr_eur": erloes_jahr,
|
||||
})
|
||||
# PV-Gestehungskosten als Vergleich
|
||||
pv_gestehung = cfg.get("anbieter", {}).get("pv_gestehung_ct_kWh", 9.0)
|
||||
cop = cfg["waermepumpe"]["cop"]
|
||||
waerme_gestehung_ct = pv_gestehung / cop
|
||||
|
||||
# Stromverkauf als Alternative (Einspeisung / Direktvermarktung)
|
||||
pv_cfg = cfg.get("pv", {})
|
||||
einspeisung_ct = pv_cfg.get("einspeiseverguetung_ct_kWh", 8.1)
|
||||
direktvermarktung_ct = pv_cfg.get("direktvermarktung_ct_kWh", 7.0)
|
||||
erloes_einspeisung = jahres_pv_kWh * einspeisung_ct / 100.0
|
||||
erloes_direktvermarktung = jahres_pv_kWh * direktvermarktung_ct / 100.0
|
||||
|
||||
return {
|
||||
"pv_gestehung_ct_kWh": pv_gestehung,
|
||||
"waerme_gestehung_ct_kWh": waerme_gestehung_ct,
|
||||
"vergleich": vergleich,
|
||||
"stromverkauf": {
|
||||
"jahres_pv_kWh": jahres_pv_kWh,
|
||||
"einspeiseverguetung_ct_kWh": einspeisung_ct,
|
||||
"direktvermarktung_ct_kWh": direktvermarktung_ct,
|
||||
"erloes_einspeisung_eur": erloes_einspeisung,
|
||||
"erloes_direktvermarktung_eur": erloes_direktvermarktung,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def rechne(kwp: float, cfg: dict, sonnenstunden: float = 5.0) -> dict:
|
||||
mon = monatsberechnung(kwp, cfg)
|
||||
wp = waermepumpe_auslegung(kwp, cfg, sonnenstunden)
|
||||
wt = waermetauscher_auslegung(wp)
|
||||
hh = haushalte_versorgung(mon["jahres_waerme_luft_kWh"], cfg)
|
||||
wi = wirtschaftlichkeit(kwp, mon["jahres_pv_kWh"], mon["jahres_waerme_luft_kWh"], cfg)
|
||||
return {
|
||||
"eingang": {"kwp": kwp, "sonnenstunden": sonnenstunden},
|
||||
"cop_luft": mon["cop_luft"],
|
||||
"cop_wasser": mon["cop_wasser"],
|
||||
"monatsberechnung": mon,
|
||||
"waermepumpe": wp,
|
||||
"waermetauscher": wt,
|
||||
"haushalte": hh,
|
||||
"wirtschaftlichkeit": wi,
|
||||
}
|
||||
|
||||
|
||||
def report(e: dict, cfg: dict) -> str:
|
||||
wp = e["waermepumpe"]
|
||||
wt = e["waermetauscher"]
|
||||
mon = e["monatsberechnung"]
|
||||
hh = e["haushalte"]
|
||||
wi = e["wirtschaftlichkeit"]
|
||||
kwp = e["eingang"]["kwp"]
|
||||
cop_l = e["cop_luft"]
|
||||
cop_w = e["cop_wasser"]
|
||||
L = []
|
||||
|
||||
L.append(f"# PV-REVIVAL: {kwp:.0f} kWp Anlage -> Waermeproduktion")
|
||||
L.append("")
|
||||
L.append(f"Luft-WP COP {cop_l:.1f} / Wasser-WP COP {cop_w:.1f}, "
|
||||
f"{wp['sonnenstunden']:.1f} h aequiv. Sonnenstunden/Tag")
|
||||
|
||||
L.append("")
|
||||
L.append("## Jaehrliche Erzeugung")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert | Einheit |")
|
||||
L.append("|:---|---:|:---|")
|
||||
L.append(f"| PV-Strom | {t(mon['jahres_pv_kWh'])} | kWh/a |")
|
||||
L.append(f"| Waerme Luft-WP (COP {cop_l:.1f}) | {t(mon['jahres_waerme_luft_kWh'])} | kWh/a |")
|
||||
L.append(f"| Waerme Wasser-WP (COP {cop_w:.1f}) | {t(mon['jahres_waerme_wasser_kWh'])} | kWh/a |")
|
||||
L.append(f"| Waerme Tagesmittel (Luft) | {t(hh['waerme_tag_schnitt_kWh'], 1)} | kWh/Tag |")
|
||||
|
||||
L.append("")
|
||||
L.append("## Erforderliche Waermepumpe")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert | Einheit |")
|
||||
L.append("|:---|---:|:---|")
|
||||
L.append(f"| Spitzenmonat | {wp['spitzenmonat']} | |")
|
||||
L.append(f"| PV-Erzeugung Spitzentag | {t(wp['pv_peak_tag_kWh'], 1)} | kWh/Tag |")
|
||||
L.append(f"| Elektr. Leistung WP | {t(wp['wp_elektrisch_kW'], 1)} | kW |")
|
||||
L.append(f"| Thermische Leistung WP | {t(wp['wp_thermisch_kW'], 1)} | kW |")
|
||||
L.append(f"| Kaelteleistung (Verdampfer) | {t(wp['wp_kaelte_kW'], 1)} | kW |")
|
||||
|
||||
L.append("")
|
||||
L.append(f"## Erforderlicher Waermetauscher (Sicherheitsfaktor {wt['sicherheitsfaktor']:.1f}x)")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert | Einheit |")
|
||||
L.append("|:---|---:|:---|")
|
||||
L.append(f"| Ladeleistung | {t(wt['ladeleistung_kW'], 1)} | kW |")
|
||||
L.append(f"| Entladeleistung | {t(wt['entladeleistung_kW'], 1)} | kW |")
|
||||
|
||||
L.append("")
|
||||
L.append("## Monatsuebersicht")
|
||||
L.append("")
|
||||
L.append(f"| Monat | PV kWh | kWh/Tag | Luft kWh (COP {cop_l:.1f}) | Wasser kWh (COP {cop_w:.1f}) |")
|
||||
L.append("|:---|---:|---:|---:|---:|")
|
||||
for monat in MONATSTAGE:
|
||||
m = mon["monate"][monat]
|
||||
L.append(f"| {monat} | {t(m['pv_kWh'])} | {t(m['pv_kWh_tag'], 1)} |"
|
||||
f" {t(m['waerme_luft_kWh'])} | {t(m['waerme_wasser_kWh'])} |")
|
||||
L.append(f"| **Summe** | **{t(mon['jahres_pv_kWh'])}** | |"
|
||||
f" **{t(mon['jahres_waerme_luft_kWh'])}** |"
|
||||
f" **{t(mon['jahres_waerme_wasser_kWh'])}** |")
|
||||
|
||||
# Mermaid-Diagramm
|
||||
L.append("")
|
||||
L.append("## Diagramm: Strom- und Waermeproduktion pro Monat")
|
||||
L.append("")
|
||||
L.append("```mermaid")
|
||||
L.append("%%{init: {'theme': 'base', 'themeVariables': {"
|
||||
"'xyChart': {'plotColorPalette': '#f0c808, #5bc0eb, #1d3557'}"
|
||||
"}}}%%")
|
||||
L.append("xychart-beta")
|
||||
L.append(f" title \"PV-Revival {kwp:.0f} kWp - Monatsproduktion (MWh)\"")
|
||||
L.append(" x-axis [Jan, Feb, Mae, Apr, Mai, Jun, Jul, Aug, Sep, Okt, Nov, Dez]")
|
||||
# Werte in MWh fuer bessere Lesbarkeit
|
||||
pv_vals = [f"{mon['monate'][m]['pv_kWh']/1000:.1f}" for m in MONATSTAGE]
|
||||
luft_vals = [f"{mon['monate'][m]['waerme_luft_kWh']/1000:.1f}" for m in MONATSTAGE]
|
||||
wasser_vals = [f"{mon['monate'][m]['waerme_wasser_kWh']/1000:.1f}" for m in MONATSTAGE]
|
||||
L.append(f" y-axis \"MWh\"")
|
||||
L.append(f" bar [{', '.join(pv_vals)}]")
|
||||
L.append(f" line [{', '.join(luft_vals)}]")
|
||||
L.append(f" line [{', '.join(wasser_vals)}]")
|
||||
L.append("```")
|
||||
L.append("")
|
||||
L.append(f"*Balken: PV-Strom | Linie 1: Waerme Luft-WP (COP {cop_l:.1f}) | "
|
||||
f"Linie 2: Waerme Wasser-WP (COP {cop_w:.1f}) -- Werte in MWh*")
|
||||
|
||||
L.append("")
|
||||
L.append(f"## Versorgbare Haushalte ({cfg['haushalt']['erwachsene']} Erw. + "
|
||||
f"{cfg['haushalt']['kinder']} Kinder, Tagesmittel)")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert |")
|
||||
L.append("|:---|---:|")
|
||||
L.append(f"| Warmwasser ganzjaehrig | {t(hh['haushalte_warmwasser'])} Haushalte |")
|
||||
L.append(f"| Winter (Heizung + WW) | {t(hh['haushalte_winter'], 1)} Haushalte |")
|
||||
|
||||
if wi["vergleich"]:
|
||||
L.append("")
|
||||
L.append("## Wirtschaftlichkeit")
|
||||
L.append("")
|
||||
L.append("| Kenngroesse | Wert | Einheit |")
|
||||
L.append("|:---|---:|:---|")
|
||||
L.append(f"| PV-Stromgestehung | {t(wi['pv_gestehung_ct_kWh'], 1)} | ct/kWh |")
|
||||
L.append(f"| Waermegestehung (/ COP) | {t(wi['waerme_gestehung_ct_kWh'], 1)} | ct/kWh |")
|
||||
L.append("")
|
||||
L.append("Zum Vergleich: Kosten der anderen Brennstoffe (Nutzwaerme-Preis):")
|
||||
L.append("")
|
||||
L.append("| Brennstoff | ct/kWh | Erloes EUR/a |")
|
||||
L.append("|:---|---:|---:|")
|
||||
for v in wi["vergleich"]:
|
||||
L.append(f"| {v['name']} | {t(v['nutzwaerme_ct_kWh'], 1)} | {t(v['erloes_jahr_eur'])} |")
|
||||
L.append("")
|
||||
L.append("*Erloes = Waerme bewertet zu Ersatzbrennstoffkosten*")
|
||||
|
||||
sv = wi["stromverkauf"]
|
||||
L.append("")
|
||||
L.append("### Alternative: Strom direkt verkaufen")
|
||||
L.append("")
|
||||
L.append("| Variante | ct/kWh | Erloes EUR/a |")
|
||||
L.append("|:---|---:|---:|")
|
||||
L.append(f"| Einspeiseverguetung (EEG) | {t(sv['einspeiseverguetung_ct_kWh'], 1)} |"
|
||||
f" {t(sv['erloes_einspeisung_eur'])} |")
|
||||
L.append(f"| Direktvermarktung | {t(sv['direktvermarktung_ct_kWh'], 1)} |"
|
||||
f" {t(sv['erloes_direktvermarktung_eur'])} |")
|
||||
L.append("")
|
||||
# Vergleich: bester Waerme-Erloes vs. Stromverkauf
|
||||
best_waerme = max(wi["vergleich"], key=lambda v: v["erloes_jahr_eur"])
|
||||
vorteil = best_waerme["erloes_jahr_eur"] - sv["erloes_einspeisung_eur"]
|
||||
if vorteil > 0:
|
||||
L.append(f"*Waermeverkauf (vs. {best_waerme['name']}) bringt "
|
||||
f"{t(vorteil)} EUR/a mehr als Einspeisung.*")
|
||||
else:
|
||||
L.append(f"*Einspeisung bringt {t(-vorteil)} EUR/a mehr als "
|
||||
f"Waermeverkauf (vs. {best_waerme['name']}).*")
|
||||
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser(description=__doc__,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
ap.add_argument("kwp", type=float, help="Groesse der PV-Anlage in kWp")
|
||||
ap.add_argument("--config", type=Path,
|
||||
default=PROJECT_ROOT / "cfg" / "config.json",
|
||||
help="Konfiguration (Standard: cfg/config.json)")
|
||||
ap.add_argument("--sonnenstunden", type=float, default=5.0,
|
||||
help="Aequivalente Volllaststunden Sonne pro Tag im Spitzenmonat "
|
||||
"(Standard: 5.0 h, bestimmt die noetige WP-Spitzenleistung)")
|
||||
ap.add_argument("--out", type=Path, default=None,
|
||||
help="Ausgabeverzeichnis (Standard: $MILCH_OUT oder out/)")
|
||||
ap.add_argument("--json", type=Path, default=None,
|
||||
help="Ergebnis zusaetzlich als JSON schreiben")
|
||||
ap.add_argument("-v", "--verbose", action="store_true",
|
||||
help="Ausgaben auf der Konsole")
|
||||
args = ap.parse_args()
|
||||
|
||||
out_ordner = (
|
||||
args.out
|
||||
or Path(os.environ.get("MILCH_OUT", ""))
|
||||
or PROJECT_ROOT / "out"
|
||||
)
|
||||
if not out_ordner or str(out_ordner) == ".":
|
||||
out_ordner = PROJECT_ROOT / "out"
|
||||
out_ordner = Path(out_ordner)
|
||||
out_ordner.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
cfg = lade_config(args.config)
|
||||
ergebnis = rechne(args.kwp, cfg, sonnenstunden=args.sonnenstunden)
|
||||
text = report(ergebnis, cfg)
|
||||
|
||||
if args.verbose:
|
||||
print(text)
|
||||
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
md_name = f"revival_{args.kwp:.0f}kWp_{ts}.md"
|
||||
md_path = out_ordner / md_name
|
||||
md_path.write_text(text, encoding="utf-8")
|
||||
if args.verbose:
|
||||
print(f"\nReport geschrieben: {md_path}")
|
||||
|
||||
if args.json:
|
||||
with args.json.open("w", encoding="utf-8") as f:
|
||||
json.dump(ergebnis, f, ensure_ascii=False, indent=2)
|
||||
if args.verbose:
|
||||
print(f"JSON geschrieben: {args.json}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user