Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The ob_sp._template_obj obs contains absurd values but is used to check the bounds entered in functs in BoxGymObsSpace #665

Open
EBoguslawski opened this issue Nov 22, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@EBoguslawski
Copy link
Contributor

Environment

  • Grid2op version: 1.10.5
  • Python version: 3.12.1
  • System: ubuntu

Bug description

In the _get_info method of the BoxGymObsSpace class, the attribute _template_obj of the grid2op observation space is used to check the bounds indicated in the argument functs.
But on my computer, most of the attributes of _template_obj contain absurd values (wrong order of magnitude or wrong sign). This is problematic because if you compute an attribute entered in functs from an observation with absurd values, you obtain absurd values for the computed attribute that may not respect the bounds indicated in functs and it may result in an error.

How to reproduce

In this code, I compare the attributes of the _template_obj observation to the bounds indicated in BoxGymObsSpace._dict_properties that seem to be adequate (I only check attributes that have at least one finite bound).
I do it for 50 instances of environment, because the _template_obj observation randomly changes.
I print the attributes whose bounds have not been respected and attributes whose bounds have never been exceeded.

import grid2op
from grid2op.dtypes import dt_int, dt_float
from grid2op.gym_compat.utils import _compute_extra_power_for_losses
import numpy as np

env = grid2op.make("l2rpn_idf_2023")
ob_sp = env.observation_space

# Bounds
if ob_sp.obs_env is not None:
    tol_redisp = ob_sp.obs_env._tol_poly
else:
    tol_redisp = 1e-2
extra_for_losses = _compute_extra_power_for_losses(ob_sp)

