diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bb14a5b4..711b87706 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,11 +13,18 @@ project(power_grid_model VERSION ${PGM_VERSION}) option(PGM_ENABLE_DEV_BUILD "Enable developer build (e.g.: tests)" OFF) -set(CMAKE_CXX_STANDARD 23) +set(PGM_C_STANDARD 11) +set(PGM_CXX_STANDARD 23) + +if(NOT CMAKE_CXX_STANDARD OR CMAKE_CXX_STANDARD LESS ${PGM_CXX_STANDARD}) + set(CMAKE_CXX_STANDARD ${PGM_CXX_STANDARD}) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_C_STANDARD 11) +if(NOT CMAKE_C_STANDARD OR CMAKE_C_STANDARD LESS ${PGM_C_STANDARD}) + set(CMAKE_C_STANDARD ${PGM_C_STANDARD}) +endif() set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_C_VISIBILITY_PRESET hidden) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/calculation.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/calculation.hpp new file mode 100644 index 000000000..6cef2efc2 --- /dev/null +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/calculation.hpp @@ -0,0 +1,121 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "main_model_fwd.hpp" + +#include "common/common.hpp" +#include "common/enum.hpp" + +#include "main_core/calculation_input_preparation.hpp" +#include "main_core/main_model_type.hpp" + +namespace power_grid_model { + +struct calculation_type_t {}; +struct power_flow_t : calculation_type_t {}; +struct state_estimation_t : calculation_type_t {}; +struct short_circuit_t : calculation_type_t {}; + +template +concept calculation_type_tag = std::derived_from; + +template +decltype(auto) calculation_symmetry_func_selector(CalculationSymmetry calculation_symmetry, Functor&& f, + Args&&... args) { + using enum CalculationSymmetry; + + switch (calculation_symmetry) { + case symmetric: + return std::forward(f).template operator()(std::forward(args)...); + case asymmetric: + return std::forward(f).template operator()(std::forward(args)...); + default: + throw MissingCaseForEnumError{"Calculation symmetry selector", calculation_symmetry}; + } +} + +template +decltype(auto) calculation_type_func_selector(CalculationType calculation_type, Functor&& f, Args&&... args) { + using enum CalculationType; + + switch (calculation_type) { + case CalculationType::power_flow: + return std::forward(f).template operator()(std::forward(args)...); + case CalculationType::state_estimation: + return std::forward(f).template operator()(std::forward(args)...); + case CalculationType::short_circuit: + return std::forward(f).template operator()(std::forward(args)...); + default: + throw MissingCaseForEnumError{"CalculationType", calculation_type}; + } +} + +template +decltype(auto) calculation_type_symmetry_func_selector(CalculationType calculation_type, + CalculationSymmetry calculation_symmetry, Functor&& f, + Args&&... args) { + calculation_type_func_selector( + calculation_type, + []( + CalculationSymmetry calculation_symmetry_, Functor_&& f_, Args_&&... args_) { + calculation_symmetry_func_selector( + calculation_symmetry_, + [](SubFunctor&& sub_f, + SubArgs&&... sub_args) { + std::forward(sub_f).template operator()( + std::forward(sub_args)...); + }, + std::forward(f_), std::forward(args_)...); + }, + calculation_symmetry, std::forward(f), std::forward(args)...); +} + +template struct Calculator; +template struct Calculator { + template + static auto preparer(State const& state, ComponentToMathCoupling& /*comp_coup*/, + MainModelOptions const& /*options*/) { + return [&state](Idx n_math_solvers) { return main_core::prepare_power_flow_input(state, n_math_solvers); }; + } + static auto solver(CalculationMethod calculation_method, MainModelOptions const& options, Logger& logger) { + return [calculation_method, err_tol = options.err_tol, max_iter = options.max_iter, + &logger](MathSolverProxy& solver, YBus const& y_bus, PowerFlowInput const& input) { + return solver.get().run_power_flow(input, err_tol, max_iter, logger, calculation_method, y_bus); + }; + } +}; +template struct Calculator { + template + static auto preparer(State const& state, ComponentToMathCoupling& /*comp_coup*/, + MainModelOptions const& /*options*/) { + return [&state](Idx n_math_solvers) { + return main_core::prepare_state_estimation_input(state, n_math_solvers); + }; + } + static auto solver(CalculationMethod calculation_method, MainModelOptions const& options, Logger& logger) { + return [calculation_method, err_tol = options.err_tol, max_iter = options.max_iter, + &logger](MathSolverProxy& solver, YBus const& y_bus, StateEstimationInput const& input) { + return solver.get().run_state_estimation(input, err_tol, max_iter, logger, calculation_method, y_bus); + }; + } +}; + +template struct Calculator { + template + static auto preparer(State const& state, ComponentToMathCoupling& comp_coup, MainModelOptions const& options) { + return [&state, &comp_coup, voltage_scaling = options.short_circuit_voltage_scaling](Idx n_math_solvers) { + return main_core::prepare_short_circuit_input(state, comp_coup, n_math_solvers, voltage_scaling); + }; + } + static auto solver(CalculationMethod calculation_method, MainModelOptions const& /*options*/, Logger& logger) { + return [calculation_method, &logger](MathSolverProxy& solver, YBus const& y_bus, + ShortCircuitInput const& input) { + return solver.get().run_short_circuit(input, logger, calculation_method, y_bus); + }; + } +}; + +} // namespace power_grid_model diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/counting_iterator.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/counting_iterator.hpp index 6ecc3c7b2..4e5ba1432 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/counting_iterator.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/counting_iterator.hpp @@ -21,4 +21,12 @@ struct IdxRange : public std::ranges::iota_view { }; using IdxCount = typename IdxRange::iterator; +#if __cpp_lib_ranges_enumerate >= 20202L +using std::views::enumerate; +#else +constexpr auto enumerate(std::ranges::viewable_range auto&& rng) { + return std::views::zip(IdxRange{std::ssize(rng)}, rng); +} +#endif + } // namespace power_grid_model diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/calculation_input_preparation.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/calculation_input_preparation.hpp index fd31acfe7..f3198164c 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/calculation_input_preparation.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/calculation_input_preparation.hpp @@ -5,8 +5,10 @@ #pragma once #include "state_queries.hpp" +#include "y_bus.hpp" #include "../calculation_parameters.hpp" +#include "../index_mapping.hpp" #include #include @@ -258,7 +260,7 @@ std::vector prepare_short_circuit_input(main_model_state_c au std::vector topo_fault_indices(state.math_topology.size()); std::vector topo_bus_indices(state.math_topology.size()); - for (Idx fault_idx{0}; fault_idx < state.components.template size(); ++fault_idx) { + for (Idx const fault_idx : IdxRange{state.components.template size()}) { auto const& fault = state.components.template get_item_by_seq(fault_idx); if (fault.status()) { auto const node_idx = state.components.template get_seq(fault.get_fault_object()); @@ -275,11 +277,11 @@ std::vector prepare_short_circuit_input(main_model_state_c au Idx2D{.group = isolated_component, .pos = not_connected}); std::vector sc_input(n_math_solvers); - for (Idx i = 0; i != n_math_solvers; ++i) { + for (Idx const i : IdxRange{n_math_solvers}) { auto map = build_dense_mapping(topo_bus_indices[i], state.math_topology[i]->n_bus()); - for (Idx reordered_idx{0}; reordered_idx < static_cast(map.reorder.size()); ++reordered_idx) { - fault_coup[topo_fault_indices[i][map.reorder[reordered_idx]]] = Idx2D{.group = i, .pos = reordered_idx}; + for (auto&& [reordered_idx, original_idx] : enumerate(map.reorder)) { + fault_coup[topo_fault_indices[i][original_idx]] = Idx2D{.group = i, .pos = reordered_idx}; } sc_input[i].fault_buses = {from_dense, std::move(map.indvector), state.math_topology[i]->n_bus()}; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/state_queries.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/state_queries.hpp index b3a5e64b7..b6a8f0075 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/state_queries.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/state_queries.hpp @@ -9,7 +9,6 @@ #include "../all_components.hpp" namespace power_grid_model::main_core { - template ComponentType, class ComponentContainer> requires model_component_state_c constexpr auto get_branch_nodes(MainModelState const& state, Idx topology_sequence_idx) { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/y_bus.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/y_bus.hpp index 183abcfd9..9febd634c 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/y_bus.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/y_bus.hpp @@ -4,6 +4,8 @@ #pragma once +#include "math_state.hpp" + #include "../common/common.hpp" namespace power_grid_model::main_core { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model_impl.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model_impl.hpp index 89b5bba88..ec0524ae6 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model_impl.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model_impl.hpp @@ -8,6 +8,7 @@ // main include #include "batch_parameter.hpp" +#include "calculation.hpp" #include "calculation_parameters.hpp" #include "calculation_preparation.hpp" #include "container.hpp" @@ -44,7 +45,6 @@ namespace power_grid_model { // solver output type to output type getter meta function - template struct output_type_getter; template struct output_type_getter { using type = meta_data::sc_output_getter_s; @@ -56,65 +56,6 @@ template <> struct output_type_getter> { using type = meta_data::asym_output_getter_s; }; -struct power_flow_t {}; -struct state_estimation_t {}; -struct short_circuit_t {}; - -template -concept calculation_type_tag = std::derived_from || std::derived_from || - std::derived_from; - -template -decltype(auto) calculation_symmetry_func_selector(CalculationSymmetry calculation_symmetry, Functor&& f, - Args&&... args) { - using enum CalculationSymmetry; - - switch (calculation_symmetry) { - case symmetric: - return std::forward(f).template operator()(std::forward(args)...); - case asymmetric: - return std::forward(f).template operator()(std::forward(args)...); - default: - throw MissingCaseForEnumError{"Calculation symmetry selector", calculation_symmetry}; - } -} - -template -decltype(auto) calculation_type_func_selector(CalculationType calculation_type, Functor&& f, Args&&... args) { - using enum CalculationType; - - switch (calculation_type) { - case CalculationType::power_flow: - return std::forward(f).template operator()(std::forward(args)...); - case CalculationType::state_estimation: - return std::forward(f).template operator()(std::forward(args)...); - case CalculationType::short_circuit: - return std::forward(f).template operator()(std::forward(args)...); - default: - throw MissingCaseForEnumError{"CalculationType", calculation_type}; - } -} - -template -decltype(auto) calculation_type_symmetry_func_selector(CalculationType calculation_type, - CalculationSymmetry calculation_symmetry, Functor&& f, - Args&&... args) { - calculation_type_func_selector( - calculation_type, - []( - CalculationSymmetry calculation_symmetry_, Functor_&& f_, Args_&&... args_) { - calculation_symmetry_func_selector( - calculation_symmetry_, - [](SubFunctor&& sub_f, - SubArgs&&... sub_args) { - std::forward(sub_f).template operator()( - std::forward(sub_args)...); - }, - std::forward(f_), std::forward(args_)...); - }, - calculation_symmetry, std::forward(f), std::forward(args)...); -} - template requires(main_core::is_main_model_type_v) class MainModelImpl { @@ -323,14 +264,18 @@ class MainModelImpl { }); } - template + template requires std::invocable, Idx /*n_math_solvers*/> && - std::invocable, MathSolverType&, YBus const&, InputType const&> && - std::same_as, std::vector> && - std::same_as, - SolverOutputType> - std::vector calculate_(PrepareInputFn&& prepare_input, SolveFn&& solve, Logger& logger) { + std::ranges::range> && + std::invocable< + std::remove_cvref_t, MathSolverType&, YBus const&, + typename std::invoke_result_t::const_reference> && + solver_output_type::const_reference>> + auto calculate_(PrepareInputFn&& prepare_input, SolveFn&& solve, Logger& logger) { + using InputType = typename std::invoke_result_t::const_reference; + using SolverOutputType = typename std::invoke_result_t; using sym = typename SolverOutputType::sym; assert(construction_complete_); @@ -357,85 +302,32 @@ class MainModelImpl { }(); } - template auto calculate_power_flow_(double err_tol, Idx max_iter, Logger& logger) { - return - [this, err_tol, max_iter, &logger](MainModelState const& state, - CalculationMethod calculation_method) -> std::vector> { - return calculate_, MathSolverProxy, YBus, PowerFlowInput>( - [&state](Idx n_math_solvers) { - return main_core::prepare_power_flow_input(state, n_math_solvers); - }, - [err_tol, max_iter, calculation_method, - &logger](MathSolverProxy& solver, YBus const& y_bus, PowerFlowInput const& input) { - return solver.get().run_power_flow(input, err_tol, max_iter, logger, calculation_method, y_bus); - }, - logger); - }; - } - - template auto calculate_state_estimation_(double err_tol, Idx max_iter, Logger& logger) { - return - [this, err_tol, max_iter, &logger](MainModelState const& state, - CalculationMethod calculation_method) -> std::vector> { - return calculate_, MathSolverProxy, YBus, StateEstimationInput>( - [&state](Idx n_math_solvers) { - return main_core::prepare_state_estimation_input(state, n_math_solvers); - }, - [err_tol, max_iter, calculation_method, &logger]( - MathSolverProxy& solver, YBus const& y_bus, StateEstimationInput const& input) { - return solver.get().run_state_estimation(input, err_tol, max_iter, logger, calculation_method, - y_bus); - }, - logger); - }; - } - - template - auto calculate_short_circuit_(ShortCircuitVoltageScaling voltage_scaling, Logger& logger) { - return [this, voltage_scaling, - &logger](MainModelState const& state, - CalculationMethod calculation_method) -> std::vector> { - (void)state; // to avoid unused-lambda-capture when in Release build - assert(&state == &state_); - - return calculate_, MathSolverProxy, YBus, ShortCircuitInput>( - [this, voltage_scaling](Idx n_math_solvers) { - assert(solvers_cache_status_.is_topology_valid()); - assert(solvers_cache_status_.template is_parameter_valid()); - return main_core::prepare_short_circuit_input(state_, state_.comp_coup, n_math_solvers, - voltage_scaling); - }, - [calculation_method, &logger](MathSolverProxy& solver, YBus const& y_bus, - ShortCircuitInput const& input) { - return solver.get().run_short_circuit(input, logger, calculation_method, y_bus); - }, - logger); - }; - } - // Calculate with optimization, e.g., automatic tap changer template auto calculate(Options const& options, Logger& logger) { - auto const calculator = [this, &options, &logger] { + auto const get_calculator = [this, &options, &logger] { + using Calc = Calculator; + assert(options.optimizer_type == OptimizerType::no_optimization || (std::derived_from)); - if constexpr (std::derived_from) { - return calculate_power_flow_(options.err_tol, options.max_iter, logger); - } else if constexpr (std::derived_from) { - return calculate_state_estimation_(options.err_tol, options.max_iter, logger); - } else if constexpr (std::derived_from) { - return calculate_short_circuit_(options.short_circuit_voltage_scaling, logger); - } else { - throw UnreachableHit{"MainModelImpl::calculate", "Unknown calculation type"}; - } - }(); + + return [this, &comp_coup = state_.comp_coup, &options, &logger](MainModelState const& state, + CalculationMethod calculation_method) { + (void)state; // to avoid unused-lambda-capture when in Release build + assert(&state == &state_); + + return calculate_, YBus>(Calc::preparer(state, comp_coup, options), + Calc::solver(calculation_method, options, logger), + logger); + }; + }; SearchMethod const& search_method = options.optimizer_strategy == OptimizerStrategy::any ? SearchMethod::linear_search : SearchMethod::binary_search; return optimizer::get_optimizer( - options.optimizer_type, options.optimizer_strategy, calculator, + options.optimizer_type, options.optimizer_strategy, get_calculator(), [this](ConstDataset const& update_data) { this->update_components(update_data); }, diff --git a/tests/cpp_unit_tests/test_counting_iterator.cpp b/tests/cpp_unit_tests/test_counting_iterator.cpp index 60f117cb6..3d374c156 100644 --- a/tests/cpp_unit_tests/test_counting_iterator.cpp +++ b/tests/cpp_unit_tests/test_counting_iterator.cpp @@ -30,4 +30,18 @@ TEST_CASE("Counting Iterator") { CHECK(*IdxRange{IdxRange{1, 3}.begin(), IdxRange{1, 3}.end()}.begin() == 1); CHECK(*(IdxRange{IdxRange{1, 3}.begin(), IdxRange{1, 3}.end()}.end() - 1) == 2); } + +TEST_CASE("Enumerate") { + IdxVector vec{10, 20, 30}; + auto enumerated = enumerate(vec); + auto it = enumerated.begin(); + CHECK(std::get<0>(*it) == 0); + CHECK(std::get<1>(*it) == 10); + ++it; + CHECK(std::get<0>(*it) == 1); + CHECK(std::get<1>(*it) == 20); + ++it; + CHECK(std::get<0>(*it) == 2); + CHECK(std::get<1>(*it) == 30); +} } // namespace power_grid_model