This commit is contained in:
Nicolai 2026-03-17 12:00:21 +01:00
parent ecd727be78
commit acf40969ed
8 changed files with 3207 additions and 1 deletions

View File

@ -1,2 +1,81 @@
# LEAG-COALLOG # POC1: Braunkohle Supply Chain Modellierung
## Überblick
Dieser POC modelliert eine Braunkohle Supply Chain mit Pyomo zur Optimierung von Produktions-, Transport- und Distributionsprozessen.
## Projektstruktur
```
POC1/
├── src/ # Quellcode
│ └── preprocessing/
│ └── exploration_preprocess.py # Notebook-exportiertes Preprocessing
├── data/ # Daten
│ ├── input/ # Eingabe-Parameter (Excel)
│ └── processed/ # Vorverarbeitete Parquet-Dateien
├── models/ # Modell-Definitionen
│ └── README.md
├── notebooks/ # Jupyter Notebooks für Analyse
├── latex/ # Latex-Export/Artefakte
├── requirements.txt # Python Dependencies
├── pyproject.toml # Projektmetadaten
└── setup.py # Setup-Skript
```
## Installation
```bash
pip install -r requirements.txt
```
## Verwendung
```bash
# Preprocessing-Skript ausführen
python src/preprocessing/exploration_preprocess.py
```
## Modell-Hinweise
- Bergbauwoche: Wochenstart Samstag (SaFr) über Datum+2TageShift.
- IIS-Debug: Bei Infeasibility schreibt Gurobi eine `results/iis.ilp` mit lesbaren Constraint-Namen.
- 3-Tage-Regel: `no_three_in_a_row` ist aktuell auf <= 3 gelockert (Debug).
## Aktuelle Änderungen (Modell & Output)
- Tages/ Wochen/ Monatsabweichungen basieren auf **Lieferungen (x)**, nicht auf Bunkerabfluss.
- Mischungsverhältnisse (min/max + Ziel) werden **auf Lieferungen** angewendet (nicht auf Bunkerbestand).
- Zusätzliche weiche **MidZielAbweichung** für Mix (linear) ist aktiv.
- B3BunkerMix: weiche Zielmischung auf B3Bunkerbestand (stark gewichtet), um Welzow im Bunker zu vermeiden.
- StreckenPenalties/Bonuses wurden wieder **entfernt** (nur Mix/AbweichungsPenalties aktiv).
- ExcelOutput:
- Sheet1 bleibt unverändert, `mit_Bunkerbestand` enthält Bunkerzufluss und Bunkerbestand.
- Neue Sheet `Kohlemischverhältnis` mit Zielwerten + empirischem Mix (Lieferung) + Bunkermix.
- Empirischer Mix wird rot/grün markiert, Gruppenfarben nur bis Spalte F.
## Webapp-Prototyp
Backend (FastAPI):
```bash
uv run python -m uvicorn webapp.backend.main:app --reload
```
Frontend (React/Vite):
```bash
cd webapp/frontend
npm install
npm run dev
```
cd webapp/frontend
npm run dev
Optional: eigene Input/Output-Pfade beim Preprocessing via Umgebungsvariablen:
`POC1_INPUT_XLSX`, `POC1_OUTPUT_DIR`.
## Abhängigkeiten
- Pyomo (Optimierungsmodellierung)
- Pandas (Datenverarbeitung)
- NumPy (Numerische Berechnungen)
- Matplotlib/Plotly (Visualisierung)

40
models/README.md Normal file
View File

@ -0,0 +1,40 @@
# Model Documentation
## Braunkohle Supply Chain POC1
### Modellübersicht
Das Modell optimiert die Braunkohle-Lieferkette mit folgenden Komponenten:
- **Mines (Gruben)**: Primäre Produktionsstellen
- **Plants (Anlagen)**: Verarbeitungs- und Verteilzentren
- **Customers (Kunden)**: Endabnehmer/Marktsegmente
- **Periods (Zeitperioden)**: Planungshorizont (z.B. Monate/Quarthale)
### Entscheidungsvariablen
| Variable | Definition |
|----------|-----------|
| `production[i, t]` | Produktionsmenge von Mine i in Periode t |
| `transport[i, p, t]` | Transport von Mine i zu Anlage p in Periode t |
| `sales[p, c, t]` | Verkauf von Anlage p an Kunde c in Periode t |
### Parameter
| Parameter | Definition |
|-----------|-----------|
| `production_capacity[i, t]` | Max. Produktionskapazität Mine i, Periode t |
| `production_cost[i, t]` | Produktionskosten Mine i, Periode t |
| `transport_cost[i, p]` | Transportkosten Mine i → Anlage p |
| `demand[c, t]` | Nachfrage Kunde c, Periode t |
| `plant_capacity[p]` | Max. Verarbeitungskapazität Anlage p |
### Constraints
1. **Production Capacity**: Produktion ≤ Kapazität
2. **Plant Balance**: Eingangsmaterial ≥ Ausgangsmaterial
3. **Plant Capacity**: Verkauf ≤ Verarbeitungskapazität
4. **Demand**: Verkauf ≥ Nachfrage
### Objective
Minimierung der Gesamt-Kosten (Produktion + Transport)

