class TestTopoAction(unittest.TestCase): # Cette méthode sera appelée avant chaque test. def setUp(self): self.backend = PandaPowerBackend() self.path_matpower = PATH_DATA_TEST self.case_file = "test_case14.json" self.backend.load_grid(self.path_matpower, self.case_file) self.tolvect = 1e-2 self.tol_one = 1e-5 self.game_rules = GameRules() self.helper_action = HelperAction( gridobj=self.backend, legal_action=self.game_rules.legal_action) # Cette méthode sera appelée après chaque test. def tearDown(self): pass def compare_vect(self, pred, true): return np.max(np.abs(pred - true)) <= self.tolvect def test_topo_set1sub(self): # retrieve some initial data to be sure only a subpart of the _grid is modified conv = self.backend.runpf() init_amps_flow = self.backend.get_line_flow() # check that maintenance vector is properly taken into account arr = np.array([1, 1, 1, 2, 2, 2], dtype=np.int) id_ = 1 action = self.helper_action( {"set_bus": { "substations_id": [(id_, arr)] }}) # apply the action here self.backend.apply_action(action) conv = self.backend.runpf() assert conv after_amps_flow = self.backend.get_line_flow() topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1, "all buses have been changed" assert np.max(topo_vect) == 2, "no buses have been changed" # check that the objects have been properly moved load_ids = np.where(self.backend.load_to_subid == id_)[0] assert np.all(topo_vect[self.backend.load_pos_topo_vect[load_ids]] == arr[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.line_or_to_subid == id_)[0] assert np.all(topo_vect[self.backend.line_or_pos_topo_vect[lor_ids]] == arr[self.backend.line_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.line_ex_to_subid == id_)[0] assert np.all(topo_vect[self.backend.line_ex_pos_topo_vect[lex_ids]] == arr[self.backend.line_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid == id_)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == arr[self.backend.gen_to_sub_pos[gen_ids]]) after_amps_flow_th = np.array([ 6.38865247e+02, 3.81726828e+02, 1.78001287e+04, 2.70742428e+04, 1.06755055e+04, 4.71160165e+03, 1.52265925e+04, 3.37755751e+02, 3.00535519e+02, 5.01164454e-13, 7.01900962e+01, 1.73874580e+02, 2.08904697e+04, 2.11757439e+04, 4.93863382e+04, 1.31935835e+02, 6.99779475e+01, 1.85068609e+02, 7.47283039e+02, 1.14125596e+03 ]) after_amps_flow_th = np.array([ 596.58386539, 342.31364678, 18142.87789987, 27084.37162086, 10155.86483194, 4625.93022957, 15064.92626615, 322.59381855, 273.6977149, 82.21908229, 80.91290202, 206.04740125, 20480.81970337, 21126.22533095, 49275.71520428, 128.04429617, 69.00661266, 188.44754187, 688.1371226, 1132.42521887 ]) assert self.compare_vect(after_amps_flow, after_amps_flow_th) try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max( np.abs(p_subs) ) <= self.tolvect, "problem with active values, at substation" assert np.max( np.abs(q_subs) ) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten( ))) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten( ))) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass def test_topo_change1sub(self): # check that switching the bus of 3 object is equivalent to set them to bus 2 (as above) conv = self.backend.runpf() init_amps_flow = self.backend.get_line_flow() # check that maintenance vector is properly taken into account arr = np.array([False, False, False, True, True, True], dtype=np.bool) id_ = 1 action = self.helper_action( {"change_bus": { "substations_id": [(id_, arr)] }}) # apply the action here self.backend.apply_action(action) # run the powerflow conv = self.backend.runpf() assert conv after_amps_flow = self.backend.get_line_flow() topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1, "all buses have been changed" assert np.max(topo_vect) == 2, "no buses have been changed" # check that the objects have been properly moved load_ids = np.where(self.backend.load_to_subid == id_)[0] assert np.all( topo_vect[self.backend.load_pos_topo_vect[load_ids]] == 1 + arr[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.line_or_to_subid == id_)[0] assert np.all( topo_vect[self.backend.line_or_pos_topo_vect[lor_ids]] == 1 + arr[self.backend.line_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.line_ex_to_subid == id_)[0] assert np.all( topo_vect[self.backend.line_ex_pos_topo_vect[lex_ids]] == 1 + arr[self.backend.line_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid == id_)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == 1 + arr[self.backend.gen_to_sub_pos[gen_ids]]) after_amps_flow_th = np.array([ 596.58386539, 342.31364678, 18142.87789987, 27084.37162086, 10155.86483194, 4625.93022957, 15064.92626615, 322.59381855, 273.6977149, 82.21908229, 80.91290202, 206.04740125, 20480.81970337, 21126.22533095, 49275.71520428, 128.04429617, 69.00661266, 188.44754187, 688.1371226, 1132.42521887 ]) assert self.compare_vect(after_amps_flow, after_amps_flow_th) try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max( np.abs(p_subs) ) <= self.tolvect, "problem with active values, at substation" assert np.max( np.abs(q_subs) ) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten( ))) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten( ))) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass def test_topo_change_1sub_twice(self): # check that switching the bus of 3 object is equivalent to set them to bus 2 (as above) # and that setting it again is equivalent to doing nothing conv = self.backend.runpf() init_amps_flow = np.array([el for el in self.backend.get_line_flow()]) # check that maintenance vector is properly taken into account arr = np.array([False, False, False, True, True, True], dtype=np.bool) id_ = 1 action = self.helper_action( {"change_bus": { "substations_id": [(id_, arr)] }}) # apply the action here self.backend.apply_action(action) conv = self.backend.runpf() assert conv after_amps_flow = self.backend.get_line_flow() topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1, "all buses have been changed" assert np.max(topo_vect) == 2, "no buses have been changed" # check that the objects have been properly moved load_ids = np.where(self.backend.load_to_subid == id_)[0] assert np.all( topo_vect[self.backend.load_pos_topo_vect[load_ids]] == 1 + arr[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.line_or_to_subid == id_)[0] assert np.all( topo_vect[self.backend.line_or_pos_topo_vect[lor_ids]] == 1 + arr[self.backend.line_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.line_ex_to_subid == id_)[0] assert np.all( topo_vect[self.backend.line_ex_pos_topo_vect[lex_ids]] == 1 + arr[self.backend.line_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid == id_)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == 1 + arr[self.backend.gen_to_sub_pos[gen_ids]]) after_amps_flow_th = np.array([ 596.58386539, 342.31364678, 18142.87789987, 27084.37162086, 10155.86483194, 4625.93022957, 15064.92626615, 322.59381855, 273.6977149, 82.21908229, 80.91290202, 206.04740125, 20480.81970337, 21126.22533095, 49275.71520428, 128.04429617, 69.00661266, 188.44754187, 688.1371226, 1132.42521887 ]) assert self.compare_vect(after_amps_flow, after_amps_flow_th) try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max( np.abs(p_subs) ) <= self.tolvect, "problem with active values, at substation" assert np.max( np.abs(q_subs) ) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten( ))) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten( ))) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass action = self.helper_action( {"change_bus": { "substations_id": [(id_, arr)] }}) # apply the action here self.backend.apply_action(action) conv = self.backend.runpf() assert conv after_amps_flow = self.backend.get_line_flow() assert self.compare_vect(after_amps_flow, init_amps_flow) topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1 assert np.max(topo_vect) == 1 try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max( np.abs(p_subs) ) <= self.tolvect, "problem with active values, at substation" assert np.max( np.abs(q_subs) ) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten( ))) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten( ))) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass def test_topo_change_2sub(self): # check that maintenance vector is properly taken into account 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)] } }) # apply the action here self.backend.apply_action(action) conv = self.backend.runpf() assert conv # check the _grid is correct topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1, "all buses have been changed" assert np.max(topo_vect) == 2, "no buses have been changed" # check that the objects have been properly moved load_ids = np.where(self.backend.load_to_subid == id_1)[0] assert np.all( topo_vect[self.backend.load_pos_topo_vect[load_ids]] == 1 + arr1[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.line_or_to_subid == id_1)[0] assert np.all( topo_vect[self.backend.line_or_pos_topo_vect[lor_ids]] == 1 + arr1[self.backend.line_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.line_ex_to_subid == id_1)[0] assert np.all( topo_vect[self.backend.line_ex_pos_topo_vect[lex_ids]] == 1 + arr1[self.backend.line_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid == id_1)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == 1 + arr1[self.backend.gen_to_sub_pos[gen_ids]]) load_ids = np.where(self.backend.load_to_subid == id_2)[0] assert np.all(topo_vect[self.backend.load_pos_topo_vect[load_ids]] == arr2[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.line_or_to_subid == id_2)[0] assert np.all(topo_vect[self.backend.line_or_pos_topo_vect[lor_ids]] == arr2[self.backend.line_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.line_ex_to_subid == id_2)[0] assert np.all(topo_vect[self.backend.line_ex_pos_topo_vect[lex_ids]] == arr2[self.backend.line_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid == id_2)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == arr2[self.backend.gen_to_sub_pos[gen_ids]]) after_amps_flow = self.backend.get_line_flow() after_amps_flow_th = np.array([ 596.97014348, 342.10559579, 16615.11815357, 31328.50690716, 11832.77202397, 11043.10650167, 11043.10650167, 322.79533908, 273.86501458, 82.34066647, 80.89289074, 208.42396413, 22178.16766548, 27690.51322075, 38684.31540646, 129.44842477, 70.02629553, 185.67687123, 706.77680037, 1155.45754617 ]) assert self.compare_vect(after_amps_flow, after_amps_flow_th) try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max( np.abs(p_subs) ) <= self.tolvect, "problem with active values, at substation" assert np.max( np.abs(q_subs) ) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten( ))) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten( ))) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass
class TestLoadingBackendFunc(unittest.TestCase): # Cette méthode sera appelée avant chaque test. def setUp(self): self.backend = PandaPowerBackend() self.path_matpower = PATH_DATA_TEST self.case_file = "test_case14.json" self.backend.load_grid(self.path_matpower, self.case_file) self.tolvect = 1e-2 self.tol_one = 1e-5 self.game_rules = GameRules() self.action_env = HelperAction( gridobj=self.backend, legal_action=self.game_rules.legal_action) # Cette méthode sera appelée après chaque test. def tearDown(self): pass def compare_vect(self, pred, true): return np.max(np.abs(pred - true)) <= self.tolvect def test_runpf(self): conv = self.backend.runpf(is_dc=True) assert conv true_values_ac = np.array([ 1.56882891e+02, 7.55103818e+01, 5.22755247e+00, 9.42638103e+00, -3.78532238e+00, 1.61425777e+00, 5.64385098e+00, 7.32375792e+01, 5.61314959e+01, 4.15162150e+01, -2.32856901e+01, -6.11582304e+01, 7.35327698e+00, 7.78606702e+00, 1.77479769e+01, 2.80741759e+01, 1.60797576e+01, 4.40873209e+01, -1.11022302e-14, 2.80741759e+01 ]) true_values_dc = np.array([ 147.83859556, 71.16140444, 5.7716542, 9.64132512, -3.2283458, 1.50735814, 5.25867488, 70.01463596, 55.1518527, 40.9721069, -24.18536404, -61.74649065, 6.7283458, 7.60735814, 17.25131674, 28.36115279, 16.55182652, 42.78702069, 0., 28.36115279 ]) p_or, *_ = self.backend.lines_or_info() assert self.compare_vect(p_or, true_values_dc) conv = self.backend.runpf(is_dc=False) assert conv p_or, *_ = self.backend.lines_or_info() assert self.compare_vect(p_or, true_values_ac) def test_voltage_convert_powerlines(self): # i have the correct voltages in powerlines if the formula to link mw, mvar, kv and amps is correct conv = self.backend.runpf(is_dc=False) assert conv p_or, q_or, v_or, a_or = self.backend.lines_or_info() a_th = np.sqrt(p_or**2 + q_or**2) * 1e3 / (np.sqrt(3) * v_or) assert self.compare_vect(a_th, a_or) p_ex, q_ex, v_ex, a_ex = self.backend.lines_ex_info() a_th = np.sqrt(p_ex**2 + q_ex**2) * 1e3 / (np.sqrt(3) * v_ex) assert self.compare_vect(a_th, a_ex) def test_voltages_correct_load_gen(self): # i have the right voltages to generators and load, if it's the same as the voltage (correct from the above test) # of the powerline connected to it. conv = self.backend.runpf(is_dc=False) load_p, load_q, load_v = self.backend.loads_info() gen_p, gen__q, gen_v = self.backend.generators_info() p_or, q_or, v_or, a_or = self.backend.lines_or_info() p_ex, q_ex, v_ex, a_ex = self.backend.lines_ex_info() for c_id, sub_id in enumerate(self.backend.load_to_subid): l_id = np.where(self.backend.line_or_to_subid == sub_id)[0] if len(l_id): l_id = l_id[0] assert np.abs(v_or[l_id] - load_v[c_id] ) <= self.tol_one, "problem for load {}".format( c_id) continue l_id = np.where(self.backend.line_ex_to_subid == sub_id)[0] if len(l_id): l_id = l_id[0] assert np.abs(v_ex[l_id] - load_v[c_id] ) <= self.tol_one, "problem for load {}".format( c_id) continue assert False, "load {} has not been checked".format(c_id) for g_id, sub_id in enumerate(self.backend.gen_to_subid): l_id = np.where(self.backend.line_or_to_subid == sub_id)[0] if len(l_id): l_id = l_id[0] assert np.abs( v_or[l_id] - gen_v[g_id] ) <= self.tol_one, "problem for generator {}".format(g_id) continue l_id = np.where(self.backend.line_ex_to_subid == sub_id)[0] if len(l_id): l_id = l_id[0] assert np.abs( v_ex[l_id] - gen_v[g_id] ) <= self.tol_one, "problem for generator {}".format(g_id) continue assert False, "generator {} has not been checked".format(g_id) def test_copy(self): conv = self.backend.runpf(is_dc=False) p_or_orig, *_ = self.backend.lines_or_info() adn_backend_cpy = self.backend.copy() self.backend._disconnect_line(3) conv = self.backend.runpf(is_dc=False) assert conv conv2 = adn_backend_cpy.runpf(is_dc=False) assert conv2 p_or_ref, *_ = self.backend.lines_or_info() p_or, *_ = adn_backend_cpy.lines_or_info() assert np.abs(p_or_ref[3]) <= self.tol_one assert self.compare_vect(p_or_orig, p_or) def test_get_line_status(self): assert np.all(self.backend.get_line_status()) self.backend._disconnect_line(3) assert np.sum(~self.backend.get_line_status()) == 1 assert not self.backend.get_line_status()[3] def test_get_line_flow(self): self.backend.runpf(is_dc=False) true_values_ac = np.array([ -20.40429168, 3.85499114, 4.2191378, 3.61000624, -1.61506292, 0.75395917, 1.74717378, 3.56020295, -1.5503504, 1.17099786, 4.47311562, 15.82364194, 3.56047297, 2.50341424, 7.21657539, -9.68106571, -0.42761118, 12.47067981, -17.16297051, 5.77869057 ]) p_or_orig, q_or_orig, *_ = self.backend.lines_or_info() assert self.compare_vect(q_or_orig, true_values_ac) self.backend._disconnect_line(3) a = self.backend.runpf(is_dc=False) true_values_ac = np.array([ -20.40028207, 3.65600775, 3.77916284, 0., -2.10761554, 1.34025308, 5.86505081, 3.58514625, -2.28717836, 0.81979017, 3.72328838, 17.09556423, 3.9548798, 3.18389804, 11.24144925, -11.09660174, -1.70423701, 13.14347167, -14.82917601, 2.276297 ]) p_or_orig, q_or_orig, *_ = self.backend.lines_or_info() assert self.compare_vect(q_or_orig, true_values_ac) def test_pf_ac_dc(self): true_values_ac = np.array([ -20.40429168, 3.85499114, 4.2191378, 3.61000624, -1.61506292, 0.75395917, 1.74717378, 3.56020295, -1.5503504, 1.17099786, 4.47311562, 15.82364194, 3.56047297, 2.50341424, 7.21657539, -9.68106571, -0.42761118, 12.47067981, -17.16297051, 5.77869057 ]) conv = self.backend.runpf(is_dc=True) assert conv p_or_orig, q_or_orig, *_ = self.backend.lines_or_info() assert np.all(q_or_orig == 0.) conv = self.backend.runpf(is_dc=False) assert conv p_or_orig, q_or_orig, *_ = self.backend.lines_or_info() assert self.compare_vect(q_or_orig, true_values_ac) def test_get_thermal_limit(self): res = self.backend.get_thermal_limit() true_values_ac = np.array([ 42339.01974057, 42339.01974057, 27479652.23546777, 27479652.23546777, 27479652.23546777, 27479652.23546777, 27479652.23546777, 42339.01974057, 42339.01974057, 42339.01974057, 42339.01974057, 42339.01974057, 27479652.23546777, 27479652.23546777, 27479652.23546777, 42339.01974057, 42339.01974057, 42339.01974057, 408269.11892695, 408269.11892695 ]) assert self.compare_vect(res, true_values_ac) def test_disconnect_line(self): for i in range(self.backend.n_line): if i == 18: # powerflow diverge if line 1 is removed, unfortunately continue backend_cpy = self.backend.copy() backend_cpy._disconnect_line(i) conv = backend_cpy.runpf() assert conv, "Power flow computation does not converge if line {} is removed".format( i) flows = backend_cpy.get_line_status() assert not flows[i] assert np.sum(~flows) == 1 def test_donothing_action(self): conv = self.backend.runpf() init_flow = self.backend.get_line_flow() init_lp, *_ = self.backend.loads_info() init_gp, *_ = self.backend.generators_info() init_ls = self.backend.get_line_status() action = self.action_env({}) # update the action self.backend.apply_action(action) after_lp, *_ = self.backend.loads_info() after_gp, *_ = self.backend.generators_info() after_ls = self.backend.get_line_status() assert self.compare_vect(init_lp, after_lp) # check i didn't modify the loads # assert self.compare_vect(init_gp, after_gp) # check i didn't modify the generators # TODO here !!! problem with steady state P=C+L assert np.all( init_ls == after_ls) # check i didn't disconnect any powerlines conv = self.backend.runpf() assert conv, "Cannot perform a powerflow after doing nothing" after_flow = self.backend.get_line_flow() assert self.compare_vect(init_flow, after_flow) def test_apply_action_active_value(self): # test that i can modify only the load / prod active values of the powergrid # to do that i modify the productions and load all of a factor 0.5 and compare that the DC flows are # also multiply by 2 conv = self.backend.runpf(is_dc=True) init_flow, *_ = self.backend.lines_or_info() init_lp, init_l_q, *_ = self.backend.loads_info() init_gp, *_ = self.backend.generators_info() init_ls = self.backend.get_line_status() ratio = 0.5 action = self.action_env({ "injection": { "load_p": ratio * init_lp, "prod_p": ratio * init_gp * np.sum(init_lp) / np.sum(init_gp) } }) # update the action self.backend.apply_action(action) conv = self.backend.runpf(is_dc=True) assert conv, "Cannot perform a powerflow after doing nothing" after_lp, after_lq, *_ = self.backend.loads_info() after_gp, *_ = self.backend.generators_info() after_ls = self.backend.get_line_status() assert self.compare_vect(ratio * init_lp, after_lp) # check i didn't modify the loads try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() # i'm in DC mode, i can't check for reactive values... assert np.max( np.abs(p_subs) ) <= self.tolvect, "problem with active values, at substation" assert np.max(np.abs(p_bus.flatten( ))) <= self.tolvect, "problem with active values, at a bus" except Grid2OpException: pass assert self.compare_vect( ratio * init_gp, after_gp) # check i didn't modify the generators assert np.all( init_ls == after_ls) # check i didn't disconnect any powerlines after_flow, *_ = self.backend.lines_or_info() assert self.compare_vect( ratio * init_flow, after_flow) # probably an error with the DC approx def test_apply_action_prod_v(self): conv = self.backend.runpf(is_dc=False) prod_p_init, prod_q_init, prod_v_init = self.backend.generators_info() ratio = 1.05 action = self.action_env( {"injection": { "prod_v": ratio * prod_v_init }}) # update the action self.backend.apply_action(action) conv = self.backend.runpf(is_dc=False) assert conv, "Cannot perform a powerflow aftermodifying the powergrid" prod_p_after, prod_q_after, prod_v_after = self.backend.generators_info( ) assert self.compare_vect( ratio * prod_v_init, prod_v_after) # check i didn't modify the generators def test_apply_action_maintenance(self): # retrieve some initial data to be sure only a subpart of the _grid is modified conv = self.backend.runpf() init_lp, *_ = self.backend.loads_info() init_gp, *_ = self.backend.generators_info() # check that maintenance vector is properly taken into account maintenance = np.full((self.backend.n_line, ), fill_value=False, dtype=np.bool) maintenance[19] = True action = self.action_env({"maintenance": maintenance}) # update the action # apply the action here self.backend.apply_action(action) # compute a load flow an performs more tests conv = self.backend.runpf() assert conv, "Power does not converge if line {} is removed".format(19) # performs basic check after_lp, *_ = self.backend.loads_info() after_gp, *_ = self.backend.generators_info() after_ls = self.backend.get_line_status() assert self.compare_vect(init_lp, after_lp) # check i didn't modify the loads # assert self.compare_vect(init_gp, after_gp) # check i didn't modify the generators # TODO here problem with steady state P=C+L assert np.all( ~maintenance == after_ls ) # check i didn't disconnect any powerlines beside the correct one flows = self.backend.get_line_status() assert np.sum(~flows) == 1 assert not flows[19] def test_apply_action_hazard(self): conv = self.backend.runpf() init_lp, *_ = self.backend.loads_info() init_gp, *_ = self.backend.generators_info() # check that maintenance vector is properly taken into account maintenance = np.full((self.backend.n_line, ), fill_value=False, dtype=np.bool) maintenance[17] = True action = self.action_env({"hazards": maintenance}) # update the action # apply the action here self.backend.apply_action(action) # compute a load flow an performs more tests conv = self.backend.runpf() assert conv, "Power does not converge if line {} is removed".format(19) # performs basic check after_lp, *_ = self.backend.loads_info() after_gp, *_ = self.backend.generators_info() after_ls = self.backend.get_line_status() assert self.compare_vect(init_lp, after_lp) # check i didn't modify the loads # assert self.compare_vect(init_gp, after_gp) # check i didn't modify the generators # TODO here problem with steady state P=C+L assert np.all( maintenance == ~after_ls ) # check i didn't disconnect any powerlines beside the correct one # def test_apply_action_disconnection(self): # retrieve some initial data to be sure only a subpart of the _grid is modified conv = self.backend.runpf() init_lp, *_ = self.backend.loads_info() init_gp, *_ = self.backend.generators_info() # check that maintenance vector is properly taken into account maintenance = np.full((self.backend.n_line, ), fill_value=False, dtype=np.bool) maintenance[19] = True disc = np.full((self.backend.n_line, ), fill_value=False, dtype=np.bool) disc[17] = True action = self.action_env({ "hazards": disc, "maintenance": maintenance }) # update the action # apply the action here self.backend.apply_action(action) # compute a load flow an performs more tests conv = self.backend.runpf() assert conv, "Power does not converge if lines {} and {} are removed".format( 17, 19) # performs basic check after_lp, *_ = self.backend.loads_info() after_gp, *_ = self.backend.generators_info() after_ls = self.backend.get_line_status() assert self.compare_vect(init_lp, after_lp) # check i didn't modify the loads # assert self.compare_vect(init_gp, after_gp) # check i didn't modify the generators # TODO here problem with steady state, P=C+L assert np.all( disc | maintenance == ~after_ls ) # check i didn't disconnect any powerlines beside the correct one flows = self.backend.get_line_status() assert np.sum(~flows) == 2 assert not flows[19] assert not flows[17]
class TestTopoAction(unittest.TestCase): # Cette méthode sera appelée avant chaque test. def setUp(self): self.backend = PandaPowerBackend() self.path_matpower = PATH_DATA_TEST self.case_file = "test_case14.json" self.backend.load_grid(self.path_matpower, self.case_file) self.tolvect = 1e-2 self.tol_one = 1e-5 self.game_rules = GameRules() self.helper_action = HelperAction(name_prod=self.backend.name_prods, name_load=self.backend.name_loads, name_line=self.backend.name_lines, subs_info=self.backend.subs_elements, load_to_subid=self.backend.load_to_subid, gen_to_subid=self.backend.gen_to_subid, lines_or_to_subid=self.backend.lines_or_to_subid, lines_ex_to_subid=self.backend.lines_ex_to_subid, ##### load_to_sub_pos=self.backend.load_to_sub_pos, gen_to_sub_pos=self.backend.gen_to_sub_pos, lines_or_to_sub_pos=self.backend.lines_or_to_sub_pos, lines_ex_to_sub_pos=self.backend.lines_ex_to_sub_pos, ##### load_pos_topo_vect=self.backend.load_pos_topo_vect, gen_pos_topo_vect=self.backend.gen_pos_topo_vect, lines_or_pos_topo_vect=self.backend.lines_or_pos_topo_vect, lines_ex_pos_topo_vect=self.backend.lines_ex_pos_topo_vect, game_rules=self.game_rules) # Cette méthode sera appelée après chaque test. def tearDown(self): pass def compare_vect(self, pred, true): return np.max(np.abs(pred- true)) <= self.tolvect def test_topo_set1sub(self): # retrieve some initial data to be sure only a subpart of the _grid is modified conv = self.backend.runpf() init_amps_flow = self.backend.get_line_flow() # check that maintenance vector is properly taken into account arr = np.array([1, 1, 1, 2, 2, 2], dtype=np.int) id_=1 action = self.helper_action({"set_bus": {"substations_id": [(id_, arr)]}}) # apply the action here self.backend.apply_action(action) conv = self.backend.runpf() assert conv after_amps_flow = self.backend.get_line_flow() topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1, "all buses have been changed" assert np.max(topo_vect) == 2, "no buses have been changed" # check that the objects have been properly moved load_ids = np.where(self.backend.load_to_subid==id_)[0] assert np.all(topo_vect[self.backend.load_pos_topo_vect[load_ids]] == arr[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.lines_or_to_subid==id_)[0] assert np.all(topo_vect[self.backend.lines_or_pos_topo_vect[lor_ids]] == arr[self.backend.lines_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.lines_ex_to_subid==id_)[0] assert np.all(topo_vect[self.backend.lines_ex_pos_topo_vect[lex_ids]] == arr[self.backend.lines_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid==id_)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == arr[self.backend.gen_to_sub_pos[gen_ids]]) after_amps_flow_th = np.array([6.38865247e+02, 3.81726828e+02, 1.78001287e+04, 2.70742428e+04, 1.06755055e+04, 4.71160165e+03, 1.52265925e+04, 3.37755751e+02, 3.00535519e+02, 5.01164454e-13, 7.01900962e+01, 1.73874580e+02, 2.08904697e+04, 2.11757439e+04, 4.93863382e+04, 1.31935835e+02, 6.99779475e+01, 1.85068609e+02, 7.47283039e+02, 1.14125596e+03]) assert self.compare_vect(after_amps_flow, after_amps_flow_th) try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max(np.abs(p_subs)) <= self.tolvect, "problem with active values, at substation" assert np.max(np.abs(q_subs)) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten())) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten())) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass def test_topo_change1sub(self): # check that switching the bus of 3 object is equivalent to set them to bus 2 (as above) conv = self.backend.runpf() init_amps_flow = self.backend.get_line_flow() # check that maintenance vector is properly taken into account arr = np.array([False, False, False, True, True, True], dtype=np.bool) id_ = 1 action = self.helper_action({"change_bus": {"substations_id": [(id_, arr)]}}) # apply the action here self.backend.apply_action(action) # run the powerflow conv = self.backend.runpf() assert conv after_amps_flow = self.backend.get_line_flow() topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1, "all buses have been changed" assert np.max(topo_vect) == 2, "no buses have been changed" # check that the objects have been properly moved load_ids = np.where(self.backend.load_to_subid==id_)[0] assert np.all(topo_vect[self.backend.load_pos_topo_vect[load_ids]] == 1+arr[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.lines_or_to_subid==id_)[0] assert np.all(topo_vect[self.backend.lines_or_pos_topo_vect[lor_ids]] == 1+arr[self.backend.lines_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.lines_ex_to_subid==id_)[0] assert np.all(topo_vect[self.backend.lines_ex_pos_topo_vect[lex_ids]] == 1+arr[self.backend.lines_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid==id_)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == 1+arr[self.backend.gen_to_sub_pos[gen_ids]]) after_amps_flow_th = np.array([6.38865247e+02, 3.81726828e+02, 1.78001287e+04, 2.70742428e+04, 1.06755055e+04, 4.71160165e+03, 1.52265925e+04, 3.37755751e+02, 3.00535519e+02, 5.01164454e-13, 7.01900962e+01, 1.73874580e+02, 2.08904697e+04, 2.11757439e+04, 4.93863382e+04, 1.31935835e+02, 6.99779475e+01, 1.85068609e+02, 7.47283039e+02, 1.14125596e+03]) assert self.compare_vect(after_amps_flow, after_amps_flow_th) try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max(np.abs(p_subs)) <= self.tolvect, "problem with active values, at substation" assert np.max(np.abs(q_subs)) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten())) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten())) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass def test_topo_change_1sub_twice(self): # check that switching the bus of 3 object is equivalent to set them to bus 2 (as above) # and that setting it again is equivalent to doing nothing conv = self.backend.runpf() init_amps_flow = np.array([el for el in self.backend.get_line_flow()]) # check that maintenance vector is properly taken into account arr = np.array([False, False, False, True, True, True], dtype=np.bool) id_ = 1 action = self.helper_action({"change_bus": {"substations_id": [(id_, arr)]}}) # apply the action here self.backend.apply_action(action) conv = self.backend.runpf() assert conv after_amps_flow = self.backend.get_line_flow() topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1, "all buses have been changed" assert np.max(topo_vect) == 2, "no buses have been changed" # check that the objects have been properly moved load_ids = np.where(self.backend.load_to_subid == id_)[0] assert np.all( topo_vect[self.backend.load_pos_topo_vect[load_ids]] == 1+arr[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.lines_or_to_subid == id_)[0] assert np.all( topo_vect[self.backend.lines_or_pos_topo_vect[lor_ids]] == 1+arr[self.backend.lines_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.lines_ex_to_subid == id_)[0] assert np.all( topo_vect[self.backend.lines_ex_pos_topo_vect[lex_ids]] == 1+arr[self.backend.lines_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid == id_)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == 1+arr[self.backend.gen_to_sub_pos[gen_ids]]) after_amps_flow_th = np.array([6.38865247e+02, 3.81726828e+02, 1.78001287e+04, 2.70742428e+04, 1.06755055e+04, 4.71160165e+03, 1.52265925e+04, 3.37755751e+02, 3.00535519e+02, 5.01164454e-13, 7.01900962e+01, 1.73874580e+02, 2.08904697e+04, 2.11757439e+04, 4.93863382e+04, 1.31935835e+02, 6.99779475e+01, 1.85068609e+02, 7.47283039e+02, 1.14125596e+03]) assert self.compare_vect(after_amps_flow, after_amps_flow_th) try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max(np.abs(p_subs)) <= self.tolvect, "problem with active values, at substation" assert np.max(np.abs(q_subs)) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten())) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten())) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass action = self.helper_action({"change_bus": {"substations_id": [(id_, arr)]}}) # apply the action here self.backend.apply_action(action) conv = self.backend.runpf() assert conv after_amps_flow = self.backend.get_line_flow() assert self.compare_vect(after_amps_flow, init_amps_flow) topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1 assert np.max(topo_vect) == 1 try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max(np.abs(p_subs)) <= self.tolvect, "problem with active values, at substation" assert np.max(np.abs(q_subs)) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten())) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten())) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass def test_topo_change_2sub(self): # check that maintenance vector is properly taken into account 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)]}}) # apply the action here self.backend.apply_action(action) conv = self.backend.runpf() assert conv # check the _grid is correct topo_vect = self.backend.get_topo_vect() assert np.min(topo_vect) == 1, "all buses have been changed" assert np.max(topo_vect) == 2, "no buses have been changed" # check that the objects have been properly moved load_ids = np.where(self.backend.load_to_subid == id_1)[0] assert np.all( topo_vect[self.backend.load_pos_topo_vect[load_ids]] == 1+arr1[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.lines_or_to_subid == id_1)[0] assert np.all( topo_vect[self.backend.lines_or_pos_topo_vect[lor_ids]] == 1+arr1[self.backend.lines_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.lines_ex_to_subid == id_1)[0] assert np.all( topo_vect[self.backend.lines_ex_pos_topo_vect[lex_ids]] == 1+arr1[self.backend.lines_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid == id_1)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == 1+arr1[self.backend.gen_to_sub_pos[gen_ids]]) load_ids = np.where(self.backend.load_to_subid == id_2)[0] assert np.all( topo_vect[self.backend.load_pos_topo_vect[load_ids]] == arr2[self.backend.load_to_sub_pos[load_ids]]) lor_ids = np.where(self.backend.lines_or_to_subid == id_2)[0] assert np.all( topo_vect[self.backend.lines_or_pos_topo_vect[lor_ids]] == arr2[self.backend.lines_or_to_sub_pos[lor_ids]]) lex_ids = np.where(self.backend.lines_ex_to_subid == id_2)[0] assert np.all( topo_vect[self.backend.lines_ex_pos_topo_vect[lex_ids]] == arr2[self.backend.lines_ex_to_sub_pos[lex_ids]]) gen_ids = np.where(self.backend.gen_to_subid == id_2)[0] assert np.all(topo_vect[self.backend.gen_pos_topo_vect[gen_ids]] == arr2[self.backend.gen_to_sub_pos[gen_ids]]) after_amps_flow = self.backend.get_line_flow() after_amps_flow_th = np.array([6.06484526e+02, 3.53118362e+02, 1.31069171e+04, 2.91362693e+04, 1.81182785e+04, 1.36186919e+04, 1.36186919e+04, 3.26671053e+02, 2.79359713e+02, 4.86200936e-13, 7.92740141e+01, 1.87635540e+02, 2.82695392e+04, 3.02581716e+04, 3.25275006e-10, 1.21389438e+02, 6.44646825e+01, 1.43147411e+02, 7.56229924e+02, 1.07504986e+03]) assert self.compare_vect(after_amps_flow, after_amps_flow_th) try: p_subs, q_subs, p_bus, q_bus = self.backend.check_kirchoff() assert np.max(np.abs(p_subs)) <= self.tolvect, "problem with active values, at substation" assert np.max(np.abs(q_subs)) <= self.tolvect, "problem with reactive values, at substation" assert np.max(np.abs(p_bus.flatten())) <= self.tolvect, "problem with active values, at a bus" assert np.max(np.abs(q_bus.flatten())) <= self.tolvect, "problem with reaactive values, at a load" except Grid2OpException: pass