Skip to content

Commit a985a8e

Browse files
committed
improve documentation for redispatching failed vs illegal vs ambiguous
Signed-off-by: DONNOT Benjamin <[email protected]>
1 parent 6aa8500 commit a985a8e

File tree

9 files changed

+145
-52
lines changed

9 files changed

+145
-52
lines changed

CHANGELOG.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ Native multi agents support:
100100
- add detachment
101101
- add change_bus / set_bus
102102

103+
[1.11.1] - 2025-xx-yy
104+
--------------------------
105+
- [BREAKING] the `info` returned argument of `env.step(...)` function
106+
does not have the unclear `is_redispatching_illegal` key. This key has been
107+
replaced (without any change to its signification) with `failed_redispatching`
108+
- [IMPROVED] clarity of the `failed_redispatching` key of the `info` returned value
109+
of the `env.step` function (previously called `is_redispatching_illegal` which
110+
was not clear)
111+
112+
103113
[1.11.0] - 2025-04-14
104114
-----------------------
105115
- [BREAKING] Change for `FromMultiEpisodeData` that disables the caching by default

grid2op/Environment/baseEnv.py

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
from grid2op.typing_variables import N_BUSBAR_PER_SUB_TYPING
3737
from grid2op.Exceptions import (Grid2OpException,
3838
EnvError,
39-
InvalidRedispatching,
39+
IllegalRedispatching,
40+
ImpossibleRedispatching,
4041
GeneratorTurnedOffTooSoon,
4142
GeneratorTurnedOnTooSoon,
4243
AmbiguousActionRaiseAlert,
@@ -78,7 +79,7 @@
7879
)
7980

8081

