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 87c70004e1..429372b30a 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 @@ -8,6 +8,7 @@ #include "common/enum.hpp" #include "common/grouped_index_vector.hpp" #include "common/maybe_owning_view.hpp" +#include "common/small_vector.hpp" #include "common/statistics.hpp" #include "common/three_phase_tensor.hpp" @@ -410,10 +411,14 @@ struct OptimizerOutput { template struct SupernodeOutput; +// One entry per user node of a topological node. Only link-merged supernodes hold more than one, so +// inline room for a single element keeps the ordinary node from allocating at all. +template using UserNodeValueVector = SmallVector, 1>; + template struct SupernodeOutput { using sym = decode_symmetry_v; - ComplexValueVector bus_injection; // user bus output + UserNodeValueVector bus_injection; // user bus output std::vector> link; // user link }; template struct SupernodeOutput { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/small_vector.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/small_vector.hpp new file mode 100644 index 0000000000..cfa4ec956d --- /dev/null +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/small_vector.hpp @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "common.hpp" + +#include + +#include + +namespace power_grid_model { + +// A vector that keeps room for the first N elements inside the object itself and only allocates +// beyond that. Use it where a container is almost always short but occasionally is not. N is a +// per-use-site decision: too large inflates every object for slots that go unused. +template using SmallVector = boost::container::small_vector; + +} // namespace power_grid_model 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 bc060310ae..b771004209 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 @@ -21,8 +21,8 @@ namespace power_grid_model::main_core { namespace detail { template struct SuperNodeSolverInput { std::span links; - ComplexValueVector node_injection; - ComplexValueVector node_flow_from_branch; + UserNodeValueVector node_injection; + UserNodeValueVector node_flow_from_branch; ComplexValueVector get_total_injection_per_node() const { assert(node_injection.size() == node_flow_from_branch.size()); @@ -225,8 +225,8 @@ solve_topological_nodes(LinkSolver link_solver, State const& state, std::views::transform([](auto const& topo_node) -> SuperNodeSolverInput { auto const node_number = topo_node.user_nodes.size(); return {.links = std::span{topo_node.user_links}, - .node_injection = ComplexValueVector(node_number), - .node_flow_from_branch = ComplexValueVector(node_number)}; + .node_injection = UserNodeValueVector(node_number), + .node_flow_from_branch = UserNodeValueVector(node_number)}; }) | std::ranges::to(); 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 b81337043e..3136f42d61 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 @@ -532,9 +532,10 @@ TEST_CASE("Test topological node output") { CHECK(mock.recorded_loads[1] == ComplexVector{DoubleComplex{}}); REQUIRE(result.size() == 2); - CHECK(result[0].bus_injection == - ComplexVector{dummy_complex_value_sym(), dummy_complex_value_sym(), dummy_complex_value_sym()}); - CHECK(result[1].bus_injection == ComplexVector{DoubleComplex{}}); + CHECK(result[0].bus_injection == UserNodeValueVector{dummy_complex_value_sym(), + dummy_complex_value_sym(), + dummy_complex_value_sym()}); + CHECK(result[1].bus_injection == UserNodeValueVector{DoubleComplex{}}); REQUIRE(result[0].link.size() == 2); check_close(result[0].link[0].s_f, 2.0 * dummy_complex_value_sym()); check_close(result[0].link[0].s_t, -2.0 * dummy_complex_value_sym());