Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature / Step-up transformer tap changer support updated ranking #832

Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
}
}

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");
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
}
}

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 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@ class AutomaticTapCalculationError : public PowerGridError {
}
};

class AutomaticTapInputError : public PowerGridError {
public:
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
AutomaticTapInputError(std::string const& msg) {
append_msg(msg); // NOSONAR
}
};

class IDWrongType : public PowerGridError {
public:
explicit IDWrongType(ID id) { append_msg("Wrong type for object with id " + detail::to_string(id) + '\n'); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ inline void add_component(MainModelState<ComponentContainer>& state, ForwardIter
throw InvalidRegulatedObject(input.regulated_object, Component::name);
}
}();

// (TODO: jguo) This validation still needs to be updated
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
if (regulated_object_idx.group == get_component_type_index<Transformer>(state)) {
auto const& regulated_object = get_component<Transformer>(state, regulated_object_idx);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <optional>
#include <queue>
#include <ranges>
#include <utility>
#include <variant>
#include <vector>

Expand All @@ -50,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
Expand All @@ -65,13 +69,36 @@ 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<std::pair<TrafoGraphIdx, TrafoGraphIdx>>;
using TrafoGraphEdgeProperties = std::vector<TrafoGraphEdge>;

struct RegulatedTrafoProperties {
Idx id{};
ControlSide control_side{};

auto operator<=>(RegulatedTrafoProperties const& other) const = default; // NOLINT(modernize-use-nullptr)
};

using RegulatedTrafos = std::set<RegulatedTrafoProperties>;

inline std::pair<bool, ControlSide> regulated_trafos_contain(RegulatedTrafos const& trafos_set, Idx const& 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};
}
return {false, ControlSide{}}; // no default invalid control side, won't be used by logic
}

struct RegulatedObjects {
std::set<Idx> transformers{};
std::set<Idx> transformers3w{};
RegulatedTrafos trafos{};
RegulatedTrafos trafos3w{};
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved

std::pair<bool, ControlSide> contains_trafo(Idx const& id) const { return regulated_trafos_contain(trafos, id); }
std::pair<bool, ControlSide> contains_trafo3w(Idx const& id) const {
return regulated_trafos_contain(trafos3w, id);
}
};

using TransformerGraph = boost::compressed_sparse_row_graph<boost::directedS, TrafoGraphVertex, TrafoGraphEdge,
Expand All @@ -90,13 +117,16 @@ inline void add_to_edge(main_core::MainModelState<ComponentContainer> 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;

constexpr std::array<std::tuple<Branch3Side, Branch3Side>, 3> const branch3_combinations{
{{side_1, side_2}, {side_2, side_3}, {side_3, side_1}}};
{{side_1, side_2}, {side_1, side_3}, {side_2, side_3}}};
nitbharambe marked this conversation as resolved.
Show resolved Hide resolved

auto const tap_at_control_side = [&control_side](Branch3Side const& tap_side) {
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
return static_cast<IntS>(control_side) == static_cast<IntS>(tap_side);
};
for (auto const& [first_side, second_side] : branch3_combinations) {
if (!transformer3w.status(first_side) || !transformer3w.status(second_side)) {
continue;
Expand All @@ -105,17 +135,26 @@ inline void process_trafo3w_edge(main_core::main_model_state_c auto const& state
auto const& to_node = transformer3w.node(second_side);

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
if (single_direction_condition) {
auto const connected_to_primary_side_regulated =
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 (connected_to_primary_side_regulated) {
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) The following commented out logic was a potential bug
// (from_node != tap_side_node)
// ? unregulated_edge_prop
// :
TrafoGraphEdge{trafo3w_idx, 1, branch_3_side_to_tap_side(transformer3w.tap_side()), control_side};
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
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);
Expand All @@ -130,9 +169,10 @@ constexpr void add_edge(main_core::MainModelState<ComponentContainer> const& sta
TrafoGraphEdgeProperties& edge_props) {

for (auto const& transformer3w : state.components.template citer<ThreeWindingTransformer>()) {
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);
}
}

Expand All @@ -147,12 +187,15 @@ constexpr void add_edge(main_core::MainModelState<ComponentContainer> const& sta
}
auto const& from_node = transformer.from_node();
auto const& to_node = transformer.to_node();
if (regulated_objects.transformers.contains(transformer.id())) {
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;
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 trafo_regulated = regulated_objects.contains_trafo(transformer.id());
if (trafo_regulated.first) {
auto const control_side = trafo_regulated.second;
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());

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);
Expand Down Expand Up @@ -191,10 +234,11 @@ 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;
Expand Down Expand Up @@ -244,7 +288,8 @@ inline void process_edges_dijkstra(Idx v, std::vector<EdgeWeight>& vertex_distan
auto t = boost::target(e, graph);
const EdgeWeight weight = graph[e].weight;

// We can not use BGL_FORALL_OUTEDGES here because our grid is undirected
// We can not use BGL_FORALL_OUTEDGES here because we need information
// regardless of edge direction
if (u == s && vertex_distances[s] + weight < vertex_distances[t]) {
vertex_distances[t] = vertex_distances[s] + weight;
pq.push({vertex_distances[t], t});
Expand All @@ -271,9 +316,19 @@ inline auto get_edge_weights(TransformerGraph const& graph) -> TrafoGraphEdgePro
if (graph[e].regulated_idx == unregulated_idx) {
continue;
}
auto edge_res = std::min(vertex_distances[boost::source(e, graph)], vertex_distances[boost::target(e, graph)]);
// control_side, two winding or three winding, is always parallel to the
// source side of the transformer edge
auto const edge_src_rank = vertex_distances[boost::source(e, graph)];
auto const edge_tgt_rank = vertex_distances[boost::target(e, graph)];
auto const edge_res = std::min(edge_src_rank, edge_tgt_rank);
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved

if (edge_src_rank != edge_tgt_rank - 1) {
throw AutomaticTapInputError("Transformer is not unrechable: control side rank mismatch");
}
if (!is_unreachable(edge_res)) {
result.push_back({graph[e].regulated_idx, edge_res});
// (TODO: jguo) old way -> broke existing validation tests
// result.push_back({graph[e].regulated_idx, edge_res});
Jerry-Jinfeng-Guo marked this conversation as resolved.
Show resolved Hide resolved
result.push_back({graph[e].regulated_idx, edge_tgt_rank});
}
}

Expand All @@ -293,7 +348,10 @@ inline auto rank_transformers(TrafoGraphEdgeProperties const& w_trafo_list) -> R
groups.emplace_back();
previous_weight = trafo.weight;
}
groups.back().push_back(trafo.regulated_idx);
auto& current_group = groups.back(); // avoid duplicates
if (std::find(current_group.begin(), current_group.end(), trafo.regulated_idx) == current_group.end()) {
current_group.push_back(trafo.regulated_idx);
}
}
return groups;
}
Expand Down
Loading
Loading