diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/core_utils.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/core_utils.hpp index a87cc8dd7..93fb6d065 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/core_utils.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/core_utils.hpp @@ -12,6 +12,8 @@ namespace power_grid_model::main_core::utils { +constexpr Idx invalid_index{-1}; + template constexpr size_t n_types = sizeof...(ComponentTypes); template constexpr size_t index_of_component = container_impl::get_cls_pos_v; diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/update.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/update.hpp index 958f48f77..25a304f17 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/update.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/main_core/update.hpp @@ -15,8 +15,6 @@ namespace power_grid_model::main_core::update { -constexpr Idx invalid_index{-1}; - namespace detail { template ForwardIterator, typename Func> requires std::invocable, typename Component::UpdateType, Idx2D const&> @@ -47,16 +45,15 @@ template bool check_id_na(T const& obj) { namespace independence { struct UpdateCompProperties { - std::string name{}; - bool has_any_elements{false}; // whether the component has any elements in the update data - bool ids_all_na{false}; // whether all ids are all NA - bool ids_part_na{false}; // whether some ids are NA but some are not - bool dense{false}; // whether the component is dense - bool uniform{false}; // whether the component is uniform - bool is_columnar{false}; // whether the component is columnar - bool update_ids_match{false}; // whether the ids match - Idx elements_ps_in_update{invalid_index}; // count of elements for this component per scenario in update - Idx elements_in_base{invalid_index}; // count of elements for this component per scenario in input + bool has_any_elements{false}; // whether the component has any elements in the update data + bool ids_all_na{false}; // whether all ids are all NA + bool ids_part_na{false}; // whether some ids are NA but some are not + bool dense{false}; // whether the component is dense + bool uniform{false}; // whether the component is uniform + bool is_columnar{false}; // whether the component is columnar + bool update_ids_match{false}; // whether the ids match + Idx elements_ps_in_update{utils::invalid_index}; // count of elements for this component per scenario in update + Idx elements_in_base{utils::invalid_index}; // count of elements for this component per scenario in input constexpr bool no_id() const { return !has_any_elements || ids_all_na; } constexpr bool qualify_for_optional_id() const { @@ -68,7 +65,7 @@ struct UpdateCompProperties { constexpr bool is_empty_component() const { return !has_any_elements; } constexpr bool is_independent() const { return qualify_for_optional_id() || provided_ids_valid(); } constexpr Idx get_n_elements() const { - assert(uniform || elements_ps_in_update == invalid_index); + assert(uniform || elements_ps_in_update == utils::invalid_index); return qualify_for_optional_id() ? elements_ps_in_update : na_Idx; } @@ -107,15 +104,14 @@ template void process_buffer_span(auto const& all_spans, Upd template UpdateCompProperties check_component_independence(ConstDataset const& update_data, Idx n_component) { UpdateCompProperties properties; - properties.name = CompType::name; - auto const component_idx = update_data.find_component(properties.name, false); - properties.is_columnar = update_data.is_columnar(properties.name); - properties.dense = update_data.is_dense(properties.name); - properties.uniform = update_data.is_uniform(properties.name); + auto const component_idx = update_data.find_component(CompType::name, false); + properties.is_columnar = update_data.is_columnar(CompType::name); + properties.dense = update_data.is_dense(CompType::name); + properties.uniform = update_data.is_uniform(CompType::name); properties.has_any_elements = - component_idx != invalid_index && update_data.get_component_info(component_idx).total_elements > 0; + component_idx != utils::invalid_index && update_data.get_component_info(component_idx).total_elements > 0; properties.elements_ps_in_update = - properties.uniform ? update_data.uniform_elements_per_scenario(properties.name) : invalid_index; + properties.uniform ? update_data.uniform_elements_per_scenario(CompType::name) : utils::invalid_index; properties.elements_in_base = n_component; if (properties.is_columnar) { @@ -130,7 +126,10 @@ UpdateCompProperties check_component_independence(ConstDataset const& update_dat return properties; } -inline void validate_update_data_independence(UpdateCompProperties const& comp) { +template +using UpdateIndependence = std::array>; + +inline void validate_update_data_independence(UpdateCompProperties const& comp, std::string const& comp_name) { if (comp.is_empty_component()) { return; // empty dataset is still supported } @@ -138,30 +137,26 @@ inline void validate_update_data_independence(UpdateCompProperties const& comp) assert(comp.uniform || elements_ps < 0); if (elements_ps >= 0 && comp.elements_in_base < elements_ps) { - throw DatasetError("Update data has more elements per scenario than input data for component " + comp.name + + throw DatasetError("Update data has more elements per scenario than input data for component " + comp_name + "!"); } if (comp.ids_part_na) { - throw DatasetError("IDs contain both numbers and NANs for component " + comp.name + " in update data!"); + throw DatasetError("IDs contain both numbers and NANs for component " + comp_name + " in update data!"); } if (comp.ids_all_na && comp.elements_in_base != elements_ps) { - throw DatasetError("Update data without IDs for component " + comp.name + + throw DatasetError("Update data without IDs for component " + comp_name + " has a different number of elements per scenario then input data!"); } } -template -main_core::utils::ComponentFlags -is_update_independent(ConstDataset const& update_data, std::span relevant_component_count) { - main_core::utils::ComponentFlags result{}; - size_t comp_idx{}; - utils::run_functor_with_all_types_return_void( - [&result, &relevant_component_count, &update_data, &comp_idx]() { - Idx const n_component = relevant_component_count[comp_idx]; - result[comp_idx] = check_component_independence(update_data, n_component).is_independent(); - ++comp_idx; +template +UpdateIndependence check_update_independence(MainModelState const& state, + ConstDataset const& update_data) { + return utils::run_functor_with_all_types_return_array( + [&state, &update_data]() { + auto const n_component = state.components.template size(); + return check_component_independence(update_data, n_component); }); - return result; } } // namespace independence @@ -221,35 +216,26 @@ std::vector get_component_sequence(MainModelState con } // namespace detail template -main_core::utils::SequenceIdx +utils::SequenceIdx get_all_sequence_idx_map(MainModelState const& state, ConstDataset const& update_data, - Idx scenario_idx, - main_core::utils::ComponentFlags const& components_to_store) { + Idx scenario_idx, utils::ComponentFlags const& components_to_store, + independence::UpdateIndependence const& independence, bool cached) { return utils::run_functor_with_all_types_return_array( - [&update_data, &components_to_store, &state, scenario_idx]() { - if (!std::get>(components_to_store)) { + [&state, &update_data, scenario_idx, &components_to_store, &independence, cached]() { + auto const component_properties = + std::get>(independence); + // The sequence for the independent components is cached (true). For the remaining components, the sequence + // cannot be cached (false), so the independence flags are inverted to not return an empty sequence when + // this is the case. + + if (bool const component_independence = cached != component_properties.is_independent(); + !component_independence || + !std::get>(components_to_store)) { return std::vector{}; } - auto const n_components = state.components.template size(); - auto const independence = independence::check_component_independence(update_data, n_components); - independence::validate_update_data_independence(independence); - return detail::get_component_sequence(state, update_data, scenario_idx, independence); - }); -} -// Get sequence idx map of an entire batch for fast caching of component sequences. -// The sequence idx map of the batch is the same as that of the first scenario in the batch (assuming homogeneity) -// This is the entry point for permanent updates. -template -main_core::utils::SequenceIdx -get_all_sequence_idx_map(MainModelState const& state, ConstDataset const& update_data) { - main_core::utils::ComponentFlags components_to_store{}; - Idx comp_idx{}; - utils::run_functor_with_all_types_return_void( - [&update_data, &components_to_store, &comp_idx]() { - components_to_store[comp_idx] = (update_data.find_component(CompType::name, false) != invalid_index); - ++comp_idx; + independence::validate_update_data_independence(component_properties, CompType::name); + return detail::get_component_sequence(state, update_data, scenario_idx, component_properties); }); - return get_all_sequence_idx_map(state, update_data, 0, components_to_store); } // template to update components 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 cc749d64a..4c7cfa288 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 @@ -5,6 +5,7 @@ #pragma once #include "main_model_impl.hpp" + #include namespace power_grid_model { @@ -67,7 +68,7 @@ class MainModel { } template void update_components(ConstDataset const& update_data) { - impl().update_components(update_data); + impl().update_components(update_data.get_individual_scenario(0)); } template 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 88324ffc0..b4b8b3ab0 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 @@ -47,7 +47,7 @@ namespace power_grid_model { namespace detail { template -static auto calculate_param(auto const& c, auto const&... extra_args) +inline auto calculate_param(auto const& c, auto const&... extra_args) requires requires { { c.calc_param(extra_args...) }; } @@ -56,7 +56,7 @@ static auto calculate_param(auto const& c, auto const&... extra_args) } template -static auto calculate_param(auto const& c, auto const&... extra_args) +inline auto calculate_param(auto const& c, auto const&... extra_args) requires requires { { c.template calc_param(extra_args...) }; } @@ -172,22 +172,13 @@ class MainModelImpl, ComponentLis explicit MainModelImpl(double system_frequency, meta_data::MetaData const& meta_data) : system_frequency_{system_frequency}, meta_data_{&meta_data} {} - // get number - template Idx component_count() const { - assert(construction_complete_); - return state_.components.template size(); - } - - // helper function to get the number of components per type - std::array> get_n_components_per_type() const { - std::array> result{}; - size_t idx{}; - main_core::utils::run_functor_with_all_types_return_void( - [&result, this, &idx]() { - result[idx] = this->component_count(); - ++idx; + // helper function to get what components are present in the update data + std::array> + get_components_to_update(ConstDataset const& update_data) const { + return main_core::utils::run_functor_with_all_types_return_array( + [&update_data]() { + return (update_data.find_component(CompType::name, false) != main_core::utils::invalid_index); }); - return result; } // helper function to add vectors of components @@ -301,8 +292,11 @@ class MainModelImpl, ComponentLis } // overload to update all components in the first scenario (e.g. permanent update) template void update_components(ConstDataset const& update_data) { - auto const sequence_idx_map = - main_core::update::get_all_sequence_idx_map(state_, update_data); + auto const components_to_update = get_components_to_update(update_data); + auto const update_independence = + main_core::update::independence::check_update_independence(state_, update_data); + auto const sequence_idx_map = main_core::update::get_all_sequence_idx_map( + state_, update_data, 0, components_to_update, update_independence, false); update_components(update_data, 0, sequence_idx_map); } @@ -388,9 +382,12 @@ class MainModelImpl, ComponentLis } // Entry point for main_model.hpp - template main_core::utils::SequenceIdx get_all_sequence_idx_map(ConstDataset const& update_data) { - return main_core::update::get_all_sequence_idx_map(state_, update_data); + auto const components_to_update = get_components_to_update(update_data); + auto const update_independence = + main_core::update::independence::check_update_independence(state_, update_data); + return main_core::update::get_all_sequence_idx_map( + state_, update_data, 0, components_to_update, update_independence, false); } private: @@ -553,15 +550,15 @@ class MainModelImpl, ComponentLis // cache component update order where possible. // the order for a cacheable (independent) component by definition is the same across all scenarios - auto const relevant_component_count = get_n_components_per_type(); - auto const is_independent = main_core::update::independence::is_update_independent( - update_data, relevant_component_count); - all_scenarios_sequence = - main_core::update::get_all_sequence_idx_map(state_, update_data, 0, is_independent); + auto const components_to_update = get_components_to_update(update_data); + auto const update_independence = + main_core::update::independence::check_update_independence(state_, update_data); + all_scenarios_sequence = main_core::update::get_all_sequence_idx_map( + state_, update_data, 0, components_to_update, update_independence, false); return [&base_model, &exceptions, &infos, &calculation_fn, &result_data, &update_data, - &all_scenarios_sequence = std::as_const(all_scenarios_sequence), - is_independent](Idx start, Idx stride, Idx n_scenarios) { + &all_scenarios_sequence_ = std::as_const(all_scenarios_sequence), components_to_update, + update_independence](Idx start, Idx stride, Idx n_scenarios) { assert(n_scenarios <= narrow_cast(exceptions.size())); assert(n_scenarios <= narrow_cast(infos.size())); @@ -574,8 +571,9 @@ class MainModelImpl, ComponentLis auto model = copy_model_functor(start); auto current_scenario_sequence_cache = main_core::utils::SequenceIdx{}; - auto [setup, winddown] = scenario_update_restore(model, update_data, is_independent, all_scenarios_sequence, - current_scenario_sequence_cache, infos); + auto [setup, winddown] = + scenario_update_restore(model, update_data, components_to_update, update_independence, + all_scenarios_sequence_, current_scenario_sequence_cache, infos); auto calculate_scenario = MainModelImpl::call_with( [&model, &calculation_fn, &result_data, &infos](Idx scenario_idx) { @@ -647,24 +645,22 @@ class MainModelImpl, ComponentLis }; } - static auto - scenario_update_restore(MainModelImpl& model, ConstDataset const& update_data, - main_core::utils::ComponentFlags const& independence_flags, - main_core::utils::SequenceIdx const& all_scenario_sequence, - main_core::utils::SequenceIdx& current_scenario_sequence_cache, - std::vector& infos) noexcept { - auto do_update_cache = [&independence_flags] { - main_core::utils::ComponentFlags result; - std::ranges::transform(independence_flags, result.begin(), std::logical_not<>{}); - return result; - }(); - + static auto scenario_update_restore( + MainModelImpl& model, ConstDataset const& update_data, + main_core::utils::ComponentFlags const& components_to_store, + main_core::update::independence::UpdateIndependence const& do_update_cache, + main_core::utils::SequenceIdx const& all_scenario_sequence, + main_core::utils::SequenceIdx& current_scenario_sequence_cache, + std::vector& infos) noexcept { + main_core::utils::ComponentFlags independence_flags{}; + std::ranges::transform(do_update_cache, independence_flags.begin(), + [](auto const& comp) { return comp.is_independent(); }); auto const scenario_sequence = [&all_scenario_sequence, ¤t_scenario_sequence_cache, - &independence_flags]() -> SequenceIdxView { + independence_flags_ = std::move(independence_flags)]() -> SequenceIdxView { return main_core::utils::run_functor_with_all_types_return_array( - [&all_scenario_sequence, ¤t_scenario_sequence_cache, &independence_flags]() { + [&all_scenario_sequence, ¤t_scenario_sequence_cache, &independence_flags_]() { constexpr auto comp_idx = main_core::utils::index_of_component; - if (std::get(independence_flags)) { + if (std::get(independence_flags_)) { return std::span{std::get(all_scenario_sequence)}; } return std::span{std::get(current_scenario_sequence_cache)}; @@ -672,11 +668,11 @@ class MainModelImpl, ComponentLis }; return std::make_pair( - [&model, &update_data, scenario_sequence, ¤t_scenario_sequence_cache, + [&model, &update_data, scenario_sequence, ¤t_scenario_sequence_cache, &components_to_store, do_update_cache_ = std::move(do_update_cache), &infos](Idx scenario_idx) { Timer const t_update_model(infos[scenario_idx], 1200, "Update model"); current_scenario_sequence_cache = main_core::update::get_all_sequence_idx_map( - model.state_, update_data, scenario_idx, do_update_cache_); + model.state_, update_data, scenario_idx, components_to_store, do_update_cache_, true); model.template update_components(update_data, scenario_idx, scenario_sequence()); },