From 98e4e1846957f9da4c3938e77ac677e04d9c7681 Mon Sep 17 00:00:00 2001 From: Benedikt Date: Mon, 18 Sep 2023 11:27:50 -0700 Subject: [PATCH 01/46] Fixed readme style --- README.md | 215 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 120 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index fb3489a98..77222cc10 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Grid2Op + [![Downloads](https://pepy.tech/badge/grid2op)](https://pepy.tech/project/grid2op) [![PyPi_Version](https://img.shields.io/pypi/v/grid2op.svg)](https://pypi.org/project/Grid2Op/) [![PyPi_Compat](https://img.shields.io/pypi/pyversions/grid2op.svg)](https://pypi.org/project/Grid2Op/) @@ -10,57 +11,65 @@ Grid2Op is a platform, built with modularity in mind, that allows to perform powergrid operation. And that's what it stands for: Grid To Operate. -Grid2Op acts as a replacement of [pypownet](https://github.com/MarvinLer/pypownet) -as a library used for the Learning To Run Power Network [L2RPN](https://l2rpn.chalearn.org/). +Grid2Op acts as a replacement of [pypownet](https://github.com/MarvinLer/pypownet) +as a library used for the Learning To Run Power Network [L2RPN](https://l2rpn.chalearn.org/). This framework allows to perform most kind of powergrid operations, from modifying the setpoint of generators, to load shedding, performing maintenance operations or modifying the *topology* of a powergrid to solve security issues. -Official documentation: the official documentation is available at +Official documentation: the official documentation is available at [https://grid2op.readthedocs.io/](https://grid2op.readthedocs.io/). -* [1 Installation](#installation) - * [1.1 Setup a Virtualenv (optional)](#setup-a-virtualenv-optional) - * [1.2 Install from source](#install-from-source) - * [1.3 Install from PyPI](#install-from-pypi) - * [1.4 Install for contributors](#install-for-contributors) - * [1.5 Docker](#docker) -* [2 Main features of Grid2Op](#main-features-of-grid2op) -* [3 Getting Started](#getting-started) - * [0 Basic features](getting_started/0_basic_functionalities.ipynb) - * [1 BaseObservation Agents](getting_started/1_Observation_Agents.ipynb) - * [2 BaseAction Grid Manipulation](getting_started/2_Action_GridManipulation.ipynb) - * [3 Training An BaseAgent](getting_started/3_TrainingAnAgent.ipynb) - * [4 Study Your BaseAgent](getting_started/4_StudyYourAgent.ipynb) -* [4 Citing](#Citing) -* [5 Documentation](#documentation) -* [6 Contribute](#contributing) -* [7 Test and known issues](#tests-and-known-issues) -* [8 License information](#license-information) - -# Installation -## Requirements: -* Python >= 3.6 - -## Setup a Virtualenv (optional) -### Create a virtual environment +* [1 Installation](#installation) + * [1.1 Setup a Virtualenv (optional)](#setup-a-virtualenv-optional) + * [1.2 Install from source](#install-from-source) + * [1.3 Install from PyPI](#install-from-pypi) + * [1.4 Install for contributors](#install-for-contributors) + * [1.5 Docker](#docker) +* [2 Main features of Grid2Op](#main-features-of-grid2op) +* [3 Getting Started](#getting-started) + * [0 Basic features](getting_started/0_basic_functionalities.ipynb) + * [1 BaseObservation Agents](getting_started/1_Observation_Agents.ipynb) + * [2 BaseAction Grid Manipulation](getting_started/2_Action_GridManipulation.ipynb) + * [3 Training An BaseAgent](getting_started/3_TrainingAnAgent.ipynb) + * [4 Study Your BaseAgent](getting_started/4_StudyYourAgent.ipynb) +* [4 Citing](#citing) +* [5 Documentation](#documentation) +* [6 Contribute](#contributing) +* [7 Test and known issues](#tests-and-known-issues) +* [8 License information](#license-information) + +## Installation + +### Requirements + +* Python >= 3.6 + +### Setup a Virtualenv (optional) + +#### Create a virtual environment + ```commandline cd my-project-folder pip3 install -U virtualenv python3 -m virtualenv venv_grid2op ``` -### Enter virtual environment + +#### Enter virtual environment + ```commandline source venv_grid2op/bin/activate ``` -## Install from PyPI +### Install from PyPI + ```commandline pip3 install grid2op ``` -## Install from source +### Install from source + ```commandline git clone https://github.com/rte-france/Grid2Op.git cd Grid2Op @@ -68,7 +77,8 @@ pip3 install -U . cd .. ``` -## Install for contributors +### Install for contributors + ```commandline git clone https://github.com/rte-france/Grid2Op.git cd Grid2Op @@ -77,25 +87,30 @@ pip3 install -e .[optional] pip3 install -e .[docs] ``` -## Docker +### Docker + Grid2Op docker containers are available on [dockerhub](https://hub.docker.com/r/bdonnot/grid2op/tags). To install the latest Grid2Op container locally, use the following: + ```commandline docker pull bdonnot/grid2op:latest ``` -# Main features of Grid2Op -## Core functionalities +## Main features of Grid2Op + +### Core functionalities + Built with modulartiy in mind, Grid2Op is a library used for the "Learning To Run Power Network" [L2RPN](https://l2rpn.chalearn.org/) competitions series. It can also Its main features are: + * emulates the behavior of a powergrid of any size at any format (provided that a *backend* is properly implemented) -* allows for grid modifications (active and reactive load values, generator voltages setpoints, active production but most +* allows for grid modifications (active and reactive load values, generator voltages setpoints, active production but most importantly grid topology beyond powerline connection / disconnection) * allows for maintenance operations and powergrid topological changes -* can adopt any powergrid modeling, especially Alternating Current (AC) and Direct Current (DC) approximation to +* can adopt any powergrid modeling, especially Alternating Current (AC) and Direct Current (DC) approximation to when performing the compitations * supports changes of powerflow solvers, actions, observations to better suit any need in performing power system operations modeling * has an RL-focused interface, compatible with [OpenAI-gym](https://gym.openai.com/): same interface for the @@ -103,46 +118,48 @@ Its main features are: * parameters, game rules or type of actions are perfectly parametrizable * can adapt to any kind of input data, in various format (might require the rewriting of a class) -## Powerflow solver +### Powerflow solver + Grid2Op relies on an open source powerflow solver ([PandaPower](https://www.pandapower.org/)), -but is also compatible with other *Backend*. If you have at your disposal another powerflow solver, +but is also compatible with other *Backend*. If you have at your disposal another powerflow solver, the documentation of [grid2op/Backend](grid2op/Backend/Backend.py) can help you integrate it into a proper "Backend" and have Grid2Op using this powerflow instead of PandaPower. -# Getting Started -Some Jupyter notebook are provided as tutorials for the Grid2Op package. They are located in the -[getting_started](getting_started) directories. +## Getting Started + +Some Jupyter notebook are provided as tutorials for the Grid2Op package. They are located in the +[getting_started](getting_started) directories. TODO: this needs to be redone, refactorize and better explained for some of them. These notebooks will help you in understanding how this framework is used and cover the most interesting part of this framework: -* [00_Introduction](getting_started/00_Introduction.ipynb) +* [00_Introduction](getting_started/00_Introduction.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/00_Introduction.ipynb) - and [00_SmallExample](getting_started/00_SmallExample.ipynb) + and [00_SmallExample](getting_started/00_SmallExample.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/00_SmallExample.ipynb) - describe what is - adressed by the grid2op framework (with a tiny introductions to both power systems and reinforcement learning) + describe what is + adressed by the grid2op framework (with a tiny introductions to both power systems and reinforcement learning) and give and introductory example to a small powergrid manipulation. * [01_Grid2opFramework](getting_started/01_Grid2opFramework.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/01_Grid2opFramework.ipynb) - covers the basics + covers the basics of the - Grid2Op framework. It also covers how to create a valid environment and how to use the + Grid2Op framework. It also covers how to create a valid environment and how to use the `Runner` class to assess how well an agent is performing rapidly. * [02_Observation](getting_started/02_Observation.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/02_Observation.ipynb) - details how to create - an "expert agent" that will take pre defined actions based on the observation it gets from + details how to create + an "expert agent" that will take pre defined actions based on the observation it gets from the environment. This Notebook also covers the functioning of the BaseObservation class. * [03_Action](getting_started/03_Action.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/03_Action.ipynb) - demonstrates + demonstrates how to use the BaseAction class and how to manipulate the powergrid. * [04_TrainingAnAgent](getting_started/04_TrainingAnAgent.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/04_TrainingAnAgent.ipynb) - shows how to get started with + shows how to get started with reinforcement learning with the grid2op environment. It shows the basic on how to train a "PPO" model operating the grid relying on "stable baselines 3" PPO implementation. * [05_StudyYourAgent](getting_started/05_StudyYourAgent.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/05_StudyYourAgent.ipynb) @@ -152,48 +169,49 @@ interesting part of this framework: come soon. * [06_Redispatching_Curtailment](getting_started/06_Redispatching_Curtailment.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/06_Redispatching_Curtailment.ipynb) - explains what is the - "redispatching" and curtailment from the point - of view of a company who's in charge of keeping the powergrid safe (aka a Transmission System Operator) and how to - manipulate this concept in grid2op. Redispatching (and curtailment) allows you to perform **continuous** - actions on the powergrid + explains what is the + "redispatching" and curtailment from the point + of view of a company who's in charge of keeping the powergrid safe (aka a Transmission System Operator) and how to + manipulate this concept in grid2op. Redispatching (and curtailment) allows you to perform **continuous** + actions on the powergrid problem. * [07_MultiEnv](getting_started/07_MultiEnv.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/07_MultiEnv.ipynb) details how grid2op natively support a single agent interacting - with multiple environments at the same time. This is particularly handy to train "asynchronous" agent in the + with multiple environments at the same time. This is particularly handy to train "asynchronous" agent in the Reinforcement Learning community for example. * [08_PlottingCapabilities](getting_started/08_PlottingCapabilities.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/08_PlottingCapabilities.ipynb) - shows you the different ways with which you - can represent (visually) the grid your agent interact with. A renderer is available like in many open AI gym + shows you the different ways with which you + can represent (visually) the grid your agent interact with. A renderer is available like in many open AI gym environment. But you also have the possibility to post process an agent and make some movies out of it, and we also developed a Graphical User Interface (GUI) called "[grid2viz](https://github.com/mjothy/grid2viz)" that allows - to perform in depth study of your agent's behaviour on different scenarios and even to compare it with baselines. + to perform in depth study of your agent's behaviour on different scenarios and even to compare it with baselines. * [09_EnvironmentModifications](getting_started/09_EnvironmentModifications.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/09_EnvironmentModifications.ipynb) - elaborates on the maintenance, + elaborates on the maintenance, hazards and attacks. All three of these represents external events that can disconnect some powerlines. This notebook covers how to spot when such things happened and what can be done when the maintenance or the attack is over. * [10_StorageUnits](getting_started/10_StorageUnits.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/10_StorageUnits.ipynb) details the usage and behaviour of the storage units - in grid2op. + in grid2op. * [11_IntegrationWithExistingRLFrameworks](getting_started/11_IntegrationWithExistingRLFrameworks.ipynb) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/rte-france/Grid2Op/blob/master/getting_started/11_IntegrationWithExistingRLFrameworks.ipynb) explains how to use grid2op with other reinforcement learning framework. TODO: this needs to be redone - -Try them out in your own browser without installing -anything with the help of mybinder: + +Try them out in your own browser without installing +anything with the help of mybinder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/rte-france/Grid2Op/master) Or thanks to google colab (all links are provided near the notebook description) -# Citing +## Citing If you use this package in one of your work, please cite: -``` + +```text @misc{grid2op, author = {B. Donnot}, title = {{Grid2op- A testbed platform to model sequential decision making in power systems. }}, @@ -204,34 +222,37 @@ If you use this package in one of your work, please cite: } ``` -# Documentation +## Documentation -The official documentation is available at +The official documentation is available at [https://grid2op.readthedocs.io/](https://grid2op.readthedocs.io/). -## Build the documentation locally +### Build the documentation locally A copy of the documentation can be built if the project is installed *from source*: you will need Sphinx, a Documentation building tool, and a nice-looking custom [Sphinx theme similar to the one of readthedocs.io](https://sphinx-rtd-theme.readthedocs.io/en/latest/). These can be installed with: + ```commandline pip3 install -U grid2op[docs] ``` -This installs both the Sphinx package and the custom template. + +This installs both the Sphinx package and the custom template. Then, on systems where `make` is available (mainly gnu-linux and macos) the documentation can be built with the command: + ```commandline make html ``` For windows, or systems where `make` is not available, the command: + ```commandline sphinx-build -b html docs documentation ``` - -This will create a "documentation" subdirectory and the main entry point of the document will be located at +This will create a "documentation" subdirectory and the main entry point of the document will be located at [index.html](documentation/html/index.html). It is recommended to build this documentation locally, for convenience. @@ -239,17 +260,17 @@ For example, the "getting started" notebooks referenced some pages of the help. -# Contributing +## Contributing -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 +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. To contribute to this code, you need to: -1. fork the repository located at https://github.com/rte-france/Grid2Op +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.6.5` you need to synch your repo with the branch named `dev_1.6.6` or `dev_1.7.0` (if + on pypi is `1.6.5` you need to synch your repo with the branch named `dev_1.6.6` or `dev_1.7.0` (if the branch `dev_1.6.6` 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 or anything else @@ -258,55 +279,59 @@ To contribute to this code, you need to: make sure to solve any possible conflicts 6. write a pull request and make sure to target the right branch (the "last development branch") - Code in the contribution should pass all the tests, have some dedicated tests for the new feature (if applicable) and documentation (if applicable). Before implementing any major feature, please write a github issue first. -# Tests and known issues +## Tests and known issues + +### Tests performed currently -## Tests performed currently Grid2op is currently tested on windows, linux and macos. The unit tests includes testing, on linux machines the correct integration of grid2op with: -- python 3.8 -- python 3.9 -- python 3.10 -- python 3.11 +* python 3.8 +* python 3.9 +* python 3.10 +* python 3.11 On all of these cases, we tested grid2op on all available numpy version >= 1.20 (**nb** available numpy versions depend on python version). The complete test suit is run on linux with the latest numpy version on python 3.8. -## Known issues +### Known issues 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, +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. A quick fix that is known to work include to set the `experimental_read_from_local_dir` when creating the environment with `grid2op.make(..., experimental_read_from_local_dir=True)` (see doc for more information) -## Perform tests locally +### Perform tests locally + Provided that Grid2Op is installed *from source*: -### Install additional dependencies +#### Install additional dependencies + ```commandline pip3 install -U grid2op[optional] ``` -### Launch tests + +#### Launch tests + ```commandline cd grid2op/tests python3 -m unittest discover ``` -# License information -Copyright 2019-2020 RTE France +## License information - RTE: http://www.rte-france.com +Copyright 2019-2020 RTE France +RTE: -This Source Code is subject to the terms of the Mozilla Public License (MPL) v2 also available +This Source Code is subject to the terms of the Mozilla Public License (MPL) v2 also available [here](https://www.mozilla.org/en-US/MPL/2.0/) From 61b0839d546ba60eabc5af06879e693db603b08e Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 20 Sep 2023 11:38:05 +0200 Subject: [PATCH 02/46] adding type hints for the backend classes --- CHANGELOG.rst | 5 + examples/backend_integration/Step1_loading.py | 20 ++- .../backend_integration/Step2_modify_load.py | 12 +- .../backend_integration/Step3_modify_gen.py | 12 +- .../Step4_modify_line_status.py | 15 +- .../Step5_modify_topology.py | 17 +- grid2op/Backend/backend.py | 139 +++++++++------ grid2op/Backend/educPandaPowerBackend.py | 39 ++-- grid2op/Backend/pandaPowerBackend.py | 167 +++++++++--------- grid2op/Environment/environment.py | 2 - setup.py | 3 +- 11 files changed, 239 insertions(+), 192 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a13bb88dc..bdc84f111 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -31,6 +31,11 @@ Change Log - [???] "asynch" multienv - [???] properly model interconnecting powerlines +[1.9.6] - 2023-xx-yy +---------------------- +- [ADDED] now depends on the `typing_extensions` package +- [IMPROVED] type hints for Backend and PandapowerBackend + [1.9.5] - 2023-09-18 --------------------- - [FIXED] issue https://github.com/rte-france/Grid2Op/issues/518 diff --git a/examples/backend_integration/Step1_loading.py b/examples/backend_integration/Step1_loading.py index ec9972ead..a456a2106 100644 --- a/examples/backend_integration/Step1_loading.py +++ b/examples/backend_integration/Step1_loading.py @@ -23,6 +23,8 @@ import os import numpy as np import grid2op +from typing import Optional, Tuple, Union + from grid2op.Backend import Backend # required # to serve as an example @@ -30,7 +32,9 @@ class CustomBackend_Step1(Backend): - def load_grid(self, path, filename=None): + def load_grid(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: # first load the grid from the file full_path = path if filename is not None: @@ -92,25 +96,25 @@ def load_grid(self, path, filename=None): self._compute_pos_big_topo() - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: raise NotImplementedError("Will be detailed in another example script") - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: raise NotImplementedError("Will be detailed in another example script") - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: raise NotImplementedError("Will be detailed in another example script") - def generators_info(self): + def generators_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: raise NotImplementedError("Will be detailed in another example script") - def loads_info(self): + def loads_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: raise NotImplementedError("Will be detailed in another example script") - def lines_or_info(self): + def lines_or_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: raise NotImplementedError("Will be detailed in another example script") - def lines_ex_info(self): + def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: raise NotImplementedError("Will be detailed in another example script") diff --git a/examples/backend_integration/Step2_modify_load.py b/examples/backend_integration/Step2_modify_load.py index 371a1b2cf..c55049458 100644 --- a/examples/backend_integration/Step2_modify_load.py +++ b/examples/backend_integration/Step2_modify_load.py @@ -15,13 +15,15 @@ """ import numpy as np import pandapower as pp +from typing import Optional, Tuple, Union + from Step1_loading import CustomBackend_Step1 class CustomBackend_Step2(CustomBackend_Step1): - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: # the following few lines are highly recommended - if action is None: + if backendAction is None: return ( @@ -29,7 +31,7 @@ def apply_action(self, action): (prod_p, prod_v, load_p, load_q, storage), _, shunts__, - ) = action() + ) = backendAction() # change the active values of the loads for load_id, new_p in load_p: @@ -38,7 +40,7 @@ def apply_action(self, action): for load_id, new_q in load_q: self._grid.load["q_mvar"].iloc[load_id] = new_q - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: # possible implementation of the runpf function try: if is_dc: @@ -50,7 +52,7 @@ def runpf(self, is_dc=False): # of the powerflow has not converged, results are Nan return False, exc_ - def loads_info(self): + def loads_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: # carefull with copy / deep copy load_p = self._grid.res_load["p_mw"].values # in MW load_q = self._grid.res_load["q_mvar"].values # in MVAr diff --git a/examples/backend_integration/Step3_modify_gen.py b/examples/backend_integration/Step3_modify_gen.py index 95a85cc0d..8ec174f34 100644 --- a/examples/backend_integration/Step3_modify_gen.py +++ b/examples/backend_integration/Step3_modify_gen.py @@ -18,24 +18,26 @@ """ import numpy as np +from typing import Optional, Tuple, Union + from Step2_modify_load import CustomBackend_Step2 class CustomBackend_Step3(CustomBackend_Step2): - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: # the following few lines are highly recommended - if action is None: + if backendAction is None: return # loads are modified in the previous script - super().apply_action(action) + super().apply_action(backendAction) ( active_bus, (prod_p, prod_v, load_p, load_q, storage), _, shunts__, - ) = action() + ) = backendAction() # change the active value of generators for gen_id, new_p in prod_p: @@ -49,7 +51,7 @@ def apply_action(self, action): self.gen_to_subid[gen_id] ] # now it is :-) - def generators_info(self): + def generators_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: prod_p = self._grid.res_gen["p_mw"].values # in MW prod_q = self._grid.res_gen["q_mvar"].values # in MVAr diff --git a/examples/backend_integration/Step4_modify_line_status.py b/examples/backend_integration/Step4_modify_line_status.py index 33df0c0af..1f3cac741 100644 --- a/examples/backend_integration/Step4_modify_line_status.py +++ b/examples/backend_integration/Step4_modify_line_status.py @@ -18,18 +18,19 @@ """ import numpy as np +from typing import Optional, Tuple, Union from Step3_modify_gen import CustomBackend_Step3 class CustomBackend_Step4(CustomBackend_Step3): - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: # the following few lines are highly recommended - if action is None: + if backendAction is None: return # loads and generators are modified in the previous script - super().apply_action(action) + super().apply_action(backendAction) # disconnected powerlines are indicated because they are # connected to bus "-1" in the `get_lines_or_bus()` and @@ -46,7 +47,7 @@ def apply_action(self, action): n_line_pp = self._grid.line.shape[0] # handle the disconnection on "or" side - lines_or_bus = action.get_lines_or_bus() + lines_or_bus = backendAction.get_lines_or_bus() for line_id, new_bus in lines_or_bus: if line_id < n_line_pp: # a pandapower powerline has bee disconnected in grid2op @@ -64,7 +65,7 @@ def apply_action(self, action): # element was connected dt["in_service"].iloc[line_id_db] = True - lines_ex_bus = action.get_lines_ex_bus() + lines_ex_bus = backendAction.get_lines_ex_bus() for line_id, new_bus in lines_ex_bus: if line_id < n_line_pp: # a pandapower powerline has bee disconnected in grid2op @@ -95,7 +96,7 @@ def _aux_get_line_info(self, colname_powerline, colname_trafo): ) return res - def lines_or_info(self): + def lines_or_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Main method to retrieve the information at the "origin" side of the powerlines and transformers. @@ -134,7 +135,7 @@ def lines_or_info(self): v_or[~status] = 0. return p_or, q_or, v_or, a_or - def lines_ex_info(self): + def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Main method to retrieve the information at the "extremity" side of the powerlines and transformers. diff --git a/examples/backend_integration/Step5_modify_topology.py b/examples/backend_integration/Step5_modify_topology.py index e80efcfdc..c582aae9d 100644 --- a/examples/backend_integration/Step5_modify_topology.py +++ b/examples/backend_integration/Step5_modify_topology.py @@ -22,6 +22,7 @@ import copy import pandas as pd import numpy as np +from typing import Optional, Tuple, Union from Step4_modify_line_status import CustomBackend_Step4 @@ -55,17 +56,17 @@ def _aux_change_bus_or_disconnect(self, new_bus, dt, key, el_id): # grid2op built-in "***_global()" functions that allows to retrieve the global id # (from 0 to n_total_bus-1) directly (instead of manipulating local bus id that # are either 1 or 2) - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: # the following few lines are highly recommended if action is None: return # loads and generators are modified in the previous script - super().apply_action(action) + super().apply_action(backendAction) # handle the load (see the comment above the definition of this # function as to why it's possible to use the get_loads_bus_global) - loads_bus = action.get_loads_bus_global() + loads_bus = backendAction.get_loads_bus_global() for load_id, new_bus in loads_bus: self._aux_change_bus_or_disconnect(new_bus, self._grid.load, @@ -74,7 +75,7 @@ def apply_action(self, action): # handle the generators (see the comment above the definition of this # function as to why it's possible to use the get_loads_bus_global) - gens_bus = action.get_gens_bus_global() + gens_bus = backendAction.get_gens_bus_global() for gen_id, new_bus in gens_bus: self._aux_change_bus_or_disconnect(new_bus, self._grid.gen, @@ -85,7 +86,7 @@ def apply_action(self, action): # function as to why it's possible to use the get_lines_or_bus_global) n_line_pp = self._grid.line.shape[0] - lines_or_bus = action.get_lines_or_bus_global() + lines_or_bus = backendAction.get_lines_or_bus_global() for line_id, new_bus in lines_or_bus: if line_id < n_line_pp: dt = self._grid.line @@ -101,7 +102,7 @@ def apply_action(self, action): key, line_id_pp) - lines_ex_bus = action.get_lines_ex_bus_global() + lines_ex_bus = backendAction.get_lines_ex_bus_global() for line_id, new_bus in lines_ex_bus: if line_id < n_line_pp: dt = self._grid.line @@ -126,7 +127,7 @@ def apply_action(self, action): (prod_p, prod_v, load_p, load_q, storage), _, shunts__, - ) = action() + ) = backendAction() bus_is = self._grid.bus["in_service"] for i, (bus1_status, bus2_status) in enumerate(active_bus): bus_is[i] = bus1_status @@ -149,7 +150,7 @@ def _aux_get_topo_vect(self, res, dt, key, pos_topo_vect, add_id=0): el_id += 1 # it should return, in the correct order, on which bus each element is connected - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: res = np.full(self.dim_topo, fill_value=-2, dtype=int) # read results for load self._aux_get_topo_vect(res, self._grid.load, "bus", self.load_pos_topo_vect) diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index 917f41c2c..5d839cac1 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -15,7 +15,13 @@ from abc import ABC, abstractmethod import numpy as np import pandas as pd - +from typing import Tuple, Optional, Any, Dict, Union +try: + from typing import Self +except ImportError: + # python version is probably bellow 3.11 + from typing_extensions import Self + from grid2op.dtypes import dt_int, dt_float, dt_bool from grid2op.Exceptions import ( EnvError, @@ -114,15 +120,15 @@ class Backend(GridObjects, ABC): Time to compute the powerflow (might be unset, ie stay at 0.0) """ - IS_BK_CONVERTER = False + IS_BK_CONVERTER : bool = False - env_name = "unknown" + env_name : str = "unknown" # action to set me - my_bk_act_class = None - _complete_action_class = None + my_bk_act_class : "Optional[grid2op.Action._backendAction._BackendAction]"= None + _complete_action_class : "Optional[grid2op.Action.CompleteAction]"= None - ERR_INIT_POWERFLOW = "Power cannot be computed on the first time step, please check your data." + ERR_INIT_POWERFLOW : str = "Power cannot be computed on the first time step, please check your data." def __init__(self, detailed_infos_for_cascading_failures: bool=False, can_be_copied: bool=True, @@ -139,46 +145,49 @@ def __init__(self, # the following parameter is used to control the amount of verbosity when computing a cascading failure # if it's set to true, it returns all intermediate _grid states. This can slow down the computation! - self.detailed_infos_for_cascading_failures = ( + self.detailed_infos_for_cascading_failures :bool= ( detailed_infos_for_cascading_failures ) # the power _grid manipulated. One powergrid per backend. - self._grid = None + self._grid : Any = None # thermal limit setting, in ampere, at the same "side" of the powerline than self.get_line_overflow - self.thermal_limit_a = None + self.thermal_limit_a : Optional[np.ndarray] = None # for the shunt (only if supported) - self._sh_vnkv = None # for each shunt gives the nominal value at the bus at which it is connected + self._sh_vnkv : Optional[np.ndarray]= None # for each shunt gives the nominal value at the bus at which it is connected # if this information is not present, then "get_action_to_set" might not behave correctly - self.comp_time = 0.0 - self.can_output_theta = False + self.comp_time : float = 0.0 + self.can_output_theta : bool = False # to prevent the use of the same backend instance in different environment. - self._is_loaded = False + self._is_loaded : bool = False - self._can_be_copied = can_be_copied + self._can_be_copied : bool = can_be_copied - self._my_kwargs = {"detailed_infos_for_cascading_failures": detailed_infos_for_cascading_failures, - "can_be_copied": self._can_be_copied} + self._my_kwargs : Dict[str, Any] = {"detailed_infos_for_cascading_failures": detailed_infos_for_cascading_failures, + "can_be_copied": self._can_be_copied} for k, v in kwargs.items(): self._my_kwargs[k] = v @property - def is_loaded(self): + def is_loaded(self) -> bool: + """Return whether or not this backend has been loaded, that is if `load_grid` has been called or not with this instance.""" return self._is_loaded @is_loaded.setter - def is_loaded(self, value): + def is_loaded(self, value : bool) -> None: if value is True: self._is_loaded = True else: raise BackendError('Impossible to unset the "is_loaded" status.') @abstractmethod - def load_grid(self, path, filename=None): + def load_grid(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -206,7 +215,7 @@ def load_grid(self, path, filename=None): pass @abstractmethod - def apply_action(self, action): + def apply_action(self, Union["grid2op.Action._backendAction._BackendAction", None]) -> None: """ INTERNAL @@ -224,7 +233,7 @@ def apply_action(self, action): The help of :func:`grid2op.BaseAction.BaseAction.__call__` or the code in BaseActiontion.py file give more information about the implementation of this method. - :param action: the action to be implemented on the powergrid. + :param backendAction: the action to be implemented on the powergrid. :type action: :class:`grid2op.Action._BackendAction._BackendAction` :return: ``None`` @@ -232,7 +241,7 @@ def apply_action(self, action): pass @abstractmethod - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: """ INTERNAL @@ -261,7 +270,7 @@ def runpf(self, is_dc=False): pass @abstractmethod - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: """ INTERNAL @@ -297,7 +306,7 @@ def get_topo_vect(self): pass @abstractmethod - def generators_info(self): + def generators_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -326,7 +335,7 @@ def generators_info(self): pass @abstractmethod - def loads_info(self): + def loads_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -355,7 +364,7 @@ def loads_info(self): pass @abstractmethod - def lines_or_info(self): + def lines_or_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -385,7 +394,7 @@ def lines_or_info(self): pass @abstractmethod - def lines_ex_info(self): + def lines_ex_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -414,7 +423,7 @@ def lines_ex_info(self): """ pass - def close(self): + def close(self) -> None: """ INTERNAL @@ -429,7 +438,9 @@ def close(self): """ pass - def reset(self, grid_path, grid_filename=None): + def reset(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -444,7 +455,7 @@ def reset(self, grid_path, grid_filename=None): self.comp_time = 0.0 self.load_grid(grid_path, filename=grid_filename) - def copy(self): + def copy(self) -> Self: """ INTERNAL @@ -489,7 +500,7 @@ def copy(self): res._is_loaded = False # i can reload a copy of an environment return res - def save_file(self, full_path): + def save_file(self, full_path: Union[os.PathLike, str]) -> None: """ INTERNAL @@ -507,7 +518,7 @@ def save_file(self, full_path): """ raise RuntimeError("Class {} does not allow for saving file.".format(self)) - def get_line_status(self): + def get_line_status(self) -> np.ndarray: """ INTERNAL @@ -533,7 +544,7 @@ def get_line_status(self): topo_vect[self.line_ex_pos_topo_vect] >= 0 ) - def get_line_flow(self): + def get_line_flow(self) -> np.ndarray: """ INTERNAL @@ -562,7 +573,7 @@ def get_line_flow(self): p_or, q_or, v_or, a_or = self.lines_or_info() return a_or - def set_thermal_limit(self, limits): + def set_thermal_limit(self, limits : Union[np.ndarray, Dict["str", float]]) -> None: """ INTERNAL @@ -620,7 +631,7 @@ def set_thermal_limit(self, limits): ) self.thermal_limit_a[i] = tmp - def update_thermal_limit_from_vect(self, thermal_limit_a): + def update_thermal_limit_from_vect(self, thermal_limit_a : np.ndarray) -> None: """You can use it if your backend stores the thermal limits of the grid in a vector (see :class:`PandaPowerBackend` for example) @@ -639,7 +650,7 @@ def update_thermal_limit_from_vect(self, thermal_limit_a): thermal_limit_a = np.array(thermal_limit_a).astype(dt_float) self.thermal_limit_a[:] = thermal_limit_a - def update_thermal_limit(self, env): + def update_thermal_limit(self, env : "grid2op.Environment.BaseEnv") -> None: """ INTERNAL @@ -668,7 +679,7 @@ def update_thermal_limit(self, env): """ pass - def get_thermal_limit(self): + def get_thermal_limit(self) -> np.ndarray: """ INTERNAL @@ -692,7 +703,7 @@ def get_thermal_limit(self): """ return self.thermal_limit_a - def get_relative_flow(self): + def get_relative_flow(self) -> np.ndarray: """ INTERNAL @@ -714,7 +725,7 @@ def get_relative_flow(self): res = np.divide(num_, denom_) return res - def get_line_overflow(self): + def get_line_overflow(self) -> np.ndarray: """ INTERNAL @@ -739,7 +750,7 @@ def get_line_overflow(self): flow = self.get_line_flow() return flow > th_lim - def shunt_info(self): + def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -769,7 +780,7 @@ def shunt_info(self): """ return [], [], [], [] - def get_theta(self): + def get_theta(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Notes @@ -794,7 +805,7 @@ def get_theta(self): "Your backend does not support the retrieval of the voltage angle theta." ) - def sub_from_bus_id(self, bus_id): + def sub_from_bus_id(self, bus_id : int) -> int: """ INTERNAL @@ -817,7 +828,7 @@ def sub_from_bus_id(self, bus_id): "This backend doesn't allow to get the substation from the bus id." ) - def _disconnect_line(self, id_): + def _disconnect_line(self, id_ : int) -> None: """ INTERNAL @@ -847,7 +858,7 @@ def _disconnect_line(self, id_): bk_act += action self.apply_action(bk_act) - def _runpf_with_diverging_exception(self, is_dc): + def _runpf_with_diverging_exception(self, is_dc : bool) -> Optional[Exception]: """ INTERNAL @@ -886,7 +897,9 @@ def _runpf_with_diverging_exception(self, is_dc): ) return exc_me - def next_grid_state(self, env, is_dc=False): + def next_grid_state(self, + env: "grid2op.Environment.BaseEnv", + is_dc: Optional[bool]=False): """ INTERNAL @@ -964,7 +977,7 @@ def next_grid_state(self, env, is_dc=False): ts += 1 return disconnected_during_cf, infos, conv_ - def storages_info(self): + def storages_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -989,7 +1002,7 @@ def storages_info(self): "storages_info method is not implemented yet there is batteries on the grid." ) - def storage_deact_for_backward_comaptibility(self): + def storage_deact_for_backward_comaptibility(self) -> None: """ INTERNAL @@ -1006,7 +1019,7 @@ def storage_deact_for_backward_comaptibility(self): """ pass - def check_kirchoff(self): + def check_kirchoff(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -1233,7 +1246,9 @@ def check_kirchoff(self): diff_v_bus[:, :] = v_bus[:, :, 1] - v_bus[:, :, 0] return p_subs, q_subs, p_bus, q_bus, diff_v_bus - def load_redispacthing_data(self, path, name="prods_charac.csv"): + def load_redispacthing_data(self, + path : Union[os.PathLike, str], + name : Optional[str]="prods_charac.csv") -> None: """ INTERNAL @@ -1377,7 +1392,9 @@ def load_redispacthing_data(self, path, name="prods_charac.csv"): self.redispatching_unit_commitment_availble = True - def load_storage_data(self, path, name="storage_units_charac.csv"): + def load_storage_data(self, + path : Union[os.PathLike, str], + name: Optional[str] ="storage_units_charac.csv") -> None: """ INTERNAL @@ -1551,7 +1568,7 @@ def load_storage_data(self, path, name="storage_units_charac.csv"): f' for {sto_nm} and column "discharging_efficiency"', ) - def _aux_check_finite_float(self, nb_, str_=""): + def _aux_check_finite_float(self, nb_ : float, str_ : Optional[str]="") -> None: """ INTERNAL @@ -1567,7 +1584,9 @@ def _aux_check_finite_float(self, nb_, str_=""): ) return tmp - def load_grid_layout(self, path, name="grid_layout.json"): + def load_grid_layout(self, + path : Union[os.PathLike, str], + name: Optional[str] ="grid_layout.json") -> None: """ INTERNAL @@ -1611,14 +1630,13 @@ def load_grid_layout(self, path, name="grid_layout.json"): ) self.attach_layout(grid_layout=new_grid_layout) - return None - def _aux_get_line_status_to_set(self, line_status): + def _aux_get_line_status_to_set(self, line_status) -> np.ndarray: line_status = 2 * line_status - 1 line_status = line_status.astype(dt_int) return line_status - def get_action_to_set(self): + def get_action_to_set(self) -> "grid2op.Action.CompleteAction": """ Get the action to set another backend to represent the internal state of this current backend. @@ -1668,7 +1686,9 @@ def get_action_to_set(self): set_me.update(dict_) return set_me - def update_from_obs(self, obs, force_update=False): + def update_from_obs(self, + obs: "grid2op.Observation.CompleteObservation", + force_update: Optional[bool]=False): """ Takes an observation as input and update the internal state of `self` to match the state of the backend that produced this observation. @@ -1687,6 +1707,9 @@ def update_from_obs(self, obs, force_update=False): ---------- obs: :class:`grid2op.Observation.CompleteObservation` A complete observation describing the state of the grid you want this backend to be in. + force_update : bool + If set to ``True`` the backend will be updated without checking the type of the observation + you used. This is dangerous. Default value is ``False`` (safe). """ # lazy loading to prevent circular references @@ -1734,7 +1757,7 @@ def update_from_obs(self, obs, force_update=False): backend_action += act self.apply_action(backend_action) - def assert_grid_correct(self): + def assert_grid_correct(self) -> None: """ INTERNAL @@ -1774,7 +1797,7 @@ def assert_grid_correct(self): my_cls._complete_action_class._update_value_set() my_cls.assert_grid_correct_cls() - def assert_grid_correct_after_powerflow(self): + def assert_grid_correct_after_powerflow(self) -> None: """ INTERNAL diff --git a/grid2op/Backend/educPandaPowerBackend.py b/grid2op/Backend/educPandaPowerBackend.py index effbaa67c..1b7d181c4 100644 --- a/grid2op/Backend/educPandaPowerBackend.py +++ b/grid2op/Backend/educPandaPowerBackend.py @@ -13,6 +13,7 @@ import numpy as np import pandas as pd +from typing import Optional, Tuple, Union import pandapower as pp import scipy @@ -63,8 +64,8 @@ class EducPandaPowerBackend(Backend): """ def __init__(self, - detailed_infos_for_cascading_failures=False, - can_be_copied=True): + detailed_infos_for_cascading_failures : Optional[bool]=False, + can_be_copied : Optional[bool]=True): """ Nothing much to do here except initializing what you would need (a tensorflow session, link to some external dependencies etc.) @@ -89,13 +90,15 @@ def __init__(self, "This backend is used for demonstration purpose only, you should not use it under any " "circumstances. Please use grid2op.Backend.PandaPowerBackend instead" ) - self._nb_real_line_pandapower = None + self._nb_real_line_pandapower : int = None # NB: this instance of backend is here for academic purpose only. For clarity, it does not handle # neither shunt nor storage unit. ####### load the grid - def load_grid(self, path=None, filename=None): + def load_grid(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ Demonstration on how you can load a powergrid and then initialize the proper grid2op attributes. @@ -222,7 +225,7 @@ def load_grid(self, path=None, filename=None): type(self).set_no_storage() ###### modify the grid - def apply_action(self, backendAction=None): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: """ Here the implementation of the "modify the grid" function. @@ -321,7 +324,7 @@ def apply_action(self, backendAction=None): bus_is[i + self.n_sub] = bus2_status ###### computes powerflow - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: """ Now we just perform a powerflow with pandapower which can be done with either `rundcpp` for dc powerflow or with `runpp` for AC powerflow. @@ -347,7 +350,7 @@ def runpf(self, is_dc=False): return False, exc_ ###### getters - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: """ Retrieve the bus to which the objects are connected based on the information stored on the grid. @@ -407,7 +410,7 @@ def get_topo_vect(self): i += 1 return res - def generators_info(self): + def generators_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ We chose to keep the same order in grid2op and in pandapower. So we just return the correct values. """ @@ -420,7 +423,7 @@ def generators_info(self): ) # in kV return prod_p, prod_q, prod_v - def loads_info(self): + def loads_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ We chose to keep the same order in grid2op and in pandapower. So we just return the correct values. """ @@ -439,7 +442,7 @@ def loads_info(self): ) # in kV return load_p, load_q, load_v - def _aux_get_line_info(self, colname_powerline, colname_trafo): + def _aux_get_line_info(self, colname_powerline, colname_trafo) -> np.ndarray: """ concatenate the information of powerlines and trafo using the convention that "powerlines go first" """ @@ -451,7 +454,7 @@ def _aux_get_line_info(self, colname_powerline, colname_trafo): ) return res - def lines_or_info(self): + def lines_or_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Main method to retrieve the information at the "origin" side of the powerlines and transformers. @@ -470,7 +473,7 @@ def lines_or_info(self): a_or = self._aux_get_line_info("i_from_ka", "i_hv_ka") * 1000 return p_or, q_or, v_or, a_or - def lines_ex_info(self): + def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Main method to retrieve the information at the "extremity" side of the powerlines and transformers. @@ -490,7 +493,7 @@ def lines_ex_info(self): return p_ex, q_ex, v_ex, a_ex # other less important method that you will need to implement - def get_line_status(self): + def get_line_status(self) -> np.ndarray: """ you might consider implementing it @@ -504,7 +507,7 @@ def get_line_status(self): ) ).astype(dt_bool) - def _disconnect_line(self, id_): + def _disconnect_line(self, id_ : int) -> None: """ you might consider implementing it @@ -518,7 +521,7 @@ def _disconnect_line(self, id_): id_ - self._nb_real_line_pandapower ] = False - def copy(self): + def copy(self) -> "EducPandaPowerBackend": """ you might consider implementing it @@ -533,7 +536,9 @@ def copy(self): res = copy.deepcopy(self) return res - def reset(self, path=None, grid_filename=None): + def reset(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ you might consider implementing it @@ -570,7 +575,7 @@ def reset(self, path=None, grid_filename=None): self._grid.bus["in_service"].iloc[: self.n_sub] = True self._grid.bus["in_service"].iloc[self.n_sub :] = False - def close(self): + def close(self) -> None: """ you might consider implementing it diff --git a/grid2op/Backend/pandaPowerBackend.py b/grid2op/Backend/pandaPowerBackend.py index 116d09080..bade25d7f 100644 --- a/grid2op/Backend/pandaPowerBackend.py +++ b/grid2op/Backend/pandaPowerBackend.py @@ -13,6 +13,7 @@ import numpy as np import pandas as pd +from typing import Optional, Union, Tuple import pandapower as pp import scipy @@ -109,12 +110,12 @@ class PandaPowerBackend(Backend): def __init__( self, - detailed_infos_for_cascading_failures=False, - lightsim2grid=False, # use lightsim2grid as pandapower powerflow solver - dist_slack=False, - max_iter=10, - can_be_copied=True, - with_numba=NUMBA_, + detailed_infos_for_cascading_failures : bool=False, + lightsim2grid : bool=False, # use lightsim2grid as pandapower powerflow solver + dist_slack : bool=False, + max_iter : int=10, + can_be_copied: bool=True, + with_numba: bool=NUMBA_, ): Backend.__init__( self, @@ -125,44 +126,44 @@ def __init__( max_iter=max_iter, with_numba=with_numba ) - self.with_numba = with_numba - self.prod_pu_to_kv = None - self.load_pu_to_kv = None - self.lines_or_pu_to_kv = None - self.lines_ex_pu_to_kv = None - self.storage_pu_to_kv = None - - self.p_or = None - self.q_or = None - self.v_or = None - self.a_or = None - self.p_ex = None - self.q_ex = None - self.v_ex = None - self.a_ex = None - - self.load_p = None - self.load_q = None - self.load_v = None - - self.storage_p = None - self.storage_q = None - self.storage_v = None - - self.prod_p = None - self.prod_q = None - self.prod_v = None - self.line_status = None - - self._pf_init = "flat" - self._pf_init = "results" - self._nb_bus_before = None # number of active bus at the preceeding step - - self.thermal_limit_a = None - - self._iref_slack = None - self._id_bus_added = None - self._fact_mult_gen = -1 + self.with_numba : bool = with_numba + self.prod_pu_to_kv : Optional[np.ndarray] = None + self.load_pu_to_kv : Optional[np.ndarray] = None + self.lines_or_pu_to_kv : Optional[np.ndarray] = None + self.lines_ex_pu_to_kv : Optional[np.ndarray] = None + self.storage_pu_to_kv : Optional[np.ndarray] = None + + self.p_or : Optional[np.ndarray] = None + self.q_or : Optional[np.ndarray] = None + self.v_or : Optional[np.ndarray] = None + self.a_or : Optional[np.ndarray] = None + self.p_ex : Optional[np.ndarray] = None + self.q_ex : Optional[np.ndarray] = None + self.v_ex : Optional[np.ndarray] = None + self.a_ex : Optional[np.ndarray] = None + + self.load_p : Optional[np.ndarray] = None + self.load_q : Optional[np.ndarray] = None + self.load_v : Optional[np.ndarray] = None + + self.storage_p : Optional[np.ndarray] = None + self.storage_q : Optional[np.ndarray] = None + self.storage_v : Optional[np.ndarray] = None + + self.prod_p : Optional[np.ndarray] = None + self.prod_q : Optional[np.ndarray] = None + self.prod_v : Optional[np.ndarray] = None + self.line_status : Optional[np.ndarray] = None + + self._pf_init : str = "flat" + self._pf_init : str = "results" + self._nb_bus_before : Optional[int] = None # number of active bus at the preceeding step + + self.thermal_limit_a : Optional[np.ndarray] = None + + self._iref_slack : Optional[int] = None + self._id_bus_added : Optional[int] = None + self._fact_mult_gen : int = -1 self._what_object_where = None self._number_true_line = -1 self._corresp_name_fun = {} @@ -203,15 +204,15 @@ def __init__( # TODO storage doc (in grid2op rst) of the backend self.can_output_theta = True # I support the voltage angle - self.theta_or = None - self.theta_ex = None - self.load_theta = None - self.gen_theta = None - self.storage_theta = None + self.theta_or : Optional[np.ndarray] = None + self.theta_ex : Optional[np.ndarray] = None + self.load_theta : Optional[np.ndarray] = None + self.gen_theta : Optional[np.ndarray] = None + self.storage_theta : Optional[np.ndarray] = None - self._lightsim2grid = lightsim2grid - self._dist_slack = dist_slack - self._max_iter = max_iter + self._lightsim2grid : bool = lightsim2grid + self._dist_slack : bool = dist_slack + self._max_iter : bool = max_iter def _check_for_non_modeled_elements(self): """This function check for elements in the pandapower grid that will have no impact on grid2op. @@ -238,7 +239,7 @@ def _check_for_non_modeled_elements(self): f"work, but you won't be able to modify them)." ) - def get_theta(self): + def get_theta(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ TODO doc @@ -263,7 +264,7 @@ def get_theta(self): self.cst_1 * self.storage_theta, ) - def get_nb_active_bus(self): + def get_nb_active_bus(self) -> int: """ INTERNAL @@ -279,22 +280,24 @@ def get_nb_active_bus(self): return self._grid.bus["in_service"].sum() @staticmethod - def _load_grid_load_p_mw(grid): + def _load_grid_load_p_mw(grid) -> pd.Series: return grid.load["p_mw"] @staticmethod - def _load_grid_load_q_mvar(grid): + def _load_grid_load_q_mvar(grid) -> pd.Series: return grid.load["q_mvar"] @staticmethod - def _load_grid_gen_p_mw(grid): + def _load_grid_gen_p_mw(grid) -> pd.Series: return grid.gen["p_mw"] @staticmethod - def _load_grid_gen_vm_pu(grid): + def _load_grid_gen_vm_pu(grid) -> pd.Series: return grid.gen["vm_pu"] - def reset(self, path=None, grid_filename=None): + def reset(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -312,7 +315,9 @@ def reset(self, path=None, grid_filename=None): self._topo_vect[:] = self._get_topo_vect() self.comp_time = 0.0 - def load_grid(self, path=None, filename=None): + def load_grid(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -558,7 +563,7 @@ def load_grid(self, path=None, filename=None): self._init_private_attrs() - def _init_private_attrs(self): + def _init_private_attrs(self) -> None: # number of elements per substation self.sub_info = np.zeros(self.n_sub, dtype=dt_int) @@ -780,7 +785,7 @@ def _init_private_attrs(self): self._grid ) # will be initialized in the "assert_grid_correct" - def storage_deact_for_backward_comaptibility(self): + def storage_deact_for_backward_comaptibility(self) -> None: self._init_private_attrs() def _convert_id_topo(self, id_big_topo): @@ -797,7 +802,7 @@ def _convert_id_topo(self, id_big_topo): """ return self._big_topo_to_obj[id_big_topo] - def apply_action(self, backendAction=None): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: """ INTERNAL @@ -807,6 +812,7 @@ def apply_action(self, backendAction=None): """ if backendAction is None: return + cls = type(self) ( @@ -983,7 +989,7 @@ def _aux_get_line_info(self, colname1, colname2): ) return res - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: """ INTERNAL @@ -1149,7 +1155,7 @@ def runpf(self, is_dc=False): msg = exc_.__str__() return False, DivergingPowerFlow(f'powerflow diverged with error :"{msg}"') - def assert_grid_correct(self): + def assert_grid_correct(self) -> None: """ INTERNAL @@ -1159,7 +1165,7 @@ def assert_grid_correct(self): """ super().assert_grid_correct() - def _reset_all_nan(self): + def _reset_all_nan(self) -> None: self.p_or[:] = np.NaN self.q_or[:] = np.NaN self.v_or[:] = np.NaN @@ -1185,14 +1191,13 @@ def _reset_all_nan(self): self.gen_theta[:] = np.NaN self.storage_theta[:] = np.NaN - def copy(self): + def copy(self) -> "PandaPowerBackend": """ INTERNAL .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\ - Performs a deep copy of the power :attr:`_grid`. - As pandapower is pure python, the deep copy operator is perfectly suited for the task. + This should return a deep copy of the Backend itself and not just the `self._grid` """ # res = copy.deepcopy(self) # this was really slow... res = type(self)(**self._my_kwargs) @@ -1287,7 +1292,7 @@ def copy(self): return res - def close(self): + def close(self) -> None: """ INTERNAL @@ -1301,7 +1306,7 @@ def close(self): del self.__pp_backend_initial_grid self.__pp_backend_initial_grid = None - def save_file(self, full_path): + def save_file(self, full_path: Union[os.PathLike, str]) -> None: """ INTERNAL @@ -1315,7 +1320,7 @@ def save_file(self, full_path): """ pp.to_json(self._grid, full_path) - def get_line_status(self): + def get_line_status(self) -> np.ndarray: """ INTERNAL @@ -1334,7 +1339,7 @@ def _get_line_status(self): ) ).astype(dt_bool) - def get_line_flow(self): + def get_line_flow(self) -> np.ndarray: return self.a_or def _disconnect_line(self, id_): @@ -1353,7 +1358,7 @@ def _reconnect_line(self, id_): self._grid.trafo["in_service"].iloc[id_ - self._number_true_line] = True self.line_status[id_] = True - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: return self._topo_vect def _get_topo_vect(self): @@ -1461,21 +1466,21 @@ def _loads_info(self): ].values.astype(dt_float) return load_p, load_q, load_v, load_theta - def generators_info(self): + def generators_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.prod_p, self.cst_1 * self.prod_q, self.cst_1 * self.prod_v, ) - def loads_info(self): + def loads_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.load_p, self.cst_1 * self.load_q, self.cst_1 * self.load_v, ) - def lines_or_info(self): + def lines_or_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.p_or, self.cst_1 * self.q_or, @@ -1483,7 +1488,7 @@ def lines_or_info(self): self.cst_1 * self.a_or, ) - def lines_ex_info(self): + def lines_ex_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.p_ex, self.cst_1 * self.q_ex, @@ -1491,7 +1496,7 @@ def lines_ex_info(self): self.cst_1 * self.a_ex, ) - def shunt_info(self): + def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: shunt_p = self.cst_1 * self._grid.res_shunt["p_mw"].values.astype(dt_float) shunt_q = self.cst_1 * self._grid.res_shunt["q_mvar"].values.astype(dt_float) shunt_v = ( @@ -1513,7 +1518,7 @@ def shunt_info(self): shunt_bus[alone] = -1 return shunt_p, shunt_q, shunt_v, shunt_bus - def storages_info(self): + def storages_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.storage_p, self.cst_1 * self.storage_q, @@ -1545,7 +1550,7 @@ def _storages_info(self): theta_storage = np.zeros(shape=0, dtype=dt_float) return p_storage, q_storage, v_storage, theta_storage - def sub_from_bus_id(self, bus_id): + def sub_from_bus_id(self, bus_id : int) -> int: if bus_id >= self._number_true_line: return bus_id - self._number_true_line return bus_id diff --git a/grid2op/Environment/environment.py b/grid2op/Environment/environment.py index 08763efba..f8c72684b 100644 --- a/grid2op/Environment/environment.py +++ b/grid2op/Environment/environment.py @@ -195,7 +195,6 @@ def _init_backend( Create a proper and valid environment. """ - if isinstance(rewardClass, type): if not issubclass(rewardClass, BaseReward): raise Grid2OpException( @@ -234,7 +233,6 @@ def _init_backend( if self._read_from_local_dir: # test to support pickle conveniently self.backend._PATH_ENV = self.get_path_env() - # all the above should be done in this exact order, otherwise some weird behaviour might occur # this is due to the class attribute self.backend.set_env_name(self.name) diff --git a/setup.py b/setup.py index 857e3e685..16a171cf8 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,8 @@ def my_test_suite(): "tqdm>=4.45.0", "networkx>=2.4", "requests>=2.23.0", - "packaging" # because gym changes the way it uses numpy prng in version 0.26 and i need both gym before and after... + "packaging", # because gym changes the way it uses numpy prng in version 0.26 and i need both gym before and after... + "typing_extensions" ], "extras": { "optional": [ From ad2b9e81c2636276b1122d6c78a2bc37ed8d8fcb Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 20 Sep 2023 15:11:22 +0200 Subject: [PATCH 03/46] adding type hints for the backend classes --- grid2op/Backend/backend.py | 43 +++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index 5d839cac1..0221bbd6e 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -215,7 +215,7 @@ def load_grid(self, pass @abstractmethod - def apply_action(self, Union["grid2op.Action._backendAction._BackendAction", None]) -> None: + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: """ INTERNAL @@ -279,12 +279,16 @@ def get_topo_vect(self) -> np.ndarray: Prefer using :attr:`grid2op.Observation.BaseObservation.topo_vect` Get the topology vector from the :attr:`Backend._grid`. + + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + The topology vector defines, for each object, on which bus it is connected. It returns -1 if the object is not connected. - It is a vector with as much elements (productions, loads and lines extremity) as there are in the powergrid. + It is a vector with as much elements (productions, loads and lines extremity, storage) as there are in the powergrid. - For each elements, it gives on which bus it is connected in its substation. + For each elements, it gives on which bus it is connected in its substation (after the solver has ran) For example, if the first element of this vector is the load of id 1, then if `res[0] = 2` it means that the load of id 1 is connected to the second bus of its substation. @@ -316,6 +320,9 @@ def generators_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: :attr:`grid2op.Observation.BaseObservation.gen_q` and :attr:`grid2op.Observation.BaseObservation.gen_v` instead. + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + This method is used to retrieve information about the generators (active, reactive production and voltage magnitude of the bus to which it is connected). @@ -345,6 +352,9 @@ def loads_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: :attr:`grid2op.Observation.BaseObservation.load_q` and :attr:`grid2op.Observation.BaseObservation.load_v` instead. + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + This method is used to retrieve information about the loads (active, reactive consumption and voltage magnitude of the bus to which it is connected). @@ -375,6 +385,9 @@ def lines_or_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray] :attr:`grid2op.Observation.BaseObservation.a_or` and, :attr:`grid2op.Observation.BaseObservation.v_or` instead + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + It returns the information extracted from the _grid at the origin end of each powerline. For assumption about the order of the powerline flows return in this vector, see the help of the @@ -405,6 +418,9 @@ def lines_ex_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray] :attr:`grid2op.Observation.BaseObservation.a_ex` and, :attr:`grid2op.Observation.BaseObservation.v_ex` instead + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + It returns the information extracted from the _grid at the extremity end of each powerline. For assumption about the order of the powerline flows return in this vector, see the help of the @@ -453,7 +469,7 @@ def reset(self, But it is encouraged to overload it in the subclasses. """ self.comp_time = 0.0 - self.load_grid(grid_path, filename=grid_filename) + self.load_grid(path, filename=filename) def copy(self) -> Self: """ @@ -526,6 +542,9 @@ def get_line_status(self) -> np.ndarray: Prefer using :attr:`grid2op.Observation.BaseObservation.line_status` instead + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + Return the status of each lines (connected : True / disconnected: False ) It is assume that the order of the powerline is fixed: if the status of powerline "l1" is put at the 42nd element @@ -555,6 +574,9 @@ def get_line_flow(self) -> np.ndarray: Return the current flow in each lines of the powergrid. Only one value per powerline is returned. + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + If the AC mod is used, this shall return the current flow on the end of the powerline where there is a protection. For example, if there is a protection on "origin end" of powerline "l2" then this method shall return the current flow of at the "origin end" of powerline l2. @@ -711,6 +733,9 @@ def get_relative_flow(self) -> np.ndarray: Prefer using :attr:`grid2op.Observation.BaseObservation.rho` + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + This method return the relative flows, *eg.* the current flow divided by the thermal limits. It has a pretty straightforward default implementation, but it can be overriden for example for transformer if the limits are on the lower voltage side or on the upper voltage level. @@ -736,6 +761,9 @@ def get_line_overflow(self) -> np.ndarray: :attr:`grid2op.Observation.BaseObservation.timestep_overflow` and check the non zero index. + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + Prefer using the attribute of the :class:`grid2op.Observation.BaseObservation` faster accessor to the line that are on overflow. @@ -756,6 +784,9 @@ def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\ + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + This method is optional. If implemented, it should return the proper information about the shunt in the powergrid. @@ -782,7 +813,9 @@ def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: def get_theta(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ - + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + Notes ----- Don't forget to set the flag :attr:`Backend.can_output_theta` to ``True`` in the From 0e3c88951b6983c04f4f0b5700d8d46b7e8b891c Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 20 Sep 2023 16:28:43 +0200 Subject: [PATCH 04/46] adding type hints for the backend classes --- grid2op/Backend/backend.py | 4 ++-- grid2op/Backend/educPandaPowerBackend.py | 2 +- grid2op/Backend/pandaPowerBackend.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index 0221bbd6e..05350aa13 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -456,7 +456,7 @@ def close(self) -> None: def reset(self, path : Union[os.PathLike, str], - filename : Optional[Union[os.PathLike, str]]=None) -> None: + grid_filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -469,7 +469,7 @@ def reset(self, But it is encouraged to overload it in the subclasses. """ self.comp_time = 0.0 - self.load_grid(path, filename=filename) + self.load_grid(path, filename=grid_filename) def copy(self) -> Self: """ diff --git a/grid2op/Backend/educPandaPowerBackend.py b/grid2op/Backend/educPandaPowerBackend.py index 1b7d181c4..f9f21a867 100644 --- a/grid2op/Backend/educPandaPowerBackend.py +++ b/grid2op/Backend/educPandaPowerBackend.py @@ -538,7 +538,7 @@ def copy(self) -> "EducPandaPowerBackend": def reset(self, path : Union[os.PathLike, str], - filename : Optional[Union[os.PathLike, str]]=None) -> None: + grid_filename : Optional[Union[os.PathLike, str]]=None) -> None: """ you might consider implementing it diff --git a/grid2op/Backend/pandaPowerBackend.py b/grid2op/Backend/pandaPowerBackend.py index bade25d7f..c66153e9b 100644 --- a/grid2op/Backend/pandaPowerBackend.py +++ b/grid2op/Backend/pandaPowerBackend.py @@ -297,7 +297,7 @@ def _load_grid_gen_vm_pu(grid) -> pd.Series: def reset(self, path : Union[os.PathLike, str], - filename : Optional[Union[os.PathLike, str]]=None) -> None: + grid_filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL From f448a099dabb02a52997dbf986302e3c07acd22c Mon Sep 17 00:00:00 2001 From: Benjamin DONNOT Date: Thu, 21 Sep 2023 10:08:32 +0200 Subject: [PATCH 05/46] Adding type hints for backend classes and examples (#532) --- CHANGELOG.rst | 5 + examples/backend_integration/Step1_loading.py | 20 +- .../backend_integration/Step2_modify_load.py | 12 +- .../backend_integration/Step3_modify_gen.py | 12 +- .../Step4_modify_line_status.py | 15 +- .../Step5_modify_topology.py | 17 +- grid2op/Backend/backend.py | 180 ++++++++++++------ grid2op/Backend/educPandaPowerBackend.py | 39 ++-- grid2op/Backend/pandaPowerBackend.py | 167 ++++++++-------- grid2op/Environment/environment.py | 2 - setup.py | 3 +- 11 files changed, 276 insertions(+), 196 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a13bb88dc..bdc84f111 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -31,6 +31,11 @@ Change Log - [???] "asynch" multienv - [???] properly model interconnecting powerlines +[1.9.6] - 2023-xx-yy +---------------------- +- [ADDED] now depends on the `typing_extensions` package +- [IMPROVED] type hints for Backend and PandapowerBackend + [1.9.5] - 2023-09-18 --------------------- - [FIXED] issue https://github.com/rte-france/Grid2Op/issues/518 diff --git a/examples/backend_integration/Step1_loading.py b/examples/backend_integration/Step1_loading.py index ec9972ead..a456a2106 100644 --- a/examples/backend_integration/Step1_loading.py +++ b/examples/backend_integration/Step1_loading.py @@ -23,6 +23,8 @@ import os import numpy as np import grid2op +from typing import Optional, Tuple, Union + from grid2op.Backend import Backend # required # to serve as an example @@ -30,7 +32,9 @@ class CustomBackend_Step1(Backend): - def load_grid(self, path, filename=None): + def load_grid(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: # first load the grid from the file full_path = path if filename is not None: @@ -92,25 +96,25 @@ def load_grid(self, path, filename=None): self._compute_pos_big_topo() - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: raise NotImplementedError("Will be detailed in another example script") - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: raise NotImplementedError("Will be detailed in another example script") - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: raise NotImplementedError("Will be detailed in another example script") - def generators_info(self): + def generators_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: raise NotImplementedError("Will be detailed in another example script") - def loads_info(self): + def loads_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: raise NotImplementedError("Will be detailed in another example script") - def lines_or_info(self): + def lines_or_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: raise NotImplementedError("Will be detailed in another example script") - def lines_ex_info(self): + def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: raise NotImplementedError("Will be detailed in another example script") diff --git a/examples/backend_integration/Step2_modify_load.py b/examples/backend_integration/Step2_modify_load.py index 371a1b2cf..c55049458 100644 --- a/examples/backend_integration/Step2_modify_load.py +++ b/examples/backend_integration/Step2_modify_load.py @@ -15,13 +15,15 @@ """ import numpy as np import pandapower as pp +from typing import Optional, Tuple, Union + from Step1_loading import CustomBackend_Step1 class CustomBackend_Step2(CustomBackend_Step1): - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: # the following few lines are highly recommended - if action is None: + if backendAction is None: return ( @@ -29,7 +31,7 @@ def apply_action(self, action): (prod_p, prod_v, load_p, load_q, storage), _, shunts__, - ) = action() + ) = backendAction() # change the active values of the loads for load_id, new_p in load_p: @@ -38,7 +40,7 @@ def apply_action(self, action): for load_id, new_q in load_q: self._grid.load["q_mvar"].iloc[load_id] = new_q - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: # possible implementation of the runpf function try: if is_dc: @@ -50,7 +52,7 @@ def runpf(self, is_dc=False): # of the powerflow has not converged, results are Nan return False, exc_ - def loads_info(self): + def loads_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: # carefull with copy / deep copy load_p = self._grid.res_load["p_mw"].values # in MW load_q = self._grid.res_load["q_mvar"].values # in MVAr diff --git a/examples/backend_integration/Step3_modify_gen.py b/examples/backend_integration/Step3_modify_gen.py index 95a85cc0d..8ec174f34 100644 --- a/examples/backend_integration/Step3_modify_gen.py +++ b/examples/backend_integration/Step3_modify_gen.py @@ -18,24 +18,26 @@ """ import numpy as np +from typing import Optional, Tuple, Union + from Step2_modify_load import CustomBackend_Step2 class CustomBackend_Step3(CustomBackend_Step2): - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: # the following few lines are highly recommended - if action is None: + if backendAction is None: return # loads are modified in the previous script - super().apply_action(action) + super().apply_action(backendAction) ( active_bus, (prod_p, prod_v, load_p, load_q, storage), _, shunts__, - ) = action() + ) = backendAction() # change the active value of generators for gen_id, new_p in prod_p: @@ -49,7 +51,7 @@ def apply_action(self, action): self.gen_to_subid[gen_id] ] # now it is :-) - def generators_info(self): + def generators_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: prod_p = self._grid.res_gen["p_mw"].values # in MW prod_q = self._grid.res_gen["q_mvar"].values # in MVAr diff --git a/examples/backend_integration/Step4_modify_line_status.py b/examples/backend_integration/Step4_modify_line_status.py index 33df0c0af..1f3cac741 100644 --- a/examples/backend_integration/Step4_modify_line_status.py +++ b/examples/backend_integration/Step4_modify_line_status.py @@ -18,18 +18,19 @@ """ import numpy as np +from typing import Optional, Tuple, Union from Step3_modify_gen import CustomBackend_Step3 class CustomBackend_Step4(CustomBackend_Step3): - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: # the following few lines are highly recommended - if action is None: + if backendAction is None: return # loads and generators are modified in the previous script - super().apply_action(action) + super().apply_action(backendAction) # disconnected powerlines are indicated because they are # connected to bus "-1" in the `get_lines_or_bus()` and @@ -46,7 +47,7 @@ def apply_action(self, action): n_line_pp = self._grid.line.shape[0] # handle the disconnection on "or" side - lines_or_bus = action.get_lines_or_bus() + lines_or_bus = backendAction.get_lines_or_bus() for line_id, new_bus in lines_or_bus: if line_id < n_line_pp: # a pandapower powerline has bee disconnected in grid2op @@ -64,7 +65,7 @@ def apply_action(self, action): # element was connected dt["in_service"].iloc[line_id_db] = True - lines_ex_bus = action.get_lines_ex_bus() + lines_ex_bus = backendAction.get_lines_ex_bus() for line_id, new_bus in lines_ex_bus: if line_id < n_line_pp: # a pandapower powerline has bee disconnected in grid2op @@ -95,7 +96,7 @@ def _aux_get_line_info(self, colname_powerline, colname_trafo): ) return res - def lines_or_info(self): + def lines_or_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Main method to retrieve the information at the "origin" side of the powerlines and transformers. @@ -134,7 +135,7 @@ def lines_or_info(self): v_or[~status] = 0. return p_or, q_or, v_or, a_or - def lines_ex_info(self): + def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Main method to retrieve the information at the "extremity" side of the powerlines and transformers. diff --git a/examples/backend_integration/Step5_modify_topology.py b/examples/backend_integration/Step5_modify_topology.py index e80efcfdc..c582aae9d 100644 --- a/examples/backend_integration/Step5_modify_topology.py +++ b/examples/backend_integration/Step5_modify_topology.py @@ -22,6 +22,7 @@ import copy import pandas as pd import numpy as np +from typing import Optional, Tuple, Union from Step4_modify_line_status import CustomBackend_Step4 @@ -55,17 +56,17 @@ def _aux_change_bus_or_disconnect(self, new_bus, dt, key, el_id): # grid2op built-in "***_global()" functions that allows to retrieve the global id # (from 0 to n_total_bus-1) directly (instead of manipulating local bus id that # are either 1 or 2) - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: # the following few lines are highly recommended if action is None: return # loads and generators are modified in the previous script - super().apply_action(action) + super().apply_action(backendAction) # handle the load (see the comment above the definition of this # function as to why it's possible to use the get_loads_bus_global) - loads_bus = action.get_loads_bus_global() + loads_bus = backendAction.get_loads_bus_global() for load_id, new_bus in loads_bus: self._aux_change_bus_or_disconnect(new_bus, self._grid.load, @@ -74,7 +75,7 @@ def apply_action(self, action): # handle the generators (see the comment above the definition of this # function as to why it's possible to use the get_loads_bus_global) - gens_bus = action.get_gens_bus_global() + gens_bus = backendAction.get_gens_bus_global() for gen_id, new_bus in gens_bus: self._aux_change_bus_or_disconnect(new_bus, self._grid.gen, @@ -85,7 +86,7 @@ def apply_action(self, action): # function as to why it's possible to use the get_lines_or_bus_global) n_line_pp = self._grid.line.shape[0] - lines_or_bus = action.get_lines_or_bus_global() + lines_or_bus = backendAction.get_lines_or_bus_global() for line_id, new_bus in lines_or_bus: if line_id < n_line_pp: dt = self._grid.line @@ -101,7 +102,7 @@ def apply_action(self, action): key, line_id_pp) - lines_ex_bus = action.get_lines_ex_bus_global() + lines_ex_bus = backendAction.get_lines_ex_bus_global() for line_id, new_bus in lines_ex_bus: if line_id < n_line_pp: dt = self._grid.line @@ -126,7 +127,7 @@ def apply_action(self, action): (prod_p, prod_v, load_p, load_q, storage), _, shunts__, - ) = action() + ) = backendAction() bus_is = self._grid.bus["in_service"] for i, (bus1_status, bus2_status) in enumerate(active_bus): bus_is[i] = bus1_status @@ -149,7 +150,7 @@ def _aux_get_topo_vect(self, res, dt, key, pos_topo_vect, add_id=0): el_id += 1 # it should return, in the correct order, on which bus each element is connected - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: res = np.full(self.dim_topo, fill_value=-2, dtype=int) # read results for load self._aux_get_topo_vect(res, self._grid.load, "bus", self.load_pos_topo_vect) diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index 917f41c2c..05350aa13 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -15,7 +15,13 @@ from abc import ABC, abstractmethod import numpy as np import pandas as pd - +from typing import Tuple, Optional, Any, Dict, Union +try: + from typing import Self +except ImportError: + # python version is probably bellow 3.11 + from typing_extensions import Self + from grid2op.dtypes import dt_int, dt_float, dt_bool from grid2op.Exceptions import ( EnvError, @@ -114,15 +120,15 @@ class Backend(GridObjects, ABC): Time to compute the powerflow (might be unset, ie stay at 0.0) """ - IS_BK_CONVERTER = False + IS_BK_CONVERTER : bool = False - env_name = "unknown" + env_name : str = "unknown" # action to set me - my_bk_act_class = None - _complete_action_class = None + my_bk_act_class : "Optional[grid2op.Action._backendAction._BackendAction]"= None + _complete_action_class : "Optional[grid2op.Action.CompleteAction]"= None - ERR_INIT_POWERFLOW = "Power cannot be computed on the first time step, please check your data." + ERR_INIT_POWERFLOW : str = "Power cannot be computed on the first time step, please check your data." def __init__(self, detailed_infos_for_cascading_failures: bool=False, can_be_copied: bool=True, @@ -139,46 +145,49 @@ def __init__(self, # the following parameter is used to control the amount of verbosity when computing a cascading failure # if it's set to true, it returns all intermediate _grid states. This can slow down the computation! - self.detailed_infos_for_cascading_failures = ( + self.detailed_infos_for_cascading_failures :bool= ( detailed_infos_for_cascading_failures ) # the power _grid manipulated. One powergrid per backend. - self._grid = None + self._grid : Any = None # thermal limit setting, in ampere, at the same "side" of the powerline than self.get_line_overflow - self.thermal_limit_a = None + self.thermal_limit_a : Optional[np.ndarray] = None # for the shunt (only if supported) - self._sh_vnkv = None # for each shunt gives the nominal value at the bus at which it is connected + self._sh_vnkv : Optional[np.ndarray]= None # for each shunt gives the nominal value at the bus at which it is connected # if this information is not present, then "get_action_to_set" might not behave correctly - self.comp_time = 0.0 - self.can_output_theta = False + self.comp_time : float = 0.0 + self.can_output_theta : bool = False # to prevent the use of the same backend instance in different environment. - self._is_loaded = False + self._is_loaded : bool = False - self._can_be_copied = can_be_copied + self._can_be_copied : bool = can_be_copied - self._my_kwargs = {"detailed_infos_for_cascading_failures": detailed_infos_for_cascading_failures, - "can_be_copied": self._can_be_copied} + self._my_kwargs : Dict[str, Any] = {"detailed_infos_for_cascading_failures": detailed_infos_for_cascading_failures, + "can_be_copied": self._can_be_copied} for k, v in kwargs.items(): self._my_kwargs[k] = v @property - def is_loaded(self): + def is_loaded(self) -> bool: + """Return whether or not this backend has been loaded, that is if `load_grid` has been called or not with this instance.""" return self._is_loaded @is_loaded.setter - def is_loaded(self, value): + def is_loaded(self, value : bool) -> None: if value is True: self._is_loaded = True else: raise BackendError('Impossible to unset the "is_loaded" status.') @abstractmethod - def load_grid(self, path, filename=None): + def load_grid(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -206,7 +215,7 @@ def load_grid(self, path, filename=None): pass @abstractmethod - def apply_action(self, action): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: """ INTERNAL @@ -224,7 +233,7 @@ def apply_action(self, action): The help of :func:`grid2op.BaseAction.BaseAction.__call__` or the code in BaseActiontion.py file give more information about the implementation of this method. - :param action: the action to be implemented on the powergrid. + :param backendAction: the action to be implemented on the powergrid. :type action: :class:`grid2op.Action._BackendAction._BackendAction` :return: ``None`` @@ -232,7 +241,7 @@ def apply_action(self, action): pass @abstractmethod - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: """ INTERNAL @@ -261,7 +270,7 @@ def runpf(self, is_dc=False): pass @abstractmethod - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: """ INTERNAL @@ -270,12 +279,16 @@ def get_topo_vect(self): Prefer using :attr:`grid2op.Observation.BaseObservation.topo_vect` Get the topology vector from the :attr:`Backend._grid`. + + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + The topology vector defines, for each object, on which bus it is connected. It returns -1 if the object is not connected. - It is a vector with as much elements (productions, loads and lines extremity) as there are in the powergrid. + It is a vector with as much elements (productions, loads and lines extremity, storage) as there are in the powergrid. - For each elements, it gives on which bus it is connected in its substation. + For each elements, it gives on which bus it is connected in its substation (after the solver has ran) For example, if the first element of this vector is the load of id 1, then if `res[0] = 2` it means that the load of id 1 is connected to the second bus of its substation. @@ -297,7 +310,7 @@ def get_topo_vect(self): pass @abstractmethod - def generators_info(self): + def generators_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -307,6 +320,9 @@ def generators_info(self): :attr:`grid2op.Observation.BaseObservation.gen_q` and :attr:`grid2op.Observation.BaseObservation.gen_v` instead. + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + This method is used to retrieve information about the generators (active, reactive production and voltage magnitude of the bus to which it is connected). @@ -326,7 +342,7 @@ def generators_info(self): pass @abstractmethod - def loads_info(self): + def loads_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -336,6 +352,9 @@ def loads_info(self): :attr:`grid2op.Observation.BaseObservation.load_q` and :attr:`grid2op.Observation.BaseObservation.load_v` instead. + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + This method is used to retrieve information about the loads (active, reactive consumption and voltage magnitude of the bus to which it is connected). @@ -355,7 +374,7 @@ def loads_info(self): pass @abstractmethod - def lines_or_info(self): + def lines_or_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -366,6 +385,9 @@ def lines_or_info(self): :attr:`grid2op.Observation.BaseObservation.a_or` and, :attr:`grid2op.Observation.BaseObservation.v_or` instead + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + It returns the information extracted from the _grid at the origin end of each powerline. For assumption about the order of the powerline flows return in this vector, see the help of the @@ -385,7 +407,7 @@ def lines_or_info(self): pass @abstractmethod - def lines_ex_info(self): + def lines_ex_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -396,6 +418,9 @@ def lines_ex_info(self): :attr:`grid2op.Observation.BaseObservation.a_ex` and, :attr:`grid2op.Observation.BaseObservation.v_ex` instead + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + It returns the information extracted from the _grid at the extremity end of each powerline. For assumption about the order of the powerline flows return in this vector, see the help of the @@ -414,7 +439,7 @@ def lines_ex_info(self): """ pass - def close(self): + def close(self) -> None: """ INTERNAL @@ -429,7 +454,9 @@ def close(self): """ pass - def reset(self, grid_path, grid_filename=None): + def reset(self, + path : Union[os.PathLike, str], + grid_filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -442,9 +469,9 @@ def reset(self, grid_path, grid_filename=None): But it is encouraged to overload it in the subclasses. """ self.comp_time = 0.0 - self.load_grid(grid_path, filename=grid_filename) + self.load_grid(path, filename=grid_filename) - def copy(self): + def copy(self) -> Self: """ INTERNAL @@ -489,7 +516,7 @@ def copy(self): res._is_loaded = False # i can reload a copy of an environment return res - def save_file(self, full_path): + def save_file(self, full_path: Union[os.PathLike, str]) -> None: """ INTERNAL @@ -507,7 +534,7 @@ def save_file(self, full_path): """ raise RuntimeError("Class {} does not allow for saving file.".format(self)) - def get_line_status(self): + def get_line_status(self) -> np.ndarray: """ INTERNAL @@ -515,6 +542,9 @@ def get_line_status(self): Prefer using :attr:`grid2op.Observation.BaseObservation.line_status` instead + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + Return the status of each lines (connected : True / disconnected: False ) It is assume that the order of the powerline is fixed: if the status of powerline "l1" is put at the 42nd element @@ -533,7 +563,7 @@ def get_line_status(self): topo_vect[self.line_ex_pos_topo_vect] >= 0 ) - def get_line_flow(self): + def get_line_flow(self) -> np.ndarray: """ INTERNAL @@ -544,6 +574,9 @@ def get_line_flow(self): Return the current flow in each lines of the powergrid. Only one value per powerline is returned. + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + If the AC mod is used, this shall return the current flow on the end of the powerline where there is a protection. For example, if there is a protection on "origin end" of powerline "l2" then this method shall return the current flow of at the "origin end" of powerline l2. @@ -562,7 +595,7 @@ def get_line_flow(self): p_or, q_or, v_or, a_or = self.lines_or_info() return a_or - def set_thermal_limit(self, limits): + def set_thermal_limit(self, limits : Union[np.ndarray, Dict["str", float]]) -> None: """ INTERNAL @@ -620,7 +653,7 @@ def set_thermal_limit(self, limits): ) self.thermal_limit_a[i] = tmp - def update_thermal_limit_from_vect(self, thermal_limit_a): + def update_thermal_limit_from_vect(self, thermal_limit_a : np.ndarray) -> None: """You can use it if your backend stores the thermal limits of the grid in a vector (see :class:`PandaPowerBackend` for example) @@ -639,7 +672,7 @@ def update_thermal_limit_from_vect(self, thermal_limit_a): thermal_limit_a = np.array(thermal_limit_a).astype(dt_float) self.thermal_limit_a[:] = thermal_limit_a - def update_thermal_limit(self, env): + def update_thermal_limit(self, env : "grid2op.Environment.BaseEnv") -> None: """ INTERNAL @@ -668,7 +701,7 @@ def update_thermal_limit(self, env): """ pass - def get_thermal_limit(self): + def get_thermal_limit(self) -> np.ndarray: """ INTERNAL @@ -692,7 +725,7 @@ def get_thermal_limit(self): """ return self.thermal_limit_a - def get_relative_flow(self): + def get_relative_flow(self) -> np.ndarray: """ INTERNAL @@ -700,6 +733,9 @@ def get_relative_flow(self): Prefer using :attr:`grid2op.Observation.BaseObservation.rho` + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + This method return the relative flows, *eg.* the current flow divided by the thermal limits. It has a pretty straightforward default implementation, but it can be overriden for example for transformer if the limits are on the lower voltage side or on the upper voltage level. @@ -714,7 +750,7 @@ def get_relative_flow(self): res = np.divide(num_, denom_) return res - def get_line_overflow(self): + def get_line_overflow(self) -> np.ndarray: """ INTERNAL @@ -725,6 +761,9 @@ def get_line_overflow(self): :attr:`grid2op.Observation.BaseObservation.timestep_overflow` and check the non zero index. + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + Prefer using the attribute of the :class:`grid2op.Observation.BaseObservation` faster accessor to the line that are on overflow. @@ -739,12 +778,15 @@ def get_line_overflow(self): flow = self.get_line_flow() return flow > th_lim - def shunt_info(self): + def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\ + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + This method is optional. If implemented, it should return the proper information about the shunt in the powergrid. @@ -769,9 +811,11 @@ def shunt_info(self): """ return [], [], [], [] - def get_theta(self): + def get_theta(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ - + .. note:: + It is called after the solver has been ran, only in case of success (convergence). + Notes ----- Don't forget to set the flag :attr:`Backend.can_output_theta` to ``True`` in the @@ -794,7 +838,7 @@ def get_theta(self): "Your backend does not support the retrieval of the voltage angle theta." ) - def sub_from_bus_id(self, bus_id): + def sub_from_bus_id(self, bus_id : int) -> int: """ INTERNAL @@ -817,7 +861,7 @@ def sub_from_bus_id(self, bus_id): "This backend doesn't allow to get the substation from the bus id." ) - def _disconnect_line(self, id_): + def _disconnect_line(self, id_ : int) -> None: """ INTERNAL @@ -847,7 +891,7 @@ def _disconnect_line(self, id_): bk_act += action self.apply_action(bk_act) - def _runpf_with_diverging_exception(self, is_dc): + def _runpf_with_diverging_exception(self, is_dc : bool) -> Optional[Exception]: """ INTERNAL @@ -886,7 +930,9 @@ def _runpf_with_diverging_exception(self, is_dc): ) return exc_me - def next_grid_state(self, env, is_dc=False): + def next_grid_state(self, + env: "grid2op.Environment.BaseEnv", + is_dc: Optional[bool]=False): """ INTERNAL @@ -964,7 +1010,7 @@ def next_grid_state(self, env, is_dc=False): ts += 1 return disconnected_during_cf, infos, conv_ - def storages_info(self): + def storages_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -989,7 +1035,7 @@ def storages_info(self): "storages_info method is not implemented yet there is batteries on the grid." ) - def storage_deact_for_backward_comaptibility(self): + def storage_deact_for_backward_comaptibility(self) -> None: """ INTERNAL @@ -1006,7 +1052,7 @@ def storage_deact_for_backward_comaptibility(self): """ pass - def check_kirchoff(self): + def check_kirchoff(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ INTERNAL @@ -1233,7 +1279,9 @@ def check_kirchoff(self): diff_v_bus[:, :] = v_bus[:, :, 1] - v_bus[:, :, 0] return p_subs, q_subs, p_bus, q_bus, diff_v_bus - def load_redispacthing_data(self, path, name="prods_charac.csv"): + def load_redispacthing_data(self, + path : Union[os.PathLike, str], + name : Optional[str]="prods_charac.csv") -> None: """ INTERNAL @@ -1377,7 +1425,9 @@ def load_redispacthing_data(self, path, name="prods_charac.csv"): self.redispatching_unit_commitment_availble = True - def load_storage_data(self, path, name="storage_units_charac.csv"): + def load_storage_data(self, + path : Union[os.PathLike, str], + name: Optional[str] ="storage_units_charac.csv") -> None: """ INTERNAL @@ -1551,7 +1601,7 @@ def load_storage_data(self, path, name="storage_units_charac.csv"): f' for {sto_nm} and column "discharging_efficiency"', ) - def _aux_check_finite_float(self, nb_, str_=""): + def _aux_check_finite_float(self, nb_ : float, str_ : Optional[str]="") -> None: """ INTERNAL @@ -1567,7 +1617,9 @@ def _aux_check_finite_float(self, nb_, str_=""): ) return tmp - def load_grid_layout(self, path, name="grid_layout.json"): + def load_grid_layout(self, + path : Union[os.PathLike, str], + name: Optional[str] ="grid_layout.json") -> None: """ INTERNAL @@ -1611,14 +1663,13 @@ def load_grid_layout(self, path, name="grid_layout.json"): ) self.attach_layout(grid_layout=new_grid_layout) - return None - def _aux_get_line_status_to_set(self, line_status): + def _aux_get_line_status_to_set(self, line_status) -> np.ndarray: line_status = 2 * line_status - 1 line_status = line_status.astype(dt_int) return line_status - def get_action_to_set(self): + def get_action_to_set(self) -> "grid2op.Action.CompleteAction": """ Get the action to set another backend to represent the internal state of this current backend. @@ -1668,7 +1719,9 @@ def get_action_to_set(self): set_me.update(dict_) return set_me - def update_from_obs(self, obs, force_update=False): + def update_from_obs(self, + obs: "grid2op.Observation.CompleteObservation", + force_update: Optional[bool]=False): """ Takes an observation as input and update the internal state of `self` to match the state of the backend that produced this observation. @@ -1687,6 +1740,9 @@ def update_from_obs(self, obs, force_update=False): ---------- obs: :class:`grid2op.Observation.CompleteObservation` A complete observation describing the state of the grid you want this backend to be in. + force_update : bool + If set to ``True`` the backend will be updated without checking the type of the observation + you used. This is dangerous. Default value is ``False`` (safe). """ # lazy loading to prevent circular references @@ -1734,7 +1790,7 @@ def update_from_obs(self, obs, force_update=False): backend_action += act self.apply_action(backend_action) - def assert_grid_correct(self): + def assert_grid_correct(self) -> None: """ INTERNAL @@ -1774,7 +1830,7 @@ def assert_grid_correct(self): my_cls._complete_action_class._update_value_set() my_cls.assert_grid_correct_cls() - def assert_grid_correct_after_powerflow(self): + def assert_grid_correct_after_powerflow(self) -> None: """ INTERNAL diff --git a/grid2op/Backend/educPandaPowerBackend.py b/grid2op/Backend/educPandaPowerBackend.py index effbaa67c..f9f21a867 100644 --- a/grid2op/Backend/educPandaPowerBackend.py +++ b/grid2op/Backend/educPandaPowerBackend.py @@ -13,6 +13,7 @@ import numpy as np import pandas as pd +from typing import Optional, Tuple, Union import pandapower as pp import scipy @@ -63,8 +64,8 @@ class EducPandaPowerBackend(Backend): """ def __init__(self, - detailed_infos_for_cascading_failures=False, - can_be_copied=True): + detailed_infos_for_cascading_failures : Optional[bool]=False, + can_be_copied : Optional[bool]=True): """ Nothing much to do here except initializing what you would need (a tensorflow session, link to some external dependencies etc.) @@ -89,13 +90,15 @@ def __init__(self, "This backend is used for demonstration purpose only, you should not use it under any " "circumstances. Please use grid2op.Backend.PandaPowerBackend instead" ) - self._nb_real_line_pandapower = None + self._nb_real_line_pandapower : int = None # NB: this instance of backend is here for academic purpose only. For clarity, it does not handle # neither shunt nor storage unit. ####### load the grid - def load_grid(self, path=None, filename=None): + def load_grid(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ Demonstration on how you can load a powergrid and then initialize the proper grid2op attributes. @@ -222,7 +225,7 @@ def load_grid(self, path=None, filename=None): type(self).set_no_storage() ###### modify the grid - def apply_action(self, backendAction=None): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: """ Here the implementation of the "modify the grid" function. @@ -321,7 +324,7 @@ def apply_action(self, backendAction=None): bus_is[i + self.n_sub] = bus2_status ###### computes powerflow - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: """ Now we just perform a powerflow with pandapower which can be done with either `rundcpp` for dc powerflow or with `runpp` for AC powerflow. @@ -347,7 +350,7 @@ def runpf(self, is_dc=False): return False, exc_ ###### getters - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: """ Retrieve the bus to which the objects are connected based on the information stored on the grid. @@ -407,7 +410,7 @@ def get_topo_vect(self): i += 1 return res - def generators_info(self): + def generators_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ We chose to keep the same order in grid2op and in pandapower. So we just return the correct values. """ @@ -420,7 +423,7 @@ def generators_info(self): ) # in kV return prod_p, prod_q, prod_v - def loads_info(self): + def loads_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]: """ We chose to keep the same order in grid2op and in pandapower. So we just return the correct values. """ @@ -439,7 +442,7 @@ def loads_info(self): ) # in kV return load_p, load_q, load_v - def _aux_get_line_info(self, colname_powerline, colname_trafo): + def _aux_get_line_info(self, colname_powerline, colname_trafo) -> np.ndarray: """ concatenate the information of powerlines and trafo using the convention that "powerlines go first" """ @@ -451,7 +454,7 @@ def _aux_get_line_info(self, colname_powerline, colname_trafo): ) return res - def lines_or_info(self): + def lines_or_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Main method to retrieve the information at the "origin" side of the powerlines and transformers. @@ -470,7 +473,7 @@ def lines_or_info(self): a_or = self._aux_get_line_info("i_from_ka", "i_hv_ka") * 1000 return p_or, q_or, v_or, a_or - def lines_ex_info(self): + def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ Main method to retrieve the information at the "extremity" side of the powerlines and transformers. @@ -490,7 +493,7 @@ def lines_ex_info(self): return p_ex, q_ex, v_ex, a_ex # other less important method that you will need to implement - def get_line_status(self): + def get_line_status(self) -> np.ndarray: """ you might consider implementing it @@ -504,7 +507,7 @@ def get_line_status(self): ) ).astype(dt_bool) - def _disconnect_line(self, id_): + def _disconnect_line(self, id_ : int) -> None: """ you might consider implementing it @@ -518,7 +521,7 @@ def _disconnect_line(self, id_): id_ - self._nb_real_line_pandapower ] = False - def copy(self): + def copy(self) -> "EducPandaPowerBackend": """ you might consider implementing it @@ -533,7 +536,9 @@ def copy(self): res = copy.deepcopy(self) return res - def reset(self, path=None, grid_filename=None): + def reset(self, + path : Union[os.PathLike, str], + grid_filename : Optional[Union[os.PathLike, str]]=None) -> None: """ you might consider implementing it @@ -570,7 +575,7 @@ def reset(self, path=None, grid_filename=None): self._grid.bus["in_service"].iloc[: self.n_sub] = True self._grid.bus["in_service"].iloc[self.n_sub :] = False - def close(self): + def close(self) -> None: """ you might consider implementing it diff --git a/grid2op/Backend/pandaPowerBackend.py b/grid2op/Backend/pandaPowerBackend.py index 116d09080..c66153e9b 100644 --- a/grid2op/Backend/pandaPowerBackend.py +++ b/grid2op/Backend/pandaPowerBackend.py @@ -13,6 +13,7 @@ import numpy as np import pandas as pd +from typing import Optional, Union, Tuple import pandapower as pp import scipy @@ -109,12 +110,12 @@ class PandaPowerBackend(Backend): def __init__( self, - detailed_infos_for_cascading_failures=False, - lightsim2grid=False, # use lightsim2grid as pandapower powerflow solver - dist_slack=False, - max_iter=10, - can_be_copied=True, - with_numba=NUMBA_, + detailed_infos_for_cascading_failures : bool=False, + lightsim2grid : bool=False, # use lightsim2grid as pandapower powerflow solver + dist_slack : bool=False, + max_iter : int=10, + can_be_copied: bool=True, + with_numba: bool=NUMBA_, ): Backend.__init__( self, @@ -125,44 +126,44 @@ def __init__( max_iter=max_iter, with_numba=with_numba ) - self.with_numba = with_numba - self.prod_pu_to_kv = None - self.load_pu_to_kv = None - self.lines_or_pu_to_kv = None - self.lines_ex_pu_to_kv = None - self.storage_pu_to_kv = None - - self.p_or = None - self.q_or = None - self.v_or = None - self.a_or = None - self.p_ex = None - self.q_ex = None - self.v_ex = None - self.a_ex = None - - self.load_p = None - self.load_q = None - self.load_v = None - - self.storage_p = None - self.storage_q = None - self.storage_v = None - - self.prod_p = None - self.prod_q = None - self.prod_v = None - self.line_status = None - - self._pf_init = "flat" - self._pf_init = "results" - self._nb_bus_before = None # number of active bus at the preceeding step - - self.thermal_limit_a = None - - self._iref_slack = None - self._id_bus_added = None - self._fact_mult_gen = -1 + self.with_numba : bool = with_numba + self.prod_pu_to_kv : Optional[np.ndarray] = None + self.load_pu_to_kv : Optional[np.ndarray] = None + self.lines_or_pu_to_kv : Optional[np.ndarray] = None + self.lines_ex_pu_to_kv : Optional[np.ndarray] = None + self.storage_pu_to_kv : Optional[np.ndarray] = None + + self.p_or : Optional[np.ndarray] = None + self.q_or : Optional[np.ndarray] = None + self.v_or : Optional[np.ndarray] = None + self.a_or : Optional[np.ndarray] = None + self.p_ex : Optional[np.ndarray] = None + self.q_ex : Optional[np.ndarray] = None + self.v_ex : Optional[np.ndarray] = None + self.a_ex : Optional[np.ndarray] = None + + self.load_p : Optional[np.ndarray] = None + self.load_q : Optional[np.ndarray] = None + self.load_v : Optional[np.ndarray] = None + + self.storage_p : Optional[np.ndarray] = None + self.storage_q : Optional[np.ndarray] = None + self.storage_v : Optional[np.ndarray] = None + + self.prod_p : Optional[np.ndarray] = None + self.prod_q : Optional[np.ndarray] = None + self.prod_v : Optional[np.ndarray] = None + self.line_status : Optional[np.ndarray] = None + + self._pf_init : str = "flat" + self._pf_init : str = "results" + self._nb_bus_before : Optional[int] = None # number of active bus at the preceeding step + + self.thermal_limit_a : Optional[np.ndarray] = None + + self._iref_slack : Optional[int] = None + self._id_bus_added : Optional[int] = None + self._fact_mult_gen : int = -1 self._what_object_where = None self._number_true_line = -1 self._corresp_name_fun = {} @@ -203,15 +204,15 @@ def __init__( # TODO storage doc (in grid2op rst) of the backend self.can_output_theta = True # I support the voltage angle - self.theta_or = None - self.theta_ex = None - self.load_theta = None - self.gen_theta = None - self.storage_theta = None + self.theta_or : Optional[np.ndarray] = None + self.theta_ex : Optional[np.ndarray] = None + self.load_theta : Optional[np.ndarray] = None + self.gen_theta : Optional[np.ndarray] = None + self.storage_theta : Optional[np.ndarray] = None - self._lightsim2grid = lightsim2grid - self._dist_slack = dist_slack - self._max_iter = max_iter + self._lightsim2grid : bool = lightsim2grid + self._dist_slack : bool = dist_slack + self._max_iter : bool = max_iter def _check_for_non_modeled_elements(self): """This function check for elements in the pandapower grid that will have no impact on grid2op. @@ -238,7 +239,7 @@ def _check_for_non_modeled_elements(self): f"work, but you won't be able to modify them)." ) - def get_theta(self): + def get_theta(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """ TODO doc @@ -263,7 +264,7 @@ def get_theta(self): self.cst_1 * self.storage_theta, ) - def get_nb_active_bus(self): + def get_nb_active_bus(self) -> int: """ INTERNAL @@ -279,22 +280,24 @@ def get_nb_active_bus(self): return self._grid.bus["in_service"].sum() @staticmethod - def _load_grid_load_p_mw(grid): + def _load_grid_load_p_mw(grid) -> pd.Series: return grid.load["p_mw"] @staticmethod - def _load_grid_load_q_mvar(grid): + def _load_grid_load_q_mvar(grid) -> pd.Series: return grid.load["q_mvar"] @staticmethod - def _load_grid_gen_p_mw(grid): + def _load_grid_gen_p_mw(grid) -> pd.Series: return grid.gen["p_mw"] @staticmethod - def _load_grid_gen_vm_pu(grid): + def _load_grid_gen_vm_pu(grid) -> pd.Series: return grid.gen["vm_pu"] - def reset(self, path=None, grid_filename=None): + def reset(self, + path : Union[os.PathLike, str], + grid_filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -312,7 +315,9 @@ def reset(self, path=None, grid_filename=None): self._topo_vect[:] = self._get_topo_vect() self.comp_time = 0.0 - def load_grid(self, path=None, filename=None): + def load_grid(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> None: """ INTERNAL @@ -558,7 +563,7 @@ def load_grid(self, path=None, filename=None): self._init_private_attrs() - def _init_private_attrs(self): + def _init_private_attrs(self) -> None: # number of elements per substation self.sub_info = np.zeros(self.n_sub, dtype=dt_int) @@ -780,7 +785,7 @@ def _init_private_attrs(self): self._grid ) # will be initialized in the "assert_grid_correct" - def storage_deact_for_backward_comaptibility(self): + def storage_deact_for_backward_comaptibility(self) -> None: self._init_private_attrs() def _convert_id_topo(self, id_big_topo): @@ -797,7 +802,7 @@ def _convert_id_topo(self, id_big_topo): """ return self._big_topo_to_obj[id_big_topo] - def apply_action(self, backendAction=None): + def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None: """ INTERNAL @@ -807,6 +812,7 @@ def apply_action(self, backendAction=None): """ if backendAction is None: return + cls = type(self) ( @@ -983,7 +989,7 @@ def _aux_get_line_info(self, colname1, colname2): ) return res - def runpf(self, is_dc=False): + def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: """ INTERNAL @@ -1149,7 +1155,7 @@ def runpf(self, is_dc=False): msg = exc_.__str__() return False, DivergingPowerFlow(f'powerflow diverged with error :"{msg}"') - def assert_grid_correct(self): + def assert_grid_correct(self) -> None: """ INTERNAL @@ -1159,7 +1165,7 @@ def assert_grid_correct(self): """ super().assert_grid_correct() - def _reset_all_nan(self): + def _reset_all_nan(self) -> None: self.p_or[:] = np.NaN self.q_or[:] = np.NaN self.v_or[:] = np.NaN @@ -1185,14 +1191,13 @@ def _reset_all_nan(self): self.gen_theta[:] = np.NaN self.storage_theta[:] = np.NaN - def copy(self): + def copy(self) -> "PandaPowerBackend": """ INTERNAL .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\ - Performs a deep copy of the power :attr:`_grid`. - As pandapower is pure python, the deep copy operator is perfectly suited for the task. + This should return a deep copy of the Backend itself and not just the `self._grid` """ # res = copy.deepcopy(self) # this was really slow... res = type(self)(**self._my_kwargs) @@ -1287,7 +1292,7 @@ def copy(self): return res - def close(self): + def close(self) -> None: """ INTERNAL @@ -1301,7 +1306,7 @@ def close(self): del self.__pp_backend_initial_grid self.__pp_backend_initial_grid = None - def save_file(self, full_path): + def save_file(self, full_path: Union[os.PathLike, str]) -> None: """ INTERNAL @@ -1315,7 +1320,7 @@ def save_file(self, full_path): """ pp.to_json(self._grid, full_path) - def get_line_status(self): + def get_line_status(self) -> np.ndarray: """ INTERNAL @@ -1334,7 +1339,7 @@ def _get_line_status(self): ) ).astype(dt_bool) - def get_line_flow(self): + def get_line_flow(self) -> np.ndarray: return self.a_or def _disconnect_line(self, id_): @@ -1353,7 +1358,7 @@ def _reconnect_line(self, id_): self._grid.trafo["in_service"].iloc[id_ - self._number_true_line] = True self.line_status[id_] = True - def get_topo_vect(self): + def get_topo_vect(self) -> np.ndarray: return self._topo_vect def _get_topo_vect(self): @@ -1461,21 +1466,21 @@ def _loads_info(self): ].values.astype(dt_float) return load_p, load_q, load_v, load_theta - def generators_info(self): + def generators_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.prod_p, self.cst_1 * self.prod_q, self.cst_1 * self.prod_v, ) - def loads_info(self): + def loads_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.load_p, self.cst_1 * self.load_q, self.cst_1 * self.load_v, ) - def lines_or_info(self): + def lines_or_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.p_or, self.cst_1 * self.q_or, @@ -1483,7 +1488,7 @@ def lines_or_info(self): self.cst_1 * self.a_or, ) - def lines_ex_info(self): + def lines_ex_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.p_ex, self.cst_1 * self.q_ex, @@ -1491,7 +1496,7 @@ def lines_ex_info(self): self.cst_1 * self.a_ex, ) - def shunt_info(self): + def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: shunt_p = self.cst_1 * self._grid.res_shunt["p_mw"].values.astype(dt_float) shunt_q = self.cst_1 * self._grid.res_shunt["q_mvar"].values.astype(dt_float) shunt_v = ( @@ -1513,7 +1518,7 @@ def shunt_info(self): shunt_bus[alone] = -1 return shunt_p, shunt_q, shunt_v, shunt_bus - def storages_info(self): + def storages_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: return ( self.cst_1 * self.storage_p, self.cst_1 * self.storage_q, @@ -1545,7 +1550,7 @@ def _storages_info(self): theta_storage = np.zeros(shape=0, dtype=dt_float) return p_storage, q_storage, v_storage, theta_storage - def sub_from_bus_id(self, bus_id): + def sub_from_bus_id(self, bus_id : int) -> int: if bus_id >= self._number_true_line: return bus_id - self._number_true_line return bus_id diff --git a/grid2op/Environment/environment.py b/grid2op/Environment/environment.py index 08763efba..f8c72684b 100644 --- a/grid2op/Environment/environment.py +++ b/grid2op/Environment/environment.py @@ -195,7 +195,6 @@ def _init_backend( Create a proper and valid environment. """ - if isinstance(rewardClass, type): if not issubclass(rewardClass, BaseReward): raise Grid2OpException( @@ -234,7 +233,6 @@ def _init_backend( if self._read_from_local_dir: # test to support pickle conveniently self.backend._PATH_ENV = self.get_path_env() - # all the above should be done in this exact order, otherwise some weird behaviour might occur # this is due to the class attribute self.backend.set_env_name(self.name) diff --git a/setup.py b/setup.py index 857e3e685..16a171cf8 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,8 @@ def my_test_suite(): "tqdm>=4.45.0", "networkx>=2.4", "requests>=2.23.0", - "packaging" # because gym changes the way it uses numpy prng in version 0.26 and i need both gym before and after... + "packaging", # because gym changes the way it uses numpy prng in version 0.26 and i need both gym before and after... + "typing_extensions" ], "extras": { "optional": [ From d3875a3abfe829a1190c598a80bf17cb06dd1cdd Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 26 Sep 2023 18:09:31 +0200 Subject: [PATCH 06/46] fixing a bug in DC mode where pandapower did not detect some game over --- CHANGELOG.rst | 2 ++ grid2op/Backend/pandaPowerBackend.py | 9 ++--- grid2op/tests/test_dc_isolated_load.py | 47 ++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 grid2op/tests/test_dc_isolated_load.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bdc84f111..a906ffaa8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -33,6 +33,8 @@ Change Log [1.9.6] - 2023-xx-yy ---------------------- +- [FIXED] a bug in PandaPowerBackend (DC mode) where isolated load did not raised + exception (they should lead to a divergence) - [ADDED] now depends on the `typing_extensions` package - [IMPROVED] type hints for Backend and PandapowerBackend diff --git a/grid2op/Backend/pandaPowerBackend.py b/grid2op/Backend/pandaPowerBackend.py index c66153e9b..e9d1b7c7d 100644 --- a/grid2op/Backend/pandaPowerBackend.py +++ b/grid2op/Backend/pandaPowerBackend.py @@ -1033,9 +1033,8 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: if is_dc: pp.rundcpp(self._grid, check_connectivity=False, init="flat") - self._nb_bus_before = ( - None # if dc i start normally next time i call an ac powerflow - ) + # if dc i start normally next time i call an ac powerflow + self._nb_bus_before = None else: pp.runpp( self._grid, @@ -1069,11 +1068,13 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: self.load_theta[:], ) = self._loads_info() if not is_dc: - if not np.all(np.isfinite(self.load_v)): + if not np.isfinite(self.load_v).all(): # TODO see if there is a better way here # some loads are disconnected: it's a game over case! raise pp.powerflow.LoadflowNotConverged("Isolated load") else: + if self._grid.res_bus.loc[self._grid.bus["in_service"]]["va_degree"].isnull().any(): + raise pp.powerflow.LoadflowNotConverged("Isolated bus (dc mode)") # fix voltages magnitude that are always "nan" for dc case # self._grid.res_bus["vm_pu"] is always nan when computed in DC self.load_v[:] = self.load_pu_to_kv # TODO diff --git a/grid2op/tests/test_dc_isolated_load.py b/grid2op/tests/test_dc_isolated_load.py new file mode 100644 index 000000000..4ba4356f8 --- /dev/null +++ b/grid2op/tests/test_dc_isolated_load.py @@ -0,0 +1,47 @@ +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import grid2op +import numpy as np +from superposition_theorem import State +import unittest +import itertools +import warnings + +# from lightsim2grid import LightSimBackend + + +class TestIsolatedLoad(unittest.TestCase): + def setUp(self) -> None: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + self.env = grid2op.make("l2rpn_case14_sandbox", test=True) # , backend=LightSimBackend()) + param = self.env.parameters + param.ENV_DC = True # force the computation of the powerflow in DC mode + param.MAX_LINE_STATUS_CHANGED = 99999 + param.MAX_SUB_CHANGED = 99999 + param.NO_OVERFLOW_DISCONNECTION = True + self.env.change_parameters(param) + self.env.change_forecast_parameters(param) + time_series_id = 0 + self.env.set_id(time_series_id) + self.obs = self.env.reset() + self.obs_start, *_ = self.env.step(self.env.action_space({})) + self.tol = 3e-5 + return super().setUp() + + def test_specific_action(self): + act = self.env.action_space({'set_bus': {'loads_id': [(0, 1), (1, 2)], + 'generators_id': [(0, 2), (1, 1)], + 'lines_or_id': [(2, 2), (3, 1), (4, 2), (5, -1)], + 'lines_ex_id': [(0, 1), (2, 1), (5, -1)]}} + ) + obs, reward, done, info = self.env.step(act) + assert done + assert info["exception"] + \ No newline at end of file From f51d648d34e63d1018639ec8cf3c8365af36203f Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 26 Sep 2023 18:11:21 +0200 Subject: [PATCH 07/46] adding test for gen also --- ...isolated_load.py => test_dc_isolated_elements.py} | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) rename grid2op/tests/{test_dc_isolated_load.py => test_dc_isolated_elements.py} (76%) diff --git a/grid2op/tests/test_dc_isolated_load.py b/grid2op/tests/test_dc_isolated_elements.py similarity index 76% rename from grid2op/tests/test_dc_isolated_load.py rename to grid2op/tests/test_dc_isolated_elements.py index 4ba4356f8..5e23f2fec 100644 --- a/grid2op/tests/test_dc_isolated_load.py +++ b/grid2op/tests/test_dc_isolated_elements.py @@ -35,7 +35,17 @@ def setUp(self) -> None: self.tol = 3e-5 return super().setUp() - def test_specific_action(self): + def test_specific_action_load(self): + act = self.env.action_space({'set_bus': {'loads_id': [(0, 1), (1, 2)], + 'generators_id': [(0, 2), (1, 1)], + 'lines_or_id': [(2, 2), (3, 1), (4, 2), (5, -1)], + 'lines_ex_id': [(0, 1), (2, 1), (5, -1)]}} + ) + obs, reward, done, info = self.env.step(act) + assert done + assert info["exception"] + + def test_specific_action_gen(self): act = self.env.action_space({'set_bus': {'loads_id': [(0, 1), (1, 2)], 'generators_id': [(0, 2), (1, 1)], 'lines_or_id': [(2, 2), (3, 1), (4, 2), (5, -1)], From 63c036c8fc0b70283d9281ceb72feab0b1dadec3 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 28 Sep 2023 10:51:42 +0200 Subject: [PATCH 08/46] fix a bug in spotting divergence when shunts alone on a bus --- CHANGELOG.rst | 2 + grid2op/Action/_backendAction.py | 14 ++++- grid2op/Backend/pandaPowerBackend.py | 13 ++-- grid2op/tests/test_bug_shunt_dc.py | 80 ++++++++++++++++++++++++ grid2op/tests/test_global_to_local_dc.py | 46 -------------- 5 files changed, 100 insertions(+), 55 deletions(-) create mode 100644 grid2op/tests/test_bug_shunt_dc.py delete mode 100644 grid2op/tests/test_global_to_local_dc.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a906ffaa8..1e6e514fb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -33,6 +33,8 @@ Change Log [1.9.6] - 2023-xx-yy ---------------------- +- [BREAKING] when a shunt is alone on a bus, the powerflow will diverge even in DC mode + (previously it only converges which was wrong behaviour: grid2op should not disconnect shunt) - [FIXED] a bug in PandaPowerBackend (DC mode) where isolated load did not raised exception (they should lead to a divergence) - [ADDED] now depends on the `typing_extensions` package diff --git a/grid2op/Action/_backendAction.py b/grid2op/Action/_backendAction.py index b3f72b9a8..36e3cc733 100644 --- a/grid2op/Action/_backendAction.py +++ b/grid2op/Action/_backendAction.py @@ -240,6 +240,8 @@ def __init__(self): self.shunt_p = ValueStore(self.n_shunt, dtype=dt_float) self.shunt_q = ValueStore(self.n_shunt, dtype=dt_float) self.shunt_bus = ValueStore(self.n_shunt, dtype=dt_int) + self.current_shunt_bus = ValueStore(self.n_shunt, dtype=dt_int) + self.current_shunt_bus.values[:] = 1 self._status_or_before = np.ones(self.n_line, dtype=dt_int) self._status_ex_before = np.ones(self.n_line, dtype=dt_int) @@ -268,6 +270,7 @@ def __deepcopy__(self, memodict={}): res.shunt_p.copy(self.shunt_p) res.shunt_q.copy(self.shunt_q) res.shunt_bus.copy(self.shunt_bus) + res.current_shunt_bus.copy(self.current_shunt_bus) res._status_or_before[:] = self._status_or_before res._status_ex_before[:] = self._status_ex_before @@ -308,6 +311,7 @@ def reorder(self, no_load, no_gen, no_topo, no_storage, no_shunt): self.shunt_p.reorder(no_shunt) self.shunt_q.reorder(no_shunt) self.shunt_bus.reorder(no_shunt) + self.current_shunt_bus.reorder(no_shunt) def reset(self): # last topo @@ -331,6 +335,7 @@ def reset(self): self.shunt_p.reset() self.shunt_q.reset() self.shunt_bus.reset() + self.current_shunt_bus.reset() def all_changed(self): # last topo @@ -404,7 +409,7 @@ def __iadd__(self, other): self.storage_power.set_val(storage_power) # II shunts - if self.shunts_data_available: + if type(self).shunts_data_available: shunts = {} if other.shunts_data_available: shunts["shunt_p"] = other.shunt_p @@ -417,6 +422,7 @@ def __iadd__(self, other): self.shunt_q.set_val(arr_) arr_ = shunts["shunt_bus"] self.shunt_bus.set_val(arr_) + self.current_shunt_bus.values[self.shunt_bus.changed] = self.shunt_bus.values[self.shunt_bus.changed] # III line status # this need to be done BEFORE the topology, as a connected powerline will be connected to their old bus. @@ -575,9 +581,13 @@ def get_storages_bus_global(self): def _get_active_bus(self): self.activated_bus[:, :] = False - tmp = self.current_topo.values - 1 + tmp = self.current_topo.values - 1 # TODO global to local ! is_el_conn = tmp >= 0 self.activated_bus[self.big_topo_to_subid[is_el_conn], tmp[is_el_conn]] = True + if type(self).shunts_data_available: + is_el_conn = self.current_shunt_bus.values >= 0 + tmp = self.current_shunt_bus.values - 1 + self.activated_bus[type(self).shunt_to_subid[is_el_conn], tmp[is_el_conn]] = True def update_state(self, powerline_disconnected): """ diff --git a/grid2op/Backend/pandaPowerBackend.py b/grid2op/Backend/pandaPowerBackend.py index e9d1b7c7d..7c4e770a9 100644 --- a/grid2op/Backend/pandaPowerBackend.py +++ b/grid2op/Backend/pandaPowerBackend.py @@ -1054,7 +1054,11 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: # TODO see if there is a better way here -> do not handle this here, but rather in Backend._next_grid_state # sometimes pandapower does not detect divergence and put Nan. raise pp.powerflow.LoadflowNotConverged("Divergence due to Nan values in res_gen table.") - + + # if a connected bus has a no voltage, it's a divergence (grid was not connected) + if self._grid.res_bus.loc[self._grid.bus["in_service"]]["va_degree"].isnull().any(): + raise pp.powerflow.LoadflowNotConverged("Isolated bus") + ( self.prod_p[:], self.prod_q[:], @@ -1067,14 +1071,13 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: self.load_v[:], self.load_theta[:], ) = self._loads_info() + if not is_dc: if not np.isfinite(self.load_v).all(): # TODO see if there is a better way here # some loads are disconnected: it's a game over case! raise pp.powerflow.LoadflowNotConverged("Isolated load") else: - if self._grid.res_bus.loc[self._grid.bus["in_service"]]["va_degree"].isnull().any(): - raise pp.powerflow.LoadflowNotConverged("Isolated bus (dc mode)") # fix voltages magnitude that are always "nan" for dc case # self._grid.res_bus["vm_pu"] is always nan when computed in DC self.load_v[:] = self.load_pu_to_kv # TODO @@ -1513,10 +1516,6 @@ def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: shunt_bus = type(self).global_bus_to_local(self._grid.shunt["bus"].values, self.shunt_to_subid) shunt_v[~self._grid.shunt["in_service"].values] = 0 shunt_bus[~self._grid.shunt["in_service"].values] = -1 - # handle shunt alone on a bus (in this case it should probably diverge...) - alone = ~np.isfinite(shunt_v) - shunt_v[alone] = 0 - shunt_bus[alone] = -1 return shunt_p, shunt_q, shunt_v, shunt_bus def storages_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: diff --git a/grid2op/tests/test_bug_shunt_dc.py b/grid2op/tests/test_bug_shunt_dc.py new file mode 100644 index 000000000..31a9409f1 --- /dev/null +++ b/grid2op/tests/test_bug_shunt_dc.py @@ -0,0 +1,80 @@ +# Copyright (c) 2019-2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import unittest +import warnings +import grid2op +from grid2op.Action import CompleteAction +from grid2op.Backend import PandaPowerBackend +from lightsim2grid import LightSimBackend +import pandapower as pp +import numpy as np +import pdb + + +class AuxTestBugShuntDC: + def get_backend(self): + raise NotImplementedError() + + def setUp(self) -> None: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + self.env = grid2op.make("l2rpn_case14_sandbox", + test=True, + action_class=CompleteAction, + backend=self.get_backend(), + _add_to_name=type(self).__name__) + self.bk_act = type(self.env.backend).my_bk_act_class() + + def tearDown(self) -> None: + self.env.close() + + def _aux_modify_shunt(self): + self.bk_act += self.env.action_space({"shunt": {"set_bus": np.array([2], dtype=int)}}) + self.env.backend.apply_action(self.bk_act) + if isinstance(self.env.backend, PandaPowerBackend): + assert self.env.backend._grid.shunt["bus"][0] == 22 + assert self.env.backend._grid.bus["in_service"][self.env.backend._grid.shunt["bus"][0]] + + def test_shunt_dc(self): + conv, exc_ = self.env.backend.runpf(is_dc=True) + p_subs, q_subs, p_bus, q_bus, diff_v_bus = self.env.backend.check_kirchoff() + assert np.abs(p_subs).max() <= 1e-5 + assert np.abs(p_bus).max() <= 1e-5 + # below it does not pass due to https://github.com/e2nIEE/pandapower/issues/1996 (fixed !) + assert np.abs(diff_v_bus).max() <= 1e-5 + + def test_shunt_alone_dc(self): + self._aux_modify_shunt() + conv, exc_ = self.env.backend.runpf(is_dc=True) + assert not conv + # does not work now because of an isolated element + # p_subs, q_subs, p_bus, q_bus, diff_v_bus = self.env.backend.check_kirchoff() + # assert np.abs(p_subs).max() <= 1e-5 + # assert np.abs(p_bus).max() <= 1e-5 + # # below it does not pass due to https://github.com/e2nIEE/pandapower/issues/1996 + # assert np.abs(diff_v_bus).max() <= 1e-5 + + def test_shunt_alone_ac(self): + self._aux_modify_shunt() + conv, exc_ = self.env.backend.runpf(is_dc=False) + assert not conv + + +class TestBugShuntDCPP(AuxTestBugShuntDC, unittest.TestCase): + def get_backend(self): + return PandaPowerBackend() + + +class TestBugShuntDCLS(AuxTestBugShuntDC, unittest.TestCase): + def get_backend(self): + return LightSimBackend() + + +if __name__ == "__main__": + unittest.main() diff --git a/grid2op/tests/test_global_to_local_dc.py b/grid2op/tests/test_global_to_local_dc.py deleted file mode 100644 index ec43088f1..000000000 --- a/grid2op/tests/test_global_to_local_dc.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2019-2023, RTE (https://www.rte-france.com) -# See AUTHORS.txt -# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. -# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, -# you can obtain one at http://mozilla.org/MPL/2.0/. -# SPDX-License-Identifier: MPL-2.0 -# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. - -import unittest -import warnings -import grid2op -import pandapower as pp -import numpy as np -import pdb - - -class TestBugShuntDC(unittest.TestCase): - def setUp(self) -> None: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) - - def tearDown(self) -> None: - self.env.close() - - def test_shunt_dc(self): - self.env.backend.runpf(is_dc=True) - p_subs, q_subs, p_bus, q_bus, diff_v_bus = self.env.backend.check_kirchoff() - assert np.abs(p_subs).max() <=1e-5 - assert np.abs(p_bus).max() <=1e-5 - # below it does not pass due to https://github.com/e2nIEE/pandapower/issues/1996 - # assert np.abs(diff_v_bus).max() <=1e-5 - - def test_shunt_dc_alone(self): - self.env.backend._grid.shunt["bus"][0] += 14 - self.env.backend._grid.bus["in_service"][self.env.backend._grid.shunt["bus"][0]] = True - self.env.backend.runpf(is_dc=True) - p_subs, q_subs, p_bus, q_bus, diff_v_bus = self.env.backend.check_kirchoff() - assert np.abs(p_subs).max() <=1e-5 - assert np.abs(p_bus).max() <=1e-5 - # below it does not pass due to https://github.com/e2nIEE/pandapower/issues/1996 - # assert np.abs(diff_v_bus).max() <=1e-5 - - -if __name__ == "__main__": - unittest.main() From 48a8fbc4f0e41ffc84d8ab2ade192c8ba76840f1 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 28 Sep 2023 11:12:03 +0200 Subject: [PATCH 09/46] fix the act.remove_line_status_from_topo --- CHANGELOG.rst | 4 +- grid2op/Action/baseAction.py | 97 +++++++++++++------ grid2op/tests/test_Environment.py | 2 +- .../test_remove_line_status_from_topo.py | 36 ++++++- 4 files changed, 101 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1e6e514fb..880ba9eb6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -37,8 +37,10 @@ Change Log (previously it only converges which was wrong behaviour: grid2op should not disconnect shunt) - [FIXED] a bug in PandaPowerBackend (DC mode) where isolated load did not raised exception (they should lead to a divergence) +- [FIXED] some wrong behaviour in the `remove_line_status_from_topo` when no observation where provided + and `check_cooldown` is `False` - [ADDED] now depends on the `typing_extensions` package -- [IMPROVED] type hints for Backend and PandapowerBackend +- [IMPROVED] type hints for `Backend` and `PandapowerBackend` [1.9.5] - 2023-09-18 --------------------- diff --git a/grid2op/Action/baseAction.py b/grid2op/Action/baseAction.py index 02e3770f1..14c1ed1bc 100644 --- a/grid2op/Action/baseAction.py +++ b/grid2op/Action/baseAction.py @@ -1244,10 +1244,10 @@ def remove_line_status_from_topo(self, - if a powerline is affected to a certain bus at one of its end with `set_bus` (for example `set_bus` to 1 or 2) and at the same time disconnected (`set_line_status` is -1) then - the `set_bus` part is ignore to avoid `AmbiguousAction` + the `set_bus` part is ignored to avoid `AmbiguousAction` - if a powerline is disconnect from its bus at one of its end with `set_bus` (for example `set_bus` to -1) and at the same time reconnected (`set_line_status` is 1) then - the `set_bus` part is ignore to avoid `AmbiguousAction` + the `set_bus` part is ignored to avoid `AmbiguousAction` - if a powerline is affected to a certain bus at one of its end with `change_bus` (`change_bus` is ``True``) and at the same time disconnected (`set_line_status` is -1) then the `change_bus` part is ignore to avoid `AmbiguousAction` @@ -1261,6 +1261,10 @@ def remove_line_status_from_topo(self, .. note:: As from version 1.9.0 you are no longer forced to provide an observation if `check_cooldown=False` + + .. warning:: + For grid2op equal or lower to 1.9.5 this function was bugged in some corner cases. We highly recommend + upgrading if you use this function with these grid2op versions. Examples --------- @@ -1335,44 +1339,75 @@ def remove_line_status_from_topo(self, # remove the "set" part that would cause a reconnection mask_reco = np.full(cls.dim_topo, fill_value=False) - reco_or_ = np.full(cls.n_line, fill_value=False) - reco_or_[(self._set_topo_vect[cls.line_or_pos_topo_vect] > 0) & - disconnected & line_under_cooldown] = True - mask_reco[cls.line_or_pos_topo_vect] = reco_or_ - - reco_ex_ = np.full(cls.n_line, fill_value=False) - reco_ex_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] > 0) & - disconnected & line_under_cooldown] = True - mask_reco[cls.line_ex_pos_topo_vect] = reco_ex_ + if check_cooldown: + reco_or_ = np.full(cls.n_line, fill_value=False) + reco_or_[(self._set_topo_vect[cls.line_or_pos_topo_vect] > 0) & + disconnected & line_under_cooldown] = True + mask_reco[cls.line_or_pos_topo_vect] = reco_or_ + + reco_ex_ = np.full(cls.n_line, fill_value=False) + reco_ex_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] > 0) & + disconnected & line_under_cooldown] = True + mask_reco[cls.line_ex_pos_topo_vect] = reco_ex_ + # do not reconnect powerline that will be disconnected + reco_or_and_disco_ = np.full(cls.n_line, fill_value=False) + reco_or_and_disco_[(self._set_topo_vect[cls.line_or_pos_topo_vect] > 0) & (self._set_topo_vect[cls.line_ex_pos_topo_vect] < 0)] = True + reco_or_and_disco_[(self._set_topo_vect[cls.line_or_pos_topo_vect] > 0) & (self._set_line_status < 0)] = True + mask_reco[cls.line_or_pos_topo_vect] |= reco_or_and_disco_ + + reco_ex_and_disco_ = np.full(cls.n_line, fill_value=False) + reco_ex_and_disco_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] > 0) & (self._set_topo_vect[cls.line_or_pos_topo_vect] < 0)] = True + reco_ex_and_disco_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] > 0) & (self._set_line_status < 0)] = True + mask_reco[cls.line_ex_pos_topo_vect] |= reco_ex_and_disco_ + # and now remove the change from the set_bus self._set_topo_vect[mask_reco] = 0 + # remove the "set" that would cause a disconnection mask_disco = np.full(cls.dim_topo, fill_value=False) - reco_or_ = np.full(cls.n_line, fill_value=False) - reco_or_[(self._set_topo_vect[cls.line_or_pos_topo_vect] < 0) & - connected & line_under_cooldown] = True - mask_disco[cls.line_or_pos_topo_vect] = reco_or_ - - reco_ex_ = np.full(cls.n_line, fill_value=False) - reco_ex_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] < 0) & - connected & line_under_cooldown] = True - mask_disco[cls.line_ex_pos_topo_vect] = reco_ex_ - + if check_cooldown: + disco_or_ = np.full(cls.n_line, fill_value=False) + disco_or_[(self._set_topo_vect[cls.line_or_pos_topo_vect] < 0) & + connected & line_under_cooldown] = True + mask_disco[cls.line_or_pos_topo_vect] = disco_or_ + + disco_ex_ = np.full(cls.n_line, fill_value=False) + disco_ex_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] < 0) & + connected & line_under_cooldown] = True + mask_disco[cls.line_ex_pos_topo_vect] = disco_ex_ + + disco_or_and_reco_ = np.full(cls.n_line, fill_value=False) + disco_or_and_reco_[(self._set_topo_vect[cls.line_or_pos_topo_vect] < 0) & (self._set_line_status > 0)] = True + mask_disco[cls.line_or_pos_topo_vect] |= disco_or_and_reco_ + + disco_ex_and_reco_ = np.full(cls.n_line, fill_value=False) + disco_ex_and_reco_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] < 0) & (self._set_line_status > 0)] = True + mask_disco[cls.line_ex_pos_topo_vect] |= disco_ex_and_reco_ self._set_topo_vect[mask_disco] = 0 # remove the "change" part when powerlines is disconnected mask_disco = np.full(cls.dim_topo, fill_value=False) - reco_or_ = np.full(cls.n_line, fill_value=False) - reco_or_[self._change_bus_vect[cls.line_or_pos_topo_vect] & - disconnected & line_under_cooldown] = True - mask_disco[cls.line_or_pos_topo_vect] = reco_or_ - - reco_ex_ = np.full(cls.n_line, fill_value=False) - reco_ex_[self._change_bus_vect[cls.line_ex_pos_topo_vect] & - disconnected & line_under_cooldown] = True - mask_disco[cls.line_ex_pos_topo_vect] = reco_ex_ - + if check_cooldown: + reco_or_ = np.full(cls.n_line, fill_value=False) + reco_or_[self._change_bus_vect[cls.line_or_pos_topo_vect] & + disconnected & line_under_cooldown] = True + mask_disco[cls.line_or_pos_topo_vect] = reco_or_ + + reco_ex_ = np.full(cls.n_line, fill_value=False) + reco_ex_[self._change_bus_vect[cls.line_ex_pos_topo_vect] & + disconnected & line_under_cooldown] = True + mask_disco[cls.line_ex_pos_topo_vect] = reco_ex_ + + # do not change bus of powerline that will be disconnected + reco_or_and_disco_ = np.full(cls.n_line, fill_value=False) + reco_or_and_disco_[(self._change_bus_vect[cls.line_or_pos_topo_vect]) & (self._set_line_status < 0)] = True + mask_disco[cls.line_or_pos_topo_vect] |= reco_or_and_disco_ + reco_ex_and_disco_ = np.full(cls.n_line, fill_value=False) + reco_ex_and_disco_[(self._change_bus_vect[cls.line_ex_pos_topo_vect]) & (self._set_line_status < 0)] = True + mask_disco[cls.line_ex_pos_topo_vect] |= reco_ex_and_disco_ + + # "erase" the change_bus for concerned powerlines self._change_bus_vect[mask_disco] = False return self diff --git a/grid2op/tests/test_Environment.py b/grid2op/tests/test_Environment.py index 9d36079f1..204c65a85 100644 --- a/grid2op/tests/test_Environment.py +++ b/grid2op/tests/test_Environment.py @@ -256,7 +256,7 @@ def test_stop_right_time(self): do_nothing ) # should load the first time stamp i += 1 - assert i == 287 + assert i == 287, f"env stopped at {i} instead of 287" def test_reward(self): done = False diff --git a/grid2op/tests/test_remove_line_status_from_topo.py b/grid2op/tests/test_remove_line_status_from_topo.py index d336a5166..a9d7ba92b 100644 --- a/grid2op/tests/test_remove_line_status_from_topo.py +++ b/grid2op/tests/test_remove_line_status_from_topo.py @@ -96,9 +96,12 @@ def test_something_when_nocooldown_butcheck_cooldown(self): # action should not be modified because there is a cooldown act_sub4_clean = self.env.action_space({"set_bus": {"substations_id": [(4, [2, 2, 2, 1, 1])]}}) + act_sub4_clean += self.env.action_space({"set_line_status": [(4, -1)]}) assert act_sub4_clean._set_topo_vect[20] == 2 - act_sub4_clean.remove_line_status_from_topo(obs, check_cooldown=False) + act_sub4_clean.remove_line_status_from_topo(obs, check_cooldown=False) + assert act_sub4_clean._set_topo_vect[19] == 2 assert act_sub4_clean._set_topo_vect[20] == 0 + assert act_sub4_clean._set_topo_vect[21] == 2 def test_limit_withoutobs(self): """test that it limit the action correctly when no obs is provided""" @@ -108,12 +111,17 @@ def test_limit_withoutobs(self): # limit reco when set act_sub4_clean = self.env.action_space({"set_bus": {"substations_id": [(4, [2, 2, 2, 1, 1])]}}) act_sub4_clean += disco + assert act_sub4_clean._set_topo_vect[19] == 2 assert act_sub4_clean._set_topo_vect[20] == 2 + assert act_sub4_clean._set_topo_vect[21] == 2 assert act_sub4_clean._set_line_status[4] == -1 with self.assertRaises(AmbiguousAction): act_sub4_clean._check_for_ambiguity() - act_sub4_clean.remove_line_status_from_topo(check_cooldown=False) - assert act_sub4_clean._set_topo_vect[20] == 0 + + act_sub4_clean.remove_line_status_from_topo(check_cooldown=False) + assert act_sub4_clean._set_topo_vect[19] == 2 # it should not affect this ! + assert act_sub4_clean._set_topo_vect[20] == 0, f"{act_sub4_clean._set_topo_vect[20]} vs 0" + assert act_sub4_clean._set_topo_vect[21] == 2 assert act_sub4_clean._set_line_status[4] == -1 act_sub4_clean._check_for_ambiguity() # does not raise @@ -125,23 +133,41 @@ def test_limit_withoutobs(self): with self.assertRaises(AmbiguousAction): act_sub4_clean._check_for_ambiguity() act_sub4_clean.remove_line_status_from_topo(check_cooldown=False) + assert act_sub4_clean._change_bus_vect[19] # it should not affect this ! assert not act_sub4_clean._change_bus_vect[20] + assert act_sub4_clean._change_bus_vect[21] # it should not affect this ! assert act_sub4_clean._set_line_status[4] == -1 act_sub4_clean._check_for_ambiguity() # does not raise # limit disco when set act_sub4_clean = self.env.action_space({"set_bus": {"substations_id": [(4, [2, -1, 2, 1, 1])]}}) act_sub4_clean += reco + assert act_sub4_clean._set_topo_vect[19] == 2 # it should not affect this ! assert act_sub4_clean._set_topo_vect[20] == -1 + assert act_sub4_clean._set_topo_vect[21] == 2 assert act_sub4_clean._set_line_status[4] == 1 with self.assertRaises(AmbiguousAction): act_sub4_clean._check_for_ambiguity() + act_sub4_clean.remove_line_status_from_topo(check_cooldown=False) + assert act_sub4_clean._set_topo_vect[19] == 2 # it should not affect this ! assert act_sub4_clean._set_topo_vect[20] == 0 + assert act_sub4_clean._set_topo_vect[21] == 2 assert act_sub4_clean._set_line_status[4] == 1 act_sub4_clean._check_for_ambiguity() # does not raise - - + def test_more_complex_action(self): + """this caused an error: all lines actions were removed""" + act_sub4_clean = self.env.action_space({"set_bus": {"lines_or_id": [(2, 2), (3, 1), (4, 2)], + "lines_ex_id": [(0, 1)], + "generators_id": [(0, 2)], + "loads_id": [(0, 1)]}}) + disco = self.env.action_space({"set_bus": (0, -1)}) + act = act_sub4_clean + disco + assert (act.set_bus[3:9] == [1, 2, 1, 2, 2, 1]).all() + act.remove_line_status_from_topo(check_cooldown=False) + assert (act.set_bus[3:9] == [0, 2, 1, 2, 2, 1]).all() + + if __name__ == "__main__": unittest.main() From 4ddded5a76b062af134cde10607e0f0bb4e7e104 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 28 Sep 2023 11:33:06 +0200 Subject: [PATCH 10/46] fix rte-france#533 and other broken tests --- grid2op/Action/baseAction.py | 9 +- grid2op/tests/BaseBackendTest.py | 110 +++++++++++---------- grid2op/tests/test_dc_isolated_elements.py | 7 +- grid2op/tests/test_issue_533.py | 35 +++++++ 4 files changed, 103 insertions(+), 58 deletions(-) create mode 100644 grid2op/tests/test_issue_533.py diff --git a/grid2op/Action/baseAction.py b/grid2op/Action/baseAction.py index 14c1ed1bc..5ee081a4e 100644 --- a/grid2op/Action/baseAction.py +++ b/grid2op/Action/baseAction.py @@ -588,6 +588,9 @@ def as_serializable_dict(self) -> dict: Once you have these dictionnary, you can use them to build back the action from the action space. + .. warning:: + This function does not work correctly with version of grid2op lower (or equal to) 1.9.5 + Examples --------- @@ -628,7 +631,8 @@ def as_serializable_dict(self) -> dict: self._aux_serialize_add_key_change("gen_change_bus", "generators_id", res["change_bus"]) self._aux_serialize_add_key_change("line_or_change_bus", "lines_or_id", res["change_bus"]) self._aux_serialize_add_key_change("line_ex_change_bus", "lines_ex_id", res["change_bus"]) - self._aux_serialize_add_key_change("storage_change_bus", "storages_id", res["change_bus"]) + if hasattr(type(self), "n_storage") and type(self).n_storage: + self._aux_serialize_add_key_change("storage_change_bus", "storages_id", res["change_bus"]) if not res["change_bus"]: del res["change_bus"] @@ -646,7 +650,8 @@ def as_serializable_dict(self) -> dict: self._aux_serialize_add_key_set("gen_set_bus", "generators_id", res["set_bus"]) self._aux_serialize_add_key_set("line_or_set_bus", "lines_or_id", res["set_bus"]) self._aux_serialize_add_key_set("line_ex_set_bus", "lines_ex_id", res["set_bus"]) - self._aux_serialize_add_key_set("storage_set_bus", "storages_id", res["set_bus"]) + if hasattr(type(self), "n_storage") and type(self).n_storage: + self._aux_serialize_add_key_set("storage_set_bus", "storages_id", res["set_bus"]) if not res["set_bus"]: del res["set_bus"] diff --git a/grid2op/tests/BaseBackendTest.py b/grid2op/tests/BaseBackendTest.py index 01e849e20..f751446a7 100644 --- a/grid2op/tests/BaseBackendTest.py +++ b/grid2op/tests/BaseBackendTest.py @@ -2572,60 +2572,66 @@ def test_storage_action_topo(self): } ) obs, reward, done, info = self.env.step(act) - assert not info[ - "exception" - ], "error when storage is disconnected with 0 production, throw an error, but should not" - assert not done - storage_p, storage_q, storage_v = self.env.backend.storages_info() - assert np.all( - np.abs(storage_p - [0.0, array_modif[1]]) <= self.tol_one - ), "storage is not disconnected, yet alone on its busbar" - assert obs.storage_bus[0] == -1, "storage should be disconnected" - assert storage_v[0] == 0.0, "storage 0 should be disconnected" - assert obs.line_or_bus[8] == 1 - assert obs.gen_bus[3] == 1 - self._aux_test_kirchoff() + assert done # as of grid2op 1.9.6 + assert info["exception"] # as of grid2op 1.9.6 + + # LEGACY BEHAVIOUR: storage was automatically disconnected in this case + # which was NOT normal ! + + # assert not info[ + # "exception" + # ], "error when storage is disconnected with 0 production, throw an error, but should not" + # assert not done + # storage_p, storage_q, storage_v = self.env.backend.storages_info() + # assert np.all( + # np.abs(storage_p - [0.0, array_modif[1]]) <= self.tol_one + # ), "storage is not disconnected, yet alone on its busbar" + # assert obs.storage_bus[0] == -1, "storage should be disconnected" + # assert storage_v[0] == 0.0, "storage 0 should be disconnected" + # assert obs.line_or_bus[8] == 1 + # assert obs.gen_bus[3] == 1 + # self._aux_test_kirchoff() # check that if i don't touch it it's set to 0 - act = self.env.action_space() - obs, reward, done, info = self.env.step(act) - assert not info["exception"] - storage_p, storage_q, storage_v = self.env.backend.storages_info() - assert np.all( - np.abs(storage_p - 0.0) <= self.tol_one - ), "storage should produce 0" - assert np.all( - np.abs(storage_q - 0.0) <= self.tol_one - ), "storage should produce 0" - assert obs.storage_bus[0] == -1, "storage should be disconnected" - assert storage_v[0] == 0.0, "storage 0 should be disconnected" - assert obs.line_or_bus[8] == 1 - assert obs.gen_bus[3] == 1 - self._aux_test_kirchoff() - - # trying to act on a disconnected storage => illegal) - array_modif = np.array([2.0, 7.0], dtype=dt_float) - act = self.env.action_space({"set_storage": array_modif}) - obs, reward, done, info = self.env.step(act) - assert info["exception"] # action should be illegal - assert not done # this is fine, as it's illegal it's replaced by do nothing - self._aux_test_kirchoff() - - # trying to reconnect a storage alone on a bus => game over, not connected bus - array_modif = np.array([1.0, 7.0], dtype=dt_float) - act = self.env.action_space( - { - "set_storage": array_modif, - "set_bus": { - "storages_id": [(0, 2)], - "lines_or_id": [(8, 1)], - "generators_id": [(3, 1)], - }, - } - ) - obs, reward, done, info = self.env.step(act) - assert info["exception"] # this is a game over - assert done + # act = self.env.action_space() + # obs, reward, done, info = self.env.step(act) + # assert not info["exception"] + # storage_p, storage_q, storage_v = self.env.backend.storages_info() + # assert np.all( + # np.abs(storage_p - 0.0) <= self.tol_one + # ), "storage should produce 0" + # assert np.all( + # np.abs(storage_q - 0.0) <= self.tol_one + # ), "storage should produce 0" + # assert obs.storage_bus[0] == -1, "storage should be disconnected" + # assert storage_v[0] == 0.0, "storage 0 should be disconnected" + # assert obs.line_or_bus[8] == 1 + # assert obs.gen_bus[3] == 1 + # self._aux_test_kirchoff() + + # # trying to act on a disconnected storage => illegal) + # array_modif = np.array([2.0, 7.0], dtype=dt_float) + # act = self.env.action_space({"set_storage": array_modif}) + # obs, reward, done, info = self.env.step(act) + # assert info["exception"] # action should be illegal + # assert not done # this is fine, as it's illegal it's replaced by do nothing + # self._aux_test_kirchoff() + + # # trying to reconnect a storage alone on a bus => game over, not connected bus + # array_modif = np.array([1.0, 7.0], dtype=dt_float) + # act = self.env.action_space( + # { + # "set_storage": array_modif, + # "set_bus": { + # "storages_id": [(0, 2)], + # "lines_or_id": [(8, 1)], + # "generators_id": [(3, 1)], + # }, + # } + # ) + # obs, reward, done, info = self.env.step(act) + # assert info["exception"] # this is a game over + # assert done class BaseIssuesTest(MakeBackend): diff --git a/grid2op/tests/test_dc_isolated_elements.py b/grid2op/tests/test_dc_isolated_elements.py index 5e23f2fec..c672b8b9e 100644 --- a/grid2op/tests/test_dc_isolated_elements.py +++ b/grid2op/tests/test_dc_isolated_elements.py @@ -8,13 +8,10 @@ import grid2op import numpy as np -from superposition_theorem import State import unittest import itertools import warnings -# from lightsim2grid import LightSimBackend - class TestIsolatedLoad(unittest.TestCase): def setUp(self) -> None: @@ -54,4 +51,6 @@ def test_specific_action_gen(self): obs, reward, done, info = self.env.step(act) assert done assert info["exception"] - \ No newline at end of file + +if __name__ == "__main__": + unittest.main() diff --git a/grid2op/tests/test_issue_533.py b/grid2op/tests/test_issue_533.py new file mode 100644 index 000000000..016a71f12 --- /dev/null +++ b/grid2op/tests/test_issue_533.py @@ -0,0 +1,35 @@ + +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import grid2op +import warnings +import unittest +import pdb + + +class Issue533Tester(unittest.TestCase): + def setUp(self) -> None: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + self.env = grid2op.make('l2rpn_neurips_2020_track1', + test=True, + ) + self.env.seed(0) + return super().setUp() + + def tearDown(self): + self.env.close() + + def test_issue_as_serializable_dict(self): + actions = self.env.action_space.get_all_unitary_topologies_set(self.env.action_space, sub_id=1) + actions[1].as_serializable_dict() + + +if __name__ == '__main__': + unittest.main() From 42c090607a50d3b6464b17727e32cd4002fd1e2c Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 28 Sep 2023 12:02:33 +0200 Subject: [PATCH 11/46] fix the tests for storage units --- CHANGELOG.rst | 3 + grid2op/tests/test_Storage.py | 129 +++++++++++++++++----------------- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 880ba9eb6..d9ade0e5a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -33,6 +33,9 @@ Change Log [1.9.6] - 2023-xx-yy ---------------------- +- [BREAKING] when a storage is connected alone on a bus, even if it produces / absorbs 0.0 MW it + will raise a diverging powerflow error (previously the storage was automatically disconnected by + `PandaPowerBackend`, but probably not by other backends) - [BREAKING] when a shunt is alone on a bus, the powerflow will diverge even in DC mode (previously it only converges which was wrong behaviour: grid2op should not disconnect shunt) - [FIXED] a bug in PandaPowerBackend (DC mode) where isolated load did not raised diff --git a/grid2op/tests/test_Storage.py b/grid2op/tests/test_Storage.py index 29955cf51..5944983d2 100644 --- a/grid2op/tests/test_Storage.py +++ b/grid2op/tests/test_Storage.py @@ -876,71 +876,72 @@ def test_storage_action_topo(self): assert obs.gen_bus[3] == 1 self._aux_test_kirchoff() + # THIS IS EXPECTED THAT IT DOES NOT PASS FROM GRID2OP 1.9.6 ! # fourth case: isolated storage on a busbar (so it is disconnected, but with 0. production => so thats fine) - array_modif = np.array([0.0, 7.0], dtype=dt_float) - act = self.env.action_space( - { - "set_storage": array_modif, - "set_bus": { - "storages_id": [(0, 2)], - "lines_or_id": [(8, 1)], - "generators_id": [(3, 1)], - }, - } - ) - obs, reward, done, info = self.env.step(act) - assert not info["exception"] - storage_p, storage_q, storage_v = self.env.backend.storages_info() - assert np.all( - np.abs(storage_p - [0.0, array_modif[1]]) <= self.tol_one - ), "storage is not disconnected, yet alone on its busbar" - assert np.all(np.abs(storage_q - 0.0) <= self.tol_one) - assert obs.storage_bus[0] == -1, "storage should be disconnected" - assert storage_v[0] == 0.0, "storage 0 should be disconnected" - assert obs.line_or_bus[8] == 1 - assert obs.gen_bus[3] == 1 - self._aux_test_kirchoff() - - # check that if i don't touch it it's set to 0 - act = self.env.action_space() - obs, reward, done, info = self.env.step(act) - assert not info["exception"] - storage_p, storage_q, storage_v = self.env.backend.storages_info() - assert np.all( - np.abs(storage_p - 0.0) <= self.tol_one - ), "storage should produce 0" - assert np.all( - np.abs(storage_q - 0.0) <= self.tol_one - ), "storage should produce 0" - assert obs.storage_bus[0] == -1, "storage should be disconnected" - assert storage_v[0] == 0.0, "storage 0 should be disconnected" - assert obs.line_or_bus[8] == 1 - assert obs.gen_bus[3] == 1 - self._aux_test_kirchoff() - - # trying to act on a disconnected storage => illegal) - array_modif = np.array([2.0, 7.0], dtype=dt_float) - act = self.env.action_space({"set_storage": array_modif}) - obs, reward, done, info = self.env.step(act) - assert info["exception"] # action should be illegal - assert not done # this is fine, as it's illegal it's replaced by do nothing - self._aux_test_kirchoff() - - # trying to reconnect a storage alone on a bus => game over, not connected bus - array_modif = np.array([1.0, 7.0], dtype=dt_float) - act = self.env.action_space( - { - "set_storage": array_modif, - "set_bus": { - "storages_id": [(0, 2)], - "lines_or_id": [(8, 1)], - "generators_id": [(3, 1)], - }, - } - ) - obs, reward, done, info = self.env.step(act) - assert info["exception"] # this is a game over - assert done + # array_modif = np.array([0.0, 7.0], dtype=dt_float) + # act = self.env.action_space( + # { + # "set_storage": array_modif, + # "set_bus": { + # "storages_id": [(0, 2)], + # "lines_or_id": [(8, 1)], + # "generators_id": [(3, 1)], + # }, + # } + # ) + # obs, reward, done, info = self.env.step(act) + # assert not info["exception"] + # storage_p, storage_q, storage_v = self.env.backend.storages_info() + # assert np.all( + # np.abs(storage_p - [0.0, array_modif[1]]) <= self.tol_one + # ), "storage is not disconnected, yet alone on its busbar" + # assert np.all(np.abs(storage_q - 0.0) <= self.tol_one) + # assert obs.storage_bus[0] == -1, "storage should be disconnected" + # assert storage_v[0] == 0.0, "storage 0 should be disconnected" + # assert obs.line_or_bus[8] == 1 + # assert obs.gen_bus[3] == 1 + # self._aux_test_kirchoff() + + # # check that if i don't touch it it's set to 0 + # act = self.env.action_space() + # obs, reward, done, info = self.env.step(act) + # assert not info["exception"] + # storage_p, storage_q, storage_v = self.env.backend.storages_info() + # assert np.all( + # np.abs(storage_p - 0.0) <= self.tol_one + # ), "storage should produce 0" + # assert np.all( + # np.abs(storage_q - 0.0) <= self.tol_one + # ), "storage should produce 0" + # assert obs.storage_bus[0] == -1, "storage should be disconnected" + # assert storage_v[0] == 0.0, "storage 0 should be disconnected" + # assert obs.line_or_bus[8] == 1 + # assert obs.gen_bus[3] == 1 + # self._aux_test_kirchoff() + + # # trying to act on a disconnected storage => illegal) + # array_modif = np.array([2.0, 7.0], dtype=dt_float) + # act = self.env.action_space({"set_storage": array_modif}) + # obs, reward, done, info = self.env.step(act) + # assert info["exception"] # action should be illegal + # assert not done # this is fine, as it's illegal it's replaced by do nothing + # self._aux_test_kirchoff() + + # # trying to reconnect a storage alone on a bus => game over, not connected bus + # array_modif = np.array([1.0, 7.0], dtype=dt_float) + # act = self.env.action_space( + # { + # "set_storage": array_modif, + # "set_bus": { + # "storages_id": [(0, 2)], + # "lines_or_id": [(8, 1)], + # "generators_id": [(3, 1)], + # }, + # } + # ) + # obs, reward, done, info = self.env.step(act) + # assert info["exception"] # this is a game over + # assert done if __name__ == "__main__": unittest.main() From aba5f2a7081c4c18fb4389a5d1dccf3b812948c3 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 3 Oct 2023 16:58:03 +0200 Subject: [PATCH 12/46] adressing issue rte-france#536 [skip ci] --- grid2op/MakeEnv/get_default_aux.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/grid2op/MakeEnv/get_default_aux.py b/grid2op/MakeEnv/get_default_aux.py index 8aa2d61e6..364418177 100644 --- a/grid2op/MakeEnv/get_default_aux.py +++ b/grid2op/MakeEnv/get_default_aux.py @@ -7,6 +7,8 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import numbers +import copy +import warnings from tarfile import ENCODING from grid2op.Exceptions import * @@ -86,6 +88,13 @@ def _get_default_aux( # first seek for the parameter in the kwargs, and check it's valid if name in kwargs: res = kwargs[name] + if defaultClassApp in (dict, list, set):# see https://github.com/rte-france/Grid2Op/issues/536 + try: + res = copy.deepcopy(res) + except copy.Error: + warnings.warn(f"Impossible to copy mutable value for kwargs {name}. Make sure not to reuse " + f"the same kwargs for creating two environments." + "(more info on https://github.com/rte-france/Grid2Op/issues/536)") if isclass is None: # I don't know whether it's an object or a class error_msg_here = None From 1937f4d4f5d98601736ac1650111ab9f829f24e1 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 6 Oct 2023 09:20:50 +0200 Subject: [PATCH 13/46] adding a utility function to better load the grid --- grid2op/Backend/backend.py | 33 ++++++++++++++++++++++++ grid2op/Backend/educPandaPowerBackend.py | 13 +--------- grid2op/Backend/pandaPowerBackend.py | 15 +---------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index 05350aa13..936df7e48 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -172,6 +172,39 @@ def __init__(self, for k, v in kwargs.items(): self._my_kwargs[k] = v + def make_complete_path(self, + path : Union[os.PathLike, str], + filename : Optional[Union[os.PathLike, str]]=None) -> str + """Auxiliary function to retrieve the full path of the grid. + + It is best used at the beginning of the `load_grid` function of a backend. + + Returns + ------- + _type_ + _description_ + + Raises + ------ + Grid2OpException + _description_ + Grid2OpException + _description_ + """ + if path is None and filename is None: + raise Grid2OpException( + "You must provide at least one of path or file to load a powergrid." + ) + if path is None: + full_path = filename + elif filename is None: + full_path = path + else: + full_path = os.path.join(path, filename) + if not os.path.exists(full_path): + raise Grid2OpException('There is no powergrid at "{}"'.format(full_path)) + return full_path + @property def is_loaded(self) -> bool: """Return whether or not this backend has been loaded, that is if `load_grid` has been called or not with this instance.""" diff --git a/grid2op/Backend/educPandaPowerBackend.py b/grid2op/Backend/educPandaPowerBackend.py index f9f21a867..6caf2f039 100644 --- a/grid2op/Backend/educPandaPowerBackend.py +++ b/grid2op/Backend/educPandaPowerBackend.py @@ -133,18 +133,7 @@ def load_grid(self, """ # first, handles different kind of path: - if path is None and filename is None: - raise RuntimeError( - "You must provide at least one of path or file to load a powergrid." - ) - if path is None: - full_path = filename - elif filename is None: - full_path = path - else: - full_path = os.path.join(path, filename) - if not os.path.exists(full_path): - raise RuntimeError('There is no powergrid at "{}"'.format(full_path)) + full_path = self.make_complete_path(path, filename) # then load the grid located at the full path and store it in `self._grid` # raise an exception if it can't be loaded diff --git a/grid2op/Backend/pandaPowerBackend.py b/grid2op/Backend/pandaPowerBackend.py index 7c4e770a9..7de151f1b 100644 --- a/grid2op/Backend/pandaPowerBackend.py +++ b/grid2op/Backend/pandaPowerBackend.py @@ -328,26 +328,13 @@ def load_grid(self, are set as "out of service" unless a topological action acts on these specific substations. """ - - if path is None and filename is None: - raise RuntimeError( - "You must provide at least one of path or file to load a powergrid." - ) - if path is None: - full_path = filename - elif filename is None: - full_path = path - else: - full_path = os.path.join(path, filename) - if not os.path.exists(full_path): - raise RuntimeError('There is no powergrid at "{}"'.format(full_path)) + full_path = self.make_complete_path(path, filename) with warnings.catch_warnings(): # remove deprecationg warnings for old version of pandapower warnings.filterwarnings("ignore", category=DeprecationWarning) warnings.filterwarnings("ignore", category=FutureWarning) self._grid = pp.from_json(full_path) - self._check_for_non_modeled_elements() # add the slack bus that is often not modeled as a generator, but i need it for this backend to work From 55d92a57cfb235e1839b307137814fdc231f6e83 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 6 Oct 2023 09:36:44 +0200 Subject: [PATCH 14/46] adding a utility function to better load the grid [skip ci] --- grid2op/Backend/backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index 936df7e48..77b0e4a9d 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -174,7 +174,7 @@ def __init__(self, def make_complete_path(self, path : Union[os.PathLike, str], - filename : Optional[Union[os.PathLike, str]]=None) -> str + filename : Optional[Union[os.PathLike, str]]=None) -> str: """Auxiliary function to retrieve the full path of the grid. It is best used at the beginning of the `load_grid` function of a backend. From 1812d5d214e8d0aa889e2b7e495c36a8ea2b569c Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 6 Oct 2023 17:35:38 +0200 Subject: [PATCH 15/46] test a generic function to make a testsuite more easily than with copy pasting [skip ci] --- grid2op/Chronics/gridStateFromFile.py | 2 + grid2op/tests/BaseBackendTest.py | 50 +++++++++++++-- grid2op/tests/helper_path_test.py | 7 +- grid2op/tests/make_backend_test_suite.py | 64 +++++++++++++++++++ grid2op/tests/test_Agent.py | 11 ++-- grid2op/tests/test_AgentsFast.py | 5 +- grid2op/tests/test_BackendConverter.py | 27 ++++---- grid2op/tests/test_ChronicsHandler.py | 27 ++++---- grid2op/tests/test_Converter.py | 7 +- grid2op/tests/test_Curtailment.py | 3 +- grid2op/tests/test_Environment.py | 29 +++++++-- grid2op/tests/test_ObsPlusAct.py | 31 ++++----- grid2op/tests/test_PandaPowerBackend.py | 63 ++++-------------- .../test_PandaPowerBackendDefaultFunc.py | 25 ++++---- grid2op/tests/test_RedispatchEnv.py | 10 +-- grid2op/tests/test_Runner.py | 3 +- grid2op/tests/test_RunnerFast.py | 3 +- grid2op/tests/test_Storage.py | 3 +- grid2op/tests/test_ts_handlers.py | 6 +- grid2op/tests/test_utils.py | 9 ++- 20 files changed, 241 insertions(+), 144 deletions(-) create mode 100644 grid2op/tests/make_backend_test_suite.py diff --git a/grid2op/Chronics/gridStateFromFile.py b/grid2op/Chronics/gridStateFromFile.py index 1cc53a725..19c7f3f83 100644 --- a/grid2op/Chronics/gridStateFromFile.py +++ b/grid2op/Chronics/gridStateFromFile.py @@ -234,6 +234,8 @@ def _assert_correct(self, dict_convert, order_backend): lend_dict_values = len(vals) if len_dict_keys != len_backend: + import pdb + pdb.set_trace() err_msg = "Conversion mismatch between backend data {} elements and converter data {} (keys)" raise IncorrectNumberOfElements(err_msg.format(len_backend, len_dict_keys)) if lend_dict_values != len_backend: diff --git a/grid2op/tests/BaseBackendTest.py b/grid2op/tests/BaseBackendTest.py index f751446a7..51a32797d 100644 --- a/grid2op/tests/BaseBackendTest.py +++ b/grid2op/tests/BaseBackendTest.py @@ -17,6 +17,11 @@ from abc import ABC, abstractmethod import inspect +from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST +PATH_DATA_TEST_INIT = PATH_DATA_TEST +PATH_DATA_TEST = PATH_DATA_TEST_PP +from grid2op.tests.helper_path_test import HelperTests + from grid2op.Action import CompleteAction try: @@ -62,7 +67,7 @@ def comb(n, k): import pdb -class MakeBackend(ABC): +class MakeBackend(ABC, HelperTests): @abstractmethod def make_backend(self, detailed_infos_for_cascading_failures=False): pass @@ -85,6 +90,9 @@ def skip_if_needed(self): class BaseTestNames(MakeBackend): + def get_path(self): + return PATH_DATA_TEST_INIT + def test_properNames(self): self.skip_if_needed() backend = self.make_backend() @@ -103,6 +111,12 @@ def test_properNames(self): class BaseTestLoadingCase(MakeBackend): + def get_path(self): + return PATH_DATA_TEST + + def get_casefile(self): + return "test_case14.json" + def test_load_file(self): backend = self.make_backend() path_matpower = self.get_path() @@ -212,6 +226,12 @@ def test_assert_grid_correct(self): class BaseTestLoadingBackendFunc(MakeBackend): + def get_path(self): + return PATH_DATA_TEST + + def get_casefile(self): + return "test_case14.json" + def setUp(self): self.backend = self.make_backend() self.path_matpower = self.get_path() @@ -230,9 +250,10 @@ def setUp(self): self.bkact_class = _BackendAction.init_grid(self.backend) self.backend.runpf() self.backend.assert_grid_correct_after_powerflow() + super().setUp() def tearDown(self): - pass + super().tearDown() def test_theta_ok(self): self.skip_if_needed() @@ -801,6 +822,17 @@ def test_apply_action_disconnection(self): class BaseTestTopoAction(MakeBackend): + def make_backend(self, detailed_infos_for_cascading_failures=False): + return PandaPowerBackend( + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures + ) + + def get_path(self): + return PATH_DATA_TEST + + def get_casefile(self): + return "test_case14.json" + def setUp(self): self.backend = self.make_backend() self.path_matpower = self.get_path() @@ -817,9 +849,10 @@ def setUp(self): gridobj=self.backend, legal_action=self.game_rules.legal_action ) self.bkact_class = _BackendAction.init_grid(self.backend) + super().setUp() def tearDown(self): - pass + super().tearDown() def compare_vect(self, pred, true): return np.max(np.abs(pred - true)) <= self.tolvect @@ -1540,6 +1573,12 @@ class BaseTestEnvPerformsCorrectCascadingFailures(MakeBackend): Test the "next_grid_state" method of the back-end """ + def get_casefile(self): + return "test_case14.json" + + def get_path(self): + return PATH_DATA_TEST + def setUp(self): self.backend = self.make_backend(detailed_infos_for_cascading_failures=True) type(self.backend)._clear_class_attribute() @@ -1587,9 +1626,10 @@ def setUp(self): self.chronics_handler = ChronicsHandler() self.id_first_line_disco = 8 # due to hard overflow self.id_2nd_line_disco = 11 # due to soft overflow + super().setUp() def tearDown(self): - pass + super().tearDown() def next_grid_state_no_overflow(self): # first i test that, when there is no overflow, i dont do a cascading failure @@ -2152,10 +2192,12 @@ def setUp(self): self.env2 = make("rte_case5_example", test=True, backend=backend2) self.backend2 = self.env2.backend np.random.seed(69) + super().setUp() def tearDown(self): self.env1.close() self.env2.close() + super().tearDown() def test_reset_equals_reset(self): self.skip_if_needed() diff --git a/grid2op/tests/helper_path_test.py b/grid2op/tests/helper_path_test.py index 1579ee12c..f1f37c041 100644 --- a/grid2op/tests/helper_path_test.py +++ b/grid2op/tests/helper_path_test.py @@ -12,7 +12,6 @@ # Grid2Op/tests subdirectory import sys import os -import unittest import numpy as np from pathlib import Path @@ -39,11 +38,11 @@ PATH_DATA_MULTIMIX = os.path.abspath(os.path.join(data_test_dir, "multimix")) -class HelperTests(unittest.TestCase): - def __init__(self, methodName="runTest"): - unittest.TestCase.__init__(self, methodName=methodName) +class HelperTests: + def setUp(self): self.tolvect = dt_float(1e-2) self.tol_one = dt_float(1e-5) + super().setUp() def compare_vect(self, pred, true): res = dt_float(np.max(np.abs(pred - true))) <= self.tolvect diff --git a/grid2op/tests/make_backend_test_suite.py b/grid2op/tests/make_backend_test_suite.py new file mode 100644 index 000000000..9655e4147 --- /dev/null +++ b/grid2op/tests/make_backend_test_suite.py @@ -0,0 +1,64 @@ +# Copyright (c) 2019-2020, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import unittest +import re + +from grid2op.tests.BaseBackendTest import (BaseTestNames, BaseTestLoadingCase, BaseTestLoadingBackendFunc, + BaseTestTopoAction, BaseTestEnvPerformsCorrectCascadingFailures, + BaseTestChangeBusAffectRightBus, BaseTestShuntAction, + BaseTestResetEqualsLoadGrid, BaseTestVoltageOWhenDisco, BaseTestChangeBusSlack, + BaseIssuesTest, BaseStatusActions, + BaseTestStorageAction) +from grid2op.tests.test_Environment import (BaseTestLoadingBackendPandaPower, + BaseTestResetOk, + BaseTestResetAfterCascadingFailure, + BaseTestCascadingFailure) +from grid2op.tests.BaseRedispTest import (BaseTestRedispatch, BaseTestRedispatchChangeNothingEnvironment, + BaseTestRedispTooLowHigh, BaseTestDispatchRampingIllegalETC, + BaseTestLoadingAcceptAlmostZeroSumRedisp) + + +# Issue131Tester + +def create_test_suite(make_backend_fun, add_name_cls, *args, tests_skipped=(), **kwargs): + def this_make_backend(self, detailed_infos_for_cascading_failures=False): + return make_backend_fun(self, + *args, + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, + **kwargs) + all_classes = [] + # Does only the last one... + for el in [BaseTestNames, + BaseTestLoadingCase, + BaseTestLoadingBackendFunc, + BaseTestTopoAction, + BaseTestEnvPerformsCorrectCascadingFailures, + BaseTestChangeBusAffectRightBus, + BaseTestShuntAction, + BaseTestResetEqualsLoadGrid, + BaseTestVoltageOWhenDisco, + BaseTestChangeBusSlack, + BaseIssuesTest, + BaseStatusActions, + BaseTestStorageAction, + BaseTestLoadingBackendPandaPower, + BaseTestResetOk, + BaseTestResetAfterCascadingFailure, + BaseTestCascadingFailure, + BaseTestRedispatch, + BaseTestRedispatchChangeNothingEnvironment, + BaseTestRedispTooLowHigh, + BaseTestDispatchRampingIllegalETC, + BaseTestLoadingAcceptAlmostZeroSumRedisp]: + this_cls = type(f"{re.sub('Base', '', el.__name__)}_{add_name_cls}", + (el, unittest.TestCase), + {"make_backend": this_make_backend}) + all_classes.append(this_cls) + + return all_classes diff --git a/grid2op/tests/test_Agent.py b/grid2op/tests/test_Agent.py index ebe33e582..0f7cfa9a7 100644 --- a/grid2op/tests/test_Agent.py +++ b/grid2op/tests/test_Agent.py @@ -9,6 +9,7 @@ import time import warnings import pandapower as pp +import unittest from grid2op.tests.helper_path_test import * @@ -34,7 +35,7 @@ print("pandapower version : {}".format(pp.__version__)) -class TestAgent(HelperTests): +class TestAgent(HelperTests, unittest.TestCase): def setUp(self): """ The case file is a representation of the case14 as found in the ieee14 powergrid. @@ -145,7 +146,7 @@ def test_2_busswitch(self): ), f"The reward has not been properly computed {cum_reward} instead of {expected_reward}" -class TestMake2Agents(HelperTests): +class TestMake2Agents(HelperTests, unittest.TestCase): def test_2random(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -166,7 +167,7 @@ def test_2random(self): env2.close() -class TestSeeding(HelperTests): +class TestSeeding(HelperTests, unittest.TestCase): def test_random(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -193,7 +194,7 @@ def test_random(self): assert np.any(res != res3) -class TestRecoPowerlineAgent(HelperTests): +class TestRecoPowerlineAgent(HelperTests, unittest.TestCase): def test_reco_simple(self): param = Parameters() param.NO_OVERFLOW_DISCONNECTION = True @@ -280,7 +281,7 @@ def test_reco_more_difficult(self): assert ddict4["set_line_status"]["connected_id"][0] == 2 -class TestFromList(HelperTests): +class TestFromList(HelperTests, unittest.TestCase): def test_agentfromlist_empty(self): param = Parameters() param.NO_OVERFLOW_DISCONNECTION = True diff --git a/grid2op/tests/test_AgentsFast.py b/grid2op/tests/test_AgentsFast.py index 0907f6da2..2796dfe6a 100644 --- a/grid2op/tests/test_AgentsFast.py +++ b/grid2op/tests/test_AgentsFast.py @@ -9,16 +9,15 @@ import time import warnings import pandapower as pp +import unittest from grid2op.tests.helper_path_test import * -import grid2op from grid2op.Exceptions import * from grid2op.MakeEnv import make from grid2op.Agent import DoNothingAgent, BaseAgent from grid2op.Parameters import Parameters from grid2op.dtypes import dt_float -from grid2op.Agent import RandomAgent import pdb @@ -37,7 +36,7 @@ def act(self, observation, reward, done=False): return self.action_space.sample() -class TestAgent(HelperTests): +class TestAgent(HelperTests, unittest.TestCase): def setUp(self): """ The case file is a representation of the case14 as found in the ieee14 powergrid. diff --git a/grid2op/tests/test_BackendConverter.py b/grid2op/tests/test_BackendConverter.py index b6811381f..cc4f8b49b 100644 --- a/grid2op/tests/test_BackendConverter.py +++ b/grid2op/tests/test_BackendConverter.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest from grid2op.Converter import BackendConverter from grid2op.tests.helper_path_test import * @@ -39,7 +40,7 @@ warnings.simplefilter("error") -class TestLoading(HelperTests): +class TestLoading(HelperTests, unittest.TestCase): def test_init(self): backend = BackendConverter( source_backend_class=BKclass1, @@ -51,7 +52,7 @@ def test_init(self): env = make("rte_case14_realistic", test=True, backend=backend) -class TestNames(HelperTests, BaseTestNames): +class TestNames(HelperTests, BaseTestNames, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -65,7 +66,7 @@ def get_path(self): return PATH_DATA_TEST_INIT -class TestLoadingCase(HelperTests, BaseTestLoadingCase): +class TestLoadingCase(HelperTests, BaseTestLoadingCase, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -82,7 +83,7 @@ def get_casefile(self): return "test_case14.json" -class TestLoadingBackendFunc(HelperTests, BaseTestLoadingBackendFunc): +class TestLoadingBackendFunc(HelperTests, BaseTestLoadingBackendFunc, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestLoadingBackendFunc.setUp(self) @@ -107,7 +108,7 @@ def get_casefile(self): return "test_case14.json" -class TestTopoAction(HelperTests, BaseTestTopoAction): +class TestTopoAction(HelperTests, BaseTestTopoAction, unittest.TestCase): def setUp(self): BaseTestTopoAction.setUp(self) @@ -132,7 +133,7 @@ def get_casefile(self): class TestEnvPerformsCorrectCascadingFailures( - HelperTests, BaseTestEnvPerformsCorrectCascadingFailures + HelperTests, BaseTestEnvPerformsCorrectCascadingFailures, unittest.TestCase ): def setUp(self): BaseTestEnvPerformsCorrectCascadingFailures.setUp(self) @@ -157,7 +158,7 @@ def get_path(self): return PATH_DATA_TEST -class TestChangeBusAffectRightBus(HelperTests, BaseTestChangeBusAffectRightBus): +class TestChangeBusAffectRightBus(HelperTests, BaseTestChangeBusAffectRightBus, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -168,7 +169,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestShuntAction(HelperTests, BaseTestShuntAction): +class TestShuntAction(HelperTests, BaseTestShuntAction, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -179,7 +180,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestResetEqualsLoadGrid(HelperTests, BaseTestResetEqualsLoadGrid): +class TestResetEqualsLoadGrid(HelperTests, BaseTestResetEqualsLoadGrid, unittest.TestCase): def setUp(self): BaseTestResetEqualsLoadGrid.setUp(self) @@ -193,7 +194,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestVoltageOWhenDisco(HelperTests, BaseTestVoltageOWhenDisco): +class TestVoltageOWhenDisco(HelperTests, BaseTestVoltageOWhenDisco, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -204,7 +205,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestChangeBusSlack(HelperTests, BaseTestChangeBusSlack): +class TestChangeBusSlack(HelperTests, BaseTestChangeBusSlack, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -215,7 +216,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestIssuesTest(HelperTests, BaseIssuesTest): +class TestIssuesTest(HelperTests, BaseIssuesTest, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -226,7 +227,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestStatusAction(HelperTests, BaseStatusActions): +class TestStatusAction(HelperTests, BaseStatusActions, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, diff --git a/grid2op/tests/test_ChronicsHandler.py b/grid2op/tests/test_ChronicsHandler.py index a9349f105..d3220cffd 100644 --- a/grid2op/tests/test_ChronicsHandler.py +++ b/grid2op/tests/test_ChronicsHandler.py @@ -11,6 +11,7 @@ import pandas as pd import tempfile import re +import unittest from grid2op.tests.helper_path_test import * import grid2op @@ -36,7 +37,7 @@ warnings.simplefilter("error") -class TestProperHandlingHazardsMaintenance(HelperTests): +class TestProperHandlingHazardsMaintenance(HelperTests, unittest.TestCase): def setUp(self): self.path_hazard = os.path.join(PATH_CHRONICS, "chronics_with_hazards") self.path_maintenance = os.path.join(PATH_CHRONICS, "chronics_with_maintenance") @@ -314,7 +315,7 @@ def test_loadchornics_maintenance_ok(self): ), "incorrect time for next maintenance on powerline 19 at finish" -class TestLoadingChronicsHandler(HelperTests): +class TestLoadingChronicsHandler(HelperTests, unittest.TestCase): def setUp(self): self.path = os.path.join(PATH_CHRONICS, "chronics") @@ -529,7 +530,7 @@ def test_name_invariant(self): pass -class TestLoadingChronicsHandlerWithForecast(HelperTests): +class TestLoadingChronicsHandlerWithForecast(HelperTests, unittest.TestCase): # Cette méthode sera appelée avant chaque test. def setUp(self): self.path = os.path.join(PATH_CHRONICS, "chronics_with_forecast") @@ -621,7 +622,7 @@ def test_check_validity(self): chron_handl.check_validity(backend) -class TestLoadingChronicsHandlerPP(HelperTests): +class TestLoadingChronicsHandlerPP(HelperTests, unittest.TestCase): # Cette méthode sera appelée avant chaque test. def setUp(self): self.pathfake = os.path.join(PATH_CHRONICS, "chronics") @@ -934,7 +935,7 @@ def test_name_invariant(self): pass -class TestLoadingMultiFolder(HelperTests): +class TestLoadingMultiFolder(HelperTests, unittest.TestCase): def setUp(self): self.path = os.path.join(PATH_CHRONICS, "test_multi_chronics") @@ -1119,7 +1120,7 @@ def test_stopiteration_chunksize(self): pass -class TestEnvChunk(HelperTests): +class TestEnvChunk(HelperTests, unittest.TestCase): def setUp(self): self.max_iter = 10 with warnings.catch_warnings(): @@ -1151,7 +1152,7 @@ def test_normal_chunck(self): assert i == self.max_iter # I used 1 data to intialize the environment -class TestMissingData(HelperTests): +class TestMissingData(HelperTests, unittest.TestCase): def test_load_error(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -1212,7 +1213,7 @@ def test_load_still(self): pass -class TestCFFWFWM(HelperTests): +class TestCFFWFWM(HelperTests, unittest.TestCase): def test_load(self): param = Parameters() param.NO_OVERFLOW_DISCONNECTION = True @@ -1583,7 +1584,7 @@ def test_chunk_size(self): assert np.all(maint == maint2) -class TestWithCache(HelperTests): +class TestWithCache(HelperTests, unittest.TestCase): def test_load(self): param = Parameters() param.NO_OVERFLOW_DISCONNECTION = True @@ -1612,7 +1613,7 @@ def test_load(self): assert env.chronics_handler.real_data.data.curr_iter == 1 -class TestMaintenanceBehavingNormally(HelperTests): +class TestMaintenanceBehavingNormally(HelperTests, unittest.TestCase): def test_withrealistic(self): param = Parameters() param.NO_OVERFLOW_DISCONNECTION = True @@ -1827,7 +1828,7 @@ def test_with_alwayslegal(self): ) -class TestMultiFolder(HelperTests): +class TestMultiFolder(HelperTests, unittest.TestCase): def get_multifolder_class(self): self.res_th = [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0] return Multifolder @@ -2109,7 +2110,7 @@ def _reset_chron_handl(self, chronics_handler): chronics_handler.reset() -class TestDeactivateMaintenance(HelperTests): +class TestDeactivateMaintenance(HelperTests, unittest.TestCase): def test_maintenance_deactivated(self): param = Parameters() param.NO_OVERFLOW_DISCONNECTION = True @@ -2137,7 +2138,7 @@ def test_maintenance_deactivated(self): assert np.all(obs.time_next_maintenance == -1) -class TestMaxIter(HelperTests): +class TestMaxIter(HelperTests, unittest.TestCase): def test_max_iter(self): nb_episode = 2 max_iter = 288*2 diff --git a/grid2op/tests/test_Converter.py b/grid2op/tests/test_Converter.py index 58a6e758c..28a164b00 100644 --- a/grid2op/tests/test_Converter.py +++ b/grid2op/tests/test_Converter.py @@ -9,6 +9,7 @@ import warnings import os import json +import unittest from grid2op.Action import BaseAction, PlayableAction from grid2op.tests.helper_path_test import * @@ -20,10 +21,8 @@ import warnings -warnings.simplefilter("error") - -class TestConnectivityConverter(HelperTests): +class TestConnectivityConverter(HelperTests, unittest.TestCase): def setUp(self): """ The case file is a representation of the case14 as found in the ieee14 powergrid. @@ -361,7 +360,7 @@ def test_bug_in_doc(self): assert abs(disag3 - 4.0 / size_) <= self.tol_one -class TestIdToAct(HelperTests): +class TestIdToAct(HelperTests, unittest.TestCase): def setUp(self): """ The case file is a representation of the case14 as found in the ieee14 powergrid. diff --git a/grid2op/tests/test_Curtailment.py b/grid2op/tests/test_Curtailment.py index 6d5355bb9..e10cb0506 100644 --- a/grid2op/tests/test_Curtailment.py +++ b/grid2op/tests/test_Curtailment.py @@ -11,6 +11,7 @@ import time import warnings +import unittest from grid2op.tests.helper_path_test import * import grid2op @@ -23,7 +24,7 @@ # TODO check when there is also redispatching -class TestCurtailmentEnv(HelperTests): +class TestCurtailmentEnv(HelperTests, unittest.TestCase): """test the env part of the storage functionality""" def setUp(self) -> None: diff --git a/grid2op/tests/test_Environment.py b/grid2op/tests/test_Environment.py index 204c65a85..62d158f24 100644 --- a/grid2op/tests/test_Environment.py +++ b/grid2op/tests/test_Environment.py @@ -11,6 +11,7 @@ import time import warnings +import unittest from grid2op.tests.helper_path_test import * from grid2op.Exceptions import * @@ -30,7 +31,7 @@ import cProfile -class TestLoadingBackendPandaPower(unittest.TestCase): +class BaseTestLoadingBackendPandaPower: def get_backend(self, detailed_infos_for_cascading_failures=True): return PandaPowerBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) @@ -108,9 +109,11 @@ def setUp(self): names_chronics_to_backend=self.names_chronics_to_backend, name="test_env_env1", ) + super().setUp() def tearDown(self): self.env.close() + super().tearDown() def compare_vect(self, pred, true): return dt_float(np.max(np.abs(pred - true))) <= self.tolvect @@ -315,6 +318,10 @@ def test_reward(self): ), "Wrong reward" +class TestLoadingBackendPandaPower(BaseTestLoadingBackendPandaPower, unittest.TestCase): + pass + + class TestIllegalAmbiguous(unittest.TestCase): """ This function test that the behaviour of "step" is the one we want: it does nothing if an action if ambiguous @@ -432,7 +439,7 @@ def test_copy(self): assert obs_after.minute_of_hour == 0 -class TestResetOk(unittest.TestCase): +class BaseTestResetOk(unittest.TestCase): """ This function test that the behaviour of "step" is the one we want: it does nothing if an action if ambiguous or illegal @@ -457,9 +464,11 @@ def setUp(self): backend=self.make_backend(), other_rewards={"test": L2RPNReward}, ) + super().setUp() def tearDown(self): self.env.close() + super().tearDown() def test_reset_after_blackout(self): # make the grid in bad shape @@ -571,6 +580,10 @@ def test_attach(self): } +class TestResetOk(BaseTestResetOk, unittest.TestCase): + pass + + class TestLineChangeLastBus(unittest.TestCase): """ This function test that the behaviour of "step": it updates the action with the last known bus when reconnecting @@ -645,7 +658,7 @@ def test_change_reconnect(self): assert obs.topo_vect[line_ex_topo] == 2, "Line ex should be on bus 2" -class TestResetAfterCascadingFailure(unittest.TestCase): +class BaseTestResetAfterCascadingFailure(unittest.TestCase): """ Fake a cascading failure, do a reset of an env, check that it can be loaded @@ -691,7 +704,11 @@ def test_reset_after_cascading(self): assert d is False -class TestCascadingFailure(unittest.TestCase): +class TestResetAfterCascadingFailure(BaseTestResetAfterCascadingFailure, unittest.TestCase): + pass + + +class BaseTestCascadingFailure(unittest.TestCase): """ There has been a bug preventing to reload an environment if the previous one ended with a cascading failure. It check that here. @@ -762,6 +779,10 @@ def test_simulate_cf(self): assert not done +class TestCascadingFailure(BaseTestCascadingFailure, unittest.TestCase): + pass + + class TestLoading2envDontCrash(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): diff --git a/grid2op/tests/test_ObsPlusAct.py b/grid2op/tests/test_ObsPlusAct.py index 84b0b2061..cae3e2dd7 100644 --- a/grid2op/tests/test_ObsPlusAct.py +++ b/grid2op/tests/test_ObsPlusAct.py @@ -9,6 +9,7 @@ import warnings from grid2op.tests.helper_path_test import * +import unittest import grid2op from grid2op.dtypes import dt_int, dt_float, dt_bool @@ -614,77 +615,77 @@ def test_status_change_status_action(self): ) -class TestCompleteAction(BaseHelper, HelperTests): +class TestCompleteAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return CompleteAction -class TestDispatchAction(BaseHelper, HelperTests): +class TestDispatchAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return DispatchAction -class TestDontAct(BaseHelper, HelperTests): +class TestDontAct(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return DontAct -class TestPlayableAction(BaseHelper, HelperTests): +class TestPlayableAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return PlayableAction -class TestPowerlineChangeAction(BaseHelper, HelperTests): +class TestPowerlineChangeAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return PowerlineChangeAction -class TestPowerlineChangeAndDispatchAction(BaseHelper, HelperTests): +class TestPowerlineChangeAndDispatchAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return PowerlineChangeAndDispatchAction -class TestPowerlineChangeDispatchAndStorageAction(BaseHelper, HelperTests): +class TestPowerlineChangeDispatchAndStorageAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return PowerlineChangeDispatchAndStorageAction -class TestPowerlineSetAction(BaseHelper, HelperTests): +class TestPowerlineSetAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return PowerlineSetAction -class TestPowerlineSetAndDispatchAction(BaseHelper, HelperTests): +class TestPowerlineSetAndDispatchAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return PowerlineSetAndDispatchAction -class TestTopologyAction(BaseHelper, HelperTests): +class TestTopologyAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return TopologyAction -class TestTopologyAndDispatchAction(BaseHelper, HelperTests): +class TestTopologyAndDispatchAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return TopologyAndDispatchAction -class TestTopologyChangeAction(BaseHelper, HelperTests): +class TestTopologyChangeAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return TopologyChangeAction -class TestTopologyChangeAndDispatchAction(BaseHelper, HelperTests): +class TestTopologyChangeAndDispatchAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return TopologyChangeAndDispatchAction -class TestTopologySetAction(BaseHelper, HelperTests): +class TestTopologySetAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return TopologySetAction -class TestTopologySetAndDispatchAction(BaseHelper, HelperTests): +class TestTopologySetAndDispatchAction(BaseHelper, HelperTests, unittest.TestCase): def get_act_cls(self): return TopologySetAndDispatchAction diff --git a/grid2op/tests/test_PandaPowerBackend.py b/grid2op/tests/test_PandaPowerBackend.py index c229129bb..11def46bd 100644 --- a/grid2op/tests/test_PandaPowerBackend.py +++ b/grid2op/tests/test_PandaPowerBackend.py @@ -7,12 +7,12 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import unittest import warnings +import unittest import numpy as np from grid2op import make -from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST from grid2op.Backend import PandaPowerBackend from grid2op.tests.helper_path_test import HelperTests @@ -30,38 +30,26 @@ from grid2op.tests.BaseBackendTest import BaseStatusActions from grid2op.tests.BaseBackendTest import BaseTestStorageAction -PATH_DATA_TEST_INIT = PATH_DATA_TEST -PATH_DATA_TEST = PATH_DATA_TEST_PP - import warnings warnings.simplefilter("error") -class TestNames(HelperTests, BaseTestNames): +class TestNames(BaseTestNames, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) - def get_path(self): - return PATH_DATA_TEST_INIT - -class TestLoadingCase(HelperTests, BaseTestLoadingCase): +class TestLoadingCase(BaseTestLoadingCase, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) - def get_path(self): - return PATH_DATA_TEST - - def get_casefile(self): - return "test_case14.json" - -class TestLoadingBackendFunc(HelperTests, BaseTestLoadingBackendFunc): +class TestLoadingBackendFunc(BaseTestLoadingBackendFunc, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestLoadingBackendFunc.setUp(self) @@ -75,14 +63,8 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) - def get_path(self): - return PATH_DATA_TEST - - def get_casefile(self): - return "test_case14.json" - -class TestTopoAction(HelperTests, BaseTestTopoAction): +class TestTopoAction(BaseTestTopoAction, unittest.TestCase): def setUp(self): BaseTestTopoAction.setUp(self) @@ -90,20 +72,9 @@ def tearDown(self): # TODO find something more elegant BaseTestTopoAction.tearDown(self) - def make_backend(self, detailed_infos_for_cascading_failures=False): - return PandaPowerBackend( - detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures - ) - - def get_path(self): - return PATH_DATA_TEST - - def get_casefile(self): - return "test_case14.json" - class TestEnvPerformsCorrectCascadingFailures( - HelperTests, BaseTestEnvPerformsCorrectCascadingFailures + BaseTestEnvPerformsCorrectCascadingFailures, unittest.TestCase ): def setUp(self): BaseTestEnvPerformsCorrectCascadingFailures.setUp(self) @@ -117,28 +88,22 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) - def get_casefile(self): - return "test_case14.json" - - def get_path(self): - return PATH_DATA_TEST - -class TestChangeBusAffectRightBus(HelperTests, BaseTestChangeBusAffectRightBus): +class TestChangeBusAffectRightBus(BaseTestChangeBusAffectRightBus, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestShuntAction(HelperTests, BaseTestShuntAction): +class TestShuntAction(BaseTestShuntAction, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestResetEqualsLoadGrid(HelperTests, BaseTestResetEqualsLoadGrid): +class TestResetEqualsLoadGrid(BaseTestResetEqualsLoadGrid, unittest.TestCase): def setUp(self): BaseTestResetEqualsLoadGrid.setUp(self) @@ -148,35 +113,35 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): ) -class TestVoltageOWhenDisco(HelperTests, BaseTestVoltageOWhenDisco): +class TestVoltageOWhenDisco(BaseTestVoltageOWhenDisco, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestChangeBusSlack(HelperTests, BaseTestChangeBusSlack): +class TestChangeBusSlack(BaseTestChangeBusSlack, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestIssuesTest(HelperTests, BaseIssuesTest): +class TestIssuesTest(BaseIssuesTest, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestStatusAction(HelperTests, BaseStatusActions): +class TestStatusAction(BaseStatusActions, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestStorageAction(HelperTests, BaseTestStorageAction): +class TestStorageAction(BaseTestStorageAction, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackend( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures diff --git a/grid2op/tests/test_PandaPowerBackendDefaultFunc.py b/grid2op/tests/test_PandaPowerBackendDefaultFunc.py index 847dde51e..18822f404 100644 --- a/grid2op/tests/test_PandaPowerBackendDefaultFunc.py +++ b/grid2op/tests/test_PandaPowerBackendDefaultFunc.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import unittest import warnings +import unittest import numpy as np @@ -132,7 +133,7 @@ def get_topo_vect(self): return res -class TestNames(HelperTests, BaseTestNames): +class TestNames(HelperTests, BaseTestNames, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures @@ -142,7 +143,7 @@ def get_path(self): return PATH_DATA_TEST_INIT -class TestLoadingCase(HelperTests, BaseTestLoadingCase): +class TestLoadingCase(HelperTests, BaseTestLoadingCase, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures @@ -155,7 +156,7 @@ def get_casefile(self): return "test_case14.json" -class TestLoadingBackendFunc(HelperTests, BaseTestLoadingBackendFunc): +class TestLoadingBackendFunc(HelperTests, BaseTestLoadingBackendFunc, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestLoadingBackendFunc.setUp(self) @@ -176,7 +177,7 @@ def get_casefile(self): return "test_case14.json" -class TestTopoAction(HelperTests, BaseTestTopoAction): +class TestTopoAction(HelperTests, BaseTestTopoAction, unittest.TestCase): def setUp(self): BaseTestTopoAction.setUp(self) @@ -197,7 +198,7 @@ def get_casefile(self): class TestEnvPerformsCorrectCascadingFailures( - HelperTests, BaseTestEnvPerformsCorrectCascadingFailures + HelperTests, BaseTestEnvPerformsCorrectCascadingFailures, unittest.TestCase ): def setUp(self): BaseTestEnvPerformsCorrectCascadingFailures.setUp(self) @@ -218,21 +219,21 @@ def get_path(self): return PATH_DATA_TEST -class TestChangeBusAffectRightBus(HelperTests, BaseTestChangeBusAffectRightBus): +class TestChangeBusAffectRightBus(HelperTests, BaseTestChangeBusAffectRightBus, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestShuntAction(HelperTests, BaseTestShuntAction): +class TestShuntAction(HelperTests, BaseTestShuntAction, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestResetEqualsLoadGrid(HelperTests, BaseTestResetEqualsLoadGrid): +class TestResetEqualsLoadGrid(HelperTests, BaseTestResetEqualsLoadGrid, unittest.TestCase): def setUp(self): BaseTestResetEqualsLoadGrid.setUp(self) @@ -242,28 +243,28 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): ) -class TestVoltageOWhenDisco(HelperTests, BaseTestVoltageOWhenDisco): +class TestVoltageOWhenDisco(HelperTests, BaseTestVoltageOWhenDisco, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestChangeBusSlack(HelperTests, BaseTestChangeBusSlack): +class TestChangeBusSlack(HelperTests, BaseTestChangeBusSlack, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestIssuesTest(HelperTests, BaseIssuesTest): +class TestIssuesTest(HelperTests, BaseIssuesTest, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestStatusAction(HelperTests, BaseStatusActions): +class TestStatusAction(HelperTests, BaseStatusActions, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures diff --git a/grid2op/tests/test_RedispatchEnv.py b/grid2op/tests/test_RedispatchEnv.py index 71727eea6..d519b2440 100644 --- a/grid2op/tests/test_RedispatchEnv.py +++ b/grid2op/tests/test_RedispatchEnv.py @@ -30,7 +30,7 @@ warnings.simplefilter("error") -class TestRedispatch(HelperTests, BaseTestRedispatch): +class TestRedispatch(HelperTests, BaseTestRedispatch, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestRedispatch.setUp(self) @@ -52,7 +52,7 @@ def get_casefile(self): class TestRedispatchChangeNothingEnvironment( - HelperTests, BaseTestRedispatchChangeNothingEnvironment + HelperTests, BaseTestRedispatchChangeNothingEnvironment, unittest.TestCase ): def setUp(self): # TODO find something more elegant @@ -74,7 +74,7 @@ def get_casefile(self): return "test_case14.json" -class TestRedispTooLowHigh(HelperTests, BaseTestRedispTooLowHigh): +class TestRedispTooLowHigh(HelperTests, BaseTestRedispTooLowHigh, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestRedispTooLowHigh.setUp(self) @@ -89,7 +89,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): ) -class TestDispatchRampingIllegalETC(HelperTests, BaseTestDispatchRampingIllegalETC): +class TestDispatchRampingIllegalETC(HelperTests, BaseTestDispatchRampingIllegalETC, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestDispatchRampingIllegalETC.setUp(self) @@ -105,7 +105,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): class TestLoadingAcceptAlmostZeroSumRedisp( - HelperTests, BaseTestLoadingAcceptAlmostZeroSumRedisp + HelperTests, BaseTestLoadingAcceptAlmostZeroSumRedisp, unittest.TestCase ): def setUp(self): # TODO find something more elegant diff --git a/grid2op/tests/test_Runner.py b/grid2op/tests/test_Runner.py index 6f5152ba6..8ceb4fd27 100644 --- a/grid2op/tests/test_Runner.py +++ b/grid2op/tests/test_Runner.py @@ -9,6 +9,7 @@ import warnings import tempfile import json +import unittest import pdb from grid2op.tests.helper_path_test import * @@ -41,7 +42,7 @@ def act(self, observation: BaseObservation, reward: float, done: bool = False): return super().act(observation, reward, done) -class TestRunner(HelperTests): +class TestRunner(HelperTests, unittest.TestCase): def setUp(self): self.init_grid_path = os.path.join(PATH_DATA_TEST_PP, "test_case14.json") self.path_chron = PATH_ADN_CHRONICS_FOLDER diff --git a/grid2op/tests/test_RunnerFast.py b/grid2op/tests/test_RunnerFast.py index 1fc8f4551..ccfe5704f 100644 --- a/grid2op/tests/test_RunnerFast.py +++ b/grid2op/tests/test_RunnerFast.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest from grid2op.tests.helper_path_test import * @@ -23,7 +24,7 @@ warnings.simplefilter("error") -class TestRunner(HelperTests): +class TestRunner(HelperTests, unittest.TestCase): def setUp(self): self.init_grid_path = os.path.join(PATH_DATA_TEST_PP, "test_case14.json") self.path_chron = PATH_ADN_CHRONICS_FOLDER diff --git a/grid2op/tests/test_Storage.py b/grid2op/tests/test_Storage.py index 5944983d2..261c12b6c 100644 --- a/grid2op/tests/test_Storage.py +++ b/grid2op/tests/test_Storage.py @@ -10,6 +10,7 @@ import pdb import time import warnings +import unittest from grid2op.tests.helper_path_test import * @@ -23,7 +24,7 @@ # TODO check when there is also redispatching -class TestStorageEnv(HelperTests): +class TestStorageEnv(HelperTests, unittest.TestCase): """test the env part of the storage functionality""" def setUp(self) -> None: diff --git a/grid2op/tests/test_ts_handlers.py b/grid2op/tests/test_ts_handlers.py index e1626457c..24456ee4e 100644 --- a/grid2op/tests/test_ts_handlers.py +++ b/grid2op/tests/test_ts_handlers.py @@ -6,10 +6,8 @@ # 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 copy -import pdb -import time import warnings +import unittest from grid2op.tests.helper_path_test import * @@ -45,7 +43,7 @@ def _load_next_chunk_in_memory_hack(self): self.current_index = 0 -class TestCSVHandlerEnv(HelperTests): +class TestCSVHandlerEnv(HelperTests, unittest.TestCase): """test the env part of the storage functionality""" def _aux_assert_right_type_chronics(self): assert isinstance(self.env1.chronics_handler.real_data.data, GridStateFromFile) diff --git a/grid2op/tests/test_utils.py b/grid2op/tests/test_utils.py index 4eb80763c..f3ae5b3c5 100644 --- a/grid2op/tests/test_utils.py +++ b/grid2op/tests/test_utils.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. from grid2op.tests.helper_path_test import * +import unittest PATH_ADN_CHRONICS_FOLDER = os.path.abspath( os.path.join(PATH_CHRONICS, "test_multi_chronics") @@ -22,10 +23,8 @@ import warnings -warnings.simplefilter("error") - -class TestEpisodeStatistics(HelperTests): +class TestEpisodeStatistics(HelperTests, unittest.TestCase): """test teh grid2op.utils.EpisodeStatistics""" def test_read(self): @@ -128,7 +127,7 @@ def test_compute_without_score(self): ) -class TestL2RPNSCORE(HelperTests): +class TestL2RPNSCORE(HelperTests, unittest.TestCase): """test teh grid2op.utils.EpisodeStatistics""" def test_can_compute(self): @@ -452,7 +451,7 @@ def test_reco_noov_80(self): ) -class TestICAPSSCORE(HelperTests): +class TestICAPSSCORE(HelperTests, unittest.TestCase): """test teh grid2op.utils.EpisodeStatistics""" def test_can_compute(self): From d89e50798b8f0e2d9527d80af5065ee7a4e78d6f Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 9 Oct 2023 17:23:01 +0200 Subject: [PATCH 16/46] trying to adapt the test to make it easier to test easily new backends --- grid2op/tests/BaseBackendTest.py | 403 ++++++++++++++++-- grid2op/tests/BaseRedispTest.py | 35 +- .../tests/aux_test_make_backend_test_suite.py | 19 + grid2op/tests/make_backend_test_suite.py | 34 +- grid2op/tests/test_RedispatchEnv.py | 22 +- 5 files changed, 448 insertions(+), 65 deletions(-) create mode 100644 grid2op/tests/aux_test_make_backend_test_suite.py diff --git a/grid2op/tests/BaseBackendTest.py b/grid2op/tests/BaseBackendTest.py index 51a32797d..552757263 100644 --- a/grid2op/tests/BaseBackendTest.py +++ b/grid2op/tests/BaseBackendTest.py @@ -17,7 +17,7 @@ from abc import ABC, abstractmethod import inspect -from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST +from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST, PATH_DATA PATH_DATA_TEST_INIT = PATH_DATA_TEST PATH_DATA_TEST = PATH_DATA_TEST_PP from grid2op.tests.helper_path_test import HelperTests @@ -63,32 +63,341 @@ def comb(n, k): from grid2op.MakeEnv import make from grid2op.Rules import AlwaysLegal from grid2op.Action._backendAction import _BackendAction +from grid2op.Backend import Backend, PandaPowerBackend import pdb class MakeBackend(ABC, HelperTests): @abstractmethod - def make_backend(self, detailed_infos_for_cascading_failures=False): + def make_backend(self, detailed_infos_for_cascading_failures=False) -> Backend: pass - def get_path(self): + def get_path(self) -> str: raise NotImplementedError( "This function should be implemented for the test suit you are developping" ) - def get_casefile(self): + def get_casefile(self) -> str: raise NotImplementedError( "This function should be implemented for the test suit you are developping" ) - def skip_if_needed(self): + def skip_if_needed(self) -> None: if hasattr(self, "tests_skipped"): nm_ = inspect.currentframe().f_back.f_code.co_name if nm_ in self.tests_skipped: self.skipTest('the test "{}" is skipped'.format(nm_)) +import unittest # TODO REMOVE +class AAATestBackendAPI(HelperTests, unittest.TestCase): + def make_backend(self, detailed_infos_for_cascading_failures=False): + return PandaPowerBackend() # TODO REMOVE + + def get_path(self): + return os.path.join(PATH_DATA, "educ_case14_storage") + + def get_casefile(self): + return "grid.json" + + def aux_get_env_name(self): + """do not run nor modify ! (used for this test class only)""" + return "BasicTest_load_grid_" + type(self).__name__ + def aux_make_backend(self): + """do not run nor modify ! (used for this test class only)""" + backend = self.make_backend() + backend.load_grid(self.get_path(), self.get_casefile()) + backend.load_redispacthing_data(self.get_path()) + backend.load_storage_data(self.get_path()) + env_name = self.aux_get_env_name() + backend.env_name = env_name + backend.assert_grid_correct() + return backend + + def test_00create_backend(self): + """test the backend can be created (not integrated in a grid2op environment yet)""" + backend = self.make_backend() + + def test_01load_grid(self): + """test the grid can be loaded (supposes that your backend can read the grid.json in educ_case14_storage)""" + backend = self.make_backend() + backend.load_grid(self.get_path(), self.get_casefile()) # both argument filled + backend.load_redispacthing_data(self.get_path()) + backend.load_storage_data(self.get_path()) + env_name = "BasicTest_load_grid0_" + type(self).__name__ + backend.env_name = env_name + backend.assert_grid_correct() + cls = type(backend) + assert cls.n_line == 20, f"there should be 20 lines / trafos on the grid, found {cls.n_line} (remember trafo are conted grid2op side as powerline)" + assert cls.n_gen == 6, f"there should be 6 generators on the grid found {cls.n_gen} (remember a generator is added to the slack if none are present)" + assert cls.n_load == 11, f"there should be 11 loads on the grid, found {cls.n_load}" + assert cls.n_sub == 14, f"there should be 14 substations on this grid, found {cls.n_sub}" + if cls.shunts_data_available: + assert cls.n_shunt == 1, f"there should be 1 shunt on the grid, found {cls.n_shunt}" + if cls.n_storage > 0: + assert cls.n_storage == 2, f"there should be 2 storage units on this grid, found {cls.n_storage}" + assert env_name in cls.env_name, f"you probably should not have overidden the assert_grid_correct function !" + backend.close() + + backend = self.make_backend() + backend.load_grid(os.path.join(self.get_path(), self.get_casefile())) # first argument filled, second None + backend.load_redispacthing_data(self.get_path()) + backend.load_storage_data(self.get_path()) + backend.env_name = "BasicTest_load_grid2_" + type(self).__name__ + backend.assert_grid_correct() + backend.close() + + backend = self.make_backend() + with self.assertRaises(Exception): + backend.load_grid() # should raise if nothing is loaded + + def test_02modify_load(self): + """test the loads can be modified + + NB it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + # try to modify load_p + action = type(backend)._complete_action_class() + action.update({"injection": {"load_p": 1.01 * init_load_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of load_p only + + # try to modify load_q + action = type(backend)._complete_action_class() + action.update({"injection": {"load_q": 1.01 * init_load_q}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of load_q only + + def test_03modify_gen(self): + """test the generators (including slack !) can be modified + + NB it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + # try to modify gen_p + action = type(backend)._complete_action_class() + action.update({"injection": {"prod_p": 1.01 * init_gen_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of prod_p / gen_p only + + # try to modify prod_v only + action = type(backend)._complete_action_class() + action.update({"injection": {"prod_v": 1.01 * init_gen_v}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of prod_v / gen_v only + + def test_04disco_reco_lines(self): + """test the powerlines can be disconnected and connected + + NB it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + + line_id = 0 + # try to disconnect line 0 + action = type(backend)._complete_action_class() + action.update({"set_line_status": [(line_id, -1)]}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # disconnection of line 0 only + + # try to reconnect line 0 + action = type(backend)._complete_action_class() + action.update({"set_line_status": [(line_id, +1)]}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # reconnection of line 0 only + + def test_05change_topology(self): + """try to change the topology of 2 different substations : connect their elements to different busbars + + NB it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + + sub_id = 0 + # everything on busbar 2 at sub 0 (should have no impact) + action = type(backend)._complete_action_class() + action.update({"set_bus": {"substations_id": [(sub_id, [2 for _ in range(type(backend).sub_info[sub_id])])]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # everything on busbar 2 at sub 0 + + sub_id = 1 + # mix of bus 1 and 2 on substation 1 + action = type(backend)._complete_action_class() + action.update({"set_bus": {"substations_id": [(sub_id, [i % 2 + 1 for i in range(type(backend).sub_info[sub_id])])]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + + def test_06modify_shunt(self): + """test the shunt can be modified (p, q and topology) + + NB it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + cls = type(backend) + if not cls.shunts_data_available: + self.skipTest("Your backend does not support shunts") + + init_shunt_p = np.array([0.0]) + init_shunt_q = np.array([-19.]) + init_shunt_bus = np.array([1]) + + # try to modify shunt_p + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_p": init_shunt_p + 0.01}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of shunt_p only + + # try to modify shunt_q only + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_q": init_shunt_q * 1.01}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of shunt_q only + + # try to modify shunt_bus only + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_bus": init_shunt_bus + 1}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of shunt_bus only + + def test_07modify_storage(self): + """test the modification of storage unit (active power) + + NB it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + cls = type(backend) + if cls.n_storage == 0: + self.skipTest("Your backend does not support storage units") + + storage_power = np.array([-0.5, +0.5]) + # try to modify storage active power only + action = type(backend)._complete_action_class() + action.update({"set_storage": storage_power}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # try to modify storage active power only + + def test_08run_ac_pf(self): + """test the runpf method (AC) without modification""" + backend = self.aux_make_backend() + + res = backend.runpf(is_dc=False) + assert len(res) == 2, "runpf should return tuple of size 2" + converged, exc_ = res + if converged: + assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" + else: + warnings.warn("It is surprising that your backend diverges without any modification (AC)") + assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" + + def test_09run_dc_pf(self): + """test the runpf method (DC) without modification""" + backend = self.aux_make_backend() + + res = backend.runpf(is_dc=True) + assert len(res) == 2, "runpf should return tuple of size 2" + converged, exc_ = res + if converged: + assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" + else: + warnings.warn("It is surprising that your backend diverges without any modification (DC)") + assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" + + def test_10_ac_forced_divergence(self): + """increase the load / generation until the powerflow diverges, and check the flags are properly returned""" + backend = self.aux_make_backend() + + res = backend.runpf(is_dc=False) + assert len(res) == 2, "runpf should return tuple of size 2" + + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + gen_p = 1. * init_gen_p + load_p = 1. * init_load_p + nb_iter = 0 + while True: + gen_p *= 1.5 + load_p *= 1.5 + action = type(backend)._complete_action_class() + action.update({"injection": {"prod_p": gen_p, + "load_p": load_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + res = backend.runpf(is_dc=False) + converged, exc_ = res + if converged: + assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" + else: + assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" + break + nb_iter += 1 + if nb_iter >= 10: + raise RuntimeError("It is surprising that your backend still converges when the load / generation are multiplied by " + "something like 50 (1.5**10). I suppose it's an error. " + "It should stop in approx 3 iteration (so when multiplied by 1.5**3)") + + def test_11_modify_load_pf_getter(self): + """test that the modification has an impact on the backend (by reading back the states)""" + backend = self.aux_make_backend() + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + res = backend.runpf(is_dc=False) + tmp = backend.loads_info() + assert len(tmp) == 3, "loads_info() should return 3 elements: load_p, load_q, load_v (see doc)" + load_p_init, load_q_init, load_v_init = tmp + + # try to modify load_p + action = type(backend)._complete_action_class() + action.update({"injection": {"load_p": 1.01 * init_load_p, + "gen_p": 1.01 * init_gen_p, + "load_q": 1.01 * init_load_q}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of load_p, load_q and gen_p + + res2 = backend.runpf(is_dc=False) + tmp2 = backend.loads_info() + assert len(tmp) == 3, "loads_info() should return 3 elements: load_p, load_q, load_v (see doc)" + load_p_after, load_q_after, load_v_after = tmp2 + assert not np.allclose(load_p_after, load_p_init), f"load_p does not seemed to be modified by apply_action when loads are impacted (active value)" + assert not np.allclose(load_q_after, load_q_init), f"load_p does not seemed to be modified by apply_action when loads are impacted (reactive value)" + import pdb + pdb.set_trace() + + # TODO accessor after modification + # TODO kirchoff law's + # TODO divergence (AC and DC) when an element: generator, load, shunt, storage is alone + class BaseTestNames(MakeBackend): def get_path(self): return PATH_DATA_TEST_INIT @@ -103,7 +412,7 @@ def test_properNames(self): with make( os.path.join(path, "5bus_example_diff_name"), backend=backend, - _add_to_name="_BaseTestNames", + _add_to_name=type(self).__name__ ) as env: obs = env.reset() assert np.all(type(obs).name_load == ["tutu", "toto", "tata"]) @@ -243,11 +552,11 @@ def setUp(self): type(self.backend).set_no_storage() self.backend.assert_grid_correct() self.game_rules = RulesChecker() - self.action_env_class = ActionSpace.init_grid(self.backend) + self.action_env_class = ActionSpace.init_grid(self.backend, extra_name=type(self).__name__) self.action_env = self.action_env_class( gridobj=self.backend, legal_action=self.game_rules.legal_action ) - self.bkact_class = _BackendAction.init_grid(self.backend) + self.bkact_class = _BackendAction.init_grid(self.backend, extra_name=type(self).__name__) self.backend.runpf() self.backend.assert_grid_correct_after_powerflow() super().setUp() @@ -844,11 +1153,11 @@ def setUp(self): type(self.backend).set_no_storage() self.backend.assert_grid_correct() self.game_rules = RulesChecker() - as_class = ActionSpace.init_grid(self.backend) + as_class = ActionSpace.init_grid(self.backend, extra_name=type(self).__name__) self.helper_action = as_class( gridobj=self.backend, legal_action=self.game_rules.legal_action ) - self.bkact_class = _BackendAction.init_grid(self.backend) + self.bkact_class = _BackendAction.init_grid(self.backend, extra_name=type(self).__name__) super().setUp() def tearDown(self): @@ -1444,13 +1753,13 @@ def test_get_action_to_set_storage(self): "educ_case14_storage", test=True, backend=self.make_backend(), - _add_to_name="test_gats_storage", + _add_to_name=type(self).__name__ ) env2 = make( "educ_case14_storage", test=True, backend=self.make_backend(), - _add_to_name="test_gats_storage", + _add_to_name=type(self).__name__ ) obs, *_ = env.step(env.action_space({"set_storage": [-1.0, 1.0]})) act = env.backend.get_action_to_set() @@ -1475,7 +1784,7 @@ def test_update_from_obs(self): "rte_case14_realistic", test=True, backend=self.make_backend(), - _add_to_name="test_update_from_obs", + _add_to_name=type(self).__name__ ) self.backend.close() @@ -1642,7 +1951,8 @@ def next_grid_state_no_overflow(self): init_env_path=os.path.join(self.path_matpower, self.case_file), chronics_handler=self.chronics_handler, parameters=self.env_params, - name="test_pp_env1", + name="test_pp_env1" + type(self).__name__, + ) disco, infos, conv_ = self.backend.next_grid_state(env, is_dc=False) @@ -1663,7 +1973,7 @@ def test_next_grid_state_1overflow(self): backend=self.backend, chronics_handler=self.chronics_handler, parameters=env_params, - name="test_pp_env2", + name="test_pp_env2" + type(self).__name__, ) with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -1698,7 +2008,7 @@ def test_next_grid_state_1overflow_envNoCF(self): init_env_path=os.path.join(self.path_matpower, case_file), chronics_handler=self.chronics_handler, parameters=self.env_params, - name="test_pp_env3", + name="test_pp_env3" + type(self).__name__, ) with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -1742,7 +2052,7 @@ def test_nb_timestep_overflow_disc0(self): init_env_path=os.path.join(self.path_matpower, case_file), chronics_handler=self.chronics_handler, parameters=env_params, - name="test_pp_env4", + name="test_pp_env4" + type(self).__name__, ) with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -1787,7 +2097,7 @@ def test_nb_timestep_overflow_nodisc(self): chronics_handler=self.chronics_handler, init_env_path=os.path.join(self.path_matpower, case_file), parameters=env_params, - name="test_pp_env5", + name="test_pp_env5" + type(self).__name__, ) with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -1829,7 +2139,7 @@ def test_nb_timestep_overflow_nodisc_2(self): chronics_handler=self.chronics_handler, init_env_path=os.path.join(self.path_matpower, case_file), parameters=env_params, - name="test_pp_env6", + name="test_pp_env6" + type(self).__name__, ) with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -1872,7 +2182,7 @@ def test_nb_timestep_overflow_disc2(self): chronics_handler=self.chronics_handler, init_env_path=os.path.join(self.path_matpower, case_file), parameters=env_params, - name="test_pp_env7", + name="test_pp_env7" + type(self).__name__, ) with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -1909,7 +2219,8 @@ def test_set_bus(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True, backend=backend) + env = make("rte_case14_realistic", test=True, backend=backend, + _add_to_name=type(self).__name__) env.reset() action = env.action_space({"set_bus": {"lines_or_id": [(17, 2)]}}) obs, reward, done, info = env.step(action) @@ -1924,7 +2235,8 @@ def test_change_bus(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True, backend=backend) + env = make("rte_case14_realistic", test=True, backend=backend, + _add_to_name=type(self).__name__) env.reset() action = env.action_space({"change_bus": {"lines_or_id": [17]}}) obs, reward, done, info = env.step(action) @@ -1938,7 +2250,8 @@ def test_change_bustwice(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True, backend=backend) + env = make("rte_case14_realistic", test=True, backend=backend, + _add_to_name=type(self).__name__) env.reset() action = env.action_space({"change_bus": {"lines_or_id": [17]}}) obs, reward, done, info = env.step(action) @@ -1959,7 +2272,8 @@ def test_isolate_load(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True, backend=backend) + env = make("rte_case14_realistic", test=True, backend=backend, + _add_to_name=type(self).__name__) act = env.action_space({"set_bus": {"loads_id": [(0, 2)]}}) obs, reward, done, info = env.step(act) assert done, "an isolated load has not lead to a game over" @@ -1976,6 +2290,7 @@ def test_reco_disco_bus(self): test=True, gamerules_class=AlwaysLegal, backend=backend, + _add_to_name=type(self).__name__ ) obs = env_case1.reset() # reset is good act = env_case1.action_space.disconnect_powerline( @@ -2002,6 +2317,7 @@ def test_reco_disco_bus2(self): test=True, gamerules_class=AlwaysLegal, backend=backend, + _add_to_name=type(self).__name__ ) obs = env_case2.reset() # reset is good obs, reward, done, info = env_case2.step( @@ -2028,6 +2344,7 @@ def test_reco_disco_bus3(self): test=True, gamerules_class=AlwaysLegal, backend=backend, + _add_to_name=type(self).__name__ ) obs = env_case2.reset() # reset is good obs, reward, done, info = env_case2.step( @@ -2052,6 +2369,7 @@ def test_reco_disco_bus4(self): test=True, gamerules_class=AlwaysLegal, backend=backend, + _add_to_name=type(self).__name__ ) obs = env_case2.reset() # reset is good obs, reward, done, info = env_case2.step( @@ -2076,6 +2394,7 @@ def test_reco_disco_bus5(self): test=True, gamerules_class=AlwaysLegal, backend=backend, + _add_to_name=type(self).__name__ ) obs = env_case2.reset() # reset is good act_case2 = env_case2.action_space( @@ -2099,6 +2418,7 @@ def test_shunt_ambiguous_id_incorrect(self): gamerules_class=AlwaysLegal, action_class=CompleteAction, backend=backend, + _add_to_name=type(self).__name__ ) as env_case2: with self.assertRaises(AmbiguousAction): act = env_case2.action_space({"shunt": {"set_bus": [(0, 2)]}}) @@ -2116,7 +2436,7 @@ def test_shunt_effect(self): gamerules_class=AlwaysLegal, action_class=CompleteAction, backend=backend1, - _add_to_name="BaseTestShuntAction", + _add_to_name=type(self).__name__ ) env_change_q = make( "rte_case14_realistic", @@ -2124,7 +2444,7 @@ def test_shunt_effect(self): gamerules_class=AlwaysLegal, action_class=CompleteAction, backend=backend2, - _add_to_name="BaseTestShuntAction", + _add_to_name=type(self).__name__ ) param = env_ref.parameters param.NO_OVERFLOW_DISCONNECTION = True @@ -2187,9 +2507,9 @@ def setUp(self): type(backend1)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = make("rte_case5_example", test=True, backend=backend1) + self.env1 = make("rte_case5_example", test=True, backend=backend1, _add_to_name=type(self).__name__) self.backend1 = self.env1.backend - self.env2 = make("rte_case5_example", test=True, backend=backend2) + self.env2 = make("rte_case5_example", test=True, backend=backend2, _add_to_name=type(self).__name__) self.backend2 = self.env2.backend np.random.seed(69) super().setUp() @@ -2324,7 +2644,7 @@ def test_combined_changes(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "rte_case14_realistic", test=True, backend=backend, param=params + "rte_case14_realistic", test=True, backend=backend, param=params, _add_to_name=type(self).__name__ ) # Find N valid iadd combination of R change actions @@ -2392,7 +2712,7 @@ def test_this(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_realistic", test=True, backend=backend) as env: + with make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) as env: line_id = 1 act = env.action_space({"set_line_status": [(line_id, -1)]}) obs, *_ = env.step(act) @@ -2408,7 +2728,7 @@ def test_change_slack_case14(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("rte_case14_realistic", test=True, backend=backend) + env = grid2op.make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) action = env.action_space( { "set_bus": { @@ -2455,7 +2775,7 @@ def test_there_are_storage(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("educ_case14_storage", test=True, backend=backend) + self.env = grid2op.make("educ_case14_storage", test=True, backend=backend, _add_to_name=type(self).__name__) assert self.env.n_storage == 2 def test_storage_action_mw(self): @@ -2465,7 +2785,7 @@ def test_storage_action_mw(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("educ_case14_storage", test=True, backend=backend) + self.env = grid2op.make("educ_case14_storage", test=True, backend=backend, _add_to_name=type(self).__name__) array_modif = np.array([-1.5, -10.0], dtype=dt_float) act = self.env.action_space({"set_storage": array_modif}) @@ -2542,6 +2862,7 @@ def test_storage_action_topo(self): backend=backend, param=param, action_class=CompleteAction, + _add_to_name=type(self).__name__ ) # test i can do a reset @@ -2684,7 +3005,7 @@ def test_issue_125(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("rte_case14_realistic", test=True, backend=backend) + env = grid2op.make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) action = env.action_space({"set_bus": {"loads_id": [(1, -1)]}}) obs, reward, am_i_done, info = env.step(action) assert info["is_illegal"] is False @@ -2712,7 +3033,8 @@ def test_issue_134(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "rte_case14_realistic", test=True, backend=backend, param=param + "rte_case14_realistic", test=True, backend=backend, param=param, + _add_to_name=type(self).__name__ ) obs_init = env.get_obs() LINE_ID = 2 @@ -2790,7 +3112,8 @@ def test_issue_134_check_ambiguity(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "rte_case14_realistic", test=True, backend=backend, param=param + "rte_case14_realistic", test=True, backend=backend, param=param, + _add_to_name=type(self).__name__ ) LINE_ID = 2 @@ -2818,7 +3141,8 @@ def test_issue_134_withcooldown_forrules(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "rte_case14_realistic", test=True, backend=backend, param=param + "rte_case14_realistic", test=True, backend=backend, param=param, + _add_to_name=type(self).__name__ ) LINE_ID = 2 @@ -2953,7 +3277,7 @@ def test_issue_copyenv(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env1 = grid2op.make("rte_case14_realistic", test=True, backend=backend) + env1 = grid2op.make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) env2 = env1.copy() obs1 = env1.reset() obs2 = env2.get_obs() @@ -2971,7 +3295,8 @@ def _make_my_env(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "rte_case14_realistic", test=True, backend=backend, param=param + "rte_case14_realistic", test=True, backend=backend, param=param, + _add_to_name=type(self).__name__ ) return env diff --git a/grid2op/tests/BaseRedispTest.py b/grid2op/tests/BaseRedispTest.py index 2bf292a31..1a47edaa6 100644 --- a/grid2op/tests/BaseRedispTest.py +++ b/grid2op/tests/BaseRedispTest.py @@ -23,7 +23,15 @@ class BaseTestRedispatch(MakeBackend): + + def get_path(self): + return PATH_DATA_TEST_PP + + def get_casefile(self): + return "test_case14.json" + def setUp(self): + super().setUp() # powergrid self.backend = self.make_backend() self.path_matpower = self.get_path() @@ -104,6 +112,7 @@ def setUp(self): def tearDown(self): self.env.close() + super().tearDown() def test_negative_dispatch(self): self.skip_if_needed() @@ -355,7 +364,15 @@ def test_redispacth_non_dispatchable_generator(self): class BaseTestRedispatchChangeNothingEnvironment(MakeBackend): + + def get_path(self): + return PATH_DATA_TEST_PP + + def get_casefile(self): + return "test_case14.json" + def setUp(self): + super().setUp() # powergrid self.backend = self.make_backend() self.path_matpower = self.get_path() @@ -432,6 +449,7 @@ def setUp(self): def tearDown(self): self.env.close() + super().tearDown() def test_redispatch_generator_off(self): """Redispatch a turned off generator is illegal""" @@ -457,10 +475,14 @@ def test_redispatch_generator_off(self): class BaseTestRedispTooLowHigh(MakeBackend): # test bug reported in issues https://github.com/rte-france/Grid2Op/issues/44 def setUp(self) -> None: + super().setUp() backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_redisp", test=True, backend=backend) + self.env = make("rte_case14_redisp", + test=True, + backend=backend, + _add_to_name=type(self).__name__) # i don't want to be bother by ramps in these test (note that is NOT recommended to change that) type(self.env).gen_max_ramp_down[:] = 5000 @@ -477,6 +499,7 @@ def setUp(self) -> None: def tearDown(self): self.env.close() + super().tearDown() def test_redisp_toohigh_toolow(self): """ @@ -549,15 +572,18 @@ def test_error_message_notzerosum_threesteps(self): class BaseTestDispatchRampingIllegalETC(MakeBackend): def setUp(self): + super().setUp() # powergrid backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_test", test=True, backend=backend) + self.env = make("rte_case14_test", test=True, backend=backend, + _add_to_name=type(self).__name__) self.tol_one = self.env._tol_poly def tearDown(self): self.env.close() + super().tearDown() def test_invalid_dispatch(self): self.skip_if_needed() @@ -814,15 +840,18 @@ def test_dispatch_still_not_zero(self): class BaseTestLoadingAcceptAlmostZeroSumRedisp(MakeBackend): def setUp(self): + super().setUp() # powergrid backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_test", test=True, backend=backend) + self.env = make("rte_case14_test", test=True, backend=backend, + _add_to_name=type(self).__name__) self.tol_one = self.env._tol_poly def tearDown(self): self.env.close() + super().tearDown() def test_accept_almost_zerozum_too_high(self): self.skip_if_needed() diff --git a/grid2op/tests/aux_test_make_backend_test_suite.py b/grid2op/tests/aux_test_make_backend_test_suite.py new file mode 100644 index 000000000..0787ae0f5 --- /dev/null +++ b/grid2op/tests/aux_test_make_backend_test_suite.py @@ -0,0 +1,19 @@ +import unittest +from make_backend_test_suite import create_test_suite +from grid2op.Backend import PandaPowerBackend +import sys + +def this_make_backend(self, detailed_infos_for_cascading_failures=False): + return PandaPowerBackend( + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures + ) +add_name_cls = "test_functionality" + +res = create_test_suite(make_backend_fun=this_make_backend, + add_name_cls=add_name_cls, + add_to_module=__name__, + extended_test=False) + +if __name__ == "__main__": + unittest.main() + \ No newline at end of file diff --git a/grid2op/tests/make_backend_test_suite.py b/grid2op/tests/make_backend_test_suite.py index 9655e4147..890306f37 100644 --- a/grid2op/tests/make_backend_test_suite.py +++ b/grid2op/tests/make_backend_test_suite.py @@ -8,8 +8,10 @@ import unittest import re +import sys -from grid2op.tests.BaseBackendTest import (BaseTestNames, BaseTestLoadingCase, BaseTestLoadingBackendFunc, +from grid2op.tests.BaseBackendTest import (AAATestBackendAPI, + BaseTestNames, BaseTestLoadingCase, BaseTestLoadingBackendFunc, BaseTestTopoAction, BaseTestEnvPerformsCorrectCascadingFailures, BaseTestChangeBusAffectRightBus, BaseTestShuntAction, BaseTestResetEqualsLoadGrid, BaseTestVoltageOWhenDisco, BaseTestChangeBusSlack, @@ -26,13 +28,36 @@ # Issue131Tester -def create_test_suite(make_backend_fun, add_name_cls, *args, tests_skipped=(), **kwargs): +def make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes): + this_cls = type(f"{re.sub('Base', '', el.__name__)}_{add_name_cls}", + (el, unittest.TestCase), + {"make_backend": this_make_backend}) + if add_to_module is not None: + # make the created class visible to the default module + setattr(sys.modules[add_to_module], + this_cls.__name__, + this_cls, + ) + all_classes.append(this_cls) + + +def create_test_suite(make_backend_fun, + add_name_cls, + *args, + add_to_module=None, + extended_test=True, + tests_skipped=(), + **kwargs): def this_make_backend(self, detailed_infos_for_cascading_failures=False): return make_backend_fun(self, *args, detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, **kwargs) all_classes = [] + make_and_add_cls(AAATestBackendAPI, add_name_cls, this_make_backend, add_to_module, all_classes) + if not extended_test: + return + # Does only the last one... for el in [BaseTestNames, BaseTestLoadingCase, @@ -56,9 +81,6 @@ def this_make_backend(self, detailed_infos_for_cascading_failures=False): BaseTestRedispTooLowHigh, BaseTestDispatchRampingIllegalETC, BaseTestLoadingAcceptAlmostZeroSumRedisp]: - this_cls = type(f"{re.sub('Base', '', el.__name__)}_{add_name_cls}", - (el, unittest.TestCase), - {"make_backend": this_make_backend}) - all_classes.append(this_cls) + make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes) return all_classes diff --git a/grid2op/tests/test_RedispatchEnv.py b/grid2op/tests/test_RedispatchEnv.py index d519b2440..191d8f645 100644 --- a/grid2op/tests/test_RedispatchEnv.py +++ b/grid2op/tests/test_RedispatchEnv.py @@ -30,7 +30,7 @@ warnings.simplefilter("error") -class TestRedispatch(HelperTests, BaseTestRedispatch, unittest.TestCase): +class TestRedispatch(BaseTestRedispatch, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestRedispatch.setUp(self) @@ -44,15 +44,9 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) - def get_path(self): - return PATH_DATA_TEST_PP - - def get_casefile(self): - return "test_case14.json" - class TestRedispatchChangeNothingEnvironment( - HelperTests, BaseTestRedispatchChangeNothingEnvironment, unittest.TestCase + BaseTestRedispatchChangeNothingEnvironment, unittest.TestCase ): def setUp(self): # TODO find something more elegant @@ -67,14 +61,8 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) - def get_path(self): - return PATH_DATA_TEST_PP - - def get_casefile(self): - return "test_case14.json" - -class TestRedispTooLowHigh(HelperTests, BaseTestRedispTooLowHigh, unittest.TestCase): +class TestRedispTooLowHigh(BaseTestRedispTooLowHigh, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestRedispTooLowHigh.setUp(self) @@ -89,7 +77,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): ) -class TestDispatchRampingIllegalETC(HelperTests, BaseTestDispatchRampingIllegalETC, unittest.TestCase): +class TestDispatchRampingIllegalETC(BaseTestDispatchRampingIllegalETC, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestDispatchRampingIllegalETC.setUp(self) @@ -105,7 +93,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): class TestLoadingAcceptAlmostZeroSumRedisp( - HelperTests, BaseTestLoadingAcceptAlmostZeroSumRedisp, unittest.TestCase + BaseTestLoadingAcceptAlmostZeroSumRedisp, unittest.TestCase ): def setUp(self): # TODO find something more elegant From 100c6daac38148f8d5d804a84298c83662d3b448 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 12 Oct 2023 09:18:37 +0200 Subject: [PATCH 17/46] fixing broken tests after refacto --- CHANGELOG.rst | 7 + grid2op/Action/baseAction.py | 4 +- grid2op/Backend/pandaPowerBackend.py | 60 +- grid2op/__init__.py | 9 +- grid2op/tests/BaseBackendTest.py | 312 +---- grid2op/tests/aaa_test_backend_interface.py | 1062 +++++++++++++++++ .../tests/aux_test_make_backend_test_suite.py | 19 - grid2op/tests/make_backend_test_suite.py | 86 -- grid2op/tests/test_ActionProperties.py | 1 + grid2op/tests/test_Agent.py | 2 + grid2op/tests/test_AgentConverter.py | 10 +- grid2op/tests/test_AgentsFast.py | 6 +- grid2op/tests/test_BackendConverter.py | 28 +- grid2op/tests/test_ChronicsHandler.py | 16 +- grid2op/tests/test_Converter.py | 4 + grid2op/tests/test_EnvironmentCpy.py | 4 +- grid2op/tests/test_EpisodeData.py | 3 +- grid2op/tests/test_GymConverter.py | 6 +- grid2op/tests/test_MultiMix.py | 2 +- grid2op/tests/test_MultiProcess.py | 1 + grid2op/tests/test_Observation.py | 1 + .../test_ObservationHazard_Maintenance.py | 1 + grid2op/tests/test_Opponent.py | 3 +- .../test_PandaPowerBackendDefaultFunc.py | 29 +- grid2op/tests/test_Rules.py | 2 + grid2op/tests/test_RulesByArea.py | 2 + grid2op/tests/test_Runner.py | 1 + grid2op/tests/test_RunnerFast.py | 1 + grid2op/tests/test_Storage.py | 6 +- grid2op/tests/test_VoltageControler.py | 5 +- grid2op/tests/test_alert_gym_compat.py | 2 + grid2op/tests/test_basicBackendInterface.py | 41 + grid2op/tests/test_issue_217.py | 1 + grid2op/tests/test_issue_223.py | 1 + grid2op/tests/test_issue_224.py | 1 + grid2op/tests/test_issue_235.py | 2 +- grid2op/tests/test_issue_238.py | 2 +- grid2op/tests/test_issue_245.py | 1 + 38 files changed, 1229 insertions(+), 515 deletions(-) create mode 100644 grid2op/tests/aaa_test_backend_interface.py delete mode 100644 grid2op/tests/aux_test_make_backend_test_suite.py delete mode 100644 grid2op/tests/make_backend_test_suite.py create mode 100644 grid2op/tests/test_basicBackendInterface.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d9ade0e5a..e7eb5c8a4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -42,7 +42,14 @@ Change Log exception (they should lead to a divergence) - [FIXED] some wrong behaviour in the `remove_line_status_from_topo` when no observation where provided and `check_cooldown` is `False` +- [FIXED] a bug in PandaPowerBackend in AC powerflow: disconnected storage unit had no 0. as voltage +- [FIXED] a bug in PandaPowerBackend in AC powerflow when a generator was alone a bus it made the powerflow + crash on some cases (*eg* without lightsim2grid, without numba) +- [FIXED] a bug in PandaPowerBackend in DC (in some cases non connected grid were not spotted) - [ADDED] now depends on the `typing_extensions` package +- [ADDED] a complete test suite to help people develop new backend using "Test Driven Programming" + techniques +- [IMPROVED] now easier than ever to run the grid2op test suite with a new backend (for relevant tests) - [IMPROVED] type hints for `Backend` and `PandapowerBackend` [1.9.5] - 2023-09-18 diff --git a/grid2op/Action/baseAction.py b/grid2op/Action/baseAction.py index 5ee081a4e..9fa1d6601 100644 --- a/grid2op/Action/baseAction.py +++ b/grid2op/Action/baseAction.py @@ -1879,7 +1879,7 @@ def _digest_setbus(self, dict_): msg += ( ' at least one of "loads_id", "generators_id", "lines_or_id", ' ) - msg += '"lines_ex_id" or "substations_id"' + msg += '"lines_ex_id" or "substations_id" or "storages_id"' msg += " as keys. None where found. Current used keys are: " msg += "{}".format(sorted(ddict_.keys())) raise AmbiguousAction(msg) @@ -1922,7 +1922,7 @@ def _digest_change_bus(self, dict_): msg += ( ' at least one of "loads_id", "generators_id", "lines_or_id", ' ) - msg += '"lines_ex_id" or "substations_id"' + msg += '"lines_ex_id" or "substations_id" or "storages_id"' msg += " as keys. None where found. Current used keys are: " msg += "{}".format(sorted(ddict_.keys())) raise AmbiguousAction(msg) diff --git a/grid2op/Backend/pandaPowerBackend.py b/grid2op/Backend/pandaPowerBackend.py index 7de151f1b..fdbadd4d7 100644 --- a/grid2op/Backend/pandaPowerBackend.py +++ b/grid2op/Backend/pandaPowerBackend.py @@ -99,12 +99,12 @@ class PandaPowerBackend(Backend): .. code-block:: python - import grid2op - from grid2op.Backend import PandaPowerBackend - backend = PandaPowerBackend() + import grid2op + from grid2op.Backend import PandaPowerBackend + backend = PandaPowerBackend() - env = grid2op.make(backend=backend) - # and use "env" as any open ai gym environment. + env = grid2op.make(backend=backend) + # and use "env" as "any open ai gym" environment. """ @@ -546,7 +546,9 @@ def load_grid(self, add_topo = copy.deepcopy(self._grid.bus) add_topo.index += add_topo.shape[0] add_topo["in_service"] = False - self._grid.bus = pd.concat((self._grid.bus, add_topo)) + # self._grid.bus = pd.concat((self._grid.bus, add_topo)) + for ind, el in add_topo.iterrows(): + pp.create_bus(self._grid, index=ind, **el) self._init_private_attrs() @@ -1017,22 +1019,30 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: " produces 0. instead. Please check generators: " f"{np.where(~self._grid.gen['in_service'])[0]}" ) - - if is_dc: - pp.rundcpp(self._grid, check_connectivity=False, init="flat") - # if dc i start normally next time i call an ac powerflow - self._nb_bus_before = None - else: - pp.runpp( - self._grid, - check_connectivity=False, - init=self._pf_init, - numba=self.with_numba, - lightsim2grid=self._lightsim2grid, - max_iteration=self._max_iter, - distributed_slack=self._dist_slack, - ) - + try: + if is_dc: + pp.rundcpp(self._grid, check_connectivity=True, init="flat") + # if I put check_connectivity=False then the test AAATestBackendAPI.test_22_islanded_grid_make_divergence + # does not pass + + # if dc i start normally next time i call an ac powerflow + self._nb_bus_before = None + else: + pp.runpp( + self._grid, + check_connectivity=False, + init=self._pf_init, + numba=self.with_numba, + lightsim2grid=self._lightsim2grid, + max_iteration=self._max_iter, + distributed_slack=self._dist_slack, + ) + except IndexError as exc_: + raise pp.powerflow.LoadflowNotConverged(f"Surprising behaviour of pandapower when a bus is not connected to " + f"anything but present on the bus (with check_connectivity=False). " + f"Error was {exc_}" + ) + # stores the computation time if "_ppc" in self._grid: if "et" in self._grid["_ppc"]: @@ -1040,7 +1050,8 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: if self._grid.res_gen.isnull().values.any(): # TODO see if there is a better way here -> do not handle this here, but rather in Backend._next_grid_state # sometimes pandapower does not detect divergence and put Nan. - raise pp.powerflow.LoadflowNotConverged("Divergence due to Nan values in res_gen table.") + raise pp.powerflow.LoadflowNotConverged("Divergence due to Nan values in res_gen table (most likely due to " + "a non connected grid).") # if a connected bus has a no voltage, it's a divergence (grid was not connected) if self._grid.res_bus.loc[self._grid.bus["in_service"]]["va_degree"].isnull().any(): @@ -1501,7 +1512,7 @@ def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: .values.astype(dt_float) ) shunt_bus = type(self).global_bus_to_local(self._grid.shunt["bus"].values, self.shunt_to_subid) - shunt_v[~self._grid.shunt["in_service"].values] = 0 + shunt_v[~self._grid.shunt["in_service"].values] = 0. shunt_bus[~self._grid.shunt["in_service"].values] = -1 return shunt_p, shunt_q, shunt_v, shunt_bus @@ -1530,6 +1541,7 @@ def _storages_info(self): ].values.astype(dt_float) * self.storage_pu_to_kv ) + v_storage[~self._grid.storage["in_service"].values] = 0. else: p_storage = np.zeros(shape=0, dtype=dt_float) q_storage = np.zeros(shape=0, dtype=dt_float) diff --git a/grid2op/__init__.py b/grid2op/__init__.py index 102463cfb..d73bb017d 100644 --- a/grid2op/__init__.py +++ b/grid2op/__init__.py @@ -52,4 +52,11 @@ get_current_local_dir, change_local_dir, list_available_test_env - ) \ No newline at end of file + ) + +try: + from grid2op._create_test_suite import create_test_suite + __all__.append(create_test_suite) +except ImportError as exc_: + # grid2op is most likely not installed in editable mode from source + pass \ No newline at end of file diff --git a/grid2op/tests/BaseBackendTest.py b/grid2op/tests/BaseBackendTest.py index 552757263..70bf3c8f6 100644 --- a/grid2op/tests/BaseBackendTest.py +++ b/grid2op/tests/BaseBackendTest.py @@ -17,7 +17,7 @@ from abc import ABC, abstractmethod import inspect -from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST, PATH_DATA +from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST PATH_DATA_TEST_INIT = PATH_DATA_TEST PATH_DATA_TEST = PATH_DATA_TEST_PP from grid2op.tests.helper_path_test import HelperTests @@ -88,315 +88,7 @@ def skip_if_needed(self) -> None: nm_ = inspect.currentframe().f_back.f_code.co_name if nm_ in self.tests_skipped: self.skipTest('the test "{}" is skipped'.format(nm_)) - -import unittest # TODO REMOVE -class AAATestBackendAPI(HelperTests, unittest.TestCase): - def make_backend(self, detailed_infos_for_cascading_failures=False): - return PandaPowerBackend() # TODO REMOVE - - def get_path(self): - return os.path.join(PATH_DATA, "educ_case14_storage") - - def get_casefile(self): - return "grid.json" - - def aux_get_env_name(self): - """do not run nor modify ! (used for this test class only)""" - return "BasicTest_load_grid_" + type(self).__name__ - - def aux_make_backend(self): - """do not run nor modify ! (used for this test class only)""" - backend = self.make_backend() - backend.load_grid(self.get_path(), self.get_casefile()) - backend.load_redispacthing_data(self.get_path()) - backend.load_storage_data(self.get_path()) - env_name = self.aux_get_env_name() - backend.env_name = env_name - backend.assert_grid_correct() - return backend - - def test_00create_backend(self): - """test the backend can be created (not integrated in a grid2op environment yet)""" - backend = self.make_backend() - - def test_01load_grid(self): - """test the grid can be loaded (supposes that your backend can read the grid.json in educ_case14_storage)""" - backend = self.make_backend() - backend.load_grid(self.get_path(), self.get_casefile()) # both argument filled - backend.load_redispacthing_data(self.get_path()) - backend.load_storage_data(self.get_path()) - env_name = "BasicTest_load_grid0_" + type(self).__name__ - backend.env_name = env_name - backend.assert_grid_correct() - cls = type(backend) - assert cls.n_line == 20, f"there should be 20 lines / trafos on the grid, found {cls.n_line} (remember trafo are conted grid2op side as powerline)" - assert cls.n_gen == 6, f"there should be 6 generators on the grid found {cls.n_gen} (remember a generator is added to the slack if none are present)" - assert cls.n_load == 11, f"there should be 11 loads on the grid, found {cls.n_load}" - assert cls.n_sub == 14, f"there should be 14 substations on this grid, found {cls.n_sub}" - if cls.shunts_data_available: - assert cls.n_shunt == 1, f"there should be 1 shunt on the grid, found {cls.n_shunt}" - if cls.n_storage > 0: - assert cls.n_storage == 2, f"there should be 2 storage units on this grid, found {cls.n_storage}" - assert env_name in cls.env_name, f"you probably should not have overidden the assert_grid_correct function !" - backend.close() - - backend = self.make_backend() - backend.load_grid(os.path.join(self.get_path(), self.get_casefile())) # first argument filled, second None - backend.load_redispacthing_data(self.get_path()) - backend.load_storage_data(self.get_path()) - backend.env_name = "BasicTest_load_grid2_" + type(self).__name__ - backend.assert_grid_correct() - backend.close() - - backend = self.make_backend() - with self.assertRaises(Exception): - backend.load_grid() # should raise if nothing is loaded - - def test_02modify_load(self): - """test the loads can be modified - - NB it does not check whether or not the modification is - consistent with the input. This will be done in a later test""" - backend = self.aux_make_backend() - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) - - # try to modify load_p - action = type(backend)._complete_action_class() - action.update({"injection": {"load_p": 1.01 * init_load_p}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # modification of load_p only - - # try to modify load_q - action = type(backend)._complete_action_class() - action.update({"injection": {"load_q": 1.01 * init_load_q}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # modification of load_q only - - def test_03modify_gen(self): - """test the generators (including slack !) can be modified - - NB it does not check whether or not the modification is - consistent with the input. This will be done in a later test""" - backend = self.aux_make_backend() - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) - - # try to modify gen_p - action = type(backend)._complete_action_class() - action.update({"injection": {"prod_p": 1.01 * init_gen_p}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # modification of prod_p / gen_p only - - # try to modify prod_v only - action = type(backend)._complete_action_class() - action.update({"injection": {"prod_v": 1.01 * init_gen_v}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # modification of prod_v / gen_v only - - def test_04disco_reco_lines(self): - """test the powerlines can be disconnected and connected - - NB it does not check whether or not the modification is - consistent with the input. This will be done in a later test""" - backend = self.aux_make_backend() - - line_id = 0 - # try to disconnect line 0 - action = type(backend)._complete_action_class() - action.update({"set_line_status": [(line_id, -1)]}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # disconnection of line 0 only - - # try to reconnect line 0 - action = type(backend)._complete_action_class() - action.update({"set_line_status": [(line_id, +1)]}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # reconnection of line 0 only - - def test_05change_topology(self): - """try to change the topology of 2 different substations : connect their elements to different busbars - - NB it does not check whether or not the modification is - consistent with the input. This will be done in a later test""" - backend = self.aux_make_backend() - - sub_id = 0 - # everything on busbar 2 at sub 0 (should have no impact) - action = type(backend)._complete_action_class() - action.update({"set_bus": {"substations_id": [(sub_id, [2 for _ in range(type(backend).sub_info[sub_id])])]}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # everything on busbar 2 at sub 0 - - sub_id = 1 - # mix of bus 1 and 2 on substation 1 - action = type(backend)._complete_action_class() - action.update({"set_bus": {"substations_id": [(sub_id, [i % 2 + 1 for i in range(type(backend).sub_info[sub_id])])]}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 - - def test_06modify_shunt(self): - """test the shunt can be modified (p, q and topology) - - NB it does not check whether or not the modification is - consistent with the input. This will be done in a later test""" - backend = self.aux_make_backend() - cls = type(backend) - if not cls.shunts_data_available: - self.skipTest("Your backend does not support shunts") - - init_shunt_p = np.array([0.0]) - init_shunt_q = np.array([-19.]) - init_shunt_bus = np.array([1]) - - # try to modify shunt_p - action = type(backend)._complete_action_class() - action.update({"shunt": {"shunt_p": init_shunt_p + 0.01}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # modification of shunt_p only - - # try to modify shunt_q only - action = type(backend)._complete_action_class() - action.update({"shunt": {"shunt_q": init_shunt_q * 1.01}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # modification of shunt_q only - - # try to modify shunt_bus only - action = type(backend)._complete_action_class() - action.update({"shunt": {"shunt_bus": init_shunt_bus + 1}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # modification of shunt_bus only - - def test_07modify_storage(self): - """test the modification of storage unit (active power) - - NB it does not check whether or not the modification is - consistent with the input. This will be done in a later test""" - backend = self.aux_make_backend() - cls = type(backend) - if cls.n_storage == 0: - self.skipTest("Your backend does not support storage units") - - storage_power = np.array([-0.5, +0.5]) - # try to modify storage active power only - action = type(backend)._complete_action_class() - action.update({"set_storage": storage_power}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # try to modify storage active power only - - def test_08run_ac_pf(self): - """test the runpf method (AC) without modification""" - backend = self.aux_make_backend() - - res = backend.runpf(is_dc=False) - assert len(res) == 2, "runpf should return tuple of size 2" - converged, exc_ = res - if converged: - assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" - else: - warnings.warn("It is surprising that your backend diverges without any modification (AC)") - assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" - - def test_09run_dc_pf(self): - """test the runpf method (DC) without modification""" - backend = self.aux_make_backend() - - res = backend.runpf(is_dc=True) - assert len(res) == 2, "runpf should return tuple of size 2" - converged, exc_ = res - if converged: - assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" - else: - warnings.warn("It is surprising that your backend diverges without any modification (DC)") - assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" - - def test_10_ac_forced_divergence(self): - """increase the load / generation until the powerflow diverges, and check the flags are properly returned""" - backend = self.aux_make_backend() - - res = backend.runpf(is_dc=False) - assert len(res) == 2, "runpf should return tuple of size 2" - - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) - - gen_p = 1. * init_gen_p - load_p = 1. * init_load_p - nb_iter = 0 - while True: - gen_p *= 1.5 - load_p *= 1.5 - action = type(backend)._complete_action_class() - action.update({"injection": {"prod_p": gen_p, - "load_p": load_p}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) - res = backend.runpf(is_dc=False) - converged, exc_ = res - if converged: - assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" - else: - assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" - break - nb_iter += 1 - if nb_iter >= 10: - raise RuntimeError("It is surprising that your backend still converges when the load / generation are multiplied by " - "something like 50 (1.5**10). I suppose it's an error. " - "It should stop in approx 3 iteration (so when multiplied by 1.5**3)") - - def test_11_modify_load_pf_getter(self): - """test that the modification has an impact on the backend (by reading back the states)""" - backend = self.aux_make_backend() - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) - - res = backend.runpf(is_dc=False) - tmp = backend.loads_info() - assert len(tmp) == 3, "loads_info() should return 3 elements: load_p, load_q, load_v (see doc)" - load_p_init, load_q_init, load_v_init = tmp - - # try to modify load_p - action = type(backend)._complete_action_class() - action.update({"injection": {"load_p": 1.01 * init_load_p, - "gen_p": 1.01 * init_gen_p, - "load_q": 1.01 * init_load_q}}) - bk_act = type(backend).my_bk_act_class() - bk_act += action - backend.apply_action(bk_act) # modification of load_p, load_q and gen_p - - res2 = backend.runpf(is_dc=False) - tmp2 = backend.loads_info() - assert len(tmp) == 3, "loads_info() should return 3 elements: load_p, load_q, load_v (see doc)" - load_p_after, load_q_after, load_v_after = tmp2 - assert not np.allclose(load_p_after, load_p_init), f"load_p does not seemed to be modified by apply_action when loads are impacted (active value)" - assert not np.allclose(load_q_after, load_q_init), f"load_p does not seemed to be modified by apply_action when loads are impacted (reactive value)" - import pdb - pdb.set_trace() - - # TODO accessor after modification - # TODO kirchoff law's - # TODO divergence (AC and DC) when an element: generator, load, shunt, storage is alone + class BaseTestNames(MakeBackend): def get_path(self): diff --git a/grid2op/tests/aaa_test_backend_interface.py b/grid2op/tests/aaa_test_backend_interface.py new file mode 100644 index 000000000..5ec2c77f6 --- /dev/null +++ b/grid2op/tests/aaa_test_backend_interface.py @@ -0,0 +1,1062 @@ +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import os +import numpy as np +import warnings +from grid2op.tests.helper_path_test import HelperTests, PATH_DATA +from grid2op.Exceptions import BackendError + + +class AAATestBackendAPI(HelperTests): + # def make_backend(self, detailed_infos_for_cascading_failures=False): + # return PandaPowerBackend() # TODO REMOVE + # # from lightsim2grid import LightSimBackend + # # return LightSimBackend() # TODO REMOVE + + def get_path(self): + return os.path.join(PATH_DATA, "educ_case14_storage") + + def get_casefile(self): + return "grid.json" + + def aux_get_env_name(self): + """do not run nor modify ! (used for this test class only)""" + return "BasicTest_load_grid_" + type(self).__name__ + + def aux_make_backend(self): + """do not run nor modify ! (used for this test class only)""" + backend = self.make_backend() + backend.load_grid(self.get_path(), self.get_casefile()) + backend.load_redispacthing_data(self.get_path()) + backend.load_storage_data(self.get_path()) + env_name = self.aux_get_env_name() + backend.env_name = env_name + backend.assert_grid_correct() + return backend + + def test_00create_backend(self): + """Tests the backend can be created (not integrated in a grid2op environment yet)""" + backend = self.make_backend() + + def test_01load_grid(self): + """Tests the grid can be loaded (supposes that your backend can read the grid.json in educ_case14_storage)* + + This test supposes that : + + - backend.load_grid(...) is implemented + """ + backend = self.make_backend() + backend.load_grid(self.get_path(), self.get_casefile()) # both argument filled + backend.load_redispacthing_data(self.get_path()) + backend.load_storage_data(self.get_path()) + env_name = "BasicTest_load_grid0_" + type(self).__name__ + backend.env_name = env_name + backend.assert_grid_correct() + cls = type(backend) + assert cls.n_line == 20, f"there should be 20 lines / trafos on the grid, found {cls.n_line} (remember trafo are conted grid2op side as powerline)" + assert cls.n_gen == 6, f"there should be 6 generators on the grid found {cls.n_gen} (remember a generator is added to the slack if none are present)" + assert cls.n_load == 11, f"there should be 11 loads on the grid, found {cls.n_load}" + assert cls.n_sub == 14, f"there should be 14 substations on this grid, found {cls.n_sub}" + if cls.shunts_data_available: + assert cls.n_shunt == 1, f"there should be 1 shunt on the grid, found {cls.n_shunt}" + if cls.n_storage > 0: + assert cls.n_storage == 2, f"there should be 2 storage units on this grid, found {cls.n_storage}" + assert env_name in cls.env_name, f"you probably should not have overidden the assert_grid_correct function !" + backend.close() + + backend = self.make_backend() + backend.load_grid(os.path.join(self.get_path(), self.get_casefile())) # first argument filled, second None + backend.load_redispacthing_data(self.get_path()) + backend.load_storage_data(self.get_path()) + backend.env_name = "BasicTest_load_grid2_" + type(self).__name__ + backend.assert_grid_correct() + backend.close() + + backend = self.make_backend() + with self.assertRaises(Exception): + backend.load_grid() # should raise if nothing is loaded + + def test_02modify_load(self): + """Tests the loads can be modified + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.apply_action(...) for modification of loads is implemented + + NB: it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + # try to modify load_p + action = type(backend)._complete_action_class() + action.update({"injection": {"load_p": 1.01 * init_load_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of load_p only + + # try to modify load_q + action = type(backend)._complete_action_class() + action.update({"injection": {"load_q": 1.01 * init_load_q}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of load_q only + + def test_03modify_gen(self): + """Tests the generators (including slack !) can be modified + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.apply_action(...) for modification of generators is implemented + + NB: it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + # try to modify gen_p + action = type(backend)._complete_action_class() + action.update({"injection": {"prod_p": 1.01 * init_gen_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of prod_p / gen_p only + + # try to modify prod_v only + action = type(backend)._complete_action_class() + action.update({"injection": {"prod_v": 1.01 * init_gen_v}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of prod_v / gen_v only + + def test_04disco_reco_lines(self): + """Tests the powerlines can be disconnected and connected + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.apply_action(...) for connection / reconnection of powerline is implemented + + NB: it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + + line_id = 0 + # try to disconnect line 0 + action = type(backend)._complete_action_class() + action.update({"set_line_status": [(line_id, -1)]}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # disconnection of line 0 only + + # try to reconnect line 0 + action = type(backend)._complete_action_class() + action.update({"set_line_status": [(line_id, +1)]}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # reconnection of line 0 only + + def test_05change_topology(self): + """try to change the topology of 2 different substations : connect their elements to different busbars + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.apply_action(...) for topology (change of busbars) is implemented + + NB: it does not check whether or not the modification is + consistent with the input. This will be done in a later test""" + backend = self.aux_make_backend() + + sub_id = 0 + # everything on busbar 2 at sub 0 (should have no impact) + action = type(backend)._complete_action_class() + action.update({"set_bus": {"substations_id": [(sub_id, [2 for _ in range(type(backend).sub_info[sub_id])])]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # everything on busbar 2 at sub 0 + + sub_id = 1 + # mix of bus 1 and 2 on substation 1 + action = type(backend)._complete_action_class() + action.update({"set_bus": {"substations_id": [(sub_id, [i % 2 + 1 for i in range(type(backend).sub_info[sub_id])])]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + + def test_06modify_shunt(self): + """Tests the shunt can be modified (p, q and topology) + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.apply_action(...) for shunts is implemented + + NB: this test is skipped if your backend does not support (yet :-) ) shunts + + NB: it does not check whether or not the modification is + consistent with the input. This will be done in a later test + """ + backend = self.aux_make_backend() + cls = type(backend) + if not cls.shunts_data_available: + self.skipTest("Your backend does not support shunts") + + init_shunt_p = np.array([0.0]) + init_shunt_q = np.array([-19.]) + init_shunt_bus = np.array([1]) + + # try to modify shunt_p + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_p": init_shunt_p + 0.01}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of shunt_p only + + # try to modify shunt_q only + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_q": init_shunt_q * 1.01}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of shunt_q only + + # try to modify shunt_bus only + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_bus": init_shunt_bus + 1}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of shunt_bus only + + def test_07modify_storage(self): + """Tests the modification of storage unit (active power) + + NB it does not check whether or not the modification is + consistent with the input. This will be done in a later test + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.apply_action(...) for storage units is implemented + + NB: this test is skipped if your backend does not support (yet :-) ) storage units + + """ + backend = self.aux_make_backend() + cls = type(backend) + if cls.n_storage == 0: + self.skipTest("Your backend does not support storage units") + + storage_power = np.array([-0.5, +0.5]) + # try to modify storage active power only + action = type(backend)._complete_action_class() + action.update({"set_storage": storage_power}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # try to modify storage active power only + + def test_08run_ac_pf(self): + """Tests the runpf method (AC) without modification + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (DC mode) is implemented + + """ + backend = self.aux_make_backend() + + res = backend.runpf(is_dc=False) + assert len(res) == 2, "runpf should return tuple of size 2" + converged, exc_ = res + if converged: + assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" + else: + warnings.warn("It is surprising that your backend diverges without any modification (AC)") + assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" + + def test_09run_dc_pf(self): + """Tests the runpf method (DC) without modification + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (DC mode) is implemented + """ + backend = self.aux_make_backend() + + res = backend.runpf(is_dc=True) + assert len(res) == 2, "runpf should return tuple of size 2" + converged, exc_ = res + if converged: + assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" + else: + warnings.warn("It is surprising that your backend diverges without any modification (DC)") + assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" + + def test_10_ac_forced_divergence(self): + """increase the load / generation until the powerflow diverges, and check the flags are properly returned + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC mode) is implemented + - backend.apply_action() for generator and load is implemented + """ + backend = self.aux_make_backend() + + res = backend.runpf(is_dc=False) + assert len(res) == 2, "runpf should return tuple of size 2" + + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + gen_p = 1. * init_gen_p + load_p = 1. * init_load_p + nb_iter = 0 + while True: + gen_p *= 1.5 + load_p *= 1.5 + action = type(backend)._complete_action_class() + action.update({"injection": {"prod_p": gen_p, + "load_p": load_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + res = backend.runpf(is_dc=False) + converged, exc_ = res + if converged: + assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" + else: + assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" + break + nb_iter += 1 + if nb_iter >= 10: + raise RuntimeError("It is surprising that your backend still converges when the load / generation are multiplied by " + "something like 50 (1.5**10). I suppose it's an error. " + "It should stop in approx 3 iteration (so when multiplied by 1.5**3)") + + def test_11_modify_load_pf_getter(self): + """Tests that the modification of loads has an impact on the backend (by reading back the states) + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC mode) is implemented + - backend.apply_action() for generator and load is implemented + - backend.loads_info() is implemented + """ + + backend = self.aux_make_backend() + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + res = backend.runpf(is_dc=False) + tmp = backend.loads_info() + assert len(tmp) == 3, "loads_info() should return 3 elements: load_p, load_q, load_v (see doc)" + load_p_init, load_q_init, load_v_init = tmp + + # try to modify load_p + action = type(backend)._complete_action_class() + action.update({"injection": {"load_p": 1.01 * init_load_p, + "prod_p": 1.01 * init_gen_p, + "load_q": 1.01 * init_load_q}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of load_p, load_q and gen_p + + res2 = backend.runpf(is_dc=False) + tmp2 = backend.loads_info() + assert len(tmp) == 3, "loads_info() should return 3 elements: load_p, load_q, load_v (see doc)" + load_p_after, load_q_after, load_v_after = tmp2 + assert not np.allclose(load_p_after, load_p_init), f"load_p does not seemed to be modified by apply_action when loads are impacted (active value): check `apply_action` for load_p" + assert not np.allclose(load_q_after, load_q_init), f"load_q does not seemed to be modified by apply_action when loads are impacted (reactive value): check `apply_action` for load_q" + + def test_12_modify_gen_pf_getter(self): + """Tests that the modification of generators has an impact on the backend (by reading back the states) + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC mode) is implemented + - backend.apply_action() for generator and load is implemented + - backend.generators_info() is implemented + """ + backend = self.aux_make_backend() + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + + res = backend.runpf(is_dc=False) + tmp = backend.generators_info() + assert len(tmp) == 3, "generators_info() should return 3 elements: gen_p, gen_q, gen_v (see doc)" + gen_p_init, gen_q_init, gen_v_init = tmp + + # try to modify load_p + action = type(backend)._complete_action_class() + action.update({"injection": {"load_p": 1.01 * init_load_p, + "prod_p": 1.01 * init_gen_p, + "prod_v": init_gen_v + 0.1}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of load_p, load_q and gen_p + + res2 = backend.runpf(is_dc=False) + tmp2 = backend.generators_info() + assert len(tmp) == 3, "generators_info() should return 3 elements: gen_p, gen_q, gen_v (see doc)" + gen_p_after, gen_q_after, gen_v_after = tmp2 + assert not np.allclose(gen_p_after, gen_p_init), f"gen_p does not seemed to be modified by apply_action when generators are impacted (active value): check `apply_action` for gen_p / prod_p" + assert not np.allclose(gen_v_after, gen_v_init), f"load_p does not seemed to be modified by apply_action when loads are impacted (reactive value): check `apply_action` for gen_v / prod_v" + + def test_13_disco_reco_lines_pf_getter(self): + """Tests the powerlines can be disconnected and connected and that getter info are consistent + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC mode) is implemented + - backend.apply_action() for line reconnection / disconnection + - backend.generators_info(), loads_info(), storages_info(), shunts_info(), lines_or_info(), lines_ex_info() is implemented + - backend.get_topo_vect() is implemented + + It is expected that this test fails if there are shunts (or storage units) in + the grid (modeled by your powerflow) but that you did not yet coded the interface + between said element and grid2op (the backend you are creating) + """ + backend = self.aux_make_backend() + cls = type(backend) + + res = backend.runpf(is_dc=False) + tmp_or = backend.lines_or_info() + assert len(tmp_or) == 4, "lines_or_info() should return 4 elements: p, q, v, a (see doc)" + p_or, q_or, v_or, a_or = tmp_or + for arr, arr_nm in zip([p_or, q_or, v_or, a_or], + ["p_or", "q_or", "v_or", "a_or"]): + if arr.shape[0] != cls.n_line: + raise RuntimeError(f"{arr_nm} should have size {cls.n_line} (number of lines) but has size {arr.shape[0]}") + tmp_ex = backend.lines_ex_info() + assert len(tmp_ex) == 4, "lines_ex_info() should return 4 elements: p, q, v, a (see doc)" + p_ex, q_ex, v_ex, a_ex = tmp_ex + for arr, arr_nm in zip([p_ex, q_ex, v_ex, a_ex], + ["p_ex", "q_ex", "v_ex", "a_ex"]): + if arr.shape[0] != cls.n_line: + raise RuntimeError(f"{arr_nm} should have size {cls.n_line} (number of lines) but has size {arr.shape[0]}") + + line_id = 0 + + # try to disconnect line 0 + action1 = type(backend)._complete_action_class() + action1.update({"set_line_status": [(line_id, -1)]}) + bk_act = type(backend).my_bk_act_class() + bk_act += action1 + backend.apply_action(bk_act) # disconnection of line 0 only + res_disco = backend.runpf(is_dc=False) + assert res_disco[0], f"your backend diverge after disconnection of line {line_id}, which should not be the case" + tmp_or_disco = backend.lines_or_info() + tmp_ex_disco = backend.lines_ex_info() + assert not np.allclose(tmp_or_disco[0], p_or), f"p_or does not seemed to be modified by apply_action when a powerline is disconnected (active value): check `apply_action` for line connection disconnection" + assert not np.allclose(tmp_or_disco[1], p_or), f"q_or does not seemed to be modified by apply_action when a powerline is disconnected (active value): check `apply_action` for line connection disconnection" + assert not np.allclose(tmp_ex_disco[0], p_ex), f"p_ex does not seemed to be modified by apply_action when a powerline is disconnected (active value): check `apply_action` for line connection disconnection" + assert not np.allclose(tmp_ex_disco[1], p_ex), f"q_ex does not seemed to be modified by apply_action when a powerline is disconnected (active value): check `apply_action` for line connection disconnection" + assert np.allclose(tmp_or_disco[0][line_id], 0.), f"origin flow (active) on disconnected line {line_id} is > 0." + assert np.allclose(tmp_or_disco[1][line_id], 0.), f"origin flow (reactive) on disconnected line {line_id} is > 0." + assert np.allclose(tmp_or_disco[2][line_id], 0.), f"origin voltage on disconnected line {line_id} is > 0." + assert np.allclose(tmp_or_disco[3][line_id], 0.), f"origin flow (amps) on disconnected line {line_id} is > 0." + assert np.allclose(tmp_ex_disco[0][line_id], 0.), f"extremity flow (active) on disconnected line {line_id} is > 0." + assert np.allclose(tmp_ex_disco[1][line_id], 0.), f"extremity flow (reactive) on disconnected line {line_id} is > 0." + assert np.allclose(tmp_ex_disco[2][line_id], 0.), f"extremity voltage on disconnected line {line_id} is > 0." + assert np.allclose(tmp_ex_disco[3][line_id], 0.), f"extremity flow (amps) on disconnected line {line_id} is > 0." + + # try to reconnect line 0 + action2 = type(backend)._complete_action_class() + action2.update({"set_line_status": [(line_id, +1)]}) + bk_act = type(backend).my_bk_act_class() + bk_act += action1 + bk_act += action2 + backend.apply_action(bk_act) # disconnection of line 0 only + res_disco = backend.runpf(is_dc=False) + assert res_disco[0], f"your backend diverge after disconnection of line {line_id}, which should not be the case" + tmp_or_reco = backend.lines_or_info() + tmp_ex_reco = backend.lines_ex_info() + assert not np.allclose(tmp_or_disco[0], tmp_or_reco[0]), f"p_or does not seemed to be modified by apply_action when a powerline is reconnected (active value): check `apply_action` for line connection reconnection" + assert not np.allclose(tmp_or_disco[1], tmp_or_reco[1]), f"q_or does not seemed to be modified by apply_action when a powerline is reconnected (active value): check `apply_action` for line connection reconnection" + assert not np.allclose(tmp_ex_disco[0], tmp_ex_reco[0]), f"p_ex does not seemed to be modified by apply_action when a powerline is reconnected (active value): check `apply_action` for line connection reconnection" + assert not np.allclose(tmp_ex_disco[1], tmp_ex_reco[0]), f"q_ex does not seemed to be modified by apply_action when a powerline is reconnected (active value): check `apply_action` for line connection reconnection" + assert not np.allclose(tmp_or_reco[0][line_id], 0.), f"origin flow (active) on connected line {line_id} is 0." + assert not np.allclose(tmp_or_reco[1][line_id], 0.), f"origin flow (reactive) on connected line {line_id} is 0." + assert not np.allclose(tmp_or_reco[2][line_id], 0.), f"origin voltage on connected line {line_id} is > 0." + assert not np.allclose(tmp_or_reco[3][line_id], 0.), f"origin flow (amps) on connected line {line_id} is > 0." + assert not np.allclose(tmp_ex_reco[0][line_id], 0.), f"extremity flow (active) on connected line {line_id} is > 0." + assert not np.allclose(tmp_ex_reco[1][line_id], 0.), f"extremity flow (reactive) on connected line {line_id} is > 0." + assert not np.allclose(tmp_ex_reco[2][line_id], 0.), f"extremity voltage on connected line {line_id} is > 0." + assert not np.allclose(tmp_ex_reco[3][line_id], 0.), f"extremity flow (amps) on connected line {line_id} is > 0." + + def test_14change_topology(self): + """try to change the topology of 2 different substations : connect their elements to different busbars and check consistency + + The same action as for test test_05change_topology. + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC mode) is implemented + - backend.apply_action() for topology modification + - backend.generators_info(), loads_info(), storages_info(), shunts_info(), lines_or_info(), lines_ex_info() is implemented + - backend.get_topo_vect() is implemented + + It is expected that this test fails if there are shunts (or storage units) in + the grid (modeled by your powerflow) but that you did not yet coded the interface + between said element and grid2op (the backend you are creating) + """ + backend = self.aux_make_backend() + cls = type(backend) + + res = backend.runpf(is_dc=False) + p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() + assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for p (creation or suppression of active). Check the handling of the slack bus(se) maybe ?" + assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for q (creation or suppression of reactive). Check the handling of the slack bus(se) maybe ?" + assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for p (creation or suppression of active). Check the handling of the slack bus(se) maybe ?" + assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for q (creation or suppression of reactive). Check the handling of the slack bus(se) maybe ?" + assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" + + p_or, q_or, v_or, a_or = backend.lines_or_info() + + sub_id = 0 + # everything on busbar 2 at sub 0 (should have no impact) + action1 = type(backend)._complete_action_class() + action1.update({"set_bus": {"substations_id": [(sub_id, [2 for _ in range(type(backend).sub_info[sub_id])])]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action1 + backend.apply_action(bk_act) # everything on busbar 2 at sub 0 + res = backend.runpf(is_dc=False) + assert res[0], "Your powerflow has diverged after the loading of the file, which should not happen" + p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() + assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for p (creation or suppression of active)." + assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for q (creation or suppression of reactive)." + assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for p (creation or suppression of active)." + assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for q (creation or suppression of reactive)." + assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow: some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" + p_after_or, q_after_or, v_after_or, a_after_or = backend.lines_or_info() + assert np.allclose(p_after_or, p_or), f"The p_or flow changed while the topology action is supposed to have no impact, check the `apply_action` for topology" + assert np.allclose(q_after_or, q_or), f"The q_or flow changed while the topology action is supposed to do nothing, check the `apply_action` for topology" + assert np.allclose(v_after_or, v_or), f"The v_or changed while the topology action is supposed to do nothing, check the `apply_action` for topology" + assert np.allclose(a_after_or, a_or), f"The a_or flow changed while the topology action is supposed to do nothing, check the `apply_action` for topology" + + sub_id = 1 + # mix of bus 1 and 2 on substation 1 + action2 = type(backend)._complete_action_class() + action2.update({"set_bus": {"substations_id": [(sub_id, [i % 2 + 1 for i in range(type(backend).sub_info[sub_id])])]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action1 + bk_act += action2 + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=False) + assert res[0], "Your powerflow has diverged after a topology action (but should not). Check `apply_action` for topology" + p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() + assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for p (creation or suppression of active)." + assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for q (creation or suppression of reactive)." + assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for p (creation or suppression of active)." + assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for q (creation or suppression of reactive)." + assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow: some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" + p_after_or, q_after_or, v_after_or, a_after_or = backend.lines_or_info() + assert not np.allclose(p_after_or, p_or), f"The p_or flow doesn't change while the topology action is supposed to have a real impact, check the `apply_action` for topology" + assert not np.allclose(q_after_or, q_or), f"The q_or flow doesn't change while the topology action is supposed to have a real impact, check the `apply_action` for topology" + assert not np.allclose(v_after_or, v_or), f"The v_or doesn't change while the topology action is supposed to have a real impact, check the `apply_action` for topology" + assert not np.allclose(a_after_or, a_or), f"The a_or flow doesn't change while the topology action is supposed to have a real impact, check the `apply_action` for topology" + + def test_15_reset(self): + """Tests that when a backend is reset, it is indeed reset in the original state + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC mode) is implemented + - backend.apply_action() for generator and load is implemented + - backend.lines_or_info() is implemented + """ + + backend = self.aux_make_backend() + cls = type(backend) + backend2 = self.aux_make_backend() + res = backend.runpf(is_dc=False) + assert len(res) == 2, "runpf should return tuple of size 2" + + # create a situation where the backend diverges + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + gen_p = 1. * init_gen_p + load_p = 1. * init_load_p + nb_iter = 0 + while True: + gen_p *= 1.5 + load_p *= 1.5 + action = type(backend)._complete_action_class() + action.update({"injection": {"prod_p": gen_p, + "load_p": load_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + res = backend.runpf(is_dc=False) + converged, exc_ = res + if converged: + assert exc_ is None, "when a powerflow converges, we expect exc_ (2nd returned value) to be None" + else: + assert isinstance(exc_, Exception), "when a powerflow diverges, we expect exc_ (2nd returned value) to be an exception" + break + nb_iter += 1 + if nb_iter >= 10: + raise RuntimeError("It is surprising that your backend still converges when the load / generation are multiplied by " + "something like 50 (1.5**10). I suppose it's an error. " + "It should stop in approx 3 iteration (so when multiplied by 1.5**3)") + + backend.reset(self.get_path(), self.get_casefile()) + res = backend.runpf(is_dc=False) + assert res[0], "your backend has diverged after being reset" + res_ref = backend2.runpf(is_dc=False) + assert res_ref[0], "your backend has diverged after just loading the grid" + p_or, q_or, v_or, a_or = backend.lines_or_info() + p2_or, q2_or, v2_or, a2_or = backend2.lines_or_info() + assert np.allclose(p2_or, p_or), f"The p_or flow differ between its original value and after a reset. Check backend.reset()" + assert np.allclose(q2_or, q_or), f"The q_or flow differ between its original value and after a reset. Check backend.reset()" + assert np.allclose(v2_or, v_or), f"The v_or differ between its original value and after a reset. Check backend.reset()" + assert np.allclose(a2_or, a_or), f"The a_or flow differ between its original value and after a reset. Check backend.reset()" + + def test_16_isolated_load_make_divergence(self): + """Tests that an isolated load will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.reset() is implemented + """ + backend = self.aux_make_backend() + cls = type(backend) + + # a load alone on a bus + action = type(backend)._complete_action_class() + action.update({"set_bus": {"loads_id": [(0, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=False) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated loads in AC." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + backend.reset(self.get_path(), self.get_casefile()) + # a load alone on a bus + action = type(backend)._complete_action_class() + action.update({"set_bus": {"loads_id": [(0, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=True) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated loads in DC." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + def test_17_isolated_gen_make_divergence(self): + """Tests that an isolated generator will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.reset() is implemented + """ + backend = self.aux_make_backend() + cls = type(backend) + + # disconnect a gen + action = type(backend)._complete_action_class() + action.update({"set_bus": {"generators_id": [(0, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=False) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated gen." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + backend.reset(self.get_path(), self.get_casefile()) + # disconnect a gen + action = type(backend)._complete_action_class() + action.update({"set_bus": {"generators_id": [(0, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=True) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated gen." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + def test_18_isolated_shunt_make_divergence(self): + """Tests test that an isolated shunt will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.reset() is implemented + + NB: this test is skipped if your backend does not (yet :-) ) supports shunt + """ + backend = self.aux_make_backend() + cls = type(backend) + if not cls.shunts_data_available: + self.skipTest("Your backend does not support shunts") + + # make a shunt alone on a bus + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_bus": [(0, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=False) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated shunt." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + backend.reset(self.get_path(), self.get_casefile()) + # make a shunt alone on a bus + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_bus": [(0, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=True) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated shunt." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + def test_19_isolated_storage_make_divergence(self): + """Teststest that an isolated storage unit will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.reset() is implemented + + NB: this test is skipped if your backend does not (yet :-) ) supports storage units + """ + backend = self.aux_make_backend() + cls = type(backend) + if cls.n_storage == 0: + self.skipTest("Your backend does not support storage units") + + action = type(backend)._complete_action_class() + action.update({"set_bus": {"storages_id": [(0, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=False) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated storage unit." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + backend.reset(self.get_path(), self.get_casefile()) + action = type(backend)._complete_action_class() + action.update({"set_bus": {"storages_id": [(0, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=True) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated storage unit." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + def test_20_disconnected_load_make_divergence(self): + """Tests that a disconnected load unit will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.reset() is implemented + + NB: this test is skipped if your backend does not (yet :-) ) supports storage units + """ + backend = self.aux_make_backend() + + # a load alone on a bus + action = type(backend)._complete_action_class() + action.update({"set_bus": {"loads_id": [(0, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=False) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of disconnected load in AC." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + backend.reset(self.get_path(), self.get_casefile()) + # a load alone on a bus + action = type(backend)._complete_action_class() + action.update({"set_bus": {"loads_id": [(0, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=True) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of disconnected load in DC." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + def test_21_disconnected_gen_make_divergence(self): + """Tests that a disconnected generator will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.reset() is implemented + + NB: this test is skipped if your backend does not (yet :-) ) supports storage units + """ + backend = self.aux_make_backend() + + # a disconnected generator + action = type(backend)._complete_action_class() + action.update({"set_bus": {"generators_id": [(0, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=False) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of disconnected gen in AC." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + backend.reset(self.get_path(), self.get_casefile()) + # a disconnected generator + action = type(backend)._complete_action_class() + action.update({"set_bus": {"generators_id": [(0, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=True) + assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of disconnected gen in DC." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + def test_22_islanded_grid_make_divergence(self): + """Tests that when the grid is split in two different "sub_grid" it makes the runpf diverge both in AC and DC + + For information, this is suppose to make a subgrid with substation 5, 11 and 12 on one side + and all the rest on the other. + + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.reset() is implemented + + NB: this test is skipped if your backend does not (yet :-) ) supports storage units + """ + backend = self.aux_make_backend() + # a non connected grid + action = type(backend)._complete_action_class() + action.update({"set_bus": {"lines_or_id": [(17, 2)], + "lines_ex_id": [(7, 2), (14, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=False) + assert not res[0], "It is expected that your backend 'diverges' in case of non connected grid in AC." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + backend.reset(self.get_path(), self.get_casefile()) + + # a non connected grid + backend.reset(self.get_path(), self.get_casefile()) + action = type(backend)._complete_action_class() + action.update({"set_bus": {"lines_or_id": [(17, 2)], + "lines_ex_id": [(7, 2), (14, 2)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 + res = backend.runpf(is_dc=True) + assert not res[0], "It is expected that your backend 'diverges' in case of non connected grid in DC." + assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + + def test_23_disco_line_v_null(self): + """Tests that disconnected elements shunt have v = 0. (and not nan or something) + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.shunt() and lines_ex_info() are implemented + - backend.reset() is implemented + """ + backend = self.aux_make_backend() + + line_id = 0 + # a disconnected line + action = type(backend)._complete_action_class() + action.update({"set_bus": {"lines_or_id": [(line_id, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + + res = backend.runpf(is_dc=False) + p_or, q_or, v_or, a_or = backend.lines_or_info() + p_ex, q_ex, v_ex, a_ex = backend.lines_ex_info() + assert np.allclose(v_or[line_id], 0.), f"v_or should be 0. for disconnected line, but is currently {v_or[line_id]} (AC)" + assert np.allclose(v_ex[line_id], 0.), f"v_ex should be 0. for disconnected line, but is currently {v_ex[line_id]} (AC)" + assert np.allclose(a_or[line_id], 0.), f"v_or should be 0. for disconnected line, but is currently {v_or[line_id]} (AC)" + assert np.allclose(a_ex[line_id], 0.), f"v_ex should be 0. for disconnected line, but is currently {v_ex[line_id]} (AC)" + + # reset and do the same in DC + backend.reset(self.get_path(), self.get_casefile()) + action = type(backend)._complete_action_class() + action.update({"set_bus": {"lines_or_id": [(line_id, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + + res = backend.runpf(is_dc=True) + p_or, q_or, v_or, a_or = backend.lines_or_info() + p_ex, q_ex, v_ex, a_ex = backend.lines_ex_info() + assert np.allclose(v_or[line_id], 0.), f"v_or should be 0. for disconnected line, but is currently {v_or[line_id]} (DC)" + assert np.allclose(v_ex[line_id], 0.), f"v_ex should be 0. for disconnected line, but is currently {v_ex[line_id]} (DC)" + assert np.allclose(a_or[line_id], 0.), f"v_or should be 0. for disconnected line, but is currently {v_or[line_id]} (DC)" + assert np.allclose(a_ex[line_id], 0.), f"v_ex should be 0. for disconnected line, but is currently {v_ex[line_id]} (DC)" + + def test_24_disco_shunt_v_null(self): + """Tests that disconnected shunts have v = 0. (and not nan or something) + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.storages_info() are implemented + - backend.reset() is implemented + + NB: this test is skipped if your backend does not support shunt (yet :-) ) + """ + backend = self.aux_make_backend() + cls = type(backend) + if not cls.shunts_data_available: + self.skipTest("Your backend does not support shunts") + + shunt_id = 0 + # a disconnected shunt + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_bus": [(shunt_id, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + res = backend.runpf(is_dc=False) + p_, q_, v_, bus_ = backend.shunt_info() + assert np.allclose(v_[shunt_id], 0.), f"v should be 0. for disconnected shunt, but is currently {v_[shunt_id]} (AC)" + assert bus_[shunt_id] == -1, f"bus_ should be -1 for disconnected shunt, but is currently {bus_[shunt_id]} (AC)" + + # a disconnected shunt + backend.reset(self.get_path(), self.get_casefile()) + action = type(backend)._complete_action_class() + action.update({"shunt": {"shunt_bus": [(shunt_id, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + res = backend.runpf(is_dc=True) + p_, q_, v_, bus_ = backend.shunt_info() + assert np.allclose(v_[shunt_id], 0.), f"v should be 0. for disconnected shunt, but is currently {v_[shunt_id]} (DC)" + assert bus_[shunt_id] == -1, f"bus_ should be -1 for disconnected shunt, but is currently {bus_[shunt_id]} (DC)" + + def test_25_disco_storage_v_null(self): + """Tests that disconnected shunts have v = 0. (and not nan or something) + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for topology modification + - backend.storages_info() are implemented + - backend.reset() is implemented + + NB: this test is skipped if your backend does not support storage unit (yet :-) ) + """ + backend = self.aux_make_backend() + cls = type(backend) + if cls.n_storage == 0: + self.skipTest("Your backend does not support storage unit") + + storage_id = 0 + # a disconnected storage + action = type(backend)._complete_action_class() + action.update({"set_bus": {"storages_id": [(storage_id, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + + res = backend.runpf(is_dc=False) + p_, q_, v_ = backend.storages_info() + assert np.allclose(v_[storage_id], 0.), f"v should be 0. for disconnected storage, but is currently {v_[storage_id]} (AC)" + + # disconnect a storage + backend.reset(self.get_path(), self.get_casefile()) + action = type(backend)._complete_action_class() + action.update({"set_bus": {"storages_id": [(storage_id, -1)]}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + res = backend.runpf(is_dc=True) + p_, q_, v_ = backend.storages_info() + assert np.allclose(v_[storage_id], 0.), f"v should be 0. for disconnected storage, but is currently {v_[storage_id]} (AC)" + + def test_26_copy(self): + """Tests that the backend can be copied (and that the copied backend and the + original one can be modified independantly) + + This test supposes that : + + - backend.load_grid(...) is implemented + - backend.runpf() (AC and DC mode) is implemented + - backend.apply_action() for prod / gen modification + - backend.lines_or_info() are implemented + - backend.reset() is implemented + + NB: this test is skipped if the backend cannot be copied + """ + backend = self.aux_make_backend() + if not backend._can_be_copied: + with self.assertRaises(BackendError): + backend_cpy = backend.copy() + return + + # backend can be copied + backend_cpy = backend.copy() + assert isinstance(backend_cpy, type(backend)), f"backend.copy() is supposed to return an object of the same type as your backend. Check backend.copy()" + # now modify original one + init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + + action = type(backend)._complete_action_class() + action.update({"injection": {"prod_p": 1.1 * init_gen_p, + "load_p": 1.1 * init_load_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + res = backend.runpf(is_dc=True) + res_cpy = backend_cpy.runpf(is_dc=True) + + p_or, *_ = backend.lines_or_info() + p_or_cpy, *_ = backend_cpy.lines_or_info() + assert not np.allclose(p_or, p_or_cpy), (f"The p_or for your backend and its copy are identical though one has been modify and not the other. " + "It is likely that backend.copy implementation does not perform a deep copy") + \ No newline at end of file diff --git a/grid2op/tests/aux_test_make_backend_test_suite.py b/grid2op/tests/aux_test_make_backend_test_suite.py deleted file mode 100644 index 0787ae0f5..000000000 --- a/grid2op/tests/aux_test_make_backend_test_suite.py +++ /dev/null @@ -1,19 +0,0 @@ -import unittest -from make_backend_test_suite import create_test_suite -from grid2op.Backend import PandaPowerBackend -import sys - -def this_make_backend(self, detailed_infos_for_cascading_failures=False): - return PandaPowerBackend( - detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures - ) -add_name_cls = "test_functionality" - -res = create_test_suite(make_backend_fun=this_make_backend, - add_name_cls=add_name_cls, - add_to_module=__name__, - extended_test=False) - -if __name__ == "__main__": - unittest.main() - \ No newline at end of file diff --git a/grid2op/tests/make_backend_test_suite.py b/grid2op/tests/make_backend_test_suite.py deleted file mode 100644 index 890306f37..000000000 --- a/grid2op/tests/make_backend_test_suite.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2019-2020, RTE (https://www.rte-france.com) -# See AUTHORS.txt -# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. -# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, -# you can obtain one at http://mozilla.org/MPL/2.0/. -# SPDX-License-Identifier: MPL-2.0 -# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. - -import unittest -import re -import sys - -from grid2op.tests.BaseBackendTest import (AAATestBackendAPI, - BaseTestNames, BaseTestLoadingCase, BaseTestLoadingBackendFunc, - BaseTestTopoAction, BaseTestEnvPerformsCorrectCascadingFailures, - BaseTestChangeBusAffectRightBus, BaseTestShuntAction, - BaseTestResetEqualsLoadGrid, BaseTestVoltageOWhenDisco, BaseTestChangeBusSlack, - BaseIssuesTest, BaseStatusActions, - BaseTestStorageAction) -from grid2op.tests.test_Environment import (BaseTestLoadingBackendPandaPower, - BaseTestResetOk, - BaseTestResetAfterCascadingFailure, - BaseTestCascadingFailure) -from grid2op.tests.BaseRedispTest import (BaseTestRedispatch, BaseTestRedispatchChangeNothingEnvironment, - BaseTestRedispTooLowHigh, BaseTestDispatchRampingIllegalETC, - BaseTestLoadingAcceptAlmostZeroSumRedisp) - - -# Issue131Tester - -def make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes): - this_cls = type(f"{re.sub('Base', '', el.__name__)}_{add_name_cls}", - (el, unittest.TestCase), - {"make_backend": this_make_backend}) - if add_to_module is not None: - # make the created class visible to the default module - setattr(sys.modules[add_to_module], - this_cls.__name__, - this_cls, - ) - all_classes.append(this_cls) - - -def create_test_suite(make_backend_fun, - add_name_cls, - *args, - add_to_module=None, - extended_test=True, - tests_skipped=(), - **kwargs): - def this_make_backend(self, detailed_infos_for_cascading_failures=False): - return make_backend_fun(self, - *args, - detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, - **kwargs) - all_classes = [] - make_and_add_cls(AAATestBackendAPI, add_name_cls, this_make_backend, add_to_module, all_classes) - if not extended_test: - return - - # Does only the last one... - for el in [BaseTestNames, - BaseTestLoadingCase, - BaseTestLoadingBackendFunc, - BaseTestTopoAction, - BaseTestEnvPerformsCorrectCascadingFailures, - BaseTestChangeBusAffectRightBus, - BaseTestShuntAction, - BaseTestResetEqualsLoadGrid, - BaseTestVoltageOWhenDisco, - BaseTestChangeBusSlack, - BaseIssuesTest, - BaseStatusActions, - BaseTestStorageAction, - BaseTestLoadingBackendPandaPower, - BaseTestResetOk, - BaseTestResetAfterCascadingFailure, - BaseTestCascadingFailure, - BaseTestRedispatch, - BaseTestRedispatchChangeNothingEnvironment, - BaseTestRedispTooLowHigh, - BaseTestDispatchRampingIllegalETC, - BaseTestLoadingAcceptAlmostZeroSumRedisp]: - make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes) - - return all_classes diff --git a/grid2op/tests/test_ActionProperties.py b/grid2op/tests/test_ActionProperties.py index d512be720..86d908e5a 100644 --- a/grid2op/tests/test_ActionProperties.py +++ b/grid2op/tests/test_ActionProperties.py @@ -8,6 +8,7 @@ import copy import re +import unittest from grid2op.tests.helper_path_test import * diff --git a/grid2op/tests/test_Agent.py b/grid2op/tests/test_Agent.py index 0f7cfa9a7..717cdb982 100644 --- a/grid2op/tests/test_Agent.py +++ b/grid2op/tests/test_Agent.py @@ -41,6 +41,7 @@ def setUp(self): The case file is a representation of the case14 as found in the ieee14 powergrid. :return: """ + super.setUp() param = Parameters() param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): @@ -49,6 +50,7 @@ def setUp(self): def tearDown(self): self.env.close() + super.tearDown() def _aux_test_agent(self, agent, i_max=30): done = False diff --git a/grid2op/tests/test_AgentConverter.py b/grid2op/tests/test_AgentConverter.py index cf84cb73a..5c8e927cd 100644 --- a/grid2op/tests/test_AgentConverter.py +++ b/grid2op/tests/test_AgentConverter.py @@ -6,12 +6,8 @@ # 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 copy -import json -import re import warnings -import pdb -from abc import ABC, abstractmethod +import unittest from grid2op.tests.helper_path_test import * from grid2op.Agent import AgentWithConverter, MLAgent @@ -20,10 +16,6 @@ from grid2op import make from grid2op.Parameters import Parameters -import warnings - -warnings.simplefilter("error") - class TestAgent(AgentWithConverter): def __init__( diff --git a/grid2op/tests/test_AgentsFast.py b/grid2op/tests/test_AgentsFast.py index 2796dfe6a..f373b6636 100644 --- a/grid2op/tests/test_AgentsFast.py +++ b/grid2op/tests/test_AgentsFast.py @@ -26,10 +26,6 @@ if DEBUG: print("pandapower version : {}".format(pp.__version__)) -import warnings - -warnings.simplefilter("error") - class RandomTestAgent(BaseAgent): def act(self, observation, reward, done=False): @@ -42,6 +38,7 @@ def setUp(self): The case file is a representation of the case14 as found in the ieee14 powergrid. :return: """ + super().setUp() param = Parameters() param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): @@ -49,6 +46,7 @@ def setUp(self): self.env = make("rte_case14_redisp", test=True, param=param) def tearDown(self): + super().tearDown() self.env.close() def _aux_test_agent(self, agent, i_max=30): diff --git a/grid2op/tests/test_BackendConverter.py b/grid2op/tests/test_BackendConverter.py index cc4f8b49b..4d5d64552 100644 --- a/grid2op/tests/test_BackendConverter.py +++ b/grid2op/tests/test_BackendConverter.py @@ -35,10 +35,6 @@ BKclass1 = PandaPowerBackend BKclass2 = PandaPowerBackend -import warnings - -warnings.simplefilter("error") - class TestLoading(HelperTests, unittest.TestCase): def test_init(self): @@ -52,7 +48,7 @@ def test_init(self): env = make("rte_case14_realistic", test=True, backend=backend) -class TestNames(HelperTests, BaseTestNames, unittest.TestCase): +class TestNames(BaseTestNames, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -66,7 +62,7 @@ def get_path(self): return PATH_DATA_TEST_INIT -class TestLoadingCase(HelperTests, BaseTestLoadingCase, unittest.TestCase): +class TestLoadingCase(BaseTestLoadingCase, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -83,7 +79,7 @@ def get_casefile(self): return "test_case14.json" -class TestLoadingBackendFunc(HelperTests, BaseTestLoadingBackendFunc, unittest.TestCase): +class TestLoadingBackendFunc(BaseTestLoadingBackendFunc, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestLoadingBackendFunc.setUp(self) @@ -108,7 +104,7 @@ def get_casefile(self): return "test_case14.json" -class TestTopoAction(HelperTests, BaseTestTopoAction, unittest.TestCase): +class TestTopoAction(BaseTestTopoAction, unittest.TestCase): def setUp(self): BaseTestTopoAction.setUp(self) @@ -133,7 +129,7 @@ def get_casefile(self): class TestEnvPerformsCorrectCascadingFailures( - HelperTests, BaseTestEnvPerformsCorrectCascadingFailures, unittest.TestCase + BaseTestEnvPerformsCorrectCascadingFailures, unittest.TestCase ): def setUp(self): BaseTestEnvPerformsCorrectCascadingFailures.setUp(self) @@ -158,7 +154,7 @@ def get_path(self): return PATH_DATA_TEST -class TestChangeBusAffectRightBus(HelperTests, BaseTestChangeBusAffectRightBus, unittest.TestCase): +class TestChangeBusAffectRightBus(BaseTestChangeBusAffectRightBus, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -169,7 +165,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestShuntAction(HelperTests, BaseTestShuntAction, unittest.TestCase): +class TestShuntAction(BaseTestShuntAction, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -180,7 +176,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestResetEqualsLoadGrid(HelperTests, BaseTestResetEqualsLoadGrid, unittest.TestCase): +class TestResetEqualsLoadGrid(BaseTestResetEqualsLoadGrid, unittest.TestCase): def setUp(self): BaseTestResetEqualsLoadGrid.setUp(self) @@ -194,7 +190,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestVoltageOWhenDisco(HelperTests, BaseTestVoltageOWhenDisco, unittest.TestCase): +class TestVoltageOWhenDisco(BaseTestVoltageOWhenDisco, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -205,7 +201,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestChangeBusSlack(HelperTests, BaseTestChangeBusSlack, unittest.TestCase): +class TestChangeBusSlack(BaseTestChangeBusSlack, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -216,7 +212,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestIssuesTest(HelperTests, BaseIssuesTest, unittest.TestCase): +class TestIssuesTest(BaseIssuesTest, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, @@ -227,7 +223,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return backend -class TestStatusAction(HelperTests, BaseStatusActions, unittest.TestCase): +class TestStatusAction(BaseStatusActions, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): backend = BackendConverter( source_backend_class=BKclass1, diff --git a/grid2op/tests/test_ChronicsHandler.py b/grid2op/tests/test_ChronicsHandler.py index d3220cffd..d3cc5b6f7 100644 --- a/grid2op/tests/test_ChronicsHandler.py +++ b/grid2op/tests/test_ChronicsHandler.py @@ -39,6 +39,7 @@ class TestProperHandlingHazardsMaintenance(HelperTests, unittest.TestCase): def setUp(self): + super().setUp() self.path_hazard = os.path.join(PATH_CHRONICS, "chronics_with_hazards") self.path_maintenance = os.path.join(PATH_CHRONICS, "chronics_with_maintenance") @@ -317,6 +318,7 @@ def test_loadchornics_maintenance_ok(self): class TestLoadingChronicsHandler(HelperTests, unittest.TestCase): def setUp(self): + super().setUp() self.path = os.path.join(PATH_CHRONICS, "chronics") self.n_gen = 5 @@ -382,10 +384,6 @@ def setUp(self): "bus_14", ] - # Cette méthode sera appelée après chaque test. - def tearDown(self): - pass - def test_check_validity(self): chron_handl = ChronicsHandler(chronicsClass=GridStateFromFile, path=self.path) chron_handl.initialize( @@ -533,6 +531,7 @@ def test_name_invariant(self): class TestLoadingChronicsHandlerWithForecast(HelperTests, unittest.TestCase): # Cette méthode sera appelée avant chaque test. def setUp(self): + super().setUp() self.path = os.path.join(PATH_CHRONICS, "chronics_with_forecast") self.n_gen = 5 @@ -598,10 +597,6 @@ def setUp(self): "bus_14", ] - # Cette méthode sera appelée après chaque test. - def tearDown(self): - pass - def compare_vect(self, pred, true): return np.max(np.abs(pred - true)) <= self.tolvect @@ -625,6 +620,7 @@ def test_check_validity(self): class TestLoadingChronicsHandlerPP(HelperTests, unittest.TestCase): # Cette méthode sera appelée avant chaque test. def setUp(self): + super().setUp() self.pathfake = os.path.join(PATH_CHRONICS, "chronics") self.path = os.path.join(PATH_CHRONICS, "chronics") @@ -937,6 +933,7 @@ def test_name_invariant(self): class TestLoadingMultiFolder(HelperTests, unittest.TestCase): def setUp(self): + super().setUp() self.path = os.path.join(PATH_CHRONICS, "test_multi_chronics") self.n_gen = 5 @@ -1047,7 +1044,6 @@ def setUp(self): "8_G40.43": "gen_7_3", }, } - self.max_iter = 10 # Cette méthode sera appelée après chaque test. @@ -1122,6 +1118,7 @@ def test_stopiteration_chunksize(self): class TestEnvChunk(HelperTests, unittest.TestCase): def setUp(self): + super().setUp() self.max_iter = 10 with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -1837,6 +1834,7 @@ def _reset_chron_handl(self, chronics_handler): pass def setUp(self) -> None: + super().setUp() chronics_class = self.get_multifolder_class() with warnings.catch_warnings(): warnings.filterwarnings("ignore") diff --git a/grid2op/tests/test_Converter.py b/grid2op/tests/test_Converter.py index 28a164b00..7dabb97ba 100644 --- a/grid2op/tests/test_Converter.py +++ b/grid2op/tests/test_Converter.py @@ -28,6 +28,7 @@ def setUp(self): The case file is a representation of the case14 as found in the ieee14 powergrid. :return: """ + super().setUp() param = Parameters() param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): @@ -41,6 +42,7 @@ def setUp(self): np.random.seed(0) def tearDown(self): + super().tearDown() self.env.close() def test_ConnectivityConverter(self): @@ -366,6 +368,7 @@ def setUp(self): The case file is a representation of the case14 as found in the ieee14 powergrid. :return: """ + super().setUp() param = Parameters() param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): @@ -380,6 +383,7 @@ def setUp(self): self.filenamedict = "test_action_json_educ_case14_storage.json" def tearDown(self): + super().tearDown() self.env.close() def test_save_reload(self): diff --git a/grid2op/tests/test_EnvironmentCpy.py b/grid2op/tests/test_EnvironmentCpy.py index e42447400..4cdeff686 100644 --- a/grid2op/tests/test_EnvironmentCpy.py +++ b/grid2op/tests/test_EnvironmentCpy.py @@ -10,9 +10,7 @@ # This module will test that the environment, when copied, works as expected (ie with making some basic tests # for the results of "env.copy()" -import copy -import pdb -import time +import unittest import warnings from grid2op.tests.helper_path_test import * diff --git a/grid2op/tests/test_EpisodeData.py b/grid2op/tests/test_EpisodeData.py index 890ee1883..30dccafaf 100644 --- a/grid2op/tests/test_EpisodeData.py +++ b/grid2op/tests/test_EpisodeData.py @@ -9,9 +9,10 @@ import tempfile import warnings import pdb +import unittest import grid2op -from grid2op.Agent import OneChangeThenNothing, RandomAgent +from grid2op.Agent import OneChangeThenNothing from grid2op.tests.helper_path_test import * from grid2op.Chronics import Multifolder from grid2op.Reward import L2RPNReward diff --git a/grid2op/tests/test_GymConverter.py b/grid2op/tests/test_GymConverter.py index dce5b1399..f5bbfec1c 100644 --- a/grid2op/tests/test_GymConverter.py +++ b/grid2op/tests/test_GymConverter.py @@ -9,12 +9,14 @@ # TODO test the json part but... https://github.com/openai/gym-http-api/issues/62 or https://github.com/openai/gym/issues/1841 import tempfile import json -from grid2op.gym_compat import (DiscreteActSpace, GymActionSpace, GymObservationSpace, GymEnv, ContinuousToDiscreteConverter) +import unittest + +from grid2op.gym_compat import (DiscreteActSpace, GymActionSpace, + GymObservationSpace, GymEnv, ContinuousToDiscreteConverter) from grid2op.tests.helper_path_test import * from grid2op.Action import PlayableAction from grid2op.dtypes import dt_float, dt_bool, dt_int -from grid2op.tests.helper_path_test import * from grid2op.MakeEnv import make from grid2op.Converter import IdToAct, ToVect import pdb diff --git a/grid2op/tests/test_MultiMix.py b/grid2op/tests/test_MultiMix.py index ad45566e4..9ee05802f 100644 --- a/grid2op/tests/test_MultiMix.py +++ b/grid2op/tests/test_MultiMix.py @@ -7,11 +7,11 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import tempfile +import unittest from grid2op.tests.helper_path_test import * from grid2op.Environment import MultiMixEnvironment from grid2op.Environment import BaseEnv -from grid2op.Observation import CompleteObservation from grid2op.Parameters import Parameters from grid2op.Reward import GameplayReward, L2RPNReward from grid2op.Exceptions import EnvError, NoForecastAvailable diff --git a/grid2op/tests/test_MultiProcess.py b/grid2op/tests/test_MultiProcess.py index 0b45e8515..2845f67e2 100644 --- a/grid2op/tests/test_MultiProcess.py +++ b/grid2op/tests/test_MultiProcess.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest from grid2op.tests.helper_path_test import * import grid2op diff --git a/grid2op/tests/test_Observation.py b/grid2op/tests/test_Observation.py index c49a9eeb6..e3cc8fa0a 100644 --- a/grid2op/tests/test_Observation.py +++ b/grid2op/tests/test_Observation.py @@ -11,6 +11,7 @@ import warnings import copy import pdb +import unittest from grid2op.tests.helper_path_test import * diff --git a/grid2op/tests/test_ObservationHazard_Maintenance.py b/grid2op/tests/test_ObservationHazard_Maintenance.py index f953f0f6c..5437cadb6 100644 --- a/grid2op/tests/test_ObservationHazard_Maintenance.py +++ b/grid2op/tests/test_ObservationHazard_Maintenance.py @@ -8,6 +8,7 @@ import warnings import pdb +import unittest from grid2op.tests.helper_path_test import * diff --git a/grid2op/tests/test_Opponent.py b/grid2op/tests/test_Opponent.py index cdded1937..a29c2e8ca 100644 --- a/grid2op/tests/test_Opponent.py +++ b/grid2op/tests/test_Opponent.py @@ -8,6 +8,8 @@ import tempfile import warnings +import unittest + import grid2op from grid2op.Opponent.opponentSpace import OpponentSpace from grid2op.tests.helper_path_test import * @@ -18,7 +20,6 @@ WeightedRandomOpponent, GeometricOpponent ) -from grid2op.Opponent.geometricOpponentMultiArea import GeometricOpponentMultiArea from grid2op.Action import TopologyAction from grid2op.MakeEnv import make from grid2op.Opponent.baseActionBudget import BaseActionBudget diff --git a/grid2op/tests/test_PandaPowerBackendDefaultFunc.py b/grid2op/tests/test_PandaPowerBackendDefaultFunc.py index 18822f404..ddb25301e 100644 --- a/grid2op/tests/test_PandaPowerBackendDefaultFunc.py +++ b/grid2op/tests/test_PandaPowerBackendDefaultFunc.py @@ -5,6 +5,7 @@ # you can obtain one at http://mozilla.org/MPL/2.0/. # SPDX-License-Identifier: MPL-2.0 # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + import unittest import warnings import unittest @@ -34,10 +35,6 @@ PATH_DATA_TEST_INIT = PATH_DATA_TEST PATH_DATA_TEST = PATH_DATA_TEST_PP -import warnings - -warnings.simplefilter("error") - class PandaPowerBackendDefault(PandaPowerBackend): """ @@ -133,7 +130,7 @@ def get_topo_vect(self): return res -class TestNames(HelperTests, BaseTestNames, unittest.TestCase): +class TestNames(BaseTestNames, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures @@ -143,7 +140,7 @@ def get_path(self): return PATH_DATA_TEST_INIT -class TestLoadingCase(HelperTests, BaseTestLoadingCase, unittest.TestCase): +class TestLoadingCase(BaseTestLoadingCase, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures @@ -156,7 +153,7 @@ def get_casefile(self): return "test_case14.json" -class TestLoadingBackendFunc(HelperTests, BaseTestLoadingBackendFunc, unittest.TestCase): +class TestLoadingBackendFunc(BaseTestLoadingBackendFunc, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestLoadingBackendFunc.setUp(self) @@ -177,7 +174,7 @@ def get_casefile(self): return "test_case14.json" -class TestTopoAction(HelperTests, BaseTestTopoAction, unittest.TestCase): +class TestTopoAction(BaseTestTopoAction, unittest.TestCase): def setUp(self): BaseTestTopoAction.setUp(self) @@ -198,7 +195,7 @@ def get_casefile(self): class TestEnvPerformsCorrectCascadingFailures( - HelperTests, BaseTestEnvPerformsCorrectCascadingFailures, unittest.TestCase + BaseTestEnvPerformsCorrectCascadingFailures, unittest.TestCase ): def setUp(self): BaseTestEnvPerformsCorrectCascadingFailures.setUp(self) @@ -219,21 +216,21 @@ def get_path(self): return PATH_DATA_TEST -class TestChangeBusAffectRightBus(HelperTests, BaseTestChangeBusAffectRightBus, unittest.TestCase): +class TestChangeBusAffectRightBus(BaseTestChangeBusAffectRightBus, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestShuntAction(HelperTests, BaseTestShuntAction, unittest.TestCase): +class TestShuntAction(BaseTestShuntAction, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestResetEqualsLoadGrid(HelperTests, BaseTestResetEqualsLoadGrid, unittest.TestCase): +class TestResetEqualsLoadGrid(BaseTestResetEqualsLoadGrid, unittest.TestCase): def setUp(self): BaseTestResetEqualsLoadGrid.setUp(self) @@ -243,28 +240,28 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): ) -class TestVoltageOWhenDisco(HelperTests, BaseTestVoltageOWhenDisco, unittest.TestCase): +class TestVoltageOWhenDisco(BaseTestVoltageOWhenDisco, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestChangeBusSlack(HelperTests, BaseTestChangeBusSlack, unittest.TestCase): +class TestChangeBusSlack(BaseTestChangeBusSlack, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestIssuesTest(HelperTests, BaseIssuesTest, unittest.TestCase): +class TestIssuesTest(BaseIssuesTest, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures ) -class TestStatusAction(HelperTests, BaseStatusActions, unittest.TestCase): +class TestStatusAction(BaseStatusActions, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return PandaPowerBackendDefault( detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures diff --git a/grid2op/tests/test_Rules.py b/grid2op/tests/test_Rules.py index abef23d09..77a1810d9 100644 --- a/grid2op/tests/test_Rules.py +++ b/grid2op/tests/test_Rules.py @@ -8,6 +8,8 @@ import pdb import warnings +import unittest + from grid2op.tests.helper_path_test import * from grid2op.dtypes import dt_int, dt_bool, dt_float diff --git a/grid2op/tests/test_RulesByArea.py b/grid2op/tests/test_RulesByArea.py index 2b0c9c6c1..b4793fa65 100644 --- a/grid2op/tests/test_RulesByArea.py +++ b/grid2op/tests/test_RulesByArea.py @@ -7,6 +7,8 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. from itertools import chain +import unittest + from grid2op.tests.helper_path_test import * from grid2op.Exceptions import * from grid2op.Parameters import Parameters diff --git a/grid2op/tests/test_Runner.py b/grid2op/tests/test_Runner.py index 8ceb4fd27..c27440120 100644 --- a/grid2op/tests/test_Runner.py +++ b/grid2op/tests/test_Runner.py @@ -44,6 +44,7 @@ def act(self, observation: BaseObservation, reward: float, done: bool = False): class TestRunner(HelperTests, unittest.TestCase): def setUp(self): + super().setUp() self.init_grid_path = os.path.join(PATH_DATA_TEST_PP, "test_case14.json") self.path_chron = PATH_ADN_CHRONICS_FOLDER self.parameters_path = None diff --git a/grid2op/tests/test_RunnerFast.py b/grid2op/tests/test_RunnerFast.py index ccfe5704f..92e1100ac 100644 --- a/grid2op/tests/test_RunnerFast.py +++ b/grid2op/tests/test_RunnerFast.py @@ -26,6 +26,7 @@ class TestRunner(HelperTests, unittest.TestCase): def setUp(self): + super().setUp() self.init_grid_path = os.path.join(PATH_DATA_TEST_PP, "test_case14.json") self.path_chron = PATH_ADN_CHRONICS_FOLDER self.parameters_path = None diff --git a/grid2op/tests/test_Storage.py b/grid2op/tests/test_Storage.py index 261c12b6c..a1666971a 100644 --- a/grid2op/tests/test_Storage.py +++ b/grid2op/tests/test_Storage.py @@ -6,9 +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 copy -import pdb -import time import warnings import unittest @@ -19,8 +16,6 @@ from grid2op.dtypes import dt_float from grid2op.Action import CompleteAction -import warnings - # TODO check when there is also redispatching @@ -28,6 +23,7 @@ class TestStorageEnv(HelperTests, unittest.TestCase): """test the env part of the storage functionality""" def setUp(self) -> None: + super().setUp() with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make("educ_case14_storage", test=True) diff --git a/grid2op/tests/test_VoltageControler.py b/grid2op/tests/test_VoltageControler.py index a8d4ba967..2103021c3 100644 --- a/grid2op/tests/test_VoltageControler.py +++ b/grid2op/tests/test_VoltageControler.py @@ -7,15 +7,12 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import pdb +import unittest import warnings from grid2op.tests.helper_path_test import * from grid2op.VoltageControler import ControlVoltageFromFile from grid2op.MakeEnv import make -import warnings - -warnings.simplefilter("error") - class TestLoadingVoltageControl(unittest.TestCase): def test_creation_ControlVoltage(self): diff --git a/grid2op/tests/test_alert_gym_compat.py b/grid2op/tests/test_alert_gym_compat.py index df74c725e..4c84a15a2 100644 --- a/grid2op/tests/test_alert_gym_compat.py +++ b/grid2op/tests/test_alert_gym_compat.py @@ -6,6 +6,8 @@ # 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 unittest + from grid2op.tests.helper_path_test import * import grid2op diff --git a/grid2op/tests/test_basicBackendInterface.py b/grid2op/tests/test_basicBackendInterface.py new file mode 100644 index 000000000..9445e6712 --- /dev/null +++ b/grid2op/tests/test_basicBackendInterface.py @@ -0,0 +1,41 @@ +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import unittest +from aaa_test_backend_interface import AAATestBackendAPI +from grid2op.Backend import PandaPowerBackend +from grid2op.Converter import BackendConverter + + +BKclass1 = PandaPowerBackend +BKclass2 = PandaPowerBackend + +class TestPandapowerBkInterface(AAATestBackendAPI, unittest.TestCase): + def make_backend(self, detailed_infos_for_cascading_failures=False): + return PandaPowerBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + + +class TestPandapowerCpyBkInterface(AAATestBackendAPI, unittest.TestCase): + def make_backend(self, detailed_infos_for_cascading_failures=False): + tmp = PandaPowerBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + return tmp.copy() + + +class TestConverterBkInterface(AAATestBackendAPI, unittest.TestCase): + def make_backend(self, detailed_infos_for_cascading_failures=False): + backend = BackendConverter( + source_backend_class=BKclass1, + target_backend_class=BKclass2, + target_backend_grid_path=None, + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, + ) + return backend + + +if __name__ == "__main__": + unittest.main() diff --git a/grid2op/tests/test_issue_217.py b/grid2op/tests/test_issue_217.py index 4ad21ffb9..ad5593fcb 100644 --- a/grid2op/tests/test_issue_217.py +++ b/grid2op/tests/test_issue_217.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest import grid2op from grid2op.Chronics import ChangeNothing diff --git a/grid2op/tests/test_issue_223.py b/grid2op/tests/test_issue_223.py index 334f37823..9312bcd38 100644 --- a/grid2op/tests/test_issue_223.py +++ b/grid2op/tests/test_issue_223.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest import grid2op from grid2op.Chronics import ChangeNothing diff --git a/grid2op/tests/test_issue_224.py b/grid2op/tests/test_issue_224.py index 3f4dbf5d9..b72155562 100644 --- a/grid2op/tests/test_issue_224.py +++ b/grid2op/tests/test_issue_224.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest import grid2op from grid2op.Chronics import ChangeNothing diff --git a/grid2op/tests/test_issue_235.py b/grid2op/tests/test_issue_235.py index bc7e95883..116b419a9 100644 --- a/grid2op/tests/test_issue_235.py +++ b/grid2op/tests/test_issue_235.py @@ -7,9 +7,9 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest import grid2op -from grid2op.Chronics import ChangeNothing from grid2op.tests.helper_path_test import * from grid2op.Observation import CompleteObservation diff --git a/grid2op/tests/test_issue_238.py b/grid2op/tests/test_issue_238.py index a0013d539..fc6aebe1a 100644 --- a/grid2op/tests/test_issue_238.py +++ b/grid2op/tests/test_issue_238.py @@ -7,9 +7,9 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest import grid2op -from grid2op.Chronics import ChangeNothing from grid2op.tests.helper_path_test import * from grid2op.Action import PowerlineSetAction from grid2op.Opponent import WeightedRandomOpponent, BaseActionBudget diff --git a/grid2op/tests/test_issue_245.py b/grid2op/tests/test_issue_245.py index 6a7e81c09..5295c4332 100644 --- a/grid2op/tests/test_issue_245.py +++ b/grid2op/tests/test_issue_245.py @@ -7,6 +7,7 @@ # This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. import warnings +import unittest import grid2op from grid2op.tests.helper_path_test import * From 67ca6e7ee6275448f6e3af5334a0e7f59f1b601d Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 12 Oct 2023 10:22:32 +0200 Subject: [PATCH 18/46] fixing broken tests after refacto --- grid2op/_create_test_suite.py | 156 ++++++++++++++++++++++++++++++++++ grid2op/tests/test_Agent.py | 4 +- 2 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 grid2op/_create_test_suite.py diff --git a/grid2op/_create_test_suite.py b/grid2op/_create_test_suite.py new file mode 100644 index 000000000..aba883e78 --- /dev/null +++ b/grid2op/_create_test_suite.py @@ -0,0 +1,156 @@ +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import unittest +import re +import sys + +from grid2op.tests.aaa_test_backend_interface import AAATestBackendAPI +from grid2op.tests.BaseBackendTest import (BaseTestNames, BaseTestLoadingCase, BaseTestLoadingBackendFunc, + BaseTestTopoAction, BaseTestEnvPerformsCorrectCascadingFailures, + BaseTestChangeBusAffectRightBus, BaseTestShuntAction, + BaseTestResetEqualsLoadGrid, BaseTestVoltageOWhenDisco, BaseTestChangeBusSlack, + BaseIssuesTest, BaseStatusActions, + BaseTestStorageAction) +from grid2op.tests.test_Environment import (BaseTestLoadingBackendPandaPower, + BaseTestResetOk, + BaseTestResetAfterCascadingFailure, + BaseTestCascadingFailure) +from grid2op.tests.BaseRedispTest import (BaseTestRedispatch, BaseTestRedispatchChangeNothingEnvironment, + BaseTestRedispTooLowHigh, BaseTestDispatchRampingIllegalETC, + BaseTestLoadingAcceptAlmostZeroSumRedisp) + + +# Issue131Tester + +def _make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes): + this_cls = type(f"{re.sub('Base', '', el.__name__)}_{add_name_cls}", + (el, unittest.TestCase), + {"make_backend": this_make_backend}) + if add_to_module is not None: + # make the created class visible to the default module + setattr(sys.modules[add_to_module], + this_cls.__name__, + this_cls, + ) + all_classes.append(this_cls) + + +def create_test_suite(make_backend_fun, + add_name_cls, + *args, + add_to_module=None, + extended_test=True, + tests_skipped=(), + **kwargs): + """This function helps you create a test suite to test the behaviour of your agent. + + We recommend to use it this way: + + First use `extended_test=False` and make sure all the tests pass (or at least understand why + the failing tests are failing) + + Then, once your backend is fully working, you can pass the kwarg `extended_test=False` to + test it more in depth. + + .. alert:: + You need to install grid2op from source (from github), in developer mode to use this function ! + + For example you can do (inside a virtual env): + + - git clone https://github.com/rte-france/grid2op.git grid2op_dev + - cd grid2op_dev + - pip install -e . + + And then you can use this function. It will **NOT** work if you install grid2op from pypi (with pip install grid2op) + or if you install grid2op "not in editable mode". + + .. note:: + If pip complains at the installation, then you can remove the "pyproject.toml" and re run `pip install -e .` + + The best way to use it is (in my humble opinion) is first to make a script (*eg* `my_tests.py`) like this: + + .. code-block:: python + + from grid2op.create_test_suite import create_test_suite + + def this_make_backend(self, detailed_infos_for_cascading_failures=False): + # of course replace this function by the function that creates your backend ! + # this should be the only line you have to change in this script + return PandaPowerBackend( + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures + ) + add_name_cls = "test_functionality" # you can change this if you want to + + create_test_suite(make_backend_fun=this_make_backend, + add_name_cls=add_name_cls, + add_to_module=__name__, + extended_test=False) + + if __name__ == "__main__": + unittest.main() + + And then run this script normally `python my_script.py` and observe the results + + .. warning:: + Do not forget to include the `self` as the first argument of the `this_make_backend` (or whatever + you decide to name it) even if you don't use it ! + + Otherwise this script will NOT work ! + + Parameters + ---------- + make_backend_fun : _type_ + _description_ + add_name_cls : _type_ + _description_ + add_to_module : _type_, optional + _description_, by default None + extended_test : bool, optional + _description_, by default True + tests_skipped : tuple, optional + # TODO ! + _description_, by default () + """ + + def this_make_backend(self, detailed_infos_for_cascading_failures=False): + return make_backend_fun(self, + *args, + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, + **kwargs) + all_classes = [] + _make_and_add_cls(AAATestBackendAPI, add_name_cls, this_make_backend, add_to_module, all_classes) + if not extended_test: + return + + # Does only the last one... + for el in [BaseTestNames, + BaseTestLoadingCase, + BaseTestLoadingBackendFunc, + BaseTestTopoAction, + BaseTestEnvPerformsCorrectCascadingFailures, + BaseTestChangeBusAffectRightBus, + BaseTestShuntAction, + BaseTestResetEqualsLoadGrid, + BaseTestVoltageOWhenDisco, + BaseTestChangeBusSlack, + BaseIssuesTest, + BaseStatusActions, + BaseTestStorageAction, + BaseTestLoadingBackendPandaPower, + BaseTestResetOk, + BaseTestResetAfterCascadingFailure, + BaseTestCascadingFailure, + BaseTestRedispatch, + BaseTestRedispatchChangeNothingEnvironment, + BaseTestRedispTooLowHigh, + BaseTestDispatchRampingIllegalETC, + BaseTestLoadingAcceptAlmostZeroSumRedisp]: + _make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes) + + return all_classes diff --git a/grid2op/tests/test_Agent.py b/grid2op/tests/test_Agent.py index 717cdb982..52987203b 100644 --- a/grid2op/tests/test_Agent.py +++ b/grid2op/tests/test_Agent.py @@ -41,7 +41,7 @@ def setUp(self): The case file is a representation of the case14 as found in the ieee14 powergrid. :return: """ - super.setUp() + super().setUp() param = Parameters() param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): @@ -50,7 +50,7 @@ def setUp(self): def tearDown(self): self.env.close() - super.tearDown() + super().tearDown() def _aux_test_agent(self, agent, i_max=30): done = False From ebdea603d6987020db20bbbf627e2ffa27024c44 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 12 Oct 2023 10:31:26 +0200 Subject: [PATCH 19/46] fixing broken tests after refacto --- grid2op/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid2op/__init__.py b/grid2op/__init__.py index d73bb017d..ac42f79b0 100644 --- a/grid2op/__init__.py +++ b/grid2op/__init__.py @@ -56,7 +56,7 @@ try: from grid2op._create_test_suite import create_test_suite - __all__.append(create_test_suite) + __all__.append("create_test_suite") except ImportError as exc_: # grid2op is most likely not installed in editable mode from source pass \ No newline at end of file From 6d14072672c3225a85899f484dc839f803dd355f Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 12 Oct 2023 11:52:52 +0200 Subject: [PATCH 20/46] fixing broken tests after refacto --- grid2op/tests/BaseBackendTest.py | 39 +++-- grid2op/tests/BaseRedispTest.py | 7 +- grid2op/tests/_aux_test_gym_compat.py | 21 +-- grid2op/tests/helper_gen_iadd.py | 3 +- grid2op/tests/test_Action.py | 10 +- grid2op/tests/test_Action_iadd.py | 3 +- grid2op/tests/test_Agent.py | 22 +-- grid2op/tests/test_AgentsFast.py | 5 +- grid2op/tests/test_BackendAction.py | 4 +- grid2op/tests/test_ChronicsHandler.py | 69 +++++--- grid2op/tests/test_Converter.py | 11 +- grid2op/tests/test_Curtailment.py | 1 + grid2op/tests/test_Environment.py | 33 ++-- grid2op/tests/test_EnvironmentCpy.py | 4 +- grid2op/tests/test_EpisodeData.py | 6 +- grid2op/tests/test_GridGraphObs.py | 2 +- grid2op/tests/test_GridObjects.py | 2 +- grid2op/tests/test_GymConverter.py | 3 +- grid2op/tests/test_MultiProcess.py | 17 +- grid2op/tests/test_ObsPlusAct.py | 1 + grid2op/tests/test_Observation.py | 20 +-- grid2op/tests/test_Opponent.py | 147 +++++++++++------- grid2op/tests/test_PlotGrid.py | 3 +- grid2op/tests/test_Reward.py | 25 +-- grid2op/tests/test_RewardAlertCostScore.py | 9 +- ...est_RewardNewRenewableSourcesUsageScore.py | 6 +- grid2op/tests/test_Rules.py | 19 +-- grid2op/tests/test_RulesByArea.py | 22 +-- grid2op/tests/test_Runner.py | 34 ++-- grid2op/tests/test_RunnerFast.py | 9 +- grid2op/tests/test_Storage.py | 19 +-- grid2op/tests/test_VoltageControler.py | 7 +- .../tests/test_act_as_serializable_dict.py | 4 +- grid2op/tests/test_action_space.py | 2 +- grid2op/tests/test_alert_gym_compat.py | 11 +- grid2op/tests/test_attached_envs.py | 12 +- grid2op/tests/test_attached_envs_compat.py | 12 +- grid2op/tests/test_back_to_orig.py | 3 +- grid2op/tests/test_change_param_from_obs.py | 2 +- grid2op/tests/test_chronics_npy.py | 5 +- grid2op/tests/test_copy_env_close.py | 2 +- grid2op/tests/test_dc_isolated_elements.py | 2 +- .../tests/test_decompose_as_unary_actions.py | 1 + grid2op/tests/test_diff_backend.py | 6 +- grid2op/tests/test_educpp_backend.py | 2 +- grid2op/tests/test_elements_graph.py | 6 +- grid2op/tests/test_env_from_episode.py | 32 +++- grid2op/tests/test_feature_issue_447.py | 7 +- grid2op/tests/test_forecast_from_arrays.py | 2 +- grid2op/tests/test_fromChronix2grid.py | 5 +- grid2op/tests/test_generate_classes.py | 7 +- grid2op/tests/test_gym_env_renderer.py | 2 +- grid2op/tests/test_highres_sim_counter.py | 1 + .../test_highres_sim_counter_in_scores.py | 1 + grid2op/tests/test_issue_131.py | 2 +- grid2op/tests/test_issue_140.py | 1 + grid2op/tests/test_issue_146.py | 3 +- grid2op/tests/test_issue_147.py | 2 +- grid2op/tests/test_issue_148.py | 1 + grid2op/tests/test_issue_151.py | 2 +- grid2op/tests/test_issue_153.py | 2 +- grid2op/tests/test_issue_164.py | 3 +- grid2op/tests/test_issue_185.py | 10 +- grid2op/tests/test_issue_187.py | 7 +- grid2op/tests/test_issue_196.py | 2 +- grid2op/tests/test_issue_217.py | 2 +- grid2op/tests/test_issue_220.py | 2 +- grid2op/tests/test_issue_223.py | 2 +- grid2op/tests/test_issue_224.py | 8 +- grid2op/tests/test_issue_235.py | 3 +- grid2op/tests/test_issue_238.py | 2 +- grid2op/tests/test_issue_245.py | 3 +- grid2op/tests/test_issue_274.py | 2 +- grid2op/tests/test_issue_281.py | 3 +- grid2op/tests/test_issue_282.py | 3 +- grid2op/tests/test_issue_283.py | 3 +- grid2op/tests/test_issue_285.py | 3 +- grid2op/tests/test_issue_313.py | 3 +- grid2op/tests/test_issue_319.py | 3 +- grid2op/tests/test_issue_321.py | 9 +- grid2op/tests/test_issue_327.py | 2 +- grid2op/tests/test_issue_331.py | 2 +- grid2op/tests/test_issue_340.py | 2 +- grid2op/tests/test_issue_361.py | 2 +- grid2op/tests/test_issue_364.py | 2 +- grid2op/tests/test_issue_367.py | 3 +- grid2op/tests/test_issue_369.py | 1 + grid2op/tests/test_issue_374.py | 2 +- grid2op/tests/test_issue_377.py | 2 +- grid2op/tests/test_issue_379.py | 2 +- grid2op/tests/test_issue_380.py | 2 +- grid2op/tests/test_issue_389.py | 2 +- grid2op/tests/test_issue_396.py | 2 +- grid2op/tests/test_issue_403.py | 2 +- grid2op/tests/test_issue_407.py | 2 +- grid2op/tests/test_issue_418.py | 2 +- grid2op/tests/test_issue_433.py | 2 +- grid2op/tests/test_issue_446.py | 4 +- grid2op/tests/test_issue_494.py | 2 +- grid2op/tests/test_issue_503.py | 4 +- grid2op/tests/test_issue_511.py | 3 +- grid2op/tests/test_issue_527.py | 3 +- grid2op/tests/test_issue_533.py | 1 + grid2op/tests/test_issue_sim2real_storage.py | 4 +- grid2op/tests/test_l2rpn_idf_2023.py | 2 +- grid2op/tests/test_limit_curtail.py | 3 +- grid2op/tests/test_multi_steps_env.py | 3 +- grid2op/tests/test_multi_steps_forecasts.py | 12 +- grid2op/tests/test_nb_simulate_called.py | 4 +- grid2op/tests/test_no_backend_copy.py | 4 +- grid2op/tests/test_noisy_obs.py | 8 +- grid2op/tests/test_opp_with_area.py | 7 +- grid2op/tests/test_pickling.py | 4 +- grid2op/tests/test_recopowerlineperarea.py | 2 +- grid2op/tests/test_redisp_extreme.py | 4 +- .../test_remove_line_status_from_topo.py | 2 +- grid2op/tests/test_render.py | 2 +- grid2op/tests/test_reward_to_obs.py | 3 +- grid2op/tests/test_runner_kwargs_backend.py | 9 +- grid2op/tests/test_score_idf_2023.py | 3 +- .../tests/test_score_idf_2023_assistant.py | 3 +- grid2op/tests/test_score_idf_2023_nres.py | 3 +- grid2op/tests/test_score_wcci_2022.py | 10 +- grid2op/tests/test_simenv_blackout.py | 3 +- grid2op/tests/test_simulate_disco_load.py | 2 +- grid2op/tests/test_simulator.py | 5 +- grid2op/tests/test_soft_overflow_threshold.py | 2 +- grid2op/tests/test_timeOutEnvironment.py | 6 +- grid2op/tests/test_ts_handlers.py | 42 ++--- grid2op/tests/test_utils.py | 27 ++-- 130 files changed, 592 insertions(+), 434 deletions(-) diff --git a/grid2op/tests/BaseBackendTest.py b/grid2op/tests/BaseBackendTest.py index 70bf3c8f6..57f259d87 100644 --- a/grid2op/tests/BaseBackendTest.py +++ b/grid2op/tests/BaseBackendTest.py @@ -60,7 +60,6 @@ def comb(n, k): from grid2op.Environment import Environment from grid2op.Exceptions import * from grid2op.Rules import RulesChecker -from grid2op.MakeEnv import make from grid2op.Rules import AlwaysLegal from grid2op.Action._backendAction import _BackendAction from grid2op.Backend import Backend, PandaPowerBackend @@ -101,7 +100,7 @@ def test_properNames(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(path, "5bus_example_diff_name"), backend=backend, _add_to_name=type(self).__name__ @@ -1441,13 +1440,13 @@ def test_get_action_to_set(self): def test_get_action_to_set_storage(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( + env = grid2op.make( "educ_case14_storage", test=True, backend=self.make_backend(), _add_to_name=type(self).__name__ ) - env2 = make( + env2 = grid2op.make( "educ_case14_storage", test=True, backend=self.make_backend(), @@ -1472,7 +1471,7 @@ def test_update_from_obs(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( + env = grid2op.make( "rte_case14_realistic", test=True, backend=self.make_backend(), @@ -1911,7 +1910,7 @@ def test_set_bus(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True, backend=backend, + env = grid2op.make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) env.reset() action = env.action_space({"set_bus": {"lines_or_id": [(17, 2)]}}) @@ -1927,7 +1926,7 @@ def test_change_bus(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True, backend=backend, + env = grid2op.make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) env.reset() action = env.action_space({"change_bus": {"lines_or_id": [17]}}) @@ -1942,7 +1941,7 @@ def test_change_bustwice(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True, backend=backend, + env = grid2op.make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) env.reset() action = env.action_space({"change_bus": {"lines_or_id": [17]}}) @@ -1964,7 +1963,7 @@ def test_isolate_load(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True, backend=backend, + env = grid2op.make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) act = env.action_space({"set_bus": {"loads_id": [(0, 2)]}}) obs, reward, done, info = env.step(act) @@ -1977,7 +1976,7 @@ def test_reco_disco_bus(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_case1 = make( + env_case1 = grid2op.make( "rte_case5_example", test=True, gamerules_class=AlwaysLegal, @@ -2004,7 +2003,7 @@ def test_reco_disco_bus2(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_case2 = make( + env_case2 = gridop.make( "rte_case5_example", test=True, gamerules_class=AlwaysLegal, @@ -2031,7 +2030,7 @@ def test_reco_disco_bus3(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_case2 = make( + env_case2 = grid2op.make( "rte_case5_example", test=True, gamerules_class=AlwaysLegal, @@ -2056,7 +2055,7 @@ def test_reco_disco_bus4(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_case2 = make( + env_case2 = grid2op.make( "rte_case5_example", test=True, gamerules_class=AlwaysLegal, @@ -2081,7 +2080,7 @@ def test_reco_disco_bus5(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_case2 = make( + env_case2 = grid2op.make( "rte_case5_example", test=True, gamerules_class=AlwaysLegal, @@ -2104,7 +2103,7 @@ def test_shunt_ambiguous_id_incorrect(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "rte_case5_example", test=True, gamerules_class=AlwaysLegal, @@ -2122,7 +2121,7 @@ def test_shunt_effect(self): type(backend1)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_ref = make( + env_ref = grid2op.make( "rte_case14_realistic", test=True, gamerules_class=AlwaysLegal, @@ -2130,7 +2129,7 @@ def test_shunt_effect(self): backend=backend1, _add_to_name=type(self).__name__ ) - env_change_q = make( + env_change_q = grid2op.make( "rte_case14_realistic", test=True, gamerules_class=AlwaysLegal, @@ -2199,9 +2198,9 @@ def setUp(self): type(backend1)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = make("rte_case5_example", test=True, backend=backend1, _add_to_name=type(self).__name__) + self.env1 = grid2op.make("rte_case5_example", test=True, backend=backend1, _add_to_name=type(self).__name__) self.backend1 = self.env1.backend - self.env2 = make("rte_case5_example", test=True, backend=backend2, _add_to_name=type(self).__name__) + self.env2 = grid2op.make("rte_case5_example", test=True, backend=backend2, _add_to_name=type(self).__name__) self.backend2 = self.env2.backend np.random.seed(69) super().setUp() @@ -2404,7 +2403,7 @@ def test_this(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) as env: + with grid2op.make("rte_case14_realistic", test=True, backend=backend, _add_to_name=type(self).__name__) as env: line_id = 1 act = env.action_space({"set_line_status": [(line_id, -1)]}) obs, *_ = env.step(act) diff --git a/grid2op/tests/BaseRedispTest.py b/grid2op/tests/BaseRedispTest.py index 1a47edaa6..9f3df9069 100644 --- a/grid2op/tests/BaseRedispTest.py +++ b/grid2op/tests/BaseRedispTest.py @@ -16,7 +16,6 @@ from grid2op.Environment import Environment from grid2op.Parameters import Parameters from grid2op.Chronics import ChronicsHandler, GridStateFromFile, ChangeNothing -from grid2op.MakeEnv import make from grid2op.Action import BaseAction from grid2op.tests.BaseBackendTest import MakeBackend @@ -479,7 +478,7 @@ def setUp(self) -> None: backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_redisp", + self.env = grid2op.make("rte_case14_redisp", test=True, backend=backend, _add_to_name=type(self).__name__) @@ -577,7 +576,7 @@ def setUp(self): backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_test", test=True, backend=backend, + self.env = grid2op.make("rte_case14_test", test=True, backend=backend, _add_to_name=type(self).__name__) self.tol_one = self.env._tol_poly @@ -845,7 +844,7 @@ def setUp(self): backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_test", test=True, backend=backend, + self.env = grid2op.make("rte_case14_test", test=True, backend=backend, _add_to_name=type(self).__name__) self.tol_one = self.env._tol_poly diff --git a/grid2op/tests/_aux_test_gym_compat.py b/grid2op/tests/_aux_test_gym_compat.py index e7b856586..66dcd9710 100644 --- a/grid2op/tests/_aux_test_gym_compat.py +++ b/grid2op/tests/_aux_test_gym_compat.py @@ -111,7 +111,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - "l2rpn_case14_sandbox", test=True, _add_to_name="TestGymCompatModule" + "l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.reset() # seed part ! @@ -123,7 +123,7 @@ def test_print_with_no_storage(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - "l2rpn_icaps_2021", test=True, _add_to_name="TestGymCompatModule" + "l2rpn_icaps_2021", test=True, _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.reset() # seed part ! @@ -432,7 +432,7 @@ def test_low_high_obs_space(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "educ_case14_storage", test=True, _add_to_name="TestGymCompatModule" + "educ_case14_storage", test=True, _add_to_name=type(self).__name__+"test_low_high_obs_space" ) env.seed(0) env.reset() # seed part ! @@ -791,7 +791,7 @@ def setUp(self) -> None: "educ_case14_storage", test=True, action_class=PlayableAction, - _add_to_name="TestBoxGymObsSpace", + _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.reset() # seed part ! @@ -1083,7 +1083,7 @@ def setUp(self) -> None: "educ_case14_storage", test=True, action_class=PlayableAction, - _add_to_name="TestBoxGymActSpace", + _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.reset() # seed part ! @@ -1356,7 +1356,7 @@ def setUp(self) -> None: "educ_case14_storage", test=True, action_class=PlayableAction, - _add_to_name="TestMultiDiscreteGymActSpace", + _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.reset() # seed part ! @@ -1574,7 +1574,7 @@ def setUp(self) -> None: "educ_case14_storage", test=True, action_class=PlayableAction, - _add_to_name="TestMultiDiscreteGymActSpace", + _add_to_name=type(self).__name__, ) self.env.seed(0) self.env.reset() # seed part ! @@ -1700,7 +1700,7 @@ def setUp(self) -> None: os.path.join(PATH_DATA_TEST, "l2rpn_neurips_2020_track1_with_alarm"), test=True, action_class=PlayableAction, - _add_to_name="TestAllGymActSpaceWithAlarm", + _add_to_name=type(self).__name__, ) self.env.seed(0) self.env.reset() # seed part ! @@ -1882,7 +1882,7 @@ def setUp(self) -> None: "educ_case14_storage", test=True, action_class=PlayableAction, - _add_to_name="TestMultiDiscreteGymActSpace", + _add_to_name=type(self).__name__, ) self.env.seed(0) self.env.reset() # seed part ! @@ -1906,7 +1906,8 @@ def test_all_attr_in_obs(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make("educ_case14_storage", test=True, - action_class=PlayableAction) + action_class=PlayableAction, + _add_to_name=type(self).__name__) gym_env = self._aux_GymEnv_cls()(env) obs, info = gym_env.reset() all_attrs = ["year", diff --git a/grid2op/tests/helper_gen_iadd.py b/grid2op/tests/helper_gen_iadd.py index acff32be0..19fbab636 100755 --- a/grid2op/tests/helper_gen_iadd.py +++ b/grid2op/tests/helper_gen_iadd.py @@ -33,7 +33,8 @@ def setUpClass(cls): cls.action_t = cls._action_setup() cls.env = grid2op.make("rte_case14_realistic", test=True, - action_class=cls.action_t) + action_class=cls.action_t, + _add_to_name=type(self).__name__) @classmethod def tearDownClass(cls): cls.env.close() diff --git a/grid2op/tests/test_Action.py b/grid2op/tests/test_Action.py index d6bcdb611..e9f8bf5b1 100644 --- a/grid2op/tests/test_Action.py +++ b/grid2op/tests/test_Action.py @@ -426,7 +426,7 @@ def test_eq_diff_grid(self): act = self.helper_action.sample() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_ref = grid2op.make("rte_case5_example", test=True) + env_ref = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) act_ref = env_ref.action_space() assert not (act == act_ref) @@ -2271,7 +2271,7 @@ class TestDeepCopy(unittest.TestCase): def test_alarm(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("l2rpn_icaps_2021", test=True) as env: + with grid2op.make("l2rpn_icaps_2021", test=True, _add_to_name=type(self).__name__) as env: act = env.action_space() act.raise_alarm = [0] @@ -2282,7 +2282,7 @@ def test_alarm(self): def test_redisp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("l2rpn_icaps_2021", test=True) as env: + with grid2op.make("l2rpn_icaps_2021", test=True, _add_to_name=type(self).__name__) as env: act = env.action_space() act.redispatch = [(0, -1.0)] @@ -2293,7 +2293,7 @@ def test_redisp(self): def test_storage(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("educ_case14_storage", test=True) as env: + with grid2op.make("educ_case14_storage", test=True, _add_to_name=type(self).__name__) as env: act = env.action_space() act.storage_p = [(0, -1.0)] @@ -2304,7 +2304,7 @@ def test_storage(self): def test_topo(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("l2rpn_case14_sandbox", test=True) as env: + with grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) as env: # set line status act = env.action_space() act.set_line_status = [(0, -1)] diff --git a/grid2op/tests/test_Action_iadd.py b/grid2op/tests/test_Action_iadd.py index 87dccc844..10f203cab 100644 --- a/grid2op/tests/test_Action_iadd.py +++ b/grid2op/tests/test_Action_iadd.py @@ -28,7 +28,8 @@ def setUpClass(cls): warnings.filterwarnings("ignore") cls.action_t = cls._action_setup() cls.env = grid2op.make( - "rte_case14_realistic", test=True, action_class=cls.action_t + "rte_case14_realistic", test=True, action_class=cls.action_t, + _add_to_name=cls.__name__ ) @classmethod diff --git a/grid2op/tests/test_Agent.py b/grid2op/tests/test_Agent.py index 52987203b..30195af39 100644 --- a/grid2op/tests/test_Agent.py +++ b/grid2op/tests/test_Agent.py @@ -15,7 +15,6 @@ import grid2op from grid2op.Exceptions import * -from grid2op.MakeEnv import make from grid2op.Agent import ( PowerLineSwitch, TopologyGreedy, @@ -46,7 +45,7 @@ def setUp(self): param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_redisp", test=True, param=param) + self.env = grid2op.make("rte_case14_redisp", test=True, param=param, _add_to_name=type(self).__name__) def tearDown(self): self.env.close() @@ -152,8 +151,8 @@ class TestMake2Agents(HelperTests, unittest.TestCase): def test_2random(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("rte_case5_example", test=True) - env2 = grid2op.make("rte_case14_realistic", test=True) + env = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) + env2 = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) agent = RandomAgent(env.action_space) agent2 = RandomAgent(env2.action_space) # test i can reset the env @@ -173,7 +172,7 @@ class TestSeeding(HelperTests, unittest.TestCase): def test_random(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: obs = env.reset() my_agent = RandomAgent(env.action_space) my_agent.seed(0) @@ -203,7 +202,7 @@ def test_reco_simple(self): param.NB_TIMESTEP_COOLDOWN_LINE = 1 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("rte_case5_example", test=True, param=param) as env: + with grid2op.make("rte_case5_example", test=True, param=param, _add_to_name=type(self).__name__) as env: my_agent = RecoPowerlineAgent(env.action_space) obs = env.reset() assert np.sum(obs.time_before_cooldown_line) == 0 @@ -231,7 +230,7 @@ def test_reco_more_difficult(self): param.NB_TIMESTEP_COOLDOWN_LINE = 3 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("rte_case5_example", test=True, param=param) as env: + with grid2op.make("rte_case5_example", test=True, param=param, _add_to_name=type(self).__name__) as env: my_agent = RecoPowerlineAgent(env.action_space) obs = env.reset() obs, reward, done, info = env.step( @@ -290,7 +289,7 @@ def test_agentfromlist_empty(self): param.NB_TIMESTEP_COOLDOWN_LINE = 3 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("rte_case5_example", test=True, param=param) as env: + with grid2op.make("rte_case5_example", test=True, param=param, _add_to_name=type(self).__name__) as env: agent = FromActionsListAgent(env.action_space, action_list=[]) obs = env.reset() @@ -309,7 +308,7 @@ def test_agentfromlist(self): param.NB_TIMESTEP_COOLDOWN_LINE = 3 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("rte_case5_example", test=True, param=param) as env: + with grid2op.make("rte_case5_example", test=True, param=param, _add_to_name=type(self).__name__) as env: agent = FromActionsListAgent( env.action_space, action_list=[env.action_space({"set_line_status": [(0, +1)]})], @@ -331,7 +330,7 @@ def test_agentfromlist_creation_fails(self): param.NB_TIMESTEP_COOLDOWN_LINE = 3 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("rte_case5_example", test=True, param=param) as env: + with grid2op.make("rte_case5_example", test=True, param=param, _add_to_name=type(self).__name__) as env: with self.assertRaises(AgentError): # action_list should be an iterable agent = FromActionsListAgent(env.action_space, action_list=1) @@ -340,7 +339,8 @@ def test_agentfromlist_creation_fails(self): agent = FromActionsListAgent(env.action_space, action_list=[1]) with grid2op.make( - "l2rpn_case14_sandbox", test=True, param=param + "l2rpn_case14_sandbox", test=True, param=param, + _add_to_name=type(self).__name__ ) as env2: with self.assertRaises(AgentError): # action_list should contain only actions from a compatible environment diff --git a/grid2op/tests/test_AgentsFast.py b/grid2op/tests/test_AgentsFast.py index f373b6636..f902385d8 100644 --- a/grid2op/tests/test_AgentsFast.py +++ b/grid2op/tests/test_AgentsFast.py @@ -14,7 +14,6 @@ from grid2op.tests.helper_path_test import * from grid2op.Exceptions import * -from grid2op.MakeEnv import make from grid2op.Agent import DoNothingAgent, BaseAgent from grid2op.Parameters import Parameters from grid2op.dtypes import dt_float @@ -32,7 +31,7 @@ def act(self, observation, reward, done=False): return self.action_space.sample() -class TestAgent(HelperTests, unittest.TestCase): +class TestAgentFaster(HelperTests, unittest.TestCase): def setUp(self): """ The case file is a representation of the case14 as found in the ieee14 powergrid. @@ -43,7 +42,7 @@ def setUp(self): param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_redisp", test=True, param=param) + self.env = grid2op.make("rte_case14_redisp", test=True, param=param, _add_to_name=type(self).__name__) def tearDown(self): super().tearDown() diff --git a/grid2op/tests/test_BackendAction.py b/grid2op/tests/test_BackendAction.py index 6c0d15545..b773404cd 100644 --- a/grid2op/tests/test_BackendAction.py +++ b/grid2op/tests/test_BackendAction.py @@ -185,14 +185,14 @@ def setUp(self) -> None: "rte_case14_realistic", test=True, backend=TestSuitePandaPowerBackend(), - _add_to_name="test_get_xxx_bus_test", + _add_to_name=type(self).__name__+"test_get_xxx_bus_test", ) with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.envref = grid2op.make( "rte_case14_realistic", test=True, - _add_to_name="test_get_xxx_bus_ref" + _add_to_name=type(self).__name__+"test_get_xxx_bus_ref" ) seed = 0 self.nb_test = 10 diff --git a/grid2op/tests/test_ChronicsHandler.py b/grid2op/tests/test_ChronicsHandler.py index d3cc5b6f7..c19ad2164 100644 --- a/grid2op/tests/test_ChronicsHandler.py +++ b/grid2op/tests/test_ChronicsHandler.py @@ -16,7 +16,6 @@ import grid2op from grid2op.dtypes import dt_int, dt_float -from grid2op.MakeEnv import make from grid2op.Exceptions import * from grid2op.Chronics import ( ChronicsHandler, @@ -1122,7 +1121,7 @@ def setUp(self): self.max_iter = 10 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_realistic", test=True) + self.env = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) self.env.chronics_handler.set_max_iter(self.max_iter) def tearDown(self): @@ -1154,8 +1153,9 @@ def test_load_error(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") with self.assertRaises(EnvError): - with make( - "rte_case14_realistic", test=True, chronics_path="/answer/life/42" + with grid2op.make( + "rte_case14_realistic", test=True, chronics_path="/answer/life/42", + _add_to_name=type(self).__name__ ): pass @@ -1173,12 +1173,13 @@ def test_load_still(self): max_iter = 10 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "rte_case5_example", test=True, chronics_path=os.path.join( PATH_CHRONICS, "5bus_example_some_missing", "chronics" ), + _add_to_name=type(self).__name__, ) as env: # test a first time without chunks env.set_id(0) @@ -1216,10 +1217,11 @@ def test_load(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_DATA_TEST, "ieee118_R2subgrid_wcci_test_maintenance"), test=True, param=param, + _add_to_name=type(self).__name__, ) as env: env.seed(123456) # for reproducible tests ! obs = env.reset() @@ -1297,10 +1299,11 @@ def test_maintenance_multiple_timesteps(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_DATA_TEST, "ieee118_R2subgrid_wcci_test_maintenance"), test=True, param=param, + _add_to_name=type(self).__name__ ) as env: env.seed(0) envLines = env.name_line @@ -1373,12 +1376,13 @@ def test_proba(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join( PATH_DATA_TEST, "ieee118_R2subgrid_wcci_test_maintenance_2" ), test=True, param=param, + _add_to_name=type(self).__name__ ) as env: env.seed(0) # input data @@ -1412,12 +1416,13 @@ def test_load_fake_january(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join( PATH_DATA_TEST, "ieee118_R2subgrid_wcci_test_maintenance_3" ), test=True, param=param, + _add_to_name=type(self).__name__ ) as env: env.seed(0) # get input data, to check they were correctly applied in @@ -1452,10 +1457,11 @@ def test_split_and_save(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_DATA_TEST, "ieee118_R2subgrid_wcci_test_maintenance"), test=True, param=param, + _add_to_name=type(self).__name__, ) as env: env.seed(0) env.set_id(0) @@ -1510,12 +1516,13 @@ def test_split_and_save(self): assert np.all(maintenance_0_0 == maintenance_0_1) # make sure i can reload the environment - env2 = make( + env2 = grid2op.make( os.path.join( PATH_DATA_TEST, "ieee118_R2subgrid_wcci_test_maintenance" ), test=True, param=param, + _add_to_name=type(self).__name__, data_feeding_kwargs={ "gridvalueClass": GridStateFromFileWithForecasts }, @@ -1534,10 +1541,11 @@ def test_seed(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_DATA_TEST, "ieee118_R2subgrid_wcci_test_maintenance"), test=True, param=param, + _add_to_name=type(self).__name__, ) as env: nb_scenario = 10 nb_maintenance = np.zeros((nb_scenario, env.n_line), dtype=dt_float) @@ -1563,12 +1571,13 @@ def test_chunk_size(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join( PATH_DATA_TEST, "ieee118_R2subgrid_wcci_test_maintenance_3" ), test=True, param=param, + _add_to_name=type(self).__name__, ) as env: env.seed(0) obs = env.reset() @@ -1587,11 +1596,12 @@ def test_load(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_DATA_TEST, "5bus_example_some_missing"), test=True, param=param, chronics_class=MultifolderWithCache, + _add_to_name=type(self).__name__ ) as env: env.seed(123456) # for reproducible tests ! env.chronics_handler.reset() @@ -1616,10 +1626,11 @@ def test_withrealistic(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, + _add_to_name=type(self).__name__, ) as env: l_id = 11 obs = env.reset() @@ -1724,11 +1735,12 @@ def test_with_alwayslegal(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, gamerules_class=AlwaysLegal, + _add_to_name=type(self).__name__, ) as env: l_id = 11 obs = env.reset() @@ -1838,8 +1850,9 @@ def setUp(self) -> None: chronics_class = self.get_multifolder_class() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( - "rte_case14_realistic", test=True, chronics_class=chronics_class + self.env = grid2op.make( + "rte_case14_realistic", test=True, chronics_class=chronics_class, + _add_to_name=type(self).__name__ ) root_path = self.env.chronics_handler.real_data.path self.chronics_paths = np.array( @@ -1976,7 +1989,8 @@ def test_sample_next_chronics_withfilter(self): chronics_class = self.get_multifolder_class() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case5_example", test=True, chronics_class=chronics_class) + env = grid2op.make("rte_case5_example", test=True, chronics_class=chronics_class, + _add_to_name=type(self).__name__) self._reset_chron_handl(env.chronics_handler) env.seed(0) @@ -2017,7 +2031,8 @@ def test_set_id_int(self): chronics_class = self.get_multifolder_class() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case5_example", test=True, chronics_class=chronics_class) + env = grid2op.make("rte_case5_example", test=True, chronics_class=chronics_class, + _add_to_name=type(self).__name__) if issubclass(chronics_class, MultifolderWithCache): env.chronics_handler.set_filter( @@ -2046,7 +2061,8 @@ def test_set_id_full_path(self): chronics_class = self.get_multifolder_class() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case5_example", test=True, chronics_class=chronics_class) + env = grid2op.make("rte_case5_example", test=True, chronics_class=chronics_class, + _add_to_name=type(self).__name__) if issubclass(chronics_class, MultifolderWithCache): env.chronics_handler.set_filter( lambda x: re.match(".*(01|04|05).*", x) is not None @@ -2071,7 +2087,8 @@ def test_set_id_chron_dir(self): chronics_class = self.get_multifolder_class() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case5_example", test=True, chronics_class=chronics_class) + env = grid2op.make("rte_case5_example", test=True, chronics_class=chronics_class, + _add_to_name=type(self).__name__) if issubclass(chronics_class, MultifolderWithCache): env.chronics_handler.set_filter( lambda x: re.match(".*(01|04|05).*", x) is not None @@ -2114,22 +2131,24 @@ def test_maintenance_deactivated(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, + _add_to_name=type(self).__name__, ) as env: obs = env.reset() # there is a maintenance by default for some powerlines assert np.any(obs.time_next_maintenance != -1) - with make( + with grid2op.make( os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, data_feeding_kwargs={ "gridvalueClass": GridStateFromFileWithForecastsWithoutMaintenance }, + _add_to_name=type(self).__name__, ) as env: obs = env.reset() # all maintenance are deactivated @@ -2142,7 +2161,7 @@ def test_max_iter(self): max_iter = 288*2 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True) + env = grid2op.make("educ_case14_storage", test=True, _add_to_name=type(self).__name__) runner = Runner(**env.get_params_for_runner()) res = runner.run(nb_episode=nb_episode, diff --git a/grid2op/tests/test_Converter.py b/grid2op/tests/test_Converter.py index 7dabb97ba..233acbb16 100644 --- a/grid2op/tests/test_Converter.py +++ b/grid2op/tests/test_Converter.py @@ -10,10 +10,11 @@ import os import json import unittest + from grid2op.Action import BaseAction, PlayableAction from grid2op.tests.helper_path_test import * -from grid2op.MakeEnv import make +import grid2op from grid2op.Parameters import Parameters from grid2op.Converter import ConnectivityConverter, IdToAct import tempfile @@ -33,11 +34,12 @@ def setUp(self): param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( + self.env = grid2op.make( "educ_case14_storage", test=True, param=param, action_class=PlayableAction, + _add_to_name=type(self).__name__, ) np.random.seed(0) @@ -292,7 +294,7 @@ def test_bug_in_doc(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True) + env = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) converter = ConnectivityConverter(env.action_space) # it's a good practice to seed the element that can be, for reproducibility converter.seed(0) @@ -373,11 +375,12 @@ def setUp(self): param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( + self.env = grid2op.make( "educ_case14_storage", test=True, param=param, action_class=PlayableAction, + _add_to_name=type(self).__name__ ) np.random.seed(0) self.filenamedict = "test_action_json_educ_case14_storage.json" diff --git a/grid2op/tests/test_Curtailment.py b/grid2op/tests/test_Curtailment.py index e10cb0506..b245ffcd8 100644 --- a/grid2op/tests/test_Curtailment.py +++ b/grid2op/tests/test_Curtailment.py @@ -37,6 +37,7 @@ def setUp(self) -> None: test=True, action_class=PlayableAction, param=param, + _add_to_name=type(self).__name__ ) self.env2 = self.env1.copy() diff --git a/grid2op/tests/test_Environment.py b/grid2op/tests/test_Environment.py index 62d158f24..28dcec27d 100644 --- a/grid2op/tests/test_Environment.py +++ b/grid2op/tests/test_Environment.py @@ -14,13 +14,13 @@ import unittest from grid2op.tests.helper_path_test import * +import grid2op from grid2op.Exceptions import * from grid2op.Environment import Environment from grid2op.Backend import PandaPowerBackend from grid2op.Parameters import Parameters from grid2op.Chronics import ChronicsHandler, GridStateFromFile, ChangeNothing from grid2op.Reward import L2RPNReward -from grid2op.MakeEnv import make from grid2op.Rules import RulesChecker, DefaultRules from grid2op.dtypes import dt_float @@ -335,7 +335,7 @@ def setUp(self): self.tol_one = 1e-4 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case5_example", test=True) + self.env = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) def tearDown(self): self.env.close() @@ -384,11 +384,12 @@ def setUp(self): self.tol_one = 1e-4 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( + self.env = grid2op.make( "rte_case5_example", test=True, reward_class=L2RPNReward, other_rewards={"test": L2RPNReward}, + _add_to_name=type(self).__name__, ) def tearDown(self): @@ -457,12 +458,13 @@ def setUp(self): self.tol_one = 1e-4 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( + self.env = grid2op.make( "rte_case5_example", test=True, reward_class=L2RPNReward, backend=self.make_backend(), other_rewards={"test": L2RPNReward}, + _add_to_name=type(self).__name__ ) super().setUp() @@ -504,12 +506,13 @@ def test_reset_after_blackout_withdetailed_info(self, env=None): if env is None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( + env = grid2op.make( "rte_case5_example", test=True, reward_class=L2RPNReward, other_rewards={"test": L2RPNReward}, backend=backend, + _add_to_name=type(self).__name__, ) # make the grid in bad shape @@ -545,11 +548,12 @@ def test_attach(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "rte_case5_example", test=True, reward_class=L2RPNReward, other_rewards={"test": L2RPNReward}, + _add_to_name=type(self).__name__, ) as env: env.attach_layout(my_layout) act = env.action_space() @@ -599,11 +603,12 @@ def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( + self.env = grid2op.make( "rte_case14_test", test=True, chronics_class=ChangeNothing, param=self.params, + _add_to_name=type(self).__name__, ) def tearDown(self): @@ -674,12 +679,13 @@ def setUp(self): warnings.filterwarnings("ignore") params = Parameters() params.MAX_SUB_CHANGED = 2 - self.env = make( + self.env = grid2op.make( "rte_case14_test", test=True, chronics_class=ChangeNothing, param=params, backend=self.make_backend(), + _add_to_name=type(self).__name__ ) def tearDown(self): @@ -727,13 +733,14 @@ def setUp(self): params.MAX_SUB_CHANGED = 0 params.NB_TIMESTEP_POWERFLOW_ALLOWED = 2 rules = DefaultRules - self.env = make( + self.env = grid2op.make( "rte_case14_test", test=True, chronics_class=ChangeNothing, param=params, gamerules_class=rules, backend=self.make_backend(), + _add_to_name=type(self).__name__ ) def tearDown(self): @@ -787,8 +794,8 @@ class TestLoading2envDontCrash(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = make("rte_case14_test", test=True) - self.env2 = make("rte_case5_example", test=True) + self.env1 = grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) + self.env2 = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env1.close() @@ -813,7 +820,7 @@ class TestDeactivateForecast(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = make("rte_case14_test", test=True) + self.env1 = grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env1.close() @@ -943,7 +950,7 @@ class TestMaxIter(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env.close() diff --git a/grid2op/tests/test_EnvironmentCpy.py b/grid2op/tests/test_EnvironmentCpy.py index 4cdeff686..00fd38d3b 100644 --- a/grid2op/tests/test_EnvironmentCpy.py +++ b/grid2op/tests/test_EnvironmentCpy.py @@ -16,7 +16,6 @@ from grid2op.tests.helper_path_test import * from grid2op.Reward import L2RPNReward -from grid2op.MakeEnv import make from grid2op.tests.test_Environment import ( TestLoadingBackendPandaPower as Aux_TestLoadingBackendPandaPower, TestIllegalAmbiguous as Aux_TestIllegalAmbiguous, @@ -69,12 +68,13 @@ def test_reset_after_blackout_withdetailed_info(self, env=None): if env is None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( + env = grid2op.make( "rte_case5_example", test=True, reward_class=L2RPNReward, other_rewards={"test": L2RPNReward}, backend=backend, + _add_to_name=type(self).__name__, ) super().test_reset_after_blackout_withdetailed_info(env=env.copy()) diff --git a/grid2op/tests/test_EpisodeData.py b/grid2op/tests/test_EpisodeData.py index 30dccafaf..15f231979 100644 --- a/grid2op/tests/test_EpisodeData.py +++ b/grid2op/tests/test_EpisodeData.py @@ -23,7 +23,6 @@ from grid2op.Agent import BaseAgent from grid2op.Action import TopologyAction from grid2op.Parameters import Parameters -from grid2op.MakeEnv import make from grid2op.Opponent.baseActionBudget import BaseActionBudget from grid2op.Opponent import RandomLineOpponent @@ -124,7 +123,7 @@ def act(self, observation, reward, done=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make("rte_case14_test", test=True) as env: + with grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) as env: my_agent = TestSuitAgent(env.action_space) runner = Runner( **env.get_params_for_runner(), @@ -238,7 +237,7 @@ def test_with_opponent(self): p.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( + env = grid2op.make( "rte_case14_realistic", test=True, param=p, @@ -250,6 +249,7 @@ def test_with_opponent(self): opponent_budget_class=BaseActionBudget, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) env.seed(0) runner = Runner(**env.get_params_for_runner()) diff --git a/grid2op/tests/test_GridGraphObs.py b/grid2op/tests/test_GridGraphObs.py index dd981fb3a..ca299f8c4 100644 --- a/grid2op/tests/test_GridGraphObs.py +++ b/grid2op/tests/test_GridGraphObs.py @@ -22,7 +22,7 @@ class TestNetworkXGraph(unittest.TestCase): def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_neurips_2020_track1", test=True) + self.env = grid2op.make("l2rpn_neurips_2020_track1", test=True, _add_to_name=type(self).__name__) self.tol = 1e-5 def test_kirchhoff(self): diff --git a/grid2op/tests/test_GridObjects.py b/grid2op/tests/test_GridObjects.py index d606e7fe3..a5ee0a493 100644 --- a/grid2op/tests/test_GridObjects.py +++ b/grid2op/tests/test_GridObjects.py @@ -25,7 +25,7 @@ def setUp(self) -> None: self.envref = grid2op.make( "rte_case14_realistic", test=True, - _add_to_name="test_gridobjects_testauxfunctions", + _add_to_name=type(self).__name__+"test_gridobjects_testauxfunctions", ) seed = 0 self.nb_test = 10 diff --git a/grid2op/tests/test_GymConverter.py b/grid2op/tests/test_GymConverter.py index f5bbfec1c..20798a10d 100644 --- a/grid2op/tests/test_GymConverter.py +++ b/grid2op/tests/test_GymConverter.py @@ -15,9 +15,8 @@ GymObservationSpace, GymEnv, ContinuousToDiscreteConverter) from grid2op.tests.helper_path_test import * from grid2op.Action import PlayableAction - +import grid2op from grid2op.dtypes import dt_float, dt_bool, dt_int -from grid2op.MakeEnv import make from grid2op.Converter import IdToAct, ToVect import pdb diff --git a/grid2op/tests/test_MultiProcess.py b/grid2op/tests/test_MultiProcess.py index 2845f67e2..0c1d982c2 100644 --- a/grid2op/tests/test_MultiProcess.py +++ b/grid2op/tests/test_MultiProcess.py @@ -14,7 +14,6 @@ from grid2op.Environment import BaseMultiProcessEnvironment from grid2op.Environment import SingleEnvMultiProcess from grid2op.Environment import MultiEnvMultiProcess -from grid2op.MakeEnv import make from grid2op.Observation import CompleteObservation import pdb @@ -24,7 +23,7 @@ def test_creation_multienv(self): nb_env = 2 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: envs = [env for _ in range(nb_env)] multi_envs = BaseMultiProcessEnvironment(envs) @@ -49,7 +48,7 @@ def test_seeding(self): nb_env = 2 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: envs = [env for _ in range(nb_env)] env.seed(2) multi_envs1 = BaseMultiProcessEnvironment(envs) @@ -69,8 +68,8 @@ def test_simulate(self): env_name = "l2rpn_case14_sandbox" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env1 = grid2op.make(env_name, test=True) - env2 = grid2op.make(env_name, test=True) + env1 = grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) + env2 = grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) multi_env = BaseMultiProcessEnvironment([env1, env2]) obss = multi_env.reset() @@ -88,7 +87,7 @@ def test_creation_multienv(self): nb_env = 2 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: multi_envs = SingleEnvMultiProcess(env=env, nb_env=nb_env) obss, rewards, dones, infos = multi_envs.step( @@ -112,7 +111,7 @@ def test_seeding(self): nb_env = 2 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: env.seed(2) multi_envs1 = SingleEnvMultiProcess(env=env, nb_env=nb_env) seeds_1 = multi_envs1.get_seeds() @@ -133,7 +132,7 @@ def test_creation_multienv(self): nb_envs = [1, 1] with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: envs = [env for _ in range(len(nb_envs))] multi_envs = MultiEnvMultiProcess(envs, nb_envs) @@ -158,7 +157,7 @@ def test_seeding(self): nb_envs = [1, 1] with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: envs = [env for _ in range(len(nb_envs))] env.seed(2) multi_envs1 = MultiEnvMultiProcess(envs, nb_envs) diff --git a/grid2op/tests/test_ObsPlusAct.py b/grid2op/tests/test_ObsPlusAct.py index cae3e2dd7..28b3e125b 100644 --- a/grid2op/tests/test_ObsPlusAct.py +++ b/grid2op/tests/test_ObsPlusAct.py @@ -41,6 +41,7 @@ def setUp(self) -> None: action_class=self.get_act_cls(), param=param, gamerules_class=AlwaysLegal, + _add_to_name=type(self).__name__, ) self.reset_without_pp_futurewarnings() self.act = self.env.action_space() diff --git a/grid2op/tests/test_Observation.py b/grid2op/tests/test_Observation.py index e3cc8fa0a..bc0080857 100644 --- a/grid2op/tests/test_Observation.py +++ b/grid2op/tests/test_Observation.py @@ -25,7 +25,6 @@ RedispReward, RewardHelper, ) -from grid2op.MakeEnv import make from grid2op.Action import CompleteAction, PlayableAction # TODO add unit test for the proper update the backend in the observation [for now there is a "data leakage" as @@ -51,7 +50,7 @@ def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_test", test=True) + self.env = grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) self.dict_ = { "name_gen": ["gen_1_0", "gen_2_1", "gen_5_2", "gen_7_3", "gen_0_4"], "name_load": [ @@ -1810,7 +1809,7 @@ def test_conn_mat2_csr(self): def aux_test_conn_mat3(self, as_csr=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True) + env = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) obs, reward, done, info = env.step( env.action_space({"set_bus": {"lines_or_id": [(7, 2), (8, 2)]}}) ) @@ -1853,7 +1852,7 @@ def test_reactive_flow_bus_matrix(self): def aux_flow_bus_matrix(self, active_flow): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_realistic", test=True) + env = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) obs, reward, done, info = env.step( env.action_space({"set_bus": {"lines_or_id": [(7, 2), (8, 2)]}}) ) @@ -1917,7 +1916,7 @@ def aux_flow_bus_matrix(self, active_flow): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("educ_case14_storage", test=True, action_class=CompleteAction) + env = grid2op.make("educ_case14_storage", test=True, action_class=CompleteAction, _add_to_name=type(self).__name__) obs = env.reset() mat, (load, prod, stor, ind_lor, ind_lex) = obs.flow_bus_matrix( active_flow=active_flow, as_csr_matrix=True @@ -2333,13 +2332,13 @@ def setUp(self): # Create env and obs in left hand with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.lenv = make("rte_case5_example", test=True) + self.lenv = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) self.lobs = self.lenv.reset() # Create env and obs in right hand with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.renv = make("rte_case5_example", test=True) + self.renv = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) # Step once to make it different self.robs, _, _, _ = self.renv.step(self.renv.action_space()) @@ -2462,7 +2461,7 @@ def setUp(self): # Create env with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_realistic", test=True) + self.env = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) self.obs = self._make_forecast_perfect(self.env) self.sim_obs = None @@ -2969,8 +2968,9 @@ def setUp(self): # Create env with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( - "educ_case14_storage", test=True, action_class=PlayableAction + self.env = grid2op.make( + "educ_case14_storage", test=True, action_class=PlayableAction, + _add_to_name=type(self).__name__ ) self.obs = self._make_forecast_perfect(self.env) self.sim_obs = None diff --git a/grid2op/tests/test_Opponent.py b/grid2op/tests/test_Opponent.py index a29c2e8ca..d4a07d981 100644 --- a/grid2op/tests/test_Opponent.py +++ b/grid2op/tests/test_Opponent.py @@ -21,7 +21,6 @@ GeometricOpponent ) from grid2op.Action import TopologyAction -from grid2op.MakeEnv import make from grid2op.Opponent.baseActionBudget import BaseActionBudget from grid2op.dtypes import dt_int from grid2op.Parameters import Parameters @@ -89,14 +88,15 @@ class TestLoadingOpp(unittest.TestCase): def test_creation_BaseOpponent(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: my_opp = BaseOpponent(action_space=env.action_space) def test_env_modif_oppo(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( - "rte_case5_example", test=True, opponent_class=TestSuiteOpponent_001 + with grid2op.make( + "rte_case5_example", test=True, opponent_class=TestSuiteOpponent_001, + _add_to_name=type(self).__name__ ) as env: obs = env.reset() obs, reward, done, info = env.step(env.action_space()) @@ -105,10 +105,11 @@ def test_env_modif_oppo(self): def test_env_modif_oppobudg(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "rte_case5_example", test=True, opponent_budget_class=TestSuiteBudget_001, + _add_to_name=type(self).__name__, ) as env: obs = env.reset() obs, reward, done, info = env.step(env.action_space()) @@ -118,8 +119,9 @@ def test_env_modif_opponent_init_budget(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") init_budg = 100.0 - with make( - "rte_case5_example", test=True, opponent_init_budget=init_budg + with grid2op.make( + "rte_case5_example", test=True, opponent_init_budget=init_budg, + _add_to_name=type(self).__name__ ) as env: obs = env.reset() obs, reward, done, info = env.step(env.action_space()) @@ -129,8 +131,9 @@ def test_env_modif_opponent_init_budget_ts(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") init_budg = 100.0 - with make( - "rte_case5_example", test=True, opponent_budget_per_ts=init_budg + with grid2op.make( + "rte_case5_example", test=True, opponent_budget_per_ts=init_budg, + _add_to_name=type(self).__name__ ) as env: obs = env.reset() obs, reward, done, info = env.step(env.action_space()) @@ -139,8 +142,9 @@ def test_env_modif_opponent_init_budget_ts(self): def test_env_modif_opponent_action_class(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( - "rte_case5_example", test=True, opponent_action_class=TopologyAction + with grid2op.make( + "rte_case5_example", test=True, opponent_action_class=TopologyAction, + _add_to_name=type(self).__name__ ) as env: obs = env.reset() obs, reward, done, info = env.step(env.action_space()) @@ -151,7 +155,7 @@ def test_env_opp_attack(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") init_budg = 100.0 - with make( + with grid2op.make( "rte_case5_example", test=True, opponent_init_budget=init_budg, @@ -160,6 +164,7 @@ def test_env_opp_attack(self): opponent_attack_duration=ATTACK_DURATION, opponent_attack_cooldown=ATTACK_COOLDOWN, opponent_class=TestSuiteOpponent_001, + _add_to_name=type(self).__name__, ) as env: obs = env.reset() # opponent should not attack at the first time step @@ -178,7 +183,7 @@ def test_env_opp_attack_budget_ts(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") init_budg_ts = 0.5 - with make( + with grid2op.make( "rte_case5_example", test=True, opponent_budget_per_ts=init_budg_ts, @@ -187,6 +192,7 @@ def test_env_opp_attack_budget_ts(self): opponent_budget_class=TestSuiteBudget_001, opponent_attack_cooldown=ATTACK_COOLDOWN, opponent_class=TestSuiteOpponent_001, + _add_to_name=type(self).__name__, ) as env: obs = env.reset() assert env._opponent_init_budget == 0.0 @@ -209,7 +215,7 @@ def test_RandomLineOpponent_not_enough_budget(self): init_budget = 50 param = Parameters() param.NO_OVERFLOW_DISCONNECTION = True # otherwise there's a game over - with make( + with grid2op.make( "rte_case14_realistic", test=True, param=param, @@ -221,6 +227,7 @@ def test_RandomLineOpponent_not_enough_budget(self): opponent_attack_duration=ATTACK_DURATION, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -247,7 +254,7 @@ def test_RandomLineOpponent_attackable_lines(self): init_budget = 1000 tries = 30 attackable_lines_case14 = LINES_ATTACKED - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -258,6 +265,7 @@ def test_RandomLineOpponent_attackable_lines(self): opponent_attack_cooldown=ATTACK_COOLDOWN, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__ ) as env: env.seed(0) # Collect some attacks and check that they belong to the correct lines @@ -278,7 +286,7 @@ def test_RandomLineOpponent_disconnects_only_one_line(self): warnings.filterwarnings("ignore") init_budget = 1000 tries = 30 - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -289,6 +297,7 @@ def test_RandomLineOpponent_disconnects_only_one_line(self): opponent_attack_cooldown=ATTACK_COOLDOWN, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__ ) as env: env.seed(0) # Collect some attacks and check that they belong to the correct lines @@ -319,7 +328,7 @@ def test_RandomLineOpponent_with_agent(self): line_opponent_attack = 4 line_opponent_attack = 15 lines_attacked = ["3_6_15"] - with make( + with grid2op.make( "rte_case14_realistic", test=True, param=param, @@ -331,6 +340,7 @@ def test_RandomLineOpponent_with_agent(self): opponent_attack_cooldown=attack_cooldown, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": lines_attacked}, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -389,7 +399,7 @@ def test_RandomLineOpponent_with_maintenance_1(self): # 1. attack is at the same time than the maintenance lines_attacked = ["8_13_11"] - with make( + with grid2op.make( os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, @@ -401,6 +411,7 @@ def test_RandomLineOpponent_with_maintenance_1(self): opponent_attack_cooldown=attack_cooldown, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": lines_attacked}, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -426,7 +437,7 @@ def test_RandomLineOpponent_with_maintenance_1(self): attack_duration = 5 lines_attacked = ["9_10_12"] line_id = 12 - with make( + with grid2op.make( os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, @@ -438,6 +449,7 @@ def test_RandomLineOpponent_with_maintenance_1(self): opponent_attack_cooldown=attack_cooldown, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": lines_attacked}, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -546,7 +558,7 @@ def test_RandomLineOpponent_only_attack_connected(self): warnings.filterwarnings("ignore") init_budget = 10000 length = 300 - env = make( + env = grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -557,6 +569,7 @@ def test_RandomLineOpponent_only_attack_connected(self): opponent_attack_duration=ATTACK_DURATION, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) env.seed(0) # Collect some attacks @@ -617,7 +630,7 @@ def test_RandomLineOpponent_same_attack_order_and_attacks_all_lines(self): ] attack_order = [] has_disconnected_all = False - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -628,6 +641,7 @@ def test_RandomLineOpponent_same_attack_order_and_attacks_all_lines(self): opponent_budget_class=BaseActionBudget, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) as env: env.seed(0) # Collect some attacks and check that they belong to the correct lines @@ -666,7 +680,7 @@ def test_simulate(self): opponent_attack_duration = 15 opponent_attack_cooldown = 20 line_id = 4 - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -677,6 +691,7 @@ def test_simulate(self): opponent_budget_class=BaseActionBudget, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) as env: env.seed(0) reco_line = env.action_space({"set_line_status": [(line_id, 1)]}) @@ -723,19 +738,21 @@ def test_simulate(self): def test_opponent_load(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "rte_case5_example", test=True, opponent_action_class=TopologyAction, opponent_class=RandomLineOpponent, + _add_to_name=type(self).__name__, ) as env_1: env_1.seed(0) obs, reward, done, info = env_1.step(env_1.action_space()) - with make( + with grid2op.make( "rte_case118_example", test=True, opponent_action_class=TopologyAction, opponent_class=RandomLineOpponent, + _add_to_name=type(self).__name__, ) as env_2: env_2.seed(0) obs, reward, done, info = env_2.step(env_2.action_space()) @@ -749,7 +766,7 @@ def test_proper_action_class(self): line_id = 4 opponent_action_class = TopologyAction - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -760,6 +777,7 @@ def test_proper_action_class(self): opponent_budget_class=BaseActionBudget, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) as env: env.seed(0) assert env._opponent_action_class == opponent_action_class @@ -783,7 +801,7 @@ def test_get_set_state(self): opponent_attack_cooldown = 20 line_id = 4 - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -794,6 +812,7 @@ def test_get_set_state(self): opponent_budget_class=BaseActionBudget, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) as env: env.seed(0) @@ -852,7 +871,7 @@ def test_withrunner(self): p = Parameters() p.NO_OVERFLOW_DISCONNECTION = True - env = make( + env = grid2op.make( "rte_case14_realistic", test=True, param=p, @@ -864,6 +883,7 @@ def test_withrunner(self): opponent_budget_class=BaseActionBudget, opponent_class=RandomLineOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) env.seed(0) runner = Runner(**env.get_params_for_runner()) @@ -897,7 +917,7 @@ def test_env_opponent(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_opponent", test=True, param=param) + env = grid2op.make("rte_case14_opponent", test=True, param=param, _add_to_name=type(self).__name__) env.seed(0) # make sure i have reproducible experiments obs = env.reset() assert env._oppSpace.budget == 0 @@ -914,7 +934,7 @@ def test_multienv_opponent(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case14_opponent", test=True, param=param) + env = grid2op.make("rte_case14_opponent", test=True, param=param, _add_to_name=type(self).__name__) env.seed(0) # make sure i have reproducible experiments multi_env = SingleEnvMultiProcess(env=env, nb_env=2) obs = multi_env.reset() @@ -930,7 +950,7 @@ def test_WeightedRandomOpponent_not_enough_budget(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") init_budget = 50 - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_attack_cooldown=1, # only for testing @@ -945,6 +965,7 @@ def test_WeightedRandomOpponent_not_enough_budget(self): "rho_normalization": RHO_NORMALIZATION, "attack_period": 1, }, + _add_to_name=type(self).__name__, ) as env: env.seed(0) obs = env.reset() @@ -971,7 +992,7 @@ def test_WeightedRandomOpponent_attackable_lines(self): init_budget = 1000 tries = 30 attackable_lines_case14 = LINES_ATTACKED - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -986,6 +1007,7 @@ def test_WeightedRandomOpponent_attackable_lines(self): "rho_normalization": RHO_NORMALIZATION, "attack_period": 1, }, + _add_to_name=type(self).__name__, ) as env: env.seed(0) # Collect some attacks and check that they belong to the correct lines @@ -1006,7 +1028,7 @@ def test_WeightedRandomOpponent_disconnects_only_one_line(self): warnings.filterwarnings("ignore") init_budget = 1000 tries = 30 - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -1021,6 +1043,7 @@ def test_WeightedRandomOpponent_disconnects_only_one_line(self): "rho_normalization": RHO_NORMALIZATION, "attack_period": 1, }, + _add_to_name=type(self).__name__, ) as env: env.seed(0) # Collect some attacks and check that they belong to the correct lines @@ -1052,7 +1075,7 @@ def test_WeightedRandomOpponent_with_agent(self): line_opponent_attack = 15 lines_attacked = ["3_6_15"] rho_normalization = [1] - with make( + with grid2op.make( "rte_case14_realistic", test=True, param=param, @@ -1068,6 +1091,7 @@ def test_WeightedRandomOpponent_with_agent(self): "rho_normalization": rho_normalization, "attack_period": 1, }, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -1127,7 +1151,7 @@ def test_WeightedRandomOpponent_with_maintenance_1(self): # 1. attack is at the same time than the maintenance lines_attacked = ["8_13_11"] rho_normalization = [1] - with make( + with grid2op.make( os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, @@ -1143,6 +1167,7 @@ def test_WeightedRandomOpponent_with_maintenance_1(self): "rho_normalization": rho_normalization, "attack_period": 1, }, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -1169,7 +1194,7 @@ def test_WeightedRandomOpponent_with_maintenance_1(self): lines_attacked = ["9_10_12"] rho_normalization = [1] line_id = 12 - with make( + with grid2op.make( os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, @@ -1185,6 +1210,7 @@ def test_WeightedRandomOpponent_with_maintenance_1(self): "rho_normalization": rho_normalization, "attack_period": 1, }, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -1292,7 +1318,7 @@ def test_WeightedRandomOpponent_only_attack_connected(self): warnings.filterwarnings("ignore") init_budget = 10000 length = 300 - env = make( + env = grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -1307,6 +1333,7 @@ def test_WeightedRandomOpponent_only_attack_connected(self): "rho_normalization": RHO_NORMALIZATION, "attack_period": 1, }, + _add_to_name=type(self).__name__ ) env.seed(0) # Collect some attacks @@ -1374,7 +1401,7 @@ def test_WeightedRandomOpponent_same_attack_order_and_attacks_all_lines(self): attack_order = [] has_disconnected_all = False - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -1389,6 +1416,7 @@ def test_WeightedRandomOpponent_same_attack_order_and_attacks_all_lines(self): "rho_normalization": RHO_NORMALIZATION, "attack_period": 1, }, + _add_to_name=type(self).__name__ ) as env: env.seed(0) # Collect some attacks and check that they belong to the correct lines @@ -1424,7 +1452,7 @@ def test_either_attack_or_tell_attack_continues(self): init_budget = 1000 length = 100 attack_cooldown = 15 - with make( + with grid2op.make( "rte_case14_realistic", test=True, opponent_init_budget=init_budget, @@ -1439,6 +1467,7 @@ def test_either_attack_or_tell_attack_continues(self): "rho_normalization": RHO_NORMALIZATION, "attack_period": attack_cooldown, }, + _add_to_name=type(self).__name__ ) as env: env.seed(0) # Collect some attacks and check that they belong to the correct lines @@ -1462,7 +1491,7 @@ class TestGeometricOpponent(unittest.TestCase): def test_can_create(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: my_opp = GeometricOpponent(action_space=env.action_space) def test_can_init(self): @@ -1470,7 +1499,7 @@ def test_can_init(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "l2rpn_case14_sandbox", test=True, opponent_init_budget=init_budget, @@ -1481,6 +1510,7 @@ def test_can_init(self): opponent_budget_class=BaseActionBudget, opponent_class=GeometricOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) as env: env.seed(0) obs = env.reset() @@ -1490,7 +1520,7 @@ def test_does_attack_outsideenv(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "l2rpn_case14_sandbox", test=True, opponent_init_budget=init_budget, @@ -1501,6 +1531,7 @@ def test_does_attack_outsideenv(self): opponent_budget_class=BaseActionBudget, opponent_class=GeometricOpponent, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -1569,7 +1600,7 @@ def test_does_attack(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "l2rpn_case14_sandbox", test=True, opponent_init_budget=init_budget, @@ -1581,6 +1612,7 @@ def test_does_attack(self): opponent_class=GeometricOpponent, param=param, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -1648,7 +1680,7 @@ def test_minimum_attack_duration(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "l2rpn_case14_sandbox", test=True, opponent_init_budget=init_budget, @@ -1665,6 +1697,7 @@ def test_minimum_attack_duration(self): "average_attack_duration_hour": 5, "minimum_attack_duration_hour": 4, }, + _add_to_name=type(self).__name__, ) as env: env.seed(0) obs = env.reset() @@ -1675,7 +1708,7 @@ def test_minimum_attack_duration(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "l2rpn_case14_sandbox", test=True, opponent_init_budget=init_budget, @@ -1692,6 +1725,7 @@ def test_minimum_attack_duration(self): "average_attack_duration_hour": 5, "minimum_attack_duration_hour": 1, }, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -1702,7 +1736,7 @@ def test_minimum_attack_duration(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "l2rpn_case14_sandbox", test=True, opponent_init_budget=init_budget, @@ -1719,6 +1753,7 @@ def test_minimum_attack_duration(self): "average_attack_duration_hour": 31, "minimum_attack_duration_hour": 30, }, + _add_to_name=type(self).__name__ ) as env: env.seed(0) obs = env.reset() @@ -1730,8 +1765,9 @@ def test_minimum_attack_duration(self): def test_average_attack_duration(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( - "rte_case5_example", test=True, chronics_class=ChangeNothing + with grid2op.make( + "rte_case5_example", test=True, chronics_class=ChangeNothing, + _add_to_name=type(self).__name__ ) as env: my_opp = GeometricOpponent(action_space=env.action_space) with self.assertRaises(OpponentError): @@ -1762,8 +1798,9 @@ def test_average_attack_duration(self): def test_attack_every_xxx_hour(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( - "rte_case5_example", test=True, chronics_class=ChangeNothing + with grid2op.make( + "rte_case5_example", test=True, chronics_class=ChangeNothing, + _add_to_name=type(self).__name__ ) as env: my_opp = GeometricOpponent(action_space=env.action_space) n_ = 3_000_000 @@ -1794,7 +1831,7 @@ def test_attack_every_xxx_hour(self): def test_cannot_init_with_wrong_param(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: my_opp = GeometricOpponent(action_space=env.action_space) with self.assertRaises(OpponentError): @@ -1827,7 +1864,7 @@ def test_simulate(self): first_attack_ts = 64 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "l2rpn_case14_sandbox", test=True, opponent_init_budget=init_budget, @@ -1839,6 +1876,7 @@ def test_simulate(self): opponent_class=GeometricOpponent, param=param, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) as env: reco_line = env.action_space({"set_line_status": [(line_id, 1)]}) env.seed(0) @@ -1897,7 +1935,7 @@ def test_last_attack(self): param.NO_OVERFLOW_DISCONNECTION = True with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( "l2rpn_case14_sandbox", test=True, opponent_init_budget=init_budget, @@ -1909,6 +1947,7 @@ def test_last_attack(self): opponent_class=GeometricOpponent, param=param, kwargs_opponent={"lines_attacked": LINES_ATTACKED}, + _add_to_name=type(self).__name__, ) as env: env.seed(0) _ = env.reset() @@ -1935,11 +1974,11 @@ class OpponentSpaceCust(OpponentSpace): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("l2rpn_icaps_2021", test=True) + env = grid2op.make("l2rpn_icaps_2021", test=True, _add_to_name=type(self).__name__) assert isinstance(env._oppSpace, OpponentSpace) # check i can change it from "make" - env = grid2op.make("l2rpn_icaps_2021", opponent_space_type=OpponentSpaceCust, test=True) + env = grid2op.make("l2rpn_icaps_2021", opponent_space_type=OpponentSpaceCust, test=True, _add_to_name=type(self).__name__) assert isinstance(env._oppSpace, OpponentSpaceCust) # check it's properly propagated when copied env_cpy = env.copy() diff --git a/grid2op/tests/test_PlotGrid.py b/grid2op/tests/test_PlotGrid.py index 9baa347ff..4b7cb963a 100644 --- a/grid2op/tests/test_PlotGrid.py +++ b/grid2op/tests/test_PlotGrid.py @@ -11,7 +11,6 @@ import warnings from grid2op.Exceptions import * -from grid2op.MakeEnv import make from grid2op.PlotGrid import * @@ -19,7 +18,7 @@ class BaseTestPlot(ABC): def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("rte_case14_redisp", test=True) + self.env = grid2op.make("rte_case14_redisp", test=True, _add_to_name=type(self).__name__) self.obs = self.env.current_obs self.plot = self._plotter(self.env.observation_space) diff --git a/grid2op/tests/test_Reward.py b/grid2op/tests/test_Reward.py index eef818aed..c76c55f03 100644 --- a/grid2op/tests/test_Reward.py +++ b/grid2op/tests/test_Reward.py @@ -15,7 +15,6 @@ import grid2op from grid2op.tests.helper_path_test import * from grid2op.Reward import * -from grid2op.MakeEnv import make from grid2op.Parameters import Parameters from grid2op.Runner import Runner from grid2op.Agent import BaseAgent @@ -27,8 +26,9 @@ class TestLoadingReward(ABC): def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( - "rte_case5_example", test=True, reward_class=self._reward_type() + self.env = grid2op.make( + "rte_case5_example", test=True, reward_class=self._reward_type(), + _add_to_name=type(self).__name__ ) self.action = self.env.action_space() @@ -193,8 +193,9 @@ class TestIncreaseFlatReward(unittest.TestCase): def test_ok(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( - "l2rpn_case14_sandbox", reward_class=IncreasingFlatReward, test=True + env = grid2op.make( + "l2rpn_case14_sandbox", reward_class=IncreasingFlatReward, test=True, + _add_to_name=type(self).__name__ ) assert env.nb_time_step == 0 @@ -215,8 +216,9 @@ class TestEpisodeDurationReward(unittest.TestCase): def test_ok(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( - "l2rpn_case14_sandbox", reward_class=EpisodeDurationReward, test=True + env = grid2op.make( + "l2rpn_case14_sandbox", reward_class=EpisodeDurationReward, test=True, + _add_to_name=type(self).__name__ ) assert env.nb_time_step == 0 @@ -253,8 +255,9 @@ def test_ok(self): L_ID = 2 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( - "l2rpn_case14_sandbox", reward_class=N1Reward(l_id=L_ID), test=True + env = grid2op.make( + "l2rpn_case14_sandbox", reward_class=N1Reward(l_id=L_ID), test=True, + _add_to_name=type(self).__name__ ) obs = env.reset() @@ -276,6 +279,7 @@ def test_ok(self): "l2rpn_case14_sandbox", other_rewards={f"line_{l_id}": N1Reward(l_id=l_id) for l_id in L_IDS}, test=True, + _add_to_name=type(self).__name__, ) obs, reward, done, info = env.step(env.action_space()) for l_id in L_IDS: @@ -308,7 +312,8 @@ def setUp(self) -> None: param = Parameters() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True, reward_class=TMPRewardForTest) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, + reward_class=TMPRewardForTest, _add_to_name=type(self).__name__) return super().setUp() def tearDown(self) -> None: diff --git a/grid2op/tests/test_RewardAlertCostScore.py b/grid2op/tests/test_RewardAlertCostScore.py index 160872fa6..1c594a0a8 100644 --- a/grid2op/tests/test_RewardAlertCostScore.py +++ b/grid2op/tests/test_RewardAlertCostScore.py @@ -65,7 +65,8 @@ def test_assistant_reward_value_no_blackout_no_attack_no_alert(self) -> None : self.env_nm, test=True, difficulty="1", - reward_class=_AlertCostScore + reward_class=_AlertCostScore, + _add_to_name=type(self).__name__ ) as env: env.seed(0) env.reset() @@ -85,7 +86,8 @@ def setUp(self) -> None: PATH_DATA_TEST, "l2rpn_idf_2023_with_alert" ) self.env = grid2op.make(self.env_nm, test=True, difficulty="1", - reward_class=_AlertCostScore) + reward_class=_AlertCostScore, + _add_to_name=type(self).__name__) self.env.seed(0) return super().setUp() @@ -126,7 +128,8 @@ def setUp(self) -> None: PATH_DATA_TEST, "l2rpn_idf_2023_with_alert" ) self.env = grid2op.make(self.env_nm, test=True, difficulty="1", - reward_class=_AlertCostScore) + reward_class=_AlertCostScore, + _add_to_name=type(self).__name__) self.env.seed(0) return super().setUp() diff --git a/grid2op/tests/test_RewardNewRenewableSourcesUsageScore.py b/grid2op/tests/test_RewardNewRenewableSourcesUsageScore.py index 53b7974f0..b09188f44 100644 --- a/grid2op/tests/test_RewardNewRenewableSourcesUsageScore.py +++ b/grid2op/tests/test_RewardNewRenewableSourcesUsageScore.py @@ -50,7 +50,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make(env_name, reward_class=_NewRenewableSourcesUsageScore, - test=True + test=True, + _add_to_name=type(self).__name__ ) self.env.set_max_iter(20) self.env.parameters.NO_OVERFLOW_DISCONNECTION = True @@ -76,7 +77,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make(env_name, reward_class = _NewRenewableSourcesUsageScore, - test=True + test=True, + _add_to_name=type(self).__name__ ) self.env.set_max_iter(20) self.env.parameters.NO_OVERFLOW_DISCONNECTION = True diff --git a/grid2op/tests/test_Rules.py b/grid2op/tests/test_Rules.py index 77a1810d9..6e3c5b2a8 100644 --- a/grid2op/tests/test_Rules.py +++ b/grid2op/tests/test_Rules.py @@ -12,6 +12,7 @@ from grid2op.tests.helper_path_test import * +import grid2op from grid2op.dtypes import dt_int, dt_bool, dt_float from grid2op.Exceptions import * from grid2op.Environment import Environment @@ -19,11 +20,6 @@ from grid2op.Parameters import Parameters from grid2op.Chronics import ChronicsHandler, GridStateFromFile from grid2op.Rules import * -from grid2op.MakeEnv import make - -import warnings - -warnings.simplefilter("error") class TestLoadingBackendFunc(unittest.TestCase): @@ -532,11 +528,12 @@ def setUp(self): params.NB_TIMESTEP_COOLDOWN_SUB = 15 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( + self.env = grid2op.make( "rte_case5_example", test=True, gamerules_class=DefaultRules, param=params, + _add_to_name=type(self).__name__ ) def tearDown(self): @@ -577,7 +574,7 @@ class TestReconnectionsLegality(unittest.TestCase): def test_reconnect_already_connected(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_case2 = make("rte_case5_example", test=True) + env_case2 = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) obs = env_case2.reset() # reset is good obs, reward, done, info = env_case2.step( env_case2.action_space() @@ -600,7 +597,7 @@ def test_reconnect_disconnected(self): params = Parameters() params.MAX_SUB_CHANGED = 0 params.NO_OVERFLOW_DISCONNECTION = True - env_case2 = make("rte_case5_example", test=True, param=params) + env_case2 = grid2op.make("rte_case5_example", test=True, param=params, _add_to_name=type(self).__name__) obs = env_case2.reset() # reset is good line_id = 5 @@ -633,7 +630,7 @@ def test_sub_dont_change(self): params.NB_TIMESTEP_COOLDOWN_LINE = 3 params.NB_TIMESTEP_COOLDOWN_SUB = 3 params.NO_OVERFLOW_DISCONNECTION = True - env = make("rte_case5_example", test=True, param=params) + env = grid2op.make("rte_case5_example", test=True, param=params, _add_to_name=type(self).__name__) l_id = 2 # prepare the actions disco_act = env.action_space.disconnect_powerline(line_id=l_id) @@ -686,7 +683,7 @@ def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.params = Parameters() - self.env = make("rte_case5_example", test=True, param=self.params) + self.env = grid2op.make("rte_case5_example", test=True, param=self.params, _add_to_name=type(self).__name__) def tearDown(self): self.env.close() @@ -793,7 +790,7 @@ def test_correct(self): try: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("rte_case5_example", test=True, gamerules_class=rules) + env = grid2op.make("rte_case5_example", test=True, gamerules_class=rules, _add_to_name=type(self).__name__) assert hasattr(env._game_rules.legal_action, "TOTO") assert env._game_rules.legal_action.TOTO == 1 finally: diff --git a/grid2op/tests/test_RulesByArea.py b/grid2op/tests/test_RulesByArea.py index b4793fa65..deb182850 100644 --- a/grid2op/tests/test_RulesByArea.py +++ b/grid2op/tests/test_RulesByArea.py @@ -10,10 +10,10 @@ import unittest from grid2op.tests.helper_path_test import * +import grid2op from grid2op.Exceptions import * from grid2op.Parameters import Parameters from grid2op.Rules.rulesByArea import * -from grid2op.MakeEnv import make from grid2op.Agent import OneChangeThenNothing from grid2op.Runner import Runner @@ -31,11 +31,12 @@ def test_legal_when_islegal(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") #noaction - self.env = make( + self.env = grid2op.make( "l2rpn_case14_sandbox", test=True, param=params, - gamerules_class = self.rules_1area + gamerules_class = self.rules_1area, + _add_to_name=type(self).__name__ ) self.helper_action = self.env._helper_action_env self.env._parameters.MAX_SUB_CHANGED = 1 @@ -49,11 +50,12 @@ def test_legal_when_islegal(self): #test allowance max action in all areas over the grid for rules in [self.rules_2areas, self.rules_3areas]: - self.env = make( + self.env = grid2op.make( "l2rpn_case14_sandbox", test=True, param=params, - gamerules_class = rules + gamerules_class = rules, + _add_to_name=type(self).__name__ ) self.helper_action = self.env._helper_action_env lines_by_area = self.env._game_rules.legal_action.lines_id_by_area @@ -103,11 +105,12 @@ def test_illegal_when_illegal(self): params = Parameters() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( + self.env = grid2op.make( "l2rpn_case14_sandbox", test=True, param=params, - gamerules_class = self.rules_3areas + gamerules_class = self.rules_3areas, + _add_to_name=type(self).__name__ ) self.env._parameters.MAX_SUB_CHANGED = 1 self.env._parameters.MAX_LINE_STATUS_CHANGED = 1 @@ -150,11 +153,12 @@ def test_catch_runner_area_action_illegality(self): nn_episode = 1 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make( + self.env = grid2op.make( "l2rpn_case14_sandbox", test=True, param=params, - gamerules_class = self.rules_3areas + gamerules_class = self.rules_3areas, + _add_to_name=type(self).__name__ ) lines_by_area = [list_ids for list_ids in self.env._game_rules.legal_action.lines_id_by_area.values()] diff --git a/grid2op/tests/test_Runner.py b/grid2op/tests/test_Runner.py index c27440120..3f07aa996 100644 --- a/grid2op/tests/test_Runner.py +++ b/grid2op/tests/test_Runner.py @@ -24,7 +24,6 @@ from grid2op.Chronics import Multifolder, ChangeNothing from grid2op.Reward import L2RPNReward, N1Reward from grid2op.Backend import PandaPowerBackend -from grid2op.MakeEnv import make from grid2op.Runner.aux_fun import _aux_one_process_parrallel from grid2op.Runner import Runner from grid2op.dtypes import dt_float @@ -257,7 +256,7 @@ def test_multiprocess_windows_no_fail(self): nb_episode = 2 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: f = tempfile.mkdtemp() runner_params = env.get_params_for_runner() runner = Runner(**runner_params) @@ -296,7 +295,7 @@ def test_complex_agent(self): nb_episode = 4 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: f = tempfile.mkdtemp() runner_params = env.get_params_for_runner() runner = Runner(**runner_params) @@ -314,8 +313,9 @@ def test_complex_agent(self): def test_init_from_env_with_other_reward(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( - "rte_case14_test", test=True, other_rewards={"test": L2RPNReward} + with grid2op.make( + "rte_case14_test", test=True, other_rewards={"test": L2RPNReward}, + _add_to_name=type(self).__name__ ) as env: runner = Runner(**env.get_params_for_runner()) res = runner.run(nb_episode=1, max_iter=self.max_iter) @@ -334,7 +334,7 @@ def seed(self, seed): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_test", test=True) as env: + with grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) as env: my_agent = TestSuitAgent(env.action_space) runner = Runner( **env.get_params_for_runner(), @@ -361,7 +361,7 @@ def test_always_same_order(self): # regardless of the seed or the parallelism or the number of call to runner.run with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_test", test=True) as env: + with grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) as env: runner = Runner(**env.get_params_for_runner()) res = runner.run( nb_episode=2, @@ -398,7 +398,7 @@ def test_always_same_order(self): def test_nomaxiter(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_test", test=True) as env: + with grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) as env: runner = Runner(**env.get_params_for_runner()) runner.gridStateclass_kwargs["max_iter"] = 2 * self.max_iter runner.chronics_handler.set_max_iter(2 * self.max_iter) @@ -409,7 +409,7 @@ def test_nomaxiter(self): def test_nomaxiter_par(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_test", test=True) as env: + with grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) as env: dict_ = env.get_params_for_runner() dict_["max_iter"] = -1 sub_dict = dict_["gridStateclass_kwargs"] @@ -508,8 +508,9 @@ def test_backward_compatibility(self): ), "error at the beginning" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( - "rte_case5_example", test=True + with grid2op.make( + "rte_case5_example", test=True, + _add_to_name=type(self).__name__ ) as env, tempfile.TemporaryDirectory() as path: runner = Runner(**env.get_params_for_runner(), agentClass=RandomAgent) runner.run( @@ -557,8 +558,9 @@ def test_reward_as_object(self): L_ID = 2 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make( - "l2rpn_case14_sandbox", reward_class=N1Reward(l_id=L_ID), test=True + env = grid2op.make( + "l2rpn_case14_sandbox", reward_class=N1Reward(l_id=L_ID), test=True, + _add_to_name=type(self).__name__ ) runner = Runner(**env.get_params_for_runner()) runner.run(nb_episode=1, max_iter=10) @@ -570,6 +572,7 @@ def test_reward_as_object(self): "l2rpn_case14_sandbox", other_rewards={f"line_{l_id}": N1Reward(l_id=l_id) for l_id in [0, 1]}, test=True, + _add_to_name=type(self).__name__ ) runner = Runner(**env.get_params_for_runner()) @@ -579,7 +582,7 @@ def test_reward_as_object(self): def test_legal_ambiguous_regular(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("l2rpn_case14_sandbox", test=True) + env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) runner = Runner(**env.get_params_for_runner(), agentClass=AgentTestLegalAmbiguous) env.close() @@ -599,7 +602,8 @@ def test_legal_ambiguous_regular(self): def test_legal_ambiguous_nofaststorage(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("l2rpn_case14_sandbox", test=True, chronics_class=ChangeNothing) + env = grid2op.make("l2rpn_case14_sandbox", test=True, chronics_class=ChangeNothing, + _add_to_name=type(self).__name__) runner = Runner(**env.get_params_for_runner(), agentClass=AgentTestLegalAmbiguous) env.close() diff --git a/grid2op/tests/test_RunnerFast.py b/grid2op/tests/test_RunnerFast.py index 92e1100ac..1da9d05f4 100644 --- a/grid2op/tests/test_RunnerFast.py +++ b/grid2op/tests/test_RunnerFast.py @@ -17,7 +17,6 @@ PATH_PREVIOUS_RUNNER = os.path.join(data_test_dir, "runner_data") import grid2op -from grid2op.MakeEnv import make from grid2op.Runner import Runner from grid2op.dtypes import dt_float @@ -51,7 +50,7 @@ def setUp(self): ] with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.runner = Runner(**self.env.get_params_for_runner()) def test_one_episode(self): @@ -89,7 +88,7 @@ def test_2episode(self): def test_init_from_env(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_test", test=True) as env: + with grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) as env: runner = Runner(**env.get_params_for_runner()) res = runner.run(nb_episode=1, max_iter=self.max_iter) for i, _, cum_reward, timestep, total_ts in res: @@ -98,7 +97,7 @@ def test_init_from_env(self): def test_seed_seq(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_test", test=True) as env: + with grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) as env: runner = Runner(**env.get_params_for_runner()) res = runner.run( nb_episode=1, max_iter=self.max_iter, env_seeds=[1], agent_seeds=[2] @@ -109,7 +108,7 @@ def test_seed_seq(self): def test_seed_par(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case14_test", test=True) as env: + with grid2op.make("rte_case14_test", test=True, _add_to_name=type(self).__name__) as env: runner = Runner(**env.get_params_for_runner()) res = runner.run( nb_episode=2, diff --git a/grid2op/tests/test_Storage.py b/grid2op/tests/test_Storage.py index a1666971a..26c75cd10 100644 --- a/grid2op/tests/test_Storage.py +++ b/grid2op/tests/test_Storage.py @@ -26,7 +26,7 @@ def setUp(self) -> None: super().setUp() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("educ_case14_storage", test=True) + self.env = grid2op.make("educ_case14_storage", test=True, _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env.close() @@ -50,7 +50,7 @@ def test_init_storage_ok(self): param.INIT_STORAGE_CAPACITY = 0.0 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True, param=param) + env = grid2op.make("educ_case14_storage", test=True, param=param, _add_to_name=type(self).__name__) obs = env.reset() assert np.all(np.abs(obs.storage_charge) <= self.tol_one) @@ -58,7 +58,7 @@ def test_init_storage_ok(self): param.INIT_STORAGE_CAPACITY = 1.0 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True, param=param) + env = grid2op.make("educ_case14_storage", test=True, param=param, _add_to_name=type(self).__name__) obs = env.reset() assert np.all(np.abs(obs.storage_charge - obs.storage_Emax) <= self.tol_one) @@ -248,7 +248,7 @@ def test_activate_storage_loss(self): param.ACTIVATE_STORAGE_LOSS = False with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True, param=param) + env = grid2op.make("educ_case14_storage", test=True, param=param, _add_to_name=type(self).__name__) obs = env.get_obs() assert np.all( np.abs(obs.storage_charge - 0.5 * obs.storage_Emax) <= self.tol_one @@ -270,7 +270,7 @@ def test_storage_loss_dont_make_negative(self): param.INIT_STORAGE_CAPACITY = init_coeff with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True, param=param) + env = grid2op.make("educ_case14_storage", test=True, param=param, _add_to_name=type(self).__name__) obs = env.get_obs() init_charge = init_coeff * obs.storage_Emax loss = 1.0 * env.storage_loss @@ -371,7 +371,7 @@ def test_env_storage_cut_because_too_high_noloss(self): param.INIT_STORAGE_CAPACITY = init_coeff with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True, param=param) + env = grid2op.make("educ_case14_storage", test=True, param=param, _add_to_name=type(self).__name__) self.env.close() self.env = env @@ -448,7 +448,7 @@ def test_env_storage_cut_because_too_high_withloss(self): param.INIT_STORAGE_CAPACITY = init_coeff with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True, param=param) + env = grid2op.make("educ_case14_storage", test=True, param=param, _add_to_name=type(self).__name__) self.env.close() self.env = env @@ -549,7 +549,7 @@ def test_env_storage_cut_because_too_low_noloss(self): param.INIT_STORAGE_CAPACITY = init_coeff with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True, param=param) + env = grid2op.make("educ_case14_storage", test=True, param=param, _add_to_name=type(self).__name__) self.env.close() self.env = env @@ -626,7 +626,7 @@ def test_env_storage_cut_because_too_low_withloss(self): param.INIT_STORAGE_CAPACITY = init_coeff with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_storage", test=True, param=param) + env = grid2op.make("educ_case14_storage", test=True, param=param, _add_to_name=type(self).__name__) self.env.close() self.env = env @@ -823,6 +823,7 @@ def test_storage_action_topo(self): test=True, action_class=CompleteAction, param=param, + _add_to_name=type(self).__name__, ) self.env.close() self.env = env diff --git a/grid2op/tests/test_VoltageControler.py b/grid2op/tests/test_VoltageControler.py index 2103021c3..1eddab269 100644 --- a/grid2op/tests/test_VoltageControler.py +++ b/grid2op/tests/test_VoltageControler.py @@ -9,16 +9,17 @@ import pdb import unittest import warnings + from grid2op.tests.helper_path_test import * +import grid2op from grid2op.VoltageControler import ControlVoltageFromFile -from grid2op.MakeEnv import make class TestLoadingVoltageControl(unittest.TestCase): def test_creation_ControlVoltage(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: volt_cont = ControlVoltageFromFile( controler_backend=env.backend, gridobj=env.backend, @@ -28,7 +29,7 @@ def test_creation_ControlVoltage(self): def test_copy(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: volt_cont = ControlVoltageFromFile( controler_backend=env.backend, gridobj=env.backend, diff --git a/grid2op/tests/test_act_as_serializable_dict.py b/grid2op/tests/test_act_as_serializable_dict.py index 479f8e7f8..e9590714f 100644 --- a/grid2op/tests/test_act_as_serializable_dict.py +++ b/grid2op/tests/test_act_as_serializable_dict.py @@ -382,11 +382,11 @@ def setUp(self) -> None: self.env1 = grid2op.make("l2rpn_case14_sandbox", test=True, action_class=PlayableAction, - _add_to_name="_TestMultiGrid") + _add_to_name=type(self).__name__+"env1") self.env2 = grid2op.make("educ_case14_storage", test=True, action_class=PlayableAction, - _add_to_name="_TestMultiGrid") + _add_to_name=type(self).__name__+"env2") return super().setUp() def tearDown(self) -> None: self.env1.close() diff --git a/grid2op/tests/test_action_space.py b/grid2op/tests/test_action_space.py index 83170994d..387abfb6f 100644 --- a/grid2op/tests/test_action_space.py +++ b/grid2op/tests/test_action_space.py @@ -20,7 +20,7 @@ class BasicTestActSpace(unittest.TestCase): def test_is_legal_None(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("l2rpn_case14_sandbox", test=True) + env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) is_legal, reason = env.action_space._is_legal(None, None) assert reason is None diff --git a/grid2op/tests/test_alert_gym_compat.py b/grid2op/tests/test_alert_gym_compat.py index 4c84a15a2..e522deee5 100644 --- a/grid2op/tests/test_alert_gym_compat.py +++ b/grid2op/tests/test_alert_gym_compat.py @@ -56,7 +56,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - self.env_nm, test=True, _add_to_name="TestGymAlertCompat" + self.env_nm, test=True, _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.reset() # seed part ! @@ -246,7 +246,7 @@ def test_low_high_alert_obs_space(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "l2rpn_idf_2023", test=True, _add_to_name="TestGymCompatModule" + "l2rpn_idf_2023", test=True, _add_to_name=type(self).__name__ ) env.seed(0) env.reset() # seed part ! @@ -304,7 +304,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make( "l2rpn_idf_2023", - test=True + test=True, + _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.reset() # seed part ! @@ -349,7 +350,7 @@ def setUp(self) -> None: os.path.join(PATH_DATA_TEST, "l2rpn_idf_2023_with_alert"), test=True, action_class=PlayableAction, - _add_to_name="TestAllGymActSpaceWithAlert", + _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.reset() # seed part ! @@ -431,7 +432,7 @@ def test_alert_attr_in_obs(self): warnings.filterwarnings("ignore") env = grid2op.make("l2rpn_idf_2023", test=True, action_class=PlayableAction, - _add_to_name="ObsAlertAttr") + _add_to_name=type(self).__name__) gym_env = GymEnv(env) obs, info = gym_env.reset() alert_attrs = [ diff --git a/grid2op/tests/test_attached_envs.py b/grid2op/tests/test_attached_envs.py index 028878c94..d9c0742bc 100644 --- a/grid2op/tests/test_attached_envs.py +++ b/grid2op/tests/test_attached_envs.py @@ -23,7 +23,7 @@ class TestL2RPNNEURIPS2020_Track1(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_neurips_2020_track1", test=True) + self.env = grid2op.make("l2rpn_neurips_2020_track1", test=True, _add_to_name=type(self).__name__) self.env.seed(0) _ = self.env.reset() @@ -67,7 +67,7 @@ class TestL2RPNICAPS2021(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_icaps_2021", test=True) + self.env = grid2op.make("l2rpn_icaps_2021", test=True, _add_to_name=type(self).__name__) self.env.seed(0) _ = self.env.reset() @@ -116,7 +116,7 @@ class TestL2RPNNEURIPS2020_Track2(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_neurips_2020_track2", test=True) + self.env = grid2op.make("l2rpn_neurips_2020_track2", test=True, _add_to_name=type(self).__name__) self.env.seed(2) # 0 or 1 breaks the test `test_random_action` _ = self.env.reset() @@ -160,7 +160,7 @@ class TestL2RPN_CASE14_SANDBOX(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(42) _ = self.env.reset() @@ -204,7 +204,7 @@ class TestEDUC_CASE14_REDISP(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("educ_case14_redisp", test=True) + self.env = grid2op.make("educ_case14_redisp", test=True, _add_to_name=type(self).__name__) self.env.seed(0) _ = self.env.reset() @@ -248,7 +248,7 @@ class TestEDUC_STORAGE(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("educ_case14_storage", test=True) + self.env = grid2op.make("educ_case14_storage", test=True, _add_to_name=type(self).__name__) self.env.seed(0) _ = self.env.reset() diff --git a/grid2op/tests/test_attached_envs_compat.py b/grid2op/tests/test_attached_envs_compat.py index 6c1850ab6..65b170cd2 100644 --- a/grid2op/tests/test_attached_envs_compat.py +++ b/grid2op/tests/test_attached_envs_compat.py @@ -29,7 +29,7 @@ def setUp(self) -> None: "l2rpn_neurips_2020_track1", test=True, _compat_glop_version=GridObjects.BEFORE_COMPAT_VERSION, - _add_to_name="test_attached_compat_0", + _add_to_name=type(self).__name__+"test_attached_compat_0", ) self.env.seed(0) @@ -74,7 +74,7 @@ def setUp(self) -> None: "l2rpn_neurips_2020_track2", test=True, _compat_glop_version=GridObjects.BEFORE_COMPAT_VERSION, - _add_to_name="test_attached_compat_1", + _add_to_name=type(self).__name__+"test_attached_compat_1", ) self.env.seed(0) @@ -122,7 +122,7 @@ def setUp(self) -> None: "l2rpn_case14_sandbox", test=True, _compat_glop_version=GridObjects.BEFORE_COMPAT_VERSION, - _add_to_name="test_attached_compat_2", + _add_to_name=type(self).__name__+"test_attached_compat_2", ) self.env.seed(42) @@ -167,7 +167,7 @@ def setUp(self) -> None: "educ_case14_redisp", test=True, _compat_glop_version=GridObjects.BEFORE_COMPAT_VERSION, - _add_to_name="test_attached_compat_3", + _add_to_name=type(self).__name__+"test_attached_compat_3", ) self.env.seed(0) @@ -212,7 +212,7 @@ def setUp(self) -> None: "educ_case14_storage", test=True, _compat_glop_version=GridObjects.BEFORE_COMPAT_VERSION, - _add_to_name="test_attached_compat_4", + _add_to_name=type(self).__name__+"test_attached_compat_4", ) self.env.seed(0) @@ -239,7 +239,7 @@ def test_same_env_as_no_storage(self): res = 0 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("educ_case14_redisp", test=True) + env = grid2op.make("educ_case14_redisp", test=True, _add_to_name=type(self).__name__+"test_same_env_as_no_storage") for attr in self.env.observation_space.attr_list_vect: tmp = getattr(self.env.observation_space._template_obj, attr).shape tmp2 = getattr(env.observation_space._template_obj, attr).shape diff --git a/grid2op/tests/test_back_to_orig.py b/grid2op/tests/test_back_to_orig.py index 9c6684ea2..479759cf0 100644 --- a/grid2op/tests/test_back_to_orig.py +++ b/grid2op/tests/test_back_to_orig.py @@ -30,7 +30,8 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - self.env_name, test=True, action_class=BaseAction, param=param + self.env_name, test=True, action_class=BaseAction, param=param, + _add_to_name=type(self).__name__ ) def tearDown(self) -> None: diff --git a/grid2op/tests/test_change_param_from_obs.py b/grid2op/tests/test_change_param_from_obs.py index 8a0b124f6..7f20d222c 100644 --- a/grid2op/tests/test_change_param_from_obs.py +++ b/grid2op/tests/test_change_param_from_obs.py @@ -16,7 +16,7 @@ class TestChangeParamFromObs(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) return super().setUp() def tearDown(self) -> None: diff --git a/grid2op/tests/test_chronics_npy.py b/grid2op/tests/test_chronics_npy.py index e02ffa931..f1173a980 100644 --- a/grid2op/tests/test_chronics_npy.py +++ b/grid2op/tests/test_chronics_npy.py @@ -29,7 +29,7 @@ def setUp(self): self.env_name = "l2rpn_case14_sandbox" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env_ref = grid2op.make(self.env_name, test=True) + self.env_ref = grid2op.make(self.env_name, test=True, _add_to_name=type(self).__name__) self.load_p = 1.0 * self.env_ref.chronics_handler.real_data.data.load_p self.load_q = 1.0 * self.env_ref.chronics_handler.real_data.data.load_q @@ -436,7 +436,7 @@ def test_maintenance_ok(self): env_path = os.path.join(PATH_DATA_TEST, "env_14_test_maintenance") with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_ref = grid2op.make(env_path, test=True, param=param) + env_ref = grid2op.make(env_path, test=True, param=param, _add_to_name=type(self).__name__) env_ref.chronics_handler.real_data.data.maintenance_starting_hour = 1 env_ref.chronics_handler.real_data.data.maintenance_ending_hour = 2 env_ref.seed(0) # 1 -> 108 @@ -456,6 +456,7 @@ def test_maintenance_ok(self): env_path, chronics_class=FromNPY, test=True, + _add_to_name=type(self).__name__, data_feeding_kwargs={ "i_start": 0, "i_end": 10, # excluded diff --git a/grid2op/tests/test_copy_env_close.py b/grid2op/tests/test_copy_env_close.py index 0e5ca9e86..d48cc8d17 100644 --- a/grid2op/tests/test_copy_env_close.py +++ b/grid2op/tests/test_copy_env_close.py @@ -53,7 +53,7 @@ def test_dangling_reference(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("l2rpn_case14_sandbox", test=True) + env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) nb_env_before = ( len( [ diff --git a/grid2op/tests/test_dc_isolated_elements.py b/grid2op/tests/test_dc_isolated_elements.py index c672b8b9e..d496b1d58 100644 --- a/grid2op/tests/test_dc_isolated_elements.py +++ b/grid2op/tests/test_dc_isolated_elements.py @@ -17,7 +17,7 @@ class TestIsolatedLoad(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) # , backend=LightSimBackend()) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) param = self.env.parameters param.ENV_DC = True # force the computation of the powerflow in DC mode param.MAX_LINE_STATUS_CHANGED = 99999 diff --git a/grid2op/tests/test_decompose_as_unary_actions.py b/grid2op/tests/test_decompose_as_unary_actions.py index 5ed4ab82e..755eb4165 100644 --- a/grid2op/tests/test_decompose_as_unary_actions.py +++ b/grid2op/tests/test_decompose_as_unary_actions.py @@ -34,6 +34,7 @@ def setUp(self) -> None: "educ_case14_storage", test=True, action_class=PlayableAction, + _add_to_name=type(self).__name__, ) def tearDown(self) -> None: self.env.close() diff --git a/grid2op/tests/test_diff_backend.py b/grid2op/tests/test_diff_backend.py index ceeefdbb5..aa48b314c 100644 --- a/grid2op/tests/test_diff_backend.py +++ b/grid2op/tests/test_diff_backend.py @@ -41,7 +41,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_case14_sandbox_diff_grid", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox_diff_grid", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) @@ -141,7 +141,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.aux_env = grid2op.make("l2rpn_case14_sandbox_diff_grid", test=True) + self.aux_env = grid2op.make("l2rpn_case14_sandbox_diff_grid", test=True, _add_to_name=type(self).__name__) self.env = self.aux_env.copy() self.env.seed(0) @@ -182,6 +182,7 @@ def test_bk_kwargs(self) -> None: warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend env = grid2op.make("l2rpn_case14_sandbox_diff_grid", test=True, + _add_to_name=type(self).__name__, observation_backend_kwargs={"max_iter": 15, "lightsim2grid": True}) self._aux_check_different_stuff(env, self._aux_check_bk_kwargs) @@ -199,6 +200,7 @@ def test_bk_class(self): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend env = grid2op.make("l2rpn_case14_sandbox_diff_grid", test=True, + _add_to_name=type(self).__name__, observation_backend_class=ModifPPBackend) self._aux_check_different_stuff(env, self._aux_bk_class) diff --git a/grid2op/tests/test_educpp_backend.py b/grid2op/tests/test_educpp_backend.py index fb1916b4f..003b88170 100644 --- a/grid2op/tests/test_educpp_backend.py +++ b/grid2op/tests/test_educpp_backend.py @@ -38,7 +38,7 @@ def test_make(self): env = grid2op.make(env_name, test=True, backend=EducPandaPowerBackend(), - _add_to_name="educppbk") + _add_to_name=type(self).__name__+"educppbk") assert type(env).n_shunt is None, f"error for {env_name}" assert not type(env).shunts_data_available, f"error for {env_name}" env.close() diff --git a/grid2op/tests/test_elements_graph.py b/grid2op/tests/test_elements_graph.py index 1daf83d17..f704de67a 100644 --- a/grid2op/tests/test_elements_graph.py +++ b/grid2op/tests/test_elements_graph.py @@ -19,7 +19,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) self.tol = 1e-5 @@ -169,7 +169,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("educ_case14_storage", test=True, action_class=PlayableAction) + self.env = grid2op.make("educ_case14_storage", test=True, action_class=PlayableAction, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) self.tol = 1e-5 @@ -185,7 +185,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_wcci_2022", test=True) + self.env = grid2op.make("l2rpn_wcci_2022", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) self.tol = 3e-5 diff --git a/grid2op/tests/test_env_from_episode.py b/grid2op/tests/test_env_from_episode.py index aeebc0e1c..b71aed247 100644 --- a/grid2op/tests/test_env_from_episode.py +++ b/grid2op/tests/test_env_from_episode.py @@ -87,7 +87,8 @@ def setUp(self) -> None: opponent_attack_duration=0, opponent_budget_per_ts=0., opponent_init_budget=0., - opponent_action_class=DontAct,) + opponent_action_class=DontAct, + _add_to_name=type(self).__name__) self.env.set_id(0) self.env.seed(0) self.max_iter = 10 @@ -118,6 +119,7 @@ def test_basic(self): env = grid2op.make(self.env_name, test=True, chronics_class=FromOneEpisodeData, + _add_to_name=type(self).__name__, data_feeding_kwargs={"ep_data": ep_data}, opponent_attack_cooldown=99999999, opponent_attack_duration=0, @@ -154,6 +156,7 @@ def test_fast_forward(self): env = grid2op.make(self.env_name, test=True, chronics_class=FromOneEpisodeData, + _add_to_name=type(self).__name__, data_feeding_kwargs={"ep_data": ep_data}, opponent_attack_cooldown=99999999, opponent_attack_duration=0, @@ -182,6 +185,7 @@ def test_forecasts(self): env = grid2op.make(self.env_name, test=True, chronics_class=FromOneEpisodeData, + _add_to_name=type(self).__name__, data_feeding_kwargs={"ep_data": ep_data, "list_perfect_forecasts": [1]}, opponent_attack_cooldown=99999999, @@ -195,6 +199,7 @@ def test_forecasts(self): env = grid2op.make(self.env_name, test=True, chronics_class=FromOneEpisodeData, + _add_to_name=type(self).__name__, data_feeding_kwargs={"ep_data": ep_data, "list_perfect_forecasts": [5]}, opponent_attack_cooldown=99999999, @@ -236,6 +241,7 @@ def test_when_game_over(self): env = grid2op.make(self.env_name, test=True, chronics_class=FromOneEpisodeData, + _add_to_name=type(self).__name__, data_feeding_kwargs={"ep_data": ep_data}, opponent_attack_cooldown=99999999, opponent_attack_duration=0, @@ -282,6 +288,7 @@ def test_maintenance(self): env = grid2op.make(self.env_name, test=True, chronics_class=FromOneEpisodeData, + _add_to_name=type(self).__name__, data_feeding_kwargs={"ep_data": ep_data}, opponent_attack_cooldown=99999999, opponent_attack_duration=0, @@ -313,6 +320,7 @@ def test_maintenance(self): env = grid2op.make(self.env_name, test=True, chronics_class=FromOneEpisodeData, + _add_to_name=type(self).__name__, data_feeding_kwargs={"ep_data": ep_data2}, opponent_attack_cooldown=99999999, opponent_attack_duration=0, @@ -333,7 +341,7 @@ def test_given_example_oneepdata(self): env_name = "l2rpn_case14_sandbox" # or any other name with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make(env_name, test=True) + env = grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) param = env.parameters param.NO_OVERFLOW_DISCONNECTION = True @@ -358,6 +366,7 @@ def test_given_example_oneepdata(self): chronics_class=FromOneEpisodeData, data_feeding_kwargs={"ep_data": ep_data}, opponent_class=FromEpisodeDataOpponent, + _add_to_name=type(self).__name__, ) ep_data2 = EpisodeData.from_disk(*ep_data) obs = env2.reset() @@ -373,7 +382,7 @@ def test_given_example_multiepdata(self): env_name = "l2rpn_case14_sandbox" # or any other name with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make(env_name, test=True) + env = grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) param = env.parameters param.NO_OVERFLOW_DISCONNECTION = True @@ -401,6 +410,7 @@ def test_given_example_multiepdata(self): data_feeding_kwargs={"li_ep_data": li_episode}, opponent_class=FromEpisodeDataOpponent, opponent_attack_cooldown=1, + _add_to_name=type(self).__name__, ) # li_ep_data in this case is a list of anything that is accepted by `FromOneEpisodeData` @@ -424,7 +434,8 @@ def test_load_with_opp(self): opponent_class=RandomLineOpponent, opponent_budget_class=BaseActionBudget, kwargs_opponent={"lines_attacked": - ["1_3_3", "1_4_4", "3_6_15", "9_10_12", "11_12_13", "12_13_14"]}) + ["1_3_3", "1_4_4", "3_6_15", "9_10_12", "11_12_13", "12_13_14"]}, + _add_to_name=type(self).__name__) runner = Runner( **env.get_params_for_runner(), agentClass=RecoPowerlineAgent ) @@ -450,6 +461,7 @@ def test_load_with_opp(self): opponent_init_budget=100000., opponent_action_class=PowerlineSetAction, opponent_budget_class=BaseActionBudget, + _add_to_name=type(self).__name__ ) obs = env2.reset() agent = RecoPowerlineAgent(env2.action_space) @@ -474,7 +486,8 @@ def test_assert_warnings(self): opponent_budget_class=BaseActionBudget, kwargs_opponent={"lines_attacked": ["1_3_3", "1_4_4", "3_6_15", - "9_10_12", "11_12_13", "12_13_14"]}) + "9_10_12", "11_12_13", "12_13_14"]}, + _add_to_name=type(self).__name__) runner = Runner( **env.get_params_for_runner(), agentClass=RecoPowerlineAgent ) @@ -498,6 +511,8 @@ def test_assert_warnings(self): opponent_init_budget=100000., opponent_action_class=PowerlineSetAction, opponent_budget_class=BaseActionBudget, + _add_to_name=type(self).__name__, + ) with self.assertWarns(UserWarning): env2 = grid2op.make(env_name, @@ -511,6 +526,7 @@ def test_assert_warnings(self): opponent_init_budget=100000., opponent_action_class=PowerlineSetAction, opponent_budget_class=BaseActionBudget, + _add_to_name=type(self).__name__ ) @@ -525,7 +541,8 @@ def setUp(self) -> None: opponent_attack_duration=0, opponent_budget_per_ts=0., opponent_init_budget=0., - opponent_action_class=DontAct,) + opponent_action_class=DontAct, + _add_to_name=type(self).__name__) self.env.set_id(0) self.env.seed(0) self.max_iter = 10 @@ -553,7 +570,8 @@ def test_basic(self): opponent_attack_duration=0, opponent_budget_per_ts=0., opponent_init_budget=0., - opponent_action_class=DontAct) + opponent_action_class=DontAct, + _add_to_name=type(self).__name__) # test init data obs = env.reset() TestTSFromEpisodeMaintenance._aux_obs_equal(obs, ep_data[0].observations[0]) diff --git a/grid2op/tests/test_feature_issue_447.py b/grid2op/tests/test_feature_issue_447.py index 70878c255..5b10b96f4 100644 --- a/grid2op/tests/test_feature_issue_447.py +++ b/grid2op/tests/test_feature_issue_447.py @@ -23,7 +23,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make(env_name, chronics_class=MultifolderWithCache, - test=True) + test=True, + _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env.close() @@ -90,10 +91,6 @@ def test_copy(self): env_cpy2.step(env_cpy2.action_space()) def test_runner_max_iter(self): - # env_name = "l2rpn_case14_sandbox" - # self.env = grid2op.make(env_name, - # # chronics_class=MultifolderWithCache, - # test=True) self.env.chronics_handler.real_data.reset() runner = Runner(**self.env.get_params_for_runner()) res = runner.run(nb_episode=1, diff --git a/grid2op/tests/test_forecast_from_arrays.py b/grid2op/tests/test_forecast_from_arrays.py index add5fe7c3..7d5d40ade 100644 --- a/grid2op/tests/test_forecast_from_arrays.py +++ b/grid2op/tests/test_forecast_from_arrays.py @@ -17,7 +17,7 @@ class TestForecastFromArrays(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) return super().setUp() def tearDown(self) -> None: diff --git a/grid2op/tests/test_fromChronix2grid.py b/grid2op/tests/test_fromChronix2grid.py index ddf216526..5b6a9422a 100644 --- a/grid2op/tests/test_fromChronix2grid.py +++ b/grid2op/tests/test_fromChronix2grid.py @@ -37,7 +37,8 @@ def setUp(self) -> None: "with_maintenance": True, "max_iter": 10, "with_loss": False - } + }, + _add_to_name=type(self).__name__ ) @@ -92,7 +93,7 @@ def test_maintenance(self): warnings.filterwarnings("ignore") self.env = grid2op.make(self.env_nm, test=True, - # backend=LightSimBackend(), + _add_to_name=type(self).__name__, chronics_class=FromChronix2grid, data_feeding_kwargs={"env_path": os.path.join(DEV_DATA_FOLDER, self.env_nm), "with_maintenance": True, diff --git a/grid2op/tests/test_generate_classes.py b/grid2op/tests/test_generate_classes.py index f27339287..f88cdcfd8 100644 --- a/grid2op/tests/test_generate_classes.py +++ b/grid2op/tests/test_generate_classes.py @@ -39,7 +39,7 @@ def test_can_generate(self): for env_nm in self.list_env(): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make(env_nm, test=True) + env = grid2op.make(env_nm, test=True, _add_to_name=type(self).__name__+"test_generate") env.generate_classes() self._aux_assert_exists_then_delete(env) env.close() @@ -48,7 +48,7 @@ def test_can_load(self): for env_nm in self.list_env(): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make(env_nm, test=True, _add_to_name="_TestGenerateFile") + env = grid2op.make(env_nm, test=True, _add_to_name=type(self).__name__+"_TestGenerateFile") env.generate_classes() with warnings.catch_warnings(): @@ -56,7 +56,8 @@ def test_can_load(self): try: env2 = grid2op.make(env_nm, test=True, - experimental_read_from_local_dir=True) + experimental_read_from_local_dir=True, + _add_to_name=type(self).__name__) env2.close() except RuntimeError as exc_: raise RuntimeError(f"Error for {env_nm}") from exc_ diff --git a/grid2op/tests/test_gym_env_renderer.py b/grid2op/tests/test_gym_env_renderer.py index 57829a806..4b26d89be 100644 --- a/grid2op/tests/test_gym_env_renderer.py +++ b/grid2op/tests/test_gym_env_renderer.py @@ -20,7 +20,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_highres_sim_counter.py b/grid2op/tests/test_highres_sim_counter.py index 8c434a265..752eec7e7 100644 --- a/grid2op/tests/test_highres_sim_counter.py +++ b/grid2op/tests/test_highres_sim_counter.py @@ -37,6 +37,7 @@ def _make_env(self): # this needs to be tested with pandapower backend env = grid2op.make("l2rpn_case14_sandbox", test=True, + _add_to_name=type(self).__name__, data_feeding_kwargs={"gridvalueClass": FromHandlers, "gen_p_handler": CSVHandler("prod_p"), "load_p_handler": CSVHandler("load_p"), diff --git a/grid2op/tests/test_highres_sim_counter_in_scores.py b/grid2op/tests/test_highres_sim_counter_in_scores.py index dae7ae611..6838cd570 100644 --- a/grid2op/tests/test_highres_sim_counter_in_scores.py +++ b/grid2op/tests/test_highres_sim_counter_in_scores.py @@ -38,6 +38,7 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make(self._env_name(), test=True, + _add_to_name=type(self).__name__, data_feeding_kwargs={"gridvalueClass": FromHandlers, "gen_p_handler": CSVHandler("prod_p"), "load_p_handler": CSVHandler("load_p"), diff --git a/grid2op/tests/test_issue_131.py b/grid2op/tests/test_issue_131.py index f9afc331a..81aaa4803 100755 --- a/grid2op/tests/test_issue_131.py +++ b/grid2op/tests/test_issue_131.py @@ -18,7 +18,7 @@ class Issue131Tester(unittest.TestCase): def test_issue_131(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("rte_case14_realistic", test=True) + env = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) # Get forecast after a simulate works obs = env.reset() diff --git a/grid2op/tests/test_issue_140.py b/grid2op/tests/test_issue_140.py index e4f416bb9..d89924a4a 100644 --- a/grid2op/tests/test_issue_140.py +++ b/grid2op/tests/test_issue_140.py @@ -52,6 +52,7 @@ def test_issue_140(self): env = grid2op.make( env_name, param=param, + _add_to_name=type(self).__name__, ) ts_per_chronics = 2016 diff --git a/grid2op/tests/test_issue_146.py b/grid2op/tests/test_issue_146.py index 0c525c1ef..35d55ff55 100644 --- a/grid2op/tests/test_issue_146.py +++ b/grid2op/tests/test_issue_146.py @@ -43,7 +43,8 @@ def test_issue_146(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "rte_case14_realistic", test=True, reward_class=TestReward + "rte_case14_realistic", test=True, reward_class=TestReward, + _add_to_name=type(self).__name__ ) action = env.action_space( diff --git a/grid2op/tests/test_issue_147.py b/grid2op/tests/test_issue_147.py index c3b9190b0..13636627b 100644 --- a/grid2op/tests/test_issue_147.py +++ b/grid2op/tests/test_issue_147.py @@ -31,7 +31,7 @@ def test_issue_147(self): param.NB_TIMESTEP_COOLDOWN_LINE = 3 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("rte_case14_realistic", test=True, param=param) + env = grid2op.make("rte_case14_realistic", test=True, param=param, _add_to_name=type(self).__name__) action = env.action_space( {"set_bus": {"substations_id": [(1, [2, 2, 1, 1, 2, 2])]}} diff --git a/grid2op/tests/test_issue_148.py b/grid2op/tests/test_issue_148.py index f407c886e..ee9b0b7a6 100644 --- a/grid2op/tests/test_issue_148.py +++ b/grid2op/tests/test_issue_148.py @@ -34,6 +34,7 @@ def test_issue_148(self): os.path.join(PATH_CHRONICS, "env_14_test_maintenance"), test=True, param=param, + _add_to_name=type(self).__name__ ) ID_MAINT = 11 # in maintenance at the second time step diff --git a/grid2op/tests/test_issue_151.py b/grid2op/tests/test_issue_151.py index 036252612..97b7d8d4d 100644 --- a/grid2op/tests/test_issue_151.py +++ b/grid2op/tests/test_issue_151.py @@ -30,7 +30,7 @@ def test_issue_151(self): param.NB_TIMESTEP_COOLDOWN_SUB = 3 with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("rte_case14_realistic", test=True) + env = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) do_nothing = env.action_space({}) obs, reward, done, info = env.step(do_nothing) obs.line_status = ( diff --git a/grid2op/tests/test_issue_153.py b/grid2op/tests/test_issue_153.py index be0e05e13..2a26d945a 100644 --- a/grid2op/tests/test_issue_153.py +++ b/grid2op/tests/test_issue_153.py @@ -27,7 +27,7 @@ def test_issue_153(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "rte_case14_realistic", test=True, _add_to_name="test_issue_153" + "rte_case14_realistic", test=True, _add_to_name=type(self).__name__+"test_issue_153" ) env.gen_max_ramp_up[:] = env.gen_pmax env.gen_max_ramp_down[:] = env.gen_pmax diff --git a/grid2op/tests/test_issue_164.py b/grid2op/tests/test_issue_164.py index 40fe12cc5..6af4c210b 100644 --- a/grid2op/tests/test_issue_164.py +++ b/grid2op/tests/test_issue_164.py @@ -34,7 +34,8 @@ class Issue164Tester(unittest.TestCase): def test_issue_164(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make('rte_case14_realistic', reward_class=Test164_Reward, test=True) + env = grid2op.make('rte_case14_realistic', reward_class=Test164_Reward, test=True, + _add_to_name=type(self).__name__) max_timestep = env.chronics_handler.max_timestep() obs = env.reset() diff --git a/grid2op/tests/test_issue_185.py b/grid2op/tests/test_issue_185.py index 61e462048..1c2c4e5df 100644 --- a/grid2op/tests/test_issue_185.py +++ b/grid2op/tests/test_issue_185.py @@ -46,7 +46,7 @@ def test_issue_185(self): continue with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make(env_name, test=True) as env: + with grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) as env: try: gym_env = GymEnv(env) # gym_env.seed(0) @@ -74,7 +74,7 @@ def test_issue_185_act_box_space(self): continue with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make(env_name, test=True) as env: + with grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) as env: try: gym_env = GymEnv(env) try: @@ -110,7 +110,7 @@ def test_issue_185_obs_box_space(self): # continue with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make(env_name, test=True) as env: + with grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) as env: try: gym_env = GymEnv(env) gym_env.observation_space.close() @@ -151,7 +151,7 @@ def test_issue_185_act_multidiscrete_space(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make(env_name, test=True) as env: + with grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) as env: try: gym_env = GymEnv(env) gym_env.action_space = MultiDiscreteActSpace( @@ -187,7 +187,7 @@ def test_issue_185_act_discrete_space(self): continue with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make(env_name, test=True) as env: + with grid2op.make(env_name, test=True, _add_to_name=type(self).__name__) as env: try: gym_env = GymEnv(env) gym_env.action_space = DiscreteActSpace( diff --git a/grid2op/tests/test_issue_187.py b/grid2op/tests/test_issue_187.py index 9b8d9d3e3..3fc00996a 100644 --- a/grid2op/tests/test_issue_187.py +++ b/grid2op/tests/test_issue_187.py @@ -34,7 +34,8 @@ def test_issue_187(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") with grid2op.make( - env_name, test=True, reward_class=RedispReward + env_name, test=True, reward_class=RedispReward, + _add_to_name=type(self).__name__ ) as env: obs = env.reset() obs, reward, done, info = env.step(env.action_space()) @@ -61,7 +62,7 @@ def test_custom_reward(self): continue with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make(env_name, test=True, reward_class=reward_cls) as env: + with grid2op.make(env_name, test=True, reward_class=reward_cls, _add_to_name=type(self).__name__) as env: obs = env.reset() obs, reward, done, info = env.step(env.action_space()) # test that reward is in the correct range @@ -124,7 +125,7 @@ def test_custom_reward_runner(self): env_name = "l2rpn_case14_sandbox" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with grid2op.make(env_name, test=True, reward_class=reward_cls) as env: + with grid2op.make(env_name, test=True, reward_class=reward_cls, _add_to_name=type(self).__name__) as env: obs = env.reset() runner = Runner(**env.get_params_for_runner()) res = runner.run(nb_episode=2, nb_process=2) diff --git a/grid2op/tests/test_issue_196.py b/grid2op/tests/test_issue_196.py index f7083e342..c6a4b815d 100644 --- a/grid2op/tests/test_issue_196.py +++ b/grid2op/tests/test_issue_196.py @@ -20,7 +20,7 @@ class Issue196Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("l2rpn_neurips_2020_track1", test=True) + env = grid2op.make("l2rpn_neurips_2020_track1", test=True, _add_to_name=type(self).__name__) env_gym = GymEnv(env) ob_space = env_gym.observation_space ob_space = ob_space.keep_only_attr( diff --git a/grid2op/tests/test_issue_217.py b/grid2op/tests/test_issue_217.py index ad5593fcb..6ddcdba42 100644 --- a/grid2op/tests/test_issue_217.py +++ b/grid2op/tests/test_issue_217.py @@ -18,7 +18,7 @@ class Issue217Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_nm = os.path.join(PATH_DATA_TEST, "5bus_modif_grid") + env_nm = os.path.join(PATH_DATA_TEST, "5bus_modif_grid", _add_to_name=type(self).__name__) self.env = grid2op.make(env_nm, test=True, chronics_class=ChangeNothing) self.env.seed(0) self.env.reset() diff --git a/grid2op/tests/test_issue_220.py b/grid2op/tests/test_issue_220.py index e038f4dcd..b1275f703 100644 --- a/grid2op/tests/test_issue_220.py +++ b/grid2op/tests/test_issue_220.py @@ -17,7 +17,7 @@ class Issue220Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.reset() diff --git a/grid2op/tests/test_issue_223.py b/grid2op/tests/test_issue_223.py index 9312bcd38..721841b93 100644 --- a/grid2op/tests/test_issue_223.py +++ b/grid2op/tests/test_issue_223.py @@ -37,7 +37,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") env_nm = os.path.join(PATH_DATA_TEST, "5bus_modif_grid") - self.env = grid2op.make(env_nm, test=True, chronics_class=ChangeNothing) + self.env = grid2op.make(env_nm, test=True, chronics_class=ChangeNothing, _add_to_name=type(self).__name__) self.env.seed(0) self.reset_without_pp_futurewarnings() diff --git a/grid2op/tests/test_issue_224.py b/grid2op/tests/test_issue_224.py index b72155562..ed89e0249 100644 --- a/grid2op/tests/test_issue_224.py +++ b/grid2op/tests/test_issue_224.py @@ -23,7 +23,7 @@ def setUp(self) -> None: env_nm = os.path.join( PATH_DATA_TEST, "l2rpn_neurips_2020_track1_with_alarm" ) - self.env = grid2op.make(env_nm, test=True, chronics_class=ChangeNothing) + self.env = grid2op.make(env_nm, test=True, chronics_class=ChangeNothing, _add_to_name=type(self).__name__) self.env.seed(0) self.env.reset() @@ -46,7 +46,8 @@ def test_env_alarmtime_changed(self): PATH_DATA_TEST, "l2rpn_neurips_2020_track1_with_alarm" ) env = grid2op.make( - env_nm, test=True, chronics_class=ChangeNothing, param=param + env_nm, test=True, chronics_class=ChangeNothing, param=param, + _add_to_name=type(self).__name__ ) assert env.parameters.ALARM_WINDOW_SIZE == 99 assert env.parameters.ALARM_BEST_TIME == 12 @@ -63,7 +64,8 @@ def test_env_alarmtime_changed(self): PATH_DATA_TEST, "l2rpn_neurips_2020_track1_with_alarm" ) env = grid2op.make( - env_nm, test=True, chronics_class=ChangeNothing, param=param + env_nm, test=True, chronics_class=ChangeNothing, param=param, + _add_to_name=type(self).__name__ ) assert env.parameters.ALARM_WINDOW_SIZE == 12 assert env.parameters.ALARM_BEST_TIME == 42 diff --git a/grid2op/tests/test_issue_235.py b/grid2op/tests/test_issue_235.py index 116b419a9..eb70e64e9 100644 --- a/grid2op/tests/test_issue_235.py +++ b/grid2op/tests/test_issue_235.py @@ -45,7 +45,8 @@ def setUp(self) -> None: # from lightsim2grid import LightSimBackend # backend=LightSimBackend(), self.env = grid2op.make( - env_nm, test=True, observation_class=Issue235TesterObs + env_nm, test=True, observation_class=Issue235TesterObs, + _add_to_name=type(self).__name__ ) # now set the right observation class for the simulate action diff --git a/grid2op/tests/test_issue_238.py b/grid2op/tests/test_issue_238.py index fc6aebe1a..e4eb0c3f8 100644 --- a/grid2op/tests/test_issue_238.py +++ b/grid2op/tests/test_issue_238.py @@ -29,7 +29,7 @@ def setUp(self) -> None: self.env = grid2op.make( env_nm, test=True, - # chronics_class=ChangeNothing, + _add_to_name=type(self).__name__, opponent_attack_cooldown=opponent_attack_cooldown, opponent_attack_duration=opponent_attack_duration, opponent_budget_per_ts=opponent_budget_per_ts, diff --git a/grid2op/tests/test_issue_245.py b/grid2op/tests/test_issue_245.py index 5295c4332..49dfa6a58 100644 --- a/grid2op/tests/test_issue_245.py +++ b/grid2op/tests/test_issue_245.py @@ -19,7 +19,8 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") env_nm = "l2rpn_case14_sandbox" - self.env = grid2op.make(env_nm, test=True, action_class=CompleteAction) + self.env = grid2op.make(env_nm, test=True, action_class=CompleteAction, + _add_to_name=type(self).__name__) self.env.seed(0) self.env.reset() diff --git a/grid2op/tests/test_issue_274.py b/grid2op/tests/test_issue_274.py index 7fca52219..ec11d3ee8 100644 --- a/grid2op/tests/test_issue_274.py +++ b/grid2op/tests/test_issue_274.py @@ -16,7 +16,7 @@ class Issue274Tester(unittest.TestCase): def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_icaps_2021", test=True) + self.env = grid2op.make("l2rpn_icaps_2021", test=True, _add_to_name=type(self).__name__) def test_same_opponent_state(self): """test that the opponent state is correctly copied""" diff --git a/grid2op/tests/test_issue_281.py b/grid2op/tests/test_issue_281.py index 97c4d49e9..1bbf78ff6 100644 --- a/grid2op/tests/test_issue_281.py +++ b/grid2op/tests/test_issue_281.py @@ -17,7 +17,8 @@ def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - "educ_case14_storage", test=True, action_class=CompleteAction + "educ_case14_storage", test=True, action_class=CompleteAction, + _add_to_name=type(self).__name__ ) def tearDown(self): diff --git a/grid2op/tests/test_issue_282.py b/grid2op/tests/test_issue_282.py index 946ae4b22..622412fcc 100644 --- a/grid2op/tests/test_issue_282.py +++ b/grid2op/tests/test_issue_282.py @@ -20,7 +20,8 @@ def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - "educ_case14_storage", test=True, action_class=CompleteAction + "educ_case14_storage", test=True, action_class=CompleteAction, + _add_to_name=type(self).__name__ ) self.env_gym = GymEnv(self.env) diff --git a/grid2op/tests/test_issue_283.py b/grid2op/tests/test_issue_283.py index f610f470d..a27afc616 100644 --- a/grid2op/tests/test_issue_283.py +++ b/grid2op/tests/test_issue_283.py @@ -20,7 +20,8 @@ def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - "educ_case14_storage", test=True, gamerules_class=AlwaysLegal + "educ_case14_storage", test=True, gamerules_class=AlwaysLegal, + _add_to_name=type(self).__name__ ) self.env_gym = GymEnv(self.env) diff --git a/grid2op/tests/test_issue_285.py b/grid2op/tests/test_issue_285.py index 1ad56b3ac..b59b76047 100644 --- a/grid2op/tests/test_issue_285.py +++ b/grid2op/tests/test_issue_285.py @@ -20,7 +20,8 @@ def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - "rte_case5_example", test=True, chronics_class=MultifolderWithCache + "rte_case5_example", test=True, chronics_class=MultifolderWithCache, + _add_to_name=type(self).__name__ ) self.env.chronics_handler.real_data.set_filter( lambda x: re.match(".*0$", x) is not None diff --git a/grid2op/tests/test_issue_313.py b/grid2op/tests/test_issue_313.py index af45c5a46..4be064ecf 100644 --- a/grid2op/tests/test_issue_313.py +++ b/grid2op/tests/test_issue_313.py @@ -24,7 +24,8 @@ def setUp(self): backend=LightSimBackend(), action_class=PlayableAction, param=param, - test=True) + test=True, + _add_to_name=type(self).__name__) self.env.set_id(2) self.env.seed(0) self.env.reset() diff --git a/grid2op/tests/test_issue_319.py b/grid2op/tests/test_issue_319.py index 11b7fc7cf..109b8d160 100644 --- a/grid2op/tests/test_issue_319.py +++ b/grid2op/tests/test_issue_319.py @@ -20,7 +20,8 @@ def setUp(self): warnings.filterwarnings("ignore") self.env = grid2op.make( "rte_case14_realistic", - test=True + test=True, + _add_to_name=type(self).__name__ ) def tearDown(self) -> None: self.env.close() diff --git a/grid2op/tests/test_issue_321.py b/grid2op/tests/test_issue_321.py index 847cb7fe8..5b1698627 100644 --- a/grid2op/tests/test_issue_321.py +++ b/grid2op/tests/test_issue_321.py @@ -29,7 +29,8 @@ def test_curtail_limit_effective(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make(env_name, test=True, - action_class=PlayableAction, param=param) + action_class=PlayableAction, param=param, + _add_to_name=type(self).__name__) env.seed(0) env.set_id(0) @@ -49,7 +50,8 @@ def test_when_params_active_cutdown(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make("l2rpn_wcci_2022_dev", - test=True) + test=True, + _add_to_name=type(self).__name__) param = env.parameters param.LIMIT_INFEASIBLE_CURTAILMENT_STORAGE_ACTION = True env.seed(seed_) @@ -82,7 +84,8 @@ def test_when_params_active_limitup(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make("l2rpn_wcci_2022_dev", - test=True) + test=True, + _add_to_name=type(self).__name__) param = env.parameters param.LIMIT_INFEASIBLE_CURTAILMENT_STORAGE_ACTION = True env.seed(seed_) diff --git a/grid2op/tests/test_issue_327.py b/grid2op/tests/test_issue_327.py index 3909286eb..097f618f8 100644 --- a/grid2op/tests/test_issue_327.py +++ b/grid2op/tests/test_issue_327.py @@ -21,7 +21,7 @@ class Issue327Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) return super().setUp() def tearDown(self) -> None: diff --git a/grid2op/tests/test_issue_331.py b/grid2op/tests/test_issue_331.py index 65a97c2fe..04b947234 100644 --- a/grid2op/tests/test_issue_331.py +++ b/grid2op/tests/test_issue_331.py @@ -17,7 +17,7 @@ class Issue331Tester(unittest.TestCase): def test_seed(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("l2rpn_case14_sandbox", test=True) + env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) with self.assertRaises(Grid2OpException): env.seed(2735729614) # crashes ! diff --git a/grid2op/tests/test_issue_340.py b/grid2op/tests/test_issue_340.py index 0615450e2..3fe5b51f9 100644 --- a/grid2op/tests/test_issue_340.py +++ b/grid2op/tests/test_issue_340.py @@ -18,7 +18,7 @@ class Issue340Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_wcci_2022", test=True) + self.env = grid2op.make("l2rpn_wcci_2022", test=True, _add_to_name=type(self).__name__) return super().setUp() def tearDown(self) -> None: diff --git a/grid2op/tests/test_issue_361.py b/grid2op/tests/test_issue_361.py index 360bbd02b..6556ab815 100644 --- a/grid2op/tests/test_issue_361.py +++ b/grid2op/tests/test_issue_361.py @@ -17,7 +17,7 @@ class Issue361Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env.close() diff --git a/grid2op/tests/test_issue_364.py b/grid2op/tests/test_issue_364.py index 10de1638c..a72a9310d 100644 --- a/grid2op/tests/test_issue_364.py +++ b/grid2op/tests/test_issue_364.py @@ -17,7 +17,7 @@ class Issue364Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_wcci_2022", test=True) + self.env = grid2op.make("l2rpn_wcci_2022", test=True, _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env.close() diff --git a/grid2op/tests/test_issue_367.py b/grid2op/tests/test_issue_367.py index 84f289c51..417a6b5ff 100644 --- a/grid2op/tests/test_issue_367.py +++ b/grid2op/tests/test_issue_367.py @@ -18,8 +18,7 @@ class Issue367Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "test_issue_367"), test=True) - # self.env = grid2op.make("l2rpn_wcci_2022", test=True) + self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "test_issue_367"), test=True, _add_to_name=type(self).__name__) self.env.set_id(0) self.env.seed(0) param = self.env.parameters diff --git a/grid2op/tests/test_issue_369.py b/grid2op/tests/test_issue_369.py index 218eafb5e..5ee7e27c5 100644 --- a/grid2op/tests/test_issue_369.py +++ b/grid2op/tests/test_issue_369.py @@ -24,6 +24,7 @@ def setUp(self) -> None: # Creation of the environment self.env = grid2op.make("l2rpn_wcci_2022_dev", chronics_class=MultifolderWithCache, + _add_to_name=type(self).__name__, test=True) # No bug wihout the MultifolderWithCache argument here self.env.chronics_handler.real_data.set_filter(lambda x: re.match(".*2050-02-14_0", x) is not None) # We test on a randomly chosen chronic for the example self.env.chronics_handler.reset() diff --git a/grid2op/tests/test_issue_374.py b/grid2op/tests/test_issue_374.py index aeb704687..7b52b17f2 100644 --- a/grid2op/tests/test_issue_374.py +++ b/grid2op/tests/test_issue_374.py @@ -25,7 +25,7 @@ def setUp(self) -> None: param.NB_TIMESTEP_COOLDOWN_SUB = 3 param.NB_TIMESTEP_COOLDOWN_LINE = 3 param.NO_OVERFLOW_DISCONNECTION = True - self.env = grid2op.make('l2rpn_wcci_2022', param=param) + self.env = grid2op.make('l2rpn_wcci_2022', param=param, _add_to_name=type(self).__name__) self.env.set_id(0) self.env.seed(0) diff --git a/grid2op/tests/test_issue_377.py b/grid2op/tests/test_issue_377.py index b650c51ca..17151b8fb 100644 --- a/grid2op/tests/test_issue_377.py +++ b/grid2op/tests/test_issue_377.py @@ -26,7 +26,7 @@ def setUp(self) -> None: param.NB_TIMESTEP_COOLDOWN_SUB = 3 param.NB_TIMESTEP_COOLDOWN_LINE = 3 param.NO_OVERFLOW_DISCONNECTION = True - self.env = grid2op.make('l2rpn_wcci_2022', param=param) + self.env = grid2op.make('l2rpn_wcci_2022', param=param, _add_to_name=type(self).__name__) self.env.set_id(0) self.env.seed(0) diff --git a/grid2op/tests/test_issue_379.py b/grid2op/tests/test_issue_379.py index 1907259c0..087dd9ecd 100644 --- a/grid2op/tests/test_issue_379.py +++ b/grid2op/tests/test_issue_379.py @@ -29,7 +29,7 @@ class Issue379Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.gym_env = GymEnv(self.env) def tearDown(self) -> None: diff --git a/grid2op/tests/test_issue_380.py b/grid2op/tests/test_issue_380.py index 7980627a0..fe602132e 100644 --- a/grid2op/tests/test_issue_380.py +++ b/grid2op/tests/test_issue_380.py @@ -16,7 +16,7 @@ class Issue380Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_issue_389.py b/grid2op/tests/test_issue_389.py index 4113ed257..c9a92c2f4 100644 --- a/grid2op/tests/test_issue_389.py +++ b/grid2op/tests/test_issue_389.py @@ -22,7 +22,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("rte_case5_example", test=True) + self.env = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_issue_396.py b/grid2op/tests/test_issue_396.py index 0a28a479c..85c1d24e7 100644 --- a/grid2op/tests/test_issue_396.py +++ b/grid2op/tests/test_issue_396.py @@ -22,7 +22,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("rte_case5_example", test=True) + self.env = grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_issue_403.py b/grid2op/tests/test_issue_403.py index e2fa374f9..b3115b62e 100644 --- a/grid2op/tests/test_issue_403.py +++ b/grid2op/tests/test_issue_403.py @@ -19,7 +19,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_issue_407.py b/grid2op/tests/test_issue_407.py index 60d9c2e4f..c66b571fb 100644 --- a/grid2op/tests/test_issue_407.py +++ b/grid2op/tests/test_issue_407.py @@ -27,7 +27,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_issue_418.py b/grid2op/tests/test_issue_418.py index a01205c7c..0621a2ca6 100644 --- a/grid2op/tests/test_issue_418.py +++ b/grid2op/tests/test_issue_418.py @@ -19,7 +19,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_issue_433.py b/grid2op/tests/test_issue_433.py index 266751d0e..3fa7f6d9b 100644 --- a/grid2op/tests/test_issue_433.py +++ b/grid2op/tests/test_issue_433.py @@ -17,7 +17,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_issue_446.py b/grid2op/tests/test_issue_446.py index d9aad58f7..dd0278a0b 100644 --- a/grid2op/tests/test_issue_446.py +++ b/grid2op/tests/test_issue_446.py @@ -8,7 +8,7 @@ import grid2op -from grid2op.gym_compat import BoxGymActSpace, BoxGymObsSpace +from grid2op.gym_compat import BoxGymObsSpace import numpy as np import unittest @@ -16,7 +16,7 @@ class Issue446Tester(unittest.TestCase): def test_box_action_space(self): # We considers only redispatching actions - env = grid2op.make("l2rpn_case14_sandbox", test=True) + env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) divide = {"hour_of_day": np.ones(1)} subtract = {"hour_of_day": np.zeros(1)} diff --git a/grid2op/tests/test_issue_494.py b/grid2op/tests/test_issue_494.py index e6d9ee1b3..5356e21b5 100644 --- a/grid2op/tests/test_issue_494.py +++ b/grid2op/tests/test_issue_494.py @@ -17,7 +17,7 @@ class Issue494Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_idf_2023", test=True) + self.env = grid2op.make("l2rpn_idf_2023", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_issue_503.py b/grid2op/tests/test_issue_503.py index bdbc33013..3c3e7e747 100644 --- a/grid2op/tests/test_issue_503.py +++ b/grid2op/tests/test_issue_503.py @@ -17,11 +17,11 @@ def test_only_kwargs(self): params = Parameters() params.NO_OVERFLOW_DISCONNECTION = True with self.assertRaises(TypeError): - _ = grid2op.make("l2rpn_case14_sandbox", params) + _ = grid2op.make("l2rpn_case14_sandbox", params, test=True, _add_to_name=type(self).__name__) with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("l2rpn_case14_sandbox", test=True, param=params) + env = grid2op.make("l2rpn_case14_sandbox", test=True, param=params, _add_to_name=type(self).__name__) env.close() diff --git a/grid2op/tests/test_issue_511.py b/grid2op/tests/test_issue_511.py index 0303c32ac..5ff3db8de 100644 --- a/grid2op/tests/test_issue_511.py +++ b/grid2op/tests/test_issue_511.py @@ -18,7 +18,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make( "l2rpn_idf_2023", - test=True + test=True, + _add_to_name=type(self).__name__ ) return super().setUp() diff --git a/grid2op/tests/test_issue_527.py b/grid2op/tests/test_issue_527.py index 3b7a6e381..4c0307593 100644 --- a/grid2op/tests/test_issue_527.py +++ b/grid2op/tests/test_issue_527.py @@ -19,7 +19,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make( "l2rpn_case14_sandbox", - test=True + test=True, + _add_to_name=type(self).__name__ ) self.env.seed(0) return super().setUp() diff --git a/grid2op/tests/test_issue_533.py b/grid2op/tests/test_issue_533.py index 016a71f12..edfe22357 100644 --- a/grid2op/tests/test_issue_533.py +++ b/grid2op/tests/test_issue_533.py @@ -19,6 +19,7 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make('l2rpn_neurips_2020_track1', test=True, + _add_to_name=type(self).__name__, ) self.env.seed(0) return super().setUp() diff --git a/grid2op/tests/test_issue_sim2real_storage.py b/grid2op/tests/test_issue_sim2real_storage.py index 64d167cb1..6cec033f9 100644 --- a/grid2op/tests/test_issue_sim2real_storage.py +++ b/grid2op/tests/test_issue_sim2real_storage.py @@ -23,10 +23,10 @@ def setUp(self) -> None: # print(f"\n\n\nfor {type(self.get_backend())}") with warnings.catch_warnings(): warnings.filterwarnings("ignore") - # self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "educ_case14_storage_diffgrid"), self.env = grid2op.make(os.path.join(PATH_DATA_TEST, self.get_name()), test=True, - backend=self.get_backend()) + backend=self.get_backend(), + _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_l2rpn_idf_2023.py b/grid2op/tests/test_l2rpn_idf_2023.py index b8aad0cfe..74ebf53bc 100644 --- a/grid2op/tests/test_l2rpn_idf_2023.py +++ b/grid2op/tests/test_l2rpn_idf_2023.py @@ -23,7 +23,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make("l2rpn_idf_2023", test=True) + self.env = grid2op.make("l2rpn_idf_2023", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_limit_curtail.py b/grid2op/tests/test_limit_curtail.py index 0146e5b32..03e94008a 100644 --- a/grid2op/tests/test_limit_curtail.py +++ b/grid2op/tests/test_limit_curtail.py @@ -29,7 +29,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make("l2rpn_wcci_2022_dev", test=True, - backend=LightSimBackend()) + backend=LightSimBackend(), + _add_to_name=type(self).__name__) self.act = self.env.action_space() tmp_ = np.zeros(self.env.n_gen, dtype=float) -1 diff --git a/grid2op/tests/test_multi_steps_env.py b/grid2op/tests/test_multi_steps_env.py index 25d0cc064..215de2676 100644 --- a/grid2op/tests/test_multi_steps_env.py +++ b/grid2op/tests/test_multi_steps_env.py @@ -25,7 +25,8 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend - self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True) + self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True, + _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) self.dn = self.env.action_space() diff --git a/grid2op/tests/test_multi_steps_forecasts.py b/grid2op/tests/test_multi_steps_forecasts.py index 8054754db..2608f3cb0 100644 --- a/grid2op/tests/test_multi_steps_forecasts.py +++ b/grid2op/tests/test_multi_steps_forecasts.py @@ -25,7 +25,8 @@ class MultiStepsForcaTester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True) + self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True, + _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) @@ -103,7 +104,8 @@ def test_cache(self): warnings.filterwarnings("ignore") env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True, - chronics_class=MultifolderWithCache) + chronics_class=MultifolderWithCache, + _add_to_name=type(self).__name__) env.seed(0) env.set_id(0) @@ -240,7 +242,8 @@ class ChainSimulateTester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True) + self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True, + _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) @@ -422,7 +425,8 @@ class SoftOverflowTester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True) + self.env = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True, + _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) a_or_first = np.array([442.308, 198.55365, 116.50534, 93.63006, diff --git a/grid2op/tests/test_nb_simulate_called.py b/grid2op/tests/test_nb_simulate_called.py index cd92deea8..65d832f70 100644 --- a/grid2op/tests/test_nb_simulate_called.py +++ b/grid2op/tests/test_nb_simulate_called.py @@ -26,9 +26,9 @@ def _aux_make_env(self, param=None): with warnings.catch_warnings(): warnings.filterwarnings("ignore") if param is not None: - env = grid2op.make("l2rpn_case14_sandbox", test=True, param=param) + env = grid2op.make("l2rpn_case14_sandbox", test=True, param=param, _add_to_name=type(self).__name__) else: - env = grid2op.make("l2rpn_case14_sandbox", test=True) + env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) return env def test_simple_cases(self): diff --git a/grid2op/tests/test_no_backend_copy.py b/grid2op/tests/test_no_backend_copy.py index 840f47044..ada1e7336 100644 --- a/grid2op/tests/test_no_backend_copy.py +++ b/grid2op/tests/test_no_backend_copy.py @@ -42,7 +42,7 @@ def setUp(self) -> None: env_name = "l2rpn_case14_sandbox" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make(env_name, test=True, backend=PPNoCpy()) + self.env = grid2op.make(env_name, test=True, backend=PPNoCpy(), _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env.close() @@ -84,7 +84,7 @@ def setUp(self) -> None: env_name = "l2rpn_case14_sandbox" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make(env_name, test=True, backend=PPNoCpyInCtor()) + self.env = grid2op.make(env_name, test=True, backend=PPNoCpyInCtor(), _add_to_name=type(self).__name__) if __name__ == "__main__": diff --git a/grid2op/tests/test_noisy_obs.py b/grid2op/tests/test_noisy_obs.py index f78edd668..f1901e9e2 100644 --- a/grid2op/tests/test_noisy_obs.py +++ b/grid2op/tests/test_noisy_obs.py @@ -21,7 +21,8 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - "educ_case14_storage", test=True, observation_class=NoisyObservation + "educ_case14_storage", test=True, observation_class=NoisyObservation, + _add_to_name=type(self).__name__ ) self.env.seed(0) self.env.set_id(0) @@ -127,7 +128,8 @@ def test_simulate(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "educ_case14_storage", test=True, observation_class=CompleteObservation + "educ_case14_storage", test=True, observation_class=CompleteObservation, + _add_to_name=type(self).__name__ ) env.seed(0) env.set_id(0) @@ -184,6 +186,7 @@ def setUp(self) -> None: test=True, observation_class=NoisyObservation, kwargs_observation=kwargs_observation, + _add_to_name=type(self).__name__, ) self.env.seed(0) self.env.set_id(0) @@ -199,6 +202,7 @@ def test_param_working(self): test=True, observation_class=NoisyObservation, kwargs_observation=kwargs_observation, + _add_to_name=type(self).__name__ ) env.seed(0) env.set_id(0) diff --git a/grid2op/tests/test_opp_with_area.py b/grid2op/tests/test_opp_with_area.py index 8462f0c67..f933df458 100644 --- a/grid2op/tests/test_opp_with_area.py +++ b/grid2op/tests/test_opp_with_area.py @@ -14,7 +14,6 @@ GeometricOpponent ) from grid2op.Action import TopologyAction -from grid2op.MakeEnv import make from grid2op.Opponent.baseActionBudget import BaseActionBudget from grid2op.dtypes import dt_int from grid2op.Parameters import Parameters @@ -28,7 +27,7 @@ class TestMultiAreaOpponentBasic(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.opponent = GeometricOpponentMultiArea(self.env.action_space) self.opponent.init(self.env, lines_attacked=[LINES_ATTACKED[:3],LINES_ATTACKED[3:]], @@ -58,9 +57,9 @@ def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = make("l2rpn_case14_sandbox", + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, - _add_to_name="multiarea", + _add_to_name=type(self).__name__+"multiarea", opponent_budget_per_ts=0.17*2, # 0.17 per area opponent_init_budget=1000, # I don't really care much right now opponent_attack_cooldown=0, # otherwise it will not work diff --git a/grid2op/tests/test_pickling.py b/grid2op/tests/test_pickling.py index 560cad06d..9521bba75 100644 --- a/grid2op/tests/test_pickling.py +++ b/grid2op/tests/test_pickling.py @@ -24,7 +24,7 @@ with warnings.catch_warnings(): # this needs to be imported in the main module for multiprocessing to work "approximately" warnings.filterwarnings("ignore") - _ = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name="for_mp_test") + _ = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__+"for_mp_test") class TestMultiProc(unittest.TestCase): @@ -41,7 +41,7 @@ def test_basic(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "l2rpn_case14_sandbox", test=True, _add_to_name="for_mp_test" + "l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__+"for_mp_test" ) env_gym = GymEnv(env) diff --git a/grid2op/tests/test_recopowerlineperarea.py b/grid2op/tests/test_recopowerlineperarea.py index 7cbeedc6a..19a68f249 100644 --- a/grid2op/tests/test_recopowerlineperarea.py +++ b/grid2op/tests/test_recopowerlineperarea.py @@ -27,7 +27,7 @@ class TestRecoPowerlinePerArea(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_idf_2023", test=True) + self.env = grid2op.make("l2rpn_idf_2023", test=True, _add_to_name=type(self).__name__) param = self.env.parameters param.NO_OVERFLOW_DISCONNECTION = True self.env.change_parameters(param) diff --git a/grid2op/tests/test_redisp_extreme.py b/grid2op/tests/test_redisp_extreme.py index 9662a4f52..d23abdbcd 100644 --- a/grid2op/tests/test_redisp_extreme.py +++ b/grid2op/tests/test_redisp_extreme.py @@ -32,7 +32,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make( self.env_name, - test=True + test=True, + _add_to_name=type(self).__name__ ) # retrieve the reference values, without curtailment @@ -559,6 +560,7 @@ def setUp(self) -> None: data_feeding_kwargs={"max_iter": 10}, _add_to_name="TestExtremeStorage", action_class=PlayableAction, + _add_to_name=type(self).__name__, ) # increase the storage capacity increase_storage = np.array([15.0, 30.0]) diff --git a/grid2op/tests/test_remove_line_status_from_topo.py b/grid2op/tests/test_remove_line_status_from_topo.py index a9d7ba92b..0c4d995a8 100644 --- a/grid2op/tests/test_remove_line_status_from_topo.py +++ b/grid2op/tests/test_remove_line_status_from_topo.py @@ -18,7 +18,7 @@ class RemoveLineStatusFromTopoTester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) param = self.env.parameters param.NB_TIMESTEP_COOLDOWN_SUB = 3 diff --git a/grid2op/tests/test_render.py b/grid2op/tests/test_render.py index cce64b13d..0ddd08b1a 100644 --- a/grid2op/tests/test_render.py +++ b/grid2op/tests/test_render.py @@ -15,7 +15,7 @@ class BaseTestPlot(unittest.TestCase): def setUp(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) def tearDown(self): self.env.close() diff --git a/grid2op/tests/test_reward_to_obs.py b/grid2op/tests/test_reward_to_obs.py index c1a00e2e2..bb7790b25 100644 --- a/grid2op/tests/test_reward_to_obs.py +++ b/grid2op/tests/test_reward_to_obs.py @@ -34,7 +34,8 @@ def setUp(self): warnings.filterwarnings("ignore") self.env = grid2op.make("l2rpn_case14_sandbox", test=True, observation_class=CustomTestObservation, - reward_class=CustomTestReward) + reward_class=CustomTestReward, + _add_to_name=type(self).__name__) CustomTestReward.nb_call = 0 def tearDown(self): diff --git a/grid2op/tests/test_runner_kwargs_backend.py b/grid2op/tests/test_runner_kwargs_backend.py index 1eac59a21..f85742898 100644 --- a/grid2op/tests/test_runner_kwargs_backend.py +++ b/grid2op/tests/test_runner_kwargs_backend.py @@ -48,7 +48,8 @@ def setUp(self) -> None: self.env_name = "l2rpn_case14_sandbox" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make(self.env_name, test=True, backend=PPExtraArgs()) + self.env = grid2op.make(self.env_name, test=True, backend=PPExtraArgs(), + _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env.close() @@ -71,7 +72,8 @@ def test_non_default_args(self): warnings.filterwarnings("ignore") self.env = grid2op.make(self.env_name, test=True, - backend=PPExtraArgs(stuff="toto")) + backend=PPExtraArgs(stuff="toto"), + _add_to_name=type(self).__name__) runner = Runner(**self.env.get_params_for_runner()) env = runner.init_env() assert env.backend._my_kwargs["stuff"] == "toto" @@ -85,7 +87,8 @@ def test_make_no_copy(self): warnings.filterwarnings("ignore") self.env = grid2op.make(self.env_name, test=True, - backend=PPExtraArgs(stuff=counter)) + backend=PPExtraArgs(stuff=counter), + _add_to_name=type(self).__name__) runner = Runner(**self.env.get_params_for_runner()) env = runner.init_env() assert isinstance(env.backend._my_kwargs["stuff"], InstanceCount) diff --git a/grid2op/tests/test_score_idf_2023.py b/grid2op/tests/test_score_idf_2023.py index 8fbcbbb00..c2be7cd79 100644 --- a/grid2op/tests/test_score_idf_2023.py +++ b/grid2op/tests/test_score_idf_2023.py @@ -21,7 +21,8 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make(env_name, - test=True) + test=True, + _add_to_name=type(self).__name__) self.env.set_max_iter(20) params = self.env.parameters diff --git a/grid2op/tests/test_score_idf_2023_assistant.py b/grid2op/tests/test_score_idf_2023_assistant.py index 4de9fee57..bb5b4b210 100644 --- a/grid2op/tests/test_score_idf_2023_assistant.py +++ b/grid2op/tests/test_score_idf_2023_assistant.py @@ -48,7 +48,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env = grid2op.make(env_name, - test=True) + test=True, + _add_to_name=type(self).__name__) self.env.set_max_iter(30) params = self.env.parameters diff --git a/grid2op/tests/test_score_idf_2023_nres.py b/grid2op/tests/test_score_idf_2023_nres.py index 61537814f..28b9487fe 100644 --- a/grid2op/tests/test_score_idf_2023_nres.py +++ b/grid2op/tests/test_score_idf_2023_nres.py @@ -64,7 +64,8 @@ def setUp(self) -> None: "gen_v_for_handler": PerfectForecastHandler("prod_v_forecasted", quiet_warnings=True), "load_p_for_handler": PerfectForecastHandler("load_p_forecasted", quiet_warnings=True), "load_q_for_handler": PerfectForecastHandler("load_q_forecasted", quiet_warnings=True), - },) + }, + _add_to_name=type(self).__name__) self.env.set_max_iter(20) params = self.env.parameters params.NO_OVERFLOW_DISCONNECTION = True diff --git a/grid2op/tests/test_score_wcci_2022.py b/grid2op/tests/test_score_wcci_2022.py index 307c30b4c..24bd9fc25 100644 --- a/grid2op/tests/test_score_wcci_2022.py +++ b/grid2op/tests/test_score_wcci_2022.py @@ -35,7 +35,10 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("educ_case14_storage", test=True, reward_class=L2RPNWCCI2022ScoreFun) + self.env = grid2op.make("educ_case14_storage", + test=True, + reward_class=L2RPNWCCI2022ScoreFun, + _add_to_name=type(self).__name__) def tearDown(self) -> None: self.env.close() @@ -77,7 +80,8 @@ def test_storage_cost_2(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make("educ_case14_storage", test=True, - reward_class=L2RPNWCCI2022ScoreFun(storage_cost=storage_cost)) + reward_class=L2RPNWCCI2022ScoreFun(storage_cost=storage_cost), + _add_to_name=type(self).__name__) score_fun = L2RPNWCCI2022ScoreFun(storage_cost=storage_cost) score_fun.initialize(self.env) th_val = storage_cost * 10. / 12. @@ -144,7 +148,7 @@ class TestL2RPNWCCI2022ScoreFun(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_wcci_2022", test=True) + self.env = grid2op.make("l2rpn_wcci_2022", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.reset() self.score_fun = L2RPNWCCI2022ScoreFun() diff --git a/grid2op/tests/test_simenv_blackout.py b/grid2op/tests/test_simenv_blackout.py index 7ec3c1bef..deca94c62 100644 --- a/grid2op/tests/test_simenv_blackout.py +++ b/grid2op/tests/test_simenv_blackout.py @@ -33,7 +33,8 @@ def setUp(self) -> None: env_name = "l2rpn_case14_sandbox" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make(env_name, test=True, reward_class=SimEnvRewardTester) + self.env = grid2op.make(env_name, test=True, reward_class=SimEnvRewardTester, + _add_to_name=type(self).__name__) return super().setUp() def tearDown(self) -> None: diff --git a/grid2op/tests/test_simulate_disco_load.py b/grid2op/tests/test_simulate_disco_load.py index c0412bc7c..a92d5aa28 100644 --- a/grid2op/tests/test_simulate_disco_load.py +++ b/grid2op/tests/test_simulate_disco_load.py @@ -25,7 +25,7 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") # this needs to be tested with pandapower backend # self.env = grid2op.make("l2rpn_case14_sandbox", backend=LightSimBackend(), test=True) # TODO when lightsim will be fixed ! - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) diff --git a/grid2op/tests/test_simulator.py b/grid2op/tests/test_simulator.py index 9536490db..cda569e7f 100644 --- a/grid2op/tests/test_simulator.py +++ b/grid2op/tests/test_simulator.py @@ -22,7 +22,7 @@ class TestSimulator(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.obs = self.env.reset() @@ -158,7 +158,8 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make( - "educ_case14_storage", test=True, action_class=PlayableAction + "educ_case14_storage", test=True, action_class=PlayableAction, + _add_to_name=type(self).__name__ ) self.env.seed(0) self.obs = self.env.reset() diff --git a/grid2op/tests/test_soft_overflow_threshold.py b/grid2op/tests/test_soft_overflow_threshold.py index c1fd921b0..75a45dabf 100644 --- a/grid2op/tests/test_soft_overflow_threshold.py +++ b/grid2op/tests/test_soft_overflow_threshold.py @@ -16,7 +16,7 @@ class TestSoftOverflowThreshold(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True) + self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) self.env.seed(0) self.env.set_id(0) th_lim = self.env.get_thermal_limit() diff --git a/grid2op/tests/test_timeOutEnvironment.py b/grid2op/tests/test_timeOutEnvironment.py index 460826c79..209ee9e1c 100644 --- a/grid2op/tests/test_timeOutEnvironment.py +++ b/grid2op/tests/test_timeOutEnvironment.py @@ -67,7 +67,7 @@ def get_timeout_ms(self): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = TimedOutEnvironment(grid2op.make("l2rpn_case14_sandbox", test=True), + self.env1 = TimedOutEnvironment(grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__), time_out_ms=self.get_timeout_ms()) params = self.env1.parameters params.NO_OVERFLOW_DISCONNECTION = True @@ -158,7 +158,7 @@ def get_timeout_ms(self): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = TimedOutEnvironment(grid2op.make("l2rpn_case14_sandbox", test=True), + self.env1 = TimedOutEnvironment(grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__), time_out_ms=self.get_timeout_ms()) params = self.env1.parameters params.NO_OVERFLOW_DISCONNECTION = True @@ -226,7 +226,7 @@ def get_timeout_ms(self): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = TimedOutEnvironment(grid2op.make("l2rpn_case14_sandbox", test=True), + self.env1 = TimedOutEnvironment(grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__), time_out_ms=self.get_timeout_ms()) def tearDown(self) -> None: diff --git a/grid2op/tests/test_ts_handlers.py b/grid2op/tests/test_ts_handlers.py index 24456ee4e..2e00ae061 100644 --- a/grid2op/tests/test_ts_handlers.py +++ b/grid2op/tests/test_ts_handlers.py @@ -59,7 +59,7 @@ def _aux_reproducibility(self): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = grid2op.make("l2rpn_case14_sandbox", test=True) # regular env + self.env1 = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) # regular env self.env2 = grid2op.make("l2rpn_case14_sandbox", data_feeding_kwargs={"gridvalueClass": FromHandlers, "gen_p_handler": CSVHandler("prod_p"), @@ -67,7 +67,7 @@ def setUp(self) -> None: "gen_v_handler": CSVHandler("prod_v"), "load_q_handler": CSVHandler("load_q"), }, - _add_to_name="_TestCSVHandlerEnv", + _add_to_name=type(self).__name__+"_TestCSVHandlerEnv", test=True) # regular env self._aux_reproducibility() return super().setUp() @@ -186,7 +186,7 @@ def test_if_file_absent(self): "gen_v_handler": DoNothingHandler(), "load_q_handler": CSVHandler("load_q"), # crash because this file does not exist }, - _add_to_name="_TestCSVHandlerEnv") # regular env + _add_to_name=type(self).__name__+"_TestCSVHandlerEnv") # regular env def test_max_episode_duration(self): assert self.env2.max_episode_duration() == self.env1.max_episode_duration() @@ -239,7 +239,7 @@ class TestSomeFileMissingEnv(TestCSVHandlerEnv): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_some_missing")) # regular env + self.env1 = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_some_missing"), _add_to_name=type(self).__name__) # regular env self.env2 = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_some_missing"), data_feeding_kwargs={"gridvalueClass": FromHandlers, "gen_p_handler": CSVHandler("prod_p"), @@ -247,7 +247,7 @@ def setUp(self) -> None: "gen_v_handler": DoNothingHandler(), "load_q_handler": DoNothingHandler(), }, - _add_to_name="_TestDNHandlerEnv") + _add_to_name=type(self).__name__+"_TestDNHandlerEnv") self._aux_reproducibility() @@ -261,7 +261,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env1 = grid2op.make("l2rpn_case14_sandbox", test=True, chronics_class=GridStateFromFileWithForecasts, - chronics_path=chronics_path) # regular env + chronics_path=chronics_path, + _add_to_name=type(self).__name__) # regular env self.env2 = grid2op.make("l2rpn_case14_sandbox", chronics_class=FromHandlers, data_feeding_kwargs={ @@ -271,7 +272,7 @@ def setUp(self) -> None: "load_q_handler": CSVHandler("load_q"), }, chronics_path=chronics_path, - _add_to_name="TestWithoutMultiFolderEnv", + _add_to_name=type(self).__name__+"TestWithoutMultiFolderEnv", test=True) self._aux_reproducibility() @@ -291,7 +292,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env1 = grid2op.make("l2rpn_case14_sandbox", test=True, chronics_class=GridStateFromFileWithForecasts, - chronics_path=chronics_path) # regular env + chronics_path=chronics_path, + _add_to_name=type(self).__name__) # regular env self.env2 = grid2op.make("l2rpn_case14_sandbox", chronics_class=FromHandlers, data_feeding_kwargs={"gen_p_handler": CSVHandler("prod_p"), @@ -304,7 +306,7 @@ def setUp(self) -> None: "load_q_for_handler": CSVForecastHandler("load_q_forecasted"), }, chronics_path=chronics_path, - _add_to_name="TestForecastHandlerNoMulti14", + _add_to_name=type(self).__name__+"TestForecastHandlerNoMulti14", test=True) self._aux_reproducibility() assert np.all(self.env1.chronics_handler.real_data.load_p_forecast == @@ -321,7 +323,7 @@ class TestForecastHandler14(TestCSVHandlerEnv): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env1 = grid2op.make("l2rpn_case14_sandbox", test=True) # regular env + self.env1 = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) # regular env self.env2 = grid2op.make("l2rpn_case14_sandbox", data_feeding_kwargs={"gridvalueClass": FromHandlers, "gen_p_handler": CSVHandler("prod_p"), @@ -333,7 +335,7 @@ def setUp(self) -> None: "gen_v_for_handler": CSVForecastHandler("prod_v_forecasted"), "load_q_for_handler": CSVForecastHandler("load_q_forecasted"), }, - _add_to_name="TestForecastHandlerEnv", + _add_to_name=type(self).__name__+"TestForecastHandlerEnv", test=True) self._aux_reproducibility() assert np.all(self.env1.chronics_handler.real_data.data.load_p_forecast == @@ -353,6 +355,7 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env1 = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), test=True, data_feeding_kwargs={"gridvalueClass": GridStateFromFileWithForecastsWithoutMaintenance}, + _add_to_name=type(self).__name__, ) # regular env self.env2 = grid2op.make(os.path.join(PATH_DATA_TEST, "5bus_example_forecasts"), data_feeding_kwargs={"gridvalueClass": FromHandlers, @@ -364,7 +367,7 @@ def setUp(self) -> None: "load_p_for_handler": CSVForecastHandler("load_p_forecasted"), "load_q_for_handler": CSVForecastHandler("load_q_forecasted"), }, - _add_to_name="TestForecastHandler5MultiSteps", + _add_to_name=type(self).__name__+"TestForecastHandler5MultiSteps", test=True) self._aux_reproducibility() assert np.all(self.env1.chronics_handler.real_data.data.load_p == @@ -399,7 +402,8 @@ def setUp(self) -> None: warnings.filterwarnings("ignore") self.env1 = grid2op.make(os.path.join(PATH_DATA_TEST, "env_14_test_maintenance"), test=True, - param=param + param=param, + _add_to_name=type(self).__name__ ) # regular env self.env2 = grid2op.make(os.path.join(PATH_DATA_TEST, "env_14_test_maintenance"), data_feeding_kwargs={"gridvalueClass": FromHandlers, @@ -413,7 +417,7 @@ def setUp(self) -> None: "load_p_for_handler": CSVForecastHandler("load_p_forecasted"), "load_q_for_handler": CSVForecastHandler("load_q_forecasted"), }, - _add_to_name="TestMaintenanceCSV", + _add_to_name=type(self).__name__+"TestMaintenanceCSV", test=True, param=param) self._aux_reproducibility() @@ -442,7 +446,7 @@ def setUp(self) -> None: "load_p_for_handler": CSVForecastHandler("load_p_forecasted"), "load_q_for_handler": CSVForecastHandler("load_q_forecasted"), }, - _add_to_name="TestMaintenanceCSV", + _add_to_name=type(self).__name__+"TestMaintenanceCSV", test=True, param=param) # carefull here ! the "seed" mechanism does not work the same way between the two alternative. @@ -499,7 +503,7 @@ def setUp(self) -> None: "load_p_for_handler": PersistenceForecastHandler("load_p_forecasted"), "load_q_for_handler": PersistenceForecastHandler("load_q_forecasted"), }, - _add_to_name="TestPersistenceHandler", + _add_to_name=type(self).__name__+"TestPersistenceHandler", test=True) def tearDown(self) -> None: @@ -585,7 +589,7 @@ def setUp(self) -> None: "load_p_for_handler": PerfectForecastHandler("load_p_forecasted"), "load_q_for_handler": PerfectForecastHandler("load_q_forecasted"), }, - _add_to_name="TestPerfectForecastHandler", + _add_to_name=type(self).__name__+"TestPerfectForecastHandler", test=True) def tearDown(self) -> None: self.env.close() @@ -623,7 +627,7 @@ def setUp(self) -> None: "load_p_for_handler": NoisyForecastHandler("load_p_forecasted"), "load_q_for_handler": NoisyForecastHandler("load_q_forecasted"), }, - _add_to_name="TestPerfectForecastHandler", + _add_to_name=type(self).__name__+"TestPerfectForecastHandler", test=True) def tearDown(self) -> None: self.env.close() @@ -717,7 +721,7 @@ def setUp(self) -> None: "load_p_for_handler": CSVForecastHandler("load_p_forecasted"), "load_q_for_handler": LoadQFromPHandler("load_q_forecasted"), }, - _add_to_name="TestForecastHandlerEnv", + _add_to_name=type(self).__name__+"TestForecastHandlerEnv", test=True) self._aux_reproducibility() diff --git a/grid2op/tests/test_utils.py b/grid2op/tests/test_utils.py index f3ae5b3c5..dad5e5b64 100644 --- a/grid2op/tests/test_utils.py +++ b/grid2op/tests/test_utils.py @@ -15,7 +15,7 @@ PATH_PREVIOUS_RUNNER = os.path.join(data_test_dir, "runner_data") from grid2op.Reward import L2RPNSandBoxScore -from grid2op.MakeEnv import make +import grid2op from grid2op.dtypes import dt_float from grid2op.Agent import DoNothingAgent, RecoPowerlineAgent from grid2op.utils import EpisodeStatistics, ScoreL2RPN2020, ScoreICAPS2021 @@ -31,7 +31,7 @@ def test_read(self): """test that i can read the data stored""" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: obs = env.reset() stats = EpisodeStatistics(env) aor_, ids_ = stats.get("a_or") @@ -59,7 +59,7 @@ def test_compute_erase(self): """test that i can compute and erase the results afterwards""" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: stats = EpisodeStatistics(env, "test") stats.compute(nb_scenario=1, max_step=10, pbar=False) # the file have been created @@ -84,7 +84,7 @@ def test_compute_with_score(self): """test that i can compute and erase the results afterwards""" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: stats = EpisodeStatistics(env, "test") stats.compute( nb_scenario=2, @@ -110,7 +110,7 @@ def test_compute_without_score(self): """test that i can compute and erase the results afterwards""" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: stats = EpisodeStatistics(env, "test") stats.compute(nb_scenario=2, max_step=10, pbar=False) # i can access it @@ -134,7 +134,7 @@ def test_can_compute(self): """test that i can initialize the score and then delete the statistics""" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: scores = ScoreL2RPN2020(env, nb_scenario=4, verbose=0, max_step=50) # the statistics have been properly computed @@ -176,7 +176,7 @@ def test_donothing_0(self): """test that do nothing has a score of 0.00""" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: scores = ScoreL2RPN2020(env, nb_scenario=4, verbose=0, max_step=20) # the statistics have been properly computed @@ -225,7 +225,7 @@ def test_modif_max_step_decrease(self): """ with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: scores = ScoreL2RPN2020(env, nb_scenario=2, verbose=0, max_step=15) # the statistics have been properly computed @@ -282,7 +282,7 @@ def test_modif_max_step_increase(self): """test that i can modify the max step (and that if I increase it it does trigger a recomputation)""" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: scores = ScoreL2RPN2020(env, nb_scenario=2, verbose=0, max_step=5) # the statistics have been properly computed @@ -337,7 +337,7 @@ def test_modif_nb_scenario(self): """ with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: scores = ScoreL2RPN2020(env, nb_scenario=2, verbose=0, max_step=5) # the statistics have been properly computed @@ -393,7 +393,7 @@ def test_reco_noov_80(self): """test that do nothing has a score of 80.0 if it is run with "no overflow disconnection" """ with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("rte_case5_example", test=True) as env: + with grid2op.make("rte_case5_example", test=True, _add_to_name=type(self).__name__) as env: # I cannot decrease the max step: it must be above the number of steps the do nothing does scores = ScoreL2RPN2020(env, nb_scenario=2, verbose=0, max_step=130) assert scores._recomputed_dn @@ -423,7 +423,7 @@ def test_reco_noov_80(self): param = Parameters() param.NO_OVERFLOW_DISCONNECTION = True - with make("rte_case5_example", test=True, param=param) as env: + with grid2op.make("rte_case5_example", test=True, param=param, _add_to_name=type(self).__name__) as env: scores2 = ScoreL2RPN2020(env, nb_scenario=2, verbose=0, max_step=130) assert not scores2._recomputed_dn assert not scores2._recomputed_no_ov_rp @@ -458,9 +458,10 @@ def test_can_compute(self): """test that i can initialize the score and then delete the statistics""" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make( + with grid2op.make( os.path.join(PATH_DATA_TEST, "l2rpn_neurips_2020_track1_with_alarm"), test=True, + _add_to_name=type(self).__name__ ) as env: scores = ScoreICAPS2021( env, From 6248781a1a79b77fd41b3217dd1000ab250d0cd5 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 12 Oct 2023 15:55:23 +0200 Subject: [PATCH 21/46] fixing broken tests after refacto --- grid2op/tests/BaseBackendTest.py | 4 ++- grid2op/tests/test_AgentsFast.py | 1 + grid2op/tests/test_BackendConverter.py | 1 + grid2op/tests/test_EnvironmentCpy.py | 1 + grid2op/tests/test_GymConverter.py | 26 +++++++++---------- grid2op/tests/test_Observation.py | 5 ++-- .../test_PandaPowerBackendDefaultFunc.py | 8 +++--- grid2op/tests/test_PlotGrid.py | 1 + grid2op/tests/test_RedispatchEnv.py | 4 +-- grid2op/tests/test_issue_217.py | 4 +-- grid2op/tests/test_noisy_obs.py | 2 +- grid2op/tests/test_opp_with_area.py | 1 + grid2op/tests/test_pickling.py | 4 +-- 13 files changed, 35 insertions(+), 27 deletions(-) diff --git a/grid2op/tests/BaseBackendTest.py b/grid2op/tests/BaseBackendTest.py index 57f259d87..e7a13422f 100644 --- a/grid2op/tests/BaseBackendTest.py +++ b/grid2op/tests/BaseBackendTest.py @@ -20,6 +20,8 @@ from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST PATH_DATA_TEST_INIT = PATH_DATA_TEST PATH_DATA_TEST = PATH_DATA_TEST_PP + +import grid2op from grid2op.tests.helper_path_test import HelperTests from grid2op.Action import CompleteAction @@ -2003,7 +2005,7 @@ def test_reco_disco_bus2(self): type(backend)._clear_class_attribute() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_case2 = gridop.make( + env_case2 = grid2op.make( "rte_case5_example", test=True, gamerules_class=AlwaysLegal, diff --git a/grid2op/tests/test_AgentsFast.py b/grid2op/tests/test_AgentsFast.py index f902385d8..43ed3ff30 100644 --- a/grid2op/tests/test_AgentsFast.py +++ b/grid2op/tests/test_AgentsFast.py @@ -13,6 +13,7 @@ from grid2op.tests.helper_path_test import * +import grid2op from grid2op.Exceptions import * from grid2op.Agent import DoNothingAgent, BaseAgent from grid2op.Parameters import Parameters diff --git a/grid2op/tests/test_BackendConverter.py b/grid2op/tests/test_BackendConverter.py index 4d5d64552..4b5894726 100644 --- a/grid2op/tests/test_BackendConverter.py +++ b/grid2op/tests/test_BackendConverter.py @@ -10,6 +10,7 @@ import unittest from grid2op.Converter import BackendConverter +import grid2op from grid2op.tests.helper_path_test import * from grid2op import make from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST diff --git a/grid2op/tests/test_EnvironmentCpy.py b/grid2op/tests/test_EnvironmentCpy.py index 00fd38d3b..93778e574 100644 --- a/grid2op/tests/test_EnvironmentCpy.py +++ b/grid2op/tests/test_EnvironmentCpy.py @@ -15,6 +15,7 @@ from grid2op.tests.helper_path_test import * +import grid2op from grid2op.Reward import L2RPNReward from grid2op.tests.test_Environment import ( TestLoadingBackendPandaPower as Aux_TestLoadingBackendPandaPower, diff --git a/grid2op/tests/test_GymConverter.py b/grid2op/tests/test_GymConverter.py index 20798a10d..de0ca0e49 100644 --- a/grid2op/tests/test_GymConverter.py +++ b/grid2op/tests/test_GymConverter.py @@ -14,8 +14,8 @@ from grid2op.gym_compat import (DiscreteActSpace, GymActionSpace, GymObservationSpace, GymEnv, ContinuousToDiscreteConverter) from grid2op.tests.helper_path_test import * -from grid2op.Action import PlayableAction import grid2op +from grid2op.Action import PlayableAction from grid2op.dtypes import dt_float, dt_bool, dt_int from grid2op.Converter import IdToAct, ToVect import pdb @@ -74,7 +74,7 @@ def test_creation(self): ) else: env_path_or_name = self.get_env_name() - with make(env_path_or_name, test=True) as env: + with grid2op.make(env_path_or_name, test=True) as env: # test i can create obs_space = GymObservationSpace(env) act_space = GymActionSpace(env) @@ -82,7 +82,7 @@ def test_creation(self): def test_json(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make(self.get_env_name(), test=True) as env: + with grid2op.make(self.get_env_name(), test=True) as env: # test i can create obs_space = GymObservationSpace(env) act_space = GymActionSpace(env) @@ -96,7 +96,7 @@ def test_json(self): def test_to_from_gym_obs(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make(self.get_env_name(), test=True) as env: + with grid2op.make(self.get_env_name(), test=True) as env: obs_space = GymObservationSpace(env) obs = env.reset() @@ -139,7 +139,7 @@ def test_to_from_gym_obs(self): def test_to_from_gym_act(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make(self.get_env_name(), test=True) as env: + with grid2op.make(self.get_env_name(), test=True) as env: act_space = GymActionSpace(env) act = env.action_space() @@ -166,7 +166,7 @@ def init_converter(self, env): def test_creation(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("l2rpn_wcci_2020", test=True) as env: + with grid2op.make("l2rpn_wcci_2020", test=True) as env: # test i can create converter = self.init_converter(env) act_space = GymActionSpace(env=env, converter=converter) @@ -175,7 +175,7 @@ def test_creation(self): def test_json(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("l2rpn_wcci_2020", test=True) as env: + with grid2op.make("l2rpn_wcci_2020", test=True) as env: # test i can create converter = self.init_converter(env) act_space = GymActionSpace(env=env, converter=converter) @@ -185,7 +185,7 @@ def test_json(self): def test_to_from_gym_act(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - with make("l2rpn_wcci_2020", test=True) as env: + with grid2op.make("l2rpn_wcci_2020", test=True) as env: converter = self.init_converter(env) act_space = GymActionSpace(env=env, converter=converter) act_space.seed(0) @@ -234,7 +234,7 @@ class TestDropAttr(unittest.TestCase): def test_keep_only_attr(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("educ_case14_redisp", test=True) + env = grid2op.make("educ_case14_redisp", test=True) gym_env = GymEnv(env) attr_kept = sorted( ("rho", "line_status", "actual_dispatch", "target_dispatch") @@ -246,7 +246,7 @@ def test_keep_only_attr(self): def test_ignore_attr(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("educ_case14_redisp", test=True) + env = grid2op.make("educ_case14_redisp", test=True) gym_env = GymEnv(env) attr_deleted = sorted( ("rho", "line_status", "actual_dispatch", "target_dispatch") @@ -266,7 +266,7 @@ def setUp(self) -> None: def test_split_in_3(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("educ_case14_redisp", test=True) + env = grid2op.make("educ_case14_redisp", test=True) gym_env = GymEnv(env) act_space = gym_env.action_space act_space = act_space.reencode_space( @@ -316,7 +316,7 @@ def test_split_in_3(self): def test_split_in_5(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make("educ_case14_redisp", test=True) + env = grid2op.make("educ_case14_redisp", test=True) gym_env = GymEnv(env) act_space = gym_env.action_space act_space = act_space.reencode_space( @@ -395,7 +395,7 @@ def setUp(self) -> None: self.filenamedict = "test_action_json_educ_case14_storage.json" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.glop_env = make( + self.glop_env = grid2op.make( "educ_case14_storage", test=True, action_class=PlayableAction ) diff --git a/grid2op/tests/test_Observation.py b/grid2op/tests/test_Observation.py index bc0080857..944653f1b 100644 --- a/grid2op/tests/test_Observation.py +++ b/grid2op/tests/test_Observation.py @@ -106,7 +106,8 @@ def setUp(self): ], "name_storage": [], "glop_version": grid2op.__version__, - "env_name": "rte_case14_test", + # "env_name": "rte_case14_test", + "env_name": "rte_case14_testTestBasisObsBehaviour", "sub_info": [3, 6, 4, 6, 5, 6, 3, 2, 5, 3, 3, 3, 4, 3], "load_to_subid": [1, 2, 13, 3, 4, 5, 8, 9, 10, 11, 12], "gen_to_subid": [1, 2, 5, 7, 0], @@ -2205,7 +2206,7 @@ def test_space_to_dict(self): ok_ = np.array_equal(val, val_res) assert ok_, (f"values different for {el}: " - f"{dict_[el]}" + f"{dict_[el]} vs " f"{self.dict_[el]}") # self.maxDiff = None diff --git a/grid2op/tests/test_PandaPowerBackendDefaultFunc.py b/grid2op/tests/test_PandaPowerBackendDefaultFunc.py index ddb25301e..847f1d3bb 100644 --- a/grid2op/tests/test_PandaPowerBackendDefaultFunc.py +++ b/grid2op/tests/test_PandaPowerBackendDefaultFunc.py @@ -12,7 +12,7 @@ import numpy as np -from grid2op import make +import grid2op from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST from grid2op.Backend import PandaPowerBackend, Backend @@ -281,7 +281,7 @@ def test_set_bus(self): backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make('rte_case14_realistic', test=True, backend=backend) + env = grid2op.make('rte_case14_realistic', test=True, backend=backend) env.reset() # action = env.action_space({"change_bus": {"lines_or_id": [17]}}) action = env.action_space({"set_bus": {"lines_or_id": [(17, 2)]}}) @@ -294,7 +294,7 @@ def test_change_bus(self): backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make('rte_case14_realistic', test=True, backend=backend) + env = grid2op.make('rte_case14_realistic', test=True, backend=backend) env.reset() action = env.action_space({"change_bus": {"lines_or_id": [17]}}) obs, reward, done, info = env.step(action) @@ -306,7 +306,7 @@ def test_change_bustwice(self): backend = self.make_backend() with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = make('rte_case14_realistic', test=True, backend=backend) + env = grid2op.make('rte_case14_realistic', test=True, backend=backend) env.reset() action = env.action_space({"change_bus": {"lines_or_id": [17]}}) obs, reward, done, info = env.step(action) diff --git a/grid2op/tests/test_PlotGrid.py b/grid2op/tests/test_PlotGrid.py index 4b7cb963a..5af4c0024 100644 --- a/grid2op/tests/test_PlotGrid.py +++ b/grid2op/tests/test_PlotGrid.py @@ -10,6 +10,7 @@ import unittest import warnings +import grid2op from grid2op.Exceptions import * from grid2op.PlotGrid import * diff --git a/grid2op/tests/test_RedispatchEnv.py b/grid2op/tests/test_RedispatchEnv.py index 191d8f645..cd261c507 100644 --- a/grid2op/tests/test_RedispatchEnv.py +++ b/grid2op/tests/test_RedispatchEnv.py @@ -9,9 +9,9 @@ import unittest from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST -from grid2op.Backend import PandaPowerBackend -from grid2op.tests.helper_path_test import HelperTests +import grid2op +from grid2op.Backend import PandaPowerBackend from grid2op.tests.BaseRedispTest import ( BaseTestRedispatch, BaseTestRedispatchChangeNothingEnvironment, diff --git a/grid2op/tests/test_issue_217.py b/grid2op/tests/test_issue_217.py index 6ddcdba42..85eb5552e 100644 --- a/grid2op/tests/test_issue_217.py +++ b/grid2op/tests/test_issue_217.py @@ -18,8 +18,8 @@ class Issue217Tester(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env_nm = os.path.join(PATH_DATA_TEST, "5bus_modif_grid", _add_to_name=type(self).__name__) - self.env = grid2op.make(env_nm, test=True, chronics_class=ChangeNothing) + env_nm = os.path.join(PATH_DATA_TEST, "5bus_modif_grid") + self.env = grid2op.make(env_nm, test=True, chronics_class=ChangeNothing, _add_to_name=type(self).__name__) self.env.seed(0) self.env.reset() diff --git a/grid2op/tests/test_noisy_obs.py b/grid2op/tests/test_noisy_obs.py index f1901e9e2..e51a5ba3a 100644 --- a/grid2op/tests/test_noisy_obs.py +++ b/grid2op/tests/test_noisy_obs.py @@ -113,7 +113,7 @@ def test_with_copy(self): def test_simulate(self): sim_o, *_ = self.obs.simulate(self.env.action_space()) - assert type(sim_o).env_name == "educ_case14_storage" + assert type(sim_o).env_name == "educ_case14_storage"+type(self).__name__ assert isinstance(sim_o, CompleteObservation) # test that it is reproducible diff --git a/grid2op/tests/test_opp_with_area.py b/grid2op/tests/test_opp_with_area.py index f933df458..73a635d92 100644 --- a/grid2op/tests/test_opp_with_area.py +++ b/grid2op/tests/test_opp_with_area.py @@ -9,6 +9,7 @@ import unittest import numpy as np import warnings +import grid2op from grid2op.Opponent import ( GeometricOpponentMultiArea, GeometricOpponent diff --git a/grid2op/tests/test_pickling.py b/grid2op/tests/test_pickling.py index 9521bba75..ea262d583 100644 --- a/grid2op/tests/test_pickling.py +++ b/grid2op/tests/test_pickling.py @@ -24,7 +24,7 @@ with warnings.catch_warnings(): # this needs to be imported in the main module for multiprocessing to work "approximately" warnings.filterwarnings("ignore") - _ = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__+"for_mp_test") + _ = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=__name__+"for_mp_test") class TestMultiProc(unittest.TestCase): @@ -41,7 +41,7 @@ def test_basic(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") env = grid2op.make( - "l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__+"for_mp_test" + "l2rpn_case14_sandbox", test=True, _add_to_name=__name__+"for_mp_test" ) env_gym = GymEnv(env) From 872ac765073d899ef52854b41d0de400d8c1d48d Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 13 Oct 2023 14:43:38 +0200 Subject: [PATCH 22/46] fix tests after refacto --- grid2op/tests/BaseRedispTest.py | 1 + grid2op/tests/test_RedispatchEnv.py | 4 ---- grid2op/tests/test_redisp_extreme.py | 8 ++++---- grid2op/tests/test_simulator.py | 4 +++- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/grid2op/tests/BaseRedispTest.py b/grid2op/tests/BaseRedispTest.py index 9f3df9069..749d332c7 100644 --- a/grid2op/tests/BaseRedispTest.py +++ b/grid2op/tests/BaseRedispTest.py @@ -11,6 +11,7 @@ import warnings from grid2op.tests.helper_path_test import * +import grid2op from grid2op.Exceptions import * from grid2op.Environment import Environment diff --git a/grid2op/tests/test_RedispatchEnv.py b/grid2op/tests/test_RedispatchEnv.py index cd261c507..2a12bc819 100644 --- a/grid2op/tests/test_RedispatchEnv.py +++ b/grid2op/tests/test_RedispatchEnv.py @@ -25,10 +25,6 @@ PATH_DATA_TEST_INIT = PATH_DATA_TEST PATH_DATA_TEST = PATH_DATA_TEST_PP -import warnings - -warnings.simplefilter("error") - class TestRedispatch(BaseTestRedispatch, unittest.TestCase): def setUp(self): diff --git a/grid2op/tests/test_redisp_extreme.py b/grid2op/tests/test_redisp_extreme.py index d23abdbcd..9e0f2e8f2 100644 --- a/grid2op/tests/test_redisp_extreme.py +++ b/grid2op/tests/test_redisp_extreme.py @@ -8,11 +8,12 @@ import warnings import os +import unittest import numpy as np + +from grid2op.tests.helper_path_test import * import grid2op from grid2op.Action.playableAction import PlayableAction -from grid2op.tests.helper_path_test import * -import unittest import pdb @@ -558,9 +559,8 @@ def setUp(self) -> None: self.env_name, test=True, data_feeding_kwargs={"max_iter": 10}, - _add_to_name="TestExtremeStorage", action_class=PlayableAction, - _add_to_name=type(self).__name__, + _add_to_name=type(self).__name__+"TestExtremeStorage", ) # increase the storage capacity increase_storage = np.array([15.0, 30.0]) diff --git a/grid2op/tests/test_simulator.py b/grid2op/tests/test_simulator.py index cda569e7f..52a819d09 100644 --- a/grid2op/tests/test_simulator.py +++ b/grid2op/tests/test_simulator.py @@ -22,7 +22,9 @@ class TestSimulator(unittest.TestCase): def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") - self.env = grid2op.make("l2rpn_case14_sandbox", test=True, _add_to_name=type(self).__name__) + self.env = grid2op.make("l2rpn_case14_sandbox", + test=True, + _add_to_name=type(self).__name__) self.env.seed(0) self.obs = self.env.reset() From fcd73658b930b07c9c779444225ebaf8cef4d53c Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 13 Oct 2023 15:58:39 +0200 Subject: [PATCH 23/46] fix tests after refacto --- .circleci/config.yml | 42 ++++++++++++++++++++++++++++++++++ CHANGELOG.rst | 2 ++ README.md | 5 ++-- grid2op/simulator/simulator.py | 20 ++++++++++++++-- 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a5618f363..b61ad2db6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,6 +21,9 @@ executors: python311: docker: - image: python:3.11-buster + python311: + docker: + - image: python:3.12-buster jobs: test: @@ -231,6 +234,13 @@ jobs: python -m pip install -U .[test] export _GRID2OP_FORCE_TEST=1 grid2op.testinstall + - run: + command: | + source venv_test/bin/activate + python -m pip install -U "numpy>=1.26,<1.27" + python -m pip install -U .[test] + export _GRID2OP_FORCE_TEST=1 + grid2op.testinstall install310: executor: python310 resource_class: small @@ -282,6 +292,13 @@ jobs: python -m pip install -U .[test] export _GRID2OP_FORCE_TEST=1 grid2op.testinstall + - run: + command: | + source venv_test/bin/activate + python -m pip install -U "numpy>=1.26,<1.27" + python -m pip install -U .[test] + export _GRID2OP_FORCE_TEST=1 + grid2op.testinstall install311: executor: python311 resource_class: small @@ -319,6 +336,31 @@ jobs: python -m pip install -U .[test] export _GRID2OP_FORCE_TEST=1 grid2op.testinstall + - run: + command: | + source venv_test/bin/activate + python -m pip install -U "numpy>=1.26,<1.27" + python -m pip install -U .[test] + export _GRID2OP_FORCE_TEST=1 + grid2op.testinstall + install312: + executor: python312 + resource_class: small + steps: + - checkout + - run: + command: | + apt-get update + apt-get install -y coinor-cbc + - run: python -m pip install virtualenv + - run: python -m virtualenv venv_test + - run: + command: | + source venv_test/bin/activate + python -m pip install -U "numpy>=1.26,<1.27" + python -m pip install -U .[test] + export _GRID2OP_FORCE_TEST=1 + grid2op.testinstall workflows: version: 2.1 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e7eb5c8a4..b920ee524 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -51,6 +51,8 @@ Change Log techniques - [IMPROVED] now easier than ever to run the grid2op test suite with a new backend (for relevant tests) - [IMPROVED] type hints for `Backend` and `PandapowerBackend` +- [IMPROVED] distribute python 3.12 wheel +- [IMPROVED] test for python 3.12 and numpy 1.26 when appropriate (*eg* when numpy version is released) [1.9.5] - 2023-09-18 --------------------- diff --git a/README.md b/README.md index 77222cc10..cddf1f5a9 100644 --- a/README.md +++ b/README.md @@ -296,11 +296,12 @@ The unit tests includes testing, on linux machines the correct integration of gr * python 3.9 * python 3.10 * python 3.11 +* python 3.12 -On all of these cases, we tested grid2op on all available numpy version >= 1.20 (**nb** available numpy versions depend +On all of these cases, we tested grid2op on all available numpy versions >= 1.20 (**nb** available numpy versions depend on python version). -The complete test suit is run on linux with the latest numpy version on python 3.8. +The complete test suit is run on linux with the latest numpy version on python 3.10. ### Known issues diff --git a/grid2op/simulator/simulator.py b/grid2op/simulator/simulator.py index 66b203e06..41dd719e9 100644 --- a/grid2op/simulator/simulator.py +++ b/grid2op/simulator/simulator.py @@ -198,9 +198,25 @@ def change_backend_type(self, backend_type: type, grid_path: os.PathLike, **kwar if not os.path.isfile(grid_path): raise SimulatorError(f'the supposed grid path "{grid_path}" if not a file') - tmp_backend = backend_type(**kwargs) - tmp_backend.load_grid(grid_path) + if backend_type._IS_INIT: + backend_type_init = backend_type + else: + backend_type_init= backend_type.init_grid(type(self.backend)) + + tmp_backend = backend_type_init(**kwargs) + # load a forecasted grid if there are any + path_env, grid_name_with_ext = os.path.split(grid_path) + grid_name, ext = os.path.splitext(grid_name_with_ext) + grid_forecast_name = f"{grid_name}_forecast.{ext}" + if os.path.exists(os.path.join(path_env, grid_forecast_name)): + grid_path_loaded = os.path.join(path_env, grid_forecast_name) + else: + grid_path_loaded = grid_path + tmp_backend.load_grid(grid_path_loaded) tmp_backend.assert_grid_correct() + tmp_backend.runpf() + tmp_backend.assert_grid_correct_after_powerflow() + tmp_backend.set_thermal_limit(self.backend.get_thermal_limit()) self.backend.close() self.backend = tmp_backend self.set_state(obs=self.current_obs) From db9daa8eb6042399397f37a24360021d79be3e64 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 13 Oct 2023 16:34:55 +0200 Subject: [PATCH 24/46] adressing issue rte-france#538 --- CHANGELOG.rst | 2 ++ grid2op/Episode/EpisodeData.py | 3 +- grid2op/Observation/baseObservation.py | 1 + grid2op/tests/test_issue_538.py | 39 ++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 grid2op/tests/test_issue_538.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b920ee524..c419cbfb4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -46,6 +46,8 @@ Change Log - [FIXED] a bug in PandaPowerBackend in AC powerflow when a generator was alone a bus it made the powerflow crash on some cases (*eg* without lightsim2grid, without numba) - [FIXED] a bug in PandaPowerBackend in DC (in some cases non connected grid were not spotted) +- [FIXED] now the observations once reloaded have the correct `_is_done` flag (`obs._is_done = False`) + which allows to use the `obs.get_energy_graph()` for example. This fixes https://github.com/rte-france/Grid2Op/issues/538 - [ADDED] now depends on the `typing_extensions` package - [ADDED] a complete test suite to help people develop new backend using "Test Driven Programming" techniques diff --git a/grid2op/Episode/EpisodeData.py b/grid2op/Episode/EpisodeData.py index 0fe0c2bfd..934121ba2 100644 --- a/grid2op/Episode/EpisodeData.py +++ b/grid2op/Episode/EpisodeData.py @@ -908,7 +908,8 @@ def __init__( except IncorrectNumberOfElements as exc_: # grid2op does not allow to load the object: there is a mismatch between what has been stored # and what is currently used. - raise + raise Grid2OpException("grid2op does not allow to load the object: there is a mismatch " + "between what has been stored and what is currently used.") from exc_ except NonFiniteElement: self._game_over = i break diff --git a/grid2op/Observation/baseObservation.py b/grid2op/Observation/baseObservation.py index 872290c33..9d0bcc5be 100644 --- a/grid2op/Observation/baseObservation.py +++ b/grid2op/Observation/baseObservation.py @@ -3390,6 +3390,7 @@ def from_vect(self, vect, check_legit=True): self._reset_matrices() # and ensure everything is reloaded properly super().from_vect(vect, check_legit=check_legit) + self._is_done=False def to_dict(self): """ diff --git a/grid2op/tests/test_issue_538.py b/grid2op/tests/test_issue_538.py new file mode 100644 index 000000000..2a53e02e3 --- /dev/null +++ b/grid2op/tests/test_issue_538.py @@ -0,0 +1,39 @@ +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import tempfile +import grid2op +from grid2op.Runner import Runner +from grid2op.Episode import EpisodeData +import warnings +import unittest + + +class Issue538Tester(unittest.TestCase): + def test_is_done(self): + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + env = grid2op.make("rte_case14_realistic", test=True, _add_to_name=type(self).__name__) + obs = env.reset() + with tempfile.TemporaryDirectory() as tmp_path: + runner = Runner(**env.get_params_for_runner()) + li_ep_data_ref = runner.run(nb_episode=1, path_save=tmp_path, add_detailed_output=True, max_iter=10) + + li_ep = EpisodeData.list_episode(path_agent=tmp_path) + ep_data_loaded = EpisodeData.from_disk(*li_ep[0]) + + assert not li_ep_data_ref[0][-1].observations[0]._is_done + assert not ep_data_loaded.observations[0]._is_done + g1 = li_ep_data_ref[0][-1].observations[0].get_energy_graph() + assert len(g1.nodes) == 14 + g2 = ep_data_loaded.observations[0].get_energy_graph() + assert len(g2.nodes) == 14 + + +if __name__ == "__main__": + unittest.main() From ab0ea79fc6c30a6cbfa6409969f38cdca6117591 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 13 Oct 2023 16:42:40 +0200 Subject: [PATCH 25/46] trying to fix the circleci config.yml --- .circleci/config.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index b61ad2db6..694de04d4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -241,6 +241,7 @@ jobs: python -m pip install -U .[test] export _GRID2OP_FORCE_TEST=1 grid2op.testinstall + install310: executor: python310 resource_class: small @@ -299,6 +300,7 @@ jobs: python -m pip install -U .[test] export _GRID2OP_FORCE_TEST=1 grid2op.testinstall + install311: executor: python311 resource_class: small @@ -354,6 +356,11 @@ jobs: apt-get install -y coinor-cbc - run: python -m pip install virtualenv - run: python -m virtualenv venv_test + - run: + command: | + source venv_test/bin/activate + python -m pip install -U pip setuptools wheel + python -m pip install -U numba - run: command: | source venv_test/bin/activate @@ -373,3 +380,4 @@ workflows: - install39 - install310 - install311 + - install312 From 5989c5bcd570ce8e7426940058d2bb570e47ef7e Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 13 Oct 2023 16:43:22 +0200 Subject: [PATCH 26/46] trying to fix the circleci config.yml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 694de04d4..4337a9a38 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ executors: python311: docker: - image: python:3.11-buster - python311: + python312: docker: - image: python:3.12-buster From b30f86cd21410c2644df6ac7f03f02692cccb748 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 09:14:09 +0200 Subject: [PATCH 27/46] trying to fix the circleci config.yml --- .circleci/config.yml | 2 +- .github/workflows/main.yml | 5 +++++ grid2op/Observation/baseObservation.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4337a9a38..1fcbbaef3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,7 +23,7 @@ executors: - image: python:3.11-buster python312: docker: - - image: python:3.12-buster + - image: cimg/python:3.12.0 jobs: test: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9fbd60352..965d540f8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,6 +35,11 @@ jobs: abi: cp311, version: '3.11', } + - { + name: cp312, + abi: cp312, + version: '3.12', + } steps: diff --git a/grid2op/Observation/baseObservation.py b/grid2op/Observation/baseObservation.py index 9d0bcc5be..ca8523a1d 100644 --- a/grid2op/Observation/baseObservation.py +++ b/grid2op/Observation/baseObservation.py @@ -3390,7 +3390,7 @@ def from_vect(self, vect, check_legit=True): self._reset_matrices() # and ensure everything is reloaded properly super().from_vect(vect, check_legit=check_legit) - self._is_done=False + self._is_done = False def to_dict(self): """ From 6428e2e72efc52834b35abe0ec72145228647881 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 09:18:23 +0200 Subject: [PATCH 28/46] trying to fix the circleci config.yml --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1fcbbaef3..053e9ab88 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -352,8 +352,8 @@ jobs: - checkout - run: command: | - apt-get update - apt-get install -y coinor-cbc + apt update + apt install -y coinor-cbc - run: python -m pip install virtualenv - run: python -m virtualenv venv_test - run: From 47a93e09ff4d017373fb0b256e038e3294c23717 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 09:20:36 +0200 Subject: [PATCH 29/46] trying to fix the circleci config.yml --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 053e9ab88..14470bb55 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -352,8 +352,8 @@ jobs: - checkout - run: command: | - apt update - apt install -y coinor-cbc + sudo apt-get update + sudo apt-get install -y coinor-cbc - run: python -m pip install virtualenv - run: python -m virtualenv venv_test - run: From d3ef6777c0b5a31d9126a211649d70eade4a667f Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 09:52:37 +0200 Subject: [PATCH 30/46] trying to fix the circleci config.yml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 14470bb55..1732e70fb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -360,7 +360,7 @@ jobs: command: | source venv_test/bin/activate python -m pip install -U pip setuptools wheel - python -m pip install -U numba + # python -m pip install -U numba # not on python 3.12 at the moment - run: command: | source venv_test/bin/activate From 68ba549561278484919a7a8b44c44c8c3a70cbbc Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 10:10:01 +0200 Subject: [PATCH 31/46] trying to fix the circleci config.yml --- .gitignore | 5 +++++ setup.cfg | 1 + setup.py | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/.gitignore b/.gitignore index b7b8900c8..2a03d9711 100644 --- a/.gitignore +++ b/.gitignore @@ -392,6 +392,11 @@ actspace_converter.py grid2op/data_test/input_data_local/ test_sim2real_battery.py grid2op/tests/list_test_debug +aux_test_make_backend_test_suite.py +grid2op/tests/test_failing_simulator.txt +old_pyproject.toml +pp_bug_gen_alone.py +test_dunder.py # profiling files **.prof diff --git a/setup.cfg b/setup.cfg index 35fa532fd..788347457 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,6 +11,7 @@ classifiers = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) Intended Audience :: Developers Intended Audience :: Education diff --git a/setup.py b/setup.py index 16a171cf8..94438ae20 100644 --- a/setup.py +++ b/setup.py @@ -93,6 +93,10 @@ def my_test_suite(): pkgs["required"][3] = "pandapower>=2.2.2,<2.12" # importlib provided importlib.metadata as of python 3.8 pkgs["required"].append("importlib_metadata") + +if sys.version_info.minor == 12: + # numba is not available for python 3.12 at the moment + pkgs["extras"]["test"] = [el for el in pkgs["extras"]["test"] if el != "numba"] setup(description='An gymnasium compatible environment to model sequential decision making for powersystems', long_description=long_description, From ebfe060cbd5c1d5b107a088384f533c032b4bddd Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 10:20:52 +0200 Subject: [PATCH 32/46] trying to fix the circleci config.yml --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 94438ae20..5e4a176d0 100644 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ def my_test_suite(): if sys.version_info.minor == 12: # numba is not available for python 3.12 at the moment - pkgs["extras"]["test"] = [el for el in pkgs["extras"]["test"] if el != "numba"] + pkgs["extras"]["test"] = [el for el in pkgs["extras"]["test"] if not ("numba" in el)] setup(description='An gymnasium compatible environment to model sequential decision making for powersystems', long_description=long_description, From 78ce1444f4e11ca9a8914cf7dd937465fedb5e06 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 10:36:08 +0200 Subject: [PATCH 33/46] trying to fix the circleci config.yml --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1732e70fb..f54f17919 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -380,4 +380,4 @@ workflows: - install39 - install310 - install311 - - install312 + # - install312 # failing because of dependencies of numba, torch etc. Tired of it so ignoring it ! From 68b50dc412b6f58f472cba6ff7e175733cbbbf84 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 11:07:25 +0200 Subject: [PATCH 34/46] adding 3.12 wheels for macos and windows --- .github/workflows/main.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 965d540f8..a4e979c12 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -107,6 +107,10 @@ jobs: name: cp311, version: '3.11', } + - { + name: cp312, + version: '3.12', + } steps: @@ -139,7 +143,7 @@ jobs: python3 -c "from grid2op.Action._backendAction import _BackendAction" - name: Build source archive - if: matrix.config.name == 'darwin' && matrix.python.name == 'cp39' + if: matrix.config.name == 'darwin' && matrix.python.name == 'cp310' run: python setup.py sdist - name: Upload wheel From e4d4ae336dede2762f17b96950552c98103ba046 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 11:09:57 +0200 Subject: [PATCH 35/46] addressing rte-france#524 --- grid2op/MakeEnv/Make.py | 6 +++--- grid2op/Reward/redispReward.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/grid2op/MakeEnv/Make.py b/grid2op/MakeEnv/Make.py index 17d99d919..8dbb24104 100644 --- a/grid2op/MakeEnv/Make.py +++ b/grid2op/MakeEnv/Make.py @@ -267,7 +267,7 @@ def _aux_make_multimix( def make( - dataset : Union[str, os.PathLike]=None, + dataset : Union[str, os.PathLike], *, test : bool=False, logger: Optional[logging.Logger]=None, @@ -327,12 +327,12 @@ def make( Examples -------- - If you want to create the environment "rte_case14_realistic": + If you want to create the environment "l2rpn_case14_sandbox": .. code-block: python import grid2op - env_name = "rte_case14_realistic" # or any other supported environment + env_name = "l2rpn_case14_sandbox" # or any other supported environment env = grid2op.make(env_name) # env implements the openai gym interface (env.step, env.render, env.reset etc.) diff --git a/grid2op/Reward/redispReward.py b/grid2op/Reward/redispReward.py index fb8d494c5..9869683ce 100644 --- a/grid2op/Reward/redispReward.py +++ b/grid2op/Reward/redispReward.py @@ -33,7 +33,7 @@ class RedispReward(BaseReward): from grid2op.Reward import RedispReward # then you create your environment with it: - NAME_OF_THE_ENVIRONMENT = "rte_case14_realistic" + NAME_OF_THE_ENVIRONMENT = "l2rpn_case14_sandbox" env = grid2op.make(NAME_OF_THE_ENVIRONMENT,reward_class=RedispReward) # and do a step with a "do nothing" action obs = env.reset() From 3215f15ab15a63e11c998a418beb73390711ef59 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 16 Oct 2023 11:53:25 +0200 Subject: [PATCH 36/46] adding setuptools as the dependencies in the CI --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a4e979c12..310f61316 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,6 +55,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install --upgrade wheel + python -m pip install --upgrade setuptools - name: Build wheel run: | @@ -128,6 +129,7 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install --upgrade wheel + python -m pip install --upgrade setuptools - name: Build wheel run: python setup.py bdist_wheel From b5482054d9d285810947e6e92e7888a702286411 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 20 Oct 2023 13:56:34 +0200 Subject: [PATCH 37/46] adding the chronics id as the return info of the env.step --- CHANGELOG.rst | 7 ++ docs/action.rst | 2 +- docs/data_pipeline.rst | 2 +- docs/environment.rst | 12 ++-- docs/makeenv.rst | 18 ++--- docs/parameters.rst | 2 +- docs/plot.rst | 12 ++-- docs/quickstart.rst | 2 +- docs/runner.rst | 6 +- grid2op/Action/actionSpace.py | 2 +- grid2op/Action/serializableActionSpace.py | 16 ++--- grid2op/Agent/agentWithConverter.py | 2 +- grid2op/Agent/oneChangeThenNothing.py | 2 +- grid2op/Backend/backend.py | 6 +- grid2op/Backend/name_attributes.py | 66 +++++++++++++++++ grid2op/Chronics/multiFolder.py | 2 +- grid2op/Converter/Converters.py | 4 +- grid2op/Converter/IdToAct.py | 8 +-- grid2op/Environment/baseEnv.py | 23 +++--- grid2op/Environment/baseMultiProcessEnv.py | 6 +- grid2op/Environment/environment.py | 14 ++-- grid2op/Environment/multiEnvMultiProcess.py | 6 +- grid2op/Environment/singleEnvMultiProcess.py | 4 +- grid2op/Episode/EpisodeData.py | 4 +- grid2op/Episode/EpisodeReplay.py | 2 +- grid2op/Observation/baseObservation.py | 2 +- grid2op/Plot/EpisodeReplay.py | 2 +- grid2op/PlotGrid/BasePlot.py | 2 +- grid2op/PlotGrid/PlotMatplot.py | 2 +- grid2op/PlotGrid/PlotPlotly.py | 2 +- grid2op/Runner/runner.py | 8 +-- grid2op/Space/GridObjects.py | 30 ++++---- grid2op/Space/RandomObject.py | 4 +- grid2op/Space/SerializableSpace.py | 4 +- grid2op/_create_test_suite.py | 74 ++++++++++++++++++-- grid2op/gym_compat/gym_obs_space.py | 2 +- grid2op/multi_agent/my_test.py | 16 +++++ grid2op/tests/helper_path_test.py | 10 ++- grid2op/tests/test_Environment.py | 6 +- grid2op/utils/underlying_statistics.py | 4 +- 40 files changed, 280 insertions(+), 118 deletions(-) create mode 100644 grid2op/Backend/name_attributes.py create mode 100644 grid2op/multi_agent/my_test.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c419cbfb4..11fd0a9b1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -51,10 +51,17 @@ Change Log - [ADDED] now depends on the `typing_extensions` package - [ADDED] a complete test suite to help people develop new backend using "Test Driven Programming" techniques +- [ADDED] the information on which time series data has been used by the environment in the `info`return value + of `env.step(...)` +- [ADDED] a test suite easy to set up to test the backend API (and only the backend for now, integration tests with + runner and environment will follow) - [IMPROVED] now easier than ever to run the grid2op test suite with a new backend (for relevant tests) - [IMPROVED] type hints for `Backend` and `PandapowerBackend` - [IMPROVED] distribute python 3.12 wheel - [IMPROVED] test for python 3.12 and numpy 1.26 when appropriate (*eg* when numpy version is released) +- [IMPROVED] handling of environments without shunts +- [IMPROVED] error message when grid is not consistent +- [IMPROVED] add the default `l2rpn_case14_sandbox` environment in all part of the docs (substituing `rte_case14_realistic` or nothing) [1.9.5] - 2023-09-18 --------------------- diff --git a/docs/action.rst b/docs/action.rst index 2eea62359..a370d4d8b 100644 --- a/docs/action.rst +++ b/docs/action.rst @@ -75,7 +75,7 @@ Constructing an action in grid2op is made in the following manner: .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") dictionary_describing_the_action = {...} # se bellow my_action = env.action_space(dictionary_describing_the_action) print(my_action) diff --git a/docs/data_pipeline.rst b/docs/data_pipeline.rst index 2bf5a11ea..cb86a6723 100644 --- a/docs/data_pipeline.rst +++ b/docs/data_pipeline.rst @@ -72,7 +72,7 @@ footprint (less RAM taken). import re import grid2op from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) env.seed(0) # for reproducible experiments diff --git a/docs/environment.rst b/docs/environment.rst index 49b8f3aee..11cac0a59 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -38,7 +38,7 @@ This example is adapted from gym documentation available at import grid2op from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) env.seed(0) # for reproducible experiments episode_count = 100 # i want to make 100 episodes @@ -123,7 +123,7 @@ the call to "env.reset". This gives the following code: import grid2op from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) env.seed(0) # for reproducible experiments episode_count = 100 # i want to make 100 episodes @@ -174,7 +174,7 @@ that will do exactly that. You can use it like this: import numpy as np import grid2op from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) env.seed(0) # for reproducible experiments episode_count = 10000 # i want to make lots of episode @@ -224,7 +224,7 @@ chronics and should return ``True`` / ``False`` whether or not you want to keep import re import grid2op from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) env.seed(0) # for reproducible experiments @@ -276,7 +276,7 @@ reason oversampling the 10 first chronics, and under sample the last 10: import re import grid2op from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) env.seed(0) # for reproducible experiments @@ -338,7 +338,7 @@ episode: import re import grid2op from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) env.seed(0) # for reproducible experiments diff --git a/docs/makeenv.rst b/docs/makeenv.rst index eef647ca1..8fb895cb4 100644 --- a/docs/makeenv.rst +++ b/docs/makeenv.rst @@ -19,7 +19,7 @@ To get started with such an environment, you can simply do: .. code-block:: python import grid2op - env = grid2op.make("rte_case14_realistic") + env = grid2op.make("l2rpn_case14_sandbox") You can consult the different notebooks in the `getting_stared` directory of this package for more information on @@ -44,9 +44,9 @@ you want to use: .. code-block:: python import grid2op - env = grid2op.make("rte_case14_realistic") + env = grid2op.make("l2rpn_case14_sandbox") -This will create the environment known "rte_case14_realistic" with all default parameters. If this environment is has +This will create the environment known "l2rpn_case14_sandbox" with all default parameters. If this environment is has not been downloaded, at the first call to this function it will download it and store it in a cache on your system ( see the section :ref:`cache_manip` for more information), afterwards it will use the downloaded environment. @@ -56,18 +56,18 @@ the full absolute path of you dataset. On linux / unix (including macos) machine .. code-block:: python import grid2op - env = grid2op.make("/full/path/where/the/env/is/located/rte_case14_realistic") + env = grid2op.make("/full/path/where/the/env/is/located/l2rpn_case14_sandbox") And on windows based machine this will look like: .. code-block:: python import grid2op - env = grid2op.make("C:\\where\\the\\env\\is\\located\\rte_case14_realistic") + env = grid2op.make("C:\\where\\the\\env\\is\\located\\l2rpn_case14_sandbox") -In bot cases it will load the environment named "rte_case14_realistic" (provided that you found a way to get it on your -machine) located at the path "/full/path/where/the/env/is/located/rte_case14_realistic" (or -"C:\\the\\full\\path\\where\\the\\env\\is\\located\\rte_case14_realistic"). +In bot cases it will load the environment named "l2rpn_case14_sandbox" (provided that you found a way to get it on your +machine) located at the path "/full/path/where/the/env/is/located/l2rpn_case14_sandbox" (or +"C:\\the\\full\\path\\where\\the\\env\\is\\located\\l2rpn_case14_sandbox"). Important notes --------------- @@ -81,7 +81,7 @@ It has the following behavior: 2) if you specify the name of an environment that you have already downloaded, it will use this environment (NB currently no checks are implemented if the environment has been updated remotely, which can happen if we realize there were some issues with it.) -3) if you provided no arguments a default environment will be used: ``rte_case14_realistic`` +3) you are expected to provide an environment name (if you don't know what this is just put `"l2rpn_case14_sandbox"`) 4) if the flag `test` is set to ``False`` (default behaviour) and none of the above conditions are met, the :func:`make` will download the data of this environment locally the first time it is called. If you don't want to download anything then you can pass the flag ``test=True`` diff --git a/docs/parameters.rst b/docs/parameters.rst index 713011a94..f89ccc78e 100644 --- a/docs/parameters.rst +++ b/docs/parameters.rst @@ -32,7 +32,7 @@ This can be done with: p.MAX_LINE_STATUS_CHANGED = 10 # Give Parameters instance to make, so its used - env = grid2op.make("rte_case14_realistic", param=p) + env = grid2op.make("l2rpn_case14_sandbox", param=p) .. automodule:: grid2op.Parameters diff --git a/docs/plot.rst b/docs/plot.rst index 47b38ea44..25058cf49 100644 --- a/docs/plot.rst +++ b/docs/plot.rst @@ -42,7 +42,7 @@ the agent pretty easily, and allows easy saving into gif format (see below for m path_agents = "agent_pseudo_random" with warnings.catch_warnings(): warnings.filterwarnings("ignore") - env = grid2op.make("rte_case14_realistic") + env = grid2op.make("l2rpn_case14_sandbox") class CustomRandom(RandomAgent): """ @@ -87,7 +87,7 @@ an action. This can be done with the provided environments following openAI gym from grid2op.Agent import RandomAgent from grid2op.Episode import EpisodeReplay - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) nb_episode = 1 for i in range(nb_episode): @@ -117,7 +117,7 @@ class. Here is how you can do this: path_saved_data = "where_i_want_to_save_it" # create an environment and an agent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) # create a runner @@ -144,7 +144,7 @@ You can use them as follow: import grid2op from grid2op.PlotGrid import PlotMatplot - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") plot_helper = PlotMatplot(env.observation_space) obs = env.reset() @@ -186,7 +186,7 @@ of the each powerline: import grid2op from grid2op.PlotGrid import PlotMatplot - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") plot_helper = PlotMatplot(env.observation_space) # plot the thermal limits of each powerlines @@ -203,7 +203,7 @@ Of course you can also "project" on the grid all kind of variable and also for g import grid2op from grid2op.PlotGrid import PlotMatplot - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") plot_helper = PlotMatplot(env.observation_space) # plot the thermal limits of each powerlines and the voltages magnitude of each load diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 679e61523..3a641da06 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -94,7 +94,7 @@ The most basic code, for those familiar with openAI gym (a well-known framework import grid2op # create an environment - env_name = "rte_case14_realistic" # for example, other environments might be usable + env_name = "l2rpn_case14_sandbox" # for example, other environments might be usable env = grid2op.make(env_name) # create an agent diff --git a/docs/runner.rst b/docs/runner.rst index e6c6bc80b..266c26c2d 100644 --- a/docs/runner.rst +++ b/docs/runner.rst @@ -28,7 +28,7 @@ an agent: import grid2op from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) NB_EPISODE = 10 # assess the performance for 10 episodes, for example for i in range(NB_EPISODE): @@ -48,7 +48,7 @@ the data (and make it easier to integrate it with other applications) we can use import grid2op from grid2op.Runner import Runner from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") NB_EPISODE = 10 # assess the performance for 10 episodes, for example NB_CORE = 2 # do it on 2 cores, for example PATH_SAVE = "agents_log" # and store the results in the "agents_log" folder @@ -65,7 +65,7 @@ you can also use the Runner pretty easily by passing it an instance of your agen import grid2op from grid2op.Runner import Runner - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") NB_EPISODE = 10 # assess the performance for 10 episodes, for example NB_CORE = 2 # do it on 2 cores, for example PATH_SAVE = "agents_log" # and store the results in the "agents_log" folder diff --git a/grid2op/Action/actionSpace.py b/grid2op/Action/actionSpace.py index c7ade19a7..975b5e9d0 100644 --- a/grid2op/Action/actionSpace.py +++ b/grid2op/Action/actionSpace.py @@ -91,7 +91,7 @@ def __call__( import grid2op # create a simple environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") act = env.action_space({}) # act is now the "do nothing" action, that doesn't modify the grid. diff --git a/grid2op/Action/serializableActionSpace.py b/grid2op/Action/serializableActionSpace.py index 4264b839e..d7cee94cf 100644 --- a/grid2op/Action/serializableActionSpace.py +++ b/grid2op/Action/serializableActionSpace.py @@ -287,7 +287,7 @@ def sample(self): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # and now you can sample from the action space random_action = env.action_space.sample() @@ -300,7 +300,7 @@ def sample(self): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # and now you can sample from the action space random_action = env.action_space() @@ -380,7 +380,7 @@ def disconnect_powerline(self, line_id=None, line_name=None, previous_action=Non .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # and now you can disconnect line 0 disco_line_0 = env.action_space.disconnect_powerline(line_id=0) @@ -475,7 +475,7 @@ def reconnect_powerline( .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # and now you can reconnect line 0 reco_line_0 = env.action_space.reconnect_powerline(line_id=0, bus_or=1, bus_ex=0) @@ -582,7 +582,7 @@ def change_bus( .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # change bus of element named 'gen_1_0' change_gen_0 = env.action_space.change_bus('gen_1_0', type_element="gen") @@ -757,7 +757,7 @@ def set_bus( .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # set bus of element named 'gen_1_0' to bus 2 setbus_gen_0 = env.action_space.set_bus('gen_1_0', new_bus=2, type_element="gen") @@ -982,7 +982,7 @@ def get_all_unitary_topologies_change(action_space, sub_id=None): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # all "change bus" action for all the substations all_change_actions = env.action_space.get_all_unitary_topologies_change(env.action_space) @@ -1053,7 +1053,7 @@ def get_all_unitary_topologies_set(action_space, sub_id=None): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # all "set_bus" actions all_change_actions = env.action_space.get_all_unitary_topologies_set(env.action_space) diff --git a/grid2op/Agent/agentWithConverter.py b/grid2op/Agent/agentWithConverter.py index 92fee527a..724984157 100644 --- a/grid2op/Agent/agentWithConverter.py +++ b/grid2op/Agent/agentWithConverter.py @@ -66,7 +66,7 @@ class AgentWithConverter(BaseAgent): import grid2op import AwesomeNN # this does not exists! # create a simple environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # define the class above class AgentCustomObservation(AgentWithConverter): diff --git a/grid2op/Agent/oneChangeThenNothing.py b/grid2op/Agent/oneChangeThenNothing.py index 08e762b5a..49f8e6df8 100644 --- a/grid2op/Agent/oneChangeThenNothing.py +++ b/grid2op/Agent/oneChangeThenNothing.py @@ -34,7 +34,7 @@ class OneChangeThenNothing(BaseAgent): acts_dict_ = [{}, {"set_line_status": [(0,-1)]}] # list of dictionaries. Each dictionary # represents a valid action - env = grid2op.make() # create an environment + env = grid2op.make("l2rpn_case14_sandbox") # create an environment 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) diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index 77b0e4a9d..f6684c0f6 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -1285,7 +1285,7 @@ def check_kirchoff(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray v_storage[i], ) - if self.shunts_data_available: + if type(self).shunts_data_available: p_s, q_s, v_s, bus_s = self.shunt_info() for i in range(self.n_shunt): # for substations @@ -1734,7 +1734,7 @@ def get_action_to_set(self) -> "grid2op.Action.CompleteAction": }, } - if self.shunts_data_available: + if type(self).shunts_data_available: p_s, q_s, sh_v, bus_s = self.shunt_info() dict_["shunt"] = {"shunt_bus": bus_s} if (bus_s >= 1).sum(): @@ -1802,7 +1802,7 @@ def update_from_obs(self, }, } - if self.shunts_data_available and obs.shunts_data_available: + if type(self).shunts_data_available and type(obs).shunts_data_available: if "_shunt_bus" not in type(obs).attr_list_set: raise BackendError( "Impossible to set the backend to the state given by the observation: shunts data " diff --git a/grid2op/Backend/name_attributes.py b/grid2op/Backend/name_attributes.py new file mode 100644 index 000000000..6d8ce15c7 --- /dev/null +++ b/grid2op/Backend/name_attributes.py @@ -0,0 +1,66 @@ +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import numpy as np + +def _line_name(row, id_obj): + return f"{row['from_bus']}_{row['to_bus']}_{id_obj}" + + +def _trafo_name(row, id_obj): + return f"{row['hv_bus']}_{row['lv_bus']}_{id_obj}" + + +def _gen_name(row, id_obj): + return f"gen_{row['bus']}_{id_obj}" + + +def _load_name(row, id_obj): + return f"load_{row['bus']}_{id_obj}" + + +def _storage_name(row, id_obj): + return f"storage_{row['bus']}_{id_obj}" + + +def _sub_name(row, id_obj): + return f"sub_{id_obj}" + + +def _shunt_name(row, id_obj): + return f"shunt_{row['bus']}_{id_obj}" + + +def _aux_get_names(grid, grid_attrs): + res = [] + obj_id = 0 + for (attr, fun_to_name) in grid_attrs: + df = getattr(grid, attr) + if ( + "name" in df.columns + and not df["name"].isnull().values.any() + ): + res += [name for name in df["name"]] + else: + res += [ + fun_to_name(row, id_obj=obj_id + i) + for i, (_, row) in enumerate(df.iterrows()) + ] + obj_id += df.shape[0] + res = np.array(res) + return res + + +def get_pandapower_default_names(pp_grid): + sub_names = ["sub_{}".format(i) for i, row in pp_grid.bus.iterrows()] + load_names = _aux_get_names(pp_grid, [("load", _load_name)]) + shunt_names = _aux_get_names(pp_grid, [("shunt", _shunt_name)]) + gen_names = _aux_get_names(pp_grid, [("gen", _gen_name), ("ext_grid", _gen_name)]) + line_names = _aux_get_names(pp_grid, [("line", _line_name), ("trafo", _trafo_name)]) + storage_names = _aux_get_names(pp_grid, [("storage", _storage_name)]) + return sub_names, load_names, shunt_names, gen_names, line_names, storage_names diff --git a/grid2op/Chronics/multiFolder.py b/grid2op/Chronics/multiFolder.py index 377cd3172..f948f94ac 100644 --- a/grid2op/Chronics/multiFolder.py +++ b/grid2op/Chronics/multiFolder.py @@ -690,7 +690,7 @@ def split_and_save(self, datetime_beg, datetime_end, path_out): import grid2op import os - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") env.chronics_handler.real_data.split_and_save({"004": "2019-01-08 02:00", "005": "2019-01-30 08:00", diff --git a/grid2op/Converter/Converters.py b/grid2op/Converter/Converters.py index f5ccc8a14..0a744695e 100644 --- a/grid2op/Converter/Converters.py +++ b/grid2op/Converter/Converters.py @@ -104,7 +104,7 @@ def convert_action_from_gym(self, gymlike_action): .. code-block:: python # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # create the converter converter = IdToAct(env.action_space) @@ -147,7 +147,7 @@ def convert_action_to_gym(self, action): .. code-block:: python # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # create the converter converter = IdToAct(env.action_space) diff --git a/grid2op/Converter/IdToAct.py b/grid2op/Converter/IdToAct.py index 60fc9484c..895d6f20d 100644 --- a/grid2op/Converter/IdToAct.py +++ b/grid2op/Converter/IdToAct.py @@ -144,7 +144,7 @@ def init_converter(self, all_actions=None, **kwargs): import grid2op from grid2op.Converter import IdToAct - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") converter = IdToAct(env.action_space) # the path were will save it @@ -319,7 +319,7 @@ def save(self, path, name="action_space_vect.npy"): import grid2op from grid2op.Converter import IdToAct - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") converter = IdToAct(env.action_space) # the path were will save it @@ -435,7 +435,7 @@ def convert_action_from_gym(self, gymlike_action): .. code-block:: python # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # create the converter converter = IdToAct(env.action_space) @@ -477,7 +477,7 @@ def convert_action_to_gym(self, action): .. code-block:: python # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # create the converter converter = IdToAct(env.action_space) diff --git a/grid2op/Environment/baseEnv.py b/grid2op/Environment/baseEnv.py index a71542416..40aaf5252 100644 --- a/grid2op/Environment/baseEnv.py +++ b/grid2op/Environment/baseEnv.py @@ -1389,7 +1389,7 @@ def seed(self, seed=None, _seed_me=True): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") env.seed(0) obs = env.reset() @@ -1618,7 +1618,7 @@ def set_thermal_limit(self, thermal_limit): import grid2op # I create an environment - env = grid2op.make("rte_case5_example", test=True) + env = grid2op.make(""l2rpn_case14_sandbox"", test=True) # i set the thermal limit of each powerline to 20000 amps env.set_thermal_limit([20000 for _ in range(env.n_line)]) @@ -2311,7 +2311,7 @@ def get_obs(self, _update_state=True, _do_copy=True): import grid2op # I create an environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") obs = env.reset() @@ -2351,7 +2351,7 @@ def get_thermal_limit(self): import grid2op # I create an environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") thermal_limits = env.get_thermal_limit() @@ -3038,6 +3038,7 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation, float, bool, dict]: `detailed_infos_for_cascading_failures=True`) the list of the intermediate steps computed during the simulation of the "cascading failures". - "rewards": dictionary of all "other_rewards" provided when the env was built. + - "time_series_id": id of the time series used (if any, similar to a call to `env.chronics_handler.get_id()`) Examples --------- @@ -3050,7 +3051,7 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation, float, bool, dict]: from grid2op.Agent import RandomAgent # I create an environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # define an agent here, this is an example agent = RandomAgent(env.action_space) @@ -3229,6 +3230,11 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation, float, bool, dict]: self._time_step += end_step - beg_step if conv_ is not None: except_.append(conv_) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + chron_id = self.chronics_handler.get_id() + if chron_id == "": + chron_id = None self.infos = { "disc_lines": self._disc_lines, "is_illegal": is_illegal, @@ -3241,6 +3247,7 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation, float, bool, dict]: "opponent_attack_sub": subs_attacked, "opponent_attack_duration": attack_duration, "exception": except_, + "time_series_id": chron_id } if self.backend.detailed_infos_for_cascading_failures: @@ -3364,7 +3371,7 @@ def __enter__(self): import grid2op import grid2op.BaseAgent - with grid2op.make() as env: + with grid2op.make("l2rpn_case14_sandbox") as env: agent = grid2op.BaseAgent.DoNothingAgent(env.action_space) act = env.action_space() obs, r, done, info = env.step(act) @@ -3575,7 +3582,7 @@ def attach_layout(self, grid_layout): import grid2op # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # assign coordinates (0., 0.) to all substations (this is a dummy thing to do here!) layout = {sub_name: (0., 0.) for sub_name in env.name_sub} @@ -3649,7 +3656,7 @@ def fast_forward_chronics(self, nb_timestep): import grid2op # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # skip the first 150 steps of the chronics env.fast_forward_chronics(150) diff --git a/grid2op/Environment/baseMultiProcessEnv.py b/grid2op/Environment/baseMultiProcessEnv.py index aa32fea2b..37d571702 100644 --- a/grid2op/Environment/baseMultiProcessEnv.py +++ b/grid2op/Environment/baseMultiProcessEnv.py @@ -396,8 +396,8 @@ def step(self, actions): import grid2op from grid2op.Environment import BaseMultiProcessEnv - env1 = grid2op.make() # create an environment of your choosing - env2 = grid2op.make() # create another environment of your choosing + env1 = grid2op.make("l2rpn_case14_sandbox") # create an environment of your choosing + env2 = grid2op.make("l2rpn_case14_sandbox") # create another environment of your choosing multi_env = BaseMultiProcessEnv([env1, env2]) obss = multi_env.reset() @@ -793,7 +793,7 @@ def __del__(self): nb_env = 8 # change that to adapt to your system NB_STEP = 100 # number of step for each environment - env = make() + env = make("l2rpn_case14_sandbox") env.seed(42) envs = [env for _ in range(nb_env)] diff --git a/grid2op/Environment/environment.py b/grid2op/Environment/environment.py index f8c72684b..f7047204a 100644 --- a/grid2op/Environment/environment.py +++ b/grid2op/Environment/environment.py @@ -623,7 +623,7 @@ def set_chunk_size(self, new_chunk_size): import grid2op # I create an environment - env = grid2op.make("rte_case5_example", test=True) + env = grid2op.make("l2rpn_case14_sandbox", test=True) env.set_chunk_size(100) env.reset() # otherwise chunk size has no effect ! # and now data will be read from the hard drive 100 time steps per 100 time steps @@ -766,7 +766,7 @@ def attach_renderer(self, graph_layout=None): import grid2op # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") if False: # if you want to change the default layout of the powergrid @@ -881,7 +881,7 @@ def reset(self) -> BaseObservation: import grid2op # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # and now you can "render" (plot) the state of the grid obs = env.reset() @@ -943,7 +943,7 @@ def render(self, mode="rgb_array"): import grid2op # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # if you want to use the renderer env.attach_renderer() @@ -1011,7 +1011,7 @@ def copy(self) -> "Environment": .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") cpy_of_env = env.copy() """ @@ -1050,7 +1050,7 @@ def get_kwargs(self, with_backend=True, with_chronics_handler=True): import grid2op from grid2op.Environment import Environment - env = grid2op.make() # create the environment of your choice + env = grid2op.make("l2rpn_case14_sandbox") # create the environment of your choice copy_of_env = Environment(**env.get_kwargs()) # And you can use this one as you would any other environment. # NB this is not a "proper" copy. for example it will not be at the same step, it will be possible @@ -1652,7 +1652,7 @@ def get_params_for_runner(self): import grid2op from grid2op.Runner import Runner from grid2op.Agent import DoNothingAgent # for example - env = grid2op.make() # create the environment of your choice + env = grid2op.make("l2rpn_case14_sandbox") # create the environment of your choice # create the proper runner runner = Runner(**env.get_params_for_runner(), agentClass=DoNothingAgent) diff --git a/grid2op/Environment/multiEnvMultiProcess.py b/grid2op/Environment/multiEnvMultiProcess.py index 647113740..53c2cec18 100644 --- a/grid2op/Environment/multiEnvMultiProcess.py +++ b/grid2op/Environment/multiEnvMultiProcess.py @@ -42,8 +42,8 @@ class MultiEnvMultiProcess(BaseMultiProcessEnvironment): import grid2op from grid2op.Environment import MultiEnvMultiProcess - env0 = grid2op.make() # create an environment - env1 = grid2op.make() # create a second environment, that can be similar, or not + env0 = grid2op.make("l2rpn_case14_sandbox") # create an environment + env1 = grid2op.make("l2rpn_case14_sandbox") # create a second environment, that can be similar, or not # it is recommended to filter or create the environment with different parameters, otherwise this class # is of little interest envs = [env0, env1] # list of all environments created @@ -103,7 +103,7 @@ def __init__(self, envs, nb_envs, obs_as_class=True, return_info=True, logger=No nb_env = [2, 2, 1, 1, 2] # change that to adapt to your system NB_STEP = 100 # number of step for each environment - env = make() + env = make("l2rpn_case14_sandbox") env.seed(42) envs = [env, env, env, env, env] diff --git a/grid2op/Environment/singleEnvMultiProcess.py b/grid2op/Environment/singleEnvMultiProcess.py index f648b7e59..dfd82747b 100644 --- a/grid2op/Environment/singleEnvMultiProcess.py +++ b/grid2op/Environment/singleEnvMultiProcess.py @@ -42,7 +42,7 @@ class SingleEnvMultiProcess(BaseMultiProcessEnvironment): from grid2op.Environment import SingleEnvMultiProcess # create a simple environment - env = make() + env = make("l2rpn_case14_sandbox") # number of parrallel environment nb_env = 2 # change that to adapt to your system NB_STEP = 100 # number of step for each environment @@ -94,7 +94,7 @@ def __init__(self, env, nb_env, obs_as_class=True, return_info=True, logger=None nb_env = 8 # change that to adapt to your system NB_STEP = 100 # number of step for each environment - env = make() + env = make("l2rpn_case14_sandbox") env.seed(42) agent = DoNothingAgent(env.action_space) diff --git a/grid2op/Episode/EpisodeData.py b/grid2op/Episode/EpisodeData.py index 934121ba2..e06ac7325 100644 --- a/grid2op/Episode/EpisodeData.py +++ b/grid2op/Episode/EpisodeData.py @@ -98,7 +98,7 @@ class EpisodeData: from grid2op.Runner import Runner # I create an environment - env = grid2op.make("rte_case5_example", test=True) + env = grid2op.make("l2rpn_case14_sandbox", test=True) # I create the runner runner = Runner(**env.get_params_for_runner()) @@ -356,7 +356,7 @@ def list_episode(path_agent): ################ # INTRO # create a runner - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # see the documentation of the Runner if you want to change the agent. # in this case it will be "do nothing" runner = Runner(**env.get_params_for_runner()) diff --git a/grid2op/Episode/EpisodeReplay.py b/grid2op/Episode/EpisodeReplay.py index 9f2f9e510..6213bf450 100644 --- a/grid2op/Episode/EpisodeReplay.py +++ b/grid2op/Episode/EpisodeReplay.py @@ -31,7 +31,7 @@ class EpisodeReplay(object): import grid2op agent_class = grid2op.Agent.DoNothingAgent # change that for studying other agent - env = grid2op.make() # make the default environment + env = grid2op.make("l2rpn_case14_sandbox") # make the default environment runner = grid2op.Runner.Runner(**env.get_params_for_runner(), agentClass=agent_class) path_log = "agent_log" # where the runner will output the standardized data when running the agent. res = runner.run(nb_episode=1, path_save=path_log) diff --git a/grid2op/Observation/baseObservation.py b/grid2op/Observation/baseObservation.py index ca8523a1d..ca913485f 100644 --- a/grid2op/Observation/baseObservation.py +++ b/grid2op/Observation/baseObservation.py @@ -1619,7 +1619,7 @@ def connectivity_matrix(self, as_csr_matrix=False): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") obs = env.reset() # retrieve the id of extremity of powerline 1: diff --git a/grid2op/Plot/EpisodeReplay.py b/grid2op/Plot/EpisodeReplay.py index d72aa1882..fca8e5f82 100644 --- a/grid2op/Plot/EpisodeReplay.py +++ b/grid2op/Plot/EpisodeReplay.py @@ -52,7 +52,7 @@ class EpisodeReplay(object): import grid2op agent_class = grid2op.Agent.DoNothingAgent # change that for studying other agent - env = grid2op.make() # make the default environment + env = grid2op.make("l2rpn_case14_sandbox") # make the default environment runner = grid2op.Runner.Runner(**env.get_params_for_runner(), agentClass=agent_class) path_log = "agent_log" # where the runner will output the standardized data when running the agent. res = runner.run(nb_episode=1, path_save=path_log) diff --git a/grid2op/PlotGrid/BasePlot.py b/grid2op/PlotGrid/BasePlot.py index 365425b17..041cd6d45 100644 --- a/grid2op/PlotGrid/BasePlot.py +++ b/grid2op/PlotGrid/BasePlot.py @@ -925,7 +925,7 @@ def plot_info( import grid2op from grid2op.PlotGrid import PlotMatplot - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") plot_helper = PlotMatplot(env.observation_space) diff --git a/grid2op/PlotGrid/PlotMatplot.py b/grid2op/PlotGrid/PlotMatplot.py index 4585bf783..9befd1cc4 100644 --- a/grid2op/PlotGrid/PlotMatplot.py +++ b/grid2op/PlotGrid/PlotMatplot.py @@ -139,7 +139,7 @@ class PlotMatplot(BasePlot): import grid2op from grid2op.PlotGrid import PlotMatplot - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") plot_helper = PlotMatplot(env.observation_space) # and now plot an observation (for example) diff --git a/grid2op/PlotGrid/PlotPlotly.py b/grid2op/PlotGrid/PlotPlotly.py index db6c0bec3..52653b0b9 100644 --- a/grid2op/PlotGrid/PlotPlotly.py +++ b/grid2op/PlotGrid/PlotPlotly.py @@ -33,7 +33,7 @@ class PlotPlotly(BasePlot): import grid2op from grid2op.PlotGrid import PlotPlotly - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") plot_helper = PlotPlotly(env.observation_space) # and now plot an observation (for example) diff --git a/grid2op/Runner/runner.py b/grid2op/Runner/runner.py index 870f7dc51..c790b0883 100644 --- a/grid2op/Runner/runner.py +++ b/grid2op/Runner/runner.py @@ -68,7 +68,7 @@ class Runner(object): from grid2op.Agent import RandomAgent # for example... from grid2op.Runner import Runner - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") ############### # the gym loops @@ -1153,7 +1153,7 @@ def run( from gri2op.Runner import Runner from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") runner = Runner(**env.get_params_for_runner(), agentClass=RandomAgent) res = runner.run(nb_episode=1) @@ -1165,7 +1165,7 @@ def run( from gri2op.Runner import Runner from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") my_agent = RandomAgent(env.action_space) runner = Runner(**env.get_params_for_runner(), agentClass=None, agentInstance=my_agent) res = runner.run(nb_episode=1) @@ -1181,7 +1181,7 @@ def run( from gri2op.Runner import Runner from grid2op.Agent import RandomAgent - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") my_agent = RandomAgent(env.action_space) runner = Runner(**env.get_params_for_runner(), agentClass=None, agentInstance=my_agent) res = runner.run(nb_episode=1, agent_seeds=[42], env_seeds=[0]) diff --git a/grid2op/Space/GridObjects.py b/grid2op/Space/GridObjects.py index a47b2a9aa..26cffb323 100644 --- a/grid2op/Space/GridObjects.py +++ b/grid2op/Space/GridObjects.py @@ -871,7 +871,7 @@ def to_vect(self): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # for an observation: obs = env.reset() @@ -1008,7 +1008,7 @@ def shape(self): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # for an observation: obs_space_shapes = env.observation_space.shape() @@ -1052,7 +1052,7 @@ def dtype(self): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # for an observation: obs_space_types = env.observation_space.dtype() @@ -1126,7 +1126,7 @@ def from_vect(self, vect, check_legit=True): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # get the vector representation of an observation: obs = env.reset() @@ -1221,7 +1221,7 @@ def size(self): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # get the vector representation of an observation: obs = env.reset() @@ -1712,7 +1712,7 @@ def _compute_sub_pos(cls): if cls.gen_to_sub_pos is None: if need_implement is False: raise BackendError( - 'You chose to implement "load_to_sub_pos" but not "gen_to_sub_pos". We cannot ' + 'You chose not to implement "gen_to_sub_pos" but not "load_to_sub_pos". We cannot ' "work with that. Please either use the automatic setting, or implement all of " "*_to_sub_pos vectors" "" @@ -1721,7 +1721,7 @@ def _compute_sub_pos(cls): if cls.line_or_to_sub_pos is None: if need_implement is False: raise BackendError( - 'You chose to implement "line_or_to_sub_pos" but not "load_to_sub_pos"' + 'You chose not to implement "line_or_to_sub_pos" but "load_to_sub_pos"' 'or "gen_to_sub_pos". We cannot ' "work with that. Please either use the automatic setting, or implement all of " "*_to_sub_pos vectors" @@ -1731,7 +1731,7 @@ def _compute_sub_pos(cls): if cls.line_ex_to_sub_pos is None: if need_implement is False: raise BackendError( - 'You chose to implement "line_ex_to_sub_pos" but not "load_to_sub_pos"' + 'You chose not to implement "line_ex_to_sub_pos" but "load_to_sub_pos"' 'or "gen_to_sub_pos" or "line_or_to_sub_pos". We cannot ' "work with that. Please either use the automatic setting, or implement all of " "*_to_sub_pos vectors" @@ -1741,7 +1741,7 @@ def _compute_sub_pos(cls): if cls.storage_to_sub_pos is None: if need_implement is False: raise BackendError( - 'You chose to implement "storage_to_sub_pos" but not "load_to_sub_pos"' + 'You chose not to implement "storage_to_sub_pos" but "load_to_sub_pos"' 'or "gen_to_sub_pos" or "line_or_to_sub_pos" or "line_ex_to_sub_pos". ' "We cannot " "work with that. Please either use the automatic setting, or implement all of " @@ -2796,7 +2796,7 @@ def get_obj_connect_to(cls, _sentinel=None, substation_id=None): .. code-block:: python import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # get the vector representation of an observation: sub_id = 1 @@ -2878,7 +2878,7 @@ def get_obj_substations(cls, _sentinel=None, substation_id=None): import numpy as np import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # get the vector representation of an observation: sub_id = 1 @@ -2984,7 +2984,7 @@ def get_lines_id(self, _sentinel=None, from_=None, to_=None): import numpy as np import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") l_ids = env.get_lines_id(from_=0, to_=1) print("The powerlines connecting substation 0 to substation 1 have for ids: {}".format(l_ids)) @@ -3043,7 +3043,7 @@ def get_generators_id(self, sub_id): import numpy as np import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") g_ids = env.get_generators_id(sub_id=1) print("The generators connected to substation 1 have for ids: {}".format(g_ids)) @@ -3094,7 +3094,7 @@ def get_loads_id(self, sub_id): import numpy as np import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") c_ids = env.get_loads_id(sub_id=1) print("The loads connected to substation 1 have for ids: {}".format(c_ids)) @@ -3146,7 +3146,7 @@ def get_storages_id(self, sub_id): import numpy as np import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") sto_ids = env.get_storages_id(sub_id=1) print("The loads connected to substation 1 have for ids: {}".format(c_ids)) diff --git a/grid2op/Space/RandomObject.py b/grid2op/Space/RandomObject.py index 404a5fdcb..0288ba469 100644 --- a/grid2op/Space/RandomObject.py +++ b/grid2op/Space/RandomObject.py @@ -59,7 +59,7 @@ class RandomObject(object): import grid2op from grid2op.Agent import RandomAgent # or any other agent of course. It might also be a custom you developed # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") agent = RandomAgent(env.action_space) # and now set the seed @@ -84,7 +84,7 @@ class RandomObject(object): np.random.seed(42) # or any other seed of course :-) # create the environment - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # NB setting a seed in this environment will have absolutely no effect on the runner # and now set the seed diff --git a/grid2op/Space/SerializableSpace.py b/grid2op/Space/SerializableSpace.py index 8742928ed..6beaa5e7f 100644 --- a/grid2op/Space/SerializableSpace.py +++ b/grid2op/Space/SerializableSpace.py @@ -360,7 +360,7 @@ def extract_from_vect(self, obj_as_vect, attr_name): import numpy as np import grid2op - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # get the vector representation of an observation: obs = env.reset() @@ -415,7 +415,7 @@ def get_indx_extract(self, attr_name): ################ # INTRO # create a runner - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") # see the documentation of the Runner if you want to change the agent. # in this case it will be "do nothing" runner = Runner(**env.get_params_for_runner()) diff --git a/grid2op/_create_test_suite.py b/grid2op/_create_test_suite.py index aba883e78..3b8fba0dd 100644 --- a/grid2op/_create_test_suite.py +++ b/grid2op/_create_test_suite.py @@ -28,10 +28,23 @@ # Issue131Tester -def _make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes): +def _make_and_add_cls(el, + add_name_cls, + this_make_backend, + add_to_module, + all_classes, + get_paths, + get_casefiles): + this_methods = {"make_backend": this_make_backend} + if get_paths is not None: + if el.__name__ in get_paths: + this_methods["get_path"] = get_paths[el.__name__] + if get_casefiles is not None: + if el.__name__ in get_casefiles: + this_methods["get_casefile"] = get_casefiles[el.__name__] this_cls = type(f"{re.sub('Base', '', el.__name__)}_{add_name_cls}", (el, unittest.TestCase), - {"make_backend": this_make_backend}) + this_methods) if add_to_module is not None: # make the created class visible to the default module setattr(sys.modules[add_to_module], @@ -47,6 +60,8 @@ def create_test_suite(make_backend_fun, add_to_module=None, extended_test=True, tests_skipped=(), + get_paths=None, + get_casefiles=None, **kwargs): """This function helps you create a test suite to test the behaviour of your agent. @@ -94,15 +109,55 @@ def this_make_backend(self, detailed_infos_for_cascading_failures=False): if __name__ == "__main__": unittest.main() - - And then run this script normally `python my_script.py` and observe the results - + .. warning:: Do not forget to include the `self` as the first argument of the `this_make_backend` (or whatever you decide to name it) even if you don't use it ! Otherwise this script will NOT work ! + And then run this script normally `python my_script.py` and observe the results + + + You can also import it this way (a bit more verbose but might give you more control on which tests is launched) + + .. code-block:: python + + import unittest + from grid2op.tests.aaa_test_backend_interface import AAATestBackendAPI + + class TestBackendAPI_PyPoBk(AAATestBackendAPI, unittest.TestCase): + + def make_backend(self, detailed_infos_for_cascading_failures=False): + # replace this with the backend you want to test. It should return a backend ! + return LightSimBackend(loader_method="pypowsybl", + loader_kwargs=_aux_get_loader_kwargs(), + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + + It allows also to modify the grid format (for example) that your backend can read from: + + + .. code-block:: python + + import unittest + from grid2op.tests.aaa_test_backend_interface import AAATestBackendAPI + + class TestBackendAPI_PyPoBk(AAATestBackendAPI, unittest.TestCase): + def get_path(self): + # if you want to change the path from which data will be read + return path_case_14_storage_iidm + + def get_casefile(self): + # if you want to change the grid file that will be read by the backend. + return "grid.xiidm" + + def make_backend(self, detailed_infos_for_cascading_failures=False): + # replace this with the backend you want to test. It should return a backend ! + return LightSimBackend(loader_method="pypowsybl", + loader_kwargs=_aux_get_loader_kwargs(), + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + + Parameters ---------- make_backend_fun : _type_ @@ -123,8 +178,13 @@ def this_make_backend(self, detailed_infos_for_cascading_failures=False): *args, detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, **kwargs) + if get_paths is None: + get_paths = {} + if get_casefiles is None: + get_casefiles = {} + all_classes = [] - _make_and_add_cls(AAATestBackendAPI, add_name_cls, this_make_backend, add_to_module, all_classes) + _make_and_add_cls(AAATestBackendAPI, add_name_cls, this_make_backend, add_to_module, all_classes, get_paths, get_casefiles) if not extended_test: return @@ -151,6 +211,6 @@ def this_make_backend(self, detailed_infos_for_cascading_failures=False): BaseTestRedispTooLowHigh, BaseTestDispatchRampingIllegalETC, BaseTestLoadingAcceptAlmostZeroSumRedisp]: - _make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes) + _make_and_add_cls(el, add_name_cls, this_make_backend, add_to_module, all_classes, get_paths, get_casefiles) return all_classes diff --git a/grid2op/gym_compat/gym_obs_space.py b/grid2op/gym_compat/gym_obs_space.py index 1cd72a772..d427f4230 100644 --- a/grid2op/gym_compat/gym_obs_space.py +++ b/grid2op/gym_compat/gym_obs_space.py @@ -62,7 +62,7 @@ class __AuxGymObservationSpace: import grid2op from grid2op.Converter import GymObservationSpace - env = grid2op.make() + 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 diff --git a/grid2op/multi_agent/my_test.py b/grid2op/multi_agent/my_test.py new file mode 100644 index 000000000..3c48e88ec --- /dev/null +++ b/grid2op/multi_agent/my_test.py @@ -0,0 +1,16 @@ +import grid2op +from grid2op.multi_agent.multiAgentEnv import MultiAgentEnv + +env = grid2op.make("l2rpn_case14_sandbox", test = True) +action_domains = { + 'agent_0' : [0,1,2,3, 4], + 'agent_1' : [5,6,7,8,9,10,11,12,13] + } +observation_domains = { + 'agent_0' : action_domains['agent_1'], + 'agent_1' : action_domains['agent_0'] + } + +# run redispatch agent on one scenario for 100 timesteps +ma_env = MultiAgentEnv(env, observation_domains, action_domains) + diff --git a/grid2op/tests/helper_path_test.py b/grid2op/tests/helper_path_test.py index f1f37c041..f3f1c0667 100644 --- a/grid2op/tests/helper_path_test.py +++ b/grid2op/tests/helper_path_test.py @@ -42,8 +42,14 @@ class HelperTests: def setUp(self): self.tolvect = dt_float(1e-2) self.tol_one = dt_float(1e-5) - super().setUp() - + if hasattr(type(super()), "setUp"): + # needed for backward compatibility + super().setUp() + + def tearDown(self): + # needed for backward compatibility + pass + def compare_vect(self, pred, true): res = dt_float(np.max(np.abs(pred - true))) <= self.tolvect res = res and dt_float(np.mean(np.abs(pred - true))) <= self.tolvect diff --git a/grid2op/tests/test_Environment.py b/grid2op/tests/test_Environment.py index 28dcec27d..639de2e7a 100644 --- a/grid2op/tests/test_Environment.py +++ b/grid2op/tests/test_Environment.py @@ -10,8 +10,8 @@ import pdb import time import warnings - import unittest + from grid2op.tests.helper_path_test import * import grid2op @@ -36,6 +36,7 @@ def get_backend(self, detailed_infos_for_cascading_failures=True): return PandaPowerBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) def setUp(self): + super().setUp() # powergrid self.backend = self.get_backend(True) self.path_matpower = PATH_DATA_TEST_PP @@ -109,11 +110,10 @@ def setUp(self): names_chronics_to_backend=self.names_chronics_to_backend, name="test_env_env1", ) - super().setUp() def tearDown(self): - self.env.close() super().tearDown() + self.env.close() def compare_vect(self, pred, true): return dt_float(np.max(np.abs(pred - true))) <= self.tolvect diff --git a/grid2op/utils/underlying_statistics.py b/grid2op/utils/underlying_statistics.py index a083facec..40b2faa84 100644 --- a/grid2op/utils/underlying_statistics.py +++ b/grid2op/utils/underlying_statistics.py @@ -45,7 +45,7 @@ class EpisodeStatistics(object): import grid2op from grid2op.utils import EpisodeStatistics - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") stats = EpisodeStatistics(env) @@ -67,7 +67,7 @@ class EpisodeStatistics(object): import grid2op from grid2op.utils import EpisodeStatistics from grid2op.Parameters import Parameters - env = grid2op.make() + env = grid2op.make("l2rpn_case14_sandbox") nb_scenario = 8 From 7650585c14a455e91ac3ddec27e96cef748b9f8c Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 23 Oct 2023 17:36:25 +0200 Subject: [PATCH 38/46] adding a backend attribute used to know which file format will be loaded --- CHANGELOG.rst | 4 +- grid2op/Backend/backend.py | 3 +- grid2op/MakeEnv/MakeFromPath.py | 41 +++--- grid2op/_create_test_suite.py | 18 ++- grid2op/tests/aaa_test_backend_interface.py | 131 ++++++++++++++------ 5 files changed, 141 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 11fd0a9b1..d999428a8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -55,12 +55,14 @@ Change Log of `env.step(...)` - [ADDED] a test suite easy to set up to test the backend API (and only the backend for now, integration tests with runner and environment will follow) +- [ADDED] an attribute of the backend to specify which file extension can be processed by it. Environment creation will + fail if none are found. See `backend.supported_grid_format` - [IMPROVED] now easier than ever to run the grid2op test suite with a new backend (for relevant tests) - [IMPROVED] type hints for `Backend` and `PandapowerBackend` - [IMPROVED] distribute python 3.12 wheel - [IMPROVED] test for python 3.12 and numpy 1.26 when appropriate (*eg* when numpy version is released) - [IMPROVED] handling of environments without shunts -- [IMPROVED] error message when grid is not consistent +- [IMPROVED] error messages when grid is not consistent - [IMPROVED] add the default `l2rpn_case14_sandbox` environment in all part of the docs (substituing `rte_case14_realistic` or nothing) [1.9.5] - 2023-09-18 diff --git a/grid2op/Backend/backend.py b/grid2op/Backend/backend.py index f6684c0f6..ba29e5e28 100644 --- a/grid2op/Backend/backend.py +++ b/grid2op/Backend/backend.py @@ -148,7 +148,8 @@ def __init__(self, self.detailed_infos_for_cascading_failures :bool= ( detailed_infos_for_cascading_failures ) - + self.supported_grid_format = ("json", ) # new in 1.9.6 + # the power _grid manipulated. One powergrid per backend. self._grid : Any = None diff --git a/grid2op/MakeEnv/MakeFromPath.py b/grid2op/MakeEnv/MakeFromPath.py index 7cebcf2fb..98054513f 100644 --- a/grid2op/MakeEnv/MakeFromPath.py +++ b/grid2op/MakeEnv/MakeFromPath.py @@ -91,7 +91,7 @@ } NAME_CHRONICS_FOLDER = "chronics" -NAME_GRID_FILE = "grid.json" +NAME_GRID_FILE = "grid" NAME_GRID_LAYOUT_FILE = "grid_layout.json" NAME_CONFIG_FILE = "config.py" @@ -293,20 +293,6 @@ def make_from_dataset_path( except Exception as exc_: exc_chronics = exc_ - # Compute and find backend/grid file - grid_path = _get_default_aux( - "grid_path", - kwargs, - defaultClassApp=str, - defaultinstance="", - msg_error=ERR_MSG_KWARGS["grid_path"], - ) - if grid_path == "": - grid_path_abs = os.path.abspath(os.path.join(dataset_path_abs, NAME_GRID_FILE)) - else: - grid_path_abs = os.path.abspath(grid_path) - _check_path(grid_path_abs, "Dataset power flow solver configuration") - # Compute and find grid layout file grid_layout_path_abs = os.path.abspath( os.path.join(dataset_path_abs, NAME_GRID_LAYOUT_FILE) @@ -386,6 +372,31 @@ def make_from_dataset_path( msg_error=ERR_MSG_KWARGS["backend"], ) + # Compute and find backend/grid file + grid_path = _get_default_aux( + "grid_path", + kwargs, + defaultClassApp=str, + defaultinstance="", + msg_error=ERR_MSG_KWARGS["grid_path"], + ) + if grid_path == "": + grid_path_abs = None + for ext in backend.supported_grid_format: + grid_path_abs = os.path.abspath(os.path.join(dataset_path_abs, f"{NAME_GRID_FILE}.{ext}")) + try: + _check_path(grid_path_abs, "Dataset power flow solver configuration") + break + except EnvError as exc_: + pass + if grid_path_abs is None: + raise EnvError(f"Impossible to find a grid file format supported by your backend. Your backend said it supports " + f"the file with extension {backend.supported_grid_format}, " + f"none of which are found in '{dataset_path_abs}'") + else: + grid_path_abs = os.path.abspath(grid_path) + _check_path(grid_path_abs, "Dataset power flow solver configuration") + # Get default observation class observation_class_cfg = CompleteObservation if ( diff --git a/grid2op/_create_test_suite.py b/grid2op/_create_test_suite.py index 3b8fba0dd..07a11988d 100644 --- a/grid2op/_create_test_suite.py +++ b/grid2op/_create_test_suite.py @@ -135,7 +135,6 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) It allows also to modify the grid format (for example) that your backend can read from: - .. code-block:: python @@ -156,8 +155,25 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return LightSimBackend(loader_method="pypowsybl", loader_kwargs=_aux_get_loader_kwargs(), detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + + Yet another use, if you want more customization: + + .. code-block:: python + + def get_path_test_api(self): + return path + def get_casefile(self): + return "grid.xiidm" + res = create_test_suite(make_backend_fun=this_make_backend, + add_name_cls=add_name_cls, + add_to_module=__name__, + extended_test=False, # for now keep `extended_test=False` until all problems are solved + get_paths={"AAATestBackendAPI": get_path_test_api}, + get_casefiles={"AAATestBackendAPI": get_casefile} + ) + Parameters ---------- make_backend_fun : _type_ diff --git a/grid2op/tests/aaa_test_backend_interface.py b/grid2op/tests/aaa_test_backend_interface.py index 5ec2c77f6..da0baebee 100644 --- a/grid2op/tests/aaa_test_backend_interface.py +++ b/grid2op/tests/aaa_test_backend_interface.py @@ -19,6 +19,13 @@ class AAATestBackendAPI(HelperTests): # # from lightsim2grid import LightSimBackend # # return LightSimBackend() # TODO REMOVE + + # if same ordering as pandapower + # init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + # init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + # init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + # init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + def get_path(self): return os.path.join(PATH_DATA, "educ_case14_storage") @@ -33,7 +40,7 @@ def aux_make_backend(self): """do not run nor modify ! (used for this test class only)""" backend = self.make_backend() backend.load_grid(self.get_path(), self.get_casefile()) - backend.load_redispacthing_data(self.get_path()) + backend.load_redispacthing_data("tmp") # pretend there is no generator backend.load_storage_data(self.get_path()) env_name = self.aux_get_env_name() backend.env_name = env_name @@ -93,21 +100,19 @@ def test_02modify_load(self): NB: it does not check whether or not the modification is consistent with the input. This will be done in a later test""" backend = self.aux_make_backend() - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + random_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) + random_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) # try to modify load_p action = type(backend)._complete_action_class() - action.update({"injection": {"load_p": 1.01 * init_load_p}}) + action.update({"injection": {"load_p": 1.01 * random_load_p}}) bk_act = type(backend).my_bk_act_class() bk_act += action backend.apply_action(bk_act) # modification of load_p only # try to modify load_q action = type(backend)._complete_action_class() - action.update({"injection": {"load_q": 1.01 * init_load_q}}) + action.update({"injection": {"load_q": 1.01 * random_load_q}}) bk_act = type(backend).my_bk_act_class() bk_act += action backend.apply_action(bk_act) # modification of load_q only @@ -123,21 +128,19 @@ def test_03modify_gen(self): NB: it does not check whether or not the modification is consistent with the input. This will be done in a later test""" backend = self.aux_make_backend() - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + random_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + random_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) # try to modify gen_p action = type(backend)._complete_action_class() - action.update({"injection": {"prod_p": 1.01 * init_gen_p}}) + action.update({"injection": {"prod_p": 1.01 * random_gen_p}}) bk_act = type(backend).my_bk_act_class() bk_act += action backend.apply_action(bk_act) # modification of prod_p / gen_p only # try to modify prod_v only action = type(backend)._complete_action_class() - action.update({"injection": {"prod_v": 1.01 * init_gen_v}}) + action.update({"injection": {"prod_v": random_gen_v + 0.1}}) bk_act = type(backend).my_bk_act_class() bk_act += action backend.apply_action(bk_act) # modification of prod_v / gen_v only @@ -314,16 +317,17 @@ def test_10_ac_forced_divergence(self): - backend.load_grid(...) is implemented - backend.runpf() (AC mode) is implemented - backend.apply_action() for generator and load is implemented + - backend.generators_info() is implemented + - backend.loads_info() is implemented + """ backend = self.aux_make_backend() res = backend.runpf(is_dc=False) assert len(res) == 2, "runpf should return tuple of size 2" - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + init_gen_p, *_ = backend.generators_info() + init_load_p, *_ = backend.loads_info() gen_p = 1. * init_gen_p load_p = 1. * init_load_p @@ -359,35 +363,55 @@ def test_11_modify_load_pf_getter(self): - backend.runpf() (AC mode) is implemented - backend.apply_action() for generator and load is implemented - backend.loads_info() is implemented + - backend.generators_info() is implemented """ backend = self.aux_make_backend() - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) res = backend.runpf(is_dc=False) tmp = backend.loads_info() assert len(tmp) == 3, "loads_info() should return 3 elements: load_p, load_q, load_v (see doc)" load_p_init, load_q_init, load_v_init = tmp + init_gen_p, *_ = backend.generators_info() # try to modify load_p action = type(backend)._complete_action_class() - action.update({"injection": {"load_p": 1.01 * init_load_p, + action.update({"injection": {"load_p": 1.01 * load_p_init, "prod_p": 1.01 * init_gen_p, - "load_q": 1.01 * init_load_q}}) + "load_q": 1.01 * load_q_init}}) bk_act = type(backend).my_bk_act_class() bk_act += action backend.apply_action(bk_act) # modification of load_p, load_q and gen_p res2 = backend.runpf(is_dc=False) + assert res2[0], "backend should not have diverge after such a little perturbation" tmp2 = backend.loads_info() assert len(tmp) == 3, "loads_info() should return 3 elements: load_p, load_q, load_v (see doc)" load_p_after, load_q_after, load_v_after = tmp2 assert not np.allclose(load_p_after, load_p_init), f"load_p does not seemed to be modified by apply_action when loads are impacted (active value): check `apply_action` for load_p" assert not np.allclose(load_q_after, load_q_init), f"load_q does not seemed to be modified by apply_action when loads are impacted (reactive value): check `apply_action` for load_q" - + + # now a basic check for "one load at a time" + delta_mw = 1. + delta_mvar = 1. + for load_id in range(backend.n_load): + this_load_p = 1. * load_p_init + this_load_p[load_id] += delta_mw # add 1 MW + this_load_q = 1. * load_q_init + this_load_q[load_id] += delta_mvar # add 1 MVAr + action = type(backend)._complete_action_class() + action.update({"injection": {"load_p": this_load_p, + "prod_p": init_gen_p, + "load_q": this_load_q}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) # modification of load_p, load_q and gen_p + res_tmp = backend.runpf(is_dc=False) + assert res_tmp[0], "backend should not have diverge after such a little perturbation" + tmp = backend.loads_info() + assert np.abs(tmp[0][load_id] - load_p_init[load_id]) >= delta_mw / 2., f"error when trying to modify load {load_id}: check the consistency between backend.loads_info() and backend.apply_action for load_p" + assert np.abs(tmp[1][load_id] - load_q_init[load_id]) >= delta_mvar / 2., f"error when trying to modify load {load_id}: check the consistency between backend.loads_info() and backend.apply_action for load_q" + def test_12_modify_gen_pf_getter(self): """Tests that the modification of generators has an impact on the backend (by reading back the states) @@ -397,33 +421,59 @@ def test_12_modify_gen_pf_getter(self): - backend.runpf() (AC mode) is implemented - backend.apply_action() for generator and load is implemented - backend.generators_info() is implemented - """ - backend = self.aux_make_backend() - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + - backend.loads_info() is implemented + """ + backend = self.aux_make_backend() res = backend.runpf(is_dc=False) tmp = backend.generators_info() assert len(tmp) == 3, "generators_info() should return 3 elements: gen_p, gen_q, gen_v (see doc)" gen_p_init, gen_q_init, gen_v_init = tmp + load_p_init, *_ = backend.loads_info() # try to modify load_p action = type(backend)._complete_action_class() - action.update({"injection": {"load_p": 1.01 * init_load_p, - "prod_p": 1.01 * init_gen_p, - "prod_v": init_gen_v + 0.1}}) + action.update({"injection": {"load_p": 1.01 * load_p_init, + "prod_p": 1.01 * gen_p_init, + "prod_v": gen_v_init + 0.1}}) bk_act = type(backend).my_bk_act_class() bk_act += action backend.apply_action(bk_act) # modification of load_p, load_q and gen_p res2 = backend.runpf(is_dc=False) + assert res2[0], "backend should not have diverge after such a little perturbation" tmp2 = backend.generators_info() assert len(tmp) == 3, "generators_info() should return 3 elements: gen_p, gen_q, gen_v (see doc)" gen_p_after, gen_q_after, gen_v_after = tmp2 assert not np.allclose(gen_p_after, gen_p_init), f"gen_p does not seemed to be modified by apply_action when generators are impacted (active value): check `apply_action` for gen_p / prod_p" - assert not np.allclose(gen_v_after, gen_v_init), f"load_p does not seemed to be modified by apply_action when loads are impacted (reactive value): check `apply_action` for gen_v / prod_v" + assert not np.allclose(gen_v_after, gen_v_init), f"gen_v does not seemed to be modified by apply_action when generators are impacted (voltage setpoint value): check `apply_action` for gen_v / prod_v" + + # now a basic check for "one gen at a time" + # NB this test cannot be done like this for "prod_v" / gen_v because two generators might be connected to the same + # bus, and changing only one would cause an issue ! + delta_mw = 1. + nb_error = 0 + prev_exc = None + for gen_id in range(backend.n_gen): + this_gen_p = 1. * gen_p_init + this_gen_p[gen_id] += delta_mw # remove 1 MW + action = type(backend)._complete_action_class() + action.update({"injection": {"load_p": load_p_init, + "prod_p": this_gen_p}}) + bk_act = type(backend).my_bk_act_class() + bk_act += action + backend.apply_action(bk_act) + res_tmp = backend.runpf(is_dc=False) + assert res_tmp[0], "backend should not have diverge after such a little perturbation" + tmp = backend.generators_info() + if np.abs(tmp[0][gen_id] - gen_p_init[gen_id]) <= delta_mw / 2.: + # in case of non distributed slack, backend cannot control the generator acting as the slack. + # this is why this test is expected to fail at most once. + # if it fails twice, then there is a bug. + if prev_exc is None: + prev_exc = AssertionError(f"error when trying to modify active generator of gen {gen_id}: check the consistency between backend.generators_info() and backend.apply_action for gen_p / prod_p") + else: + raise AssertionError(f"error when trying to modify active generator of gen {gen_id}: check the consistency between backend.generators_info() and backend.apply_action for gen_p / prod_p") from prev_exc def test_13_disco_reco_lines_pf_getter(self): """Tests the powerlines can be disconnected and connected and that getter info are consistent @@ -589,6 +639,8 @@ def test_15_reset(self): - backend.load_grid(...) is implemented - backend.runpf() (AC mode) is implemented - backend.apply_action() for generator and load is implemented + - backend.loads_info() is implemented + - backend.generators_info() is implemented - backend.lines_or_info() is implemented """ @@ -599,8 +651,8 @@ def test_15_reset(self): assert len(res) == 2, "runpf should return tuple of size 2" # create a situation where the backend diverges - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_p, *_ = backend.generators_info() + init_load_p, *_ = backend.loads_info() gen_p = 1. * init_gen_p load_p = 1. * init_load_p nb_iter = 0 @@ -1028,6 +1080,8 @@ def test_26_copy(self): - backend.load_grid(...) is implemented - backend.runpf() (AC and DC mode) is implemented - backend.apply_action() for prod / gen modification + - backend.loads_info() are implemented + - backend.generators_info() are implemented - backend.lines_or_info() are implemented - backend.reset() is implemented @@ -1042,9 +1096,10 @@ def test_26_copy(self): # backend can be copied backend_cpy = backend.copy() assert isinstance(backend_cpy, type(backend)), f"backend.copy() is supposed to return an object of the same type as your backend. Check backend.copy()" + backend.runpf(is_dc=False) # now modify original one - init_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) + init_gen_p, *_ = backend.generators_info() + init_load_p, *_ = backend.loads_info() action = type(backend)._complete_action_class() action.update({"injection": {"prod_p": 1.1 * init_gen_p, From 9ef3cb7ed876ba3c5aa25bee2edac544e7672c5e Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 24 Oct 2023 09:02:49 +0200 Subject: [PATCH 39/46] update changelog to point to issue rte-france#429 [skip ci] --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d999428a8..28b8f13a5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -56,7 +56,7 @@ Change Log - [ADDED] a test suite easy to set up to test the backend API (and only the backend for now, integration tests with runner and environment will follow) - [ADDED] an attribute of the backend to specify which file extension can be processed by it. Environment creation will - fail if none are found. See `backend.supported_grid_format` + fail if none are found. See `backend.supported_grid_format` see https://github.com/rte-france/Grid2Op/issues/429 - [IMPROVED] now easier than ever to run the grid2op test suite with a new backend (for relevant tests) - [IMPROVED] type hints for `Backend` and `PandapowerBackend` - [IMPROVED] distribute python 3.12 wheel From 6417bbd1341eae5e2c7307deb98e2f5ba9984f0e Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 24 Oct 2023 10:12:47 +0200 Subject: [PATCH 40/46] improving docs for backend test suite --- docs/createbackend.rst | 189 ++++++++++++++++++++++--- grid2op/Chronics/fromOneEpisodeData.py | 6 +- 2 files changed, 172 insertions(+), 23 deletions(-) diff --git a/docs/createbackend.rst b/docs/createbackend.rst index 2f9c48238..54dcf1c24 100644 --- a/docs/createbackend.rst +++ b/docs/createbackend.rst @@ -216,6 +216,7 @@ Basically the `load_grid` function would look something like: # the initial thermal limit self.thermal_limit_a = ... + The grid2op attributes that need to be implemented in the :func:`grid2op.Backend.Backend.load_grid` function are given in the table bellow: @@ -350,24 +351,6 @@ Nothing much to say here, you count each object and assign the right val to the For example, `n_line` is 8 because there are 8 lines on the grid, labeled from 0 to 7. -.. _sub-i: - -(optional) Substation information (sub_info, dim_topo) -******************************************************* -.. versionadded:: 1.3.2 - This is now done automatically if the user do not set it. - -For these attributes too, there is nothing really surprising (we remember that these can be set automatically if -you don't do it). We show how to set them mainly to explain grid2op "encoding" for these attributes that -you might want to reuse somewhere else. - -For each component of `sub_info` you tell grid2op of the number of elements connected to it. And then you sum -up each of these elements in the `dim_topo` attributes. - -|5subs_grid_5_sub_i| - -.. note:: Only the loads, line ends ("origin" or "extremity") and generators are counted as "elements". - .. _subid: Substation id (\*_to_subid) @@ -387,6 +370,24 @@ For the other attributes, you follow the same pattern: |5subs_grid_el_to_subid| +.. _sub-i: + +(optional) Substation information (sub_info, dim_topo) +******************************************************* +.. versionadded:: 1.3.2 + This is now done automatically if the user do not set it. + +For these attributes too, there is nothing really surprising (we remember that these can be set automatically if +you don't do it). We show how to set them mainly to explain grid2op "encoding" for these attributes that +you might want to reuse somewhere else. + +For each component of `sub_info` you tell grid2op of the number of elements connected to it. And then you sum +up each of these elements in the `dim_topo` attributes. + +|5subs_grid_5_sub_i| + +.. note:: Only the loads, line ends ("origin" or "extremity") and generators are counted as "elements". + .. _subpo: (optional) Position in substation (\*_to_sub_pos) @@ -476,14 +477,17 @@ Finally, you proceed in the same manner for all substation and you get: .. _subtv: -Position in the topology vector (\*_pos_topo_vect) -************************************************** +(optional) Position in the topology vector (\*_pos_topo_vect) +**************************************************************** This information is redundant with the other vector. It can be initialized with a call to the function :func:`grid2op.Space.GridObjects._compute_pos_big_topo` that you will need to perform after having initialized all the other vectors as explained above (simply call `self._compute_pos_big_topo()` at the end of your implementation of `load_grid` function) +.. note:: + Shunts are not "part of" the topology vector. + .. _backend-action-create-backend: BackendAction: modification @@ -795,6 +799,151 @@ In this case, at substation 2 there are 2 connected busbars: But there is no direct connection between busbar 1 and busbar 2. +Automatic testing and "Test Driven Programming" +-------------------------------------------------- + +.. versionadded:: 1.9.6 + Before this is was not specified in the code but only on some description what was expected from a backend. + +In grid2op was added some convenience class to "make sure" that a backend candidate was working as expected, following the +grid2op "expectation" in terms of grid representation and all. This test suite has been made independant of the solver used, you can +use it directly if your backend can read pandapower "json" format but can also be used (thanks to some customization) even +if it's not the case. + +The only requirement is that you install grid2op FROM SOURCE and in "editable" mode. You can do that (using a python +virtual environment is a good idea and is not covered here): + +.. code-block:: + + git clone https://github.com/rte-france/grid2op.git grid2op_dev + cd grid2op_dev + pip install -e . + +.. warning:: + If it fails with some error like `AttributeError: module 'grid2op' has no attribute '__version__'` + or equivalent, you can remove the "pyproject.toml" file: + + .. code-block:: + + cd grid2op_dev # same repo as above + rm -rf pyproject.toml + pip install -e . + + +Most simple way +++++++++++++++++ + +Write a script (say "test_basic_api.py") with this in your code: + +.. code-block:: python + + from grid2op._create_test_suite import create_test_suite + + def this_make_backend(self, detailed_infos_for_cascading_failures=False): + # the function that will create your backend + # do not put "PandaPowerBackend" of course, but the class you coded as a backend ! + return PandaPowerBackend( + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures + ) + add_name_cls = "test_PandaPowerBackend" # h + + res = create_test_suite(make_backend_fun=this_make_backend, + add_name_cls=add_name_cls, + add_to_module=__name__, + extended_test=False, # for now keep `extended_test=False` until all problems are solved + ) + + if __name__ == "__main__": + unittest.main() + +Then you can run your test with: + +.. code-block:: + + python -m unittest test_basic_api.py + +If all tests pass then you are done. + + +More verbose, but easier to debug +++++++++++++++++++++++++++++++++++++++++++++++++ + +You can also do the equivalent script: + +.. code-block:: python + + import unittest + from grid2op.tests.aaa_test_backend_interface import AAATestBackendAPI + + class TestBackendAPI_PandaPowerBackend(AAATestBackendAPI, unittest.TestCase): + def make_backend(self, detailed_infos_for_cascading_failures=False): + # the function that will create your backend + # do not put "PandaPowerBackend" of course, but the class you coded as a backend ! + return PandaPowerBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + + + if __name__ == "__main__": + unittest.main() + + +Advantages: + +1) you can tell to python which test you want to run exactly, for example with + `python -m unittest test_basic_api.TestBackendAPI_PandaPowerBackend.test_01load_grid` which + makes it easier to debug (you can run only the failed test) +2) it's more clear what is being done and the name of everything +3) it's easier to customized + +Drawbacks: + +1) Only tests in `AAATestBackendAPI` will be performed. Some other tests (integration test, backend more in depth tests) + will not be performed + + +More customization ++++++++++++++++++++ + +If you don't have a backend that reads pandapower file format (or if you want to test part of your backend +when initialized from another data format) it is also easy to customize it. + +You need to: + +1) make a repo somewhere on you machine (say `my_path_for_test` which would be located at `/home/user/Documents/my_path_for_test` or + `C:\\users\\Documents\\my_path_for_test` or anywhere else) +2) put there a grid with your specific format, for example `grid.json` or `grid.xiidm` or `grid.xml`. Two things are important here + + i) the file name should be `grid` and nothing else. `Grid.json` or `my_grid.json` or `case14.xml` will NOT work + ii) the extension should be set in the `self.supported_grid_format`, for example if you want your backend to + be able to read `grid.xml` then the object you create in `def make_backend(...)` should have `xml` somewhere + in its `supported_grid_format` attribute +3) Write a python script similar to this one: + +.. code-block:: python + + import unittest + from grid2op.tests.aaa_test_backend_interface import AAATestBackendAPI + FILE_FORMAT = "xiidm" # for this example, put whatever here ! + + class TestBackendAPI_PandaPowerBackend(AAATestBackendAPI, unittest.TestCase): + def get_path(self): + return "my_path_for_test" # or /home/user/Documents/my_path_for_test + + def get_casefile(self): + return "grid.xiidm" # or `grid.xml` or any other format + + def make_backend(self, detailed_infos_for_cascading_failures=False): + # the function that will create your backend + # do not put "PandaPowerBackend" of course, but the class you coded as a backend ! + backend = PandaPowerBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + assert FILE_FORMAT in backend.supported_grid_format, f"your backend does not recognize the '{FILE_FORMAT}' extension, grid2op will not work" + return backend + + + if __name__ == "__main__": + unittest.main() + +TODO there are ways to use the `create_test_suite` but they have not been tested at the moment. + Advanced usage and speed optimization -------------------------------------- TODO this will be explained "soon". diff --git a/grid2op/Chronics/fromOneEpisodeData.py b/grid2op/Chronics/fromOneEpisodeData.py index a21324337..556736a09 100644 --- a/grid2op/Chronics/fromOneEpisodeData.py +++ b/grid2op/Chronics/fromOneEpisodeData.py @@ -31,7 +31,7 @@ class FromOneEpisodeData(GridValue): It can be used if you want to loop indefinitely through one episode. - .. newinversion:: 1.9.4 + .. versionadded:: 1.9.4 TODO there will be "perfect" forecast, as original forecasts are not stored ! @@ -311,8 +311,8 @@ def _aux_forecasts(self, h_id, dict_, key, def forecasts(self): """Retrieve PERFECT forecast from this time series generator. - .. alert:: - These are perfect forecast and not the original forecasts. + .. danger:: + These are **perfect forecast** and **NOT** the original forecasts. Notes ----- From a26fe3dd965138c3d9813fd6d34aca41c8544d07 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 24 Oct 2023 12:04:55 +0200 Subject: [PATCH 41/46] adding a test for the backend feature of different format --- docs/createbackend.rst | 7 + .../data_test/5bus_fake_grid_format/config.py | 19 + .../5bus_fake_grid_format/grid.json_custom | 1772 +++++++++++++++++ .../5bus_fake_grid_format/grid_layout.json | 30 + .../data_test/5bus_fake_grid_format/readme.md | 1 + grid2op/tests/BaseBackendTest.py | 25 +- grid2op/tests/BaseRedispTest.py | 2 +- grid2op/tests/aaa_test_backend_interface.py | 69 +- grid2op/tests/helper_path_test.py | 27 +- grid2op/tests/test_env_diff_format.py | 54 + 10 files changed, 1963 insertions(+), 43 deletions(-) create mode 100644 grid2op/data_test/5bus_fake_grid_format/config.py create mode 100644 grid2op/data_test/5bus_fake_grid_format/grid.json_custom create mode 100644 grid2op/data_test/5bus_fake_grid_format/grid_layout.json create mode 100644 grid2op/data_test/5bus_fake_grid_format/readme.md create mode 100644 grid2op/tests/test_env_diff_format.py diff --git a/docs/createbackend.rst b/docs/createbackend.rst index 54dcf1c24..c4746f6d9 100644 --- a/docs/createbackend.rst +++ b/docs/createbackend.rst @@ -916,6 +916,13 @@ You need to: ii) the extension should be set in the `self.supported_grid_format`, for example if you want your backend to be able to read `grid.xml` then the object you create in `def make_backend(...)` should have `xml` somewhere in its `supported_grid_format` attribute + iii) your grid should count at least 17 lines + iv) if your grid is not based on the `educ_case14_storage` (modification of the ieee case 14 with addition of 2 storage units and 2 generators), + expect failure in the tests: + + i) the `test_01load_grid` + ii) `test_22_islanded_grid_make_divergence` + 3) Write a python script similar to this one: .. code-block:: python diff --git a/grid2op/data_test/5bus_fake_grid_format/config.py b/grid2op/data_test/5bus_fake_grid_format/config.py new file mode 100644 index 000000000..1ec901a06 --- /dev/null +++ b/grid2op/data_test/5bus_fake_grid_format/config.py @@ -0,0 +1,19 @@ +from grid2op.Action import TopologyAction +from grid2op.Reward import L2RPNReward +from grid2op.Rules import DefaultRules +from grid2op.Chronics import Multifolder +from grid2op.Chronics import GridStateFromFileWithForecasts +from grid2op.Backend import PandaPowerBackend + +config = { + "backend": PandaPowerBackend, + "action_class": TopologyAction, + "observation_class": None, + "reward_class": L2RPNReward, + "gamerules_class": DefaultRules, + "chronics_class": Multifolder, + "grid_value_class": GridStateFromFileWithForecasts, + "volagecontroler_class": None, + "thermal_limits": None, + "names_chronics_to_grid": None, +} diff --git a/grid2op/data_test/5bus_fake_grid_format/grid.json_custom b/grid2op/data_test/5bus_fake_grid_format/grid.json_custom new file mode 100644 index 000000000..b94667b9c --- /dev/null +++ b/grid2op/data_test/5bus_fake_grid_format/grid.json_custom @@ -0,0 +1,1772 @@ +{ + "_module": "pandapower.auxiliary", + "_class": "pandapowerNet", + "_object": { + "bus": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"vn_kv\",\"type\",\"zone\",\"in_service\"],\"index\":[0,1,2,3,4],\"data\":[[\"substation_1\",100.0,\"b\",null,true],[\"substation_2\",100.0,\"b\",null,true],[\"substation_3\",100.0,\"b\",null,true],[\"substation_4\",100.0,\"b\",null,true],[\"substation_5\",100.0,\"b\",null,true]]}", + "orient": "split", + "dtype": { + "name": "object", + "vn_kv": "float64", + "type": "object", + "zone": "object", + "in_service": "bool" + } + }, + "load": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"q_mvar\",\"const_z_percent\",\"const_i_percent\",\"sn_mva\",\"scaling\",\"in_service\",\"type\"],\"index\":[0,1,2],\"data\":[[\"load_0_0\",0,10.0,7.0,0.0,0.0,null,1.0,true,null],[\"load_3_1\",3,10.0,7.0,0.0,0.0,null,1.0,true,null],[\"load_4_2\",4,10.0,7.0,0.0,0.0,null,1.0,true,null]]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "p_mw": "float64", + "q_mvar": "float64", + "const_z_percent": "float64", + "const_i_percent": "float64", + "sn_mva": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object" + } + }, + "sgen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"q_mvar\",\"sn_mva\",\"scaling\",\"in_service\",\"type\",\"current_source\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "int64", + "p_mw": "float64", + "q_mvar": "float64", + "sn_mva": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object", + "current_source": "bool" + } + }, + "motor": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"pn_mech_mw\",\"loading_percent\",\"cos_phi\",\"cos_phi_n\",\"efficiency_percent\",\"efficiency_n_percent\",\"lrc_pu\",\"vn_kv\",\"scaling\",\"in_service\",\"rx\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "int64", + "pn_mech_mw": "float64", + "loading_percent": "float64", + "cos_phi": "float64", + "cos_phi_n": "float64", + "efficiency_percent": "float64", + "efficiency_n_percent": "float64", + "lrc_pu": "float64", + "vn_kv": "float64", + "scaling": "float64", + "in_service": "bool", + "rx": "float64" + } + }, + "asymmetric_load": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\",\"sn_mva\",\"scaling\",\"in_service\",\"type\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64", + "sn_mva": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object" + } + }, + "asymmetric_sgen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\",\"sn_mva\",\"scaling\",\"in_service\",\"type\",\"current_source\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "int64", + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64", + "sn_mva": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object", + "current_source": "bool" + } + }, + "storage": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"p_mw\",\"q_mvar\",\"sn_mva\",\"soc_percent\",\"min_e_mwh\",\"max_e_mwh\",\"scaling\",\"in_service\",\"type\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "int64", + "p_mw": "float64", + "q_mvar": "float64", + "sn_mva": "float64", + "soc_percent": "float64", + "min_e_mwh": "float64", + "max_e_mwh": "float64", + "scaling": "float64", + "in_service": "bool", + "type": "object" + } + }, + "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\",\"slack_weight\"],\"index\":[0,1],\"data\":[[\"gen_0_0\",0,10.0,1.02,null,null,null,1.0,false,true,null,0.0],[\"gen_1_1\",1,20.0,1.02,null,null,null,1.0,true,true,null,1.0]]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "p_mw": "float64", + "vm_pu": "float64", + "sn_mva": "float64", + "min_q_mvar": "float64", + "max_q_mvar": "float64", + "scaling": "float64", + "slack": "bool", + "in_service": "bool", + "type": "object", + "slack_weight": "float64" + } + }, + "switch": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"bus\",\"element\",\"et\",\"type\",\"closed\",\"name\",\"z_ohm\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "bus": "int64", + "element": "int64", + "et": "object", + "type": "object", + "closed": "bool", + "name": "object", + "z_ohm": "float64" + } + }, + "shunt": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"bus\",\"name\",\"q_mvar\",\"p_mw\",\"vn_kv\",\"step\",\"max_step\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "bus": "uint32", + "name": "object", + "q_mvar": "float64", + "p_mw": "float64", + "vn_kv": "float64", + "step": "uint32", + "max_step": "uint32", + "in_service": "bool" + } + }, + "ext_grid": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"vm_pu\",\"va_degree\",\"in_service\",\"slack_weight\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "vm_pu": "float64", + "va_degree": "float64", + "in_service": "bool", + "slack_weight": "float64" + } + }, + "line": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"std_type\",\"from_bus\",\"to_bus\",\"length_km\",\"r_ohm_per_km\",\"x_ohm_per_km\",\"c_nf_per_km\",\"g_us_per_km\",\"max_i_ka\",\"df\",\"parallel\",\"type\",\"in_service\"],\"index\":[0,1,2,3,4,5,6,7],\"data\":[[null,\"NAYY 4x50 SE\",0,1,4.0,0.642,0.083,210.0,0.0,0.6,1.0,1,\"cs\",true],[\"0_2_2\",\"NAYY 4x50 SE\",0,2,4.47,0.642,0.083,210.0,0.0,0.22,1.0,1,\"cs\",true],[\"0_3_3\",\"NAYY 4x50 SE\",0,3,5.65,0.642,0.083,210.0,0.0,0.16,1.0,1,\"cs\",true],[\"0_4_4\",\"NAYY 4x50 SE\",0,4,4.0,0.642,0.083,210.0,0.0,0.16,1.0,1,\"cs\",true],[\"1_2_5\",\"NAYY 4x50 SE\",1,2,2.0,0.642,0.083,210.0,0.0,0.6,1.0,1,\"cs\",true],[\"2_3_6\",\"NAYY 4x50 SE\",2,3,2.0,0.642,0.083,210.0,0.0,0.3,1.0,1,\"cs\",true],[\"2_3_7\",\"NAYY 4x50 SE\",2,3,2.0,0.642,0.083,210.0,0.0,0.3,1.0,1,\"cs\",true],[\"3_4_8\",\"NAYY 4x50 SE\",3,4,4.0,0.642,0.083,210.0,0.0,0.16,1.0,1,\"cs\",true]]}", + "orient": "split", + "dtype": { + "name": "object", + "std_type": "object", + "from_bus": "uint32", + "to_bus": "uint32", + "length_km": "float64", + "r_ohm_per_km": "float64", + "x_ohm_per_km": "float64", + "c_nf_per_km": "float64", + "g_us_per_km": "float64", + "max_i_ka": "float64", + "df": "float64", + "parallel": "uint32", + "type": "object", + "in_service": "bool" + } + }, + "trafo": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"std_type\",\"hv_bus\",\"lv_bus\",\"sn_mva\",\"vn_hv_kv\",\"vn_lv_kv\",\"vk_percent\",\"vkr_percent\",\"pfe_kw\",\"i0_percent\",\"shift_degree\",\"tap_side\",\"tap_neutral\",\"tap_min\",\"tap_max\",\"tap_step_percent\",\"tap_step_degree\",\"tap_pos\",\"tap_phase_shifter\",\"parallel\",\"df\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "std_type": "object", + "hv_bus": "uint32", + "lv_bus": "uint32", + "sn_mva": "float64", + "vn_hv_kv": "float64", + "vn_lv_kv": "float64", + "vk_percent": "float64", + "vkr_percent": "float64", + "pfe_kw": "float64", + "i0_percent": "float64", + "shift_degree": "float64", + "tap_side": "object", + "tap_neutral": "int32", + "tap_min": "int32", + "tap_max": "int32", + "tap_step_percent": "float64", + "tap_step_degree": "float64", + "tap_pos": "int32", + "tap_phase_shifter": "bool", + "parallel": "uint32", + "df": "float64", + "in_service": "bool" + } + }, + "trafo3w": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"std_type\",\"hv_bus\",\"mv_bus\",\"lv_bus\",\"sn_hv_mva\",\"sn_mv_mva\",\"sn_lv_mva\",\"vn_hv_kv\",\"vn_mv_kv\",\"vn_lv_kv\",\"vk_hv_percent\",\"vk_mv_percent\",\"vk_lv_percent\",\"vkr_hv_percent\",\"vkr_mv_percent\",\"vkr_lv_percent\",\"pfe_kw\",\"i0_percent\",\"shift_mv_degree\",\"shift_lv_degree\",\"tap_side\",\"tap_neutral\",\"tap_min\",\"tap_max\",\"tap_step_percent\",\"tap_step_degree\",\"tap_pos\",\"tap_at_star_point\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "std_type": "object", + "hv_bus": "uint32", + "mv_bus": "uint32", + "lv_bus": "uint32", + "sn_hv_mva": "float64", + "sn_mv_mva": "float64", + "sn_lv_mva": "float64", + "vn_hv_kv": "float64", + "vn_mv_kv": "float64", + "vn_lv_kv": "float64", + "vk_hv_percent": "float64", + "vk_mv_percent": "float64", + "vk_lv_percent": "float64", + "vkr_hv_percent": "float64", + "vkr_mv_percent": "float64", + "vkr_lv_percent": "float64", + "pfe_kw": "float64", + "i0_percent": "float64", + "shift_mv_degree": "float64", + "shift_lv_degree": "float64", + "tap_side": "object", + "tap_neutral": "int32", + "tap_min": "int32", + "tap_max": "int32", + "tap_step_percent": "float64", + "tap_step_degree": "float64", + "tap_pos": "int32", + "tap_at_star_point": "bool", + "in_service": "bool" + } + }, + "impedance": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"from_bus\",\"to_bus\",\"rft_pu\",\"xft_pu\",\"rtf_pu\",\"xtf_pu\",\"sn_mva\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "from_bus": "uint32", + "to_bus": "uint32", + "rft_pu": "float64", + "xft_pu": "float64", + "rtf_pu": "float64", + "xtf_pu": "float64", + "sn_mva": "float64", + "in_service": "bool" + } + }, + "dcline": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"from_bus\",\"to_bus\",\"p_mw\",\"loss_percent\",\"loss_mw\",\"vm_from_pu\",\"vm_to_pu\",\"max_p_mw\",\"min_q_from_mvar\",\"min_q_to_mvar\",\"max_q_from_mvar\",\"max_q_to_mvar\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "from_bus": "uint32", + "to_bus": "uint32", + "p_mw": "float64", + "loss_percent": "float64", + "loss_mw": "float64", + "vm_from_pu": "float64", + "vm_to_pu": "float64", + "max_p_mw": "float64", + "min_q_from_mvar": "float64", + "min_q_to_mvar": "float64", + "max_q_from_mvar": "float64", + "max_q_to_mvar": "float64", + "in_service": "bool" + } + }, + "ward": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"ps_mw\",\"qs_mvar\",\"qz_mvar\",\"pz_mw\",\"in_service\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "ps_mw": "float64", + "qs_mvar": "float64", + "qz_mvar": "float64", + "pz_mw": "float64", + "in_service": "bool" + } + }, + "xward": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"bus\",\"ps_mw\",\"qs_mvar\",\"qz_mvar\",\"pz_mw\",\"r_ohm\",\"x_ohm\",\"vm_pu\",\"in_service\",\"slack_weight\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "bus": "uint32", + "ps_mw": "float64", + "qs_mvar": "float64", + "qz_mvar": "float64", + "pz_mw": "float64", + "r_ohm": "float64", + "x_ohm": "float64", + "vm_pu": "float64", + "in_service": "bool", + "slack_weight": "float64" + } + }, + "measurement": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"name\",\"measurement_type\",\"element_type\",\"element\",\"value\",\"std_dev\",\"side\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "name": "object", + "measurement_type": "object", + "element_type": "object", + "element": "uint32", + "value": "float64", + "std_dev": "float64", + "side": "object" + } + }, + "pwl_cost": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"power_type\",\"element\",\"et\",\"points\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "power_type": "object", + "element": "uint32", + "et": "object", + "points": "object" + } + }, + "poly_cost": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"element\",\"et\",\"cp0_eur\",\"cp1_eur_per_mw\",\"cp2_eur_per_mw2\",\"cq0_eur\",\"cq1_eur_per_mvar\",\"cq2_eur_per_mvar2\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "element": "uint32", + "et": "object", + "cp0_eur": "float64", + "cp1_eur_per_mw": "float64", + "cp2_eur_per_mw2": "float64", + "cq0_eur": "float64", + "cq1_eur_per_mvar": "float64", + "cq2_eur_per_mvar2": "float64" + } + }, + "characteristic": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"object\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "object": "object" + } + }, + "controller": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"object\",\"in_service\",\"order\",\"level\",\"initial_run\",\"recycle\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "object": "object", + "in_service": "bool", + "order": "float64", + "level": "object", + "initial_run": "bool", + "recycle": "object" + } + }, + "line_geodata": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"coords\"],\"index\":[0,1,2,3,4,5,6,7],\"data\":[[[[0,0],[0,4]]],[[[0,0],[2,4]]],[[[0,0],[4,4]]],[[[0,0],[4,0]]],[[[0,4],[2,4]]],[[[2,4],[3,4.2],[4,4]]],[[[2,4],[3,3.8],[4,4]]],[[[4,4],[4,0]]]]}", + "orient": "split", + "dtype": { + "coords": "object" + } + }, + "bus_geodata": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"x\",\"y\",\"coords\"],\"index\":[0,1,2,3,4],\"data\":[[0.0,0.0,null],[0.0,4.0,null],[2.0,4.0,null],[4.0,4.0,null],[4.0,0.0,null]]}", + "orient": "split", + "dtype": { + "x": "float64", + "y": "float64", + "coords": "object" + } + }, + "version": "2.8.0", + "converged": true, + "name": "5bus", + "f_hz": 50.0, + "sn_mva": 1, + "std_types": { + "line": { + "NAYY 4x50 SE": { + "c_nf_per_km": 210, + "r_ohm_per_km": 0.642, + "x_ohm_per_km": 0.083, + "max_i_ka": 0.142, + "type": "cs", + "q_mm2": 50, + "alpha": 0.00403 + }, + "NAYY 4x120 SE": { + "c_nf_per_km": 264, + "r_ohm_per_km": 0.225, + "x_ohm_per_km": 0.08, + "max_i_ka": 0.242, + "type": "cs", + "q_mm2": 120, + "alpha": 0.00403 + }, + "NAYY 4x150 SE": { + "c_nf_per_km": 261, + "r_ohm_per_km": 0.208, + "x_ohm_per_km": 0.08, + "max_i_ka": 0.27, + "type": "cs", + "q_mm2": 150, + "alpha": 0.00403 + }, + "NA2XS2Y 1x95 RM/25 12/20 kV": { + "c_nf_per_km": 216, + "r_ohm_per_km": 0.313, + "x_ohm_per_km": 0.132, + "max_i_ka": 0.252, + "type": "cs", + "q_mm2": 95, + "alpha": 0.00403 + }, + "NA2XS2Y 1x185 RM/25 12/20 kV": { + "c_nf_per_km": 273, + "r_ohm_per_km": 0.161, + "x_ohm_per_km": 0.117, + "max_i_ka": 0.362, + "type": "cs", + "q_mm2": 185, + "alpha": 0.00403 + }, + "NA2XS2Y 1x240 RM/25 12/20 kV": { + "c_nf_per_km": 304, + "r_ohm_per_km": 0.122, + "x_ohm_per_km": 0.112, + "max_i_ka": 0.421, + "type": "cs", + "q_mm2": 240, + "alpha": 0.00403 + }, + "NA2XS2Y 1x95 RM/25 6/10 kV": { + "c_nf_per_km": 315, + "r_ohm_per_km": 0.313, + "x_ohm_per_km": 0.123, + "max_i_ka": 0.249, + "type": "cs", + "q_mm2": 95, + "alpha": 0.00403 + }, + "NA2XS2Y 1x185 RM/25 6/10 kV": { + "c_nf_per_km": 406, + "r_ohm_per_km": 0.161, + "x_ohm_per_km": 0.11, + "max_i_ka": 0.358, + "type": "cs", + "q_mm2": 185, + "alpha": 0.00403 + }, + "NA2XS2Y 1x240 RM/25 6/10 kV": { + "c_nf_per_km": 456, + "r_ohm_per_km": 0.122, + "x_ohm_per_km": 0.105, + "max_i_ka": 0.416, + "type": "cs", + "q_mm2": 240, + "alpha": 0.00403 + }, + "NA2XS2Y 1x150 RM/25 12/20 kV": { + "c_nf_per_km": 250, + "r_ohm_per_km": 0.206, + "x_ohm_per_km": 0.116, + "max_i_ka": 0.319, + "type": "cs", + "q_mm2": 150, + "alpha": 0.00403 + }, + "NA2XS2Y 1x120 RM/25 12/20 kV": { + "c_nf_per_km": 230, + "r_ohm_per_km": 0.253, + "x_ohm_per_km": 0.119, + "max_i_ka": 0.283, + "type": "cs", + "q_mm2": 120, + "alpha": 0.00403 + }, + "NA2XS2Y 1x70 RM/25 12/20 kV": { + "c_nf_per_km": 190, + "r_ohm_per_km": 0.443, + "x_ohm_per_km": 0.132, + "max_i_ka": 0.22, + "type": "cs", + "q_mm2": 70, + "alpha": 0.00403 + }, + "NA2XS2Y 1x150 RM/25 6/10 kV": { + "c_nf_per_km": 360, + "r_ohm_per_km": 0.206, + "x_ohm_per_km": 0.11, + "max_i_ka": 0.315, + "type": "cs", + "q_mm2": 150, + "alpha": 0.00403 + }, + "NA2XS2Y 1x120 RM/25 6/10 kV": { + "c_nf_per_km": 340, + "r_ohm_per_km": 0.253, + "x_ohm_per_km": 0.113, + "max_i_ka": 0.28, + "type": "cs", + "q_mm2": 120, + "alpha": 0.00403 + }, + "NA2XS2Y 1x70 RM/25 6/10 kV": { + "c_nf_per_km": 280, + "r_ohm_per_km": 0.443, + "x_ohm_per_km": 0.123, + "max_i_ka": 0.217, + "type": "cs", + "q_mm2": 70, + "alpha": 0.00403 + }, + "N2XS(FL)2Y 1x120 RM/35 64/110 kV": { + "c_nf_per_km": 112, + "r_ohm_per_km": 0.153, + "x_ohm_per_km": 0.166, + "max_i_ka": 0.366, + "type": "cs", + "q_mm2": 120, + "alpha": 0.00393 + }, + "N2XS(FL)2Y 1x185 RM/35 64/110 kV": { + "c_nf_per_km": 125, + "r_ohm_per_km": 0.099, + "x_ohm_per_km": 0.156, + "max_i_ka": 0.457, + "type": "cs", + "q_mm2": 185, + "alpha": 0.00393 + }, + "N2XS(FL)2Y 1x240 RM/35 64/110 kV": { + "c_nf_per_km": 135, + "r_ohm_per_km": 0.075, + "x_ohm_per_km": 0.149, + "max_i_ka": 0.526, + "type": "cs", + "q_mm2": 240, + "alpha": 0.00393 + }, + "N2XS(FL)2Y 1x300 RM/35 64/110 kV": { + "c_nf_per_km": 144, + "r_ohm_per_km": 0.06, + "x_ohm_per_km": 0.144, + "max_i_ka": 0.588, + "type": "cs", + "q_mm2": 300, + "alpha": 0.00393 + }, + "15-AL1/3-ST1A 0.4": { + "c_nf_per_km": 11, + "r_ohm_per_km": 1.8769, + "x_ohm_per_km": 0.35, + "max_i_ka": 0.105, + "type": "ol", + "q_mm2": 16, + "alpha": 0.00403 + }, + "24-AL1/4-ST1A 0.4": { + "c_nf_per_km": 11.25, + "r_ohm_per_km": 1.2012, + "x_ohm_per_km": 0.335, + "max_i_ka": 0.14, + "type": "ol", + "q_mm2": 24, + "alpha": 0.00403 + }, + "48-AL1/8-ST1A 0.4": { + "c_nf_per_km": 12.2, + "r_ohm_per_km": 0.5939, + "x_ohm_per_km": 0.3, + "max_i_ka": 0.21, + "type": "ol", + "q_mm2": 48, + "alpha": 0.00403 + }, + "94-AL1/15-ST1A 0.4": { + "c_nf_per_km": 13.2, + "r_ohm_per_km": 0.306, + "x_ohm_per_km": 0.29, + "max_i_ka": 0.35, + "type": "ol", + "q_mm2": 94, + "alpha": 0.00403 + }, + "34-AL1/6-ST1A 10.0": { + "c_nf_per_km": 9.7, + "r_ohm_per_km": 0.8342, + "x_ohm_per_km": 0.36, + "max_i_ka": 0.17, + "type": "ol", + "q_mm2": 34, + "alpha": 0.00403 + }, + "48-AL1/8-ST1A 10.0": { + "c_nf_per_km": 10.1, + "r_ohm_per_km": 0.5939, + "x_ohm_per_km": 0.35, + "max_i_ka": 0.21, + "type": "ol", + "q_mm2": 48, + "alpha": 0.00403 + }, + "70-AL1/11-ST1A 10.0": { + "c_nf_per_km": 10.4, + "r_ohm_per_km": 0.4132, + "x_ohm_per_km": 0.339, + "max_i_ka": 0.29, + "type": "ol", + "q_mm2": 70, + "alpha": 0.00403 + }, + "94-AL1/15-ST1A 10.0": { + "c_nf_per_km": 10.75, + "r_ohm_per_km": 0.306, + "x_ohm_per_km": 0.33, + "max_i_ka": 0.35, + "type": "ol", + "q_mm2": 94, + "alpha": 0.00403 + }, + "122-AL1/20-ST1A 10.0": { + "c_nf_per_km": 11.1, + "r_ohm_per_km": 0.2376, + "x_ohm_per_km": 0.323, + "max_i_ka": 0.41, + "type": "ol", + "q_mm2": 122, + "alpha": 0.00403 + }, + "149-AL1/24-ST1A 10.0": { + "c_nf_per_km": 11.25, + "r_ohm_per_km": 0.194, + "x_ohm_per_km": 0.315, + "max_i_ka": 0.47, + "type": "ol", + "q_mm2": 149, + "alpha": 0.00403 + }, + "34-AL1/6-ST1A 20.0": { + "c_nf_per_km": 9.15, + "r_ohm_per_km": 0.8342, + "x_ohm_per_km": 0.382, + "max_i_ka": 0.17, + "type": "ol", + "q_mm2": 34, + "alpha": 0.00403 + }, + "48-AL1/8-ST1A 20.0": { + "c_nf_per_km": 9.5, + "r_ohm_per_km": 0.5939, + "x_ohm_per_km": 0.372, + "max_i_ka": 0.21, + "type": "ol", + "q_mm2": 48, + "alpha": 0.00403 + }, + "70-AL1/11-ST1A 20.0": { + "c_nf_per_km": 9.7, + "r_ohm_per_km": 0.4132, + "x_ohm_per_km": 0.36, + "max_i_ka": 0.29, + "type": "ol", + "q_mm2": 70, + "alpha": 0.00403 + }, + "94-AL1/15-ST1A 20.0": { + "c_nf_per_km": 10, + "r_ohm_per_km": 0.306, + "x_ohm_per_km": 0.35, + "max_i_ka": 0.35, + "type": "ol", + "q_mm2": 94, + "alpha": 0.00403 + }, + "122-AL1/20-ST1A 20.0": { + "c_nf_per_km": 10.3, + "r_ohm_per_km": 0.2376, + "x_ohm_per_km": 0.344, + "max_i_ka": 0.41, + "type": "ol", + "q_mm2": 122, + "alpha": 0.00403 + }, + "149-AL1/24-ST1A 20.0": { + "c_nf_per_km": 10.5, + "r_ohm_per_km": 0.194, + "x_ohm_per_km": 0.337, + "max_i_ka": 0.47, + "type": "ol", + "q_mm2": 149, + "alpha": 0.00403 + }, + "184-AL1/30-ST1A 20.0": { + "c_nf_per_km": 10.75, + "r_ohm_per_km": 0.1571, + "x_ohm_per_km": 0.33, + "max_i_ka": 0.535, + "type": "ol", + "q_mm2": 184, + "alpha": 0.00403 + }, + "243-AL1/39-ST1A 20.0": { + "c_nf_per_km": 11, + "r_ohm_per_km": 0.1188, + "x_ohm_per_km": 0.32, + "max_i_ka": 0.645, + "type": "ol", + "q_mm2": 243, + "alpha": 0.00403 + }, + "48-AL1/8-ST1A 110.0": { + "c_nf_per_km": 8, + "r_ohm_per_km": 0.5939, + "x_ohm_per_km": 0.46, + "max_i_ka": 0.21, + "type": "ol", + "q_mm2": 48, + "alpha": 0.00403 + }, + "70-AL1/11-ST1A 110.0": { + "c_nf_per_km": 8.4, + "r_ohm_per_km": 0.4132, + "x_ohm_per_km": 0.45, + "max_i_ka": 0.29, + "type": "ol", + "q_mm2": 70, + "alpha": 0.00403 + }, + "94-AL1/15-ST1A 110.0": { + "c_nf_per_km": 8.65, + "r_ohm_per_km": 0.306, + "x_ohm_per_km": 0.44, + "max_i_ka": 0.35, + "type": "ol", + "q_mm2": 94, + "alpha": 0.00403 + }, + "122-AL1/20-ST1A 110.0": { + "c_nf_per_km": 8.5, + "r_ohm_per_km": 0.2376, + "x_ohm_per_km": 0.43, + "max_i_ka": 0.41, + "type": "ol", + "q_mm2": 122, + "alpha": 0.00403 + }, + "149-AL1/24-ST1A 110.0": { + "c_nf_per_km": 8.75, + "r_ohm_per_km": 0.194, + "x_ohm_per_km": 0.41, + "max_i_ka": 0.47, + "type": "ol", + "q_mm2": 149, + "alpha": 0.00403 + }, + "184-AL1/30-ST1A 110.0": { + "c_nf_per_km": 8.8, + "r_ohm_per_km": 0.1571, + "x_ohm_per_km": 0.4, + "max_i_ka": 0.535, + "type": "ol", + "q_mm2": 184, + "alpha": 0.00403 + }, + "243-AL1/39-ST1A 110.0": { + "c_nf_per_km": 9, + "r_ohm_per_km": 0.1188, + "x_ohm_per_km": 0.39, + "max_i_ka": 0.645, + "type": "ol", + "q_mm2": 243, + "alpha": 0.00403 + }, + "305-AL1/39-ST1A 110.0": { + "c_nf_per_km": 9.2, + "r_ohm_per_km": 0.0949, + "x_ohm_per_km": 0.38, + "max_i_ka": 0.74, + "type": "ol", + "q_mm2": 305, + "alpha": 0.00403 + }, + "490-AL1/64-ST1A 110.0": { + "c_nf_per_km": 9.75, + "r_ohm_per_km": 0.059, + "x_ohm_per_km": 0.37, + "max_i_ka": 0.96, + "type": "ol", + "q_mm2": 490, + "alpha": 0.00403 + }, + "679-AL1/86-ST1A 110.0": { + "c_nf_per_km": 9.95, + "r_ohm_per_km": 0.042, + "x_ohm_per_km": 0.36, + "max_i_ka": 0.115, + "type": "ol", + "q_mm2": 679, + "alpha": 0.00403 + }, + "490-AL1/64-ST1A 220.0": { + "c_nf_per_km": 10, + "r_ohm_per_km": 0.059, + "x_ohm_per_km": 0.285, + "max_i_ka": 0.96, + "type": "ol", + "q_mm2": 490, + "alpha": 0.00403 + }, + "679-AL1/86-ST1A 220.0": { + "c_nf_per_km": 11.7, + "r_ohm_per_km": 0.042, + "x_ohm_per_km": 0.275, + "max_i_ka": 0.115, + "type": "ol", + "q_mm2": 679, + "alpha": 0.00403 + }, + "490-AL1/64-ST1A 380.0": { + "c_nf_per_km": 11, + "r_ohm_per_km": 0.059, + "x_ohm_per_km": 0.253, + "max_i_ka": 0.96, + "type": "ol", + "q_mm2": 490, + "alpha": 0.00403 + }, + "679-AL1/86-ST1A 380.0": { + "c_nf_per_km": 14.6, + "r_ohm_per_km": 0.042, + "x_ohm_per_km": 0.25, + "max_i_ka": 0.115, + "type": "ol", + "q_mm2": 679, + "alpha": 0.00403 + } + }, + "trafo": { + "160 MVA 380/110 kV": { + "i0_percent": 0.06, + "pfe_kw": 60, + "vkr_percent": 0.25, + "sn_mva": 160, + "vn_lv_kv": 110.0, + "vn_hv_kv": 380.0, + "vk_percent": 12.2, + "shift_degree": 0, + "vector_group": "Yy0", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "100 MVA 220/110 kV": { + "i0_percent": 0.06, + "pfe_kw": 55, + "vkr_percent": 0.26, + "sn_mva": 100, + "vn_lv_kv": 110.0, + "vn_hv_kv": 220.0, + "vk_percent": 12.0, + "shift_degree": 0, + "vector_group": "Yy0", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "63 MVA 110/20 kV": { + "i0_percent": 0.04, + "pfe_kw": 22, + "vkr_percent": 0.32, + "sn_mva": 63, + "vn_lv_kv": 20.0, + "vn_hv_kv": 110.0, + "vk_percent": 18, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "40 MVA 110/20 kV": { + "i0_percent": 0.05, + "pfe_kw": 18, + "vkr_percent": 0.34, + "sn_mva": 40, + "vn_lv_kv": 20.0, + "vn_hv_kv": 110.0, + "vk_percent": 16.2, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "25 MVA 110/20 kV": { + "i0_percent": 0.07, + "pfe_kw": 14, + "vkr_percent": 0.41, + "sn_mva": 25, + "vn_lv_kv": 20.0, + "vn_hv_kv": 110.0, + "vk_percent": 12, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "63 MVA 110/10 kV": { + "sn_mva": 63, + "vn_hv_kv": 110, + "vn_lv_kv": 10, + "vk_percent": 18, + "vkr_percent": 0.32, + "pfe_kw": 22, + "i0_percent": 0.04, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "40 MVA 110/10 kV": { + "sn_mva": 40, + "vn_hv_kv": 110, + "vn_lv_kv": 10, + "vk_percent": 16.2, + "vkr_percent": 0.34, + "pfe_kw": 18, + "i0_percent": 0.05, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "25 MVA 110/10 kV": { + "sn_mva": 25, + "vn_hv_kv": 110, + "vn_lv_kv": 10, + "vk_percent": 12, + "vkr_percent": 0.41, + "pfe_kw": 14, + "i0_percent": 0.07, + "shift_degree": 150, + "vector_group": "YNd5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -9, + "tap_max": 9, + "tap_step_degree": 0, + "tap_step_percent": 1.5, + "tap_phase_shifter": false + }, + "0.25 MVA 20/0.4 kV": { + "sn_mva": 0.25, + "vn_hv_kv": 20, + "vn_lv_kv": 0.4, + "vk_percent": 6, + "vkr_percent": 1.44, + "pfe_kw": 0.8, + "i0_percent": 0.32, + "shift_degree": 150, + "vector_group": "Yzn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.4 MVA 20/0.4 kV": { + "sn_mva": 0.4, + "vn_hv_kv": 20, + "vn_lv_kv": 0.4, + "vk_percent": 6, + "vkr_percent": 1.425, + "pfe_kw": 1.35, + "i0_percent": 0.3375, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.63 MVA 20/0.4 kV": { + "sn_mva": 0.63, + "vn_hv_kv": 20, + "vn_lv_kv": 0.4, + "vk_percent": 6, + "vkr_percent": 1.206, + "pfe_kw": 1.65, + "i0_percent": 0.2619, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.25 MVA 10/0.4 kV": { + "sn_mva": 0.25, + "vn_hv_kv": 10, + "vn_lv_kv": 0.4, + "vk_percent": 4, + "vkr_percent": 1.2, + "pfe_kw": 0.6, + "i0_percent": 0.24, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.4 MVA 10/0.4 kV": { + "sn_mva": 0.4, + "vn_hv_kv": 10, + "vn_lv_kv": 0.4, + "vk_percent": 4, + "vkr_percent": 1.325, + "pfe_kw": 0.95, + "i0_percent": 0.2375, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + }, + "0.63 MVA 10/0.4 kV": { + "sn_mva": 0.63, + "vn_hv_kv": 10, + "vn_lv_kv": 0.4, + "vk_percent": 4, + "vkr_percent": 1.0794, + "pfe_kw": 1.18, + "i0_percent": 0.1873, + "shift_degree": 150, + "vector_group": "Dyn5", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -2, + "tap_max": 2, + "tap_step_degree": 0, + "tap_step_percent": 2.5, + "tap_phase_shifter": false + } + }, + "trafo3w": { + "63/25/38 MVA 110/20/10 kV": { + "sn_hv_mva": 63, + "sn_mv_mva": 25, + "sn_lv_mva": 38, + "vn_hv_kv": 110, + "vn_mv_kv": 20, + "vn_lv_kv": 10, + "vk_hv_percent": 10.4, + "vk_mv_percent": 10.4, + "vk_lv_percent": 10.4, + "vkr_hv_percent": 0.28, + "vkr_mv_percent": 0.32, + "vkr_lv_percent": 0.35, + "pfe_kw": 35, + "i0_percent": 0.89, + "shift_mv_degree": 0, + "shift_lv_degree": 0, + "vector_group": "YN0yn0yn0", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -10, + "tap_max": 10, + "tap_step_percent": 1.2 + }, + "63/25/38 MVA 110/10/10 kV": { + "sn_hv_mva": 63, + "sn_mv_mva": 25, + "sn_lv_mva": 38, + "vn_hv_kv": 110, + "vn_mv_kv": 10, + "vn_lv_kv": 10, + "vk_hv_percent": 10.4, + "vk_mv_percent": 10.4, + "vk_lv_percent": 10.4, + "vkr_hv_percent": 0.28, + "vkr_mv_percent": 0.32, + "vkr_lv_percent": 0.35, + "pfe_kw": 35, + "i0_percent": 0.89, + "shift_mv_degree": 0, + "shift_lv_degree": 0, + "vector_group": "YN0yn0yn0", + "tap_side": "hv", + "tap_neutral": 0, + "tap_min": -10, + "tap_max": 10, + "tap_step_percent": 1.2 + } + } + }, + "res_bus": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"vm_pu\",\"va_degree\",\"p_mw\",\"q_mvar\"],\"index\":[0,1,2,3,4],\"data\":[[1.02,-0.845445168673926,0.0,-111.791243672370911],[1.02,0.0,-21.729831330858325,116.839935541152954],[1.019214100496144,-0.409103297622625,0.0,0.0],[1.018637116919488,-0.503470352662766,10.0,7.0],[1.017983079721402,-0.653497665026562,10.0,7.0]]}", + "orient": "split", + "dtype": { + "vm_pu": "float64", + "va_degree": "float64", + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_line": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"ql_mvar\",\"i_from_ka\",\"i_to_ka\",\"i_ka\",\"vm_from_pu\",\"va_from_degree\",\"vm_to_pu\",\"va_to_degree\",\"loading_percent\"],\"index\":[0,1,2,3,4,5,6,7],\"data\":[[-7.167647147657727,57.480079867900443,8.03525639977348,-60.113463233922118,0.867609252115754,-2.633383366021676,0.327874112511858,0.343286326507116,0.343286326507116,1.02,-0.845445168673926,1.02,0.0,57.214387751185988],[-0.657313913963437,25.969126903729045,0.866078469150186,-29.007927174007612,0.208764555186749,-3.038800270278568,0.147040043868819,0.164393305610081,0.164393305610081,1.02,-0.845445168673926,1.019214100496144,-0.409103297622625,74.724229822763931],[1.64566972119938,15.370129751576128,-1.540268914180618,-19.229415550834709,0.105400807018762,-3.859285799258581,0.087496748884432,0.109338903896103,0.109338903896103,1.02,-0.845445168673926,1.018637116919488,-0.503470352662766,68.336814935064211],[6.179291340421495,12.971907266349552,-6.119076735247816,-15.70424981919658,0.060214605173678,-2.732342552847028,0.081330018729726,0.095589209712924,0.095589209712924,1.02,-0.845445168673926,1.017983079721402,-0.653497665026562,59.743256070577175],[13.694574931085771,-56.726472302863066,-13.283848894885464,55.407854241119566,0.410726036200307,-1.3186180617435,0.330312825878128,0.322760996590474,0.330312825878128,1.02,0.0,1.019214100496144,-0.409103297622625,55.052137646354595],[6.208885212872048,-13.199963533555254,-6.184761786109662,11.833197159642042,0.024123426762386,-1.366766373913212,0.082632108556076,0.075677384410291,0.082632108556076,1.019214100496144,-0.409103297622625,1.018637116919488,-0.503470352662766,27.544036185358689],[6.208885212872048,-13.199963533555254,-6.184761786109662,11.833197159642042,0.024123426762386,-1.366766373913212,0.082632108556076,0.075677384410291,0.082632108556076,1.019214100496144,-0.409103297622625,1.018637116919488,-0.503470352662766,27.544036185358689],[3.909792486391969,-11.436978768449999,-3.88092326475316,8.704249819196738,0.028869221638809,-2.732728949253261,0.068506463438984,0.054050881891821,0.068506463438984,1.018637116919488,-0.503470352662766,1.017983079721402,-0.653497665026562,42.816539649365005]]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_from_ka": "float64", + "i_to_ka": "float64", + "i_ka": "float64", + "vm_from_pu": "float64", + "va_from_degree": "float64", + "vm_to_pu": "float64", + "va_to_degree": "float64", + "loading_percent": "float64" + } + }, + "res_trafo": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_hv_mw\",\"q_hv_mvar\",\"p_lv_mw\",\"q_lv_mvar\",\"pl_mw\",\"ql_mvar\",\"i_hv_ka\",\"i_lv_ka\",\"vm_hv_pu\",\"va_hv_degree\",\"vm_lv_pu\",\"va_lv_degree\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_hv_mw": "float64", + "q_hv_mvar": "float64", + "p_lv_mw": "float64", + "q_lv_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_hv_ka": "float64", + "i_lv_ka": "float64", + "vm_hv_pu": "float64", + "va_hv_degree": "float64", + "vm_lv_pu": "float64", + "va_lv_degree": "float64", + "loading_percent": "float64" + } + }, + "res_trafo3w": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_hv_mw\",\"q_hv_mvar\",\"p_mv_mw\",\"q_mv_mvar\",\"p_lv_mw\",\"q_lv_mvar\",\"pl_mw\",\"ql_mvar\",\"i_hv_ka\",\"i_mv_ka\",\"i_lv_ka\",\"vm_hv_pu\",\"va_hv_degree\",\"vm_mv_pu\",\"va_mv_degree\",\"vm_lv_pu\",\"va_lv_degree\",\"va_internal_degree\",\"vm_internal_pu\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_hv_mw": "float64", + "q_hv_mvar": "float64", + "p_mv_mw": "float64", + "q_mv_mvar": "float64", + "p_lv_mw": "float64", + "q_lv_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_hv_ka": "float64", + "i_mv_ka": "float64", + "i_lv_ka": "float64", + "vm_hv_pu": "float64", + "va_hv_degree": "float64", + "vm_mv_pu": "float64", + "va_mv_degree": "float64", + "vm_lv_pu": "float64", + "va_lv_degree": "float64", + "va_internal_degree": "float64", + "vm_internal_pu": "float64", + "loading_percent": "float64" + } + }, + "res_impedance": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"ql_mvar\",\"i_from_ka\",\"i_to_ka\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_from_ka": "float64", + "i_to_ka": "float64" + } + }, + "res_ext_grid": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_load": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[0,1,2],\"data\":[[10.0,7.0],[10.0,7.0],[10.0,7.0]]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_motor": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_sgen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_storage": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_shunt": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\",\"vm_pu\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64", + "vm_pu": "float64" + } + }, + "res_gen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\",\"va_degree\",\"vm_pu\"],\"index\":[0,1],\"data\":[[10.0,118.791243672370911,-0.845445168673926,1.02],[21.729831330858325,-116.839935541152954,0.0,1.02]]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64", + "va_degree": "float64", + "vm_pu": "float64" + } + }, + "res_ward": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\",\"vm_pu\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64", + "vm_pu": "float64" + } + }, + "res_xward": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\",\"vm_pu\",\"va_internal_degree\",\"vm_internal_pu\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64", + "vm_pu": "float64", + "va_internal_degree": "float64", + "vm_internal_pu": "float64" + } + }, + "res_dcline": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"vm_from_pu\",\"va_from_degree\",\"vm_to_pu\",\"va_to_degree\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "vm_from_pu": "float64", + "va_from_degree": "float64", + "vm_to_pu": "float64", + "va_to_degree": "float64" + } + }, + "res_asymmetric_load": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_asymmetric_sgen": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_bus_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"vm_pu\",\"va_degree\",\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "vm_pu": "float64", + "va_degree": "float64", + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_line_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"ql_mvar\",\"i_from_ka\",\"i_to_ka\",\"i_ka\",\"vm_from_pu\",\"va_from_degree\",\"vm_to_pu\",\"va_to_degree\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_from_ka": "float64", + "i_to_ka": "float64", + "i_ka": "float64", + "vm_from_pu": "float64", + "va_from_degree": "float64", + "vm_to_pu": "float64", + "va_to_degree": "float64", + "loading_percent": "float64" + } + }, + "res_trafo_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_hv_mw\",\"q_hv_mvar\",\"p_lv_mw\",\"q_lv_mvar\",\"pl_mw\",\"ql_mvar\",\"i_hv_ka\",\"i_lv_ka\",\"vm_hv_pu\",\"va_hv_degree\",\"vm_lv_pu\",\"va_lv_degree\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_hv_mw": "float64", + "q_hv_mvar": "float64", + "p_lv_mw": "float64", + "q_lv_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_hv_ka": "float64", + "i_lv_ka": "float64", + "vm_hv_pu": "float64", + "va_hv_degree": "float64", + "vm_lv_pu": "float64", + "va_lv_degree": "float64", + "loading_percent": "float64" + } + }, + "res_trafo3w_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_hv_mw\",\"q_hv_mvar\",\"p_mv_mw\",\"q_mv_mvar\",\"p_lv_mw\",\"q_lv_mvar\",\"pl_mw\",\"ql_mvar\",\"i_hv_ka\",\"i_mv_ka\",\"i_lv_ka\",\"vm_hv_pu\",\"va_hv_degree\",\"vm_mv_pu\",\"va_mv_degree\",\"vm_lv_pu\",\"va_lv_degree\",\"va_internal_degree\",\"vm_internal_pu\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_hv_mw": "float64", + "q_hv_mvar": "float64", + "p_mv_mw": "float64", + "q_mv_mvar": "float64", + "p_lv_mw": "float64", + "q_lv_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_hv_ka": "float64", + "i_mv_ka": "float64", + "i_lv_ka": "float64", + "vm_hv_pu": "float64", + "va_hv_degree": "float64", + "vm_mv_pu": "float64", + "va_mv_degree": "float64", + "vm_lv_pu": "float64", + "va_lv_degree": "float64", + "va_internal_degree": "float64", + "vm_internal_pu": "float64", + "loading_percent": "float64" + } + }, + "res_impedance_est": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_from_mw\",\"q_from_mvar\",\"p_to_mw\",\"q_to_mvar\",\"pl_mw\",\"ql_mvar\",\"i_from_ka\",\"i_to_ka\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_from_mw": "float64", + "q_from_mvar": "float64", + "p_to_mw": "float64", + "q_to_mvar": "float64", + "pl_mw": "float64", + "ql_mvar": "float64", + "i_from_ka": "float64", + "i_to_ka": "float64" + } + }, + "res_bus_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_line_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_trafo_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_trafo3w_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_ext_grid_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_gen_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_sgen_sc": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_bus_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"vm_a_pu\",\"va_a_degree\",\"vm_b_pu\",\"va_b_degree\",\"vm_c_pu\",\"va_c_degree\",\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "vm_a_pu": "float64", + "va_a_degree": "float64", + "vm_b_pu": "float64", + "va_b_degree": "float64", + "vm_c_pu": "float64", + "va_c_degree": "float64", + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64" + } + }, + "res_line_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_from_mw\",\"q_a_from_mvar\",\"p_b_from_mw\",\"q_b_from_mvar\",\"q_c_from_mvar\",\"p_a_to_mw\",\"q_a_to_mvar\",\"p_b_to_mw\",\"q_b_to_mvar\",\"p_c_to_mw\",\"q_c_to_mvar\",\"p_a_l_mw\",\"q_a_l_mvar\",\"p_b_l_mw\",\"q_b_l_mvar\",\"p_c_l_mw\",\"q_c_l_mvar\",\"i_a_from_ka\",\"i_a_to_ka\",\"i_b_from_ka\",\"i_b_to_ka\",\"i_c_from_ka\",\"i_c_to_ka\",\"i_a_ka\",\"i_b_ka\",\"i_c_ka\",\"i_n_from_ka\",\"i_n_to_ka\",\"i_n_ka\",\"loading_a_percent\",\"loading_b_percent\",\"loading_c_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_from_mw": "float64", + "q_a_from_mvar": "float64", + "p_b_from_mw": "float64", + "q_b_from_mvar": "float64", + "q_c_from_mvar": "float64", + "p_a_to_mw": "float64", + "q_a_to_mvar": "float64", + "p_b_to_mw": "float64", + "q_b_to_mvar": "float64", + "p_c_to_mw": "float64", + "q_c_to_mvar": "float64", + "p_a_l_mw": "float64", + "q_a_l_mvar": "float64", + "p_b_l_mw": "float64", + "q_b_l_mvar": "float64", + "p_c_l_mw": "float64", + "q_c_l_mvar": "float64", + "i_a_from_ka": "float64", + "i_a_to_ka": "float64", + "i_b_from_ka": "float64", + "i_b_to_ka": "float64", + "i_c_from_ka": "float64", + "i_c_to_ka": "float64", + "i_a_ka": "float64", + "i_b_ka": "float64", + "i_c_ka": "float64", + "i_n_from_ka": "float64", + "i_n_to_ka": "float64", + "i_n_ka": "float64", + "loading_a_percent": "float64", + "loading_b_percent": "float64", + "loading_c_percent": "float64" + } + }, + "res_trafo_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_hv_mw\",\"q_a_hv_mvar\",\"p_b_hv_mw\",\"q_b_hv_mvar\",\"p_c_hv_mw\",\"q_c_hv_mvar\",\"p_a_lv_mw\",\"q_a_lv_mvar\",\"p_b_lv_mw\",\"q_b_lv_mvar\",\"p_c_lv_mw\",\"q_c_lv_mvar\",\"p_a_l_mw\",\"q_a_l_mvar\",\"p_b_l_mw\",\"q_b_l_mvar\",\"p_c_l_mw\",\"q_c_l_mvar\",\"i_a_hv_ka\",\"i_a_lv_ka\",\"i_b_hv_ka\",\"i_b_lv_ka\",\"i_c_hv_ka\",\"i_c_lv_ka\",\"loading_a_percent\",\"loading_b_percent\",\"loading_c_percent\",\"loading_percent\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_hv_mw": "float64", + "q_a_hv_mvar": "float64", + "p_b_hv_mw": "float64", + "q_b_hv_mvar": "float64", + "p_c_hv_mw": "float64", + "q_c_hv_mvar": "float64", + "p_a_lv_mw": "float64", + "q_a_lv_mvar": "float64", + "p_b_lv_mw": "float64", + "q_b_lv_mvar": "float64", + "p_c_lv_mw": "float64", + "q_c_lv_mvar": "float64", + "p_a_l_mw": "float64", + "q_a_l_mvar": "float64", + "p_b_l_mw": "float64", + "q_b_l_mvar": "float64", + "p_c_l_mw": "float64", + "q_c_l_mvar": "float64", + "i_a_hv_ka": "float64", + "i_a_lv_ka": "float64", + "i_b_hv_ka": "float64", + "i_b_lv_ka": "float64", + "i_c_hv_ka": "float64", + "i_c_lv_ka": "float64", + "loading_a_percent": "float64", + "loading_b_percent": "float64", + "loading_c_percent": "float64", + "loading_percent": "float64" + } + }, + "res_ext_grid_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64" + } + }, + "res_shunt_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[],\"index\":[],\"data\":[]}", + "orient": "split" + }, + "res_load_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_sgen_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_storage_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_mw\",\"q_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_mw": "float64", + "q_mvar": "float64" + } + }, + "res_asymmetric_load_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64" + } + }, + "res_asymmetric_sgen_3ph": { + "_module": "pandas.core.frame", + "_class": "DataFrame", + "_object": "{\"columns\":[\"p_a_mw\",\"q_a_mvar\",\"p_b_mw\",\"q_b_mvar\",\"p_c_mw\",\"q_c_mvar\"],\"index\":[],\"data\":[]}", + "orient": "split", + "dtype": { + "p_a_mw": "float64", + "q_a_mvar": "float64", + "p_b_mw": "float64", + "q_b_mvar": "float64", + "p_c_mw": "float64", + "q_c_mvar": "float64" + } + }, + "user_pf_options": {}, + "OPF_converged": false + } +} \ No newline at end of file diff --git a/grid2op/data_test/5bus_fake_grid_format/grid_layout.json b/grid2op/data_test/5bus_fake_grid_format/grid_layout.json new file mode 100644 index 000000000..1107ee55a --- /dev/null +++ b/grid2op/data_test/5bus_fake_grid_format/grid_layout.json @@ -0,0 +1,30 @@ +{ + "sub_0": [ + 0.0, + 0.0 + ], + "sub_1": [ + 0.0, + 400.0 + ], + "sub_2": [ + 200.0, + 400.0 + ], + "sub_3": [ + 400.0, + 400.0 + ], + "sub_4": [ + 400.0, + 0.0 + ], + "sub_5": [ + 500.0, + 0.0 + ], + "sub_6": [ + 400.0, + -100.0 + ] +} diff --git a/grid2op/data_test/5bus_fake_grid_format/readme.md b/grid2op/data_test/5bus_fake_grid_format/readme.md new file mode 100644 index 000000000..82ad05a3e --- /dev/null +++ b/grid2op/data_test/5bus_fake_grid_format/readme.md @@ -0,0 +1 @@ +# see issue https://github.com/rte-france/Grid2Op/issues/217 diff --git a/grid2op/tests/BaseBackendTest.py b/grid2op/tests/BaseBackendTest.py index e7a13422f..c0cd9ba19 100644 --- a/grid2op/tests/BaseBackendTest.py +++ b/grid2op/tests/BaseBackendTest.py @@ -17,12 +17,11 @@ from abc import ABC, abstractmethod import inspect -from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST +from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST, MakeBackend, HelperTests PATH_DATA_TEST_INIT = PATH_DATA_TEST PATH_DATA_TEST = PATH_DATA_TEST_PP import grid2op -from grid2op.tests.helper_path_test import HelperTests from grid2op.Action import CompleteAction @@ -67,28 +66,6 @@ def comb(n, k): from grid2op.Backend import Backend, PandaPowerBackend import pdb - - -class MakeBackend(ABC, HelperTests): - @abstractmethod - def make_backend(self, detailed_infos_for_cascading_failures=False) -> Backend: - pass - - def get_path(self) -> str: - raise NotImplementedError( - "This function should be implemented for the test suit you are developping" - ) - - def get_casefile(self) -> str: - raise NotImplementedError( - "This function should be implemented for the test suit you are developping" - ) - - def skip_if_needed(self) -> None: - if hasattr(self, "tests_skipped"): - nm_ = inspect.currentframe().f_back.f_code.co_name - if nm_ in self.tests_skipped: - self.skipTest('the test "{}" is skipped'.format(nm_)) class BaseTestNames(MakeBackend): diff --git a/grid2op/tests/BaseRedispTest.py b/grid2op/tests/BaseRedispTest.py index 749d332c7..f12087dd4 100644 --- a/grid2op/tests/BaseRedispTest.py +++ b/grid2op/tests/BaseRedispTest.py @@ -11,6 +11,7 @@ import warnings from grid2op.tests.helper_path_test import * +from grid2op.tests.helper_path_test import MakeBackend import grid2op from grid2op.Exceptions import * @@ -19,7 +20,6 @@ from grid2op.Chronics import ChronicsHandler, GridStateFromFile, ChangeNothing from grid2op.Action import BaseAction -from grid2op.tests.BaseBackendTest import MakeBackend class BaseTestRedispatch(MakeBackend): diff --git a/grid2op/tests/aaa_test_backend_interface.py b/grid2op/tests/aaa_test_backend_interface.py index da0baebee..02467f690 100644 --- a/grid2op/tests/aaa_test_backend_interface.py +++ b/grid2op/tests/aaa_test_backend_interface.py @@ -9,11 +9,11 @@ import os import numpy as np import warnings -from grid2op.tests.helper_path_test import HelperTests, PATH_DATA +from grid2op.tests.helper_path_test import HelperTests, MakeBackend, PATH_DATA from grid2op.Exceptions import BackendError -class AAATestBackendAPI(HelperTests): +class AAATestBackendAPI(MakeBackend): # def make_backend(self, detailed_infos_for_cascading_failures=False): # return PandaPowerBackend() # TODO REMOVE # # from lightsim2grid import LightSimBackend @@ -25,7 +25,7 @@ class AAATestBackendAPI(HelperTests): # init_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) # init_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) # init_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) - + def get_path(self): return os.path.join(PATH_DATA, "educ_case14_storage") @@ -49,6 +49,7 @@ def aux_make_backend(self): def test_00create_backend(self): """Tests the backend can be created (not integrated in a grid2op environment yet)""" + self.skip_if_needed() backend = self.make_backend() def test_01load_grid(self): @@ -57,7 +58,12 @@ def test_01load_grid(self): This test supposes that : - backend.load_grid(...) is implemented + + .. danger:: + This test will NOT pass if the grid is not the "educ_case14_storage" file. + """ + self.skip_if_needed() backend = self.make_backend() backend.load_grid(self.get_path(), self.get_casefile()) # both argument filled backend.load_redispacthing_data(self.get_path()) @@ -66,14 +72,14 @@ def test_01load_grid(self): backend.env_name = env_name backend.assert_grid_correct() cls = type(backend) - assert cls.n_line == 20, f"there should be 20 lines / trafos on the grid, found {cls.n_line} (remember trafo are conted grid2op side as powerline)" - assert cls.n_gen == 6, f"there should be 6 generators on the grid found {cls.n_gen} (remember a generator is added to the slack if none are present)" - assert cls.n_load == 11, f"there should be 11 loads on the grid, found {cls.n_load}" - assert cls.n_sub == 14, f"there should be 14 substations on this grid, found {cls.n_sub}" + assert cls.n_line == 20, f"there should be 20 lines / trafos on the grid (if you used the pandapower default grid), found {cls.n_line} (remember trafo are conted grid2op side as powerline)" + assert cls.n_gen == 6, f"there should be 6 generators on the grid (if you used the pandapower default grid) found {cls.n_gen} (remember a generator is added to the slack if none are present)" + assert cls.n_load == 11, f"there should be 11 loads on the grid (if you used the pandapower default grid), found {cls.n_load}" + assert cls.n_sub == 14, f"there should be 14 substations on this grid (if you used the pandapower default grid), found {cls.n_sub}" if cls.shunts_data_available: - assert cls.n_shunt == 1, f"there should be 1 shunt on the grid, found {cls.n_shunt}" + assert cls.n_shunt == 1, f"there should be 1 shunt on the grid (if you used the pandapower default grid), found {cls.n_shunt}" if cls.n_storage > 0: - assert cls.n_storage == 2, f"there should be 2 storage units on this grid, found {cls.n_storage}" + assert cls.n_storage == 2, f"there should be 2 storage units on this grid (if you used the pandapower default grid), found {cls.n_storage}" assert env_name in cls.env_name, f"you probably should not have overidden the assert_grid_correct function !" backend.close() @@ -99,9 +105,11 @@ def test_02modify_load(self): NB: it does not check whether or not the modification is consistent with the input. This will be done in a later test""" + self.skip_if_needed() backend = self.aux_make_backend() - random_load_p = np.array([21.7, 94.2, 47.8, 7.6, 11.2, 29.5, 9. , 3.5, 6.1, 13.5, 14.9]) - random_load_q = np.array([12.7, 19. , -3.9, 1.6, 7.5, 16.6, 5.8, 1.8, 1.6, 5.8, 5. ]) + np.random.seed(0) + random_load_p = np.random.uniform(0, 1, size=type(backend).n_load) + random_load_q = np.random.uniform(0, 1, size=type(backend).n_load) # try to modify load_p action = type(backend)._complete_action_class() @@ -127,9 +135,11 @@ def test_03modify_gen(self): NB: it does not check whether or not the modification is consistent with the input. This will be done in a later test""" + self.skip_if_needed() backend = self.aux_make_backend() - random_gen_p = np.array([ 40., 0., 0., 0., 0., 219.]) - random_gen_v = np.array([144.21, 139.38, 21.4 , 21.4 , 13.08, 146.28]) + np.random.seed(0) + random_gen_p = np.random.uniform(0, 1, size=type(backend).n_gen) + random_gen_v = np.random.uniform(0, 1, size=type(backend).n_gen) # try to modify gen_p action = type(backend)._complete_action_class() @@ -155,6 +165,7 @@ def test_04disco_reco_lines(self): NB: it does not check whether or not the modification is consistent with the input. This will be done in a later test""" + self.skip_if_needed() backend = self.aux_make_backend() line_id = 0 @@ -182,6 +193,7 @@ def test_05change_topology(self): NB: it does not check whether or not the modification is consistent with the input. This will be done in a later test""" + self.skip_if_needed() backend = self.aux_make_backend() sub_id = 0 @@ -213,6 +225,7 @@ def test_06modify_shunt(self): NB: it does not check whether or not the modification is consistent with the input. This will be done in a later test """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) if not cls.shunts_data_available: @@ -257,6 +270,7 @@ def test_07modify_storage(self): NB: this test is skipped if your backend does not support (yet :-) ) storage units """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) if cls.n_storage == 0: @@ -279,6 +293,7 @@ def test_08run_ac_pf(self): - backend.runpf() (DC mode) is implemented """ + self.skip_if_needed() backend = self.aux_make_backend() res = backend.runpf(is_dc=False) @@ -298,6 +313,7 @@ def test_09run_dc_pf(self): - backend.load_grid(...) is implemented - backend.runpf() (DC mode) is implemented """ + self.skip_if_needed() backend = self.aux_make_backend() res = backend.runpf(is_dc=True) @@ -321,6 +337,7 @@ def test_10_ac_forced_divergence(self): - backend.loads_info() is implemented """ + self.skip_if_needed() backend = self.aux_make_backend() res = backend.runpf(is_dc=False) @@ -365,7 +382,7 @@ def test_11_modify_load_pf_getter(self): - backend.loads_info() is implemented - backend.generators_info() is implemented """ - + self.skip_if_needed() backend = self.aux_make_backend() res = backend.runpf(is_dc=False) @@ -424,6 +441,7 @@ def test_12_modify_gen_pf_getter(self): - backend.loads_info() is implemented """ + self.skip_if_needed() backend = self.aux_make_backend() res = backend.runpf(is_dc=False) tmp = backend.generators_info() @@ -490,6 +508,7 @@ def test_13_disco_reco_lines_pf_getter(self): the grid (modeled by your powerflow) but that you did not yet coded the interface between said element and grid2op (the backend you are creating) """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) @@ -575,6 +594,7 @@ def test_14change_topology(self): the grid (modeled by your powerflow) but that you did not yet coded the interface between said element and grid2op (the backend you are creating) """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) @@ -643,7 +663,7 @@ def test_15_reset(self): - backend.generators_info() is implemented - backend.lines_or_info() is implemented """ - + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) backend2 = self.aux_make_backend() @@ -700,6 +720,7 @@ def test_16_isolated_load_make_divergence(self): - backend.apply_action() for topology modification - backend.reset() is implemented """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) @@ -734,6 +755,7 @@ def test_17_isolated_gen_make_divergence(self): - backend.apply_action() for topology modification - backend.reset() is implemented """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) @@ -770,11 +792,14 @@ def test_18_isolated_shunt_make_divergence(self): NB: this test is skipped if your backend does not (yet :-) ) supports shunt """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) if not cls.shunts_data_available: self.skipTest("Your backend does not support shunts") - + if cls.n_shunt == 0: + self.skipTest("Your grid has no shunt in it") + # make a shunt alone on a bus action = type(backend)._complete_action_class() action.update({"shunt": {"shunt_bus": [(0, 2)]}}) @@ -808,6 +833,7 @@ def test_19_isolated_storage_make_divergence(self): NB: this test is skipped if your backend does not (yet :-) ) supports storage units """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) if cls.n_storage == 0: @@ -844,6 +870,7 @@ def test_20_disconnected_load_make_divergence(self): NB: this test is skipped if your backend does not (yet :-) ) supports storage units """ + self.skip_if_needed() backend = self.aux_make_backend() # a load alone on a bus @@ -879,6 +906,7 @@ def test_21_disconnected_gen_make_divergence(self): NB: this test is skipped if your backend does not (yet :-) ) supports storage units """ + self.skip_if_needed() backend = self.aux_make_backend() # a disconnected generator @@ -918,6 +946,7 @@ def test_22_islanded_grid_make_divergence(self): NB: this test is skipped if your backend does not (yet :-) ) supports storage units """ + self.skip_if_needed() backend = self.aux_make_backend() # a non connected grid action = type(backend)._complete_action_class() @@ -954,6 +983,7 @@ def test_23_disco_line_v_null(self): - backend.shunt() and lines_ex_info() are implemented - backend.reset() is implemented """ + self.skip_if_needed() backend = self.aux_make_backend() line_id = 0 @@ -1001,11 +1031,14 @@ def test_24_disco_shunt_v_null(self): NB: this test is skipped if your backend does not support shunt (yet :-) ) """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) if not cls.shunts_data_available: self.skipTest("Your backend does not support shunts") - + if cls.n_shunt == 0: + self.skipTest("Your grid has no shunt in it") + shunt_id = 0 # a disconnected shunt action = type(backend)._complete_action_class() @@ -1043,6 +1076,7 @@ def test_25_disco_storage_v_null(self): NB: this test is skipped if your backend does not support storage unit (yet :-) ) """ + self.skip_if_needed() backend = self.aux_make_backend() cls = type(backend) if cls.n_storage == 0: @@ -1087,6 +1121,7 @@ def test_26_copy(self): NB: this test is skipped if the backend cannot be copied """ + self.skip_if_needed() backend = self.aux_make_backend() if not backend._can_be_copied: with self.assertRaises(BackendError): diff --git a/grid2op/tests/helper_path_test.py b/grid2op/tests/helper_path_test.py index f3f1c0667..f1ad50281 100644 --- a/grid2op/tests/helper_path_test.py +++ b/grid2op/tests/helper_path_test.py @@ -14,8 +14,10 @@ import os import numpy as np from pathlib import Path - +from abc import ABC, abstractmethod +import inspect from grid2op.dtypes import dt_float +from grid2op.Backend import Backend test_dir = Path(__file__).parent.absolute() grid2op_dir = os.fspath(test_dir.parent.absolute()) @@ -54,3 +56,26 @@ def compare_vect(self, pred, true): res = dt_float(np.max(np.abs(pred - true))) <= self.tolvect res = res and dt_float(np.mean(np.abs(pred - true))) <= self.tolvect return res + + +class MakeBackend(ABC, HelperTests): + @abstractmethod + def make_backend(self, detailed_infos_for_cascading_failures=False) -> Backend: + pass + + def get_path(self) -> str: + raise NotImplementedError( + "This function should be implemented for the test suit you are developping" + ) + + def get_casefile(self) -> str: + raise NotImplementedError( + "This function should be implemented for the test suit you are developping" + ) + + def skip_if_needed(self) -> None: + if hasattr(self, "tests_skipped"): + nm_ = inspect.currentframe().f_back.f_code.co_name + if nm_ in self.tests_skipped: + self.skipTest('the test "{}" is skipped: it has been added to self.tests_skipped'.format(nm_)) + \ No newline at end of file diff --git a/grid2op/tests/test_env_diff_format.py b/grid2op/tests/test_env_diff_format.py new file mode 100644 index 000000000..213233f1e --- /dev/null +++ b/grid2op/tests/test_env_diff_format.py @@ -0,0 +1,54 @@ +# Copyright (c) 2023, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems. + +import os +from typing import Optional, Union +import unittest + +from grid2op.tests.helper_path_test import PATH_DATA_TEST +from grid2op.tests.aaa_test_backend_interface import AAATestBackendAPI +from grid2op.Backend import PandaPowerBackend +FILE_FORMAT = "json_custom" + + +class BackendDiffFormatTester(PandaPowerBackend): + def __init__(self, + detailed_infos_for_cascading_failures: bool = False, + lightsim2grid: bool = False, + dist_slack: bool = False, + max_iter: int = 10, + can_be_copied: bool = True, + with_numba: bool = False): + super().__init__(detailed_infos_for_cascading_failures, lightsim2grid, dist_slack, max_iter, can_be_copied, with_numba) + self.supported_grid_format = ("json_custom", ) + + # def load_grid(self, path: Union[os.PathLike, str], filename: Union[os.PathLike, str, None] = None) -> None: + # full_path = self.make_complete_path(path, filename) + # full_path = full_path.rstrip("_custom") + # return super().load_grid(full_path) + + +class TestBackendAPI_BackendDiffFormatTester(AAATestBackendAPI, unittest.TestCase): + def get_path(self): + return os.path.join(PATH_DATA_TEST, "5bus_fake_grid_format") + + def get_casefile(self): + return "grid.json_custom" # or `grid.xml` or any other format + + def make_backend(self, detailed_infos_for_cascading_failures=False): + # the function that will create your backend + # do not put "PandaPowerBackend" of course, but the class you coded as a backend ! + backend = BackendDiffFormatTester(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + assert FILE_FORMAT in backend.supported_grid_format, f"your backend does not recognize the '{FILE_FORMAT}' extension, grid2op will not work" + return backend + + def setUp(self): + self.tests_skipped = ("test_01load_grid", "test_22_islanded_grid_make_divergence") + return super().setUp() +if __name__ == "__main__": + unittest.main() From ff98512b8941e05b73f1f346f6667fb823bb0583 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 24 Oct 2023 12:12:36 +0200 Subject: [PATCH 42/46] adding script to test environment with grid having different file format --- .../data_test/5bus_fake_grid_format/config.py | 5 +-- grid2op/tests/test_env_diff_format.py | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/grid2op/data_test/5bus_fake_grid_format/config.py b/grid2op/data_test/5bus_fake_grid_format/config.py index 1ec901a06..08ed24882 100644 --- a/grid2op/data_test/5bus_fake_grid_format/config.py +++ b/grid2op/data_test/5bus_fake_grid_format/config.py @@ -1,7 +1,7 @@ from grid2op.Action import TopologyAction from grid2op.Reward import L2RPNReward from grid2op.Rules import DefaultRules -from grid2op.Chronics import Multifolder +from grid2op.Chronics import ChangeNothing from grid2op.Chronics import GridStateFromFileWithForecasts from grid2op.Backend import PandaPowerBackend @@ -11,8 +11,7 @@ "observation_class": None, "reward_class": L2RPNReward, "gamerules_class": DefaultRules, - "chronics_class": Multifolder, - "grid_value_class": GridStateFromFileWithForecasts, + "chronics_class": ChangeNothing, "volagecontroler_class": None, "thermal_limits": None, "names_chronics_to_grid": None, diff --git a/grid2op/tests/test_env_diff_format.py b/grid2op/tests/test_env_diff_format.py index 213233f1e..0db8e86ce 100644 --- a/grid2op/tests/test_env_diff_format.py +++ b/grid2op/tests/test_env_diff_format.py @@ -9,10 +9,13 @@ import os from typing import Optional, Union import unittest +import warnings from grid2op.tests.helper_path_test import PATH_DATA_TEST +import grid2op from grid2op.tests.aaa_test_backend_interface import AAATestBackendAPI from grid2op.Backend import PandaPowerBackend +from grid2op.Runner import Runner FILE_FORMAT = "json_custom" @@ -50,5 +53,46 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): def setUp(self): self.tests_skipped = ("test_01load_grid", "test_22_islanded_grid_make_divergence") return super().setUp() + + +class EnvTester_BackendDiffFormatTester(unittest.TestCase): + def setUp(self) -> None: + path_env = os.path.join(PATH_DATA_TEST, "5bus_fake_grid_format") + detailed_infos_for_cascading_failures = False + self.env = grid2op.make(path_env, + backend=BackendDiffFormatTester(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures), + _add_to_name=type(self).__name__ + ) + super().setUp() + + def tearDown(self) -> None: + self.env.close() + return super().tearDown() + + def test_can_make(self): + self.env.reset() + 1 + 1 + + def test_copy(self): + obs = self.env.reset() + env_cpy = self.env.copy() + obs_cpy = env_cpy.reset() + assert self.env.backend.supported_grid_format == ("json_custom", ) + assert env_cpy.backend.supported_grid_format == ("json_custom", ) + + def test_runner(self): + obs = self.env.reset() + env_cpy = self.env.copy() + runner = Runner(**self.env.get_params_for_runner()) + runner_cpy = Runner(**env_cpy.get_params_for_runner()) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + res = runner.run(nb_episode=1, max_iter=10) + res_cpy = runner_cpy.run(nb_episode=1, max_iter=10) + for el, el_cpy in zip(res[0], res_cpy[0]): + assert el == el_cpy, f"{el} vs {el_cpy}" + +# TODO test simulate, test simulate with copy etc.(but require an env with... simulate available) + if __name__ == "__main__": unittest.main() From 8e6fe9be8c1779f85b34c0fd9f9153c68ef83fa5 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 26 Oct 2023 11:18:35 +0200 Subject: [PATCH 43/46] should be ready for version 1.9.6 now --- CHANGELOG.rst | 4 +- grid2op/Backend/pandaPowerBackend.py | 2 +- grid2op/Exceptions/BackendExceptions.py | 27 ++++ grid2op/Exceptions/__init__.py | 112 +++++++------ grid2op/tests/aaa_test_backend_interface.py | 168 ++++++++++++++------ grid2op/tests/test_env_diff_format.py | 5 - 6 files changed, 210 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 28b8f13a5..28b8ddfa2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -31,7 +31,7 @@ Change Log - [???] "asynch" multienv - [???] properly model interconnecting powerlines -[1.9.6] - 2023-xx-yy +[1.9.6] - 2023-10-26 ---------------------- - [BREAKING] when a storage is connected alone on a bus, even if it produces / absorbs 0.0 MW it will raise a diverging powerflow error (previously the storage was automatically disconnected by @@ -64,6 +64,8 @@ Change Log - [IMPROVED] handling of environments without shunts - [IMPROVED] error messages when grid is not consistent - [IMPROVED] add the default `l2rpn_case14_sandbox` environment in all part of the docs (substituing `rte_case14_realistic` or nothing) +- [IMPROVED] imports on the `Exceptions` module +- [IMPROVED] pandapower backend raises `BackendError` when "diverging" [1.9.5] - 2023-09-18 --------------------- diff --git a/grid2op/Backend/pandaPowerBackend.py b/grid2op/Backend/pandaPowerBackend.py index fdbadd4d7..a2a296eac 100644 --- a/grid2op/Backend/pandaPowerBackend.py +++ b/grid2op/Backend/pandaPowerBackend.py @@ -1155,7 +1155,7 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]: # of the powerflow has not converged, results are Nan self._reset_all_nan() msg = exc_.__str__() - return False, DivergingPowerFlow(f'powerflow diverged with error :"{msg}"') + return False, BackendError(f'powerflow diverged with error :"{msg}"') def assert_grid_correct(self) -> None: """ diff --git a/grid2op/Exceptions/BackendExceptions.py b/grid2op/Exceptions/BackendExceptions.py index 6ad86df4e..d980de4ff 100644 --- a/grid2op/Exceptions/BackendExceptions.py +++ b/grid2op/Exceptions/BackendExceptions.py @@ -16,3 +16,30 @@ class BackendError(Grid2OpException): """ pass + + +class DivergingPowerflow(BackendError): + """Specific error that should be raised when the powerflow diverges + """ + pass + + +class IslandedGrid(BackendError): + """Specific error when then backend "fails" because of an islanded grid""" + pass + + +class IsolatedElement(IslandedGrid): + """Specific error that should be raised when a element is alone on a bus (islanded grid when only one element is islanded) + """ + pass + + +class DisconnectedLoad(BackendError): + """Specific error raised by the backend when a load is disconnected""" + pass + + +class DisconnectedGenerator(BackendError): + """Specific error raised by the backend when a generator is disconnected""" + pass diff --git a/grid2op/Exceptions/__init__.py b/grid2op/Exceptions/__init__.py index a04b50166..2fec308a1 100644 --- a/grid2op/Exceptions/__init__.py +++ b/grid2op/Exceptions/__init__.py @@ -48,6 +48,11 @@ "ChronicsNotFoundError", "InsufficientData", "BackendError", + "DivergingPowerflow", + "IslandedGrid", + "IsolatedElement", + "DisconnectedLoad", + "DisconnectedGenerator", "PlotError", "OpponentError", "UsedRunnerError", @@ -59,63 +64,70 @@ from grid2op.Exceptions.Grid2OpException import Grid2OpException -from grid2op.Exceptions.EnvExceptions import EnvError -from grid2op.Exceptions.EnvExceptions import IncorrectNumberOfLoads -from grid2op.Exceptions.EnvExceptions import IncorrectNumberOfGenerators -from grid2op.Exceptions.EnvExceptions import IncorrectNumberOfLines -from grid2op.Exceptions.EnvExceptions import IncorrectNumberOfSubstation -from grid2op.Exceptions.EnvExceptions import IncorrectNumberOfStorages -from grid2op.Exceptions.EnvExceptions import IncorrectNumberOfElements -from grid2op.Exceptions.EnvExceptions import IncorrectPositionOfLoads -from grid2op.Exceptions.EnvExceptions import IncorrectPositionOfGenerators -from grid2op.Exceptions.EnvExceptions import IncorrectPositionOfLines -from grid2op.Exceptions.EnvExceptions import IncorrectPositionOfStorages -from grid2op.Exceptions.EnvExceptions import UnknownEnv -from grid2op.Exceptions.EnvExceptions import MultiEnvException +from grid2op.Exceptions.EnvExceptions import (EnvError, + IncorrectNumberOfLoads, + IncorrectNumberOfGenerators, + IncorrectNumberOfLines, + IncorrectNumberOfSubstation, + IncorrectNumberOfStorages, + IncorrectNumberOfElements, + IncorrectPositionOfLoads, + IncorrectPositionOfGenerators, + IncorrectPositionOfLines, + IncorrectPositionOfStorages, + UnknownEnv, + MultiEnvException) -from grid2op.Exceptions.IllegalActionExceptions import IllegalAction -from grid2op.Exceptions.IllegalActionExceptions import OnProduction -from grid2op.Exceptions.IllegalActionExceptions import VSetpointModified -from grid2op.Exceptions.IllegalActionExceptions import ActiveSetPointAbovePmax -from grid2op.Exceptions.IllegalActionExceptions import ActiveSetPointBelowPmin -from grid2op.Exceptions.IllegalActionExceptions import OnLoad -from grid2op.Exceptions.IllegalActionExceptions import OnLines -from grid2op.Exceptions.IllegalActionExceptions import InvalidReconnection -from grid2op.Exceptions.IllegalActionExceptions import ( - UnitCommitorRedispachingNotAvailable, -) +from grid2op.Exceptions.IllegalActionExceptions import (IllegalAction, + OnProduction, + VSetpointModified, + ActiveSetPointAbovePmax, + ActiveSetPointBelowPmin, + OnLoad, + OnLines, + InvalidReconnection, + UnitCommitorRedispachingNotAvailable, + ) -from grid2op.Exceptions.AmbiguousActionExceptions import NotEnoughGenerators -from grid2op.Exceptions.AmbiguousActionExceptions import GeneratorTurnedOffTooSoon -from grid2op.Exceptions.AmbiguousActionExceptions import GeneratorTurnedOnTooSoon -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidRedispatching -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidBusStatus -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidNumberOfObjectEnds -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidNumberOfLines -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidNumberOfGenerators -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidNumberOfLoads -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidCurtailment -from grid2op.Exceptions.AmbiguousActionExceptions import UnrecognizedAction -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidLineStatus -from grid2op.Exceptions.AmbiguousActionExceptions import InvalidStorage -from grid2op.Exceptions.AmbiguousActionExceptions import AmbiguousAction -from grid2op.Exceptions.AmbiguousActionExceptions import NonFiniteElement -from grid2op.Exceptions.AmbiguousActionExceptions import AmbiguousActionRaiseAlert +from grid2op.Exceptions.AmbiguousActionExceptions import (NotEnoughGenerators, + GeneratorTurnedOffTooSoon, + GeneratorTurnedOnTooSoon, + InvalidRedispatching, + InvalidBusStatus, + InvalidNumberOfObjectEnds, + InvalidNumberOfLines, + InvalidNumberOfGenerators, + InvalidNumberOfLoads, + InvalidCurtailment, + UnrecognizedAction, + InvalidLineStatus, + InvalidStorage, + AmbiguousAction, + NonFiniteElement, + AmbiguousActionRaiseAlert) from grid2op.Exceptions.PowerflowExceptions import DivergingPowerFlow -from grid2op.Exceptions.ObservationExceptions import BaseObservationError -from grid2op.Exceptions.ObservationExceptions import NoForecastAvailable -from grid2op.Exceptions.ObservationExceptions import SimulateError -from grid2op.Exceptions.ObservationExceptions import SimulateUsedTooMuchThisStep -from grid2op.Exceptions.ObservationExceptions import SimulateUsedTooMuchThisEpisode +from grid2op.Exceptions.ObservationExceptions import (BaseObservationError, + NoForecastAvailable, + SimulateError, + SimulateUsedTooMuchThisStep, + SimulateUsedTooMuchThisEpisode) -from grid2op.Exceptions.ChronicsExceptions import ChronicsError -from grid2op.Exceptions.ChronicsExceptions import ChronicsNotFoundError -from grid2op.Exceptions.ChronicsExceptions import InsufficientData -from grid2op.Exceptions.handlers_exceptions import HandlerError +from grid2op.Exceptions.ChronicsExceptions import (ChronicsError, + ChronicsNotFoundError, + InsufficientData, + ) +from grid2op.Exceptions.handlers_exceptions import (HandlerError, + ) -from grid2op.Exceptions.BackendExceptions import BackendError +from grid2op.Exceptions.BackendExceptions import (BackendError, + DivergingPowerflow, + IslandedGrid, + IsolatedElement, + DisconnectedLoad, + DisconnectedGenerator, + ) from grid2op.Exceptions.PlotExceptions import PlotError diff --git a/grid2op/tests/aaa_test_backend_interface.py b/grid2op/tests/aaa_test_backend_interface.py index 02467f690..b320b0d44 100644 --- a/grid2op/tests/aaa_test_backend_interface.py +++ b/grid2op/tests/aaa_test_backend_interface.py @@ -10,7 +10,7 @@ import numpy as np import warnings from grid2op.tests.helper_path_test import HelperTests, MakeBackend, PATH_DATA -from grid2op.Exceptions import BackendError +from grid2op.Exceptions import BackendError, Grid2OpException class AAATestBackendAPI(MakeBackend): @@ -599,13 +599,16 @@ def test_14change_topology(self): cls = type(backend) res = backend.runpf(is_dc=False) - p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() - assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for p (creation or suppression of active). Check the handling of the slack bus(se) maybe ?" - assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for q (creation or suppression of reactive). Check the handling of the slack bus(se) maybe ?" - assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for p (creation or suppression of active). Check the handling of the slack bus(se) maybe ?" - assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for q (creation or suppression of reactive). Check the handling of the slack bus(se) maybe ?" - assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" - + if not cls.shunts_data_available: + warnings.warn(f"{type(self.__name__)} test_14change_topology: This test is not performed in depth as your backend does not support shunts") + else: + p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() + assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for p (creation or suppression of active). Check the handling of the slack bus(se) maybe ?" + assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for q (creation or suppression of reactive). Check the handling of the slack bus(se) maybe ?" + assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for p (creation or suppression of active). Check the handling of the slack bus(se) maybe ?" + assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): kirchoff laws are not met for q (creation or suppression of reactive). Check the handling of the slack bus(se) maybe ?" + assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (no modif): some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" + p_or, q_or, v_or, a_or = backend.lines_or_info() sub_id = 0 @@ -617,12 +620,17 @@ def test_14change_topology(self): backend.apply_action(bk_act) # everything on busbar 2 at sub 0 res = backend.runpf(is_dc=False) assert res[0], "Your powerflow has diverged after the loading of the file, which should not happen" - p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() - assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for p (creation or suppression of active)." - assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for q (creation or suppression of reactive)." - assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for p (creation or suppression of active)." - assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for q (creation or suppression of reactive)." - assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow: some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" + + if not cls.shunts_data_available: + warnings.warn(f"{type(self.__name__)} test_14change_topology: This test is not performed in depth as your backend does not support shunts") + else: + p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() + assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for p (creation or suppression of active)." + assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for q (creation or suppression of reactive)." + assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for p (creation or suppression of active)." + assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with no impact): kirchoff laws are not met for q (creation or suppression of reactive)." + assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow: some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" + p_after_or, q_after_or, v_after_or, a_after_or = backend.lines_or_info() assert np.allclose(p_after_or, p_or), f"The p_or flow changed while the topology action is supposed to have no impact, check the `apply_action` for topology" assert np.allclose(q_after_or, q_or), f"The q_or flow changed while the topology action is supposed to do nothing, check the `apply_action` for topology" @@ -639,12 +647,16 @@ def test_14change_topology(self): backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=False) assert res[0], "Your powerflow has diverged after a topology action (but should not). Check `apply_action` for topology" - p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() - assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for p (creation or suppression of active)." - assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for q (creation or suppression of reactive)." - assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for p (creation or suppression of active)." - assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for q (creation or suppression of reactive)." - assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow: some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" + if not cls.shunts_data_available: + warnings.warn(f"{type(self.__name__)} test_14change_topology: This test is not performed in depth as your backend does not support shunts") + else: + p_subs, q_subs, p_bus, q_bus, diff_v_bus = backend.check_kirchoff() + assert np.allclose(p_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for p (creation or suppression of active)." + assert np.allclose(q_subs, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for q (creation or suppression of reactive)." + assert np.allclose(p_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for p (creation or suppression of active)." + assert np.allclose(q_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow (modif with a real impact): kirchoff laws are not met for q (creation or suppression of reactive)." + assert np.allclose(diff_v_bus, 0., atol=3 * self.tol_one), "there are some discrepency in the backend after a powerflow: some nodes have two different voltages. Check the accessor for voltage in all the `***_info()` (*eg* `loads_info()`)" + p_after_or, q_after_or, v_after_or, a_after_or = backend.lines_or_info() assert not np.allclose(p_after_or, p_or), f"The p_or flow doesn't change while the topology action is supposed to have a real impact, check the `apply_action` for topology" assert not np.allclose(q_after_or, q_or), f"The q_or flow doesn't change while the topology action is supposed to have a real impact, check the `apply_action` for topology" @@ -731,9 +743,13 @@ def test_16_isolated_load_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=False) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated loads in AC." + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of isolated loads in AC." assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" - + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to isolated shunt) should preferably inherit from BackendError") + backend.reset(self.get_path(), self.get_casefile()) # a load alone on a bus action = type(backend)._complete_action_class() @@ -742,9 +758,13 @@ def test_16_isolated_load_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=True) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated loads in DC." + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of isolated loads in DC." assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" - + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to isolated shunt) should preferably inherit from BackendError") + def test_17_isolated_gen_make_divergence(self): """Tests that an isolated generator will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] @@ -766,9 +786,13 @@ def test_17_isolated_gen_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=False) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated gen." + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of isolated gen." assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" - + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to isolated shunt) should preferably inherit from BackendError") + backend.reset(self.get_path(), self.get_casefile()) # disconnect a gen action = type(backend)._complete_action_class() @@ -777,9 +801,13 @@ def test_17_isolated_gen_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=True) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated gen." + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of isolated gen." assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" - + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to isolated shunt) should preferably inherit from BackendError") + def test_18_isolated_shunt_make_divergence(self): """Tests test that an isolated shunt will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] @@ -807,9 +835,13 @@ def test_18_isolated_shunt_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=False) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated shunt." + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of isolated shunt." assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" - + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to isolated shunt) should preferably inherit from BackendError") + backend.reset(self.get_path(), self.get_casefile()) # make a shunt alone on a bus action = type(backend)._complete_action_class() @@ -818,8 +850,12 @@ def test_18_isolated_shunt_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=True) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated shunt." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of isolated shunt in DC." + assert res[1] is not None, "When your backend stops, we expect it throws an exception (second return value)" + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend returns `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to isolated shunt) should preferably inherit from BackendError") def test_19_isolated_storage_make_divergence(self): """Teststest that an isolated storage unit will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] @@ -845,9 +881,13 @@ def test_19_isolated_storage_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=False) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated storage unit." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" - + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of isolated storage units in AC." + assert res[1] is not None, "When your backend stops, we expect it throws an exception (second return value)" + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to isolated storage units) should preferably inherit from BackendError") + backend.reset(self.get_path(), self.get_casefile()) action = type(backend)._complete_action_class() action.update({"set_bus": {"storages_id": [(0, 2)]}}) @@ -855,9 +895,13 @@ def test_19_isolated_storage_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=True) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of isolated storage unit." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" - + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of isolated storage unit." + assert res[1] is not None, "When your backend stops, we expect it throws an exception (second return value)" + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to isolated storage units) should preferably inherit from BackendError") + def test_20_disconnected_load_make_divergence(self): """Tests that a disconnected load unit will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] @@ -880,8 +924,12 @@ def test_20_disconnected_load_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=False) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of disconnected load in AC." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of disconnected load in AC." + assert res[1] is not None, "When your backend stops, we expect it throws an exception (second return value)" + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to disconnected load) should preferably inherit from BackendError") backend.reset(self.get_path(), self.get_casefile()) # a load alone on a bus @@ -891,9 +939,13 @@ def test_20_disconnected_load_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=True) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of disconnected load in DC." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" - + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of disconnected load in DC." + assert res[1] is not None, "When your backend stops, we expect it throws an exception (second return value)" + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to disconnected load) should preferably inherit from BackendError") + def test_21_disconnected_gen_make_divergence(self): """Tests that a disconnected generator will make the method `run_pf` "diverge" (in AC and DC) [behaviour might change in the future] @@ -916,8 +968,12 @@ def test_21_disconnected_gen_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=False) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of disconnected gen in AC." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of disconnected gen in AC." + assert res[1] is not None, "When your backend stops, we expect it throws an exception (second return value)" + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to disconnected gen) should preferably inherit from BackendError") backend.reset(self.get_path(), self.get_casefile()) # a disconnected generator @@ -927,8 +983,12 @@ def test_21_disconnected_gen_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=True) - assert not res[0], "It is expected (at time of writing) that your backend 'diverges' in case of disconnected gen in DC." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + assert not res[0], "It is expected (at time of writing) that your backend returns `False` in case of disconnected gen in DC." + assert res[1] is not None, "When your backend stops, we expect it throws an exception (second return value)" + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to disconnected gen) should preferably inherit from BackendError") def test_22_islanded_grid_make_divergence(self): """Tests that when the grid is split in two different "sub_grid" it makes the runpf diverge both in AC and DC @@ -956,8 +1016,11 @@ def test_22_islanded_grid_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=False) - assert not res[0], "It is expected that your backend 'diverges' in case of non connected grid in AC." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + assert not res[0], "It is expected that your backend return `False` in case of non connected grid in AC." + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to non connected grid) should preferably inherit from BackendError") backend.reset(self.get_path(), self.get_casefile()) # a non connected grid @@ -969,8 +1032,11 @@ def test_22_islanded_grid_make_divergence(self): bk_act += action backend.apply_action(bk_act) # mix of bus 1 and 2 on substation 1 res = backend.runpf(is_dc=True) - assert not res[0], "It is expected that your backend 'diverges' in case of non connected grid in DC." - assert res[1] is not None, "When your backend diverges, we expect it throws an exception (second return value)" + assert not res[0], "It is expected that your backend throws an exception inheriting from BackendError in case of non connected grid in DC." + error = res[1] + assert isinstance(error, Grid2OpException), "When your backend return `False`, we expect it throws an exception inheriting from Grid2OpException (second return value)" + if not isinstance(error, BackendError): + warnings.warn("The error returned by your backend when it stopped (due to non connected grid) should preferably inherit from BackendError") def test_23_disco_line_v_null(self): """Tests that disconnected elements shunt have v = 0. (and not nan or something) diff --git a/grid2op/tests/test_env_diff_format.py b/grid2op/tests/test_env_diff_format.py index 0db8e86ce..ea1f7e3f3 100644 --- a/grid2op/tests/test_env_diff_format.py +++ b/grid2op/tests/test_env_diff_format.py @@ -29,11 +29,6 @@ def __init__(self, with_numba: bool = False): super().__init__(detailed_infos_for_cascading_failures, lightsim2grid, dist_slack, max_iter, can_be_copied, with_numba) self.supported_grid_format = ("json_custom", ) - - # def load_grid(self, path: Union[os.PathLike, str], filename: Union[os.PathLike, str, None] = None) -> None: - # full_path = self.make_complete_path(path, filename) - # full_path = full_path.rstrip("_custom") - # return super().load_grid(full_path) class TestBackendAPI_BackendDiffFormatTester(AAATestBackendAPI, unittest.TestCase): From d60b2575320f2302a1ea588bfdf1f1c9b04d4100 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 26 Oct 2023 11:47:11 +0200 Subject: [PATCH 44/46] fixing broken tests: pandapower Exception was catched - legacy behaviour --- grid2op/tests/test_Environment.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grid2op/tests/test_Environment.py b/grid2op/tests/test_Environment.py index 639de2e7a..1cd2eee78 100644 --- a/grid2op/tests/test_Environment.py +++ b/grid2op/tests/test_Environment.py @@ -440,7 +440,7 @@ def test_copy(self): assert obs_after.minute_of_hour == 0 -class BaseTestResetOk(unittest.TestCase): +class BaseTestResetOk: """ This function test that the behaviour of "step" is the one we want: it does nothing if an action if ambiguous or illegal @@ -492,7 +492,7 @@ def test_reset_after_blackout(self): obs, reward, done, info = self.env.step(act) # at this stage there is a cascading failure assert len(info["exception"]) - assert isinstance(info["exception"][0], DivergingPowerFlow) + assert isinstance(info["exception"][0], BackendError) # reset the grid obs = self.env.reset() assert np.all(obs.topo_vect == 1) @@ -663,7 +663,7 @@ def test_change_reconnect(self): assert obs.topo_vect[line_ex_topo] == 2, "Line ex should be on bus 2" -class BaseTestResetAfterCascadingFailure(unittest.TestCase): +class BaseTestResetAfterCascadingFailure: """ Fake a cascading failure, do a reset of an env, check that it can be loaded @@ -714,7 +714,7 @@ class TestResetAfterCascadingFailure(BaseTestResetAfterCascadingFailure, unittes pass -class BaseTestCascadingFailure(unittest.TestCase): +class BaseTestCascadingFailure: """ There has been a bug preventing to reload an environment if the previous one ended with a cascading failure. It check that here. From bbcf404dd5d61d028bd4528590da09fee63bf548 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 26 Oct 2023 13:52:31 +0200 Subject: [PATCH 45/46] fixing broken tests: pandapower Exception was catched - legacy behaviour --- grid2op/tests/test_Environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grid2op/tests/test_Environment.py b/grid2op/tests/test_Environment.py index 1cd2eee78..ac1e96df5 100644 --- a/grid2op/tests/test_Environment.py +++ b/grid2op/tests/test_Environment.py @@ -530,7 +530,7 @@ def test_reset_after_blackout_withdetailed_info(self, env=None): obs, reward, done, info = env.step(env.action_space()) # at this stage there is a cascading failure assert len(info["exception"]) - assert isinstance(info["exception"][0], DivergingPowerFlow) + assert isinstance(info["exception"][0], BackendError) assert "detailed_infos_for_cascading_failures" in info assert len(info["detailed_infos_for_cascading_failures"]) # reset the grid From 5619beec9da1a27870646942f1832b731863bfed Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 26 Oct 2023 15:22:21 +0200 Subject: [PATCH 46/46] fixing small issues spotted in the PR rte-france#547 --- grid2op/Chronics/gridStateFromFile.py | 2 -- grid2op/multi_agent/my_test.py | 16 ---------------- 2 files changed, 18 deletions(-) delete mode 100644 grid2op/multi_agent/my_test.py diff --git a/grid2op/Chronics/gridStateFromFile.py b/grid2op/Chronics/gridStateFromFile.py index 19c7f3f83..1cc53a725 100644 --- a/grid2op/Chronics/gridStateFromFile.py +++ b/grid2op/Chronics/gridStateFromFile.py @@ -234,8 +234,6 @@ def _assert_correct(self, dict_convert, order_backend): lend_dict_values = len(vals) if len_dict_keys != len_backend: - import pdb - pdb.set_trace() err_msg = "Conversion mismatch between backend data {} elements and converter data {} (keys)" raise IncorrectNumberOfElements(err_msg.format(len_backend, len_dict_keys)) if lend_dict_values != len_backend: diff --git a/grid2op/multi_agent/my_test.py b/grid2op/multi_agent/my_test.py deleted file mode 100644 index 3c48e88ec..000000000 --- a/grid2op/multi_agent/my_test.py +++ /dev/null @@ -1,16 +0,0 @@ -import grid2op -from grid2op.multi_agent.multiAgentEnv import MultiAgentEnv - -env = grid2op.make("l2rpn_case14_sandbox", test = True) -action_domains = { - 'agent_0' : [0,1,2,3, 4], - 'agent_1' : [5,6,7,8,9,10,11,12,13] - } -observation_domains = { - 'agent_0' : action_domains['agent_1'], - 'agent_1' : action_domains['agent_0'] - } - -# run redispatch agent on one scenario for 100 timesteps -ma_env = MultiAgentEnv(env, observation_domains, action_domains) -