feat: initial project setup with bash-based NREL analysis

- 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>
This commit is contained in:
Charles N Wyble
2026-02-27 16:45:41 -05:00
commit 400764a9ff
22 changed files with 3587 additions and 0 deletions

136
images/image-5.svg Normal file
View File

@@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 500">
<defs>
<linearGradient id="steelGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#9ca3af;stop-opacity:1" />
<stop offset="50%" style="stop-color:#6b7280;stop-opacity:1" />
<stop offset="100%" style="stop-color:#4b5563;stop-opacity:1" />
</linearGradient>
<linearGradient id="panelGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#1e40af;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1e3a8a;stop-opacity:1" />
</linearGradient>
<linearGradient id="groundGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#92400e;stop-opacity:1" />
<stop offset="100%" style="stop-color:#78350f;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background -->
<rect width="800" height="500" fill="#f8fafc"/>
<!-- Title -->
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#1e293b">Ground-Mount Rack Construction</text>
<text x="400" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">Custom Steel Design - 2 Panels per Rack</text>
<!-- Ground -->
<rect x="0" y="380" width="800" height="120" fill="url(#groundGrad)"/>
<ellipse cx="200" cy="385" rx="150" ry="8" fill="#a3a38a" opacity="0.3"/>
<!-- Main Side View -->
<text x="200" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">Side View</text>
<!-- Steel posts in ground -->
<rect x="80" y="260" width="12" height="140" fill="url(#steelGrad)" rx="2"/>
<rect x="150" y="260" width="12" height="140" fill="url(#steelGrad)" rx="2"/>
<rect x="220" y="260" width="12" height="140" fill="url(#steelGrad)" rx="2"/>
<!-- Post labels -->
<text x="86" y="415" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="white">Post 1</text>
<text x="156" y="415" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="white">Post 2</text>
<text x="226" y="415" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="white">Post 3</text>
<!-- Horizontal rails -->
<rect x="75" y="230" width="165" height="8" fill="url(#steelGrad)" rx="2" transform="rotate(-15, 157, 234)"/>
<rect x="75" y="270" width="165" height="8" fill="url(#steelGrad)" rx="2" transform="rotate(-15, 157, 274)"/>
<!-- Solar panel (tilted) -->
<g transform="rotate(-15, 157, 200)">
<rect x="80" y="140" width="155" height="90" fill="url(#panelGrad)" rx="3" stroke="#1e3a8a" stroke-width="2"/>
<!-- Panel grid pattern -->
<g stroke="#3b82f6" stroke-width="1" opacity="0.4">
<line x1="80" y1="170" x2="235" y2="170"/>
<line x1="80" y1="200" x2="235" y2="200"/>
<line x1="120" y1="140" x2="120" y2="230"/>
<line x1="160" y1="140" x2="160" y2="230"/>
<line x1="200" y1="140" x2="200" y2="230"/>
</g>
<!-- Panel cells -->
<rect x="85" y="145" width="30" height="22" fill="#2563eb" opacity="0.5"/>
<rect x="120" y="145" width="35" height="22" fill="#2563eb" opacity="0.5"/>
<rect x="160" y="145" width="35" height="22" fill="#2563eb" opacity="0.5"/>
<rect x="200" y="145" width="30" height="22" fill="#2563eb" opacity="0.5"/>
</g>
<!-- Tilt angle indicator -->
<path d="M 250 250 L 250 340 L 180 340" fill="none" stroke="#22c55e" stroke-width="2"/>
<text x="260" y="300" font-family="Arial, sans-serif" font-size="11" fill="#22c55e">30° tilt</text>
<!-- Post depth indicator -->
<line x1="60" y1="380" x2="60" y2="400" stroke="#ef4444" stroke-width="2"/>
<text x="55" y="395" text-anchor="end" font-family="Arial, sans-serif" font-size="9" fill="#ef4444">~4' in ground</text>
<!-- Top View -->
<text x="580" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">Top View (Single Rack)</text>
<!-- Top view background -->
<rect x="480" y="100" width="200" height="140" fill="#e2e8f0" rx="4"/>
<!-- Posts from above -->
<circle cx="510" cy="130" r="8" fill="url(#steelGrad)"/>
<circle cx="510" cy="210" r="8" fill="url(#steelGrad)"/>
<circle cx="650" cy="130" r="8" fill="url(#steelGrad)"/>
<circle cx="650" cy="210" r="8" fill="url(#steelGrad)"/>
<!-- Rails from above -->
<rect x="510" y="125" width="140" height="6" fill="url(#steelGrad)" rx="2"/>
<rect x="510" y="205" width="140" height="6" fill="url(#steelGrad)" rx="2"/>
<!-- Panels from above -->
<rect x="520" y="120" width="60" height="100" fill="url(#panelGrad)" rx="2" stroke="#1e3a8a" stroke-width="1"/>
<rect x="590" y="120" width="60" height="100" fill="url(#panelGrad)" rx="2" stroke="#1e3a8a" stroke-width="1"/>
<!-- Panel labels -->
<text x="550" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">Panel 1</text>
<text x="620" y="175" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="white">Panel 2</text>
<!-- Dimensions -->
<line x1="480" y1="250" x2="680" y2="250" stroke="#64748b" stroke-width="1"/>
<text x="580" y="265" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">~6' width</text>
<!-- Materials List -->
<rect x="50" y="430" width="300" height="60" rx="4" fill="white" stroke="#e2e8f0"/>
<text x="200" y="450" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#1e293b">Materials Per Rack</text>
<text x="70" y="470" font-family="Arial, sans-serif" font-size="10" fill="#64748b">• 6× 8' Steel Posts ($34.97 ea)</text>
<text x="220" y="470" font-family="Arial, sans-serif" font-size="10" fill="#64748b">• 14× U-bolts ($2.33 ea)</text>
<text x="70" y="485" font-family="Arial, sans-serif" font-size="10" fill="#64748b">• 3× Adj Clamps ($3.77 ea)</text>
<text x="220" y="485" font-family="Arial, sans-serif" font-size="10" fill="#64748b">• Total: ~$267/rack</text>
<!-- System Layout -->
<rect x="400" y="280" width="350" height="140" rx="4" fill="white" stroke="#e2e8f0"/>
<text x="575" y="300" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#1e293b">Full System Layout (8 Racks)</text>
<!-- Mini rack representations -->
<g fill="#3b82f6" opacity="0.7">
<rect x="420" y="320" width="30" height="20" rx="2"/>
<rect x="460" y="320" width="30" height="20" rx="2"/>
<rect x="500" y="320" width="30" height="20" rx="2"/>
<rect x="540" y="320" width="30" height="20" rx="2"/>
<rect x="580" y="320" width="30" height="20" rx="2"/>
<rect x="620" y="320" width="30" height="20" rx="2"/>
<rect x="660" y="320" width="30" height="20" rx="2"/>
<rect x="700" y="320" width="30" height="20" rx="2"/>
</g>
<text x="575" y="360" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">8 Racks × 2 Panels = 16 Panels Total</text>
<text x="575" y="375" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">System: 4.0 kW DC | Array: ~48' × 6'</text>
<text x="575" y="390" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#22c55e">1 rack completed ($360.95)</text>
<!-- U-bolt detail -->
<rect x="720" y="430" width="60" height="60" rx="4" fill="white" stroke="#e2e8f0"/>
<text x="750" y="448" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" font-weight="bold" fill="#1e293b">U-bolt</text>
<path d="M 735 480 Q 750 460 765 480" fill="none" stroke="#6b7280" stroke-width="3"/>
<circle cx="735" cy="480" r="3" fill="#6b7280"/>
<circle cx="765" cy="480" r="3" fill="#6b7280"/>
<text x="750" y="500" text-anchor="middle" font-family="Arial, sans-serif" font-size="8" fill="#94a3b8">5/16" × 5-3/8"</text>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB