From fdbe08427fe41f8e69ee0ddd714db4bad6e1bd1b Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 12 Jul 2024 09:41:16 +0200 Subject: [PATCH 01/29] new dev version --- CHANGELOG.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ee78f885..a9ffbf9e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -64,6 +64,10 @@ Next release - TODO L2RPN scores as reward (sum loads after the game over and have it in the final reward) - TODO on CI: test only gym, only gymnasium and keep current test for both gym and gymnasium +[1.10.4] - 2024-xx-yy +------------------------- + + [1.10.3] - 2024-07-12 ------------------------- - [BREAKING] `env.chronics_hander.set_max_iter(xxx)` is now a private function. Use From b304dc74e73068e076bfb912d9b95ec9fac20178 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 12 Jul 2024 11:06:50 +0200 Subject: [PATCH 02/29] start dev of 1.10.4 --- CHANGELOG.rst | 2 ++ docs/conf.py | 2 +- grid2op/__init__.py | 2 +- grid2op/tests/test_Runner.py | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a9ffbf9e..ea364a3a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -63,6 +63,8 @@ Next release - TODO in multi-mix increase the reset options with the mix the user wants - TODO L2RPN scores as reward (sum loads after the game over and have it in the final reward) - TODO on CI: test only gym, only gymnasium and keep current test for both gym and gymnasium +- TODO work on the reward class (see https://github.com/rte-france/Grid2Op/issues/584) + [1.10.4] - 2024-xx-yy ------------------------- diff --git a/docs/conf.py b/docs/conf.py index 1abcdb2f..726281bb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ author = 'Benjamin Donnot' # The full version, including alpha/beta/rc tags -release = '1.10.3' +release = '1.10.4.dev0' version = '1.10' diff --git a/grid2op/__init__.py b/grid2op/__init__.py index ea88ee3c..3bb1d7bc 100644 --- a/grid2op/__init__.py +++ b/grid2op/__init__.py @@ -11,7 +11,7 @@ Grid2Op """ -__version__ = '1.10.3' +__version__ = '1.10.4.dev0' __all__ = [ "Action", diff --git a/grid2op/tests/test_Runner.py b/grid2op/tests/test_Runner.py index 0bca1dc7..fbc1cbb6 100644 --- a/grid2op/tests/test_Runner.py +++ b/grid2op/tests/test_Runner.py @@ -520,6 +520,7 @@ def test_backward_compatibility(self): "1.10.0", "1.10.1", "1.10.2", + "1.10.3", ] curr_version = "test_version" assert ( From 98d0fc5c0107c1208d14a91d2883f6abc0bb9f4c Mon Sep 17 00:00:00 2001 From: DEUCE1957 <2246306W@student.gla.ac.uk> Date: Fri, 16 Aug 2024 10:50:13 +0200 Subject: [PATCH 03/29] Debug: Fix Missing Legal and Ambigious Arrays in CompactEpisodeData --- grid2op/Episode/CompactEpisodeData.py | 4 +++- grid2op/Runner/runner.py | 2 +- grid2op/tests/test_CompactEpisodeData.py | 14 +++++++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/grid2op/Episode/CompactEpisodeData.py b/grid2op/Episode/CompactEpisodeData.py index e5cdabf9..f9f59431 100644 --- a/grid2op/Episode/CompactEpisodeData.py +++ b/grid2op/Episode/CompactEpisodeData.py @@ -95,9 +95,11 @@ def __init__(self, env, obs, exp_dir, ep_id:str=None): """ if exp_dir is not None: self.exp_dir = p(exp_dir) + self.exp_dir = self.exp_dir / "CompactEpisodeData" + self.exp_dir.mkdir(parents=False, exist_ok=True) else: self.exp_dir = None - self.array_names = ("actions", "env_actions", "attacks", "observations", "rewards", "other_rewards", "disc_lines", "times") + self.array_names = ("actions", "env_actions", "attacks", "observations", "rewards", "other_rewards", "disc_lines", "times", "legal", "ambiguous") self.space_names = ("observation_space", "action_space", "attack_space", "env_modification_space") if ep_id is None: self.ep_id = env.chronics_handler.get_name() diff --git a/grid2op/Runner/runner.py b/grid2op/Runner/runner.py index 189dbefa..67606750 100644 --- a/grid2op/Runner/runner.py +++ b/grid2op/Runner/runner.py @@ -1728,7 +1728,7 @@ def run( ) else: if add_detailed_output and (_IS_WINDOWS or _IS_MACOS): - self.logger.warn( + self.logger.warning( "Parallel run are not fully supported on windows or macos when " '"add_detailed_output" is True. So we decided ' "to fully deactivate them." diff --git a/grid2op/tests/test_CompactEpisodeData.py b/grid2op/tests/test_CompactEpisodeData.py index 11f9dec7..c9bcde58 100644 --- a/grid2op/tests/test_CompactEpisodeData.py +++ b/grid2op/tests/test_CompactEpisodeData.py @@ -12,7 +12,7 @@ import unittest import grid2op -from grid2op.Agent import OneChangeThenNothing +from grid2op.Agent import DoNothingAgent, OneChangeThenNothing from grid2op.tests.helper_path_test import * from grid2op.Chronics import Multifolder from grid2op.Reward import L2RPNReward @@ -140,6 +140,8 @@ def act(self, observation, reward, done=False): assert len(episode_data.observations) == self.max_iter + 1 assert len(episode_data.env_actions) == self.max_iter assert len(episode_data.attacks) == self.max_iter + assert len(episode_data.ambiguous) == self.max_iter + assert len(episode_data.legal) == self.max_iter def test_one_episode_with_saving(self): f = tempfile.mkdtemp() @@ -163,6 +165,7 @@ def test_collection_wrapper_after_run(self): OneChange = OneChangeThenNothing.gen_next( {"set_bus": {"lines_or_id": [(1, -1)]}} ) + # env.reset(options=) runner = Runner( init_grid_path=self.init_grid_path, init_env_path=self.init_grid_path, @@ -178,9 +181,10 @@ def test_collection_wrapper_after_run(self): agentClass=OneChange, use_compact_episode_data=True, ) - ep_id, ep_name, cum_reward, timestep, max_ts, episode_data = runner.run_one_episode( - max_iter=self.max_iter, detailed_output=True - ) + with warnings.catch_warnings(category=UserWarning, action="ignore"): + *_, episode_data = runner.run_one_episode( + max_iter=self.max_iter, detailed_output=True, + ) # Check that the type of first action is set bus assert episode_data.action_space.from_vect(episode_data.actions[0]).get_types()[2] @@ -257,7 +261,7 @@ def test_with_opponent(self): ) episode_data = CompactEpisodeData.from_disk(path=f, ep_id=res[0][1]) - lines_impacted, subs_impacted = episode_data.attack_space.from_vect(episode_data.attacks[0]).get_topological_impact() + lines_impacted, _ = episode_data.attack_space.from_vect(episode_data.attacks[0]).get_topological_impact() assert lines_impacted[3] def test_can_return_ep_data(self): From d67b5162c5f8c3a28fc581d65f8728488a141cfe Mon Sep 17 00:00:00 2001 From: DEUCE1957 <2246306W@student.gla.ac.uk> Date: Fri, 16 Aug 2024 11:39:43 +0200 Subject: [PATCH 04/29] Debug: Remove Catch Warning --- grid2op/tests/test_CompactEpisodeData.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/grid2op/tests/test_CompactEpisodeData.py b/grid2op/tests/test_CompactEpisodeData.py index c9bcde58..e755b478 100644 --- a/grid2op/tests/test_CompactEpisodeData.py +++ b/grid2op/tests/test_CompactEpisodeData.py @@ -181,10 +181,8 @@ def test_collection_wrapper_after_run(self): agentClass=OneChange, use_compact_episode_data=True, ) - with warnings.catch_warnings(category=UserWarning, action="ignore"): - *_, episode_data = runner.run_one_episode( - max_iter=self.max_iter, detailed_output=True, - ) + *_, episode_data = runner.run_one_episode( + max_iter=self.max_iter, detailed_output=True, # Check that the type of first action is set bus assert episode_data.action_space.from_vect(episode_data.actions[0]).get_types()[2] From bde7b30a5aa2455833d69e40ad70ec22f60832f0 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 29 Aug 2024 11:46:06 +0200 Subject: [PATCH 05/29] few minor fixes --- CHANGELOG.rst | 4 +++- README.md | 7 +++++++ grid2op/Action/_backendAction.py | 1 + grid2op/Backend/backend.py | 12 +++++++----- grid2op/Space/GridObjects.py | 6 ++++-- grid2op/tests/BaseBackendTest.py | 1 + 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ea364a3a..16c8e756 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -68,7 +68,9 @@ Next release [1.10.4] - 2024-xx-yy ------------------------- - +- [FIXED] an issue in the backend: if the backend failed to be + created the `_grid` attribute was set to `None` and not set back to +- [FIXED] the `self.skip_if_needed()` was missing for one of the test suite. [1.10.3] - 2024-07-12 ------------------------- diff --git a/README.md b/README.md index 868150c1..b24db06d 100644 --- a/README.md +++ b/README.md @@ -305,6 +305,8 @@ The complete test suit is run on linux with the latest numpy version on python 3 ### Known issues + +#### Multi processing Due to the underlying behaviour of the "multiprocessing" package on windows based python versions, the "multiprocessing" of the grid2op "Runner" is not supported on windows. This might change in the future, but it is currently not on our priorities. @@ -316,6 +318,11 @@ Sometimes, on some configuration (python version) we do not recommend to use gri If you encounter any trouble, please downgrade to pandas<2.2. This behaviour occured in our continuous integration environment for python >=3.9 but could not be reproduced locally. +#### python 3.11 +Some version of grid2op (*eg* 1.6.3) are not compatible with python 3.10 or 3.11. + +Either use python version 3.8 or 3.9 or upgrade grid2op (1.6.5 works) if that is the case. + ### Perform tests locally Provided that Grid2Op is installed *from source*: diff --git a/grid2op/Action/_backendAction.py b/grid2op/Action/_backendAction.py index 99d61c92..fa4c835f 100644 --- a/grid2op/Action/_backendAction.py +++ b/grid2op/Action/_backendAction.py @@ -613,6 +613,7 @@ def reset(self) -> None: self.load_p.reset() self.load_q.reset() self.storage_power.reset() + # storage unit have their power reset to 0. each step self.storage_power.changed[:] = True self.storage_power.values[:] = 0.0 diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index 3e875e6a..b71c8532 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -611,11 +611,13 @@ def copy(self): start_grid = self._grid self._grid = None - res = copy.deepcopy(self) - res.__class__ = type(self) # somehow deepcopy forget the init class... weird - res._grid = copy.deepcopy(start_grid) - self._grid = start_grid - res._is_loaded = False # i can reload a copy of an environment + try: + res = copy.deepcopy(self) + res.__class__ = type(self) # somehow deepcopy forget the init class... weird + res._grid = copy.deepcopy(start_grid) + finally: + self._grid = start_grid + res._is_loaded = False # i can reload a copy of an environment return res def save_file(self, full_path: Union[os.PathLike, str]) -> None: diff --git a/grid2op/Space/GridObjects.py b/grid2op/Space/GridObjects.py index 5b8d285b..1e0e28c1 100644 --- a/grid2op/Space/GridObjects.py +++ b/grid2op/Space/GridObjects.py @@ -1935,10 +1935,12 @@ def _assign_attr(cls, attrs_list, tp, tp_nm, raise_if_none=False): try: arr2 = np.array(arr).astype(tp) except ValueError as exc_: - raise Grid2OpException(f"Impossible to convert attribute name {el} to {tp_nm}.") from exc_ + raise Grid2OpException(f"Impossible to convert attribute name {el} to {tp_nm} for attr {el}") from exc_ + if len(arr) != len(arr2): + raise Grid2OpException(f"During the conversion to {tp} for attr {el} an error occured (results have not the proper size {len(arr2)} vs {len(arr)})") if (arr != arr2).any(): mask = arr != arr2 - raise Grid2OpException(f"Impossible to safely convert attribute name {el} to {tp_nm}: {arr[mask]} vs {arr2[mask]}.") + raise Grid2OpException(f"Impossible to safely convert attribute name {el} to {tp_nm} for attr {el}: {arr[mask]} vs {arr2[mask]}.") setattr(cls, el, arr2) @classmethod diff --git a/grid2op/tests/BaseBackendTest.py b/grid2op/tests/BaseBackendTest.py index b75f32e3..3ffbea5d 100644 --- a/grid2op/tests/BaseBackendTest.py +++ b/grid2op/tests/BaseBackendTest.py @@ -97,6 +97,7 @@ def get_casefile(self): return "test_case14.json" def test_load_file(self): + self.skip_if_needed() backend = self.make_backend_with_glue_code() path_matpower = self.get_path() case_file = self.get_casefile() From caf5e304b851b753f09c165d754531446d4f9838 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 30 Aug 2024 17:27:57 +0200 Subject: [PATCH 06/29] start refacto of the doc --- docs/developer.rst | 8 + .../{ => developer}/create_an_environment.rst | 0 docs/{ => developer}/createbackend.rst | 0 docs/{ => developer}/env_content.rst | 0 docs/developer/final.rst | 2 + docs/grid2op.rst | 19 +- docs/grid2op_dev/final.rst | 2 + docs/index.rst | 213 ++++++++++++------ docs/topology.rst | 30 +++ docs/user.rst | 24 ++ docs/{ => user}/action.rst | 0 docs/{ => user}/agent.rst | 0 docs/{ => user}/backend.rst | 0 docs/{ => user}/chronics.rst | 0 docs/{ => user}/converter.rst | 0 docs/{ => user}/environment.rst | 2 +- docs/{ => user}/episode.rst | 0 docs/{ => user}/exception.rst | 0 docs/user/final.rst | 2 + docs/{ => user}/observation.rst | 0 docs/{ => user}/opponent.rst | 0 docs/{ => user}/parameters.rst | 0 docs/{ => user}/reward.rst | 0 docs/{ => user}/rules.rst | 0 docs/{ => user}/runner.rst | 0 docs/{ => user}/simulator.rst | 0 docs/{ => user}/space.rst | 0 docs/{ => user}/timeserie_handlers.rst | 0 docs/{ => user}/utils.rst | 0 docs/{ => user}/voltagecontroler.rst | 0 grid2op/Agent/oneChangeThenNothing.py | 7 +- grid2op/Environment/baseEnv.py | 2 +- 32 files changed, 227 insertions(+), 84 deletions(-) create mode 100644 docs/developer.rst rename docs/{ => developer}/create_an_environment.rst (100%) rename docs/{ => developer}/createbackend.rst (100%) rename docs/{ => developer}/env_content.rst (100%) create mode 100644 docs/developer/final.rst create mode 100644 docs/grid2op_dev/final.rst create mode 100644 docs/topology.rst create mode 100644 docs/user.rst rename docs/{ => user}/action.rst (100%) rename docs/{ => user}/agent.rst (100%) rename docs/{ => user}/backend.rst (100%) rename docs/{ => user}/chronics.rst (100%) rename docs/{ => user}/converter.rst (100%) rename docs/{ => user}/environment.rst (99%) rename docs/{ => user}/episode.rst (100%) rename docs/{ => user}/exception.rst (100%) create mode 100644 docs/user/final.rst rename docs/{ => user}/observation.rst (100%) rename docs/{ => user}/opponent.rst (100%) rename docs/{ => user}/parameters.rst (100%) rename docs/{ => user}/reward.rst (100%) rename docs/{ => user}/rules.rst (100%) rename docs/{ => user}/runner.rst (100%) rename docs/{ => user}/simulator.rst (100%) rename docs/{ => user}/space.rst (100%) rename docs/{ => user}/timeserie_handlers.rst (100%) rename docs/{ => user}/utils.rst (100%) rename docs/{ => user}/voltagecontroler.rst (100%) diff --git a/docs/developer.rst b/docs/developer.rst new file mode 100644 index 00000000..f32eb13d --- /dev/null +++ b/docs/developer.rst @@ -0,0 +1,8 @@ +.. toctree:: + :maxdepth: 1 + + developer/env_content + developer/create_an_environment + developer/createbackend + +.. include:: final.rst diff --git a/docs/create_an_environment.rst b/docs/developer/create_an_environment.rst similarity index 100% rename from docs/create_an_environment.rst rename to docs/developer/create_an_environment.rst diff --git a/docs/createbackend.rst b/docs/developer/createbackend.rst similarity index 100% rename from docs/createbackend.rst rename to docs/developer/createbackend.rst diff --git a/docs/env_content.rst b/docs/developer/env_content.rst similarity index 100% rename from docs/env_content.rst rename to docs/developer/env_content.rst diff --git a/docs/developer/final.rst b/docs/developer/final.rst new file mode 100644 index 00000000..f095ba7c --- /dev/null +++ b/docs/developer/final.rst @@ -0,0 +1,2 @@ + +.. include:: ../final.rst diff --git a/docs/grid2op.rst b/docs/grid2op.rst index 02fc4826..f2e6b763 100644 --- a/docs/grid2op.rst +++ b/docs/grid2op.rst @@ -26,7 +26,8 @@ competitions. This platform is still under development. If you notice a bug, let us know with a github issue at `Grid2Op `_ -.. note:: Grid2op do not model any object on the powergrid. It has no internal modeling of the equations of the +.. note:: + Grid2op do not model any object on the powergrid. It has no internal modeling of the equations of the grids, or what kind of solver you need to adopt. On the other hand, grid2op aims at representing the grid in a relatively "high level" point of view: it knows @@ -43,6 +44,7 @@ This platform is still under development. If you notice a bug, let us know with Objectives ----------- + The primary goal of grid2op is to model decision making process in power systems. Indeed, we believe that developing new flexibilities on the grid would make the "energy transition" an easier, less costly process. @@ -72,7 +74,8 @@ Though grid2op has been primarily developed for the L2RPN competitions series, i can also help developing and benchmarking new powerflow solvers for example. Controlling the grid --------------------- +--------------------- + Modeling all what happens in the powergrid would be an extremely difficult task. Grid2op focusing on controls that could be done today by a human (happening with **a frequency of approximately the minute**). It does not aim at simulation really high frequency control that are often automatic today. That being said, such controls @@ -107,8 +110,10 @@ Other "flexibilities" (ways to act on the grid) are coming soon (-: solver uses some physical laws to compute these "weights" from the amount of power produced / absorbed in different part of the grid where generators and loads are connected). + What is modeled in an grid2op environment ------------------------------------------ +------------------------------------------ + The simulator is able to emulate a power grid (of any size or characteristics) subject to a set of temporal injections (productions and consumptions) or maintenance / hazards for discretized time-steps (usually there is the equivalent of *5* minutes between two consective steps). @@ -216,7 +221,8 @@ Module Name Main usage ============================= ========================================================================================= Properties of this environments -------------------------------- +-------------------------------- + The grid2op environments have multiple shared properties: - highly constrained environments: these environments obey physical laws. You cannot directly choose how much @@ -398,7 +404,7 @@ If it fails between "now" and "12 steps from now" reward associated with alert w negative (this is the situation where the agent should have told the human operator "help me"). -Let's replay again (again ?) the same scenario again: same attack, same everything: +Let's replay again (again ?) the same scenario: same attack, same everything: .. code-block:: python @@ -457,7 +463,8 @@ that are available, without any installation thanks to `Binder `_ . Feel free to visit the "getting_started" page for more information and a detailed tour about the issue that grid2op tries to address. -.. note:: As of writing (december 2020) most of these notebooks focus on the "agent" part of grid2op. We would welcome +.. note:: + As of writing (december 2020) most of these notebooks focus on the "agent" part of grid2op. We would welcome any contribution to better explain the other aspect of this platform. .. include:: final.rst diff --git a/docs/grid2op_dev/final.rst b/docs/grid2op_dev/final.rst new file mode 100644 index 00000000..f095ba7c --- /dev/null +++ b/docs/grid2op_dev/final.rst @@ -0,0 +1,2 @@ + +.. include:: ../final.rst diff --git a/docs/index.rst b/docs/index.rst index 42179d3b..e8558fa6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,82 +6,82 @@ .. |episode_example| image:: ./img/grid2op_action.jpg =============================================== -Welcome to Grid2Op's technical documentation! +Welcome to Grid2Op's documentation =============================================== Grid2Op is a pythonic, easy to use framework, to be able to develop, train or evaluate performances of "agent" or -"controller" that acts on a powergrid in different ways. +"controller" that acts on a powergrid. -It is modular and can be use to train reinforcement learning agent or to assess the performance of optimal control -algorithm. +It is modular and can be use to train reinforcement learning agent or to assess the performance of any kind of +agent controlling powergrids (heuristic, machine learning, optimization, mix of everything etc.) -It is flexible and allows the power flow to be computed by the algorithm of your choice. It abstracts the modification -of a powergrid and use this abstraction to compute the **cascading failures** resulting from powerlines disconnection -for example. - -**Features** - - - abstract the computation of the "cascading failures" - - ability to have the same code running with multiple powerflows - - parallel execution of one agent / controller on multiple independent scenarios (multiprocessing) - - fully customisable: this software has been built to be fully customizable to serve different - purposes and not only reinforcement learning, or the L2RPN competition. +It is highly flexible and can be modified in different ways. Grid2Op philosophy -------------------- Grid2Op is a python module that aims to make easier the research on sequential decision making applied to power systems. -This package adopt the "reinforcement learning" point of view and is compatible with the openAI gym programming -interface (see section :ref:`openai-gym` for more information). +This package adopt the "sequential decision making" point of view, for example suited +for training and evaluation "reinforcement learning" agents. -Applied to power system, the "reinforcement learning" framework ask: +It is made of 4 main blocks: -- a "controller" (named Agent) to take an "action" on the powergrid (for example for L2RPN competitions in 2019 - and 2020 these actions consist in modifying the connectivity of the powergrid). -- the "environment" (*a.k.a* the "real world") applies that action on the powergrid, applies some other modifications - and return the next state. +- a module that will embed all the "external" / "exogenous" data, called the "time series" (formelly call "chronics"). + This module for example contain the variation of each and generators. +- a module that will compute the "powerflows", called "backend". It is important to note that grid2op itself + assumes nothing on the powergrid. And in theory you can use any solver that you want to compute the state of + the grid (static / steady state or dynamic / transient or DC modeling or AC modeling etc. with trafo being modled in `T` or in `Pi`) +- a module that "takes decision", called "action" on the grid based on the current grid state, called "observation" and + possible future forecasted grid states +- a module that wrap all the above together and implements a few other feature such as making sure provided actions are "legal" + (meet certain rules) or even emulating (if the module that compute the grid states does not do it) the behaviour of some + "protections". -The goal of grid2op is to model "sequential decision making" that could be made by human operators, for example -changing the configuration of some "substations" as demonstrate in the figure below: +The goal of grid2op is to model "sequential decision making" that could be made by +human operators, for example changing the configuration of some "substations" +as demonstrate in the figure below: |episode_example| -Any kind of "controller" can be implemented using this framework even though it has been inspired by the -"reinforcement learning" community. You can implement some heuristic "controllers" (some examples are available in the -:ref:`agent-module` module description), "controllers" that comes from the Optimization community -(for example "Optimal Power Flow") or -"Model Predictive Control". One of the goal of Grid2Op is to allow everyone to contribute to closing the gap -between all these research communities. +.. note:: + Any kind of "controller" can be implemented using this framework even though it has been inspired by the + "reinforcement learning" community. You can implement some heuristic "controllers" (some examples are available in the + :ref:`agent-module` module description), "controllers" that comes from the Optimization community + (for example "Optimal Power Flow") or + "Model Predictive Control". One of the goal of Grid2Op is to allow everyone to contribute to closing the gap + between all these research communities. + +.. note:: + Consecutive steps are "correlated" in the sense that the action taken + at time `t` is part of the process that defines the state observed at + step `t+1`. More information on this is given in the + :ref:`mdp-doc-module` for example. + Main module content --------------------- +This is where you can go if you want some quick introduction about grid2op +or overall view of what is happing when you "run" a scenario using in grid2op. + .. toctree:: - :maxdepth: 2 - :caption: Quickstart + :maxdepth: 1 + :caption: Overview quickstart grid2op -Environments ---------------- -.. toctree:: - :maxdepth: 2 - :caption: Focus on an "environment" - - available_envs - makeenv - env_content - create_an_environment - dive_into_time_series - data_pipeline - troubleshoot - Usage examples --------------------- + +On this part of the documentation we focus on some usage of grid2op in different +context, for example using optimization or when "wrapping" grid2op into +a gymnsium compatible environment (with only subset of grid2op capabilities) +to ease training of reinforcement learning agents. + .. toctree:: - :maxdepth: 2 - :caption: Learn by Example + :maxdepth: 1 + :caption: Learn with examples optimization gym @@ -91,49 +91,112 @@ Usage examples Modeling ---------- +This part of the documentation focuses on the different +"model" in grid2op. You can find the formal definition +(or at least an attempt at such) for the "Markov Decision Process" +(a mathematical framework used to model sequential decisions making) and +the how the elements accessible in the observation or modifiable in +the action of the agent are represented. + +You can also find some discussion about the topology of the grid (one +of the focus of grid2op) and the representation of the grid as a +graph. + .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :caption: Models mdp modeled_elements grid_graph + topology + +Environments +--------------- + +Here we try to explain rapidly how to load pre existing environment and how some +customization can make grid2op faster (depending on the context) + +.. toctree:: + :maxdepth: 1 + :caption: Focus on an "environment" + + available_envs + makeenv + dive_into_time_series + data_pipeline + troubleshoot Plotting capabilities ---------------------- +Some plotting capabilities of grid2op. + +.. warning:: + This has not been updated + for a long time and is maybe not up to date. + .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :caption: Plot plot -Technical Documentation ----------------------------- +Technical documentation for grid2op users +------------------------------------------- + +This part of the documentation is dedicated to grid2op users. It +covers detailed description of all the modules, classes and their main method +that you, as a user, can use in grid2op. + +The documentation there is mainly descirptive. It explains what is done but +avoid (in general) getting in "too much gory details" on how these +things are done. + +As a starting point, we suggest you first look here before diving more +deeply into the other section of the documentation. .. toctree:: - :maxdepth: 2 - :caption: Technical Documentation - - action - agent - backend - chronics - converter - createbackend - environment - episode - exception - observation - opponent - parameters - reward - rules - runner - simulator - space - timeserie_handlers - utils - voltagecontroler + :maxdepth: 1 + :caption: Technical documentation for grid2op users + + user + +External contribution technical Documentation +------------------------------------------------ + +This part of the documentation is focued on external contribution. +It is best suited if you want to use grid2op as a "core" and extend / modify +it with different elements. + +For example, you might want to : +- use a different solver to compute powerflows + (called :class:`grid2op.Backend.Backend` in grid2op) +- create a new environment +- load time series from a different format than the grid2op default csv +- have an opponent that act differently than the provided ones +- evaluate the performance of the agent differently (change the reward / score function) +- use a different way to control the voltages +- etc. + +The main focuse of these pages of the documentation is put on the +interface and still avoid getting into too much detail on how things +are done internally whenever possible. + +This is the type of documentation you should be looking at if the +current grid2op modelling statisfies you in its vast majority +but if you want to slightly modify one of its component + +.. toctree:: + :maxdepth: 1 + :caption: Technical documentation for grid2op external contributors + + developer + +Developer technical Documentation +----------------------------------- + + +DOC IN PROGRESS... .. include:: final.rst diff --git a/docs/topology.rst b/docs/topology.rst new file mode 100644 index 00000000..b8b80b78 --- /dev/null +++ b/docs/topology.rst @@ -0,0 +1,30 @@ + + +.. _topology-module: + +Dive into the topology "modeling" in grid2op +=================================================================== + +.. warning:: + Work in progress + +What do we call topology +--------------------------------- + +How it is accessible in grid2op +--------------------------------- + +The "topo_vect" vector +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the observation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the action +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Relation with real powergrids +--------------------------------- + +.. include:: final.rst + \ No newline at end of file diff --git a/docs/user.rst b/docs/user.rst new file mode 100644 index 00000000..d1b71542 --- /dev/null +++ b/docs/user.rst @@ -0,0 +1,24 @@ +.. toctree:: + :maxdepth: 1 + + user/action + user/agent + user/backend + user/chronics + user/converter + user/environment + user/episode + user/exception + user/observation + user/opponent + user/parameters + user/reward + user/rules + user/runner + user/simulator + user/space + user/timeserie_handlers + user/utils + user/voltagecontroler + +.. include:: final.rst diff --git a/docs/action.rst b/docs/user/action.rst similarity index 100% rename from docs/action.rst rename to docs/user/action.rst diff --git a/docs/agent.rst b/docs/user/agent.rst similarity index 100% rename from docs/agent.rst rename to docs/user/agent.rst diff --git a/docs/backend.rst b/docs/user/backend.rst similarity index 100% rename from docs/backend.rst rename to docs/user/backend.rst diff --git a/docs/chronics.rst b/docs/user/chronics.rst similarity index 100% rename from docs/chronics.rst rename to docs/user/chronics.rst diff --git a/docs/converter.rst b/docs/user/converter.rst similarity index 100% rename from docs/converter.rst rename to docs/user/converter.rst diff --git a/docs/environment.rst b/docs/user/environment.rst similarity index 99% rename from docs/environment.rst rename to docs/user/environment.rst index b90c0c8b..5c71eb3e 100644 --- a/docs/environment.rst +++ b/docs/user/environment.rst @@ -151,7 +151,7 @@ Feel free to consult the documentation of the :func:`Environment.reset` function for more information (this doc might be outdated, the one of the function should be more up to date with the code). -.. info:: +.. note:: In the near future (next few releases) we will also attempt to make the customization of the `parameters` or the `skip number of steps`, `maximum duration of the scenarios` also available in `env.reset()` options. diff --git a/docs/episode.rst b/docs/user/episode.rst similarity index 100% rename from docs/episode.rst rename to docs/user/episode.rst diff --git a/docs/exception.rst b/docs/user/exception.rst similarity index 100% rename from docs/exception.rst rename to docs/user/exception.rst diff --git a/docs/user/final.rst b/docs/user/final.rst new file mode 100644 index 00000000..79beb619 --- /dev/null +++ b/docs/user/final.rst @@ -0,0 +1,2 @@ + +.. include:: ../final.rst \ No newline at end of file diff --git a/docs/observation.rst b/docs/user/observation.rst similarity index 100% rename from docs/observation.rst rename to docs/user/observation.rst diff --git a/docs/opponent.rst b/docs/user/opponent.rst similarity index 100% rename from docs/opponent.rst rename to docs/user/opponent.rst diff --git a/docs/parameters.rst b/docs/user/parameters.rst similarity index 100% rename from docs/parameters.rst rename to docs/user/parameters.rst diff --git a/docs/reward.rst b/docs/user/reward.rst similarity index 100% rename from docs/reward.rst rename to docs/user/reward.rst diff --git a/docs/rules.rst b/docs/user/rules.rst similarity index 100% rename from docs/rules.rst rename to docs/user/rules.rst diff --git a/docs/runner.rst b/docs/user/runner.rst similarity index 100% rename from docs/runner.rst rename to docs/user/runner.rst diff --git a/docs/simulator.rst b/docs/user/simulator.rst similarity index 100% rename from docs/simulator.rst rename to docs/user/simulator.rst diff --git a/docs/space.rst b/docs/user/space.rst similarity index 100% rename from docs/space.rst rename to docs/user/space.rst diff --git a/docs/timeserie_handlers.rst b/docs/user/timeserie_handlers.rst similarity index 100% rename from docs/timeserie_handlers.rst rename to docs/user/timeserie_handlers.rst diff --git a/docs/utils.rst b/docs/user/utils.rst similarity index 100% rename from docs/utils.rst rename to docs/user/utils.rst diff --git a/docs/voltagecontroler.rst b/docs/user/voltagecontroler.rst similarity index 100% rename from docs/voltagecontroler.rst rename to docs/user/voltagecontroler.rst diff --git a/grid2op/Agent/oneChangeThenNothing.py b/grid2op/Agent/oneChangeThenNothing.py index 3b8aa229..e981c9ba 100644 --- a/grid2op/Agent/oneChangeThenNothing.py +++ b/grid2op/Agent/oneChangeThenNothing.py @@ -12,6 +12,10 @@ class OneChangeThenNothing(BaseAgent): """ + .. warning:: + As of grid2op 1.10.2, this class has been deprecated. Please use `env.reset(options={"init state": THE_INITIAl_CHANGE})` + instead. + This is a specific kind of BaseAgent. It does an BaseAction (possibly non empty) at the first time step and then does nothing. @@ -26,6 +30,7 @@ class OneChangeThenNothing(BaseAgent): Examples --------- + We advise to use this class as following .. code-block:: python @@ -50,7 +55,7 @@ class OneChangeThenNothing(BaseAgent): # run 2 episode with it res_2 = runner.run(nb_episode=2) - Notes: + Notes ------ After grid2op 1.10.2, this class has been deprecated. A cleaner alternative diff --git a/grid2op/Environment/baseEnv.py b/grid2op/Environment/baseEnv.py index 440d89e9..f45a733f 100644 --- a/grid2op/Environment/baseEnv.py +++ b/grid2op/Environment/baseEnv.py @@ -4448,7 +4448,7 @@ def classes_are_in_files(self) -> bool: Whether the classes created when this environment has been made are store on the hard drive (will return `True`) or not. - .. info:: + .. note:: This will become the default behaviour in future grid2op versions. See :ref:`troubleshoot_pickle` for more information. From bc61ead89a3919c7d9cb9943b7025dac54fb8a75 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 3 Sep 2024 16:22:00 +0200 Subject: [PATCH 07/29] improving doc --- docs/data_pipeline.rst | 6 +- docs/developer/create_an_environment.rst | 10 +- docs/developer/createbackend.rst | 45 ++-- docs/developer/env_content.rst | 10 +- docs/grid2op_dev.rst | 7 + docs/grid2op_dev/action.rst | 7 + docs/grid2op_dev/observation.rst | 7 + docs/index.rst | 45 +++- docs/mdp.rst | 6 +- docs/special.rst | 69 +----- docs/topology.rst | 291 ++++++++++++++++++++++- docs/troubleshoot.rst | 1 + docs/user/environment.rst | 6 +- docs/user/reward.rst | 2 +- docs/user/special.rst | 69 ++++++ getting_started/11_ray_integration.ipynb | 2 +- grid2op/Action/_backendAction.py | 6 +- grid2op/Action/baseAction.py | 10 +- grid2op/Agent/oneChangeThenNothing.py | 10 +- grid2op/Chronics/fromMultiEpisodeData.py | 2 +- grid2op/Environment/environment.py | 7 +- grid2op/gym_compat/gymenv.py | 27 +++ grid2op/tests/_aux_test_gym_compat.py | 4 +- grid2op/tests/automatic_classes.py | 18 +- 24 files changed, 523 insertions(+), 144 deletions(-) create mode 100644 docs/grid2op_dev.rst create mode 100644 docs/grid2op_dev/action.rst create mode 100644 docs/grid2op_dev/observation.rst create mode 100644 docs/user/special.rst diff --git a/docs/data_pipeline.rst b/docs/data_pipeline.rst index 1792e834..0c316667 100644 --- a/docs/data_pipeline.rst +++ b/docs/data_pipeline.rst @@ -55,9 +55,9 @@ Results are reported in the table below: ============================== ================ =================== Method used memory footprint time to perform (s) ============================== ================ =================== -Nothing (see `Basic Usage`_) low 44.6 -set_chunk (see `Chunk size`_) ultra low 26.8 -`MultifolderWithCache`_ high 11.0 +Nothing (see Basic Usage ) low 44.6 +set_chunk (see `Chunk size`_ ) ultra low 26.8 +`MultifolderWithCache`_ high 11.0 ============================== ================ =================== As you can see, the default usage uses relatively little memory but takes a while to compute (almost 45s to perform diff --git a/docs/developer/create_an_environment.rst b/docs/developer/create_an_environment.rst index e0e36f8d..2e7dcfb8 100644 --- a/docs/developer/create_an_environment.rst +++ b/docs/developer/create_an_environment.rst @@ -1,8 +1,8 @@ -.. |l2rpn_case14_sandbox_layout| image:: ./img/l2rpn_case14_sandbox_layout.png -.. |R2_full_grid| image:: ./img/R2_full_grid.png -.. |l2rpn_neurips_2020_track1_layout| image:: ./img/l2rpn_neurips_2020_track1_layout.png -.. |l2rpn_neurips_2020_track2_layout| image:: ./img/l2rpn_neurips_2020_track2_layout.png -.. |l2rpn_wcci_2022_layout| image:: ./img/l2rpn_wcci_2022_layout.png +.. |l2rpn_case14_sandbox_layout| image:: ../img/l2rpn_case14_sandbox_layout.png +.. |R2_full_grid| image:: ../img/R2_full_grid.png +.. |l2rpn_neurips_2020_track1_layout| image:: ../img/l2rpn_neurips_2020_track1_layout.png +.. |l2rpn_neurips_2020_track2_layout| image:: ../img/l2rpn_neurips_2020_track2_layout.png +.. |l2rpn_wcci_2022_layout| image:: ../img/l2rpn_wcci_2022_layout.png Possible workflow to create an environment from existing time series diff --git a/docs/developer/createbackend.rst b/docs/developer/createbackend.rst index db767c27..93d0fb9c 100644 --- a/docs/developer/createbackend.rst +++ b/docs/developer/createbackend.rst @@ -21,26 +21,26 @@ .. _line_or_pos_topo_vect: ./space.html#grid2op.Space.GridObjects.line_or_pos_topo_vect .. _line_ex_pos_topo_vect: ./space.html#grid2op.Space.GridObjects.line_ex_pos_topo_vect -.. |5subs_grid_layout| image:: ./img/5subs_grid_layout.jpg -.. |5subs_grid_1_sub| image:: ./img/5subs_grid_1_sub.jpg -.. |5subs_grid_2_loads| image:: ./img/5subs_grid_2_loads.jpg -.. |5subs_grid_3_gens| image:: ./img/5subs_grid_3_gens.jpg -.. |5subs_grid_4_lines| image:: ./img/5subs_grid_4_lines.jpg -.. |5subs_grid_5_obj_in_sub| image:: ./img/5subs_grid_5_obj_in_sub.jpg -.. |5subs_grid_layout_with_repr| image:: ./img/5subs_grid_layout_with_repr.jpg -.. |5subs_grid_n_el| image:: ./img/5subs_grid_n_el.jpg -.. |5subs_grid_5_sub_i| image:: ./img/5subs_grid_5_sub_i.jpg -.. |5subs_grid_load_to_subid| image:: ./img/5subs_grid_load_to_subid.jpg -.. |5subs_grid_el_to_subid| image:: ./img/5subs_grid_el_to_subid.jpg -.. |5subs_grid_sub0| image:: ./img/5subs_grid_sub0.jpg -.. |5subs_grid_sub0_final| image:: ./img/5subs_grid_sub0_final.jpg -.. |5subs_grid_sub1_final| image:: ./img/5subs_grid_sub1_final.jpg -.. |5subs_grid_loads_info| image:: ./img/5subs_grid_loads_info.jpg -.. |5subs_grid_sub1_topo| image:: ./img/5subs_grid_sub1_topo.jpg -.. |5subs_grid_sub1_2_topo| image:: ./img/5subs_grid_sub1_2_topo.jpg -.. |5subs_grid_suball_topo| image:: ./img/5subs_grid_suball_topo.jpg -.. |5subs_grid_ex_disco| image:: ./img/5subs_grid_ex_disco.jpg -.. |5subs_grid_ex_2buses| image:: ./img/5subs_grid_ex_2buses.jpg +.. |5subs_grid_layout| image:: ../img/5subs_grid_layout.jpg +.. |5subs_grid_1_sub| image:: ../img/5subs_grid_1_sub.jpg +.. |5subs_grid_2_loads| image:: ../img/5subs_grid_2_loads.jpg +.. |5subs_grid_3_gens| image:: ../img/5subs_grid_3_gens.jpg +.. |5subs_grid_4_lines| image:: ../img/5subs_grid_4_lines.jpg +.. |5subs_grid_5_obj_in_sub| image:: ../img/5subs_grid_5_obj_in_sub.jpg +.. |5subs_grid_layout_with_repr| image:: ../img/5subs_grid_layout_with_repr.jpg +.. |5subs_grid_n_el| image:: ../img/5subs_grid_n_el.jpg +.. |5subs_grid_5_sub_i| image:: ../img/5subs_grid_5_sub_i.jpg +.. |5subs_grid_load_to_subid| image:: ../img/5subs_grid_load_to_subid.jpg +.. |5subs_grid_el_to_subid| image:: ../img/5subs_grid_el_to_subid.jpg +.. |5subs_grid_sub0| image:: ../img/5subs_grid_sub0.jpg +.. |5subs_grid_sub0_final| image:: ../img/5subs_grid_sub0_final.jpg +.. |5subs_grid_sub1_final| image:: ../img/5subs_grid_sub1_final.jpg +.. |5subs_grid_loads_info| image:: ../img/5subs_grid_loads_info.jpg +.. |5subs_grid_sub1_topo| image:: ../img/5subs_grid_sub1_topo.jpg +.. |5subs_grid_sub1_2_topo| image:: ../img/5subs_grid_sub1_2_topo.jpg +.. |5subs_grid_suball_topo| image:: ../img/5subs_grid_suball_topo.jpg +.. |5subs_grid_ex_disco| image:: ../img/5subs_grid_ex_disco.jpg +.. |5subs_grid_ex_2buses| image:: ../img/5subs_grid_ex_2buses.jpg .. _create-backend-module: @@ -689,7 +689,8 @@ These functions can be used in the following manner: And of course you do the same for generators and both ends of each powerline. -.. note:: About powerline, grid2op adopts the following convention: a powerline **cannot** be connected on one side +.. note:: + About powerline, grid2op adopts the following convention: a powerline **cannot** be connected on one side and disconnected on the other. That being said, it's still possible to connect the extremity of a powerline "alone" on a busbar, which will have @@ -697,7 +698,7 @@ And of course you do the same for generators and both ends of each powerline. .. _vector-orders-create-backend: -***_infos() : Read back the results (flows, voltages etc.) +\*\*\*_infos() : Read back the results (flows, voltages etc.) -------------------------------------------------------------- This last "technical" part concerns what can be refer to as "getters" from the backend. These functions allow to read back the state of the grid and expose its results to grid2op in a standardize manner. diff --git a/docs/developer/env_content.rst b/docs/developer/env_content.rst index 96af7a4e..c4351185 100644 --- a/docs/developer/env_content.rst +++ b/docs/developer/env_content.rst @@ -1,9 +1,9 @@ -.. |l2rpn_case14_sandbox_layout| image:: ./img/l2rpn_case14_sandbox_layout.png -.. |R2_full_grid| image:: ./img/R2_full_grid.png -.. |l2rpn_neurips_2020_track1_layout| image:: ./img/l2rpn_neurips_2020_track1_layout.png -.. |l2rpn_neurips_2020_track2_layout| image:: ./img/l2rpn_neurips_2020_track2_layout.png -.. |l2rpn_wcci_2022_layout| image:: ./img/l2rpn_wcci_2022_layout.png +.. |l2rpn_case14_sandbox_layout| image:: ../img/l2rpn_case14_sandbox_layout.png +.. |R2_full_grid| image:: ../img/R2_full_grid.png +.. |l2rpn_neurips_2020_track1_layout| image:: ../img/l2rpn_neurips_2020_track1_layout.png +.. |l2rpn_neurips_2020_track2_layout| image:: ../img/l2rpn_neurips_2020_track2_layout.png +.. |l2rpn_wcci_2022_layout| image:: ../img/l2rpn_wcci_2022_layout.png Content of an environment diff --git a/docs/grid2op_dev.rst b/docs/grid2op_dev.rst new file mode 100644 index 00000000..6beedb0d --- /dev/null +++ b/docs/grid2op_dev.rst @@ -0,0 +1,7 @@ +.. toctree:: + :maxdepth: 1 + + grid2op_dev/action + grid2op_dev/observation + +.. include:: final.rst diff --git a/docs/grid2op_dev/action.rst b/docs/grid2op_dev/action.rst new file mode 100644 index 00000000..67cfd919 --- /dev/null +++ b/docs/grid2op_dev/action.rst @@ -0,0 +1,7 @@ +How to add a new type of action +=================================== + +Work in progress ! + + +.. include:: final.rst \ No newline at end of file diff --git a/docs/grid2op_dev/observation.rst b/docs/grid2op_dev/observation.rst new file mode 100644 index 00000000..989fce42 --- /dev/null +++ b/docs/grid2op_dev/observation.rst @@ -0,0 +1,7 @@ +How to add a new attribute to the observation +============================================== + +Work in progress ! + + +.. include:: final.rst \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst index e8558fa6..59278735 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -162,14 +162,16 @@ deeply into the other section of the documentation. user -External contribution technical Documentation ------------------------------------------------- + +Technical documentation for grid2op "external" contributions +---------------------------------------------------------------- This part of the documentation is focued on external contribution. It is best suited if you want to use grid2op as a "core" and extend / modify it with different elements. For example, you might want to : + - use a different solver to compute powerflows (called :class:`grid2op.Backend.Backend` in grid2op) - create a new environment @@ -185,18 +187,47 @@ are done internally whenever possible. This is the type of documentation you should be looking at if the current grid2op modelling statisfies you in its vast majority -but if you want to slightly modify one of its component +but if you want to slightly modify one of its component. + +.. note:: + This type of contribution can be developed and hosted in a different + github repository than grid2op (*eg* lightsim2grid, another faster backend + is hosted on https://github.com/bdonnot/lightsim2grid.git) + + Feel free to contact us if you have done such an "external contribution" so + that we can at least reference it in the documentation. .. toctree:: :maxdepth: 1 - :caption: Technical documentation for grid2op external contributors + :caption: Technical documentation for grid2op "external" contributions developer -Developer technical Documentation ------------------------------------ +Technical documentation for grid2op developers +------------------------------------------------- + +This part of the documentation also focuses on external contribution. It +focuses on the core of grid2op. If you want to : + +- change the grid2op internal representation +- add a functionality to grid2op (*eg* a new type of actions or a new attribute to the observation) +- change the representatino of this or this elements +- etc. + +We encourage you to get in touch with us for such development. + +.. note:: + Most of the work falling into this category should probably be + integrated into the main grid2op repository. + +.. warning:: + DOC IN PROGRESS... + +.. toctree:: + :maxdepth: 1 + :caption: Technical documentation for grid2op developers -DOC IN PROGRESS... + grid2op_dev .. include:: final.rst diff --git a/docs/mdp.rst b/docs/mdp.rst index 64e6ed46..c889287e 100644 --- a/docs/mdp.rst +++ b/docs/mdp.rst @@ -1,5 +1,9 @@ -.. include:: special.rst +.. for the color +.. include:: special.rst +.. for the observation attributes +.. include:: user/special.rst + .. _mdp-doc-module: Dive into grid2op sequential decision process diff --git a/docs/special.rst b/docs/special.rst index 44bcdfb8..5b210239 100644 --- a/docs/special.rst +++ b/docs/special.rst @@ -38,71 +38,4 @@ .. role:: center .. role:: left .. role:: right -.. (c) Lilian Besson, 2011-2016, https://bitbucket.org/lbesson/web-sphinx/ - -.. _n_gen: ./space.html#grid2op.Space.GridObjects.n_gen -.. _n_load: ./space.html#grid2op.Space.GridObjects.n_load -.. _n_line: ./space.html#grid2op.Space.GridObjects.n_line -.. _n_sub: ./space.html#grid2op.Space.GridObjects.n_sub -.. _n_storage: ./space.html#grid2op.Space.GridObjects.n_storage -.. _dim_topo: ./space.html#grid2op.Space.GridObjects.dim_topo -.. _dim_alarms: ./space.html#grid2op.Space.GridObjects.dim_alarms -.. _dim_alerts: ./space.html#grid2op.Space.GridObjects.dim_alerts -.. _year: ./observation.html#grid2op.Observation.BaseObservation.year -.. _month: ./observation.html#grid2op.Observation.BaseObservation.month -.. _day: ./observation.html#grid2op.Observation.BaseObservation.day -.. _hour_of_day: ./observation.html#grid2op.Observation.BaseObservation.hour_of_day -.. _minute_of_hour: ./observation.html#grid2op.Observation.BaseObservation.minute_of_hour -.. _day_of_week: ./observation.html#grid2op.Observation.BaseObservation.day_of_week -.. _gen_p: ./observation.html#grid2op.Observation.BaseObservation.gen_p -.. _gen_q: ./observation.html#grid2op.Observation.BaseObservation.gen_q -.. _gen_v: ./observation.html#grid2op.Observation.BaseObservation.gen_v -.. _load_p: ./observation.html#grid2op.Observation.BaseObservation.load_p -.. _load_q: ./observation.html#grid2op.Observation.BaseObservation.load_q -.. _load_v: ./observation.html#grid2op.Observation.BaseObservation.load_v -.. _p_or: ./observation.html#grid2op.Observation.BaseObservation.p_or -.. _q_or: ./observation.html#grid2op.Observation.BaseObservation.q_or -.. _v_or: ./observation.html#grid2op.Observation.BaseObservation.v_or -.. _a_or: ./observation.html#grid2op.Observation.BaseObservation.a_or -.. _p_ex: ./observation.html#grid2op.Observation.BaseObservation.p_ex -.. _q_ex: ./observation.html#grid2op.Observation.BaseObservation.q_ex -.. _v_ex: ./observation.html#grid2op.Observation.BaseObservation.v_ex -.. _a_ex: ./observation.html#grid2op.Observation.BaseObservation.a_ex -.. _rho: ./observation.html#grid2op.Observation.BaseObservation.rho -.. _topo_vect: ./observation.html#grid2op.Observation.BaseObservation.topo_vect -.. _line_status: ./observation.html#grid2op.Observation.BaseObservation.line_status -.. _timestep_overflow: ./observation.html#grid2op.Observation.BaseObservation.timestep_overflow -.. _time_before_cooldown_line: ./observation.html#grid2op.Observation.BaseObservation.time_before_cooldown_line -.. _time_before_cooldown_sub: ./observation.html#grid2op.Observation.BaseObservation.time_before_cooldown_sub -.. _time_next_maintenance: ./observation.html#grid2op.Observation.BaseObservation.time_next_maintenance -.. _duration_next_maintenance: ./observation.html#grid2op.Observation.BaseObservation.duration_next_maintenance -.. _target_dispatch: ./observation.html#grid2op.Observation.BaseObservation.target_dispatch -.. _actual_dispatch: ./observation.html#grid2op.Observation.BaseObservation.actual_dispatch -.. _storage_charge: ./observation.html#grid2op.Observation.BaseObservation.storage_charge -.. _storage_power_target: ./observation.html#grid2op.Observation.BaseObservation.storage_power_target -.. _storage_power: ./observation.html#grid2op.Observation.BaseObservation.storage_power -.. _storage_theta: ./observation.html#grid2op.Observation.BaseObservation.storage_theta -.. _gen_p_before_curtail: ./observation.html#grid2op.Observation.BaseObservation.gen_p_before_curtail -.. _curtailment: ./observation.html#grid2op.Observation.BaseObservation.curtailment -.. _curtailment_limit: ./observation.html#grid2op.Observation.BaseObservation.curtailment_limit -.. _is_alarm_illegal: ./observation.html#grid2op.Observation.BaseObservation.is_alarm_illegal -.. _time_since_last_alarm: ./observation.html#grid2op.Observation.BaseObservation.time_since_last_alarm -.. _last_alarm: ./observation.html#grid2op.Observation.BaseObservation.last_alarm -.. _attention_budget: ./observation.html#grid2op.Observation.BaseObservation.attention_budget -.. _max_step: ./observation.html#grid2op.Observation.BaseObservation.max_step -.. _current_step: ./observation.html#grid2op.Observation.BaseObservation.current_step -.. _delta_time: ./observation.html#grid2op.Observation.BaseObservation.delta_time -.. _gen_margin_up: ./observation.html#grid2op.Observation.BaseObservation.gen_margin_up -.. _gen_margin_down: ./observation.html#grid2op.Observation.BaseObservation.gen_margin_down -.. _curtailment_mw: ./observation.html#grid2op.Observation.BaseObservation.curtailment_mw -.. _theta_or: ./observation.html#grid2op.Observation.BaseObservation.theta_or -.. _theta_ex: ./observation.html#grid2op.Observation.BaseObservation.theta_ex -.. _gen_theta: ./observation.html#grid2op.Observation.BaseObservation.gen_theta -.. _load_theta: ./observation.html#grid2op.Observation.BaseObservation.load_theta -.. _active_alert: ./observation.html#grid2op.Observation.BaseObservation.active_alert -.. _time_since_last_alert: ./observation.html#grid2op.Observation.BaseObservation.time_since_last_alert -.. _alert_duration: ./observation.html#grid2op.Observation.BaseObservation.alert_duration -.. _total_number_of_alert: ./observation.html#grid2op.Observation.BaseObservation.total_number_of_alert -.. _time_since_last_attack: ./observation.html#grid2op.Observation.BaseObservation.time_since_last_attack -.. _was_alert_used_after_attack: ./observation.html#grid2op.Observation.BaseObservation.was_alert_used_after_attack -.. _attack_under_alert: ./observation.html#grid2op.Observation.BaseObservation.attack_under_alert +.. (c) Lilian Besson, 2011-2016, https://bitbucket.org/lbesson/web-sphinx/ \ No newline at end of file diff --git a/docs/topology.rst b/docs/topology.rst index b8b80b78..2c81c722 100644 --- a/docs/topology.rst +++ b/docs/topology.rst @@ -1,30 +1,315 @@ -.. _topology-module: +.. _topology-modeling-module: Dive into the topology "modeling" in grid2op =================================================================== +In this page of the documentation we dive into the description of the +"topology" of the grid in grid2op. + .. warning:: Work in progress What do we call topology --------------------------------- +In the powersystem literature "topology" might refer to different things and +be encoded in different ways, for example there is the "nodal topology" which +is often use by the physical solvers (backends in case of grid2op), or there +is the "detailed topoology" which rather uses swithes, breakers etc. + +.. note:: + The "nodal topology" is a graph that meets the Kirchhoff Current Laws. + + The vertex of this graph are the "electrical node". These vertices contains, + from grid2op point of view, one or more "elements" of grid (side of powerline, + loads, generators, storage units etc.) that can be directly connected together. + + The edges of this graph are merging of 1 or more powerlines that connects two + vertices together. + +.. note:: + The "detailed topology" is more complicated. It also represents a graph but + at a more granular level. + + In real powergrid, elements of the grid are connected together with switches / + breakers / couplers etc. that can be either closed or opened. + + In real grid, the "topology" is controled with actions on these switches / + breakers / couplers etc. + +In the case of grid2op we adopt another representation for this "topology". +It is more detailed than containing purely the "nodal" information but +does not model the switches. + +.. note:: + TODO have some illustrative examples here of "nodal" and "detailed" + + For example inspired from https://www.powsybl.org/pages/documentation/developer/tutorials/topology.html + +.. note:: + This explanation is correct as of writing (September 2024) but there are + some efforts to use a more detailed representation of the topology in + the form of `switches` in a branch in grid2op. + +In plain English, the "topology" is a representation of the powergrid +as a graph with the edges being the powerlines / transformers and the +nodes being some "things" having attributes such that the power produced +or consumed at this nodes. + +As often in computer science, there are different ways to informatically +represent a graph. + +We chose to encode this "graph" in the form of a vector. This vector, +often called the "topology vector" or "topo vect" has the following properties: + +- it has as many component as the number of elements (load, generator, side of powerline + or transformer, storage unit etc.) present in the grid. Each component of this vector + provide information about the state of an unique element of the grid. +- it is a vector of integer (`=> -1`) with the following convention: + + - if a given component is `-1` this means the relevant element is connected + - if a given component is `1` it means the element of the grid represented by this component is connected to "busbar 1" + - if a given component is `2` it means the element of the grid is connected to "busbar 2" + - etc. (for all `k >= 1` if a given component is `k` then it means the relevant element of the grid is connected to busbar `k`) + - the component can never be `<= -2` nor `0` + +This "topology vector" can change depending on the state of the grid. + +Another "fixed" / "constant" / "immutable" information is needed to retrieve the +"topology" of the grid. It concerns the mapping between each elements of +the grid and the "substation" to which it "connected". + +.. note:: + The same word "connected" used here means two different things. + + The "connected to a substation" is independant of the status "connected / disconnected" + of an element. + + Let's suppose the city of Nowhere is modeled by a load in the grid: + + - "*Nowhere is connected to substation 5*" means that + the powergrid is made in such a way that the physical place where the transformer + that powers the city of "Nowhere" is in a location that is called "substation 5". + It can never be "disconnected" from substation 5 (this would mean the city ceased + to exist) nor can it be "connected to substation 1 [or 2, or 3, or 4, etc.]" + (this would mean this city magically change its geographical location and is + moved from a few hundred of miles / km) + - "*Nowhere is disconnected*" means that the transformer + powering the city of Nowhere is switched-off (blackout in this city) + - "*Nowhere is connected to busbar 1*" means that + within the "substation 5" there is an object called "busbar 1" and that + there is a "direct electrical path" (made of all closed switches) that + connects the transformer of the city of Nowhere to this "busbar 1" + +.. note:: + The mapping between each object and the substation to which it is connected + does not change. This is why it is not stored in the topology vector. + +This mapping is loadedonce and for all from the grid file by the "backend" at the +creation of the environment. + +With both these information the "nodal topology" can be computed as followed: + +- if an object is disconnected (associated component to the topology vector is `-1`) + it is not connected (no kidding...) and can be ommitted when building the graph +- if two obejcts `o_i` and `o_j` are not "connected to the same substation" they + are not connected to the same vertex of the graph. +- if two objects `o_i` and `o_j` are "connected to the same substation" they are + part of the same "electrical node" (also called bus) if (and only if) + the associated component + of the "topoolgy vector" has the same integer. For example if the component + of the topology vector for `o_i` is 2 and the component for `o_j` is 1 + they are NOT connected together. But if its component is 3 for `o_i` + and 3 for `o_j` they are connected together. + +.. note:: + As of writing, if a load or a generator is disconnected, there is a "game over". + + +Why the "switches" are not modled by default +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. warning:: + Doc in progress... + + +Switches are not in most synthetic grids +++++++++++++++++++++++++++++++++++++++++ + +There are no switches in most IEEE test cases which serve as reference +for most of grid2op environment and are widely used in the literature. +Forcing switches in grid2op would mean inventing them on these grid, which is +not necessary. When creating an open source environment, it would be +mandatory to come up with a layout for each substation of the +fictive grid. And there are many different "substation layout" possible ( +see *eg* https://www.technomaxme.com/understanding-busbar-systems/ ) + + +Switches will not make the problem more realistic ++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Switches information is too complicated to be manipulated correctly if we +consider time dependant states.Switches would also make the rules much more difficult to +implement. For example, in real time, some breakers can be opened / closed +while under charge but some other might not. This means an agent that would +operate the grid would have to anticipate to "pre configure" the switches +"before" real time if it wants to adopt this and that. We believe that this +is too complicated for an agent to do yet [TODO more info about that needed] + +Closer to human reasoning ++++++++++++++++++++++++++++ + +As for our experience, human operators do not think in terms of opening / closing +switches. The first target a given "topology": these two elements connected together, +these other three also, but not with the previous ones etc. And then they +use their expertise to find a combination of breakers which match what +they want to achieve. We believe that the added value of AI is greater in the +first step (find the good nodal topology) so we decided to entirely skip the second +one (which, we think, can be solved by optimization routines or heuristics) + +Smaller action space ++++++++++++++++++++++ + +The problem we expose in grid2op is far from being solved (to our knowledge). And +we believe that making multiple consecutive small steps into the right direction is better than +modeling every bit of complexity of the "real" problem and then find a solution +to this really hard problem. Removing switches is a way to reduce the action space. Indeed, +if you consider the "grid2op standard" : "*maximum 2 independant buses per substation*" and +a substation with 4 elements. You need: + +- an action space of **4 bits** with current grid2op modeling + (one bit per elements) +- whereas you would need to "build" the substation layout, for example: + you create two busbars (one for each independant buses), then + one switch connecting each of the 4 elements to both busbars plus possibly a + breaker between both busbars. Making **9 switches** / breakers in total. + +.. note:: + Both type of action spaces would represent the same reality. This means that + in the second case lots of "possible action" would be ambiguous or lead finally + to the "do nothing" action, which is not ideal. + +In this case, adding switches would more than double (in this case) the size of the action space +(4 btis without, 9 bits with them). + +Simpler action and observaton spaces ++++++++++++++++++++++++++++++++++++++ + +One of the main issue with "topology" is that the same topology can be encoded differently. + +With the proposed grid2op encoding this problem is not totally solved: the symmetry still exists. +However it is drastically reduced from the symmetry there would have when manipulating directly +the switches. + +Let's take again our example with a substation of 4 elements. For the "fully connected" topology, +the grid2op encoding can be either [1, 1, 1, 1] or [2, 2, 2, 2] which makes 2 solutions. + +With the substation layout detailed in the paragraph `Smaller action space`_ it can be encoding with: + +- [[1, 0], [1, 0], [1, 0], [1, 0], 0] : every element connected to busbar 1 and the busbar coupler between busbar 1 and 2 opened +- [[0, 1], [0, 1], [0, 1], [0, 1], 0] : every element connected to busbar 2 and the busbar coupler between busbar 1 and 2 opened +- [[1, 0], [1, 0], [1, 0], [1, 0], 1] : every element connected to busbar 1 and the busbar coupler between busbar 1 and 2 closed +- [[0, 1], [0, 1], [0, 1], [0, 1], 1] : every element connected to busbar 2 and the busbar coupler between busbar 1 and 2 closed +- [[1, 0], [0, 1], [0, 1], [0, 1], 1] : first element connected to busbar 1, all others to busbar 2 + and the busbar coupler between busbar 1 and 2 closed +- [[0, 0], [1, 1], [0, 1], [0, 1], 1] : second element connected to busbar 1, all others to busbar 2 + and the busbar coupler between busbar 1 and 2 closed +- ... + +Basically, as long at the busbar coupler between busbar 1 and busbar 2 is closed, you can connect every element to every +busbar and end-up with a valid encoding of the topology "fully connected". + +In this representation, you have 2 + 2**4 = 18 possible "valid" encoding of the same "fully connected" topology. + +.. note:: + We only count here "valid" topology, in the sense that an element is either connected to busbar 1 or busbar 2 + but not to both at the same time. But in fact it would be perfectly fine to connect and object to + both busbar as long as the busbar coupler is closed (for each element this lead to 3 possible combination) + + There would be not 2**4 but 4**3 = 128 encoding of this "fully connected" topology. + + In general it is considered a good practice to chose a reprensentation that is as explicit and "unique" + as possible. + +Switches make the solver slightly slower ++++++++++++++++++++++++++++++++++++++++++ + +The switches information is also a reprensentation of the topology that is not the one used by the solver. + +At some point, any solver will have to compute a (sparse) matrices and a (dense) vetor to represent +the physical laws. These are often computed by first reducing the "switches state" to the "nodal topology" +and then convert this graph to the proper matrix and vector. + +By passing directly the "nodal topology" it is faster (for some solver at least) as the initial pre processing +of the switches state to the "graph" does not need to be performed. + +.. note:: + And this why it is relatively hard for some "solver" to be used as a backend. + + Some solver can only manipulate switches. In order to match grid2op representation, + it is then required to cast the "nodal topology" of grid2op to a switches state + (which is for now HARD and slow), then pass these swtiches to the "solver". + + Afterwards, the "solver" will then run its internal routine (often really fast) + to retrieve the "nodal topology" + of the grid (what the agent wanted to get) from the swtiches state. + + +It is easy to compute the grid2op representation from the switches ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +This is done internally by all solvers (pandapower when using switches but also all solver we +know) at the initial state of running a powerflow and is relatively easy. Some graph +alrogithms BFS (*eg* Breadth First Search) allows to quickly compute the "grid2op representation" +from the state of the switches. + +This means that an agent can have full access to the switches, manipulate them and at the end +inform grid2op about the "grid2op topology" without too much trouble. + +If we had modeled "by default" the switches it would mean that an agent that would "do like the human" +(*ie* target a nodal topology) would then need to find some "switches states" that matches The +representation it targets. So an agent would have to do two things, instead of just one. + +.. da,ger:: + To be honest, it also means that the current grid2op representation is not entirely "consistent". + + For some real grid, with some given substations layout, a agent could target a topology that is + not feasible: there does not exist a switches state that can represent this topology. + + This is currently a problem for real time grid operations. But we believe that a "routine" + (heuristic or optimization based) can be used to detect such cases. + This routine is yet to be implemented (it is not on our priority list). The first step + (in our opinion) is to make a "proof of concept" that something can work. So basically + that a "target nodal topology" can be found. + + In a second stage, when things will be closer to production context, we will thing + about + How it is accessible in grid2op --------------------------------- +.. warning:: + Doc in progress + The "topo_vect" vector ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. warning:: + Doc in progress + In the observation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. warning:: + Doc in progress + In the action ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Relation with real powergrids ---------------------------------- +.. warning:: + Doc in progress .. include:: final.rst \ No newline at end of file diff --git a/docs/troubleshoot.rst b/docs/troubleshoot.rst index fbfec1fc..d07539fd 100644 --- a/docs/troubleshoot.rst +++ b/docs/troubleshoot.rst @@ -101,6 +101,7 @@ The file `~/.grid2opconfig.json` can look like: } or + .. code-block:: json { diff --git a/docs/user/environment.rst b/docs/user/environment.rst index 5c71eb3e..3b4af59c 100644 --- a/docs/user/environment.rst +++ b/docs/user/environment.rst @@ -84,7 +84,7 @@ What happens here is the following: You might want to customize this general behaviour in multiple way: - you might want to study only one chronics (equivalent to only one level of a video game) - see `Study always the same chronics`_ + see `Study always the same time serie`_ - you might want to loop through the chronics, but not always in the same order. If that is the case you might want to consult the section `Shuffle the chronics order`_ - you might also have spotted some chronics that have bad properties. In this case, you can @@ -153,8 +153,8 @@ be more up to date with the code). .. note:: In the near future (next few releases) we will also attempt to make the - customization of the `parameters` or the `skip number of steps`, `maximum duration - of the scenarios` also available in `env.reset()` options. + customization of the `parameters` or the `skip number of steps`, + `maximum duration of the scenarios` also available in `env.reset()` options. .. _environment-module-chronics-info: diff --git a/docs/user/reward.rst b/docs/user/reward.rst index 04996295..684eacca 100644 --- a/docs/user/reward.rst +++ b/docs/user/reward.rst @@ -44,7 +44,7 @@ the environment you are using. For the example above, the "l2rpn_case14_sandbox" using the :class:`RedispReward`. Using a reward function available in grid2op -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want to customize your environment by adapting the reward and use a reward available in grid2op it is rather simple, you need to specify it in the `make` command: diff --git a/docs/user/special.rst b/docs/user/special.rst new file mode 100644 index 00000000..a4b7a9d9 --- /dev/null +++ b/docs/user/special.rst @@ -0,0 +1,69 @@ +.. for the color +.. include:: ../special.rst + +.. _n_gen: ./space.html#grid2op.Space.GridObjects.n_gen +.. _n_load: ./space.html#grid2op.Space.GridObjects.n_load +.. _n_line: ./space.html#grid2op.Space.GridObjects.n_line +.. _n_sub: ./space.html#grid2op.Space.GridObjects.n_sub +.. _n_storage: ./space.html#grid2op.Space.GridObjects.n_storage +.. _dim_topo: ./space.html#grid2op.Space.GridObjects.dim_topo +.. _dim_alarms: ./space.html#grid2op.Space.GridObjects.dim_alarms +.. _dim_alerts: ./space.html#grid2op.Space.GridObjects.dim_alerts +.. _year: ./observation.html#grid2op.Observation.BaseObservation.year +.. _month: ./observation.html#grid2op.Observation.BaseObservation.month +.. _day: ./observation.html#grid2op.Observation.BaseObservation.day +.. _hour_of_day: ./observation.html#grid2op.Observation.BaseObservation.hour_of_day +.. _minute_of_hour: ./observation.html#grid2op.Observation.BaseObservation.minute_of_hour +.. _day_of_week: ./observation.html#grid2op.Observation.BaseObservation.day_of_week +.. _gen_p: ./observation.html#grid2op.Observation.BaseObservation.gen_p +.. _gen_q: ./observation.html#grid2op.Observation.BaseObservation.gen_q +.. _gen_v: ./observation.html#grid2op.Observation.BaseObservation.gen_v +.. _load_p: ./observation.html#grid2op.Observation.BaseObservation.load_p +.. _load_q: ./observation.html#grid2op.Observation.BaseObservation.load_q +.. _load_v: ./observation.html#grid2op.Observation.BaseObservation.load_v +.. _p_or: ./observation.html#grid2op.Observation.BaseObservation.p_or +.. _q_or: ./observation.html#grid2op.Observation.BaseObservation.q_or +.. _v_or: ./observation.html#grid2op.Observation.BaseObservation.v_or +.. _a_or: ./observation.html#grid2op.Observation.BaseObservation.a_or +.. _p_ex: ./observation.html#grid2op.Observation.BaseObservation.p_ex +.. _q_ex: ./observation.html#grid2op.Observation.BaseObservation.q_ex +.. _v_ex: ./observation.html#grid2op.Observation.BaseObservation.v_ex +.. _a_ex: ./observation.html#grid2op.Observation.BaseObservation.a_ex +.. _rho: ./observation.html#grid2op.Observation.BaseObservation.rho +.. _topo_vect: ./observation.html#grid2op.Observation.BaseObservation.topo_vect +.. _line_status: ./observation.html#grid2op.Observation.BaseObservation.line_status +.. _timestep_overflow: ./observation.html#grid2op.Observation.BaseObservation.timestep_overflow +.. _time_before_cooldown_line: ./observation.html#grid2op.Observation.BaseObservation.time_before_cooldown_line +.. _time_before_cooldown_sub: ./observation.html#grid2op.Observation.BaseObservation.time_before_cooldown_sub +.. _time_next_maintenance: ./observation.html#grid2op.Observation.BaseObservation.time_next_maintenance +.. _duration_next_maintenance: ./observation.html#grid2op.Observation.BaseObservation.duration_next_maintenance +.. _target_dispatch: ./observation.html#grid2op.Observation.BaseObservation.target_dispatch +.. _actual_dispatch: ./observation.html#grid2op.Observation.BaseObservation.actual_dispatch +.. _storage_charge: ./observation.html#grid2op.Observation.BaseObservation.storage_charge +.. _storage_power_target: ./observation.html#grid2op.Observation.BaseObservation.storage_power_target +.. _storage_power: ./observation.html#grid2op.Observation.BaseObservation.storage_power +.. _storage_theta: ./observation.html#grid2op.Observation.BaseObservation.storage_theta +.. _gen_p_before_curtail: ./observation.html#grid2op.Observation.BaseObservation.gen_p_before_curtail +.. _curtailment: ./observation.html#grid2op.Observation.BaseObservation.curtailment +.. _curtailment_limit: ./observation.html#grid2op.Observation.BaseObservation.curtailment_limit +.. _is_alarm_illegal: ./observation.html#grid2op.Observation.BaseObservation.is_alarm_illegal +.. _time_since_last_alarm: ./observation.html#grid2op.Observation.BaseObservation.time_since_last_alarm +.. _last_alarm: ./observation.html#grid2op.Observation.BaseObservation.last_alarm +.. _attention_budget: ./observation.html#grid2op.Observation.BaseObservation.attention_budget +.. _max_step: ./observation.html#grid2op.Observation.BaseObservation.max_step +.. _current_step: ./observation.html#grid2op.Observation.BaseObservation.current_step +.. _delta_time: ./observation.html#grid2op.Observation.BaseObservation.delta_time +.. _gen_margin_up: ./observation.html#grid2op.Observation.BaseObservation.gen_margin_up +.. _gen_margin_down: ./observation.html#grid2op.Observation.BaseObservation.gen_margin_down +.. _curtailment_mw: ./observation.html#grid2op.Observation.BaseObservation.curtailment_mw +.. _theta_or: ./observation.html#grid2op.Observation.BaseObservation.theta_or +.. _theta_ex: ./observation.html#grid2op.Observation.BaseObservation.theta_ex +.. _gen_theta: ./observation.html#grid2op.Observation.BaseObservation.gen_theta +.. _load_theta: ./observation.html#grid2op.Observation.BaseObservation.load_theta +.. _active_alert: ./observation.html#grid2op.Observation.BaseObservation.active_alert +.. _time_since_last_alert: ./observation.html#grid2op.Observation.BaseObservation.time_since_last_alert +.. _alert_duration: ./observation.html#grid2op.Observation.BaseObservation.alert_duration +.. _total_number_of_alert: ./observation.html#grid2op.Observation.BaseObservation.total_number_of_alert +.. _time_since_last_attack: ./observation.html#grid2op.Observation.BaseObservation.time_since_last_attack +.. _was_alert_used_after_attack: ./observation.html#grid2op.Observation.BaseObservation.was_alert_used_after_attack +.. _attack_under_alert: ./observation.html#grid2op.Observation.BaseObservation.attack_under_alert diff --git a/getting_started/11_ray_integration.ipynb b/getting_started/11_ray_integration.ipynb index cac674ae..7410bdac 100644 --- a/getting_started/11_ray_integration.ipynb +++ b/getting_started/11_ray_integration.ipynb @@ -680,7 +680,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.13" + "version": "3.10.14" } }, "nbformat": 4, diff --git a/grid2op/Action/_backendAction.py b/grid2op/Action/_backendAction.py index fa4c835f..727fcab8 100644 --- a/grid2op/Action/_backendAction.py +++ b/grid2op/Action/_backendAction.py @@ -30,14 +30,15 @@ class ValueStore: it but we won't change it (for now at least) .. warning:: - Objects from this class should never be created by anyone except by objects of the :class:`grid2op.Action._backendAction._BackendAction` + Objects from this class should never be created by anyone except by objects of the + :class:`grid2op.Action._backendAction._BackendAction` when they are created or when instances of `_BackendAction` are process *eg* with :func:`_BackendAction.__call__` or :func:`_BackendAction.get_loads_bus` etc. There are two correct uses for this class: #. by iterating manually with the `for xxx in value_stor_instance: ` - #. by checking which objects have been changed (with :attr:`ValueStore.changed`) and then check the + #. by checking which objects have been changed (with :attr:`ValueStore.changed` ) and then check the new value of the elements **changed** with :attr:`ValueStore.values` [el_id] .. danger:: @@ -60,6 +61,7 @@ class ValueStore: Attributes ---------- + TODO Examples --------- diff --git a/grid2op/Action/baseAction.py b/grid2op/Action/baseAction.py index db38feb0..50b22d31 100644 --- a/grid2op/Action/baseAction.py +++ b/grid2op/Action/baseAction.py @@ -2217,11 +2217,13 @@ def update(self, - -1: You can use this method to disconnect an object by setting the value to -1. - "change_bus": (numpy bool vector or dictionary) will change the bus to which the object is connected. - True will + ``True`` will change it (eg switch it from bus 1 to bus 2 or from bus 2 to bus 1). NB this is only active if the system has only 2 buses per substation. - .. versionchanged:: 1.10.0 - This feature is deactivated if `act.n_busbar_per_sub >= 3` or `act.n_busbar_per_sub == 1` + + .. note:: + Change in version: 1.10.0 This feature is deactivated if `act.n_busbar_per_sub >= 3` + or `act.n_busbar_per_sub == 1` - "redispatch": the best use of this is to specify either the numpy array of the redispatch vector you want to apply (that should have the size of the number of generators on the grid) or to specify a list of @@ -2252,7 +2254,7 @@ def update(self, **NB**: if for a given powerline, both switch_line_status and set_line_status is set, the action will not be usable. - This will lead to an :class:`grid2op.Exception.AmbiguousAction` exception. + This will lead to an :class:`grid2op.Exceptions.AmbiguousAction` exception. **NB**: The length of vectors provided here is NOT check in this function. This method can be "chained" and only on the final action, when used, eg. in the Backend, is checked. diff --git a/grid2op/Agent/oneChangeThenNothing.py b/grid2op/Agent/oneChangeThenNothing.py index e981c9ba..2eab3f9c 100644 --- a/grid2op/Agent/oneChangeThenNothing.py +++ b/grid2op/Agent/oneChangeThenNothing.py @@ -31,15 +31,16 @@ class OneChangeThenNothing(BaseAgent): Examples --------- - We advise to use this class as following + This class is deprecated in favor of the "init state" reset options. Please avoid using it. + + But if you really want to use it... then you can do it with: .. code-block:: python # This class has been deprecated, please use the env.reset() # with proper options instead - - DEPRECATED ! + # DEPRECATED ! import grid2op from grid2op.Agent import OneChangeThenNothing acts_dict_ = [{}, {"set_line_status": [(0,-1)]}] # list of dictionaries. Each dictionary @@ -49,12 +50,13 @@ class OneChangeThenNothing(BaseAgent): for act_as_dict in zip(acts_dict_): # generate the proper class that will perform the first action (encoded by {}) in acts_dict_ agent_class = OneChangeThenNothing.gen_next(act_as_dict) - + # start a runner with this agent runner = Runner(**env.get_params_for_runner(), agentClass=agent_class) # run 2 episode with it res_2 = runner.run(nb_episode=2) + Notes ------ diff --git a/grid2op/Chronics/fromMultiEpisodeData.py b/grid2op/Chronics/fromMultiEpisodeData.py index 7cfc2433..d7f77d22 100644 --- a/grid2op/Chronics/fromMultiEpisodeData.py +++ b/grid2op/Chronics/fromMultiEpisodeData.py @@ -30,7 +30,7 @@ class FromMultiEpisodeData(GridValue): It is an extension of the class :class:`FromOneEpisodeData` but with multiple episodes. .. seealso:: - :class:`grid2op.Chronics.FromOneEpisodeData`if you want to use only one episode + :class:`grid2op.Chronics.FromOneEpisodeData` if you want to use only one episode .. warning:: It has the same limitation as :class:`grid2op.Chronics.FromOneEpisodeData`, including: diff --git a/grid2op/Environment/environment.py b/grid2op/Environment/environment.py index 97b5d0a2..c41871bf 100644 --- a/grid2op/Environment/environment.py +++ b/grid2op/Environment/environment.py @@ -1070,7 +1070,7 @@ def reset(self, episode by calling `env.reset(seed=..., options={"time serie id": ...})` Before version 1.9.8, if you wanted to use a fixed seed, you would need to (see - doc of :func:`Environment.seed` ): + doc of :func:`grid2op.Environment.BaseEnv.seed` ): .. code-block:: python @@ -1114,9 +1114,9 @@ def reset(self, the reset function. The value associated to this key should be dictionnary that can be converted to a non ambiguous grid2op action using an "action space". - .. notes:: + .. note:: The "action space" used here is not the action space of the agent. It's an "action - space" that uses a :grid2op:`Action.Action.BaseAction` class meaning you can do any + space" that uses a :func:`grid2op.Action.Action.BaseAction` class meaning you can do any type of action, on shunts, on topology, on line status etc. even if the agent is not allowed to. @@ -1147,6 +1147,7 @@ def reset(self, Another example in this case: if the action you provide would change topology of substation 2 and 4 then the initial state (after `env.reset`) will give: + - substation 1 as in the time serie - substation 2 as in "options" - substation 4 as in "options" diff --git a/grid2op/gym_compat/gymenv.py b/grid2op/gym_compat/gymenv.py index 0584ff2a..db6c59a4 100644 --- a/grid2op/gym_compat/gymenv.py +++ b/grid2op/gym_compat/gymenv.py @@ -333,5 +333,32 @@ def reset(self, return self._aux_reset_new(seed, options) def step(self, action: ActType) -> Tuple[ObsType, float, bool, bool, STEP_INFO_TYPING]: + """Run one timestep of the environment’s dynamics using the agent actions. + + When the end of an episode is reached (terminated or truncated), + it is necessary to call reset() to reset this environment’s state for the next episode. + + Parameters + ---------- + action : ``ActType`` + An action that can be process by the :func:`grid2op.gym_compat.gym_act_space.GymActionSpace.from_gym` + (given in the form of a gymnasium action belonging to a gymnasium space.). + + For example it can be a sorted dictionary if you are using default + :class:`grid2op.gym_compat.gym_act_space.GymActionSpace` + or a numpy array if you are using :class:`grid2op.gym_compat.box_gym_actspace.BoxGymnasiumActSpace` + + Returns + ------- + Tuple[ObsType, float, bool, bool, STEP_INFO_TYPING] + + - observation: an instance of the current observation space (can be a dictionary, a numpy array etc.) + - reward: the reward for the previous action + - truncated: whether the environment was terminated + - done: whether the environment is done + - info: other information, see :func:`grid2op.Environment.BaseEnv.step` for more + information about the available informations. + + """ return self._aux_step_new(action) GymnasiumEnv.__doc__ = __AuxGymEnv.__doc__ diff --git a/grid2op/tests/_aux_test_gym_compat.py b/grid2op/tests/_aux_test_gym_compat.py index e9c697ad..9284731a 100644 --- a/grid2op/tests/_aux_test_gym_compat.py +++ b/grid2op/tests/_aux_test_gym_compat.py @@ -18,7 +18,7 @@ from grid2op.Action import PlayableAction from grid2op.gym_compat import GymActionSpace, GymObservationSpace -from grid2op.gym_compat import GymEnv +from grid2op.gym_compat.legacy import GymEnv as LegacyGymEnv # TODO GYMENV from grid2op.gym_compat import ContinuousToDiscreteConverter from grid2op.gym_compat import ScalerAttrConverter from grid2op.gym_compat import MultiToTupleConverter @@ -36,7 +36,7 @@ class AuxilliaryForTest: def _aux_GymEnv_cls(self): - return GymEnv + return LegacyGymEnv def _aux_ContinuousToDiscreteConverter_cls(self): return ContinuousToDiscreteConverter diff --git a/grid2op/tests/automatic_classes.py b/grid2op/tests/automatic_classes.py index 57306c48..8196945a 100644 --- a/grid2op/tests/automatic_classes.py +++ b/grid2op/tests/automatic_classes.py @@ -29,11 +29,11 @@ SingleEnvMultiProcess, MultiMixEnvironment) from grid2op.Exceptions import NoForecastAvailable -from grid2op.gym_compat import (GymEnv, - BoxGymActSpace, +from grid2op.gym_compat import (BoxGymActSpace, BoxGymObsSpace, DiscreteActSpace, MultiDiscreteActSpace) +from grid2op.gym_compat.legacy import GymEnv as LegacyGymEnv # TODO GYMENV # TODO test the runner saved classes and reload @@ -558,7 +558,7 @@ def _aux_run_envs(self, act, env_gym): def test_gym_with_step(self): """test the step function also disconnects (or not) the lines""" - env_gym = GymEnv(self.env) + env_gym = LegacyGymEnv(self.env) act = {} self._aux_run_envs(act, env_gym) env_gym.reset() @@ -566,12 +566,12 @@ def test_gym_with_step(self): def test_gym_normal(self): """test I can create the gym env""" - env_gym = GymEnv(self.env) + env_gym = LegacyGymEnv(self.env) env_gym.reset() def test_gym_box(self): """test I can create the gym env with box ob space and act space""" - env_gym = GymEnv(self.env) + env_gym = LegacyGymEnv(self.env) with warnings.catch_warnings(): warnings.filterwarnings("ignore") env_gym.action_space = BoxGymActSpace(self.env.action_space) @@ -580,7 +580,7 @@ def test_gym_box(self): def test_gym_discrete(self): """test I can create the gym env with discrete act space""" - env_gym = GymEnv(self.env) + env_gym = LegacyGymEnv(self.env) with warnings.catch_warnings(): warnings.filterwarnings("ignore") env_gym.action_space = DiscreteActSpace(self.env.action_space) @@ -590,7 +590,7 @@ def test_gym_discrete(self): def test_gym_multidiscrete(self): """test I can create the gym env with multi discrete act space""" - env_gym = GymEnv(self.env) + env_gym = LegacyGymEnv(self.env) with warnings.catch_warnings(): warnings.filterwarnings("ignore") env_gym.action_space = MultiDiscreteActSpace(self.env.action_space) @@ -602,12 +602,12 @@ def test_gym_multidiscrete(self): def test_asynch_fork(self): if _IS_WINDOWS: self.skipTest("no fork on windows") - async_vect_env = AsyncVectorEnv((lambda: GymEnv(self.env), lambda: GymEnv(self.env)), + async_vect_env = AsyncVectorEnv((lambda: LegacyGymEnv(self.env), lambda: LegacyGymEnv(self.env)), context="fork") obs = async_vect_env.reset() def test_asynch_spawn(self): - async_vect_env = AsyncVectorEnv((lambda: GymEnv(self.env), lambda: GymEnv(self.env)), + async_vect_env = AsyncVectorEnv((lambda: LegacyGymEnv(self.env), lambda: LegacyGymEnv(self.env)), context="spawn") obs = async_vect_env.reset() From 03df698a4a82aba41763587c6538a6c2e43eade9 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 3 Sep 2024 16:25:39 +0200 Subject: [PATCH 08/29] fixing some tests that have been broken --- grid2op/tests/_aux_test_gym_compat.py | 4 ++-- grid2op/tests/automatic_classes.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/grid2op/tests/_aux_test_gym_compat.py b/grid2op/tests/_aux_test_gym_compat.py index 9284731a..6f574b37 100644 --- a/grid2op/tests/_aux_test_gym_compat.py +++ b/grid2op/tests/_aux_test_gym_compat.py @@ -18,7 +18,7 @@ from grid2op.Action import PlayableAction from grid2op.gym_compat import GymActionSpace, GymObservationSpace -from grid2op.gym_compat.legacy import GymEnv as LegacyGymEnv # TODO GYMENV +from grid2op.gym_compat import GymEnv # TODO GYMENV from grid2op.gym_compat import ContinuousToDiscreteConverter from grid2op.gym_compat import ScalerAttrConverter from grid2op.gym_compat import MultiToTupleConverter @@ -36,7 +36,7 @@ class AuxilliaryForTest: def _aux_GymEnv_cls(self): - return LegacyGymEnv + return GymEnv def _aux_ContinuousToDiscreteConverter_cls(self): return ContinuousToDiscreteConverter diff --git a/grid2op/tests/automatic_classes.py b/grid2op/tests/automatic_classes.py index 8196945a..f68c6f51 100644 --- a/grid2op/tests/automatic_classes.py +++ b/grid2op/tests/automatic_classes.py @@ -33,7 +33,7 @@ BoxGymObsSpace, DiscreteActSpace, MultiDiscreteActSpace) -from grid2op.gym_compat.legacy import GymEnv as LegacyGymEnv # TODO GYMENV +from grid2op.gym_compat import GymEnv # TODO GYMENV # TODO test the runner saved classes and reload @@ -558,7 +558,7 @@ def _aux_run_envs(self, act, env_gym): def test_gym_with_step(self): """test the step function also disconnects (or not) the lines""" - env_gym = LegacyGymEnv(self.env) + env_gym = GymEnv(self.env) act = {} self._aux_run_envs(act, env_gym) env_gym.reset() @@ -566,12 +566,12 @@ def test_gym_with_step(self): def test_gym_normal(self): """test I can create the gym env""" - env_gym = LegacyGymEnv(self.env) + env_gym = GymEnv(self.env) env_gym.reset() def test_gym_box(self): """test I can create the gym env with box ob space and act space""" - env_gym = LegacyGymEnv(self.env) + env_gym = GymEnv(self.env) with warnings.catch_warnings(): warnings.filterwarnings("ignore") env_gym.action_space = BoxGymActSpace(self.env.action_space) @@ -580,7 +580,7 @@ def test_gym_box(self): def test_gym_discrete(self): """test I can create the gym env with discrete act space""" - env_gym = LegacyGymEnv(self.env) + env_gym = GymEnv(self.env) with warnings.catch_warnings(): warnings.filterwarnings("ignore") env_gym.action_space = DiscreteActSpace(self.env.action_space) @@ -590,7 +590,7 @@ def test_gym_discrete(self): def test_gym_multidiscrete(self): """test I can create the gym env with multi discrete act space""" - env_gym = LegacyGymEnv(self.env) + env_gym = GymEnv(self.env) with warnings.catch_warnings(): warnings.filterwarnings("ignore") env_gym.action_space = MultiDiscreteActSpace(self.env.action_space) @@ -602,12 +602,12 @@ def test_gym_multidiscrete(self): def test_asynch_fork(self): if _IS_WINDOWS: self.skipTest("no fork on windows") - async_vect_env = AsyncVectorEnv((lambda: LegacyGymEnv(self.env), lambda: LegacyGymEnv(self.env)), + async_vect_env = AsyncVectorEnv((lambda: GymEnv(self.env), lambda: GymEnv(self.env)), context="fork") obs = async_vect_env.reset() def test_asynch_spawn(self): - async_vect_env = AsyncVectorEnv((lambda: LegacyGymEnv(self.env), lambda: LegacyGymEnv(self.env)), + async_vect_env = AsyncVectorEnv((lambda: GymEnv(self.env), lambda: GymEnv(self.env)), context="spawn") obs = async_vect_env.reset() From 2bc55f8aeecff01296068b275c9768090453ac60 Mon Sep 17 00:00:00 2001 From: deuce1957 Date: Tue, 17 Sep 2024 14:38:10 +0200 Subject: [PATCH 09/29] Debug: Fix collection_wrapper_after_run test --- grid2op/tests/test_CompactEpisodeData.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/grid2op/tests/test_CompactEpisodeData.py b/grid2op/tests/test_CompactEpisodeData.py index e755b478..380abd4b 100644 --- a/grid2op/tests/test_CompactEpisodeData.py +++ b/grid2op/tests/test_CompactEpisodeData.py @@ -181,8 +181,11 @@ def test_collection_wrapper_after_run(self): agentClass=OneChange, use_compact_episode_data=True, ) - *_, episode_data = runner.run_one_episode( - max_iter=self.max_iter, detailed_output=True, + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + *_, episode_data = runner.run_one_episode( + max_iter=self.max_iter, detailed_output=True, + ) # Check that the type of first action is set bus assert episode_data.action_space.from_vect(episode_data.actions[0]).get_types()[2] @@ -298,4 +301,4 @@ def test_can_return_ep_data(self): if __name__ == "__main__": - unittest.main() + unittest.main() \ No newline at end of file From 77c5594ea28b24f3e780abd37b5b7fa061ece4ce Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 30 Sep 2024 15:21:56 +0200 Subject: [PATCH 10/29] 2 small fixes --- CHANGELOG.rst | 3 +++ grid2op/Chronics/gridStateFromFileWithForecasts.py | 8 +++++--- grid2op/data/educ_case14_storage/grid.json | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 16c8e756..e90e7bdf 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -71,6 +71,9 @@ Next release - [FIXED] an issue in the backend: if the backend failed to be created the `_grid` attribute was set to `None` and not set back to - [FIXED] the `self.skip_if_needed()` was missing for one of the test suite. +- [FIXED] an error in the descirption of the `educ_case14_storage` environment + (wrong sign for the slack generator) +- [IMPROVED] error message when forecasts are not correctly set-up [1.10.3] - 2024-07-12 ------------------------- diff --git a/grid2op/Chronics/gridStateFromFileWithForecasts.py b/grid2op/Chronics/gridStateFromFileWithForecasts.py index 3e60ed4a..8dadcdb4 100644 --- a/grid2op/Chronics/gridStateFromFileWithForecasts.py +++ b/grid2op/Chronics/gridStateFromFileWithForecasts.py @@ -71,10 +71,10 @@ def __init__( self._order_prod_p_forecasted = None self._order_prod_v_forecasted = None self._data_already_in_mem = False # says if the "main" value from the base class had to be reloaded (used for chunk) + self._nb_forecast = len(h_forecast) self._h_forecast = copy.deepcopy(h_forecast) self._check_hs_consistent(self._h_forecast, time_interval) - # init base class GridStateFromFile.__init__( self, @@ -83,7 +83,7 @@ def __init__( time_interval=time_interval, max_iter=max_iter, chunk_size=chunk_size, - ) + ) def _clear(self): super()._clear() @@ -106,7 +106,9 @@ def _check_hs_consistent(self, h_forecast, time_interval): if prev.total_seconds() // 60 != h: raise ChronicsError("For now you cannot build non contiuguous forecast. " "Forecast should look like [5, 10, 15, 20] " - "but not [10, 15, 20] (missing h=5mins) or [5, 10, 20] (missing h=15)") + "but not [10, 15, 20] (missing h=5mins) or [5, 10, 20] " + f"(missing h=15 in this example). Missing h={prev} " + f"at position {i}, found {h}") def _get_next_chunk_forecasted(self): load_p = None diff --git a/grid2op/data/educ_case14_storage/grid.json b/grid2op/data/educ_case14_storage/grid.json index 2f27b352..f19d375d 100644 --- a/grid2op/data/educ_case14_storage/grid.json +++ b/grid2op/data/educ_case14_storage/grid.json @@ -137,7 +137,7 @@ "gen": { "_module": "pandas.core.frame", "_class": "DataFrame", - "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"vm_pu\",\"sn_mva\",\"min_q_mvar\",\"max_q_mvar\",\"scaling\",\"slack\",\"in_service\",\"type\",\"controllable\",\"min_p_mw\",\"max_p_mw\",\"slack_weight\",\"power_station_trafo\"],\"index\":[0,1,2,3,4,5],\"data\":[[null,1,40.0,1.045,null,-40.0,50.0,1.0,false,true,null,true,0.0,140.0,0.0,null],[null,2,0.0,1.01,null,0.0,40.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,5,0.0,1.07,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,5,0.0,1.07,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,7,0.0,1.09,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[\"gen_0_5\",0,-219.0,1.06,null,-9999.0,9999.0,1.0,true,true,null,true,null,null,1.0,null]]}", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"vm_pu\",\"sn_mva\",\"min_q_mvar\",\"max_q_mvar\",\"scaling\",\"slack\",\"in_service\",\"type\",\"controllable\",\"min_p_mw\",\"max_p_mw\",\"slack_weight\",\"power_station_trafo\"],\"index\":[0,1,2,3,4,5],\"data\":[[null,1,40.0,1.045,null,-40.0,50.0,1.0,false,true,null,true,0.0,140.0,0.0,null],[null,2,0.0,1.01,null,0.0,40.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,5,0.0,1.07,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,5,0.0,1.07,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[null,7,0.0,1.09,null,-6.0,24.0,1.0,false,true,null,true,0.0,100.0,0.0,null],[\"gen_0_5\",0,219.0,1.06,null,-9999.0,9999.0,1.0,true,true,null,true,null,null,1.0,null]]}", "orient": "split", "dtype": { "name": "object", @@ -1763,4 +1763,4 @@ }, "user_pf_options": {} } -} \ No newline at end of file +} From 7157d82d036c3610d1701d10c0f6cd789d86d89a Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 30 Sep 2024 15:53:54 +0200 Subject: [PATCH 11/29] fix CI --- .github/workflows/main.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1e8054ad..18544397 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,6 +14,7 @@ jobs: container: quay.io/pypa/manylinux2014_x86_64 env: ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true + GHA_USE_NODE_20: false strategy: matrix: python: @@ -46,7 +47,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@v1 + uses: actions/checkout@v3 with: submodules: true @@ -159,13 +160,13 @@ jobs: run: python setup.py sdist - name: Upload wheel - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: grid2op-wheel-${{ matrix.config.name }}-${{ matrix.python.name }} path: dist/*.whl - name: Upload source archive - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 if: matrix.config.name == 'darwin' && matrix.python.name == 'cp310' with: name: grid2op-sources @@ -238,12 +239,12 @@ jobs: steps: - name: Download wheels - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v4 with: path: download - name: Upload wheels - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: grid2op-wheels path: | From df61eaf07a6aa29a4bfc57710abf617c9d2ec2df Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 30 Sep 2024 16:17:24 +0200 Subject: [PATCH 12/29] fix CI --- grid2op/tests/test_MakeEnv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid2op/tests/test_MakeEnv.py b/grid2op/tests/test_MakeEnv.py index 964383dc..9bdfbd1a 100644 --- a/grid2op/tests/test_MakeEnv.py +++ b/grid2op/tests/test_MakeEnv.py @@ -763,7 +763,7 @@ def test_hash_l2rpn_case14_sandbox(self): def test_hash_educ_case14_storage(self): # the file "storage_units_charac" was not used when hashing the environment, which was a bug self.aux_test_hash_l2rpn_case14_sandbox("educ_case14_storage", - "c5192c21b778129ae4201ff5c992c1d7605fda26280c7267858d3e87cf03adbc15a15913355908b39a7c0839811eec399bed82714d4cd78e5fcae7d984bd641b") + "fb8cfe8d2cd7ab24558c90ca0309303600343091d41c43eae50abb09ad56c0fc8bec321bfefb0239c28ebdb4f2e75fc11948b4dd8dc967e4a10303eac41c7176") if __name__ == "__main__": unittest.main() From 4b60e36b3cc137fed5cc8efc42ec83cd6be61635 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 1 Oct 2024 10:05:45 +0200 Subject: [PATCH 13/29] fixing a bug preventing to load an environment when the layout is not correct --- CHANGELOG.rst | 2 ++ docs/conf.py | 2 +- grid2op/MakeEnv/MakeFromPath.py | 6 ++++-- grid2op/__init__.py | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e90e7bdf..1f61008b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -73,6 +73,8 @@ Next release - [FIXED] the `self.skip_if_needed()` was missing for one of the test suite. - [FIXED] an error in the descirption of the `educ_case14_storage` environment (wrong sign for the slack generator) +- [FIXED] the environment would not load in case of an incorrect "layout.json" + instead of raising a warning. - [IMPROVED] error message when forecasts are not correctly set-up [1.10.3] - 2024-07-12 diff --git a/docs/conf.py b/docs/conf.py index 726281bb..8f354d8e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ author = 'Benjamin Donnot' # The full version, including alpha/beta/rc tags -release = '1.10.4.dev0' +release = '1.10.4.dev1' version = '1.10' diff --git a/grid2op/MakeEnv/MakeFromPath.py b/grid2op/MakeEnv/MakeFromPath.py index ff85d56f..5f3f7f78 100644 --- a/grid2op/MakeEnv/MakeFromPath.py +++ b/grid2op/MakeEnv/MakeFromPath.py @@ -1046,6 +1046,8 @@ def make_from_dataset_path( # Set graph layout if not None and not an empty dict if graph_layout is not None and graph_layout: - env.attach_layout(graph_layout) - + try: + env.attach_layout(graph_layout) + except EnvError as exc_: + warnings.warn(f"Error {exc_} while setting the environment layout.") return env diff --git a/grid2op/__init__.py b/grid2op/__init__.py index 3bb1d7bc..f0399504 100644 --- a/grid2op/__init__.py +++ b/grid2op/__init__.py @@ -11,7 +11,7 @@ Grid2Op """ -__version__ = '1.10.4.dev0' +__version__ = '1.10.4.dev1' __all__ = [ "Action", From e091b3143cb50275fde0ea85d2e2ac5b421a3254 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 9 Oct 2024 14:12:53 +0200 Subject: [PATCH 14/29] renaming gym to gymansium in the code, remove the deprecated gym space --- grid2op/gym_compat/__init__.py | 2 +- grid2op/gym_compat/base_gym_attr_converter.py | 12 +++--- grid2op/gym_compat/box_gym_actspace.py | 18 ++++----- grid2op/gym_compat/box_gym_obsspace.py | 10 ++--- grid2op/gym_compat/continuous_to_discrete.py | 2 +- grid2op/gym_compat/discrete_gym_actspace.py | 10 ++--- grid2op/gym_compat/gym_act_space.py | 31 ++++++++------- grid2op/gym_compat/gym_obs_space.py | 38 +++++++++---------- grid2op/gym_compat/gym_space_converter.py | 10 ++--- grid2op/gym_compat/gymenv.py | 16 ++++---- grid2op/gym_compat/legacy/__init__.py | 0 .../gym_compat/multi_to_tuple_converter.py | 4 +- .../gym_compat/multidiscrete_gym_actspace.py | 12 +++--- grid2op/gym_compat/scaler_attr_converter.py | 2 +- grid2op/gym_compat/utils.py | 10 +++++ 15 files changed, 91 insertions(+), 86 deletions(-) create mode 100644 grid2op/gym_compat/legacy/__init__.py diff --git a/grid2op/gym_compat/__init__.py b/grid2op/gym_compat/__init__.py index 0672745d..71ff5a96 100644 --- a/grid2op/gym_compat/__init__.py +++ b/grid2op/gym_compat/__init__.py @@ -25,7 +25,7 @@ if GYM_AVAILABLE is False and GYMNASIUM_AVAILABLE is False: raise ImportError("Neither gymnasium nor gym are installed. The `grid2op.gym_compat` module cannot be used.") -# base for all gym converter +# base for all gymnasium / gym converter from grid2op.gym_compat.base_gym_attr_converter import BaseGymAttrConverter if GYMNASIUM_AVAILABLE: from grid2op.gym_compat.base_gym_attr_converter import BaseGymnasiumAttrConverter diff --git a/grid2op/gym_compat/base_gym_attr_converter.py b/grid2op/gym_compat/base_gym_attr_converter.py index b56264b6..9ffd443e 100644 --- a/grid2op/gym_compat/base_gym_attr_converter.py +++ b/grid2op/gym_compat/base_gym_attr_converter.py @@ -72,12 +72,12 @@ def initialize_space(self, space): def gym_to_g2op(self, gym_object): """ - Convert a gym object to a grid2op object + Convert a gymnasium object to a grid2op object Parameters ---------- gym_object: - An object (action or observation) represented as a gym "ordered dictionary" + An object (action or observation) represented as a gymnasium "dictionary" Returns ------- @@ -86,13 +86,13 @@ def gym_to_g2op(self, gym_object): """ if self._my_gym_to_g2op is None: raise NotImplementedError( - "Unable to convert gym object to grid2op object with this converter" + "Unable to convert gymnasium object to grid2op object with this converter" ) return self._my_gym_to_g2op(gym_object) def g2op_to_gym(self, g2op_object): """ - Convert a gym object to a grid2op object + Convert a gymnasium object to a grid2op object Parameters ---------- @@ -102,12 +102,12 @@ def g2op_to_gym(self, g2op_object): Returns ------- - The same object, represented as a gym "ordered dictionary" + The same object, represented as a gymnasium "ordered dictionary" """ if self._my_g2op_to_gym is None: raise NotImplementedError( - "Unable to convert grid2op object to gym object with this converter" + "Unable to convert grid2op object to gymnasium object with this converter" ) return self._my_g2op_to_gym(g2op_object) diff --git a/grid2op/gym_compat/box_gym_actspace.py b/grid2op/gym_compat/box_gym_actspace.py index 0516fcf7..3dd4ab98 100644 --- a/grid2op/gym_compat/box_gym_actspace.py +++ b/grid2op/gym_compat/box_gym_actspace.py @@ -43,7 +43,7 @@ class __AuxBoxGymActSpace: """ - This class allows to convert a grid2op action space into a gym "Box" which is + This class allows to convert a grid2op action space into a gymnasium "Box" which is a regular Box in R^d. It also allows to customize which part of the action you want to use and offer capacity to @@ -54,7 +54,7 @@ class __AuxBoxGymActSpace: this is not recommended at all to use it for discrete attribute (set_bus, change_bus, set_line_status or change_line_status) ! - Basically, when doing action in gym for these attributes, this converter will involve rounding and + Basically, when doing action in gymnasium for these attributes, this converter will involve rounding and is definitely not the best representation. Prefer the :class:`MultiDiscreteActSpace` or the :class:`DiscreteActSpace` classes. @@ -136,7 +136,7 @@ class __AuxBoxGymActSpace: gym_env = GymEnv(env) gym_env.action_space = BoxGymActSpace(env.action_space) - obs = gym_env.reset() # obs will be an OrderedDict (default, but you can customize it) + obs = gym_env.reset() # obs will be an Dict (default, but you can customize it) # you can do a "do nothing" action act = np.zeros(gym_env.action_space.shape) @@ -174,7 +174,7 @@ class __AuxBoxGymActSpace: env = grid2op.make(env_name) from grid2op.gym_compat import GymEnv - # this of course will not work... Replace "AGymSpace" with a normal gym space, like Dict, Box, MultiDiscrete etc. + # this of course will not work... Replace "AGymSpace" with a normal gymnasium space, like Dict, Box, MultiDiscrete etc. from gym.spaces import AGymSpace gym_env = GymEnv(env) @@ -188,7 +188,7 @@ def __init__(self, whatever, you, want): def from_gym(self, gym_action): # this is this very same function that you need to implement - # it should have this exact name, take only one action (member of your gym space) as input + # it should have this exact name, take only one action (member of your gymnasium space) as input # and return a grid2op action return TheGymAction_ConvertedTo_Grid2op_Action # eg. return np.concatenate((obs.gen_p * 0.1, np.sqrt(obs.load_p)) @@ -461,7 +461,7 @@ def _get_info(self, functs): shape = (shape[0] + shape_[0],) # handle low / high - # NB: the formula is: glop = gym * multiply + add + # NB: the formula is: glop = gymnasium * multiply + add if el in self._add: low_ = 1.0 * low_.astype(dtype) high_ = 1.0 * high_.astype(dtype) @@ -543,7 +543,7 @@ def _handle_attribute(self, res, gym_act_this, attr_nm): return res def get_indexes(self, key: POSSIBLE_KEYS) -> Tuple[int, int]: - """Allows to retrieve the indexes of the gym action that + """Allows to retrieve the indexes of the gymnasium action that are concerned by the attribute name `key` given in input. Parameters @@ -587,14 +587,14 @@ def get_indexes(self, key: POSSIBLE_KEYS) -> Tuple[int, int]: def from_gym(self, gym_act: np.ndarray) -> BaseAction: """ - This is the function that is called to transform a gym action (in this case a numpy array!) + This is the function that is called to transform a gymnasium action (in this case a numpy array!) sent by the agent and convert it to a grid2op action that will be sent to the underlying grid2op environment. Parameters ---------- gym_act: ``numpy.ndarray`` - the gym action + the gymnasium action Returns ------- diff --git a/grid2op/gym_compat/box_gym_obsspace.py b/grid2op/gym_compat/box_gym_obsspace.py index eefe7189..298488cb 100644 --- a/grid2op/gym_compat/box_gym_obsspace.py +++ b/grid2op/gym_compat/box_gym_obsspace.py @@ -87,7 +87,7 @@ class __AuxBoxGymObsSpace: """ - This class allows to convert a grid2op observation space into a gym "Box" which is + This class allows to convert a grid2op observation space into a gymnasium "Box" which is a regular Box in R^d. It also allows to customize which part of the observation you want to use and offer capacity to @@ -138,7 +138,7 @@ class __AuxBoxGymObsSpace: attr_to_keep=['load_p', "gen_p", "rho]) You can also apply some basic transformation to the attribute of the observation before building - the resulting gym observation (which in this case is a vector). This can be done with: + the resulting gymnasium observation (which in this case is a vector). This can be done with: .. code-block:: python @@ -788,7 +788,7 @@ def _handle_attribute(self, grid2op_observation, attr_nm): def to_gym(self, grid2op_observation): """ This is the function that is called to transform a grid2Op observation, sent by the grid2op environment - and convert it to a numpy array (an element of a gym Box) + and convert it to a numpy array (an element of a gymnasium Box) Parameters ---------- @@ -798,7 +798,7 @@ def to_gym(self, grid2op_observation): Returns ------- res: :class:`numpy.ndarray` - A numpy array compatible with the openAI gym Box that represents the action space. + A numpy array compatible with the openAI gymnasium Box that represents the action space. """ res = np.empty(shape=self.shape, dtype=self.dtype) @@ -818,7 +818,7 @@ def close(self): pass def get_indexes(self, key: str) -> Tuple[int, int]: - """Allows to retrieve the indexes of the gym action that + """Allows to retrieve the indexes of the gymnasium action that are concerned by the attribute name `key` given in input. .. versionadded:: 1.9.3 diff --git a/grid2op/gym_compat/continuous_to_discrete.py b/grid2op/gym_compat/continuous_to_discrete.py index f27ba60c..3c4de285 100644 --- a/grid2op/gym_compat/continuous_to_discrete.py +++ b/grid2op/gym_compat/continuous_to_discrete.py @@ -97,7 +97,7 @@ def __init__(self, nb_bins, init_space=None): def initialize_space(self, init_space): if not isinstance(init_space, type(self)._BoxType): raise RuntimeError( - "Impossible to convert a gym space of type {} to a discrete space" + "Impossible to convert a gymnasium space of type {} to a discrete space" " (it should be of " "type space.Box)" "".format(type(init_space)) diff --git a/grid2op/gym_compat/discrete_gym_actspace.py b/grid2op/gym_compat/discrete_gym_actspace.py index 4e89c448..d7c5fe6b 100644 --- a/grid2op/gym_compat/discrete_gym_actspace.py +++ b/grid2op/gym_compat/discrete_gym_actspace.py @@ -24,10 +24,10 @@ class __AuxDiscreteActSpace: """ TODO the documentation of this class is in progress. - This class allows to convert a grid2op action space into a gym "Discrete". This means that the action are + This class allows to convert a grid2op action space into a gymnasium "Discrete". This means that the action are labeled, and instead of describing the action itself, you provide only its ID. - Let's take an example of line disconnection. In the "standard" gym representation you need to: + Let's take an example of line disconnection. In the "standard" gymnasium representation you need to: .. code-block:: python @@ -113,7 +113,7 @@ class __AuxDiscreteActSpace: .. note:: This class is really closely related to the :class:`grid2op.Converter.IdToAct`. It basically "maps" - this "IdToAct" into a type of gym space, which, in this case, will be a `Discrete` one. + this "IdToAct" into a type of gymnasium space, which, in this case, will be a `Discrete` one. .. note:: By default, the "do nothing" action is encoded by the integer '0'. @@ -322,14 +322,14 @@ def _get_info(self): def from_gym(self, gym_act: int) -> BaseAction: """ - This is the function that is called to transform a gym action (in this case a numpy array!) + This is the function that is called to transform a gymnasium action (in this case a numpy array!) sent by the agent and convert it to a grid2op action that will be sent to the underlying grid2op environment. Parameters ---------- gym_act: ``int`` - the gym action (a single integer for this action space) + the gymnasium action (a single integer for this action space) Returns ------- diff --git a/grid2op/gym_compat/gym_act_space.py b/grid2op/gym_compat/gym_act_space.py index 984de412..c0dd4643 100644 --- a/grid2op/gym_compat/gym_act_space.py +++ b/grid2op/gym_compat/gym_act_space.py @@ -6,7 +6,6 @@ # 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 collections import OrderedDict import warnings import numpy as np @@ -18,20 +17,20 @@ from grid2op.Action import BaseAction, ActionSpace from grid2op.dtypes import dt_int, dt_bool, dt_float from grid2op.Converter.Converters import Converter -from grid2op.gym_compat.utils import GYM_AVAILABLE, GYMNASIUM_AVAILABLE, ActType +from grid2op.gym_compat.utils import GYM_AVAILABLE, GYMNASIUM_AVAILABLE, DictType class __AuxGymActionSpace: """ - This class enables the conversion of the action space into a gym "space". + This class enables the conversion of the action space into a gymnasium "space". Resulting action space will be a :class:`gym.spaces.Dict`. - **NB** it is NOT recommended to use the sample of the gym action space. Please use the sampling ( + **NB** it is NOT recommended to use the sample of the gymnasium action space. Please use the sampling ( if availabe) of the original action space instead [if not available this means there is no implemented way to generate reliable random action] - **Note** that gym space converted with this class should be seeded independently. It is NOT seeded + **Note** that gymnasium space converted with this class should be seeded independently. It is NOT seeded when calling :func:`grid2op.Environment.Environment.seed`. .. warning:: @@ -51,7 +50,7 @@ class __AuxGymActionSpace: See :ref:`gymnasium_gym` for more information .. note:: - A gymnasium Dict is encoded as a OrderedDict (`from collection import OrderedDict`) + A gymnasium Dict can be encoded as a OrderedDict (`from collection import OrderedDict`) see the example section for more information. Examples @@ -69,7 +68,7 @@ class __AuxGymActionSpace: env = grid2op.make(env_name) gym_env = GymEnv(env) - obs = gym_env.reset() # obs will be an OrderedDict (default, but you can customize it) + obs = gym_env.reset() # obs will be an Dict (default, but you can customize it) # is equivalent to "do nothing" act = {} @@ -151,7 +150,7 @@ def __init__(self, env, converter=None, dict_variables=None): # TODO Make sure it works well ! if converter is not None and isinstance(converter, Converter): - # a converter allows to ... convert the data so they have specific gym space + # a converter allows to ... convert the data so they have specific gymnasium space # self.initial_act_space = converter self._converter = converter self._template_act = converter.init_action_space() @@ -159,7 +158,7 @@ def __init__(self, env, converter=None, dict_variables=None): self.__is_converter = True elif converter is not None: raise RuntimeError( - 'Impossible to initialize a gym action space with a converter of type "{}" ' + 'Impossible to initialize a gymnasium action space with a converter of type "{}" ' "A converter should inherit from grid2op.Converter".format( type(converter) ) @@ -246,7 +245,7 @@ def _fill_dict_act_space(self, dict_, dict_variables): self._template_act.dtypes() ): if sh == 0: - # do not add "empty" (=0 dimension) arrays to gym otherwise it crashes + # do not add "empty" (=0 dimension) arrays to gymnasium otherwise it crashes continue my_type = None shape = (sh,) @@ -312,14 +311,14 @@ def _fix_dict_keys(self, dict_: dict) -> dict: res[self.keys_grid2op_2_human[k]] = v return res - def from_gym(self, gymlike_action: OrderedDict) -> object: + def from_gym(self, gymlike_action: DictType) -> object: """ Transform a gym-like action (such as the output of "sample()") into a grid2op action Parameters ---------- - gymlike_action: :class:`gym.spaces.dict.OrderedDict` - The action, represented as a gym action (ordered dict) + gymlike_action: :class:`gym.spaces.dict.Dict` + The action, represented as a gymnasium action (ordered dict) Returns ------- @@ -343,9 +342,9 @@ def from_gym(self, gymlike_action: OrderedDict) -> object: res._assign_attr_from_name(internal_k, tmp) return res - def to_gym(self, action: object) -> OrderedDict: + def to_gym(self, action: object) -> DictType: """ - Transform an action (non gym) into an action compatible with the gym Space. + Transform an action (non gymnasium) into an action compatible with the gymnasium Space. Parameters ---------- @@ -355,7 +354,7 @@ def to_gym(self, action: object) -> OrderedDict: Returns ------- gym_action: - The same action converted as a OrderedDict (default used by gym in case of action space + The same action converted as a Dict (default used by gymnasium in case of action space being Dict) """ diff --git a/grid2op/gym_compat/gym_obs_space.py b/grid2op/gym_compat/gym_obs_space.py index 170435d0..e1776d2b 100644 --- a/grid2op/gym_compat/gym_obs_space.py +++ b/grid2op/gym_compat/gym_obs_space.py @@ -16,26 +16,22 @@ BaseMultiProcessEnvironment, ) from grid2op.gym_compat.utils import GYM_AVAILABLE, GYMNASIUM_AVAILABLE -if GYMNASIUM_AVAILABLE: - from gymnasium import spaces # only used for type hints -elif GYM_AVAILABLE: - from gym import spaces from grid2op.Observation import BaseObservation from grid2op.dtypes import dt_int, dt_bool, dt_float -from grid2op.gym_compat.utils import _compute_extra_power_for_losses +from grid2op.gym_compat.utils import _compute_extra_power_for_losses, DictType class __AuxGymObservationSpace: """ TODO explain gym / gymnasium - This class allows to transform the observation space into a gym space. + This class allows to transform the observation space into a gymnasium space. - Gym space will be a :class:`gym.spaces.Dict` with the keys being the different attributes + Gymnasium space will be a :class:`gym.spaces.Dict` with the keys being the different attributes of the grid2op observation. All attributes are used. - Note that gym space converted with this class should be seeded independently. It is NOT seeded + Note that gymnasium space converted with this class should be seeded independently. It is NOT seeded when calling :func:`grid2op.Environment.Environment.seed`. .. warning:: @@ -65,7 +61,7 @@ class __AuxGymObservationSpace: env = grid2op.make("l2rpn_case14_sandbox") gym_observation_space = GymObservationSpace(env.observation_space) - # and now gym_observation_space is a `gym.spaces.Dict` representing the observation space + # and now gym_observation_space is a `gymnasium.spaces.dict.Dict` representing the observation space # you can "convert" the grid2op observation to / from this space with: @@ -74,7 +70,7 @@ class __AuxGymObservationSpace: # the conversion from gym_obs to grid2op obs is feasible, but i don't imagine # a situation where it is useful. And especially, you will not be able to - # use "obs.simulate" for the observation converted back from this gym action. + # use "obs.simulate" for the observation converted back from this gymnasium action. Notes ----- @@ -123,12 +119,12 @@ def __init__(self, env, dict_variables=None): self._env_params = env._env_params self._opp_attack_max_duration = env._opp_attack_max_duration else: - raise RuntimeError("Unknown way to build a gym observation space") + raise RuntimeError("Unknown way to build a gymnasium observation space") - dict_ = {} # will represent the gym.Dict space + dict_ = {} # will represent the gymnasium.Dict space if dict_variables is None: - # get the extra variables in the gym space I want to get + # get the extra variables in the gymnasium space I want to get dict_variables = { "thermal_limit": type(self)._BoxType( @@ -256,7 +252,7 @@ def _fill_dict_obs_space( self.initial_obs.dtypes(), ): if sh == 0: - # do not add "empty" (=0 dimension) arrays to gym otherwise it crashes + # do not add "empty" (=0 dimension) arrays to gymnasium otherwise it crashes continue if (attr_nm in dict_ or @@ -411,14 +407,14 @@ def _fill_dict_obs_space( my_type = self._generic_gym_space(dt, sh) dict_[attr_nm] = my_type - def from_gym(self, gymlike_observation: spaces.dict.OrderedDict) -> BaseObservation: + def from_gym(self, gymlike_observation: DictType) -> BaseObservation: """ This function convert the gym-like representation of an observation to a grid2op observation. Parameters ---------- - gymlike_observation: :class:`gym.spaces.dict.OrderedDict` - The observation represented as a gym ordered dict + gymlike_observation: :class:`gymnasium.spaces.dict.Dict` + The observation represented as a gymnasium dict Returns ------- @@ -434,9 +430,9 @@ def from_gym(self, gymlike_observation: spaces.dict.OrderedDict) -> BaseObservat f"This key is ignored.") return res - def to_gym(self, grid2op_observation: BaseObservation) -> spaces.dict.OrderedDict: + def to_gym(self, grid2op_observation: BaseObservation) -> DictType: """ - Convert a grid2op observation into a gym ordered dict. + Convert a grid2op observation into a gymnasium Dict. Parameters ---------- @@ -445,8 +441,8 @@ def to_gym(self, grid2op_observation: BaseObservation) -> spaces.dict.OrderedDic Returns ------- - gymlike_observation: :class:`gym.spaces.dict.OrderedDict` - The corresponding gym ordered dict + gymlike_observation: :class:`gymnasium.spaces.dict.Dict` + The corresponding gymnasium dict """ return self._base_to_gym( diff --git a/grid2op/gym_compat/gym_space_converter.py b/grid2op/gym_compat/gym_space_converter.py index 6e9953d2..a0200e22 100644 --- a/grid2op/gym_compat/gym_space_converter.py +++ b/grid2op/gym_compat/gym_space_converter.py @@ -20,7 +20,7 @@ class __AuxBaseGymSpaceConverter: INTERNAL .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\ - Used as a base class to convert grid2op state to gym state (wrapper for some useful function + Used as a base class to convert grid2op state to gymnasium state (wrapper for some useful function for both the action space and the observation space). .. warning:: @@ -107,7 +107,7 @@ def _extract_obj_grid2op(vect, dtype, key): return res def _base_to_gym(self, keys, obj, dtypes, converter=None): - """convert the obj (grid2op object) into a gym observation / action space""" + """convert the obj (grid2op object) into a gymnasium observation / action space""" res = OrderedDict() for k in keys: if k in self.__func: @@ -139,7 +139,7 @@ def _base_to_gym(self, keys, obj, dtypes, converter=None): def add_key(self, key_name, function, return_type): """ - Allows to add arbitrary function to the representation, as a gym environment of + Allows to add arbitrary function to the representation, as a gymnasium environment of the action space of the observation space. TODO @@ -165,7 +165,7 @@ def add_key(self, key_name, function, return_type): Examples --------- In the example below, we explain how to add the "connectivity_matrix" as part of the observation space - (when converted to gym). The new key "connectivity matrix" will be added to the gym observation. + (when converted to gym). The new key "connectivity matrix" will be added to the gymnasium observation. .. code-block:: python @@ -181,7 +181,7 @@ def add_key(self, key_name, function, return_type): from grid2op.gym_compat import GymEnv env_gym = GymEnv(env_glop) - # default gym environment, the connectivity matrix is not computed + # default gymnasium environment, the connectivity matrix is not computed obs_gym = env_gym.reset() print(f"Is the connectivity matrix part of the observation in gym: {'connectivity_matrix' in obs_gym}") diff --git a/grid2op/gym_compat/gymenv.py b/grid2op/gym_compat/gymenv.py index db6c59a4..f6c9d109 100644 --- a/grid2op/gym_compat/gymenv.py +++ b/grid2op/gym_compat/gymenv.py @@ -34,8 +34,8 @@ def decorator(func): class __AuxGymEnv(Generic[ObsType, ActType]): """ - fully implements the openAI gym API by using the :class:`GymActionSpace` and :class:`GymObservationSpace` - for compliance with openAI gym. + fully implements the gymnasium API by using the :class:`GymActionSpace` and :class:`GymObservationSpace` + for compliance with gymnasium. They can handle action_space_converter or observation_space converter to change the representation of data that will be fed to the agent. #TODO @@ -43,7 +43,7 @@ class __AuxGymEnv(Generic[ObsType, ActType]): .. warning:: The `gym` package has some breaking API change since its version 0.26. Depending on the version installed, we attempted, in grid2op, to maintain compatibility both with former version and later one. This makes this - class behave differently depending on the version of gym you have installed ! + class behave differently depending on the version of gymnasium / gym you have installed ! The main changes involve the functions `env.step` and `env.reset` @@ -72,7 +72,7 @@ class behave differently depending on the version of gym you have installed ! Notes ------ - The environment passed as input is copied. It is not modified by this "gym environment" + The environment passed as input is copied. It is not modified by this "gymnasium environment" Examples -------- @@ -85,7 +85,7 @@ class behave differently depending on the version of gym you have installed ! env_name = "l2rpn_case14_sandbox" # or any other name env = grid2op.make(env_name) - gym_env = GymEnv(env) # is a gym environment properly inheriting from gym.Env ! + gym_env = GymEnv(env) # is a gymnasium environment properly inheriting from gym.Env ! There are a few difference between "raw" grid2op environment and gymnasium environments. @@ -101,7 +101,7 @@ class behave differently depending on the version of gym you have installed ! In gym, there are no specific representations of the action class. More precisely, for each action type (:class:`MultiDiscreteActSpace`, :class:`DiscreteActSpace`, :class:`BoxGymActSpace` or :class:`GymActionSpace`) there is a way to encode it. For example, by default (:class:`GymActionSpace`) - an action is represented through an OrderedDict (`from collection import OrderedDict`) + an action is represented through an Dict (`from collection import OrderedDict`) """ def __init__(self, @@ -132,7 +132,7 @@ def __init__(self, super().__init__() # super should reference either gym.Env or gymnasium.Env if not hasattr(self, "_np_random"): - # for older version of gym it does not exist + # for older version of gymnasium it does not exist self._np_random = np.random.RandomState() def _aux_step(self, gym_action: ActType) -> Tuple[ObsType, float, bool, STEP_INFO_TYPING]: @@ -206,7 +206,7 @@ def _aux_reset_new(self, return gym_obs, info def render(self): - """for compatibility with open ai gym render function""" + """for compatibility with open ai gymnasium render function""" return self.init_env.render() def close(self) -> None: diff --git a/grid2op/gym_compat/legacy/__init__.py b/grid2op/gym_compat/legacy/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/grid2op/gym_compat/multi_to_tuple_converter.py b/grid2op/gym_compat/multi_to_tuple_converter.py index 7980608e..444bbe07 100644 --- a/grid2op/gym_compat/multi_to_tuple_converter.py +++ b/grid2op/gym_compat/multi_to_tuple_converter.py @@ -63,7 +63,7 @@ class __AuxMultiToTupleConverter: We choose to encode some variable using `MultiBinary` variable in grid2op. This allows for easy manipulation of them if using these frameworks. - MultiBinary are encoded with gym Tuple of gym Discrete variables. + MultiBinary are encoded with gymnasium Tuple of gymnasium Discrete variables. .. warning:: Depending on the presence absence of gymnasium and gym packages this class might behave differently. @@ -113,7 +113,7 @@ def initialize_space(self, init_space): ) else: raise RuntimeError( - "Impossible to convert a gym space of type {} to a Tuple (it should be of " + "Impossible to convert a gymnasium space of type {} to a Tuple (it should be of " "type space.MultiBinary or space.MultiDiscrete)" "".format(type(init_space)) ) diff --git a/grid2op/gym_compat/multidiscrete_gym_actspace.py b/grid2op/gym_compat/multidiscrete_gym_actspace.py index 60999fd9..1ba4d37f 100644 --- a/grid2op/gym_compat/multidiscrete_gym_actspace.py +++ b/grid2op/gym_compat/multidiscrete_gym_actspace.py @@ -24,7 +24,7 @@ class __AuxMultiDiscreteActSpace: """ - This class allows to convert a grid2op action space into a gym "MultiDiscrete". This means that the action are + This class allows to convert a grid2op action space into a gymnasium "MultiDiscrete". This means that the action are labeled, and instead of describing the action itself, you provide only its ID. .. note:: @@ -302,7 +302,7 @@ def __init__(self, self._aux_check_continuous_elements(el, attr_to_keep, nb_bins, act_sp) self._dims = None - self._functs = None # final functions that is applied to the gym action to map it to a grid2Op action + self._functs = None # final functions that is applied to the gymnasium action to map it to a grid2Op action self._binarizers = None # contains all the kwarg to binarize the data self._types = None nvec = self._get_info() @@ -343,7 +343,7 @@ def _aux_check_continuous_elements(self, el, attr_to_keep, nb_bins, act_sp): @staticmethod def _funct_set(vect): - # gym encodes: + # gymnasium encodes: # for set_bus: 0 -> -1, 1-> 0 (don't change)), 2-> 1, 3 -> 2 # for set_status: 0 -> -1, 1-> 0 (don't change)), 2-> 1 [3 do not exist for set_line_status !] vect -= 1 @@ -351,7 +351,7 @@ def _funct_set(vect): @staticmethod def _funct_change(vect): - # gym encodes 0 -> False, 1 -> True + # gymnasium encodes 0 -> False, 1 -> True vect = vect.astype(dt_bool) return vect @@ -550,14 +550,14 @@ def _handle_attribute(self, res, gym_act_this, attr_nm, funct, type_): def from_gym(self, gym_act): """ - This is the function that is called to transform a gym action (in this case a numpy array!) + This is the function that is called to transform a gymnasium action (in this case a numpy array!) sent by the agent and convert it to a grid2op action that will be sent to the underlying grid2op environment. Parameters ---------- gym_act: ``numpy.ndarray`` - the gym action + the gymnasium action Returns ------- diff --git a/grid2op/gym_compat/scaler_attr_converter.py b/grid2op/gym_compat/scaler_attr_converter.py index 1484df0d..693c28b9 100644 --- a/grid2op/gym_compat/scaler_attr_converter.py +++ b/grid2op/gym_compat/scaler_attr_converter.py @@ -15,7 +15,7 @@ class __AuxScalerAttrConverter: """ - This is a scaler that transforms a initial gym space `init_space` into its scale version. + This is a scaler that transforms a initial gymnasium space `init_space` into its scale version. It can be use to scale the observation by substracting the mean and dividing by the variance for example. diff --git a/grid2op/gym_compat/utils.py b/grid2op/gym_compat/utils.py index 4374ae4a..9f84c670 100644 --- a/grid2op/gym_compat/utils.py +++ b/grid2op/gym_compat/utils.py @@ -36,6 +36,16 @@ from typing import TypeVar ObsType = TypeVar("ObsType") ActType = TypeVar("ActType") + + +if GYMNASIUM_AVAILABLE: + from gymnasium.spaces import Dict as DictType +elif GYM_AVAILABLE: + from gym.spaces import Dict as DictType +else: + from typing import TypeVar + DictType = TypeVar("Dict") + _MIN_GYM_VERSION = version.parse("0.17.2") # this is the last gym version to use the "old" numpy prng From 945ebc7834de6cbf82d1a722424a1502ce1b1181 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 9 Oct 2024 14:32:19 +0200 Subject: [PATCH 15/29] fix some issue with gym_compat module --- .gitignore | 1 + CHANGELOG.rst | 2 ++ grid2op/gym_compat/gym_space_converter.py | 6 +++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6bd200b6..7bbcd36e 100644 --- a/.gitignore +++ b/.gitignore @@ -416,6 +416,7 @@ getting_started/venv_310_ray/ grid2op/tests/venv_test_autoclass/ test_eduardo.py grid2op/tests/failed_test* +venv_312 # profiling files **.prof diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1f61008b..82df64ba 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -75,6 +75,8 @@ Next release (wrong sign for the slack generator) - [FIXED] the environment would not load in case of an incorrect "layout.json" instead of raising a warning. +- [FIXED] some issue with gym_compat module for "newest" version of + gymnasium (1.0.0) - [IMPROVED] error message when forecasts are not correctly set-up [1.10.3] - 2024-07-12 diff --git a/grid2op/gym_compat/gym_space_converter.py b/grid2op/gym_compat/gym_space_converter.py index a0200e22..5aa7d509 100644 --- a/grid2op/gym_compat/gym_space_converter.py +++ b/grid2op/gym_compat/gym_space_converter.py @@ -266,7 +266,11 @@ def seed(self, seed=None): of openAI gym """ seeds = super(type(self)._DictType, self).seed(seed) - sub_seeds = seeds + if isinstance(seeds, (int, dt_int)): + # newer gymansium version returns int and not a list + sub_seeds = [seeds] + else: + sub_seeds = seeds max_ = np.iinfo(dt_int).max for i, space_key in enumerate(sorted(self.spaces.keys())): sub_seed = sample_seed(max_, self.np_random) From 8e014643815c46a1a7d8d7eb0ac2f07ab3ebeede Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 9 Oct 2024 17:26:27 +0200 Subject: [PATCH 16/29] trying to fix the issue after gymnasium upgrade [skip ci] --- grid2op/Chronics/multiFolder.py | 2 +- grid2op/gym_compat/gymenv.py | 11 +-- grid2op/tests/test_issue_379.py | 114 +++++++++++++++++++++++++++++++- 3 files changed, 119 insertions(+), 8 deletions(-) diff --git a/grid2op/Chronics/multiFolder.py b/grid2op/Chronics/multiFolder.py index e8b8c9b4..9e71b8da 100644 --- a/grid2op/Chronics/multiFolder.py +++ b/grid2op/Chronics/multiFolder.py @@ -357,7 +357,7 @@ def sample_next_chronics(self, probabilities=None): id_sel = (self._order == selected).nonzero()[0] self._prev_cache_id = selected - 1 return id_sel - + def reset(self): """ Rebuilt the :attr:`Multifolder._order`. This should be called after a call to :func:`Multifolder.set_filter` diff --git a/grid2op/gym_compat/gymenv.py b/grid2op/gym_compat/gymenv.py index f6c9d109..1bb7b746 100644 --- a/grid2op/gym_compat/gymenv.py +++ b/grid2op/gym_compat/gymenv.py @@ -181,16 +181,17 @@ def _aux_reset(self, def _aux_reset_new(self, seed: Optional[int]=None, options: RESET_OPTIONS_TYPING=None) -> Tuple[ObsType,RESET_INFO_GYM_TYPING]: - # used for gym > 0.26 - if (self._shuffle_chronics and - isinstance(self.init_env.chronics_handler.real_data, Multifolder) and - (options is not None and _TIME_SERIE_ID not in options)): - self.init_env.chronics_handler.sample_next_chronics() super().reset(seed=seed) # seed gymnasium env if seed is not None: self._aux_seed_spaces() seed, next_seed, underlying_env_seeds = self._aux_seed_g2op(seed) + + # used for gym > 0.26 + if (self._shuffle_chronics and + isinstance(self.init_env.chronics_handler.real_data, Multifolder) and + (not (options is not None and _TIME_SERIE_ID in options))): + self.init_env.chronics_handler.sample_next_chronics() # we don't seed grid2op with reset as it is done # earlier diff --git a/grid2op/tests/test_issue_379.py b/grid2op/tests/test_issue_379.py index 087dd9ec..60d0c8e8 100644 --- a/grid2op/tests/test_issue_379.py +++ b/grid2op/tests/test_issue_379.py @@ -6,7 +6,6 @@ # SPDX-License-Identifier: MPL-2.0 # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. -import grid2op import unittest import warnings @@ -17,7 +16,118 @@ CAN_TEST_ALL = True if GYMNASIUM_AVAILABLE: from gymnasium.utils.env_checker import check_env - from gymnasium.utils.env_checker import check_reset_return_type, check_reset_options, check_reset_seed + from gymnasium.utils.env_checker import check_reset_return_type, check_reset_options + try: + from gymnasium.utils.env_checker import check_reset_seed + except ImportError: + # not present in most recent version of gymnasium, I copy pasted + # it from an oldest version + import gymnasium + from logging import getLogger + import inspect + from copy import deepcopy + import numpy as np + logger = getLogger() + + + def data_equivalence(data_1, data_2) -> bool: + """Assert equality between data 1 and 2, i.e observations, actions, info. + + Args: + data_1: data structure 1 + data_2: data structure 2 + + Returns: + If observation 1 and 2 are equivalent + """ + if type(data_1) == type(data_2): + if isinstance(data_1, dict): + return data_1.keys() == data_2.keys() and all( + data_equivalence(data_1[k], data_2[k]) for k in data_1.keys() + ) + elif isinstance(data_1, (tuple, list)): + return len(data_1) == len(data_2) and all( + data_equivalence(o_1, o_2) for o_1, o_2 in zip(data_1, data_2) + ) + elif isinstance(data_1, np.ndarray): + return data_1.shape == data_2.shape and np.allclose( + data_1, data_2, atol=0.00001 + ) + else: + return data_1 == data_2 + else: + return False + + + def check_reset_seed(env: gymnasium.Env): + """Check that the environment can be reset with a seed. + + Args: + env: The environment to check + + Raises: + AssertionError: The environment cannot be reset with a random seed, + even though `seed` or `kwargs` appear in the signature. + """ + signature = inspect.signature(env.reset) + if "seed" in signature.parameters or ( + "kwargs" in signature.parameters + and signature.parameters["kwargs"].kind is inspect.Parameter.VAR_KEYWORD + ): + try: + obs_1, info = env.reset(seed=123) + assert ( + obs_1 in env.observation_space + ), "The observation returned by `env.reset(seed=123)` is not within the observation space." + assert ( + env.unwrapped._np_random # pyright: ignore [reportPrivateUsage] + is not None + ), "Expects the random number generator to have been generated given a seed was passed to reset. Mostly likely the environment reset function does not call `super().reset(seed=seed)`." + seed_123_rng = deepcopy( + env.unwrapped._np_random # pyright: ignore [reportPrivateUsage] + ) + + obs_2, info = env.reset(seed=123) + assert ( + obs_2 in env.observation_space + ), "The observation returned by `env.reset(seed=123)` is not within the observation space." + if env.spec is not None and env.spec.nondeterministic is False: + assert data_equivalence( + obs_1, obs_2 + ), "Using `env.reset(seed=123)` is non-deterministic as the observations are not equivalent." + assert ( + env.unwrapped._np_random.bit_generator.state # pyright: ignore [reportPrivateUsage] + == seed_123_rng.bit_generator.state + ), "Mostly likely the environment reset function does not call `super().reset(seed=seed)` as the random generates are not same when the same seeds are passed to `env.reset`." + + obs_3, info = env.reset(seed=456) + assert ( + obs_3 in env.observation_space + ), "The observation returned by `env.reset(seed=456)` is not within the observation space." + assert ( + env.unwrapped._np_random.bit_generator.state # pyright: ignore [reportPrivateUsage] + != seed_123_rng.bit_generator.state + ), "Mostly likely the environment reset function does not call `super().reset(seed=seed)` as the random number generators are not different when different seeds are passed to `env.reset`." + + except TypeError as e: + raise AssertionError( + "The environment cannot be reset with a random seed, even though `seed` or `kwargs` appear in the signature. " + f"This should never happen, please report this issue. The error was: {e}" + ) from e + + seed_param = signature.parameters.get("seed") + # Check the default value is None + if seed_param is not None and seed_param.default is not None: + logger.warning( + "The default seed argument in reset should be `None`, otherwise the environment will by default always be deterministic. " + f"Actual default: {seed_param.default}" + ) + else: + raise gymnasium.error.Error( + "The `reset` method does not provide a `seed` or `**kwargs` keyword argument." + ) + + elif GYM_AVAILABLE: from gym.utils.env_checker import check_env from gym.utils.env_checker import check_reset_return_type, check_reset_options, check_reset_seed From 56e904db80dc128dd6cde6f17d7f4a333bbe0421 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 9 Oct 2024 17:27:01 +0200 Subject: [PATCH 17/29] adding support for other type of action_list in DiscreteActSpace [skip ci] --- grid2op/Converter/IdToAct.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/grid2op/Converter/IdToAct.py b/grid2op/Converter/IdToAct.py index 063b1f59..1adf80dc 100644 --- a/grid2op/Converter/IdToAct.py +++ b/grid2op/Converter/IdToAct.py @@ -274,7 +274,16 @@ def init_converter(self, all_actions=None, **kwargs): "grid2op action. The error was:\n{}".format(e) ) from exc_ else: - raise RuntimeError("Impossible to load the action provided.") + # first make sure that all action is "correct" + try: + nb = len(all_actions) # assert I can compute the "len" + for i in range(nb): + act = all_actions[i] # assert I can use the `[]` operator + assert isinstance(act, BaseAction) # assert what's in there is a BaseAction + except Exception as exc_: + raise RuntimeError("Impossible to load the action provided.") from exc_ + # does not copy here (to save memory in case of shared memory setting) + self.all_actions = all_actions self.n = len(self.all_actions) def filter_action(self, filtering_fun): From a1fccd3b3e971991a6a6c32ad01319c2ba20e316 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 10 Oct 2024 16:31:03 +0200 Subject: [PATCH 18/29] trying to fix error caused by gymnasium 1.0 --- grid2op/gym_compat/gymenv.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/grid2op/gym_compat/gymenv.py b/grid2op/gym_compat/gymenv.py index 1bb7b746..9b054dc4 100644 --- a/grid2op/gym_compat/gymenv.py +++ b/grid2op/gym_compat/gymenv.py @@ -149,6 +149,8 @@ def _aux_step_new(self, gym_action: ActType) -> Tuple[ObsType, float, bool, bool g2op_obs, reward, terminated, info = self.init_env.step(g2op_act) gym_obs = self.observation_space.to_gym(g2op_obs) truncated = False # see https://github.com/openai/gym/pull/2752 + if "exception" in info: + info["exception"] = [str(el) for el in info["exception"]] return gym_obs, float(reward), terminated, truncated, info def _aux_reset(self, From 90d37c518b32bcca2a633b9115baaded0081cd2b Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 10 Oct 2024 17:16:31 +0200 Subject: [PATCH 19/29] trying to fix error caused by gymnasium 1.0 --- grid2op/tests/test_new_reset.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/grid2op/tests/test_new_reset.py b/grid2op/tests/test_new_reset.py index 9977ffb8..a96eac4f 100644 --- a/grid2op/tests/test_new_reset.py +++ b/grid2op/tests/test_new_reset.py @@ -60,23 +60,23 @@ def _aux_obs_equals(self, obs1, obs2): def test_gym_env(self): gym_env = GymEnv(self.env) - # original way - gym_env.init_env.set_id(0) - gym_env.init_env.seed(0) - obs, *_ = gym_env.reset() + # original way (deprecated) + # gym_env.init_env.set_id(0) + # gym_env.init_env.seed(0) + # obs, info = gym_env.reset() # test with seed in reset gym_env.init_env.set_id(0) - obs_seed, *_ = gym_env.reset(seed=0) + obs_seed, info_seed = gym_env.reset(seed=0) # test with ts_id in reset gym_env.init_env.seed(0) - obs_ts, *_ = gym_env.reset(options={"time serie id": 0}) + obs_ts, info_ts = gym_env.reset(options={"time serie id": 0}) # test with both - obs_both, *_ = gym_env.reset(seed=0, options={"time serie id": 0}) + obs_both, info_both = gym_env.reset(seed=0, options={"time serie id": 0}) - self._aux_obs_equals(obs_seed, obs) - self._aux_obs_equals(obs_ts, obs) - self._aux_obs_equals(obs_both, obs) + # self._aux_obs_equals(obs_seed, obs) + self._aux_obs_equals(obs_ts, obs_seed) + self._aux_obs_equals(obs_both, obs_seed) \ No newline at end of file From 9792478609fb046d7079ecacf12ddce97854cb3f Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 10 Oct 2024 17:42:38 +0200 Subject: [PATCH 20/29] removing the word gym and replace it by gymnasium in the doc [skip ci] --- docs/gym.rst | 62 +++++++++++++++++++-------------------- docs/makeenv.rst | 6 ++-- docs/model_free.rst | 2 +- docs/plot.rst | 8 ++--- docs/quickstart.rst | 10 +++---- docs/user/environment.rst | 2 +- docs/user/runner.rst | 4 +-- 7 files changed, 47 insertions(+), 47 deletions(-) diff --git a/docs/gym.rst b/docs/gym.rst index 02e47d79..931bb093 100644 --- a/docs/gym.rst +++ b/docs/gym.rst @@ -29,7 +29,7 @@ your base code. More information on the section :ref:`gymnasium_gym` -Before grid2op 1.2.0 only some classes fully implemented the open AI gym interface: +Before grid2op 1.2.0 only some classes fully implemented the gymnasium interface: - the :class:`grid2op.Environment` (with methods such as `env.reset`, `env.step` etc.) - the :class:`grid2op.Agent` (with the `agent.act` etc.) @@ -37,12 +37,12 @@ Before grid2op 1.2.0 only some classes fully implemented the open AI gym interfa Starting from 1.2.0 we implemented some automatic converters that are able to automatically map -grid2op representation for the action space and the observation space into open AI gym "spaces". More precisely these +grid2op representation for the action space and the observation space into gymnasium "spaces". More precisely these are represented as gym.spaces.Dict. -As of grid2op 1.4.0 we tighten the gap between openAI gym and grid2op by introducing the dedicated module +As of grid2op 1.4.0 we tighten the gap between gymnasium and grid2op by introducing the dedicated module `grid2op.gym_compat` . Withing this module there are lots of functionalities to convert a grid2op environment -into a gym environment (that inherit `gym.Env` instead of "simply" implementing the open ai gym interface). +into a gymnasium environment (that inherit `gymnasium.Env` instead of "simply" implementing the gymnasium interface). A simple usage is: @@ -55,12 +55,12 @@ A simple usage is: env_name = "l2rpn_case14_sandbox" # or any other grid2op environment name g2op_env = grid2op.make(env_name) # create the gri2op environment - gym_env = GymEnv(g2op_env) # create the gym environment + gym_env = GymEnv(g2op_env) # create the gymnasium environment - # check that this is a properly defined gym environment: + # check that this is a properly defined gymnasium environment: import gym - print(f"Is gym_env and open AI gym environment: {isinstance(gym_env, gym.Env)}") - # it shows "Is gym_env and open AI gym environment: True" + print(f"Is gym_env a gymnasium environment: {isinstance(gym_env, gym.Env)}") + # it shows "Is gym_env a gymnasium environment: True" .. note:: @@ -73,9 +73,9 @@ A simple usage is: .. warning:: The `gym` package has some breaking API change since its version 0.26. We attempted, in grid2op, to maintain compatibility both with former versions and later ones. This makes **this - class behave differently depending on the version of gym you have installed** ! + class behave differently depending on the version of gymnasium you have installed** ! - The main changes involve the functions `env.step` and `env.reset` (core gym functions) + The main changes involve the functions `env.step` and `env.reset` (core gymnasium functions) This page is organized as follow: @@ -164,7 +164,7 @@ You can transform the observation space as you wish. There are some examples in Default Action space ****************************** -The default action space is also a type of gym Dict. As for the observation space above, it is a +The default action space is also a type of gymnasium Dict. As for the observation space above, it is a straight translation from the attribute of the action to the key of the dictionary. This gives: - "change_bus": MultiBinary(`env.dim_topo`) @@ -177,7 +177,7 @@ straight translation from the attribute of the action to the key of the dictiona - "raise_alarm": MultiBinary(`env.dim_alarms`) - "raise_alert": MultiBinary(`env.dim_alerts`) -For example you can create a "gym action" (for the default encoding) like: +For example you can create a "gymnasium action" (for the default encoding) like: .. code-block:: python @@ -191,7 +191,7 @@ For example you can create a "gym action" (for the default encoding) like: gym_env = GymEnv(env) seed = ... - obs, info = gym_env.reset(seed) # for new gym interface + obs, info = gym_env.reset(seed) # for new gymnasium interface # do nothing gym_act = {} @@ -199,19 +199,19 @@ For example you can create a "gym action" (for the default encoding) like: #change the bus of the element 6 and 7 of the "topo_vect" gym_act = {} - gym_act["change_bus"] = np.zeros(env.dim_topo, dtype=np.int8) # gym encoding of a multi binary + gym_act["change_bus"] = np.zeros(env.dim_topo, dtype=np.int8) # gymnasium encoding of a multi binary gym_act["change_bus"][[6, 7]] = 1 obs, reward, done, truncated, info = gym_env.step(gym_act) # redispatch generator 2 of 1.7MW gym_act = {} - gym_act["redispatch"] = np.zeros(env.n_gen, dtype=np.float32) # gym encoding of a Box + gym_act["redispatch"] = np.zeros(env.n_gen, dtype=np.float32) # gymnasium encoding of a Box gym_act["redispatch"][2] = 1.7 obs, reward, done, truncated, info = gym_env.step(gym_act) # set the bus of element 8 and 9 to bus 2 gym_act = {} - gym_act["set_bus"] = np.zeros(env.dim_topo, dtype=int) # gym encoding of a Box + gym_act["set_bus"] = np.zeros(env.dim_topo, dtype=int) # gymnasium encoding of a Box gym_act["set_bus"][[8, 9]] = 2 obs, reward, done, truncated, info = gym_env.step(gym_act) @@ -238,7 +238,7 @@ If you want a full control on this spaces, you need to implement something like: env = grid2op.make(env_name) from grid2op.gym_compat import GymEnv - # this of course will not work... Replace "AGymSpace" with a normal gym space, like Dict, Box, MultiDiscrete etc. + # this of course will not work... Replace "AGymSpace" with a normal gymnasium space, like Dict, Box, MultiDiscrete etc. from gym.spaces import AGymSpace gym_env = GymEnv(env) @@ -253,7 +253,7 @@ If you want a full control on this spaces, you need to implement something like: def to_gym(self, observation): # this is this very same function that you need to implement # it should have this exact name, take only one observation (grid2op) as input - # and return a gym object that belong to your space "AGymSpace" + # and return a gymnasium object that belong to your space "AGymSpace" return SomethingThatBelongTo_AGymSpace # eg. return np.concatenate((obs.gen_p * 0.1, np.sqrt(obs.load_p)) @@ -268,7 +268,7 @@ And for the action space: env = grid2op.make(env_name) from grid2op.gym_compat import GymEnv - # this of course will not work... Replace "AGymSpace" with a normal gym space, like Dict, Box, MultiDiscrete etc. + # this of course will not work... Replace "AGymSpace" with a normal gymnasium space, like Dict, Box, MultiDiscrete etc. from gym.spaces import AGymSpace gym_env = GymEnv(env) @@ -282,7 +282,7 @@ And for the action space: def from_gym(self, gym_action): # this is this very same function that you need to implement - # it should have this exact name, take only one action (member of your gym space) as input + # it should have this exact name, take only one action (member of your gymnasium space) as input # and return a grid2op action return TheGymAction_ConvertedTo_Grid2op_Action # eg. return np.concatenate((obs.gen_p * 0.1, np.sqrt(obs.load_p)) @@ -311,7 +311,7 @@ and divide input data by `divide`): env_name = "l2rpn_case14_sandbox" # or any other grid2op environment name g2op_env = grid2op.make(env_name) # create the gri2op environment - gym_env = GymEnv(g2op_env) # create the gym environment + gym_env = GymEnv(g2op_env) # create the gymnasium environment ob_space = gym_env.observation_space ob_space = ob_space.reencode_space("actual_dispatch", @@ -336,7 +336,7 @@ the log of the loads instead of giving the direct value to your agent. This can env_name = "l2rpn_case14_sandbox" # or any other grid2op environment name g2op_env = grid2op.make(env_name) # create the gri2op environment - gym_env = GymEnv(g2op_env) # create the gym environment + gym_env = GymEnv(g2op_env) # create the gymnasium environment ob_space = gym_env.observation_space shape_ = (g2op_env.n_load, ) @@ -350,7 +350,7 @@ the log of the loads instead of giving the direct value to your agent. This can ) gym_env.observation_space = ob_space - # and now you will get the key "log_load" as part of your gym observation. + # and now you will get the key "log_load" as part of your gymnasium observation. A detailed list of such "converter" is documented on the section "Detailed Documentation by class". In the table below we describe some of them (**nb** if you notice a converter is not displayed there, @@ -360,11 +360,11 @@ do not hesitate to write us a "feature request" for the documentation, thanks in Converter name Objective ============================================= ============================================================ :class:`ContinuousToDiscreteConverter` Convert a continuous space into a discrete one -:class:`MultiToTupleConverter` Convert a gym MultiBinary to a gym Tuple of gym Binary and a gym MultiDiscrete to a Tuple of Discrete +:class:`MultiToTupleConverter` Convert a gymnasium MultiBinary to a gymnasium Tuple of gymnasium Binary and a gymnasium MultiDiscrete to a Tuple of Discrete :class:`ScalerAttrConverter` Allows to scale (divide an attribute by something and subtract something from it) -`BaseGymSpaceConverter.add_key`_ Allows you to compute another "part" of the observation space (you add an information to the gym space) +`BaseGymSpaceConverter.add_key`_ Allows you to compute another "part" of the observation space (you add an information to the gymnasium space) `BaseGymSpaceConverter.keep_only_attr`_ Allows you to specify which part of the action / observation you want to keep -`BaseGymSpaceConverter.ignore_attr`_ Allows you to ignore some attributes of the action / observation (they will not be part of the gym space) +`BaseGymSpaceConverter.ignore_attr`_ Allows you to ignore some attributes of the action / observation (they will not be part of the gymnasium space) ============================================= ============================================================ .. warning:: @@ -383,7 +383,7 @@ Converter name Objective .. note:: With the "converters" above, note that the observation space AND action space will still - inherit from gym Dict. + inherit from gymnasium Dict. They are complex spaces that are not well handled by some RL framework. @@ -395,19 +395,19 @@ Converter name Objective Customizing the action and observation space, into Box or Discrete ******************************************************************* -The use of the converter above is nice if you can work with gym Dict, but in some cases, or for some frameworks +The use of the converter above is nice if you can work with gymnasium Dict, but in some cases, or for some frameworks it is not convenient to do it at all. -TO alleviate this problem, we developed 4 types of gym action space, following the architecture +TO alleviate this problem, we developed 4 types of gymnasium action space, following the architecture detailed in subsection :ref:`base_gym_space_function` =============================== ============================================================ Converter name Objective =============================== ============================================================ :class:`BoxGymObsSpace` Convert the observation space to a single "Box" -:class:`BoxGymActSpace` Convert a gym MultiBinary to a gym Tuple of gym Binary and a gym MultiDiscrete to a Tuple of Discrete +:class:`BoxGymActSpace` Convert a gymnasium MultiBinary to a gymnasium Tuple of gymnasium Binary and a gymnasium MultiDiscrete to a Tuple of Discrete :class:`MultiDiscreteActSpace` Allows to scale (divide an attribute by something and subtract something from it) -:class:`DiscreteActSpace` Allows you to compute another "part" of the observation space (you add an information to the gym space) +:class:`DiscreteActSpace` Allows you to compute another "part" of the observation space (you add an information to the gymnasium space) =============================== ============================================================ They can all be used like: diff --git a/docs/makeenv.rst b/docs/makeenv.rst index 55184f7a..493818c6 100644 --- a/docs/makeenv.rst +++ b/docs/makeenv.rst @@ -25,11 +25,11 @@ To get started with such an environment, you can simply do: You can consult the different notebooks in the `getting_stared` directory of this package for more information on how to use it. -Created Environment should behave exactly like a gym environment. If you notice any unwanted behavior, please address +Created Environment should behave exactly like a gymnasium environment. If you notice any unwanted behavior, please address an issue in the official grid2op repository: `Grid2Op `_ -The environment created with this method should be fully compatible with the gym framework: if you are developing -a new algorithm of "Reinforcement Learning" and you used the openai gym framework to do so, you can port your code +The environment created with this method should be fully compatible with the gymnasium framework: if you are developing +a new algorithm of "Reinforcement Learning" and you used the openai gymnasium framework to do so, you can port your code in a few minutes (basically this consists in adapting the input and output dimension of your BaseAgent) and make it work with a Grid2Op environment. An example of such modifications is exposed in the getting_started/ notebooks. diff --git a/docs/model_free.rst b/docs/model_free.rst index 94f8f745..10424d7c 100644 --- a/docs/model_free.rst +++ b/docs/model_free.rst @@ -8,7 +8,7 @@ Model Free Reinforcement Learning See some example in "l2rpn-baselines" package for now ! -The main idea is first to convert the grid2op environment to a gym environment, for example using :ref:`openai-gym`. +The main idea is first to convert the grid2op environment to a gymnasium environment, for example using :ref:`openai-gym`. And then use some libaries available, for example `Stable Baselines `_ or `RLLIB `_ diff --git a/docs/plot.rst b/docs/plot.rst index 25058cf4..ab7f6f93 100644 --- a/docs/plot.rst +++ b/docs/plot.rst @@ -76,10 +76,10 @@ An possible output will look like this: Render the state of the grid ----------------------------- -During the gym loop -++++++++++++++++++++ +During the gymnasium loop +++++++++++++++++++++++++++ In Grid2Op we also made available the possibility to render the state of the grid that your agent sees before taking -an action. This can be done with the provided environments following openAI gym interface like this: +an action. This can be done with the provided environments following gymnasium interface like this: .. code-block:: python @@ -104,7 +104,7 @@ significantly. Offline, after the scenarios were played ++++++++++++++++++++++++++++++++++++++++ -In Grid2Op, you can execute a :ref:`runner-module` to perform the "gym loops" and store the results +In Grid2Op, you can execute a :ref:`runner-module` to perform the "gymnasium loops" and store the results in a standardized manner. Once stored, the results can be loaded back and "replayed" using the appropriate class. Here is how you can do this: diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 3955b818..54309452 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -88,7 +88,7 @@ that are available, without any installation thanks to `Binder `_ . Feel free to visit the "getting_started" page for more information and a detailed tour about the issue that grid2op tries to address. -The most basic code, for those familiar with openAI gym (a well-known framework in reinforcement learning) is: +The most basic code, for those familiar with gymnasium (a well-known framework in reinforcement learning) is: .. code-block:: python @@ -101,7 +101,7 @@ The most basic code, for those familiar with openAI gym (a well-known framework from grid2op.Agent import RandomAgent my_agent = RandomAgent(env.action_space) - # proceed as you would any open ai gym loop + # proceed as you would any gymnasium loop nb_episode = 10 for _ in range(nb_episode): # you perform in this case 10 different episodes @@ -115,9 +115,9 @@ The most basic code, for those familiar with openAI gym (a well-known framework act = my_agent.act(obs, reward, done) obs, reward, done, info = env.step(act) -.. warning:: Grid2Op environments implements the interface of defined by openAI gym environment, but they don't - inherit from them. You can use the Grid2Op environment as you would any Gym environment but they are - not strictly speaking gym environment. +.. warning:: Grid2Op environments implements the interface of defined by gymnasium environment, but they don't + inherit from them. You can use the Grid2Op environment as you would any gymnasium environment but they are + not strictly speaking gymnasium environment. To make the use of grid2op alongside grid2op environment easier, we developed a module described in :ref:`openai-gym`. diff --git a/docs/user/environment.rst b/docs/user/environment.rst index 3b4af59c..5c1c9613 100644 --- a/docs/user/environment.rst +++ b/docs/user/environment.rst @@ -32,7 +32,7 @@ In this section we present some way to use the :class:`Environment` class. Basic Usage ++++++++++++ -This example is adapted from gym documentation available at +This example is adapted from gymnasium documentation available at `gym random_agent.py `_ ): .. code-block:: python diff --git a/docs/user/runner.rst b/docs/user/runner.rst index 2752971c..8f96ffaf 100644 --- a/docs/user/runner.rst +++ b/docs/user/runner.rst @@ -13,7 +13,7 @@ Objectives The runner class aims at: i) facilitate the evaluation of the performance of :class:`grid2op.Agent` by performing automatically the - "open ai gym loop" (see below) + "gymnasium loop" (see below) ii) define a format to store the results of the evaluation of such agent in a standardized manner iii) this "agent logs" can then be re read by third party applications, such as `grid2viz `_ or by internal class to ease the study of the behaviour of @@ -21,7 +21,7 @@ iii) this "agent logs" can then be re read by third party applications, such as :class:`grid2op.Episode.EpisodeReplay` iv) allow easy use of parallelization of this assessment. -Basically, the runner simplifies the assessment of the performance of some agent. This is the "usual" gym code to run +Basically, the runner simplifies the assessment of the performance of some agent. This is the "usual" gymnasium code to run an agent: .. code-block:: python From de693d2dbc11ea56ca1c0a1a8c67b5cdd8f4122f Mon Sep 17 00:00:00 2001 From: Benjamin DONNOT Date: Mon, 14 Oct 2024 10:44:13 +0200 Subject: [PATCH 21/29] Create CODE_OF_CONDUCT.md Using the github default one --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..18c91471 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. From c0b2342ed66f5560feb5a818e4df47e3b8282d3d Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 14 Oct 2024 10:49:19 +0200 Subject: [PATCH 22/29] remove v1 and v2 github artifacts --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 18544397..97af71e9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -125,7 +125,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@v1 + uses: actions/checkout@v3 with: submodules: true @@ -203,7 +203,7 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@v1 + uses: actions/checkout@v3 with: submodules: true From 4a30e5658b868e51502de9304f189660a9dfa7b7 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 14 Oct 2024 10:51:54 +0200 Subject: [PATCH 23/29] add some tests for python 3.13 --- .github/workflows/main.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 97af71e9..6fa5206e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,6 +43,11 @@ jobs: abi: cp312, version: '3.12', } + - { + name: cp313, + abi: cp313, + version: '3.13', + } steps: @@ -121,6 +126,10 @@ jobs: name: cp312, version: '3.12', } + - { + name: cp313, + version: '3.13', + } steps: From 0173dbcd32ce96e992d0561f9f8771326ffa1ad2 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 14 Oct 2024 11:10:12 +0200 Subject: [PATCH 24/29] rollback python 3.13 support, waiting for pandapower --- .github/workflows/main.yml | 18 +++++++++--------- setup.py | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6fa5206e..92a60c6f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -43,11 +43,11 @@ jobs: abi: cp312, version: '3.12', } - - { - name: cp313, - abi: cp313, - version: '3.13', - } + # - { + # name: cp313, # issue with pandapower atm + # abi: cp313, + # version: '3.13', + # } steps: @@ -126,10 +126,10 @@ jobs: name: cp312, version: '3.12', } - - { - name: cp313, - version: '3.13', - } + # - { # issue with pandapower atm + # name: cp313, + # version: '3.13', + # } steps: diff --git a/setup.py b/setup.py index db3c36bf..34d79b3b 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def my_test_suite(): pkgs = { "required": [ - "numpy>=1.20,<2", # disable numpy 2 for now + "numpy", "scipy>=1.4.1", "pandas>=1.0.3", "pandapower>=2.2.2", From a98a470e329f4098a593e96e56d09d0dcbb2728b Mon Sep 17 00:00:00 2001 From: Benjamin DONNOT Date: Mon, 14 Oct 2024 11:46:33 +0200 Subject: [PATCH 25/29] Create CONTRIBUTING.md --- CONTRIBUTING.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..69dbbc0f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,52 @@ +# Contribution + +We welcome contributions from everyone. + +If you want to contribute, a good starting point is to get in touch with us through: +- github (discussion, issues or pull-request) +- the project [discord](https://discord.gg/cYsYrPT) +- mail (see current corresponding author from the grid2op package on pypi here https://pypi.org/project/Grid2Op/) + +Contribution can take different forms: + +- reporting bugs +- improving the documentation +- improving the code examples (notebooks or example in the doc) +- fixing some reported issues (for example at https://github.com/rte-france/Grid2Op/issues ) +- adding a new functionality to grid2op (or increase its speed) +- extend grid2op + +# What to do ? + +For smaller changes (including, but not limited to the reporting of a bug or a contribution to the explanotory notebooks or the documentations) +a simple "pull request" with your modifications by detailing what you had in mind and the goal of your changes. + +In case of a major change (or if you have a doubt on what is "a small change"), please open an issue first +to discuss what you would like to change and then follow as closely as possible the guidelines below. This is to ensure +first that no other contribution is this direction is being currently made but also to make sure that it +does not conflicts with some core ideas of the project. + +# Guidelines for contributing to the codebase + +For larger contributions, you can follow the given : + +1. fork the repository located at +2. synch your fork with the "latest developement branch of grid2op". For example, if the latest grid2op release + on pypi is `1.10.3` you need to synch your repo with the branch named `dev_1.10.4` or `dev_1.11.0` (if + the branch `dev_1.10.4` does not exist). It will be the highest number in the branches `dev_*` on + grid2op official github repository. +3. implement your functionality / code your modifications, add documentation or any kind of contribution +4. make sure to add tests and documentation if applicable +5. once it is developed, synch your repo with the last development branch again (see point 2 above) and + make sure to solve any possible conflicts +6. write a pull request and make sure to target the right branch (the "last development branch") + +# When will it be merged ? + +A contribution will be merged once approved by the maintainers (this is why it is recommended to first +get in touch with the team) + +Code in the contribution should pass all the current tests, have some dedicated tests for the new feature (if applicable) +and documentation (if applicable). + +New contributions should respect the "**Code of Conduct**" of grid2op. From b77f31856cfcfd79f02315ec80d962c946ec2158 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 14 Oct 2024 11:50:29 +0200 Subject: [PATCH 26/29] adding contributing.md and code of conduct [skip ci] --- CHANGELOG.rst | 5 +++++ README.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 82df64ba..c859329e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -77,6 +77,11 @@ Next release instead of raising a warning. - [FIXED] some issue with gym_compat module for "newest" version of gymnasium (1.0.0) +- [FIXED] github ci (v1 and v2 artifact are now deprecated) +- [ADDED] a code of conduct from github +- [ADDED] a "CONTRIBUTING.md" files (instead of having contribution instructions + in the readme) +- [ADDED] numpy 2 support (now that pandapower allows it) - [IMPROVED] error message when forecasts are not correctly set-up [1.10.3] - 2024-07-12 diff --git a/README.md b/README.md index b24db06d..7406a782 100644 --- a/README.md +++ b/README.md @@ -262,6 +262,11 @@ For example, the "getting started" notebooks referenced some pages of the help. ## Contributing +Please consult the "CONTRIBUTING.md" file for extra information. This is a summary and +in case of conflicting instructions, follow the one given in the `CONTRIBUTING.md` file +and discard these ones. + + We welcome contributions from everyone. They can take the form of pull requests for smaller changed. In case of a major change (or if you have a doubt on what is "a small change"), please open an issue first to discuss what you would like to change. From 3e23381092b9d70c6c456aab29444c554d78f577 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 14 Oct 2024 11:53:54 +0200 Subject: [PATCH 27/29] slightly change the CONTRIBUTING.md and the changelog, for the new release --- CHANGELOG.rst | 3 ++- CONTRIBUTING.md | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c859329e..e9116b35 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -66,7 +66,7 @@ Next release - TODO work on the reward class (see https://github.com/rte-france/Grid2Op/issues/584) -[1.10.4] - 2024-xx-yy +[1.10.4] - 2024-10-14 ------------------------- - [FIXED] an issue in the backend: if the backend failed to be created the `_grid` attribute was set to `None` and not set back to @@ -84,6 +84,7 @@ Next release - [ADDED] numpy 2 support (now that pandapower allows it) - [IMPROVED] error message when forecasts are not correctly set-up + [1.10.3] - 2024-07-12 ------------------------- - [BREAKING] `env.chronics_hander.set_max_iter(xxx)` is now a private function. Use diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 69dbbc0f..6fb0b538 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -49,4 +49,7 @@ get in touch with the team) Code in the contribution should pass all the current tests, have some dedicated tests for the new feature (if applicable) and documentation (if applicable). +If you contribute to some grid2op notebooks, please make sure to "clear all outputs" +before making the pull request. + New contributions should respect the "**Code of Conduct**" of grid2op. From a51c8257937a33649e7fc705c87ff2dd05995488 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 14 Oct 2024 12:06:20 +0200 Subject: [PATCH 28/29] update docs --- docs/environment.rst | 2 +- grid2op/Environment/baseEnv.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/environment.rst b/docs/environment.rst index b90c0c8b..5c71eb3e 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -151,7 +151,7 @@ Feel free to consult the documentation of the :func:`Environment.reset` function for more information (this doc might be outdated, the one of the function should be more up to date with the code). -.. info:: +.. note:: In the near future (next few releases) we will also attempt to make the customization of the `parameters` or the `skip number of steps`, `maximum duration of the scenarios` also available in `env.reset()` options. diff --git a/grid2op/Environment/baseEnv.py b/grid2op/Environment/baseEnv.py index 440d89e9..f45a733f 100644 --- a/grid2op/Environment/baseEnv.py +++ b/grid2op/Environment/baseEnv.py @@ -4448,7 +4448,7 @@ def classes_are_in_files(self) -> bool: Whether the classes created when this environment has been made are store on the hard drive (will return `True`) or not. - .. info:: + .. note:: This will become the default behaviour in future grid2op versions. See :ref:`troubleshoot_pickle` for more information. From 3116d0e9579e8fd48424b63ad37521ae6b2e08e3 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 14 Oct 2024 14:41:03 +0200 Subject: [PATCH 29/29] refacto the 'make_release' to sign commits [skip ci] --- utils/make_release.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/utils/make_release.py b/utils/make_release.py index c91346dd..171c12ad 100644 --- a/utils/make_release.py +++ b/utils/make_release.py @@ -197,10 +197,10 @@ def modify_and_push_docker(version, # grid2op version start_subprocess_print(["git", "add", f'{os.path.join(PATH_PREVIOUS_RUNNER, f"res_agent_{version}")}/*']) # Commit - start_subprocess_print(["git", "commit", "-m", "Release v{}".format(version)]) + start_subprocess_print(["git", "commit", "-S", "-m", "Release v{}".format(version)]) if not is_prerelease: # Create a new git tag - start_subprocess_print(["git", "tag", "-a", "v{}".format(version), "-m", "Release v{}".format(version)]) + start_subprocess_print(["git", "tag", "-s", "-a", "v{}".format(version), "-m", "Release v{}".format(version)]) if is_prerelease: print("Please push changes: 'git push'") @@ -227,4 +227,3 @@ def modify_and_push_docker(version, # grid2op version path=path, docker_versions=[version, "latest"], docker_tags=["--no-cache"]) -