Observation

This page is organized as follow:

Objectives

In a “reinforcement learning” framework, an grid2op.Agent receive two information before taking any action on the grid2op.Environment.Environment. One of them is the grid2op.Reward.BaseReward that tells it how well the past action performed. The second main input received from the environment is the BaseObservation. This is gives the BaseAgent partial, noisy, or complete information about the current state of the environment. This module implement a generic BaseObservation class and an example of a complete observation in the case of the Learning To Run a Power Network (L2RPN ) competition.

Compared to other Reinforcement Learning problems the L2PRN competition allows another flexibility. Today, when operating a powergrid, operators have “forecasts” at their disposal. We wanted to make them available in the L2PRN competition too. In the first edition of the L2PRN competition, was offered the functionality to simulate the effect of an action on a forecasted powergrid. This forecasted powergrid used:

  • the topology of the powergrid of the last know time step

  • all the injections of given in files.

This functionality was originally attached to the Environment and could only be used to simulate the effect of an action on this unique time step. We wanted in this recoding to change that:

  • in an RL setting, an grid2op.Agent.BaseAgent should not be able to look directly at the grid2op.Environment.Environment. The only information about the Environment the BaseAgent should have is through the grid2op.Observation.BaseObservation and the grid2op.Reward.BaseReward. Having this principle implemented will help enforcing this principle.

  • In some wider context, it is relevant to have these forecasts available in multiple way, or modified by the grid2op.Agent.BaseAgent itself (for example having forecast available for the next 2 or 3 hours, with the Agent able not only to change the topology of the powergrid with actions, but also the injections if he’s able to provide more accurate predictions for example.

The BaseObservation class implement the two above principles and is more flexible to other kind of forecasts, or other methods to build a power grid based on the forecasts of injections.

Main observation attributes

In general, observations have the following attributes (if an attributes has name XXX [eg rho] it can be accessed with obs.XXX [eg obs.rho])

Name(s)

Type

Size (each)

year, month, day, hour_of_day, minute_of_hour, day_of_week

int

1

gen_p, gen_q, gen_v, gen_theta

float

n_gen

load_p, load_q, load_v , load_theta

float

n_load

p_or, q_or, v_or, a_or, theta_or

float

n_line

p_ex, q_ex, v_ex, a_ex, theta_ex

float

n_line

rho

float

n_line

topo_vect

int

dim_topo

line_status

bool

n_line

timestep_overflow

int

n_line

time_before_cooldown_line

int

n_line

time_before_cooldown_sub

int

n_sub

time_next_maintenance

int

n_line

duration_next_maintenance

int

n_line

target_dispatch

float

n_gen

actual_dispatch

float

n_gen

storage_charge

float

n_storage

storage_power_target

float

n_storage

storage_power

float

n_storage

storage_theta

float

n_storage

gen_p_before_curtail

float

n_gen

curtailment_mw, curtailment, curtailment_limit

float

n_gen

gen_margin_up, gen_margin_down

float

n_gen

is_alarm_illegal

bool

1

time_since_last_alarm

int

1

last_alarm

int

dim_alarms

attention_budget

int

1

max_step , current_step

int

1

delta_time

float

1

total_number_of_alert ,

int

dim_alerts

was_alert_used_after_attack , attack_under_alert

int

dim_alerts

time_since_last_alert , alert_duration , time_since_last_attack

int

dim_alerts

alert_duration

bool

dim_alerts

(NB for concision, if a coma (”,”) is present in the “Name(s)” part of the column, it means multiple attributes are present. If we take the example of the first row, it means that obs.year, obs.month, etc. are all valid attributes of the observation, they are all integers and each is of size 1.)

But where is the graph ?

A powergrid can be represented as (at least) a graph (here: a mathematical object with nodes / vertices are connected by edges).

Grid2op is made in a way that the observation and action do not explicitly represents such graph. This is motivated first by performance reasons, but also because multiple “graphs” can represent equally well a powergrid.

The first one that come to mind is the graph where the nodes / vertices are the buses and the edges are the powerline. We will call this graph the “bus graph”. It can be accessed in grid2op using the grid2op.Observation.BaseObservation.bus_connectivity_matrix() for example. This will return a matrix with 1 when 2 buses are connected and 0 otherwise, with the convention that a bus is always connected to itself. You can think of the environments in grid2op as an environment that allows you to manipulate this graph: split some bus in sub buses by changing at which busbar some elements are connected, or removing some edges from this graph when powerlines are connected / disconnected. An important feature of this graph is that its size changes: it can have a different number of nodes at different steps!

Some methods allow to retrieve these graphs, for example:

For more information, you can consult the A grid, a graph: grid2op representation of the powergrid page.

Detailed Documentation by class

Classes:

BaseObservation([obs_env, action_helper, ...])

Basic class representing an observation.

CompleteObservation([obs_env, ...])

This class represent a complete observation, where everything on the powergrid can be observed without any noise.

HighResSimCounter()

This classes helps to count the total number of call to "high fidelity simulator" the agent made.

NoisyObservation([obs_env, action_helper, ...])

This class represent a complete observation (in the sens that all attributes of an CompleteObservation are accessible) but some of them are "noisy".

ObservationSpace(gridobj, env[, ...])

Helper that provides useful functions to manipulate BaseObservation.

class grid2op.Observation.BaseObservation(obs_env=None, action_helper=None, random_prng=None, kwargs_env=None)[source]

Basic class representing an observation.

All observation must derive from this class and implement all its abstract methods.

action_helper

A representation of the possible action space.

Type:

grid2op.Action.ActionSpace

year

The current year

Type:

int

month

The current month (1 = january, 12 = december)

Type:

int

day

The current day of the month (1 = first day of the month)

Type:

int

hour_of_day

The current hour of the day (from O to 23)

Type:

int

minute_of_hour

The current minute of the current hour (from 0 to 59)

Type:

int

day_of_week

The current day of the week (monday = 0 and sunday = 6)

Type:

int

support_theta

This flag indicates whether the backend supports the retrieval of the voltage angle. If so (which is the case for most backend) then some supplementary attributes are available, such as BaseObservation.gen_theta, BaseObservation.load_theta, BaseObservation.storage_theta, BaseObservation.theta_or or BaseObservation.theta_ex .

Type:

bool

gen_p

The active production value of each generator (expressed in MW). (the old name “prod_p” is still usable)

Type:

numpy.ndarray, dtype:float

gen_q

The reactive production value of each generator (expressed in MVar). (the old name “prod_q” is still usable)

Type:

numpy.ndarray, dtype:float

gen_v

The voltage magnitude of the bus to which each generator is connected (expressed in kV). (the old name “prod_v” is still usable)

Type:

numpy.ndarray, dtype:float

gen_theta

The voltage angle (in degree) of the bus to which each generator is connected. Only availble if the backend supports the retrieval of voltage angles (see BaseObservation.support_theta).

Type:

numpy.ndarray, dtype:float

load_p

The active load value of each consumption (expressed in MW).

Type:

numpy.ndarray, dtype:float

load_q

The reactive load value of each consumption (expressed in MVar).

Type:

numpy.ndarray, dtype:float

load_v

The voltage magnitude of the bus to which each consumption is connected (expressed in kV).

Type:

numpy.ndarray, dtype:float

load_theta

The voltage angle (in degree) of the bus to which each consumption is connected. Only availble if the backend supports the retrieval of voltage angles (see BaseObservation.support_theta).

Type:

numpy.ndarray, dtype:float

p_or

The active power flow at the origin side of each powerline (expressed in MW).

Type:

numpy.ndarray, dtype:float

q_or

The reactive power flow at the origin side of each powerline (expressed in MVar).

Type:

numpy.ndarray, dtype:float

v_or

The voltage magnitude at the bus to which the origin side of each powerline is connected (expressed in kV).

Type:

numpy.ndarray, dtype:float

theta_or

The voltage angle at the bus to which the origin side of each powerline is connected (expressed in degree). Only availble if the backend supports the retrieval of voltage angles (see BaseObservation.support_theta).

Type:

numpy.ndarray, dtype:float

a_or

The current flow at the origin side of each powerline (expressed in A).

Type:

numpy.ndarray, dtype:float

p_ex

The active power flow at the extremity side of each powerline (expressed in MW).

Type:

numpy.ndarray, dtype:float

q_ex

The reactive power flow at the extremity side of each powerline (expressed in MVar).

Type:

numpy.ndarray, dtype:float

v_ex

The voltage magnitude at the bus to which the extremity side of each powerline is connected (expressed in kV).

Type:

numpy.ndarray, dtype:float

theta_ex

The voltage angle at the bus to which the extremity side of each powerline is connected (expressed in degree). Only availble if the backend supports the retrieval of voltage angles (see BaseObservation.support_theta).

Type:

numpy.ndarray, dtype:float

a_ex

The current flow at the extremity side of each powerline (expressed in A).

Type:

numpy.ndarray, dtype:float

rho

The capacity of each powerline. It is defined at the observed current flow divided by the thermal limit of each powerline (no unit)

Type:

numpy.ndarray, dtype:float

topo_vect

For each object (load, generator, ends of a powerline) it gives on which bus this object is connected in its substation. See grid2op.Backend.Backend.get_topo_vect() for more information.

Type:

numpy.ndarray, dtype:int

line_status

Gives the status (connected / disconnected) for every powerline (True at position i means the powerline i is connected)

Type:

numpy.ndarray, dtype:bool

timestep_overflow

Gives the number of time steps since a powerline is in overflow.

Type:

numpy.ndarray, dtype:int

time_before_cooldown_line

For each powerline, it gives the number of time step the powerline is unavailable due to “cooldown” (see grid2op.Parameters.Parameters.NB_TIMESTEP_COOLDOWN_LINE for more information). 0 means the an action will be able to act on this same powerline, a number > 0 (eg 1) means that an action at this time step cannot act on this powerline (in the example the agent have to wait 1 time step)

Type:

numpy.ndarray, dtype:int

time_before_cooldown_sub

Same as BaseObservation.time_before_cooldown_line but for substations. For each substation, it gives the number of timesteps to wait before acting on this substation (see see grid2op.Parameters.Parameters.NB_TIMESTEP_COOLDOWN_SUB for more information).

Type:

numpy.ndarray, dtype:int

time_next_maintenance

For each powerline, it gives the time of the next planned maintenance. For example if there is:

  • 1 at position i it means that the powerline i will be disconnected for maintenance operation at the next time step.

  • 0 at position i means that powerline i is disconnected from the powergrid for maintenance operation at the current time step.

  • -1 at position i means that powerline i will not be disconnected for maintenance reason for this episode.

  • k > 1 at position i it means that the powerline i will be disconnected for maintenance operation at in k time steps

When a powerline is “in maintenance”, it cannot be reconnected by the Agent before the end of this maintenance.

Type:

numpy.ndarray, dtype:int

duration_next_maintenance

For each powerline, it gives the number of time step that the maintenance will last (if any). This means that, if at position i of this vector:

  • there is a 0: the powerline is not disconnected from the grid for maintenance

  • there is a 1, 2, … the powerline will be disconnected for at least 1, 2, … timestep (NB in all case, the powerline will stay disconnected until a grid2op.BaseAgent.BaseAgent performs the proper grid2op.BaseAction.BaseAction to reconnect it).

When a powerline is “in maintenance”, it cannot be reconnected by the Agent before the end of this maintenance.

Type:

numpy.ndarray, dtype:int

target_dispatch

For each generators, it gives the target redispatching, asked by the agent. This is the sum of all redispatching asked by the agent for during all the episode. It for each generator it is a number between: - pmax and pmax. Note that there is information about all generators there, even the one that are not dispatchable.

Type:

numpy.ndarray, dtype:float

actual_dispatch

For each generators, it gives the redispatching currently implemented by the environment. Indeed, the environment tries to implement at best the BaseObservation.target_dispatch, but sometimes, due to physical limitation (pmin, pmax, ramp min and ramp max) it cannot. In this case, only the best possible redispatching is implemented at the current time step, and this is what this vector stores. Note that there is information about all generators there, even the one that are not dispatchable.

Type:

numpy.ndarray, dtype:float

storage_charge

The actual ‘state of charge’ of each storage unit, expressed in MWh.

Type:

numpy.ndarray, dtype:float

storage_power_target

For each storage units, give the setpoint of production / consumption as given by the agent

Type:

numpy.ndarray, dtype:float

storage_power

Give the actual storage production / loads at the given state.

Type:

numpy.ndarray, dtype:float

storage_theta

The voltage angle (in degree) of the bus to which each storage units is connected. Only availble if the backend supports the retrieval of voltage angles (see BaseObservation.support_theta).

Type:

numpy.ndarray, dtype:float

gen_p_before_curtail

Give the production of renewable generator there would have been if no curtailment were applied (NB it returns 0.0 for non renewable generators that cannot be curtailed)

Type:

numpy.ndarray, dtype:float

curtailment_limit

Limit (in ratio of gen_pmax) imposed on each renewable generator as set by the agent.

It is always 1. if no curtailment actions is acting on the generator.

This is the “curtailment” given in the action by the agent.

Type:

numpy.ndarray, dtype:float

curtailment_limit_effective

Limit (in ratio of gen_pmax) imposed on each renewable generator effectively imposed by the environment.

It matches BaseObservation.curtailment_limit if param.LIMIT_INFEASIBLE_CURTAILMENT_STORAGE_ACTION is False (default) otherwise the environment is able to limit the curtailment actions if too much power would be needed to compensate the “loss” of generation due to renewables.

It is always 1. if no curtailment actions is acting on the generator.

Type:

numpy.ndarray, dtype:float

curtailment_mw

Gives the amount of power curtailed for each generator (it is 0. for all non renewable generators)

This is NOT the “curtailment” given in the action by the agent.

Type:

numpy.ndarray, dtype:float

curtailment

Give the power curtailed for each generator. It is expressed in ratio of gen_pmax (so between 0. - meaning no curtailment in effect for this generator - to 1.0 - meaning this generator should have produced pmax, but a curtailment action limits it to 0.)

This is NOT the “curtailment” given in the action by the agent.

Type:

numpy.ndarray, dtype:float

current_step

Current number of step performed up until this observation (NB this is not given in the observation if it is transformed into a vector)

Type:

int

max_step

Maximum number of steps possible for this episode

Type:

int

delta_time

Time (in minutes) between the last step and the current step (usually constant in an episode, even in an environment)

Type:

float

is_alarm_illegal

whether the last alarm has been illegal (due to budget constraint). It can only be True if an alarm was raised by the agent on the previous step. Otherwise it is always False (warning: /!\ Only valid with “l2rpn_icaps_2021” environment /!\)

Type:

bool

time_since_last_alarm

Number of steps since the last successful alarm has been raised. It is -1 if no alarm has been raised yet. (warning: /!\ Only valid with “l2rpn_icaps_2021” environment /!\)

Type:

int

last_alarm

For each zones, gives how many steps since the last alarm was raised successfully for this zone (warning: /!\ Only valid with “l2rpn_icaps_2021” environment /!\)

Type:

numpy.ndarray, dtype:int

attention_budget

The current attention budget

Type:

int

was_alarm_used_after_game_over

Was the last alarm used to compute anything related to the attention budget when there was a game over. It can only be set to True if the observation corresponds to a game over, but not necessarily. (warning: /!\ Only valid with “l2rpn_icaps_2021” environment /!\)

Type:

bool

gen_margin_up

From how much can you increase each generators production between this step and the next.

It is always 0. for non renewable generators. For the others it is defined as np.minimum(type(self).gen_pmax - self.gen_p, self.gen_max_ramp_up)

Type:

numpy.ndarray, dtype:float

gen_margin_down

From how much can you decrease each generators production between this step and the next.

It is always 0. for non renewable generators. For the others it is defined as np.minimum(self.gen_p - type(self).gen_pmin, self.gen_max_ramp_down)

Type:

numpy.ndarray, dtype:float

active_alert

Warning

Only available if the environment supports the “alert” feature (eg “l2rpn_idf_2023”).

See also

Alert feature section of the doc for more information

New in version 1.9.1.

This attribute gives the lines “under alert” at the given observation. It is only relevant for the “real” environment and not for obs.simulate nor obs.get_forecast_env

Type:

numpy.ndarray, dtype:bool

time_since_last_alert

Warning

Only available if the environment supports the “alert” feature (eg “l2rpn_idf_2023”).

See also

Alert feature section of the doc for more information

New in version 1.9.1.

Give the time since an alert has been raised for each powerline. If you just raise an alert for attackable line i then obs.time_since_last_alert[i] = 0 (and counter increase by 1 each step).

If attackable line i has never been “under alert” then obs.time_since_last_alert[i] = -1

Type:

numpy.ndarray, dtype:int

alert_duration

Warning

Only available if the environment supports the “alert” feature (eg “l2rpn_idf_2023”).

See also

Alert feature section of the doc for more information

New in version 1.9.1.

Give the time since an alert has started for all attackable line. If you just raise an alert for attackable line i then obs.time_since_last_alert[i] = 1 and this counter increase by 1 each step as long as the agent continues to “raise an alert on attackable line i”

When the attackable line i is not under an alert then obs.time_since_last_alert[i] = 0

Type:

numpy.ndarray, dtype:int

total_number_of_alert

Warning

Only available if the environment supports the “alert” feature (eg “l2rpn_idf_2023”).

See also

Alert feature section of the doc for more information

New in version 1.9.1.

This attribute stores, since the beginning of the current episode, the total number of alerts (here 1 alert = one alert for 1 powerline for 1 step) sent by the agent.

Type:

numpy.ndarray, dtype:int

time_since_last_attack

Warning

Only available if the environment supports the “alert” feature (eg “l2rpn_idf_2023”).

See also

Alert feature section of the doc for more information

New in version 1.9.1.

Similar to time_since_last_alert but for the attack.

For each attackable line i it counts the number of steps since the powerline has been attacked:

  • obs.time_since_last_attack[i] = -1 then attackable line i has never been attacked

  • obs.time_since_last_attack[i] = 0 then attackable line i has been attacked “for the first time” this step

  • obs.time_since_last_attack[i] = 1 then attackable line i has been attacked “for the first time” the previous step

  • obs.time_since_last_attack[i] = 2 then attackable line i has been attacked “for the first time” 2 steps ago

Note

An attack “for the first time” is NOT an attack “for the first time of the scenario”. Indeed, for this attribute, if a powerline is under attack for say 5 consecutive steps, then the opponent stops its attack on this line and says 6 or 7 steps later it start again to attack it then obs.time_since_last_attack[i] = 0 at the “first time” the opponent attacks again this powerline.

Type:

numpy.ndarray, dtype:int

was_alert_used_after_attack

Warning

Only available if the environment supports the “alert” feature (eg “l2rpn_idf_2023”).

Danger

This attribute is only filled if you use a compatible reward (eg grid2op.Reward.AlertReward) as the main reward (or a “combined” reward with this reward being part of it)

See also

Alert feature section of the doc for more information

New in version 1.9.1.

For each attackable line i it says:

  • obs.was_alert_used_after_attack[i] = 0 => attackable line i has not been attacked

  • obs.was_alert_used_after_attack[i] = -1 => attackable line i has been attacked and for the last attack the INCORRECT alert was sent (meaning that: if the agent survives, it sends an alert and if the agent died it fails to send an alert)

  • obs.was_alert_used_after_attack[i] = +1 => attackable line i has been attacked and for the last attack the CORRECT alert was sent (meaning that: if the agent survives, it did not send an alert and if the agent died it properly sent an alert)

By “last attack”, we mean the last attack that occured until now.

Type:

numpy.ndarray, dtype:int

attack_under_alert

Warning

Only available if the environment supports the “alert” feature (eg “l2rpn_idf_2023”).

See also

Alert feature section of the doc for more information

New in version 1.9.1.

For each attackable line i it says:

  • obs.attack_under_alert[i] = 0 => attackable line i has not been attacked OR it has been attacked before the relevant window (env.parameters.ALERT_TIME_WINDOW)

  • obs.attack_under_alert[i] = -1 => attackable line i has been attacked and (before the attack) no alert was sent (so your agent expects to survive at least env.parameters.ALERT_TIME_WINDOW steps)

  • obs.attack_under_alert[i] = +1 => attackable line i has been attacked and (before the attack) an alert was sent (so your agent expects to “game over” within the next env.parameters.ALERT_TIME_WINDOW steps)

Type:

numpy.ndarray, dtype:int

_shunt_p

Shunt active value (only available if shunts are available) (in MW)

Type:

numpy.ndarray, dtype:float

_shunt_q

Shunt reactive value (only available if shunts are available) (in MVAr)

Type:

numpy.ndarray, dtype:float

_shunt_v

Shunt voltage (only available if shunts are available) (in kV)

Type:

numpy.ndarray, dtype:float

_shunt_bus

Bus (-1 disconnected, 1 for bus 1, 2 for bus 2) at which each shunt is connected (only available if shunts are available)

Type:

numpy.ndarray, dtype:float

Methods:

__eq__(other)

INTERNAL

__init__([obs_env, action_helper, ...])

nothing to do when an object of this class is created, the information is held by the class attributes

__sub__(other)

Computes the difference between two observations, and return an observation corresponding to this difference.

add_act(act[, issue_warn])

Easier access to the impact on the observation if an action were applied.

as_networkx()

Old name for BaseObservation.get_energy_graph(), will be removed in the future.

bus_connectivity_matrix([as_csr_matrix, ...])

If we denote by nb_bus the total number bus of the powergrid (you can think of a "bus" being a "node" if you represent a powergrid as a graph [mathematical object, not a plot] with the lines being the "edges"].

change_forecast_parameters(params)

This function allows to change the parameters (see grid2op.Parameters.Parameters for more information) that are used for the obs.simulate() and obs.get_forecast_env() method.

change_reward(reward_func)

Allow to change the reward used when calling BaseObservation.simulate() without having to access the observation space.

connectivity_matrix([as_csr_matrix])

Computes and return the "connectivity matrix" con_mat.

copy([env])

INTERNAL

flow_bus_matrix([active_flow, as_csr_matrix])

A matrix of size "nb bus" "nb bus".

from_vect(vect[, check_legit])

INTERNAL

get_back_to_ref_state([storage_setpoint, ...])

Allows to retrieve the list of actions that needs to be performed to get back the grid in the "reference" state (all elements connected to busbar 1, no redispatching, no curtailment)

get_elements_graph()

This function returns the "elements graph" as a networkx object.

get_energy_graph()

Convert this observation as a networkx graph.

get_env_from_external_forecasts([load_p, ...])

New in version 1.9.0.

get_forecast_arrays()

This functions allows to retrieve (as numpy arrays) the values for all the loads / generators / maintenance for the forseable future (they are the forecast availble in BaseObservation.simulate() and BaseObservation.get_forecast_env())

get_forecast_env()

New in version 1.9.0.

get_forecasted_inj([time_step])

This function allows you to retrieve directly the "forecast" injections for the step time_step.

get_simulator()

This function allows to retrieve a valid and properly initialized "Simulator"

get_time_stamp()

Get the time stamp of the current observation as a datetime.datetime object

process_grid2op_compat()

INTERNAL

process_shunt_satic_data()

remove possible shunts data from the classes, if shunts are deactivated

reset()

INTERNAL

set_game_over([env])

INTERNAL

simulate(action[, time_step])

This method is used to simulate the effect of an action on a forecast powergrid state.

state_of([_sentinel, load_id, gen_id, ...])

Return the state of this action on a give unique load, generator unit, powerline of substation.

sub_topology(sub_id)

Returns the topology of the given substation.

to_dict()

Transform this observation as a dictionary.

update(env[, with_forecast])

INTERNAL

update_after_reward(env)

Only called for the regular environment (so not available for BaseObservation.get_forecast_env() or BaseObservation.simulate())

where_different(other)

Returns the difference between two observation.

Attributes:

__hash__

curtailment_limit_mw

return the limit of production of a generator in MW rather in ratio

curtailment_mw

return the curtailment, expressed in MW rather than in ratio of pmax.

gen_bus

Retrieve the busbar at which each generator is connected.

line_ex_bus

Retrieve the busbar at which each extremity side of powerline is connected.

line_or_bus

Retrieve the busbar at which each origin side of powerline is connected.

load_bus

Retrieve the busbar at which each load is connected.

prod_p

As of grid2op version 1.5.0, for better consistency, the "prod_p" attribute has been renamed "gen_p", see the doc of BaseObservation.gen_p for more information.

prod_q

As of grid2op version 1.5.0, for better consistency, the "prod_q" attribute has been renamed "gen_q", see the doc of BaseObservation.gen_q for more information.

prod_v

As of grid2op version 1.5.0, for better consistency, the "prod_v" attribute has been renamed "gen_v", see the doc of BaseObservation.gen_v for more information.

storage_bus

Retrieve the busbar at which each storage unit is connected.

thermal_limit

Return the thermal limit of the powergrid, given in Amps (A)

__eq__(other: Self) bool[source]

INTERNAL

Warning

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

Test the equality of two observations.

2 actions are said to be identical if the have the same impact on the powergrid. This is unlrelated to their respective class. For example, if an BaseAction is of class BaseAction and doesn’t act on the _injection, it can be equal to a an BaseAction of derived class TopologyAction (if the topological modification are the same of course).

This implies that the attributes BaseAction.authorized_keys is not checked in this method.

Note that if 2 actions doesn’t act on the same powergrid, or on the same backend (eg number of loads, or generators is not the same in self and other, or they are not in the same order) then action will be declared as different.

Known issue if two backend are different, but the description of the _grid are identical (ie all n_gen, n_load, n_line, sub_info, dim_topo, all vectors *_to_subid, and *_pos_topo_vect are identical) then this method will not detect the backend are different, and the action could be declared as identical. For now, this is only a theoretical behaviour: if everything is the same, then probably, up to the naming convention, then the powergrid are identical too.

Parameters:

other (BaseObservation) – An instance of class BaseAction to which “self” will be compared.

Return type:

True if the action are equal, False otherwise.

__hash__ = None
__init__(obs_env=None, action_helper=None, random_prng=None, kwargs_env=None)[source]

nothing to do when an object of this class is created, the information is held by the class attributes

__sub__(other: Self) Self[source]

Computes the difference between two observations, and return an observation corresponding to this difference.

This can be used to easily plot the difference between two observations at different step for example.

Examples

import grid2op
env = grid2op.make("l2rpn_case14_sandbox")

obs_0 = env.reset()

action = env.action_space()
obs_1, reward, done, info = env.step(action)

diff_obs = obs_1 - obs_0

diff_obs.gen_p  # the variation in generator between these steps
add_act(act: BaseAction, issue_warn=True) Self[source]

Easier access to the impact on the observation if an action were applied.

This is for now only useful to get a topology in which the grid would be without doing an expensive obs.simuulate

Notes

This will not give the real topology of the grid in all cases for many reasons amongst:

  1. past topologies are not known by the observation. If you reconnect a powerline in the action without having specified on which bus, it has no way to know (but the environment does!) on which bus it should be reconnected (which is the last known bus)

  2. some “protections” are emulated in the environment. This means that the environment can disconnect some powerline under certain conditions. This is absolutely not taken into account here.

  3. the environment is stochastic, for example there can be maintenance or attacks (hazards) and the generators and loads change each step. This is not taken into account in this function.

  4. no checks are performed to see if the action meets the rules of the game (number of elements you can modify in the action, cooldowns etc.) This method supposes that the action is legal and non ambiguous.

  5. It do not check for possible “game over”, for example due to isolated elements or non-connected grid (grid with 2 or more connex components)

If these issues are important for you, you will need to use the grid2op.Observation.BaseObservation.simulate() method. It can be used like obs.simulate(act, time_step=0) but it is much more expensive.

Parameters:
  • act (grid2op.Action.BaseAction) – The action you want to add to the observation

  • issue_warn (bool) – Issue a warning when this method might not compute the proper resulting topologies. Default to True: it issues warning when something not supported is done in the action.

Returns:

res – The resulting observation. Note that this observation is not initialized with everything. It is only relevant when you want to study the resulting topology after you applied an action. Lots of res attributes are empty.

Return type:

grid2op.Observation.BaseObservation

Examples

You can use it this way, for example if you want to retrieve the topology you would get (see the restriction in the above description) after applying an action:

import grid2op

# create the environment
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)

