Simulator

This page is organized as follow:

Objectives

The “Simulator” class is used to interact, with a grid2op “api-like” (the concept of action and observation) with a powergrid simulator that is independant of the one used by the environment.

This can for example be used for “model based reinforcement learning” or to assess the “validity” of an action in some “more extreme” conditions.

It behaves similarly to env.step(…) or obs.simulate(…) with a few key differences:

  • you can “chain” the call to simulator: simulator.predict(…).predict(…).predict(…)

  • it does not take into account the “time”: no cooldown on lines nor substation, storage “state of charge” (energy) does not decrease when you use them

  • no automatic line disconnection: lines are not disconnected when they are above their limit

  • no opponent will act on the grid

Usage

You can use it to assess if the grid state is “resilient enough” if the loads and generators increase of 5% :

import grid2op
env_name = ...  # any environment name available (eg. "l2rpn_case14_sandbox")
env = grid2op.make(env_name)

obs = env.reset()

#### later in the code, for example in an Agent:

simulator = obs.get_simulator()

load_p_stressed = obs.load_p * 1.05
gen_p_stressed = obs.gen_p * 1.05
do_nothing = env.action_space()
simulator_stressed = simulator.predict(act=do_nothing,
                                       new_gen_p=gen_p_stressed,
                                       new_load_p=load_p_stressed)
if not simulator_stressed.converged:
    # the solver fails to find a solution for this action
    # you are likely to run into trouble if you use that...
    ...  # do something
obs_stressed = simulator_stressed.current_obs

You can also “chain” the call to simulators, usefull for “model based” strategies

import grid2op
env_name = ...  # any environment name available (eg. "l2rpn_case14_sandbox")
env = grid2op.make(env_name)

obs = env.reset()

#### later in the code, for example in an Agent:

simulator = obs.get_simulator()

load_p_stressed = obs.load_p * 1.05
gen_p_stressed = obs.gen_p * 1.05
do_nothing = env.action_space()
simulator_stressed = simulator.predict(act=do_nothing, new_gen_p=gen_p_stressed, new_load_p=load_p_stressed)
if not simulator_stressed.converged:
    # the solver fails to find a solution for this action
    # you are likely to run into trouble if you use that...
    ...  # do something

act1 = ...
simulator_afteract1 = simulator_stressed.predict(act=act1)
act2 = ...
simulator_afteract2 = simulator_stressed.predict(act=act2)
# etc.

