garima-mahato's picture
Upload 17 files
a1c701e verified
Raw
History Blame Contribute Delete
5.85 kB
"""
Carbon intensity profiles for 6 real data centre regions.
Values in gCO2eq/kWh, calibrated from Electricity Map 2024 data.
Profiles capture:
- Solar regions: low midday, higher at night
- Wind regions: variable, often lowest at night / early morning
- Hydro regions: consistently low
- Gas/coal: consistently high with minor variation
"""
import math
import random
from typing import List
# ── Regional base profiles (24-hour cycle, index = hour 0–23) ───────────────
def _solar_curve(base: float, peak_reduction: float) -> List[float]:
"""Dips around hours 10–15 when solar is generating."""
result = []
for h in range(24):
solar = max(0.0, math.sin(math.pi * (h - 6) / 12)) # peaks at noon
result.append(base - peak_reduction * solar)
return result
def _wind_curve(base: float, amplitude: float, phase: float) -> List[float]:
"""Wind is variable — modelled as a slow sinusoid with noise seed."""
return [
base + amplitude * math.sin(2 * math.pi * h / 24 + phase)
for h in range(24)
]
# Profiles keyed by region id
BASE_PROFILES = {
"us-west-2": {
"name": "Oregon (Hydro + Wind)",
"profile": _wind_curve(base=45, amplitude=15, phase=0.5),
"renewable": 0.82,
"capacity": 200.0,
},
"us-west-1": {
"name": "California (Solar + Grid)",
"profile": _solar_curve(base=220, peak_reduction=140),
"renewable": 0.52,
"capacity": 150.0,
},
"us-east-1": {
"name": "Virginia (Gas + Nuclear)",
"profile": _wind_curve(base=360, amplitude=20, phase=1.0),
"renewable": 0.24,
"capacity": 180.0,
},
"eu-west-1": {
"name": "Ireland (Wind + Gas)",
"profile": _wind_curve(base=240, amplitude=80, phase=2.0),
"renewable": 0.48,
"capacity": 120.0,
},
"ap-southeast-1": {
"name": "Singapore (Natural Gas)",
"profile": _wind_curve(base=455, amplitude=10, phase=0.0),
"renewable": 0.08,
"capacity": 100.0,
},
"ap-south-1": {
"name": "Mumbai (Coal + Solar)",
"profile": _solar_curve(base=680, peak_reduction=200),
"renewable": 0.18,
"capacity": 90.0,
},
}
def get_carbon_forecast(
region: str,
current_hour: int,
noise_seed: int = 0,
noise_level: float = 0.08,
) -> List[float]:
"""
Return 24-hour forecast starting from current_hour.
Adds realistic noise — the forecast is imperfect.
"""
rng = random.Random(noise_seed + hash(region))
profile = BASE_PROFILES[region]["profile"]
forecast = []
for offset in range(24):
h = (current_hour + offset) % 24
base_val = profile[h]
noise = base_val * noise_level * (rng.random() * 2 - 1)
forecast.append(max(10.0, base_val + noise))
return forecast
def get_carbon_now(region: str, current_hour: int, noise_seed: int = 0) -> float:
"""Current carbon intensity — less noisy than forecast."""
forecast = get_carbon_forecast(region, current_hour, noise_seed, noise_level=0.03)
return round(forecast[0], 1)
def get_renewable_pct(region: str, current_hour: int) -> float:
"""Renewable percentage varies slightly with solar/wind availability."""
base = BASE_PROFILES[region]["renewable"]
profile_val = BASE_PROFILES[region]["profile"][current_hour]
profile_min = min(BASE_PROFILES[region]["profile"])
profile_max = max(BASE_PROFILES[region]["profile"])
spread = max(1.0, profile_max - profile_min)
# when carbon is low → renewables are high
renewable_boost = 0.15 * (1 - (profile_val - profile_min) / spread)
return min(1.0, max(0.0, base + renewable_boost))
def naive_carbon_for_job(
energy_kwh: float,
current_hour: int,
noise_seed: int = 0,
available_regions: List[str] = None,
) -> float:
"""
FIX: Baseline is now 'run immediately in the best available region right now'.
Previously used Mumbai (worst region, 680 gCO2/kWh) which made carbon_score
trivially easy — even Virginia (360 gCO2/kWh) scored 47% efficiency without
learning anything useful.
Now the baseline is the true opportunity cost: what you would emit if you
picked the cleanest region available right now but did zero temporal planning.
The agent must beat THIS to prove it has learned to read forecasts and time
jobs to cleaner future windows.
"""
if available_regions is None:
available_regions = list(BASE_PROFILES.keys())
best_ci = min(
get_carbon_now(region, current_hour, noise_seed)
for region in available_regions
)
return energy_kwh * best_ci
def worst_carbon_for_job(
energy_kwh: float,
current_hour: int,
noise_seed: int = 0,
) -> float:
"""
Worst-case baseline (Mumbai) — kept for episode summary context logging only.
Not used in the reward function.
"""
ci = get_carbon_now("ap-south-1", current_hour, noise_seed)
return energy_kwh * ci
def optimal_carbon_for_job(
energy_kwh: float,
current_hour: int,
noise_seed: int = 0,
) -> float:
"""
Oracle: run job at the cleanest future hour across all regions.
Used to compute theoretical maximum savings (lower bound on reward).
"""
best = float("inf")
for region in BASE_PROFILES:
forecast = get_carbon_forecast(region, current_hour, noise_seed)
best = min(best, min(forecast))
return energy_kwh * best