# generate the first observation
obs = env.reset()

# make some action
act = ...  # see the dedicated page

# have a look at the impact on the action on the topology
partial_obs = obs + act
# or `partial_obs = obs.add_act(act, issue_warn=False)` if you want to silence the warnings

# and now you can inspect the topology with any method you want:
partial_obs.topo_vect
partial_obs.load_bus
bus_mat = partial_obs.bus_connectivity_matrix()
# or even
elem_mat = partial_obs.connectivity_matrix()

# but you cannot use
partial_obs.prod_p
# or
partial_obs.load_q
etc.
as_networkx() Graph[source]

Old name for BaseObservation.get_energy_graph(), will be removed in the future.

bus_connectivity_matrix(as_csr_matrix: bool = False, return_lines_index: bool = False) Tuple[ndarray | csr_matrix, Tuple[ndarray, ndarray] | None][source]

If we denote by nb_bus the total number bus of the powergrid (you can think of a “bus” being a “node” if you represent a powergrid as a graph [mathematical object, not a plot] with the lines being the “edges”].

The bus_connectivity_matrix will have a size nb_bus, nb_bus and will be made of 0 and 1.

If bus_connectivity_matrix[i,j] = 1 then at least a power line connects bus i and bus j. Otherwise, nothing connects it.

Warning

