Skip to content

Commit 9a4ac6b

Browse files
authored
Merge pull request #604 from BDonnot/bd_dev
Bd dev
2 parents 37fcc16 + af5b3b5 commit 9a4ac6b

32 files changed

+1315
-459
lines changed

CHANGELOG.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,32 @@ Change Log
3737
an observation (no need to do it from the Observation Space)
3838
- [ADDED] method to change the reward from the observation (observation_space
3939
is not needed anymore): you can use `obs.change_reward`
40+
- [ADDED] a way to automatically set the `experimental_read_from_local_dir` flags
41+
(with automatic class creation). For now it is disable by default, but you can
42+
activate it transparently (see doc)
43+
- [ADDED] TODO the possibility to set the grid in an initial state (using an action) TODO
4044
- [FIXED] a small issue that could lead to having
4145
"redispatching_unit_commitment_availble" flag set even if the redispatching
4246
data was not loded correctly
4347
- [FIXED] EducPandaPowerBackend now properly sends numpy array in the class attributes
4448
(instead of pandas series)
45-
- [FIXED] an issue when loading back data (with EpisodeData): when there were no storage units
49+
- [FIXED] an issue when loading back data (with `EpisodeData`): when there were no storage units
4650
on the grid it did not set properly the "storage relevant" class attributes
47-
- [FIXED] notebook 5 on loading back data.
51+
- [FIXED] a bug in the "gridobj.generate_classes()" function which crashes when no
52+
grid layout was set
53+
- [FIXED] notebook 5 on loading back data with `EpisodeData`.
54+
- [FIXED] converter between backends (could not handle more than 2 busbars)
4855
- [IMPROVED] documentation about `obs.simulate` to make it clearer the
4956
difference between env.step and obs.simulate on some cases
5057
- [IMPROVED] type hints on some methods of `GridObjects`
5158
- [IMPROVED] replace `np.nonzero(arr)` calls with `arr.nonzero()` which could
5259
save up a bit of computation time.
5360
- [IMPROVED] force class attributes to be numpy arrays of proper types when the
5461
classes are initialized from the backend.
62+
- [IMPROVED] some (slight) speed improvments when comparing actions or deep copying objects
63+
- [IMPROVED] the way the "grid2op compat" mode is handled
64+
- [IMPROVED] the coverage of the tests in the "test_basic_env_ls.py" to test more in depth lightsim2grid
65+
(creation of multiple environments, grid2op compatibility mode)
5566

5667
[1.10.1] - 2024-03-xx
5768
----------------------

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
author = 'Benjamin Donnot'
2323

2424
# The full version, including alpha/beta/rc tags
25-
release = '1.10.2.dev0'
25+
release = '1.10.2.dev1'
2626
version = '1.10'
2727

2828

grid2op/Action/baseAction.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,12 +782,13 @@ def alert_raised(self) -> np.ndarray:
782782

783783
@classmethod
784784
def _aux_process_old_compat(cls):
785+
super()._aux_process_old_compat()
786+
785787
# this is really important, otherwise things from grid2op base types will be affected
786788
cls.authorized_keys = copy.deepcopy(cls.authorized_keys)
787789
cls.attr_list_vect = copy.deepcopy(cls.attr_list_vect)
788790

789791
# deactivate storage
790-
cls.set_no_storage()
791792
if "set_storage" in cls.authorized_keys:
792793
cls.authorized_keys.remove("set_storage")
793794
if "_storage_power" in cls.attr_list_vect:

grid2op/Backend/backend.py

Lines changed: 171 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# python version is probably bellow 3.11
2323
from typing_extensions import Self
2424