dict_properties = {
            "year": (
                np.zeros(1, dtype=dt_int),
                np.zeros(1, dtype=dt_int) + 2200,
            ),
            "month": (
                np.zeros(1, dtype=dt_int),
                np.zeros(1, dtype=dt_int) + 12,
            ),
            "day": (
                np.zeros(1, dtype=dt_int),
                np.zeros(1, dtype=dt_int) + 31,
            ),
            "hour_of_day": (
                np.zeros(1, dtype=dt_int),
                np.zeros(1, dtype=dt_int) + 24,
            ),
            "minute_of_hour": (
                np.zeros(1, dtype=dt_int),
                np.zeros(1, dtype=dt_int) + 60,
            ),
            "day_of_week": (
                np.zeros(1, dtype=dt_int),
                np.zeros(1, dtype=dt_int) + 7,
            ),
            "current_step": (
                np.zeros(1, dtype=dt_int),
                np.zeros(1, dtype=dt_int) + np.iinfo(dt_int).max,
            ),
            "gen_p": (
                np.full(shape=(ob_sp.n_gen,), fill_value=0.0, dtype=dt_float)
                - tol_redisp
                - extra_for_losses,
                ob_sp.gen_pmax + tol_redisp + extra_for_losses,
            ),
            "gen_v": (
                np.full(shape=(ob_sp.n_gen,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_gen,), fill_value=np.inf, dtype=dt_float),
            ),
            "gen_margin_up": (
                np.full(shape=(ob_sp.n_gen,), fill_value=0.0, dtype=dt_float),
                1.0 * ob_sp.gen_max_ramp_up,
            ),
            "gen_margin_down": (
                np.full(shape=(ob_sp.n_gen,), fill_value=0.0, dtype=dt_float),
                1.0 * ob_sp.gen_max_ramp_down,
            ),
            "gen_theta": (
                np.full(shape=(ob_sp.n_gen,), fill_value=-180., dtype=dt_float),
                np.full(shape=(ob_sp.n_gen,), fill_value=180., dtype=dt_float),
            ),
            "load_p": (
                np.full(shape=(ob_sp.n_load,), fill_value=-np.inf, dtype=dt_float),
                np.full(shape=(ob_sp.n_load,), fill_value=+np.inf, dtype=dt_float),
            ),
            "load_q": (
                np.full(shape=(ob_sp.n_load,), fill_value=-np.inf, dtype=dt_float),
                np.full(shape=(ob_sp.n_load,), fill_value=+np.inf, dtype=dt_float),
            ),
            "load_v": (
                np.full(shape=(ob_sp.n_load,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_load,), fill_value=np.inf, dtype=dt_float),
            ),
            "load_theta": (
                np.full(shape=(ob_sp.n_load,), fill_value=-180., dtype=dt_float),
                np.full(shape=(ob_sp.n_load,), fill_value=180., dtype=dt_float),
            ),
            "p_or": (
                np.full(shape=(ob_sp.n_line,), fill_value=-np.inf, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "q_or": (
                np.full(shape=(ob_sp.n_line,), fill_value=-np.inf, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "a_or": (
                np.full(shape=(ob_sp.n_line,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "v_or": (
                np.full(shape=(ob_sp.n_line,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "theta_or": (
                np.full(shape=(ob_sp.n_line,), fill_value=-180., dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=180., dtype=dt_float),
            ),
            "p_ex": (
                np.full(shape=(ob_sp.n_line,), fill_value=-np.inf, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "q_ex": (
                np.full(shape=(ob_sp.n_line,), fill_value=-np.inf, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "a_ex": (
                np.full(shape=(ob_sp.n_line,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "v_ex": (
                np.full(shape=(ob_sp.n_line,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "theta_ex": (
                np.full(shape=(ob_sp.n_line,), fill_value=-180., dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=180., dtype=dt_float),
            ),
            "rho": (
                np.full(shape=(ob_sp.n_line,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "timestep_overflow": (
                np.full(
                    shape=(ob_sp.n_line,), fill_value=np.iinfo(dt_int).min, dtype=dt_int
                ),
                np.full(
                    shape=(ob_sp.n_line,), fill_value=np.iinfo(dt_int).max, dtype=dt_int
                ),
            ),
            "topo_vect": (
                np.full(shape=(ob_sp.dim_topo,), fill_value=-1, dtype=dt_int),
                np.full(shape=(ob_sp.dim_topo,), fill_value=ob_sp.n_busbar_per_sub, dtype=dt_int),
            ),
            "time_before_cooldown_line": (
                np.full(shape=(ob_sp.n_line,), fill_value=0, dtype=dt_int),
                np.full(
                    shape=(ob_sp.n_line,), fill_value=np.iinfo(dt_int).max, dtype=dt_int
                ),
            ),
            "time_before_cooldown_sub": (
                np.full(shape=(ob_sp.n_sub,), fill_value=0, dtype=dt_int),
                np.full(
                    shape=(ob_sp.n_sub,), fill_value=np.iinfo(dt_int).max, dtype=dt_int
                ),
            ),
            "time_next_maintenance": (
                np.full(shape=(ob_sp.n_line,), fill_value=-1, dtype=dt_int),
                np.full(
                    shape=(ob_sp.n_line,), fill_value=np.iinfo(dt_int).max, dtype=dt_int
                ),
            ),
            "duration_next_maintenance": (
                np.full(shape=(ob_sp.n_line,), fill_value=0, dtype=dt_int),
                np.full(
                    shape=(ob_sp.n_line,), fill_value=np.iinfo(dt_int).max, dtype=dt_int
                ),
            ),
            "target_dispatch": (
                np.minimum(ob_sp.gen_pmin, -ob_sp.gen_pmax),
                np.maximum(-ob_sp.gen_pmin, +ob_sp.gen_pmax),
            ),
            "actual_dispatch": (
                np.minimum(ob_sp.gen_pmin, -ob_sp.gen_pmax),
                np.maximum(-ob_sp.gen_pmin, +ob_sp.gen_pmax),
            ),
            "storage_charge": (
                np.full(shape=(ob_sp.n_storage,), fill_value=0, dtype=dt_float),
                1.0 * ob_sp.storage_Emax,
            ),
            "storage_power_target": (
                -1.0 * ob_sp.storage_max_p_prod,
                1.0 * ob_sp.storage_max_p_absorb,
            ),
            "storage_power": (
                -1.0 * ob_sp.storage_max_p_prod,
                1.0 * ob_sp.storage_max_p_absorb,
            ),
            "storage_theta": (
                np.full(shape=(ob_sp.n_storage,), fill_value=-180., dtype=dt_float),
                np.full(shape=(ob_sp.n_storage,), fill_value=180., dtype=dt_float),
            ),
            "curtailment": (
                np.full(shape=(ob_sp.n_gen,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_gen,), fill_value=1.0, dtype=dt_float),
            ),
            "curtailment_limit": (
                np.full(shape=(ob_sp.n_gen,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_gen,), fill_value=1.0, dtype=dt_float),
            ),
            "curtailment_mw": (
                np.full(shape=(ob_sp.n_gen,), fill_value=0.0, dtype=dt_float),
                1.0 * ob_sp.gen_pmax,
            ),
            "curtailment_limit_mw": (
                np.full(shape=(ob_sp.n_gen,), fill_value=0.0, dtype=dt_float),
                1.0 * ob_sp.gen_pmax,
            ),
            "thermal_limit": (
                np.full(shape=(ob_sp.n_line,), fill_value=0.0, dtype=dt_float),
                np.full(shape=(ob_sp.n_line,), fill_value=np.inf, dtype=dt_float),
            ),
            "time_since_last_alarm": (
                np.full(shape=(1,), fill_value=-1, dtype=dt_int),
                np.full(shape=(1,), fill_value=np.iinfo(dt_int).max, dtype=dt_int),
            ),
            "last_alarm": (
                np.full(shape=(ob_sp.dim_alarms,), fill_value=-1, dtype=dt_int),
                np.full(
                    shape=(ob_sp.dim_alarms,),
                    fill_value=np.iinfo(dt_int).max,
                    dtype=dt_int,
                ),
            ),
            "attention_budget": (
                np.full(shape=(1,), fill_value=-1, dtype=dt_float),
                np.full(shape=(1,), fill_value=np.inf, dtype=dt_float),
            ),
            "delta_time": (
                np.full(shape=(1,), fill_value=0, dtype=dt_float),
                np.full(shape=(1,), fill_value=np.inf, dtype=dt_float),
            ),
            # alert stuff
            "time_since_last_alert": (
                np.full(shape=(ob_sp.dim_alerts,), fill_value=-1, dtype=dt_int),
                np.full(shape=(ob_sp.dim_alerts,), fill_value=np.iinfo(dt_int).max, dtype=dt_int),
            ),
            "alert_duration": (
                np.full(shape=(ob_sp.dim_alerts,), fill_value=-1, dtype=dt_int),
                np.full(shape=(ob_sp.dim_alerts,), fill_value=np.iinfo(dt_int).max, dtype=dt_int),
            ),
            "total_number_of_alert": (
                np.full(shape=(1 if ob_sp.dim_alerts else 0,), fill_value=-1, dtype=dt_int),
                np.full(shape=(1 if ob_sp.dim_alerts else 0,), fill_value=np.iinfo(dt_int).max, dtype=dt_int),
            ),
            "time_since_last_attack": (
                np.full(shape=(ob_sp.dim_alerts,), fill_value=-1, dtype=dt_int),
                np.full(shape=(ob_sp.dim_alerts,), fill_value=np.iinfo(dt_int).max, dtype=dt_int),
            ),
            "was_alert_used_after_attack": (
                np.full(shape=(ob_sp.dim_alerts,), fill_value=-1, dtype=dt_int),
                np.full(shape=(ob_sp.dim_alerts,), fill_value=1, dtype=dt_int),
            ),
            "attack_under_alert": (
                np.full(shape=(ob_sp.dim_alerts,), fill_value=-1, dtype=dt_int),
                np.full(shape=(ob_sp.dim_alerts,), fill_value=1, dtype=dt_int),
            ),
        }

dict_properties["max_step"] = dict_properties["current_step"]
dict_properties["delta_time"] = dict_properties["current_step"]
dict_properties["prod_p"] = dict_properties["gen_p"]
dict_properties["prod_v"] = dict_properties["gen_v"]
dict_properties["gen_p_before_curtail"] = dict_properties["gen_p"]
dict_properties["curtailment_limit_effective"] = dict_properties["curtailment_limit"]


attributes_names = set(dict_properties.keys())
attr_with_a_problem = set() # I put an attribute here if at least one bound has been exceeded at least once
attr_without_a_problem = set(dict_properties.keys()) # I remove an attribute from here if at least one bound has been exceeded at least once

i = 0
while i < 50 and attr_without_a_problem != {}:
    env = grid2op.make("l2rpn_idf_2023")
    env.reset()
    obs_temp = env.observation_space._template_obj

    # I check only attributes which has not exceeded their bounds yet
    for attr_name in attr_without_a_problem:
        attr = getattr(obs_temp, attr_name)
        low = dict_properties[attr_name][0]
        high = dict_properties[attr_name][1]

        ids = np.where((attr < low) | (attr > high))[0]
        if ids.shape[0] > 0: # Case where at least a bound has been exceeded
            # I uppdate my set
            attr_with_a_problem.add(attr_name)
            # I print a value (the one with the lower index) that exceeded its bounds
            id0 = ids[0]
            print(f"The {attr_name} attribute is out of the bounds with index {id0}. Bounds : {low[id0]} <= {high[id0]}, value: {attr[id0]}.")

    # I uppdate my set
    attr_without_a_problem = attributes_names - attr_with_a_problem
    i+=1

print(f"Attributes with a problem: {attr_with_a_problem}")
print(f"Attributes without a problem: {attr_without_a_problem}")

Current output

The time_next_maintenance attribute is out of the bounds with index 0. Bounds : -1 <= 2147483647, value: -615714592.
The curtailment_limit_mw attribute is out of the bounds with index 0. Bounds : 0.0 <= 130.0, value: -7.498466991292809e+18.
The theta_ex attribute is out of the bounds with index 0. Bounds : -180.0 <= 180.0, value: -5.768051743208243e+16.
The curtailment_limit_effective attribute is out of the bounds with index 0. Bounds : 0.0 <= 1.0, value: -5.768051743208243e+16.
The load_theta attribute is out of the bounds with index 0. Bounds : -180.0 <= 180.0, value: 6.967259498760739e+19.
The v_or attribute is out of the bounds with index 4. Bounds : 0.0 <= inf, value: -2.6917968793305088e+17.
The prod_v attribute is out of the bounds with index 0. Bounds : 0.0 <= inf, value: -5.768161694371021e+16.
The topo_vect attribute is out of the bounds with index 0. Bounds : -1 <= 2, value: -615714176.
The gen_margin_down attribute is out of the bounds with index 0. Bounds : 0.0 <= 0.0, value: -5.768189182161715e+16.
The curtailment_mw attribute is out of the bounds with index 0. Bounds : 0.0 <= 130.0, value: -7.498466991292809e+18.
The target_dispatch attribute is out of the bounds with index 0. Bounds : -130.0 <= 130.0, value: -5.768051743208243e+16.
The storage_power_target attribute is out of the bounds with index 0. Bounds : -12.0 <= 12.0, value: 1.0434096265290193e+36.
The a_or attribute is out of the bounds with index 4. Bounds : 0.0 <= inf, value: -2.6924524631385702e+17.
The curtailment_limit attribute is out of the bounds with index 0. Bounds : 0.0 <= 1.0, value: -5.768051743208243e+16.
The storage_theta attribute is out of the bounds with index 0. Bounds : -180.0 <= 180.0, value: 1.0411552683928384e+36.
The total_number_of_alert attribute is out of the bounds with index 0. Bounds : -1 <= 2147483647, value: -615715808.
The gen_p_before_curtail attribute is out of the bounds with index 0. Bounds : -3156.010009765625 <= 3286.010009765625, value: -5.768051743208243e+16.
The gen_v attribute is out of the bounds with index 0. Bounds : 0.0 <= inf, value: -5.768161694371021e+16.
The gen_theta attribute is out of the bounds with index 0. Bounds : -180.0 <= 180.0, value: -5.768051743208243e+16.
The gen_margin_up attribute is out of the bounds with index 0. Bounds : 0.0 <= 0.0, value: -5.768161694371021e+16.
The theta_or attribute is out of the bounds with index 0. Bounds : -180.0 <= 180.0, value: -5.768051743208243e+16.
The attack_under_alert attribute is out of the bounds with index 0. Bounds : -1 <= 1, value: 106.
The curtailment attribute is out of the bounds with index 0. Bounds : 0.0 <= 1.0, value: -5.768051743208243e+16.
The attention_budget attribute is out of the bounds with index 0. Bounds : -1.0 <= inf, value: -171.52000427246094.
The actual_dispatch attribute is out of the bounds with index 0. Bounds : -130.0 <= 130.0, value: -5.768051743208243e+16.
The a_ex attribute is out of the bounds with index 0. Bounds : 0.0 <= inf, value: -5.768539651493069e+16.
The rho attribute is out of the bounds with index 0. Bounds : 0.0 <= inf, value: -5.768051743208243e+16.
The storage_power attribute is out of the bounds with index 0. Bounds : -12.0 <= 12.0, value: 2.783533808643751e+36.
The duration_next_maintenance attribute is out of the bounds with index 0. Bounds : 0 <= 2147483647, value: -615714592.
The was_alert_used_after_attack attribute is out of the bounds with index 0. Bounds : -1 <= 1, value: 2068385739.
The v_ex attribute is out of the bounds with index 0. Bounds : 0.0 <= inf, value: -5.76848467591168e+16.
The thermal_limit attribute is out of the bounds with index 0. Bounds : 0.0 <= inf, value: -5.768725194080256e+16.
The time_before_cooldown_line attribute is out of the bounds with index 0. Bounds : 0 <= 2147483647, value: -615714592.
The time_since_last_attack attribute is out of the bounds with index 4. Bounds : -1 <= 2147483647, value: -795569856.
The alert_duration attribute is out of the bounds with index 4. Bounds : -1 <= 2147483647, value: -16513.
Attributes with a problem: {'time_next_maintenance', 'curtailment_limit_mw', 'theta_ex', 'curtailment_limit_effective', 'load_theta', 'v_or', 'prod_v', 'topo_vect', 'gen_margin_down', 'curtailment_mw', 'target_dispatch', 'a_or', 'time_before_cooldown_line', 'curtailment_limit', 'time_since_last_attack', 'storage_theta', 'total_number_of_alert', 'gen_p_before_curtail', 'gen_v', 'gen_theta', 'gen_margin_up', 'theta_or', 'attack_under_alert', 'curtailment', 'attention_budget', 'actual_dispatch', 'a_ex', 'rho', 'storage_power', 'duration_next_maintenance', 'alert_duration', 'was_alert_used_after_attack', 'v_ex', 'thermal_limit', 'storage_power_target'}
Attributes without a problem: {'time_since_last_alert', 'minute_of_hour', 'last_alarm', 'prod_p', 'day', 'max_step', 'month', 'time_since_last_alarm', 'current_step', 'time_before_cooldown_sub', 'year', 'q_or', 'load_q', 'gen_p', 'p_or', 'hour_of_day', 'day_of_week', 'load_v', 'q_ex', 'delta_time', 'load_p', 'p_ex', 'storage_charge', 'timestep_overflow'}

Note : I also noticed absurd values for time_since_last_alert and time_since_last_alarm with manual tests even if they don't appear here. The code I propose to test attributes stays a randow way to do it.

Expected output

Attributes with a problem: {}
Attributes without a problem: {'time_next_maintenance', 'curtailment_limit_mw', 'theta_ex', 'curtailment_limit_effective', 'load_theta', 'v_or', 'prod_v', 'topo_vect', 'gen_margin_down', 'curtailment_mw', 'target_dispatch', 'a_or', 'time_before_cooldown_line', 'curtailment_limit', 'time_since_last_attack', 'storage_theta', 'total_number_of_alert', 'gen_p_before_curtail', 'gen_v', 'gen_theta', 'gen_margin_up', 'theta_or', 'attack_under_alert', 'curtailment', 'attention_budget', 'actual_dispatch', 'a_ex', 'rho', 'storage_power', 'duration_next_maintenance', 'alert_duration', 'was_alert_used_after_attack', 'v_ex', 'thermal_limit', 'storage_power_target', 'time_since_last_alert', 'minute_of_hour', 'last_alarm', 'prod_p', 'day', 'max_step', 'month', 'time_since_last_alarm', 'current_step', 'time_before_cooldown_sub', 'year', 'q_or', 'load_q', 'gen_p', 'p_or', 'hour_of_day', 'day_of_week', 'load_v', 'q_ex', 'delta_time', 'load_p', 'p_ex', 'storage_charge', 'timestep_overflow'}
@EBoguslawski EBoguslawski added the bug Something isn't working label Nov 22, 2024
BDonnot added a commit to BDonnot/Grid2Op that referenced this issue Nov 28, 2024
Signed-off-by: DONNOT Benjamin <[email protected]>
BDonnot added a commit that referenced this issue Nov 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant