Skip to content

Conversation

@jvonmuralt
Copy link
Member

@jvonmuralt jvonmuralt commented Dec 24, 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

  • Bug Fixes

    • World bodies are now consistently labeled in MuJoCo constraint names to avoid mislabeling.
  • New Features

    • MJCF import now supports site-based connect and weld constraints, resolving sites to bodies/anchors and computing relative poses; logs warnings for incomplete site pairs.
  • Tests

    • Tests tightened to check exact equality-constraint counts and inspect per-constraint metadata (bodies, anchors, torque scale).

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 24, 2025

📝 Walkthrough

Walkthrough

Added a safe body-name helper in the MuJoCo solver and implemented site-based handling for MJCF connect and weld (resolving sites to body+anchor and computing relpose). Tests were updated to assert exact equality-constraint metadata and counts after finalize.

Changes

Cohort / File(s) Summary
Body naming helper
newton/_src/solvers/mujoco/solver_mujoco.py
Added local get_body_name(body_idx) mapping -1"world" and replaced direct model.body_key[...] lookups when naming CONNECT/WELD constraints.
Site-based constraint resolution (MJCF import)
newton/_src/utils/import_mjcf.py
Added get_site_body_and_anchor(site_name) and extended parse_common_attributes to resolve site1/site2 for connect and weld: resolve sites → (body, anchor), create equality constraints using anchors, compute relpose for welds, log/skip when sites missing, retain body-based fallback.
Tests — equality constraints
newton/tests/test_equality_constraints.py
Expanded test to validate exact equality-constraint metadata after finalize (count = 5, per-constraint body indices, anchors, torquescale) for both body- and site-based constraints.

Sequence Diagram(s)

(Skipped — changes are parsing/naming/tests without a new multi-component sequential control flow.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • adenzler-nvidia
  • eric-heiden
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 'Site based constraints' directly describes the main feature being implemented across all three modified files, which add support for site-based equality constraints (connect and weld) with helper functions and comprehensive testing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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_mjcf.py (2)

987-1025: Behavior of site-based CONNECT constraints and missing-site handling

The new site1/site2 branch for <connect> correctly maps:

  • site1body1_idx and anchor, and
  • optional site2body2_idx,

falling back to body2 (or world via -1) when site2 is omitted, and skipping the constraint if a referenced site cannot be resolved.

Two points worth tightening:

  1. Anchor frame consistency: In the site path, anchor_vec comes from the site transform, while in the non‑site path it comes directly from the MJCF anchor attribute. If MuJoCo’s connect anchor is interpreted in world coordinates, confirm that anchor_vec from the site path is in that same frame (see prior comment on get_site_body_and_anchor).
  2. Error visibility for bad site2: When site2 is specified but missing, the constraint is silently dropped (aside from an optional verbose print). For malformed MJCF this can be hard to notice. Consider raising a clearer error or at least including the constraint name in the warning to make debugging easier.

The overall structure looks good; I’d just verify the frame conventions and consider improving diagnostics around unresolved sites.


1043-1095: Site-based WELD constraints: anchor semantics and type conversion

The new site1 or site2 branch for <weld> covers:

  • site1(body1_idx, anchor_vec) via the site, or falls back to (body1_name, anchor) when site1 is absent.
  • site2body2_idx via the site, or falls back to body2_name.

A couple of details to validate:

  1. Anchor definition vs. MuJoCo semantics: add_equality_constraint_weld is documented as taking anchor “relative to body2”, while here:

    • When site1 is used, anchor_vec is derived from site1 (on body1 or world), not body2.
    • When only site2 is used, anchor_vec comes from the anchor attribute.

    Please confirm the intended frame of anchor in the Newton equality model and ensure it matches how _convert_to_mjc interprets eq_constraint_anchor when filling eq.data[0:3] for mjEQ_WELD. If the frame is world‑space, it may be fine; if it is body‑2‑local, you might need to convert site/world positions accordingly.

  2. torquescale type: torquescale is passed through as the raw string attribute value (same as the existing non‑site path). If add_equality_constraint_weld expects a float, consider normalizing here (float(torquescale)) for consistency and to fail fast on invalid inputs.

Functionally the structure is sound; I’d just make the anchor frame and torquescale typing explicit and verify them against MuJoCo/Newton weld semantics.

newton/tests/test_import_mjcf.py (1)

1669-1781: Extend site-based equality tests to cover names and anchors

These tests nicely exercise the new site-based CONNECT/WELD paths end‑to‑end (through SolverMuJoCo) and confirm neq and eq_type. They don’t currently check:

  • That eq_name1/eq_name2 resolve as expected (e.g., "world" for world sites, proper body names for site‑attached bodies), or
  • That eq_data’s position entries match the corresponding site positions.

Adding a couple of assertions per test (e.g., on solver.mj_model.eq_obj1id/eq_obj2id via mj_name2id, eq_name1/2, and eq_data[0:3] vs. the known site coordinates) would give much stronger coverage of the new get_body_name and get_site_body_and_anchor logic without significantly increasing test complexity.

📜 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 e5f6493 and 4dee562.

📒 Files selected for processing (3)
  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/_src/utils/import_mjcf.py
  • newton/tests/test_import_mjcf.py
🧰 Additional context used
🧠 Learnings (6)
📓 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: 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.
Learnt from: gdaviet
Repo: newton-physics/newton PR: 631
File: newton/examples/mpm/example_anymal_c_walk_on_sand.py:164-169
Timestamp: 2025-08-25T11:07:47.818Z
Learning: In Newton's MuJoCo solver implementation, setting TARGET_POSITION mode on floating base DOFs (first 6 DOFs) does not overconstrain the base - the solver handles this appropriately and maintains expected behavior for floating robots.
Learnt from: adenzler-nvidia
Repo: newton-physics/newton PR: 552
File: newton/_src/solvers/mujoco/solver_mujoco.py:0-0
Timestamp: 2025-08-18T15:56:26.587Z
Learning: In Newton's MuJoCo solver, when transforming joint axes from Newton's internal frame to MuJoCo's expected frame, use wp.quat_rotate(joint_rot, axis) not wp.quat_rotate_inv(joint_rot, axis). The joint_rot represents rotation from joint-local to body frame, so forward rotation is correct.
Learnt from: dylanturpin
Repo: newton-physics/newton PR: 806
File: newton/_src/sim/ik/ik_lbfgs_optimizer.py:638-648
Timestamp: 2025-09-22T21:03:18.367Z
Learning: The L-BFGS optimizer in newton/_src/sim/ik/ik_lbfgs_optimizer.py currently only supports additive updates, meaning it assumes n_coords == n_dofs. Velocity space integration for joints with mismatched coordinate/DOF dimensions (like free/ball joints) is planned for future work.
Learnt from: preist-nvidia
Repo: newton-physics/newton PR: 579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, Example.use_mujoco is a high-level attribute that controls whether to use MuJoCo solver vs other solvers (like XPBD), while SolverMuJoCo.use_mujoco_cpu controls the backend within MuJoCo (CPU vs Warp). These are separate concepts serving different purposes - the PR rename only applies to the solver parameter, not the Example class attributes.
Learnt from: dylanturpin
Repo: newton-physics/newton PR: 806
File: newton/_src/sim/ik/ik_lbfgs_optimizer.py:739-752
Timestamp: 2025-09-22T21:03:39.624Z
Learning: The L-BFGS optimizer in newton/_src/sim/ik/ik_lbfgs_optimizer.py currently intentionally only supports additive updates (assuming n_coords == n_dofs). Velocity space integration for joints with mismatched coordinate/DOF dimensions (like free/ball joints) is planned for future work and should not be flagged as an issue in current reviews.
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
Repo: newton-physics/newton PR: 579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, Example.use_mujoco is a high-level attribute that controls whether to use MuJoCo solver vs other solvers (like XPBD), while SolverMuJoCo.use_mujoco_cpu controls the backend within MuJoCo (CPU vs Warp). These are separate concepts serving different purposes - the PR rename only applies to the solver parameter, not the Example class attributes.

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.py
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
Repo: newton-physics/newton PR: 579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, there's a distinction between solver parameters and Example class attributes. The Example class can have its own use_mujoco attribute for controlling example-level behavior (like CUDA graphs, rendering logic), while the solver uses use_mujoco_cpu for backend selection. These serve different purposes and should not be conflated during API renames.

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.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-18T15:56:26.587Z
Learnt from: adenzler-nvidia
Repo: newton-physics/newton PR: 552
File: newton/_src/solvers/mujoco/solver_mujoco.py:0-0
Timestamp: 2025-08-18T15:56:26.587Z
Learning: In Newton's MuJoCo solver, when transforming joint axes from Newton's internal frame to MuJoCo's expected frame, use wp.quat_rotate(joint_rot, axis) not wp.quat_rotate_inv(joint_rot, axis). The joint_rot represents rotation from joint-local to body frame, so forward rotation is correct.

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.py
📚 Learning: 2025-08-12T05:17:34.423Z
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.

Applied to files:

  • newton/_src/utils/import_mjcf.py
🧬 Code graph analysis (3)
newton/_src/solvers/mujoco/solver_mujoco.py (1)
newton/_src/sim/joints.py (1)
  • EqType (82-97)
newton/_src/utils/import_mjcf.py (1)
newton/_src/sim/builder.py (3)
  • add_equality_constraint_connect (2683-2715)
  • key (410-412)
  • add_equality_constraint_weld (2751-2789)
newton/tests/test_import_mjcf.py (1)
newton/_src/sim/builder.py (1)
  • add_mjcf (1338-1431)
⏰ 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
🔇 Additional comments (2)
newton/_src/solvers/mujoco/solver_mujoco.py (1)

1686-1691: World body name handling for equality constraints looks correct

Mapping body_idx == -1 to "world" via get_body_name() avoids out-of-range indexing on model.body_key and aligns MuJoCo’s expectation that the world body is addressed by name "world". The refactor keeps behavior unchanged for regular bodies and should make world‑referencing CONNECT/WELD constraints robust.

Also applies to: 1698-1699, 1718-1719

newton/_src/utils/import_mjcf.py (1)

971-985: Clarify site transform frame used for anchors

get_site_body_and_anchor() pulls the anchor from builder.shape_transform[site_idx] and directly uses its first three components as a position. This assumes that:

  • builder.shape_transform for sites exposes translation in indices 0:3, and
  • that this translation is in the frame expected by equality constraints (same convention as the non‑site anchor attribute path).

If shape_transform is stored body‑local for non‑world bodies (which is common) while non‑site anchors are interpreted in world coordinates, this could make site‑based anchors inconsistent with body/anchor‑based constraints.

Please double‑check:

  1. The exact representation of builder.shape_transform for sites (world vs body‑local, and indexing layout).
  2. That eq_constraint_anchor is consumed in the same frame in _convert_to_mjc for both site‑based and non‑site equalities.

If necessary, adjust to compute a world‑space anchor (e.g., by composing the site’s local transform with the body’s world transform) so that site‑based and anchor‑based equalities are consistent.

@codecov
Copy link

codecov bot commented Dec 24, 2025

Codecov Report

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

Files with missing lines Patch % Lines
newton/_src/utils/import_mjcf.py 63.26% 18 Missing ⚠️

📢 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/_src/utils/import_mjcf.py (1)

971-985: Consider verifying that the retrieved shape is actually a site.

The function looks up site_name in builder.shape_key, which contains names for all shapes (collision, visual, and sites). If a collision or visual shape happens to have the same name as a site, this function could return incorrect data.

Consider adding a type check to verify the shape is actually a site, for example by checking builder.shape_geo_type[site_idx] or another indicator that distinguishes sites from other shapes.

💡 Suggested verification

Run the following script to check if there's a way to distinguish sites from other shapes in the builder:

#!/bin/bash
# Search for site-related attributes in the builder that could be used for type checking
rg -n "shape_geo_type|GeoType\.|add_site" --type=py -C 3
📜 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 4dee562 and d38b0f4.

📒 Files selected for processing (1)
  • newton/_src/utils/import_mjcf.py
🧰 Additional context used
🧠 Learnings (1)
📓 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: 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.
🧬 Code graph analysis (1)
newton/_src/utils/import_mjcf.py (1)
newton/_src/sim/builder.py (3)
  • add_equality_constraint_connect (2683-2715)
  • key (410-412)
  • add_equality_constraint_weld (2751-2789)
⏰ 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). (3)
  • GitHub Check: Run GPU Tests / Run GPU Unit Tests on AWS EC2
  • GitHub Check: Run GPU Benchmarks / Run GPU Benchmarks on AWS EC2
  • GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (2)
