Skip to content

Commit

Permalink
Merge pull request #655 from DEUCE1957/dev_shedding
Browse files Browse the repository at this point in the history
Load and Generation Shedding
  • Loading branch information
BDonnot authored Dec 13, 2024
2 parents 3bf8934 + 73d14dd commit d0e8385
Show file tree
Hide file tree
Showing 40 changed files with 1,389 additions and 709 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Native multi agents support:
name is added. This behaviour can be turned off by passing `_add_cls_nm_bk=False`
when calling `grid2op.make(...)`. If you develop a new Backend, you can also
customize the added name by overloading the `get_class_added_name` class method.
- [BREAKING] it is now forbidden to create environment with arguments.
Only key-word arguments are allowed.
- [FIXED] issue https://github.com/Grid2op/grid2op/issues/657
- [FIXED] missing an import on the `MaskedEnvironment` class
- [FIXED] a bug when trying to set the load_p, load_q, gen_p, gen_v by names.
Expand All @@ -123,6 +125,7 @@ Native multi agents support:
environment data
- [FIXED] an issue preventing to set the thermal limit in the options
if the last simulated action lead to a game over
- [FIXED] logos now have the correct URL
- [ADDED] possibility to set the "thermal limits" when calling `env.reset(..., options={"thermal limit": xxx})`
- [ADDED] possibility to retrieve some structural information about elements with
with `gridobj.get_line_info(...)`, `gridobj.get_load_info(...)`, `gridobj.get_gen_info(...)`
Expand All @@ -131,6 +134,8 @@ Native multi agents support:
- [ADDED] a method to check the KCL (`obs.check_kirchhoff`) directly from the observation
(previously it was only possible to do it from the backend). This should
be used for testing purpose only
- [ADDED] possibility to set the initial time stamp of the observation in the `env.reset`
kwargs by using `env.reset(..., options={"init datetime": XXX})`
- [IMPROVED] possibility to set the injections values with names
to be consistent with other way to set the actions (*eg* set_bus)
- [IMPROVED] error messages when creating an action which changes the injections
Expand All @@ -148,13 +153,15 @@ Native multi agents support:
does not have shunt information but there are not shunts on the grid.
- [IMPROVED] consistency of `MultiMixEnv` in case of automatic_classes (only one
class is generated for all mixes)
- [IMRPOVED] handling of disconnected elements in the backend no more
raise error. The base `Backend` class does that.
- [IMPROVED] the `act.as_serializable_dict()` to be more 'backend agnostic'as
it nows tries to use the name of the elements in the json output
- [IMPROVED] the way shunt data are digested in the `BaseAction` class (it is now
possible to use the same things as for the other types of element)
- [IMPROVED] grid2op does not require the `chronics` folder when using the `FromHandlers`
class

