Skip to content

Conversation

@gyeomannvidia
Copy link
Member

@gyeomannvidia gyeomannvidia commented Dec 16, 2025

Description

Newton Migration Guide

Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this PR.

  • [~] The migration guide in docs/migration.rst is up-to date

Before your PR is "Ready for review"

  • Necessary tests have been added and new examples are tested (see newton/tests/test_examples.py)
  • Documentation is up-to-date
  • Code passes formatting and linting checks with pre-commit run -a

Summary by CodeRabbit

  • New Features
    • Per-geometry collision margin support: parsed from MJCF and USD, included in shape configs, carried through model export, and applied/updated at runtime across multi-world setups.
  • Tests
    • Added tests for MJCF/USD margin parsing, conversion to the solver, and dynamic margin updates (multi-world validation); minor test adjustments to exercise new behavior.
  • Chores
    • Updated examples and test setups to set/propagate default rigid contact margins.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

📝 Walkthrough

Walkthrough

Propagates a per-shape contact margin (shape_contact_margin → geom_margin) from MJCF/USD import through ModelBuilder into SolverMuJoCo and the MuJoCo kernel; adds parsing, multi-world data expansion, kernel I/O changes, and unit tests for parsing and runtime updates.

Changes

Cohort / File(s) Summary
MuJoCo kernel & solver runtime
newton/_src/solvers/mujoco/kernels.py, newton/_src/solvers/mujoco/solver_mujoco.py
Added shape_contact_margin input and geom_margin output to update_geom_properties_kernel; threaded per-shape contact margin through SolverMuJoCo export, included geom_margin in multi-world expansion, and wrote margins into MuJoCo geom properties during updates.
Model import utilities
newton/_src/utils/import_mjcf.py, newton/_src/utils/import_usd.py
MJCF parser reads geometry margin into shape_cfg.contact_margin; USD import populates per-shape contact_margin (falls back to builder.rigid_contact_margin).
Model builder / shape config
newton/_src/sim/builder.py
ModelBuilder.ShapeConfig now accepts a contact_margin parameter and propagates it into model shape fields.
Tests
newton/tests/test_import_mjcf.py, newton/tests/test_import_usd.py, newton/tests/test_mujoco_solver.py, newton/tests/test_rigid_contact.py, newton/tests/test_anymal_reset.py
Added tests for per-shape margin parsing and MuJoCo conversion/update; adjusted fixtures to set rigid_contact_margin where needed; minor test control-flow adjustments related to contact computation.
Examples
newton/examples/robot/example_robot_allegro_hand.py
Sets allegro_hand.rigid_contact_margin = 0.0 in example configuration.

Sequence Diagram(s)

sequenceDiagram
    participant MJCF as MJCF / USD Import
    participant Builder as ModelBuilder
    participant Solver as SolverMuJoCo
    participant Kernel as update_geom_properties_kernel
    participant MuJoCo as MuJoCo Model

    MJCF->>Builder: parse shapes (include contact_margin)
    Builder->>Solver: export per-shape fields (shape_contact_margin)
    Solver->>Solver: expand per-shape fields for multi-worlds -> geom_margin array
    Solver->>Kernel: invoke update_geom_properties_kernel(..., shape_contact_margin, ...)
    Kernel->>Kernel: geom_margin[world, geom_idx] = shape_contact_margin[shape_idx]
    Kernel->>MuJoCo: write geom_margin into MuJoCo geom properties
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • adenzler-nvidia
  • eric-heiden
  • lenroe-nv

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 65.22% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add support for geom contact margin' directly and accurately summarizes the main objective of the pull request, which is to introduce geometry contact margin functionality.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8062092 and 0688c66.