newton/_src/utils/import_mjcf.py (2)

996-1026: Site-based connect constraint implementation looks good.

The logic correctly handles the site-based connect constraint workflow:

  • Uses site1 to determine body1 and anchor
  • Optionally uses site2 to determine body2, falling back to body2_name
  • Proper error handling when sites are not found
  • Maintains backward compatibility with body-name-based constraints

1054-1098: Site-based weld constraint implementation looks good.

The logic correctly implements site-based weld constraints:

  • Uses site1 to determine body1 (if provided)
  • Uses site2 to determine body2 and anchor in body2's frame (if provided)
  • Properly parses relpose and creates the transform
  • Maintains backward compatibility with body-name-based constraints

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: 2

📜 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 a4cfdd1 and c110b74.

📒 Files selected for processing (2)
  • newton/_src/utils/import_mjcf.py
  • newton/tests/test_equality_constraints.py
🧰 Additional context used
🧠 Learnings (2)
📓 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: 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.
📚 Learning: 2025-08-12T18:04:06.287Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/_src/utils/import_usd.py:364-365
Timestamp: 2025-08-12T18:04:06.287Z
Learning: The quat_between_axes function in Newton accepts AxisType parameters, which includes strings like "X", "Y", "Z" as well as Axis enum values and integers. The function internally handles conversion using Axis.from_any(), so passing strings directly to quat_between_axes is valid and correct behavior.

