Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e1f3606
Support xsimd batch scalars in the distance functions
zfergus Aug 31, 2026
cc767d2
Consolidate sqr/cubic and split math.hpp by dependency
zfergus Sep 3, 2026
0f7c726
Extend SIMD batch support beyond the distance functions
zfergus Sep 3, 2026
23c8fb2
Merge branch 'main' into feature/simd-distance
zfergus Sep 3, 2026
54e1aa8
Add a benchmark of the barrier potential per scalar type
zfergus Sep 3, 2026
d06ea9a
Implement SIMD handling for edge-edge mollification
zfergus Sep 4, 2026
16f3960
Add SIMD batch support to the normals and signed distances
zfergus Sep 4, 2026
faeb654
Consolidate the SIMD test helpers and cover the remaining batch funct…
zfergus Sep 4, 2026
36c061e
Align the SIMD benchmark's packed collision buffers
zfergus Sep 4, 2026
b356e55
Ignore __cmake_systeminformation
zfergus Sep 4, 2026
1191eff
Fix single bracker errors on Linux
zfergus Sep 5, 2026
67cc436
Add SIMD support to the closest point solves
zfergus Sep 5, 2026
c6f1c86
Template friction, adhesion, and angle on scalar
zfergus Sep 5, 2026
889509c
Fix ADL, singular solves, and literal narrowing
zfergus Sep 5, 2026
1cd5e94
Fix alignment issues with dynamic reshaped matrices
zfergus Sep 6, 2026
2033cd0
Narrow the warning suppressions and drop the Eigen abs workaround
zfergus Sep 6, 2026
22391bb
Configure MeshFEM_export.h instead of write
zfergus Sep 6, 2026
fec48ce
Document xsimd's public linkage in dependencies
zfergus Sep 6, 2026
b4be016
Color the xsimd dependency edge as public
zfergus Sep 6, 2026
3db577d
Show all default dependencies in the graph
zfergus Sep 6, 2026
647e189
Give the ortho router room to turn into igl_core
zfergus Sep 6, 2026
7b589c1
Add Benchmarks section to CLAUDE.md
zfergus Sep 6, 2026
f14e5a2
Test a singular lane beside well-conditioned ones
zfergus Sep 6, 2026
491a163
Hold random edge-edge coordinates to the derivative bound
zfergus Sep 6, 2026
3caa764
Instrument the tests in coverage builds
zfergus Sep 6, 2026
b49ca51
Ignore geninfo's mismatch error in coverage capture
zfergus Sep 6, 2026
d795223
Ignore the whole tests tree in codecov
zfergus Sep 6, 2026
ffed4a8
Test the closest-point Hessians and the runtime dimension dispatch
zfergus Sep 6, 2026
c1874d0
Remove coverage comments
zfergus Sep 6, 2026
00c8b7c
Instrument the tests from the test target, not via PUBLIC
zfergus Sep 6, 2026
8343e3b
Compute the 2x2 determinant with Kahan's FMA form
zfergus Sep 6, 2026
8a10c4d
Rename DETECTED_FMA to DETECTED_FMA3_X86
zfergus Sep 6, 2026
1c96cc0
Update the closest-point solve accuracy claim
zfergus Sep 6, 2026
0df5486
Move the std math forwarders to ipc::numext
zfergus Sep 6, 2026
93d09f7
Update release_notes.rst
zfergus Sep 6, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
run: |
cd build
ctest --verbose -j ${{ steps.cpu-cores.outputs.count }}
lcov --directory . --capture --output-file coverage.info --ignore-errors inconsistent,format,gcov
lcov --directory . --capture --output-file coverage.info --ignore-errors inconsistent,format,gcov,mismatch
lcov --remove coverage.info --ignore-errors unused '/usr/*' "$HOME/.cache/*" "*tests/*" --output-file coverage.info