📒 Files selected for processing (4)
  • newton/_src/solvers/mujoco/kernels.py
  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/_src/utils/import_usd.py
  • newton/tests/test_rigid_contact.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • newton/_src/utils/import_usd.py
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: shi-eric
Repo: newton-physics/newton PR: 521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.
📚 Learning: 2025-10-10T10:47:41.082Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-12-12T08:45:51.908Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 1221
File: newton/examples/example_sdf.py:277-287
Timestamp: 2025-12-12T08:45:51.908Z
Learning: In Newton examples (e.g., example_sdf.py), computing contacts once per frame and reusing them across multiple substeps is an intentional design choice, not a bug. The contacts are computed before the substep loop and intentionally kept constant throughout all substeps within that frame.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-10-24T07:56:14.792Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 981
File: newton/_src/geometry/collision_convex.py:180-289
Timestamp: 2025-10-24T07:56:14.792Z
Learning: In newton/_src/geometry/collision_convex.py, the single-contact solver (create_solve_convex_single_contact) intentionally returns count=1 even when shapes are separated (signed_distance > contact_threshold). The calling code is responsible for deciding whether to accept the contact based on the signed distance value. This design pushes filtering responsibility to the caller.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-08-20T18:02:36.099Z
Learnt from: eric-heiden
Repo: newton-physics/newton PR: 587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.099Z
Learning: In Newton physics, collisions between shapes with body index -1 (world-attached/global shapes) are automatically skipped by the collision system, so no manual collision filter pairs need to be added between global shapes from different builders.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-11-24T08:05:21.390Z
Learnt from: adenzler-nvidia
Repo: newton-physics/newton PR: 1107
File: newton/_src/solvers/mujoco/kernels.py:973-974
Timestamp: 2025-11-24T08:05:21.390Z
Learning: In Newton's MuJoCo solver integration (newton/_src/solvers/mujoco/), the mapping between Newton joints and MuJoCo joints is not 1-to-1. Instead, each Newton DOF maps to a distinct MuJoCo joint. This means that for multi-DOF joints (like D6 with 6 DOFs), there will be 6 corresponding MuJoCo joints, each with its own properties (margin, solimp, solref, etc.). The mapping is done via dof_to_mjc_joint array, ensuring each DOF's properties are written to its own MuJoCo joint without overwriting.

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.py
📚 Learning: 2025-08-12T17:51:37.474Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/geometry.py:16-22
Timestamp: 2025-08-12T17:51:37.474Z
Learning: In the Newton public API refactor, common geometry symbols like ParticleFlags and ShapeFlags are now exposed at the top level in newton/__init__.py rather than through newton.geometry. The newton/geometry.py module is intentionally focused only on broad-phase collision detection classes (BroadPhaseAllPairs, BroadPhaseExplicit, BroadPhaseSAP).

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/_src/solvers/mujoco/kernels.py
📚 Learning: 2025-08-25T20:10:59.536Z
Learnt from: dylanturpin
Repo: newton-physics/newton PR: 634
File: newton/examples/ik/example_ik_franka.py:0-0
Timestamp: 2025-08-25T20:10:59.536Z
Learning: In Newton's IK examples, when creating solver arrays with `wp.array(source_array, shape=new_shape)`, this creates a view into the same underlying memory rather than a copy. This means updates to the reshaped array are automatically reflected in the original array without needing explicit copy operations like `wp.copy()`.

Applied to files:

  • newton/_src/solvers/mujoco/kernels.py
🪛 GitHub Actions: Pull Request - AWS GPU
newton/_src/solvers/mujoco/solver_mujoco.py

[warning] 434-434: UserWarning: Value for nconmax is changed from 6 to 8 following an MjWarp requirement.


[warning] 434-434: UserWarning: Value for njmax is changed from 24 to 32 following an MjWarp requirement.

🔇 Additional comments (8)
newton/tests/test_rigid_contact.py (2)

34-37: LGTM! Collision handling properly aligned with solver capabilities.

The conditional contact computation is correct: MuJoCo solvers handle contacts internally (when use_mujoco_contacts=True), while other solvers (XPBD, Featherstone, etc.) require explicit collision detection via model.collide().


106-106: LGTM! Zero margin aligns with per-geom margin feature.

The change from 100.0 to 0.0 reflects the shift from a large global contact margin to the new per-geom shape_contact_margin approach. With per-geom margins now propagating through the system, the test no longer requires a large global margin for contact detection.

newton/_src/solvers/mujoco/kernels.py (2)

1217-1217: LGTM! Kernel signature correctly updated for per-geom margin.

The new shape_contact_margin input and geom_margin output parameters follow the established pattern for propagating per-shape properties to MuJoCo geoms across multiple worlds. The types are correct: 1D array for Newton shapes, 2D array for MuJoCo geoms indexed by [world, geom].

Also applies to: 1239-1239


1286-1287: LGTM! Per-geom margin propagation is correct.

The direct assignment of shape_contact_margin[shape_idx] to geom_margin[world, geom_idx] is correct and follows the established pattern for propagating shape properties in this kernel.

newton/_src/solvers/mujoco/solver_mujoco.py (4)

1123-1123: LGTM! Shape contact margin properly retrieved from model.

The retrieval of shape_contact_margin follows the established pattern for accessing shape properties during MuJoCo model conversion.


1381-1382: LGTM! Geom margin correctly assigned during creation.

The conditional assignment of geom_params["margin"] from shape_contact_margin[shape] is correct and maintains backward compatibility by checking for None before assignment.


2026-2026: LGTM! Geom margin properly included in multi-world expansion.

Adding "geom_margin" to model_fields_to_expand ensures per-geom margins are correctly replicated across all worlds in multi-world setups.


2294-2294: LGTM! Kernel invocation correctly updated with margin parameters.

The update_geom_properties_kernel invocation now correctly includes shape_contact_margin as input and geom_margin as output, matching the kernel signature changes and completing the margin propagation pipeline.

Also applies to: 2317-2317


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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
newton/_src/utils/import_usd.py (1)

1372-1377: USD contact margin resolution is sound; consider adding verbose flag for consistency

Resolving contact_margin via R.get_value(..., default=builder.rigid_contact_margin) nicely mirrors other per-shape properties and honors scene/builder defaults. To keep diagnostics consistent with the rest of the file, you might want to pass verbose=verbose here as you do for other R.get_value calls.

newton/tests/test_import_mjcf.py (1)

1431-1472: Margin behavior test is correct; fix stale numeric in comment

The test correctly exercises explicit geom margins and the fallback to builder.rigid_contact_margin (0.17). The inline comment on Line 1463 still mentions a default margin of 0.1, which no longer matches the test setup or expectations—worth updating to avoid confusion.

newton/tests/test_import_usd.py (1)

1311-1412: Solid USD geom margin test; drop unused noqa directives.

The construction and assertions in test_geom_margin_parsing look consistent with the existing USD/MuJoCo attribute tests and correctly verify:

  • explicit per‑geom margins (0.82, 0.71),
  • default margin staying at 0.0 and independent of builder.rigid_contact_margin.

Only nit: Ruff flags the # noqa: PLC0415 comments on the local imports as unused (RUF100). Since these imports are already inside the test function, the noqa is unnecessary here—please remove the # noqa: PLC0415 on lines 1314 and 1316.

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ad9b49 and df44377.

📒 Files selected for processing (7)
  • newton/_src/solvers/mujoco/kernels.py (3 hunks)
  • newton/_src/solvers/mujoco/solver_mujoco.py (5 hunks)
  • newton/_src/utils/import_mjcf.py (1 hunks)
  • newton/_src/utils/import_usd.py (1 hunks)
  • newton/tests/test_import_mjcf.py (1 hunks)
  • newton/tests/test_import_usd.py (1 hunks)
  • newton/tests/test_mujoco_solver.py (2 hunks)
🧰 Additional context used
🧠 Learnings (11)
📓 Common learnings
Learnt from: shi-eric
Repo: newton-physics/newton PR: 521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-08-12T17:51:37.474Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/geometry.py:16-22
Timestamp: 2025-08-12T17:51:37.474Z
Learning: In the Newton public API refactor, common geometry symbols like ParticleFlags and ShapeFlags are now exposed at the top level in newton/__init__.py rather than through newton.geometry. The newton/geometry.py module is intentionally focused only on broad-phase collision detection classes (BroadPhaseAllPairs, BroadPhaseExplicit, BroadPhaseSAP).

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/_src/solvers/mujoco/kernels.py
  • newton/_src/utils/import_usd.py
📚 Learning: 2025-11-24T08:05:21.390Z
Learnt from: adenzler-nvidia
Repo: newton-physics/newton PR: 1107
File: newton/_src/solvers/mujoco/kernels.py:973-974
Timestamp: 2025-11-24T08:05:21.390Z
Learning: In Newton's MuJoCo solver integration (newton/_src/solvers/mujoco/), the mapping between Newton joints and MuJoCo joints is not 1-to-1. Instead, each Newton DOF maps to a distinct MuJoCo joint. This means that for multi-DOF joints (like D6 with 6 DOFs), there will be 6 corresponding MuJoCo joints, each with its own properties (margin, solimp, solref, etc.). The mapping is done via dof_to_mjc_joint array, ensuring each DOF's properties are written to its own MuJoCo joint without overwriting.

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.py
📚 Learning: 2025-08-25T20:10:59.536Z
Learnt from: dylanturpin
Repo: newton-physics/newton PR: 634
File: newton/examples/ik/example_ik_franka.py:0-0
Timestamp: 2025-08-25T20:10:59.536Z
Learning: In Newton's IK examples, when creating solver arrays with `wp.array(source_array, shape=new_shape)`, this creates a view into the same underlying memory rather than a copy. This means updates to the reshaped array are automatically reflected in the original array without needing explicit copy operations like `wp.copy()`.

Applied to files:

  • newton/_src/solvers/mujoco/kernels.py
📚 Learning: 2025-09-22T21:08:31.901Z
Learnt from: dylanturpin
Repo: newton-physics/newton PR: 806
File: newton/examples/ik/example_ik_franka.py:121-123
Timestamp: 2025-09-22T21:08:31.901Z
Learning: In the newton physics framework, when creating warp arrays for IK solver joint variables using wp.array(self.model.joint_q, shape=(1, coord_count)), the resulting array acts as a reference/pointer to the original model's joint coordinates, so updates from the IK solver automatically reflect in the model's joint_q buffer used for rendering.

Applied to files:

  • newton/_src/solvers/mujoco/kernels.py
📚 Learning: 2025-08-20T03:30:16.037Z
Learnt from: eric-heiden
Repo: newton-physics/newton PR: 584
File: newton/_src/utils/import_urdf.py:159-167
Timestamp: 2025-08-20T03:30:16.037Z
Learning: In the URDF importer (newton/_src/utils/import_urdf.py), cylinders are intentionally created using add_shape_capsule() instead of add_shape_cylinder() because cylinder collisions are not fully supported yet. This is a deliberate workaround until proper cylinder collision support is implemented.

Applied to files:

  • newton/_src/utils/import_usd.py
📚 Learning: 2025-10-10T10:47:41.082Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.

Applied to files:

  • newton/_src/utils/import_usd.py
  • newton/tests/test_mujoco_solver.py
📚 Learning: 2025-10-24T07:56:14.792Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 981
File: newton/_src/geometry/collision_convex.py:180-289
Timestamp: 2025-10-24T07:56:14.792Z
Learning: In newton/_src/geometry/collision_convex.py, the single-contact solver (create_solve_convex_single_contact) intentionally returns count=1 even when shapes are separated (signed_distance > contact_threshold). The calling code is responsible for deciding whether to accept the contact based on the signed distance value. This design pushes filtering responsibility to the caller.

Applied to files:

  • newton/_src/utils/import_usd.py
📚 Learning: 2025-09-24T00:26:54.486Z
Learnt from: eric-heiden
Repo: newton-physics/newton PR: 710
File: newton/_src/utils/import_usd.py:597-599
Timestamp: 2025-09-24T00:26:54.486Z
Learning: In the Newton ModelBuilder class, `joint_count`, `body_count`, `articulation_count`, and `shape_count` are implemented as property decorated methods, so they should be accessed as attributes (e.g., `builder.joint_count`) rather than called as methods (e.g., `builder.joint_count()`).

