From 8085e5b1d29f04af02cfc7636ecef20e04f6f6dd Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:10:05 +0100 Subject: [PATCH 01/40] start slice dataset Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/auxiliary/dataset.hpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp index 3108f6307c..deb13a5736 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp @@ -287,6 +287,9 @@ template class Dataset { } constexpr bool is_dense(Idx const i) const { return is_dense(buffers_[i]); } constexpr bool is_dense(Buffer const& buffer) const { return buffer.indptr.empty(); } + constexpr bool is_dense() const { + return std::ranges::all_of(buffers_, [this](Buffer const& buffer) { return is_dense(buffer); }); + } constexpr bool is_sparse(std::string_view component, bool with_attribute_buffers = false) const { Idx const idx = find_component(component, false); if (idx == invalid_index) { @@ -510,6 +513,17 @@ template class Dataset { return result; } + // get slice dataset from batch + Dataset get_slice_scenario(Idx begin, Idx end) const + requires(!is_indptr_mutable_v) + { + assert(0 <= begin && begin < batch_size()); + assert(0 <= end && end < batch_size()); + assert(begin <= end); + assert(is_batch()); + assert(is_dense()); + } + private: MetaData const* meta_data_; DatasetInfo dataset_info_; From 73d85c531d8b26c14d3add4474123b0835e02961 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 10:17:48 +0100 Subject: [PATCH 02/40] add slice scenario Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/auxiliary/dataset.hpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp index deb13a5736..07405ce5ce 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp @@ -517,11 +517,21 @@ template class Dataset { Dataset get_slice_scenario(Idx begin, Idx end) const requires(!is_indptr_mutable_v) { - assert(0 <= begin && begin < batch_size()); - assert(0 <= end && end < batch_size()); assert(begin <= end); + assert(0 <= begin); + assert(end < batch_size()); assert(is_batch()); assert(is_dense()); + + Dataset result = get_individual_scenario(begin); + Idx const batch_size = end - begin; + result.dataset_info_.is_batch = true; + result.dataset_info_.batch_size = batch_size; + for (auto&& [buffer, component_info] : std::views::zip(result.buffers_, result.dataset_info_.component_info)) { + Idx const size = component_info.elements_per_scenario * batch_size; + component_info.total_elements = size; + } + return result; } private: From be44d80fdf068c82770ecac4e3055e6c86cbc603 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:46:28 +0100 Subject: [PATCH 03/40] batch dimension Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_c/options.h | 9 +++++++++ power_grid_model_c/power_grid_model_c/src/options.cpp | 3 +++ power_grid_model_c/power_grid_model_c/src/options.hpp | 1 + 3 files changed, 13 insertions(+) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h index 3671727a00..6bb38cecfb 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h @@ -121,6 +121,15 @@ PGM_API void PGM_set_short_circuit_voltage_scaling(PGM_Handle* handle, PGM_Optio */ PGM_API void PGM_set_tap_changing_strategy(PGM_Handle* handle, PGM_Options* opt, PGM_Idx tap_changing_strategy); +/** + * @brief Specify the batch dimension for batch calculations + * + * @param handle + * @param opt pointer to option instance + * @param batch_dimension dimension of the batch calculation + */ +PGM_API void PGM_set_batch_dimension(PGM_Handle* handle, PGM_Options* opt, PGM_Idx batch_dimension); + /** * @brief Enable/disable experimental features. * diff --git a/power_grid_model_c/power_grid_model_c/src/options.cpp b/power_grid_model_c/power_grid_model_c/src/options.cpp index 5c22d7acaa..15b517f757 100644 --- a/power_grid_model_c/power_grid_model_c/src/options.cpp +++ b/power_grid_model_c/power_grid_model_c/src/options.cpp @@ -33,6 +33,9 @@ void PGM_set_short_circuit_voltage_scaling(PGM_Handle* /* handle */, PGM_Options void PGM_set_tap_changing_strategy(PGM_Handle* /* handle */, PGM_Options* opt, PGM_Idx tap_changing_strategy) { opt->tap_changing_strategy = tap_changing_strategy; } +void PGM_set_batch_dimension(PGM_Handle* /* handle */, PGM_Options* opt, PGM_Idx batch_dimension) { + opt->batch_dimension = batch_dimension; +} void PGM_set_experimental_features(PGM_Handle* /* handle */, PGM_Options* opt, PGM_Idx experimental_features) { opt->experimental_features = experimental_features; } diff --git a/power_grid_model_c/power_grid_model_c/src/options.hpp b/power_grid_model_c/power_grid_model_c/src/options.hpp index 37da22cb77..b72b4d2d5f 100644 --- a/power_grid_model_c/power_grid_model_c/src/options.hpp +++ b/power_grid_model_c/power_grid_model_c/src/options.hpp @@ -25,5 +25,6 @@ struct PGM_Options { Idx threading{-1}; Idx short_circuit_voltage_scaling{PGM_short_circuit_voltage_scaling_maximum}; Idx tap_changing_strategy{PGM_tap_changing_strategy_disabled}; + Idx batch_dimension{-1}; Idx experimental_features{PGM_experimental_features_disabled}; }; From 1fd7ed3e80442d7f5db05945cd4da61ecfbeb621 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:51:55 +0100 Subject: [PATCH 04/40] calculation implementation Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../power_grid_model_c/src/model.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/power_grid_model_c/power_grid_model_c/src/model.cpp b/power_grid_model_c/power_grid_model_c/src/model.cpp index 8310c62eae..a567591306 100644 --- a/power_grid_model_c/power_grid_model_c/src/model.cpp +++ b/power_grid_model_c/power_grid_model_c/src/model.cpp @@ -55,6 +55,7 @@ void PGM_get_indexer(PGM_Handle* handle, PGM_PowerGridModel const* model, char c PGM_regular_error); } +// helper functions namespace { void check_no_experimental_features_used(MainModel const& model, MainModel::Options const& opt) { // optionally add experimental feature checks here @@ -142,9 +143,10 @@ constexpr auto extract_calculation_options(PGM_Options const& opt) { } } // namespace -// run calculation -void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options const* opt, - PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { +// calculation implementation +namespace { +void calculate_impl(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options const* opt, + PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { PGM_clear_error(handle); // check dataset integrity if ((batch_dataset != nullptr) && (!batch_dataset->is_batch() || !output_dataset->is_batch())) { @@ -179,6 +181,13 @@ void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options co handle->err_msg = "Unknown error!\n"; } } +} // namespace + +// run calculation +void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options const* opt, + PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { + calculate_impl(handle, model, opt, output_dataset, batch_dataset); +} // destroy model void PGM_destroy_model(PGM_PowerGridModel* model) { delete model; } From 45f4d727d89633645b07a06d7ddc3800ec53c0b9 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 13:18:58 +0100 Subject: [PATCH 05/40] error handling still needs to be done Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../power_grid_model_c/src/model.cpp | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/power_grid_model_c/power_grid_model_c/src/model.cpp b/power_grid_model_c/power_grid_model_c/src/model.cpp index a567591306..b495938c0b 100644 --- a/power_grid_model_c/power_grid_model_c/src/model.cpp +++ b/power_grid_model_c/power_grid_model_c/src/model.cpp @@ -15,6 +15,9 @@ #include #include +#include +#include + namespace { using namespace power_grid_model; } // namespace @@ -146,7 +149,7 @@ constexpr auto extract_calculation_options(PGM_Options const& opt) { // calculation implementation namespace { void calculate_impl(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options const* opt, - PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { + PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { PGM_clear_error(handle); // check dataset integrity if ((batch_dataset != nullptr) && (!batch_dataset->is_batch() || !output_dataset->is_batch())) { @@ -186,7 +189,41 @@ void calculate_impl(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options c // run calculation void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options const* opt, PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { - calculate_impl(handle, model, opt, output_dataset, batch_dataset); + // for dimensions which are 1D batch or default (-1), call implementation directly + if (opt->batch_dimension < 2) { + return calculate_impl(handle, model, opt, output_dataset, batch_dataset); + } + + // get stride size of the rest of dimensions + Idx const first_batch_size = batch_dataset->batch_size(); + Idx const stride_size = + std::transform_reduce(batch_dataset + 1, batch_dataset + opt->batch_dimension, Idx{1}, std::multiplies{}, + [](PGM_ConstDataset const& ds) { return ds.batch_size(); }); + + // loop over the first dimension batche + for (Idx i = 0; i < first_batch_size; ++i) { + // a new handle + PGM_Handle local_handle{}; + // deep opt is one dimension less + PGM_Options deep_opt = *opt; + --deep_opt.batch_dimension; + // create sliced datasets for the rest of dimensions + PGM_ConstDataset const single_update_dataset = batch_dataset->get_individual_scenario(i); + PGM_MutableDataset const sliced_output_dataset = + output_dataset->get_slice_scenario(i * stride_size, (i + 1) * stride_size); + // create a model copy and apply update + std::unique_ptr const local_model{PGM_copy_model(&local_handle, model)}; + + PGM_update_model(&local_handle, local_model.get(), &single_update_dataset); + + // if the deep opt have less than 2 dimensions, call implementation directly + if (deep_opt.batch_dimension < 2) { + calculate_impl(&local_handle, local_model.get(), &deep_opt, &sliced_output_dataset, batch_dataset + 1); + } else { + // recursive call + PGM_calculate(&local_handle, local_model.get(), &deep_opt, &sliced_output_dataset, batch_dataset + 1); + } + } } // destroy model From 06cb330357e98df88ef72c2f7570cdf0c16eb323 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:27:25 +0100 Subject: [PATCH 06/40] error handling Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../power_grid_model_c/src/model.cpp | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model_c/src/model.cpp b/power_grid_model_c/power_grid_model_c/src/model.cpp index b495938c0b..bb41e0d44e 100644 --- a/power_grid_model_c/power_grid_model_c/src/model.cpp +++ b/power_grid_model_c/power_grid_model_c/src/model.cpp @@ -148,6 +148,7 @@ constexpr auto extract_calculation_options(PGM_Options const& opt) { // calculation implementation namespace { + void calculate_impl(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options const* opt, PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { PGM_clear_error(handle); @@ -184,6 +185,25 @@ void calculate_impl(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options c handle->err_msg = "Unknown error!\n"; } } + +void merge_batch_error_msgs(PGM_Handle* handle, PGM_Handle const& local_handle, Idx scenario_offset, Idx stride_size) { + if (local_handle.err_code == PGM_no_error) { + return; + } + handle->err_code = PGM_batch_error; + if (local_handle.err_code == PGM_batch_error) { + for (auto&& [idx, err_msg] : std::views::zip(local_handle.failed_scenarios, local_handle.batch_errs)) { + handle->failed_scenarios.push_back(idx + scenario_offset); + handle->batch_errs.push_back(err_msg); + } + } else { + for (Idx i = 0; i < stride_size; ++i) { + handle->failed_scenarios.push_back(scenario_offset + i); + handle->batch_errs.push_back(local_handle.err_msg); + } + } +} + } // namespace // run calculation @@ -211,10 +231,20 @@ void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options co PGM_ConstDataset const single_update_dataset = batch_dataset->get_individual_scenario(i); PGM_MutableDataset const sliced_output_dataset = output_dataset->get_slice_scenario(i * stride_size, (i + 1) * stride_size); - // create a model copy and apply update + + // create a model copy std::unique_ptr const local_model{PGM_copy_model(&local_handle, model)}; + if (local_handle.err_code != PGM_no_error) { + merge_batch_error_msgs(handle, local_handle, i * stride_size, stride_size); + continue; + } + // apply the update PGM_update_model(&local_handle, local_model.get(), &single_update_dataset); + if (local_handle.err_code != PGM_no_error) { + merge_batch_error_msgs(handle, local_handle, i * stride_size, stride_size); + continue; + } // if the deep opt have less than 2 dimensions, call implementation directly if (deep_opt.batch_dimension < 2) { @@ -223,6 +253,8 @@ void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options co // recursive call PGM_calculate(&local_handle, local_model.get(), &deep_opt, &sliced_output_dataset, batch_dataset + 1); } + // merge errors + merge_batch_error_msgs(handle, local_handle, i * stride_size, stride_size); } } From a4fc01e22e96748b70d5c57cc765ce58cf98d86b Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:29:55 +0100 Subject: [PATCH 07/40] add batch dimensions Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_cpp/options.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp index 5603650e5f..cce0c2d813 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp @@ -39,6 +39,10 @@ class Options { handle_.call_with(PGM_set_tap_changing_strategy, get(), tap_changing_strategy); } + void set_batch_dimensions(Idx batch_dimensions) { + handle_.call_with(PGM_set_batch_dimensions, get(), batch_dimensions); + } + void set_experimental_features(Idx experimental_features) { handle_.call_with(PGM_set_experimental_features, get(), experimental_features); } From 118655fb4aa8f447393ded3badd0804812029044 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 14:31:39 +0100 Subject: [PATCH 08/40] batch dimension Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_cpp/options.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp index cce0c2d813..3b29364546 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp @@ -39,8 +39,8 @@ class Options { handle_.call_with(PGM_set_tap_changing_strategy, get(), tap_changing_strategy); } - void set_batch_dimensions(Idx batch_dimensions) { - handle_.call_with(PGM_set_batch_dimensions, get(), batch_dimensions); + void set_batch_dimension(Idx batch_dimension) { + handle_.call_with(PGM_set_batch_dimension, get(), batch_dimension); } void set_experimental_features(Idx experimental_features) { From 3b41f2849d2d0f94927ee90133863a7bba9283d1 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:46:18 +0100 Subject: [PATCH 09/40] start test Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- tests/native_api_tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/native_api_tests/CMakeLists.txt b/tests/native_api_tests/CMakeLists.txt index f82dcd7439..dbe0ba1845 100644 --- a/tests/native_api_tests/CMakeLists.txt +++ b/tests/native_api_tests/CMakeLists.txt @@ -10,6 +10,7 @@ set(PROJECT_SOURCES "test_api_model_update.cpp" "test_api_serialization.cpp" "test_api_utils.cpp" + "test_api_model_multi_dimension.cpp" ) add_executable(power_grid_model_api_tests ${PROJECT_SOURCES}) From d88db976d243213836d1ddb8bb90a82c610ea8bb Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:46:29 +0100 Subject: [PATCH 10/40] start test Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../test_api_model_multi_dimension.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/native_api_tests/test_api_model_multi_dimension.cpp diff --git a/tests/native_api_tests/test_api_model_multi_dimension.cpp b/tests/native_api_tests/test_api_model_multi_dimension.cpp new file mode 100644 index 0000000000..eae260afd5 --- /dev/null +++ b/tests/native_api_tests/test_api_model_multi_dimension.cpp @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Contributors to the Power Grid Model project +// +// SPDX-License-Identifier: MPL-2.0 + +#include "power_grid_model_cpp.hpp" + +#include + +#include + +TEST_CASE("API Model Multi-Dimension") {} From 1b1190dfc2a6e349c9b0dfe9805f7d63c78f8663 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:59:11 +0100 Subject: [PATCH 11/40] api will not work as intended Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../test_api_model_multi_dimension.cpp | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/tests/native_api_tests/test_api_model_multi_dimension.cpp b/tests/native_api_tests/test_api_model_multi_dimension.cpp index eae260afd5..6f2b34bb6c 100644 --- a/tests/native_api_tests/test_api_model_multi_dimension.cpp +++ b/tests/native_api_tests/test_api_model_multi_dimension.cpp @@ -2,10 +2,104 @@ // // SPDX-License-Identifier: MPL-2.0 +#include "load_dataset.hpp" + #include "power_grid_model_cpp.hpp" #include #include -TEST_CASE("API Model Multi-Dimension") {} +#include +#include +#include +#include +#include + +namespace power_grid_model_cpp { +namespace { +using namespace std::string_literals; +using power_grid_model_cpp_test::load_dataset; +using std::numbers::sqrt3; + +// input +auto const complete_state_json = R"json({ + "version": "1.0", + "type": "input", + "is_batch": false, + "attributes": {}, + "data": { + "sym_load": [ + {"id": 2, "node": 0, "status": 1, "type": 0, "p_specified": 0, "q_specified": 0} + ], + "source": [ + {"id": 1, "node": 0, "status": 1, "u_ref": 1, "sk": 1e20} + ], + "node": [ + {"id": 0, "u_rated": 10e3} + ] + } +})json"s; + +} // namespace + +TEST_CASE("API Model Multi-Dimension") { + // model + auto const owning_input_dataset = load_dataset(complete_state_json); + auto const& input_dataset = owning_input_dataset.dataset; + Model model{50.0, input_dataset}; + + // 3-D batch update + double const u_rated = 10e3; + std::vector const u_ref{0.9, 1.0, 1.1}; + std::vector const p_specified{1e6, 2e6, 3e6, 4e6}; + std::vector const q_specified{0.1e6, 0.2e6, 0.3e6, 0.4e6, 0.5e6}; + Idx const size_u_ref = static_cast(u_ref.size()); + Idx const size_p_specified = static_cast(p_specified.size()); + Idx const size_q_specified = static_cast(q_specified.size()); + Idx const total_batch_size = size_u_ref * size_p_specified * size_q_specified; + + // calculate source current manually + std::vector i_source_ref(total_batch_size); + for (Idx i = 0; i < size_u_ref; ++i) { + for (Idx j = 0; j < size_p_specified; ++j) { + for (Idx k = 0; k < size_q_specified; ++k) { + Idx index = i * size_p_specified * size_q_specified + j * size_q_specified + k; + double const s = std::abs(std::complex{p_specified[j], q_specified[k]}); + i_source_ref[index] = s / (sqrt3 * u_rated * u_ref[i]); + } + } + } + + // construct batch update dataset + std::vector batch_datasets; + batch_datasets.emplace_back("update", true, size_u_ref); + batch_datasets.emplace_back("update", true, size_p_specified); + batch_datasets.emplace_back("update", true, size_q_specified); + + DatasetConst& batch_u_ref = batch_datasets[0]; + batch_u_ref.add_buffer("source", 1, size_u_ref, nullptr, nullptr); + batch_u_ref.add_attribute_buffer("source", "u_ref", u_ref.data()); + DatasetConst& batch_p_specified = batch_datasets[1]; + batch_p_specified.add_buffer("sym_load", 1, size_p_specified, nullptr, nullptr); + batch_p_specified.add_attribute_buffer("sym_load", "p_specified", p_specified.data()); + DatasetConst& batch_q_specified = batch_datasets[2]; + batch_q_specified.add_buffer("sym_load", 1, size_q_specified, nullptr, nullptr); + batch_q_specified.add_attribute_buffer("sym_load", "q_specified", q_specified.data()); + + + // output dataset + std::vector i_source_result(total_batch_size); + DatasetMutable batch_output_dataset{"sym_output", true, total_batch_size}; + batch_output_dataset.add_buffer("source", 1, total_batch_size, nullptr, nullptr); + batch_output_dataset.add_attribute_buffer("source", "i_source", i_source_result.data()); + + // options + Options options{}; + options.set_batch_dimension(3); + + // calculate + model.calculate(options, batch_output_dataset, batch_u_ref, batch_p_specified, batch_q_specified); +} + +} // namespace power_grid_model_cpp From 21c31c5592f4049b513518b4415eaf6193c4a8cb Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Mon, 10 Nov 2025 20:59:57 +0100 Subject: [PATCH 12/40] api will not work as intended Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- tests/native_api_tests/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/native_api_tests/CMakeLists.txt b/tests/native_api_tests/CMakeLists.txt index dbe0ba1845..a97cea3243 100644 --- a/tests/native_api_tests/CMakeLists.txt +++ b/tests/native_api_tests/CMakeLists.txt @@ -10,7 +10,7 @@ set(PROJECT_SOURCES "test_api_model_update.cpp" "test_api_serialization.cpp" "test_api_utils.cpp" - "test_api_model_multi_dimension.cpp" + # "test_api_model_multi_dimension.cpp" ) add_executable(power_grid_model_api_tests ${PROJECT_SOURCES}) From 6e8c081a6b8ea32f0f353411e1b399312af797c6 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 09:36:41 +0100 Subject: [PATCH 13/40] adjust md dataset Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_c/basics.h | 6 ++++ .../include/power_grid_model_c/dataset.h | 30 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h index 1986f35dd7..0c68ac8eaa 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h @@ -79,6 +79,12 @@ typedef struct PGM_Handle PGM_Handle; */ typedef struct PGM_Options PGM_Options; +/** + * @brief Opaque struct for the multi dimensional dataset class. + * + */ +typedef struct PGM_MultiDimensionalDataset PGM_MultiDimensionalDataset; + // Only enable the opaque struct definition if this header is consumed by the C-API user. // If this header is included when compiling the C-API, the structs below are decleared/defined in the C++ files. #ifndef PGM_DLL_EXPORTS diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h index 6edd6dd0d0..ac3ea0573d 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h @@ -309,6 +309,36 @@ PGM_API void PGM_dataset_mutable_add_attribute_buffer(PGM_Handle* handle, PGM_Mu */ PGM_API PGM_DatasetInfo const* PGM_dataset_mutable_get_info(PGM_Handle* handle, PGM_MutableDataset const* dataset); +/** + * @brief Create a PGM_MultiDimensionalDataset from multiple PGM_MutableDataset + * + * @param handle + * @param mutable_dataset + * @param n_datasets + * @return + */ +PGM_API PGM_MultiDimensionalDataset* +PGM_dataset_create_multidimensional_from_mutable(PGM_Handle* handle, PGM_MutableDataset const** mutable_dataset, + PGM_Idx n_datasets); + +/** + * @brief Get the array pointer from a PGM_MultiDimensionalDataset + * + * @param handle + * @param multidimensional_dataset + * @return + */ +PGM_API PGM_MutableDataset const* +PGM_get_array_pointer_from_multidimensional(PGM_Handle* handle, + PGM_MultiDimensionalDataset const* multidimensional_dataset); + +/** + * @brief destroy the multidimensional dataset object + * + * @param multidimensional_dataset + */ +PGM_API void PGM_destroy_multidimensional_dataset(PGM_MultiDimensionalDataset* multidimensional_dataset); + #ifdef __cplusplus } #endif From 0b989b40a13567148636265c5039bbc646ffca8a Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 09:46:05 +0100 Subject: [PATCH 14/40] add dataset Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_c/basics.h | 12 ++++----- .../include/power_grid_model_c/dataset.h | 8 +++--- .../power_grid_model_c/src/dataset.cpp | 25 +++++++++++++++++++ .../src/forward_declarations.hpp | 3 +++ 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h index 0c68ac8eaa..9153d62d88 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h @@ -79,12 +79,6 @@ typedef struct PGM_Handle PGM_Handle; */ typedef struct PGM_Options PGM_Options; -/** - * @brief Opaque struct for the multi dimensional dataset class. - * - */ -typedef struct PGM_MultiDimensionalDataset PGM_MultiDimensionalDataset; - // Only enable the opaque struct definition if this header is consumed by the C-API user. // If this header is included when compiling the C-API, the structs below are decleared/defined in the C++ files. #ifndef PGM_DLL_EXPORTS @@ -143,6 +137,12 @@ typedef struct PGM_WritableDataset PGM_WritableDataset; * @brief Opaque struct for the information of the dataset. */ typedef struct PGM_DatasetInfo PGM_DatasetInfo; + +/** + * @brief Opaque struct for the multi dimensional dataset class. + * + */ +typedef struct PGM_MultiDimensionalDataset PGM_MultiDimensionalDataset; #endif // NOLINTEND(modernize-use-using) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h index ac3ea0573d..afb2665dbe 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h @@ -310,15 +310,15 @@ PGM_API void PGM_dataset_mutable_add_attribute_buffer(PGM_Handle* handle, PGM_Mu PGM_API PGM_DatasetInfo const* PGM_dataset_mutable_get_info(PGM_Handle* handle, PGM_MutableDataset const* dataset); /** - * @brief Create a PGM_MultiDimensionalDataset from multiple PGM_MutableDataset + * @brief Create a PGM_MultiDimensionalDataset from multiple PGM_ConstDataset instances * * @param handle - * @param mutable_dataset + * @param const_datasets * @param n_datasets * @return */ PGM_API PGM_MultiDimensionalDataset* -PGM_dataset_create_multidimensional_from_mutable(PGM_Handle* handle, PGM_MutableDataset const** mutable_dataset, +PGM_dataset_create_multidimensional_from_const(PGM_Handle* handle, PGM_ConstDataset const** const_datasets, PGM_Idx n_datasets); /** @@ -328,7 +328,7 @@ PGM_dataset_create_multidimensional_from_mutable(PGM_Handle* handle, PGM_Mutable * @param multidimensional_dataset * @return */ -PGM_API PGM_MutableDataset const* +PGM_API PGM_ConstDataset const* PGM_get_array_pointer_from_multidimensional(PGM_Handle* handle, PGM_MultiDimensionalDataset const* multidimensional_dataset); diff --git a/power_grid_model_c/power_grid_model_c/src/dataset.cpp b/power_grid_model_c/power_grid_model_c/src/dataset.cpp index 0b9e3bee6e..4926fa169f 100644 --- a/power_grid_model_c/power_grid_model_c/src/dataset.cpp +++ b/power_grid_model_c/power_grid_model_c/src/dataset.cpp @@ -160,3 +160,28 @@ void PGM_dataset_mutable_add_attribute_buffer(PGM_Handle* handle, PGM_MutableDat PGM_DatasetInfo const* PGM_dataset_mutable_get_info(PGM_Handle* /*unused*/, PGM_MutableDataset const* dataset) { return &dataset->get_description(); } + +PGM_MultiDimensionalDataset* PGM_dataset_create_multidimensional_from_const(PGM_Handle* handle, + PGM_ConstDataset const** const_datasets, + PGM_Idx n_datasets) { + return call_with_catch( + handle, + [const_datasets, n_datasets]() { + auto multidimensional_dataset = new PGM_MultiDimensionalDataset(); + for (PGM_Idx i = 0; i < n_datasets; ++i) { + multidimensional_dataset->emplace_back(*const_datasets[i]); + } + return multidimensional_dataset; + }, + PGM_regular_error); +} + +PGM_ConstDataset const* +PGM_get_array_pointer_from_multidimensional(PGM_Handle* /*unused*/, + PGM_MultiDimensionalDataset const* multidimensional_dataset) { + return multidimensional_dataset->data(); +} + +void PGM_destroy_multidimensional_dataset(PGM_MultiDimensionalDataset* multidimensional_dataset) { + delete multidimensional_dataset; +} diff --git a/power_grid_model_c/power_grid_model_c/src/forward_declarations.hpp b/power_grid_model_c/power_grid_model_c/src/forward_declarations.hpp index f68d7def3f..12532cd5f7 100644 --- a/power_grid_model_c/power_grid_model_c/src/forward_declarations.hpp +++ b/power_grid_model_c/power_grid_model_c/src/forward_declarations.hpp @@ -10,6 +10,8 @@ #include +#include + // forward declare all referenced struct/class in C++ core // alias them in the root namespace @@ -36,3 +38,4 @@ using PGM_ConstDataset = power_grid_model::meta_data::Dataset; using PGM_WritableDataset = power_grid_model::meta_data::Dataset; using PGM_DatasetInfo = power_grid_model::meta_data::DatasetInfo; +using PGM_MultiDimensionalDataset = std::vector; From 14b40392855264815cf9583e1ebf2a141577767f Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 09:57:46 +0100 Subject: [PATCH 15/40] crash yet Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_cpp/model.hpp | 21 +++++++++++++++++++ tests/native_api_tests/CMakeLists.txt | 2 +- .../test_api_model_multi_dimension.cpp | 10 ++++++--- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp index 822e383ae0..6b33b8f181 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp @@ -13,6 +13,8 @@ #include "power_grid_model_c/model.h" +#include + namespace power_grid_model_cpp { class Model { public: @@ -54,6 +56,25 @@ class Model { handle_.call_with(PGM_calculate, get(), opt.get(), output_dataset.get(), nullptr); } + void calculate(Options const& opt, DatasetMutable const& output_dataset, + std::span batch_datasets) { + // create multidimensional dataset from the span of datasets + std::vector dataset_ptrs; + dataset_ptrs.reserve(batch_datasets.size()); + for (auto const& ds : batch_datasets) { + dataset_ptrs.push_back(ds.get()); + } + auto multidimensional_dataset = + detail::UniquePtr{ + handle_.call_with(PGM_dataset_create_multidimensional_from_const, dataset_ptrs.data(), + static_cast(dataset_ptrs.size()))}; + RawConstDataset const* batch_dataset_array_pointer = + PGM_get_array_pointer_from_multidimensional(nullptr, multidimensional_dataset.get()); + + // call calculate with the multidimensional dataset + handle_.call_with(PGM_calculate, get(), opt.get(), output_dataset.get(), batch_dataset_array_pointer); + } + private: Handle handle_{}; detail::UniquePtr model_; diff --git a/tests/native_api_tests/CMakeLists.txt b/tests/native_api_tests/CMakeLists.txt index a97cea3243..dbe0ba1845 100644 --- a/tests/native_api_tests/CMakeLists.txt +++ b/tests/native_api_tests/CMakeLists.txt @@ -10,7 +10,7 @@ set(PROJECT_SOURCES "test_api_model_update.cpp" "test_api_serialization.cpp" "test_api_utils.cpp" - # "test_api_model_multi_dimension.cpp" + "test_api_model_multi_dimension.cpp" ) add_executable(power_grid_model_api_tests ${PROJECT_SOURCES}) diff --git a/tests/native_api_tests/test_api_model_multi_dimension.cpp b/tests/native_api_tests/test_api_model_multi_dimension.cpp index 6f2b34bb6c..4e2cc5fb14 100644 --- a/tests/native_api_tests/test_api_model_multi_dimension.cpp +++ b/tests/native_api_tests/test_api_model_multi_dimension.cpp @@ -87,19 +87,23 @@ TEST_CASE("API Model Multi-Dimension") { batch_q_specified.add_buffer("sym_load", 1, size_q_specified, nullptr, nullptr); batch_q_specified.add_attribute_buffer("sym_load", "q_specified", q_specified.data()); - // output dataset std::vector i_source_result(total_batch_size); DatasetMutable batch_output_dataset{"sym_output", true, total_batch_size}; batch_output_dataset.add_buffer("source", 1, total_batch_size, nullptr, nullptr); - batch_output_dataset.add_attribute_buffer("source", "i_source", i_source_result.data()); + batch_output_dataset.add_attribute_buffer("source", "i", i_source_result.data()); // options Options options{}; options.set_batch_dimension(3); // calculate - model.calculate(options, batch_output_dataset, batch_u_ref, batch_p_specified, batch_q_specified); + model.calculate(options, batch_output_dataset, batch_datasets); + + // check results + for (Idx idx = 0; idx < total_batch_size; ++idx) { + CHECK(i_source_result[idx] == doctest::Approx(i_source_ref[idx])); + } } } // namespace power_grid_model_cpp From 33388814e02016a9cd5ea6c4157df3b6667e3a64 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:00:31 +0100 Subject: [PATCH 16/40] fix bounds checking Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/auxiliary/dataset.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp index 07405ce5ce..462b71ccad 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp @@ -488,7 +488,7 @@ template class Dataset { Dataset get_individual_scenario(Idx scenario) const requires(!is_indptr_mutable_v) { - assert(0 <= scenario && scenario < batch_size()); + assert(0 <= scenario && scenario <= batch_size()); Dataset result{*this}; result.dataset_info_.is_batch = false; @@ -519,7 +519,7 @@ template class Dataset { { assert(begin <= end); assert(0 <= begin); - assert(end < batch_size()); + assert(end <= batch_size()); assert(is_batch()); assert(is_dense()); From 01c86a91eeee9b1d2acc65b88f94377a7a07fe93 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 10:41:05 +0100 Subject: [PATCH 17/40] remove span Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_cpp/model.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp index 6b33b8f181..d2e3cd38cc 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp @@ -13,7 +13,7 @@ #include "power_grid_model_c/model.h" -#include +#include namespace power_grid_model_cpp { class Model { @@ -57,7 +57,7 @@ class Model { } void calculate(Options const& opt, DatasetMutable const& output_dataset, - std::span batch_datasets) { + std::vector const& batch_datasets) { // create multidimensional dataset from the span of datasets std::vector dataset_ptrs; dataset_ptrs.reserve(batch_datasets.size()); @@ -70,7 +70,7 @@ class Model { static_cast(dataset_ptrs.size()))}; RawConstDataset const* batch_dataset_array_pointer = PGM_get_array_pointer_from_multidimensional(nullptr, multidimensional_dataset.get()); - + // call calculate with the multidimensional dataset handle_.call_with(PGM_calculate, get(), opt.get(), output_dataset.get(), batch_dataset_array_pointer); } From 7786b0c7fc8f1ffbf60de8006c01b5ed6a183514 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:26:56 +0100 Subject: [PATCH 18/40] fix clang tidy Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- power_grid_model_c/power_grid_model_c/src/dataset.cpp | 2 +- power_grid_model_c/power_grid_model_c/src/model.cpp | 3 ++- tests/native_api_tests/test_api_model_multi_dimension.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/power_grid_model_c/power_grid_model_c/src/dataset.cpp b/power_grid_model_c/power_grid_model_c/src/dataset.cpp index 4926fa169f..32f785075d 100644 --- a/power_grid_model_c/power_grid_model_c/src/dataset.cpp +++ b/power_grid_model_c/power_grid_model_c/src/dataset.cpp @@ -167,7 +167,7 @@ PGM_MultiDimensionalDataset* PGM_dataset_create_multidimensional_from_const(PGM_ return call_with_catch( handle, [const_datasets, n_datasets]() { - auto multidimensional_dataset = new PGM_MultiDimensionalDataset(); + auto* multidimensional_dataset = new PGM_MultiDimensionalDataset(); for (PGM_Idx i = 0; i < n_datasets; ++i) { multidimensional_dataset->emplace_back(*const_datasets[i]); } diff --git a/power_grid_model_c/power_grid_model_c/src/model.cpp b/power_grid_model_c/power_grid_model_c/src/model.cpp index bb41e0d44e..24432dfe8d 100644 --- a/power_grid_model_c/power_grid_model_c/src/model.cpp +++ b/power_grid_model_c/power_grid_model_c/src/model.cpp @@ -211,7 +211,8 @@ void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options co PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { // for dimensions which are 1D batch or default (-1), call implementation directly if (opt->batch_dimension < 2) { - return calculate_impl(handle, model, opt, output_dataset, batch_dataset); + calculate_impl(handle, model, opt, output_dataset, batch_dataset); + return; } // get stride size of the rest of dimensions diff --git a/tests/native_api_tests/test_api_model_multi_dimension.cpp b/tests/native_api_tests/test_api_model_multi_dimension.cpp index 4e2cc5fb14..b9370426ae 100644 --- a/tests/native_api_tests/test_api_model_multi_dimension.cpp +++ b/tests/native_api_tests/test_api_model_multi_dimension.cpp @@ -64,7 +64,7 @@ TEST_CASE("API Model Multi-Dimension") { for (Idx i = 0; i < size_u_ref; ++i) { for (Idx j = 0; j < size_p_specified; ++j) { for (Idx k = 0; k < size_q_specified; ++k) { - Idx index = i * size_p_specified * size_q_specified + j * size_q_specified + k; + Idx const index = i * size_p_specified * size_q_specified + j * size_q_specified + k; double const s = std::abs(std::complex{p_specified[j], q_specified[k]}); i_source_ref[index] = s / (sqrt3 * u_rated * u_ref[i]); } From 5a3f394f0272fbb1f51666ff28a35443d4b2ac42 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 13:22:11 +0100 Subject: [PATCH 19/40] format| Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../power_grid_model_c/include/power_grid_model_c/dataset.h | 2 +- .../power_grid_model_c/include/power_grid_model_c/options.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h index afb2665dbe..6db842f495 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h @@ -319,7 +319,7 @@ PGM_API PGM_DatasetInfo const* PGM_dataset_mutable_get_info(PGM_Handle* handle, */ PGM_API PGM_MultiDimensionalDataset* PGM_dataset_create_multidimensional_from_const(PGM_Handle* handle, PGM_ConstDataset const** const_datasets, - PGM_Idx n_datasets); + PGM_Idx n_datasets); /** * @brief Get the array pointer from a PGM_MultiDimensionalDataset diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h index 6bb38cecfb..867a3c517c 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h @@ -123,8 +123,8 @@ PGM_API void PGM_set_tap_changing_strategy(PGM_Handle* handle, PGM_Options* opt, /** * @brief Specify the batch dimension for batch calculations - * - * @param handle + * + * @param handle * @param opt pointer to option instance * @param batch_dimension dimension of the batch calculation */ From 26509688adbba2f8c1016faec2b71da5a3ff692d Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:01:29 +0100 Subject: [PATCH 20/40] [skip ci] add cfunc in python Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- src/power_grid_model/_core/power_grid_core.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/power_grid_model/_core/power_grid_core.py b/src/power_grid_model/_core/power_grid_core.py index c276303273..529b6d3a4d 100644 --- a/src/power_grid_model/_core/power_grid_core.py +++ b/src/power_grid_model/_core/power_grid_core.py @@ -87,6 +87,12 @@ class WritableDatasetPtr(c_void_p): """ +class MultiDimensionDatasetPtr(c_void_p): + """ + Pointer to multi-dimensional dataset + """ + + class DatasetInfoPtr(c_void_p): """ Pointer to dataset info @@ -505,6 +511,28 @@ def dataset_writable_set_attribute_buffer( ) -> None: # type: ignore[empty-body] pass # pragma: no cover + @make_c_binding + def dataset_create_multidimensional_from_const( # type: ignore[empty-body] + self, + const_datasets: POINTER(ConstDatasetPtr), # type: ignore[valid-type] + n_datasets: int, + ) -> MultiDimensionDatasetPtr: + pass # pragma: no cover + + @make_c_binding + def get_array_pointer_from_multidimensional( # type: ignore[empty-body] + self, + multidimensional_dataset: MultiDimensionDatasetPtr, + ) -> ConstDatasetPtr: # type: ignore[valid-type] + pass # pragma: no cover + + @make_c_binding + def destroy_multidimensional_dataset( # type: ignore[empty-body] + self, + multidimensional_dataset: MultiDimensionDatasetPtr, + ) -> None: + pass # pragma: no cover + @make_c_binding def create_deserializer_from_binary_buffer( # type: ignore[empty-body] self, data: bytes, size: int, serialization_format: int From 9aaa6bd442e59ffe0da0107384fecb7e37bb6731 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:44:12 +0100 Subject: [PATCH 21/40] force nullptr Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- power_grid_model_c/power_grid_model_c/src/model.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/power_grid_model_c/power_grid_model_c/src/model.cpp b/power_grid_model_c/power_grid_model_c/src/model.cpp index 24432dfe8d..840e3cf64d 100644 --- a/power_grid_model_c/power_grid_model_c/src/model.cpp +++ b/power_grid_model_c/power_grid_model_c/src/model.cpp @@ -209,6 +209,11 @@ void merge_batch_error_msgs(PGM_Handle* handle, PGM_Handle const& local_handle, // run calculation void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options const* opt, PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { + // if dimension is zero, no batch calculation, force pointer to NULL + if (opt->batch_dimension == 0) { + batch_dataset = nullptr; + } + // for dimensions which are 1D batch or default (-1), call implementation directly if (opt->batch_dimension < 2) { calculate_impl(handle, model, opt, output_dataset, batch_dataset); From e4aa439936d3bf5d121f485883be7e656968bace Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 14:47:41 +0100 Subject: [PATCH 22/40] add options Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- src/power_grid_model/_core/options.py | 1 + src/power_grid_model/_core/power_grid_core.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/power_grid_model/_core/options.py b/src/power_grid_model/_core/options.py index f8f5a910ba..1bb9aa419e 100644 --- a/src/power_grid_model/_core/options.py +++ b/src/power_grid_model/_core/options.py @@ -45,6 +45,7 @@ class Options: tap_changing_strategy = OptionSetter(pgc.set_tap_changing_strategy) short_circuit_voltage_scaling = OptionSetter(pgc.set_short_circuit_voltage_scaling) experimental_features = OptionSetter(pgc.set_experimental_features) + batch_dimension = OptionSetter(pgc.set_batch_dimension) @property def opt(self) -> OptionsPtr: diff --git a/src/power_grid_model/_core/power_grid_core.py b/src/power_grid_model/_core/power_grid_core.py index 529b6d3a4d..3a03a34d49 100644 --- a/src/power_grid_model/_core/power_grid_core.py +++ b/src/power_grid_model/_core/power_grid_core.py @@ -344,6 +344,10 @@ def set_max_iter(self, opt: OptionsPtr, max_iter: int) -> None: # type: ignore[ def set_threading(self, opt: OptionsPtr, threading: int) -> None: # type: ignore[empty-body] pass # pragma: no cover + @make_c_binding + def set_batch_dimension(self, opt: OptionsPtr, batch_dimension: int) -> None: # type: ignore[empty-body] + pass # pragma: no cover + @make_c_binding def create_model( # type: ignore[empty-body] self, From 237681f15aa2d7b16163036a360e7d65fa38599f Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:16:35 +0100 Subject: [PATCH 23/40] proxy for multidimensional in python Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../_core/power_grid_dataset.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/power_grid_model/_core/power_grid_dataset.py b/src/power_grid_model/_core/power_grid_dataset.py index f6013f16f0..88bbf6c359 100644 --- a/src/power_grid_model/_core/power_grid_dataset.py +++ b/src/power_grid_model/_core/power_grid_dataset.py @@ -32,6 +32,7 @@ from power_grid_model._core.power_grid_core import ( ConstDatasetPtr, DatasetInfoPtr, + MultiDimensionDatasetPtr, MutableDatasetPtr, WritableDatasetPtr, power_grid_core as pgc, @@ -354,6 +355,57 @@ def __del__(self): pgc.destroy_dataset_const(self._const_dataset) +class CMultiDimensionalDataset: + """ + A proxy of a multi-dimensional dataset representing multiple const dataset for multidimensional batch scenarios. + """ + + _multi_dimensional_dataset: MultiDimensionDatasetPtr + _const_datasets: list[CConstDataset] + _total_batch_size: int + + def __new__(cls, datasets: list[CConstDataset]): + instance = super().__new__(cls) + instance._multi_dimensional_dataset = MultiDimensionDatasetPtr() + instance._const_datasets = datasets + + DatasetsPtrArray = ConstDatasetPtr * len(datasets) + datasets_ptr_array = DatasetsPtrArray() + total_batch_size = 1 + for idx, dataset in enumerate(datasets): + datasets_ptr_array[idx] = dataset.get_dataset_ptr() + total_batch_size *= dataset.get_info().batch_size() + instance._total_batch_size = total_batch_size + + instance._multi_dimensional_dataset = pgc.dataset_create_multidimensional_from_const( + datasets_ptr_array, len(datasets) + ) + assert_no_error() + + return instance + + def get_array_ptr(self) -> ConstDatasetPtr: + """ + Get the raw underlying multi-dimensional dataset pointer. + + Returns: + MultiDimensionDatasetPtr: the raw underlying multi-dimensional dataset pointer. + """ + return pgc.get_array_pointer_from_multidimensional(self._multi_dimensional_dataset) + + def get_total_batch_size(self) -> int: + """ + Get the total batch size of the multi-dimensional dataset. + + Returns: + int: the total batch size of the multi-dimensional dataset. + """ + return self._total_batch_size + + def __del__(self): + pgc.destroy_multidimensional_dataset(self._multi_dimensional_dataset) + + class CWritableDataset: """ A view of a Power Grid Model-owned dataset. From b602f2e71f09b36e763ebcefada7e92cafabc8c2 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:25:20 +0100 Subject: [PATCH 24/40] modify main calculate input Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../_core/power_grid_model.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/power_grid_model/_core/power_grid_model.py b/src/power_grid_model/_core/power_grid_model.py index 242f35f3f6..c4efc45913 100644 --- a/src/power_grid_model/_core/power_grid_model.py +++ b/src/power_grid_model/_core/power_grid_model.py @@ -49,6 +49,7 @@ from power_grid_model._core.index_integer import IdNp, IdxNp from power_grid_model._core.options import Options from power_grid_model._core.power_grid_core import ConstDatasetPtr, IDPtr, IdxPtr, ModelPtr, power_grid_core as pgc +from power_grid_model._core.power_grid_dataset import CMultiDimensionalDataset from power_grid_model._core.typing import ComponentAttributeMapping, ComponentAttributeMappingDict @@ -252,7 +253,7 @@ def _calculate_impl( # noqa: PLR0913 self, calculation_type: CalculationType, symmetric: bool, - update_data: Dataset | None, + update_data: Dataset | None | list[Dataset], output_component_types: ComponentAttributeMapping, options: Options, continue_on_batch_error: bool, @@ -274,15 +275,17 @@ def _calculate_impl( # noqa: PLR0913 Returns: """ self._batch_error = None - is_batch = update_data is not None - - if update_data is not None: - prepared_update = prepare_update_view(update_data) - update_ptr = prepared_update.get_dataset_ptr() - batch_size = prepared_update.get_info().batch_size() + if update_data is None: + is_batch = False + update_data = [] else: - update_ptr = ConstDatasetPtr() - batch_size = 1 + is_batch = True + if not isinstance(update_data, list): + update_data = [update_data] + + prepared_update = CMultiDimensionalDataset([prepare_update_view(x) for x in update_data]) + update_ptr: ConstDatasetPtr = prepared_update.get_array_ptr() + batch_size = prepared_update.get_total_batch_size() output_data = self._construct_output( output_component_types=output_component_types, From 3e79237ee012a2523d011c4af1bf2ae848fd3898 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:30:37 +0100 Subject: [PATCH 25/40] type annotation Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../_core/power_grid_model.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/power_grid_model/_core/power_grid_model.py b/src/power_grid_model/_core/power_grid_model.py index c4efc45913..ddb782ee0e 100644 --- a/src/power_grid_model/_core/power_grid_model.py +++ b/src/power_grid_model/_core/power_grid_model.py @@ -253,7 +253,7 @@ def _calculate_impl( # noqa: PLR0913 self, calculation_type: CalculationType, symmetric: bool, - update_data: Dataset | None | list[Dataset], + update_data: Dataset | list[Dataset] | None, output_component_types: ComponentAttributeMapping, options: Options, continue_on_batch_error: bool, @@ -323,7 +323,7 @@ def _calculate_power_flow( # noqa: PLR0913 error_tolerance: float = 1e-8, max_iterations: int = 20, calculation_method: CalculationMethod | str = CalculationMethod.newton_raphson, - update_data: Dataset | None = None, + update_data: Dataset | list[Dataset] | None = None, threading: int = -1, output_component_types: ComponentAttributeMapping = None, continue_on_batch_error: bool = False, @@ -360,7 +360,7 @@ def _calculate_state_estimation( # noqa: PLR0913 error_tolerance: float = 1e-8, max_iterations: int = 20, calculation_method: CalculationMethod | str = CalculationMethod.iterative_linear, - update_data: Dataset | None = None, + update_data: Dataset | list[Dataset] | None = None, threading: int = -1, output_component_types: ComponentAttributeMapping = None, continue_on_batch_error: bool = False, @@ -392,7 +392,7 @@ def _calculate_short_circuit( # noqa: PLR0913 self, *, calculation_method: CalculationMethod | str = CalculationMethod.iec60909, - update_data: Dataset | None = None, + update_data: Dataset | list[Dataset] | None = None, threading: int = -1, output_component_types: ComponentAttributeMapping = None, continue_on_batch_error: bool = False, @@ -489,7 +489,7 @@ def calculate_power_flow( error_tolerance: float = ..., max_iterations: int = ..., calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ..., continue_on_batch_error: bool = ..., @@ -504,7 +504,7 @@ def calculate_power_flow( error_tolerance: float = ..., max_iterations: int = ..., calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: ComponentAttributeFilterOptions = ..., continue_on_batch_error: bool = ..., @@ -519,7 +519,7 @@ def calculate_power_flow( error_tolerance: float = ..., max_iterations: int = ..., calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: ComponentAttributeMappingDict = ..., continue_on_batch_error: bool = ..., @@ -533,7 +533,7 @@ def calculate_power_flow( # noqa: PLR0913 error_tolerance: float = 1e-8, max_iterations: int = 20, calculation_method: CalculationMethod | str = CalculationMethod.newton_raphson, - update_data: BatchDataset | None = None, + update_data: BatchDataset | list[BatchDataset] | None = None, threading: int = -1, output_component_types: ComponentAttributeMapping = None, continue_on_batch_error: bool = False, @@ -675,7 +675,7 @@ def calculate_state_estimation( error_tolerance: float = ..., max_iterations: int = ..., calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ..., continue_on_batch_error: bool = ..., @@ -689,7 +689,7 @@ def calculate_state_estimation( error_tolerance: float = ..., max_iterations: int = ..., calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: ComponentAttributeFilterOptions = ..., continue_on_batch_error: bool = ..., @@ -703,7 +703,7 @@ def calculate_state_estimation( error_tolerance: float = ..., max_iterations: int = ..., calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: ComponentAttributeMappingDict = ..., continue_on_batch_error: bool = ..., @@ -716,7 +716,7 @@ def calculate_state_estimation( # noqa: PLR0913 error_tolerance: float = 1e-8, max_iterations: int = 20, calculation_method: CalculationMethod | str = CalculationMethod.iterative_linear, - update_data: BatchDataset | None = None, + update_data: BatchDataset | list[BatchDataset] | None = None, threading: int = -1, output_component_types: ComponentAttributeMapping = None, continue_on_batch_error: bool = False, @@ -844,7 +844,7 @@ def calculate_short_circuit( self, *, calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: None | set[ComponentTypeVar] | list[ComponentTypeVar] = ..., continue_on_batch_error: bool = ..., @@ -856,7 +856,7 @@ def calculate_short_circuit( self, *, calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: ComponentAttributeFilterOptions = ..., continue_on_batch_error: bool = ..., @@ -868,7 +868,7 @@ def calculate_short_circuit( self, *, calculation_method: CalculationMethod | str = ..., - update_data: BatchDataset = ..., + update_data: BatchDataset | list[BatchDataset] = ..., threading: int = ..., output_component_types: ComponentAttributeMappingDict = ..., continue_on_batch_error: bool = ..., @@ -879,7 +879,7 @@ def calculate_short_circuit( # noqa: PLR0913 self, *, calculation_method: CalculationMethod | str = CalculationMethod.iec60909, - update_data: BatchDataset | None = None, + update_data: BatchDataset | list[BatchDataset] | None = None, threading: int = -1, output_component_types: ComponentAttributeMapping = None, continue_on_batch_error: bool = False, From 8d8d80c271ee664b784acb264cf0c9b534db2432 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:53:53 +0100 Subject: [PATCH 26/40] [skip ci] not working yet Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../_core/dataset_definitions.py | 5 +- tests/unit/test_multi_dimensional_batch.py | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 tests/unit/test_multi_dimensional_batch.py diff --git a/src/power_grid_model/_core/dataset_definitions.py b/src/power_grid_model/_core/dataset_definitions.py index 38fc93f909..39ad779887 100644 --- a/src/power_grid_model/_core/dataset_definitions.py +++ b/src/power_grid_model/_core/dataset_definitions.py @@ -102,8 +102,11 @@ def _str_to_component_type(component: ComponentTypeLike) -> ComponentType: return ComponentType[component] -def _map_to_component_types(data: Mapping[ComponentTypeVar, Any]) -> dict[ComponentType, Any]: +def _map_to_component_types(data: Mapping[ComponentTypeVar, Any] | list[Mapping[ComponentTypeVar, Any]] + ) -> dict[ComponentType, Any] | list[dict[ComponentType, Any]]: """Helper function to map componenttype str keys to ComponentType.""" + if isinstance(data, list): + return [_map_to_component_types(item) for item in data] return {_str_to_component_type(key): value for key, value in data.items()} # fmt: on diff --git a/tests/unit/test_multi_dimensional_batch.py b/tests/unit/test_multi_dimensional_batch.py new file mode 100644 index 0000000000..2ca9e13faa --- /dev/null +++ b/tests/unit/test_multi_dimensional_batch.py @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: Contributors to the Power Grid Model project +# +# SPDX-License-Identifier: MPL-2.0 + +import json + +import numpy as np + +from power_grid_model import PowerGridModel +from power_grid_model.utils import json_deserialize + +input_data = { + "version": "1.0", + "type": "input", + "is_batch": False, + "attributes": {}, + "data": { + "sym_load": [{"id": 2, "node": 0, "status": 1, "type": 0, "p_specified": 0, "q_specified": 0}], + "source": [{"id": 1, "node": 0, "status": 1, "u_ref": 1, "sk": 1e20}], + "node": [{"id": 0, "u_rated": 10e3}], + }, +} + +input_data_json = json.dumps(input_data) + + +def test_multi_dimensional_batch(): + input_dataset = json_deserialize(input_data_json) + pgm = PowerGridModel(input_dataset) + + u_rated = 10e3 + u_ref = np.array([0.9, 1.0, 1.1], dtype=np.float64).reshape(-1, 1) + p_specified = np.array([1e6, 2e6, 3e6, 4e6], dtype=np.float64).reshape(-1, 1) + q_specified = np.array([0.1e6, 0.2e6, 0.3e6, 0.4e6, 0.5e6], dtype=np.float64).reshape(-1, 1) + i_source_ref = np.abs(p_specified.reshape(1, -1, 1) + 1j * q_specified.reshape(1, 1, -1)) / ( + u_ref.reshape(-1, 1, 1) * u_rated * np.sqrt(3) + ) + i_source_ref = i_source_ref.ravel() + + u_ref_batch = {"source": {"u_ref": u_ref}} + p_specified_batch = {"sym_load": {"p_specified": p_specified}} + q_specified_batch = {"sym_load": {"q_specified": q_specified}} + + result = pgm.calculate_power_flow( + update_data=[u_ref_batch, p_specified_batch, q_specified_batch], output_component_types={"source": ["i"]} + ) + + assert np.allclose(result["source"]["i"].ravel(), i_source_ref) From 61cfd57616e90311dd94a163ee203759c0405aba Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 11 Nov 2025 20:56:49 +0100 Subject: [PATCH 27/40] fix dimensions Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- src/power_grid_model/_core/power_grid_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/power_grid_model/_core/power_grid_model.py b/src/power_grid_model/_core/power_grid_model.py index ddb782ee0e..8942f8cabc 100644 --- a/src/power_grid_model/_core/power_grid_model.py +++ b/src/power_grid_model/_core/power_grid_model.py @@ -282,6 +282,7 @@ def _calculate_impl( # noqa: PLR0913 is_batch = True if not isinstance(update_data, list): update_data = [update_data] + options.batch_dimension = len(update_data) prepared_update = CMultiDimensionalDataset([prepare_update_view(x) for x in update_data]) update_ptr: ConstDatasetPtr = prepared_update.get_array_ptr() From a1331ba527dbd65747ddced15929cdff787541d5 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:09:16 +0100 Subject: [PATCH 28/40] fix mypy Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- src/power_grid_model/_core/dataset_definitions.py | 5 +---- src/power_grid_model/_core/power_grid_model.py | 7 ++++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/power_grid_model/_core/dataset_definitions.py b/src/power_grid_model/_core/dataset_definitions.py index 39ad779887..38fc93f909 100644 --- a/src/power_grid_model/_core/dataset_definitions.py +++ b/src/power_grid_model/_core/dataset_definitions.py @@ -102,11 +102,8 @@ def _str_to_component_type(component: ComponentTypeLike) -> ComponentType: return ComponentType[component] -def _map_to_component_types(data: Mapping[ComponentTypeVar, Any] | list[Mapping[ComponentTypeVar, Any]] - ) -> dict[ComponentType, Any] | list[dict[ComponentType, Any]]: +def _map_to_component_types(data: Mapping[ComponentTypeVar, Any]) -> dict[ComponentType, Any]: """Helper function to map componenttype str keys to ComponentType.""" - if isinstance(data, list): - return [_map_to_component_types(item) for item in data] return {_str_to_component_type(key): value for key, value in data.items()} # fmt: on diff --git a/src/power_grid_model/_core/power_grid_model.py b/src/power_grid_model/_core/power_grid_model.py index 8942f8cabc..43db99cb6e 100644 --- a/src/power_grid_model/_core/power_grid_model.py +++ b/src/power_grid_model/_core/power_grid_model.py @@ -283,6 +283,7 @@ def _calculate_impl( # noqa: PLR0913 if not isinstance(update_data, list): update_data = [update_data] options.batch_dimension = len(update_data) + update_data = [_map_to_component_types(x) for x in update_data] prepared_update = CMultiDimensionalDataset([prepare_update_view(x) for x in update_data]) update_ptr: ConstDatasetPtr = prepared_update.get_array_ptr() @@ -618,7 +619,7 @@ def calculate_power_flow( # noqa: PLR0913 error_tolerance=error_tolerance, max_iterations=max_iterations, calculation_method=calculation_method, - update_data=(_map_to_component_types(update_data) if update_data is not None else None), + update_data=update_data, threading=threading, output_component_types=output_component_types, continue_on_batch_error=continue_on_batch_error, @@ -797,7 +798,7 @@ def calculate_state_estimation( # noqa: PLR0913 error_tolerance=error_tolerance, max_iterations=max_iterations, calculation_method=calculation_method, - update_data=(_map_to_component_types(update_data) if update_data is not None else None), + update_data=update_data, threading=threading, output_component_types=output_component_types, continue_on_batch_error=continue_on_batch_error, @@ -952,7 +953,7 @@ def calculate_short_circuit( # noqa: PLR0913 """ return self._calculate_short_circuit( calculation_method=calculation_method, - update_data=(_map_to_component_types(update_data) if update_data is not None else None), + update_data=update_data, threading=threading, output_component_types=output_component_types, continue_on_batch_error=continue_on_batch_error, From 9c27e6ea780ea9e9c75c2c72c9d099f87fd42d39 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:02:19 +0100 Subject: [PATCH 29/40] empty slice Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/auxiliary/dataset.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp index 462b71ccad..5f5ea8ad38 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp @@ -488,7 +488,7 @@ template class Dataset { Dataset get_individual_scenario(Idx scenario) const requires(!is_indptr_mutable_v) { - assert(0 <= scenario && scenario <= batch_size()); + assert(0 <= scenario && scenario < batch_size()); Dataset result{*this}; result.dataset_info_.is_batch = false; @@ -523,6 +523,14 @@ template class Dataset { assert(is_batch()); assert(is_dense()); + // empty slice + if (begin == end) { + Dataset result{true, 0, dataset_info_.dataset->name, *meta_data_}; + result.add_buffer("node", 0, 0, nullptr, nullptr); + return result; + } + + // start with begin Dataset result = get_individual_scenario(begin); Idx const batch_size = end - begin; result.dataset_info_.is_batch = true; From 7faa6c1ee7a5062306d1ab14bc66188b6d472294 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:20:25 +0100 Subject: [PATCH 30/40] begin next Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model/auxiliary/dataset.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp index 5f5ea8ad38..ed75915161 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/auxiliary/dataset.hpp @@ -542,10 +542,14 @@ template class Dataset { return result; } + void set_next(Dataset const* next) { next_ = next; } + Dataset const* get_next() const { return next_; } + private: MetaData const* meta_data_; DatasetInfo dataset_info_; std::vector buffers_; + Dataset const* next_{}; std::span get_indptr_span(Indptr* indptr) const { return std::span{indptr, static_cast(batch_size() + 1)}; From c4b854e370e99671f0eb4dcce4080a269b181e17 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:22:30 +0100 Subject: [PATCH 31/40] revert api change Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_c/basics.h | 6 ---- .../include/power_grid_model_c/dataset.h | 30 ------------------- .../include/power_grid_model_c/options.h | 9 ------ .../power_grid_model_c/src/dataset.cpp | 25 ---------------- .../src/forward_declarations.hpp | 3 -- .../power_grid_model_c/src/options.cpp | 3 -- .../power_grid_model_c/src/options.hpp | 1 - 7 files changed, 77 deletions(-) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h index 9153d62d88..1986f35dd7 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/basics.h @@ -137,12 +137,6 @@ typedef struct PGM_WritableDataset PGM_WritableDataset; * @brief Opaque struct for the information of the dataset. */ typedef struct PGM_DatasetInfo PGM_DatasetInfo; - -/** - * @brief Opaque struct for the multi dimensional dataset class. - * - */ -typedef struct PGM_MultiDimensionalDataset PGM_MultiDimensionalDataset; #endif // NOLINTEND(modernize-use-using) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h index 6db842f495..6edd6dd0d0 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h @@ -309,36 +309,6 @@ PGM_API void PGM_dataset_mutable_add_attribute_buffer(PGM_Handle* handle, PGM_Mu */ PGM_API PGM_DatasetInfo const* PGM_dataset_mutable_get_info(PGM_Handle* handle, PGM_MutableDataset const* dataset); -/** - * @brief Create a PGM_MultiDimensionalDataset from multiple PGM_ConstDataset instances - * - * @param handle - * @param const_datasets - * @param n_datasets - * @return - */ -PGM_API PGM_MultiDimensionalDataset* -PGM_dataset_create_multidimensional_from_const(PGM_Handle* handle, PGM_ConstDataset const** const_datasets, - PGM_Idx n_datasets); - -/** - * @brief Get the array pointer from a PGM_MultiDimensionalDataset - * - * @param handle - * @param multidimensional_dataset - * @return - */ -PGM_API PGM_ConstDataset const* -PGM_get_array_pointer_from_multidimensional(PGM_Handle* handle, - PGM_MultiDimensionalDataset const* multidimensional_dataset); - -/** - * @brief destroy the multidimensional dataset object - * - * @param multidimensional_dataset - */ -PGM_API void PGM_destroy_multidimensional_dataset(PGM_MultiDimensionalDataset* multidimensional_dataset); - #ifdef __cplusplus } #endif diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h index 867a3c517c..3671727a00 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/options.h @@ -121,15 +121,6 @@ PGM_API void PGM_set_short_circuit_voltage_scaling(PGM_Handle* handle, PGM_Optio */ PGM_API void PGM_set_tap_changing_strategy(PGM_Handle* handle, PGM_Options* opt, PGM_Idx tap_changing_strategy); -/** - * @brief Specify the batch dimension for batch calculations - * - * @param handle - * @param opt pointer to option instance - * @param batch_dimension dimension of the batch calculation - */ -PGM_API void PGM_set_batch_dimension(PGM_Handle* handle, PGM_Options* opt, PGM_Idx batch_dimension); - /** * @brief Enable/disable experimental features. * diff --git a/power_grid_model_c/power_grid_model_c/src/dataset.cpp b/power_grid_model_c/power_grid_model_c/src/dataset.cpp index 32f785075d..0b9e3bee6e 100644 --- a/power_grid_model_c/power_grid_model_c/src/dataset.cpp +++ b/power_grid_model_c/power_grid_model_c/src/dataset.cpp @@ -160,28 +160,3 @@ void PGM_dataset_mutable_add_attribute_buffer(PGM_Handle* handle, PGM_MutableDat PGM_DatasetInfo const* PGM_dataset_mutable_get_info(PGM_Handle* /*unused*/, PGM_MutableDataset const* dataset) { return &dataset->get_description(); } - -PGM_MultiDimensionalDataset* PGM_dataset_create_multidimensional_from_const(PGM_Handle* handle, - PGM_ConstDataset const** const_datasets, - PGM_Idx n_datasets) { - return call_with_catch( - handle, - [const_datasets, n_datasets]() { - auto* multidimensional_dataset = new PGM_MultiDimensionalDataset(); - for (PGM_Idx i = 0; i < n_datasets; ++i) { - multidimensional_dataset->emplace_back(*const_datasets[i]); - } - return multidimensional_dataset; - }, - PGM_regular_error); -} - -PGM_ConstDataset const* -PGM_get_array_pointer_from_multidimensional(PGM_Handle* /*unused*/, - PGM_MultiDimensionalDataset const* multidimensional_dataset) { - return multidimensional_dataset->data(); -} - -void PGM_destroy_multidimensional_dataset(PGM_MultiDimensionalDataset* multidimensional_dataset) { - delete multidimensional_dataset; -} diff --git a/power_grid_model_c/power_grid_model_c/src/forward_declarations.hpp b/power_grid_model_c/power_grid_model_c/src/forward_declarations.hpp index 12532cd5f7..f68d7def3f 100644 --- a/power_grid_model_c/power_grid_model_c/src/forward_declarations.hpp +++ b/power_grid_model_c/power_grid_model_c/src/forward_declarations.hpp @@ -10,8 +10,6 @@ #include -#include - // forward declare all referenced struct/class in C++ core // alias them in the root namespace @@ -38,4 +36,3 @@ using PGM_ConstDataset = power_grid_model::meta_data::Dataset; using PGM_WritableDataset = power_grid_model::meta_data::Dataset; using PGM_DatasetInfo = power_grid_model::meta_data::DatasetInfo; -using PGM_MultiDimensionalDataset = std::vector; diff --git a/power_grid_model_c/power_grid_model_c/src/options.cpp b/power_grid_model_c/power_grid_model_c/src/options.cpp index 15b517f757..5c22d7acaa 100644 --- a/power_grid_model_c/power_grid_model_c/src/options.cpp +++ b/power_grid_model_c/power_grid_model_c/src/options.cpp @@ -33,9 +33,6 @@ void PGM_set_short_circuit_voltage_scaling(PGM_Handle* /* handle */, PGM_Options void PGM_set_tap_changing_strategy(PGM_Handle* /* handle */, PGM_Options* opt, PGM_Idx tap_changing_strategy) { opt->tap_changing_strategy = tap_changing_strategy; } -void PGM_set_batch_dimension(PGM_Handle* /* handle */, PGM_Options* opt, PGM_Idx batch_dimension) { - opt->batch_dimension = batch_dimension; -} void PGM_set_experimental_features(PGM_Handle* /* handle */, PGM_Options* opt, PGM_Idx experimental_features) { opt->experimental_features = experimental_features; } diff --git a/power_grid_model_c/power_grid_model_c/src/options.hpp b/power_grid_model_c/power_grid_model_c/src/options.hpp index b72b4d2d5f..37da22cb77 100644 --- a/power_grid_model_c/power_grid_model_c/src/options.hpp +++ b/power_grid_model_c/power_grid_model_c/src/options.hpp @@ -25,6 +25,5 @@ struct PGM_Options { Idx threading{-1}; Idx short_circuit_voltage_scaling{PGM_short_circuit_voltage_scaling_maximum}; Idx tap_changing_strategy{PGM_tap_changing_strategy_disabled}; - Idx batch_dimension{-1}; Idx experimental_features{PGM_experimental_features_disabled}; }; From cb67ae347d4383c56c75c14be063d985911dfa1d Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:35:56 +0100 Subject: [PATCH 32/40] model calculate for chaining Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../power_grid_model_c/src/model.cpp | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/power_grid_model_c/power_grid_model_c/src/model.cpp b/power_grid_model_c/power_grid_model_c/src/model.cpp index 840e3cf64d..fb538b2297 100644 --- a/power_grid_model_c/power_grid_model_c/src/model.cpp +++ b/power_grid_model_c/power_grid_model_c/src/model.cpp @@ -204,35 +204,44 @@ void merge_batch_error_msgs(PGM_Handle* handle, PGM_Handle const& local_handle, } } +Idx get_batch_dimension(PGM_ConstDataset const* batch_dataset) { + Idx dimension = 0; + while (batch_dataset != nullptr) { + ++dimension; + batch_dataset = batch_dataset->get_next(); + } + return dimension; +} + } // namespace // run calculation void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options const* opt, PGM_MutableDataset const* output_dataset, PGM_ConstDataset const* batch_dataset) { - // if dimension is zero, no batch calculation, force pointer to NULL - if (opt->batch_dimension == 0) { - batch_dataset = nullptr; - } + Idx const batch_dimension = get_batch_dimension(batch_dataset); - // for dimensions which are 1D batch or default (-1), call implementation directly - if (opt->batch_dimension < 2) { + // for dimension < 2 (one-time or 1D batch), call implementation directly + if (batch_dimension < 2) { calculate_impl(handle, model, opt, output_dataset, batch_dataset); return; } // get stride size of the rest of dimensions Idx const first_batch_size = batch_dataset->batch_size(); - Idx const stride_size = - std::transform_reduce(batch_dataset + 1, batch_dataset + opt->batch_dimension, Idx{1}, std::multiplies{}, - [](PGM_ConstDataset const& ds) { return ds.batch_size(); }); + Idx const stride_size = [batch_dataset]() { + Idx size = 1; + PGM_ConstDataset const* current = batch_dataset->get_next(); + while (current != nullptr) { + size *= current->batch_size(); + current = current->get_next(); + } + return size; + }(); // loop over the first dimension batche for (Idx i = 0; i < first_batch_size; ++i) { // a new handle PGM_Handle local_handle{}; - // deep opt is one dimension less - PGM_Options deep_opt = *opt; - --deep_opt.batch_dimension; // create sliced datasets for the rest of dimensions PGM_ConstDataset const single_update_dataset = batch_dataset->get_individual_scenario(i); PGM_MutableDataset const sliced_output_dataset = @@ -252,13 +261,8 @@ void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options co continue; } - // if the deep opt have less than 2 dimensions, call implementation directly - if (deep_opt.batch_dimension < 2) { - calculate_impl(&local_handle, local_model.get(), &deep_opt, &sliced_output_dataset, batch_dataset + 1); - } else { - // recursive call - PGM_calculate(&local_handle, local_model.get(), &deep_opt, &sliced_output_dataset, batch_dataset + 1); - } + // recursive call + PGM_calculate(&local_handle, local_model.get(), opt, &sliced_output_dataset, batch_dataset->get_next()); // merge errors merge_batch_error_msgs(handle, local_handle, i * stride_size, stride_size); } From a7352f2e2bc35db6f25ac0f56a24c69fcb5f7600 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:38:13 +0100 Subject: [PATCH 33/40] add dataset chaining Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_c/dataset.h | 10 ++++++++++ power_grid_model_c/power_grid_model_c/src/dataset.cpp | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h index 6edd6dd0d0..8da08e4f26 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h @@ -212,6 +212,16 @@ PGM_API void PGM_dataset_const_add_attribute_buffer(PGM_Handle* handle, PGM_Cons */ PGM_API PGM_DatasetInfo const* PGM_dataset_const_get_info(PGM_Handle* handle, PGM_ConstDataset const* dataset); +/** + * @brief Set next const dataset for chaining for MD batch + * + * @param handle + * @param dataset + * @param next_dataset The next dataset in the chain. + */ +PGM_API void PGM_dataset_const_set_next(PGM_Handle* handle, PGM_ConstDataset* dataset, + PGM_ConstDataset const* next_dataset); + /** * @brief Get the dataset info of the instance PGM_WritableDataset. * @param handle diff --git a/power_grid_model_c/power_grid_model_c/src/dataset.cpp b/power_grid_model_c/power_grid_model_c/src/dataset.cpp index 0b9e3bee6e..af70d5e812 100644 --- a/power_grid_model_c/power_grid_model_c/src/dataset.cpp +++ b/power_grid_model_c/power_grid_model_c/src/dataset.cpp @@ -101,6 +101,11 @@ void PGM_dataset_const_add_attribute_buffer(PGM_Handle* handle, PGM_ConstDataset PGM_regular_error); } +void PGM_dataset_const_set_next(PGM_Handle* /*unused*/, PGM_ConstDataset* dataset, + PGM_ConstDataset const* next_dataset) { + dataset->set_next(next_dataset); +} + PGM_DatasetInfo const* PGM_dataset_const_get_info(PGM_Handle* /*unused*/, PGM_ConstDataset const* dataset) { return &dataset->get_description(); } From b3c8901f23dd13135359eb43ca79308a2697ef0c Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:39:42 +0100 Subject: [PATCH 34/40] revert cpp interface Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_cpp/model.hpp | 21 ------------------- .../include/power_grid_model_cpp/options.hpp | 4 ---- 2 files changed, 25 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp index d2e3cd38cc..822e383ae0 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/model.hpp @@ -13,8 +13,6 @@ #include "power_grid_model_c/model.h" -#include - namespace power_grid_model_cpp { class Model { public: @@ -56,25 +54,6 @@ class Model { handle_.call_with(PGM_calculate, get(), opt.get(), output_dataset.get(), nullptr); } - void calculate(Options const& opt, DatasetMutable const& output_dataset, - std::vector const& batch_datasets) { - // create multidimensional dataset from the span of datasets - std::vector dataset_ptrs; - dataset_ptrs.reserve(batch_datasets.size()); - for (auto const& ds : batch_datasets) { - dataset_ptrs.push_back(ds.get()); - } - auto multidimensional_dataset = - detail::UniquePtr{ - handle_.call_with(PGM_dataset_create_multidimensional_from_const, dataset_ptrs.data(), - static_cast(dataset_ptrs.size()))}; - RawConstDataset const* batch_dataset_array_pointer = - PGM_get_array_pointer_from_multidimensional(nullptr, multidimensional_dataset.get()); - - // call calculate with the multidimensional dataset - handle_.call_with(PGM_calculate, get(), opt.get(), output_dataset.get(), batch_dataset_array_pointer); - } - private: Handle handle_{}; detail::UniquePtr model_; diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp index 3b29364546..5603650e5f 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/options.hpp @@ -39,10 +39,6 @@ class Options { handle_.call_with(PGM_set_tap_changing_strategy, get(), tap_changing_strategy); } - void set_batch_dimension(Idx batch_dimension) { - handle_.call_with(PGM_set_batch_dimension, get(), batch_dimension); - } - void set_experimental_features(Idx experimental_features) { handle_.call_with(PGM_set_experimental_features, get(), experimental_features); } From fa6fdf96b59389bce4a29b163b97da2dd980380d Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:43:24 +0100 Subject: [PATCH 35/40] c-api ready Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../include/power_grid_model_cpp/dataset.hpp | 4 ++++ .../test_api_model_multi_dimension.cpp | 18 ++++++++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp index 3adb5cf44b..74f7e3deed 100644 --- a/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp +++ b/power_grid_model_c/power_grid_model_cpp/include/power_grid_model_cpp/dataset.hpp @@ -198,6 +198,10 @@ class DatasetConst { data.get()); } + void set_next(DatasetConst const& next_dataset) { + handle_.call_with(PGM_dataset_const_set_next, get(), next_dataset.get()); + } + DatasetInfo const& get_info() const { return info_; } private: diff --git a/tests/native_api_tests/test_api_model_multi_dimension.cpp b/tests/native_api_tests/test_api_model_multi_dimension.cpp index b9370426ae..c240d4af28 100644 --- a/tests/native_api_tests/test_api_model_multi_dimension.cpp +++ b/tests/native_api_tests/test_api_model_multi_dimension.cpp @@ -72,21 +72,20 @@ TEST_CASE("API Model Multi-Dimension") { } // construct batch update dataset - std::vector batch_datasets; - batch_datasets.emplace_back("update", true, size_u_ref); - batch_datasets.emplace_back("update", true, size_p_specified); - batch_datasets.emplace_back("update", true, size_q_specified); - - DatasetConst& batch_u_ref = batch_datasets[0]; + DatasetConst batch_u_ref{"update", true, size_u_ref}; batch_u_ref.add_buffer("source", 1, size_u_ref, nullptr, nullptr); batch_u_ref.add_attribute_buffer("source", "u_ref", u_ref.data()); - DatasetConst& batch_p_specified = batch_datasets[1]; + DatasetConst batch_p_specified{"update", true, size_p_specified}; batch_p_specified.add_buffer("sym_load", 1, size_p_specified, nullptr, nullptr); batch_p_specified.add_attribute_buffer("sym_load", "p_specified", p_specified.data()); - DatasetConst& batch_q_specified = batch_datasets[2]; + DatasetConst batch_q_specified{"update", true, size_q_specified}; batch_q_specified.add_buffer("sym_load", 1, size_q_specified, nullptr, nullptr); batch_q_specified.add_attribute_buffer("sym_load", "q_specified", q_specified.data()); + // chaining + batch_u_ref.set_next(batch_p_specified); + batch_p_specified.set_next(batch_q_specified); + // output dataset std::vector i_source_result(total_batch_size); DatasetMutable batch_output_dataset{"sym_output", true, total_batch_size}; @@ -95,10 +94,9 @@ TEST_CASE("API Model Multi-Dimension") { // options Options options{}; - options.set_batch_dimension(3); // calculate - model.calculate(options, batch_output_dataset, batch_datasets); + model.calculate(options, batch_output_dataset, batch_u_ref); // check results for (Idx idx = 0; idx < total_batch_size; ++idx) { From 65888cdbb4a9f334e5454d61d9d6bc06112e7caa Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:48:01 +0100 Subject: [PATCH 36/40] revert python side Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- src/power_grid_model/_core/options.py | 1 - src/power_grid_model/_core/power_grid_core.py | 32 ------------ .../_core/power_grid_dataset.py | 52 ------------------- 3 files changed, 85 deletions(-) diff --git a/src/power_grid_model/_core/options.py b/src/power_grid_model/_core/options.py index 1bb9aa419e..f8f5a910ba 100644 --- a/src/power_grid_model/_core/options.py +++ b/src/power_grid_model/_core/options.py @@ -45,7 +45,6 @@ class Options: tap_changing_strategy = OptionSetter(pgc.set_tap_changing_strategy) short_circuit_voltage_scaling = OptionSetter(pgc.set_short_circuit_voltage_scaling) experimental_features = OptionSetter(pgc.set_experimental_features) - batch_dimension = OptionSetter(pgc.set_batch_dimension) @property def opt(self) -> OptionsPtr: diff --git a/src/power_grid_model/_core/power_grid_core.py b/src/power_grid_model/_core/power_grid_core.py index 3a03a34d49..c276303273 100644 --- a/src/power_grid_model/_core/power_grid_core.py +++ b/src/power_grid_model/_core/power_grid_core.py @@ -87,12 +87,6 @@ class WritableDatasetPtr(c_void_p): """ -class MultiDimensionDatasetPtr(c_void_p): - """ - Pointer to multi-dimensional dataset - """ - - class DatasetInfoPtr(c_void_p): """ Pointer to dataset info @@ -344,10 +338,6 @@ def set_max_iter(self, opt: OptionsPtr, max_iter: int) -> None: # type: ignore[ def set_threading(self, opt: OptionsPtr, threading: int) -> None: # type: ignore[empty-body] pass # pragma: no cover - @make_c_binding - def set_batch_dimension(self, opt: OptionsPtr, batch_dimension: int) -> None: # type: ignore[empty-body] - pass # pragma: no cover - @make_c_binding def create_model( # type: ignore[empty-body] self, @@ -515,28 +505,6 @@ def dataset_writable_set_attribute_buffer( ) -> None: # type: ignore[empty-body] pass # pragma: no cover - @make_c_binding - def dataset_create_multidimensional_from_const( # type: ignore[empty-body] - self, - const_datasets: POINTER(ConstDatasetPtr), # type: ignore[valid-type] - n_datasets: int, - ) -> MultiDimensionDatasetPtr: - pass # pragma: no cover - - @make_c_binding - def get_array_pointer_from_multidimensional( # type: ignore[empty-body] - self, - multidimensional_dataset: MultiDimensionDatasetPtr, - ) -> ConstDatasetPtr: # type: ignore[valid-type] - pass # pragma: no cover - - @make_c_binding - def destroy_multidimensional_dataset( # type: ignore[empty-body] - self, - multidimensional_dataset: MultiDimensionDatasetPtr, - ) -> None: - pass # pragma: no cover - @make_c_binding def create_deserializer_from_binary_buffer( # type: ignore[empty-body] self, data: bytes, size: int, serialization_format: int diff --git a/src/power_grid_model/_core/power_grid_dataset.py b/src/power_grid_model/_core/power_grid_dataset.py index 88bbf6c359..f6013f16f0 100644 --- a/src/power_grid_model/_core/power_grid_dataset.py +++ b/src/power_grid_model/_core/power_grid_dataset.py @@ -32,7 +32,6 @@ from power_grid_model._core.power_grid_core import ( ConstDatasetPtr, DatasetInfoPtr, - MultiDimensionDatasetPtr, MutableDatasetPtr, WritableDatasetPtr, power_grid_core as pgc, @@ -355,57 +354,6 @@ def __del__(self): pgc.destroy_dataset_const(self._const_dataset) -class CMultiDimensionalDataset: - """ - A proxy of a multi-dimensional dataset representing multiple const dataset for multidimensional batch scenarios. - """ - - _multi_dimensional_dataset: MultiDimensionDatasetPtr - _const_datasets: list[CConstDataset] - _total_batch_size: int - - def __new__(cls, datasets: list[CConstDataset]): - instance = super().__new__(cls) - instance._multi_dimensional_dataset = MultiDimensionDatasetPtr() - instance._const_datasets = datasets - - DatasetsPtrArray = ConstDatasetPtr * len(datasets) - datasets_ptr_array = DatasetsPtrArray() - total_batch_size = 1 - for idx, dataset in enumerate(datasets): - datasets_ptr_array[idx] = dataset.get_dataset_ptr() - total_batch_size *= dataset.get_info().batch_size() - instance._total_batch_size = total_batch_size - - instance._multi_dimensional_dataset = pgc.dataset_create_multidimensional_from_const( - datasets_ptr_array, len(datasets) - ) - assert_no_error() - - return instance - - def get_array_ptr(self) -> ConstDatasetPtr: - """ - Get the raw underlying multi-dimensional dataset pointer. - - Returns: - MultiDimensionDatasetPtr: the raw underlying multi-dimensional dataset pointer. - """ - return pgc.get_array_pointer_from_multidimensional(self._multi_dimensional_dataset) - - def get_total_batch_size(self) -> int: - """ - Get the total batch size of the multi-dimensional dataset. - - Returns: - int: the total batch size of the multi-dimensional dataset. - """ - return self._total_batch_size - - def __del__(self): - pgc.destroy_multidimensional_dataset(self._multi_dimensional_dataset) - - class CWritableDataset: """ A view of a Power Grid Model-owned dataset. From 61e915fb9bf3f160cb498e8ac4589cbada9ba1df Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 15:54:02 +0100 Subject: [PATCH 37/40] adjust python chaining Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- src/power_grid_model/_core/power_grid_core.py | 4 ++++ src/power_grid_model/_core/power_grid_dataset.py | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/power_grid_model/_core/power_grid_core.py b/src/power_grid_model/_core/power_grid_core.py index c276303273..9df7e551f4 100644 --- a/src/power_grid_model/_core/power_grid_core.py +++ b/src/power_grid_model/_core/power_grid_core.py @@ -481,6 +481,10 @@ def destroy_dataset_const(self, dataset: ConstDatasetPtr) -> None: # type: igno def dataset_const_get_info(self, dataset: ConstDatasetPtr) -> DatasetInfoPtr: # type: ignore[empty-body] pass # pragma: no cover + @make_c_binding + def dataset_const_set_next(self, dataset: ConstDatasetPtr, next_dataset: ConstDatasetPtr) -> None: # type: ignore[empty-body] + pass # pragma: no cover + @make_c_binding def dataset_writable_get_info(self, dataset: WritableDatasetPtr) -> DatasetInfoPtr: # type: ignore[empty-body] pass # pragma: no cover diff --git a/src/power_grid_model/_core/power_grid_dataset.py b/src/power_grid_model/_core/power_grid_dataset.py index f6013f16f0..36597b3bd5 100644 --- a/src/power_grid_model/_core/power_grid_dataset.py +++ b/src/power_grid_model/_core/power_grid_dataset.py @@ -350,6 +350,16 @@ def get_info(self) -> CDatasetInfo: """ return CDatasetInfo(pgc.dataset_const_get_info(self._const_dataset)) + def set_next(self, next_dataset: "CConstDataset") -> None: + """ + Set the next dataset in the linked list. + + Args: + next_dataset: The next dataset to set. + """ + pgc.dataset_const_set_next(self._const_dataset, next_dataset._const_dataset) + assert_no_error() + def __del__(self): pgc.destroy_dataset_const(self._const_dataset) From fb22a7033bf27628061206f3dcf340d960334d08 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:05:43 +0100 Subject: [PATCH 38/40] python side ready Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- src/power_grid_model/_core/power_grid_model.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/power_grid_model/_core/power_grid_model.py b/src/power_grid_model/_core/power_grid_model.py index 2f9c0d2803..17a4abc5b5 100644 --- a/src/power_grid_model/_core/power_grid_model.py +++ b/src/power_grid_model/_core/power_grid_model.py @@ -6,6 +6,7 @@ Main power grid model class """ +import itertools from enum import IntEnum from typing import Any, overload @@ -49,7 +50,6 @@ from power_grid_model._core.index_integer import IdNp, IdxNp from power_grid_model._core.options import Options from power_grid_model._core.power_grid_core import ConstDatasetPtr, IDPtr, IdxPtr, ModelPtr, power_grid_core as pgc -from power_grid_model._core.power_grid_dataset import CMultiDimensionalDataset from power_grid_model._core.typing import ComponentAttributeMapping, ComponentAttributeMappingDict @@ -291,12 +291,12 @@ def _calculate_impl( # noqa: PLR0913 is_batch = True if not isinstance(update_data, list): update_data = [update_data] - options.batch_dimension = len(update_data) update_data = [_map_to_component_types(x) for x in update_data] - - prepared_update = CMultiDimensionalDataset([prepare_update_view(x) for x in update_data]) - update_ptr: ConstDatasetPtr = prepared_update.get_array_ptr() - batch_size = prepared_update.get_total_batch_size() + prepared_update = [prepare_update_view(x) for x in update_data] + for this_dataset, next_dataset in itertools.pairwise(prepared_update): + this_dataset.set_next(next_dataset) + update_ptr: ConstDatasetPtr = prepared_update[0].get_dataset_ptr() if prepared_update else ConstDatasetPtr() + batch_size = np.prod([x.get_info().batch_size() for x in prepared_update]) output_data = self._construct_output( output_component_types=output_component_types, From 79936a612f3a38ec646e737bd44ca59a9792c4f9 Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Wed, 26 Nov 2025 09:22:30 +0100 Subject: [PATCH 39/40] fix format Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- src/power_grid_model/_core/power_grid_model.py | 3 ++- tests/native_api_tests/test_api_model_multi_dimension.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/power_grid_model/_core/power_grid_model.py b/src/power_grid_model/_core/power_grid_model.py index 17a4abc5b5..71adb6a42a 100644 --- a/src/power_grid_model/_core/power_grid_model.py +++ b/src/power_grid_model/_core/power_grid_model.py @@ -8,6 +8,7 @@ import itertools from enum import IntEnum +from math import prod from typing import Any, overload import numpy as np @@ -296,7 +297,7 @@ def _calculate_impl( # noqa: PLR0913 for this_dataset, next_dataset in itertools.pairwise(prepared_update): this_dataset.set_next(next_dataset) update_ptr: ConstDatasetPtr = prepared_update[0].get_dataset_ptr() if prepared_update else ConstDatasetPtr() - batch_size = np.prod([x.get_info().batch_size() for x in prepared_update]) + batch_size = prod([x.get_info().batch_size() for x in prepared_update]) output_data = self._construct_output( output_component_types=output_component_types, diff --git a/tests/native_api_tests/test_api_model_multi_dimension.cpp b/tests/native_api_tests/test_api_model_multi_dimension.cpp index c240d4af28..bd3945b919 100644 --- a/tests/native_api_tests/test_api_model_multi_dimension.cpp +++ b/tests/native_api_tests/test_api_model_multi_dimension.cpp @@ -93,7 +93,7 @@ TEST_CASE("API Model Multi-Dimension") { batch_output_dataset.add_attribute_buffer("source", "i", i_source_result.data()); // options - Options options{}; + Options const options{}; // calculate model.calculate(options, batch_output_dataset, batch_u_ref); From 2e0ff7355e8dc12d9eea70244bc2b9f66e137c4f Mon Sep 17 00:00:00 2001 From: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> Date: Wed, 26 Nov 2025 09:53:18 +0100 Subject: [PATCH 40/40] fix format Signed-off-by: Tony Xiang <19280867+TonyXiang8787@users.noreply.github.com> --- .../power_grid_model_c/include/power_grid_model_c/dataset.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h index 8da08e4f26..88294d0518 100644 --- a/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h +++ b/power_grid_model_c/power_grid_model_c/include/power_grid_model_c/dataset.h @@ -214,9 +214,9 @@ PGM_API PGM_DatasetInfo const* PGM_dataset_const_get_info(PGM_Handle* handle, PG /** * @brief Set next const dataset for chaining for MD batch - * - * @param handle - * @param dataset + * + * @param handle + * @param dataset * @param next_dataset The next dataset in the chain. */ PGM_API void PGM_dataset_const_set_next(PGM_Handle* handle, PGM_ConstDataset* dataset,