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>
11
.env.example
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 |
9
solar-analysis/Dockerfile
Normal 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
@@ -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" "$@"
|
||||||
674
solar-analysis/siter-solar-analysis.sh
Executable 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
|
||||||
299
solar-analysis/solar_estimate.py
Normal 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()
|
||||||
96
solar-analysis/solar_optimal.py
Normal 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)
|
||||||
61
solar-analysis/solar_optimized.py
Normal 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")
|
||||||
153
solar-analysis/tests/siter-solar-analysis.bats
Normal 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
@@ -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*
|
||||||