Applied to files:

  • newton/tests/test_mujoco_solver.py
📚 Learning: 2025-08-12T18:07:17.267Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/utils.py:30-33
Timestamp: 2025-08-12T18:07:17.267Z
Learning: In newton/_src/utils/import_usd.py, the PXR/USD modules are lazily loaded using try-except blocks within the functions rather than at module import time, making it safe to import parse_usd at the module level without causing import-time failures for users without USD dependencies.

Applied to files:

  • newton/tests/test_import_usd.py
📚 Learning: 2025-08-12T18:07:17.267Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/utils.py:30-33
Timestamp: 2025-08-12T18:07:17.267Z
Learning: In newton/_src/utils/import_usd.py, the PXR/USD modules are lazily loaded using try-except blocks within the functions (lines 109-111 and 1289-1291) rather than at module import time, making it safe to import parse_usd at the module level without causing import-time failures for users without USD dependencies.

Applied to files:

  • newton/tests/test_import_usd.py
🧬 Code graph analysis (1)
newton/_src/utils/import_usd.py (2)
newton/_src/usd/schema_resolver.py (3)
  • get_value (101-120)
  • get_value (196-247)
  • PrimType (33-49)
newton/_src/sim/builder.py (1)
  • key (395-397)
🪛 Ruff (0.14.8)
newton/tests/test_import_usd.py

1314-1314: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


1316-1316: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (9)
newton/_src/utils/import_mjcf.py (1)

330-333: MJCF per-geom margin wiring looks correct

Reading geom_attrib["margin"] into shape_cfg.contact_margin only when authored cleanly extends ShapeConfig without disturbing existing defaults; no issues spotted.

newton/_src/solvers/mujoco/kernels.py (1)

1177-1205: Per-geom margin propagation through MuJoCo kernel is consistent and minimal

Adding shape_contact_margin as an input and copying it into geom_margin[world, geom_idx] mirrors how other shape properties are exported (friction, solimp/solmix, etc.) and cleanly feeds contact_params’ existing use of geom_margin without altering control flow. Implementation looks correct assuming caller updates the kernel launch signature accordingly.

If not already done, double‑check the corresponding kernel launch in solver_mujoco.py passes shape_contact_margin and geom_margin in the updated order to avoid subtle argument misalignment.

Also applies to: 1247-1248

newton/tests/test_mujoco_solver.py (2)

2499-2591: geom_margin multi‑world conversion and update test looks correct.

The new test_geom_margin_conversion_and_update follows the established patterns for geom_* tests:

  • assigns per‑shape shape_contact_margin using shape_world,
  • verifies mjw_model.geom_margin via mjc_geom_to_newton_shape,
  • exercises runtime updates through notify_model_changed(SHAPE_PROPERTIES).

Indexing and multi‑world expansion logic are consistent with the neighboring geom property tests.


3582-3583: Explicitly zeroing rigid_contact_margin in mocap test is appropriate.

Setting builder.rigid_contact_margin = 0.0 in test_mocap_body_transform_updates_collision_geoms keeps this scenario independent of global contact margins, which is helpful now that per‑geom margins are supported. The change is localized to this test and should not affect other cases.

newton/_src/solvers/mujoco/solver_mujoco.py (5)

1093-1093: LGTM: Contact margin retrieval follows established pattern.

The retrieval of shape_contact_margin is consistent with how other core shape properties are accessed (e.g., shape_mu, shape_torsional_friction). This appropriately treats contact margin as a core Newton shape attribute rather than a MuJoCo-specific custom attribute.


1347-1348: LGTM: Margin parameter correctly set during geom creation.

The conditional setting of the margin parameter follows the same pattern as other shape properties (e.g., shape_condim, shape_priority, shape_geom_solimp) and correctly indexes into the shape-specific margin value.


1968-1968: LGTM: Geom margin correctly included in multi-world expansion.

The addition of "geom_margin" to model_fields_to_expand ensures that margin values are properly replicated across multiple worlds in the MuJoCo Warp model, consistent with other geometry properties like geom_friction and geom_solmix.


2236-2236: LGTM: Contact margin correctly passed to update kernel.

The shape_contact_margin input to update_geom_properties_kernel enables dynamic updates of margin values through the notify_model_changed system, consistent with other shape properties.


2257-2257: LGTM: Geom margin properly output to MuJoCo model.

The output of geom_margin to the MuJoCo Warp model completes the data flow from Newton's shape properties through to MuJoCo's collision detection system.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
newton/_src/utils/import_mjcf.py (1)

336-338: Minor: Redundant default parameter in parse_float call.

The parse_float(geom_attrib, "margin", 0.0) call on line 338 will never use the default value 0.0 because the code already checks if "margin" in geom_attrib: on line 337. When margin is not specified, the code correctly preserves shape_cfg.contact_margin from builder.default_shape_cfg (line 316).

This works correctly, but for clarity, consider one of these alternatives:

Option 1: Remove the if-check and let parse_float handle the missing attribute (but would need to change default):

