File size: 6,704 Bytes
479b6b0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# EcoGrid-OpenEnv β€” Technical Specification

## 1. Overview

EcoGrid-OpenEnv is a production-grade Reinforcement Learning environment for the
OpenEnv framework that simulates sustainable energy grid management. An agent
controls energy distribution across renewable sources (solar + wind), fossil fuels,
and battery storage to meet variable demand while minimising cost and carbon emissions.

**Author:** Team DD  
**Hackathon:** Scaler School of Technology Γ— Meta PyTorch Finale  
**Theme:** #3 β€” World Modeling (Professional Tasks) + Mercor Sub-theme

---

## 2. File Tree

```
eco-grid-openenv/
β”œβ”€β”€ env/
β”‚   β”œβ”€β”€ __init__.py           # Package init, re-exports EcoGridEnv
β”‚   β”œβ”€β”€ environment.py        # Core EcoGridEnv class (reset/step/state)
β”‚   β”œβ”€β”€ dynamics.py           # Deterministic physics helpers (solar/wind/demand/battery)
β”‚   β”œβ”€β”€ reward.py             # Multi-objective reward function
β”‚   └── tasks.py              # 3 task graders (easy/medium/hard)
β”œβ”€β”€ models/
β”‚   β”œβ”€β”€ __init__.py           # Package init, re-exports all schemas
β”‚   └── schemas.py            # Pydantic v2 typed models
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ test_environment.py   # Environment unit tests
β”‚   β”œβ”€β”€ test_reward.py        # Reward function tests
β”‚   └── test_graders.py       # Grader determinism tests
β”œβ”€β”€ docs/
β”‚   └── SPEC.md               # This file
β”œβ”€β”€ logs/                     # Runtime logs (gitignored)
β”œβ”€β”€ lora_adapter/             # Trained LoRA weights (gitignored)
β”œβ”€β”€ openenv.yaml              # OpenEnv specification file
β”œβ”€β”€ baseline.py               # Heuristic + LLM inference script
β”œβ”€β”€ train_unsloth.py          # Unsloth GRPO training script
β”œβ”€β”€ app.py                    # Streamlit dashboard for HF Spaces
β”œβ”€β”€ Dockerfile                # Multi-stage production Dockerfile
β”œβ”€β”€ docker-compose.yml        # Local dev compose config
β”œβ”€β”€ requirements.txt          # Pinned Python dependencies
β”œβ”€β”€ README.md                 # Full hackathon README
β”œβ”€β”€ BLOG.md                   # HuggingFace blog post (≀400 words)
└── .gitignore                # Ignore logs/, lora_adapter/, __pycache__
```

---

## 3. State Space

| Field | Type | Range | Unit | Description |
|-------|------|-------|------|-------------|
| demand | float | [0, 200] | MWh | Energy demand this timestep |
| solar_capacity | float | [0, 1] | ratio | Available solar generation capacity |
| wind_capacity | float | [0, 1] | ratio | Available wind generation capacity |
| battery_level | float | [0, 1] | ratio | Battery state of charge |
| grid_stability | float | [0, 1] | ratio | Grid frequency stability indicator |
| carbon_budget_remaining | float | [0, 1000] | kgCOβ‚‚ | Remaining carbon emission budget |
| price_signal | float | [0, 300] | $/MWh | Current spot electricity price |
| time_step | int | [0, 96] | step | Current simulation timestep |

---

## 4. Action Space

| Field | Type | Range | Constraint | Description |
|-------|------|-------|-----------|-------------|
| renewable_ratio | float | [0, 1] | sum ≀ 1.0 | Fraction of demand met by renewables |
| fossil_ratio | float | [0, 1] | sum ≀ 1.0 | Fraction of demand met by fossil fuels |
| battery_action | float | [-1, 1] | β€” | Charge (+1) or discharge (-1) battery |