81-
BASE_TXT_COPYRIGHT = """# Copyright (c) 2019-2020, RTE (https://www.rte-france.com)
82+
BASE_TXT_COPYRIGHT = """# Copyright (c) 2019-2025, RTE (https://www.rte-france.com)
8283
# See AUTHORS.txt
8384
# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
8485
# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
@@ -87,7 +88,13 @@
8788
# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.
8889
8990
# THIS FILE HAS BEEN AUTOMATICALLY GENERATED BY "env.generate_classes()"
90-
# WE DO NOT RECOMMEND TO ALTER IT IN ANY WAY
91+
# WE DO NOT RECOMMEND TO ALTER IT IN ANY WAY.
92+
# THIS INCLUDES, BUT IS NOT LIMITED TO:
93+
# - MODIFY IT
94+
# - DELETE IT
95+
# - MOVE IT
96+
# - RENAME IT
97+
9198
"""
9299

93100
class BaseEnv(GridObjects, RandomObject, ABC):
@@ -2067,6 +2074,7 @@ def _get_already_modified_gen(self, action):
20672074
return self._already_modified_gen
20682075

20692076
def _prepare_redisp(self, action, new_p, already_modified_gen):
2077+
cls = type(self)
20702078
# trying with an optimization method
20712079
except_ = None
20722080
info_ = []
@@ -2082,10 +2090,10 @@ def _prepare_redisp(self, action, new_p, already_modified_gen):
20822090
):
20832091
return valid, except_, info_
20842092
# check that everything is consistent with pmin, pmax:
2085-
if (self._target_dispatch > self.gen_pmax - self.gen_pmin).any():
2093+
if (self._target_dispatch > cls.gen_pmax - cls.gen_pmin).any():
20862094
# action is invalid, the target redispatching would be above pmax for at least a generator
2087-
cond_invalid = self._target_dispatch > self.gen_pmax - self.gen_pmin
2088-
except_ = InvalidRedispatching(
2095+
cond_invalid = self._target_dispatch > cls.gen_pmax - cls.gen_pmin
2096+
except_ = IllegalRedispatching(
20892097
"You cannot ask for a dispatch higher than pmax - pmin [it would be always "
20902098
"invalid because, even if the sepoint is pmin, this dispatch would set it "
20912099
"to a number higher than pmax, which is impossible]. Invalid dispatch for "
@@ -2094,10 +2102,10 @@ def _prepare_redisp(self, action, new_p, already_modified_gen):
20942102
)
20952103
self._target_dispatch -= redisp_act_orig
20962104
return valid, except_, info_
2097-
if (self._target_dispatch < self.gen_pmin - self.gen_pmax).any():
2105+
if (self._target_dispatch < cls.gen_pmin - cls.gen_pmax).any():
20982106
# action is invalid, the target redispatching would be below pmin for at least a generator
2099-
cond_invalid = self._target_dispatch < self.gen_pmin - self.gen_pmax
2100-
except_ = InvalidRedispatching(
2107+
cond_invalid = self._target_dispatch < cls.gen_pmin - cls.gen_pmax
2108+
except_ = IllegalRedispatching(
21012109
"You cannot ask for a dispatch lower than pmin - pmax [it would be always "
21022110
"invalid because, even if the sepoint is pmax, this dispatch would set it "
21032111
"to a number bellow pmin, which is impossible]. Invalid dispatch for "
@@ -2110,13 +2118,13 @@ def _prepare_redisp(self, action, new_p, already_modified_gen):
21102118
# i can't redispatch turned off generators [turned off generators need to be turned on before redispatching]
21112119
if (redisp_act_orig[np.abs(new_p) <= 1e-7]).any() and self._forbid_dispatch_off:
21122120
# action is invalid, a generator has been redispatched, but it's turned off
2113-
except_ = InvalidRedispatching(
2121+
except_ = IllegalRedispatching(
21142122
"Impossible to dispatch a turned off generator"
21152123
)
21162124
self._target_dispatch -= redisp_act_orig
21172125
return valid, except_, info_
21182126

2119-
if self._forbid_dispatch_off is True:
2127+
if self._forbid_dispatch_off:
21202128
redisp_act_orig_cut = 1.0 * redisp_act_orig
21212129
redisp_act_orig_cut[np.abs(new_p) <= 1e-7] = 0.0
21222130
if (redisp_act_orig_cut != redisp_act_orig).any():
@@ -2148,7 +2156,7 @@ def _make_redisp(self, already_modified_gen, new_p):
21482156

21492157
def _compute_dispatch_vect(self, already_modified_gen, new_p):
21502158
except_ = None
2151-
2159+
cls = type(self)
21522160
# handle the case where there are storage or redispatching
21532161
# action or curtailment action on the "init state"
21542162
# of the grid
@@ -2162,8 +2170,8 @@ def _compute_dispatch_vect(self, already_modified_gen, new_p):
21622170
| (np.abs(self._actual_dispatch) >= 1e-7)
21632171
| (self._target_dispatch != self._actual_dispatch)
21642172
)
2165-
gen_participating[~self.gen_redispatchable] = False
2166-
if type(self).detachment_is_allowed:
2173+
gen_participating[~cls.gen_redispatchable] = False
2174+
if cls.detachment_is_allowed:
21672175
gen_participating[self._backend_action.get_gen_detached()] = False
21682176
incr_in_chronics = new_p - (
21692177
self._gen_activeprod_t_redisp - self._actual_dispatch
@@ -2172,16 +2180,16 @@ def _compute_dispatch_vect(self, already_modified_gen, new_p):
21722180
# check if the constraints are violated
21732181
## total available "juice" to go down (incl ramp and pmin / pmax)
21742182
p_min_down = (
2175-
self.gen_pmin[gen_participating]
2183+
cls.gen_pmin[gen_participating]
21762184
- self._gen_activeprod_t_redisp[gen_participating]
21772185
)
2178-
avail_down = np.maximum(p_min_down, -self.gen_max_ramp_down[gen_participating])
2186+
avail_down = np.maximum(p_min_down, -cls.gen_max_ramp_down[gen_participating])
21792187
## total available "juice" to go up (incl. ramp and pmin / pmax)
21802188
p_max_up = (
2181-
self.gen_pmax[gen_participating]
2189+
cls.gen_pmax[gen_participating]
21822190
- self._gen_activeprod_t_redisp[gen_participating]
21832191
)
2184-
avail_up = np.minimum(p_max_up, self.gen_max_ramp_up[gen_participating])
2192+
avail_up = np.minimum(p_max_up, cls.gen_max_ramp_up[gen_participating])
21852193
except_ = self._detect_infeasible_dispatch(
21862194
incr_in_chronics[gen_participating], avail_down, avail_up
21872195
)
@@ -2192,21 +2200,21 @@ def _compute_dispatch_vect(self, already_modified_gen, new_p):
21922200
and self._parameters.ALLOW_DISPATCH_GEN_SWITCH_OFF
21932201
):
21942202
gen_participating_tmp = self.gen_redispatchable
2195-
if type(self).detachment_is_allowed:
2203+
if cls.detachment_is_allowed:
21962204
gen_participating_tmp[self._backend_action.get_gen_detached()] = False
21972205
p_min_down_tmp = (
2198-
self.gen_pmin[gen_participating_tmp]
2206+
cls.gen_pmin[gen_participating_tmp]
21992207
- self._gen_activeprod_t_redisp[gen_participating_tmp]
22002208
)
22012209
avail_down_tmp = np.maximum(
2202-
p_min_down_tmp, -self.gen_max_ramp_down[gen_participating_tmp]
2210+
p_min_down_tmp, -cls.gen_max_ramp_down[gen_participating_tmp]
22032211
)
22042212
p_max_up_tmp = (
2205-
self.gen_pmax[gen_participating_tmp]
2213+
cls.gen_pmax[gen_participating_tmp]
22062214
- self._gen_activeprod_t_redisp[gen_participating_tmp]
22072215
)
22082216
avail_up_tmp = np.minimum(
2209-
p_max_up_tmp, self.gen_max_ramp_up[gen_participating_tmp]
2217+
p_max_up_tmp, cls.gen_max_ramp_up[gen_participating_tmp]
22102218
)
22112219
except_tmp = self._detect_infeasible_dispatch(
22122220
incr_in_chronics[gen_participating_tmp],
@@ -2404,7 +2412,7 @@ def f(init):
24042412
"Redispatching automaton terminated with error (no more information available "
24052413
'at this point):\n"{}"'.format(res.message)
24062414
)
2407-
except_ = InvalidRedispatching(error_dispatch)
2415+
except_ = ImpossibleRedispatching(error_dispatch)
24082416
return except_
24092417

24102418
def _detect_infeasible_dispatch(self, incr_in_chronics, avail_down, avail_up):
@@ -2431,7 +2439,7 @@ def _detect_infeasible_dispatch(self, incr_in_chronics, avail_down, avail_up):
24312439
pmax="pmax",
24322440
max_ramp_up="max_ramp_up",
24332441
)
2434-
except_ = InvalidRedispatching(msg)
2442+
except_ = ImpossibleRedispatching(msg)
24352443
elif sum_move < avail_down_sum:
24362444
# infeasible because not enough is asked
24372445
msg = DETAILED_REDISP_ERR_MSG.format(
@@ -2447,7 +2455,7 @@ def _detect_infeasible_dispatch(self, incr_in_chronics, avail_down, avail_up):
24472455
pmax="pmin",
24482456
max_ramp_up="max_ramp_down",
24492457
)
2450-
except_ = InvalidRedispatching(msg)
2458+
except_ = ImpossibleRedispatching(msg)
24512459
return except_
24522460

24532461
def _update_actions(self):
@@ -3116,7 +3124,7 @@ def _aux_apply_redisp(self,
31163124
except_: List[Exception],
31173125
powerline_status):
31183126
cls = type(self)
3119-
is_illegal_redisp = False
3127+
failed_redisp = False
31203128
is_done = False
31213129
is_illegal_reco = False
31223130

@@ -3136,7 +3144,7 @@ def _aux_apply_redisp(self,
31363144
_ = action.get_topological_impact(powerline_status, _store_in_cache=True, _read_from_cache=False)
31373145
if cls.dim_alerts:
31383146
action.raise_alert = orig_action.raise_alert
3139-
is_illegal_redisp = True
3147+
failed_redisp = True
31403148
except_.append(except_tmp)
31413149

31423150
if cls.n_storage > 0:
@@ -3170,11 +3178,12 @@ def _aux_apply_redisp(self,
31703178
_ = res_action.get_topological_impact(powerline_status, _store_in_cache=True, _read_from_cache=False)
31713179
if cls.dim_alerts:
31723180
res_action.raise_alert = action.raise_alert
3173-
is_illegal_redisp = True
3181+
failed_redisp = True
3182+
31743183
except_.append(except_tmp)
31753184
is_done = True
31763185
except_.append(
3177-
InvalidRedispatching(
3186+
ImpossibleRedispatching(
31783187
"Game over due to infeasible redispatching state. "
31793188
'The routine used to compute the "next state" has diverged. '
31803189
"This means that there is no way to compute a physically valid generator state "
@@ -3183,7 +3192,7 @@ def _aux_apply_redisp(self,
31833192
"a generator would be damaged in real life. This is a game over."
31843193
)
31853194
)
3186-
return res_action, is_illegal_redisp, is_illegal_reco, is_done
3195+
return res_action, failed_redisp, is_illegal_reco, is_done
31873196

31883197
# check the validity of min downtime and max uptime
31893198
except_tmp = self._handle_updown_times(gen_up_before, self._actual_dispatch)
@@ -3198,7 +3207,7 @@ def _aux_apply_redisp(self,
31983207
else:
31993208
res_action = action
32003209
# self._backend_action.set_redispatch(self._actual_dispatch)
3201-
return res_action, is_illegal_redisp, is_illegal_reco, is_done
3210+
return res_action, failed_redisp, is_illegal_reco, is_done
32023211

32033212
def _aux_update_backend_action(self,
32043213
action: BaseAction,
@@ -3514,7 +3523,9 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation,
35143523
that it is disconnected just after, 2 that it's disconnected just after etc.)
35153524
- "is_illegal" (``bool``) whether the action given as input was illegal
35163525
- "is_ambiguous" (``bool``) whether the action given as input was ambiguous.
3517-
- "is_dispatching_illegal" (``bool``) was the action illegal due to redispatching
3526+
- "failed_redispatching" (``bool``) has the redispatching part of this action (or any previous one)
3527+
lead to infeasibility. If `True` it can means that either the redispatching part of the
3528+
action has been completely ignored or that the environment is over.
35183529
- "is_illegal_reco" (``bool``) was the action illegal due to a powerline reconnection
35193530
- "reason_alarm_illegal" (``None`` or ``Exception``) reason for which the alarm is illegal
35203531
(it's None if no alarm are raised or if the alarm feature is not used)
@@ -3584,7 +3595,7 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation,
35843595
is_done = False
35853596
is_illegal = False
35863597
is_ambiguous = False
3587-
is_illegal_redisp = False
3598+
failed_redisp = False
35883599
is_illegal_reco = False
35893600
reason_alarm_illegal = None
35903601
self._is_alarm_illegal = False
@@ -3703,7 +3714,7 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation,
37033714
res_disp = self._aux_apply_redisp(
37043715
action, new_p, new_p_th, gen_curtailed, except_, powerline_status
37053716
)
3706-
action, is_illegal_redisp, is_illegal_reco, is_done = res_disp
3717+
action, failed_redisp, is_illegal_reco, is_done = res_disp
37073718

37083719
self._time_redisp += time.perf_counter() - beg__redisp
37093720

@@ -3769,7 +3780,7 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation,
37693780
"disc_lines": self._disc_lines,
37703781
"is_illegal": is_illegal,
37713782
"is_ambiguous": is_ambiguous,
3772-
"is_dispatching_illegal": is_illegal_redisp,
3783+
"failed_redispatching": failed_redisp,
37733784
"is_illegal_reco": is_illegal_reco,
37743785
"reason_alarm_illegal": reason_alarm_illegal,
37753786
"reason_alert_illegal": reason_alert_illegal,
@@ -3788,7 +3799,7 @@ def step(self, action: BaseAction) -> Tuple[BaseObservation,
37883799
action,
37893800
has_error,
37903801
self.done, # is_done
3791-
is_illegal or is_illegal_redisp or is_illegal_reco,
3802+
is_illegal or failed_redisp or is_illegal_reco,
37923803
is_ambiguous,
37933804
)
37943805
self.infos["rewards"] = other_reward

grid2op/Exceptions/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"UnknownEnv",
1515
"MultiEnvException",
1616
"IllegalAction",
17+
"IllegalRedispatching",
1718
"OnProduction",
1819
"VSetpointModified",
1920
"ActiveSetPointAbovePmax",
@@ -63,7 +64,8 @@
6364
"SomeGeneratorAbovePmax",
6465
"SomeGeneratorBelowPmin",
6566
"SomeGeneratorAboveRampmax",
66-
"SomeGeneratorBelowRampmin"
67+
"SomeGeneratorBelowRampmin",
68+
"ImpossibleRedispatching",
6769
]
6870

6971
from grid2op.Exceptions.grid2OpException import Grid2OpException
@@ -84,7 +86,8 @@
8486
SomeGeneratorAbovePmax,
8587
SomeGeneratorBelowPmin,
8688
SomeGeneratorAboveRampmax,
87-
SomeGeneratorBelowRampmin)
89+
SomeGeneratorBelowRampmin,
90+
ImpossibleRedispatching)
8891

8992
from grid2op.Exceptions.illegalActionExceptions import (IllegalAction,
9093
OnProduction,
@@ -95,6 +98,7 @@
9598
OnLines,
9699
InvalidReconnection,
97100
UnitCommitorRedispachingNotAvailable,
101+
IllegalRedispatching,
98102
)
99103

100104
from grid2op.Exceptions.ambiguousActionExceptions import (NotEnoughGenerators,

grid2op/Exceptions/envExceptions.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ class SomeGeneratorBelowPmin(EnvError):
134134
"""
135135
pass
136136

