def _retrieve_scores(self, path_tmp, episode_name): my_path = os.path.join(path_tmp, episode_name, EpisodeData.OTHER_REWARDS) with open(my_path, "r", encoding="utf-8") as f: dict_rewards = json.load(f) arr_ = np.array([dt_float(el[self.KEY_SCORE]) for el in dict_rewards]) self._save_numpy(os.path.join(path_tmp, episode_name, self.SCORES), arr_)
def init_from_dict(self, dict_): """ Initialize the object given a dictionary. All keys are optional. If a key is not present in the dictionary, the default parameters is used. Parameters ---------- dict_: ``dict`` The dictionary representing the parameters to load. """ if "NO_OVERFLOW_DISCONNECTION" in dict_: self.NO_OVERFLOW_DISCONNECTION = Parameters._isok_txt( dict_["NO_OVERFLOW_DISCONNECTION"]) if "IGNORE_MIN_UP_DOWN_TIME" in dict_: self.IGNORE_MIN_UP_DOWN_TIME = Parameters._isok_txt( dict_["IGNORE_MIN_UP_DOWN_TIME"]) if "ALLOW_DISPATCH_GEN_SWITCH_OFF" in dict_: self.ALLOW_DISPATCH_GEN_SWITCH_OFF = Parameters._isok_txt( dict_["ALLOW_DISPATCH_GEN_SWITCH_OFF"]) if "NB_TIMESTEP_POWERFLOW_ALLOWED" in dict_: self.NB_TIMESTEP_OVERFLOW_ALLOWED = dt_int( dict_["NB_TIMESTEP_POWERFLOW_ALLOWED"]) if "NB_TIMESTEP_OVERFLOW_ALLOWED" in dict_: self.NB_TIMESTEP_OVERFLOW_ALLOWED = dt_int( dict_["NB_TIMESTEP_OVERFLOW_ALLOWED"]) if "NB_TIMESTEP_RECONNECTION" in dict_: self.NB_TIMESTEP_RECONNECTION = dt_int( dict_["NB_TIMESTEP_RECONNECTION"]) if "HARD_OVERFLOW_THRESHOLD" in dict_: self.HARD_OVERFLOW_THRESHOLD = dt_float( dict_["HARD_OVERFLOW_THRESHOLD"]) if "ENV_DC" in dict_: self.ENV_DC = Parameters._isok_txt(dict_["ENV_DC"]) if "FORECAST_DC" in dict_: new_val = Parameters._isok_txt(dict_["FORECAST_DC"]) if new_val != self.FORECAST_DC: warnings.warn( "The FORECAST_DC attributes is deprecated. Please change the parameters of the " "\"forecast\" backend with \"env.change_forecast_parameters(new_param)\" function " "with \"new_param.ENV_DC=...\" ") self.FORECAST_DC = new_val if "MAX_SUB_CHANGED" in dict_: self.MAX_SUB_CHANGED = dt_int(dict_["MAX_SUB_CHANGED"]) if "MAX_LINE_STATUS_CHANGED" in dict_: self.MAX_LINE_STATUS_CHANGED = dt_int( dict_["MAX_LINE_STATUS_CHANGED"]) if "NB_TIMESTEP_TOPOLOGY_REMODIF" in dict_: # for backward compatibility (in case of old dataset) self.NB_TIMESTEP_COOLDOWN_SUB = dt_int( dict_["NB_TIMESTEP_TOPOLOGY_REMODIF"]) if "NB_TIMESTEP_COOLDOWN_SUB" in dict_: self.NB_TIMESTEP_COOLDOWN_SUB = dt_int( dict_["NB_TIMESTEP_COOLDOWN_SUB"]) if "NB_TIMESTEP_LINE_STATUS_REMODIF" in dict_: # for backward compatibility (in case of old dataset) self.NB_TIMESTEP_COOLDOWN_LINE = dt_int( dict_["NB_TIMESTEP_LINE_STATUS_REMODIF"]) if "NB_TIMESTEP_COOLDOWN_LINE" in dict_: self.NB_TIMESTEP_COOLDOWN_LINE = dt_int( dict_["NB_TIMESTEP_COOLDOWN_LINE"]) # storage parameters if "INIT_STORAGE_CAPACITY" in dict_: self.INIT_STORAGE_CAPACITY = dt_float( dict_["INIT_STORAGE_CAPACITY"]) if "ACTIVATE_STORAGE_LOSS" in dict_: self.ACTIVATE_STORAGE_LOSS = Parameters._isok_txt( dict_["ACTIVATE_STORAGE_LOSS"]) # alarm parameters if "ALARM_BEST_TIME" in dict_: self.ALARM_BEST_TIME = dt_int(dict_["ALARM_BEST_TIME"]) if "ALARM_WINDOW_SIZE" in dict_: self.ALARM_WINDOW_SIZE = dt_int(dict_["ALARM_WINDOW_SIZE"]) authorized_keys = set(self.__dict__.keys()) authorized_keys = authorized_keys | { 'NB_TIMESTEP_POWERFLOW_ALLOWED', 'NB_TIMESTEP_TOPOLOGY_REMODIF', "NB_TIMESTEP_LINE_STATUS_REMODIF" } ignored_keys = dict_.keys() - authorized_keys if len(ignored_keys): warnings.warn( "Parameters: The _parameters \"{}\" used to build the Grid2Op.Parameters " "class are not recognized and will be ignored.".format( ignored_keys))
def setUp(self): """ The case file is a representation of the case14 as found in the ieee14 powergrid. :return: """ self.tolvect = dt_float(1e-2) self.tol_one = dt_float(1e-5) self.max_iter = 10 self.real_reward = dt_float(199.99800) self.init_grid_path = os.path.join(PATH_DATA_TEST_PP, "test_case14.json") self.path_chron = PATH_ADN_CHRONICS_FOLDER self.parameters_path = None 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" }, } self.gridStateclass = Multifolder self.backendClass = PandaPowerBackend self.runner = Runner( init_grid_path=self.init_grid_path, path_chron=self.path_chron, parameters_path=self.parameters_path, names_chronics_to_backend=self.names_chronics_to_backend, gridStateclass=self.gridStateclass, backendClass=self.backendClass, rewardClass=L2RPNReward, other_rewards={"test": L2RPNReward}, max_iter=self.max_iter, name_env="test_episodedata_env")
def __init__(self, action_space): BaseActionBudget.__init__(self, action_space) self._zero = dt_float(0.)
def __init__(self, detailed_infos_for_cascading_failures=False): Backend.__init__(self, detailed_infos_for_cascading_failures= detailed_infos_for_cascading_failures) # lazy loading because it crashes... from grid2op.Backend import PandaPowerBackend from grid2op.Space import GridObjects # lazy import self.__has_storage = hasattr(GridObjects, "n_storage") if not self.__has_storage: pass # warnings.warn("Please upgrade your grid2Op to >= 1.5.0. You are using a backward compatibility " # "feature that will be removed in further lightsim2grid version.") self.nb_bus_total = None self.initdc = True # does not really hurt computation time self.__nb_powerline = None self.__nb_bus_before = None self._init_bus_load = None self._init_bus_gen = None self._init_bus_lor = None self._init_bus_lex = None self._big_topo_to_obj = None self.nb_obj_per_bus = None self.next_prod_p = None # this vector is updated with the action that will modify the environment # it is done to keep track of the redispatching self.topo_vect = None self.shunt_topo_vect = None self.init_pp_backend = PandaPowerBackend() self.V = None self.max_it = 10 self.tol = 1e-8 # tolerance for the solver self.prod_pu_to_kv = None self.load_pu_to_kv = None self.lines_or_pu_to_kv = None self.lines_ex_pu_to_kv = None self.p_or = None self.q_or = None self.v_or = None self.a_or = None self.p_ex = None self.q_ex = None self.v_ex = None self.a_ex = None self.load_p = None self.load_q = None self.load_v = None self.prod_p = None self.prod_q = None self.prod_v = None self.storage_p = None self.storage_q = None self.storage_v = None self.thermal_limit_a = None self._iref_slack = None self._id_bus_added = None self._fact_mult_gen = -1 self._what_object_where = None self._number_true_line = -1 self._corresp_name_fun = {} self._get_vector_inj = {} self.dim_topo = -1 self._init_action_to_set = None self._backend_action_class = None self.cst_1 = dt_float(1.0) self.__me_at_init = None self.__init_topo_vect = None # available solver in lightsim self.available_solvers = [] self.comp_time = 0. # computation time of just the powerflow self.__current_solver_type = None # hack for the storage unit: # in grid2op, for simplicity, I suppose that if a storage is alone on a busbar, and # that it produces / absorbs nothing, then that's fine # this behaviour in lightsim (c++ side) would be detected as a non connex grid and raise # a diverging powerflow # i "fake" to disconnect storage with these properties # TODO hummm we need to clarify that ! pandapower automatically disconnect this stuff too ! This is super weird # TODO and should rather be handled in pandapower backend # backend SHOULD not do these kind of stuff self._idx_hack_storage = []
def compare_vect(self, pred, true): return dt_float(np.max(np.abs(pred - true))) <= self.tolvect
def __init__(self, min_pen_lte=0.0, max_pen_gte=1.0): BaseReward.__init__(self) self.reward_min = dt_float(0.0) self.reward_max = dt_float(1.0) self.min_pen_lte = dt_float(min_pen_lte) self.max_pen_gte = dt_float(max_pen_gte)
def __init__(self, detailed_infos_for_cascading_failures=False): Backend.__init__(self, detailed_infos_for_cascading_failures= detailed_infos_for_cascading_failures) # lazy loading becuase otherwise somehow it crashes... from grid2op.Backend import PandaPowerBackend self.nb_bus_total = None self.initdc = True # does not really hurt computation time self.__nb_powerline = None self.__nb_bus_before = None self._init_bus_load = None self._init_bus_gen = None self._init_bus_lor = None self._init_bus_lex = None self._big_topo_to_obj = None self.nb_obj_per_bus = None self.next_prod_p = None # this vector is updated with the action that will modify the environment # it is done to keep track of the redispatching self.topo_vect = None self.shunt_topo_vect = None self.init_pp_backend = PandaPowerBackend() self.V = None self.max_it = 10 self.tol = 1e-8 # tolerance for the solver self.prod_pu_to_kv = None self.load_pu_to_kv = None self.lines_or_pu_to_kv = None self.lines_ex_pu_to_kv = None self.p_or = None self.q_or = None self.v_or = None self.a_or = None self.p_ex = None self.q_ex = None self.v_ex = None self.a_ex = None self.load_p = None self.load_q = None self.load_v = None self.prod_p = None self.prod_q = None self.prod_v = None self.thermal_limit_a = None self._iref_slack = None self._id_bus_added = None self._fact_mult_gen = -1 self._what_object_where = None self._number_true_line = -1 self._corresp_name_fun = {} self._get_vector_inj = {} self.dim_topo = -1 self._init_action_to_set = None self._backend_action_class = None self.cst_1 = dt_float(1.0) self.__me_at_init = None self.__init_topo_vect = None
def update(self, env, with_forecast=True): """ This use the environement to update properly the BaseObservation. Parameters ---------- env: :class:`grid2op.Environment.Environment` The environment from which to update this observation. """ # reset the matrices self._reset_matrices() self.reset() # extract the time stamps self.year = dt_int(env.time_stamp.year) self.month = dt_int(env.time_stamp.month) self.day = dt_int(env.time_stamp.day) self.hour_of_day = dt_int(env.time_stamp.hour) self.minute_of_hour = dt_int(env.time_stamp.minute) self.day_of_week = dt_int(env.time_stamp.weekday()) # get the values related to topology self.timestep_overflow = copy.copy(env.timestep_overflow) self.line_status = copy.copy(env.backend.get_line_status()) self.topo_vect = copy.copy(env.backend.get_topo_vect()) # get the values related to continuous values self.prod_p[:], self.prod_q[:], self.prod_v[:] = env.backend.generators_info( ) self.load_p[:], self.load_q[:], self.load_v[:] = env.backend.loads_info( ) self.p_or[:], self.q_or[:], self.v_or[:], self.a_or[:] = env.backend.lines_or_info( ) self.p_ex[:], self.q_ex[:], self.v_ex[:], self.a_ex[:] = env.backend.lines_ex_info( ) # handles forecasts here if with_forecast: inj_action = {} dict_ = {} dict_["load_p"] = dt_float(1.0 * self.load_p) dict_["load_q"] = dt_float(1.0 * self.load_q) dict_["prod_p"] = dt_float(1.0 * self.prod_p) dict_["prod_v"] = dt_float(1.0 * self.prod_v) inj_action["injection"] = dict_ # inj_action = self.action_helper(inj_action) timestamp = self.get_time_stamp() self._forecasted_inj = [(timestamp, inj_action)] self._forecasted_inj += env.chronics_handler.forecasts() self._forecasted_grid = [None for _ in self._forecasted_inj] self.rho = env.backend.get_relative_flow().astype(dt_float) # cool down and reconnection time after hard overflow, soft overflow or cascading failure self.time_before_cooldown_line[:] = env.times_before_line_status_actionable self.time_before_cooldown_sub[:] = env.times_before_topology_actionable self.time_next_maintenance[:] = env.time_next_maintenance self.duration_next_maintenance[:] = env.duration_next_maintenance # redispatching self.target_dispatch[:] = env.target_dispatch self.actual_dispatch[:] = env.actual_dispatch
def __init__(self, detailed_infos_for_cascading_failures=False): Backend.__init__(self, detailed_infos_for_cascading_failures= detailed_infos_for_cascading_failures) self.prod_pu_to_kv = None self.load_pu_to_kv = None self.lines_or_pu_to_kv = None self.lines_ex_pu_to_kv = None self.p_or = None self.q_or = None self.v_or = None self.a_or = None self.p_ex = None self.q_ex = None self.v_ex = None self.a_ex = None self.load_p = None self.load_q = None self.load_v = None self.prod_p = None self.prod_q = None self.prod_v = None self.line_status = None self._pf_init = "flat" self._pf_init = "results" self._nb_bus_before = None # number of active bus at the preceeding step self.thermal_limit_a = None self._iref_slack = None self._id_bus_added = None self._fact_mult_gen = -1 self._what_object_where = None self._number_true_line = -1 self._corresp_name_fun = {} self._get_vector_inj = {} self.dim_topo = -1 self._vars_action = BaseAction.attr_list_vect self._vars_action_set = BaseAction.attr_list_vect self.cst_1 = dt_float(1.0) self._topo_vect = None self.slack_id = None self.comp_time = 0. # function to rstore some information self.__nb_bus_before = None # number of substation in the powergrid self.__nb_powerline = None # number of powerline (real powerline, not transformer) self._init_bus_load = None self._init_bus_gen = None self._init_bus_lor = None self._init_bus_lex = None self._get_vector_inj = None self._big_topo_to_obj = None self._big_topo_to_backend = None self.__pp_backend_initial_state = None # initial state to facilitate the "reset" # Mapping some fun to apply bus updates self._type_to_bus_set = [ self._apply_load_bus, self._apply_gen_bus, self._apply_lor_bus, self._apply_trafo_hv, self._apply_lex_bus, self._apply_trafo_lv ]
def update(self, env, with_forecast=True): # reset the matrices self._reset_matrices() self.reset() # extract the time stamps self.year = dt_int(env.time_stamp.year) self.month = dt_int(env.time_stamp.month) self.day = dt_int(env.time_stamp.day) self.hour_of_day = dt_int(env.time_stamp.hour) self.minute_of_hour = dt_int(env.time_stamp.minute) self.day_of_week = dt_int(env.time_stamp.weekday()) # get the values related to topology self.timestep_overflow[:] = env._timestep_overflow self.line_status[:] = env.backend.get_line_status() self.topo_vect[:] = env.backend.get_topo_vect() # get the values related to continuous values self.prod_p[:], self.prod_q[:], self.prod_v[:] = env.backend.generators_info( ) self.load_p[:], self.load_q[:], self.load_v[:] = env.backend.loads_info( ) self.p_or[:], self.q_or[:], self.v_or[:], self.a_or[:] = env.backend.lines_or_info( ) self.p_ex[:], self.q_ex[:], self.v_ex[:], self.a_ex[:] = env.backend.lines_ex_info( ) # handles forecasts here if with_forecast: inj_action = {} dict_ = {} dict_["load_p"] = dt_float(1.0 * self.load_p) dict_["load_q"] = dt_float(1.0 * self.load_q) dict_["prod_p"] = dt_float(1.0 * self.prod_p) dict_["prod_v"] = dt_float(1.0 * self.prod_v) inj_action["injection"] = dict_ # inj_action = self.action_helper(inj_action) timestamp = self.get_time_stamp() self._forecasted_inj = [(timestamp, inj_action)] self._forecasted_inj += env.chronics_handler.forecasts() self._forecasted_grid = [None for _ in self._forecasted_inj] self.rho[:] = env.backend.get_relative_flow().astype(dt_float) # cool down and reconnection time after hard overflow, soft overflow or cascading failure self.time_before_cooldown_line[:] = env._times_before_line_status_actionable self.time_before_cooldown_sub[:] = env._times_before_topology_actionable self.time_next_maintenance[:] = env._time_next_maintenance self.duration_next_maintenance[:] = env._duration_next_maintenance # redispatching self.target_dispatch[:] = env._target_dispatch self.actual_dispatch[:] = env._actual_dispatch # handle shunts (if avaialble) if self.shunts_data_available: sh_p, sh_q, sh_v, sh_bus = env.backend.shunt_info() self._shunt_p[:] = sh_p self._shunt_q[:] = sh_q self._shunt_v[:] = sh_v self._shunt_bus[:] = sh_bus
def __init__(self, alpha_redisph=1.0): BaseReward.__init__(self) self.reward_min = dt_float(1.0) # carefull here between min and max... self.reward_max = dt_float(300.0 * 70.0) self.alpha_redisph = dt_float(alpha_redisph)
def __call__(self, action, env, has_error, is_done, is_illegal, is_ambiguous): if not has_error: res = dt_float(env._nb_time_step * self.per_timestep) else: res = self.reward_min return res
def _init_backend(self, init_grid_path, chronics_handler, backend, names_chronics_to_backend, actionClass, observationClass, rewardClass, legalActClass): """ INTERNAL .. warning:: /!\\\\ Internal, do not use unless you know what you are doing /!\\\\ Create a proper and valid environment. """ if not isinstance(rewardClass, type): raise Grid2OpException( "Parameter \"rewardClass\" used to build the Environment should be a type (a class) " "and not an object (an instance of a class). " "It is currently \"{}\"".format(type(rewardClass))) if not issubclass(rewardClass, BaseReward): raise Grid2OpException( "Parameter \"rewardClass\" used to build the Environment should derived form " "the grid2op.BaseReward class, type provided is \"{}\"".format( type(rewardClass))) self._rewardClass = rewardClass self._actionClass = actionClass self._observationClass = observationClass # backend self._init_grid_path = os.path.abspath(init_grid_path) if not isinstance(backend, Backend): raise Grid2OpException( "Parameter \"backend\" used to build the Environment should derived form the " "grid2op.Backend class, type provided is \"{}\"".format( type(backend))) self.backend = backend # all the above should be done in this exact order, otherwise some weird behaviour might occur # this is due to the class attribute self.backend.set_env_name(self.name) self.backend.load_grid( self._init_grid_path) # the real powergrid of the environment self.backend.load_redispacthing_data(self.get_path_env()) self.backend.load_storage_data(self.get_path_env()) self.backend.load_grid_layout(self.get_path_env()) self.backend.assert_grid_correct() self._has_been_initialized( ) # really important to include this piece of code! and just here after the # backend has loaded everything self._line_status = np.ones(shape=self.n_line, dtype=dt_bool) if self._thermal_limit_a is None: self._thermal_limit_a = self.backend.thermal_limit_a.astype( dt_float) else: self.backend.set_thermal_limit( self._thermal_limit_a.astype(dt_float)) *_, tmp = self.backend.generators_info() # rules of the game if not isinstance(legalActClass, type): raise Grid2OpException( "Parameter \"legalActClass\" used to build the Environment should be a type " "(a class) and not an object (an instance of a class). " "It is currently \"{}\"".format(type(legalActClass))) if not issubclass(legalActClass, BaseRules): raise Grid2OpException( "Parameter \"legalActClass\" used to build the Environment should derived form the " "grid2op.BaseRules class, type provided is \"{}\"".format( type(legalActClass))) self._game_rules = RulesChecker(legalActClass=legalActClass) self._legalActClass = legalActClass # action helper if not isinstance(actionClass, type): raise Grid2OpException( "Parameter \"actionClass\" used to build the Environment should be a type (a class) " "and not an object (an instance of a class). " "It is currently \"{}\"".format(type(legalActClass))) if not issubclass(actionClass, BaseAction): raise Grid2OpException( "Parameter \"actionClass\" used to build the Environment should derived form the " "grid2op.BaseAction class, type provided is \"{}\"".format( type(actionClass))) if not isinstance(observationClass, type): raise Grid2OpException( "Parameter \"actionClass\" used to build the Environment should be a type (a class) " "and not an object (an instance of a class). " "It is currently \"{}\"".format(type(legalActClass))) if not issubclass(observationClass, BaseObservation): raise Grid2OpException( "Parameter \"observationClass\" used to build the Environment should derived form the " "grid2op.BaseObservation class, type provided is \"{}\"". format(type(observationClass))) # action affecting the grid that will be made by the agent self._helper_action_class = ActionSpace.init_grid(gridobj=self.backend) self._helper_action_player = self._helper_action_class( gridobj=self.backend, actionClass=actionClass, legal_action=self._game_rules.legal_action) # action that affect the grid made by the environment. self._helper_action_env = self._helper_action_class( gridobj=self.backend, actionClass=CompleteAction, legal_action=self._game_rules.legal_action) self._helper_observation_class = ObservationSpace.init_grid( gridobj=self.backend) self._helper_observation = self._helper_observation_class( gridobj=self.backend, observationClass=observationClass, rewardClass=rewardClass, env=self) # handles input data if not isinstance(chronics_handler, ChronicsHandler): raise Grid2OpException( "Parameter \"chronics_handler\" used to build the Environment should derived form the " "grid2op.ChronicsHandler class, type provided is \"{}\"". format(type(chronics_handler))) self.chronics_handler = chronics_handler self.chronics_handler.initialize( self.name_load, self.name_gen, self.name_line, self.name_sub, names_chronics_to_backend=names_chronics_to_backend) self.names_chronics_to_backend = names_chronics_to_backend # test to make sure the backend is consistent with the chronics generator self.chronics_handler.check_validity(self.backend) self.delta_time_seconds = dt_float( self.chronics_handler.time_interval.seconds) self._reset_storage( ) # this should be called after the self.delta_time_seconds is set # reward function self._reward_helper = RewardHelper(self._rewardClass) self._reward_helper.initialize(self) for k, v in self.other_rewards.items(): v.initialize(self) # controller for voltage if not issubclass(self._voltagecontrolerClass, BaseVoltageController): raise Grid2OpException( "Parameter \"voltagecontrolClass\" should derive from \"ControlVoltageFromFile\"." ) self._voltage_controler = self._voltagecontrolerClass( gridobj=self.backend, controler_backend=self.backend) # create the opponent # At least the 3 following attributes should be set before calling _create_opponent self._create_opponent() # performs one step to load the environment properly (first action need to be taken at first time step after # first injections given) self._reset_maintenance() self._reset_redispatching() do_nothing = self._helper_action_env({}) *_, fail_to_start, info = self.step(do_nothing) if fail_to_start: raise Grid2OpException( "Impossible to initialize the powergrid, the powerflow diverge at iteration 0. " "Available information are: {}".format(info)) # test the backend returns object of the proper size self.backend.assert_grid_correct_after_powerflow() # for gym compatibility self.action_space = self._helper_action_player # this should be an action !!! self.observation_space = self._helper_observation # this return an observation. self.reward_range = self._reward_helper.range() self.viewer = None self.viewer_fig = None self.metadata = {'render.modes': []} self.spec = None self.current_reward = self.reward_range[0] self.done = False # reset everything to be consistent self._reset_vectors_and_timings()
def setUp(self): self.init_grid_path = os.path.join(PATH_DATA_TEST_PP, "test_case14.json") self.path_chron = PATH_ADN_CHRONICS_FOLDER self.parameters_path = None self.max_iter = 10 # self.real_reward = dt_float(199.99800) self.real_reward = dt_float(179.99818) self.all_real_rewards = [ 19.999783, 19.999786, 19.999784, 19.999794, 19.9998, 19.999804, 19.999804, 19.999817, 19.999823, 0. ] 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" }, } self.gridStateclass = Multifolder self.backendClass = PandaPowerBackend with warnings.catch_warnings(): warnings.filterwarnings( "ignore") # silence the warning about missing layout self.runner = Runner( init_grid_path=self.init_grid_path, path_chron=self.path_chron, parameters_path=self.parameters_path, names_chronics_to_backend=self.names_chronics_to_backend, gridStateclass=self.gridStateclass, backendClass=self.backendClass, rewardClass=L2RPNReward, max_iter=self.max_iter, name_env="test_runner_env")
def __init__(self, methodName='runTest'): unittest.TestCase.__init__(self, methodName=methodName) self.tolvect = dt_float(1e-2) self.tol_one = dt_float(1e-5)
def __init__(self): super().__init__() self.reward_min = dt_float(-1.0) self.reward_max = dt_float(1.0)
def __init__(self): BaseReward.__init__(self) self.reward_min = dt_float(0.0) self.reward_max = dt_float(1.0) self.penalty_max_at_n_lines = dt_float(2.0)
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.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() 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 initialize(self, env): self.reward_min = dt_float(0.0) self.reward_max = dt_float(env.backend.n_line)
def init_from_dict(self, dict_): """ Initialize the object given a dictionary. All keys are optional. If a key is not present in the dictionary, the default parameters is used. Parameters ---------- dict_: ``dict`` The dictionary representing the parameters to load. """ if "NO_OVERFLOW_DISCONNECTION" in dict_: self.NO_OVERFLOW_DISCONNECTION = Parameters._isok_txt( dict_["NO_OVERFLOW_DISCONNECTION"]) if "IGNORE_MIN_UP_DOWN_TIME" in dict_: self.IGNORE_MIN_UP_DOWN_TIME = Parameters._isok_txt( dict_["IGNORE_MIN_UP_DOWN_TIME"]) if "ALLOW_DISPATCH_GEN_SWITCH_OFF" in dict_: self.ALLOW_DISPATCH_GEN_SWITCH_OFF = Parameters._isok_txt( dict_["ALLOW_DISPATCH_GEN_SWITCH_OFF"]) if "NB_TIMESTEP_POWERFLOW_ALLOWED" in dict_: self.NB_TIMESTEP_OVERFLOW_ALLOWED = dt_int( dict_["NB_TIMESTEP_POWERFLOW_ALLOWED"]) if "NB_TIMESTEP_OVERFLOW_ALLOWED" in dict_: self.NB_TIMESTEP_OVERFLOW_ALLOWED = dt_int( dict_["NB_TIMESTEP_OVERFLOW_ALLOWED"]) if "NB_TIMESTEP_RECONNECTION" in dict_: self.NB_TIMESTEP_RECONNECTION = dt_int( dict_["NB_TIMESTEP_RECONNECTION"]) if "HARD_OVERFLOW_THRESHOLD" in dict_: self.HARD_OVERFLOW_THRESHOLD = dt_float( dict_["HARD_OVERFLOW_THRESHOLD"]) if "ENV_DC" in dict_: self.ENV_DC = Parameters._isok_txt(dict_["ENV_DC"]) if "FORECAST_DC" in dict_: self.FORECAST_DC = Parameters._isok_txt(dict_["FORECAST_DC"]) if "MAX_SUB_CHANGED" in dict_: self.MAX_SUB_CHANGED = dt_int(dict_["MAX_SUB_CHANGED"]) if "MAX_LINE_STATUS_CHANGED" in dict_: self.MAX_LINE_STATUS_CHANGED = dt_int( dict_["MAX_LINE_STATUS_CHANGED"]) if "NB_TIMESTEP_TOPOLOGY_REMODIF" in dict_: # for backward compatibility (in case of old dataset) self.NB_TIMESTEP_COOLDOWN_SUB = dt_int( dict_["NB_TIMESTEP_TOPOLOGY_REMODIF"]) if "NB_TIMESTEP_COOLDOWN_SUB" in dict_: self.NB_TIMESTEP_COOLDOWN_SUB = dt_int( dict_["NB_TIMESTEP_COOLDOWN_SUB"]) if "NB_TIMESTEP_LINE_STATUS_REMODIF" in dict_: # for backward compatibility (in case of old dataset) self.NB_TIMESTEP_COOLDOWN_LINE = dt_int( dict_["NB_TIMESTEP_LINE_STATUS_REMODIF"]) if "NB_TIMESTEP_COOLDOWN_LINE" in dict_: self.NB_TIMESTEP_COOLDOWN_LINE = dt_int( dict_["NB_TIMESTEP_COOLDOWN_LINE"]) authorized_keys = set(self.__dict__.keys()) authorized_keys = authorized_keys | { 'NB_TIMESTEP_POWERFLOW_ALLOWED', 'NB_TIMESTEP_TOPOLOGY_REMODIF', "NB_TIMESTEP_LINE_STATUS_REMODIF" } ignored_keys = dict_.keys() - authorized_keys if len(ignored_keys): warnings.warn( "Parameters: The _parameters \"{}\" used to build the Grid2Op.Parameters " "class are not recognized and will be ignored.".format( ignored_keys))
def __init__(self, max_lines=5): BaseReward.__init__(self) self.reward_min = dt_float(0.0) self.reward_max = dt_float(1.0) self.max_overflowed = dt_float(max_lines)
def __init__( self, init_grid_path: str, path_chron, # path where chronics of injections are stored name_env="unknown", parameters_path=None, names_chronics_to_backend=None, actionClass=TopologyAction, observationClass=CompleteObservation, rewardClass=FlatReward, legalActClass=AlwaysLegal, envClass=Environment, gridStateclass=GridStateFromFile, # type of chronics to use. For example GridStateFromFile if forecasts are not used, # or GridStateFromFileWithForecasts otherwise backendClass=PandaPowerBackend, agentClass=DoNothingAgent, # class used to build the agent agentInstance=None, verbose=False, gridStateclass_kwargs={}, voltageControlerClass=ControlVoltageFromFile, thermal_limit_a=None, max_iter=-1, other_rewards={}, opponent_action_class=DontAct, opponent_class=BaseOpponent, opponent_init_budget=0., opponent_budget_per_ts=0., opponent_budget_class=NeverAttackBudget, opponent_attack_duration=0, opponent_attack_cooldown=99999, opponent_kwargs={}, grid_layout=None, with_forecast=True, attention_budget_cls=LinearAttentionBudget, kwargs_attention_budget=None, has_attention_budget=False, # experimental: whether to read from local dir or generate the classes on the fly: _read_from_local_dir=False): """ Initialize the Runner. Parameters ---------- init_grid_path: ``str`` Madantory, used to initialize :attr:`Runner.init_grid_path`. path_chron: ``str`` Madantory where to look for chronics data, used to initialize :attr:`Runner.path_chron`. parameters_path: ``str`` or ``dict``, optional Used to initialize :attr:`Runner.parameters_path`. If it's a string, this will suppose parameters are located at this path, if it's a dictionary, this will use the parameters converted from this dictionary. names_chronics_to_backend: ``dict``, optional Used to initialize :attr:`Runner.names_chronics_to_backend`. actionClass: ``type``, optional Used to initialize :attr:`Runner.actionClass`. observationClass: ``type``, optional Used to initialize :attr:`Runner.observationClass`. rewardClass: ``type``, optional Used to initialize :attr:`Runner.rewardClass`. Default to :class:`grid2op.ConstantReward` that *should not** be used to train or evaluate an agent, but rather as debugging purpose. legalActClass: ``type``, optional Used to initialize :attr:`Runner.legalActClass`. envClass: ``type``, optional Used to initialize :attr:`Runner.envClass`. gridStateclass: ``type``, optional Used to initialize :attr:`Runner.gridStateclass`. backendClass: ``type``, optional Used to initialize :attr:`Runner.backendClass`. agentClass: ``type``, optional Used to initialize :attr:`Runner.agentClass`. agentInstance: :class:`grid2op.Agent.Agent` Used to initialize the agent. Note that either :attr:`agentClass` or :attr:`agentInstance` is used at the same time. If both ot them are ``None`` or both of them are "not ``None``" it throw an error. verbose: ``bool``, optional Used to initialize :attr:`Runner.verbose`. thermal_limit_a: ``numpy.ndarray`` The thermal limit for the environment (if any). voltagecontrolerClass: :class:`grid2op.VoltageControler.ControlVoltageFromFile`, optional The controler that will change the voltage setpoints of the generators. # TODO documentation on the opponent # TOOD doc for the attention budget """ self.with_forecast = with_forecast self.name_env = name_env if not isinstance(envClass, type): raise Grid2OpException( "Parameter \"envClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(envClass))) if not issubclass(envClass, Environment): raise RuntimeError( "Impossible to create a runner without an evnrionment derived from grid2op.Environement" " class. Please modify \"envClass\" parameter.") self.envClass = envClass if not isinstance(actionClass, type): raise Grid2OpException( "Parameter \"actionClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(actionClass))) if not issubclass(actionClass, BaseAction): raise RuntimeError( "Impossible to create a runner without an action class derived from grid2op.BaseAction. " "Please modify \"actionClass\" parameter.") self.actionClass = actionClass if not isinstance(observationClass, type): raise Grid2OpException( "Parameter \"observationClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(observationClass))) if not issubclass(observationClass, BaseObservation): raise RuntimeError( "Impossible to create a runner without an observation class derived from " "grid2op.BaseObservation. Please modify \"observationClass\" parameter." ) self.observationClass = observationClass if not isinstance(rewardClass, type): raise Grid2OpException( "Parameter \"rewardClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(rewardClass))) if not issubclass(rewardClass, BaseReward): raise RuntimeError( "Impossible to create a runner without an observation class derived from " "grid2op.BaseReward. Please modify \"rewardClass\" parameter.") self.rewardClass = rewardClass if not isinstance(gridStateclass, type): raise Grid2OpException( "Parameter \"gridStateclass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(gridStateclass))) if not issubclass(gridStateclass, GridValue): raise RuntimeError( "Impossible to create a runner without an chronics class derived from " "grid2op.GridValue. Please modify \"gridStateclass\" parameter." ) self.gridStateclass = gridStateclass if not isinstance(legalActClass, type): raise Grid2OpException( "Parameter \"legalActClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(legalActClass))) if not issubclass(legalActClass, BaseRules): raise RuntimeError( "Impossible to create a runner without a class defining legal actions derived " "from grid2op.BaseRules. Please modify \"legalActClass\" parameter." ) self.legalActClass = legalActClass if not isinstance(backendClass, type): raise Grid2OpException( "Parameter \"legalActClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(backendClass))) if not issubclass(backendClass, Backend): raise RuntimeError( "Impossible to create a runner without a backend class derived from grid2op.GridValue. " "Please modify \"backendClass\" parameter.") self.backendClass = backendClass self.__can_copy_agent = True if agentClass is not None: if agentInstance is not None: raise RuntimeError( "Impossible to build the backend. Only one of AgentClass or agentInstance can be " "used (both are not None).") if not isinstance(agentClass, type): raise Grid2OpException( "Parameter \"agentClass\" used to build the Runner should be a type (a class) and not an object " "(an instance of a class). It is currently \"{}\"".format( type(agentClass))) if not issubclass(agentClass, BaseAgent): raise RuntimeError( "Impossible to create a runner without an agent class derived from " "grid2op.BaseAgent. " "Please modify \"agentClass\" parameter.") self.agentClass = agentClass self._useclass = True self.agent = None elif agentInstance is not None: if not isinstance(agentInstance, BaseAgent): raise RuntimeError( "Impossible to create a runner without an agent class derived from " "grid2op.BaseAgent. " "Please modify \"agentInstance\" parameter.") self.agentClass = None self._useclass = False self.agent = agentInstance # Test if we can copy the agent for parallel runs try: copy.copy(self.agent) except: self.__can_copy_agent = False else: raise RuntimeError( "Impossible to build the backend. Either AgentClass or agentInstance must be provided " "and both are None.") self.agentInstance = agentInstance self._read_from_local_dir = _read_from_local_dir self.logger = ConsoleLog( DoNothingLog.INFO if verbose else DoNothingLog.ERROR) # store _parameters self.init_grid_path = init_grid_path self.names_chronics_to_backend = names_chronics_to_backend # game _parameters self.parameters_path = parameters_path if isinstance(parameters_path, str): self.parameters = Parameters(parameters_path) elif isinstance(parameters_path, dict): self.parameters = Parameters() self.parameters.init_from_dict(parameters_path) elif parameters_path is None: self.parameters = Parameters() else: raise RuntimeError( "Impossible to build the parameters. The argument \"parameters_path\" should either " "be a string or a dictionary.") # chronics of grid state self.path_chron = path_chron self.gridStateclass_kwargs = gridStateclass_kwargs self.max_iter = max_iter if max_iter > 0: self.gridStateclass_kwargs["max_iter"] = max_iter self.chronics_handler = ChronicsHandler( chronicsClass=self.gridStateclass, path=self.path_chron, **self.gridStateclass_kwargs) self.verbose = verbose self.thermal_limit_a = thermal_limit_a # controler for voltage if not issubclass(voltageControlerClass, ControlVoltageFromFile): raise Grid2OpException( "Parameter \"voltagecontrolClass\" should derive from \"ControlVoltageFromFile\"." ) self.voltageControlerClass = voltageControlerClass self._other_rewards = other_rewards # for opponent (should be defined here) after the initialization of BaseEnv if not issubclass(opponent_action_class, BaseAction): raise EnvError( "Impossible to make an environment with an opponent action class not " "derived from BaseAction") try: self.opponent_init_budget = dt_float(opponent_init_budget) except Exception as e: raise EnvError( "Impossible to convert \"opponent_init_budget\" to a float with error {}" .format(e)) if self.opponent_init_budget < 0.: raise EnvError( "If you want to deactive the opponent, please don't set its budget to a negative number." "Prefer the use of the DontAct action type (\"opponent_action_class=DontAct\" " "and / or set its budget to 0.") if not issubclass(opponent_class, BaseOpponent): raise EnvError( "Impossible to make an opponent with a type that does not inherit from BaseOpponent." ) self.opponent_action_class = opponent_action_class self.opponent_class = opponent_class self.opponent_init_budget = opponent_init_budget self.opponent_budget_per_ts = opponent_budget_per_ts self.opponent_budget_class = opponent_budget_class self.opponent_attack_duration = opponent_attack_duration self.opponent_attack_cooldown = opponent_attack_cooldown self.opponent_kwargs = opponent_kwargs self.grid_layout = grid_layout # attention budget self._attention_budget_cls = attention_budget_cls self._kwargs_attention_budget = copy.deepcopy(kwargs_attention_budget) self._has_attention_budget = has_attention_budget # otherwise on windows / macos it sometimes fail in the runner in multi process # on linux like OS i prefer to generate all the proper classes accordingly if _IS_LINUX: pass with warnings.catch_warnings(): warnings.filterwarnings("ignore") with self.init_env() as env: bk_class = type(env.backend) pass self.__used = False
def load_redispacthing_data(self, path, name='prods_charac.csv'): """ This method will load everything needed for the redispatching and unit commitment problem. Parameters ---------- path name Returns ------- """ # for redispatching fullpath = os.path.join(path, name) if not os.path.exists(fullpath): self.redispatching_unit_commitment_availble = False return try: df = pd.read_csv(fullpath) except Exception as e: return for el in ["type", "Pmax", "Pmin", "max_ramp_up", "max_ramp_down", "start_cost", "shut_down_cost", "marginal_cost", "min_up_time", "min_down_time"]: if el not in df.columns: return gen_info = {} for _, row in df.iterrows(): gen_info[row["name"]] = {"type": row["type"], "pmax": row["Pmax"], "pmin": row["Pmin"], "max_ramp_up": row["max_ramp_up"], "max_ramp_down": row["max_ramp_down"], "start_cost": row["start_cost"], "shut_down_cost": row["shut_down_cost"], "marginal_cost": row["marginal_cost"], "min_up_time": row["min_up_time"], "min_down_time": row["min_down_time"] } self.redispatching_unit_commitment_availble = True self.gen_type = np.full(self.n_gen, fill_value="aaaaaaaaaa") self.gen_pmin = np.full(self.n_gen, fill_value=1., dtype=dt_float) self.gen_pmax = np.full(self.n_gen, fill_value=1., dtype=dt_float) self.gen_redispatchable = np.full(self.n_gen, fill_value=False, dtype=dt_bool) self.gen_max_ramp_up = np.full(self.n_gen, fill_value=0., dtype=dt_float) self.gen_max_ramp_down = np.full(self.n_gen, fill_value=0., dtype=dt_float) self.gen_min_uptime = np.full(self.n_gen, fill_value=-1, dtype=dt_int) self.gen_min_downtime = np.full(self.n_gen, fill_value=-1, dtype=dt_int) self.gen_cost_per_MW = np.full(self.n_gen, fill_value=1., dtype=dt_float) # marginal cost self.gen_startup_cost = np.full(self.n_gen, fill_value=1., dtype=dt_float) # start cost self.gen_shutdown_cost = np.full(self.n_gen, fill_value=1., dtype=dt_float) # shutdown cost for i, gen_nm in enumerate(self.name_gen): tmp_gen = gen_info[gen_nm] self.gen_type[i] = str(tmp_gen["type"]) self.gen_pmin[i] = dt_float(tmp_gen["pmin"]) self.gen_pmax[i] = dt_float(tmp_gen["pmax"]) self.gen_redispatchable[i] = dt_bool(tmp_gen["type"] not in ["wind", "solar"]) tmp = dt_float(tmp_gen["max_ramp_up"]) if np.isfinite(tmp): self.gen_max_ramp_up[i] = tmp tmp = dt_float(tmp_gen["max_ramp_down"]) if np.isfinite(tmp): self.gen_max_ramp_down[i] = tmp self.gen_min_uptime[i] = dt_int(tmp_gen["min_up_time"]) self.gen_min_downtime[i] = dt_int(tmp_gen["min_down_time"]) self.gen_cost_per_MW[i] = dt_float(tmp_gen["marginal_cost"]) self.gen_startup_cost[i] = dt_float(tmp_gen["start_cost"]) self.gen_shutdown_cost[i] = dt_float(tmp_gen["shut_down_cost"])
def __init__(self, parameters_path=None): """ Build an object representing the _parameters of the game. Parameters ---------- parameters_path: ``str``, optional Path where to look for parameters. """ # if True, then it will not disconnect lines above their thermal limits self.NO_OVERFLOW_DISCONNECTION = False # number of timestep before powerline with an overflow is automatically disconnected self.NB_TIMESTEP_OVERFLOW_ALLOWED = dt_int(2) # number of timestep before a line can be reconnected if it has suffer a forced disconnection self.NB_TIMESTEP_RECONNECTION = dt_int(10) # number of timestep before a substation topology can be modified again self.NB_TIMESTEP_COOLDOWN_LINE = dt_int(0) self.NB_TIMESTEP_COOLDOWN_SUB = dt_int(0) # threshold above which a powerline is instantly disconnected by protections # this is expressed in relative value of the thermal limits # for example setting "HARD_OVERFLOW_THRESHOLD = 2" is equivalent, if a powerline has a thermal limit of # 243 A, to disconnect it instantly if it has a powerflow higher than 2 * 243 = 486 A self.HARD_OVERFLOW_THRESHOLD = dt_float(2.) # are the powerflow performed by the environment in DC mode (dc powerflow) or AC (ac powerflow) self.ENV_DC = False # same as above, but for the forecast states self.FORECAST_DC = False # DEPRECATED use "change_forecast_parameters(new_param)" with "new_param.ENV_DC=..." # maximum number of substations that can be change in one action self.MAX_SUB_CHANGED = dt_int(1) # maximum number of powerline status that can be changed in one action self.MAX_LINE_STATUS_CHANGED = dt_int(1) # ignore the min_uptime and downtime for the generators: allow them to be connected / disconnected # at will self.IGNORE_MIN_UP_DOWN_TIME = True # allow dispatch on turned off generator (if ``True`` you can actually dispatch a turned on geenrator) self.ALLOW_DISPATCH_GEN_SWITCH_OFF = True # storage capacity (NOT in pct) self.INIT_STORAGE_CAPACITY = 0.5 # do i take into account the storage loss in the step function self.ACTIVATE_STORAGE_LOSS = True if parameters_path is not None: if os.path.isfile(parameters_path): self.init_from_json(parameters_path) else: warn_msg = "Parameters: the file {} is not found. Continuing with default parameters." warnings.warn(warn_msg.format(parameters_path)) self.ALARM_BEST_TIME = 12 self.ALARM_WINDOW_SIZE = 12
def __init__(self): BaseReward.__init__(self) self.reward_min = dt_float(0.0) self.reward_max = dt_float(1.0)
def check_valid(self): """ check the parameter is valid (ie it checks that all the values are of correct types and within the correct range. Raises ------- An exception if the parameter is not valid """ try: if not isinstance(self.NO_OVERFLOW_DISCONNECTION, (bool, dt_bool)): raise RuntimeError( "NO_OVERFLOW_DISCONNECTION should be a boolean") self.NO_OVERFLOW_DISCONNECTION = dt_bool( self.NO_OVERFLOW_DISCONNECTION) except Exception as exc_: raise RuntimeError( f"Impossible to convert NO_OVERFLOW_DISCONNECTION to bool with error \n:\"{exc_}\"" ) try: self.NB_TIMESTEP_OVERFLOW_ALLOWED = int( self.NB_TIMESTEP_OVERFLOW_ALLOWED) # to raise if numpy array self.NB_TIMESTEP_OVERFLOW_ALLOWED = dt_int( self.NB_TIMESTEP_OVERFLOW_ALLOWED) except Exception as exc_: raise RuntimeError( f"Impossible to convert NB_TIMESTEP_OVERFLOW_ALLOWED to int with error \n:\"{exc_}\"" ) if self.NB_TIMESTEP_OVERFLOW_ALLOWED < 0: raise RuntimeError( "NB_TIMESTEP_OVERFLOW_ALLOWED < 0., this should be >= 0.") try: self.NB_TIMESTEP_RECONNECTION = int( self.NB_TIMESTEP_RECONNECTION) # to raise if numpy array self.NB_TIMESTEP_RECONNECTION = dt_int( self.NB_TIMESTEP_RECONNECTION) except Exception as exc_: raise RuntimeError( f"Impossible to convert NB_TIMESTEP_RECONNECTION to int with error \n:\"{exc_}\"" ) if self.NB_TIMESTEP_RECONNECTION < 0: raise RuntimeError( "NB_TIMESTEP_RECONNECTION < 0., this should be >= 0.") try: self.NB_TIMESTEP_COOLDOWN_LINE = int( self.NB_TIMESTEP_COOLDOWN_LINE) self.NB_TIMESTEP_COOLDOWN_LINE = dt_int( self.NB_TIMESTEP_COOLDOWN_LINE) except Exception as exc_: raise RuntimeError( f"Impossible to convert NB_TIMESTEP_COOLDOWN_LINE to int with error \n:\"{exc_}\"" ) if self.NB_TIMESTEP_COOLDOWN_LINE < 0: raise RuntimeError( "NB_TIMESTEP_COOLDOWN_LINE < 0., this should be >= 0.") try: self.NB_TIMESTEP_COOLDOWN_SUB = int( self.NB_TIMESTEP_COOLDOWN_SUB) # to raise if numpy array self.NB_TIMESTEP_COOLDOWN_SUB = dt_int( self.NB_TIMESTEP_COOLDOWN_SUB) except Exception as exc_: raise RuntimeError( f"Impossible to convert NB_TIMESTEP_COOLDOWN_SUB to int with error \n:\"{exc_}\"" ) if self.NB_TIMESTEP_COOLDOWN_SUB < 0: raise RuntimeError( "NB_TIMESTEP_COOLDOWN_SUB < 0., this should be >= 0.") try: self.HARD_OVERFLOW_THRESHOLD = float( self.HARD_OVERFLOW_THRESHOLD) # to raise if numpy array self.HARD_OVERFLOW_THRESHOLD = dt_float( self.HARD_OVERFLOW_THRESHOLD) except Exception as exc_: raise RuntimeError( f"Impossible to convert HARD_OVERFLOW_THRESHOLD to float with error \n:\"{exc_}\"" ) if self.HARD_OVERFLOW_THRESHOLD < 1.: raise RuntimeError( "HARD_OVERFLOW_THRESHOLD < 1., this should be >= 1. (use env.set_thermal_limit " "to modify the thermal limit)") try: if not isinstance(self.ENV_DC, (bool, dt_bool)): raise RuntimeError( "NO_OVERFLOW_DISCONNECTION should be a boolean") self.ENV_DC = dt_bool(self.ENV_DC) except Exception as exc_: raise RuntimeError( f"Impossible to convert ENV_DC to bool with error \n:\"{exc_}\"" ) try: self.MAX_SUB_CHANGED = int( self.MAX_SUB_CHANGED) # to raise if numpy array self.MAX_SUB_CHANGED = dt_int(self.MAX_SUB_CHANGED) except Exception as exc_: raise RuntimeError( f"Impossible to convert MAX_SUB_CHANGED to int with error \n:\"{exc_}\"" ) if self.MAX_SUB_CHANGED < 0: raise RuntimeError( "MAX_SUB_CHANGED should be >=0 (or -1 if you want to be able to change every " "substation at once)") try: self.MAX_LINE_STATUS_CHANGED = int( self.MAX_LINE_STATUS_CHANGED) # to raise if numpy array self.MAX_LINE_STATUS_CHANGED = dt_int(self.MAX_LINE_STATUS_CHANGED) except Exception as exc_: raise RuntimeError( f"Impossible to convert MAX_LINE_STATUS_CHANGED to int with error \n:\"{exc_}\"" ) if self.MAX_LINE_STATUS_CHANGED < 0: raise RuntimeError( "MAX_LINE_STATUS_CHANGED should be >=0 " "(or -1 if you want to be able to change every powerline at once)" ) try: if not isinstance(self.IGNORE_MIN_UP_DOWN_TIME, (bool, dt_bool)): raise RuntimeError( "IGNORE_MIN_UP_DOWN_TIME should be a boolean") self.IGNORE_MIN_UP_DOWN_TIME = dt_bool( self.IGNORE_MIN_UP_DOWN_TIME) except Exception as exc_: raise RuntimeError( f"Impossible to convert IGNORE_MIN_UP_DOWN_TIME to bool with error \n:\"{exc_}\"" ) try: if not isinstance(self.ALLOW_DISPATCH_GEN_SWITCH_OFF, (bool, dt_bool)): raise RuntimeError( "ALLOW_DISPATCH_GEN_SWITCH_OFF should be a boolean") self.ALLOW_DISPATCH_GEN_SWITCH_OFF = dt_bool( self.ALLOW_DISPATCH_GEN_SWITCH_OFF) except Exception as exc_: raise RuntimeError( f"Impossible to convert ALLOW_DISPATCH_GEN_SWITCH_OFF to bool with error \n:\"{exc_}\"" ) try: self.INIT_STORAGE_CAPACITY = float( self.INIT_STORAGE_CAPACITY) # to raise if numpy array self.INIT_STORAGE_CAPACITY = dt_float(self.INIT_STORAGE_CAPACITY) except Exception as exc_: raise RuntimeError( f"Impossible to convert INIT_STORAGE_CAPACITY to float with error \n:\"{exc_}\"" ) if self.INIT_STORAGE_CAPACITY < 0.: raise RuntimeError( "INIT_STORAGE_CAPACITY < 0., this should be within range [0., 1.]" ) if self.INIT_STORAGE_CAPACITY > 1.: raise RuntimeError( "INIT_STORAGE_CAPACITY > 1., this should be within range [0., 1.]" ) try: if not isinstance(self.ACTIVATE_STORAGE_LOSS, (bool, dt_bool)): raise RuntimeError("ACTIVATE_STORAGE_LOSS should be a boolean") self.ACTIVATE_STORAGE_LOSS = dt_bool(self.ACTIVATE_STORAGE_LOSS) except Exception as exc_: raise RuntimeError( f"Impossible to convert ACTIVATE_STORAGE_LOSS to bool with error \n:\"{exc_}\"" ) try: self.ALARM_WINDOW_SIZE = dt_int(self.ALARM_WINDOW_SIZE) except Exception as exc_: raise RuntimeError( f"Impossible to convert ALARM_WINDOW_SIZE to int with error \n:\"{exc_}\"" ) try: self.ALARM_BEST_TIME = dt_int(self.ALARM_BEST_TIME) except Exception as exc_: raise RuntimeError( f"Impossible to convert ALARM_BEST_TIME to int with error \n:\"{exc_}\"" ) if self.ALARM_WINDOW_SIZE <= 0: raise RuntimeError( "self.ALARM_WINDOW_SIZE should be a positive integer !") if self.ALARM_BEST_TIME <= 0: raise RuntimeError( "self.ALARM_BEST_TIME should be a positive integer !")
def __call__(self, action, env, has_error, is_done, is_illegal, is_ambiguous): return dt_float(0.0)
def __init__(self, per_timestep=1): BaseReward.__init__(self) self.per_timestep = dt_float(per_timestep) self.reward_min = dt_float(0.0) self.reward_max = dt_float(per_timestep)
def update(self, env, with_forecast=True): # reset the matrices self._reset_matrices() self.reset() # counter self.current_step = env.nb_time_step self.max_step = env.max_episode_duration() # extract the time stamps self.year = dt_int(env.time_stamp.year) self.month = dt_int(env.time_stamp.month) self.day = dt_int(env.time_stamp.day) self.hour_of_day = dt_int(env.time_stamp.hour) self.minute_of_hour = dt_int(env.time_stamp.minute) self.day_of_week = dt_int(env.time_stamp.weekday()) # get the values related to topology self.timestep_overflow[:] = env._timestep_overflow self.line_status[:] = env.backend.get_line_status() self.topo_vect[:] = env.backend.get_topo_vect() # get the values related to continuous values self.gen_p[:], self.gen_q[:], self.gen_v[:] = env.backend.generators_info( ) self.load_p[:], self.load_q[:], self.load_v[:] = env.backend.loads_info( ) self.p_or[:], self.q_or[:], self.v_or[:], self.a_or[:] = env.backend.lines_or_info( ) self.p_ex[:], self.q_ex[:], self.v_ex[:], self.a_ex[:] = env.backend.lines_ex_info( ) # storage units self.storage_charge[:] = env._storage_current_charge self.storage_power_target[:] = env._action_storage self.storage_power[:] = env._storage_power # handles forecasts here if with_forecast: inj_action = {} dict_ = {} dict_["load_p"] = dt_float(1.0 * self.load_p) dict_["load_q"] = dt_float(1.0 * self.load_q) dict_["prod_p"] = dt_float(1.0 * self.gen_p) dict_["prod_v"] = dt_float(1.0 * self.gen_v) inj_action["injection"] = dict_ # inj_action = self.action_helper(inj_action) timestamp = self.get_time_stamp() self._forecasted_inj = [(timestamp, inj_action)] self._forecasted_inj += env.chronics_handler.forecasts() self._forecasted_grid = [None for _ in self._forecasted_inj] self.rho[:] = env.backend.get_relative_flow().astype(dt_float) # cool down and reconnection time after hard overflow, soft overflow or cascading failure self.time_before_cooldown_line[:] = env._times_before_line_status_actionable self.time_before_cooldown_sub[:] = env._times_before_topology_actionable self.time_next_maintenance[:] = env._time_next_maintenance self.duration_next_maintenance[:] = env._duration_next_maintenance # redispatching self.target_dispatch[:] = env._target_dispatch self.actual_dispatch[:] = env._actual_dispatch # handle shunts (if avaialble) if self.shunts_data_available: sh_p, sh_q, sh_v, sh_bus = env.backend.shunt_info() self._shunt_p[:] = sh_p self._shunt_q[:] = sh_q self._shunt_v[:] = sh_v self._shunt_bus[:] = sh_bus self._thermal_limit[:] = env.get_thermal_limit() if self.redispatching_unit_commitment_availble: self.gen_p_before_curtail[:] = env._gen_before_curtailment self.curtailment[:] = (self.gen_p_before_curtail - self.gen_p) / self.gen_pmax self.curtailment[~self.gen_renewable] = 0. self.curtailment_limit[:] = env._limit_curtailment self.curtailment_limit[self.curtailment_limit >= 1.] = 1.0 else: self.curtailment[:] = 0. self.gen_p_before_curtail[:] = self.gen_p self.curtailment_limit[:] = 1.0 if env.backend.can_output_theta: self.support_theta = True # backend supports the computation of theta self.theta_or[:], self.theta_ex[:], self.load_theta[:], self.gen_theta[:], self.storage_theta[:] = \ env.backend.get_theta() if self.dim_alarms and env._has_attention_budget: self.is_alarm_illegal[:] = env._is_alarm_illegal if env._attention_budget.time_last_successful_alarm_raised > 0: self.time_since_last_alarm[:] = self.current_step - env._attention_budget.time_last_successful_alarm_raised else: self.time_since_last_alarm[:] = -1 self.last_alarm[:] = env._attention_budget.last_successful_alarm_raised self.attention_budget[:] = env._attention_budget.current_budget