rte-france / l2rpn-baselines

L2RPN Baselines a repository to host baselines for l2rpn competitions.
https://l2rpn-baselines.readthedocs.io/en/stable/
Mozilla Public License 2.0
81 stars 45 forks source link

'_missing_two_busbars_support_info' attribute is lost when using Train #63

Closed DEUCE1957 closed 5 months ago

DEUCE1957 commented 6 months ago

Environment

Bug description

When using the LightSim2Grid backend the attribute '_missing_two_busbars_support_info' is False since LightSim2Grid currently does not support more than 2 busbars per substation. Normally, this only results in a warning. However, when using the train() method from l2rpn_baselines, it seems this attribute is lost somewhere. As a result when backend.assert_grid_correct() is run an attribute error is thrown: 'LightSimBackend_rte_case14_realistic_train' object has no attribute '_missing_two_busbars_support_info'

How to reproduce

import grid2op
import lightsim2grid
import l2rpn_baselines.PPO_SB3 as PPO_SB3
ENV_NAME = "rte_case14_realistic_train"
env = grid2op.make(ENV_NAME, backend=lightsim2grid.LightSimBackend())
obs = env.reset()
agent = PPO_SB3.train(env, iterations=1, logs_dir=None, save_path=None, net_arch=[100,100,100])

Error Trace

AttributeError                            Traceback (most recent call last)
Cell In[3], line 24
     22 env = grid2op.make(ENV_NAME, backend=lightsim2grid.LightSimBackend())
     23 obs = env.reset()
---> 24 agent = PPO_SB3.train(env, iterations=1, logs_dir=None, save_path=None, verbose=True, net_arch=[100,100,100])

File ...\.venv\Lib\site-packages\l2rpn_baselines\PPO_SB3\train.py:306, in train(env, name, iterations, save_path, load_path, net_arch, logs_dir, learning_rate, checkpoint_callback, save_every_xxx_steps, model_policy, obs_attr_to_keep, obs_space_kwargs, act_attr_to_keep, act_space_kwargs, policy_kwargs, normalize_obs, normalize_act, gymenv_class, gymenv_kwargs, verbose, seed, eval_env, **kwargs)
    299     agent = SB3Agent(env.action_space,
    300                      env_gym.action_space,
    301                      env_gym.observation_space,
    302                      nn_path=os.path.join(load_path, name)
    303     )
    305 # train it
--> 306 agent.nn_model.learn(total_timesteps=iterations,
    307                      callback=checkpoint_callback,
    308                      # eval_env=eval_env  # TODO
    309                      )
    311 # save it
    312 if save_path is not None:

File ...\.venv\Lib\site-packages\stable_baselines3\ppo\ppo.py:315, in PPO.learn(self, total_timesteps, callback, log_interval, tb_log_name, reset_num_timesteps, progress_bar)
    306 def learn(
    307     self: SelfPPO,
    308     total_timesteps: int,
   (...)
    313     progress_bar: bool = False,
    314 ) -> SelfPPO:
--> 315     return super().learn(
    316         total_timesteps=total_timesteps,
    317         callback=callback,
    318         log_interval=log_interval,
    319         tb_log_name=tb_log_name,
    320         reset_num_timesteps=reset_num_timesteps,
    321         progress_bar=progress_bar,
    322     )

File ...\.venv\Lib\site-packages\stable_baselines3\common\on_policy_algorithm.py:264, in OnPolicyAlgorithm.learn(self, total_timesteps, callback, log_interval, tb_log_name, reset_num_timesteps, progress_bar)
    253 def learn(
    254     self: SelfOnPolicyAlgorithm,
    255     total_timesteps: int,
   (...)
    260     progress_bar: bool = False,
    261 ) -> SelfOnPolicyAlgorithm:
    262     iteration = 0
--> 264     total_timesteps, callback = self._setup_learn(
    265         total_timesteps,
    266         callback,
    267         reset_num_timesteps,
    268         tb_log_name,
    269         progress_bar,
    270     )
    272     callback.on_training_start(locals(), globals())
    274     assert self.env is not None

File ...\.venv\Lib\site-packages\stable_baselines3\common\base_class.py:423, in BaseAlgorithm._setup_learn(self, total_timesteps, callback, reset_num_timesteps, tb_log_name, progress_bar)
    421 if reset_num_timesteps or self._last_obs is None:
    422     assert self.env is not None
--> 423     self._last_obs = self.env.reset()  # type: ignore[assignment]
    424     self._last_episode_starts = np.ones((self.env.num_envs,), dtype=bool)
    425     # Retrieve unnormalized observation for saving into the buffer