Another use (though you might need to use dedicated tools for such purpose such as https://lightsim2grid.readthedocs.io/en/latest/security_analysis.html ) is to check whether your grid is “N-1 secure” which means that if a powerline were to be disconnected (for example by the opponent) then the state it is in would not be too dangerous:

import numpy as np
import grid2op
env_name = ...  # any environment name available (eg. "l2rpn_case14_sandbox")
env = grid2op.make(env_name)

obs = env.reset()

#### later in the code, for example in an Agent:
simulator = obs.get_simulator()
act1 = ...  # the action your agent has selected
simulator_afteract1 = simulator.predict(act=act1)

for line_id in range(obs.n_line):
    act_disco_line = env.action_space()
    act_disco_line.set_line_status = [(line_id, -1)]
    sim_after_disco_line = simulator_afteract1.predict(act_disco_line)
    if sim_after_disco_line.converged:
        obs_after_disco = sim_after_disco_line.current_obs
        # and for example check the current on all line is bellow the limit
        if np.any(obs_after_disco.rho > 1.):
            # your action does not make the grid "N-1" safe,
            # because if you disconnect line `line_id` you got some overflow
            ...  # do something
    else:
        # the simulator has not found any solution, it is likely that this will lead
        # to a game over in "real time"
        # your grid is not "N-1" safe because disconnection of line `line_id` lead
        # to a non feasible grid state
            ...  # do something

Warning

This module is still under development, if you need any functionality, let us know with a github “feature request” (https://github.com/rte-france/Grid2Op/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)

Detailed Documentation by class

Classes:

Simulator(backend[, env, tol_redisp, ...])

This class represents a "simulator".

class grid2op.simulator.Simulator(backend: Backend | None, env: BaseEnv | None = None, tol_redisp=1e-06, _highres_sim_counter: HighResSimCounter | None = None)[source]

Bases: object

This class represents a “simulator”. It allows to check the impact on this or that on th powergrid, quite like what human operators have at their disposal in control rooms.

It behaves similarly to env.step(…) or obs.simulate(…) with a few key differences:

  • you can “chain” the call to simulator: simulator.predict(…).predict(…).predict(…)

  • it does not take into account the “time”: no cooldown on lines nor substation, storage “state of charge” (energy) does not decrease when you use them

  • no automatic line disconnection: lines are not disconnected when they are above their limit

  • no opponent will act on the grid

Please see the documentation for usage examples.

Methods:

__init__(backend[, env, tol_redisp, ...])

change_backend(backend)

You can use this function in case you want to change the "solver" use to perform the computation.

change_backend_type(backend_type, grid_path, ...)

It allows to change the type of the backend used

close()

close the underlying backend

copy()

Allows to perform a (deep) copy of the simulator.

predict(act[, new_gen_p, new_gen_v, ...])

Predict the state of the grid after a given action has been taken.

set_state([obs, do_powerflow, new_gen_p, ...])

Set the state of the simulator to a given state described by an observation (and optionally some new loads and generation)

Attributes:

__weakref__

list of weak references to the object (if defined)

converged

returns: Whether or not the powerflow has converged :rtype: bool

__init__(backend: Backend | None, env: BaseEnv | None = None, tol_redisp=1e-06, _highres_sim_counter: HighResSimCounter | None = None)[source]
__weakref__

list of weak references to the object (if defined)

change_backend(backend: Backend) None[source]

You can use this function in case you want to change the “solver” use to perform the computation.

For example, you could use a machine learning based model to do the computation (to accelerate them), provided that you have at your disposal such an algorithm.

Warning

The backend you pass as argument should be initialized with the same grid as the one currently in use.

Notes

Once changed, all the “simulator” that “derived” from this simulator will use the same backend types.

Parameters:

backend (Backend) – Another grid2op backend you can use to perform the computation.

Raises:

SimulatorError – When you do not pass a correct backend.

change_backend_type(backend_type: type, grid_path: PathLike, **kwargs)[source]

It allows to change the type of the backend used

Parameters:
  • backend_type (type) – The new backend type

  • grid_path (os.PathLike) – The path from where to load the powergrid

  • kwargs – Extra arguments used to build the backend.

Notes

Once changed, all the “simulator” that “derived” from this simulator will use the same backend types.

Raises:

SimulatorError – if something went wrong (eg you do not pass a type, your type does not inherit from Backend, the file located at grid_path does not exists etc.)

close()[source]

close the underlying backend

property converged: bool

returns: Whether or not the powerflow has converged :rtype: bool

copy() Self[source]

Allows to perform a (deep) copy of the simulator.

Returns:

A (deep) copy of the simulator you want to copy.

Return type:

Simulator

Raises:

SimulatorError – In case the simulator is not initialized.

predict(act: BaseAction, new_gen_p: ndarray | None = None, new_gen_v: ndarray | None = None, new_load_p: ndarray | None = None, new_load_q: ndarray | None = None, do_copy: bool = True) Simulator[source]

Predict the state of the grid after a given action has been taken.

Parameters:
  • act (BaseAction) – The action you want to take

  • new_gen_p (np.ndarray, optional) – the new production active setpoint, by default None

  • new_gen_v (np.ndarray, optional) – the new production voltage setpoint, by default None

  • new_load_p (np.ndarray, optional) – the new consumption active values, by default None

  • new_load_q (np.ndarray, optional) – the new consumption reactive values, by default None

  • do_copy (bool, optional) – Whether to make a copy or not, by default True

Examples

A possible example is:

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

obs = env.reset()

#### later in the code, for example in an Agent:

simulator = obs.get_simulator()

load_p_stressed = obs.load_p * 1.05
gen_p_stressed = obs.gen_p * 1.05
do_nothing = env.action_space()
simulator_stressed = simulator.predict(act=do_nothing,
                                    new_gen_p=gen_p_stressed,
                                    new_load_p=load_p_stressed)
if not simulator_stressed.converged:
    # the solver fails to find a solution for this action
    # you are likely to run into trouble if you use that...
    ...  # do something
obs_stressed = simulator_stressed.current_obs
Returns:

The new simulator representing the grid state after the simulation of the action.

Return type:

Simulator

set_state(obs: BaseObservation | None = None, do_powerflow: bool = True, new_gen_p: ndarray | None = None, new_gen_v: ndarray | None = None, new_load_p: ndarray | None = None, new_load_q: ndarray | None = None, update_thermal_limit: bool = True)[source]

Set the state of the simulator to a given state described by an observation (and optionally some new loads and generation)

Parameters:
  • obs (Optional[BaseObservation], optional) – The observation to get the state from, by default None

  • do_powerflow (bool, optional) – Whether to use the underlying backend to get a consistent state after this modification or not, by default True

  • new_gen_p (np.ndarray, optional) – new generator active setpoint, by default None

  • new_gen_v (np.ndarray, optional) – new generator voltage setpoint, by default None

  • new_load_p (np.ndarray, optional) – new load active consumption, by default None

  • new_load_q (np.ndarray, optional) – new load reactive consumption, by default None

  • update_thermal_limit (bool, optional) – Do you update the thermal limit of the backend (we recommend to leave it to True otherwise some bugs can appear such as https://github.com/rte-france/Grid2Op/issues/377)

Raises:

SimulatorError – In case the current simulator is not initialized.

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