Skip to content

Full Optimization

Run a comprehensive optimization job with custom bounds, constraints, and iteration control. Returns a job ID for async polling.

Endpoint

POST /solar/optimization/run

Request

{
  "site": {
    "latitude": 35.0,
    "longitude": -110.0,
    "capacity_kw": 1000,
    "land_area_ha": 10
  },
  "objective": "npv",
  "max_iterations": 50,
  "bounds": {
    "tilt_min": 15,
    "tilt_max": 45,
    "azimuth_min": 160,
    "azimuth_max": 200,
    "dc_ac_ratio_min": 1.1,
    "dc_ac_ratio_max": 1.4,
    "gcr_min": 0.3,
    "gcr_max": 0.5
  },
  "constraints": {
    "max_budget": 1000000,
    "min_annual_energy_kwh": 1500000,
    "min_capacity_factor": 0.18
  }
}

Parameters

Field Type Required Default Description
site.latitude float Yes - Site latitude
site.longitude float Yes - Site longitude
site.capacity_kw float Yes - DC capacity in kW
site.land_area_ha float No - Available land (hectares)
objective string No energy Optimization objective
max_iterations int No 50 Maximum optimization iterations
bounds object No defaults Parameter bounds
constraints object No none Hard constraints

Bounds

Field Default Min Default Max Description
tilt_min/max 0 60 Tilt angle (degrees)
azimuth_min/max 90 270 Azimuth angle (degrees)
dc_ac_ratio_min/max 1.0 1.6 DC/AC ratio
gcr_min/max 0.2 0.6 Ground coverage ratio

Constraints

Field Description
max_budget Maximum CAPEX in USD
min_annual_energy_kwh Minimum annual energy
min_capacity_factor Minimum capacity factor

Response (Job Created)

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "message": "Optimization job submitted",
  "poll_url": "/solar/optimization/550e8400-e29b-41d4-a716-446655440000"
}

Polling for Results

GET /solar/optimization/{job_id}

Pending Response

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "running",
  "progress": 45,
  "stage": "Bayesian optimization iteration 23/50"
}

Completed Response

{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "progress": 100,
  "optimal_parameters": {
    "tilt": 32.5,
    "azimuth": 182.3,
    "dc_ac_ratio": 1.28,
    "gcr": 0.38
  },
  "energy_metrics": {
    "annual_energy_mwh": 1850.5,
    "capacity_factor": 0.211,
    "specific_yield": 1850.5,
    "performance_ratio": 0.82
  },
  "financial_metrics": {
    "lcoe_cents_per_kwh": 3.45,
    "npv_dollars": 1250000,
    "irr_percent": 14.5,
    "payback_years": 6.2
  },
  "optimization_time_seconds": 12.5,
  "iterations_completed": 50
}

Code Examples

import requests
import time

# Submit job
response = requests.post(
    "https://api.tessellaterenewables.com/solar/optimization/run",
    json={
        "site": {
            "latitude": 35.0,
            "longitude": -110.0,
            "capacity_kw": 1000
        },
        "objective": "npv",
        "max_iterations": 50,
        "bounds": {
            "tilt_min": 20,
            "tilt_max": 40
        }
    }
)

job = response.json()
job_id = job["job_id"]
print(f"Job submitted: {job_id}")

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

    print(f"Status: {result['status']} ({result.get('progress', 0)}%)")

    if result["status"] == "completed":
        print(f"\nOptimal tilt: {result['optimal_parameters']['tilt']}°")
        print(f"NPV: ${result['financial_metrics']['npv_dollars']:,.0f}")
        break
    elif result["status"] == "failed":
        print(f"Error: {result.get('error_message')}")
        break

    time.sleep(2)
async function runFullOptimization(site, objective, bounds) {
  // Submit job
  const submitResponse = await fetch(
    'https://api.tessellaterenewables.com/solar/optimization/run',
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ site, objective, bounds })
    }
  );

  const { job_id } = await submitResponse.json();
  console.log(`Job submitted: ${job_id}`);

  // Poll for results
  while (true) {
    const statusResponse = await fetch(
      `https://api.tessellaterenewables.com/solar/optimization/${job_id}`
    );
    const result = await statusResponse.json();

    console.log(`Status: ${result.status} (${result.progress || 0}%)`);

    if (result.status === 'completed') {
      return result;
    } else if (result.status === 'failed') {
      throw new Error(result.error_message);
    }

    await new Promise(resolve => setTimeout(resolve, 2000));
  }
}
# Submit job
JOB_RESPONSE=$(curl -s -X POST \
  https://api.tessellaterenewables.com/solar/optimization/run \
  -H "Content-Type: application/json" \
  -d '{
    "site": {"latitude": 35.0, "longitude": -110.0, "capacity_kw": 1000},
    "objective": "npv"
  }')

JOB_ID=$(echo $JOB_RESPONSE | jq -r '.job_id')
echo "Job ID: $JOB_ID"

# Poll for results
while true; do
  RESULT=$(curl -s "https://api.tessellaterenewables.com/solar/optimization/$JOB_ID")
  STATUS=$(echo $RESULT | jq -r '.status')
  echo "Status: $STATUS"

  if [ "$STATUS" = "completed" ]; then
    echo $RESULT | jq '.optimal_parameters'
    break
  fi

  sleep 2
done

Optimization Algorithm

The full optimization uses Bayesian Optimization with a Gaussian Process surrogate model:

  1. Initial sampling of parameter space
  2. Surrogate model fitting
  3. Acquisition function optimization (Expected Improvement)
  4. Iterative refinement until convergence or max iterations

This approach is sample-efficient and works well for expensive objective functions.

Job Status Values

Status Description
pending Job queued, waiting to start
running Optimization in progress
completed Successfully completed
failed Error occurred

Tips

Choosing Iterations

  • 20-30 iterations: Quick results, may not be globally optimal
  • 50 iterations: Good balance (default)
  • 100+ iterations: More thorough, diminishing returns

Narrowing Bounds

If you know approximate good values, narrow the bounds for faster convergence:

"bounds": {
  "tilt_min": 25,
  "tilt_max": 40,
  "azimuth_min": 175,
  "azimuth_max": 185
}