diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bb14a5b4..a1d363ae8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ include("cmake/pgm_version.cmake") project(power_grid_model VERSION ${PGM_VERSION}) option(PGM_ENABLE_DEV_BUILD "Enable developer build (e.g.: tests)" OFF) +option(PGM_ENABLE_HARDENING "Enable compile and link time hardening options" ON) set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/CMakePresets.json b/CMakePresets.json index 577b6d497..ee2a77d09 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -112,7 +112,7 @@ { "name": "unix-sanitizer", "environment": { - "SANITIZER_FLAGS": "-fsanitize=address" + "SANITIZER_FLAGS": "-fstack-protector -fsanitize=address,pointer-compare,undefined -fno-sanitize-recover" }, "inherits": "unix-base", "hidden": true @@ -140,6 +140,9 @@ { "name": "msvc-debug", "displayName": "Debug (MSVC)", + "environment": { + "SANITIZER_FLAGS": "/RTC1" + }, "inherits": [ "msvc-base", "debug" 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..1257d0d44 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 @@ -83,10 +83,15 @@ class ColumnarAttributeRange : public std::ranges::view_interface { - AttributeType* buffer_ptr = reinterpret_cast(attribute_buffer.data) + idx_; - auto const& attribute_ref = meta_attribute.template get_attribute( - reinterpret_cast(&value)); - *buffer_ptr = attribute_ref; + if constexpr (sizeof(AttributeType) <= sizeof(value_type)) { + AttributeType* buffer_ptr = reinterpret_cast(attribute_buffer.data) + idx_; + auto const& attribute_ref = meta_attribute.template get_attribute( + reinterpret_cast(&value)); + *buffer_ptr = attribute_ref; + } else { + throw UnreachableHit{"ColumnarAttributeRange::operator=", + "AttributeType larger than value_type!"}; + } }); } return *this; @@ -97,14 +102,18 @@ class ColumnarAttributeRange : public std::ranges::view_interface { + ctype_func_selector(meta_attribute.ctype, [&result, &attribute_buffer, &meta_attribute, + idx = idx_] { + if constexpr (sizeof(AttributeType) <= sizeof(value_type)) { AttributeType const* buffer_ptr = - reinterpret_cast(attribute_buffer.data) + idx_; - auto& attribute_ref = + reinterpret_cast(attribute_buffer.data) + idx; + AttributeType& attribute_ref = meta_attribute.template get_attribute(reinterpret_cast(&result)); attribute_ref = *buffer_ptr; - }); + } else { + throw UnreachableHit{"ColumnarAttributeRange::get", "AttributeType larger than value_type!"}; + } + }); } return result; } diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/common/three_phase_tensor.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/common/three_phase_tensor.hpp index 1cb1a4867..862993240 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/common/three_phase_tensor.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/common/three_phase_tensor.hpp @@ -28,7 +28,7 @@ template using EigenDiagonalTensor3 = Eigen::DiagonalMatrix; template class Vector : public EigenVector3 { public: - Vector() { (*this) = EigenVector3::Zero(); }; + Vector() { this->setConstant(T{}); }; // eigen expression template Vector(Eigen::ArrayBase const& other) : EigenVector3{other} {} template Vector& operator=(Eigen::ArrayBase const& other) { diff --git a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp index 9f76876fb..3c5a48a96 100644 --- a/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp +++ b/power_grid_model_c/power_grid_model/include/power_grid_model/math_solver/y_bus.hpp @@ -289,6 +289,9 @@ template class YBus { std::shared_ptr const> const& param, std::shared_ptr const& y_bus_struct = {}) : math_topology_{topo_ptr} { + assert(math_topology_ != nullptr); + assert(param != nullptr); + // use existing struct or make new struct if (y_bus_struct) { y_bus_struct_ = y_bus_struct; @@ -447,29 +450,31 @@ template class YBus { template requires std::same_as> || std::same_as> std::vector calculate_branch_flow(ComplexValueVector const& u) const { - std::vector branch_flow(math_topology_->branch_bus_idx.size()); - std::transform(math_topology_->branch_bus_idx.cbegin(), math_topology_->branch_bus_idx.cend(), - math_model_param_->branch_param.cbegin(), branch_flow.begin(), - [&u](BranchIdx branch_idx, BranchCalcParam const& param) { - auto const [f, t] = branch_idx; - // if one side is disconnected, use zero voltage at that side - ComplexValue const uf = f != -1 ? u[f] : ComplexValue{0.0}; - ComplexValue const ut = t != -1 ? u[t] : ComplexValue{0.0}; - T output; - - // See "Branch Flow Calculation" in "State Estimation Alliander" - output.i_f = dot(param.yff(), uf) + dot(param.yft(), ut); - output.i_t = dot(param.ytf(), uf) + dot(param.ytt(), ut); - - if constexpr (std::same_as>) { - // See "Shunt Injection Flow Calculation" in "State Estimation Alliander" - output.s_f = uf * conj(output.i_f); - output.s_t = ut * conj(output.i_t); - } - - return output; - }); - return branch_flow; + assert(math_topology_ != nullptr); + + return std::views::zip(math_topology_->branch_bus_idx, math_model_param_->branch_param) | + std::views::transform([&u](auto const& branch_idx_params) -> T { + auto const& [branch_idx, param] = branch_idx_params; + + auto const [f, t] = branch_idx; + // if one side is disconnected, use zero voltage at that side + ComplexValue const uf = f != -1 ? u[f] : ComplexValue{0.0}; + ComplexValue const ut = t != -1 ? u[t] : ComplexValue{0.0}; + T output; + + // See "Branch Flow Calculation" in "State Estimation Alliander" + output.i_f = dot(param.yff(), uf) + dot(param.yft(), ut); + output.i_t = dot(param.ytf(), uf) + dot(param.ytt(), ut); + + if constexpr (std::same_as>) { + // See "Shunt Injection Flow Calculation" in "State Estimation Alliander" + output.s_f = uf * conj(output.i_f); + output.s_t = ut * conj(output.i_t); + } + + return output; + }) | + std::ranges::to>(); } // calculate shunt flow based on voltage, injection direction @@ -477,6 +482,8 @@ template class YBus { requires std::same_as> || std::same_as> std::vector calculate_shunt_flow(ComplexValueVector const& u) const { + assert(math_topology_ != nullptr); + std::vector shunt_flow(math_topology_->n_shunt()); for (auto const [bus, shunts] : enumerated_zip_sequence(math_topology_->shunts_per_bus)) { for (Idx const shunt : shunts) { diff --git a/power_grid_model_c/power_grid_model_c/CMakeLists.txt b/power_grid_model_c/power_grid_model_c/CMakeLists.txt index 14a8ad986..96f21adc7 100644 --- a/power_grid_model_c/power_grid_model_c/CMakeLists.txt +++ b/power_grid_model_c/power_grid_model_c/CMakeLists.txt @@ -49,6 +49,33 @@ set_target_properties( INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE ) +if(PGM_ENABLE_HARDENING) + target_compile_definitions( + power_grid_model_c + PRIVATE + "$<$,$>:_FORTIFY_SOURCE=2>" + "$<$:_ITERATOR_DEBUG_LEVEL=$,2,1>>" + ) + if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(_HAS_ADDRESS_SANITIZER + "$,$>>" + ) + target_compile_options( + power_grid_model_c + BEFORE + PRIVATE "-fstack-protector" "-fsanitize=undefined" "-fno-sanitize-recover=undefined" + ) + endif() + target_link_options( + power_grid_model_c + BEFORE + PRIVATE + "$<$,$>:-fstack-protector;-fsanitize=undefined;-fno-sanitize-recover=undefined>" + "$<$:-static-libubsan>" + "$<$:-lubsan>" + ) +endif() + install( TARGETS power_grid_model_c EXPORT power_grid_modelTargets 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..acd3617cc 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 @@ -31,7 +31,7 @@ using power_grid_model_c::to_c_size; // 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/pyproject.toml b/pyproject.toml index b2c4ae17f..56344a1fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,11 +76,13 @@ Discussion = "https://github.com/orgs/PowerGridModel/discussions" power_grid_model = "power_grid_model._core.power_grid_model_c" [tool.scikit-build] -logging.level = "INFO" +logging.level = "DEBUG" cmake.version = ">=3.23" cmake.build-type = "Release" cmake.args = ["-GNinja"] +cmake.verbose = true + build-dir = "build" @@ -204,11 +206,12 @@ test-command = "pytest {package}/tests" # we do not support # PyPy # musllinux in aarch64 -# disable Python 3.14 for now until it is released skip = ["pp*", "*-musllinux_aarch64"] [tool.cibuildwheel.linux] archs = ["x86_64", "aarch64"] +before-all = """yum install -y gcc-toolset-14 && \ + source /opt/rh/gcc-toolset-14/enable""" environment = { CC = "gcc", CXX = "g++" } manylinux-x86_64-image = "manylinux_2_28" manylinux-aarch64-image = "manylinux_2_28" diff --git a/tests/native_api_tests/CMakeLists.txt b/tests/native_api_tests/CMakeLists.txt index 0306b8611..4d436a7b1 100644 --- a/tests/native_api_tests/CMakeLists.txt +++ b/tests/native_api_tests/CMakeLists.txt @@ -31,4 +31,9 @@ target_compile_definitions( PRIVATE PGM_ENABLE_EXPERIMENTAL ) +set_target_properties( + power_grid_model_api_tests + PROPERTIES BUILD_RPATH $ +) + doctest_discover_tests(power_grid_model_api_tests)