-# Parse MJCF margin (contact margin for collision detection)
-if "margin" in geom_attrib:
-    shape_cfg.contact_margin = parse_float(geom_attrib, "margin", 0.0)
+# Parse MJCF margin (contact margin for collision detection)
+# If not specified, parse_float returns current value from shape_cfg
+if "margin" in geom_attrib:
+    shape_cfg.contact_margin = parse_float(geom_attrib, "margin", shape_cfg.contact_margin)

Option 2: Make the unused default explicit:

-    shape_cfg.contact_margin = parse_float(geom_attrib, "margin", 0.0)
+    shape_cfg.contact_margin = parse_float(geom_attrib, "margin", 0.0)  # default unused; already checked key exists

However, since this follows the same pattern as the friction parsing above (lines 322-334), maintaining consistency is reasonable.

newton/tests/test_mujoco_solver.py (2)

2597-2689: LGTM! Comprehensive test for geom_margin feature.

The test correctly validates per-shape contact margin conversion to MuJoCo and runtime updates across multiple worlds. The structure follows established patterns and covers both initial conversion and dynamic updates via notify_model_changed.

Optional: Consider adding a clarifying comment to explain why this test doesn't call SolverMuJoCo.register_custom_attributes() unlike similar tests (e.g., test_geom_gap_conversion_and_update). This would help future maintainers understand that shape_contact_margin is a standard Newton Model attribute rather than a MuJoCo-specific custom attribute.

For example:

 def test_geom_margin_conversion_and_update(self):
     """Test per-shape geom_margin conversion to MuJoCo and dynamic updates across multiple worlds."""
 
+    # Note: shape_contact_margin is a standard Newton Model attribute, not a MuJoCo custom attribute,
+    # so no register_custom_attributes() call is needed (unlike geom_gap, geom_solmix, etc.)
     # Create a model with custom attributes registered

2792-2792: Clarify the reason for setting rigid_contact_margin to zero.

This line sets the contact margin to zero, likely to isolate the test from margin-related behavior. Adding a brief comment would help future maintainers understand why this configuration is necessary.

Apply this diff to add a clarifying comment:

         builder = newton.ModelBuilder()
         builder.default_shape_cfg.ke = 1e4
         builder.default_shape_cfg.kd = 1000.0
-        builder.rigid_contact_margin = 0.0
+        builder.rigid_contact_margin = 0.0  # Disable contact margin to ensure clean contact behavior
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df44377 and 6925a7d.

📒 Files selected for processing (6)
  • newton/_src/solvers/mujoco/kernels.py (3 hunks)
  • newton/_src/solvers/mujoco/solver_mujoco.py (5 hunks)
  • newton/_src/utils/import_mjcf.py (1 hunks)
  • newton/tests/test_import_mjcf.py (1 hunks)
  • newton/tests/test_import_usd.py (1 hunks)
  • newton/tests/test_mujoco_solver.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/tests/test_import_usd.py
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: shi-eric
Repo: newton-physics/newton PR: 521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.
📚 Learning: 2025-08-12T17:51:37.474Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/geometry.py:16-22
Timestamp: 2025-08-12T17:51:37.474Z
Learning: In the Newton public API refactor, common geometry symbols like ParticleFlags and ShapeFlags are now exposed at the top level in newton/__init__.py rather than through newton.geometry. The newton/geometry.py module is intentionally focused only on broad-phase collision detection classes (BroadPhaseAllPairs, BroadPhaseExplicit, BroadPhaseSAP).

Applied to files:

  • newton/_src/solvers/mujoco/kernels.py
  • newton/tests/test_mujoco_solver.py
📚 Learning: 2025-08-25T20:10:59.536Z
Learnt from: dylanturpin
Repo: newton-physics/newton PR: 634
File: newton/examples/ik/example_ik_franka.py:0-0
Timestamp: 2025-08-25T20:10:59.536Z
Learning: In Newton's IK examples, when creating solver arrays with `wp.array(source_array, shape=new_shape)`, this creates a view into the same underlying memory rather than a copy. This means updates to the reshaped array are automatically reflected in the original array without needing explicit copy operations like `wp.copy()`.

Applied to files:

  • newton/_src/solvers/mujoco/kernels.py
📚 Learning: 2025-10-10T10:47:41.082Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.

Applied to files:

  • newton/tests/test_mujoco_solver.py
📚 Learning: 2025-09-24T00:26:54.486Z
Learnt from: eric-heiden
Repo: newton-physics/newton PR: 710
File: newton/_src/utils/import_usd.py:597-599
Timestamp: 2025-09-24T00:26:54.486Z
Learning: In the Newton ModelBuilder class, `joint_count`, `body_count`, `articulation_count`, and `shape_count` are implemented as property decorated methods, so they should be accessed as attributes (e.g., `builder.joint_count`) rather than called as methods (e.g., `builder.joint_count()`).

Applied to files:

  • newton/tests/test_mujoco_solver.py
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run GPU Tests / Run GPU Unit Tests on AWS EC2
  • GitHub Check: Run GPU Benchmarks / Run GPU Benchmarks on AWS EC2
🔇 Additional comments (2)
newton/tests/test_import_mjcf.py (1)

1500-1542: LGTM! Test correctly validates per-geom margin parsing and default handling.

The test properly verifies:

  • Explicit per-geom margin values are parsed and applied (0.5, 0.8)
  • Missing margin attributes fall back to builder.rigid_contact_margin (0.17) instead of MuJoCo's default (0.0)
  • The shape_contact_margin attribute is correctly exposed on the finalized model