The matrix returned by this function has not a fixed size. Its number of nodes and edges can change depending on the state of the grid. See How to retrieve “the” graph in grid2op for more information.

Also, note that when “done=True” this matrix has size (1, 1) and contains only 0.

Parameters:
  • as_csr_matrix (bool) – Whether to return the bus connectivity matrix as a sparse matrix (csr format) or as a dense matrix. By default it’s False meaning a dense matrix is returned.

  • return_lines_index (bool) – Whether to also return the bus index associated to both side of each powerline. False by default, meaning the indexes are not returned.

Returns:

  • res (numpy.ndarray, shape: (nb_bus, nb_bus) dtype:float) – The bus connectivity matrix defined above.

  • optional

  • - `lor_bus` (for each powerline, it gives the id (row / column of the matrix)) – of the bus of the matrix to which its origin side is connected

  • - `lex_bus` (for each powerline, it gives the id (row / column of the matrix)) – of the bus of the matrix to which its extremity side is connected

Notes

By convention we say that a bus is connected to itself. So the diagonal of this matrix is 1.

Examples

Here is how you can use this function:

bus_bus_graph, (line_or_bus, line_ex_bus) = obs.bus_connectivity_matrix(return_lines_index=True)

# bus_bus_graph is the matrix described above.
# line_or_bus[0] give the id of the bus to which the origin side of powerline 0 is connected
# line_ex_bus[0] give the id of the bus to which the extremity side of powerline 0 is connected
# (NB: if the powerline is disconnected, both are -1)
# this means that if line 0 is connected: bus_bus_graph[line_or_bus[0], line_ex_bus[0]] = 1
# and bus_bus_graph[line_ex_bus[0], line_or_bus[0]] = 1
# (of course you can replace 0 with any integer `0 <= l_id < obs.n_line`
change_forecast_parameters(params: Parameters) None[source]

This function allows to change the parameters (see grid2op.Parameters.Parameters for more information) that are used for the obs.simulate() and obs.get_forecast_env() method.

Danger

This function has a global impact. It changes the parameters for all sucessive calls to BaseObservation.simulate() and BaseObservation.get_forecast_env() !

See also

grid2op.Environment.BaseEnv.change_parameters() to change the parameters of the environment of grid2op.Environment.BaseEnv.change_forecast_parameters() to change the paremters used for the obs.simulate and obs.get_forecast_env functions.

The main advantages of this function is that you do not require to have access to an environment to change them.

New in version 1.9.0.

Examples

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)

obs = env.reset()

new_params = env.parameters
new_params.NO_OVERFLOW_DISCONNECTION = True
obs.change_forecast_parameters(new_params)

obs.simulate(...)  # uses the parameters `new_params`
f_env = obs.get_forecast_env()  # uses also the parameters `new_params`
change_reward(reward_func: BaseReward)[source]

Allow to change the reward used when calling BaseObservation.simulate() without having to access the observation space.

New in version 1.10.2.

See also

grid2op.ObservationSpace.change_reward() It has the same effet as grid2op.ObservationSpace.change_reward()

Parameters:

reward_func (grid2op.Reward.BaseReward) – _description_

Raises:

BaseObservationError – _description_

connectivity_matrix(as_csr_matrix: bool = False) ndarray | csr_matrix[source]

Computes and return the “connectivity matrix” con_mat. Let “dim_topo := 2 * n_line + n_prod + n_conso + n_storage” (the total number of elements on the grid)

It is a matrix of size dim_topo, dim_topo, with values 0 or 1. For two objects (lines extremity, generator unit, load) i,j :

  • if i and j are connected on the same substation:
    • if conn_mat[i,j] = 0 it means the objects id’ed i and j are not connected to the same bus.

    • if conn_mat[i,j] = 1 it means the objects id’ed i and j are connected to the same bus

  • if i and j are not connected on the same substation then`conn_mat[i,j] = 0` except if i and j are the two extremities of the same power line, in this case conn_mat[i,j] = 1 (if the powerline is in service or 0 otherwise).

By definition, the diagonal is made of 0.

Returns:

res – The connectivity matrix, as defined above

Return type:

numpy.ndarray, shape:dim_topo,dim_topo, dtype:float

Notes

Matrix can be either a sparse matrix or a dense matrix depending on the argument as_csr_matrix

An object, is not disconnected, is always connected to itself.

Examples

If you want to know if powerline 0 is connected at its “extremity” side with the load of id 0 you can do

import grid2op
env = grid2op.make("l2rpn_case14_sandbox")
obs = env.reset()

# retrieve the id of extremity of powerline 1:
id_lineex_0 = obs.line_ex_pos_topo_vect[0]
id_load_1 = obs.load_pos_topo_vect[0]

# get the connectivity matrix
connectivity_matrix = obs.connectivity_matrix()

