diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4a2a1833a8..8035e23264 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: entry: uv run --frozen pytest language: system pass_filenames: false - types_or: [ python, c++, c ] + types_or: [ python, c++, c, json ] - id: ruff-format name: ruff-format entry: uv run --frozen ruff format diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/calculation_parameters.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/calculation_parameters.hpp index 429372b30a..bc3779d7e7 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/calculation_parameters.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/calculation_parameters.hpp @@ -19,7 +19,8 @@ namespace power_grid_model { constexpr Idx disconnected = -1; -constexpr Idx status_off = 0; +constexpr IntS status_on = 1; +constexpr IntS status_off = 0; // Entry of YBus, node addmittance matrix struct YBusElement { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/component/link.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/component/link.hpp index 7cdbfc4900..c531a9c9a5 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/component/link.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/component/link.hpp @@ -30,6 +30,18 @@ class Link final : public Edge { double loading(double /* max_s */, double /* max_i */) const override { return 0.0; }; double phase_shift() const override { return 0.0; } + template BranchOutput get_energized_zero_output() const { + BranchOutput output = get_null_output(); + static_cast(output) = base_output(true); + return output; + } + + BranchShortCircuitOutput get_energized_zero_sc_output() const { + BranchShortCircuitOutput output = get_null_sc_output(); + static_cast(output) = base_output(true); + return output; + } + private: double base_i_from_; double base_i_to_; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/container.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/container.hpp index 4e280e2890..0d87415062 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/container.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/container.hpp @@ -68,6 +68,7 @@ template class Container; template class Container, StorageableTypes...> { public: + using storageable_types = std::tuple; using gettable_types = std::tuple; static constexpr size_t num_storageable = sizeof...(StorageableTypes); diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/math_output_queries.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/math_output_queries.hpp index e5c08b7dc9..15d30677d6 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/math_output_queries.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/math_output_queries.hpp @@ -21,27 +21,23 @@ namespace power_grid_model::main_core { template constexpr auto const& get_component_output(MathOutput> const& math_output, - Idx2D const& math_id) { + Idx2D math_id) { auto const& solver_output = math_output.solver_output[math_id.group]; - auto const& component_type_output = [&solver_output]() -> auto const& { - // TODO(mgovers): cleanup v2: change back to std::derived_from - if constexpr (std::derived_from || std::derived_from) { - return solver_output.branch; - } else if constexpr (std::same_as && requires { solver_output.source; }) { - return solver_output.source; - } else if constexpr (std::same_as && requires { solver_output.shunt; }) { - return solver_output.shunt; - } else if constexpr (std::derived_from && requires { solver_output.load_gen; }) { - return solver_output.load_gen; - } else if constexpr (std::same_as && requires { solver_output.fault; }) { - return solver_output.fault; - } else { - static_assert(false, "Unsupported component type for output retrieval"); - } - }(); - - return component_type_output[math_id.pos]; + // TODO(mgovers): cleanup v2: change back to std::derived_from + if constexpr (std::derived_from || std::derived_from) { + return solver_output.branch[math_id.pos]; + } else if constexpr (std::same_as && requires { solver_output.source; }) { + return solver_output.source[math_id.pos]; + } else if constexpr (std::same_as && requires { solver_output.shunt; }) { + return solver_output.shunt[math_id.pos]; + } else if constexpr (std::derived_from && requires { solver_output.load_gen; }) { + return solver_output.load_gen[math_id.pos]; + } else if constexpr (std::same_as && requires { solver_output.fault; }) { + return solver_output.fault[math_id.pos]; + } else { + static_assert(false, "Unsupported component type for output retrieval"); + } } } // namespace power_grid_model::main_core diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/output.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/output.hpp index c4a7004b94..781fb84eb4 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/output.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/output.hpp @@ -128,8 +128,37 @@ constexpr auto output_result(Component const& node, MainModelState Component, class ComponentContainer, steady_state_solver_output_type SolverOutputType> + requires model_component_state_c +constexpr auto output_result(Component const& link, MainModelState const& /* state */, + MathOutput> const& math_output, Idx2D const& topo_id) { + using sym = decode_symmetry_v; + + if (topo_id.group == disconnected) { + return link.template get_null_output(); + } + if (!link.edge_status()) { + return link.template get_energized_zero_output(); + } + return link.template get_output(math_output.supernode_output[topo_id.group].link[topo_id.pos]); +} +template Component, class ComponentContainer, short_circuit_solver_output_type SolverOutputType> + requires model_component_state_c +inline auto output_result(Component const& link, MainModelState const& /* state */, + MathOutput> const& math_output, Idx2D const& topo_id) { + if (topo_id.group == disconnected) { + return link.get_null_sc_output(); + } + if (!link.edge_status()) { + return link.get_energized_zero_sc_output(); + } + return link.get_sc_output(math_output.supernode_output[topo_id.group].link[topo_id.pos]); +} + // output branch template Component, steady_state_solver_output_type SolverOutputType> + requires(!std::same_as) // TODO(mgovers): cleanup v2: change back to only derived_from constexpr auto output_result(Component const& branch, std::vector const& solver_output, Idx2D math_id) { using sym = decode_symmetry_v; @@ -139,7 +168,9 @@ constexpr auto output_result(Component const& branch, std::vector(solver_output[math_id.group].branch[math_id.pos]); } +// TODO(mgovers): cleanup v2: change back to only derived_from template Component, short_circuit_solver_output_type SolverOutputType> + requires(!std::same_as) // TODO(mgovers): cleanup v2: change back to only derived_from inline auto output_result(Component const& branch, std::vector const& solver_output, Idx2D math_id) { if (math_id.group == disconnected) { return branch.get_null_sc_output(); @@ -473,6 +504,28 @@ constexpr auto output_result(Component const& voltage_regulator, MainModelState< return voltage_regulator.get_null_sc_output(); } +template Component, class ComponentContainer, solver_output_type SolverOutputType, + non_owning_view_c ComponentOutput> + requires model_component_state_c +constexpr void output_result(MainModelState const& state, + MathOutput> const& math_output, ComponentOutput output) { + if (auto const& link_topo_ids = state.reduced_topology->topo_node_coup.coupling.user_links_to_topo_nodes; + std::ranges::ssize(link_topo_ids) == + get_component_size( + state.components)) { // TODO(mgovers): cleanup v2: this should be the only code path remaining + std::ranges::transform( + get_component_citer(state.components), link_topo_ids, std::ranges::begin(output), + [&state, &math_output](Component const& link, Idx2D const& topo_id) { + return output_result(link, state, math_output, topo_id); + }); + } else { + detail::produce_output( + state, output, [&math_output](Component const& link, Idx2D const& math_id) { + return output_result(link, math_output.solver_output, math_id); + }); + } +} + // output base component template Component, class ComponentContainer, solver_output_type SolverOutputType, non_owning_view_c ComponentOutput> diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/topological_node_output.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/topological_node_output.hpp index 1df64f4f48..0a0946bbeb 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/topological_node_output.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/topological_node_output.hpp @@ -22,12 +22,12 @@ namespace detail { template struct SuperNodeSolverInput { std::span links; UserNodeValueVector node_injection; - UserNodeValueVector node_flow_from_branch; + UserNodeValueVector node_flow_through_grid; ComplexValueVector get_total_injection_per_node() const { - assert(node_injection.size() == node_flow_from_branch.size()); + assert(node_injection.size() == node_flow_through_grid.size()); - return std::views::zip(node_injection, node_flow_from_branch) | std::views::transform([](auto const& pair) { + return std::views::zip(node_injection, node_flow_through_grid) | std::views::transform([](auto const& pair) { auto const& [node_inj, branch_flow] = pair; return ComplexValue(node_inj + branch_flow); }) | @@ -85,6 +85,9 @@ inline Idx get_node_sequence_idx(main_model_state_c auto const& state, Idx compo return state.comp_topo ->load_gen_node_idx[get_component_sequence_offset(state.components) + component_idx]; + } else if constexpr (std::derived_from) { + return state.comp_topo + ->shunt_node_idx[get_component_sequence_offset(state.components) + component_idx]; } else if constexpr (std::same_as) { auto const& fault = get_component_by_sequence(state.components, component_idx); return get_component_sequence_idx(state.components, fault.get_fault_object()); @@ -99,10 +102,16 @@ inline BranchIdx const& get_branch_sequence_idx(main_model_state_c auto const& s ->branch_node_idx[get_component_sequence_offset(state.components) + component_idx]; } +template ComponentType> +inline Branch3Idx const& get_branch3_sequence_idx(main_model_state_c auto const& state, Idx component_idx) { + return state.comp_topo + ->branch3_node_idx[get_component_sequence_offset(state.components) + component_idx]; +} + struct AddApplianceInjection { template requires flow_accumulator_c && - (is_in_list_c || + (std::derived_from || (std::same_as && short_circuit_solver_output_type)) void operator()(main_model_state_c auto const& state, MathOutput> const& math_output, AddToTarget accumulate_injection) const { @@ -133,8 +142,8 @@ struct AddApplianceInjection { auto const& component_output = get_component_output(math_output, component_math_id); auto const& branch_node_idx = get_branch_sequence_idx(state, component_idx); - for (auto const side : {BranchSide::from, BranchSide::to}) { - auto const& user_node_idx = branch_node_idx[std::to_underlying(side)]; + for (auto&& [side, user_node_idx] : + std::views::zip(std::array{BranchSide::from, BranchSide::to}, branch_node_idx)) { auto const& user_topo_id = state.reduced_topology->topo_node_coup.coupling.user_nodes_to_topo_nodes[user_node_idx]; accumulate_injection.template operator()(user_topo_id, @@ -142,17 +151,57 @@ struct AddApplianceInjection { } } } + + template + requires flow_accumulator_c && + std::derived_from + void operator()(main_model_state_c auto const& state, MathOutput> const& math_output, + AddToTarget accumulate_injection) const { + for (auto const& [component_idx, component_math_id] : enumerate(comp_base_sequence(state))) { + if (component_math_id.group == disconnected) { + continue; + } + + auto const& branch3_node_idx = get_branch3_sequence_idx(state, component_idx); + for (auto&& [side_pos, user_node_idx] : std::views::zip(component_math_id.pos, branch3_node_idx)) { + if (side_pos == disconnected) { + continue; + } + auto const& component_output = + get_component_output(math_output, {component_math_id.group, side_pos}); + auto const& user_topo_id = + state.reduced_topology->topo_node_coup.coupling.user_nodes_to_topo_nodes[user_node_idx]; + accumulate_injection.template operator()( + user_topo_id, get_injection(component_output, BranchSide::from)); + } + } + } }; constexpr auto add_appliance_injection = AddApplianceInjection{}; -template +struct user_node_contribution_tag_t {}; +struct ContributesToSteadyStateUserNodeInjection : public user_node_contribution_tag_t { + template + static constexpr bool value = std::derived_from || + std::derived_from || std::derived_from; +}; +struct ContributesToShortCircuitUserNodeInjection : public user_node_contribution_tag_t { + template + static constexpr bool value = std::derived_from || std::derived_from || + std::derived_from || + std::derived_from || std::derived_from; +}; + +template ContributesToUserNodeInjection, main_model_state_c State, + solver_output_type SolverOutput, typename AddToTarget> inline void add_flows(State const& state, MathOutput> const& math_output, AddToTarget accumulate_injection) { - utils::run_functor_with_tuple_return_void( + using Container = decltype(state.components); + + utils::run_functor_with_tuple_return_void( [&state, &math_output, &accumulate_injection]() { - if constexpr (decltype(state.components)::template is_storageable_v) { + if constexpr (ContributesToUserNodeInjection::template value) { add_appliance_injection.template operator()(state, math_output, accumulate_injection); } }); @@ -227,26 +276,24 @@ solve_topological_nodes(LinkSolver link_solver, State const& state, auto const node_number = topo_node.user_nodes.size(); return {.links = std::span{topo_node.user_links}, .node_injection = UserNodeValueVector(node_number), - .node_flow_from_branch = UserNodeValueVector(node_number)}; + .node_flow_through_grid = UserNodeValueVector(node_number)}; }) | std::ranges::to(); auto const accumulate_injection = [&link_solver_input](Idx2D const& user_topo_id, ComplexValue const& injection) { - if constexpr (std::derived_from) { - link_solver_input[user_topo_id.group].node_flow_from_branch[user_topo_id.pos] += injection; + if constexpr (std::derived_from || std::derived_from || + std::derived_from) { + link_solver_input[user_topo_id.group].node_flow_through_grid[user_topo_id.pos] += injection; } else { link_solver_input[user_topo_id.group].node_injection[user_topo_id.pos] += injection; } }; if constexpr (steady_state_solver_output_type) { - using InjectionComponentTypesTuple = - std::tuple; - add_flows(state, math_output, accumulate_injection); + add_flows(state, math_output, accumulate_injection); } else if constexpr (short_circuit_solver_output_type) { - using InjectionComponentTypesTuple = std::tuple; - add_flows(state, math_output, accumulate_injection); + add_flows(state, math_output, accumulate_injection); } auto result = diff --git a/tests/cpp_unit_tests/main_core/test_main_core_output.cpp b/tests/cpp_unit_tests/main_core/test_main_core_output.cpp index 30d7d1e0eb..bccfb3a94b 100644 --- a/tests/cpp_unit_tests/main_core/test_main_core_output.cpp +++ b/tests/cpp_unit_tests/main_core/test_main_core_output.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -222,6 +223,71 @@ template void check_null_current_sensor_output(CurrentSensorO } // namespace TEST_CASE("Test main core output") { + SUBCASE("Link") { + using ComponentContainer = Container, Link>; + using State = MainModelState; + + State state; + emplace_component(state.components, 0, + LinkInput{.id = 0, .from_status = status_on, .to_status = status_on}, 10e3, 20e3); + emplace_component(state.components, 1, + LinkInput{.id = 1, .from_status = status_on, .to_status = status_off}, 10e3, 20e3); + emplace_component(state.components, 2, + LinkInput{.id = 2, .from_status = status_off, .to_status = status_off}, 10e3, 20e3); + state.components.set_construction_complete(); + + auto reduced_topology = std::make_shared(); + reduced_topology->topo_node_coup.coupling.user_links_to_topo_nodes = { + {.group = 0, .pos = 0}, {.group = 0, .pos = 1}, {.group = disconnected, .pos = disconnected}}; + state.reduced_topology = std::make_shared(std::move(*reduced_topology)); + + SUBCASE("Steady state output") { + MathOutput>> const math_output{ + .solver_output = {}, + .optimizer_output = {}, + .supernode_output = { + {.bus_injection = {}, + .link = {{.s_f = {1.0, 2.0}, .s_t = {-1.0, -1.5}, .i_f = {3.0, 4.0}, .i_t = {-3.0, -4.0}}, + {.s_f = {3.0, 4.0}, .s_t = {-3.0, -4.0}, .i_f = {5.0, 6.0}, .i_t = {-5.0, -5.5}}}}}}; + std::vector output(3); + + output_result(state, math_output, output); + + CHECK(output[0].id == 0); + CHECK(output[0].energized == IntS{1}); + CHECK(output[0].p_from == doctest::Approx(base_power_3p)); + CHECK(output[0].q_from == doctest::Approx(2.0 * base_power_3p)); + CHECK(output[0].p_to == doctest::Approx(-1.0 * base_power_3p)); + CHECK(output[0].q_to == doctest::Approx(-1.5 * base_power_3p)); + CHECK(output[0].i_from == doctest::Approx(5.0 * base_power_3p / 10e3 / sqrt3)); + CHECK(output[0].i_to == doctest::Approx(5.0 * base_power_3p / 20e3 / sqrt3)); + CHECK(output[1].id == 1); + CHECK(output[1].energized == status_on); // connected rest of grid but one of the ends is off + CHECK(output[2].id == 2); + CHECK(output[2].energized == status_off); // completely disconnected from rest of grid + } + + SUBCASE("Short circuit output") { + MathOutput>> const math_output{ + .solver_output = {}, + .optimizer_output = {}, + .supernode_output = { + {.link = {{.i_f = {1.0, 0.0}, .i_t = {-2.0, 0.0}}, {.i_f = {3.0, 0.0}, .i_t = {-4.0, 0.0}}}}}}; + std::vector output(3); + + output_result(state, math_output, output); + + CHECK(output[0].id == 0); + CHECK(output[0].energized == IntS{1}); + CHECK(output[0].i_from(0) == doctest::Approx(base_power_3p / 10e3 / sqrt3)); + CHECK(output[0].i_to(0) == doctest::Approx(2.0 * base_power_3p / 20e3 / sqrt3)); + CHECK(output[1].id == 1); + CHECK(output[1].energized == status_on); + CHECK(output[2].id == 2); + CHECK(output[2].energized == status_off); + } + } + SUBCASE("TransformerTapRegulator") { using ComponentContainer = Container, TransformerTapRegulator>; using State = MainModelState; diff --git a/tests/cpp_unit_tests/main_core/test_topological_node_output.cpp b/tests/cpp_unit_tests/main_core/test_topological_node_output.cpp index 82b3b28659..203065b047 100644 --- a/tests/cpp_unit_tests/main_core/test_topological_node_output.cpp +++ b/tests/cpp_unit_tests/main_core/test_topological_node_output.cpp @@ -11,8 +11,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -21,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -33,17 +36,42 @@ #include #include #include -#include #include #include #include namespace power_grid_model::main_core { namespace { -using ComponentContainer = Container, AsymLoad, - SymLoad, Fault, Line, Node, Source, Shunt>; +using ComponentContainer = Container, + AsymLoad, SymLoad, Fault, Line, Node, Source, Shunt, ThreeWindingTransformer>; using State = MainModelState; +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(detail::ContributesToSteadyStateUserNodeInjection::template value); +static_assert(!detail::ContributesToSteadyStateUserNodeInjection::template value); + +static_assert(detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(!detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(!detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(!detail::ContributesToShortCircuitUserNodeInjection::template value); +static_assert(!detail::ContributesToShortCircuitUserNodeInjection::template value); + double constexpr dummy_value = 123.321; constexpr ComplexValue dummy_complex_value_sym() { return {2.14, 3.71}; } ComplexValue dummy_complex_value_asym() { return {{1.0, 2.0}, {-3.0, -4.0}, {5.0, -6.0}}; } @@ -53,6 +81,41 @@ void check_close(ComplexValue const& x, ComplexValue c CHECK(x.imag() == doctest::Approx(y.imag())); } +inline ThreeWindingTransformerInput make_three_winding_transformer_input() { + return {.id = 11, + .node_1 = 101, + .node_2 = 102, + .node_3 = 103, + .status_1 = 1, + .status_2 = 1, + .status_3 = 1, + .u1 = 10e3, + .u2 = 10e3, + .u3 = 10e3, + .sn_1 = 10e6, + .sn_2 = 10e6, + .sn_3 = 10e6, + .uk_12 = 0.1, + .uk_13 = 0.1, + .uk_23 = 0.1, + .pk_12 = 10e3, + .pk_13 = 10e3, + .pk_23 = 10e3, + .i0 = 0.0, + .p0 = 0.0, + .winding_1 = WindingType::wye_n, + .winding_2 = WindingType::wye_n, + .winding_3 = WindingType::wye_n, + .clock_12 = 0, + .clock_13 = 0, + .tap_side = Branch3Side::side_1, + .tap_pos = 0, + .tap_min = 0, + .tap_max = 0, + .tap_nom = 0, + .tap_size = 0.0}; +} + inline State make_state() { State state; state.comp_topo = std::make_shared([]() { @@ -62,6 +125,7 @@ inline State make_state() { comp_topo.shunt_node_idx = {Idx{1}}; comp_topo.load_gen_node_idx = {Idx{1}, Idx{2}}; comp_topo.branch_node_idx = {{Idx{0}, Idx{1}}, {Idx{1}, Idx{1}}, {Idx{1}, Idx{0}}, {Idx{2}, Idx{3}}}; + comp_topo.branch3_node_idx = {{Idx{1}, Idx{2}, Idx{3}}}; comp_topo.link_node_idx = {{Idx{0}, Idx{1}}, {Idx{1}, Idx{2}}}; return comp_topo; }()); @@ -87,6 +151,7 @@ inline State make_state() { {.group = 0, .pos = 1}, {.group = 0, .pos = disconnected}, {.group = disconnected, .pos = disconnected}}; + topo_comp_coup.branch3 = {{.group = 0, .pos = {4, 5, 6}}}; return topo_comp_coup; }()); @@ -109,6 +174,8 @@ inline State make_state() { emplace_component(state.components, 8, LineInput{}, dummy_value, dummy_value, dummy_value); emplace_component(state.components, 9, LineInput{}, dummy_value, dummy_value, dummy_value); emplace_component(state.components, 10, LineInput{}, dummy_value, dummy_value, dummy_value); + emplace_component(state.components, 11, make_three_winding_transformer_input(), + dummy_value, dummy_value, dummy_value); state.components.set_construction_complete(); return state; }; @@ -128,7 +195,10 @@ inline MathOutput>> make_steady_state_math .i_f = 0.5 * dummy_complex_value_sym(), .i_t = 0.5 * dummy_complex_value_sym()}, {}, - {}}, + {}, + {.s_f = dummy_complex_value_sym()}, + {.s_f = 2.0 * dummy_complex_value_sym()}, + {.s_f = 3.0 * dummy_complex_value_sym()}}, .source = {{.s = dummy_complex_value_sym(), .i = dummy_complex_value_sym()}, {}}, .shunt = {{.s = dummy_complex_value_sym(), .i = dummy_complex_value_sym()}}, .load_gen = {{.s = dummy_complex_value_sym(), .i = dummy_complex_value_sym()}, @@ -145,7 +215,10 @@ inline MathOutput>> make_short .branch = {{.i_f = dummy_complex_value_sym(), .i_t = dummy_complex_value_sym()}, {.i_f = 0.5 * dummy_complex_value_sym(), .i_t = 0.5 * dummy_complex_value_sym()}, {}, - {}}, + {}, + {.i_f = dummy_complex_value_sym()}, + {.i_f = 2.0 * dummy_complex_value_sym()}, + {.i_f = 3.0 * dummy_complex_value_sym()}}, .source = {{.i = dummy_complex_value_sym()}, {}}, .shunt = {{.i = dummy_complex_value_sym()}}}); return math_output; @@ -154,7 +227,10 @@ inline MathOutput>> make_short template struct InjectionAccumulator { auto accumulator() { return [this](Idx2D const& math_id, ComplexValue const& injection) { - auto& target_map = std::derived_from ? branch_flow_into_nodes : net_node_injections; + auto& target_map = (std::derived_from || std::derived_from || + std::derived_from) + ? branch_flow_into_nodes + : net_node_injections; if (auto [it, inserted] = target_map.try_emplace(math_id, injection); !inserted) { it->second += injection; @@ -271,6 +347,13 @@ TEST_CASE("Test topological node output") { CHECK(detail::get_node_sequence_idx(state, 0) == Idx{0}); CHECK(detail::get_node_sequence_idx(state, 1) == Idx{1}); } + SUBCASE("Shunt") { CHECK(detail::get_node_sequence_idx(state, 0) == Idx{1}); } + SUBCASE("ThreeWindingTransformer") { + auto const& branch3_node_idx = detail::get_branch3_sequence_idx(state, 0); + CHECK(branch3_node_idx[0] == Idx{1}); + CHECK(branch3_node_idx[1] == Idx{2}); + CHECK(branch3_node_idx[2] == Idx{3}); + } } SUBCASE("add_appliance_injection") { auto const state = make_state(); @@ -351,6 +434,54 @@ TEST_CASE("Test topological node output") { CHECK(!accumulator.branch_flow_into_nodes.contains(Idx2D{.group = 0, .pos = 2})); CHECK(!accumulator.branch_flow_into_nodes.contains(Idx2D{.group = 1, .pos = 0})); } + + // Shunt and ThreeWindingTransformer feed the link accumulation, never the node injection + SUBCASE("Shunt steady state output") { + auto const math_output = make_steady_state_math_output_sym(); + + detail::add_appliance_injection.template operator()(state, math_output, accumulator.accumulator()); + + CHECK(accumulator.net_node_injections.empty()); + CHECK(accumulator.branch_flow_into_nodes.size() == 1); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 1}) == dummy_complex_value_sym()); + } + SUBCASE("Shunt short circuit output") { + auto const math_output = make_short_circuit_math_output_sym(); + + detail::add_appliance_injection.template operator()(state, math_output, accumulator.accumulator()); + + CHECK(accumulator.net_node_injections.empty()); + CHECK(accumulator.branch_flow_into_nodes.size() == 1); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 1}) == dummy_complex_value_sym()); + } + SUBCASE("ThreeWindingTransformer steady state output") { + auto const math_output = make_steady_state_math_output_sym(); + + detail::add_appliance_injection.template operator()(state, math_output, + accumulator.accumulator()); + + CHECK(accumulator.net_node_injections.empty()); + CHECK(accumulator.branch_flow_into_nodes.size() == 3); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 1}) == -dummy_complex_value_sym()); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 2}) == + -2.0 * dummy_complex_value_sym()); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 1, .pos = 0}) == + -3.0 * dummy_complex_value_sym()); + } + SUBCASE("ThreeWindingTransformer short circuit output") { + auto const math_output = make_short_circuit_math_output_sym(); + + detail::add_appliance_injection.template operator()(state, math_output, + accumulator.accumulator()); + + CHECK(accumulator.net_node_injections.empty()); + CHECK(accumulator.branch_flow_into_nodes.size() == 3); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 1}) == -dummy_complex_value_sym()); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 2}) == + -2.0 * dummy_complex_value_sym()); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 1, .pos = 0}) == + -3.0 * dummy_complex_value_sym()); + } } SUBCASE("add_flows") { auto const state = make_state(); @@ -358,36 +489,45 @@ TEST_CASE("Test topological node output") { SUBCASE("Steady state output") { auto const math_output = make_steady_state_math_output_sym(); - using ComponentTypes = - std::tuple; - detail::add_flows(state, math_output, accumulator.accumulator()); + detail::add_flows(state, math_output, + accumulator.accumulator()); CHECK(accumulator.net_node_injections.size() == 3); CHECK(accumulator.net_node_injections.at(Idx2D{.group = 0, .pos = 0}) == dummy_complex_value_sym()); CHECK(accumulator.net_node_injections.at(Idx2D{.group = 0, .pos = 1}) == dummy_complex_value_sym()); CHECK(accumulator.net_node_injections.at(Idx2D{.group = 0, .pos = 2}) == dummy_complex_value_sym()); - CHECK(accumulator.branch_flow_into_nodes.size() == 2); + // {0,1} = lines (-2) + shunt (+1) + 3w side 1 (-1); {0,2} and {1,0} are 3w-only + CHECK(accumulator.branch_flow_into_nodes.size() == 4); CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 0}) == -dummy_complex_value_sym()); CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 1}) == -2.0 * dummy_complex_value_sym()); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 2}) == + -2.0 * dummy_complex_value_sym()); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 1, .pos = 0}) == + -3.0 * dummy_complex_value_sym()); } SUBCASE("Short circuit output") { auto const math_output = make_short_circuit_math_output_sym(); - using ComponentTypes = std::tuple; - detail::add_flows(state, math_output, accumulator.accumulator()); + detail::add_flows(state, math_output, + accumulator.accumulator()); CHECK(accumulator.net_node_injections.size() == 2); CHECK(accumulator.net_node_injections.at(Idx2D{.group = 0, .pos = 0}) == 2.0 * dummy_complex_value_sym()); CHECK(accumulator.net_node_injections.at(Idx2D{.group = 0, .pos = 1}) == dummy_complex_value_sym()); - CHECK(accumulator.branch_flow_into_nodes.size() == 2); + // {0,1} = lines (-2) + shunt (+1) + 3w side 1 (-1); {0,2} and {1,0} are 3w-only + CHECK(accumulator.branch_flow_into_nodes.size() == 4); CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 0}) == -dummy_complex_value_sym()); CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 1}) == -2.0 * dummy_complex_value_sym()); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 0, .pos = 2}) == + -2.0 * dummy_complex_value_sym()); + CHECK(accumulator.branch_flow_into_nodes.at(Idx2D{.group = 1, .pos = 0}) == + -3.0 * dummy_complex_value_sym()); } } SUBCASE("SuperNodeSolverInput::get_total_injection_per_node") { @@ -398,8 +538,8 @@ TEST_CASE("Test topological node output") { detail::SuperNodeSolverInput const input{ .links = links, .node_injection = {dummy_complex_value_sym(), dummy_complex_value_sym(), dummy_complex_value_sym()}, - .node_flow_from_branch = {dummy_complex_value_sym(), dummy_complex_value_sym(), - dummy_complex_value_sym()}}; + .node_flow_through_grid = {dummy_complex_value_sym(), dummy_complex_value_sym(), + dummy_complex_value_sym()}}; auto const total = input.get_total_injection_per_node(); REQUIRE(total.size() == 3); @@ -410,8 +550,8 @@ TEST_CASE("Test topological node output") { detail::SuperNodeSolverInput const input{ .links = links, .node_injection = {dummy_complex_value_asym(), dummy_complex_value_asym(), dummy_complex_value_asym()}, - .node_flow_from_branch = {dummy_complex_value_asym(), dummy_complex_value_asym(), - dummy_complex_value_asym()}}; + .node_flow_through_grid = {dummy_complex_value_asym(), dummy_complex_value_asym(), + dummy_complex_value_asym()}}; auto const total = input.get_total_injection_per_node(); REQUIRE(total.size() == 3); @@ -427,8 +567,8 @@ TEST_CASE("Test topological node output") { detail::SuperNodeSolverInput const input{ .links = links, .node_injection = {dummy_complex_value_sym(), DoubleComplex{}, 2.0 * dummy_complex_value_sym()}, - .node_flow_from_branch = {DoubleComplex{}, 3.0 * dummy_complex_value_sym(), - -dummy_complex_value_sym()}}; + .node_flow_through_grid = {DoubleComplex{}, 3.0 * dummy_complex_value_sym(), + -dummy_complex_value_sym()}}; LinkSolverMock mock{.return_values = { {2.0 * dummy_complex_value_sym(), -dummy_complex_value_sym()}, @@ -450,8 +590,8 @@ TEST_CASE("Test topological node output") { .links = links, .node_injection = {dummy_complex_value_asym(), ComplexValue{}, 2.0 * dummy_complex_value_asym()}, - .node_flow_from_branch = {ComplexValue{}, 3.0 * dummy_complex_value_asym(), - -dummy_complex_value_asym()}}; + .node_flow_through_grid = {ComplexValue{}, 3.0 * dummy_complex_value_asym(), + -dummy_complex_value_asym()}}; LinkSolverMock mock{.return_values = {{dummy_complex_value_asym()(0), 2.0 * dummy_complex_value_asym()(0)}, {-dummy_complex_value_asym()(1), 3.0 * dummy_complex_value_asym()(1)}, @@ -527,7 +667,7 @@ TEST_CASE("Test topological node output") { REQUIRE(mock.call_count == 1); CHECK(mock.recorded_edges[0] == links); CHECK(mock.recorded_loads[0] == - ComplexVector{DoubleComplex{}, -dummy_complex_value_sym(), dummy_complex_value_sym()}); + ComplexVector{DoubleComplex{}, -dummy_complex_value_sym(), -dummy_complex_value_sym()}); REQUIRE(result.size() == 2); CHECK(result[0].bus_injection == UserNodeValueVector{dummy_complex_value_sym(), @@ -559,8 +699,8 @@ TEST_CASE("Test topological node output") { REQUIRE(mock.call_count == 1); CHECK(mock.recorded_edges[0] == links); - CHECK(mock.recorded_loads[0] == - ComplexVector{dummy_complex_value_sym(), -dummy_complex_value_sym(), DoubleComplex{}}); + CHECK(mock.recorded_loads[0] == ComplexVector{dummy_complex_value_sym(), -dummy_complex_value_sym(), + -2.0 * dummy_complex_value_sym()}); REQUIRE(result.size() == 2); REQUIRE(result[0].link.size() == 2); diff --git a/tests/data/power_flow/dummy-test-link/asym_output_batch.json b/tests/data/power_flow/dummy-test-link/asym_output_batch.json new file mode 100644 index 0000000000..70c2ea0855 --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/asym_output_batch.json @@ -0,0 +1,68 @@ +{ + "version": "1.0", + "type": "asym_output", + "is_batch": true, + "attributes": {}, + "data": [ + { + "node": [ + {"id": 1, "energized": 1, "u_pu": [1.05, 1.05, 1.05], "u": [6062.18778, 6062.18778, 6062.18778], "u_angle": [0, -2.0944, 2.0944], "p": [220500, 110250, 330751], "q": [110250, 110250, 110250]}, + {"id": 2, "energized": 1, "u_pu": [1.05, 1.05, 1.05], "u": [6062.18778, 6062.18778, 6062.18778], "u_angle": [0, -2.0944, 2.0944], "p": [-220500, -110250, -330751], "q": [-110250, -110250, -110250]} + ], + "link": [ + {"id": 3, "energized": 1, "loading": 0, "p_from": [220500, 110250, 330751], "q_from": [110250, 110250, 110250], "p_to": [-220500, -110250, -330751], "q_to": [-110250, -110250, -110250]} + ], + "source": [ + {"id": 4, "energized": 1, "p": [220500, 110250, 330751], "q": [110250, 110250, 110250]} + ], + "asym_load": [ + {"id": 5, "energized": 1, "p": [220500, 110250, 330751], "q": [110250, 110250, 110250]} + ] + }, + { + "node": [ + {"id": 1, "energized": 1, "u_pu": [1.05, 1.05, 1.05], "u": [6062.18778, 6062.18778, 6062.18778], "u_angle": [0, -2.0944, 2.0944], "p": [0, 0, 0], "q": [0, 0, 0]}, + {"id": 2, "energized": 0, "u_pu": [0, 0, 0], "u": [0, 0, 0], "u_angle": [0, -2.0944, 2.0944], "p": [0, 0, 0], "q": [0, 0, 0]} + ], + "link": [ + {"id": 3, "energized": 1, "loading": 0, "p_from": [0, 0, 0], "q_from": [0, 0, 0], "p_to": [0, 0, 0], "q_to": [0, 0, 0]} + ], + "source": [ + {"id": 4, "energized": 1, "p": [0, 0, 0], "q": [0, 0, 0]} + ], + "asym_load": [ + {"id": 5, "energized": 0, "p": [0, 0, 0], "q": [0, 0, 0]} + ] + }, + { + "node": [ + {"id": 1, "energized": 1, "u_pu": [1.05, 1.05, 1.05], "u": [6062.18778, 6062.18778, 6062.18778], "u_angle": [0, -2.0944, 2.0944], "p": [0, 0, 0], "q": [0, 0, 0]}, + {"id": 2, "energized": 0, "u_pu": [0, 0, 0], "u": [0, 0, 0], "u_angle": [0, -2.0944, 2.0944], "p": [0, 0, 0], "q": [0, 0, 0]} + ], + "link": [ + {"id": 3, "energized": 0, "loading": 0, "p_from": [0, 0, 0], "q_from": [0, 0, 0], "p_to": [0, 0, 0], "q_to": [0, 0, 0]} + ], + "source": [ + {"id": 4, "energized": 1, "p": [0, 0, 0], "q": [0, 0, 0]} + ], + "asym_load": [ + {"id": 5, "energized": 0, "p": [0, 0, 0], "q": [0, 0, 0]} + ] + }, + { + "node": [ + {"id": 1, "energized": 1, "u_pu": [1.05, 1.05, 1.05], "u": [6062.18778, 6062.18778, 6062.18778], "u_angle": [0, -2.0944, 2.0944], "p": [0, 0, 0], "q": [0, 0, 0]}, + {"id": 2, "energized": 0, "u_pu": [0, 0, 0], "u": [0, 0, 0], "u_angle": [0, -2.0944, 2.0944], "p": [0, 0, 0], "q": [0, 0, 0]} + ], + "link": [ + {"id": 3, "energized": 0, "loading": 0, "p_from": [0, 0, 0], "q_from": [0, 0, 0], "p_to": [0, 0, 0], "q_to": [0, 0, 0]} + ], + "source": [ + {"id": 4, "energized": 1, "p": [0, 0, 0], "q": [0, 0, 0]} + ], + "asym_load": [ + {"id": 5, "energized": 0, "p": [0, 0, 0], "q": [0, 0, 0]} + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/power_flow/dummy-test-link/asym_output_batch.json.license b/tests/data/power_flow/dummy-test-link/asym_output_batch.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/asym_output_batch.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/data/power_flow/dummy-test-link/input.json b/tests/data/power_flow/dummy-test-link/input.json new file mode 100644 index 0000000000..55c862990b --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/input.json @@ -0,0 +1,46 @@ +{ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "node": [ + { + "id": 1, + "u_rated": 10e3 + }, + { + "id": 2, + "u_rated": 10e3 + } + ], + "link": [ + { + "id": 3, + "from_node": 1, + "to_node": 2, + "from_status": 1, + "to_status": 1 + } + ], + "source": [ + { + "id": 4, + "node": 1, + "status": 1, + "u_ref": 1.05, + "sk": 1e12 + } + ], + "asym_load": [ + { + "id": 5, + "node": 2, + "status": 1, + "type": 1, + "p_specified": [2.0e5, 1.0e5, 3.0e5], + "q_specified": [1.0e5, 1.0e5, 1.0e5] + } + ] + } +} \ No newline at end of file diff --git a/tests/data/power_flow/dummy-test-link/input.json.license b/tests/data/power_flow/dummy-test-link/input.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/input.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/data/power_flow/dummy-test-link/params.json b/tests/data/power_flow/dummy-test-link/params.json new file mode 100644 index 0000000000..2f096834b9 --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/params.json @@ -0,0 +1,5 @@ +{ + "calculation_method": ["newton_raphson", "iterative_current"], + "rtol": 1e-5, + "atol": 1e-5 +} diff --git a/tests/data/power_flow/dummy-test-link/params.json.license b/tests/data/power_flow/dummy-test-link/params.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/params.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/data/power_flow/dummy-test-link/sym_output_batch.json b/tests/data/power_flow/dummy-test-link/sym_output_batch.json new file mode 100644 index 0000000000..64528c504d --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/sym_output_batch.json @@ -0,0 +1,68 @@ +{ + "version": "1.0", + "type": "sym_output", + "is_batch": true, + "attributes": {}, + "data": [ + { + "node": [ + {"id": 1, "energized": 1, "u_pu": 1.05, "u": 10500, "u_angle": 0, "p": 661500, "q": 330750}, + {"id": 2, "energized": 1, "u_pu": 1.05, "u": 10500, "u_angle": 0, "p": -661500, "q": -330750} + ], + "link": [ + {"id": 3, "energized": 1, "loading": 0, "p_from": 661500, "q_from": 330750, "p_to": -661500, "q_to": -330750} + ], + "source": [ + {"id": 4, "energized": 1, "p": 661500, "q": 330750} + ], + "asym_load": [ + {"id": 5, "energized": 1, "p": 661500, "q": 330750} + ] + }, + { + "node": [ + {"id": 1, "energized": 1, "u_pu": 1.05, "u": 10500, "u_angle": 0, "p": 0, "q": 0}, + {"id": 2, "energized": 0, "u_pu": 0, "u": 0, "u_angle": 0, "p": 0, "q": 0} + ], + "link": [ + {"id": 3, "energized": 1, "loading": 0, "p_from": 0, "q_from": 0, "p_to": 0, "q_to": 0} + ], + "source": [ + {"id": 4, "energized": 1, "p": 0, "q": 0} + ], + "asym_load": [ + {"id": 5, "energized": 0, "p": 0, "q": 0} + ] + }, + { + "node": [ + {"id": 1, "energized": 1, "u_pu": 1.05, "u": 10500, "u_angle": 0, "p": 0, "q": 0}, + {"id": 2, "energized": 0, "u_pu": 0, "u": 0, "u_angle": 0, "p": 0, "q": 0} + ], + "link": [ + {"id": 3, "energized": 0, "loading": 0, "p_from": 0, "q_from": 0, "p_to": 0, "q_to": 0} + ], + "source": [ + {"id": 4, "energized": 1, "p": 0, "q": 0} + ], + "asym_load": [ + {"id": 5, "energized": 0, "p": 0, "q": 0} + ] + }, + { + "node": [ + {"id": 1, "energized": 1, "u_pu": 1.05, "u": 10500, "u_angle": 0, "p": 0, "q": 0}, + {"id": 2, "energized": 0, "u_pu": 0, "u": 0, "u_angle": 0, "p": 0, "q": 0} + ], + "link": [ + {"id": 3, "energized": 0, "loading": 0, "p_from": 0, "q_from": 0, "p_to": 0, "q_to": 0} + ], + "source": [ + {"id": 4, "energized": 1, "p": 0, "q": 0} + ], + "asym_load": [ + {"id": 5, "energized": 0, "p": 0, "q": 0} + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/power_flow/dummy-test-link/sym_output_batch.json.license b/tests/data/power_flow/dummy-test-link/sym_output_batch.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/sym_output_batch.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/data/power_flow/dummy-test-link/update_batch.json b/tests/data/power_flow/dummy-test-link/update_batch.json new file mode 100644 index 0000000000..bd5d3e7baa --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/update_batch.json @@ -0,0 +1,44 @@ +{ + "version": "1.0", + "type": "update", + "is_batch": true, + "attributes": {}, + "data": [ + { + "link": [ + { + "id": 3, + "from_status": 1, + "to_status": 1 + } + ] + }, + { + "link": [ + { + "id": 3, + "from_status": 1, + "to_status": 0 + } + ] + }, + { + "link": [ + { + "id": 3, + "from_status": 0, + "to_status": 1 + } + ] + }, + { + "link": [ + { + "id": 3, + "from_status": 0, + "to_status": 0 + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/data/power_flow/dummy-test-link/update_batch.json.license b/tests/data/power_flow/dummy-test-link/update_batch.json.license new file mode 100644 index 0000000000..7601059167 --- /dev/null +++ b/tests/data/power_flow/dummy-test-link/update_batch.json.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: Contributors to the Power Grid Model project + +SPDX-License-Identifier: MPL-2.0 diff --git a/tests/unit/utils.py b/tests/unit/utils.py index a62ef162fb..4a932f29c1 100644 --- a/tests/unit/utils.py +++ b/tests/unit/utils.py @@ -211,16 +211,20 @@ def _add_cases(case_dir: Path, calculation_type: str, **kwargs): def pytest_cases(get_batch_cases: bool = False, data_dir: str | None = None, test_cases: list[str] | None = None): relevant_calculations = [data_dir] if data_dir is not None else ["power_flow", "state_estimation", "short_circuit"] + cases = [] for calculation_type in relevant_calculations: test_case_paths = get_test_case_paths(calculation_type=calculation_type, test_cases=test_cases) for case_name, case_dir in test_case_paths.items(): - yield from _add_cases( - case_name=case_name, - case_dir=case_dir, - calculation_type=calculation_type, - is_batch=get_batch_cases, + cases.extend( + _add_cases( + case_name=case_name, + case_dir=case_dir, + calculation_type=calculation_type, + is_batch=get_batch_cases, + ) ) + return cases def bool_params(true_id: str, false_id: str | None = None, **kwargs): diff --git a/tests/unit/validation/test_validation_functions.py b/tests/unit/validation/test_validation_functions.py index 9cb6ebb69b..88be0c2eae 100644 --- a/tests/unit/validation/test_validation_functions.py +++ b/tests/unit/validation/test_validation_functions.py @@ -468,7 +468,7 @@ def test_validate_required_values_asym_calculation(): assert MissingValueError(CT.shunt, AT.b0, [NaN]) in required_values_errors -@pytest.mark.parametrize("fault_types", product(list(FaultType), list(FaultType))) +@pytest.mark.parametrize("fault_types", list(product(list(FaultType), list(FaultType)))) def test_validate_fault_sc_calculation(fault_types): line = initialize_array(DatasetType.input, CT.line, 1) shunt = initialize_array(DatasetType.input, CT.shunt, 1)