Skip to content

Commit

Permalink
improve the importer from iidm
Browse files Browse the repository at this point in the history
  • Loading branch information
BDonnot committed Oct 23, 2023
1 parent dddd4c4 commit 82f9cde
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Change Log
- [FIXED] now voltage is properly set to 0. when shunts are disconnected
- [FIXED] now voltage is properly set to 0. when storage units are disconnected
- [FIXED] a bug where non connected grid were not spotted in DC
- [FIXED] a bug when trying to set the slack for a non existing genererator
- [IMPROVED] now making the new grid2op `create_test_suite`

[0.7.5] 2023-10-05
Expand Down
4 changes: 2 additions & 2 deletions lightsim2grid/gridmodel/from_pypowsybl.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ def init(net : pypo.network,
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(voltage_levels.loc[bus_df["voltage_level_id"].values]["nominal_v"].values,
0, 0 # unused
)
model._orig_to_ls = 1 * bus_df_orig["bus_id"].values

# do the generators
if sort_index:
Expand All @@ -67,7 +67,7 @@ def init(net : pypo.network,
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]
gen_is_conn_slack = gen_bus == model._orig_to_ls[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")
Expand Down
7 changes: 3 additions & 4 deletions lightsim2grid/gridmodel/initGridModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,14 @@ def init(pp_net):
model.set_sn_mva(pp_net.sn_mva)

tmp_bus_ind = np.argsort(pp_net.bus.index)
model.init_bus(pp_net.bus.iloc[tmp_bus_ind]["vn_kv"].values,
pp_net.line.shape[0],
pp_net.trafo.shape[0])
if np.any(np.sort(pp_net.bus.index) != np.arange(pp_net.bus.shape[0])):
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
model.init_bus(pp_net.bus.iloc[tmp_bus_ind]["vn_kv"].values,
pp_net.line.shape[0],
pp_net.trafo.shape[0])

# deactivate in lightsim the deactivated bus in pandapower
for bus_id in range(pp_net.bus.shape[0]):
if not pp_net.bus["in_service"].values[bus_id]:
Expand Down
10 changes: 7 additions & 3 deletions lightsim2grid/lightSimBackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ def make_complete_path(path, filename):
gen_slack_id = None
if "gen_slack_id" in loader_kwargs:
gen_slack_id = int(loader_kwargs["gen_slack_id"])
self._grid = init_pypow(grid_tmp, gen_slack_id=None) # TODO gen_slack_id, make things crash !
self._grid = init_pypow(grid_tmp, gen_slack_id=gen_slack_id, sort_index=True)
self._aux_setup_right_after_grid_init()

# mandatory for the backend
Expand Down Expand Up @@ -507,10 +507,15 @@ def make_complete_path(path, filename):
# as it is done with grid2Op simulated environment
if "double_bus_per_sub" in loader_kwargs and loader_kwargs["double_bus_per_sub"]:
bus_init = self._grid.get_buses()
orig_to_ls = np.array(self._grid._orig_to_ls)
bus_doubled = np.concatenate((bus_init, bus_init))
self._grid.init_bus(bus_doubled, 0, 0)
for i in range(self.__nb_bus_before):
self._grid.deactivate_bus(i + self.__nb_bus_before)
new_orig_to_ls = np.concatenate((orig_to_ls,
np.zeros(orig_to_ls.shape[0], dtype=orig_to_ls.dtype) + self.__nb_bus_before)
)
self._grid._orig_to_ls = new_orig_to_ls
self.nb_bus_total = len(self._grid.get_buses())

# and now things needed by the backend (legacy)
Expand Down Expand Up @@ -1052,7 +1057,7 @@ def copy(self):
"_big_topo_to_obj", "max_it", "tol", "dim_topo",
"_idx_hack_storage",
"_timer_preproc", "_timer_postproc", "_timer_solver",
"_my_kwargs",
"_my_kwargs", "supported_grid_format",
"_turned_off_pv", "_dist_slack_non_renew",
"_loader_method", "_loader_kwargs"
]
Expand Down Expand Up @@ -1106,7 +1111,6 @@ def copy(self):
setattr(res, attr_nm, copy.deepcopy(getattr(self, attr_nm)))
###############


# handle the most complicated
res._grid = mygrid.copy()
res.__me_at_init = __me_at_init.copy() # this is const
Expand Down
60 changes: 56 additions & 4 deletions lightsim2grid/tests/test_backend_pypowsybl.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
import unittest
import warnings
import os
import numpy as np
from lightsim2grid import LightSimBackend
from lightsim2grid.gridmodel.from_pypowsybl import init as init_pypow
import grid2op
from grid2op.Runner import Runner
import pypowsybl.network as pypow_net

try:
from grid2op._create_test_suite import create_test_suite
Expand All @@ -20,10 +24,10 @@


def _aux_get_loader_kwargs_storage():
return {"use_buses_for_sub": True, "double_bus_per_sub": True, "gen_slack_id": 6}
return {"use_buses_for_sub": True, "double_bus_per_sub": True, "gen_slack_id": 5}

def _aux_get_loader_kwargs():
return {"use_buses_for_sub": True, "double_bus_per_sub": True, "gen_slack_id": 5}
return {"use_buses_for_sub": True, "double_bus_per_sub": True, "gen_slack_id": 0}


class BackendTester(unittest.TestCase):
Expand All @@ -34,7 +38,7 @@ def setUp(self) -> None:
self.file_name = "grid.xiidm"

def _aux_prep_backend(self, backend):
backend.set_env_name("case_14_iidm")
backend.set_env_name("case_14_iidm_BackendTester")
backend.load_grid(self.path, self.file_name)
backend.load_storage_data(self.path)
backend.load_redispacthing_data(self.path)
Expand Down Expand Up @@ -68,6 +72,35 @@ def test_runpf(self):
conv, exc_ = backend.runpf(is_dc=True)
assert conv

class BackendTester2(unittest.TestCase):
"""issue is still not replicated and these tests pass"""
def _aux_prep_backend(self, backend):
backend.set_env_name("case_14_storage_iidm_BackendTester2")
backend.load_grid(self.path, self.file_name)
backend.load_storage_data(self.path)
backend.load_redispacthing_data(self.path)
backend.assert_grid_correct()

def setUp(self) -> None:
dir_path = os.path.dirname(os.path.realpath(__file__))
self.path = os.path.join(dir_path, "case_14_storage_iidm")
self.file_name = "grid.xiidm"

def test_init(self):
grid_tmp = pypow_net.load(os.path.join(self.path, self.file_name))
grid = init_pypow(grid_tmp, gen_slack_id=5, sort_index=True)
grid.ac_pf(np.ones(14, dtype=np.complex128), 10, 1e-6)

def test_runpf(self):
backend = LightSimBackend(loader_method="pypowsybl", loader_kwargs=_aux_get_loader_kwargs())
self._aux_prep_backend(backend)
# AC powerflow
conv, exc_ = backend.runpf()
assert conv
# DC powerflow
conv, exc_ = backend.runpf(is_dc=True)
assert conv

if CAN_DO_TEST_SUITE:
dir_path = os.path.dirname(os.path.realpath(__file__))
path_case_14_storage_iidm = os.path.join(dir_path, "case_14_storage_iidm")
Expand Down Expand Up @@ -103,7 +136,8 @@ def setUp(self) -> None:
self.env = grid2op.make(path_case_14_storage_iidm,
backend=LightSimBackend(loader_method="pypowsybl",
loader_kwargs=_aux_get_loader_kwargs_storage(),
)
),
_add_to_name=type(self).__name__
)
super().setUp()

