Skip to content

Commit

Permalink
Merge pull request #501 from rte-france/dev_1.9.2
Browse files Browse the repository at this point in the history
Dev 1.9.2
  • Loading branch information
BDonnot committed Jul 26, 2023
2 parents b9969fd + bed9609 commit be628ce
Show file tree
Hide file tree
Showing 131 changed files with 5,120 additions and 1,596 deletions.
27 changes: 27 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,33 @@ Change Log
- [???] "asynch" multienv
- [???] properly model interconnecting powerlines

[1.9.2] - 2023-07-26
---------------------
- [BREAKING] rename with filename starting with lowercase all the files in the "`Backend`", "`Action`" and
"`Environment`" modules. This is both consistent with python practice but allows also to make the
difference between the files in the
module and the class imported. This should have little to no impact on all codes but to "upgrade"
instead of `from grid2op.Action.BaseAction import BaseAction` (which you should not have done in the first place)
just do `from grid2op.Action import BaseAction`. Expect other changes like this for other grid2op modules
in the near future.
- [FIXED] broken environ "l2rpn_idf_2023" (with test=True) due to the presence of a `__pycache__` folder
- [FIXED] time series `MultiFolder` will now ignore folder `__pycache__`
- [FIXED] an issue with compatibility with previous versions (due to alert)
- [FIXED] an issue with the `_ObsEnv` when using reward that could not be used in forecast (`self.is_simulated_env()`
was not working as expected due to a wrong init of the reward in `_ObsEnv`)
- [FIXED] an issue when disconnecting loads / generators / storage units and changing their values in the same
action: the behaviour could depend on the backend. As of 1.9.2 the "disconnections" have the priority (if
an action disconnect an element, it will not change its sepoint at the same time).
- [FIXED] a bug in `AlertReward` due to `reset` not being called.
- [FIXED] issue https://github.com/rte-france/Grid2Op/issues/494
- [ADDED] the score function used for the L2RPN 2023 competition (Paris Area)
- [IMPROVED] overall performances by calling `arr.sum()` or `arr.any()` instead of `np.sum(arr)` or
`np.any(arr)` see https://numpy.org/neps/nep-0018-array-function-protocol.html#performance
- [IMPROVED] overall performance of `obs.simulate` function by improving speed of copy of `_BackendAction`
- [IMPROVED] overall performance of `env.step` / `obs.simulate` by preventing unnecessary observation deep copy
- [IMPROVED] overall performance of `env.step` / `obs.simulate` by switching to `copy.deepcopy(obs)` instead of
`obs.copy()`