5
notebooks/README.md Normal file
View File

@ -0,0 +1,5 @@
"""
Beispiel-Notebook für Supply Chain Analyse
Installieren Sie Jupyter mit: pip install jupyter
Starten Sie mit: jupyter notebook
"""

View File

@ -0,0 +1,62 @@
first_opt_model.ipynb Nebenbedingungen & Datenquellen (geordnet)
=================================================================
## Setup
- **Imports** (Zelle 2): pandas, numpy, pyomo, matplotlib; Basis-Notebook-Kontext.
- **Tabellen laden** (Zelle 3): Alle Parquet-Files aus `../data/processed` → Dict `tables[<name>]`.
## Datenaufbereitung
- **bedarf** (Zelle 5): Aus `tables['rohkohlebedarf']`; Spalten JW, SP, BW3, BW4, Veredel_Nochtener, Veredel_Welzower; Datum → week/weekday/day.
- **bounds_power_plants** (Zelle 5): Toleranzen Kraftwerke pro Tag/Woche/Monat.
- **bounds_day** (Zelle 8): Filter `bounds_power_plants` auf Zeitraum „pro Tag“; `match_kraftwerk_row` findet Zeile pro j.
- **veredelung_bounds** (Zelle 14): `tables['veredelung_bounds']` in `v_day/v_week/v_month`; `match_ver_row` holt Zeile je Kohlesorte/Zeitraum.
- **weitere Tabellen** (später genutzt): `tables['kohle_mix']` (Mischungsanteile), `tables['foerderkapaz']` (Förderlimits, Monat), `tables['verladungskap']` (KLP, Schicht/Tag), `tables['Verfuegbarkeiten']` (Schichtgrenzen, NaN = kein Limit).
## Modellaufbau
- **Sets** (Zelle 10): `J={J, SP, B3, B4, V}`, `W` aus `bedarf.week`, `D={Sa..Fr}`, `S={F,S,N}`, `I={Reichwalde, Nochten, Welzow}`, Hilfssets `I_W/I_N`.
- **Parameter-Container** (Zelle 12): `d, dV_N, dV_W, a_min_day, a_max_day` (alle mutable).
- **Befüllung Bedarfe & Tages-Toleranzen** (Zelle 14): `d` und `a_min_day/a_max_day` aus `rohkohlebedarf` + `bounds_day` (Absolut/Pct kombiniert). `dV_N/dV_W` aus `rohkohlebedarf`.
## Parameterdefinition (weiterführend)
- **Wochen/Monats-Toleranzen** (Zelle 21): `d_week/d_month` und `a_min/a_max` aus `bounds_power_plants` (pro Woche/Monat, inkl. Gesamtzeilen).
- **Veredlung Toleranzen** (Zelle 22): `a_min/a_max` für Tag/Woche/Monat aus `veredelung_bounds` pro Kohlesorte.
- **Mischungsparameter** (Zelle 25): `alpha_min/alpha_max` aus `tables['kohle_mix']` (Mapping Namen→Model-Keys).
- **Förderkapazitäten** (Zelle 45): `F_max_month` aus `tables['foerderkapaz']` (Zeitraum pro Monat).
- **Verladungskapazitäten** (Zelle 48): Caps aus `tables['verladungskap']` für Boxberg (RW+NO) und Welzow-Süd (Schicht/Tag).
- **Verfügbarkeiten** (Zelle 52): `cap_Welzow / cap_RW_N` aus `tables['Verfuegbarkeiten']` (NaN ⇒ `Constraint.Skip`).
## Tageslieferung und Abweichungen
- **delivery_tolerance** (Zelle 19): `y[j,w,d]` in `[d + a_min_day, d + a_max_day]` für `j≠V`. Daten: `d` aus `rohkohlebedarf`; `a_min_day/a_max_day` aus `bounds_power_plants` (pro Tag).
- **demand/dev/dev_balance** (Zelle 33): Nachfrage für V = `dV_N + dV_W`, sonst `d`; `dev_pos/neg` balancieren. Daten: `rohkohlebedarf`.
- **max_reached / no_three_in_a_row** (Zelle 35): Binärflag für erreichte Max-Tagesabweichung, nicht drei Tage in Folge. Daten: `a_max_day` aus `bounds_power_plants`.
- **Veredlung devV_*** (Zelle 37): Abweichungen je Kohlesorte; Daten: `dV_N/dV_W` aus `rohkohlebedarf`.
- **Schichtglättung glatt_hi/lo** (Zelle 39): L1-Abweichung zu Schichtmittel; Parameter `lambda_glatt` intern gesetzt.
- **Verbot Reichwalde→V** (Zelle 43): Hart kodiert.
## Wochen-/Monatslieferungen und Abweichung
- **week_tolerance / month_tolerance** (Zelle 21): `y_week/y_month` je Kraftwerk in Wochen-/Monats-Bändern. Daten: `d_week/d_month` + Toleranzen aus `bounds_power_plants`.
- **week_total_tolerance / month_total_tolerance** (Zelle 21): Gesamt über Kraftwerke in Bändern; Daten: Gesamtzeilen `bounds_power_plants`.
- **Veredlung v_*_day/week/month_tol** (Zelle 22): Lieferungen nach V (Nochten/Welzow/Gesamt) in Toleranzbändern; Daten: `veredelung_bounds` + `rohkohlebedarf`.
## Kohlensortenmischverhältnis
- **mix_lower / mix_upper** (Zelle 25): Anteil Kohlesorte i an Tagesgesamt `y_day[j]` zwischen `alpha_min/alpha_max`. Daten: `tables['kohle_mix']`.
## Kapazitäten & kombinierte Flussgrenzen
- **cap_month / cap_month_RWNO** (Zelle 45): Monatsförderung je Tagebau aus `foerderkapaz`; RW+NO hart auf `3_000_000` t.
- **cap_boxberg_shift/day, cap_welzow_shift/day** (Zelle 48): KLP-Limits aus `verladungskap` (Schicht/Tag).
- **cap_welzow_con / cap_rw_n_con** (Zelle 52): Schichtlimits aus `Verfuegbarkeiten`, sonst Skip.
- **Paar-Grenzen RW+N** (Zellen 5760): Max je Schicht zu J/SP/V/B3; hart kodiert (`20k/25k/25k/32k`).
- **Welzow Einzelziele** (Zellen 6265): Max je Schicht; hart kodiert (`26k/24k/24k/20k`); plus `welzow_j_multiple_2kt` mit `2_000`er Schritt.
- **Kombinierte Summen** (Zellen 6669): RW+N → SP/V/J/B3 und W → SP/V/B3; hart kodiert (`25k, 55k, 24k, 25k`).
- **Gesamtkombis** (Zellen 7071): RW+N+W → J/B3; hart kodiert (`34k, 32k`).
- **Gekoppelte Kombinationen** (Zellen 72, 74): RW+N→{J/SP/V} mit W→B3; hart kodiert (`20k, 32k`).
## Schichtmuster / Balance
- **shift_dev_4k** (Zelle 79): `|x_s1 - x_s2| ≤ 10_000` (≈10 kt) je Arc/Tag; hart kodiert.
- **shift_pattern** (Zelle 80): Erzwingt Muster aufsteigend/absteigend/gleich; Parameter `MAX_FLOW=10_000_000`, `TOL=0`; hart kodiert.
## Ausgeschaltete Blöcke
- Zellen 61, 76, 78: Alternative Schichtlogiken (min je Schicht, Binärflags für Nutzung); aktuell auskommentiert.
## Offene TODOs
- (Zelle 107) Straf-/Bonusterme (Early/Late), Förderband Boxberg 4, Lager/Bunker-Logik, Nichtverfügbarkeiten Verbraucher/Tagebaue, weitere kapazitive Constraints aus PDF.

