From 98b748a3600875dc46a912d33711ea910a7173ed Mon Sep 17 00:00:00 2001 From: DONNOT Benjamin Date: Fri, 22 Mar 2024 18:22:20 +0100 Subject: [PATCH] prep the addition of another reward to compute N-1 --- CHANGELOG.rst | 11 +- docs/conf.py | 2 +- docs/index.rst | 1 + docs/quickstart.rst | 2 +- docs/rewards.rst | 13 ++ lightsim2grid/__init__.py | 13 +- lightsim2grid/contingencyAnalysis.py | 46 ++++--- lightsim2grid/gridmodel/from_pypowsybl.py | 4 +- lightsim2grid/lightSimBackend.py | 23 ++-- lightsim2grid/rewards/__init__.py | 11 ++ lightsim2grid/rewards/n1ContingencyReward.py | 124 +++++++++++++++++++ setup.py | 2 +- src/GridModel.cpp | 27 ---- src/batch_algorithm/BaseBatchSolverSynch.h | 6 +- src/batch_algorithm/ContingencyAnalysis.h | 6 + src/element_container/GeneratorContainer.cpp | 31 +++-- src/main.cpp | 1 + 17 files changed, 243 insertions(+), 80 deletions(-) create mode 100644 docs/rewards.rst create mode 100644 lightsim2grid/rewards/__init__.py create mode 100644 lightsim2grid/rewards/n1ContingencyReward.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0cab730..43d53b1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -22,12 +22,21 @@ Change Log -------------------- - [FIXED] a bug with shunts when `nb_busbar_per_sub` >= 2 - [FIXED] some bugs preventing backward compatibility +- [FIXED] an issue in the computation of gen_q when intialized with pypowsybl + (some overflow cpp side leading to infinite number in gen_q) +- [ADDED] some information of compilation directly in the cpp module +- [ADDED] some information of compilation available in the python `compilation_options` + module python side +- [ADDED] some convenient methods for `ContingencyAnalysis` python side (most + notably the possibility to initialize it from a `LightSimBackend` and to + change the topology of the grid) +- [ADDED] a "reward" module in lightsim2grid with custom reward + based on lightsim2grid. - [IMPROVED] time measurments in python and c++ - [IMPROVED] now test lightsim2grid with oldest grid2op version - [IMPROVED] speed, by accelerating the reading back of the data (now read only once and then pointers are re used) - [IMPROVED] c++ side avoid allocating memory (which allow to gain speed python side too) -- [ADDED] some information of compilation directly in the cpp module [0.8.0] 2024-03-18 -------------------- diff --git a/docs/conf.py b/docs/conf.py index 8aa9cf9..39bcd89 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,7 +22,7 @@ author = 'Benjamin DONNOT' # The full version, including alpha/beta/rc tags -release = "0.8.1.dev0" +release = "0.8.1.dev1" version = '0.8' # -- General configuration --------------------------------------------------- diff --git a/docs/index.rst b/docs/index.rst index f13c36d..66f4dcd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,6 +30,7 @@ As from version 0.5.3: use_with_grid2op benchmarks use_solver + rewards physical_law_checker diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 46b4ba4..7ab2224 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -34,7 +34,7 @@ this way: from grid2op.Agent import RandomAgent # create an environment - env_name = "rte_case14_realistic" # for example, other environments might be usable + env_name = "l2rpn_case14_sandbox" # for example, other environments might be usable env = grid2op.make(env_name, backend=LightSimBackend() # this is the only change you have to make! ) diff --git a/docs/rewards.rst b/docs/rewards.rst new file mode 100644 index 0000000..db2593b --- /dev/null +++ b/docs/rewards.rst @@ -0,0 +1,13 @@ +Custom Rewards (doc in progress) +======================================= + +Detailed usage +-------------------------- + +.. automodule:: lightsim2grid.rewards + :members: + :autosummary: + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` \ No newline at end of file diff --git a/lightsim2grid/__init__.py b/lightsim2grid/__init__.py index d60f9ae..e1e682f 100644 --- a/lightsim2grid/__init__.py +++ b/lightsim2grid/__init__.py @@ -1,13 +1,13 @@ -# 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, # 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.8.1.dev0" +__version__ = "0.8.1.dev1" -__all__ = ["newtonpf", "SolverType", "ErrorType", "solver"] +__all__ = ["newtonpf", "SolverType", "ErrorType", "solver", "compilation_options"] # import directly from c++ module from lightsim2grid.solver import SolverType @@ -46,3 +46,10 @@ # grid2op is not installed, the SecurtiyAnalysis module will not be available pass print(f"ContingencyAnalysis import error: {exc_}") + +try: + __all__.append("rewards") +except ImportError as exc_: + # grid2op is not installed, the SecurtiyAnalysis module will not be available + pass + print(f"ContingencyAnalysis import error: {exc_}") diff --git a/lightsim2grid/contingencyAnalysis.py b/lightsim2grid/contingencyAnalysis.py index f16a504..77b5c79 100644 --- a/lightsim2grid/contingencyAnalysis.py +++ b/lightsim2grid/contingencyAnalysis.py @@ -76,10 +76,20 @@ class ContingencyAnalysis(object): 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) + from grid2op.Environment import Environment + if isinstance(grid2op_env, Environment): + if not isinstance(grid2op_env.backend, LightSimBackend): + raise RuntimeError("This class only works with LightSimBackend") + self._ls_backend = grid2op_env.backend.copy() + elif isinstance(grid2op_env, LightSimBackend): + if hasattr(grid2op_env, "_is_loaded") and not grid2op_env._is_loaded: + raise RuntimeError("Impossible to init a `ContingencyAnalysis` " + "with a backend that has not been initialized.") + self._ls_backend = grid2op_env.copy() + else: + raise RuntimeError("`ContingencyAnalysis` can only be created " + "with a grid2op `Environment` or a `LightSimBackend`") + self.computer = ContingencyAnalysisCPP(self._ls_backend._grid) self._contingency_order = {} # key: contingency (as tuple), value: order in which it is entered self._all_contingencies = [] self.__computed = False @@ -100,16 +110,24 @@ 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): + def update_grid(self, backend_act): + self.clear(with_contlist=False) + self._ls_backend.apply_action(backend_act) + self.computer = ContingencyAnalysisCPP(self._ls_backend._grid) + + def clear(self, with_contlist=True): """ 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 = [] + if with_contlist: + self.computer.clear() + self._contingency_order = {} + self._all_contingencies = [] + else: + self.computer.clear_results_only() def _single_cont_to_li_int(self, single_cont): li_disc = [] @@ -118,7 +136,7 @@ def _single_cont_to_li_int(self, single_cont): for stuff in single_cont: if isinstance(stuff, type(self).STR_TYPES): - stuff = np.where(self.grid2op_env.name_line == stuff) + stuff = np.where(type(self._ls_backend).name_line == stuff) stuff = stuff[0] if stuff.size == 0: # name is not found @@ -233,7 +251,7 @@ def add_all_n1_contingencies(self): 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): + for single_cont_id in range(type(self._ls_backend).n_line): self.add_single_contingency(single_cont_id) def get_flows(self, *args): @@ -302,10 +320,10 @@ def compute_V(self): been entered. Please use `get_flows()` method for easier reading back of the results """ - v_init = self.grid2op_env.backend.V + v_init = self._ls_backend.V self.computer.compute(v_init, - self.grid2op_env.backend.max_it, - self.grid2op_env.backend.tol) + self._ls_backend.max_it, + self._ls_backend.tol) self._vs = self.computer.get_voltages() self.__computed = True return self._vs @@ -342,6 +360,6 @@ def compute_P(self): def close(self): """permanently close the object""" - self.grid2op_env.close() + self._ls_backend.close() self.clear() self.computer.close() diff --git a/lightsim2grid/gridmodel/from_pypowsybl.py b/lightsim2grid/gridmodel/from_pypowsybl.py index 6501ead..798742c 100644 --- a/lightsim2grid/gridmodel/from_pypowsybl.py +++ b/lightsim2grid/gridmodel/from_pypowsybl.py @@ -78,8 +78,8 @@ def init(net : pypo.network, # to handle encoding in 32 bits and overflow when "splitting" the Q values among min_q = df_gen["min_q"].values.astype(np.float32) max_q = df_gen["max_q"].values.astype(np.float32) - min_q[~np.isfinite(min_q)] = np.finfo(np.float32).min * 0.5 + 1. - max_q[~np.isfinite(max_q)] = np.finfo(np.float32).max * 0.5 - 1. + min_q[~np.isfinite(min_q)] = np.finfo(np.float32).min * 1e-4 + 1. + max_q[~np.isfinite(max_q)] = np.finfo(np.float32).max * 1e-4 - 1. gen_bus, gen_disco = _aux_get_bus(bus_df, df_gen) model.init_generators_full(df_gen["target_p"].values, df_gen["target_v"].values / voltage_levels.loc[df_gen["voltage_level_id"].values]["nominal_v"].values, diff --git a/lightsim2grid/lightSimBackend.py b/lightsim2grid/lightSimBackend.py index 8a1934f..8b9e92d 100644 --- a/lightsim2grid/lightSimBackend.py +++ b/lightsim2grid/lightSimBackend.py @@ -215,9 +215,9 @@ def __init__(self, # 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 + #: 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. @@ -247,6 +247,7 @@ def __init__(self, self._trafo_hv_res = None self._trafo_lv_res = None self._storage_res = None + self._reset_res_pointers() def _aux_init_super(self, detailed_infos_for_cascading_failures, @@ -615,6 +616,7 @@ def _load_grid_pypowsybl(self, path=None, filename=None): 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) + self.name_sub = np.array(buses_sub_id.index) if "reconnect_disco_gen" in loader_kwargs and loader_kwargs["reconnect_disco_gen"]: for el in self._grid.get_generators(): @@ -1346,19 +1348,9 @@ def get_line_status(self): def get_line_flow(self): return self.a_or - - def _grid2op_bus_from_klu_bus(self, klu_bus): - res = 0 - if klu_bus != 0: - # object is connected - res = 1 if klu_bus < self.__nb_bus_before else 2 - return res - - def _klu_bus_from_grid2op_bus(self, grid2op_bus, grid2op_bus_init): - return grid2op_bus_init[grid2op_bus - 1] - + def get_topo_vect(self): - return self.topo_vect + return 1 * self.topo_vect def generators_info(self): return self.cst_1 * self.prod_p, self.cst_1 * self.prod_q, self.cst_1 * self.prod_v @@ -1430,5 +1422,6 @@ def reset(self, grid_path, grid_filename=None): self._timer_fetch_data_cpp = 0. self._timer_apply_act = 0. self._grid.tell_solver_need_reset() + self._reset_res_pointers() self.sh_bus[:] = 1 # TODO self._compute_shunt_bus_with_compat(self._grid.get_all_shunt_buses()) self.topo_vect[:] = self.__init_topo_vect # TODO diff --git a/lightsim2grid/rewards/__init__.py b/lightsim2grid/rewards/__init__.py new file mode 100644 index 0000000..1515cc5 --- /dev/null +++ b/lightsim2grid/rewards/__init__.py @@ -0,0 +1,11 @@ +# 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, +# 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__ = ["N1ContingencyReward"] + +from lightsim2grid.rewards.n1ContingencyReward import N1ContingencyReward diff --git a/lightsim2grid/rewards/n1ContingencyReward.py b/lightsim2grid/rewards/n1ContingencyReward.py new file mode 100644 index 0000000..7fe755d --- /dev/null +++ b/lightsim2grid/rewards/n1ContingencyReward.py @@ -0,0 +1,124 @@ +# 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, +# 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. + +from grid2op.Reward import BaseReward +from grid2op.Environment import Environment +from grid2op.Backend import PandaPowerBackend +from grid2op.Action._backendAction import _BackendAction + +from lightsim2grid import LightSimBackend, ContingencyAnalysis +from lightsim2grid.compilation_options import klu_solver_available +from lightsim2grid.solver import SolverType + + +class N1ContingencyReward(BaseReward): + """ + This class implements a reward that leverage the :class:`lightsim2grid.ContingencyAnalysis` + to compute the number of unsafe contingency at any given time. + + Examples + -------- + + This can be used as: + + .. code-block:: python + + import grid2op + from lightsim2grid.rewards import N1ContingencyReward + l_ids = [0, 1, 7] + env = grid2op.make("l2rpn_case14_sandbox", + reward_class=N1ContingencyReward(l_ids=l_ids) + ) + obs = env.reset() + obs, reward, *_ = env.step(env.action_space()) + print(f"reward: {reward:.3f}") + + """ + + def __init__(self, + l_ids=None, + threshold_margin=1., + dc=False, + normalize=False, + logger=None,): + BaseReward.__init__(self, logger=logger) + self._backend = None + self._backend_action = None + self._l_ids = None + self._dc = dc + self._normalize = normalize + if l_ids is not None: + self._l_ids = [int(el) for el in l_ids] + self._threshold_margin = float(threshold_margin) + if klu_solver_available: + if self._dc: + self._solver_type = SolverType.KLUDC + else: + self._solver_type = SolverType.KLU + else: + if self._dc: + self._solver_type = SolverType.DC + else: + self._solver_type = SolverType.SparseLU + + def initialize(self, env: Environment): + if not isinstance(env.backend, (PandaPowerBackend, LightSimBackend)): + raise RuntimeError("Impossible to use the `N1ContingencyReward` with " + "a environment with a backend that is not " + "``PandaPowerBackend` nor `LightSimBackend`." + ) + if isinstance(env.backend, LightSimBackend): + self._backend = env.backend.copy() + elif isinstance(env.backend, PandaPowerBackend): + from lightsim2grid.gridmodel import init + self._backend = init(env.backend) + else: + raise NotImplementedError() + + bk_act_cls = _BackendAction.init_grid(env.backend) + self._backend_action = bk_act_cls() + if self._l_ids is None: + self._l_ids = list(range(type(env).n_line)) + + self.reward_min = 0. + self.reward_max = type(env).n_line if not self._normalize else 1. + self._contingecy_analyzer = ContingencyAnalysis(self._backend) + self._contingecy_analyzer.add_multiple_contingencies(self._l_ids) + + def __call__(self, action, env, has_error, is_done, is_illegal, is_ambiguous): + if is_done: + return self.reward_min + self._backend_action.reset() + act = env.backend.get_action_to_set() + th_lim = env.get_thermal_limit() + th_lim[th_lim <= 1] = 1 # assign 1 for the thermal limit + this_n1 = copy.deepcopy(act) + self._backend_action += this_n1 + + self._backend.apply_action(self._backend_action) + self._backend._disconnect_line(self.l_id) + div_exc_ = None + try: + # TODO there is a bug in lightsimbackend that make it crash instead of diverging + conv, div_exc_ = self._backend.runpf() + except Exception as exc_: + conv = False + div_exc_ = exc_ + + if conv: + flow = self._backend.get_line_flow() + res = (flow / th_lim).max() + else: + self.logger.info(f"Divergence of the backend at step {env.nb_time_step} for N1Reward with error `{div_exc_}`") + res = self.reward_min + return res + + def close(self): + self._backend.close() + del self._backend + self._backend = None diff --git a/setup.py b/setup.py index 4e0b1e6..2c396d5 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ from pybind11.setup_helpers import Pybind11Extension, build_ext -__version__ = "0.8.1.dev0" +__version__ = "0.8.1.dev1" KLU_SOLVER_AVAILABLE = False # Try to link against SuiteSparse (if available) diff --git a/src/GridModel.cpp b/src/GridModel.cpp index 068e235..1ca5fe0 100644 --- a/src/GridModel.cpp +++ b/src/GridModel.cpp @@ -387,20 +387,17 @@ CplxVect GridModel::ac_pf(const CplxVect & Vinit, solver_control_); // start the solver - // std::cout << "\tbefore get_slack_weights (ac)" << 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 << "\tbefore 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) - // std::cout << "\tbefore process_results" << std::endl; process_results(conv, res, Vinit, true, id_me_to_ac_solver_); timer_last_ac_pf_ = timer.duration(); @@ -535,47 +532,33 @@ 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()){ - // std::cout << "\t\tslack_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 - - // 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()){ - // std::cout << "\t\tinit_Ybus;" << std::endl; 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()){ - // std::cout << "\t\tfillYbus;" << 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 << "\t\tinit_Sbus;" << std::endl; 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()) { - // std::cout << "\t\tinit_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 << "\t\tfillpv_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; } if (is_ac && (solver_control.need_reset_solver() || @@ -583,14 +566,12 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, solver_control.need_recompute_sbus() || // TODO do we need it ? solver_control.has_pq_changed()) // TODO do we need it ? ){ - // std::cout << "\t\tq vector" << 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.); 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; } if (solver_control.need_reset_solver() || @@ -598,11 +579,8 @@ CplxVect GridModel::pre_process_solver(const CplxVect & Vinit, solver_control.has_slack_participate_changed() || 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){ @@ -612,11 +590,6 @@ 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 << "pre_process_solver: V result: "< & Va, 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_); v_deg_from_va(Va, Vm, status_, nb_gen, bus_id_, id_grid_to_solver, bus_vn_kv, res_theta_); - res_p_ = p_mw_; + for(int gen_id = 0; gen_id < nb_gen; ++gen_id){ + if(!status_[gen_id]){ + res_p_(gen_id) = 0.; + continue; + } + res_p_(gen_id) = p_mw_(gen_id); + } } void GeneratorContainer::reset_results(){ @@ -227,7 +233,6 @@ void GeneratorContainer::reset_results(){ res_q_ = RealVect(nb()); // in MVar res_v_ = RealVect(nb()); // in kV res_theta_ = RealVect(nb()); // in deg - // bus_slack_weight_ = RealVect(); } void GeneratorContainer::get_vm_for_dc(RealVect & Vm){ @@ -381,8 +386,6 @@ void GeneratorContainer::set_p_slack(const RealVect& node_mismatch, // TODO DEBUG MODE: check bus_slack_weight_[bus_id_solver] > 0 const auto total_contrib_slack = bus_slack_weight_(bus_id_solver); const auto my_contrib_slack = gen_slack_weight_[gen_id]; - // now take "my part" - // std::cout << "gen_id " << gen_id << " my_contrib_slack " << my_contrib_slack << ", " << total_contrib_slack << " node_mismatch " << node_mismatch(bus_id_solver) << std::endl; res_p_(gen_id) += node_mismatch(bus_id_solver) * my_contrib_slack / total_contrib_slack; } } @@ -415,9 +418,7 @@ void GeneratorContainer::set_q(const RealVect & reactive_mismatch, const RealVect & total_q_max_per_bus) { const int nb_gen = nb(); - // res_q_ = RealVect::Constant(nb_gen, 0.); - if(!ac) - { + if(!ac){ // do not consider Q values in dc mode for(int gen_id = 0; gen_id < nb_gen; ++gen_id) res_q_(gen_id) = 0.; return; @@ -426,15 +427,22 @@ void GeneratorContainer::set_q(const RealVect & reactive_mismatch, real_type eps_q = 1e-8; for(int gen_id = 0; gen_id < nb_gen; ++gen_id) { - real_type real_q = 0.; if(!status_[gen_id]){ // set at 0 for disconnected generators res_q_(gen_id) = 0.; continue; } - - if (!voltage_regulator_on_[gen_id]) continue; // gen is purposedly not pv - if ((!turnedoff_gen_pv_) && p_mw_(gen_id) == 0.) continue; // in this case turned off generators are not pv + real_type real_q = 0.; + if (!voltage_regulator_on_[gen_id]){ + // gen is purposedly not pv + res_q_(gen_id) = 0.; + continue; + } + if ((!turnedoff_gen_pv_) && p_mw_(gen_id) == 0.) { + // in this case turned off generators are not pv + res_q_(gen_id) = 0.; + continue; + } int bus_id = bus_id_(gen_id); const auto bus_solver = id_grid_to_solver[bus_id]; @@ -455,7 +463,6 @@ void GeneratorContainer::set_q(const RealVect & reactive_mismatch, } } - void GeneratorContainer::update_slack_weights(Eigen::Ref > could_be_slack, SolverControl & solver_control) { diff --git a/src/main.cpp b/src/main.cpp index ba19bd5..b8aac5b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -979,6 +979,7 @@ PYBIND11_MODULE(lightsim2grid_cpp, m) // remove some defaults (TODO) .def("reset", &ContingencyAnalysis::clear, DocSecurityAnalysis::clear.c_str()) .def("clear", &ContingencyAnalysis::clear, DocSecurityAnalysis::clear.c_str()) + .def("clear_results_only", &ContingencyAnalysis::clear_results_only, 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())