Applied to files:

  • newton/_src/utils/import_mjcf.py
🪛 GitHub Actions: Pull Request
newton/tests/test_equality_constraints.py

[error] 1-1: MuJoCo solver compilation failed due to NaN/Inf or unstable state in QACC at DOF 0. Time = 0.0000.

🪛 Ruff (0.14.10)
newton/tests/test_equality_constraints.py

418-418: Local variable body_remap is assigned to but never used

Remove assignment to unused variable body_remap

(F841)

⏰ 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
🔇 Additional comments (10)
newton/_src/utils/import_mjcf.py (4)

29-29: LGTM!

The ShapeFlags import is correctly added to support site detection in the new helper function.


1019-1047: LGTM!

The site-based connect constraint logic correctly resolves both sites, uses the anchor from site1, and provides appropriate warnings when inputs are incomplete.


1096-1100: The default relpose is not identity—it's "0 1 0 0 0 0 0" (position offset of (0, 1, 0) with identity rotation).

The code at lines 1096-1100 parses the relpose XML attribute with the default "0 1 0 0 0 0 0", which doesn't represent an identity pose. This unusual default (position offset in the y-direction) suggests either a bug or an undocumented design choice. Verify that this default relpose is intentional and matches the expected behavior for site-based weld constraints in MuJoCo.


971-989: No issues found. The code correctly extracts the anchor position from shape_transform using direct indexing of the translation components ([0], [1], [2]), which is the standard pattern in the codebase.

newton/tests/test_equality_constraints.py (6)

82-125: LGTM!

The body-based robot creation helper is well-structured with clear documentation and correct constraint setup.


127-154: LGTM!

The site-based robot creation helper correctly mirrors the body-based version using MJCF with appropriate site definitions.


156-229: LGTM!

Comprehensive test that verifies constraint handling for both body-based and site-based scenarios across multiple worlds. The assertions properly validate constraint counts and body index remapping.


231-311: LGTM!

Well-structured helper that creates a chain suitable for testing collapse_fixed_joints with equality constraints. The returned info dict enables detailed verification of remapping behavior.


313-345: LGTM!

The site-based chain helper correctly creates an equivalent structure using MJCF. The absence of a joint equality constraint (compared to body-based) is appropriate since joint constraints don't support site-based definitions.


402-414: No evidence of pipeline failures or solver initialization issues related to this code. The search for the referenced error message "QACC at DOF" returns no results in the codebase, and the test itself is properly validating constraint counts after solver creation. The relpose value at line 340 is a benign constraint offset and not problematic.

