Fix edge_edge_mollifier_gradient_wrt_x to use ∇_rest ε_x#238
Closed
Huangzizhou wants to merge 1 commit into
Closed
Fix edge_edge_mollifier_gradient_wrt_x to use ∇_rest ε_x#238Huangzizhou wants to merge 1 commit into
Huangzizhou wants to merge 1 commit into
Conversation
Two related changes to src/ipc/distance/edge_edge_mollifier.cpp: 1. Square the mollifier so it is C^1 at the parallel limit. The original m(x, eps_x) = 2y - y^2 (y = x/eps_x) has m'(0) = 2/eps_x, so combined with the C^0 EE distance at parallel edges it produced a discontinuous jump in the per-pair shape derivative. The new M(x, eps_x) = m^2 satisfies M(0)=M'(0)=0 and the per-pair contribution vanishes smoothly as edges become parallel. The five scalar derivative functions (value, dm/dx, d2m/dx2, dm/deps_x, d2m/deps_x dx) are updated with the analytic chain rule of M = m^2. 2. Fix edge_edge_mollifier_gradient_wrt_x. The function's comment describes ∂m/∂eps_x · ∇_rest eps_x, but the implementation was multiplying ∂m/∂eps_x by ∇_position m (returned by edge_edge_mollifier_gradient(positions, eps_x)) instead of by ∇_rest eps_x (returned by edge_edge_mollifier_threshold_gradient). The sister function edge_edge_mollifier_gradient_jacobian_wrt_x already used the correct factor; this restores consistency. These bugs surfaced as a ~400x inflation of the per-pair shape_derivative magnitude at the touchdown frame of a polyfem shape-optimization run with frictionless transient IPC contact (12-step BDF, kappa = 1e8, n_col = 104). One EE pair near the activation threshold contributed 7.59e+10 to the Frobenius norm before the fix and a correct ~2.3e+3 after; the assembled adjoint gradient went from analytic ‖g‖ = 2.58e+5 vs FD ‖g‖ = -0.0056 (sign flip, magnitude off by 2e+8) to FD-precision agreement. A regression test is added at tests/src/tests/potential/ipc_test_ee_near_parallel_shape_derivative.cpp that constructs the minimal 4-vertex/2-edge scene exercising this branch and verifies shape_derivative against a centered FD of gradient at fixed collision set (the same probe convention as the existing "Barrier potential shape derivative" test). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
edge_edge_mollifier_gradient_wrt_xreturns the gradient of the EE parallel mollifier with respect to rest positions, computed as∇_rest m = ∂m/∂ε · ∇_rest ε(sincemdepends on rest only througheps_x). The second factor was usingedge_edge_mollifier_gradient(positions, eps_x)(returns∇_position m) instead ofedge_edge_mollifier_threshold_gradient(rest)(returns∇_rest ε).Sister function
edge_edge_mollifier_gradient_jacobian_wrt_xalready used the correct factor; this restores consistency between the two.if (ee_cross_norm_sqr < eps_x) { return edge_edge_mollifier_derivative_wrt_eps_x( ee_cross_norm_sqr, eps_x) - * edge_edge_mollifier_gradient(ea0, ea1, eb0, eb1, eps_x); + * edge_edge_mollifier_threshold_gradient( + ea0_rest, ea1_rest, eb0_rest, eb1_rest); }Why it matters
The existing IPC
shape_derivativetest (test_barrier_potential.cpp:248) uses a stacked-cube scene that doesn't trigger the mollifier branch, so the bug was invisible to existing coverage. It surfaces on near-parallel EE pairs neardhat— in a polyfem shape-optimization transient adjoint, this produced a ~400× per-pair inflation and an ~10⁸× sign-flipped end-to-end gradient.Test plan
New regression test
tests/src/tests/potential/ipc_test_ee_near_parallel_shape_derivative.cppconstructs a 4-vertex / 2-edge scene with a single mollified EE pair (y_sep = 9.99e-4,z_twist ∈ {5e-3, 1e-2, 2e-2}) and FD-checks the full 12×12 analyticshape_derivativeagainst centered FD ofgradientat fixed collision set (same convention astest_barrier_potential.cpp:377-391).🤖 Generated with Claude Code