From 400764a9ff4e619cde43209e2454131231dc53db Mon Sep 17 00:00:00 2001 From: Charles N Wyble Date: Fri, 27 Feb 2026 16:45:41 -0500 Subject: [PATCH] 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 --- .env.example | 11 + .gitignore | 27 + AGENTS.md | 161 +++++ Dockerfile.test | 32 + README.md | 181 +++++ images/image-1.svg | 162 +++++ images/image-2.svg | 112 +++ images/image-3.svg | 125 ++++ images/image-4.svg | 145 ++++ images/image-5.svg | 136 ++++ images/v2-image-1.svg | 167 +++++ images/v2-image-2.svg | 121 ++++ images/v2-image-3.svg | 131 ++++ images/v2-image-4.svg | 145 ++++ solar-analysis/Dockerfile | 9 + solar-analysis/run-analysis.sh | 16 + solar-analysis/siter-solar-analysis.sh | 674 ++++++++++++++++++ solar-analysis/solar_estimate.py | 299 ++++++++ solar-analysis/solar_optimal.py | 96 +++ solar-analysis/solar_optimized.py | 61 ++ .../tests/siter-solar-analysis.bats | 153 ++++ v2-siter-solar-plan.md | 623 ++++++++++++++++ 22 files changed, 3587 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 Dockerfile.test create mode 100644 README.md create mode 100644 images/image-1.svg create mode 100644 images/image-2.svg create mode 100644 images/image-3.svg create mode 100644 images/image-4.svg create mode 100644 images/image-5.svg create mode 100644 images/v2-image-1.svg create mode 100644 images/v2-image-2.svg create mode 100644 images/v2-image-3.svg create mode 100644 images/v2-image-4.svg create mode 100644 solar-analysis/Dockerfile create mode 100755 solar-analysis/run-analysis.sh create mode 100755 solar-analysis/siter-solar-analysis.sh create mode 100644 solar-analysis/solar_estimate.py create mode 100644 solar-analysis/solar_optimal.py create mode 100644 solar-analysis/solar_optimized.py create mode 100644 solar-analysis/tests/siter-solar-analysis.bats create mode 100644 v2-siter-solar-plan.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ed92f8e --- /dev/null +++ b/.env.example @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c84c1c1 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..17b3f98 --- /dev/null +++ b/AGENTS.md @@ -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 diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..5d3d797 --- /dev/null +++ b/Dockerfile.test @@ -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/"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fdcfdc --- /dev/null +++ b/README.md @@ -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/) diff --git a/images/image-1.svg b/images/image-1.svg new file mode 100644 index 0000000..f81612f --- /dev/null +++ b/images/image-1.svg @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + SITER Solar System Architecture + + + + + + + + + + + + + + Solar Radiation + 5.52 kWh/m²/day + + + + + + + + + + + + + + + + Solar Array + 16 × 250W = 4.0 kW DC + Ground Mount, 30° Tilt + + + + + + + + + + + + + + + + + + Sol-Ark 5K + Hybrid Inverter + + + + DC + + + + + + + + + + SITER + ~2,000 kWh/month + + + + + + + + + AC + + + + + + + + 78% + + Base Power Battery + + Automatic Transfer Switch + + + + charge + + + + + + + + + + + + + + Oncor Grid + Utility Delivery + + + + + + + + + export + @ $0.04/kWh + + + + backup + + + + Energy Flow + + + DC Power + + + AC to Home + + + Grid Export + + + Battery Charge + \ No newline at end of file diff --git a/images/image-2.svg b/images/image-2.svg new file mode 100644 index 0000000..9ee231d --- /dev/null +++ b/images/image-2.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + Monthly Solar Production Estimate (NREL PVWatts) + 16 × 250W panels = 4.0 kW DC | SITER, Pflugerville, TX + + + + AC Output (kWh) + + + 0 + 150 + 300 + 450 + 600 + 750 + + + + + + + + + + + + + + + + + + + 450 + Jan + + + + 453 + Feb + + + + 492 + Mar + + + + 508 + Apr + + + + 534 + May + + + + 514 + Jun + + + + 555 + Jul + + + + 571 + Aug + + + + 514 + Sep + + + + 516 + Oct + + + + 469 + Nov + + + + 429 + Dec + + + + 500 kWh/mo avg + + + + Annual: 6,004 kWh + Daily Avg: 16 kWh + \ No newline at end of file diff --git a/images/image-3.svg b/images/image-3.svg new file mode 100644 index 0000000..e7e8663 --- /dev/null +++ b/images/image-3.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + 10-Year Payback Timeline + 16 × 250W System | $4,100 Investment | $402/yr Savings + + + + Cumulative ($) + + + -$4,500 + -$3,000 + -$1,500 + $0 + $1,500 + + + + Breakeven + + + + + + + + + + + Years + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -$4,100 + -$2,089 + -$78 + +$325 + + + + ~10.2 yrs + + + + + Investment (Year 0) + + Profit (Year 11+) + diff --git a/images/image-4.svg b/images/image-4.svg new file mode 100644 index 0000000..e54ec9c --- /dev/null +++ b/images/image-4.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + System Size Comparison + NREL PVWatts Analysis: 16 Panels at Different Wattages + + + Annual Production (kWh) + + + + kWh/year + + + 0 + 5k + 10k + 15k + + + + + + + + + + + + + + + + + 6,004 + 250W + + + + 7,204 + 300W + + + + 8,405 + 350W + + + + 9,606 + 400W + + + + 10,806 + 450W + + + (current) + + + Payback Period (Years) + + + + Years + + + 0 + 5 + 10 + 15 + + + + + + + + + + + + + + + + + 10.2 + 250W + + + + 8.5 + 300W + + + + 7.3 + 350W + + + + 6.4 + 400W + + + + 5.7 + 450W + + + + 5 yr target + + + + Recommendation Summary + + Current (16×250W): + 10.2 yr payback, 11% offset + + Optimal (16×400W): + 6.4 yr payback, 18% offset + + 100% Offset requires: + ~36 kW (89 panels) + + Consumption: + ~24,000 kWh/yr + diff --git a/images/image-5.svg b/images/image-5.svg new file mode 100644 index 0000000..e76eee5 --- /dev/null +++ b/images/image-5.svg @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + Ground-Mount Rack Construction + Custom Steel Design - 2 Panels per Rack + + + + + + + Side View + + + + + + + + Post 1 + Post 2 + Post 3 + + + + + + + + + + + + + + + + + + + + + + + + + + 30° tilt + + + + ~4' in ground + + + Top View (Single Rack) + + + + + + + + + + + + + + + + + + + + Panel 1 + Panel 2 + + + + ~6' width + + + + Materials Per Rack + • 6× 8' Steel Posts ($34.97 ea) + • 14× U-bolts ($2.33 ea) + • 3× Adj Clamps ($3.77 ea) + • Total: ~$267/rack + + + + Full System Layout (8 Racks) + + + + + + + + + + + + + + 8 Racks × 2 Panels = 16 Panels Total + System: 4.0 kW DC | Array: ~48' × 6' + 1 rack completed ($360.95) + + + + U-bolt + + + + 5/16" × 5-3/8" + diff --git a/images/v2-image-1.svg b/images/v2-image-1.svg new file mode 100644 index 0000000..6472e8e --- /dev/null +++ b/images/v2-image-1.svg @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + SITER Solar System Architecture + + + + + + + + + + + + + + Solar Radiation + 5.52 kWh/m²/day + + + + + + + + + + + + + + + + Solar Array + 16 × 250W = 4.0 kW DC + Ground Mount, 30° Tilt + + + + + + + + + + + + + + + + + + Sol-Ark 5K + Hybrid Inverter + + + + DC + + + + + + + + + + SITER + ~1,885 kWh/month + + + + + + + + + AC + + + + + + + + 78% + + Base Power Battery + + Automatic Transfer Switch + + + + charge + + + + + + + + + + + + + + Oncor Grid + Utility Delivery + + + + + + + + + export + @ $0.04/kWh + + + + backup + + + + 15-Month Billing Summary + Avg: 1,885 kWh/mo | $264.47/mo + Rate: $0.140/kWh effective + Summer peak: 2,912 kWh + Winter low: 1,333 kWh + + + + Energy Flow + + + DC Power + + + AC to Home + + + Grid + \ No newline at end of file diff --git a/images/v2-image-2.svg b/images/v2-image-2.svg new file mode 100644 index 0000000..410bb87 --- /dev/null +++ b/images/v2-image-2.svg @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + Solar Production vs Monthly Consumption + 16 × 250W panels = 4.0 kW DC | SITER, Pflugerville, TX + + + + kWh + + + 0 + 750 + 1,500 + 2,250 + 3,000 + 3,750 + + + + + + + + + + + + + + + + + + + + + Jan + + + + + Feb + + + + + Mar + + + + + Apr + + + + + May + + + + + Jun + + + + + Jul + + + + + Aug + + + + + Sep + + + + + Oct + + + + + Nov + + + + + Dec + + + + + Monthly Consumption (avg by season) + + Solar Production (NREL estimate) + + + + Annual Production: 6,004 kWh + Annual Consumption: ~22,600 kWh + Offset: 26.6% + \ No newline at end of file diff --git a/images/v2-image-3.svg b/images/v2-image-3.svg new file mode 100644 index 0000000..1f4591a --- /dev/null +++ b/images/v2-image-3.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + 10-Year Payback Timeline + 16 × 250W System | $4,100 Investment | $402/yr Savings | $264/mo Avg Bill + + + + Cumulative ($) + + + -$4,500 + -$3,000 + -$1,500 + $0 + $1,500 + + + + Breakeven + + + + + + + + + + + Years + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -$4,100 + -$2,089 + -$78 + +$325 + + + + ~10.2 yrs + + + + + Investment (Year 0) + + Profit (Year 11+) + + + + Monthly Savings: $33.52 + Bill Offset: 12.7% + (of $264.47 avg bill) + \ No newline at end of file diff --git a/images/v2-image-4.svg b/images/v2-image-4.svg new file mode 100644 index 0000000..a9e9bb1 --- /dev/null +++ b/images/v2-image-4.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + System Size Comparison (Updated) + NREL PVWatts Analysis: 16 Panels at Different Wattages | Avg Bill: $264.47/mo + + + Annual Production (kWh) + + + + kWh/year + + + 0 + 5k + 10k + 15k + + + + + + + + + + + + + + + + + 6,004 + 250W + + + + 7,204 + 300W + + + + 8,405 + 350W + + + + 9,606 + 400W + + + + 10,806 + 450W + + + (current) + + + Payback Period (Years) + + + + Years + + + 0 + 5 + 10 + 15 + + + + + + + + + + + + + + + + + 10.2 + 250W + + + + 8.5 + 300W + + + + 7.3 + 350W + + + + 6.4 + 400W + + + + 5.7 + 450W + + + + 5 yr target + + + + Recommendation Summary (Updated) + + Current (16×250W): + 10.2 yr payback, 12.7% offset + + Optimal (16×400W): + 6.4 yr payback, 20.3% offset + + 100% Offset requires: + ~32 kW (80 panels) + + Consumption: + ~22,600 kWh/yr + \ No newline at end of file diff --git a/solar-analysis/Dockerfile b/solar-analysis/Dockerfile new file mode 100644 index 0000000..35cc155 --- /dev/null +++ b/solar-analysis/Dockerfile @@ -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"] diff --git a/solar-analysis/run-analysis.sh b/solar-analysis/run-analysis.sh new file mode 100755 index 0000000..bd8e1d9 --- /dev/null +++ b/solar-analysis/run-analysis.sh @@ -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" "$@" diff --git a/solar-analysis/siter-solar-analysis.sh b/solar-analysis/siter-solar-analysis.sh new file mode 100755 index 0000000..afab680 --- /dev/null +++ b/solar-analysis/siter-solar-analysis.sh @@ -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 diff --git a/solar-analysis/solar_estimate.py b/solar-analysis/solar_estimate.py new file mode 100644 index 0000000..d056f3f --- /dev/null +++ b/solar-analysis/solar_estimate.py @@ -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() diff --git a/solar-analysis/solar_optimal.py b/solar-analysis/solar_optimal.py new file mode 100644 index 0000000..b9e2fa1 --- /dev/null +++ b/solar-analysis/solar_optimal.py @@ -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) diff --git a/solar-analysis/solar_optimized.py b/solar-analysis/solar_optimized.py new file mode 100644 index 0000000..8506844 --- /dev/null +++ b/solar-analysis/solar_optimized.py @@ -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") diff --git a/solar-analysis/tests/siter-solar-analysis.bats b/solar-analysis/tests/siter-solar-analysis.bats new file mode 100644 index 0000000..2052ab8 --- /dev/null +++ b/solar-analysis/tests/siter-solar-analysis.bats @@ -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 +} diff --git a/v2-siter-solar-plan.md b/v2-siter-solar-plan.md new file mode 100644 index 0000000..24b965c --- /dev/null +++ b/v2-siter-solar-plan.md @@ -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*