Skip to content

Multi-Objective Optimization

Generate Pareto-optimal solutions that show tradeoffs between competing objectives using the NSGA-II evolutionary algorithm.

Endpoint

POST /solar/optimization/multi

Request

{
  "site": {
    "latitude": 35.0,
    "longitude": -110.0,
    "capacity_kw": 1000
  },
  "objectives": ["energy", "lcoe"],
  "generations": 100,
  "population_size": 50
}

Parameters

Field Type Required Default Description
site object Yes - Site configuration
objectives array Yes - 2-3 objectives to optimize
generations int No 100 Number of NSGA-II generations
population_size int No 50 Population size per generation

Available Objectives

Objective Direction Description
energy Maximize Annual energy (MWh)
lcoe Minimize Levelized cost (¢/kWh)
land_use Minimize Land area required (ha)

Response

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "pareto_front": [
    {
      "tilt": 28.5,
      "azimuth": 180.0,
      "dc_ac_ratio": 1.20,
      "gcr": 0.45,
      "objectives": {
        "energy": 1920.5,
        "lcoe": 3.85
      }
    },
    {
      "tilt": 32.0,
      "azimuth": 180.0,
      "dc_ac_ratio": 1.25,
      "gcr": 0.38,
      "objectives": {
        "energy": 1850.2,
        "lcoe": 3.45
      }
    },
    {
      "tilt": 35.5,
      "azimuth": 180.0,
      "dc_ac_ratio": 1.30,
      "gcr": 0.32,
      "objectives": {
        "energy": 1780.8,
        "lcoe": 3.25
      }
    }
  ],
  "num_solutions": 25,
  "optimization_time_seconds": 45.2
}

Understanding Pareto Fronts

A Pareto front contains solutions where improving one objective requires sacrificing another.

Energy (MWh)
    ^
    |     * High energy, high cost
    |   *
    |  *    <- Pareto Front (all optimal)
    | *
    |*      Low energy, low cost
    +-----------------------> LCOE (¢/kWh)

No solution on the Pareto front dominates another - they represent different tradeoffs.

Code Examples

import requests
import time

# Submit multi-objective job
response = requests.post(
    "https://api.tessellaterenewables.com/solar/optimization/multi",
    json={
        "site": {
            "latitude": 35.0,
            "longitude": -110.0,
            "capacity_kw": 1000
        },
        "objectives": ["energy", "lcoe"],
        "generations": 100,
        "population_size": 50
    }
)

job_id = response.json()["job_id"]

# Poll for results
while True:
    result = requests.get(
        f"https://api.tessellaterenewables.com/solar/optimization/{job_id}"
    ).json()

    if result["status"] == "completed":
        break
    time.sleep(2)

# Analyze Pareto front
pareto = result["pareto_front"]
print(f"Found {len(pareto)} Pareto-optimal solutions:\n")

for i, sol in enumerate(pareto):
    print(f"Solution {i+1}:")
    print(f"  Tilt: {sol['tilt']:.1f}°, GCR: {sol['gcr']:.2f}")
    print(f"  Energy: {sol['objectives']['energy']:.0f} MWh")
    print(f"  LCOE: {sol['objectives']['lcoe']:.2f} ¢/kWh")
    print()
import requests
import matplotlib.pyplot as plt

# ... (get results as above) ...

# Plot Pareto front
energies = [s['objectives']['energy'] for s in pareto]
lcoes = [s['objectives']['lcoe'] for s in pareto]

plt.figure(figsize=(10, 6))
plt.scatter(lcoes, energies, s=100, c='orange', edgecolors='black')
plt.xlabel('LCOE (¢/kWh)')
plt.ylabel('Annual Energy (MWh)')
plt.title('Pareto Front: Energy vs LCOE')
plt.grid(True, alpha=0.3)

# Annotate some points
for i in [0, len(pareto)//2, -1]:
    sol = pareto[i]
    plt.annotate(
        f"Tilt={sol['tilt']:.0f}°",
        (sol['objectives']['lcoe'], sol['objectives']['energy']),
        textcoords="offset points", xytext=(10,5)
    )

plt.tight_layout()
plt.savefig('pareto_front.png')

Choosing a Solution

The Pareto front gives you options. Choose based on priorities:

Priority Select Solution With
Maximum energy Highest energy value
Minimum cost Lowest lcoe value
Balanced Middle of Pareto front
Budget-constrained Lowest lcoe meeting energy target
# Example: Find solution closest to target
target_energy = 1850  # MWh
target_lcoe = 3.5     # ¢/kWh

best = min(pareto, key=lambda s: (
    (s['objectives']['energy'] - target_energy)**2 +
    (s['objectives']['lcoe'] - target_lcoe)**2 * 10000  # Scale LCOE
))

print(f"Best balanced solution: Tilt={best['tilt']:.1f}°")

Three-Objective Optimization

You can optimize three objectives simultaneously:

{
  "objectives": ["energy", "lcoe", "land_use"]
}

This creates a 3D Pareto surface, useful when land constraints matter.

Performance Tips

Setting Faster More Thorough
generations 50 200
population_size 30 100
Expected time ~15s ~120s
Solutions found 10-20 50-100