Skip to content

Commit

Permalink
Merge pull request #180 from BDonnot/bd_dev
Browse files Browse the repository at this point in the history
Rules as instance
  • Loading branch information
BDonnot authored May 23, 2023
2 parents 653c6d8 + 03441a2 commit 1bb8085
Show file tree
Hide file tree
Showing 48 changed files with 1,157 additions and 172 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,10 @@ requirements_issue_389.txt
test_multi_step_for.py
test_vecenv.7z
test_vecenv.py
test_timeout_env.py
test_pp_networks.py
test_timeout_env.py
test_timeout_env.py
test_pp_networks.py
test_timeout_env.py
test_pp_dc.py
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Change Log

[1.8.2] - 2023-xx-yy
--------------------
- [BREAKING] (because prone to bug): force the environment name in the `grid2op.make` function.
- [BREAKING] because bugged... The default behaviour for `env.render()` is now "rgb_array". The mode
"human" has been removed because it needs some fixes. This should not impact lots of code.
- [BREAKING] the "maintenance_forecast" file is deprecated and is no longer used (this should not
Expand All @@ -44,6 +45,8 @@ Change Log
- [BREAKING] In `PandaPowerBackend` the kwargs argument "ligthsim2grid" was misspelled and is now properly
renamed `lightsim2grid`
- [BREAKING] you can no longer use the `env.reactivate_forecast()` in the middle of an episode.
- [BREAKING] the method `runner.run_one_episode()` (that should not use !) now
returns also the total number of steps of the environment.
- [FIXED] a bug in `PandapowerBackend` when running in dc mode (voltages were not read correctly
from the generators)
- [FIXED] issue https://github.com/rte-france/Grid2Op/issues/389 which was caused by 2 independant things:
Expand Down Expand Up @@ -72,6 +75,9 @@ Change Log
`GridStateFromFileWithForecastsWithoutMaintenance` classes that caused the maintenance file to be
ignored when "chunk_size" was set.
- [FIXED] a bug when shunts were alone in `backend.check_kirchoff()`
- [FIXED] an issue with "max_iter" in the runner when `MultifolderWithCache`
(see issue https://github.com/rte-france/Grid2Op/issues/447)
- [FIXED] a bug in `MultifolderWithCache` when seeding was applied
- [ADDED] the function `obs.get_forecast_env()` that is able to generate a grid2op environment from the
forecasts data in the observation. This is especially useful in model based RL.
- [ADDED] an example on how to write a backend.
Expand All @@ -88,6 +94,8 @@ Change Log
directly from the grid2op action, see `obs.change_forecast_parameters()`
- [ADDED] possibility to retrieve a "forecast environment" with custom forecasts, see
`obs.get_env_from_external_forecasts(...)`
- [ADDED] adding the `TimedOutEnvironment` that takes "do nothing" actions when the agent
takes too much time to compute. This involves quite some changes in the runner too.
- [IMPROVED] possibility to "chain" the call to simulate when multiple forecast
horizon are available.
- [IMPROVED] the `GridStateFromFileWithForecasts` is now able to read forecast from multiple steps
Expand Down Expand Up @@ -128,6 +136,7 @@ Change Log
passed to `chronix2grid.add_data`
- [IMPROVED] it is no more reasonably possible to misuse the `MultifolderWithCache` (for example by
forgetting to `reset()` the cache): an error will be raised in case the proper function has not been called.
- [IMPROVED] possibility to pass game rules by instance of object and not by class.

[1.8.1] - 2023-01-11
---------------------
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.8.2.dev4'
release = '1.8.2.dev5'
version = '1.8'


Expand Down
31 changes: 31 additions & 0 deletions docs/opponent.rst
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,37 @@ To summarize what is going on here:
type of Opponent, we don't provide any information in the documentation at this stage. Feel free to submit
a github issue if this is an issue for you.

How to deactivate an opponent in an environment
--------------------------------------------------

If you come accross an environment with an "opponent" already present but for some reasons you want to
deactivate it, you can do this by customization the call to "grid2op.make" like this:

.. code-block:: python
import grid2op
from grid2op.Action import DontAct
from grid2op.Opponent import BaseOpponent, NeverAttackBudget
env_name = ...
env_without_opponent = grid2op.make(env_name,
opponent_attack_cooldown=999999,
opponent_attack_duration=0,
opponent_budget_per_ts=0,
opponent_init_budget=0,
opponent_action_class=DontAct,
opponent_class=BaseOpponent,
opponent_budget_class=NeverAttackBudget,
... # other arguments pass to the "make" function
)
.. note::
Currently it's not possible to deactivate an opponent once the environment is created.

If you want this feature, you can comment the issue https://github.com/rte-france/Grid2Op/issues/426


Detailed Documentation by class
--------------------------------
.. automodule:: grid2op.Opponent
Expand Down
11 changes: 6 additions & 5 deletions grid2op/Agent/baseAgent.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import os
from abc import ABC, abstractmethod
from grid2op.Space import RandomObject
from grid2op.Observation import BaseObservation


class BaseAgent(RandomObject, ABC):
Expand All @@ -32,7 +33,7 @@ def __init__(self, action_space):
RandomObject.__init__(self)
self.action_space = copy.deepcopy(action_space)

def reset(self, obs):
def reset(self, obs: BaseObservation):
"""
This method is called at the beginning of a new episode.
It is implemented by agents to reset their internal state if needed.
Expand All @@ -44,7 +45,7 @@ def reset(self, obs):
"""
pass

def seed(self, seed):
def seed(self, seed: int):
"""
This function is used to guarantee that the "pseudo random numbers" generated and used by the agent instance
will be deterministic.
Expand All @@ -70,7 +71,7 @@ def seed(self, seed):
return super().seed(seed), self.action_space.seed(seed)

@abstractmethod
def act(self, observation, reward, done=False):
def act(self, observation: BaseObservation, reward: float, done : bool=False):
"""
This is the main method of an BaseAgent. Given the current observation and the current reward (ie the reward
that the environment send to the agent after the previous action has been implemented).
Expand Down Expand Up @@ -110,7 +111,7 @@ def act(self, observation, reward, done=False):
The action chosen by the bot / controler / agent.
"""
pass
return self.action_space()

def save_state(self, savestate_path :os.PathLike):
"""
Expand Down Expand Up @@ -169,4 +170,4 @@ def load_state(self, loadstate_path :os.PathLike):
savestate_path: ``string``
The path from which your agent state variables should be loaded
"""
pass
pass
10 changes: 7 additions & 3 deletions grid2op/Chronics/chronicsHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,18 @@ def get_name(self):
"""
return str(os.path.split(self.get_id())[-1])

def set_max_iter(self, max_iter):
def set_max_iter(self, max_iter: int):
"""
This function is used to set the maximum number of iterations possible before the chronics ends.
This function is used to set the maximum number of
iterations possible before the chronics ends.
You can reset this by setting it to `-1`.
Parameters
----------
max_iter: ``int``
The maximum number of steps that can be done before reaching the end of the episode
The maximum number of steps that can be done before reaching
the end of the episode
"""

Expand Down
8 changes: 4 additions & 4 deletions grid2op/Chronics/fromChronix2grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ def load_next(self):# TODO refacto with fromNPY
)

def max_timestep(self):
return self.max_iter
return self._max_iter

def forecasts(self):
"""
Expand Down Expand Up @@ -263,8 +263,8 @@ def done(self):
res = False
if self.current_index >= self._load_p.shape[0]:
res = True
elif self.max_iter > 0:
if self.curr_iter > self.max_iter:
elif self._max_iter > 0:
if self.curr_iter > self._max_iter:
res = True
return res

Expand All @@ -281,7 +281,7 @@ def next_chronics(self):
res_gen = self._generate_one_episode(self.env, self.dict_ref, self.dt, self._init_datetime,
seed=self._seed_used_for_chronix2grid,
with_loss=self._with_loss,
nb_steps=self.max_iter)
nb_steps=self._max_iter)

self._load_p = res_gen[0].values
self._load_p_forecasted = res_gen[1].values
Expand Down
8 changes: 4 additions & 4 deletions grid2op/Chronics/fromNPY.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,8 +454,8 @@ def done(self):
or self.current_index >= self._load_p.shape[0]
):
res = True
elif self.max_iter > 0:
if self.curr_iter > self.max_iter:
elif self._max_iter > 0:
if self.curr_iter > self._max_iter:
res = True
return res

Expand Down Expand Up @@ -608,8 +608,8 @@ def change_forecasts(
)

def max_timestep(self):
if self.max_iter >= 0:
return min(self.max_iter, self._load_p.shape[0], self._i_end)
if self._max_iter >= 0:
return min(self._max_iter, self._load_p.shape[0], self._i_end)
return min(self._load_p.shape[0], self._i_end)

def change_i_start(self, new_i_start: Union[int, None]):
Expand Down
48 changes: 32 additions & 16 deletions grid2op/Chronics/gridStateFromFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,8 @@ def _get_data(self, data_name, chunksize=-1, nrows=None):
file_ext = self._get_fileext(data_name)

if nrows is None:
if self.max_iter > 0:
nrows = self.max_iter + 1
if self._max_iter > 0:
nrows = self._max_iter + 1

if file_ext is not None:
if chunksize == -1:
Expand Down Expand Up @@ -558,8 +558,8 @@ def initialize(
prod_v_iter = self._get_data("prod_v")
read_compressed = self._get_fileext("hazards")
nrows = None
if self.max_iter > 0:
nrows = self.max_iter + 1
if self._max_iter > 0:
nrows = self._max_iter + 1

if read_compressed is not None:
hazards = pd.read_csv(
Expand Down Expand Up @@ -664,18 +664,18 @@ def initialize(
)
self.n_ = n_ # the -1 is present because the initial grid state doesn't count as a "time step"

if self.max_iter > 0:
if self._max_iter > 0:
if self.n_ is not None:
if self.max_iter >= self.n_:
self.max_iter = self.n_ - 1
if self._max_iter >= self.n_:
self._max_iter = self.n_ - 1
# TODO: issue warning in this case
self.n_ = self.max_iter + 1
self.n_ = self._max_iter + 1
else:
# if the number of maximum time step is not set yet, we set it to be the number of
# data in the chronics (number of rows of the files) -1.
# the -1 is present because the initial grid state doesn't count as a "time step" but is read
# from these data.
self.max_iter = self.n_ - 1
self._max_iter = self.n_ - 1

self._init_attrs(
load_p, load_q, prod_p, prod_v, hazards=hazards, maintenance=maintenance,
Expand Down Expand Up @@ -785,11 +785,27 @@ def done(self):
# if self.current_index+1 >= self.tmp_max_index:
if self.current_index > self.n_:
res = True
elif self.max_iter > 0:
if self.curr_iter > self.max_iter:
elif self._max_iter > 0:
if self.curr_iter > self._max_iter:
res = True
return res

@property
def max_iter(self):
return self._max_iter

@max_iter.setter
def max_iter(self, value : int):
if value == -1:
self._max_iter = self.n_ - 1
else:
self._max_iter = int(value)

def max_timestep(self):
if self._max_iter == -1:
return self.n_ - 1
return self._max_iter

def _data_in_memory(self):
if self.chunk_size is None:
# if i don't use chunk, all the data are in memory alreay
Expand Down Expand Up @@ -824,8 +840,8 @@ def load_next(self):
if self.current_index >= self.tmp_max_index:
raise StopIteration

if self.max_iter > 0:
if self.curr_iter > self.max_iter:
if self._max_iter > 0:
if self.curr_iter > self._max_iter:
raise StopIteration

res = {}
Expand Down Expand Up @@ -987,10 +1003,10 @@ def check_validity(self, backend):
)
raise EnvError(msg_err.format(name_arr, arr.shape[0], self.n_))

if self.max_iter > 0:
if self.max_iter > self.n_:
if self._max_iter > 0:
if self._max_iter > self.n_:
msg_err = "Files count {} rows and you ask this episode to last at {} timestep."
raise InsufficientData(msg_err.format(self.n_, self.max_iter))
raise InsufficientData(msg_err.format(self.n_, self._max_iter))

def next_chronics(self):
self.current_datetime = self.start_datetime
Expand Down
4 changes: 2 additions & 2 deletions grid2op/Chronics/gridStateFromFileWithForecasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ def initialize(
else:
chunk_size = None

if self.max_iter > 0:
nrows_to_load = (self.max_iter + 1) * self._nb_forecast
if self._max_iter > 0:
nrows_to_load = (self._max_iter + 1) * self._nb_forecast

load_p_iter = self._get_data("load_p_forecasted",
chunk_size, nrows_to_load)
Expand Down
Loading

0 comments on commit 1bb8085

Please sign in to comment.