View File

@ -0,0 +1,66 @@
first_opt_model.ipynb Nebenbedingungen & Datenquellen (geordnet)
=================================================================
Setup
- Imports (Zelle 2): pandas, numpy, pyomo, matplotlib; Basis-Notebook-Kontext.
- Tabellen laden (Zelle 3): Alle Parquet-Files aus ../data/processed → Dict tables[<name>].
Datenaufbereitung
- bedarf (Zelle 5): Aus tables['rohkohlebedarf']; Spalten JW, SP, BW3, BW4, Veredel_Nochtener, Veredel_Welzower; Datum→week/weekday/day.
- bounds_power_plants (Zelle 5): Toleranzen Kraftwerke pro Tag/Woche/Monat.
- bounds_day (Zelle 8): Filter bounds_power_plants auf Zeitraum „pro Tag“; match_kraftwerk_row findet Zeile pro j.
- Veredlung_Bounds (Zelle 14): tables['veredelung_bounds'] in v_day/v_week/v_month; match_ver_row holt Zeile je Kohlesorte/Zeitraum.
- Weitere Tabellen, die später genutzt werden (jeweils in den Constraints referenziert):
* tables['kohle_mix'] Mischungsanteile
* tables['foerderkapaz'] Förderlimits (pro Monat)
* tables['verladungskap'] KLP-Kapazitäten (Schicht/Tag)
* tables['Verfuegbarkeiten'] Schichtgenaue Limits (NaN = kein Limit)
Modellaufbau
- Sets (Zelle 10): J = {J, SP, B3, B4, V}; W aus bedarf.week; D = {Sa..Fr}; S = {F,S,N}; I = {Reichwalde, Nochten, Welzow}; Hilfssets I_W/I_N.
- Parameter-Container (Zelle 12): d, dV_N, dV_W, a_min_day, a_max_day (alle mutable).
- Befüllung Bedarfe & Tages-Toleranzen (Zelle 14): d und a_min_day/a_max_day aus rohkohlebedarf + bounds_day (Absolut/Pct kombiniert). Veredlung dV_N/dV_W ebenfalls aus rohkohlebedarf.
Parameterdefinition (weiterführend)
- Wochen/Monats-Toleranzen (Zelle 21): Berechnung d_week/d_month und Toleranzen a_min/a_max aus bounds_power_plants (pro Woche/Monat, inkl. Gesamtzeilen).
- Veredlung Toleranzen (Zelle 22): a_min/a_max für Tag/Woche/Monat aus veredelung_bounds pro Kohlesorte.
- Mischungsparameter (Zelle 25): alpha_min/alpha_max aus tables['kohle_mix'] (Mapping Namen→Model-Keys).
- Förderkapazitäten (Zelle 45): F_max_month aus tables['foerderkapaz'] (Zeitraum pro Monat).
- Verladungskapazitäten (Zelle 48): Caps aus tables['verladungskap'] für Boxberg (RW+NO) und Welzow-Süd (pro Schicht/Tag).
- Verfügbarkeiten (Zelle 52): cap_Welzow / cap_RW_N aus tables['Verfuegbarkeiten'] (NaN ⇒ Constraint.Skip).
Tageslieferung und Abweichungen
- delivery_tolerance (Zelle 19): y[j,w,d] in [d + a_min_day, d + a_max_day] für j≠V. Daten: d aus rohkohlebedarf; a_min_day/a_max_day aus bounds_power_plants (pro Tag).
- demand/dev/dev_balance (Zelle 33): Nachfrage für V = dV_N + dV_W, sonst d; dev_pos/neg balancieren. Daten: rohkohlebedarf.
- max_reached / no_three_in_a_row (Zelle 35): Binärflag für erreichte Max-Tagesabweichung, nicht drei Tage in Folge. Daten: a_max_day aus bounds_power_plants.
- Veredlung devV_* (Zelle 37): Abweichungen je Kohlesorte; Daten: dV_N/dV_W aus rohkohlebedarf.
- Schichtglättung glatt_hi/lo (Zelle 39): L1-Abweichung zu Schichtmittel; Parameter lambda_glatt intern gesetzt.
- Verbot Reichwalde→V (Zelle 43): Hart kodiert.
Wochen-/Monatslieferungen und Abweichung
- week_tolerance / month_tolerance (Zelle 21): y_week/y_month je Kraftwerk in Wochen-/Monats-Bändern. Daten: d_week/d_month + Toleranzen aus bounds_power_plants.
- week_total_tolerance / month_total_tolerance (Zelle 21): Gesamt über Kraftwerke in Bändern; Daten: Gesamtzeilen bounds_power_plants.
- Veredlung v_*_day/week/month_tol (Zelle 22): Lieferungen nach V (Nochten/Welzow/Gesamt) in Toleranzbändern; Daten: veredelung_bounds + rohkohlebedarf.
Kohlensortenmischverhältnis
- mix_lower / mix_upper (Zelle 25): Anteil Kohlesorte i an Tagesgesamt y_day[j] zwischen alpha_min/alpha_max. Daten: tables['kohle_mix'].
Kapazitäten & kombinierte Flussgrenzen
- cap_month / cap_month_RWNO (Zelle 45): Monatsförderung je Tagebau aus foerderkapaz; RW+NO hart auf 3_000_000 t.
- cap_boxberg_shift/day, cap_welzow_shift/day (Zelle 48): KLP-Limits aus verladungskap (pro Schicht/Tag).
- cap_welzow_con / cap_rw_n_con (Zelle 52): Schichtlimits aus Verfuegbarkeiten, sonst Skip.
- Paar-Grenzen RW+N (Zellen 5760): Max je Schicht zu J/SP/V/B3; hart kodiert (20k/25k/25k/32k).
- Welzow Einzelziele (Zellen 6265): Max je Schicht; hart kodiert (26k/24k/24k/20k); plus welzow_j_multiple_2kt mit 2_000er Schritt.
- Kombinierte Summen (Zellen 6669): RW+N → SP/V/J/B3 und W → SP/V/B3; hart kodiert (25k, 55k, 24k, 25k).
- Gesamtkombis (Zellen 7071): RW+N+W → J/B3; hart kodiert (34k, 32k).
- Gekoppelte Kombinationen (Zellen 72, 74): RW+N→{J/SP/V} mit W→B3; hart kodiert (20k, 32k).
Schichtmuster / Balance
- shift_dev_4k (Zelle 79): |x_s1 - x_s2| ≤ 10_000 (≈10 kt) je Arc/Tag; hart kodiert.
- shift_pattern (Zelle 80): Erzwingt Muster aufsteigend/absteigend/gleich; Parameter MAX_FLOW=10_000_000, TOL=0; hart kodiert.
Ausgeschaltete Blöcke
- Zellen 61, 76, 78: Alternative Schichtlogiken (min je Schicht, Binärflags für Nutzung); aktuell auskommentiert.
Offene TODOs (Zelle 107)
- Straf-/Bonusterme (Early/Late), Förderband Boxberg 4, Lager/Bunker-Logik, Nichtverfügbarkeiten Verbraucher/Tagebaue, weitere kapazitive Constraints aus PDF.