Likely an incorrect or invalid review comment.

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)

1096-1100: Consider auto-computing relpose for site-based welds.

The current implementation uses the XML relpose attribute directly for site-based welds (defaulting to identity). In a site-based workflow, users might expect the relpose to be automatically derived from the relative transforms of site1 and site2 when not explicitly specified.

If this is intentional to maintain parity with the body-based path, consider documenting this behavior in verbose output.

newton/tests/test_equality_constraints.py (2)

132-185: Clarify test scenario naming.

The method is named _create_robot_site_based and docstring says "site-based equality constraints," but lines 176-184 use body indices directly (body1=base, body2=link2) rather than site-based constraint creation. The sites are added but not used by the constraints.

This appears to test models with sites present, not actual site-based constraint resolution. Consider renaming to _create_robot_with_sites or updating the docstring to clarify the test intent.


316-403: Consider reducing duplication between chain builders.

_create_chain_site_based is nearly identical to _create_chain_body_based, differing only in the addition of sites. While explicit test code can be valuable for clarity, this could be refactored to a single helper with an add_sites parameter to reduce maintenance burden.

Same observation as the robot helpers: the "site_based" name is misleading since constraints still use body indices directly.

📜 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 c110b74 and 942f073.

📒 Files selected for processing (2)
  • newton/_src/utils/import_mjcf.py
  • newton/tests/test_equality_constraints.py
🧰 Additional context used
🧠 Learnings (3)
📓 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: preist-nvidia
Repo: newton-physics/newton PR: 701
File: CONTRIBUTING.md:7-7
Timestamp: 2025-09-02T13:59:02.072Z
Learning: The newton-physics/newton-governance repository's CONTRIBUTING.md file contains both "Legal Requirements" (line 73, anchor #legal-requirements) and "Project Members" (line 108, anchor #project-members) sections that can be properly referenced from other repositories' CONTRIBUTING.md files.
📚 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/utils/import_mjcf.py
📚 Learning: 2025-08-12T18:04:06.287Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/_src/utils/import_usd.py:364-365
Timestamp: 2025-08-12T18:04:06.287Z
Learning: The quat_between_axes function in Newton accepts AxisType parameters, which includes strings like "X", "Y", "Z" as well as Axis enum values and integers. The function internally handles conversion using Axis.from_any(), so passing strings directly to quat_between_axes is valid and correct behavior.

Applied to files:

  • newton/_src/utils/import_mjcf.py
🧬 Code graph analysis (2)
newton/tests/test_equality_constraints.py (1)
newton/_src/sim/builder.py (4)
  • key (435-437)
  • add_equality_constraint_connect (2785-2817)
  • add_equality_constraint_joint (2819-2851)
  • add_equality_constraint_weld (2853-2891)
newton/_src/utils/import_mjcf.py (2)
newton/_src/geometry/flags.py (1)
  • ShapeFlags (30-48)
newton/_src/sim/builder.py (3)
  • add_equality_constraint_connect (2785-2817)
  • key (435-437)
  • add_equality_constraint_weld (2853-2891)
⏰ 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 GPU Benchmarks / Run GPU Benchmarks on AWS EC2
🔇 Additional comments (8)
newton/_src/utils/import_mjcf.py (4)

29-29: LGTM!

The ShapeFlags import is correctly added to support the site detection logic via ShapeFlags.SITE flag check in the new get_site_body_and_anchor helper.


971-989: LGTM!

The helper correctly validates site existence and type before extracting body index and anchor position. The guard site_name not in builder.shape_key prevents ValueError from index() call.


1019-1047: LGTM!

The site-based connect implementation correctly:

  • Resolves both sites and validates their existence
  • Uses site1's anchor (appropriate for connect constraints where anchor is on body1)
  • Guards the warning with if verbose
  • Uses continue to skip invalid constraints gracefully

1085-1118: LGTM!

The site-based weld implementation correctly:

  • Uses site2's anchor position (matching the builder API where anchor is "relative to body2")
  • Properly parses and applies relpose transformation
  • Guards all warnings with if verbose (past review issue has been addressed)
newton/tests/test_equality_constraints.py (4)