The comment on line 1535 helpfully clarifies Newton's design decision to use its own default margin rather than MuJoCo's convention.

newton/_src/solvers/mujoco/kernels.py (1)

1183-1183: LGTM! Kernel changes correctly propagate per-shape contact margin.

The implementation:

  • Adds shape_contact_margin input and geom_margin output parameters to the kernel signature
  • Writes the per-shape margin to the per-world geom array (lines 1252-1253)
  • Follows the same pattern as existing properties like geom_gap (lines 1249-1250)
  • Benefits from the existing shape_idx bounds check on line 1215

The changes integrate cleanly with the existing update path and maintain consistency with the codebase patterns.

Also applies to: 1205-1205, 1252-1253

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
newton/tests/test_import_usd.py (3)

1431-1433: Clean up unused noqa directives.

The static analysis tool indicates these noqa directives are unnecessary since the PLC0415 rule is not enabled. While this is a minor issue, removing them would clean up the code.

Apply this diff to remove the unused directives:

-        from pxr import Usd  # noqa: PLC0415
+        from pxr import Usd

-        from newton._src.usd.schemas import SchemaResolverMjc  # noqa: PLC0415
+        from newton._src.usd.schemas import SchemaResolverMjc

1520-1523: Clarify that builder.rigid_contact_margin is ignored for USD imports.

Setting builder.rigid_contact_margin = 0.2 here is potentially misleading because USD imports with SchemaResolverMjc use the hardcoded default of 0.0 from the schema resolver, not the builder's setting. This differs from MJCF imports where the builder's margin is used as the default (see test_import_mjcf.py::test_geom_margin_parsing).

Consider either removing this line or adding a comment explaining that it's intentionally set to a different value to verify that USD parsing ignores it.

Option 1 - Remove the misleading assignment:

         builder = newton.ModelBuilder()
-        builder.rigid_contact_margin = 0.2
         builder.add_usd(stage, schema_resolvers=[SchemaResolverMjc()])

Option 2 - Add clarifying comment:

         builder = newton.ModelBuilder()
+        # Note: This setting is ignored for USD imports with SchemaResolverMjc,
+        # which uses its own hardcoded default of 0.0
         builder.rigid_contact_margin = 0.2
         builder.add_usd(stage, schema_resolvers=[SchemaResolverMjc()])

1534-1537: Improve comment formatting.

The inline comment on line 1536 is lengthy and disrupts readability. Consider moving it to a separate line above the assignment for better clarity.

Apply this diff:

         # Check that we have shapes with expected values
         expected_explicit_1 = 0.82
-        expected_default = 0.0  # default Note that we default to 0.0 when parsing USD and that builder.rigid_contact_margin plays no role.
+        # Note: USD parsing with SchemaResolverMjc defaults to 0.0, ignoring builder.rigid_contact_margin
+        expected_default = 0.0
         expected_explicit_2 = 0.71
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6925a7d and c88d654.

📒 Files selected for processing (1)
  • newton/tests/test_import_usd.py (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: shi-eric
Repo: newton-physics/newton PR: 521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.
📚 Learning: 2025-08-12T18:07:17.267Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/utils.py:30-33
Timestamp: 2025-08-12T18:07:17.267Z
Learning: In newton/_src/utils/import_usd.py, the PXR/USD modules are lazily loaded using try-except blocks within the functions (lines 109-111 and 1289-1291) rather than at module import time, making it safe to import parse_usd at the module level without causing import-time failures for users without USD dependencies.

Applied to files:

  • newton/tests/test_import_usd.py
📚 Learning: 2025-08-12T18:07:17.267Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/utils.py:30-33
Timestamp: 2025-08-12T18:07:17.267Z
Learning: In newton/_src/utils/import_usd.py, the PXR/USD modules are lazily loaded using try-except blocks within the functions rather than at module import time, making it safe to import parse_usd at the module level without causing import-time failures for users without USD dependencies.

Applied to files:

  • newton/tests/test_import_usd.py
📚 Learning: 2025-10-10T10:47:41.082Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.

Applied to files:

  • newton/tests/test_import_usd.py
🧬 Code graph analysis (1)
newton/tests/test_import_usd.py (2)
newton/tests/test_import_mjcf.py (1)
  • test_geom_margin_parsing (1500-1541)
newton/_src/usd/schemas.py (1)
  • SchemaResolverMjc (245-315)
🪛 Ruff (0.14.8)
newton/tests/test_import_usd.py

1431-1431: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)


1433-1433: Unused noqa directive (non-enabled: PLC0415)

Remove unused noqa directive

(RUF100)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run GPU Benchmarks / Run GPU Benchmarks on AWS EC2
  • GitHub Check: Run GPU Tests / Run GPU Unit Tests on AWS EC2

… value is there: shape_cfg.contact_margin = float(geom_attrib["margin"])

Signed-off-by: Gordon Yeoman <[email protected]>
Copy link
Member

@adenzler-nvidia adenzler-nvidia left a comment

Choose a reason for hiding this comment

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

Thank you!


# Parse MJCF margin (contact margin for collision detection)
if "margin" in geom_attrib:
shape_cfg.contact_margin = float(geom_attrib["margin"])
Copy link
Member

Choose a reason for hiding this comment

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

I think you could just use parse_float here.