25+
import grid2op
2526
from grid2op.dtypes import dt_int, dt_float, dt_bool
2627
from grid2op.Exceptions import (
2728
EnvError,
@@ -66,19 +67,22 @@ class Backend(GridObjects, ABC):
6667
All the abstract methods (that need to be implemented for a backend to work properly) are (more information given
6768
in the :ref:`create-backend-module` page):
6869
69-
- :func:`Backend.load_grid`
70-
- :func:`Backend.apply_action`
71-
- :func:`Backend.runpf`
72-
- :func:`Backend.get_topo_vect`
73-
- :func:`Backend.generators_info`
74-
- :func:`Backend.loads_info`
75-
- :func:`Backend.lines_or_info`
76-
- :func:`Backend.lines_ex_info`
70+
- :func:`Backend.load_grid` (called once per episode, or if :func:`Backend.reset` is implemented, once for the entire
71+
lifetime of the environment)
72+
- :func:`Backend.apply_action` (called once per episode -initialization- and at least once per step)
73+
- :func:`Backend.runpf` (called once per episode -initialization- and at least once per step)
74+
- :func:`Backend.get_topo_vect` (called once per episode -initialization- and at least once per step)
75+
- :func:`Backend.generators_info` (called once per episode -initialization- and at least once per step)
76+
- :func:`Backend.loads_info` (called once per episode -initialization- and at least once per step)
77+
- :func:`Backend.lines_or_info` (called once per episode -initialization- and at least once per step)
78+
- :func:`Backend.lines_ex_info` (called once per episode -initialization- and at least once per step)
7779
7880
And optionally:
7981
82+
- :func:`Backend.reset` will reload the powergrid from the hard drive by default. This is rather slow and we
83+
recommend to overload it.
8084
- :func:`Backend.close` (this is mandatory if your backend implementation (`self._grid`) is relying on some
81-
c / c++ code that do not free memory automatically.
85+
c / c++ code that do not free memory automatically.)
8286
- :func:`Backend.copy` (not that this is mandatory if your backend implementation (in `self._grid`) cannot be
8387
deep copied using the python copy.deepcopy function) [as of grid2op >= 1.7.1 it is no more
8488
required. If not implemented, you won't be able to use some of grid2op feature however]
@@ -88,8 +92,6 @@ class Backend(GridObjects, ABC):
8892
at the "origin" side and just return the "a_or" vector. You want to do something smarter here.
8993
- :func:`Backend._disconnect_line`: has a default slow implementation using "apply_action" that might
9094
can most likely be optimized in your backend.
91-
- :func:`Backend.reset` will reload the powergrid from the hard drive by default. This is rather slow and we
92-
recommend to overload it.
9395
9496
And, if the flag :attr:Backend.shunts_data_available` is set to ``True`` the method :func:`Backend.shunt_info`
9597
should also be implemented.
@@ -99,12 +101,6 @@ class Backend(GridObjects, ABC):
99101
`shunt_to_subid`, `name_shunt` and function `shunt_info` and handle the modification of shunts
100102
bus, active value and reactive value in the "apply_action" function).
101103
102-
103-
In order to be valid and carry out some computations, you should call :func:`Backend.load_grid` and later
104-
:func:`grid2op.Spaces.GridObjects.assert_grid_correct`. It is also more than recommended to call
105-
:func:`Backend.assert_grid_correct_after_powerflow` after the first powerflow. This is all carried ou in the
106-
environment properly.
107-
108104
Attributes
109105
----------
110106
detailed_infos_for_cascading_failures: :class:`bool`
@@ -119,9 +115,7 @@ class Backend(GridObjects, ABC):
119115
120116
"""
121117
IS_BK_CONVERTER : bool = False
122-
123-
env_name : str = "unknown"
124-
118+
125119
# action to set me
126120
my_bk_act_class : "Optional[grid2op.Action._backendAction._BackendAction]"= None
127121
_complete_action_class : "Optional[grid2op.Action.CompleteAction]"= None
@@ -224,7 +218,7 @@ def cannot_handle_more_than_2_busbar(self):
224218
If not called, then the `environment` will not be able to use more than 2 busbars per substations.
225219
226220
.. seealso::
227-
:func:`Backend.cnot_handle_more_than_2_busbar`
221+
:func:`Backend.cannot_handle_more_than_2_busbar`
228222
229223
.. note::
230224
From grid2op 1.10.0 it is preferable that your backend calls one of
@@ -1418,6 +1412,119 @@ def check_kirchoff(self) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray
14181412
diff_v_bus[:, :] = v_bus[:, :, 1] - v_bus[:, :, 0]
14191413
return p_subs, q_subs, p_bus, q_bus, diff_v_bus
14201414

1415+
def _fill_names_obj(self):
1416+
"""fill the name vectors (**eg** name_line) if not done already in the backend.
1417+
This function is used to fill the name of an object of a class. It will also check the existence
1418+
of these vectors in the class.
1419+
"""
1420+
cls = type(self)
1421+
if self.name_line is None:
1422+
if cls.name_line is None:
1423+
line_or_to_subid = cls.line_or_to_subid if cls.line_or_to_subid is not None else self.line_or_to_subid
1424+
line_ex_to_subid = cls.line_ex_to_subid if cls.line_ex_to_subid is not None else self.line_ex_to_subid
1425+
self.name_line = [
1426+
"{}_{}_{}".format(or_id, ex_id, l_id)
1427+
for l_id, (or_id, ex_id) in enumerate(
1428+
zip(line_or_to_subid, line_ex_to_subid)
1429+
)
1430+
]
1431+
self.name_line = np.array(self.name_line)
1432+
warnings.warn(
1433+
"name_line is None so default line names have been assigned to your grid. "
1434+
"(FYI: Line names are used to make the correspondence between the chronics and the backend)"
1435+
"This might result in impossibility to load data."
1436+
'\n\tIf "env.make" properly worked, you can safely ignore this warning.'
1437+
)
1438+
else:
1439+
self.name_line = cls.name_line
1440+
1441+
if self.name_load is None:
1442+
if cls.name_load is None:
1443+
load_to_subid = cls.load_to_subid if cls.load_to_subid is not None else self.load_to_subid
1444+
self.name_load = [
1445+
"load_{}_{}".format(bus_id, load_id)
1446+
for load_id, bus_id in enumerate(load_to_subid)
1447+
]
1448+
self.name_load = np.array(self.name_load)
1449+
warnings.warn(
1450+
"name_load is None so default load names have been assigned to your grid. "
1451+
"(FYI: load names are used to make the correspondence between the chronics and the backend)"
1452+
"This might result in impossibility to load data."
1453+
'\n\tIf "env.make" properly worked, you can safely ignore this warning.'
1454+
)
1455+
else:
1456+
self.name_load = cls.name_load
1457+
1458+
if self.name_gen is None:
1459+
if cls.name_gen is None:
1460+
gen_to_subid = cls.gen_to_subid if cls.gen_to_subid is not None else self.gen_to_subid
1461+
self.name_gen = [
1462+
"gen_{}_{}".format(bus_id, gen_id)
1463+
for gen_id, bus_id in enumerate(gen_to_subid)
1464+
]
1465+
self.name_gen = np.array(self.name_gen)
1466+
warnings.warn(
1467+
"name_gen is None so default generator names have been assigned to your grid. "
1468+
"(FYI: generator names are used to make the correspondence between the chronics and "
1469+
"the backend)"
1470+
"This might result in impossibility to load data."
1471+
'\n\tIf "env.make" properly worked, you can safely ignore this warning.'
1472+
)
1473+
else:
1474+
self.name_gen = cls.name_gen
1475+
1476+
if self.name_sub is None:
1477+
if cls.name_sub is None:
1478+
n_sub = cls.n_sub if cls.n_sub is not None and cls.n_sub > 0 else self.n_sub
1479+
self.name_sub = ["sub_{}".format(sub_id) for sub_id in range(n_sub)]
1480+
self.name_sub = np.array(self.name_sub)
1481+
warnings.warn(
1482+
"name_sub is None so default substation names have been assigned to your grid. "
1483+
"(FYI: substation names are used to make the correspondence between the chronics and "
1484+
"the backend)"
1485+
"This might result in impossibility to load data."
1486+
'\n\tIf "env.make" properly worked, you can safely ignore this warning.'
1487+
)
1488+
else:
1489+
self.name_sub = cls.name_sub
1490+
1491+
if self.name_storage is None:
1492+
if cls.name_storage is None:
1493+
storage_to_subid = cls.storage_to_subid if cls.storage_to_subid is not None else self.storage_to_subid
1494+
self.name_storage = [
1495+
"storage_{}_{}".format(bus_id, sto_id)
1496+
for sto_id, bus_id in enumerate(storage_to_subid)
1497+
]
1498+
self.name_storage = np.array(self.name_storage)
1499+
warnings.warn(
1500+
"name_storage is None so default storage unit names have been assigned to your grid. "
1501+
"(FYI: storage names are used to make the correspondence between the chronics and "
1502+
"the backend)"
1503+
"This might result in impossibility to load data."
1504+
'\n\tIf "env.make" properly worked, you can safely ignore this warning.'
1505+
)
1506+
else:
1507+
self.name_storage = cls.name_storage
1508+
1509+
if cls.shunts_data_available:
1510+
if self.name_shunt is None:
1511+
if cls.name_shunt is None:
1512+
shunt_to_subid = cls.shunt_to_subid if cls.shunt_to_subid is not None else self.shunt_to_subid
1513+
self.name_shunt = [
1514+
"shunt_{}_{}".format(bus_id, sh_id)
1515+
for sh_id, bus_id in enumerate(shunt_to_subid)
1516+
]
1517+
self.name_shunt = np.array(self.name_shunt)
1518+
warnings.warn(
1519+
"name_shunt is None so default storage unit names have been assigned to your grid. "
1520+
"(FYI: storage names are used to make the correspondence between the chronics and "
1521+
"the backend)"
1522+
"This might result in impossibility to load data."
1523+
'\n\tIf "env.make" properly worked, you can safely ignore this warning.'
1524+
)
1525+
else:
1526+
self.name_shunt = cls.name_shunt
1527+
14211528
def load_redispacthing_data(self,
14221529
path : Union[os.PathLike, str],
14231530
name : Optional[str]="prods_charac.csv") -> None:
@@ -1430,6 +1537,13 @@ def load_redispacthing_data(self,
14301537
14311538
We don't recommend at all to modify this function.
14321539
1540+
Notes
1541+
-----
1542+
Before you use this function, make sure the names of the generators are properly set.
1543+
1544+
For example you can either read them from the grid (setting self.name_gen) or call
1545+
self._fill_names_obj() beforehand (this later is done in the environment.)
1546+
14331547
Parameters
14341548
----------
14351549
path: ``str``
@@ -1458,7 +1572,6 @@ def load_redispacthing_data(self,
14581572
to change it.
14591573
14601574
"""
1461-
self._fill_names()
14621575
self.redispatching_unit_commitment_availble = False
14631576

14641577
# for redispatching
@@ -1574,6 +1687,13 @@ def load_storage_data(self,
15741687
This method will load everything needed in presence of storage unit on the grid.
15751688
15761689
We don't recommend at all to modify this function.
1690+
1691+
Notes
1692+
-----
1693+
Before you use this function, make sure the names of the generators are properly set.
1694+
1695+
For example you can either read them from the grid (setting self.name_gen) or call
1696+
self._fill_names_obj() beforehand (this later is done in the environment.)
15771697
15781698
Parameters
15791699
----------
@@ -1623,7 +1743,7 @@ def load_storage_data(self,
16231743
fullpath = os.path.join(path, name)
16241744
if not os.path.exists(fullpath):
16251745
raise BackendError(
1626-
f"There are storage unit on the grid, yet we could not locate their description."
1746+
f"There are {self.n_storage} storage unit(s) on the grid, yet we could not locate their description."
16271747
f'Please make sure to have a file "{name}" where the environment data are located.'
16281748
f'For this environment the location is "{path}"'
16291749
)
@@ -1983,19 +2103,44 @@ def assert_grid_correct(self) -> None:
19832103
self.__class__.__name__,
19842104
self.__class__,
19852105
)
2106+
19862107
# reset the attribute of the grid2op.Backend.Backend class
19872108
# that can be messed up with depending on the initialization of the backend
19882109
Backend._clear_class_attribute() # reset totally the grid2op Backend type
1989-
# orig_type._clear_class_attribute()
1990-
orig_type._clear_grid_dependant_class_attributes() # only reset the attributes that could be modified by user
1991-
2110+
2111+
# only reset the attributes that could be modified by the environment while keeping the
2112+
# attribute that can be defined in the Backend implementation (eg support of shunt)
2113+
orig_type._clear_grid_dependant_class_attributes()
2114+
19922115
my_cls = type(self)
19932116
my_cls.my_bk_act_class = _BackendAction.init_grid(my_cls)
19942117
my_cls._complete_action_class = CompleteAction.init_grid(my_cls)
19952118
my_cls._complete_action_class._add_shunt_data()
19962119
my_cls._complete_action_class._update_value_set()
19972120
my_cls.assert_grid_correct_cls()
2121+
self._remove_my_attr_cls()
19982122

2123+
def _remove_my_attr_cls(self):
2124+
"""
2125+
INTERNAL
2126+
2127+
.. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\
2128+
2129+
This function is called at the end of :func:`Backend.assert_grid_correct` and it "cleans" the attribute of the
2130+
backend object that are stored in the class now, to avoid discrepency between what has been read from the
2131+
grid and what have been processed by grid2op (for example in "compatibility" mode, storage are deactivated, so
2132+
`self.n_storage` would be different that `type(self).n_storage`)
2133+
2134+
For this to work, the grid must first be initialized correctly, with the proper type (name of the environment
2135+
in the class name !)
2136+
"""
2137+
cls = type(self)
2138+
if cls._CLS_DICT_EXTENDED is not None:
2139+
for attr_nm, val in cls._CLS_DICT_EXTENDED.items():
2140+
if hasattr(self, attr_nm) and hasattr(cls, attr_nm):
2141+
if id(getattr(self, attr_nm)) != id(getattr(cls, attr_nm)):
2142+
delattr(self, attr_nm)
2143+
19992144
def assert_grid_correct_after_powerflow(self) -> None:
20002145
"""
20012146
INTERNAL

0 commit comments

Comments
 (0)