Skip to content

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 validation
  • middleware.py -- Rate limiting, CORS, request logging, SLO tracking
  • metering/ -- Usage metering and billing integration
  • security/ -- 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 profiles
  • organizations -- Multi-tenant organizations
  • api_keys -- Hashed API keys with scopes
  • solar_jobs / wind_jobs -- Analysis job records
  • optimization_results -- Stored optimization outputs
  • metering_events -- API usage metering records
  • billing_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:

  1. Every database query is scoped by organization_id (enforced by middleware).
  2. API keys are hashed (SHA-256) and bound to a specific organization.
  3. Users can belong to multiple organizations but must select an active context.
  4. Job results are only accessible within the owning organization.
  5. Admin endpoints require platform-level superuser authentication.
  6. 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:

  1. URL Prefix: All endpoints are prefixed with /v1/. Future major versions will use /v2/, /v3/, etc.

  2. Backward Compatibility: Within a major version, all changes are backward compatible:

  3. New optional fields may be added to request bodies.
  4. New fields may be added to response bodies.
  5. New endpoints may be added.
  6. Existing fields will not be removed or have their types changed.

  7. Deprecation Policy:

  8. Deprecated features are marked with a Deprecation response header.
  9. A minimum 6-month notice is provided before removal.
  10. Deprecated endpoints return a X-Tessellate-Deprecated: true header.
  11. Migration guides are published for each breaking change.

  12. 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
  1. Content Negotiation: The API also supports an X-API-Version header as an alternative to URL-based versioning for cases where clients cannot modify URL paths:
X-API-Version: 2025-01-15

Date-based versions allow pinning to a specific API snapshot. Undated requests default to the latest stable version within the URL major version.

  1. SDK Versioning: Official SDKs (Python, JavaScript) are versioned independently but track the API version they target. SDK version 1.x targets API v1.