Skip to content

Add explicit rigid contact kinematics - #3803

Open
eric-heiden wants to merge 2 commits into
newton-physics:mainfrom
eric-heiden:eric/rigid-contact-kinematics
Open

Add explicit rigid contact kinematics#3803
eric-heiden wants to merge 2 commits into
newton-physics:mainfrom
eric-heiden:eric/rigid-contact-kinematics

Conversation

@eric-heiden

@eric-heiden eric-heiden commented Aug 5, 2026

Copy link
Copy Markdown
Member

Description

Add newton.eval_rigid_contact_kinematics() so callers can provide only the rigid-contact distance and world-point arrays they need. The simulation-layer placement and eval_* name match related APIs such as newton.eval_fk() and newton.eval_inverse_dynamics_passive().

Deprecate the Contacts.rigid_contact_diff_* compatibility outputs while retaining the distance and point arrays for the required deprecation window. Reuse Contacts.rigid_contact_normal for the deprecated differentiable-normal alias, eliminating that duplicate allocation immediately.

This separates derived contact kinematics from Contacts storage and prepares the remaining compatibility arrays for removal after the deprecation period.

eval_rigid_contact_kinematics

newton.eval_rigid_contact_kinematics(
    model,
    state,
    contacts,
    *,
    out_distance=None,
    out_point0_world=None,
    out_point1_world=None,
) -> None

Evaluates selected kinematic quantities for the active rigid contacts in a populated Contacts object. It reconstructs the world-space support points from the body-local points produced by collision detection and computes the signed distance using those points, the frozen world-space contact normal, and the contact margins.

Arguments

  • model (newton.Model): Supplies the shape-to-body mapping and determines the expected device.
  • state (newton.State): Supplies the current body transforms through state.body_q. Its body transforms must be on the same device as model.
  • contacts (newton.Contacts): Populated contacts whose rigid-contact geometry will be evaluated. The contacts must be on the same device as model.
  • out_distance (wp.array[float] | None): Optional output for signed contact distances in meters. It must have shape (contacts.rigid_contact_max,), dtype float, and reside on the model device. Positive values represent gaps and negative values represent penetration.
  • out_point0_world (wp.array[wp.vec3] | None): Optional output for world-space support points on shape 0 in meters. It must have shape (contacts.rigid_contact_max,), dtype wp.vec3, and reside on the model device.
  • out_point1_world (wp.array[wp.vec3] | None): Optional output for world-space support points on shape 1 in meters. It must have shape (contacts.rigid_contact_max,), dtype wp.vec3, and reside on the model device.

Only entries in the active contact range [0, contacts.rigid_contact_count) are written. Pass None for quantities that are not needed, but at least one output must be provided. Invalid output types or dtypes raise TypeError; missing outputs, invalid shapes, and device mismatches raise ValueError.

Calls made inside a wp.Tape scope participate in autodiff when the output arrays and state.body_q require gradients. Gradients flow through the body transforms, but not through the frozen contact normal or the discrete contact set. The existing contacts.rigid_contact_normal array provides the world-space normal directly.

Checklist

  • New or existing tests cover these changes
  • The documentation is up to date with these changes
  • CHANGELOG.md has been updated (if user-facing change)

Test plan

  • uv run python -m newton.tests.test_differentiable_contacts
  • uvx pre-commit run -a

New feature / API change

import newton
import warp as wp

pipeline = newton.CollisionPipeline(model, requires_grad=False)
contacts = pipeline.contacts()
distance = wp.empty(
    contacts.rigid_contact_max,
    dtype=float,
    requires_grad=True,
)

with wp.Tape() as tape:
    pipeline.collide(state, contacts)
    newton.eval_rigid_contact_kinematics(
        model,
        state,
        contacts,
        out_distance=distance,
    )

Summary by CodeRabbit

  • New Features

    • Added eval_rigid_contact_kinematics to compute rigid-contact distances and world-space support points into caller-provided output arrays.
    • Added input/output and device compatibility validation for safer usage.
    • Exported the API at the top level for direct access.
  • Documentation

    • Updated differentiable collision/contact docs with examples and guidance for the new call pattern.
  • Deprecations

    • Deprecated legacy differentiable rigid-contact outputs, with migration guidance and temporary compatibility.
  • Tests

    • Updated and expanded tests to cover the new API, partial outputs, gradients, and deprecation warnings.

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds eval_rigid_contact_kinematics for caller-provided contact distances and world-space points. Deprecates automatic differentiable contact outputs while preserving compatibility properties and the frozen contact normal. Updates kernels, solvers, documentation, exports, and tests.

