Skip to content

Commit

Permalink
fix legacy behaviour in bus labelling when init from pypowsybl
Browse files Browse the repository at this point in the history
  • Loading branch information
BDonnot committed Sep 30, 2024
1 parent 53a6769 commit 7250cf0
Show file tree
Hide file tree
Showing 9 changed files with 53 additions and 28 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ TODO: integration test with pandapower (see `pandapower/contingency/contingency.
was set (and later used)
- [FIXED] yet another bug when init a grid from pypowsybl: the voltage in kV (not in pu)
could be set due to "wrong" labelling of the bus ids
- [FIXED] yet another bug when init a grid from pypowsybl: the ratio of the transformers
sent in lightsim2grid did not take into account the "`rated_u1` `rated_u2`" on both side
(only used on one side)
- [FIXED] yet another bug when init a grid from pypowsybl: the ratio of the transformers
sent in lightsim2grid did not take into account the ratio in the `pypow_net.get_ratio_tap_changers()`
- [ADDED] a method for the `ContingencyAnalysisCPP` class that returns, for all contingencies
in the contingency list, which will be simulated and which causes the grid to be disconnected.
- [ADDED] it is now possible to use "one substation" (voltage level) pypowsybl side is
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'Benjamin DONNOT'

# The full version, including alpha/beta/rc tags
release = "0.9.1.dev0"
release = "0.9.1"
version = '0.9'

# -- General configuration ---------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion lightsim2grid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# SPDX-License-Identifier: MPL-2.0
# This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform.

__version__ = "0.9.1.dev0"
__version__ = "0.9.1"

__all__ = ["newtonpf", "SolverType", "ErrorType", "solver", "compilation_options"]

Expand Down
56 changes: 34 additions & 22 deletions lightsim2grid/gridmodel/from_pypowsybl/_from_pypowsybl.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def init(net : pypo.network.Network,
bus_global_id = bus_local_id * nb_sub_unique + sub_id_duplicate
bus_df["bus_id"] = bus_global_id
all_buses_vn_kv = 1. * voltage_levels["nominal_v"].values
all_buses_vn_kv = np.concatenate([all_buses_vn_kv for _ in range(n_busbar_per_sub)])
ls_to_orig = np.zeros(n_busbar_per_sub * nb_sub_unique, dtype=int) - 1
ls_to_orig[bus_df["bus_id"].values] = np.arange(bus_df.shape[0])
n_sub = nb_sub_unique
Expand All @@ -102,30 +101,32 @@ def init(net : pypo.network.Network,
else:
if buses_for_sub is not None:
raise NotImplementedError("This is not implemented at the moment")
bus_df["bus_id"] = np.arange(bus_df.shape[0])
ls_to_orig = 1 * bus_df_orig["bus_id"].values
bus_df_orig["bus_id"] = bus_df.loc[bus_df_orig.index]["bus_id"]
# retrieve the labels from the buses in the original grid
bus_df["bus_id"] = -1
bus_df.loc[bus_df_orig.index, "bus_id"] = np.arange(bus_df.shape[0])
bus_df = bus_df.sort_values("bus_id")
ls_to_orig = 1 * bus_df["bus_id"].values

n_sub = bus_df.shape[0]
n_bb_per_sub = None
if n_busbar_per_sub is not None:
# used to be done in the Backend previously, now we do it here instead
bus_init = 1. * all_buses_vn_kv
ls_to_orig = 1. * bus_df["bus_id"].values
bus_doubled = np.concatenate([bus_init for _ in range(n_busbar_per_sub)])
ls_to_orig = np.concatenate([orig_to_ls + i * self.__nb_bus_before
for i in range(self.n_busbar_per_sub)]
)
else:
warnings.warn("You should avoid using this function without at least `buses_for_sub` or `n_busbar_per_sub`")

if n_busbar_per_sub is None:
warnings.warn("You should avoid using this function without at least `buses_for_sub` or `n_busbar_per_sub`. "
"Setting automatically n_busbar_per_sub=1")
n_busbar_per_sub = 1

# used to be done in the Backend previously, now we do it here instead
bus_init = 1. * all_buses_vn_kv
bus_doubled = np.concatenate([bus_init for _ in range(n_busbar_per_sub)])
ls_to_orig = np.concatenate((ls_to_orig, np.full((n_busbar_per_sub - 1) * n_sub, fill_value=-1, dtype=ls_to_orig.dtype)))

all_buses_vn_kv = np.concatenate([all_buses_vn_kv for _ in range(n_busbar_per_sub)])
model.set_sn_mva(sn_mva)
model.set_init_vm_pu(1.06)
model.init_bus(all_buses_vn_kv,
0, 0 # unused
)
model._ls_to_orig = ls_to_orig
model.set_n_sub(nb_sub_unique)
model.set_n_sub(n_sub)
if n_bb_per_sub is not None:
model._max_nb_bus_per_sub = n_busbar_per_sub

Expand All @@ -136,10 +137,19 @@ def init(net : pypo.network.Network,
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["max_q"].values.astype(np.float32)
min_q[~np.isfinite(min_q)] = np.finfo(np.float32).min * 1e-4 + 1.
max_q[~np.isfinite(max_q)] = np.finfo(np.float32).max * 1e-4 - 1.
min_float_value = np.finfo(np.float32).min * 1e-4 + 1.
max_float_value = np.finfo(np.float32).max * 1e-4 + 1.
min_q_aux = 1. * df_gen["min_q"].values
too_small = min_q_aux < min_float_value
min_q_aux[too_small] = min_float_value
min_q = min_q_aux.astype(np.float32)

max_q_aux = 1. * df_gen["max_q"].values
too_big = np.abs(max_q_aux) > max_float_value
max_q_aux[too_big] = np.sign(max_q_aux[too_big]) * max_float_value
max_q = max_q_aux.astype(np.float32)
min_q[~np.isfinite(min_q)] = min_float_value
max_q[~np.isfinite(max_q)] = max_float_value
gen_bus, gen_disco = _aux_get_bus(bus_df, df_gen)

# dirty fix for when regulating elements are not the same
Expand Down Expand Up @@ -391,8 +401,8 @@ def init(net : pypo.network.Network,
else:
try:
gen_slack_id_int = int(gen_slack_id)
except Exception:
raise RuntimeError("'slack_bus_id' should be either an int or a generator names")
except Exception as exc_:
raise RuntimeError("'slack_bus_id' should be either an int or a generator names") from exc_
if gen_slack_id_int != gen_slack_id:
raise RuntimeError("'slack_bus_id' should be either an int or a generator names")
model.add_gen_slackbus(gen_slack_id_int, 1.)
Expand All @@ -405,6 +415,8 @@ def init(net : pypo.network.Network,
for gen_id, is_slack in enumerate(gen_is_conn_slack):
if is_slack:
model.add_gen_slackbus(gen_id, 1. / nb_conn)
else:
raise RuntimeError("You need to provide at least one slack with `gen_slack_id` or `slack_bus_id`")

# TODO
# sgen => regular gen (from net.get_generators()) with voltage_regulator off TODO
Expand Down
2 changes: 1 addition & 1 deletion lightsim2grid/lightSimBackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ def _load_grid_pypowsybl(self, path=None, filename=None):
(buses_sub_id, gen_sub, load_sub, (lor_sub, tor_sub), (lex_sub, tex_sub),
batt_sub, sh_sub, hvdc_sub_from_id, hvdc_sub_to_id) = subs_id
if not buses_for_sub:
self.__nb_bus_before = len(self._grid.get_bus_vn_kv())
self.__nb_bus_before = self._grid.get_n_sub()
else:
self.__nb_bus_before = grid_tmp.get_buses().shape[0]
self._aux_setup_right_after_grid_init()
Expand Down
7 changes: 5 additions & 2 deletions lightsim2grid/tests/test_init_from_pypowsybl.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def get_pypo_grid(self):
return pp.network.create_ieee14()

def get_slackbus_id(self):
# id in pandapower
# id in pandapower which is the same than the ID in the pypowsybl network when loaded from disk
# but might not be the same as the lightsim2grid gridmodel (if sort_index is True, which is the default)
return 0

def get_equiv_pdp_grid(self):
Expand Down Expand Up @@ -64,7 +65,7 @@ def setUp(self) -> None:
self.ref_samecase = None

# init lightsim2grid model
self.gridmodel = init_from_pypowsybl(self.network_ref, slack_bus_id=self.get_slackbus_id())
self.gridmodel, self.el_ids = init_from_pypowsybl(self.network_ref, slack_bus_id=self.get_slackbus_id(), return_sub_id=True)

# use some data
self.nb_bus_total = self.network_ref.get_buses().shape[0]
Expand Down Expand Up @@ -128,6 +129,8 @@ def test_compare_pp(self):
# # self.pp_samecase["_ppc"]["internal"]["Bbus"]
# self.pp_samecase["_ppc"]["internal"]["Ybus"][64,67]
# self.pp_samecase["_ppc"]["internal"]["bus"]
# bus_id = self.el_ids[0]
# reorder = np.argsort([int(el.lstrip("VL").rstrip("0").rstrip("_")) for el in bus_id.index]).reshape(1, -1)
if v_ls_ref is not None:
max_ = np.abs(v_ls[reorder] - v_ls_ref).max()
assert max_ <= self.tol_eq, f"error for vresults for dc: {max_:.2e}"
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pybind11.setup_helpers import Pybind11Extension, build_ext


__version__ = "0.9.1.dev0"
__version__ = "0.9.1"
KLU_SOLVER_AVAILABLE = False

# Try to link against SuiteSparse (if available)
Expand Down
4 changes: 4 additions & 0 deletions src/GridModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,10 @@ class GridModel : public GenericContainer
{
n_sub_ = n_sub;
}
int get_n_sub() const
{
return n_sub_;
}
void set_max_nb_bus_per_sub(int max_nb_bus_per_sub)
{
if(bus_vn_kv_.size() != n_sub_ * max_nb_bus_per_sub){
Expand Down
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,7 @@ PYBIND11_MODULE(lightsim2grid_cpp, m)

// auxiliary functions
.def("set_n_sub", &GridModel::set_n_sub, DocGridModel::_internal_do_not_use.c_str())
.def("get_n_sub", &GridModel::get_n_sub, DocGridModel::_internal_do_not_use.c_str())
.def("set_max_nb_bus_per_sub", &GridModel::set_max_nb_bus_per_sub, DocGridModel::_internal_do_not_use.c_str())
.def("set_load_pos_topo_vect", &GridModel::set_load_pos_topo_vect, DocGridModel::_internal_do_not_use.c_str())
.def("set_gen_pos_topo_vect", &GridModel::set_gen_pos_topo_vect, DocGridModel::_internal_do_not_use.c_str())
Expand Down

0 comments on commit 7250cf0

Please sign in to comment.