Skip to content

Fix edge_edge_mollifier_gradient_wrt_x to use ∇_rest ε_x#238

Closed
Huangzizhou wants to merge 1 commit into
ipc-sim:mainfrom
Huangzizhou:fix/ee-mollifier-shape-derivative
Closed

Fix edge_edge_mollifier_gradient_wrt_x to use ∇_rest ε_x#238
Huangzizhou wants to merge 1 commit into
ipc-sim:mainfrom
Huangzizhou:fix/ee-mollifier-shape-derivative

Conversation

@Huangzizhou
Copy link
Copy Markdown
Collaborator

@Huangzizhou Huangzizhou commented Jun 6, 2026

Summary

edge_edge_mollifier_gradient_wrt_x returns the gradient of the EE parallel mollifier with respect to rest positions, computed as ∇_rest m = ∂m/∂ε · ∇_rest ε (since m depends on rest only through eps_x). The second factor was using edge_edge_mollifier_gradient(positions, eps_x) (returns ∇_position m) instead of edge_edge_mollifier_threshold_gradient(rest) (returns ∇_rest ε).

Sister function edge_edge_mollifier_gradient_jacobian_wrt_x already 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_derivative test (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 near dhat — 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.cpp constructs 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 analytic shape_derivative against centered FD of gradient at fixed collision set (same convention as test_barrier_potential.cpp:377-391).

  • Without the fix: rel ≈ 1.4–2.6×10⁻² (test FAILS).
  • With the fix: rel ≈ 3–5×10⁻⁴ (test PASSES). Threshold is 5×10⁻³.

🤖 Generated with Claude Code

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>
@Huangzizhou Huangzizhou marked this pull request as draft June 6, 2026 20:04
@Huangzizhou Huangzizhou closed this Jun 6, 2026
@Huangzizhou Huangzizhou changed the title Fix EE shape_derivative on near-parallel mollified pairs Fix edge_edge_mollifier_gradient_wrt_x to use ∇_rest ε_x Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant