Esempio n. 1
0
    def __init__(self, detailed_infos_for_cascading_failures=False):
        """
        Initialize an instance of Backend. This does nothing per se. Only the call to :func:`Backend.load_grid`
        should guarantee the backend is properly configured.

        :param detailed_infos_for_cascading_failures: Whether to be detailed (but slow) when computing cascading failures
        :type detailed_infos_for_cascading_failures: :class:`bool`

        """
        # lazy loading
        from grid2op.Action import CompleteAction
        from grid2op.Action._BackendAction import _BackendAction

        GridObjects.__init__(self)

        # the following parameter is used to control the amount of verbosity when computing a cascading failure
        # if it's set to true, it returns all intermediate _grid states. This can slow down the computation!
        self.detailed_infos_for_cascading_failures = detailed_infos_for_cascading_failures

        # the power _grid manipulated. One powergrid per backend.
        self._grid = None

        # thermal limit setting, in ampere, at the same "side" of the powerline than self.get_line_overflow
        self.thermal_limit_a = None

        # action to set me
        self.my_bk_act_class = _BackendAction
        self._complete_action_class = CompleteAction

        # for the shunt (only if supported)
        self._sh_vnkv = None  # for each shunt gives the nominal value at the bus at which it is connected
Esempio n. 2
0
    def __init__(self):
        GridObjects.__init__(self)
        # last connected registered
        self.last_topo_registered = ValueStore(self.dim_topo, dtype=dt_int)

        # topo at time t
        self.current_topo = ValueStore(self.dim_topo, dtype=dt_int)

        # injection at time t
        self.prod_p = ValueStore(self.n_gen, dtype=dt_float)
        self.prod_v = ValueStore(self.n_gen, dtype=dt_float)
        self.load_p = ValueStore(self.n_load, dtype=dt_float)
        self.load_q = ValueStore(self.n_load, dtype=dt_float)

        self.activated_bus = np.full((self.n_sub, 2),
                                     dtype=dt_bool,
                                     fill_value=False)
        self.big_topo_to_subid = np.repeat(list(range(self.n_sub)),
                                           repeats=self.sub_info)

        # shunts
        if self.shunts_data_available:
            self.shunt_p = ValueStore(self.n_shunt, dtype=dt_float)
            self.shunt_q = ValueStore(self.n_shunt, dtype=dt_float)
            self.shunt_bus = ValueStore(self.n_shunt, dtype=dt_int)
Esempio n. 3
0
    def __init__(self, envs):
        GridObjects.__init__(self)
        self.envs = envs
        for env in envs:
            if not isinstance(env, Environment):
                raise MultiEnvException(
                    "You provided environment of type \"{}\" which is not supported."
                    "Please only provide a grid2op.Environment.Environment class."
                    "".format(type(env)))

        self.nb_env = len(envs)
        max_int = np.iinfo(dt_int).max
        self._remotes, self._work_remotes = zip(
            *[Pipe() for _ in range(self.nb_env)])

        env_params = [
            envs[e].get_kwargs(with_backend=False) for e in range(self.nb_env)
        ]
        self._ps = [
            RemoteEnv(env_params=env_,
                      remote=work_remote,
                      parent_remote=remote,
                      name="{}_subprocess_{}".format(envs[i].name, i),
                      seed=envs[i].space_prng.randint(max_int))
            for i, (work_remote, remote, env_) in enumerate(
                zip(self._work_remotes, self._remotes, env_params))
        ]

        for p in self._ps:
            p.daemon = True  # if the main process crashes, we should not cause things to hang
            p.start()
        for remote in self._work_remotes:
            remote.close()

        self._waiting = True
Esempio n. 4
0
    def __init__(self, nb_env, env):
        GridObjects.__init__(self)
        self.imported_env = env
        self.nb_env = nb_env

        self._remotes, self._work_remotes = zip(
            *[Pipe() for _ in range(self.nb_env)])

        env_params = [env.get_kwargs() for _ in range(self.nb_env)]
        for el in env_params:
            el["backendClass"] = type(env.backend)
        self._ps = [
            RemoteEnv(env_params=env_,
                      remote=work_remote,
                      parent_remote=remote,
                      name="env: {}".format(i),
                      seed=np.random.randint(np.iinfo(dt_int).max))
            for i, (work_remote, remote, env_) in enumerate(
                zip(self._work_remotes, self._remotes, env_params))
        ]

        for p in self._ps:
            p.daemon = True  # if the main process crashes, we should not cause things to hang
            p.start()
        for remote in self._work_remotes:
            remote.close()

        self._waiting = True
Esempio n. 5
0
    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
Esempio n. 6
0
    def __init__(self, detailed_infos_for_cascading_failures=False):
        """
        Initialize an instance of Backend. This does nothing per se. Only the call to :func:`Backend.load_grid`
        should guarantee the backend is properly configured.

        :param detailed_infos_for_cascading_failures: Whether to be detailed (but slow) when computing cascading failures
        :type detailed_infos_for_cascading_failures: :class:`bool`

        """
        GridObjects.__init__(self)

        # the following parameter is used to control the amount of verbosity when computing a cascading failure
        # if it's set to true, it returns all intermediate _grid states. This can slow down the computation!
        self.detailed_infos_for_cascading_failures = detailed_infos_for_cascading_failures

        # the power _grid manipulated. One powergrid per backend.
        self._grid = None

        # thermal limit setting, in ampere, at the same "side" of the powerline than self.get_line_overflow
        self.thermal_limit_a = None
Esempio n. 7
0
    def __init__(self, obs_env=None, action_helper=None, seed=None):
        GridObjects.__init__(self)

        self.action_helper = action_helper

        # time stamp information
        self.year = 1970
        self.month = 0
        self.day = 0
        self.hour_of_day = 0
        self.minute_of_hour = 0
        self.day_of_week = 0

        # for non deterministic observation that would not use default np.random module
        self.seed = None

        # handles the forecasts here
        self._forecasted_grid_act = {}
        self._forecasted_inj = []

        self.timestep_overflow = np.zeros(shape=(self.n_line, ), dtype=dt_int)

        # 0. (line is disconnected) / 1. (line is connected)
        self.line_status = np.ones(shape=self.n_line, dtype=dt_bool)

        # topological vector
        self.topo_vect = np.full(shape=self.dim_topo,
                                 dtype=dt_int,
                                 fill_value=0)

        # generators information
        self.prod_p = np.full(shape=self.n_gen,
                              dtype=dt_float,
                              fill_value=np.NaN)
        self.prod_q = np.full(shape=self.n_gen,
                              dtype=dt_float,
                              fill_value=np.NaN)
        self.prod_v = np.full(shape=self.n_gen,
                              dtype=dt_float,
                              fill_value=np.NaN)
        # loads information
        self.load_p = np.full(shape=self.n_load,
                              dtype=dt_float,
                              fill_value=np.NaN)
        self.load_q = np.full(shape=self.n_load,
                              dtype=dt_float,
                              fill_value=np.NaN)
        self.load_v = np.full(shape=self.n_load,
                              dtype=dt_float,
                              fill_value=np.NaN)
        # lines origin information
        self.p_or = np.full(shape=self.n_line,
                            dtype=dt_float,
                            fill_value=np.NaN)
        self.q_or = np.full(shape=self.n_line,
                            dtype=dt_float,
                            fill_value=np.NaN)
        self.v_or = np.full(shape=self.n_line,
                            dtype=dt_float,
                            fill_value=np.NaN)
        self.a_or = np.full(shape=self.n_line,
                            dtype=dt_float,
                            fill_value=np.NaN)
        # lines extremity information
        self.p_ex = np.full(shape=self.n_line,
                            dtype=dt_float,
                            fill_value=np.NaN)
        self.q_ex = np.full(shape=self.n_line,
                            dtype=dt_float,
                            fill_value=np.NaN)
        self.v_ex = np.full(shape=self.n_line,
                            dtype=dt_float,
                            fill_value=np.NaN)
        self.a_ex = np.full(shape=self.n_line,
                            dtype=dt_float,
                            fill_value=np.NaN)
        # lines relative flows
        self.rho = np.full(shape=self.n_line,
                           dtype=dt_float,
                           fill_value=np.NaN)

        # cool down and reconnection time after hard overflow, soft overflow or cascading failure
        self.time_before_cooldown_line = np.full(shape=self.n_line,
                                                 dtype=dt_int,
                                                 fill_value=-1)
        self.time_before_cooldown_sub = np.full(shape=self.n_sub,
                                                dtype=dt_int,
                                                fill_value=-1)
        self.time_next_maintenance = np.full(shape=self.n_line,
                                             dtype=dt_int,
                                             fill_value=-1)
        self.duration_next_maintenance = np.full(shape=self.n_line,
                                                 dtype=dt_int,
                                                 fill_value=-1)

        # calendar data
        self.year = dt_int(1970)
        self.month = dt_int(0)
        self.day = dt_int(0)
        self.hour_of_day = dt_int(0)
        self.minute_of_hour = dt_int(0)
        self.day_of_week = dt_int(0)

        # forecasts
        self._forecasted_inj = []
        self._forecasted_grid = []
        self._obs_env = obs_env

        # redispatching
        self.target_dispatch = np.full(shape=self.n_gen,
                                       dtype=dt_float,
                                       fill_value=np.NaN)
        self.actual_dispatch = np.full(shape=self.n_gen,
                                       dtype=dt_float,
                                       fill_value=np.NaN)

        # value to assess if two observations are equal
        self._tol_equal = 5e-1

        self.attr_list_vect = None
