From 8cf9d40e5223f1023ec315f7418861fd3ba4ff00 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Mon, 19 Jan 2026 11:34:59 +0100 Subject: [PATCH 1/2] cherry-picked changes from #1264 Signed-off-by: Martijn Govers --- .../power_grid_model_c/src/dataset.cpp | 4 +- .../power_grid_model_c/src/model.cpp | 45 +++++++++++-------- 2 files changed, 30 insertions(+), 19 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 241eaae88..5b1115b2f 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 @@ -15,6 +15,7 @@ #include #include +namespace { using namespace power_grid_model; using namespace power_grid_model::meta_data; using power_grid_model_c::call_with_catch; @@ -27,11 +28,12 @@ using power_grid_model_c::safe_ptr_maybe_nullptr; using power_grid_model_c::safe_str_view; using power_grid_model_c::to_c_bool; using power_grid_model_c::to_c_size; +} // namespace // dataset info char const* PGM_dataset_info_name(PGM_Handle* handle, PGM_DatasetInfo const* info) { - return call_with_catch(handle, [info] { return safe_ptr_get(safe_ptr(cast_to_cpp(info))->dataset).name; }); + return call_with_catch(handle, [info] { return safe_ptr_get(safe_ptr_get(cast_to_cpp(info)).dataset).name; }); } PGM_Idx PGM_dataset_info_is_batch(PGM_Handle* handle, PGM_DatasetInfo const* info) { 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 6e8378003..6fdc67792 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 @@ -156,7 +156,7 @@ class BadCalculationRequest : public PowerGridError { explicit BadCalculationRequest(std::string msg) : PowerGridError{std::move(msg)} {} }; -void calculate_single_batch_dimension_impl(MainModel& model, PGM_Options const& opt, +void calculate_single_batch_dimension_impl(MainModel& model, MainModel::Options const& options, MutableDataset const& output_dataset, ConstDataset const* batch_dataset) { // check dataset integrity if ((batch_dataset != nullptr) && (!batch_dataset->is_batch() || !output_dataset.is_batch())) { @@ -168,13 +168,6 @@ void calculate_single_batch_dimension_impl(MainModel& model, PGM_Options const& ? safe_ptr_get(batch_dataset) : ConstDataset{false, 1, "update", output_dataset.meta_data()}; - check_calculate_valid_options(opt); - auto const options = extract_calculation_options(opt); - - if (opt.experimental_features == PGM_experimental_features_disabled) { - check_no_experimental_features_used(model, options); - } - model.calculate(options, output_dataset, exported_update_dataset); } @@ -241,29 +234,33 @@ class MDBatchExceptionHandler : public power_grid_model_c::DefaultExceptionHandl Idx get_batch_dimension(ConstDataset const* batch_dataset) { Idx dimension = 0; - while (batch_dataset != nullptr) { + ConstDataset const* safe_batch_dataset = safe_ptr_maybe_nullptr(batch_dataset); + while (safe_batch_dataset != nullptr) { ++dimension; - batch_dataset = batch_dataset->get_next_cartesian_product_dimension(); + safe_batch_dataset = + safe_ptr_maybe_nullptr(safe_ptr_get(safe_batch_dataset).get_next_cartesian_product_dimension()); } return dimension; } Idx get_stride_size(ConstDataset const* batch_dataset) { Idx size = 1; - ConstDataset const* current = batch_dataset->get_next_cartesian_product_dimension(); + ConstDataset const* current = + safe_ptr_maybe_nullptr(safe_ptr_get(batch_dataset).get_next_cartesian_product_dimension()); while (current != nullptr) { - size *= current->batch_size(); - current = current->get_next_cartesian_product_dimension(); + auto const& safe_current = safe_ptr_get(current); + size *= safe_current.batch_size(); + current = safe_current.get_next_cartesian_product_dimension(); } return size; } // run calculation -void calculate_multi_dimensional_impl(MainModel& model, PGM_Options const& opt, MutableDataset const& output_dataset, +void calculate_multi_dimensional_impl(MainModel& model, MainModel::Options const& options, MutableDataset const& output_dataset, ConstDataset const* batch_dataset) { // for dimension < 2 (one-time or 1D batch), call implementation directly if (auto const batch_dimension = get_batch_dimension(batch_dataset); batch_dimension < 2) { - calculate_single_batch_dimension_impl(model, opt, output_dataset, batch_dataset); + calculate_single_batch_dimension_impl(model, options, output_dataset, batch_dataset); return; } @@ -280,7 +277,7 @@ void calculate_multi_dimensional_impl(MainModel& model, PGM_Options const& opt, // a new handle call_with_catch( &local_handle, - [&model, &opt, &output_dataset, &safe_batch_dataset, i, stride_size] { + [&model, &options, &output_dataset, &safe_batch_dataset, i, stride_size] { // create sliced datasets for the rest of dimensions ConstDataset const single_update_dataset = safe_batch_dataset.get_individual_scenario(i); MutableDataset const sliced_output_dataset = @@ -293,7 +290,7 @@ void calculate_multi_dimensional_impl(MainModel& model, PGM_Options const& opt, local_model.update_components(single_update_dataset); // recursive call - calculate_multi_dimensional_impl(local_model, opt, sliced_output_dataset, + calculate_multi_dimensional_impl(local_model, options, sliced_output_dataset, safe_batch_dataset.get_next_cartesian_product_dimension()); }, MDBatchExceptionHandler{i * stride_size, stride_size}); @@ -305,6 +302,18 @@ void calculate_multi_dimensional_impl(MainModel& model, PGM_Options const& opt, } } +void calculate_impl(MainModel& model, PGM_Options const& options, MutableDataset const& output_dataset, + ConstDataset const* batch_dataset) { + check_calculate_valid_options(options); + auto const extracted_options = extract_calculation_options(options); + + if (options.experimental_features == PGM_experimental_features_disabled) { + check_no_experimental_features_used(model, extracted_options); + } + + calculate_multi_dimensional_impl(model, extracted_options, output_dataset, batch_dataset); +} + } // namespace // run calculation @@ -313,7 +322,7 @@ void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options co call_with_catch( handle, [model, opt, output_dataset, batch_dataset] { - calculate_multi_dimensional_impl(safe_ptr_get(cast_to_cpp(model)), safe_ptr_get(opt), + calculate_impl(safe_ptr_get(cast_to_cpp(model)), safe_ptr_get(opt), safe_ptr_get(cast_to_cpp(output_dataset)), safe_ptr_maybe_nullptr(cast_to_cpp(batch_dataset))); }, From 7de2fc426a901222dfb94f765f61184a1d55e6f6 Mon Sep 17 00:00:00 2001 From: Martijn Govers Date: Tue, 20 Jan 2026 09:14:37 +0100 Subject: [PATCH 2/2] fix bug in which nullptr attribute buffers for size-0 components was disallowed Signed-off-by: Martijn Govers --- .../include/power_grid_model/auxiliary/dataset.hpp | 6 +++++- power_grid_model_c/power_grid_model_c/src/dataset.cpp | 6 +++--- power_grid_model_c/power_grid_model_c/src/model.cpp | 8 ++++---- tests/cpp_unit_tests/test_dataset.cpp | 9 +++++++++ 4 files changed, 21 insertions(+), 8 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 b759f40bc..b1a38612b 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 @@ -611,8 +611,12 @@ template class Dataset { }) != buffer.attributes.end()) { throw DatasetError{"Cannot have duplicated attribute buffers!\n"}; } + auto const& component_info = dataset_info_.component_info[idx]; + if (component_info.total_elements > 0 && data == nullptr) { + throw DatasetError{"Attribute buffer data pointer cannot be null for non-empty component!\n"}; + } AttributeBuffer const attribute_buffer{ - .data = data, .meta_attribute = &dataset_info_.component_info[idx].component->get_attribute(attribute)}; + .data = data, .meta_attribute = &component_info.component->get_attribute(attribute)}; buffer.attributes.emplace_back(attribute_buffer); } 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 5b1115b2f..2b59243a6 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 @@ -134,7 +134,7 @@ void PGM_dataset_const_add_attribute_buffer(PGM_Handle* handle, PGM_ConstDataset char const* attribute, void const* data) { call_with_catch(handle, [dataset, component, attribute, data] { safe_ptr_get(cast_to_cpp(dataset)) - .add_attribute_buffer(safe_str_view(component), safe_str_view(attribute), safe_ptr(data)); + .add_attribute_buffer(safe_str_view(component), safe_str_view(attribute), safe_ptr_maybe_nullptr(data)); }); } void PGM_dataset_const_set_next_cartesian_product_dimension(PGM_Handle* handle, PGM_ConstDataset* dataset, @@ -168,7 +168,7 @@ void PGM_dataset_writable_set_attribute_buffer(PGM_Handle* handle, PGM_WritableD char const* attribute, void* data) { call_with_catch(handle, [dataset, component, attribute, data] { safe_ptr_get(cast_to_cpp(dataset)) - .set_attribute_buffer(safe_str_view(component), safe_str_view(attribute), safe_ptr(data)); + .set_attribute_buffer(safe_str_view(component), safe_str_view(attribute), safe_ptr_maybe_nullptr(data)); }); } @@ -200,7 +200,7 @@ void PGM_dataset_mutable_add_attribute_buffer(PGM_Handle* handle, PGM_MutableDat char const* attribute, void* data) { call_with_catch(handle, [dataset, component, attribute, data] { safe_ptr_get(cast_to_cpp(dataset)) - .add_attribute_buffer(safe_str_view(component), safe_str_view(attribute), safe_ptr(data)); + .add_attribute_buffer(safe_str_view(component), safe_str_view(attribute), safe_ptr_maybe_nullptr(data)); }); } 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 6fdc67792..e2af7bef8 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 @@ -256,8 +256,8 @@ Idx get_stride_size(ConstDataset const* batch_dataset) { } // run calculation -void calculate_multi_dimensional_impl(MainModel& model, MainModel::Options const& options, MutableDataset const& output_dataset, - ConstDataset const* batch_dataset) { +void calculate_multi_dimensional_impl(MainModel& model, MainModel::Options const& options, + MutableDataset const& output_dataset, ConstDataset const* batch_dataset) { // for dimension < 2 (one-time or 1D batch), call implementation directly if (auto const batch_dimension = get_batch_dimension(batch_dataset); batch_dimension < 2) { calculate_single_batch_dimension_impl(model, options, output_dataset, batch_dataset); @@ -323,8 +323,8 @@ void PGM_calculate(PGM_Handle* handle, PGM_PowerGridModel* model, PGM_Options co handle, [model, opt, output_dataset, batch_dataset] { calculate_impl(safe_ptr_get(cast_to_cpp(model)), safe_ptr_get(opt), - safe_ptr_get(cast_to_cpp(output_dataset)), - safe_ptr_maybe_nullptr(cast_to_cpp(batch_dataset))); + safe_ptr_get(cast_to_cpp(output_dataset)), + safe_ptr_maybe_nullptr(cast_to_cpp(batch_dataset))); }, batch_exception_handler); } diff --git a/tests/cpp_unit_tests/test_dataset.cpp b/tests/cpp_unit_tests/test_dataset.cpp index d8a15ed57..f1eee01d2 100644 --- a/tests/cpp_unit_tests/test_dataset.cpp +++ b/tests/cpp_unit_tests/test_dataset.cpp @@ -720,6 +720,15 @@ TEST_CASE_TEMPLATE("Test dataset (common)", DatasetType, ConstDataset, MutableDa check_all_spans(); } } + + double* a0_nullptr = nullptr; + if (total_elements > 0) { + CHECK_THROWS_AS(add_attribute_buffer(dataset, A::name, A::InputType::a0_name, a0_nullptr), + DatasetError); + } else { + add_attribute_buffer(dataset, A::name, A::InputType::a0_name, a0_nullptr); + } + check_all_spans(); } } SUBCASE("Batch dataset") {