- name: Upload coverage reports to Codecov
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -675,4 +675,5 @@ CMakeGraphVizOptions.cmake
.zed/*

.claude
graphify-out
graphify-out
__cmake_systeminformation
21 changes: 21 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,27 @@ cd build/test && ctest --verbose -R "test_name_pattern"
./build/test/ipc_toolkit_tests "[tag]"
```

### Benchmarks

Benchmarks are hidden Catch2 cases tagged `[!benchmark]`, so they don't run by
default — but a tag filter like `"[simd]"` *will* pull them in alongside the
unit tests. Exclude them explicitly when you only want correctness:

```bash
./build/test/ipc_toolkit_tests "[simd] ~[!benchmark]"
```

**Never take benchmark numbers from a `test`/`debug` build.** Those presets are
`CMAKE_BUILD_TYPE=Debug`, where the templated kernels aren't inlined and the
asserts are live, so the results are meaningless — and misleading, since the
SIMD paths lean hardest on inlining and lose the most. Build a Release
configuration with tests enabled and benchmark that instead:

```bash
cmake -S . -B build/benchmark -DCMAKE_BUILD_TYPE=Release -DIPC_TOOLKIT_BUILD_TESTS=ON
cmake --build build/benchmark -j 8
```

## Code Style

- **Formatter:** clang-format, WebKit-based style, **80-character column limit**. Pre-commit hooks enforce this — always run before committing:
Expand Down
11 changes: 8 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,16 @@ target_link_libraries(ipc_toolkit PRIVATE ipc::toolkit::warnings)

# SIMD support
if(IPC_TOOLKIT_WITH_SIMD)
# Add SIMD flags to compiler flags
target_compile_options(ipc_toolkit PRIVATE ${SIMD_CXX_FLAGS})
# Add SIMD flags to compiler flags.
# NOTE: PUBLIC because xsimd::default_arch is selected from each translation
# unit's own flags. ipc/utils/simd.hpp exposes batch types in the public API,
# and a consumer compiled without these flags would name a different type
# than the one instantiated in the library, failing to link.
target_compile_options(ipc_toolkit PUBLIC ${SIMD_CXX_FLAGS})

# Link against cross-platform xsimd library
include(xsimd)
target_link_libraries(ipc_toolkit PRIVATE xsimd::xsimd)
target_link_libraries(ipc_toolkit PUBLIC xsimd::xsimd)

# Disable vectorization in Eigen since I've found it to have alignment issues.
# NOTE: I don't know why this needs to be public, but it crashes if I make it private.
Expand Down Expand Up @@ -339,6 +343,7 @@ endif()

if(IPC_TOOLKIT_WITH_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
# Add required flags (GCC & LLVM/Clang)
# NOTE: tests/CMakeLists.txt repeats these for the test target; keep in sync.
target_compile_options(ipc_toolkit PRIVATE
-g # generate debug info
--coverage # sets all required flags
Expand Down
16 changes: 11 additions & 5 deletions cmake/find/FindSIMD.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,14 @@ function (test_sse_availability)

endfunction()

# This script checks for the highest level of FMA support on the host
# by compiling and running small C++ programs that uses FMA intrinsics.
# This script checks for x86 FMA3 support on the host by compiling and running
# a small C++ program that uses the AVX2 FMA intrinsics.
#
# NOTE: This probe is x86-only -- it needs `-mavx2 -mfma` and <immintrin.h>, so
# it cannot succeed on AArch64 and reports DETECTED_FMA3_X86 as failed there.
# That is not a missing capability: AArch64 has no opt-in FMA flag because
# fmadd/fmsub and the NEON vfmaq_* family are mandatory in the base ISA, so an
# empty FMA_FLAGS is the correct answer and NEON implies a fused multiply-add.

# If any FMA support is detected, the following variables are set:
#
Expand All @@ -194,7 +200,7 @@ endfunction()
function (test_fma_availability)
set(FMA_FLAGS)
set(FMA_FOUND)
set(DETECTED_FMA)
set(DETECTED_FMA3_X86)

include(CheckCXXSourceRuns)
set(CMAKE_REQUIRED_FLAGS)
Expand All @@ -219,12 +225,12 @@ function (test_fma_availability)

__m256d result = _mm256_fmsub_pd (a, b, c);
return 0;
}" DETECTED_FMA)
}" DETECTED_FMA3_X86)
endif()

set(CMAKE_REQUIRED_FLAGS)

if(DETECTED_FMA)
if(DETECTED_FMA3_X86)
SET(FMA_FOUND 1)
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
SET(FMA_FLAGS "${FMA_FLAGS} -mfma")
Expand Down
8 changes: 8 additions & 0 deletions cmake/ipc_toolkit/ipc_toolkit_warnings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ else()
-Wpointer-arith
-Wformat=2
-Wuninitialized
-Wno-maybe-uninitialized
-Wcast-qual
-Wmissing-noreturn
-Wmissing-format-attribute
Expand Down Expand Up @@ -179,6 +180,13 @@ else()
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND IPC_TOOLKIT_WARNING_FLAGS -Wnull-dereference)
endif()

# GCC 16 mis-analyzes TBB's enumerable_thread_specific. GCC <= 15 and Clang
# are clean, so only suppress it where it fires.
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16)
list(APPEND IPC_TOOLKIT_WARNING_FLAGS -Wno-array-bounds)
endif()
endif()

add_library(ipc_toolkit_warnings INTERFACE)
Expand Down
4 changes: 3 additions & 1 deletion cmake/recipes/meshfem_sparse.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ target_include_directories(MeshFEMSparse SYSTEM PUBLIC

# MeshFEMCore's headers include the CMake-generated <MeshFEM_export.h>. We
# build a static library, so the export macros are empty.
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/meshfem/exports/MeshFEM_export.h" [[
file(CONFIGURE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/meshfem/exports/MeshFEM_export.h"
CONTENT [[
#pragma once
#define MESHFEM_EXPORT
#define MESHFEM_NO_EXPORT
Expand Down
2 changes: 1 addition & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ coverage:
threshold: 5%
only_pulls: true
ignore:
- "tests/*"
- "tests/**"
41 changes: 26 additions & 15 deletions docs/source/_static/graphviz/dependencies.dot
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ digraph "IPC Toolkit Dependencies" {
bgcolor = "transparent";
splines = ortho;
layout = dot;
nodesep = 0.2;
// 0.28 rather than 0.2: with less separation the ortho router has no room
// to turn igl_predicates -> igl_core and hairpins it into igl_core's west
// side, putting the arrowhead on top of the predicates node.
nodesep = 0.28;
ranksep = 0.5;
node [fontname = "Menlo"; style = filled; penwidth = 2;];
edge [penwidth = 2; fontname = "Menlo";];
Expand All @@ -17,9 +20,13 @@ digraph "IPC Toolkit Dependencies" {
legendNode0 [label = "Static Library";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";];
legendNode1 [label = "Shared Library";shape = box;style = "rounded,filled";fillcolor = "#CCE7F8";color = "#6596B2";];
legendNode2 [label = "Interface Library";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";];
legendNode3 [label = "Optional Dependency";shape = box;style = "rounded,filled,dashed";fillcolor = "#F5F5F5";color = "#999999";fontcolor = "#555555";];
legendNode0 -> legendNode1 [label = "Public"; color = "#8FB976"; fontcolor = "#8FB976";];
legendNode2 -> legendNode0 [label = "Interface"; color = "#DAA52D"; fontcolor = "#DAA52D";];
legendNode1 -> legendNode2 [label = "Private"; color = "#BE6562"; fontcolor = "#BE6562";];
// Dashed marks a dependency behind an IPC_TOOLKIT_WITH_* CMake option,
// independent of the link scope the edge colour encodes.
legendNode2 -> legendNode3 [label = "Optional"; style = "dashed"; color = "#999999"; fontcolor = "#555555";];
}
// Force ipc_toolkit to top
subgraph {
Expand All @@ -40,18 +47,18 @@ digraph "IPC Toolkit Dependencies" {
"node5" [label = "ipc_toolkit\n(ipc::toolkit)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";];
"node5" -> "node0" [color = "#8FB976";];
// ipc_toolkit -> Eigen3_Eigen
"node5" -> "node1" [color = "#8FB976";];
// ipc_toolkit -> filib
"node5" -> "node1" [color = "#8FB976"; style = "dashed";];
// ipc_toolkit -> filib (IPC_TOOLKIT_WITH_FILIB)
"node5" -> "node2" [color = "#BE6562";];
// ipc_toolkit -> igl_core
"node5" -> "node3" [color = "#BE6562";];
// ipc_toolkit -> igl_predicates
"node15" [label = "xsimd\n(xsimd::xsimd)";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";];
"node5" -> "node15" [color = "#BE6562";];
// ipc_toolkit -> xsimd
"node5" -> "node15" [color = "#8FB976"; style = "dashed";];
// ipc_toolkit -> xsimd (IPC_TOOLKIT_WITH_SIMD)
"node6" [label = "robin_map\n(tsl::robin_map)";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";];
"node5" -> "node6" [color = "#BE6562";];
// ipc_toolkit -> robin_map
"node5" -> "node6" [color = "#BE6562"; style = "dashed";];
// ipc_toolkit -> robin_map (IPC_TOOLKIT_WITH_ROBIN_MAP)
"node7" [label = "scalable_ccd\n(scalable_ccd::scalable_ccd)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";];
"node7" -> "node0" [color = "#8FB976";];
// scalable_ccd -> Eigen3_Eigen
Expand All @@ -75,16 +82,20 @@ digraph "IPC Toolkit Dependencies" {
"node5" -> "node11" [color = "#BE6562";];
// ipc_toolkit -> tight_inclusion
"node12" [label = "absl_hash\n(absl::hash)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";];
"node5" -> "node12" [color = "#BE6562";];
// ipc_toolkit -> TinyAD
"node5" -> "node12" [color = "#BE6562"; style = "dashed";];
// ipc_toolkit -> absl_hash (IPC_TOOLKIT_WITH_ABSEIL)
"node13" [label = "TinyAD\n(TinyAD::TinyAD)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";];
"node5" -> "node13" [color = "#8FB976";];
// ipc_toolkit -> TinyAD
"node13" -> "node0" [color = "#BE6562";];
// TinyAD -> Eigen3_Eigen
"node13" -> "node9" [color = "#BE6562";];
// ipc_toolkit -> nlohmann_json
"node14" [label = "nlohmann_json\n(nlohmann_json::nlohmann_json)";shape = box;style = "rounded,filled";fillcolor = "#FFE6CC";color = "#DAA52D";];
"node5" -> "node14" [color = "#8FB976";];
// ipc_toolkit -> tracy
"node16" [label = "TracyClient\n(Tracy::TracyClient)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";];
"node5" -> "node16" [color = "#8FB976";];
// TinyAD -> tbb
"node17" [label = "MeshFEMSparse\n(MeshFEM::Sparse)";shape = box;style = "rounded,filled";fillcolor = "#D5E8D4";color = "#8FB976";];
"node5" -> "node17" [color = "#BE6562"; style = "dashed";];
// ipc_toolkit -> MeshFEMSparse (IPC_TOOLKIT_WITH_MESHFEM_SPARSE)
"node17" -> "node0" [color = "#8FB976";];
// MeshFEMSparse -> Eigen3_Eigen
"node17" -> "node9" [color = "#8FB976";];
// MeshFEMSparse -> tbb
}
Loading
Loading