Skip to content

Commit

Permalink
Merge pull request #653 from BDonnot/bd_dev
Browse files Browse the repository at this point in the history
Some new usefull features
  • Loading branch information
BDonnot authored Oct 22, 2024
2 parents e7422c6 + 50266c9 commit ef563d5
Show file tree
Hide file tree
Showing 8 changed files with 413 additions and 13 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Work "in progress"

General grid2op improvments:

- ill formed docstring in the BaseAction module
- remove pandapower dependency (have a way to install grid2op without pandapower)
- better logging
- have functions that automatically computes topo_vect and switch_state in the backend
Expand All @@ -23,6 +24,7 @@ General grid2op improvments:
- in parallel distribute the loading of the time series if using a `MultifolderWithCache`
- Code and test the "load from disk" method
- add a "plot action" method
- does not read every data of the backend if not used

Better multi processing support:

Expand Down Expand Up @@ -95,6 +97,16 @@ Native multi agents support:
- cf ad-hoc branch (dev-multiagents)
- properly model interconnecting powerlines

[1.11.0] - 202x-yy-zz
-----------------------
- [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(...)`
or , `gridobj.get_storage_info(...)`
- [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

[1.10.4] - 2024-10-15
-------------------------
- [FIXED] new pypi link (no change in code)
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
author = 'Benjamin Donnot'

# The full version, including alpha/beta/rc tags
release = '1.10.5'
version = '1.10'
release = '1.11.0.dev0'
version = '1.11'


# -- General configuration ---------------------------------------------------
Expand Down
25 changes: 22 additions & 3 deletions grid2op/Action/baseAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -1926,7 +1926,26 @@ def _digest_injection(self, dict_):
self._modif_inj = True
for k in tmp_d:
if k in self.attr_list_set:
self._dict_inj[k] = np.array(tmp_d[k]).astype(dt_float)
possible_vals = tmp_d[k]
if isinstance(possible_vals, dict):
if k == "load_p" or k == "load_q":
nb_el = type(self).n_load
el_nms = type(self).name_load
elif k == "prod_p" or k == "prod_v":
nb_el = type(self).n_gen
el_nms = type(self).name_gen
else:
raise AmbiguousAction(f"Impossible modify the injection with the key {k}")
vals = np.full(nb_el, dtype=dt_float, fill_value=np.nan)
for el_nm, el_val in possible_vals.items():
el_ids = (el_nms == el_nm).nonzero()[0]
if len(el_ids) == 0:
raise AmbiguousAction(f"No element named {el_nm} for key {k} when trying to modify the injection")
elif len(el_ids) >= 2:
raise AmbiguousAction(f"More than one element named {el_nm} for key {k} when trying to modify the injection")
vals[el_ids[0]] = dt_float(el_val)
else:
self._dict_inj[k] = np.array(tmp_d[k]).astype(dt_float)
# TODO check the size based on the input data !
else:
warn = (
Expand Down Expand Up @@ -3949,11 +3968,11 @@ def _aux_affect_object_int(

if new_bus < min_val:
raise IllegalAction(
f"new_bus should be between {min_val} and {max_val}"
f"new_bus should be between {min_val} and {max_val} found {new_bus}, check element id {el_id}"
)
if new_bus > max_val:
raise IllegalAction(
f"new_bus should be between {min_val} and {max_val}"
f"new_bus should be between {min_val} and {max_val} found {new_bus}, check element id {el_id}"
)

if isinstance(el_id, (float, dt_float, np.float64)):
Expand Down
17 changes: 10 additions & 7 deletions grid2op/Environment/baseEnv.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ def foo(manager):

#: this are the keys of the dictionnary `options`
#: that can be used when calling `env.reset(..., options={})`
KEYS_RESET_OPTIONS = {"time serie id", "init state", "init ts", "max step"}
KEYS_RESET_OPTIONS = {"time serie id", "init state", "init ts", "max step", "thermal limit"}

def __init__(
self,
Expand Down Expand Up @@ -1456,7 +1456,10 @@ def reset(self,
# change also the reward used in simulate
self._observation_space.change_reward(self._reward_helper.template_reward)
self.__new_reward_func = None


if options is not None and "thermal limit" in options:
self.set_thermal_limit(options["thermal limit"])

self._last_obs = None

if options is not None and "time serie id" in options:
Expand Down Expand Up @@ -1823,9 +1826,10 @@ def set_thermal_limit(self, thermal_limit):
except Exception as exc_:
raise Grid2OpException(
f"When setting thermal limit with a dictionary, the keys should be "
f"the values of the thermal limit (in amps) you provided something that "
f'cannot be converted to a float. Error was "{exc_}".'
)
f"the names of the lines and the values the thermal limit (in amps) "
f"you provided something that "
f'cannot be converted to a float {type(val)}'
) from exc_
tmp[ind_line] = val_fl

elif isinstance(thermal_limit, (np.ndarray, list)):
Expand All @@ -1834,8 +1838,7 @@ def set_thermal_limit(self, thermal_limit):
except Exception as exc_:
raise Grid2OpException(
f"Impossible to convert the vector as input into a 1d numpy float array. "
f"Error was: \n {exc_}"
)
) from exc_
if tmp.shape[0] != self.n_line:
raise Grid2OpException(
"Attempt to set thermal limit on {} powerlines while there are {}"
Expand Down
184 changes: 184 additions & 0 deletions grid2op/Space/GridObjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -5045,3 +5045,187 @@ class {cls.__name__}({cls._INIT_GRID_CLS.__name__}):
"""
return res

@classmethod
def get_line_info(cls, *, line_id : Optional[int]=None, line_name : Optional[str]=None) -> Tuple[int, str, int, int]:
"""
.. versionadded:: 1.11.0
This function returns some information about a powerline, either given by its
name (with the kwargs `line_name` or by its id, with the kwargs `line_id`)
It does not accept any positional argument (only key-word argument) and you need
to specify only one of `line_name` OR `line_id` but not both.
.. info::
`line_id` is a python id, so it should be between `0` and `env.n_line - 1`
Examples
----------
You can use it like this:
.. code-block:: python
import grid2op
env_name = "l2rpn_case14_sandbox"
env = grid2op.make(env_name)
line_id, line_name, subor_id, subex_id = type(env).get_line_info("5_12_9")
line_id, line_name, subor_id, subex_id = type(env).get_line_info(15)
"""
line_id, line_name = cls._aux_info("line",
cls.name_line,
cls.n_line,
obj_id=line_id,
obj_name=line_name)
sub_or = cls.line_or_to_subid[line_id]
sub_ex = cls.line_ex_to_subid[line_id]
return line_id, line_name, sub_or, sub_ex

@classmethod
def _aux_info(cls,
el_nm,
cls_name_el,
cls_n_el,
obj_id : Optional[int]=None,
obj_name : Optional[str]=None) -> Tuple[int, str, int]:
if obj_id is None and obj_name is None:
raise RuntimeError(f"You should provide at least `{el_nm}_name` or `{el_nm}_id` key-word argument")
if obj_id is not None and obj_name is not None:
raise RuntimeError(f"You should provide only one of `{el_nm}_name` or `{el_nm}_id` key-word argument, you provided both")
if obj_id is None:
try:
obj_name = str(obj_name)
except TypeError as exc_:
raise RuntimeError(f"`{el_nm}_name` should be convertible to a string, you provided {type(obj_name)}") from exc_
id_ = (cls_name_el == obj_name).nonzero()[0]
if len(id_) == 0:
raise RuntimeError(f"No {el_nm} named {obj_name} is found on your grid")
if len(id_) >= 2:
raise RuntimeError(f"Multiple {el_nm}s are named {obj_name} on your grid.")
obj_id = int(id_[0])
if obj_name is None:
try:
obj_id = int(obj_id)
except TypeError as exc_:
raise RuntimeError(f"`{el_nm}_id` should be convertible to an int, you provided {type(obj_id)}") from exc_
if obj_id < 0:
raise RuntimeError(f"`{el_nm}_id` should be >=0, you provided {obj_id}")
if obj_id >= cls_n_el:
raise RuntimeError(f"`{el_nm}_id` should not exceed {cls_n_el}, the number of {el_nm}s on the grid. "
f"You provided `{el_nm}_id`={obj_id}")
obj_name = cls_name_el[obj_id]
return obj_id, obj_name

@classmethod
def get_load_info(cls, *, load_id : Optional[int]=None, load_name : Optional[str]=None) -> Tuple[int, str, int]:
"""
.. versionadded:: 1.11.0
This function returns some information about a load, either given by its
name (with the kwargs `load_name` or by its id, with the kwargs `load_id`)
It does not accept any positional argument (only key-word argument) and you need
to specify only one of `load_name` OR `load_id` but not both.
.. info::
`load_id` is a python id, so it should be between `0` and `env.n_load - 1`
Examples
----------
You can use it like this:
.. code-block:: python
import grid2op
env_name = "l2rpn_case14_sandbox"
env = grid2op.make(env_name)
load_id, load_name, sub_id = type(env).get_load_info("load_2_1")
load_id, load_name, sub_id = type(env).get_load_info(3)
"""
load_id, load_name = cls._aux_info("load",
cls.name_load,
cls.n_load,
obj_id=load_id,
obj_name=load_name)
sub_id = cls.load_to_subid[load_id]
return load_id, load_name, sub_id

@classmethod
def get_gen_info(cls, *, gen_id : Optional[int]=None, gen_name : Optional[str]=None) -> Tuple[int, str, int]:
"""
.. versionadded:: 1.11.0
This function returns some information about a generator, either given by its
name (with the kwargs `gen_name` or by its id, with the kwargs `gen_id`)
It does not accept any positional argument (only key-word argument) and you need
to specify only one of `gen_name` OR `gen_id` but not both.
.. info::
`gen_id` is a python id, so it should be between `0` and `env.n_gen - 1`
Examples
----------
You can use it like this:
.. code-block:: python
import grid2op
env_name = "l2rpn_case14_sandbox"
env = grid2op.make(env_name)
gen_id, gen_name, sub_id = type(env).get_gen_info("gen_5_2")
gen_id, gen_name, sub_id = type(env).get_gen_info(3)
"""
gen_id, gen_name = cls._aux_info("gen",
cls.name_gen,
cls.n_gen,
obj_id=gen_id,
obj_name=gen_name)
sub_id = cls.gen_to_subid[gen_id]
return gen_id, gen_name, sub_id

@classmethod
def get_storage_info(cls, *, storage_id : Optional[int]=None, storage_name : Optional[str]=None) -> Tuple[int, str, int]:
"""
.. versionadded:: 1.11.0
This function returns some information about a generator, either given by its
name (with the kwargs `storage_name` or by its id, with the kwargs `storage_id`)
It does not accept any positional argument (only key-word argument) and you need
to specify only one of `storage_name` OR `storage_id` but not both.
.. info::
`storage_id` is a python id, so it should be between `0` and `env.n_storage - 1`
Examples
----------
You can use it like this:
.. code-block:: python
import grid2op
env_name = "educ_case14_storage"
env = grid2op.make(env_name, test=True)
storage_id, storage_name, sub_id = type(env).get_storage_info("storage_7_1")
storage_id, storage_name, sub_id = type(env).get_storage_info(1)
"""
storage_id, storage_name = cls._aux_info("storage",
cls.name_storage,
cls.n_storage,
obj_id=storage_id,
obj_name=storage_name)
sub_id = cls.storage_to_subid[storage_id]
return storage_id, storage_name, sub_id
2 changes: 1 addition & 1 deletion grid2op/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
Grid2Op
"""
__version__ = '1.10.5'
__version__ = '1.11.0.dev0'

__all__ = [
"Action",
Expand Down
Loading

0 comments on commit ef563d5

Please sign in to comment.