Skip to content

Commit

Permalink
Merge pull request #534 from BDonnot/bd_dev
Browse files Browse the repository at this point in the history
Bd dev
  • Loading branch information
BDonnot authored Sep 28, 2023
2 parents f448a09 + 25aad47 commit 85611cb
Show file tree
Hide file tree
Showing 12 changed files with 430 additions and 213 deletions.
11 changes: 10 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@ Change Log

[1.9.6] - 2023-xx-yy
----------------------
- [BREAKING] when a storage is connected alone on a bus, even if it produces / absorbs 0.0 MW it
will raise a diverging powerflow error (previously the storage was automatically disconnected by
`PandaPowerBackend`, but probably not by other backends)
- [BREAKING] when a shunt is alone on a bus, the powerflow will diverge even in DC mode
(previously it only converges which was wrong behaviour: grid2op should not disconnect shunt)
- [FIXED] a bug in PandaPowerBackend (DC mode) where isolated load did not raised
exception (they should lead to a divergence)
- [FIXED] some wrong behaviour in the `remove_line_status_from_topo` when no observation where provided
and `check_cooldown` is `False`
- [ADDED] now depends on the `typing_extensions` package
- [IMPROVED] type hints for Backend and PandapowerBackend
- [IMPROVED] type hints for `Backend` and `PandapowerBackend`

[1.9.5] - 2023-09-18
---------------------
Expand Down
14 changes: 12 additions & 2 deletions grid2op/Action/_backendAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ def __init__(self):
self.shunt_p = ValueStore(self.n_shunt, dtype=dt_float)
self.shunt_q = ValueStore(self.n_shunt, dtype=dt_float)
self.shunt_bus = ValueStore(self.n_shunt, dtype=dt_int)
self.current_shunt_bus = ValueStore(self.n_shunt, dtype=dt_int)
self.current_shunt_bus.values[:] = 1

self._status_or_before = np.ones(self.n_line, dtype=dt_int)
self._status_ex_before = np.ones(self.n_line, dtype=dt_int)
Expand Down Expand Up @@ -268,6 +270,7 @@ def __deepcopy__(self, memodict={}):
res.shunt_p.copy(self.shunt_p)
res.shunt_q.copy(self.shunt_q)
res.shunt_bus.copy(self.shunt_bus)
res.current_shunt_bus.copy(self.current_shunt_bus)

res._status_or_before[:] = self._status_or_before
res._status_ex_before[:] = self._status_ex_before
Expand Down Expand Up @@ -308,6 +311,7 @@ def reorder(self, no_load, no_gen, no_topo, no_storage, no_shunt):
self.shunt_p.reorder(no_shunt)
self.shunt_q.reorder(no_shunt)
self.shunt_bus.reorder(no_shunt)
self.current_shunt_bus.reorder(no_shunt)

def reset(self):
# last topo
Expand All @@ -331,6 +335,7 @@ def reset(self):
self.shunt_p.reset()
self.shunt_q.reset()
self.shunt_bus.reset()
self.current_shunt_bus.reset()

def all_changed(self):
# last topo
Expand Down Expand Up @@ -404,7 +409,7 @@ def __iadd__(self, other):
self.storage_power.set_val(storage_power)

# II shunts
if self.shunts_data_available:
if type(self).shunts_data_available:
shunts = {}
if other.shunts_data_available:
shunts["shunt_p"] = other.shunt_p
Expand All @@ -417,6 +422,7 @@ def __iadd__(self, other):
self.shunt_q.set_val(arr_)
arr_ = shunts["shunt_bus"]
self.shunt_bus.set_val(arr_)
self.current_shunt_bus.values[self.shunt_bus.changed] = self.shunt_bus.values[self.shunt_bus.changed]

# III line status
# this need to be done BEFORE the topology, as a connected powerline will be connected to their old bus.
Expand Down Expand Up @@ -575,9 +581,13 @@ def get_storages_bus_global(self):