82-130: LGTM!

The helper correctly creates a robot with body-based equality constraints, establishing a clear baseline for comparison with the site-based variant.


187-229: LGTM!

Good parameterized test structure using subTest to verify constraint handling in both scenarios. The assertions correctly validate that:

  • Newton model aggregates constraints across all worlds (6 total)
  • MuJoCo solver with separate_worlds=True only includes per-world constraints (2)

231-314: LGTM!

Comprehensive helper that returns both the builder and a detailed info dictionary for verification. This enables thorough testing of index remapping after collapse_fixed_joints.


405-497: LGTM!

Comprehensive test that verifies:

  • Body count reduction after collapse
  • Constraint preservation
  • Valid body/joint index bounds
  • Correct remapping of constraint references
  • Proper transformation of anchor points and relpose

Note: The body_remap variable (line 443) is now used at lines 451-453, so the previous review comment about it being unused no longer applies.

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 (2)
newton/tests/test_equality_constraints.py (2)

124-129: Consider using joint variables instead of hardcoded indices.

The hardcoded indices joint1=1 and joint2=2 work correctly, but using the actual joint variable indices would be clearer and more maintainable:

🔎 Proposed fix
         robot.add_equality_constraint_joint(
-            joint1=1,
-            joint2=2,
+            joint1=joint2,
+            joint2=joint3,
             polycoef=[1.0, -1.0, 0, 0, 0],
             key="joint_constraint",
         )

179-184: Same hardcoded index issue applies here.

For consistency with the suggestion on _create_robot_body_based, consider using joint variables:

🔎 Proposed fix
         robot.add_equality_constraint_joint(
-            joint1=1,
-            joint2=2,
+            joint1=joint2,
+            joint2=joint3,
             polycoef=[1.0, -1.0, 0, 0, 0],
             key="joint_constraint",
         )
📜 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 c110b74 and 942f073.

📒 Files selected for processing (2)
  • newton/_src/utils/import_mjcf.py
  • newton/tests/test_equality_constraints.py
🧰 Additional context used
🧠 Learnings (2)
📓 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: preist-nvidia
Repo: newton-physics/newton PR: 701
File: CONTRIBUTING.md:7-7
Timestamp: 2025-09-02T13:59:02.072Z
Learning: The newton-physics/newton-governance repository's CONTRIBUTING.md file contains both "Legal Requirements" (line 73, anchor #legal-requirements) and "Project Members" (line 108, anchor #project-members) sections that can be properly referenced from other repositories' CONTRIBUTING.md files.
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.
📚 Learning: 2025-08-12T18:04:06.287Z
Learnt from: nvlukasz
Repo: newton-physics/newton PR: 519
File: newton/_src/utils/import_usd.py:364-365
Timestamp: 2025-08-12T18:04:06.287Z
Learning: The quat_between_axes function in Newton accepts AxisType parameters, which includes strings like "X", "Y", "Z" as well as Axis enum values and integers. The function internally handles conversion using Axis.from_any(), so passing strings directly to quat_between_axes is valid and correct behavior.

Applied to files:

  • newton/_src/utils/import_mjcf.py
🧬 Code graph analysis (1)
newton/_src/utils/import_mjcf.py (2)
newton/_src/geometry/flags.py (1)
  • ShapeFlags (30-48)
newton/_src/sim/builder.py (3)
  • add_equality_constraint_connect (2785-2817)
  • key (435-437)
  • add_equality_constraint_weld (2853-2891)
🔇 Additional comments (7)
newton/_src/utils/import_mjcf.py (3)

971-989: LGTM - Site lookup helper is well-implemented.

The function correctly guards against missing sites and non-site shapes, extracts the anchor position from the shape transform, and provides helpful verbose warnings.


1019-1047: LGTM - Site-based connect constraint handling.

The implementation correctly resolves both sites, uses site1's position as the anchor point (matching MuJoCo semantics), and properly guards the warning with the verbose flag.


1085-1118: LGTM - Site-based weld constraint handling.

The implementation correctly resolves both sites, uses anchor from site2 (matching MuJoCo semantics where anchor is relative to body2), and the warning is now properly guarded by if verbose:. The relpose handling follows the same pattern as body-based welds.

