Skip to content

Commit

Permalink
Merge pull request #658 from BDonnot/bd_dev
Browse files Browse the repository at this point in the history
Some few additions
  • Loading branch information
BDonnot authored Nov 7, 2024
2 parents ef563d5 + 9d02624 commit bd1679d
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 21 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,20 @@ Native multi agents support:

[1.11.0] - 202x-yy-zz
-----------------------
- [FIXED] issue https://github.com/Grid2op/grid2op/issues/657
- [FIXED] missing an import on the `MaskedEnvironment` class
- [ADDED] possibility to set the "thermal limits" when calling `env.reset(..., options={"thermal limit": xxx})`
- [ADDED] possibility to retrieve some structural information about elements with
with `gridobj.get_line_info(...)`, `gridobj.get_load_info(...)`, `gridobj.get_gen_info(...)`
or , `gridobj.get_storage_info(...)`
- [ADDED] codacy badge on the readme
- [IMPROVED] possibility to set the injections values with names
to be consistent with other way to set the actions (*eg* set_bus)
- [IMPROVED] error messages when creating an action which changes the injections
- [IMPROVED] (linked to https://github.com/Grid2op/grid2op/issues/657) the way the
"chronics_hander" in the ObsEnv behaves (it now fully implements the public interface of
a "real" chronic_handler)
- [IMPROVED] error message in the `FromNPY` class when the backend is checked

[1.10.4] - 2024-10-15
-------------------------
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![PyPi_Compat](https://img.shields.io/pypi/pyversions/grid2op.svg)](https://pypi.org/project/Grid2Op/)
[![LICENSE](https://img.shields.io/pypi/l/grid2op.svg)](https://www.mozilla.org/en-US/MPL/2.0/)
[![Documentation Status](https://readthedocs.org/projects/grid2op/badge/?version=latest)](https://grid2op.readthedocs.io/en/latest/?badge=latest)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/3a4e666ba20f4f20b9131e9a6081622c)](https://app.codacy.com/gh/Grid2op/grid2op/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![CircleCI](https://dl.circleci.com/status-badge/img/gh/Grid2op/grid2op/tree/master.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/Grid2op/grid2op/tree/master)
[![discord](https://discord.com/api/guilds/698080905209577513/embed.png)](https://discord.gg/cYsYrPT)
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/grid2op/grid2op.git/master)
Expand Down
123 changes: 123 additions & 0 deletions grid2op/Chronics/_obs_fake_chronics_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Copyright (c) 2019-2024, RTE (https://www.rte-france.com)
# See AUTHORS.txt
# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
# you can obtain one at http://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.

from typing import Dict, Union, Literal

import grid2op
from grid2op.Exceptions import EnvError, ChronicsError
from grid2op.Chronics import ChangeNothing


class _ObsCH(ChangeNothing):
"""
INTERNAL
.. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\
This class is reserved to internal use. Do not attempt to do anything with it.
"""

# properties that should not be accessed
@property
def chronicsClass(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `chronicsClass`")

@property
def path(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `path`")

@property
def _real_data(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_real_data`")

@property
def kwargs(self):
return {}

@kwargs.setter
def kwargs(self, new_value):
raise ChronicsError('Impossible to set the "kwargs" attribute')

@property
def _kwargs(self):
return {}

# functions overriden from the ChronicsHandler class
def forecasts(self):
return []

def get_name(self):
return ""

def next_time_step(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `next_time_step`")

def max_episode_duration(self):
return 0

def seed(self, seed):
""".. warning:: This function is part of the public API of ChronicsHandler but should not do anything here"""
pass

def cleanup_action_space(self):
""".. warning:: This function is part of the public API of ChronicsHandler but should not do anything here"""
pass

# methods overriden from the ChronicsHandler class (__getattr__) so forwarded to the Chronics class
@property
def gridvalueClass(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `gridvalueClass`")

@property
def data(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `data`")

@property
def sep(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `sep`")

@property
def subpaths(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `subpaths`")

@property
def _order(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_order`")

@property
def chunk_size(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `chunk_size`")

@property
def _order_backend_loads(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_order_backend_loads`")

@property
def _order_backend_prods(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_order_backend_prods`")

@property
def _order_backend_lines(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_order_backend_lines`")

@property
def _order_backend_subs(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_order_backend_subs`")

@property
def _names_chronics_to_backend(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_names_chronics_to_backend`")

@property
def _filter(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_filter`")

@property
def _prev_cache_id(self):
raise EnvError("There are no time series in the observation from `obs.simulate`, so no `_prev_cache_id`")

6 changes: 3 additions & 3 deletions grid2op/Chronics/fromNPY.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,12 @@ def initialize(
order_backend_subs,
names_chronics_to_backend=None,
):
assert len(order_backend_prods) == self.n_gen
assert len(order_backend_loads) == self.n_load
assert len(order_backend_prods) == self.n_gen, f"len(order_backend_prods)={len(order_backend_prods)} vs self.n_gen={self.n_gen}"
assert len(order_backend_loads) == self.n_load, f"len(order_backend_loads)={len(order_backend_loads)} vs self.n_load={self.n_load}"
if self.n_line is None:
self.n_line = len(order_backend_lines)
else:
assert len(order_backend_lines) == self.n_line
assert len(order_backend_lines) == self.n_line, f"len(order_backend_lines)={len(order_backend_lines)} vs self.n_line={self.n_line}"

if self._forecasts is not None:
self._forecasts.initialize(
Expand Down
4 changes: 3 additions & 1 deletion grid2op/Environment/_forecast_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.

from typing import Tuple

from grid2op.typing_variables import STEP_INFO_TYPING
from grid2op.Action import BaseAction
from grid2op.Observation import BaseObservation
from grid2op.Environment.environment import Environment
Expand All @@ -23,6 +25,6 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._do_not_erase_local_dir_cls = True

def step(self, action: BaseAction) -> Tuple[BaseObservation, float, bool, dict]:
def step(self, action: BaseAction) -> Tuple[BaseObservation, float, bool, STEP_INFO_TYPING]:
self._highres_sim_counter += 1
return super().step(action)
19 changes: 4 additions & 15 deletions grid2op/Environment/_obsEnv.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2019-2020, RTE (https://www.rte-france.com)
# Copyright (c) 2019-2024, RTE (https://www.rte-france.com)
# See AUTHORS.txt
# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
Expand All @@ -12,26 +12,15 @@
from typing import Dict, Union, Tuple, List, Optional, Any, Literal

import grid2op
from grid2op.Exceptions.envExceptions import EnvError
from grid2op.typing_variables import STEP_INFO_TYPING
from grid2op.dtypes import dt_int, dt_float, dt_bool
from grid2op.Environment.baseEnv import BaseEnv
from grid2op.Exceptions import EnvError
from grid2op.Chronics import ChangeNothing
from grid2op.Chronics._obs_fake_chronics_handler import _ObsCH
from grid2op.Rules import RulesChecker
from grid2op.operator_attention import LinearAttentionBudget


class _ObsCH(ChangeNothing):
"""
INTERNAL
.. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\
This class is reserved to internal use. Do not attempt to do anything with it.
"""

def forecasts(self):
return []
from grid2op.Environment.baseEnv import BaseEnv


class _ObsEnv(BaseEnv):
Expand Down
3 changes: 2 additions & 1 deletion grid2op/Environment/baseEnv.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,8 @@ def _custom_deepcopy_for_copy(self, new_obj, dict_=None):
new_obj.chronics_handler = copy.deepcopy(self.chronics_handler)
# retrieve the "pointer" to the new_obj action space (for initializing the grid)
new_obj.chronics_handler.cleanup_action_space()
new_obj.chronics_handler.action_space = new_obj._helper_action_env
if isinstance(new_obj.chronics_handler, ChronicsHandler):
new_obj.chronics_handler.action_space = new_obj._helper_action_env

# action space
new_obj._action_space = self._action_space.copy()
Expand Down
2 changes: 1 addition & 1 deletion grid2op/Environment/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -2124,7 +2124,7 @@ def get_params_for_runner(self):
else:
msg_ = ("You are probably using a legacy backend class that cannot "
"be copied properly. Please upgrade your backend to the latest version.")
self.logger.warn(msg_)
self.logger.warning(msg_)
warnings.warn(msg_)
res["backend_kwargs"] = None

Expand Down
1 change: 1 addition & 0 deletions grid2op/Environment/maskedEnvironment.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.

import copy
import warnings
import numpy as np
import os
from typing import Tuple, Union, List
Expand Down
70 changes: 70 additions & 0 deletions grid2op/tests/test_issue_657.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright (c) 2024, RTE (https://www.rte-france.com)
# See AUTHORS.txt and https://github.com/Grid2Op/grid2op/pull/319
# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
# you can obtain one at http://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.

from logging import Logger
import unittest
import warnings


from helper_path_test import PATH_DATA_TEST
import grid2op
from grid2op.Exceptions import ChronicsError, EnvError
from grid2op.Action import BaseAction
from grid2op.Environment import BaseEnv
from grid2op.Reward import BaseReward


class WeirdReward(BaseReward):
def __init__(self, logger: Logger = None):
super().__init__(logger)

def __call__(self, action: BaseAction, env:BaseEnv, has_error: bool, is_done: bool, is_illegal: bool, is_ambiguous: bool) -> float:
return len(env.chronics_handler.get_name())


class Issue657Tester(unittest.TestCase):
def setUp(self):
self.env_name = "l2rpn_case14_sandbox"
# create first env
with warnings.catch_warnings():
warnings.filterwarnings("ignore")
self.env = grid2op.make("l2rpn_case14_sandbox", test=True, reward_class=WeirdReward)

def tearDown(self) -> None:
self.env.close()
return super().tearDown()

def test_issue_657(self):
obs = self.env.reset()
obs.simulate(self.env.action_space())
self.env.step(self.env.action_space())

def test_broader_names(self):
obs = self.env.reset()
obs_ch = obs._obs_env.chronics_handler
for attr_nm in self.env.chronics_handler.__dict__:
try:
getattr(obs_ch, attr_nm)
except (EnvError, ChronicsError) as exc_:
# access to some attributes / function might return these type of errors
pass
except AttributeError as exc_:
raise TypeError(f"No know attribute {attr_nm} for obs_chronics_handler") from exc_

for attr_nm in self.env.chronics_handler.real_data.__dict__:
try:
getattr(obs_ch, attr_nm)
except (EnvError, ChronicsError) as exc_:
# access to some attributes / function might return these type of errors
pass
except AttributeError as exc_:
raise TypeError(f"No know attribute {attr_nm} (from real_data / GridValue) for obs_chronics_handler") from exc_


if __name__ == "__main__":
unittest.main()

0 comments on commit bd1679d

Please sign in to comment.