def _get_active_bus(self):
self.activated_bus[:, :] = False
tmp = self.current_topo.values - 1
tmp = self.current_topo.values - 1 # TODO global to local !
is_el_conn = tmp >= 0
self.activated_bus[self.big_topo_to_subid[is_el_conn], tmp[is_el_conn]] = True
if type(self).shunts_data_available:
is_el_conn = self.current_shunt_bus.values >= 0
tmp = self.current_shunt_bus.values - 1
self.activated_bus[type(self).shunt_to_subid[is_el_conn], tmp[is_el_conn]] = True

def update_state(self, powerline_disconnected):
"""
Expand Down
106 changes: 73 additions & 33 deletions grid2op/Action/baseAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,9 @@ def as_serializable_dict(self) -> dict:
Once you have these dictionnary, you can use them to build back the action from the action space.
.. warning::
This function does not work correctly with version of grid2op lower (or equal to) 1.9.5
Examples
---------
Expand Down Expand Up @@ -628,7 +631,8 @@ def as_serializable_dict(self) -> dict:
self._aux_serialize_add_key_change("gen_change_bus", "generators_id", res["change_bus"])
self._aux_serialize_add_key_change("line_or_change_bus", "lines_or_id", res["change_bus"])
self._aux_serialize_add_key_change("line_ex_change_bus", "lines_ex_id", res["change_bus"])
self._aux_serialize_add_key_change("storage_change_bus", "storages_id", res["change_bus"])
if hasattr(type(self), "n_storage") and type(self).n_storage:
self._aux_serialize_add_key_change("storage_change_bus", "storages_id", res["change_bus"])
if not res["change_bus"]:
del res["change_bus"]

Expand All @@ -646,7 +650,8 @@ def as_serializable_dict(self) -> dict:
self._aux_serialize_add_key_set("gen_set_bus", "generators_id", res["set_bus"])
self._aux_serialize_add_key_set("line_or_set_bus", "lines_or_id", res["set_bus"])
self._aux_serialize_add_key_set("line_ex_set_bus", "lines_ex_id", res["set_bus"])
self._aux_serialize_add_key_set("storage_set_bus", "storages_id", res["set_bus"])
if hasattr(type(self), "n_storage") and type(self).n_storage:
self._aux_serialize_add_key_set("storage_set_bus", "storages_id", res["set_bus"])
if not res["set_bus"]:
del res["set_bus"]

Expand Down Expand Up @@ -1244,10 +1249,10 @@ def remove_line_status_from_topo(self,
- if a powerline is affected to a certain bus at one of its end with `set_bus` (for example
`set_bus` to 1 or 2) and at the same time disconnected (`set_line_status` is -1) then
the `set_bus` part is ignore to avoid `AmbiguousAction`
the `set_bus` part is ignored to avoid `AmbiguousAction`
- if a powerline is disconnect from its bus at one of its end with `set_bus` (for example
`set_bus` to -1) and at the same time reconnected (`set_line_status` is 1) then
the `set_bus` part is ignore to avoid `AmbiguousAction`
the `set_bus` part is ignored to avoid `AmbiguousAction`
- if a powerline is affected to a certain bus at one of its end with `change_bus` (`change_bus` is
``True``) and at the same time disconnected (`set_line_status` is -1) then
the `change_bus` part is ignore to avoid `AmbiguousAction`
Expand All @@ -1261,6 +1266,10 @@ def remove_line_status_from_topo(self,
.. note::
As from version 1.9.0 you are no longer forced to provide an observation if `check_cooldown=False`
.. warning::
For grid2op equal or lower to 1.9.5 this function was bugged in some corner cases. We highly recommend
upgrading if you use this function with these grid2op versions.
Examples
---------
Expand Down Expand Up @@ -1335,44 +1344,75 @@ def remove_line_status_from_topo(self,

# remove the "set" part that would cause a reconnection
mask_reco = np.full(cls.dim_topo, fill_value=False)
reco_or_ = np.full(cls.n_line, fill_value=False)
reco_or_[(self._set_topo_vect[cls.line_or_pos_topo_vect] > 0) &
disconnected & line_under_cooldown] = True
mask_reco[cls.line_or_pos_topo_vect] = reco_or_

reco_ex_ = np.full(cls.n_line, fill_value=False)
reco_ex_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] > 0) &
disconnected & line_under_cooldown] = True
mask_reco[cls.line_ex_pos_topo_vect] = reco_ex_
if check_cooldown:
reco_or_ = np.full(cls.n_line, fill_value=False)
reco_or_[(self._set_topo_vect[cls.line_or_pos_topo_vect] > 0) &
disconnected & line_under_cooldown] = True
mask_reco[cls.line_or_pos_topo_vect] = reco_or_

reco_ex_ = np.full(cls.n_line, fill_value=False)
reco_ex_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] > 0) &
disconnected & line_under_cooldown] = True
mask_reco[cls.line_ex_pos_topo_vect] = reco_ex_

# do not reconnect powerline that will be disconnected
reco_or_and_disco_ = np.full(cls.n_line, fill_value=False)
reco_or_and_disco_[(self._set_topo_vect[cls.line_or_pos_topo_vect] > 0) & (self._set_topo_vect[cls.line_ex_pos_topo_vect] < 0)] = True
reco_or_and_disco_[(self._set_topo_vect[cls.line_or_pos_topo_vect] > 0) & (self._set_line_status < 0)] = True
mask_reco[cls.line_or_pos_topo_vect] |= reco_or_and_disco_

reco_ex_and_disco_ = np.full(cls.n_line, fill_value=False)
reco_ex_and_disco_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] > 0) & (self._set_topo_vect[cls.line_or_pos_topo_vect] < 0)] = True
reco_ex_and_disco_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] > 0) & (self._set_line_status < 0)] = True
mask_reco[cls.line_ex_pos_topo_vect] |= reco_ex_and_disco_
# and now remove the change from the set_bus
self._set_topo_vect[mask_reco] = 0


# remove the "set" that would cause a disconnection
mask_disco = np.full(cls.dim_topo, fill_value=False)
reco_or_ = np.full(cls.n_line, fill_value=False)
reco_or_[(self._set_topo_vect[cls.line_or_pos_topo_vect] < 0) &
connected & line_under_cooldown] = True
mask_disco[cls.line_or_pos_topo_vect] = reco_or_

reco_ex_ = np.full(cls.n_line, fill_value=False)
reco_ex_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] < 0) &
connected & line_under_cooldown] = True
mask_disco[cls.line_ex_pos_topo_vect] = reco_ex_

if check_cooldown:
disco_or_ = np.full(cls.n_line, fill_value=False)
disco_or_[(self._set_topo_vect[cls.line_or_pos_topo_vect] < 0) &
connected & line_under_cooldown] = True
mask_disco[cls.line_or_pos_topo_vect] = disco_or_

disco_ex_ = np.full(cls.n_line, fill_value=False)
disco_ex_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] < 0) &
connected & line_under_cooldown] = True
mask_disco[cls.line_ex_pos_topo_vect] = disco_ex_

disco_or_and_reco_ = np.full(cls.n_line, fill_value=False)
disco_or_and_reco_[(self._set_topo_vect[cls.line_or_pos_topo_vect] < 0) & (self._set_line_status > 0)] = True
mask_disco[cls.line_or_pos_topo_vect] |= disco_or_and_reco_

disco_ex_and_reco_ = np.full(cls.n_line, fill_value=False)
disco_ex_and_reco_[(self._set_topo_vect[cls.line_ex_pos_topo_vect] < 0) & (self._set_line_status > 0)] = True
mask_disco[cls.line_ex_pos_topo_vect] |= disco_ex_and_reco_
self._set_topo_vect[mask_disco] = 0

# remove the "change" part when powerlines is disconnected
mask_disco = np.full(cls.dim_topo, fill_value=False)
reco_or_ = np.full(cls.n_line, fill_value=False)
reco_or_[self._change_bus_vect[cls.line_or_pos_topo_vect] &
disconnected & line_under_cooldown] = True
mask_disco[cls.line_or_pos_topo_vect] = reco_or_

reco_ex_ = np.full(cls.n_line, fill_value=False)
reco_ex_[self._change_bus_vect[cls.line_ex_pos_topo_vect] &
disconnected & line_under_cooldown] = True
mask_disco[cls.line_ex_pos_topo_vect] = reco_ex_

if check_cooldown:
reco_or_ = np.full(cls.n_line, fill_value=False)
reco_or_[self._change_bus_vect[cls.line_or_pos_topo_vect] &
disconnected & line_under_cooldown] = True
mask_disco[cls.line_or_pos_topo_vect] = reco_or_

reco_ex_ = np.full(cls.n_line, fill_value=False)
reco_ex_[self._change_bus_vect[cls.line_ex_pos_topo_vect] &
disconnected & line_under_cooldown] = True
mask_disco[cls.line_ex_pos_topo_vect] = reco_ex_

# do not change bus of powerline that will be disconnected
reco_or_and_disco_ = np.full(cls.n_line, fill_value=False)
reco_or_and_disco_[(self._change_bus_vect[cls.line_or_pos_topo_vect]) & (self._set_line_status < 0)] = True
mask_disco[cls.line_or_pos_topo_vect] |= reco_or_and_disco_
reco_ex_and_disco_ = np.full(cls.n_line, fill_value=False)
reco_ex_and_disco_[(self._change_bus_vect[cls.line_ex_pos_topo_vect]) & (self._set_line_status < 0)] = True
mask_disco[cls.line_ex_pos_topo_vect] |= reco_ex_and_disco_

# "erase" the change_bus for concerned powerlines
self._change_bus_vect[mask_disco] = False

return self
Expand Down
18 changes: 9 additions & 9 deletions grid2op/Backend/pandaPowerBackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,9 +1033,8 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]:

if is_dc:
pp.rundcpp(self._grid, check_connectivity=False, init="flat")
self._nb_bus_before = (
None # if dc i start normally next time i call an ac powerflow
)
# if dc i start normally next time i call an ac powerflow
self._nb_bus_before = None
else:
pp.runpp(
self._grid,
Expand All @@ -1055,7 +1054,11 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]:
# TODO see if there is a better way here -> do not handle this here, but rather in Backend._next_grid_state
# sometimes pandapower does not detect divergence and put Nan.
raise pp.powerflow.LoadflowNotConverged("Divergence due to Nan values in res_gen table.")


# if a connected bus has a no voltage, it's a divergence (grid was not connected)
if self._grid.res_bus.loc[self._grid.bus["in_service"]]["va_degree"].isnull().any():
raise pp.powerflow.LoadflowNotConverged("Isolated bus")

(
self.prod_p[:],
self.prod_q[:],
Expand All @@ -1068,8 +1071,9 @@ def runpf(self, is_dc : bool=False) -> Tuple[bool, Union[Exception, None]]:
self.load_v[:],
self.load_theta[:],
) = self._loads_info()

if not is_dc:
if not np.all(np.isfinite(self.load_v)):
if not np.isfinite(self.load_v).all():
# TODO see if there is a better way here
# some loads are disconnected: it's a game over case!
raise pp.powerflow.LoadflowNotConverged("Isolated load")
Expand Down Expand Up @@ -1512,10 +1516,6 @@ def shunt_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
shunt_bus = type(self).global_bus_to_local(self._grid.shunt["bus"].values, self.shunt_to_subid)
shunt_v[~self._grid.shunt["in_service"].values] = 0
shunt_bus[~self._grid.shunt["in_service"].values] = -1
# handle shunt alone on a bus (in this case it should probably diverge...)
alone = ~np.isfinite(shunt_v)
shunt_v[alone] = 0
shunt_bus[alone] = -1
return shunt_p, shunt_q, shunt_v, shunt_bus

def storages_info(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
Expand Down
Loading

0 comments on commit 85611cb

Please sign in to comment.