From 92612390091b7bbbde33c1e77a8d106b4e5fadee Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 23 Oct 2023 16:56:50 +0200 Subject: [PATCH 01/42] first poc for speed improvment: not recomputing what does not need to [skip ci] --- src/BaseSolver.cpp | 6 +- src/BaseSolver.h | 2 +- src/DataDCLine.h | 38 ++++++------- src/DataGen.cpp | 8 +-- src/DataGen.h | 13 ++--- src/DataGeneric.cpp | 6 +- src/DataGeneric.h | 6 +- src/DataLine.h | 8 +-- src/DataLoad.h | 10 ++-- src/DataSGen.h | 10 ++-- src/DataShunt.h | 10 ++-- src/DataTrafo.h | 8 +-- src/GridModel.cpp | 67 ++++++++++------------ src/GridModel.h | 134 +++++++++++++++++++++++++++----------------- src/Utils.h | 65 +++++++++++++++++++++ src/main.cpp | 21 ++++++- 16 files changed, 256 insertions(+), 156 deletions(-) diff --git a/src/BaseSolver.cpp b/src/BaseSolver.cpp index 05c837e..22decb1 100644 --- a/src/BaseSolver.cpp +++ b/src/BaseSolver.cpp @@ -8,15 +8,15 @@ #include "BaseSolver.h" -void BaseSolver::reset(){ +void BaseSolver::reset(const SolverControl & solver_control){ // reset timers reset_timer(); //reset the attribute n_ = -1; Vm_ = RealVect(); // voltage magnitude - Va_= RealVect(); // voltage angle - V_= RealVect(); // voltage angle + Va_ = RealVect(); // voltage angle + V_ = RealVect(); // voltage angle // TODO solver control: see if I could reuse some of these nr_iter_ = 0; // number of iteration performs by the algorithm err_ = ErrorType::NotInitError; //error message: } diff --git a/src/BaseSolver.h b/src/BaseSolver.h index 54d8710..f88cf4d 100644 --- a/src/BaseSolver.h +++ b/src/BaseSolver.h @@ -99,7 +99,7 @@ class BaseSolver : public BaseConstants ) = 0 ; virtual - void reset(); + void reset(const SolverControl & solver_control); protected: virtual void reset_timer(){ diff --git a/src/DataDCLine.h b/src/DataDCLine.h index 185be25..46c7af3 100644 --- a/src/DataDCLine.h +++ b/src/DataDCLine.h @@ -156,20 +156,20 @@ class DataDCLine : public DataGeneric ); // accessor / modifiers - void deactivate(int dcline_id, bool & need_reset) { - _deactivate(dcline_id, status_, need_reset); - from_gen_.deactivate(dcline_id, need_reset); - to_gen_.deactivate(dcline_id, need_reset); + void deactivate(int dcline_id, SolverControl & solver_control) { + _deactivate(dcline_id, status_); + from_gen_.deactivate(dcline_id, solver_control); + to_gen_.deactivate(dcline_id, solver_control); } - void reactivate(int dcline_id, bool & need_reset) { - _reactivate(dcline_id, status_, need_reset); - from_gen_.reactivate(dcline_id, need_reset); - to_gen_.reactivate(dcline_id, need_reset); + void reactivate(int dcline_id, SolverControl & solver_control) { + _reactivate(dcline_id, status_); + from_gen_.reactivate(dcline_id, solver_control); + to_gen_.reactivate(dcline_id, solver_control); } - void change_bus_or(int dcline_id, int new_bus_id, bool & need_reset, int nb_bus) { - from_gen_.change_bus(dcline_id, new_bus_id, need_reset, nb_bus);} - void change_bus_ex(int dcline_id, int new_bus_id, bool & need_reset, int nb_bus) { - to_gen_.change_bus(dcline_id, new_bus_id, need_reset, nb_bus);} + void change_bus_or(int dcline_id, int new_bus_id, SolverControl & solver_control, int nb_bus) { + from_gen_.change_bus(dcline_id, new_bus_id, solver_control, nb_bus);} + void change_bus_ex(int dcline_id, int new_bus_id, SolverControl & solver_control, int nb_bus) { + to_gen_.change_bus(dcline_id, new_bus_id, solver_control, nb_bus);} int get_bus_or(int dcline_id) {return from_gen_.get_bus(dcline_id);} int get_bus_ex(int dcline_id) {return to_gen_.get_bus(dcline_id);} real_type get_qmin_or(int dcline_id) {return from_gen_.get_qmin(dcline_id);} @@ -185,16 +185,16 @@ class DataDCLine : public DataGeneric ; return new_p_ext; } - void change_p(int dcline_id, real_type new_p, bool & need_reset){ - from_gen_.change_p(dcline_id, -1.0 * new_p, need_reset); + void change_p(int dcline_id, real_type new_p, SolverControl & sovler_control){ + from_gen_.change_p(dcline_id, -1.0 * new_p, sovler_control); - to_gen_.change_p(dcline_id, -1.0 * get_to_mw(dcline_id, new_p), need_reset); + to_gen_.change_p(dcline_id, -1.0 * get_to_mw(dcline_id, new_p), sovler_control); } - void change_v_or(int dcline_id, real_type new_v_pu, bool & need_reset){ - from_gen_.change_v(dcline_id, new_v_pu, need_reset); + void change_v_or(int dcline_id, real_type new_v_pu, SolverControl & sovler_control){ + from_gen_.change_v(dcline_id, new_v_pu, sovler_control); } - void change_v_ex(int dcline_id, real_type new_v_pu, bool & need_reset){ - to_gen_.change_v(dcline_id, new_v_pu, need_reset); + void change_v_ex(int dcline_id, real_type new_v_pu, SolverControl & sovler_control){ + to_gen_.change_v(dcline_id, new_v_pu, sovler_control); } // solver stuff diff --git a/src/DataGen.cpp b/src/DataGen.cpp index 238a658..50a5937 100644 --- a/src/DataGen.cpp +++ b/src/DataGen.cpp @@ -377,7 +377,7 @@ void DataGen::set_q(const RealVect & reactive_mismatch, void DataGen::update_slack_weights(Eigen::Ref > could_be_slack, - bool & need_reset) + bool & need_reset_solver) { const int nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id) @@ -386,16 +386,16 @@ void DataGen::update_slack_weights(Eigen::Ref 0.){ // gen is properly connected - if(!gen_slackbus_[gen_id]) need_reset = true; // it was not in the slack before, so I need to reset the solver + if(!gen_slackbus_[gen_id]) need_reset_solver = true; // it was not in the slack before, so I need to reset the solver add_slackbus(gen_id, p_mw_(gen_id)); }else{ // gen is now "turned off" - if(gen_slackbus_[gen_id]) need_reset = true; // it was in the slack before, so I need to reset the solver + if(gen_slackbus_[gen_id]) need_reset_solver = true; // it was in the slack before, so I need to reset the solver remove_slackbus(gen_id); } }else{ - if(gen_slackbus_[gen_id]) need_reset = true; // it was in the slack before, I need to reset the solver + if(gen_slackbus_[gen_id]) need_reset_solver = true; // it was in the slack before, I need to reset the solver remove_slackbus(gen_id); } } diff --git a/src/DataGen.h b/src/DataGen.h index 237f118..c6a9cca 100644 --- a/src/DataGen.h +++ b/src/DataGen.h @@ -171,17 +171,16 @@ class DataGen: public DataGeneric void turnedoff_no_pv(){turnedoff_gen_pv_=false;} // turned off generators are not pv void turnedoff_pv(){turnedoff_gen_pv_=true;} // turned off generators are pv bool get_turnedoff_gen_pv() const {return turnedoff_gen_pv_;} - void update_slack_weights(Eigen::Ref > could_be_slack, - bool & need_reset); + void update_slack_weights(Eigen::Ref > could_be_slack, SolverControl & solver_control); - void deactivate(int gen_id, bool & need_reset) {_deactivate(gen_id, status_, need_reset);} - void reactivate(int gen_id, bool & need_reset) {_reactivate(gen_id, status_, need_reset);} - void change_bus(int gen_id, int new_bus_id, bool & need_reset, int nb_bus) {_change_bus(gen_id, new_bus_id, bus_id_, need_reset, nb_bus);} + void deactivate(int gen_id, SolverControl & solver_control) {_deactivate(gen_id, status_, need_reset);} + void reactivate(int gen_id, SolverControl & sovler_control) {_reactivate(gen_id, status_, need_reset);} + void change_bus(int gen_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(gen_id, new_bus_id, bus_id_, need_reset, nb_bus);} int get_bus(int gen_id) {return _get_bus(gen_id, status_, bus_id_);} real_type get_qmin(int gen_id) {return min_q_.coeff(gen_id);} real_type get_qmax(int gen_id) {return max_q_.coeff(gen_id);} - void change_p(int gen_id, real_type new_p, bool & need_reset); - void change_v(int gen_id, real_type new_v_pu, bool & need_reset); + void change_p(int gen_id, real_type new_p, SolverControl & solver_control); + void change_v(int gen_id, real_type new_v_pu, SolverControl & solver_control); virtual void fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const; virtual void fillpv(std::vector& bus_pv, diff --git a/src/DataGeneric.cpp b/src/DataGeneric.cpp index 6d246d3..832be6a 100644 --- a/src/DataGeneric.cpp +++ b/src/DataGeneric.cpp @@ -26,14 +26,12 @@ void DataGeneric::_get_amps(RealVect & a, const RealVect & p, const RealVect & q } a = p2q2.array() * _1_sqrt_3 / v_tmp.array(); } -void DataGeneric::_reactivate(int el_id, std::vector & status, bool & need_reset){ +void DataGeneric::_reactivate(int el_id, std::vector & status){ bool val = status.at(el_id); - if(!val) need_reset = true; // I need to recompute the grid, if a status has changed status.at(el_id) = true; //TODO why it's needed to do that again } -void DataGeneric::_deactivate(int el_id, std::vector & status, bool & need_reset){ +void DataGeneric::_deactivate(int el_id, std::vector & status){ bool val = status.at(el_id); - if(val) need_reset = true; // I need to recompute the grid, if a status has changed status.at(el_id) = false; //TODO why it's needed to do that again } void DataGeneric::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el_bus_ids, bool & need_reset, int nb_bus){ diff --git a/src/DataGeneric.h b/src/DataGeneric.h index 8d9bc60..f3db542 100644 --- a/src/DataGeneric.h +++ b/src/DataGeneric.h @@ -98,9 +98,9 @@ class DataGeneric : public BaseConstants /** activation / deactivation of elements **/ - void _reactivate(int el_id, std::vector & status, bool & need_reset); - void _deactivate(int el_id, std::vector & status, bool & need_reset); - void _change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el_bus_ids, bool & need_reset, int nb_bus); + void _reactivate(int el_id, std::vector & status); + void _deactivate(int el_id, std::vector & status); + void _change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el_bus_ids, SolverControl & solver_control, int nb_bus); int _get_bus(int el_id, const std::vector & status_, const Eigen::VectorXi & bus_id_); /** diff --git a/src/DataLine.h b/src/DataLine.h index dae3e8a..e76813b 100644 --- a/src/DataLine.h +++ b/src/DataLine.h @@ -178,10 +178,10 @@ class DataLine : public DataGeneric return LineInfo(*this, id); } - void deactivate(int powerline_id, bool & need_reset) {_deactivate(powerline_id, status_, need_reset);} - void reactivate(int powerline_id, bool & need_reset) {_reactivate(powerline_id, status_, need_reset);} - void change_bus_or(int powerline_id, int new_bus_id, bool & need_reset, int nb_bus) {_change_bus(powerline_id, new_bus_id, bus_or_id_, need_reset, nb_bus);} - void change_bus_ex(int powerline_id, int new_bus_id, bool & need_reset, int nb_bus) {_change_bus(powerline_id, new_bus_id, bus_ex_id_, need_reset, nb_bus);} + void deactivate(int powerline_id, SolverControl & solver_control) {_deactivate(powerline_id, status_, need_reset);} + void reactivate(int powerline_id, SolverControl & solver_control) {_reactivate(powerline_id, status_, need_reset);} + void change_bus_or(int powerline_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(powerline_id, new_bus_id, bus_or_id_, need_reset, nb_bus);} + void change_bus_ex(int powerline_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(powerline_id, new_bus_id, bus_ex_id_, need_reset, nb_bus);} int get_bus_or(int powerline_id) {return _get_bus(powerline_id, status_, bus_or_id_);} int get_bus_ex(int powerline_id) {return _get_bus(powerline_id, status_, bus_ex_id_);} virtual void fillYbus(std::vector > & res, diff --git a/src/DataLoad.h b/src/DataLoad.h index 78effd6..52f5429 100644 --- a/src/DataLoad.h +++ b/src/DataLoad.h @@ -132,12 +132,12 @@ class DataLoad : public DataGeneric int nb() const { return static_cast(p_mw_.size()); } - void deactivate(int load_id, bool & need_reset) {_deactivate(load_id, status_, need_reset);} - void reactivate(int load_id, bool & need_reset) {_reactivate(load_id, status_, need_reset);} - void change_bus(int load_id, int new_bus_id, bool & need_reset, int nb_bus) {_change_bus(load_id, new_bus_id, bus_id_, need_reset, nb_bus);} + void deactivate(int load_id, SolverControl & solver_control) {_deactivate(load_id, status_, need_reset);} + void reactivate(int load_id, SolverControl & solver_control) {_reactivate(load_id, status_, need_reset);} + void change_bus(int load_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(load_id, new_bus_id, bus_id_, need_reset, nb_bus);} int get_bus(int load_id) {return _get_bus(load_id, status_, bus_id_);} - void change_p(int load_id, real_type new_p, bool & need_reset); - void change_q(int load_id, real_type new_q, bool & need_reset); + void change_p(int load_id, real_type new_p, SolverControl & solver_control); + void change_q(int load_id, real_type new_q, SolverControl & solver_control); virtual void fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const; diff --git a/src/DataSGen.h b/src/DataSGen.h index 41ec3ac..b99ef35 100644 --- a/src/DataSGen.h +++ b/src/DataSGen.h @@ -152,12 +152,12 @@ class DataSGen: public DataGeneric int nb() const { return static_cast(p_mw_.size()); } - void deactivate(int sgen_id, bool & need_reset) {_deactivate(sgen_id, status_, need_reset);} - void reactivate(int sgen_id, bool & need_reset) {_reactivate(sgen_id, status_, need_reset);} - void change_bus(int sgen_id, int new_bus_id, bool & need_reset, int nb_bus) {_change_bus(sgen_id, new_bus_id, bus_id_, need_reset, nb_bus);} + void deactivate(int sgen_id, SolverControl & solver_control) {_deactivate(sgen_id, status_, need_reset);} + void reactivate(int sgen_id, SolverControl & solver_control) {_reactivate(sgen_id, status_, need_reset);} + void change_bus(int sgen_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(sgen_id, new_bus_id, bus_id_, need_reset, nb_bus);} int get_bus(int sgen_id) {return _get_bus(sgen_id, status_, bus_id_);} - void change_p(int sgen_id, real_type new_p, bool & need_reset); - void change_q(int sgen_id, real_type new_q, bool & need_reset); + void change_p(int sgen_id, real_type new_p, SolverControl & solver_control); + void change_q(int sgen_id, real_type new_q, SolverControl & solver_control); virtual void fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const ; diff --git a/src/DataShunt.h b/src/DataShunt.h index 9a74be9..3a6a6be 100644 --- a/src/DataShunt.h +++ b/src/DataShunt.h @@ -125,11 +125,11 @@ class DataShunt : public DataGeneric int nb() const { return static_cast(p_mw_.size()); } - void deactivate(int shunt_id, bool & need_reset) {_deactivate(shunt_id, status_, need_reset);} - void reactivate(int shunt_id, bool & need_reset) {_reactivate(shunt_id, status_, need_reset);} - void change_bus(int shunt_id, int new_bus_id, bool & need_reset, int nb_bus) {_change_bus(shunt_id, new_bus_id, bus_id_, need_reset, nb_bus);} - void change_p(int shunt_id, real_type new_p, bool & need_reset); - void change_q(int shunt_id, real_type new_q, bool & need_reset); + void deactivate(int shunt_id, SolverControl & solver_control) {_deactivate(shunt_id, status_, need_reset);} + void reactivate(int shunt_id, SolverControl & solver_control) {_reactivate(shunt_id, status_, need_reset);} + void change_bus(int shunt_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(shunt_id, new_bus_id, bus_id_, need_reset, nb_bus);} + void change_p(int shunt_id, real_type new_p, SolverControl & solver_control); + void change_q(int shunt_id, real_type new_q, SolverControl & solver_control); int get_bus(int shunt_id) {return _get_bus(shunt_id, status_, bus_id_);} virtual void fillYbus(std::vector > & res, diff --git a/src/DataTrafo.h b/src/DataTrafo.h index 84f29e6..0eca8ad 100644 --- a/src/DataTrafo.h +++ b/src/DataTrafo.h @@ -168,10 +168,10 @@ class DataTrafo : public DataGeneric } // method used within lightsim - void deactivate(int trafo_id, bool & need_reset) {_deactivate(trafo_id, status_, need_reset);} - void reactivate(int trafo_id, bool & need_reset) {_reactivate(trafo_id, status_, need_reset);} - void change_bus_hv(int trafo_id, int new_bus_id, bool & need_reset, int nb_bus) {_change_bus(trafo_id, new_bus_id, bus_hv_id_, need_reset, nb_bus);} - void change_bus_lv(int trafo_id, int new_bus_id, bool & need_reset, int nb_bus) {_change_bus(trafo_id, new_bus_id, bus_lv_id_, need_reset, nb_bus);} + void deactivate(int trafo_id, SolverControl & solver_control) {_deactivate(trafo_id, status_, need_reset);} + void reactivate(int trafo_id, SolverControl & solver_control) {_reactivate(trafo_id, status_, need_reset);} + void change_bus_hv(int trafo_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(trafo_id, new_bus_id, bus_hv_id_, need_reset, nb_bus);} + void change_bus_lv(int trafo_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(trafo_id, new_bus_id, bus_lv_id_, need_reset, nb_bus);} int get_bus_hv(int trafo_id) {return _get_bus(trafo_id, status_, bus_hv_id_);} int get_bus_lv(int trafo_id) {return _get_bus(trafo_id, status_, bus_lv_id_);} diff --git a/src/GridModel.cpp b/src/GridModel.cpp index 31b0135..8aa3be6 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -118,9 +118,8 @@ void GridModel::set_state(GridModel::StateRes & my_state) // after loading back, the instance need to be reset anyway // TODO see if it's worth the trouble NOT to do it reset(true, true, true); - need_reset_ = true; + solver_control_.tell_all_changed(); compute_results_ = true; - topo_changed_ = true; // extract data from the state int version_major = std::get<0>(my_state); @@ -252,8 +251,7 @@ void GridModel::reset(bool reset_solver, bool reset_ac, bool reset_dc) bus_pv_ = Eigen::VectorXi(); bus_pq_ = Eigen::VectorXi(); slack_weights_ = RealVect(); - need_reset_ = true; - topo_changed_ = true; + solver_control_.tell_all_changed(); // reset the solvers if (reset_solver){ @@ -282,12 +280,12 @@ CplxVect GridModel::ac_pf(const CplxVect & Vinit, // pre process the data to define a proper jacobian matrix, the proper voltage vector etc. bool is_ac = true; - bool reset_solver = topo_changed_; // I reset the solver only if the topology change CplxVect V = pre_process_solver(Vinit, Ybus_ac_, id_me_to_ac_solver_, id_ac_solver_to_me_, slack_bus_id_ac_solver_, - is_ac, reset_solver); + is_ac, + solver_control_); // start the solver slack_weights_ = generators_.get_slack_weights(Ybus_ac_.rows(), id_me_to_ac_solver_); @@ -364,7 +362,8 @@ CplxVect GridModel::check_solution(const CplxVect & V_proposed, bool check_q_lim // pre process the data to define a proper jacobian matrix, the proper voltage vector etc. const int nb_bus = static_cast(V_proposed.size()); bool is_ac = true; - bool reset_solver = false; + SolverControl reset_solver; + reset_solver.tell_none_changed(); // TODO reset solver CplxVect V = pre_process_solver(V_proposed, Ybus_ac_, id_me_to_ac_solver_, id_ac_solver_to_me_, slack_bus_id_ac_solver_, is_ac, reset_solver); @@ -399,40 +398,26 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, std::vector & id_solver_to_me, Eigen::VectorXi & slack_bus_id_solver, bool is_ac, - bool reset_solver) + const SolverControl & solver_control) { // TODO get rid of the "is_ac" argument: this info is available in the _solver already - // std::cout << "GridModel::pre_process_solver : topo_changed_ " << topo_changed_ << std::endl; - // std::cout << "GridModel::pre_process_solver : reset_solver " << reset_solver << std::endl; - - bool reset_ac = topo_changed_ && is_ac; - bool reset_dc = topo_changed_ && !is_ac; - // if(need_reset_){ // TODO optimization when it's not mandatory to start from scratch - if(topo_changed_) reset(reset_solver, reset_ac, reset_dc); // TODO what if pv and pq changed ? :O - else{ - // topo is not changed, but i can still reset the solver (TODO: no necessarily needed !) - if (reset_solver) - { - if(is_ac) _solver.reset(); - else _dc_solver.reset(); - } - } + if(is_ac) _solver.reset(solver_control); + else _dc_solver.reset(solver_control); slack_bus_id_ = generators_.get_slack_bus_id(); - if(topo_changed_){ - // TODO do not reinit Ybus if the topology does not change - init_Ybus(Ybus, id_me_to_solver, id_solver_to_me); - fillYbus(Ybus, is_ac, id_me_to_solver); - } - init_Sbus(Sbus_, id_me_to_solver, id_solver_to_me, slack_bus_id_solver); - fillpv_pq(id_me_to_solver, id_solver_to_me, slack_bus_id_solver); // TODO what if pv and pq changed ? :O + if (solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed()) init_Ybus(Ybus, id_me_to_solver, id_solver_to_me); + if (solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed() || solver_control.need_recompute_ybus()) fillYbus(Ybus, is_ac, id_me_to_solver); + if (solver_control.has_dimension_changed()) init_Sbus(Sbus_, id_me_to_solver, id_solver_to_me, slack_bus_id_solver); + fillpv_pq(id_me_to_solver, id_solver_to_me, slack_bus_id_solver, solver_control); - int nb_bus_total = static_cast(bus_vn_kv_.size()); - total_q_min_per_bus_ = RealVect::Constant(nb_bus_total, 0.); - total_q_max_per_bus_ = RealVect::Constant(nb_bus_total, 0.); - total_gen_per_bus_ = Eigen::VectorXi::Constant(nb_bus_total, 0); - generators_.init_q_vector(nb_bus_total, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); - dc_lines_.init_q_vector(nb_bus_total, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); - fillSbus_me(Sbus_, is_ac, id_me_to_solver); + if (solver_control.need_recompute_sbus()){ + int nb_bus_total = static_cast(bus_vn_kv_.size()); + total_q_min_per_bus_ = RealVect::Constant(nb_bus_total, 0.); + total_q_max_per_bus_ = RealVect::Constant(nb_bus_total, 0.); + total_gen_per_bus_ = Eigen::VectorXi::Constant(nb_bus_total, 0); + generators_.init_q_vector(nb_bus_total, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); + dc_lines_.init_q_vector(nb_bus_total, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); + fillSbus_me(Sbus_, is_ac, id_me_to_solver); + } const int nb_bus_solver = static_cast(id_solver_to_me.size()); CplxVect V = CplxVect::Constant(nb_bus_solver, init_vm_pu_); @@ -576,13 +561,19 @@ void GridModel::fillSbus_me(CplxVect & Sbus, bool ac, const std::vector& id void GridModel::fillpv_pq(const std::vector& id_me_to_solver, std::vector& id_solver_to_me, - Eigen::VectorXi & slack_bus_id_solver) + Eigen::VectorXi & slack_bus_id_solver, + const SolverControl & solver_control) { + // Nothing to do if neither pv, nor pq nor the dimension of the problem has changed + if(!solver_control.has_pq_changed() && !solver_control.has_pv_changed() && !solver_control.has_dimension_changed()) return; + // init pq and pv vector // TODO remove the order here..., i could be faster in this piece of code (looping once through the buses) const int nb_bus = static_cast(id_solver_to_me.size()); // number of bus in the solver! std::vector bus_pq; + bus_pq.reserve(nb_bus); std::vector bus_pv; + bus_pv.reserve(nb_bus); std::vector has_bus_been_added(nb_bus, false); // std::cout << "id_me_to_solver.size(): " << id_me_to_solver.size() << std::endl; diff --git a/src/GridModel.h b/src/GridModel.h index 751a960..54d88b7 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -72,7 +72,10 @@ class GridModel : public DataGeneric DataDCLine::StateRes > StateRes; - GridModel():need_reset_(true), topo_changed_(true), compute_results_(true), init_vm_pu_(1.04), sn_mva_(1.0){ + GridModel(): + solver_control_(), + init_vm_pu_(1.04), + sn_mva_(1.0){ _dc_solver.change_solver(SolverType::DC); _solver.set_gridmodel(this); } @@ -99,7 +102,7 @@ class GridModel : public DataGeneric void turnedoff_pv(){generators_.turnedoff_pv();} // turned off generators are pv bool get_turnedoff_gen_pv() {return generators_.get_turnedoff_gen_pv();} void update_slack_weights(Eigen::Ref > could_be_slack){ - generators_.update_slack_weights(could_be_slack, topo_changed_); + generators_.update_slack_weights(could_be_slack, solver_control_); } const DataSGen & get_static_generators_as_data() const {return sgens_;} @@ -111,8 +114,7 @@ class GridModel : public DataGeneric // solver "control" void change_solver(const SolverType & type){ - need_reset_ = true; - topo_changed_ = true; + solver_control_.tell_all_changed(); if(_solver.is_dc(type)) _dc_solver.change_solver(type); else _solver.change_solver(type); } @@ -235,8 +237,14 @@ class GridModel : public DataGeneric //powerflows // control the need to refactorize the topology - void unset_topo_changed(){topo_changed_ = false;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. - void tell_topo_changed(){topo_changed_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void unset_changes(){ + solver_control_.tell_none_changed(); + } //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_recompute_ybus(){solver_control_.tell_recompute_ybus();} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_recompute_sbus(){solver_control_.tell_recompute_sbus();} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_solver_need_reset(){solver_control_.tell_solver_need_reset();} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_ybus_change_sparsity_pattern(){solver_control_.tell_ybus_change_sparsity_pattern();} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + const SolverControl & get_solver_control() const { return solver_control_;} // dc powerflow CplxVect dc_pf(const CplxVect & Vinit, @@ -254,9 +262,27 @@ class GridModel : public DataGeneric // deactivate a bus. Be careful, if a bus is deactivated, but an element is //still connected to it, it will throw an exception - void deactivate_bus(int bus_id) {_deactivate(bus_id, bus_status_, topo_changed_); } + void deactivate_bus(int bus_id) { + if(bus_status_[bus_id]){ + // bus was connected, dim of matrix change + solver_control_.need_reset_solver(); + solver_control_.need_recompute_sbus(); + solver_control_.need_recompute_ybus(); + solver_control_.ybus_change_sparsity_pattern(); + _deactivate(bus_id, bus_status_); + } + } // if a bus is connected, but isolated, it will make the powerflow diverge - void reactivate_bus(int bus_id) {_reactivate(bus_id, bus_status_, topo_changed_); } + void reactivate_bus(int bus_id) { + if(!bus_status_[bus_id]){ + // bus was not connected, dim of matrix change + solver_control_.need_reset_solver(); + solver_control_.need_recompute_sbus(); + solver_control_.need_recompute_ybus(); + solver_control_.ybus_change_sparsity_pattern(); + _reactivate(bus_id, bus_status_); + } + } int nb_bus() const; // number of activated buses Eigen::Index nb_powerline() const {return powerlines_.nb();} Eigen::Index nb_trafo() const {return trafos_.nb();} @@ -273,57 +299,57 @@ class GridModel : public DataGeneric const RealVect & get_buses() const {return bus_vn_kv_;} //deactivate a powerline (disconnect it) - void deactivate_powerline(int powerline_id) {powerlines_.deactivate(powerline_id, topo_changed_); } - void reactivate_powerline(int powerline_id) {powerlines_.reactivate(powerline_id, topo_changed_); } - void change_bus_powerline_or(int powerline_id, int new_bus_id) {powerlines_.change_bus_or(powerline_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } - void change_bus_powerline_ex(int powerline_id, int new_bus_id) {powerlines_.change_bus_ex(powerline_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } + void deactivate_powerline(int powerline_id) {powerlines_.deactivate(powerline_id, solver_control_); } + void reactivate_powerline(int powerline_id) {powerlines_.reactivate(powerline_id, solver_control_); } + void change_bus_powerline_or(int powerline_id, int new_bus_id) {powerlines_.change_bus_or(powerline_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } + void change_bus_powerline_ex(int powerline_id, int new_bus_id) {powerlines_.change_bus_ex(powerline_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } int get_bus_powerline_or(int powerline_id) {return powerlines_.get_bus_or(powerline_id);} int get_bus_powerline_ex(int powerline_id) {return powerlines_.get_bus_ex(powerline_id);} //deactivate trafo - void deactivate_trafo(int trafo_id) {trafos_.deactivate(trafo_id, topo_changed_); } - void reactivate_trafo(int trafo_id) {trafos_.reactivate(trafo_id, topo_changed_); } - void change_bus_trafo_hv(int trafo_id, int new_bus_id) {trafos_.change_bus_hv(trafo_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } - void change_bus_trafo_lv(int trafo_id, int new_bus_id) {trafos_.change_bus_lv(trafo_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } + void deactivate_trafo(int trafo_id) {trafos_.deactivate(trafo_id, solver_control_); } + void reactivate_trafo(int trafo_id) {trafos_.reactivate(trafo_id, solver_control_); } + void change_bus_trafo_hv(int trafo_id, int new_bus_id) {trafos_.change_bus_hv(trafo_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } + void change_bus_trafo_lv(int trafo_id, int new_bus_id) {trafos_.change_bus_lv(trafo_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } int get_bus_trafo_hv(int trafo_id) {return trafos_.get_bus_hv(trafo_id);} int get_bus_trafo_lv(int trafo_id) {return trafos_.get_bus_lv(trafo_id);} //load - void deactivate_load(int load_id) {loads_.deactivate(load_id, topo_changed_); } - void reactivate_load(int load_id) {loads_.reactivate(load_id, topo_changed_); } - void change_bus_load(int load_id, int new_bus_id) {loads_.change_bus(load_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } - void change_p_load(int load_id, real_type new_p) {loads_.change_p(load_id, new_p, topo_changed_); } - void change_q_load(int load_id, real_type new_q) {loads_.change_q(load_id, new_q, topo_changed_); } + void deactivate_load(int load_id) {loads_.deactivate(load_id, solver_control_); } + void reactivate_load(int load_id) {loads_.reactivate(load_id, solver_control_); } + void change_bus_load(int load_id, int new_bus_id) {loads_.change_bus(load_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } + void change_p_load(int load_id, real_type new_p) {loads_.change_p(load_id, new_p, solver_control_); } + void change_q_load(int load_id, real_type new_q) {loads_.change_q(load_id, new_q, solver_control_); } int get_bus_load(int load_id) {return loads_.get_bus(load_id);} //generator - void deactivate_gen(int gen_id) {generators_.deactivate(gen_id, topo_changed_); } - void reactivate_gen(int gen_id) {generators_.reactivate(gen_id, topo_changed_); } - void change_bus_gen(int gen_id, int new_bus_id) {generators_.change_bus(gen_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } - void change_p_gen(int gen_id, real_type new_p) {generators_.change_p(gen_id, new_p, topo_changed_); } - void change_v_gen(int gen_id, real_type new_v_pu) {generators_.change_v(gen_id, new_v_pu, topo_changed_); } + void deactivate_gen(int gen_id) {generators_.deactivate(gen_id, solver_control_); } + void reactivate_gen(int gen_id) {generators_.reactivate(gen_id, solver_control_); } + void change_bus_gen(int gen_id, int new_bus_id) {generators_.change_bus(gen_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } + void change_p_gen(int gen_id, real_type new_p) {generators_.change_p(gen_id, new_p, solver_control_); } + void change_v_gen(int gen_id, real_type new_v_pu) {generators_.change_v(gen_id, new_v_pu, solver_control_); } int get_bus_gen(int gen_id) {return generators_.get_bus(gen_id);} //shunt - void deactivate_shunt(int shunt_id) {shunts_.deactivate(shunt_id, topo_changed_); } - void reactivate_shunt(int shunt_id) {shunts_.reactivate(shunt_id, topo_changed_); } - void change_bus_shunt(int shunt_id, int new_bus_id) {shunts_.change_bus(shunt_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } - void change_p_shunt(int shunt_id, real_type new_p) {shunts_.change_p(shunt_id, new_p, topo_changed_); } - void change_q_shunt(int shunt_id, real_type new_q) {shunts_.change_q(shunt_id, new_q, topo_changed_); } + void deactivate_shunt(int shunt_id) {shunts_.deactivate(shunt_id, solver_control_); } + void reactivate_shunt(int shunt_id) {shunts_.reactivate(shunt_id, solver_control_); } + void change_bus_shunt(int shunt_id, int new_bus_id) {shunts_.change_bus(shunt_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } + void change_p_shunt(int shunt_id, real_type new_p) {shunts_.change_p(shunt_id, new_p, solver_control_); } + void change_q_shunt(int shunt_id, real_type new_q) {shunts_.change_q(shunt_id, new_q, solver_control_); } int get_bus_shunt(int shunt_id) {return shunts_.get_bus(shunt_id);} //static gen - void deactivate_sgen(int sgen_id) {sgens_.deactivate(sgen_id, topo_changed_); } - void reactivate_sgen(int sgen_id) {sgens_.reactivate(sgen_id, topo_changed_); } - void change_bus_sgen(int sgen_id, int new_bus_id) {sgens_.change_bus(sgen_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } - void change_p_sgen(int sgen_id, real_type new_p) {sgens_.change_p(sgen_id, new_p, topo_changed_); } - void change_q_sgen(int sgen_id, real_type new_q) {sgens_.change_q(sgen_id, new_q, topo_changed_); } + void deactivate_sgen(int sgen_id) {sgens_.deactivate(sgen_id, solver_control_); } + void reactivate_sgen(int sgen_id) {sgens_.reactivate(sgen_id, solver_control_); } + void change_bus_sgen(int sgen_id, int new_bus_id) {sgens_.change_bus(sgen_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } + void change_p_sgen(int sgen_id, real_type new_p) {sgens_.change_p(sgen_id, new_p, solver_control_); } + void change_q_sgen(int sgen_id, real_type new_q) {sgens_.change_q(sgen_id, new_q, solver_control_); } int get_bus_sgen(int sgen_id) {return sgens_.get_bus(sgen_id);} //storage units - void deactivate_storage(int storage_id) {storages_.deactivate(storage_id, topo_changed_); } - void reactivate_storage(int storage_id) {storages_.reactivate(storage_id, topo_changed_); } - void change_bus_storage(int storage_id, int new_bus_id) {storages_.change_bus(storage_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } + void deactivate_storage(int storage_id) {storages_.deactivate(storage_id, solver_control_); } + void reactivate_storage(int storage_id) {storages_.reactivate(storage_id, solver_control_); } + void change_bus_storage(int storage_id, int new_bus_id) {storages_.change_bus(storage_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } void change_p_storage(int storage_id, real_type new_p) { // if(new_p == 0.) // { @@ -334,19 +360,19 @@ class GridModel : public DataGeneric // reactivate_storage(storage_id); // requirement from grid2op, might be discussed // storages_.change_p(storage_id, new_p, need_reset_); // } - storages_.change_p(storage_id, new_p, topo_changed_); + storages_.change_p(storage_id, new_p, solver_control_); } - void change_q_storage(int storage_id, real_type new_q) {storages_.change_q(storage_id, new_q, topo_changed_); } + void change_q_storage(int storage_id, real_type new_q) {storages_.change_q(storage_id, new_q, solver_control_); } int get_bus_storage(int storage_id) {return storages_.get_bus(storage_id);} //deactivate a powerline (disconnect it) - void deactivate_dcline(int dcline_id) {dc_lines_.deactivate(dcline_id, topo_changed_); } - void reactivate_dcline(int dcline_id) {dc_lines_.reactivate(dcline_id, topo_changed_); } - void change_p_dcline(int dcline_id, real_type new_p) {dc_lines_.change_p(dcline_id, new_p, topo_changed_); } - void change_v_or_dcline(int dcline_id, real_type new_v_pu) {dc_lines_.change_v_or(dcline_id, new_v_pu, topo_changed_); } - void change_v_ex_dcline(int dcline_id, real_type new_v_pu) {dc_lines_.change_v_ex(dcline_id, new_v_pu, topo_changed_); } - void change_bus_dcline_or(int dcline_id, int new_bus_id) {dc_lines_.change_bus_or(dcline_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } - void change_bus_dcline_ex(int dcline_id, int new_bus_id) {dc_lines_.change_bus_ex(dcline_id, new_bus_id, topo_changed_, static_cast(bus_vn_kv_.size())); } + void deactivate_dcline(int dcline_id) {dc_lines_.deactivate(dcline_id, solver_control_); } + void reactivate_dcline(int dcline_id) {dc_lines_.reactivate(dcline_id, solver_control_); } + void change_p_dcline(int dcline_id, real_type new_p) {dc_lines_.change_p(dcline_id, new_p, solver_control_); } + void change_v_or_dcline(int dcline_id, real_type new_v_pu) {dc_lines_.change_v_or(dcline_id, new_v_pu, solver_control_); } + void change_v_ex_dcline(int dcline_id, real_type new_v_pu) {dc_lines_.change_v_ex(dcline_id, new_v_pu, solver_control_); } + void change_bus_dcline_or(int dcline_id, int new_bus_id) {dc_lines_.change_bus_or(dcline_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } + void change_bus_dcline_ex(int dcline_id, int new_bus_id) {dc_lines_.change_bus_ex(dcline_id, new_bus_id, solver_control_, static_cast(bus_vn_kv_.size())); } int get_bus_dcline_or(int dcline_id) {return dc_lines_.get_bus_or(dcline_id);} int get_bus_dcline_ex(int dcline_id) {return dc_lines_.get_bus_ex(dcline_id);} @@ -550,7 +576,7 @@ class GridModel : public DataGeneric std::vector & id_solver_to_me, Eigen::VectorXi & slack_bus_id_solver, bool is_ac, - bool reset_solver); + const SolverControl & solver_control); void init_Ybus(Eigen::SparseMatrix & Ybus, std::vector & id_me_to_solver, std::vector& id_solver_to_me); @@ -561,7 +587,8 @@ class GridModel : public DataGeneric void fillYbus(Eigen::SparseMatrix & res, bool ac, const std::vector& id_me_to_solver); void fillSbus_me(CplxVect & res, bool ac, const std::vector& id_me_to_solver); void fillpv_pq(const std::vector& id_me_to_solver, std::vector& id_solver_to_me, - Eigen::VectorXi & slack_bus_id_solver); + Eigen::VectorXi & slack_bus_id_solver, + const SolverControl & solver_control); // results /**process the results from the solver to this instance @@ -652,8 +679,11 @@ class GridModel : public DataGeneric // member of the grid // static const int _deactivated_bus_id; - bool need_reset_; - bool topo_changed_; + // bool need_reset_solver_; // some matrices change size, needs to be computed + // bool need_recompute_sbus_; // some coeff of sbus changed, need to recompute it + // bool need_recompute_ybus_; // some coeff of ybus changed, but not its sparsity pattern + // bool ybus_change_sparsity_pattern_; // sparsity pattern of ybus changed (and so are its coeff) + SolverControl solver_control_; bool compute_results_; real_type init_vm_pu_; // default vm initialization, mainly for dc powerflow real_type sn_mva_; diff --git a/src/Utils.h b/src/Utils.h index f8a6d02..7bbf95f 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -49,4 +49,69 @@ enum class ErrorType {NoError, SingularMatrix, TooManyIterations, InifiniteValue #define VERSION_MINOR -1 #endif +class SolverControl +{ + public: + SolverControl(): + change_dimension_(true), + pv_changed_(true), + pq_changed_(true), + slack_participate_changed_(true), + need_reset_solver_(true), + need_recompute_sbus_(true), + need_recompute_ybus_(true), + ybus_change_sparsity_pattern_(true) + {}; + + void tell_all_changed(){ + change_dimension_ = true; + pv_changed_ = true; + pq_changed_ = true; + slack_participate_changed_ = true; + need_reset_solver_ = true; + need_recompute_sbus_ = true; + need_recompute_ybus_ = true; + ybus_change_sparsity_pattern_ = true; + } + + void tell_none_changed(){ + change_dimension_ = false; + pv_changed_ = false; + pq_changed_ = true; + slack_participate_changed_ = false; + need_reset_solver_ = false; + need_recompute_sbus_ = false; + need_recompute_ybus_ = false; + ybus_change_sparsity_pattern_ = false; + } + + void tell_dimension_changed(){change_dimension_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_pv_changed(){pv_changed_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_pq_changed(){pq_changed_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_slack_participate_changed(){slack_participate_changed_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_recompute_ybus(){need_recompute_ybus_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_recompute_sbus(){need_recompute_sbus_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_solver_need_reset(){need_reset_solver_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + void tell_ybus_change_sparsity_pattern(){ybus_change_sparsity_pattern_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. + + bool has_dimension_changed() const {return change_dimension_;} + bool has_pv_changed() const {return pv_changed_;} + bool has_pq_changed() const {return pq_changed_;} + bool has_tell_slack_participate_changed() const {return slack_participate_changed_;} + bool need_reset_solver() const {return need_reset_solver_;} + bool need_recompute_sbus() const {return need_recompute_sbus_;} + bool need_recompute_ybus() const {return need_recompute_ybus_;} + bool ybus_change_sparsity_pattern() const {return ybus_change_sparsity_pattern_;} + + protected: + bool change_dimension_; + bool pv_changed_; + bool pq_changed_; + bool slack_participate_changed_; + bool need_reset_solver_; // some matrices change size, needs to be computed + bool need_recompute_sbus_; // some coeff of sbus changed, need to recompute it + bool need_recompute_ybus_; // some coeff of ybus changed, but not its sparsity pattern + bool ybus_change_sparsity_pattern_; // sparsity pattern of ybus changed (and so are its coeff), or ybus change of dimension +}; + #endif // UTILS_H diff --git a/src/main.cpp b/src/main.cpp index c5130a4..f44cb45 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -583,6 +583,19 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def("get_line_param", &PandaPowerConverter::get_line_param) .def("get_trafo_param", &PandaPowerConverter::get_trafo_param); + + py::class_(m, "SolverControl", "TODO") + .def(py::init<>()) + .def("has_dimension_changed", &SolverControl::has_dimension_changed, "TODO") + .def("has_pv_changed", &SolverControl::has_pv_changed, "TODO") + .def("has_pq_changed", &SolverControl::has_pq_changed, "TODO") + .def("has_tell_slack_participate_changed", &SolverControl::has_tell_slack_participate_changed, "TODO") + .def("need_reset_solver", &SolverControl::need_reset_solver, "TODO") + .def("need_recompute_sbus", &SolverControl::need_recompute_sbus, "TODO") + .def("need_recompute_ybus", &SolverControl::need_recompute_ybus, "TODO") + .def("ybus_change_sparsity_pattern", &SolverControl::ybus_change_sparsity_pattern, "TODO") + ; + py::class_(m, "GridModel", DocGridModel::GridModel.c_str()) .def(py::init<>()) .def("copy", &GridModel::copy) @@ -775,8 +788,12 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def("reactivate_result_computation", &GridModel::reactivate_result_computation, DocGridModel::reactivate_result_computation.c_str()) .def("dc_pf", &GridModel::dc_pf, DocGridModel::dc_pf.c_str()) .def("ac_pf", &GridModel::ac_pf, DocGridModel::ac_pf.c_str()) - .def("unset_topo_changed", &GridModel::unset_topo_changed, DocGridModel::_internal_do_not_use.c_str()) - .def("tell_topo_changed", &GridModel::tell_topo_changed, DocGridModel::_internal_do_not_use.c_str()) + .def("unset_changes", &GridModel::unset_changes, DocGridModel::_internal_do_not_use.c_str()) + .def("tell_recompute_ybus", &GridModel::tell_recompute_ybus, DocGridModel::_internal_do_not_use.c_str()) + .def("tell_recompute_sbus", &GridModel::tell_recompute_sbus, DocGridModel::_internal_do_not_use.c_str()) + .def("tell_solver_need_reset", &GridModel::tell_solver_need_reset, DocGridModel::_internal_do_not_use.c_str()) + .def("tell_ybus_change_sparsity_pattern", &GridModel::tell_ybus_change_sparsity_pattern, DocGridModel::_internal_do_not_use.c_str()) + .def("get_solver_control", &GridModel::get_solver_control, "TODO") .def("compute_newton", &GridModel::ac_pf, DocGridModel::ac_pf.c_str()) // apply action faster (optimized for grid2op representation) From c5c85bb15aebe6710e9c29edd984344d319679c1 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Dec 2023 10:38:58 +0100 Subject: [PATCH 02/42] now it compiles, need to check tests, and implement logic solver side [skip ci] --- src/BaseSolver.cpp | 6 +++++- src/BaseSolver.h | 14 ++++++-------- src/ChooseSolver.h | 9 ++++++++- src/DataGen.cpp | 6 +++--- src/DataGen.h | 31 +++++++++++++++++++++---------- src/GridModel.cpp | 29 +++++++++++++++-------------- src/GridModel.h | 6 ++++-- src/Utils.h | 11 +++++++++-- src/main.cpp | 2 +- 9 files changed, 72 insertions(+), 42 deletions(-) diff --git a/src/BaseSolver.cpp b/src/BaseSolver.cpp index e5c0bc7..c2df65e 100644 --- a/src/BaseSolver.cpp +++ b/src/BaseSolver.cpp @@ -9,7 +9,8 @@ #include "BaseSolver.h" #include "GridModel.h" // needs to be included here because of the forward declaration -void BaseSolver::reset(const SolverControl & solver_control){ + +void BaseSolver::reset(){ // reset timers reset_timer(); @@ -20,8 +21,11 @@ void BaseSolver::reset(const SolverControl & solver_control){ V_ = RealVect(); // voltage angle // TODO solver control: see if I could reuse some of these nr_iter_ = 0; // number of iteration performs by the algorithm err_ = ErrorType::NotInitError; //error message: + + _solver_control = SolverControl(); } + RealVect BaseSolver::_evaluate_Fx(const Eigen::SparseMatrix & Ybus, const CplxVect & V, const CplxVect & Sbus, diff --git a/src/BaseSolver.h b/src/BaseSolver.h index 8b8006d..deeaaa1 100644 --- a/src/BaseSolver.h +++ b/src/BaseSolver.h @@ -98,10 +98,10 @@ class BaseSolver : public BaseConstants real_type tol ) = 0 ; -<<<<<<< HEAD - virtual - void reset(const SolverControl & solver_control); -======= + void tell_solver_control(const SolverControl & solver_control){ + _solver_control = solver_control; + } + virtual void reset(); virtual RealMat get_ptdf(const Eigen::SparseMatrix & dcYbus){ throw std::runtime_error("Impossible to get the PTDF matrix with this solver type."); } @@ -111,9 +111,6 @@ class BaseSolver : public BaseConstants virtual Eigen::SparseMatrix get_bsdf(){ // TODO interface is likely to change throw std::runtime_error("Impossible to get the BSDF matrix with this solver type."); } - - virtual void reset(); ->>>>>>> bd-dev protected: virtual void reset_timer(){ @@ -232,7 +229,8 @@ class BaseSolver : public BaseConstants double timer_total_nr_; const GridModel * _gridmodel; // does not have ownership so that's fine (pointer to the base gridmodel, can be used for some powerflow) - + SolverControl _solver_control; + private: // no copy allowed BaseSolver( const BaseSolver & ) ; diff --git a/src/ChooseSolver.h b/src/ChooseSolver.h index 4c713b6..08fb0cd 100644 --- a/src/ChooseSolver.h +++ b/src/ChooseSolver.h @@ -173,10 +173,12 @@ class ChooseSolver #endif // now switch the union (see https://en.cppreference.com/w/cpp/language/union) + // reset the old solver reset(); - // and assign the right solver _solver_type = type; + // and now reset the new one + reset(); } void reset() @@ -275,6 +277,11 @@ class ChooseSolver return res; } + void tell_solver_control(const SolverControl & solver_control){ + auto p_solver = get_prt_solver("get_ptdf", true); + p_solver -> tell_solver_control(solver_control); + } + /** apparently i cannot pass a const ref for a sparse matrix in python**/ Eigen::SparseMatrix get_J_python() const{ Eigen::SparseMatrix res = get_J(); diff --git a/src/DataGen.cpp b/src/DataGen.cpp index 699620b..bc60fd2 100644 --- a/src/DataGen.cpp +++ b/src/DataGen.cpp @@ -438,16 +438,16 @@ void DataGen::update_slack_weights(Eigen::Ref 0.){ // gen is properly connected if(!gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); // it was not in the slack before, so I need to reset the solver - add_slackbus(gen_id, p_mw_(gen_id)); + add_slackbus(gen_id, p_mw_(gen_id), solver_control); }else{ // gen is now "turned off" if(gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); // it was in the slack before, so I need to reset the solver - remove_slackbus(gen_id); + remove_slackbus(gen_id, solver_control); } }else{ if(gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); // it was in the slack before, I need to reset the solver - remove_slackbus(gen_id); + remove_slackbus(gen_id, solver_control); } } } diff --git a/src/DataGen.h b/src/DataGen.h index 3b90e2a..e2fb9cb 100644 --- a/src/DataGen.h +++ b/src/DataGen.h @@ -174,25 +174,29 @@ class DataGen: public DataGeneric we suppose that the data are correct (ie gen_id in the proper range, and weight > 0.) This is checked in GridModel, and not at this stage **/ - void add_slackbus(int gen_id, real_type weight){ - gen_slackbus_[gen_id] = true; - gen_slack_weight_[gen_id] = weight; + void add_slackbus(int gen_id, real_type weight, SolverControl & solver_control){ // TODO DEBUG MODE if(weight <= 0.) throw std::runtime_error("DataGen::add_slackbus Cannot assign a negative weight to the slack bus."); + if(!gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); + gen_slackbus_[gen_id] = true; + if(gen_slack_weight_[gen_id] != weight) solver_control.tell_slack_weight_changed(); + gen_slack_weight_[gen_id] = weight; } - void remove_slackbus(int gen_id){ + void remove_slackbus(int gen_id, SolverControl & solver_control){ + if(!gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); gen_slackbus_[gen_id] = false; gen_slack_weight_[gen_id] = 0.; } void remove_all_slackbus(){ const int nb_gen = nb(); + SolverControl unused_solver_control; for(int gen_id = 0; gen_id < nb_gen; ++gen_id) { - remove_slackbus(gen_id); + remove_slackbus(gen_id, unused_solver_control); } } // returns only the gen_id with the highest p that is connected to this bus ! - int assign_slack_bus(int slack_bus_id, const std::vector & gen_p_per_bus){ + int assign_slack_bus(int slack_bus_id, const std::vector & gen_p_per_bus, SolverControl & solver_control){ const int nb_gen = nb(); int res_gen_id = -1; real_type max_p = -1.; @@ -201,7 +205,7 @@ class DataGen: public DataGeneric if(!status_[gen_id]) continue; if(bus_id_(gen_id) != slack_bus_id) continue; const real_type p_mw = p_mw_(gen_id); - add_slackbus(gen_id, p_mw / gen_p_per_bus[slack_bus_id]); + add_slackbus(gen_id, p_mw / gen_p_per_bus[slack_bus_id], solver_control); if((p_mw > max_p) || (res_gen_id == -1) ){ res_gen_id = gen_id; max_p = p_mw; @@ -224,14 +228,18 @@ class DataGen: public DataGeneric void turnedoff_no_pv(){turnedoff_gen_pv_=false;} // turned off generators are not pv void turnedoff_pv(){turnedoff_gen_pv_=true;} // turned off generators are pv bool get_turnedoff_gen_pv() const {return turnedoff_gen_pv_;} - void update_slack_weights(Eigen::Ref > could_be_slack, SolverControl & solver_control); + void update_slack_weights(Eigen::Ref > could_be_slack, + SolverControl & solver_control); void deactivate(int gen_id, SolverControl & solver_control) { if (status_[gen_id]){ solver_control.tell_recompute_sbus(); if(voltage_regulator_on_[gen_id]) solver_control.tell_v_changed(); if(!turnedoff_gen_pv_) solver_control.tell_pv_changed(); - if(gen_slack_weight_[gen_id]) solver_control.tell_slack_participate_changed(); + if(gen_slack_weight_[gen_id]){ + solver_control.tell_slack_participate_changed(); + solver_control.tell_slack_weight_changed(); + } } _deactivate(gen_id, status_); } @@ -240,7 +248,10 @@ class DataGen: public DataGeneric solver_control.tell_recompute_sbus(); if(voltage_regulator_on_[gen_id]) solver_control.tell_v_changed(); if(!turnedoff_gen_pv_) solver_control.tell_pv_changed(); - if(gen_slack_weight_[gen_id]) solver_control.tell_slack_participate_changed(); + if(gen_slack_weight_[gen_id]){ + solver_control.tell_slack_participate_changed(); + solver_control.tell_slack_weight_changed(); + } } _reactivate(gen_id, status_); } diff --git a/src/GridModel.cpp b/src/GridModel.cpp index 58ef427..2aee652 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -274,7 +274,6 @@ void GridModel::reset(bool reset_solver, bool reset_ac, bool reset_dc) _solver.set_gridmodel(this); _dc_solver.set_gridmodel(this); } - // std::cout << "GridModel::reset called" << std::endl; } CplxVect GridModel::ac_pf(const CplxVect & Vinit, @@ -415,15 +414,16 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, const SolverControl & solver_control) { // TODO get rid of the "is_ac" argument: this info is available in the _solver already - if(is_ac) _solver.reset(solver_control); - else _dc_solver.reset(solver_control); - slack_bus_id_ = generators_.get_slack_bus_id(); + if(is_ac) _solver.tell_solver_control(solver_control); + else _dc_solver.tell_solver_control(solver_control); + + if (solver_control.has_slack_participate_changed()) slack_bus_id_ = generators_.get_slack_bus_id(); if (solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed()) init_Ybus(Ybus, id_me_to_solver, id_solver_to_me); if (solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed() || solver_control.need_recompute_ybus()) fillYbus(Ybus, is_ac, id_me_to_solver); if (solver_control.has_dimension_changed()) init_Sbus(Sbus_, id_me_to_solver, id_solver_to_me, slack_bus_id_solver); - fillpv_pq(id_me_to_solver, id_solver_to_me, slack_bus_id_solver, solver_control); + if (solver_control.has_slack_participate_changed() || solver_control.has_pv_changed() || solver_control.has_pq_changed()) fillpv_pq(id_me_to_solver, id_solver_to_me, slack_bus_id_solver, solver_control); - if (solver_control.need_recompute_sbus()){ + if (solver_control.has_dimension_changed() || solver_control.need_recompute_sbus() && is_ac){ int nb_bus_total = static_cast(bus_vn_kv_.size()); total_q_min_per_bus_ = RealVect::Constant(nb_bus_total, 0.); total_q_max_per_bus_ = RealVect::Constant(nb_bus_total, 0.); @@ -477,7 +477,7 @@ void GridModel::process_results(bool conv, // compute the results of the flows, P,Q,V of loads etc. compute_results(ac); } - need_reset_ = false; + solver_control_.tell_none_changed(); const CplxVect & res_tmp = ac ? _solver.get_V(): _dc_solver.get_V() ; // convert back the results to "big" vector @@ -487,7 +487,7 @@ void GridModel::process_results(bool conv, } else { //powerflow diverge reset_results(); - need_reset_ = true; // in this case, the powerflow diverge, so i need to recompute Ybus next time + // TODO solver control ??? something to do here ? } } @@ -698,13 +698,14 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, // pre process the data to define a proper jacobian matrix, the proper voltage vector etc. bool is_ac = false; - bool reset_solver = topo_changed_; // I reset the solver only if the topology change CplxVect V = pre_process_solver(Vinit, Ybus_dc_, id_me_to_dc_solver_, id_dc_solver_to_me_, slack_bus_id_dc_solver_, - is_ac, reset_solver); + is_ac, solver_control_); // start the solver - slack_weights_ = generators_.get_slack_weights(Ybus_dc_.rows(), id_me_to_dc_solver_); + if(solver_control_.has_slack_participate_changed() || + solver_control_.has_pv_changed() || + solver_control_.has_slack_weight_changed()) slack_weights_ = generators_.get_slack_weights(Ybus_dc_.rows(), id_me_to_dc_solver_); conv = _dc_solver.compute_pf(Ybus_dc_, V, dcSbus_, slack_bus_id_dc_solver_, slack_weights_, bus_pv_, bus_pq_, max_iter, tol); // store results (fase -> because I am in dc mode) @@ -761,7 +762,7 @@ void GridModel::add_gen_slackbus(int gen_id, real_type weight){ exc_ << "GridModel::add_gen_slackbus: please enter a valid weight for the slack bus (> 0.)"; throw std::runtime_error(exc_.str()); } - generators_.add_slackbus(gen_id, weight); + generators_.add_slackbus(gen_id, weight, solver_control_); } void GridModel::remove_gen_slackbus(int gen_id){ @@ -781,7 +782,7 @@ void GridModel::remove_gen_slackbus(int gen_id){ exc_ << "Generator with id " << gen_id << " does not exist and can't be the slack bus"; throw std::runtime_error(exc_.str()); } - generators_.remove_slackbus(gen_id); + generators_.remove_slackbus(gen_id, solver_control_); } /** GRID2OP SPECIFIC REPRESENTATION **/ @@ -999,7 +1000,7 @@ std::tuple GridModel::assign_slack_to_most_connected(){ // and reset the slack bus generators_.remove_all_slackbus(); - res_gen_id = generators_.assign_slack_bus(res_bus_id, gen_p_per_bus); + res_gen_id = generators_.assign_slack_bus(res_bus_id, gen_p_per_bus, solver_control_); std::get<1>(res) = res_gen_id; slack_bus_id_ = std::vector(); slack_weights_ = RealVect(); diff --git a/src/GridModel.h b/src/GridModel.h index 271f9b1..8295534 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -704,7 +704,8 @@ class GridModel : public DataGeneric { (this->*fun_react)(el_id); // eg reactivate_load(load_id); (this->*fun_change)(el_id, new_bus_backend); // eg change_bus_load(load_id, new_bus_backend); - topo_changed_ = true; + // topo_changed_ = true; + solver_control_.tell_dimension_changed(); } } else{ if(has_changed(el_pos)) @@ -714,7 +715,8 @@ class GridModel : public DataGeneric // bus_status_ is set to "false" in GridModel.update_topo // and a bus is activated if (and only if) one element is connected to it. // I must not set `bus_status_[new_bus_backend] = false;` in this case ! - topo_changed_ = true; + // topo_changed_ = true; + solver_control_.tell_dimension_changed(); } } } diff --git a/src/Utils.h b/src/Utils.h index 42a3617..0d56bc0 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -64,6 +64,7 @@ class SolverControl need_recompute_sbus_(true), need_recompute_ybus_(true), v_changed_(true), + slack_weight_changed_(true), ybus_change_sparsity_pattern_(true) {}; @@ -76,6 +77,7 @@ class SolverControl need_recompute_sbus_ = true; need_recompute_ybus_ = true; v_changed_ = true; + slack_weight_changed_ = true; ybus_change_sparsity_pattern_ = true; } @@ -88,6 +90,7 @@ class SolverControl need_recompute_sbus_ = false; need_recompute_ybus_ = false; v_changed_ = false; + slack_weight_changed_ = false; ybus_change_sparsity_pattern_ = false; } @@ -109,16 +112,19 @@ class SolverControl void tell_ybus_change_sparsity_pattern(){ybus_change_sparsity_pattern_ = true;} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. // tell at least one generator changed its v setpoint void tell_v_changed(){v_changed_ = true;} + // at least one generator has changed its slack participation + void tell_slack_weight_changed(){slack_weight_changed_ = true;} bool has_dimension_changed() const {return change_dimension_;} bool has_pv_changed() const {return pv_changed_;} bool has_pq_changed() const {return pq_changed_;} - bool has_tell_slack_participate_changed() const {return slack_participate_changed_;} + bool has_slack_participate_changed() const {return slack_participate_changed_;} bool need_reset_solver() const {return need_reset_solver_;} bool need_recompute_sbus() const {return need_recompute_sbus_;} bool need_recompute_ybus() const {return need_recompute_ybus_;} bool ybus_change_sparsity_pattern() const {return ybus_change_sparsity_pattern_;} - bool v_changed() const {return v_changed_;} + bool has_slack_weight_changed() const {return slack_weight_changed_;} + bool has_v_changed() const {return v_changed_;} protected: bool change_dimension_; @@ -129,6 +135,7 @@ class SolverControl bool need_recompute_sbus_; // some coeff of sbus changed, need to recompute it bool need_recompute_ybus_; // some coeff of ybus changed, but not its sparsity pattern bool v_changed_; + bool slack_weight_changed_; bool ybus_change_sparsity_pattern_; // sparsity pattern of ybus changed (and so are its coeff), or ybus change of dimension }; diff --git a/src/main.cpp b/src/main.cpp index 319d1e2..a743bba 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -598,7 +598,7 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def("has_dimension_changed", &SolverControl::has_dimension_changed, "TODO") .def("has_pv_changed", &SolverControl::has_pv_changed, "TODO") .def("has_pq_changed", &SolverControl::has_pq_changed, "TODO") - .def("has_tell_slack_participate_changed", &SolverControl::has_tell_slack_participate_changed, "TODO") + .def("has_slack_participate_changed", &SolverControl::has_slack_participate_changed, "TODO") .def("need_reset_solver", &SolverControl::need_reset_solver, "TODO") .def("need_recompute_sbus", &SolverControl::need_recompute_sbus, "TODO") .def("need_recompute_ybus", &SolverControl::need_recompute_ybus, "TODO") From 8334ef7c61f700cc9c0469386f1ea69cbe7a5ad4 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Dec 2023 16:54:17 +0100 Subject: [PATCH 03/42] python calls updated, now need to solve the segfault [skip ci] --- lightsim2grid/lightSimBackend.py | 11 +++-- src/BaseSolver.cpp | 1 + src/ChooseSolver.cpp | 77 ++++++++++++++++++++++++++++++++ src/ChooseSolver.h | 16 ++++++- src/DataDCLine.cpp | 2 - src/GridModel.cpp | 69 +++++++++++++++++++++++----- src/GridModel.h | 4 +- 7 files changed, 159 insertions(+), 21 deletions(-) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index cf81459..a8c66c5 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -735,7 +735,7 @@ def _aux_finish_setup_after_reading(self): self.storage_theta = np.full(cls.n_storage, dtype=dt_float, fill_value=np.NaN).reshape(-1) self._count_object_per_bus() - self._grid.tell_topo_changed() + self._grid.tell_solver_need_reset() self.__me_at_init = self._grid.copy() self.__init_topo_vect = np.ones(cls.dim_topo, dtype=dt_int) self.__init_topo_vect[:] = self.topo_vect @@ -894,7 +894,9 @@ def runpf(self, is_dc=False): self._grid.deactivate_result_computation() # if I init with dc values, it should depends on previous state self.V[:] = self._grid.get_init_vm_pu() # see issue 30 + print("before dc pf") Vdc = self._grid.dc_pf(copy.deepcopy(self.V), self.max_it, self.tol) + print("after dc pf") self._grid.reactivate_result_computation() if Vdc.shape[0] == 0: raise BackendError(f"Divergence of DC powerflow (non connected grid) at the initialization of AC powerflow. Detailed error: {self._grid.get_dc_solver().get_error()}") @@ -903,6 +905,7 @@ def runpf(self, is_dc=False): V_init = copy.deepcopy(self.V) tick = time.perf_counter() self._timer_preproc += tick - beg_preproc + print("before ac pf") V = self._grid.ac_pf(V_init, self.max_it, self.tol) self._timer_solver += time.perf_counter() - tick if V.shape[0] == 0: @@ -968,11 +971,11 @@ def runpf(self, is_dc=False): if (self.line_or_theta >= 1e6).any() or (self.line_ex_theta >= 1e6).any(): raise BackendError(f"Some theta are above 1e6 which should not be happening !") res = True - self._grid.unset_topo_changed() + self._grid.unset_changes() self._timer_postproc += time.perf_counter() - beg_postroc except Exception as exc_: # of the powerflow has not converged, results are Nan - self._grid.tell_topo_changed() + self._grid.unset_changes() self._fill_nans() res = False my_exc_ = exc_ @@ -1190,7 +1193,7 @@ def get_current_solver_type(self): def reset(self, grid_path, grid_filename=None): self._fill_nans() self._grid = self.__me_at_init.copy() - self._grid.tell_topo_changed() + self._grid.unset_changes() self._grid.change_solver(self.__current_solver_type) self._handle_turnedoff_pv() self.topo_vect[:] = self.__init_topo_vect diff --git a/src/BaseSolver.cpp b/src/BaseSolver.cpp index c2df65e..61b9d49 100644 --- a/src/BaseSolver.cpp +++ b/src/BaseSolver.cpp @@ -23,6 +23,7 @@ void BaseSolver::reset(){ err_ = ErrorType::NotInitError; //error message: _solver_control = SolverControl(); + _solver_control.tell_all_changed(); } diff --git a/src/ChooseSolver.cpp b/src/ChooseSolver.cpp index 165f787..bfd87dd 100644 --- a/src/ChooseSolver.cpp +++ b/src/ChooseSolver.cpp @@ -7,3 +7,80 @@ // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. #include "ChooseSolver.h" + +std::ostream& operator<<(std::ostream& out, const SolverType& solver_type) +{ + switch (solver_type) + { + case SolverType::SparseLU: + out << "SparseLU"; + break; + case SolverType::KLU: + out << "KLU"; + break; + case SolverType::GaussSeidel: + out << "GaussSeidel"; + break; + case SolverType::DC: + out << "DC"; + break; + case SolverType::GaussSeidelSynch: + out << "GaussSeidelSynch"; + break; + case SolverType::NICSLU: + out << "NICSLU"; + break; + case SolverType::SparseLUSingleSlack: + out << "SparseLUSingleSlack"; + break; + case SolverType::KLUSingleSlack: + out << "KLUSingleSlack"; + break; + case SolverType::NICSLUSingleSlack: + out << "NICSLUSingleSlack"; + break; + case SolverType::KLUDC: + out << "KLUDC"; + break; + case SolverType::NICSLUDC: + out << "NICSLUDC"; + break; + case SolverType::CKTSO: + out << "CKTSO"; + break; + case SolverType::CKTSOSingleSlack: + out << "CKTSOSingleSlack"; + break; + case SolverType::CKTSODC: + out << "CKTSODC"; + break; + case SolverType::FDPF_XB_SparseLU: + out << "FDPF_XB_SparseLU"; + break; + case SolverType::FDPF_BX_SparseLU: + out << "FDPF_BX_SparseLU"; + break; + case SolverType::FDPF_XB_KLU: + out << "FDPF_XB_KLU"; + break; + case SolverType::FDPF_BX_KLU: + out << "FDPF_BX_KLU"; + break; + case SolverType::FDPF_XB_NICSLU: + out << "FDPF_XB_NICSLU"; + break; + case SolverType::FDPF_BX_NICSLU: + out << "FDPF_BX_NICSLU"; + break; + case SolverType::FDPF_XB_CKTSO: + out << "FDPF_XB_CKTSO"; + break; + case SolverType::FDPF_BX_CKTSO: + out << "FDPF_BX_CKTSO"; + break; + default: + out << "(unknown)"; + break; + } + return out; +} diff --git a/src/ChooseSolver.h b/src/ChooseSolver.h index 08fb0cd..2961d8a 100644 --- a/src/ChooseSolver.h +++ b/src/ChooseSolver.h @@ -27,6 +27,8 @@ enum class SolverType {SparseLU, KLU, GaussSeidel, DC, GaussSeidelSynch, NICSLU, FDPF_XB_CKTSO, FDPF_BX_CKTSO // from 0.7.5 }; + +std::ostream& operator<<(std::ostream& out, const SolverType& solver_type); // TODO define a template class instead of these weird stuff !!! // TODO export all methods from base class ! @@ -278,7 +280,7 @@ class ChooseSolver } void tell_solver_control(const SolverControl & solver_control){ - auto p_solver = get_prt_solver("get_ptdf", true); + auto p_solver = get_prt_solver("tell_solver_control", false); p_solver -> tell_solver_control(solver_control); } @@ -312,7 +314,17 @@ class ChooseSolver private: void check_right_solver(const std::string & error_msg) const { - if(_solver_type != _type_used_for_nr) throw std::runtime_error("ChooseSolver: Solver mismatch when calling '"+error_msg+"': current solver is not the last solver used to perform a powerflow"); + if(_solver_type != _type_used_for_nr){ + std::ostringstream exc_; + exc_ << "ChooseSolver: Solver mismatch when calling '"; + exc_ << error_msg; + exc_ << ": current solver ("; + exc_ << _solver_type; + exc_ << ") is not the one used to perform a powerflow ("; + exc_ << _type_used_for_nr; + exc_ << ")."; + throw std::runtime_error(exc_.str()); + } #ifndef KLU_SOLVER_AVAILABLE if(_solver_type == SolverType::KLU){ diff --git a/src/DataDCLine.cpp b/src/DataDCLine.cpp index e7c51c4..c74301b 100644 --- a/src/DataDCLine.cpp +++ b/src/DataDCLine.cpp @@ -87,11 +87,9 @@ void DataDCLine::disconnect_if_not_in_main_component(std::vector & busbar_ auto bus_or = bus_or_id(i); auto bus_ex = bus_ex_id(i); if(!busbar_in_main_component[bus_or]) { - bool tmp = false; from_gen_.deactivate(i, unused_solver_control); } if(!busbar_in_main_component[bus_ex]) { - bool tmp = false; to_gen_.deactivate(i, unused_solver_control); } // if(!busbar_in_main_component[bus_or] || !busbar_in_main_component[bus_ex]){ diff --git a/src/GridModel.cpp b/src/GridModel.cpp index 2aee652..f4768f2 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -414,25 +414,67 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, const SolverControl & solver_control) { // TODO get rid of the "is_ac" argument: this info is available in the _solver already - if(is_ac) _solver.tell_solver_control(solver_control); - else _dc_solver.tell_solver_control(solver_control); - - if (solver_control.has_slack_participate_changed()) slack_bus_id_ = generators_.get_slack_bus_id(); - if (solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed()) init_Ybus(Ybus, id_me_to_solver, id_solver_to_me); - if (solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed() || solver_control.need_recompute_ybus()) fillYbus(Ybus, is_ac, id_me_to_solver); - if (solver_control.has_dimension_changed()) init_Sbus(Sbus_, id_me_to_solver, id_solver_to_me, slack_bus_id_solver); - if (solver_control.has_slack_participate_changed() || solver_control.has_pv_changed() || solver_control.has_pq_changed()) fillpv_pq(id_me_to_solver, id_solver_to_me, slack_bus_id_solver, solver_control); + if(is_ac){ + _solver.tell_solver_control(solver_control); + if(solver_control.need_reset_solver()) _solver.reset(); + } else { + _dc_solver.tell_solver_control(solver_control); + if(solver_control.need_reset_solver()){ + std::cout << "_dc_solver.reset();" << std::endl; + _dc_solver.reset(); + } + } + + if (solver_control.need_reset_solver() || + solver_control.has_slack_participate_changed()){ + std::cout << "get_slack_bus_id;" << std::endl; + slack_bus_id_ = generators_.get_slack_bus_id(); + } + if (solver_control.need_reset_solver() || + solver_control.ybus_change_sparsity_pattern() || + solver_control.has_dimension_changed()){ + init_Ybus(Ybus, id_me_to_solver, id_solver_to_me); + std::cout << "init_Ybus;" << std::endl; + } + if (solver_control.need_reset_solver() || + solver_control.ybus_change_sparsity_pattern() || + solver_control.has_dimension_changed() || + solver_control.need_recompute_ybus()){ + fillYbus(Ybus, is_ac, id_me_to_solver); + std::cout << "fillYbus;" << std::endl; + } + if (solver_control.need_reset_solver() || + solver_control.has_dimension_changed()) { + init_Sbus(Sbus_, id_me_to_solver, id_solver_to_me, slack_bus_id_solver); + std::cout << "init_Sbus;" << std::endl; + } + if (solver_control.need_reset_solver() || + solver_control.has_slack_participate_changed() || + solver_control.has_pv_changed() || + solver_control.has_pq_changed()) { + fillpv_pq(id_me_to_solver, id_solver_to_me, slack_bus_id_solver, solver_control); + std::cout << "fillpv_pq;" << std::endl; + } - if (solver_control.has_dimension_changed() || solver_control.need_recompute_sbus() && is_ac){ + if (is_ac && (solver_control.need_reset_solver() || + solver_control.has_dimension_changed() || + solver_control.need_recompute_sbus())){ int nb_bus_total = static_cast(bus_vn_kv_.size()); total_q_min_per_bus_ = RealVect::Constant(nb_bus_total, 0.); total_q_max_per_bus_ = RealVect::Constant(nb_bus_total, 0.); total_gen_per_bus_ = Eigen::VectorXi::Constant(nb_bus_total, 0); generators_.init_q_vector(nb_bus_total, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); dc_lines_.init_q_vector(nb_bus_total, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); - fillSbus_me(Sbus_, is_ac, id_me_to_solver); + std::cout << "total_gen_per_bus_;" << std::endl; } + if (solver_control.need_reset_solver() || + solver_control.has_slack_participate_changed() || + solver_control.has_pq_changed()) { + fillSbus_me(Sbus_, is_ac, id_me_to_solver); + std::cout << "fillSbus_me;" << std::endl; + } + const int nb_bus_solver = static_cast(id_solver_to_me.size()); CplxVect V = CplxVect::Constant(nb_bus_solver, init_vm_pu_); for(int bus_solver_id = 0; bus_solver_id < nb_bus_solver; ++bus_solver_id){ @@ -442,6 +484,7 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, } generators_.set_vm(V, id_me_to_solver); dc_lines_.set_vm(V, id_me_to_solver); + std::cout << nb_bus_solver << std::endl; return V; } @@ -701,15 +744,17 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, CplxVect V = pre_process_solver(Vinit, Ybus_dc_, id_me_to_dc_solver_, id_dc_solver_to_me_, slack_bus_id_dc_solver_, is_ac, solver_control_); - + std::cout << "after pre proces\n"; // start the solver if(solver_control_.has_slack_participate_changed() || solver_control_.has_pv_changed() || solver_control_.has_slack_weight_changed()) slack_weights_ = generators_.get_slack_weights(Ybus_dc_.rows(), id_me_to_dc_solver_); + std::cout << "slack_weights\n"; conv = _dc_solver.compute_pf(Ybus_dc_, V, dcSbus_, slack_bus_id_dc_solver_, slack_weights_, bus_pv_, bus_pq_, max_iter, tol); - + std::cout << "after compute_pf\n"; // store results (fase -> because I am in dc mode) process_results(conv, res, Vinit, false, id_me_to_dc_solver_); + std::cout << "after compute_pf\n"; return res; } diff --git a/src/GridModel.h b/src/GridModel.h index 8295534..0030c4d 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -76,8 +76,11 @@ class GridModel : public DataGeneric solver_control_(), init_vm_pu_(1.04), sn_mva_(1.0){ + _solver.change_solver(SolverType::SparseLU); _dc_solver.change_solver(SolverType::DC); _solver.set_gridmodel(this); + _dc_solver.set_gridmodel(this); + solver_control_.tell_all_changed(); } GridModel(const GridModel & other); GridModel copy() const{ @@ -166,7 +169,6 @@ class GridModel : public DataGeneric const RealVect & trafo_x, const CplxVect & trafo_b, const RealVect & trafo_tap_step_pct, - // const RealVect & trafo_tap_step_degree, const RealVect & trafo_tap_pos, const RealVect & trafo_shift_degree, const std::vector & trafo_tap_hv, // is tap on high voltage (true) or low voltate From cc25e5306546537c1e3f74404507983231efe552 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 13 Dec 2023 11:17:29 +0100 Subject: [PATCH 04/42] compiles and basic checks work --- lightsim2grid/lightSimBackend.py | 5 +- setup.py | 3 +- src/BaseNRSolver.tpp | 48 +++++--- src/BaseNRSolverSingleSlack.tpp | 75 ++++++++---- src/BaseSolver.cpp | 1 + src/DCSolver.tpp | 11 +- src/DataDCLine.h | 2 +- src/DataGen.cpp | 14 ++- src/DataGen.h | 14 ++- src/DataGeneric.cpp | 4 +- src/DataGeneric.h | 2 +- src/DataLine.h | 1 + src/DataShunt.cpp | 9 +- src/DataTrafo.h | 1 + src/GridModel.cpp | 199 ++++++++++++++++++++++--------- src/GridModel.h | 27 +++-- src/Utils.cpp | 49 ++++++++ src/Utils.h | 21 +++- 18 files changed, 354 insertions(+), 132 deletions(-) create mode 100644 src/Utils.cpp diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index a8c66c5..81732db 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -894,9 +894,9 @@ def runpf(self, is_dc=False): self._grid.deactivate_result_computation() # if I init with dc values, it should depends on previous state self.V[:] = self._grid.get_init_vm_pu() # see issue 30 - print("before dc pf") + # print(f"{self.V[:14] = }") Vdc = self._grid.dc_pf(copy.deepcopy(self.V), self.max_it, self.tol) - print("after dc pf") + # print(f"{Vdc[:14] = }") self._grid.reactivate_result_computation() if Vdc.shape[0] == 0: raise BackendError(f"Divergence of DC powerflow (non connected grid) at the initialization of AC powerflow. Detailed error: {self._grid.get_dc_solver().get_error()}") @@ -905,7 +905,6 @@ def runpf(self, is_dc=False): V_init = copy.deepcopy(self.V) tick = time.perf_counter() self._timer_preproc += tick - beg_preproc - print("before ac pf") V = self._grid.ac_pf(V_init, self.max_it, self.tol) self._timer_solver += time.perf_counter() - tick if V.shape[0] == 0: diff --git a/setup.py b/setup.py index 99193f8..9a0f4f0 100644 --- a/setup.py +++ b/setup.py @@ -157,7 +157,8 @@ "src/BaseMultiplePowerflow.cpp", "src/Computers.cpp", "src/SecurityAnalysis.cpp", - "src/Solvers.cpp"] + "src/Solvers.cpp", + "src/Utils.cpp"] if KLU_SOLVER_AVAILABLE: src_files.append("src/KLUSolver.cpp") diff --git a/src/BaseNRSolver.tpp b/src/BaseNRSolver.tpp index 9c25925..b099558 100644 --- a/src/BaseNRSolver.tpp +++ b/src/BaseNRSolver.tpp @@ -13,15 +13,15 @@ template bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix & Ybus, - CplxVect & V, - const CplxVect & Sbus, - const Eigen::VectorXi & slack_ids, - const RealVect & slack_weights, - const Eigen::VectorXi & pv, - const Eigen::VectorXi & pq, - int max_iter, - real_type tol - ) + CplxVect & V, + const CplxVect & Sbus, + const Eigen::VectorXi & slack_ids, + const RealVect & slack_weights, + const Eigen::VectorXi & pv, + const Eigen::VectorXi & pq, + int max_iter, + real_type tol + ) { /** This method uses the newton raphson algorithm to compute voltage angles and magnitudes at each bus @@ -36,19 +36,28 @@ bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix // TODO DEBUG MODE std::ostringstream exc_; exc_ << "BaseNRSolver::compute_pf: Size of the Sbus should be the same as the size of Ybus. Currently: "; - exc_ << "Sbus (" << Sbus.size() << ") and Ybus (" << Ybus.rows() << ", " << Ybus.rows() << ")."; + exc_ << "Sbus (" << Sbus.size() << ") and Ybus (" << Ybus.rows() << ", " << Ybus.cols() << ")."; throw std::runtime_error(exc_.str()); } if(V.size() != Ybus.rows() || V.size() != Ybus.cols() ){ // TODO DEBUG MODE std::ostringstream exc_; exc_ << "BaseNRSolver::compute_pf: Size of V (init voltages) should be the same as the size of Ybus. Currently: "; - exc_ << "V (" << V.size() << ") and Ybus (" << Ybus.rows()<< ", " << Ybus.rows() << ")."; + exc_ << "V (" << V.size() << ") and Ybus (" << Ybus.rows()<< ", " << Ybus.cols() << ")."; throw std::runtime_error(exc_.str()); } reset_timer(); + // std::cout << "dist slack" << std::endl; + + if(_solver_control.need_reset_solver() || + _solver_control.has_dimension_changed()){ + reset(); + } auto timer = CustTimer(); - if(!is_linear_solver_valid()) return false; + if(!is_linear_solver_valid()) { + // err_ = ErrorType::NotInitError; + return false; + } err_ = ErrorType::NoError; // reset the error if previous error happened @@ -85,6 +94,11 @@ bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix bool has_just_been_initialized = false; // to avoid a call to klu_refactor follow a call to klu_factor in the same loop // std::cout << "iter " << nr_iter_ << " dx(0): " << -F(0) << " dx(1): " << -F(1) << std::endl; // std::cout << "slack_absorbed " << slack_absorbed << std::endl; + BaseNRSolver::value_map_.clear(); // TODO smarter solver: only needed if ybus has changed + BaseNRSolver::dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed + BaseNRSolver::dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed + // BaseNRSolver::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed + // BaseNRSolver::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed while ((!converged) & (nr_iter_ < max_iter)){ nr_iter_++; fill_jacobian_matrix(Ybus, V_, slack_bus_id, slack_weights, pq, pvpq, pq_inv, pvpq_inv); @@ -146,6 +160,7 @@ bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix << "\n\t timer_total_nr_: " << timer_total_nr_ << "\n\n"; #endif // __COUT_TIMES + _solver_control.tell_none_changed(); return res; } @@ -165,7 +180,7 @@ void BaseNRSolver::reset(){ template void BaseNRSolver::_dSbus_dV(const Eigen::Ref > & Ybus, - const Eigen::Ref & V){ + const Eigen::Ref & V){ auto timer = CustTimer(); const auto size_dS = V.size(); const CplxVect Vnorm = V.array() / V.array().abs(); @@ -331,6 +346,7 @@ void BaseNRSolver::fill_jacobian_matrix(const Eigen::SparseMatrix< #endif // __COUT_TIMES // first time i initialized the matrix, so i need to compute its sparsity pattern fill_jacobian_matrix_unkown_sparsity_pattern(Ybus, V, slack_bus_id, slack_weights, pq, pvpq, pq_inv, pvpq_inv); + fill_value_map(slack_bus_id, pq, pvpq); #ifdef __COUT_TIMES std::cout << "\t\t fill_jacobian_matrix_unkown_sparsity_pattern : " << timer2.duration() << std::endl; #endif // __COUT_TIMES @@ -339,7 +355,8 @@ void BaseNRSolver::fill_jacobian_matrix(const Eigen::SparseMatrix< // properly and faster (approx 3 times faster than the previous one) #ifdef __COUT_TIMES auto timer3 = CustTimer(); - #endif // __COUT_TIMES + #endif // + if (BaseNRSolver::value_map_.size() == 0) fill_value_map(slack_bus_id,pq, pvpq); fill_jacobian_matrix_kown_sparsity_pattern(slack_bus_id, pq, pvpq ); @@ -404,7 +421,7 @@ void BaseNRSolver::fill_jacobian_matrix_unkown_sparsity_pattern( // optim : if the matrix was already computed, i don't initialize it, i instead reuse as much as i can // i can do that because the matrix will ALWAYS have the same non zero coefficients. // in this if, i allocate it in a "large enough" place to avoid copy when first filling it - if(J_.cols() != size_j) J_ = Eigen::SparseMatrix(size_j,size_j); + if(J_.cols() != size_j) J_ = Eigen::SparseMatrix(size_j, size_j); std::vector > coeffs; // HERE FOR PERF OPTIM (3) coeffs.reserve(2*(dS_dVa_.nonZeros()+dS_dVm_.nonZeros()) + slack_weights.size()); // HERE FOR PERF OPTIM (3) @@ -512,7 +529,6 @@ void BaseNRSolver::fill_jacobian_matrix_unkown_sparsity_pattern( J_.setFromTriplets(coeffs.begin(), coeffs.end()); // HERE FOR PERF OPTIM (3) // std::cout << "end fill jacobian unknown " << std::endl; J_.makeCompressed(); - fill_value_map(slack_bus_id, pq, pvpq); // std::cout << "end fill_value_map" << std::endl; } diff --git a/src/BaseNRSolverSingleSlack.tpp b/src/BaseNRSolverSingleSlack.tpp index df0a6e8..fd9e1ac 100644 --- a/src/BaseNRSolverSingleSlack.tpp +++ b/src/BaseNRSolverSingleSlack.tpp @@ -11,15 +11,15 @@ template bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix & Ybus, - CplxVect & V, - const CplxVect & Sbus, - const Eigen::VectorXi & slack_ids, - const RealVect & slack_weights, // unused here - const Eigen::VectorXi & pv, - const Eigen::VectorXi & pq, - int max_iter, - real_type tol - ) + CplxVect & V, + const CplxVect & Sbus, + const Eigen::VectorXi & slack_ids, + const RealVect & slack_weights, // unused here + const Eigen::VectorXi & pv, + const Eigen::VectorXi & pq, + int max_iter, + real_type tol + ) { /** This method uses the newton raphson algorithm to compute voltage angles and magnitudes at each bus @@ -31,18 +31,26 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix if(Sbus.size() != Ybus.rows() || Sbus.size() != Ybus.cols() ){ std::ostringstream exc_; exc_ << "BaseNRSolverSingleSlack::compute_pf: Size of the Sbus should be the same as the size of Ybus. Currently: "; - exc_ << "Sbus (" << Sbus.size() << ") and Ybus (" << Ybus.rows() << ", " << Ybus.rows() << ")."; + exc_ << "Sbus (" << Sbus.size() << ") and Ybus (" << Ybus.rows() << ", " << Ybus.cols() << ")."; throw std::runtime_error(exc_.str()); } if(V.size() != Ybus.rows() || V.size() != Ybus.cols() ){ std::ostringstream exc_; exc_ << "BaseNRSolverSingleSlack::compute_pf: Size of V (init voltages) should be the same as the size of Ybus. Currently: "; - exc_ << "V (" << V.size() << ") and Ybus (" << Ybus.rows()<<", "<::reset_timer(); - - if(!BaseNRSolver::is_linear_solver_valid()) return false; + // std::cout << "singleslack" << std::endl; + + if(BaseNRSolver::_solver_control.need_reset_solver() || + BaseNRSolver::_solver_control.has_dimension_changed()){ + BaseNRSolver::reset(); + } + + if(!BaseNRSolver::is_linear_solver_valid()){ + return false; + } BaseNRSolver::err_ = ErrorType::NoError; // reset the error if previous error happened auto timer = CustTimer(); @@ -73,14 +81,20 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix bool has_just_been_initialized = false; // to avoid a call to klu_refactor follow a call to klu_factor in the same loop const cplx_type m_i = BaseNRSolver::my_i; // otherwise it does not compile - + BaseNRSolver::value_map_.clear(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed + BaseNRSolver::dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed + BaseNRSolver::dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed + // BaseNRSolver::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed + // BaseNRSolver::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed while ((!converged) & (BaseNRSolver::nr_iter_ < max_iter)){ BaseNRSolver::nr_iter_++; + // std::cout << "\tnr_iter_ " << BaseNRSolver::nr_iter_ << std::endl; fill_jacobian_matrix(Ybus, BaseNRSolver::V_, pq, pvpq, pq_inv, pvpq_inv); if(BaseNRSolver::need_factorize_){ BaseNRSolver::initialize(); if(BaseNRSolver::err_ != ErrorType::NoError){ // I got an error during the initialization of the linear system, i need to stop here + // std::cout << BaseNRSolver::err_ << std::endl; res = false; break; } @@ -95,6 +109,7 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix has_just_been_initialized = false; if(BaseNRSolver::err_ != ErrorType::NoError){ // I got an error during the solving of the linear system, i need to stop here + // std::cout << BaseNRSolver::err_ << std::endl; res = false; break; } @@ -117,7 +132,11 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix F = BaseNRSolver::_evaluate_Fx(Ybus, BaseNRSolver::V_, Sbus, my_pv, pq); bool tmp = F.allFinite(); - if(!tmp) break; // divergence due to Nans + if(!tmp){ + BaseNRSolver::err_ = ErrorType::InifiniteValue; + // std::cout << BaseNRSolver::err_ << std::endl; + break; // divergence due to Nans + } converged = BaseNRSolver::_check_for_convergence(F, tol); } if(!converged){ @@ -135,17 +154,18 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix << "\n\t timer_total_nr_: " << BaseNRSolver::timer_total_nr_ << "\n\n"; #endif // __COUT_TIMES + BaseNRSolver::_solver_control.tell_none_changed(); return res; } template void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::SparseMatrix & Ybus, - const CplxVect & V, - const Eigen::VectorXi & pq, - const Eigen::VectorXi & pvpq, - const std::vector & pq_inv, - const std::vector & pvpq_inv - ) + const CplxVect & V, + const Eigen::VectorXi & pq, + const Eigen::VectorXi & pvpq, + const std::vector & pq_inv, + const std::vector & pvpq_inv + ) { /** J has the shape @@ -165,7 +185,6 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp const int n_pvpq = static_cast(pvpq.size()); const int n_pq = static_cast(pq.size()); const int size_j = n_pvpq + n_pq; - // TODO to gain a bit more time below, try to compute directly, in _dSbus_dV(Ybus, V); // TODO the `dS_dVa_[pvpq, pvpq]` // TODO so that it's easier to retrieve in the next few lines ! @@ -177,6 +196,8 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp #endif // __COUT_TIMES // first time i initialized the matrix, so i need to compute its sparsity pattern fill_jacobian_matrix_unkown_sparsity_pattern(Ybus, V, pq, pvpq, pq_inv, pvpq_inv); + fill_value_map(pq, pvpq); + // std::cout << "\t\tfill_jacobian_matrix_unkown_sparsity_pattern" << std::endl; #ifdef __COUT_TIMES std::cout << "\t\t fill_jacobian_matrix_unkown_sparsity_pattern : " << timer2.duration() << std::endl; #endif // __COUT_TIMES @@ -186,7 +207,12 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp #ifdef __COUT_TIMES auto timer3 = CustTimer(); #endif // __COUT_TIMES + if (BaseNRSolver::value_map_.size() == 0){ + // std::cout << "\t\tfill_value_map called" << std::endl; + fill_value_map(pq, pvpq); + } fill_jacobian_matrix_kown_sparsity_pattern(pq, pvpq); + // std::cout << "\t\tfill_jacobian_matrix_kown_sparsity_pattern" << std::endl; #ifdef __COUT_TIMES std::cout << "\t\t fill_jacobian_matrix_kown_sparsity_pattern : " << timer3.duration() << std::endl; #endif // __COUT_TIMES @@ -323,7 +349,6 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity } // J_.setFromTriplets(coeffs.begin(), coeffs.end()); // HERE FOR PERF OPTIM (3) BaseNRSolver::J_.makeCompressed(); - fill_value_map(pq, pvpq); } /** @@ -340,9 +365,9 @@ void BaseNRSolverSingleSlack::fill_value_map( const int n_pvpq = static_cast(pvpq.size()); BaseNRSolver::value_map_ = std::vector (BaseNRSolver::J_.nonZeros()); - const int n_row = static_cast(BaseNRSolver::J_.cols()); + const int n_col = static_cast(BaseNRSolver::J_.cols()); unsigned int pos_el = 0; - for (int col_=0; col_ < n_row; ++col_){ + for (int col_=0; col_ < n_col; ++col_){ for (Eigen::SparseMatrix::InnerIterator it(BaseNRSolver::J_, col_); it; ++it) { const int row_id = static_cast(it.row()); diff --git a/src/BaseSolver.cpp b/src/BaseSolver.cpp index 61b9d49..624b523 100644 --- a/src/BaseSolver.cpp +++ b/src/BaseSolver.cpp @@ -122,6 +122,7 @@ bool BaseSolver::_check_for_convergence(const RealVect & F, { auto timer = CustTimer(); const auto norm_inf = F.lpNorm(); + // std::cout << "\t\tnorm_inf: " << norm_inf << std::endl; bool res = norm_inf < tol; timer_check_ += timer.duration(); return res; diff --git a/src/DCSolver.tpp b/src/DCSolver.tpp index fee67b2..87c2fb3 100644 --- a/src/DCSolver.tpp +++ b/src/DCSolver.tpp @@ -26,6 +26,15 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix // V is used the following way: at pq buses it's completely ignored. For pv bus only the magnitude is used, // and for the slack bus both the magnitude and the angle are used. + if(!is_linear_solver_valid()) { + // err_ = ErrorType::NotInitError; + return false; + } + if(_solver_control.need_reset_solver() || + _solver_control.has_dimension_changed()){ + reset(); + } + auto timer = CustTimer(); BaseSolver::reset_timer(); sizeYbus_with_slack_ = static_cast(Ybus.rows()); @@ -135,7 +144,7 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix #ifdef __COUT_TIMES std::cout << "\t dc postproc: " << 1000. * timer_postproc.duration() << "ms" << std::endl; #endif // __COUT_TIMES - + _solver_control.tell_none_changed(); timer_total_nr_ += timer.duration(); return true; } diff --git a/src/DataDCLine.h b/src/DataDCLine.h index 8f2576a..b9363d2 100644 --- a/src/DataDCLine.h +++ b/src/DataDCLine.h @@ -224,7 +224,7 @@ class DataDCLine : public DataGeneric virtual void fillpv(std::vector& bus_pv, std::vector & has_bus_been_added, - Eigen::VectorXi & slack_bus_id_solver, + const Eigen::VectorXi & slack_bus_id_solver, const std::vector & id_grid_to_solver) const { from_gen_.fillpv(bus_pv, has_bus_been_added, slack_bus_id_solver, id_grid_to_solver); to_gen_.fillpv(bus_pv, has_bus_been_added, slack_bus_id_solver, id_grid_to_solver); diff --git a/src/DataGen.cpp b/src/DataGen.cpp index bc60fd2..c504fc4 100644 --- a/src/DataGen.cpp +++ b/src/DataGen.cpp @@ -168,7 +168,7 @@ void DataGen::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solv void DataGen::fillpv(std::vector & bus_pv, std::vector & has_bus_been_added, - Eigen::VectorXi & slack_bus_id_solver, + const Eigen::VectorXi & slack_bus_id_solver, const std::vector & id_grid_to_solver) const { const int nb_gen = nb(); @@ -216,7 +216,7 @@ void DataGen::reset_results(){ res_q_ = RealVect(); // in MVar res_v_ = RealVect(); // in kV res_theta_ = RealVect(); // in deg - bus_slack_weight_ = RealVect(); + // bus_slack_weight_ = RealVect(); } void DataGen::get_vm_for_dc(RealVect & Vm){ @@ -331,16 +331,18 @@ void DataGen::set_vm(CplxVect & V, const std::vector & id_grid_to_solver) c } } -std::vector DataGen::get_slack_bus_id() const{ - std::vector res; +Eigen::VectorXi DataGen::get_slack_bus_id() const{ + std::vector tmp; + Eigen::VectorXi res; const auto nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id){ if(gen_slackbus_[gen_id]){ const auto my_bus = bus_id_(gen_id); // do not add twice the same "slack bus" - if(!is_in_vect(my_bus, res)) res.push_back(my_bus); + if(!is_in_vect(my_bus, tmp)) tmp.push_back(my_bus); } } + res = Eigen::VectorXi::Map(&tmp[0], tmp.size()); // force the copy of the data apparently return res; } @@ -349,7 +351,7 @@ void DataGen::set_p_slack(const RealVect& node_mismatch, { if(bus_slack_weight_.size() == 0){ // TODO DEBUG MODE: perform this check only in debug mode - throw std::runtime_error("Impossible to set the active value of generators for the slack bus"); + throw std::runtime_error("DataGen::set_p_slack: Impossible to set the active value of generators for the slack bus: no known slack (you should haved called DataGen::get_slack_weights first)"); } const auto nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id){ diff --git a/src/DataGen.h b/src/DataGen.h index e2fb9cb..c143017 100644 --- a/src/DataGen.h +++ b/src/DataGen.h @@ -221,7 +221,7 @@ class DataGen: public DataGeneric **/ RealVect get_slack_weights(Eigen::Index nb_bus_solver, const std::vector & id_grid_to_solver); - std::vector get_slack_bus_id() const; + Eigen::VectorXi get_slack_bus_id() const; void set_p_slack(const RealVect& node_mismatch, const std::vector & id_grid_to_solver); // modification @@ -236,7 +236,7 @@ class DataGen: public DataGeneric solver_control.tell_recompute_sbus(); if(voltage_regulator_on_[gen_id]) solver_control.tell_v_changed(); if(!turnedoff_gen_pv_) solver_control.tell_pv_changed(); - if(gen_slack_weight_[gen_id]){ + if(gen_slack_weight_[gen_id] != 0. || gen_slackbus_[gen_id]){ solver_control.tell_slack_participate_changed(); solver_control.tell_slack_weight_changed(); } @@ -248,14 +248,18 @@ class DataGen: public DataGeneric solver_control.tell_recompute_sbus(); if(voltage_regulator_on_[gen_id]) solver_control.tell_v_changed(); if(!turnedoff_gen_pv_) solver_control.tell_pv_changed(); - if(gen_slack_weight_[gen_id]){ + if(gen_slack_weight_[gen_id] != 0. || gen_slackbus_[gen_id]){ solver_control.tell_slack_participate_changed(); solver_control.tell_slack_weight_changed(); } } _reactivate(gen_id, status_); } - void change_bus(int gen_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(gen_id, new_bus_id, bus_id_, solver_control, nb_bus);} + void change_bus(int gen_id, int new_bus_id, SolverControl & solver_control, int nb_bus) { + if (new_bus_id != bus_id_[gen_id]){ + if (gen_slack_weight_[gen_id] != 0. || gen_slackbus_[gen_id]) solver_control.has_slack_participate_changed(); + } + _change_bus(gen_id, new_bus_id, bus_id_, solver_control, nb_bus);} int get_bus(int gen_id) {return _get_bus(gen_id, status_, bus_id_);} virtual void reconnect_connected_buses(std::vector & bus_status) const; virtual void disconnect_if_not_in_main_component(std::vector & busbar_in_main_component); @@ -269,7 +273,7 @@ class DataGen: public DataGeneric virtual void fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const; virtual void fillpv(std::vector& bus_pv, std::vector & has_bus_been_added, - Eigen::VectorXi & slack_bus_id_solver, + const Eigen::VectorXi & slack_bus_id_solver, const std::vector & id_grid_to_solver) const; void init_q_vector(int nb_bus, Eigen::VectorXi & total_gen_per_bus, diff --git a/src/DataGeneric.cpp b/src/DataGeneric.cpp index 08d30ed..3b7c330 100644 --- a/src/DataGeneric.cpp +++ b/src/DataGeneric.cpp @@ -86,8 +86,8 @@ void DataGeneric::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el // TODO speed: sparsity pattern might not change if something is already there solver_control.tell_ybus_change_sparsity_pattern(); - solver_control.tell_recompute_sbus(); - solver_control.tell_recompute_ybus(); + solver_control.tell_recompute_sbus(); // if a bus changed for load / generator + solver_control.tell_recompute_ybus(); // if a bus changed for shunts / line / trafo } bus_me_id = new_bus_me_id; } diff --git a/src/DataGeneric.h b/src/DataGeneric.h index d844f4a..768cc7d 100644 --- a/src/DataGeneric.h +++ b/src/DataGeneric.h @@ -88,7 +88,7 @@ class DataGeneric : public BaseConstants virtual void fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const {}; virtual void fillpv(std::vector& bus_pv, std::vector & has_bus_been_added, - Eigen::VectorXi & slack_bus_id_solver, + const Eigen::VectorXi & slack_bus_id_solver, const std::vector & id_grid_to_solver) const {}; virtual void get_q(std::vector& q_by_bus) {}; diff --git a/src/DataLine.h b/src/DataLine.h index 2026153..10b4be0 100644 --- a/src/DataLine.h +++ b/src/DataLine.h @@ -192,6 +192,7 @@ class DataLine : public DataGeneric if(status_[powerline_id]){ solver_control.tell_recompute_ybus(); // but sparsity pattern do not change here (possibly one more coeff at 0.) + solver_control.tell_ybus_some_coeffs_zero(); } _deactivate(powerline_id, status_); } diff --git a/src/DataShunt.cpp b/src/DataShunt.cpp index 6813a7a..ba52bbd 100644 --- a/src/DataShunt.cpp +++ b/src/DataShunt.cpp @@ -170,7 +170,10 @@ void DataShunt::change_p(int shunt_id, real_type new_p, SolverControl & solver_c { bool my_status = status_.at(shunt_id); // and this check that load_id is not out of bound if(!my_status) throw std::runtime_error("Impossible to change the active value of a disconnected shunt"); - if(p_mw_(shunt_id) != new_p) solver_control.tell_recompute_sbus(); + if(p_mw_(shunt_id) != new_p){ + solver_control.tell_recompute_ybus(); + solver_control.tell_recompute_sbus(); // in dc mode sbus is modified + } p_mw_(shunt_id) = new_p; } @@ -179,7 +182,9 @@ void DataShunt::change_q(int shunt_id, real_type new_q, SolverControl & solver_c { bool my_status = status_.at(shunt_id); // and this check that load_id is not out of bound if(!my_status) throw std::runtime_error("Impossible to change the reactive value of a disconnected shunt"); - if(q_mvar_(shunt_id) != new_q) solver_control.tell_recompute_sbus(); + if(q_mvar_(shunt_id) != new_q){ + solver_control.tell_recompute_ybus(); + } q_mvar_(shunt_id) = new_q; } diff --git a/src/DataTrafo.h b/src/DataTrafo.h index 90e05e9..033a58a 100644 --- a/src/DataTrafo.h +++ b/src/DataTrafo.h @@ -178,6 +178,7 @@ class DataTrafo : public DataGeneric if(status_[trafo_id]){ solver_control.tell_recompute_ybus(); // but sparsity pattern do not change here (possibly one more coeff at 0.) + solver_control.tell_ybus_some_coeffs_zero(); } _deactivate(trafo_id, status_); } diff --git a/src/GridModel.cpp b/src/GridModel.cpp index f4768f2..7bdbea3 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -260,7 +260,7 @@ void GridModel::reset(bool reset_solver, bool reset_ac, bool reset_dc) Ybus_dc_ = Eigen::SparseMatrix(); } - Sbus_ = CplxVect(); + acSbus_ = CplxVect(); dcSbus_ = CplxVect(); bus_pv_ = Eigen::VectorXi(); bus_pq_ = Eigen::VectorXi(); @@ -291,9 +291,14 @@ CplxVect GridModel::ac_pf(const CplxVect & Vinit, bool conv = false; CplxVect res = CplxVect(); + reset_results(); // reset the results + // pre process the data to define a proper jacobian matrix, the proper voltage vector etc. bool is_ac = true; - CplxVect V = pre_process_solver(Vinit, Ybus_ac_, + // std::cout << "before pre process" << std::endl; + CplxVect V = pre_process_solver(Vinit, + acSbus_, + Ybus_ac_, id_me_to_ac_solver_, id_ac_solver_to_me_, slack_bus_id_ac_solver_, @@ -301,10 +306,20 @@ CplxVect GridModel::ac_pf(const CplxVect & Vinit, solver_control_); // start the solver - slack_weights_ = generators_.get_slack_weights(Ybus_ac_.rows(), id_me_to_ac_solver_); - conv = _solver.compute_pf(Ybus_ac_, V, Sbus_, slack_bus_id_ac_solver_, slack_weights_, bus_pv_, bus_pq_, max_iter, tol / sn_mva_); + // std::cout << "before get_slack_weights" << std::endl; + if(solver_control_.need_reset_solver() || + solver_control_.has_dimension_changed() || + solver_control_.has_slack_participate_changed() || + solver_control_.has_pv_changed() || + solver_control_.has_slack_weight_changed()){ + // std::cout << "get_slack_weights" << std::endl; + slack_weights_ = generators_.get_slack_weights(Ybus_ac_.rows(), id_me_to_ac_solver_); + } + // std::cout << "before compute_pf" << std::endl; + conv = _solver.compute_pf(Ybus_ac_, V, acSbus_, slack_bus_id_ac_solver_, slack_weights_, bus_pv_, bus_pq_, max_iter, tol / sn_mva_); - // store results (in ac mode) + // store results (in ac mode) + // std::cout << "before process_results" << std::endl; process_results(conv, res, Vinit, true, id_me_to_ac_solver_); // return the vector of complex voltage at each bus @@ -377,14 +392,18 @@ CplxVect GridModel::check_solution(const CplxVect & V_proposed, bool check_q_lim bool is_ac = true; SolverControl reset_solver; reset_solver.tell_none_changed(); // TODO reset solver - CplxVect V = pre_process_solver(V_proposed, Ybus_ac_, - id_me_to_ac_solver_, id_ac_solver_to_me_, slack_bus_id_ac_solver_, + CplxVect V = pre_process_solver(V_proposed, + acSbus_, + Ybus_ac_, + id_me_to_ac_solver_, + id_ac_solver_to_me_, + slack_bus_id_ac_solver_, is_ac, reset_solver); // compute the mismatch CplxVect tmp = Ybus_ac_ * V; // this is a vector tmp = tmp.array().conjugate(); // i take the conjugate - auto mis = V.array() * tmp.array() - Sbus_.array(); + auto mis = V.array() * tmp.array() - acSbus_.array(); // TODO ac or dc here // store results CplxVect res = _get_results_back_to_orig_nodes(mis, @@ -406,6 +425,7 @@ CplxVect GridModel::check_solution(const CplxVect & V_proposed, bool check_q_lim }; CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, + CplxVect & Sbus, Eigen::SparseMatrix & Ybus, std::vector & id_me_to_solver, std::vector & id_solver_to_me, @@ -416,44 +436,56 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, // TODO get rid of the "is_ac" argument: this info is available in the _solver already if(is_ac){ _solver.tell_solver_control(solver_control); - if(solver_control.need_reset_solver()) _solver.reset(); + if(solver_control.need_reset_solver()){ + // std::cout << "_ac_solver.reset();" << std::endl; + _solver.reset(); + } } else { _dc_solver.tell_solver_control(solver_control); if(solver_control.need_reset_solver()){ - std::cout << "_dc_solver.reset();" << std::endl; + // std::cout << "_dc_solver.reset();" << std::endl; _dc_solver.reset(); } } if (solver_control.need_reset_solver() || + solver_control.has_dimension_changed() || solver_control.has_slack_participate_changed()){ - std::cout << "get_slack_bus_id;" << std::endl; - slack_bus_id_ = generators_.get_slack_bus_id(); + slack_bus_id_solver = generators_.get_slack_bus_id(); + // this is the slack bus ids with the gridmodel ordering, not the solver ordering. + // conversion to solver ordering is done in init_slack_bus + + // std::cout << "slack_bus_id_solver 1: "; + // for(auto el: slack_bus_id_solver) std::cout << el << ", "; + // std::cout << std::endl; } if (solver_control.need_reset_solver() || solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed()){ init_Ybus(Ybus, id_me_to_solver, id_solver_to_me); - std::cout << "init_Ybus;" << std::endl; + // std::cout << "init_Ybus;" << std::endl; } if (solver_control.need_reset_solver() || solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed() || solver_control.need_recompute_ybus()){ fillYbus(Ybus, is_ac, id_me_to_solver); - std::cout << "fillYbus;" << std::endl; + // std::cout << "fillYbus;" << std::endl; } if (solver_control.need_reset_solver() || solver_control.has_dimension_changed()) { - init_Sbus(Sbus_, id_me_to_solver, id_solver_to_me, slack_bus_id_solver); - std::cout << "init_Sbus;" << std::endl; + // init Sbus + Sbus = CplxVect::Constant(id_solver_to_me.size(), 0.); + // std::cout << "init_Sbus;" << std::endl; } if (solver_control.need_reset_solver() || + solver_control.has_dimension_changed() || solver_control.has_slack_participate_changed() || solver_control.has_pv_changed() || solver_control.has_pq_changed()) { + init_slack_bus(Sbus, id_me_to_solver, id_solver_to_me, slack_bus_id_solver); fillpv_pq(id_me_to_solver, id_solver_to_me, slack_bus_id_solver, solver_control); - std::cout << "fillpv_pq;" << std::endl; + // std::cout << "fillpv_pq;" << std::endl; } if (is_ac && (solver_control.need_reset_solver() || @@ -465,14 +497,15 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, total_gen_per_bus_ = Eigen::VectorXi::Constant(nb_bus_total, 0); generators_.init_q_vector(nb_bus_total, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); dc_lines_.init_q_vector(nb_bus_total, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); - std::cout << "total_gen_per_bus_;" << std::endl; + // std::cout << "total_gen_per_bus_;" << std::endl; } if (solver_control.need_reset_solver() || + solver_control.has_dimension_changed() || solver_control.has_slack_participate_changed() || solver_control.has_pq_changed()) { - fillSbus_me(Sbus_, is_ac, id_me_to_solver); - std::cout << "fillSbus_me;" << std::endl; + fillSbus_me(Sbus, is_ac, id_me_to_solver); + // std::cout << "fillSbus_me;" << std::endl; } const int nb_bus_solver = static_cast(id_solver_to_me.size()); @@ -484,7 +517,10 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, } generators_.set_vm(V, id_me_to_solver); dc_lines_.set_vm(V, id_me_to_solver); - std::cout << nb_bus_solver << std::endl; + // std::cout << "pre_process_solver: V result: "< & Ybus, id_me_to_solver = std::vector(nb_bus_init, _deactivated_bus_id); // by default, if a bus is disconnected, then it has a -1 there id_solver_to_me = std::vector(); id_solver_to_me.reserve(nb_bus_init); - int bus_id_solver=0; + int bus_id_solver = 0; for(int bus_id_me=0; bus_id_me < nb_bus_init; ++bus_id_me){ if(bus_status_[bus_id_me]){ // bus is connected @@ -552,31 +589,56 @@ void GridModel::init_Ybus(Eigen::SparseMatrix & Ybus, ++bus_id_solver; } } - const int nb_bus = static_cast(id_solver_to_me.size()); + const int nb_bus_solver = static_cast(id_solver_to_me.size()); - Ybus = Eigen::SparseMatrix(nb_bus, nb_bus); - Ybus.reserve(nb_bus + 2*powerlines_.nb() + 2*trafos_.nb()); + Ybus = Eigen::SparseMatrix(nb_bus_solver, nb_bus_solver); + Ybus.reserve(nb_bus_solver + 2*powerlines_.nb() + 2*trafos_.nb()); } -void GridModel::init_Sbus(CplxVect & Sbus, - std::vector& id_me_to_solver, - std::vector& id_solver_to_me, - Eigen::VectorXi & slack_bus_id_solver){ +void GridModel::init_slack_bus(const CplxVect & Sbus, + const std::vector& id_me_to_solver, + const std::vector& id_solver_to_me, + Eigen::VectorXi & slack_bus_id_solver){ - const int nb_bus = static_cast(id_solver_to_me.size()); - Sbus = CplxVect::Constant(nb_bus, 0.); - slack_bus_id_solver = Eigen::VectorXi::Zero(slack_bus_id_.size()); + const int nb_bus = static_cast(id_solver_to_me.size()); + // slack_bus_id_solver = Eigen::VectorXi::Zero(slack_bus_id_.size()); + // slack_bus_id_solver = Eigen::VectorXi::Constant(slack_bus_id_solver.size(), _deactivated_bus_id); size_t i = 0; - for(auto el: slack_bus_id_) { - slack_bus_id_solver(i) = id_me_to_solver[el]; + // std::cout << "slack_bus_id_solver 2: "; + // for(auto el: slack_bus_id_solver) std::cout << el << ", "; + // std::cout << std::endl; + // std::cout << "id_me_to_solver: "; + // for(auto el: id_me_to_solver) std::cout << el << ", "; + // std::cout << std::endl; + // std::cout << "id_solver_to_me: "; + // for(auto el: id_solver_to_me) std::cout << el << ", "; + // std::cout << std::endl; + + // for(auto el: slack_bus_id_) { + for(auto el: slack_bus_id_solver) { + auto tmp = id_me_to_solver[el]; + if(tmp == _deactivated_bus_id){ + std::ostringstream exc_; + exc_ << "GridModel::init_Sbus: One of the slack bus is disconnected."; + exc_ << " You can check element "; + exc_ << el; + exc_ << ": ["; + for(auto el2 : slack_bus_id_solver) exc_ << el2 << ", "; + exc_ << "]."; + throw std::out_of_range(exc_.str()); + } + slack_bus_id_solver(i) = tmp; ++i; } + // std::cout << "slack_bus_id_solver 3: "; + // for(auto el: slack_bus_id_solver) std::cout << el << ", "; + // std::cout << std::endl; if(is_in_vect(_deactivated_bus_id, slack_bus_id_solver)){ // TODO improve error message with the gen_id // TODO DEBUG MODE: only check that in debug mode - throw std::runtime_error("One of the slack bus is disconnected !"); + throw std::runtime_error("GridModel::init_Sbus: One of the slack bus is disconnected !"); } } void GridModel::fillYbus(Eigen::SparseMatrix & res, bool ac, const std::vector& id_me_to_solver){ @@ -586,6 +648,7 @@ void GridModel::fillYbus(Eigen::SparseMatrix & res, bool ac, const st **/ // init the Ybus matrix + res.setZero(); // it should not be needed but might not hurt too much either. std::vector > tripletList; tripletList.reserve(bus_vn_kv_.size() + 4*powerlines_.nb() + 4*trafos_.nb() + shunts_.nb()); powerlines_.fillYbus(tripletList, ac, id_me_to_solver, sn_mva_); // TODO have a function to dispatch that to all type of elements @@ -596,13 +659,14 @@ void GridModel::fillYbus(Eigen::SparseMatrix & res, bool ac, const st storages_.fillYbus(tripletList, ac, id_me_to_solver, sn_mva_); generators_.fillYbus(tripletList, ac, id_me_to_solver, sn_mva_); dc_lines_.fillYbus(tripletList, ac, id_me_to_solver, sn_mva_); - res.setFromTriplets(tripletList.begin(), tripletList.end()); + res.setFromTriplets(tripletList.begin(), tripletList.end()); // works because "The initial contents of *this is destroyed" res.makeCompressed(); } void GridModel::fillSbus_me(CplxVect & Sbus, bool ac, const std::vector& id_me_to_solver) { - // init the Sbus vector + // init the Sbus + Sbus.array() = 0.; // reset to 0. powerlines_.fillSbus(Sbus, id_me_to_solver, ac); // TODO have a function to dispatch that to all type of elements trafos_.fillSbus(Sbus, id_me_to_solver, ac); shunts_.fillSbus(Sbus, id_me_to_solver, ac); @@ -617,8 +681,8 @@ void GridModel::fillSbus_me(CplxVect & Sbus, bool ac, const std::vector& id } void GridModel::fillpv_pq(const std::vector& id_me_to_solver, - std::vector& id_solver_to_me, - Eigen::VectorXi & slack_bus_id_solver, + const std::vector& id_solver_to_me, + const Eigen::VectorXi & slack_bus_id_solver, const SolverControl & solver_control) { // Nothing to do if neither pv, nor pq nor the dimension of the problem has changed @@ -633,8 +697,6 @@ void GridModel::fillpv_pq(const std::vector& id_me_to_solver, bus_pv.reserve(nb_bus); std::vector has_bus_been_added(nb_bus, false); - // std::cout << "id_me_to_solver.size(): " << id_me_to_solver.size() << std::endl; - bus_pv_ = Eigen::VectorXi(); bus_pq_ = Eigen::VectorXi(); powerlines_.fillpv(bus_pv, has_bus_been_added, slack_bus_id_solver, id_me_to_solver); // TODO have a function to dispatch that to all type of elements @@ -652,8 +714,8 @@ void GridModel::fillpv_pq(const std::vector& id_me_to_solver, bus_pq.push_back(bus_id); has_bus_been_added[bus_id] = true; // don't add it a second time } - bus_pv_ = Eigen::Map(bus_pv.data(), bus_pv.size()); - bus_pq_ = Eigen::Map(bus_pq.data(), bus_pq.size()); + bus_pv_ = Eigen::VectorXi::Map(&bus_pv[0], bus_pv.size()); + bus_pq_ = Eigen::VectorXi::Map(&bus_pq[0], bus_pq.size()); } void GridModel::compute_results(bool ac){ // retrieve results from powerflow @@ -681,12 +743,12 @@ void GridModel::compute_results(bool ac){ //handle_slack_bus active power CplxVect mismatch; // power mismatch at each bus (SOLVER BUS !!!) - RealVect ractive_mismatch; // not used in dc mode (DO NOT ATTEMPT TO USE IT THERE) + RealVect reactive_mismatch; // not used in dc mode (DO NOT ATTEMPT TO USE IT THERE) RealVect active_mismatch; if(ac){ // In AC mode i am not forced to run through all the grid auto tmp = (Ybus_ac_ * V).conjugate(); - mismatch = V.array() * tmp.array() - Sbus_.array(); + mismatch = V.array() * tmp.array() - acSbus_.array(); active_mismatch = mismatch.real() * sn_mva_; } else{ active_mismatch = RealVect::Zero(V.size()); @@ -697,13 +759,15 @@ void GridModel::compute_results(bool ac){ const auto id_slack = slack_bus_id_dc_solver_(0); active_mismatch(id_slack) = -dcSbus_.real().sum() * sn_mva_; } + // for(auto el: active_mismatch) std::cout << el << ", "; + // std::cout << std::endl; generators_.set_p_slack(active_mismatch, id_me_to_solver); - if(ac) ractive_mismatch = mismatch.imag() * sn_mva_; + if(ac) reactive_mismatch = mismatch.imag() * sn_mva_; // mainly to initialize the Q value of the generators in dc (just fill it with 0.) - generators_.set_q(ractive_mismatch, id_me_to_solver, ac, + generators_.set_q(reactive_mismatch, id_me_to_solver, ac, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); - dc_lines_.set_q(ractive_mismatch, id_me_to_solver, ac, + dc_lines_.set_q(reactive_mismatch, id_me_to_solver, ac, total_gen_per_bus_, total_q_min_per_bus_, total_q_max_per_bus_); } @@ -739,22 +803,39 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, bool conv = false; CplxVect res = CplxVect(); + reset_results(); // reset the results + // pre process the data to define a proper jacobian matrix, the proper voltage vector etc. bool is_ac = false; - CplxVect V = pre_process_solver(Vinit, Ybus_dc_, - id_me_to_dc_solver_, id_dc_solver_to_me_, slack_bus_id_dc_solver_, + CplxVect V = pre_process_solver(Vinit, + dcSbus_, + Ybus_dc_, + id_me_to_dc_solver_, + id_dc_solver_to_me_, + slack_bus_id_dc_solver_, is_ac, solver_control_); - std::cout << "after pre proces\n"; + // std::cout << "after pre proces\n"; // start the solver - if(solver_control_.has_slack_participate_changed() || + if(solver_control_.need_reset_solver() || + solver_control_.has_dimension_changed() || + solver_control_.has_slack_participate_changed() || solver_control_.has_pv_changed() || - solver_control_.has_slack_weight_changed()) slack_weights_ = generators_.get_slack_weights(Ybus_dc_.rows(), id_me_to_dc_solver_); - std::cout << "slack_weights\n"; + solver_control_.has_slack_weight_changed()){ + // TODO smarter solver: this is done both in ac and in dc ! + // std::cout << "get_slack_weights" << std::endl; + slack_weights_ = generators_.get_slack_weights(Ybus_dc_.rows(), id_me_to_dc_solver_); + } + // std::cout << "V (init to dc pf)\n"; + // for(auto el: V) std::cout << el << ", "; + // std::cout << std::endl; + // std::cout << "dcSbus (init to dc pf)\n"; + // for(auto el: dcSbus_) std::cout << el << ", "; + // std::cout << std::endl; conv = _dc_solver.compute_pf(Ybus_dc_, V, dcSbus_, slack_bus_id_dc_solver_, slack_weights_, bus_pv_, bus_pq_, max_iter, tol); - std::cout << "after compute_pf\n"; + // std::cout << "after compute_pf\n"; // store results (fase -> because I am in dc mode) process_results(conv, res, Vinit, false, id_me_to_dc_solver_); - std::cout << "after compute_pf\n"; + // std::cout << "after compute_pf\n"; return res; } @@ -1047,7 +1128,9 @@ std::tuple GridModel::assign_slack_to_most_connected(){ generators_.remove_all_slackbus(); res_gen_id = generators_.assign_slack_bus(res_bus_id, gen_p_per_bus, solver_control_); std::get<1>(res) = res_gen_id; - slack_bus_id_ = std::vector(); + // slack_bus_id_ = std::vector(); + slack_bus_id_ac_solver_ = Eigen::VectorXi(); + slack_bus_id_dc_solver_ = Eigen::VectorXi(); slack_weights_ = RealVect(); return res; } diff --git a/src/GridModel.h b/src/GridModel.h index 0030c4d..2bf85b7 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -255,7 +255,7 @@ class GridModel : public DataGeneric unsigned int size_th = 6; if (my_state.size() != size_th) { - std::cout << "LightSim::GridModel state size " << my_state.size() << " instead of "<< size_th << std::endl; + // std::cout << "LightSim::GridModel state size " << my_state.size() << " instead of "<< size_th << std::endl; // TODO more explicit error message throw std::runtime_error("Invalid state when loading LightSim::GridModel"); } @@ -471,7 +471,7 @@ class GridModel : public DataGeneric return Ybus_dc_; // This is copied to python } Eigen::Ref get_Sbus() const{ - return Sbus_; + return acSbus_; } Eigen::Ref get_dcSbus() const{ return dcSbus_; @@ -629,23 +629,30 @@ class GridModel : public DataGeneric if you will perform a powerflow after it or not. (usually put ``true`` here). **/ CplxVect pre_process_solver(const CplxVect & Vinit, + CplxVect & Sbus, Eigen::SparseMatrix & Ybus, std::vector & id_me_to_solver, std::vector & id_solver_to_me, Eigen::VectorXi & slack_bus_id_solver, bool is_ac, const SolverControl & solver_control); + + // init the Ybus matrix (its size, it is filled up elsewhere) and also the + // converter from "my bus id" to the "solver bus id" (id_me_to_solver and id_solver_to_me) void init_Ybus(Eigen::SparseMatrix & Ybus, std::vector & id_me_to_solver, std::vector& id_solver_to_me); - void init_Sbus(CplxVect & Sbus, - std::vector & id_me_to_solver, - std::vector& id_solver_to_me, - Eigen::VectorXi & slack_bus_id_solver); + + // converts the slack_bus_id from gridmodel ordering into solver ordering + void init_slack_bus(const CplxVect & Sbus, + const std::vector & id_me_to_solver, + const std::vector& id_solver_to_me, + Eigen::VectorXi & slack_bus_id_solver); void fillYbus(Eigen::SparseMatrix & res, bool ac, const std::vector& id_me_to_solver); void fillSbus_me(CplxVect & res, bool ac, const std::vector& id_me_to_solver); - void fillpv_pq(const std::vector& id_me_to_solver, std::vector& id_solver_to_me, - Eigen::VectorXi & slack_bus_id_solver, + void fillpv_pq(const std::vector& id_me_to_solver, + const std::vector& id_solver_to_me, + const Eigen::VectorXi & slack_bus_id_solver, const SolverControl & solver_control); // results @@ -794,7 +801,7 @@ class GridModel : public DataGeneric DataDCLine dc_lines_; // 8. slack bus - std::vector slack_bus_id_; + // std::vector slack_bus_id_; Eigen::VectorXi slack_bus_id_ac_solver_; Eigen::VectorXi slack_bus_id_dc_solver_; RealVect slack_weights_; @@ -802,7 +809,7 @@ class GridModel : public DataGeneric // as matrix, for the solver Eigen::SparseMatrix Ybus_ac_; Eigen::SparseMatrix Ybus_dc_; - CplxVect Sbus_; + CplxVect acSbus_; CplxVect dcSbus_; Eigen::VectorXi bus_pv_; // id are the solver internal id and NOT the initial id Eigen::VectorXi bus_pq_; // id are the solver internal id and NOT the initial id diff --git a/src/Utils.cpp b/src/Utils.cpp new file mode 100644 index 0000000..835bbb8 --- /dev/null +++ b/src/Utils.cpp @@ -0,0 +1,49 @@ +// Copyright (c) 2020, RTE (https://www.rte-france.com) +// See AUTHORS.txt +// This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +// If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +// you can obtain one at http://mozilla.org/MPL/2.0/. +// SPDX-License-Identifier: MPL-2.0 +// This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. + +#include "Utils.h" + +std::ostream& operator<<(std::ostream& out, const ErrorType & error_type){ + switch (error_type) + { + case ErrorType::NoError: + out << "NoError"; + break; + case ErrorType::SingularMatrix: + out << "SingularMatrix"; + break; + case ErrorType::TooManyIterations: + out << "TooManyIterations"; + break; + case ErrorType::InifiniteValue: + out << "InifiniteValue"; + break; + case ErrorType::SolverAnalyze: + out << "SolverAnalyze"; + break; + case ErrorType::SolverFactor: + out << "SolverFactor"; + break; + case ErrorType::SolverReFactor: + out << "SolverReFactor"; + break; + case ErrorType::SolverSolve: + out << "SolverSolve"; + break; + case ErrorType::NotInitError: + out << "NotInitError"; + break; + case ErrorType::LicenseError: + out << "LicenseError"; + break; + default: + out << "unknown error (check utils.cpp)"; + break; + } + return out; +} diff --git a/src/Utils.h b/src/Utils.h index 0d56bc0..406c125 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -37,7 +37,17 @@ typedef Eigen::Matrix RealMat; typedef Eigen::Matrix CplxMat; // type of error in the different solvers -enum class ErrorType {NoError, SingularMatrix, TooManyIterations, InifiniteValue, SolverAnalyze, SolverFactor, SolverReFactor, SolverSolve, NotInitError, LicenseError}; +enum class ErrorType {NoError, + SingularMatrix, + TooManyIterations, + InifiniteValue, + SolverAnalyze, + SolverFactor, + SolverReFactor, + SolverSolve, + NotInitError, + LicenseError}; +std::ostream& operator<<(std::ostream& out, const ErrorType & error_type); // define some constant for compilation outside of "setup.py" #ifndef VERSION_MAJOR @@ -65,6 +75,7 @@ class SolverControl need_recompute_ybus_(true), v_changed_(true), slack_weight_changed_(true), + ybus_some_coeffs_zero_(true), ybus_change_sparsity_pattern_(true) {}; @@ -78,6 +89,7 @@ class SolverControl need_recompute_ybus_ = true; v_changed_ = true; slack_weight_changed_ = true; + ybus_some_coeffs_zero_ = true; ybus_change_sparsity_pattern_ = true; } @@ -91,6 +103,7 @@ class SolverControl need_recompute_ybus_ = false; v_changed_ = false; slack_weight_changed_ = false; + ybus_some_coeffs_zero_ = false; ybus_change_sparsity_pattern_ = false; } @@ -114,6 +127,10 @@ class SolverControl void tell_v_changed(){v_changed_ = true;} // at least one generator has changed its slack participation void tell_slack_weight_changed(){slack_weight_changed_ = true;} + // tells that some coeff of ybus might have been set to 0. + // (and ybus compressed again, so these coeffs are really completely hidden) + // might need to trigger some recomputation of some solvers (eg NR based ones) + void tell_ybus_some_coeffs_zero(){ybus_some_coeffs_zero_ = true;} bool has_dimension_changed() const {return change_dimension_;} bool has_pv_changed() const {return pv_changed_;} @@ -125,6 +142,7 @@ class SolverControl bool ybus_change_sparsity_pattern() const {return ybus_change_sparsity_pattern_;} bool has_slack_weight_changed() const {return slack_weight_changed_;} bool has_v_changed() const {return v_changed_;} + bool has_ybus_some_coeffs_zero() const {return ybus_some_coeffs_zero_;} protected: bool change_dimension_; @@ -136,6 +154,7 @@ class SolverControl bool need_recompute_ybus_; // some coeff of ybus changed, but not its sparsity pattern bool v_changed_; bool slack_weight_changed_; + bool ybus_some_coeffs_zero_; // tells that some coeff of ybus might have been set to 0. (and ybus compressed again, so these coeffs are really completely hidden) bool ybus_change_sparsity_pattern_; // sparsity pattern of ybus changed (and so are its coeff), or ybus change of dimension }; From 91939fdc4637a2849a847d47bdf096b4e153989f Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Dec 2023 16:00:08 +0100 Subject: [PATCH 05/42] let's see what tests fail now --- CHANGELOG.rst | 2 + benchmarks/benchmark_solvers.py | 2 +- lightsim2grid/tests/test_GridModel.py | 5 +- lightsim2grid/tests/test_SameResPP.py | 4 +- lightsim2grid/tests/test_case118.py | 2 +- lightsim2grid/tests/test_issue_56.py | 2 +- lightsim2grid/tests/test_ptdf.py | 3 +- src/BaseNRSolver.h | 14 +++++- src/BaseNRSolver.tpp | 71 ++++++++++++++++----------- src/BaseNRSolverSingleSlack.h | 3 +- src/BaseNRSolverSingleSlack.tpp | 39 ++++++++------- src/DataGen.cpp | 4 +- src/DataGen.h | 2 + src/DataLine.h | 13 +++-- src/GridModel.cpp | 40 ++++++++++++--- src/GridModel.h | 10 +++- src/Utils.h | 2 +- 17 files changed, 144 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 56c4a88..c880b94 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -30,6 +30,8 @@ Change Log - [FIXED] a bug in init from pypowsybl when some object were disconnected. It raises an error (because they are not connected to a bus): now this function properly handles these cases. +- [FIXED] a bug leading to not propagate correctly the "compute_results" flag when the + environment was copied (for example) - [ADDED] sets of methods to extract the main component of a grid and perform powerflow only on this one. - [ADDED] possibility to set / retrieve the names of each elements of the grid. diff --git a/benchmarks/benchmark_solvers.py b/benchmarks/benchmark_solvers.py index 7281e06..55dfa24 100644 --- a/benchmarks/benchmark_solvers.py +++ b/benchmarks/benchmark_solvers.py @@ -43,10 +43,10 @@ lightsim2grid.SolverType.SparseLU: "NR (SLU)", lightsim2grid.SolverType.KLU: "NR (KLU)", lightsim2grid.SolverType.NICSLU: "NR (NICSLU *)", + lightsim2grid.SolverType.CKTSO: "NR (CKTSO *)", lightsim2grid.SolverType.SparseLUSingleSlack: "NR single (SLU)", lightsim2grid.SolverType.KLUSingleSlack: "NR single (KLU)", lightsim2grid.SolverType.NICSLUSingleSlack: "NR single (NICSLU *)", - lightsim2grid.SolverType.CKTSO: "NR (CKTSO *)", lightsim2grid.SolverType.CKTSOSingleSlack: "NR single (CKTSO *)", lightsim2grid.SolverType.FDPF_XB_SparseLU: "FDPF XB (SLU)", lightsim2grid.SolverType.FDPF_BX_SparseLU: "FDPF BX (SLU)", diff --git a/lightsim2grid/tests/test_GridModel.py b/lightsim2grid/tests/test_GridModel.py index 0d6329e..7230fc7 100644 --- a/lightsim2grid/tests/test_GridModel.py +++ b/lightsim2grid/tests/test_GridModel.py @@ -103,7 +103,8 @@ def make_v0(self, net): return V0 def run_me_pf(self, V0): - return self.model.compute_newton(V0, self.max_it, self.tol) + res = self.model.ac_pf(V0, self.max_it, self.tol) + return res def run_ref_pf(self, net): with warnings.catch_warnings(): @@ -111,7 +112,7 @@ def run_ref_pf(self, net): pp.runpp(net, init="flat", lightsim2grid=False, - numba=True, + numba=False, distributed_slack=False) def do_i_skip(self, func_name): diff --git a/lightsim2grid/tests/test_SameResPP.py b/lightsim2grid/tests/test_SameResPP.py index 9e7e1f7..13ae6a8 100644 --- a/lightsim2grid/tests/test_SameResPP.py +++ b/lightsim2grid/tests/test_SameResPP.py @@ -131,7 +131,7 @@ def _aux_test(self, pn_net): V = backend._grid.ac_pf(v_tmp, 10, 1e-5) assert V.shape[0], "? lightsim diverge when initialized with pp final voltage ?" - backend._grid.tell_topo_changed() + backend._grid.tell_solver_need_reset() Y_pp = backend.init_pp_backend._grid._ppc["internal"]["Ybus"] Sbus = backend.init_pp_backend._grid._ppc["internal"]["Sbus"] @@ -218,7 +218,7 @@ def _aux_test(self, pn_net): backend._grid.deactivate_result_computation() Vdc = backend._grid.dc_pf(Vinit, max_iter, tol_this) backend._grid.reactivate_result_computation() - backend._grid.tell_topo_changed() + backend._grid.tell_solver_need_reset() Ydc_me = copy.deepcopy(backend._grid.get_dcYbus()) Sdc_me = copy.deepcopy(backend._grid.get_dcSbus()) assert np.max(np.abs(V_init_ref[pp_vect_converter] - Vdc[:nb_sub])) <= 100.*self.tol,\ diff --git a/lightsim2grid/tests/test_case118.py b/lightsim2grid/tests/test_case118.py index c4e4890..95b5d87 100644 --- a/lightsim2grid/tests/test_case118.py +++ b/lightsim2grid/tests/test_case118.py @@ -131,7 +131,7 @@ def test_neurips_track2(self): with warnings.catch_warnings(): warnings.filterwarnings("ignore") ls_grid = init(self.pp_net) - ls_grid.tell_topo_changed() + ls_grid.tell_solver_need_reset() V = np.ones(2 * self.nb_bus_total, dtype=np.complex_) V = ls_grid.ac_pf(V, self.max_it, self.tol) self.check_results(V[:self.nb_bus_total], ls_grid, self.pp_net) diff --git a/lightsim2grid/tests/test_issue_56.py b/lightsim2grid/tests/test_issue_56.py index 6b0a3a6..9aed2a0 100644 --- a/lightsim2grid/tests/test_issue_56.py +++ b/lightsim2grid/tests/test_issue_56.py @@ -49,7 +49,7 @@ def test_dc(self): grid_model.deactivate_powerline(l_id) else: grid_model.deactivate_trafo(l_id - nb_powerline) - grid_model.tell_topo_changed() + grid_model.tell_solver_need_reset() V = 1.0 * self.env.backend.V # np.ones(2 * self.env.n_sub, dtype=np.complex_) res = grid_model.dc_pf(V, 10, 1e-8) if len(res): diff --git a/lightsim2grid/tests/test_ptdf.py b/lightsim2grid/tests/test_ptdf.py index 8bfb6da..4a98e4a 100644 --- a/lightsim2grid/tests/test_ptdf.py +++ b/lightsim2grid/tests/test_ptdf.py @@ -36,7 +36,8 @@ def setUp(self) -> None: if solver_type not in self.gridmodel.available_solvers(): self.skipTest("Solver type not supported on this platform") self.gridmodel.change_solver(solver_type) - self.gridmodel.dc_pf(self.V_init, 1, 1e-8) + V = self.gridmodel.dc_pf(self.V_init, 1, 1e-8) + assert len(V), f"dc pf has diverged with error {self.gridmodel.get_dc_solver().get_error()}" self.dcYbus = 1.0 * self.gridmodel.get_dcYbus() self.dcSbus = 1.0 * self.gridmodel.get_dcSbus().real self.Bbus = 1.0 * self.dcYbus.real diff --git a/src/BaseNRSolver.h b/src/BaseNRSolver.h index 8b7c5fa..5a46123 100644 --- a/src/BaseNRSolver.h +++ b/src/BaseNRSolver.h @@ -136,8 +136,16 @@ class BaseNRSolver : public BaseSolver void fill_value_map(Eigen::Index slack_bus_id, const Eigen::VectorXi & pq, - const Eigen::VectorXi & pvpq); - + const Eigen::VectorXi & pvpq, + bool reset_J); + + void reset_if_needed(){ + if(_solver_control.need_reset_solver() || + _solver_control.has_dimension_changed() || + _solver_control.has_ybus_some_coeffs_zero()){ + reset(); + } + } protected: // used linear solver LinearSolver _linear_solver; @@ -151,6 +159,8 @@ class BaseNRSolver : public BaseSolver // to store the mapping from the element of J_ in dS_dVm_ and dS_dVa_ // it does not own any memory at all ! std::vector value_map_; + // std::vector col_map_; + // std::vector row_map_; // timers double timer_initialize_; diff --git a/src/BaseNRSolver.tpp b/src/BaseNRSolver.tpp index b099558..4dbbfb8 100644 --- a/src/BaseNRSolver.tpp +++ b/src/BaseNRSolver.tpp @@ -46,20 +46,14 @@ bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix exc_ << "V (" << V.size() << ") and Ybus (" << Ybus.rows()<< ", " << Ybus.cols() << ")."; throw std::runtime_error(exc_.str()); } - reset_timer(); - // std::cout << "dist slack" << std::endl; - - if(_solver_control.need_reset_solver() || - _solver_control.has_dimension_changed()){ - reset(); - } - auto timer = CustTimer(); if(!is_linear_solver_valid()) { // err_ = ErrorType::NotInitError; return false; } - + reset_timer(); + reset_if_needed(); err_ = ErrorType::NoError; // reset the error if previous error happened + auto timer = CustTimer(); Eigen::VectorXi my_pv = retrieve_pv_with_slack(slack_ids, pv); // retrieve_pv_with_slack (not all), add_slack_to_pv (all) real_type slack_absorbed = std::real(Sbus.sum()); // initial guess for slack_absorbed @@ -94,9 +88,11 @@ bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix bool has_just_been_initialized = false; // to avoid a call to klu_refactor follow a call to klu_factor in the same loop // std::cout << "iter " << nr_iter_ << " dx(0): " << -F(0) << " dx(1): " << -F(1) << std::endl; // std::cout << "slack_absorbed " << slack_absorbed << std::endl; - BaseNRSolver::value_map_.clear(); // TODO smarter solver: only needed if ybus has changed - BaseNRSolver::dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed - BaseNRSolver::dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed + value_map_.clear(); // TODO smarter solver: only needed if ybus has changed + // BaseNRSolver::col_map_.clear(); // TODO smarter solver: only needed if ybus has changed + // BaseNRSolver::row_map_.clear(); // TODO smarter solver: only needed if ybus has changed + dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed + dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed // BaseNRSolver::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed // BaseNRSolver::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed while ((!converged) & (nr_iter_ < max_iter)){ @@ -181,6 +177,7 @@ void BaseNRSolver::reset(){ template void BaseNRSolver::_dSbus_dV(const Eigen::Ref > & Ybus, const Eigen::Ref & V){ + // std::cout << "Ybus.nonZeros(): " << Ybus.nonZeros() << std::endl; auto timer = CustTimer(); const auto size_dS = V.size(); const CplxVect Vnorm = V.array() / V.array().abs(); @@ -198,6 +195,12 @@ void BaseNRSolver::_dSbus_dV(const Eigen::Ref >::InnerIterator it(Ybus, col_id); it; ++it) @@ -298,14 +301,14 @@ void BaseNRSolver::_get_values_J(int & nb_obj_this_col, template void BaseNRSolver::fill_jacobian_matrix(const Eigen::SparseMatrix & Ybus, - const CplxVect & V, - Eigen::Index slack_bus_id, - const RealVect & slack_weights, - const Eigen::VectorXi & pq, - const Eigen::VectorXi & pvpq, - const std::vector & pq_inv, - const std::vector & pvpq_inv - ) + const CplxVect & V, + Eigen::Index slack_bus_id, + const RealVect & slack_weights, + const Eigen::VectorXi & pq, + const Eigen::VectorXi & pvpq, + const std::vector & pq_inv, + const std::vector & pvpq_inv + ) { /** Remember, J has the shape: @@ -346,7 +349,7 @@ void BaseNRSolver::fill_jacobian_matrix(const Eigen::SparseMatrix< #endif // __COUT_TIMES // first time i initialized the matrix, so i need to compute its sparsity pattern fill_jacobian_matrix_unkown_sparsity_pattern(Ybus, V, slack_bus_id, slack_weights, pq, pvpq, pq_inv, pvpq_inv); - fill_value_map(slack_bus_id, pq, pvpq); + fill_value_map(slack_bus_id, pq, pvpq, false); #ifdef __COUT_TIMES std::cout << "\t\t fill_jacobian_matrix_unkown_sparsity_pattern : " << timer2.duration() << std::endl; #endif // __COUT_TIMES @@ -356,7 +359,7 @@ void BaseNRSolver::fill_jacobian_matrix(const Eigen::SparseMatrix< #ifdef __COUT_TIMES auto timer3 = CustTimer(); #endif // - if (BaseNRSolver::value_map_.size() == 0) fill_value_map(slack_bus_id,pq, pvpq); + if (BaseNRSolver::value_map_.size() == 0) fill_value_map(slack_bus_id, pq, pvpq, true); fill_jacobian_matrix_kown_sparsity_pattern(slack_bus_id, pq, pvpq ); @@ -541,11 +544,15 @@ template void BaseNRSolver::fill_value_map( Eigen::Index slack_bus_id, const Eigen::VectorXi & pq, - const Eigen::VectorXi & pvpq + const Eigen::VectorXi & pvpq, + bool reset_J ) { const int n_pvpq = static_cast(pvpq.size()); - value_map_ = std::vector (J_.nonZeros()); + value_map_ = std::vector (); + value_map_.reserve(BaseNRSolver::J_.nonZeros()); + // col_map_ = std::vector (J_.nonZeros()); + // row_map_ = std::vector (J_.nonZeros()); const auto n_row = J_.cols(); unsigned int pos_el = 0; @@ -554,16 +561,18 @@ void BaseNRSolver::fill_value_map( { auto row_id = it.row(); const auto col_id = it.col() - 1; // it's equal to "col_" + if(reset_J) it.valueRef() = 0.; // "forget" previous J value in this setting + if(row_id==0){ // this is the row of the slack bus const Eigen::Index row_id_dS_dVx_r = slack_bus_id; // same for both matrices if(col_id < n_pvpq){ const int col_id_dS_dVa_r = pvpq[col_id]; - value_map_[pos_el] = &dS_dVa_.coeffRef(row_id_dS_dVx_r, col_id_dS_dVa_r); + value_map_.push_back(&dS_dVa_.coeffRef(row_id_dS_dVx_r, col_id_dS_dVa_r)); } else{ const int col_id_dS_dVm_r = pq[col_id - n_pvpq]; - value_map_[pos_el] = &dS_dVm_.coeffRef(row_id_dS_dVx_r, col_id_dS_dVm_r); + value_map_.push_back(&dS_dVm_.coeffRef(row_id_dS_dVx_r, col_id_dS_dVm_r)); } }else{ row_id -= 1; // "do not consider" the row for slack bus (handled above) @@ -573,7 +582,7 @@ void BaseNRSolver::fill_value_map( const int row_id_dS_dVa_r = pvpq[row_id]; const int col_id_dS_dVa_r = pvpq[col_id]; // this_el = dS_dVa_r.coeff(row_id_dS_dVa_r, col_id_dS_dVa_r); - value_map_[pos_el] = &dS_dVa_.coeffRef(row_id_dS_dVa_r, col_id_dS_dVa_r); + value_map_.push_back(&dS_dVa_.coeffRef(row_id_dS_dVa_r, col_id_dS_dVa_r)); // I don't need to perform these checks: if they failed, the element would not be in J_ in the first place // const int is_row_non_null = pq_inv[row_id_dS_dVa_r]; @@ -587,25 +596,27 @@ void BaseNRSolver::fill_value_map( const int row_id_dS_dVa_i = pq[row_id - n_pvpq]; const int col_id_dS_dVa_i = pvpq[col_id]; // this_el = dS_dVa_i.coeff(row_id_dS_dVa_i, col_id_dS_dVa_i); - value_map_[pos_el] = &dS_dVa_.coeffRef(row_id_dS_dVa_i, col_id_dS_dVa_i); + value_map_.push_back(&dS_dVa_.coeffRef(row_id_dS_dVa_i, col_id_dS_dVa_i)); }else if((col_id >= n_pvpq) && (row_id < n_pvpq)){ // this is the J12 part (dS_dVm_r) const int row_id_dS_dVm_r = pvpq[row_id]; const int col_id_dS_dVm_r = pq[col_id - n_pvpq]; // this_el = dS_dVm_r.coeff(row_id_dS_dVm_r, col_id_dS_dVm_r); - value_map_[pos_el] = &dS_dVm_.coeffRef(row_id_dS_dVm_r, col_id_dS_dVm_r); + value_map_.push_back(&dS_dVm_.coeffRef(row_id_dS_dVm_r, col_id_dS_dVm_r)); }else if((col_id >= n_pvpq) && (row_id >= n_pvpq)){ // this is the J22 part (dS_dVm_i) const int row_id_dS_dVm_i = pq[row_id - n_pvpq]; const int col_id_dS_dVm_i = pq[col_id - n_pvpq]; // this_el = dS_dVm_i.coeff(row_id_dS_dVm_i, col_id_dS_dVm_i); - value_map_[pos_el] = &dS_dVm_.coeffRef(row_id_dS_dVm_i, col_id_dS_dVm_i); + value_map_.push_back(&dS_dVm_.coeffRef(row_id_dS_dVm_i, col_id_dS_dVm_i)); } } // go to the next element ++pos_el; } } + dS_dVa_.makeCompressed(); + dS_dVm_.makeCompressed(); } template diff --git a/src/BaseNRSolverSingleSlack.h b/src/BaseNRSolverSingleSlack.h index 8cd8522..09f8042 100644 --- a/src/BaseNRSolverSingleSlack.h +++ b/src/BaseNRSolverSingleSlack.h @@ -58,7 +58,8 @@ class BaseNRSolverSingleSlack : public BaseNRSolver ); void fill_value_map(const Eigen::VectorXi & pq, - const Eigen::VectorXi & pvpq); + const Eigen::VectorXi & pvpq, + bool reset_J); }; diff --git a/src/BaseNRSolverSingleSlack.tpp b/src/BaseNRSolverSingleSlack.tpp index fd9e1ac..7e39ba2 100644 --- a/src/BaseNRSolverSingleSlack.tpp +++ b/src/BaseNRSolverSingleSlack.tpp @@ -40,19 +40,13 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix exc_ << "V (" << V.size() << ") and Ybus (" << Ybus.rows()<<", "<::reset_timer(); - // std::cout << "singleslack" << std::endl; - - if(BaseNRSolver::_solver_control.need_reset_solver() || - BaseNRSolver::_solver_control.has_dimension_changed()){ - BaseNRSolver::reset(); - } - if(!BaseNRSolver::is_linear_solver_valid()){ return false; } - + BaseNRSolver::reset_timer(); + BaseNRSolver::reset_if_needed(); BaseNRSolver::err_ = ErrorType::NoError; // reset the error if previous error happened + auto timer = CustTimer(); // initialize once and for all the "inverse" of these vectors // Eigen::VectorXi my_pv = BaseNRSolver::retrieve_pv_with_slack(slack_ids, pv); @@ -84,6 +78,7 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix BaseNRSolver::value_map_.clear(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed BaseNRSolver::dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed BaseNRSolver::dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed + // BaseNRSolver::J_.setZero(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed or ybus_some_coeffs_zero_ // BaseNRSolver::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed // BaseNRSolver::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed while ((!converged) & (BaseNRSolver::nr_iter_ < max_iter)){ @@ -196,7 +191,7 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp #endif // __COUT_TIMES // first time i initialized the matrix, so i need to compute its sparsity pattern fill_jacobian_matrix_unkown_sparsity_pattern(Ybus, V, pq, pvpq, pq_inv, pvpq_inv); - fill_value_map(pq, pvpq); + fill_value_map(pq, pvpq, false); // std::cout << "\t\tfill_jacobian_matrix_unkown_sparsity_pattern" << std::endl; #ifdef __COUT_TIMES std::cout << "\t\t fill_jacobian_matrix_unkown_sparsity_pattern : " << timer2.duration() << std::endl; @@ -209,7 +204,7 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp #endif // __COUT_TIMES if (BaseNRSolver::value_map_.size() == 0){ // std::cout << "\t\tfill_value_map called" << std::endl; - fill_value_map(pq, pvpq); + fill_value_map(pq, pvpq, true); } fill_jacobian_matrix_kown_sparsity_pattern(pq, pvpq); // std::cout << "\t\tfill_jacobian_matrix_kown_sparsity_pattern" << std::endl; @@ -264,9 +259,9 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity if(BaseNRSolver::J_.cols() != size_j) { need_insert = true; - BaseNRSolver::J_ = Eigen::SparseMatrix(size_j,size_j); + BaseNRSolver::J_ = Eigen::SparseMatrix(size_j, size_j); // pre allocate a large enough matrix - BaseNRSolver::J_.reserve(2*(BaseNRSolver::dS_dVa_.nonZeros()+BaseNRSolver::dS_dVm_.nonZeros())); + BaseNRSolver::J_.reserve(2*(BaseNRSolver::dS_dVa_.nonZeros() + BaseNRSolver::dS_dVm_.nonZeros())); // from an experiment, outerIndexPtr is initialized, with the number of columns // innerIndexPtr and valuePtr are not. } @@ -359,11 +354,14 @@ it requires that J_ is initialized, in compressed mode. template void BaseNRSolverSingleSlack::fill_value_map( const Eigen::VectorXi & pq, - const Eigen::VectorXi & pvpq + const Eigen::VectorXi & pvpq, + bool reset_J ) { const int n_pvpq = static_cast(pvpq.size()); - BaseNRSolver::value_map_ = std::vector (BaseNRSolver::J_.nonZeros()); + BaseNRSolver::value_map_.clear(); + // std::cout << "BaseNRSolver::J_.nonZeros(): " << BaseNRSolver::J_.nonZeros() << std::endl; + BaseNRSolver::value_map_.reserve(BaseNRSolver::J_.nonZeros()); const int n_col = static_cast(BaseNRSolver::J_.cols()); unsigned int pos_el = 0; @@ -372,13 +370,14 @@ void BaseNRSolverSingleSlack::fill_value_map( { const int row_id = static_cast(it.row()); const int col_id = static_cast(it.col()); // it's equal to "col_" + if(reset_J) it.valueRef() = 0.; // "forget" previous J value in this setting // real_type & this_el = J_x_ptr[pos_el]; if((col_id < n_pvpq) && (row_id < n_pvpq)){ // this is the J11 part (dS_dVa_r) const int row_id_dS_dVa_r = pvpq[row_id]; const int col_id_dS_dVa_r = pvpq[col_id]; // this_el = dS_dVa_r.coeff(row_id_dS_dVa_r, col_id_dS_dVa_r); - BaseNRSolver::value_map_[pos_el] = &BaseNRSolver::dS_dVa_.coeffRef(row_id_dS_dVa_r, col_id_dS_dVa_r); + BaseNRSolver::value_map_.push_back(&BaseNRSolver::dS_dVa_.coeffRef(row_id_dS_dVa_r, col_id_dS_dVa_r)); // I don't need to perform these checks: if they failed, the element would not be in J_ in the first place // const int is_row_non_null = pq_inv[row_id_dS_dVa_r]; @@ -393,25 +392,27 @@ void BaseNRSolverSingleSlack::fill_value_map( const int row_id_dS_dVa_i = pq[row_id - n_pvpq]; const int col_id_dS_dVa_i = pvpq[col_id]; // this_el = dS_dVa_i.coeff(row_id_dS_dVa_i, col_id_dS_dVa_i); - BaseNRSolver::value_map_[pos_el] = &BaseNRSolver::dS_dVa_.coeffRef(row_id_dS_dVa_i, col_id_dS_dVa_i); + BaseNRSolver::value_map_.push_back(&BaseNRSolver::dS_dVa_.coeffRef(row_id_dS_dVa_i, col_id_dS_dVa_i)); }else if((col_id >= n_pvpq) && (row_id < n_pvpq)){ // this is the J12 part (dS_dVm_r) const int row_id_dS_dVm_r = pvpq[row_id]; const int col_id_dS_dVm_r = pq[col_id - n_pvpq]; // this_el = dS_dVm_r.coeff(row_id_dS_dVm_r, col_id_dS_dVm_r); - BaseNRSolver::value_map_[pos_el] = &BaseNRSolver::dS_dVm_.coeffRef(row_id_dS_dVm_r, col_id_dS_dVm_r); + BaseNRSolver::value_map_.push_back(&BaseNRSolver::dS_dVm_.coeffRef(row_id_dS_dVm_r, col_id_dS_dVm_r)); }else if((col_id >= n_pvpq) && (row_id >= n_pvpq)){ // this is the J22 part (dS_dVm_i) const int row_id_dS_dVm_i = pq[row_id - n_pvpq]; const int col_id_dS_dVm_i = pq[col_id - n_pvpq]; // this_el = dS_dVm_i.coeff(row_id_dS_dVm_i, col_id_dS_dVm_i); - BaseNRSolver::value_map_[pos_el] = &BaseNRSolver::dS_dVm_.coeffRef(row_id_dS_dVm_i, col_id_dS_dVm_i); + BaseNRSolver::value_map_.push_back(&BaseNRSolver::dS_dVm_.coeffRef(row_id_dS_dVm_i, col_id_dS_dVm_i)); } // go to the next element ++pos_el; } } + // BaseNRSolver::dS_dVa_.makeCompressed(); + // BaseNRSolver::dS_dVm_.makeCompressed(); } template diff --git a/src/DataGen.cpp b/src/DataGen.cpp index c504fc4..e92152e 100644 --- a/src/DataGen.cpp +++ b/src/DataGen.cpp @@ -333,6 +333,7 @@ void DataGen::set_vm(CplxVect & V, const std::vector & id_grid_to_solver) c Eigen::VectorXi DataGen::get_slack_bus_id() const{ std::vector tmp; + tmp.reserve(gen_slackbus_.size()); Eigen::VectorXi res; const auto nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id){ @@ -342,7 +343,8 @@ Eigen::VectorXi DataGen::get_slack_bus_id() const{ if(!is_in_vect(my_bus, tmp)) tmp.push_back(my_bus); } } - res = Eigen::VectorXi::Map(&tmp[0], tmp.size()); // force the copy of the data apparently + if(tmp.empty()) throw std::runtime_error("DataGen::get_slack_bus_id: no generator are tagged slack bus for this grid."); + res = Eigen::VectorXi::Map(tmp.data(), tmp.size()); // force the copy of the data apparently return res; } diff --git a/src/DataGen.h b/src/DataGen.h index c143017..0f531cb 100644 --- a/src/DataGen.h +++ b/src/DataGen.h @@ -234,6 +234,7 @@ class DataGen: public DataGeneric void deactivate(int gen_id, SolverControl & solver_control) { if (status_[gen_id]){ solver_control.tell_recompute_sbus(); + solver_control.tell_pq_changed(); // bus might now be pq if(voltage_regulator_on_[gen_id]) solver_control.tell_v_changed(); if(!turnedoff_gen_pv_) solver_control.tell_pv_changed(); if(gen_slack_weight_[gen_id] != 0. || gen_slackbus_[gen_id]){ @@ -246,6 +247,7 @@ class DataGen: public DataGeneric void reactivate(int gen_id, SolverControl & solver_control) { if(!status_[gen_id]){ solver_control.tell_recompute_sbus(); + solver_control.tell_pq_changed(); // bus might now be pv if(voltage_regulator_on_[gen_id]) solver_control.tell_v_changed(); if(!turnedoff_gen_pv_) solver_control.tell_pv_changed(); if(gen_slack_weight_[gen_id] != 0. || gen_slackbus_[gen_id]){ diff --git a/src/DataLine.h b/src/DataLine.h index 10b4be0..49f07db 100644 --- a/src/DataLine.h +++ b/src/DataLine.h @@ -189,6 +189,7 @@ class DataLine : public DataGeneric virtual void get_graph(std::vector > & res) const; void deactivate(int powerline_id, SolverControl & solver_control) { + // std::cout << "line: deactivate called\n"; if(status_[powerline_id]){ solver_control.tell_recompute_ybus(); // but sparsity pattern do not change here (possibly one more coeff at 0.) @@ -199,12 +200,18 @@ class DataLine : public DataGeneric void reactivate(int powerline_id, SolverControl & solver_control) { if(!status_[powerline_id]){ solver_control.tell_recompute_ybus(); - solver_control.tell_ybus_change_sparsity_pattern(); // this might change + solver_control.tell_ybus_change_sparsity_pattern(); // sparsity pattern might change: a non zero coeff can pop up } _reactivate(powerline_id, status_); } - void change_bus_or(int powerline_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(powerline_id, new_bus_id, bus_or_id_, solver_control, nb_bus);} - void change_bus_ex(int powerline_id, int new_bus_id, SolverControl & solver_control, int nb_bus) {_change_bus(powerline_id, new_bus_id, bus_ex_id_, solver_control, nb_bus);} + void change_bus_or(int powerline_id, int new_bus_id, SolverControl & solver_control, int nb_bus) { + // std::cout << "line: change_bus_or called\n"; + _change_bus(powerline_id, new_bus_id, bus_or_id_, solver_control, nb_bus); + } + void change_bus_ex(int powerline_id, int new_bus_id, SolverControl & solver_control, int nb_bus) { + // std::cout << "line: change_bus_or called\n"; + _change_bus(powerline_id, new_bus_id, bus_ex_id_, solver_control, nb_bus); + } int get_bus_or(int powerline_id) {return _get_bus(powerline_id, status_, bus_or_id_);} int get_bus_ex(int powerline_id) {return _get_bus(powerline_id, status_, bus_ex_id_);} virtual void fillYbus(std::vector > & res, diff --git a/src/GridModel.cpp b/src/GridModel.cpp index 7bdbea3..b717565 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -17,6 +17,7 @@ GridModel::GridModel(const GridModel & other) init_vm_pu_ = other.init_vm_pu_; sn_mva_ = other.sn_mva_; + compute_results_ = other.compute_results_; // copy the powersystem representation // 1. bus @@ -72,8 +73,11 @@ GridModel::GridModel(const GridModel & other) _solver.change_solver(other._solver.get_type()); _dc_solver.change_solver(other._dc_solver.get_type()); compute_results_ = other.compute_results_; + solver_control_.tell_all_changed(); _dc_solver.set_gridmodel(this); _solver.set_gridmodel(this); + _dc_solver.tell_solver_control(solver_control_); + _solver.tell_solver_control(solver_control_); } //pickle @@ -264,8 +268,13 @@ void GridModel::reset(bool reset_solver, bool reset_ac, bool reset_dc) dcSbus_ = CplxVect(); bus_pv_ = Eigen::VectorXi(); bus_pq_ = Eigen::VectorXi(); - slack_weights_ = RealVect(); solver_control_.tell_all_changed(); + + slack_bus_id_ac_me_ = Eigen::VectorXi(); // slack bus id, gridmodel number + slack_bus_id_ac_solver_ = Eigen::VectorXi(); // slack bus id, solver number + slack_bus_id_dc_me_ = Eigen::VectorXi(); + slack_bus_id_dc_solver_ = Eigen::VectorXi(); + slack_weights_ = RealVect(); // reset the solvers if (reset_solver){ @@ -274,6 +283,7 @@ void GridModel::reset(bool reset_solver, bool reset_ac, bool reset_dc) _solver.set_gridmodel(this); _dc_solver.set_gridmodel(this); } + } CplxVect GridModel::ac_pf(const CplxVect & Vinit, @@ -301,6 +311,7 @@ CplxVect GridModel::ac_pf(const CplxVect & Vinit, Ybus_ac_, id_me_to_ac_solver_, id_ac_solver_to_me_, + slack_bus_id_ac_me_, slack_bus_id_ac_solver_, is_ac, solver_control_); @@ -397,6 +408,7 @@ CplxVect GridModel::check_solution(const CplxVect & V_proposed, bool check_q_lim Ybus_ac_, id_me_to_ac_solver_, id_ac_solver_to_me_, + slack_bus_id_ac_me_, slack_bus_id_ac_solver_, is_ac, reset_solver); @@ -429,6 +441,7 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, Eigen::SparseMatrix & Ybus, std::vector & id_me_to_solver, std::vector & id_solver_to_me, + Eigen::VectorXi & slack_bus_id_me, Eigen::VectorXi & slack_bus_id_solver, bool is_ac, const SolverControl & solver_control) @@ -451,7 +464,8 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, if (solver_control.need_reset_solver() || solver_control.has_dimension_changed() || solver_control.has_slack_participate_changed()){ - slack_bus_id_solver = generators_.get_slack_bus_id(); + // std::cout << "slack_bus_id_solver\n"; + slack_bus_id_me = generators_.get_slack_bus_id(); // this is the slack bus ids with the gridmodel ordering, not the solver ordering. // conversion to solver ordering is done in init_slack_bus @@ -462,6 +476,7 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, if (solver_control.need_reset_solver() || solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed()){ + // std::cout << "init_Ybus;" << std::endl; init_Ybus(Ybus, id_me_to_solver, id_solver_to_me); // std::cout << "init_Ybus;" << std::endl; } @@ -469,12 +484,14 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, solver_control.ybus_change_sparsity_pattern() || solver_control.has_dimension_changed() || solver_control.need_recompute_ybus()){ + // std::cout << "fillYbus;" << std::endl; fillYbus(Ybus, is_ac, id_me_to_solver); // std::cout << "fillYbus;" << std::endl; } if (solver_control.need_reset_solver() || solver_control.has_dimension_changed()) { // init Sbus + // std::cout << "init_Sbus;" << std::endl; Sbus = CplxVect::Constant(id_solver_to_me.size(), 0.); // std::cout << "init_Sbus;" << std::endl; } @@ -483,7 +500,9 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, solver_control.has_slack_participate_changed() || solver_control.has_pv_changed() || solver_control.has_pq_changed()) { - init_slack_bus(Sbus, id_me_to_solver, id_solver_to_me, slack_bus_id_solver); + // std::cout << "init_slack_bus;" << std::endl; + init_slack_bus(Sbus, id_me_to_solver, id_solver_to_me, slack_bus_id_me, slack_bus_id_solver); + // std::cout << "fillpv_pq;" << std::endl; fillpv_pq(id_me_to_solver, id_solver_to_me, slack_bus_id_solver, solver_control); // std::cout << "fillpv_pq;" << std::endl; } @@ -491,6 +510,7 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, if (is_ac && (solver_control.need_reset_solver() || solver_control.has_dimension_changed() || solver_control.need_recompute_sbus())){ + // std::cout << "total_gen_per_bus_;" << std::endl; int nb_bus_total = static_cast(bus_vn_kv_.size()); total_q_min_per_bus_ = RealVect::Constant(nb_bus_total, 0.); total_q_max_per_bus_ = RealVect::Constant(nb_bus_total, 0.); @@ -504,6 +524,7 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, solver_control.has_dimension_changed() || solver_control.has_slack_participate_changed() || solver_control.has_pq_changed()) { + // std::cout << "fillSbus_me;" << std::endl; fillSbus_me(Sbus, is_ac, id_me_to_solver); // std::cout << "fillSbus_me;" << std::endl; } @@ -566,6 +587,7 @@ void GridModel::process_results(bool conv, static_cast(Vinit.size())); } else { //powerflow diverge + // std::cout << "powerflow diverge" << std::endl; reset_results(); // TODO solver control ??? something to do here ? } @@ -598,11 +620,12 @@ void GridModel::init_Ybus(Eigen::SparseMatrix & Ybus, void GridModel::init_slack_bus(const CplxVect & Sbus, const std::vector& id_me_to_solver, const std::vector& id_solver_to_me, + const Eigen::VectorXi & slack_bus_id_me, Eigen::VectorXi & slack_bus_id_solver){ const int nb_bus = static_cast(id_solver_to_me.size()); // slack_bus_id_solver = Eigen::VectorXi::Zero(slack_bus_id_.size()); - // slack_bus_id_solver = Eigen::VectorXi::Constant(slack_bus_id_solver.size(), _deactivated_bus_id); + slack_bus_id_solver = Eigen::VectorXi::Constant(slack_bus_id_me.size(), _deactivated_bus_id); size_t i = 0; // std::cout << "slack_bus_id_solver 2: "; @@ -616,7 +639,7 @@ void GridModel::init_slack_bus(const CplxVect & Sbus, // std::cout << std::endl; // for(auto el: slack_bus_id_) { - for(auto el: slack_bus_id_solver) { + for(auto el: slack_bus_id_me) { auto tmp = id_me_to_solver[el]; if(tmp == _deactivated_bus_id){ std::ostringstream exc_; @@ -624,7 +647,7 @@ void GridModel::init_slack_bus(const CplxVect & Sbus, exc_ << " You can check element "; exc_ << el; exc_ << ": ["; - for(auto el2 : slack_bus_id_solver) exc_ << el2 << ", "; + for(auto el2 : slack_bus_id_me) exc_ << el2 << ", "; exc_ << "]."; throw std::out_of_range(exc_.str()); } @@ -772,6 +795,7 @@ void GridModel::compute_results(bool ac){ } void GridModel::reset_results(){ + // std::cout << "reset_results\n"; powerlines_.reset_results(); // TODO have a function to dispatch that to all type of elements shunts_.reset_results(); trafos_.reset_results(); @@ -812,8 +836,10 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, Ybus_dc_, id_me_to_dc_solver_, id_dc_solver_to_me_, + slack_bus_id_dc_me_, slack_bus_id_dc_solver_, - is_ac, solver_control_); + is_ac, + solver_control_); // std::cout << "after pre proces\n"; // start the solver if(solver_control_.need_reset_solver() || diff --git a/src/GridModel.h b/src/GridModel.h index 2bf85b7..19717ba 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -74,6 +74,7 @@ class GridModel : public DataGeneric GridModel(): solver_control_(), + compute_results_(true), init_vm_pu_(1.04), sn_mva_(1.0){ _solver.change_solver(SolverType::SparseLU); @@ -633,6 +634,7 @@ class GridModel : public DataGeneric Eigen::SparseMatrix & Ybus, std::vector & id_me_to_solver, std::vector & id_solver_to_me, + Eigen::VectorXi & slack_bus_id_me, Eigen::VectorXi & slack_bus_id_solver, bool is_ac, const SolverControl & solver_control); @@ -647,7 +649,9 @@ class GridModel : public DataGeneric void init_slack_bus(const CplxVect & Sbus, const std::vector & id_me_to_solver, const std::vector& id_solver_to_me, - Eigen::VectorXi & slack_bus_id_solver); + const Eigen::VectorXi & slack_bus_id_me, + Eigen::VectorXi & slack_bus_id_solver + ); void fillYbus(Eigen::SparseMatrix & res, bool ac, const std::vector& id_me_to_solver); void fillSbus_me(CplxVect & res, bool ac, const std::vector& id_me_to_solver); void fillpv_pq(const std::vector& id_me_to_solver, @@ -802,7 +806,9 @@ class GridModel : public DataGeneric // 8. slack bus // std::vector slack_bus_id_; - Eigen::VectorXi slack_bus_id_ac_solver_; + Eigen::VectorXi slack_bus_id_ac_me_; // slack bus id, gridmodel number + Eigen::VectorXi slack_bus_id_ac_solver_; // slack bus id, solver number + Eigen::VectorXi slack_bus_id_dc_me_; Eigen::VectorXi slack_bus_id_dc_solver_; RealVect slack_weights_; diff --git a/src/Utils.h b/src/Utils.h index 406c125..eeaec05 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -96,7 +96,7 @@ class SolverControl void tell_none_changed(){ change_dimension_ = false; pv_changed_ = false; - pq_changed_ = true; + pq_changed_ = false; slack_participate_changed_ = false; need_reset_solver_ = false; need_recompute_sbus_ = false; From 8bb7acd9f9b5fd96eaddecb6c1a63768832504a7 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 15 Dec 2023 14:48:30 +0100 Subject: [PATCH 06/42] let's see failing tests --- .../tests/test_dist_slack_backend.py | 16 +++++-- src/BaseNRSolver.h | 3 +- src/BaseSolver.cpp | 27 ++++++----- src/DCSolver.h | 2 +- src/DCSolver.tpp | 16 ++++++- src/DataGen.cpp | 2 +- src/GridModel.cpp | 48 ++++++++++--------- 7 files changed, 72 insertions(+), 42 deletions(-) diff --git a/lightsim2grid/tests/test_dist_slack_backend.py b/lightsim2grid/tests/test_dist_slack_backend.py index 40c492a..2d63007 100644 --- a/lightsim2grid/tests/test_dist_slack_backend.py +++ b/lightsim2grid/tests/test_dist_slack_backend.py @@ -44,28 +44,36 @@ def _prepare_env(self, env): env.set_id(0) def _run_env(self, env): + # print("Run env starts") obs = env.reset() done = False ts = 0 aor = np.zeros((self.max_iter_real, env.n_line)) gen_p = np.zeros((self.max_iter_real, env.n_gen)) + info = None + # print("While starts") while not done: + # print("\tbefore step") obs, reward, done, info = env.step(env.action_space()) aor[ts,:] = obs.a_or gen_p[ts,:] = obs.gen_p ts += 1 if ts >= self.max_iter_real: break - return ts, done, aor, gen_p + # print("Run env stops") + return ts, done, aor, gen_p, info def test_different(self): self._aux_test_different(self.env_ss, self.env_ds) def _aux_test_different(self, env_ss, env_ds): - ts_ss, done_ss, aor_ss, gen_p_ss = self._run_env(env_ss) - ts_ds, done_ds, aor_ds, gen_p_ds = self._run_env(env_ds) + # print("before single slack") + ts_ss, done_ss, aor_ss, gen_p_ss, info_ss = self._run_env(env_ss) + # print("before dist slack") + ts_ds, done_ds, aor_ds, gen_p_ds, info_ds = self._run_env(env_ds) + # print("after dist slack") - assert ts_ss == ts_ds + assert ts_ss == ts_ds, f"ts_ss={ts_ss} != {ts_ds}=ts_ds: info_ds={info_ds['exception']}, info_ss={info_ss['exception']}" assert done_ss == done_ds # non redispatchable gen are not affected diff --git a/src/BaseNRSolver.h b/src/BaseNRSolver.h index 5a46123..dc1019e 100644 --- a/src/BaseNRSolver.h +++ b/src/BaseNRSolver.h @@ -142,7 +142,8 @@ class BaseNRSolver : public BaseSolver void reset_if_needed(){ if(_solver_control.need_reset_solver() || _solver_control.has_dimension_changed() || - _solver_control.has_ybus_some_coeffs_zero()){ + _solver_control.has_ybus_some_coeffs_zero() || + _solver_control.has_slack_participate_changed()){ reset(); } } diff --git a/src/BaseSolver.cpp b/src/BaseSolver.cpp index 624b523..843d1a4 100644 --- a/src/BaseSolver.cpp +++ b/src/BaseSolver.cpp @@ -148,32 +148,35 @@ Eigen::VectorXi BaseSolver::extract_slack_bus_id(const Eigen::VectorXi & pv, // pq: list of index of pq nodes // nb_bus: total number of bus in the grid // returns: res: the ids of all the slack buses (by def: not PV and not PQ) - - Eigen::VectorXi res(nb_bus - pv.size() - pq.size()); + int nb_slacks = nb_bus - pv.size() - pq.size(); + if(nb_slacks == 0){ + // TODO DEBUG MODE + throw std::runtime_error("BaseSolver::extract_slack_bus_id: All buses are tagged as PV or PQ, there can be no slack."); + } + Eigen::VectorXi res(nb_slacks); Eigen::Index i_res = 0; - + // run through both pv and pq nodes and declare they are not slack bus std::vector tmp(nb_bus, true); - for(unsigned int k=0; k < pv.size(); ++k) - { - tmp[pv[k]] = false; - } - for(unsigned int k=0; k < pq.size(); ++k) - { - tmp[pq[k]] = false; - } + for(auto pv_i : pv) tmp[pv_i] = false; + for(auto pq_i : pq) tmp[pq_i] = false; + // run through all buses for(unsigned int k=0; k < nb_bus; ++k) { if(tmp[k]) { + if((i_res >= nb_slacks)){ + // TODO DEBUG MODE + throw std::runtime_error("BaseSolver::extract_slack_bus_id: too many slack found. Maybe a bus is both PV and PQ ?"); + } res[i_res] = k; ++i_res; } } if(res.size() != i_res){ // TODO DEBUG MODE - throw std::runtime_error("BaseSolver::extract_slack_bus_id: No slack bus is found in your grid"); + throw std::runtime_error("BaseSolver::extract_slack_bus_id: Some slacks are not found in your grid."); } return res; } diff --git a/src/DCSolver.h b/src/DCSolver.h index eeb14a2..5e0e035 100644 --- a/src/DCSolver.h +++ b/src/DCSolver.h @@ -10,7 +10,7 @@ #define DCSOLVER_H #include "BaseSolver.h" -// TODO make err_ more explicit: use an enum + template class BaseDCSolver: public BaseSolver { diff --git a/src/DCSolver.tpp b/src/DCSolver.tpp index 87c2fb3..30b903b 100644 --- a/src/DCSolver.tpp +++ b/src/DCSolver.tpp @@ -31,7 +31,8 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix return false; } if(_solver_control.need_reset_solver() || - _solver_control.has_dimension_changed()){ + _solver_control.has_dimension_changed() || + _solver_control.has_ybus_some_coeffs_zero()){ reset(); } @@ -48,18 +49,28 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix // TODO SLACK (for now i put all slacks as PV, except the first one) // this should be handled in Sbus, because we know the amount of power absorbed by the slack // so we can compute it correctly ! + // std::cout << "\t\t\tretrieve_pv_with_slack \n"; + // std::cout << "slack_ids: "; + // for(auto el: slack_ids) std::cout << el << ", "; + // std::cout << std::endl; my_pv_ = retrieve_pv_with_slack(slack_ids, pv); + // std::cout << "my_pv_: "; + // for(auto el: my_pv_) std::cout << el << ", "; + // std::cout << std::endl; // const Eigen::VectorXi & my_pv = pv; // find the slack buses + // std::cout << "\t\t\textract_slack_bus_id \n"; slack_buses_ids_solver_ = extract_slack_bus_id(my_pv_, pq, sizeYbus_with_slack_); sizeYbus_without_slack_ = sizeYbus_with_slack_ - slack_buses_ids_solver_.size(); // corresp bus -> solverbus + // std::cout << "\t\t\tfill_mat_bus_id \n"; fill_mat_bus_id(sizeYbus_with_slack_); // remove the slack bus from Ybus // and extract only real part + // std::cout << "\t\t\tfill_dcYbus_noslack \n"; fill_dcYbus_noslack(sizeYbus_with_slack_, Ybus); #ifdef __COUT_TIMES @@ -72,6 +83,7 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix #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; @@ -82,6 +94,7 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix } // remove the slack bus from Sbus + // std::cout << "\t\t\t dcSbus_noslack_ \n"; dcSbus_noslack_ = RealVect::Constant(sizeYbus_without_slack_, my_zero_); for (int k=0; k < sizeYbus_with_slack_; ++k){ if(mat_bus_id_(k) == -1) continue; // I don't add anything to the slack bus @@ -90,6 +103,7 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix } // solve for theta: Sbus = dcY . theta (make a copy to keep dcSbus_noslack_) + // std::cout << "\t\t\t Va_dc_without_slack \n"; RealVect Va_dc_without_slack = dcSbus_noslack_; ErrorType error = _linear_solver.solve(dcYbus_noslack_, Va_dc_without_slack, just_factorize); if(error != ErrorType::NoError){ diff --git a/src/DataGen.cpp b/src/DataGen.cpp index e92152e..973f92e 100644 --- a/src/DataGen.cpp +++ b/src/DataGen.cpp @@ -445,7 +445,7 @@ void DataGen::update_slack_weights(Eigen::Ref(bus_vn_kv_.size()); total_q_min_per_bus_ = RealVect::Constant(nb_bus_total, 0.); total_q_max_per_bus_ = RealVect::Constant(nb_bus_total, 0.); @@ -523,12 +525,13 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, if (solver_control.need_reset_solver() || solver_control.has_dimension_changed() || solver_control.has_slack_participate_changed() || - solver_control.has_pq_changed()) { - // std::cout << "fillSbus_me;" << std::endl; + solver_control.has_pq_changed() || + solver_control.need_recompute_sbus()) { + // std::cout << "\t\tfillSbus_me;" << std::endl; fillSbus_me(Sbus, is_ac, id_me_to_solver); // std::cout << "fillSbus_me;" << std::endl; } - + // std::cout << "\t\tstatic_cast(id_solver_to_me.size());" << std::endl; const int nb_bus_solver = static_cast(id_solver_to_me.size()); CplxVect V = CplxVect::Constant(nb_bus_solver, init_vm_pu_); for(int bus_solver_id = 0; bus_solver_id < nb_bus_solver; ++bus_solver_id){ @@ -542,6 +545,7 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, // for(auto el: V) std::cout << el << ", "; // std::cout << std::endl; // std::cout << "nb_bus_solver " << nb_bus_solver << std::endl; + // std::cout << "\t\tend pre_process_solver" << std::endl; return V; } @@ -709,7 +713,6 @@ void GridModel::fillpv_pq(const std::vector& id_me_to_solver, const SolverControl & solver_control) { // Nothing to do if neither pv, nor pq nor the dimension of the problem has changed - if(!solver_control.has_pq_changed() && !solver_control.has_pv_changed() && !solver_control.has_dimension_changed()) return; // init pq and pv vector // TODO remove the order here..., i could be faster in this piece of code (looping once through the buses) @@ -840,7 +843,7 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, slack_bus_id_dc_solver_, is_ac, solver_control_); - // std::cout << "after pre proces\n"; + // std::cout << "\tafter pre proces (dc)\n"; // start the solver if(solver_control_.need_reset_solver() || solver_control_.has_dimension_changed() || @@ -848,7 +851,7 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, solver_control_.has_pv_changed() || solver_control_.has_slack_weight_changed()){ // TODO smarter solver: this is done both in ac and in dc ! - // std::cout << "get_slack_weights" << std::endl; + // std::cout << "\tget_slack_weights" << std::endl; slack_weights_ = generators_.get_slack_weights(Ybus_dc_.rows(), id_me_to_dc_solver_); } // std::cout << "V (init to dc pf)\n"; @@ -857,11 +860,12 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, // std::cout << "dcSbus (init to dc pf)\n"; // for(auto el: dcSbus_) std::cout << el << ", "; // std::cout << std::endl; + // std::cout << "\tbefore compute dc pf" << std::endl; conv = _dc_solver.compute_pf(Ybus_dc_, V, dcSbus_, slack_bus_id_dc_solver_, slack_weights_, bus_pv_, bus_pq_, max_iter, tol); - // std::cout << "after compute_pf\n"; + // std::cout << "\tprocess_results (dc) \n"; // store results (fase -> because I am in dc mode) - process_results(conv, res, Vinit, false, id_me_to_dc_solver_); - // std::cout << "after compute_pf\n"; + process_results(conv, res, Vinit, is_ac, id_me_to_dc_solver_); + // std::cout << "\tafter compute_pf\n"; return res; } From d37aa429b29b55e18aacb4a3ea7dbc9758db6a17 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 18 Dec 2023 09:32:43 +0100 Subject: [PATCH 07/42] fixing some broken tests --- src/BaseNRSolver.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/BaseNRSolver.h b/src/BaseNRSolver.h index dc1019e..bde0d6a 100644 --- a/src/BaseNRSolver.h +++ b/src/BaseNRSolver.h @@ -143,7 +143,10 @@ class BaseNRSolver : public BaseSolver if(_solver_control.need_reset_solver() || _solver_control.has_dimension_changed() || _solver_control.has_ybus_some_coeffs_zero() || - _solver_control.has_slack_participate_changed()){ + _solver_control.has_slack_participate_changed() || + _solver_control.has_pv_changed() || + _solver_control.has_pq_changed() + ){ reset(); } } From 44cea300999c3603cba2f652912ae921c2c56292 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 18 Dec 2023 15:34:32 +0100 Subject: [PATCH 08/42] huge renaming to clean src (cpp) repo --- CHANGELOG.rst | 3 + docs/benchmarks.rst | 27 +- setup.py | 36 +- src/BaseMultiplePowerflow.cpp | 4 +- src/ChooseSolver.h | 17 +- src/GridModel.cpp | 20 +- src/GridModel.h | 90 ++--- src/SecurityAnalysis.cpp | 3 +- src/Solvers.cpp | 2 +- src/Solvers.h | 59 +-- .../DCLineContainer.cpp} | 15 +- .../DCLineContainer.h} | 43 ++- .../GeneratorContainer.cpp} | 74 ++-- .../GeneratorContainer.h} | 37 +- .../GenericContainer.cpp} | 33 +- .../GenericContainer.h} | 27 +- .../LineContainer.cpp} | 59 +-- .../LineContainer.h} | 41 +- .../LoadContainer.cpp} | 34 +- .../LoadContainer.h} | 30 +- .../SGenContainer.cpp} | 67 ++-- .../SGenContainer.h} | 30 +- .../ShuntContainer.cpp} | 43 +-- .../ShuntContainer.h} | 32 +- .../TrafoContainer.cpp} | 97 ++--- .../TrafoContainer.h} | 31 +- src/help_fun_msg.cpp | 65 ++-- src/help_fun_msg.h | 14 +- src/{ => linear_solvers}/CKTSOSolver.cpp | 0 src/{ => linear_solvers}/CKTSOSolver.h | 0 src/{ => linear_solvers}/KLUSolver.cpp | 0 src/{ => linear_solvers}/KLUSolver.h | 0 src/{ => linear_solvers}/NICSLUSolver.cpp | 0 src/{ => linear_solvers}/NICSLUSolver.h | 0 src/{ => linear_solvers}/SparseLUSolver.cpp | 0 src/{ => linear_solvers}/SparseLUSolver.h | 0 src/main.cpp | 355 +++++++++--------- .../BaseAlgo.cpp} | 24 +- .../BaseAlgo.h} | 18 +- .../BaseDCAlgo.h} | 22 +- .../BaseDCAlgo.tpp} | 24 +- .../BaseFDPFAlgo.h} | 22 +- .../BaseFDPFAlgo.tpp} | 10 +- .../BaseNRAlgo.h} | 20 +- .../BaseNRAlgo.tpp} | 38 +- .../BaseNRSingleSlackAlgo.h} | 16 +- .../BaseNRSingleSlackAlgo.tpp} | 188 +++++----- .../GaussSeidelAlgo.cpp} | 6 +- .../GaussSeidelAlgo.h} | 18 +- .../GaussSeidelSynchAlgo.cpp} | 4 +- .../GaussSeidelSynchAlgo.h} | 18 +- 51 files changed, 919 insertions(+), 897 deletions(-) rename src/{DataDCLine.cpp => element_container/DCLineContainer.cpp} (88%) rename src/{DataDCLine.h => element_container/DCLineContainer.h} (92%) rename src/{DataGen.cpp => element_container/GeneratorContainer.cpp} (84%) rename src/{DataGen.h => element_container/GeneratorContainer.h} (92%) rename src/{DataGeneric.cpp => element_container/GenericContainer.cpp} (80%) rename src/{DataGeneric.h => element_container/GenericContainer.h} (89%) rename src/{DataLine.cpp => element_container/LineContainer.cpp} (90%) rename src/{DataLine.h => element_container/LineContainer.h} (91%) rename src/{DataLoad.cpp => element_container/LoadContainer.cpp} (77%) rename src/{DataLoad.h => element_container/LoadContainer.h} (89%) rename src/{DataSGen.cpp => element_container/SGenContainer.cpp} (71%) rename src/{DataSGen.h => element_container/SGenContainer.h} (90%) rename src/{DataShunt.cpp => element_container/ShuntContainer.cpp} (81%) rename src/{DataShunt.h => element_container/ShuntContainer.h} (89%) rename src/{DataTrafo.cpp => element_container/TrafoContainer.cpp} (86%) rename src/{DataTrafo.h => element_container/TrafoContainer.h} (93%) rename src/{ => linear_solvers}/CKTSOSolver.cpp (100%) rename src/{ => linear_solvers}/CKTSOSolver.h (100%) rename src/{ => linear_solvers}/KLUSolver.cpp (100%) rename src/{ => linear_solvers}/KLUSolver.h (100%) rename src/{ => linear_solvers}/NICSLUSolver.cpp (100%) rename src/{ => linear_solvers}/NICSLUSolver.h (100%) rename src/{ => linear_solvers}/SparseLUSolver.cpp (100%) rename src/{ => linear_solvers}/SparseLUSolver.h (100%) rename src/{BaseSolver.cpp => powerflow_algorithm/BaseAlgo.cpp} (87%) rename src/{BaseSolver.h => powerflow_algorithm/BaseAlgo.h} (96%) rename src/{DCSolver.h => powerflow_algorithm/BaseDCAlgo.h} (88%) rename src/{DCSolver.tpp => powerflow_algorithm/BaseDCAlgo.tpp} (92%) rename src/{BaseFDPFSolver.h => powerflow_algorithm/BaseFDPFAlgo.h} (95%) rename src/{BaseFDPFSolver.tpp => powerflow_algorithm/BaseFDPFAlgo.tpp} (93%) rename src/{BaseNRSolver.h => powerflow_algorithm/BaseNRAlgo.h} (95%) rename src/{BaseNRSolver.tpp => powerflow_algorithm/BaseNRAlgo.tpp} (94%) rename src/{BaseNRSolverSingleSlack.h => powerflow_algorithm/BaseNRSingleSlackAlgo.h} (86%) rename src/{BaseNRSolverSingleSlack.tpp => powerflow_algorithm/BaseNRSingleSlackAlgo.tpp} (65%) rename src/{GaussSeidelSolver.cpp => powerflow_algorithm/GaussSeidelAlgo.cpp} (96%) rename src/{GaussSeidelSolver.h => powerflow_algorithm/GaussSeidelAlgo.h} (81%) rename src/{GaussSeidelSynchSolver.cpp => powerflow_algorithm/GaussSeidelSynchAlgo.cpp} (95%) rename src/{GaussSeidelSynchSolver.h => powerflow_algorithm/GaussSeidelSynchAlgo.h} (68%) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c880b94..025c310 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -39,6 +39,9 @@ Change Log - [ADDED] computation of PTPF (Power Transfer Distribution Factor) is now possible - [IMPROVED] now performing the new grid2op `create_test_suite` - [IMPROVED] now lightsim2grid properly throw `BackendError` +- [IMPROVED] clean ce cpp side by refactoring: making clearer the difference (linear) solver + vs powerflow algorithm and move same type of files in the same directory. This change + does not really affect python side at the moment (but will in future versions) [0.7.5] 2023-10-05 -------------------- diff --git a/docs/benchmarks.rst b/docs/benchmarks.rst index a49c428..4ad4aa2 100644 --- a/docs/benchmarks.rst +++ b/docs/benchmarks.rst @@ -39,7 +39,8 @@ To run the benchmark `cd` in the [benchmark](./benchmarks) folder and type: (we remind that these simulations correspond to simulation on one core of the CPU. Of course it is possible to make use of all the available cores, which would increase the number of steps that can be performed) -We compare up to 19 different solvers: +We compare up to 19 different "solvers" (combination of "linear solver used" (*eg* Eigen, KLU, CKTSO, NICSLU) +and powerflow algorithm (*eg* "Newton Raphson", or "Fast Decoupled")): - **PP**: PandaPowerBackend (default grid2op backend) which is the reference in our benchmarks (uses the numba acceleration). It is our reference solver. @@ -102,16 +103,24 @@ Computation time In this first subsection we compare the computation times: - **grid2op speed** from a grid2op point of view - (this include the time to compute the powerflow, plus the time to modify the powergrid plus the - time to read back the data once the powerflow has run plus the time to update the environment and - the observations etc.). It is reported in "iteration per second" (`it/s`) and represents the number of grid2op "step" + (this include the time to compute the powerflow, plus the time to modify + the powergrid plus the + time to read back the data once the powerflow has run plus the time to update + the environment and + the observations etc.). It is reported in "iteration per second" (`it/s`) and + represents the number of grid2op "step" that can be computed per second. -- **grid2op 'backend.runpf' time** corresponds to the time the solver take to perform a powerflow - as seen from grid2op (counting the resolution time and some time to check the validity of the results but - not the time to update the grid nor the grid2op environment), for lightsim2grid it includes the time to read back the data +- **grid2op 'backend.runpf' time** corresponds to the time the solver take + to perform a powerflow + as seen from grid2op (counting the resolution time and some time to check + the validity of the results but + not the time to update the grid nor the grid2op environment), for lightsim2grid + it includes the time to read back the data from c++ to python. It is reported in milli seconds (ms). -- **solver powerflow time** corresponds only to the time spent in the solver itself. It does not take into - account any of the checking, nor the transfer of the data python side etc. It is reported in milli seconds (ms) as well. +- **solver powerflow time** corresponds only to the time spent in the solver + itself. It does not take into + account any of the checking, nor the transfer of the data python side etc. + It is reported in milli seconds (ms) as well. There are two major differences between **grid2op 'backend.runpf' time** and **solver powerflow time**. In **grid2op 'backend.runpf' time** the time to initialize the solver (usually with the DC approximation) is counted (it is not in **solver powerflow time**). Secondly, diff --git a/setup.py b/setup.py index 9a0f4f0..8ddbbf5 100644 --- a/setup.py +++ b/setup.py @@ -95,7 +95,7 @@ "be available, which is maybe ~30% slower than \"KLU\". If you are using grid2op there " "will still be a huge benefit.") -INCLUDE = INCLUDE_suitesparse +INCLUDE = ["src"] + INCLUDE_suitesparse # now add the Eigen library (header only) eigen_path = os.path.abspath(".") @@ -137,31 +137,31 @@ f"-DVERSION_MEDIUM={VERSION_MEDIUM}", f"-DVERSION_MINOR={VERSION_MINOR}"] src_files = ['src/main.cpp', + "src/powerflow_algorithm/GaussSeidelAlgo.cpp", + "src/powerflow_algorithm/GaussSeidelSynchAlgo.cpp", + "src/powerflow_algorithm/BaseAlgo.cpp", + "src/linear_solvers/SparseLUSolver.cpp", "src/help_fun_msg.cpp", - "src/SparseLUSolver.cpp", "src/BaseConstants.cpp", "src/GridModel.cpp", - "src/DataConverter.cpp", - "src/DataLine.cpp", - "src/DataGeneric.cpp", - "src/DataShunt.cpp", - "src/DataTrafo.cpp", - "src/DataLoad.cpp", - "src/DataGen.cpp", - "src/DataSGen.cpp", - "src/DataDCLine.cpp", "src/ChooseSolver.cpp", - "src/GaussSeidelSolver.cpp", - "src/GaussSeidelSynchSolver.cpp", - "src/BaseSolver.cpp", "src/BaseMultiplePowerflow.cpp", "src/Computers.cpp", "src/SecurityAnalysis.cpp", "src/Solvers.cpp", - "src/Utils.cpp"] + "src/Utils.cpp", + "src/DataConverter.cpp", + "src/element_container/LineContainer.cpp", + "src/element_container/GenericContainer.cpp", + "src/element_container/ShuntContainer.cpp", + "src/element_container/TrafoContainer.cpp", + "src/element_container/LoadContainer.cpp", + "src/element_container/GeneratorContainer.cpp", + "src/element_container/SGenContainer.cpp", + "src/element_container/DCLineContainer.cpp"] if KLU_SOLVER_AVAILABLE: - src_files.append("src/KLUSolver.cpp") + src_files.append("src/linear_solvers/KLUSolver.cpp") extra_compile_args_tmp.append("-DKLU_SOLVER_AVAILABLE") print("INFO: Using KLU package") @@ -208,7 +208,7 @@ if include_nicslu and libnicslu_path is not None: LIBS.append(os.path.join(path_nicslu, libnicslu_path)) include_dirs.append(os.path.join(path_nicslu, "include")) - src_files.append("src/NICSLUSolver.cpp") + src_files.append("src/linear_solvers/NICSLUSolver.cpp") extra_compile_args.append("-DNICSLU_SOLVER_AVAILABLE") print("INFO: Using NICSLU package") @@ -255,7 +255,7 @@ if include_cktso and libcktso_path is not None: LIBS.append(os.path.join(path_cktso, libcktso_path)) include_dirs.append(os.path.join(path_cktso, "include")) - src_files.append("src/CKTSOSolver.cpp") + src_files.append("src/linear_solvers/CKTSOSolver.cpp") extra_compile_args.append("-DCKTSO_SOLVER_AVAILABLE") print("INFO: Using CKTSO package") diff --git a/src/BaseMultiplePowerflow.cpp b/src/BaseMultiplePowerflow.cpp index 6793869..48aac80 100644 --- a/src/BaseMultiplePowerflow.cpp +++ b/src/BaseMultiplePowerflow.cpp @@ -33,8 +33,8 @@ bool BaseMultiplePowerflow::compute_one_powerflow(const Eigen::SparseMatrix reset(); } - // benefit from dynamic stuff and inheritance by having a method that returns a BaseSolver * + // benefit from dynamic stuff and inheritance by having a method that returns a BaseAlgo * bool compute_pf(const Eigen::SparseMatrix & Ybus, // size (nb_bus, nb_bus) CplxVect & V, // size nb_bus const CplxVect & Sbus, // size nb_bus @@ -388,9 +385,9 @@ class ChooseSolver /** returns a pointer to the current solver used **/ - const BaseSolver * get_prt_solver(const std::string & error_msg, bool check_right_solver_=true) const { + const BaseAlgo * get_prt_solver(const std::string & error_msg, bool check_right_solver_=true) const { if (check_right_solver_) check_right_solver(error_msg); - const BaseSolver * res; + const BaseAlgo * res; if(_solver_type == SolverType::SparseLU){res = &_solver_lu;} else if(_solver_type == SolverType::SparseLUSingleSlack){res = &_solver_lu_single;} else if(_solver_type == SolverType::DC){res = &_solver_dc;} @@ -422,9 +419,9 @@ class ChooseSolver else throw std::runtime_error("Unknown solver type encountered (ChooseSolver get_prt_solver const)"); return res; } - BaseSolver * get_prt_solver(const std::string & error_msg, bool check_right_solver_=true) { + BaseAlgo * get_prt_solver(const std::string & error_msg, bool check_right_solver_=true) { if (check_right_solver_) check_right_solver(error_msg); - BaseSolver * res; + BaseAlgo * res; if(_solver_type == SolverType::SparseLU){res = &_solver_lu;} else if(_solver_type == SolverType::SparseLUSingleSlack){res = &_solver_lu_single;} else if(_solver_type == SolverType::DC){res = &_solver_dc;} @@ -464,8 +461,8 @@ class ChooseSolver // TODO have a way to use Union here https://en.cppreference.com/w/cpp/language/union SparseLUSolver _solver_lu; SparseLUSolverSingleSlack _solver_lu_single; - GaussSeidelSolver _solver_gaussseidel; - GaussSeidelSynchSolver _solver_gaussseidelsynch; + GaussSeidelAlgo _solver_gaussseidel; + GaussSeidelSynchAlgo _solver_gaussseidelsynch; DCSolver _solver_dc; FDPF_XB_SparseLUSolver _solver_fdpf_xb_lu; FDPF_BX_SparseLUSolver _solver_fdpf_bx_lu; diff --git a/src/GridModel.cpp b/src/GridModel.cpp index 85307d3..5f4579a 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -146,21 +146,21 @@ void GridModel::set_state(GridModel::StateRes & my_state) std::vector & bus_status = std::get<7>(my_state); // powerlines - DataLine::StateRes & state_lines = std::get<8>(my_state); + LineContainer::StateRes & state_lines = std::get<8>(my_state); // shunts - DataShunt::StateRes & state_shunts = std::get<9>(my_state); + ShuntContainer::StateRes & state_shunts = std::get<9>(my_state); // trafos - DataTrafo::StateRes & state_trafos = std::get<10>(my_state); + TrafoContainer::StateRes & state_trafos = std::get<10>(my_state); // generators - DataGen::StateRes & state_gens = std::get<11>(my_state); + GeneratorContainer::StateRes & state_gens = std::get<11>(my_state); // loads - DataLoad::StateRes & state_loads = std::get<12>(my_state); + LoadContainer::StateRes & state_loads = std::get<12>(my_state); // static gen - DataSGen::StateRes & state_sgens= std::get<13>(my_state); + SGenContainer::StateRes & state_sgens= std::get<13>(my_state); // storage units - DataLoad::StateRes & state_storages = std::get<14>(my_state); + LoadContainer::StateRes & state_storages = std::get<14>(my_state); // dc lines - DataDCLine::StateRes & state_dc_lines = std::get<15>(my_state); + DCLineContainer::StateRes & state_dc_lines = std::get<15>(my_state); // assign it to this instance @@ -338,7 +338,7 @@ CplxVect GridModel::ac_pf(const CplxVect & Vinit, }; void GridModel::check_solution_q_values_onegen(CplxVect & res, - const DataGen::GenInfo& gen, + const GeneratorContainer::GenInfo& gen, bool check_q_limits) const{ if(check_q_limits) { @@ -626,8 +626,6 @@ void GridModel::init_slack_bus(const CplxVect & Sbus, const std::vector& id_solver_to_me, const Eigen::VectorXi & slack_bus_id_me, Eigen::VectorXi & slack_bus_id_solver){ - - const int nb_bus = static_cast(id_solver_to_me.size()); // slack_bus_id_solver = Eigen::VectorXi::Zero(slack_bus_id_.size()); slack_bus_id_solver = Eigen::VectorXi::Constant(slack_bus_id_me.size(), _deactivated_bus_id); diff --git a/src/GridModel.h b/src/GridModel.h index 19717ba..eee6cc7 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -27,14 +27,14 @@ #include "Eigen/SparseLU" // import data classes -#include "DataGeneric.h" -#include "DataLine.h" -#include "DataShunt.h" -#include "DataTrafo.h" -#include "DataLoad.h" -#include "DataGen.h" -#include "DataSGen.h" -#include "DataDCLine.h" +#include "element_container/GenericContainer.h" +#include "element_container/LineContainer.h" +#include "element_container/ShuntContainer.h" +#include "element_container/TrafoContainer.h" +#include "element_container/LoadContainer.h" +#include "element_container/GeneratorContainer.h" +#include "element_container/SGenContainer.h" +#include "element_container/DCLineContainer.h" // import newton raphson solvers using different linear algebra solvers #include "ChooseSolver.h" @@ -42,7 +42,7 @@ // enum class SolverType; //TODO implement a BFS check to make sure the Ymatrix is "connected" [one single component] -class GridModel : public DataGeneric +class GridModel : public GenericContainer { public: typedef std::tuple< @@ -55,21 +55,21 @@ class GridModel : public DataGeneric std::vector, // bus_vn_kv std::vector, // bus_status // powerlines - DataLine::StateRes , + LineContainer::StateRes , // shunts - DataShunt::StateRes, + ShuntContainer::StateRes, // trafos - DataTrafo::StateRes, + TrafoContainer::StateRes, // gens - DataGen::StateRes, + GeneratorContainer::StateRes, // loads - DataLoad::StateRes, + LoadContainer::StateRes, // static generators - DataSGen::StateRes, + SGenContainer::StateRes, // storage units - DataLoad::StateRes, + LoadContainer::StateRes, //dc lines - DataDCLine::StateRes + DCLineContainer::StateRes > StateRes; GridModel(): @@ -101,7 +101,7 @@ class GridModel : public DataGeneric const std::vector & id_dc_solver_to_me() const {return id_dc_solver_to_me_;} // retrieve the underlying data (raw class) - const DataGen & get_generators_as_data() const {return generators_;} + const GeneratorContainer & get_generators_as_data() const {return generators_;} void turnedoff_no_pv(){generators_.turnedoff_no_pv();} // turned off generators are not pv void turnedoff_pv(){generators_.turnedoff_pv();} // turned off generators are pv bool get_turnedoff_gen_pv() {return generators_.get_turnedoff_gen_pv();} @@ -109,11 +109,11 @@ class GridModel : public DataGeneric generators_.update_slack_weights(could_be_slack, solver_control_); } - const DataSGen & get_static_generators_as_data() const {return sgens_;} - const DataLoad & get_loads_as_data() const {return loads_;} - const DataLine & get_powerlines_as_data() const {return powerlines_;} - const DataTrafo & get_trafos_as_data() const {return trafos_;} - const DataDCLine & get_dclines_as_data() const {return dc_lines_;} + const SGenContainer & get_static_generators_as_data() const {return sgens_;} + const LoadContainer & get_loads_as_data() const {return loads_;} + const LineContainer & get_powerlines_as_data() const {return powerlines_;} + const TrafoContainer & get_trafos_as_data() const {return trafos_;} + const DCLineContainer & get_dclines_as_data() const {return dc_lines_;} Eigen::Ref get_bus_vn_kv() const {return bus_vn_kv_;} std::tuple assign_slack_to_most_connected(); void consider_only_main_component(); @@ -271,7 +271,7 @@ class GridModel : public DataGeneric void tell_recompute_sbus(){solver_control_.tell_recompute_sbus();} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. void tell_solver_need_reset(){solver_control_.tell_solver_need_reset();} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. void tell_ybus_change_sparsity_pattern(){solver_control_.tell_ybus_change_sparsity_pattern();} //should be used after the powerflow as run, so some vectors will not be recomputed if not needed. - const SolverControl & get_solver_control() const { return solver_control_;} + const SolverControl & get_solver_control() const {return solver_control_;} // dc powerflow CplxVect dc_pf(const CplxVect & Vinit, @@ -317,14 +317,14 @@ class GridModel : public DataGeneric Eigen::Index nb_trafo() const {return trafos_.nb();} // read only data accessor - const DataLine & get_lines() const {return powerlines_;} - const DataDCLine & get_dclines() const {return dc_lines_;} - const DataTrafo & get_trafos() const {return trafos_;} - const DataGen & get_generators() const {return generators_;} - const DataLoad & get_loads() const {return loads_;} - const DataLoad & get_storages() const {return storages_;} - const DataSGen & get_static_generators() const {return sgens_;} - const DataShunt & get_shunts() const {return shunts_;} + const LineContainer & get_lines() const {return powerlines_;} + const DCLineContainer & get_dclines() const {return dc_lines_;} + const TrafoContainer & get_trafos() const {return trafos_;} + const GeneratorContainer & get_generators() const {return generators_;} + const LoadContainer & get_loads() const {return loads_;} + const LoadContainer & get_storages() const {return storages_;} + const SGenContainer & get_static_generators() const {return sgens_;} + const ShuntContainer & get_shunts() const {return shunts_;} const std::vector & get_bus_status() const {return bus_status_;} void set_line_names(const std::vector & names){ @@ -740,7 +740,7 @@ class GridModel : public DataGeneric int size); void check_solution_q_values( CplxVect & res, bool check_q_limits) const; - void check_solution_q_values_onegen(CplxVect & res, const DataGen::GenInfo& gen, bool check_q_limits) const; + void check_solution_q_values_onegen(CplxVect & res, const GeneratorContainer::GenInfo& gen, bool check_q_limits) const; protected: // memory for the import @@ -776,35 +776,35 @@ class GridModel : public DataGeneric std::vector id_dc_solver_to_me_; // 2. powerline - DataLine powerlines_; + LineContainer powerlines_; // 3. shunt - DataShunt shunts_; + ShuntContainer shunts_; // 4. transformers // have the r, x, h and ratio // ratio is computed from the tap, so maybe store tap num and tap_step_pct - DataTrafo trafos_; + TrafoContainer trafos_; // 5. generators RealVect total_q_min_per_bus_; RealVect total_q_max_per_bus_; Eigen::VectorXi total_gen_per_bus_; - DataGen generators_; + GeneratorContainer generators_; // 6. loads - DataLoad loads_; + LoadContainer loads_; - // 6. static generators (P,Q generators) - DataSGen sgens_; + // 7. static generators (P,Q generators) + SGenContainer sgens_; - // 7. storage units - DataLoad storages_; + // 8. storage units + LoadContainer storages_; - // hvdc - DataDCLine dc_lines_; + // 9. hvdc + DCLineContainer dc_lines_; - // 8. slack bus + // 10. slack bus // std::vector slack_bus_id_; Eigen::VectorXi slack_bus_id_ac_me_; // slack bus id, gridmodel number Eigen::VectorXi slack_bus_id_ac_solver_; // slack bus id, solver number diff --git a/src/SecurityAnalysis.cpp b/src/SecurityAnalysis.cpp index f4d5c9a..ccab406 100644 --- a/src/SecurityAnalysis.cpp +++ b/src/SecurityAnalysis.cpp @@ -7,6 +7,7 @@ // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. #include "SecurityAnalysis.h" + #include #include /* isfinite */ @@ -89,7 +90,7 @@ void SecurityAnalysis::init_li_coeffs(bool ac_solver_used){ } } - if(bus_1_id != DataGeneric::_deactivated_bus_id && bus_2_id != DataGeneric::_deactivated_bus_id) + if(bus_1_id != GenericContainer::_deactivated_bus_id && bus_2_id != GenericContainer::_deactivated_bus_id) { // element is connected this_cont_coeffs.push_back({bus_1_id, bus_1_id, y_ff}); diff --git a/src/Solvers.cpp b/src/Solvers.cpp index eed71c9..9cd8060 100644 --- a/src/Solvers.cpp +++ b/src/Solvers.cpp @@ -13,7 +13,7 @@ // this is why i need to define them here for every specialization. template -void BaseFDPFSolver::fillBp_Bpp(Eigen::SparseMatrix & Bp, Eigen::SparseMatrix & Bpp) const +void BaseFDPFAlgo::fillBp_Bpp(Eigen::SparseMatrix & Bp, Eigen::SparseMatrix & Bpp) const { _gridmodel->fillBp_Bpp(Bp, Bpp, XB_BX); } diff --git a/src/Solvers.h b/src/Solvers.h index 4adaac8..705be7e 100644 --- a/src/Solvers.h +++ b/src/Solvers.h @@ -6,36 +6,39 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "BaseNRSolver.h" -#include "BaseNRSolverSingleSlack.h" -#include "DCSolver.h" -#include "BaseFDPFSolver.h" -#include "SparseLUSolver.h" -#include "KLUSolver.h" -#include "NICSLUSolver.h" -#include "CKTSOSolver.h" +#include "powerflow_algorithm/BaseDCAlgo.h" +#include "powerflow_algorithm/BaseNRAlgo.h" +#include "powerflow_algorithm/BaseNRSingleSlackAlgo.h" +#include "powerflow_algorithm/BaseFDPFAlgo.h" +#include "powerflow_algorithm/GaussSeidelSynchAlgo.h" +#include "powerflow_algorithm/GaussSeidelAlgo.h" + +#include "linear_solvers/SparseLUSolver.h" +#include "linear_solvers/KLUSolver.h" +#include "linear_solvers/NICSLUSolver.h" +#include "linear_solvers/CKTSOSolver.h" /** Solver based on Newton Raphson, using the SparseLU decomposition of Eigen**/ -typedef BaseNRSolver SparseLUSolver; +typedef BaseNRAlgo SparseLUSolver; /** Solver based on Newton Raphson, using the SparseLU decomposition of Eigen, do not consider multiple slack bus**/ -typedef BaseNRSolverSingleSlack SparseLUSolverSingleSlack; +typedef BaseNRSingleSlackAlgo SparseLUSolverSingleSlack; /** Solver based on Newton Raphson, using the SparseLU decomposition of Eigen, only suitable for the DC approximation**/ -typedef BaseDCSolver DCSolver; +typedef BaseDCAlgo DCSolver; /** Solver based on Fast Decoupled, using the SparseLU decomposition of Eigen**/ -typedef BaseFDPFSolver FDPF_XB_SparseLUSolver; -typedef BaseFDPFSolver FDPF_BX_SparseLUSolver; +typedef BaseFDPFAlgo FDPF_XB_SparseLUSolver; +typedef BaseFDPFAlgo FDPF_BX_SparseLUSolver; #ifdef KLU_SOLVER_AVAILABLE /** Solver based on Newton Raphson, using the KLU linear solver**/ - typedef BaseNRSolver KLUSolver; + typedef BaseNRAlgo KLUSolver; /** Solver based on Newton Raphson, using the KLU linear solver, do not consider multiple slack bus**/ - typedef BaseNRSolverSingleSlack KLUSolverSingleSlack; + typedef BaseNRSingleSlackAlgo KLUSolverSingleSlack; /** Solver based on Newton Raphson, using the KLU linear solver, only suitable for the DC approximation**/ - typedef BaseDCSolver KLUDCSolver; + typedef BaseDCAlgo KLUDCSolver; /** Solver based on Fast Decoupled, using the KLU linear solver**/ - typedef BaseFDPFSolver FDPF_XB_KLUSolver; - typedef BaseFDPFSolver FDPF_BX_KLUSolver; + typedef BaseFDPFAlgo FDPF_XB_KLUSolver; + typedef BaseFDPFAlgo FDPF_BX_KLUSolver; #elif defined(_READ_THE_DOCS) // hack to display accurately the doc in read the doc even if the models are not compiled /** Solver based on Newton Raphson, using the KLU linear solver**/ @@ -51,14 +54,14 @@ typedef BaseFDPFSolver FDPF_BX_SparseLUSol #ifdef NICSLU_SOLVER_AVAILABLE /** Solver based on Newton Raphson, using the NICSLU linear solver (needs a specific license)**/ - typedef BaseNRSolver NICSLUSolver; + typedef BaseNRAlgo NICSLUSolver; /** Solver based on Newton Raphson, using the NICSLU linear solver (needs a specific license), do not consider multiple slack bus**/ - typedef BaseNRSolverSingleSlack NICSLUSolverSingleSlack; + typedef BaseNRSingleSlackAlgo NICSLUSolverSingleSlack; /** Solver based on Newton Raphson, using the NICSLU linear solver (needs a specific license), only suitable for the DC approximation**/ - typedef BaseDCSolver NICSLUDCSolver; + typedef BaseDCAlgo NICSLUDCSolver; /** Solver based on Fast Decoupled, using the NICSLU linear solver (needs a specific license)**/ - typedef BaseFDPFSolver FDPF_XB_NICSLUSolver; - typedef BaseFDPFSolver FDPF_BX_NICSLUSolver; + typedef BaseFDPFAlgo FDPF_XB_NICSLUSolver; + typedef BaseFDPFAlgo FDPF_BX_NICSLUSolver; #elif defined(_READ_THE_DOCS) // hack to display accurately the doc in read the doc even if the models are not compiled /** Solver based on Newton Raphson, using the NICSLU linear solver (needs a specific license)**/ @@ -74,14 +77,14 @@ typedef BaseFDPFSolver FDPF_BX_SparseLUSol #ifdef CKTSO_SOLVER_AVAILABLE /** Solver based on Newton Raphson, using the CKTSO linear solver (needs a specific license)**/ - typedef BaseNRSolver CKTSOSolver; + typedef BaseNRAlgo CKTSOSolver; /** Solver based on Newton Raphson, using the CKTSO linear solver (needs a specific license), do not consider multiple slack bus**/ - typedef BaseNRSolverSingleSlack CKTSOSolverSingleSlack; + typedef BaseNRSingleSlackAlgo CKTSOSolverSingleSlack; /** Solver based on Newton Raphson, using the CKTSO linear solver (needs a specific license), only suitable for the DC approximation**/ - typedef BaseDCSolver CKTSODCSolver; + typedef BaseDCAlgo CKTSODCSolver; /** Solver based on Fast Decoupled, using the CKTSO linear solver (needs a specific license)**/ - typedef BaseFDPFSolver FDPF_XB_CKTSOSolver; - typedef BaseFDPFSolver FDPF_BX_CKTSOSolver; + typedef BaseFDPFAlgo FDPF_XB_CKTSOSolver; + typedef BaseFDPFAlgo FDPF_BX_CKTSOSolver; #elif defined(_READ_THE_DOCS) // hack to display accurately the doc in read the doc even if the models are not compiled /** Solver based on Newton Raphson, using the CKTSO linear solver (needs a specific license)**/ diff --git a/src/DataDCLine.cpp b/src/element_container/DCLineContainer.cpp similarity index 88% rename from src/DataDCLine.cpp rename to src/element_container/DCLineContainer.cpp index c74301b..716229b 100644 --- a/src/DataDCLine.cpp +++ b/src/element_container/DCLineContainer.cpp @@ -6,16 +6,17 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "DataDCLine.h" +#include "DCLineContainer.h" + #include #include -DataDCLine::StateRes DataDCLine::get_state() const +DCLineContainer::StateRes DCLineContainer::get_state() const { std::vector loss_percent(loss_percent_.begin(), loss_percent_.end()); std::vector loss_mw(loss_mw_.begin(), loss_mw_.end()); std::vector status = status_; - DataDCLine::StateRes res(names_, + DCLineContainer::StateRes res(names_, from_gen_.get_state(), to_gen_.get_state(), loss_percent, @@ -24,7 +25,7 @@ DataDCLine::StateRes DataDCLine::get_state() const return res; } -void DataDCLine::set_state(DataDCLine::StateRes & my_state){ +void DCLineContainer::set_state(DCLineContainer::StateRes & my_state){ reset_results(); names_ = std::get<0>(my_state); from_gen_.set_state(std::get<1>(my_state)); @@ -37,7 +38,7 @@ void DataDCLine::set_state(DataDCLine::StateRes & my_state){ loss_mw_ = RealVect::Map(&loss_mw[0], loss_percent.size()); } -void DataDCLine::init(const Eigen::VectorXi & branch_from_id, +void DCLineContainer::init(const Eigen::VectorXi & branch_from_id, const Eigen::VectorXi & branch_to_id, const RealVect & p_mw, const RealVect & loss_percent, @@ -61,7 +62,7 @@ void DataDCLine::init(const Eigen::VectorXi & branch_from_id, to_gen_.init(p_ex, vm_ex_pu, min_q_ex, max_q_ex, branch_to_id); } -void DataDCLine::nb_line_end(std::vector & res) const +void DCLineContainer::nb_line_end(std::vector & res) const { const Eigen::Index nb = from_gen_.nb(); const auto & bus_or_id = get_bus_id_or(); @@ -76,7 +77,7 @@ void DataDCLine::nb_line_end(std::vector & res) const } // TODO DC LINE: one side might be in the connected comp and not the other ! -void DataDCLine::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component) +void DCLineContainer::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component) { const Eigen::Index nb = from_gen_.nb(); const auto & bus_or_id = get_bus_id_or(); diff --git a/src/DataDCLine.h b/src/element_container/DCLineContainer.h similarity index 92% rename from src/DataDCLine.h rename to src/element_container/DCLineContainer.h index b9363d2..14abf21 100644 --- a/src/DataDCLine.h +++ b/src/element_container/DCLineContainer.h @@ -6,23 +6,22 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DATADCLINE_H -#define DATADCLINE_H +#ifndef DCLINECONTAINER_H +#define DCLINECONTAINER_H #include -#include "Utils.h" - #include "Eigen/Core" #include "Eigen/Dense" #include "Eigen/SparseCore" #include "Eigen/SparseLU" -#include "DataGeneric.h" -#include "DataGen.h" +#include "Utils.h" +#include "GenericContainer.h" +#include "GeneratorContainer.h" -class DataDCLine : public DataGeneric +class DCLineContainer : public GenericContainer { public: class DCLineInfo @@ -39,8 +38,8 @@ class DataDCLine : public DataGeneric real_type target_vm_ex_pu; real_type loss_pct; real_type loss_mw; - DataGen::GenInfo gen_or; - DataGen::GenInfo gen_ex; + GeneratorContainer::GenInfo gen_or; + GeneratorContainer::GenInfo gen_ex; bool has_res; real_type res_p_or_mw; @@ -52,7 +51,7 @@ class DataDCLine : public DataGeneric real_type res_v_ex_kv; real_type res_theta_ex_deg; - DCLineInfo(const DataDCLine & r_data_dcline, int my_id): + DCLineInfo(const DCLineContainer & r_data_dcline, int my_id): id(-1), name(""), connected(false), @@ -109,13 +108,13 @@ class DataDCLine : public DataGeneric typedef DCLineInfo DataInfo; private: - typedef DataConstIterator DataDCLineConstIterator; + typedef GenericContainerConstIterator DCLineConstIterator; public: typedef std::tuple< std::vector, - DataGen::StateRes, - DataGen::StateRes, + GeneratorContainer::StateRes, + GeneratorContainer::StateRes, std::vector, // loss_percent std::vector, // vm_to_pu std::vector // loss_mw @@ -124,9 +123,9 @@ class DataDCLine : public DataGeneric int nb() const { return static_cast(from_gen_.nb()); } // iterator - typedef DataDCLineConstIterator const_iterator_type; - const_iterator_type begin() const {return DataDCLineConstIterator(this, 0); } - const_iterator_type end() const {return DataDCLineConstIterator(this, nb()); } + typedef DCLineConstIterator const_iterator_type; + const_iterator_type begin() const {return DCLineConstIterator(this, 0); } + const_iterator_type end() const {return DCLineConstIterator(this, nb()); } DCLineInfo operator[](int id) const { if(id < 0) @@ -141,11 +140,11 @@ class DataDCLine : public DataGeneric } // underlying generators are not pv when powerline is off - DataDCLine(): from_gen_(false), to_gen_(false) {}; + DCLineContainer(): from_gen_(false), to_gen_(false) {}; // pickle - DataDCLine::StateRes get_state() const; - void set_state(DataDCLine::StateRes & my_state); + DCLineContainer::StateRes get_state() const; + void set_state(DCLineContainer::StateRes & my_state); // TODO min_p, max_p void init(const Eigen::VectorXi & branch_from_id, @@ -297,12 +296,12 @@ class DataDCLine : public DataGeneric protected: // it is modeled as 2 generators that are "linked" together // see https://pandapower.readthedocs.io/en/v2.0.1/elements/dcline.html#electric-model - DataGen from_gen_; - DataGen to_gen_; + GeneratorContainer from_gen_; + GeneratorContainer to_gen_; RealVect loss_percent_; RealVect loss_mw_; std::vector status_; }; -#endif //DATADCLINE_H \ No newline at end of file +#endif //DCLINECONTAINER_H \ No newline at end of file diff --git a/src/DataGen.cpp b/src/element_container/GeneratorContainer.cpp similarity index 84% rename from src/DataGen.cpp rename to src/element_container/GeneratorContainer.cpp index 973f92e..254813f 100644 --- a/src/DataGen.cpp +++ b/src/element_container/GeneratorContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,11 +6,11 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "DataGen.h" +#include "GeneratorContainer.h" #include #include -void DataGen::init(const RealVect & generators_p, +void GeneratorContainer::init(const RealVect & generators_p, const RealVect & generators_v, const RealVect & generators_min_q, const RealVect & generators_max_q, @@ -24,7 +24,7 @@ void DataGen::init(const RealVect & generators_p, if(min_q_.size() != max_q_.size()) { std::ostringstream exc_; - exc_ << "DataGen::init: Impossible to initialize generator with generators_min_q of size "; + exc_ << "GeneratorContainer::init: Impossible to initialize generator with generators_min_q of size "; exc_ << min_q_.size(); exc_ << " and generators_max_q of size "; exc_ << max_q_.size(); @@ -36,7 +36,7 @@ void DataGen::init(const RealVect & generators_p, if (min_q_(gen_id) > max_q_(gen_id)) { std::ostringstream exc_; - exc_ << "DataGen::init: Impossible to initialize generator min_q being above max_q for generator "; + exc_ << "GeneratorContainer::init: Impossible to initialize generator min_q being above max_q for generator "; exc_ << gen_id; throw std::runtime_error(exc_.str()); } @@ -49,7 +49,7 @@ void DataGen::init(const RealVect & generators_p, q_mvar_ = RealVect::Zero(generators_p.size()); } -void DataGen::init_full(const RealVect & generators_p, +void GeneratorContainer::init_full(const RealVect & generators_p, const RealVect & generators_v, const RealVect & generators_q, const std::vector & voltage_regulator_on, @@ -64,7 +64,7 @@ void DataGen::init_full(const RealVect & generators_p, } -DataGen::StateRes DataGen::get_state() const +GeneratorContainer::StateRes GeneratorContainer::get_state() const { std::vector p_mw(p_mw_.begin(), p_mw_.end()); std::vector vm_pu(vm_pu_.begin(), vm_pu_.end()); @@ -76,13 +76,13 @@ DataGen::StateRes DataGen::get_state() const std::vector slack_bus = gen_slackbus_; std::vector voltage_regulator_on = voltage_regulator_on_; std::vector slack_weight = gen_slack_weight_; - DataGen::StateRes res(names_, turnedoff_gen_pv_, voltage_regulator_on, + GeneratorContainer::StateRes res(names_, turnedoff_gen_pv_, voltage_regulator_on, p_mw, vm_pu, q_mvar, min_q, max_q, bus_id, status, slack_bus, slack_weight); return res; } -void DataGen::set_state(DataGen::StateRes & my_state) +void GeneratorContainer::set_state(GeneratorContainer::StateRes & my_state) { reset_results(); names_ = std::get<0>(my_state); @@ -114,7 +114,7 @@ void DataGen::set_state(DataGen::StateRes & my_state) gen_slack_weight_ = slack_weight; } -RealVect DataGen::get_slack_weights(Eigen::Index nb_bus_solver, const std::vector & id_grid_to_solver){ +RealVect GeneratorContainer::get_slack_weights(Eigen::Index nb_bus_solver, const std::vector & id_grid_to_solver){ const int nb_gen = nb(); int bus_id_me, bus_id_solver; RealVect res = RealVect::Zero(nb_bus_solver); @@ -126,7 +126,7 @@ RealVect DataGen::get_slack_weights(Eigen::Index nb_bus_solver, const std::vecto if(bus_id_solver == _deactivated_bus_id){ // TODO DEBUG MODE: only check in debug mode std::ostringstream exc_; - exc_ << "DataGen::get_slack_weights: Generator with id "; + exc_ << "GeneratorContainer::get_slack_weights: Generator with id "; exc_ << gen_id; exc_ << " is connected to a disconnected bus while being connected to the grid."; throw std::runtime_error(exc_.str()); @@ -139,7 +139,7 @@ RealVect DataGen::get_slack_weights(Eigen::Index nb_bus_solver, const std::vecto return res; } -void DataGen::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const { +void GeneratorContainer::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const { const int nb_gen = nb(); int bus_id_me, bus_id_solver; cplx_type tmp; @@ -152,7 +152,7 @@ void DataGen::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solv if(bus_id_solver == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataGen::fillSbus: Generator with id "; + exc_ << "GeneratorContainer::fillSbus: Generator with id "; exc_ << gen_id; exc_ << " is connected to a disconnected bus while being connected to the grid."; throw std::runtime_error(exc_.str()); @@ -166,7 +166,7 @@ void DataGen::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solv } } -void DataGen::fillpv(std::vector & bus_pv, +void GeneratorContainer::fillpv(std::vector & bus_pv, std::vector & has_bus_been_added, const Eigen::VectorXi & slack_bus_id_solver, const std::vector & id_grid_to_solver) const @@ -184,7 +184,7 @@ void DataGen::fillpv(std::vector & bus_pv, if(bus_id_solver == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataGen::fillpv: Generator with id "; + exc_ << "GeneratorContainer::fillpv: Generator with id "; exc_ << gen_id; exc_ << " is connected to a disconnected bus while being connected to the grid."; throw std::runtime_error(exc_.str()); @@ -197,7 +197,7 @@ void DataGen::fillpv(std::vector & bus_pv, } } -void DataGen::compute_results(const Eigen::Ref & Va, +void GeneratorContainer::compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, const Eigen::Ref & V, const std::vector & id_grid_to_solver, @@ -211,7 +211,7 @@ void DataGen::compute_results(const Eigen::Ref & Va, res_p_ = p_mw_; } -void DataGen::reset_results(){ +void GeneratorContainer::reset_results(){ res_p_ = RealVect(); // in MW res_q_ = RealVect(); // in MVar res_v_ = RealVect(); // in kV @@ -219,7 +219,7 @@ void DataGen::reset_results(){ // bus_slack_weight_ = RealVect(); } -void DataGen::get_vm_for_dc(RealVect & Vm){ +void GeneratorContainer::get_vm_for_dc(RealVect & Vm){ const int nb_gen = nb(); int bus_id_me; for(int gen_id = 0; gen_id < nb_gen; ++gen_id){ @@ -235,14 +235,14 @@ void DataGen::get_vm_for_dc(RealVect & Vm){ } } -void DataGen::change_p(int gen_id, real_type new_p, SolverControl & solver_control) +void GeneratorContainer::change_p(int gen_id, real_type new_p, SolverControl & solver_control) { bool my_status = status_.at(gen_id); // and this check that load_id is not out of bound if(!my_status) { // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataGen::change_p: Impossible to change the active value of a disconnected generator (check gen. id "; + exc_ << "GeneratorContainer::change_p: Impossible to change the active value of a disconnected generator (check gen. id "; exc_ << gen_id; exc_ << ")"; throw std::runtime_error(exc_.str()); @@ -261,14 +261,14 @@ void DataGen::change_p(int gen_id, real_type new_p, SolverControl & solver_contr p_mw_(gen_id) = new_p; } -void DataGen::change_q(int gen_id, real_type new_q, SolverControl & solver_control) +void GeneratorContainer::change_q(int gen_id, real_type new_q, SolverControl & solver_control) { bool my_status = status_.at(gen_id); // and this check that load_id is not out of bound if(!my_status) { // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataGen::change_q: Impossible to change the reactive value of a disconnected generator (check gen. id "; + exc_ << "GeneratorContainer::change_q: Impossible to change the reactive value of a disconnected generator (check gen. id "; exc_ << gen_id; exc_ << ")"; throw std::runtime_error(exc_.str()); @@ -279,14 +279,14 @@ void DataGen::change_q(int gen_id, real_type new_q, SolverControl & solver_contr q_mvar_(gen_id) = new_q; } -void DataGen::change_v(int gen_id, real_type new_v_pu, SolverControl & solver_control) +void GeneratorContainer::change_v(int gen_id, real_type new_v_pu, SolverControl & solver_control) { bool my_status = status_.at(gen_id); // and this check that load_id is not out of bound if(!my_status) { // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataGen::change_p: Impossible to change the voltage setpoint of a disconnected generator (check gen. id "; + exc_ << "GeneratorContainer::change_p: Impossible to change the voltage setpoint of a disconnected generator (check gen. id "; exc_ << gen_id; exc_ << ")"; throw std::runtime_error(exc_.str()); @@ -295,7 +295,7 @@ void DataGen::change_v(int gen_id, real_type new_v_pu, SolverControl & solver_co vm_pu_(gen_id) = new_v_pu; } -void DataGen::set_vm(CplxVect & V, const std::vector & id_grid_to_solver) const +void GeneratorContainer::set_vm(CplxVect & V, const std::vector & id_grid_to_solver) const { const int nb_gen = nb(); int bus_id_me, bus_id_solver; @@ -311,7 +311,7 @@ void DataGen::set_vm(CplxVect & V, const std::vector & id_grid_to_solver) c if(bus_id_solver == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataGen::set_vm: Generator with id "; + exc_ << "GeneratorContainer::set_vm: Generator with id "; exc_ << gen_id; exc_ << " is connected to a disconnected bus while being connected to the grid."; throw std::runtime_error(exc_.str()); @@ -331,7 +331,7 @@ void DataGen::set_vm(CplxVect & V, const std::vector & id_grid_to_solver) c } } -Eigen::VectorXi DataGen::get_slack_bus_id() const{ +Eigen::VectorXi GeneratorContainer::get_slack_bus_id() const{ std::vector tmp; tmp.reserve(gen_slackbus_.size()); Eigen::VectorXi res; @@ -343,17 +343,17 @@ Eigen::VectorXi DataGen::get_slack_bus_id() const{ if(!is_in_vect(my_bus, tmp)) tmp.push_back(my_bus); } } - if(tmp.empty()) throw std::runtime_error("DataGen::get_slack_bus_id: no generator are tagged slack bus for this grid."); + if(tmp.empty()) throw std::runtime_error("GeneratorContainer::get_slack_bus_id: no generator are tagged slack bus for this grid."); res = Eigen::VectorXi::Map(tmp.data(), tmp.size()); // force the copy of the data apparently return res; } -void DataGen::set_p_slack(const RealVect& node_mismatch, +void GeneratorContainer::set_p_slack(const RealVect& node_mismatch, const std::vector & id_grid_to_solver) { if(bus_slack_weight_.size() == 0){ // TODO DEBUG MODE: perform this check only in debug mode - throw std::runtime_error("DataGen::set_p_slack: Impossible to set the active value of generators for the slack bus: no known slack (you should haved called DataGen::get_slack_weights first)"); + throw std::runtime_error("Generator::set_p_slack: Impossible to set the active value of generators for the slack bus: no known slack (you should haved called Generator::get_slack_weights first)"); } const auto nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id){ @@ -372,7 +372,7 @@ void DataGen::set_p_slack(const RealVect& node_mismatch, } } -void DataGen::init_q_vector(int nb_bus, +void GeneratorContainer::init_q_vector(int nb_bus, Eigen::VectorXi & total_gen_per_bus, RealVect & total_q_min_per_bus, RealVect & total_q_max_per_bus) const // delta_q_per_gen_) // total number of bus on the grid @@ -392,7 +392,7 @@ void DataGen::init_q_vector(int nb_bus, } } -void DataGen::set_q(const RealVect & reactive_mismatch, +void GeneratorContainer::set_q(const RealVect & reactive_mismatch, const std::vector & id_grid_to_solver, bool ac, const Eigen::VectorXi & total_gen_per_bus, @@ -431,7 +431,7 @@ void DataGen::set_q(const RealVect & reactive_mismatch, } -void DataGen::update_slack_weights(Eigen::Ref > could_be_slack, +void GeneratorContainer::update_slack_weights(Eigen::Ref > could_be_slack, SolverControl & solver_control) { const int nb_gen = nb(); @@ -456,7 +456,7 @@ void DataGen::update_slack_weights(Eigen::Ref & bus_status) const { +void GeneratorContainer::reconnect_connected_buses(std::vector & bus_status) const { const int nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id) { @@ -465,7 +465,7 @@ void DataGen::reconnect_connected_buses(std::vector & bus_status) const { if(my_bus == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataGen::reconnect_connected_buses: Generator with id "; + exc_ << "Generator::reconnect_connected_buses: Generator with id "; exc_ << gen_id; exc_ << " is connected to bus '-1' (meaning disconnected) while you said it was disconnected. Have you called `gridmodel.deactivate_gen(...)` ?."; throw std::runtime_error(exc_.str()); @@ -474,7 +474,7 @@ void DataGen::reconnect_connected_buses(std::vector & bus_status) const { } } -void DataGen::gen_p_per_bus(std::vector & res) const +void GeneratorContainer::gen_p_per_bus(std::vector & res) const { const int nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id) @@ -485,7 +485,7 @@ void DataGen::gen_p_per_bus(std::vector & res) const } } -void DataGen::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component){ +void GeneratorContainer::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component){ const int nb_gen = nb(); SolverControl unused_solver_control; for(int gen_id = 0; gen_id < nb_gen; ++gen_id) diff --git a/src/DataGen.h b/src/element_container/GeneratorContainer.h similarity index 92% rename from src/DataGen.h rename to src/element_container/GeneratorContainer.h index 0f531cb..d9b854d 100644 --- a/src/DataGen.h +++ b/src/element_container/GeneratorContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,20 +6,19 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DATAGEN_H -#define DATAGEN_H +#ifndef GENERATORCONTAINER_H +#define GENERATORCONTAINER_H #include #include -#include "Utils.h" - #include "Eigen/Core" #include "Eigen/Dense" #include "Eigen/SparseCore" #include "Eigen/SparseLU" -#include "DataGeneric.h" +#include "Utils.h" +#include "GenericContainer.h" /** This class represents the list of all generators. @@ -30,7 +29,7 @@ The convention used for the generator is the same as in pandapower: and for modeling of the Ybus matrix: https://pandapower.readthedocs.io/en/latest/elements/gen.html#electric-model **/ -class DataGen: public DataGeneric +class GeneratorContainer: public GenericContainer { public: class GenInfo @@ -57,7 +56,7 @@ class DataGen: public DataGeneric real_type res_v_kv; real_type res_theta_deg; - GenInfo(const DataGen & r_data_gen, int my_id): + GenInfo(const GeneratorContainer & r_data_gen, int my_id): id(-1), name(""), connected(false), @@ -108,7 +107,7 @@ class DataGen: public DataGeneric typedef GenInfo DataInfo; private: - typedef DataConstIterator DataGenConstIterator; + typedef GenericContainerConstIterator GeneratorConstIterator; public: typedef std::tuple< @@ -126,8 +125,8 @@ class DataGen: public DataGeneric std::vector // gen_slack_weight_ > StateRes; - DataGen():turnedoff_gen_pv_(true){}; - DataGen(bool turnedoff_gen_pv):turnedoff_gen_pv_(turnedoff_gen_pv) {}; + GeneratorContainer():turnedoff_gen_pv_(true){}; + GeneratorContainer(bool turnedoff_gen_pv):turnedoff_gen_pv_(turnedoff_gen_pv) {}; // TODO add pmin and pmax here ! void init(const RealVect & generators_p, @@ -149,9 +148,9 @@ class DataGen: public DataGeneric int nb() const { return static_cast(p_mw_.size()); } // iterator - typedef DataGenConstIterator const_iterator_type; - const_iterator_type begin() const {return DataGenConstIterator(this, 0); } - const_iterator_type end() const {return DataGenConstIterator(this, nb()); } + typedef GeneratorConstIterator const_iterator_type; + const_iterator_type begin() const {return GeneratorConstIterator(this, 0); } + const_iterator_type end() const {return GeneratorConstIterator(this, nb()); } GenInfo operator[](int id) const { if(id < 0) @@ -166,8 +165,8 @@ class DataGen: public DataGeneric } // pickle - DataGen::StateRes get_state() const; - void set_state(DataGen::StateRes & my_state ); + GeneratorContainer::StateRes get_state() const; + void set_state(GeneratorContainer::StateRes & my_state ); // slack handling /** @@ -176,7 +175,7 @@ class DataGen: public DataGeneric **/ void add_slackbus(int gen_id, real_type weight, SolverControl & solver_control){ // TODO DEBUG MODE - if(weight <= 0.) throw std::runtime_error("DataGen::add_slackbus Cannot assign a negative weight to the slack bus."); + if(weight <= 0.) throw std::runtime_error("GeneratorContainer::add_slackbus Cannot assign a negative weight to the slack bus."); if(!gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); gen_slackbus_[gen_id] = true; if(gen_slack_weight_[gen_id] != weight) solver_control.tell_slack_weight_changed(); @@ -212,7 +211,7 @@ class DataGen: public DataGeneric } } // TODO DEBUG MODE - if(res_gen_id == -1) throw std::runtime_error("DataGen::assign_slack_bus No generator connected to the desired buses"); + if(res_gen_id == -1) throw std::runtime_error("GeneratorContainer::assign_slack_bus No generator connected to the desired buses"); return res_gen_id; } @@ -348,4 +347,4 @@ class DataGen: public DataGeneric bool turnedoff_gen_pv_; // are turned off generators (including one with p=0) pv ? }; -#endif //DATAGEN_H +#endif //GENERATORCONTAINER_H diff --git a/src/DataGeneric.cpp b/src/element_container/GenericContainer.cpp similarity index 80% rename from src/DataGeneric.cpp rename to src/element_container/GenericContainer.cpp index 3b7c330..6daa4fe 100644 --- a/src/DataGeneric.cpp +++ b/src/element_container/GenericContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,14 +6,15 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "DataGeneric.h" +#include "GenericContainer.h" + #include #include -const int DataGeneric::_deactivated_bus_id = -1; +const int GenericContainer::_deactivated_bus_id = -1; // TODO all functions bellow are generic ! Make a base class for that -void DataGeneric::_get_amps(RealVect & a, const RealVect & p, const RealVect & q, const RealVect & v){ +void GenericContainer::_get_amps(RealVect & a, const RealVect & p, const RealVect & q, const RealVect & v){ const real_type _1_sqrt_3 = 1.0 / std::sqrt(3.); RealVect p2q2 = p.array() * p.array() + q.array() * q.array(); p2q2 = p2q2.array().cwiseSqrt(); @@ -26,22 +27,22 @@ void DataGeneric::_get_amps(RealVect & a, const RealVect & p, const RealVect & q } a = p2q2.array() * _1_sqrt_3 / v_tmp.array(); } -void DataGeneric::_reactivate(int el_id, std::vector & status){ +void GenericContainer::_reactivate(int el_id, std::vector & status){ bool val = status.at(el_id); status.at(el_id) = true; //TODO why it's needed to do that again } -void DataGeneric::_deactivate(int el_id, std::vector & status){ +void GenericContainer::_deactivate(int el_id, std::vector & status){ bool val = status.at(el_id); status.at(el_id) = false; //TODO why it's needed to do that again } -void DataGeneric::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el_bus_ids, SolverControl & solver_control, int nb_bus){ +void GenericContainer::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el_bus_ids, SolverControl & solver_control, int nb_bus){ // bus id here "me_id" and NOT "solver_id" // throw error: object id does not exist if(el_id >= el_bus_ids.size()) { // TODO DEBUG MODE: only check in debug mode std::ostringstream exc_; - exc_ << "DataGeneric::_change_bus: Cannot change the bus of element with id "; + exc_ << "GenericContainer::_change_bus: Cannot change the bus of element with id "; exc_ << el_id; exc_ << " while the grid counts "; exc_ << el_bus_ids.size(); @@ -52,7 +53,7 @@ void DataGeneric::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el { // TODO DEBUG MODE: only check in debug mode std::ostringstream exc_; - exc_ << "DataGeneric::_change_bus: Cannot change the bus of element with id "; + exc_ << "GenericContainer::_change_bus: Cannot change the bus of element with id "; exc_ << el_id; exc_ << " (id should be >= 0)"; throw std::out_of_range(exc_.str()); @@ -63,7 +64,7 @@ void DataGeneric::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el { // TODO DEBUG MODE: only check in debug mode std::ostringstream exc_; - exc_ << "DataGeneric::_change_bus: Cannot change an element to bus "; + exc_ << "GenericContainer::_change_bus: Cannot change an element to bus "; exc_ << new_bus_me_id; exc_ << " There are only "; exc_ << nb_bus; @@ -74,7 +75,7 @@ void DataGeneric::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el { // TODO DEBUG MODE: only check in debug mode std::ostringstream exc_; - exc_ << "DataGeneric::_change_bus: new bus id should be >=0 and not "; + exc_ << "GenericContainer::_change_bus: new bus id should be >=0 and not "; exc_ << new_bus_me_id; throw std::out_of_range(exc_.str()); } @@ -92,7 +93,7 @@ void DataGeneric::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el bus_me_id = new_bus_me_id; } -int DataGeneric::_get_bus(int el_id, const std::vector & status_, const Eigen::VectorXi & bus_id_) +int GenericContainer::_get_bus(int el_id, const std::vector & status_, const Eigen::VectorXi & bus_id_) { int res; bool val = status_.at(el_id); // also check if the el_id is out of bound @@ -103,7 +104,7 @@ int DataGeneric::_get_bus(int el_id, const std::vector & status_, const Ei return res; } -void DataGeneric::v_kv_from_vpu(const Eigen::Ref & Va, +void GenericContainer::v_kv_from_vpu(const Eigen::Ref & Va, const Eigen::Ref & Vm, const std::vector & status, int nb_element, @@ -120,7 +121,7 @@ void DataGeneric::v_kv_from_vpu(const Eigen::Ref & Va, if(bus_solver_id == _deactivated_bus_id){ // TODO DEBUG MODE: only check in debug mode std::ostringstream exc_; - exc_ << "DataGeneric::v_kv_from_vpu: The element of id "; + exc_ << "GenericContainer::v_kv_from_vpu: The element of id "; exc_ << bus_solver_id; exc_ << " is connected to a disconnected bus"; throw std::runtime_error(exc_.str()); @@ -131,7 +132,7 @@ void DataGeneric::v_kv_from_vpu(const Eigen::Ref & Va, } -void DataGeneric::v_deg_from_va(const Eigen::Ref & Va, +void GenericContainer::v_deg_from_va(const Eigen::Ref & Va, const Eigen::Ref & Vm, const std::vector & status, int nb_element, @@ -148,7 +149,7 @@ void DataGeneric::v_deg_from_va(const Eigen::Ref & Va, if(bus_solver_id == _deactivated_bus_id){ // TODO DEBUG MODE: only check in debug mode std::ostringstream exc_; - exc_ << "DataGeneric::v_deg_from_va: The element of id "; + exc_ << "GenericContainer::v_deg_from_va: The element of id "; exc_ << bus_solver_id; exc_ << " is connected to a disconnected bus"; throw std::runtime_error(exc_.str()); diff --git a/src/DataGeneric.h b/src/element_container/GenericContainer.h similarity index 89% rename from src/DataGeneric.h rename to src/element_container/GenericContainer.h index 768cc7d..c0e6da3 100644 --- a/src/DataGeneric.h +++ b/src/element_container/GenericContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,23 +6,22 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DATAGENERIC_H -#define DATAGENERIC_H +#ifndef GENERIC_CONTAINER_H +#define GENERIC_CONTAINER_H #include // for std::find -#include "Utils.h" - #include "Eigen/Core" #include "Eigen/Dense" #include "Eigen/SparseCore" #include "Eigen/SparseLU" +#include "Utils.h" #include "BaseConstants.h" // iterator type template -class DataConstIterator +class GenericContainerConstIterator { protected: typedef typename DataType::DataInfo DataInfo; @@ -34,22 +33,22 @@ class DataConstIterator DataInfo my_info; // functions - DataConstIterator(const DataType * const data_, int id): + GenericContainerConstIterator(const DataType * const data_, int id): _p_data_(data_), my_id(id), my_info(*data_, id) {}; const DataInfo& operator*() const { return my_info; } - bool operator==(const DataConstIterator & other) const { return (my_id == other.my_id) && (_p_data_ == other._p_data_); } - bool operator!=(const DataConstIterator & other) const { return !(*this == other); } - DataConstIterator & operator++() + bool operator==(const GenericContainerConstIterator & other) const { return (my_id == other.my_id) && (_p_data_ == other._p_data_); } + bool operator!=(const GenericContainerConstIterator & other) const { return !(*this == other); } + GenericContainerConstIterator & operator++() { ++my_id; my_info = DataInfo(*_p_data_, my_id); return *this; } - DataConstIterator & operator--() + GenericContainerConstIterator & operator--() { --my_id; my_info = DataInfo(*_p_data_, my_id); @@ -63,7 +62,7 @@ class DataConstIterator /** Base class for every object that can be manipulated **/ -class DataGeneric : public BaseConstants +class GenericContainer : public BaseConstants { public: @@ -109,7 +108,7 @@ class DataGeneric : public BaseConstants } /**"define" the destructor for compliance with clang (otherwise lots of warnings)**/ - virtual ~DataGeneric() {}; + virtual ~GenericContainer() {}; protected: std::vector names_; @@ -168,5 +167,5 @@ class DataGeneric : public BaseConstants bool is_in_vect(int val, const T & cont) const {return std::find(cont.begin(), cont.end(), val) != cont.end();} }; -#endif // DATAGENERIC_H +#endif // GENERIC_CONTAINER_H diff --git a/src/DataLine.cpp b/src/element_container/LineContainer.cpp similarity index 90% rename from src/DataLine.cpp rename to src/element_container/LineContainer.cpp index 984f5b2..610aa45 100644 --- a/src/DataLine.cpp +++ b/src/element_container/LineContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,10 +6,11 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "DataLine.h" +#include "LineContainer.h" + #include -void DataLine::init(const RealVect & branch_r, +void LineContainer::init(const RealVect & branch_r, const RealVect & branch_x, const CplxVect & branch_h, const Eigen::VectorXi & branch_from_id, @@ -38,7 +39,7 @@ void DataLine::init(const RealVect & branch_r, _update_model_coeffs(); } -void DataLine::init(const RealVect & branch_r, +void LineContainer::init(const RealVect & branch_r, const RealVect & branch_x, const CplxVect & branch_h_or, const CplxVect & branch_h_ex, @@ -68,7 +69,7 @@ void DataLine::init(const RealVect & branch_r, _update_model_coeffs(); } -DataLine::StateRes DataLine::get_state() const +LineContainer::StateRes LineContainer::get_state() const { std::vector branch_r(powerlines_r_.begin(), powerlines_r_.end()); std::vector branch_x(powerlines_x_.begin(), powerlines_x_.end()); @@ -77,10 +78,10 @@ DataLine::StateRes DataLine::get_state() const std::vector branch_from_id(bus_or_id_.begin(), bus_or_id_.end()); std::vector branch_to_id(bus_ex_id_.begin(), bus_ex_id_.end()); std::vector status = status_; - DataLine::StateRes res(names_, branch_r, branch_x, branch_hor, branch_hex, branch_from_id, branch_to_id, status); + LineContainer::StateRes res(names_, branch_r, branch_x, branch_hor, branch_hex, branch_from_id, branch_to_id, status); return res; } -void DataLine::set_state(DataLine::StateRes & my_state) +void LineContainer::set_state(LineContainer::StateRes & my_state) { reset_results(); names_ = std::get<0>(my_state); @@ -107,7 +108,7 @@ void DataLine::set_state(DataLine::StateRes & my_state) _update_model_coeffs(); } -void DataLine::_update_model_coeffs() +void LineContainer::_update_model_coeffs() { const auto my_size = powerlines_r_.size(); @@ -143,12 +144,12 @@ void DataLine::_update_model_coeffs() } } -void DataLine::fillYbus_spmat(Eigen::SparseMatrix & res, bool ac, const std::vector & id_grid_to_solver) +void LineContainer::fillYbus_spmat(Eigen::SparseMatrix & res, bool ac, const std::vector & id_grid_to_solver) { throw std::runtime_error("You should not use that!"); } -void DataLine::fillYbus(std::vector > & res, +void LineContainer::fillYbus(std::vector > & res, bool ac, const std::vector & id_grid_to_solver, real_type sn_mva) const @@ -169,7 +170,7 @@ void DataLine::fillYbus(std::vector > & res, int bus_or_solver_id = id_grid_to_solver[bus_or_id_me]; if(bus_or_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLine::fillYbusBranch: the line with id "; + exc_ << "Line::fillYbusBranch: the line with id "; exc_ << line_id; exc_ << " is connected (or side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -178,7 +179,7 @@ void DataLine::fillYbus(std::vector > & res, int bus_ex_solver_id = id_grid_to_solver[bus_ex_id_me]; if(bus_ex_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLine::fillYbusBranch: the line with id "; + exc_ << "Line::fillYbusBranch: the line with id "; exc_ << line_id; exc_ << " is connected (ex side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -204,7 +205,7 @@ void DataLine::fillYbus(std::vector > & res, } } -void DataLine::fillBp_Bpp(std::vector > & Bp, +void LineContainer::fillBp_Bpp(std::vector > & Bp, std::vector > & Bpp, const std::vector & id_grid_to_solver, real_type sn_mva, @@ -234,7 +235,7 @@ void DataLine::fillBp_Bpp(std::vector > & Bp, int bus_or_solver_id = id_grid_to_solver[bus_or_id_me]; if(bus_or_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLine::fillBp_Bpp: the line with id "; + exc_ << "LineContainer::fillBp_Bpp: the line with id "; exc_ << line_id; exc_ << " is connected (or side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -243,7 +244,7 @@ void DataLine::fillBp_Bpp(std::vector > & Bp, int bus_ex_solver_id = id_grid_to_solver[bus_ex_id_me]; if(bus_ex_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLine::fillBp_Bpp: the line with id "; + exc_ << "LineContainer::fillBp_Bpp: the line with id "; exc_ << line_id; exc_ << " is connected (ex side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -259,7 +260,7 @@ void DataLine::fillBp_Bpp(std::vector > & Bp, ys_bpp = 1. / (0. + my_i * powerlines_x_(line_id)); }else{ std::ostringstream exc_; - exc_ << "DataLine::fillBp_Bpp: unknown method for the FDPF powerflow for line id "; + exc_ << "LineContainer::fillBp_Bpp: unknown method for the FDPF powerflow for line id "; exc_ << line_id; throw std::runtime_error(exc_.str()); } @@ -289,7 +290,7 @@ void DataLine::fillBp_Bpp(std::vector > & Bp, } -void DataLine::fillBf_for_PTDF(std::vector > & Bf, +void LineContainer::fillBf_for_PTDF(std::vector > & Bf, const std::vector & id_grid_to_solver, real_type sn_mva, int nb_powerline, @@ -306,7 +307,7 @@ void DataLine::fillBf_for_PTDF(std::vector > & Bf, int bus_or_solver_id = id_grid_to_solver[bus_or_id_me]; if(bus_or_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLine::fillBf_for_PTDF: the line with id "; + exc_ << "LineContainer::fillBf_for_PTDF: the line with id "; exc_ << line_id; exc_ << " is connected (or side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -315,7 +316,7 @@ void DataLine::fillBf_for_PTDF(std::vector > & Bf, int bus_ex_solver_id = id_grid_to_solver[bus_ex_id_me]; if(bus_ex_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLine::fillBf_for_PTDF: the line with id "; + exc_ << "LineContainer::fillBf_for_PTDF: the line with id "; exc_ << line_id; exc_ << " is connected (ex side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -337,7 +338,7 @@ void DataLine::fillBf_for_PTDF(std::vector > & Bf, } -void DataLine::reset_results() +void LineContainer::reset_results() { res_powerline_por_ = RealVect(); // in MW res_powerline_qor_ = RealVect(); // in MVar @@ -349,7 +350,7 @@ void DataLine::reset_results() res_powerline_aex_ = RealVect(); // in kA } -void DataLine::compute_results(const Eigen::Ref & Va, +void LineContainer::compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, const Eigen::Ref & V, const std::vector & id_grid_to_solver, @@ -378,7 +379,7 @@ void DataLine::compute_results(const Eigen::Ref & Va, int bus_or_solver_id = id_grid_to_solver[bus_or_id_me]; if(bus_or_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLine::compute_results: the line with id "; + exc_ << "LineContainer::compute_results: the line with id "; exc_ << line_id; exc_ << " is connected (or side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -387,7 +388,7 @@ void DataLine::compute_results(const Eigen::Ref & Va, int bus_ex_solver_id = id_grid_to_solver[bus_ex_id_me]; if(bus_ex_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLine::compute_results: the line with id "; + exc_ << "LineContainer::compute_results: the line with id "; exc_ << line_id; exc_ << " is connected (ex side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -440,7 +441,7 @@ void DataLine::compute_results(const Eigen::Ref & Va, _get_amps(res_powerline_aex_, res_powerline_pex_, res_powerline_qex_, res_powerline_vex_); } -void DataLine::reconnect_connected_buses(std::vector & bus_status) const{ +void LineContainer::reconnect_connected_buses(std::vector & bus_status) const{ const auto my_size = powerlines_r_.size(); for(Eigen::Index line_id = 0; line_id < my_size; ++line_id){ @@ -451,7 +452,7 @@ void DataLine::reconnect_connected_buses(std::vector & bus_status) const{ if(bus_or_id_me == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataLine::reconnect_connected_buses: Line with id "; + exc_ << "LineContainer::reconnect_connected_buses: Line with id "; exc_ << line_id; exc_ << " is connected (origin) to bus '-1' (meaning disconnected) while you said it was disconnected. Have you called `gridmodel.deactivate_powerline(...)` ?."; throw std::runtime_error(exc_.str()); @@ -462,7 +463,7 @@ void DataLine::reconnect_connected_buses(std::vector & bus_status) const{ if(bus_ex_id_me == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataLine::reconnect_connected_buses: Line with id "; + exc_ << "LineContainer::reconnect_connected_buses: Line with id "; exc_ << line_id; exc_ << " is connected (ext) to bus '-1' (meaning disconnected) while you said it was disconnected. Have you called `gridmodel.deactivate_powerline(...)` ?."; throw std::runtime_error(exc_.str()); @@ -471,7 +472,7 @@ void DataLine::reconnect_connected_buses(std::vector & bus_status) const{ } } -void DataLine::nb_line_end(std::vector & res) const{ +void LineContainer::nb_line_end(std::vector & res) const{ const auto my_size = powerlines_r_.size(); for(Eigen::Index line_id = 0; line_id < my_size; ++line_id){ // don't do anything if the element is disconnected @@ -483,7 +484,7 @@ void DataLine::nb_line_end(std::vector & res) const{ } } -void DataLine::get_graph(std::vector > & res) const +void LineContainer::get_graph(std::vector > & res) const { const auto my_size = powerlines_r_.size(); for(Eigen::Index line_id = 0; line_id < my_size; ++line_id){ @@ -496,7 +497,7 @@ void DataLine::get_graph(std::vector > & res) const } } -void DataLine::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component) +void LineContainer::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component) { const Eigen::Index nb_line = nb(); SolverControl unused_solver_control; diff --git a/src/DataLine.h b/src/element_container/LineContainer.h similarity index 91% rename from src/DataLine.h rename to src/element_container/LineContainer.h index 49f07db..fb78817 100644 --- a/src/DataLine.h +++ b/src/element_container/LineContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,31 +6,24 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DATALINE_H -#define DATALINE_H +#ifndef LINE_CONTAINER_H +#define LINE_CONTAINER_H #include -#include "Utils.h" - #include "Eigen/Core" #include "Eigen/Dense" #include "Eigen/SparseCore" #include "Eigen/SparseLU" -#include "DataGeneric.h" +#include "Utils.h" +#include "GenericContainer.h" /** This class is a container for all the powerlines on the grid. -The convention used for the generator is the same as in pandapower: -https://pandapower.readthedocs.io/en/latest/elements/line.html - -and for modeling of the Ybus matrix: -https://pandapower.readthedocs.io/en/latest/elements/line.html#electric-model - **/ -class DataLine : public DataGeneric +class LineContainer : public GenericContainer { public: class LineInfo @@ -60,7 +53,7 @@ class DataLine : public DataGeneric real_type res_a_ex_ka; real_type res_theta_ex_deg; - LineInfo(const DataLine & r_data_line, int my_id): + LineInfo(const LineContainer & r_data_line, int my_id): id(my_id), name(""), connected(false), @@ -118,7 +111,7 @@ class DataLine : public DataGeneric typedef LineInfo DataInfo; private: - typedef DataConstIterator DataLineConstIterator; + typedef GenericContainerConstIterator LineConstIterator; public: typedef std::tuple< @@ -132,7 +125,7 @@ class DataLine : public DataGeneric std::vector // status_ > StateRes; - DataLine() {}; + LineContainer() {}; void init(const RealVect & branch_r, const RealVect & branch_x, @@ -150,8 +143,8 @@ class DataLine : public DataGeneric ); // pickle - DataLine::StateRes get_state() const; - void set_state(DataLine::StateRes & my_state ); + LineContainer::StateRes get_state() const; + void set_state(LineContainer::StateRes & my_state ); template void check_size(const T& my_state) { @@ -159,18 +152,18 @@ class DataLine : public DataGeneric unsigned int size_th = 6; if (my_state.size() != size_th) { - std::cout << "LightSim::DataLine state size " << my_state.size() << " instead of "<< size_th << std::endl; + std::cout << "LightSim::LineContainer state size " << my_state.size() << " instead of "<< size_th << std::endl; // TODO more explicit error message - throw std::runtime_error("Invalid state when loading LightSim::DataLine"); + throw std::runtime_error("Invalid state when loading LightSim::LineContainer"); } } int nb() const { return static_cast(powerlines_r_.size()); } // make it iterable - typedef DataLineConstIterator const_iterator_type; - const_iterator_type begin() const {return DataLineConstIterator(this, 0); } - const_iterator_type end() const {return DataLineConstIterator(this, nb()); } + typedef LineConstIterator const_iterator_type; + const_iterator_type begin() const {return LineConstIterator(this, 0); } + const_iterator_type end() const {return LineConstIterator(this, nb()); } LineInfo operator[](int id) const { if(id < 0) @@ -302,4 +295,4 @@ class DataLine : public DataGeneric CplxVect ydc_tt_; }; -#endif //DATALINE_H +#endif //LINE_CONTAINER_H diff --git a/src/DataLoad.cpp b/src/element_container/LoadContainer.cpp similarity index 77% rename from src/DataLoad.cpp rename to src/element_container/LoadContainer.cpp index 4354f76..4a4de71 100644 --- a/src/DataLoad.cpp +++ b/src/element_container/LoadContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,10 +6,10 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "DataLoad.h" +#include "LoadContainer.h" #include -void DataLoad::init(const RealVect & loads_p, +void LoadContainer::init(const RealVect & loads_p, const RealVect & loads_q, const Eigen::VectorXi & loads_bus_id) { @@ -20,16 +20,16 @@ void DataLoad::init(const RealVect & loads_p, } -DataLoad::StateRes DataLoad::get_state() const +LoadContainer::StateRes LoadContainer::get_state() const { std::vector p_mw(p_mw_.begin(), p_mw_.end()); std::vector q_mvar(q_mvar_.begin(), q_mvar_.end()); std::vector bus_id(bus_id_.begin(), bus_id_.end()); std::vector status = status_; - DataLoad::StateRes res(names_, p_mw, q_mvar, bus_id, status); + LoadContainer::StateRes res(names_, p_mw, q_mvar, bus_id, status); return res; } -void DataLoad::set_state(DataLoad::StateRes & my_state ) +void LoadContainer::set_state(LoadContainer::StateRes & my_state ) { reset_results(); names_ = std::get<0>(my_state); @@ -47,7 +47,7 @@ void DataLoad::set_state(DataLoad::StateRes & my_state ) } -void DataLoad::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const { +void LoadContainer::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const { int nb_load = nb(); int bus_id_me, bus_id_solver; cplx_type tmp; @@ -59,7 +59,7 @@ void DataLoad::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_sol bus_id_solver = id_grid_to_solver[bus_id_me]; if(bus_id_solver == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataLoad::fillSbus: the load with id "; + exc_ << "LoadContainer::fillSbus: the load with id "; exc_ << load_id; exc_ << " is connected to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -70,7 +70,7 @@ void DataLoad::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_sol } } -void DataLoad::compute_results(const Eigen::Ref & Va, +void LoadContainer::compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, const Eigen::Ref & V, const std::vector & id_grid_to_solver, @@ -85,19 +85,19 @@ void DataLoad::compute_results(const Eigen::Ref & Va, res_q_ = q_mvar_; } -void DataLoad::reset_results(){ +void LoadContainer::reset_results(){ res_p_ = RealVect(); // in MW res_q_ = RealVect(); // in MVar res_v_ = RealVect(); // in kV } -void DataLoad::change_p(int load_id, real_type new_p, SolverControl & solver_control) +void LoadContainer::change_p(int load_id, real_type new_p, SolverControl & solver_control) { bool my_status = status_.at(load_id); // and this check that load_id is not out of bound if(!my_status) { std::ostringstream exc_; - exc_ << "DataLoad::change_p: Impossible to change the active value of a disconnected load (check load id "; + exc_ << "LoadContainer::change_p: Impossible to change the active value of a disconnected load (check load id "; exc_ << load_id; exc_ << ")"; throw std::runtime_error(exc_.str()); @@ -106,13 +106,13 @@ void DataLoad::change_p(int load_id, real_type new_p, SolverControl & solver_con p_mw_(load_id) = new_p; } -void DataLoad::change_q(int load_id, real_type new_q, SolverControl & solver_control) +void LoadContainer::change_q(int load_id, real_type new_q, SolverControl & solver_control) { bool my_status = status_.at(load_id); // and this check that load_id is not out of bound if(!my_status) { std::ostringstream exc_; - exc_ << "DataLoad::change_q: Impossible to change the reactive value of a disconnected load (check load id "; + exc_ << "LoadContainer::change_q: Impossible to change the reactive value of a disconnected load (check load id "; exc_ << load_id; exc_ << ")"; throw std::runtime_error(exc_.str()); @@ -121,7 +121,7 @@ void DataLoad::change_q(int load_id, real_type new_q, SolverControl & solver_con q_mvar_(load_id) = new_q; } -void DataLoad::reconnect_connected_buses(std::vector & bus_status) const { +void LoadContainer::reconnect_connected_buses(std::vector & bus_status) const { const int nb_load = nb(); for(int load_id = 0; load_id < nb_load; ++load_id) { @@ -130,7 +130,7 @@ void DataLoad::reconnect_connected_buses(std::vector & bus_status) const { if(my_bus == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataLoad::reconnect_connected_buses: Load with id "; + exc_ << "LoadContainer::reconnect_connected_buses: Load with id "; exc_ << load_id; exc_ << " is connected to bus '-1' (meaning disconnected) while you said it was disconnected. Have you called `gridmodel.deactivate_load(...)` ?."; throw std::runtime_error(exc_.str()); @@ -139,7 +139,7 @@ void DataLoad::reconnect_connected_buses(std::vector & bus_status) const { } } -void DataLoad::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component){ +void LoadContainer::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component){ const int nb_el = nb(); SolverControl unused_solver_control; for(int el_id = 0; el_id < nb_el; ++el_id) diff --git a/src/DataLoad.h b/src/element_container/LoadContainer.h similarity index 89% rename from src/DataLoad.h rename to src/element_container/LoadContainer.h index 5f08d4b..3786d52 100644 --- a/src/DataLoad.h +++ b/src/element_container/LoadContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,17 +6,17 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DATALOAD_H -#define DATALOAD_H +#ifndef LOAD_CONTAINER_H +#define LOAD_CONTAINER_H -#include "Utils.h" #include "Eigen/Core" #include "Eigen/Dense" #include "Eigen/SparseCore" #include "Eigen/SparseLU" -#include "DataGeneric.h" +#include "Utils.h" +#include "GenericContainer.h" /** This class is a container for all loads on the grid. @@ -31,7 +31,7 @@ NOTE: this class is also used for the storage units! So storage units are modele which entails that negative storage: the unit is discharging, power is injected in the grid, positive storage: the unit is charging, power is taken from the grid. **/ -class DataLoad : public DataGeneric +class LoadContainer : public GenericContainer { // TODO make a single class for load and shunt and just specialize the part where the // TODO powerflow equations are located (when i update the Y matrix) @@ -56,7 +56,7 @@ class DataLoad : public DataGeneric real_type res_v_kv; real_type res_theta_deg; - LoadInfo(const DataLoad & r_data_load, int my_id): + LoadInfo(const LoadContainer & r_data_load, int my_id): id(-1), name(""), connected(false), @@ -95,12 +95,12 @@ class DataLoad : public DataGeneric typedef LoadInfo DataInfo; private: - typedef DataConstIterator DataLoadConstIterator; + typedef GenericContainerConstIterator LoadContainerConstIterator; public: - typedef DataLoadConstIterator const_iterator_type; - const_iterator_type begin() const {return DataLoadConstIterator(this, 0); } - const_iterator_type end() const {return DataLoadConstIterator(this, nb()); } + typedef LoadContainerConstIterator const_iterator_type; + const_iterator_type begin() const {return LoadContainerConstIterator(this, 0); } + const_iterator_type end() const {return LoadContainerConstIterator(this, nb()); } LoadInfo operator[](int id) const { if(id < 0) @@ -124,11 +124,11 @@ class DataLoad : public DataGeneric std::vector // status > StateRes; - DataLoad() {}; + LoadContainer() {}; // pickle (python) - DataLoad::StateRes get_state() const; - void set_state(DataLoad::StateRes & my_state ); + LoadContainer::StateRes get_state() const; + void set_state(LoadContainer::StateRes & my_state ); void init(const RealVect & loads_p, @@ -189,4 +189,4 @@ class DataLoad : public DataGeneric RealVect res_theta_; // in degree }; -#endif //DATALOAD_H +#endif //LOAD_CONTAINER_H diff --git a/src/DataSGen.cpp b/src/element_container/SGenContainer.cpp similarity index 71% rename from src/DataSGen.cpp rename to src/element_container/SGenContainer.cpp index 08d9100..48c1baa 100644 --- a/src/DataSGen.cpp +++ b/src/element_container/SGenContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,8 +6,9 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "DataSGen.h" -void DataSGen::init(const RealVect & sgen_p, +#include "SGenContainer.h" + +void SGenContainer::init(const RealVect & sgen_p, const RealVect & sgen_q, const RealVect & sgen_pmin, const RealVect & sgen_pmax, @@ -16,13 +17,13 @@ void DataSGen::init(const RealVect & sgen_p, const Eigen::VectorXi & sgen_bus_id) { int size = static_cast(sgen_p.size()); - DataGeneric::check_size(sgen_p, size, "sgen_p"); - DataGeneric::check_size(sgen_q, size, "sgen_q"); - DataGeneric::check_size(sgen_pmin, size, "sgen_pmin"); - DataGeneric::check_size(sgen_pmax, size, "sgen_pmax"); - DataGeneric::check_size(sgen_qmin, size, "sgen_qmin"); - DataGeneric::check_size(sgen_qmax, size, "sgen_qmax"); - DataGeneric::check_size(sgen_bus_id, size, "sgen_bus_id"); + GenericContainer::check_size(sgen_p, size, "sgen_p"); + GenericContainer::check_size(sgen_q, size, "sgen_q"); + GenericContainer::check_size(sgen_pmin, size, "sgen_pmin"); + GenericContainer::check_size(sgen_pmax, size, "sgen_pmax"); + GenericContainer::check_size(sgen_qmin, size, "sgen_qmin"); + GenericContainer::check_size(sgen_qmax, size, "sgen_qmax"); + GenericContainer::check_size(sgen_bus_id, size, "sgen_bus_id"); p_mw_ = sgen_p; q_mvar_ = sgen_q; @@ -34,7 +35,7 @@ void DataSGen::init(const RealVect & sgen_p, status_ = std::vector(sgen_p.size(), true); } -DataSGen::StateRes DataSGen::get_state() const +SGenContainer::StateRes SGenContainer::get_state() const { std::vector p_mw(p_mw_.begin(), p_mw_.end()); std::vector q_mvar(q_mvar_.begin(), q_mvar_.end()); @@ -44,11 +45,11 @@ DataSGen::StateRes DataSGen::get_state() const std::vector q_max(q_max_mvar_.begin(), q_max_mvar_.end()); std::vector bus_id(bus_id_.begin(), bus_id_.end()); std::vector status = status_; - DataSGen::StateRes res(names_, p_mw, q_mvar, p_min, p_max, q_min, q_max, bus_id, status); + SGenContainer::StateRes res(names_, p_mw, q_mvar, p_min, p_max, q_min, q_max, bus_id, status); return res; } -void DataSGen::set_state(DataSGen::StateRes & my_state ) +void SGenContainer::set_state(SGenContainer::StateRes & my_state ) { reset_results(); @@ -62,14 +63,14 @@ void DataSGen::set_state(DataSGen::StateRes & my_state ) std::vector & bus_id = std::get<7>(my_state); std::vector & status = std::get<8>(my_state); auto size = p_mw.size(); - DataGeneric::check_size(p_mw, size, "p_mw"); - DataGeneric::check_size(q_mvar, size, "q_mvar"); - DataGeneric::check_size(p_min, size, "p_min"); - DataGeneric::check_size(p_max, size, "p_max"); - DataGeneric::check_size(q_min, size, "q_min"); - DataGeneric::check_size(q_max, size, "q_max"); - DataGeneric::check_size(bus_id, size, "bus_id"); - DataGeneric::check_size(status, size, "status"); + GenericContainer::check_size(p_mw, size, "p_mw"); + GenericContainer::check_size(q_mvar, size, "q_mvar"); + GenericContainer::check_size(p_min, size, "p_min"); + GenericContainer::check_size(p_max, size, "p_max"); + GenericContainer::check_size(q_min, size, "q_min"); + GenericContainer::check_size(q_max, size, "q_max"); + GenericContainer::check_size(bus_id, size, "bus_id"); + GenericContainer::check_size(status, size, "status"); p_mw_ = RealVect::Map(&p_mw[0], size); q_mvar_ = RealVect::Map(&q_mvar[0], size); @@ -82,7 +83,7 @@ void DataSGen::set_state(DataSGen::StateRes & my_state ) status_ = status; } -void DataSGen::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const { +void SGenContainer::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const { const int nb_sgen = nb(); int bus_id_me, bus_id_solver; cplx_type tmp; @@ -94,7 +95,7 @@ void DataSGen::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_sol bus_id_solver = id_grid_to_solver[bus_id_me]; if(bus_id_solver == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataSGen::fillSbus: Static Generator with id "; + exc_ << "SGenContainer::fillSbus: Static Generator with id "; exc_ << sgen_id; exc_ << " is connected to a disconnected bus while being connected to the grid."; throw std::runtime_error(exc_.str()); @@ -105,7 +106,7 @@ void DataSGen::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_sol } } -void DataSGen::compute_results(const Eigen::Ref & Va, +void SGenContainer::compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, const Eigen::Ref & V, const std::vector & id_grid_to_solver, @@ -121,19 +122,19 @@ void DataSGen::compute_results(const Eigen::Ref & Va, else res_q_ = RealVect::Zero(nb_sgen); } -void DataSGen::reset_results(){ +void SGenContainer::reset_results(){ res_p_ = RealVect(); // in MW res_q_ = RealVect(); // in MVar res_v_ = RealVect(); // in kV } -void DataSGen::change_p(int sgen_id, real_type new_p, SolverControl & solver_control) +void SGenContainer::change_p(int sgen_id, real_type new_p, SolverControl & solver_control) { bool my_status = status_.at(sgen_id); // and this check that load_id is not out of bound if(!my_status) { std::ostringstream exc_; - exc_ << "DataSGen::change_p: Impossible to change the active value of a disconnected static generator (check sgen id "; + exc_ << "SGenContainer::change_p: Impossible to change the active value of a disconnected static generator (check sgen id "; exc_ << sgen_id; exc_ << ")"; throw std::runtime_error(exc_.str()); @@ -142,13 +143,13 @@ void DataSGen::change_p(int sgen_id, real_type new_p, SolverControl & solver_con p_mw_(sgen_id) = new_p; } -void DataSGen::change_q(int sgen_id, real_type new_q, SolverControl & solver_control) +void SGenContainer::change_q(int sgen_id, real_type new_q, SolverControl & solver_control) { bool my_status = status_.at(sgen_id); // and this check that load_id is not out of bound if(!my_status) { std::ostringstream exc_; - exc_ << "DataSGen::change_q: Impossible to change the reactive value of a disconnected static generator (check sgen id "; + exc_ << "SGenContainer::change_q: Impossible to change the reactive value of a disconnected static generator (check sgen id "; exc_ << sgen_id; exc_ << ")"; throw std::runtime_error(exc_.str()); @@ -157,7 +158,7 @@ void DataSGen::change_q(int sgen_id, real_type new_q, SolverControl & solver_con q_mvar_(sgen_id) = new_q; } -void DataSGen::reconnect_connected_buses(std::vector & bus_status) const { +void SGenContainer::reconnect_connected_buses(std::vector & bus_status) const { const int nb_sgen = nb(); for(int sgen_id = 0; sgen_id < nb_sgen; ++sgen_id) { @@ -166,7 +167,7 @@ void DataSGen::reconnect_connected_buses(std::vector & bus_status) const { if(my_bus == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataSGen::reconnect_connected_buses: Static Generator with id "; + exc_ << "SGenContainer::reconnect_connected_buses: Static Generator with id "; exc_ << sgen_id; exc_ << " is connected to bus '-1' (meaning disconnected) while you said it was disconnected. Have you called `gridmodel.deactivate_sgen(...)` ?."; throw std::runtime_error(exc_.str()); @@ -175,7 +176,7 @@ void DataSGen::reconnect_connected_buses(std::vector & bus_status) const { } } -void DataSGen::gen_p_per_bus(std::vector & res) const +void SGenContainer::gen_p_per_bus(std::vector & res) const { const int nb_gen = nb(); for(int sgen_id = 0; sgen_id < nb_gen; ++sgen_id) @@ -186,7 +187,7 @@ void DataSGen::gen_p_per_bus(std::vector & res) const } } -void DataSGen::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component){ +void SGenContainer::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component){ const int nb_el = nb(); SolverControl unused_solver_control; for(int el_id = 0; el_id < nb_el; ++el_id) diff --git a/src/DataSGen.h b/src/element_container/SGenContainer.h similarity index 90% rename from src/DataSGen.h rename to src/element_container/SGenContainer.h index d9c1b30..ae05cdb 100644 --- a/src/DataSGen.h +++ b/src/element_container/SGenContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,17 +6,17 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DATASGEN_H -#define DATASGEN_H +#ifndef SGEN_CONTAINER_H +#define SGEN_CONTAINER_H -#include "Utils.h" #include "Eigen/Core" #include "Eigen/Dense" #include "Eigen/SparseCore" #include "Eigen/SparseLU" -#include "DataGeneric.h" +#include "Utils.h" +#include "GenericContainer.h" /** This class is a container for all static generator (PQ generators) on the grid. @@ -28,7 +28,7 @@ The convention used for the static is the same as in pandapower: and for modeling of the Ybus matrix: https://pandapower.readthedocs.io/en/latest/elements/sgen.html#electric-model **/ -class DataSGen: public DataGeneric +class SGenContainer: public GenericContainer { // TODO make a single class for load and shunt and just specialize the part where the // TODO powerflow equations are located (when i update the Y matrix) @@ -60,7 +60,7 @@ class DataSGen: public DataGeneric real_type res_v_kv; real_type res_theta_deg; - SGenInfo(const DataSGen & r_data_sgen, int my_id): + SGenInfo(const SGenContainer & r_data_sgen, int my_id): id(-1), name(""), connected(false), @@ -108,12 +108,12 @@ class DataSGen: public DataGeneric typedef SGenInfo DataInfo; private: - typedef DataConstIterator DataSGenConstIterator; + typedef GenericContainerConstIterator SGenContainerConstIterator; public: - typedef DataSGenConstIterator const_iterator_type; - const_iterator_type begin() const {return DataSGenConstIterator(this, 0); } - const_iterator_type end() const {return DataSGenConstIterator(this, nb()); } + typedef SGenContainerConstIterator const_iterator_type; + const_iterator_type begin() const {return SGenContainerConstIterator(this, 0); } + const_iterator_type end() const {return SGenContainerConstIterator(this, nb()); } SGenInfo operator[](int id) const { if(id < 0) @@ -140,11 +140,11 @@ class DataSGen: public DataGeneric std::vector // status > StateRes; - DataSGen() {}; + SGenContainer() {}; // pickle (python) - DataSGen::StateRes get_state() const; - void set_state(DataSGen::StateRes & my_state ); + SGenContainer::StateRes get_state() const; + void set_state(SGenContainer::StateRes & my_state ); void init(const RealVect & sgen_p, @@ -214,4 +214,4 @@ class DataSGen: public DataGeneric RealVect res_theta_; // in degree }; -#endif //DATASGEN_H +#endif //SGEN_CONTAINER_H diff --git a/src/DataShunt.cpp b/src/element_container/ShuntContainer.cpp similarity index 81% rename from src/DataShunt.cpp rename to src/element_container/ShuntContainer.cpp index ba52bbd..5817834 100644 --- a/src/DataShunt.cpp +++ b/src/element_container/ShuntContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,10 +6,11 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "DataShunt.h" +#include "ShuntContainer.h" + #include -void DataShunt::init(const RealVect & shunt_p_mw, +void ShuntContainer::init(const RealVect & shunt_p_mw, const RealVect & shunt_q_mvar, const Eigen::VectorXi & shunt_bus_id) { @@ -19,17 +20,17 @@ void DataShunt::init(const RealVect & shunt_p_mw, status_ = std::vector(p_mw_.size(), true); // by default everything is connected } -DataShunt::StateRes DataShunt::get_state() const +ShuntContainer::StateRes ShuntContainer::get_state() const { std::vector p_mw(p_mw_.begin(), p_mw_.end()); std::vector q_mvar(q_mvar_.begin(), q_mvar_.end()); std::vector bus_id(bus_id_.begin(), bus_id_.end()); std::vector status = status_; - DataShunt::StateRes res(names_, p_mw, q_mvar, bus_id, status); + ShuntContainer::StateRes res(names_, p_mw, q_mvar, bus_id, status); return res; } -void DataShunt::set_state(DataShunt::StateRes & my_state ) +void ShuntContainer::set_state(ShuntContainer::StateRes & my_state ) { reset_results(); names_ = std::get<0>(my_state); @@ -46,7 +47,7 @@ void DataShunt::set_state(DataShunt::StateRes & my_state ) status_ = status; } -void DataShunt::fillYbus(std::vector > & res, +void ShuntContainer::fillYbus(std::vector > & res, bool ac, const std::vector & id_grid_to_solver, real_type sn_mva) const @@ -67,7 +68,7 @@ void DataShunt::fillYbus(std::vector > & res, bus_id_solver = id_grid_to_solver[bus_id_me]; if(bus_id_solver == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataShunt::fillYbus: the shunt with id "; + exc_ << "ShuntContainer::fillYbus: the shunt with id "; exc_ << shunt_id; exc_ << " is connected to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -77,7 +78,7 @@ void DataShunt::fillYbus(std::vector > & res, } } -void DataShunt::fillBp_Bpp(std::vector > & Bp, +void ShuntContainer::fillBp_Bpp(std::vector > & Bp, std::vector > & Bpp, const std::vector & id_grid_to_solver, real_type sn_mva, @@ -94,7 +95,7 @@ void DataShunt::fillBp_Bpp(std::vector > & Bp, bus_id_solver = id_grid_to_solver[bus_id_me]; if(bus_id_solver == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataShunt::fillBp_Bpp: the shunt with id "; + exc_ << "ShuntContainer::fillBp_Bpp: the shunt with id "; exc_ << shunt_id; exc_ << " is connected to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -106,7 +107,7 @@ void DataShunt::fillBp_Bpp(std::vector > & Bp, } } -void DataShunt::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const // in DC i need that +void ShuntContainer::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const // in DC i need that { if(ac) return; // in AC I do not do that // std::cout << " ok i use this function" << std::endl; @@ -126,11 +127,11 @@ void DataShunt::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_so } } -void DataShunt::fillYbus_spmat(Eigen::SparseMatrix & res, bool ac, const std::vector & id_grid_to_solver){ - throw std::runtime_error("DataShunt::fillYbus_spmat: should not be used anymore !"); +void ShuntContainer::fillYbus_spmat(Eigen::SparseMatrix & res, bool ac, const std::vector & id_grid_to_solver){ + throw std::runtime_error("ShuntContainer::fillYbus_spmat: should not be used anymore !"); } -void DataShunt::compute_results(const Eigen::Ref & Va, +void ShuntContainer::compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, const Eigen::Ref & V, const std::vector & id_grid_to_solver, @@ -148,7 +149,7 @@ void DataShunt::compute_results(const Eigen::Ref & Va, int bus_id_me = bus_id_(shunt_id); int bus_solver_id = id_grid_to_solver[bus_id_me]; if(bus_solver_id == _deactivated_bus_id){ - throw std::runtime_error("DataShunt::compute_results: A shunt is connected to a disconnected bus."); + throw std::runtime_error("ShuntContainer::compute_results: A shunt is connected to a disconnected bus."); } cplx_type E = V(bus_solver_id); cplx_type y = -my_one_ * (p_mw_(shunt_id) + my_i * q_mvar_(shunt_id)) / sn_mva; @@ -160,13 +161,13 @@ void DataShunt::compute_results(const Eigen::Ref & Va, } } -void DataShunt::reset_results(){ +void ShuntContainer::reset_results(){ res_p_ = RealVect(); // in MW res_q_ = RealVect(); // in MVar res_v_ = RealVect(); // in kV } -void DataShunt::change_p(int shunt_id, real_type new_p, SolverControl & solver_control) +void ShuntContainer::change_p(int shunt_id, real_type new_p, SolverControl & solver_control) { bool my_status = status_.at(shunt_id); // and this check that load_id is not out of bound if(!my_status) throw std::runtime_error("Impossible to change the active value of a disconnected shunt"); @@ -178,7 +179,7 @@ void DataShunt::change_p(int shunt_id, real_type new_p, SolverControl & solver_c } -void DataShunt::change_q(int shunt_id, real_type new_q, SolverControl & solver_control) +void ShuntContainer::change_q(int shunt_id, real_type new_q, SolverControl & solver_control) { bool my_status = status_.at(shunt_id); // and this check that load_id is not out of bound if(!my_status) throw std::runtime_error("Impossible to change the reactive value of a disconnected shunt"); @@ -188,7 +189,7 @@ void DataShunt::change_q(int shunt_id, real_type new_q, SolverControl & solver_c q_mvar_(shunt_id) = new_q; } -void DataShunt::reconnect_connected_buses(std::vector & bus_status) const { +void ShuntContainer::reconnect_connected_buses(std::vector & bus_status) const { const int nb_shunt = nb(); for(int shunt_id = 0; shunt_id < nb_shunt; ++shunt_id) { @@ -197,7 +198,7 @@ void DataShunt::reconnect_connected_buses(std::vector & bus_status) const if(my_bus == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataShunt::reconnect_connected_buses: Shunt with id "; + exc_ << "ShuntContainer::reconnect_connected_buses: Shunt with id "; exc_ << shunt_id; exc_ << " is connected to bus '-1' (meaning disconnected) while you said it was disconnected. Have you called `gridmodel.deactivate_shunt(...)` ?."; throw std::runtime_error(exc_.str()); @@ -206,7 +207,7 @@ void DataShunt::reconnect_connected_buses(std::vector & bus_status) const } } -void DataShunt::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component){ +void ShuntContainer::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component){ const int nb_el = nb(); SolverControl unused_solver_control; for(int el_id = 0; el_id < nb_el; ++el_id) diff --git a/src/DataShunt.h b/src/element_container/ShuntContainer.h similarity index 89% rename from src/DataShunt.h rename to src/element_container/ShuntContainer.h index 53e5c33..2cbed98 100644 --- a/src/DataShunt.h +++ b/src/element_container/ShuntContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,18 +6,16 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DATASHUNT_H -#define DATASHUNT_H - -#include "Utils.h" +#ifndef SHUNT_CONTAINER_H +#define SHUNT_CONTAINER_H #include "Eigen/Core" #include "Eigen/Dense" #include "Eigen/SparseCore" #include "Eigen/SparseLU" - -#include "DataGeneric.h" +#include "Utils.h" +#include "GenericContainer.h" /** This class is a container for all shunts on the grid. @@ -28,7 +26,7 @@ The convention used for the shunt is the same as in pandapower: and for modeling of the Ybus matrix: https://pandapower.readthedocs.io/en/latest/elements/shunt.html#electric-model **/ -class DataShunt : public DataGeneric +class ShuntContainer : public GenericContainer { // iterators part public: @@ -50,7 +48,7 @@ class DataShunt : public DataGeneric real_type res_v_kv; real_type res_theta_deg; - ShuntInfo(const DataShunt & r_data_shunt, int my_id): + ShuntInfo(const ShuntContainer & r_data_shunt, int my_id): id(-1), name(""), connected(false), @@ -89,12 +87,12 @@ class DataShunt : public DataGeneric typedef ShuntInfo DataInfo; private: - typedef DataConstIterator DataShuntConstIterator; + typedef GenericContainerConstIterator ShuntContainerConstIterator; public: - typedef DataShuntConstIterator const_iterator_type; - const_iterator_type begin() const {return DataShuntConstIterator(this, 0); } - const_iterator_type end() const {return DataShuntConstIterator(this, nb()); } + typedef ShuntContainerConstIterator const_iterator_type; + const_iterator_type begin() const {return ShuntContainerConstIterator(this, 0); } + const_iterator_type end() const {return ShuntContainerConstIterator(this, nb()); } ShuntInfo operator[](int id) const { if(id < 0) @@ -117,7 +115,7 @@ class DataShunt : public DataGeneric std::vector // status > StateRes; - DataShunt() {}; + ShuntContainer() {}; void init(const RealVect & shunt_p_mw, const RealVect & shunt_q_mvar, @@ -125,8 +123,8 @@ class DataShunt : public DataGeneric ); // pickle (python) - DataShunt::StateRes get_state() const; - void set_state(DataShunt::StateRes & my_state ); + ShuntContainer::StateRes get_state() const; + void set_state(ShuntContainer::StateRes & my_state ); int nb() const { return static_cast(p_mw_.size()); } @@ -191,4 +189,4 @@ class DataShunt : public DataGeneric RealVect res_theta_; // in kV }; -#endif //DATASHUNT_H +#endif //SHUNT_CONTAINER_H diff --git a/src/DataTrafo.cpp b/src/element_container/TrafoContainer.cpp similarity index 86% rename from src/DataTrafo.cpp rename to src/element_container/TrafoContainer.cpp index 15afa2a..ac4c346 100644 --- a/src/DataTrafo.cpp +++ b/src/element_container/TrafoContainer.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,11 +6,12 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "DataTrafo.h" +#include "TrafoContainer.h" + #include #include -void DataTrafo::init(const RealVect & trafo_r, +void TrafoContainer::init(const RealVect & trafo_r, const RealVect & trafo_x, const CplxVect & trafo_b, const RealVect & trafo_tap_step_pct, @@ -27,15 +28,15 @@ void DataTrafo::init(const RealVect & trafo_r, DOES NOT WORK WITH POWERLINES **/ const int size = static_cast(trafo_r.size()); - DataGeneric::check_size(trafo_r, size, "trafo_r"); - DataGeneric::check_size(trafo_x, size, "trafo_x"); - DataGeneric::check_size(trafo_b, size, "trafo_b"); - DataGeneric::check_size(trafo_tap_step_pct, size, "trafo_tap_step_pct"); - DataGeneric::check_size(trafo_tap_pos, size, "trafo_tap_pos"); - DataGeneric::check_size(trafo_shift_degree, size, "trafo_shift_degree"); - DataGeneric::check_size(trafo_tap_hv, static_cast::size_type>(size), "trafo_tap_hv"); - DataGeneric::check_size(trafo_hv_id, size, "trafo_hv_id"); - DataGeneric::check_size(trafo_lv_id, size, "trafo_lv_id"); + GenericContainer::check_size(trafo_r, size, "trafo_r"); + GenericContainer::check_size(trafo_x, size, "trafo_x"); + GenericContainer::check_size(trafo_b, size, "trafo_b"); + GenericContainer::check_size(trafo_tap_step_pct, size, "trafo_tap_step_pct"); + GenericContainer::check_size(trafo_tap_pos, size, "trafo_tap_pos"); + GenericContainer::check_size(trafo_shift_degree, size, "trafo_shift_degree"); + GenericContainer::check_size(trafo_tap_hv, static_cast::size_type>(size), "trafo_tap_hv"); + GenericContainer::check_size(trafo_hv_id, size, "trafo_hv_id"); + GenericContainer::check_size(trafo_lv_id, size, "trafo_lv_id"); //TODO "parrallel" in the pandapower dataframe, like for lines, are not handled. Handle it python side! @@ -55,7 +56,7 @@ void DataTrafo::init(const RealVect & trafo_r, } -DataTrafo::StateRes DataTrafo::get_state() const +TrafoContainer::StateRes TrafoContainer::get_state() const { std::vector branch_r(r_.begin(), r_.end()); std::vector branch_x(x_.begin(), x_.end()); @@ -66,12 +67,12 @@ DataTrafo::StateRes DataTrafo::get_state() const std::vector ratio(ratio_.begin(), ratio_.end()); std::vector shift(shift_.begin(), shift_.end()); std::vector is_tap_hv_side = is_tap_hv_side_; - DataTrafo::StateRes res(names_, branch_r, branch_x, branch_h, bus_hv_id, bus_lv_id, status, ratio, is_tap_hv_side, shift); + TrafoContainer::StateRes res(names_, branch_r, branch_x, branch_h, bus_hv_id, bus_lv_id, status, ratio, is_tap_hv_side, shift); return res; } -void DataTrafo::set_state(DataTrafo::StateRes & my_state) +void TrafoContainer::set_state(TrafoContainer::StateRes & my_state) { reset_results(); @@ -87,15 +88,15 @@ void DataTrafo::set_state(DataTrafo::StateRes & my_state) std::vector & shift = std::get<9>(my_state); auto size = branch_r.size(); - DataGeneric::check_size(branch_r, size, "branch_r"); - DataGeneric::check_size(branch_x, size, "branch_x"); - DataGeneric::check_size(branch_h, size, "branch_h"); - DataGeneric::check_size(bus_hv_id, size, "bus_hv_id"); - DataGeneric::check_size(bus_lv_id, size, "bus_lv_id"); - DataGeneric::check_size(status, size, "status"); - DataGeneric::check_size(ratio, size, "ratio"); - DataGeneric::check_size(is_tap_hv_side, size, "is_tap_hv_side"); - DataGeneric::check_size(shift, size, "shift"); + GenericContainer::check_size(branch_r, size, "branch_r"); + GenericContainer::check_size(branch_x, size, "branch_x"); + GenericContainer::check_size(branch_h, size, "branch_h"); + GenericContainer::check_size(bus_hv_id, size, "bus_hv_id"); + GenericContainer::check_size(bus_lv_id, size, "bus_lv_id"); + GenericContainer::check_size(status, size, "status"); + GenericContainer::check_size(ratio, size, "ratio"); + GenericContainer::check_size(is_tap_hv_side, size, "is_tap_hv_side"); + GenericContainer::check_size(shift, size, "shift"); // now assign the values r_ = RealVect::Map(&branch_r[0], size); @@ -113,7 +114,7 @@ void DataTrafo::set_state(DataTrafo::StateRes & my_state) } -void DataTrafo::_update_model_coeffs() +void TrafoContainer::_update_model_coeffs() { const Eigen::Index my_size = r_.size(); @@ -163,12 +164,12 @@ void DataTrafo::_update_model_coeffs() } } -void DataTrafo::fillYbus_spmat(Eigen::SparseMatrix & res, bool ac, const std::vector & id_grid_to_solver) +void TrafoContainer::fillYbus_spmat(Eigen::SparseMatrix & res, bool ac, const std::vector & id_grid_to_solver) { throw std::runtime_error("You should not use that!"); } -void DataTrafo::fillYbus(std::vector > & res, +void TrafoContainer::fillYbus(std::vector > & res, bool ac, const std::vector & id_grid_to_solver, real_type sn_mva) const @@ -186,7 +187,7 @@ void DataTrafo::fillYbus(std::vector > & res, int bus_hv_solver_id = id_grid_to_solver[bus_hv_id_me]; if(bus_hv_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::fillYbus: the trafo with id "; + exc_ << "TrafoContainer::fillYbus: the trafo with id "; exc_ << trafo_id; exc_ << " is connected (hv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -195,7 +196,7 @@ void DataTrafo::fillYbus(std::vector > & res, int bus_lv_solver_id = id_grid_to_solver[bus_lv_id_me]; if(bus_lv_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::fillYbus: the trafo with id "; + exc_ << "TrafoContainer::fillYbus: the trafo with id "; exc_ << trafo_id; exc_ << " is connected (lv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -221,7 +222,7 @@ void DataTrafo::fillYbus(std::vector > & res, } } -void DataTrafo::hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, bool ac, const std::vector & id_grid_to_solver){ +void TrafoContainer::hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, bool ac, const std::vector & id_grid_to_solver){ if(ac) return; // return; const int nb_trafo = nb(); @@ -235,7 +236,7 @@ void DataTrafo::hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, bool ac, const s bus_id_solver_lv = id_grid_to_solver[bus_id_me]; if(bus_id_solver_lv == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::hack_Sbus_for_dc_phase_shifter: the trafo with id "; + exc_ << "TrafoContainer::hack_Sbus_for_dc_phase_shifter: the trafo with id "; exc_ << trafo_id; exc_ << " is connected (lv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -244,7 +245,7 @@ void DataTrafo::hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, bool ac, const s bus_id_solver_hv = id_grid_to_solver[bus_id_me]; if(bus_id_solver_hv == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::hack_Sbus_for_dc_phase_shifter: the trafo with id "; + exc_ << "TrafoContainer::hack_Sbus_for_dc_phase_shifter: the trafo with id "; exc_ << trafo_id; exc_ << " is connected (hv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -254,7 +255,7 @@ void DataTrafo::hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, bool ac, const s } } -void DataTrafo::compute_results(const Eigen::Ref & Va, +void TrafoContainer::compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, const Eigen::Ref & V, const std::vector & id_grid_to_solver, @@ -284,7 +285,7 @@ void DataTrafo::compute_results(const Eigen::Ref & Va, int bus_hv_solver_id = id_grid_to_solver[bus_hv_id_me]; if(bus_hv_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::compute_results: the trafo with id "; + exc_ << "TrafoContainer::compute_results: the trafo with id "; exc_ << trafo_id; exc_ << " is connected (hv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -293,7 +294,7 @@ void DataTrafo::compute_results(const Eigen::Ref & Va, int bus_lv_solver_id = id_grid_to_solver[bus_lv_id_me]; if(bus_lv_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::compute_results: the trafo with id "; + exc_ << "TrafoContainer::compute_results: the trafo with id "; exc_ << trafo_id; exc_ << " is connected (lv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -346,7 +347,7 @@ void DataTrafo::compute_results(const Eigen::Ref & Va, _get_amps(res_a_lv_, res_p_lv_, res_q_lv_, res_v_lv_); } -void DataTrafo::reset_results(){ +void TrafoContainer::reset_results(){ res_p_hv_ = RealVect(); // in MW res_q_hv_ = RealVect(); // in MVar res_v_hv_ = RealVect(); // in kV @@ -358,7 +359,7 @@ void DataTrafo::reset_results(){ } -void DataTrafo::fillBp_Bpp(std::vector > & Bp, +void TrafoContainer::fillBp_Bpp(std::vector > & Bp, std::vector > & Bpp, const std::vector & id_grid_to_solver, real_type sn_mva, @@ -389,7 +390,7 @@ void DataTrafo::fillBp_Bpp(std::vector > & Bp, int bus_or_solver_id = id_grid_to_solver[bus_or_id_me]; if(bus_or_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::fillBp_Bpp: the trafo with id "; + exc_ << "TrafoContainer::fillBp_Bpp: the trafo with id "; exc_ << tr_id; exc_ << " is connected (hv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -398,7 +399,7 @@ void DataTrafo::fillBp_Bpp(std::vector > & Bp, int bus_ex_solver_id = id_grid_to_solver[bus_ex_id_me]; if(bus_ex_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::fillBp_Bpp: the trafo with id "; + exc_ << "TrafoContainer::fillBp_Bpp: the trafo with id "; exc_ << tr_id; exc_ << " is connected (lv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -428,7 +429,7 @@ void DataTrafo::fillBp_Bpp(std::vector > & Bp, ys_bpp = 1. / (0. + my_i * x_(tr_id)); }else{ std::ostringstream exc_; - exc_ << "DataTrafo::fillBp_Bpp: unknown method for the FDPF powerflow for line id "; + exc_ << "TrafoContainer::fillBp_Bpp: unknown method for the FDPF powerflow for line id "; exc_ << tr_id; throw std::runtime_error(exc_.str()); } @@ -459,7 +460,7 @@ void DataTrafo::fillBp_Bpp(std::vector > & Bp, } -void DataTrafo::fillBf_for_PTDF(std::vector > & Bf, +void TrafoContainer::fillBf_for_PTDF(std::vector > & Bf, const std::vector & id_grid_to_solver, real_type sn_mva, int nb_powerline, @@ -476,7 +477,7 @@ void DataTrafo::fillBf_for_PTDF(std::vector > & Bf, int bus_or_solver_id = id_grid_to_solver[bus_or_id_me]; if(bus_or_solver_id == _deactivated_bus_id){ std::ostringstream exc_; - exc_ << "DataTrafo::fillBf_for_PTDF: the line with id "; + exc_ << "TrafoContainer::fillBf_for_PTDF: the line with id "; exc_ << tr_id; exc_ << " is connected (hv side) to a disconnected bus while being connected"; throw std::runtime_error(exc_.str()); @@ -508,7 +509,7 @@ void DataTrafo::fillBf_for_PTDF(std::vector > & Bf, } -void DataTrafo::reconnect_connected_buses(std::vector & bus_status) const{ +void TrafoContainer::reconnect_connected_buses(std::vector & bus_status) const{ const Eigen::Index nb_trafo = nb(); for(Eigen::Index trafo_id = 0; trafo_id < nb_trafo; ++trafo_id){ @@ -519,7 +520,7 @@ void DataTrafo::reconnect_connected_buses(std::vector & bus_status) const{ if(bus_or_id_me == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataTrafo::reconnect_connected_buses: Trafo with id "; + exc_ << "TrafoContainer::reconnect_connected_buses: Trafo with id "; exc_ << trafo_id; exc_ << " is connected (hv) to bus '-1' (meaning disconnected) while you said it was disconnected. Have you called `gridmodel.deactivate_trafo(...)` ?."; throw std::runtime_error(exc_.str()); @@ -530,7 +531,7 @@ void DataTrafo::reconnect_connected_buses(std::vector & bus_status) const{ if(bus_ex_id_me == _deactivated_bus_id){ // TODO DEBUG MODE only this in debug mode std::ostringstream exc_; - exc_ << "DataTrafo::reconnect_connected_buses: Trafo with id "; + exc_ << "TrafoContainer::reconnect_connected_buses: Trafo with id "; exc_ << trafo_id; exc_ << " is connected (lv) to bus '-1' (meaning disconnected) while you said it was disconnected. Have you called `gridmodel.deactivate_trafo(...)` ?."; throw std::runtime_error(exc_.str()); @@ -539,7 +540,7 @@ void DataTrafo::reconnect_connected_buses(std::vector & bus_status) const{ } } -void DataTrafo::nb_line_end(std::vector & res) const{ +void TrafoContainer::nb_line_end(std::vector & res) const{ const Eigen::Index nb_trafo = nb(); for(Eigen::Index trafo_id = 0; trafo_id < nb_trafo; ++trafo_id){ @@ -552,7 +553,7 @@ void DataTrafo::nb_line_end(std::vector & res) const{ } } -void DataTrafo::get_graph(std::vector > & res) const +void TrafoContainer::get_graph(std::vector > & res) const { const auto my_size = nb(); for(Eigen::Index line_id = 0; line_id < my_size; ++line_id){ @@ -565,7 +566,7 @@ void DataTrafo::get_graph(std::vector > & res) const } } -void DataTrafo::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component) +void TrafoContainer::disconnect_if_not_in_main_component(std::vector & busbar_in_main_component) { const Eigen::Index nb_line = nb(); SolverControl unused_solver_control; diff --git a/src/DataTrafo.h b/src/element_container/TrafoContainer.h similarity index 93% rename from src/DataTrafo.h rename to src/element_container/TrafoContainer.h index 033a58a..6e15025 100644 --- a/src/DataTrafo.h +++ b/src/element_container/TrafoContainer.h @@ -1,4 +1,4 @@ -// Copyright (c) 2020, RTE (https://www.rte-france.com) +// Copyright (c) 2020-2023, RTE (https://www.rte-france.com) // See AUTHORS.txt // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,18 +6,17 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DATATRAFO_H -#define DATATRAFO_H +#ifndef TRAFO_CONTAINER_H +#define TRAFO_CONTAINER_H -#include "Utils.h" #include "Eigen/Core" #include "Eigen/Dense" #include "Eigen/SparseCore" #include "Eigen/SparseLU" - -#include "DataGeneric.h" +#include "Utils.h" +#include "GenericContainer.h" /** This class is a container for all transformers on the grid. @@ -30,7 +29,7 @@ The convention used for the transformer is the same as in pandapower: and for modeling of the Ybus matrix: https://pandapower.readthedocs.io/en/latest/elements/trafo.html#electric-model **/ -class DataTrafo : public DataGeneric +class TrafoContainer : public GenericContainer { public: class TrafoInfo @@ -61,7 +60,7 @@ class DataTrafo : public DataGeneric real_type res_a_lv_ka; real_type res_theta_lv_deg; - TrafoInfo(const DataTrafo & r_data_trafo, int my_id): + TrafoInfo(const TrafoContainer & r_data_trafo, int my_id): id(-1), name(""), connected(false), @@ -121,7 +120,7 @@ class DataTrafo : public DataGeneric typedef TrafoInfo DataInfo; private: - typedef DataConstIterator DataTrafoConstIterator; + typedef GenericContainerConstIterator TrafoContainerConstIterator; public: typedef std::tuple< @@ -137,7 +136,7 @@ class DataTrafo : public DataGeneric std::vector // shift_ > StateRes; - DataTrafo() {}; + TrafoContainer() {}; void init(const RealVect & trafo_r, const RealVect & trafo_x, @@ -151,15 +150,15 @@ class DataTrafo : public DataGeneric const Eigen::VectorXi & trafo_lv_id ); //pickle - DataTrafo::StateRes get_state() const; - void set_state(DataTrafo::StateRes & my_state ); + TrafoContainer::StateRes get_state() const; + void set_state(TrafoContainer::StateRes & my_state ); int nb() const { return static_cast(r_.size()); } // make it iterable - typedef DataTrafoConstIterator const_iterator_type; - const_iterator_type begin() const {return DataTrafoConstIterator(this, 0); } - const_iterator_type end() const {return DataTrafoConstIterator(this, nb()); } + typedef TrafoContainerConstIterator const_iterator_type; + const_iterator_type begin() const {return TrafoContainerConstIterator(this, 0); } + const_iterator_type end() const {return TrafoContainerConstIterator(this, nb()); } TrafoInfo operator[](int id) const { if(id < 0) @@ -288,4 +287,4 @@ class DataTrafo : public DataGeneric RealVect dc_x_tau_shift_; }; -#endif //DATATRAFO_H +#endif //TRAFO_CONTAINER_H diff --git a/src/help_fun_msg.cpp b/src/help_fun_msg.cpp index a33c1b6..48f58a9 100644 --- a/src/help_fun_msg.cpp +++ b/src/help_fun_msg.cpp @@ -808,12 +808,12 @@ const std::string DocIterator::has_res = R"mydelimiter( )mydelimiter"; -const std::string DocIterator::DataGen = R"mydelimiter( +const std::string DocIterator::GeneratorContainer = R"mydelimiter( This class allows to iterate through the generators of the :class:`lightsim2grid.gridmodel.GridModel` easily, as if they were in a python list. In lightsim2grid they are modeled as "pv" meanings you give the active production setpoint and voltage magnitude setpoint - (see :attr:`lightsim2grid.elements.DataSGen` for more exotic PQ generators). + (see :attr:`lightsim2grid.elements.SGenContainer` for more exotic PQ generators). The active production value setpoint are modified only for the generators participating to the slack buses (see :attr:`lightsim2grid.elements.GenInfo.is_slack` and :attr:`lightsim2grid.elements.GenInfo.slack_weight`). @@ -847,7 +847,8 @@ const std::string DocIterator::DataGen = R"mydelimiter( )mydelimiter"; const std::string DocIterator::GenInfo = R"mydelimiter( - This class represents what you get from retrieving some elements from :class:`lightsim2grid.elements.DataGen` + This class represents what you get from retrieving some elements from + :class:`lightsim2grid.elements.GeneratorContainer` It allows to read information from each generator of the powergrid. @@ -932,11 +933,12 @@ const std::string DocIterator::max_p_mw = R"mydelimiter( )mydelimiter"; -const std::string DocIterator::DataSGen = R"mydelimiter( +const std::string DocIterator::SGenContainer = R"mydelimiter( This class allows to iterate through the static generators of the :class:`lightsim2grid.gridmodel.GridModel` easily, as if they were in a python list. - In lightsim2grid they are two types of generators the more standard PV generators (see :attr:`lightsim2grid.elements.DataGen`). These + In lightsim2grid they are two types of generators the more standard PV generators (see + :attr:`lightsim2grid.elements.GeneratorContainer`). These are more exotic generators known as PQ, where you give the active production value and reactive production value. It's basically like loads, but using the generator convention (if the value is positive, it means power is taken from the grid to the element) @@ -972,7 +974,8 @@ const std::string DocIterator::DataSGen = R"mydelimiter( )mydelimiter"; const std::string DocIterator::SGenInfo = R"mydelimiter( - This class represents what you get from retrieving some elements from :class:`lightsim2grid.elements.DataSGen` + This class represents what you get from retrieving some elements from + :class:`lightsim2grid.elements.SGenContainer` It allows to read information from each static generator of the powergrid. @@ -1001,7 +1004,7 @@ const std::string DocIterator::SGenInfo = R"mydelimiter( )mydelimiter"; -const std::string DocIterator::DataLoad = R"mydelimiter( +const std::string DocIterator::LoadContainer = R"mydelimiter( This class allows to iterate through the loads **and storage units** of the :class:`lightsim2grid.gridmodel.GridModel` easily, as if they were in a python list. @@ -1049,7 +1052,8 @@ const std::string DocIterator::DataLoad = R"mydelimiter( )mydelimiter"; const std::string DocIterator::LoadInfo = R"mydelimiter( - This class represents what you get from retrieving some elements from :class:`lightsim2grid.elements.DataLoad`. + This class represents what you get from retrieving some elements from + :class:`lightsim2grid.elements.LoadContainer`. We remind the reader that storage units are also modeled as load in lightsim2grid. It allows to read information from each load / storage unit of the powergrid. @@ -1088,7 +1092,7 @@ const std::string DocIterator::LoadInfo = R"mydelimiter( )mydelimiter"; -const std::string DocIterator::DataShunt = R"mydelimiter( +const std::string DocIterator::ShuntContainer = R"mydelimiter( This class allows to iterate through the load of the :class:`lightsim2grid.gridmodel.GridModel` easily, as if they were in a python list. @@ -1122,7 +1126,8 @@ const std::string DocIterator::DataShunt = R"mydelimiter( )mydelimiter"; const std::string DocIterator::ShuntInfo = R"mydelimiter( - This class represents what you get from retrieving the shunts from :class:`lightsim2grid.elements.DataShunt`. + This class represents what you get from retrieving the shunts from + :class:`lightsim2grid.elements.ShuntContainer`. It allows to read information from each shunt of the powergrid. @@ -1150,7 +1155,7 @@ const std::string DocIterator::ShuntInfo = R"mydelimiter( )mydelimiter"; -const std::string DocIterator::DataTrafo = R"mydelimiter( +const std::string DocIterator::TrafoContainer = R"mydelimiter( This class allows to iterate through the transformers of the :class:`lightsim2grid.gridmodel.GridModel` easily, as if they were in a python list. @@ -1184,7 +1189,8 @@ const std::string DocIterator::DataTrafo = R"mydelimiter( )mydelimiter"; const std::string DocIterator::TrafoInfo = R"mydelimiter( - This class represents what you get from retrieving the transformers from :class:`lightsim2grid.elements.DataTrafo`. + This class represents what you get from retrieving the transformers from + :class:`lightsim2grid.elements.TrafoContainer`. It allows to read information from each transformer of the powergrid. @@ -1321,7 +1327,7 @@ const std::string DocIterator::res_a_hv_ka = R"mydelimiter( )mydelimiter" + DocIterator::only_avail_res; -const std::string DocIterator::DataLine = R"mydelimiter( +const std::string DocIterator::LineContainer = R"mydelimiter( This class allows to iterate through the powerlines of the :class:`lightsim2grid.gridmodel.GridModel` easily, as if they were in a python list. @@ -1355,7 +1361,8 @@ const std::string DocIterator::DataLine = R"mydelimiter( )mydelimiter"; const std::string DocIterator::LineInfo = R"mydelimiter( - This class represents what you get from retrieving the powerlines from :class:`lightsim2grid.elements.DataLine`. + This class represents what you get from retrieving the powerlines from + :class:`lightsim2grid.elements.LineContainer`. It allows to read information from each powerline of the powergrid. @@ -1475,7 +1482,7 @@ const std::string DocIterator::res_a_ex_ka = R"mydelimiter( )mydelimiter" + DocIterator::only_avail_res; -const std::string DocIterator::DataDCLine = R"mydelimiter( +const std::string DocIterator::DCLineContainer = R"mydelimiter( This class allows to iterate through the dc lines of the :class:`lightsim2grid.gridmodel.GridModel` easily, as if they were in a python list. @@ -1523,7 +1530,8 @@ const std::string DocIterator::DataDCLine = R"mydelimiter( const std::string DocIterator::DCLineInfo = R"mydelimiter( - This class represents what you get from retrieving the dc powerlines from :class:`lightsim2grid.elements.DataDCLine`. + This class represents what you get from retrieving the dc powerlines from + :class:`lightsim2grid.elements.DCLineContainer`. It allows to read information from each dc powerline of the powergrid. @@ -1789,7 +1797,8 @@ const std::string DocGridModel::get_dc_solver = R"mydelimiter( )mydelimiter"; const std::string DocGridModel::get_lines = R"mydelimiter( - This function allows to retrieve the powerlines (as a :class:`lightsim2grid.elements.DataLine` object, + This function allows to retrieve the powerlines (as a + :class:`lightsim2grid.elements.LineContainer` object, see :ref:`elements-modeled` for more information) Examples @@ -1807,7 +1816,8 @@ const std::string DocGridModel::get_lines = R"mydelimiter( )mydelimiter"; const std::string DocGridModel::get_trafos = R"mydelimiter( - This function allows to retrieve the transformers (as a :class:`lightsim2grid.elements.DataLine` object, + This function allows to retrieve the transformers (as a + :class:`lightsim2grid.elements.LineContainer` object, see :ref:`elements-modeled` for more information) Examples @@ -1825,7 +1835,8 @@ const std::string DocGridModel::get_trafos = R"mydelimiter( )mydelimiter"; const std::string DocGridModel::get_generators = R"mydelimiter( - This function allows to retrieve the (standard) generators (as a :class:`lightsim2grid.elements.DataGen` object, + This function allows to retrieve the (standard) generators (as a + :class:`lightsim2grid.elements.GeneratorContainer` object, see :ref:`elements-modeled` for more information) Examples @@ -1843,7 +1854,8 @@ const std::string DocGridModel::get_generators = R"mydelimiter( )mydelimiter"; const std::string DocGridModel::get_static_generators = R"mydelimiter( - This function allows to retrieve the (more exotic) static generators (as a :class:`lightsim2grid.elements.DataSGen` object, + This function allows to retrieve the (more exotic) static generators (as a + :class:`lightsim2grid.elements.SGenContainer` object, see :ref:`elements-modeled` for more information) Examples @@ -1861,7 +1873,8 @@ const std::string DocGridModel::get_static_generators = R"mydelimiter( )mydelimiter"; const std::string DocGridModel::get_shunts = R"mydelimiter( - This function allows to retrieve the shunts (as a :class:`lightsim2grid.elements.DataShunt` object, + This function allows to retrieve the shunts (as a + :class:`lightsim2grid.elements.ShuntContainer` object, see :ref:`elements-modeled` for more information) Examples @@ -1879,12 +1892,13 @@ const std::string DocGridModel::get_shunts = R"mydelimiter( )mydelimiter"; const std::string DocGridModel::get_storages = R"mydelimiter( - This function allows to retrieve the storage units (as a :class:`lightsim2grid.elements.DataLoad` object, + This function allows to retrieve the storage units (as a + :class:`lightsim2grid.elements.LoadContainer` object, see :ref:`elements-modeled` for more information) .. note:: We want to emphize that, as far as lightsim2grid is concerned, the storage units are modeled as loads. This is why - this function will return a :class:`lightsim2grid.elements.DataLoad`. + this function will return a :class:`lightsim2grid.elements.LoadContainer`. Examples --------- @@ -1901,7 +1915,7 @@ const std::string DocGridModel::get_storages = R"mydelimiter( )mydelimiter"; const std::string DocGridModel::get_loads = R"mydelimiter( - This function allows to retrieve the loads (as a :class:`lightsim2grid.elements.DataLoad` object, + This function allows to retrieve the loads (as a :class:`lightsim2grid.elements.LoadContainer` object, see :ref:`elements-modeled` for more information) Examples @@ -1920,7 +1934,8 @@ const std::string DocGridModel::get_loads = R"mydelimiter( )mydelimiter"; const std::string DocGridModel::get_dclines = R"mydelimiter( - This function allows to retrieve the dc powerlines (as a :class:`lightsim2grid.elements.DataDCLine` object, + This function allows to retrieve the dc powerlines (as a + :class:`lightsim2grid.elements.DCLineContainer` object, see :ref:`elements-modeled` for more information) Examples diff --git a/src/help_fun_msg.h b/src/help_fun_msg.h index 3f503b1..cf98db4 100644 --- a/src/help_fun_msg.h +++ b/src/help_fun_msg.h @@ -90,25 +90,25 @@ struct DocIterator static const std::string h_pu; // specific to generators - static const std::string DataGen; + static const std::string GeneratorContainer; static const std::string GenInfo; static const std::string is_slack; static const std::string slack_weight; // specific to sgens - static const std::string DataSGen; + static const std::string SGenContainer; static const std::string SGenInfo; // specific to loads (and storage units) - static const std::string DataLoad; + static const std::string LoadContainer; static const std::string LoadInfo; // specific to shunts - static const std::string DataShunt; + static const std::string ShuntContainer; static const std::string ShuntInfo; // specific to transformers - static const std::string DataTrafo; + static const std::string TrafoContainer; static const std::string TrafoInfo; static const std::string bus_hv_id; static const std::string bus_lv_id; @@ -127,7 +127,7 @@ struct DocIterator static const std::string res_theta_lv_deg; // specific to powerlines - static const std::string DataLine; + static const std::string LineContainer; static const std::string LineInfo; static const std::string bus_or_id; static const std::string bus_ex_id; @@ -144,7 +144,7 @@ struct DocIterator // specific to dc lines static const std::string dc_line_formula; - static const std::string DataDCLine; + static const std::string DCLineContainer; static const std::string DCLineInfo; static const std::string target_p_or_mw; static const std::string target_vm_or_pu; diff --git a/src/CKTSOSolver.cpp b/src/linear_solvers/CKTSOSolver.cpp similarity index 100% rename from src/CKTSOSolver.cpp rename to src/linear_solvers/CKTSOSolver.cpp diff --git a/src/CKTSOSolver.h b/src/linear_solvers/CKTSOSolver.h similarity index 100% rename from src/CKTSOSolver.h rename to src/linear_solvers/CKTSOSolver.h diff --git a/src/KLUSolver.cpp b/src/linear_solvers/KLUSolver.cpp similarity index 100% rename from src/KLUSolver.cpp rename to src/linear_solvers/KLUSolver.cpp diff --git a/src/KLUSolver.h b/src/linear_solvers/KLUSolver.h similarity index 100% rename from src/KLUSolver.h rename to src/linear_solvers/KLUSolver.h diff --git a/src/NICSLUSolver.cpp b/src/linear_solvers/NICSLUSolver.cpp similarity index 100% rename from src/NICSLUSolver.cpp rename to src/linear_solvers/NICSLUSolver.cpp diff --git a/src/NICSLUSolver.h b/src/linear_solvers/NICSLUSolver.h similarity index 100% rename from src/NICSLUSolver.h rename to src/linear_solvers/NICSLUSolver.h diff --git a/src/SparseLUSolver.cpp b/src/linear_solvers/SparseLUSolver.cpp similarity index 100% rename from src/SparseLUSolver.cpp rename to src/linear_solvers/SparseLUSolver.cpp diff --git a/src/SparseLUSolver.h b/src/linear_solvers/SparseLUSolver.h similarity index 100% rename from src/SparseLUSolver.h rename to src/linear_solvers/SparseLUSolver.h diff --git a/src/main.cpp b/src/main.cpp index a743bba..453e3d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -349,31 +349,31 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) #endif // CKTSO_SOLVER_AVAILABLE (or _READ_THE_DOCS) - py::class_(m, "GaussSeidelSolver", DocSolver::GaussSeidelSolver.c_str()) + py::class_(m, "GaussSeidelSolver", DocSolver::GaussSeidelSolver.c_str()) .def(py::init<>()) - .def("get_Va", &GaussSeidelSolver::get_Va, DocSolver::get_Va.c_str()) // get the voltage angle vector (vector of double) - .def("get_Vm", &GaussSeidelSolver::get_Vm, DocSolver::get_Vm.c_str()) // get the voltage magnitude vector (vector of double) - .def("get_V", &GaussSeidelSolver::get_V, DocSolver::get_V.c_str()) - .def("get_error", &GaussSeidelSolver::get_error, DocSolver::get_error.c_str()) // get the error message, see the definition of "err_" for more information - .def("get_nb_iter", &GaussSeidelSolver::get_nb_iter, DocSolver::get_nb_iter.c_str()) // return the number of iteration performed at the last optimization - .def("reset", &GaussSeidelSolver::reset, DocSolver::reset.c_str()) // reset the solver to its original state - .def("converged", &GaussSeidelSolver::converged, DocSolver::converged.c_str()) // whether the solver has converged - .def("compute_pf", &GaussSeidelSolver::compute_pf, py::call_guard(), DocSolver::compute_pf.c_str()) // compute the powerflow - .def("get_timers", &GaussSeidelSolver::get_timers, DocSolver::get_timers.c_str()) // returns the timers corresponding to times the solver spent in different part - .def("solve", &GaussSeidelSolver::compute_pf, py::call_guard(), DocSolver::compute_pf.c_str()); // perform the newton raphson optimization - - py::class_(m, "GaussSeidelSynchSolver", DocSolver::GaussSeidelSynchSolver.c_str()) + .def("get_Va", &GaussSeidelAlgo::get_Va, DocSolver::get_Va.c_str()) // get the voltage angle vector (vector of double) + .def("get_Vm", &GaussSeidelAlgo::get_Vm, DocSolver::get_Vm.c_str()) // get the voltage magnitude vector (vector of double) + .def("get_V", &GaussSeidelAlgo::get_V, DocSolver::get_V.c_str()) + .def("get_error", &GaussSeidelAlgo::get_error, DocSolver::get_error.c_str()) // get the error message, see the definition of "err_" for more information + .def("get_nb_iter", &GaussSeidelAlgo::get_nb_iter, DocSolver::get_nb_iter.c_str()) // return the number of iteration performed at the last optimization + .def("reset", &GaussSeidelAlgo::reset, DocSolver::reset.c_str()) // reset the solver to its original state + .def("converged", &GaussSeidelAlgo::converged, DocSolver::converged.c_str()) // whether the solver has converged + .def("compute_pf", &GaussSeidelAlgo::compute_pf, py::call_guard(), DocSolver::compute_pf.c_str()) // compute the powerflow + .def("get_timers", &GaussSeidelAlgo::get_timers, DocSolver::get_timers.c_str()) // returns the timers corresponding to times the solver spent in different part + .def("solve", &GaussSeidelAlgo::compute_pf, py::call_guard(), DocSolver::compute_pf.c_str()); // perform the newton raphson optimization + + py::class_(m, "GaussSeidelSynchSolver", DocSolver::GaussSeidelSynchSolver.c_str()) .def(py::init<>()) - .def("get_Va", &GaussSeidelSynchSolver::get_Va, DocSolver::get_Va.c_str()) // get the voltage angle vector (vector of double) - .def("get_Vm", &GaussSeidelSynchSolver::get_Vm, DocSolver::get_Vm.c_str()) // get the voltage magnitude vector (vector of double) - .def("get_V", &GaussSeidelSynchSolver::get_V, DocSolver::get_V.c_str()) - .def("get_error", &GaussSeidelSynchSolver::get_error, DocSolver::get_error.c_str()) // get the error message, see the definition of "err_" for more information - .def("get_nb_iter", &GaussSeidelSynchSolver::get_nb_iter, DocSolver::get_nb_iter.c_str()) // return the number of iteration performed at the last optimization - .def("reset", &GaussSeidelSynchSolver::reset, DocSolver::reset.c_str()) // reset the solver to its original state - .def("converged", &GaussSeidelSynchSolver::converged, DocSolver::converged.c_str()) // whether the solver has converged - .def("compute_pf", &GaussSeidelSynchSolver::compute_pf, py::call_guard(), DocSolver::compute_pf.c_str()) // compute the powerflow - .def("get_timers", &GaussSeidelSynchSolver::get_timers, DocSolver::get_timers.c_str()) // returns the timers corresponding to times the solver spent in different part - .def("solve", &GaussSeidelSynchSolver::compute_pf, py::call_guard(), DocSolver::compute_pf.c_str()); // perform the newton raphson optimization + .def("get_Va", &GaussSeidelSynchAlgo::get_Va, DocSolver::get_Va.c_str()) // get the voltage angle vector (vector of double) + .def("get_Vm", &GaussSeidelSynchAlgo::get_Vm, DocSolver::get_Vm.c_str()) // get the voltage magnitude vector (vector of double) + .def("get_V", &GaussSeidelSynchAlgo::get_V, DocSolver::get_V.c_str()) + .def("get_error", &GaussSeidelSynchAlgo::get_error, DocSolver::get_error.c_str()) // get the error message, see the definition of "err_" for more information + .def("get_nb_iter", &GaussSeidelSynchAlgo::get_nb_iter, DocSolver::get_nb_iter.c_str()) // return the number of iteration performed at the last optimization + .def("reset", &GaussSeidelSynchAlgo::reset, DocSolver::reset.c_str()) // reset the solver to its original state + .def("converged", &GaussSeidelSynchAlgo::converged, DocSolver::converged.c_str()) // whether the solver has converged + .def("compute_pf", &GaussSeidelSynchAlgo::compute_pf, py::call_guard(), DocSolver::compute_pf.c_str()) // compute the powerflow + .def("get_timers", &GaussSeidelSynchAlgo::get_timers, DocSolver::get_timers.c_str()) // returns the timers corresponding to times the solver spent in different part + .def("solve", &GaussSeidelSynchAlgo::compute_pf, py::call_guard(), DocSolver::compute_pf.c_str()); // perform the newton raphson optimization // Only "const" method are exported // it is so that i cannot modify the internal solver of a gridmodel python side @@ -396,192 +396,192 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def("get_fdpf_bx_lu", &ChooseSolver::get_fdpf_bx_lu, py::return_value_policy::reference, DocGridModel::_internal_do_not_use.c_str()); // iterator for generators - py::class_(m, "DataGen", DocIterator::DataGen.c_str()) - .def("__len__", [](const DataGen & data) { return data.nb(); }) - .def("__getitem__", [](const DataGen & data, int k){return data[k]; } ) - .def("__iter__", [](const DataGen & data) { + py::class_(m, "GeneratorContainer", DocIterator::GeneratorContainer.c_str()) + .def("__len__", [](const GeneratorContainer & data) { return data.nb(); }) + .def("__getitem__", [](const GeneratorContainer & data, int k){return data[k]; } ) + .def("__iter__", [](const GeneratorContainer & data) { return py::make_iterator(data.begin(), data.end()); }, py::keep_alive<0, 1>()); /* Keep vector alive while iterator is used */ - py::class_(m, "GenInfo", DocIterator::GenInfo.c_str()) - .def_readonly("id", &DataGen::GenInfo::id, DocIterator::id.c_str()) - .def_readonly("name", &DataGen::GenInfo::name, DocIterator::name.c_str()) - .def_readonly("connected", &DataGen::GenInfo::connected, DocIterator::connected.c_str()) - .def_readonly("bus_id", &DataGen::GenInfo::bus_id, DocIterator::bus_id.c_str()) - .def_readonly("is_slack", &DataGen::GenInfo::is_slack, DocIterator::is_slack.c_str()) - .def_readonly("slack_weight", &DataGen::GenInfo::slack_weight, DocIterator::slack_weight.c_str()) - .def_readonly("voltage_regulator_on", &DataGen::GenInfo::voltage_regulator_on, "TODO") - .def_readonly("target_p_mw", &DataGen::GenInfo::target_p_mw, DocIterator::target_p_mw.c_str()) - .def_readonly("target_vm_pu", &DataGen::GenInfo::target_vm_pu, DocIterator::target_vm_pu.c_str()) - .def_readonly("target_q_mvar", &DataGen::GenInfo::target_q_mvar, "TODO") - .def_readonly("min_q_mvar", &DataGen::GenInfo::min_q_mvar, DocIterator::min_q_mvar.c_str()) - .def_readonly("max_q_mvar", &DataGen::GenInfo::max_q_mvar, DocIterator::max_q_mvar.c_str()) - .def_readonly("has_res", &DataGen::GenInfo::has_res, DocIterator::has_res.c_str()) - .def_readonly("res_p_mw", &DataGen::GenInfo::res_p_mw, DocIterator::res_p_mw.c_str()) - .def_readonly("res_q_mvar", &DataGen::GenInfo::res_q_mvar, DocIterator::res_q_mvar.c_str()) - .def_readonly("res_theta_deg", &DataGen::GenInfo::res_theta_deg, DocIterator::res_theta_deg.c_str()) - .def_readonly("res_v_kv", &DataGen::GenInfo::res_v_kv, DocIterator::res_v_kv.c_str()); + py::class_(m, "GenInfo", DocIterator::GenInfo.c_str()) + .def_readonly("id", &GeneratorContainer::GenInfo::id, DocIterator::id.c_str()) + .def_readonly("name", &GeneratorContainer::GenInfo::name, DocIterator::name.c_str()) + .def_readonly("connected", &GeneratorContainer::GenInfo::connected, DocIterator::connected.c_str()) + .def_readonly("bus_id", &GeneratorContainer::GenInfo::bus_id, DocIterator::bus_id.c_str()) + .def_readonly("is_slack", &GeneratorContainer::GenInfo::is_slack, DocIterator::is_slack.c_str()) + .def_readonly("slack_weight", &GeneratorContainer::GenInfo::slack_weight, DocIterator::slack_weight.c_str()) + .def_readonly("voltage_regulator_on", &GeneratorContainer::GenInfo::voltage_regulator_on, "TODO") + .def_readonly("target_p_mw", &GeneratorContainer::GenInfo::target_p_mw, DocIterator::target_p_mw.c_str()) + .def_readonly("target_vm_pu", &GeneratorContainer::GenInfo::target_vm_pu, DocIterator::target_vm_pu.c_str()) + .def_readonly("target_q_mvar", &GeneratorContainer::GenInfo::target_q_mvar, "TODO") + .def_readonly("min_q_mvar", &GeneratorContainer::GenInfo::min_q_mvar, DocIterator::min_q_mvar.c_str()) + .def_readonly("max_q_mvar", &GeneratorContainer::GenInfo::max_q_mvar, DocIterator::max_q_mvar.c_str()) + .def_readonly("has_res", &GeneratorContainer::GenInfo::has_res, DocIterator::has_res.c_str()) + .def_readonly("res_p_mw", &GeneratorContainer::GenInfo::res_p_mw, DocIterator::res_p_mw.c_str()) + .def_readonly("res_q_mvar", &GeneratorContainer::GenInfo::res_q_mvar, DocIterator::res_q_mvar.c_str()) + .def_readonly("res_theta_deg", &GeneratorContainer::GenInfo::res_theta_deg, DocIterator::res_theta_deg.c_str()) + .def_readonly("res_v_kv", &GeneratorContainer::GenInfo::res_v_kv, DocIterator::res_v_kv.c_str()); // iterator for sgens - py::class_(m, "DataSGen", DocIterator::DataSGen.c_str()) - .def("__len__", [](const DataSGen & data) { return data.nb(); }) - .def("__getitem__", [](const DataSGen & data, int k){return data[k]; } ) - .def("__iter__", [](const DataSGen & data) { + py::class_(m, "SGenContainer", DocIterator::SGenContainer.c_str()) + .def("__len__", [](const SGenContainer & data) { return data.nb(); }) + .def("__getitem__", [](const SGenContainer & data, int k){return data[k]; } ) + .def("__iter__", [](const SGenContainer & data) { return py::make_iterator(data.begin(), data.end()); }, py::keep_alive<0, 1>()); /* Keep vector alive while iterator is used */ - py::class_(m, "SGenInfo", DocIterator::SGenInfo.c_str()) - .def_readonly("id", &DataSGen::SGenInfo::id, DocIterator::id.c_str()) - .def_readonly("name", &DataSGen::SGenInfo::name, DocIterator::name.c_str()) - .def_readonly("connected", &DataSGen::SGenInfo::connected, DocIterator::connected.c_str()) - .def_readonly("bus_id", &DataSGen::SGenInfo::bus_id, DocIterator::bus_id.c_str()) - .def_readonly("min_q_mvar", &DataSGen::SGenInfo::min_q_mvar, DocIterator::min_q_mvar.c_str()) - .def_readonly("max_q_mvar", &DataSGen::SGenInfo::max_q_mvar, DocIterator::max_q_mvar.c_str()) - .def_readonly("min_p_mw", &DataSGen::SGenInfo::min_p_mw, DocIterator::min_p_mw.c_str()) - .def_readonly("max_p_mw", &DataSGen::SGenInfo::max_p_mw, DocIterator::max_p_mw.c_str()) - .def_readonly("target_p_mw", &DataSGen::SGenInfo::target_p_mw, DocIterator::target_p_mw.c_str()) - .def_readonly("target_q_mvar", &DataSGen::SGenInfo::target_q_mvar, DocIterator::target_q_mvar.c_str()) - .def_readonly("has_res", &DataSGen::SGenInfo::has_res, DocIterator::has_res.c_str()) - .def_readonly("res_p_mw", &DataSGen::SGenInfo::res_p_mw, DocIterator::res_p_mw.c_str()) - .def_readonly("res_q_mvar", &DataSGen::SGenInfo::res_q_mvar, DocIterator::res_q_mvar.c_str()) - .def_readonly("res_theta_deg", &DataSGen::SGenInfo::res_theta_deg, DocIterator::res_theta_deg.c_str()) - .def_readonly("res_v_kv", &DataSGen::SGenInfo::res_v_kv, DocIterator::res_v_kv.c_str()); + py::class_(m, "SGenInfo", DocIterator::SGenInfo.c_str()) + .def_readonly("id", &SGenContainer::SGenInfo::id, DocIterator::id.c_str()) + .def_readonly("name", &SGenContainer::SGenInfo::name, DocIterator::name.c_str()) + .def_readonly("connected", &SGenContainer::SGenInfo::connected, DocIterator::connected.c_str()) + .def_readonly("bus_id", &SGenContainer::SGenInfo::bus_id, DocIterator::bus_id.c_str()) + .def_readonly("min_q_mvar", &SGenContainer::SGenInfo::min_q_mvar, DocIterator::min_q_mvar.c_str()) + .def_readonly("max_q_mvar", &SGenContainer::SGenInfo::max_q_mvar, DocIterator::max_q_mvar.c_str()) + .def_readonly("min_p_mw", &SGenContainer::SGenInfo::min_p_mw, DocIterator::min_p_mw.c_str()) + .def_readonly("max_p_mw", &SGenContainer::SGenInfo::max_p_mw, DocIterator::max_p_mw.c_str()) + .def_readonly("target_p_mw", &SGenContainer::SGenInfo::target_p_mw, DocIterator::target_p_mw.c_str()) + .def_readonly("target_q_mvar", &SGenContainer::SGenInfo::target_q_mvar, DocIterator::target_q_mvar.c_str()) + .def_readonly("has_res", &SGenContainer::SGenInfo::has_res, DocIterator::has_res.c_str()) + .def_readonly("res_p_mw", &SGenContainer::SGenInfo::res_p_mw, DocIterator::res_p_mw.c_str()) + .def_readonly("res_q_mvar", &SGenContainer::SGenInfo::res_q_mvar, DocIterator::res_q_mvar.c_str()) + .def_readonly("res_theta_deg", &SGenContainer::SGenInfo::res_theta_deg, DocIterator::res_theta_deg.c_str()) + .def_readonly("res_v_kv", &SGenContainer::SGenInfo::res_v_kv, DocIterator::res_v_kv.c_str()); // iterator for loads (and storage units) - py::class_(m, "DataLoad", DocIterator::DataLoad.c_str()) - .def("__len__", [](const DataLoad & data) { return data.nb(); }) - .def("__getitem__", [](const DataLoad & data, int k){return data[k]; } ) - .def("__iter__", [](const DataLoad & data) { + py::class_(m, "LoadContainer", DocIterator::LoadContainer.c_str()) + .def("__len__", [](const LoadContainer & data) { return data.nb(); }) + .def("__getitem__", [](const LoadContainer & data, int k){return data[k]; } ) + .def("__iter__", [](const LoadContainer & data) { return py::make_iterator(data.begin(), data.end()); }, py::keep_alive<0, 1>()); /* Keep vector alive while iterator is used */ - py::class_(m, "LoadInfo", DocIterator::LoadInfo.c_str()) - .def_readonly("id", &DataLoad::LoadInfo::id, DocIterator::id.c_str()) - .def_readonly("name", &DataLoad::LoadInfo::name, DocIterator::name.c_str()) - .def_readonly("connected", &DataLoad::LoadInfo::connected, DocIterator::connected.c_str()) - .def_readonly("bus_id", &DataLoad::LoadInfo::bus_id, DocIterator::bus_id.c_str()) - .def_readonly("target_p_mw", &DataLoad::LoadInfo::target_p_mw, DocIterator::target_p_mw.c_str()) - .def_readonly("target_q_mvar", &DataLoad::LoadInfo::target_q_mvar, DocIterator::target_q_mvar.c_str()) - .def_readonly("has_res", &DataLoad::LoadInfo::has_res, DocIterator::has_res.c_str()) - .def_readonly("res_p_mw", &DataLoad::LoadInfo::res_p_mw, DocIterator::res_p_mw.c_str()) - .def_readonly("res_q_mvar", &DataLoad::LoadInfo::res_q_mvar, DocIterator::res_q_mvar.c_str()) - .def_readonly("res_theta_deg", &DataLoad::LoadInfo::res_theta_deg, DocIterator::res_theta_deg.c_str()) - .def_readonly("res_v_kv", &DataLoad::LoadInfo::res_v_kv, DocIterator::res_v_kv.c_str()); + py::class_(m, "LoadInfo", DocIterator::LoadInfo.c_str()) + .def_readonly("id", &LoadContainer::LoadInfo::id, DocIterator::id.c_str()) + .def_readonly("name", &LoadContainer::LoadInfo::name, DocIterator::name.c_str()) + .def_readonly("connected", &LoadContainer::LoadInfo::connected, DocIterator::connected.c_str()) + .def_readonly("bus_id", &LoadContainer::LoadInfo::bus_id, DocIterator::bus_id.c_str()) + .def_readonly("target_p_mw", &LoadContainer::LoadInfo::target_p_mw, DocIterator::target_p_mw.c_str()) + .def_readonly("target_q_mvar", &LoadContainer::LoadInfo::target_q_mvar, DocIterator::target_q_mvar.c_str()) + .def_readonly("has_res", &LoadContainer::LoadInfo::has_res, DocIterator::has_res.c_str()) + .def_readonly("res_p_mw", &LoadContainer::LoadInfo::res_p_mw, DocIterator::res_p_mw.c_str()) + .def_readonly("res_q_mvar", &LoadContainer::LoadInfo::res_q_mvar, DocIterator::res_q_mvar.c_str()) + .def_readonly("res_theta_deg", &LoadContainer::LoadInfo::res_theta_deg, DocIterator::res_theta_deg.c_str()) + .def_readonly("res_v_kv", &LoadContainer::LoadInfo::res_v_kv, DocIterator::res_v_kv.c_str()); // iterator for shunts - py::class_(m, "DataShunt", DocIterator::DataShunt.c_str()) - .def("__len__", [](const DataShunt & data) { return data.nb(); }) - .def("__getitem__", [](const DataShunt & data, int k){return data[k]; } ) - .def("__iter__", [](const DataShunt & data) { + py::class_(m, "ShuntContainer", DocIterator::ShuntContainer.c_str()) + .def("__len__", [](const ShuntContainer & data) { return data.nb(); }) + .def("__getitem__", [](const ShuntContainer & data, int k){return data[k]; } ) + .def("__iter__", [](const ShuntContainer & data) { return py::make_iterator(data.begin(), data.end()); }, py::keep_alive<0, 1>()); /* Keep vector alive while iterator is used */ - py::class_(m, "ShuntInfo", DocIterator::ShuntInfo.c_str()) - .def_readonly("id", &DataShunt::ShuntInfo::id, DocIterator::id.c_str()) - .def_readonly("name", &DataShunt::ShuntInfo::name, DocIterator::name.c_str()) - .def_readonly("connected", &DataShunt::ShuntInfo::connected, DocIterator::connected.c_str()) - .def_readonly("bus_id", &DataShunt::ShuntInfo::bus_id, DocIterator::bus_id.c_str()) - .def_readonly("target_p_mw", &DataShunt::ShuntInfo::target_p_mw, DocIterator::target_p_mw.c_str()) - .def_readonly("target_q_mvar", &DataShunt::ShuntInfo::target_q_mvar, DocIterator::target_q_mvar.c_str()) - .def_readonly("has_res", &DataShunt::ShuntInfo::has_res, DocIterator::has_res.c_str()) - .def_readonly("res_p_mw", &DataShunt::ShuntInfo::res_p_mw, DocIterator::res_p_mw.c_str()) - .def_readonly("res_q_mvar", &DataShunt::ShuntInfo::res_q_mvar, DocIterator::res_q_mvar.c_str()) - .def_readonly("res_theta_deg", &DataShunt::ShuntInfo::res_theta_deg, DocIterator::res_theta_deg.c_str()) - .def_readonly("res_v_kv", &DataShunt::ShuntInfo::res_v_kv, DocIterator::res_v_kv.c_str()); + py::class_(m, "ShuntInfo", DocIterator::ShuntInfo.c_str()) + .def_readonly("id", &ShuntContainer::ShuntInfo::id, DocIterator::id.c_str()) + .def_readonly("name", &ShuntContainer::ShuntInfo::name, DocIterator::name.c_str()) + .def_readonly("connected", &ShuntContainer::ShuntInfo::connected, DocIterator::connected.c_str()) + .def_readonly("bus_id", &ShuntContainer::ShuntInfo::bus_id, DocIterator::bus_id.c_str()) + .def_readonly("target_p_mw", &ShuntContainer::ShuntInfo::target_p_mw, DocIterator::target_p_mw.c_str()) + .def_readonly("target_q_mvar", &ShuntContainer::ShuntInfo::target_q_mvar, DocIterator::target_q_mvar.c_str()) + .def_readonly("has_res", &ShuntContainer::ShuntInfo::has_res, DocIterator::has_res.c_str()) + .def_readonly("res_p_mw", &ShuntContainer::ShuntInfo::res_p_mw, DocIterator::res_p_mw.c_str()) + .def_readonly("res_q_mvar", &ShuntContainer::ShuntInfo::res_q_mvar, DocIterator::res_q_mvar.c_str()) + .def_readonly("res_theta_deg", &ShuntContainer::ShuntInfo::res_theta_deg, DocIterator::res_theta_deg.c_str()) + .def_readonly("res_v_kv", &ShuntContainer::ShuntInfo::res_v_kv, DocIterator::res_v_kv.c_str()); // iterator for trafos - py::class_(m, "DataTrafo", DocIterator::DataTrafo.c_str()) - .def("__len__", [](const DataTrafo & data) { return data.nb(); }) - .def("__getitem__", [](const DataTrafo & data, int k){return data[k]; } ) - .def("__iter__", [](const DataTrafo & data) { + py::class_(m, "TrafoContainer", DocIterator::TrafoContainer.c_str()) + .def("__len__", [](const TrafoContainer & data) { return data.nb(); }) + .def("__getitem__", [](const TrafoContainer & data, int k){return data[k]; } ) + .def("__iter__", [](const TrafoContainer & data) { return py::make_iterator(data.begin(), data.end()); }, py::keep_alive<0, 1>()); /* Keep vector alive while iterator is used */ - py::class_(m, "TrafoInfo", DocIterator::TrafoInfo.c_str()) - .def_readonly("id", &DataTrafo::TrafoInfo::id, DocIterator::id.c_str()) - .def_readonly("name", &DataTrafo::TrafoInfo::name, DocIterator::name.c_str()) - .def_readonly("connected", &DataTrafo::TrafoInfo::connected, DocIterator::connected.c_str()) - .def_readonly("bus_hv_id", &DataTrafo::TrafoInfo::bus_hv_id, DocIterator::bus_hv_id.c_str()) - .def_readonly("bus_lv_id", &DataTrafo::TrafoInfo::bus_lv_id, DocIterator::bus_lv_id.c_str()) - .def_readonly("r_pu", &DataTrafo::TrafoInfo::r_pu, DocIterator::r_pu.c_str()) - .def_readonly("x_pu", &DataTrafo::TrafoInfo::x_pu, DocIterator::x_pu.c_str()) - .def_readonly("h_pu", &DataTrafo::TrafoInfo::h_pu, DocIterator::h_pu.c_str()) - .def_readonly("is_tap_hv_side", &DataTrafo::TrafoInfo::is_tap_hv_side, DocIterator::is_tap_hv_side.c_str()) - .def_readonly("ratio", &DataTrafo::TrafoInfo::ratio, DocIterator::ratio.c_str()) - .def_readonly("shift_rad", &DataTrafo::TrafoInfo::shift_rad, DocIterator::shift_rad.c_str()) - .def_readonly("has_res", &DataTrafo::TrafoInfo::has_res, DocIterator::has_res.c_str()) - .def_readonly("res_p_hv_mw", &DataTrafo::TrafoInfo::res_p_hv_mw, DocIterator::res_p_hv_mw.c_str()) - .def_readonly("res_q_hv_mvar", &DataTrafo::TrafoInfo::res_q_hv_mvar, DocIterator::res_q_hv_mvar.c_str()) - .def_readonly("res_v_hv_kv", &DataTrafo::TrafoInfo::res_v_hv_kv, DocIterator::res_v_hv_kv.c_str()) - .def_readonly("res_a_hv_ka", &DataTrafo::TrafoInfo::res_a_hv_ka, DocIterator::res_a_hv_ka.c_str()) - .def_readonly("res_p_lv_mw", &DataTrafo::TrafoInfo::res_p_lv_mw, DocIterator::res_p_lv_mw.c_str()) - .def_readonly("res_q_lv_mvar", &DataTrafo::TrafoInfo::res_q_lv_mvar, DocIterator::res_q_lv_mvar.c_str()) - .def_readonly("res_v_lv_kv", &DataTrafo::TrafoInfo::res_v_lv_kv, DocIterator::res_v_lv_kv.c_str()) - .def_readonly("res_a_lv_ka", &DataTrafo::TrafoInfo::res_a_lv_ka, DocIterator::res_a_lv_ka.c_str()) - .def_readonly("res_theta_hv_deg", &DataTrafo::TrafoInfo::res_theta_hv_deg, DocIterator::res_theta_hv_deg.c_str()) - .def_readonly("res_theta_lv_deg", &DataTrafo::TrafoInfo::res_theta_lv_deg, DocIterator::res_theta_lv_deg.c_str()); + py::class_(m, "TrafoInfo", DocIterator::TrafoInfo.c_str()) + .def_readonly("id", &TrafoContainer::TrafoInfo::id, DocIterator::id.c_str()) + .def_readonly("name", &TrafoContainer::TrafoInfo::name, DocIterator::name.c_str()) + .def_readonly("connected", &TrafoContainer::TrafoInfo::connected, DocIterator::connected.c_str()) + .def_readonly("bus_hv_id", &TrafoContainer::TrafoInfo::bus_hv_id, DocIterator::bus_hv_id.c_str()) + .def_readonly("bus_lv_id", &TrafoContainer::TrafoInfo::bus_lv_id, DocIterator::bus_lv_id.c_str()) + .def_readonly("r_pu", &TrafoContainer::TrafoInfo::r_pu, DocIterator::r_pu.c_str()) + .def_readonly("x_pu", &TrafoContainer::TrafoInfo::x_pu, DocIterator::x_pu.c_str()) + .def_readonly("h_pu", &TrafoContainer::TrafoInfo::h_pu, DocIterator::h_pu.c_str()) + .def_readonly("is_tap_hv_side", &TrafoContainer::TrafoInfo::is_tap_hv_side, DocIterator::is_tap_hv_side.c_str()) + .def_readonly("ratio", &TrafoContainer::TrafoInfo::ratio, DocIterator::ratio.c_str()) + .def_readonly("shift_rad", &TrafoContainer::TrafoInfo::shift_rad, DocIterator::shift_rad.c_str()) + .def_readonly("has_res", &TrafoContainer::TrafoInfo::has_res, DocIterator::has_res.c_str()) + .def_readonly("res_p_hv_mw", &TrafoContainer::TrafoInfo::res_p_hv_mw, DocIterator::res_p_hv_mw.c_str()) + .def_readonly("res_q_hv_mvar", &TrafoContainer::TrafoInfo::res_q_hv_mvar, DocIterator::res_q_hv_mvar.c_str()) + .def_readonly("res_v_hv_kv", &TrafoContainer::TrafoInfo::res_v_hv_kv, DocIterator::res_v_hv_kv.c_str()) + .def_readonly("res_a_hv_ka", &TrafoContainer::TrafoInfo::res_a_hv_ka, DocIterator::res_a_hv_ka.c_str()) + .def_readonly("res_p_lv_mw", &TrafoContainer::TrafoInfo::res_p_lv_mw, DocIterator::res_p_lv_mw.c_str()) + .def_readonly("res_q_lv_mvar", &TrafoContainer::TrafoInfo::res_q_lv_mvar, DocIterator::res_q_lv_mvar.c_str()) + .def_readonly("res_v_lv_kv", &TrafoContainer::TrafoInfo::res_v_lv_kv, DocIterator::res_v_lv_kv.c_str()) + .def_readonly("res_a_lv_ka", &TrafoContainer::TrafoInfo::res_a_lv_ka, DocIterator::res_a_lv_ka.c_str()) + .def_readonly("res_theta_hv_deg", &TrafoContainer::TrafoInfo::res_theta_hv_deg, DocIterator::res_theta_hv_deg.c_str()) + .def_readonly("res_theta_lv_deg", &TrafoContainer::TrafoInfo::res_theta_lv_deg, DocIterator::res_theta_lv_deg.c_str()); // iterator for trafos - py::class_(m, "DataLine", DocIterator::DataLine.c_str()) - .def("__len__", [](const DataLine & data) { return data.nb(); }) - .def("__getitem__", [](const DataLine & data, int k){return data[k]; } ) - .def("__iter__", [](const DataLine & data) { + py::class_(m, "LineContainer", DocIterator::LineContainer.c_str()) + .def("__len__", [](const LineContainer & data) { return data.nb(); }) + .def("__getitem__", [](const LineContainer & data, int k){return data[k]; } ) + .def("__iter__", [](const LineContainer & data) { return py::make_iterator(data.begin(), data.end()); }, py::keep_alive<0, 1>()); /* Keep vector alive while iterator is used */ - py::class_(m, "LineInfo", DocIterator::LineInfo.c_str()) - .def_readonly("id", &DataLine::LineInfo::id, DocIterator::id.c_str()) - .def_readonly("name", &DataLine::LineInfo::name, DocIterator::name.c_str()) - .def_readonly("connected", &DataLine::LineInfo::connected, DocIterator::connected.c_str()) - .def_readonly("bus_or_id", &DataLine::LineInfo::bus_or_id, DocIterator::bus_or_id.c_str()) - .def_readonly("bus_ex_id", &DataLine::LineInfo::bus_ex_id, DocIterator::bus_ex_id.c_str()) - .def_readonly("r_pu", &DataLine::LineInfo::r_pu, DocIterator::r_pu.c_str()) - .def_readonly("x_pu", &DataLine::LineInfo::x_pu, DocIterator::x_pu.c_str()) - .def_readonly("h_pu", &DataLine::LineInfo::h_pu, DocIterator::x_pu.c_str()) - .def_readonly("h_or_pu", &DataLine::LineInfo::h_or_pu, DocIterator::h_pu.c_str()) - .def_readonly("h_ex_pu", &DataLine::LineInfo::h_ex_pu, DocIterator::h_pu.c_str()) - .def_readonly("has_res", &DataLine::LineInfo::has_res, DocIterator::has_res.c_str()) - .def_readonly("res_p_or_mw", &DataLine::LineInfo::res_p_or_mw, DocIterator::res_p_or_mw.c_str()) - .def_readonly("res_q_or_mvar", &DataLine::LineInfo::res_q_or_mvar, DocIterator::res_q_or_mvar.c_str()) - .def_readonly("res_v_or_kv", &DataLine::LineInfo::res_v_or_kv, DocIterator::res_v_or_kv.c_str()) - .def_readonly("res_a_or_ka", &DataLine::LineInfo::res_a_or_ka, DocIterator::res_a_or_ka.c_str()) - .def_readonly("res_p_ex_mw", &DataLine::LineInfo::res_p_ex_mw, DocIterator::res_p_ex_mw.c_str()) - .def_readonly("res_q_ex_mvar", &DataLine::LineInfo::res_q_ex_mvar, DocIterator::res_q_ex_mvar.c_str()) - .def_readonly("res_v_ex_kv", &DataLine::LineInfo::res_v_ex_kv, DocIterator::res_v_ex_kv.c_str()) - .def_readonly("res_a_ex_ka", &DataLine::LineInfo::res_a_ex_ka, DocIterator::res_a_ex_ka.c_str()) - .def_readonly("res_theta_or_deg", &DataLine::LineInfo::res_theta_or_deg, DocIterator::res_theta_or_deg.c_str()) - .def_readonly("res_theta_ex_deg", &DataLine::LineInfo::res_theta_ex_deg, DocIterator::res_theta_ex_deg.c_str()); + py::class_(m, "LineInfo", DocIterator::LineInfo.c_str()) + .def_readonly("id", &LineContainer::LineInfo::id, DocIterator::id.c_str()) + .def_readonly("name", &LineContainer::LineInfo::name, DocIterator::name.c_str()) + .def_readonly("connected", &LineContainer::LineInfo::connected, DocIterator::connected.c_str()) + .def_readonly("bus_or_id", &LineContainer::LineInfo::bus_or_id, DocIterator::bus_or_id.c_str()) + .def_readonly("bus_ex_id", &LineContainer::LineInfo::bus_ex_id, DocIterator::bus_ex_id.c_str()) + .def_readonly("r_pu", &LineContainer::LineInfo::r_pu, DocIterator::r_pu.c_str()) + .def_readonly("x_pu", &LineContainer::LineInfo::x_pu, DocIterator::x_pu.c_str()) + .def_readonly("h_pu", &LineContainer::LineInfo::h_pu, DocIterator::x_pu.c_str()) + .def_readonly("h_or_pu", &LineContainer::LineInfo::h_or_pu, DocIterator::h_pu.c_str()) + .def_readonly("h_ex_pu", &LineContainer::LineInfo::h_ex_pu, DocIterator::h_pu.c_str()) + .def_readonly("has_res", &LineContainer::LineInfo::has_res, DocIterator::has_res.c_str()) + .def_readonly("res_p_or_mw", &LineContainer::LineInfo::res_p_or_mw, DocIterator::res_p_or_mw.c_str()) + .def_readonly("res_q_or_mvar", &LineContainer::LineInfo::res_q_or_mvar, DocIterator::res_q_or_mvar.c_str()) + .def_readonly("res_v_or_kv", &LineContainer::LineInfo::res_v_or_kv, DocIterator::res_v_or_kv.c_str()) + .def_readonly("res_a_or_ka", &LineContainer::LineInfo::res_a_or_ka, DocIterator::res_a_or_ka.c_str()) + .def_readonly("res_p_ex_mw", &LineContainer::LineInfo::res_p_ex_mw, DocIterator::res_p_ex_mw.c_str()) + .def_readonly("res_q_ex_mvar", &LineContainer::LineInfo::res_q_ex_mvar, DocIterator::res_q_ex_mvar.c_str()) + .def_readonly("res_v_ex_kv", &LineContainer::LineInfo::res_v_ex_kv, DocIterator::res_v_ex_kv.c_str()) + .def_readonly("res_a_ex_ka", &LineContainer::LineInfo::res_a_ex_ka, DocIterator::res_a_ex_ka.c_str()) + .def_readonly("res_theta_or_deg", &LineContainer::LineInfo::res_theta_or_deg, DocIterator::res_theta_or_deg.c_str()) + .def_readonly("res_theta_ex_deg", &LineContainer::LineInfo::res_theta_ex_deg, DocIterator::res_theta_ex_deg.c_str()); // iterator for dc lines - py::class_(m, "DataDCLine", DocIterator::DataDCLine.c_str()) - .def("__len__", [](const DataDCLine & data) { return data.nb(); }) - .def("__getitem__", [](const DataDCLine & data, int k){return data[k]; } ) - .def("__iter__", [](const DataDCLine & data) { + py::class_(m, "DCLineContainer", DocIterator::DCLineContainer.c_str()) + .def("__len__", [](const DCLineContainer & data) { return data.nb(); }) + .def("__getitem__", [](const DCLineContainer & data, int k){return data[k]; } ) + .def("__iter__", [](const DCLineContainer & data) { return py::make_iterator(data.begin(), data.end()); }, py::keep_alive<0, 1>()); /* Keep vector alive while iterator is used */ - py::class_(m, "DCLineInfo", DocIterator::DCLineInfo.c_str()) - .def_readonly("id", &DataDCLine::DCLineInfo::id, DocIterator::id.c_str()) - .def_readonly("name", &DataDCLine::DCLineInfo::name, DocIterator::name.c_str()) - .def_readonly("connected", &DataDCLine::DCLineInfo::connected, DocIterator::connected.c_str()) - .def_readonly("bus_or_id", &DataDCLine::DCLineInfo::bus_or_id, DocIterator::bus_or_id.c_str()) - .def_readonly("bus_ex_id", &DataDCLine::DCLineInfo::bus_ex_id, DocIterator::bus_ex_id.c_str()) - .def_readonly("target_p_or_mw", &DataDCLine::DCLineInfo::target_p_or_mw, DocIterator::target_p_or_mw.c_str()) - .def_readonly("target_vm_or_pu", &DataDCLine::DCLineInfo::target_vm_or_pu, DocIterator::target_vm_or_pu.c_str()) - .def_readonly("target_vm_ex_pu", &DataDCLine::DCLineInfo::target_vm_ex_pu, DocIterator::target_vm_ex_pu.c_str()) - .def_readonly("loss_pct", &DataDCLine::DCLineInfo::loss_pct, DocIterator::loss_pct.c_str()) - .def_readonly("loss_mw", &DataDCLine::DCLineInfo::loss_mw, DocIterator::loss_mw.c_str()) - .def_readonly("gen_or", &DataDCLine::DCLineInfo::gen_or, DocIterator::gen_or.c_str()) - .def_readonly("gen_ex", &DataDCLine::DCLineInfo::gen_ex, DocIterator::gen_ex.c_str()) - .def_readonly("has_res", &DataDCLine::DCLineInfo::has_res, DocIterator::has_res.c_str()) - .def_readonly("res_p_or_mw", &DataDCLine::DCLineInfo::res_p_or_mw, DocIterator::res_p_or_mw_dcline.c_str()) - .def_readonly("res_p_ex_mw", &DataDCLine::DCLineInfo::res_p_ex_mw, DocIterator::res_p_ex_mw_dcline.c_str()) - .def_readonly("res_q_or_mvar", &DataDCLine::DCLineInfo::res_q_or_mvar, DocIterator::res_q_or_mvar_dcline.c_str()) - .def_readonly("res_q_ex_mvar", &DataDCLine::DCLineInfo::res_q_ex_mvar, DocIterator::res_q_ex_mvar_dcline.c_str()) - .def_readonly("res_v_or_kv", &DataDCLine::DCLineInfo::res_v_or_kv, DocIterator::res_v_or_kv_dcline.c_str()) - .def_readonly("res_v_ex_kv", &DataDCLine::DCLineInfo::res_v_ex_kv, DocIterator::res_v_ex_kv_dcline.c_str()) - .def_readonly("res_theta_or_deg", &DataDCLine::DCLineInfo::res_theta_or_deg, DocIterator::res_theta_or_deg_dcline.c_str()) - .def_readonly("res_theta_ex_deg", &DataDCLine::DCLineInfo::res_theta_ex_deg, DocIterator::res_theta_ex_deg_dcline.c_str()) + py::class_(m, "DCLineInfo", DocIterator::DCLineInfo.c_str()) + .def_readonly("id", &DCLineContainer::DCLineInfo::id, DocIterator::id.c_str()) + .def_readonly("name", &DCLineContainer::DCLineInfo::name, DocIterator::name.c_str()) + .def_readonly("connected", &DCLineContainer::DCLineInfo::connected, DocIterator::connected.c_str()) + .def_readonly("bus_or_id", &DCLineContainer::DCLineInfo::bus_or_id, DocIterator::bus_or_id.c_str()) + .def_readonly("bus_ex_id", &DCLineContainer::DCLineInfo::bus_ex_id, DocIterator::bus_ex_id.c_str()) + .def_readonly("target_p_or_mw", &DCLineContainer::DCLineInfo::target_p_or_mw, DocIterator::target_p_or_mw.c_str()) + .def_readonly("target_vm_or_pu", &DCLineContainer::DCLineInfo::target_vm_or_pu, DocIterator::target_vm_or_pu.c_str()) + .def_readonly("target_vm_ex_pu", &DCLineContainer::DCLineInfo::target_vm_ex_pu, DocIterator::target_vm_ex_pu.c_str()) + .def_readonly("loss_pct", &DCLineContainer::DCLineInfo::loss_pct, DocIterator::loss_pct.c_str()) + .def_readonly("loss_mw", &DCLineContainer::DCLineInfo::loss_mw, DocIterator::loss_mw.c_str()) + .def_readonly("gen_or", &DCLineContainer::DCLineInfo::gen_or, DocIterator::gen_or.c_str()) + .def_readonly("gen_ex", &DCLineContainer::DCLineInfo::gen_ex, DocIterator::gen_ex.c_str()) + .def_readonly("has_res", &DCLineContainer::DCLineInfo::has_res, DocIterator::has_res.c_str()) + .def_readonly("res_p_or_mw", &DCLineContainer::DCLineInfo::res_p_or_mw, DocIterator::res_p_or_mw_dcline.c_str()) + .def_readonly("res_p_ex_mw", &DCLineContainer::DCLineInfo::res_p_ex_mw, DocIterator::res_p_ex_mw_dcline.c_str()) + .def_readonly("res_q_or_mvar", &DCLineContainer::DCLineInfo::res_q_or_mvar, DocIterator::res_q_or_mvar_dcline.c_str()) + .def_readonly("res_q_ex_mvar", &DCLineContainer::DCLineInfo::res_q_ex_mvar, DocIterator::res_q_ex_mvar_dcline.c_str()) + .def_readonly("res_v_or_kv", &DCLineContainer::DCLineInfo::res_v_or_kv, DocIterator::res_v_or_kv_dcline.c_str()) + .def_readonly("res_v_ex_kv", &DCLineContainer::DCLineInfo::res_v_ex_kv, DocIterator::res_v_ex_kv_dcline.c_str()) + .def_readonly("res_theta_or_deg", &DCLineContainer::DCLineInfo::res_theta_or_deg, DocIterator::res_theta_or_deg_dcline.c_str()) + .def_readonly("res_theta_ex_deg", &DCLineContainer::DCLineInfo::res_theta_ex_deg, DocIterator::res_theta_ex_deg_dcline.c_str()) ; // converters @@ -603,6 +603,9 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def("need_recompute_sbus", &SolverControl::need_recompute_sbus, "TODO") .def("need_recompute_ybus", &SolverControl::need_recompute_ybus, "TODO") .def("ybus_change_sparsity_pattern", &SolverControl::ybus_change_sparsity_pattern, "TODO") + .def("has_slack_weight_changed", &SolverControl::has_slack_weight_changed, "TODO") + .def("has_v_changed", &SolverControl::has_v_changed, "TODO") + .def("has_ybus_some_coeffs_zero", &SolverControl::has_ybus_some_coeffs_zero, "TODO") ; py::class_(m, "GridModel", DocGridModel::GridModel.c_str()) diff --git a/src/BaseSolver.cpp b/src/powerflow_algorithm/BaseAlgo.cpp similarity index 87% rename from src/BaseSolver.cpp rename to src/powerflow_algorithm/BaseAlgo.cpp index 843d1a4..1849562 100644 --- a/src/BaseSolver.cpp +++ b/src/powerflow_algorithm/BaseAlgo.cpp @@ -6,11 +6,11 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "BaseSolver.h" +#include "BaseAlgo.h" #include "GridModel.h" // needs to be included here because of the forward declaration -void BaseSolver::reset(){ +void BaseAlgo::reset(){ // reset timers reset_timer(); @@ -27,7 +27,7 @@ void BaseSolver::reset(){ } -RealVect BaseSolver::_evaluate_Fx(const Eigen::SparseMatrix & Ybus, +RealVect BaseAlgo::_evaluate_Fx(const Eigen::SparseMatrix & Ybus, const CplxVect & V, const CplxVect & Sbus, const Eigen::VectorXi & pv, @@ -53,7 +53,7 @@ RealVect BaseSolver::_evaluate_Fx(const Eigen::SparseMatrix & Ybus, return res; } -RealVect BaseSolver::_evaluate_Fx(const Eigen::SparseMatrix & Ybus, +RealVect BaseAlgo::_evaluate_Fx(const Eigen::SparseMatrix & Ybus, const CplxVect & V, const CplxVect & Sbus, Eigen::Index slack_id, // id of the ref slack bus @@ -117,7 +117,7 @@ RealVect BaseSolver::_evaluate_Fx(const Eigen::SparseMatrix & Ybus, } -bool BaseSolver::_check_for_convergence(const RealVect & F, +bool BaseAlgo::_check_for_convergence(const RealVect & F, real_type tol) { auto timer = CustTimer(); @@ -128,7 +128,7 @@ bool BaseSolver::_check_for_convergence(const RealVect & F, return res; } -bool BaseSolver::_check_for_convergence(const RealVect & p, +bool BaseAlgo::_check_for_convergence(const RealVect & p, const RealVect & q, real_type tol) { @@ -140,7 +140,7 @@ bool BaseSolver::_check_for_convergence(const RealVect & p, return res; } -Eigen::VectorXi BaseSolver::extract_slack_bus_id(const Eigen::VectorXi & pv, +Eigen::VectorXi BaseAlgo::extract_slack_bus_id(const Eigen::VectorXi & pv, const Eigen::VectorXi & pq, unsigned int nb_bus) { @@ -151,7 +151,7 @@ Eigen::VectorXi BaseSolver::extract_slack_bus_id(const Eigen::VectorXi & pv, int nb_slacks = nb_bus - pv.size() - pq.size(); if(nb_slacks == 0){ // TODO DEBUG MODE - throw std::runtime_error("BaseSolver::extract_slack_bus_id: All buses are tagged as PV or PQ, there can be no slack."); + throw std::runtime_error("BaseAlgo::extract_slack_bus_id: All buses are tagged as PV or PQ, there can be no slack."); } Eigen::VectorXi res(nb_slacks); Eigen::Index i_res = 0; @@ -168,7 +168,7 @@ Eigen::VectorXi BaseSolver::extract_slack_bus_id(const Eigen::VectorXi & pv, { if((i_res >= nb_slacks)){ // TODO DEBUG MODE - throw std::runtime_error("BaseSolver::extract_slack_bus_id: too many slack found. Maybe a bus is both PV and PQ ?"); + throw std::runtime_error("BaseAlgo::extract_slack_bus_id: too many slack found. Maybe a bus is both PV and PQ ?"); } res[i_res] = k; ++i_res; @@ -176,18 +176,18 @@ Eigen::VectorXi BaseSolver::extract_slack_bus_id(const Eigen::VectorXi & pv, } if(res.size() != i_res){ // TODO DEBUG MODE - throw std::runtime_error("BaseSolver::extract_slack_bus_id: Some slacks are not found in your grid."); + throw std::runtime_error("BaseAlgo::extract_slack_bus_id: Some slacks are not found in your grid."); } return res; } -void BaseSolver::get_Bf(Eigen::SparseMatrix & Bf) const { +void BaseAlgo::get_Bf(Eigen::SparseMatrix & Bf) const { if(IS_AC) throw std::runtime_error("get_Bf: impossible to use this in AC mode for now"); _gridmodel->fillBf_for_PTDF(Bf); } -void BaseSolver::get_Bf_transpose(Eigen::SparseMatrix & Bf_T) const { +void BaseAlgo::get_Bf_transpose(Eigen::SparseMatrix & Bf_T) const { if(IS_AC) throw std::runtime_error("get_Bf: impossible to use this in AC mode for now"); _gridmodel->fillBf_for_PTDF(Bf_T, true); } diff --git a/src/BaseSolver.h b/src/powerflow_algorithm/BaseAlgo.h similarity index 96% rename from src/BaseSolver.h rename to src/powerflow_algorithm/BaseAlgo.h index deeaaa1..8556609 100644 --- a/src/BaseSolver.h +++ b/src/powerflow_algorithm/BaseAlgo.h @@ -6,8 +6,8 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef BASESOLVER_H -#define BASESOLVER_H +#ifndef BASEALGO_H +#define BASEALGO_H #include #include @@ -32,17 +32,17 @@ class GridModel; /** -This class represents a solver to compute powerflow. +This class represents a algorithm to compute powerflow. It can be derived for different usecase, for example for DC powerflow, AC powerflow using Newton Raphson method etc. **/ -class BaseSolver : public BaseConstants +class BaseAlgo : public BaseConstants { public: const bool IS_AC; // should be static ideally... public: - BaseSolver(bool is_ac=true): + BaseAlgo(bool is_ac=true): BaseConstants(), IS_AC(is_ac), n_(-1), @@ -52,7 +52,7 @@ class BaseSolver : public BaseConstants timer_check_(0.), timer_total_nr_(0.){}; - virtual ~BaseSolver(){} + virtual ~BaseAlgo(){} void set_gridmodel(const GridModel * gridmodel){ _gridmodel = gridmodel; @@ -233,9 +233,9 @@ class BaseSolver : public BaseConstants private: // no copy allowed - BaseSolver( const BaseSolver & ) ; - BaseSolver & operator=( const BaseSolver & ) ; + BaseAlgo( const BaseAlgo & ) ; + BaseAlgo & operator=( const BaseAlgo & ) ; }; -#endif // BASESOLVER_H +#endif // BASEALGO_H diff --git a/src/DCSolver.h b/src/powerflow_algorithm/BaseDCAlgo.h similarity index 88% rename from src/DCSolver.h rename to src/powerflow_algorithm/BaseDCAlgo.h index 5e0e035..06e6935 100644 --- a/src/DCSolver.h +++ b/src/powerflow_algorithm/BaseDCAlgo.h @@ -6,23 +6,23 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef DCSOLVER_H -#define DCSOLVER_H +#ifndef BASE_DC_ALGO_H +#define BASE_DC_ALGO_H -#include "BaseSolver.h" +#include "BaseAlgo.h" template -class BaseDCSolver: public BaseSolver +class BaseDCAlgo: public BaseAlgo { public: - BaseDCSolver(): - BaseSolver(false), + BaseDCAlgo(): + BaseAlgo(false), _linear_solver(), need_factorize_(true), sizeYbus_with_slack_(0), sizeYbus_without_slack_(0){}; - ~BaseDCSolver(){} + ~BaseDCAlgo(){} virtual void reset(); @@ -45,8 +45,8 @@ class BaseDCSolver: public BaseSolver private: // no copy allowed - BaseDCSolver( const BaseSolver & ) =delete ; - BaseDCSolver & operator=( const BaseSolver & ) =delete; + BaseDCAlgo( const BaseDCAlgo & ) =delete ; + BaseDCAlgo & operator=( const BaseDCAlgo & ) =delete; protected: void fill_mat_bus_id(int nb_bus_solver); @@ -72,6 +72,6 @@ class BaseDCSolver: public BaseSolver }; -#include "DCSolver.tpp" +#include "BaseDCAlgo.tpp" -#endif // DCSOLVER_H +#endif // BASE_DC_ALGO_H diff --git a/src/DCSolver.tpp b/src/powerflow_algorithm/BaseDCAlgo.tpp similarity index 92% rename from src/DCSolver.tpp rename to src/powerflow_algorithm/BaseDCAlgo.tpp index 30b903b..ed7e8ab 100644 --- a/src/DCSolver.tpp +++ b/src/powerflow_algorithm/BaseDCAlgo.tpp @@ -10,7 +10,7 @@ // TODO SLACK !!! template -bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix & Ybus, +bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & Ybus, CplxVect & V, const CplxVect & Sbus, const Eigen::VectorXi & slack_ids, @@ -37,7 +37,7 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix } auto timer = CustTimer(); - BaseSolver::reset_timer(); + BaseAlgo::reset_timer(); sizeYbus_with_slack_ = static_cast(Ybus.rows()); #ifdef __COUT_TIMES @@ -131,7 +131,7 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix // retrieve back the results in the proper shape (add back the slack bus) // TODO have a better way for this, for example using `.segment(0,npv)` - // see the BaseSolver.cpp: _evaluate_Fx + // see the BaseAlgo.cpp: _evaluate_Fx RealVect Va_dc = RealVect::Constant(sizeYbus_with_slack_, my_zero_); // fill Va from dc approx for (int ybus_id=0; ybus_id < sizeYbus_with_slack_; ++ybus_id){ @@ -164,7 +164,7 @@ bool BaseDCSolver::compute_pf(const Eigen::SparseMatrix } template -void BaseDCSolver::fill_mat_bus_id(int nb_bus_solver){ +void BaseDCAlgo::fill_mat_bus_id(int nb_bus_solver){ mat_bus_id_ = Eigen::VectorXi::Constant(nb_bus_solver, -1); // Eigen::VectorXi me_to_ybus = Eigen::VectorXi::Constant(nb_bus_solver - slack_bus_ids_solver.size(), -1); int solver_id = 0; @@ -177,14 +177,14 @@ void BaseDCSolver::fill_mat_bus_id(int nb_bus_solver){ } template -void BaseDCSolver::fill_dcYbus_noslack(int nb_bus_solver, const Eigen::SparseMatrix & ref_mat){ +void BaseDCAlgo::fill_dcYbus_noslack(int nb_bus_solver, const Eigen::SparseMatrix & ref_mat){ // TODO see if "prune" might work here https://eigen.tuxfamily.org/dox/classEigen_1_1SparseMatrix.html#title29 remove_slack_buses(nb_bus_solver, ref_mat, dcYbus_noslack_); } template template // ref_mat_type should be `real_type` or `cplx_type` -void BaseDCSolver::remove_slack_buses(int nb_bus_solver, const Eigen::SparseMatrix & ref_mat, Eigen::SparseMatrix & res_mat){ +void BaseDCAlgo::remove_slack_buses(int nb_bus_solver, const Eigen::SparseMatrix & ref_mat, Eigen::SparseMatrix & res_mat){ res_mat = Eigen::SparseMatrix(sizeYbus_without_slack_, sizeYbus_without_slack_); // TODO dist slack: -1 or -mat_bus_id_.size() here ???? std::vector > tripletList; tripletList.reserve(ref_mat.nonZeros()); @@ -205,8 +205,8 @@ void BaseDCSolver::remove_slack_buses(int nb_bus_solver, const Eig } template -void BaseDCSolver::reset(){ - BaseSolver::reset(); +void BaseDCAlgo::reset(){ + BaseAlgo::reset(); _linear_solver.reset(); need_factorize_ = true; sizeYbus_with_slack_ = 0; @@ -219,7 +219,7 @@ void BaseDCSolver::reset(){ } template -RealMat BaseDCSolver::get_ptdf(const Eigen::SparseMatrix & dcYbus){ +RealMat BaseDCAlgo::get_ptdf(const Eigen::SparseMatrix & dcYbus){ Eigen::SparseMatrix Bf_T_with_slack; RealMat PTDF; RealVect rhs = RealVect::Zero(sizeYbus_without_slack_); // TODO dist slack: -1 or -mat_bus_id_.size() here ???? @@ -229,7 +229,7 @@ RealMat BaseDCSolver::get_ptdf(const Eigen::SparseMatrix::get_ptdf(const Eigen::SparseMatrix -Eigen::SparseMatrix BaseDCSolver::get_lodf(){ +Eigen::SparseMatrix BaseDCAlgo::get_lodf(){ // TODO return dcYbus_noslack_; } template -Eigen::SparseMatrix BaseDCSolver::get_bsdf(){ +Eigen::SparseMatrix BaseDCAlgo::get_bsdf(){ // TODO return dcYbus_noslack_; diff --git a/src/BaseFDPFSolver.h b/src/powerflow_algorithm/BaseFDPFAlgo.h similarity index 95% rename from src/BaseFDPFSolver.h rename to src/powerflow_algorithm/BaseFDPFAlgo.h index f670eea..67b9874 100644 --- a/src/BaseFDPFSolver.h +++ b/src/powerflow_algorithm/BaseFDPFAlgo.h @@ -6,19 +6,19 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef BASEFDPFSOLVER_H -#define BASEFDPFSOLVER_H +#ifndef BASEFDPFALGO_H +#define BASEFDPFALGO_H -#include "BaseSolver.h" +#include "BaseAlgo.h" /** Base class for Fast Decoupled Powerflow based solver **/ template -class BaseFDPFSolver : public BaseSolver +class BaseFDPFAlgo: public BaseAlgo { public: - BaseFDPFSolver():BaseSolver(true), need_factorize_(true) {} + BaseFDPFAlgo():BaseAlgo(true), need_factorize_(true) {} virtual bool compute_pf(const Eigen::SparseMatrix & Ybus, @@ -47,7 +47,7 @@ class BaseFDPFSolver : public BaseSolver virtual void reset() { - BaseSolver::reset(); + BaseAlgo::reset(); // solution of the problem Bp_ = Eigen::SparseMatrix (); // the B prime matrix (size n_pvpq) Bpp_ = Eigen::SparseMatrix(); // the B double prime matrix (size n_pq) @@ -67,7 +67,7 @@ class BaseFDPFSolver : public BaseSolver protected: virtual void reset_timer(){ - BaseSolver::reset_timer(); + BaseAlgo::reset_timer(); } CplxVect evaluate_mismatch(const Eigen::SparseMatrix & Ybus, @@ -213,10 +213,10 @@ class BaseFDPFSolver : public BaseSolver private: // no copy allowed - BaseFDPFSolver( const BaseFDPFSolver & ) =delete ; - BaseFDPFSolver & operator=( const BaseFDPFSolver & ) =delete ; + BaseFDPFAlgo( const BaseFDPFAlgo & ) =delete ; + BaseFDPFAlgo & operator=( const BaseFDPFAlgo & ) =delete ; }; -#include "BaseFDPFSolver.tpp" +#include "BaseFDPFAlgo.tpp" -#endif // BASEFDPFSOLVER_H +#endif // BASEFDPFALGO_H diff --git a/src/BaseFDPFSolver.tpp b/src/powerflow_algorithm/BaseFDPFAlgo.tpp similarity index 93% rename from src/BaseFDPFSolver.tpp rename to src/powerflow_algorithm/BaseFDPFAlgo.tpp index 080d191..fe13caf 100644 --- a/src/BaseFDPFSolver.tpp +++ b/src/powerflow_algorithm/BaseFDPFAlgo.tpp @@ -9,7 +9,7 @@ // inspired from pypower https://github.com/rwl/PYPOWER/blob/master/pypower/fdpf.py template -bool BaseFDPFSolver::compute_pf(const Eigen::SparseMatrix & Ybus, +bool BaseFDPFAlgo::compute_pf(const Eigen::SparseMatrix & Ybus, CplxVect & V, const CplxVect & Sbus, const Eigen::VectorXi & slack_ids, @@ -32,14 +32,14 @@ bool BaseFDPFSolver::compute_pf(const Eigen::SparseMatrix::compute_pf(const Eigen::SparseMatrix -void BaseFDPFSolver::fill_sparse_matrices(const Eigen::SparseMatrix & grid_Bp, +void BaseFDPFAlgo::fill_sparse_matrices(const Eigen::SparseMatrix & grid_Bp, const Eigen::SparseMatrix & grid_Bpp, const std::vector & pvpq_inv, const std::vector & pq_inv, @@ -166,7 +166,7 @@ void BaseFDPFSolver::fill_sparse_matrices(const Eigen::Spar } template -void BaseFDPFSolver::aux_fill_sparse_matrices(const Eigen::SparseMatrix & grid_Bp_Bpp, +void BaseFDPFAlgo::aux_fill_sparse_matrices(const Eigen::SparseMatrix & grid_Bp_Bpp, const std::vector & ind_inv, Eigen::Index mat_dim, Eigen::SparseMatrix & res) diff --git a/src/BaseNRSolver.h b/src/powerflow_algorithm/BaseNRAlgo.h similarity index 95% rename from src/BaseNRSolver.h rename to src/powerflow_algorithm/BaseNRAlgo.h index bde0d6a..2b68155 100644 --- a/src/BaseNRSolver.h +++ b/src/powerflow_algorithm/BaseNRAlgo.h @@ -6,19 +6,19 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef BASENRSOLVER_H -#define BASENRSOLVER_H +#ifndef BASE_NR_ALGO_H +#define BASE_NR_ALGO_H -#include "BaseSolver.h" +#include "BaseAlgo.h" /** Base class for Newton Raphson based solver **/ template -class BaseNRSolver : public BaseSolver +class BaseNRAlgo : public BaseAlgo { public: - BaseNRSolver():BaseSolver(true), need_factorize_(true), timer_initialize_(0.), timer_dSbus_(0.), timer_fillJ_(0.) {} + BaseNRAlgo():BaseAlgo(true), need_factorize_(true), timer_initialize_(0.), timer_dSbus_(0.), timer_fillJ_(0.) {} virtual Eigen::Ref > get_J() const { @@ -56,7 +56,7 @@ class BaseNRSolver : public BaseSolver protected: virtual void reset_timer(){ - BaseSolver::reset_timer(); + BaseAlgo::reset_timer(); timer_dSbus_ = 0.; timer_fillJ_ = 0.; timer_initialize_ = 0.; @@ -198,8 +198,8 @@ class BaseNRSolver : public BaseSolver private: // no copy allowed - BaseNRSolver( const BaseNRSolver & ) =delete ; - BaseNRSolver & operator=( const BaseNRSolver & ) =delete ; + BaseNRAlgo( const BaseNRAlgo & ) =delete ; + BaseNRAlgo & operator=( const BaseNRAlgo & ) =delete ; /** helper function to print the max_col left most columns of the J matrix **/ void print_J(int min_col=-1, int max_col=-1) const{ @@ -235,6 +235,6 @@ class BaseNRSolver : public BaseSolver } }; -#include "BaseNRSolver.tpp" +#include "BaseNRAlgo.tpp" -#endif // BASENRSOLVER_H +#endif // BASE_NR_ALGO_H diff --git a/src/BaseNRSolver.tpp b/src/powerflow_algorithm/BaseNRAlgo.tpp similarity index 94% rename from src/BaseNRSolver.tpp rename to src/powerflow_algorithm/BaseNRAlgo.tpp index 4dbbfb8..0977d7d 100644 --- a/src/BaseNRSolver.tpp +++ b/src/powerflow_algorithm/BaseNRAlgo.tpp @@ -6,13 +6,13 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -// #include "BaseNRSolver.h" // now a template class, so this file will be included instead ! +// #include "BaseNRAlgo.h" // now a template class, so this file will be included instead ! // TODO get rid of the pvpq, pv, pq etc and put the jacobian "in the right order" // to ease and make way faster the filling of the sparse matrix J template -bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix & Ybus, +bool BaseNRAlgo::compute_pf(const Eigen::SparseMatrix & Ybus, CplxVect & V, const CplxVect & Sbus, const Eigen::VectorXi & slack_ids, @@ -35,14 +35,14 @@ bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix if(Sbus.size() != Ybus.rows() || Sbus.size() != Ybus.cols() ){ // TODO DEBUG MODE std::ostringstream exc_; - exc_ << "BaseNRSolver::compute_pf: Size of the Sbus should be the same as the size of Ybus. Currently: "; + exc_ << "BaseNRAlgo::compute_pf: Size of the Sbus should be the same as the size of Ybus. Currently: "; exc_ << "Sbus (" << Sbus.size() << ") and Ybus (" << Ybus.rows() << ", " << Ybus.cols() << ")."; throw std::runtime_error(exc_.str()); } if(V.size() != Ybus.rows() || V.size() != Ybus.cols() ){ // TODO DEBUG MODE std::ostringstream exc_; - exc_ << "BaseNRSolver::compute_pf: Size of V (init voltages) should be the same as the size of Ybus. Currently: "; + exc_ << "BaseNRAlgo::compute_pf: Size of V (init voltages) should be the same as the size of Ybus. Currently: "; exc_ << "V (" << V.size() << ") and Ybus (" << Ybus.rows()<< ", " << Ybus.cols() << ")."; throw std::runtime_error(exc_.str()); } @@ -89,12 +89,12 @@ bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix // std::cout << "iter " << nr_iter_ << " dx(0): " << -F(0) << " dx(1): " << -F(1) << std::endl; // std::cout << "slack_absorbed " << slack_absorbed << std::endl; value_map_.clear(); // TODO smarter solver: only needed if ybus has changed - // BaseNRSolver::col_map_.clear(); // TODO smarter solver: only needed if ybus has changed - // BaseNRSolver::row_map_.clear(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::col_map_.clear(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::row_map_.clear(); // TODO smarter solver: only needed if ybus has changed dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed - // BaseNRSolver::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed - // BaseNRSolver::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed while ((!converged) & (nr_iter_ < max_iter)){ nr_iter_++; fill_jacobian_matrix(Ybus, V_, slack_bus_id, slack_weights, pq, pvpq, pq_inv, pvpq_inv); @@ -161,8 +161,8 @@ bool BaseNRSolver::compute_pf(const Eigen::SparseMatrix } template -void BaseNRSolver::reset(){ - BaseSolver::reset(); +void BaseNRAlgo::reset(){ + BaseAlgo::reset(); // reset specific attributes J_ = Eigen::SparseMatrix(); // the jacobian matrix dS_dVm_ = Eigen::SparseMatrix(); @@ -175,7 +175,7 @@ void BaseNRSolver::reset(){ } template -void BaseNRSolver::_dSbus_dV(const Eigen::Ref > & Ybus, +void BaseNRAlgo::_dSbus_dV(const Eigen::Ref > & Ybus, const Eigen::Ref & V){ // std::cout << "Ybus.nonZeros(): " << Ybus.nonZeros() << std::endl; auto timer = CustTimer(); @@ -236,7 +236,7 @@ void BaseNRSolver::_dSbus_dV(const Eigen::Ref -void BaseNRSolver::_get_values_J(int & nb_obj_this_col, +void BaseNRAlgo::_get_values_J(int & nb_obj_this_col, std::vector & inner_index, std::vector & values, const Eigen::Ref > & mat, // ex. dS_dVa_r @@ -263,7 +263,7 @@ void BaseNRSolver::_get_values_J(int & nb_obj_this_col, } template -void BaseNRSolver::_get_values_J(int & nb_obj_this_col, +void BaseNRAlgo::_get_values_J(int & nb_obj_this_col, std::vector & inner_index, std::vector & values, const Eigen::Ref > & mat, // ex. dS_dVa_r @@ -300,7 +300,7 @@ void BaseNRSolver::_get_values_J(int & nb_obj_this_col, } template -void BaseNRSolver::fill_jacobian_matrix(const Eigen::SparseMatrix & Ybus, +void BaseNRAlgo::fill_jacobian_matrix(const Eigen::SparseMatrix & Ybus, const CplxVect & V, Eigen::Index slack_bus_id, const RealVect & slack_weights, @@ -359,7 +359,7 @@ void BaseNRSolver::fill_jacobian_matrix(const Eigen::SparseMatrix< #ifdef __COUT_TIMES auto timer3 = CustTimer(); #endif // - if (BaseNRSolver::value_map_.size() == 0) fill_value_map(slack_bus_id, pq, pvpq, true); + if (BaseNRAlgo::value_map_.size() == 0) fill_value_map(slack_bus_id, pq, pvpq, true); fill_jacobian_matrix_kown_sparsity_pattern(slack_bus_id, pq, pvpq ); @@ -371,7 +371,7 @@ void BaseNRSolver::fill_jacobian_matrix(const Eigen::SparseMatrix< } template -void BaseNRSolver::fill_jacobian_matrix_unkown_sparsity_pattern( +void BaseNRAlgo::fill_jacobian_matrix_unkown_sparsity_pattern( const Eigen::SparseMatrix & Ybus, const CplxVect & V, Eigen::Index slack_bus_id, @@ -541,7 +541,7 @@ dS_dVa_ and dS_dVm_ to be used to fill J_ it requires that J_ is initialized, in compressed mode. **/ template -void BaseNRSolver::fill_value_map( +void BaseNRAlgo::fill_value_map( Eigen::Index slack_bus_id, const Eigen::VectorXi & pq, const Eigen::VectorXi & pvpq, @@ -550,7 +550,7 @@ void BaseNRSolver::fill_value_map( { const int n_pvpq = static_cast(pvpq.size()); value_map_ = std::vector (); - value_map_.reserve(BaseNRSolver::J_.nonZeros()); + value_map_.reserve(BaseNRAlgo::J_.nonZeros()); // col_map_ = std::vector (J_.nonZeros()); // row_map_ = std::vector (J_.nonZeros()); @@ -620,7 +620,7 @@ void BaseNRSolver::fill_value_map( } template -void BaseNRSolver::fill_jacobian_matrix_kown_sparsity_pattern( +void BaseNRAlgo::fill_jacobian_matrix_kown_sparsity_pattern( Eigen::Index slack_bus_id, const Eigen::VectorXi & pq, const Eigen::VectorXi & pvpq diff --git a/src/BaseNRSolverSingleSlack.h b/src/powerflow_algorithm/BaseNRSingleSlackAlgo.h similarity index 86% rename from src/BaseNRSolverSingleSlack.h rename to src/powerflow_algorithm/BaseNRSingleSlackAlgo.h index 09f8042..7ed9ccf 100644 --- a/src/BaseNRSolverSingleSlack.h +++ b/src/powerflow_algorithm/BaseNRSingleSlackAlgo.h @@ -6,21 +6,21 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef BASENRSOLVERSINGLESLACK_H -#define BASENRSOLVERSINGLESLACK_H +#ifndef BASE_NR_SINGLESLACK_ALGO_H +#define BASE_NR_SINGLESLACK_ALGO_H -#include "BaseNRSolver.h" +#include "BaseNRAlgo.h" /** Base class for Newton Raphson based solver (only interesting for single slack) **/ template -class BaseNRSolverSingleSlack : public BaseNRSolver +class BaseNRSingleSlackAlgo : public BaseNRAlgo { public: - BaseNRSolverSingleSlack():BaseNRSolver(){} + BaseNRSingleSlackAlgo():BaseNRAlgo(){} - ~BaseNRSolverSingleSlack(){} + ~BaseNRSingleSlackAlgo(){} virtual bool compute_pf(const Eigen::SparseMatrix & Ybus, @@ -63,6 +63,6 @@ class BaseNRSolverSingleSlack : public BaseNRSolver }; -#include "BaseNRSolverSingleSlack.tpp" +#include "BaseNRSingleSlackAlgo.tpp" -#endif // BASENRSOLVERSINGLESLACK_H +#endif // BASE_NR_SINGLESLACK_ALGO_H diff --git a/src/BaseNRSolverSingleSlack.tpp b/src/powerflow_algorithm/BaseNRSingleSlackAlgo.tpp similarity index 65% rename from src/BaseNRSolverSingleSlack.tpp rename to src/powerflow_algorithm/BaseNRSingleSlackAlgo.tpp index 7e39ba2..8c0a429 100644 --- a/src/BaseNRSolverSingleSlack.tpp +++ b/src/powerflow_algorithm/BaseNRSingleSlackAlgo.tpp @@ -7,10 +7,10 @@ // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. // #include "BaseNRSolverSingleSlack.h" -// #include "BaseNRSolver.h" +// #include "BaseNRAlgo.h" template -bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix & Ybus, +bool BaseNRSingleSlackAlgo::compute_pf(const Eigen::SparseMatrix & Ybus, CplxVect & V, const CplxVect & Sbus, const Eigen::VectorXi & slack_ids, @@ -30,28 +30,28 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix // TODO Ybus (nrow or ncol), pv and pq have value that are between 0 and nrow etc. if(Sbus.size() != Ybus.rows() || Sbus.size() != Ybus.cols() ){ std::ostringstream exc_; - exc_ << "BaseNRSolverSingleSlack::compute_pf: Size of the Sbus should be the same as the size of Ybus. Currently: "; + exc_ << "BaseNRSingleSlackAlgo::compute_pf: Size of the Sbus should be the same as the size of Ybus. Currently: "; exc_ << "Sbus (" << Sbus.size() << ") and Ybus (" << Ybus.rows() << ", " << Ybus.cols() << ")."; throw std::runtime_error(exc_.str()); } if(V.size() != Ybus.rows() || V.size() != Ybus.cols() ){ std::ostringstream exc_; - exc_ << "BaseNRSolverSingleSlack::compute_pf: Size of V (init voltages) should be the same as the size of Ybus. Currently: "; + exc_ << "BaseNRSingleSlackAlgo::compute_pf: Size of V (init voltages) should be the same as the size of Ybus. Currently: "; exc_ << "V (" << V.size() << ") and Ybus (" << Ybus.rows()<<", "<::is_linear_solver_valid()){ + if(!BaseNRAlgo::is_linear_solver_valid()){ return false; } - BaseNRSolver::reset_timer(); - BaseNRSolver::reset_if_needed(); - BaseNRSolver::err_ = ErrorType::NoError; // reset the error if previous error happened + BaseNRAlgo::reset_timer(); + BaseNRAlgo::reset_if_needed(); + BaseNRAlgo::err_ = ErrorType::NoError; // reset the error if previous error happened auto timer = CustTimer(); // initialize once and for all the "inverse" of these vectors - // Eigen::VectorXi my_pv = BaseNRSolver::retrieve_pv_with_slack(slack_ids, pv); + // Eigen::VectorXi my_pv = BaseNRAlgo::retrieve_pv_with_slack(slack_ids, pv); Eigen::VectorXi my_pv = pv; - // Eigen::VectorXi my_pv = pv; // BaseNRSolver::retrieve_pv_with_slack(slack_ids, pv); + // Eigen::VectorXi my_pv = pv; // BaseNRAlgo::retrieve_pv_with_slack(slack_ids, pv); const int n_pv = static_cast(my_pv.size()); const int n_pq = static_cast(pq.size()); @@ -63,33 +63,33 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix std::vector pq_inv(V.size(), -1); for(int inv_id=0; inv_id < n_pq; ++inv_id) pq_inv[pq(inv_id)] = inv_id; - BaseNRSolver::V_ = V; - BaseNRSolver::Vm_ = BaseNRSolver::V_.array().abs(); // update Vm and Va again in case - BaseNRSolver::Va_ = BaseNRSolver::V_.array().arg(); // we wrapped around with a negative Vm + BaseNRAlgo::V_ = V; + BaseNRAlgo::Vm_ = BaseNRAlgo::V_.array().abs(); // update Vm and Va again in case + BaseNRAlgo::Va_ = BaseNRAlgo::V_.array().arg(); // we wrapped around with a negative Vm // first check, if the problem is already solved, i stop there - RealVect F = BaseNRSolver::_evaluate_Fx(Ybus, V, Sbus, my_pv, pq); - bool converged = BaseNRSolver::_check_for_convergence(F, tol); - BaseNRSolver::nr_iter_ = 0; //current step + RealVect F = BaseNRAlgo::_evaluate_Fx(Ybus, V, Sbus, my_pv, pq); + bool converged = BaseNRAlgo::_check_for_convergence(F, tol); + BaseNRAlgo::nr_iter_ = 0; //current step bool res = true; // have i converged or not bool has_just_been_initialized = false; // to avoid a call to klu_refactor follow a call to klu_factor in the same loop - const cplx_type m_i = BaseNRSolver::my_i; // otherwise it does not compile - BaseNRSolver::value_map_.clear(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed - BaseNRSolver::dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed - BaseNRSolver::dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed - // BaseNRSolver::J_.setZero(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed or ybus_some_coeffs_zero_ - // BaseNRSolver::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed - // BaseNRSolver::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed - while ((!converged) & (BaseNRSolver::nr_iter_ < max_iter)){ - BaseNRSolver::nr_iter_++; - // std::cout << "\tnr_iter_ " << BaseNRSolver::nr_iter_ << std::endl; - fill_jacobian_matrix(Ybus, BaseNRSolver::V_, pq, pvpq, pq_inv, pvpq_inv); - if(BaseNRSolver::need_factorize_){ - BaseNRSolver::initialize(); - if(BaseNRSolver::err_ != ErrorType::NoError){ + const cplx_type m_i = BaseNRAlgo::my_i; // otherwise it does not compile + BaseNRAlgo::value_map_.clear(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed + BaseNRAlgo::dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed + BaseNRAlgo::dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed + // BaseNRAlgo::J_.setZero(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed or ybus_some_coeffs_zero_ + // BaseNRAlgo::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed + while ((!converged) & (BaseNRAlgo::nr_iter_ < max_iter)){ + BaseNRAlgo::nr_iter_++; + // std::cout << "\tnr_iter_ " << BaseNRAlgo::nr_iter_ << std::endl; + fill_jacobian_matrix(Ybus, BaseNRAlgo::V_, pq, pvpq, pq_inv, pvpq_inv); + if(BaseNRAlgo::need_factorize_){ + BaseNRAlgo::initialize(); + if(BaseNRAlgo::err_ != ErrorType::NoError){ // I got an error during the initialization of the linear system, i need to stop here - // std::cout << BaseNRSolver::err_ << std::endl; + // std::cout << BaseNRAlgo::err_ << std::endl; res = false; break; } @@ -99,62 +99,62 @@ bool BaseNRSolverSingleSlack::compute_pf(const Eigen::SparseMatrix // std::cout << "no need to factorize" << std::endl; } - BaseNRSolver::solve(F, has_just_been_initialized); + BaseNRAlgo::solve(F, has_just_been_initialized); has_just_been_initialized = false; - if(BaseNRSolver::err_ != ErrorType::NoError){ + if(BaseNRAlgo::err_ != ErrorType::NoError){ // I got an error during the solving of the linear system, i need to stop here - // std::cout << BaseNRSolver::err_ << std::endl; + // std::cout << BaseNRAlgo::err_ << std::endl; res = false; break; } // auto dx = -F; - BaseNRSolver::Vm_ = BaseNRSolver::V_.array().abs(); // update Vm and Va again in case - BaseNRSolver::Va_ = BaseNRSolver::V_.array().arg(); // we wrapped around with a negative Vm + BaseNRAlgo::Vm_ = BaseNRAlgo::V_.array().abs(); // update Vm and Va again in case + BaseNRAlgo::Va_ = BaseNRAlgo::V_.array().arg(); // we wrapped around with a negative Vm // update voltage (this should be done consistently with "klu_solver._evaluate_Fx") - if (n_pv > 0) BaseNRSolver::Va_(my_pv) -= F.segment(0, n_pv); + if (n_pv > 0) BaseNRAlgo::Va_(my_pv) -= F.segment(0, n_pv); if (n_pq > 0){ - BaseNRSolver::Va_(pq) -= F.segment(n_pv,n_pq); - BaseNRSolver::Vm_(pq) -= F.segment(n_pv+n_pq, n_pq); + BaseNRAlgo::Va_(pq) -= F.segment(n_pv,n_pq); + BaseNRAlgo::Vm_(pq) -= F.segment(n_pv+n_pq, n_pq); } // TODO change here for not having to cast all the time ... maybe - const RealVect & Vm = BaseNRSolver::Vm_; // I am forced to redefine the type for it to compile properly - const RealVect & Va = BaseNRSolver::Va_; - BaseNRSolver::V_ = Vm.array() * (Va.array().cos().cast() + m_i * Va.array().sin().cast() ); + const RealVect & Vm = BaseNRAlgo::Vm_; // I am forced to redefine the type for it to compile properly + const RealVect & Va = BaseNRAlgo::Va_; + BaseNRAlgo::V_ = Vm.array() * (Va.array().cos().cast() + m_i * Va.array().sin().cast() ); - F = BaseNRSolver::_evaluate_Fx(Ybus, BaseNRSolver::V_, Sbus, my_pv, pq); + F = BaseNRAlgo::_evaluate_Fx(Ybus, BaseNRAlgo::V_, Sbus, my_pv, pq); bool tmp = F.allFinite(); if(!tmp){ - BaseNRSolver::err_ = ErrorType::InifiniteValue; - // std::cout << BaseNRSolver::err_ << std::endl; + BaseNRAlgo::err_ = ErrorType::InifiniteValue; + // std::cout << BaseNRAlgo::err_ << std::endl; break; // divergence due to Nans } - converged = BaseNRSolver::_check_for_convergence(F, tol); + converged = BaseNRAlgo::_check_for_convergence(F, tol); } if(!converged){ - if (BaseNRSolver::err_ == ErrorType::NoError) BaseNRSolver::err_ = ErrorType::TooManyIterations; + if (BaseNRAlgo::err_ == ErrorType::NoError) BaseNRAlgo::err_ = ErrorType::TooManyIterations; res = false; } - BaseNRSolver::timer_total_nr_ += timer.duration(); + BaseNRAlgo::timer_total_nr_ += timer.duration(); #ifdef __COUT_TIMES - std::cout << "Computation time: " << "\n\t timer_initialize_: " << BaseNRSolver::timer_initialize_ - << "\n\t timer_dSbus_ (called in _fillJ_): " << BaseNRSolver::timer_dSbus_ - << "\n\t timer_fillJ_: " << BaseNRSolver::timer_fillJ_ - << "\n\t timer_Fx_: " << BaseNRSolver::timer_Fx_ - << "\n\t timer_check_: " << BaseNRSolver::timer_check_ - << "\n\t timer_solve_: " << BaseNRSolver::timer_solve_ - << "\n\t timer_total_nr_: " << BaseNRSolver::timer_total_nr_ + std::cout << "Computation time: " << "\n\t timer_initialize_: " << BaseNRAlgo::timer_initialize_ + << "\n\t timer_dSbus_ (called in _fillJ_): " << BaseNRAlgo::timer_dSbus_ + << "\n\t timer_fillJ_: " << BaseNRAlgo::timer_fillJ_ + << "\n\t timer_Fx_: " << BaseNRAlgo::timer_Fx_ + << "\n\t timer_check_: " << BaseNRAlgo::timer_check_ + << "\n\t timer_solve_: " << BaseNRAlgo::timer_solve_ + << "\n\t timer_total_nr_: " << BaseNRAlgo::timer_total_nr_ << "\n\n"; #endif // __COUT_TIMES - BaseNRSolver::_solver_control.tell_none_changed(); + BaseNRAlgo::_solver_control.tell_none_changed(); return res; } template -void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::SparseMatrix & Ybus, +void BaseNRSingleSlackAlgo::fill_jacobian_matrix(const Eigen::SparseMatrix & Ybus, const CplxVect & V, const Eigen::VectorXi & pq, const Eigen::VectorXi & pvpq, @@ -175,7 +175,7 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp **/ auto timer = CustTimer(); - BaseNRSolver::_dSbus_dV(Ybus, V); + BaseNRAlgo::_dSbus_dV(Ybus, V); const int n_pvpq = static_cast(pvpq.size()); const int n_pq = static_cast(pq.size()); @@ -183,7 +183,7 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp // TODO to gain a bit more time below, try to compute directly, in _dSbus_dV(Ybus, V); // TODO the `dS_dVa_[pvpq, pvpq]` // TODO so that it's easier to retrieve in the next few lines ! - if(BaseNRSolver::J_.cols() != size_j) + if(BaseNRAlgo::J_.cols() != size_j) // if(true) { #ifdef __COUT_TIMES @@ -202,7 +202,7 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp #ifdef __COUT_TIMES auto timer3 = CustTimer(); #endif // __COUT_TIMES - if (BaseNRSolver::value_map_.size() == 0){ + if (BaseNRAlgo::value_map_.size() == 0){ // std::cout << "\t\tfill_value_map called" << std::endl; fill_value_map(pq, pvpq, true); } @@ -212,11 +212,11 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix(const Eigen::Sp std::cout << "\t\t fill_jacobian_matrix_kown_sparsity_pattern : " << timer3.duration() << std::endl; #endif // __COUT_TIMES } - BaseNRSolver::timer_fillJ_ += timer.duration(); + BaseNRAlgo::timer_fillJ_ += timer.duration(); } template -void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity_pattern( +void BaseNRSingleSlackAlgo::fill_jacobian_matrix_unkown_sparsity_pattern( const Eigen::SparseMatrix & Ybus, const CplxVect & V, const Eigen::VectorXi & pq, @@ -246,22 +246,22 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity const int n_pq = static_cast(pq.size()); const int size_j = n_pvpq + n_pq; - const Eigen::SparseMatrix dS_dVa_r = BaseNRSolver::dS_dVa_.real(); - const Eigen::SparseMatrix dS_dVa_i = BaseNRSolver::dS_dVa_.imag(); - const Eigen::SparseMatrix dS_dVm_r = BaseNRSolver::dS_dVm_.real(); - const Eigen::SparseMatrix dS_dVm_i = BaseNRSolver::dS_dVm_.imag(); + const Eigen::SparseMatrix dS_dVa_r = BaseNRAlgo::dS_dVa_.real(); + const Eigen::SparseMatrix dS_dVa_i = BaseNRAlgo::dS_dVa_.imag(); + const Eigen::SparseMatrix dS_dVm_r = BaseNRAlgo::dS_dVm_.real(); + const Eigen::SparseMatrix dS_dVm_i = BaseNRAlgo::dS_dVm_.imag(); // Method (1) seems to be faster than the others // optim : if the matrix was already computed, i don't initialize it, i instead reuse as much as i can // i can do that because the matrix will ALWAYS have the same non zero coefficients. // in this if, i allocate it in a "large enough" place to avoid copy when first filling it - if(BaseNRSolver::J_.cols() != size_j) + if(BaseNRAlgo::J_.cols() != size_j) { need_insert = true; - BaseNRSolver::J_ = Eigen::SparseMatrix(size_j, size_j); + BaseNRAlgo::J_ = Eigen::SparseMatrix(size_j, size_j); // pre allocate a large enough matrix - BaseNRSolver::J_.reserve(2*(BaseNRSolver::dS_dVa_.nonZeros() + BaseNRSolver::dS_dVm_.nonZeros())); + BaseNRAlgo::J_.reserve(2*(BaseNRAlgo::dS_dVa_.nonZeros() + BaseNRAlgo::dS_dVm_.nonZeros())); // from an experiment, outerIndexPtr is initialized, with the number of columns // innerIndexPtr and valuePtr are not. } @@ -284,14 +284,14 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity // fill with the first column with the column of dS_dVa[:,pvpq[col_id]] // and check the row order ! - BaseNRSolver::_get_values_J(nb_obj_this_col, inner_index, values, + BaseNRAlgo::_get_values_J(nb_obj_this_col, inner_index, values, dS_dVa_r, pvpq_inv, pvpq, col_id, 0, 0); // fill the rest of the rows with the first column of dS_dVa_imag[:,pq[col_id]] - BaseNRSolver::_get_values_J(nb_obj_this_col, inner_index, values, + BaseNRAlgo::_get_values_J(nb_obj_this_col, inner_index, values, dS_dVa_i, pq_inv, pvpq, col_id, @@ -301,8 +301,8 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity // "efficient" insert of the element in the matrix for(int in_ind=0; in_ind < nb_obj_this_col; ++in_ind){ int row_id = inner_index[in_ind]; - if(need_insert) BaseNRSolver::J_.insert(row_id, col_id) = values[in_ind]; // HERE FOR PERF OPTIM (1) - else BaseNRSolver::J_.coeffRef(row_id, col_id) = values[in_ind]; // HERE FOR PERF OPTIM (1) + if(need_insert) BaseNRAlgo::J_.insert(row_id, col_id) = values[in_ind]; // HERE FOR PERF OPTIM (1) + else BaseNRAlgo::J_.coeffRef(row_id, col_id) = values[in_ind]; // HERE FOR PERF OPTIM (1) // J_.insert(row_id, col_id) = values[in_ind]; // HERE FOR PERF OPTIM (2) // coeffs.push_back(Eigen::Triplet(row_id, col_id, values[in_ind])); // HERE FOR PERF OPTIM (3) } @@ -318,7 +318,7 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity // fill with the first column with the column of dS_dVa[:,pvpq[col_id]] // and check the row order ! - BaseNRSolver::_get_values_J(nb_obj_this_col, inner_index, values, + BaseNRAlgo::_get_values_J(nb_obj_this_col, inner_index, values, dS_dVm_r, pvpq_inv, pq, col_id, @@ -326,7 +326,7 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity 0); // fill the rest of the rows with the first column of dS_dVa_imag[:,pq[col_id]] - BaseNRSolver::_get_values_J(nb_obj_this_col, inner_index, values, + BaseNRAlgo::_get_values_J(nb_obj_this_col, inner_index, values, dS_dVm_i, pq_inv, pq, col_id, @@ -336,14 +336,14 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_unkown_sparsity // "efficient" insert of the element in the matrix for(int in_ind=0; in_ind < nb_obj_this_col; ++in_ind){ int row_id = inner_index[in_ind]; - if(need_insert) BaseNRSolver::J_.insert(row_id, col_id + n_pvpq) = values[in_ind]; // HERE FOR PERF OPTIM (1) - else BaseNRSolver::J_.coeffRef(row_id, col_id + n_pvpq) = values[in_ind]; // HERE FOR PERF OPTIM (1) + if(need_insert) BaseNRAlgo::J_.insert(row_id, col_id + n_pvpq) = values[in_ind]; // HERE FOR PERF OPTIM (1) + else BaseNRAlgo::J_.coeffRef(row_id, col_id + n_pvpq) = values[in_ind]; // HERE FOR PERF OPTIM (1) // J_.insert(row_id, col_id + n_pvpq) = values[in_ind]; // HERE FOR PERF OPTIM (2) // coeffs.push_back(Eigen::Triplet(row_id, col_id + n_pvpq, values[in_ind])); // HERE FOR PERF OPTIM (3) } } // J_.setFromTriplets(coeffs.begin(), coeffs.end()); // HERE FOR PERF OPTIM (3) - BaseNRSolver::J_.makeCompressed(); + BaseNRAlgo::J_.makeCompressed(); } /** @@ -352,21 +352,21 @@ dS_dVa_ and dS_dVm_ to be used to fill J_ it requires that J_ is initialized, in compressed mode. **/ template -void BaseNRSolverSingleSlack::fill_value_map( +void BaseNRSingleSlackAlgo::fill_value_map( const Eigen::VectorXi & pq, const Eigen::VectorXi & pvpq, bool reset_J ) { const int n_pvpq = static_cast(pvpq.size()); - BaseNRSolver::value_map_.clear(); - // std::cout << "BaseNRSolver::J_.nonZeros(): " << BaseNRSolver::J_.nonZeros() << std::endl; - BaseNRSolver::value_map_.reserve(BaseNRSolver::J_.nonZeros()); + BaseNRAlgo::value_map_.clear(); + // std::cout << "BaseNRAlgo::J_.nonZeros(): " << BaseNRAlgo::J_.nonZeros() << std::endl; + BaseNRAlgo::value_map_.reserve(BaseNRAlgo::J_.nonZeros()); - const int n_col = static_cast(BaseNRSolver::J_.cols()); + const int n_col = static_cast(BaseNRAlgo::J_.cols()); unsigned int pos_el = 0; for (int col_=0; col_ < n_col; ++col_){ - for (Eigen::SparseMatrix::InnerIterator it(BaseNRSolver::J_, col_); it; ++it) + for (Eigen::SparseMatrix::InnerIterator it(BaseNRAlgo::J_, col_); it; ++it) { const int row_id = static_cast(it.row()); const int col_id = static_cast(it.col()); // it's equal to "col_" @@ -377,7 +377,7 @@ void BaseNRSolverSingleSlack::fill_value_map( const int row_id_dS_dVa_r = pvpq[row_id]; const int col_id_dS_dVa_r = pvpq[col_id]; // this_el = dS_dVa_r.coeff(row_id_dS_dVa_r, col_id_dS_dVa_r); - BaseNRSolver::value_map_.push_back(&BaseNRSolver::dS_dVa_.coeffRef(row_id_dS_dVa_r, col_id_dS_dVa_r)); + BaseNRAlgo::value_map_.push_back(&BaseNRAlgo::dS_dVa_.coeffRef(row_id_dS_dVa_r, col_id_dS_dVa_r)); // I don't need to perform these checks: if they failed, the element would not be in J_ in the first place // const int is_row_non_null = pq_inv[row_id_dS_dVa_r]; @@ -392,31 +392,31 @@ void BaseNRSolverSingleSlack::fill_value_map( const int row_id_dS_dVa_i = pq[row_id - n_pvpq]; const int col_id_dS_dVa_i = pvpq[col_id]; // this_el = dS_dVa_i.coeff(row_id_dS_dVa_i, col_id_dS_dVa_i); - BaseNRSolver::value_map_.push_back(&BaseNRSolver::dS_dVa_.coeffRef(row_id_dS_dVa_i, col_id_dS_dVa_i)); + BaseNRAlgo::value_map_.push_back(&BaseNRAlgo::dS_dVa_.coeffRef(row_id_dS_dVa_i, col_id_dS_dVa_i)); }else if((col_id >= n_pvpq) && (row_id < n_pvpq)){ // this is the J12 part (dS_dVm_r) const int row_id_dS_dVm_r = pvpq[row_id]; const int col_id_dS_dVm_r = pq[col_id - n_pvpq]; // this_el = dS_dVm_r.coeff(row_id_dS_dVm_r, col_id_dS_dVm_r); - BaseNRSolver::value_map_.push_back(&BaseNRSolver::dS_dVm_.coeffRef(row_id_dS_dVm_r, col_id_dS_dVm_r)); + BaseNRAlgo::value_map_.push_back(&BaseNRAlgo::dS_dVm_.coeffRef(row_id_dS_dVm_r, col_id_dS_dVm_r)); }else if((col_id >= n_pvpq) && (row_id >= n_pvpq)){ // this is the J22 part (dS_dVm_i) const int row_id_dS_dVm_i = pq[row_id - n_pvpq]; const int col_id_dS_dVm_i = pq[col_id - n_pvpq]; // this_el = dS_dVm_i.coeff(row_id_dS_dVm_i, col_id_dS_dVm_i); - BaseNRSolver::value_map_.push_back(&BaseNRSolver::dS_dVm_.coeffRef(row_id_dS_dVm_i, col_id_dS_dVm_i)); + BaseNRAlgo::value_map_.push_back(&BaseNRAlgo::dS_dVm_.coeffRef(row_id_dS_dVm_i, col_id_dS_dVm_i)); } // go to the next element ++pos_el; } } - // BaseNRSolver::dS_dVa_.makeCompressed(); - // BaseNRSolver::dS_dVm_.makeCompressed(); + // BaseNRAlgo::dS_dVa_.makeCompressed(); + // BaseNRAlgo::dS_dVm_.makeCompressed(); } template -void BaseNRSolverSingleSlack::fill_jacobian_matrix_kown_sparsity_pattern( +void BaseNRSingleSlackAlgo::fill_jacobian_matrix_kown_sparsity_pattern( const Eigen::VectorXi & pq, const Eigen::VectorXi & pvpq ) @@ -443,15 +443,15 @@ void BaseNRSolverSingleSlack::fill_jacobian_matrix_kown_sparsity_p const int n_pvpq = static_cast(pvpq.size()); // real_type * J_x_ptr = J_.valuePtr(); - const int n_cols = static_cast(BaseNRSolver::J_.cols()); // equal to nrow + const int n_cols = static_cast(BaseNRAlgo::J_.cols()); // equal to nrow unsigned int pos_el = 0; for (int col_id=0; col_id < n_cols; ++col_id){ - for (Eigen::SparseMatrix::InnerIterator it(BaseNRSolver::J_, col_id); it; ++it) + for (Eigen::SparseMatrix::InnerIterator it(BaseNRAlgo::J_, col_id); it; ++it) { const auto row_id = it.row(); // only one if is necessary (magic !) // top rows are "real" part and bottom rows are imaginary part (you can check) - it.valueRef() = row_id < n_pvpq ? std::real(*BaseNRSolver::value_map_[pos_el]) : std::imag(*BaseNRSolver::value_map_[pos_el]); + it.valueRef() = row_id < n_pvpq ? std::real(*BaseNRAlgo::value_map_[pos_el]) : std::imag(*BaseNRAlgo::value_map_[pos_el]); // go to the next element ++pos_el; } diff --git a/src/GaussSeidelSolver.cpp b/src/powerflow_algorithm/GaussSeidelAlgo.cpp similarity index 96% rename from src/GaussSeidelSolver.cpp rename to src/powerflow_algorithm/GaussSeidelAlgo.cpp index 365d02a..448dc9f 100644 --- a/src/GaussSeidelSolver.cpp +++ b/src/powerflow_algorithm/GaussSeidelAlgo.cpp @@ -6,9 +6,9 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "GaussSeidelSolver.h" +#include "GaussSeidelAlgo.h" -bool GaussSeidelSolver::compute_pf(const Eigen::SparseMatrix & Ybus, +bool GaussSeidelAlgo::compute_pf(const Eigen::SparseMatrix & Ybus, CplxVect & V, const CplxVect & Sbus, const Eigen::VectorXi & slack_ids, @@ -78,7 +78,7 @@ bool GaussSeidelSolver::compute_pf(const Eigen::SparseMatrix & Ybus, return res; } -void GaussSeidelSolver::one_iter(CplxVect & tmp_Sbus, +void GaussSeidelAlgo::one_iter(CplxVect & tmp_Sbus, const Eigen::SparseMatrix & Ybus, const Eigen::VectorXi & pv, const Eigen::VectorXi & pq) diff --git a/src/GaussSeidelSolver.h b/src/powerflow_algorithm/GaussSeidelAlgo.h similarity index 81% rename from src/GaussSeidelSolver.h rename to src/powerflow_algorithm/GaussSeidelAlgo.h index 26e0a46..2939966 100644 --- a/src/GaussSeidelSolver.h +++ b/src/powerflow_algorithm/GaussSeidelAlgo.h @@ -6,17 +6,17 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef GAUSSSEIDELSOLVER_H -#define GAUSSSEIDELSOLVER_H +#ifndef GAUSSSEIDEL_ALGO_H +#define GAUSSSEIDEL_ALGO_H -#include "BaseSolver.h" +#include "BaseAlgo.h" -class GaussSeidelSolver : public BaseSolver +class GaussSeidelAlgo : public BaseAlgo { public: - GaussSeidelSolver():BaseSolver() {}; + GaussSeidelAlgo():BaseAlgo(true) {}; - ~GaussSeidelSolver(){} + ~GaussSeidelAlgo(){} // todo can be factorized Eigen::SparseMatrix get_J(){ @@ -47,9 +47,9 @@ class GaussSeidelSolver : public BaseSolver private: // no copy allowed - GaussSeidelSolver( const GaussSeidelSolver & ) ; - GaussSeidelSolver & operator=( const GaussSeidelSolver & ) ; + GaussSeidelAlgo( const GaussSeidelAlgo & ) =delete; + GaussSeidelAlgo & operator=( const GaussSeidelAlgo & ) =delete; }; -#endif // GAUSSSEIDELSOLVER_H +#endif // GAUSSSEIDEL_ALGO_H diff --git a/src/GaussSeidelSynchSolver.cpp b/src/powerflow_algorithm/GaussSeidelSynchAlgo.cpp similarity index 95% rename from src/GaussSeidelSynchSolver.cpp rename to src/powerflow_algorithm/GaussSeidelSynchAlgo.cpp index 9225709..1a31bdb 100644 --- a/src/GaussSeidelSynchSolver.cpp +++ b/src/powerflow_algorithm/GaussSeidelSynchAlgo.cpp @@ -6,9 +6,9 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "GaussSeidelSynchSolver.h" +#include "GaussSeidelSynchAlgo.h" -void GaussSeidelSynchSolver::one_iter(CplxVect & tmp_Sbus, +void GaussSeidelSynchAlgo::one_iter(CplxVect & tmp_Sbus, const Eigen::SparseMatrix & Ybus, const Eigen::VectorXi & pv, const Eigen::VectorXi & pq) diff --git a/src/GaussSeidelSynchSolver.h b/src/powerflow_algorithm/GaussSeidelSynchAlgo.h similarity index 68% rename from src/GaussSeidelSynchSolver.h rename to src/powerflow_algorithm/GaussSeidelSynchAlgo.h index 57607ea..7749f08 100644 --- a/src/GaussSeidelSynchSolver.h +++ b/src/powerflow_algorithm/GaussSeidelSynchAlgo.h @@ -6,21 +6,21 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#ifndef GAUSSSEIDELSYNCHSOLVER_H -#define GAUSSSEIDELSYNCHSOLVER_H +#ifndef GAUSSSEIDELSYNCH_ALGO_H +#define GAUSSSEIDELSYNCH_ALGO_H -#include "GaussSeidelSolver.h" +#include "GaussSeidelAlgo.h" /** The gauss seidel method, where all the updates are happening in a synchronous way, instead of in a asynchronous way (like for standard gauss seidel) **/ -class GaussSeidelSynchSolver : public GaussSeidelSolver +class GaussSeidelSynchAlgo: public GaussSeidelAlgo { public: - GaussSeidelSynchSolver():GaussSeidelSolver() {}; + GaussSeidelSynchAlgo():GaussSeidelAlgo() {}; - ~GaussSeidelSynchSolver(){} + ~GaussSeidelSynchAlgo(){} protected: void one_iter(CplxVect & tmp_Sbus, @@ -31,9 +31,9 @@ class GaussSeidelSynchSolver : public GaussSeidelSolver private: // no copy allowed - GaussSeidelSynchSolver( const GaussSeidelSynchSolver & ) ; - GaussSeidelSynchSolver & operator=( const GaussSeidelSynchSolver & ) ; + GaussSeidelSynchAlgo( const GaussSeidelSynchAlgo & ) =delete; + GaussSeidelSynchAlgo & operator=( const GaussSeidelSynchAlgo & )=delete ; }; -#endif // GAUSSSEIDELSYNCHSOLVER_H +#endif // GAUSSSEIDELSYNCH_ALGO_H From 94f707d1ed21c0616b217e9c728bbe29c65707a1 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 18 Dec 2023 17:23:45 +0100 Subject: [PATCH 09/42] start systematic testing for solver control --- lightsim2grid/tests/test_solver_control.py | 198 +++++++++++++++++++++ lightsim2grid/tests/test_turnedoff_nopv.py | 4 +- src/powerflow_algorithm/BaseDCAlgo.tpp | 8 +- src/powerflow_algorithm/BaseNRAlgo.h | 1 + 4 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 lightsim2grid/tests/test_solver_control.py diff --git a/lightsim2grid/tests/test_solver_control.py b/lightsim2grid/tests/test_solver_control.py new file mode 100644 index 0000000..447042d --- /dev/null +++ b/lightsim2grid/tests/test_solver_control.py @@ -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") + \ No newline at end of file diff --git a/lightsim2grid/tests/test_turnedoff_nopv.py b/lightsim2grid/tests/test_turnedoff_nopv.py index 49fb8d3..0059d93 100644 --- a/lightsim2grid/tests/test_turnedoff_nopv.py +++ b/lightsim2grid/tests/test_turnedoff_nopv.py @@ -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] diff --git a/src/powerflow_algorithm/BaseDCAlgo.tpp b/src/powerflow_algorithm/BaseDCAlgo.tpp index ed7e8ab..0f249cb 100644 --- a/src/powerflow_algorithm/BaseDCAlgo.tpp +++ b/src/powerflow_algorithm/BaseDCAlgo.tpp @@ -30,14 +30,17 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & // 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(Ybus.rows()); #ifdef __COUT_TIMES @@ -83,7 +86,6 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & #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; diff --git a/src/powerflow_algorithm/BaseNRAlgo.h b/src/powerflow_algorithm/BaseNRAlgo.h index 2b68155..d593092 100644 --- a/src/powerflow_algorithm/BaseNRAlgo.h +++ b/src/powerflow_algorithm/BaseNRAlgo.h @@ -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() || From ca7dc71c7888e988ad6bd132e72c1f174775a949 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 19 Dec 2023 17:28:29 +0100 Subject: [PATCH 10/42] adding more test for the solver control --- lightsim2grid/tests/test_solver_control.py | 246 ++++++++++++++++++++- src/main.cpp | 1 - 2 files changed, 238 insertions(+), 9 deletions(-) diff --git a/lightsim2grid/tests/test_solver_control.py b/lightsim2grid/tests/test_solver_control.py index 447042d..2104eaf 100644 --- a/lightsim2grid/tests/test_solver_control.py +++ b/lightsim2grid/tests/test_solver_control.py @@ -3,12 +3,16 @@ # use backend._grid.tell_solver_need_reset() to force the reset of the solver # and by "something" do : # - disconnect line X +# - disconnect trafo X # - reconnect line X -# - change load -# - change gen -# - change shunt +# - reconnect trafo X +# - change load X +# - change gen X +# - change shunt X +# - change storage X # - change slack # - change slack weight +# - turnoff_gen_pv # - when it diverges (and then I can make it un converge normally) # - test change bus to -1 and deactivate element has the same impact @@ -21,9 +25,16 @@ import grid2op from grid2op.Action import CompleteAction from lightsim2grid import LightSimBackend +from lightsim2grid.solver import SolverType class TestSolverControl(unittest.TestCase): + def _aux_setup_grid(self): + self.need_dc = True # is it worth it to run DC powerflow ? + self.can_dist_slack = True + self.gridmodel.change_solver(SolverType.SparseLU) + self.gridmodel.change_solver(SolverType.DC) + def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -32,6 +43,7 @@ def setUp(self) -> None: action_class=CompleteAction, backend=LightSimBackend()) self.gridmodel = self.env.backend._grid + self._aux_setup_grid() self.v_init = 1.0 * self.env.backend.V self.iter = 10 self.tol_solver = 1e-8 # solver @@ -39,6 +51,8 @@ def setUp(self) -> None: 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""" + if not self.need_dc: + self.skipTest("Useless to run DC") 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() @@ -87,20 +101,19 @@ def aux_do_undo_ac(self, funname_undo="_reco_line_action", el_id=0, el_val=0., + to_add_remove=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) + getattr(self, funname_do)(gridmodel=self.gridmodel, el_id=el_id, el_val=el_val + to_add_remove) 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}" + 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}: max {np.abs(V_init - V_disc).max():.2e}" 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}" @@ -173,6 +186,8 @@ def test_disco_reco_line_ac(self, runpf_fun="_run_ac_pf"): 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""" + if not self.need_dc: + self.skipTest("Useless to run DC") self.test_disco_reco_line_ac(runpf_fun="_run_dc_pf") def test_disco_reco_trafo_ac(self, runpf_fun="_run_ac_pf"): @@ -194,5 +209,220 @@ def test_disco_reco_trafo_ac(self, runpf_fun="_run_ac_pf"): 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""" + if not self.need_dc: + self.skipTest("Useless to run DC") self.test_disco_reco_trafo_ac(runpf_fun="_run_dc_pf") + + def _change_load_p_action(self, gridmodel, el_id, el_val): + gridmodel.change_p_load(el_id, el_val) + + def test_change_load_p_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the load p with and + without restarting from scratch when running ac powerflow""" + for load in self.gridmodel.get_loads(): + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-3 + to_add_remove = 5. + self.aux_do_undo_ac(funname_do="_change_load_p_action", + funname_undo="_change_load_p_action", + runpf_fun=runpf_fun, + el_id=load.id, + expected_diff=expected_diff, + el_val=load.target_p_mw, + to_add_remove=to_add_remove, + ) + + def test_change_load_p_dc(self): + """test I have the same results if I change the load p with and + without restarting from scratch when running dc powerflow""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_change_load_p_ac(runpf_fun="_run_dc_pf") + + def _change_load_q_action(self, gridmodel, el_id, el_val): + gridmodel.change_q_load(el_id, el_val) + + def _change_gen_p_action(self, gridmodel, el_id, el_val): + gridmodel.change_p_gen(el_id, el_val) + + def test_change_load_q_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the load q with and + without restarting from scratch when running ac powerflow + + NB: this test has no sense in DC + """ + gen_bus = [el.bus_id for el in self.gridmodel.get_generators()] + for load in self.gridmodel.get_loads(): + if load.bus_id in gen_bus: + # nothing will change if there is a gen connected to the + # same bus as the load + continue + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-3 + to_add_remove = 5. + self.aux_do_undo_ac(funname_do="_change_load_q_action", + funname_undo="_change_load_q_action", + runpf_fun=runpf_fun, + el_id=load.id, + expected_diff=expected_diff, + el_val=load.target_q_mvar, + to_add_remove=to_add_remove, + ) + + def test_change_gen_p_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the gen p with and + without restarting from scratch when running ac powerflow""" + for gen in self.gridmodel.get_generators(): + if gen.is_slack: + # nothing to do for the slack bus... + continue + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-3 + to_add_remove = 5. + self.aux_do_undo_ac(funname_do="_change_gen_p_action", + funname_undo="_change_gen_p_action", + runpf_fun=runpf_fun, + el_id=gen.id, + expected_diff=expected_diff, + el_val=gen.target_p_mw, + to_add_remove=to_add_remove, + ) + + def test_change_gen_p_dc(self): + """test I have the same results if I change the gen p with and + without restarting from scratch when running dc powerflow""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_change_gen_p_ac(runpf_fun="_run_dc_pf") + + def _change_gen_v_action(self, gridmodel, el_id, el_val): + gridmodel.change_v_gen(el_id, el_val) + + def test_change_gen_v_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the gen v with and + without restarting from scratch when running ac powerflow + + NB: this test has no sense in DC + """ + gen_bus = [el.bus_id for el in self.gridmodel.get_generators()] + vn_kv = self.gridmodel.get_bus_vn_kv() + for gen in self.gridmodel.get_generators(): + if gen.bus_id in gen_bus: + # nothing will change if there is another gen connected to the + # same bus as the gen (error if everything is coded normally + # which it might not) + continue + + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-3 + to_add_remove = 0.1 * gen.target_vm_pu * vn_kv[gen.bus_id] + self.aux_do_undo_ac(funname_do="_change_gen_v_action", + funname_undo="_change_gen_v_action", + runpf_fun=runpf_fun, + el_id=gen.id, + expected_diff=expected_diff, + el_val=gen.target_vm_pu * vn_kv[gen.bus_id], + to_add_remove=to_add_remove, + ) + + def _change_shunt_p_action(self, gridmodel, el_id, el_val): + gridmodel.change_p_shunt(el_id, el_val) + + def test_change_shunt_p_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the shunt p with and + without restarting from scratch when running ac powerflow""" + for shunt in self.gridmodel.get_shunts(): + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-3 + to_add_remove = 5. + self.aux_do_undo_ac(funname_do="_change_shunt_p_action", + funname_undo="_change_shunt_p_action", + runpf_fun=runpf_fun, + el_id=shunt.id, + expected_diff=expected_diff, + el_val=shunt.target_p_mw, + to_add_remove=to_add_remove, + ) + + def test_change_shunt_p_dc(self): + """test I have the same results if I change the shunt p with and + without restarting from scratch when running dc powerflow""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_change_shunt_p_ac(runpf_fun="_run_dc_pf") + + def _change_shunt_q_action(self, gridmodel, el_id, el_val): + gridmodel.change_q_shunt(el_id, el_val) + + def test_change_shunt_q_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the shunt q with and + without restarting from scratch when running ac powerflow + + NB dc is not needed here (and does not make sense)""" + for shunt in self.gridmodel.get_shunts(): + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-3 + to_add_remove = 5. + self.aux_do_undo_ac(funname_do="_change_shunt_q_action", + funname_undo="_change_shunt_q_action", + runpf_fun=runpf_fun, + el_id=shunt.id, + expected_diff=expected_diff, + el_val=shunt.target_q_mvar, + to_add_remove=to_add_remove, + ) + + def _change_storage_p_action(self, gridmodel, el_id, el_val): + gridmodel.change_p_storage(el_id, el_val) + + def test_change_storage_p_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the storage p with and + without restarting from scratch when running ac powerflow""" + for storage in self.gridmodel.get_storages(): + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-3 + to_add_remove = 5. + self.aux_do_undo_ac(funname_do="_change_storage_p_action", + funname_undo="_change_storage_p_action", + runpf_fun=runpf_fun, + el_id=storage.id, + expected_diff=expected_diff, + el_val=storage.target_p_mw, + to_add_remove=to_add_remove, + ) + + def test_change_storage_p_dc(self): + """test I have the same results if I change the storage p with and + without restarting from scratch when running dc powerflow""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_change_storage_p_ac(runpf_fun="_run_dc_pf") + + def _change_storage_q_action(self, gridmodel, el_id, el_val): + gridmodel.change_q_storage(el_id, el_val) + + def test_change_storage_q_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the storage q with and + without restarting from scratch when running ac powerflow""" + gen_bus = [el.bus_id for el in self.gridmodel.get_generators()] + for storage in self.gridmodel.get_storages(): + if storage.bus_id in gen_bus: + # nothing will change if there is a gen connected to the + # same bus as the load + continue + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-3 + to_add_remove = 5. + self.aux_do_undo_ac(funname_do="_change_storage_q_action", + funname_undo="_change_storage_q_action", + runpf_fun=runpf_fun, + el_id=storage.id, + expected_diff=expected_diff, + el_val=storage.target_q_mvar, + to_add_remove=to_add_remove, + ) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 453e3d3..6545d63 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -592,7 +592,6 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def("get_line_param", &PandaPowerConverter::get_line_param) .def("get_trafo_param", &PandaPowerConverter::get_trafo_param); - py::class_(m, "SolverControl", "TODO") .def(py::init<>()) .def("has_dimension_changed", &SolverControl::has_dimension_changed, "TODO") From 1d80160a1a9f2eb4dbe6750c0148aa6fc3c89da9 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 21 Dec 2023 12:10:26 +0100 Subject: [PATCH 11/42] improve testing for solver control --- lightsim2grid/tests/test_solver_control.py | 173 ++++++++++++++++++++- src/GridModel.h | 4 +- src/element_container/GeneratorContainer.h | 12 +- 3 files changed, 180 insertions(+), 9 deletions(-) diff --git a/lightsim2grid/tests/test_solver_control.py b/lightsim2grid/tests/test_solver_control.py index 2104eaf..eb5eca5 100644 --- a/lightsim2grid/tests/test_solver_control.py +++ b/lightsim2grid/tests/test_solver_control.py @@ -10,10 +10,10 @@ # - change gen X # - change shunt X # - change storage X -# - change slack -# - change slack weight -# - turnoff_gen_pv -# - when it diverges (and then I can make it un converge normally) +# - change slack X +# - change slack weight X +# - turnoff_gen_pv X +# - when it diverges (and then I can make it un converge normally) X # - 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 @@ -422,7 +422,170 @@ def test_change_storage_q_ac(self, runpf_fun="_run_ac_pf"): to_add_remove=to_add_remove, ) + def _change_unique_slack_id(self, gridmodel, el_id, el_val): + # el_id : new slack + # el_val : old slack + gridmodel.remove_gen_slackbus(el_val) # remove old slack + gridmodel.add_gen_slackbus(el_id, 1.0) # add new slack + def _unchange_unique_slack_id(self, gridmodel, el_id, el_val): + # el_id : new slack + # el_val : old slack + gridmodel.remove_gen_slackbus(el_id) # remove new slack + gridmodel.add_gen_slackbus(el_val, 1.0) # add back old slack + + def test_change_gen_slack_unique_ac(self, runpf_fun="_run_ac_pf"): + """test I have the same results if I change the (for this test) unique slack bus + This is done in AC""" + gen_is_slack = [el.is_slack for el in self.gridmodel.get_generators()] + gen_id_slack_init = np.where(gen_is_slack)[0][0] + + for gen in self.gridmodel.get_generators(): + if gen_is_slack[gen.id]: + # generator was already slack, don't expect any change + continue + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-5 # change slack impact is low + self.aux_do_undo_ac(funname_do="_change_unique_slack_id", + funname_undo="_unchange_unique_slack_id", + runpf_fun=runpf_fun, + el_id=gen.id, # new slack + expected_diff=expected_diff, + el_val=gen_id_slack_init, # old slack + to_add_remove=0, + ) + + def test_change_gen_slack_unique_dc(self): + """test I have the same results if I change the (for this test) unique slack bus + This is done in DC""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_change_gen_slack_unique_ac(runpf_fun="_run_dc_pf") + + def _change_dist_slack_id(self, gridmodel, el_id, el_val): + # el_id : new slack + # el_val : old slack + gridmodel.add_gen_slackbus(el_val, 0.5) # set all slack a weight of 0.5 + gridmodel.add_gen_slackbus(el_id, 0.5) # add new slack with a weight of 0.5 + + def _unchange_dist_slack_id(self, gridmodel, el_id, el_val): + # el_id : new slack + # el_val : old slack + gridmodel.remove_gen_slackbus(el_id) # remove new slack + gridmodel.add_gen_slackbus(el_val, 1.0) # add back old slack with a weight of 1. (as originally) + + def test_distslack_weight_ac(self): + """test I have the same results if the slack weights (dist mode) are changed + + NB: not done in DC because as of now DC does not support dist slack + """ + if not self.can_dist_slack : + self.skipTest("This solver does not support distributed slack") + gen_is_slack = [el.is_slack for el in self.gridmodel.get_generators()] + gen_id_slack_init = np.where(gen_is_slack)[0][0] + runpf_fun = "_run_ac_pf" + for gen in self.gridmodel.get_generators(): + if gen_is_slack[gen.id]: + # generator was already slack, don't expect any change + continue + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-5 # change slack impact is low + self.aux_do_undo_ac(funname_do="_change_dist_slack_id", + funname_undo="_unchange_dist_slack_id", + runpf_fun=runpf_fun, + el_id=gen.id, # new added slack + expected_diff=expected_diff, + el_val=gen_id_slack_init, # old slack + to_add_remove=0, + ) + + def _change_turnedoff_pv(self, gridmodel, el_id, el_val): + gridmodel.turnedoff_no_pv() + + def _unchange_turnedoff_pv(self, gridmodel, el_id, el_val): + gridmodel.turnedoff_pv() + + def test_turnedoff_pv_ac(self): + """test the `turnedoff_pv` functionality + """ + runpf_fun = "_run_ac_pf" + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-2 # change slack impact is low + self.aux_do_undo_ac(funname_do="_change_turnedoff_pv", + funname_undo="_unchange_turnedoff_pv", + runpf_fun=runpf_fun, + el_id=0, + expected_diff=expected_diff, + el_val=0, + to_add_remove=0, + ) + + def _change_for_divergence_sbus(self, gridmodel, el_id, el_val): + # el_id=load_p_init, # initial loads + # el_val=load_p_div, # final loads + [gridmodel.change_p_load(l_id, val) for l_id, val in enumerate(el_val)] + + def _unchange_for_divergence_sbus(self, gridmodel, el_id, el_val): + # el_id=load_p_init, # initial loads + # el_val=load_p_div, # final loads + [gridmodel.change_p_load(l_id, val) for l_id, val in enumerate(el_id)] + + def test_divergence_sbus_ac(self): + """test I can make the grid diverge and converge again using sbus (ac mode only) + + It never diverge in DC because of Sbus""" + runpf_fun = "_run_ac_pf" + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-2 # change slack impact is low + load_p_init = 1.0 * np.array([el.target_p_mw for el in self.gridmodel.get_loads()]) + load_p_div = 5. * load_p_init + + # check that this load makes it diverge too + tmp_grid = self.gridmodel.copy() + [tmp_grid.change_p_load(l_id, val) for l_id, val in enumerate(load_p_div)] + V_tmp = tmp_grid.ac_pf(self.v_init, self.iter, self.tol_solver) + assert len(V_tmp) == 0, "should have diverged !" + + self.aux_do_undo_ac(funname_do="_change_for_divergence_sbus", + funname_undo="_unchange_for_divergence_sbus", + runpf_fun=runpf_fun, + el_id=load_p_init, # initial loads + expected_diff=expected_diff, + el_val=load_p_div, # final loads + to_add_remove=0., + ) + + def _change_for_divergence_ybus(self, gridmodel, el_id, el_val): + [gridmodel.deactivate_trafo(tr_id) for tr_id in el_id] + + def _unchange_for_divergence_ybus(self, gridmodel, el_id, el_val): + [gridmodel.reactivate_trafo(tr_id) for tr_id in el_id] + + def test_divergence_ybus_ac(self, runpf_fun="_run_ac_pf"): + """test I can make the grid diverge and converge again using ybus (islanding) in AC""" + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-2 # change slack impact is low + trafo_to_disc = [0, 1, 2] + + # check that this load makes it diverge too + tmp_grid = self.gridmodel.copy() + [tmp_grid.deactivate_trafo(tr_id) for tr_id in trafo_to_disc] + V_tmp = tmp_grid.ac_pf(self.v_init, self.iter, self.tol_solver) + assert len(V_tmp) == 0, "should have diverged !" + + self.aux_do_undo_ac(funname_do="_change_for_divergence_ybus", + funname_undo="_unchange_for_divergence_ybus", + runpf_fun=runpf_fun, + el_id=trafo_to_disc, # trafo to disco + expected_diff=expected_diff, + el_val=0., + to_add_remove=0., + ) + + def test_divergence_ybus_dc(self): + """test I can make the grid diverge and converge again using ybus (islanding) in DC""" + self.test_divergence_ybus_ac("_run_dc_pf") + + if __name__ == "__main__": unittest.main() - \ No newline at end of file diff --git a/src/GridModel.h b/src/GridModel.h index eee6cc7..b972d73 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -102,8 +102,8 @@ class GridModel : public GenericContainer // retrieve the underlying data (raw class) const GeneratorContainer & get_generators_as_data() const {return generators_;} - void turnedoff_no_pv(){generators_.turnedoff_no_pv();} // turned off generators are not pv - void turnedoff_pv(){generators_.turnedoff_pv();} // turned off generators are pv + void turnedoff_no_pv(){generators_.turnedoff_no_pv(solver_control_);} // turned off generators are not pv + void turnedoff_pv(){generators_.turnedoff_pv(solver_control_);} // turned off generators are pv bool get_turnedoff_gen_pv() {return generators_.get_turnedoff_gen_pv();} void update_slack_weights(Eigen::Ref > could_be_slack){ generators_.update_slack_weights(could_be_slack, solver_control_); diff --git a/src/element_container/GeneratorContainer.h b/src/element_container/GeneratorContainer.h index d9b854d..1c46327 100644 --- a/src/element_container/GeneratorContainer.h +++ b/src/element_container/GeneratorContainer.h @@ -224,8 +224,16 @@ class GeneratorContainer: public GenericContainer void set_p_slack(const RealVect& node_mismatch, const std::vector & id_grid_to_solver); // modification - void turnedoff_no_pv(){turnedoff_gen_pv_=false;} // turned off generators are not pv - void turnedoff_pv(){turnedoff_gen_pv_=true;} // turned off generators are pv + void turnedoff_no_pv(SolverControl & solver_control){ + solver_control.tell_slack_participate_changed(); + solver_control.tell_slack_weight_changed(); + turnedoff_gen_pv_=false; // turned off generators are not pv. This is NOT the default. + } + void turnedoff_pv(SolverControl & solver_control){ + solver_control.tell_slack_participate_changed(); + solver_control.tell_slack_weight_changed(); + turnedoff_gen_pv_=true; // turned off generators are pv. This is the default. + } bool get_turnedoff_gen_pv() const {return turnedoff_gen_pv_;} void update_slack_weights(Eigen::Ref > could_be_slack, SolverControl & solver_control); From 4d13a263d8569ec93361d91d84a64fe03856620c Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 21 Dec 2023 15:17:39 +0100 Subject: [PATCH 12/42] completing the test suite --- lightsim2grid/tests/test_solver_control.py | 169 ++++++++++++++++++++- src/GridModel.h | 4 - src/element_container/ShuntContainer.h | 8 +- src/powerflow_algorithm/BaseFDPFAlgo.tpp | 16 +- 4 files changed, 183 insertions(+), 14 deletions(-) diff --git a/lightsim2grid/tests/test_solver_control.py b/lightsim2grid/tests/test_solver_control.py index eb5eca5..aa1a441 100644 --- a/lightsim2grid/tests/test_solver_control.py +++ b/lightsim2grid/tests/test_solver_control.py @@ -14,9 +14,10 @@ # - change slack weight X # - turnoff_gen_pv X # - when it diverges (and then I can make it un converge normally) X -# - test change bus to -1 and deactivate element has the same impact +# - test change bus to -1 and deactivate element has the same impact (irrelevant !) +# - test when set_bus to 2 -# TODO and do that for all solver type: NR, NRSingleSlack, GaussSeidel, GaussSeidelSynch, FDPF_XB and FDPFBX +# TODO and do that for all solver type: NR X, 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 @@ -43,9 +44,9 @@ def setUp(self) -> None: action_class=CompleteAction, backend=LightSimBackend()) self.gridmodel = self.env.backend._grid - self._aux_setup_grid() - self.v_init = 1.0 * self.env.backend.V self.iter = 10 + self._aux_setup_grid() + self.v_init = 0.0 * self.env.backend.V + 1.04 # just to have a vector with the right dimension 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! @@ -586,6 +587,166 @@ def test_divergence_ybus_dc(self): """test I can make the grid diverge and converge again using ybus (islanding) in DC""" self.test_divergence_ybus_ac("_run_dc_pf") + def _aux_disco_load(self, gridmodel, el_id, el_val): + gridmodel.deactivate_load(el_id) + + def _aux_reco_load(self, gridmodel, el_id, el_val): + gridmodel.reactivate_load(el_id) + + def test_disco_reco_load_ac(self, runpf_fun="_run_ac_pf"): + """test I can disconnect a load (AC)""" + for load in self.gridmodel.get_loads(): + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-2 + if load.id == 3: + expected_diff = 3e-3 + self.aux_do_undo_ac(funname_do="_aux_disco_load", + funname_undo="_aux_reco_load", + runpf_fun=runpf_fun, + el_id=load.id, + expected_diff=expected_diff, + el_val=0, + to_add_remove=0, + ) + + def test_disco_reco_load_dc(self): + """test I can disconnect a load (DC)""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_disco_reco_load_ac(runpf_fun="_run_dc_pf") + + def _aux_disco_gen(self, gridmodel, el_id, el_val): + gridmodel.deactivate_gen(el_id) + + def _aux_reco_gen(self, gridmodel, el_id, el_val): + gridmodel.reactivate_gen(el_id) + + def test_disco_reco_gen_ac(self, runpf_fun="_run_ac_pf"): + """test I can disconnect a gen (AC)""" + for gen in self.gridmodel.get_generators(): + if gen.is_slack: + # by default single slack, so I don't disconnect it + continue + if gen.target_p_mw == 0.: + # will have not impact as by default gen with p==0. are still pv + continue + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-2 + if gen.id == 3: + expected_diff = 3e-3 + self.aux_do_undo_ac(funname_do="_aux_disco_gen", + funname_undo="_aux_reco_gen", + runpf_fun=runpf_fun, + el_id=gen.id, + expected_diff=expected_diff, + el_val=0, + to_add_remove=0, + ) + + def test_disco_reco_gen_dc(self): + """test I can disconnect a shunt (DC)""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_disco_reco_gen_ac(runpf_fun="_run_dc_pf") + + def _aux_disco_shunt(self, gridmodel, el_id, el_val): + gridmodel.deactivate_shunt(el_id) + + def _aux_reco_shunt(self, gridmodel, el_id, el_val): + gridmodel.reactivate_shunt(el_id) + + def test_disco_reco_shunt_ac(self, runpf_fun="_run_ac_pf"): + """test I can disconnect a shunt (AC)""" + for shunt in self.gridmodel.get_shunts(): + self.gridmodel.tell_solver_need_reset() + expected_diff = 1e-2 + if runpf_fun == "_run_dc_pf": + # in dc q is not used, so i skipped if no target_p_mw + if shunt.target_p_mw == 0: + continue + self.aux_do_undo_ac(funname_do="_aux_disco_shunt", + funname_undo="_aux_reco_shunt", + runpf_fun=runpf_fun, + el_id=shunt.id, + expected_diff=expected_diff, + el_val=0, + to_add_remove=0, + ) + + def test_disco_reco_shunt_dc(self): + """test I can disconnect a shunt (DC)""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_disco_reco_shunt_ac(runpf_fun="_run_dc_pf") + + def _aux_disco_shunt(self, gridmodel, el_id, el_val): + gridmodel.deactivate_bus(0) + gridmodel.reactivate_bus(15) + gridmodel.change_bus_powerline_or(0, 15) + gridmodel.change_bus_powerline_or(1, 15) + gridmodel.change_bus_gen(5, 15) + + def _aux_reco_shunt(self, gridmodel, el_id, el_val): + gridmodel.reactivate_bus(0) + gridmodel.deactivate_bus(15) + gridmodel.change_bus_powerline_or(0, 0) + gridmodel.change_bus_powerline_or(1, 0) + gridmodel.change_bus_gen(5, 0) + def test_change_bus2_ac(self, runpf_fun="_run_ac_pf"): + """test for bus 2, basic test I don't do it for all kind of objects (AC pf)""" + expected_diff = 1e-2 + self.aux_do_undo_ac(funname_do="_aux_disco_shunt", + funname_undo="_aux_reco_shunt", + runpf_fun=runpf_fun, + el_id=0, + expected_diff=expected_diff, + el_val=0, + to_add_remove=0, + ) + + +class TestSolverControlNRSing(TestSolverControl): + def _aux_setup_grid(self): + self.need_dc = False # is it worth it to run DC powerflow ? + self.can_dist_slack = False + self.gridmodel.change_solver(SolverType.SparseLUSingleSlack) + self.gridmodel.change_solver(SolverType.DC) + + +class TestSolverControlFDPF_XB(TestSolverControl): + def _aux_setup_grid(self): + self.need_dc = False # is it worth it to run DC powerflow ? + self.can_dist_slack = False + self.gridmodel.change_solver(SolverType.FDPF_XB_SparseLU) + self.gridmodel.change_solver(SolverType.DC) + self.iter = 30 + + +class TestSolverControlFDPF_BX(TestSolverControl): + def _aux_setup_grid(self): + self.need_dc = False # is it worth it to run DC powerflow ? + self.can_dist_slack = False + self.gridmodel.change_solver(SolverType.FDPF_BX_SparseLU) + self.gridmodel.change_solver(SolverType.DC) + self.iter = 30 + + +class TestSolverControlGaussSeidel(TestSolverControl): + def _aux_setup_grid(self): + self.need_dc = False # is it worth it to run DC powerflow ? + self.can_dist_slack = False + self.gridmodel.change_solver(SolverType.GaussSeidel) + self.iter = 350 + + +class TestSolverControlGaussSeidelSynch(TestSolverControl): + def _aux_setup_grid(self): + self.need_dc = False # is it worth it to run DC powerflow ? + self.can_dist_slack = False + self.gridmodel.change_solver(SolverType.GaussSeidelSynch) + self.iter = 1000 + + if __name__ == "__main__": unittest.main() diff --git a/src/GridModel.h b/src/GridModel.h index b972d73..123a711 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -717,8 +717,6 @@ class GridModel : public GenericContainer { (this->*fun_react)(el_id); // eg reactivate_load(load_id); (this->*fun_change)(el_id, new_bus_backend); // eg change_bus_load(load_id, new_bus_backend); - // topo_changed_ = true; - solver_control_.tell_dimension_changed(); } } else{ if(has_changed(el_pos)) @@ -728,8 +726,6 @@ class GridModel : public GenericContainer // bus_status_ is set to "false" in GridModel.update_topo // and a bus is activated if (and only if) one element is connected to it. // I must not set `bus_status_[new_bus_backend] = false;` in this case ! - // topo_changed_ = true; - solver_control_.tell_dimension_changed(); } } } diff --git a/src/element_container/ShuntContainer.h b/src/element_container/ShuntContainer.h index 2cbed98..4fe808c 100644 --- a/src/element_container/ShuntContainer.h +++ b/src/element_container/ShuntContainer.h @@ -131,13 +131,15 @@ class ShuntContainer : public GenericContainer void deactivate(int shunt_id, SolverControl & solver_control) { if(status_[shunt_id]){ - solver_control.tell_recompute_sbus(); + solver_control.tell_recompute_sbus(); // DC + solver_control.tell_recompute_ybus(); // AC } _deactivate(shunt_id, status_); } void reactivate(int shunt_id, SolverControl & solver_control) { - if(status_[shunt_id]){ - solver_control.tell_recompute_sbus(); + if(!status_[shunt_id]){ + solver_control.tell_recompute_sbus(); // DC + solver_control.tell_recompute_ybus(); // AC } _reactivate(shunt_id, status_); } diff --git a/src/powerflow_algorithm/BaseFDPFAlgo.tpp b/src/powerflow_algorithm/BaseFDPFAlgo.tpp index fe13caf..28a7c1b 100644 --- a/src/powerflow_algorithm/BaseFDPFAlgo.tpp +++ b/src/powerflow_algorithm/BaseFDPFAlgo.tpp @@ -33,20 +33,30 @@ bool BaseFDPFAlgo::compute_pf(const Eigen::SparseMatrix Date: Fri, 22 Dec 2023 11:57:34 +0100 Subject: [PATCH 13/42] fixing broken tests --- CHANGELOG.rst | 4 + lightsim2grid/tests/test_solver_control.py | 39 +++++++- src/GridModel.cpp | 98 +++++++++++++++++--- src/GridModel.h | 93 ++++++++++++------- src/element_container/DCLineContainer.h | 6 +- src/element_container/GeneratorContainer.cpp | 2 +- src/element_container/GeneratorContainer.h | 18 +++- src/element_container/GenericContainer.h | 3 +- src/element_container/LineContainer.h | 11 +++ src/element_container/LoadContainer.h | 8 ++ src/element_container/SGenContainer.h | 8 ++ src/element_container/ShuntContainer.h | 8 ++ src/element_container/TrafoContainer.h | 13 ++- src/main.cpp | 1 + src/powerflow_algorithm/BaseDCAlgo.tpp | 91 +++++++++--------- 15 files changed, 308 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 025c310..4a4113e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -32,11 +32,15 @@ Change Log these cases. - [FIXED] a bug leading to not propagate correctly the "compute_results" flag when the environment was copied (for example) +- [FIXED] a bug where copying a lightsim2grid `GridModel` did not fully copy it +- [FIXED] a bug in the "topo_vect" comprehension cpp side (sometimes some buses + might not be activated / deactivated correctly) - [ADDED] sets of methods to extract the main component of a grid and perform powerflow only on this one. - [ADDED] possibility to set / retrieve the names of each elements of the grid. - [ADDED] embed in the generator models the "non pv" behaviour. (TODO need to be able to change Q from python side) - [ADDED] computation of PTPF (Power Transfer Distribution Factor) is now possible +- [ADDED] (not tested) support for more than 2 busbars per substation - [IMPROVED] now performing the new grid2op `create_test_suite` - [IMPROVED] now lightsim2grid properly throw `BackendError` - [IMPROVED] clean ce cpp side by refactoring: making clearer the difference (linear) solver diff --git a/lightsim2grid/tests/test_solver_control.py b/lightsim2grid/tests/test_solver_control.py index aa1a441..4a1c413 100644 --- a/lightsim2grid/tests/test_solver_control.py +++ b/lightsim2grid/tests/test_solver_control.py @@ -25,6 +25,8 @@ import numpy as np import grid2op from grid2op.Action import CompleteAction +from grid2op.Parameters import Parameters + from lightsim2grid import LightSimBackend from lightsim2grid.solver import SolverType @@ -50,6 +52,41 @@ def setUp(self) -> None: 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_update_topo_ac(self, runpf_fun="_run_ac_pf"): + """test when I disconnect a line alone at one end: it changes the size of the ybus / sbus vector AC""" + LINE_ID = 2 + dim_topo = type(self.env).dim_topo + mask_changed = np.zeros(dim_topo, dtype=bool) + mask_val = np.zeros(dim_topo, dtype=np.int32) + mask_changed[type(self.env).line_ex_pos_topo_vect[LINE_ID]] = True + mask_val[type(self.env).line_ex_pos_topo_vect[LINE_ID]] = 2 + self.gridmodel.update_topo(mask_changed, mask_val) + V = getattr(self, runpf_fun)(gridmodel=self.gridmodel) + assert len(V), "it should not have diverged here" + self.gridmodel.unset_changes() + + mask_changed = np.zeros(dim_topo, dtype=bool) + mask_val = np.zeros(dim_topo, dtype=np.int32) + mask_changed[type(self.env).line_or_pos_topo_vect[LINE_ID]] = True + mask_val[type(self.env).line_or_pos_topo_vect[LINE_ID]] = -1 + # mask_changed[type(self.env).line_ex_pos_topo_vect[LINE_ID]] = True + # mask_val[type(self.env).line_ex_pos_topo_vect[LINE_ID]] = -1 + self.gridmodel.update_topo(mask_changed, mask_val) + solver = self.gridmodel.get_dc_solver() if runpf_fun == "_run_dc_pf" else self.gridmodel.get_solver() + V1 = getattr(self, runpf_fun)(gridmodel=self.gridmodel) + assert len(V1), f"it should not have diverged here. Error : {solver.get_error()}" + + self.gridmodel.tell_solver_need_reset() + V2 = getattr(self, runpf_fun)(gridmodel=self.gridmodel) + assert len(V2), f"it should not have diverged here. Error : {solver.get_error()}" + assert np.allclose(V1, V2, rtol=self.tol_equal, atol=self.tol_equal) + + def test_update_topo_dc(self): + """test when I disconnect a line alone at one end: it changes the size of the ybus / sbus vector AC""" + if not self.need_dc: + self.skipTest("Useless to run DC") + self.test_update_topo_ac("_run_dc_pf") + 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""" if not self.need_dc: @@ -138,7 +175,7 @@ def aux_do_undo_ac(self, 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}" + 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}: max {np.abs(V_init - V_reco).max():.2e}" 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}" diff --git a/src/GridModel.cpp b/src/GridModel.cpp index 5f4579a..ba83336 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -14,6 +14,7 @@ GridModel::GridModel(const GridModel & other) { reset(true, true, true); + max_nb_bus_per_sub_ = other.max_nb_bus_per_sub_; init_vm_pu_ = other.init_vm_pu_; sn_mva_ = other.sn_mva_; @@ -97,6 +98,22 @@ GridModel::StateRes GridModel::get_state() const auto res_storage = storages_.get_state(); auto res_dc_line = dc_lines_.get_state(); + std::vector load_pos_topo_vect(load_pos_topo_vect_.begin(), load_pos_topo_vect_.end()); + std::vector gen_pos_topo_vect(gen_pos_topo_vect_.begin(), gen_pos_topo_vect_.end()); + std::vector line_or_pos_topo_vect(line_or_pos_topo_vect_.begin(), line_or_pos_topo_vect_.end()); + std::vector line_ex_pos_topo_vect(line_ex_pos_topo_vect_.begin(), line_ex_pos_topo_vect_.end()); + std::vector trafo_hv_pos_topo_vect(trafo_hv_pos_topo_vect_.begin(), trafo_hv_pos_topo_vect_.end()); + std::vector trafo_lv_pos_topo_vect(trafo_lv_pos_topo_vect_.begin(), trafo_lv_pos_topo_vect_.end()); + std::vector storage_pos_topo_vect(storage_pos_topo_vect_.begin(), storage_pos_topo_vect_.end()); + + std::vector load_to_subid(load_to_subid_.begin(), load_to_subid_.end()); + std::vector gen_to_subid(gen_to_subid_.begin(), gen_to_subid_.end()); + std::vector line_or_to_subid(line_or_to_subid_.begin(), line_or_to_subid_.end()); + std::vector line_ex_to_subid(line_ex_to_subid_.begin(), line_ex_to_subid_.end()); + std::vector trafo_hv_to_subid(trafo_hv_to_subid_.begin(), trafo_hv_to_subid_.end()); + std::vector trafo_lv_to_subid(trafo_lv_to_subid_.begin(), trafo_lv_to_subid_.end()); + std::vector storage_to_subid(storage_to_subid_.begin(), storage_to_subid_.end()); + GridModel::StateRes res(version_major, version_medium, version_minor, @@ -112,7 +129,23 @@ GridModel::StateRes GridModel::get_state() const res_load, res_sgen, res_storage, - res_dc_line + res_dc_line, + n_sub_, + max_nb_bus_per_sub_, + load_pos_topo_vect, + gen_pos_topo_vect, + line_or_pos_topo_vect, + line_ex_pos_topo_vect, + trafo_hv_pos_topo_vect, + trafo_lv_pos_topo_vect, + storage_pos_topo_vect, + load_to_subid, + gen_to_subid, + line_or_to_subid, + line_ex_to_subid, + trafo_hv_to_subid, + trafo_lv_to_subid, + storage_to_subid ); return res; }; @@ -139,11 +172,11 @@ void GridModel::set_state(GridModel::StateRes & my_state) exc_ << "It is not possible. Please reinstall it."; throw std::runtime_error(exc_.str()); } - std::vector ls_to_pp_ = std::get<3>(my_state); + const std::vector & ls_to_pp = std::get<3>(my_state); init_vm_pu_ = std::get<4>(my_state); sn_mva_ = std::get<5>(my_state); - std::vector & bus_vn_kv = std::get<6>(my_state); - std::vector & bus_status = std::get<7>(my_state); + const std::vector & bus_vn_kv = std::get<6>(my_state); + const std::vector & bus_status = std::get<7>(my_state); // powerlines LineContainer::StateRes & state_lines = std::get<8>(my_state); @@ -162,13 +195,45 @@ void GridModel::set_state(GridModel::StateRes & my_state) // dc lines DCLineContainer::StateRes & state_dc_lines = std::get<15>(my_state); - // assign it to this instance + // grid2op specific + n_sub_ = std::get<16>(my_state); + max_nb_bus_per_sub_ = std::get<17>(my_state); + const std::vector & load_pos_topo_vect = std::get<18>(my_state); + const std::vector & gen_pos_topo_vect = std::get<19>(my_state); + const std::vector & line_or_pos_topo_vect = std::get<20>(my_state); + const std::vector & line_ex_pos_topo_vect = std::get<21>(my_state); + const std::vector & trafo_hv_pos_topo_vect = std::get<22>(my_state); + const std::vector & trafo_lv_pos_topo_vect = std::get<23>(my_state); + const std::vector & storage_pos_topo_vect = std::get<24>(my_state); + const std::vector & load_to_subid = std::get<25>(my_state); + const std::vector & gen_to_subid = std::get<26>(my_state); + const std::vector & line_or_to_subid = std::get<27>(my_state); + const std::vector & line_ex_to_subid = std::get<28>(my_state); + const std::vector & trafo_hv_to_subid = std::get<29>(my_state); + const std::vector & trafo_lv_to_subid = std::get<30>(my_state); + const std::vector & storage_to_subid = std::get<31>(my_state); + + load_pos_topo_vect_ = IntVectRowMaj::Map(load_pos_topo_vect.data(), load_pos_topo_vect.size()); + gen_pos_topo_vect_ = IntVectRowMaj::Map(gen_pos_topo_vect.data(), gen_pos_topo_vect.size()); + line_or_pos_topo_vect_ = IntVectRowMaj::Map(line_or_pos_topo_vect.data(), line_or_pos_topo_vect.size()); + line_ex_pos_topo_vect_ = IntVectRowMaj::Map(line_ex_pos_topo_vect.data(), line_ex_pos_topo_vect.size()); + trafo_hv_pos_topo_vect_ = IntVectRowMaj::Map(trafo_hv_pos_topo_vect.data(), trafo_hv_pos_topo_vect.size()); + trafo_lv_pos_topo_vect_ = IntVectRowMaj::Map(trafo_lv_pos_topo_vect.data(), trafo_lv_pos_topo_vect.size()); + storage_pos_topo_vect_ = IntVectRowMaj::Map(storage_pos_topo_vect.data(), storage_pos_topo_vect.size()); + load_to_subid_ = IntVectRowMaj::Map(load_to_subid.data(), load_to_subid.size()); + gen_to_subid_ = IntVectRowMaj::Map(gen_to_subid.data(), gen_to_subid.size()); + line_or_to_subid_ = IntVectRowMaj::Map(line_or_to_subid.data(), line_or_to_subid.size()); + line_ex_to_subid_ = IntVectRowMaj::Map(line_ex_to_subid.data(), line_ex_to_subid.size()); + trafo_hv_to_subid_ = IntVectRowMaj::Map(trafo_hv_to_subid.data(), trafo_hv_to_subid.size()); + trafo_lv_to_subid_ = IntVectRowMaj::Map(trafo_lv_to_subid.data(), trafo_lv_to_subid.size()); + storage_to_subid_ = IntVectRowMaj::Map(storage_to_subid.data(), storage_to_subid.size()); - set_ls_to_orig(IntVect::Map(&ls_to_pp_[0], ls_to_pp_.size())); // set also _orig_to_ls + // assign it to this instance + set_ls_to_orig(IntVect::Map(ls_to_pp.data(), 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()); + bus_vn_kv_ = RealVect::Map(bus_vn_kv.data(), bus_vn_kv.size()); // 2. bus status bus_status_ = bus_status; @@ -240,7 +305,8 @@ void GridModel::init_bus(const RealVect & bus_vn_kv, int nb_line, int nb_trafo){ and initialize the Ybus_ matrix at the proper shape **/ - const int nb_bus = static_cast(bus_vn_kv.size()); + const int nb_bus = static_cast(bus_vn_kv.size()); // size of buses are checked in set_max_nb_bus_per_sub + bus_vn_kv_ = bus_vn_kv; // base_kv bus_status_ = std::vector(nb_bus, true); // by default everything is connected @@ -997,9 +1063,6 @@ void GridModel::update_storages_p(Eigen::Ref > has_changed, Eigen::Ref > new_values) { - const int nb_bus = static_cast(bus_status_.size()); - for(int i = 0; i < nb_bus; ++i) bus_status_[i] = false; - update_topo_generic(has_changed, new_values, load_pos_topo_vect_, load_to_subid_, &GridModel::reactivate_load, @@ -1045,6 +1108,19 @@ void GridModel::update_topo(Eigen::Ref(bus_status_.size()); + for(int i = 0; i < nb_bus; ++i) bus_status_[i] = false; + + powerlines_.update_bus_status(bus_status_); // TODO have a function to dispatch that to all type of elements + shunts_.update_bus_status(bus_status_); + trafos_.update_bus_status(bus_status_); + loads_.update_bus_status(bus_status_); + sgens_.update_bus_status(bus_status_); + storages_.update_bus_status(bus_status_); + generators_.update_bus_status(bus_status_); + dc_lines_.update_bus_status(bus_status_); } // for FDPF (implementation of the alg 2 method FDBX (FDXB will follow) // TODO FDPF diff --git a/src/GridModel.h b/src/GridModel.h index 123a711..7a0a104 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -45,6 +45,8 @@ class GridModel : public GenericContainer { public: + typedef Eigen::Array IntVectRowMaj; + typedef std::tuple< int, // version major int, // version medium @@ -69,14 +71,32 @@ class GridModel : public GenericContainer // storage units LoadContainer::StateRes, //dc lines - DCLineContainer::StateRes + DCLineContainer::StateRes, + // grid2op specific + int, // n_sub + int, // max_nb_bus_per_sub + std::vector, // load_pos_topo_vect_ + std::vector, + std::vector, + std::vector, + std::vector, + std::vector, + std::vector, + std::vector, // load_to_subid_ + std::vector, + std::vector, + std::vector, + std::vector, + std::vector, + std::vector > StateRes; GridModel(): solver_control_(), compute_results_(true), init_vm_pu_(1.04), - sn_mva_(1.0){ + sn_mva_(1.0), + max_nb_bus_per_sub_(2){ _solver.change_solver(SolverType::SparseLU); _dc_solver.change_solver(SolverType::DC); _solver.set_gridmodel(this); @@ -591,6 +611,20 @@ class GridModel : public GenericContainer { n_sub_ = n_sub; } + void set_max_nb_bus_per_sub(int max_nb_bus_per_sub) + { + max_nb_bus_per_sub_ = max_nb_bus_per_sub; + if(bus_vn_kv_.size() != n_sub_ * max_nb_bus_per_sub_){ + std::ostringstream exc_; + exc_ << "GridModel::set_max_nb_bus_per_sub: "; + exc_ << "your model counts "; + exc_ << bus_vn_kv_.size() << " buses according to `bus_vn_kv_` but "; + exc_ << n_sub_ * max_nb_bus_per_sub_ << " according to n_sub_ * max_nb_bus_per_sub_."; + exc_ << "Both should match: either reinit it with another call to `init_bus` or set properly the number of "; + exc_ << "substations / buses per substations with `set_n_sub` / `set_max_nb_bus_per_sub`"; + throw std::runtime_error(exc_.str()); + } + } void fillSbus_other(CplxVect & res, bool ac, const std::vector& id_me_to_solver){ fillSbus_me(res, ac, id_me_to_solver); @@ -706,27 +740,23 @@ class GridModel : public GenericContainer { for(int el_id = 0; el_id < vect_pos.rows(); ++el_id) { + int el_pos = vect_pos(el_id); + if(! has_changed(el_pos)) continue; int new_bus = new_values(el_pos); if(new_bus > 0){ // new bus is a real bus, so i need to make sure to have it turned on, and then change the bus int init_bus_me = vect_subid(el_id); - int new_bus_backend = new_bus == 1 ? init_bus_me : init_bus_me + n_sub_ ; + int new_bus_backend = new_bus == 1 ? init_bus_me : init_bus_me + n_sub_ * (max_nb_bus_per_sub_ - 1); bus_status_[new_bus_backend] = true; - if(has_changed(el_pos)) - { - (this->*fun_react)(el_id); // eg reactivate_load(load_id); - (this->*fun_change)(el_id, new_bus_backend); // eg change_bus_load(load_id, new_bus_backend); - } + (this->*fun_react)(el_id); // eg reactivate_load(load_id); + (this->*fun_change)(el_id, new_bus_backend); // eg change_bus_load(load_id, new_bus_backend); } else{ - if(has_changed(el_pos)) - { - // new bus is negative, we deactivate it - (this->*fun_deact)(el_id);// eg deactivate_load(load_id); - // bus_status_ is set to "false" in GridModel.update_topo - // and a bus is activated if (and only if) one element is connected to it. - // I must not set `bus_status_[new_bus_backend] = false;` in this case ! - } + // new bus is negative, we deactivate it + (this->*fun_deact)(el_id);// eg deactivate_load(load_id); + // bus_status_ is set to "false" in GridModel.update_topo + // and a bus is activated if (and only if) one element is connected to it. + // I must not set `bus_status_[new_bus_backend] = false;` in this case ! } } } @@ -824,21 +854,22 @@ class GridModel : public GenericContainer // specific grid2op int n_sub_; - Eigen::Array load_pos_topo_vect_; - Eigen::Array gen_pos_topo_vect_; - Eigen::Array line_or_pos_topo_vect_; - Eigen::Array line_ex_pos_topo_vect_; - Eigen::Array trafo_hv_pos_topo_vect_; - Eigen::Array trafo_lv_pos_topo_vect_; - Eigen::Array storage_pos_topo_vect_; - - Eigen::Array load_to_subid_; - Eigen::Array gen_to_subid_; - Eigen::Array line_or_to_subid_; - Eigen::Array line_ex_to_subid_; - Eigen::Array trafo_hv_to_subid_; - Eigen::Array trafo_lv_to_subid_; - Eigen::Array storage_to_subid_; + int max_nb_bus_per_sub_; + IntVectRowMaj load_pos_topo_vect_; + IntVectRowMaj gen_pos_topo_vect_; + IntVectRowMaj line_or_pos_topo_vect_; + IntVectRowMaj line_ex_pos_topo_vect_; + IntVectRowMaj trafo_hv_pos_topo_vect_; + IntVectRowMaj trafo_lv_pos_topo_vect_; + IntVectRowMaj storage_pos_topo_vect_; + + IntVectRowMaj load_to_subid_; + IntVectRowMaj gen_to_subid_; + IntVectRowMaj line_or_to_subid_; + IntVectRowMaj line_ex_to_subid_; + IntVectRowMaj trafo_hv_to_subid_; + IntVectRowMaj trafo_lv_to_subid_; + IntVectRowMaj storage_to_subid_; }; diff --git a/src/element_container/DCLineContainer.h b/src/element_container/DCLineContainer.h index 14abf21..5ee14dc 100644 --- a/src/element_container/DCLineContainer.h +++ b/src/element_container/DCLineContainer.h @@ -189,7 +189,11 @@ class DCLineContainer : public GenericContainer virtual void get_graph(std::vector > & res) const {}; virtual void disconnect_if_not_in_main_component(std::vector & busbar_in_main_component); virtual void nb_line_end(std::vector & res) const; - + virtual void update_bus_status(std::vector & bus_status) const { + from_gen_.update_bus_status(bus_status); + to_gen_.update_bus_status(bus_status); + } + real_type get_qmin_or(int dcline_id) {return from_gen_.get_qmin(dcline_id);} real_type get_qmax_or(int dcline_id) {return from_gen_.get_qmax(dcline_id);} real_type get_qmin_ex(int dcline_id) {return to_gen_.get_qmin(dcline_id);} diff --git a/src/element_container/GeneratorContainer.cpp b/src/element_container/GeneratorContainer.cpp index 254813f..e45767b 100644 --- a/src/element_container/GeneratorContainer.cpp +++ b/src/element_container/GeneratorContainer.cpp @@ -432,7 +432,7 @@ void GeneratorContainer::set_q(const RealVect & reactive_mismatch, void GeneratorContainer::update_slack_weights(Eigen::Ref > could_be_slack, - SolverControl & solver_control) + SolverControl & solver_control) { const int nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id) diff --git a/src/element_container/GeneratorContainer.h b/src/element_container/GeneratorContainer.h index 1c46327..2dbdb4c 100644 --- a/src/element_container/GeneratorContainer.h +++ b/src/element_container/GeneratorContainer.h @@ -175,14 +175,15 @@ class GeneratorContainer: public GenericContainer **/ void add_slackbus(int gen_id, real_type weight, SolverControl & solver_control){ // TODO DEBUG MODE - if(weight <= 0.) throw std::runtime_error("GeneratorContainer::add_slackbus Cannot assign a negative weight to the slack bus."); + if(weight <= 0.) throw std::runtime_error("GeneratorContainer::add_slackbus Cannot assign a negative (<=0) weight to the slack bus."); if(!gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); gen_slackbus_[gen_id] = true; if(gen_slack_weight_[gen_id] != weight) solver_control.tell_slack_weight_changed(); gen_slack_weight_[gen_id] = weight; } void remove_slackbus(int gen_id, SolverControl & solver_control){ - if(!gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); + if(gen_slackbus_[gen_id]) solver_control.tell_slack_participate_changed(); + if(gen_slack_weight_[gen_id] != 0.) solver_control.tell_slack_weight_changed(); gen_slackbus_[gen_id] = false; gen_slack_weight_[gen_id] = 0.; } @@ -243,7 +244,7 @@ class GeneratorContainer: public GenericContainer solver_control.tell_recompute_sbus(); solver_control.tell_pq_changed(); // bus might now be pq if(voltage_regulator_on_[gen_id]) solver_control.tell_v_changed(); - if(!turnedoff_gen_pv_) solver_control.tell_pv_changed(); + solver_control.tell_pv_changed(); if(gen_slack_weight_[gen_id] != 0. || gen_slackbus_[gen_id]){ solver_control.tell_slack_participate_changed(); solver_control.tell_slack_weight_changed(); @@ -256,7 +257,7 @@ class GeneratorContainer: public GenericContainer solver_control.tell_recompute_sbus(); solver_control.tell_pq_changed(); // bus might now be pv if(voltage_regulator_on_[gen_id]) solver_control.tell_v_changed(); - if(!turnedoff_gen_pv_) solver_control.tell_pv_changed(); + solver_control.tell_pv_changed(); if(gen_slack_weight_[gen_id] != 0. || gen_slackbus_[gen_id]){ solver_control.tell_slack_participate_changed(); solver_control.tell_slack_weight_changed(); @@ -272,7 +273,14 @@ class GeneratorContainer: public GenericContainer int get_bus(int gen_id) {return _get_bus(gen_id, status_, bus_id_);} virtual void reconnect_connected_buses(std::vector & bus_status) const; virtual void disconnect_if_not_in_main_component(std::vector & busbar_in_main_component); - + virtual void update_bus_status(std::vector & bus_status) const { + const int nb_gen = nb(); + for(int gen_id = 0; gen_id < nb_gen; ++gen_id) + { + if(!status_[gen_id]) continue; + bus_status[bus_id_[gen_id]] = true; + } + } real_type get_qmin(int gen_id) {return min_q_.coeff(gen_id);} real_type get_qmax(int gen_id) {return max_q_.coeff(gen_id);} void change_p(int gen_id, real_type new_p, SolverControl & solver_control); diff --git a/src/element_container/GenericContainer.h b/src/element_container/GenericContainer.h index c0e6da3..d48ef00 100644 --- a/src/element_container/GenericContainer.h +++ b/src/element_container/GenericContainer.h @@ -91,7 +91,8 @@ class GenericContainer : public BaseConstants const std::vector & id_grid_to_solver) const {}; virtual void get_q(std::vector& q_by_bus) {}; - + virtual void update_bus_status(std::vector & bus_status) const {}; + void set_p_slack(const RealVect& node_mismatch, const std::vector & id_grid_to_solver) {}; static const int _deactivated_bus_id; diff --git a/src/element_container/LineContainer.h b/src/element_container/LineContainer.h index fb78817..7e6acf4 100644 --- a/src/element_container/LineContainer.h +++ b/src/element_container/LineContainer.h @@ -180,6 +180,15 @@ class LineContainer : public GenericContainer virtual void disconnect_if_not_in_main_component(std::vector & busbar_in_main_component); virtual void nb_line_end(std::vector & res) const; virtual void get_graph(std::vector > & res) const; + virtual void update_bus_status(std::vector & bus_status) const { + const int nb_ = nb(); + for(int el_id = 0; el_id < nb_; ++el_id) + { + if(!status_[el_id]) continue; + bus_status[bus_or_id_[el_id]] = true; + bus_status[bus_ex_id_[el_id]] = true; + } + } void deactivate(int powerline_id, SolverControl & solver_control) { // std::cout << "line: deactivate called\n"; @@ -187,6 +196,7 @@ class LineContainer : public GenericContainer solver_control.tell_recompute_ybus(); // but sparsity pattern do not change here (possibly one more coeff at 0.) solver_control.tell_ybus_some_coeffs_zero(); + solver_control.tell_dimension_changed(); // if the extremity of the line is alone on a bus, this can happen... } _deactivate(powerline_id, status_); } @@ -194,6 +204,7 @@ class LineContainer : public GenericContainer if(!status_[powerline_id]){ solver_control.tell_recompute_ybus(); solver_control.tell_ybus_change_sparsity_pattern(); // sparsity pattern might change: a non zero coeff can pop up + solver_control.tell_dimension_changed(); // if the extremity of the line is alone on a bus, this can happen... } _reactivate(powerline_id, status_); } diff --git a/src/element_container/LoadContainer.h b/src/element_container/LoadContainer.h index 3786d52..0483f3f 100644 --- a/src/element_container/LoadContainer.h +++ b/src/element_container/LoadContainer.h @@ -158,6 +158,14 @@ class LoadContainer : public GenericContainer virtual void disconnect_if_not_in_main_component(std::vector & busbar_in_main_component); virtual void fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const; + virtual void update_bus_status(std::vector & bus_status) const { + const int nb_ = nb(); + for(int el_id = 0; el_id < nb_; ++el_id) + { + if(!status_[el_id]) continue; + bus_status[bus_id_[el_id]] = true; + } + } void compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, diff --git a/src/element_container/SGenContainer.h b/src/element_container/SGenContainer.h index ae05cdb..b50a269 100644 --- a/src/element_container/SGenContainer.h +++ b/src/element_container/SGenContainer.h @@ -179,6 +179,14 @@ class SGenContainer: public GenericContainer virtual void fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const ; virtual void gen_p_per_bus(std::vector & res) const; + virtual void update_bus_status(std::vector & bus_status) const { + const int nb_ = nb(); + for(int el_id = 0; el_id < nb_; ++el_id) + { + if(!status_[el_id]) continue; + bus_status[bus_id_[el_id]] = true; + } + } void compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, diff --git a/src/element_container/ShuntContainer.h b/src/element_container/ShuntContainer.h index 4fe808c..8458acf 100644 --- a/src/element_container/ShuntContainer.h +++ b/src/element_container/ShuntContainer.h @@ -161,6 +161,14 @@ class ShuntContainer : public GenericContainer FDPFMethod xb_or_bx) const; virtual void fillYbus_spmat(Eigen::SparseMatrix & res, bool ac, const std::vector & id_grid_to_solver); virtual void fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const; // in DC i need that + virtual void update_bus_status(std::vector & bus_status) const { + const int nb_ = nb(); + for(int el_id = 0; el_id < nb_; ++el_id) + { + if(!status_[el_id]) continue; + bus_status[bus_id_[el_id]] = true; + } + } void compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, diff --git a/src/element_container/TrafoContainer.h b/src/element_container/TrafoContainer.h index 6e15025..fbb07f8 100644 --- a/src/element_container/TrafoContainer.h +++ b/src/element_container/TrafoContainer.h @@ -167,7 +167,7 @@ class TrafoContainer : public GenericContainer } if(id >= nb()) { - throw std::range_error("Generator out of bound. Not enough transformers on the grid."); + throw std::range_error("Trafo out of bound. Not enough transformers on the grid."); } return TrafoInfo(*this, id); } @@ -178,6 +178,7 @@ class TrafoContainer : public GenericContainer solver_control.tell_recompute_ybus(); // but sparsity pattern do not change here (possibly one more coeff at 0.) solver_control.tell_ybus_some_coeffs_zero(); + solver_control.tell_dimension_changed(); // if the extremity of the line is alone on a bus, this can happen... } _deactivate(trafo_id, status_); } @@ -185,6 +186,7 @@ class TrafoContainer : public GenericContainer if(!status_[trafo_id]){ solver_control.tell_recompute_ybus(); solver_control.tell_ybus_change_sparsity_pattern(); // this might change + solver_control.tell_dimension_changed(); // if the extremity of the line is alone on a bus, this can happen... } _reactivate(trafo_id, status_); } @@ -214,6 +216,15 @@ class TrafoContainer : public GenericContainer int nb_powerline, bool transpose) const; virtual void hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, bool ac, const std::vector & id_grid_to_solver); // needed for dc mode + virtual void update_bus_status(std::vector & bus_status) const { + const int nb_ = nb(); + for(int el_id = 0; el_id < nb_; ++el_id) + { + if(!status_[el_id]) continue; + bus_status[bus_hv_id_[el_id]] = true; + bus_status[bus_lv_id_[el_id]] = true; + } + } void compute_results(const Eigen::Ref & Va, const Eigen::Ref & Vm, diff --git a/src/main.cpp b/src/main.cpp index 6545d63..b798613 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -837,6 +837,7 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) // auxiliary functions .def("set_n_sub", &GridModel::set_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()) .def("set_line_or_pos_topo_vect", &GridModel::set_line_or_pos_topo_vect, DocGridModel::_internal_do_not_use.c_str()) diff --git a/src/powerflow_algorithm/BaseDCAlgo.tpp b/src/powerflow_algorithm/BaseDCAlgo.tpp index 0f249cb..f56c9e7 100644 --- a/src/powerflow_algorithm/BaseDCAlgo.tpp +++ b/src/powerflow_algorithm/BaseDCAlgo.tpp @@ -11,15 +11,15 @@ // TODO SLACK !!! template bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & Ybus, - CplxVect & V, - const CplxVect & Sbus, - const Eigen::VectorXi & slack_ids, - const RealVect & slack_weights, - const Eigen::VectorXi & pv, - const Eigen::VectorXi & pq, - int max_iter, - real_type tol - ) + CplxVect & V, + const CplxVect & Sbus, + const Eigen::VectorXi & slack_ids, + const RealVect & slack_weights, + const Eigen::VectorXi & pv, + const Eigen::VectorXi & pq, + int max_iter, + real_type tol + ) { // max_iter is ignored // tol is ignored @@ -27,7 +27,6 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & // and for the slack bus both the magnitude and the angle are used. if(!is_linear_solver_valid()) { - // err_ = ErrorType::NotInitError; return false; } BaseAlgo::reset_timer(); @@ -35,11 +34,12 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & auto timer = CustTimer(); if(_solver_control.need_reset_solver() || _solver_control.has_dimension_changed() || - _solver_control.has_ybus_some_coeffs_zero()){ + _solver_control.has_slack_participate_changed() || // the full "ybus without slack" has changed, everything needs to be recomputed_solver_control.ybus_change_sparsity_pattern() + _solver_control.ybus_change_sparsity_pattern() || + _solver_control.has_ybus_some_coeffs_zero() + ){ reset(); } - - if (_solver_control.ybus_change_sparsity_pattern()) need_factorize_ = true; sizeYbus_with_slack_ = static_cast(Ybus.rows()); @@ -47,34 +47,32 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & auto timer_preproc = CustTimer(); #endif // __COUT_TIMES - const CplxVect & Sbus_tmp = Sbus; - // TODO SLACK (for now i put all slacks as PV, except the first one) // this should be handled in Sbus, because we know the amount of power absorbed by the slack // so we can compute it correctly ! - // std::cout << "\t\t\tretrieve_pv_with_slack \n"; - // std::cout << "slack_ids: "; - // for(auto el: slack_ids) std::cout << el << ", "; - // std::cout << std::endl; - my_pv_ = retrieve_pv_with_slack(slack_ids, pv); - // std::cout << "my_pv_: "; - // for(auto el: my_pv_) std::cout << el << ", "; - // std::cout << std::endl; - // const Eigen::VectorXi & my_pv = pv; - - // find the slack buses - // std::cout << "\t\t\textract_slack_bus_id \n"; - slack_buses_ids_solver_ = extract_slack_bus_id(my_pv_, pq, sizeYbus_with_slack_); - sizeYbus_without_slack_ = sizeYbus_with_slack_ - slack_buses_ids_solver_.size(); - - // corresp bus -> solverbus - // std::cout << "\t\t\tfill_mat_bus_id \n"; - fill_mat_bus_id(sizeYbus_with_slack_); + if(need_factorize_ || + _solver_control.has_pv_changed()) { + my_pv_ = retrieve_pv_with_slack(slack_ids, pv); + } + + if(need_factorize_ || + _solver_control.has_pv_changed() || + _solver_control.has_pq_changed()) { + // find the slack buses + slack_buses_ids_solver_ = extract_slack_bus_id(my_pv_, pq, sizeYbus_with_slack_); + sizeYbus_without_slack_ = sizeYbus_with_slack_ - slack_buses_ids_solver_.size(); + + // corresp bus -> solverbus + fill_mat_bus_id(sizeYbus_with_slack_); + } // remove the slack bus from Ybus - // and extract only real part - // std::cout << "\t\t\tfill_dcYbus_noslack \n"; - fill_dcYbus_noslack(sizeYbus_with_slack_, Ybus); + if(need_factorize_ || + _solver_control.need_recompute_ybus() || + _solver_control.ybus_change_sparsity_pattern() || + _solver_control.has_ybus_some_coeffs_zero()) { + fill_dcYbus_noslack(sizeYbus_with_slack_, Ybus); + } #ifdef __COUT_TIMES std::cout << "\t dc: preproc: " << 1000. * timer_preproc.duration() << "ms" << std::endl; @@ -84,7 +82,14 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & #ifdef __COUT_TIMES auto timer_solve = CustTimer(); #endif // __COUT_TIMES + bool just_factorize = false; + if(_solver_control.ybus_change_sparsity_pattern() || + _solver_control.has_ybus_some_coeffs_zero()){ + need_factorize_ = true; + } + + // initialize the solver if needed if(need_factorize_){ ErrorType status_init = _linear_solver.initialize(dcYbus_noslack_); if(status_init != ErrorType::NoError){ @@ -96,16 +101,16 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & } // remove the slack bus from Sbus - // std::cout << "\t\t\t dcSbus_noslack_ \n"; - dcSbus_noslack_ = RealVect::Constant(sizeYbus_without_slack_, my_zero_); - for (int k=0; k < sizeYbus_with_slack_; ++k){ - if(mat_bus_id_(k) == -1) continue; // I don't add anything to the slack bus - const int col_res = mat_bus_id_(k); - dcSbus_noslack_(col_res) = std::real(Sbus_tmp(k)); + if(need_factorize_ || _solver_control.need_recompute_sbus()){ + dcSbus_noslack_ = RealVect::Constant(sizeYbus_without_slack_, my_zero_); + for (int k=0; k < sizeYbus_with_slack_; ++k){ + if(mat_bus_id_(k) == -1) continue; // I don't add anything to the slack bus + const int col_res = mat_bus_id_(k); + dcSbus_noslack_(col_res) = std::real(Sbus(k)); + } } // solve for theta: Sbus = dcY . theta (make a copy to keep dcSbus_noslack_) - // std::cout << "\t\t\t Va_dc_without_slack \n"; RealVect Va_dc_without_slack = dcSbus_noslack_; ErrorType error = _linear_solver.solve(dcYbus_noslack_, Va_dc_without_slack, just_factorize); if(error != ErrorType::NoError){ From 202a589ac68c99cac88b12265c4ff1c838d27cc7 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 22 Dec 2023 15:52:07 +0100 Subject: [PATCH 14/42] fixing some bugs for the batch powerflows --- lightsim2grid/securityAnalysis.py | 2 + lightsim2grid/tests/test_issue_56.py | 11 ++--- src/BaseMultiplePowerflow.cpp | 1 + src/BaseMultiplePowerflow.h | 6 +++ src/Computers.cpp | 2 + src/SecurityAnalysis.cpp | 4 ++ src/linear_solvers/CKTSOSolver.cpp | 4 +- src/linear_solvers/CKTSOSolver.h | 2 +- src/linear_solvers/KLUSolver.cpp | 4 +- src/linear_solvers/KLUSolver.h | 2 +- src/linear_solvers/NICSLUSolver.cpp | 4 +- src/linear_solvers/NICSLUSolver.h | 2 +- src/linear_solvers/SparseLUSolver.cpp | 4 +- src/linear_solvers/SparseLUSolver.h | 2 +- src/powerflow_algorithm/BaseDCAlgo.tpp | 56 +++++++++++++------------- 15 files changed, 61 insertions(+), 45 deletions(-) diff --git a/lightsim2grid/securityAnalysis.py b/lightsim2grid/securityAnalysis.py index a0cc724..f95d45f 100644 --- a/lightsim2grid/securityAnalysis.py +++ b/lightsim2grid/securityAnalysis.py @@ -300,9 +300,11 @@ def compute_V(self): """ v_init = self.grid2op_env.backend.V + print("self.computer.compute") self.computer.compute(v_init, self.grid2op_env.backend.max_it, self.grid2op_env.backend.tol) + print("self.computer.get_voltages()") self._vs = self.computer.get_voltages() self.__computed = True return self._vs diff --git a/lightsim2grid/tests/test_issue_56.py b/lightsim2grid/tests/test_issue_56.py index 9aed2a0..7bddb14 100644 --- a/lightsim2grid/tests/test_issue_56.py +++ b/lightsim2grid/tests/test_issue_56.py @@ -35,15 +35,16 @@ def test_dc(self): self.sa.add_all_n1_contingencies() res_p_dc, res_a_dc, res_v_dc = self.sa.get_flows() - assert np.any(res_p != res_p_dc) - assert np.any(res_a != res_a_dc) - assert np.any(res_v != res_v_dc) + assert np.any(res_p != res_p_dc), "DC and AC solver leads to same results" + assert np.any(res_a != res_a_dc), "DC and AC solver leads to same results" + assert np.any(res_v != res_v_dc), "DC and AC solver leads to same results" assert self.sa.computer.get_solver_type() == SolverType.DC + return nb_bus = self.env.n_sub nb_powerline = len(self.env.backend._grid.get_lines()) # now check with the DC computation - for l_id in range(self.env.n_line): + for l_id in range(type(self.env).n_line): grid_model = self.env.backend._grid.copy() if l_id < nb_powerline: grid_model.deactivate_powerline(l_id) @@ -55,7 +56,7 @@ def test_dc(self): if len(res): # model has converged, I check the results are the same # check voltages - assert np.allclose(res_v_dc[l_id, :nb_bus], res[:nb_bus]), f"error for contingency {l_id}" + assert np.allclose(res_v_dc[l_id, :nb_bus], res[:nb_bus]), f"error for contingency {l_id}: {np.abs(res_v_dc[l_id, :nb_bus]-res[:nb_bus]).max():.2e}" # now check the flows pl_dc, ql_dc, vl_dc, al_dc = grid_model.get_lineor_res() pt_dc, qt_dc, vt_dc, at_dc = grid_model.get_trafohv_res() diff --git a/src/BaseMultiplePowerflow.cpp b/src/BaseMultiplePowerflow.cpp index 48aac80..3c90661 100644 --- a/src/BaseMultiplePowerflow.cpp +++ b/src/BaseMultiplePowerflow.cpp @@ -22,6 +22,7 @@ bool BaseMultiplePowerflow::compute_one_powerflow(const Eigen::SparseMatrix gen_p, const Eigen::VectorXi & slack_ids = ac_solver_used ? _grid_model.get_slack_ids(): _grid_model.get_slack_ids_dc(); const RealVect & slack_weights = _grid_model.get_slack_weights(); _solver.reset(); + _solver_control.tell_none_changed(); + _solver_control.tell_recompute_sbus(); // now build the Sbus _Sbuses = CplxMat::Zero(nb_steps, nb_buses_solver); diff --git a/src/SecurityAnalysis.cpp b/src/SecurityAnalysis.cpp index ccab406..17f9c3c 100644 --- a/src/SecurityAnalysis.cpp +++ b/src/SecurityAnalysis.cpp @@ -165,6 +165,10 @@ void SecurityAnalysis::compute(const CplxVect & Vinit, int max_iter, real_type t // reset the solver _solver.reset(); + _solver_control.tell_none_changed(); + _solver_control.tell_recompute_ybus(); + // _solver_control.tell_ybus_some_coeffs_zero(); + // ybus does not change sparsity pattern here // compute the right Vinit to send to the solver CplxVect Vinit_solver = extract_Vsolver_from_Vinit(Vinit, nb_buses_solver, nb_total_bus, id_me_to_solver); diff --git a/src/linear_solvers/CKTSOSolver.cpp b/src/linear_solvers/CKTSOSolver.cpp index 8d5b734..4c5a01b 100644 --- a/src/linear_solvers/CKTSOSolver.cpp +++ b/src/linear_solvers/CKTSOSolver.cpp @@ -87,7 +87,7 @@ ErrorType CKTSOLinearSolver::initialize(Eigen::SparseMatrix & J){ return err; } -ErrorType CKTSOLinearSolver::solve(Eigen::SparseMatrix & J, RealVect & b, bool has_just_been_inialized){ +ErrorType CKTSOLinearSolver::solve(Eigen::SparseMatrix & J, RealVect & b, bool doesnt_need_refactor){ // solves (for x) the linear system J.x = b // with standard use of lightsim2grid, the solver should have already been initialized // J is const even if it does not compile if said const @@ -95,7 +95,7 @@ ErrorType CKTSOLinearSolver::solve(Eigen::SparseMatrix & J, RealVect bool stop = false; RealVect x; ErrorType err = ErrorType::NoError; - if(!has_just_been_inialized){ + if(!doesnt_need_refactor){ ret = solver_->Refactorize(J.valuePtr()); if (ret < 0) { // std::cout << "CKTSOLinearSolver::solve solver_->Refactorize error: " << ret << std::endl; diff --git a/src/linear_solvers/CKTSOSolver.h b/src/linear_solvers/CKTSOSolver.h index 2ed7e34..af98c93 100644 --- a/src/linear_solvers/CKTSOSolver.h +++ b/src/linear_solvers/CKTSOSolver.h @@ -62,7 +62,7 @@ class CKTSOLinearSolver // public api ErrorType reset(); ErrorType initialize(Eigen::SparseMatrix & J); - ErrorType solve(Eigen::SparseMatrix & J, RealVect & b, bool has_just_been_inialized); + ErrorType solve(Eigen::SparseMatrix & J, RealVect & b, bool doesnt_need_refactor); // can this linear solver solve problem where RHS is a matrix static const bool CAN_SOLVE_MAT; diff --git a/src/linear_solvers/KLUSolver.cpp b/src/linear_solvers/KLUSolver.cpp index fe8efc4..4e05361 100644 --- a/src/linear_solvers/KLUSolver.cpp +++ b/src/linear_solvers/KLUSolver.cpp @@ -37,14 +37,14 @@ ErrorType KLULinearSolver::initialize(Eigen::SparseMatrix& J){ return res; } -ErrorType KLULinearSolver::solve(Eigen::SparseMatrix& J, RealVect & b, bool has_just_been_initialized){ +ErrorType KLULinearSolver::solve(Eigen::SparseMatrix& J, RealVect & b, bool doesnt_need_refactor){ // solves (for x) the linear system J.x = b // supposes that the solver has been initialized (call klu_solver.analyze() before calling that) // J is const even if it does not compile if said const int ok; ErrorType err = ErrorType::NoError; bool stop = false; - if(!has_just_been_initialized){ + if(!doesnt_need_refactor){ // if the call to "klu_factor" has been made this iteration, there is no need // to re factor again the matrix // i'm in the case where it has not diff --git a/src/linear_solvers/KLUSolver.h b/src/linear_solvers/KLUSolver.h index 2eefe06..f75b554 100644 --- a/src/linear_solvers/KLUSolver.h +++ b/src/linear_solvers/KLUSolver.h @@ -46,7 +46,7 @@ class KLULinearSolver // public api ErrorType reset(); ErrorType initialize(Eigen::SparseMatrix& J); - ErrorType solve(Eigen::SparseMatrix& J, RealVect & b, bool has_just_been_inialized); + ErrorType solve(Eigen::SparseMatrix& J, RealVect & b, bool doesnt_need_refactor); // can this linear solver solve problem where RHS is a matrix static const bool CAN_SOLVE_MAT; diff --git a/src/linear_solvers/NICSLUSolver.cpp b/src/linear_solvers/NICSLUSolver.cpp index 357a672..a6f39a1 100644 --- a/src/linear_solvers/NICSLUSolver.cpp +++ b/src/linear_solvers/NICSLUSolver.cpp @@ -75,7 +75,7 @@ ErrorType NICSLULinearSolver::initialize(Eigen::SparseMatrix & J){ return err; } -ErrorType NICSLULinearSolver::solve(Eigen::SparseMatrix & J, RealVect & b, bool has_just_been_inialized){ +ErrorType NICSLULinearSolver::solve(Eigen::SparseMatrix & J, RealVect & b, bool doesnt_need_refactor){ // solves (for x) the linear system J.x = b // supposes that the solver has been initialized (call klu_solver.analyze() before calling that) // J is const even if it does not compile if said const @@ -83,7 +83,7 @@ ErrorType NICSLULinearSolver::solve(Eigen::SparseMatrix & J, RealVect bool stop = false; RealVect x; ErrorType err = ErrorType::NoError; - if(!has_just_been_inialized){ + if(!doesnt_need_refactor){ ret = solver_.FactorizeMatrix(J.valuePtr(), nb_thread_); // TODO maybe 0 instead of nb_thread_ here, see https://github.com/chenxm1986/nicslu/blob/master/nicslu202110/demo/demo2.cpp if (ret < 0) { // std::cout << "NICSLULinearSolver::solve solver_.FactorizeMatrix error: " << ret << std::endl; diff --git a/src/linear_solvers/NICSLUSolver.h b/src/linear_solvers/NICSLUSolver.h index 481b4a0..bb6412c 100644 --- a/src/linear_solvers/NICSLUSolver.h +++ b/src/linear_solvers/NICSLUSolver.h @@ -54,7 +54,7 @@ class NICSLULinearSolver // public api ErrorType reset(); ErrorType initialize(Eigen::SparseMatrix & J); - ErrorType solve(Eigen::SparseMatrix & J, RealVect & b, bool has_just_been_inialized); + ErrorType solve(Eigen::SparseMatrix & J, RealVect & b, bool doesnt_need_refactor); // can this linear solver solve problem where RHS is a matrix static const bool CAN_SOLVE_MAT; diff --git a/src/linear_solvers/SparseLUSolver.cpp b/src/linear_solvers/SparseLUSolver.cpp index 98c3149..5a1b6ab 100644 --- a/src/linear_solvers/SparseLUSolver.cpp +++ b/src/linear_solvers/SparseLUSolver.cpp @@ -23,13 +23,13 @@ ErrorType SparseLULinearSolver::initialize(const Eigen::SparseMatrix return res; } -ErrorType SparseLULinearSolver::solve(const Eigen::SparseMatrix & J, RealVect & b, bool has_just_been_inialized){ +ErrorType SparseLULinearSolver::solve(const Eigen::SparseMatrix & J, RealVect & b, bool doesnt_need_refactor){ // solves (for x) the linear system J.x = b // supposes that the solver has been initialized (call sparselu_solver.analyze() before calling that) // J is const even if it does not compile if said const ErrorType err = ErrorType::NoError; bool stop = false; - if(!has_just_been_inialized){ + if(!doesnt_need_refactor){ // if the call to "klu_factor" has been made this iteration, there is no need // to re factor again the matrix // i'm in the case where it has not diff --git a/src/linear_solvers/SparseLUSolver.h b/src/linear_solvers/SparseLUSolver.h index 768fd67..710abcc 100644 --- a/src/linear_solvers/SparseLUSolver.h +++ b/src/linear_solvers/SparseLUSolver.h @@ -34,7 +34,7 @@ class SparseLULinearSolver // public api ErrorType initialize(const Eigen::SparseMatrix & J); - ErrorType solve(const Eigen::SparseMatrix & J, RealVect & b, bool has_just_been_inialized); + ErrorType solve(const Eigen::SparseMatrix & J, RealVect & b, bool doesnt_need_refactor); ErrorType reset(){ return ErrorType::NoError; } // can this linear solver solve problem where RHS is a matrix diff --git a/src/powerflow_algorithm/BaseDCAlgo.tpp b/src/powerflow_algorithm/BaseDCAlgo.tpp index f56c9e7..bb59466 100644 --- a/src/powerflow_algorithm/BaseDCAlgo.tpp +++ b/src/powerflow_algorithm/BaseDCAlgo.tpp @@ -30,9 +30,11 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & return false; } BaseAlgo::reset_timer(); - + bool doesnt_need_refactor = true; + auto timer = CustTimer(); - if(_solver_control.need_reset_solver() || + if(need_factorize_ || + _solver_control.need_reset_solver() || _solver_control.has_dimension_changed() || _solver_control.has_slack_participate_changed() || // the full "ybus without slack" has changed, everything needs to be recomputed_solver_control.ybus_change_sparsity_pattern() _solver_control.ybus_change_sparsity_pattern() || @@ -47,17 +49,16 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & auto timer_preproc = CustTimer(); #endif // __COUT_TIMES - // TODO SLACK (for now i put all slacks as PV, except the first one) - // this should be handled in Sbus, because we know the amount of power absorbed by the slack - // so we can compute it correctly ! if(need_factorize_ || - _solver_control.has_pv_changed()) { + _solver_control.has_pv_changed() || + _solver_control.has_pq_changed()) { + + // TODO SLACK (for now i put all slacks as PV, except the first one) + // this should be handled in Sbus, because we know the amount of power absorbed by the slack + // so we can compute it correctly ! + // std::cout << "\tneed to retrieve slack\n"; my_pv_ = retrieve_pv_with_slack(slack_ids, pv); - } - if(need_factorize_ || - _solver_control.has_pv_changed() || - _solver_control.has_pq_changed()) { // find the slack buses slack_buses_ids_solver_ = extract_slack_bus_id(my_pv_, pq, sizeYbus_with_slack_); sizeYbus_without_slack_ = sizeYbus_with_slack_ - slack_buses_ids_solver_.size(); @@ -71,48 +72,47 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & _solver_control.need_recompute_ybus() || _solver_control.ybus_change_sparsity_pattern() || _solver_control.has_ybus_some_coeffs_zero()) { + // std::cout << "\tneed to change Ybus\n"; fill_dcYbus_noslack(sizeYbus_with_slack_, Ybus); + doesnt_need_refactor = false; // force a call to "factor" the linear solver as the lhs (ybus) changed + // no need to refactor if ybus did not change } #ifdef __COUT_TIMES std::cout << "\t dc: preproc: " << 1000. * timer_preproc.duration() << "ms" << std::endl; #endif // __COUT_TIMES - + // initialize the solver (only if needed) #ifdef __COUT_TIMES auto timer_solve = CustTimer(); #endif // __COUT_TIMES - bool just_factorize = false; - if(_solver_control.ybus_change_sparsity_pattern() || - _solver_control.has_ybus_some_coeffs_zero()){ - need_factorize_ = true; + // remove the slack bus from Sbus + if(need_factorize_ || _solver_control.need_recompute_sbus()){ + // std::cout << "\tneed to compute Sbus\n"; + dcSbus_noslack_ = RealVect::Constant(sizeYbus_without_slack_, my_zero_); + for (int k=0; k < sizeYbus_with_slack_; ++k){ + if(mat_bus_id_(k) == -1) continue; // I don't add anything to the slack bus + const int col_res = mat_bus_id_(k); + dcSbus_noslack_(col_res) = std::real(Sbus(k)); + } } // initialize the solver if needed if(need_factorize_){ + // std::cout << "\tneed to factorize\n"; ErrorType status_init = _linear_solver.initialize(dcYbus_noslack_); if(status_init != ErrorType::NoError){ err_ = status_init; return false; } need_factorize_ = false; - just_factorize = true; - } - - // remove the slack bus from Sbus - if(need_factorize_ || _solver_control.need_recompute_sbus()){ - dcSbus_noslack_ = RealVect::Constant(sizeYbus_without_slack_, my_zero_); - for (int k=0; k < sizeYbus_with_slack_; ++k){ - if(mat_bus_id_(k) == -1) continue; // I don't add anything to the slack bus - const int col_res = mat_bus_id_(k); - dcSbus_noslack_(col_res) = std::real(Sbus(k)); - } + doesnt_need_refactor = true; } // solve for theta: Sbus = dcY . theta (make a copy to keep dcSbus_noslack_) RealVect Va_dc_without_slack = dcSbus_noslack_; - ErrorType error = _linear_solver.solve(dcYbus_noslack_, Va_dc_without_slack, just_factorize); + ErrorType error = _linear_solver.solve(dcYbus_noslack_, Va_dc_without_slack, doesnt_need_refactor); if(error != ErrorType::NoError){ err_ = error; timer_total_nr_ += timer.duration(); @@ -270,7 +270,7 @@ RealMat BaseDCAlgo::get_ptdf(const Eigen::SparseMatrix rhs.array() = 0.; // rhs = RealVect::Zero(sizeYbus_without_slack_); } - // TODO PTDF: if the solver can solve the directly, do that instead + // TODO PTDF: if the solver can solve the MAT directly, do that instead return PTDF; } From cbe0b1bd553221d44e5e0b3aa504fb764d405602 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 22 Dec 2023 16:56:34 +0100 Subject: [PATCH 15/42] refactor the MultiplePower to BatchPowerflow, ContingencyAnalysis and TimeSeries --- CHANGELOG.rst | 4 + examples/computers_with_grid2op.py | 6 +- .../computers_with_grid2op_multithreading.py | 6 +- examples/security_analysis.py | 4 +- lightsim2grid/__init__.py | 8 +- lightsim2grid/contingencyAnalysis.py | 347 ++++++++++++++++++ lightsim2grid/securityAnalysis.py | 343 +---------------- lightsim2grid/tests/test_Computers.py | 12 +- lightsim2grid/tests/test_issue_56.py | 9 +- lightsim2grid/tests/test_solver_control.py | 3 + lightsim2grid/timeSerie.py | 11 +- setup.py | 6 +- .../BaseBatchSolverSynch.cpp} | 6 +- .../BaseBatchSolverSynch.h} | 13 +- .../ContingencyAnalysis.cpp} | 16 +- .../ContingencyAnalysis.h} | 26 +- .../TimeSeries.cpp} | 20 +- .../TimeSeries.h} | 12 +- src/main.cpp | 97 ++--- 19 files changed, 489 insertions(+), 460 deletions(-) create mode 100644 lightsim2grid/contingencyAnalysis.py rename src/{BaseMultiplePowerflow.cpp => batch_algorithm/BaseBatchSolverSynch.cpp} (93%) rename src/{BaseMultiplePowerflow.h => batch_algorithm/BaseBatchSolverSynch.h} (96%) rename src/{SecurityAnalysis.cpp => batch_algorithm/ContingencyAnalysis.cpp} (94%) rename src/{SecurityAnalysis.h => batch_algorithm/ContingencyAnalysis.h} (89%) rename src/{Computers.cpp => batch_algorithm/TimeSeries.cpp} (87%) rename src/{Computers.h => batch_algorithm/TimeSeries.h} (94%) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4a4113e..d5c04d2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -23,6 +23,10 @@ Change Log - [BREAKING] now able to retrieve `dcSbus` with a dedicated method (and not with the old `get_Sbus`). If you previously used `gridmodel.get_Subus()` to retrieve the Sbus used for DC powerflow, please use `gridmodel.get_dcSbus()` instead. +- [DEPRECATED] in the cpp class: the old `SecurityAnalysisCPP` has been renamed `ContingencyAnalysisCPP` + (you should not import it, but it you do you can `from lightsim2grid.securityAnalysis import ContingencyAnalysisCPP` now) +- [DEPRECATED] in the cpp class: the old `Computers` has been renamed `TimeSerieCPP` + (you should not import it, but it you do you can `from lightsim2grid.time_serie import TimeSerieCPP` now) - [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 diff --git a/examples/computers_with_grid2op.py b/examples/computers_with_grid2op.py index fbb930a..ceff6cf 100644 --- a/examples/computers_with_grid2op.py +++ b/examples/computers_with_grid2op.py @@ -7,7 +7,7 @@ # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. # ADVANCED USAGE -# This files explains how to use the Computers cpp class, for easier use +# This files explains how to use the TimeSeriesCPP cpp class, for easier use # please consult the documentation of TimeSeries or the # time_serie.py file ! @@ -16,7 +16,7 @@ import warnings import numpy as np from lightsim2grid import LightSimBackend -from lightsim2grid.timeSerie import Computers +from lightsim2grid.timeSerie import TimeSeriesCPP env_name = "l2rpn_neurips_2020_track2" test = True @@ -36,7 +36,7 @@ nb_sim = prod_p.shape[0] # now perform the computation -computer = Computers(grid) +computer = TimeSeriesCPP(grid) # print("start the computation") status = computer.compute_Vs(prod_p, np.zeros((nb_sim, 0)), # no static generators for now ! diff --git a/examples/computers_with_grid2op_multithreading.py b/examples/computers_with_grid2op_multithreading.py index ef9d80b..8307056 100644 --- a/examples/computers_with_grid2op_multithreading.py +++ b/examples/computers_with_grid2op_multithreading.py @@ -7,7 +7,7 @@ # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. # ADVANCED USAGE -# This files explains how to use the Computers cpp class, for easier use +# This files explains how to use the TimeSeriesCPP cpp class, for easier use # please consult the documentation of TimeSeries or the # time_serie.py file ! @@ -22,7 +22,7 @@ import grid2op from grid2op.Parameters import Parameters from lightsim2grid import LightSimBackend -from lightsim2grid.timeSerie import Computers +from lightsim2grid.timeSerie import TimeSeriesCPP NB_THREAD = 4 ENV_NAME = "l2rpn_neurips_2020_track2_small" @@ -51,7 +51,7 @@ def get_injs(env): def get_flows(grid, Vinit, prod_p, load_p, load_q, max_it=10, tol=1e-8): # now perform the computation - computer = Computers(grid) + computer = TimeSeriesCPP(grid) # print("start the computation") status = computer.compute_Vs(prod_p, np.zeros((prod_p.shape[0], 0)), # no static generators for now ! diff --git a/examples/security_analysis.py b/examples/security_analysis.py index dbdd5e6..ff137fa 100644 --- a/examples/security_analysis.py +++ b/examples/security_analysis.py @@ -14,7 +14,7 @@ from grid2op.Action import BaseAction from grid2op.Chronics import ChangeNothing import warnings -from lightsim2grid import LightSimBackend, SecurityAnalysis +from lightsim2grid import LightSimBackend, ContingencyAnalysis env_name = "l2rpn_neurips_2020_track2_small" test = False @@ -43,7 +43,7 @@ env_pp = multi_mix_env_pp[key_env] # Run the environment on a scenario using the TimeSerie module -security_analysis = SecurityAnalysis(env) +security_analysis = ContingencyAnalysis(env) security_analysis.add_all_n1_contingencies() p_or, a_or, voltages = security_analysis.get_flows() # the 3 lines above are the only lines you need to do to perform a security analysis ! diff --git a/lightsim2grid/__init__.py b/lightsim2grid/__init__.py index 929780f..057b4d1 100644 --- a/lightsim2grid/__init__.py +++ b/lightsim2grid/__init__.py @@ -39,10 +39,10 @@ print(f"TimeSerie import error: {exc_}") try: - from lightsim2grid.securityAnalysis import SecurityAnalysis - __all__.append("SecurityAnalysis") - __all__.append("securityAnalysis") + from lightsim2grid.contingencyAnalysis import ContingencyAnalysis + __all__.append("contingencyAnalysis") + __all__.append("ContingencyAnalysis") except ImportError as exc_: # grid2op is not installed, the SecurtiyAnalysis module will not be available pass - print(f"SecurityAnalysis import error: {exc_}") + print(f"ContingencyAnalysis import error: {exc_}") diff --git a/lightsim2grid/contingencyAnalysis.py b/lightsim2grid/contingencyAnalysis.py new file mode 100644 index 0000000..f16a504 --- /dev/null +++ b/lightsim2grid/contingencyAnalysis.py @@ -0,0 +1,347 @@ +# Copyright (c) 2020, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. + +__all__ = ["ContingencyAnalysisCPP", "ContingencyAnalysis", + # deprecated + "SecurityAnalysisCPP", "SecurityAnalysis", + ] + +import copy +import numpy as np +from collections.abc import Iterable + +from lightsim2grid.lightSimBackend import LightSimBackend +from lightsim2grid.solver import SolverType +from lightsim2grid_cpp import ContingencyAnalysisCPP + + +class ContingencyAnalysis(object): + """ + This class allows to perform a "security analysis" from a given grid state. + + For now, you cannot change the grid state, and it only computes the security analysis with + current flows at origin of powerlines. + + Feel free to post a feature request if you want to extend it. + + This class is used in 4 phases: + + 0) you create it from a grid2op environment (the grid topology will not be modified from this environment) + 1) you add some contingencies to simulate + 2) you start the simulation + 3) you read back the results + + + Examples + -------- + An example is given here + + .. code-block:: python + + import grid2op + from lightsim2grid import SecurityAnalysis + from lightsim2grid import LightSimBackend + env_name = ... + env = grid2op.make(env_name, backend=LightSimBackend()) + + 0) you create + security_analysis = SecurityAnalysis(env) + + 1) you add some contingencies to simulate + security_analysis.add_multiple_contingencies(...) # or security_analysis.add_single_contingency(...) + + 2) you start the simulation (done automatically) + 3) you read back the results + res_p, res_a, res_v = security_analysis.get_flows() + + # in this results, then + # res_a[row_id] will be the flows, on all powerline corresponding to the `row_id` contingency. + # you can retrieve it with `security_analysis.contingency_order[row_id]` + + Notes + ------ + + Sometimes, the behaviour might differ from grid2op. For example, if simulating a contingency + leads to a non connected grid, then this function will return "Nan" for the flows and 0. for + the voltages. + + In grid2op, it would be, in this case, 0. for the flows and 0. for the voltages. + + """ + STR_TYPES = (str, np.str_) # np.str deprecated in numpy 1.20 and earlier versions not supported anyway + + def __init__(self, grid2op_env): + if not isinstance(grid2op_env.backend, LightSimBackend): + raise RuntimeError("This class only works with LightSimBackend") + self.grid2op_env = grid2op_env.copy() + self.computer = ContingencyAnalysisCPP(self.grid2op_env.backend._grid) + self._contingency_order = {} # key: contingency (as tuple), value: order in which it is entered + self._all_contingencies = [] + self.__computed = False + self._vs = None + self._ampss = None + + self.available_solvers = self.computer.available_solvers() + if SolverType.KLU in self.available_solvers: + # use the faster KLU if available + self.computer.change_solver(SolverType.KLU) + + @property + def all_contingencies(self): + return copy.deepcopy(self._all_contingencies) + + @all_contingencies.setter + def all_contingencies(self, val): + raise RuntimeError("Impossible to add new topologies like this. Please use `add_single_contingency` " + "or `add_multiple_contingencies`.") + + def clear(self): + """ + Clear the list of contingencies to simulate + """ + self.computer.clear() + self._contingency_order = {} + self.__computed = False + self._vs = None + self._ampss = None + self._all_contingencies = [] + + def _single_cont_to_li_int(self, single_cont): + li_disc = [] + if isinstance(single_cont, int): + single_cont = [single_cont] + + for stuff in single_cont: + if isinstance(stuff, type(self).STR_TYPES): + stuff = np.where(self.grid2op_env.name_line == stuff) + stuff = stuff[0] + if stuff.size == 0: + # name is not found + raise RuntimeError(f"Impossible to find a powerline named \"{stuff}\" in the environment") + stuff = int(stuff[0]) + else: + stuff = int(stuff) + li_disc.append(stuff) + return li_disc + + def add_single_contingency(self, *args): + """ + This function allows to add a single contingency specified by either the powerlines names + (which should match env.name_line) or by their ID. + + The contingency added can be a "n-1" which will simulate a single powerline disconnection + or a "n-k" which will simulate the disconnection of multiple powerlines. + + It does not accept any keword arguments. + + Examples + -------- + + .. code-block:: python + + import grid2op + from lightsim2grid import SecurityAnalysis + from lightsim2grid import LightSimBackend + env_name = ... + env = grid2op.make(env_name, backend=LightSimBackend()) + + security_anlysis = SecurityAnalysis(env) + # the single (n-1) contingency "disconnect powerline 0" is added + security_anlysis.add_single_contingency(0) + + # add the single (n-1) contingency "disconnect line 1 + security_anlysis.add_single_contingency(env.name_line[1]) + + # add a single contingency that disconnect powerline 2 and 3 at the same time + security_anlysis.add_single_contingency(env.name_line[2], 3) + + Notes + ----- + If it raises an error for a given contingency, the object might be not properly initialized. + In this case, we recommend you to clear it (using the `clear()` method and to attempt to + add contingencies again.) + + """ + li_disc = self._single_cont_to_li_int(args) + li_disc_tup = tuple(li_disc) + if li_disc_tup not in self._contingency_order: + # this is really the first time this contingency is seen + try: + self.computer.add_nk(li_disc) + my_id = len(self._contingency_order) + self._contingency_order[li_disc_tup] = my_id + self._all_contingencies.append(li_disc_tup) + except Exception as exc_: + raise RuntimeError(f"Impossible to add the contingency {args}. The most likely cause " + f"is that you try to disconnect a powerline that is not present " + f"on the grid") from exc_ + + def add_multiple_contingencies(self, *args): + """ + This function will add multiple contingencies at the same time. + + This code is equivalent to: + + .. code-block:: python + + for single_cont in args: + self.add_single_contingency(single_cont) + + It does not accept any keword arguments. + + Examples + -------- + + .. code-block:: python + + import grid2op + from lightsim2grid import SecurityAnalysis + from lightsim2grid import LightSimBackend + env_name = ... + env = grid2op.make(env_name, backend=LightSimBackend()) + + security_anlysis = SecurityAnalysis(env) + + # add a single contingency that disconnect powerline 2 and 3 at the same time + security_anlysis.add_single_contingency(env.name_line[2], 3) + + # add a multiple contingencies the first one disconnect powerline 2 and + # and the second one disconnect powerline 3 + security_anlysis.add_multiple_contingencies(env.name_line[2], 3) + """ + for single_cont in args: + if isinstance(single_cont, Iterable) and not isinstance(single_cont, type(self).STR_TYPES): + # this is a contingency consisting in cutting multiple powerlines + self.add_single_contingency(*single_cont) + else: + # this is likely an int or a string representing a contingency + self.add_single_contingency(single_cont) + + def add_all_n1_contingencies(self): + """ + This method registers as the contingencies that will be computed all the contingencies that disconnects 1 powerline + + This is equivalent to: + + .. code-block:: python + + for single_cont_id in range(env.n_line): + self.add_single_contingency(single_cont_id) + """ + for single_cont_id in range(self.grid2op_env.n_line): + self.add_single_contingency(single_cont_id) + + def get_flows(self, *args): + """ + Retrieve the flows after each contingencies has been simulated. + + Each row of the resulting flow matrix will correspond to a contingency simulated in the arguments. + + You can require only the result on some contingencies with the `args` argument, but in each case, all the results will + be computed. If you don't specify anything, the results will be returned for all contingencies (which we recommend to do) + + Examples + -------- + + .. code-block:: python + + import grid2op + from lightsim2grid import SecurityAnalysis + from lightsim2grid import LightSimBackend + env_name = ... + env = grid2op.make(env_name, backend=LightSimBackend()) + + security_analysis = SecurityAnalysis(env) + security_analysis.add_multiple_contingencies(...) # or security_analysis.add_single_contingency(...) + res_p, res_a, res_v = security_analysis.get_flows() + + # in this results, then + # res_a[row_id] will be the flows, on all powerline corresponding to the `row_id` contingency. + # you can retrieve it with `security_analysis.contingency_order[row_id]` + """ + + all_defaults = self.computer.my_defaults() + if len(args) == 0: + # default: i consider all contingencies + orders_ = np.zeros(len(all_defaults), dtype=int) + for id_cpp, cont_ in enumerate(all_defaults): + tup_ = tuple(cont_) + orders_[self._contingency_order[tup_]] = id_cpp + else: + # a list of interesting contingencies has been provided + orders_ = np.zeros(len(args), dtype=int) + all_defaults = [tuple(cont) for cont in all_defaults] + for id_me, cont_ in enumerate(args): + cont_li = self._single_cont_to_li_int(cont_) + tup_ = tuple(cont_li) + if tup_ not in self._contingency_order: + raise RuntimeError(f"Contingency {cont_} is not simulated by this class. Have you called " + f"`add_single_contingency` or `add_multiple_contingencies` ?") + id_cpp = all_defaults.index(tup_) + orders_[id_me] = id_cpp + + if not self.__computed: + self.compute_V() + self.compute_A() + self.compute_P() + + return self._mws[orders_], self._ampss[orders_], self._vs[orders_] + + def compute_V(self): + """ + This function allows to retrieve the complex voltage at each bus of the grid for each contingency. + + .. warning:: Order of the results + + The order in which the results are returned is NOT necessarily the order in which the contingencies have + been entered. Please use `get_flows()` method for easier reading back of the results + + """ + v_init = self.grid2op_env.backend.V + self.computer.compute(v_init, + self.grid2op_env.backend.max_it, + self.grid2op_env.backend.tol) + self._vs = self.computer.get_voltages() + self.__computed = True + return self._vs + + def compute_A(self): + """ + This function returns the current flows (in Amps, A) at the origin / high voltage side + + .. warning:: Order of the results + + The order in which the results are returned is NOT necessarily the order in which the contingencies have + been entered. Please use `get_flows()` method for easier reading back of the results ! + + """ + if not self.__computed: + raise RuntimeError("This function can only be used if compute_V has been sucessfully called") + self._ampss = 1e3 * self.computer.compute_flows() + return self._ampss + + def compute_P(self): + """ + This function returns the active power flows (in MW) at the origin / high voltage side + + .. warning:: Order of the results + + The order in which the results are returned is NOT necessarily the order in which the contingencies have + been entered. Please use `get_flows()` method for easier reading back of the results ! + + """ + if not self.__computed: + raise RuntimeError("This function can only be used if compute_V has been sucessfully called") + self._mws = 1.0 * self.computer.compute_power_flows() + return self._mws + + def close(self): + """permanently close the object""" + self.grid2op_env.close() + self.clear() + self.computer.close() diff --git a/lightsim2grid/securityAnalysis.py b/lightsim2grid/securityAnalysis.py index f95d45f..f9db2b1 100644 --- a/lightsim2grid/securityAnalysis.py +++ b/lightsim2grid/securityAnalysis.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, RTE (https://www.rte-france.com) +# Copyright (c) 2023, RTE (https://www.rte-france.com) # See AUTHORS.txt # This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. # If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,341 +6,8 @@ # SPDX-License-Identifier: MPL-2.0 # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -__all__ = ["SecurityAnalysisCPP", "SecurityAnalysis"] +from lightsim2grid.contingencyAnalysis import ContingencyAnalysisCPP, ContingencyAnalysis -import copy -import numpy as np -from collections.abc import Iterable - -from lightsim2grid.lightSimBackend import LightSimBackend -from lightsim2grid.solver import SolverType -from lightsim2grid_cpp import SecurityAnalysisCPP - - -class SecurityAnalysis(object): - """ - This class allows to perform a "security analysis" from a given grid state. - - For now, you cannot change the grid state, and it only computes the security analysis with - current flows at origin of powerlines. - - Feel free to post a feature request if you want to extend it. - - This class is used in 4 phases: - - 0) you create it from a grid2op environment (the grid topology will not be modified from this environment) - 1) you add some contingencies to simulate - 2) you start the simulation - 3) you read back the results - - - Examples - -------- - An example is given here - - .. code-block:: python - - import grid2op - from lightsim2grid import SecurityAnalysis - from lightsim2grid import LightSimBackend - env_name = ... - env = grid2op.make(env_name, backend=LightSimBackend()) - - 0) you create - security_analysis = SecurityAnalysis(env) - - 1) you add some contingencies to simulate - security_analysis.add_multiple_contingencies(...) # or security_analysis.add_single_contingency(...) - - 2) you start the simulation (done automatically) - 3) you read back the results - res_p, res_a, res_v = security_analysis.get_flows() - - # in this results, then - # res_a[row_id] will be the flows, on all powerline corresponding to the `row_id` contingency. - # you can retrieve it with `security_analysis.contingency_order[row_id]` - - Notes - ------ - - Sometimes, the behaviour might differ from grid2op. For example, if simulating a contingency - leads to a non connected grid, then this function will return "Nan" for the flows and 0. for - the voltages. - - In grid2op, it would be, in this case, 0. for the flows and 0. for the voltages. - - """ - STR_TYPES = (str, np.str_) # np.str deprecated in numpy 1.20 and earlier versions not supported anyway - - def __init__(self, grid2op_env): - if not isinstance(grid2op_env.backend, LightSimBackend): - raise RuntimeError("This class only works with LightSimBackend") - self.grid2op_env = grid2op_env.copy() - self.computer = SecurityAnalysisCPP(self.grid2op_env.backend._grid) - self._contingency_order = {} # key: contingency (as tuple), value: order in which it is entered - self._all_contingencies = [] - self.__computed = False - self._vs = None - self._ampss = None - - self.available_solvers = self.computer.available_solvers() - if SolverType.KLU in self.available_solvers: - # use the faster KLU if available - self.computer.change_solver(SolverType.KLU) - - @property - def all_contingencies(self): - return copy.deepcopy(self._all_contingencies) - - @all_contingencies.setter - def all_contingencies(self, val): - raise RuntimeError("Impossible to add new topologies like this. Please use `add_single_contingency` " - "or `add_multiple_contingencies`.") - - def clear(self): - """ - Clear the list of contingencies to simulate - """ - self.computer.clear() - self._contingency_order = {} - self.__computed = False - self._vs = None - self._ampss = None - self._all_contingencies = [] - - def _single_cont_to_li_int(self, single_cont): - li_disc = [] - if isinstance(single_cont, int): - single_cont = [single_cont] - - for stuff in single_cont: - if isinstance(stuff, type(self).STR_TYPES): - stuff = np.where(self.grid2op_env.name_line == stuff) - stuff = stuff[0] - if stuff.size == 0: - # name is not found - raise RuntimeError(f"Impossible to find a powerline named \"{stuff}\" in the environment") - stuff = int(stuff[0]) - else: - stuff = int(stuff) - li_disc.append(stuff) - return li_disc - - def add_single_contingency(self, *args): - """ - This function allows to add a single contingency specified by either the powerlines names - (which should match env.name_line) or by their ID. - - The contingency added can be a "n-1" which will simulate a single powerline disconnection - or a "n-k" which will simulate the disconnection of multiple powerlines. - - It does not accept any keword arguments. - - Examples - -------- - - .. code-block:: python - - import grid2op - from lightsim2grid import SecurityAnalysis - from lightsim2grid import LightSimBackend - env_name = ... - env = grid2op.make(env_name, backend=LightSimBackend()) - - security_anlysis = SecurityAnalysis(env) - # the single (n-1) contingency "disconnect powerline 0" is added - security_anlysis.add_single_contingency(0) - - # add the single (n-1) contingency "disconnect line 1 - security_anlysis.add_single_contingency(env.name_line[1]) - - # add a single contingency that disconnect powerline 2 and 3 at the same time - security_anlysis.add_single_contingency(env.name_line[2], 3) - - Notes - ----- - If it raises an error for a given contingency, the object might be not properly initialized. - In this case, we recommend you to clear it (using the `clear()` method and to attempt to - add contingencies again.) - - """ - li_disc = self._single_cont_to_li_int(args) - li_disc_tup = tuple(li_disc) - if li_disc_tup not in self._contingency_order: - # this is really the first time this contingency is seen - try: - self.computer.add_nk(li_disc) - my_id = len(self._contingency_order) - self._contingency_order[li_disc_tup] = my_id - self._all_contingencies.append(li_disc_tup) - except Exception as exc_: - raise RuntimeError(f"Impossible to add the contingency {args}. The most likely cause " - f"is that you try to disconnect a powerline that is not present " - f"on the grid") from exc_ - - def add_multiple_contingencies(self, *args): - """ - This function will add multiple contingencies at the same time. - - This code is equivalent to: - - .. code-block:: python - - for single_cont in args: - self.add_single_contingency(single_cont) - - It does not accept any keword arguments. - - Examples - -------- - - .. code-block:: python - - import grid2op - from lightsim2grid import SecurityAnalysis - from lightsim2grid import LightSimBackend - env_name = ... - env = grid2op.make(env_name, backend=LightSimBackend()) - - security_anlysis = SecurityAnalysis(env) - - # add a single contingency that disconnect powerline 2 and 3 at the same time - security_anlysis.add_single_contingency(env.name_line[2], 3) - - # add a multiple contingencies the first one disconnect powerline 2 and - # and the second one disconnect powerline 3 - security_anlysis.add_multiple_contingencies(env.name_line[2], 3) - """ - for single_cont in args: - if isinstance(single_cont, Iterable) and not isinstance(single_cont, type(self).STR_TYPES): - # this is a contingency consisting in cutting multiple powerlines - self.add_single_contingency(*single_cont) - else: - # this is likely an int or a string representing a contingency - self.add_single_contingency(single_cont) - - def add_all_n1_contingencies(self): - """ - This method registers as the contingencies that will be computed all the contingencies that disconnects 1 powerline - - This is equivalent to: - - .. code-block:: python - - for single_cont_id in range(env.n_line): - self.add_single_contingency(single_cont_id) - """ - for single_cont_id in range(self.grid2op_env.n_line): - self.add_single_contingency(single_cont_id) - - def get_flows(self, *args): - """ - Retrieve the flows after each contingencies has been simulated. - - Each row of the resulting flow matrix will correspond to a contingency simulated in the arguments. - - You can require only the result on some contingencies with the `args` argument, but in each case, all the results will - be computed. If you don't specify anything, the results will be returned for all contingencies (which we recommend to do) - - Examples - -------- - - .. code-block:: python - - import grid2op - from lightsim2grid import SecurityAnalysis - from lightsim2grid import LightSimBackend - env_name = ... - env = grid2op.make(env_name, backend=LightSimBackend()) - - security_analysis = SecurityAnalysis(env) - security_analysis.add_multiple_contingencies(...) # or security_analysis.add_single_contingency(...) - res_p, res_a, res_v = security_analysis.get_flows() - - # in this results, then - # res_a[row_id] will be the flows, on all powerline corresponding to the `row_id` contingency. - # you can retrieve it with `security_analysis.contingency_order[row_id]` - """ - - all_defaults = self.computer.my_defaults() - if len(args) == 0: - # default: i consider all contingencies - orders_ = np.zeros(len(all_defaults), dtype=int) - for id_cpp, cont_ in enumerate(all_defaults): - tup_ = tuple(cont_) - orders_[self._contingency_order[tup_]] = id_cpp - else: - # a list of interesting contingencies has been provided - orders_ = np.zeros(len(args), dtype=int) - all_defaults = [tuple(cont) for cont in all_defaults] - for id_me, cont_ in enumerate(args): - cont_li = self._single_cont_to_li_int(cont_) - tup_ = tuple(cont_li) - if tup_ not in self._contingency_order: - raise RuntimeError(f"Contingency {cont_} is not simulated by this class. Have you called " - f"`add_single_contingency` or `add_multiple_contingencies` ?") - id_cpp = all_defaults.index(tup_) - orders_[id_me] = id_cpp - - if not self.__computed: - self.compute_V() - self.compute_A() - self.compute_P() - - return self._mws[orders_], self._ampss[orders_], self._vs[orders_] - - def compute_V(self): - """ - This function allows to retrieve the complex voltage at each bus of the grid for each contingency. - - .. warning:: Order of the results - - The order in which the results are returned is NOT necessarily the order in which the contingencies have - been entered. Please use `get_flows()` method for easier reading back of the results - - """ - v_init = self.grid2op_env.backend.V - print("self.computer.compute") - self.computer.compute(v_init, - self.grid2op_env.backend.max_it, - self.grid2op_env.backend.tol) - print("self.computer.get_voltages()") - self._vs = self.computer.get_voltages() - self.__computed = True - return self._vs - - def compute_A(self): - """ - This function returns the current flows (in Amps, A) at the origin / high voltage side - - .. warning:: Order of the results - - The order in which the results are returned is NOT necessarily the order in which the contingencies have - been entered. Please use `get_flows()` method for easier reading back of the results ! - - """ - if not self.__computed: - raise RuntimeError("This function can only be used if compute_V has been sucessfully called") - self._ampss = 1e3 * self.computer.compute_flows() - return self._ampss - - def compute_P(self): - """ - This function returns the active power flows (in MW) at the origin / high voltage side - - .. warning:: Order of the results - - The order in which the results are returned is NOT necessarily the order in which the contingencies have - been entered. Please use `get_flows()` method for easier reading back of the results ! - - """ - if not self.__computed: - raise RuntimeError("This function can only be used if compute_V has been sucessfully called") - self._mws = 1.0 * self.computer.compute_power_flows() - return self._mws - - def close(self): - """permanently close the object""" - self.grid2op_env.close() - self.clear() - self.computer.close() +# Deprecated now, will be removed +SecurityAnalysisCPP = ContingencyAnalysisCPP +SecurityAnalysis = ContingencyAnalysis diff --git a/lightsim2grid/tests/test_Computers.py b/lightsim2grid/tests/test_Computers.py index 15940a9..afb6d82 100644 --- a/lightsim2grid/tests/test_Computers.py +++ b/lightsim2grid/tests/test_Computers.py @@ -11,13 +11,11 @@ from grid2op.Parameters import Parameters import warnings import numpy as np -from numpy.core.shape_base import stack -import lightsim2grid -import lightsim2grid_cpp from lightsim2grid import LightSimBackend -from lightsim2grid_cpp import Computers +from lightsim2grid_cpp import TimeSeriesCPP -class TestComputers(unittest.TestCase): + +class TestTimeSeriesCPP(unittest.TestCase): def test_basic(self): # print(f"{lightsim2grid_cpp.__file__}") env_name = "l2rpn_case14_sandbox" @@ -37,7 +35,7 @@ def test_basic(self): load_q = 1.0 * env.chronics_handler.real_data.data.load_q # now perform the computation - computer = Computers(grid) + computer = TimeSeriesCPP(grid) # print("start the computation") status = computer.compute_Vs(prod_p, np.zeros((prod_p.shape[0], 0)), # no static generators for now ! @@ -83,7 +81,7 @@ def test_amps(self): load_q = 1.0 * env.chronics_handler.real_data.data.load_q # now perform the computation - computer = Computers(grid) + computer = TimeSeriesCPP(grid) # print("start the computation") status = computer.compute_Vs(prod_p, np.zeros((prod_p.shape[0], 0)), # no static generators for now ! diff --git a/lightsim2grid/tests/test_issue_56.py b/lightsim2grid/tests/test_issue_56.py index 7bddb14..412615b 100644 --- a/lightsim2grid/tests/test_issue_56.py +++ b/lightsim2grid/tests/test_issue_56.py @@ -15,7 +15,7 @@ import numpy as np import grid2op from lightsim2grid import LightSimBackend, SolverType -from lightsim2grid.securityAnalysis import SecurityAnalysis +from lightsim2grid.contingencyAnalysis import ContingencyAnalysis import pdb @@ -24,7 +24,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make("l2rpn_case14_sandbox", test=True, backend=LightSimBackend()) - self.sa = SecurityAnalysis(self.env) + self.sa = ContingencyAnalysis(self.env) def test_dc(self): self.sa.add_all_n1_contingencies() @@ -39,7 +39,6 @@ def test_dc(self): assert np.any(res_a != res_a_dc), "DC and AC solver leads to same results" assert np.any(res_v != res_v_dc), "DC and AC solver leads to same results" assert self.sa.computer.get_solver_type() == SolverType.DC - return nb_bus = self.env.n_sub nb_powerline = len(self.env.backend._grid.get_lines()) @@ -78,7 +77,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make("l2rpn_neurips_2020_track1", test=True, backend=LightSimBackend()) - self.sa = SecurityAnalysis(self.env) + self.sa = ContingencyAnalysis(self.env) class TestSADC_118(TestSADC_14): @@ -86,7 +85,7 @@ def setUp(self) -> None: with warnings.catch_warnings(): warnings.filterwarnings("ignore") self.env = grid2op.make("l2rpn_wcci_2022", test=True, backend=LightSimBackend()) - self.sa = SecurityAnalysis(self.env) + self.sa = ContingencyAnalysis(self.env) if __name__ == "__main__": diff --git a/lightsim2grid/tests/test_solver_control.py b/lightsim2grid/tests/test_solver_control.py index 4a1c413..ec9a897 100644 --- a/lightsim2grid/tests/test_solver_control.py +++ b/lightsim2grid/tests/test_solver_control.py @@ -30,6 +30,9 @@ from lightsim2grid import LightSimBackend from lightsim2grid.solver import SolverType +# TODO when line connected alone at one end, starts a Security Analysis +# to see if it works + class TestSolverControl(unittest.TestCase): def _aux_setup_grid(self): diff --git a/lightsim2grid/timeSerie.py b/lightsim2grid/timeSerie.py index 75bbaf1..2942441 100644 --- a/lightsim2grid/timeSerie.py +++ b/lightsim2grid/timeSerie.py @@ -6,7 +6,9 @@ # SPDX-License-Identifier: MPL-2.0 # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -__all__ = ["Computers", "TimeSerie"] +__all__ = ["TimeSerieCPP", "TimeSerie", + # deprecated + "Computers"] import numpy as np import warnings @@ -15,7 +17,10 @@ from lightsim2grid.lightSimBackend import LightSimBackend from lightsim2grid.solver import SolverType -from lightsim2grid_cpp import Computers +from lightsim2grid_cpp import TimeSeriesCPP + +# deprecated +Computers = TimeSeriesCPP class TimeSerie: @@ -79,7 +84,7 @@ def __init__(self, grid2op_env): raise RuntimeError("Please an environment of class \"Environment\", " "and not \"MultimixEnv\" or \"BaseMultiProcessEnv\"") self.grid2op_env = grid2op_env.copy() - self.computer = Computers(self.grid2op_env.backend._grid) + self.computer = TimeSeriesCPP(self.grid2op_env.backend._grid) self.prod_p = None self.load_p = None self.load_q = None diff --git a/setup.py b/setup.py index 8ddbbf5..88b3f05 100644 --- a/setup.py +++ b/setup.py @@ -145,12 +145,12 @@ "src/BaseConstants.cpp", "src/GridModel.cpp", "src/ChooseSolver.cpp", - "src/BaseMultiplePowerflow.cpp", - "src/Computers.cpp", - "src/SecurityAnalysis.cpp", "src/Solvers.cpp", "src/Utils.cpp", "src/DataConverter.cpp", + "src/batch_algorithm/BaseBatchSolverSynch.cpp", + "src/batch_algorithm/TimeSeries.cpp", + "src/batch_algorithm/ContingencyAnalysis.cpp", "src/element_container/LineContainer.cpp", "src/element_container/GenericContainer.cpp", "src/element_container/ShuntContainer.cpp", diff --git a/src/BaseMultiplePowerflow.cpp b/src/batch_algorithm/BaseBatchSolverSynch.cpp similarity index 93% rename from src/BaseMultiplePowerflow.cpp rename to src/batch_algorithm/BaseBatchSolverSynch.cpp index 3c90661..23cc935 100644 --- a/src/BaseMultiplePowerflow.cpp +++ b/src/batch_algorithm/BaseBatchSolverSynch.cpp @@ -6,12 +6,12 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "BaseMultiplePowerflow.h" +#include "BaseBatchSolverSynch.h" /** V is modified at each call ! **/ -bool BaseMultiplePowerflow::compute_one_powerflow(const Eigen::SparseMatrix & Ybus, +bool BaseBatchSolverSynch::compute_one_powerflow(const Eigen::SparseMatrix & Ybus, CplxVect & V, const CplxVect & Sbus, const Eigen::VectorXi & slack_ids, @@ -32,7 +32,7 @@ bool BaseMultiplePowerflow::compute_one_powerflow(const Eigen::SparseMatrix RealMat; typedef Eigen::Matrix CplxMat; - BaseMultiplePowerflow(const GridModel & init_grid_model): + BaseBatchSolverSynch(const GridModel & init_grid_model): _grid_model(init_grid_model), n_line_(init_grid_model.nb_powerline()), n_trafos_(init_grid_model.nb_trafo()), @@ -50,7 +52,7 @@ class BaseMultiplePowerflow _solver.tell_solver_control(_solver_control); } - BaseMultiplePowerflow(const BaseMultiplePowerflow&) = delete; + BaseBatchSolverSynch(const BaseBatchSolverSynch&) = delete; // solver "control" void change_solver(const SolverType & type){ @@ -242,4 +244,5 @@ class BaseMultiplePowerflow SolverControl _solver_control; }; + #endif // BASEMULTIPLEPOWERFLOW_H diff --git a/src/SecurityAnalysis.cpp b/src/batch_algorithm/ContingencyAnalysis.cpp similarity index 94% rename from src/SecurityAnalysis.cpp rename to src/batch_algorithm/ContingencyAnalysis.cpp index 17f9c3c..1118289 100644 --- a/src/SecurityAnalysis.cpp +++ b/src/batch_algorithm/ContingencyAnalysis.cpp @@ -6,12 +6,12 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "SecurityAnalysis.h" +#include "ContingencyAnalysis.h" #include #include /* isfinite */ -bool SecurityAnalysis::check_invertible(const Eigen::SparseMatrix & Ybus) const{ +bool ContingencyAnalysis::check_invertible(const Eigen::SparseMatrix & Ybus) const{ std::vector visited(Ybus.cols(), false); std::vector already_added(Ybus.cols(), false); std::queue neighborhood; @@ -44,7 +44,7 @@ bool SecurityAnalysis::check_invertible(const Eigen::SparseMatrix & Y return ok; } -void SecurityAnalysis::init_li_coeffs(bool ac_solver_used){ +void ContingencyAnalysis::init_li_coeffs(bool ac_solver_used){ _li_coeffs.clear(); _li_coeffs.reserve(_li_defaults.size()); const auto & powerlines = _grid_model.get_powerlines_as_data(); @@ -104,7 +104,7 @@ void SecurityAnalysis::init_li_coeffs(bool ac_solver_used){ } -bool SecurityAnalysis::remove_from_Ybus(Eigen::SparseMatrix & Ybus, +bool ContingencyAnalysis::remove_from_Ybus(Eigen::SparseMatrix & Ybus, const std::vector & coeffs) const { for(const auto & coeff_to_remove: coeffs){ @@ -112,7 +112,7 @@ bool SecurityAnalysis::remove_from_Ybus(Eigen::SparseMatrix & Ybus, } return check_invertible(Ybus); } -void SecurityAnalysis::readd_to_Ybus(Eigen::SparseMatrix & Ybus, +void ContingencyAnalysis::readd_to_Ybus(Eigen::SparseMatrix & Ybus, const std::vector & coeffs) const { for(const auto & coeff_to_remove: coeffs){ @@ -120,7 +120,7 @@ void SecurityAnalysis::readd_to_Ybus(Eigen::SparseMatrix & Ybus, } } -void SecurityAnalysis::compute(const CplxVect & Vinit, int max_iter, real_type tol) +void ContingencyAnalysis::compute(const CplxVect & Vinit, int max_iter, real_type tol) { auto timer = CustTimer(); auto timer_preproc = CustTimer(); @@ -160,7 +160,7 @@ void SecurityAnalysis::compute(const CplxVect & Vinit, int max_iter, real_type t Eigen::Index nb_steps = _li_defaults.size(); // init the results matrices - _voltages = BaseMultiplePowerflow::CplxMat::Zero(nb_steps, nb_total_bus); + _voltages = BaseBatchSolverSynch::CplxMat::Zero(nb_steps, nb_total_bus); _amps_flows = RealMat::Zero(0, n_total_); // reset the solver @@ -215,7 +215,7 @@ void SecurityAnalysis::compute(const CplxVect & Vinit, int max_iter, real_type t _timer_total = timer.duration(); } -void SecurityAnalysis::clean_flows(bool is_amps) +void ContingencyAnalysis::clean_flows(bool is_amps) { auto timer = CustTimer(); Eigen::Index cont_id = 0; diff --git a/src/SecurityAnalysis.h b/src/batch_algorithm/ContingencyAnalysis.h similarity index 89% rename from src/SecurityAnalysis.h rename to src/batch_algorithm/ContingencyAnalysis.h index ce7fe2d..052722e 100644 --- a/src/SecurityAnalysis.h +++ b/src/batch_algorithm/ContingencyAnalysis.h @@ -9,7 +9,7 @@ #ifndef SECURITYANALYSIS_H #define SECURITYANALYSIS_H -#include "BaseMultiplePowerflow.h" +#include "BaseBatchSolverSynch.h" #include struct Coeff{ @@ -19,20 +19,22 @@ struct Coeff{ }; /** -Class to perform a security analysis, which consist of performing some powerflow after some powerlines +Class to perform a contingency analysis (security analysis), which consist of performing some powerflow after some powerlines have been disconnected **/ -class SecurityAnalysis: public BaseMultiplePowerflow +class ContingencyAnalysis: public BaseBatchSolverSynch { public: - SecurityAnalysis(const GridModel & init_grid_model): - BaseMultiplePowerflow(init_grid_model), - _li_defaults(), - _li_coeffs(), - _timer_total(0.), - _timer_modif_Ybus(0.), - _timer_pre_proc(0.) - { } + ContingencyAnalysis(const GridModel & init_grid_model): + BaseBatchSolverSynch(init_grid_model), + _li_defaults(), + _li_coeffs(), + _timer_total(0.), + _timer_modif_Ybus(0.), + _timer_pre_proc(0.) + { } + + ContingencyAnalysis(const ContingencyAnalysis&) = delete; // utilities to add defaults to simulate void add_all_n1(){ @@ -65,7 +67,7 @@ class SecurityAnalysis: public BaseMultiplePowerflow // utilities to remove defaults to simulate (TODO) virtual void clear(){ - BaseMultiplePowerflow::clear(); + BaseBatchSolverSynch::clear(); _li_defaults.clear(); _li_coeffs.clear(); _timer_total = 0.; diff --git a/src/Computers.cpp b/src/batch_algorithm/TimeSeries.cpp similarity index 87% rename from src/Computers.cpp rename to src/batch_algorithm/TimeSeries.cpp index af8da7e..52b4edb 100644 --- a/src/Computers.cpp +++ b/src/batch_algorithm/TimeSeries.cpp @@ -6,23 +6,23 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -#include "Computers.h" +#include "TimeSeries.h" #include #include -int Computers::compute_Vs(Eigen::Ref gen_p, - Eigen::Ref sgen_p, - Eigen::Ref load_p, - Eigen::Ref load_q, - const CplxVect & Vinit, - const int max_iter, - const real_type tol) +int TimeSeries::compute_Vs(Eigen::Ref gen_p, + Eigen::Ref sgen_p, + Eigen::Ref load_p, + Eigen::Ref load_q, + const CplxVect & Vinit, + const int max_iter, + const real_type tol) { auto timer = CustTimer(); const Eigen::Index nb_total_bus = _grid_model.total_bus(); if(Vinit.size() != nb_total_bus){ std::ostringstream exc_; - exc_ << "Computers::compute_Sbuses: Size of the Vinit should be the same as the total number of buses. Currently: "; + exc_ << "TimeSeries::compute_Sbuses: Size of the Vinit should be the same as the total number of buses. Currently: "; exc_ << "Vinit: " << Vinit.size() << " and there are " << nb_total_bus << " buses."; exc_ << "(fyi: Components of Vinit corresponding to deactivated bus will be ignored anyway, so you can put whatever you want there)."; throw std::runtime_error(exc_.str()); @@ -72,7 +72,7 @@ int Computers::compute_Vs(Eigen::Ref gen_p, // TODO trafo hack for Sbus ! // init the results matrices - _voltages = BaseMultiplePowerflow::CplxMat::Zero(nb_steps, nb_total_bus); + _voltages = BaseBatchSolverSynch::CplxMat::Zero(nb_steps, nb_total_bus); _amps_flows = RealMat::Zero(0, n_total_); // extract V solver from the given V diff --git a/src/Computers.h b/src/batch_algorithm/TimeSeries.h similarity index 94% rename from src/Computers.h rename to src/batch_algorithm/TimeSeries.h index 091583e..6a20345 100644 --- a/src/Computers.h +++ b/src/batch_algorithm/TimeSeries.h @@ -9,17 +9,17 @@ #ifndef COMPUTERS_H #define COMPUTERS_H -#include "BaseMultiplePowerflow.h" +#include "BaseBatchSolverSynch.h" /** Allws the computation of time series, that is, the same grid topology is used along with time series of injections (productions and loads) to compute powerflows/ **/ -class Computers: public BaseMultiplePowerflow +class TimeSeries: public BaseBatchSolverSynch { public: - Computers(const GridModel & init_grid_model): - BaseMultiplePowerflow(init_grid_model), + TimeSeries(const GridModel & init_grid_model): + BaseBatchSolverSynch(init_grid_model), _Sbuses(), _status(1), // 1: success, 0: failure _compute_flows(true), @@ -27,7 +27,7 @@ class Computers: public BaseMultiplePowerflow _timer_pre_proc(0.) {} - Computers(const Computers&) = delete; + TimeSeries(const TimeSeries&) = delete; // control on whether I compute the flows or not void deactivate_flow_computations() {_compute_flows = false;} @@ -55,7 +55,7 @@ class Computers: public BaseMultiplePowerflow const real_type tol); virtual void clear(){ - BaseMultiplePowerflow::clear(); + BaseBatchSolverSynch::clear(); _Sbuses = CplxMat(); _status = 1; _compute_flows = true; diff --git a/src/main.cpp b/src/main.cpp index b798613..a1c9a6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,8 +13,9 @@ #include "ChooseSolver.h" #include "DataConverter.h" #include "GridModel.h" -#include "Computers.h" -#include "SecurityAnalysis.h" + +#include "batch_algorithm/TimeSeries.h" +#include "batch_algorithm/ContingencyAnalysis.h" #include "help_fun_msg.h" @@ -858,78 +859,78 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def("debug_get_Bpp_python", &GridModel::debug_get_Bpp_python, DocGridModel::_internal_do_not_use.c_str()) ; - py::class_(m, "Computers", DocComputers::Computers.c_str()) + py::class_(m, "TimeSeriesCPP", DocComputers::Computers.c_str()) .def(py::init()) // solver control - .def("change_solver", &Computers::change_solver, DocGridModel::change_solver.c_str()) - .def("available_solvers", &Computers::available_solvers, DocGridModel::available_solvers.c_str()) - .def("get_solver_type", &Computers::get_solver_type, DocGridModel::get_solver_type.c_str()) + .def("change_solver", &TimeSeries::change_solver, DocGridModel::change_solver.c_str()) + .def("available_solvers", &TimeSeries::available_solvers, DocGridModel::available_solvers.c_str()) + .def("get_solver_type", &TimeSeries::get_solver_type, DocGridModel::get_solver_type.c_str()) // timers - .def("total_time", &Computers::total_time, DocComputers::total_time.c_str()) - .def("solver_time", &Computers::solver_time, DocComputers::solver_time.c_str()) - .def("preprocessing_time", &Computers::preprocessing_time, DocComputers::preprocessing_time.c_str()) - .def("amps_computation_time", &Computers::amps_computation_time, DocComputers::amps_computation_time.c_str()) - .def("nb_solved", &Computers::nb_solved, DocComputers::nb_solved.c_str()) + .def("total_time", &TimeSeries::total_time, DocComputers::total_time.c_str()) + .def("solver_time", &TimeSeries::solver_time, DocComputers::solver_time.c_str()) + .def("preprocessing_time", &TimeSeries::preprocessing_time, DocComputers::preprocessing_time.c_str()) + .def("amps_computation_time", &TimeSeries::amps_computation_time, DocComputers::amps_computation_time.c_str()) + .def("nb_solved", &TimeSeries::nb_solved, DocComputers::nb_solved.c_str()) // status - .def("get_status", &Computers::get_status, DocComputers::get_status.c_str()) - .def("clear", &Computers::clear, DocComputers::clear.c_str()) - .def("close", &Computers::clear, DocComputers::clear.c_str()) + .def("get_status", &TimeSeries::get_status, DocComputers::get_status.c_str()) + .def("clear", &TimeSeries::clear, DocComputers::clear.c_str()) + .def("close", &TimeSeries::clear, DocComputers::clear.c_str()) // perform the computations - .def("compute_Vs", &Computers::compute_Vs, py::call_guard(), DocComputers::compute_Vs.c_str()) - .def("compute_flows", &Computers::compute_flows, py::call_guard(), DocComputers::compute_flows.c_str()) - .def("compute_power_flows", &Computers::compute_power_flows, DocComputers::compute_power_flows.c_str()) // need to be done after "compute_Vs" and "compute_flows" + .def("compute_Vs", &TimeSeries::compute_Vs, py::call_guard(), DocComputers::compute_Vs.c_str()) + .def("compute_flows", &TimeSeries::compute_flows, py::call_guard(), DocComputers::compute_flows.c_str()) + .def("compute_power_flows", &TimeSeries::compute_power_flows, DocComputers::compute_power_flows.c_str()) // need to be done after "compute_Vs" and "compute_flows" // results (for now only flow (at each -line origin- or voltages -at each buses) - .def("get_flows", &Computers::get_flows, DocComputers::get_flows.c_str()) // need to be done after "compute_Vs" and "compute_flows" - .def("get_power_flows", &Computers::get_power_flows, DocComputers::get_power_flows.c_str()) // need to be done after "compute_Vs" and "compute_flows" - .def("get_voltages", &Computers::get_voltages, DocComputers::get_voltages.c_str()) // need to be done after "compute_Vs" - .def("get_sbuses", &Computers::get_sbuses, DocComputers::get_sbuses.c_str()) // need to be done after "compute_Vs" + .def("get_flows", &TimeSeries::get_flows, DocComputers::get_flows.c_str()) // need to be done after "compute_Vs" and "compute_flows" + .def("get_power_flows", &TimeSeries::get_power_flows, DocComputers::get_power_flows.c_str()) // need to be done after "compute_Vs" and "compute_flows" + .def("get_voltages", &TimeSeries::get_voltages, DocComputers::get_voltages.c_str()) // need to be done after "compute_Vs" + .def("get_sbuses", &TimeSeries::get_sbuses, DocComputers::get_sbuses.c_str()) // need to be done after "compute_Vs" ; - py::class_(m, "SecurityAnalysisCPP", DocSecurityAnalysis::SecurityAnalysis.c_str()) + py::class_(m, "ContingencyAnalysisCPP", DocSecurityAnalysis::SecurityAnalysis.c_str()) .def(py::init()) // solver control - .def("change_solver", &Computers::change_solver, DocGridModel::change_solver.c_str()) - .def("available_solvers", &Computers::available_solvers, DocGridModel::available_solvers.c_str()) - .def("get_solver_type", &Computers::get_solver_type, DocGridModel::get_solver_type.c_str()) + .def("change_solver", &ContingencyAnalysis::change_solver, DocGridModel::change_solver.c_str()) + .def("available_solvers", &ContingencyAnalysis::available_solvers, DocGridModel::available_solvers.c_str()) + .def("get_solver_type", &ContingencyAnalysis::get_solver_type, DocGridModel::get_solver_type.c_str()) // add some defaults - .def("add_all_n1", &SecurityAnalysis::add_all_n1, DocSecurityAnalysis::add_all_n1.c_str()) - .def("add_n1", &SecurityAnalysis::add_n1, DocSecurityAnalysis::add_n1.c_str()) - .def("add_nk", &SecurityAnalysis::add_nk, DocSecurityAnalysis::add_nk.c_str()) - .def("add_multiple_n1", &SecurityAnalysis::add_multiple_n1, DocSecurityAnalysis::add_multiple_n1.c_str()) + .def("add_all_n1", &ContingencyAnalysis::add_all_n1, DocSecurityAnalysis::add_all_n1.c_str()) + .def("add_n1", &ContingencyAnalysis::add_n1, DocSecurityAnalysis::add_n1.c_str()) + .def("add_nk", &ContingencyAnalysis::add_nk, DocSecurityAnalysis::add_nk.c_str()) + .def("add_multiple_n1", &ContingencyAnalysis::add_multiple_n1, DocSecurityAnalysis::add_multiple_n1.c_str()) // remove some defaults (TODO) - .def("reset", &SecurityAnalysis::clear, DocSecurityAnalysis::clear.c_str()) - .def("clear", &SecurityAnalysis::clear, DocSecurityAnalysis::clear.c_str()) - .def("close", &SecurityAnalysis::clear, DocComputers::clear.c_str()) - .def("remove_n1", &SecurityAnalysis::remove_n1, DocSecurityAnalysis::remove_n1.c_str()) - .def("remove_nk", &SecurityAnalysis::remove_nk, DocSecurityAnalysis::remove_nk.c_str()) - .def("remove_multiple_n1", &SecurityAnalysis::remove_multiple_n1, DocSecurityAnalysis::remove_multiple_n1.c_str()) + .def("reset", &ContingencyAnalysis::clear, DocSecurityAnalysis::clear.c_str()) + .def("clear", &ContingencyAnalysis::clear, DocSecurityAnalysis::clear.c_str()) + .def("close", &ContingencyAnalysis::clear, DocComputers::clear.c_str()) + .def("remove_n1", &ContingencyAnalysis::remove_n1, DocSecurityAnalysis::remove_n1.c_str()) + .def("remove_nk", &ContingencyAnalysis::remove_nk, DocSecurityAnalysis::remove_nk.c_str()) + .def("remove_multiple_n1", &ContingencyAnalysis::remove_multiple_n1, DocSecurityAnalysis::remove_multiple_n1.c_str()) // inspect the class - .def("my_defaults", &SecurityAnalysis::my_defaults_vect, DocSecurityAnalysis::my_defaults_vect.c_str()) + .def("my_defaults", &ContingencyAnalysis::my_defaults_vect, DocSecurityAnalysis::my_defaults_vect.c_str()) // perform the computation - .def("compute", &SecurityAnalysis::compute, py::call_guard(), DocSecurityAnalysis::compute.c_str()) - .def("compute_flows", &SecurityAnalysis::compute_flows, py::call_guard(), DocSecurityAnalysis::compute_flows.c_str()) - .def("compute_power_flows", &SecurityAnalysis::compute_power_flows, DocSecurityAnalysis::compute_power_flows.c_str()) + .def("compute", &ContingencyAnalysis::compute, py::call_guard(), DocSecurityAnalysis::compute.c_str()) + .def("compute_flows", &ContingencyAnalysis::compute_flows, py::call_guard(), DocSecurityAnalysis::compute_flows.c_str()) + .def("compute_power_flows", &ContingencyAnalysis::compute_power_flows, DocSecurityAnalysis::compute_power_flows.c_str()) // results (for now only flow (at each -line origin- or voltages -at each buses) - .def("get_flows", &SecurityAnalysis::get_flows, DocSecurityAnalysis::get_flows.c_str()) - .def("get_voltages", &SecurityAnalysis::get_voltages, DocSecurityAnalysis::get_voltages.c_str()) - .def("get_power_flows", &SecurityAnalysis::get_power_flows, DocSecurityAnalysis::get_power_flows.c_str()) + .def("get_flows", &ContingencyAnalysis::get_flows, DocSecurityAnalysis::get_flows.c_str()) + .def("get_voltages", &ContingencyAnalysis::get_voltages, DocSecurityAnalysis::get_voltages.c_str()) + .def("get_power_flows", &ContingencyAnalysis::get_power_flows, DocSecurityAnalysis::get_power_flows.c_str()) // timers - .def("total_time", &SecurityAnalysis::total_time, DocComputers::total_time.c_str()) - .def("solver_time", &SecurityAnalysis::solver_time, DocComputers::solver_time.c_str()) - .def("preprocessing_time", &SecurityAnalysis::preprocessing_time, DocSecurityAnalysis::preprocessing_time.c_str()) - .def("amps_computation_time", &SecurityAnalysis::amps_computation_time, DocComputers::amps_computation_time.c_str()) - .def("modif_Ybus_time", &SecurityAnalysis::modif_Ybus_time, DocSecurityAnalysis::modif_Ybus_time.c_str()) - .def("nb_solved", &SecurityAnalysis::nb_solved, DocComputers::nb_solved.c_str()) + .def("total_time", &ContingencyAnalysis::total_time, DocComputers::total_time.c_str()) + .def("solver_time", &ContingencyAnalysis::solver_time, DocComputers::solver_time.c_str()) + .def("preprocessing_time", &ContingencyAnalysis::preprocessing_time, DocSecurityAnalysis::preprocessing_time.c_str()) + .def("amps_computation_time", &ContingencyAnalysis::amps_computation_time, DocComputers::amps_computation_time.c_str()) + .def("modif_Ybus_time", &ContingencyAnalysis::modif_Ybus_time, DocSecurityAnalysis::modif_Ybus_time.c_str()) + .def("nb_solved", &ContingencyAnalysis::nb_solved, DocComputers::nb_solved.c_str()) ; } From b5646ddb3bc156d47f4da759f8a31c1280b73898 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Wed, 10 Jan 2024 14:13:35 +0100 Subject: [PATCH 16/42] fixing broken imports after renaming --- lightsim2grid/tests/test_SecurityAnlysis.py | 30 +++++++++---------- .../tests/test_SecurityAnlysis_cpp.py | 24 +++++++-------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lightsim2grid/tests/test_SecurityAnlysis.py b/lightsim2grid/tests/test_SecurityAnlysis.py index 4927f6f..1ab67da 100644 --- a/lightsim2grid/tests/test_SecurityAnlysis.py +++ b/lightsim2grid/tests/test_SecurityAnlysis.py @@ -11,7 +11,7 @@ import numpy as np import grid2op -from lightsim2grid import SecurityAnalysis +from lightsim2grid import ContingencyAnalysis from lightsim2grid import LightSimBackend import warnings import pdb @@ -28,10 +28,10 @@ def tearDown(self) -> None: return super().tearDown() def test_can_create(self): - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) def test_clear(self): - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) # add simple contingencies sa.add_multiple_contingencies(0, 1, 2, 3) @@ -46,7 +46,7 @@ def test_clear(self): assert len(sa._contingency_order) == 0 def test_add_single_contingency(self): - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) with self.assertRaises(RuntimeError): sa.add_single_contingency("toto") with self.assertRaises(RuntimeError): @@ -64,7 +64,7 @@ def test_add_single_contingency(self): assert len(sa._contingency_order) == 4 def test_add_multiple_contingencies(self): - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) # add simple contingencies sa.add_multiple_contingencies(0, 1, 2, 3) all_conts = sa.computer.my_defaults() @@ -91,7 +91,7 @@ def test_add_multiple_contingencies(self): assert len(sa._contingency_order) == 4 def test_add_all_n1_contingencies(self): - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) sa.add_all_n1_contingencies() all_conts = sa.computer.my_defaults() assert len(all_conts) == self.env.n_line @@ -100,7 +100,7 @@ def test_add_all_n1_contingencies(self): def test_get_flows_simple(self): """test the get_flows method in the most simplest way: ask for all contingencies, contingencies are given in the right order""" - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) sa.add_multiple_contingencies(0, 1, 2) res_p, res_a, res_v = sa.get_flows() assert res_a.shape == (3, self.env.n_line) @@ -111,7 +111,7 @@ def test_get_flows_simple(self): def test_get_flows_1(self): """test the get_flows method: ask for all contingencies , contingencies are NOT given in the right order""" - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) sa.add_multiple_contingencies(0, 2, 1) res_p, res_a, res_v = sa.get_flows() assert res_a.shape == (3, self.env.n_line) @@ -122,7 +122,7 @@ def test_get_flows_1(self): def test_get_flows_2(self): """test the get_flows method: don't ask for all contingencies (same order as given), contingencies are given in the right order""" - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) sa.add_multiple_contingencies(0, 1, 2) res_p, res_a, res_v = sa.get_flows(0, 1) assert res_a.shape == (2, self.env.n_line) @@ -132,7 +132,7 @@ def test_get_flows_2(self): def test_get_flows_3(self): """test the get_flows method in the most simplest way: not all contingencies (not same order as given), contingencies are given in the right order""" - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) sa.add_multiple_contingencies(0, 1, 2) res_p, res_a, res_v = sa.get_flows(0, 2) assert res_a.shape == (2, self.env.n_line) @@ -142,7 +142,7 @@ def test_get_flows_3(self): def test_get_flows_4(self): """test the get_flows method: don't ask for all contingencies (same order as given), contingencies are NOT given in the right order""" - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) sa.add_multiple_contingencies(0, 2, 1) res_p, res_a, res_v = sa.get_flows(0, 2) assert res_a.shape == (2, self.env.n_line) @@ -152,7 +152,7 @@ def test_get_flows_4(self): def test_get_flows_5(self): """test the get_flows method in the most simplest way: not all contingencies (not same order as given), contingencies are NOT given in the right order""" - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) sa.add_multiple_contingencies(0, 2, 1) res_p, res_a, res_v = sa.get_flows(0, 1) assert res_a.shape == (2, self.env.n_line) @@ -161,7 +161,7 @@ def test_get_flows_5(self): def test_get_flows_multiple(self): """test the get_flows function when multiple contingencies""" - sa = SecurityAnalysis(self.env) + sa = ContingencyAnalysis(self.env) sa.add_multiple_contingencies(0, [0, 4], [5, 7], 4) # everything @@ -197,11 +197,11 @@ def test_get_flows_multiple(self): def test_change_injection(self): """test the capacity of the things to handle different steps""" - sa1 = SecurityAnalysis(self.env) + sa1 = ContingencyAnalysis(self.env) conts = [0, [0, 4], [5, 7], 4] sa1.add_multiple_contingencies(*conts) self.env.reset() - sa2 = SecurityAnalysis(self.env) + sa2 = ContingencyAnalysis(self.env) sa2.add_multiple_contingencies(*conts) res_p1, res_a1, res_v1 = sa1.get_flows() diff --git a/lightsim2grid/tests/test_SecurityAnlysis_cpp.py b/lightsim2grid/tests/test_SecurityAnlysis_cpp.py index bbf73ae..15de108 100644 --- a/lightsim2grid/tests/test_SecurityAnlysis_cpp.py +++ b/lightsim2grid/tests/test_SecurityAnlysis_cpp.py @@ -10,7 +10,7 @@ import numpy as np import grid2op -from lightsim2grid_cpp import SecurityAnalysisCPP +from lightsim2grid_cpp import ContingencyAnalysisCPP from lightsim2grid import LightSimBackend import warnings import pdb @@ -29,11 +29,11 @@ def tearDown(self) -> None: return super().tearDown() def test_can_create(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) assert len(SA.my_defaults()) == 0 def test_add_n1(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) SA.add_n1(0) all_def = SA.my_defaults() assert len(all_def) == 1 @@ -64,7 +64,7 @@ def test_add_n1(self): SA.add_n1(20) def test_add_multiple_n1(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) SA.add_multiple_n1([0]) all_def = SA.my_defaults() assert len(all_def) == 1 @@ -99,7 +99,7 @@ def test_add_multiple_n1(self): SA.add_multiple_n1([20]) def test_add_nk(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) SA.add_nk([0]) all_def = SA.my_defaults() assert len(all_def) == 1 @@ -132,13 +132,13 @@ def test_add_nk(self): SA.add_nk([20]) def test_add_all_n1(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) SA.add_all_n1() all_def = SA.my_defaults() assert len(all_def) == self.env.n_line def test_remove_n1(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) SA.add_all_n1() assert SA.remove_n1(0) # this should remove it and return true (because the removing is a success) all_def = SA.my_defaults() @@ -148,7 +148,7 @@ def test_remove_n1(self): assert len(all_def) == self.env.n_line - 1 def test_clear(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) SA.add_all_n1() all_def = SA.my_defaults() assert len(all_def) == self.env.n_line @@ -157,7 +157,7 @@ def test_clear(self): assert len(all_def) == 0 def test_remove_multiple_n1(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) SA.add_all_n1() nb_removed = SA.remove_multiple_n1([0, 1, 2]) assert nb_removed == 3 @@ -169,7 +169,7 @@ def test_remove_multiple_n1(self): assert len(all_def) == self.env.n_line - 5 def test_remove_nk(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) SA.add_nk([0, 1]) SA.add_nk([0, 2]) SA.add_nk([0, 3]) @@ -188,7 +188,7 @@ def test_remove_nk(self): assert len(all_def) == 2 def test_compute(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) lid_cont = [0, 1, 2, 3] nb_sub = self.env.n_sub SA.add_multiple_n1(lid_cont) @@ -207,7 +207,7 @@ def test_compute(self): assert np.max(np.abs(res_flows[cont_id] - sim_obs.a_or*1e-3)) <= 1e-6, f"error in flows when disconnecting line {l_id} (contingency nb {cont_id})" def test_compute_nonconnected_graph(self): - SA = SecurityAnalysisCPP(self.env.backend._grid) + SA = ContingencyAnalysisCPP(self.env.backend._grid) lid_cont = [17, 18, 19] # 17 is ok, 18 lead to divergence, i need to check then that 19 is correct (no divergence) nb_sub = self.env.n_sub SA.add_multiple_n1(lid_cont) From 3ed4bad4aa4c126f5c42c964534b6d10cc078952 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 7 Mar 2024 11:32:50 +0100 Subject: [PATCH 17/42] adding support for more than 2 busbars per substation --- CHANGELOG.rst | 5 +- benchmarks/benchmark_grid_size.py | 51 ++- lightsim2grid/lightSimBackend.py | 134 ++++---- lightsim2grid/tests/test_n_busbar_per_sub.py | 307 +++++++++++++++++++ lightsim2grid/tests/test_solver_control.py | 1 - src/GridModel.cpp | 9 +- src/GridModel.h | 16 +- src/main.cpp | 6 + 8 files changed, 463 insertions(+), 66 deletions(-) create mode 100644 lightsim2grid/tests/test_n_busbar_per_sub.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d5c04d2..666a2a4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -21,7 +21,7 @@ Change Log [0.7.6] 2023-xx-yy -------------------- - [BREAKING] now able to retrieve `dcSbus` with a dedicated method (and not with the old `get_Sbus`). - If you previously used `gridmodel.get_Subus()` to retrieve the Sbus used for DC powerflow, please use + If you previously used `gridmodel.get_Sbus()` to retrieve the Sbus used for DC powerflow, please use `gridmodel.get_dcSbus()` instead. - [DEPRECATED] in the cpp class: the old `SecurityAnalysisCPP` has been renamed `ContingencyAnalysisCPP` (you should not import it, but it you do you can `from lightsim2grid.securityAnalysis import ContingencyAnalysisCPP` now) @@ -45,6 +45,9 @@ Change Log - [ADDED] embed in the generator models the "non pv" behaviour. (TODO need to be able to change Q from python side) - [ADDED] computation of PTPF (Power Transfer Distribution Factor) is now possible - [ADDED] (not tested) support for more than 2 busbars per substation +- [ADDED] a timer to get the time spent in the gridmodel for the powerflow (env.backend.timer_gridmodel_xx_pf) + which also include the time +- [ADDED] support for more than 2 busbars per substation (requires grid2op >= 1.10.0) - [IMPROVED] now performing the new grid2op `create_test_suite` - [IMPROVED] now lightsim2grid properly throw `BackendError` - [IMPROVED] clean ce cpp side by refactoring: making clearer the difference (linear) solver diff --git a/benchmarks/benchmark_grid_size.py b/benchmarks/benchmark_grid_size.py index b7a15ad..09d627e 100644 --- a/benchmarks/benchmark_grid_size.py +++ b/benchmarks/benchmark_grid_size.py @@ -13,7 +13,12 @@ import matplotlib.pyplot as plt from grid2op import make, Parameters from grid2op.Chronics import FromNPY -from lightsim2grid import LightSimBackend, TimeSerie, SecurityAnalysis +from lightsim2grid import LightSimBackend, TimeSerie +try: + from lightsim2grid import ContingencyAnalysis +except ImportError: + from lightsim2grid import SecurityAnalysis as ContingencyAnalysis + from tqdm import tqdm import os from utils_benchmark import print_configuration, get_env_name_displayed @@ -157,6 +162,8 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): g2op_speeds = [] g2op_sizes = [] g2op_step_time = [] + ls_solver_time = [] + ls_gridmodel_time = [] ts_times = [] ts_speeds = [] @@ -227,13 +234,18 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): g2op_times.append(None) g2op_speeds.append(None) g2op_step_time.append(None) + ls_solver_time.append(None) + ls_gridmodel_time.append(None) g2op_sizes.append(env_lightsim.n_sub) else: total_time = env_lightsim.backend._timer_preproc + env_lightsim.backend._timer_solver # + env_lightsim.backend._timer_postproc + total_time = env_lightsim._time_step g2op_times.append(total_time) g2op_speeds.append(1.0 * nb_step / total_time) g2op_step_time.append(1.0 * env_lightsim._time_step / nb_step) - g2op_sizes.append(env_lightsim.n_sub) + ls_solver_time.append(env_lightsim.backend.comp_time) + ls_gridmodel_time.append(env_lightsim.backend.timer_gridmodel_xx_pf) + g2op_sizes.append(env_lightsim.n_sub) # Perform the computation using TimeSerie env_lightsim.reset() @@ -274,7 +286,7 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): # Perform a securtiy analysis (up to 1000 contingencies) env_lightsim.reset() - sa = SecurityAnalysis(env_lightsim) + sa = ContingencyAnalysis(env_lightsim) for i in range(env_lightsim.n_line): sa.add_single_contingency(i) if i >= 1000: @@ -297,11 +309,24 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): print("Results using grid2op.steps (288 consecutive steps, only measuring 'dc pf [init] + ac pf')") tab_g2op = [] for i, nm_ in enumerate(case_names_displayed): - tab_g2op.append((nm_, ts_sizes[i], 1000. / g2op_speeds[i] if g2op_speeds[i] else None, g2op_speeds[i], - 1000. * g2op_step_time[i] if g2op_step_time[i] else None)) + tab_g2op.append((nm_, + ts_sizes[i], + 1000. / g2op_speeds[i] if g2op_speeds[i] else None, + g2op_speeds[i], + 1000. * g2op_step_time[i] if g2op_step_time[i] else None, + 1000. * ls_gridmodel_time[i] / nb_step if ls_gridmodel_time[i] else None, + 1000. * ls_solver_time[i] / nb_step if ls_solver_time[i] else None, + )) if TABULATE_AVAIL: res_use_with_grid2op_2 = tabulate(tab_g2op, - headers=["grid", "size (nb bus)", "time (ms / pf)", "speed (pf / s)", "avg step duration (ms)"], + headers=["grid", + "size (nb bus)", + "step time (ms / pf)", + "speed (pf / s)", + "avg step duration (ms)", + "time in 'gridmodel' (ms / pf)", + "time in 'pf algo' (ms / pf)", + ], tablefmt="rst") print(res_use_with_grid2op_2) else: @@ -349,6 +374,20 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): plt.title(f"Computation speed using Grid2Op.step (dc pf [init] + ac pf)") plt.yscale("log") plt.show() + + plt.plot(g2op_sizes, ls_solver_time, linestyle='solid', marker='+', markersize=8) + plt.xlabel("Size (number of substation)") + plt.ylabel("Speed (solver time)") + plt.title(f"Computation speed for solving the powerflow only") + plt.yscale("log") + plt.show() + + plt.plot(g2op_sizes, ls_gridmodel_time, linestyle='solid', marker='+', markersize=8) + plt.xlabel("Size (number of substation)") + plt.ylabel("Speed (solver time)") + plt.title(f"Computation speed for solving the powerflow only") + plt.yscale("log") + plt.show() # make the plot summarizing all results plt.plot(ts_sizes, ts_times, linestyle='solid', marker='+', markersize=8) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 81732db..bbecd58 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -168,7 +168,17 @@ def __init__(self, # available solver in lightsim self.available_solvers = [] - self.comp_time = 0. # computation time of just the powerflow + + # computation time of just the powerflow (when the grid is formatted + # by the gridmodel already) + # it takes only into account the time spend in the powerflow algorithm + self.comp_time = 0. + + # computation time of the powerflow + # it takes into account everything in the gridmodel, including the mapping + # to the solver, building of Ybus and Sbus AND the time to solve the powerflow + self.timer_gridmodel_xx_pf = 0. + self._timer_postproc = 0. self._timer_preproc = 0. self._timer_solver = 0. @@ -195,22 +205,6 @@ def __init__(self, # add the static gen to the list of controlable gen in grid2Op self._use_static_gen = use_static_gen # TODO implement it - - # storage data for this object (otherwise it's in the class) - # self.n_storage = None - # self.storage_to_subid = None - # self.storage_pu_to_kv = None - # self.name_storage = None - # self.storage_to_sub_pos = None - # self.storage_type = None - # self.storage_Emin = None - # self.storage_Emax = None - # self.storage_max_p_prod = None - # self.storage_max_p_absorb = None - # self.storage_marginal_cost = None - # self.storage_loss = None - # self.storage_discharging_efficiency = None - # self.storage_charging_efficiency = None def turnedoff_no_pv(self): self._turned_off_pv = False @@ -411,6 +405,10 @@ def _assign_right_solver(self): self._grid.change_solver(SolverType.SparseLU) def load_grid(self, path=None, filename=None): + if hasattr(type(self), "can_handle_more_than_2_busbar"): + # grid2op version >= 1.10.0 then we use this + self.can_handle_more_than_2_busbar() + if self._loader_method == "pandapower": self._load_grid_pandapower(path, filename) elif self._loader_method == "pypowsybl": @@ -418,6 +416,42 @@ def load_grid(self, path=None, filename=None): else: raise BackendError(f"Impossible to initialize the backend with '{self._loader_method}'") + def _should_not_have_to_do_this(self, path=None, filename=None): + # included in grid2op now ! + # but before `make_complete_path` was introduced we need to still + # be able to use lightsim2grid + import os + from grid2op.Exceptions import Grid2OpException + if path is None and filename is None: + raise Grid2OpException( + "You must provide at least one of path or file to load a powergrid." + ) + if path is None: + full_path = filename + elif filename is None: + full_path = path + else: + full_path = os.path.join(path, filename) + if not os.path.exists(full_path): + raise Grid2OpException('There is no powergrid at "{}"'.format(full_path)) + return full_path + + def _aux_pypowsybl_init_substations(self, loader_kwargs): + # now handle the legacy "make as if there are 2 busbars per substation" + # as it is done with grid2Op simulated environment + if (("double_bus_per_sub" in loader_kwargs and loader_kwargs["double_bus_per_sub"]) or + ("n_busbar_per_sub" in loader_kwargs and loader_kwargs["n_busbar_per_sub"])): + bus_init = self._grid.get_bus_vn_kv() + orig_to_ls = np.array(self._grid._orig_to_ls) + bus_doubled = np.concatenate([bus_init for _ in range(self.n_busbar_per_sub)]) + 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 + i * self.__nb_bus_before + for i in range(self.n_busbar_per_sub)] + ) + self._grid._orig_to_ls = new_orig_to_ls + def _load_grid_pypowsybl(self, path=None, filename=None): from lightsim2grid.gridmodel.from_pypowsybl import init as init_pypow import pypowsybl.network as pypow_net @@ -429,28 +463,15 @@ def _load_grid_pypowsybl(self, path=None, filename=None): full_path = self.make_complete_path(path, filename) except AttributeError as exc_: warnings.warn("Please upgrade your grid2op version") - import os - from grid2op.Exceptions import Grid2OpException - def make_complete_path(path, filename): - if path is None and filename is None: - raise Grid2OpException( - "You must provide at least one of path or file to load a powergrid." - ) - if path is None: - full_path = filename - elif filename is None: - full_path = path - else: - full_path = os.path.join(path, filename) - if not os.path.exists(full_path): - raise Grid2OpException('There is no powergrid at "{}"'.format(full_path)) - return full_path - full_path = make_complete_path(path, filename) + full_path = self._should_not_have_to_do_this(path, filename) + grid_tmp = pypow_net.load(full_path) 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=gen_slack_id, sort_index=True) + self.__nb_bus_before = len(self._grid.get_bus_vn_kv()) + self._aux_pypowsybl_init_substations(loader_kwargs) self._aux_setup_right_after_grid_init() # mandatory for the backend @@ -482,7 +503,11 @@ def make_complete_path(path, filename): self.shunt_to_subid = np.array([el.bus_id for el in self._grid.get_shunts()], dtype=dt_int) else: # TODO get back the sub id from the grid_tmp.get_substations() - raise NotImplementedError("TODO") + raise NotImplementedError("Today the only supported behaviour is to consider the 'buses' of the powsybl grid " + "are the 'substation' of this backend. " + "This will change in the future, but in the meantime please add " + "'use_buses_for_sub' = True in the `loader_kwargs` when loading " + "a lightsim2grid grid.") # the names self.name_load = np.array([f"load_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_loads())]) @@ -496,25 +521,10 @@ def make_complete_path(path, filename): self._compute_pos_big_topo() self.__nb_powerline = len(self._grid.get_lines()) - self.__nb_bus_before = len(self._grid.get_bus_vn_kv()) # init this self.prod_p = np.array([el.target_p_mw for el in self._grid.get_generators()], dtype=dt_float) self.next_prod_p = np.array([el.target_p_mw for el in self._grid.get_generators()], dtype=dt_float) - - # now handle the legacy "make as if there are 2 busbars per substation" - # 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_bus_vn_kv() - 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, - orig_to_ls + self.__nb_bus_before) - ) - self._grid._orig_to_ls = new_orig_to_ls self.nb_bus_total = len(self._grid.get_bus_vn_kv()) # and now things needed by the backend (legacy) @@ -531,6 +541,7 @@ def make_complete_path(path, filename): self._aux_finish_setup_after_reading() def _aux_setup_right_after_grid_init(self): + self._grid.set_n_sub(self.__nb_bus_before) self._handle_turnedoff_pv() self.available_solvers = self._grid.available_solvers() @@ -549,12 +560,19 @@ def _aux_setup_right_after_grid_init(self): # check that the solver type provided is installed with lightsim2grid self._check_suitable_solver_type(self.__current_solver_type) self._grid.change_solver(self.__current_solver_type) + + # handle multiple busbar per substations + if hasattr(type(self), "can_handle_more_than_2_busbar"): + # grid2op version >= 1.10.0 then we use this + self._grid._max_nb_bus_per_sub = self.n_busbar_per_sub def _load_grid_pandapower(self, path=None, filename=None): + type(self.init_pp_backend).n_busbar_per_sub = self.n_busbar_per_sub self.init_pp_backend.load_grid(path, filename) self.can_output_theta = True # i can compute the "theta" and output it to grid2op - self._grid = init(self.init_pp_backend._grid) + self._grid = init(self.init_pp_backend._grid) + self.__nb_bus_before = self.init_pp_backend.get_nb_active_bus() self._aux_setup_right_after_grid_init() self.n_line = self.init_pp_backend.n_line @@ -653,7 +671,6 @@ def _aux_finish_setup_after_reading(self): # set up the "lightsim grid" accordingly cls = type(self) - self._grid.set_n_sub(self.__nb_bus_before) self._grid.set_load_pos_topo_vect(cls.load_pos_topo_vect) self._grid.set_gen_pos_topo_vect(cls.gen_pos_topo_vect) self._grid.set_line_or_pos_topo_vect(cls.line_or_pos_topo_vect[:self.__nb_powerline]) @@ -913,8 +930,18 @@ def runpf(self, is_dc=False): beg_postroc = time.perf_counter() if is_dc: self.comp_time += self._grid.get_dc_computation_time() + self.timer_gridmodel_xx_pf += self._grid.timer_last_dc_pf else: self.comp_time += self._grid.get_computation_time() + # NB get_computation_time returns "time_total_nr", which is + # defined in the powerflow algorithm and not on the linear solver. + # it takes into account everything needed to solve the powerflow + # once everything is passed to the solver. + # It does not take into account the time to format the data in the + # from the GridModel + + self.timer_gridmodel_xx_pf += self._grid.timer_last_ac_pf + # timer_gridmodel_xx_pf takes all the time within the gridmodel "ac_pf" self.V[:] = V (self.p_or[:self.__nb_powerline], @@ -1046,6 +1073,8 @@ def copy(self): #################### # res = copy.deepcopy(self) # super slow res = type(self).__new__(type(self)) + res.comp_time = self.comp_time + res.timer_gridmodel_xx_pf = self.timer_gridmodel_xx_pf # copy the regular attribute res.__has_storage = self.__has_storage @@ -1197,6 +1226,7 @@ def reset(self, grid_path, grid_filename=None): self._handle_turnedoff_pv() self.topo_vect[:] = self.__init_topo_vect self.comp_time = 0. + self.timer_gridmodel_xx_pf = 0. self._timer_postproc = 0. self._timer_preproc = 0. self._timer_solver = 0. diff --git a/lightsim2grid/tests/test_n_busbar_per_sub.py b/lightsim2grid/tests/test_n_busbar_per_sub.py new file mode 100644 index 0000000..aa51bb0 --- /dev/null +++ b/lightsim2grid/tests/test_n_busbar_per_sub.py @@ -0,0 +1,307 @@ +# Copyright (c) 2020, RTE (https://www.rte-france.com) +# See AUTHORS.txt +# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. +# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, +# you can obtain one at http://mozilla.org/MPL/2.0/. +# SPDX-License-Identifier: MPL-2.0 +# This file is part of LightSim2grid, LightSim2grid a implements a c++ backend targeting the Grid2Op platform. +import unittest +import warnings +import numpy as np + +import grid2op +from grid2op.Action import CompleteAction + +from lightsim2grid import LightSimBackend + +class TestLightSimBackend_3busbars(unittest.TestCase): + def get_nb_bus(self): + return 3 + + def get_env_nm(self): + return "educ_case14_storage" + + def get_backend_kwargs(self): + return dict() + + def setUp(self) -> None: + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + self.env = grid2op.make(self.get_env_nm(), + backend=LightSimBackend(**self.get_backend_kwargs()), + action_class=CompleteAction, + test=True, + n_busbar=self.get_nb_bus(), + _add_to_name=type(self).__name__ + f'_{self.get_nb_bus()}') + self.list_loc_bus = [-1] + list(range(1, type(self.env).n_busbar_per_sub + 1)) + return super().setUp() + + def tearDown(self) -> None: + self.env.close() + return super().tearDown() + + def test_right_bus_made(self): + assert len(self.env.backend._grid.get_bus_vn_kv()) == self.get_nb_bus() * type(self.env).n_sub + assert (~np.array(self.env.backend._grid.get_bus_status())[type(self.env).n_sub:]).all() + assert (np.array(self.env.backend._grid.get_bus_status())[:type(self.env).n_sub]).all() + + @staticmethod + def _aux_find_sub(env, obj_col): + """find a sub with 4 elements, the type of elements and at least 2 lines""" + cls = type(env) + res = None + for sub_id in range(cls.n_sub): + this_sub_mask = cls.grid_objects_types[:,cls.SUB_COL] == sub_id + this_sub = cls.grid_objects_types[this_sub_mask, :] + if this_sub.shape[0] <= 3: + # not enough element + continue + if (this_sub[:, obj_col] == -1).all(): + # no load + continue + if ((this_sub[:, cls.LOR_COL] != -1) | (this_sub[:, cls.LEX_COL] != -1)).sum() <= 1: + # only 1 line + continue + el_id = this_sub[this_sub[:, obj_col] != -1, obj_col][0] + if (this_sub[:, cls.LOR_COL] != -1).any(): + line_or_id = this_sub[this_sub[:, cls.LOR_COL] != -1, cls.LOR_COL][0] + line_ex_id = None + else: + line_or_id = None + line_ex_id = this_sub[this_sub[:, cls.LEX_COL] != -1, cls.LEX_COL][0] + res = (sub_id, el_id, line_or_id, line_ex_id) + break + return res + + @staticmethod + def _aux_find_sub_shunt(env): + """find a sub with 4 elements, the type of elements and at least 2 lines""" + cls = type(env) + res = None + for el_id in range(cls.n_shunt): + sub_id = cls.shunt_to_subid[el_id] + this_sub_mask = cls.grid_objects_types[:,cls.SUB_COL] == sub_id + this_sub = cls.grid_objects_types[this_sub_mask, :] + if this_sub.shape[0] <= 3: + # not enough element + continue + if ((this_sub[:, cls.LOR_COL] != -1) | (this_sub[:, cls.LEX_COL] != -1)).sum() <= 1: + # only 1 line + continue + if (this_sub[:, cls.LOR_COL] != -1).any(): + line_or_id = this_sub[this_sub[:, cls.LOR_COL] != -1, cls.LOR_COL][0] + line_ex_id = None + else: + line_or_id = None + line_ex_id = this_sub[this_sub[:, cls.LEX_COL] != -1, cls.LEX_COL][0] + res = (sub_id, el_id, line_or_id, line_ex_id) + break + return res + + def test_move_load(self): + cls = type(self.env) + res = self._aux_find_sub(self.env, cls.LOA_COL) + if res is None: + raise RuntimeError(f"Cannot carry the test 'test_move_load' as " + "there are no suitable subastation in your grid.") + (sub_id, el_id, line_or_id, line_ex_id) = res + for new_bus in self.list_loc_bus: + if line_or_id is not None: + act = self.env.action_space({"set_bus": {"loads_id": [(el_id, new_bus)], "lines_or_id": [(line_or_id, new_bus)]}}) + else: + act = self.env.action_space({"set_bus": {"loads_id": [(el_id, new_bus)], "lines_ex_id": [(line_ex_id, new_bus)]}}) + bk_act = self.env._backend_action_class() + bk_act += act + self.env.backend.apply_action(bk_act) + global_bus = sub_id + (new_bus -1) * cls.n_sub + if new_bus >= 1: + assert self.env.backend._grid.get_loads()[el_id].bus_id == global_bus, f"error for new_bus {new_bus}: {self.env.backend._grid.get_loads()[el_id].bus_id} vs {global_bus}" + if line_or_id is not None: + assert self.env.backend._grid.get_lines()[line_or_id].bus_or_id == global_bus + else: + assert self.env.backend._grid.get_lines()[line_ex_id].bus_ex_id == global_bus + assert self.env.backend._grid.get_bus_status()[global_bus] + else: + assert not self.env.backend._grid.get_loads()[el_id].connected + if line_or_id is not None: + assert not self.env.backend._grid.get_lines()[line_or_id].connected + else: + assert not self.env.backend._grid.get_lines()[line_ex_id].connected + topo_vect = 1 * self.env.backend.get_topo_vect() + assert topo_vect[cls.load_pos_topo_vect[el_id]] == new_bus, f"{topo_vect[cls.load_pos_topo_vect[el_id]]} vs {new_bus}" + + def test_move_gen(self): + cls = type(self.env) + res = self._aux_find_sub(self.env, cls.GEN_COL) + if res is None: + raise RuntimeError(f"Cannot carry the test 'test_move_gen' as " + "there are no suitable subastation in your grid.") + (sub_id, el_id, line_or_id, line_ex_id) = res + for new_bus in self.list_loc_bus: + if line_or_id is not None: + act = self.env.action_space({"set_bus": {"generators_id": [(el_id, new_bus)], "lines_or_id": [(line_or_id, new_bus)]}}) + else: + act = self.env.action_space({"set_bus": {"generators_id": [(el_id, new_bus)], "lines_ex_id": [(line_ex_id, new_bus)]}}) + bk_act = self.env._backend_action_class() + bk_act += act + self.env.backend.apply_action(bk_act) + global_bus = sub_id + (new_bus -1) * cls.n_sub + if new_bus >= 1: + assert self.env.backend._grid.get_generators()[el_id].bus_id == global_bus, f"error for new_bus {new_bus}: {self.env.backend._grid.get_loads()[el_id].bus_id} vs {global_bus}" + if line_or_id is not None: + assert self.env.backend._grid.get_lines()[line_or_id].bus_or_id == global_bus + else: + assert self.env.backend._grid.get_lines()[line_ex_id].bus_ex_id == global_bus + assert self.env.backend._grid.get_bus_status()[global_bus] + else: + assert not self.env.backend._grid.get_generators()[el_id].connected + if line_or_id is not None: + assert not self.env.backend._grid.get_lines()[line_or_id].connected + else: + assert not self.env.backend._grid.get_lines()[line_ex_id].connected + topo_vect = 1 * self.env.backend.get_topo_vect() + assert topo_vect[cls.gen_pos_topo_vect[el_id]] == new_bus, f"{topo_vect[cls.gen_pos_topo_vect[el_id]]} vs {new_bus}" + + def test_move_storage(self): + cls = type(self.env) + res = self._aux_find_sub(self.env, cls.STORAGE_COL) + if res is None: + raise RuntimeError(f"Cannot carry the test 'test_move_storage' as " + "there are no suitable subastation in your grid.") + (sub_id, el_id, line_or_id, line_ex_id) = res + for new_bus in self.list_loc_bus: + if line_or_id is not None: + act = self.env.action_space({"set_bus": {"storages_id": [(el_id, new_bus)], "lines_or_id": [(line_or_id, new_bus)]}}) + else: + act = self.env.action_space({"set_bus": {"storages_id": [(el_id, new_bus)], "lines_ex_id": [(line_ex_id, new_bus)]}}) + bk_act = self.env._backend_action_class() + bk_act += act + self.env.backend.apply_action(bk_act) + global_bus = sub_id + (new_bus -1) * cls.n_sub + if new_bus >= 1: + assert self.env.backend._grid.get_storages()[el_id].bus_id == global_bus, f"error for new_bus {new_bus}: {self.env.backend._grid.get_loads()[el_id].bus_id} vs {global_bus}" + if line_or_id is not None: + assert self.env.backend._grid.get_lines()[line_or_id].bus_or_id == global_bus + else: + assert self.env.backend._grid.get_lines()[line_ex_id].bus_ex_id == global_bus + assert self.env.backend._grid.get_bus_status()[global_bus] + else: + assert not self.env.backend._grid.get_storages()[el_id].connected + if line_or_id is not None: + assert not self.env.backend._grid.get_lines()[line_or_id].connected + else: + assert not self.env.backend._grid.get_lines()[line_ex_id].connected + topo_vect = 1 * self.env.backend.get_topo_vect() + assert topo_vect[cls.storage_pos_topo_vect[el_id]] == new_bus, f"{topo_vect[cls.storage_pos_topo_vect[el_id]]} vs {new_bus}" + + def test_move_line_or(self): + cls = type(self.env) + line_id = 0 + for new_bus in self.list_loc_bus: + act = self.env.action_space({"set_bus": {"lines_or_id": [(line_id, new_bus)]}}) + bk_act = self.env._backend_action_class() + bk_act += act + self.env.backend.apply_action(bk_act) + global_bus = cls.line_or_to_subid[line_id] + (new_bus -1) * cls.n_sub + if new_bus >= 1: + assert self.env.backend._grid.get_lines()[line_id].bus_or_id == global_bus + assert self.env.backend._grid.get_bus_status()[global_bus] + else: + assert not self.env.backend._grid.get_lines()[line_id].connected + topo_vect = 1 * self.env.backend.get_topo_vect() + assert topo_vect[cls.line_or_pos_topo_vect[line_id]] == new_bus, f"{topo_vect[cls.line_or_pos_topo_vect[line_id]]} vs {new_bus}" + + def test_move_line_ex(self): + cls = type(self.env) + line_id = 0 + for new_bus in self.list_loc_bus: + act = self.env.action_space({"set_bus": {"lines_ex_id": [(line_id, new_bus)]}}) + bk_act = self.env._backend_action_class() + bk_act += act + self.env.backend.apply_action(bk_act) + global_bus = cls.line_ex_to_subid[line_id] + (new_bus -1) * cls.n_sub + if new_bus >= 1: + assert self.env.backend._grid.get_lines()[line_id].bus_ex_id == global_bus + assert self.env.backend._grid.get_bus_status()[global_bus] + else: + assert not self.env.backend._grid.get_lines()[line_id].connected + topo_vect = 1 * self.env.backend.get_topo_vect() + assert topo_vect[cls.line_ex_pos_topo_vect[line_id]] == new_bus, f"{topo_vect[cls.line_ex_pos_topo_vect[line_id]]} vs {new_bus}" + + def test_move_shunt(self): + cls = type(self.env) + res = self._aux_find_sub_shunt(self.env) + if res is None: + raise RuntimeError(f"Cannot carry the test 'test_move_load' as " + "there are no suitable subastation in your grid.") + (sub_id, el_id, line_or_id, line_ex_id) = res + for new_bus in self.list_loc_bus: + if line_or_id is not None: + act = self.env.action_space({"shunt": {"set_bus": [(el_id, new_bus)]}, "set_bus": {"lines_or_id": [(line_or_id, new_bus)]}}) + else: + act = self.env.action_space({"shunt": {"set_bus": [(el_id, new_bus)]}, "set_bus": {"lines_ex_id": [(line_ex_id, new_bus)]}}) + bk_act = self.env._backend_action_class() + bk_act += act + self.env.backend.apply_action(bk_act) + global_bus = sub_id + (new_bus -1) * cls.n_sub + if new_bus >= 1: + assert self.env.backend._grid.get_shunts()[el_id].bus_id == global_bus, f"error for new_bus {new_bus}: {self.env.backend._grid.get_loads()[el_id].bus_id} vs {global_bus}" + if line_or_id is not None: + assert self.env.backend._grid.get_lines()[line_or_id].bus_or_id == global_bus + else: + assert self.env.backend._grid.get_lines()[line_ex_id].bus_ex_id == global_bus + assert self.env.backend._grid.get_bus_status()[global_bus] + else: + assert not self.env.backend._grid.get_shunts()[el_id].connected + if line_or_id is not None: + assert not self.env.backend._grid.get_lines()[line_or_id].connected + else: + assert not self.env.backend._grid.get_lines()[line_ex_id].connected + + def test_check_kirchoff(self): + cls = type(self.env) + res = self._aux_find_sub(self.env, cls.LOA_COL) + if res is None: + raise RuntimeError("Cannot carry the test 'test_move_load' as " + "there are no suitable subastation in your grid.") + (sub_id, el_id, line_or_id, line_ex_id) = res + for new_bus in self.list_loc_bus: + if new_bus <= -1: + continue + if line_or_id is not None: + act = self.env.action_space({"set_bus": {"loads_id": [(el_id, new_bus)], "lines_or_id": [(line_or_id, new_bus)]}}) + else: + act = self.env.action_space({"set_bus": {"loads_id": [(el_id, new_bus)], "lines_ex_id": [(line_ex_id, new_bus)]}}) + bk_act = self.env._backend_action_class() + bk_act += act + self.env.backend.apply_action(bk_act) + conv, maybe_exc = self.env.backend.runpf() + assert conv, f"error : {maybe_exc}" + p_subs, q_subs, p_bus, q_bus, diff_v_bus = self.env.backend.check_kirchoff() + # assert laws are met + assert np.abs(p_subs).max() <= 1e-5, f"error for busbar {new_bus}: {np.abs(p_subs).max():.2e}" + assert np.abs(q_subs).max() <= 1e-5, f"error for busbar {new_bus}: {np.abs(q_subs).max():.2e}" + assert np.abs(p_bus).max() <= 1e-5, f"error for busbar {new_bus}: {np.abs(p_bus).max():.2e}" + assert np.abs(q_bus).max() <= 1e-5, f"error for busbar {new_bus}: {np.abs(q_bus).max():.2e}" + assert np.abs(diff_v_bus).max() <= 1e-5, f"error for busbar {new_bus}: {np.abs(diff_v_bus).max():.2e}" + + +class TestLightSimBackend_1busbar(TestLightSimBackend_3busbars): + def get_nb_bus(self): + return 1 + + +class TestLightSimBackend_3busbars_iidm(TestLightSimBackend_3busbars): + def get_env_nm(self): + return "./case_14_storage_iidm" + + def get_backend_kwargs(self): + return dict(loader_method="pypowsybl", + loader_kwargs={"use_buses_for_sub": True, + "double_bus_per_sub": True, + "gen_slack_id": 5} + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/lightsim2grid/tests/test_solver_control.py b/lightsim2grid/tests/test_solver_control.py index ec9a897..ae2a8f1 100644 --- a/lightsim2grid/tests/test_solver_control.py +++ b/lightsim2grid/tests/test_solver_control.py @@ -25,7 +25,6 @@ import numpy as np import grid2op from grid2op.Action import CompleteAction -from grid2op.Parameters import Parameters from lightsim2grid import LightSimBackend from lightsim2grid.solver import SolverType diff --git a/src/GridModel.cpp b/src/GridModel.cpp index ba83336..67b4853 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -330,6 +330,9 @@ void GridModel::reset(bool reset_solver, bool reset_ac, bool reset_dc) Ybus_dc_ = Eigen::SparseMatrix(); } + timer_last_ac_pf_= 0.; + timer_last_dc_pf_ = 0.; + acSbus_ = CplxVect(); dcSbus_ = CplxVect(); bus_pv_ = Eigen::VectorXi(); @@ -356,6 +359,7 @@ CplxVect GridModel::ac_pf(const CplxVect & Vinit, int max_iter, real_type tol) { + auto timer = CustTimer(); const int nb_bus = static_cast(bus_vn_kv_.size()); if(Vinit.size() != nb_bus){ std::ostringstream exc_; @@ -399,6 +403,7 @@ CplxVect GridModel::ac_pf(const CplxVect & Vinit, // std::cout << "\tbefore process_results" << std::endl; process_results(conv, res, Vinit, true, id_me_to_ac_solver_); + timer_last_ac_pf_ = timer.duration(); // return the vector of complex voltage at each bus return res; }; @@ -882,6 +887,8 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, // the idea is to "mess" with the Sbus beforehand to split the "losses" // ie fake the action of generators to adjust Sbus such that sum(Sbus) = 0 // and the slack contribution factors are met. + auto timer = CustTimer(); + const int nb_bus = static_cast(bus_vn_kv_.size()); if(Vinit.size() != nb_bus){ //TODO DEBUG MODE: @@ -929,7 +936,7 @@ CplxVect GridModel::dc_pf(const CplxVect & Vinit, // std::cout << "\tprocess_results (dc) \n"; // store results (fase -> because I am in dc mode) process_results(conv, res, Vinit, is_ac, id_me_to_dc_solver_); - // std::cout << "\tafter compute_pf\n"; + timer_last_dc_pf_ = timer.duration(); return res; } diff --git a/src/GridModel.h b/src/GridModel.h index 7a0a104..4fefb2f 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -92,6 +92,8 @@ class GridModel : public GenericContainer > StateRes; GridModel(): + timer_last_ac_pf_(0.), + timer_last_dc_pf_(0.), solver_control_(), compute_results_(true), init_vm_pu_(1.04), @@ -113,6 +115,8 @@ class GridModel : public GenericContainer 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;} + double timer_last_ac_pf() const {return timer_last_ac_pf_;} + double timer_last_dc_pf() const {return timer_last_dc_pf_;} Eigen::Index total_bus() const {return bus_vn_kv_.size();} const std::vector & id_me_to_ac_solver() const {return id_me_to_ac_solver_;} @@ -613,8 +617,7 @@ class GridModel : public GenericContainer } void set_max_nb_bus_per_sub(int max_nb_bus_per_sub) { - max_nb_bus_per_sub_ = max_nb_bus_per_sub; - if(bus_vn_kv_.size() != n_sub_ * max_nb_bus_per_sub_){ + if(bus_vn_kv_.size() != n_sub_ * max_nb_bus_per_sub){ std::ostringstream exc_; exc_ << "GridModel::set_max_nb_bus_per_sub: "; exc_ << "your model counts "; @@ -624,7 +627,9 @@ class GridModel : public GenericContainer exc_ << "substations / buses per substations with `set_n_sub` / `set_max_nb_bus_per_sub`"; throw std::runtime_error(exc_.str()); } + max_nb_bus_per_sub_ = max_nb_bus_per_sub; } + int get_max_nb_bus_per_sub() const { return max_nb_bus_per_sub_;} void fillSbus_other(CplxVect & res, bool ac, const std::vector& id_me_to_solver){ fillSbus_me(res, ac, id_me_to_solver); @@ -746,8 +751,8 @@ class GridModel : public GenericContainer int new_bus = new_values(el_pos); if(new_bus > 0){ // new bus is a real bus, so i need to make sure to have it turned on, and then change the bus - int init_bus_me = vect_subid(el_id); - int new_bus_backend = new_bus == 1 ? init_bus_me : init_bus_me + n_sub_ * (max_nb_bus_per_sub_ - 1); + int sub_id = vect_subid(el_id); + int new_bus_backend = sub_id + (new_bus - 1) * n_sub_; bus_status_[new_bus_backend] = true; (this->*fun_react)(el_id); // eg reactivate_load(load_id); (this->*fun_change)(el_id, new_bus_backend); // eg change_bus_load(load_id, new_bus_backend); @@ -774,7 +779,8 @@ class GridModel : public GenericContainer 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; + double timer_last_ac_pf_; + double timer_last_dc_pf_; // bool need_reset_solver_; // some matrices change size, needs to be computed // bool need_recompute_sbus_; // some coeff of sbus changed, need to recompute it diff --git a/src/main.cpp b/src/main.cpp index a1c9a6b..dd001e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -613,6 +613,12 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) .def("copy", &GridModel::copy) .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.") + .def_property("_max_nb_bus_per_sub", + &GridModel::get_max_nb_bus_per_sub, + &GridModel::set_max_nb_bus_per_sub, + "do not modify it after loading !") + .def_property_readonly("timer_last_ac_pf", &GridModel::timer_last_ac_pf, "TODO") + .def_property_readonly("timer_last_dc_pf", &GridModel::timer_last_dc_pf, "TODO") // pickle .def(py::pickle( From b465c2b26f2ddfe2011990ca11990d1b554ec723 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 7 Mar 2024 17:58:41 +0100 Subject: [PATCH 18/42] fixing bug in the benchmarking of grid size, improve solver control --- .circleci/config.yml | 15 ++- .readthedocs.yml | 11 +- CHANGELOG.rst | 1 + benchmarks/benchmark_grid_size.py | 113 +++++++++++++----- lightsim2grid/lightSimBackend.py | 2 + src/element_container/GeneratorContainer.cpp | 30 +++-- src/element_container/LoadContainer.cpp | 24 ++-- src/element_container/SGenContainer.cpp | 40 ++++--- src/element_container/ShuntContainer.cpp | 19 ++- src/powerflow_algorithm/BaseDCAlgo.tpp | 5 + src/powerflow_algorithm/BaseFDPFAlgo.tpp | 51 +++++--- src/powerflow_algorithm/BaseNRAlgo.tpp | 28 +++-- .../BaseNRSingleSlackAlgo.tpp | 45 ++++--- 13 files changed, 259 insertions(+), 125 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6973ecc..4ea5c7c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,6 +46,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -64,6 +65,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -82,6 +84,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -100,6 +103,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -118,6 +122,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip python3-virtualenv -y + - run: python3 -m pip install --upgrade pip setuptools # - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -148,6 +153,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -165,6 +171,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip git -y + - run: python3 -m pip install --upgrade pip setuptools - run: command: | git submodule init @@ -184,6 +191,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip git -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -202,6 +210,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip git -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -220,6 +229,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip git -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -238,6 +248,7 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip git -y + - run: python3 -m pip install --upgrade pip setuptools - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -268,9 +279,7 @@ jobs: size: medium # ("medium" "large" "xlarge" "2xlarge") steps: - checkout - # - run: - # name: "Install Python" - # command: choco install python --version=3.9 # use python 3.9 for windows test + - run: python3 -m pip install --upgrade pip setuptools - run: py -m pip install virtualenv - run: py -m virtualenv venv_test - run: diff --git a/.readthedocs.yml b/.readthedocs.yml index 97df330..ab6ff9c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,4 +1,4 @@ -version: 2 +version: "2" submodules: include: @@ -6,8 +6,15 @@ submodules: - eigen recursive: true +build: + os: "ubuntu-22.04" + tools: + python: "3.10" + +sphinx: + configuration: docs/conf.py + python: - version: 3.8 install: - method: pip path: . diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 666a2a4..2fa05e1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -39,6 +39,7 @@ Change Log - [FIXED] a bug where copying a lightsim2grid `GridModel` did not fully copy it - [FIXED] a bug in the "topo_vect" comprehension cpp side (sometimes some buses might not be activated / deactivated correctly) +- [FIXED] read the docs was broken - [ADDED] sets of methods to extract the main component of a grid and perform powerflow only on this one. - [ADDED] possibility to set / retrieve the names of each elements of the grid. diff --git a/benchmarks/benchmark_grid_size.py b/benchmarks/benchmark_grid_size.py index 09d627e..a2e2668 100644 --- a/benchmarks/benchmark_grid_size.py +++ b/benchmarks/benchmark_grid_size.py @@ -7,12 +7,15 @@ # This file is part of LightSim2grid, LightSim2grid a implements a c++ backend targeting the Grid2Op platform. import warnings +import copy import pandapower as pp -import numpy as np +import numpy as np +import hashlib from scipy.interpolate import interp1d import matplotlib.pyplot as plt from grid2op import make, Parameters from grid2op.Chronics import FromNPY +from grid2op.Backend import PandaPowerBackend from lightsim2grid import LightSimBackend, TimeSerie try: from lightsim2grid import ContingencyAnalysis @@ -33,6 +36,8 @@ VERBOSE = False MAKE_PLOT = False +WITH_PP = False +DEBUG = False case_names = [ "case14.json", @@ -69,7 +74,28 @@ def make_grid2op_env(pp_case, casse_name, load_p, load_q, gen_p, sgen_p): ) return env_lightsim -def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): + +def make_grid2op_env_pp(pp_case, casse_name, load_p, load_q, gen_p, sgen_p): + param = Parameters.Parameters() + param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True}) + + with warnings.catch_warnings(): + warnings.filterwarnings("ignore") + env_pp = make("blank", + param=param, test=True, + backend=PandaPowerBackend(lightsim2grid=False), + chronics_class=FromNPY, + data_feeding_kwargs={"load_p": load_p, + "load_q": load_q, + "prod_p": gen_p + }, + grid_path=case_name, + _add_to_name=f"{case_name}", + ) + return env_pp + + +def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init, prng): # scale loads # use some French time series data for loads @@ -137,28 +163,31 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): vals = np.array(vals) * coeffs["month"]["oct"] * coeffs["day"]["mon"] x_interp = 12 * np.arange(len(vals)) coeffs = interp1d(x=x_interp, y=vals, kind="cubic") - all_vals = coeffs(x_final) + all_vals = coeffs(x_final).reshape(-1, 1) + if DEBUG: + all_vals[:] = 1 # compute the "smooth" loads matrix - load_p_smooth = all_vals.reshape(-1, 1) * load_p_init.reshape(1, -1) - load_q_smooth = all_vals.reshape(-1, 1) * load_q_init.reshape(1, -1) + load_p_smooth = all_vals * load_p_init.reshape(1, -1) + load_q_smooth = all_vals * load_q_init.reshape(1, -1) # add a bit of noise to it to get the "final" loads matrix - load_p = load_p_smooth * np.random.lognormal(mean=0., sigma=0.003, size=load_p_smooth.shape) - load_q = load_q_smooth * np.random.lognormal(mean=0., sigma=0.003, size=load_q_smooth.shape) - + load_p = load_p_smooth * prng.lognormal(mean=0., sigma=0.003, size=load_p_smooth.shape) + load_q = load_q_smooth * prng.lognormal(mean=0., sigma=0.003, size=load_q_smooth.shape) + if DEBUG: + load_p[:] = load_p_smooth + load_q[:] = load_q_smooth + # scale generators accordingly gen_p = load_p.sum(axis=1).reshape(-1, 1) / load_p_init.sum() * gen_p_init.reshape(1, -1) sgen_p = load_p.sum(axis=1).reshape(-1, 1) / load_p_init.sum() * sgen_p_init.reshape(1, -1) - return load_p, load_q, gen_p, sgen_p if __name__ == "__main__": - np.random.seed(42) - + prng = np.random.default_rng(42) case_names_displayed = [get_env_name_displayed(el) for el in case_names] - g2op_times = [] + solver_preproc_solver_time = [] g2op_speeds = [] g2op_sizes = [] g2op_step_time = [] @@ -197,8 +226,14 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): res_unit = "ms" # simulate the data - load_p, load_q, gen_p, sgen_p = get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init) - + load_p, load_q, gen_p, sgen_p = get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init, prng) + if DEBUG: + hash_fun = hashlib.blake2b(digest_size=16) + hash_fun.update(load_p.tobytes()) + hash_fun.update(load_q.tobytes()) + hash_fun.update(gen_p.tobytes()) + hash_fun.update(sgen_p.tobytes()) + print(hash_fun.hexdigest()) # create the grid2op env nb_ts = gen_p.shape[0] # add slack ! @@ -206,15 +241,33 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): if "res_ext_grid" in case: slack_gens += np.tile(case.res_ext_grid["p_mw"].values.reshape(1,-1), (nb_ts, 1)) gen_p_g2op = np.concatenate((gen_p, slack_gens), axis=1) - # get the env + # get the env + if WITH_PP: + env_pp = make_grid2op_env_pp(case, + case_name, + load_p, + load_q, + gen_p_g2op, + sgen_p) + _ = env_pp.reset() + done = False + nb_step_pp = 0 + changed_sgen = case.sgen["in_service"].values + while not done: + # hack for static gen... + env_pp.backend._grid.sgen["p_mw"] = sgen_p[nb_step_pp, :] + obs, reward, done, info = env_pp.step(env_pp.action_space()) + nb_step_pp += 1 + if nb_step_pp != nb_ts: + warnings.warn("Divergence even with pandapower !") + print("Pandapower stops, lightsim starts") + env_lightsim = make_grid2op_env(case, case_name, load_p, load_q, gen_p_g2op, sgen_p) - - # Perform the computation using grid2op _ = env_lightsim.reset() done = False @@ -222,16 +275,20 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): changed_sgen = case.sgen["in_service"].values while not done: # hack for static gen... - env_lightsim.backend._grid.update_sgens_p(changed_sgen, - sgen_p[nb_step, changed_sgen].astype(np.float32)) + changed_sgen = copy.deepcopy(case.sgen["in_service"].values) + this_sgen = sgen_p[nb_step, :].astype(np.float32) + # this_sgen = sgen_p_init[changed_sgen].astype(np.float32) + env_lightsim.backend._grid.update_sgens_p(changed_sgen, this_sgen) obs, reward, done, info = env_lightsim.step(env_lightsim.action_space()) nb_step += 1 # NB lightsim2grid does not handle "static gen" because I cannot set "p" in gen in grid2op # so results will vary between TimeSeries and grid2op ! - + # env_lightsim.backend._grid.tell_solver_need_reset() + # env_lightsim.backend._grid.dc_pf(env_lightsim.backend.V, 1, 1e-7) + # env_lightsim.backend._grid.get_bus_status() if nb_step != nb_ts: warnings.warn(f"only able to make {nb_step} (out of {nb_ts}) for {case_name} in grid2op. Results will not be availabe for grid2op step") - g2op_times.append(None) + solver_preproc_solver_time.append(None) g2op_speeds.append(None) g2op_step_time.append(None) ls_solver_time.append(None) @@ -239,8 +296,8 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): g2op_sizes.append(env_lightsim.n_sub) else: total_time = env_lightsim.backend._timer_preproc + env_lightsim.backend._timer_solver # + env_lightsim.backend._timer_postproc - total_time = env_lightsim._time_step - g2op_times.append(total_time) + # total_time = env_lightsim._time_step + solver_preproc_solver_time.append(total_time) g2op_speeds.append(1.0 * nb_step / total_time) g2op_step_time.append(1.0 * env_lightsim._time_step / nb_step) ls_solver_time.append(env_lightsim.backend.comp_time) @@ -262,7 +319,7 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): env_lightsim.backend.tol) time_serie._TimeSerie__computed = True a_or = time_serie.compute_A() - assert status, f"some powerflow diverge for {case_name}: {computer.nb_solved()} " + assert status, f"some powerflow diverge for Time Series for {case_name}: {computer.nb_solved()} " if VERBOSE: # print detailed results if needed @@ -311,9 +368,9 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): for i, nm_ in enumerate(case_names_displayed): tab_g2op.append((nm_, ts_sizes[i], + 1000. * g2op_step_time[i] if g2op_step_time[i] else None, 1000. / g2op_speeds[i] if g2op_speeds[i] else None, g2op_speeds[i], - 1000. * g2op_step_time[i] if g2op_step_time[i] else None, 1000. * ls_gridmodel_time[i] / nb_step if ls_gridmodel_time[i] else None, 1000. * ls_solver_time[i] / nb_step if ls_solver_time[i] else None, )) @@ -321,9 +378,9 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): res_use_with_grid2op_2 = tabulate(tab_g2op, headers=["grid", "size (nb bus)", - "step time (ms / pf)", - "speed (pf / s)", "avg step duration (ms)", + "time [DC + AC] (ms / pf)", + "speed (pf / s)", "time in 'gridmodel' (ms / pf)", "time in 'pf algo' (ms / pf)", ], @@ -362,7 +419,7 @@ def get_loads_gens(load_p_init, load_q_init, gen_p_init, sgen_p_init): if MAKE_PLOT: # make the plot summarizing all results - plt.plot(g2op_sizes, g2op_times, linestyle='solid', marker='+', markersize=8) + plt.plot(g2op_sizes, solver_preproc_solver_time, linestyle='solid', marker='+', markersize=8) plt.xlabel("Size (number of substation)") plt.ylabel("Time taken (s)") plt.title(f"Time to compute {g2op_sizes[0]} powerflows using Grid2Op.step (dc pf [init] + ac pf)") diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index bbecd58..f0d57c5 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -415,6 +415,7 @@ def load_grid(self, path=None, filename=None): self._load_grid_pypowsybl(path, filename) else: raise BackendError(f"Impossible to initialize the backend with '{self._loader_method}'") + self._grid.tell_solver_need_reset() def _should_not_have_to_do_this(self, path=None, filename=None): # included in grid2op now ! @@ -1230,3 +1231,4 @@ def reset(self, grid_path, grid_filename=None): self._timer_postproc = 0. self._timer_preproc = 0. self._timer_solver = 0. + self._grid.tell_solver_need_reset() diff --git a/src/element_container/GeneratorContainer.cpp b/src/element_container/GeneratorContainer.cpp index e45767b..b580c40 100644 --- a/src/element_container/GeneratorContainer.cpp +++ b/src/element_container/GeneratorContainer.cpp @@ -167,9 +167,9 @@ void GeneratorContainer::fillSbus(CplxVect & Sbus, const std::vector & id_g } void GeneratorContainer::fillpv(std::vector & bus_pv, - std::vector & has_bus_been_added, - const Eigen::VectorXi & slack_bus_id_solver, - const std::vector & id_grid_to_solver) const + std::vector & has_bus_been_added, + const Eigen::VectorXi & slack_bus_id_solver, + const std::vector & id_grid_to_solver) const { const int nb_gen = nb(); int bus_id_me, bus_id_solver; @@ -198,12 +198,12 @@ void GeneratorContainer::fillpv(std::vector & bus_pv, } void GeneratorContainer::compute_results(const Eigen::Ref & Va, - const Eigen::Ref & Vm, - const Eigen::Ref & V, - const std::vector & id_grid_to_solver, - const RealVect & bus_vn_kv, - real_type sn_mva, - bool ac) + const Eigen::Ref & Vm, + const Eigen::Ref & V, + const std::vector & id_grid_to_solver, + const RealVect & bus_vn_kv, + real_type sn_mva, + bool ac) { const int nb_gen = nb(); v_kv_from_vpu(Va, Vm, status_, nb_gen, bus_id_, id_grid_to_solver, bus_vn_kv, res_v_); @@ -257,8 +257,10 @@ void GeneratorContainer::change_p(int gen_id, real_type new_p, SolverControl & s solver_control.tell_pv_changed(); } } - if (p_mw_(gen_id) != new_p) solver_control.tell_recompute_sbus(); - p_mw_(gen_id) = new_p; + if (p_mw_(gen_id) != new_p){ + solver_control.tell_recompute_sbus(); + p_mw_(gen_id) = new_p; + } } void GeneratorContainer::change_q(int gen_id, real_type new_q, SolverControl & solver_control) @@ -275,8 +277,10 @@ void GeneratorContainer::change_q(int gen_id, real_type new_q, SolverControl & s } // TODO DEBUG MODE : raise an error if generator is regulating voltage, maybe ? // this would have not effect - if (q_mvar_(gen_id) != new_q) solver_control.tell_recompute_sbus(); - q_mvar_(gen_id) = new_q; + if (q_mvar_(gen_id) != new_q){ + solver_control.tell_recompute_sbus(); + q_mvar_(gen_id) = new_q; + } } void GeneratorContainer::change_v(int gen_id, real_type new_v_pu, SolverControl & solver_control) diff --git a/src/element_container/LoadContainer.cpp b/src/element_container/LoadContainer.cpp index 4a4de71..d7eb561 100644 --- a/src/element_container/LoadContainer.cpp +++ b/src/element_container/LoadContainer.cpp @@ -71,12 +71,12 @@ void LoadContainer::fillSbus(CplxVect & Sbus, const std::vector & id_grid_t } void LoadContainer::compute_results(const Eigen::Ref & Va, - const Eigen::Ref & Vm, - const Eigen::Ref & V, - const std::vector & id_grid_to_solver, - const RealVect & bus_vn_kv, - real_type sn_mva, - bool ac) + const Eigen::Ref & Vm, + const Eigen::Ref & V, + const std::vector & id_grid_to_solver, + const RealVect & bus_vn_kv, + real_type sn_mva, + bool ac) { int nb_load = nb(); v_kv_from_vpu(Va, Vm, status_, nb_load, bus_id_, id_grid_to_solver, bus_vn_kv, res_v_); @@ -102,8 +102,10 @@ void LoadContainer::change_p(int load_id, real_type new_p, SolverControl & solve exc_ << ")"; throw std::runtime_error(exc_.str()); } - if (p_mw_(load_id) != new_p) solver_control.tell_recompute_sbus(); - p_mw_(load_id) = new_p; + if (p_mw_(load_id) != new_p) { + solver_control.tell_recompute_sbus(); + p_mw_(load_id) = new_p; + } } void LoadContainer::change_q(int load_id, real_type new_q, SolverControl & solver_control) @@ -117,8 +119,10 @@ void LoadContainer::change_q(int load_id, real_type new_q, SolverControl & solve exc_ << ")"; throw std::runtime_error(exc_.str()); } - if (q_mvar_(load_id) != new_q) solver_control.tell_recompute_sbus(); - q_mvar_(load_id) = new_q; + if (q_mvar_(load_id) != new_q) { + solver_control.tell_recompute_sbus(); + q_mvar_(load_id) = new_q; + } } void LoadContainer::reconnect_connected_buses(std::vector & bus_status) const { diff --git a/src/element_container/SGenContainer.cpp b/src/element_container/SGenContainer.cpp index 48c1baa..6300727 100644 --- a/src/element_container/SGenContainer.cpp +++ b/src/element_container/SGenContainer.cpp @@ -7,14 +7,15 @@ // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. #include "SGenContainer.h" +#include void SGenContainer::init(const RealVect & sgen_p, - const RealVect & sgen_q, - const RealVect & sgen_pmin, - const RealVect & sgen_pmax, - const RealVect & sgen_qmin, - const RealVect & sgen_qmax, - const Eigen::VectorXi & sgen_bus_id) + const RealVect & sgen_q, + const RealVect & sgen_pmin, + const RealVect & sgen_pmax, + const RealVect & sgen_qmin, + const RealVect & sgen_qmax, + const Eigen::VectorXi & sgen_bus_id) { int size = static_cast(sgen_p.size()); GenericContainer::check_size(sgen_p, size, "sgen_p"); @@ -100,19 +101,18 @@ void SGenContainer::fillSbus(CplxVect & Sbus, const std::vector & id_grid_t exc_ << " is connected to a disconnected bus while being connected to the grid."; throw std::runtime_error(exc_.str()); } - tmp = static_cast(p_mw_(sgen_id)); - tmp += my_i * q_mvar_(sgen_id); + tmp = {p_mw_(sgen_id), q_mvar_(sgen_id)}; Sbus.coeffRef(bus_id_solver) += tmp; } } void SGenContainer::compute_results(const Eigen::Ref & Va, - const Eigen::Ref & Vm, - const Eigen::Ref & V, - const std::vector & id_grid_to_solver, - const RealVect & bus_vn_kv, - real_type sn_mva, - bool ac) + const Eigen::Ref & Vm, + const Eigen::Ref & V, + const std::vector & id_grid_to_solver, + const RealVect & bus_vn_kv, + real_type sn_mva, + bool ac) { const int nb_sgen = nb(); v_kv_from_vpu(Va, Vm, status_, nb_sgen, bus_id_, id_grid_to_solver, bus_vn_kv, res_v_); @@ -139,8 +139,10 @@ void SGenContainer::change_p(int sgen_id, real_type new_p, SolverControl & solve exc_ << ")"; throw std::runtime_error(exc_.str()); } - if (p_mw_(sgen_id) != new_p) solver_control.tell_recompute_sbus(); - p_mw_(sgen_id) = new_p; + if (p_mw_(sgen_id) != new_p){ + solver_control.tell_recompute_sbus(); + p_mw_(sgen_id) = new_p; + } } void SGenContainer::change_q(int sgen_id, real_type new_q, SolverControl & solver_control) @@ -154,8 +156,10 @@ void SGenContainer::change_q(int sgen_id, real_type new_q, SolverControl & solve exc_ << ")"; throw std::runtime_error(exc_.str()); } - if (q_mvar_(sgen_id) != new_q) solver_control.tell_recompute_sbus(); - q_mvar_(sgen_id) = new_q; + if (q_mvar_(sgen_id) != new_q){ + solver_control.tell_recompute_sbus(); + q_mvar_(sgen_id) = new_q; + } } void SGenContainer::reconnect_connected_buses(std::vector & bus_status) const { diff --git a/src/element_container/ShuntContainer.cpp b/src/element_container/ShuntContainer.cpp index 5817834..76a6129 100644 --- a/src/element_container/ShuntContainer.cpp +++ b/src/element_container/ShuntContainer.cpp @@ -48,9 +48,9 @@ void ShuntContainer::set_state(ShuntContainer::StateRes & my_state ) } void ShuntContainer::fillYbus(std::vector > & res, - bool ac, - const std::vector & id_grid_to_solver, - real_type sn_mva) const + bool ac, + const std::vector & id_grid_to_solver, + real_type sn_mva) const { if(!ac) return; // no shunt in DC @@ -79,10 +79,10 @@ void ShuntContainer::fillYbus(std::vector > & res, } void ShuntContainer::fillBp_Bpp(std::vector > & Bp, - std::vector > & Bpp, - const std::vector & id_grid_to_solver, - real_type sn_mva, - FDPFMethod xb_or_bx) const + std::vector > & Bpp, + const std::vector & id_grid_to_solver, + real_type sn_mva, + FDPFMethod xb_or_bx) const { const Eigen::Index nb_shunt = static_cast(q_mvar_.size()); real_type tmp; @@ -174,9 +174,8 @@ void ShuntContainer::change_p(int shunt_id, real_type new_p, SolverControl & sol if(p_mw_(shunt_id) != new_p){ solver_control.tell_recompute_ybus(); solver_control.tell_recompute_sbus(); // in dc mode sbus is modified + p_mw_(shunt_id) = new_p; } - p_mw_(shunt_id) = new_p; - } void ShuntContainer::change_q(int shunt_id, real_type new_q, SolverControl & solver_control) @@ -185,8 +184,8 @@ void ShuntContainer::change_q(int shunt_id, real_type new_q, SolverControl & sol if(!my_status) throw std::runtime_error("Impossible to change the reactive value of a disconnected shunt"); if(q_mvar_(shunt_id) != new_q){ solver_control.tell_recompute_ybus(); + q_mvar_(shunt_id) = new_q; } - q_mvar_(shunt_id) = new_q; } void ShuntContainer::reconnect_connected_buses(std::vector & bus_status) const { diff --git a/src/powerflow_algorithm/BaseDCAlgo.tpp b/src/powerflow_algorithm/BaseDCAlgo.tpp index bb59466..7de831f 100644 --- a/src/powerflow_algorithm/BaseDCAlgo.tpp +++ b/src/powerflow_algorithm/BaseDCAlgo.tpp @@ -27,6 +27,7 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & // and for the slack bus both the magnitude and the angle are used. if(!is_linear_solver_valid()) { + // std::cout << "!is_linear_solver_valid()\n"; return false; } BaseAlgo::reset_timer(); @@ -104,6 +105,7 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & ErrorType status_init = _linear_solver.initialize(dcYbus_noslack_); if(status_init != ErrorType::NoError){ err_ = status_init; + // std::cout << "_linear_solver.initialize\n"; return false; } need_factorize_ = false; @@ -116,6 +118,7 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & if(error != ErrorType::NoError){ err_ = error; timer_total_nr_ += timer.duration(); + // std::cout << "_linear_solver.solve\n"; return false; } @@ -128,8 +131,10 @@ bool BaseDCAlgo::compute_pf(const Eigen::SparseMatrix & Vm_ = RealVect(); Va_ = RealVect(); timer_total_nr_ += timer.duration(); + // std::cout << "_linear_solver.allFinite" << Va_dc_without_slack.array().allFinite() <<", " << Va_dc_without_slack.lpNorm() <<"\n"; return false; } + // std::cout << "\t " << Va_dc_without_slack.lpNorm() << "\n"; #ifdef __COUT_TIMES std::cout << "\t dc solve: " << 1000. * timer_solve.duration() << "ms" << std::endl; diff --git a/src/powerflow_algorithm/BaseFDPFAlgo.tpp b/src/powerflow_algorithm/BaseFDPFAlgo.tpp index 28a7c1b..1c063b3 100644 --- a/src/powerflow_algorithm/BaseFDPFAlgo.tpp +++ b/src/powerflow_algorithm/BaseFDPFAlgo.tpp @@ -67,22 +67,35 @@ bool BaseFDPFAlgo::compute_pf(const Eigen::SparseMatrix grid_Bp; - Eigen::SparseMatrix grid_Bpp; - fillBp_Bpp(grid_Bp, grid_Bpp); - - // fill the solver matrices Bp and Bpp : - // Bp_ = Bp[array([pvpq]).T, pvpq].tocsc() # splu requires a CSC matrix - // Bpp_ = Bpp[array([pq]).T, pq].tocsc() - const auto n_pvpq = pvpq.size(); - std::vector pvpq_inv(V.size(), -1); - for(int inv_id=0; inv_id < n_pvpq; ++inv_id) pvpq_inv[pvpq(inv_id)] = inv_id; - std::vector pq_inv(V.size(), -1); - for(int inv_id=0; inv_id < n_pq; ++inv_id) pq_inv[pq(inv_id)] = inv_id; - fill_sparse_matrices(grid_Bp, grid_Bpp, pvpq_inv, pq_inv, n_pvpq, n_pq); + if(need_factorize_ || + _solver_control.need_reset_solver() || + _solver_control.has_dimension_changed() || + _solver_control.has_slack_participate_changed() || // the full "ybus without slack" has changed, everything needs to be recomputed_solver_control.ybus_change_sparsity_pattern() + _solver_control.ybus_change_sparsity_pattern() || + _solver_control.has_ybus_some_coeffs_zero() || + _solver_control.need_recompute_ybus() || + _solver_control.has_slack_participate_changed() || + _solver_control.has_pv_changed() || + _solver_control.has_pq_changed() + ) + { + // extract Bp and Bpp for the whole grid + Eigen::SparseMatrix grid_Bp; + Eigen::SparseMatrix grid_Bpp; + fillBp_Bpp(grid_Bp, grid_Bpp); + + // fill the solver matrices Bp_ and Bpp_ + // Bp_ = Bp[array([pvpq]).T, pvpq].tocsc() + // Bpp_ = Bpp[array([pq]).T, pq].tocsc() + std::vector pvpq_inv(V.size(), -1); + for(int inv_id=0; inv_id < n_pvpq; ++inv_id) pvpq_inv[pvpq(inv_id)] = inv_id; + std::vector pq_inv(V.size(), -1); + for(int inv_id=0; inv_id < n_pq; ++inv_id) pq_inv[pq(inv_id)] = inv_id; + fill_sparse_matrices(grid_Bp, grid_Bpp, pvpq_inv, pq_inv, n_pvpq, n_pq); + } V_ = V; // V = V0 Vm_ = V_.array().abs(); // Vm = abs(V) @@ -160,11 +173,11 @@ bool BaseFDPFAlgo::compute_pf(const Eigen::SparseMatrix void BaseFDPFAlgo::fill_sparse_matrices(const Eigen::SparseMatrix & grid_Bp, - const Eigen::SparseMatrix & grid_Bpp, - const std::vector & pvpq_inv, - const std::vector & pq_inv, - Eigen::Index n_pvpq, - Eigen::Index n_pq) + const Eigen::SparseMatrix & grid_Bpp, + const std::vector & pvpq_inv, + const std::vector & pq_inv, + Eigen::Index n_pvpq, + Eigen::Index n_pq) { /** Init Bp_ and Bpp_ such that: diff --git a/src/powerflow_algorithm/BaseNRAlgo.tpp b/src/powerflow_algorithm/BaseNRAlgo.tpp index 0977d7d..33ae20e 100644 --- a/src/powerflow_algorithm/BaseNRAlgo.tpp +++ b/src/powerflow_algorithm/BaseNRAlgo.tpp @@ -88,13 +88,27 @@ bool BaseNRAlgo::compute_pf(const Eigen::SparseMatrix & bool has_just_been_initialized = false; // to avoid a call to klu_refactor follow a call to klu_factor in the same loop // std::cout << "iter " << nr_iter_ << " dx(0): " << -F(0) << " dx(1): " << -F(1) << std::endl; // std::cout << "slack_absorbed " << slack_absorbed << std::endl; - value_map_.clear(); // TODO smarter solver: only needed if ybus has changed - // BaseNRAlgo::col_map_.clear(); // TODO smarter solver: only needed if ybus has changed - // BaseNRAlgo::row_map_.clear(); // TODO smarter solver: only needed if ybus has changed - dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed - dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed - // BaseNRAlgo::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed - // BaseNRAlgo::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed + if(need_factorize_ || + _solver_control.need_reset_solver() || + _solver_control.has_dimension_changed() || + _solver_control.has_slack_participate_changed() || // the full "ybus without slack" has changed, everything needs to be recomputed_solver_control.ybus_change_sparsity_pattern() + _solver_control.ybus_change_sparsity_pattern() || + _solver_control.has_ybus_some_coeffs_zero() || + _solver_control.need_recompute_ybus() || + _solver_control.has_slack_participate_changed() || + _solver_control.has_pv_changed() || + _solver_control.has_pq_changed() + ) + { + value_map_.clear(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::col_map_.clear(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::row_map_.clear(); // TODO smarter solver: only needed if ybus has changed + dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed + dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed + + } while ((!converged) & (nr_iter_ < max_iter)){ nr_iter_++; fill_jacobian_matrix(Ybus, V_, slack_bus_id, slack_weights, pq, pvpq, pq_inv, pvpq_inv); diff --git a/src/powerflow_algorithm/BaseNRSingleSlackAlgo.tpp b/src/powerflow_algorithm/BaseNRSingleSlackAlgo.tpp index 8c0a429..612f963 100644 --- a/src/powerflow_algorithm/BaseNRSingleSlackAlgo.tpp +++ b/src/powerflow_algorithm/BaseNRSingleSlackAlgo.tpp @@ -11,15 +11,15 @@ template bool BaseNRSingleSlackAlgo::compute_pf(const Eigen::SparseMatrix & Ybus, - CplxVect & V, - const CplxVect & Sbus, - const Eigen::VectorXi & slack_ids, - const RealVect & slack_weights, // unused here - const Eigen::VectorXi & pv, - const Eigen::VectorXi & pq, - int max_iter, - real_type tol - ) + CplxVect & V, + const CplxVect & Sbus, + const Eigen::VectorXi & slack_ids, + const RealVect & slack_weights, // unused here + const Eigen::VectorXi & pv, + const Eigen::VectorXi & pq, + int max_iter, + real_type tol + ) { /** This method uses the newton raphson algorithm to compute voltage angles and magnitudes at each bus @@ -75,12 +75,27 @@ bool BaseNRSingleSlackAlgo::compute_pf(const Eigen::SparseMatrix::my_i; // otherwise it does not compile - BaseNRAlgo::value_map_.clear(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed - BaseNRAlgo::dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed - BaseNRAlgo::dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed - // BaseNRAlgo::J_.setZero(); // TODO smarter solver: only needed if ybus has changed or pq changed or pv changed or ybus_some_coeffs_zero_ - // BaseNRAlgo::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed - // BaseNRAlgo::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed + if(BaseNRAlgo::need_factorize_ || + BaseNRAlgo::_solver_control.need_reset_solver() || + BaseNRAlgo::_solver_control.has_dimension_changed() || + BaseNRAlgo::_solver_control.has_slack_participate_changed() || // the full "ybus without slack" has changed, everything needs to be recomputed_solver_control.ybus_change_sparsity_pattern() + BaseNRAlgo::_solver_control.ybus_change_sparsity_pattern() || + BaseNRAlgo::_solver_control.has_ybus_some_coeffs_zero() || + BaseNRAlgo::_solver_control.need_recompute_ybus() || + // BaseNRAlgo:: _solver_control.has_slack_participate_changed() || + BaseNRAlgo::_solver_control.has_pv_changed() || + BaseNRAlgo::_solver_control.has_pq_changed() + ) + { + BaseNRAlgo::value_map_.clear(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::col_map_.clear(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::row_map_.clear(); // TODO smarter solver: only needed if ybus has changed + BaseNRAlgo::dS_dVm_.resize(0,0); // TODO smarter solver: only needed if ybus has changed + BaseNRAlgo::dS_dVa_.resize(0,0); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::dS_dVm_.setZero(); // TODO smarter solver: only needed if ybus has changed + // BaseNRAlgo::dS_dVa_.setZero(); // TODO smarter solver: only needed if ybus has changed + + } while ((!converged) & (BaseNRAlgo::nr_iter_ < max_iter)){ BaseNRAlgo::nr_iter_++; // std::cout << "\tnr_iter_ " << BaseNRAlgo::nr_iter_ << std::endl; From 2bf578767ff142d06214b3182f33201664565b72 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Mar 2024 09:20:14 +0100 Subject: [PATCH 19/42] fixing some issues in the CI --- lightsim2grid/lightSimBackend.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index f0d57c5..410d2da 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -568,7 +568,8 @@ def _aux_setup_right_after_grid_init(self): self._grid._max_nb_bus_per_sub = self.n_busbar_per_sub def _load_grid_pandapower(self, path=None, filename=None): - type(self.init_pp_backend).n_busbar_per_sub = self.n_busbar_per_sub + 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.can_output_theta = True # i can compute the "theta" and output it to grid2op @@ -1091,11 +1092,12 @@ def copy(self): "_timer_preproc", "_timer_postproc", "_timer_solver", "_my_kwargs", "supported_grid_format", "_turned_off_pv", "_dist_slack_non_renew", - "_loader_method", "_loader_kwargs" + "_loader_method", "_loader_kwargs", + "_missing_two_busbars_support_info", "n_busbar_per_sub" ] for attr_nm in li_regular_attr: if hasattr(self, attr_nm): - # this test is needed for backward compatibility with other grid2op version + # this test is needed for backward compatibility with older grid2op version setattr(res, attr_nm, copy.deepcopy(getattr(self, attr_nm))) # copy the numpy array @@ -1114,7 +1116,7 @@ def copy(self): ] for attr_nm in li_attr_npy: if hasattr(self, attr_nm): - # this test is needed for backward compatibility with other grid2op version + # this test is needed for backward compatibility with older grid2op version setattr(res, attr_nm, copy.deepcopy(getattr(self, attr_nm))) # copy class attribute for older grid2op version (did not use the class attribute) From ab0c89c65b49a7334f4a9b72fa08ea1ba7ea45be Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Mar 2024 15:30:56 +0100 Subject: [PATCH 20/42] some more fixes, and improved CI --- .circleci/config.yml | 157 +++++++++++++++---- .gitignore | 2 + CHANGELOG.rst | 8 + README.md | 5 +- lightsim2grid/gridmodel/from_pypowsybl.py | 59 +++++-- lightsim2grid/lightSimBackend.py | 96 +++++++++--- src/GridModel.h | 8 + src/element_container/DCLineContainer.cpp | 32 ++-- src/element_container/DCLineContainer.h | 2 +- src/element_container/GeneratorContainer.cpp | 50 +++--- src/element_container/GenericContainer.cpp | 32 ++-- src/element_container/LineContainer.cpp | 71 +++++---- src/element_container/LoadContainer.cpp | 15 +- src/element_container/ShuntContainer.cpp | 21 ++- src/element_container/TrafoContainer.cpp | 65 ++++---- 15 files changed, 434 insertions(+), 189 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4ea5c7c..5721da7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,6 +5,9 @@ orbs: executors: + gcc_13: + docker: + - image: gcc:13 gcc_12: docker: - image: gcc:12 @@ -20,6 +23,18 @@ executors: gcc_8: docker: - image: gcc:8 + clang18: + docker: + - image: silkeh/clang:18 + clang17: + docker: + - image: silkeh/clang:17 + clang16: + docker: + - image: silkeh/clang:16 + clang15: + docker: + - image: silkeh/clang:15 clang14: docker: - image: silkeh/clang:14 @@ -40,13 +55,30 @@ executors: - image: silkeh/clang:9 # no c++ 11, does not work jobs: + compile_gcc12: + executor: gcc_12 + resource_class: small + steps: + - checkout + - run: apt-get update && apt-get install python3-pip python3-full -y + - run: python3 -m pip install virtualenv + - run: python3 -m virtualenv venv_test + - run: + command: | + source venv_test/bin/activate + pip install -U grid2op + pip install -U pybind11 + git submodule init + git submodule update + make + CC=gcc python setup.py build + python -m pip install -U . compile_gcc11: executor: gcc_11 resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -64,8 +96,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -83,8 +114,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -102,8 +132,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -116,13 +145,12 @@ jobs: make CC=gcc python setup.py build python -m pip install -U . - compile_gcc12: - executor: gcc_12 + compile_gcc13: + executor: gcc_13 resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip python3-virtualenv -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full -y # - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -152,8 +180,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full git -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -170,8 +197,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip git -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full git -y - run: command: | git submodule init @@ -190,8 +216,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip git -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full git -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -209,8 +234,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip git -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full git -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -228,8 +252,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip git -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full git -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -247,8 +270,79 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip git -y - - run: python3 -m pip install --upgrade pip setuptools + - run: apt-get update && apt-get install python3-pip python3-full git -y + - run: python3 -m pip install virtualenv + - run: python3 -m virtualenv venv_test + - run: + command: | + source venv_test/bin/activate + pip install -U grid2op + pip install -U pybind11 + git submodule init + git submodule update + make + CC=clang python setup.py build + CC=clang python -m pip install -U . + compile_clang15: + executor: clang15 + resource_class: small + steps: + - checkout + - run: apt-get update && apt-get install python3-pip python3-full git -y + - run: python3 -m pip install virtualenv + - run: python3 -m virtualenv venv_test + - run: + command: | + source venv_test/bin/activate + pip install -U grid2op + pip install -U pybind11 + git submodule init + git submodule update + make + CC=clang python setup.py build + CC=clang python -m pip install -U . + compile_clang16: + executor: clang16 + resource_class: small + steps: + - checkout + - run: apt-get update && apt-get install python3-pip python3-full git -y + - run: python3 -m pip install virtualenv + - run: python3 -m virtualenv venv_test + - run: + command: | + source venv_test/bin/activate + pip install -U grid2op + pip install -U pybind11 + git submodule init + git submodule update + make + CC=clang python setup.py build + CC=clang python -m pip install -U . + compile_clang17: + executor: clang17 + resource_class: small + steps: + - checkout + - run: apt-get update && apt-get install python3-pip python3-full git -y + - run: python3 -m pip install virtualenv + - run: python3 -m virtualenv venv_test + - run: + command: | + source venv_test/bin/activate + pip install -U grid2op + pip install -U pybind11 + git submodule init + git submodule update + make + CC=clang python setup.py build + CC=clang python -m pip install -U . + compile_clang18: + executor: clang18 + resource_class: small + steps: + - checkout + - run: apt-get update && apt-get install python3-pip python3-full git -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -279,7 +373,7 @@ jobs: size: medium # ("medium" "large" "xlarge" "2xlarge") steps: - checkout - - run: python3 -m pip install --upgrade pip setuptools + - run: py -m pip install --upgrade pip setuptools - run: py -m pip install virtualenv - run: py -m virtualenv venv_test - run: @@ -309,11 +403,16 @@ workflows: compile: jobs: - compile_gcc8 - - compile_gcc10 - - compile_gcc11 + # - compile_gcc10 + # - compile_gcc11 - compile_gcc12 + - compile_gcc13 # - compile_clang10 # does not work I don't know why, too lazy to check - compile_clang11 - - compile_clang13 - - compile_clang14 + # - compile_clang13 + # - compile_clang14 + # - compile_clang15 + # - compile_clang16 + - compile_clang17 + - compile_clang18 - compile_windows diff --git a/.gitignore b/.gitignore index e4e3737..5272312 100644 --- a/.gitignore +++ b/.gitignore @@ -278,3 +278,5 @@ lightsim2grid/tests/_grid2op_for_test/ bug_sparselu bug_sparselu_eigen.cpp test_segfault.sh +nohup.out +test_rte/ \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2fa05e1..a02c84e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -39,6 +39,8 @@ Change Log - [FIXED] a bug where copying a lightsim2grid `GridModel` did not fully copy it - [FIXED] a bug in the "topo_vect" comprehension cpp side (sometimes some buses might not be activated / deactivated correctly) +- [FIXED] a bug when reading a grid initialize from pypowsybl (trafo names where put in place + of shunt names) - [FIXED] read the docs was broken - [ADDED] sets of methods to extract the main component of a grid and perform powerflow only on this one. @@ -49,11 +51,17 @@ Change Log - [ADDED] a timer to get the time spent in the gridmodel for the powerflow (env.backend.timer_gridmodel_xx_pf) which also include the time - [ADDED] support for more than 2 busbars per substation (requires grid2op >= 1.10.0) +- [ADDED] possibility to retrieve the bus id of the original iidm when initializing from pypowsybl + (`return_sub_id` kwargs). This is a "beta" feature and will be adressed in a better way + in a near future. +- [ADDED] possibility to continue the grid2op 'step' when the solver converges but a load or a + generator is disconnected from the grid. - [IMPROVED] now performing the new grid2op `create_test_suite` - [IMPROVED] now lightsim2grid properly throw `BackendError` - [IMPROVED] clean ce cpp side by refactoring: making clearer the difference (linear) solver vs powerflow algorithm and move same type of files in the same directory. This change does not really affect python side at the moment (but will in future versions) +- [IMPROVED] CI to test on gcc 13 and clang 18 (latest versions to date) [0.7.5] 2023-10-05 -------------------- diff --git a/README.md b/README.md index 303ef73..cf1c078 100644 --- a/README.md +++ b/README.md @@ -268,9 +268,10 @@ cd .. Some tests are performed automatically on standard platform each time modifications are made in the lightsim2grid code. -These tests include, for now, compilation on gcc (version 8, 10, 11 and 12) and clang (version 11, 13 and 14). +These tests include, for now, compilation on gcc (version 8, 12 and 13) and clang (version 11, 16 and 17). -**NB** Intermediate versions of clang and gcc (*eg* gcc 9 or clang 12) are not tested regularly, but lightsim2grid used to work on these. We suppose that if it works on *eg* clang 10 and clang 14 then it compiles also on all intermediate versions. +**NB** Intermediate versions of clang and gcc (*eg* gcc 9 or clang 12) are not tested regularly, but lightsim2grid used to work on these. +We suppose that if it works on *eg* clang 10 and clang 14 then it compiles also on all intermediate versions. **NB** Package might work (we never tested it) on earlier version of these compilers. The only "real" requirement for lightsim2grid is to have a compiler supporting c++11 diff --git a/lightsim2grid/gridmodel/from_pypowsybl.py b/lightsim2grid/gridmodel/from_pypowsybl.py index ef7a856..6501ead 100644 --- a/lightsim2grid/gridmodel/from_pypowsybl.py +++ b/lightsim2grid/gridmodel/from_pypowsybl.py @@ -1,4 +1,4 @@ -# Copyright (c) 2023, RTE (https://www.rte-france.com) +# Copyright (c) 2023-2024, RTE (https://www.rte-france.com) # See AUTHORS.txt # This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. # If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -6,7 +6,9 @@ # SPDX-License-Identifier: MPL-2.0 # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. +import warnings import numpy as np +import pandas as pd import pypowsybl as pypo from lightsim2grid_cpp import GridModel @@ -39,7 +41,8 @@ def init(net : pypo.network, sn_mva = 100., sort_index=True, f_hz = 50., # unused - only_main_component=True): + only_main_component=True, + return_sub_id=False): model = GridModel() # model.set_f_hz(f_hz) @@ -66,7 +69,7 @@ def init(net : pypo.network, 0, 0 # unused ) model._orig_to_ls = 1 * bus_df_orig["bus_id"].values - + # do the generators if sort_index: df_gen = net.get_generators().sort_index() @@ -148,7 +151,7 @@ def init(net : pypo.network, for line_id, (is_or_disc, is_ex_disc) in enumerate(zip(lor_disco, lex_disco)): if is_or_disc or is_ex_disc: model.deactivate_powerline(line_id) - model.set_line_names(df_line.index) + model.set_line_names(df_line.index) # for trafo if sort_index: @@ -201,19 +204,23 @@ def init(net : pypo.network, for shunt_id, disco in enumerate(sh_disco): if disco: model.deactivate_shunt(shunt_id) - model.set_shunt_names(df_trafo.index) + model.set_shunt_names(df_shunt.index) # for hvdc (TODO not tested yet) - df_dc = net.get_hvdc_lines().sort_index() - df_sations = net.get_vsc_converter_stations().sort_index() + if sort_index: + df_dc = net.get_hvdc_lines().sort_index() + df_sations = net.get_vsc_converter_stations().sort_index() + else: + df_dc = net.get_hvdc_lines() + df_sations = net.get_vsc_converter_stations() # 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 - bus_from_id, hvdc_from_disco = _aux_get_bus(bus_df, df_sations.loc[df_dc["converter_station1_id"].values]) - bus_to_id, hvdc_to_disco = _aux_get_bus(bus_df, df_sations.loc[df_dc["converter_station2_id"].values]) + hvdc_bus_from_id, hvdc_from_disco = _aux_get_bus(bus_df, df_sations.loc[df_dc["converter_station1_id"].values]) + hvdc_bus_to_id, hvdc_to_disco = _aux_get_bus(bus_df, df_sations.loc[df_dc["converter_station2_id"].values]) loss_percent = np.zeros(df_dc.shape[0]) # TODO loss_mw = np.zeros(df_dc.shape[0]) # TODO - model.init_dclines(bus_from_id, - bus_to_id, + model.init_dclines(hvdc_bus_from_id, + hvdc_bus_to_id, df_dc["target_p"].values, loss_percent, loss_mw, @@ -270,10 +277,34 @@ def init(net : pypo.network, # TODO checks # no 3windings trafo and other exotic stuff if net.get_phase_tap_changers().shape[0] > 0: - pass - # raise RuntimeError("Impossible currently to init a grid with tap changers at the moment.") + warnings.warn("There are tap changers in the iidm grid which are not taken " + "into account in the lightsim2grid at the moment. " + "NB: lightsim2grid gridmodel can handle tap changer, it is just not " + "handled by the 'from_pypowsybl` function at the moment.") # and now deactivate all elements and nodes not in the main component if only_main_component: model.consider_only_main_component() - return model + if not return_sub_id: + # for backward compatibility + return model + else: + # voltage_level_id is kind of what I call "substation" in grid2op + vl_unique = bus_df["voltage_level_id"].unique() + sub_df = pd.DataFrame(index=np.sort(vl_unique), data={"sub_id": np.arange(vl_unique.size)}) + buses_sub_id = pd.merge(left=bus_df, right=sub_df, how="left", left_on="voltage_level_id", right_index=True)[["bus_id", "sub_id"]] + gen_sub = pd.merge(left=df_gen, right=sub_df, how="left", left_on="voltage_level_id", right_index=True)[["sub_id"]] + load_sub = pd.merge(left=df_load, right=sub_df, how="left", left_on="voltage_level_id", right_index=True)[["sub_id"]] + lor_sub = pd.merge(left=df_line, right=sub_df, how="left", left_on="voltage_level1_id", right_index=True)[["sub_id"]] + lex_sub = pd.merge(left=df_line, right=sub_df, how="left", left_on="voltage_level2_id", right_index=True)[["sub_id"]] + tor_sub = pd.merge(left=df_trafo, right=sub_df, how="left", left_on="voltage_level1_id", right_index=True)[["sub_id"]] + tex_sub = pd.merge(left=df_trafo, right=sub_df, how="left", left_on="voltage_level2_id", right_index=True)[["sub_id"]] + batt_sub = pd.merge(left=df_batt, right=sub_df, how="left", left_on="voltage_level_id", right_index=True)[["sub_id"]] + sh_sub = pd.merge(left=df_shunt, right=sub_df, how="left", left_on="voltage_level_id", right_index=True)[["sub_id"]] + hvdc_vl_info = pd.DataFrame(index=df_dc.index, + data={"voltage_level1_id": df_sations.loc[df_dc["converter_station1_id"].values]["voltage_level_id"].values, + "voltage_level2_id": df_sations.loc[df_dc["converter_station2_id"].values]["voltage_level_id"].values + }) + hvdc_sub_from_id = pd.merge(left=hvdc_vl_info, right=sub_df, how="left", left_on="voltage_level1_id", right_index=True)[["sub_id"]] + hvdc_sub_to_id = pd.merge(left=hvdc_vl_info, right=sub_df, how="left", left_on="voltage_level2_id", right_index=True)[["sub_id"]] + return model, (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) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 410d2da..f91d869 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -10,6 +10,7 @@ from typing import Optional, Union import warnings import numpy as np +import pandas as pd import time from grid2op.Action import CompleteAction @@ -49,6 +50,8 @@ def __init__(self, use_static_gen: bool=False, # add the static generators as generator gri2dop side loader_method: Literal["pandapower", "pypowsybl"] = "pandapower", loader_kwargs : Optional[dict] = None, + stop_if_load_disco : Optional[bool] = True, + stop_if_gen_disco : Optional[bool] = True, ): try: # for grid2Op >= 1.7.1 @@ -62,7 +65,10 @@ def __init__(self, dist_slack_non_renew=dist_slack_non_renew, use_static_gen=use_static_gen, loader_method=loader_method, - loader_kwargs=loader_kwargs) + loader_kwargs=loader_kwargs, + stop_if_load_disco=stop_if_load_disco, + stop_if_gen_disco=stop_if_gen_disco, + ) except TypeError as exc_: warnings.warn("Please use grid2op >= 1.7.1: with older grid2op versions, " "you cannot set max_iter, tol nor solver_type arguments.") @@ -79,6 +85,16 @@ def __init__(self, self._loader_method = loader_method self._loader_kwargs = loader_kwargs + #: .. versionadded:: 0.7.6 + #: if set to `True` (default) then the backend will raise a + #: BackendError in case of disconnected load + self._stop_if_load_disco = stop_if_load_disco + + #: .. versionadded:: 0.7.6 + #: if set to `True` (default) then the backend will raise a + #: BackendError in case of disconnected generator + self._stop_if_gen_disco = stop_if_gen_disco + if loader_method == "pandapower": self.supported_grid_format = ("json", ) # new in 1.9.6 elif loader_method == "pypowsybl": @@ -452,7 +468,14 @@ def _aux_pypowsybl_init_substations(self, loader_kwargs): for i in range(self.n_busbar_per_sub)] ) self._grid._orig_to_ls = new_orig_to_ls - + + def _get_subid_from_buses_legacy(self, buses_sub_id, el_sub_df): + # buses_sub_id is the first element as returned by from_pypowsybl / init function + # el_sub_df is an element dataframe returned by the same function + tmp = pd.merge(el_sub_df.reset_index(), buses_sub_id, how="left", right_on="sub_id", left_on="sub_id") + res = tmp.drop_duplicates(subset='id').set_index("id").sort_index()["bus_id"].values + return res + def _load_grid_pypowsybl(self, path=None, filename=None): from lightsim2grid.gridmodel.from_pypowsybl import init as init_pypow import pypowsybl.network as pypow_net @@ -470,7 +493,8 @@ def _load_grid_pypowsybl(self, path=None, filename=None): 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=gen_slack_id, sort_index=True) + self._grid, subs_id = init_pypow(grid_tmp, gen_slack_id=gen_slack_id, sort_index=True, return_sub_id=True) + (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 self.__nb_bus_before = len(self._grid.get_bus_vn_kv()) self._aux_pypowsybl_init_substations(loader_kwargs) self._aux_setup_right_after_grid_init() @@ -492,18 +516,28 @@ def _load_grid_pypowsybl(self, path=None, filename=None): self.name_sub = ["sub_{}".format(i) for i, _ in enumerate(df.iterrows())] if not from_sub: - self.load_to_subid = np.array([el.bus_id for el in self._grid.get_loads()], dtype=dt_int) - self.gen_to_subid = np.array([el.bus_id for el in self._grid.get_generators()], dtype=dt_int) - self.line_or_to_subid = np.array([el.bus_or_id for el in self._grid.get_lines()] + - [el.bus_hv_id for el in self._grid.get_trafos()], - dtype=dt_int) - self.line_ex_to_subid = np.array([el.bus_ex_id for el in self._grid.get_lines()] + - [el.bus_lv_id for el in self._grid.get_trafos()], - dtype=dt_int) - self.storage_to_subid = np.array([el.bus_id for el in self._grid.get_storages()], dtype=dt_int) - self.shunt_to_subid = np.array([el.bus_id for el in self._grid.get_shunts()], dtype=dt_int) + # consider that each "bus" in the powsybl grid is a substation + # this is the "standard" behaviour for IEEE grid in grid2op + # but can be considered "legacy" behaviour for more realistic grid + this_load_sub = self._get_subid_from_buses_legacy(buses_sub_id, load_sub) + this_gen_sub = self._get_subid_from_buses_legacy(buses_sub_id, gen_sub) + this_lor_sub = self._get_subid_from_buses_legacy(buses_sub_id, lor_sub) + this_tor_sub = self._get_subid_from_buses_legacy(buses_sub_id, tor_sub) + this_lex_sub = self._get_subid_from_buses_legacy(buses_sub_id, lex_sub) + this_tex_sub = self._get_subid_from_buses_legacy(buses_sub_id, tex_sub) + this_batt_sub = self._get_subid_from_buses_legacy(buses_sub_id, batt_sub) + this_sh_sub = self._get_subid_from_buses_legacy(buses_sub_id, sh_sub) + + self.load_to_subid = np.array(this_load_sub, dtype=dt_int) + self.gen_to_subid = np.array(this_gen_sub, dtype=dt_int) + self.line_or_to_subid = np.concatenate((this_lor_sub, this_tor_sub)).astype(dt_int) + self.line_ex_to_subid = np.concatenate((this_lex_sub, this_tex_sub)).astype(dt_int) + self.storage_to_subid = np.array(this_batt_sub, dtype=dt_int) + self.shunt_to_subid = np.array(this_sh_sub, dtype=dt_int) else: # TODO get back the sub id from the grid_tmp.get_substations() + # need to work on that grid2op side: different make sure the labelling of the buses are correct ! + (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 raise NotImplementedError("Today the only supported behaviour is to consider the 'buses' of the powsybl grid " "are the 'substation' of this backend. " "This will change in the future, but in the meantime please add " @@ -511,12 +545,23 @@ def _load_grid_pypowsybl(self, path=None, filename=None): "a lightsim2grid grid.") # the names - self.name_load = np.array([f"load_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_loads())]) - self.name_gen = np.array([f"gen_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_generators())]) - self.name_line = np.array([f"{el.bus_or_id}_{el.bus_ex_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_lines())] + - [f"{el.bus_hv_id}_{el.bus_lv_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_trafos())]) - self.name_storage = np.array([f"storage_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_storages())]) - self.name_shunt = np.array([f"shunt_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_shunts())]) + use_grid2op_default_names = True + if "use_grid2op_default_names" in loader_kwargs and not loader_kwargs["use_grid2op_default_names"]: + use_grid2op_default_names = False + + if use_grid2op_default_names: + self.name_load = np.array([f"load_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_loads())]) + self.name_gen = np.array([f"gen_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_generators())]) + self.name_line = np.array([f"{el.bus_or_id}_{el.bus_ex_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_lines())] + + [f"{el.bus_hv_id}_{el.bus_lv_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_trafos())]) + self.name_storage = np.array([f"storage_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_storages())]) + self.name_shunt = np.array([f"shunt_{el.bus_id}_{id_obj}" for id_obj, el in enumerate(self._grid.get_shunts())]) + else: + self.name_load = np.array(load_sub.index) + self.name_gen = np.array(gen_sub.index) + self.name_line = np.concatenate((lor_sub.index, tor_sub.index)) + self.name_storage = np.array(batt_sub.index) + self.name_shunt = np.array(sh_sub.index) # complete the other vectors self._compute_pos_big_topo() @@ -537,8 +582,8 @@ def _load_grid_pypowsybl(self, path=None, filename=None): max_not_too_max = (np.finfo(dt_float).max * 0.5 - 1.) self.thermal_limit_a = max_not_too_max * np.ones(self.n_line, dtype=dt_float) bus_vn_kv = np.array(self._grid.get_bus_vn_kv()) - shunt_bus_id = np.array([el.bus_id for el in self._grid.get_shunts()]) - self._sh_vnkv = bus_vn_kv[shunt_bus_id] + # shunt_bus_id = np.array([el.bus_id for el in self._grid.get_shunts()]) + self._sh_vnkv = bus_vn_kv[self.shunt_to_subid] self._aux_finish_setup_after_reading() def _aux_setup_right_after_grid_init(self): @@ -567,6 +612,8 @@ def _aux_setup_right_after_grid_init(self): # grid2op version >= 1.10.0 then we use this self._grid._max_nb_bus_per_sub = self.n_busbar_per_sub + self._grid.tell_solver_need_reset() + 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 @@ -979,12 +1026,12 @@ def runpf(self, is_dc=False): self.next_prod_p[:] = self.prod_p - if np.any(~np.isfinite(self.load_v)) or np.any(self.load_v <= 0.): + if self._stop_if_load_disco and ((~np.isfinite(self.load_v)).any() or (self.load_v <= 0.).any()): disco = (~np.isfinite(self.load_v)) | (self.load_v <= 0.) load_disco = np.where(disco)[0] self._timer_postproc += time.perf_counter() - beg_postroc raise BackendError(f"At least one load is disconnected (check loads {load_disco})") - if np.any(~np.isfinite(self.prod_v)) or np.any(self.prod_v <= 0.): + if self._stop_if_gen_disco and ((~np.isfinite(self.prod_v)).any() or (self.prod_v <= 0.).any()): disco = (~np.isfinite(self.prod_v)) | (self.prod_v <= 0.) gen_disco = np.where(disco)[0] self._timer_postproc += time.perf_counter() - beg_postroc @@ -1093,7 +1140,8 @@ def copy(self): "_my_kwargs", "supported_grid_format", "_turned_off_pv", "_dist_slack_non_renew", "_loader_method", "_loader_kwargs", - "_missing_two_busbars_support_info", "n_busbar_per_sub" + "_missing_two_busbars_support_info", "n_busbar_per_sub", + "_use_static_gen", "_stop_if_load_disco", "_stop_if_gen_disco" ] for attr_nm in li_regular_attr: if hasattr(self, attr_nm): diff --git a/src/GridModel.h b/src/GridModel.h index 4fefb2f..7a4b69c 100644 --- a/src/GridModel.h +++ b/src/GridModel.h @@ -352,27 +352,35 @@ class GridModel : public GenericContainer const std::vector & get_bus_status() const {return bus_status_;} void set_line_names(const std::vector & names){ + GenericContainer::check_size(names, powerlines_.nb(), "set_line_names"); powerlines_.set_names(names); } void set_dcline_names(const std::vector & names){ + GenericContainer::check_size(names, dc_lines_.nb(), "set_dcline_names"); dc_lines_.set_names(names); } void set_trafo_names(const std::vector & names){ + GenericContainer::check_size(names, trafos_.nb(), "set_trafo_names"); trafos_.set_names(names); } void set_gen_names(const std::vector & names){ + GenericContainer::check_size(names, generators_.nb(), "set_gen_names"); generators_.set_names(names); } void set_load_names(const std::vector & names){ + GenericContainer::check_size(names, loads_.nb(), "set_load_names"); loads_.set_names(names); } void set_storage_names(const std::vector & names){ + GenericContainer::check_size(names, storages_.nb(), "set_storage_names"); storages_.set_names(names); } void set_sgen_names(const std::vector & names){ + GenericContainer::check_size(names, sgens_.nb(), "set_sgen_names"); sgens_.set_names(names); } void set_shunt_names(const std::vector & names){ + GenericContainer::check_size(names, shunts_.nb(), "set_shunt_names"); shunts_.set_names(names); } diff --git a/src/element_container/DCLineContainer.cpp b/src/element_container/DCLineContainer.cpp index 716229b..01a683b 100644 --- a/src/element_container/DCLineContainer.cpp +++ b/src/element_container/DCLineContainer.cpp @@ -15,13 +15,13 @@ DCLineContainer::StateRes DCLineContainer::get_state() const { std::vector loss_percent(loss_percent_.begin(), loss_percent_.end()); std::vector loss_mw(loss_mw_.begin(), loss_mw_.end()); - std::vector status = status_; + std::vector status = status_; DCLineContainer::StateRes res(names_, - from_gen_.get_state(), - to_gen_.get_state(), - loss_percent, - loss_mw, - status); + from_gen_.get_state(), + to_gen_.get_state(), + loss_percent, + loss_mw, + status); return res; } @@ -39,16 +39,16 @@ void DCLineContainer::set_state(DCLineContainer::StateRes & my_state){ } void DCLineContainer::init(const Eigen::VectorXi & branch_from_id, - const Eigen::VectorXi & branch_to_id, - const RealVect & p_mw, - const RealVect & loss_percent, - const RealVect & loss_mw, - const RealVect & vm_or_pu, - const RealVect & vm_ex_pu, - const RealVect & min_q_or, - const RealVect & max_q_or, - const RealVect & min_q_ex, - const RealVect & max_q_ex){ + const Eigen::VectorXi & branch_to_id, + const RealVect & p_mw, + const RealVect & loss_percent, + const RealVect & loss_mw, + const RealVect & vm_or_pu, + const RealVect & vm_ex_pu, + const RealVect & min_q_or, + const RealVect & max_q_or, + const RealVect & min_q_ex, + const RealVect & max_q_ex){ loss_percent_ = loss_percent; loss_mw_ = loss_mw; status_ = std::vector(branch_from_id.size(), true); diff --git a/src/element_container/DCLineContainer.h b/src/element_container/DCLineContainer.h index 5ee14dc..a64fb1f 100644 --- a/src/element_container/DCLineContainer.h +++ b/src/element_container/DCLineContainer.h @@ -186,7 +186,7 @@ class DCLineContainer : public GenericContainer } // for buses only connected through dc line, i don't add them // they are not in the same "connected component" - virtual void get_graph(std::vector > & res) const {}; + virtual void get_graph(std::vector > & res) const {}; virtual void disconnect_if_not_in_main_component(std::vector & busbar_in_main_component); virtual void nb_line_end(std::vector & res) const; virtual void update_bus_status(std::vector & bus_status) const { diff --git a/src/element_container/GeneratorContainer.cpp b/src/element_container/GeneratorContainer.cpp index b580c40..2515997 100644 --- a/src/element_container/GeneratorContainer.cpp +++ b/src/element_container/GeneratorContainer.cpp @@ -11,11 +11,18 @@ #include void GeneratorContainer::init(const RealVect & generators_p, - const RealVect & generators_v, - const RealVect & generators_min_q, - const RealVect & generators_max_q, - const Eigen::VectorXi & generators_bus_id) + const RealVect & generators_v, + const RealVect & generators_min_q, + const RealVect & generators_max_q, + const Eigen::VectorXi & generators_bus_id) { + int size = static_cast(generators_p.size()); + GenericContainer::check_size(generators_p, size, "generators_p"); + GenericContainer::check_size(generators_v, size, "generators_v"); + GenericContainer::check_size(generators_min_q, size, "generators_min_q"); + GenericContainer::check_size(generators_max_q, size, "generators_max_q"); + GenericContainer::check_size(generators_bus_id, size, "generators_bus_id"); + p_mw_ = generators_p; vm_pu_ = generators_v; bus_id_ = generators_bus_id; @@ -50,15 +57,18 @@ void GeneratorContainer::init(const RealVect & generators_p, } void GeneratorContainer::init_full(const RealVect & generators_p, - const RealVect & generators_v, - const RealVect & generators_q, - const std::vector & voltage_regulator_on, - const RealVect & generators_min_q, - const RealVect & generators_max_q, - const Eigen::VectorXi & generators_bus_id - ) + const RealVect & generators_v, + const RealVect & generators_q, + const std::vector & voltage_regulator_on, + const RealVect & generators_min_q, + const RealVect & generators_max_q, + const Eigen::VectorXi & generators_bus_id + ) { init(generators_p, generators_v, generators_min_q, generators_max_q, generators_bus_id); + int size = static_cast(generators_p.size()); + GenericContainer::check_size(generators_q, size, "generators_q"); + GenericContainer::check_size(voltage_regulator_on, size, "voltage_regulator_on"); voltage_regulator_on_ = voltage_regulator_on; q_mvar_ = generators_q; } @@ -353,7 +363,7 @@ Eigen::VectorXi GeneratorContainer::get_slack_bus_id() const{ } void GeneratorContainer::set_p_slack(const RealVect& node_mismatch, - const std::vector & id_grid_to_solver) + const std::vector & id_grid_to_solver) { if(bus_slack_weight_.size() == 0){ // TODO DEBUG MODE: perform this check only in debug mode @@ -377,9 +387,9 @@ void GeneratorContainer::set_p_slack(const RealVect& node_mismatch, } void GeneratorContainer::init_q_vector(int nb_bus, - Eigen::VectorXi & total_gen_per_bus, - RealVect & total_q_min_per_bus, - RealVect & total_q_max_per_bus) const // delta_q_per_gen_) // total number of bus on the grid + Eigen::VectorXi & total_gen_per_bus, + RealVect & total_q_min_per_bus, + RealVect & total_q_max_per_bus) const { const int nb_gen = nb(); for(int gen_id = 0; gen_id < nb_gen; ++gen_id) @@ -397,11 +407,11 @@ void GeneratorContainer::init_q_vector(int nb_bus, } void GeneratorContainer::set_q(const RealVect & reactive_mismatch, - const std::vector & id_grid_to_solver, - bool ac, - const Eigen::VectorXi & total_gen_per_bus, - const RealVect & total_q_min_per_bus, - const RealVect & total_q_max_per_bus) + const std::vector & id_grid_to_solver, + bool ac, + const Eigen::VectorXi & total_gen_per_bus, + const RealVect & total_q_min_per_bus, + const RealVect & total_q_max_per_bus) { const int nb_gen = nb(); res_q_ = RealVect::Constant(nb_gen, 0.); diff --git a/src/element_container/GenericContainer.cpp b/src/element_container/GenericContainer.cpp index 6daa4fe..cf6dc51 100644 --- a/src/element_container/GenericContainer.cpp +++ b/src/element_container/GenericContainer.cpp @@ -27,14 +27,17 @@ void GenericContainer::_get_amps(RealVect & a, const RealVect & p, const RealVec } a = p2q2.array() * _1_sqrt_3 / v_tmp.array(); } + void GenericContainer::_reactivate(int el_id, std::vector & status){ bool val = status.at(el_id); status.at(el_id) = true; //TODO why it's needed to do that again } + void GenericContainer::_deactivate(int el_id, std::vector & status){ bool val = status.at(el_id); status.at(el_id) = false; //TODO why it's needed to do that again } + void GenericContainer::_change_bus(int el_id, int new_bus_me_id, Eigen::VectorXi & el_bus_ids, SolverControl & solver_control, int nb_bus){ // bus id here "me_id" and NOT "solver_id" // throw error: object id does not exist @@ -105,13 +108,14 @@ int GenericContainer::_get_bus(int el_id, const std::vector & status_, con } void GenericContainer::v_kv_from_vpu(const Eigen::Ref & Va, - const Eigen::Ref & Vm, - const std::vector & status, - int nb_element, - const Eigen::VectorXi & bus_me_id, - const std::vector & id_grid_to_solver, - const RealVect & bus_vn_kv, - RealVect & v){ + const Eigen::Ref & Vm, + const std::vector & status, + int nb_element, + const Eigen::VectorXi & bus_me_id, + const std::vector & id_grid_to_solver, + const RealVect & bus_vn_kv, + RealVect & v) +{ v = RealVect::Constant(nb_element, -1.0); for(int el_id = 0; el_id < nb_element; ++el_id){ // if the element is disconnected, i leave it like that @@ -133,13 +137,13 @@ void GenericContainer::v_kv_from_vpu(const Eigen::Ref & Va, void GenericContainer::v_deg_from_va(const Eigen::Ref & Va, - const Eigen::Ref & Vm, - const std::vector & status, - int nb_element, - const Eigen::VectorXi & bus_me_id, - const std::vector & id_grid_to_solver, - const RealVect & bus_vn_kv, - RealVect & theta){ + const Eigen::Ref & Vm, + const std::vector & status, + int nb_element, + const Eigen::VectorXi & bus_me_id, + const std::vector & id_grid_to_solver, + const RealVect & bus_vn_kv, + RealVect & theta){ theta = RealVect::Constant(nb_element, 0.0); for(int el_id = 0; el_id < nb_element; ++el_id){ // if the element is disconnected, i leave it like that diff --git a/src/element_container/LineContainer.cpp b/src/element_container/LineContainer.cpp index 610aa45..efeac7d 100644 --- a/src/element_container/LineContainer.cpp +++ b/src/element_container/LineContainer.cpp @@ -11,11 +11,11 @@ #include void LineContainer::init(const RealVect & branch_r, - const RealVect & branch_x, - const CplxVect & branch_h, - const Eigen::VectorXi & branch_from_id, - const Eigen::VectorXi & branch_to_id - ) + const RealVect & branch_x, + const CplxVect & branch_h, + const Eigen::VectorXi & branch_from_id, + const Eigen::VectorXi & branch_to_id + ) { /** This method initialize the Ybus matrix from the branch matrix. @@ -29,6 +29,13 @@ void LineContainer::init(const RealVect & branch_r, //TODO consistency with trafo: have a converter methods to convert this value into pu, and store the pu // in this method + int size = static_cast(branch_r.size()); + GenericContainer::check_size(branch_r, size, "branch_r"); + GenericContainer::check_size(branch_x, size, "branch_x"); + GenericContainer::check_size(branch_h, size, "branch_h"); + GenericContainer::check_size(branch_from_id, size, "branch_from_id"); + GenericContainer::check_size(branch_to_id, size, "branch_to_id"); + bus_or_id_ = branch_from_id; bus_ex_id_ = branch_to_id; powerlines_h_or_ = 0.5 * branch_h; @@ -40,12 +47,12 @@ void LineContainer::init(const RealVect & branch_r, } void LineContainer::init(const RealVect & branch_r, - const RealVect & branch_x, - const CplxVect & branch_h_or, - const CplxVect & branch_h_ex, - const Eigen::VectorXi & branch_from_id, - const Eigen::VectorXi & branch_to_id - ) + const RealVect & branch_x, + const CplxVect & branch_h_or, + const CplxVect & branch_h_ex, + const Eigen::VectorXi & branch_from_id, + const Eigen::VectorXi & branch_to_id + ) { /** This method initialize the Ybus matrix from the branch matrix. @@ -59,6 +66,14 @@ void LineContainer::init(const RealVect & branch_r, //TODO consistency with trafo: have a converter methods to convert this value into pu, and store the pu // in this method + int size = static_cast(branch_r.size()); + GenericContainer::check_size(branch_r, size, "branch_r"); + GenericContainer::check_size(branch_x, size, "branch_x"); + GenericContainer::check_size(branch_h_or, size, "branch_h_or"); + GenericContainer::check_size(branch_h_ex, size, "branch_h_ex"); + GenericContainer::check_size(branch_from_id, size, "branch_from_id"); + GenericContainer::check_size(branch_to_id, size, "branch_to_id"); + bus_or_id_ = branch_from_id; bus_ex_id_ = branch_to_id; powerlines_h_or_ = branch_h_or; @@ -150,9 +165,9 @@ void LineContainer::fillYbus_spmat(Eigen::SparseMatrix & res, bool ac } void LineContainer::fillYbus(std::vector > & res, - bool ac, - const std::vector & id_grid_to_solver, - real_type sn_mva) const + bool ac, + const std::vector & id_grid_to_solver, + real_type sn_mva) const { // fill the matrix //TODO template here instead of "if" for ac / dc @@ -206,10 +221,10 @@ void LineContainer::fillYbus(std::vector > & res, } void LineContainer::fillBp_Bpp(std::vector > & Bp, - std::vector > & Bpp, - const std::vector & id_grid_to_solver, - real_type sn_mva, - FDPFMethod xb_or_bx) const + std::vector > & Bpp, + const std::vector & id_grid_to_solver, + real_type sn_mva, + FDPFMethod xb_or_bx) const { // For Bp @@ -291,10 +306,10 @@ void LineContainer::fillBp_Bpp(std::vector > & Bp, void LineContainer::fillBf_for_PTDF(std::vector > & Bf, - const std::vector & id_grid_to_solver, - real_type sn_mva, - int nb_powerline, - bool transpose) const + const std::vector & id_grid_to_solver, + real_type sn_mva, + int nb_powerline, + bool transpose) const { const Eigen::Index nb_line = powerlines_r_.size(); @@ -351,12 +366,12 @@ void LineContainer::reset_results() } void LineContainer::compute_results(const Eigen::Ref & Va, - const Eigen::Ref & Vm, - const Eigen::Ref & V, - const std::vector & id_grid_to_solver, - const RealVect & bus_vn_kv, - real_type sn_mva, - bool ac) + const Eigen::Ref & Vm, + const Eigen::Ref & V, + const std::vector & id_grid_to_solver, + const RealVect & bus_vn_kv, + real_type sn_mva, + bool ac) { // it needs to be initialized at 0. Eigen::Index nb_element = nb(); diff --git a/src/element_container/LoadContainer.cpp b/src/element_container/LoadContainer.cpp index d7eb561..d9d0a1b 100644 --- a/src/element_container/LoadContainer.cpp +++ b/src/element_container/LoadContainer.cpp @@ -10,9 +10,14 @@ #include void LoadContainer::init(const RealVect & loads_p, - const RealVect & loads_q, - const Eigen::VectorXi & loads_bus_id) + const RealVect & loads_q, + const Eigen::VectorXi & loads_bus_id) { + int size = static_cast(loads_p.size()); + GenericContainer::check_size(loads_p, size, "loads_p"); + GenericContainer::check_size(loads_q, size, "loads_q"); + GenericContainer::check_size(loads_bus_id, size, "loads_bus_id"); + p_mw_ = loads_p; q_mvar_ = loads_q; bus_id_ = loads_bus_id; @@ -29,6 +34,7 @@ LoadContainer::StateRes LoadContainer::get_state() const LoadContainer::StateRes res(names_, p_mw, q_mvar, bus_id, status); return res; } + void LoadContainer::set_state(LoadContainer::StateRes & my_state ) { reset_results(); @@ -47,7 +53,10 @@ void LoadContainer::set_state(LoadContainer::StateRes & my_state ) } -void LoadContainer::fillSbus(CplxVect & Sbus, const std::vector & id_grid_to_solver, bool ac) const { +void LoadContainer::fillSbus(CplxVect & Sbus, + const std::vector & id_grid_to_solver, + bool ac) const +{ int nb_load = nb(); int bus_id_me, bus_id_solver; cplx_type tmp; diff --git a/src/element_container/ShuntContainer.cpp b/src/element_container/ShuntContainer.cpp index 76a6129..4b5687b 100644 --- a/src/element_container/ShuntContainer.cpp +++ b/src/element_container/ShuntContainer.cpp @@ -11,9 +11,14 @@ #include void ShuntContainer::init(const RealVect & shunt_p_mw, - const RealVect & shunt_q_mvar, - const Eigen::VectorXi & shunt_bus_id) + const RealVect & shunt_q_mvar, + const Eigen::VectorXi & shunt_bus_id) { + int size = static_cast(shunt_p_mw.size()); + GenericContainer::check_size(shunt_p_mw, size, "shunt_p_mw"); + GenericContainer::check_size(shunt_q_mvar, size, "shunt_q_mvar"); + GenericContainer::check_size(shunt_bus_id, size, "shunt_bus_id"); + p_mw_ = shunt_p_mw; q_mvar_ = shunt_q_mvar; bus_id_ = shunt_bus_id; @@ -132,12 +137,12 @@ void ShuntContainer::fillYbus_spmat(Eigen::SparseMatrix & res, bool a } void ShuntContainer::compute_results(const Eigen::Ref & Va, - const Eigen::Ref & Vm, - const Eigen::Ref & V, - const std::vector & id_grid_to_solver, - const RealVect & bus_vn_kv, - real_type sn_mva, - bool ac) + const Eigen::Ref & Vm, + const Eigen::Ref & V, + const std::vector & id_grid_to_solver, + const RealVect & bus_vn_kv, + real_type sn_mva, + bool ac) { const int nb_shunt = static_cast(p_mw_.size()); v_kv_from_vpu(Va, Vm, status_, nb_shunt, bus_id_, id_grid_to_solver, bus_vn_kv, res_v_); diff --git a/src/element_container/TrafoContainer.cpp b/src/element_container/TrafoContainer.cpp index ac4c346..f33e1cd 100644 --- a/src/element_container/TrafoContainer.cpp +++ b/src/element_container/TrafoContainer.cpp @@ -12,16 +12,16 @@ #include void TrafoContainer::init(const RealVect & trafo_r, - const RealVect & trafo_x, - const CplxVect & trafo_b, - const RealVect & trafo_tap_step_pct, - // const RealVect & trafo_tap_step_degree, - const RealVect & trafo_tap_pos, - const RealVect & trafo_shift_degree, - const std::vector & trafo_tap_hv, // is tap on high voltage (true) or low voltate - const Eigen::VectorXi & trafo_hv_id, - const Eigen::VectorXi & trafo_lv_id - ) + const RealVect & trafo_x, + const CplxVect & trafo_b, + const RealVect & trafo_tap_step_pct, + // const RealVect & trafo_tap_step_degree, + const RealVect & trafo_tap_pos, + const RealVect & trafo_shift_degree, + const std::vector & trafo_tap_hv, // is tap on high voltage (true) or low voltate + const Eigen::VectorXi & trafo_hv_id, + const Eigen::VectorXi & trafo_lv_id + ) { /** INPUT DATA ARE ALREADY PAIR UNIT !! @@ -164,15 +164,17 @@ void TrafoContainer::_update_model_coeffs() } } -void TrafoContainer::fillYbus_spmat(Eigen::SparseMatrix & res, bool ac, const std::vector & id_grid_to_solver) +void TrafoContainer::fillYbus_spmat(Eigen::SparseMatrix & res, + bool ac, + const std::vector & id_grid_to_solver) { throw std::runtime_error("You should not use that!"); } void TrafoContainer::fillYbus(std::vector > & res, - bool ac, - const std::vector & id_grid_to_solver, - real_type sn_mva) const + bool ac, + const std::vector & id_grid_to_solver, + real_type sn_mva) const { //TODO merge that with fillYbusBranch! //TODO template here instead of "if" for ac / dc @@ -222,7 +224,10 @@ void TrafoContainer::fillYbus(std::vector > & res, } } -void TrafoContainer::hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, bool ac, const std::vector & id_grid_to_solver){ +void TrafoContainer::hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, + bool ac, + const std::vector & id_grid_to_solver) +{ if(ac) return; // return; const int nb_trafo = nb(); @@ -256,13 +261,13 @@ void TrafoContainer::hack_Sbus_for_dc_phase_shifter(CplxVect & Sbus, bool ac, co } void TrafoContainer::compute_results(const Eigen::Ref & Va, - const Eigen::Ref & Vm, - const Eigen::Ref & V, - const std::vector & id_grid_to_solver, - const RealVect & bus_vn_kv, - real_type sn_mva, - bool ac - ) + const Eigen::Ref & Vm, + const Eigen::Ref & V, + const std::vector & id_grid_to_solver, + const RealVect & bus_vn_kv, + real_type sn_mva, + bool ac + ) { // it needs to be initialized at 0. const int nb_element = nb(); @@ -360,10 +365,10 @@ void TrafoContainer::reset_results(){ void TrafoContainer::fillBp_Bpp(std::vector > & Bp, - std::vector > & Bpp, - const std::vector & id_grid_to_solver, - real_type sn_mva, - FDPFMethod xb_or_bx) const + std::vector > & Bpp, + const std::vector & id_grid_to_solver, + real_type sn_mva, + FDPFMethod xb_or_bx) const { // For Bp @@ -461,10 +466,10 @@ void TrafoContainer::fillBp_Bpp(std::vector > & Bp, void TrafoContainer::fillBf_for_PTDF(std::vector > & Bf, - const std::vector & id_grid_to_solver, - real_type sn_mva, - int nb_powerline, - bool transpose) const + const std::vector & id_grid_to_solver, + real_type sn_mva, + int nb_powerline, + bool transpose) const { const Eigen::Index nb_trafo = r_.size(); From b95bd0cc3e364d6c68c34948b70a424af9a3e514 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Mar 2024 15:48:11 +0100 Subject: [PATCH 21/42] debuging CI, a passion --- .circleci/config.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5721da7..4cae81a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -61,11 +61,11 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip python3-full -y - - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -132,7 +132,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip python3-full -y + - run: apt-get update && apt-get install python3-pip -y - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -157,6 +157,7 @@ jobs: name: "Install grid2op from source" command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel git clone https://github.com/rte-france/grid2op.git _grid2op pip install -e _grid2op - run: @@ -325,7 +326,6 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip python3-full git -y - - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: command: | @@ -343,7 +343,6 @@ jobs: steps: - checkout - run: apt-get update && apt-get install python3-pip python3-full git -y - - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: name: "Install grid2op from source" @@ -373,7 +372,7 @@ jobs: size: medium # ("medium" "large" "xlarge" "2xlarge") steps: - checkout - - run: py -m pip install --upgrade pip setuptools + - run: py -m pip install --upgrade pip setuptools wheel distribute - run: py -m pip install virtualenv - run: py -m virtualenv venv_test - run: From 8734cbf7abea553aa62aaa8eb2359fe29f936aff Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Mar 2024 15:57:29 +0100 Subject: [PATCH 22/42] debuging CI, a passion --- .circleci/config.yml | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4cae81a..250a285 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -60,7 +60,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip python3-full -y + - run: apt-get update && apt-get install python3-full python3-dev python3-pip python3-virtualenv git -y - run: python3 -m virtualenv venv_test - run: command: | @@ -84,6 +84,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -102,6 +103,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -120,6 +122,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -138,6 +141,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -150,7 +154,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip python3-full -y + - run: apt-get update && apt-get install python3-full python3-dev python3-pip python3-virtualenv git -y # - run: python3 -m pip install virtualenv - run: python3 -m virtualenv venv_test - run: @@ -187,6 +191,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U pybind11 git submodule init git submodule update @@ -209,6 +214,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U pybind11 CC=clang python setup.py build python -m pip install . @@ -223,6 +229,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -241,6 +248,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -259,6 +267,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -277,6 +286,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -295,6 +305,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -313,6 +324,7 @@ jobs: - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -325,11 +337,12 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip python3-full git -y + - run: apt-get update && apt-get install python3-full python3-dev python3-pip python3-virtualenv git -y - run: python3 -m virtualenv venv_test - run: command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute pip install -U grid2op pip install -U pybind11 git submodule init @@ -342,12 +355,13 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip python3-full git -y + - run: apt-get update && apt-get install python3-full python3-dev python3-pip python3-virtualenv git -y - run: python3 -m virtualenv venv_test - run: name: "Install grid2op from source" command: | source venv_test/bin/activate + pip install --upgrade pip setuptools wheel distribute git clone https://github.com/rte-france/grid2op.git _grid2op pip install -e _grid2op - run: @@ -372,7 +386,9 @@ jobs: size: medium # ("medium" "large" "xlarge" "2xlarge") steps: - checkout - - run: py -m pip install --upgrade pip setuptools wheel distribute + - run: choco install python --version=3.10 + - run: py --version + - run: py -m pip install --upgrade pip setuptools wheel - run: py -m pip install virtualenv - run: py -m virtualenv venv_test - run: From 6fa83dd900573f6767484fb39d8430518ae5388d Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Mar 2024 16:01:56 +0100 Subject: [PATCH 23/42] debuging CI, a passion --- .circleci/config.yml | 53 ++++++++++++++------------------------------ 1 file changed, 17 insertions(+), 36 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 250a285..542bd2d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,9 +23,9 @@ executors: gcc_8: docker: - image: gcc:8 - clang18: - docker: - - image: silkeh/clang:18 + # clang18: + # docker: + # - image: silkeh/clang:18 clang17: docker: - image: silkeh/clang:17 @@ -84,7 +84,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -103,7 +103,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -122,7 +122,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -141,7 +141,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -191,7 +191,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U pybind11 git submodule init git submodule update @@ -214,7 +214,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U pybind11 CC=clang python setup.py build python -m pip install . @@ -229,7 +229,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -248,7 +248,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -267,7 +267,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -286,7 +286,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -305,7 +305,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -324,7 +324,7 @@ jobs: - run: command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel pip install -U grid2op pip install -U pybind11 git submodule init @@ -335,24 +335,6 @@ jobs: compile_clang17: executor: clang17 resource_class: small - steps: - - checkout - - run: apt-get update && apt-get install python3-full python3-dev python3-pip python3-virtualenv git -y - - run: python3 -m virtualenv venv_test - - run: - command: | - source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute - pip install -U grid2op - pip install -U pybind11 - git submodule init - git submodule update - make - CC=clang python setup.py build - CC=clang python -m pip install -U . - compile_clang18: - executor: clang18 - resource_class: small steps: - checkout - run: apt-get update && apt-get install python3-full python3-dev python3-pip python3-virtualenv git -y @@ -361,7 +343,7 @@ jobs: name: "Install grid2op from source" command: | source venv_test/bin/activate - pip install --upgrade pip setuptools wheel distribute + pip install --upgrade pip setuptools wheel git clone https://github.com/rte-france/grid2op.git _grid2op pip install -e _grid2op - run: @@ -427,7 +409,6 @@ workflows: # - compile_clang13 # - compile_clang14 # - compile_clang15 - # - compile_clang16 + - compile_clang16 - compile_clang17 - - compile_clang18 - compile_windows From 85852a1ea41093a9d7d7fb987dfca9ad8b000caf Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Mar 2024 16:46:27 +0100 Subject: [PATCH 24/42] trying to fix CI yet again --- .circleci/config.yml | 5 ++-- CHANGELOG.rst | 2 ++ lightsim2grid/gridmodel/_aux_add_slack.py | 4 +-- lightsim2grid/lightSimBackend.py | 22 +++++++++----- .../tests/test_init_from_pypowsybl.py | 30 +++++++++++++------ 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 542bd2d..a476e0b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -318,8 +318,7 @@ jobs: resource_class: small steps: - checkout - - run: apt-get update && apt-get install python3-pip python3-full git -y - - run: python3 -m pip install virtualenv + - run: apt-get update && apt-get install python3-full python3-dev python3-pip python3-virtualenv git -y - run: python3 -m virtualenv venv_test - run: command: | @@ -368,7 +367,7 @@ jobs: size: medium # ("medium" "large" "xlarge" "2xlarge") steps: - checkout - - run: choco install python --version=3.10 + - run: choco install python --version=3.10 --force - run: py --version - run: py -m pip install --upgrade pip setuptools wheel - run: py -m pip install virtualenv diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a02c84e..bfa3cd8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -42,6 +42,8 @@ Change Log - [FIXED] a bug when reading a grid initialize from pypowsybl (trafo names where put in place of shunt names) - [FIXED] read the docs was broken +- [FIXED] a bug when reading a grid from pandapower for multiple slacks when slack + are given by the "ext_grid" information. - [ADDED] sets of methods to extract the main component of a grid and perform powerflow only on this one. - [ADDED] possibility to set / retrieve the names of each elements of the grid. diff --git a/lightsim2grid/gridmodel/_aux_add_slack.py b/lightsim2grid/gridmodel/_aux_add_slack.py index 17b9c83..cfa66f0 100644 --- a/lightsim2grid/gridmodel/_aux_add_slack.py +++ b/lightsim2grid/gridmodel/_aux_add_slack.py @@ -103,8 +103,8 @@ def _aux_add_slack(model, pp_net, pp_to_ls): gen_p = np.concatenate((pp_net.gen["p_mw"].values, slack_contrib)) gen_v = np.concatenate((pp_net.gen["vm_pu"].values, vm_pu)) gen_bus = np.concatenate((pp_bus_to_ls(pp_net.gen["bus"].values, pp_to_ls), slack_bus_ids)) - gen_min_q = np.concatenate((pp_net.gen["min_q_mvar"].values, [-999999.])) - gen_max_q = np.concatenate((pp_net.gen["max_q_mvar"].values, [+99999.])) + gen_min_q = np.concatenate((pp_net.gen["min_q_mvar"].values, [-999999. for _ in range(nb_slack)])) + gen_max_q = np.concatenate((pp_net.gen["max_q_mvar"].values, [+99999. for _ in range(nb_slack)])) model.init_generators(gen_p, gen_v, gen_min_q, gen_max_q, gen_bus) # handle the possible distributed slack bus diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index f91d869..cd38e07 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -462,8 +462,12 @@ def _aux_pypowsybl_init_substations(self, loader_kwargs): orig_to_ls = np.array(self._grid._orig_to_ls) bus_doubled = np.concatenate([bus_init for _ in range(self.n_busbar_per_sub)]) 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) + if hasattr(type(self), "can_handle_more_than_2_busbar"): + for i in range(self.__nb_bus_before * (self.n_busbar_per_sub - 1)): + self._grid.deactivate_bus(i + self.__nb_bus_before) + else: + 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 + i * self.__nb_bus_before for i in range(self.n_busbar_per_sub)] ) @@ -622,7 +626,14 @@ def _load_grid_pandapower(self, path=None, filename=None): self._grid = init(self.init_pp_backend._grid) self.__nb_bus_before = self.init_pp_backend.get_nb_active_bus() - self._aux_setup_right_after_grid_init() + self._aux_setup_right_after_grid_init() + + # deactive the buses that have been added + for bus_id, bus_status in enumerate(self.init_pp_backend._grid.bus["in_service"].values): + if bus_status: + self._grid.reactivate_bus(bus_id) + else: + self._grid.deactivate_bus(bus_id) self.n_line = self.init_pp_backend.n_line self.n_gen = self.init_pp_backend.n_gen @@ -670,11 +681,6 @@ def _load_grid_pandapower(self, path=None, filename=None): self.thermal_limit_a = copy.deepcopy(self.init_pp_backend.thermal_limit_a) - # deactive the buses that have been added - nb_bus_init = self.init_pp_backend._grid.bus.shape[0] // 2 - for i in range(nb_bus_init): - self._grid.deactivate_bus(i + nb_bus_init) - self.__nb_powerline = self.init_pp_backend._grid.line.shape[0] self.__nb_bus_before = self.init_pp_backend.get_nb_active_bus() self._init_bus_load = 1.0 * self.init_pp_backend._grid.load["bus"].values diff --git a/lightsim2grid/tests/test_init_from_pypowsybl.py b/lightsim2grid/tests/test_init_from_pypowsybl.py index 7e008be..6621ed6 100644 --- a/lightsim2grid/tests/test_init_from_pypowsybl.py +++ b/lightsim2grid/tests/test_init_from_pypowsybl.py @@ -175,15 +175,27 @@ def test_ac_pf(self): 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}" - param = lf.Parameters(voltage_init_mode=pp._pypowsybl.VoltageInitMode.UNIFORM_VALUES, - transformer_voltage_control_on=False, - no_generator_reactive_limits=True, - phase_shifter_regulation_on=False, - simul_shunt=False, - distributed_slack=False, - provider_parameters={"slackBusSelectionMode": "NAME", - "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} - ) + try: + param = lf.Parameters(voltage_init_mode=pp._pypowsybl.VoltageInitMode.UNIFORM_VALUES, + transformer_voltage_control_on=False, + no_generator_reactive_limits=True, + phase_shifter_regulation_on=False, + simul_shunt=False, + distributed_slack=False, + provider_parameters={"slackBusSelectionMode": "NAME", + "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} + ) + except TypeError: + param = lf.Parameters(voltage_init_mode=pp._pypowsybl.VoltageInitMode.UNIFORM_VALUES, + transformer_voltage_control_on=False, + # no_generator_reactive_limits=True, # documented in the doc but apparently fails + phase_shifter_regulation_on=False, + simul_shunt=False, + distributed_slack=False, + provider_parameters={"slackBusSelectionMode": "NAME", + "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} + ) + res_pypow = lf.run_ac(self.network_ref, parameters=param) bus_ref_kv = self.network_ref.get_voltage_levels().loc[self.network_ref.get_buses()["voltage_level_id"].values]["nominal_v"].values v_mag_pypo = self.network_ref.get_buses()["v_mag"].values / bus_ref_kv From c02545c8d877ecfdfe45d2bff41677b2416299f3 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 8 Mar 2024 17:38:52 +0100 Subject: [PATCH 25/42] trying to fix CI yet again --- lightsim2grid/tests/test_init_from_pypowsybl.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lightsim2grid/tests/test_init_from_pypowsybl.py b/lightsim2grid/tests/test_init_from_pypowsybl.py index 6621ed6..1770754 100644 --- a/lightsim2grid/tests/test_init_from_pypowsybl.py +++ b/lightsim2grid/tests/test_init_from_pypowsybl.py @@ -190,10 +190,10 @@ def test_ac_pf(self): transformer_voltage_control_on=False, # no_generator_reactive_limits=True, # documented in the doc but apparently fails phase_shifter_regulation_on=False, - simul_shunt=False, + # simul_shunt=False, # documented in the doc but apparently fails distributed_slack=False, provider_parameters={"slackBusSelectionMode": "NAME", - "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} + "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} ) res_pypow = lf.run_ac(self.network_ref, parameters=param) From 0bdc52f733eca283a8610da1fdbde2a371a9b6c0 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Mon, 11 Mar 2024 10:16:37 +0100 Subject: [PATCH 26/42] fix new parameters names in pypowsybl leading to broken tests --- .../tests/test_init_from_pypowsybl.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lightsim2grid/tests/test_init_from_pypowsybl.py b/lightsim2grid/tests/test_init_from_pypowsybl.py index 1770754..398cdb9 100644 --- a/lightsim2grid/tests/test_init_from_pypowsybl.py +++ b/lightsim2grid/tests/test_init_from_pypowsybl.py @@ -177,20 +177,20 @@ def test_ac_pf(self): try: param = lf.Parameters(voltage_init_mode=pp._pypowsybl.VoltageInitMode.UNIFORM_VALUES, - transformer_voltage_control_on=False, - no_generator_reactive_limits=True, - phase_shifter_regulation_on=False, - simul_shunt=False, - distributed_slack=False, - provider_parameters={"slackBusSelectionMode": "NAME", - "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} - ) + transformer_voltage_control_on=False, + use_reactive_limits=False, + shunt_compensator_voltage_control_on=False, + phase_shifter_regulation_on=False, + distributed_slack=False, + provider_parameters={"slackBusSelectionMode": "NAME", + "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} + ) except TypeError: param = lf.Parameters(voltage_init_mode=pp._pypowsybl.VoltageInitMode.UNIFORM_VALUES, transformer_voltage_control_on=False, - # no_generator_reactive_limits=True, # documented in the doc but apparently fails + no_generator_reactive_limits=True, # documented in the doc but apparently fails phase_shifter_regulation_on=False, - # simul_shunt=False, # documented in the doc but apparently fails + simul_shunt=False, # documented in the doc but apparently fails distributed_slack=False, provider_parameters={"slackBusSelectionMode": "NAME", "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} From f1992bf4c99a7762a75b5e519e8c358e19f10379 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Tue, 12 Mar 2024 17:51:13 +0100 Subject: [PATCH 27/42] some more fixes --- .circleci/config.yml | 4 ++-- CHANGELOG.rst | 2 ++ lightsim2grid/lightSimBackend.py | 12 ++++++++++++ src/element_container/GeneratorContainer.cpp | 2 +- src/element_container/GeneratorContainer.h | 6 ++++-- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a476e0b..e13e48c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -367,7 +367,7 @@ jobs: size: medium # ("medium" "large" "xlarge" "2xlarge") steps: - checkout - - run: choco install python --version=3.10 --force + - run: choco install python --version=3.10 --force -y - run: py --version - run: py -m pip install --upgrade pip setuptools wheel - run: py -m pip install virtualenv @@ -392,7 +392,7 @@ jobs: command: | .\venv_test\Scripts\activate cd lightsim2grid\tests - python -m unittest discover -v + py -m unittest discover -v workflows: version: 2.1 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bfa3cd8..59b4169 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -44,6 +44,8 @@ Change Log - [FIXED] read the docs was broken - [FIXED] a bug when reading a grid from pandapower for multiple slacks when slack are given by the "ext_grid" information. +- [FIXED] a bug in "gridmodel.assign_slack_to_most_connected()" that could throw an error if a + generator with "target_p" == 0. was connected to the most connected bus on the grid - [ADDED] sets of methods to extract the main component of a grid and perform powerflow only on this one. - [ADDED] possibility to set / retrieve the names of each elements of the grid. diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index cd38e07..c7dbb52 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -567,6 +567,18 @@ def _load_grid_pypowsybl(self, path=None, filename=None): self.name_storage = np.array(batt_sub.index) self.name_shunt = np.array(sh_sub.index) + if "reconnect_disco_gen" in loader_kwargs and loader_kwargs["reconnect_disco_gen"]: + for el in self._grid.get_generators(): + if not el.connected: + self._grid.reactivate_gen(el.id) + self._grid.change_bus_gen(el.id, self.gen_to_subid[el.id]) + + if "reconnect_disco_load" in loader_kwargs and loader_kwargs["reconnect_disco_load"]: + for el in self._grid.get_loads(): + if not el.connected: + self._grid.reactivate_load(el.id) + self._grid.change_bus_load(el.id, self.load_to_subid[el.id]) + # complete the other vectors self._compute_pos_big_topo() diff --git a/src/element_container/GeneratorContainer.cpp b/src/element_container/GeneratorContainer.cpp index 2515997..7598301 100644 --- a/src/element_container/GeneratorContainer.cpp +++ b/src/element_container/GeneratorContainer.cpp @@ -495,7 +495,7 @@ void GeneratorContainer::gen_p_per_bus(std::vector & res) const { if(!status_[gen_id]) continue; const auto my_bus = bus_id_(gen_id); - res[my_bus] += p_mw_(gen_id); + if (p_mw_(gen_id) > 0.) res[my_bus] += p_mw_(gen_id); } } diff --git a/src/element_container/GeneratorContainer.h b/src/element_container/GeneratorContainer.h index 2dbdb4c..c8c86e6 100644 --- a/src/element_container/GeneratorContainer.h +++ b/src/element_container/GeneratorContainer.h @@ -196,7 +196,9 @@ class GeneratorContainer: public GenericContainer } } // returns only the gen_id with the highest p that is connected to this bus ! - int assign_slack_bus(int slack_bus_id, const std::vector & gen_p_per_bus, SolverControl & solver_control){ + int assign_slack_bus(int slack_bus_id, + const std::vector & gen_p_per_bus, + SolverControl & solver_control){ const int nb_gen = nb(); int res_gen_id = -1; real_type max_p = -1.; @@ -205,7 +207,7 @@ class GeneratorContainer: public GenericContainer if(!status_[gen_id]) continue; if(bus_id_(gen_id) != slack_bus_id) continue; const real_type p_mw = p_mw_(gen_id); - add_slackbus(gen_id, p_mw / gen_p_per_bus[slack_bus_id], solver_control); + if (p_mw > 0.) add_slackbus(gen_id, p_mw / gen_p_per_bus[slack_bus_id], solver_control); if((p_mw > max_p) || (res_gen_id == -1) ){ res_gen_id = gen_id; max_p = p_mw; From dc08f106db1dde66b2aba045bfeebca483e8c155 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 09:07:41 +0100 Subject: [PATCH 28/42] more robust implementation of copy --- lightsim2grid/lightSimBackend.py | 106 ++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index c7dbb52..d649b07 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -53,27 +53,19 @@ def __init__(self, stop_if_load_disco : Optional[bool] = True, stop_if_gen_disco : Optional[bool] = True, ): - try: - # for grid2Op >= 1.7.1 - Backend.__init__(self, - detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, - can_be_copied=can_be_copied, - solver_type=solver_type, - max_iter=max_iter, - tol=tol, - turned_off_pv=turned_off_pv, - dist_slack_non_renew=dist_slack_non_renew, - use_static_gen=use_static_gen, - loader_method=loader_method, - loader_kwargs=loader_kwargs, - stop_if_load_disco=stop_if_load_disco, - stop_if_gen_disco=stop_if_gen_disco, - ) - except TypeError as exc_: - warnings.warn("Please use grid2op >= 1.7.1: with older grid2op versions, " - "you cannot set max_iter, tol nor solver_type arguments.") - Backend.__init__(self, - detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + + self._aux_init_super(detailed_infos_for_cascading_failures, + can_be_copied, + solver_type, + max_iter, + tol, + turned_off_pv, + dist_slack_non_renew, + use_static_gen, + loader_method, + loader_kwargs, + stop_if_load_disco, + stop_if_gen_disco) # lazy loading because it crashes... from lightsim2grid._utils import _DoNotUseAnywherePandaPowerBackend @@ -222,6 +214,41 @@ def __init__(self, # add the static gen to the list of controlable gen in grid2Op self._use_static_gen = use_static_gen # TODO implement it + def _aux_init_super(self, + detailed_infos_for_cascading_failures, + can_be_copied, + solver_type, + max_iter, + tol, + turned_off_pv, + dist_slack_non_renew, + use_static_gen, + loader_method, + loader_kwargs, + stop_if_load_disco, + stop_if_gen_disco): + try: + # for grid2Op >= 1.7.1 + Backend.__init__(self, + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, + can_be_copied=can_be_copied, + solver_type=solver_type, + max_iter=max_iter, + tol=tol, + turned_off_pv=turned_off_pv, + dist_slack_non_renew=dist_slack_non_renew, + use_static_gen=use_static_gen, + loader_method=loader_method, + loader_kwargs=loader_kwargs, + stop_if_load_disco=stop_if_load_disco, + stop_if_gen_disco=stop_if_gen_disco, + ) + except TypeError as exc_: + warnings.warn("Please use grid2op >= 1.7.1: with older grid2op versions, " + "you cannot set max_iter, tol nor solver_type arguments.") + Backend.__init__(self, + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + def turnedoff_no_pv(self): self._turned_off_pv = False self._grid.turnedoff_no_pv() @@ -1140,26 +1167,45 @@ def copy(self): #################### # res = copy.deepcopy(self) # super slow res = type(self).__new__(type(self)) + res._aux_init_super(self.detailed_infos_for_cascading_failures, + self._can_be_copied, + self.__current_solver_type, + self.max_it, + self.tol, + self._turned_off_pv, + self._dist_slack_non_renew, + self._use_static_gen, + self._loader_method, + self._loader_kwargs, + self._stop_if_load_disco, + self._stop_if_gen_disco) res.comp_time = self.comp_time res.timer_gridmodel_xx_pf = self.timer_gridmodel_xx_pf # copy the regular attribute res.__has_storage = self.__has_storage - res.__current_solver_type = self.__current_solver_type + # res.__current_solver_type = self.__current_solver_type res.__nb_powerline = self.__nb_powerline res.__nb_bus_before = self.__nb_bus_before - res._can_be_copied = self._can_be_copied + # res._can_be_copied = self._can_be_copied res.cst_1 = dt_float(1.0) - li_regular_attr = ["detailed_infos_for_cascading_failures", "comp_time", "can_output_theta", "_is_loaded", + # li_regular_attr = ["detailed_infos_for_cascading_failures", "comp_time", "can_output_theta", "_is_loaded", + # "nb_bus_total", "initdc", + # "_big_topo_to_obj", "max_it", "tol", "dim_topo", + # "_idx_hack_storage", + # "_timer_preproc", "_timer_postproc", "_timer_solver", + # "_my_kwargs", "supported_grid_format", + # "_turned_off_pv", "_dist_slack_non_renew", + # "_loader_method", "_loader_kwargs", + # "_missing_two_busbars_support_info", "n_busbar_per_sub", + # "_use_static_gen", "_stop_if_load_disco", "_stop_if_gen_disco" + # ] + li_regular_attr = ["comp_time", "can_output_theta", "_is_loaded", "nb_bus_total", "initdc", - "_big_topo_to_obj", "max_it", "tol", "dim_topo", + "_big_topo_to_obj", "dim_topo", "_idx_hack_storage", "_timer_preproc", "_timer_postproc", "_timer_solver", - "_my_kwargs", "supported_grid_format", - "_turned_off_pv", "_dist_slack_non_renew", - "_loader_method", "_loader_kwargs", - "_missing_two_busbars_support_info", "n_busbar_per_sub", - "_use_static_gen", "_stop_if_load_disco", "_stop_if_gen_disco" + "supported_grid_format", ] for attr_nm in li_regular_attr: if hasattr(self, attr_nm): From 2c8947d01df2ea76e8d5fd77bb5597bbd08cb8b0 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 09:19:48 +0100 Subject: [PATCH 29/42] fixing the previously bugy more robust implementation of copy --- lightsim2grid/lightSimBackend.py | 58 ++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index d649b07..82f72f0 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -54,6 +54,34 @@ def __init__(self, stop_if_gen_disco : Optional[bool] = True, ): + self.max_it = max_iter + self.tol = tol # tolerance for the solver + self._check_suitable_solver_type(solver_type, check_in_avail_solver=False) + self.__current_solver_type = solver_type + + # does the "turned off" generators (including when p=0) + # are pv buses + self._turned_off_pv = turned_off_pv + + # distributed slack, on non renewable gen with P > 0 + self._dist_slack_non_renew = dist_slack_non_renew + + # add the static gen to the list of controlable gen in grid2Op + self._use_static_gen = use_static_gen # TODO implement it + + self._loader_method = loader_method + self._loader_kwargs = loader_kwargs + + #: .. versionadded:: 0.7.6 + #: if set to `True` (default) then the backend will raise a + #: BackendError in case of disconnected load + self._stop_if_load_disco = stop_if_load_disco + + #: .. versionadded:: 0.7.6 + #: if set to `True` (default) then the backend will raise a + #: BackendError in case of disconnected generator + self._stop_if_gen_disco = stop_if_gen_disco + self._aux_init_super(detailed_infos_for_cascading_failures, can_be_copied, solver_type, @@ -74,18 +102,7 @@ def __init__(self, if not self.__has_storage: warnings.warn("Please upgrade your grid2Op to >= 1.5.0. You are using a backward compatibility " "feature that will be removed in further lightsim2grid version.") - self._loader_method = loader_method - self._loader_kwargs = loader_kwargs - #: .. versionadded:: 0.7.6 - #: if set to `True` (default) then the backend will raise a - #: BackendError in case of disconnected load - self._stop_if_load_disco = stop_if_load_disco - - #: .. versionadded:: 0.7.6 - #: if set to `True` (default) then the backend will raise a - #: BackendError in case of disconnected generator - self._stop_if_gen_disco = stop_if_gen_disco if loader_method == "pandapower": self.supported_grid_format = ("json", ) # new in 1.9.6 @@ -123,8 +140,6 @@ def __init__(self, self.init_pp_backend = _DoNotUseAnywherePandaPowerBackend() self.V = None - self.max_it = max_iter - self.tol = tol # tolerance for the solver self.prod_pu_to_kv = None self.load_pu_to_kv = None @@ -190,8 +205,6 @@ def __init__(self, self._timer_postproc = 0. self._timer_preproc = 0. self._timer_solver = 0. - self._check_suitable_solver_type(solver_type, check_in_avail_solver=False) - self.__current_solver_type = solver_type # hack for the storage unit: # in grid2op, for simplicity, I suppose that if a storage is alone on a busbar, and @@ -204,15 +217,7 @@ def __init__(self, # backend SHOULD not do these kind of stuff self._idx_hack_storage = [] - # does the "turned off" generators (including when p=0) - # are pv buses - self._turned_off_pv = turned_off_pv - - # distributed slack, on non renewable gen with P > 0 - self._dist_slack_non_renew = dist_slack_non_renew - - # add the static gen to the list of controlable gen in grid2Op - self._use_static_gen = use_static_gen # TODO implement it + def _aux_init_super(self, detailed_infos_for_cascading_failures, @@ -1184,7 +1189,7 @@ def copy(self): # copy the regular attribute res.__has_storage = self.__has_storage - # res.__current_solver_type = self.__current_solver_type + res.__current_solver_type = self.__current_solver_type # forced here because of special `__` res.__nb_powerline = self.__nb_powerline res.__nb_bus_before = self.__nb_bus_before # res._can_be_copied = self._can_be_copied @@ -1206,6 +1211,9 @@ def copy(self): "_idx_hack_storage", "_timer_preproc", "_timer_postproc", "_timer_solver", "supported_grid_format", + "max_it", "tol", "_turned_off_pv", "_dist_slack_non_renew", + "_use_static_gen", "_loader_method", "_loader_kwargs", + "_stop_if_load_disco", "_stop_if_gen_disco" ] for attr_nm in li_regular_attr: if hasattr(self, attr_nm): From a927c83307fe115693d094f0e04a5bfcd681dcc4 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 09:35:32 +0100 Subject: [PATCH 30/42] trying to fix the CI --- .circleci/config.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e13e48c..cc6fe65 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -368,10 +368,16 @@ jobs: steps: - checkout - run: choco install python --version=3.10 --force -y - - run: py --version - - run: py -m pip install --upgrade pip setuptools wheel - - run: py -m pip install virtualenv - - run: py -m virtualenv venv_test + - run: C:\Python310\python --version + - run: C:\Python310\python -m pip install --upgrade pip setuptools wheel + - run: C:\Python310\python -m pip install virtualenv + - run: C:\Python310\python -m virtualenv venv_test + - run: + name: "Chekc python / pip version in venv" + command: | + .\venv_test\Scripts\activate + python --version + pip --version - run: name: "Install grid2op from source" command: | @@ -385,14 +391,14 @@ jobs: pip install -U pybind11 git submodule init git submodule update - py setup.py build - py -m pip install -e .[test] + python setup.py build + python -m pip install -e .[test] - run: name: "make tests" command: | .\venv_test\Scripts\activate cd lightsim2grid\tests - py -m unittest discover -v + python -m unittest discover -v workflows: version: 2.1 From ac260ae5ea31f7d532a52b85e0597a9ec73d9c88 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 09:40:27 +0100 Subject: [PATCH 31/42] fixing the backend, trying to fix windows CI --- lightsim2grid/lightSimBackend.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 82f72f0..ccc7058 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, RTE (https://www.rte-france.com) +# Copyright (c) 2020-2024, RTE (https://www.rte-france.com) # See AUTHORS.txt # This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. # If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -1172,6 +1172,8 @@ def copy(self): #################### # res = copy.deepcopy(self) # super slow res = type(self).__new__(type(self)) + # make sure to init the "base class" + # in particular with "new" attributes in future grid2op Backend res._aux_init_super(self.detailed_infos_for_cascading_failures, self._can_be_copied, self.__current_solver_type, @@ -1192,19 +1194,7 @@ def copy(self): res.__current_solver_type = self.__current_solver_type # forced here because of special `__` res.__nb_powerline = self.__nb_powerline res.__nb_bus_before = self.__nb_bus_before - # res._can_be_copied = self._can_be_copied res.cst_1 = dt_float(1.0) - # li_regular_attr = ["detailed_infos_for_cascading_failures", "comp_time", "can_output_theta", "_is_loaded", - # "nb_bus_total", "initdc", - # "_big_topo_to_obj", "max_it", "tol", "dim_topo", - # "_idx_hack_storage", - # "_timer_preproc", "_timer_postproc", "_timer_solver", - # "_my_kwargs", "supported_grid_format", - # "_turned_off_pv", "_dist_slack_non_renew", - # "_loader_method", "_loader_kwargs", - # "_missing_two_busbars_support_info", "n_busbar_per_sub", - # "_use_static_gen", "_stop_if_load_disco", "_stop_if_gen_disco" - # ] li_regular_attr = ["comp_time", "can_output_theta", "_is_loaded", "nb_bus_total", "initdc", "_big_topo_to_obj", "dim_topo", From f2837b304c551d35dccbce3abbadaab1721c5466 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 09:50:34 +0100 Subject: [PATCH 32/42] Backward compat with improved .copy() method --- docs/conf.py | 6 +- lightsim2grid/__init__.py | 2 +- lightsim2grid/lightSimBackend.py | 140 ++++++++++++++++++------------- setup.py | 2 +- 4 files changed, 87 insertions(+), 63 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 8438a0a..796a133 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,11 +22,7 @@ author = 'Benjamin DONNOT' # The full version, including alpha/beta/rc tags -<<<<<<< HEAD -release = "0.7.5.dev0" -======= -release = "0.7.4" ->>>>>>> master +release = "0.7.5.dev0.post1" version = '0.7' # -- General configuration --------------------------------------------------- diff --git a/lightsim2grid/__init__.py b/lightsim2grid/__init__.py index 145b6f3..abc421d 100644 --- a/lightsim2grid/__init__.py +++ b/lightsim2grid/__init__.py @@ -5,7 +5,7 @@ # you can obtain one at http://mozilla.org/MPL/2.0/. # SPDX-License-Identifier: MPL-2.0 # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. -__version__ = "0.7.5" +__version__ = "0.7.5.post1" __all__ = ["newtonpf", "SolverType", "ErrorType", "solver"] diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 7a0ce86..522a998 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, RTE (https://www.rte-france.com) +# Copyright (c) 2020-2024, RTE (https://www.rte-france.com) # See AUTHORS.txt # This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. # If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -40,22 +40,29 @@ def __init__(self, dist_slack_non_renew: bool=False, # distribute the slack on non renewable turned on (and with P>0) generators use_static_gen: bool=False, # add the static generators as generator gri2dop side ): - try: - # for grid2Op >= 1.7.1 - Backend.__init__(self, - detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, - can_be_copied=can_be_copied, - solver_type=solver_type, - max_iter=max_iter, - tol=tol, - turned_off_pv=turned_off_pv, - dist_slack_non_renew=dist_slack_non_renew, - use_static_gen=use_static_gen) - except TypeError as exc_: - warnings.warn("Please use grid2op >= 1.7.1: with older grid2op versions, " - "you cannot set max_iter, tol nor solver_type arguments.") - Backend.__init__(self, - detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + self.max_it = max_iter + self.tol = tol # tolerance for the solver + self._check_suitable_solver_type(solver_type, check_in_avail_solver=False) + self.__current_solver_type = solver_type + + # does the "turned off" generators (including when p=0) + # are pv buses + self._turned_off_pv = turned_off_pv + + # distributed slack, on non renewable gen with P > 0 + self._dist_slack_non_renew = dist_slack_non_renew + + # add the static gen to the list of controlable gen in grid2Op + self._use_static_gen = use_static_gen # TODO implement it + + self._aux_init_super(detailed_infos_for_cascading_failures, + can_be_copied, + solver_type, + max_iter, + tol, + turned_off_pv, + dist_slack_non_renew, + use_static_gen) # lazy loading because it crashes... from lightsim2grid._utils import _DoNotUseAnywherePandaPowerBackend @@ -64,7 +71,9 @@ def __init__(self, if not self.__has_storage: warnings.warn("Please upgrade your grid2Op to >= 1.5.0. You are using a backward compatibility " "feature that will be removed in further lightsim2grid version.") - + + 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 @@ -92,8 +101,6 @@ def __init__(self, self.init_pp_backend = _DoNotUseAnywherePandaPowerBackend() self.V = None - self.max_it = max_iter - self.tol = tol # tolerance for the solver self.prod_pu_to_kv = None self.load_pu_to_kv = None @@ -145,12 +152,20 @@ def __init__(self, # available solver in lightsim self.available_solvers = [] - self.comp_time = 0. # computation time of just the powerflow + + # computation time of just the powerflow (when the grid is formatted + # by the gridmodel already) + # it takes only into account the time spend in the powerflow algorithm + self.comp_time = 0. + + # computation time of the powerflow + # it takes into account everything in the gridmodel, including the mapping + # to the solver, building of Ybus and Sbus AND the time to solve the powerflow + self.timer_gridmodel_xx_pf = 0. + self._timer_postproc = 0. self._timer_preproc = 0. self._timer_solver = 0. - self._check_suitable_solver_type(solver_type, check_in_avail_solver=False) - self.__current_solver_type = solver_type # hack for the storage unit: # in grid2op, for simplicity, I suppose that if a storage is alone on a busbar, and @@ -162,33 +177,34 @@ def __init__(self, # TODO and should rather be handled in pandapower backend # backend SHOULD not do these kind of stuff self._idx_hack_storage = [] - - # does the "turned off" generators (including when p=0) - # are pv buses - self._turned_off_pv = turned_off_pv - - # distributed slack, on non renewable gen with P > 0 - self._dist_slack_non_renew = dist_slack_non_renew - - # add the static gen to the list of controlable gen in grid2Op - self._use_static_gen = use_static_gen # TODO implement it - - # storage data for this object (otherwise it's in the class) - self.n_storage = None - self.storage_to_subid = None - self.storage_pu_to_kv = None - self.name_storage = None - self.storage_to_sub_pos = None - self.storage_type = None - self.storage_Emin = None - self.storage_Emax = None - self.storage_max_p_prod = None - self.storage_max_p_absorb = None - self.storage_marginal_cost = None - self.storage_loss = None - self.storage_discharging_efficiency = None - self.storage_charging_efficiency = None + def _aux_init_super(self, + detailed_infos_for_cascading_failures, + can_be_copied, + solver_type, + max_iter, + tol, + turned_off_pv, + dist_slack_non_renew, + use_static_gen): + try: + # for grid2Op >= 1.7.1 + Backend.__init__(self, + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures, + can_be_copied=can_be_copied, + solver_type=solver_type, + max_iter=max_iter, + tol=tol, + turned_off_pv=turned_off_pv, + dist_slack_non_renew=dist_slack_non_renew, + use_static_gen=use_static_gen + ) + except TypeError as exc_: + warnings.warn("Please use grid2op >= 1.7.1: with older grid2op versions, " + "you cannot set max_iter, tol nor solver_type arguments.") + Backend.__init__(self, + detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) + def turnedoff_no_pv(self): self._turned_off_pv = False self._grid.turnedoff_no_pv() @@ -909,25 +925,37 @@ def copy(self): #################### # res = copy.deepcopy(self) # super slow res = type(self).__new__(type(self)) + # make sure to init the "base class" + # in particular with "new" attributes in future grid2op Backend + res._aux_init_super(self.detailed_infos_for_cascading_failures, + self._can_be_copied, + self.__current_solver_type, + self.max_it, + self.tol, + self._turned_off_pv, + self._dist_slack_non_renew, + self._use_static_gen) + res.comp_time = self.comp_time + res.timer_gridmodel_xx_pf = self.timer_gridmodel_xx_pf # copy the regular attribute res.__has_storage = self.__has_storage - res.__current_solver_type = self.__current_solver_type + res.__current_solver_type = self.__current_solver_type # forced here because of special `__` res.__nb_powerline = self.__nb_powerline res.__nb_bus_before = self.__nb_bus_before - res._can_be_copied = self._can_be_copied res.cst_1 = dt_float(1.0) - li_regular_attr = ["detailed_infos_for_cascading_failures", "comp_time", "can_output_theta", "_is_loaded", + li_regular_attr = ["comp_time", "can_output_theta", "_is_loaded", "nb_bus_total", "initdc", - "_big_topo_to_obj", "max_it", "tol", "dim_topo", + "_big_topo_to_obj", "dim_topo", "_idx_hack_storage", "_timer_preproc", "_timer_postproc", "_timer_solver", - "_my_kwargs", - "_turned_off_pv", "_dist_slack_non_renew" + "supported_grid_format", + "max_it", "tol", "_turned_off_pv", "_dist_slack_non_renew", + "_use_static_gen" ] for attr_nm in li_regular_attr: if hasattr(self, attr_nm): - # this test is needed for backward compatibility with other grid2op version + # this test is needed for backward compatibility with older grid2op version setattr(res, attr_nm, copy.deepcopy(getattr(self, attr_nm))) # copy the numpy array diff --git a/setup.py b/setup.py index b28c51d..154f7ed 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ from pybind11.setup_helpers import Pybind11Extension, build_ext -__version__ = "0.7.5" +__version__ = "0.7.5.post1" KLU_SOLVER_AVAILABLE = False # Try to link against SuiteSparse (if available) From 1a231f3219ddc9f120b3c2d8b8ca74b29e26a30c Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 10:03:12 +0100 Subject: [PATCH 33/42] trying to fix windows CI --- .circleci/config.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6973ecc..f62aefe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -268,11 +268,17 @@ jobs: size: medium # ("medium" "large" "xlarge" "2xlarge") steps: - checkout - # - run: - # name: "Install Python" - # command: choco install python --version=3.9 # use python 3.9 for windows test - - run: py -m pip install virtualenv - - run: py -m virtualenv venv_test + - run: choco install python --version=3.10 --force -y + - run: C:\Python310\python --version + - run: C:\Python310\python -m pip install --upgrade pip setuptools wheel + - run: C:\Python310\python -m pip install virtualenv + - run: C:\Python310\python -m virtualenv venv_test + - run: + name: "Chekc python / pip version in venv" + command: | + .\venv_test\Scripts\activate + python --version + pip --version - run: name: "Install grid2op from source" command: | @@ -286,8 +292,8 @@ jobs: pip install -U pybind11 git submodule init git submodule update - py setup.py build - py -m pip install -e .[test] + python setup.py build + python -m pip install -e .[test] - run: name: "make tests" command: | @@ -295,6 +301,7 @@ jobs: cd lightsim2grid\tests python -m unittest discover -v + workflows: version: 2.1 compile: From 557cda62c56b3dffdeabd6e1312bbe7a2788c3bf Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 10:52:33 +0100 Subject: [PATCH 34/42] backward compat with new grid2op tests --- CHANGELOG.rst | 5 ++++ lightsim2grid/lightSimBackend.py | 2 +- lightsim2grid/tests/test_DCPF.py | 3 +- lightsim2grid/tests/test_Runner.py | 5 ++-- .../tests/test_init_from_pypowsybl.py | 29 +++++++++++++------ 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 147c4ab..60a23df 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,6 +19,11 @@ Change Log - maybe have a look at suitesparse "sliplu" tools ? - easier building (get rid of the "make" part) +[0.7.5.post1] 2024-03-14 +------------------------- +- [FIXED] backward compat with "future" grid2op version with a + better way to copy `LightSimBackend` + [0.7.5] 2023-10-05 -------------------- - [FIXED] a bug in DC powerflow when asking for computation time: it was not reset to 0. when diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 522a998..8a737e8 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -998,7 +998,7 @@ def copy(self): ] + type(self)._li_attr_disp for attr_nm in cls_attr: - if hasattr(self, attr_nm): + if hasattr(self, attr_nm) and not hasattr(type(self), attr_nm): # this test is needed for backward compatibility with other grid2op version setattr(res, attr_nm, copy.deepcopy(getattr(self, attr_nm))) ############### diff --git a/lightsim2grid/tests/test_DCPF.py b/lightsim2grid/tests/test_DCPF.py index 93604c1..847aff8 100644 --- a/lightsim2grid/tests/test_DCPF.py +++ b/lightsim2grid/tests/test_DCPF.py @@ -127,7 +127,8 @@ def _aux_test(self, pn_net): with warnings.catch_warnings(): warnings.filterwarnings("ignore") backend.load_grid(case_name) - + backend.assert_grid_correct() + nb_sub = backend.n_sub pp_net = backend.init_pp_backend._grid # first i deactivate all slack bus in pp that are connected but not handled in ls diff --git a/lightsim2grid/tests/test_Runner.py b/lightsim2grid/tests/test_Runner.py index e998a8d..32e8ccf 100644 --- a/lightsim2grid/tests/test_Runner.py +++ b/lightsim2grid/tests/test_Runner.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020, RTE (https://www.rte-france.com) +# Copyright (c) 2020-2024, RTE (https://www.rte-france.com) # See AUTHORS.txt # This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. # If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, @@ -11,7 +11,7 @@ import grid2op from grid2op.tests.test_Runner import TestRunner as TestRunner_glop from grid2op.tests.test_RunnerFast import TestRunner as TestRunnerFast_glop -from grid2op.tests.test_Runner import HelperTests, L2RPNReward, make +from grid2op.tests.test_Runner import L2RPNReward from grid2op.Runner import Runner from lightsim2grid.lightSimBackend import LightSimBackend @@ -80,3 +80,4 @@ def setUp(self): if __name__ == "__main__": unittest.main() + \ No newline at end of file diff --git a/lightsim2grid/tests/test_init_from_pypowsybl.py b/lightsim2grid/tests/test_init_from_pypowsybl.py index 97528f5..e9d8c40 100644 --- a/lightsim2grid/tests/test_init_from_pypowsybl.py +++ b/lightsim2grid/tests/test_init_from_pypowsybl.py @@ -170,15 +170,26 @@ def test_ac_pf(self): v_ls_ref = self.ref_samecase.ac_pf(1.0 * self.V_init_ac, 10, self.tol) assert np.abs(v_ls - v_ls_ref).max() <= self.tol_eq, "error for vresults for ac" - param = lf.Parameters(voltage_init_mode=pp._pypowsybl.VoltageInitMode.UNIFORM_VALUES, - transformer_voltage_control_on=False, - no_generator_reactive_limits=True, - phase_shifter_regulation_on=False, - simul_shunt=False, - distributed_slack=False, - provider_parameters={"slackBusSelectionMode": "NAME", - "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} - ) + try: + param = lf.Parameters(voltage_init_mode=pp._pypowsybl.VoltageInitMode.UNIFORM_VALUES, + transformer_voltage_control_on=False, + use_reactive_limits=False, + shunt_compensator_voltage_control_on=False, + phase_shifter_regulation_on=False, + distributed_slack=False, + provider_parameters={"slackBusSelectionMode": "NAME", + "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} + ) + except TypeError: + param = lf.Parameters(voltage_init_mode=pp._pypowsybl.VoltageInitMode.UNIFORM_VALUES, + transformer_voltage_control_on=False, + no_generator_reactive_limits=True, # documented in the doc but apparently fails + phase_shifter_regulation_on=False, + simul_shunt=False, # documented in the doc but apparently fails + distributed_slack=False, + provider_parameters={"slackBusSelectionMode": "NAME", + "slackBusesIds": self.network_ref.get_buses().iloc[self.get_slackbus_id()].name} + ) res_pypow = lf.run_ac(self.network_ref, parameters=param) if self.compare_pp(): pdp.runpp(self.pp_samecase, init="flat") From 1cf647dc722c80273c47a33c4784c7aa03154fb3 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 11:13:45 +0100 Subject: [PATCH 35/42] trying to fix readthedocs --- .readthedocs.yml | 11 +++++++++-- src/BaseSolver.cpp | 1 - src/DataGeneric.h | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 97df330..ab6ff9c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,4 +1,4 @@ -version: 2 +version: "2" submodules: include: @@ -6,8 +6,15 @@ submodules: - eigen recursive: true +build: + os: "ubuntu-22.04" + tools: + python: "3.10" + +sphinx: + configuration: docs/conf.py + python: - version: 3.8 install: - method: pip path: . diff --git a/src/BaseSolver.cpp b/src/BaseSolver.cpp index 05c837e..e9b3f65 100644 --- a/src/BaseSolver.cpp +++ b/src/BaseSolver.cpp @@ -145,7 +145,6 @@ Eigen::VectorXi BaseSolver::extract_slack_bus_id(const Eigen::VectorXi & pv, Eigen::VectorXi res(nb_bus - pv.size() - pq.size()); Eigen::Index i_res = 0; - bool found=false; // run through both pv and pq nodes and declare they are not slack bus std::vector tmp(nb_bus, true); for(unsigned int k=0; k < pv.size(); ++k) diff --git a/src/DataGeneric.h b/src/DataGeneric.h index 8d9bc60..4cf0a68 100644 --- a/src/DataGeneric.h +++ b/src/DataGeneric.h @@ -139,7 +139,7 @@ class DataGeneric : public BaseConstants template void check_size(const T & container, intType size, const std::string & container_name) { - if(container.size() != size) throw std::runtime_error(container_name + " do not have the proper size"); + if(static_cast(container.size()) != size) throw std::runtime_error(container_name + " do not have the proper size"); } /** From 6b8a10a5daa7abdcb10c8a98752b5411af62badb Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 11:16:07 +0100 Subject: [PATCH 36/42] CI might pass again --- lightsim2grid/tests/test_DCPF.py | 1 + lightsim2grid/tests/test_LightSimBackend.py | 117 +++++++++++--------- lightsim2grid/tests/test_RedispatchEnv.py | 79 ++----------- lightsim2grid/tests/test_Runner.py | 3 +- 4 files changed, 78 insertions(+), 122 deletions(-) diff --git a/lightsim2grid/tests/test_DCPF.py b/lightsim2grid/tests/test_DCPF.py index 847aff8..c659014 100644 --- a/lightsim2grid/tests/test_DCPF.py +++ b/lightsim2grid/tests/test_DCPF.py @@ -128,6 +128,7 @@ def _aux_test(self, pn_net): warnings.filterwarnings("ignore") backend.load_grid(case_name) backend.assert_grid_correct() + # backend.init_pp_backend.assert_grid_correct() nb_sub = backend.n_sub pp_net = backend.init_pp_backend._grid diff --git a/lightsim2grid/tests/test_LightSimBackend.py b/lightsim2grid/tests/test_LightSimBackend.py index 2cae0ba..52103bd 100644 --- a/lightsim2grid/tests/test_LightSimBackend.py +++ b/lightsim2grid/tests/test_LightSimBackend.py @@ -19,14 +19,50 @@ from grid2op.Space import GridObjects # lazy import __has_storage = hasattr(GridObjects, "n_storage") -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 -from grid2op.tests.BaseBackendTest import BaseTestResetEqualsLoadGrid, BaseTestVoltageOWhenDisco, BaseTestChangeBusSlack -from grid2op.tests.BaseBackendTest import BaseIssuesTest, BaseStatusActions -from grid2op.tests.test_Environment import TestLoadingBackendPandaPower, TestResetOk -from grid2op.tests.test_Environment import TestResetAfterCascadingFailure, TestCascadingFailure +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, + BaseTestTopoAction, + BaseTestEnvPerformsCorrectCascadingFailures, + BaseTestChangeBusAffectRightBus, + BaseTestShuntAction, + BaseTestResetEqualsLoadGrid, + BaseTestVoltageOWhenDisco, + BaseTestChangeBusSlack, + BaseIssuesTest, + BaseStatusActions) + +from grid2op.tests.test_Environment import (BaseTestLoadingBackendPandaPower, + BaseTestResetOk, + BaseTestResetAfterCascadingFailure, + BaseTestCascadingFailure) if __has_storage: from grid2op.tests.BaseBackendTest import BaseTestStorageAction @@ -37,32 +73,24 @@ from lightsim2grid.solver import SolverType from grid2op.Runner import Runner -class TestNames(HelperTests, BaseTestNames): + +class TestNames(BaseTestNames, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") bk = LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) return bk - def get_path(self): - return PATH_DATA_TEST_INIT - -class TestLoadingCase(HelperTests, BaseTestLoadingCase): +class TestLoadingCase(BaseTestLoadingCase, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") bk = LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) return bk - def get_path(self): - return PATH_DATA_TEST - def get_casefile(self): - return "test_case14.json" - - -class TestLoadingBackendFunc(HelperTests, BaseTestLoadingBackendFunc): +class TestLoadingBackendFunc(BaseTestLoadingBackendFunc, unittest.TestCase): def setUp(self): # TODO find something more elegant BaseTestLoadingBackendFunc.setUp(self) @@ -84,14 +112,8 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): bk = LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) return bk - def get_path(self): - return PATH_DATA_TEST - - def get_casefile(self): - return "test_case14.json" - -class TestTopoAction(HelperTests, BaseTestTopoAction): +class TestTopoAction(BaseTestTopoAction, unittest.TestCase): def setUp(self): BaseTestTopoAction.setUp(self) @@ -105,14 +127,8 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): bk = LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) return bk - def get_path(self): - return PATH_DATA_TEST - - def get_casefile(self): - return "test_case14.json" - -class TestEnvPerformsCorrectCascadingFailures(HelperTests, BaseTestEnvPerformsCorrectCascadingFailures): +class TestEnvPerformsCorrectCascadingFailures(BaseTestEnvPerformsCorrectCascadingFailures, unittest.TestCase): def setUp(self): BaseTestEnvPerformsCorrectCascadingFailures.setUp(self) @@ -125,15 +141,9 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): warnings.filterwarnings("ignore") bk = LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) return bk + - def get_casefile(self): - return "test_case14.json" - - def get_path(self): - return PATH_DATA_TEST - - -class TestChangeBusAffectRightBus(HelperTests, BaseTestChangeBusAffectRightBus): +class TestChangeBusAffectRightBus(BaseTestChangeBusAffectRightBus, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -141,7 +151,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestShuntAction(HelperTests, BaseTestShuntAction): +class TestShuntAction(BaseTestShuntAction, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -149,7 +159,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestResetEqualsLoadGrid(HelperTests, BaseTestResetEqualsLoadGrid): +class TestResetEqualsLoadGrid(BaseTestResetEqualsLoadGrid, unittest.TestCase): def setUp(self): BaseTestResetEqualsLoadGrid.setUp(self) @@ -160,7 +170,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestVoltageOWhenDisco(HelperTests, BaseTestVoltageOWhenDisco): +class TestVoltageOWhenDisco(BaseTestVoltageOWhenDisco, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -168,7 +178,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestChangeBusSlack(HelperTests, BaseTestChangeBusSlack): +class TestChangeBusSlack(BaseTestChangeBusSlack, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -176,7 +186,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestIssuesTest(HelperTests, BaseIssuesTest): +class TestIssuesTest(BaseIssuesTest, unittest.TestCase): tests_skipped = ["test_issue_125"] if version.parse(grid2op.__version__) < version.parse("1.9.2") else [] def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): @@ -185,7 +195,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestStatusAction(HelperTests, BaseStatusActions): +class TestStatusAction(BaseStatusActions, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -194,8 +204,9 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): if __has_storage: - class TestStorageAction(HelperTests, BaseTestStorageAction): + class TestStorageAction(BaseTestStorageAction, unittest.TestCase): def setUp(self): + super().setUp() self.tests_skipped = ["test_storage_action_topo"] # TODO this test is super weird ! It's like we impose # TODO a behaviour from pandapower (weird one) to all backends... @@ -206,22 +217,22 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestLoadingBackendLightSim(TestLoadingBackendPandaPower): +class TestLoadingBackendLightSim(BaseTestLoadingBackendPandaPower, unittest.TestCase): def get_backend(self, detailed_infos_for_cascading_failures=True): return LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) -class TestResetOkLS(TestResetOk): +class TestResetOkLS(BaseTestResetOk, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) -class TestResetAfterCascadingFailureLS(TestResetAfterCascadingFailure): +class TestResetAfterCascadingFailureLS(BaseTestResetAfterCascadingFailure, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) -class TestCascadingFailureLS(TestCascadingFailure): +class TestCascadingFailureLS(BaseTestCascadingFailure, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): return LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) diff --git a/lightsim2grid/tests/test_RedispatchEnv.py b/lightsim2grid/tests/test_RedispatchEnv.py index f49451b..0f5b5e0 100644 --- a/lightsim2grid/tests/test_RedispatchEnv.py +++ b/lightsim2grid/tests/test_RedispatchEnv.py @@ -5,75 +5,36 @@ # you can obtain one at http://mozilla.org/MPL/2.0/. # SPDX-License-Identifier: MPL-2.0 # This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. + import unittest import warnings -from grid2op.tests.helper_path_test import PATH_DATA_TEST_PP, PATH_DATA_TEST - -from grid2op.tests.helper_path_test import HelperTests -from grid2op.tests.BaseRedispTest import BaseTestRedispatch, BaseTestRedispatchChangeNothingEnvironment -from grid2op.tests.BaseRedispTest import BaseTestRedispTooLowHigh, BaseTestDispatchRampingIllegalETC -from grid2op.tests.BaseRedispTest import BaseTestLoadingAcceptAlmostZeroSumRedisp +from grid2op.tests.BaseRedispTest import (BaseTestRedispatch, + BaseTestRedispatchChangeNothingEnvironment, + BaseTestRedispTooLowHigh, + BaseTestDispatchRampingIllegalETC, + BaseTestLoadingAcceptAlmostZeroSumRedisp) from lightsim2grid.lightSimBackend import LightSimBackend -PATH_DATA_TEST_INIT = PATH_DATA_TEST -PATH_DATA_TEST = PATH_DATA_TEST_PP - - -class TestRedispatch(HelperTests, BaseTestRedispatch): - def setUp(self): - # TODO find something more elegant - BaseTestRedispatch.setUp(self) - - def tearDown(self): - # TODO find something more elegant - BaseTestRedispatch.tearDown(self) +class TestRedispatch(BaseTestRedispatch, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") bk = LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) return bk - def get_path(self): - return PATH_DATA_TEST_PP - - def get_casefile(self): - return "test_case14.json" - - -class TestRedispatchChangeNothingEnvironment(HelperTests, BaseTestRedispatchChangeNothingEnvironment): - def setUp(self): - # TODO find something more elegant - BaseTestRedispatchChangeNothingEnvironment.setUp(self) - - def tearDown(self): - # TODO find something more elegant - BaseTestRedispatchChangeNothingEnvironment.tearDown(self) +class TestRedispatchChangeNothingEnvironment(BaseTestRedispatchChangeNothingEnvironment, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") bk = LightSimBackend(detailed_infos_for_cascading_failures=detailed_infos_for_cascading_failures) return bk - def get_path(self): - return PATH_DATA_TEST_PP - - def get_casefile(self): - return "test_case14.json" - - -class TestRedispTooLowHigh(HelperTests, BaseTestRedispTooLowHigh): - def setUp(self): - # TODO find something more elegant - BaseTestRedispTooLowHigh.setUp(self) - - def tearDown(self): - # TODO find something more elegant - BaseTestRedispTooLowHigh.tearDown(self) +class TestRedispTooLowHigh(BaseTestRedispTooLowHigh, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -81,15 +42,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestDispatchRampingIllegalETC(HelperTests, BaseTestDispatchRampingIllegalETC): - def setUp(self): - # TODO find something more elegant - BaseTestDispatchRampingIllegalETC.setUp(self) - - def tearDown(self): - # TODO find something more elegant - BaseTestDispatchRampingIllegalETC.tearDown(self) - +class TestDispatchRampingIllegalETC(BaseTestDispatchRampingIllegalETC, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -97,15 +50,7 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): return bk -class TestLoadingAcceptAlmostZeroSumRedisp(HelperTests, BaseTestLoadingAcceptAlmostZeroSumRedisp): - def setUp(self): - # TODO find something more elegant - BaseTestLoadingAcceptAlmostZeroSumRedisp.setUp(self) - - def tearDown(self): - # TODO find something more elegant - BaseTestLoadingAcceptAlmostZeroSumRedisp.tearDown(self) - +class TestLoadingAcceptAlmostZeroSumRedisp(BaseTestLoadingAcceptAlmostZeroSumRedisp, unittest.TestCase): def make_backend(self, detailed_infos_for_cascading_failures=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore") @@ -114,4 +59,4 @@ def make_backend(self, detailed_infos_for_cascading_failures=False): if __name__ == "__main__": - unittest.main() + unittest.main() \ No newline at end of file diff --git a/lightsim2grid/tests/test_Runner.py b/lightsim2grid/tests/test_Runner.py index 32e8ccf..9f7a497 100644 --- a/lightsim2grid/tests/test_Runner.py +++ b/lightsim2grid/tests/test_Runner.py @@ -79,5 +79,4 @@ def setUp(self): if __name__ == "__main__": - unittest.main() - \ No newline at end of file + unittest.main() \ No newline at end of file From 919eb64958bfbb83d9898136be27efa1478623da Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 11:35:50 +0100 Subject: [PATCH 37/42] tyring to fix test in DC powerflow --- lightsim2grid/tests/test_DCPF.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lightsim2grid/tests/test_DCPF.py b/lightsim2grid/tests/test_DCPF.py index c659014..3e910d7 100644 --- a/lightsim2grid/tests/test_DCPF.py +++ b/lightsim2grid/tests/test_DCPF.py @@ -124,8 +124,10 @@ def _aux_test(self, pn_net): real_init_file = pp.from_json(case_name) backend = LightSimBackend() + type(backend)._clear_grid_dependant_class_attributes() with warnings.catch_warnings(): warnings.filterwarnings("ignore") + type(backend).env_name = pn_net backend.load_grid(case_name) backend.assert_grid_correct() # backend.init_pp_backend.assert_grid_correct() From af31a9112539a518b449d2b789251b6cf1b23be0 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Thu, 14 Mar 2024 12:02:17 +0100 Subject: [PATCH 38/42] fixing read the doc --- src/Solvers.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Solvers.h b/src/Solvers.h index 4adaac8..d3678f5 100644 --- a/src/Solvers.h +++ b/src/Solvers.h @@ -6,6 +6,9 @@ // SPDX-License-Identifier: MPL-2.0 // This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform. +#ifndef SOLVERS_H +#define SOLVERS_H + #include "BaseNRSolver.h" #include "BaseNRSolverSingleSlack.h" #include "DCSolver.h" @@ -94,3 +97,5 @@ typedef BaseFDPFSolver FDPF_BX_SparseLUSol class FDPF_XB_CKTSOSolver : public FDPF_XB_SparseLUSolver {}; class FDPF_BX_CKTSOSolver : public FDPF_BX_SparseLUSolver {}; #endif // CKTSO_SOLVER_AVAILABLE + +#endif // SOLVERS_H From daf8a9344fe1b4fefa3c55ae8e7c75bf91a551ee Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 15 Mar 2024 11:54:14 +0100 Subject: [PATCH 39/42] ready for version 0.8.0 --- lightsim2grid/lightSimBackend.py | 35 ++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 1a72af7..4b29741 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -67,7 +67,33 @@ def __init__(self, # add the static gen to the list of controlable gen in grid2Op self._use_static_gen = use_static_gen # TODO implement it - + + self._loader_method = loader_method + + self._loader_kwargs = loader_kwargs + + #: .. versionadded:: 0.8.0 + #: Which type of grid format can be read by your backend. + #: It is "json" if loaded from pandapower or + #: "xiidm" if loaded from pypowsybl. + self.supported_grid_format = None + if loader_method == "pandapower": + self.supported_grid_format = ("json", ) # new in 0.8.0 + elif loader_method == "pypowsybl": + self.supported_grid_format = ("xiidm", ) # new in 0.8.0 + else: + raise BackendError(f"Uknown loader_metho : '{loader_method}'") + + #: .. versionadded:: 0.8.0 + #: if set to `True` (default) then the backend will raise a + #: BackendError in case of disconnected load + self._stop_if_load_disco = stop_if_load_disco + + #: .. versionadded:: 0.8.0 + #: if set to `True` (default) then the backend will raise a + #: BackendError in case of disconnected generator + self._stop_if_gen_disco = stop_if_gen_disco + self._aux_init_super(detailed_infos_for_cascading_failures, can_be_copied, solver_type, @@ -88,13 +114,6 @@ def __init__(self, if not self.__has_storage: warnings.warn("Please upgrade your grid2Op to >= 1.5.0. You are using a backward compatibility " "feature that will be removed in further lightsim2grid version.") - - if loader_method == "pandapower": - self.supported_grid_format = ("json", ) # new in 1.9.6 - elif loader_method == "pypowsybl": - self.supported_grid_format = ("xiidm", ) # new in 1.9.6 - else: - raise BackendError(f"Uknown loader_metho : '{loader_method}'") self.shunts_data_available = True # needs to be self and not type(self) here From db3ef355e4e44a4c5107baf61326f5659175638a Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 15 Mar 2024 13:38:59 +0100 Subject: [PATCH 40/42] fixing some issues --- src/powerflow_algorithm/BaseFDPFAlgo.h | 4 ++++ src/powerflow_algorithm/BaseFDPFAlgo.tpp | 13 ++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/powerflow_algorithm/BaseFDPFAlgo.h b/src/powerflow_algorithm/BaseFDPFAlgo.h index 67b9874..22ca7f5 100644 --- a/src/powerflow_algorithm/BaseFDPFAlgo.h +++ b/src/powerflow_algorithm/BaseFDPFAlgo.h @@ -51,6 +51,8 @@ class BaseFDPFAlgo: public BaseAlgo // solution of the problem Bp_ = Eigen::SparseMatrix (); // the B prime matrix (size n_pvpq) Bpp_ = Eigen::SparseMatrix(); // the B double prime matrix (size n_pq) + grid_Bp_ = Eigen::SparseMatrix (); // the B prime matrix (size n_pvpq) + grid_Bpp_ = Eigen::SparseMatrix(); // the B double prime matrix (size n_pq) p_ = RealVect(); q_ = RealVect(); need_factorize_ = true; @@ -200,6 +202,8 @@ class BaseFDPFAlgo: public BaseAlgo LinearSolver _linear_solver_Bpp; // solution of the problem + Eigen::SparseMatrix grid_Bp_; + Eigen::SparseMatrix grid_Bpp_; Eigen::SparseMatrix Bp_; // the B prime matrix (size n_pvpq) Eigen::SparseMatrix Bpp_; // the B double prime matrix (size n_pq) RealVect p_; // (size n_pvpq) diff --git a/src/powerflow_algorithm/BaseFDPFAlgo.tpp b/src/powerflow_algorithm/BaseFDPFAlgo.tpp index 2e426cb..5d6a74d 100644 --- a/src/powerflow_algorithm/BaseFDPFAlgo.tpp +++ b/src/powerflow_algorithm/BaseFDPFAlgo.tpp @@ -77,12 +77,12 @@ bool BaseFDPFAlgo::compute_pf(const Eigen::SparseMatrix grid_Bp; - Eigen::SparseMatrix grid_Bpp; - fillBp_Bpp(grid_Bp, grid_Bpp); + // need to extract Bp and Bpp for the whole grid + grid_Bp_ = Eigen::SparseMatrix (); + grid_Bpp_ = Eigen::SparseMatrix(); + fillBp_Bpp(grid_Bp_, grid_Bpp_); } - + // init "my" matrices // fill the solver matrices Bp_ and Bpp_ // Bp_ = Bp[array([pvpq]).T, pvpq].tocsc() @@ -97,12 +97,11 @@ bool BaseFDPFAlgo::compute_pf(const Eigen::SparseMatrix pvpq_inv(V.size(), -1); for(int inv_id=0; inv_id < n_pvpq; ++inv_id) pvpq_inv[pvpq(inv_id)] = inv_id; std::vector pq_inv(V.size(), -1); for(int inv_id=0; inv_id < n_pq; ++inv_id) pq_inv[pq(inv_id)] = inv_id; - fill_sparse_matrices(grid_Bp, grid_Bpp, pvpq_inv, pq_inv, n_pvpq, n_pq); + fill_sparse_matrices(grid_Bp_, grid_Bpp_, pvpq_inv, pq_inv, n_pvpq, n_pq); } V_ = V; // V = V0 From ffcb56481d6bac80cb9749c20ca156a0c3fe2790 Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 15 Mar 2024 13:45:03 +0100 Subject: [PATCH 41/42] fixing some issues --- src/powerflow_algorithm/BaseFDPFAlgo.tpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/powerflow_algorithm/BaseFDPFAlgo.tpp b/src/powerflow_algorithm/BaseFDPFAlgo.tpp index 5d6a74d..acfe9fb 100644 --- a/src/powerflow_algorithm/BaseFDPFAlgo.tpp +++ b/src/powerflow_algorithm/BaseFDPFAlgo.tpp @@ -10,15 +10,15 @@ template bool BaseFDPFAlgo::compute_pf(const Eigen::SparseMatrix & Ybus, - CplxVect & V, - const CplxVect & Sbus, - const Eigen::VectorXi & slack_ids, - const RealVect & slack_weights, - const Eigen::VectorXi & pv, - const Eigen::VectorXi & pq, - int max_iter, - real_type tol - ) + CplxVect & V, + const CplxVect & Sbus, + const Eigen::VectorXi & slack_ids, + const RealVect & slack_weights, + const Eigen::VectorXi & pv, + const Eigen::VectorXi & pq, + int max_iter, + real_type tol + ) { /** This method uses the newton raphson algorithm to compute voltage angles and magnitudes at each bus @@ -75,7 +75,7 @@ bool BaseFDPFAlgo::compute_pf(const Eigen::SparseMatrix (); From 9bb7f7caac9600ad7db42788ac9f9a03591c482e Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 15 Mar 2024 16:34:42 +0100 Subject: [PATCH 42/42] fix a bug introduced by refactoring --- lightsim2grid/lightSimBackend.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 4b29741..a7c6319 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -71,18 +71,6 @@ def __init__(self, self._loader_method = loader_method self._loader_kwargs = loader_kwargs - - #: .. versionadded:: 0.8.0 - #: Which type of grid format can be read by your backend. - #: It is "json" if loaded from pandapower or - #: "xiidm" if loaded from pypowsybl. - self.supported_grid_format = None - if loader_method == "pandapower": - self.supported_grid_format = ("json", ) # new in 0.8.0 - elif loader_method == "pypowsybl": - self.supported_grid_format = ("xiidm", ) # new in 0.8.0 - else: - raise BackendError(f"Uknown loader_metho : '{loader_method}'") #: .. versionadded:: 0.8.0 #: if set to `True` (default) then the backend will raise a @@ -107,6 +95,18 @@ def __init__(self, stop_if_load_disco, stop_if_gen_disco) + #: .. versionadded:: 0.8.0 + #: Which type of grid format can be read by your backend. + #: It is "json" if loaded from pandapower or + #: "xiidm" if loaded from pypowsybl. + self.supported_grid_format = None + if loader_method == "pandapower": + self.supported_grid_format = ("json", ) # new in 0.8.0 + elif loader_method == "pypowsybl": + self.supported_grid_format = ("xiidm", ) # new in 0.8.0 + else: + raise BackendError(f"Uknown loader_metho : '{loader_method}'") + # lazy loading because it crashes... from lightsim2grid._utils import _DoNotUseAnywherePandaPowerBackend from grid2op.Space import GridObjects # lazy import