Skip to content

Commit

Permalink
fixing tests for N1ContingencyReward
Browse files Browse the repository at this point in the history
  • Loading branch information
BDonnot committed Mar 25, 2024
1 parent 0657680 commit 6ef1a7a
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 41 deletions.
26 changes: 13 additions & 13 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
steps:

- name: Checkout sources
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: true

Expand Down Expand Up @@ -117,13 +117,13 @@ jobs:
python3 -v -c "from lightsim2grid import LightSimBackend; import grid2op; env = grid2op.make('l2rpn_case14_sandbox', test=True, backend=LightSimBackend())"

- name: Upload wheel
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lightsim2grid-wheel-linux-${{ matrix.python.name }}
path: wheelhouse/*.whl

- name: Upload source archive
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: matrix.python.name == 'cp311'
with:
name: lightsim2grid-sources
Expand Down Expand Up @@ -167,12 +167,12 @@ jobs:
steps:

- name: Checkout sources
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: true

- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python.version }}

Expand Down Expand Up @@ -220,7 +220,7 @@ jobs:
python -c "from lightsim2grid import LightSimBackend; import grid2op; env = grid2op.make('l2rpn_case14_sandbox', test=True, backend=LightSimBackend())"
- name: Upload wheel
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lightsim2grid-wheel-${{ matrix.config.name }}-${{ matrix.python.name }}
path: dist/*.whl
Expand All @@ -238,12 +238,12 @@ jobs:
}
steps:
- name: Checkout sources
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: true

- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python.version }}

Expand Down Expand Up @@ -282,7 +282,7 @@ jobs:
python -c "from lightsim2grid import LightSimBackend; import grid2op; env = grid2op.make('l2rpn_case14_sandbox', test=True, backend=LightSimBackend())"
- name: Upload wheel
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lightsim2grid-wheel-darwin-${{ matrix.python.name }}
path: dist/*.whl
Expand Down Expand Up @@ -321,12 +321,12 @@ jobs:
steps:

- name: Checkout sources
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
submodules: true

- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python.version }}

Expand Down Expand Up @@ -356,7 +356,7 @@ jobs:
python -c "from lightsim2grid import LightSimBackend; import grid2op; env = grid2op.make('l2rpn_case14_sandbox', test=True, backend=LightSimBackend())"
- name: Upload wheel
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: wheels-darwin-${{ matrix.python.name }}
path: ./wheelhouse/*.whl
Expand All @@ -373,7 +373,7 @@ jobs:
path: download

- name: Upload wheels
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: lightsim2grid-wheels
path: |
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,16 @@ Change Log
change the topology of the grid)
- [ADDED] a "reward" module in lightsim2grid with custom reward
based on lightsim2grid.
- [ADDED] a class `N1ContingencyReward` that can leverage lightsim2grid to
assess the number of safe / unsafe N-1.
- [IMPROVED] time measurments in python and c++
- [IMPROVED] now test lightsim2grid with oldest grid2op version
- [IMPROVED] speed, by accelerating the reading back of the data (now read only once and then
pointers are re used)
- [IMPROVED] c++ side avoid allocating memory (which allow to gain speed python side too)
- [IMPROVED] type hinting in `LightSimBackend` for all 'public' methods (most
notably the one used by grid2op)
- [IMPROVED] now the benchmarks are more verbose (detailing some compilation options)

[0.8.0] 2024-03-18
--------------------
Expand Down
36 changes: 35 additions & 1 deletion lightsim2grid/lightSimBackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,11 +690,45 @@ def _aux_setup_right_after_grid_init(self):
self._grid._max_nb_bus_per_sub = self.n_busbar_per_sub

self._grid.tell_solver_need_reset()


def init_from_loaded_pandapower(self, pp_net):
if hasattr(type(self), "can_handle_more_than_2_busbar"):
type(self.init_pp_backend).n_busbar_per_sub = self.n_busbar_per_sub
self.init_pp_backend = pp_net.copy()
self._aux_init_pandapower()

# handles redispatching
if type(pp_net).redispatching_unit_commitment_availble:
self.redispatching_unit_commitment_availble = True
for attr_nm in ["gen_type", "gen_pmin", "gen_pmax",
"gen_redispatchable", "gen_max_ramp_up",
"gen_max_ramp_down", "gen_min_uptime",
"gen_min_downtime", "gen_cost_per_MW",
"gen_startup_cost", "gen_shutdown_cost",
"gen_renewable"
]:
setattr(self, attr_nm, copy.deepcopy(getattr( type(pp_net), attr_nm)))

# handles storages
for attr_nm in ["storage_type",
"storage_Emax",
"storage_Emin",
"storage_max_p_prod" ,
"storage_max_p_absorb",
"storage_marginal_cost",
"storage_loss",
"storage_charging_efficiency",
"storage_discharging_efficiency",
]:
setattr(self, attr_nm, copy.deepcopy(getattr( type(pp_net), attr_nm)))

def _load_grid_pandapower(self, path=None, filename=None):
if hasattr(type(self), "can_handle_more_than_2_busbar"):
type(self.init_pp_backend).n_busbar_per_sub = self.n_busbar_per_sub
self.init_pp_backend.load_grid(path, filename)
self._aux_init_pandapower()

def _aux_init_pandapower(self):
self.can_output_theta = True # i can compute the "theta" and output it to grid2op

self._grid = init(self.init_pp_backend._grid)
Expand Down
35 changes: 19 additions & 16 deletions lightsim2grid/rewards/n1ContingencyReward.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
import time
import numpy as np

import grid2op
from grid2op.Reward import BaseReward
from grid2op.Environment import Environment
from grid2op.Backend import PandaPowerBackend
from grid2op.Action._backendAction import _BackendAction

from lightsim2grid import LightSimBackend, ContingencyAnalysis
Expand Down Expand Up @@ -52,14 +51,14 @@ def __init__(self,
tol=1e-8,
nb_iter=10):
BaseReward.__init__(self, logger=logger)
self._backend = None
self._backend : LightSimBackend = None
self._backend_action = None
self._l_ids = None
self._dc = dc
self._normalize = normalize
self._dc : bool = dc
self._normalize : bool = normalize
if l_ids is not None:
self._l_ids = [int(el) for el in l_ids]
self._threshold_margin = float(threshold_margin)
self._threshold_margin :float = float(threshold_margin)
if klu_solver_available:
if self._dc:
self._solver_type = SolverType.KLUDC
Expand All @@ -78,7 +77,13 @@ def __init__(self,
self._timer_compute = 0.
self._timer_post_proc = 0.

def initialize(self, env: Environment):
def initialize(self, env: "grid2op.Environment.Environment"):
from grid2op.Environment import Environment
from grid2op.Backend import PandaPowerBackend # lazy import because grid2op -> pandapower-> lightsim2grid -> grid2op
if not isinstance(env, Environment):
raise RuntimeError("You can only initialize this reward with a "
"proper grid2op environment")

if not isinstance(env.backend, (PandaPowerBackend, LightSimBackend)):
raise RuntimeError("Impossible to use the `N1ContingencyReward` with "
"a environment with a backend that is not "
Expand All @@ -88,10 +93,9 @@ def initialize(self, env: Environment):
self._backend : LightSimBackend = env.backend.copy()
self._backend_ls :bool = True
elif isinstance(env.backend, PandaPowerBackend):
from lightsim2grid.gridmodel import init
gridmodel = init(env.backend._grid)
self._backend = LightSimBackend.init_grid(type(env.backend))()
self._backend._grid = gridmodel
self._backend.init_from_loaded_pandapower(env.backend)
self._backend.is_loaded = True
else:
raise NotImplementedError()

Expand Down Expand Up @@ -143,22 +147,21 @@ def __call__(self, action, env, has_error, is_done, is_illegal, is_ambiguous):
self._timer_compute += now_2 - now_
if self._dc:
# In DC is study p, but take into account q in the limits
res = np.abs(tmp[0]) # this is Por
tmp_res = np.abs(tmp[0]) # this is Por
# now transform the limits in A in MW
por, qor, vor, aor = env.backend.lines_or_info()
p_sq = (1e-3*th_lim_a)**2 * 3. * vor**2 - qor**2
p_sq[p_sq <= 0.] = 0.
limits = np.sqrt(p_sq)
else:
res = tmp[1]
tmp_res = tmp[1]
limits = th_lim_a
# print("Reward:")
# print(res)
# print(tmp_res)
# print(self._threshold_margin * limits)
res = ((res > self._threshold_margin * limits) | (~np.isfinite(res))).any(axis=1) # whether one powerline is above its limit, per cont
res = ((tmp_res > self._threshold_margin * limits) | (~np.isfinite(tmp_res))).any(axis=1) # whether one powerline is above its limit, per cont
res |= (np.abs(tmp_res) <= self._tol).all(axis=1) # other type of divergence: all 0.
# print(res.nonzero())
# import pdb
# pdb.set_trace()
res = res.sum() # count total of n-1 unsafe
res = len(self._l_ids) - res # reward = things to maximise
if self._normalize:
Expand Down
31 changes: 21 additions & 10 deletions lightsim2grid/tests/test_n1contingencyrewards.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
import numpy as np

import grid2op
from grid2op.Backend import PandaPowerBackend
from grid2op.Action import CompleteAction
from grid2op.Reward import EpisodeDurationReward

from lightsim2grid import LightSimBackend
from lightsim2grid.rewards import N1ContingencyReward


TH_LIM_A_REF = np.array([
541.0,
450.0,
Expand Down Expand Up @@ -55,7 +57,6 @@ def threshold_margin(self):
return 1.

def l_ids(self):
return [0]
return None

def setUp(self) -> None:
Expand Down Expand Up @@ -107,20 +108,18 @@ def _aux_test_reward(self, obs, reward):
# MW**2 = kA**2 * 3. * kV**2 - MVAr**2
p_square = 3. * (1e-3*th_lim)**2 * (obs.v_or)**2 - (obs.q_or)**2
p_square[p_square <= 0.] = 0.
th_lim_p = np.sqrt(p_square)
th_lim_p = np.sqrt(p_square) * self.threshold_margin()

# print("test:")
for l_id in self.my_ids:
sim_obs, sim_r, sim_d, sim_i = obs.simulate(self.env.action_space({"set_line_status": [(l_id, -1)]}),
time_step=0)
if not self.is_dc():
if np.any(sim_obs.a_or > obs._thermal_limit) or sim_d:
if np.any(sim_obs.a_or > obs._thermal_limit * self.threshold_margin()) or sim_d:
unsafe_cont += 1
else:
# print(sim_obs.p_or)
# print(th_lim_p)
if np.any(np.abs(sim_obs.p_or) > th_lim_p) or sim_d:
unsafe_cont += 1
unsafe_cont += 1

assert reward == (len(self.my_ids) - unsafe_cont), f"{reward} vs {(len(self.my_ids) - unsafe_cont)}"

Expand Down Expand Up @@ -184,8 +183,20 @@ def test_copy(self):
class TestN1ContingencyReward_DC(TestN1ContingencyReward_Base):
def is_dc(self):
return True
# def l_ids(self):
# return [18]


class TestN1ContingencyReward_LIDS(TestN1ContingencyReward_Base):
def l_ids(self):
return [0, 1, 2, 18, 16]

# TODO test with only a subset of powerlines
# TODO test with the "margin"
# TODO test with pandapower and lightsim as base backend
# TODO test AC and DC

class TestN1ContingencyReward_Margins(TestN1ContingencyReward_Base):
def threshold_margin(self):
return 0.9


class TestN1ContingencyReward_PP(TestN1ContingencyReward_Base):
def init_backend(self):
return PandaPowerBackend(with_numba=False, lightsim2grid=False)
2 changes: 1 addition & 1 deletion lightsim2grid/timeSerie.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class ___TimeSerie:
"""
def __init__(self, grid2op_env):
if not GRID2OP_INSTALL:
if not GRID2OP_INSTALLED:
raise RuntimeError("Impossible to use the python wrapper `TimeSerie` "
"when grid2op is not installed. Please fall back to the "
"c++ version (available in python) with:\n"
Expand Down

0 comments on commit 6ef1a7a

Please sign in to comment.