# know if the objects are connected or not
are_connected = connectivity_matrix[id_lineex_0, id_load_1]
# as `are_connected` is 1.0 then these objects are indeed connected

And now, supposes we do an action that changes the topology of the substation to which these two objects are connected, then we get (same example continues)

topo_action = env.action_space({"set_bus": {"substations_id": [(1, [1,1,1,2,2,2])]}})
print(topo_action)
# This action will:
#   - NOT change anything to the injections
#   - NOT perform any redispatching action
#   - NOT force any line status
#   - NOT switch any line status
#   - NOT switch anything in the topology
#   - Set the bus of the following element:
#     - assign bus 1 to line (extremity) 0 [on substation 1]
#     - assign bus 1 to line (origin) 2 [on substation 1]
#     - assign bus 1 to line (origin) 3 [on substation 1]
#     - assign bus 2 to line (origin) 4 [on substation 1]
#     - assign bus 2 to generator 0 [on substation 1]
#     - assign bus 2 to load 0 [on substation 1]

obs, reward, done, info = env.step(topo_action)
# and now retrieve the matrix
connectivity_matrix = obs.connectivity_matrix()

# know if the objects are connected or not
are_connected = connectivity_matrix[id_lineex_0, id_load_1]
# as `are_connected` is 0.0 then these objects are not connected anymore
# this is visible when you "print" the action (see above) in the two following lines:
#     - assign bus 1 to line (extremity) 0 [on substation 1]
#     - assign bus 2 to load 0 [on substation 1]
# -> one of them is on bus 1 [line (extremity) 0] and the other on bus 2 [load 0]
copy(env=None) Self[source]

INTERNAL

Warning

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

Make a copy of the observation.

Returns:

res – The deep copy of the observation

Return type:

BaseObservation

Notes

The “obs_env” attributes

property curtailment_limit_mw: ndarray

return the limit of production of a generator in MW rather in ratio

Examples

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)

obs = env.reset()
curtailment_limit_mw = obs.curtailment_limit_mw
property curtailment_mw: ndarray

return the curtailment, expressed in MW rather than in ratio of pmax.

Examples

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)

obs = env.reset()
curtailment_mw = obs.curtailment_mw
flow_bus_matrix(active_flow: bool = True, as_csr_matrix: bool = False) Tuple[ndarray, ndarray, ndarray, ndarray, ndarray][source]

A matrix of size “nb bus” “nb bus”. Each row and columns represent a “bus” of the grid (“bus” is a power system word that for computer scientist means “nodes” if the powergrid is represented as a graph). See the note in case of a grid in “game over” mode.

The diagonal will sum the power produced and consumed at each bus.

The other element of each row of this matrix will be the flow of power from the bus represented by the line i to the bus represented by column j.

Warning

The matrix returned by this function has not a fixed size. Its number of nodes and edges can change depending on the state of the grid. See How to retrieve “the” graph in grid2op for more information.

Also, note that when “done=True” this matrix has size (1, 1) and contains only 0.

Notes

When the observation is in a “done” state (eg there has been a game over) then this function returns a “matrix” of dimension (1,1) [yes, yes it’s a scalar] with only one element that is 0.

In this case, load_bus, prod_bus, stor_bus, lor_bus and lex_bus are vectors full of 0.

Parameters:
  • active_flow (bool) – Whether to get the active flow (in MW) or the reactive flow (in MVAr). Defaults to active flow.

  • as_csr_matrix (bool) – Whether to retrieve the results as a scipy csr sparse matrix or as a dense matrix (default)

Returns:

  • res (matrix) – Which can either be a sparse matrix or a dense matrix depending on the value of the argument “as_csr_matrix”.

  • mappings (tuple) – The mapping that makes the correspondence between each object and the bus to which it is connected. It is made of 4 elements: (load_bus, prod_bus, stor_bus, lor_bus, lex_bus).

    For example if load_bus[i] = 14 it means that the load with id i is connected to the bus 14. If load_bus[i] = -1 then the object is disconnected.

Examples

Here is how you can use this function:

flow_mat, (load, prod, stor, ind_lor, ind_lex) = obs.flow_bus_matrix()

# flow_mat is the matrix described above.

Lots of information can be deduce from this matrix. For example if you want to know how much power goes from one bus say bus i to another bus (say bus j ) you can look at the associated coefficient flow_mat[i,j] which will also be related to the flow on the origin (or extremity) side of the powerline connecting bus i to bus j

You can also know how much power (total generation + total storage discharging - total load - total storage charging) is injected at each bus i by looking at the i th diagonal coefficient.

Another use would be to check if the current powergrid state (as seen by grid2op) meet the Kirchhoff circuit laws (conservation of energy), by doing the sum (row by row) of this matrix. flow_mat.sum(axis=1)

from_vect(vect, check_legit=True)[source]

INTERNAL

Warning

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

To reload an observation from a vector, use the “env.observation_space.from_vect()”.

Convert back an observation represented as a vector into a proper observation.

Some conversion are done silently from float to the type of the corresponding observation attribute.

Parameters:

vect (numpy.ndarray) – A representation of an BaseObservation in the form of a vector that is used to convert back the current observation to be equal to the vect.

property gen_bus: ndarray

Retrieve the busbar at which each generator is connected.

The result follow grid2op convention:

  • -1 means the generator is disconnected

  • 1 means it is generator to busbar 1

  • 2 means it is connected to busbar 2

  • etc.

Notes

In a same substation, two objects are connected together if (and only if) they are connected to the same busbar.

get_back_to_ref_state(storage_setpoint: float = 0.5, precision: int = 5) Dict[Literal['powerline', 'substation', 'redispatching', 'storage', 'curtailment'], List[BaseAction]][source]

Allows to retrieve the list of actions that needs to be performed to get back the grid in the “reference” state (all elements connected to busbar 1, no redispatching, no curtailment)

New in version 1.10.0.

This function uses the method of the underlying action_space used for the forecasts.

See grid2op.Action.SerializableActionSpace.get_back_to_ref_state() for more information.

Examples

You can use it like this:

import grid2op

env_name = "l2rpn_case14_sandbox"
env = grid2op.make(env_name)
obs = env.reset(seed=1)

# perform a random action
obs, reward, done, info = env.step(env.action_space.sample())
assert not done # you might end up in a "done" state depending on the random action

acts = obs.get_back_to_ref_state()
print(acts)
get_elements_graph() DiGraph[source]

This function returns the “elements graph” as a networkx object.

See also

This object is extensively described in the documentation, see Graph2: the “elements graph” for more information.

Basically, each “element” of the grid (element = a substation, a bus, a load, a generator, a powerline, a storate unit or a shunt) is represented by a node in this graph.

There might be some edges between the nodes representing buses and the nodes representing substations, indicating “this bus is part of this substation”.

There might be some edges between the nodes representing load / generator / powerline / storage unit / shunt and the nodes representing buses, indicating “this load / generator / powerline / storage unit is connected to this bus”.

Nodes and edges of this graph have different attributes depending on the underlying element they represent. For a detailed description, please refer to the documentation: Graph2: the “elements graph”

Examples

You can use, for example to “check” Kirchoff Current Law (or at least that no energy is created at none of the buses):

import grid2op
env_name = "l2rpn_case14_sandbox" # or any other name...

env = grid2op.make(env_name)
obs = env.reset()

# retrieve the graph and do something
elmnt_graph = obs.get_elements_graph()
for bus_id, node_id in enumerate(elmnt_graph.graph["bus_nodes_id"]):
    sum_p = 0.
    sum_q = 0.
    for ancestor in graph.predecessors(node_id):
        # ancestor is the id of a node representing an element connected to this
        # bus
        this_edge = graph.edges[(ancestor, node_id)]
        if "p" in this_edge:
            sum_p += this_edge["p"]
        if "q" in this_edge:
            sum_q += this_edge["q"]
    assert abs(sum_p) <= self.tol, f"error for node {node_id} representing bus {bus_id}: {abs(sum_p)} != 0."
    assert abs(sum_q) <= self.tol, f"error for node {node_id} representing bus {bus_id}: {abs(sum_q)} != 0."
Returns:

The “elements graph”, see Graph2: the “elements graph” .

Return type:

networkx.DiGraph

get_energy_graph() Graph[source]

Convert this observation as a networkx graph. This graph is the graph “seen” by “the electron” / “the energy” of the power grid.

Changed in version 1.10.0: Addition of the attribute local_bus_id and global_bus_id for the nodes of the returned graph.

local_bus_id give the local bus id (from 1 to obs.n_busbar_per_sub) id of the bus represented by this node.

global_bus_id give the global bus id (from 0 to obs.n_busbar_per_sub * obs.n_sub - 1) id of the bus represented by this node.

Addition of the attribute global_bus_or and global_bus_ex for the edges of the returned graph.

These provides the global id of the origin / ext side to which powerline(s) represented by this edge is (are) connected.

Notes

The resulting graph is “frozen” this means that you cannot add / remove attribute on nodes or edges, nor add / remove edges or nodes.

This graphs has the following properties:

  • it counts as many nodes as the number of buses of the grid (so it has a dynamic size !)

  • it counts less edges than the number of lines of the grid (two lines connecting the same buses are “merged” into one single edge - this is the case for parallel line, that are hence “merged” into the same edge)

  • nodes (represents “buses” of the grid) have attributes:

    • p: the active power produced at this node (negative means the sum of power produce minus power absorbed is negative) in MW

    • q: the reactive power produced at this node in MVAr

    • v: the voltage magnitude at this node

    • cooldown: how much longer you need to wait before being able to merge / split or change this node

    • ‘sub_id’: the id of the substation to which it is connected (typically between 0 and obs.n_sub - 1)

    • ‘local_bus_id’: the local bus id (from 1 to obs.n_busbar_per_sub) of the bus represented by this node (new in version 1.10.0)

    • ‘global_bus_id’: the global bus id (from 0 to obs.n_busbar_per_sub * obs.n_sub - 1) of the bus represented by this node (new in version 1.10.0)

    • cooldown : the time you need to wait (in number of steps) before being able to act on the substation to which this bus is connected.

    • (optional) theta: the voltage angle (in degree) at this nodes

  • edges have attributes too (in this modeling an edge might represent more than one powerline, all parallel powerlines are represented by the same edge):

    • nb_connected: number of connected powerline represented by this edge.

    • rho: the relative flow on this powerline (in %) (sum over all powerlines))

    • cooldown: the number of step you need to wait before being able to act on this powerline (max over all powerlines)

    • thermal_limit: maximum flow allowed on the the powerline (sum over all powerlines)

    • timestep_overflow: number of time steps during which the powerline is on overflow (max over all powerlines)

    • p_or: active power injected at this node at the “origin side” (in MW) (sum over all the powerlines).

    • p_ex: active power injected at this node at the “extremity side” (in MW) (sum over all the powerlines).

    • q_or: reactive power injected at this node at the “origin side” (in MVAr) (sum over all the powerlines).

    • q_ex: reactive power injected at this node at the “extremity side” (in MVAr) (sum over all the powerlines).

    • a_or: current flow injected at this node at the “origin side” (in A) (sum over all the powerlines) (sum over all powerlines).

    • a_ex: current flow injected at this node at the “extremity side” (in A) (sum over all the powerlines) (sum over all powerlines).

    • p: active power injected at the “or” side (equal to p_or) (in MW)

    • v_or: voltage magnitude at the “or” bus (in kV)

    • v_ex: voltage magnitude at the “ex” bus (in kV)

    • time_next_maintenance: see BaseObservation.time_next_maintenance (min over all powerline)

    • duration_next_maintenance see BaseObservation.duration_next_maintenance (max over all powerlines)

    • sub_id_or: id of the substation of the “or” side of the powerlines

    • sub_id_ex: id of the substation of the “ex” side of the powerlines

    • node_id_or: id of the node (in this graph) of the “or” side of the powergraph

    • node_id_ex: id of the node (in this graph) of the “ex” side of the powergraph

    • bus_or: on which bus [1 or 2 or 3, etc.] is this powerline connected to at its “or” substation (this is the local id of the bus)

    • bus_ex: on which bus [1 or 2 or 3, etc.] is this powerline connected to at its “ex” substation (this is the local id of the bus)

    • ‘global_bus_or’: the global bus id (from 0 to obs.n_busbar_per_sub * obs.n_sub - 1) of the bus to which the origin side of the line(s) represented by this edge is (are) connected (new in version 1.10.0)

    • ‘global_bus_ex’: the global bus id (from 0 to obs.n_busbar_per_sub * obs.n_sub - 1) of the bus to which the ext side of the line(s) represented by this edge is (are) connected (new in version 1.10.0)

    • (optional) theta_or: voltage angle at the “or” bus (in deg)

    • (optional) theta_ex: voltage angle at the “ex” bus (in deg)

