コード例 #1
0
class TestRedispatch(HelperTests):
    def setUp(self):
        # powergrid
        self.backend = PandaPowerBackend()
        self.path_matpower = PATH_DATA_TEST_PP
        self.case_file = "test_case14.json"
        # chronics
        self.path_chron = os.path.join(PATH_CHRONICS, "chronics")
        self.chronics_handler = ChronicsHandler(
            chronicsClass=GridStateFromFile, path=self.path_chron)
        self.id_chron_to_back_load = np.array(
            [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9])

        # force the verbose backend
        self.backend.detailed_infos_for_cascading_failures = True
        self.names_chronics_to_backend = {
            "loads": {
                "2_C-10.61": 'load_1_0',
                "3_C151.15": 'load_2_1',
                "14_C63.6": 'load_13_2',
                "4_C-9.47": 'load_3_3',
                "5_C201.84": 'load_4_4',
                "6_C-6.27": 'load_5_5',
                "9_C130.49": 'load_8_6',
                "10_C228.66": 'load_9_7',
                "11_C-138.89": 'load_10_8',
                "12_C-27.88": 'load_11_9',
                "13_C-13.33": 'load_12_10'
            },
            "lines": {
                '1_2_1': '0_1_0',
                '1_5_2': '0_4_1',
                '9_10_16': '8_9_2',
                '9_14_17': '8_13_3',
                '10_11_18': '9_10_4',
                '12_13_19': '11_12_5',
                '13_14_20': '12_13_6',
                '2_3_3': '1_2_7',
                '2_4_4': '1_3_8',
                '2_5_5': '1_4_9',
                '3_4_6': '2_3_10',
                '4_5_7': '3_4_11',
                '6_11_11': '5_10_12',
                '6_12_12': '5_11_13',
                '6_13_13': '5_12_14',
                '4_7_8': '3_6_15',
                '4_9_9': '3_8_16',
                '5_6_10': '4_5_17',
                '7_8_14': '6_7_18',
                '7_9_15': '6_8_19'
            },
            "prods": {
                "1_G137.1": 'gen_0_4',
                "3_G36.31": "gen_2_1",
                "6_G63.29": "gen_5_2",
                "2_G-56.47": "gen_1_0",
                "8_G40.43": "gen_7_3"
            },
        }

        # _parameters for the environment
        self.env_params = Parameters()
        self.env = Environment(
            init_grid_path=os.path.join(self.path_matpower, self.case_file),
            backend=self.backend,
            chronics_handler=self.chronics_handler,
            parameters=self.env_params,
            names_chronics_to_backend=self.names_chronics_to_backend,
            actionClass=BaseAction)
        self.array_double_dispatch = np.array([0., 10., 20., 0., -30.])

    def tearDown(self):
        self.env.close()

    def test_negative_dispatch(self):
        act = self.env.action_space({"redispatch": [(1, -10)]})
        obs, reward, done, info = self.env.step(act)
        assert np.all(obs.prod_p >= self.env.gen_pmin)
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.abs(np.sum(obs.actual_dispatch)) <= self.tol_one

    def test_no_impact_env(self):
        # perform a valid redispatching action
        obs_init = self.env.reset()  # reset the environment
        act = self.env.action_space()
        for i in range(
                1
        ):  # number cherry picked to introduce explain the behaviour in the cells bellow
            obsinit, rewardinit, doneinit, infoinit = self.env.step(
                self.env.action_space())
        ref_data = copy.deepcopy(obsinit.prod_p)
        act = self.env.action_space({"redispatch": [(0, -10)]})
        # act = env.action_space({"redispatch": [(4,0)]})
        obs, reward, done, info = self.env.step(act)
        assert self.compare_vect(obsinit.prod_p, ref_data)

        target_val = obs.prod_p + self.env.actual_dispatch
        assert self.compare_vect(
            obs.prod_p[:-1],
            target_val[:-1])  # I remove last component which is the slack bus
        assert np.all(obs.prod_p >= self.env.gen_pmin)
        assert np.all(target_val <= self.env.gen_pmax)
        assert np.all(obs.prod_p - obsinit.prod_p <= self.env.gen_max_ramp_up)
        assert np.all(
            obsinit.prod_p - obs.prod_p <= self.env.gen_max_ramp_down)

    def test_basic_redispatch_act(self):
        # test of the implementation of a simple case redispatching on one generator, bellow ramp min and ramp max
        act = self.env.action_space({"redispatch": [2, 5]})
        obs, reward, done, info = self.env.step(act)
        assert np.abs(np.sum(self.env.actual_dispatch)) <= self.tol_one
        th_dispatch = np.array([0., -1.44301856, 5., 0., -3.55698144])
        assert self.compare_vect(self.env.actual_dispatch, th_dispatch)
        target_val = self.chronics_handler.real_data.prod_p[
            1, :] + self.env.actual_dispatch
        assert self.compare_vect(
            obs.prod_p[:-1],
            target_val[:-1])  # I remove last component which is the slack bus
        assert np.all(obs.prod_p >= self.env.gen_pmin)
        assert np.all(target_val <= self.env.gen_pmax)

        # check that the redispatching is apply in the right direction
        indx_ok = self.env.target_dispatch != 0.
        assert np.all(
            np.sign(self.env.actual_dispatch[indx_ok]) == np.sign(
                self.env.target_dispatch[indx_ok]))

    def test_redispatch_act_above_pmax(self):
        # in this test, the asked redispatching for generator 2 would make it above pmax, so the environment
        # need to "cut" it automatically, without invalidating the action
        act = self.env.action_space({"redispatch": [2, 60]})
        obs, reward, done, info = self.env.step(act)
        assert np.abs(np.sum(self.env.actual_dispatch)) <= self.tol_one
        th_dispatch = np.array(
            [0., -10.57042905, 50.89066718, 0., -40.32023813])
        assert self.compare_vect(self.env.actual_dispatch, th_dispatch)
        target_val = self.chronics_handler.real_data.prod_p[
            1, :] + self.env.actual_dispatch
        assert self.compare_vect(
            obs.prod_p[:-1],
            target_val[:-1])  # I remove last component which is the slack bus
        assert np.all(obs.prod_p >= self.env.gen_pmin)
        assert np.all(target_val <= self.env.gen_pmax)

    def test_two_redispatch_act(self):
        act = self.env.action_space({"redispatch": [2, 20]})
        obs, reward, done, info = self.env.step(act)
        act = self.env.action_space({"redispatch": [1, 10]})
        obs, reward, done, info = self.env.step(act)
        th_dispatch = np.array([0., 10, 20., 0., 0.])
        assert self.compare_vect(self.env.target_dispatch, th_dispatch)
        # check that the redispatching is apply in the right direction
        indx_ok = self.env.target_dispatch != 0.
        assert np.all(
            np.sign(self.env.actual_dispatch[indx_ok]) == np.sign(
                self.env.target_dispatch[indx_ok]))
        th_dispatch = np.array([0., 10., 20., 0., -30.])
        assert self.compare_vect(self.env.actual_dispatch, th_dispatch)

        target_val = self.chronics_handler.real_data.prod_p[
            2, :] + self.env.actual_dispatch
        assert self.compare_vect(
            obs.prod_p[:-1],
            target_val[:-1])  # I remove last component which is the slack bus
        assert np.abs(np.sum(self.env.actual_dispatch)) <= self.tol_one
        assert np.all(target_val <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

    def test_redispacth_two_gen(self):
        act = self.env.action_space({"redispatch": [(2, 20), (1, 10)]})
        obs, reward, done, info = self.env.step(act)
        th_dispatch = np.array([0., 10, 20., 0., 0.])
        assert self.compare_vect(self.env.target_dispatch, th_dispatch)
        assert self.compare_vect(self.env.actual_dispatch,
                                 self.array_double_dispatch)

        # check that the redispatching is apply in the right direction
        indx_ok = self.env.target_dispatch != 0.
        assert np.all(
            np.sign(self.env.actual_dispatch[indx_ok]) == np.sign(
                self.env.target_dispatch[indx_ok]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

    def test_redispacth_all_gen(self):
        # this should be exactly the same as the previous one
        act = self.env.action_space(
            {"redispatch": [(2, 20.), (1, 10.), (4, -30.)]})
        obs, reward, done, info = self.env.step(act)

        th_dispatch = np.array([0., 10, 20., 0., -30.])
        assert self.compare_vect(self.env.target_dispatch, th_dispatch)
        assert self.compare_vect(self.env.actual_dispatch,
                                 self.array_double_dispatch)

        # check that the redispatching is apply in the right direction
        indx_ok = self.env.target_dispatch != 0.
        assert np.all(
            np.sign(self.env.actual_dispatch[indx_ok]) == np.sign(
                self.env.target_dispatch[indx_ok]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

    def test_count_turned_on(self):
        act = self.env.action_space()
        obs, reward, done, info = self.env.step(act)
        # pdb.set_trace()
        assert np.all(self.env.gen_uptime == np.array([0, 1, 1, 0, 1]))
        assert np.all(self.env.gen_downtime == np.array([1, 0, 0, 1, 0]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

        obs, reward, done, info = self.env.step(act)
        assert np.all(self.env.gen_uptime == np.array([0, 2, 2, 0, 2]))
        assert np.all(self.env.gen_downtime == np.array([2, 0, 0, 2, 0]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

        for i in range(63):
            obs, reward, done, info = self.env.step(act)
            assert np.all(obs.prod_p <= self.env.gen_pmax)
            assert np.all(obs.prod_p >= self.env.gen_pmin)

        obs, reward, done, info = self.env.step(act)
        assert np.all(self.env.gen_uptime == np.array([0, 66, 66, 1, 66]))
        assert np.all(self.env.gen_downtime == np.array([66, 0, 0, 0, 0]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

        obs, reward, done, info = self.env.step(act)
        assert np.all(self.env.gen_uptime == np.array([1, 67, 67, 2, 67]))
        assert np.all(self.env.gen_downtime == np.array([0, 0, 0, 0, 0]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

    def test_redispacth_twice_same(self):
        # this should be exactly the same as the previous one
        act = self.env.action_space({"redispatch": [(2, 5.)]})
        obs, reward, done, info = self.env.step(act)
        assert np.all(obs.target_dispatch == np.array([0., 0., 5., 0., 0.]))
        assert np.abs(np.sum(obs.actual_dispatch)) <= self.tol_one
        assert self.compare_vect(
            obs.actual_dispatch,
            np.array([0., -1.44301856, 5., 0., -3.55698144]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

        act = self.env.action_space({"redispatch": [(2, 5.)]})
        obs, reward, done, info = self.env.step(act)
        assert np.all(obs.target_dispatch == np.array([0., 0., 10., 0., 0.]))
        assert np.abs(np.sum(obs.actual_dispatch)) <= self.tol_one
        assert self.compare_vect(
            obs.actual_dispatch,
            np.array([0., -2.81339987, 10., 0., -7.18660013]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

    def test_redispacth_secondabovepmax(self):
        act = self.env.action_space({"redispatch": [(2, 20.)]})
        obs, reward, done, info = self.env.step(act)
        assert np.all(obs.target_dispatch == np.array([0., 0., 20., 0., 0.]))
        assert np.abs(np.sum(obs.actual_dispatch)) <= self.tol_one
        assert self.compare_vect(
            obs.actual_dispatch,
            np.array([0., -5.36765536, 20., 0., -14.63234464]))
        assert np.all(obs.prod_p <= self.env.gen_pmax)
        assert np.all(obs.prod_p >= self.env.gen_pmin)

        act = self.env.action_space({"redispatch": [(2, 40.)]})
        obs, reward, done, info = self.env.step(act)
        assert np.all(obs.target_dispatch == np.array([0., 0., 60., 0., 0.]))
        assert self.compare_vect(
            obs.actual_dispatch,
            np.array([0., -10.3814061, 50.39070301, 0., -40.00929691]))
        assert np.all(obs.prod_p[:-1] <= self.env.gen_pmax[:-1])
        assert np.all(obs.prod_p[:-1] >= self.env.gen_pmin[:-1])

    def test_redispacth_non_dispatchable_generator(self):
        """ Dispatch a non redispatchable generator is ambiguous """
        act = self.env.action_space()
        obs, reward, done, info = self.env.step(act)

        # Check that generator 0 isn't redispatchable
        assert self.env.gen_redispatchable[0] == False
        # Check that generator 0 is off
        assert self.env.gen_downtime[0] >= 1

        # Try to redispatch
        redispatch_act = self.env.action_space({"redispatch": [(0, 5.)]})
        obs, reward, done, info = self.env.step(redispatch_act)

        assert info['is_ambiguous']
コード例 #2
0
ファイル: test_Environment.py プロジェクト: colin-fox/Grid2Op
class TestLoadingBackendPandaPower(unittest.TestCase):
    def get_backend(self):
        return PandaPowerBackend()

    def setUp(self):
        # powergrid
        self.backend = self.get_backend()
        self.path_matpower = PATH_DATA_TEST_PP
        self.case_file = "test_case14.json"

        # chronics
        self.path_chron = os.path.join(PATH_CHRONICS, "chronics")
        self.chronics_handler = ChronicsHandler(
            chronicsClass=GridStateFromFile, path=self.path_chron)

        self.tolvect = dt_float(1e-2)
        self.tol_one = dt_float(1e-5)
        self.id_chron_to_back_load = np.array(
            [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9])

        # force the verbose backend
        self.backend.detailed_infos_for_cascading_failures = True

        self.names_chronics_to_backend = {
            "loads": {
                "2_C-10.61": 'load_1_0',
                "3_C151.15": 'load_2_1',
                "14_C63.6": 'load_13_2',
                "4_C-9.47": 'load_3_3',
                "5_C201.84": 'load_4_4',
                "6_C-6.27": 'load_5_5',
                "9_C130.49": 'load_8_6',
                "10_C228.66": 'load_9_7',
                "11_C-138.89": 'load_10_8',
                "12_C-27.88": 'load_11_9',
                "13_C-13.33": 'load_12_10'
            },
            "lines": {
                '1_2_1': '0_1_0',
                '1_5_2': '0_4_1',
                '9_10_16': '8_9_2',
                '9_14_17': '8_13_3',
                '10_11_18': '9_10_4',
                '12_13_19': '11_12_5',
                '13_14_20': '12_13_6',
                '2_3_3': '1_2_7',
                '2_4_4': '1_3_8',
                '2_5_5': '1_4_9',
                '3_4_6': '2_3_10',
                '4_5_7': '3_4_11',
                '6_11_11': '5_10_12',
                '6_12_12': '5_11_13',
                '6_13_13': '5_12_14',
                '4_7_8': '3_6_15',
                '4_9_9': '3_8_16',
                '5_6_10': '4_5_17',
                '7_8_14': '6_7_18',
                '7_9_15': '6_8_19'
            },
            "prods": {
                "1_G137.1": 'gen_0_4',
                "3_G36.31": "gen_2_1",
                "6_G63.29": "gen_5_2",
                "2_G-56.47": "gen_1_0",
                "8_G40.43": "gen_7_3"
            },
        }

        # _parameters for the environment
        self.env_params = Parameters()
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore")
            self.env = Environment(
                init_grid_path=os.path.join(self.path_matpower,
                                            self.case_file),
                backend=self.backend,
                chronics_handler=self.chronics_handler,
                parameters=self.env_params,
                names_chronics_to_backend=self.names_chronics_to_backend,
                name="test_env_env1")

    def tearDown(self):
        self.env.close()

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

    def test_copy_env(self):
        # first copying method

        with warnings.catch_warnings():
            warnings.filterwarnings("ignore")
            cpy = Environment(**self.env.get_kwargs())
        obs1 = cpy.reset()
        obs2 = self.env.reset()
        assert obs1 == obs2
        # test both coppy and not copy behave the same if we do the same
        obs1, reward1, done1, info1 = cpy.step(self.env.action_space())
        obs2, reward2, done2, info2 = self.env.step(self.env.action_space())
        assert abs(reward1 - reward2) <= self.tol_one
        assert done1 == done2
        assert info1.keys() == info2.keys()
        for kk in info1.keys():
            assert np.all(info1[kk] == info2[kk])
        assert obs1 == obs2
        # test they are different if we do different stuff
        obs2, reward2, done2, info2 = self.env.step(
            self.env.action_space({"set_line_status": [(0, -1)]}))
        obs1, reward1, done1, info1 = cpy.step(self.env.action_space())
        assert obs1.line_status[0]
        assert not obs2.line_status[0]
        assert obs1 != obs2

        # second copying method
        self.env.reset()
        env2 = self.env.copy()
        # test both coppy and not copy behave the same if we do the same
        obs1, reward1, done1, info1 = env2.step(self.env.action_space())
        obs2, reward2, done2, info2 = self.env.step(self.env.action_space())
        assert abs(reward1 - reward2) <= self.tol_one
        assert done1 == done2
        assert info1.keys() == info2.keys()
        for kk in info1.keys():
            assert np.all(info1[kk] == info2[kk])
        assert obs1 == obs2

        # test they are different if we do different stuff
        obs2, reward2, done2, info2 = self.env.step(
            self.env.action_space({"set_line_status": [(0, -1)]}))
        obs1, reward1, done1, info1 = env2.step(self.env.action_space())
        assert obs1.line_status[0]
        assert not obs2.line_status[0]
        assert obs1 != obs2

        # new "same obs" again after reset
        obs1 = self.env.reset()
        obs2 = env2.reset()
        assert obs1 == obs2

    def test_assign_action_space(self):
        """test that i cannot change the action_space"""
        with self.assertRaises(EnvError):
            self.env.action_space = self.env._action_space

    def test_assign_obs_space(self):
        """test that i cannot change the observation_space"""
        with self.assertRaises(EnvError):
            self.env.observation_space = self.env._observation_space

    def test_step_doesnt_change_action(self):
        act = self.env.action_space()
        act_init = copy.deepcopy(act)
        res = self.env.step(act)
        assert act == act_init

    def test_load_env(self):
        """
        Just executes the SetUp and tearDown functions.
        :return:
        """
        if DEBUG:
            if PROFILE_CODE:
                cp = cProfile.Profile()
                cp.enable()
            import pandapower as pp
            nb_powerflow = 5000
            beg_ = time.time()
            for i in range(nb_powerflow):
                pp.runpp(self.backend._grid)
            end_ = time.time()
            print("Time to compute {} powerflows: {:.2f}".format(
                nb_powerflow, end_ - beg_))
            if PROFILE_CODE:
                cp.disable()
                cp.print_stats(sort="tottime")
        pass

    def test_proper_injection_at_first(self):
        injs_act, *_ = self.env.backend.loads_info()
        # below: row as found in the file
        vect = np.array(
            [18.8, 86.5, 44.5, 7.1, 10.4, 27.6, 8.1, 3.2, 5.6, 11.9, 13.6])
        # now it's in the "backend" order (ie properly reordered)
        vect = vect[self.id_chron_to_back_load]
        # and now i make sure everything is working as intentended
        assert self.compare_vect(injs_act, vect)

    def test_proper_voltage_modification(self):
        do_nothing = self.env.action_space({})
        obs, reward, done, info = self.env.step(
            do_nothing)  # should load the first time stamp
        vect = np.array([143.9, 139.1, 0.2, 13.3, 146.])
        assert self.compare_vect(
            obs.prod_v, vect
        ), "Production voltages setpoint have not changed at first time step"
        obs, reward, done, info = self.env.step(
            do_nothing)  # should load the first time stamp
        vect = np.array([145.3, 140.4, 0.2, 13.5, 147.4])
        assert self.compare_vect(
            obs.prod_v, vect
        ), "Production voltages setpoint have not changed at second time step"

    def test_number_of_timesteps(self):
        for i in range(287):
            do_nothing = self.env.action_space({})
            obs, reward, done, info = self.env.step(
                do_nothing)  # should load the first time stamp
        injs_act, *_ = self.env.backend.loads_info()
        vect = np.array(
            [19.0, 87.9, 44.4, 7.2, 10.4, 27.5, 8.4, 3.2, 5.7, 12.2, 13.6])
        vect = vect[self.id_chron_to_back_load]
        assert self.compare_vect(injs_act, vect)

    def test_stop_right_time(self):
        done = False
        i = 0
        while not done:
            do_nothing = self.env.action_space({})
            obs, reward, done, info = self.env.step(
                do_nothing)  # should load the first time stamp
            i += 1
        assert i == 287

    def test_reward(self):
        done = False
        i = 0
        self.chronics_handler.next_chronics()

        with warnings.catch_warnings():
            warnings.filterwarnings("ignore")
            self.env = Environment(
                init_grid_path=os.path.join(self.path_matpower,
                                            self.case_file),
                backend=self.get_backend(),
                chronics_handler=self.chronics_handler,
                parameters=self.env_params,
                rewardClass=L2RPNReward,
                names_chronics_to_backend=self.names_chronics_to_backend,
                name="test_env_env2")
        if PROFILE_CODE:
            cp = cProfile.Profile()
            cp.enable()
        beg_ = time.time()
        cum_reward = dt_float(0.0)
        do_nothing = self.env.action_space({})
        while not done:
            obs, reward, done, info = self.env.step(
                do_nothing)  # should load the first time stamp
            cum_reward += reward
            i += 1
        end_ = time.time()
        if DEBUG:
            msg_ = "\nEnv: {:.2f}s\n\t - apply act {:.2f}s\n\t - run pf: {:.2f}s\n" \
                   "\t - env update + observation: {:.2f}s\nTotal time: {:.2f}\nCumulative reward: {:1f}"
            print(
                msg_.format(
                    self.env._time_apply_act + self.env._time_powerflow +
                    self.env._time_extract_obs, self.env._time_apply_act,
                    self.env._time_powerflow, self.env._time_extract_obs,
                    end_ - beg_, cum_reward))
        if PROFILE_CODE:
            cp.disable()
            cp.print_stats(sort="tottime")
        assert i == 287, "Wrong number of timesteps"
        expected_reward = dt_float(5719.9336)
        assert dt_float(
            np.abs(cum_reward -
                   expected_reward)) <= self.tol_one, "Wrong reward"
コード例 #3
0
class TestRedispatchChangeNothingEnvironment(HelperTests):
    def setUp(self):
        # powergrid
        self.backend = PandaPowerBackend()
        self.path_matpower = PATH_DATA_TEST_PP
        self.case_file = "test_case14.json"
        # chronics
        self.path_chron = os.path.join(PATH_CHRONICS, "chronics")
        self.chronics_handler = ChronicsHandler(chronicsClass=ChangeNothing)
        self.id_chron_to_back_load = np.array(
            [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9])

        # force the verbose backend
        self.backend.detailed_infos_for_cascading_failures = True
        self.names_chronics_to_backend = {
            "loads": {
                "2_C-10.61": 'load_1_0',
                "3_C151.15": 'load_2_1',
                "14_C63.6": 'load_13_2',
                "4_C-9.47": 'load_3_3',
                "5_C201.84": 'load_4_4',
                "6_C-6.27": 'load_5_5',
                "9_C130.49": 'load_8_6',
                "10_C228.66": 'load_9_7',
                "11_C-138.89": 'load_10_8',
                "12_C-27.88": 'load_11_9',
                "13_C-13.33": 'load_12_10'
            },
            "lines": {
                '1_2_1': '0_1_0',
                '1_5_2': '0_4_1',
                '9_10_16': '8_9_2',
                '9_14_17': '8_13_3',
                '10_11_18': '9_10_4',
                '12_13_19': '11_12_5',
                '13_14_20': '12_13_6',
                '2_3_3': '1_2_7',
                '2_4_4': '1_3_8',
                '2_5_5': '1_4_9',
                '3_4_6': '2_3_10',
                '4_5_7': '3_4_11',
                '6_11_11': '5_10_12',
                '6_12_12': '5_11_13',
                '6_13_13': '5_12_14',
                '4_7_8': '3_6_15',
                '4_9_9': '3_8_16',
                '5_6_10': '4_5_17',
                '7_8_14': '6_7_18',
                '7_9_15': '6_8_19'
            },
            "prods": {
                "1_G137.1": 'gen_0_4',
                "3_G36.31": "gen_2_1",
                "6_G63.29": "gen_5_2",
                "2_G-56.47": "gen_1_0",
                "8_G40.43": "gen_7_3"
            },
        }

        # _parameters for the environment
        self.env_params = Parameters()
        self.env = Environment(
            init_grid_path=os.path.join(self.path_matpower, self.case_file),
            backend=self.backend,
            chronics_handler=self.chronics_handler,
            parameters=self.env_params,
            names_chronics_to_backend=self.names_chronics_to_backend,
            actionClass=BaseAction)

    def tearDown(self):
        self.env.close()

    def test_redispatch_generator_off(self):
        """ Redispatch a turned off generator is illegal """

        # Step into simulation once
        nothing_act = self.env.action_space()
        obs, reward, done, info = self.env.step(nothing_act)

        # Check that generator 1 is redispatchable
        assert self.env.gen_redispatchable[1] == True

        # Check that generator 1 is off
        assert obs.prod_p[1] == 0
        assert self.env.gen_downtime[1] >= 1

        # Try to redispatch generator 1
        redispatch_act = self.env.action_space({"redispatch": [(1, 5.)]})
        obs, reward, done, info = self.env.step(redispatch_act)

        assert info['is_dispatching_illegal'] == True
コード例 #4
0
class TestObservationHazard(unittest.TestCase):
    def setUp(self):
        """
        The case file is a representation of the case14 as found in the ieee14 powergrid.
        :return:
        """
        # from ADNBackend import ADNBackend
        # self.backend = ADNBackend()
        # self.path_matpower = "/home/donnotben/Documents/RL4Grid/RL4Grid/data"
        # self.case_file = "ieee14_ADN.xml"
        # self.backend.load_grid(self.path_matpower, self.case_file)
        self.tolvect = 1e-2
        self.tol_one = 1e-5
        self.game_rules = RulesChecker()
        # pdb.set_trace()
        self.rewardClass = L2RPNReward
        self.reward_helper = self.rewardClass()
        self.obsClass = CompleteObservation
        self.parameters = Parameters()

        # powergrid
        self.backend = PandaPowerBackend()
        self.path_matpower = PATH_DATA_TEST_PP
        self.case_file = "test_case14.json"

        # chronics
        self.path_chron = os.path.join(PATH_CHRONICS, "chronics_with_hazards")
        self.chronics_handler = ChronicsHandler(
            chronicsClass=GridStateFromFile, path=self.path_chron)

        self.tolvect = 1e-2
        self.tol_one = 1e-5
        self.id_chron_to_back_load = np.array(
            [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9])

        # force the verbose backend
        self.backend.detailed_infos_for_cascading_failures = True

        self.names_chronics_to_backend = {
            "loads": {
                "2_C-10.61": 'load_1_0',
                "3_C151.15": 'load_2_1',
                "14_C63.6": 'load_13_2',
                "4_C-9.47": 'load_3_3',
                "5_C201.84": 'load_4_4',
                "6_C-6.27": 'load_5_5',
                "9_C130.49": 'load_8_6',
                "10_C228.66": 'load_9_7',
                "11_C-138.89": 'load_10_8',
                "12_C-27.88": 'load_11_9',
                "13_C-13.33": 'load_12_10'
            },
            "lines": {
                '1_2_1': '0_1_0',
                '1_5_2': '0_4_1',
                '9_10_16': '8_9_2',
                '9_14_17': '8_13_3',
                '10_11_18': '9_10_4',
                '12_13_19': '11_12_5',
                '13_14_20': '12_13_6',
                '2_3_3': '1_2_7',
                '2_4_4': '1_3_8',
                '2_5_5': '1_4_9',
                '3_4_6': '2_3_10',
                '4_5_7': '3_4_11',
                '6_11_11': '5_10_12',
                '6_12_12': '5_11_13',
                '6_13_13': '5_12_14',
                '4_7_8': '3_6_15',
                '4_9_9': '3_8_16',
                '5_6_10': '4_5_17',
                '7_8_14': '6_7_18',
                '7_9_15': '6_8_19'
            },
            "prods": {
                "1_G137.1": 'gen_0_4',
                "3_G36.31": "gen_2_1",
                "6_G63.29": "gen_5_2",
                "2_G-56.47": "gen_1_0",
                "8_G40.43": "gen_7_3"
            },
        }

        # _parameters for the environment
        self.env_params = Parameters()
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore")
            self.env = Environment(
                init_grid_path=os.path.join(self.path_matpower,
                                            self.case_file),
                backend=self.backend,
                chronics_handler=self.chronics_handler,
                parameters=self.env_params,
                names_chronics_to_backend=self.names_chronics_to_backend,
                rewardClass=self.rewardClass,
                name="test_obs_env1")

    def tearDown(self) -> None:
        self.env.close()

    def test_1_generating_obs_withhazard(self):
        # test that helper_obs is abl to generate a valid observation
        obs = self.env.get_obs()
        assert np.all(
            obs.time_before_cooldown_line ==
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
        action = self.env.action_space({})
        _ = self.env.step(action)
        obs = self.env.get_obs()
        assert np.all(
            obs.time_before_cooldown_line ==
            [0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
        _ = self.env.step(action)
        obs = self.env.get_obs()
        assert np.all(
            obs.time_before_cooldown_line ==
            [0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
コード例 #5
0
class TestObservationMaintenance(unittest.TestCase):
    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()
        # pdb.set_trace()
        self.rewardClass = L2RPNReward
        self.reward_helper = self.rewardClass()
        self.obsClass = CompleteObservation
        self.parameters = Parameters()

        # powergrid
        self.backend = PandaPowerBackend()
        self.path_matpower = PATH_DATA_TEST_PP
        self.case_file = "test_case14.json"

        # chronics
        self.path_chron = os.path.join(PATH_CHRONICS,
                                       "chronics_with_maintenance")
        self.chronics_handler = ChronicsHandler(
            chronicsClass=GridStateFromFileWithForecasts, path=self.path_chron)

        self.tolvect = 1e-2
        self.tol_one = 1e-5
        self.id_chron_to_back_load = np.array(
            [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9])

        # force the verbose backend
        self.backend.detailed_infos_for_cascading_failures = True

        self.names_chronics_to_backend = {
            "loads": {
                "2_C-10.61": 'load_1_0',
                "3_C151.15": 'load_2_1',
                "14_C63.6": 'load_13_2',
                "4_C-9.47": 'load_3_3',
                "5_C201.84": 'load_4_4',
                "6_C-6.27": 'load_5_5',
                "9_C130.49": 'load_8_6',
                "10_C228.66": 'load_9_7',
                "11_C-138.89": 'load_10_8',
                "12_C-27.88": 'load_11_9',
                "13_C-13.33": 'load_12_10'
            },
            "lines": {
                '1_2_1': '0_1_0',
                '1_5_2': '0_4_1',
                '9_10_16': '8_9_2',
                '9_14_17': '8_13_3',
                '10_11_18': '9_10_4',
                '12_13_19': '11_12_5',
                '13_14_20': '12_13_6',
                '2_3_3': '1_2_7',
                '2_4_4': '1_3_8',
                '2_5_5': '1_4_9',
                '3_4_6': '2_3_10',
                '4_5_7': '3_4_11',
                '6_11_11': '5_10_12',
                '6_12_12': '5_11_13',
                '6_13_13': '5_12_14',
                '4_7_8': '3_6_15',
                '4_9_9': '3_8_16',
                '5_6_10': '4_5_17',
                '7_8_14': '6_7_18',
                '7_9_15': '6_8_19'
            },
            "prods": {
                "1_G137.1": 'gen_0_4',
                "3_G36.31": "gen_2_1",
                "6_G63.29": "gen_5_2",
                "2_G-56.47": "gen_1_0",
                "8_G40.43": "gen_7_3"
            },
        }

        # _parameters for the environment
        self.env_params = Parameters()

        with warnings.catch_warnings():
            warnings.filterwarnings("ignore")
            self.env = Environment(
                init_grid_path=os.path.join(self.path_matpower,
                                            self.case_file),
                backend=self.backend,
                chronics_handler=self.chronics_handler,
                parameters=self.env_params,
                names_chronics_to_backend=self.names_chronics_to_backend,
                rewardClass=self.rewardClass,
                name="test_obs_env2",
                legalActClass=DefaultRules)

    def tearDown(self) -> None:
        self.env.close()

    def test_1_generating_obs_withmaintenance(self):
        # test that helper_obs is abl to generate a valid observation
        obs = self.env.get_obs()
        assert np.all(obs.time_next_maintenance == np.array([
            -1, -1, -1, -1, 1, -1, 276, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1
        ]))
        assert np.all(obs.duration_next_maintenance == np.array(
            [0, 0, 0, 0, 12, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
        action = self.env.action_space({})
        _ = self.env.step(action)
        obs = self.env.get_obs()
        assert np.all(obs.time_next_maintenance == np.array([
            -1, -1, -1, -1, 0, -1, 275, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1
        ]))
        assert np.all(obs.duration_next_maintenance == np.array(
            [0, 0, 0, 0, 12, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
        _ = self.env.step(action)
        obs = self.env.get_obs()
        assert np.all(obs.time_next_maintenance == np.array([
            -1, -1, -1, -1, 0, -1, 274, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
            -1, -1, -1
        ]))
        assert np.all(obs.duration_next_maintenance == np.array(
            [0, 0, 0, 0, 11, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))

    def test_simulate_disco_planned_maintenance(self):
        obs = self.env.get_obs()
        assert obs.line_status[4]
        assert obs.time_next_maintenance[4] == 1
        assert obs.duration_next_maintenance[4] == 12

        # line will be disconnected next time step
        sim_obs, *_ = obs.simulate(self.env.action_space(), time_step=1)
        assert not sim_obs.line_status[4]
        assert sim_obs.time_next_maintenance[4] == 0
        assert sim_obs.duration_next_maintenance[4] == 11
        # simulation at current step
        sim_obs, *_ = obs.simulate(self.env.action_space(), time_step=0)
        assert sim_obs.line_status[4]
        assert sim_obs.time_next_maintenance[4] == 1
        assert sim_obs.duration_next_maintenance[4] == 12
        # line will be disconnected next time step
        sim_obs, *_ = obs.simulate(self.env.action_space(), time_step=1)
        assert not sim_obs.line_status[4]
        assert sim_obs.time_next_maintenance[4] == 0
        assert sim_obs.duration_next_maintenance[4] == 11

        for ts in range(12):
            obs, reward, done, info = self.env.step(self.env.action_space())
        # maintenance will be over next time step
        assert not obs.line_status[4]
        assert obs.time_next_maintenance[4] == 0
        assert obs.duration_next_maintenance[4] == 1

        # if i don't do anything, it's updated properly
        sim_obs, *_ = obs.simulate(self.env.action_space(), time_step=1)
        assert not sim_obs.line_status[4]
        assert sim_obs.time_next_maintenance[4] == -1
        assert sim_obs.duration_next_maintenance[4] == 0

        # i have the right to reconnect it (if i simulate in the future)
        act = self.env.action_space()
        act.line_set_status = [(4, +1)]
        sim_obs, reward, done, info = obs.simulate(act, time_step=1)
        assert not info["is_illegal"]
        assert sim_obs.line_status[4]
        assert sim_obs.time_next_maintenance[4] == -1
        assert sim_obs.duration_next_maintenance[4] == 0

        # i don't have the right to reconnect it if i don't simulate in the future
        sim_obs, reward, done, info = obs.simulate(act, time_step=0)
        assert info["is_illegal"]
        assert not sim_obs.line_status[4]
        assert sim_obs.time_next_maintenance[4] == 0
        assert sim_obs.duration_next_maintenance[4] == 1
コード例 #6
0
ファイル: MultiEnv.py プロジェクト: gayukrishna/Grid2Op
class RemoteEnv(Process):
    """
    This class represent the environment that is executed on a remote process.

    Note that the environment is only created in the subprocess, and is not available in the main process. Once created
    it is not possible to access anything directly from it in the main process, where the BaseAgent lives. Only the
    :class:`grid2op.Observation.BaseObservation` are forwarded to the agent.

    """
    def __init__(self, env_params, remote, parent_remote, seed, name=None):
        Process.__init__(self, group=None, target=None, name=name)
        self.backend = None
        self.env = None
        self.env_params = env_params
        self.remote = remote
        self.parent_remote = parent_remote
        self.seed_used = seed
        self.space_prng = None

    def init_env(self):
        """
        Initialize the environment  that will perform all the computation of this process.
        Remember the environment only lives in this process. It cannot
        be transfer to / from the main process.

        This function also makes sure the chronics are read in different order accross all processes. This is done
        by calling the :func:`grid2op.Chronics.GridValue.shuffle` method. An example of how to use this function
        is provided in :func:`grid2op.Chronics.Multifolder.shuffle`.

        """
        # TODO documentation
        # TODO seed of the environment.

        self.space_prng = np.random.RandomState()
        self.space_prng.seed(seed=self.seed_used)
        self.backend = self.env_params["backendClass"]()
        del self.env_params["backendClass"]
        self.env = Environment(**self.env_params, backend=self.backend)
        self.env.chronics_handler.shuffle(shuffler=lambda x: x[
            self.space_prng.choice(len(x), size=len(x), replace=False)])

    def _clean_observation(self, obs):
        obs._forecasted_grid = []
        obs._forecasted_inj = []
        obs._obs_env = None
        obs.action_helper = None

    def get_obs_ifnotconv(self):
        # TODO dirty hack because of wrong chronics
        # need to check!!!
        conv = False
        obs = None
        while not conv:
            try:
                obs = self.env.reset()
                conv = True
            except:
                pass
        return obs

    def run(self):
        if self.env is None:
            self.init_env()

        while True:
            cmd, data = self.remote.recv()
            if cmd == 'get_spaces':
                self.remote.send(
                    (self.env.observation_space, self.env.action_space))
            elif cmd == 's':
                # perform a step
                data = self.env.action_space.from_vect(data)
                obs, reward, done, info = self.env.step(data)
                if done:
                    # if done do a reset
                    obs = self.get_obs_ifnotconv()
                self._clean_observation(obs)
                self.remote.send((obs.to_vect(), reward, done, info))
            elif cmd == 'r':
                # perfom a reset
                obs = self.get_obs_ifnotconv()
                self._clean_observation(obs)
                self.remote.send(obs.to_vect())
            elif cmd == 'c':
                # close everything
                self.env.close()
                self.remote.close()
                break
            elif cmd == 'z':
                # adapt the chunk size
                self.env.set_chunk_size(data)
            else:
                raise NotImplementedError
コード例 #7
0
class RemoteEnv(Process):
    """
    INTERNAL

     .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\

    This class represent the environment that is executed on a remote process.

    Note that the environment is only created in the subprocess, and is not available in the main process. Once created
    it is not possible to access anything directly from it in the main process, where the BaseAgent lives. Only the
    :class:`grid2op.Observation.BaseObservation` are forwarded to the agent.

    """
    def __init__(self,
                 env_params,
                 remote,
                 parent_remote,
                 seed,
                 name=None,
                 return_info=True,
                 _obs_to_vect=True):
        Process.__init__(self, group=None, target=None, name=name)
        self.backend = None
        self.env = None
        self.env_params = env_params
        self.remote = remote
        self.parent_remote = parent_remote
        self.seed_used = seed
        self.space_prng = None
        self.fast_forward = 0
        self.all_seeds = []

        # internal do not modify  # Do not work (in the sens that is it less efficient)
        self.return_info = return_info
        self._obs_to_vect = _obs_to_vect
        self._comp_time = 0.

    def init_env(self):
        """
        INTERNAL

        .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\

        Initialize the environment  that will perform all the computation of this process.
        Remember the environment only lives in this process. It cannot
        be transfer to / from the main process.

        This function also makes sure the chronics are read in different order accross all processes. This is done
        by calling the :func:`grid2op.Chronics.GridValue.shuffle` method. An example of how to use this function
        is provided in :func:`grid2op.Chronics.Multifolder.shuffle`.

        """
        self.space_prng = np.random.RandomState()
        self.space_prng.seed(seed=self.seed_used)
        self.backend = self.env_params["_raw_backend_class"]()
        with warnings.catch_warnings():
            # warnings have bee already sent in the main process, no need to resend them
            warnings.filterwarnings("ignore")
            self.env = Environment(**self.env_params, backend=self.backend)
        env_seed = self.space_prng.randint(np.iinfo(dt_int).max)
        self.all_seeds = self.env.seed(env_seed)
        self.env.chronics_handler.shuffle(shuffler=lambda x: x[
            self.space_prng.choice(len(x), size=len(x), replace=False)])

    def _clean_observation(self, obs):
        obs._forecasted_grid = []
        obs._forecasted_inj = []
        obs._obs_env = None
        obs.action_helper = None
        return obs

    def get_obs_ifnotconv(self):
        # warnings.warn(f"get_obs_ifnotconv is used")
        # TODO dirty hack because of wrong chronics
        # need to check!!!
        conv = False
        obs_v = None
        obs = None
        while not conv:
            try:
                self.env.reset()
                if self.fast_forward > 0:
                    self.env.fast_forward_chronics(
                        self.space_prng.randint(0, self.fast_forward))
                obs = self.env.get_obs()
                obs_v = obs.to_vect()
                if np.all(np.isfinite(obs_v)):
                    # i make sure that everything is not Nan
                    # other i consider it's "divergence" so "game over"
                    conv = True
            except Exception as exc_:
                pass
        if self._obs_to_vect:
            res = obs_v
        else:
            res = obs
        return res

    def run(self):
        if self.env is None:
            self.init_env()

        while True:
            cmd, data = self.remote.recv()
            if cmd == 'get_spaces':
                self.remote.send(
                    (self.env.observation_space, self.env.action_space))
            elif cmd == 's':
                # perform a step
                beg_ = time.time()
                if data is None:
                    data = self.env.action_space()
                else:
                    data = self.env.action_space.from_vect(data)
                obs, reward, done, info = self.env.step(data)
                obs_v = obs.to_vect()
                if done or np.any(~np.isfinite(obs_v)):
                    # if done do a reset
                    res_obs = self.get_obs_ifnotconv()
                elif self._obs_to_vect:
                    res_obs = obs.to_vect()
                else:
                    res_obs = self._clean_observation(obs)

                if not self.return_info:
                    info = None
                end_ = time.time()
                self._comp_time += end_ - beg_
                self.remote.send((res_obs, reward, done, info))
            elif cmd == 'r':
                # perfom a reset
                obs_v = self.get_obs_ifnotconv()
                self.remote.send(obs_v)
            elif cmd == 'c':
                # close everything
                self.env.close()
                self.remote.close()
                break
            elif cmd == 'z':
                # adapt the chunk size
                self.env.set_chunk_size(data)
            elif cmd == 'o':
                # get_obs
                tmp = self.env.get_obs()
                if self._obs_to_vect:
                    res_obs = tmp.to_vect()
                else:
                    res_obs = self._clean_observation(tmp)
                self.remote.send(res_obs)
            elif cmd == "f":
                # fast forward the chronics when restart
                self.fast_forward = int(data)
            elif cmd == "seed":
                self.remote.send((self.seed_used, self.all_seeds))
            elif cmd == "params":
                self.remote.send(self.env.parameters)
            elif cmd == "comp_time":
                self.remote.send(self._comp_time)
            elif cmd == "powerflow_time":
                self.remote.send(self.env.backend.comp_time)
            elif cmd == "step_time":
                self.remote.send(self.env._time_step)
            elif cmd == "set_filter":
                self.env.chronics_handler.set_filter(data)
                self.remote.send(None)
            elif cmd == "set_id":
                self.env.set_id(data)
                self.remote.send(None)
            elif hasattr(self.env, cmd):
                tmp = getattr(self.env, cmd)
                self.remote.send(tmp)
            else:
                raise NotImplementedError
コード例 #8
0
class BaseTestRedispatch(MakeBackend):
    def setUp(self):
        # powergrid
        self.backend = self.make_backend()
        self.path_matpower = self.get_path()
        self.case_file = self.get_casefile()

        # chronics
        self.path_chron = os.path.join(PATH_CHRONICS, "chronics")
        self.chronics_handler = ChronicsHandler(
            chronicsClass=GridStateFromFile, path=self.path_chron)
        self.id_chron_to_back_load = np.array(
            [0, 1, 10, 2, 3, 4, 5, 6, 7, 8, 9])

        # force the verbose backend
        self.backend.detailed_infos_for_cascading_failures = True
        self.names_chronics_to_backend = {
            "loads": {
                "2_C-10.61": 'load_1_0',
                "3_C151.15": 'load_2_1',
                "14_C63.6": 'load_13_2',
                "4_C-9.47": 'load_3_3',
                "5_C201.84": 'load_4_4',
                "6_C-6.27": 'load_5_5',
                "9_C130.49": 'load_8_6',
                "10_C228.66": 'load_9_7',
                "11_C-138.89": 'load_10_8',
                "12_C-27.88": 'load_11_9',
                "13_C-13.33": 'load_12_10'
            },
            "lines": {
                '1_2_1': '0_1_0',
                '1_5_2': '0_4_1',
                '9_10_16': '8_9_2',
                '9_14_17': '8_13_3',
                '10_11_18': '9_10_4',
                '12_13_19': '11_12_5',
                '13_14_20': '12_13_6',
                '2_3_3': '1_2_7',
                '2_4_4': '1_3_8',
                '2_5_5': '1_4_9',
                '3_4_6': '2_3_10',
                '4_5_7': '3_4_11',
                '6_11_11': '5_10_12',
                '6_12_12': '5_11_13',
                '6_13_13': '5_12_14',
                '4_7_8': '3_6_15',
                '4_9_9': '3_8_16',
                '5_6_10': '4_5_17',
                '7_8_14': '6_7_18',
                '7_9_15': '6_8_19'
            },
            "prods": {
                "1_G137.1": 'gen_0_4',
                "3_G36.31": "gen_2_1",
                "6_G63.29": "gen_5_2",
                "2_G-56.47": "gen_1_0",
                "8_G40.43": "gen_7_3"
            },
        }

        # _parameters for the environment
        self.env_params = Parameters()
        self.env_params.ALLOW_DISPATCH_GEN_SWITCH_OFF = False
        with warnings.catch_warnings():
            warnings.filterwarnings("ignore")
            self.env = Environment(
                init_grid_path=os.path.join(self.path_matpower,
                                            self.case_file),
                backend=self.backend,
                chronics_handler=self.chronics_handler,
                parameters=self.env_params,
                names_chronics_to_backend=self.names_chronics_to_backend,
                actionClass=BaseAction,
                name="test_redisp_env1")
        self.array_double_dispatch = np.array([0., 10., 20., 0., -30.])
        # self.array_double_dispatch = np.array([0.,  11.208119,  12.846733, 0., -24.054852])
        self.tol_one = self.env._tol_poly

    def tearDown(self):
        self.env.close()

    def test_negative_dispatch(self):
        self.skip_if_needed()
        act = self.env.action_space({"redispatch": [(1, -10)]})
        obs, reward, done, info = self.env.step(act)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.abs(np.sum(obs.actual_dispatch)) <= self.tol_one

    def test_no_impact_env(self):
        # perform a valid redispatching action
        self.skip_if_needed()
        obs_init = self.env.reset()  # reset the environment
        act = self.env.action_space()
        for i in range(
                1
        ):  # number cherry picked to introduce explain the behaviour in the cells bellow
            obsinit, rewardinit, doneinit, infoinit = self.env.step(
                self.env.action_space())
        ref_data = copy.deepcopy(obsinit.prod_p)
        act = self.env.action_space({"redispatch": [(0, -10)]})
        # act = env.action_space({"redispatch": [(4,0)]})
        obs, reward, done, info = self.env.step(act)
        assert self.compare_vect(obsinit.prod_p, ref_data)

        target_val = obs.prod_p + self.env._actual_dispatch
        assert self.compare_vect(
            obs.prod_p[:-1],
            target_val[:-1])  # I remove last component which is the slack bus
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)
        assert np.all(target_val <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - obsinit.prod_p <= self.env.gen_max_ramp_up)
        assert np.all(
            obsinit.prod_p - obs.prod_p <= self.env.gen_max_ramp_down)

    def test_basic_redispatch_act(self):
        # test of the implementation of a simple case redispatching on one generator, bellow ramp min and ramp max
        self.skip_if_needed()
        act = self.env.action_space({"redispatch": (2, 5)})
        obs, reward, done, info = self.env.step(act)
        assert np.abs(np.sum(self.env._actual_dispatch)) <= self.tol_one
        th_dispatch = np.array([0., -2.5, 5., 0., -2.5])
        th_dispatch = np.array([0., -1.4814819, 5., 0., -3.518518])
        assert self.compare_vect(self.env._actual_dispatch, th_dispatch)
        target_val = self.chronics_handler.real_data.prod_p[
            1, :] + self.env._actual_dispatch
        assert self.compare_vect(
            obs.prod_p[:-1],
            target_val[:-1])  # I remove last component which is the slack bus
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)
        assert np.all(target_val <= self.env.gen_pmax + self.tol_one)

        # check that the redispatching is apply in the right direction
        indx_ok = self.env._target_dispatch != 0.
        assert np.all(
            np.sign(self.env._actual_dispatch[indx_ok]) == np.sign(
                self.env._target_dispatch[indx_ok]))

    def test_redispatch_act_above_pmax(self):
        # in this test, the asked redispatching for generator 2 would make it above pmax, so the environment
        # need to "cut" it automatically, without invalidating the action
        self.skip_if_needed()
        act = self.env.action_space({"redispatch": (2, 60)})
        obs, reward, done, info = self.env.step(act)
        assert np.abs(np.sum(self.env._actual_dispatch)) <= self.tol_one
        th_dispatch = np.array([0., -23.2999, 50.899902, 0., -27.600002])
        th_dispatch = np.array([0., -20., 40., 0., -20.])
        th_dispatch = np.array([0., -13.227808, 50.90005, 0., -37.67224])
        assert self.compare_vect(self.env._actual_dispatch, th_dispatch)
        target_val = self.chronics_handler.real_data.prod_p[
            1, :] + self.env._actual_dispatch
        assert self.compare_vect(
            obs.prod_p[:-1],
            target_val[:-1])  # I remove last component which is the slack bus
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)
        assert np.all(target_val <= self.env.gen_pmax + self.tol_one)

    def test_two_redispatch_act(self):
        self.skip_if_needed()
        act = self.env.action_space({"redispatch": (2, 20)})
        obs_first, reward, done, info = self.env.step(act)
        act = self.env.action_space({"redispatch": (1, 10)})
        obs, reward, done, info = self.env.step(act)
        th_dispatch = np.array([0., 10, 20., 0., 0.])
        th_dispatch[1] += obs_first.actual_dispatch[1]
        assert self.compare_vect(self.env._target_dispatch, th_dispatch)
        # check that the redispatching is apply in the right direction
        indx_ok = self.env._target_dispatch != 0.
        assert np.all(
            np.sign(self.env._actual_dispatch[indx_ok]) == np.sign(
                self.env._target_dispatch[indx_ok]))
        th_dispatch = np.array([0., 10., 20., 0., -30.])
        th_dispatch = np.array([0., 4.0765514, 20.004545, 0., -24.081097])
        assert self.compare_vect(self.env._actual_dispatch, th_dispatch)

        target_val = self.chronics_handler.real_data.prod_p[
            2, :] + self.env._actual_dispatch
        assert self.compare_vect(
            obs.prod_p[:-1],
            target_val[:-1])  # I remove last component which is the slack bus
        assert np.abs(np.sum(self.env._actual_dispatch)) <= self.tol_one
        assert np.all(target_val <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

    def test_redispacth_two_gen(self):
        self.skip_if_needed()
        act = self.env.action_space({"redispatch": [(2, 20), (1, 10)]})
        obs, reward, done, info = self.env.step(act)
        assert not done
        th_dispatch = np.array([0., 10, 20., 0., 0.])
        assert self.compare_vect(self.env._target_dispatch, th_dispatch)
        assert self.compare_vect(self.env._actual_dispatch,
                                 self.array_double_dispatch)

        # check that the redispatching is apply in the right direction
        indx_ok = self.env._target_dispatch != 0.
        assert np.all(
            np.sign(self.env._actual_dispatch[indx_ok]) == np.sign(
                self.env._target_dispatch[indx_ok]))
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

    def test_redispacth_all_gen(self):
        # this should be exactly the same as the previous one
        self.skip_if_needed()
        act = self.env.action_space(
            {"redispatch": [(2, 20.), (1, 10.), (4, -30.)]})
        obs, reward, done, info = self.env.step(act)

        th_dispatch = np.array([0., 10, 20., 0., -30.])
        assert self.compare_vect(self.env._target_dispatch, th_dispatch)
        assert self.compare_vect(self.env._actual_dispatch,
                                 self.array_double_dispatch)

        # check that the redispatching is apply in the right direction
        indx_ok = self.env._target_dispatch != 0.
        assert np.all(
            np.sign(self.env._actual_dispatch[indx_ok]) == np.sign(
                self.env._target_dispatch[indx_ok]))
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

    def test_count_turned_on(self):
        self.skip_if_needed()
        act = self.env.action_space()

        # recoded it: it's the normal behavior to call "env.reset()" to get the first time step
        obs = self.env.reset()
        assert np.all(self.env._gen_uptime == np.array([0, 1, 1, 0, 1]))
        assert np.all(self.env._gen_downtime == np.array([1, 0, 0, 1, 0]))
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

        obs, reward, done, info = self.env.step(act)
        assert np.all(self.env._gen_uptime == np.array([0, 2, 2, 0, 2]))
        assert np.all(self.env._gen_downtime == np.array([2, 0, 0, 2, 0]))
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

        for i in range(64):
            obs, reward, done, info = self.env.step(act)
            assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
            assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

        obs, reward, done, info = self.env.step(act)
        assert np.all(self.env._gen_uptime == np.array([0, 67, 67, 1, 67]))
        assert np.all(self.env._gen_downtime == np.array([67, 0, 0, 0, 0]))
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

        obs, reward, done, info = self.env.step(act)
        assert np.all(self.env._gen_uptime == np.array([1, 68, 68, 2, 68]))
        assert np.all(self.env._gen_downtime == np.array([0, 0, 0, 0, 0]))
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

    def test_redispacth_twice_same(self):
        self.skip_if_needed()
        # this should be exactly the same as the previous one
        act = self.env.action_space({"redispatch": [(2, 5.)]})
        obs, reward, done, info = self.env.step(act)
        assert np.all(obs.target_dispatch == np.array([0., 0., 5., 0., 0.]))
        assert np.abs(np.sum(obs.actual_dispatch)) <= self.tol_one
        th_disp = np.array([0., -2.5, 5., 0., -2.5])
        th_disp = np.array([0., -1.4814819, 5., 0., -3.518518])
        assert self.compare_vect(obs.actual_dispatch, th_disp)
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

        act = self.env.action_space({"redispatch": [(2, 5.)]})
        obs, reward, done, info = self.env.step(act)
        assert np.all(obs.target_dispatch == np.array([0., 0., 10., 0., 0.]))
        assert np.abs(np.sum(obs.actual_dispatch)) <= self.tol_one
        th_disp = np.array([0., -5., 10., 0., -5.])
        assert self.compare_vect(obs.actual_dispatch, th_disp)
        assert np.all(obs.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs.prod_p - self.env.gen_pmin >= -self.tol_one)

    def test_redispacth_secondabovepmax(self):
        self.skip_if_needed()
        act = self.env.action_space({"redispatch": [(2, 20.)]})
        obs0, reward, done, info = self.env.step(act)
        assert np.all(obs0.target_dispatch == np.array([0., 0., 20., 0., 0.]))
        assert np.abs(np.sum(obs0.actual_dispatch)) <= self.tol_one
        th_disp = np.array([0., -10., 20., 0., -10.])
        th_disp = np.array([0., -5.9259276, 20., 0., -14.074072])
        assert self.compare_vect(obs0.actual_dispatch, th_disp)
        assert np.all(obs0.prod_p <= self.env.gen_pmax + self.tol_one)
        assert np.all(obs0.prod_p >= self.env.gen_pmin - self.tol_one)

        act = self.env.action_space({"redispatch": [(2, 40.)]})
        obs, reward, done, info = self.env.step(act)
        assert not info["is_dispatching_illegal"]
        assert np.all(obs.target_dispatch == np.array([0., 0., 60., 0., 0.]))
        th_disp = np.array([0., -23.5, 50.4, 0., -26.900002])
        assert self.compare_vect(obs.actual_dispatch, th_disp)
        assert np.all(obs.prod_p[:-1] <= self.env.gen_pmax[:-1] + self.tol_one)
        assert np.all(obs.prod_p[:-1] >= self.env.gen_pmin[:-1] - self.tol_one)
        assert np.all(obs.prod_p[:-1] -
                      obs0.prod_p[:-1] >= -self.env.gen_max_ramp_down[:-1])
        assert np.all(obs.prod_p[:-1] -
                      obs0.prod_p[:-1] <= self.env.gen_max_ramp_up[:-1])

    def test_redispacth_non_dispatchable_generator(self):
        """ Dispatch a non redispatchable generator is ambiguous """
        self.skip_if_needed()
        act = self.env.action_space()
        obs, reward, done, info = self.env.step(act)

        # Check that generator 0 isn't redispatchable
        assert self.env.gen_redispatchable[0] == False
        # Check that generator 0 is off
        assert self.env._gen_downtime[0] >= 1

        # Try to redispatch
        redispatch_act = self.env.action_space({"redispatch": [(0, 5.)]})
        obs, reward, done, info = self.env.step(redispatch_act)

        assert info['is_ambiguous']