6
6
7
7
#include " base_optimizer.hpp"
8
8
9
+ #include " ../all_components.hpp"
9
10
#include " ../auxiliary/dataset.hpp"
10
11
#include " ../common/enum.hpp"
12
+ #include " ../common/exception.hpp"
13
+ #include " ../main_core/state_queries.hpp"
11
14
12
15
#include < boost/graph/compressed_sparse_row_graph.hpp>
16
+
13
17
#include < functional>
14
18
#include < queue>
15
19
#include < vector>
@@ -21,114 +25,239 @@ namespace detail = power_grid_model::optimizer::detail;
21
25
22
26
using TrafoGraphIdx = Idx;
23
27
using EdgeWeight = int64_t ;
24
- using WeightedTrafo = std::pair<Idx2D, EdgeWeight>;
25
- using WeightedTrafoList = std::vector<WeightedTrafo>;
26
- using RankedTransformerGroups = std::vector<std::vector<Idx2D>>;
27
28
constexpr auto infty = std::numeric_limits<Idx>::max();
29
+ constexpr Idx2D unregulated_idx = {-1 , -1 };
28
30
29
31
struct TrafoGraphVertex {
30
- bool is_source{}; // is_source = true if the vertex is a source
32
+ bool is_source{};
31
33
};
32
34
33
35
struct TrafoGraphEdge {
34
- Idx2D pos {};
36
+ Idx2D regulated_idx {};
35
37
EdgeWeight weight{};
38
+
39
+ bool operator ==(const TrafoGraphEdge& other) const {
40
+ return regulated_idx == other.regulated_idx && weight == other.weight ;
41
+ } // thanks boost
42
+
43
+ auto operator <=>(const TrafoGraphEdge& other) const {
44
+ if (auto cmp = weight <=> other.weight ; cmp != 0 ) { // NOLINT(modernize-use-nullptr)
45
+ return cmp;
46
+ }
47
+ if (auto cmp = regulated_idx.group <=> other.regulated_idx .group ; cmp != 0 ) { // NOLINT(modernize-use-nullptr)
48
+ return cmp;
49
+ }
50
+ return regulated_idx.pos <=> other.regulated_idx .pos ;
51
+ }
52
+ };
53
+
54
+ using TrafoGraphEdges = std::vector<std::pair<TrafoGraphIdx, TrafoGraphIdx>>;
55
+ using TrafoGraphEdgeProperties = std::vector<TrafoGraphEdge>;
56
+ using RankedTransformerGroups = std::vector<Idx2D>;
57
+
58
+ struct RegulatedObjects {
59
+ std::set<Idx> transformers{};
60
+ std::set<Idx> transformers3w{};
36
61
};
37
62
38
- // TODO(mgovers): investigate whether this really is the correct graph structure
39
63
using TransformerGraph = boost::compressed_sparse_row_graph<boost::directedS, TrafoGraphVertex, TrafoGraphEdge,
40
64
boost::no_property, TrafoGraphIdx, TrafoGraphIdx>;
41
65
66
+ inline void add_to_edge (TrafoGraphEdges& edges, TrafoGraphEdgeProperties& edge_props, Idx const & start, Idx const & end,
67
+ TrafoGraphEdge const & edge_prop) {
68
+ edges.emplace_back (start, end);
69
+ edge_props.emplace_back (edge_prop);
70
+ }
71
+
72
+ inline void process_trafo3w_edge (ThreeWindingTransformer const & transformer3w, bool const & trafo3w_is_regulated,
73
+ Idx2D const & trafo3w_idx, TrafoGraphEdges& edges,
74
+ TrafoGraphEdgeProperties& edge_props) {
75
+ using enum Branch3Side;
76
+
77
+ constexpr std::array<std::tuple<Branch3Side, Branch3Side>, 3 > const branch3_combinations{
78
+ {{side_1, side_2}, {side_2, side_3}, {side_3, side_1}}};
79
+
80
+ for (auto const & [first_side, second_side] : branch3_combinations) {
81
+ if (!transformer3w.status (first_side) || !transformer3w.status (second_side)) {
82
+ continue ;
83
+ }
84
+ auto const & from_node = transformer3w.node (first_side);
85
+ auto const & to_node = transformer3w.node (second_side);
86
+
87
+ auto const tap_at_first_side = transformer3w.tap_side () == first_side;
88
+ auto const single_direction_condition =
89
+ trafo3w_is_regulated && (tap_at_first_side || transformer3w.tap_side () == second_side);
90
+ // ranking
91
+ if (single_direction_condition) {
92
+ auto const & tap_side_node = tap_at_first_side ? from_node : to_node;
93
+ auto const & non_tap_side_node = tap_at_first_side ? to_node : from_node;
94
+ // add regulated idx only when the first side node is tap side node.
95
+ // This is done to add only one directional edge with regulated idx.
96
+ Idx2D const regulated_idx = from_node == tap_side_node ? unregulated_idx : trafo3w_idx;
97
+ add_to_edge (edges, edge_props, tap_side_node, non_tap_side_node, {regulated_idx, 1 });
98
+ } else {
99
+ add_to_edge (edges, edge_props, from_node, to_node, {unregulated_idx, 1 });
100
+ add_to_edge (edges, edge_props, to_node, from_node, {unregulated_idx, 1 });
101
+ }
102
+ }
103
+ }
104
+
105
+ template <std::derived_from<ThreeWindingTransformer> Component, class ComponentContainer >
106
+ requires main_core::model_component_state_c<main_core::MainModelState, ComponentContainer, Component>
107
+ constexpr void add_edge (main_core::MainModelState<ComponentContainer> const & state,
108
+ RegulatedObjects const & regulated_objects, TrafoGraphEdges& edges,
109
+ TrafoGraphEdgeProperties& edge_props) {
110
+
111
+ for (auto const & transformer3w : state.components .template citer <ThreeWindingTransformer>()) {
112
+ bool const trafo3w_is_regulated = regulated_objects.transformers3w .contains (transformer3w.id ());
113
+ Idx2D const trafo3w_idx = main_core::get_component_idx_by_id (state, transformer3w.id ());
114
+ process_trafo3w_edge (transformer3w, trafo3w_is_regulated, trafo3w_idx, edges, edge_props);
115
+ }
116
+ }
117
+
118
+ template <std::derived_from<Transformer> Component, class ComponentContainer >
119
+ requires main_core::model_component_state_c<main_core::MainModelState, ComponentContainer, Component>
120
+ constexpr void add_edge (main_core::MainModelState<ComponentContainer> const & state,
121
+ RegulatedObjects const & regulated_objects, TrafoGraphEdges& edges,
122
+ TrafoGraphEdgeProperties& edge_props) {
123
+ for (auto const & transformer : state.components .template citer <Transformer>()) {
124
+ if (!transformer.from_status () || !transformer.to_status ()) {
125
+ continue ;
126
+ }
127
+ auto const & from_node = transformer.from_node ();
128
+ auto const & to_node = transformer.to_node ();
129
+
130
+ if (regulated_objects.transformers .contains (transformer.id ())) {
131
+ auto const tap_at_from_side = transformer.tap_side () == BranchSide::from;
132
+ auto const & tap_side_node = tap_at_from_side ? from_node : to_node;
133
+ auto const & non_tap_side_node = tap_at_from_side ? to_node : from_node;
134
+ add_to_edge (edges, edge_props, tap_side_node, non_tap_side_node,
135
+ {main_core::get_component_idx_by_id (state, transformer.id ()), 1 });
136
+ } else {
137
+ add_to_edge (edges, edge_props, from_node, to_node, {unregulated_idx, 1 });
138
+ add_to_edge (edges, edge_props, to_node, from_node, {unregulated_idx, 1 });
139
+ }
140
+ }
141
+ }
142
+
143
+ template <std::derived_from<Branch> Component, class ComponentContainer >
144
+ requires main_core::model_component_state_c<main_core::MainModelState, ComponentContainer, Component> &&
145
+ (!transformer_c<Component>)
146
+ constexpr void add_edge (main_core::MainModelState<ComponentContainer> const & state,
147
+ RegulatedObjects const & /* regulated_objects */ , TrafoGraphEdges& edges,
148
+ TrafoGraphEdgeProperties& edge_props) {
149
+ auto const & iter = state.components .template citer <Component>();
150
+ edges.reserve (std::distance (iter.begin (), iter.end ()) * 2 );
151
+ edge_props.reserve (std::distance (iter.begin (), iter.end ()) * 2 );
152
+ for (auto const & branch : iter) {
153
+ if (!branch.from_status () || !branch.to_status ()) {
154
+ continue ;
155
+ }
156
+ add_to_edge (edges, edge_props, branch.from_node (), branch.to_node (), {unregulated_idx, 0 });
157
+ add_to_edge (edges, edge_props, branch.to_node (), branch.from_node (), {unregulated_idx, 0 });
158
+ }
159
+ }
160
+
161
+ template <typename ... ComponentTypes, main_core::main_model_state_c State>
162
+ inline auto add_edges (State const & state, RegulatedObjects const & regulated_objects, TrafoGraphEdges& edges,
163
+ TrafoGraphEdgeProperties& edge_props) {
164
+ (add_edge<ComponentTypes>(state, regulated_objects, edges, edge_props), ...);
165
+ }
166
+
167
+ template <main_core::main_model_state_c State>
168
+ inline auto retrieve_regulator_info (State const & state) -> RegulatedObjects {
169
+ RegulatedObjects regulated_objects;
170
+ for (auto const & regulator : state.components .template citer <TransformerTapRegulator>()) {
171
+ if (!regulator.status ()) {
172
+ continue ;
173
+ }
174
+ if (regulator.regulated_object_type () == ComponentType::branch) {
175
+ regulated_objects.transformers .emplace (regulator.regulated_object ());
176
+ } else {
177
+ regulated_objects.transformers3w .emplace (regulator.regulated_object ());
178
+ }
179
+ }
180
+ return regulated_objects;
181
+ }
182
+
42
183
template <main_core::main_model_state_c State>
43
- inline auto build_transformer_graph (State const & /* state*/ ) -> TransformerGraph {
44
- // TODO(nbharambe): implement
45
- return {};
184
+ inline auto build_transformer_graph (State const & state) -> TransformerGraph {
185
+ TrafoGraphEdges edges;
186
+ TrafoGraphEdgeProperties edge_props;
187
+
188
+ const RegulatedObjects regulated_objects = retrieve_regulator_info (state);
189
+
190
+ add_edges<Transformer, ThreeWindingTransformer, Line, Link>(state, regulated_objects, edges, edge_props);
191
+
192
+ // build graph
193
+ TransformerGraph trafo_graph{boost::edges_are_unsorted_multi_pass, edges.cbegin (), edges.cend (),
194
+ edge_props.cbegin (),
195
+ static_cast <TrafoGraphIdx>(state.components .template size <Node>())};
196
+
197
+ BGL_FORALL_VERTICES (v, trafo_graph, TransformerGraph) { trafo_graph[v].is_source = false ; }
198
+
199
+ // Mark sources
200
+ for (auto const & source : state.components .template citer <Source>()) {
201
+ // ignore disabled sources
202
+ trafo_graph[source.node ()].is_source = source.status ();
203
+ }
204
+
205
+ return trafo_graph;
46
206
}
47
207
48
- inline auto process_edges_dijkstra (Idx v, std::vector<EdgeWeight>& edge_weight, std::vector<Idx2D>& edge_pos,
49
- TransformerGraph const & graph) -> void {
208
+ inline void process_edges_dijkstra (Idx v, std::vector<EdgeWeight>& vertex_distances, TransformerGraph const & graph) {
50
209
using TrafoGraphElement = std::pair<EdgeWeight, TrafoGraphIdx>;
51
210
std::priority_queue<TrafoGraphElement, std::vector<TrafoGraphElement>, std::greater<>> pq;
52
- edge_weight[v] = 0 ;
53
- edge_pos[v] = {v, v};
211
+ vertex_distances[v] = 0 ;
54
212
pq.push ({0 , v});
55
213
56
214
while (!pq.empty ()) {
57
215
auto [dist, u] = pq.top ();
58
216
pq.pop ();
59
217
60
- if (dist != edge_weight [u]) {
218
+ if (dist != vertex_distances [u]) {
61
219
continue ;
62
220
}
63
221
64
- for ( auto e : boost::make_iterator_range ( boost::out_edges (u , graph)) ) {
222
+ BGL_FORALL_OUTEDGES (u, e , graph, TransformerGraph ) {
65
223
auto v = boost::target (e, graph);
66
224
const EdgeWeight weight = graph[e].weight ;
67
225
68
- if (edge_weight[u] + weight < edge_weight[v]) {
69
- edge_weight[v] = edge_weight[u] + weight;
70
- edge_pos[v] = graph[e].pos ;
71
- pq.push ({edge_weight[v], v});
226
+ if (vertex_distances[u] + weight < vertex_distances[v]) {
227
+ vertex_distances[v] = vertex_distances[u] + weight;
228
+ pq.push ({vertex_distances[v], v});
72
229
}
73
230
}
74
231
}
75
232
}
76
233
77
- // Step 2: Initialize the rank of all vertices (transformer nodes) as infinite (INT_MAX)
78
- // Step 3: Loop all the connected sources (status == 1)
79
- // a. Perform Dijkstra shortest path algorithm from the vertex with that source.
80
- // This is to determine the shortest path of all vertices to this particular source.
81
- inline auto get_edge_weights (TransformerGraph const & graph) -> WeightedTrafoList {
82
- std::vector<EdgeWeight> edge_weight (boost::num_vertices (graph), infty);
83
- std::vector<Idx2D> edge_pos (boost::num_vertices (graph));
84
-
85
- for (auto v : boost::make_iterator_range (boost::vertices (graph))) {
234
+ inline auto get_edge_weights (TransformerGraph const & graph) -> TrafoGraphEdgeProperties {
235
+ std::vector<EdgeWeight> vertex_distances (boost::num_vertices (graph), infty);
236
+ BGL_FORALL_VERTICES (v, graph, TransformerGraph) {
86
237
if (graph[v].is_source ) {
87
- process_edges_dijkstra (v, edge_weight, edge_pos , graph);
238
+ process_edges_dijkstra (v, vertex_distances , graph);
88
239
}
89
240
}
90
241
91
- WeightedTrafoList result;
92
- for (size_t i = 0 ; i < edge_weight.size (); ++i) {
93
- result.emplace_back (edge_pos[i], edge_weight[i]);
242
+ TrafoGraphEdgeProperties result;
243
+ BGL_FORALL_EDGES (e, graph, TransformerGraph) {
244
+ if (graph[e].regulated_idx == unregulated_idx) {
245
+ continue ;
246
+ }
247
+ result.push_back ({graph[e].regulated_idx , vertex_distances[boost::source (e, graph)]});
94
248
}
95
249
96
250
return result;
97
251
}
98
252
99
- // Step 4: Loop all transformers with automatic tap changers, including the transformers which are not
100
- // fully connected
101
- // a.Rank of the transformer <-
102
- // i. Infinity(INT_MAX), if tap side of the transformer is disconnected.
103
- // The transformer regulation should be ignored
104
- // ii.Rank of the vertex at the tap side of the transformer, if tap side of the transformer is connected
105
- inline auto transformer_disconnected (Idx2D const & /* pos*/ ) -> bool {
106
- // <TODO: jguo> waiting for the functionalities in step 1 to be implemented
107
- return false ;
108
- }
109
-
110
- inline auto rank_transformers (WeightedTrafoList const & w_trafo_list) -> RankedTransformerGroups {
253
+ inline auto rank_transformers (TrafoGraphEdgeProperties const & w_trafo_list) -> RankedTransformerGroups {
111
254
auto sorted_trafos = w_trafo_list;
112
255
113
- for (auto & trafo : sorted_trafos) {
114
- if (transformer_disconnected (trafo.first )) {
115
- trafo.second = infty;
116
- }
117
- }
118
-
119
256
std::sort (sorted_trafos.begin (), sorted_trafos.end (),
120
- [](const WeightedTrafo& a, const WeightedTrafo& b) { return a.second < b.second ; });
121
-
122
- RankedTransformerGroups groups;
123
- Idx last_weight = -1 ;
124
- for (const auto & trafo : sorted_trafos) {
125
- if (groups.empty () || last_weight != trafo.second ) {
126
- groups.push_back (std::vector<Idx2D>{trafo.first });
127
- last_weight = trafo.second ;
128
- } else {
129
- groups.back ().push_back (trafo.first );
130
- }
131
- }
257
+ [](const TrafoGraphEdge& a, const TrafoGraphEdge& b) { return a.weight < b.weight ; });
258
+
259
+ RankedTransformerGroups groups (sorted_trafos.size ());
260
+ std::ranges::transform (sorted_trafos, groups.begin (), [](const TrafoGraphEdge& x) { return x.regulated_idx ; });
132
261
return groups;
133
262
}
134
263
0 commit comments