newton/tests/test_equality_constraints.py (4)

187-229: LGTM - Well-structured test for constraint duplication.

Good use of subTest for testing both scenarios, and clear assertions for expected constraint counts in both Newton model and MuJoCo solver contexts.


231-314: LGTM - Well-designed helper with comprehensive info dict.

The helper returns all relevant indices and transforms needed for detailed post-collapse validation, enabling thorough testing of constraint remapping.


316-403: LGTM - Consistent with body-based helper.

The site-based chain helper follows the same structure as the body-based version, adding sites to each link while maintaining the same info dict return format for unified validation.


405-497: LGTM - Comprehensive post-collapse validation.

The test thoroughly verifies constraint remapping including body/joint index updates, merged body tracking, and proper transformation of anchor points and relpose. The past review comment about unused body_remap no longer applies as it's now used in the validation logic (lines 451-453).

@jvonmuralt jvonmuralt marked this pull request as ready for review January 8, 2026 11:23
@jvonmuralt jvonmuralt linked an issue Jan 8, 2026 that may be closed by this pull request
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.

Change looks good, could you please add a test for this?

@jvonmuralt
Copy link
Member Author

Change looks good, could you please add a test for this?

Thanks, test_multiple_constraints does check that constraints with sites are satisfied. I added now more explicit testing that parsing worked correctly for them

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 (2)
newton/tests/test_equality_constraints.py (2)

42-58: Add error handling for missing constraints or use more descriptive assertions.

The test directly calls eq_keys.index("c_site") and eq_keys.index("w_site") without verifying these constraints exist in the loaded asset. If the asset file is missing, malformed, or doesn't contain these constraints, the test will fail with a generic ValueError rather than a clear assertion message.

Consider adding explicit assertions before indexing:

self.assertIn("c_site", eq_keys, "Expected 'c_site' constraint not found in model")
self.assertIn("w_site", eq_keys, "Expected 'w_site' constraint not found in model")

Alternatively, use dictionary-style access with .get() and assert the result is not None, or wrap the index calls in a try-except with a more descriptive error message.


82-82: Consider adding a comment explaining the expected constraint count.

The assertion expects exactly 5 equality constraints, but there's no context in the test about why 5 is the expected value. Adding a brief comment would make the test more maintainable when the asset file changes.

Example:

# constraints.xml contains: c_site, w_site, and 3 others
self.assertEqual(self.solver.mj_model.eq_type.shape[0], 5)
📜 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 942f073 and f9833e8.

📒 Files selected for processing (1)
  • newton/tests/test_equality_constraints.py
🧰 Additional context used
🧠 Learnings (1)
📓 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: preist-nvidia
Repo: newton-physics/newton PR: 701
File: CONTRIBUTING.md:7-7
Timestamp: 2025-09-02T13:59:02.072Z
Learning: The newton-physics/newton-governance repository's CONTRIBUTING.md file contains both "Legal Requirements" (line 73, anchor #legal-requirements) and "Project Members" (line 108, anchor #project-members) sections that can be properly referenced from other repositories' CONTRIBUTING.md files.
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.
⏰ 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 (2)
newton/tests/test_equality_constraints.py (2)

97-224: LGTM! Comprehensive test for separate_worlds constraint handling.

The test thoroughly verifies:

  • Correct constraint count in both Newton and MuJoCo models
  • Proper index remapping for bodies and joints across multiple worlds
  • That separate_worlds=True prevents constraint duplication

The logic and math for offset calculations (3 bodies/joints per world) are correct.


225-368: The test expectation is correct. The anchor should not be transformed in this case.

The implementation in collapse_fixed_joints (builder.py lines 3363-3385) explicitly handles WELD constraints differently depending on which body is merged:

  • When body1_was_merged (link2 merges into link1): only relpose is transformed, anchor remains unchanged
  • When body2_was_merged: both anchor and relpose are transformed

In your test, link2 (body1) is merged into link1, so the first condition applies. The anchor is defined in the body1 (link2) frame and semantically belongs to that body, so it remains unchanged when body1 is merged. Only the relative pose (relpose) needs transformation to maintain the spatial relationship between the merged body and body2.