Esempio n. 8
0
    def setUp(self):
        """
        The case file is a representation of the case14 as found in the ieee14 powergrid.
        :return:
        """
        self.tolvect = 1e-2
        self.tol_one = 1e-5
        self.game_rules = RulesChecker()
        self.gridobj = GridObjects()
        self.gridobj.init_grid_vect(
            name_prod=["gen_{}".format(i) for i in range(5)],
            name_load=["load_{}".format(i) for i in range(11)],
            name_line=["line_{}".format(i) for i in range(20)],
            name_sub=["sub_{}".format(i) for i in range(14)],
            sub_info=np.array([3, 6, 4, 6, 5, 6, 3, 2, 5, 3, 3, 3, 4, 3],
                              dtype=np.int),
            load_to_subid=np.array([1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13]),
            gen_to_subid=np.array([0, 1, 2, 5, 7]),
            line_or_to_subid=np.array(
                [0, 0, 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 5, 6, 6, 8, 8, 9, 11,
                 12]),
            line_ex_to_subid=np.array([
                1, 4, 2, 3, 4, 3, 4, 6, 8, 5, 10, 11, 12, 7, 8, 9, 13, 10, 12,
                13
            ]),
            load_to_sub_pos=np.array([4, 2, 5, 4, 4, 4, 1, 1, 1, 2, 1]),
            gen_to_sub_pos=np.array([2, 5, 3, 5, 1]),
            line_or_to_sub_pos=np.array(
                [0, 1, 1, 2, 3, 1, 2, 3, 4, 3, 1, 2, 3, 1, 2, 2, 3, 0, 0, 1]),
            line_ex_to_sub_pos=np.array(
                [0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 2, 2, 3, 0, 1, 2, 2, 0, 0, 0]),
            load_pos_topo_vect=np.array(
                [7, 11, 18, 23, 28, 39, 41, 44, 47, 51, 54]),
            gen_pos_topo_vect=np.array([2, 8, 12, 29, 34]),
            line_or_pos_topo_vect=np.array([
                0, 1, 4, 5, 6, 10, 15, 16, 17, 22, 25, 26, 27, 31, 32, 37, 38,
                40, 46, 50
            ]),
            line_ex_pos_topo_vect=np.array([
                3, 19, 9, 13, 20, 14, 21, 30, 35, 24, 45, 48, 52, 33, 36, 42,
                55, 43, 49, 53
            ]))
        # pdb.set_trace()
        self.res = {
            'name_gen': ['gen_0', 'gen_1', 'gen_2', 'gen_3', 'gen_4'],
            'name_load': [
                'load_0', 'load_1', 'load_2', 'load_3', 'load_4', 'load_5',
                'load_6', 'load_7', 'load_8', 'load_9', 'load_10'
            ],
            'name_line': [
                'line_0', 'line_1', 'line_2', 'line_3', 'line_4', 'line_5',
                'line_6', 'line_7', 'line_8', 'line_9', 'line_10', 'line_11',
                'line_12', 'line_13', 'line_14', 'line_15', 'line_16',
                'line_17', 'line_18', 'line_19'
            ],
            'name_sub': [
                'sub_0', 'sub_1', 'sub_2', 'sub_3', 'sub_4', 'sub_5', 'sub_6',
                'sub_7', 'sub_8', 'sub_9', 'sub_10', 'sub_11', 'sub_12',
                'sub_13'
            ],
            'sub_info': [3, 6, 4, 6, 5, 6, 3, 2, 5, 3, 3, 3, 4, 3],
            'load_to_subid': [1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13],
            'gen_to_subid': [0, 1, 2, 5, 7],
            'line_or_to_subid':
            [0, 0, 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 5, 6, 6, 8, 8, 9, 11, 12],
            'line_ex_to_subid': [
                1, 4, 2, 3, 4, 3, 4, 6, 8, 5, 10, 11, 12, 7, 8, 9, 13, 10, 12,
                13
            ],
            'load_to_sub_pos': [4, 2, 5, 4, 4, 4, 1, 1, 1, 2, 1],
            'gen_to_sub_pos': [2, 5, 3, 5, 1],
            'line_or_to_sub_pos':
            [0, 1, 1, 2, 3, 1, 2, 3, 4, 3, 1, 2, 3, 1, 2, 2, 3, 0, 0, 1],
            'line_ex_to_sub_pos':
            [0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 2, 2, 3, 0, 1, 2, 2, 0, 0, 0],
            'load_pos_topo_vect': [7, 11, 18, 23, 28, 39, 41, 44, 47, 51, 54],
            'gen_pos_topo_vect': [2, 8, 12, 29, 34],
            'line_or_pos_topo_vect': [
                0, 1, 4, 5, 6, 10, 15, 16, 17, 22, 25, 26, 27, 31, 32, 37, 38,
                40, 46, 50
            ],
            'line_ex_pos_topo_vect': [
                3, 19, 9, 13, 20, 14, 21, 30, 35, 24, 45, 48, 52, 33, 36, 42,
                55, 43, 49, 53
            ],
            'gen_type':
            None,
            'gen_pmin':
            None,
            'gen_pmax':
            None,
            'gen_redispatchable':
            None,
            'gen_max_ramp_up':
            None,
            'gen_max_ramp_down':
            None,
            'gen_min_uptime':
            None,
            'gen_min_downtime':
            None,
            'gen_cost_per_MW':
            None,
            'gen_startup_cost':
            None,
            'gen_shutdown_cost':
            None,
            "grid_layout":
            None,
            "shunt_to_subid":
            None,
            "name_shunt":
            None
        }

        # self.size_act = 229

        # self.helper_action = ActionSpace(self.gridobj, legal_action=self.game_rules.legal_action)
        self.helper_action = self._action_setup()
        save_to_dict(self.res, self.helper_action, "subtype",
                     lambda x: re.sub("(<class ')|('>)", "", "{}".format(x)))
        # print(self.res["subtype"])
        self.authorized_keys = self.helper_action().authorized_keys
        self.size_act = self.helper_action.size()
