Skip to content

Commit

Permalink
Merge pull request #605 from BDonnot/bd_dev
Browse files Browse the repository at this point in the history
Add 90% of the "init state" feature
  • Loading branch information
BDonnot authored May 3, 2024
2 parents 9a4ac6b + cc9fd62 commit 3b0b687
Show file tree
Hide file tree
Showing 116 changed files with 6,547 additions and 147 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ jobs:
grid2op.testinstall
legacy_lightsim:
executor: python38
executor: python38 # needs to be 38: whl of lightsim were not released for 3.10 at the time
resource_class: small
steps:
- checkout
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ jobs:
- name: Install wheel
run: |
pip3 install dist/*.whl --user
pip3 install dist/*.whl
pip freeze
- name: Check package can be imported
run: |
python3 -c "import grid2op"
python3 -c "from grid2op import *"
python3 -c "from grid2op.Action._backendAction import _BackendAction"
- name: Upload wheel
uses: actions/upload-artifact@v2
with:
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,11 @@ grid2op/tests/requirements.txt
grid2op/tests/venv_test_311/
issue_577/
junk.py
grid2op/tests/20240429_failed_tests.txt
grid2op/tests/20240429_failed_tests_small.txt
grid2op/tests/20240429_teq_test.txt
grid2op/tests/req_38_np121
test_make_2_envs.py

# profiling files
**.prof
20 changes: 18 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,28 @@ Change Log

[1.10.2] - 2024-xx-yy
-------------------------
- [BREAKING] the `runner.run_one_episode` now returns an extra first argument:
`chron_id, chron_name, cum_reward, timestep, max_ts = runner.run_one_episode()` which
is consistant with `runner.run(...)` (previously it returned only
`chron_name, cum_reward, timestep, max_ts = runner.run_one_episode()`)
- [BREAKING] the runner now has no `chronics_handler` attribute (`runner.chronics_handler`
is not defined)
- [ADDED] it is now possible to call `change_reward` directly from
an observation (no need to do it from the Observation Space)
- [ADDED] method to change the reward from the observation (observation_space
is not needed anymore): you can use `obs.change_reward`
- [ADDED] a way to automatically set the `experimental_read_from_local_dir` flags
(with automatic class creation). For now it is disable by default, but you can
activate it transparently (see doc)
- [ADDED] TODO the possibility to set the grid in an initial state (using an action) TODO
- [ADDED] possibility to set the grid to an initial state (using an action) when using the
"time series" classes. The supported classes are `GridStateFromFile` - and all its derivative,
`FromOneEpisodeData`, `FromMultiEpisodeData`, `FromNPY` and `FromHandlers`. The classes `ChangeNothing`
and `FromChronix2grid` are not supported at the moment.
- [ADDED] an "Handler" (`JSONInitStateHandler`) that can set the grid to an initial state (so as to make
compatible the `FromHandlers` time series class with this new feature)
- [FIXED] a small issue that could lead to having
"redispatching_unit_commitment_availble" flag set even if the redispatching
data was not loded correctly
data was not loaded correctly
- [FIXED] EducPandaPowerBackend now properly sends numpy array in the class attributes
(instead of pandas series)
- [FIXED] an issue when loading back data (with `EpisodeData`): when there were no storage units
Expand All @@ -52,6 +63,11 @@ Change Log
grid layout was set
- [FIXED] notebook 5 on loading back data with `EpisodeData`.
- [FIXED] converter between backends (could not handle more than 2 busbars)
- [FIXED] a bug in `BaseMultiProcessEnvironment`: set_filter had no impact
- [FIXED] an issue in the `Runner` (`self.chronics_handler` was sometimes used, sometimes not
and most of the time incorrectly)
- [FIXED] on `RemoteEnv` class (impact all multi process environment): the kwargs used to build then backend
where not used which could lead to"wrong" backends being used in the sub processes.
- [IMPROVED] documentation about `obs.simulate` to make it clearer the
difference between env.step and obs.simulate on some cases
- [IMPROVED] type hints on some methods of `GridObjects`
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.10.2.dev1'
release = '1.10.2.dev2'
version = '1.10'


Expand Down
27 changes: 11 additions & 16 deletions grid2op/Action/actionSpace.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import copy
from typing import Dict, List, Any, Literal

import grid2op
from grid2op.typing_variables import DICT_ACT_TYPING
from grid2op.Action.baseAction import BaseAction
from grid2op.Action.serializableActionSpace import SerializableActionSpace

Expand Down Expand Up @@ -74,22 +76,9 @@ def __init__(

def __call__(
self,
dict_: Dict[Literal["injection",
"hazards",
"maintenance",
"set_line_status",
"change_line_status",
"set_bus",
"change_bus",
"redispatch",
"set_storage",
"curtail",
"raise_alarm",
"raise_alert"], Any] = None,
dict_: DICT_ACT_TYPING = None,
check_legal: bool = False,
env: "grid2op.Environment.BaseEnv" = None,
*,
injection=None,
env: "grid2op.Environment.BaseEnv" = None
) -> BaseAction:
"""
This utility allows you to build a valid action, with the proper sizes if you provide it with a valid
Expand Down Expand Up @@ -132,9 +121,15 @@ def __call__(
An action that is valid and corresponds to what the agent want to do with the formalism defined in
see :func:`Action.udpate`.
Notes
-----
This function is not in the "SerializableActionSpace" because the
"legal_action" is not serialized. TODO ?
"""
# build the action
res = self.actionClass()
res : BaseAction = self.actionClass()

# update the action
res.update(dict_)
Expand Down
93 changes: 85 additions & 8 deletions grid2op/Action/baseAction.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
import numpy as np
import warnings
from typing import Tuple, Dict, Literal, Any, List


try:
from typing import Self
except ImportError:
from typing_extensions import Self

from packaging import version

import grid2op
from grid2op.typing_variables import DICT_ACT_TYPING
from grid2op.dtypes import dt_int, dt_bool, dt_float
from grid2op.Exceptions import *
from grid2op.Space import GridObjects
Expand Down Expand Up @@ -624,6 +628,7 @@ def as_serializable_dict(self) -> dict:
"""
res = {}
cls = type(self)
# bool elements
if self._modif_alert:
res["raise_alert"] = [
Expand All @@ -645,7 +650,7 @@ 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"])
if hasattr(type(self), "n_storage") and type(self).n_storage:
if hasattr(cls, "n_storage") and cls.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 @@ -664,7 +669,7 @@ 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"])
if hasattr(type(self), "n_storage") and type(self).n_storage:
if hasattr(cls, "n_storage") and cls.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 @@ -715,7 +720,7 @@ def as_serializable_dict(self) -> dict:
if not res["injection"]:
del res["injection"]

if type(self).shunts_data_available:
if cls.shunts_data_available:
res["shunt"] = {}
if np.isfinite(self.shunt_p).any():
res["shunt"]["shunt_p"] = [
Expand Down Expand Up @@ -2094,11 +2099,31 @@ def _digest_redispatching(self, dict_):

def _digest_storage(self, dict_):
if "set_storage" in dict_:
self.storage_p = dict_["set_storage"]

try:
self.storage_p = dict_["set_storage"]
except IllegalAction as exc_:
cls = type(self)
# only raise the error if I am not in compat mode
if cls.glop_version == grid2op.__version__:
raise exc_
else:
# TODO be more specific on the version
warnings.warn(f"Ignored error on storage units, because "
f"you are in a backward compatibility mode.")

def _digest_curtailment(self, dict_):
if "curtail" in dict_:
self.curtail = dict_["curtail"]
try:
self.curtail = dict_["curtail"]
except IllegalAction as exc_:
cls = type(self)
# only raise the error if I am not in compat mode
if cls.glop_version == grid2op.__version__:
raise exc_
else:
# TODO be more specific on the version
warnings.warn(f"Ignored error on curtailment, because "
f"you are in a backward compatibility mode.")

def _digest_alarm(self, dict_):
"""
Expand All @@ -2125,7 +2150,7 @@ def _reset_vect(self):
self._subs_impacted = None
self._lines_impacted = None

def update(self, dict_):
def update(self, dict_: DICT_ACT_TYPING):
"""
Update the action with a comprehensible format specified by a dictionary.
Expand Down Expand Up @@ -6410,7 +6435,6 @@ def decompose_as_unary_actions(self,
tmp += a
assert tmp == act
Parameters
----------
group_topo : bool, optional
Expand Down Expand Up @@ -6473,3 +6497,56 @@ def decompose_as_unary_actions(self,
if self._modif_curtailment:
self._aux_decompose_as_unary_actions_curtail(cls, group_curtail, res)
return res

def _add_act_and_remove_line_status_only_set(self, other: "BaseAction") -> "BaseAction":
"""INTERNAL
This is used by the environment when combining action in the "set state" in env.reset.
It supposes both self and other are only "set" actions
.. versionadded:: 1.10.2
"""
self += other
cls = type(self)
# switch off in self the element disconnected in other
switched_off = other._set_line_status == -1
switched_off |= other._set_topo_vect[cls.line_or_pos_topo_vect] == -1
switched_off |= other._set_topo_vect[cls.line_ex_pos_topo_vect] == -1
self._set_topo_vect[cls.line_or_pos_topo_vect[switched_off]] = -1
self._set_topo_vect[cls.line_ex_pos_topo_vect[switched_off]] = -1
self._set_line_status[switched_off] = -1

# switch on in self the element reconnected in other
switched_on = other._set_line_status == 1
switched_on |= other._set_topo_vect[cls.line_or_pos_topo_vect] > 0
switched_on |= other._set_topo_vect[cls.line_ex_pos_topo_vect] > 0
self._set_line_status[switched_on] = 1
# "reconnect" object through topo vect
topo_vect = other._set_topo_vect > 0
self._set_topo_vect[topo_vect] = other._set_topo_vect[topo_vect]

if (self._set_line_status != 0).any():
self._modif_set_status = True
if (self._set_topo_vect != 0).any():
self._modif_set_bus = True
return self

def remove_change(self) -> "BaseAction":
"""This function will modify 'self' and remove all "change" action type.
It is mainly used in the environment, when removing the "change" type for setting the original
state of the grid.
.. versionadded:: 1.10.2
"""
if self._change_bus_vect.any():
warnings.warn("This action modified the buses with `change_bus` ")
self._change_bus_vect[:] = False
self._modif_change_bus = False
if self._switch_line_status.any():
self._switch_line_status[:] = False
self._modif_change_status = False
return self
29 changes: 29 additions & 0 deletions grid2op/Agent/oneChangeThenNothing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# SPDX-License-Identifier: MPL-2.0
# This file is part of Grid2Op, Grid2Op a testbed platform to model sequential decision making in power systems.

import warnings
from grid2op.Agent.baseAgent import BaseAgent


Expand All @@ -29,6 +30,11 @@ class OneChangeThenNothing(BaseAgent):
.. code-block:: python
# This class has been deprecated, please use the env.reset()
# with proper options instead
DEPRECATED !
import grid2op
from grid2op.Agent import OneChangeThenNothing
acts_dict_ = [{}, {"set_line_status": [(0,-1)]}] # list of dictionaries. Each dictionary
Expand All @@ -44,12 +50,35 @@ class OneChangeThenNothing(BaseAgent):
# run 2 episode with it
res_2 = runner.run(nb_episode=2)
Notes:
------
After grid2op 1.10.2, this class has been deprecated. A cleaner alternative
to use it is to set the initial state of grid when calling `env.reset` like this:
.. code-block:: python
import grid2op
env = grid2op.make("l2rpn_case14_sandbox") # create an environment
dict_act_json = ... # dict representing an action
obs = env.reset(options={"init state": dict_act_json})
This way of doing offers:
- more flexibility: rules are not checked
- more flexibility: any type of actions acting on anything can be performed
(even if the action would be illegal for the agent)
- less trouble: cooldown are not affected
"""

my_dict = {}

def __init__(self, action_space):
BaseAgent.__init__(self, action_space)
cls = type(self)
warnings.warn(f"Deprecated class, please use `env.reset(options={{'init state': {self.action_space(cls.my_dict).to_json()}, 'method': 'ignore' }})` instead")
self.has_changed = False
self.do_nothing_action = self.action_space({})

Expand Down
Loading

0 comments on commit 3b0b687

Please sign in to comment.