File ...\.venv\Lib\site-packages\stable_baselines3\common\vec_env\dummy_vec_env.py:77, in DummyVecEnv.reset(self)
     75 for env_idx in range(self.num_envs):
     76     maybe_options = {"options": self._options[env_idx]} if self._options[env_idx] else {}
---> 77     obs, self.reset_infos[env_idx] = self.envs[env_idx].reset(seed=self._seeds[env_idx], **maybe_options)
     78     self._save_obs(env_idx, obs)
     79 # Seeds and options are only used once

File ...\.venv\Lib\site-packages\stable_baselines3\common\monitor.py:83, in Monitor.reset(self, **kwargs)
     81         raise ValueError(f"Expected you to pass keyword argument {key} into reset")
     82     self.current_reset_info[key] = value
---> 83 return self.env.reset(**kwargs)

File ...\.venv\Lib\site-packages\grid2op\gym_compat\gymenv.py:303, in GymnasiumEnv.reset(self, seed, options)
    296 def reset(self,
    297           *,
    298           seed: Optional[int]=None,
   (...)
    301              RESET_INFO_GYM_TYPING
    302           ]:
--> 303     return self._aux_reset_new(seed, options)

File ...\.venv\Lib\site-packages\grid2op\gym_compat\gymenv.py:184, in __AuxGymEnv._aux_reset_new(self, seed, options)
    180     seed, next_seed, underlying_env_seeds = self._aux_seed_g2op(seed)
    182 # we don't seed grid2op with reset as it is done
    183 # earlier
--> 184 g2op_obs = self.init_env.reset(seed=None, options=options)
    185 gym_obs = self.observation_space.to_gym(g2op_obs)
    187 chron_id = self.init_env.chronics_handler.get_id()

File ...\.venv\Lib\site-packages\grid2op\Environment\environment.py:988, in Environment.reset(self, seed, options)
    986 self._reset_redispatching()
    987 self._reset_vectors_and_timings()  # it need to be done BEFORE to prevent cascading failure when there has been
--> 988 self.reset_grid()
    989 if self.viewer_fig is not None:
    990     del self.viewer_fig

File ...\.venv\Lib\site-packages\grid2op\Environment\environment.py:868, in Environment.reset_grid(self)
    852 """
    853 INTERNAL
    854 
   (...)
    863 
    864 """
    865 self.backend.reset(
    866     self._init_grid_path
    867 )  # the real powergrid of the environment
--> 868 self.backend.assert_grid_correct()
    870 if self._thermal_limit_a is not None:
    871     self.backend.set_thermal_limit(self._thermal_limit_a.astype(dt_float))

File ...\.venv\Lib\site-packages\grid2op\Backend\backend.py:1947, in Backend.assert_grid_correct(self)
   1944 from grid2op.Action import CompleteAction
   1945 from grid2op.Action._backendAction import _BackendAction
-> 1947 if self._missing_two_busbars_support_info:
   1948     warnings.warn("The backend implementation you are using is probably too old to take advantage of the "
   1949                   "new feature added in grid2op 1.10.0: the possibility "
   1950                   "to have more than 2 busbars per substations (or not). "
   (...)
   1958                   "handle more than 2 busbars per substation, then change it :-)\n"
   1959                   "Your backend will behave as if it did not support it.")
   1960     self._missing_two_busbars_support_info = False
DEUCE1957 commented 6 months ago

Two-line reproducible error:

import grid2op; import lightsim2grid
grid2op.make("rte_case14_realistic", backend=lightsim2grid.LightSimBackend()).copy().backend._missing_two_busbars_support_info

It appears to be caused by copy not copying over the '._missing_two_busbars_support_info' attribute of the Backend.

Quick Fix: Add Line after Line No 1077 in Grid2Op's environement.py class:

new_obj._raw_backend_class._missing_two_busbars_support_info = self.backend._missing_two_busbars_support_info

Tried playing around with copy.deepcopy on the 'self._raw_backend_class' but that did not fix the issue.

BDonnot commented 6 months ago

Hello,

Indeed this is a bug due to a misuse of deep copy in lightsim2grid.

I wrote a post here https://discord.com/channels/698080905209577513/698133564381724764/1217514511284830259

In the mean time you can use grid2op 1.9.8.

Sorry for that and that for proposing a fix grid2op side (I'll implement one and this sounds reasonable)

BDonnot commented 6 months ago

Hello,

This should be fixed by upgrade to latest lightsim2grid version:

pip install LightSim2Grid==0.7.5.post1

Fix grid2op will follow most likely tomorrow, today i'll work on backporting this fix for other lightsim2grid version

BDonnot commented 5 months ago

Fixed in lightsim2grid and grid2op now.

You simply need to upgrade either (lightsim2grid or grid2op) but preferably both.