From 1f2e30f416de779a712c0ac44f3e63906269c186 Mon Sep 17 00:00:00 2001 From: Jerry Guo Date: Thu, 14 Nov 2024 11:20:49 +0100 Subject: [PATCH 1/5] init branch Signed-off-by: Jerry Guo From fa55bb42dec65062a6c3deec77607d8fe2084a83 Mon Sep 17 00:00:00 2001 From: Jerry Guo Date: Fri, 15 Nov 2024 17:39:57 +0100 Subject: [PATCH 2/5] made control side information available to trafo graph Signed-off-by: Jerry Guo --- .../optimizer/tap_position_optimizer.hpp | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp index 599380d42..1f9024ec3 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -69,9 +70,35 @@ constexpr TrafoGraphEdge unregulated_edge_prop = {unregulated_idx, 0}; using TrafoGraphEdges = std::vector>; using TrafoGraphEdgeProperties = std::vector; +struct RegulatedTrafoProperties { + Idx id{}; + ControlSide control_side{}; + + bool operator<(const RegulatedTrafoProperties& other) const { + return std::tie(this->id, this->control_side) < std::tie(other.id, other.control_side); + } +}; + struct RegulatedObjects { std::set transformers{}; std::set transformers3w{}; + std::set trafos{}; + std::set trafos3w{}; + + template + std::pair contains(TrafoType const& trafos_set, Idx const& id) const { + auto it = std::find_if(trafos_set.begin(), trafos_set.end(), + [&](const RegulatedTrafoProperties& trafo) { return trafo.id == id; }); + if (it != trafos_set.end()) { + return {true, it->control_side}; + } else { + return {false, ControlSide{}}; + } + } + + std::pair contains_trafo(Idx const& id) const { return contains(trafos, id); } + + std::pair contains_trafo3w(Idx const& id) const { return contains(trafos3w, id); } }; using TransformerGraph = boost::compressed_sparse_row_graph const& sta inline void process_trafo3w_edge(main_core::main_model_state_c auto const& state, ThreeWindingTransformer const& transformer3w, bool const& trafo3w_is_regulated, - Idx2D const& trafo3w_idx, TrafoGraphEdges& edges, + ControlSide const& /*control_side*/, Idx2D const& trafo3w_idx, TrafoGraphEdges& edges, TrafoGraphEdgeProperties& edge_props) { using enum Branch3Side; @@ -130,9 +157,11 @@ constexpr void add_edge(main_core::MainModelState const& sta TrafoGraphEdgeProperties& edge_props) { for (auto const& transformer3w : state.components.template citer()) { - bool const trafo3w_is_regulated = regulated_objects.transformers3w.contains(transformer3w.id()); + // bool const trafo3w_is_regulated = regulated_objects.transformers3w.contains(transformer3w.id()); + auto const trafo3w_is_regulated = regulated_objects.contains_trafo3w(transformer3w.id()); Idx2D const trafo3w_idx = main_core::get_component_idx_by_id(state, transformer3w.id()); - process_trafo3w_edge(state, transformer3w, trafo3w_is_regulated, trafo3w_idx, edges, edge_props); + process_trafo3w_edge(state, transformer3w, trafo3w_is_regulated.first, trafo3w_is_regulated.second, trafo3w_idx, + edges, edge_props); } } @@ -147,10 +176,13 @@ constexpr void add_edge(main_core::MainModelState const& sta } auto const& from_node = transformer.from_node(); auto const& to_node = transformer.to_node(); - if (regulated_objects.transformers.contains(transformer.id())) { + // if (regulated_objects.transformers.contains(transformer.id())) { + auto const trafo_regulated = regulated_objects.contains_trafo(transformer.id()); + if (trafo_regulated.first) { auto const tap_at_from_side = transformer.tap_side() == BranchSide::from; auto const& tap_side_node = tap_at_from_side ? from_node : to_node; auto const& non_tap_side_node = tap_at_from_side ? to_node : from_node; + auto const control_side = trafo_regulated.second; add_to_edge(state, edges, edge_props, tap_side_node, non_tap_side_node, {main_core::get_component_idx_by_id(state, transformer.id()), 1}); } else { @@ -191,10 +223,13 @@ inline auto retrieve_regulator_info(State const& state) -> RegulatedObjects { if (!regulator.status()) { continue; } + auto const control_side = regulator.control_side(); if (regulator.regulated_object_type() == ComponentType::branch) { regulated_objects.transformers.emplace(regulator.regulated_object()); + regulated_objects.trafos.emplace(RegulatedTrafoProperties{regulator.regulated_object(), control_side}); } else { regulated_objects.transformers3w.emplace(regulator.regulated_object()); + regulated_objects.trafos3w.emplace(RegulatedTrafoProperties{regulator.regulated_object(), control_side}); } } return regulated_objects; From fc2efee6c98332b3d64471c57c58b35baeb921cb Mon Sep 17 00:00:00 2001 From: Jerry Guo Date: Sat, 16 Nov 2024 15:15:27 +0100 Subject: [PATCH 3/5] logic edges in place Signed-off-by: Jerry Guo --- .../optimizer/tap_position_optimizer.hpp | 40 +++++++++++++------ .../test_tap_position_optimizer.cpp | 2 +- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp index 1f9024ec3..22181f1de 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp @@ -85,15 +85,14 @@ struct RegulatedObjects { std::set trafos{}; std::set trafos3w{}; - template + template // not really necessary to be templated, but just in case std::pair contains(TrafoType const& trafos_set, Idx const& id) const { auto it = std::find_if(trafos_set.begin(), trafos_set.end(), [&](const RegulatedTrafoProperties& trafo) { return trafo.id == id; }); if (it != trafos_set.end()) { return {true, it->control_side}; - } else { - return {false, ControlSide{}}; } + return {false, ControlSide{}}; // no default invalid control side, won't be used by logic } std::pair contains_trafo(Idx const& id) const { return contains(trafos, id); } @@ -117,13 +116,16 @@ inline void add_to_edge(main_core::MainModelState const& sta inline void process_trafo3w_edge(main_core::main_model_state_c auto const& state, ThreeWindingTransformer const& transformer3w, bool const& trafo3w_is_regulated, - ControlSide const& /*control_side*/, Idx2D const& trafo3w_idx, TrafoGraphEdges& edges, + ControlSide const& control_side, Idx2D const& trafo3w_idx, TrafoGraphEdges& edges, TrafoGraphEdgeProperties& edge_props) { using enum Branch3Side; constexpr std::array, 3> const branch3_combinations{ {{side_1, side_2}, {side_2, side_3}, {side_3, side_1}}}; + auto const tap_at_control_side = [&control_side](Branch3Side const& tap_side) { + return static_cast(control_side) == static_cast(tap_side); + }; for (auto const& [first_side, second_side] : branch3_combinations) { if (!transformer3w.status(first_side) || !transformer3w.status(second_side)) { continue; @@ -133,16 +135,23 @@ inline void process_trafo3w_edge(main_core::main_model_state_c auto const& state auto const tap_at_first_side = transformer3w.tap_side() == first_side; auto const single_direction_condition = - trafo3w_is_regulated && (tap_at_first_side || transformer3w.tap_side() == second_side); - // ranking + trafo3w_is_regulated && (tap_at_first_side || (transformer3w.tap_side() == second_side)); + + auto const tap_at_control = tap_at_control_side(transformer3w.tap_side()); + + // only add weighted edge if the trafo3w meets the condition if (single_direction_condition) { auto const& tap_side_node = tap_at_first_side ? from_node : to_node; auto const& non_tap_side_node = tap_at_first_side ? to_node : from_node; + auto const edge_from_node = tap_at_control ? non_tap_side_node : tap_side_node; + auto const edge_to_node = tap_at_control ? tap_side_node : non_tap_side_node; // add regulated idx only when the first side node is tap side node. // This is done to add only one directional edge with regulated idx. auto const edge_value = (from_node == tap_side_node) ? unregulated_edge_prop : TrafoGraphEdge{trafo3w_idx, 1}; - add_to_edge(state, edges, edge_props, tap_side_node, non_tap_side_node, edge_value); + // (TODO: jguo) old way, to be removed + // add_to_edge(state, edges, edge_props, tap_side_node, non_tap_side_node, edge_value); + add_to_edge(state, edges, edge_props, edge_from_node, edge_to_node, edge_value); } else { add_to_edge(state, edges, edge_props, from_node, to_node, unregulated_edge_prop); add_to_edge(state, edges, edge_props, to_node, from_node, unregulated_edge_prop); @@ -179,12 +188,19 @@ constexpr void add_edge(main_core::MainModelState const& sta // if (regulated_objects.transformers.contains(transformer.id())) { auto const trafo_regulated = regulated_objects.contains_trafo(transformer.id()); if (trafo_regulated.first) { - auto const tap_at_from_side = transformer.tap_side() == BranchSide::from; - auto const& tap_side_node = tap_at_from_side ? from_node : to_node; - auto const& non_tap_side_node = tap_at_from_side ? to_node : from_node; + // (TODO: jguo) old way, to be removed + // auto const tap_at_from_side = transformer.tap_side() == BranchSide::from; + // auto const& tap_side_node = tap_at_from_side ? from_node : to_node; + // auto const& non_tap_side_node = tap_at_from_side ? to_node : from_node; auto const control_side = trafo_regulated.second; - add_to_edge(state, edges, edge_props, tap_side_node, non_tap_side_node, - {main_core::get_component_idx_by_id(state, transformer.id()), 1}); + auto const control_side_node = control_side == ControlSide::from ? from_node : to_node; + auto const non_control_side_node = control_side == ControlSide::from ? to_node : from_node; + auto const trafo_idx = main_core::get_component_idx_by_id(state, transformer.id()); + + // (TODO: jguo) old way, to be removed + // add_to_edge(state, edges, edge_props, tap_side_node, non_tap_side_node, + // {main_core::get_component_idx_by_id(state, transformer.id()), 1}); + add_to_edge(state, edges, edge_props, non_control_side_node, control_side_node, {trafo_idx, 1}); } else { add_to_edge(state, edges, edge_props, from_node, to_node, unregulated_edge_prop); add_to_edge(state, edges, edge_props, to_node, from_node, unregulated_edge_prop); diff --git a/tests/cpp_unit_tests/test_tap_position_optimizer.cpp b/tests/cpp_unit_tests/test_tap_position_optimizer.cpp index f0dce52f3..672202457 100644 --- a/tests/cpp_unit_tests/test_tap_position_optimizer.cpp +++ b/tests/cpp_unit_tests/test_tap_position_optimizer.cpp @@ -337,7 +337,7 @@ TEST_CASE("Test Transformer ranking") { SUBCASE("Ranking complete the graph") { pgm_tap::RankedTransformerGroups order = pgm_tap::rank_transformers(state); pgm_tap::RankedTransformerGroups const ref_order{ - {Idx2D{3, 0}, Idx2D{3, 1}, Idx2D{4, 0}, Idx2D{3, 3}, Idx2D{3, 2}, Idx2D{3, 4}}}; + {Idx2D{3, 0}, Idx2D{3, 1}, Idx2D{3, 3}, Idx2D{4, 0}, Idx2D{3, 2}, Idx2D{3, 4}}}; CHECK(order == ref_order); } } From 3a910f5288d42fb167003ac737d01b3ae20edbc1 Mon Sep 17 00:00:00 2001 From: Jerry Guo Date: Sun, 17 Nov 2024 15:53:23 +0100 Subject: [PATCH 4/5] minor re-organize Signed-off-by: Jerry Guo --- .../optimizer/tap_position_optimizer.hpp | 45 ++++++++++--------- .../test_tap_position_optimizer.cpp | 1 + 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp index 22181f1de..49282bc44 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp @@ -74,30 +74,31 @@ struct RegulatedTrafoProperties { Idx id{}; ControlSide control_side{}; - bool operator<(const RegulatedTrafoProperties& other) const { - return std::tie(this->id, this->control_side) < std::tie(other.id, other.control_side); - } + auto operator<=>(RegulatedTrafoProperties const& other) const = default; }; -struct RegulatedObjects { - std::set transformers{}; - std::set transformers3w{}; - std::set trafos{}; - std::set trafos3w{}; - - template // not really necessary to be templated, but just in case - std::pair contains(TrafoType const& trafos_set, Idx const& id) const { - auto it = std::find_if(trafos_set.begin(), trafos_set.end(), - [&](const RegulatedTrafoProperties& trafo) { return trafo.id == id; }); - if (it != trafos_set.end()) { - return {true, it->control_side}; - } - return {false, ControlSide{}}; // no default invalid control side, won't be used by logic - } +using RegulatedTrafos = std::set; - std::pair contains_trafo(Idx const& id) const { return contains(trafos, id); } +inline std::pair regulated_trafos_contain(RegulatedTrafos const& trafos_set, Idx const& id) { + if (auto it = std::find_if(trafos_set.begin(), trafos_set.end(), + [&](RegulatedTrafoProperties const& trafo) { return trafo.id == id; }); + it != trafos_set.end()) { + return {true, it->control_side}; + } + return {false, ControlSide{}}; // no default invalid control side, won't be used by logic +} - std::pair contains_trafo3w(Idx const& id) const { return contains(trafos3w, id); } +struct RegulatedObjects { + // (TODO: jguo) old way, to be removed + // std::set transformers{}; + // std::set transformers3w{}; + RegulatedTrafos trafos{}; + RegulatedTrafos trafos3w{}; + + std::pair contains_trafo(Idx const& id) const { return regulated_trafos_contain(trafos, id); } + std::pair contains_trafo3w(Idx const& id) const { + return regulated_trafos_contain(trafos3w, id); + } }; using TransformerGraph = boost::compressed_sparse_row_graph RegulatedObjects { } auto const control_side = regulator.control_side(); if (regulator.regulated_object_type() == ComponentType::branch) { - regulated_objects.transformers.emplace(regulator.regulated_object()); + // regulated_objects.transformers.emplace(regulator.regulated_object()); regulated_objects.trafos.emplace(RegulatedTrafoProperties{regulator.regulated_object(), control_side}); } else { - regulated_objects.transformers3w.emplace(regulator.regulated_object()); + // regulated_objects.transformers3w.emplace(regulator.regulated_object()); regulated_objects.trafos3w.emplace(RegulatedTrafoProperties{regulator.regulated_object(), control_side}); } } diff --git a/tests/cpp_unit_tests/test_tap_position_optimizer.cpp b/tests/cpp_unit_tests/test_tap_position_optimizer.cpp index 672202457..46b10b4c3 100644 --- a/tests/cpp_unit_tests/test_tap_position_optimizer.cpp +++ b/tests/cpp_unit_tests/test_tap_position_optimizer.cpp @@ -256,6 +256,7 @@ TEST_CASE("Test Transformer ranking") { CHECK(actual_edges_prop == expected_edges_prop); } + // (TODO: jguo) old way, to be removed SUBCASE("Automatic tap unsupported tap side at LV") { TestState bad_state; std::vector bad_nodes{{0, 50e3}, {1, 10e3}}; From 04b173c670387497b13d428e420df1abf3ea5b76 Mon Sep 17 00:00:00 2001 From: Jerry Guo Date: Sun, 17 Nov 2024 16:30:49 +0100 Subject: [PATCH 5/5] tap side and control side made available to ranking Signed-off-by: Jerry Guo --- .../include/power_grid_model/common/enum.hpp | 26 +++++++++++++++++++ .../optimizer/tap_position_optimizer.hpp | 18 ++++++++----- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp index e24291164..2be8ac245 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/enum.hpp @@ -20,6 +20,32 @@ enum class BranchSide : IntS { from = 0, to = 1 }; enum class Branch3Side : IntS { side_1 = 0, side_2 = 1, side_3 = 2 }; +enum class TapSide : IntS { from = 0, to = 1, side_1 = 0, side_2 = 1, side_3 = 2 }; + +inline TapSide branch_side_to_tap_side(BranchSide branch_side) { + switch (branch_side) { + case BranchSide::from: + return TapSide::from; + case BranchSide::to: + return TapSide::to; + default: + throw std::invalid_argument("branch_side_to_tap_side: Invalid BranchSide value"); + } +} + +inline TapSide branch_3_side_to_tap_side(Branch3Side branch3_side) { + switch (branch3_side) { + case Branch3Side::side_1: + return TapSide::side_1; + case Branch3Side::side_2: + return TapSide::side_2; + case Branch3Side::side_3: + return TapSide::side_3; + default: + throw std::invalid_argument("branch_3_side_to_tap_side: Invalid Branch3Side value"); + } +} + enum class ControlSide : IntS { from = 0, to = 1, side_1 = 0, side_2 = 1, side_3 = 2 }; enum class CalculationType : IntS { power_flow = 0, state_estimation = 1, short_circuit = 2 }; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp index 49282bc44..f4b843f7f 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/optimizer/tap_position_optimizer.hpp @@ -51,6 +51,9 @@ struct TrafoGraphEdge { Idx2D regulated_idx{}; EdgeWeight weight{}; + TapSide tap_side{}; + ControlSide control_side{}; + bool operator==(const TrafoGraphEdge& other) const { return regulated_idx == other.regulated_idx && weight == other.weight; } // thanks boost @@ -66,7 +69,7 @@ struct TrafoGraphEdge { } }; -constexpr TrafoGraphEdge unregulated_edge_prop = {unregulated_idx, 0}; +constexpr TrafoGraphEdge unregulated_edge_prop = {unregulated_idx, 0, TapSide::from, ControlSide::from}; using TrafoGraphEdges = std::vector>; using TrafoGraphEdgeProperties = std::vector; @@ -74,14 +77,14 @@ struct RegulatedTrafoProperties { Idx id{}; ControlSide control_side{}; - auto operator<=>(RegulatedTrafoProperties const& other) const = default; + auto operator<=>(RegulatedTrafoProperties const& other) const = default; // NOLINT(modernize-use-nullptr) }; using RegulatedTrafos = std::set; inline std::pair regulated_trafos_contain(RegulatedTrafos const& trafos_set, Idx const& id) { - if (auto it = std::find_if(trafos_set.begin(), trafos_set.end(), - [&](RegulatedTrafoProperties const& trafo) { return trafo.id == id; }); + if (auto it = + std::ranges::find_if(trafos_set, [&](RegulatedTrafoProperties const& trafo) { return trafo.id == id; }); it != trafos_set.end()) { return {true, it->control_side}; } @@ -149,7 +152,9 @@ inline void process_trafo3w_edge(main_core::main_model_state_c auto const& state // add regulated idx only when the first side node is tap side node. // This is done to add only one directional edge with regulated idx. auto const edge_value = - (from_node == tap_side_node) ? unregulated_edge_prop : TrafoGraphEdge{trafo3w_idx, 1}; + (from_node == tap_side_node) + ? unregulated_edge_prop + : TrafoGraphEdge{trafo3w_idx, 1, branch_3_side_to_tap_side(transformer3w.tap_side()), control_side}; // (TODO: jguo) old way, to be removed // add_to_edge(state, edges, edge_props, tap_side_node, non_tap_side_node, edge_value); add_to_edge(state, edges, edge_props, edge_from_node, edge_to_node, edge_value); @@ -201,7 +206,8 @@ constexpr void add_edge(main_core::MainModelState const& sta // (TODO: jguo) old way, to be removed // add_to_edge(state, edges, edge_props, tap_side_node, non_tap_side_node, // {main_core::get_component_idx_by_id(state, transformer.id()), 1}); - add_to_edge(state, edges, edge_props, non_control_side_node, control_side_node, {trafo_idx, 1}); + add_to_edge(state, edges, edge_props, non_control_side_node, control_side_node, + {trafo_idx, 1, branch_side_to_tap_side(transformer.tap_side()), control_side}); } else { add_to_edge(state, edges, edge_props, from_node, to_node, unregulated_edge_prop); add_to_edge(state, edges, edge_props, to_node, from_node, unregulated_edge_prop);