Skip to content

Commit

Permalink
fixing some more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
BDonnot committed Oct 19, 2023
1 parent 7a91699 commit e86bca8
Show file tree
Hide file tree
Showing 9 changed files with 187 additions and 105 deletions.
111 changes: 84 additions & 27 deletions lightsim2grid/gridmodel/from_pypowsybl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,94 @@
# SPDX-License-Identifier: MPL-2.0
# This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform.

import copy
import numpy as np
import pypowsybl as pypo
# import pypowsybl.loadflow as lf

from lightsim2grid_cpp import GridModel


def init(net : pypo.network,
gen_slack_id: int = None,
slack_bus_id: int = None,
sn_mva = 100.,
sort_index=True,
f_hz = 50.):
model = GridModel()
# for substation
# network.get_voltage_levels()["substation_id"]
# network.get_substations()
# network.get_busbar_sections()

if gen_slack_id is not None and slack_bus_id is not None:
raise RuntimeError("Impossible to intialize a grid with both gen_slack_id and slack_bus_id")

# assign unique id to the buses
bus_df = net.get_buses().sort_index().copy()
bus_df_orig = net.get_buses()
if sort_index:
bus_df = bus_df_orig.sort_index()
else:
bus_df = bus_df_orig
bus_df["bus_id"] = np.arange(bus_df.shape[0])
bus_df_orig["bus_id"] = bus_df.loc[bus_df_orig.index]["bus_id"]
model._ls_to_orig = 1 * bus_df_orig["bus_id"].values
voltage_levels = net.get_voltage_levels()
model.set_sn_mva(sn_mva)
model.set_init_vm_pu(1.06)
model.init_bus(net.get_voltage_levels().loc[bus_df["voltage_level_id"].values]["nominal_v"].values,
model.init_bus(voltage_levels.loc[bus_df["voltage_level_id"].values]["nominal_v"].values,
0, 0 # unused
)

# do the generators
df_gen = net.get_generators().sort_index()
if sort_index:
df_gen = net.get_generators().sort_index()
else:
df_gen = net.get_generators()
# to handle encoding in 32 bits and overflow when "splitting" the Q values among
min_q = df_gen["min_q"].values.astype(np.float32)
max_q = df_gen["min_q"].values.astype(np.float32)
# to handle encoding in 32 bits and overflow when "splitting" the Q values among generators
max_q = df_gen["max_q"].values.astype(np.float32)
min_q[~np.isfinite(min_q)] = np.finfo(np.float32).min / 2. + 1.
max_q[~np.isfinite(max_q)] = np.finfo(np.float32).max / 2. - 1.
model.init_generators(df_gen["target_p"].values,
df_gen["target_v"].values / net.get_voltage_levels().loc[df_gen["voltage_level_id"].values]["nominal_v"].values,
df_gen["target_v"].values / voltage_levels.loc[df_gen["voltage_level_id"].values]["nominal_v"].values,
min_q,
max_q,
1 * bus_df.loc[df_gen["bus_id"].values]["bus_id"].values
)
# TODO dist slack
if gen_slack_id is None:
model.add_gen_slackbus(0, 1.)
else:
if gen_slack_id is not None:
model.add_gen_slackbus(gen_slack_id, 1.)

elif slack_bus_id is not None:
gen_bus = np.array([el.bus_id for el in model.get_generators()])
gen_is_conn_slack = gen_bus == model._ls_to_orig[slack_bus_id]
nb_conn = gen_is_conn_slack.sum()
if nb_conn == 0:
raise RuntimeError(f"There is no generator connected to bus {slack_bus_id}. It cannot be the slack")
for gen_id, is_slack in enumerate(gen_is_conn_slack):
if is_slack:
model.add_gen_slackbus(gen_id, 1. / nb_conn)
else:
model.add_gen_slackbus(0, 1.)

# for loads
df_load = net.get_loads().sort_index()
if sort_index:
df_load = net.get_loads().sort_index()
else:
df_load = net.get_loads()
model.init_loads(df_load["p0"].values,
df_load["q0"].values,
1 * bus_df.loc[df_load["bus_id"].values]["bus_id"].values
)

# for lines
df_line = net.get_lines().sort_index()
# TODO add g1 / b1 and g2 / b2 in lightsim2grid
# line_h = (1j*df_line["g1"].values + df_line["b1"].values + 1j*df_line["g2"].values + df_line["b2"].values)
if sort_index:
df_line = net.get_lines().sort_index()
else:
df_line = net.get_lines()
# per unit
branch_from_kv = net.get_voltage_levels().loc[df_line["voltage_level1_id"].values]["nominal_v"].values
branch_to_kv = net.get_voltage_levels().loc[df_line["voltage_level2_id"].values]["nominal_v"].values
branch_from_kv = voltage_levels.loc[df_line["voltage_level1_id"].values]["nominal_v"].values
branch_to_kv = voltage_levels.loc[df_line["voltage_level2_id"].values]["nominal_v"].values

# only valid for lines with same voltages at both side...
# branch_from_pu = branch_from_kv * branch_from_kv / sn_mva
Expand Down Expand Up @@ -94,16 +125,19 @@ def init(net : pypo.network,
)

# for trafo
df_trafo = net.get_2_windings_transformers().sort_index()
if sort_index:
df_trafo = net.get_2_windings_transformers().sort_index()
else:
df_trafo = net.get_2_windings_transformers()
# TODO net.get_ratio_tap_changers()
# TODO net.get_phase_tap_changers()
shift_ = np.zeros(df_trafo.shape[0])
tap_pos = 1.0 * shift_
is_tap_hv_side = np.ones(df_trafo.shape[0], dtype=bool) # TODO

# per unit
trafo_from_kv = net.get_voltage_levels().loc[df_trafo["voltage_level1_id"].values]["nominal_v"].values
trafo_to_kv = net.get_voltage_levels().loc[df_trafo["voltage_level2_id"].values]["nominal_v"].values
trafo_from_kv = voltage_levels.loc[df_trafo["voltage_level1_id"].values]["nominal_v"].values
trafo_to_kv = voltage_levels.loc[df_trafo["voltage_level2_id"].values]["nominal_v"].values
trafo_to_pu = trafo_to_kv * trafo_to_kv / sn_mva
# tap
tap_step_pct = (df_trafo["rated_u1"] / trafo_from_kv - 1.) * 100.
Expand All @@ -121,16 +155,34 @@ def init(net : pypo.network,
1 * bus_df.loc[df_trafo["bus2_id"].values]["bus_id"].values)

# for shunt
df_shunt = net.get_shunt_compensators().sort_index()
shunt_kv = net.get_voltage_levels().loc[df_shunt["voltage_level_id"].values]["nominal_v"].values
if sort_index:
df_shunt = net.get_shunt_compensators().sort_index()
else:
df_shunt = net.get_shunt_compensators()

is_on = copy.deepcopy(df_shunt["connected"])
if (~is_on).any():
df_shunt["connected"] = True
net.update_shunt_compensators(df_shunt[["connected"]])
if sort_index:
df_shunt = net.get_shunt_compensators().sort_index()
else:
df_shunt = net.get_shunt_compensators()
df_shunt["connected"] = is_on
net.update_shunt_compensators(df_shunt[["connected"]])

shunt_kv = voltage_levels.loc[df_shunt["voltage_level_id"].values]["nominal_v"].values
model.init_shunt(-df_shunt["g"].values * shunt_kv**2,
-df_shunt["b"].values * shunt_kv**2,
1 * bus_df.loc[df_shunt["bus_id"].values]["bus_id"].values
)

for shunt_id, conn in enumerate(is_on):
if not conn:
model.deactivate_shunt(shunt_id)

# for hvdc (TODO not tested yet)
df_dc = net.get_hvdc_lines().sort_index()
df_sations = net.get_vsc_converter_stations()
df_sations = net.get_vsc_converter_stations().sort_index()
bus_from_id = df_sations.loc[df_dc["converter_station1_id"].values]["bus_id"].values
bus_to_id = df_sations.loc[df_dc["converter_station2_id"].values]["bus_id"].values
loss_percent = np.zeros(df_dc.shape[0]) # TODO
Expand All @@ -140,24 +192,29 @@ def init(net : pypo.network,
df_dc["target_p"].values,
loss_percent,
loss_mw,
net.get_voltage_levels().loc[df_sations.loc[df_dc["converter_station1_id"].values]["voltage_level_id"].values]["nominal_v"].values,
net.get_voltage_levels().loc[df_sations.loc[df_dc["converter_station2_id"].values]["voltage_level_id"].values]["nominal_v"].values,
voltage_levels.loc[df_sations.loc[df_dc["converter_station1_id"].values]["voltage_level_id"].values]["nominal_v"].values,
voltage_levels.loc[df_sations.loc[df_dc["converter_station2_id"].values]["voltage_level_id"].values]["nominal_v"].values,
df_sations.loc[df_dc["converter_station1_id"].values]["min_q"].values,
df_sations.loc[df_dc["converter_station1_id"].values]["max_q"].values,
df_sations.loc[df_dc["converter_station2_id"].values]["min_q"].values,
df_sations.loc[df_dc["converter_station2_id"].values]["max_q"].values
)

# storage units (TODO not tested yet)
df_batt = net.get_batteries().sort_index()
if sort_index:
df_batt = net.get_batteries().sort_index()
else:
df_batt = net.get_batteries()
model.init_storages(df_batt["target_p"].values,
df_batt["target_q"].values,
1 * bus_df.loc[df_batt["bus_id"].values]["bus_id"].values
)

# TODO
# sgen
# sgen => regular gen (from net.get_generators()) with voltage_regulator off TODO

# TODO checks
# no 3windings trafo and other exotic stuff
if net.get_phase_tap_changers().shape[0] > 0:
raise RuntimeError("Impossible currently to init a grid with tap changers at the moment.")
return model
2 changes: 1 addition & 1 deletion lightsim2grid/gridmodel/initGridModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def init(pp_net):

tmp_bus_ind = np.argsort(pp_net.bus.index)
if np.any(np.sort(pp_net.bus.index) != np.arange(pp_net.bus.shape[0])):
model._ls_to_pp = 1 * pp_net.bus.index.values.astype(int)
model._ls_to_orig = 1 * pp_net.bus.index.values.astype(int)
pp_to_ls = {pp_bus: ls_bus for pp_bus, ls_bus in zip(pp_net.bus.index, tmp_bus_ind)}
else:
pp_to_ls = None
Expand Down
3 changes: 2 additions & 1 deletion lightsim2grid/lightSimBackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ def __init__(self,
self._loader_method = loader_method
self._loader_kwargs = loader_kwargs

self.shunts_data_available = True # needs to be self and not type(self) here

self.nb_bus_total = None
self.initdc = True # does not really hurt computation time
self.__nb_powerline = None
Expand Down Expand Up @@ -468,7 +470,6 @@ def _load_grid_pypowsybl(self, path=None, filename=None):

self.__nb_powerline = len(self._grid.get_lines())
self.__nb_bus_before = len(self._grid.get_buses())
type(self).shunts_data_available = True

# init this
self.prod_p = np.array([el.target_p_mw for el in self._grid.get_generators()], dtype=dt_float)
Expand Down
29 changes: 28 additions & 1 deletion lightsim2grid/tests/test_LightSimBackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,33 @@
from grid2op.Space import GridObjects # lazy import
__has_storage = hasattr(GridObjects, "n_storage")

from grid2op.tests.helper_path_test import HelperTests
try:
# new way of doing, does not need to inherit from HelperTests but from unittest.TestCase
from grid2op._create_test_suite import create_test_suite
from grid2op.tests.helper_path_test import HelperTests as DEPRECATEDHelper

class _Garbage:
def setUp(self):
pass

class _SuperGarbage(DEPRECATEDHelper, _Garbage):
pass

_garbage = _SuperGarbage()
_garbage.setUp()

class HelperTests(unittest.TestCase):
def setUp(self) -> None:
self.tol_one = _garbage.tol_one
self.tolvect = _garbage.tolvect
return super().setUp()

def tearDown(self) -> None:
return super().tearDown()

except ImportError as exc_:
# old way of doing, need to inherit from that
from grid2op.tests.helper_path_test import HelperTests
from grid2op.tests.BaseBackendTest import BaseTestNames, BaseTestLoadingCase, BaseTestLoadingBackendFunc
from grid2op.tests.BaseBackendTest import BaseTestTopoAction, BaseTestEnvPerformsCorrectCascadingFailures
from grid2op.tests.BaseBackendTest import BaseTestChangeBusAffectRightBus, BaseTestShuntAction
Expand All @@ -37,6 +63,7 @@
from lightsim2grid.solver import SolverType
from grid2op.Runner import Runner


class TestNames(HelperTests, BaseTestNames):
def make_backend(self, detailed_infos_for_cascading_failures=False):
with warnings.catch_warnings():
Expand Down
3 changes: 1 addition & 2 deletions lightsim2grid/tests/test_basic_backend_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ def this_make_backend(self, detailed_infos_for_cascading_failures=False):
extended_test=False, # for now keep `extended_test=False` until all problems are solved
)
else:
import warnings
warnings.warn("Have you installed grid2op in dev / editable mode ? We cannot make the `create_test_suite` :-(")
print("Have you installed grid2op in dev / editable mode ? We cannot make the `create_test_suite` :-(")

# and run it with `python -m unittest gridcal_backend_tests.py`
if __name__ == "__main__":
Expand Down
Loading

0 comments on commit e86bca8

Please sign in to comment.