Changes

Rigid contact kinematics

Layer / File(s) Summary
Kinematics API and kernel
newton/_src/sim/contact_kinematics.py, newton/_src/geometry/differentiable_contacts.py, newton/_src/sim/__init__.py, newton/__init__.py
Adds eval_rigid_contact_kinematics, output validation, optional output selection, kernel launching, and top-level exports.
Compatibility storage and pipeline integration
newton/_src/sim/contacts.py, newton/_src/sim/collide.py, newton/_src/solvers/coupled/solver_coupled.py
Stores legacy distance and point buffers privately, exposes deprecated properties with warnings, reuses the contact normal, and updates pipeline and solver access.
Usage migration and validation
newton/tests/test_differentiable_contacts.py, docs/concepts/collisions.rst, docs/api/newton.rst, CHANGELOG.md
Migrates examples and tests to caller-provided outputs. Adds coverage for validation, gradients, partial outputs, repeated collisions, and deprecated properties.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant eval_rigid_contact_kinematics
  participant rigid_contact_kinematics_kernel
  participant Contacts
  Caller->>eval_rigid_contact_kinematics: provide model, state, contacts, and output arrays
  eval_rigid_contact_kinematics->>eval_rigid_contact_kinematics: validate arrays and devices
  eval_rigid_contact_kinematics->>rigid_contact_kinematics_kernel: evaluate selected outputs
  rigid_contact_kinematics_kernel->>Caller: write distances and world-space points
  Caller->>Contacts: read frozen rigid_contact_normal
Loading

Suggested reviewers: nvtw

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.97% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: adding an explicit rigid contact kinematics API.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
newton/tests/test_differentiable_contacts.py (1)

55-118: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explanatory docstring body for this multi-scenario test.

