def __init__( self, envs_dir, _add_to_name="", # internal, for test only, do not use ! **kwargs): GridObjects.__init__(self) RandomObject.__init__(self) self.current_env = None self.env_index = None self.mix_envs = [] # Special case handling for backend backendClass = None if "backend" in kwargs: backendClass = type(kwargs["backend"]) del kwargs["backend"] # Inline import to prevent cyclical import from grid2op.MakeEnv.Make import make try: for env_dir in sorted(os.listdir(envs_dir)): env_path = os.path.join(envs_dir, env_dir) if not os.path.isdir(env_path): continue # Special case for backend if backendClass is not None: env = make(env_path, backend=backendClass(), _add_to_name=_add_to_name, **kwargs) else: env = make(env_path, **kwargs) self.mix_envs.append(env) except Exception as e: err_msg = "MultiMix environment creation failed: {}".format(e) raise EnvError(err_msg) if len(self.mix_envs) == 0: err_msg = "MultiMix envs_dir did not contain any valid env" raise EnvError(err_msg) self.env_index = 0 self.current_env = self.mix_envs[self.env_index] # Make sure GridObject class attributes are set from first env # Should be fine since the grid is the same for all envs multi_env_name = os.path.basename( os.path.abspath(envs_dir)) + _add_to_name save_env_name = self.current_env.env_name self.current_env.env_name = multi_env_name self.__class__ = self.init_grid(self.current_env) self.current_env.env_name = save_env_name
def check_validity(self, backend): super(GridStateFromFileWithForecasts, self).check_validity(backend) at_least_one = False if self.load_p_forecast is not None: if self.load_p_forecast.shape[1] != backend.n_load: raise IncorrectNumberOfLoads( "for the active part. It should be {} but is in fact {}" "".format(backend.n_load, len(self.load_p))) at_least_one = True if self.load_q_forecast is not None: if self.load_q_forecast.shape[1] != backend.n_load: raise IncorrectNumberOfLoads( "for the reactive part. It should be {} but is in fact {}" "".format(backend.n_load, len(self.load_q))) at_least_one = True if self.prod_p_forecast is not None: if self.prod_p_forecast.shape[1] != backend.n_gen: raise IncorrectNumberOfGenerators( "for the active part. It should be {} but is in fact {}" "".format(backend.n_gen, len(self.prod_p))) at_least_one = True if self.prod_v_forecast is not None: if self.prod_v_forecast.shape[1] != backend.n_gen: raise IncorrectNumberOfGenerators( "for the voltage part. It should be {} but is in fact {}" "".format(backend.n_gen, len(self.prod_v))) at_least_one = True if self.maintenance_forecast is not None: if self.maintenance_forecast.shape[1] != backend.n_line: raise IncorrectNumberOfLines( "for the _maintenance. It should be {} but is in fact {}" "".format(backend.n_line, len(self.maintenance))) at_least_one = True if not at_least_one: raise ChronicsError( "You used a class that read forecasted data, yet there is no forecasted data in" "\"{}\". Please fall back to using class \"GridStateFromFile\" instead of " "\"{}\"".format(self.path, type(self))) for name_arr, arr in zip( ["load_q", "load_p", "prod_v", "prod_p", "maintenance"], [ self.load_q_forecast, self.load_p_forecast, self.prod_v_forecast, self.prod_p_forecast, self.maintenance_forecast ]): if arr is not None: if self.chunk_size is None: if arr.shape[0] < self.n_: raise EnvError( "Array for forecast {}_forecasted as not the same number of rows of load_p. " "The chronics cannot be loaded properly.".format( name_arr))
def check_validity(self, backend): """ .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\ This is called at the creation of the environment to ensure the Backend and the chronics are consistent with one another. A call to this method ensure that the action that will be sent to the current :class:`grid2op.Environment` can be properly implemented by its :class:`grid2op.Backend`. This specific method check that the dimension of all vectors are consistent Parameters ---------- backend: :class:`grid2op.Backend.Backend` The backend used by the :class:`grid2op.Environment.Environment` """ raise EnvError("check_validity not implemented")
def check_validity(self, backend): """ To make sure that the data returned by this class are of the proper dimension, a call to this method must be performed before actually using the data generated by this class. In the grid2op framework, this is ensure because the :class:`grid2op.Environment` calls this method in its initialization. Parameters ---------- backend: :class:`grid2op.Backend` The backend used by the :class;`Environment`. Returns ------- """ raise EnvError("check_validity not implemented")
def assert_grid_correct_after_powerflow(self): """ .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\ This is done as it should be by the Environment This method is called by the environment. It ensure that the backend remains consistent even after a powerflow has be run with :func:`Backend.runpf` method. :return: ``None`` :raise: :class:`grid2op.Exceptions.EnvError` and possibly all of its derived class. """ # test the results gives the proper size self.__class__ = self.init_grid(self) self.my_bk_act_class = self.my_bk_act_class.init_grid(self) self._complete_action_class = self._complete_action_class.init_grid( self) tmp = self.get_line_status() if tmp.shape[0] != self.n_line: raise IncorrectNumberOfLines( "returned by \"backend.get_line_status()\"") if np.any(~np.isfinite(tmp)): raise EnvironmentError( "Power cannot be computed on the first time step, please check your data." ) tmp = self.get_line_flow() if tmp.shape[0] != self.n_line: raise IncorrectNumberOfLines( "returned by \"backend.get_line_flow()\"") if np.any(~np.isfinite(tmp)): raise EnvironmentError( "Power cannot be computed on the first time step, please check your data." ) tmp = self.get_thermal_limit() if tmp.shape[0] != self.n_line: raise IncorrectNumberOfLines( "returned by \"backend.get_thermal_limit()\"") if np.any(~np.isfinite(tmp)): raise EnvironmentError( "Power cannot be computed on the first time step, please check your data." ) tmp = self.get_line_overflow() if tmp.shape[0] != self.n_line: raise IncorrectNumberOfLines( "returned by \"backend.get_line_overflow()\"") if np.any(~np.isfinite(tmp)): raise EnvironmentError( "Power cannot be computed on the first time step, please check your data." ) tmp = self.generators_info() if len(tmp) != 3: raise EnvError( "\"generators_info()\" should return a tuple with 3 elements: p, q and v" ) for el in tmp: if el.shape[0] != self.n_gen: raise IncorrectNumberOfGenerators( "returned by \"backend.generators_info()\"") tmp = self.loads_info() if len(tmp) != 3: raise EnvError( "\"loads_info()\" should return a tuple with 3 elements: p, q and v" ) for el in tmp: if el.shape[0] != self.n_load: raise IncorrectNumberOfLoads( "returned by \"backend.loads_info()\"") tmp = self.lines_or_info() if len(tmp) != 4: raise EnvError( "\"lines_or_info()\" should return a tuple with 4 elements: p, q, v and a" ) for el in tmp: if el.shape[0] != self.n_line: raise IncorrectNumberOfLines( "returned by \"backend.lines_or_info()\"") tmp = self.lines_ex_info() if len(tmp) != 4: raise EnvError( "\"lines_ex_info()\" should return a tuple with 4 elements: p, q, v and a" ) for el in tmp: if el.shape[0] != self.n_line: raise IncorrectNumberOfLines( "returned by \"backend.lines_ex_info()\"") tmp = self.get_topo_vect() if tmp.shape[0] != np.sum(self.sub_info): raise IncorrectNumberOfElements( "returned by \"backend.get_topo_vect()\"") if np.any(~np.isfinite(tmp)): raise EnvError( "Some components of \"backend.get_topo_vect()\" are not finite. This should be integer." )
def check_validity(self, backend): at_least_one = False if self.load_p is not None: if self.load_p.shape[1] != backend.n_load: msg_err = "for the active part. It should be {} but is in fact {}" raise IncorrectNumberOfLoads( msg_err.format(backend.n_load, self.load_p.shape[1])) at_least_one = True if self.load_q is not None: if self.load_q.shape[1] != backend.n_load: msg_err = "for the reactive part. It should be {} but is in fact {}" raise IncorrectNumberOfLoads( msg_err.format(backend.n_load, self.load_q.shape[1])) at_least_one = True if self.prod_p is not None: if self.prod_p.shape[1] != backend.n_gen: msg_err = "for the active part. It should be {} but is in fact {}" raise IncorrectNumberOfGenerators( msg_err.format(backend.n_gen, self.prod_p.shape[1])) at_least_one = True if self.prod_v is not None: if self.prod_v.shape[1] != backend.n_gen: msg_err = "for the voltage part. It should be {} but is in fact {}" raise IncorrectNumberOfGenerators( msg_err.format(backend.n_gen, self.prod_v.shape[1])) at_least_one = True if self.hazards is not None: if self.hazards.shape[1] != backend.n_line: msg_err = "for the outage. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.hazards.shape[1])) at_least_one = True if self.maintenance is not None: if self.maintenance.shape[1] != backend.n_line: msg_err = "for the maintenance. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.maintenance.shape[1])) at_least_one = True if self.maintenance_time is not None: if self.maintenance_time.shape[1] != backend.n_line: msg_err = "for the maintenance times. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.maintenance_time.shape[1])) at_least_one = True if self.maintenance_duration is not None: if self.maintenance_duration.shape[1] != backend.n_line: msg_err = "for the maintenance durations. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.maintenance_duration.shape[1])) at_least_one = True if self.hazard_duration is not None: if self.hazard_duration.shape[1] != backend.n_line: msg_err = "for the hazard durations. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.hazard_duration.shape[1])) at_least_one = True if not at_least_one: raise ChronicsError( "No files are found in directory \"{}\". If you don't want to load any chronics, use " "\"ChangeNothing\" and not \"{}\" to load chronics." "".format(self.path, type(self))) for name_arr, arr in zip([ "load_q", "load_p", "prod_v", "prod_p", "maintenance", "hazards", "maintenance time", "maintenance duration", "hazard duration" ], [ self.load_q, self.load_p, self.prod_v, self.prod_p, self.maintenance, self.hazards, self.maintenance_time, self.maintenance_duration, self.hazard_duration ]): if arr is not None: if self.chunk_size is None: if arr.shape[0] != self.n_: msg_err = "Array {} has not the same number of rows than the maintenance. " \ "The chronics cannot be loaded properly." raise EnvError(msg_err.format(name_arr)) 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))
def check_validity(self, backend): """ A call to this method ensure that the action that will be sent to the current :class:`grid2op.Environment` can be properly implemented by its :class:`grid2op.Backend`. This specific method check that the dimension of all vectors are consistent Parameters ---------- backend: :class:`grid2op.Backend.Backend` The backend used by the :class:`grid2op.Environment.Environment` Returns ------- ``None`` """ at_least_one = False if self.load_p is not None: if self.load_p.shape[1] != backend.n_load: msg_err = "for the active part. It should be {} but is in fact {}" raise IncorrectNumberOfLoads( msg_err.format(backend.n_load, self.load_p.shape[1])) at_least_one = True if self.load_q is not None: if self.load_q.shape[1] != backend.n_load: msg_err = "for the reactive part. It should be {} but is in fact {}" raise IncorrectNumberOfLoads( msg_err.format(backend.n_load, self.load_q.shape[1])) at_least_one = True if self.prod_p is not None: if self.prod_p.shape[1] != backend.n_gen: msg_err = "for the active part. It should be {} but is in fact {}" raise IncorrectNumberOfGenerators( msg_err.format(backend.n_gen, self.prod_p.shape[1])) at_least_one = True if self.prod_v is not None: if self.prod_v.shape[1] != backend.n_gen: msg_err = "for the voltage part. It should be {} but is in fact {}" raise IncorrectNumberOfGenerators( msg_err.format(backend.n_gen, self.prod_v.shape[1])) at_least_one = True if self.hazards is not None: if self.hazards.shape[1] != backend.n_line: msg_err = "for the outage. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.hazards.shape[1])) at_least_one = True if self.maintenance is not None: if self.maintenance.shape[1] != backend.n_line: msg_err = "for the maintenance. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.maintenance.shape[1])) at_least_one = True if self.maintenance_time is not None: if self.maintenance_time.shape[1] != backend.n_line: msg_err = "for the maintenance times. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.maintenance_time.shape[1])) at_least_one = True if self.maintenance_duration is not None: if self.maintenance_duration.shape[1] != backend.n_line: msg_err = "for the maintenance durations. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.maintenance_duration.shape[1])) at_least_one = True if self.hazard_duration is not None: if self.hazard_duration.shape[1] != backend.n_line: msg_err = "for the hazard durations. It should be {} but is in fact {}" raise IncorrectNumberOfLines( msg_err.format(backend.n_line, self.hazard_duration.shape[1])) at_least_one = True if not at_least_one: raise ChronicsError( "No files are found in directory \"{}\". If you don't want to load any chronics, use " "\"ChangeNothing\" and not \"{}\" to load chronics." "".format(self.path, type(self))) for name_arr, arr in zip([ "load_q", "load_p", "prod_v", "prod_p", "maintenance", "hazards", "maintenance time", "maintenance duration", "hazard duration" ], [ self.load_q, self.load_p, self.prod_v, self.prod_p, self.maintenance, self.hazards, self.maintenance_time, self.maintenance_duration, self.hazard_duration ]): if arr is not None: if self.chunk_size is None: if arr.shape[0] != self.n_: msg_err = "Array {} has not the same number of rows of load_p. The chronics cannot be loaded properly." raise EnvError(msg_err.format(name_arr)) 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))
def __init__( self, init_grid_path: str, path_chron, # path where chronics of injections are stored name_env="unknown", parameters_path=None, names_chronics_to_backend=None, actionClass=TopologyAction, observationClass=CompleteObservation, rewardClass=FlatReward, legalActClass=AlwaysLegal, envClass=Environment, gridStateclass=GridStateFromFile, # type of chronics to use. For example GridStateFromFile if forecasts are not used, # or GridStateFromFileWithForecasts otherwise backendClass=PandaPowerBackend, agentClass=DoNothingAgent, # class used to build the agent agentInstance=None, verbose=False, gridStateclass_kwargs={}, voltageControlerClass=ControlVoltageFromFile, thermal_limit_a=None, max_iter=-1, other_rewards={}, opponent_action_class=DontAct, opponent_class=BaseOpponent, opponent_init_budget=0., opponent_budget_per_ts=0., opponent_budget_class=NeverAttackBudget, opponent_attack_duration=0, opponent_attack_cooldown=99999, opponent_kwargs={}, grid_layout=None, with_forecast=True, attention_budget_cls=LinearAttentionBudget, kwargs_attention_budget=None, has_attention_budget=False, # experimental: whether to read from local dir or generate the classes on the fly: _read_from_local_dir=False): """ Initialize the Runner. Parameters ---------- init_grid_path: ``str`` Madantory, used to initialize :attr:`Runner.init_grid_path`. path_chron: ``str`` Madantory where to look for chronics data, used to initialize :attr:`Runner.path_chron`. parameters_path: ``str`` or ``dict``, optional Used to initialize :attr:`Runner.parameters_path`. If it's a string, this will suppose parameters are located at this path, if it's a dictionary, this will use the parameters converted from this dictionary. names_chronics_to_backend: ``dict``, optional Used to initialize :attr:`Runner.names_chronics_to_backend`. actionClass: ``type``, optional Used to initialize :attr:`Runner.actionClass`. observationClass: ``type``, optional Used to initialize :attr:`Runner.observationClass`. rewardClass: ``type``, optional Used to initialize :attr:`Runner.rewardClass`. Default to :class:`grid2op.ConstantReward` that *should not** be used to train or evaluate an agent, but rather as debugging purpose. legalActClass: ``type``, optional Used to initialize :attr:`Runner.legalActClass`. envClass: ``type``, optional Used to initialize :attr:`Runner.envClass`. gridStateclass: ``type``, optional Used to initialize :attr:`Runner.gridStateclass`. backendClass: ``type``, optional Used to initialize :attr:`Runner.backendClass`. agentClass: ``type``, optional Used to initialize :attr:`Runner.agentClass`. agentInstance: :class:`grid2op.Agent.Agent` Used to initialize the agent. Note that either :attr:`agentClass` or :attr:`agentInstance` is used at the same time. If both ot them are ``None`` or both of them are "not ``None``" it throw an error. verbose: ``bool``, optional Used to initialize :attr:`Runner.verbose`. thermal_limit_a: ``numpy.ndarray`` The thermal limit for the environment (if any). voltagecontrolerClass: :class:`grid2op.VoltageControler.ControlVoltageFromFile`, optional The controler that will change the voltage setpoints of the generators. # TODO documentation on the opponent # TOOD doc for the attention budget """ self.with_forecast = with_forecast self.name_env = name_env if not isinstance(envClass, type): raise Grid2OpException( "Parameter \"envClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(envClass))) if not issubclass(envClass, Environment): raise RuntimeError( "Impossible to create a runner without an evnrionment derived from grid2op.Environement" " class. Please modify \"envClass\" parameter.") self.envClass = envClass if not isinstance(actionClass, type): raise Grid2OpException( "Parameter \"actionClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(actionClass))) if not issubclass(actionClass, BaseAction): raise RuntimeError( "Impossible to create a runner without an action class derived from grid2op.BaseAction. " "Please modify \"actionClass\" parameter.") self.actionClass = actionClass if not isinstance(observationClass, type): raise Grid2OpException( "Parameter \"observationClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(observationClass))) if not issubclass(observationClass, BaseObservation): raise RuntimeError( "Impossible to create a runner without an observation class derived from " "grid2op.BaseObservation. Please modify \"observationClass\" parameter." ) self.observationClass = observationClass if not isinstance(rewardClass, type): raise Grid2OpException( "Parameter \"rewardClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(rewardClass))) if not issubclass(rewardClass, BaseReward): raise RuntimeError( "Impossible to create a runner without an observation class derived from " "grid2op.BaseReward. Please modify \"rewardClass\" parameter.") self.rewardClass = rewardClass if not isinstance(gridStateclass, type): raise Grid2OpException( "Parameter \"gridStateclass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(gridStateclass))) if not issubclass(gridStateclass, GridValue): raise RuntimeError( "Impossible to create a runner without an chronics class derived from " "grid2op.GridValue. Please modify \"gridStateclass\" parameter." ) self.gridStateclass = gridStateclass if not isinstance(legalActClass, type): raise Grid2OpException( "Parameter \"legalActClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(legalActClass))) if not issubclass(legalActClass, BaseRules): raise RuntimeError( "Impossible to create a runner without a class defining legal actions derived " "from grid2op.BaseRules. Please modify \"legalActClass\" parameter." ) self.legalActClass = legalActClass if not isinstance(backendClass, type): raise Grid2OpException( "Parameter \"legalActClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(backendClass))) if not issubclass(backendClass, Backend): raise RuntimeError( "Impossible to create a runner without a backend class derived from grid2op.GridValue. " "Please modify \"backendClass\" parameter.") self.backendClass = backendClass self.__can_copy_agent = True if agentClass is not None: if agentInstance is not None: raise RuntimeError( "Impossible to build the backend. Only one of AgentClass or agentInstance can be " "used (both are not None).") if not isinstance(agentClass, type): raise Grid2OpException( "Parameter \"agentClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(agentClass))) if not issubclass(agentClass, BaseAgent): raise RuntimeError( "Impossible to create a runner without an agent class derived from " "grid2op.BaseAgent. " "Please modify \"agentClass\" parameter.") self.agentClass = agentClass self._useclass = True self.agent = None elif agentInstance is not None: if not isinstance(agentInstance, BaseAgent): raise RuntimeError( "Impossible to create a runner without an agent class derived from " "grid2op.BaseAgent. " "Please modify \"agentInstance\" parameter.") self.agentClass = None self._useclass = False self.agent = agentInstance # Test if we can copy the agent for parallel runs try: copy.copy(self.agent) except: self.__can_copy_agent = False else: raise RuntimeError( "Impossible to build the backend. Either AgentClass or agentInstance must be provided " "and both are None.") self.agentInstance = agentInstance self._read_from_local_dir = _read_from_local_dir self.logger = ConsoleLog( DoNothingLog.INFO if verbose else DoNothingLog.ERROR) # store _parameters self.init_grid_path = init_grid_path self.names_chronics_to_backend = names_chronics_to_backend # game _parameters self.parameters_path = parameters_path if isinstance(parameters_path, str): self.parameters = Parameters(parameters_path) elif isinstance(parameters_path, dict): self.parameters = Parameters() self.parameters.init_from_dict(parameters_path) elif parameters_path is None: self.parameters = Parameters() else: raise RuntimeError( "Impossible to build the parameters. The argument \"parameters_path\" should either " "be a string or a dictionary.") # chronics of grid state self.path_chron = path_chron self.gridStateclass_kwargs = gridStateclass_kwargs self.max_iter = max_iter if max_iter > 0: self.gridStateclass_kwargs["max_iter"] = max_iter self.chronics_handler = ChronicsHandler( chronicsClass=self.gridStateclass, path=self.path_chron, **self.gridStateclass_kwargs) self.verbose = verbose self.thermal_limit_a = thermal_limit_a # controler for voltage if not issubclass(voltageControlerClass, ControlVoltageFromFile): raise Grid2OpException( "Parameter \"voltagecontrolClass\" should derive from \"ControlVoltageFromFile\"." ) self.voltageControlerClass = voltageControlerClass self._other_rewards = other_rewards # for opponent (should be defined here) after the initialization of BaseEnv if not issubclass(opponent_action_class, BaseAction): raise EnvError( "Impossible to make an environment with an opponent action class not " "derived from BaseAction") try: self.opponent_init_budget = dt_float(opponent_init_budget) except Exception as e: raise EnvError( "Impossible to convert \"opponent_init_budget\" to a float with error {}" .format(e)) if self.opponent_init_budget < 0.: raise EnvError( "If you want to deactive the opponent, please don't set its budget to a negative number." "Prefer the use of the DontAct action type (\"opponent_action_class=DontAct\" " "and / or set its budget to 0.") if not issubclass(opponent_class, BaseOpponent): raise EnvError( "Impossible to make an opponent with a type that does not inherit from BaseOpponent." ) self.opponent_action_class = opponent_action_class self.opponent_class = opponent_class self.opponent_init_budget = opponent_init_budget self.opponent_budget_per_ts = opponent_budget_per_ts self.opponent_budget_class = opponent_budget_class self.opponent_attack_duration = opponent_attack_duration self.opponent_attack_cooldown = opponent_attack_cooldown self.opponent_kwargs = opponent_kwargs self.grid_layout = grid_layout # attention budget self._attention_budget_cls = attention_budget_cls self._kwargs_attention_budget = copy.deepcopy(kwargs_attention_budget) self._has_attention_budget = has_attention_budget # otherwise on windows / macos it sometimes fail in the runner in multi process # on linux like OS i prefer to generate all the proper classes accordingly if _IS_LINUX: pass with warnings.catch_warnings(): warnings.filterwarnings("ignore") with self.init_env() as env: bk_class = type(env.backend) pass self.__used = False
def __init__( self, envs_dir, experimental_read_from_local_dir=False, _add_to_name="", # internal, for test only, do not use ! _compat_glop_version=None, # internal, for test only, do not use ! _test=False, **kwargs): GridObjects.__init__(self) RandomObject.__init__(self) self.current_env = None self.env_index = None self.mix_envs = [] self._env_dir = os.path.abspath(envs_dir) # Special case handling for backend # TODO: with backend.copy() instead ! backendClass = None if "backend" in kwargs: backendClass = type(kwargs["backend"]) del kwargs["backend"] # Inline import to prevent cyclical import from grid2op.MakeEnv.Make import make # TODO reuse same observation_space and action_space in all the envs maybe ? try: for env_dir in sorted(os.listdir(envs_dir)): env_path = os.path.join(envs_dir, env_dir) if not os.path.isdir(env_path): continue # Special case for backend if backendClass is not None: env = make(env_path, backend=backendClass(), _add_to_name=_add_to_name, _compat_glop_version=_compat_glop_version, test=_test, experimental_read_from_local_dir= experimental_read_from_local_dir, **kwargs) else: env = make(env_path, _add_to_name=_add_to_name, _compat_glop_version=_compat_glop_version, test=_test, experimental_read_from_local_dir= experimental_read_from_local_dir, **kwargs) self.mix_envs.append(env) except Exception as exc_: err_msg = "MultiMix environment creation failed: {}".format(exc_) raise EnvError(err_msg) if len(self.mix_envs) == 0: err_msg = "MultiMix envs_dir did not contain any valid env" raise EnvError(err_msg) self.env_index = 0 self.current_env = self.mix_envs[self.env_index] # Make sure GridObject class attributes are set from first env # Should be fine since the grid is the same for all envs multi_env_name = os.path.basename( os.path.abspath(envs_dir)) + _add_to_name save_env_name = self.current_env.env_name self.current_env.env_name = multi_env_name self.__class__ = self.init_grid(self.current_env) self.current_env.env_name = save_env_name