Skip to content

Commit

Permalink
Merge pull request #769 from PowerGridModel/feature/alignment-bug-rep…
Browse files Browse the repository at this point in the history
…ro-case

Repro case for buffer alignment
  • Loading branch information
TonyXiang8787 authored Oct 8, 2024
2 parents 31914b2 + e3f9a86 commit 74d351c
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 5 deletions.
2 changes: 1 addition & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ if [[ "${COVERAGE}" ]]; then
-d ${BUILD_DIR}/tests/cpp_unit_tests/CMakeFiles/power_grid_model_unit_tests.dir \
-d ${BUILD_DIR}/tests/cpp_integration_tests/CMakeFiles/power_grid_model_integration_tests.dir \
-d ${BUILD_DIR}/tests/cpp_validation_tests/CMakeFiles/power_grid_model_validation_tests.dir \
-d ${BUILD_DIR}/tests/c_api_tests/CMakeFiles/power_grid_model_c_api_tests.dir \
-d ${BUILD_DIR}/tests/native_api_tests/CMakeFiles/power_grid_model_api_tests.dir \
-d ${BUILD_DIR}/power_grid_model_c/power_grid_model_c/CMakeFiles/power_grid_model_c.dir \
-b . \
--no-external \
Expand Down
6 changes: 4 additions & 2 deletions power_grid_model_c/power_grid_model_c/src/handle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ auto call_with_catch(PGM_Handle* handle, Functor func, PGM_Idx error_code, std::
try {
return func();
} catch (Exception const& e) {
handle->err_code = error_code;
handle->err_msg = std::string(e.what()) + std::string(extra_msg);
if (handle) {
handle->err_code = error_code;
handle->err_msg = std::string{e.what()} + std::string{extra_msg};
}
return static_cast<ReturnValueType>(empty);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ class MetaData {
return std::string{PGM_meta_attribute_name(nullptr, attribute)};
}

static Idx attribute_ctype(MetaAttribute const* attribute) { return PGM_meta_attribute_ctype(nullptr, attribute); }
static PGM_CType attribute_ctype(MetaAttribute const* attribute) {
return static_cast<PGM_CType>(PGM_meta_attribute_ctype(nullptr, attribute));
}

static size_t attribute_offset(MetaAttribute const* attribute) {
return PGM_meta_attribute_offset(nullptr, attribute);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "basics.hpp"
#include "handle.hpp"
#include "meta_data.hpp"

#include <array>
#include <complex>
Expand All @@ -26,6 +27,15 @@ inline bool is_nan(std::array<std::complex<double>, 3> const& array) {
return is_nan(array[0]) || is_nan(array[1]) || is_nan(array[2]);
}

constexpr double nan = std::numeric_limits<double>::quiet_NaN();
constexpr int8_t na_IntS = std::numeric_limits<int8_t>::min();
constexpr ID na_IntID = std::numeric_limits<ID>::min();

template <std::same_as<double> T> constexpr T nan_value() { return nan; }
template <std::same_as<std::array<double, 3>> T> constexpr T nan_value() { return {nan, nan, nan}; }
template <std::same_as<ID> T> constexpr T nan_value() { return na_IntID; }
template <std::same_as<int8_t> T> constexpr T nan_value() { return na_IntS; }

class UnsupportedPGM_CType : public PowerGridError {
public:
UnsupportedPGM_CType()
Expand All @@ -51,6 +61,12 @@ decltype(auto) pgm_type_func_selector(enum PGM_CType type, Functor&& f, Args&&..
}
}

template <class Functor, class... Args>
decltype(auto) pgm_type_func_selector(MetaAttribute const* attribute, Functor&& f, Args&&... args) {
return pgm_type_func_selector(MetaData::attribute_ctype(attribute), std::forward<Functor>(f),
std::forward<Args>(args)...);
}

} // namespace power_grid_model_cpp

#endif // POWER_GRID_MODEL_CPP_UTILS_HPP
2 changes: 1 addition & 1 deletion tests/cpp_validation_tests/test_validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ void assert_result(OwningDataset const& owning_result, OwningDataset const& owni
attribute_name, dynamic_atol, rtol);
};

pgm_type_func_selector(static_cast<PGM_CType>(attribute_type), callable_wrapper);
pgm_type_func_selector(attribute_type, callable_wrapper);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions tests/native_api_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
set(PROJECT_SOURCES
"test_entry_point.cpp"
"test_api_meta_data.cpp"
"test_api_buffer.cpp"
"test_api_model.cpp"
"test_api_serialization.cpp"
"test_api_utils.cpp"
)

add_executable(power_grid_model_api_tests ${PROJECT_SOURCES})
Expand Down
233 changes: 233 additions & 0 deletions tests/native_api_tests/test_api_buffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// SPDX-FileCopyrightText: Contributors to the Power Grid Model project <[email protected]>
//
// SPDX-License-Identifier: MPL-2.0

#include <power_grid_model_cpp/buffer.hpp>
#include <power_grid_model_cpp/meta_data.hpp>
#include <power_grid_model_cpp/utils.hpp>

#include <power_grid_model_c/dataset_definitions.h>

#include <doctest/doctest.h>

#include <algorithm>
#include <array>
#include <cmath>
#include <exception>
#include <limits>
#include <ranges>
#include <string>

namespace power_grid_model_cpp {
namespace {
template <typename T, std::convertible_to<T> U> constexpr T as_type(U const& value) {
if constexpr (std::same_as<T, U>) {
return value;
} else {
return static_cast<T>(value);
}
}
template <std::same_as<std::array<double, 3>> T, std::convertible_to<double> U> constexpr T as_type(U const& value) {
auto const d_value = as_type<double>(value);
return {d_value, d_value, d_value};
}

void check_array_get_value(MetaComponent const* component, MetaAttribute const* attribute, Idx size) {
pgm_type_func_selector(attribute, [component, attribute, size]<typename T>() {
std::vector<T> ref_buffer(size);

Buffer buffer{component, size};
buffer.set_nan();

buffer.get_value(attribute, ref_buffer.data(), sizeof(T));
for (Idx idx = 0; idx < size; ++idx) {
REQUIRE(is_nan(ref_buffer[idx]));
}
});
}

void check_array_set_value(MetaComponent const* component, MetaAttribute const* attribute, Idx size) {
pgm_type_func_selector(attribute, [component, attribute, size]<typename T>() {
std::vector<T> source_buffer(size);
std::vector<T> ref_buffer(size);
for (Idx idx = 0; idx < size; ++idx) {
source_buffer[idx] = as_type<T>(idx);
}

Buffer buffer{component, size};
buffer.set_nan();

buffer.set_value(attribute, source_buffer.data(), sizeof(T));
buffer.get_value(attribute, ref_buffer.data(), sizeof(T));
for (Idx idx = 0; idx < size; ++idx) {
REQUIRE(ref_buffer[idx] == as_type<T>(idx));
}
});
}

void check_sub_array_get_value(MetaComponent const* component, MetaAttribute const* attribute, Idx size) {
auto const check_sub_offset_size = [component, attribute, size]<typename T>(Idx offset, Idx sub_size) {
std::vector<T> ref_buffer(size);

Buffer buffer{component, size};
buffer.set_nan();

buffer.get_value(attribute, ref_buffer.data(), offset, sub_size, sizeof(T));
for (Idx idx = 0; idx < size; ++idx) {
if (idx >= offset && idx < offset + sub_size) {
REQUIRE(is_nan(ref_buffer[idx]));
} else {
REQUIRE(ref_buffer[idx] == T{});
}
}
};
for (Idx sub_size = 0; sub_size < size; ++sub_size) {
CAPTURE(sub_size);
for (Idx offset = 0; offset < size - sub_size; ++offset) {
CAPTURE(offset);
pgm_type_func_selector(attribute, check_sub_offset_size, offset, sub_size);
}
}
}

void check_sub_array_set_value(MetaComponent const* component, MetaAttribute const* attribute, Idx size) {
auto const check_sub_offset_size = [component, attribute, size]<typename T>(Idx offset, Idx sub_size) {
std::vector<T> ref_buffer(size);
std::vector<T> source_buffer(size);
for (Idx idx = 0; idx < size; ++idx) {
source_buffer[idx] = as_type<T>(idx);
}

Buffer buffer{component, size};
buffer.set_nan();
buffer.set_value(attribute, source_buffer.data(), offset, sub_size, sizeof(T));
buffer.get_value(attribute, ref_buffer.data(), sizeof(T));
for (Idx idx = 0; idx < size; ++idx) {
if (idx >= offset && idx < offset + sub_size) {
REQUIRE(ref_buffer[idx] == as_type<T>(idx));
} else {
REQUIRE(is_nan(ref_buffer[idx]));
}
}
};
for (Idx sub_size = 0; sub_size < size; ++sub_size) {
CAPTURE(sub_size);
for (Idx offset = 0; offset < size - sub_size; ++offset) {
CAPTURE(offset);
pgm_type_func_selector(attribute, check_sub_offset_size, offset, sub_size);
}
}
}

void check_single_get_value(MetaComponent const* component, MetaAttribute const* attribute, Idx size) {
pgm_type_func_selector(attribute, [component, attribute, size]<typename T>() {
T ref_value{};

Buffer buffer{component, size};
buffer.set_nan();

for (Idx idx = 0; idx < size; ++idx) {
buffer.get_value(attribute, &ref_value, idx, 0);
if (size > 0) {
REQUIRE(is_nan(ref_value));
} else {
REQUIRE(ref_value == T{});
}
}
});
}

void check_single_set_value(MetaComponent const* component, MetaAttribute const* attribute, Idx size) {
pgm_type_func_selector(attribute, [component, attribute, size]<typename T>() {
T const source_value{1};
T ref_value{};

Buffer buffer{component, size};
buffer.set_nan();

for (Idx idx = 0; idx < size; ++idx) {
buffer.set_value(attribute, &source_value, idx, 0);
buffer.get_value(attribute, &ref_value, idx, 0);
if (size > 0) {
REQUIRE(ref_value == source_value);
} else {
REQUIRE(ref_value == T{});
}
}
});
}
} // namespace

TEST_CASE("API Buffer") {
auto const loop_datasets_components_attributes = []<typename Func>(Func func) {
for (Idx dataset_idx = 0; dataset_idx < MetaData::n_datasets(); ++dataset_idx) {
CAPTURE(dataset_idx);
MetaDataset const* dataset = MetaData::get_dataset_by_idx(dataset_idx);
CAPTURE(MetaData::dataset_name(dataset));
for (Idx component_idx = 0; component_idx < MetaData::n_components(dataset); ++component_idx) {
CAPTURE(component_idx);
MetaComponent const* component = MetaData::get_component_by_idx(dataset, component_idx);
CAPTURE(MetaData::component_name(component));

for (Idx attribute_idx = 0; attribute_idx < MetaData::n_attributes(component); ++attribute_idx) {
CAPTURE(attribute_idx);
MetaAttribute const* attribute = MetaData::get_attribute_by_idx(component, attribute_idx);
CAPTURE(MetaData::attribute_name(attribute));

func(component, attribute);
}
}
}
};

SUBCASE("Array buffer access") {
SUBCASE("get value") {
loop_datasets_components_attributes([](MetaComponent const* component, MetaAttribute const* attribute) {
for (Idx size = 0; size < 4; ++size) {
check_array_get_value(component, attribute, size);
}
});
}
SUBCASE("set value") {
loop_datasets_components_attributes([](MetaComponent const* component, MetaAttribute const* attribute) {
for (Idx size = 0; size < 4; ++size) {
check_array_set_value(component, attribute, size);
}
});
}
}
SUBCASE("Sub-array buffer access") {
SUBCASE("get value") {
loop_datasets_components_attributes([](MetaComponent const* component, MetaAttribute const* attribute) {
for (Idx size = 0; size < 4; ++size) {
check_sub_array_get_value(component, attribute, size);
}
});
}
SUBCASE("set value") {
loop_datasets_components_attributes([](MetaComponent const* component, MetaAttribute const* attribute) {
for (Idx size = 0; size < 4; ++size) {
check_sub_array_set_value(component, attribute, size);
}
});
}
}
SUBCASE("Single buffer access") {
SUBCASE("get value") {
loop_datasets_components_attributes([](MetaComponent const* component, MetaAttribute const* attribute) {
for (Idx size = 0; size < 4; ++size) {
check_single_get_value(component, attribute, size);
}
});
}
SUBCASE("set value") {
loop_datasets_components_attributes([](MetaComponent const* component, MetaAttribute const* attribute) {
for (Idx size = 0; size < 4; ++size) {
check_single_set_value(component, attribute, size);
}
});
}
}
}

} // namespace power_grid_model_cpp
28 changes: 28 additions & 0 deletions tests/native_api_tests/test_api_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Contributors to the Power Grid Model project <[email protected]>
//
// SPDX-License-Identifier: MPL-2.0

#include <power_grid_model_cpp/utils.hpp>

#include <doctest/doctest.h>

namespace power_grid_model_cpp {
TEST_CASE("API Utils") {
SUBCASE("NaN values and checks") {
CHECK_FALSE(is_nan(ID{}));
CHECK_FALSE(is_nan(IntS{}));
CHECK_FALSE(is_nan(std::complex<double>{}));
CHECK_FALSE(is_nan(std::array<double, 3>{}));
CHECK_FALSE(is_nan(std::array<std::complex<double>, 3>{}));

CHECK(is_nan(nan));
CHECK(is_nan(na_IntS));
CHECK(is_nan(na_IntID));

CHECK(is_nan(nan_value<double>()));
CHECK(is_nan(nan_value<std::array<double, 3>>()));
CHECK(is_nan(nan_value<ID>()));
CHECK(is_nan(nan_value<IntS>()));
}
}
} // namespace power_grid_model_cpp

0 comments on commit 74d351c

Please sign in to comment.