This test exercises five distinct behaviors: full-output computation, distance-only output, point0-only output, missing-output validation, and gradient propagation. As per path instructions, "add a Google-style explanatory body for particularly complex tests." Add a short body listing the scenarios covered, so a future reader does not need to parse the whole test body to know what it verifies.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/tests/test_differentiable_contacts.py` around lines 55 - 118, Expand
the docstring of test_compute_rigid_contact_kinematics with a concise
Google-style explanatory body listing its five covered scenarios: full-output
computation, distance-only output, point0-only output, missing-output
validation, and gradient propagation. Keep the test behavior unchanged.

Source: Path instructions

newton/_src/sim/contacts.py (1)

507-518: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Clarify that rigid_contact_diff_normal returns the same array object, not a copy.

The getter returns self.rigid_contact_normal directly when no override is set (line 517). The docstring says this attribute "contains the same values," which reads as value-equality. It is in fact the identical array object. A caller who mutates the array returned from rigid_contact_diff_normal in place (for example .zero_() or wp.copy into it) silently corrupts rigid_contact_normal, the buffer solvers read for contact resolution. Before this change, rigid_contact_diff_normal was a separate buffer, so in-place writes were safe; that is no longer true.

State explicitly in the docstring that the same underlying array object is returned, so callers do not mutate it in place.

📝 Proposed docstring clarification
     def rigid_contact_diff_normal(self) -> wp.array[wp.vec3] | None:
         """Frozen world-space contact normal.

         .. deprecated:: 1.5
-            Use :attr:`rigid_contact_normal`, which contains the same values.
+            Use :attr:`rigid_contact_normal`. When no override was assigned, this
+            property returns that same array object (not a copy); do not write into
+            it in place, since doing so mutates :attr:`rigid_contact_normal` itself.
         """
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/_src/sim/contacts.py` around lines 507 - 518, Update the docstring of
rigid_contact_diff_normal to state explicitly that, when no override is set, it
returns the same underlying array object as rigid_contact_normal rather than a
copy. Preserve the existing getter behavior and deprecation guidance.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@newton/_src/geometry/differentiable_contacts.py`:
- Around line 195-201: Update the state.body_q device validation in
compute_rigid_contact_kinematics to explicitly handle state.body_q being None
before accessing .device, raising a clear ValueError for body-less models;
retain the existing device mismatch validation when body_q is present.

---

Nitpick comments:
In `@newton/_src/sim/contacts.py`:
- Around line 507-518: Update the docstring of rigid_contact_diff_normal to
state explicitly that, when no override is set, it returns the same underlying
array object as rigid_contact_normal rather than a copy. Preserve the existing
getter behavior and deprecation guidance.

In `@newton/tests/test_differentiable_contacts.py`:
- Around line 55-118: Expand the docstring of
test_compute_rigid_contact_kinematics with a concise Google-style explanatory
body listing its five covered scenarios: full-output computation, distance-only
output, point0-only output, missing-output validation, and gradient propagation.
Keep the test behavior unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8316b32d-9abc-4dbd-9bd8-a03d23792ebb

📥 Commits

Reviewing files that changed from the base of the PR and between b0260f3 and 52b146e.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • docs/api/newton_geometry.rst
  • docs/concepts/collisions.rst
  • newton/_src/geometry/differentiable_contacts.py
  • newton/_src/sim/collide.py
  • newton/_src/sim/contacts.py
  • newton/_src/solvers/coupled/solver_coupled.py
  • newton/geometry.py
  • newton/tests/test_differentiable_contacts.py

Comment thread newton/_src/geometry/differentiable_contacts.py Outdated
@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.08696% with 22 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
newton/_src/sim/contacts.py 72.22% 15 Missing ⚠️
newton/_src/sim/contact_kinematics.py 79.31% 6 Missing ⚠️
newton/_src/geometry/differentiable_contacts.py 83.33% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@eric-heiden eric-heiden self-assigned this Aug 5, 2026
@eric-heiden
eric-heiden marked this pull request as ready for review August 5, 2026 10:23
@eric-heiden
eric-heiden requested a review from a team as a code owner August 5, 2026 10:23
@eric-heiden
eric-heiden requested a review from nvtw August 5, 2026 11:11
Let callers provide only the differentiable rigid-contact outputs they need, instead of coupling derived quantities to Contacts storage.

Deprecate the legacy attributes and reuse rigid_contact_normal for the redundant differentiable-normal compatibility alias.
Match simulation evaluation APIs such as eval_fk by exporting the contact helper as newton.eval_rigid_contact_kinematics.

Keep the caller-provided output contract while moving the public wrapper into the simulation layer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
CHANGELOG.md (1)

50-50: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

State the migration action for this changed behavior.

The Changed category requires migration guidance. Add No migration is required or state that callers should use Contacts.rigid_contact_normal directly when they do not need the deprecated alias.

As per coding guidelines, "Deprecated, Changed, and Removed entries must include migration guidance."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` at line 50, Update the changelog entry for
Contacts.rigid_contact_diff_normal to include migration guidance, stating that
no migration is required or directing callers who do not need the deprecated
alias to use Contacts.rigid_contact_normal directly.

Sources: Coding guidelines, Path instructions

🧹 Nitpick comments (1)
newton/_src/sim/contact_kinematics.py (1)

16-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a typed Warp array annotation.

Replace wp.array | None with wp.array[Any] | None. Import Any from typing.

Proposed fix
+from typing import Any
+
 def _validate_output(
     name: str,
-    output: wp.array | None,
+    output: wp.array[Any] | None,

As per coding guidelines, annotate Warp arrays with bracket syntax.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/_src/sim/contact_kinematics.py` around lines 16 - 22, Update the
_validate_output parameter annotation from wp.array | None to wp.array[Any] |
None, and import Any from typing. Keep the function behavior unchanged while
using Warp’s bracketed array annotation syntax.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@newton/_src/sim/contact_kinematics.py`:
- Around line 83-86: Update eval_rigid_contact_kinematics to validate that
contacts and state.body_q belong to the supplied model, not merely that their
devices match. Use an available model-identity or preallocated-buffer
association check, and reject inputs from different models before indexing
model.shape_body; if identity cannot be tracked, document and enforce the
required ownership contract.

---

Outside diff comments:
In `@CHANGELOG.md`:
- Line 50: Update the changelog entry for Contacts.rigid_contact_diff_normal to
include migration guidance, stating that no migration is required or directing
callers who do not need the deprecated alias to use
Contacts.rigid_contact_normal directly.

---

Nitpick comments:
In `@newton/_src/sim/contact_kinematics.py`:
- Around line 16-22: Update the _validate_output parameter annotation from
wp.array | None to wp.array[Any] | None, and import Any from typing. Keep the
function behavior unchanged while using Warp’s bracketed array annotation
syntax.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 69389ed4-5f3e-40f7-bf8e-316511a6f24c

📥 Commits

Reviewing files that changed from the base of the PR and between 52b146e and 4625216.

📒 Files selected for processing (10)
  • CHANGELOG.md
  • docs/api/newton.rst
  • docs/concepts/collisions.rst
  • newton/__init__.py
  • newton/_src/geometry/differentiable_contacts.py
  • newton/_src/sim/__init__.py
  • newton/_src/sim/collide.py
  • newton/_src/sim/contact_kinematics.py
  • newton/_src/sim/contacts.py
  • newton/tests/test_differentiable_contacts.py
💤 Files with no reviewable changes (1)
  • newton/_src/geometry/differentiable_contacts.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • newton/tests/test_differentiable_contacts.py
  • newton/_src/sim/contacts.py
  • newton/_src/sim/collide.py

Comment thread newton/_src/sim/contact_kinematics.py
@eric-heiden
eric-heiden force-pushed the eric/rigid-contact-kinematics branch from 4625216 to ef1aed0 Compare August 5, 2026 16:21
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
newton/_src/sim/contact_kinematics.py (1)

18-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use a parameterized wp.array annotation.

Line 18 uses bare wp.array. Use wp.array[Any] | None because _validate_output accepts arrays with multiple element types.

As per coding guidelines, “Annotate Warp arrays with bracket syntax.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/_src/sim/contact_kinematics.py` at line 18, Update the output
parameter annotation in the surrounding contact kinematics function from bare
wp.array to wp.array[Any] | None, matching the multiple element types accepted
by _validate_output and the required bracket syntax.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@newton/_src/sim/contact_kinematics.py`:
- Around line 88-90: Update eval_rigid_contact_kinematics and its output
validation around _validate_output to reject out_distance, out_point0_world, and
out_point1_world when they alias any contact input buffer consumed by
_launch_rigid_contact_kinematics, including the margin and point arrays.
Preserve existing dtype, shape, and device validation, add regression coverage
for margin and point aliases, and document the changed accepted API behavior in
the changelog.

---

Nitpick comments:
In `@newton/_src/sim/contact_kinematics.py`:
- Line 18: Update the output parameter annotation in the surrounding contact
kinematics function from bare wp.array to wp.array[Any] | None, matching the
multiple element types accepted by _validate_output and the required bracket
syntax.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 12ed80ed-dfc9-4845-b41f-a7c9d7395925

📥 Commits

Reviewing files that changed from the base of the PR and between 238c2f6 and ef1aed0.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • docs/api/newton.rst
  • docs/concepts/collisions.rst
  • newton/__init__.py
  • newton/_src/geometry/differentiable_contacts.py
  • newton/_src/sim/__init__.py
  • newton/_src/sim/collide.py
  • newton/_src/sim/contact_kinematics.py
  • newton/_src/sim/contacts.py
  • newton/_src/solvers/coupled/solver_coupled.py
  • newton/tests/test_differentiable_contacts.py
🚧 Files skipped from review as they are similar to previous changes (10)
  • docs/api/newton.rst
  • newton/_src/sim/init.py
  • newton/_src/sim/collide.py
  • docs/concepts/collisions.rst
  • newton/_src/geometry/differentiable_contacts.py
  • newton/_src/sim/contacts.py
  • newton/tests/test_differentiable_contacts.py
  • newton/init.py
  • CHANGELOG.md
  • newton/_src/solvers/coupled/solver_coupled.py

Comment thread newton/_src/sim/contact_kinematics.py
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.

2 participants