geom_margin = model.shape_contact_margin.numpy()
self.assertEqual(model.shape_count, 3, "Should have 3 shapes")

# Expected values: shape 0 has margin=0.5, shape 1 has default margin=0.1 (builder.rigid_contact_margin), shape 2 has margin=0.8
Copy link
Member

Choose a reason for hiding this comment

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

nitpick: comment is outdated (1 vs 1.7)

model = builder.finalize()

# shape_contact_margin is a built-in Newton attribute, not a custom mujoco attribute
self.assertTrue(hasattr(model, "shape_contact_margin"), "Model should have shape_contact_margin attribute")
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if that assert is really needed, the attribute should always be there and the test is going to fail anyway if you're going to access it. We never really do this test for other built-ins. But it's a subjective issue, leaving the decision up to you.

Copy link
Member Author

Choose a reason for hiding this comment

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

It's really just a reminder that we are not working with a custom attribute. It's useful for me, even if for nobody else.

@codecov
Copy link

codecov bot commented Dec 18, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
newton/tests/test_rigid_contact.py (1)

40-53: Consider using a dictionary for cleaner margin lookup.

The current if-elif chain works correctly, but a dictionary-based approach would be more maintainable and concise.

🔎 Optional refactoring to use dictionary lookup:
-def get_contact_margin(solver_name):
-    if solver_name == "featherstone":
-        return 100.0
-    elif solver_name == "mujoco_cpu":
-        return 0.0
-    elif solver_name == "mujoco_warp":
-        return 0.0
-    elif solver_name == "xpbd":
-        return 100.0
-    elif solver_name == "semi_implicit":
-        return 100.0
-    else:
-        return 0.0
+def get_contact_margin(solver_name):
+    margins = {
+        "featherstone": 100.0,
+        "mujoco_cpu": 0.0,
+        "mujoco_warp": 0.0,
+        "xpbd": 100.0,
+        "semi_implicit": 100.0,
+    }
+    return margins.get(solver_name, 0.0)
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd9e314 and ebec7e8.