Expand All @@ -115,6 +149,24 @@ def test_can_make(self):
self.env.reset()
1 + 1

def test_copy(self):
obs = self.env.reset()
env_cpy = self.env.copy()
obs_cpy = env_cpy.reset()
assert self.env.backend.supported_grid_format == ("xiidm", )
assert env_cpy.backend.supported_grid_format == ("xiidm", )

def test_runner(self):
obs = self.env.reset()
env_cpy = self.env.copy()
runner = Runner(**self.env.get_params_for_runner())
runner_cpy = Runner(**env_cpy.get_params_for_runner())
res = runner.run(nb_episode=1, max_iter=10)
res_cpy = runner_cpy.run(nb_episode=1, max_iter=10)
for el, el_cpy in zip(res[0], res_cpy[0]):
assert el == el_cpy, f"{el} vs {el_cpy}"


# TODO env tester
if __name__ == "__main__":
unittest.main()
Expand Down
6 changes: 3 additions & 3 deletions lightsim2grid/tests/test_init_from_pypowsybl.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def test_compare_pp(self):
v_ls = self.gridmodel.dc_pf(1.0 * self.V_init_dc, 2, self.tol)
v_ls_ref = self.ref_samecase.dc_pf(1.0 * self.V_init_dc, 2, self.tol)
slack_id = self.get_slackbus_id()
reorder = self.gridmodel._ls_to_orig.reshape(1, -1)
reorder = self.gridmodel._orig_to_ls.reshape(1, -1)