Esempio n. 9
0
class TestActionBase(ABC):
    @abstractmethod
    def _action_setup(self):
        pass

    def _skipMissingKey(self, key):
        if key not in self.authorized_keys:
            unittest.TestCase.skipTest(
                self, "Skipped: Missing authorized_key {key}")

    def setUp(self):
        """
        The case file is a representation of the case14 as found in the ieee14 powergrid.
        :return:
        """
        self.tolvect = 1e-2
        self.tol_one = 1e-5
        self.game_rules = RulesChecker()
        self.gridobj = GridObjects()
        self.gridobj.init_grid_vect(
            name_prod=["gen_{}".format(i) for i in range(5)],
            name_load=["load_{}".format(i) for i in range(11)],
            name_line=["line_{}".format(i) for i in range(20)],
            name_sub=["sub_{}".format(i) for i in range(14)],
            sub_info=np.array([3, 6, 4, 6, 5, 6, 3, 2, 5, 3, 3, 3, 4, 3],
                              dtype=np.int),
            load_to_subid=np.array([1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13]),
            gen_to_subid=np.array([0, 1, 2, 5, 7]),
            line_or_to_subid=np.array(
                [0, 0, 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 5, 6, 6, 8, 8, 9, 11,
                 12]),
            line_ex_to_subid=np.array([
                1, 4, 2, 3, 4, 3, 4, 6, 8, 5, 10, 11, 12, 7, 8, 9, 13, 10, 12,
                13
            ]),
            load_to_sub_pos=np.array([4, 2, 5, 4, 4, 4, 1, 1, 1, 2, 1]),
            gen_to_sub_pos=np.array([2, 5, 3, 5, 1]),
            line_or_to_sub_pos=np.array(
                [0, 1, 1, 2, 3, 1, 2, 3, 4, 3, 1, 2, 3, 1, 2, 2, 3, 0, 0, 1]),
            line_ex_to_sub_pos=np.array(
                [0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 2, 2, 3, 0, 1, 2, 2, 0, 0, 0]),
            load_pos_topo_vect=np.array(
                [7, 11, 18, 23, 28, 39, 41, 44, 47, 51, 54]),
            gen_pos_topo_vect=np.array([2, 8, 12, 29, 34]),
            line_or_pos_topo_vect=np.array([
                0, 1, 4, 5, 6, 10, 15, 16, 17, 22, 25, 26, 27, 31, 32, 37, 38,
                40, 46, 50
            ]),
            line_ex_pos_topo_vect=np.array([
                3, 19, 9, 13, 20, 14, 21, 30, 35, 24, 45, 48, 52, 33, 36, 42,
                55, 43, 49, 53
            ]))
        # pdb.set_trace()
        self.res = {
            'name_gen': ['gen_0', 'gen_1', 'gen_2', 'gen_3', 'gen_4'],
            'name_load': [
                'load_0', 'load_1', 'load_2', 'load_3', 'load_4', 'load_5',
                'load_6', 'load_7', 'load_8', 'load_9', 'load_10'
            ],
            'name_line': [
                'line_0', 'line_1', 'line_2', 'line_3', 'line_4', 'line_5',
                'line_6', 'line_7', 'line_8', 'line_9', 'line_10', 'line_11',
                'line_12', 'line_13', 'line_14', 'line_15', 'line_16',
                'line_17', 'line_18', 'line_19'
            ],
            'name_sub': [
                'sub_0', 'sub_1', 'sub_2', 'sub_3', 'sub_4', 'sub_5', 'sub_6',
                'sub_7', 'sub_8', 'sub_9', 'sub_10', 'sub_11', 'sub_12',
                'sub_13'
            ],
            'sub_info': [3, 6, 4, 6, 5, 6, 3, 2, 5, 3, 3, 3, 4, 3],
            'load_to_subid': [1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13],
            'gen_to_subid': [0, 1, 2, 5, 7],
            'line_or_to_subid':
            [0, 0, 1, 1, 1, 2, 3, 3, 3, 4, 5, 5, 5, 6, 6, 8, 8, 9, 11, 12],
            'line_ex_to_subid': [
                1, 4, 2, 3, 4, 3, 4, 6, 8, 5, 10, 11, 12, 7, 8, 9, 13, 10, 12,
                13
            ],
            'load_to_sub_pos': [4, 2, 5, 4, 4, 4, 1, 1, 1, 2, 1],
            'gen_to_sub_pos': [2, 5, 3, 5, 1],
            'line_or_to_sub_pos':
            [0, 1, 1, 2, 3, 1, 2, 3, 4, 3, 1, 2, 3, 1, 2, 2, 3, 0, 0, 1],
            'line_ex_to_sub_pos':
            [0, 0, 0, 0, 1, 1, 2, 0, 0, 0, 2, 2, 3, 0, 1, 2, 2, 0, 0, 0],
            'load_pos_topo_vect': [7, 11, 18, 23, 28, 39, 41, 44, 47, 51, 54],
            'gen_pos_topo_vect': [2, 8, 12, 29, 34],
            'line_or_pos_topo_vect': [
                0, 1, 4, 5, 6, 10, 15, 16, 17, 22, 25, 26, 27, 31, 32, 37, 38,
                40, 46, 50
            ],
            'line_ex_pos_topo_vect': [
                3, 19, 9, 13, 20, 14, 21, 30, 35, 24, 45, 48, 52, 33, 36, 42,
                55, 43, 49, 53
            ],
            'gen_type':
            None,
            'gen_pmin':
            None,
            'gen_pmax':
            None,
            'gen_redispatchable':
            None,
            'gen_max_ramp_up':
            None,
            'gen_max_ramp_down':
            None,
            'gen_min_uptime':
            None,
            'gen_min_downtime':
            None,
            'gen_cost_per_MW':
            None,
            'gen_startup_cost':
            None,
            'gen_shutdown_cost':
            None,
            "grid_layout":
            None,
            "shunt_to_subid":
            None,
            "name_shunt":
            None
        }

        # self.size_act = 229

        # self.helper_action = ActionSpace(self.gridobj, legal_action=self.game_rules.legal_action)
        self.helper_action = self._action_setup()
        save_to_dict(self.res, self.helper_action, "subtype",
                     lambda x: re.sub("(<class ')|('>)", "", "{}".format(x)))
        # print(self.res["subtype"])
        self.authorized_keys = self.helper_action().authorized_keys
        self.size_act = self.helper_action.size()

    def tearDown(self):
        self.authorized_keys = {}

    def compare_vect(self, pred, true):
        return np.max(np.abs(pred - true)) <= self.tolvect

    def test_call(self):
        action = self.helper_action()
        dict_injection, set_status, switch_status, set_topo_vect, switcth_topo_vect, redispatching, shunts = action(
        )

    def test_compare(self):
        action = self.helper_action()
        action2 = self.helper_action()
        assert action == action2

    def test_instanciate_action(self):
        """
        test i can instanciate an action without crashing
        :return:
        """
        action = self.helper_action()

    def test_size(self):
        action = self.helper_action()
        action.size()

    def test_proper_size(self):
        action = self.helper_action()
        assert action.size() == self.size_act

    def test_size_action_space(self):
        assert self.helper_action.size() == self.size_act

    def test_print_notcrash(self):
        """
        test the conversion to str does not crash
        :return:
        """
        action = self.helper_action({})
        a = "{}".format(action)

    def test_change_p(self):
        """

        :return:
        """
        self._skipMissingKey('injection')

        new_vect = np.random.randn(self.helper_action.n_load)
        action = self.helper_action({"injection": {"load_p": new_vect}})
        self.compare_vect(action._dict_inj["load_p"], new_vect)
        for i in range(self.helper_action.n_load):
            assert action.effect_on(load_id=i)["new_p"] == new_vect[i]

    def test_change_v(self):
        """

        :return:
        """
        self._skipMissingKey('injection')

        new_vect = np.random.randn(self.helper_action.n_gen)
        action = self.helper_action({"injection": {"prod_v": new_vect}})
        self.compare_vect(action._dict_inj["prod_v"], new_vect)
        for i in range(self.helper_action.n_gen):
            assert action.effect_on(gen_id=i)["new_v"] == new_vect[i]

    def test_change_p_q(self):
        """

        :return:
        """
        self._skipMissingKey('injection')

        new_vect = np.random.randn(self.helper_action.n_load)
        new_vect2 = np.random.randn(self.helper_action.n_load)
        action = self.helper_action(
            {"injection": {
                "load_p": new_vect,
                "load_q": new_vect2
            }})
        assert self.compare_vect(action._dict_inj["load_p"], new_vect)
        assert self.compare_vect(action._dict_inj["load_q"], new_vect2)
        for i in range(self.helper_action.n_load):
            assert action.effect_on(load_id=i)["new_p"] == new_vect[i]
            assert action.effect_on(load_id=i)["new_q"] == new_vect2[i]

    def test_update_disconnection_1(self):
        """
        Test if the disconnection is working properly
        :return:
        """
        self._skipMissingKey('set_line_status')

        for i in range(self.helper_action.n_line):
            disco = np.full(shape=self.helper_action.n_line,
                            fill_value=0,
                            dtype=np.int)
            disco[i] = 1
            action = self.helper_action({"set_line_status": disco})
            for j in range(self.helper_action.n_line):
                assert action.effect_on(line_id=j)["set_line_status"] == disco[
                    j], "problem with line {} if line {} is disconnected".format(
                        j, i)
                assert action.effect_on(
                    line_id=j)["change_line_status"] == False

    def test_update_disconnection_m1(self):
        """
        Test if the disconnection is working properly
        :return:
        """
        self._skipMissingKey('set_line_status')

        for i in range(self.helper_action.n_line):
            disco = np.full(shape=self.helper_action.n_line,
                            fill_value=0,
                            dtype=np.int)
            disco[i] = -1
            action = self.helper_action({"set_line_status": disco})
            for j in range(self.helper_action.n_line):
                assert action.effect_on(line_id=j)["set_line_status"] == disco[
                    j], "problem with line {} if line {} is disconnected".format(
                        j, i)
                assert action.effect_on(
                    line_id=j)["change_line_status"] == False

    def test_update_hazard(self):
        """
        Same test as above, but with hazard
        :return:
        """
        self._skipMissingKey('hazards')

        for i in range(self.helper_action.n_line):
            disco = np.full(shape=self.helper_action.n_line,
                            fill_value=False,
                            dtype=np.bool)
            disco[i] = True
            action = self.helper_action({"hazards": disco})
            for j in range(self.helper_action.n_line):
                expected_res = -1 if j == i else 0
                assert action.effect_on(
                    line_id=j
                )["set_line_status"] == expected_res, "problem with line {} if line {} is disconnected".format(
                    j, i)
                assert action.effect_on(
                    line_id=j)["change_line_status"] == False

    def test_update_status(self):
        self._skipMissingKey('change_line_status')

        for i in range(self.helper_action.n_line):
            disco = np.full(shape=self.helper_action.n_line,
                            fill_value=False,
                            dtype=np.bool)
            disco[i] = True
            action = self.helper_action({"change_line_status": disco})
            for j in range(self.helper_action.n_line):
                expected_res = j == i
                assert action.effect_on(line_id=j)["set_line_status"] == 0
                assert action.effect_on(
                    line_id=j)["change_line_status"] == expected_res

    def test_update_set_topo_by_dict_obj(self):
        self._skipMissingKey('set_bus')

        action = self.helper_action({"set_bus": {"loads_id": [(1, 3)]}})
        assert action.effect_on(load_id=1)["set_bus"] == 3
        assert action.effect_on(load_id=1)["change_bus"] == False
        assert action.effect_on(load_id=0)["set_bus"] == 0
        assert action.effect_on(load_id=0)["change_bus"] == False

    def test_update_set_topo_by_dict_sub(self):
        self._skipMissingKey('set_bus')

        arr = np.array([1, 1, 1, 2, 2, 2], dtype=np.int)
        action = self.helper_action(
            {"set_bus": {
                "substations_id": [(1, arr)]
            }})
        assert action.effect_on(line_id=2)["set_bus_or"] == 1
        assert action.effect_on(line_id=3)["set_bus_or"] == 1
        assert action.effect_on(line_id=4)["set_bus_or"] == 2
        assert action.effect_on(line_id=0)["set_bus_ex"] == 1
        assert action.effect_on(load_id=0)["set_bus"] == 2
        assert action.effect_on(gen_id=1)["set_bus"] == 2

        assert action.effect_on(load_id=1)["set_bus"] == 0
        assert action.effect_on(gen_id=0)["set_bus"] == 0

    def test_update_set_topo_by_dict_sub2(self):
        self._skipMissingKey('set_bus')

        arr = np.array([1, 1, 1, 2, 2, 2], dtype=np.int)
        arr3 = np.array([1, 2, 1, 2, 1, 2], dtype=np.int)
        action = self.helper_action(
            {"set_bus": {
                "substations_id": [(3, arr3), (1, arr)]
            }})
        assert action.effect_on(line_id=2)["set_bus_or"] == 1
        assert action.effect_on(line_id=3)["set_bus_or"] == 1
        assert action.effect_on(line_id=4)["set_bus_or"] == 2
        assert action.effect_on(line_id=0)["set_bus_ex"] == 1
        assert action.effect_on(load_id=0)["set_bus"] == 2
        assert action.effect_on(gen_id=1)["set_bus"] == 2

        assert action.effect_on(load_id=1)["set_bus"] == 0
        assert action.effect_on(gen_id=0)["set_bus"] == 0

    def test_update_undo_change_bus(self):
        self._skipMissingKey('change_bus')

        # Create dummy change_bus action
        action = self.helper_action({"change_bus": {"loads_id": [1]}})
        # Check it is valid
        assert action.effect_on(load_id=0)["set_bus"] == 0
        assert action.effect_on(load_id=0)["change_bus"] == False
        assert action.effect_on(load_id=1)["set_bus"] == 0
        assert action.effect_on(load_id=1)["change_bus"] == True
        # Save a copy
        action_copy = copy.deepcopy(action)

        # Update it
        action.update({"change_bus": {"loads_id": [1]}})
        # Check it's updated
        assert action.effect_on(load_id=0)["set_bus"] == 0
        assert action.effect_on(load_id=0)["change_bus"] == False
        assert action.effect_on(load_id=1)["set_bus"] == 0
        assert action.effect_on(load_id=1)["change_bus"] == False

        # Update back to original
        action.update({"change_bus": {"loads_id": [1]}})
        # Check it's updated
        assert action.effect_on(load_id=0)["set_bus"] == 0
        assert action.effect_on(load_id=0)["change_bus"] == False
        assert action.effect_on(load_id=1)["set_bus"] == 0
        assert action.effect_on(load_id=1)["change_bus"] == True

        # Check it's equal to original
        assert action == action_copy

    def test_update_change_bus_by_dict_obj(self):
        self._skipMissingKey('change_bus')

        action = self.helper_action({"change_bus": {"loads_id": [1]}})
        assert action.effect_on(load_id=1)["set_bus"] == 0
        assert action.effect_on(load_id=1)["change_bus"] == True
        assert action.effect_on(load_id=0)["set_bus"] == 0
        assert action.effect_on(load_id=0)["change_bus"] == False

    def test_update_change_bus_by_dict_sub(self):
        self._skipMissingKey('change_bus')

        arr = np.array([True, True, True, False, False, False], dtype=np.bool)
        action = self.helper_action(
            {"change_bus": {
                "substations_id": [(1, arr)]
            }})
        assert action.effect_on(line_id=2)["change_bus_or"] == True
        assert action.effect_on(line_id=3)["change_bus_or"] == True
        assert action.effect_on(line_id=4)["change_bus_or"] == False
        assert action.effect_on(line_id=0)["change_bus_ex"] == True
        assert action.effect_on(load_id=0)["change_bus"] == False
        assert action.effect_on(gen_id=1)["change_bus"] == False

        assert action.effect_on(load_id=1)["change_bus"] == False
        assert action.effect_on(gen_id=0)["change_bus"] == False

    def test_update_change_bus_by_dict_sub2(self):
        self._skipMissingKey('change_bus')

        arr = np.array([True, True, True, False, False, False], dtype=np.bool)
        arr3 = np.array([True, False, True, False, True, False], dtype=np.bool)
        action = self.helper_action(
            {"change_bus": {
                "substations_id": [(3, arr3), (1, arr)]
            }})
        assert action.effect_on(line_id=2)["change_bus_or"] == True
        assert action.effect_on(line_id=3)["change_bus_or"] == True
        assert action.effect_on(line_id=4)["change_bus_or"] == False
        assert action.effect_on(line_id=0)["change_bus_ex"] == True
        assert action.effect_on(load_id=0)["change_bus"] == False
        assert action.effect_on(gen_id=1)["change_bus"] == False

        assert action.effect_on(load_id=1)["change_bus"] == False
        assert action.effect_on(gen_id=0)["change_bus"] == False

    def test_ambiguity_topo(self):
        self._skipMissingKey('change_bus')

        action = self.helper_action({"change_bus": {
            "lines_or_id": [1]
        }})  # i switch the bus of the origin of powerline 1
        action.update({"set_bus": {
            "lines_or_id": [(1, 1)]
        }})  # i set the origin of powerline 1 to bus 1
        try:
            action()
            raise RuntimeError(
                "This should hav thrown an InvalidBusStatus error")
        except InvalidBusStatus as e:
            pass

    def test_ambiguity_line_status_when_set_and_change(self):
        self._skipMissingKey('set_line_status')
        self._skipMissingKey('change_line_status')

        arr = np.zeros(self.helper_action.n_line)
        arr[1] = -1
        action = self.helper_action(
            {"set_line_status":
             arr})  # i switch set the status of powerline 1 to "disconnected"
        action.update({"change_line_status":
                       [1]})  # i asked to change this status
        try:
            action()
            raise RuntimeError(
                "This should hav thrown an InvalidBusStatus error")
        except InvalidLineStatus as e:
            pass

    def test_ambiguity_line_reconnected_without_bus(self):
        self._skipMissingKey('set_line_status')
        arr = np.zeros(self.helper_action.n_line)
        arr[1] = 1
        action = self.helper_action(
            {"set_line_status":
             arr})  # i switch set the status of powerline 1 to "connected"
        # and i don't say on which bus to connect it

        if issubclass(self.helper_action.actionClass, PowerLineSet):
            # this is a legal action for powerlineSet
            action()
        else:
            try:
                action()
                raise RuntimeError(
                    "This should have thrown an InvalidBusStatus error for {}"
                    "".format(self.helper_action.actionClass))
            except InvalidLineStatus as e:
                pass

    def test_set_status_and_setbus_isambiguous(self):
        """

        :return:
        """
        self._skipMissingKey('set_bus')

        arr = np.array([1, 1, 1, 2, 2, 2], dtype=np.int)
        id_ = 2
        action = self.helper_action(
            {"set_bus": {
                "substations_id": [(1, arr)]
            }})
        arr2 = np.zeros(self.helper_action.n_line)
        arr2[id_] = -1
        action.update({"set_line_status": arr2})
        try:
            action()
            raise RuntimeError(
                "This should have thrown an InvalidBusStatus error")
        except InvalidLineStatus as e:
            pass

    def test_hazard_overides_setbus(self):
        """

        :return:
        """
        self._skipMissingKey('set_bus')
        self._skipMissingKey('hazards')

        arr = np.array([1, 1, 1, 2, 2, 2], dtype=np.int)
        id_ = 2
        action = self.helper_action(
            {"set_bus": {
                "substations_id": [(1, arr)]
            }})
        assert action.effect_on(
            line_id=id_)["set_bus_or"] == 1, "fail for {}".format(
                self.helper_action.actionClass)
        action.update({"hazards": [id_]})
        assert action.effect_on(
            line_id=id_)["set_bus_or"] == 0, "fail for {}".format(
                self.helper_action.actionClass)
        assert action.effect_on(
            line_id=id_)["set_line_status"] == -1, "fail for {}".format(
                self.helper_action.actionClass)
        assert action.effect_on(
            line_id=id_)["set_bus_ex"] == 0, "fail for {}".format(
                self.helper_action.actionClass)

    def test_action_str(self):
        self._skipMissingKey('set_bus')
        self._skipMissingKey('change_bus')

        arr1 = np.array([False, False, False, True, True, True], dtype=np.bool)
        arr2 = np.array([1, 1, 2, 2], dtype=np.int)
        id_1 = 1
        id_2 = 12
        action = self.helper_action({
            "change_bus": {
                "substations_id": [(id_1, arr1)]
            },
            "set_bus": {
                "substations_id": [(id_2, arr2)]
            }
        })
        res = action.__str__()
        act_str = 'This action will:\n\t - NOT change anything to the injections\n\t - NOT perform any redispatching ' \
                  'action\n\t - NOT force any line status\n\t - NOT switch any line status\n\t - Change the bus of the ' \
                  'following element:\n\t \t - switch bus of line (origin) 4 [on substation 1]\n\t \t - switch bus of ' \
                  'load 0 [on substation 1]\n\t \t - switch bus of generator 1 [on substation 1]\n\t - Set the bus of ' \
                  'the following element:\n\t \t - assign bus 1 to line (extremity) 18 [on substation 12]\n\t \t - ' \
                  'assign bus 1 to line (origin) 19 [on substation 12]\n\t \t - assign bus 2 to load 9 ' \
                  '[on substation 12]\n\t \t - assign bus 2 to line (extremity) 12 [on substation 12]'
        assert res == act_str

    def test_to_vect(self):
        self._skipMissingKey('set_bus')

        arr1 = np.array([False, False, False, True, True, True], dtype=np.bool)
        arr2 = np.array([1, 1, 2, 2], dtype=np.int)
        id_1 = 1
        id_2 = 12
        action = self.helper_action({
            "change_bus": {
                "substations_id": [(id_1, arr1)]
            },
            "set_bus": {
                "substations_id": [(id_2, arr2)]
            }
        })
        res = action.to_vect()
        tmp = np.zeros(self.size_act)

        # compute the "set_bus" vect
        id_set = np.where(
            np.array(action.attr_list_vect) == "_set_topo_vect")[0][0]
        size_before = 0
        for el in action.attr_list_vect[:id_set]:
            arr_ = action._get_array_from_attr_name(el)
            size_before += arr_.shape[0]
        tmp[size_before:(size_before + action.dim_topo)] = np.array([
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 1, 1, 2, 2, 0, 0, 0
        ])

        id_change = np.where(
            np.array(action.attr_list_vect) == "_change_bus_vect")[0][0]
        size_before = 0
        for el in action.attr_list_vect[:id_change]:
            arr_ = action._get_array_from_attr_name(el)
            size_before += arr_.shape[0]
        tmp[size_before:(size_before + action.dim_topo)] = 1.0 * np.array([
            False, False, False, False, False, False, True, True, True, False,
            False, False, False, False, False, False, False, False, False,
            False, False, False, False, False, False, False, False, False,
            False, False, False, False, False, False, False, False, False,
            False, False, False, False, False, False, False, False, False,
            False, False, False, False, False, False, False, False, False,
            False
        ])

        assert np.all(res[np.isfinite(tmp)] == tmp[np.isfinite(tmp)])
        assert np.all(np.isfinite(res) == np.isfinite(tmp))

    def test__eq__(self):
        self._skipMissingKey('set_bus')
        self._skipMissingKey('change_bus')

        arr1 = np.array([False, False, False, True, True, True], dtype=np.bool)
        arr2 = np.array([1, 1, 2, 2], dtype=np.int)
        id_1 = 1
        id_2 = 12
        action1 = self.helper_action({
            "change_bus": {
                "substations_id": [(id_1, arr1)]
            },
            "set_bus": {
                "substations_id": [(id_2, arr2)]
            }
        })
        action2 = self.helper_action({
            "change_bus": {
                "substations_id": [(id_1, arr1)]
            },
            "set_bus": {
                "substations_id": [(id_2, arr2)]
            }
        })
        action3 = self.helper_action()
        assert action1 == action2
        assert action1 != action3

    def test_from_vect(self):
        self._skipMissingKey('set_bus')
        self._skipMissingKey('change_bus')

        arr1 = np.array([False, False, False, True, True, True], dtype=np.bool)
        arr2 = np.array([1, 1, 2, 2], dtype=np.int)
        id_1 = 1
        id_2 = 12
        action1 = self.helper_action({
            "change_bus": {
                "substations_id": [(id_1, arr1)]
            },
            "set_bus": {
                "substations_id": [(id_2, arr2)]
            }
        })
        action2 = self.helper_action({})

        vect_act1 = action1.to_vect()
        action2.from_vect(vect_act1)
        # if i load an action with from_vect it's equal to the original one
        assert action1 == action2
        vect_act2 = action2.to_vect()

        # if i convert it back to a vector, it's equal to the original converted vector
        assert np.all(vect_act1[np.isfinite(vect_act2)] == vect_act2[
            np.isfinite(vect_act2)])
        assert np.all(np.isfinite(vect_act1) == np.isfinite(vect_act2))

    def test_call_change_set(self):
        self._skipMissingKey('set_bus')
        self._skipMissingKey('change_bus')
        self._skipMissingKey('set_line_status')
        self._skipMissingKey('change_line_status')
        self._skipMissingKey('injection')

        arr1 = np.array([False, False, False, True, True, True], dtype=np.bool)
        arr2 = np.array([1, 1, 2, 2], dtype=np.int)
        id_1 = 1
        id_2 = 12
        new_vect = np.random.randn(self.helper_action.n_load)
        new_vect2 = np.random.randn(self.helper_action.n_load)

        change_status_orig = np.random.randint(
            0, 2, self.helper_action.n_line).astype(np.bool)
        set_status_orig = np.random.randint(-1, 2, self.helper_action.n_line)
        set_status_orig[change_status_orig] = 0

        change_topo_vect_orig = np.random.randint(
            0, 2, self.helper_action.dim_topo).astype(np.bool)
        # powerline that are set to be reconnected, can't be moved to another bus
        change_topo_vect_orig[self.helper_action.line_or_pos_topo_vect[
            set_status_orig == 1]] = False
        change_topo_vect_orig[self.helper_action.line_ex_pos_topo_vect[
            set_status_orig == 1]] = False
        # powerline that are disconnected, can't be moved to the other bus
        change_topo_vect_orig[self.helper_action.line_or_pos_topo_vect[
            set_status_orig == -1]] = False
        change_topo_vect_orig[self.helper_action.line_ex_pos_topo_vect[
            set_status_orig == -1]] = False

        set_topo_vect_orig = np.random.randint(0, 3,
                                               self.helper_action.dim_topo)
        set_topo_vect_orig[
            change_topo_vect_orig] = 0  # don't both change and set
        # I need to make sure powerlines that are reconnected are indeed reconnected to a bus
        set_topo_vect_orig[self.helper_action.line_or_pos_topo_vect[
            set_status_orig == 1]] = 1
        set_topo_vect_orig[self.helper_action.line_ex_pos_topo_vect[
            set_status_orig == 1]] = 1
        # I need to make sure powerlines that are disconnected are not assigned to a bus
        set_topo_vect_orig[self.helper_action.line_or_pos_topo_vect[
            set_status_orig == -1]] = 0
        set_topo_vect_orig[self.helper_action.line_ex_pos_topo_vect[
            set_status_orig == -1]] = 0

        action = self.helper_action({
            "change_bus": change_topo_vect_orig,
            "set_bus": set_topo_vect_orig,
            "injection": {
                "load_p": new_vect,
                "load_q": new_vect2
            },
            "change_line_status": change_status_orig,
            "set_line_status": set_status_orig
        })
        dict_injection, set_status, change_status, set_topo_vect, switcth_topo_vect, redispatching, shunts = action(
        )
        assert "load_p" in dict_injection
        assert np.all(dict_injection["load_p"] == new_vect)
        assert "load_q" in dict_injection
        assert np.all(dict_injection["load_q"] == new_vect2)

        assert np.all(set_status == set_status_orig)
        assert np.all(change_status == change_status_orig)
        assert np.all(set_topo_vect == set_topo_vect_orig)
        assert np.all(switcth_topo_vect == change_topo_vect_orig)

    def test_get_topological_impact(self):
        self._skipMissingKey('set_bus')
        self._skipMissingKey('change_bus')
        self._skipMissingKey('set_line_status')
        self._skipMissingKey('change_line_status')

        id_1 = 1
        id_2 = 12
        id_line = 17
        id_line2 = 15

        arr1 = np.array([False, False, False, True, True, True], dtype=np.bool)
        arr2 = np.array([1, 1, 2, 2], dtype=np.int)
        arr_line1 = np.full(self.helper_action.n_line,
                            fill_value=False,
                            dtype=np.bool)
        arr_line1[id_line] = True
        arr_line2 = np.full(self.helper_action.n_line,
                            fill_value=0,
                            dtype=np.int)
        arr_line2[id_line2] = 2

        do_nothing = self.helper_action({})
        aff_lines, aff_subs = do_nothing.get_topological_impact()
        assert np.sum(aff_lines) == 0
        assert np.sum(aff_subs) == 0

        act_sub1 = self.helper_action(
            {"change_bus": {
                "substations_id": [(id_1, arr1)]
            }})
        aff_lines, aff_subs = act_sub1.get_topological_impact()
        assert np.sum(aff_lines) == 0
        assert np.sum(aff_subs) == 1
        assert aff_subs[id_1]

        act_sub1_sub12 = self.helper_action({
            "change_bus": {
                "substations_id": [(id_1, arr1)]
            },
            "set_bus": {
                "substations_id": [(id_2, arr2)]
            }
        })
        aff_lines, aff_subs = act_sub1_sub12.get_topological_impact()
        assert np.sum(aff_lines) == 0
        assert np.sum(aff_subs) == 2
        assert aff_subs[id_1]
        assert aff_subs[id_2]

        act_sub1_sub12_line1 = self.helper_action({
            "change_bus": {
                "substations_id": [(id_1, arr1)]
            },
            "set_bus": {
                "substations_id": [(id_2, arr2)]
            },
            "change_line_status":
            arr_line1
        })
        aff_lines, aff_subs = act_sub1_sub12_line1.get_topological_impact()
        assert np.sum(aff_lines) == 1
        assert aff_lines[id_line] == 1
        assert np.sum(aff_subs) == 2
        assert aff_subs[id_1]
        assert aff_subs[id_2]

        act_sub1_sub12_line1_line2 = self.helper_action({
            "change_bus": {
                "substations_id": [(id_1, arr1)]
            },
            "set_bus": {
                "substations_id": [(id_2, arr2)]
            },
            "change_line_status":
            arr_line1,
            "set_line_status":
            arr_line2
        })
        aff_lines, aff_subs = act_sub1_sub12_line1_line2.get_topological_impact(
        )
        assert np.sum(aff_lines) == 2
        assert aff_lines[id_line] == 1
        assert aff_lines[id_line2] == 1
        assert np.sum(aff_subs) == 2
        assert aff_subs[id_1]
        assert aff_subs[id_2]

    def test_to_dict(self):
        dict_ = self.helper_action.to_dict()
        assert dict_ == self.res

    def test_from_dict(self):
        res = ActionSpace.from_dict(self.res)
        assert np.all(res.name_gen == self.helper_action.name_gen)
        assert np.all(res.name_load == self.helper_action.name_load)
        assert np.all(res.name_line == self.helper_action.name_line)
        assert np.all(res.sub_info == self.helper_action.sub_info)
        assert np.all(res.load_to_subid == self.helper_action.load_to_subid)
        assert np.all(res.gen_to_subid == self.helper_action.gen_to_subid)
        assert np.all(
            res.line_or_to_subid == self.helper_action.line_or_to_subid)
        assert np.all(
            res.line_ex_to_subid == self.helper_action.line_ex_to_subid)
        assert np.all(
            res.load_to_sub_pos == self.helper_action.load_to_sub_pos)
        assert np.all(res.gen_to_sub_pos == self.helper_action.gen_to_sub_pos)
        assert np.all(
            res.line_or_to_sub_pos == self.helper_action.line_or_to_sub_pos)
        assert np.all(
            res.line_ex_to_sub_pos == self.helper_action.line_ex_to_sub_pos)
        assert np.all(
            res.load_pos_topo_vect == self.helper_action.load_pos_topo_vect)
        assert np.all(
            res.gen_pos_topo_vect == self.helper_action.gen_pos_topo_vect)
        assert np.all(res.line_or_pos_topo_vect ==
                      self.helper_action.line_or_pos_topo_vect)
        assert np.all(res.line_ex_pos_topo_vect ==
                      self.helper_action.line_ex_pos_topo_vect)
        # pdb.set_trace()
        assert np.all(res.actionClass == self.helper_action.actionClass)

    def test_json_serializable(self):
        dict_ = self.helper_action.to_dict()
        res = json.dumps(obj=dict_, indent=4, sort_keys=True)

    def test_json_loadable(self):
        dict_ = self.helper_action.to_dict()
        tmp = json.dumps(obj=dict_, indent=4, sort_keys=True)
        res = ActionSpace.from_dict(json.loads(tmp))

        assert np.all(res.name_gen == self.helper_action.name_gen)
        assert np.all(res.name_load == self.helper_action.name_load)
        assert np.all(res.name_line == self.helper_action.name_line)
        assert np.all(res.sub_info == self.helper_action.sub_info)
        assert np.all(res.load_to_subid == self.helper_action.load_to_subid)
        assert np.all(res.gen_to_subid == self.helper_action.gen_to_subid)
        assert np.all(
            res.line_or_to_subid == self.helper_action.line_or_to_subid)
        assert np.all(
            res.line_ex_to_subid == self.helper_action.line_ex_to_subid)
        assert np.all(
            res.load_to_sub_pos == self.helper_action.load_to_sub_pos)
        assert np.all(res.gen_to_sub_pos == self.helper_action.gen_to_sub_pos)
        assert np.all(
            res.line_or_to_sub_pos == self.helper_action.line_or_to_sub_pos)
        assert np.all(
            res.line_ex_to_sub_pos == self.helper_action.line_ex_to_sub_pos)
        assert np.all(
            res.load_pos_topo_vect == self.helper_action.load_pos_topo_vect)
        assert np.all(
            res.gen_pos_topo_vect == self.helper_action.gen_pos_topo_vect)
        assert np.all(res.line_or_pos_topo_vect ==
                      self.helper_action.line_or_pos_topo_vect)
        assert np.all(res.line_ex_pos_topo_vect ==
                      self.helper_action.line_ex_pos_topo_vect)
        assert np.all(res.actionClass == self.helper_action.actionClass)

    def test_as_dict(self):
        act = self.helper_action({})
        dict_ = act.as_dict()
        assert dict_ == {}

    def test_to_from_vect_action(self):
        act = self.helper_action({})
        vect_ = act.to_vect()
        act2 = self.helper_action.from_vect(vect_)
        assert act == act2

    def test_sum_shape_equal_size(self):
        act = self.helper_action({})
        assert act.size() == np.sum(act.shape())

    def test_shape_correct(self):
        act = self.helper_action({})
        assert act.shape().shape == act.dtype().shape

    def test_redispatching(self):
        self._skipMissingKey('redispatch')

        act = self.helper_action({"redispatch": [1, 10]})
        act = self.helper_action({"redispatch": [(1, 10), (2, 100)]})
        act = self.helper_action(
            {"redispatch": np.array([10, 20, 30, 40, 50])})

    def test_possibility_reconnect_powerlines(self):
        self._skipMissingKey('set_line_status')
        self._skipMissingKey('set_bus')
        self.helper_action.legal_action = DefaultRules()

        act = self.helper_action.reconnect_powerline(line_id=1,
                                                     bus_or=1,
                                                     bus_ex=1)
        line_impact, sub_impact = act.get_topological_impact()
        assert np.sum(line_impact) == 1
        assert np.sum(sub_impact) == 0

        act = self.helper_action.reconnect_powerline(line_id=1,
                                                     bus_or=1,
                                                     bus_ex=1)
        act.update({"set_bus": {"generators_id": [(1, 2)]}})
        line_impact, sub_impact = act.get_topological_impact()
        assert np.sum(line_impact) == 1
        assert np.all(sub_impact == [False, True] + [False for _ in range(12)])

        act = self.helper_action.reconnect_powerline(line_id=1,
                                                     bus_or=1,
                                                     bus_ex=1)
        act.update({"set_bus": {"generators_id": [(0, 2)]}})
        line_impact, sub_impact = act.get_topological_impact()
        assert np.sum(line_impact) == 1
        assert np.all(sub_impact == [True] + [False for _ in range(13)])

        # there were a bug that occurred when updating an action, some vectors were not reset
        act = self.helper_action.reconnect_powerline(line_id=1,
                                                     bus_or=1,
                                                     bus_ex=1)
        line_impact, sub_impact = act.get_topological_impact()
        assert np.sum(line_impact) == 1
        assert np.sum(sub_impact) == 0
        act.update({"set_bus": {"generators_id": [(1, 2)]}})
        line_impact, sub_impact = act.get_topological_impact()
        assert np.sum(line_impact) == 1
        assert np.all(sub_impact == [False, True] + [False for _ in range(12)])
