Skip to content

Commit 8f093b2

Browse files
committed
first proposal for issue rte-france#616
1 parent 0de6ad1 commit 8f093b2

File tree

12 files changed

+209
-31
lines changed

12 files changed

+209
-31
lines changed

CHANGELOG.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ Work kind of in progress
4040

4141
Next release
4242
---------------------------------
43+
- TODO bug on maintenance starting at midnight (they are not correctly handled in the observation)
44+
=> cf script test_issue_616
4345
- TODO Notebook for tf_agents
4446
- TODO Notebook for acme
4547
- TODO Notebook using "keras rl" (see https://keras.io/examples/rl/ppo_cartpole/)
@@ -66,11 +68,23 @@ Next release
6668
(because it should always have been like this)
6769
- [FIXED] a bug in the `MultiFolder` and `MultifolderWithCache` leading to the wrong
6870
computation of `max_iter` on some corner cases
71+
- [FIXED] issue on `seed` and `MultifolderWithCache` which caused
72+
https://github.com/rte-france/Grid2Op/issues/616
73+
- [FIXED] another issue with the seeding of `MultifolderWithCache`: the seed was not used
74+
correctly on the cache data when calling `chronics_handler.reset` multiple times without
75+
any changes
6976
- [ADDED] possibility to skip some step when calling `env.reset(..., options={"init ts": ...})`
7077
- [ADDED] possibility to limit the duration of an episode with `env.reset(..., options={"max step": ...})`
7178
- [ADDED] possibility to specify the "reset_options" used in `env.reset` when
7279
using the runner with `runner.run(..., reset_options=xxx)`
80+
- [ADDED] the time series now are able to regenerate their "random" part
81+
even when "cached" thanks to the addition of the `regenerate_with_new_seed` of the
82+
`GridValue` class (in public API)
83+
- [ADDED] `MultifolderWithCache` now supports `FromHandlers` time series generator
7384
- [IMPROVED] the documentation on the `time series` folder.
85+
- [IMPROVED] now the "maintenance from json" (*eg* the `JSONMaintenanceHandler` or the
86+
`GridStateFromFileWithForecastsWithMaintenance`) can be customized with the day
87+
of the week where the maintenance happens (key `maintenance_day_of_week`)
7488

7589
[1.10.2] - 2024-05-27
7690
-------------------------

grid2op/Chronics/GSFFWFWM.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ def initialize(
108108
self.max_daily_number_per_month_maintenance = dict_[
109109
"max_daily_number_per_month_maintenance"
110110
]
111+
112+
if "maintenance_day_of_week" in dict_:
113+
self.maintenance_day_of_week = [int(el) for el in dict_[
114+
"maintenance_day_of_week"
115+
]]
116+
else:
117+
self.maintenance_day_of_week = np.arange(5)
118+
111119
super().initialize(
112120
order_backend_loads,
113121
order_backend_prods,
@@ -133,7 +141,6 @@ def _sample_maintenance(self):
133141
########
134142
# new method to introduce generated maintenance
135143
self.maintenance = self._generate_maintenance() #
136-
137144
##########
138145
# same as before in GridStateFromFileWithForecasts
139146
GridStateFromFileWithForecastsWithMaintenance._fix_maintenance_format(self)
@@ -171,7 +178,12 @@ def _generate_matenance_static(name_line,
171178
daily_proba_per_month_maintenance,
172179
max_daily_number_per_month_maintenance,
173180
space_prng,
181+
maintenance_day_of_week=None
174182
):
183+
if maintenance_day_of_week is None:
184+
# new in grid2op 1.10.3
185+
maintenance_day_of_week = np.arange(5)
186+
175187
# define maintenance dataframe with size (nbtimesteps,nlines)
176188
columnsNames = name_line
177189
nbTimesteps = n_
@@ -203,8 +215,6 @@ def _generate_matenance_static(name_line,
203215
datelist = datelist[:-1]
204216

205217
n_lines_maintenance = len(line_to_maintenance)
206-
207-
_24_h = timedelta(seconds=86400)
208218
nb_rows = int(86400 / time_interval.total_seconds())
209219
selected_rows_beg = int(
210220
maintenance_starting_hour * 3600 / time_interval.total_seconds()
@@ -220,7 +230,7 @@ def _generate_matenance_static(name_line,
220230
maxDailyMaintenance = -1
221231
for nb_day_since_beg, this_day in enumerate(datelist):
222232
dayOfWeek = this_day.weekday()
223-
if dayOfWeek < 5: # only maintenance starting on working days
233+
if dayOfWeek in maintenance_day_of_week:
224234
month = this_day.month
225235

226236
maintenance_me = np.zeros((nb_rows, nb_line_maint))
@@ -279,5 +289,9 @@ def _generate_maintenance(self):
279289
self.maintenance_ending_hour,
280290
self.daily_proba_per_month_maintenance,
281291
self.max_daily_number_per_month_maintenance,
282-
self.space_prng
292+
self.space_prng,
293+
self.maintenance_day_of_week
283294
)
295+
296+
def regenerate_with_new_seed(self):
297+
self._sample_maintenance()

grid2op/Chronics/fromChronix2grid.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,12 @@ def next_chronics(self):
309309
GridStateFromFileWithForecastsWithMaintenance._fix_maintenance_format(self)
310310

311311
self.check_validity(backend=None)
312+
313+
def regenerate_with_new_seed(self):
314+
raise ChronicsError("You should not 'cache' the data coming from the "
315+
"`FromChronix2grid`, which is probably why you ended "
316+
"up calling this function. If you want to generate data "
317+
"'on the fly' please do not use the `MultiFolder` or "
318+
"`MultiFolderWithCache` `chronics_class` when making your "
319+
"environment.")
312320

grid2op/Chronics/gridValue.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,3 +856,21 @@ def cleanup_action_space(self):
856856
"""
857857
self.__action_space = None
858858
# NB the action space is not closed as it is NOT own by this class
859+
860+
def regenerate_with_new_seed(self):
861+
"""
862+
INTERNAL this function is called by some classes (*eg* :class:`MultifolderWithCache`)
863+
when a new seed has been set.
864+
865+
For example, if you use some 'chronics' that generate part of them randomly (*eg*
866+
:class:`GridStateFromFileWithForecastsWithMaintenance`) they need to be aware of this
867+
so that a reset actually update the seeds.
868+
869+
This is closely related to issue https://github.com/rte-france/Grid2Op/issues/616
870+
871+
.. danger::
872+
This function should be called only once (not 0, not twice) after a "seed" function has been set.
873+
Otherwise results might not be fully reproducible.
874+
875+
"""
876+
pass

grid2op/Chronics/handlers/baseHandler.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,14 @@ def get_init_dict_action(self) -> Union[dict, None]:
494494
action space.
495495
"""
496496
raise NotImplementedError()
497+
498+
def regenerate_with_new_seed(self):
499+
"""This function is called in case of data being "cached" (for example using the
500+
:class:`grid2op.Chronics.MultifolderWithCache`)
501+
502+
In this case, the data in cache needs to be updated if the seed has changed since
503+
the time they have been added to it.
504+
505+
If your handler has some random part, we recommend you to implement this function.
506+
Otherwise feel free to ignore it"""
507+
pass

grid2op/Chronics/handlers/jsonMaintenanceHandler.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def __init__(self,
6363
self.n_line = None # used in one of the GridStateFromFileWithForecastsWithMaintenance functions
6464
self._duration_episode_default = _duration_episode_default
6565
self.current_index = 0
66-
66+
self._order_backend_arrays = None
67+
6768
def get_maintenance_time_1d(self, maintenance):
6869
return GridValue.get_maintenance_time_1d(maintenance)
6970

@@ -82,7 +83,8 @@ def _create_maintenance_arrays(self, current_datetime):
8283
self.dict_meta_data["maintenance_ending_hour"],
8384
self.dict_meta_data["daily_proba_per_month_maintenance"],
8485
self.dict_meta_data["max_daily_number_per_month_maintenance"],
85-
self.space_prng
86+
self.space_prng,
87+
self.dict_meta_data["maintenance_day_of_week"] if "maintenance_day_of_week" in self.dict_meta_data else None
8688
)
8789
GridStateFromFileWithForecastsWithMaintenance._fix_maintenance_format(self)
8890

@@ -128,4 +130,8 @@ def _clear(self):
128130

129131
def done(self):
130132
# maintenance can be generated on the fly so they are never "done"
131-
return False
133+
return False
134+
135+
def regenerate_with_new_seed(self):
136+
if self.dict_meta_data is not None:
137+
self._create_maintenance_arrays(self.init_datetime)

grid2op/Chronics/handlers/noisyForecastHandler.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,7 @@ def forecast(self,
212212
res *= self._env_loss_ratio(inj_dict_env)
213213
# TODO ramps, pmin, pmax !
214214
return res.astype(dt_float) if res is not None else None
215+
216+
def regenerate_with_new_seed(self):
217+
# there is nothing to do for this handler as things are generated "on the fly"
218+
pass

grid2op/Chronics/multiFolder.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,17 @@ def reset(self):
394394
self._order = np.array(self._order)
395395
return self.subpaths[self._order]
396396

397+
def _get_nex_data(self, this_path):
398+
res = self.gridvalueClass(
399+
time_interval=self.time_interval,
400+
sep=self.sep,
401+
path=this_path,
402+
max_iter=self.max_iter,
403+
chunk_size=self.chunk_size,
404+
**self._kwargs
405+
)
406+
return res
407+
397408
def initialize(
398409
self,
399410
order_backend_loads,
@@ -419,14 +430,7 @@ def initialize(
419430

420431
id_scenario = self._order[self._prev_cache_id]
421432
this_path = self.subpaths[id_scenario]
422-
self.data = self.gridvalueClass(
423-
time_interval=self.time_interval,
424-
sep=self.sep,
425-
path=this_path,
426-
max_iter=self.max_iter,
427-
chunk_size=self.chunk_size,
428-
**self._kwargs
429-
)
433+
self.data = self._get_nex_data(this_path)
430434
if self.seed is not None:
431435
max_int = np.iinfo(dt_int).max
432436
seed_chronics = self.space_prng.randint(max_int)

0 commit comments

Comments
 (0)