- Add bash script (siter-solar-analysis.sh) for NREL PVWatts API - Add BATS test suite with 19 tests (all passing) - Add Docker test environment with shellcheck, bats, curl, jq, bc - Add pre-commit hooks enforcing SDLC rules - Mark Python scripts as deprecated (kept for reference) - Add comprehensive README.md and AGENTS.md documentation - Add .env.example for configuration template - Add .gitignore excluding private data (base-bill/, .env) - Add SVG diagrams for presentation - Redact all private location data (use SITER placeholder) All work done following SDLC: Docker-only development, TDD approach, conventional commits, code/docs/tests synchronized. Generated with Crush Assisted-by: GLM-5 via Crush <crush@charm.land>
146 lines
8.7 KiB
XML
146 lines
8.7 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450">
|
||
<defs>
|
||
<linearGradient id="scenarioGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||
<stop offset="0%" style="stop-color:#3b82f6;stop-opacity:1" />
|
||
<stop offset="100%" style="stop-color:#1d4ed8;stop-opacity:1" />
|
||
</linearGradient>
|
||
<linearGradient id="highlightGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||
<stop offset="0%" style="stop-color:#22c55e;stop-opacity:1" />
|
||
<stop offset="100%" style="stop-color:#16a34a;stop-opacity:1" />
|
||
</linearGradient>
|
||
</defs>
|
||
|
||
<!-- Background -->
|
||
<rect width="800" height="450" fill="#f8fafc"/>
|
||
|
||
<!-- Title -->
|
||
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#1e293b">System Size Comparison</text>
|
||
<text x="400" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">NREL PVWatts Analysis: 16 Panels at Different Wattages</text>
|
||
|
||
<!-- Left Chart: Annual Production -->
|
||
<text x="200" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">Annual Production (kWh)</text>
|
||
|
||
<!-- Y-axis -->
|
||
<line x1="60" y1="100" x2="60" y2="320" stroke="#cbd5e1" stroke-width="1"/>
|
||
<text x="25" y="210" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b" transform="rotate(-90, 25, 210)">kWh/year</text>
|
||
|
||
<!-- Y-axis labels -->
|
||
<text x="55" y="324" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#94a3b8">0</text>
|
||
<text x="55" y="264" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#94a3b8">5k</text>
|
||
<text x="55" y="204" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#94a3b8">10k</text>
|
||
<text x="55" y="144" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#94a3b8">15k</text>
|
||
|
||
<!-- Grid lines -->
|
||
<g stroke="#e2e8f0" stroke-width="1">
|
||
<line x1="60" y1="260" x2="340" y2="260"/>
|
||
<line x1="60" y1="200" x2="340" y2="200"/>
|
||
<line x1="60" y1="140" x2="340" y2="140"/>
|
||
</g>
|
||
|
||
<!-- X-axis -->
|
||
<line x1="60" y1="320" x2="340" y2="320" stroke="#cbd5e1" stroke-width="1"/>
|
||
|
||
<!-- Bars: 250W=6004, 300W=7204, 350W=8405, 400W=9606, 450W=10806 -->
|
||
<!-- Scale: 15000 kWh = 220px, so 1 kWh = 0.0147px -->
|
||
|
||
<!-- 250W: 6004 kWh = 88px -->
|
||
<rect x="75" y="232" width="40" height="88" fill="url(#scenarioGrad)" rx="3"/>
|
||
<text x="95" y="225" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b">6,004</text>
|
||
<text x="95" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">250W</text>
|
||
|
||
<!-- 300W: 7204 kWh = 106px -->
|
||
<rect x="125" y="214" width="40" height="106" fill="url(#scenarioGrad)" rx="3"/>
|
||
<text x="145" y="207" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b">7,204</text>
|
||
<text x="145" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">300W</text>
|
||
|
||
<!-- 350W: 8405 kWh = 123px -->
|
||
<rect x="175" y="197" width="40" height="123" fill="url(#scenarioGrad)" rx="3"/>
|
||
<text x="195" y="190" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b">8,405</text>
|
||
<text x="195" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">350W</text>
|
||
|
||
<!-- 400W: 9606 kWh = 141px -->
|
||
<rect x="225" y="179" width="40" height="141" fill="url(#highlightGrad)" rx="3"/>
|
||
<text x="245" y="172" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b" font-weight="bold">9,606</text>
|
||
<text x="245" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#22c55e" font-weight="bold">400W</text>
|
||
|
||
<!-- 450W: 10806 kWh = 158px -->
|
||
<rect x="275" y="162" width="40" height="158" fill="url(#scenarioGrad)" rx="3"/>
|
||
<text x="295" y="155" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b">10,806</text>
|
||
<text x="295" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">450W</text>
|
||
|
||
<!-- Current system marker -->
|
||
<text x="95" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#ef4444">(current)</text>
|
||
|
||
<!-- Right Chart: Payback Period -->
|
||
<text x="580" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">Payback Period (Years)</text>
|
||
|
||
<!-- Y-axis -->
|
||
<line x1="440" y1="100" x2="440" y2="320" stroke="#cbd5e1" stroke-width="1"/>
|
||
<text x="405" y="210" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b" transform="rotate(-90, 405, 210)">Years</text>
|
||
|
||
<!-- Y-axis labels -->
|
||
<text x="435" y="324" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#94a3b8">0</text>
|
||
<text x="435" y="264" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#94a3b8">5</text>
|
||
<text x="435" y="204" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#94a3b8">10</text>
|
||
<text x="435" y="144" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#94a3b8">15</text>
|
||
|
||
<!-- Grid lines -->
|
||
<g stroke="#e2e8f0" stroke-width="1">
|
||
<line x1="440" y1="260" x2="720" y2="260"/>
|
||
<line x1="440" y1="200" x2="720" y2="200"/>
|
||
<line x1="440" y1="140" x2="720" y2="140"/>
|
||
</g>
|
||
|
||
<!-- X-axis -->
|
||
<line x1="440" y1="320" x2="720" y2="320" stroke="#cbd5e1" stroke-width="1"/>
|
||
|
||
<!-- Bars: 250W=10.2yr, 300W=8.5yr, 350W=7.3yr, 400W=6.4yr, 450W=5.7yr -->
|
||
<!-- Scale: 15 yr = 220px, so 1 yr = 14.67px -->
|
||
|
||
<!-- 250W: 10.2 yr = 150px -->
|
||
<rect x="455" y="170" width="40" height="150" fill="#ef4444" rx="3"/>
|
||
<text x="475" y="163" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b">10.2</text>
|
||
<text x="475" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">250W</text>
|
||
|
||
<!-- 300W: 8.5 yr = 125px -->
|
||
<rect x="505" y="195" width="40" height="125" fill="#f97316" rx="3"/>
|
||
<text x="525" y="188" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b">8.5</text>
|
||
<text x="525" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">300W</text>
|
||
|
||
<!-- 350W: 7.3 yr = 107px -->
|
||
<rect x="555" y="213" width="40" height="107" fill="#eab308" rx="3"/>
|
||
<text x="575" y="206" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b">7.3</text>
|
||
<text x="575" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">350W</text>
|
||
|
||
<!-- 400W: 6.4 yr = 94px -->
|
||
<rect x="605" y="226" width="40" height="94" fill="url(#highlightGrad)" rx="3"/>
|
||
<text x="625" y="219" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b" font-weight="bold">6.4</text>
|
||
<text x="625" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#22c55e" font-weight="bold">400W</text>
|
||
|
||
<!-- 450W: 5.7 yr = 84px -->
|
||
<rect x="655" y="236" width="40" height="84" fill="#22c55e" rx="3"/>
|
||
<text x="675" y="229" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#1e293b">5.7</text>
|
||
<text x="675" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">450W</text>
|
||
|
||
<!-- Target line -->
|
||
<line x1="440" y1="260" x2="720" y2="260" stroke="#22c55e" stroke-width="2" stroke-dasharray="5,3"/>
|
||
<text x="725" y="264" font-family="Arial, sans-serif" font-size="9" fill="#22c55e">5 yr target</text>
|
||
|
||
<!-- Summary table -->
|
||
<rect x="150" y="365" width="500" height="75" rx="6" fill="white" stroke="#e2e8f0"/>
|
||
<text x="400" y="385" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e293b">Recommendation Summary</text>
|
||
|
||
<text x="170" y="405" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Current (16×250W):</text>
|
||
<text x="290" y="405" font-family="Arial, sans-serif" font-size="10" fill="#ef4444">10.2 yr payback, 11% offset</text>
|
||
|
||
<text x="170" y="425" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Optimal (16×400W):</text>
|
||
<text x="290" y="425" font-family="Arial, sans-serif" font-size="10" fill="#22c55e" font-weight="bold">6.4 yr payback, 18% offset</text>
|
||
|
||
<text x="480" y="405" font-family="Arial, sans-serif" font-size="10" fill="#64748b">100% Offset requires:</text>
|
||
<text x="600" y="405" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">~36 kW (89 panels)</text>
|
||
|
||
<text x="480" y="425" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Consumption:</text>
|
||
<text x="570" y="425" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">~24,000 kWh/yr</text>
|
||
</svg>
|