Skip to content

Commit

Permalink
start systematic testing for solver control
Browse files Browse the repository at this point in the history
  • Loading branch information
BDonnot committed Dec 18, 2023
1 parent 44cea30 commit 94f707d
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 5 deletions.
198 changes: 198 additions & 0 deletions lightsim2grid/tests/test_solver_control.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# TODO: test that "if I do something, then with or without the speed optim, I have the same thing"

# use backend._grid.tell_solver_need_reset() to force the reset of the solver
# and by "something" do :
# - disconnect line X
# - reconnect line X
# - change load
# - change gen
# - change shunt
# - change slack
# - change slack weight
# - when it diverges (and then I can make it un converge normally)
# - test change bus to -1 and deactivate element has the same impact

# TODO and do that for all solver type: NR, NRSingleSlack, GaussSeidel, GaussSeidelSynch, FDPF_XB and FDPFBX
# and for all solver check everything that can be check: final tolerance, number of iteration, etc. etc.

import unittest
import warnings
import numpy as np
import grid2op
from grid2op.Action import CompleteAction
from lightsim2grid import LightSimBackend


class TestSolverControl(unittest.TestCase):
def setUp(self) -> None:
with warnings.catch_warnings():
warnings.filterwarnings("ignore")
self.env = grid2op.make("educ_case14_storage",
test=True,
action_class=CompleteAction,
backend=LightSimBackend())
self.gridmodel = self.env.backend._grid
self.v_init = 1.0 * self.env.backend.V
self.iter = 10
self.tol_solver = 1e-8 # solver
self.tol_equal = 1e-10 # for comparing with and without the "smarter solver" things, and make sure everything is really equal!

def test_pf_run_dc(self):
"""test I have the same results if nothing is done with and without restarting from scratch when running dc powerflow"""
Vdc_init = self.gridmodel.dc_pf(self.v_init, self.iter, self.tol_solver)
assert len(Vdc_init), f"error: gridmodel should converge in DC"
self.gridmodel.unset_changes()
Vdc_init2 = self.gridmodel.dc_pf(self.v_init, self.iter, self.tol_solver)
assert len(Vdc_init2), f"error: gridmodel should converge in DC"
self.gridmodel.tell_solver_need_reset()
Vdc_init3 = self.gridmodel.dc_pf(self.v_init, self.iter, self.tol_solver)
assert len(Vdc_init3), f"error: gridmodel should converge in DC"
assert np.allclose(Vdc_init, Vdc_init2, rtol=self.tol_equal, atol=self.tol_equal)
assert np.allclose(Vdc_init2, Vdc_init3, rtol=self.tol_equal, atol=self.tol_equal)

def test_pf_run_ac(self):
"""test I have the same results if nothing is done with and without restarting from scratch when running ac powerflow"""
Vac_init = self.gridmodel.ac_pf(self.v_init, self.iter, self.tol_solver)
assert len(Vac_init), f"error: gridmodel should converge in AC"
self.gridmodel.unset_changes()
Vac_init2 = self.gridmodel.ac_pf(self.v_init, self.iter, self.tol_solver)
assert len(Vac_init2), f"error: gridmodel should converge in AC"
self.gridmodel.tell_solver_need_reset()
Vac_init3 = self.gridmodel.ac_pf(self.v_init, self.iter, self.tol_solver)
assert len(Vac_init3), f"error: gridmodel should converge in AC"
assert np.allclose(Vac_init, Vac_init2, rtol=self.tol_equal, atol=self.tol_equal)
assert np.allclose(Vac_init2, Vac_init3, rtol=self.tol_equal, atol=self.tol_equal)

def _disco_line_action(self, gridmodel, el_id=0, el_val=0.):
gridmodel.deactivate_powerline(el_id)

def _reco_line_action(self, gridmodel, el_id=0, el_val=0.):
gridmodel.reactivate_powerline(el_id)

def _disco_trafo_action(self, gridmodel, el_id=0, el_val=0.):
gridmodel.deactivate_trafo(el_id)

def _reco_trafo_action(self, gridmodel, el_id=0, el_val=0.):
gridmodel.reactivate_trafo(el_id)

def _run_ac_pf(self, gridmodel):
return gridmodel.ac_pf(self.v_init, self.iter, self.tol_solver)

