Highlights
- LBVH is now the default broad phase, replacing HashGrid. Combined with this release's LBVH optimizations, it is roughly 2× faster than the (now-removed) SimpleBVH and, because HashGrid scales poorly, several-fold faster (≈6–8× on large benchmark scenes) than the previous default (#212).
- Add analytic plane-vertex collisions (#216).
- Add anisotropic friction by @antoinebou12 (#210).
- Add barrier stiffness (
$\kappa$ ) toipc::BarrierPotentialand simplify the tangential API (#215). - Add composable collision filters (#235).
Broad Phase
-
Set the default broad phase to
ipc::LBVH(#212).- LBVH outperforms the previous default (HashGrid) and all other methods across a range of scenes. On the benchmark scenes below it is ~1.5× faster than SimpleBVH (the fastest existing method) at baseline; with this release's pruning and bottom-up-build optimizations that grows to roughly 2× faster than SimpleBVH and ~6–8× faster than HashGrid.

Total broad-phase performance across methods; LBVH is roughly 1.5× faster than the fastest existing method. Benchmarked on an Apple M2 Max (12 cores). -
Remove the SimpleBVH dependency and the deprecated
BVHbroad phase (#213).
LBVH construction is more than 3× faster than the removed BVH method. Benchmarked on an Apple M2 Max (12 cores). -
Add rightmost-leaf pruning to LBVH self-collision traversal, skipping subtrees fully left of the query. 39% average speed-up in edge-edge traversal across benchmark scenes (#222).
- Also fixes three bugs in the OGC edge-edge feasibility check.
-
Optimize LBVH construction with a single bottom-up pass [Apetrei 2014], building the hierarchy and bounding boxes simultaneously instead of the two-pass build of [Karras 2012]. Up to 10% faster to build (#230).

Bottom-up [Apetrei 2014] vs. two-pass [Karras 2012] LBVH construction, benchmarked on an Apple M3 Pro (11 cores). -
Refactor the AABB, HashGrid, and LBVH parallel loops to use
tbb::parallel_forwith index ranges directly (#228).
New Features 🚀
-
💥 [Breaking] Add barrier stiffness and simplify the tangential API (#215).
- Add a barrier stiffness
$\kappa$ toipc::BarrierPotential: new constructors, member, and getter/setter, scaling the potential, gradient, and Hessian by$\kappa$ . - Remove the redundant
normal_stiffnessparameter from the tangential collision constructors andipc::TangentialPotentialinterfaces, updating all call sites and Python bindings.
- Add a barrier stiffness
-
Add analytic plane-vertex collision support (#216).
- Add
ipc::PlaneVertexCandidateand normal and tangential plane-vertex collisions, integrated into the collision builders. - Add
ipc::CollisionMesh::planes(a list ofEigen::Hyperplane<double, 3>) to represent infinite analytic planes such as a ground plane, with Python bindings. - Remove the old
implicitsmodule.
- Add
-
Add anisotropic friction by @antoinebou12 (#210).
- Per-contact tangent-space velocity scaling and optional [Erleben et al. 2019] "matchstick" direction-dependent static/kinetic coefficients.
- Direction-dependent coefficients are lagged: refresh with
ipc::TangentialCollisions::update_lagged_anisotropic_friction_coefficientsafterbuildand whenever the lagged state changes. - Default behavior remains isotropic. The directional model is active only in the 2D tangent space of 3D simulations.
- Tutorial available here.
-
Implement the Gauss-Newton preconditioner from [Shen et al. 2024] (#221).
- Add
ipc::CollisionStencildistance-vector utilities (compute_distance_vector,compute_distance_vector_jacobian, and diagonal/Jacobian-contraction helpers). - Add cumulative
ipc::NormalPotentialGauss-Newton routines (diagonal and quadratic form), parallelized with TBB. - Exposed in the Python bindings with unit tests.
- Add
-
Add the Planar Divide-and-Truncate (Planar-DAT) trust-region filter for OGC [Chen et al. 2026] (#228).
-
ipc::ogc::TrustRegion::planar_filter_stepis a direction-aware alternative to isotropic filtering: it computes a division plane per collision pair and truncates only motion toward the opposing primitive, reducing artificial damping and deadlock in dense-contact scenarios. - Available in both C++ and Python.
- Tutorial available here.
-
-
Add composable collision filters (#235).
- New
ipc::CollisionFilter(C++ and Python) wraps anybool(int, int)callable and composes via|(union),&(intersection), and!(negation). - Factory functions for common cases:
make_vertex_patches_filter,make_static_obstacle_filter,make_codim_cross_filter, andmake_connected_components_filter. - The FAQ is rewritten to document the new system with C++ and Python examples.
- New
-
Add support for nonmanifold smooth edges (#223).
- Generalize the
Edge3primitive andsmooth_edge3_term(and its derivatives) to an arbitrary number of adjacent faces. - Change
edges_to_facesfrom a fixed-size matrix to a vector of vectors and increaseN_EDGE_NEIGHBORS_3Dfrom 4 to 6.
- Generalize the
API Changes 🔧
-
Configurable derivative layout (#217).
- Add a
VERTEX_DERIVATIVE_LAYOUTconstant toipc/config.hppand parameterize the gradient, sparse-gradient, Hessian-triplet, and Jacobian-triplet assembly helpers with an optional row- or column-major global ordering (defaulting toVERTEX_DERIVATIVE_LAYOUT).
- Add a
-
💥 [Breaking] Update the local 3rd-order tensor Jacobian layout (#219).
- Store 3rd-order tensors as matrices following the convention in "Dynamic Deformables" [Kim and Eberle 2022], easing tensor contractions in the chain rule.
- Rename the relative-velocity API from
*_matrix/*_matrix_jacobianto*_jacobian/*_dx_dbetaacross C++, Python, and documentation.
-
💥 [Breaking] Refactor the nonlinear CCD into a
ipc::NonlinearCCDclass (#218).- Encapsulate the point-point, point-edge, edge-edge, and point-triangle nonlinear CCD methods, replacing the previous free-function API.
- Update signatures to take
Eigen::ConstRefand adjust the conservative-rescaling parameter handling.
Bug Fixes 🐛
- Use a relative
PARALLEL_THRESHOLDinedge_edge_distance_typeto correctly classify nearly-collinear coplanar edges, and add defensive guards for mollified collisions at$d=0$ ; adds a regression test (#225). - Fix a 2D GCP bug caused by a trivially loose
Edge2active check by @udaykusupati (#227). - Skip the edge-edge planar filter for nearly-parallel edges with negligible approach velocity to avoid spurious truncation (#232).
- Fix MSVC duplicate-symbol errors for
PrimitiveDistanceby adding explicit specialization declarations (#237). - Fix two bugs in the mollified edge-edge shape derivative (a wrong gradient factor and a missing outer-product term) by @Huangzizhou (#239).
Profiling ⏱️
- Add fine-grained profiling instrumentation throughout, recording only on the main thread (#233).
- Add an optional Tracy frame profiler via the
IPC_TOOLKIT_WITH_TRACYCMake option (#234). - Record profiler data on the TBB arena coordinator thread rather than by main-thread ID (#236).
Python 🐍
- Allow the thread limit to be set globally via the
TBB_NUM_THREADSenvironment variable, applied on import ofipctk(#242). - Add the
IPCTK_WITH_SIMDenvironment variable to disable SIMD in Python builds (#231).
Miscellaneous
- Replace the
maybe_parallel_forwrapper with directtbb::parallel_forandtbb::enumerable_thread_specific(#214). - Clean up the closest-point auto-generated code (#220).
- Update GitHub Actions to the latest major versions (#224).
- Disable pedantic and unneeded MSVC compiler warnings.
- Strip notebook outputs and add an
nbstripoutpre-commit hook (#240). - Updated dependencies:
- Bump finite-diff from
v1.0.3tov1.0.4.
- Bump finite-diff from
New Contributors
- @udaykusupati made their first contribution in #227.
Full Changelog: v1.5.0...v1.6.0