Files
pumpingStation/wiki/diagrams/modes/levelbased.drawio.svg
znetsixe 4637448c49 Add modes/ section with levelbased page as the template
Introduces the pattern: basin model is the shared canvas (mode-agnostic
physics); each control mode is its own page under wiki/modes/ plus a
demand-vs-level transfer-function diagram under wiki/diagrams/modes/.

- wiki/modes/README.md — index + per-mode page template (inputs,
  threshold policy, demand formula, edge cases, related)
- wiki/modes/levelbased.md — first worked example using the new naming
  convention (dryRunLevel / minLevel / startLevel / maxLevel /
  overflowLevel). Forward-looking — the code still uses the old names
  until the pending rename refactor.
- wiki/diagrams/modes/levelbased.drawio.svg — transfer-function plot
  (zones: STOP / DEAD ZONE / RAMP / SATURATE, safety trips outside the
  plot). Round-trippable via embedded drawio XML.
- functional-description.md — replaced the inline levelbased/manual
  subsection with a table pointing at the modes/ pages. Removed the
  old control-zones ASCII diagram reference (superseded by the
  per-mode transfer function).
- wiki/README.md — added Control modes entry + diagrams/modes/ pointer.

The remaining placeholder modes (flowbased, pressureBased,
percentageBased, powerBased, hybrid, manual) can each fill in the
template independently.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 15:45:01 +02:00

105 lines
12 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 820 560" font-family="Arial, sans-serif" font-size="13"
content="&lt;mxfile host=&quot;app.diagrams.net&quot; agent=&quot;Claude Code placeholder&quot; version=&quot;22.0.0&quot;&gt;&#10; &lt;diagram id=&quot;levelbasedMode&quot; name=&quot;Level-based mode&quot;&gt;&#10; &lt;mxGraphModel dx=&quot;1000&quot; dy=&quot;700&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; pageWidth=&quot;820&quot; pageHeight=&quot;560&quot;&gt;&#10; &lt;root&gt;&#10; &lt;mxCell id=&quot;0&quot;/&gt;&#10; &lt;mxCell id=&quot;1&quot; parent=&quot;0&quot;/&gt;&#10; &lt;mxCell id=&quot;title&quot; value=&quot;Level-based mode — demand as a function of basin level&quot; style=&quot;text;html=1;fontSize=16;fontStyle=1;align=center;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;60&quot; y=&quot;20&quot; width=&quot;700&quot; height=&quot;24&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;subtitle&quot; value=&quot;Thresholds are static (from config); safety trips are handled by the mode-independent safety layer&quot; style=&quot;text;html=1;fontSize=11;fontStyle=2;align=center;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;60&quot; y=&quot;46&quot; width=&quot;700&quot; height=&quot;18&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;plot_area&quot; value=&quot;&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;fillColor=#FAFAFA;strokeColor=#555555;strokeWidth=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;140&quot; y=&quot;100&quot; width=&quot;560&quot; height=&quot;340&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;stop_zone&quot; value=&quot;STOP&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;fillColor=#FADBD8;strokeColor=none;fontSize=12;fontStyle=1;verticalAlign=top;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;140&quot; y=&quot;100&quot; width=&quot;90&quot; height=&quot;340&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;dead_zone&quot; value=&quot;DEAD ZONE&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;fillColor=#FEF9E7;strokeColor=none;fontSize=12;fontStyle=1;verticalAlign=top;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;230&quot; y=&quot;100&quot; width=&quot;140&quot; height=&quot;340&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;ramp_zone&quot; value=&quot;RAMP&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;fillColor=#EAFAF1;strokeColor=none;fontSize=12;fontStyle=1;verticalAlign=top;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;370&quot; y=&quot;100&quot; width=&quot;190&quot; height=&quot;340&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;saturate_zone&quot; value=&quot;SATURATE&quot; style=&quot;rounded=0;whiteSpace=wrap;html=1;fillColor=#D5F5E3;strokeColor=none;fontSize=12;fontStyle=1;verticalAlign=top;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;560&quot; y=&quot;100&quot; width=&quot;140&quot; height=&quot;340&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;curve_stop&quot; value=&quot;demand = 0&quot; style=&quot;endArrow=none;html=1;strokeColor=#C0392B;strokeWidth=3;fontSize=11;verticalAlign=top;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10; &lt;mxPoint x=&quot;140&quot; y=&quot;440&quot; as=&quot;sourcePoint&quot;/&gt;&#10; &lt;mxPoint x=&quot;230&quot; y=&quot;440&quot; as=&quot;targetPoint&quot;/&gt;&#10; &lt;/mxGeometry&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;curve_dead&quot; value=&quot;demand = last cmd&quot; style=&quot;endArrow=none;html=1;strokeColor=#E67E22;strokeWidth=3;dashed=1;fontSize=11;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10; &lt;mxPoint x=&quot;230&quot; y=&quot;440&quot; as=&quot;sourcePoint&quot;/&gt;&#10; &lt;mxPoint x=&quot;370&quot; y=&quot;440&quot; as=&quot;targetPoint&quot;/&gt;&#10; &lt;/mxGeometry&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;curve_ramp&quot; value=&quot;demand = lerp(level, [start,max], [0,100])&quot; style=&quot;endArrow=none;html=1;strokeColor=#1E8449;strokeWidth=3;fontSize=11;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10; &lt;mxPoint x=&quot;370&quot; y=&quot;440&quot; as=&quot;sourcePoint&quot;/&gt;&#10; &lt;mxPoint x=&quot;560&quot; y=&quot;100&quot; as=&quot;targetPoint&quot;/&gt;&#10; &lt;/mxGeometry&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;curve_sat&quot; value=&quot;demand = 100&quot; style=&quot;endArrow=none;html=1;strokeColor=#1E8449;strokeWidth=3;fontSize=11;verticalAlign=top;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&#10; &lt;mxPoint x=&quot;560&quot; y=&quot;100&quot; as=&quot;sourcePoint&quot;/&gt;&#10; &lt;mxPoint x=&quot;700&quot; y=&quot;100&quot; as=&quot;targetPoint&quot;/&gt;&#10; &lt;/mxGeometry&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;safety_left&quot; value=&quot;SAFETY: dry-run&#10;(level &lt; dryRunLevel)&quot; style=&quot;text;html=1;align=center;fontColor=#C0392B;fontSize=11;fontStyle=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;50&quot; y=&quot;250&quot; width=&quot;100&quot; height=&quot;40&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;mxCell id=&quot;safety_right&quot; value=&quot;SAFETY: spill&#10;(level &gt; overflowLevel)&quot; style=&quot;text;html=1;align=center;fontColor=#C0392B;fontSize=11;fontStyle=1;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&#10; &lt;mxGeometry x=&quot;695&quot; y=&quot;250&quot; width=&quot;110&quot; height=&quot;40&quot; as=&quot;geometry&quot;/&gt;&#10; &lt;/mxCell&gt;&#10; &lt;/root&gt;&#10; &lt;/mxGraphModel&gt;&#10; &lt;/diagram&gt;&#10;&lt;/mxfile&gt;">
<title>Level-based mode — demand as a function of basin level</title>
<!-- Title -->
<text x="410" y="32" text-anchor="middle" font-weight="bold" font-size="16">Level-based mode — demand as a function of basin level</text>
<text x="410" y="52" text-anchor="middle" font-size="11" font-style="italic" fill="#555">Thresholds are static (from config); safety trips are handled by the mode-independent safety layer</text>
<!-- Plot area background (zones) -->
<rect x="140" y="100" width="90" height="340" fill="#FADBD8" />
<rect x="230" y="100" width="140" height="340" fill="#FEF9E7" />
<rect x="370" y="100" width="190" height="340" fill="#EAFAF1" />
<rect x="560" y="100" width="140" height="340" fill="#D5F5E3" />
<!-- Plot outline -->
<rect x="140" y="100" width="560" height="340" fill="none" stroke="#555" stroke-width="1" />
<!-- Zone labels -->
<text x="185" y="120" text-anchor="middle" font-weight="bold" font-size="13" fill="#922B21">STOP</text>
<text x="185" y="136" text-anchor="middle" font-size="10" fill="#922B21">MGC shutdown</text>
<text x="300" y="120" text-anchor="middle" font-weight="bold" font-size="13" fill="#B7950B">DEAD ZONE</text>
<text x="300" y="136" text-anchor="middle" font-size="10" fill="#B7950B">hysteresis</text>
<text x="465" y="120" text-anchor="middle" font-weight="bold" font-size="13" fill="#1E8449">RAMP</text>
<text x="465" y="136" text-anchor="middle" font-size="10" fill="#1E8449">linear 0→100 %</text>
<text x="630" y="120" text-anchor="middle" font-weight="bold" font-size="13" fill="#148F3D">SATURATE</text>
<text x="630" y="136" text-anchor="middle" font-size="10" fill="#148F3D">clamped at 100 %</text>
<!-- Y axis -->
<line x1="140" y1="100" x2="140" y2="440" stroke="#000" stroke-width="1.5" />
<text x="132" y="104" text-anchor="end" font-size="11">100 %</text>
<text x="132" y="444" text-anchor="end" font-size="11">0 %</text>
<line x1="136" y1="100" x2="140" y2="100" stroke="#000" stroke-width="1" />
<line x1="136" y1="440" x2="140" y2="440" stroke="#000" stroke-width="1" />
<text x="95" y="276" text-anchor="middle" font-weight="bold" transform="rotate(-90 95 276)">demand</text>
<!-- X axis -->
<line x1="140" y1="440" x2="700" y2="440" stroke="#000" stroke-width="1.5" />
<!-- Ticks + labels for each threshold -->
<g font-size="11" text-anchor="middle">
<line x1="140" y1="440" x2="140" y2="448" stroke="#000" />
<text x="140" y="462" fill="#C0392B" font-weight="bold">dryRunLevel</text>
<text x="140" y="476" fill="#C0392B" font-size="9" font-style="italic">(safety)</text>
<line x1="230" y1="440" x2="230" y2="448" stroke="#000" />
<text x="230" y="462">minLevel</text>
<line x1="370" y1="440" x2="370" y2="448" stroke="#000" />
<text x="370" y="462">startLevel</text>
<line x1="560" y1="440" x2="560" y2="448" stroke="#000" />
<text x="560" y="462">maxLevel</text>
<line x1="700" y1="440" x2="700" y2="448" stroke="#000" />
<text x="700" y="462" fill="#C0392B" font-weight="bold">overflowLevel</text>
<text x="700" y="476" fill="#C0392B" font-size="9" font-style="italic">(safety)</text>
</g>
<text x="420" y="500" text-anchor="middle" font-weight="bold">basin level</text>
<!-- Transfer function curve -->
<!-- STOP segment: y=440 (demand=0) -->
<line x1="140" y1="440" x2="230" y2="440" stroke="#C0392B" stroke-width="3" />
<!-- DEAD ZONE segment: dashed to signal "hold" -->
<line x1="230" y1="440" x2="370" y2="440" stroke="#E67E22" stroke-width="3" stroke-dasharray="8 4" />
<!-- RAMP: diagonal -->
<line x1="370" y1="440" x2="560" y2="100" stroke="#1E8449" stroke-width="3" />
<!-- SATURATE: y=100 (demand=100) -->
<line x1="560" y1="100" x2="700" y2="100" stroke="#1E8449" stroke-width="3" />
<!-- Endpoint dots -->
<circle cx="230" cy="440" r="4" fill="#C0392B" />
<circle cx="370" cy="440" r="4" fill="#1E8449" />
<circle cx="560" cy="100" r="4" fill="#1E8449" />
<!-- Curve annotation for dead-zone semantics (important — the DEAD ZONE is not really "0") -->
<g>
<line x1="300" y1="440" x2="300" y2="220" stroke="#B7950B" stroke-width="1" stroke-dasharray="2 3" />
<polygon points="297,220 303,220 300,214" fill="#B7950B" />
<text x="308" y="225" font-size="10" fill="#B7950B" font-style="italic">could be anywhere 0100 %</text>
<text x="308" y="239" font-size="10" fill="#B7950B" font-style="italic">(holds whatever RAMP last set)</text>
</g>
<!-- Safety trip markers — outside the plot to emphasize "never-should-happen" -->
<g>
<!-- Left: dry-run -->
<line x1="108" y1="265" x2="128" y2="285" stroke="#C0392B" stroke-width="3" />
<line x1="128" y1="265" x2="108" y2="285" stroke="#C0392B" stroke-width="3" />
<text x="118" y="310" text-anchor="middle" font-size="11" font-weight="bold" fill="#C0392B">SAFETY</text>
<text x="118" y="324" text-anchor="middle" font-size="10" fill="#C0392B">dry-run</text>
<text x="118" y="338" text-anchor="middle" font-size="10" fill="#C0392B">(pumps forced OFF)</text>
<!-- Right: overflow -->
<line x1="712" y1="265" x2="732" y2="285" stroke="#C0392B" stroke-width="3" />
<line x1="732" y1="265" x2="712" y2="285" stroke="#C0392B" stroke-width="3" />
<text x="722" y="310" text-anchor="middle" font-size="11" font-weight="bold" fill="#C0392B">SAFETY</text>
<text x="722" y="324" text-anchor="middle" font-size="10" fill="#C0392B">spill</text>
<text x="722" y="338" text-anchor="middle" font-size="10" fill="#C0392B">(log + alarm)</text>
</g>
<!-- Footer note -->
<text x="410" y="540" text-anchor="middle" font-size="10" font-style="italic" fill="#666">
Every mode page in this wiki uses the same transfer-function format. Only the curve shape (and what drives the X-axis) changes.
</text>
</svg>