Danger

IMPORTANT NOTE edges represents “fusion” of 1 or more powerlines. This graph is intended to be a Graph and not a MultiGraph on purpose. This is why sometimes some attributes of the edges are not the same of the attributes of a given powerlines. For example, in the case of 2 parrallel powerlines (say powerlines 3 and 4) going from bus 10 to bus 12 (for example), the edges graph.edges[(10, 12)][“nb_connected”] will be 2 and you will get graph.edges[(10, 12)][“p_or”] = obs.p_or[3] + obs.p_or[4]

Warning

The graph returned by this function has not a fixed size. Its number of nodes and edges can change depending on the state of the grid. See How to retrieve “the” graph in grid2op for more information.

Also, note that when “done=True” this graph has only one node and no edge.

Note

The graph returned by this function is “frozen” to prevent its modification. If you really want to modify it you can “unfroze” it.

Returns:

graph – A possible representation of the observation as a networkx graph

Return type:

networkx graph

Examples

The following code explains how to check that a grid meet the kirchoffs law (conservation of energy)

# create an environment and get the observation
import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)
obs = env.reset()

# retrieve the networkx graph
graph = obs.get_energy_graph()

# perform the check for every nodes
for node_id in graph.nodes:
    # retrieve power (active and reactive) produced at this node
    p_ = graph.nodes[node_id]["p"]
    q_ = graph.nodes[node_id]["q"]

    # get the edges
    edges = graph.edges(node_id)
    p_lines = 0
    q_lines = 0
    # get the power that is "evacuated" at each nodes on all the edges connecting it to the other nodes
    # of the network
    for (k1, k2) in edges:
        # now retrieve the active / reactive power injected at this node (looking at either *_or or *_ex
        # depending on the direction of the powerline: remember that the "origin" is always the lowest
        # bus id.
        if k1 < k2:
            # the current inspected node is the lowest, so on the "origin" side
            p_lines += graph.edges[(k1, k2)]["p_or"]
            q_lines += graph.edges[(k1, k2)]["q_or"]
        else:
            # the current node is the largest, so on the "extremity" side
            p_lines += graph.edges[(k1, k2)]["p_ex"]
            q_lines += graph.edges[(k1, k2)]["q_ex"]
    assert abs(p_line - p_) <= 1e-5, "error for kirchoff's law for graph for P"
    assert abs(q_line - q_) <= 1e-5, "error for kirchoff's law for graph for Q"
get_env_from_external_forecasts(load_p: ndarray | None = None, load_q: ndarray | None = None, gen_p: ndarray | None = None, gen_v: ndarray | None = None, with_maintenance: bool = False) Environment[source]

New in version 1.9.0.

This function will return a grid2op “environment” where the data (load, generation and maintenance) comes from the provided forecast data.

This “forecasted environment” can be used like any grid2op environment. It checks the same “rules” as the BaseObservation.simulate() (if you want to change them, make sure to use grid2op.Environment.BaseEnv.change_forecast_parameters() or BaseObservation.change_forecast_parameters()), with the exact same behaviour as “env.step(…)”.

This can be particularly useful for model based RL for example.

Data should be:

  • load_p a numpy array of float32 (or convertible to it) with n_rows and n_load columns representing the load active values in MW.

  • load_q a numpy array of float32 (or convertible to it) with n_rows and n_load columns representing the load reactive values in MVAr.

  • gen_p a numpy array of float32 (or convertible to it) with n_rows and n_gen columns representing the generation active values in MW.

  • gen_v a numpy array of float32 (or convertible to it) with n_rows and n_gen columns representing the voltage magnitude setpoint in kV.

All arrays are optional. If nothing is provided for a given array then it’s replaced by the value in the observation. For example, if you do not provided the gen_p value then obs.gen_p is used.

All provided arrays should have the same number of rows.

Note

Maintenance will be added from the information of the observation. If you don’t want to add maintenance, you can passe the kwarg with_maintenance=False

Note

With this method, you can have as many “steps” in the forecasted environment as you want. You are not limited with the amount of data provided: if you send data with 10 rows, you have 10 steps. If you have 100 rows then you have 100 steps.

Warning

We remind that, if you provide some forecasts, it is expected that they allow some powerflow to converge. The balance between total generation on one side and total demand and losses on the other should also make “as close as possible” to reduce some modeling artifact (by the backend, grid2op does not check anything here).

Finally, make sure that your input data meet the constraints on the generators (pmin, pmax and ramps) otherwise you might end up with incorrect behaviour. Grid2op supposes that data fed to it is consistent with its model. If not it’s “undefined behaviour”.

Danger

Long story short, once a environment (and a forecast_env is one) is deleted, you cannot use anything it “holds” including, but not limited to the capacity to perform obs.simulate(…) even if the obs is still referenced.

See Notes (first danger block).

This caused issue https://github.com/rte-france/Grid2Op/issues/568 for example.

Examples

A typical use might look like

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)
obs = env.reset()

# make some "forecast" with the method of your choice
load_p_forecasted = ...
load_q_forecasted = ...
gen_p_forecasted = ...
gen_v_forecasted = ...

# and now retrieve the associated "forecasted_env"
forcast_env = obs.get_env_from_external_forecasts(load_p_forecasted,
                                                  load_q_forecasted,
                                                  gen_p_forecasted,
                                                  gen_v_forecasted)

# when reset this should be at the same "step" as the action
forecast_obs = forcast_env.reset()
# forecast_obs == obs  # should be True

done = False
while not done:
    next_forecast_obs, reward, done, info = forcast_env.step(env.action_space())
Returns:

The “forecasted environment” that is a grid2op environment with the data corresponding to the forecasts provided as input.

Return type:

grid2op.Environment.Environment

get_forecast_arrays() Tuple[ndarray, ndarray, ndarray, ndarray][source]

This functions allows to retrieve (as numpy arrays) the values for all the loads / generators / maintenance for the forseable future (they are the forecast availble in BaseObservation.simulate() and BaseObservation.get_forecast_env())

New in version 1.9.0.

Examples

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)

obs = env.reset()

load_p, load_q, prod_p, prod_v, maintenance = obs.get_forecast_arrays()
get_forecast_env() Environment[source]

New in version 1.9.0.

This function will return a grid2op “environment” where the data (load, generation and maintenance) comes from the forecast data in the observation.

This “forecasted environment” can be used like any grid2op environment. It checks the same “rules” as the BaseObservation.simulate() (if you want to change them, make sure to use grid2op.Environment.BaseEnv.change_forecast_parameters() or BaseObservation.change_forecast_parameters()), with the exact same behaviour as “env.step(…)”.

With this function, your agent can now make some predictions about the future.

This can be particularly useful for model based RL for example.

Examples

A typical use might look like

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)
obs = env.reset()

# and now retrieve the "forecasted_env"
forcast_env = obs.get_forecast_env()

# when reset this should be at the same "step" as the action
forecast_obs = forcast_env.reset()
# forecast_obs == obs  # should be True

done = False
while not done:
    next_forecast_obs, reward, done, info = forcast_env.step(env.action_space())

Note

The code above is closely related to the BaseObservation.simulate() and a very similar result (up to some corner cases beyond the scope of this documentation) can be obtained with:

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)
obs = env.reset()

forecast_env = obs.get_forecast_env()
f_obs = forecast_env.reset()

act_1 = ...  # a grid2op action
f_obs_1, *_ = forecast_env.step(act_1)
sim_obs_1, *_ = obs.simulate(act_1)
# f_obs_1 should be sim_obs_1

act_2 = ...  # a grid2op action
f_obs_2, *_ = forecast_env.step(act_2)
sim_obs_2, *_ = sim_obs_1.simulate(act_2)
# f_obs_2 should be sim_obs_2

act_3 = ... # a grid2op action
f_obs_3, *_ = forecast_env.step(act_3)
sim_obs_3, *_ = sim_obs_2.simulate(act_3)
# f_obs_3 should be sim_obs_3

Danger

Long story short, once a environment (and a forecast_env is one) is deleted, you cannot use anything it “holds” including, but not limited to the capacity to perform obs.simulate(…) even if the obs is still referenced.

See Notes (first danger block).

This caused issue https://github.com/rte-france/Grid2Op/issues/568 for example.

Returns:

The “forecasted environment” that is a grid2op environment with the data corresponding to the forecast made at the time of the observation.

Return type:

grid2op.Environment.Environment

Raises:

BaseObservationError – When no forecast are available, for example.

get_forecasted_inj(time_step: int = 1) ndarray[source]

This function allows you to retrieve directly the “forecast” injections for the step time_step.

We remind that the environment, under some conditions, can produce these forecasts automatically. This function allows to retrieve what has been forecast.

Parameters:

time_step (int) – The horizon of the forecast (given in number of time steps)

Returns:

  • gen_p_f (numpy.ndarray) – The forecast generators active values

  • gen_v_f (numpy.ndarray) – The forecast generators voltage setpoins

  • load_p_f (numpy.ndarray) – The forecast load active consumption

  • load_q_f (numpy.ndarray) – The forecast load reactive consumption

get_simulator() Simulator[source]

This function allows to retrieve a valid and properly initialized “Simulator”

A grid2op.simulator.Simulator can be used to simulate the impact of multiple consecutive actions, without taking into account any kind of rules.

It can also be use with forecast of the productions / consumption to predict whether or not a given state is “robust” to variation of the injections for example.

You can find more information about simulator on the dedicated page of the documentation Simulator. TODO

Basic usage are:

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name

env = grid2op.make(env_name)
obs = env.reset()

simulator = obs.get_simulator()

Please consult the page Simulator for more information about how to use them.

get_time_stamp() datetime[source]

Get the time stamp of the current observation as a datetime.datetime object

property line_ex_bus: ndarray

Retrieve the busbar at which each extremity side of powerline is connected.

The result follow grid2op convention:

  • -1 means the powerline is disconnected

  • 1 means it is connected to busbar 1

  • 2 means it is connected to busbar 2

  • etc.

Notes

In a same substation, two objects are connected together if (and only if) they are connected to the same busbar.

property line_or_bus: ndarray

Retrieve the busbar at which each origin side of powerline is connected.

The result follow grid2op convention:

  • -1 means the powerline is disconnected

  • 1 means it is connected to busbar 1

  • 2 means it is connected to busbar 2

  • etc.

