from dataclasses import dataclass from typing import Dict @dataclass class TaskConfig: task_id: str description: str max_steps: int initial_demand_mwh: float initial_renewable_mwh: float peaker_capacity_mwh: float ev_storage_mwh: float ev_storage_capacity_mwh: float base_price_usd_per_mwh: float demand_trend_mwh: float renewable_trend_mwh: float demand_volatility: float renewable_volatility: float shock_step: int shock_renewable_drop: float reserve_margin_ratio: float reserve_commitment_threshold_ratio: float peaker_ramp_limit_mwh: float peaker_activation_delay_steps: int ev_ramp_limit_mwh: float peaker_startup_cost_usd: float peaker_emission_factor_tco2_per_mwh: float carbon_price_usd_per_tco2: float load_forecast_sigma: float renewable_forecast_sigma: float contingency_type: str contingency_step: int contingency_derate_pct: float hint: str enable_reserve_logic: bool = True enable_ramp_limits: bool = True enable_startup_emissions: bool = True TASKS: Dict[str, TaskConfig] = { "default": TaskConfig( task_id="default", description=( "Hybrid multi-agent smart-grid market simulation. Agents submit strategic bids, " "market clears, and LDU enforces physical feasibility with correction logs." ), max_steps=24, initial_demand_mwh=120.0, initial_renewable_mwh=70.0, peaker_capacity_mwh=85.0, ev_storage_mwh=25.0, ev_storage_capacity_mwh=60.0, base_price_usd_per_mwh=45.0, demand_trend_mwh=1.2, renewable_trend_mwh=-0.6, demand_volatility=4.0, renewable_volatility=6.0, shock_step=16, shock_renewable_drop=22.0, reserve_margin_ratio=0.12, reserve_commitment_threshold_ratio=1.0, peaker_ramp_limit_mwh=14.0, peaker_activation_delay_steps=0, ev_ramp_limit_mwh=9.0, peaker_startup_cost_usd=180.0, peaker_emission_factor_tco2_per_mwh=0.45, carbon_price_usd_per_tco2=50.0, load_forecast_sigma=4.0, renewable_forecast_sigma=6.0, contingency_type="none", contingency_step=-1, contingency_derate_pct=0.0, hint=( "Coordinate bids with expected dispatch feasibility. Market-optimal bids that violate " "physical constraints are corrected by LDU and reduce reward." ), ), "long_horizon": TaskConfig( task_id="long_horizon", description=( "Extended 48-step planning regime with recurring scarcity windows and delayed consequences. " "This task emphasizes long-horizon stability and storage foresight." ), max_steps=48, initial_demand_mwh=135.0, initial_renewable_mwh=78.0, peaker_capacity_mwh=90.0, ev_storage_mwh=32.0, ev_storage_capacity_mwh=75.0, base_price_usd_per_mwh=48.0, demand_trend_mwh=1.5, renewable_trend_mwh=-0.8, demand_volatility=5.0, renewable_volatility=7.0, shock_step=28, shock_renewable_drop=26.0, reserve_margin_ratio=0.14, reserve_commitment_threshold_ratio=1.0, peaker_ramp_limit_mwh=12.0, peaker_activation_delay_steps=1, ev_ramp_limit_mwh=8.0, peaker_startup_cost_usd=210.0, peaker_emission_factor_tco2_per_mwh=0.45, carbon_price_usd_per_tco2=60.0, load_forecast_sigma=5.5, renewable_forecast_sigma=7.0, contingency_type="transmission_derate", contingency_step=30, contingency_derate_pct=0.18, hint=( "Favor smooth dispatch and avoid repeated infeasible corrections. Battery misuse early in the " "episode causes compounding penalties later." ), ), "stress_shock": TaskConfig( task_id="stress_shock", description=( "Shock-heavy reliability scenario. Market remains strategic, but severe renewable drop and high " "demand volatility test emergency balancing behavior." ), max_steps=30, initial_demand_mwh=150.0, initial_renewable_mwh=85.0, peaker_capacity_mwh=96.0, ev_storage_mwh=24.0, ev_storage_capacity_mwh=70.0, base_price_usd_per_mwh=55.0, demand_trend_mwh=2.0, renewable_trend_mwh=-1.0, demand_volatility=7.5, renewable_volatility=9.0, shock_step=12, shock_renewable_drop=35.0, reserve_margin_ratio=0.16, reserve_commitment_threshold_ratio=1.05, peaker_ramp_limit_mwh=10.0, peaker_activation_delay_steps=1, ev_ramp_limit_mwh=7.0, peaker_startup_cost_usd=260.0, peaker_emission_factor_tco2_per_mwh=0.47, carbon_price_usd_per_tco2=70.0, load_forecast_sigma=8.0, renewable_forecast_sigma=10.0, contingency_type="peaker_trip", contingency_step=13, contingency_derate_pct=0.55, hint=( "Expect early shock and repeated scarcity. Policies must adapt quickly while keeping LDU corrections low." ), ), "normal": TaskConfig( task_id="normal", description="Normal operations benchmark with mild variability and no forced shock.", max_steps=24, initial_demand_mwh=118.0, initial_renewable_mwh=74.0, peaker_capacity_mwh=86.0, ev_storage_mwh=28.0, ev_storage_capacity_mwh=62.0, base_price_usd_per_mwh=44.0, demand_trend_mwh=1.0, renewable_trend_mwh=-0.4, demand_volatility=3.0, renewable_volatility=4.5, shock_step=-1, shock_renewable_drop=0.0, reserve_margin_ratio=0.12, reserve_commitment_threshold_ratio=1.0, peaker_ramp_limit_mwh=15.0, peaker_activation_delay_steps=0, ev_ramp_limit_mwh=9.0, peaker_startup_cost_usd=170.0, peaker_emission_factor_tco2_per_mwh=0.44, carbon_price_usd_per_tco2=45.0, load_forecast_sigma=3.5, renewable_forecast_sigma=5.0, contingency_type="none", contingency_step=-1, contingency_derate_pct=0.0, hint="Normal benchmark mode: prioritize efficient, low-correction dispatch.", ), "outage": TaskConfig( task_id="outage", description="Outage benchmark with N-1 generator-or-feeder contingency and delayed thermal recovery.", max_steps=30, initial_demand_mwh=142.0, initial_renewable_mwh=82.0, peaker_capacity_mwh=95.0, ev_storage_mwh=26.0, ev_storage_capacity_mwh=68.0, base_price_usd_per_mwh=53.0, demand_trend_mwh=1.8, renewable_trend_mwh=-0.9, demand_volatility=6.0, renewable_volatility=8.0, shock_step=-1, shock_renewable_drop=0.0, reserve_margin_ratio=0.16, reserve_commitment_threshold_ratio=1.08, peaker_ramp_limit_mwh=9.0, peaker_activation_delay_steps=1, ev_ramp_limit_mwh=7.0, peaker_startup_cost_usd=255.0, peaker_emission_factor_tco2_per_mwh=0.47, carbon_price_usd_per_tco2=70.0, load_forecast_sigma=7.5, renewable_forecast_sigma=9.0, contingency_type="n_minus_one", contingency_step=10, contingency_derate_pct=0.6, hint="Outage mode: N-1 resilience, reserve, and startup realism dominate.", ), "renewable_collapse": TaskConfig( task_id="renewable_collapse", description="Renewable collapse benchmark with severe forecast error and large drop event.", max_steps=30, initial_demand_mwh=146.0, initial_renewable_mwh=88.0, peaker_capacity_mwh=96.0, ev_storage_mwh=24.0, ev_storage_capacity_mwh=70.0, base_price_usd_per_mwh=56.0, demand_trend_mwh=1.9, renewable_trend_mwh=-1.2, demand_volatility=7.0, renewable_volatility=10.0, shock_step=8, shock_renewable_drop=48.0, reserve_margin_ratio=0.18, reserve_commitment_threshold_ratio=1.1, peaker_ramp_limit_mwh=9.0, peaker_activation_delay_steps=1, ev_ramp_limit_mwh=6.5, peaker_startup_cost_usd=270.0, peaker_emission_factor_tco2_per_mwh=0.48, carbon_price_usd_per_tco2=75.0, load_forecast_sigma=9.0, renewable_forecast_sigma=12.0, contingency_type="transmission_derate", contingency_step=12, contingency_derate_pct=0.22, hint="Renewable collapse mode: recover from severe uncertainty and scarcity.", ), } def get_task(task_id: str) -> TaskConfig: if task_id not in TASKS: raise ValueError(f"Unknown task_id '{task_id}'. Available: {list(TASKS.keys())}") return TASKS[task_id] def list_tasks() -> list[str]: return list(TASKS.keys())