The test at line 299 correctly expects expected_anchor = original_anchor, which matches the implementation behavior.

@adenzler-nvidia
Copy link
Member

Thanks for the test. Could you merge latest main such the the dependencies are correct for the tests? Otherwise I think it looks good.

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/_src/solvers/mujoco/solver_mujoco.py (1)

1733-1748: Consider using get_body_name helper for consistency.

The loop-closure joints (lines 1737, 1738, 1745, 1746) directly access model.body_key[joint_parent[j]] and model.body_key[joint_child[j]] without using the get_body_name helper. While loop joints may not reference the world body in practice, using the helper would:

  1. Be consistent with explicit equality constraint handling (lines 1700-1701, 1720-1721)
  2. Provide defensive handling if world-referenced loop joints are ever encountered
  3. Make the code's intent clearer
♻️ Proposed refactor for consistency
         eq = spec.add_equality(objtype=mujoco.mjtObj.mjOBJ_BODY)
         eq.type = mujoco.mjtEq.mjEQ_CONNECT
         eq.active = True
-        eq.name1 = model.body_key[joint_parent[j]]
-        eq.name2 = model.body_key[joint_child[j]]
+        eq.name1 = get_body_name(joint_parent[j])
+        eq.name2 = get_body_name(joint_child[j])
         eq.data[0:3] = joint_parent_xform[j][:3]
         mjc_eq_to_newton_jnt[eq.id] = j

         eq = spec.add_equality(objtype=mujoco.mjtObj.mjOBJ_BODY)
         eq.type = mujoco.mjtEq.mjEQ_CONNECT
         eq.active = True
-        eq.name1 = model.body_key[joint_child[j]]
-        eq.name2 = model.body_key[joint_parent[j]]
+        eq.name1 = get_body_name(joint_child[j])
+        eq.name2 = get_body_name(joint_parent[j])
         eq.data[0:3] = joint_child_xform[j][:3]
         mjc_eq_to_newton_jnt[eq.id] = j
📜 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 f9833e8 and 56e952d.

📒 Files selected for processing (1)
  • newton/_src/solvers/mujoco/solver_mujoco.py
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
Repo: newton-physics/newton PR: 579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, Example.use_mujoco is a high-level attribute that controls whether to use MuJoCo solver vs other solvers (like XPBD), while SolverMuJoCo.use_mujoco_cpu controls the backend within MuJoCo (CPU vs Warp). These are separate concepts serving different purposes - the PR rename only applies to the solver parameter, not the Example class attributes.

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.py
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
Repo: newton-physics/newton PR: 579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, there's a distinction between solver parameters and Example class attributes. The Example class can have its own use_mujoco attribute for controlling example-level behavior (like CUDA graphs, rendering logic), while the solver uses use_mujoco_cpu for backend selection. These serve different purposes and should not be conflated during API renames.

Applied to files:

  • newton/_src/solvers/mujoco/solver_mujoco.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
⏰ 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). (4)
  • GitHub Check: Run GPU Tests / Run GPU Unit Tests on AWS EC2
  • GitHub Check: Run GPU Benchmarks / Run GPU Benchmarks on AWS EC2
  • GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
  • GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (3)
newton/_src/solvers/mujoco/solver_mujoco.py (3)

1688-1692: LGTM! Clean helper for world body name resolution.

The helper correctly handles the special case of the world body (index -1) by returning "world" instead of attempting to access model.body_key[-1], which would cause incorrect behavior. The implementation is straightforward and well-documented.


1700-1701: Correct usage of the helper for CONNECT constraints.

Properly uses get_body_name to handle both body references, ensuring that world body (-1) is correctly resolved to "world" string for MuJoCo constraint creation.


1720-1721: Correct usage of the helper for WELD constraints.

Consistent with the CONNECT constraint handling above, properly resolving body names including world body references.

@adenzler-nvidia adenzler-nvidia added this pull request to the merge queue Jan 12, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 12, 2026
@adenzler-nvidia adenzler-nvidia added this pull request to the merge queue Jan 12, 2026
Merged via the queue into newton-physics:main with commit c67b6fb Jan 12, 2026
20 checks passed
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.

Implement site-based equality constraints

3 participants