Skip to content

Commit

Permalink
Merge pull request #675 from Grid2op/dev_turnoff_gen_load
Browse files Browse the repository at this point in the history
Dev possibility to turn off generators, loads and storage units
  • Loading branch information
BDonnot authored Dec 16, 2024
2 parents aab2b8d + 1b717c8 commit 2a7caa0
Show file tree
Hide file tree
Showing 50 changed files with 3,271 additions and 1,104 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ 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.
- [BREAKING] the way actions is serialized has been changed with respect to the `from_vect` /
`to_vect` method. This might introduce some issues when loading previously saved actions
with this methods.
- [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,7 +128,11 @@ 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] some bugs in `act.from_json(...)` due to the handling of the injection modifications.
- [FIXED] logos now have the correct URL
- [FIXED] deprecated call to `tostring_rgb` (replaced `tostring_argb`) in the env.render function.
- [FIXED] warnings not properly issued in the AAA test when backend failed to call
`can_handle_XXX` functions (*eg* `can_handle_more_than_2_busbar()` or `can_handle_detachment()`)
- [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 @@ -132,6 +141,10 @@ 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] parameters to disable the "redispatching routine" of the environment
(see `params.ENV_DOES_REDISPATCHING`)
- [ADDED] parameters to stop the episode when one of the constraints of one of the
generators is not met (see `params.STOP_EP_IF_SLACK_BREAK_CONSTRAINTS`)
- [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
Expand All @@ -151,12 +164,18 @@ 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
- [IMPROVED] the function `action.get_topological_impact(...)` has now a "caching" mechanism
that allows not to recompute it over and over again (this is internal API please do not change
it... unless you know what you are doing)


[1.10.4] - 2024-10-15
-------------------------
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.11.0.dev2'
release = '1.11.0.dev3'
version = '1.11'


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
}
119 changes: 88 additions & 31 deletions grid2op/Action/_backendAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,66 @@ def _aux_iadd_reconcile_disco_reco(self):
self.line_ex_pos_topo_vect,
self.last_topo_registered,
)


def _aux_iadd_detach(self, other, set_topo_vect : np.ndarray, modif_inj: bool):
cls = type(self)
if other._modif_detach_load:
set_topo_vect[cls.load_pos_topo_vect[other._detach_load]] = -1
modif_set_bus = True
if other._modif_detach_gen:
set_topo_vect[cls.gen_pos_topo_vect[other._detach_gen]] = -1
modif_set_bus = True
if other._modif_detach_storage:
set_topo_vect[cls.storage_pos_topo_vect[other._detach_storage]] = -1
modif_set_bus = True
if modif_inj:
for key, vect_ in other._dict_inj.items():
if key == "load_p" or key == "load_q":
vect_[other._detach_load] = 0.
elif key == "prod_p":
vect_[other._detach_gen] = 0.
elif key == "prod_v":
vect_[other._detach_gen] = np.nan
else:
raise RuntimeError(f"Unknown key {key} for injection found.")
else:
# TODO when injection is not modified by the action (eg change nothing)
if other._modif_detach_load:
modif_inj = True
other._dict_inj["load_p"] = np.full(cls.n_load, fill_value=np.nan, dtype=dt_float)
other._dict_inj["load_q"] = np.full(cls.n_load, fill_value=np.nan, dtype=dt_float)
other._dict_inj["load_p"][other._detach_load] = 0.
other._dict_inj["load_q"][other._detach_load] = 0.
if other._modif_detach_gen:
modif_inj = True
other._dict_inj["prod_p"] = np.full(cls.n_gen, fill_value=np.nan, dtype=dt_float)
other._dict_inj["prod_p"][other._detach_gen] = 0.
return modif_set_bus, modif_inj

def _aux_iadd_line_status(self, other: BaseAction, switch_status: np.ndarray, set_status: np.ndarray):
if other._modif_change_status:
self.current_topo.change_status(
switch_status,
self.line_or_pos_topo_vect,
self.line_ex_pos_topo_vect,
self.last_topo_registered,
)
if other._modif_set_status:
self.current_topo.set_status(
set_status,
self.line_or_pos_topo_vect,
self.line_ex_pos_topo_vect,
self.last_topo_registered,
)