# for case 118
# reorder_flat = reorder.reshape(-1)
Expand Down Expand Up @@ -150,7 +150,7 @@ def test_compare_pp(self):
def test_dc_pf(self):
"""test I get the same results as pandapower in dc"""
v_ls = self.gridmodel.dc_pf(self.V_init_dc, 2, self.tol)
reorder = self.gridmodel._ls_to_orig.reshape(1, -1)
reorder = self.gridmodel._orig_to_ls.reshape(1, -1)
if self.compare_pp():
v_ls_ref = self.ref_samecase.dc_pf(self.V_init_dc, 2, self.tol)
assert np.abs(v_ls[reorder] - v_ls_ref).max() <= self.tol_eq, f"error for vresults for dc: {np.abs(v_ls[reorder] - v_ls_ref).max():.2e}"
Expand All @@ -170,7 +170,7 @@ def test_dc_pf(self):
def test_ac_pf(self):
# run the powerflows
v_ls = self.gridmodel.ac_pf(1.0 * self.V_init_ac, 10, self.tol)
reorder = self.gridmodel._ls_to_orig.reshape(1, -1)
reorder = self.gridmodel._orig_to_ls.reshape(1, -1)
if self.compare_pp():
v_ls_ref = self.ref_samecase.ac_pf(1.0 * self.V_init_ac, 10, self.tol)
assert np.abs(v_ls[reorder] - v_ls_ref).max() <= self.tol_eq, f"error for vresults for ac: {np.abs(v_ls[reorder] - v_ls_ref).max():.2e}"
Expand Down
44 changes: 38 additions & 6 deletions src/GridModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ GridModel::GridModel(const GridModel & other)
{
reset(true, true, true);

_ls_to_orig = other._ls_to_orig;
set_ls_to_orig(other._ls_to_orig); // set also orig_to_ls

init_vm_pu_ = other.init_vm_pu_;
sn_mva_ = other.sn_mva_;
Expand Down Expand Up @@ -80,7 +80,7 @@ GridModel::GridModel(const GridModel & other)
GridModel::StateRes GridModel::get_state() const
{
std::vector<real_type> bus_vn_kv(bus_vn_kv_.begin(), bus_vn_kv_.end());
std::vector<int> ls_to_pp(_ls_to_orig.begin(), _ls_to_orig.end());
std::vector<int> ls_to_orig(_ls_to_orig.begin(), _ls_to_orig.end());
int version_major = VERSION_MAJOR;
int version_medium = VERSION_MEDIUM;
int version_minor = VERSION_MINOR;
Expand All @@ -96,7 +96,7 @@ GridModel::StateRes GridModel::get_state() const
GridModel::StateRes res(version_major,
version_medium,
version_minor,
ls_to_pp,
ls_to_orig,
init_vm_pu_,
sn_mva_,
bus_vn_kv,
Expand Down Expand Up @@ -161,7 +161,8 @@ void GridModel::set_state(GridModel::StateRes & my_state)

// assign it to this instance

_ls_to_orig =IntVect::Map(&ls_to_pp_[0], ls_to_pp_.size());
set_ls_to_orig(IntVect::Map(&ls_to_pp_[0], ls_to_pp_.size())); // set also _orig_to_ls

// buses
// 1. bus_vn_kv_
bus_vn_kv_ = RealVect::Map(&bus_vn_kv[0], bus_vn_kv.size());
Expand All @@ -187,6 +188,35 @@ void GridModel::set_state(GridModel::StateRes & my_state)
dc_lines_.set_state(state_dc_lines);
};

void GridModel::set_ls_to_orig(const IntVect & ls_to_orig){
if(ls_to_orig.size() != bus_vn_kv_.size())
throw std::runtime_error("Impossible to set the converter ls_to_orig: the provided vector has not the same size as the number of bus on the grid.");
_ls_to_orig = ls_to_orig;
const auto size = ls_to_orig.lpNorm<Eigen::Infinity>();
_orig_to_ls = IntVect::Zero(size);
_orig_to_ls.array() -= 1;
for(auto i = 0; i < size; ++i){
_orig_to_ls[i] = _ls_to_orig[i];
}
}

void GridModel::set_orig_to_ls(const IntVect & orig_to_ls){
_orig_to_ls = orig_to_ls;
Eigen::Index nb_bus_ls = 0;
for(const auto el : orig_to_ls){
if (el != -1) nb_bus_ls += 1;
}
_ls_to_orig = IntVect::Zero(nb_bus_ls);
Eigen::Index ls2or_ind = 0;
for(auto or2ls_ind = 0; or2ls_ind < nb_bus_ls; ++or2ls_ind){
const auto my_ind = _orig_to_ls[or2ls_ind];
if(my_ind >= 0){
_ls_to_orig[ls2or_ind] = my_ind;
ls2or_ind++;
}
}
}

//init
void GridModel::init_bus(const RealVect & bus_vn_kv, int nb_line, int nb_trafo){
/**
Expand All @@ -198,6 +228,8 @@ void GridModel::init_bus(const RealVect & bus_vn_kv, int nb_line, int nb_trafo){
bus_vn_kv_ = bus_vn_kv; // base_kv

bus_status_ = std::vector<bool>(nb_bus, true); // by default everything is connected
_orig_to_ls = IntVect();
_ls_to_orig = IntVect();
}

void GridModel::reset(bool reset_solver, bool reset_ac, bool reset_dc)
Expand Down Expand Up @@ -696,7 +728,7 @@ void GridModel::add_gen_slackbus(int gen_id, real_type weight){
exc_ << gen_id;
throw std::runtime_error(exc_.str());
}
if(gen_id > generators_.nb())
if(gen_id >= generators_.nb())
{
std::ostringstream exc_;
exc_ << "GridModel::add_gen_slackbus: There are only " << generators_.nb() << " generators on the grid. ";
Expand All @@ -720,7 +752,7 @@ void GridModel::remove_gen_slackbus(int gen_id){
exc_ << gen_id;
throw std::runtime_error(exc_.str());
}
if(gen_id > generators_.nb())
if(gen_id >= generators_.nb())
{
// TODO DEBUG MODE: only check when in debug mode
std::ostringstream exc_;
Expand Down
13 changes: 10 additions & 3 deletions src/GridModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@
//TODO implement a BFS check to make sure the Ymatrix is "connected" [one single component]
class GridModel : public DataGeneric
{
public: // can be modified python side
IntVect _ls_to_orig; // for converter from bus in lightsim2grid index to bus in original file format (*eg* pandapower or pypowsybl)

public:
typedef std::tuple<
int, // version major
Expand Down Expand Up @@ -84,6 +81,12 @@ class GridModel : public DataGeneric
GridModel res(*this);
return res;
}

void set_ls_to_orig(const IntVect & ls_to_orig); // set both _ls_to_orig and _orig_to_ls
void set_orig_to_ls(const IntVect & orig_to_ls); // set both _orig_to_ls and _ls_to_orig
const IntVect & get_ls_to_orig(void) const {return _ls_to_orig;}
const IntVect & get_orig_to_ls(void) const {return _orig_to_ls;}

Eigen::Index total_bus() const {return bus_vn_kv_.size();}
const std::vector<int> & id_me_to_ac_solver() const {return id_me_to_ac_solver_;}
const std::vector<int> & id_ac_solver_to_me() const {return id_ac_solver_to_me_;}
Expand Down Expand Up @@ -642,6 +645,10 @@ class GridModel : public DataGeneric
void check_solution_q_values_onegen(CplxVect & res, const DataGen::GenInfo& gen, bool check_q_limits) const;

protected:
// memory for the import
IntVect _ls_to_orig; // for converter from bus in lightsim2grid index to bus in original file format (*eg* pandapower or pypowsybl)
IntVect _orig_to_ls; // for converter from bus in lightsim2grid index to bus in original file format (*eg* pandapower or pypowsybl)

// member of the grid
// static const int _deactivated_bus_id;

Expand Down
3 changes: 2 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,8 @@ PYBIND11_MODULE(lightsim2grid_cpp, m)
py::class_<GridModel>(m, "GridModel", DocGridModel::GridModel.c_str())
.def(py::init<>())
.def("copy", &GridModel::copy)
.def_readwrite("_ls_to_orig", &GridModel::_ls_to_orig, "for converter from bus in lightsim2grid index to bus index in original file format (*eg* pandapower of pypowsybl)")
.def_property("_ls_to_orig", &GridModel::get_ls_to_orig, &GridModel::set_ls_to_orig, "remember the conversion from bus index in lightsim2grid to bus index in original file format (*eg* pandapower of pypowsybl).")
.def_property("_orig_to_ls", &GridModel::get_orig_to_ls, &GridModel::set_orig_to_ls, "remember the conversion from bus index in original file format (*eg* pandapower of pypowsybl) to bus index in lightsim2grid.")

// pickle
.def(py::pickle(
Expand Down

0 comments on commit 82f9cde

Please sign in to comment.