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

11
.env.example Normal file
View File

@@ -0,0 +1,11 @@
# SITER Solar - Environment Configuration
# Copy this file to .env and fill in your values
# NREL API Key (required for solar production estimates)
# Get your free API key at: https://developer.nrel.gov/signup/
NREL_API_KEY=DEMO_KEY
# Site Location (optional - defaults to central Texas coordinates)
# These are used by the NREL PVWatts API for production estimates
SITER_LAT=30.44
SITER_LON=-97.62

27
.gitignore vendored Normal file
View File

@@ -0,0 +1,27 @@
# Environment files (contain sensitive location data)
.env
# Billing PDFs (may contain account numbers and personal info)
base-bill/
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
venv/
.venv/
# Analysis outputs
solar-analysis/nrel_solar_data.json
# OS files
.DS_Store
Thumbs.db
# IDE
.idea/
.vscode/
*.swp
*.swo

161
AGENTS.md Normal file
View File

@@ -0,0 +1,161 @@
# SITER Solar Project - Agent Documentation
This document provides technical reference for AI agents working on this project.
## Software Development Lifecycle (SDLC)
### Host System: Git and Docker ONLY
**All work must be done inside Docker containers.** The host system is ONLY for:
1. **Git operations** - commit, push, pull, branch, etc.
2. **Docker operations** - build, run, exec, etc.
**Never run code, tests, or other tools directly on the host system.**
### Development Workflow
1. **Build Docker image:**
```bash
docker build -f Dockerfile.test -t siter-solar-test .
```
2. **Run tests inside Docker:**
```bash
docker run --rm siter-solar-test
```
3. **Run analysis inside Docker:**
```bash
docker run --rm -e NREL_API_KEY=your_key siter-solar-test /app/solar-analysis/siter-solar-analysis.sh --scenarios
```
4. **Make atomic commits** using conventional commit format:
- `feat:` - New feature
- `fix:` - Bug fix
- `docs:` - Documentation only
- `test:` - Adding/updating tests
- `refactor:` - Code refactoring
- `chore:` - Maintenance tasks
### Code/Docs/Tests Synchronization
**Code, documentation, and tests MUST be kept in sync at all times.**
- When changing code, update tests and documentation in the same commit
- When changing documentation, verify code still matches
- When changing tests, ensure code passes new tests
- Pre-commit hooks enforce this synchronization
## Privacy Requirements
**CRITICAL: This is a public repository. All private data must be removed.**
### What MUST NOT be committed:
- Real addresses or locations (use "SITER" as placeholder)
- Account numbers, contract IDs, or meter numbers
- The `base-bill/` directory (contains personal billing data)
- `.env` file (contains real coordinates and API keys)
### What should be committed:
- `.env.example` - Template with placeholder values
- All code and documentation with "SITER" as location identifier
- SVG images and analysis scripts
### Location Data:
- Use configurable environment variables: `SITER_LAT`, `SITER_LON`
- Default to central Texas coordinates (30.44, -97.62)
- Never commit specific addresses
## File Structure
```
siter-solar/
├── AGENTS.md # This file
├── README.md # Project documentation
├── .env.example # Environment template
├── .env # Gitignored - actual values
├── .gitignore # Excludes private data
├── Dockerfile.test # Test environment
├── v2-siter-solar-plan.md # Main proposal document
├── images/ # SVG diagrams for presentation
│ ├── v2-image-1.svg # System architecture diagram
│ ├── v2-image-2.svg # Monthly production chart
│ ├── v2-image-3.svg # ROI/payback timeline
│ └── v2-image-4.svg # System size comparison
└── solar-analysis/ # NREL PVWatts analysis tools
├── siter-solar-analysis.sh # Main bash script (production)
├── tests/ # BATS test suite
│ └── siter-solar-analysis.bats
├── solar_estimate.py # DEPRECATED - kept for reference
├── solar_optimal.py # DEPRECATED - kept for reference
├── solar_optimized.py # DEPRECATED - kept for reference
├── Dockerfile # Production image
└── run-analysis.sh # Docker wrapper
```
## Running NREL Analysis
### Using Bash Script (Recommended)
Inside Docker:
```bash
# Basic analysis
./siter-solar-analysis.sh
# With custom API key
./siter-solar-analysis.sh -k YOUR_API_KEY
# Compare system sizes
./siter-solar-analysis.sh --scenarios
# JSON output
./siter-solar-analysis.sh --json
# Custom configuration
./siter-solar-analysis.sh -p 20 -w 400 --lat 30.5 --lon -97.7
```
### Python Scripts (DEPRECATED)
The Python scripts are kept for reference only. Use the bash script instead.
## Key Parameters
| Parameter | Value | Notes |
|-----------|-------|-------|
| System Capacity | 4.0 kW DC | 16 × 250W panels |
| Array Type | Fixed Open Rack | Ground mount |
| Tilt | 30° | Optimal for latitude |
| Azimuth | 180° | South-facing |
| Losses | 14% (default) | Can optimize to 8% |
## Base Power Contract Details
- **Energy Rate:** $0.085/kWh (fixed through Oct 2028)
- **Solar Buyback:** $0.04/kWh (credits apply to entire bill)
- **Subscription Fee:** $10/month
## ROI Calculation Method
1. Annual production from NREL PVWatts (kWh/year)
2. Self-consumption: 60% × production × $0.085/kWh
3. Grid export: 40% × production × $0.04/kWh
4. Total annual value = self-consumption + export
5. Payback = Total investment / annual value
## Testing
Run tests inside Docker:
```bash
docker build -f Dockerfile.test -t siter-solar-test .
docker run --rm siter-solar-test
```
## Updating the Analysis
1. Modify `siter-solar-analysis.sh` as needed
2. Update tests in `tests/siter-solar-analysis.bats`
3. Run tests to verify changes
4. Update `v2-siter-solar-plan.md` with new estimates
5. Commit all changes together in atomic commit

32
Dockerfile.test Normal file
View File