def _run_dc_pf(self, gridmodel):
return gridmodel.dc_pf(self.v_init, self.iter, self.tol_solver)

def aux_do_undo_ac(self,
runpf_fun="_run_ac_pf",
funname_do="_disco_line_action",
funname_undo="_reco_line_action",
el_id=0,
el_val=0.,
expected_diff=0.1
):
pf_mode = "AC" if runpf_fun=="_run_ac_pf" else "DC"
print(f"Looking at {el_id} in {pf_mode}")

# test "do the action"
V_init = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
assert len(V_init), f"error for el_id={el_id}: gridmodel should converge in {pf_mode}"

getattr(self, funname_do)(gridmodel=self.gridmodel, el_id=el_id, el_val=el_val)
V_disc = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
if len(V_disc) > 0:
# powerflow converges, all should converge
assert (np.abs(V_init - V_disc) >= expected_diff).any(), f"error for el_id={el_id}: at least one bus should have changed its result voltage in {pf_mode}"
self.gridmodel.unset_changes()
V_disc1 = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
assert len(V_disc1), f"error for el_id={el_id}: gridmodel should converge in {pf_mode}"
assert np.allclose(V_disc, V_disc1, rtol=self.tol_equal, atol=self.tol_equal)
self.gridmodel.tell_solver_need_reset()
V_disc2 = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
assert len(V_disc2), f"error for el_id={el_id}: gridmodel should converge in {pf_mode}"
assert np.allclose(V_disc2, V_disc1, rtol=self.tol_equal, atol=self.tol_equal)
assert np.allclose(V_disc2, V_disc, rtol=self.tol_equal, atol=self.tol_equal)
else:
#powerflow diverges
self.gridmodel.unset_changes()
V_disc1 = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
assert len(V_disc1) == 0, f"error for el_id={el_id}: powerflow should diverge as it did initially in {pf_mode}"
self.gridmodel.tell_solver_need_reset()
V_disc2 = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
assert len(V_disc1) == 0, f"error for el_id={el_id}: powerflow should diverge as it did initially in {pf_mode}"

# test "undo the action"
self.gridmodel.unset_changes()
getattr(self, funname_undo)(gridmodel=self.gridmodel, el_id=el_id, el_val=el_val)
V_reco = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
assert len(V_reco), f"error for el_id={el_id}: gridmodel should converge in {pf_mode}"
assert np.allclose(V_reco, V_init, rtol=self.tol_equal, atol=self.tol_equal), f"error for el_id={el_id}: do an action and then undo it should not have any impact in {pf_mode}"
self.gridmodel.unset_changes()
V_reco1 = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
assert len(V_reco1), f"error for el_id={el_id}: gridmodel should converge in {pf_mode}"
assert np.allclose(V_reco1, V_reco, rtol=self.tol_equal, atol=self.tol_equal)
self.gridmodel.tell_solver_need_reset()
V_reco2 = getattr(self, runpf_fun)(gridmodel=self.gridmodel)
assert len(V_reco2), f"error for el_id={el_id}: gridmodel should converge in {pf_mode}"
assert np.allclose(V_reco2, V_reco1, rtol=self.tol_equal, atol=self.tol_equal)
assert np.allclose(V_reco2, V_reco, rtol=self.tol_equal, atol=self.tol_equal)

def test_disco_reco_line_ac(self, runpf_fun="_run_ac_pf"):
"""test I have the same results if I disconnect a line with and
without restarting from scratch when running ac powerflow"""
for el_id in range(len(self.gridmodel.get_lines())):
self.gridmodel.tell_solver_need_reset()
expected_diff = 3e-2
if runpf_fun=="_run_ac_pf":
if el_id == 4:
expected_diff = 1e-2
elif el_id == 10:
expected_diff = 1e-3
elif el_id == 12:
expected_diff = 1e-2
elif el_id == 13:
expected_diff = 3e-3
elif runpf_fun=="_run_dc_pf":
if el_id == 4:
expected_diff = 1e-2
elif el_id == 8:
expected_diff = 1e-2
elif el_id == 10:
expected_diff = 1e-2
elif el_id == 12:
expected_diff = 1e-2
elif el_id == 13:
expected_diff = 3e-3
elif el_id == 14:
expected_diff = 1e-2
self.aux_do_undo_ac(funname_do="_disco_line_action",
funname_undo="_reco_line_action",
runpf_fun=runpf_fun,
el_id=el_id,
expected_diff=expected_diff
)

