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 f752f20e5..acadf9e14 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 @@ -12,7 +12,6 @@ #include namespace power_grid_model::main_core { -constexpr Idx isolated_component{-1}; constexpr Idx not_connected{-1}; namespace detail { 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 9f6a8b0cb..183abcfd9 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 @@ -7,6 +7,7 @@ #include "../common/common.hpp" namespace power_grid_model::main_core { +constexpr Idx isolated_component{-1}; namespace detail { template ComponentType, typename ComponentContainer> diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp index b7cac93a1..64749df29 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_model.hpp @@ -10,6 +10,8 @@ #include "common/calculation_info.hpp" +#include "prepare_calculate.hpp" + #include namespace power_grid_model { @@ -25,10 +27,14 @@ class MainModel { explicit MainModel(double system_frequency, ConstDataset const& input_data, MathSolverDispatcher const& math_solver_dispatcher, Idx pos = 0) - : impl_{std::make_unique(system_frequency, input_data, math_solver_dispatcher, pos)} {} + : impl_{std::make_unique( + system_frequency, input_data, + SolverPreparationContext{.math_state = {}, .math_solver_dispatcher = &math_solver_dispatcher}, pos)} {} explicit MainModel(double system_frequency, meta_data::MetaData const& meta_data, MathSolverDispatcher const& math_solver_dispatcher) - : impl_{std::make_unique(system_frequency, meta_data, math_solver_dispatcher)} {}; + : impl_{std::make_unique( + system_frequency, meta_data, + SolverPreparationContext{.math_state = {}, .math_solver_dispatcher = &math_solver_dispatcher})} {}; // deep copy MainModel(MainModel const& other) { 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 6599f69c8..62b95873c 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 @@ -11,6 +11,7 @@ #include "calculation_parameters.hpp" #include "container.hpp" #include "main_model_fwd.hpp" +#include "prepare_calculate.hpp" #include "topology.hpp" // common @@ -33,11 +34,9 @@ #include "main_core/calculation_input_preparation.hpp" #include "main_core/input.hpp" #include "main_core/main_model_type.hpp" -#include "main_core/math_state.hpp" #include "main_core/output.hpp" #include "main_core/topology.hpp" #include "main_core/update.hpp" -#include "main_core/y_bus.hpp" // stl library #include @@ -122,7 +121,6 @@ class MainModelImpl { private: // internal type traits - using ComponentContainer = typename ModelType::ComponentContainer; using MainModelState = typename ModelType::MainModelState; using SequenceIdx = typename ModelType::SequenceIdx; @@ -131,22 +129,17 @@ class MainModelImpl { using OwnedUpdateDataset = typename ModelType::OwnedUpdateDataset; using ComponentFlags = typename ModelType::ComponentFlags; - static constexpr Idx isolated_component{main_core::isolated_component}; - static constexpr Idx not_connected{main_core::not_connected}; - static constexpr Idx sequential{main_core::utils::sequential}; - public: using ImplType = ModelType; using Options = MainModelOptions; - using MathState = main_core::MathState; using MetaData = meta_data::MetaData; // constructor with data explicit MainModelImpl(double system_frequency, ConstDataset const& input_data, - MathSolverDispatcher const& math_solver_dispatcher, Idx pos = 0) + SolverPreparationContext solver_preparation_context, Idx pos = 0) : system_frequency_{system_frequency}, meta_data_{&input_data.meta_data()}, - math_solver_dispatcher_{&math_solver_dispatcher} { + solver_preparation_context_{std::move(solver_preparation_context)} { assert(input_data.get_description().dataset->name == std::string_view("input")); add_components(input_data, pos); set_construction_complete(); @@ -154,10 +147,10 @@ class MainModelImpl { // constructor with only frequency explicit MainModelImpl(double system_frequency, meta_data::MetaData const& meta_data, - MathSolverDispatcher const& math_solver_dispatcher) + SolverPreparationContext solver_preparation_context) : system_frequency_{system_frequency}, meta_data_{&meta_data}, - math_solver_dispatcher_{&math_solver_dispatcher} {} + solver_preparation_context_{std::move(solver_preparation_context)} {} // helper function to get what components are present in the update data ComponentFlags get_components_to_update(ConstDataset const& update_data) const { @@ -206,7 +199,7 @@ class MainModelImpl { UpdateChange const changed = main_core::update::update_component( state_.components, std::forward(updates), - std::back_inserter(std::get(parameter_changed_components_)), sequence_idx); + std::back_inserter(std::get(state_status_context_.parameter_changed_components)), sequence_idx); // update, get changed variable update_state(changed); @@ -266,18 +259,6 @@ class MainModelImpl { std::make_shared(main_core::construct_topology(state_.components)); } - void reset_solvers() { - assert(construction_complete_); - is_topology_up_to_date_ = false; - is_sym_parameter_up_to_date_ = false; - is_asym_parameter_up_to_date_ = false; - n_math_solvers_ = 0; - main_core::clear(math_state_); - state_.math_topology.clear(); - state_.topo_comp_coup.reset(); - state_.comp_coup = {}; - } - public: /* the the sequence indexer given an input array of ID's for a given component type @@ -307,9 +288,11 @@ class MainModelImpl { void update_state(UpdateChange const& changes) { // if topology changed, everything is not up to date // if only param changed, set param to not up to date - is_topology_up_to_date_ = is_topology_up_to_date_ && !changes.topo; - is_sym_parameter_up_to_date_ = is_sym_parameter_up_to_date_ && !changes.topo && !changes.param; - is_asym_parameter_up_to_date_ = is_asym_parameter_up_to_date_ && !changes.topo && !changes.param; + state_status_context_.is_topology_up_to_date = state_status_context_.is_topology_up_to_date && !changes.topo; + state_status_context_.is_parameter_up_to_date.sym = + state_status_context_.is_parameter_up_to_date.sym && !changes.topo && !changes.param; + state_status_context_.is_parameter_up_to_date.asym = + state_status_context_.is_parameter_up_to_date.asym && !changes.topo && !changes.param; } template void restore_component(SequenceIdxView const& sequence_idx) { @@ -363,18 +346,20 @@ class MainModelImpl { // prepare auto const& input = [this, &logger, prepare_input_ = std::forward(prepare_input)] { Timer const timer{logger, LogEvent::prepare}; - prepare_solvers(); - assert(is_topology_up_to_date_ && is_parameter_up_to_date()); - return prepare_input_(n_math_solvers_); + assert(construction_complete_); + prepare_solvers(state_, solver_preparation_context_, state_status_context_); + assert((state_status_context_.is_topology_up_to_date && + is_parameter_up_to_date(state_status_context_.is_parameter_up_to_date))); + return prepare_input_(get_n_math_solvers(state_)); }(); // calculate return [this, &logger, &input, solve_ = std::forward(solve)] { Timer const timer{logger, LogEvent::math_calculation}; - auto& solvers = main_core::get_solvers(math_state_); - auto& y_bus_vec = main_core::get_y_bus(math_state_); + auto& solvers = main_core::get_solvers(solver_preparation_context_.math_state); + auto& y_bus_vec = main_core::get_y_bus(solver_preparation_context_.math_state); std::vector solver_output; - solver_output.reserve(n_math_solvers_); - for (Idx i = 0; i != n_math_solvers_; ++i) { + solver_output.reserve(get_n_math_solvers(state_)); + for (Idx i = 0; i != get_n_math_solvers(state_); ++i) { solver_output.emplace_back(solve_(solvers[i], y_bus_vec[i], input[i])); } return solver_output; @@ -424,7 +409,8 @@ class MainModelImpl { return calculate_, MathSolverProxy, YBus, ShortCircuitInput>( [this, voltage_scaling](Idx n_math_solvers) { - assert(is_topology_up_to_date_ && is_parameter_up_to_date()); + assert((state_status_context_.is_topology_up_to_date && + is_parameter_up_to_date(state_status_context_.is_parameter_up_to_date))); return main_core::prepare_short_circuit_input(state_, state_.comp_coup, n_math_solvers, voltage_scaling); }, @@ -542,89 +528,19 @@ class MainModelImpl { double system_frequency_; MetaData const* meta_data_; - MathSolverDispatcher const* math_solver_dispatcher_; MainModelState state_; - // math model - MathState math_state_; - Idx n_math_solvers_{0}; - bool is_topology_up_to_date_{false}; - bool is_sym_parameter_up_to_date_{false}; - bool is_asym_parameter_up_to_date_{false}; - bool is_accumulated_component_updated_{true}; - bool last_updated_calculation_symmetry_mode_{false}; + + SolverPreparationContext solver_preparation_context_; + + StatusCheckingContext state_status_context_{}; OwnedUpdateDataset cached_inverse_update_{}; UpdateChange cached_state_changes_{}; - SequenceIdx parameter_changed_components_{}; #ifndef NDEBUG // construction_complete is used for debug assertions only bool construction_complete_{false}; #endif // !NDEBUG - - template bool& is_parameter_up_to_date() { - if constexpr (is_symmetric_v) { - return is_sym_parameter_up_to_date_; - } else { - return is_asym_parameter_up_to_date_; - } - } - - void rebuild_topology() { - assert(construction_complete_); - // clear old solvers - reset_solvers(); - ComponentConnections const comp_conn = - main_core::construct_components_connections(state_.components); - // re build - Topology topology{*state_.comp_topo, comp_conn}; - std::tie(state_.math_topology, state_.topo_comp_coup) = topology.build_topology(); - n_math_solvers_ = static_cast(state_.math_topology.size()); - is_topology_up_to_date_ = true; - is_sym_parameter_up_to_date_ = false; - is_asym_parameter_up_to_date_ = false; - } - - template void prepare_solvers() { - std::vector>& solvers = main_core::get_solvers(math_state_); - // rebuild topology if needed - if (!is_topology_up_to_date_) { - rebuild_topology(); - } - main_core::prepare_y_bus(state_, n_math_solvers_, math_state_); - - if (n_math_solvers_ != static_cast(solvers.size())) { - assert(solvers.empty()); - assert(n_math_solvers_ == static_cast(state_.math_topology.size())); - assert(n_math_solvers_ == static_cast(main_core::get_y_bus(math_state_).size())); - - solvers.clear(); - solvers.reserve(n_math_solvers_); - std::ranges::transform(state_.math_topology, std::back_inserter(solvers), [this](auto const& math_topo) { - return MathSolverProxy{math_solver_dispatcher_, math_topo}; - }); - for (Idx idx = 0; idx < n_math_solvers_; ++idx) { - main_core::get_y_bus(math_state_)[idx].register_parameters_changed_callback( - [solver = std::ref(solvers[idx])](bool changed) { - solver.get().get().parameters_changed(changed); - }); - } - } else if (!is_parameter_up_to_date()) { - std::vector> const math_params = - main_core::get_math_param(state_, n_math_solvers_); - std::vector const math_param_increments = - main_core::get_math_param_increment(state_, n_math_solvers_, parameter_changed_components_); - if (last_updated_calculation_symmetry_mode_ == is_symmetric_v) { - main_core::update_y_bus(math_state_, math_params, math_param_increments); - } else { - main_core::update_y_bus(math_state_, math_params); - } - } - // else do nothing, set everything up to date - is_parameter_up_to_date() = true; - std::ranges::for_each(parameter_changed_components_, [](auto& comps) { comps.clear(); }); - last_updated_calculation_symmetry_mode_ = is_symmetric_v; - } }; } // namespace power_grid_model diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/prepare_calculate.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/prepare_calculate.hpp new file mode 100644 index 000000000..48d8de866 --- /dev/null +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/prepare_calculate.hpp @@ -0,0 +1,119 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#pragma once + +#include "topology.hpp" + +#include "common/common.hpp" + +#include "math_solver/math_solver_dispatch.hpp" + +#include "main_core/main_model_type.hpp" +#include "main_core/math_state.hpp" +#include "main_core/topology.hpp" +#include "main_core/y_bus.hpp" + +namespace power_grid_model { +struct SolverPreparationContext { + main_core::MathState math_state; + MathSolverDispatcher const* math_solver_dispatcher; +}; + +template + requires(main_core::is_main_model_type_v) +struct StatusCheckingContext { + bool is_topology_up_to_date{false}; + bool last_updated_calculation_symmetry_mode{false}; + typename ModelType::SequenceIdx parameter_changed_components{}; + struct IsParameterUpToDateHelper { + bool sym{false}; + bool asym{false}; + }; + IsParameterUpToDateHelper is_parameter_up_to_date{}; +}; + +namespace detail { +template +void reset_solvers(typename ModelType::MainModelState& state, SolverPreparationContext& solver_context, + StatusCheckingContext& status_context) { + status_context.is_topology_up_to_date = false; + status_context.is_parameter_up_to_date.sym = false; + status_context.is_parameter_up_to_date.asym = false; + main_core::clear(solver_context.math_state); + state.math_topology.clear(); + state.topo_comp_coup.reset(); + state.comp_coup = {}; +} + +template +void rebuild_topology(typename ModelType::MainModelState& state, SolverPreparationContext& solver_context, + StatusCheckingContext& status_context) { + // clear old solvers + reset_solvers(state, solver_context, status_context); + ComponentConnections const comp_conn = main_core::construct_components_connections(state.components); + // re build + Topology topology{*state.comp_topo, comp_conn}; + std::tie(state.math_topology, state.topo_comp_coup) = topology.build_topology(); + status_context.is_topology_up_to_date = true; + status_context.is_parameter_up_to_date.sym = false; + status_context.is_parameter_up_to_date.asym = false; +} +} // namespace detail + +template +bool& is_parameter_up_to_date( + typename StatusCheckingContext::IsParameterUpToDateHelper& is_parameter_up_to_date) { + if constexpr (is_symmetric_v) { + return is_parameter_up_to_date.sym; + } else { + return is_parameter_up_to_date.asym; + } +} + +template Idx get_n_math_solvers(typename ModelType::MainModelState const& state) { + return static_cast(state.math_topology.size()); +} + +template +void prepare_solvers(typename ModelType::MainModelState& state, SolverPreparationContext& solver_context, + StatusCheckingContext& status_context) { + std::vector>& solvers = main_core::get_solvers(solver_context.math_state); + // rebuild topology if needed + if (!status_context.is_topology_up_to_date) { + detail::rebuild_topology(state, solver_context, status_context); + } + Idx const n_math_solvers = get_n_math_solvers(state); + main_core::prepare_y_bus(state, n_math_solvers, solver_context.math_state); + if (n_math_solvers != static_cast(solvers.size())) { + assert(solvers.empty()); + assert(n_math_solvers == static_cast(main_core::get_y_bus(solver_context.math_state).size())); + + solvers.clear(); + solvers.reserve(n_math_solvers); + std::ranges::transform(state.math_topology, std::back_inserter(solvers), + [&solver_context](auto const& math_topo) { + return MathSolverProxy{solver_context.math_solver_dispatcher, math_topo}; + }); + for (Idx idx = 0; idx < n_math_solvers; ++idx) { + main_core::get_y_bus(solver_context.math_state)[idx].register_parameters_changed_callback( + [solver = std::ref(solvers[idx])](bool changed) { solver.get().get().parameters_changed(changed); }); + } + } else if (!is_parameter_up_to_date(status_context.is_parameter_up_to_date)) { + std::vector> const math_params = main_core::get_math_param(state, n_math_solvers); + std::vector const math_param_increments = + main_core::get_math_param_increment(state, n_math_solvers, + status_context.parameter_changed_components); + if (status_context.last_updated_calculation_symmetry_mode == is_symmetric_v) { + main_core::update_y_bus(solver_context.math_state, math_params, math_param_increments); + } else { + main_core::update_y_bus(solver_context.math_state, math_params); + } + } + // else do nothing, set everything up to date + is_parameter_up_to_date(status_context.is_parameter_up_to_date) = true; + std::ranges::for_each(status_context.parameter_changed_components, [](auto& comps) { comps.clear(); }); + status_context.last_updated_calculation_symmetry_mode = is_symmetric_v; +} +} // namespace power_grid_model diff --git a/tests/cpp_unit_tests/test_main_model_type.cpp b/tests/cpp_unit_tests/test_main_model_type.cpp index 59533a9b3..26366c44c 100644 --- a/tests/cpp_unit_tests/test_main_model_type.cpp +++ b/tests/cpp_unit_tests/test_main_model_type.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -30,7 +31,7 @@ static_assert(!is_main_model_type_v> static_assert(!is_main_model_type_v>); static_assert(std::constructible_from>, double, - meta_data::MetaData const&, MathSolverDispatcher const&>); + meta_data::MetaData const&, SolverPreparationContext>); static_assert(detail::validate_component_types_c); @@ -57,7 +58,7 @@ TEST_CASE("MainModelType") { static_assert(ModelType::n_types == 2); static_assert(is_main_model_type_v); static_assert(std::constructible_from, double, meta_data::MetaData const&, - MathSolverDispatcher const&>); + SolverPreparationContext>); CHECK(ModelType::run_functor_with_all_component_types_return_array([]() { return std::string_view(CompType::name); @@ -89,7 +90,7 @@ TEST_CASE("MainModelType") { static_assert(ModelType::n_types == 3); static_assert(is_main_model_type_v); static_assert(std::constructible_from, double, meta_data::MetaData const&, - MathSolverDispatcher const&>); + SolverPreparationContext>); CHECK(ModelType::run_functor_with_all_component_types_return_array([]() { return std::string_view(CompType::name); @@ -122,7 +123,7 @@ TEST_CASE("MainModelType") { static_assert(is_main_model_type_v); static_assert(std::constructible_from, double, meta_data::MetaData const&, - MathSolverDispatcher const&>); + SolverPreparationContext>); CHECK(ModelType::run_functor_with_all_component_types_return_array([]() { return std::string_view(CompType::name); @@ -155,7 +156,7 @@ TEST_CASE("MainModelType") { static_assert(ModelType::n_types == 3); static_assert(is_main_model_type_v); static_assert(std::constructible_from, double, meta_data::MetaData const&, - MathSolverDispatcher const&>); + SolverPreparationContext>); CHECK(ModelType::run_functor_with_all_component_types_return_array([]() { return std::string_view(CompType::name);