Esempio n. 10
0
    def __init__(self,
                 parameters,
                 thermal_limit_a=None,
                 epsilon_poly=1e-2,
                 tol_poly=1e-6,
                 other_rewards={}):
        GridObjects.__init__(self)

        # specific to power system
        if not isinstance(parameters, Parameters):
            raise Grid2OpException(
                "Parameter \"parameters\" used to build the Environment should derived form the "
                "grid2op.Parameters class, type provided is \"{}\"".format(
                    type(parameters)))
        self.parameters = parameters

        # some timers
        self._time_apply_act = 0
        self._time_powerflow = 0
        self._time_extract_obs = 0
        self._time_opponent = 0

        # data relative to interpolation
        self._epsilon_poly = epsilon_poly
        self._tol_poly = tol_poly

        # define logger
        self.logger = None

        # and calendar data
        self.time_stamp = None
        self.nb_time_step = 0

        # observation
        self.current_obs = None

        # type of power flow to play
        # if True, then it will not disconnect lines above their thermal limits
        self.no_overflow_disconnection = self.parameters.NO_OVERFLOW_DISCONNECTION
        self.timestep_overflow = None
        self.nb_timestep_overflow_allowed = None

        # store actions "cooldown"
        self.times_before_line_status_actionable = None
        self.max_timestep_line_status_deactivated = self.parameters.NB_TIMESTEP_LINE_STATUS_REMODIF

        self.times_before_topology_actionable = None
        self.max_timestep_topology_deactivated = self.parameters.NB_TIMESTEP_TOPOLOGY_REMODIF

        # for maintenance operation
        self.time_next_maintenance = None
        self.duration_next_maintenance = None

        # hazard (not used outside of this class, information is given in `time_remaining_before_line_reconnection`
        self._hazard_duration = None

        # hard overflow part
        self.hard_overflow_threshold = self.parameters.HARD_OVERFLOW_THRESHOLD
        self.time_remaining_before_line_reconnection = None
        self.env_dc = self.parameters.ENV_DC

        # redispatching data
        self.target_dispatch = None
        self.actual_dispatch = None
        self.gen_uptime = None
        self.gen_downtime = None
        self.gen_activeprod_t = None

        self._thermal_limit_a = thermal_limit_a

        # maintenance / hazards
        self.time_next_maintenance = None
        self.duration_next_maintenance = None
        self.time_remaining_before_reconnection = None

        # store environment modifications
        self._injection = None
        self._maintenance = None
        self._hazards = None
        self.env_modification = None

        # to use the data
        self.done = False
        self.current_reward = None
        self.helper_action_env = None
        self.chronics_handler = None
        self.game_rules = None
        self.helper_action_player = None

        self.rewardClass = None
        self.actionClass = None
        self.observationClass = None
        self.legalActClass = None
        self.helper_observation = None
        self.names_chronics_to_backend = None
        self.reward_helper = None
        self.reward_range = None, None

        # other rewards
        self.other_rewards = {}
        for k, v in other_rewards.items():
            if not issubclass(v, BaseReward):
                raise Grid2OpException(
                    "All keys of \"rewards\" key word argument should be classes that inherit from "
                    "\"grid2op.BaseReward\"")
            self.other_rewards[k] = RewardHelper(v)

        # opponent
        self.opponent_action_class = DontAct  # class of the action of the opponent
        self.opponent_class = BaseOpponent  # class of the opponent
        self.opponent_init_budget = 0

        ## below initialized by _create_env, above: need to be called
        self.opponent_action_space = None  # ActionSpace(gridobj=)
        self.compute_opp_budg = None  # UnlimitedBudget(self.opponent_act_space)
        self.opponent = None  # OpponentSpace()
        self.oppSpace = None

        # voltage
        self.voltage_controler = None

        # backend
        self.init_grid_path = None

        # specific to Basic Env, do not change
        self.backend = None
        self.__is_init = False
