Skip to content

Commit 74c6948

Browse files
authored
Merge pull request #612 from BDonnot/master
fix example notebook (how to make a backend)
2 parents 181383a + d615fa2 commit 74c6948

File tree

13 files changed

+187
-61
lines changed

13 files changed

+187
-61
lines changed

CHANGELOG.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ Change Log
3131
- [???] "asynch" multienv
3232
- [???] properly model interconnecting powerlines
3333

34+
[1.10.3] - 2024-xx-yy
35+
-------------------------
36+
- TODO A number of max buses per sub
37+
- TODO Automatic "experimental_read_from_local_dir"
38+
- TODO Notebook for stable baselines
39+
- TODO in the reset options: datetime start and max number of steps
40+
3441
[1.10.2] - 2024-05-27
3542
-------------------------
3643
- [BREAKING] the `runner.run_one_episode` now returns an extra first argument:

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
author = 'Benjamin Donnot'
2323

2424
# The full version, including alpha/beta/rc tags
25-
release = '1.10.2'
25+
release = '1.10.3.dev0'
2626
version = '1.10'
2727

2828

examples/backend_integration/Step0_make_env.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@
4141
from grid2op.Opponent import BaseOpponent
4242

4343

44+
class PandaPowerBackendNoShunt(PandaPowerBackend):
45+
shunts_data_available = False
46+
47+
48+
def create_action(env, backend, action):
49+
"""this is done internally by grid2op.
50+
51+
The idea is to generate a "backend action" (which again is provided by grid2op)
52+
easily
53+
"""
54+
# bk_act = env._backend_action_class()
55+
# bk_act += action # action for pandapower backend
56+
# bk_act.reorder(env.backend._load_sr2tg,
57+
# env.backend._gen_sr2tg,
58+
# env.backend._topo_sr2tg,
59+
# env.backend._storage_sr2tg,
60+
# env.backend._shunt_sr2tg)
61+
bk_act = type(backend).my_bk_act_class()
62+
bk_act += action
63+
return bk_act
64+
65+
4466
def make_env_for_backend(env_name, backend_class):
4567
# env_name: one of:
4668
# - rte_case5_example: the grid in the documentation (completely fake grid)
@@ -65,8 +87,9 @@ def make_env_for_backend(env_name, backend_class):
6587
action_class=CompleteAction, # we tell grid2op we will manipulate all type of actions
6688
reward_class=ConstantReward, # we don't have yet redispatching data, that might be use by the reward
6789
opponent_class=BaseOpponent, # we deactivate the opponents
90+
# backend=backend_class()
6891
backend=BackendConverter(source_backend_class=backend_class,
69-
target_backend_class=PandaPowerBackend,
92+
target_backend_class=PandaPowerBackendNoShunt,
7093
use_target_backend_name=True)
7194
)
7295
obs = env.reset()

examples/backend_integration/Step1_loading.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535

3636
class CustomBackend_Step1(Backend):
37+
shunts_data_available = False
3738
def load_grid(self,
3839
path : Union[os.PathLike, str],
3940
filename : Optional[Union[os.PathLike, str]]=None) -> None:
@@ -170,8 +171,8 @@ def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
170171
# storage_pos_topo_vect
171172

172173
# for example
173-
print(type(backend).name_load)
174-
print(type(backend).load_to_subid)
175-
print(type(backend).load_to_sub_pos)
176-
print(type(backend).load_pos_topo_vect)
174+
print(f"Name of the loads, seen in grid2op: {type(backend).name_load}")
175+
print(f"Id of substation, for each load: {type(backend).load_to_subid}")
176+
print(f"Position in the substation topology vector, for each load: {type(backend).load_to_sub_pos}")
177+
print(f"Position in the global topology vector, for each load: {type(backend).load_pos_topo_vect}")
177178

examples/backend_integration/Step2_modify_load.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def loads_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]:
6969
if __name__ == "__main__":
7070
import grid2op
7171
import os
72-
from Step0_make_env import make_env_for_backend
72+
from Step0_make_env import make_env_for_backend, create_action
7373

7474
path_grid2op = grid2op.__file__
7575
path_data_test = os.path.join(os.path.split(path_grid2op)[0], "data")
@@ -105,8 +105,7 @@ def loads_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]:
105105
# have the proper size)
106106

107107
# this is technical to grid2op (done internally)
108-
bk_act = env._backend_action_class()
109-
bk_act += action
108+
bk_act = create_action(env, backend, action)
110109
#############
111110

112111
# this is what the backend receive:

examples/backend_integration/Step3_modify_gen.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def generators_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]:
6767
if __name__ == "__main__":
6868
import grid2op
6969
import os
70-
from Step0_make_env import make_env_for_backend
70+
from Step0_make_env import make_env_for_backend, create_action
7171

7272
path_grid2op = grid2op.__file__
7373
path_data_test = os.path.join(os.path.split(path_grid2op)[0], "data")
@@ -103,8 +103,7 @@ def generators_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray]:
103103
# have the proper size)
104104

