Skip to content

Commit

Permalink
Merge pull request #562 from BDonnot/bd_dev
Browse files Browse the repository at this point in the history
Bd dev
  • Loading branch information
BDonnot authored Dec 1, 2023
2 parents f4cd921 + f932d7c commit c30f1ce
Show file tree
Hide file tree
Showing 46 changed files with 453 additions and 208 deletions.
20 changes: 19 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,26 @@ Change Log
- [???] properly model interconnecting powerlines


[1.9.7] - 20xx-yy-zz
[1.9.7] - 2023-12-0z
----------------------
- [BREAKING] removal of the `grid2op/Exceptions/PowerflowExceptions.py` file and move the
`DivergingPowerflow` as part of the BackendException. If you imported (to be avoided)
with `from grid2op.Exceptions.PowerflowExceptions import PowerflowExceptions`
simply do `from grid2op.Exceptions import PowerflowExceptions` and nothing
will change.
- [BREAKING] rename with filename starting with lowercase all the files in the "`Exceptions`",
module. 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.Exceptions.XXX import PowerflowExceptions` (which you should not have done in the first place)
just do `from grid2op.Exceptions import PowerflowExceptions`. Expect other changes like this for other grid2op modules
in the near future.
- [BREAKING] change the `gridobj_cls.shape()` and `gridobj_cls.dtype()` to `gridobj_cls.shapes()` and `gridobj_cls.dtypes()`
to be more clear when dealing with action_space and observation_space (where `shape` and `dtype` are attribute and not functions)
This change means you can still use `act.shape()` and `act.dtype()` but that `act_space.shape` and `act_space.dtype` are now
clearly properties (and NOT attribute). For the old function `gridobj_cls.dtype()` you can now use `gridobj_cls.dtypes()`
- [FIXED] issue https://github.com/rte-france/Grid2Op/issues/561 (indent issue)
- [FIXED] issue https://github.com/rte-france/Grid2Op/issues/550 : issue with `shunts_data_available` now better handled
- [IMPROVED] the function to check the backend interface now also check that
the `topo_vect` returns value between 1 and 2.
- [IMPROVED] the function to check backend now also check the `topo_vect`
Expand Down
16 changes: 10 additions & 6 deletions grid2op/Action/_backendAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ def __init__(self):
)

# shunts
if self.shunts_data_available:
cls = type(self)
if cls.shunts_data_available:
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)
Expand Down Expand Up @@ -266,7 +267,8 @@ def __deepcopy__(self, memodict={}):
res.storage_power.copy(self.storage_power)
res.activated_bus[:, :] = self.activated_bus
# res.big_topo_to_subid[:] = self.big_topo_to_subid # cste
if self.shunts_data_available:
cls = type(self)
if cls.shunts_data_available:
res.shunt_p.copy(self.shunt_p)
res.shunt_q.copy(self.shunt_q)
res.shunt_bus.copy(self.shunt_bus)
Expand Down Expand Up @@ -307,7 +309,8 @@ def reorder(self, no_load, no_gen, no_topo, no_storage, no_shunt):

self.storage_power.reorder(no_storage)

if self.shunts_data_available:
cls = type(self)
if cls.shunts_data_available:
self.shunt_p.reorder(no_shunt)
self.shunt_q.reorder(no_shunt)
self.shunt_bus.reorder(no_shunt)
Expand All @@ -331,7 +334,8 @@ def reset(self):
self.storage_power.values[:] = 0.0

# shunts
if self.shunts_data_available:
cls = type(self)
if cls.shunts_data_available:
self.shunt_p.reset()
self.shunt_q.reset()
self.shunt_bus.reset()
Expand Down Expand Up @@ -411,7 +415,7 @@ def __iadd__(self, other):
# II shunts
if type(self).shunts_data_available:
shunts = {}
if other.shunts_data_available:
if type(other).shunts_data_available:
shunts["shunt_p"] = other.shunt_p
shunts["shunt_q"] = other.shunt_q
shunts["shunt_bus"] = other.shunt_bus
Expand Down Expand Up @@ -515,7 +519,7 @@ def __call__(self):
)
topo = self.current_topo
shunts = None
if self.shunts_data_available:
if type(self).shunts_data_available:
shunts = self.shunt_p, self.shunt_q, self.shunt_bus
self._get_active_bus()
return self.activated_bus, injections, topo, shunts
Expand Down
24 changes: 15 additions & 9 deletions grid2op/Action/baseAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ def __init__(self):
self._subs_impacted = None

# shunts
if self.shunts_data_available:
if type(self).shunts_data_available:
self.shunt_p = np.full(
shape=self.n_shunt, fill_value=np.NaN, dtype=dt_float
)
Expand Down Expand Up @@ -495,6 +495,12 @@ def copy(self) -> "BaseAction":
# sometimes this method is used...
return self.__deepcopy__()

def shape(self):
return type(self).shapes()

def dtype(self):
return type(self).dtypes()

def _aux_copy(self, other):
attr_simple = [
"_modif_inj",
Expand Down Expand Up @@ -524,7 +530,7 @@ def _aux_copy(self, other):
"_raise_alert",
]

if self.shunts_data_available:
if type(self).shunts_data_available:
attr_vect += ["shunt_p", "shunt_q", "shunt_bus"]

for attr_nm in attr_simple:
Expand Down Expand Up @@ -1050,7 +1056,7 @@ def __eq__(self, other) -> bool:
return False

# shunts are the same
if self.shunts_data_available:
if type(self).shunts_data_available:
if self.n_shunt != other.n_shunt:
return False
is_ok_me = np.isfinite(self.shunt_p)
Expand Down Expand Up @@ -1455,7 +1461,7 @@ def reset(self):
self._subs_impacted = None

# shunts
if self.shunts_data_available:
if type(self).shunts_data_available:
self.shunt_p[:] = np.NaN
self.shunt_q[:] = np.NaN
self.shunt_bus[:] = 0
Expand Down Expand Up @@ -1631,7 +1637,7 @@ def __iadd__(self, other):
self._assign_iadd_or_warn("_change_bus_vect", me_change)

# shunts
if self.shunts_data_available:
if type(self).shunts_data_available:
val = other.shunt_p
ok_ind = np.isfinite(val)
shunt_p = 1.0 * self.shunt_p
Expand Down Expand Up @@ -1763,7 +1769,7 @@ def __call__(self) -> Tuple[dict, np.ndarray, np.ndarray, np.ndarray, np.ndarray
storage_power = self._storage_power
# remark: curtailment is handled by an algorithm in the environment, so don't need to be returned here
shunts = {}
if self.shunts_data_available:
if type(self).shunts_data_available:
shunts["shunt_p"] = self.shunt_p
shunts["shunt_q"] = self.shunt_q
shunts["shunt_bus"] = self.shunt_bus
Expand All @@ -1780,7 +1786,7 @@ def __call__(self) -> Tuple[dict, np.ndarray, np.ndarray, np.ndarray, np.ndarray
)

def _digest_shunt(self, dict_):
if not self.shunts_data_available:
if not type(self).shunts_data_available:
return

if "shunt" in dict_:
Expand Down Expand Up @@ -2664,7 +2670,7 @@ def _check_for_ambiguity(self):
"which it is connected. This is ambiguous. You must *set* this bus instead."
)

if self.shunts_data_available:
if type(self).shunts_data_available:
if self.shunt_p.shape[0] != self.n_shunt:
raise IncorrectNumberOfElements(
"Incorrect number of shunt (for shunt_p) in your action."
Expand Down Expand Up @@ -3396,7 +3402,7 @@ def get_types(self) -> Tuple[bool, bool, bool, bool, bool, bool, bool]:
"""
injection = "load_p" in self._dict_inj or "prod_p" in self._dict_inj
voltage = "prod_v" in self._dict_inj
if self.shunts_data_available:
if type(self).shunts_data_available:
voltage = voltage or np.isfinite(self.shunt_p).any()
voltage = voltage or np.isfinite(self.shunt_q).any()
voltage = voltage or (self.shunt_bus != 0).any()
Expand Down
4 changes: 2 additions & 2 deletions grid2op/Action/voltageOnlyAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def __init__(self):
"""
BaseAction.__init__(self)

if VoltageOnlyAction._shunt_added is False and self.shunts_data_available:
if VoltageOnlyAction._shunt_added is False and type(self).shunts_data_available:
VoltageOnlyAction.attr_list_vect += ["shunt_p", "shunt_q", "shunt_bus"]
VoltageOnlyAction.authorized_keys.add("shunt")
VoltageOnlyAction._shunt_added = True
Expand All @@ -61,7 +61,7 @@ def _check_dict(self):
"""
if self._dict_inj:
for el in self._dict_inj:
if el not in self.attr_list_vect:
if el not in type(self).attr_list_vect:
raise AmbiguousAction(
'Impossible to modify something different than "prod_v" using '
'"VoltageOnlyAction" action.'
Expand Down
21 changes: 11 additions & 10 deletions grid2op/Backend/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,15 @@
from grid2op.dtypes import dt_int, dt_float, dt_bool
from grid2op.Exceptions import (
EnvError,
DivergingPowerFlow,
IncorrectNumberOfElements,
IncorrectNumberOfLoads,
)
from grid2op.Exceptions import (
IncorrectNumberOfGenerators,
BackendError,
IncorrectNumberOfLines,
DivergingPowerflow,
Grid2OpException,
)
from grid2op.Space import GridObjects
from grid2op.Exceptions import Grid2OpException


# TODO method to get V and theta at each bus, could be in the same shape as check_kirchoff
Expand Down Expand Up @@ -941,7 +939,7 @@ def _runpf_with_diverging_exception(self, is_dc : bool) -> Optional[Exception]:
Raises
------
exc_: :class:`grid2op.Exceptions.DivergingPowerFlow`
exc_: :class:`grid2op.Exceptions.DivergingPowerflow`
In case of divergence of the powerflow
"""
Expand All @@ -952,13 +950,13 @@ def _runpf_with_diverging_exception(self, is_dc : bool) -> Optional[Exception]:
except Grid2OpException as exc_:
exc_me = exc_
except Exception as exc_:
exc_me = DivergingPowerFlow(
exc_me = DivergingPowerflow(
f" An unexpected error occurred during the computation of the powerflow."
f"The error is: \n {exc_} \n. This is game over"
)

if not conv and exc_me is None:
exc_me = DivergingPowerFlow(
exc_me = DivergingPowerflow(
"GAME OVER: Powerflow has diverged during computation "
"or a load has been disconnected or a generator has been disconnected."
)
Expand Down Expand Up @@ -1820,7 +1818,9 @@ def update_from_obs(self,
sh_q[~shunt_co] = np.NaN
dict_["shunt"]["shunt_p"] = sh_p
dict_["shunt"]["shunt_q"] = sh_q
act.update(dict_)
elif type(self).shunts_data_available and not type(obs).shunts_data_available:
warnings.warn("Backend supports shunt but not the observation. This behaviour is non standard.")
act.update(dict_)
backend_action += act
self.apply_action(backend_action)

Expand Down Expand Up @@ -1854,8 +1854,9 @@ def assert_grid_correct(self) -> None:
)
# reset the attribute of the grid2op.Backend.Backend class
# that can be messed up with depending on the initialization of the backend
Backend._clear_class_attribute()
orig_type._clear_class_attribute()
Backend._clear_class_attribute() # reset totally the grid2op Backend type
# orig_type._clear_class_attribute()
orig_type._clear_grid_dependant_class_attributes() # only reset the attributes that could be modified by user

my_cls = type(self)
my_cls.my_bk_act_class = _BackendAction.init_grid(my_cls)
Expand Down
5 changes: 3 additions & 2 deletions grid2op/Backend/pandaPowerBackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ class PandaPowerBackend(Backend):
# and use "env" as "any open ai gym" environment.
"""

shunts_data_available = True

def __init__(
self,
detailed_infos_for_cascading_failures : bool=False,
Expand Down Expand Up @@ -713,7 +714,7 @@ def _init_private_attrs(self) -> None:
self._sh_vnkv = self._grid.bus["vn_kv"][self.shunt_to_subid].values.astype(
dt_float
)
self.shunts_data_available = True
# self.shunts_data_available = True # TODO shunts_data_available

# store the topoid -> objid
self._big_topo_to_obj = [(None, None) for _ in range(self.dim_topo)]
Expand Down
6 changes: 3 additions & 3 deletions grid2op/Chronics/fromOneEpisodeData.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ class FromOneEpisodeData(GridValue):
env = grid2op.make(env_name,
chronics_class=FromOneEpisodeData,
data_feeding_kwargs={"ep_data": ep_data},
data_feeding_kwargs={"ep_data": ep_data,
"list_perfect_forecasts": (5, 10, 15)},
opponent_class=FromEpisodeDataOpponent,
opponent_attack_cooldown=1,
list_perfect_forecasts=(5, 10, 15)
)
# it creates an environment with perfect forecasts available for the next step (5),
# the step afterwards (10) and again the following one (15)
Expand Down Expand Up @@ -327,7 +327,6 @@ def forecasts(self):
"""
if not self.list_perfect_forecasts:
return []

res = []
for h_id, h in enumerate(self.list_perfect_forecasts):
res_d = {}
Expand All @@ -342,6 +341,7 @@ def forecasts(self):

def get_kwargs(self, dict_):
dict_["ep_data"] = copy.deepcopy(self._episode_data)
# dict_["list_perfect_forecasts"] = copy.deepcopy(self.list_perfect_forecasts)
return dict_

def get_id(self) -> str:
Expand Down
32 changes: 22 additions & 10 deletions grid2op/Converter/BackendConverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,12 +268,18 @@ def _init_myself(self):
nm="storage",
)

if type(self.source_backend).shunts_data_available and not type(self.target_backend).shunts_data_available:
raise Grid2OpException("Source backend supports shunts and not target backend. This is not handled")
elif type(self.target_backend).shunts_data_available and not type(self.source_backend).shunts_data_available:
raise Grid2OpException("Target backend supports shunts and not source backend. This is not handled")

# TODO shunts data available
# shunt are available if both source and target provide it
self.shunts_data_available = (
self.source_backend.shunts_data_available
and self.target_backend.shunts_data_available
type(self).shunts_data_available = (
type(self.source_backend).shunts_data_available
and type(self.target_backend).shunts_data_available
)
if self.shunts_data_available:
if type(self).shunts_data_available:
self._shunt_tg2sr = np.full(self.n_shunt, fill_value=-1, dtype=dt_int)
self._shunt_sr2tg = np.full(self.n_shunt, fill_value=-1, dtype=dt_int)
# automatic mode
Expand Down Expand Up @@ -408,11 +414,17 @@ def assert_grid_correct(self):
sr_cls.set_env_name(env_name)

# handle specifc case of shunt data:
if not self.target_backend.shunts_data_available:
if not type(self.target_backend).shunts_data_available:
if type(self.source_backend).shunts_data_available:
raise Grid2OpException("Impossible to use a converter when one of the backend "
"supports shunt and the other not at the moment.")
# TODO shunts_data_available: you need ideally to create a different class
# for the backend that does not support it.

# disable the shunt data in grid2op.
self.source_backend.shunts_data_available = False
self.source_backend.n_shunt = None
self.source_backend.name_shunt = np.empty(0, dtype=str)
# type(self.source_backend).shunts_data_available = False
# type(self.source_backend).n_shunt = None
# type(self.source_backend).name_shunt = np.empty(0, dtype=str)

self._init_class_attr(obj=self.source_backend)
if self.path_redisp is not None:
Expand Down Expand Up @@ -494,7 +506,7 @@ def assert_grid_correct(self):
assert np.all(sorted(self._topo_tg2sr[topo_sr2tg_without_storage]) == target_without_storage)
self._topo_sr2tg = topo_sr2tg_without_storage

if self.shunts_data_available:
if type(self).shunts_data_available:
self._check_both_consistent(self._shunt_tg2sr, self._shunt_sr2tg)

# finally check that powergrids are identical (up to the env name)
Expand Down Expand Up @@ -716,7 +728,7 @@ def get_action_to_set(self):
act._set_line_status[:] = act._set_line_status[line_vect]
act._switch_line_status[:] = act._switch_line_status[line_vect]

if act.shunt_added and act.shunts_data_available:
if act.shunt_added and type(act).shunts_data_available:
shunt_vect = self._shunt_sr2tg
act.shunt_p[:] = act.shunt_p[shunt_vect]
act.shunt_q[:] = act.shunt_q[shunt_vect]
Expand Down
Loading

0 comments on commit c30f1ce

Please sign in to comment.