Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think enable it by default is logical. Disable by default and only enable in the our dev preset is logical.

In the Python build we do not enable it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardening is intended to be used in production

Copy link
Member

@TonyXiang8787 TonyXiang8787 Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardened code self (so the adjustment to make compilation to pass with hardening check flag) is intended to be used in production. The compilation flags to check hardening are not intended to be used in production.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the GCC command line options (https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fhardened)

This option is intended to be used in production builds, not merely in debug builds.

See also https://www.youtube.com/watch?v=GtYD-AIXBHk&list=PLHTh1InhhwT57vblPGsVag5MkTm_Z9-uq&index=2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still has huge doubt for the default. This goes against the other golden rule of open-source cmake project: make as little as needed for extra compile and link flag in the default cmake build.

Also, enabling address sanitizing for production is questionable.

We need more in-depth consideration and discussion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also let CIBW set the compiler flags for hardening


set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand Down
5 changes: 4 additions & 1 deletion CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -140,6 +140,9 @@
{
"name": "msvc-debug",
"displayName": "Debug (MSVC)",
"environment": {
"SANITIZER_FLAGS": "/RTC1"
},
Comment on lines +143 to +145
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we move this also to the CMakeLists?

"inherits": [
"msvc-base",
"debug"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,15 @@ class ColumnarAttributeRange : public std::ranges::view_interface<ColumnarAttrib
auto const& meta_attribute = *attribute_buffer.meta_attribute;
ctype_func_selector(
meta_attribute.ctype, [&value, &attribute_buffer, &meta_attribute, this]<typename AttributeType> {
AttributeType* buffer_ptr = reinterpret_cast<AttributeType*>(attribute_buffer.data) + idx_;
auto const& attribute_ref = meta_attribute.template get_attribute<AttributeType const>(
reinterpret_cast<RawDataConstPtr>(&value));
*buffer_ptr = attribute_ref;
if constexpr (sizeof(AttributeType) <= sizeof(value_type)) {
AttributeType* buffer_ptr = reinterpret_cast<AttributeType*>(attribute_buffer.data) + idx_;
auto const& attribute_ref = meta_attribute.template get_attribute<AttributeType const>(
reinterpret_cast<RawDataConstPtr>(&value));
*buffer_ptr = attribute_ref;
} else {
throw UnreachableHit{"ColumnarAttributeRange::operator=",
"AttributeType larger than value_type!"};
}
});
}
return *this;
Expand All @@ -97,14 +102,18 @@ class ColumnarAttributeRange : public std::ranges::view_interface<ColumnarAttrib
for (auto const& attribute_buffer : attribute_buffers_) {
assert(attribute_buffer.meta_attribute != nullptr);
auto const& meta_attribute = *attribute_buffer.meta_attribute;
ctype_func_selector(
meta_attribute.ctype, [&result, &attribute_buffer, &meta_attribute, this]<typename AttributeType> {
ctype_func_selector(meta_attribute.ctype, [&result, &attribute_buffer, &meta_attribute,
idx = idx_]<typename AttributeType> {
if constexpr (sizeof(AttributeType) <= sizeof(value_type)) {
AttributeType const* buffer_ptr =
reinterpret_cast<AttributeType const*>(attribute_buffer.data) + idx_;
auto& attribute_ref =
reinterpret_cast<AttributeType const*>(attribute_buffer.data) + idx;
AttributeType& attribute_ref =
meta_attribute.template get_attribute<AttributeType>(reinterpret_cast<RawDataPtr>(&result));
attribute_ref = *buffer_ptr;
});
} else {
throw UnreachableHit{"ColumnarAttributeRange::get", "AttributeType larger than value_type!"};
}
});
}
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ template <class T> using EigenDiagonalTensor3 = Eigen::DiagonalMatrix<T, 3>;

template <scalar_value T> class Vector : public EigenVector3<T> {
public:
Vector() { (*this) = EigenVector3<T>::Zero(); };
Vector() { this->setConstant(T{}); };
// eigen expression
template <typename OtherDerived> Vector(Eigen::ArrayBase<OtherDerived> const& other) : EigenVector3<T>{other} {}
template <typename OtherDerived> Vector& operator=(Eigen::ArrayBase<OtherDerived> const& other) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ template <symmetry_tag sym> class YBus {
std::shared_ptr<MathModelParam<sym> const> const& param,
std::shared_ptr<YBusStructure const> 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;
Expand Down Expand Up @@ -447,36 +450,40 @@ template <symmetry_tag sym> class YBus {
template <typename T>
requires std::same_as<T, BranchSolverOutput<sym>> || std::same_as<T, BranchShortCircuitSolverOutput<sym>>
std::vector<T> calculate_branch_flow(ComplexValueVector<sym> const& u) const {
std::vector<T> 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<sym> const& param) {
auto const [f, t] = branch_idx;
// if one side is disconnected, use zero voltage at that side
ComplexValue<sym> const uf = f != -1 ? u[f] : ComplexValue<sym>{0.0};
ComplexValue<sym> const ut = t != -1 ? u[t] : ComplexValue<sym>{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<T, BranchSolverOutput<sym>>) {
// 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<sym> const uf = f != -1 ? u[f] : ComplexValue<sym>{0.0};
ComplexValue<sym> const ut = t != -1 ? u[t] : ComplexValue<sym>{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<T, BranchSolverOutput<sym>>) {
// 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<std::vector<T>>();
}

// calculate shunt flow based on voltage, injection direction
template <typename SolverOutputType>
requires std::same_as<SolverOutputType, ApplianceSolverOutput<sym>> ||
std::same_as<SolverOutputType, ApplianceShortCircuitSolverOutput<sym>>
std::vector<SolverOutputType> calculate_shunt_flow(ComplexValueVector<sym> const& u) const {
assert(math_topology_ != nullptr);

std::vector<SolverOutputType> shunt_flow(math_topology_->n_shunt());
for (auto const [bus, shunts] : enumerated_zip_sequence(math_topology_->shunts_per_bus)) {
for (Idx const shunt : shunts) {
Expand Down
27 changes: 27 additions & 0 deletions power_grid_model_c/power_grid_model_c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,33 @@ set_target_properties(
INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE
)

if(PGM_ENABLE_HARDENING)
target_compile_definitions(
power_grid_model_c
PRIVATE
"$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:_FORTIFY_SOURCE=2>"
"$<$<CXX_COMPILER_ID:MSVC>:_ITERATOR_DEBUG_LEVEL=$<IF:$<CONFIG:DEBUG>,2,1>>"
)
if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
set(_HAS_ADDRESS_SANITIZER
"$<NOT:$<OR:$<IN_LIST:address,${CMAKE_CFLAGS}>,$<IN_LIST:address,${CMAKE_CXXFLAGS}>>>"
)
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
"$<$<OR:$<CXX_COMPILER_ID:GNU>,$<CXX_COMPILER_ID:Clang>>:-fstack-protector;-fsanitize=undefined;-fno-sanitize-recover=undefined>"
"$<$<CXX_COMPILER_ID:GNU>:-static-libubsan>"
"$<$<CXX_COMPILER_ID:Clang>:-lubsan>"
)
endif()

install(
TARGETS power_grid_model_c
EXPORT power_grid_modelTargets
Expand Down
2 changes: 1 addition & 1 deletion power_grid_model_c/power_grid_model_c/src/dataset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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"""
Comment on lines +213 to +214
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be needed if we disable build hardening by default.

environment = { CC = "gcc", CXX = "g++" }
manylinux-x86_64-image = "manylinux_2_28"
manylinux-aarch64-image = "manylinux_2_28"
Expand Down
5 changes: 5 additions & 0 deletions tests/native_api_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ target_compile_definitions(
PRIVATE PGM_ENABLE_EXPERIMENTAL
)

set_target_properties(
power_grid_model_api_tests
PROPERTIES BUILD_RPATH $<TARGET_FILE_DIR:power_grid_model_c>
)

Comment on lines +34 to +38
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to be removed

doctest_discover_tests(power_grid_model_api_tests)
Loading