Repository navigation
Conversation
- Define EIGEN_DONT_VECTORIZE only with IPC_TOOLKIT_WITH_SIMD again, as on main. Defining it unconditionally changed Eigen's object layout for every SIMD-off build (including the Linux Python wheels) and disagreed with MeshFEMSparse, which still defines it only with SIMD. - Remove the ESPCollisionTemplate static_assert that required Eigen vectorization to be off. The ESP primitives hold no Eigen members, and the ESP tests pass with vectorization on. - Default IPC_TOOLKIT_WITH_GEOGRAM to OFF, so the new dependency is opt-in. Without it, the distance-type classifiers use consistent floating-point predicates. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
tests-data's cube.ply is the same unit cube as the cube.obj this PR committed under tests/src/tests/potential/ (same vertices and triangles, only the vertex and face order differ), so load it with tests::load_mesh like the other tests and delete the copy. The old path reached it through DATA_DIR / "../src/...", which only worked with the default IPC_TOOLKIT_TESTS_DATA_DIR. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
ESP collisions encode their distance type in their class and assert it when evaluated (Vertex2-Edge2P1 only at P_E), so evaluating the set built at V at an FD stencil point across a switch aborted in Debug, with or without geogram (mesh_1: a P_E pair 3.8e-10 inside its region, FD step 1e-8). - Gradient: rebuild the collisions at each perturbed configuration. The potential is C1 across switches, so the FD may cross one. mesh_1 (880 DOFs) checks a directional derivative, as the 3D tests do, because a full FD with a rebuild per evaluation is too slow. - Hessian: keep the FD of the analytic gradient on the set built at V. Rebuilding does not work there: the potential is only C1 across a switch, so a central difference averages two one-sided Hessians. Following the GCP tests, the Hessian is checked only where no stored P_E pair is within the FD step of a switch. Rounding leaves pairs about 1e-17 from one in the squares (order 14) and mesh_2 (orders 7 and 14), so those sections check the gradient only. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
- Use the near/far split for every 0 < dbar_factor <= 2 (was (0, 1)). The far part of NearFarBarrier vanishes identically at 2, so the potential is continuous in dbar_factor, and dbar_factor = 1 is now a real split. The normalized unsplit branch is only reached at 0, where edge-edge terms are skipped, so the combined per-face PSD projection covers every case with indefinite weight-derivative terms. - Cap the edge-edge weight support at dhat: the mollifier and the edge-edge cutoff use min(dbar, dhat) (ESPParameters::ee_support()), also in the tangential collisions. - Keep edge-edge collision sets that cancel to empty, so their mollifier weight still counts in the face's weighted average; the potential jumped when a set changed between empty and non-empty. - ESPParameters throws std::invalid_argument for dbar_factor outside [0, 2] and warns at 0. Default dbar_factor 1.0 -> 0.2 (C++ and Python). - IPC_TOOLKIT_WITH_GEOGRAM defaults to ON again. - Tests: dbar_factor range, NearFarBarrier far part at alpha = 2, continuity in dbar_factor at 1 and 2, cancelled edge-edge sets; both Hessian PSD tests also run on two-cubes-close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
- ESPParameters::face_quad_rule is now private. Set it with set_quad_rule(), which throws std::invalid_argument for a negative or non-finite weight, or for weights that sum to zero. A negative weight makes that point's barrier term unbounded below as it approaches contact (and broke the PSD projection of the per-stencil Hessian branches). get_quad_rule() is unchanged. - Breaking: callers that assigned params.face_quad_rule must call params.set_quad_rule() instead. - Remove the quad_order 6/8 and 10-12 log messages from ESPParameters: they describe a downstream triangle-rule table, while here quad_order only selects the 2D Gauss-Lobatto rule, whose weights are positive. - Tests: rule validation; the 3D friction force-jacobian test uses set_quad_rule(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
- set_quad_rule() also throws std::invalid_argument for a negative or non-finite barycentric coordinate (point outside the face). Points whose coordinates do not sum to 1 (within 1e-10) only log a warning. - Remove the quad_order > 14 check from ESPParameters: quad_order n selects the (n + 1)-point Gauss-Lobatto rule, which is tabulated up to 19 points and computed beyond, and get_rule() already throws for n < 1 when the 2D rule is used. quad_order is unused in 3D. - Tests: coordinate checks and the warning; Gauss-Lobatto weights are positive and sum to 1 for orders 1-25. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
- Each rejected coordinate case now has exactly one bad coordinate, so +inf is tested on its own (it was previously rejected via -inf). - Capture the expected warning by swapping the logger's sinks, so it no longer prints to stdout. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
Drop the infinite-coordinate case and the warning-capture block; keep the negative and NaN coordinate cases. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #259 +/- ##
==========================================
+ Coverage 96.93% 97.08% +0.15%
==========================================
Files 194 221 +27
Lines 17349 22248 +4899
Branches 944 1540 +596
==========================================
+ Hits 16817 21600 +4783
- Misses 532 648 +116
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
- MSVC: the generic operator_nearfar, gradient_nearfar and hessian_nearfar were defined inline in the ESPCollisionTemplate class body, while the 3D versions are explicit specializations in the .cpp that the header does not declare. MSVC put the zero stubs in the vtable, so every ESP energy on the near/far path was 0.0 on Windows. Define the stubs out of line in the .cpp, as operator(), gradient() and hessian() already are. - clang-tidy: ESPCollisions::build takes const ESPParameters& (performance-unnecessary-value-param); the Python binding's overload_cast follows. - Debug timeouts: the Hessian PSD tests compute eigenvalues only on the DOFs with nonzero Hessian entries (the others are exact zeros). Convergent Quadrature Hessian PSD: 555 s -> 15 s locally in Debug. - The face quadrature tests looped over quad_order, which 3D ignores. They now set positive-weight face rules with set_quad_rule(); the PSD test covers dbar_factor 1, 0.4 and 0 (48 runs instead of 96; 1691 s -> 46 s locally in Debug). - Docs of ESPCollisions::build and the compute_minimum_distance docstring (squared distance) ride along in the touched files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
- Dependencies page: geogram row (IPC_TOOLKIT_WITH_GEOGRAM, BSD-3) and a note on the floating-point fallback; geogram node in dependencies.dot (dependencies.svg still needs regenerating). - Barrier API reference: InversePowerBarrier and NearFarBarrier pages. - NearFarBarrier: document alpha, the near/far split and the near/far methods. - ESP doc comments: ESPCollision and ESPCollisionDict no longer describe GCP, fixed vertex counts or finish_insertion(); the barrier, area_weights, use_near_far, normalize_weights and the remaining build/hessian parameters are documented; stale comments about quad_order, Gauss-Lobatto point counts and "HOP" are fixed. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
The unsquared branches of operator(), gradient() and hessian() returned the barrier without the stiffness factor, which upstream moved into BarrierPotential. They now scale by stiffness() as the squared branches do. The test runs with stiffness 1 and 3.5, with and without the physical barrier, and checks that force_magnitude_gradient() throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
The continuous overload accepted all_types but never passed it to the broad phase. Only the discrete overload, which ESP uses, needs it. The signature and its Python binding match upstream again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
Fixes: - The ESP TangentialCollisions::build never reset the lagged friction coefficients, which default to 0, so ESP friction was identically zero. It now calls reset_lagged_anisotropic_friction_coefficients() like the other overloads, and the friction test requires a nonzero force. - ESPPotential::hessian returned a 0x0 matrix on a 3D mesh without faces. - ESPCollisions::to_string read the face quadrature point, a virtual vertex, out of bounds. Face collisions now print ids and weights only. Dead code (each removal verified against its callers): - Edge-edge guards for non-EA_EB types and shared vertices, which the builder already filters out, become asserts. The ESP edge-edge distance helpers keep only the EA_EB case. - Zero-weight fallbacks of the near/far normalization, the dbar_factor = 0 edge-edge terms (the edge-edge pairs are skipped there), skips that ee_set/ef_set already guarantee, the obstacle-edge candidate check that was always true, the deep copies of the TBB exemplar and the unused copy assignment of QuadratureCollisionsBuilder, the uninstantiated Edge3P1-Edge3P1 distance and the ESPCollision near/far default bodies (now pure virtual). - The 20-point Gauss-Lobatto rule now uses its table. Tests: ESP friction with edge-edge contact, two cubes and zero stiffness; obstacle integration types with a face rule, in 2D and with dynamic vertex-vertex candidates; dbar_factor = 0; a 3D mesh without faces; 2D Hessian projection; an edge-midpoint face rule; collision accessors and stencils; distance helpers; candidate adjacency sets; and error paths. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
Rename the ESPParameters constructor parameter dbar_factor_value, which shadowed the member of the same name, to _dbar_factor like the other parameters, and use static_cast in ESPCollisionTemplate::vertex_id. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
Remove PointPotential::build_collisions_at_face_center and the gradient and Hessian at the face center, with and without the near/far split. Nothing in the library or the Python bindings called them; ESPPotential uses the face-interior-point versions. The test that only checked them against each other goes with them. The two value functions stay: they evaluate the potential at every face quadrature point. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
evaluate_potential_at_face_center_with_cached_collisions and its _nearfar version evaluate at any face quadrature point, not only the center. They now match the gradient, Hessian and build functions, which already use face_interior_point. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
fsichetti
marked this pull request as ready for review
October 9, 2026 15:53
The PSD check ran inside "ESP potential 2D finite differences" for every configuration, including the 880-DOF mesh, and pushed that test past the 1500 s CTest limit in the Windows Debug job (it passed in 1115 s before). It now runs on the two small configurations, in under a second. Claude-Session: https://claude.ai/code/session_01R7sJzFKyZAgyK7X4Vs6mXf
2 of 10 tasks
3 of 11 tasks
The test timed out (1500 s) in the Windows Debug job in 4 of the last 5 runs; Linux and macOS Debug take about 850 s. Orders 1 and 14 keep the two extremes (endpoints only, and the densest rule with points near the endpoints); orders 2 and 7 stay covered by the 2D no-forces and Hessian PSD tests. The test takes 59% of its previous time in a local Debug build.
These new tests ran several times over one code path and made the Debug CI jobs three times longer than on main. - ESP potential 2D no forces: quadrature orders 1 and 14, and circles up to 200 vertices. The 2000-vertex circle took 97% of the test. - Convergent Quadrature Gradient and Hessian: dbar_factor 0.3, 1.0 and 2.0. dhat = 0.15 / min(dbar_factor, 1) keeps the edge-edge support at 0.15; dividing by dbar_factor left no collisions at 2.0. - Face Quadrature Gradient and Hessian: drop the interior-degree-2 rule, whose interior points the centroid of vertices+centroid already covers. - Convergent Quadrature Hessian PSD: dbar_factor 0.4 only, since every value in (0, 2] takes the same projection path. - Face Quadrature Hessian PSD: one rule, and dbar_factor 0.4 and 0. Zero is the per-stencil projection branch. - Random distance-type tests: 10,000 samples instead of 100,000. The rarest type (edge-edge vertex-vertex, 2.5%) still appears about 250 times per run.
- Prefix the non-public members of ESPPotential, ArbitraryPointESP, ArbitraryPointBVH, ESPCollisionDict and ESPCollisionTemplate with m_. - Rename the getters ESPParameters::get_quad_rule() to quad_rule() and ESPPotential::get_use_near_far() to use_near_far(). - Make ESPParameters' dbar_factor private behind dbar_factor(). - closest_point_uv() takes ea0, ea1, eb0 and eb1. - Document the ESPPotential and ESPCollisions constructors and maps(). - Remove the unused Vertex2ogc primitive, and the minimum-distance tracker that every collision evaluation updated with an atomic compare-and-swap but nothing read. - Cast the six vertex-count asserts that MSVC flagged as signed/unsigned comparisons (C4389).
- Geogram compiles its sources with -Weverything on clang, which added about 3,000 warnings to the macOS builds. Build it with -w. - Clear the link directory geogram adds from its source and relative build dirs. Under CPM that path does not exist, and the macOS linker warned about it.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Implementation of the SigAsia paper "Extremum Sum Proximity Potential".
Type of change
Please delete options that are not relevant.
How Has This Been Tested?
Built and ran the test suite locally on macOS/arm64.
This branch adds the following tests, all passing locally:
New test files
tests/src/tests/potential/test_esp_potential.cpp(19 cases,[esp_potential])ESP potential in 2D and 3D: convergent-quadrature edge-edge limits under
three barriers, gradient/Hessian finite differences, Hessian PSD-ness,
codimensional collisions, adaptive support, NearFarBarrier decomposition.
tests/src/tests/potential/test_arbitrary_point_esp.cpp(6 cases,[arbitrary_point_esp]) Evaluating the ESP at an arbitrary off-mesh pointin 2D/3D: zero beyond dhat, FD gradient/Hessian, and that
evaluate()agrees with
operator()/gradient()/hessian().tests/src/tests/potential/test_smooth_clamp.cpp(12 cases,[smooth_clamp])smooth_clamp01andsmooth_clamp_simplex: anchorvalues, saturation, monotonicity, C0/C1 continuity at knots, FD-vs-analytic
derivatives, and simplex partition-of-unity.
tests/src/tests/utils/test_vertex_matrix_view.cpp(5 cases,[vertex_matrix_view]) Non-owning two-matrix vertex view: concatenationagainst a naive vstack, aliasing semantics, empty-matrix edge case.
New cases in existing files
test_distance_type.cpp: four randomized tests comparing the analyticdistance-type classifiers against a geogram exact-predicate reference
(new
distance_type_reference.hpp), including the near-parallel edge-edge case.test_force_jacobian.cpp: ESP friction force-Jacobian in 2D and 3D(
[friction-esp]).test_barrier.cpp: log barrier derivatives.Test Configuration:
IPC_TOOLKIT_WITH_GEOGRAM=ON,IPC_TOOLKIT_WITH_CUDA=OFFChecklist