File diff suppressed because it is too large Load Diff

51
pyproject.toml Normal file
View File

@ -0,0 +1,51 @@
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "poc1"
version = "0.1.0"
description = "Braunkohle Supply Chain mit Pyomo"
readme = "README.md"
requires-python = ">=3.13"
authors = [
{name = "LEAG", email = "abc@leag.de"}
]
license = {text = "MIT"}
dependencies = [
"pyomo>=6.7",
"pandas>=2.0",
"numpy>=1.24",
"openpyxl>=3.0",
"pyarrow>=15.0",
"highspy>=1.6.0",
"matplotlib>=3.7",
"plotly>=5.14",
]
[project.optional-dependencies]
dev = [
"jupyter>=1.0",
"ipython>=8.12",
"pytest>=7.3",
"black>=23.0",
"flake8>=6.0",
"ipykernel>=6.20",
]
web = [
"fastapi>=0.110",
"uvicorn>=0.29",
"python-multipart>=0.0.9",
]
[project.scripts]
poc1 = "main:main"
[tool.black]
line-length = 100
target-version = ['py313']
[tool.flake8]
max-line-length = 100
exclude = [".git", "__pycache__", "build", "dist"]

11
requirements.txt Normal file
View File

@ -0,0 +1,11 @@
pyomo>=6.7
pandas>=2.0
numpy>=1.24
openpyxl>=3.0
matplotlib>=3.7
plotly>=5.14
jupyter>=1.0
ipython>=8.12
pytest>=7.3
black>=23.0
flake8>=6.0