137+
137138
class SomeGeneratorAboveRampmax(EnvError):
138139
"""This is a more precise exception saying that, at the end of the simulation, some generator would
139140
have their production vary too much, which is not possible in practice.
@@ -157,6 +158,16 @@ class SomeGeneratorBelowRampmin(EnvError):
157158
"""
158159
pass
159160

161+
162+
class ImpossibleRedispatching(EnvError):
163+
"""This is a more precise exception saying that, the simulation of a step cannot be made because some
164+
generators would break their physical constraints.
165+
166+
.. versionadded:: 1.11.1
167+
"""
168+
pass
169+
170+
160171
# Unknown environment at creation
161172
class UnknownEnv(Grid2OpException):
162173
"""

grid2op/Exceptions/illegalActionExceptions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ class InvalidReconnection(OnLines):
8989
pass
9090

9191

92+
class IllegalRedispatching(IllegalAction):
93+
"""
94+
This is a more precise exception than :class:`IllegalAction` indicating that
95+
the :class:`grid2op.BaseAction.BaseAction`
96+
try to apply an invalid redispatching strategy.
97+
"""
98+
99+
pass
100+
101+
92102
# attempt to use redispatching or unit commit method in an environment not set up.
93103
class UnitCommitorRedispachingNotAvailable(IllegalAction):
94104
"""

0 commit comments

Comments
 (0)