Analysis behind the restitution-decoupling PR (#14) and the stacked per-manifold prototype (branch dylanturpin/newton-collab:draft/pr14-manifold-restitution). Everything quantitative here was measured (RTX 3090, warp 1.15 dev); nothing is asserted from reading code alone unless marked.
The one-video version
rigid_contact_restitution_iterations = 1 / 2 / 8 / 32; sphere (blue), cylinder (orange), box (green). Top row — current scheme: flat shapes barely bounce at practical iteration counts; only the 32-iteration column recovers them, at the cost quantified in §3. Bottom row — per-manifold solve: ~96% rebound for every shape, identical in every column.Enabling restitution on SolverXPBD used to switch every body in the scene to position-delta-derived velocity integration — trajectories changed even with no contacts anywhere (a contact-free pendulum went 86× out of tolerance; that finding is what the upstream #2602 deprecation work ran into). Eric's #14 cuts that wire: bodies always stay on Newton's public (v_com, ω) convention, and restitution becomes a velocity-level contact pass after the positional solve.
Verified on the branch: contact-free trajectories are bit-identical with restitution on/off (the PR's invariance test asserts exact array equality and passes); the full xpbd test file is green with the review fixes applied — including six parent_f Newton's-second-law tests that were failing because of the old coupling and pass with #14; the restitution-off path costs nothing extra (§5); and the margin-gate failure mode — a positional correction applied while restitution is gated out — is structurally impossible in the new scheme: the active-contact recording is a superset of every contact the positional solver corrects.
Rebound recovery vs restitution iterations, by shape
e=1.0 body dropped 0.30 m onto a restitutive surface; recovery = post-impact rebound peak ÷ release height; the elastic regression test asserts ≥ 80%.
Each contact sizes its impulse with its full effective mass K = m⁻¹ + (r×n)ᵀI⁻¹(r×n) — linear plus lever-arm response. On a symmetric manifold the angular components of the summed impulses oppose and cancel, so after the per-body average only the linear fraction lands: residual ratio 1 − m⁻¹/K per iteration. Instrumented and matched exactly: box predicted 0.8276, measured 0.8276 at every iteration; sphere (r ∥ n, no angular term) converges in one. Two aggravators: whatever a substep fails to converge is permanently lost (the next substep re-anchors its target), and asymmetric rim manifolds don't fully cancel — they inject spurious spin instead.
Solver cost vs restitution iterations
1,024-body mixed contact scene, XPBD, 10 substeps @ 100 fps, CUDA-graph-captured, 200 timed frames, key configs ×3.
Stop treating a box's four corners as four independent problems. Restitution-eligible contacts are grouped by body pair (a fixed-size open-addressing hash table fused into the existing recording kernel — the contact stream is not pair-contiguous, and a radix-sort variant measured too slow). One GPU thread per manifold then runs a small Gauss–Seidel solve: 8 fixed inner sweeps over ≤8 contacts held in registers, accumulated impulses clamped ≥ 0, full linear+angular response per contact, contact order canonicalized. The outer iteration loop survives only as cross-manifold coupling (stacks/chains), and the inner sweeps are register-resident — effectively free, so there is nothing to tune.
| Check | Result |
|---|---|
| Rebound at the default (2 outer iterations) | ✓ sphere 95.6%, cylinder 95.6%, box 95.6% (was 96 / 17 / 23) — identical at outer = 1, 2, 4, 8 |
| Cost (same 1,024-body scene) | ✓ 1.30 vs 1.23 ms/frame — correct flat-shape restitution for ~+6% |
| Full xpbd test file | ✓ 62/62 green, CPU and CUDA, including the bit-exact invariance test |
| Rocking (10° tilted box, e=0.5) | ✓ no energy gain — the impulse clamp holds on asymmetric impacts |
| Momentum (head-on equal masses, e=1, zero g) | ✓ error exactly 0 |
Restitution is off (or every coefficient is zero) in the default and benchmark configurations, so the bar is: no cost there. Measured three-way, same session (ms/frame):
| Path | pre-#14 | #14 | +manifold | Reading |
|---|---|---|---|---|
Literal enable_restitution=False, 1,024-body scene | 1.02 | 1.00 | 1.03 | deltas ≤2%, ranges overlap — noise |
True + all-zero coefficients | 1.16 | 1.05 | 1.04 | ~9% faster: the old path ran the velocity rewrite + restitution kernel even at e=0 |
| Pyramid contact example (4,200 boxes, the ASV scene) | 4.21 | 3.87 | 3.85 | ~8% faster on the representative benchmark workload |
Expect XPBD benchmarks to step down ~5–10% where call sites pass enable_restitution=True with zero coefficients, and not to move where restitution is off entirely.
rigid_contact_restitution_iterations stops being a knob anyone needs to think about.newton-physics/newton as the #2602 resolution after the manifold review settles — flagged here, deliberately not scheduled yet.