[1.9.1] - 2023-07-06
--------------------
- [BREAKING] (slightly): default `gym_compat` module now inherit from `gymnasium` (if
Expand Down
6 changes: 5 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
recursive-include grid2op/data *.bz2 *.json *.zip prods_charac.csv *.py .multimix storage_units_charac.csv start_datetime.info time_interval.info
recursive-include grid2op/data *.bz2 *.json *.zip prods_charac.csv *.py .multimix storage_units_charac.csv start_datetime.info time_interval.info
global-exclude */__pycache__/*
global-exclude *.pyc
global-exclude grid2op/data_test/*
global-exclude grid2op/tests/*
81 changes: 81 additions & 0 deletions _profiling/profiler_gym_compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# 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.


"""
This file aims at profiling a case where the "simulate" function is heavily used.
"""

import grid2op
from grid2op.gym_compat import GymEnv

import warnings
try:
from lightsim2grid import LightSimBackend
bk_cls = LightSimBackend
nm_bk_used = "LightSimBackend"
print("LightSimBackend used")
except ImportError:
from grid2op.Backend import PandaPowerBackend
bk_cls = PandaPowerBackend
nm_bk_used = "PandaPowerBackend"
print("PandaPowerBackend used")

import os
import cProfile
import pdb


NB_SIMULATE = 10
ENV_NAME = "l2rpn_icaps_2021_small"
ENV_NAME = "l2rpn_idf_2023"


def make_env(env_name=ENV_NAME):
with warnings.catch_warnings():
warnings.filterwarnings("ignore")
fake_env = grid2op.make(env_name, test=True)
param = fake_env.parameters
param.NO_OVERFLOW_DISCONNECTION = True
env = grid2op.make(env_name, backend=bk_cls(), param=param)
env.seed(0)
env.reset()
gym_env = GymEnv(env)
return gym_env, env


def run_env(gym_env, cp_gym_env, env, cp_env):
done = False
while not done:
act = {}
cp_gym_env.enable()
obs, reward, done, truncated, info = gym_env.step(act)
cp_gym_env.disable()

done = False
while not done:
act = env.action_space()
cp_env.enable()
obs, reward, done, info = env.step(act)
cp_env.disable()


if __name__ == "__main__":
gym_env, env = make_env()
cp_gym = cProfile.Profile()
cp_env = cProfile.Profile()
run_env(gym_env, cp_gym, env, cp_env)
nm_f, ext = os.path.splitext(__file__)
nm_out_gym = f"gym_{nm_f}_{nm_bk_used}_{ENV_NAME}_gymenv.prof"
nm_out_env = f"gym_{nm_f}_{nm_bk_used}_{ENV_NAME}_env.prof"
cp_gym.dump_stats(nm_out_gym)
cp_env.dump_stats(nm_out_env)
print("You can view profiling grid2op raw results with:\n\tsnakeviz {}".format(nm_out_env))
print("You can view profiling gym results with:\n\tsnakeviz {}".format(nm_out_gym))
# base: 66.7 s
# sans copy dans simulate: 65.2
52 changes: 38 additions & 14 deletions _profiling/profiler_simulate.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,37 +29,61 @@
import pdb


def make_env():
env_name = "l2rpn_icaps_2021"
NB_SIMULATE = 10
ENV_NAME = "l2rpn_icaps_2021_small"
ENV_NAME = "l2rpn_idf_2023"


def make_env(env_name=ENV_NAME):
with warnings.catch_warnings():
warnings.filterwarnings("ignore")
fake_env = grid2op.make(env_name, test=True)
param = fake_env.parameters
param.NO_OVERFLOW_DISCONNECTION = True
env = grid2op.make(env_name+"_small", backend=LightSimBackend(), param=param)
env = grid2op.make(env_name, backend=bk_cls(), param=param)
env.seed(0)
env.reset()
return env


def run_env(env):
def run_env(env, cp_env, cp_simu):
done = False
while not done:
act = env.action_space()
cp_env.enable()
obs, reward, done, info = env.step(act)
cp_env.disable()
if not done:
simulate(obs, env.action_space())
simulate(obs, env.action_space, NB_SIMULATE, cp_simu)


def simulate(obs, act):
simobs, rim_r, sim_d, sim_info = obs.simulate(act)
def simulate(obs, action_space, nb_simu=NB_SIMULATE, cp=None):
acts = [action_space.sample() for _ in range(nb_simu)]
# acts = [action_space() for _ in range(nb_simu)]
tmp = sum(acts, start = action_space())
try:
if cp is not None:
cp.enable()
for i in range(nb_simu):
simobs, rim_r, sim_d, sim_info = obs.simulate(acts[i])
prev_act = acts[i]
if cp is not None:
cp.disable()
except RuntimeError as exc_:
raise exc_


if __name__ == "__main__":
env = make_env()
cp = cProfile.Profile()
cp.enable()
run_env(env)
cp.disable()
cp_simu = cProfile.Profile()
cp_env = cProfile.Profile()
run_env(env, cp_env, cp_simu)
nm_f, ext = os.path.splitext(__file__)
nm_out = f"{nm_f}_{nm_bk_used}.prof"
cp.dump_stats(nm_out)
print("You can view profiling results with:\n\tsnakeviz {}".format(nm_out))
nm_out_simu = f"{nm_f}_{nm_bk_used}_{ENV_NAME}_{NB_SIMULATE}_simu.prof"
nm_out_env = f"{nm_f}_{nm_bk_used}_{ENV_NAME}_{NB_SIMULATE}_env.prof"
cp_simu.dump_stats(nm_out_simu)
cp_env.dump_stats(nm_out_env)
print("You can view profiling results with:\n\tsnakeviz {}".format(nm_out_env))
print("You can view profiling results with:\n\tsnakeviz {}".format(nm_out_simu))
# base: 66.7 s
# sans copy dans simulate: 65.2
84 changes: 83 additions & 1 deletion docs/available_envs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
.. |l2rpn_neurips_2020_track1_layout| image:: ./img/l2rpn_neurips_2020_track1_layout.png
.. |l2rpn_neurips_2020_track2_layout| image:: ./img/l2rpn_neurips_2020_track2_layout.png
.. |l2rpn_wcci_2022_layout| image:: ./img/l2rpn_wcci_2022_layout.png
.. |l2rpn_idf_2023_layout| image:: ./img/l2rpn_idf_2023_layout.png
.. |l2rpn_idf_2023_areas| image:: ./img/l2rpn_idf_2023_areas.png
.. |l2rpn_idf_2023_maint| image:: ./img/l2rpn_idf_2023_maint.png
.. |l2rpn_idf_2023_att| image:: ./img/l2rpn_idf_2023_att.png


Available environments
Expand Down Expand Up @@ -55,6 +59,7 @@ env name grid size maintenance opponent redis
:ref:`l2rpn_neurips_2020_track2` 118 sub. ✔️ ️ ❌ ️ ✔️ ️ ❌
:ref:`l2rpn_icaps_2021` 36 sub. ✔️ ️ ✔️ ️ ✔️ ️ ❌
:ref:`l2rpn_wcci_2022` 118 sub. ✔️ ️ ✔️ ️ ✔️ ️ ✔️ ️
:ref:`l2rpn_idf_2023` 118 sub. ✔️ ️ ✔️ ️ ✔️ ️ ✔️ ️
\* educ_case14_redisp \* 14 sub. ❌️ ❌ ️ ️ ✔️ ️ ❌
\* educ_case14_storage \* 14 sub. ❌️ ❌ ️ ✔️ ️ ✔️
\* rte_case5_example \* 5 sub. ❌️ ❌ ️ ️ ❌ ️ ️ ❌
Expand Down Expand Up @@ -139,6 +144,83 @@ This grid looks like:
|l2rpn_case14_sandbox_layout|


.. _l2rpn_idf_2023:


l2rpn_idf_2023
++++++++++++++++

This environment is also based on the 118 grid. The original grid has been modified (mainly for generator and loads location) to
accomodate for the "possible energy mix" of France in 2035.

It comes with 16 years worth of data, 1 year being divided in 52 weeks so 16 x 52 = 832 different scenarios and takes up around
~ 5.4 GB of space.

To create it you can :

.. code-block:: python
import grid2op
env_name = "lrpn_idf_2023"
env = grid2op.make(env_name)
It counts 118 substations, 186 powerlines, 99 loads and 62 generators. It will be used for the L2RPN competitions funded by Region Ile De France:
"Paris Region AI Challenge Energy Transition" and is free to use for everyone.

You have the possibility, provided that you installed `chronix2grid` (with `pip install grid2op[chronix2grid]`), to generate as
much data as you want with the :func:`grid2op.Environment.Environment.generate_data` function. See its documentation for more information.

The environment can be seen:

|l2rpn_idf_2023_layout|

Compared to previous available environments there are some new features including:

- 12 steps ahead forecast: with any observation, you can now have access to forecast 12 steps ahead with, for example `obs.simulate(..., time_step=12)`
or `obs.get_forecast_env()` which has a maximum duration of 12 steps (previously it was only 1 step ahead forecast). This could be used in
`model based` strategy for example (see page :ref:`model_based_rl` for more examples)
- a more complex opponent: the opponent can attack 3 lines in 3 different areas of the grid at the same time (instead of
being limited to just 1 attack)
- more complex rules: in this environment to balance for the fact that the opponent can makes 3 attacks, the agent can also act on 3 different
powerlines and 3 different substation per step (one per area).

The grid is split in 3 disctinct areas:

|l2rpn_idf_2023_areas|

Like most grid2op environment, it also has maintenance. Lines that can be in maintenance are:

.. line_color_maint = np.zeros(env.n_line)
.. line_in_maintenance = {'21_22_93', '39_41_121', '54_58_154', '17_18_88', '29_37_117',
.. '91_92_37', '41_48_131', '80_79_175', '88_91_33', '48_50_136',
.. '43_44_125', '12_14_68', '62_58_180', '44_45_126', '74_117_81',
.. '26_31_106', '4_10_162', '93_95_43', '62_63_160', '48_53_141',
.. '34_35_110'}
.. line_in_maintenance = list(line_in_maintenance)
.. line_color_maint[np.isin(env.name_line, line_in_maintenance) & np.isin(np.arange(env.n_line), lines_by_area[0])] = 1.0
.. line_color_maint[np.isin(env.name_line, line_in_maintenance) & np.isin(np.arange(env.n_line), lines_by_area[1])] = 2.0
.. line_color_maint[np.isin(env.name_line, line_in_maintenance) & np.isin(np.arange(env.n_line), lines_by_area[2])] = 3.0
.. plot_helper._line_color_scheme = ["gray", "blue", "orange", "red"]
.. _ = plot_helper.plot_info(line_values=line_color_maint, coloring="line")
.. plot_helper.restore_line_palette()
|l2rpn_idf_2023_maint|

And the lines that can be attacked by the opponent are:

.. attacked_lines = [106, 93, 88, 162, 68, 117, 180, 160, 136, 141, 131, 121, 125, 126, 110, 154, 81, 43, 33, 37, 62, 61]
.. line_color_att = np.zeros(env.n_line)
.. line_color_att[np.isin(np.arange(env.n_line), attacked_lines) & np.isin(np.arange(env.n_line), lines_by_area[0])] = 1.0
.. line_color_att[np.isin(np.arange(env.n_line), attacked_lines) & np.isin(np.arange(env.n_line), lines_by_area[1])] = 2.0
.. line_color_att[np.isin(np.arange(env.n_line), attacked_lines) & np.isin(np.arange(env.n_line), lines_by_area[2])] = 3.0
.. plot_helper._line_color_scheme = ["gray", "blue", "orange", "red"]
.. _ = plot_helper.plot_info(line_values=line_color_att, coloring="line")
.. plot_helper.restore_line_palette()
|l2rpn_idf_2023_att|


.. _l2rpn_wcci_2022:

l2rpn_wcci_2022
Expand All @@ -159,7 +241,7 @@ much data as you want with the :func:`grid2op.Environment.Environment.generate_d
env_name = "l2rpn_wcci_2022"
env = grid2op.make(env_name)
It counts 118 substations, 186 powerlines, 91 loads and 62 loads. It will be used for the L2RPN competitions at WCCI in 2022.
It counts 118 substations, 186 powerlines, 91 loads and 62 generators. It will be used for the L2RPN competitions at WCCI in 2022.

|l2rpn_wcci_2022_layout|

Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'Benjamin Donnot'

# The full version, including alpha/beta/rc tags
release = '1.9.1'
release = '1.9.2.dev0'
version = '1.9'


Expand Down
Binary file added docs/img/l2rpn_idf_2023_areas.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/l2rpn_idf_2023_att.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/l2rpn_idf_2023_layout.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/l2rpn_idf_2023_maint.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docs/observation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ how well the past action
performed. The second main input received from the environment is the :class:`BaseObservation`. This is gives the BaseAgent
partial, noisy, or complete information about the current state of the environment. This module implement a generic
:class:`BaseObservation` class and an example of a complete observation in the case of the Learning
To Run a Power Network (`l2RPN <https://l2rpn.chalearn.org/>`_ ) competition.
To Run a Power Network (`L2RPN <https://l2rpn.chalearn.org/>`_ ) competition.

Compared to other Reinforcement Learning problems the L2PRN competition allows another flexibility. Today, when
operating a powergrid, operators have "forecasts" at their disposal. We wanted to make them available in the
Expand Down

0 comments on commit be628ce

Please sign in to comment.