Esempio n. 11
0
    def __init__(self,
                 observation_space,
                 substation_layout=None,
                 radius_sub=20.,
                 load_prod_dist=70.,
                 bus_radius=6.):

        if substation_layout is None:
            if observation_space.grid_layout is None:
                # if no layout is provided, and observation_space has no layout, then it fails
                raise PlotError(
                    "Impossible to use plotting abilities without specifying a layout (coordinates) "
                    "of the substations.")

            # if no layout is provided, use the one in the observation_space
            substation_layout = []
            for el in observation_space.name_sub:
                substation_layout.append(observation_space.grid_layout[el])

        if len(substation_layout) != observation_space.n_sub:
            raise PlotError(
                "You provided a layout with {} elements while there are {} substations on the powergrid. "
                "Your layout is invalid".format(len(substation_layout),
                                                observation_space.n_sub))
        GridObjects.__init__(self)
        self.init_grid(observation_space)

        self.observation_space = observation_space
        self._layout = {}
        self._layout["substations"] = self._get_sub_layout(substation_layout)

        self.radius_sub = radius_sub
        self.load_prod_dist = load_prod_dist  # distance between load and generator to the center of the substation
        self.bus_radius = bus_radius

        self.subs_elements = [None for _ in self.observation_space.sub_info]

        # get the element in each substation
        for sub_id in range(self.observation_space.sub_info.shape[0]):
            this_sub = {}
            objs = self.observation_space.get_obj_connect_to(
                substation_id=sub_id)

            for c_id in objs["loads_id"]:
                c_nm = self._get_load_name(sub_id, c_id)
                this_load = {}
                this_load["type"] = "load"
                this_load["sub_pos"] = self.observation_space.load_to_sub_pos[
                    c_id]
                this_sub[c_nm] = this_load

            for g_id in objs["generators_id"]:
                g_nm = self._get_gen_name(sub_id, g_id)
                this_gen = {}
                this_gen["type"] = "gen"
                this_gen["sub_pos"] = self.observation_space.gen_to_sub_pos[
                    g_id]
                this_sub[g_nm] = this_gen

            for lor_id in objs["lines_or_id"]:
                ext_id = self.observation_space.line_ex_to_subid[lor_id]
                l_nm = self._get_line_name(sub_id, ext_id, lor_id)
                this_line = {}
                this_line["type"] = "line"
                this_line[
                    "sub_pos"] = self.observation_space.line_or_to_sub_pos[
                        lor_id]
                this_sub[l_nm] = this_line

            for lex_id in objs["lines_ex_id"]:
                or_id = self.observation_space.line_or_to_subid[lex_id]
                l_nm = self._get_line_name(or_id, sub_id, lex_id)
                this_line = {}
                this_line["type"] = "line"
                this_line[
                    "sub_pos"] = self.observation_space.line_ex_to_sub_pos[
                        lex_id]
                this_sub[l_nm] = this_line
            self.subs_elements[sub_id] = this_sub
        self._compute_layout()
