System Architecture¶
Overview¶
Tessellate Renewables is a multi-tenant SaaS platform for renewable energy analysis, optimization, and forecasting. The system is built on an async-first architecture with background job processing for computationally intensive workloads (solar optimization, wind farm layout, energy yield analysis).
System Architecture Overview
============================================================================
Clients (SDK / Dashboard / API)
|
v
+------------------+ +------------------+ +------------------+
| | | | | |
| Nginx / CDN |------>| FastAPI API |<----->| PostgreSQL |
| (TLS Term) | | (Uvicorn) | | (Primary DB) |
| | | | | |
+------------------+ +--------+---------+ +------------------+
|
+--------+---------+
| |
| Redis Cluster |
| (Cache+Queue) |
| |
+--------+---------+
|
+----------------+----------------+
| |
+--------+---------+ +--------+---------+
| | | |
| Celery Workers | | Celery Beat |
| (CPU/GPU) | | (Scheduler) |
| | | |
+--------+---------+ +------------------+
|
+----------+----------+
| | |
+----+----+ +---+----+ +---+-----+
| Solar | | Wind | | Report |
| Engine | | Engine | | Engine |
+---------+ +--------+ +---------+
External Services:
+------------+ +------------+ +------------+ +------------+
| SendGrid | | Stripe | | NREL API | | Modal |
| (Email) | | (Billing) | | (Solar/ | | (GPU |
| | | | | Weather) | | Compute) |
+------------+ +------------+ +------------+ +------------+
Core Components¶
1. FastAPI API Server¶
The primary HTTP interface serving all REST API endpoints. Runs on Uvicorn with multiple workers behind Nginx in production.
| Attribute | Detail |
|---|---|
| Framework | FastAPI 0.100+ |
| ASGI Server | Uvicorn with --workers 4 |
| Auth | JWT tokens + API key authentication |
| Docs | Auto-generated OpenAPI at /docs and /redoc |
| Versioning | URL prefix versioning (/v1/...) |
Key Modules:
api/-- Route handlers organized by domain (solar, wind, billing, admin)auth/-- Authentication middleware, JWT handling, API key validationmiddleware.py-- Rate limiting, CORS, request logging, SLO trackingmetering/-- Usage metering and billing integrationsecurity/-- Input validation, RBAC, tenant isolation
2. Celery Workers¶
Distributed task queue workers processing computationally intensive jobs. Workers are horizontally scalable and support both CPU-bound and GPU-accelerated workloads.
| Attribute | Detail |
|---|---|
| Broker | Redis (queue transport) |
| Backend | Redis (result storage) |
| Concurrency | 4 per worker (CPU), 1 per worker (GPU) |
| Serializer | JSON |
| Task Queues | default, solar, wind, reports, gpu |
Task Types:
- Solar optimization (Bayesian, NSGA-II, grid search)
- Wind farm layout optimization and AEP calculation
- Report generation (PDF, Excel)
- Data ingestion and preprocessing
- Scheduled maintenance (cache warming, data refresh)
3. Redis¶
Serves dual purpose as both the Celery message broker and a high-performance cache layer.
| Function | Detail |
|---|---|
| Message Broker | Celery task dispatch and result storage |
| Cache | API response caching with TTL |
| Rate Limiting | Sliding window counters per API key |
| Sessions | Ephemeral session data |
| Sorted Sets | SLO time-series metrics storage |
| Pub/Sub | Real-time job status notifications via SSE |
4. PostgreSQL¶
Primary relational database storing all persistent application data.
| Attribute | Detail |
|---|---|
| Version | PostgreSQL 15+ |
| ORM | SQLAlchemy 2.0 with async support |
| Migrations | Alembic |
| Extensions | PostGIS (geospatial), pgcrypto (encryption) |
Key Tables:
users-- User accounts and profilesorganizations-- Multi-tenant organizationsapi_keys-- Hashed API keys with scopessolar_jobs/wind_jobs-- Analysis job recordsoptimization_results-- Stored optimization outputsmetering_events-- API usage metering recordsbilling_subscriptions-- Stripe subscription mirror
5. SendGrid¶
Transactional email service for user notifications.
- Welcome and onboarding emails
- Analysis completion notifications
- Weekly usage reports
- Billing alerts (approaching limits, payment failures)
- Password reset and account verification
6. Stripe¶
Payment processing and subscription management.
- Subscription plans (Free, Pro, Enterprise)
- Usage-based billing for compute credits
- Invoice generation
- Payment method management
- Webhook processing for payment events
Data Flow¶
Standard API Request Flow¶
Client Request
|
v
[1] TLS Termination (Nginx)
|
v
[2] Authentication Middleware
|--- JWT token validation (dashboard)
|--- API key lookup + hash verification (SDK/API)
|--- Extract user_id, org_id, scopes
|
v
[3] Rate Limit Middleware
|--- Check sliding window counter in Redis
|--- Key: rate_limit:{api_key}:{window}
|--- Return 429 if exceeded
|
v
[4] Usage Metering
|--- Record API call event
|--- Increment endpoint counter
|--- Check quota limits
|
v
[5] Request Validation
|--- Pydantic model validation
|--- Input sanitization
|--- Permission / scope check
|
v
[6] Route Handler
|--- Business logic execution
|--- Database queries (SQLAlchemy async)
|--- Cache lookup (Redis)
|--- External API calls (if needed)
|
v
[7] Response Serialization
|--- Pydantic response model
|--- Cache population (if cacheable)
|--- SLO metric recording
|
v
Client Response
Async Job Flow (Solar/Wind Analysis)¶
POST /v1/solar/optimize (or /v1/wind/analysis/run)
|
v
[1] Validate parameters + check credits
|
v
[2] Create job record in PostgreSQL (status: "queued")
|
v
[3] Dispatch Celery task to appropriate queue
|--- solar queue for solar jobs
|--- wind queue for wind jobs
|--- gpu queue for ML inference
|
v
[4] Return job_id to client (HTTP 202 Accepted)
... Background processing ...
[5] Celery worker picks up task
|
v
[6] Update job status to "processing"
|--- Publish status via Redis pub/sub
|
v
[7] Execute computation pipeline
|--- Load data, run models, generate results
|
v
[8] Store results in PostgreSQL
|
v
[9] Update job status to "completed"
|--- Publish completion via Redis pub/sub
|--- Fire webhook (if callback_url provided)
|--- Send email notification (if configured)
|
v
[10] Client polls GET /v1/{domain}/analysis/{job_id}
|--- Returns full results when status = "completed"
Solar Optimization Pipeline¶
The solar division runs multi-objective optimization to maximize energy yield while considering shading, clipping, terrain constraints, and financial parameters.
Input Parameters
|
+-- Location (lat, lon, boundary)
+-- Module specifications (power, dimensions, efficiency)
+-- Inverter specifications (capacity, clipping threshold)
+-- Financial parameters (cost/watt, degradation rate, tariff)
+-- Constraints (setbacks, shading objects, terrain slope)
|
v
[1] Site Preprocessing
|--- Fetch elevation data (SRTM / LiDAR)
|--- Compute slope and aspect maps
|--- Identify exclusion zones
|
v
[2] Shading Model
|--- Horizon shading from terrain
|--- Near-object shading (buildings, trees, equipment)
|--- Inter-row self-shading based on tilt and spacing
|--- Hourly shade factor matrix (8760 hours x zones)
|
v
[3] Irradiance Model
|--- TMY data fetch (NREL NSRDB or uploaded)
|--- Transposition to plane-of-array (Perez model)
|--- Apply shading losses from step 2
|--- Spectral and angle-of-incidence corrections
|
v
[4] Clipping Model
|--- DC-to-AC ratio evaluation
|--- Inverter saturation curve
|--- Annual clipping loss estimation
|--- Optimal DC/AC ratio search
|
v
[5] Optimization Engine
|
+-- Bayesian Optimization (single objective)
| |--- Gaussian process surrogate
| |--- Expected improvement acquisition
| |--- 50-200 evaluations
| |--- Objective: maximize LCOE or IRR
|
+-- NSGA-II (multi-objective)
| |--- Population size: 100-500
| |--- Generations: 50-200
| |--- Objectives: maximize energy, minimize cost, minimize land use
| |--- Returns Pareto front
|
+-- Grid Search (exhaustive)
|--- Tilt: 0-60 deg, step 1 deg
|--- Azimuth: 90-270 deg, step 5 deg
|--- Row spacing: 1-5x module height
|
v
[6] Financial Model
|--- LCOE calculation
|--- NPV / IRR analysis
|--- Payback period
|--- Revenue projection (25-year)
|
v
[7] Results Assembly
|--- Optimal configuration (tilt, azimuth, spacing, DC/AC)
|--- Energy yield (P50, P75, P90)
|--- Loss waterfall breakdown
|--- Financial summary
|--- Pareto front (if NSGA-II)
|--- Visualization data
Wind Analysis Pipeline¶
The wind division handles resource assessment, wake modeling, layout optimization, and energy yield estimation for wind farm development.
Input Parameters
|
+-- Site boundary (GeoJSON)
+-- Turbine positions (fixed or optimize)
+-- Turbine model (from catalog)
+-- Wake model selection
+-- Terrain model selection
|
v
[1] Boundary Processing
|--- Parse and validate GeoJSON boundary
|--- Compute area, centroid, convex hull
|--- Identify exclusion zones (setbacks, constraints)
|--- Generate candidate turbine positions (grid-based)
|
v
[2] Terrain Analysis
|--- Fetch DEM data (SRTM 30m or 90m)
|--- Compute elevation map within boundary
|--- Calculate slope gradient and aspect
|--- Compute RIX (Ruggedness Index)
|--- Surface roughness classification (land cover)
|--- Wind speed-up factors for complex terrain
|
v
[3] Wind Resource Assessment
|--- Fetch reanalysis data (ERA5 / MERRA-2)
|--- Long-term wind climatology (10-30 years)
|--- Vertical extrapolation to hub height (log/power law)
|--- Directional wind rose and Weibull fit
|--- Turbulence intensity estimation
|--- MCP (Measure-Correlate-Predict) if on-site data exists
|
v
[4] Wake Modeling
|
+-- Jensen Model (single wake, analytical)
| |--- Top-hat wake deficit profile
| |--- Linear wake expansion (k = 0.04-0.075)
|
+-- Bastankhah-Porte-Agel (Gaussian)
| |--- Gaussian wake deficit profile
| |--- More accurate near-wake region
| |--- Accounts for turbulence-induced mixing
|
+-- Gauss-Curl Hybrid
|--- Includes wake deflection from yaw
|--- Secondary steering effects
|--- Counter-rotating vortex pairs
|
|--- Wake superposition (linear / RSS / max)
|--- Deep-array wake effects
|--- Blockage model (upstream induction)
|
v
[5] Layout Optimizer (if optimize_layout = true)
|--- Genetic algorithm or NSGA-II
|--- Decision variables: turbine X, Y positions
|--- Constraints: minimum spacing, boundary, exclusions
|--- Objective: maximize AEP (or minimize wake loss)
|--- Evaluate 100-1000 candidate layouts
|
v
[6] Energy Yield Calculation
|--- Gross AEP from wind resource + power curve
|--- Wake losses from step 4
|--- Electrical losses (2-3%)
|--- Availability losses (2-5%)
|--- Turbine performance degradation
|--- Environmental losses (icing, blade soiling)
|--- Net AEP = Gross * (1 - total_losses)
|
v
[7] Uncertainty Quantification
|--- Monte Carlo simulation (10,000 iterations)
|--- Wind resource uncertainty (measurement + long-term)
|--- Wake model uncertainty
|--- Loss factor uncertainty
|--- P50, P75, P90, P99 energy yield estimates
|
v
[8] Report Generation
|--- Farm summary (AEP, capacity factor, losses)
|--- Per-turbine results table
|--- Wind rose and energy rose
|--- Wake loss map visualization
|--- Uncertainty distribution plot
|--- Terrain analysis maps
Multi-Tenancy Model¶
The platform implements strict multi-tenant isolation at the database level using organization-scoped queries.
Multi-Tenancy Hierarchy
========================
Organization (Tenant)
|
+-- Owner (User, role: owner)
| |
| +-- API Keys (scoped: full access)
| +-- Jobs (solar, wind, reports)
|
+-- Admin (User, role: admin)
| |
| +-- API Keys (scoped: read/write)
| +-- Jobs (solar, wind, reports)
|
+-- Member (User, role: member)
| |
| +-- API Keys (scoped: read-only or custom)
| +-- Jobs (own jobs only)
|
+-- Billing
| +-- Stripe customer ID
| +-- Subscription (plan + usage)
| +-- Payment methods
| +-- Invoices
|
+-- Settings
+-- Rate limit overrides
+-- Webhook configurations
+-- White-label branding
+-- Data retention policy
Isolation Rules:
- Every database query is scoped by
organization_id(enforced by middleware). - API keys are hashed (SHA-256) and bound to a specific organization.
- Users can belong to multiple organizations but must select an active context.
- Job results are only accessible within the owning organization.
- Admin endpoints require platform-level superuser authentication.
- Rate limits and quotas are enforced at the organization level.
API Key Scopes:
| Scope | Permissions |
|---|---|
solar:read |
Read solar analysis results |
solar:write |
Create and manage solar analyses |
wind:read |
Read wind analysis results |
wind:write |
Create and manage wind analyses |
billing:read |
View billing and usage data |
admin |
Full access to organization settings |
Deployment Architecture¶
The platform is deployed on Railway with separate services for each component.
Railway Project: tessellate-renewables
=========================================
+----------------------------+ +----------------------------+
| Service: api | | Service: worker |
| (FastAPI + Uvicorn) | | (Celery Worker) |
| | | |
| Dockerfile: Dockerfile | | Dockerfile: Dockerfile. |
| Port: 8000 | | worker |
| Replicas: 2 | | Queues: default, solar, |
| Health: /health | | wind, reports |
| Public domain: yes | | Replicas: 2 |
+----------------------------+ +----------------------------+
| |
+------------------+-----------------+
|
+-------------+-------------+
| |
+------------+------------+ +-----------+-----------+
| Service: redis | | Service: postgres |
| (Redis 7.x) | | (PostgreSQL 15) |
| | | |
| Plugin: Railway Redis | | Plugin: Railway |
| Memory: 256MB+ | | Postgres |
| Persistence: AOF | | Storage: 10GB+ |
+-------------------------+ | Backups: daily |
+------------------------+
Optional Services:
+----------------------------+ +----------------------------+
| Service: ml-engine | | Service: streamlit |
| (Modal GPU / Railway) | | (Streamlit Dashboard) |
| | | |
| GPU: T4 / A10G | | Port: 8501 |
| Tasks: ML training, | | Internal dashboard |
| inference | | for demos |
+----------------------------+ +----------------------------+
Service Communication:
- API to Redis: Direct TCP connection via
REDIS_URL - API to PostgreSQL: Connection pool via
DATABASE_URL(SQLAlchemy async) - Worker to Redis: Celery broker connection via
REDIS_URL - Worker to PostgreSQL: Direct connection for job status updates
- API to Worker: Indirect via Redis task queue (Celery
send_task) - External services: HTTPS outbound (SendGrid, Stripe, NREL, Modal)
API Versioning Strategy¶
The API uses URL path versioning with a commitment to backward compatibility.
Current Version: v1
Versioning Rules:
-
URL Prefix: All endpoints are prefixed with
/v1/. Future major versions will use/v2/,/v3/, etc. -
Backward Compatibility: Within a major version, all changes are backward compatible:
- New optional fields may be added to request bodies.
- New fields may be added to response bodies.
- New endpoints may be added.
-
Existing fields will not be removed or have their types changed.
-
Deprecation Policy:
- Deprecated features are marked with a
Deprecationresponse header. - A minimum 6-month notice is provided before removal.
- Deprecated endpoints return a
X-Tessellate-Deprecated: trueheader. -
Migration guides are published for each breaking change.
-
Version Lifecycle:
| Phase | Duration | Description |
|---|---|---|
| Active | Ongoing | Full feature development and support |
| Maintenance | 12 months | Bug fixes and security patches only |
| Deprecated | 6 months | Read-only, no new features or fixes |
| Sunset | - | Version removed entirely |
- Content Negotiation: The API also supports an
X-API-Versionheader as an alternative to URL-based versioning for cases where clients cannot modify URL paths:
Date-based versions allow pinning to a specific API snapshot. Undated requests default to the latest stable version within the URL major version.
- SDK Versioning: Official SDKs (Python, JavaScript) are versioned independently
but track the API version they target. SDK version
1.xtargets APIv1.