📒 Files selected for processing (6)
  • newton/_src/utils/import_mjcf.py (1 hunks)
  • newton/_src/utils/import_usd.py (1 hunks)
  • newton/examples/robot/example_robot_allegro_hand.py (1 hunks)
  • newton/tests/test_anymal_reset.py (1 hunks)
  • newton/tests/test_import_mjcf.py (1 hunks)
  • newton/tests/test_rigid_contact.py (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • newton/_src/utils/import_mjcf.py
  • newton/tests/test_import_mjcf.py
  • newton/_src/utils/import_usd.py
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: shi-eric
Repo: newton-physics/newton PR: 521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-12-12T08:45:43.428Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 1221
File: newton/examples/example_sdf.py:277-287
Timestamp: 2025-12-12T08:45:43.428Z
Learning: In Newtown (Newton) example code, specifically files under newton/examples, computing contacts once per frame and reusing them across all substeps is an intentional design choice, not a bug. Reviewers should verify that contacts are computed before the substep loop and reused for every substep within the same frame. This pattern reduces redundant work and preserves frame-consistency; do not flag as a regression unless the behavior is changed for correctness or performance reasons.

Applied to files:

  • newton/examples/robot/example_robot_allegro_hand.py
📚 Learning: 2025-10-10T10:47:41.082Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-10-24T07:56:14.792Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 981
File: newton/_src/geometry/collision_convex.py:180-289
Timestamp: 2025-10-24T07:56:14.792Z
Learning: In newton/_src/geometry/collision_convex.py, the single-contact solver (create_solve_convex_single_contact) intentionally returns count=1 even when shapes are separated (signed_distance > contact_threshold). The calling code is responsible for deciding whether to accept the contact based on the signed distance value. This design pushes filtering responsibility to the caller.

Applied to files:

  • newton/tests/test_rigid_contact.py
🪛 Ruff (0.14.8)
newton/tests/test_rigid_contact.py

66-66: Unused function argument: test

(ARG001)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run GPU Tests / Run GPU Unit Tests on AWS EC2
  • GitHub Check: Run GPU Benchmarks / Run GPU Benchmarks on AWS EC2
🔇 Additional comments (5)
newton/tests/test_anymal_reset.py (1)

39-39: LGTM! Contact margin explicitly configured for test consistency.

Setting rigid_contact_margin = 0.0 ensures deterministic contact behavior for the reset stability tests. The placement before model finalization is correct.

newton/examples/robot/example_robot_allegro_hand.py (1)

96-96: LGTM! Contact margin properly configured for the example.

Setting rigid_contact_margin = 0.0 is appropriate for the MuJoCo solver used in this example (line 135). The placement in the builder configuration sequence is correct.

newton/tests/test_rigid_contact.py (3)

31-37: LGTM! Multi-solver test infrastructure is well-structured.

The solvers dictionary provides a clean, extensible way to test across different solver backends. The lambda-based lazy initialization is appropriate.


66-66: LGTM! Solver-specific margin handling is correctly implemented.

The solver_name parameter enables dynamic margin configuration per solver type. The margin is correctly set before adding shapes (line 128), as required.

Note: The static analysis warning about unused test parameter is a false positive—the parameter is used for assertions throughout the function (e.g., line 205, 207).

Also applies to: 128-128


754-767: LGTM! Test registration correctly wires solver-specific parameters.

The test registration loop properly passes solver_name to enable per-solver margin configuration. The device filtering logic (lines 756-759) appropriately skips incompatible solver-device combinations.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
newton/tests/test_rigid_contact.py (1)

55-55: Unused solver_name parameter (likely intentional).

The solver_name parameter is not used within the function body. This appears intentional for test registration purposes (passed to add_function_test at line 755 for test naming), which is an acceptable pattern in test frameworks.

If this is the intended design, consider adding a comment or suppressing the linting warning. Alternatively, you can prefix unused parameters with an underscore (e.g., _solver_name) to indicate they're intentionally unused.

🔎 Optional: Suppress linting warning
-def test_shapes_on_plane(test, device, solver_fn, solver_name):
+def test_shapes_on_plane(test, device, solver_fn, solver_name):  # noqa: ARG001

Or prefix with underscore to indicate intentional:

-def test_shapes_on_plane(test, device, solver_fn, solver_name):
+def test_shapes_on_plane(test, device, solver_fn, _solver_name):
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ebec7e8 and 8062092.

📒 Files selected for processing (1)
  • newton/tests/test_rigid_contact.py
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: shi-eric
Repo: newton-physics/newton PR: 521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.
📚 Learning: 2025-10-10T10:47:41.082Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 899
File: newton/tests/test_rigid_contact.py:214-268
Timestamp: 2025-10-10T10:47:41.082Z
Learning: In `newton/tests/test_rigid_contact.py`, using `add_shape_plane(width=0, length=0)` for an infinite plane works correctly with the collision system and does not cause degenerate AABB issues in the test scenario `test_shape_collisions_gjk_mpr_multicontact`.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-09-18T07:05:56.836Z
Learnt from: gdaviet
Repo: newton-physics/newton PR: 750
File: newton/_src/solvers/implicit_mpm/solve_rheology.py:969-986
Timestamp: 2025-09-18T07:05:56.836Z
Learning: In newton/_src/solvers/implicit_mpm/solve_rheology.py, transposed_strain_mat parameter cannot be None - the type signature was corrected to reflect this guarantee, eliminating the need for None checks when accessing transposed_strain_mat.offsets.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-10-24T07:56:14.792Z
Learnt from: nvtw
Repo: newton-physics/newton PR: 981
File: newton/_src/geometry/collision_convex.py:180-289
Timestamp: 2025-10-24T07:56:14.792Z
Learning: In newton/_src/geometry/collision_convex.py, the single-contact solver (create_solve_convex_single_contact) intentionally returns count=1 even when shapes are separated (signed_distance > contact_threshold). The calling code is responsible for deciding whether to accept the contact based on the signed distance value. This design pushes filtering responsibility to the caller.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-08-12T17:51:37.474Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/geometry.py:16-22
Timestamp: 2025-08-12T17:51:37.474Z
Learning: In the Newton public API refactor, common geometry symbols like ParticleFlags and ShapeFlags are now exposed at the top level in newton/__init__.py rather than through newton.geometry. The newton/geometry.py module is intentionally focused only on broad-phase collision detection classes (BroadPhaseAllPairs, BroadPhaseExplicit, BroadPhaseSAP).

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-08-12T17:58:16.929Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/tests/test_ik.py:25-26
Timestamp: 2025-08-12T17:58:16.929Z
Learning: In the Newton physics engine codebase, it's acceptable for tests to import and use private `_src` internal modules and functions when needed for testing purposes, even though this breaks the public API boundary. This is an intentional project decision for test code.

Applied to files:

  • newton/tests/test_rigid_contact.py
📚 Learning: 2025-08-20T18:02:36.099Z
Learnt from: eric-heiden
Repo: newton-physics/newton PR: 587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.099Z
Learning: In Newton physics, collisions between shapes with body index -1 (world-attached/global shapes) are automatically skipped by the collision system, so no manual collision filter pairs need to be added between global shapes from different builders.

Applied to files:

  • newton/tests/test_rigid_contact.py
🪛 Ruff (0.14.10)
newton/tests/test_rigid_contact.py

55-55: Unused function argument: test

(ARG001)


55-55: Unused function argument: solver_name

(ARG001)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (4)
newton/tests/test_rigid_contact.py (4)

31-37: LGTM!

The module-scope solvers dictionary is well-structured for test parameterization. The lambda-based lazy instantiation is appropriate, and the cleanup of the previously duplicated dictionary block improves code organization.


42-52: LGTM!

The conditional contact computation logic correctly handles the different solver backends. MuJoCo solvers handle collision detection internally (contacts=None), while other solvers require explicit collision computation via model.collide().


116-117: LGTM!

Setting rigid_contact_margin = 0.0 is appropriate for this test, which verifies that shapes settle to precise expected positions (lines 129, 140, 149, 157, 165) matching their geometric dimensions. The comment correctly notes the ordering constraint.


743-756: LGTM!

The test registration loop correctly integrates with the module-scope solvers dictionary and passes solver_name for test naming. The device/solver compatibility filtering (skipping mujoco_warp on CPU and mujoco_cpu on CUDA) is appropriate.

@adenzler-nvidia
Copy link
Member

@gyeomannvidia should we close this for now?

@gyeomannvidia
Copy link
Member Author

Definitely close this.

@adenzler-nvidia
Copy link
Member

closing as per recent margin-related discussions.

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