[1.10.4] - 2024-10-15
-------------------------
- [FIXED] new pypi link (no change in code)
Expand Down
83 changes: 83 additions & 0 deletions getting_started/13_DetachmentOfLoadsAndGenerators.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Detachment of Loads and Generators\n",
"In emergency conditions, it may be possible / necessary for a grid operator to detach certain loads, generators, or other components in order to prevent a larger blackout. This notebook explores how this can be achieved in Grid2OP. \n",
"\n",
"By default detachment is disabled in all environments, to provide the keyword argument allow_detachment when initializing the environment. The backend must be able to support this feature."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import grid2op\n",
"from grid2op.Parameters import Parameters\n",
"from grid2op.PlotGrid import PlotMatplot\n",
"from pathlib import Path\n",
"\n",
"# Setup Environment\n",
"data_path = Path.cwd() / \"grid2op\" / \"data\"\n",
"p = Parameters()\n",
"p.MAX_SUB_CHANGED = 5\n",
"env = grid2op.make(data_path / \"rte_case5_example\", param=p, allow_detachment=True)\n",
"\n",
"# Setup Plotter Utility\n",
"plotter = PlotMatplot(env.observation_space, load_name=True, gen_name=True, dpi=150)\n",
"print(f\"Loads: {env.n_load}, Generators: {env.n_gen}, Storage: {env.n_storage}, Allow Detachment: {env._allow_detachment}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Detach the loads at substation 3 and 4. Normally this would cause a game-over, but if allow_detachment is True, the powerflow will be run. Game over in these cases can only occur if the powerflow does not converge."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"load_lookup = {name:i for i,name in enumerate(env.name_load)}\n",
"gen_lookup = {name:i for i,name in enumerate(env.name_gen)}\n",
"act = env.action_space({\"set_bus\":[(env.load_pos_topo_vect[load_lookup[\"load_3_1\"]], -1),\n",
" (env.load_pos_topo_vect[load_lookup[\"load_4_2\"]], -1)]})\n",
"print(act)\n",
"env.set_id(\"00\")\n",
"init_obs = env.reset()\n",
"obs, reward, done, info = env.step(act)\n",
"plotter.plot_obs(obs, figure=plt.figure(figsize=(8,5)))\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "venv_test",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.7"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
58 changes: 35 additions & 23 deletions grid2op/Action/baseAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ def __init__(self, _names_chronics_to_backend: Optional[Dict[Literal["loads", "p
self._modif_alert = False

@classmethod
def process_shunt_satic_data(cls):
def process_shunt_static_data(cls):
if not cls.shunts_data_available:
# this is really important, otherwise things from grid2op base types will be affected
cls.attr_list_vect = copy.deepcopy(cls.attr_list_vect)
Expand All @@ -506,7 +506,7 @@ def process_shunt_satic_data(cls):
except ValueError:
pass
cls.attr_list_set = set(cls.attr_list_vect)
return super().process_shunt_satic_data()
return super().process_shunt_static_data()

def copy(self) -> "BaseAction":
# sometimes this method is used...
Expand Down Expand Up @@ -573,8 +573,8 @@ def __copy__(self) -> "BaseAction":
return res

@classmethod
def process_shunt_satic_data(cls):
return super().process_shunt_satic_data()
def process_shunt_static_data(cls):
return super().process_shunt_static_data()

def __deepcopy__(self, memodict={}) -> "BaseAction":
res = type(self)()
Expand Down Expand Up @@ -846,7 +846,7 @@ def process_grid2op_compat(cls):
# if there are only one busbar, the "set_bus" action can still be used
# to disconnect the element, this is why it's not removed
cls._aux_process_n_busbar_per_sub()

cls.attr_list_set = copy.deepcopy(cls.attr_list_set)
cls.attr_list_set = set(cls.attr_list_vect)

Expand Down Expand Up @@ -1099,7 +1099,7 @@ def __eq__(self, other) -> bool:
self._change_bus_vect == other._change_bus_vect
):
return False

# shunts are the same
if type(self).shunts_data_available:
if self.n_shunt != other.n_shunt:
Expand Down Expand Up @@ -2163,6 +2163,34 @@ def _reset_vect(self):
self._vectorized = None
self._subs_impacted = None
self._lines_impacted = None

@staticmethod
def _check_keys_exist(action_cls:GridObjects, act_dict):
"""
Checks whether an action has the same keys in its
action space as are present in the provided dictionary.
Args:
action_cls (GridObjects): A Grid2Op action
act_dict (str:Any): Dictionary representation of an action
"""
for kk in act_dict.keys():
if kk not in action_cls.authorized_keys:
if kk == "shunt" and not action_cls.shunts_data_available:
# no warnings are raised in this case because if a warning
# were raised it could crash some environment
# with shunt in "init_state.json" with a backend that does not
# handle shunt
continue
if kk == "set_storage" and action_cls.n_storage == 0:
# no warnings are raised in this case because if a warning
# were raised it could crash some environment
# with storage in "init_state.json" but if the backend did not
# handle storage units
continue
warnings.warn(
f"The key '{kk}' used to update an action will be ignored. Valid keys are {action_cls.authorized_keys}"
)

def update(self,
dict_: DICT_ACT_TYPING
Expand Down Expand Up @@ -2374,23 +2402,7 @@ def update(self,
cls = type(self)

if dict_ is not None:
for kk in dict_.keys():
if kk not in cls.authorized_keys:
if kk == "shunt" and not cls.shunts_data_available:
# no warnings are raised in this case because if a warning
# were raised it could crash some environment
# with shunt in "init_state.json" with a backend that does not
# handle shunt
continue
if kk == "set_storage" and cls.n_storage == 0:
# no warnings are raised in this case because if a warning
# were raised it could crash some environment
# with storage in "init_state.json" but if the backend did not
# handle storage units
continue
warn = 'The key "{}" used to update an action will be ignored. Valid keys are {}'
warn = warn.format(kk, cls.authorized_keys)
warnings.warn(warn)
BaseAction._check_keys_exist(cls, dict_)

if cls.shunts_data_available:
# do not digest shunt when backend does not support it
Expand Down
4 changes: 2 additions & 2 deletions grid2op/Agent/topologyGreedy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# SPDX-License-Identifier: MPL-2.0
# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.

from typing import List
from typing import List, Optional
from grid2op.Observation import BaseObservation
from grid2op.Action import BaseAction, ActionSpace
from grid2op.Agent.greedyAgent import GreedyAgent
Expand All @@ -27,7 +27,7 @@ class TopologyGreedy(GreedyAgent):

def __init__(self, action_space: ActionSpace, simulated_time_step : int =1):
GreedyAgent.__init__(self, action_space, simulated_time_step=simulated_time_step)
self.tested_action : List[BaseAction]= None
self.tested_action : Optional[list[BaseAction]] = None

def _get_tested_action(self, observation: BaseObservation) -> List[BaseAction]:
if self.tested_action is None:
Expand Down
Loading

0 comments on commit d0e8385

Please sign in to comment.