# if other._modif_change_status or other._modif_set_status:
(
self._status_or_before[:],
self._status_ex_before[:],
) = self.current_topo.get_line_status(
self.line_or_pos_topo_vect, self.line_ex_pos_topo_vect
)

def __iadd__(self, other : BaseAction) -> Self:
"""
.. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\
Expand All @@ -760,16 +819,23 @@ def __iadd__(self, other : BaseAction) -> Self:
"""

set_status = other._set_line_status
set_status = 1 * other._set_line_status
switch_status = other._switch_line_status
set_topo_vect = other._set_topo_vect
set_topo_vect = 1 * other._set_topo_vect
switcth_topo_vect = other._change_bus_vect
redispatching = other._redispatch
storage_power = other._storage_power

modif_set_bus = other._modif_set_bus
modif_inj = other._modif_inj
cls = type(self)

# I detachment (before all else)
if cls.detachment_is_allowed and other.has_element_detached():
modif_set_bus, modif_inj = self._aux_iadd_detach(other, set_topo_vect, modif_inj)

# I deal with injections
# Ia set the injection
if other._modif_inj:
if modif_inj:
self._aux_iadd_inj(other._dict_inj)

# Ib change the injection aka redispatching
Expand All @@ -781,39 +847,18 @@ def __iadd__(self, other : BaseAction) -> Self:
self.storage_power.set_val(storage_power)

# II shunts
if type(self).shunts_data_available:
if cls.shunts_data_available:
self._aux_iadd_shunt(other)

# III line status
# this need to be done BEFORE the topology, as a connected powerline will be connected to their old bus.
# regardless if the status is changed in the action or not.
if other._modif_change_status:
self.current_topo.change_status(
switch_status,
self.line_or_pos_topo_vect,
self.line_ex_pos_topo_vect,
self.last_topo_registered,
)
if other._modif_set_status:
self.current_topo.set_status(
set_status,
self.line_or_pos_topo_vect,
self.line_ex_pos_topo_vect,
self.last_topo_registered,
)

# if other._modif_change_status or other._modif_set_status:
(
self._status_or_before[:],
self._status_ex_before[:],
) = self.current_topo.get_line_status(
self.line_or_pos_topo_vect, self.line_ex_pos_topo_vect
)

self._aux_iadd_line_status(other, switch_status, set_status)

# IV topo
if other._modif_change_bus:
self.current_topo.change_val(switcth_topo_vect)
if other._modif_set_bus:
if modif_set_bus:
self.current_topo.set_val(set_topo_vect)

# V Force disconnected status
Expand All @@ -823,7 +868,7 @@ def __iadd__(self, other : BaseAction) -> Self:
)

# At least one disconnected extremity
if other._modif_change_bus or other._modif_set_bus:
if other._modif_change_bus or modif_set_bus:
self._aux_iadd_reconcile_disco_reco()
return self

Expand Down Expand Up @@ -1444,3 +1489,15 @@ def update_state(self, powerline_disconnected) -> None:
)
self.last_topo_registered.update_connected(self.current_topo)
self.current_topo.reset()

def get_load_detached(self):
cls = type(self)
return self.current_topo.values[cls.load_pos_topo_vect] == -1

def get_gen_detached(self):
cls = type(self)
return self.current_topo.values[cls.gen_pos_topo_vect] == -1

def get_sto_detached(self):
cls = type(self)
return self.current_topo.values[cls.storage_pos_topo_vect] == -1
2 changes: 0 additions & 2 deletions grid2op/Action/actionSpace.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ def __init__(
Class specifying the rules of the game used to check the legality of the actions.
"""
actionClass._add_shunt_data()
actionClass._update_value_set()
SerializableActionSpace.__init__(self, gridobj, actionClass=actionClass, _local_dir_cls=_local_dir_cls)
self.legal_action = legal_action

Expand Down
Loading

0 comments on commit 2a7caa0

Please sign in to comment.