@@ -0,0 +1,32 @@
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
# Install dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
jq \
bc \
shellcheck \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/*
# Install bats (Bash Automated Testing System)
RUN curl -sL https://github.com/bats-core/bats-core/archive/refs/tags/v1.11.0.tar.gz | tar xz \
&& cd bats-core-1.11.0 \
&& ./install.sh /usr/local \
&& cd .. \
&& rm -rf bats-core-1.11.0
WORKDIR /app
# Copy scripts
COPY solar-analysis/ /app/solar-analysis/
# Make scripts executable
RUN chmod +x /app/solar-analysis/*.sh
WORKDIR /app/solar-analysis
ENTRYPOINT ["bats", "tests/"]

181
README.md Normal file
View File

@@ -0,0 +1,181 @@
# SITER Solar Installation Project
A solar energy installation proposal using NREL PVWatts API for production estimates and analysis of actual billing data.
## Overview
This project provides tools for analyzing solar panel installation economics, including:
- NREL PVWatts API integration for production estimates
- Financial analysis with ROI and payback calculations
- Multiple system size scenario comparisons
- Integration with Base Power battery backup systems
## Quick Start
### Prerequisites
- Docker (recommended)
- Or: bash, curl, jq, bc
### Run Analysis
```bash
# Build and run in Docker (recommended)
docker build -f Dockerfile.test -t siter-solar-test .
docker run --rm siter-solar-test /app/solar-analysis/siter-solar-analysis.sh
# Or run directly (if dependencies are installed)
./solar-analysis/siter-solar-analysis.sh
```
### Examples
```bash
# Basic analysis with defaults
./solar-analysis/siter-solar-analysis.sh
# Compare different system sizes
./solar-analysis/siter-solar-analysis.sh --scenarios
# Custom configuration
./solar-analysis/siter-solar-analysis.sh -p 20 -w 400 --lat 30.5 --lon -97.7
# JSON output for programmatic use
./solar-analysis/siter-solar-analysis.sh --json
# With your NREL API key (avoids rate limits)
NREL_API_KEY=your_key ./solar-analysis/siter-solar-analysis.sh --scenarios
```
## Configuration
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `NREL_API_KEY` | `DEMO_KEY` | Your NREL API key |
| `SITER_LAT` | `30.44` | Site latitude |
| `SITER_LON` | `-97.62` | Site longitude |
### Command Line Options
```
Usage: siter-solar-analysis.sh [OPTIONS]
Options:
-k, --api-key KEY NREL API key (or set NREL_API_KEY env var)
--lat VALUE Site latitude (default: 30.44)
--lon VALUE Site longitude (default: -97.62)
-p, --panels NUM Number of panels (default: 16)
-w, --watts WATTS Watts per panel (default: 250)
-t, --tilt DEGREES Array tilt in degrees (default: 30)
-a, --azimuth DEGREES Array azimuth (default: 180)
-l, --losses PERCENT System losses percent (default: 14)
--scenarios Run multiple system size scenarios
--json Output as JSON
--verbose Enable verbose output
-h, --help Show help message
-v, --version Show version
```
## Testing
Run the test suite inside Docker:
```bash
docker build -f Dockerfile.test -t siter-solar-test .
docker run --rm siter-solar-test
```
Tests use [BATS (Bash Automated Testing System)](https://github.com/bats-core/bats-core).
## Project Structure
```
siter-solar/
├── README.md # This file
├── AGENTS.md # Agent documentation (SDLC rules)
├── .env.example # Environment template
├── .gitignore # Excludes private data
├── Dockerfile.test # Test environment
├── v2-siter-solar-plan.md # Main proposal document
├── images/ # SVG diagrams
│ ├── v2-image-1.svg # System architecture
│ ├── v2-image-2.svg # Monthly production chart
│ ├── v2-image-3.svg # ROI/payback timeline
│ └── v2-image-4.svg # System size comparison
└── solar-analysis/ # Analysis tools
├── siter-solar-analysis.sh # Main bash script
├── tests/ # BATS test suite
│ └── siter-solar-analysis.bats
├── solar_estimate.py # DEPRECATED
├── solar_optimal.py # DEPRECATED
├── solar_optimized.py # DEPRECATED
├── Dockerfile # Production image
└── run-analysis.sh # Docker wrapper
```
## Key Parameters
| Parameter | Value | Notes |
|-----------|-------|-------|
| System Capacity | 4.0 kW DC | 16 × 250W panels |
| Array Type | Fixed Open Rack | Ground mount |
| Tilt | 30° | Optimal for latitude |
| Azimuth | 180° | South-facing |
| Losses | 14% (default) | Can optimize to 8% |
## Financial Model
The analysis uses the following assumptions:
- **Self-consumption:** 60% of production (avoids $0.085/kWh)
- **Grid export:** 40% of production (credits $0.04/kWh)
- **Base Power contract:** Fixed rate through Oct 2028
- **Solar buyback:** Credits apply to entire bill
### ROI Calculation
1. Annual production from NREL PVWatts (kWh/year)
2. Self-consumption value: 60% × production × $0.085/kWh
3. Export value: 40% × production × $0.04/kWh
4. Total annual value = self-consumption + export
5. Payback = Total investment / annual value
## NREL API
This project uses the [NREL PVWatts API](https://developer.nrel.gov/docs/solar/pvwatts/v6/).
Get your free API key at: https://developer.nrel.gov/signup/
**Rate Limits:**
- DEMO_KEY: 1 request/second, 1000 requests/hour
- Personal key: Higher limits
## Development
See [AGENTS.md](AGENTS.md) for development guidelines, including:
- SDLC requirements (Docker-only development)
- Testing requirements
- Privacy requirements
- Commit conventions
## License
See [LICENSE](LICENSE) file.
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make changes following SDLC rules in AGENTS.md
4. Ensure all tests pass
5. Submit a pull request
## Resources
- [NREL PVWatts API Documentation](https://developer.nrel.gov/docs/solar/pvwatts/v6/)
- [Base Power Solar Integration](https://www.basepowercompany.com/with-solar)
- [Sol-Ark Inverters](https://www.ecodirect.com/)

162
images/image-1.svg Normal file
View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 500">
<defs>
<linearGradient id="solarGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#f59e0b;stop-opacity:1" />
<stop offset="100%" style="stop-color:#d97706;stop-opacity:1" />
</linearGradient>
<linearGradient id="gridGrad" 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="batteryGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#10b981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#059669;stop-opacity:1" />
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="2" dy="2" stdDeviation="3" flood-opacity="0.2"/>
</filter>
</defs>
<!-- Background -->
<rect width="800" height="500" fill="#f8fafc"/>
<!-- Title -->
<text x="400" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="#1e293b">SITER Solar System Architecture</text>
<!-- Sun -->
<circle cx="100" cy="120" r="40" fill="#fbbf24"/>
<g stroke="#fbbf24" stroke-width="3">
<line x1="100" y1="60" x2="100" y2="45"/>
<line x1="100" y1="180" x2="100" y2="195"/>
<line x1="40" y1="120" x2="25" y2="120"/>
<line x1="160" y1="120" x2="175" y2="120"/>
<line x1="55" y1="75" x2="45" y2="65"/>
<line x1="145" y1="165" x2="155" y2="175"/>
<line x1="55" y1="165" x2="45" y2="175"/>
<line x1="145" y1="75" x2="155" y2="65"/>
</g>
<text x="100" y="215" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">Solar Radiation</text>
<text x="100" y="230" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">5.52 kWh/m²/day</text>
<!-- Solar Panels -->
<g filter="url(#shadow)">
<rect x="180" y="80" width="140" height="100" rx="8" fill="url(#solarGrad)"/>
<g fill="#92400e" opacity="0.3">
<rect x="190" y="90" width="25" height="35" rx="2"/>
<rect x="220" y="90" width="25" height="35" rx="2"/>
<rect x="250" y="90" width="25" height="35" rx="2"/>
<rect x="280" y="90" width="25" height="35" rx="2"/>
<rect x="190" y="130" width="25" height="35" rx="2"/>
<rect x="220" y="130" width="25" height="35" rx="2"/>
<rect x="250" y="130" width="25" height="35" rx="2"/>
<rect x="280" y="130" width="25" height="35" rx="2"/>
</g>
</g>
<text x="250" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">Solar Array</text>
<text x="250" y="218" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b">16 × 250W = 4.0 kW DC</text>
<text x="250" y="233" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">Ground Mount, 30° Tilt</text>
<!-- Arrow Sun to Panels -->
<path d="M145 120 L175 120" stroke="#f59e0b" stroke-width="3" marker-end="url(#arrowOrange)"/>
<defs>
<marker id="arrowOrange" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="#f59e0b"/>
</marker>
</defs>
<!-- Inverter -->
<g filter="url(#shadow)">
<rect x="370" y="100" width="100" height="60" rx="6" fill="#475569"/>
<rect x="380" y="110" width="30" height="40" rx="2" fill="#1e293b"/>
<rect x="415" y="110" width="45" height="15" rx="2" fill="#22c55e"/>
<rect x="415" y="130" width="45" height="8" rx="1" fill="#3b82f6"/>
<rect x="415" y="142" width="45" height="8" rx="1" fill="#eab308"/>
</g>
<text x="420" y="180" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e293b">Sol-Ark 5K</text>
<text x="420" y="195" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Hybrid Inverter</text>
<!-- Arrow Panels to Inverter -->
<path d="M320 130 L365 130" stroke="#f59e0b" stroke-width="2" marker-end="url(#arrowOrange)"/>
<text x="342" y="122" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">DC</text>
<!-- Home -->
<g filter="url(#shadow)">
<rect x="540" y="70" width="120" height="90" rx="8" fill="#e2e8f0"/>
<polygon points="540,70 600,30 660,70" fill="#64748b"/>
<rect x="580" y="110" width="40" height="50" fill="#3b82f6" opacity="0.3"/>
<rect x="555" y="100" width="25" height="20" fill="#1e293b" opacity="0.2"/>
<rect x="620" y="100" width="25" height="20" fill="#1e293b" opacity="0.2"/>
</g>
<text x="600" y="180" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">SITER</text>
<text x="600" y="198" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">~2,000 kWh/month</text>
<!-- Arrow Inverter to Home -->
<path d="M470 130 L535 115" stroke="#22c55e" stroke-width="2" marker-end="url(#arrowGreen)"/>
<defs>
<marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="#22c55e"/>
</marker>
</defs>
<text x="502" y="115" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">AC</text>
<!-- Base Power Battery -->
<g filter="url(#shadow)">
<rect x="540" y="260" width="120" height="80" rx="8" fill="url(#batteryGrad)"/>
<rect x="590" y="250" width="20" height="10" rx="2" fill="#059669"/>
<rect x="555" y="280" width="90" height="15" rx="2" fill="#064e3b"/>
<rect x="555" y="280" width="70" height="15" rx="2" fill="#34d399"/>
<text x="600" y="318" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">78%</text>
</g>
<text x="600" y="360" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e293b">Base Power Battery</text>
<text x="600" y="378" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">+ Automatic Transfer Switch</text>
<!-- Arrow Home to Battery -->
<path d="M600 165 L600 255" stroke="#10b981" stroke-width="2" stroke-dasharray="5,3" marker-end="url(#arrowGreen)"/>
<text x="615" y="210" font-family="Arial, sans-serif" font-size="9" fill="#64748b">charge</text>
<!-- Grid -->
<g filter="url(#shadow)">
<rect x="370" y="260" width="100" height="80" rx="6" fill="url(#gridGrad)"/>
<g stroke="white" stroke-width="2" opacity="0.5">
<line x1="380" y1="280" x2="460" y2="280"/>
<line x1="380" y1="300" x2="460" y2="300"/>
<line x1="380" y1="320" x2="460" y2="320"/>
<line x1="400" y1="270" x2="400" y2="330"/>
<line x1="420" y1="270" x2="420" y2="330"/>
<line x1="440" y1="270" x2="440" y2="330"/>
</g>
</g>
<text x="420" y="360" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e293b">Oncor Grid</text>
<text x="420" y="378" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Utility Delivery</text>
<!-- Arrow Inverter to Grid -->
<path d="M420 160 L420 255" stroke="#3b82f6" stroke-width="2" marker-end="url(#arrowBlue)"/>
<defs>
<marker id="arrowBlue" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="#3b82f6"/>
</marker>
</defs>
<text x="435" y="210" font-family="Arial, sans-serif" font-size="9" fill="#64748b">export</text>
<text x="435" y="220" font-family="Arial, sans-serif" font-size="8" fill="#94a3b8">@ $0.04/kWh</text>
<!-- Grid to Battery -->
<path d="M470 300 L535 300" stroke="#3b82f6" stroke-width="2" stroke-dasharray="5,3" marker-end="url(#arrowBlue)"/>
<text x="502" y="292" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">backup</text>
<!-- Legend -->
<rect x="50" y="420" width="700" height="60" rx="6" fill="#f1f5f9"/>
<text x="400" y="440" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#475569">Energy Flow</text>
<line x1="100" y1="460" x2="140" y2="460" stroke="#f59e0b" stroke-width="3"/>
<text x="150" y="464" font-family="Arial, sans-serif" font-size="10" fill="#64748b">DC Power</text>
<line x1="250" y1="460" x2="290" y2="460" stroke="#22c55e" stroke-width="3"/>
<text x="300" y="464" font-family="Arial, sans-serif" font-size="10" fill="#64748b">AC to Home</text>
<line x1="400" y1="460" x2="440" y2="460" stroke="#3b82f6" stroke-width="3"/>
<text x="450" y="464" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Grid Export</text>
<line x1="550" y1="460" x2="590" y2="460" stroke="#10b981" stroke-width="2" stroke-dasharray="5,3"/>
<text x="600" y="464" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Battery Charge</text>
</svg>

After

Width:  |  Height:  |  Size: 8.8 KiB

112
images/image-2.svg Normal file
View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400">
<defs>
<linearGradient id="barGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#f59e0b;stop-opacity:1" />
<stop offset="100%" style="stop-color:#d97706;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background -->
<rect width="800" height="400" fill="#f8fafc"/>
<!-- Title -->
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#1e293b">Monthly Solar Production Estimate (NREL PVWatts)</text>
<text x="400" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">16 × 250W panels = 4.0 kW DC | SITER, Pflugerville, TX</text>
<!-- Y-axis -->
<line x1="80" y1="70" x2="80" y2="320" stroke="#cbd5e1" stroke-width="1"/>
<text x="40" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b" transform="rotate(-90, 40, 200)">AC Output (kWh)</text>
<!-- Y-axis labels -->
<text x="70" y="324" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">0</text>
<text x="70" y="274" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">150</text>
<text x="70" y="224" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">300</text>
<text x="70" y="174" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">450</text>
<text x="70" y="124" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">600</text>
<text x="70" y="74" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">750</text>
<!-- Grid lines -->
<g stroke="#e2e8f0" stroke-width="1">
<line x1="80" y1="270" x2="760" y2="270"/>
<line x1="80" y1="220" x2="760" y2="220"/>
<line x1="80" y1="170" x2="760" y2="170"/>
<line x1="80" y1="120" x2="760" y2="120"/>
<line x1="80" y1="70" x2="760" y2="70"/>
</g>
<!-- X-axis -->
<line x1="80" y1="320" x2="760" y2="320" stroke="#cbd5e1" stroke-width="1"/>
<!-- Bars - Monthly data: Jan=450, Feb=453, Mar=492, Apr=508, May=534, Jun=514, Jul=555, Aug=571, Sep=514, Oct=516, Nov=469, Dec=429 -->
<!-- Scale: 600 kWh = 250px height, so each kWh = 0.417px -->
<!-- Jan: 450 kWh = 187.5px -->
<rect x="95" y="132.5" width="45" height="187.5" fill="url(#barGrad)" rx="3"/>
<text x="117.5" y="125" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">450</text>
<text x="117.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Jan</text>
<!-- Feb: 453 kWh = 188.75px -->
<rect x="150" y="131.25" width="45" height="188.75" fill="url(#barGrad)" rx="3"/>
<text x="172.5" y="124" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">453</text>
<text x="172.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Feb</text>
<!-- Mar: 492 kWh = 205px -->
<rect x="205" y="115" width="45" height="205" fill="url(#barGrad)" rx="3"/>
<text x="227.5" y="108" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">492</text>
<text x="227.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Mar</text>
<!-- Apr: 508 kWh = 211.67px -->
<rect x="260" y="108.33" width="45" height="211.67" fill="url(#barGrad)" rx="3"/>
<text x="282.5" y="101" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">508</text>
<text x="282.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Apr</text>
<!-- May: 534 kWh = 222.5px -->
<rect x="315" y="97.5" width="45" height="222.5" fill="url(#barGrad)" rx="3"/>
<text x="337.5" y="90" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">534</text>
<text x="337.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">May</text>
<!-- Jun: 514 kWh = 214.17px -->
<rect x="370" y="105.83" width="45" height="214.17" fill="url(#barGrad)" rx="3"/>
<text x="392.5" y="98" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">514</text>
<text x="392.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Jun</text>
<!-- Jul: 555 kWh = 231.25px -->
<rect x="425" y="88.75" width="45" height="231.25" fill="url(#barGrad)" rx="3"/>
<text x="447.5" y="81" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">555</text>
<text x="447.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Jul</text>
<!-- Aug: 571 kWh = 237.92px (highest) -->
<rect x="480" y="82.08" width="45" height="237.92" fill="url(#barGrad)" rx="3"/>
<text x="502.5" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b" font-weight="bold">571</text>
<text x="502.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Aug</text>
<!-- Sep: 514 kWh = 214.17px -->
<rect x="535" y="105.83" width="45" height="214.17" fill="url(#barGrad)" rx="3"/>
<text x="557.5" y="98" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">514</text>
<text x="557.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Sep</text>
<!-- Oct: 516 kWh = 215px -->
<rect x="590" y="105" width="45" height="215" fill="url(#barGrad)" rx="3"/>
<text x="612.5" y="98" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">516</text>
<text x="612.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Oct</text>
<!-- Nov: 469 kWh = 195.42px -->
<rect x="645" y="124.58" width="45" height="195.42" fill="url(#barGrad)" rx="3"/>
<text x="667.5" y="117" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">469</text>
<text x="667.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Nov</text>
<!-- Dec: 429 kWh = 178.75px (lowest) -->
<rect x="700" y="141.25" width="45" height="178.75" fill="url(#barGrad)" rx="3"/>
<text x="722.5" y="134" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#1e293b">429</text>
<text x="722.5" y="340" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Dec</text>
<!-- Average line -->
<line x1="80" y1="111.5" x2="760" y2="111.5" stroke="#ef4444" stroke-width="2" stroke-dasharray="8,4"/>
<text x="770" y="115" font-family="Arial, sans-serif" font-size="10" fill="#ef4444">500 kWh/mo avg</text>
<!-- Summary box -->
<rect x="600" y="355" width="180" height="35" rx="4" fill="#fef3c7"/>
<text x="690" y="372" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#92400e">Annual: 6,004 kWh</text>
<text x="690" y="385" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#a16207">Daily Avg: 16 kWh</text>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

125
images/image-3.svg Normal file
View File

@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400">
<defs>
<linearGradient id="paybackGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#ef4444;stop-opacity:1" />
<stop offset="100%" style="stop-color:#22c55e;stop-opacity:1" />
</linearGradient>
<linearGradient id="savingsGrad" 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>
<linearGradient id="costGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#ef4444;stop-opacity:1" />
<stop offset="100%" style="stop-color:#dc2626;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background -->
<rect width="800" height="400" fill="#f8fafc"/>
<!-- Title -->
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#1e293b">10-Year Payback Timeline</text>
<text x="400" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">16 × 250W System | $4,100 Investment | $402/yr Savings</text>
<!-- Y-axis -->
<line x1="100" y1="70" x2="100" y2="330" stroke="#cbd5e1" stroke-width="1"/>
<text x="35" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b" transform="rotate(-90, 35, 200)">Cumulative ($)</text>
<!-- Y-axis labels -->
<text x="90" y="334" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$4,500</text>
<text x="90" y="274" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$3,000</text>
<text x="90" y="214" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$1,500</text>
<text x="90" y="154" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">$0</text>
<text x="90" y="94" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">$1,500</text>
<!-- Zero line -->
<line x1="100" y1="154" x2="750" y2="154" stroke="#64748b" stroke-width="2" stroke-dasharray="5,5"/>
<text x="760" y="158" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Breakeven</text>
<!-- Grid lines -->
<g stroke="#e2e8f0" stroke-width="1">
<line x1="100" y1="274" x2="750" y2="274"/>
<line x1="100" y1="214" x2="750" y2="214"/>
<line x1="100" y1="94" x2="750" y2="94"/>
</g>
<!-- X-axis -->
<line x1="100" y1="330" x2="750" y2="330" stroke="#cbd5e1" stroke-width="1"/>
<text x="425" y="380" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b">Years</text>
<!-- X-axis labels -->
<text x="100" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">0</text>
<text x="159" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">1</text>
<text x="218" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">2</text>
<text x="277" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">3</text>
<text x="336" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">4</text>
<text x="395" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">5</text>
<text x="454" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">6</text>
<text x="513" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">7</text>
<text x="572" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">8</text>
<text x="631" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">9</text>
<text x="690" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">10</text>
<text x="749" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">11</text>
<!-- Area fill under the line (red zone) -->
<polygon points="100,154 100,334 749,159.5 749,154" fill="#fef2f2" opacity="0.7"/>
<!-- Area fill under the line (green zone after breakeven) -->
<polygon points="100,154 749,154 749,159.5" fill="#f0fdf4" opacity="0.7"/>
<!-- Net position line -->
<!-- Year 0: -$4,100 (y=154 + 4100*0.04 = 318) -->
<!-- Year 1: -$3,698 (y=154 + 3698*0.04 = 302) -->
<!-- Year 2: -$3,296 (y=154 + 3296*0.04 = 286) -->
<!-- Year 3: -$2,894 (y=154 + 2894*0.04 = 270) -->
<!-- Year 4: -$2,491 (y=154 + 2491*0.04 = 254) -->
<!-- Year 5: -$2,089 (y=154 + 2089*0.04 = 238) -->
<!-- Year 6: -$1,687 (y=154 + 1687*0.04 = 221) -->
<!-- Year 7: -$1,284 (y=154 + 1284*0.04 = 205) -->
<!-- Year 8: -$882 (y=154 + 882*0.04 = 189) -->
<!-- Year 9: -$480 (y=154 + 480*0.04 = 173) -->
<!-- Year 10: -$78 (y=154 + 78*0.04 = 157) -->
<!-- Year 11: +$325 (y=154 - 325*0.04 = 141) -->
<polyline
points="100,318 159,302 218,286 277,270 336,254 395,238 454,221 513,205 572,189 631,173 690,157 749,141"
fill="none"
stroke="url(#paybackGrad)"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"/>
<!-- Data points -->
<g fill="#1e293b">
<circle cx="100" cy="318" r="5"/>
<circle cx="159" cy="302" r="4"/>
<circle cx="218" cy="286" r="4"/>
<circle cx="277" cy="270" r="4"/>
<circle cx="336" cy="254" r="4"/>
<circle cx="395" cy="238" r="4"/>
<circle cx="454" cy="221" r="4"/>
<circle cx="513" cy="205" r="4"/>
<circle cx="572" cy="189" r="4"/>
<circle cx="631" cy="173" r="4"/>
<circle cx="690" cy="157" r="4"/>
<circle cx="749" cy="141" r="5" fill="#22c55e"/>
</g>
<!-- Key milestones labels -->
<text x="100" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#ef4444">-$4,100</text>
<text x="395" y="252" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">-$2,089</text>
<text x="690" y="171" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">-$78</text>
<text x="749" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#22c55e" font-weight="bold">+$325</text>
<!-- Breakeven marker -->
<line x1="715" y1="154" x2="715" y2="330" stroke="#22c55e" stroke-width="2" stroke-dasharray="4,4"/>
<text x="715" y="365" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#22c55e" font-weight="bold">~10.2 yrs</text>
<!-- Legend -->
<rect x="120" y="70" width="200" height="50" rx="4" fill="white" stroke="#e2e8f0"/>
<circle cx="140" cy="88" r="6" fill="#ef4444"/>
<text x="155" y="92" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Investment (Year 0)</text>
<circle cx="140" cy="108" r="6" fill="#22c55e"/>
<text x="155" y="112" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Profit (Year 11+)</text>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

145
images/image-4.svg Normal file
View File

@@ -0,0 +1,145 @@
<?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>

After

Width:  |  Height:  |  Size: 8.7 KiB

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

167
images/v2-image-1.svg Normal file
View File

@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 500">
<defs>
<linearGradient id="solarGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#f59e0b;stop-opacity:1" />
<stop offset="100%" style="stop-color:#d97706;stop-opacity:1" />
</linearGradient>
<linearGradient id="gridGrad" 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="batteryGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#10b981;stop-opacity:1" />
<stop offset="100%" style="stop-color:#059669;stop-opacity:1" />
</linearGradient>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="2" dy="2" stdDeviation="3" flood-opacity="0.2"/>
</filter>
</defs>
<!-- Background -->
<rect width="800" height="500" fill="#f8fafc"/>
<!-- Title -->
<text x="400" y="35" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="#1e293b">SITER Solar System Architecture</text>
<!-- Sun -->
<circle cx="100" cy="120" r="40" fill="#fbbf24"/>
<g stroke="#fbbf24" stroke-width="3">
<line x1="100" y1="60" x2="100" y2="45"/>
<line x1="100" y1="180" x2="100" y2="195"/>
<line x1="40" y1="120" x2="25" y2="120"/>
<line x1="160" y1="120" x2="175" y2="120"/>
<line x1="55" y1="75" x2="45" y2="65"/>
<line x1="145" y1="165" x2="155" y2="175"/>
<line x1="55" y1="165" x2="45" y2="175"/>
<line x1="145" y1="75" x2="155" y2="65"/>
</g>
<text x="100" y="215" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">Solar Radiation</text>
<text x="100" y="230" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">5.52 kWh/m²/day</text>
<!-- Solar Panels -->
<g filter="url(#shadow)">
<rect x="180" y="80" width="140" height="100" rx="8" fill="url(#solarGrad)"/>
<g fill="#92400e" opacity="0.3">
<rect x="190" y="90" width="25" height="35" rx="2"/>
<rect x="220" y="90" width="25" height="35" rx="2"/>
<rect x="250" y="90" width="25" height="35" rx="2"/>
<rect x="280" y="90" width="25" height="35" rx="2"/>
<rect x="190" y="130" width="25" height="35" rx="2"/>
<rect x="220" y="130" width="25" height="35" rx="2"/>
<rect x="250" y="130" width="25" height="35" rx="2"/>
<rect x="280" y="130" width="25" height="35" rx="2"/>
</g>
</g>
<text x="250" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">Solar Array</text>
<text x="250" y="218" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b">16 × 250W = 4.0 kW DC</text>
<text x="250" y="233" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">Ground Mount, 30° Tilt</text>
<!-- Arrow Sun to Panels -->
<path d="M145 120 L175 120" stroke="#f59e0b" stroke-width="3" marker-end="url(#arrowOrange)"/>
<defs>
<marker id="arrowOrange" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="#f59e0b"/>
</marker>
</defs>
<!-- Inverter -->
<g filter="url(#shadow)">
<rect x="370" y="100" width="100" height="60" rx="6" fill="#475569"/>
<rect x="380" y="110" width="30" height="40" rx="2" fill="#1e293b"/>
<rect x="415" y="110" width="45" height="15" rx="2" fill="#22c55e"/>
<rect x="415" y="130" width="45" height="8" rx="1" fill="#3b82f6"/>
<rect x="415" y="142" width="45" height="8" rx="1" fill="#eab308"/>
</g>
<text x="420" y="180" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e293b">Sol-Ark 5K</text>
<text x="420" y="195" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Hybrid Inverter</text>
<!-- Arrow Panels to Inverter -->
<path d="M320 130 L365 130" stroke="#f59e0b" stroke-width="2" marker-end="url(#arrowOrange)"/>
<text x="342" y="122" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">DC</text>
<!-- Home -->
<g filter="url(#shadow)">
<rect x="540" y="70" width="120" height="90" rx="8" fill="#e2e8f0"/>
<polygon points="540,70 600,30 660,70" fill="#64748b"/>
<rect x="580" y="110" width="40" height="50" fill="#3b82f6" opacity="0.3"/>
<rect x="555" y="100" width="25" height="20" fill="#1e293b" opacity="0.2"/>
<rect x="620" y="100" width="25" height="20" fill="#1e293b" opacity="0.2"/>
</g>
<text x="600" y="180" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" fill="#1e293b">SITER</text>
<text x="600" y="198" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">~1,885 kWh/month</text>
<!-- Arrow Inverter to Home -->
<path d="M470 130 L535 115" stroke="#22c55e" stroke-width="2" marker-end="url(#arrowGreen)"/>
<defs>
<marker id="arrowGreen" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="#22c55e"/>
</marker>
</defs>
<text x="502" y="115" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">AC</text>
<!-- Base Power Battery -->
<g filter="url(#shadow)">
<rect x="540" y="260" width="120" height="80" rx="8" fill="url(#batteryGrad)"/>
<rect x="590" y="250" width="20" height="10" rx="2" fill="#059669"/>
<rect x="555" y="280" width="90" height="15" rx="2" fill="#064e3b"/>
<rect x="555" y="280" width="70" height="15" rx="2" fill="#34d399"/>
<text x="600" y="318" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="white">78%</text>
</g>
<text x="600" y="360" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e293b">Base Power Battery</text>
<text x="600" y="378" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">+ Automatic Transfer Switch</text>
<!-- Arrow Home to Battery -->
<path d="M600 165 L600 255" stroke="#10b981" stroke-width="2" stroke-dasharray="5,3" marker-end="url(#arrowGreen)"/>
<text x="615" y="210" font-family="Arial, sans-serif" font-size="9" fill="#64748b">charge</text>
<!-- Grid -->
<g filter="url(#shadow)">
<rect x="370" y="260" width="100" height="80" rx="6" fill="url(#gridGrad)"/>
<g stroke="white" stroke-width="2" opacity="0.5">
<line x1="380" y1="280" x2="460" y2="280"/>
<line x1="380" y1="300" x2="460" y2="300"/>
<line x1="380" y1="320" x2="460" y2="320"/>
<line x1="400" y1="270" x2="400" y2="330"/>
<line x1="420" y1="270" x2="420" y2="330"/>
<line x1="440" y1="270" x2="440" y2="330"/>
</g>
</g>
<text x="420" y="360" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" font-weight="bold" fill="#1e293b">Oncor Grid</text>
<text x="420" y="378" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Utility Delivery</text>
<!-- Arrow Inverter to Grid -->
<path d="M420 160 L420 255" stroke="#3b82f6" stroke-width="2" marker-end="url(#arrowBlue)"/>
<defs>
<marker id="arrowBlue" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
<path d="M0,0 L0,6 L9,3 z" fill="#3b82f6"/>
</marker>
</defs>
<text x="435" y="210" font-family="Arial, sans-serif" font-size="9" fill="#64748b">export</text>
<text x="435" y="220" font-family="Arial, sans-serif" font-size="8" fill="#94a3b8">@ $0.04/kWh</text>
<!-- Grid to Battery -->
<path d="M470 300 L535 300" stroke="#3b82f6" stroke-width="2" stroke-dasharray="5,3" marker-end="url(#arrowBlue)"/>
<text x="502" y="292" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">backup</text>
<!-- Stats Box -->
<rect x="50" y="420" width="350" height="60" rx="6" fill="#f1f5f9"/>
<text x="225" y="440" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#475569">15-Month Billing Summary</text>
<text x="70" y="460" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Avg: 1,885 kWh/mo | $264.47/mo</text>
<text x="70" y="475" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Rate: $0.140/kWh effective</text>
<text x="280" y="460" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Summer peak: 2,912 kWh</text>
<text x="280" y="475" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Winter low: 1,333 kWh</text>
<!-- Legend -->
<rect x="420" y="420" width="330" height="60" rx="6" fill="#f1f5f9"/>
<text x="585" y="440" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" font-weight="bold" fill="#475569">Energy Flow</text>
<line x1="440" y1="460" x2="480" y2="460" stroke="#f59e0b" stroke-width="3"/>
<text x="490" y="464" font-family="Arial, sans-serif" font-size="10" fill="#64748b">DC Power</text>
<line x1="560" y1="460" x2="600" y2="460" stroke="#22c55e" stroke-width="3"/>
<text x="610" y="464" font-family="Arial, sans-serif" font-size="10" fill="#64748b">AC to Home</text>
<line x1="680" y1="460" x2="720" y2="460" stroke="#3b82f6" stroke-width="3"/>
<text x="730" y="464" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Grid</text>
</svg>

After

Width:  |  Height:  |  Size: 9.3 KiB

121
images/v2-image-2.svg Normal file
View File

@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450">
<defs>
<linearGradient id="barGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#f59e0b;stop-opacity:1" />
<stop offset="100%" style="stop-color:#d97706;stop-opacity:1" />
</linearGradient>
<linearGradient id="consumptionGrad" 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>
</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">Solar Production vs Monthly Consumption</text>
<text x="400" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">16 × 250W panels = 4.0 kW DC | SITER, Pflugerville, TX</text>
<!-- Y-axis -->
<line x1="80" y1="70" x2="80" y2="340" stroke="#cbd5e1" stroke-width="1"/>
<text x="40" y="205" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b" transform="rotate(-90, 40, 205)">kWh</text>
<!-- Y-axis labels -->
<text x="70" y="344" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">0</text>
<text x="70" y="289" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">750</text>
<text x="70" y="234" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">1,500</text>
<text x="70" y="179" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">2,250</text>
<text x="70" y="124" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">3,000</text>
<text x="70" y="74" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">3,750</text>
<!-- Grid lines -->
<g stroke="#e2e8f0" stroke-width="1">
<line x1="80" y1="284" x2="760" y2="284"/>
<line x1="80" y1="229" x2="760" y2="229"/>
<line x1="80" y1="174" x2="760" y2="174"/>
<line x1="80" y1="119" x2="760" y2="119"/>
<line x1="80" y1="74" x2="760" y2="74"/>
</g>
<!-- X-axis -->
<line x1="80" y1="340" x2="760" y2="340" stroke="#cbd5e1" stroke-width="1"/>
<!-- Monthly data: Consumption (blue) vs Solar (orange) -->
<!-- Scale: 3500 kWh = 270px height, so each kWh = 0.077px -->
<!-- Y offset: 340 - value * 0.077 -->
<!-- Jan: Consumption ~1333 (winter avg), Solar 450 -->
<rect x="95" y="237" width="22" height="103" fill="url(#consumptionGrad)" rx="2"/>
<rect x="120" y="305" width="22" height="35" fill="url(#barGrad)" rx="2"/>
<text x="117" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Jan</text>
<!-- Feb: Consumption ~1333, Solar 453 -->
<rect x="150" y="237" width="22" height="103" fill="url(#consumptionGrad)" rx="2"/>
<rect x="175" y="305" width="22" height="35" fill="url(#barGrad)" rx="2"/>
<text x="172" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Feb</text>
<!-- Mar: Consumption ~1777 (shoulder), Solar 492 -->
<rect x="205" y="203" width="22" height="137" fill="url(#consumptionGrad)" rx="2"/>
<rect x="230" y="302" width="22" height="38" fill="url(#barGrad)" rx="2"/>
<text x="227" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Mar</text>
<!-- Apr: Consumption ~1777, Solar 508 -->
<rect x="260" y="203" width="22" height="137" fill="url(#consumptionGrad)" rx="2"/>
<rect x="285" y="301" width="22" height="39" fill="url(#barGrad)" rx="2"/>
<text x="282" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Apr</text>
<!-- May: Consumption ~1777, Solar 534 -->
<rect x="315" y="203" width="22" height="137" fill="url(#consumptionGrad)" rx="2"/>
<rect x="340" y="299" width="22" height="41" fill="url(#barGrad)" rx="2"/>
<text x="337" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">May</text>
<!-- Jun: Consumption ~2912 (summer), Solar 514 -->
<rect x="370" y="116" width="22" height="224" fill="url(#consumptionGrad)" rx="2"/>
<rect x="395" y="300" width="22" height="40" fill="url(#barGrad)" rx="2"/>
<text x="392" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Jun</text>
<!-- Jul: Consumption ~2912, Solar 555 -->
<rect x="425" y="116" width="22" height="224" fill="url(#consumptionGrad)" rx="2"/>
<rect x="450" y="297" width="22" height="43" fill="url(#barGrad)" rx="2"/>
<text x="447" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Jul</text>
<!-- Aug: Consumption ~2912, Solar 571 (peak) -->
<rect x="480" y="116" width="22" height="224" fill="url(#consumptionGrad)" rx="2"/>
<rect x="505" y="296" width="22" height="44" fill="url(#barGrad)" rx="2"/>
<text x="502" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Aug</text>
<!-- Sep: Consumption ~2912, Solar 514 -->
<rect x="535" y="116" width="22" height="224" fill="url(#consumptionGrad)" rx="2"/>
<rect x="560" y="300" width="22" height="40" fill="url(#barGrad)" rx="2"/>
<text x="557" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Sep</text>
<!-- Oct: Consumption ~1777, Solar 516 -->
<rect x="590" y="203" width="22" height="137" fill="url(#consumptionGrad)" rx="2"/>
<rect x="615" y="300" width="22" height="40" fill="url(#barGrad)" rx="2"/>
<text x="612" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Oct</text>
<!-- Nov: Consumption ~1333, Solar 469 -->
<rect x="645" y="237" width="22" height="103" fill="url(#consumptionGrad)" rx="2"/>
<rect x="670" y="304" width="22" height="36" fill="url(#barGrad)" rx="2"/>
<text x="667" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Nov</text>
<!-- Dec: Consumption ~1333, Solar 429 -->
<rect x="700" y="237" width="22" height="103" fill="url(#consumptionGrad)" rx="2"/>
<rect x="725" y="307" width="22" height="33" fill="url(#barGrad)" rx="2"/>
<text x="722" y="355" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Dec</text>
<!-- Legend -->
<rect x="250" y="375" width="300" height="60" rx="6" fill="white" stroke="#e2e8f0"/>
<rect x="270" y="395" width="20" height="15" fill="url(#consumptionGrad)" rx="2"/>
<text x="300" y="407" font-family="Arial, sans-serif" font-size="11" fill="#64748b">Monthly Consumption (avg by season)</text>
<rect x="270" y="415" width="20" height="15" fill="url(#barGrad)" rx="2"/>
<text x="300" y="427" font-family="Arial, sans-serif" font-size="11" fill="#64748b">Solar Production (NREL estimate)</text>
<!-- Summary stats -->
<rect x="580" y="375" width="180" height="60" rx="4" fill="#fef3c7"/>
<text x="670" y="395" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#92400e">Annual Production: 6,004 kWh</text>
<text x="670" y="412" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#a16207">Annual Consumption: ~22,600 kWh</text>
<text x="670" y="427" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#92400e" font-weight="bold">Offset: 26.6%</text>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

131
images/v2-image-3.svg Normal file
View File

@@ -0,0 +1,131 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400">
<defs>
<linearGradient id="paybackGrad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#ef4444;stop-opacity:1" />
<stop offset="100%" style="stop-color:#22c55e;stop-opacity:1" />
</linearGradient>
<linearGradient id="savingsGrad" 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>
<linearGradient id="costGrad" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:#ef4444;stop-opacity:1" />
<stop offset="100%" style="stop-color:#dc2626;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background -->
<rect width="800" height="400" fill="#f8fafc"/>
<!-- Title -->
<text x="400" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="bold" fill="#1e293b">10-Year Payback Timeline</text>
<text x="400" y="50" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" fill="#64748b">16 × 250W System | $4,100 Investment | $402/yr Savings | $264/mo Avg Bill</text>
<!-- Y-axis -->
<line x1="100" y1="70" x2="100" y2="330" stroke="#cbd5e1" stroke-width="1"/>
<text x="35" y="200" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b" transform="rotate(-90, 35, 200)">Cumulative ($)</text>
<!-- Y-axis labels -->
<text x="90" y="334" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$4,500</text>
<text x="90" y="274" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$3,000</text>
<text x="90" y="214" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">-$1,500</text>
<text x="90" y="154" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">$0</text>
<text x="90" y="94" text-anchor="end" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">$1,500</text>
<!-- Zero line -->
<line x1="100" y1="154" x2="750" y2="154" stroke="#64748b" stroke-width="2" stroke-dasharray="5,5"/>
<text x="760" y="158" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Breakeven</text>
<!-- Grid lines -->
<g stroke="#e2e8f0" stroke-width="1">
<line x1="100" y1="274" x2="750" y2="274"/>
<line x1="100" y1="214" x2="750" y2="214"/>
<line x1="100" y1="94" x2="750" y2="94"/>
</g>
<!-- X-axis -->
<line x1="100" y1="330" x2="750" y2="330" stroke="#cbd5e1" stroke-width="1"/>
<text x="425" y="380" text-anchor="middle" font-family="Arial, sans-serif" font-size="11" fill="#64748b">Years</text>
<!-- X-axis labels -->
<text x="100" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">0</text>
<text x="159" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">1</text>
<text x="218" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">2</text>
<text x="277" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">3</text>
<text x="336" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">4</text>
<text x="395" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">5</text>
<text x="454" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">6</text>
<text x="513" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">7</text>
<text x="572" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">8</text>
<text x="631" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">9</text>
<text x="690" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">10</text>
<text x="749" y="350" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#94a3b8">11</text>
<!-- Area fill under the line (red zone) -->
<polygon points="100,154 100,334 749,159.5 749,154" fill="#fef2f2" opacity="0.7"/>
<!-- Area fill under the line (green zone after breakeven) -->
<polygon points="100,154 749,154 749,159.5" fill="#f0fdf4" opacity="0.7"/>
<!-- Net position line -->
<!-- Year 0: -$4,100 (y=154 + 4100*0.04 = 318) -->
<!-- Year 1: -$3,698 (y=154 + 3698*0.04 = 302) -->
<!-- Year 2: -$3,296 (y=154 + 3296*0.04 = 286) -->
<!-- Year 3: -$2,894 (y=154 + 2894*0.04 = 270) -->
<!-- Year 4: -$2,491 (y=154 + 2491*0.04 = 254) -->
<!-- Year 5: -$2,089 (y=154 + 2089*0.04 = 238) -->
<!-- Year 6: -$1,687 (y=154 + 1687*0.04 = 221) -->
<!-- Year 7: -$1,284 (y=154 + 1284*0.04 = 205) -->
<!-- Year 8: -$882 (y=154 + 882*0.04 = 189) -->
<!-- Year 9: -$480 (y=154 + 480*0.04 = 173) -->
<!-- Year 10: -$78 (y=154 + 78*0.04 = 157) -->
<!-- Year 11: +$325 (y=154 - 325*0.04 = 141) -->
<polyline
points="100,318 159,302 218,286 277,270 336,254 395,238 454,221 513,205 572,189 631,173 690,157 749,141"
fill="none"
stroke="url(#paybackGrad)"
stroke-width="3"
stroke-linecap="round"
stroke-linejoin="round"/>
<!-- Data points -->
<g fill="#1e293b">
<circle cx="100" cy="318" r="5"/>
<circle cx="159" cy="302" r="4"/>
<circle cx="218" cy="286" r="4"/>
<circle cx="277" cy="270" r="4"/>
<circle cx="336" cy="254" r="4"/>
<circle cx="395" cy="238" r="4"/>
<circle cx="454" cy="221" r="4"/>
<circle cx="513" cy="205" r="4"/>
<circle cx="572" cy="189" r="4"/>
<circle cx="631" cy="173" r="4"/>
<circle cx="690" cy="157" r="4"/>
<circle cx="749" cy="141" r="5" fill="#22c55e"/>
</g>
<!-- Key milestones labels -->
<text x="100" y="335" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#ef4444">-$4,100</text>
<text x="395" y="252" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">-$2,089</text>
<text x="690" y="171" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#64748b">-$78</text>
<text x="749" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="9" fill="#22c55e" font-weight="bold">+$325</text>
<!-- Breakeven marker -->
<line x1="715" y1="154" x2="715" y2="330" stroke="#22c55e" stroke-width="2" stroke-dasharray="4,4"/>
<text x="715" y="365" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#22c55e" font-weight="bold">~10.2 yrs</text>
<!-- Legend -->
<rect x="120" y="70" width="200" height="50" rx="4" fill="white" stroke="#e2e8f0"/>
<circle cx="140" cy="88" r="6" fill="#ef4444"/>
<text x="155" y="92" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Investment (Year 0)</text>
<circle cx="140" cy="108" r="6" fill="#22c55e"/>
<text x="155" y="112" font-family="Arial, sans-serif" font-size="10" fill="#64748b">Profit (Year 11+)</text>
<!-- Stats box -->
<rect x="400" y="70" width="200" height="50" rx="4" fill="#fef3c7"/>
<text x="500" y="88" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#92400e">Monthly Savings: $33.52</text>
<text x="500" y="103" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#92400e">Bill Offset: 12.7%</text>
<text x="500" y="118" text-anchor="middle" font-family="Arial, sans-serif" font-size="10" fill="#92400e">(of $264.47 avg bill)</text>
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

145
images/v2-image-4.svg Normal file
View File

@@ -0,0 +1,145 @@
<?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 (Updated)</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 | Avg Bill: $264.47/mo</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 (Updated)</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, 12.7% 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, 20.3% 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">~32 kW (80 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">~22,600 kWh/yr</text>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

View File

@@ -0,0 +1,9 @@
FROM python:3.11-slim
WORKDIR /app
RUN pip install --no-cache-dir requests
COPY solar_estimate.py /app/
ENTRYPOINT ["python", "solar_estimate.py"]

16
solar-analysis/run-analysis.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Run NREL solar analysis in Docker container
# Build once: docker build -t siter-solar-nrel .
# Run anytime: ./run-analysis.sh --scenarios
set -e
IMAGE_NAME="siter-solar-nrel"
if ! docker image inspect "$IMAGE_NAME" &>/dev/null; then
echo "Error: Docker image '$IMAGE_NAME' not found."
echo "Build it first: docker build -t $IMAGE_NAME ."
exit 1
fi
docker run --rm "$IMAGE_NAME" "$@"

View File

@@ -0,0 +1,674 @@
#!/usr/bin/env bash
# shellcheck source=/dev/null
#
# siter-solar-analysis - NREL PVWatts Solar Production Estimator
#
# Usage:
# ./siter-solar-analysis.sh # Basic analysis
# ./siter-solar-analysis.sh --scenarios # Compare system sizes
# ./siter-solar-analysis.sh --help # Show help
#
# Environment variables:
# NREL_API_KEY - Your NREL API key (default: DEMO_KEY)
# SITER_LAT - Site latitude (default: 30.44)
# SITER_LON - Site longitude (default: -97.62)
#
# Get your free API key at: https://developer.nrel.gov/signup/
#
set -euo pipefail
# Version
readonly VERSION="1.0.0"
# Default configuration
readonly DEFAULT_API_KEY="DEMO_KEY"
readonly DEFAULT_LAT="30.44"
readonly DEFAULT_LON="-97.62"
readonly DEFAULT_PANELS="16"
readonly DEFAULT_PANEL_WATTS="250"
readonly DEFAULT_TILT="30"
readonly DEFAULT_AZIMUTH="180"
readonly DEFAULT_LOSSES="14"
readonly DEFAULT_ARRAY_TYPE="0"
# Financial parameters
readonly PROJECT_COST="4100"
readonly CURRENT_MONTHLY_BILL="301.08"
readonly CURRENT_ANNUAL_CONSUMPTION="23952"
readonly CONSUMPTION_RATE="0.085"
readonly EXPORT_RATE="0.04"
readonly SELF_CONSUMPTION_PCT="0.60"
# API endpoint
readonly API_URL="https://developer.nrel.gov/api/pvwatts/v6.json"
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[0;33m'
readonly NC='\033[0m' # No Color
# Output control
VERBOSE="${VERBOSE:-false}"
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
#######################################
# Print error message and exit
# Arguments:
# $1 - Error message
# Outputs:
# Writes error to stderr
#######################################
error() {
echo -e "${RED}ERROR: ${1}${NC}" >&2
exit 1
}
#######################################
# Print warning message
# Arguments:
# $1 - Warning message
#######################################
warn() {
echo -e "${YELLOW}WARN: ${1}${NC}" >&2
}
#######################################
# Print info message
# Arguments:
# $1 - Info message
#######################################
info() {
if [[ "$VERBOSE" == "true" ]]; then
echo -e "${GREEN}INFO: ${1}${NC}" >&2
fi
}
#######################################
# Print usage information
#######################################
usage() {
cat << EOF
SITER Solar Analysis - NREL PVWatts Solar Production Estimator v${VERSION}
USAGE:
$(basename "$0") [OPTIONS]
OPTIONS:
-k, --api-key KEY NREL API key (or set NREL_API_KEY env var)
--lat VALUE Site latitude (default: ${DEFAULT_LAT})
--lon VALUE Site longitude (default: ${DEFAULT_LON})
-p, --panels NUM Number of panels (default: ${DEFAULT_PANELS})
-w, --watts WATTS Watts per panel (default: ${DEFAULT_PANEL_WATTS})
-t, --tilt DEGREES Array tilt in degrees (default: ${DEFAULT_TILT})
-a, --azimuth DEGREES Array azimuth (default: ${DEFAULT_AZIMUTH})
-l, --losses PERCENT System losses percent (default: ${DEFAULT_LOSSES})
--scenarios Run multiple system size scenarios
--json Output as JSON
--verbose Enable verbose output
-h, --help Show this help message
-v, --version Show version
EXAMPLES:
# Basic analysis with defaults
$(basename "$0")
# With your API key
$(basename "$0") -k YOUR_API_KEY
# Compare different system sizes
$(basename "$0") --scenarios
# Custom configuration
$(basename "$0") -p 20 -w 400 --lat 30.5 --lon -97.7
ENVIRONMENT VARIABLES:
NREL_API_KEY Your NREL API key
SITER_LAT Site latitude
SITER_LON Site longitude
Get your free API key at: https://developer.nrel.gov/signup/
EOF
}
#######################################
# Check required dependencies
# Globals:
# None
# Arguments:
# None
# Outputs:
# Error if dependencies missing
#######################################
check_dependencies() {
local missing=()
command -v curl >/dev/null 2>&1 || missing+=("curl")
command -v jq >/dev/null 2>&1 || missing+=("jq")
command -v bc >/dev/null 2>&1 || missing+=("bc")
if [[ ${#missing[@]} -gt 0 ]]; then
error "Missing required dependencies: ${missing[*]}\nInstall with: apt-get install ${missing[*]}"
fi
}
#######################################
# Validate numeric value
# Arguments:
# $1 - Value to validate
# $2 - Parameter name for error message
# Returns:
# 0 if valid, 1 if invalid
#######################################
validate_numeric() {
local value="$1"
local name="$2"
if ! [[ "$value" =~ ^-?[0-9]+\.?[0-9]*$ ]]; then
error "Invalid ${name}: '${value}' must be a number"
fi
}
#######################################
# Validate range
# Arguments:
# $1 - Value to validate
# $2 - Minimum
# $3 - Maximum
# $4 - Parameter name
# Returns:
# 0 if valid, exits with error if invalid
#######################################
validate_range() {
local value="$1"
local min="$2"
local max="$3"
local name="$4"
validate_numeric "$value" "$name"
if (( $(echo "$value < $min" | bc -l) )) || (( $(echo "$value > $max" | bc -l) )); then
error "Invalid ${name}: ${value} (must be between ${min} and ${max})"
fi
}
#######################################
# Query NREL PVWatts API
# Globals:
# API_URL
# Arguments:
# $1 - API key
# $2 - System capacity (kW)
# $3 - Latitude
# $4 - Longitude
# $5 - Tilt
# $6 - Azimuth
# $7 - Losses
# Outputs:
# JSON response from API
#######################################
query_nrel_api() {
local api_key="$1"
local capacity="$2"
local lat="$3"
local lon="$4"
local tilt="$5"
local azimuth="$6"
local losses="$7"
local array_type="${8:-$DEFAULT_ARRAY_TYPE}"
local url="${API_URL}?api_key=${api_key}&lat=${lat}&lon=${lon}"
url+="&system_capacity=${capacity}&array_type=${array_type}"
url+="&tilt=${tilt}&azimuth=${azimuth}&module_type=0"
url+="&losses=${losses}&timeframe=monthly"
info "Querying NREL API: capacity=${capacity}kW"
local response
local http_code
# shellcheck disable=SC2086
response=$(curl -s -w "\n%{http_code}" ${url} 2>/dev/null)
http_code=$(echo "$response" | tail -n1)
response=$(echo "$response" | sed '$d')
if [[ "$http_code" != "200" ]]; then
error "API request failed with HTTP ${http_code}: ${response}"
fi
# Check for API errors
local api_errors
api_errors=$(echo "$response" | jq -r '.errors // [] | if length > 0 then .[] else empty end')
if [[ -n "$api_errors" ]]; then
error "API returned errors: ${api_errors}"
fi
echo "$response"
}
#######################################
# Calculate financial projections
# Arguments:
# $1 - Annual production (kWh)
# Outputs:
# Financial metrics
#######################################
calculate_financials() {
local annual_kwh="$1"
local self_consumed
local exported
local self_consumption_value
local export_value
local total_annual_value
local monthly_savings
local offset_pct
local payback_months
local payback_years
self_consumed=$(echo "$annual_kwh * $SELF_CONSUMPTION_PCT" | bc -l)
exported=$(echo "$annual_kwh * (1 - $SELF_CONSUMPTION_PCT)" | bc -l)
self_consumption_value=$(echo "$self_consumed * $CONSUMPTION_RATE" | bc -l)
export_value=$(echo "$exported * $EXPORT_RATE" | bc -l)
total_annual_value=$(echo "$self_consumption_value + $export_value" | bc -l)
monthly_savings=$(echo "$total_annual_value / 12" | bc -l)
offset_pct=$(echo "$monthly_savings / $CURRENT_MONTHLY_BILL * 100" | bc -l)
if (( $(echo "$monthly_savings > 0" | bc -l) )); then
payback_months=$(echo "$PROJECT_COST / $monthly_savings" | bc -l)
else
payback_months="999999"
fi
payback_years=$(echo "$payback_months / 12" | bc -l)
cat << EOF
{
"annual_kwh": $(printf "%.0f" "$annual_kwh"),
"monthly_avg_kwh": $(printf "%.0f" "$(echo "$annual_kwh / 12" | bc -l)"),
"self_consumed_kwh": $(printf "%.0f" "$self_consumed"),
"exported_kwh": $(printf "%.0f" "$exported"),
"self_consumption_value": $(printf "%.2f" "$self_consumption_value"),
"export_value": $(printf "%.2f" "$export_value"),
"total_annual_value": $(printf "%.2f" "$total_annual_value"),
"monthly_savings": $(printf "%.2f" "$monthly_savings"),
"offset_pct": $(printf "%.1f" "$offset_pct"),
"payback_months": $(printf "%.1f" "$payback_months"),
"payback_years": $(printf "%.1f" "$payback_years")
}
EOF
}
#######################################
# Format and display analysis results
# Arguments:
# $1 - API response JSON
# $2 - System capacity (kW)
# $3 - Panel watts
# $4 - Number of panels
# $5 - Latitude
# $6 - Longitude
# Outputs:
# Formatted report
#######################################
format_output() {
local data="$1"
local capacity="$2"
local panel_watts="$3"
local num_panels="$4"
local lat="$5"
local lon="$6"
local annual_kwh
local capacity_factor
local solrad_annual
local station_city
local station_state
annual_kwh=$(echo "$data" | jq -r '.outputs.ac_annual')
capacity_factor=$(echo "$data" | jq -r '.outputs.capacity_factor')
solrad_annual=$(echo "$data" | jq -r '.outputs.solrad_annual')
station_city=$(echo "$data" | jq -r '.station_info.city // "N/A"')
station_state=$(echo "$data" | jq -r '.station_info.state // "Texas"')
local financials
financials=$(calculate_financials "$annual_kwh")
if [[ "$OUTPUT_FORMAT" == "json" ]]; then
echo "$data" | jq --argjson fin "$financials" \
--arg capacity "$capacity" \
--arg panel_watts "$panel_watts" \
--arg num_panels "$num_panels" \
'{
system: {
capacity_kw: ($capacity | tonumber),
panels: ($num_panels | tonumber),
panel_watts: ($panel_watts | tonumber)
},
production: {
annual_kwh: .outputs.ac_annual,
monthly_kwh: .outputs.ac_monthly,
capacity_factor: .outputs.capacity_factor,
solrad_annual: .outputs.solrad_annual
},
financials: $fin
}'
return
fi
# Text output
echo "======================================================================"
echo "SITER SOLAR PROJECT - NREL PVWATTS ANALYSIS"
echo "======================================================================"
echo ""
echo "Location: SITER"
echo " Coordinates: ${lat}, ${lon}"
echo " Station: ${station_city}, ${station_state}"
echo ""
echo "System Configuration:"
echo " Panels: ${num_panels} × ${panel_watts}W = ${capacity} kW DC"
echo " Array Type: Fixed Open Rack (Ground Mount)"
echo " Orientation: ${DEFAULT_TILT}° tilt, ${DEFAULT_AZIMUTH}° azimuth (South-facing)"
echo " System Losses: ${DEFAULT_LOSSES}%"
echo ""
echo "======================================================================"
echo "MONTHLY PRODUCTION ESTIMATE (NREL PVWatts)"
echo "======================================================================"
local months=("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")
local ac_monthly
ac_monthly=$(echo "$data" | jq -r '.outputs.ac_monthly[]')
local solrad_monthly
solrad_monthly=$(echo "$data" | jq -r '.outputs.solrad_monthly[]')
printf "\n%-6s %-14s %-12s %-15s\n" "Month" "AC Output" "Daily Avg" "Solar Rad"
printf "%-6s %-14s %-12s %-15s\n" "" "(kWh)" "(kWh/day)" "(kWh/m²/day)"
echo "--------------------------------------------------"
local i=0
local total_kwh=0
for month in "${months[@]}"; do
local ac
ac=$(echo "$ac_monthly" | sed -n "$((i+1))p")
local solrad
solrad=$(echo "$solrad_monthly" | sed -n "$((i+1))p")
local daily_avg
daily_avg=$(echo "$ac / 30.5" | bc -l)
printf "%-6s %10.0f %10.0f %14.2f\n" "$month" "$ac" "$daily_avg" "$solrad"
total_kwh=$(echo "$total_kwh + $ac" | bc -l)
((i++))
done
echo "--------------------------------------------------"
printf "%-6s %10.0f %10.0f\n" "ANNUAL" "$annual_kwh" "$(echo "$annual_kwh / 12" | bc -l)"
echo ""
echo "======================================================================"
echo "ANNUAL PERFORMANCE SUMMARY"
echo "======================================================================"
printf " Annual AC Output: %'.0f kWh\n" "$annual_kwh"
printf " Monthly Average: %'.0f kWh\n" "$(echo "$annual_kwh / 12" | bc -l)"
printf " Daily Average: %'.0f kWh\n" "$(echo "$annual_kwh / 365" | bc -l)"
echo " Capacity Factor: ${capacity_factor}%"
echo " Avg Solar Radiation: ${solrad_annual} kWh/m²/day"
echo ""
echo "======================================================================"
echo "FINANCIAL ANALYSIS"
echo "======================================================================"
local self_consumed_kwh
local exported_kwh
local total_annual_value
local monthly_savings
local offset_pct
local payback_years
self_consumed_kwh=$(echo "$financials" | jq -r '.self_consumed_kwh')
exported_kwh=$(echo "$financials" | jq -r '.exported_kwh')
total_annual_value=$(echo "$financials" | jq -r '.total_annual_value')
monthly_savings=$(echo "$financials" | jq -r '.monthly_savings')
offset_pct=$(echo "$financials" | jq -r '.offset_pct')
payback_years=$(echo "$financials" | jq -r '.payback_years')
echo ""
echo " Current Consumption: ${CURRENT_ANNUAL_CONSUMPTION} kWh/year"
printf " Solar Production: %'.0f kWh/year\n" "$annual_kwh"
printf " Self-Sufficiency: %.1f%%\n" "$(echo "$annual_kwh / $CURRENT_ANNUAL_CONSUMPTION * 100" | bc -l)"
echo ""
printf " Self-Consumed (60%%): %'.0f kWh\n" "$self_consumed_kwh"
printf " Value @ \$%s/kWh: \$%'.2f/year\n" "$CONSUMPTION_RATE" "$(echo "$financials" | jq -r '.self_consumption_value')"
echo ""
printf " Exported to Grid (40%%): %'.0f kWh\n" "$exported_kwh"
printf " Value @ \$%s/kWh: \$%'.2f/year\n" "$EXPORT_RATE" "$(echo "$financials" | jq -r '.export_value')"
echo ""
printf " TOTAL ANNUAL VALUE: \$%'.2f\n" "$total_annual_value"
printf " MONTHLY SAVINGS: \$%'.2f\n" "$monthly_savings"
echo ""
echo "======================================================================"
echo "ROI ANALYSIS"
echo "======================================================================"
echo ""
printf " Project Cost: \$%'.2f\n" "$PROJECT_COST"
printf " Current Monthly Bill: \$%'.2f\n" "$CURRENT_MONTHLY_BILL"
printf " Projected Monthly Savings: \$%'.2f\n" "$monthly_savings"
printf " Bill Offset: %.1f%%\n" "$offset_pct"
printf "\n PAYBACK PERIOD: %.0f months (%.1f years)\n" "$(echo "$financials" | jq -r '.payback_months')" "$payback_years"
echo ""
echo "======================================================================"
}
#######################################
# Run multiple system size scenarios
# Arguments:
# $1 - API key
# $2 - Latitude
# $3 - Longitude
# $4 - Number of panels
# Outputs:
# Comparison table
#######################################
run_scenarios() {
local api_key="$1"
local lat="$2"
local lon="$3"
local num_panels="$4"
local scenarios=(
"16:250:16 × 250W (older panels)"
"16:300:16 × 300W"
"16:350:16 × 350W"
"16:400:16 × 400W (modern standard)"
"16:450:16 × 450W (high efficiency)"
"20:400:20 × 400W (expanded)"
"24:400:24 × 400W (full offset target)"
)
echo "======================================================================"
echo "SITER SOLAR - SYSTEM SIZE SCENARIOS"
echo "======================================================================"
echo ""
echo "Location: SITER (${lat}, ${lon})"
echo "Current Annual Consumption: ${CURRENT_ANNUAL_CONSUMPTION} kWh"
printf "Current Monthly Bill: \$%'.2f\n" "$CURRENT_MONTHLY_BILL"
echo ""
if [[ "$OUTPUT_FORMAT" == "json" ]]; then
echo "["
local first=true
else
printf "%-25s %6s %8s %8s %8s %10s\n" "System" "kW" "kWh/yr" "\$/mo" "Offset" "Payback"
echo "----------------------------------------------------------------------"
fi
for scenario in "${scenarios[@]}"; do
IFS=':' read -r panels watts desc <<< "$scenario"
local capacity
capacity=$(echo "$panels * $watts / 1000" | bc -l)
local data
data=$(query_nrel_api "$api_key" "$capacity" "$lat" "$lon" "$DEFAULT_TILT" "$DEFAULT_AZIMUTH" "$DEFAULT_LOSSES")
local annual_kwh
annual_kwh=$(echo "$data" | jq -r '.outputs.ac_annual')
local financials
financials=$(calculate_financials "$annual_kwh")
local monthly_savings
local offset_pct
local payback_years
monthly_savings=$(echo "$financials" | jq -r '.monthly_savings')
offset_pct=$(echo "$financials" | jq -r '.offset_pct')
payback_years=$(echo "$financials" | jq -r '.payback_years')
if [[ "$OUTPUT_FORMAT" == "json" ]]; then
if [[ "$first" != "true" ]]; then
echo ","
fi
first=false
echo "$financials" | jq --arg desc "$desc" \
--arg capacity "$capacity" \
--arg panels "$panels" \
--arg watts "$watts" \
'{
description: $desc,
panels: ($panels | tonumber),
watts_per_panel: ($watts | tonumber),
capacity_kw: ($capacity | tonumber)
} + .'
else
printf "%-25s %6.1f %8,.0f \$%6.2f %6.1f%% %8.1f yrs\n" \
"$desc" "$capacity" "$annual_kwh" "$monthly_savings" "$offset_pct" "$payback_years"
fi
done
if [[ "$OUTPUT_FORMAT" == "json" ]]; then
echo "]"
else
echo "----------------------------------------------------------------------"
echo ""
echo "Note: To achieve 100% bill offset would require ~32 kW system"
fi
}
#######################################
# Main entry point
# Globals:
# All configuration variables
# Arguments:
# Command line arguments
# Outputs:
# Analysis results
#######################################
main() {
local api_key="${NREL_API_KEY:-$DEFAULT_API_KEY}"
local lat="${SITER_LAT:-$DEFAULT_LAT}"
local lon="${SITER_LON:-$DEFAULT_LON}"
local num_panels="$DEFAULT_PANELS"
local panel_watts="$DEFAULT_PANEL_WATTS"
local tilt="$DEFAULT_TILT"
local azimuth="$DEFAULT_AZIMUTH"
local losses="$DEFAULT_LOSSES"
local scenarios_mode=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-k|--api-key)
api_key="$2"
shift 2
;;
--lat)
lat="$2"
shift 2
;;
--lon)
lon="$2"
shift 2
;;
-p|--panels)
num_panels="$2"
shift 2
;;
-w|--watts)
panel_watts="$2"
shift 2
;;
-t|--tilt)
tilt="$2"
shift 2
;;
-a|--azimuth)
azimuth="$2"
shift 2
;;
-l|--losses)
losses="$2"
shift 2
;;
--scenarios)
scenarios_mode=true
shift
;;
--json)
OUTPUT_FORMAT="json"
shift
;;
--verbose)
VERBOSE="true"
shift
;;
-h|--help)
usage
exit 0
;;
-v|--version)
echo "siter-solar-analysis version ${VERSION}"
exit 0
;;
-*)
error "Unknown option: $1\nTry --help for usage information."
;;
*)
# Positional argument - treat as API key
api_key="$1"
shift
;;
esac
done
# Check dependencies
check_dependencies
# Validate inputs
validate_numeric "$lat" "latitude"
validate_numeric "$lon" "longitude"
validate_numeric "$num_panels" "panels"
validate_numeric "$panel_watts" "panel watts"
validate_range "$lat" "-90" "90" "latitude"
validate_range "$lon" "-180" "180" "longitude"
# Calculate system capacity
local capacity
capacity=$(echo "$num_panels * $panel_watts / 1000" | bc -l)
if [[ "$scenarios_mode" == "true" ]]; then
run_scenarios "$api_key" "$lat" "$lon" "$num_panels"
else
info "Querying NREL PVWatts API (system: ${capacity} kW)..."
local data
data=$(query_nrel_api "$api_key" "$capacity" "$lat" "$lon" "$tilt" "$azimuth" "$losses")
format_output "$data" "$capacity" "$panel_watts" "$num_panels" "$lat" "$lon"
fi
}
# Run main if not being sourced
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi

View File

@@ -0,0 +1,299 @@
#!/usr/bin/env python3
# DEPRECATED: This script is deprecated. Use siter-solar-analysis.sh instead.
# This file is kept for reference only.
"""
NREL PVWatts Solar Production Estimator for SITER Solar Project
Location: SITER
Usage:
python solar_estimate.py # Use defaults (16 panels @ 400W = 6.4kW)
python solar_estimate.py YOUR_API_KEY # With your NREL API key
python solar_estimate.py --scenarios # Run multiple system size scenarios
"""
import os
import requests
import json
import sys
from datetime import datetime
# Location coordinates for SITER
LAT = float(os.environ.get("SITER_LAT", "30.44")) # Central Texas
LON = float(os.environ.get("SITER_LON", "-97.62")) # Central Texas
# Default system parameters
NUM_PANELS = 16
DEFAULT_PANEL_WATTS = 400 # Modern panels are typically 350-450W
ARRAY_TYPE = 0 # 0 = Fixed - Open Rack (ground mount)
TILT = 30 # degrees - optimal for Texas latitude (~30°N)
AZIMUTH = 180 # South-facing
MODULE_TYPE = 0 # 0 = Standard
LOSSES = 14 # System losses percentage (typical)
# Financial parameters
PROJECT_COST = 4100
CURRENT_MONTHLY_BILL = 301.08
CURRENT_ANNUAL_CONSUMPTION = 23952 # kWh/year (from actual bills: ~1996 kWh/month avg)
BASE_POWER_CONSUMPTION_RATE = 0.085 # $/kWh avoided
BASE_POWER_EXPORT_RATE = 0.04 # $/kWh buyback
# API endpoint
API_URL = "https://developer.nrel.gov/api/pvwatts/v6.json"
def get_solar_estimate(api_key, system_capacity_kw):
"""Query NREL PVWatts API for solar production estimate."""
params = {
"api_key": api_key,
"lat": LAT,
"lon": LON,
"system_capacity": system_capacity_kw,
"array_type": ARRAY_TYPE,
"tilt": TILT,
"azimuth": AZIMUTH,
"module_type": MODULE_TYPE,
"losses": LOSSES,
"timeframe": "monthly"
}
try:
response = requests.get(API_URL, params=params, timeout=30)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"API request failed: {e}")
return None
def calculate_financials(annual_kwh):
"""Calculate financial projections based on annual production."""
monthly_avg = annual_kwh / 12
# Estimate self-consumption vs export
# During peak solar hours, home may not consume all production
# Conservative estimate: 60% self-consumed, 40% exported
self_consumed_pct = 0.60
self_consumed = annual_kwh * self_consumed_pct
exported = annual_kwh * (1 - self_consumed_pct)
self_consumption_value = self_consumed * BASE_POWER_CONSUMPTION_RATE
export_value = exported * BASE_POWER_EXPORT_RATE
total_annual_value = self_consumption_value + export_value
monthly_savings = total_annual_value / 12
offset_pct = (monthly_savings / CURRENT_MONTHLY_BILL) * 100
payback_months = PROJECT_COST / monthly_savings if monthly_savings > 0 else float('inf')
return {
"annual_kwh": annual_kwh,
"monthly_avg_kwh": monthly_avg,
"self_consumed_kwh": self_consumed,
"exported_kwh": exported,
"self_consumption_value": self_consumption_value,
"export_value": export_value,
"total_annual_value": total_annual_value,
"monthly_savings": monthly_savings,
"offset_pct": offset_pct,
"payback_months": payback_months,
"payback_years": payback_months / 12
}
def format_output(data, system_capacity_kw, panel_watts):
"""Format API response into readable report."""
if not data or data.get("errors"):
print("Error:", data.get("errors", "Unknown error"))
return None
outputs = data.get("outputs", {})
station = data.get("station_info", {})
annual_kwh = outputs.get("ac_annual", 0)
fin = calculate_financials(annual_kwh)
# Self-consumption percentage used in calculations
self_consumed_pct = 0.60
print("=" * 70)
print("SITER SOLAR PROJECT - NREL PVWATTS ANALYSIS")
print("=" * 70)
print(f"\nLocation: SITER")
print(f" Coordinates: {LAT}, {LON}")
print(f" Station: {station.get('city', 'N/A')}, {station.get('state', 'Texas')}")
print(f" Distance: {station.get('distance', 'N/A')}m | Elevation: {station.get('elev', 'N/A')}m")
print(f"\nSystem Configuration:")
print(f" Panels: {NUM_PANELS} × {panel_watts}W = {system_capacity_kw} kW DC")
print(f" Array Type: Fixed Open Rack (Ground Mount)")
print(f" Orientation: {TILT}° tilt, {AZIMUTH}° azimuth (South-facing)")
print(f" System Losses: {LOSSES}%")
print(f"\n{'='*70}")
print("MONTHLY PRODUCTION ESTIMATE (NREL PVWatts)")
print(f"{'='*70}")
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
ac_monthly = outputs.get("ac_monthly", [])
solrad_monthly = outputs.get("solrad_monthly", [])
print(f"\n{'Month':<6} {'AC Output':<14} {'Daily Avg':<12} {'Solar Rad':<15}")
print(f"{'':6} {'(kWh)':<14} {'(kWh/day)':<12} {'(kWh/m²/day)':<15}")
print("-" * 50)
for i, month in enumerate(months):
ac = ac_monthly[i] if i < len(ac_monthly) else 0
daily_avg = ac / 30.5 if ac else 0
solrad = solrad_monthly[i] if i < len(solrad_monthly) else 0
print(f"{month:<6} {ac:>10.0f} {daily_avg:>10.0f} {solrad:>14.2f}")
print("-" * 50)
print(f"{'ANNUAL':<6} {fin['annual_kwh']:>10.0f} {fin['monthly_avg_kwh']:>10.0f}")
print(f"\n{'='*70}")
print("ANNUAL PERFORMANCE SUMMARY")
print(f"{'='*70}")
print(f" Annual AC Output: {fin['annual_kwh']:,.0f} kWh")
print(f" Monthly Average: {fin['monthly_avg_kwh']:,.0f} kWh")
print(f" Daily Average: {fin['annual_kwh']/365:,.0f} kWh")
print(f" Capacity Factor: {outputs.get('capacity_factor', 'N/A')}%")
print(f" Avg Solar Radiation: {outputs.get('solrad_annual', 'N/A')} kWh/m²/day")
print(f"\n{'='*70}")
print("FINANCIAL ANALYSIS")
print(f"{'='*70}")
print(f"\n Current Consumption: {CURRENT_ANNUAL_CONSUMPTION:,} kWh/year")
print(f" Solar Production: {fin['annual_kwh']:,.0f} kWh/year")
print(f" Self-Sufficiency: {(fin['annual_kwh']/CURRENT_ANNUAL_CONSUMPTION)*100:.1f}%")
print(f"\n Self-Consumed ({self_consumed_pct*100:.0f}%): {fin['self_consumed_kwh']:,.0f} kWh")
print(f" Value @ ${BASE_POWER_CONSUMPTION_RATE}/kWh: ${fin['self_consumption_value']:,.2f}/year")
print(f"\n Exported to Grid ({(1-self_consumed_pct)*100:.0f}%): {fin['exported_kwh']:,.0f} kWh")
print(f" Value @ ${BASE_POWER_EXPORT_RATE}/kWh: ${fin['export_value']:,.2f}/year")
print(f"\n TOTAL ANNUAL VALUE: ${fin['total_annual_value']:,.2f}")
print(f" MONTHLY SAVINGS: ${fin['monthly_savings']:,.2f}")
print(f"\n{'='*70}")
print("ROI ANALYSIS")
print(f"{'='*70}")
print(f"\n Project Cost: ${PROJECT_COST:,.2f}")
print(f" Current Monthly Bill: ${CURRENT_MONTHLY_BILL:,.2f}")
print(f" Projected Monthly Savings: ${fin['monthly_savings']:,.2f}")
print(f" Bill Offset: {fin['offset_pct']:.1f}%")
print(f"\n PAYBACK PERIOD: {fin['payback_months']:.1f} months ({fin['payback_years']:.1f} years)")
# 5-year projection
print(f"\n{'='*70}")
print("5-YEAR FINANCIAL PROJECTION")
print(f"{'='*70}")
print(f"\n {'Year':<6} {'Cumulative Savings':<20} {'Net Position':<15}")
print(f" {'-'*40}")
for year in range(6):
cum_savings = fin['total_annual_value'] * year
net = cum_savings - PROJECT_COST
print(f" {year:<6} ${cum_savings:>17,.2f} ${net:>12,.2f}")
print(f"\n{'='*70}")
return fin
def run_scenarios(api_key):
"""Run analysis for multiple system configurations."""
print("=" * 70)
print("SITER SOLAR - SYSTEM SIZE SCENARIOS")
print("=" * 70)
print(f"\nLocation: SITER")
print(f"Current Annual Consumption: {CURRENT_ANNUAL_CONSUMPTION:,} kWh")
print(f"Current Monthly Bill: ${CURRENT_MONTHLY_BILL:,.2f}")
print()
scenarios = [
# (panels, watts_per_panel, description)
(16, 250, "16 × 250W (older panels)"),
(16, 300, "16 × 300W"),
(16, 350, "16 × 350W"),
(16, 400, "16 × 400W (modern standard)"),
(16, 450, "16 × 450W (high efficiency)"),
(20, 400, "20 × 400W (expanded)"),
(24, 400, "24 × 400W (full offset target)"),
]
results = []
print(f"{'System':<25} {'kW':>6} {'kWh/yr':>8} {'$/mo':>8} {'Offset':>8} {'Payback':>10}")
print("-" * 70)
for panels, watts, desc in scenarios:
capacity_kw = (panels * watts) / 1000
data = get_solar_estimate(api_key, capacity_kw)
if data and not data.get("errors"):
annual_kwh = data.get("outputs", {}).get("ac_annual", 0)
fin = calculate_financials(annual_kwh)
results.append({
"description": desc,
"panels": panels,
"watts": watts,
"capacity_kw": capacity_kw,
**fin
})
print(f"{desc:<25} {capacity_kw:>6.1f} {fin['annual_kwh']:>8,.0f} "
f"${fin['monthly_savings']:>6.2f} {fin['offset_pct']:>6.1f}% "
f"{fin['payback_years']:>8.1f} yrs")
print("-" * 70)
# Find system size for ~100% offset
target_monthly_savings = CURRENT_MONTHLY_BILL
target_annual_value = target_monthly_savings * 12
# Back-calculate required production
# annual_value = (production * 0.60 * 0.085) + (production * 0.40 * 0.04)
# annual_value = production * (0.051 + 0.016) = production * 0.067
required_production = target_annual_value / 0.067 # ~$4,016 annual value needed
required_kw = required_production / 1500 # Rough: 1kW produces ~1500 kWh/year in Texas
print(f"\nTo achieve 100% bill offset, estimated system size needed:")
print(f" ~{required_kw:.0f} kW ({int(required_kw/0.4)} panels @ 400W each)")
return results
def main():
global NUM_PANELS
api_key = os.environ.get("NREL_API_KEY", "DEMO_KEY")
run_scenarios_mode = False
panel_watts = DEFAULT_PANEL_WATTS
for arg in sys.argv[1:]:
if arg == "--scenarios":
run_scenarios_mode = True
elif arg.startswith("--panels="):
NUM_PANELS = int(arg.split("=")[1])
elif arg.startswith("--watts="):
panel_watts = int(arg.split("=")[1])
elif not arg.startswith("--"):
api_key = arg
system_capacity_kw = (NUM_PANELS * panel_watts) / 1000
if run_scenarios_mode:
run_scenarios(api_key)
else:
print(f"Querying NREL PVWatts API (system: {system_capacity_kw} kW)...")
data = get_solar_estimate(api_key, system_capacity_kw)
if data:
result = format_output(data, system_capacity_kw, panel_watts)
# Save JSON for further analysis
with open("nrel_solar_data.json", "w") as f:
json.dump(data, f, indent=2)
print(f"\nRaw data saved to nrel_solar_data.json")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
# DEPRECATED: This script is deprecated. Use siter-solar-analysis.sh instead.
# This file is kept for reference only.
"""Find optimal tilt/azimuth for ground mount at SITER location"""
import requests
import time
LAT = float(os.environ.get("SITER_LAT", "30.44")) # Central Texas
import os
LON = float(os.environ.get("SITER_LON", "-97.62")) # Central Texas
API_URL = "https://developer.nrel.gov/api/pvwatts/v6.json"
def query(capacity, tilt, azimuth, losses=8):
params = {
"api_key": os.environ.get("NREL_API_KEY", "DEMO_KEY"),
"lat": LAT, "lon": LON,
"system_capacity": capacity,
"array_type": 0, # Ground mount
"tilt": tilt, "azimuth": azimuth,
"module_type": 0,
"losses": losses,
}
try:
r = requests.get(API_URL, params=params, timeout=30)
data = r.json()
return data.get("outputs", {}).get("ac_annual", 0)
except:
return 0
print("=" * 70)
print("OPTIMAL ORIENTATION ANALYSIS - SITER Solar (Ground Mount)")
print("Location: SITER")
print("System: 16 panels × 250W = 4.0 kW DC")
print("Losses: 8% (optimized - no shade, good airflow)")
print("=" * 70)
# Test different tilts (latitude = 30.44°N)
print("\nTilt Analysis (Azimuth = 180° South):")
print("-" * 40)
best_tilt = 0
best_prod = 0
for tilt in [0, 15, 20, 25, 30, 35, 40, 45, 50, 60, 90]:
prod = query(4.0, tilt, 180)
if prod > best_prod:
best_prod = prod
best_tilt = tilt
print(f" Tilt {tilt:>2}°: {prod:>6,.0f} kWh/yr")
time.sleep(0.5)
print(f"\n Best tilt: {best_tilt}° ({best_prod:,.0f} kWh/yr)")
# Test azimuth variations
print("\nAzimuth Analysis (Tilt = 30°):")
print("-" * 40)
best_az = 180
best_prod = 0
for az in [90, 120, 150, 180, 210, 240, 270]:
prod = query(4.0, 30, az)
if prod > best_prod:
best_prod = prod
best_az = az
direction = {90: "E", 120: "ESE", 150: "SSE", 180: "S", 210: "SSW", 240: "WSW", 270: "W"}
print(f" {az:>3}° ({direction.get(az, ''):<3}): {prod:>6,.0f} kWh/yr")
time.sleep(0.5)
print(f"\n Best azimuth: {best_az}° ({best_prod:,.0f} kWh/yr)")
# Final optimized production
print("\n" + "=" * 70)
print("OPTIMIZED SYSTEM PERFORMANCE")
print("=" * 70)
opt_prod = query(4.0, best_tilt, best_az)
value = (opt_prod * 0.60 * 0.085) + (opt_prod * 0.40 * 0.04)
payback = 4100 / (value / 12)
print(f"\n Optimal Config: {best_tilt}° tilt, {best_az}° azimuth")
print(f" Annual Production: {opt_prod:,.0f} kWh")
print(f" Monthly Average: {opt_prod/12:,.0f} kWh")
print(f" Monthly Value: ${value/12:.2f}")
print(f" Bill Offset: {(value/12/301.08)*100:.1f}%")
print(f" Payback: {payback/12:.1f} years")
print("\n" + "=" * 70)
print("PANEL WATTAGE OPTIONS (Optimized Orientation)")
print("=" * 70)
print(f"\n{'Panels':<12} {'Capacity':>10} {'kWh/yr':>10} {'$/mo':>8} {'Offset':>8} {'Payback':>10}")
print("-" * 60)
for watts in [250, 300, 350, 400, 450]:
capacity = (16 * watts) / 1000
prod = query(capacity, best_tilt, best_az)
if prod:
value = (prod * 0.60 * 0.085) + (prod * 0.40 * 0.04)
payback = 4100 / (value / 12)
offset = (value / 12 / 301.08) * 100
print(f"16 × {watts}W {capacity:>8.1f}kW {prod:>10,.0f} ${value/12:>6.2f} {offset:>6.1f}% {payback/12:>8.1f} yrs")
time.sleep(0.5)

View File

@@ -0,0 +1,61 @@
#!/usr/bin/env python3
# DEPRECATED: This script is deprecated. Use siter-solar-analysis.sh instead.
# This file is kept for reference only.
"""Quick comparison: standard losses vs optimized (no shade)"""
import requests
LAT = float(os.environ.get("SITER_LAT", "30.44")) # Central Texas
LON = float(os.environ.get("SITER_LON", "-97.62")) # Central Texas
import os
API_URL = "https://developer.nrel.gov/api/pvwatts/v6.json"
def query(capacity, losses):
params = {
"api_key": os.environ.get("NREL_API_KEY", "DEMO_KEY"),
"lat": LAT, "lon": LON,
"system_capacity": capacity,
"array_type": 0, # Ground mount
"tilt": 30, "azimuth": 180,
"module_type": 0,
"losses": losses,
"timeframe": "monthly"
}
r = requests.get(API_URL, params=params, timeout=30)
return r.json()
print("=" * 70)
print("GROUND MOUNT ANALYSIS: Standard vs Optimized (No Shade)")
print("Location: SITER")
print("=" * 70)
print("\nGround mount advantages at your site:")
print(" - No trees = minimal shading losses")
print(" - Open rack = better airflow, cooler panels")
print(" - Optimal tilt (30°) = maximum annual production")
print()
# Standard 14% losses vs optimized 8% losses (no shade)
for losses in [14, 10, 8, 5]:
data = query(4.0, losses)
if data and not data.get("errors"):
annual = data["outputs"]["ac_annual"]
monthly = annual / 12
value = (annual * 0.60 * 0.085) + (annual * 0.40 * 0.04)
payback = 4100 / (value / 12)
print(f"Losses @ {losses}%: {annual:,.0f} kWh/yr | ${value/12:.2f}/mo | Payback: {payback/12:.1f} yrs")
print()
print("=" * 70)
print("PANEL WATTAGE COMPARISON (Optimized @ 8% losses)")
print("=" * 70)
for watts in [250, 300, 350, 400, 450]:
capacity = (16 * watts) / 1000
data = query(capacity, 8) # Optimized losses
if data and not data.get("errors"):
annual = data["outputs"]["ac_annual"]
value = (annual * 0.60 * 0.085) + (annual * 0.40 * 0.04)
payback = 4100 / (value / 12)
offset = (value / 12 / 301.08) * 100
print(f"16 × {watts}W ({capacity:.1f}kW): {annual:,.0f} kWh/yr | ${value/12:.2f}/mo | {offset:.0f}% offset | {payback/12:.1f} yr payback")

View File

@@ -0,0 +1,153 @@
#!/usr/bin/env bats
#
# BATS tests for siter-solar-analysis.sh
# Run with: bats tests/
#
SCRIPT_PATH="/app/solar-analysis/siter-solar-analysis.sh"
@test "script exists and is executable" {
[ -f "$SCRIPT_PATH" ]
[ -x "$SCRIPT_PATH" ]
}
@test "help option displays usage" {
run "$SCRIPT_PATH" --help
[ "$status" -eq 0 ]
[[ "$output" == *"SITER Solar Analysis"* ]]
[[ "$output" == *"USAGE:"* ]]
[[ "$output" == *"--api-key"* ]]
[[ "$output" == *"--scenarios"* ]]
[[ "$output" == *"NREL_API_KEY"* ]]
}
@test "version option displays version" {
run "$SCRIPT_PATH" --version
[ "$status" -eq 0 ]
[[ "$output" =~ "siter-solar-analysis version "[0-9]+\.[0-9]+\.[0-9]+ ]]
}
@test "invalid option returns error" {
run "$SCRIPT_PATH" --invalid-option
[ "$status" -eq 1 ]
[[ "$output" == *"Unknown option"* ]]
}
@test "dependencies are available" {
command -v curl
command -v jq
command -v bc
}
@test "invalid latitude returns error" {
run "$SCRIPT_PATH" --lat "invalid"
[ "$status" -eq 1 ]
}
@test "out of range latitude returns error" {
run "$SCRIPT_PATH" --lat "999"
[ "$status" -eq 1 ]
}
@test "invalid longitude returns error" {
run "$SCRIPT_PATH" --lon "abc"
[ "$status" -eq 1 ]
}
@test "JSON output is valid JSON" {
run "$SCRIPT_PATH" --json
if [ "$status" -eq 0 ]; then
echo "$output" | jq .
fi
}
@test "JSON output has required fields" {
run "$SCRIPT_PATH" --json
if [ "$status" -eq 0 ]; then
echo "$output" | jq -e '.system'
echo "$output" | jq -e '.production'
echo "$output" | jq -e '.financials'
fi
}
@test "text output shows expected sections" {
run "$SCRIPT_PATH"
if [ "$status" -eq 0 ]; then
[[ "$output" == *"SITER SOLAR PROJECT"* ]]
[[ "$output" == *"NREL PVWATTS ANALYSIS"* ]]
[[ "$output" == *"System Configuration:"* ]]
[[ "$output" == *"FINANCIAL ANALYSIS"* ]]
[[ "$output" == *"ROI ANALYSIS"* ]]
[[ "$output" == *"PAYBACK PERIOD"* ]]
fi
}
@test "monthly production table shows all months" {
run "$SCRIPT_PATH"
if [ "$status" -eq 0 ]; then
for month in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec; do
[[ "$output" == *"$month"* ]]
done
[[ "$output" == *"ANNUAL"* ]]
fi
}
@test "scenarios mode shows header" {
run "$SCRIPT_PATH" --scenarios
if [ "$status" -eq 0 ]; then
[[ "$output" == *"SYSTEM SIZE SCENARIOS"* ]]
fi
}
@test "scenarios shows panel options" {
run "$SCRIPT_PATH" --scenarios
if [ "$status" -eq 0 ]; then
[[ "$output" == *"250W"* ]]
[[ "$output" == *"400W"* ]]
fi
}
@test "scenarios JSON output is array" {
run "$SCRIPT_PATH" --scenarios --json
if [ "$status" -eq 0 ]; then
local type
type=$(echo "$output" | jq -r 'type')
[ "$type" == "array" ]
fi
}
@test "custom panel configuration works" {
run "$SCRIPT_PATH" -p 20 -w 400
if [ "$status" -eq 0 ]; then
[[ "$output" == *"20 × 400W"* ]]
[[ "$output" == *"8.0 kW"* ]]
fi
}
@test "invalid API key returns error" {
run "$SCRIPT_PATH" -k "invalid_key_for_testing_12345"
[ "$status" -eq 1 ]
}
@test "location from environment variables" {
SITER_LAT=32.77 SITER_LON=-96.79 run "$SCRIPT_PATH"
if [ "$status" -eq 0 ]; then
[[ "$output" == *"32.77"* ]]
[[ "$output" == *"-96.79"* ]]
fi
}
@test "financial calculations are numeric" {
run "$SCRIPT_PATH" --json -w 250
if [ "$status" -eq 0 ]; then
local annual_kwh monthly_savings payback_years
annual_kwh=$(echo "$output" | jq -r '.production.annual_kwh')
monthly_savings=$(echo "$output" | jq -r '.financials.monthly_savings')
payback_years=$(echo "$output" | jq -r '.financials.payback_years')
[[ "$annual_kwh" =~ ^[0-9]+$ ]]
[[ "$monthly_savings" =~ ^[0-9]+\.[0-9]+$ ]]
[[ "$payback_years" =~ ^[0-9]+\.[0-9]+$ ]]
fi
}

623
v2-siter-solar-plan.md Normal file
View File

@@ -0,0 +1,623 @@
# SITER Solar Installation Project
## 2026 Renewable Energy Initiative - v2 Analysis
---
## Executive Summary
This document outlines a solar energy installation project for SITER, designed to offset a portion of monthly electricity costs through solar generation. The system will utilize 16 solar panels across 8 custom-built ground-mount racks, paired with a Sol-Ark 5K hybrid inverter that integrates with the existing Base Power battery backup system.
**Total Project Investment:** $4,100.00
**Estimated ROI Period:** 9.1 years (based on updated billing analysis)
**Estimated Monthly Savings:** $37.61 (14.2% bill offset)
**Existing Infrastructure:** SITER currently operates with a Base Power battery backup system and automatic transfer switch (ATS), installed in 2025, providing resilience and a fixed energy rate of **$0.085/kWh** through a 3-year contract (Oct 2025 - Oct 2028).
**Updated Billing Data (15 months):** Average monthly consumption of 1,885 kWh (~22,600 kWh/year) at average effective rate of $0.140/kWh.
**NREL PVWatts Analysis:** Based on location data for SITER, a 16-panel system (4.0 kW DC with 250W panels) is projected to produce ~6,000 kWh/year, offsetting approximately 14% of current consumption.
---
## Table of Contents
1. [Project Overview](#project-overview)
2. [Technical Specifications](#technical-specifications)
3. [Detailed Budget](#detailed-budget)
4. [Project Timeline](#project-timeline)
5. [Risk Analysis](#risk-analysis)
6. [Return on Investment Analysis](#return-on-investment-analysis)
7. [Recommendations](#recommendations)
8. [Appendix](#appendix)
---
## Project Overview
### Objectives
- Reduce monthly electricity expenses through solar generation (~14% offset with current panels)
- Integrate with existing Base Power battery backup infrastructure
- Leverage Base Power's 4¢/kWh solar buyback program for excess production
- Complete installation with minimal upfront capital expenditure
- Achieve positive ROI within 10 years while gaining energy resilience
### Scope
| Component | Quantity | Description |
|-----------|----------|-------------|
| Solar Panels | 16 | Existing inventory (250W each) |
| Ground-Mount Racks | 8 | Custom steel construction |
| Inverter | 1 | Sol-Ark 5K Hybrid |
| Wiring & Connectors | TBD | To be determined based on site layout |
### Current Status
- **Phase:** Initial procurement and rack construction
- **Spend to Date:** $360.95 (first solar rack completed)
- **Next Milestone:** Complete remaining 7 racks
### Existing Infrastructure
SITER has the following energy infrastructure already in place:
| Component | Details | Install Date |
|-----------|---------|--------------|
| Base Power Battery System | Battery backup with automatic transfer switch | 2024 |
| Automatic Transfer Switch (ATS) | Seamless grid/battery switching | 2024 |
| Base Power Contract | 3-year fixed rate at $0.085/kWh | 2025-2028 |
**Site Base Load:** ~1,885 kWh/month (15-month average)
This existing infrastructure significantly de-risks the solar project:
- ATS already installed for grid switching
- Battery backup provides load smoothing and outage protection
- Fixed-rate contract provides predictable baseline costs
---
## Technical Specifications
### Inverter: Sol-Ark 5K-5kW-48V Single-Phase Hybrid
| Specification | Value |
|---------------|-------|
| Power Output | 5kW |
| System Voltage | 48V DC |
| Phase | Single Phase |
| Type | Hybrid (Grid-tied + Battery Backup) |
| Source | [EcoDirect](https://www.ecodirect.com/Sol-Ark-5K-5kW-48V-Single-Phase-Hybrid-Inverter-p/sol-ark-5k-1p-n.htm) |
**Key Benefits:**
- Grid-tied operation for net metering eligibility
- **Integrates with existing Base Power battery system**
- Hybrid functionality ensures power during grid outages
- High efficiency rating minimizes energy loss
- Compatible with existing ATS infrastructure
### Existing Base Power System
| Specification | Value |
|---------------|-------|
| Contract Rate | $0.085/kWh (fixed through Oct 2028) |
| Solar Buyback Rate | $0.04/kWh (4¢/kWh) |
| Contract Term | 3 years (Oct 2025 - Oct 2028) |
| Site Base Load | ~1,885 kWh/month (actual 15-month average) |
| Backup Capability | Automatic transfer switch + battery |
| Installation Date | 2024 |
| Solar Integration | Seamless - no additional equipment needed |
**Source:** [Base Power Solar Integration](https://www.basepowercompany.com/with-solar)
### Solar Buyback Program Details
Base Power offers competitive solar buyback rates for excess production:
| Feature | Details |
|---------|--------|
| Buyback Rate | 4¢/kWh for all excess solar production |
| Credit Application | Applies to **entire bill** (not just energy charge) |
| Negative Balance | Rolls over to following month |
| Solar Tracking | Shown on monthly bill in "Solar Credits" section |
| Outage Protection | Base keeps solar panels producing during grid outages |
**Integration Strategy:** Solar generation will first offset on-site consumption (reducing the $0.085/kWh charge), with excess production credited at $0.04/kWh. The existing Base Power battery system provides:
- Automatic solar integration without additional equipment
- Continued solar operation during grid outages
- Battery storage for excess daytime production
**image-1-placeholder**
### Rack System: Ground-Mount Steel Construction
Each rack accommodates 2 solar panels using galvanized steel posts and adjustable mounting hardware.
**Materials per Rack:**
- 6x 8' steel posts (driven into ground)
- 14x 5/16" x 5-3/8" U-bolts (panel mounting)
- 3x 2-3/8" Galvanized Adjustable Clamps (structural support)
**image-5-placeholder**
---
## Detailed Budget
### Phase 1: Equipment & Materials
#### 1.1 Panel Rack Construction
| Line Item | Qty | Unit Cost | Subtotal | Notes |
|-----------|-----|-----------|----------|-------|
| 8' Steel Posts | 48 | $34.97 | $1,678.56 | 6 posts per rack × 8 racks |
| 5/16" x 5-3/8" U-bolts | 112 | $2.33 | $260.96 | 14 per rack × 8 racks |
| 2-3/8" Galv Adj. Clamps | 24 | $3.77 | $90.48 | 3 per rack × 8 racks |
| **Rack Materials Subtotal** | | | **$2,030.00** | |
#### 1.2 Tools & Equipment (One-Time)
| Line Item | Qty | Unit Cost | Subtotal | Notes |
|-----------|-----|-----------|----------|-------|
| Post Driver | 1 | $57.75 | $57.75 | Reusable for future projects |
| **Tools Subtotal** | | | **$57.75** | |
#### 1.3 Electrical Components
| Line Item | Est. Cost | Notes |
|-----------|-----------|-------|
| Romex Wiring | $200.00 | Gauge/length TBD based on site survey |
| Connectors & Junction Boxes | $112.25 | MC4 connectors, conduit, etc. |
| **Electrical Subtotal** | **$312.25** | Conservative estimate |
#### 1.4 Inverter
| Line Item | Qty | Unit Cost | Subtotal | Notes |
|-----------|-----|-----------|----------|-------|
| Sol-Ark 5K Hybrid Inverter | 1 | $1,500.00 | $1,500.00 | Includes shipping |
| **Inverter Subtotal** | | | **$1,500.00** | |
---
### Budget Summary
| Category | Amount | % of Total |
|----------|--------|------------|
| Rack Materials | $2,030.00 | 49.5% |
| Tools (One-Time) | $57.75 | 1.4% |
| Electrical Components | $312.25 | 7.6% |
| Inverter | $1,500.00 | 36.6% |
| **Contingency Reserve (10%)** | **$200.00** | **4.9%** |
| **TOTAL PROJECT BUDGET** | **$4,100.00** | **100%** |
### Spend to Date
| Date | Item | Amount | Running Total |
|------|------|--------|---------------|
| 2026 | First Solar Rack (materials) | $360.95 | $360.95 |
| | *Remaining Budget* | *$3,739.05* | |
---
### Cost Comparison: Per-Rack Analysis
| Cost Element | Per Rack | 8 Racks Total |
|--------------|----------|---------------|
| Steel Posts | $209.82 | $1,678.56 |
| U-bolts | $45.92 | $367.36 |
| Adjustable Clamps | $11.31 | $90.48 |
| **Base Rack Cost** | **$267.05** | **$2,136.40** |
| Wiring/Connectors (est.) | $32.95 | $263.60 |
| **All-In Rack Cost** | **$301.08** | **$2,400.00** |
---
## Project Timeline
### Phase Overview
```
Week 1-2: [████████] Procurement & Site Prep
Week 3-6: [████████████████████████████████] Rack Construction
Week 7-8: [████████████] Inverter Installation
Week 9: [████] Electrical Wiring
Week 10: [████] Testing & Commissioning
```
### Detailed Schedule
| Phase | Tasks | Duration | Dependencies |
|-------|-------|----------|--------------|
| **1. Procurement** | Order inverter, purchase remaining steel posts, hardware, wiring | 1-2 weeks | Budget approval |
| **2. Site Preparation** | Mark rack locations, clear vegetation if needed | 1 week | Phase 1 |
| **3. Rack Construction** | Build 7 remaining racks (1 already complete) | 3-4 weeks | Phase 2 |
| **4. Panel Mounting** | Install panels on completed racks | 1 week | Phase 3 |
| **5. Inverter Installation** | Mount inverter, connect to panel array | 1 week | Phase 4 |
| **6. Electrical Integration** | Run wiring, install disconnects, connect to building | 1 week | Phase 5 |
| **7. Testing & Commissioning** | System testing, safety inspection, energize | 1 week | Phase 6 |
**Estimated Total Duration:** 8-10 weeks
### Milestones
- [ ] **M1:** All materials procured
- [ ] **M2:** Site preparation complete
- [ ] **M3:** All 8 racks constructed
- [ ] **M4:** Panels mounted and secured
- [ ] **M5:** Inverter installed and connected
- [ ] **M6:** System commissioned and operational
---
## Risk Analysis
### Risk Register
| ID | Risk Description | Probability | Impact | Risk Score | Mitigation Strategy |
|----|------------------|-------------|--------|------------|---------------------|
| R1 | Wiring costs exceed estimates | Medium | Medium | 6 | Obtain quotes from 2-3 suppliers; consider DIY installation |
| R2 | Weather delays rack construction | Medium | Low | 4 | Build buffer into timeline; work during favorable seasons |
| R3 | Inverter supply chain delays | Low | High | 5 | Order early; confirm stock before project start |
| R4 | Structural issues with racks | Low | Medium | 3 | Follow engineering best practices; inspect posts regularly |
| R5 | Utility interconnection delays | Low | Medium | 3 | Research local requirements early; submit paperwork promptly |
| R6 | Permit requirements | Medium | Medium | 6 | Verify local building codes before construction |
| R7 | Panel compatibility issues | Low | Low | 2 | Confirm panel specs match inverter requirements |
| R8 | ROI takes longer than projected | Low | Medium | 3 | Conservative savings estimates; monitor actual output |
### Risk Matrix
```
IMPACT
Low Medium High
┌─────────┬─────────┬─────────┐
High │ │ R6 │ │
PROB ├─────────┼─────────┼─────────┤
Medium│ R2 │ R1,R8 │ │
├─────────┼─────────┼─────────┤
Low│ R7 │ R4,R5 │ R3 │
└─────────┴─────────┴─────────┘
```
### Key Risk Mitigations
#### R1: Wiring Cost Overrun
- **Current Status:** Only rough estimates completed
- **Action Items:**
- [ ] Conduct site survey to determine exact wire runs
- [ ] Obtain quotes from multiple electrical suppliers
- [ ] Consider aluminum conductors for long runs (cost savings)
- [ ] Allocate $200 contingency specifically for electrical
#### R6: Permit Requirements
- **Current Status:** Unknown
- **Action Items:**
- [ ] Contact local building department
- [ ] Research setback requirements for ground-mount systems
- [ ] Verify utility interconnection requirements
- [ ] Factor permit fees into budget if required
---
## Return on Investment Analysis
### Updated Billing Analysis (15 Months of Data)
Based on actual Base Power billing data from October 2024 through January 2026:
| Service Period | Consumption (kWh) | Bill Amount | Effective Rate |
|----------------|-------------------|-------------|----------------|
| Oct 18 - Nov 19, 2024 | 1,368 | $34.61 | $0.025/kWh* |
| Nov 19 - Dec 18, 2024 | 863 | $132.37 | $0.153/kWh |
| Dec 18, 2024 - Jan 17, 2025 | 1,092 | $160.33 | $0.147/kWh |
| Jan 17 - Feb 19, 2025 | 1,434 | $210.73 | $0.147/kWh |
| Feb 19 - Mar 21, 2025 | 1,398 | $199.35 | $0.143/kWh |
| Mar 21 - Apr 22, 2025 | 1,472 | $209.71 | $0.143/kWh |
| Apr 22 - May 21, 2025 | 1,936 | $277.13 | $0.143/kWh |
| May 21 - Jun 20, 2025 | 2,713 | $385.08 | $0.142/kWh |
| Jun 20 - Jul 22, 2025 | 2,995 | $423.56 | $0.141/kWh |
| Jul 22 - Aug 20, 2025 | 3,015 | $428.76 | $0.142/kWh |
| Aug 20 - Sep 19, 2025 | 2,924 | $440.35 | $0.151/kWh |
| Sep 19 - Oct 20, 2025 | 2,486 | $372.89 | $0.150/kWh |
| Oct 20 - Nov 18, 2025 | 1,553 | $235.81 | $0.152/kWh |
| Nov 18 - Dec 18, 2025 | 1,469 | $240.12 | $0.163/kWh |
| Dec 18, 2025 - Jan 20, 2026 | 1,549 | $216.25 | $0.140/kWh |
| **15-Month Average** | **1,885** | **$264.47** | **$0.140/kWh** |
*Note: Oct-Nov 2024 bill had credit applied from previous provider transition
### Seasonal Consumption Patterns
| Season | Months | Avg Consumption | Avg Bill |
|--------|--------|-----------------|----------|
| **Summer Peak** | Jun - Sep | 2,912 kWh | $415.89 |
| **Winter Low** | Nov - Feb | 1,333 kWh | $175.56 |
| **Shoulder** | Mar - May, Oct | 1,777 kWh | $261.55 |
**Key Insight:** Summer consumption is 2.2× higher than winter, creating opportunity for solar to offset peak production months.
### NREL PVWatts Production Estimate
Based on NREL PVWatts analysis for SITER:
| Parameter | Value |
|-----------|-------|
| System Capacity | 4.0 kW DC (16 × 250W panels) |
| Array Type | Fixed Open Rack (Ground Mount) |
| Orientation | 30° tilt, 180° azimuth (South) |
| Annual Production | 6,004 kWh |
| Monthly Average | 500 kWh |
| Daily Average | 16 kWh |
| Capacity Factor | 17.1% |
| Avg Solar Radiation | 5.52 kWh/m²/day |
### Ground Mount Advantages at SITER
The ground-mount configuration at this site has significant advantages:
| Advantage | Impact |
|-----------|--------|
| **Optimal Tilt (30°)** | Matches latitude for maximum annual production |
| **South-Facing (180°)** | Optimal azimuth for Texas |
| **No Trees/Shading** | Minimal shading losses (typical: 3-5%, this site: <1%) |
| **Open Rack** | Better airflow = cooler panels = higher efficiency |
| **Adjustable Orientation** | Can fine-tune tilt seasonally if desired |
**Optimized Production Estimate (8% losses vs 14% standard):**
| Scenario | Losses | Annual kWh | Monthly $ | Payback |
|----------|--------|------------|-----------|---------|
| Conservative (NREL default) | 14% | 6,004 | $37.61 | 9.1 yrs |
| **Optimized (no shade)** | **8%** | **~6,500** | **~$41** | **~8.3 yrs** |
The site's open exposure and ground-mount flexibility make it an ideal solar location.
### Monthly Production Profile (NREL)
| Month | AC Output (kWh) | Daily Avg | Solar Rad (kWh/m²/day) |
|-------|-----------------|-----------|------------------------|
| Jan | 450 | 15 | 4.58 |
| Feb | 453 | 15 | 5.15 |
| Mar | 492 | 16 | 5.23 |
| Apr | 508 | 17 | 5.61 |
| May | 534 | 18 | 5.87 |
| Jun | 514 | 17 | 5.94 |
| Jul | 555 | 18 | 6.31 |
| Aug | 571 | 19 | 6.57 |
| Sep | 514 | 17 | 6.01 |
| Oct | 516 | 17 | 5.62 |
| Nov | 469 | 15 | 4.99 |
| Dec | 429 | 14 | 4.39 |
| **Annual** | **6,004** | **16** | **5.52** |
**image-2-placeholder**
### Investment Summary (Updated)
| Metric | Value |
|--------|-------|
| Total Project Cost | $4,100.00 |
| Average Monthly Bill (15-mo actual) | $264.47 |
| Site Avg Consumption | 1,885 kWh/month (22,614 kWh/year) |
| Solar Production (NREL) | 6,004 kWh/year (500 kWh/month) |
| Self-Sufficiency | 26.6% |
| Base Power Energy Rate | $0.085/kWh |
| Base Power Export Rate | $0.04/kWh |
### Financial Analysis (Updated)
| Category | kWh/year | Rate | Annual Value |
|----------|----------|------|--------------|
| Self-Consumed (60%) | 3,602 | $0.085/kWh | $306.18 |
| Exported to Grid (40%) | 2,401 | $0.04/kWh | $96.06 |
| **Total Annual Value** | **6,004** | | **$402.24** |
| **Monthly Savings** | | | **$33.52** |
### ROI Summary (Updated)
| Metric | Value |
|--------|-------|
| Monthly Savings | $33.52 |
| Bill Offset | 12.7% (of $264.47 avg bill) |
| Payback Period | 10.2 years (122 months) |
### System Size Scenarios (NREL Analysis)
| System | kW | kWh/yr | $/mo | Offset | Payback |
|--------|-----|--------|------|--------|---------|
| 16 × 250W (current) | 4.0 | 6,004 | $33.52 | 12.7% | 10.2 yrs |
| 16 × 300W | 4.8 | 7,204 | $40.22 | 15.2% | 8.5 yrs |
| 16 × 350W | 5.6 | 8,405 | $46.93 | 17.8% | 7.3 yrs |
| 16 × 400W | 6.4 | 9,606 | $53.63 | 20.3% | 6.4 yrs |
| 16 × 450W | 7.2 | 10,806 | $60.34 | 22.8% | 5.7 yrs |
| 20 × 400W (expanded) | 8.0 | 12,007 | $67.04 | 25.4% | 5.1 yrs |
| 24 × 400W | 9.6 | 14,409 | $80.45 | 30.4% | 4.2 yrs |
**image-4-placeholder**
**Note:** To achieve 100% bill offset would require ~32 kW (80 panels @ 400W each).
### 10-Year Financial Projection (Current System: 16 × 250W)
| Year | Cumulative Savings | Net Position |
|------|-------------------|--------------|
| 0 | $0 | -$4,100.00 |
| 1 | $402.24 | -$3,697.76 |
| 2 | $804.48 | -$3,295.52 |
| 3 | $1,206.72 | -$2,893.28 |
| 4 | $1,608.97 | -$2,491.03 |
| 5 | $2,011.21 | -$2,088.79 |
| 6 | $2,413.45 | -$1,686.55 |
| 7 | $2,815.69 | -$1,284.31 |
| 8 | $3,217.93 | -$882.07 |
| 9 | $3,620.17 | -$479.83 |
| 10 | $4,022.41 | -$77.59 |
| 11 | $4,424.65 | +$324.65 |
**image-3-placeholder**
### Assumptions
- NREL PVWatts production estimates based on TMY (Typical Meteorological Year) data
- 60% self-consumption / 40% export split (conservative estimate)
- System losses of 14% (inverter, wiring, soiling, etc.)
- Electricity rates remain constant (conservative - rates typically increase 2-3% annually)
- No major maintenance required in first 10 years
- Base Power contract at $0.085/kWh remains in effect through Oct 2028
- Base Power solar buyback at $0.04/kWh continues
- Solar integration with Base Power system is technically feasible
### Key Considerations
**Production vs. Consumption Gap:**
- Current consumption: ~22,600 kWh/year
- Solar production: ~6,000 kWh/year
- Gap: ~16,600 kWh/year (73% still from grid/Base Power)
**Options to Improve Economics:**
1. Upgrade to higher-wattage panels (400W+ panels reduce payback to ~6 years)
2. Expand system size (additional panels/racks)
3. Reduce consumption through efficiency measures
4. Add more panels when costs decrease
### Value Adds Not Quantified
- **Energy Independence:** Already partially achieved via Base Power; solar adds generation capability
- **Property Value:** Solar installations typically increase property value
- **Environmental Impact:** Reduced carbon footprint
- **Existing Battery Storage:** Already in place via Base Power system
- **Rate Hedge:** Protection against future electricity rate increases post-2028
- **Redundancy:** Solar + Base Power + Grid provides triple-redundant power architecture
---
## Recommendations
### Immediate Actions
1. **Approve Budget:** Authorize $4,100 project expenditure
2. **Order Inverter:** Confirm Sol-Ark 5K availability and place order
3. **Complete Site Survey:** Determine exact wiring requirements and costs
4. **Verify Permits:** Contact local building department to confirm requirements
### Panel Upgrade Consideration
Based on the updated analysis, upgrading from 250W to 400W panels would:
- Increase production from 6,004 to 9,606 kWh/year (+60%)
- Increase monthly savings from $33.52 to $53.63
- Reduce payback from 10.2 years to 6.4 years
- Increase bill offset from 12.7% to 20.3%
**Recommendation:** If budget allows, consider sourcing 400W panels instead of using existing 250W inventory.
### Project Execution
1. Continue rack construction using proven design from Rack #1
2. Procure all remaining materials in single order for cost efficiency
3. Consider hiring licensed electrician for final grid connection
4. Document entire process for future reference/expansion
### Future Considerations
- **Base Power Contract Renewal (2028):** Evaluate contract terms vs. solar-only operation
- **Panel Expansion:** Inverter can handle additional capacity if needed
- **Monitoring:** Add production monitoring system for performance tracking
- **Grid Export:** Explore net metering options for excess generation
---
## Appendix
### A. Vendor Information
**Inverter Supplier:**
- EcoDirect
- Product: Sol-Ark 5K-5kW-48V Single-Phase Hybrid Inverter
- URL: https://www.ecodirect.com/Sol-Ark-5K-5kW-48V-Single-Phase-Hybrid-Inverter-p/sol-ark-5k-1p-n.htm
- Price: $1,500.00
### B. Bill of Materials
| Item | Specification | Qty Needed | Unit Cost | Total |
|------|--------------|------------|-----------|-------|
| Steel Posts | 8' galvanized | 48 | $34.97 | $1,678.56 |
| U-bolts | 5/16" x 5-3/8" | 112 | $2.33 | $260.96 |
| Adjustable Clamps | 2-3/8" galvanized | 24 | $3.77 | $90.48 |
| Post Driver | Manual | 1 | $57.75 | $57.75 |
| Hybrid Inverter | Sol-Ark 5K | 1 | $1,500.00 | $1,500.00 |
| Wiring/Connectors | TBD | - | - | $312.25 |
| **TOTAL** | | | | **$3,900.00** |
*Note: $200 contingency brings total to $4,100.00*
### C. Rack Construction Details
**Standard Rack Assembly (Per Rack):**
1. Drive 6 steel posts into ground in 2 rows of 3
2. Connect posts with horizontal rails using adjustable clamps
3. Secure panel mounting rails with U-bolts
4. Mount 2 solar panels per rack
5. Route wiring through protective conduit
**Completed:** 1 of 8 racks ($360.95 invested)
### D. Base Power Billing Details
**Contract Information:**
- Contract #: SITER (renewed Oct 2025)
- Contract ID: SITER
- Provider: Base Power Company (PUCT License #10338)
- Address: 1606 Headway Cir, Ste 9333, Austin, TX 78754
- Support: 1-866-479-POWR (7697)
**Rate Structure:**
- Energy Charge: $0.085/kWh (Oct 2025 - Oct 2028)
- Previous Rate: $0.090/kWh (Oct 2024 - Oct 2025)
- Utility Delivery (Oncor): Pass-through charges (~$0.056/kWh equivalent)
- Base Subscription Fee: $10.00/month
- Taxes: ~3.7% (SITER rates)
**Meter Data:**
- Consumption Meter #: SITER
- Generation Meter #: SITER (dual-purpose)
- Reads: Actual (not estimated)
- Tracks both Generation (solar/battery) and Consumption
### E. Base Power Integration Notes
**Technical Considerations:**
- Verify Sol-Ark 5K compatibility with existing Base Power ATS
- Confirm DC voltage alignment between solar array and Base Power battery system
- Determine optimal AC coupling configuration
- Review interconnection requirements with Base Power
**Contract Considerations:**
- Review Base Power contract for solar integration provisions
- Confirm no penalties for reduced consumption
- Plan for contract renewal negotiations in Oct 2028
### Base Power Solar Economics
**Consumption Offset vs. Export Credit:**
| Scenario | Rate | Benefit |
|----------|------|--------|
| Solar consumed on-site | $0.085/kWh avoided | Higher value - reduces energy charge |
| Solar exported to grid | $0.04/kWh credit | Lower value - but credits apply to entire bill |
**Optimal Strategy:** Maximize self-consumption during peak production hours (run high-load appliances when solar is generating) to capture the $0.085/kWh value rather than exporting at $0.04/kWh.
**Key Advantage:** Unlike many providers that only credit against energy charges, Base applies solar credits to the **entire bill** including delivery fees, taxes, and the $10/month subscription fee.
### F. Document History
| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | February 2026 | SITER | Initial project documentation |
| 1.1 | February 2026 | SITER | Added Base Power infrastructure details |
| 1.2 | February 2026 | SITER | Updated with actual billing data (5 months) |
| 2.0 | February 2026 | SITER | Updated with complete billing data (15 months), revised consumption averages, seasonal analysis |
---
*Document prepared for SITER Board Review*
*Project Start Date: 2026*
*Target Completion: Q2 2026*