895 lines
117 KiB
HTML
895 lines
117 KiB
HTML
|
|
<!doctype html>
|
|||
|
|
<html lang="de">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="utf-8" />
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|||
|
|
<title>Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit · Optimierte Entscheidungsfindung mit Python</title>
|
|||
|
|
<script>
|
|||
|
|
(function () {
|
|||
|
|
try {
|
|||
|
|
var t = localStorage.getItem("or-theme");
|
|||
|
|
if (t) document.documentElement.setAttribute("data-theme", t);
|
|||
|
|
} catch (e) {}
|
|||
|
|
})();
|
|||
|
|
</script>
|
|||
|
|
<link rel="stylesheet" href="assets/highlight.css" />
|
|||
|
|
<link rel="stylesheet" href="katex/katex.min.css" />
|
|||
|
|
<script defer="" src="katex/katex.min.js"></script>
|
|||
|
|
<script>document.addEventListener("DOMContentLoaded", function () {
|
|||
|
|
var mathElements = document.getElementsByClassName("math");
|
|||
|
|
var macros = [];
|
|||
|
|
for (var i = 0; i < mathElements.length; i++) {
|
|||
|
|
var texText = mathElements[i].firstChild;
|
|||
|
|
if (mathElements[i].tagName == "SPAN") {
|
|||
|
|
katex.render(texText.data, mathElements[i], {
|
|||
|
|
displayMode: mathElements[i].classList.contains('display'),
|
|||
|
|
throwOnError: false,
|
|||
|
|
macros: macros,
|
|||
|
|
fleqn: false
|
|||
|
|
});
|
|||
|
|
}}
|
|||
|
|
// Der Browser springt zu einem #anker in der URL schon beim ersten Rendern
|
|||
|
|
// an, BEVOR die KaTeX-Formeln oben im Text ihre finale Hoehe bekommen -
|
|||
|
|
// durch den Reflow landet der Anker danach zu weit unten. Nach dem
|
|||
|
|
// Formel-Rendering hier erneut zum Anker springen, das behebt es.
|
|||
|
|
if (location.hash) {
|
|||
|
|
var ziel = document.getElementById(decodeURIComponent(location.hash.slice(1)));
|
|||
|
|
if (ziel) ziel.scrollIntoView({behavior: "instant", block: "start"});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
<link rel="stylesheet" href="assets/site.css" />
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<svg style="display:none" aria-hidden="true"><symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|||
|
|
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|||
|
|
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.2" y2="16.2"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|||
|
|
<circle cx="12" cy="12" r="4.5"/>
|
|||
|
|
<line x1="12" y1="1.5" x2="12" y2="4"/><line x1="12" y1="20" x2="12" y2="22.5"/>
|
|||
|
|
<line x1="1.5" y1="12" x2="4" y2="12"/><line x1="20" y1="12" x2="22.5" y2="12"/>
|
|||
|
|
<line x1="4.5" y1="4.5" x2="6.2" y2="6.2"/><line x1="17.8" y1="17.8" x2="19.5" y2="19.5"/>
|
|||
|
|
<line x1="19.5" y1="4.5" x2="17.8" y2="6.2"/><line x1="6.2" y1="17.8" x2="4.5" y2="19.5"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<path d="M20 14.5A8.5 8.5 0 1 1 9.5 4a6.8 6.8 0 0 0 10.5 10.5z"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-chevron-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<polyline points="15 4 7 12 15 20"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<polyline points="9 4 17 12 9 20"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<polyline points="4 13 9.5 18.5 20 6"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<path d="M18 13.5V19a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 19V7A1.5 1.5 0 0 1 5 5.5h5.5"/>
|
|||
|
|
<polyline points="14.5 3.5 20.5 3.5 20.5 9.5"/><line x1="11" y1="13" x2="20" y2="4"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<path d="M4 5.5A2 2 0 0 1 6 4h6v16H6a2 2 0 0 0-2 1.5z"/>
|
|||
|
|
<path d="M20 5.5A2 2 0 0 0 18 4h-6v16h6a2 2 0 0 1 2 1.5z"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
|||
|
|
</symbol>
|
|||
|
|
<symbol id="icon-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
|
<path d="M12 3v12"/><polyline points="7 10 12 15 17 10"/><path d="M4 19.5h16"/>
|
|||
|
|
</symbol></svg>
|
|||
|
|
<header class="site-header">
|
|||
|
|
<button type="button" class="icon-btn" id="sidebar-toggle" aria-label="Menü öffnen"><svg class="icon" aria-hidden="true"><use href="#icon-menu"></use></svg></button>
|
|||
|
|
<a class="brand" href="index.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> <span>Optimierte Entscheidungsfindung mit Python</span></a>
|
|||
|
|
<div class="site-search">
|
|||
|
|
<input id="suche-eingabe" type="search" placeholder="Suchen …" aria-label="Suche" autocomplete="off" />
|
|||
|
|
<svg class="icon such-icon" aria-hidden="true"><use href="#icon-search"></use></svg>
|
|||
|
|
<div id="suche-ergebnisse" class="suche-ergebnisse" hidden></div>
|
|||
|
|
</div>
|
|||
|
|
<button type="button" class="icon-btn" id="theme-toggle" aria-label="Darstellung umschalten">
|
|||
|
|
<svg class="icon icon-sun" aria-hidden="true"><use href="#icon-sun"></use></svg><svg class="icon icon-moon" aria-hidden="true"><use href="#icon-moon"></use></svg>
|
|||
|
|
</button>
|
|||
|
|
</header>
|
|||
|
|
<div class="site-body">
|
|||
|
|
<div class="sidebar-overlay" id="sidebar-overlay" hidden></div>
|
|||
|
|
<nav class="sidebar" id="sidebar" aria-label="Kapitelnavigation"><div class="sidebar-inhalt"><details class="sidebar-gruppe"><summary>Einstieg</summary><ul><li data-kapitel="vorwort.html"><a href="vorwort.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><li data-kapitel="notation.html"><a href="notation.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil I: Grundlagen des Operations Research</summary><ul><li data-kapitel="einfuehrung.html"><a href="einfuehrung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><li data-kapitel="fundament.html"><a href="fundament.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><li data-kapitel="oekosystem.html"><a href="oekosystem.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><li data-kapitel="modellierung.html"><a href="modellierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><li data-kapitel="lp.html"><a href="lp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><li data-kapitel="milp.html"><a href="milp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><li data-kapitel="cpsat.html"><a href="cpsat.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><li data-kapitel="graphen.html"><a href="graphen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><li data-kapitel="metaheuristiken.html"><a href="metaheuristiken.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><li data-kapitel="dekomposition.html"><a href="dekomposition.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik</summary><ul><li data-kapitel="qp-nlp.html"><a href="qp-nlp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 11: Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität</span></a></li><li data-kapitel="unsicherheit.html"><a href="unsicherheit.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></
|
|||
|
|
<main class="content">
|
|||
|
|
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="index.html">Start</a> › <span>Teil IV</span> › <span>Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit</span></nav>
|
|||
|
|
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="bruecke.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten</span></a><a class="prev-next-knopf prev-next-next" href="finanzdaten.html"><span><small>Weiter</small>Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
|
|||
|
|
<article>
|
|||
|
|
<h1 id="kap-supplychain">Kapitel 17: Supply-Chain und Energieeinsatz unter Unsicherheit</h1>
|
|||
|
|
<div class="card card-blick">
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>📌 Kapitel auf einen Blick</strong></p>
|
|||
|
|
<p><strong>Worum geht es?</strong> Um einen zusammenhängenden Industriefall, der die Werkzeuge aus Teil II und III zusammenführt: gemischt-ganzzahlige Entscheidungen, Szenarien, Risikomaße — an der Kraftwerkseinsatzplanung, der klassischen Aufgabe der Energiewirtschaft.</p>
|
|||
|
|
<p><strong>Voraussetzungen:</strong> <a href="milp.html#kap-milp">Kapitel 6</a>, <a href="unsicherheit.html#kap-unsicherheit">Kapitel 12</a> (zweistufige stochastische Programmierung) und <a href="cvar.html#kap-cvar">Kapitel 20</a>.</p>
|
|||
|
|
<p><strong>Danach können Sie:</strong> ein Modell aufsetzen, in dem die <strong>erste Stufe ganzzahlig</strong> ist und vor dem Zufall feststeht; erklären, warum eine Planung auf den Erwartungswert systematisch zu knapp ausfällt; und den Preis einer Versorgungssicherheitsvorgabe in Euro je vermiedener Megawattstunde ausweisen.</p>
|
|||
|
|
<p><strong>Zeitbedarf:</strong> ca. 4 Stunden.</p>
|
|||
|
|
<p><strong>Programme:</strong><br />
|
|||
|
|
<code>Kraftwerkseinsatz.py</code></p>
|
|||
|
|
<p><strong>Notebook:</strong> <a href="Notebooks_04/supplychain.ipynb">supplychain.ipynb</a><br />
|
|||
|
|
<a href="https://colab.research.google.com/github/dschlueter/or-mit-python/blob/main/Notebooks_04/supplychain.ipynb">In Google Colab öffnen</a></p>
|
|||
|
|
</blockquote>
|
|||
|
|
</div>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-schnellstart">17.1 In 5 Minuten gelöst</h2>
|
|||
|
|
<div class="card card-schnellstart">
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>🚀 In 5 Minuten gelöst: Warum „billigstes Kraftwerk zuerst” falsch ist</strong></p>
|
|||
|
|
<p>Zwei Kraftwerke können 120 MW liefern. Der Kernblock produziert für 22 €/MWh, die Gasturbine für 105 € — fünfmal so teuer. Die Merit-Order-Regel sagt: Kernblock.</p>
|
|||
|
|
<p>Sie übersieht die Anfahrkosten: 40 000 € gegen 1 500 €.</p>
|
|||
|
|
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Zwei Kraftwerke, die 120 MW decken sollen - aber wie lange?</span></span>
|
|||
|
|
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co"># Grenzkosten Anfahrkosten</span></span>
|
|||
|
|
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>kernblock <span class="op">=</span> <span class="bu">dict</span>(name<span class="op">=</span><span class="st">"Kernblock"</span>, grenz<span class="op">=</span><span class="dv">22</span>, anfahrt<span class="op">=</span><span class="dv">40_000</span>)</span>
|
|||
|
|
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>gasturbine <span class="op">=</span> <span class="bu">dict</span>(name<span class="op">=</span><span class="st">"Gasturbine"</span>, grenz<span class="op">=</span><span class="dv">105</span>, anfahrt<span class="op">=</span><span class="dv">1_500</span>)</span>
|
|||
|
|
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>LEISTUNG <span class="op">=</span> <span class="dv">120</span> <span class="co"># MW, die gebraucht werden</span></span>
|
|||
|
|
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>kosten <span class="op">=</span> <span class="kw">lambda</span> kw, stunden: kw[<span class="st">"anfahrt"</span>] <span class="op">+</span> kw[<span class="st">"grenz"</span>] <span class="op">*</span> LEISTUNG <span class="op">*</span> stunden</span>
|
|||
|
|
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span><span class="st">'Dauer'</span><span class="sc">:>7}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Kernblock'</span><span class="sc">:>12}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Gasturbine'</span><span class="sc">:>12}</span><span class="ss"> guenstiger"</span>)</span>
|
|||
|
|
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> stunden <span class="kw">in</span> (<span class="dv">1</span>, <span class="dv">2</span>, <span class="dv">3</span>, <span class="dv">4</span>, <span class="dv">5</span>, <span class="dv">6</span>, <span class="dv">8</span>):</span>
|
|||
|
|
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a> k, g <span class="op">=</span> kosten(kernblock, stunden), kosten(gasturbine, stunden)</span>
|
|||
|
|
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span>stunden<span class="sc">:>5}</span><span class="ss"> h </span><span class="sc">{</span>k<span class="sc">:>12,.0f}</span><span class="ss"> </span><span class="sc">{</span>g<span class="sc">:>12,.0f}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>kernblock[<span class="st">'name'</span>] <span class="cf">if</span> k <span class="op"><</span> g <span class="cf">else</span> gasturbine[<span class="st">'name'</span>]<span class="sc">}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>wechsel <span class="op">=</span> (kernblock[<span class="st">"anfahrt"</span>] <span class="op">-</span> gasturbine[<span class="st">"anfahrt"</span>]) <span class="op">/</span> (</span>
|
|||
|
|
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> (gasturbine[<span class="st">"grenz"</span>] <span class="op">-</span> kernblock[<span class="st">"grenz"</span>]) <span class="op">*</span> LEISTUNG)</span>
|
|||
|
|
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss">Umschlagpunkt: </span><span class="sc">{</span>wechsel<span class="sc">:.1f}</span><span class="ss"> Stunden"</span>)</span></code></pre></div>
|
|||
|
|
<p><strong>Ausgabe:</strong></p>
|
|||
|
|
<pre><code> Dauer Kernblock Gasturbine guenstiger
|
|||
|
|
1 h 42,640 14,100 Gasturbine
|
|||
|
|
2 h 45,280 26,700 Gasturbine
|
|||
|
|
3 h 47,920 39,300 Gasturbine
|
|||
|
|
4 h 50,560 51,900 Kernblock
|
|||
|
|
5 h 53,200 64,500 Kernblock
|
|||
|
|
6 h 55,840 77,100 Kernblock
|
|||
|
|
8 h 61,120 102,300 Kernblock
|
|||
|
|
|
|||
|
|
Umschlagpunkt: 3.9 Stunden</code></pre>
|
|||
|
|
</blockquote>
|
|||
|
|
</div>
|
|||
|
|
<p><strong>Und jetzt der Punkt.</strong> Bis knapp vier Stunden ist die <em>fünfmal teurere</em> Gasturbine die günstigere Wahl. Erst danach hat der Kernblock seine Anfahrkosten wieder eingespielt.</p>
|
|||
|
|
<p>Die Merit-Order — Kraftwerke nach Grenzkosten sortieren und von unten auffüllen — ist damit nur für einen <strong>einzelnen</strong> Zeitpunkt richtig. Sobald ein Tag geplant wird, hängt jede Stunde an allen anderen: Ein Block, der um 8 Uhr angefahren wird, muss bis mindestens 16 Uhr laufen, und diese Entscheidung fällt am Vorabend.</p>
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>🎯 Merksatz</strong> Anfahrkosten und Mindestlaufzeiten machen aus 24 unabhängigen Stundenentscheidungen <strong>ein</strong> Problem. Genau deshalb ist die Kraftwerkseinsatzplanung ein MILP und keine Sortierung.</p>
|
|||
|
|
</blockquote>
|
|||
|
|
<p><strong>Warum funktioniert das?</strong> Der Umschlagpunkt ist eine einfache Rechnung: Die Anfahrkostendifferenz (38 500 €) geteilt durch die Grenzkostendifferenz je Stunde (83 €/MWh × 120 MW = 9 960 €/h) ergibt 3,9 Stunden. Bei zwei Kraftwerken kann man das im Kopf ausrechnen — bei fünf Blöcken und 24 Stunden nicht mehr.</p>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-lernziele">17.2 Lernziele</h2>
|
|||
|
|
<p>Nach diesem Kapitel können Sie …</p>
|
|||
|
|
<ol type="1">
|
|||
|
|
<li>… ein Unit-Commitment-Modell mit Anfahrkosten und Mindestlaufzeiten aufstellen.</li>
|
|||
|
|
<li>… erklären, warum eine <strong>binäre erste Stufe</strong> das zweistufige Modell aus <a href="unsicherheit.html#kap-unsicherheit">Kapitel 12</a> verschärft.</li>
|
|||
|
|
<li>… zeigen, dass eine Planung auf den Erwartungswert nicht nur ungenau, sondern <strong>systematisch zu knapp</strong> ist.</li>
|
|||
|
|
<li>… eine CVaR-Schranke auf eine physikalische Größe (nicht gedeckte Energie) legen.</li>
|
|||
|
|
<li>… begründen, wann eine Risikoschranke etwas ändert — und wann sie überflüssig ist.</li>
|
|||
|
|
</ol>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-aufgabe">17.3 Die Aufgabe</h2>
|
|||
|
|
<p>Fünf Blöcke, 24 Stunden, eine Last zwischen 270 und 804 MW. Dazu Windeinspeisung, die niemand am Vorabend kennt.</p>
|
|||
|
|
<table>
|
|||
|
|
<colgroup>
|
|||
|
|
<col style="width: 13%" />
|
|||
|
|
<col style="width: 17%" />
|
|||
|
|
<col style="width: 17%" />
|
|||
|
|
<col style="width: 17%" />
|
|||
|
|
<col style="width: 17%" />
|
|||
|
|
<col style="width: 17%" />
|
|||
|
|
</colgroup>
|
|||
|
|
<thead>
|
|||
|
|
<tr class="header">
|
|||
|
|
<th>Block</th>
|
|||
|
|
<th style="text-align: right;"><span class="math inline">P_{\min}</span></th>
|
|||
|
|
<th style="text-align: right;"><span class="math inline">P_{\max}</span></th>
|
|||
|
|
<th style="text-align: right;">Grenzkosten</th>
|
|||
|
|
<th style="text-align: right;">Anfahrkosten</th>
|
|||
|
|
<th style="text-align: right;">Mindestlaufzeit</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td>Kernblock</td>
|
|||
|
|
<td style="text-align: right;">200</td>
|
|||
|
|
<td style="text-align: right;">600</td>
|
|||
|
|
<td style="text-align: right;">22 €/MWh</td>
|
|||
|
|
<td style="text-align: right;">40 000 €</td>
|
|||
|
|
<td style="text-align: right;">8 h</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td>Braunkohle</td>
|
|||
|
|
<td style="text-align: right;">100</td>
|
|||
|
|
<td style="text-align: right;">400</td>
|
|||
|
|
<td style="text-align: right;">35 €/MWh</td>
|
|||
|
|
<td style="text-align: right;">18 000 €</td>
|
|||
|
|
<td style="text-align: right;">6 h</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td>Steinkohle</td>
|
|||
|
|
<td style="text-align: right;">80</td>
|
|||
|
|
<td style="text-align: right;">300</td>
|
|||
|
|
<td style="text-align: right;">48 €/MWh</td>
|
|||
|
|
<td style="text-align: right;">12 000 €</td>
|
|||
|
|
<td style="text-align: right;">4 h</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td>Gas GuD</td>
|
|||
|
|
<td style="text-align: right;">50</td>
|
|||
|
|
<td style="text-align: right;">250</td>
|
|||
|
|
<td style="text-align: right;">72 €/MWh</td>
|
|||
|
|
<td style="text-align: right;">6 000 €</td>
|
|||
|
|
<td style="text-align: right;">2 h</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td>Gasturbine</td>
|
|||
|
|
<td style="text-align: right;">20</td>
|
|||
|
|
<td style="text-align: right;">150</td>
|
|||
|
|
<td style="text-align: right;">105 €/MWh</td>
|
|||
|
|
<td style="text-align: right;">1 500 €</td>
|
|||
|
|
<td style="text-align: right;">1 h</td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
<p>Die Reihenfolge ist typisch: <strong>je billiger im Betrieb, desto träger und teurer im Anfahren.</strong> Das ist keine Willkür, sondern Physik — ein großer Dampfprozess braucht Stunden, bis er auf Temperatur ist.</p>
|
|||
|
|
<h3 id="die-zeitstruktur-der-entscheidung">Die Zeitstruktur der Entscheidung</h3>
|
|||
|
|
<p>Hier geht das Kapitel über <a href="unsicherheit.html#sec:unsicherheit-zweistufige-stochastische-programmierung">Abschnitt 12.5</a> hinaus:</p>
|
|||
|
|
<table>
|
|||
|
|
<colgroup>
|
|||
|
|
<col style="width: 25%" />
|
|||
|
|
<col style="width: 25%" />
|
|||
|
|
<col style="width: 25%" />
|
|||
|
|
<col style="width: 25%" />
|
|||
|
|
</colgroup>
|
|||
|
|
<thead>
|
|||
|
|
<tr class="header">
|
|||
|
|
<th>Stufe</th>
|
|||
|
|
<th>Entscheidung</th>
|
|||
|
|
<th>Typ</th>
|
|||
|
|
<th>wann</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td><strong>1</strong></td>
|
|||
|
|
<td>Welcher Block läuft in welcher Stunde?</td>
|
|||
|
|
<td><strong>binär</strong>, für alle Szenarien gleich</td>
|
|||
|
|
<td>am Vorabend, bevor der Wind bekannt ist</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td><strong>2</strong></td>
|
|||
|
|
<td>Wie viel liefert jeder laufende Block?</td>
|
|||
|
|
<td>kontinuierlich, je Szenario verschieden</td>
|
|||
|
|
<td>am Tag selbst, laufend anpassbar</td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
<p>Im Kapitel Unsicherheit war die erste Stufe kontinuierlich (Kapazität kaufen, Menge festlegen). Hier ist sie <strong>ganzzahlig</strong> — und das ändert die Sache grundlegend: Eine Kapazität lässt sich anteilig erhöhen, ein Kraftwerk nicht zu 30 % anfahren.</p>
|
|||
|
|
<div class="card card-formel">
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>🔤 Formel-Übersetzer: die Kopplung der beiden Stufen</strong></p>
|
|||
|
|
<table>
|
|||
|
|
<colgroup>
|
|||
|
|
<col style="width: 50%" />
|
|||
|
|
<col style="width: 50%" />
|
|||
|
|
</colgroup>
|
|||
|
|
<thead>
|
|||
|
|
<tr class="header">
|
|||
|
|
<th>Mathematik</th>
|
|||
|
|
<th>Alltagssprache</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td><span class="math inline">u_{k,t} \in \{0,1\}</span></td>
|
|||
|
|
<td>„Läuft Block <span class="math inline">k</span> in Stunde <span class="math inline">t</span>?” — <strong>eine</strong> Antwort für alle Szenarien</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td><span class="math inline">p^s_{k,t} \ge P^{\min}_k \, u_{k,t}</span></td>
|
|||
|
|
<td>„Ein laufender Block liefert mindestens seine Mindestleistung.”</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td><span class="math inline">p^s_{k,t} \le P^{\max}_k \, u_{k,t}</span></td>
|
|||
|
|
<td>„Ein stehender Block liefert gar nichts.” — dieselbe Big-M-Kopplung wie in <a href="milp.html#kap-milp">Kapitel 6</a></td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td><span class="math inline">a_{k,t} \ge u_{k,t} - u_{k,t-1}</span></td>
|
|||
|
|
<td>„Wer eben noch aus war und jetzt an ist, ist angefahren.”</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td><span class="math inline">u_{k,\tau} \ge a_{k,t}</span> für <span class="math inline">\tau = t \dots t{+}L_k</span></td>
|
|||
|
|
<td>Mindestlaufzeit: „Wer anfährt, bleibt <span class="math inline">L_k</span> Stunden am Netz.”</td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td><span class="math inline">\sum_k p^s_{k,t} + w^s_t + y^s_t \ge D_t</span></td>
|
|||
|
|
<td>„Erzeugung plus Wind plus Nichtdeckung deckt die Last.”</td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
<p>Die beiden mittleren Zeilen sind der ganze Trick: Sie verbinden eine <strong>binäre</strong> Größe (<span class="math inline">u</span>) mit einer <strong>kontinuierlichen</strong> (<span class="math inline">p</span>) — und weil <span class="math inline">u</span> szenarioübergreifend gleich ist, die Szenarien untereinander.</p>
|
|||
|
|
</blockquote>
|
|||
|
|
</div>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-programm">17.4 Das Programm</h2>
|
|||
|
|
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
|
|||
|
|
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Kraftwerkseinsatz.py</span></span>
|
|||
|
|
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">"""</span></span>
|
|||
|
|
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Supply-Chain: Welche Bloecke laufen morgen? Und was, wenn kein Wind weht?</span></span>
|
|||
|
|
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="co">Die Kraftwerkseinsatzplanung (englisch Unit Commitment) ist die Aufgabe, an der</span></span>
|
|||
|
|
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="co">sich in der Energiewirtschaft alles entscheidet: Fuer jede Stunde des naechsten</span></span>
|
|||
|
|
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="co">Tages muss feststehen, welche Bloecke am Netz sind. Ein Kernblock braucht acht</span></span>
|
|||
|
|
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="co">Stunden Mindestlaufzeit und 40.000 Euro Anfahrkosten - wer ihn abschaltet, hat</span></span>
|
|||
|
|
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a><span class="co">ihn fuer den Rest des Tages verloren.</span></span>
|
|||
|
|
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="co">Das Besondere liegt in der Zeitstruktur, und es geht ueber das zweistufige</span></span>
|
|||
|
|
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="co">Modell aus dem Kapitel Unsicherheit hinaus:</span></span>
|
|||
|
|
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="co"> STUFE 1 Das AN/AUS je Block und Stunde. Binaer, und es steht am Vorabend</span></span>
|
|||
|
|
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="co"> fest - bevor irgendjemand weiss, wie viel Wind morgen weht.</span></span>
|
|||
|
|
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a><span class="co"> STUFE 2 Die Fahrweise: wie viel jeder laufende Block liefert. Das darf</span></span>
|
|||
|
|
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a><span class="co"> sich stundenweise an die Wirklichkeit anpassen.</span></span>
|
|||
|
|
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a><span class="co">Die erste Stufe ist also GANZZAHLIG und szenariouebergreifend gleich, die</span></span>
|
|||
|
|
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a><span class="co">zweite kontinuierlich und je Szenario verschieden. Genau diese Kombination</span></span>
|
|||
|
|
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a><span class="co">macht das Problem interessant - und sie ist der Grund, warum ein Plan, der auf</span></span>
|
|||
|
|
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a><span class="co">den Wind-Erwartungswert gerechnet wurde, in der Wirklichkeit teuer wird.</span></span>
|
|||
|
|
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a><span class="co">Das Programm zeigt drei Plaene auf denselben Daten:</span></span>
|
|||
|
|
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a><span class="co"> 1. Deterministisch: gerechnet mit dem Wind-ERWARTUNGSWERT, danach gegen 40</span></span>
|
|||
|
|
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a><span class="co"> Szenarien ausgewertet.</span></span>
|
|||
|
|
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a><span class="co"> 2. Zweistufig: der Commitment-Plan sieht alle 40 Szenarien.</span></span>
|
|||
|
|
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a><span class="co"> 3. Mit Versorgungssicherheit: die Nichtdeckung in den schlechtesten Faellen</span></span>
|
|||
|
|
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a><span class="co"> wird begrenzt - und der Preis dafuer in Euro je vermiedener MWh</span></span>
|
|||
|
|
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a><span class="co"> ausgewiesen.</span></span>
|
|||
|
|
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a><span class="co">Benoetigt: numpy, ortools</span></span>
|
|||
|
|
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a><span class="co">"""</span></span>
|
|||
|
|
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> __future__ <span class="im">import</span> annotations</span>
|
|||
|
|
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> time</span>
|
|||
|
|
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
|
|||
|
|
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true" tabindex="-1"></a><span class="im">from</span> ortools.linear_solver <span class="im">import</span> pywraplp</span>
|
|||
|
|
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true" tabindex="-1"></a><span class="co"># (Name, Mindestleistung, Nennleistung, Grenzkosten, Anfahrkosten, Mindestlaufzeit)</span></span>
|
|||
|
|
<span id="cb3-46"><a href="#cb3-46" aria-hidden="true" tabindex="-1"></a>KRAFTWERKE <span class="op">=</span> [</span>
|
|||
|
|
<span id="cb3-47"><a href="#cb3-47" aria-hidden="true" tabindex="-1"></a> (<span class="st">"Kernblock"</span>, <span class="dv">200</span>, <span class="dv">600</span>, <span class="dv">22</span>, <span class="dv">40_000</span>, <span class="dv">8</span>),</span>
|
|||
|
|
<span id="cb3-48"><a href="#cb3-48" aria-hidden="true" tabindex="-1"></a> (<span class="st">"Braunkohle"</span>, <span class="dv">100</span>, <span class="dv">400</span>, <span class="dv">35</span>, <span class="dv">18_000</span>, <span class="dv">6</span>),</span>
|
|||
|
|
<span id="cb3-49"><a href="#cb3-49" aria-hidden="true" tabindex="-1"></a> (<span class="st">"Steinkohle"</span>, <span class="dv">80</span>, <span class="dv">300</span>, <span class="dv">48</span>, <span class="dv">12_000</span>, <span class="dv">4</span>),</span>
|
|||
|
|
<span id="cb3-50"><a href="#cb3-50" aria-hidden="true" tabindex="-1"></a> (<span class="st">"Gas GuD"</span>, <span class="dv">50</span>, <span class="dv">250</span>, <span class="dv">72</span>, <span class="dv">6_000</span>, <span class="dv">2</span>),</span>
|
|||
|
|
<span id="cb3-51"><a href="#cb3-51" aria-hidden="true" tabindex="-1"></a> (<span class="st">"Gasturbine"</span>, <span class="dv">20</span>, <span class="dv">150</span>, <span class="dv">105</span>, <span class="dv">1_500</span>, <span class="dv">1</span>),</span>
|
|||
|
|
<span id="cb3-52"><a href="#cb3-52" aria-hidden="true" tabindex="-1"></a>]</span>
|
|||
|
|
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true" tabindex="-1"></a>STUNDEN <span class="op">=</span> <span class="dv">24</span></span>
|
|||
|
|
<span id="cb3-54"><a href="#cb3-54" aria-hidden="true" tabindex="-1"></a>SZENARIEN <span class="op">=</span> <span class="dv">40</span></span>
|
|||
|
|
<span id="cb3-55"><a href="#cb3-55" aria-hidden="true" tabindex="-1"></a>FLAUTENANTEIL <span class="op">=</span> <span class="fl">0.15</span></span>
|
|||
|
|
<span id="cb3-56"><a href="#cb3-56" aria-hidden="true" tabindex="-1"></a>ALPHA <span class="op">=</span> <span class="fl">0.90</span> <span class="co"># die schlechtesten 10 % der Szenarien</span></span>
|
|||
|
|
<span id="cb3-57"><a href="#cb3-57" aria-hidden="true" tabindex="-1"></a>LASTABWURF <span class="op">=</span> <span class="fl">3_000.0</span> <span class="co"># EUR je nicht gedeckter MWh ("value of lost load")</span></span>
|
|||
|
|
<span id="cb3-58"><a href="#cb3-58" aria-hidden="true" tabindex="-1"></a>NIEDRIGER_ABWURFPREIS <span class="op">=</span> <span class="fl">300.0</span> <span class="co"># zum Vergleich: ein Marktpreisdeckel</span></span>
|
|||
|
|
<span id="cb3-59"><a href="#cb3-59" aria-hidden="true" tabindex="-1"></a>SAAT <span class="op">=</span> <span class="dv">4</span></span>
|
|||
|
|
<span id="cb3-60"><a href="#cb3-60" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-61"><a href="#cb3-61" aria-hidden="true" tabindex="-1"></a>_t <span class="op">=</span> np.arange(STUNDEN)</span>
|
|||
|
|
<span id="cb3-62"><a href="#cb3-62" aria-hidden="true" tabindex="-1"></a>LAST <span class="op">=</span> <span class="dv">620</span> <span class="op">+</span> <span class="dv">260</span> <span class="op">*</span> np.sin((_t <span class="op">-</span> <span class="dv">7</span>) <span class="op">/</span> <span class="dv">24</span> <span class="op">*</span> <span class="dv">2</span> <span class="op">*</span> np.pi) <span class="op">+</span> <span class="dv">90</span> <span class="op">*</span> np.sin((_t <span class="op">-</span> <span class="dv">4</span>) <span class="op">/</span> <span class="dv">12</span> <span class="op">*</span> <span class="dv">2</span> <span class="op">*</span> np.pi)</span>
|
|||
|
|
<span id="cb3-63"><a href="#cb3-63" aria-hidden="true" tabindex="-1"></a>WIND_ERWARTUNG <span class="op">=</span> np.maximum(<span class="fl">0.0</span>, <span class="dv">160</span> <span class="op">+</span> <span class="dv">130</span> <span class="op">*</span> np.sin((_t <span class="op">-</span> <span class="dv">14</span>) <span class="op">/</span> <span class="dv">24</span> <span class="op">*</span> <span class="dv">2</span> <span class="op">*</span> np.pi))</span>
|
|||
|
|
<span id="cb3-64"><a href="#cb3-64" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-65"><a href="#cb3-65" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-66"><a href="#cb3-66" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> erzeuge_windszenarien(anzahl: <span class="bu">int</span> <span class="op">=</span> SZENARIEN, saat: <span class="bu">int</span> <span class="op">=</span> SAAT):</span>
|
|||
|
|
<span id="cb3-67"><a href="#cb3-67" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Windeinspeisung je Szenario und Stunde.</span></span>
|
|||
|
|
<span id="cb3-68"><a href="#cb3-68" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-69"><a href="#cb3-69" aria-hidden="true" tabindex="-1"></a><span class="co"> Zwei Zutaten, die zusammen den Unterschied machen: eine breite</span></span>
|
|||
|
|
<span id="cb3-70"><a href="#cb3-70" aria-hidden="true" tabindex="-1"></a><span class="co"> lognormale Streuung des Tagesniveaus - und in 15 % der Faelle eine</span></span>
|
|||
|
|
<span id="cb3-71"><a href="#cb3-71" aria-hidden="true" tabindex="-1"></a><span class="co"> DUNKELFLAUTE, in der praktisch gar kein Wind weht. Solche seltenen,</span></span>
|
|||
|
|
<span id="cb3-72"><a href="#cb3-72" aria-hidden="true" tabindex="-1"></a><span class="co"> extremen Faelle sind der Grund, warum der Erwartungswert als</span></span>
|
|||
|
|
<span id="cb3-73"><a href="#cb3-73" aria-hidden="true" tabindex="-1"></a><span class="co"> Planungsgrundlage nicht genuegt.</span></span>
|
|||
|
|
<span id="cb3-74"><a href="#cb3-74" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
|||
|
|
<span id="cb3-75"><a href="#cb3-75" aria-hidden="true" tabindex="-1"></a> rng <span class="op">=</span> np.random.default_rng(saat)</span>
|
|||
|
|
<span id="cb3-76"><a href="#cb3-76" aria-hidden="true" tabindex="-1"></a> tagesniveau <span class="op">=</span> rng.lognormal(<span class="dv">0</span>, <span class="fl">0.40</span>, (anzahl, <span class="dv">1</span>))</span>
|
|||
|
|
<span id="cb3-77"><a href="#cb3-77" aria-hidden="true" tabindex="-1"></a> wind <span class="op">=</span> np.clip(WIND_ERWARTUNG <span class="op">*</span> tagesniveau</span>
|
|||
|
|
<span id="cb3-78"><a href="#cb3-78" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> rng.normal(<span class="dv">0</span>, <span class="dv">25</span>, (anzahl, STUNDEN)), <span class="dv">0</span>, <span class="dv">420</span>)</span>
|
|||
|
|
<span id="cb3-79"><a href="#cb3-79" aria-hidden="true" tabindex="-1"></a> ist_flaute <span class="op">=</span> rng.random(anzahl) <span class="op"><</span> FLAUTENANTEIL</span>
|
|||
|
|
<span id="cb3-80"><a href="#cb3-80" aria-hidden="true" tabindex="-1"></a> wind[ist_flaute] <span class="op">*=</span> <span class="fl">0.05</span></span>
|
|||
|
|
<span id="cb3-81"><a href="#cb3-81" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> wind, ist_flaute</span>
|
|||
|
|
<span id="cb3-82"><a href="#cb3-82" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-83"><a href="#cb3-83" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-84"><a href="#cb3-84" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> plane(wind: np.ndarray, gewichte, cvar_grenze: <span class="bu">float</span> <span class="op">|</span> <span class="va">None</span> <span class="op">=</span> <span class="va">None</span>,</span>
|
|||
|
|
<span id="cb3-85"><a href="#cb3-85" aria-hidden="true" tabindex="-1"></a> abwurfpreis: <span class="bu">float</span> <span class="op">=</span> <span class="va">None</span>, zeitlimit: <span class="bu">float</span> <span class="op">=</span> <span class="fl">300.0</span>):</span>
|
|||
|
|
<span id="cb3-86"><a href="#cb3-86" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Commitment (Stufe 1) und Fahrweise je Szenario (Stufe 2) in einem Modell.</span></span>
|
|||
|
|
<span id="cb3-87"><a href="#cb3-87" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-88"><a href="#cb3-88" aria-hidden="true" tabindex="-1"></a><span class="co"> 'wind' hat die Form (Szenarien, Stunden). Mit einer einzigen Zeile</span></span>
|
|||
|
|
<span id="cb3-89"><a href="#cb3-89" aria-hidden="true" tabindex="-1"></a><span class="co"> Windprognose wird daraus die deterministische Planung.</span></span>
|
|||
|
|
<span id="cb3-90"><a href="#cb3-90" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-91"><a href="#cb3-91" aria-hidden="true" tabindex="-1"></a><span class="co"> 'cvar_grenze' begrenzt den CVaR der Nichtdeckung ueber die Szenarien -</span></span>
|
|||
|
|
<span id="cb3-92"><a href="#cb3-92" aria-hidden="true" tabindex="-1"></a><span class="co"> dieselbe Rockafellar-Uryasev-Konstruktion wie im Kapitel CVaR, nur dass</span></span>
|
|||
|
|
<span id="cb3-93"><a href="#cb3-93" aria-hidden="true" tabindex="-1"></a><span class="co"> hier keine Verluste in Euro, sondern Megawattstunden begrenzt werden.</span></span>
|
|||
|
|
<span id="cb3-94"><a href="#cb3-94" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
|||
|
|
<span id="cb3-95"><a href="#cb3-95" aria-hidden="true" tabindex="-1"></a> abwurfpreis <span class="op">=</span> LASTABWURF <span class="cf">if</span> abwurfpreis <span class="kw">is</span> <span class="va">None</span> <span class="cf">else</span> abwurfpreis</span>
|
|||
|
|
<span id="cb3-96"><a href="#cb3-96" aria-hidden="true" tabindex="-1"></a> anzahl_szenarien <span class="op">=</span> wind.shape[<span class="dv">0</span>]</span>
|
|||
|
|
<span id="cb3-97"><a href="#cb3-97" aria-hidden="true" tabindex="-1"></a> anzahl_bloecke <span class="op">=</span> <span class="bu">len</span>(KRAFTWERKE)</span>
|
|||
|
|
<span id="cb3-98"><a href="#cb3-98" aria-hidden="true" tabindex="-1"></a> solver <span class="op">=</span> pywraplp.Solver.CreateSolver(<span class="st">"SCIP"</span>)</span>
|
|||
|
|
<span id="cb3-99"><a href="#cb3-99" aria-hidden="true" tabindex="-1"></a> solver.SetTimeLimit(<span class="bu">int</span>(zeitlimit <span class="op">*</span> <span class="dv">1000</span>))</span>
|
|||
|
|
<span id="cb3-100"><a href="#cb3-100" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-101"><a href="#cb3-101" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Stufe 1: binaer, szenariouebergreifend gleich --------------------</span></span>
|
|||
|
|
<span id="cb3-102"><a href="#cb3-102" aria-hidden="true" tabindex="-1"></a> laeuft <span class="op">=</span> [[solver.BoolVar(<span class="ss">f"laeuft_</span><span class="sc">{</span>k<span class="sc">}</span><span class="ss">_</span><span class="sc">{</span>t<span class="sc">}</span><span class="ss">"</span>) <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)]</span>
|
|||
|
|
<span id="cb3-103"><a href="#cb3-103" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke)]</span>
|
|||
|
|
<span id="cb3-104"><a href="#cb3-104" aria-hidden="true" tabindex="-1"></a> faehrt_an <span class="op">=</span> [[solver.BoolVar(<span class="ss">f"start_</span><span class="sc">{</span>k<span class="sc">}</span><span class="ss">_</span><span class="sc">{</span>t<span class="sc">}</span><span class="ss">"</span>) <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)]</span>
|
|||
|
|
<span id="cb3-105"><a href="#cb3-105" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke)]</span>
|
|||
|
|
<span id="cb3-106"><a href="#cb3-106" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-107"><a href="#cb3-107" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Stufe 2: kontinuierlich, je Szenario -----------------------------</span></span>
|
|||
|
|
<span id="cb3-108"><a href="#cb3-108" aria-hidden="true" tabindex="-1"></a> leistung <span class="op">=</span> [[[solver.NumVar(<span class="dv">0</span>, KRAFTWERKE[k][<span class="dv">2</span>], <span class="ss">f"p_</span><span class="sc">{</span>j<span class="sc">}</span><span class="ss">_</span><span class="sc">{</span>k<span class="sc">}</span><span class="ss">_</span><span class="sc">{</span>t<span class="sc">}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-109"><a href="#cb3-109" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)] <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke)]</span>
|
|||
|
|
<span id="cb3-110"><a href="#cb3-110" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(anzahl_szenarien)]</span>
|
|||
|
|
<span id="cb3-111"><a href="#cb3-111" aria-hidden="true" tabindex="-1"></a> nichtdeckung <span class="op">=</span> [[solver.NumVar(<span class="dv">0</span>, solver.infinity(), <span class="ss">f"y_</span><span class="sc">{</span>j<span class="sc">}</span><span class="ss">_</span><span class="sc">{</span>t<span class="sc">}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-112"><a href="#cb3-112" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)] <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(anzahl_szenarien)]</span>
|
|||
|
|
<span id="cb3-113"><a href="#cb3-113" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-114"><a href="#cb3-114" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k, (_, pmin, pmax, _, _, mindestlaufzeit) <span class="kw">in</span> <span class="bu">enumerate</span>(KRAFTWERKE):</span>
|
|||
|
|
<span id="cb3-115"><a href="#cb3-115" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN):</span>
|
|||
|
|
<span id="cb3-116"><a href="#cb3-116" aria-hidden="true" tabindex="-1"></a> <span class="co"># Anfahren erkennen: aus im Vortakt, an im aktuellen</span></span>
|
|||
|
|
<span id="cb3-117"><a href="#cb3-117" aria-hidden="true" tabindex="-1"></a> vorher <span class="op">=</span> laeuft[k][t <span class="op">-</span> <span class="dv">1</span>] <span class="cf">if</span> t <span class="op">></span> <span class="dv">0</span> <span class="cf">else</span> <span class="dv">0</span></span>
|
|||
|
|
<span id="cb3-118"><a href="#cb3-118" aria-hidden="true" tabindex="-1"></a> solver.Add(faehrt_an[k][t] <span class="op">>=</span> laeuft[k][t] <span class="op">-</span> vorher)</span>
|
|||
|
|
<span id="cb3-119"><a href="#cb3-119" aria-hidden="true" tabindex="-1"></a> <span class="co"># Mindestlaufzeit: wer anfaehrt, laeuft die naechsten Stunden weiter</span></span>
|
|||
|
|
<span id="cb3-120"><a href="#cb3-120" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> spaeter <span class="kw">in</span> <span class="bu">range</span>(t, <span class="bu">min</span>(STUNDEN, t <span class="op">+</span> mindestlaufzeit)):</span>
|
|||
|
|
<span id="cb3-121"><a href="#cb3-121" aria-hidden="true" tabindex="-1"></a> solver.Add(laeuft[k][spaeter] <span class="op">>=</span> faehrt_an[k][t])</span>
|
|||
|
|
<span id="cb3-122"><a href="#cb3-122" aria-hidden="true" tabindex="-1"></a> <span class="co"># Ein laufender Block liefert zwischen Mindest- und Nennleistung,</span></span>
|
|||
|
|
<span id="cb3-123"><a href="#cb3-123" aria-hidden="true" tabindex="-1"></a> <span class="co"># ein stehender gar nichts. Das koppelt beide Stufen.</span></span>
|
|||
|
|
<span id="cb3-124"><a href="#cb3-124" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(anzahl_szenarien):</span>
|
|||
|
|
<span id="cb3-125"><a href="#cb3-125" aria-hidden="true" tabindex="-1"></a> solver.Add(leistung[j][k][t] <span class="op">>=</span> pmin <span class="op">*</span> laeuft[k][t])</span>
|
|||
|
|
<span id="cb3-126"><a href="#cb3-126" aria-hidden="true" tabindex="-1"></a> solver.Add(leistung[j][k][t] <span class="op"><=</span> pmax <span class="op">*</span> laeuft[k][t])</span>
|
|||
|
|
<span id="cb3-127"><a href="#cb3-127" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-128"><a href="#cb3-128" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(anzahl_szenarien):</span>
|
|||
|
|
<span id="cb3-129"><a href="#cb3-129" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN):</span>
|
|||
|
|
<span id="cb3-130"><a href="#cb3-130" aria-hidden="true" tabindex="-1"></a> solver.Add(<span class="bu">sum</span>(leistung[j][k][t] <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke))</span>
|
|||
|
|
<span id="cb3-131"><a href="#cb3-131" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> <span class="bu">float</span>(wind[j, t]) <span class="op">+</span> nichtdeckung[j][t] <span class="op">>=</span> LAST[t])</span>
|
|||
|
|
<span id="cb3-132"><a href="#cb3-132" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-133"><a href="#cb3-133" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Versorgungssicherheit als CVaR-Schranke --------------------------</span></span>
|
|||
|
|
<span id="cb3-134"><a href="#cb3-134" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> cvar_grenze <span class="kw">is</span> <span class="kw">not</span> <span class="va">None</span>:</span>
|
|||
|
|
<span id="cb3-135"><a href="#cb3-135" aria-hidden="true" tabindex="-1"></a> schwelle <span class="op">=</span> solver.NumVar(<span class="op">-</span>solver.infinity(), solver.infinity(), <span class="st">"schwelle"</span>)</span>
|
|||
|
|
<span id="cb3-136"><a href="#cb3-136" aria-hidden="true" tabindex="-1"></a> ueberschuss <span class="op">=</span> [solver.NumVar(<span class="dv">0</span>, solver.infinity(), <span class="ss">f"u_</span><span class="sc">{</span>j<span class="sc">}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-137"><a href="#cb3-137" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(anzahl_szenarien)]</span>
|
|||
|
|
<span id="cb3-138"><a href="#cb3-138" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(anzahl_szenarien):</span>
|
|||
|
|
<span id="cb3-139"><a href="#cb3-139" aria-hidden="true" tabindex="-1"></a> solver.Add(ueberschuss[j] <span class="op">>=</span> <span class="bu">sum</span>(nichtdeckung[j][t]</span>
|
|||
|
|
<span id="cb3-140"><a href="#cb3-140" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)) <span class="op">-</span> schwelle)</span>
|
|||
|
|
<span id="cb3-141"><a href="#cb3-141" aria-hidden="true" tabindex="-1"></a> solver.Add(schwelle <span class="op">+</span> (<span class="fl">1.0</span> <span class="op">/</span> (anzahl_szenarien <span class="op">*</span> (<span class="dv">1</span> <span class="op">-</span> ALPHA)))</span>
|
|||
|
|
<span id="cb3-142"><a href="#cb3-142" aria-hidden="true" tabindex="-1"></a> <span class="op">*</span> <span class="bu">sum</span>(ueberschuss) <span class="op"><=</span> cvar_grenze)</span>
|
|||
|
|
<span id="cb3-143"><a href="#cb3-143" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-144"><a href="#cb3-144" aria-hidden="true" tabindex="-1"></a> anfahrkosten <span class="op">=</span> <span class="bu">sum</span>(KRAFTWERKE[k][<span class="dv">4</span>] <span class="op">*</span> faehrt_an[k][t]</span>
|
|||
|
|
<span id="cb3-145"><a href="#cb3-145" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke) <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN))</span>
|
|||
|
|
<span id="cb3-146"><a href="#cb3-146" aria-hidden="true" tabindex="-1"></a> betriebskosten <span class="op">=</span> <span class="bu">sum</span>(</span>
|
|||
|
|
<span id="cb3-147"><a href="#cb3-147" aria-hidden="true" tabindex="-1"></a> gewichte[j] <span class="op">*</span> <span class="bu">sum</span>(KRAFTWERKE[k][<span class="dv">3</span>] <span class="op">*</span> leistung[j][k][t]</span>
|
|||
|
|
<span id="cb3-148"><a href="#cb3-148" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke) <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN))</span>
|
|||
|
|
<span id="cb3-149"><a href="#cb3-149" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(anzahl_szenarien))</span>
|
|||
|
|
<span id="cb3-150"><a href="#cb3-150" aria-hidden="true" tabindex="-1"></a> abwurfkosten <span class="op">=</span> <span class="bu">sum</span>(gewichte[j] <span class="op">*</span> abwurfpreis</span>
|
|||
|
|
<span id="cb3-151"><a href="#cb3-151" aria-hidden="true" tabindex="-1"></a> <span class="op">*</span> <span class="bu">sum</span>(nichtdeckung[j][t] <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN))</span>
|
|||
|
|
<span id="cb3-152"><a href="#cb3-152" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(anzahl_szenarien))</span>
|
|||
|
|
<span id="cb3-153"><a href="#cb3-153" aria-hidden="true" tabindex="-1"></a> solver.Minimize(anfahrkosten <span class="op">+</span> betriebskosten <span class="op">+</span> abwurfkosten)</span>
|
|||
|
|
<span id="cb3-154"><a href="#cb3-154" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-155"><a href="#cb3-155" aria-hidden="true" tabindex="-1"></a> beginn <span class="op">=</span> time.perf_counter()</span>
|
|||
|
|
<span id="cb3-156"><a href="#cb3-156" aria-hidden="true" tabindex="-1"></a> status <span class="op">=</span> solver.Solve()</span>
|
|||
|
|
<span id="cb3-157"><a href="#cb3-157" aria-hidden="true" tabindex="-1"></a> dauer <span class="op">=</span> time.perf_counter() <span class="op">-</span> beginn</span>
|
|||
|
|
<span id="cb3-158"><a href="#cb3-158" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> status <span class="kw">not</span> <span class="kw">in</span> (pywraplp.Solver.OPTIMAL, pywraplp.Solver.FEASIBLE):</span>
|
|||
|
|
<span id="cb3-159"><a href="#cb3-159" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="va">None</span>, dauer, <span class="va">False</span></span>
|
|||
|
|
<span id="cb3-160"><a href="#cb3-160" aria-hidden="true" tabindex="-1"></a> plan <span class="op">=</span> np.array([[laeuft[k][t].solution_value() <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)]</span>
|
|||
|
|
<span id="cb3-161"><a href="#cb3-161" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke)]).<span class="bu">round</span>()</span>
|
|||
|
|
<span id="cb3-162"><a href="#cb3-162" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> plan, dauer, status <span class="op">==</span> pywraplp.Solver.OPTIMAL</span>
|
|||
|
|
<span id="cb3-163"><a href="#cb3-163" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-164"><a href="#cb3-164" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-165"><a href="#cb3-165" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> bewerte(plan: np.ndarray, wind: np.ndarray, abwurfpreis: <span class="bu">float</span> <span class="op">=</span> <span class="va">None</span>):</span>
|
|||
|
|
<span id="cb3-166"><a href="#cb3-166" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Commitment steht fest - nur noch die Fahrweise je Szenario optimieren.</span></span>
|
|||
|
|
<span id="cb3-167"><a href="#cb3-167" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-168"><a href="#cb3-168" aria-hidden="true" tabindex="-1"></a><span class="co"> Das ist die ehrliche Bewertung eines Plans: Er wird der Wirklichkeit</span></span>
|
|||
|
|
<span id="cb3-169"><a href="#cb3-169" aria-hidden="true" tabindex="-1"></a><span class="co"> ausgesetzt und darf nur noch das anpassen, was sich am Tag selbst</span></span>
|
|||
|
|
<span id="cb3-170"><a href="#cb3-170" aria-hidden="true" tabindex="-1"></a><span class="co"> anpassen laesst.</span></span>
|
|||
|
|
<span id="cb3-171"><a href="#cb3-171" aria-hidden="true" tabindex="-1"></a><span class="co"> """</span></span>
|
|||
|
|
<span id="cb3-172"><a href="#cb3-172" aria-hidden="true" tabindex="-1"></a> abwurfpreis <span class="op">=</span> LASTABWURF <span class="cf">if</span> abwurfpreis <span class="kw">is</span> <span class="va">None</span> <span class="cf">else</span> abwurfpreis</span>
|
|||
|
|
<span id="cb3-173"><a href="#cb3-173" aria-hidden="true" tabindex="-1"></a> anzahl_bloecke <span class="op">=</span> <span class="bu">len</span>(KRAFTWERKE)</span>
|
|||
|
|
<span id="cb3-174"><a href="#cb3-174" aria-hidden="true" tabindex="-1"></a> kosten, fehlmengen <span class="op">=</span> [], []</span>
|
|||
|
|
<span id="cb3-175"><a href="#cb3-175" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> j <span class="kw">in</span> <span class="bu">range</span>(wind.shape[<span class="dv">0</span>]):</span>
|
|||
|
|
<span id="cb3-176"><a href="#cb3-176" aria-hidden="true" tabindex="-1"></a> solver <span class="op">=</span> pywraplp.Solver.CreateSolver(<span class="st">"GLOP"</span>)</span>
|
|||
|
|
<span id="cb3-177"><a href="#cb3-177" aria-hidden="true" tabindex="-1"></a> leistung <span class="op">=</span> [[solver.NumVar(<span class="dv">0</span>, KRAFTWERKE[k][<span class="dv">2</span>], <span class="ss">f"p_</span><span class="sc">{</span>k<span class="sc">}</span><span class="ss">_</span><span class="sc">{</span>t<span class="sc">}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-178"><a href="#cb3-178" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)] <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke)]</span>
|
|||
|
|
<span id="cb3-179"><a href="#cb3-179" aria-hidden="true" tabindex="-1"></a> fehlt <span class="op">=</span> [solver.NumVar(<span class="dv">0</span>, solver.infinity(), <span class="ss">f"y_</span><span class="sc">{</span>t<span class="sc">}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-180"><a href="#cb3-180" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)]</span>
|
|||
|
|
<span id="cb3-181"><a href="#cb3-181" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k, (_, pmin, pmax, _, _, _) <span class="kw">in</span> <span class="bu">enumerate</span>(KRAFTWERKE):</span>
|
|||
|
|
<span id="cb3-182"><a href="#cb3-182" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN):</span>
|
|||
|
|
<span id="cb3-183"><a href="#cb3-183" aria-hidden="true" tabindex="-1"></a> solver.Add(leistung[k][t] <span class="op">>=</span> pmin <span class="op">*</span> plan[k, t])</span>
|
|||
|
|
<span id="cb3-184"><a href="#cb3-184" aria-hidden="true" tabindex="-1"></a> solver.Add(leistung[k][t] <span class="op"><=</span> pmax <span class="op">*</span> plan[k, t])</span>
|
|||
|
|
<span id="cb3-185"><a href="#cb3-185" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN):</span>
|
|||
|
|
<span id="cb3-186"><a href="#cb3-186" aria-hidden="true" tabindex="-1"></a> solver.Add(<span class="bu">sum</span>(leistung[k][t] <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke))</span>
|
|||
|
|
<span id="cb3-187"><a href="#cb3-187" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> <span class="bu">float</span>(wind[j, t]) <span class="op">+</span> fehlt[t] <span class="op">>=</span> LAST[t])</span>
|
|||
|
|
<span id="cb3-188"><a href="#cb3-188" aria-hidden="true" tabindex="-1"></a> solver.Minimize(</span>
|
|||
|
|
<span id="cb3-189"><a href="#cb3-189" aria-hidden="true" tabindex="-1"></a> <span class="bu">sum</span>(KRAFTWERKE[k][<span class="dv">3</span>] <span class="op">*</span> leistung[k][t]</span>
|
|||
|
|
<span id="cb3-190"><a href="#cb3-190" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke) <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN))</span>
|
|||
|
|
<span id="cb3-191"><a href="#cb3-191" aria-hidden="true" tabindex="-1"></a> <span class="op">+</span> <span class="bu">sum</span>(abwurfpreis <span class="op">*</span> fehlt[t] <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN)))</span>
|
|||
|
|
<span id="cb3-192"><a href="#cb3-192" aria-hidden="true" tabindex="-1"></a> solver.Solve()</span>
|
|||
|
|
<span id="cb3-193"><a href="#cb3-193" aria-hidden="true" tabindex="-1"></a> kosten.append(solver.Objective().Value())</span>
|
|||
|
|
<span id="cb3-194"><a href="#cb3-194" aria-hidden="true" tabindex="-1"></a> fehlmengen.append(<span class="bu">sum</span>(f.solution_value() <span class="cf">for</span> f <span class="kw">in</span> fehlt))</span>
|
|||
|
|
<span id="cb3-195"><a href="#cb3-195" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-196"><a href="#cb3-196" aria-hidden="true" tabindex="-1"></a> anfahrten <span class="op">=</span> <span class="bu">sum</span>(KRAFTWERKE[k][<span class="dv">4</span>]</span>
|
|||
|
|
<span id="cb3-197"><a href="#cb3-197" aria-hidden="true" tabindex="-1"></a> <span class="op">*</span> <span class="bu">max</span>(<span class="fl">0.0</span>, plan[k, t] <span class="op">-</span> (plan[k, t <span class="op">-</span> <span class="dv">1</span>] <span class="cf">if</span> t <span class="op">></span> <span class="dv">0</span> <span class="cf">else</span> <span class="fl">0.0</span>))</span>
|
|||
|
|
<span id="cb3-198"><a href="#cb3-198" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> k <span class="kw">in</span> <span class="bu">range</span>(anzahl_bloecke) <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(STUNDEN))</span>
|
|||
|
|
<span id="cb3-199"><a href="#cb3-199" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> np.array(kosten) <span class="op">+</span> anfahrten, np.array(fehlmengen)</span>
|
|||
|
|
<span id="cb3-200"><a href="#cb3-200" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-201"><a href="#cb3-201" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-202"><a href="#cb3-202" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> cvar(werte: np.ndarray, alpha: <span class="bu">float</span> <span class="op">=</span> ALPHA) <span class="op">-></span> <span class="bu">float</span>:</span>
|
|||
|
|
<span id="cb3-203"><a href="#cb3-203" aria-hidden="true" tabindex="-1"></a> <span class="co">"""Mittelwert der schlechtesten (1-alpha) Faelle."""</span></span>
|
|||
|
|
<span id="cb3-204"><a href="#cb3-204" aria-hidden="true" tabindex="-1"></a> schwelle <span class="op">=</span> np.quantile(werte, alpha)</span>
|
|||
|
|
<span id="cb3-205"><a href="#cb3-205" aria-hidden="true" tabindex="-1"></a> schlechteste <span class="op">=</span> werte[werte <span class="op">>=</span> schwelle]</span>
|
|||
|
|
<span id="cb3-206"><a href="#cb3-206" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(schlechteste.mean()) <span class="cf">if</span> <span class="bu">len</span>(schlechteste) <span class="cf">else</span> <span class="fl">0.0</span></span>
|
|||
|
|
<span id="cb3-207"><a href="#cb3-207" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-208"><a href="#cb3-208" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-209"><a href="#cb3-209" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> zeige(name: <span class="bu">str</span>, plan, wind, abwurfpreis: <span class="bu">float</span> <span class="op">=</span> <span class="va">None</span>) <span class="op">-></span> <span class="bu">dict</span>:</span>
|
|||
|
|
<span id="cb3-210"><a href="#cb3-210" aria-hidden="true" tabindex="-1"></a> kosten, fehl <span class="op">=</span> bewerte(plan, wind, abwurfpreis)</span>
|
|||
|
|
<span id="cb3-211"><a href="#cb3-211" aria-hidden="true" tabindex="-1"></a> kennzahlen <span class="op">=</span> {<span class="st">"mittel"</span>: kosten.mean(), <span class="st">"max"</span>: kosten.<span class="bu">max</span>(),</span>
|
|||
|
|
<span id="cb3-212"><a href="#cb3-212" aria-hidden="true" tabindex="-1"></a> <span class="st">"fehl_mittel"</span>: fehl.mean(), <span class="st">"fehl_cvar"</span>: cvar(fehl),</span>
|
|||
|
|
<span id="cb3-213"><a href="#cb3-213" aria-hidden="true" tabindex="-1"></a> <span class="st">"betroffen"</span>: <span class="bu">int</span>((fehl <span class="op">></span> <span class="fl">0.01</span>).<span class="bu">sum</span>()),</span>
|
|||
|
|
<span id="cb3-214"><a href="#cb3-214" aria-hidden="true" tabindex="-1"></a> <span class="st">"blockstunden"</span>: <span class="bu">int</span>(plan.<span class="bu">sum</span>())}</span>
|
|||
|
|
<span id="cb3-215"><a href="#cb3-215" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<32}</span><span class="ss"> </span><span class="sc">{</span>kennzahlen[<span class="st">'mittel'</span>]<span class="sc">:>10,.0f}</span><span class="ss"> </span><span class="sc">{</span>kennzahlen[<span class="st">'max'</span>]<span class="sc">:>11,.0f}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-216"><a href="#cb3-216" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>kennzahlen[<span class="st">'fehl_mittel'</span>]<span class="sc">:>9.1f}</span><span class="ss"> </span><span class="sc">{</span>kennzahlen[<span class="st">'fehl_cvar'</span>]<span class="sc">:>10.1f}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-217"><a href="#cb3-217" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>kennzahlen[<span class="st">'betroffen'</span>]<span class="sc">:>6}</span><span class="ss">/</span><span class="sc">{</span><span class="bu">len</span>(fehl)<span class="sc">}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-218"><a href="#cb3-218" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> kennzahlen</span>
|
|||
|
|
<span id="cb3-219"><a href="#cb3-219" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-220"><a href="#cb3-220" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-221"><a href="#cb3-221" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">"__main__"</span>:</span>
|
|||
|
|
<span id="cb3-222"><a href="#cb3-222" aria-hidden="true" tabindex="-1"></a> wind, ist_flaute <span class="op">=</span> erzeuge_windszenarien()</span>
|
|||
|
|
<span id="cb3-223"><a href="#cb3-223" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-224"><a href="#cb3-224" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">88</span>)</span>
|
|||
|
|
<span id="cb3-225"><a href="#cb3-225" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" KRAFTWERKSEINSATZ: DER PLAN STEHT, BEVOR DER WIND WEHT"</span>)</span>
|
|||
|
|
<span id="cb3-226"><a href="#cb3-226" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">88</span>)</span>
|
|||
|
|
<span id="cb3-227"><a href="#cb3-227" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="sc">{</span><span class="bu">len</span>(KRAFTWERKE)<span class="sc">}</span><span class="ss"> Bloecke, </span><span class="sc">{</span>STUNDEN<span class="sc">}</span><span class="ss"> Stunden, </span><span class="sc">{</span>SZENARIEN<span class="sc">}</span><span class="ss"> Windszenarien "</span></span>
|
|||
|
|
<span id="cb3-228"><a href="#cb3-228" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(davon </span><span class="sc">{</span><span class="bu">int</span>(ist_flaute.<span class="bu">sum</span>())<span class="sc">}</span><span class="ss"> Dunkelflauten)."</span>)</span>
|
|||
|
|
<span id="cb3-229"><a href="#cb3-229" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Last </span><span class="sc">{</span>LAST<span class="sc">.</span><span class="bu">min</span>()<span class="sc">:.0f}</span><span class="ss"> bis </span><span class="sc">{</span>LAST<span class="sc">.</span><span class="bu">max</span>()<span class="sc">:.0f}</span><span class="ss"> MW, "</span></span>
|
|||
|
|
<span id="cb3-230"><a href="#cb3-230" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"Wind im Erwartungswert </span><span class="sc">{</span>WIND_ERWARTUNG<span class="sc">.</span><span class="bu">min</span>()<span class="sc">:.0f}</span><span class="ss"> bis "</span></span>
|
|||
|
|
<span id="cb3-231"><a href="#cb3-231" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span>WIND_ERWARTUNG<span class="sc">.</span><span class="bu">max</span>()<span class="sc">:.0f}</span><span class="ss"> MW."</span>)</span>
|
|||
|
|
<span id="cb3-232"><a href="#cb3-232" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"Nicht gedeckte Last kostet </span><span class="sc">{</span>LASTABWURF<span class="sc">:,.0f}</span><span class="ss"> EUR je MWh.</span><span class="ch">\n</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-233"><a href="#cb3-233" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-234"><a href="#cb3-234" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">'Planungsgrundlage'</span><span class="sc">:<32}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Kosten'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Kosten'</span><span class="sc">:>11}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-235"><a href="#cb3-235" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Fehlmenge'</span><span class="sc">:>9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Fehlmenge'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Szen. mit'</span><span class="sc">:>12}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-236"><a href="#cb3-236" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">''</span><span class="sc">:<32}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'im Mittel'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'schlimmst.'</span><span class="sc">:>11}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-237"><a href="#cb3-237" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Mittel'</span><span class="sc">:>9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'CVaR 90%'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Abwurf'</span><span class="sc">:>12}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-238"><a href="#cb3-238" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">86</span>)</span>
|
|||
|
|
<span id="cb3-239"><a href="#cb3-239" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-240"><a href="#cb3-240" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 1. Deterministisch ----------------------------------------------</span></span>
|
|||
|
|
<span id="cb3-241"><a href="#cb3-241" aria-hidden="true" tabindex="-1"></a> plan_det, dauer_det, _ <span class="op">=</span> plane(WIND_ERWARTUNG.reshape(<span class="dv">1</span>, <span class="op">-</span><span class="dv">1</span>), [<span class="fl">1.0</span>])</span>
|
|||
|
|
<span id="cb3-242"><a href="#cb3-242" aria-hidden="true" tabindex="-1"></a> det <span class="op">=</span> zeige(<span class="st">"nur Wind-Erwartungswert"</span>, plan_det, wind)</span>
|
|||
|
|
<span id="cb3-243"><a href="#cb3-243" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-244"><a href="#cb3-244" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 2. Zweistufig, risikoneutral ------------------------------------</span></span>
|
|||
|
|
<span id="cb3-245"><a href="#cb3-245" aria-hidden="true" tabindex="-1"></a> gleich <span class="op">=</span> [<span class="fl">1.0</span> <span class="op">/</span> SZENARIEN] <span class="op">*</span> SZENARIEN</span>
|
|||
|
|
<span id="cb3-246"><a href="#cb3-246" aria-hidden="true" tabindex="-1"></a> plan_sto, dauer_sto, optimal_sto <span class="op">=</span> plane(wind, gleich)</span>
|
|||
|
|
<span id="cb3-247"><a href="#cb3-247" aria-hidden="true" tabindex="-1"></a> sto <span class="op">=</span> zeige(<span class="st">"alle 40 Szenarien"</span>, plan_sto, wind)</span>
|
|||
|
|
<span id="cb3-248"><a href="#cb3-248" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-249"><a href="#cb3-249" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- 3. Mit Versorgungssicherheit ------------------------------------</span></span>
|
|||
|
|
<span id="cb3-250"><a href="#cb3-250" aria-hidden="true" tabindex="-1"></a> plan_sicher, dauer_sicher, optimal_sicher <span class="op">=</span> plane(wind, gleich, cvar_grenze<span class="op">=</span><span class="fl">0.0</span>)</span>
|
|||
|
|
<span id="cb3-251"><a href="#cb3-251" aria-hidden="true" tabindex="-1"></a> sicher <span class="op">=</span> zeige(<span class="st">"+ Nichtdeckung CVaR 90 % = 0"</span>, plan_sicher, wind)</span>
|
|||
|
|
<span id="cb3-252"><a href="#cb3-252" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-253"><a href="#cb3-253" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Rechenzeiten: </span><span class="sc">{</span>dauer_det<span class="sc">:.1f}</span><span class="ss">s / </span><span class="sc">{</span>dauer_sto<span class="sc">:.1f}</span><span class="ss">s / </span><span class="sc">{</span>dauer_sicher<span class="sc">:.1f}</span><span class="ss">s "</span></span>
|
|||
|
|
<span id="cb3-254"><a href="#cb3-254" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(alle beweisbar optimal: </span><span class="sc">{</span>optimal_sto <span class="kw">and</span> optimal_sicher<span class="sc">}</span><span class="ss">)"</span>)</span>
|
|||
|
|
<span id="cb3-255"><a href="#cb3-255" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-256"><a href="#cb3-256" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Was die Zeilen bedeuten -----------------------------------------</span></span>
|
|||
|
|
<span id="cb3-257"><a href="#cb3-257" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">88</span>)</span>
|
|||
|
|
<span id="cb3-258"><a href="#cb3-258" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Was der Erwartungswert-Plan anrichtet</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
|||
|
|
<span id="cb3-259"><a href="#cb3-259" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Er ist auf dem Papier der billigste - gerechnet auf den mittleren"</span>)</span>
|
|||
|
|
<span id="cb3-260"><a href="#cb3-260" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Wind kostet er weniger als jeder andere. In der Wirklichkeit liegt"</span>)</span>
|
|||
|
|
<span id="cb3-261"><a href="#cb3-261" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" er </span><span class="sc">{</span>det[<span class="st">'mittel'</span>] <span class="op">/</span> sto[<span class="st">'mittel'</span>] <span class="op">-</span> <span class="dv">1</span><span class="sc">:+.0%}</span><span class="ss"> ueber dem zweistufigen Plan, und sein "</span></span>
|
|||
|
|
<span id="cb3-262"><a href="#cb3-262" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"schlimmster Tag"</span>)</span>
|
|||
|
|
<span id="cb3-263"><a href="#cb3-263" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" kostet </span><span class="sc">{</span>det[<span class="st">'max'</span>] <span class="op">/</span> sto[<span class="st">'max'</span>]<span class="sc">:.1f}</span><span class="ss">-mal so viel."</span>)</span>
|
|||
|
|
<span id="cb3-264"><a href="#cb3-264" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Der Grund steht in der letzten Spalte: In </span><span class="sc">{</span>det[<span class="st">'betroffen'</span>]<span class="sc">}</span><span class="ss"> von </span><span class="sc">{</span>SZENARIEN<span class="sc">}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-265"><a href="#cb3-265" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"Szenarien muss"</span>)</span>
|
|||
|
|
<span id="cb3-266"><a href="#cb3-266" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Last abgeworfen werden. Was fehlt, sind nicht Kraftwerke, sondern"</span>)</span>
|
|||
|
|
<span id="cb3-267"><a href="#cb3-267" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" ANGEFAHRENE Kraftwerke - und ein Block mit acht Stunden"</span>)</span>
|
|||
|
|
<span id="cb3-268"><a href="#cb3-268" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Mindestlaufzeit laesst sich um 18 Uhr nicht mehr herbeirufen."</span>)</span>
|
|||
|
|
<span id="cb3-269"><a href="#cb3-269" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-270"><a href="#cb3-270" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Der Unterschied im Plan ist klein:"</span>)</span>
|
|||
|
|
<span id="cb3-271"><a href="#cb3-271" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, plan <span class="kw">in</span> [(<span class="st">"Erwartungswert"</span>, plan_det), (<span class="st">"zweistufig"</span>, plan_sto),</span>
|
|||
|
|
<span id="cb3-272"><a href="#cb3-272" aria-hidden="true" tabindex="-1"></a> (<span class="st">"mit Sicherheit"</span>, plan_sicher)]:</span>
|
|||
|
|
<span id="cb3-273"><a href="#cb3-273" aria-hidden="true" tabindex="-1"></a> laufend <span class="op">=</span> plan.<span class="bu">sum</span>(axis<span class="op">=</span><span class="dv">0</span>).astype(<span class="bu">int</span>)</span>
|
|||
|
|
<span id="cb3-274"><a href="#cb3-274" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>name<span class="sc">:<16}</span><span class="ss"> </span><span class="sc">{</span><span class="st">''</span><span class="sc">.</span>join(<span class="bu">str</span>(x) <span class="cf">for</span> x <span class="kw">in</span> laufend)<span class="sc">}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-275"><a href="#cb3-275" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"(</span><span class="sc">{</span><span class="bu">int</span>(plan.<span class="bu">sum</span>())<span class="sc">}</span><span class="ss"> Blockstunden)"</span>)</span>
|
|||
|
|
<span id="cb3-276"><a href="#cb3-276" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Der zweistufige Plan haelt </span><span class="sc">{</span>sto[<span class="st">'blockstunden'</span>] <span class="op">-</span> det[<span class="st">'blockstunden'</span>]<span class="sc">}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-277"><a href="#cb3-277" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"Blockstunden mehr vor - das genuegt,"</span>)</span>
|
|||
|
|
<span id="cb3-278"><a href="#cb3-278" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" um alle </span><span class="sc">{</span>det[<span class="st">'betroffen'</span>]<span class="sc">}</span><span class="ss"> Lastabwuerfe zu vermeiden."</span>)</span>
|
|||
|
|
<span id="cb3-279"><a href="#cb3-279" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-280"><a href="#cb3-280" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Der Preis der Sicherheit ----------------------------------------</span></span>
|
|||
|
|
<span id="cb3-281"><a href="#cb3-281" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">88</span>)</span>
|
|||
|
|
<span id="cb3-282"><a href="#cb3-282" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"Was Versorgungssicherheit kostet</span><span class="ch">\n</span><span class="st">"</span>)</span>
|
|||
|
|
<span id="cb3-283"><a href="#cb3-283" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Bei </span><span class="sc">{</span>LASTABWURF<span class="sc">:,.0f}</span><span class="ss"> EUR/MWh kostet die CVaR-Schranke NICHTS: Der"</span>)</span>
|
|||
|
|
<span id="cb3-284"><a href="#cb3-284" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" risikoneutrale Plan haelt sie schon ein. Das ist kein Zufall - bei"</span>)</span>
|
|||
|
|
<span id="cb3-285"><a href="#cb3-285" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" diesem Preis lohnt sich Vorhaltung bereits im Erwartungswert."</span>)</span>
|
|||
|
|
<span id="cb3-286"><a href="#cb3-286" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st"> Interessant wird die Schranke dort, wo der Schaden ZU NIEDRIG"</span>)</span>
|
|||
|
|
<span id="cb3-287"><a href="#cb3-287" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" bepreist ist. Dieselbe Rechnung mit einem Marktpreisdeckel von"</span>)</span>
|
|||
|
|
<span id="cb3-288"><a href="#cb3-288" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>NIEDRIGER_ABWURFPREIS<span class="sc">:,.0f}</span><span class="ss"> EUR/MWh statt </span><span class="sc">{</span>LASTABWURF<span class="sc">:,.0f}</span><span class="ss">:</span><span class="ch">\n</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-289"><a href="#cb3-289" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-290"><a href="#cb3-290" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">'Planungsgrundlage'</span><span class="sc">:<32}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Kosten'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Kosten'</span><span class="sc">:>11}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-291"><a href="#cb3-291" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Fehlmenge'</span><span class="sc">:>9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Fehlmenge'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Szen. mit'</span><span class="sc">:>12}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-292"><a href="#cb3-292" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span><span class="st">''</span><span class="sc">:<32}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'im Mittel'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'schlimmst.'</span><span class="sc">:>11}</span><span class="ss"> "</span></span>
|
|||
|
|
<span id="cb3-293"><a href="#cb3-293" aria-hidden="true" tabindex="-1"></a> <span class="ss">f"</span><span class="sc">{</span><span class="st">'Mittel'</span><span class="sc">:>9}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'CVaR 90%'</span><span class="sc">:>10}</span><span class="ss"> </span><span class="sc">{</span><span class="st">'Abwurf'</span><span class="sc">:>12}</span><span class="ss">"</span>)</span>
|
|||
|
|
<span id="cb3-294"><a href="#cb3-294" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" "</span> <span class="op">+</span> <span class="st">"-"</span> <span class="op">*</span> <span class="dv">86</span>)</span>
|
|||
|
|
<span id="cb3-295"><a href="#cb3-295" aria-hidden="true" tabindex="-1"></a> plan_billig, _, _ <span class="op">=</span> plane(wind, gleich, abwurfpreis<span class="op">=</span>NIEDRIGER_ABWURFPREIS)</span>
|
|||
|
|
<span id="cb3-296"><a href="#cb3-296" aria-hidden="true" tabindex="-1"></a> billig <span class="op">=</span> zeige(<span class="st">"risikoneutral, billiger Abwurf"</span>, plan_billig, wind,</span>
|
|||
|
|
<span id="cb3-297"><a href="#cb3-297" aria-hidden="true" tabindex="-1"></a> NIEDRIGER_ABWURFPREIS)</span>
|
|||
|
|
<span id="cb3-298"><a href="#cb3-298" aria-hidden="true" tabindex="-1"></a> plan_billig_sicher, _, _ <span class="op">=</span> plane(wind, gleich, cvar_grenze<span class="op">=</span><span class="fl">0.0</span>,</span>
|
|||
|
|
<span id="cb3-299"><a href="#cb3-299" aria-hidden="true" tabindex="-1"></a> abwurfpreis<span class="op">=</span>NIEDRIGER_ABWURFPREIS)</span>
|
|||
|
|
<span id="cb3-300"><a href="#cb3-300" aria-hidden="true" tabindex="-1"></a> billig_sicher <span class="op">=</span> zeige(<span class="st">"+ CVaR 90 </span><span class="sc">% d</span><span class="st">er Fehlmenge = 0"</span>, plan_billig_sicher, wind,</span>
|
|||
|
|
<span id="cb3-301"><a href="#cb3-301" aria-hidden="true" tabindex="-1"></a> NIEDRIGER_ABWURFPREIS)</span>
|
|||
|
|
<span id="cb3-302"><a href="#cb3-302" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-303"><a href="#cb3-303" aria-hidden="true" tabindex="-1"></a> aufpreis <span class="op">=</span> billig_sicher[<span class="st">"mittel"</span>] <span class="op">-</span> billig[<span class="st">"mittel"</span>]</span>
|
|||
|
|
<span id="cb3-304"><a href="#cb3-304" aria-hidden="true" tabindex="-1"></a> vermieden <span class="op">=</span> billig[<span class="st">"fehl_cvar"</span>] <span class="op">-</span> billig_sicher[<span class="st">"fehl_cvar"</span>]</span>
|
|||
|
|
<span id="cb3-305"><a href="#cb3-305" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Jetzt greift die Schranke: Der risikoneutrale Plan nimmt in"</span>)</span>
|
|||
|
|
<span id="cb3-306"><a href="#cb3-306" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>billig[<span class="st">'betroffen'</span>]<span class="sc">}</span><span class="ss"> von </span><span class="sc">{</span>SZENARIEN<span class="sc">}</span><span class="ss"> Szenarien einen Lastabwurf in Kauf, weil er bei"</span>)</span>
|
|||
|
|
<span id="cb3-307"><a href="#cb3-307" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" </span><span class="sc">{</span>NIEDRIGER_ABWURFPREIS<span class="sc">:,.0f}</span><span class="ss"> EUR/MWh billiger ist als das Vorhalten eines Blocks."</span>)</span>
|
|||
|
|
<span id="cb3-308"><a href="#cb3-308" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Aufpreis fuer die Sicherheit: </span><span class="sc">{</span>aufpreis<span class="sc">:,.0f}</span><span class="ss"> EUR je Tag"</span>)</span>
|
|||
|
|
<span id="cb3-309"><a href="#cb3-309" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> vermieden <span class="op">></span> <span class="fl">0.01</span>:</span>
|
|||
|
|
<span id="cb3-310"><a href="#cb3-310" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Vermiedene Fehlmenge (CVaR 90 %): </span><span class="sc">{</span>vermieden<span class="sc">:.1f}</span><span class="ss"> MWh"</span>)</span>
|
|||
|
|
<span id="cb3-311"><a href="#cb3-311" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" -> </span><span class="sc">{</span>aufpreis <span class="op">/</span> vermieden<span class="sc">:,.0f}</span><span class="ss"> EUR je vermiedener MWh"</span>)</span>
|
|||
|
|
<span id="cb3-312"><a href="#cb3-312" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f"</span><span class="ch">\n</span><span class="ss"> Diese Zahl ist die Entscheidungsgrundlage - genau wie die Spalte"</span>)</span>
|
|||
|
|
<span id="cb3-313"><a href="#cb3-313" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" 'EUR je kg' im Kapitel Mehrziel. Sie sagt der Aufsicht, was ihre"</span>)</span>
|
|||
|
|
<span id="cb3-314"><a href="#cb3-314" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" Versorgungssicherheitsvorgabe tatsaechlich kostet, statt darueber"</span>)</span>
|
|||
|
|
<span id="cb3-315"><a href="#cb3-315" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f" zu streiten, wie wichtig sie ist."</span>)</span>
|
|||
|
|
<span id="cb3-316"><a href="#cb3-316" aria-hidden="true" tabindex="-1"></a></span>
|
|||
|
|
<span id="cb3-317"><a href="#cb3-317" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"</span><span class="ch">\n</span><span class="st">"</span> <span class="op">+</span> <span class="st">"="</span> <span class="op">*</span> <span class="dv">88</span>)</span>
|
|||
|
|
<span id="cb3-318"><a href="#cb3-318" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" WAS MAN DARAUS MITNIMMT"</span>)</span>
|
|||
|
|
<span id="cb3-319"><a href="#cb3-319" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">88</span>)</span>
|
|||
|
|
<span id="cb3-320"><a href="#cb3-320" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"1. Die erste Stufe ist binaer und traege. Was am Vorabend nicht"</span>)</span>
|
|||
|
|
<span id="cb3-321"><a href="#cb3-321" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" angefahren wurde, steht am naechsten Tag nicht zur Verfuegung -"</span>)</span>
|
|||
|
|
<span id="cb3-322"><a href="#cb3-322" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" egal, wie hoch der Preis dann steigt."</span>)</span>
|
|||
|
|
<span id="cb3-323"><a href="#cb3-323" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"2. Deshalb ist der Erwartungswert als Planungsgrundlage nicht nur"</span>)</span>
|
|||
|
|
<span id="cb3-324"><a href="#cb3-324" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ungenau, sondern SYSTEMATISCH zu knapp: Er plant fuer einen Tag,"</span>)</span>
|
|||
|
|
<span id="cb3-325"><a href="#cb3-325" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" der so nie eintritt, und laesst keine Reserve fuer die Haelfte"</span>)</span>
|
|||
|
|
<span id="cb3-326"><a href="#cb3-326" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" der Faelle, in denen es schlechter kommt."</span>)</span>
|
|||
|
|
<span id="cb3-327"><a href="#cb3-327" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"3. Der Ausweg ist keine bessere Windprognose, sondern ein Modell,"</span>)</span>
|
|||
|
|
<span id="cb3-328"><a href="#cb3-328" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" das die Szenarien SIEHT. Die Rechenzeit dafuer liegt hier im"</span>)</span>
|
|||
|
|
<span id="cb3-329"><a href="#cb3-329" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Sekundenbereich - der Aufwand ist die Modellierung, nicht der"</span>)</span>
|
|||
|
|
<span id="cb3-330"><a href="#cb3-330" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Solver."</span>)</span>
|
|||
|
|
<span id="cb3-331"><a href="#cb3-331" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"4. Wo der Lastabwurf teuer genug bepreist ist, deckt schon die"</span>)</span>
|
|||
|
|
<span id="cb3-332"><a href="#cb3-332" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Minimierung der ERWARTETEN Kosten die Extremfaelle mit ab. Die"</span>)</span>
|
|||
|
|
<span id="cb3-333"><a href="#cb3-333" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Risikoschranke wird genau dann gebraucht, wenn der Schaden ZU"</span>)</span>
|
|||
|
|
<span id="cb3-334"><a href="#cb3-334" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" NIEDRIG bepreist ist - was bei Versorgungssicherheit die Regel"</span>)</span>
|
|||
|
|
<span id="cb3-335"><a href="#cb3-335" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" ist, weil ein Marktpreisdeckel nicht den volkswirtschaftlichen"</span>)</span>
|
|||
|
|
<span id="cb3-336"><a href="#cb3-336" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" Schaden abbildet. Dann liefert sie den Preis der Vorgabe in Euro"</span>)</span>
|
|||
|
|
<span id="cb3-337"><a href="#cb3-337" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">" je vermiedener MWh, und darueber laesst sich verhandeln."</span>)</span>
|
|||
|
|
<span id="cb3-338"><a href="#cb3-338" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">"="</span> <span class="op">*</span> <span class="dv">88</span>)</span></code></pre></div>
|
|||
|
|
<p><strong>Erwartete Ausgabe</strong> (Laufzeiten hardwareabhängig, die Werte nicht):</p>
|
|||
|
|
<pre><code>========================================================================================
|
|||
|
|
KRAFTWERKSEINSATZ: DER PLAN STEHT, BEVOR DER WIND WEHT
|
|||
|
|
========================================================================================
|
|||
|
|
5 Bloecke, 24 Stunden, 40 Windszenarien (davon 8 Dunkelflauten).
|
|||
|
|
Last 270 bis 804 MW, Wind im Erwartungswert 30 bis 290 MW.
|
|||
|
|
Nicht gedeckte Last kostet 3,000 EUR je MWh.
|
|||
|
|
|
|||
|
|
Planungsgrundlage Kosten Kosten Fehlmenge Fehlmenge Szen. mit
|
|||
|
|
im Mittel schlimmst. Mittel CVaR 90% Abwurf
|
|||
|
|
--------------------------------------------------------------------------------------
|
|||
|
|
nur Wind-Erwartungswert 874,870 2,504,154 178.7 679.2 28/40
|
|||
|
|
alle 40 Szenarien 351,356 415,924 0.0 0.0 0/40
|
|||
|
|
+ Nichtdeckung CVaR 90 % = 0 351,356 415,924 0.0 0.0 0/40
|
|||
|
|
|
|||
|
|
Rechenzeiten: 0.0s / 1.3s / 1.1s (alle beweisbar optimal: True)
|
|||
|
|
|
|||
|
|
----------------------------------------------------------------------------------------
|
|||
|
|
Was der Erwartungswert-Plan anrichtet
|
|||
|
|
|
|||
|
|
Er ist auf dem Papier der billigste - gerechnet auf den mittleren
|
|||
|
|
Wind kostet er weniger als jeder andere. In der Wirklichkeit liegt
|
|||
|
|
er +149% ueber dem zweistufigen Plan, und sein schlimmster Tag
|
|||
|
|
kostet 6.0-mal so viel.
|
|||
|
|
|
|||
|
|
Der Grund steht in der letzten Spalte: In 28 von 40 Szenarien muss
|
|||
|
|
Last abgeworfen werden. Was fehlt, sind nicht Kraftwerke, sondern
|
|||
|
|
ANGEFAHRENE Kraftwerke - und ein Block mit acht Stunden
|
|||
|
|
Mindestlaufzeit laesst sich um 18 Uhr nicht mehr herbeirufen.
|
|||
|
|
|
|||
|
|
Der Unterschied im Plan ist klein:
|
|||
|
|
Erwartungswert 111111122222222211111111 (33 Blockstunden)
|
|||
|
|
zweistufig 111111222222222222222111 (39 Blockstunden)
|
|||
|
|
mit Sicherheit 111111222222222222222111 (39 Blockstunden)
|
|||
|
|
|
|||
|
|
Der zweistufige Plan haelt 6 Blockstunden mehr vor - das genuegt,
|
|||
|
|
um alle 28 Lastabwuerfe zu vermeiden.
|
|||
|
|
|
|||
|
|
----------------------------------------------------------------------------------------
|
|||
|
|
Was Versorgungssicherheit kostet
|
|||
|
|
|
|||
|
|
Bei 3,000 EUR/MWh kostet die CVaR-Schranke NICHTS: Der
|
|||
|
|
risikoneutrale Plan haelt sie schon ein. Das ist kein Zufall - bei
|
|||
|
|
diesem Preis lohnt sich Vorhaltung bereits im Erwartungswert.
|
|||
|
|
|
|||
|
|
Interessant wird die Schranke dort, wo der Schaden ZU NIEDRIG
|
|||
|
|
bepreist ist. Dieselbe Rechnung mit einem Marktpreisdeckel von
|
|||
|
|
300 EUR/MWh statt 3,000:
|
|||
|
|
|
|||
|
|
Planungsgrundlage Kosten Kosten Fehlmenge Fehlmenge Szen. mit
|
|||
|
|
im Mittel schlimmst. Mittel CVaR 90% Abwurf
|
|||
|
|
--------------------------------------------------------------------------------------
|
|||
|
|
risikoneutral, billiger Abwurf 350,512 421,336 3.0 18.4 8/40
|
|||
|
|
+ CVaR 90 % der Fehlmenge = 0 351,356 415,924 0.0 0.0 0/40
|
|||
|
|
|
|||
|
|
Jetzt greift die Schranke: Der risikoneutrale Plan nimmt in
|
|||
|
|
8 von 40 Szenarien einen Lastabwurf in Kauf, weil er bei
|
|||
|
|
300 EUR/MWh billiger ist als das Vorhalten eines Blocks.
|
|||
|
|
|
|||
|
|
Aufpreis fuer die Sicherheit: 844 EUR je Tag
|
|||
|
|
Vermiedene Fehlmenge (CVaR 90 %): 18.4 MWh
|
|||
|
|
-> 46 EUR je vermiedener MWh
|
|||
|
|
|
|||
|
|
Diese Zahl ist die Entscheidungsgrundlage - genau wie die Spalte
|
|||
|
|
'EUR je kg' im Kapitel Mehrziel. Sie sagt der Aufsicht, was ihre
|
|||
|
|
Versorgungssicherheitsvorgabe tatsaechlich kostet, statt darueber
|
|||
|
|
zu streiten, wie wichtig sie ist.
|
|||
|
|
|
|||
|
|
========================================================================================
|
|||
|
|
WAS MAN DARAUS MITNIMMT
|
|||
|
|
========================================================================================
|
|||
|
|
1. Die erste Stufe ist binaer und traege. Was am Vorabend nicht
|
|||
|
|
angefahren wurde, steht am naechsten Tag nicht zur Verfuegung -
|
|||
|
|
egal, wie hoch der Preis dann steigt.
|
|||
|
|
2. Deshalb ist der Erwartungswert als Planungsgrundlage nicht nur
|
|||
|
|
ungenau, sondern SYSTEMATISCH zu knapp: Er plant fuer einen Tag,
|
|||
|
|
der so nie eintritt, und laesst keine Reserve fuer die Haelfte
|
|||
|
|
der Faelle, in denen es schlechter kommt.
|
|||
|
|
3. Der Ausweg ist keine bessere Windprognose, sondern ein Modell,
|
|||
|
|
das die Szenarien SIEHT. Die Rechenzeit dafuer liegt hier im
|
|||
|
|
Sekundenbereich - der Aufwand ist die Modellierung, nicht der
|
|||
|
|
Solver.
|
|||
|
|
4. Wo der Lastabwurf teuer genug bepreist ist, deckt schon die
|
|||
|
|
Minimierung der ERWARTETEN Kosten die Extremfaelle mit ab. Die
|
|||
|
|
Risikoschranke wird genau dann gebraucht, wenn der Schaden ZU
|
|||
|
|
NIEDRIG bepreist ist - was bei Versorgungssicherheit die Regel
|
|||
|
|
ist, weil ein Marktpreisdeckel nicht den volkswirtschaftlichen
|
|||
|
|
Schaden abbildet. Dann liefert sie den Preis der Vorgabe in Euro
|
|||
|
|
je vermiedener MWh, und darueber laesst sich verhandeln.
|
|||
|
|
========================================================================================</code></pre>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-befund">17.5 Der Befund</h2>
|
|||
|
|
<table>
|
|||
|
|
<colgroup>
|
|||
|
|
<col style="width: 20%" />
|
|||
|
|
<col style="width: 26%" />
|
|||
|
|
<col style="width: 26%" />
|
|||
|
|
<col style="width: 26%" />
|
|||
|
|
</colgroup>
|
|||
|
|
<thead>
|
|||
|
|
<tr class="header">
|
|||
|
|
<th>Planungsgrundlage</th>
|
|||
|
|
<th style="text-align: right;">Kosten im Mittel</th>
|
|||
|
|
<th style="text-align: right;">schlimmster Tag</th>
|
|||
|
|
<th style="text-align: right;">Szenarien mit Lastabwurf</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td>nur Wind-Erwartungswert</td>
|
|||
|
|
<td style="text-align: right;">874 870 €</td>
|
|||
|
|
<td style="text-align: right;">2 504 154 €</td>
|
|||
|
|
<td style="text-align: right;"><strong>28 von 40</strong></td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td>alle 40 Szenarien</td>
|
|||
|
|
<td style="text-align: right;"><strong>351 356 €</strong></td>
|
|||
|
|
<td style="text-align: right;"><strong>415 924 €</strong></td>
|
|||
|
|
<td style="text-align: right;"><strong>0 von 40</strong></td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
<p>Der Erwartungswert-Plan kostet in der Wirklichkeit <strong>149 % mehr</strong>, und sein schlimmster Tag ist sechsmal so teuer.</p>
|
|||
|
|
<p>Der Unterschied zwischen den beiden Plänen ist dabei erstaunlich klein:</p>
|
|||
|
|
<pre><code>Erwartungswert 111111122222222211111111 (33 Blockstunden)
|
|||
|
|
zweistufig 111111222222222222222111 (39 Blockstunden)</code></pre>
|
|||
|
|
<p><strong>Sechs zusätzliche Blockstunden verhindern 28 Lastabwürfe.</strong> Der zweistufige Plan fährt den zweiten Block eine Stunde früher an und lässt ihn bis 21 Uhr statt bis 16 Uhr laufen.</p>
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>⚠️ Warum der Erwartungswert hier systematisch versagt</strong></p>
|
|||
|
|
<p>Der Fehler ist nicht, dass die Windprognose ungenau wäre. Er liegt in der <strong>Trägheit der ersten Stufe</strong>: Was am Vorabend nicht angefahren wurde, steht am nächsten Tag nicht zur Verfügung — egal, wie hoch der Preis dann steigt.</p>
|
|||
|
|
<p>Eine Planung auf den Mittelwert lässt in der Hälfte der Fälle zu wenig Reserve. Bei einer <em>anpassbaren</em> Größe wäre das halb so schlimm, weil man nachsteuern könnte. Bei einem Kernblock mit acht Stunden Mindestlaufzeit gibt es kein Nachsteuern.</p>
|
|||
|
|
<p>Das ist eine Verschärfung des „Fluchs des Durchschnitts” aus <a href="unsicherheit.html#sec:unsicherheit-der-fluch-des-durchschnitts">Abschnitt 12.3</a>: Dort war der Mittelwert eine schlechte Näherung, hier ist er eine <strong>nicht revidierbare</strong> schlechte Näherung.</p>
|
|||
|
|
</blockquote>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-sicherheit">17.6 Was Versorgungssicherheit kostet</h2>
|
|||
|
|
<p>Der dritte Plan legt eine CVaR-Schranke auf die nicht gedeckte Energie: Im Mittel der schlechtesten 10 % der Szenarien darf <strong>nichts</strong> fehlen. Dieselbe Rockafellar-Uryasev-Konstruktion wie in <a href="cvar.html#kap-cvar">Kapitel 20</a>, nur dass hier Megawattstunden begrenzt werden statt Euro.</p>
|
|||
|
|
<p>Das Ergebnis ist zunächst enttäuschend — und dann lehrreich:</p>
|
|||
|
|
<table>
|
|||
|
|
<colgroup>
|
|||
|
|
<col style="width: 20%" />
|
|||
|
|
<col style="width: 26%" />
|
|||
|
|
<col style="width: 26%" />
|
|||
|
|
<col style="width: 26%" />
|
|||
|
|
</colgroup>
|
|||
|
|
<thead>
|
|||
|
|
<tr class="header">
|
|||
|
|
<th>Abwurfpreis</th>
|
|||
|
|
<th style="text-align: right;">risikoneutral</th>
|
|||
|
|
<th style="text-align: right;">mit CVaR-Schranke</th>
|
|||
|
|
<th style="text-align: right;">Aufpreis</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<tr class="odd">
|
|||
|
|
<td>3 000 €/MWh</td>
|
|||
|
|
<td style="text-align: right;">351 356 €, kein Abwurf</td>
|
|||
|
|
<td style="text-align: right;">351 356 €, kein Abwurf</td>
|
|||
|
|
<td style="text-align: right;"><strong>0 €</strong></td>
|
|||
|
|
</tr>
|
|||
|
|
<tr class="even">
|
|||
|
|
<td>300 €/MWh</td>
|
|||
|
|
<td style="text-align: right;">350 512 €, Abwurf in 8/40</td>
|
|||
|
|
<td style="text-align: right;">351 356 €, kein Abwurf</td>
|
|||
|
|
<td style="text-align: right;"><strong>844 €</strong></td>
|
|||
|
|
</tr>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
<p>Bei 3 000 €/MWh kostet die Schranke <strong>nichts</strong>: Der risikoneutrale Plan hält sie längst ein. Bei diesem Preis lohnt sich das Vorhalten eines Blocks schon im Erwartungswert, lange bevor jemand über Risiko spricht.</p>
|
|||
|
|
<p>Erst bei einem Marktpreisdeckel von 300 €/MWh greift sie. Dann nimmt der risikoneutrale Plan in 8 von 40 Szenarien einen Lastabwurf in Kauf, weil er billiger ist als Vorhaltung — und die Schranke kostet 844 € je Tag für 18,4 vermiedene MWh im CVaR:</p>
|
|||
|
|
<p><span class="math display">
|
|||
|
|
\frac{844\ \text{€}}{18{,}4\ \text{MWh}} \approx 46\ \text{€ je vermiedener MWh}
|
|||
|
|
</span></p>
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>🎯 Merksatz</strong> Eine Risikoschranke ändert genau dann etwas, wenn der Schaden <strong>zu niedrig bepreist</strong> ist. Ist er realistisch bewertet, erledigt die Minimierung der erwarteten Kosten die Absicherung von selbst.</p>
|
|||
|
|
</blockquote>
|
|||
|
|
<p>Das ist keine akademische Feinheit, sondern die reale Lage der Energiewirtschaft: Ein Marktpreisdeckel bildet den volkswirtschaftlichen Schaden eines Stromausfalls nicht ab. Genau deshalb gibt es regulatorische Vorgaben zur Versorgungssicherheit — und diese Zahl, 46 € je vermiedener MWh, ist das, worüber sich verhandeln lässt. Sie ist dasselbe Instrument wie die Spalte „€ je kg” in <a href="mehrziel.html#sec:mehrziel-vorlegen">Abschnitt 14.8</a>.</p>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-uebungsaufgaben">17.7 Übungsaufgaben</h2>
|
|||
|
|
<blockquote>
|
|||
|
|
<p>Lösungen: <a href="anhang-loesungen.html#sec:loesungen-supplychain">Abschnitt A.17</a>.</p>
|
|||
|
|
</blockquote>
|
|||
|
|
<p><strong>Aufgabe 17.1 ⭐ — Der Umschlagpunkt.</strong> Rechnen Sie den Umschlagpunkt aus dem Schnellstart für eine Leistung von 60 MW statt 120 MW nach. In welche Richtung verschiebt er sich, und warum ist das plausibel?</p>
|
|||
|
|
<p><strong>Aufgabe 17.2 ⭐ — Die fehlende Nebenbedingung.</strong> Das Modell kennt Mindestlaufzeiten, aber keine <strong>Mindeststillstandszeiten</strong>. Formulieren Sie die entsprechende Bedingung. Welchen realen Sachverhalt bildet sie ab?</p>
|
|||
|
|
<p><strong>Aufgabe 17.3 ⭐⭐ — Wie viele Szenarien?</strong> Rechnen Sie mit 10, 20, 40 und 80 Szenarien. Ab wann ändert sich der Commitment-Plan nicht mehr? Was folgt daraus für die Praxis — und was hat das mit <a href="prognose.html#sec:prognose-messfalle">Abschnitt 15.6</a> zu tun?</p>
|
|||
|
|
<p><strong>Aufgabe 17.4 ⭐⭐ — Der Wert eines Speichers.</strong> Ergänzen Sie einen Batteriespeicher (100 MW, 400 MWh, Wirkungsgrad 90 %). Um wie viel sinken die erwarteten Kosten — und was ist der Speicher damit je MWh Kapazität wert?</p>
|
|||
|
|
<p><strong>Aufgabe 17.5 ⭐⭐⭐ — Wenn es größer wird.</strong> Erhöhen Sie auf 20 Blöcke und 168 Stunden (eine Woche). Beobachten Sie MIP-Gap und Rechenzeit (<a href="milp.html#sec:milp-gap">Abschnitt 6.8</a>). Ab wann wird das Problem für den exakten Solver zu groß — und welches Verfahren aus <a href="metaheuristiken.html#kap-metaheuristiken">Kapitel 9</a> würden Sie einsetzen?</p>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-denkfehler">17.8 Finde den Denkfehler</h2>
|
|||
|
|
<div class="card card-denkfehler">
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>🐛 „Wir rechnen mit dem P50-Szenario”</strong></p>
|
|||
|
|
<p>Ein Netzbetreiber beschreibt sein Vorgehen:</p>
|
|||
|
|
<blockquote>
|
|||
|
|
<p><em>„Wir verwenden für die Tagesplanung das <strong>P50-Szenario</strong> der Windprognose — also den > Median. Die Hälfte der Tage fällt besser aus, die Hälfte schlechter, im Mittel gleicht > sich das aus. Für die Extremfälle haben wir zusätzlich eine Reserve von 5 % der Last > vorgehalten, das ist die branchenübliche Größenordnung.”</em></p>
|
|||
|
|
</blockquote>
|
|||
|
|
<p>Das klingt sorgfältig, und beides ist gängige Praxis.</p>
|
|||
|
|
<p><strong>Beide Teile der Aussage enthalten einen Fehler. Welche?</strong></p>
|
|||
|
|
<p>Ein Hinweis zum zweiten Teil: Sehen Sie sich in der Ausgabe des Programms an, <em>welche</em> Größe der Plan verändert hat, um die Lastabwürfe zu vermeiden — und vergleichen Sie das mit dem, was eine prozentuale Leistungsreserve leisten kann.</p>
|
|||
|
|
</blockquote>
|
|||
|
|
</div>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-quiz">17.9 Micro-Quiz</h2>
|
|||
|
|
<div class="card card-quiz">
|
|||
|
|
<blockquote>
|
|||
|
|
<p><strong>❓ Drei Fragen</strong></p>
|
|||
|
|
<p><strong>1. Warum ist die Merit-Order-Regel („billigstes Kraftwerk zuerst”) für die Tagesplanung nicht ausreichend?</strong> a) Weil sie die Netzverluste ignoriert. b) Weil Anfahrkosten und Mindestlaufzeiten die Stunden miteinander koppeln — die Entscheidung einer Stunde bindet die folgenden. c) Weil Grenzkosten in der Praxis nicht bekannt sind.</p>
|
|||
|
|
<p><strong>2. Was unterscheidet dieses zweistufige Modell von dem in <a href="unsicherheit.html#kap-unsicherheit">Kapitel 12</a>?</strong> a) Es hat mehr Szenarien. b) Die erste Stufe ist <strong>binär</strong> und damit nicht anteilig anpassbar. c) Es minimiert den CVaR statt des Erwartungswerts.</p>
|
|||
|
|
<p><strong>3. Bei 3 000 €/MWh kostet die CVaR-Schranke nichts, bei 300 €/MWh dagegen 844 €/Tag. Warum?</strong> a) Weil der Solver bei kleineren Zahlen ungenauer rechnet. b) Weil bei realistisch hohem Abwurfpreis schon die Minimierung der erwarteten Kosten Vorhaltung erzwingt; die Schranke greift nur, wenn der Schaden zu niedrig bepreist ist. c) Weil die CVaR-Schranke bei hohen Preisen numerisch unwirksam wird.</p>
|
|||
|
|
</blockquote>
|
|||
|
|
</div>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-selbsttest">17.10 Selbsttest</h2>
|
|||
|
|
<ol type="1">
|
|||
|
|
<li>Erklären Sie in zwei Sätzen, warum ein Kraftwerkspark nicht wie ein Regal aufgefüllt werden kann.</li>
|
|||
|
|
<li>Welche Größe ist in diesem Modell die „here and now”-Entscheidung, welche die „wait and see”-Entscheidung?</li>
|
|||
|
|
<li>Warum ist eine Planung auf den Erwartungswert hier <em>systematisch</em> zu knapp und nicht nur ungenau?</li>
|
|||
|
|
<li>Wann ändert eine Risikoschranke etwas an der Lösung — und wann nicht?</li>
|
|||
|
|
<li>Sie sollen einer Regulierungsbehörde erklären, was ihre Versorgungssicherheitsvorgabe kostet. Welche Zahl nennen Sie, und wie ist sie entstanden?</li>
|
|||
|
|
</ol>
|
|||
|
|
<hr />
|
|||
|
|
<h2 id="sec:supplychain-zusammenfassung">17.11 Zusammenfassung</h2>
|
|||
|
|
<ul>
|
|||
|
|
<li><strong>Anfahrkosten und Mindestlaufzeiten</strong> machen aus 24 Stundenentscheidungen ein einziges Problem. Die Merit-Order gilt nur für einen einzelnen Zeitpunkt; im Schnellstart schlägt die fünfmal teurere Gasturbine den Kernblock bis knapp vier Stunden.</li>
|
|||
|
|
<li>Das Modell ist zweistufig mit <strong>binärer erster Stufe</strong>: Das An/Aus steht am Vorabend fest und gilt für alle Szenarien, die Fahrweise passt sich an. Das ist eine Verschärfung gegenüber <a href="unsicherheit.html#kap-unsicherheit">Kapitel 12</a>, wo die erste Stufe kontinuierlich war.</li>
|
|||
|
|
<li>Eine Planung auf den <strong>Wind-Erwartungswert</strong> kostet hier 149 % mehr und führt in 28 von 40 Szenarien zum Lastabwurf. Der Grund ist nicht die Prognosegüte, sondern die Trägheit: Was nicht angefahren wurde, ist nicht verfügbar.</li>
|
|||
|
|
<li>Der zweistufige Plan braucht dafür nur <strong>sechs zusätzliche Blockstunden</strong>.</li>
|
|||
|
|
<li>Eine <strong>CVaR-Schranke</strong> auf die nicht gedeckte Energie ändert nur dann etwas, wenn der Schaden zu niedrig bepreist ist. Bei realistischen 3 000 €/MWh erledigt die Minimierung der erwarteten Kosten die Absicherung von selbst.</li>
|
|||
|
|
<li>Wo sie greift, liefert sie den <strong>Preis der Vorgabe</strong>: 46 € je vermiedener MWh — eine Zahl, über die man verhandeln kann.</li>
|
|||
|
|
</ul>
|
|||
|
|
|
|||
|
|
</article>
|
|||
|
|
<button type="button" class="fortschritt-knopf" data-kapitel="supplychain.html"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg> <span>Als gelesen markieren</span></button>
|
|||
|
|
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="bruecke.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten</span></a><a class="prev-next-knopf prev-next-next" href="finanzdaten.html"><span><small>Weiter</small>Kapitel 18: Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
|
|||
|
|
</main>
|
|||
|
|
</div>
|
|||
|
|
<footer class="site-footer">
|
|||
|
|
<p>© Dieter Schlüter · <a href="gesamtdokument.html">Gesamtdokument</a> ·
|
|||
|
|
<a href="programme.html">Beispielprogramme</a></p>
|
|||
|
|
</footer>
|
|||
|
|
<script defer src="assets/search-index.js"></script>
|
|||
|
|
<script defer src="assets/programme.js"></script>
|
|||
|
|
<script defer src="assets/site.js"></script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|