| import json |
| import os |
| import pandas as pd |
| import numpy as np |
| from uuid import uuid4 |
|
|
| from openenv.core.env_server.interfaces import Environment |
| from openenv.core.env_server.types import State |
|
|
| try: |
| from ..models import BatteryAction, BatteryObservation |
| from ..rl_env import CooptimEnv |
| except (ModuleNotFoundError, ImportError): |
| import sys |
| import os |
| sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| from models import BatteryAction, BatteryObservation |
| from rl_env import CooptimEnv |
| from models import BatteryAction, BatteryObservation |
| from rl_env import CooptimEnv |
|
|
| class BatteryEnvironment(Environment): |
| SUPPORTS_CONCURRENT_SESSIONS: bool = True |
|
|
| def __init__(self, mode="medium"): |
| self._state = State(episode_id=str(uuid4()), step_count=0) |
| self.mode = mode |
| |
| |
| config_path = os.path.join(os.path.dirname(__file__), "..", "config.json") |
| with open(config_path, "r") as f: |
| self.config = json.load(f) |
| |
| |
| if mode == "hard": |
| self.config["battery"]["eta_ch"] = 0.8 |
| self.config["battery"]["eta_dis"] = 0.8 |
| self.config["end_of_day"]["min_soc_mwh"] = 15.0 |
| elif mode == "easy": |
| self.config["throughput_penalty"]["c_eur_per_mwh"] = 0.0 |
| |
| |
| date_rng = pd.date_range(start='2025-01-01', end='2025-01-02', freq='15min') |
| df = pd.DataFrame(index=date_rng) |
| |
| |
| if mode == "easy": |
| df[self.config["columns"]["energy"]] = np.random.uniform(50, 60, size=len(date_rng)) |
| df[self.config["columns"]["fcr"]] = np.random.uniform(200, 300, size=len(date_rng)) |
| elif mode == "medium": |
| df[self.config["columns"]["energy"]] = np.random.uniform(-100, 500, size=len(date_rng)) |
| df[self.config["columns"]["fcr"]] = np.random.uniform(10, 50, size=len(date_rng)) |
| else: |
| df[self.config["columns"]["energy"]] = np.random.uniform(-50, 200, size=len(date_rng)) |
| df[self.config["columns"]["fcr"]] = np.random.uniform(10, 50, size=len(date_rng)) |
| |
| self.gym_env = CooptimEnv(df, self.config) |
|
|
| def reset(self) -> BatteryObservation: |
| self._state = State(episode_id=str(uuid4()), step_count=0) |
| obs_array, _ = self.gym_env.reset() |
| return self._get_obs(obs_array, 0.0, False) |
|
|
| def step(self, action: BatteryAction) -> BatteryObservation: |
| self._state.step_count += 1 |
| |
| act = np.array([action.market_choice, action.p_fraction]) |
| obs_array, reward, terminated, truncated, _ = self.gym_env.step(act) |
| |
| done = terminated or truncated |
| return self._get_obs(obs_array, reward, done) |
| |
| def _get_obs(self, obs_array, reward, done) -> BatteryObservation: |
| idx = min(self.gym_env.current_step, len(self.gym_env.input_data) - 1) |
| prices = self.gym_env.input_data.iloc[idx] |
| e_col = self.gym_env.config["columns"]["energy"] |
| fcr_col = self.gym_env.config["columns"]["fcr"] |
| |
| return BatteryObservation( |
| energy_price=float(prices.get(e_col, 0.0)), |
| fcr_price=float(prices.get(fcr_col, 0.0)), |
| soc=float(self.gym_env.battery.soc), |
| reward=reward, |
| done=done, |
| metadata={"step": self._state.step_count} |
| ) |
|
|
| @property |
| def state(self) -> State: |
| return self._state |
|
|