Notes

In a same substation, two objects are connected together if (and only if) they are connected to the same busbar.

property load_bus: ndarray

Retrieve the busbar at which each load is connected.

The result follow grid2op convention:

  • -1 means the load is disconnected

  • 1 means it is load to busbar 1

  • 2 means it is load to busbar 2

  • etc.

Notes

In a same substation, two objects are connected together if (and only if) they are connected to the same busbar.

classmethod process_grid2op_compat() None[source]

INTERNAL

Warning

/!\ Internal, do not use unless you know what you are doing /!\ This is done at the creation of the environment. Use of this class outside of this particular use is really dangerous and will lead to undefined behaviours. Do not use this function.

This is called when the class is initialized, with init_grid to broadcast grid2op compatibility feature.

This function can be overloaded, but in this case it’s best to call this original method too.

classmethod process_shunt_satic_data() None[source]

remove possible shunts data from the classes, if shunts are deactivated

property prod_p: ndarray

As of grid2op version 1.5.0, for better consistency, the “prod_p” attribute has been renamed “gen_p”, see the doc of BaseObservation.gen_p for more information.

This property is present to maintain the backward compatibility.

Return type:

BaseObservation.gen_p

property prod_q: ndarray

As of grid2op version 1.5.0, for better consistency, the “prod_q” attribute has been renamed “gen_q”, see the doc of BaseObservation.gen_q for more information.

This property is present to maintain the backward compatibility.

Return type:

BaseObservation.gen_q

property prod_v: ndarray

As of grid2op version 1.5.0, for better consistency, the “prod_v” attribute has been renamed “gen_v”, see the doc of BaseObservation.gen_v for more information.

This property is present to maintain the backward compatibility.

Return type:

BaseObservation.gen_v

reset() None[source]

INTERNAL

Warning

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

Resetting a single observation is unlikely to do what you want to do.

Reset the BaseObservation to a blank state, where everything is set to either None or to its default value.

set_game_over(env: Environment | None = None) None[source]

INTERNAL

Warning

/!\ Internal, do not use unless you know what you are doing /!\ This is used internally to reset an observation in a fixed state, possibly after a game over.

Set the observation to the “game over” state:

  • all powerlines are disconnected

  • all loads are 0.

  • all prods are 0.

  • etc.

Notes

As some attributes are initialized with np.empty it is recommended to reset here all attributes to avoid non deterministic behaviour.

simulate(action: BaseAction, time_step: int = 1) Tuple[BaseObservation, float, bool, Dict[Literal['disc_lines', 'is_illegal', 'is_ambiguous', 'is_dispatching_illegal', 'is_illegal_reco', 'reason_alarm_illegal', 'reason_alert_illegal', 'opponent_attack_line', 'opponent_attack_sub', 'exception', 'detailed_infos_for_cascading_failures', 'rewards', 'time_series_id'], Any]][source]

This method is used to simulate the effect of an action on a forecast powergrid state. This forecast state is built upon the current observation.

The forecast are pre computed by the environment.

More concretely, if not deactivated by the environment (see grid2op.Environment.BaseEnv.deactivate_forecast()) and the environment has the capacity to generate these forecasts (which is the case in most grid2op environments) this function will simulate the effect of doing an action now and return the “next state” (often the state you would get at time t + 5 mins) if you were to do the action at this step.

It has the same return value as the grid2op.Environment.BaseEnv.step() function.

New in version 1.9.0: If the data of the grid2op.Environment.Environment you are using supports it (ie you can access multiple steps ahead forecasts), then you can now “chain” the simulate calls.

Danger

A simulation can be different from the reality, even in case of perfect forecast or if you “simulate” an action on the current step (time_step=0).

For example, the solver used can be different for “simulate” and for the environment “step” or you can be a setting with noisy action.

A more subtle difference includes the “initialization” of the solver which is different in env.step and in obs.simulate so the outcomes of the solver might be different (this is especially relevant for larger grid).

Even more subtle is the behaviour of the ramps for some generators.

More concretely, say you want to dispatch upward a generator (with a ramp of +5) of +5MW at a given step. But in the same time this generator would see its production increased by +2MW “naturally” in the time series. Then, grid2op would limit the increase of +5MW (instead of +7 = +5 +2) by limiting the redispatching action to +3MW.

If you simulate the same action on the resulting step, as there are no “previous step” then your action will not be limited and the +5MW of redispatching will be given.

You have the same phenomenon for storage losses: they are applied even if you simulate at the current step and conversely are not applied “multiple times” if you simulate for an horizon longer than 1 (say time_step=2) or if you chain two or more calls to “simulate”.

Examples

If forecast are available, you can use this function like this:

import grid2op

env_name = "l2rpn_case14_sandbox"
env = grid2op.make(env_name)

obs = env.reset()

an_action = env.action_space()  # or any other action
simobs, sim_reward, sim_done, sim_info = obs.simulate(an_action)

# in this case, simobs will be an APPROXIMATION of the observation you will
# get after performing `an_action`
# obs, *_ = env.step(an_action)

And if your environment allows to use “multiple steps ahead forecast” you can even chain the calls like this:

import grid2op

env_name = "l2rpn_case14_sandbox"
env = grid2op.make(env_name)

obs = env.reset()

an_action = env.action_space()  # or any other action
simobs1, sim_reward1, sim_done1, sim_info1 = obs.simulate(an_action)

another_action = env.action_space()  # or any other action
simobs2, sim_reward2, sim_done2, sim_info2 = simobs1.simulate(another_action)

# in this case, simobs will be an APPROXIMATION of the observation you will
# get after performing `an_action` and then `another_action`:
# *_ = env.step(an_action)
# obs, *_ = env.step(another_action)
Parameters:
Raises:

grid2op.Exceptions.NoForecastAvailable – if no forecast are available for the time_step querried.

Returns:

  • simulated_observation (grid2op.Observation.BaseObservation) – agent’s observation of the current environment after the application of the action “act” on the the current state.

  • reward (float) – amount of reward returned after previous action

  • done (bool) – whether the episode has ended, in which case further step() calls will return undefined results

  • info (dict) – contains auxiliary diagnostic information (helpful for debugging, and sometimes learning)

Notes

This is a simulation in the sense that the “next grid state” is not the real grid state you will get. As you don’t know the future, the “injections you forecast for the next step” will not be the real injection you will get in the next step.

Also, in some circumstances, the “Backend” (ie the powerflow) used to do the simulation may not be the same one as the one used by the environment. This is to model a real fact: as accurate your powerflow is, it does not model all the reality (“all models are wrong”). Having a different solver for the environment ( “the reality”) than the one used to anticipate the impact of the action (this “simulate” function) is a way to represent this fact.

Examples

To simulate what would be the effect of the action “act” if you were to take this action at this step you can do the following:

import grid2op
# retrieve an environment
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)

# retrieve an observation, this is the same for all observations
obs = env.reset()

# and now you can simulate the effect of doing nothing in the next time step
act = env.action_space()  # this can be any action that grid2op understands
simulated_obs, simulated_reward, simulated_done, simulated_info = obs.simulate(act)

# `simulated_obs` will be the "observation" after the application of action `act` on the
#                 " forecast of the grid state (it will be the "forecast state at time t+5mins usually)
# `simulated_reward` will be the reward for the same action on the same forecast state
# `simulated_done` will indicate whether or not the simulation ended up in a "game over"
# `simulated_info` gives extra information on this forecast state

You can now chain the calls to simulate (if your environment supports it)

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)
obs = env.reset()

act_1 = ...  # a grid2op action
# you can do that (if your environment provide forecasts more tha 1 step ahead):
sim_obs_1, *_ = obs.simulate(act_1)

act_2 = ...  # a grid2op action
# but also (if your environment provide forecast more than 2 steps ahead)
sim_obs_2, *_ = sim_obs_1.simulate(act_2)

act_3 = ... # a grid2op action
# but also (if your environment provide forecast more than 3 steps ahead)
sim_obs_3, *_ = sim_obs_2.simulate(act_3)

# you get the idea!

Note

The code above is closely related to the BaseObservation.get_forecast_env() and a very similar result (up to some corner cases beyond the scope of this documentation) could be achieved with:

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)
obs = env.reset()

forecast_env = obs.get_forecast_env()
f_obs = forecast_env.reset()

act_1 = ...  # a grid2op action
f_obs_1, *_ = forecast_env.step(act_1)
# f_obs_1 should be sim_obs_1

act_2 = ...  # a grid2op action
f_obs_2, *_ = forecast_env.step(act_2)
# f_obs_2 should be sim_obs_2

act_3 = ... # a grid2op action
f_obs_3, *_ = forecast_env.step(act_3)
# f_obs_3 should be sim_obs_3

Finally, another possible use of this method is to get a “glimpse” of the effect of an action if you delay it a maximum, you can also use the time_step parameters.

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)
obs = env.reset()

act = ...  # a grid2op action

sim_obs_1, *_ = obs.simulate(act, time_step=1)
sim_obs_2, *_ = obs.simulate(act, time_step=2)
sim_obs_3, *_ = obs.simulate(act, time_step=3)
# in this case:
#    + sim_obs_1 give the results after 1 step (if your agent survives)
#      of applying the action `act`
#    + sim_obs_2 give the results after 2 steps (if your agent survives)
#      of applying the action `act`
#    + sim_obs_3 give the results after 3 steps (if your agent survives)
#      of applying the action `act`

This is an approximation as the “time is not simulated”. Here you only make 1 simulation of the effect of your action regardless of the horizon you want to target. It is related to the Simulator if used this way.

This might be used to chose the “best” time at which you could do an action for example. There is no coupling between the different simulation that you perform here.

state_of(_sentinel=None, load_id=None, gen_id=None, line_id=None, storage_id=None, substation_id=None) Dict[Literal['p', 'q', 'v', 'theta', 'bus', 'sub_id', 'actual_dispatch', 'target_dispatch', 'maintenance', 'cooldown_time', 'storage_power', 'storage_charge', 'storage_power_target', 'storage_theta', 'topo_vect', 'nb_bus', 'origin', 'extremity'], int | float | Dict[Literal['p', 'q', 'v', 'a', 'sub_id', 'bus', 'theta'], int | float]][source]

Return the state of this action on a give unique load, generator unit, powerline of substation. Only one of load, gen, line or substation should be filled.

The querry of these objects can only be done by id here (ie by giving the integer of the object in the backed). The ActionSpace has some utilities to access them by name too.

Parameters:
  • _sentinel (None) – Used to prevent positional parameters. Internal, do not use.

  • load_id (int) – ID of the load we want to inspect

  • gen_id (int) – ID of the generator we want to inspect

  • line_id (int) – ID of the powerline we want to inspect

  • line_id – ID of the powerline we want to inspect

  • storage_id (int) – ID of the storage unit we want to inspect

  • substation_id (int) – ID of the substation unit we want to inspect

Returns:

res – A dictionary with keys and value depending on which object needs to be inspected:

  • if a load is inspected, then the keys are:

    • ”p” the active value consumed by the load

    • ”q” the reactive value consumed by the load

    • ”v” the voltage magnitude of the bus to which the load is connected

    • ”theta” (optional) the voltage angle (in degree) of the bus to which the load is connected

    • ”bus” on which bus the load is connected in the substation

    • ”sub_id” the id of the substation to which the load is connected

  • if a generator is inspected, then the keys are:

    • ”p” the active value produced by the generator

    • ”q” the reactive value consumed by the generator

    • ”v” the voltage magnitude of the bus to which the generator is connected

    • ”theta” (optional) the voltage angle (in degree) of the bus to which the gen. is connected

    • ”bus” on which bus the generator is connected in the substation

    • ”sub_id” the id of the substation to which the generator is connected

    • ”actual_dispatch” the actual dispatch implemented for this generator

    • ”target_dispatch” the target dispatch (cumulation of all previously asked dispatch by the agent) for this generator

  • if a powerline is inspected then the keys are “origin” and “extremity” each being dictionary with keys:

    • ”p” the active flow on line side (extremity or origin)

    • ”q” the reactive flow on line side (extremity or origin)

    • ”v” the voltage magnitude of the bus to which the line side (extremity or origin) is connected

    • ”theta” (optional) the voltage angle (in degree) of the bus to which line side (extremity or origin)

      is connected

    • ”bus” on which bus the line side (extremity or origin) is connected in the substation

    • ”sub_id” the id of the substation to which the line side is connected

    • ”a” the current flow on the line side (extremity or origin)

    In the case of a powerline, additional information are:

    • ”maintenance”: information about the maintenance operation (time of the next maintenance and duration of this next maintenance.

    • ”cooldown_time”: for how many timestep i am not supposed to act on the powerline due to cooldown (see grid2op.Parameters.Parameters.NB_TIMESTEP_COOLDOWN_LINE for more information)

  • if a storage unit is inspected, information are:

    • ”storage_power”: the power the unit actually produced / absorbed

    • ”storage_charge”: the state of the charge of the storage unit

    • ”storage_power_target”: the power production / absorbtion targer

    • ”storage_theta”: (optional) the voltage angle of the bus at which the storage unit is connected

    • ”bus”: the bus (1 or 2) to which the storage unit is connected

    • ”sub_id” : the id of the substation to which the sotrage unit is connected

  • if a substation is inspected, it returns the topology to this substation in a dictionary with keys:

    • ”topo_vect”: the representation of which object is connected where

    • ”nb_bus”: number of active buses in this substations

    • ”cooldown_time”: for how many timestep i am not supposed to act on the substation due to cooldown (see grid2op.Parameters.Parameters.NB_TIMESTEP_COOLDOWN_SUB for more information)

Return type:

dict

Notes

This function can only be used to retrieve the state of the element of the grid, and not the alarm sent or not, to the operator.

Raises:

Grid2OpException – If _sentinel is modified, or if None of the arguments are set or alternatively if 2 or more of the parameters are being set.

property storage_bus: ndarray

Retrieve the busbar at which each storage unit is connected.

The result follow grid2op convention:

  • -1 means the storage unit is disconnected

  • 1 means it is storage unit to busbar 1

  • 2 means it is connected to busbar 2

  • etc.

Notes

In a same substation, two objects are connected together if (and only if) they are connected to the same busbar.

sub_topology(sub_id) ndarray[source]

Returns the topology of the given substation.

We remind the reader that for substation id sud_id, its topology is represented by a vector of length type(obs).subs_info[sub_id] elements. And for each elements of this vector, you now on which bus (1 or 2) it is connected or if the corresponding element is disconnected (in this case it’s -1)

property thermal_limit: ndarray

Return the thermal limit of the powergrid, given in Amps (A)

Examples

import grid2op
env_name = "l2rpn_case14_sandbox"  # or any other name
env = grid2op.make(env_name)

obs = env.reset()
thermal_limit = obs.thermal_limit
to_dict()[source]

Transform this observation as a dictionary. This dictionary allows you to inspect the state of this observation and is simply a shortcut of the class instance.

Return type:

A dictionary representing the observation.

Notes

The returned dictionary is not necessarily json serializable. To have a grid2op observation that you can serialize in a json fashion, please use the grid2op.Space.GridObjects.to_json() function.

Note

This function is different to the grid2op.Space.GridObjects.to_dict(). Indeed the dictionnary resulting from this function will count as keys all the attributes in GridObjects.attr_list_vect only.

Concretely, if obs is an observation (grid2op.Observation.BaseObservation) then obs.to_dict() will have the keys type(obs).attr_list_vect and the values will be numpy arrays whereas obs.to_json() will have the keys type(obs).attr_list_vect and type(obs).attr_list_json and the values will be lists (serializable).

abstractmethod update(env: Environment, with_forecast: bool = True) None[source]

INTERNAL

Warning

/!\ Internal, do not use unless you know what you are doing /!\ This is carried out automatically by the environment in env.step

Update the actual instance of BaseObservation with the new received value from the environment.

An observation is a description of the powergrid perceived by an agent. The agent takes his decision based on the current observation and the past rewards.

This method update receive complete detailed information about the powergrid, but that does not mean an agent sees everything. For example, it is possible to derive this class to implement some noise in the generator or load, or flows to mimic sensor inaccuracy.

It is also possible to give fake information about the topology, the line status etc.

In the Grid2Op framework it’s also through the observation that the agent has access to some forecast (the way forecast are handled depends are implemented in this class). For example, forecast data (retrieved thanks to chronics_handler) are processed, but can be processed differently. One can apply load / production forecast to each _grid state, or to make forecast for one “reference” _grid state valid a whole day and update this one only etc. All these different mechanisms can be implemented in Grid2Op framework by overloading the update observation method.

This class is really what a dispatcher observes from it environment. It can also include some temperatures, nebulosity, wind etc. can also be included in this class.

Notes

We strongly recommend to call BaseObservation.reset when implementing this function.

update_after_reward(env: BaseEnv) None[source]

Only called for the regular environment (so not available for BaseObservation.get_forecast_env() or BaseObservation.simulate())

Warning

You probably don’t have to use except if you develop a specific observation class !

Note

If you want to develop a new type of observation with a new type of reward, you can use the env._reward_to_obs attribute (dictionary) in the reward to pass information to the observation (in this function).

Basically, update env._reward_to_obs in the reward, and use the values in env._reward_to_obs in this function.

New in version 1.9.1.

Parameters:

env (grid2op.Environment.BaseEnv) – The environment with which to update the observation

where_different(other: Self) Tuple[Self, List][source]

Returns the difference between two observation.

Parameters:

other – Other action to compare

Returns:

  • diff_ (BaseObservation) – The observation showing the difference between self and other

  • attr_nm (list) – List of string representing the names of the different attributes. It’s [] if the two observations are identical.

class grid2op.Observation.CompleteObservation(obs_env=None, action_helper=None, random_prng=None, kwargs_env=None)[source]

This class represent a complete observation, where everything on the powergrid can be observed without any noise.

This is the only BaseObservation implemented (and used) in Grid2Op. Other type of observation, for other usage can of course be implemented following this example.

It has the same attributes as the BaseObservation class. Only one is added here.

For a CompleteObservation the unique representation as a vector is:

  1. BaseObservation.year the year [1 element]

  2. BaseObservation.month the month [1 element]

  3. BaseObservation.day the day [1 element]

  4. BaseObservation.hour_of_day the hour of the day [1 element]

  5. BaseObservation.minute_of_hour minute of the hour [1 element]

  6. BaseObservation.day_of_week the day of the week. Monday = 0, Sunday = 6 [1 element]

  7. BaseObservation.gen_p the active value of the productions [grid2op.Space.GridObjects.n_gen elements]

  8. BaseObservation.gen_q the reactive value of the productions [grid2op.Space.GridObjects.n_gen elements]

  9. BaseObservation.gen_v the voltage setpoint of the productions [grid2op.Space.GridObjects.n_gen elements]

  10. BaseObservation.load_p the active value of the loads [grid2op.Space.GridObjects.n_load elements]

  11. BaseObservation.load_q the reactive value of the loads [grid2op.Space.GridObjects.n_load elements]

  12. BaseObservation.load_v the voltage setpoint of the loads [grid2op.Space.GridObjects.n_load elements]

  13. BaseObservation.p_or active flow at origin of powerlines [grid2op.Space.GridObjects.n_line elements]

  14. BaseObservation.q_or reactive flow at origin of powerlines [grid2op.Space.GridObjects.n_line elements]

  15. BaseObservation.v_or voltage at origin of powerlines [grid2op.Space.GridObjects.n_line elements]

  16. BaseObservation.a_or current flow at origin of powerlines [grid2op.Space.GridObjects.n_line elements]

  17. BaseObservation.p_ex active flow at extremity of powerlines [grid2op.Space.GridObjects.n_line elements]

  18. BaseObservation.q_ex reactive flow at extremity of powerlines [grid2op.Space.GridObjects.n_line elements]

  19. BaseObservation.v_ex voltage at extremity of powerlines [grid2op.Space.GridObjects.n_line elements]

  20. BaseObservation.a_ex current flow at extremity of powerlines [grid2op.Space.GridObjects.n_line elements]

  21. BaseObservation.rho line capacity used (current flow / thermal limit) [grid2op.Space.GridObjects.n_line elements]

  22. BaseObservation.line_status line status [grid2op.Space.GridObjects.n_line elements]

  23. BaseObservation.timestep_overflow number of timestep since the powerline was on overflow (0 if the line is not on overflow)[grid2op.Space.GridObjects.n_line elements]

  24. BaseObservation.topo_vect representation as a vector of the topology [for each element it gives its bus]. See grid2op.Backend.Backend.get_topo_vect() for more information.

  25. BaseObservation.time_before_cooldown_line representation of the cooldown time on the powerlines [grid2op.Space.GridObjects.n_line elements]

  26. BaseObservation.time_before_cooldown_sub representation of the cooldown time on the substations [grid2op.Space.GridObjects.n_sub elements]

  27. BaseObservation.time_next_maintenance number of timestep before the next maintenance (-1 means no maintenance are planned, 0 a maintenance is in operation) [grid2op.Space.GridObjects.n_line elements]

  28. BaseObservation.duration_next_maintenance duration of the next maintenance. If a maintenance is taking place, this is the number of timestep before it ends. [grid2op.Space.GridObjects.n_line elements]

  29. BaseObservation.target_dispatch the target dispatch for each generator [grid2op.Space.GridObjects.n_gen elements]

  30. BaseObservation.actual_dispatch the actual dispatch for each generator [grid2op.Space.GridObjects.n_gen elements]

  31. BaseObservation.storage_charge the actual state of charge of each storage unit [grid2op.Space.GridObjects.n_storage elements]

  32. BaseObservation.storage_power_target the production / consumption of setpoint of each storage unit [grid2op.Space.GridObjects.n_storage elements]

  33. BaseObservation.storage_power the realized production / consumption of each storage unit [grid2op.Space.GridObjects.n_storage elements]

  34. BaseObservation.gen_p_before_curtail : the theoretical generation that would have happened if no generator from renewable energy sources have been performed (in MW) [grid2op.Space.GridObjects.n_gen elements]

  35. BaseObservation.curtailment : the current curtailment applied [grid2op.Space.GridObjects.n_gen elements]

  36. BaseObservation.is_alarm_illegal whether the last alarm has been illegal (due to budget constraint) [bool], .. warning: /!\ Only valid with “l2rpn_icaps_2021” environment /!\

  37. BaseObservation.curtailment_limit : the current curtailment limit (if any) [grid2op.Space.GridObjects.n_gen elements]

  38. BaseObservation.time_since_last_alarm number of step since the last alarm has been raised successfully [int] .. warning: /!\ Only valid with “l2rpn_icaps_2021” environment /!\

  39. BaseObservation.last_alarm : for each alarm zone, gives the last step at which an alarm has been successfully raised at this zone .. warning: /!\ Only valid with “l2rpn_icaps_2021” environment /!\ [grid2op.Space.GridObjects.dim_alarms elements]

  40. BaseObservation.attention_budget : the current attention budget [int]

  41. BaseObservation.was_alarm_used_after_game_over : was the last alarm used to compute anything related to the attention budget when there was a game over (can only be set to True if the observation corresponds to a game over), warning: /!\ Only valid with “l2rpn_icaps_2021” environment /!\ [bool]

  42. BaseObservation.is_alarm_illegal whether the last alert has been illegal (due to budget constraint) [bool]

  43. BaseObservation.curtailment_limit : the current curtailment limit (if any) [grid2op.Space.GridObjects.n_gen elements]

  44. BaseObservation.curtailment_limit_effective Limit (in ratio of gen_pmax) imposed on each renewable generator effectively imposed by the environment.

  45. BaseObservation.current_step the number of steps since the beginning of the episode (it’s 0 for the observation after a call to env.reset())

  46. BaseObservation.max_step maximum number of steps that can be done by the environment. When BaseObservation.current_step is BaseObservation.max_step the the environment is done.

  47. BaseObservation.delta_time Amount of time (in minutes) represented by a step. In general, there are the equivalent of 5 minutes between two steps.

  48. BaseObservation.gen_margin_up From how much can you increase each generators production between this step and the next.

  49. BaseObservation.gen_margin_down From how much can you decrease each generators production between this step and the next.

  50. BaseObservation.active_alert This attribute gives the lines “under alert” at the given observation.

  51. BaseObservation.time_since_last_alert Give the time since an alert has been raised for each powerline.

  52. BaseObservation.alert_duration Give the time since an alert has started for all attackable line.

  53. BaseObservation.total_number_of_alert Total number of alerts since the beginning of the episode sent by the agent

  54. BaseObservation.time_since_last_attack For each attackable line i it counts the number of steps since the powerline has been attacked

  55. BaseObservation.was_alert_used_after_attack For each attackable line i it says if an alert has been used or not for the computation of the reward: +1 means “used and the alert was correct”, -1 means “used and the alert was not correct” and 0 means “not used”

  56. BaseObservation.attack_under_alert For each attackable line i it says if an alert has been sent (+1) or not (-1) for each attackable line currently under attack.

Methods:

__init__([obs_env, action_helper, ...])

nothing to do when an object of this class is created, the information is held by the class attributes

update(env[, with_forecast])

INTERNAL

__init__(obs_env=None, action_helper=None, random_prng=None, kwargs_env=None)[source]

nothing to do when an object of this class is created, the information is held by the class attributes

update(env, with_forecast=True)[source]

INTERNAL

Warning

/!\ Internal, do not use unless you know what you are doing /!\ This is carried out automatically by the environment in env.step

Update the actual instance of BaseObservation with the new received value from the environment.

An observation is a description of the powergrid perceived by an agent. The agent takes his decision based on the current observation and the past rewards.

This method update receive complete detailed information about the powergrid, but that does not mean an agent sees everything. For example, it is possible to derive this class to implement some noise in the generator or load, or flows to mimic sensor inaccuracy.

It is also possible to give fake information about the topology, the line status etc.

In the Grid2Op framework it’s also through the observation that the agent has access to some forecast (the way forecast are handled depends are implemented in this class). For example, forecast data (retrieved thanks to chronics_handler) are processed, but can be processed differently. One can apply load / production forecast to each _grid state, or to make forecast for one “reference” _grid state valid a whole day and update this one only etc. All these different mechanisms can be implemented in Grid2Op framework by overloading the update observation method.

This class is really what a dispatcher observes from it environment. It can also include some temperatures, nebulosity, wind etc. can also be included in this class.

Notes

We strongly recommend to call BaseObservation.reset when implementing this function.

class grid2op.Observation.HighResSimCounter[source]

This classes helps to count the total number of call to “high fidelity simulator” the agent made.

Methods:

__init__()

Attributes:

__weakref__

list of weak references to the object (if defined)

__init__() None[source]
__weakref__

list of weak references to the object (if defined)

class grid2op.Observation.NoisyObservation(obs_env=None, action_helper=None, random_prng=None, kwargs_env=None, sigma_load_p=0.01, sigma_load_q=0.01, sigma_gen_p=0.01, sigma_gen_q=0.01, sigma_a=0.01, sigma_p=0.1, sigma_q=0.1, sigma_storage=0.1)[source]

This class represent a complete observation (in the sens that all attributes of an CompleteObservation are accessible) but some of them are “noisy”.

That is, the observation that the agent has access to is not exactly the same as the environment internal values.

The affected attributes are :

  • load_p: *= lognormal (to keep the sign)

  • load_q: *= lognormal (to keep the sign)

  • gen_p: *= lognormal (to keep the sign)

  • gen_q: *= lognormal (to keep the sign)

  • p_or += normal

  • p_ex += normal

  • q_or += normal

  • q_ex += normal

  • a_or: *= lognormal (to keep the sign)

  • a_ex: *= lognormal (to keep the sign)

  • rho: same noise as a_or (because rho is not “physical” it’s the result of a computation)

  • storage_power += normal

It can be used to emuate the acquisition of data coming from noisy sensors for example.

Examples

It can be used as follow:

import grid2op

env_name = "l2rpn_case14_sandbox"  # or any other name
kwargs_observation = {"sigma_load_p": 0.1, "sigma_gen_p": 1.0}  # noise of the observation
env = grid2op.make(env_name,
                   observation_class=NoisyObservation,
                   kwargs_observation=kwargs_observation)

# do whatever you want with env !

Methods:

__init__([obs_env, action_helper, ...])

nothing to do when an object of this class is created, the information is held by the class attributes

update(env[, with_forecast])

INTERNAL

__init__(obs_env=None, action_helper=None, random_prng=None, kwargs_env=None, sigma_load_p=0.01, sigma_load_q=0.01, sigma_gen_p=0.01, sigma_gen_q=0.01, sigma_a=0.01, sigma_p=0.1, sigma_q=0.1, sigma_storage=0.1)[source]

nothing to do when an object of this class is created, the information is held by the class attributes

update(env, with_forecast=True)[source]

INTERNAL

Warning

/!\ Internal, do not use unless you know what you are doing /!\ This is carried out automatically by the environment in env.step

Update the actual instance of BaseObservation with the new received value from the environment.

An observation is a description of the powergrid perceived by an agent. The agent takes his decision based on the current observation and the past rewards.

This method update receive complete detailed information about the powergrid, but that does not mean an agent sees everything. For example, it is possible to derive this class to implement some noise in the generator or load, or flows to mimic sensor inaccuracy.

It is also possible to give fake information about the topology, the line status etc.

In the Grid2Op framework it’s also through the observation that the agent has access to some forecast (the way forecast are handled depends are implemented in this class). For example, forecast data (retrieved thanks to chronics_handler) are processed, but can be processed differently. One can apply load / production forecast to each _grid state, or to make forecast for one “reference” _grid state valid a whole day and update this one only etc. All these different mechanisms can be implemented in Grid2Op framework by overloading the update observation method.

This class is really what a dispatcher observes from it environment. It can also include some temperatures, nebulosity, wind etc. can also be included in this class.

Notes

We strongly recommend to call BaseObservation.reset when implementing this function.

class grid2op.Observation.ObservationSpace(gridobj, env, rewardClass=None, observationClass=<class 'grid2op.Observation.completeObservation.CompleteObservation'>, actionClass=None, with_forecast=True, kwargs_observation=None, observation_bk_class=None, observation_bk_kwargs=None, logger=None, _with_obs_env=True, _local_dir_cls=None)[source]

Helper that provides useful functions to manipulate BaseObservation.

BaseObservation should only be built using this Helper. It is absolutely not recommended to make an observation directly form its constructor.

This class represents the same concept as the “BaseObservation Space” in the OpenAI gym framework.

with_forecast

If True the BaseObservation.simulate() will be available. If False it will deactivate this possibility. If simulate function is not used, setting it to False can lead to non neglectible speed-ups.

Type:

bool

observationClass

Class used to build the observations. It defaults to CompleteObservation

Type:

type

_simulate_parameters

Type of Parameters used to compute powerflow for the forecast.

Type:

grid2op.Parameters.Parameters

rewardClass

Class used by the grid2op.Environment.Environment to send information about its state to the grid2op.Agent.BaseAgent. You can change this class to differentiate between the reward of output of BaseObservation.simulate() and the reward used to train the BaseAgent.

Type:

Union[type, BaseReward]

action_helper_env

BaseAction space used to create action during the BaseObservation.simulate()

Type:

grid2op.Action.ActionSpace

reward_helper

BaseReward function used by the the BaseObservation.simulate() function.

Type:

grid2op.Reward.RewardHelper

obs_env

Instance of the environment used by the BaseObservation Helper to provide forcecast of the grid state.

Type:

grid2op.Environment._Obsenv._ObsEnv

_empty_obs

An instance of the observation with appropriate dimensions. It is updated and will be sent to he BaseAgent.

Type:

BaseObservation

Methods:

__call__(env[, _update_state])

Call self as a function.

__init__(gridobj, env[, rewardClass, ...])

INTERNAL

can_use_simulate()

This checks on the rules if the agent has not made too many calls to "obs.simulate" this step

change_other_rewards(dict_reward)

this function is used to change the "other rewards" used when you perform simulate.

copy([copy_backend, env])

INTERNAL

get_empty_observation()

INTERNAL

reset(real_env)

reset the observation space with the new values of the environment

simulate_called()

INTERNAL

size_obs()

Size if the observation vector would be flatten :return:

__call__(env, _update_state=True)[source]

Call self as a function.

__init__(gridobj, env, rewardClass=None, observationClass=<class 'grid2op.Observation.completeObservation.CompleteObservation'>, actionClass=None, with_forecast=True, kwargs_observation=None, observation_bk_class=None, observation_bk_kwargs=None, logger=None, _with_obs_env=True, _local_dir_cls=None)[source]

INTERNAL

Warning

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

Env: requires grid2op.Environment.BaseEnv.parameters and grid2op.Environment.BaseEnv.backend to be valid

can_use_simulate() bool[source]

This checks on the rules if the agent has not made too many calls to “obs.simulate” this step

change_other_rewards(dict_reward)[source]

this function is used to change the “other rewards” used when you perform simulate.

This can be used, for example, when you want to do faster call to “simulate”. In this case you can remove all the “other_rewards” that will be used by the simulate function.

Parameters:

dict_reward (dict) – see description of grid2op.Environment.BaseEnv.other_rewards

Examples

If you want to deactivate the reward in the simulate function, you can do as following:

import grid2op
from grid2op.Reward import CloseToOverflowReward, L2RPNReward, RedispReward
env_name = "l2rpn_case14_sandbox"
other_rewards = {"close_overflow": CloseToOverflowReward,
                 "l2rpn": L2RPNReward,
                 "redisp": RedispReward}
env = grid2op.make(env_name, other_rewards=other_rewards)

env.observation_space.change_other_rewards({})
copy(copy_backend=False, env=None)[source]

INTERNAL

Warning

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

Perform a deep copy of the Observation space.

get_empty_observation()[source]

INTERNAL

Warning

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

return an empty observation, for internal use only.

reset(real_env)[source]

reset the observation space with the new values of the environment

simulate_called()[source]

INTERNAL

Warning

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

Tells this class that the “obs.simulate” function has been called.

size_obs()[source]

Size if the observation vector would be flatten :return:

If you still can’t find what you’re looking for, try in one of the following pages:

Still trouble finding the information ? Do not hesitate to send a github issue about the documentation at this link: Documentation issue template