Esempio n. 12
0
    def __init__(self,
                 obs_env=None,
                 action_helper=None,
                 seed=None):
        GridObjects.__init__(self)

        self.action_helper = action_helper

        # time stamp information
        self.year = 1970
        self.month = 0
        self.day = 0
        self.hour_of_day = 0
        self.minute_of_hour = 0
        self.day_of_week = 0

        # for non deterministic observation that would not use default np.random module
        self.seed = None

        # handles the forecasts here
        self._forecasted_grid_act = {}
        self._forecasted_inj = []
        self._obs_env = obs_env

        self.timestep_overflow = np.zeros(shape=(self.n_line,), dtype=dt_int)

        # 0. (line is disconnected) / 1. (line is connected)
        self.line_status = np.ones(shape=self.n_line, dtype=dt_bool)

        # topological vector
        self.topo_vect = np.zeros(shape=self.dim_topo, dtype=dt_int)

        # generators information
        self.prod_p = np.full(shape=self.n_gen, dtype=dt_float, fill_value=np.NaN)
        self.prod_q = 1.0 * self.prod_p
        self.prod_v = 1.0 * self.prod_p
        # loads information
        self.load_p = np.full(shape=self.n_load, dtype=dt_float, fill_value=np.NaN)
        self.load_q = 1.0 * self.load_p
        self.load_v = 1.0 * self.load_p
        # lines origin information
        self.p_or = np.full(shape=self.n_line, dtype=dt_float, fill_value=np.NaN)
        self.q_or = 1.0 * self.p_or
        self.v_or = 1.0 * self.p_or
        self.a_or = 1.0 * self.p_or
        # lines extremity information
        self.p_ex = 1.0 * self.p_or
        self.q_ex = 1.0 * self.p_or
        self.v_ex = 1.0 * self.p_or
        self.a_ex = 1.0 * self.p_or
        # lines relative flows
        self.rho = 1.0 * self.p_or

        # cool down and reconnection time after hard overflow, soft overflow or cascading failure
        self.time_before_cooldown_line = np.full(shape=self.n_line, dtype=dt_int, fill_value=-1)
        self.time_before_cooldown_sub = np.full(shape=self.n_sub, dtype=dt_int, fill_value=-1)
        self.time_next_maintenance = 1 * self.time_before_cooldown_line
        self.duration_next_maintenance = 1 * self.time_before_cooldown_line

        # calendar data
        self.year = dt_int(1970)
        self.month = dt_int(0)
        self.day = dt_int(0)
        self.hour_of_day = dt_int(0)
        self.minute_of_hour = dt_int(0)
        self.day_of_week = dt_int(0)

        # redispatching
        self.target_dispatch = 1.0 * self.prod_p
        self.actual_dispatch = 1.0 * self.prod_p

        # to save some computation time
        self._connectivity_matrix_ = None
        self._bus_connectivity_matrix_ = None
        self._dictionnarized = None

        # for shunt (these are not stored!)
        if self.shunts_data_available:
            self._shunt_p = np.full(shape=self.n_shunt, dtype=dt_float, fill_value=np.NaN)
            self._shunt_q = 1.0 * self._shunt_p
            self._shunt_v = 1.0 * self._shunt_p
            self._shunt_bus = np.full(shape=self.n_shunt, dtype=dt_int, fill_value=1)