**Constraint:** `renewable_ratio + fossil_ratio ≀ 1.0`  
The remaining fraction `1 - renewable_ratio - fossil_ratio` is unmet demand (blackout risk).

---

## 5. Reward Function

### Terms (summing to [0, 1])

| Term | Weight | Formula |
|------|--------|---------|
| cost_savings | 0.30 | `1 - normalised(fossil_cost + grid_cost)` |
| carbon_score | 0.30 | `1 - normalised(carbon_emission)` |
| stability_score | 0.25 | `1 - blackout_risk` |
| renewable_bonus | 0.15 | `renewable_ratio Γ— (1 - blackout_risk)` |

### Penalties (subtracted before clip)

| Penalty | Trigger | Value |
|---------|---------|-------|
| blackout_penalty | demand unmet > 20% | -0.5 |
| carbon_overrun | carbon_budget < 0 (hard only) | -0.8 |

**Final:** `clip(weighted_sum - penalties, 0, 1)`

---

## 6. Task Specifications

### Easy: BasicGridBalance
- **Episode length:** 48 steps
- **Conditions:** Predictable solar (low noise), stable demand, no battery
- **Objective:** Minimise cost
- **Grader criteria:** avg_cost_reduction > 30%, no blackouts, avg_renewable_ratio > 0.5
- **Score:** total_reward / max_possible_reward

### Medium: RenewableVariability
- **Episode length:** 96 steps
- **Conditions:** Noisy solar+wind, demand spikes, small battery (capacity=0.3)
- **Objective:** Avoid blackouts while maintaining renewable usage
- **Grader criteria:** blackout_episodes < 3, avg_renewable_ratio > 0.6
- **Score:** 0.4Γ—(renewable) + 0.4Γ—(stability) + 0.2Γ—(cost)

### Hard: CarbonConstrained
- **Episode length:** 96 steps
- **Conditions:** Strict carbon cap (500 kgCOβ‚‚), high volatility (2Γ— noise), limited storage
- **Objective:** Never exceed carbon cap, maintain stability > 0.7
- **Grader criteria:** carbon_budget β‰₯ 0 at all steps, grid_stability > 0.7
- **Score:** 0 if any carbon overrun; else 0.5Γ—(carbon) + 0.3Γ—(cost) + 0.2Γ—(stability)

---

## 7. Physics Model

All functions are **deterministic** given a seed (use `np.random.default_rng(seed)`).

### Solar Output
```
solar = 0.5 + 0.5 Γ— sin(2Ο€ Γ— time_step / 24) + noise Γ— N(0, noise_level)
clamped to [0, 1]
```

### Wind Output
```
wind = random_walk(previous_wind, step_size=0.05) + noise Γ— N(0, noise_level)
clamped to [0, 1]
```

### Demand Curve
```
base = 80 MWh
morning_peak = 30 Γ— exp(-((t - 8)Β² / 8))
evening_peak = 40 Γ— exp(-((t - 18)Β² / 8))
spike = random_spike_probability Γ— spike_magnitude
demand = base + morning_peak + evening_peak + spike
```

### Battery
```
new_level = clamp(level + action Γ— charge_rate, 0, 1)
```

### Carbon Emission
```
emission = fossil_ratio Γ— demand Γ— emission_factor (kg COβ‚‚/MWh)
```

---

## 8. Mercor Sub-theme

The reward function is designed so that **more tokens of agent reasoning correspond
to better reward scaling**. Specifically:
- Actions that consider multiple objectives (cost + carbon + stability) score higher
- Chain-of-thought prompts in baseline.py encourage structured reasoning
- GRPO training rewards longer, more considered outputs with higher task scores

---

## 9. Performance Targets

| Metric | Target |
|--------|--------|
| Episodes/second (numpy sim) | > 10,000 |
| Random baseline score (easy) | 0.15–0.25 |
| Heuristic baseline score (easy) | 0.68–0.74 |
| Trained agent score (easy) | 0.75–0.85 |
| Seed determinism | 100% reproducible |