105105
# this is technical to grid2op (done internally)
106-
bk_act = env._backend_action_class()
107-
bk_act += action
106+
bk_act = create_action(env, backend, action)
108107
#############
109108

110109
# this is what the backend receive:

examples/backend_integration/Step4_modify_line_status.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
178178
if __name__ == "__main__":
179179
import grid2op
180180
import os
181-
from Step0_make_env import make_env_for_backend
181+
from Step0_make_env import make_env_for_backend, create_action
182182

183183
path_grid2op = grid2op.__file__
184184
path_data_test = os.path.join(os.path.split(path_grid2op)[0], "data")
@@ -205,8 +205,7 @@ def lines_ex_info(self)-> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
205205
action = env.action_space({"set_line_status": [(0, -1)]})
206206

207207
# this is technical to grid2op
208-
bk_act = env._backend_action_class()
209-
bk_act += action
208+
bk_act = create_action(env, backend, action)
210209
#############
211210

212211
# this is what the backend receive:

examples/backend_integration/Step5_modify_topology.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def _aux_change_bus_or_disconnect(self, new_bus, dt, key, el_id):
5858
# are either 1 or 2)
5959
def apply_action(self, backendAction: Union["grid2op.Action._backendAction._BackendAction", None]) -> None:
6060
# the following few lines are highly recommended
61-
if action is None:
61+
if backendAction is None:
6262
return
6363

6464
# loads and generators are modified in the previous script
@@ -173,12 +173,12 @@ def get_topo_vect(self) -> np.ndarray:
173173
if __name__ == "__main__":
174174
import grid2op
175175
import os
176-
from Step0_make_env import make_env_for_backend
176+
from Step0_make_env import make_env_for_backend, create_action
177177

178178
path_grid2op = grid2op.__file__
179179
path_data_test = os.path.join(os.path.split(path_grid2op)[0], "data")
180180

181-
env_name = "l2rpn_wcci_2022_dev"
181+
env_name = "rte_case5_example"
182182
# one of:
183183
# - rte_case5_example: the grid in the documentation (completely fake grid)
184184
# - l2rpn_case14_sandbox: inspired from IEEE 14
@@ -206,6 +206,7 @@ def get_topo_vect(self) -> np.ndarray:
206206
sub_id = 1
207207
local_topo = (1, 2, 1, 2, 1, 2)
208208
elif env_name == "l2rpn_wcci_2022_dev":
209+
raise RuntimeError("Storage units are not handled by the example backend, and there are some on the grid.")
209210
sub_id = 3
210211
local_topo = (1, 2, 1, 2, 1)
211212
else:
@@ -214,8 +215,7 @@ def get_topo_vect(self) -> np.ndarray:
214215
#############################
215216

216217
# this is technical to grid2op
217-
bk_act = env._backend_action_class()
218-
bk_act += action
218+
bk_act = create_action(env, backend, action)
219219
####################################
220220

221221
# this is what the backend receive:

examples/backend_integration/Step6_integration.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
interacts with it.
1313
1414
"""
15-
15+
from tqdm import tqdm
1616
from Step5_modify_topology import CustomBackend_Minimal
1717

1818

@@ -60,42 +60,48 @@
6060
########### First "test" perform nothing and see what it gives
6161
done = False
6262
nb_step = 0
63-
while True:
64-
obs, reward, done, info = env.step(env.action_space())
65-
if done:
66-
break
67-
nb_step += 1
63+
with tqdm() as pbar:
64+
while True:
65+
obs, reward, done, info = env.step(env.action_space())
66+
if done:
67+
break
68+
nb_step += 1
69+
pbar.update()
6870
print(f"{nb_step} steps have been made with your backend with do nothing")
6971

7072
########## Second "test" perform random actions every now and then
7173
env.seed(0)
7274
obs = env.reset()
7375
done = False
7476
nb_step = 0
75-
while True:
76-
if nb_step % 10 == 9:
77-
# do a randome action sometime
78-
act = env.action_space.sample()
79-
else:
80-
# do nothing most of the time
81-
act = env.action_space()
82-
obs, reward, done, info = env.step(act)
83-
if done:
84-
break
85-
nb_step += 1
86-
print(f"{nb_step} steps have been made with your backend with random actions")
77+
with tqdm() as pbar:
78+
while True:
79+
if nb_step % 10 == 9:
80+
# do a randome action sometime
81+
act = env.action_space.sample()
82+
else:
83+
# do nothing most of the time
84+
act = env.action_space()
85+
obs, reward, done, info = env.step(act)
86+
if done:
87+
break
88+
nb_step += 1
89+
pbar.update()
90+
print(f"{nb_step} steps have been made with your backend with some random actions")
8791

8892
########### Third "test" using an "agent" that "does smart actions" (greedy agent)
8993
done = False
9094
nb_step = 0
9195
obs = env.reset()
9296
reward = 0.
9397
agent = RecoPowerlineAgent(env.action_space)
94-
while True:
95-
act = agent.act(obs, reward)
96-
obs, reward, done, info = env.step(act)
97-
if done:
98-
break
99-
nb_step += 1
98+
with tqdm() as pbar:
99+
while True:
100+
act = agent.act(obs, reward)
101+
obs, reward, done, info = env.step(act)
102+
if done:
103+
break
104+
nb_step += 1
105+
pbar.update()
100106
print(f"{nb_step} steps have been made with the greedy agent")
101107

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright (c) 2019-2020, RTE (https://www.rte-france.com)
2+
# See AUTHORS.txt
3+
# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
4+
# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
5+
# you can obtain one at http://mozilla.org/MPL/2.0/.
6+
# SPDX-License-Identifier: MPL-2.0
7+
# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.
8+
9+
"""
10+
This script provides a way to run the tests performed by grid2Op for the backend.
11+
12+
These tests are not 100% complete (some things might not be tested and are tested somewhere else)
13+
but they cover a big part of what the backend is expected to do.
14+
15+
YOU NEED TO INSTALL GRID2OP FROM THE GITHUB REPO FOR THIS TO WORK !
16+
To do that, simply:
17+
18+
1) clone grid2op repo
19+
2) cd there
20+
3) run `pip install -e .`
21+
22+
(do this in a venv preferably)
23+
"""
24+
25+
import unittest
26+
import warnings
27+
28+
# first the backend class (for the example here)
29+
from Step5_modify_topology import CustomBackend_Minimal
30+
31+
# then some required things
32+
from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST
33+
from grid2op.tests.helper_path_test import HelperTests
34+
PATH_DATA_TEST_INIT = PATH_DATA_TEST
35+
PATH_DATA_TEST = PATH_DATA_TEST_PP
36+
37+
# then all the tests that can be automatically performed
38+
from grid2op.tests.BaseBackendTest import BaseTestNames, BaseTestLoadingCase, BaseTestLoadingBackendFunc
39+
from grid2op.tests.BaseBackendTest import BaseTestTopoAction, BaseTestEnvPerformsCorrectCascadingFailures
40+
from grid2op.tests.BaseBackendTest import BaseTestChangeBusAffectRightBus, BaseTestShuntAction
41+
from grid2op.tests.BaseBackendTest import BaseTestResetEqualsLoadGrid, BaseTestVoltageOWhenDisco, BaseTestChangeBusSlack
42+
from grid2op.tests.BaseBackendTest import BaseIssuesTest, BaseStatusActions
43+
from grid2op.tests.test_Environment import (TestLoadingBackendPandaPower as BaseTestLoadingBackendPandaPower,
44+
TestResetOk as BaseTestResetOk)
45+
from grid2op.tests.test_Environment import (TestResetAfterCascadingFailure as TestResetAfterCascadingFailure,
46+
TestCascadingFailure as BaseTestCascadingFailure)
47+
from grid2op.tests.BaseRedispTest import BaseTestRedispatch, BaseTestRedispatchChangeNothingEnvironment
48+
from grid2op.tests.BaseRedispTest import BaseTestRedispTooLowHigh, BaseTestDispatchRampingIllegalETC
49+
from grid2op.tests.BaseRedispTest import BaseTestLoadingAcceptAlmostZeroSumRedisp
50+
51+
# then still some glue code, mainly for the names of the time series
52+
from grid2op.Converter import BackendConverter
53+
from grid2op.Backend import PandaPowerBackend
54+
55+
# our backend does not read the names from the grid, so this test is not relevant
56+
# class TestNames(HelperTests, BaseTestNames):
57+
# def make_backend(self, detailed_infos_for_cascading_failures=False):
58+
# with warnings.catch_warnings():
59+
# warnings.filterwarnings("ignore")
60+
# bk = BackendConverter(source_backend_class=CustomBackend_Minimal,
61+
# target_backend_class=PandaPowerBackend,
62+
# use_target_backend_name=True,
63+
# detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures)
64+
# return bk
65+
66+
# def get_path(self):
67+
# return PATH_DATA_TEST_INIT
68+
69+
class TestLoadingCase(HelperTests, BaseTestLoadingCase):
70+
def make_backend(self, detailed_infos_for_cascading_failures=False):
71+
with warnings.catch_warnings():
72+
warnings.filterwarnings("ignore")
73+
bk = BackendConverter(source_backend_class=CustomBackend_Minimal,
74+
target_backend_class=PandaPowerBackend,
75+
use_target_backend_name=True,
76+
detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures)
77+
return bk
78+
79+
def get_path(self):
80+
return PATH_DATA_TEST
81+
82+
def get_casefile(self):
83+
return "test_case14.json"
84+
85+
86+
if __name__ == "__main__":
87+
unittest.main()

0 commit comments

Comments
 (0)