Esempio n. 13
0
    def __init__(self, gridobj, obs_env=None, action_helper=None, seed=None):
        GridObjects.__init__(self)
        self.init_grid(gridobj)

        self.action_helper = action_helper

        # time stamp information
        self.year = 1970
        self.month = 0
        self.day = 0
        self.hour_of_day = 0
        self.minute_of_hour = 0
        self.day_of_week = 0

        # for non deterministic observation that would not use default np.random module
        self.seed = None

        # handles the forecasts here
        self._forecasted_grid = []
        self._forecasted_inj = []

        self._obs_env = obs_env

        self.timestep_overflow = None

        # 0. (line is disconnected) / 1. (line is connected)
        self.line_status = None

        # topological vector
        self.topo_vect = None

        # generators information
        self.prod_p = None
        self.prod_q = None
        self.prod_v = None
        # loads information
        self.load_p = None
        self.load_q = None
        self.load_v = None
        # lines origin information
        self.p_or = None
        self.q_or = None
        self.v_or = None
        self.a_or = None
        # lines extremity information
        self.p_ex = None
        self.q_ex = None
        self.v_ex = None
        self.a_ex = None
        # lines relative flows
        self.rho = None

        # cool down and reconnection time after hard overflow, soft overflow or cascading failure
        self.time_before_cooldown_line = None
        self.time_before_cooldown_sub = None
        self.time_before_line_reconnectable = None
        self.time_next_maintenance = None
        self.duration_next_maintenance = None

        # matrices
        self.connectivity_matrix_ = None
        self.bus_connectivity_matrix_ = None
        self.vectorized = None

        # redispatching
        self.target_dispatch = None
        self.actual_dispatch = None

        # value to assess if two observations are equal
        self._tol_equal = 5e-1

        self.attr_list_vect = None
        self.reset()
Esempio n. 14
0
    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