def test_disco_reco_line_dc(self):
"""test I have the same results if I disconnect a line with and
without restarting from scratch when running DC powerflow"""
self.test_disco_reco_line_ac(runpf_fun="_run_dc_pf")

def test_disco_reco_trafo_ac(self, runpf_fun="_run_ac_pf"):
"""test I have the same results if I disconnect a trafo with and
without restarting from scratch when running ac powerflow"""
for el_id in range(len(self.gridmodel.get_trafos())):
self.gridmodel.tell_solver_need_reset()
expected_diff = 3e-2
if runpf_fun=="_run_ac_pf":
if el_id == 1:
expected_diff = 1e-2
self.aux_do_undo_ac(funname_do="_disco_trafo_action",
funname_undo="_reco_trafo_action",
runpf_fun=runpf_fun,
el_id=el_id,
expected_diff=expected_diff
)

def test_disco_reco_trafo_dc(self):
"""test I have the same results if I disconnect a trafo with and
without restarting from scratch when running DC powerflow"""
self.test_disco_reco_trafo_ac(runpf_fun="_run_dc_pf")

4 changes: 2 additions & 2 deletions lightsim2grid/tests/test_turnedoff_nopv.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ def test_after_runner(self):
"""test I can use the runner"""
runner_pv = Runner(**self.env_pv.get_params_for_runner())
runner_npv = Runner(**self.env_npv.get_params_for_runner())
res_pv = runner_pv.run(nb_episode=1, max_iter=self.max_iter_real, add_detailed_output=True)
res_npv = runner_npv.run(nb_episode=1, max_iter=self.max_iter_real, add_detailed_output=True)
res_pv = runner_pv.run(nb_episode=1, max_iter=self.max_iter_real, add_detailed_output=True, env_seeds=[0])
res_npv = runner_npv.run(nb_episode=1, max_iter=self.max_iter_real, add_detailed_output=True, env_seeds=[0])
assert res_pv[0][3] == res_npv[0][3] # same number of steps survived
assert res_pv[0][2] != res_npv[0][2] # not the same reward
ep_pv = res_pv[0][-1]
Expand Down
8 changes: 5 additions & 3 deletions src/powerflow_algorithm/BaseDCAlgo.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@ bool BaseDCAlgo<LinearSolver>::compute_pf(const Eigen::SparseMatrix<cplx_type> &
// err_ = ErrorType::NotInitError;
return false;
}
BaseAlgo::reset_timer();

auto timer = CustTimer();
if(_solver_control.need_reset_solver() ||
_solver_control.has_dimension_changed() ||
_solver_control.has_ybus_some_coeffs_zero()){
reset();
}

auto timer = CustTimer();
BaseAlgo::reset_timer();
if (_solver_control.ybus_change_sparsity_pattern()) need_factorize_ = true;

sizeYbus_with_slack_ = static_cast<int>(Ybus.rows());

#ifdef __COUT_TIMES
Expand Down Expand Up @@ -83,7 +86,6 @@ bool BaseDCAlgo<LinearSolver>::compute_pf(const Eigen::SparseMatrix<cplx_type> &
#endif // __COUT_TIMES
bool just_factorize = false;
if(need_factorize_){
// std::cout << "\t\t\t\t need_factorize_ \n";
ErrorType status_init = _linear_solver.initialize(dcYbus_noslack_);
if(status_init != ErrorType::NoError){
err_ = status_init;
Expand Down
1 change: 1 addition & 0 deletions src/powerflow_algorithm/BaseNRAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ class BaseNRAlgo : public BaseAlgo
void reset_if_needed(){
if(_solver_control.need_reset_solver() ||
_solver_control.has_dimension_changed() ||
_solver_control.ybus_change_sparsity_pattern() ||
_solver_control.has_ybus_some_coeffs_zero() ||
_solver_control.has_slack_participate_changed() ||
_solver_control.has_pv_changed() ||
Expand Down

0 comments on commit 94f707d

Please sign in to comment.