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 15d30677d6..931f67a3d3 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 @@ -19,6 +19,29 @@ namespace power_grid_model::main_core { +template +constexpr auto get_voltage_output(MathOutput> const& math_output, Idx2D const& math_id) { + if constexpr (steady_state_solver_output_type) { + return math_output.solver_output[math_id.group].u[math_id.pos]; + } else if constexpr (short_circuit_solver_output_type) { + return math_output.solver_output[math_id.group].u_bus[math_id.pos]; + } else { + static_assert(false, "Unsupported solver output type for voltage output retrieval"); + } +} + +template +constexpr auto get_bus_injection_output_from_topo_id(MathOutput> const& math_output, + Idx2D const& topo_id) { + return math_output.supernode_output[topo_id.group].bus_injection[topo_id.pos]; +} + +template +constexpr auto get_bus_injection_output_from_math_id(MathOutput> const& math_output, + Idx2D const& math_id) { + return math_output.solver_output[math_id.group].bus_injection[math_id.pos]; +} + template constexpr auto const& get_component_output(MathOutput> const& math_output, Idx2D math_id) { 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 781fb84eb4..e008e8b733 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 @@ -5,6 +5,7 @@ #pragma once #include "container_queries.hpp" +#include "math_output_queries.hpp" #include "state.hpp" #include "state_queries.hpp" @@ -111,8 +112,8 @@ constexpr auto output_result(Component const& node, MainModelState(); } - return node.template get_output(math_output.solver_output[math_id.group].u[math_id.pos], - math_output.supernode_output[topo_id.group].bus_injection[topo_id.pos]); + return node.template get_output(get_voltage_output(math_output, math_id), + get_bus_injection_output_from_topo_id(math_output, topo_id)); } template Component, class ComponentContainer, short_circuit_solver_output_type SolverOutputType> @@ -125,7 +126,7 @@ constexpr auto output_result(Component const& node, MainModelState 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, +constexpr auto output_result(Component const& branch, MathOutput> const& math_output, Idx2D math_id) { using sym = decode_symmetry_v; if (math_id.group == disconnected) { return branch.template get_null_output(); } - return branch.template get_output(solver_output[math_id.group].branch[math_id.pos]); + return branch.template get_output(get_component_output(math_output, math_id)); } // 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) { +inline auto output_result(Component const& branch, MathOutput> const& math_output, + Idx2D const& math_id) { if (math_id.group == disconnected) { return branch.get_null_sc_output(); } - return branch.get_sc_output(solver_output[math_id.group].branch[math_id.pos]); + return branch.get_sc_output(get_component_output(math_output, math_id)); } // output branch3 template Component, steady_state_solver_output_type SolverOutputType> -constexpr auto output_result(Component const& branch3, std::vector const& solver_output, +constexpr auto output_result(Component const& branch3, MathOutput> const& math_output, Idx2DBranch3 const& math_id) { using sym = decode_symmetry_v; @@ -188,76 +190,79 @@ constexpr auto output_result(Component const& branch3, std::vector(); } - auto const& branches = solver_output[math_id.group].branch; - return branch3.template get_output(branches[math_id.pos[0]], branches[math_id.pos[1]], - branches[math_id.pos[2]]); + return branch3.template get_output( + get_component_output(math_output, {.group = math_id.group, .pos = math_id.pos[0]}), + get_component_output(math_output, {.group = math_id.group, .pos = math_id.pos[1]}), + get_component_output(math_output, {.group = math_id.group, .pos = math_id.pos[2]})); } template Component, short_circuit_solver_output_type SolverOutputType> -inline auto output_result(Component const& branch3, std::vector const& solver_output, +inline auto output_result(Component const& branch3, MathOutput> const& math_output, Idx2DBranch3 const& math_id) { if (math_id.group == disconnected) { return branch3.get_null_sc_output(); } - auto const& branches = solver_output[math_id.group].branch; - return branch3.get_sc_output(branches[math_id.pos[0]], branches[math_id.pos[1]], branches[math_id.pos[2]]); + return branch3.get_sc_output( + get_component_output(math_output, {.group = math_id.group, .pos = math_id.pos[0]}), + get_component_output(math_output, {.group = math_id.group, .pos = math_id.pos[1]}), + get_component_output(math_output, {.group = math_id.group, .pos = math_id.pos[2]})); } // output source template Component, steady_state_solver_output_type SolverOutputType> -constexpr auto output_result(Component const& source, std::vector const& solver_output, +constexpr auto output_result(Component const& source, MathOutput> const& math_output, Idx2D const& math_id) { using sym = decode_symmetry_v; if (math_id.group == disconnected) { return source.template get_null_output(); } - return source.template get_output(solver_output[math_id.group].source[math_id.pos]); + return source.template get_output(get_component_output(math_output, math_id)); } template Component, short_circuit_solver_output_type SolverOutputType> -inline auto output_result(Component const& source, std::vector const& solver_output, +inline auto output_result(Component const& source, MathOutput> const& math_output, Idx2D const& math_id) { if (math_id.group == disconnected) { return source.get_null_sc_output(); } - return source.get_sc_output(solver_output[math_id.group].source[math_id.pos]); + return source.get_sc_output(get_component_output(math_output, math_id)); } // output load gen template Component, steady_state_solver_output_type SolverOutputType> -constexpr auto output_result(Component const& load_gen, std::vector const& solver_output, +constexpr auto output_result(Component const& load_gen, MathOutput> const& math_output, Idx2D const& math_id) { using sym = decode_symmetry_v; if (math_id.group == disconnected) { return load_gen.template get_null_output(); } - return load_gen.template get_output(solver_output[math_id.group].load_gen[math_id.pos]); + return load_gen.template get_output(get_component_output(math_output, math_id)); } template Component, short_circuit_solver_output_type SolverOutputType> -inline auto output_result(Component const& load_gen, std::vector const& /*solver_output*/, +inline auto output_result(Component const& load_gen, MathOutput> const& /*math_output*/, Idx2D const& /*math_id*/) { return load_gen.get_null_sc_output(); } // output shunt template Component, steady_state_solver_output_type SolverOutputType> -constexpr auto output_result(Component const& shunt, std::vector const& solver_output, +constexpr auto output_result(Component const& shunt, MathOutput> const& math_output, Idx2D const& math_id) { using sym = decode_symmetry_v; if (math_id.group == disconnected) { return shunt.template get_null_output(); } - return shunt.template get_output(solver_output[math_id.group].shunt[math_id.pos]); + return shunt.template get_output(get_component_output(math_output, math_id)); } template Component, short_circuit_solver_output_type SolverOutputType> -inline auto output_result(Component const& shunt, std::vector const& solver_output, +inline auto output_result(Component const& shunt, MathOutput> const& math_output, Idx2D const& math_id) { if (math_id.group == disconnected) { return shunt.get_null_sc_output(); } - return shunt.get_sc_output(solver_output[math_id.group].shunt[math_id.pos]); + return shunt.get_sc_output(get_component_output(math_output, math_id)); } // output voltage sensor @@ -265,20 +270,21 @@ template Component, class ComponentCont steady_state_solver_output_type SolverOutputType> requires model_component_state_c constexpr auto output_result(Component const& voltage_sensor, MainModelState const& state, - std::vector const& solver_output, Idx const node_seq) { + MathOutput> const& math_output, Idx const node_seq) { using sym = decode_symmetry_v; Idx2D const node_math_id = state.topo_comp_coup->node[node_seq]; if (node_math_id.group == disconnected) { return voltage_sensor.template get_null_output(); } - return voltage_sensor.template get_output(solver_output[node_math_id.group].u[node_math_id.pos]); + return voltage_sensor.template get_output(get_voltage_output(math_output, node_math_id)); } template Component, class ComponentContainer, short_circuit_solver_output_type SolverOutputType> requires model_component_state_c inline auto output_result(Component const& voltage_sensor, MainModelState const& /* state */, - std::vector const& /* solver_output */, Idx const /* node_seq */) { + MathOutput> const& /* math_output */, + Idx const /* node_seq */) { return voltage_sensor.get_null_sc_output(); } @@ -287,7 +293,7 @@ template Component, class ComponentContai steady_state_solver_output_type SolverOutputType> requires model_component_state_c constexpr auto output_result(Component const& power_sensor, MainModelState const& state, - std::vector const& solver_output, Idx const obj_seq) { + MathOutput> const& math_output, Idx const obj_seq) { using sym = decode_symmetry_v; auto const terminal_type = power_sensor.get_terminal_type(); @@ -336,19 +342,19 @@ constexpr auto output_result(Component const& power_sensor, MainModelState(solver_output[obj_math_id.group].branch[obj_math_id.pos].s_f); + return power_sensor.template get_output(get_component_output(math_output, obj_math_id).s_f); case branch_to: - return power_sensor.template get_output(solver_output[obj_math_id.group].branch[obj_math_id.pos].s_t); + return power_sensor.template get_output(get_component_output(math_output, obj_math_id).s_t); case source: - return power_sensor.template get_output(solver_output[obj_math_id.group].source[obj_math_id.pos].s); + return power_sensor.template get_output(get_component_output(math_output, obj_math_id).s); case shunt: - return power_sensor.template get_output(solver_output[obj_math_id.group].shunt[obj_math_id.pos].s); + return power_sensor.template get_output(get_component_output(math_output, obj_math_id).s); case load: [[fallthrough]]; case generator: - return power_sensor.template get_output(solver_output[obj_math_id.group].load_gen[obj_math_id.pos].s); + return power_sensor.template get_output(get_component_output(math_output, obj_math_id).s); case node: - return power_sensor.template get_output(solver_output[obj_math_id.group].bus_injection[obj_math_id.pos]); + return power_sensor.template get_output(get_bus_injection_output_from_math_id(math_output, obj_math_id)); default: throw MissingCaseForEnumError{std::format("{} output_result()", Component::name), terminal_type}; } @@ -356,9 +362,9 @@ constexpr auto output_result(Component const& power_sensor, MainModelState Component, class ComponentContainer, short_circuit_solver_output_type SolverOutputType> requires model_component_state_c -constexpr auto output_result(Component const& power_or_current_sensor, - MainModelState const& /* state */, - std::vector const& /* solver_output */, Idx const /* obj_seq */) { +constexpr auto +output_result(Component const& power_or_current_sensor, MainModelState const& /* state */, + MathOutput> const& /* math_output */, Idx const /* obj_seq */) { return power_or_current_sensor.get_null_sc_output(); } @@ -367,7 +373,7 @@ template Component, class ComponentCont steady_state_solver_output_type SolverOutputType> requires model_component_state_c constexpr auto output_result(Component const& current_sensor, MainModelState const& state, - std::vector const& solver_output, Idx const obj_seq) { + MathOutput> const& math_output, Idx const obj_seq) { using sym = decode_symmetry_v; auto const terminal_type = current_sensor.get_terminal_type(); @@ -411,11 +417,11 @@ constexpr auto output_result(Component const& current_sensor, MainModelState(solver_output[obj_math_id.group].branch[obj_math_id.pos].i_f, - solver_output[node_from_math_id.group].u[node_from_math_id.pos]); + return current_sensor.template get_output(get_component_output(math_output, obj_math_id).i_f, + get_voltage_output(math_output, node_from_math_id)); case branch_to: - return current_sensor.template get_output(solver_output[obj_math_id.group].branch[obj_math_id.pos].i_t, - solver_output[node_to_math_id.group].u[node_to_math_id.pos]); + return current_sensor.template get_output(get_component_output(math_output, obj_math_id).i_t, + get_voltage_output(math_output, node_to_math_id)); default: throw MissingCaseForEnumError{std::format("{} output_result()", Component::name), terminal_type}; } @@ -423,9 +429,9 @@ constexpr auto output_result(Component const& current_sensor, MainModelState Component, class ComponentContainer, short_circuit_solver_output_type SolverOutputType> requires model_component_state_c -constexpr auto output_result(Component const& power_or_current_sensor, - MainModelState const& /* state */, - std::vector const& /* solver_output */, Idx const /* obj_seq */) { +constexpr auto +output_result(Component const& power_or_current_sensor, MainModelState const& /* state */, + MathOutput> const& /* math_output */, Idx const /* obj_seq */) { return power_or_current_sensor.get_null_sc_output(); } @@ -435,7 +441,7 @@ template Component, class ComponentContainer, requires model_component_state_c && model_component_state_c constexpr auto output_result(Component const& fault, MainModelState const& /* state */, - std::vector const& /* solver_output */, Idx2D /* math_id */) { + MathOutput> const& /* math_output */, Idx2D /* math_id */) { return fault.get_output(); } template Component, class ComponentContainer, @@ -443,13 +449,13 @@ template Component, class ComponentContainer, requires model_component_state_c && model_component_state_c inline auto output_result(Component const& fault, MainModelState const& state, - std::vector const& solver_output, Idx2D math_id) { + MathOutput> const& math_output, Idx2D math_id) { if (math_id.group == disconnected) { return fault.get_null_sc_output(); } auto const u_rated = get_component(state.components, fault.get_fault_object()).u_rated(); - return fault.get_sc_output(solver_output[math_id.group].fault[math_id.pos], u_rated); + return fault.get_sc_output(get_component_output(math_output, math_id), u_rated); } // output transformer tap regulator @@ -519,10 +525,10 @@ constexpr void output_result(MainModelState const& state, 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); - }); + detail::produce_output(state, output, + [&math_output](Component const& link, Idx2D const& math_id) { + return output_result(link, math_output, math_id); + }); } } @@ -530,63 +536,66 @@ constexpr void output_result(MainModelState const& state, template Component, class ComponentContainer, solver_output_type SolverOutputType, non_owning_view_c ComponentOutput> requires model_component_state_c && - requires(Component const& component, std::vector const& solver_output, Idx2D math_id) { + requires(Component const& component, MathOutput> const& math_output, + Idx2D math_id) { { - output_result(component, solver_output, math_id) + output_result(component, math_output, math_id) } -> detail::assignable_to>; } constexpr void output_result(MainModelState const& state, MathOutput> const& math_output, ComponentOutput output) { detail::produce_output(state, output, [&math_output](Component const& component, Idx2D math_id) { - return output_result(component, math_output.solver_output, math_id); + return output_result(component, math_output, math_id); }); } template Component, class ComponentContainer, solver_output_type SolverOutputType, non_owning_view_c ComponentOutput> requires model_component_state_c && requires(Component const& component, MainModelState const& state, - std::vector const& solver_output, Idx2D math_id) { + MathOutput> const& math_output, Idx2D math_id) { { - output_result(component, state, solver_output, math_id) + output_result(component, state, math_output, math_id) } -> detail::assignable_to>; - } + } && + (!std::same_as) // TODO(mgovers): cleanup v2: this requirement should no longer be needed + // after link output is cleaned up constexpr void output_result(MainModelState const& state, MathOutput> const& math_output, ComponentOutput output) { detail::produce_output( state, output, [&state, &math_output](Component const& component, Idx2D const math_id) { - return output_result(component, state, math_output.solver_output, math_id); + return output_result(component, state, math_output, math_id); }); } template Component, class ComponentContainer, solver_output_type SolverOutputType, non_owning_view_c ComponentOutput> requires model_component_state_c && requires(Component const& component, MainModelState const& state, - std::vector const& solver_output, Idx obj_seq) { + MathOutput> const& math_output, Idx obj_seq) { { - output_result(component, state, solver_output, obj_seq) + output_result(component, state, math_output, obj_seq) } -> detail::assignable_to>; } constexpr void output_result(MainModelState const& state, MathOutput> const& math_output, ComponentOutput output) { detail::produce_output( state, output, [&state, &math_output](Component const& component, Idx const obj_seq) { - return output_result(component, state, math_output.solver_output, obj_seq); + return output_result(component, state, math_output, obj_seq); }); } template Component, class ComponentContainer, solver_output_type SolverOutputType, non_owning_view_c ComponentOutput> requires model_component_state_c && - requires(Component const& component, std::vector const& solver_output, + requires(Component const& component, MathOutput> const& math_output, Idx2DBranch3 const& math_id) { { - output_result(component, solver_output, math_id) + output_result(component, math_output, math_id) } -> detail::assignable_to>; } constexpr void output_result(MainModelState const& state, MathOutput> const& math_output, ComponentOutput output) { detail::produce_output( state, output, [&math_output](Component const& component, Idx2DBranch3 const& math_id) { - return output_result(component, math_output.solver_output, math_id); + return output_result(component, math_output, math_id); }); } template Component, class ComponentContainer, typename SolverOutputType, @@ -605,22 +614,6 @@ constexpr void output_result(MainModelState const& state, return output_result(component, state, math_output, topo_id); }); } -template Component, class ComponentContainer, typename SolverOutputType, - non_owning_view_c ComponentOutput> - requires model_component_state_c && - requires(Component const& component, MainModelState const& state, - MathOutput const& math_output, Idx const obj_seq) { - { - output_result(component, state, math_output, obj_seq) - } -> detail::assignable_to>; - } -constexpr void output_result(MainModelState const& state, - MathOutput const& math_output, ComponentOutput output) { - detail::produce_output( - state, output, [&state, &math_output](Component const& component, Idx const obj_seq) { - return output_result(component, state, math_output, obj_seq); - }); -} // vector overload template Component, class ComponentContainer, typename SolverOutputType, class T> requires model_component_state_c 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 bccfb3a94b..b0c5ca29d6 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 @@ -220,6 +220,13 @@ template void check_null_current_sensor_output(CurrentSensorO check_zero(output.i_angle_residual); } +template auto make_math_output(std::vector solver_output) { + return MathOutput{.solver_output = std::move(solver_output), .optimizer_output = {}, .supernode_output = {}}; +} +template auto make_math_output(SolverOutputType solver_output) { + return make_math_output(std::vector{std::move(solver_output)}); +} + } // namespace TEST_CASE("Test main core output") { @@ -384,11 +391,12 @@ TEST_CASE_TEMPLATE("Test main core power sensor output energized state", sym, sy .q_measured = 2.0}}; // An excluded measurement remains energized while its measured object is active. - CHECK(output_result(sensor, state, solver_output, object_sequence).energized == 1); + CHECK(output_result(sensor, state, make_math_output(std::move(solver_output)), object_sequence).energized == 1); appliance.set_status(0); - check_null_power_sensor_output(output_result(sensor, state, solver_output, object_sequence)); + check_null_power_sensor_output( + output_result(sensor, state, make_math_output(std::move(solver_output)), object_sequence)); }; check_appliance(MeasuredTerminalType::source, 10, 0, get_component(state.components, ID{10})); @@ -409,7 +417,7 @@ TEST_CASE_TEMPLATE("Test main core power sensor output energized state", sym, sy .p_measured = 1.0, .q_measured = 2.0}}; - auto const output = output_result(sensor, state, solver_output, 0); + auto const output = output_result(sensor, state, make_math_output(std::move(solver_output)), 0); if (expected_energized) { CHECK(output.energized == 1); } else { @@ -447,7 +455,7 @@ TEST_CASE_TEMPLATE("Test main core power sensor output energized state", sym, sy .p_measured = 1.0, .q_measured = 2.0}}; - check_null_power_sensor_output(output_result(sensor, state, solver_output, 0)); + check_null_power_sensor_output(output_result(sensor, state, make_math_output(std::move(solver_output)), 0)); } } @@ -489,7 +497,7 @@ TEST_CASE_TEMPLATE("Test main core power sensor output with reduced component co .p_measured = 1.0, .q_measured = 2.0}}; - CHECK(output_result(sensor, state, solver_output, 0).energized == 1); + CHECK(output_result(sensor, state, make_math_output(std::move(solver_output)), 0).energized == 1); } TEST_CASE_TEMPLATE("Test main core current sensor output energized state", sym, symmetric_t, asymmetric_t) { @@ -511,7 +519,7 @@ TEST_CASE_TEMPLATE("Test main core current sensor output energized state", sym, .i_angle_measured = 0.0}, u_rated}; - auto const output = output_result(sensor, state, solver_output, 0); + auto const output = output_result(sensor, state, make_math_output(std::move(solver_output)), 0); if (terminal_type == MeasuredTerminalType::branch_from) { check_null_current_sensor_output(output); } else { @@ -529,7 +537,8 @@ TEST_CASE_TEMPLATE("Test main core current sensor output energized state", sym, .i_measured = 1.0, .i_angle_measured = 0.0}, u_rated}; - check_null_current_sensor_output(output_result(branch3_sensor, state, solver_output, 0)); + check_null_current_sensor_output( + output_result(branch3_sensor, state, make_math_output(std::move(solver_output)), 0)); auto disconnected_coupling = std::make_shared(*state.topo_comp_coup); disconnected_coupling->branch = {{.group = disconnected, .pos = disconnected}}; @@ -555,7 +564,7 @@ TEST_CASE_TEMPLATE("Test main core current sensor output energized state", sym, .i_angle_measured = 0.0}, u_rated}; - check_null_current_sensor_output(output_result(sensor, state, solver_output, 0)); + check_null_current_sensor_output(output_result(sensor, state, make_math_output(std::move(solver_output)), 0)); } } } // namespace power_grid_model::main_core