Integrate environment groups with collision detection system #605#615
Conversation
…hysics#605 Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
…ion-detection-system
📝 WalkthroughWalkthroughAdds environment-aware collision filtering: new test_environment_and_group_pair predicate; broadphase kernels (NxN, SAP) and soft-contact kernel accept per-entity environment groups; ModelBuilder and Style3D builder APIs updated to use environment indices; tests and examples adjusted to exercise environment semantics (global = -1). Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Builder as ModelBuilder
participant Broadphase as BroadPhase (NxN / SAP)
participant Common as test_environment_and_group_pair
participant Kernel as create_soft_contacts
Builder->>Broadphase: launch(..., collision_group[], shape_group[])
Broadphase->>Broadphase: check AABB overlap
Broadphase->>Common: test_environment_and_group_pair(envA, envB, grpA, grpB)
alt envs compatible & groups compatible
Broadphase-->>Builder: emit candidate pair
else incompatible
Broadphase-->>Builder: skip pair
end
Builder->>Kernel: create_soft_contacts(..., particle_group[], shape_group[])
Kernel->>Kernel: if particle_env != -1 && shape_env != -1 && differ -> return (no contact)
Kernel-->>Builder: produce contact(s) if not filtered
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
📜 Recent review detailsConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
newton/_src/geometry/broad_phase_sap.py (1)
342-353: Docstring says geom_shape_groups “can be None” but code requires an arrayEither handle None by substituting a -1 array, or remove the “can be None” note. Handling None is more user-friendly. Suggestion below.
def launch( self, @@ - geom_shape_groups: wp.array, # Environment group ID per box (dtype=int, ndim=1) + geom_shape_groups: wp.array | None, # Environment group ID per box (dtype=int, ndim=1) @@ - The method also handles collision filtering based on both environment groups and collision groups. + The method also handles collision filtering based on both environment groups and collision groups. @@ - Can be None if environment groups are not used. + If None, all elements default to the global environment (-1).And initialize default -1 groups near the kernel launch:
@@ - wp.launch( + # Default to global environment if not provided + if geom_shape_groups is None: + # requires: import numpy as np at module scope + geom_shape_groups = wp.array(np.full(geom_count, -1, dtype=np.int32), dtype=wp.int32) + + wp.launch( kernel=_sap_broadphase_kernel,Add missing import at the top if needed:
-import warp as wp +import warp as wp +import numpy as np
🧹 Nitpick comments (15)
newton/tests/test_mujoco_solver.py (1)
234-241: Make the ground plane global so both environments collide with it.With environment-group filtering, shapes inherit the builder’s current_env_group. The plane is added before the per-env sub-builders and will default to the current (non-global) group, so only env 0 collides with it. Mark it as global (-1) to share across all envs.
Apply this diff near the plane creation:
- self.builder.add_shape_plane() + # Share the plane across all environments + prev_env = self.builder.current_env_group + self.builder.current_env_group = -1 + self.builder.add_shape_plane() + self.builder.current_env_group = prev_envnewton/examples/example_robot_manipulating_cloth.py (1)
208-215: API change adoption looks correct.Switching to builder.add_builder(articulation_builder, xform) matches the new signature. If this example later grows to multi-environment usage, consider passing environment explicitly or setting current_env_group accordingly to avoid unintended cross-env contacts.
newton/_src/geometry/broad_phase_common.py (1)
89-120: Clarify docstring to avoid conflating env -1 with collision-group negatives.Because collision_group negatives have special “collide with all except same negative” semantics, it’s worth explicitly stating that env_group -1 is unrelated to collision_group -1 and only means “global” for environments.
Apply this doc tweak:
- Environment groups define which simulation environment an entity belongs to: + Environment groups define which simulation environment an entity belongs to: - Group -1: Global entities that collide with all environments - Groups 0, 1, 2, ...: Environment-specific entities @@ - 3. Within the same environment, collision groups determine interactions + 3. Within the same environment (or if one side is global), collision groups determine interactions + + Note: + env_group values are independent from collision_group values. In particular, + env_group == -1 indicates a global environment membership and must not be + confused with collision_group < 0 semantics.newton/_src/geometry/kernels.py (1)
646-654: Prefer explicit 32-bit types for group arrays (GPU portability).Using dtype=int can map to 64-bit on some hosts. Declare particle_group and shape_group as wp.int32 to match other integer buffers and avoid implicit casts on device backends.
Apply this change to the kernel signature:
- particle_group: wp.array(dtype=int), # Environment groups for particles + particle_group: wp.array(dtype=wp.int32), # Environment groups for particles @@ - shape_group: wp.array(dtype=int), # Environment groups for shapes + shape_group: wp.array(dtype=wp.int32), # Environment groups for shapesnewton/_src/sim/style3d/builder_style3d.py (2)
108-117: Docstring/API alignment for environment parameterGood addition. Minor mismatch: the parent’s doc explains auto-increment vs. explicit environment behavior; here it still says “incremented by 1”. Align the wording with ModelBuilder.add_builder to avoid confusion.
- update_num_env_count (bool): if True, the number of environments is incremented by 1. - environment (int | None): environment group index to assign to ALL entities from this builder. + update_num_env_count (bool): if True, the number of environments is updated appropriately. + environment (int | None): Environment group index to assign to ALL entities from this builder. + If None, uses the current environment count as the group index. Use -1 for global entities.
119-124: Type narrowness onbuilder(consider widening to base type)
builder: Style3DModelBuilderlimits composition with plain ModelBuilder sub-builders. Consider typing asModelBuilder(or a union) since you delegate tosuper().add_builder(...)anyway.- builder: Style3DModelBuilder, + builder: ModelBuilder,newton/_src/sim/builder.py (2)
742-755: Offsetting collision pairs/group map before shape arrays are extended is safe—but fragile if re-ordered laterUsing
shape_count_offset = self.shape_countbefore extending shape arrays is correct becauseshape_countis based onshape_type, which hasn’t been extended yet. Add a short comment to future-proof against accidental reordering.- shape_count_offset = self.shape_count + # NOTE: shape_count is based on shape_type length, which hasn't been extended yet, + # so this offset points to the first index of the soon-to-be-appended shapes. + shape_count_offset = self.shape_count
4070-4099: Broadphase-precontact filtering by environment is correct and matches semantics
- Skips cross-env pairs unless one side is global (-1).
- Applies both for intra-group combinations and group -1 vs. others.
Consider reusing the same predicate as the kernels for parity (e.g.,test_environment_and_group_pair) to keep one source of truth, but current logic is equivalent.- # Skip shapes from different environments (unless one is global) - if env1 != -1 and env2 != -1 and env1 != env2: - continue + # Skip shapes from different environments (unless one is global) + # Equivalent to test_environment_and_group_pair(env1, env2, _, _) + if env1 != -1 and env2 != -1 and env1 != env2: + continuenewton/tests/test_broad_phase.py (2)
129-131: Passing shape_groups as global (-1) is a good baselineThis keeps parity with the numpy reference (which ignores envs). Consider adding a tiny focused test with two distinct env IDs to assert cross-env filtering at the broadphase level (there’s a separate test file, but a smoke check here helps).
386-388: SAP path parity with NXNSame comment as above; baseline use is fine. A small follow-up asserting env-separated filtering would increase coverage symmetry.
newton/_src/geometry/broad_phase_nxn.py (4)
98-106: Filter on env+collision groups before AABBFunctionally correct. If you want a minor perf tweak, you could first do the cheap AABB overlap then group filtering, but difference is likely negligible vs. memory access here.
151-156: Launch signature: specify dtype for geom_shape_groups in the APITo match other params, annotate dtype in the signature comment and enforce at runtime (assert) for clearer errors.
- geom_shape_groups: wp.array, # Environment group ID per box (dtype=int, ndim=1) + geom_shape_groups: wp.array(dtype=int, ndim=1), # Environment group ID per box (-1=global)
170-179: Docstring mentions collision-group-only testUpdate the docstring to reference the env+group predicate and the new parameter.
- 2. Their collision groups allow interaction (determined by test_group_pair()) + 2. Their environment and collision groups allow interaction + (determined by test_environment_and_group_pair())
187-201: Optional: accept None for geom_shape_groups (default to global)SAP’s doc mentions None (see separate comment); for API parity, you could optionally accept None here and substitute a -1-filled array. Not required if you prefer stricter typing.
- wp.launch( + # (Optional) enable if you want to accept None: + # if geom_shape_groups is None: + # geom_shape_groups = wp.array(np.full(geom_count, -1, dtype=np.int32), dtype=wp.int32) + wp.launch(newton/_src/geometry/broad_phase_sap.py (1)
458-481: Kernel invocation includes env groups—good. Add a dtype assertion for clearer failures.- wp.launch( + # Defensive check for dtype/shape + assert geom_shape_groups.dtype == wp.int32 and geom_shape_groups.ndim == 1, \ + "geom_shape_groups must be int32, 1D (values -1 for global or >=0 per environment)" + wp.launch(
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (12)
newton/_src/geometry/broad_phase_common.py(1 hunks)newton/_src/geometry/broad_phase_nxn.py(6 hunks)newton/_src/geometry/broad_phase_sap.py(8 hunks)newton/_src/geometry/kernels.py(2 hunks)newton/_src/sim/builder.py(4 hunks)newton/_src/sim/collide.py(1 hunks)newton/_src/sim/style3d/builder_style3d.py(1 hunks)newton/examples/example_robot_manipulating_cloth.py(1 hunks)newton/tests/test_broad_phase.py(2 hunks)newton/tests/test_environment_group_collision.py(1 hunks)newton/tests/test_model.py(0 hunks)newton/tests/test_mujoco_solver.py(1 hunks)
💤 Files with no reviewable changes (1)
- newton/tests/test_model.py
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.042Z
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.
📚 Learning: 2025-08-12T17:51:37.474Z
Learnt from: nvlukasz
PR: newton-physics/newton#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_environment_group_collision.pynewton/_src/geometry/kernels.pynewton/_src/sim/collide.pynewton/tests/test_broad_phase.pynewton/_src/geometry/broad_phase_nxn.pynewton/_src/geometry/broad_phase_sap.py
📚 Learning: 2025-08-20T18:02:36.042Z
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.042Z
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/_src/sim/builder.py
🧬 Code graph analysis (4)
newton/tests/test_environment_group_collision.py (2)
newton/_src/sim/builder.py (5)
ModelBuilder(67-4107)ShapeConfig(142-194)finalize(3719-4055)add_particle(2687-2721)add_builder(589-874)newton/_src/sim/collide.py (2)
device(270-271)collide(140-267)
newton/tests/test_mujoco_solver.py (2)
newton/_src/sim/builder.py (1)
add_builder(589-874)newton/_src/sim/style3d/builder_style3d.py (1)
add_builder(103-133)
newton/_src/sim/style3d/builder_style3d.py (1)
newton/_src/sim/builder.py (1)
add_builder(589-874)
newton/examples/example_robot_manipulating_cloth.py (2)
newton/_src/sim/builder.py (1)
add_builder(589-874)newton/_src/sim/style3d/builder_style3d.py (1)
add_builder(103-133)
⏰ 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 Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (21)
newton/tests/test_mujoco_solver.py (1)
237-241: API migration: add_builder call updated correctly.Dropping separate_collision_group aligns with the new environment-group semantics. Call site looks good.
newton/_src/geometry/broad_phase_common.py (1)
89-120: Environment-aware collision predicate is correct and minimal.The early-return on different non-global envs followed by test_group_pair preserves intended semantics (global -1 interacts with all, per-env isolation otherwise).
newton/_src/geometry/kernels.py (1)
674-681: Early env-group rejection is in the right place.The check is before any expensive geometry work, after the standard ACTIVE/COLLIDE_PARTICLES gates. This minimizes cost for cross-env pairs.
newton/_src/sim/collide.py (2)
160-180: create_soft_contacts call site updated correctly (ordering matches kernel).particle_group is passed after particle_flags; shape_group after shape_source_ptr; shape_flags after shape_count. Matches the kernel signature.
160-180: ✅ Verified create_soft_contacts kernel signatureAll references to
create_soft_contactshave been located, and the sole invocation innewton/_src/sim/collide.py(lines 160–180) passes the 15 inputs in the exact order of the updated kernel definition, with the outputs list matching the seven output parameters. No other call sites were found.newton/_src/sim/builder.py (5)
603-607: Environment-group semantics: clear and correctThe added notes accurately capture the new rules (global -1 collides with all; other envs isolated; collision groups preserved). Nice.
640-646: Group index selection covers all cases; edge-case sanityLogic for selecting
group_idxwith/withoutupdate_num_env_countis sound and matches the doc. No changes needed.
756-782: Uniform environment assignment across entity kindsOverriding particle/body/shape/joint/articulation groups to
current_env_groupis consistent and easy to reason about. LGTM.
863-872: num_envs update correctly skips globals and respects explicit env indicesUpdate logic is correct for auto-increment vs. explicit indexing. No change.
589-595: No remaining references toseparate_collision_groupI ran a global search for any usages of the removed
separate_collision_groupparameter and found none, so the change to theadd_builder()signature does not break any call sites.newton/_src/geometry/broad_phase_nxn.py (1)
18-19: Importing combined predicate is the right callSwitch to
test_environment_and_group_paircentralizes the policy. Good.newton/_src/geometry/broad_phase_sap.py (2)
18-23: Good centralization on common helpersImport set is tidy and consistent with NxN.
269-277: SAP pair filtering respects env+group policyCorrectly mirrors NxN behavior. LGTM.
newton/tests/test_environment_group_collision.py (8)
1-22: LGTM - Well-structured test module setup.The imports and module structure look good. The import of
test_environment_and_group_pairfrom the internal module is appropriate for testing the environment-aware collision filtering functionality.
24-30: LGTM - Standard test class setup.The test class follows unittest conventions with proper setup method for device initialization.
31-86: LGTM - Comprehensive shape collision filtering test.This test effectively validates the core environment group functionality:
- Shapes in different environments (0 vs 1) correctly avoid collision
- Global environment (-1) properly collides with all environments
- The test assertions verify both negative (no cross-environment collision) and positive (global collisions) cases
The test design aligns well with the PR objectives for environment-aware collision detection.
87-122: LGTM - Thorough particle-shape collision filtering test.This test validates the particle collision filtering works correctly with environment groups:
- Particle in environment 0 doesn't collide with shape in environment 1
- Particle correctly collides with global shape (environment -1)
- Proper verification of soft contact counts and contact shape indices
This addresses the particle collision aspect mentioned in the PR objectives.
123-178: LGTM - Excellent test for add_builder environment assignment.This test thoroughly validates the
add_builderfunctionality:
- Environment groups are correctly assigned when adding builders
- Both shape and body groups are properly set
- Collision groups are preserved during the builder merge process
- The test covers the breaking API change mentioned in the PR where
add_buildernow handles environment groupsThe assertions comprehensively verify the expected behavior described in the PR objectives.
179-260: LGTM - Comprehensive mixed collision and environment groups test.This is an excellent stress test that validates the interaction between collision groups and environment groups:
- Tests multiple environments with various collision group combinations
- Verifies that environment isolation takes precedence over collision group compatibility
- Only global environment (-1) can cross environment boundaries
- The expected pairs calculation and verification is thorough and correct
This test ensures the complex interaction logic works as intended per the PR design.
262-299: LGTM - Thorough broadphase kernel testing.This test validates the low-level
test_environment_and_group_pairfunction with comprehensive test cases:
- Covers same environment scenarios with different collision group combinations
- Tests cross-environment isolation
- Validates global environment (-1) behavior
- Uses Warp kernels to test the actual GPU/CPU function implementation
The test cases cover all the important edge cases and align with the broadphase kernel changes mentioned in the PR.
301-304: LGTM - Proper test runner setup.The kernel cache clearing and main runner setup is appropriate for Warp-based tests.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…ion-detection-system
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
newton/_src/sim/builder.py (1)
4217-4224: Fix: constructing wp.array(vec2i) from an empty Python list can error — emit a shaped (0,2) array.With environment filtering, it’s common to end up with zero contact pairs. Creating a
wp.array(..., dtype=wp.vec2i)from an empty list (no shape) is fragile. Emit an(0, 2)int32 ndarray first.Apply:
- model.shape_contact_pairs = wp.array(np.array(contact_pairs), dtype=wp.vec2i, device=model.device) - model.shape_contact_pair_count = len(contact_pairs) + if contact_pairs: + pairs_np = np.asarray(contact_pairs, dtype=np.int32).reshape(-1, 2) + else: + pairs_np = np.empty((0, 2), dtype=np.int32) + model.shape_contact_pairs = wp.array(pairs_np, dtype=wp.vec2i, device=model.device) + model.shape_contact_pair_count = int(pairs_np.shape[0])
🧹 Nitpick comments (2)
newton/_src/sim/builder.py (2)
665-669: Doc nit: spell out world-attached (-1 body) same-body filtering to avoid confusion with “global collides with all.”The bullets are accurate but can be misread. Global env-group (-1) shapes still won’t collide with other shapes attached to the same world body (-1) because same-body collisions are filtered. Calling this out here prevents confusion.
Proposed doc tweak:
Environment groups automatically handle collision filtering between different environments: - Entities from different environments (except -1) do not collide with each other - Global entities (group -1) collide with all environments - Collision groups from the source builder are preserved as-is for fine-grained collision control within each environment +Note: Shapes attached to the world body (body == -1) do not collide with other shapes on the world body due to same‑body +collision filtering. This remains true even when those shapes are in the global environment (-1).
4186-4193: Unify env+group predicate with the kernel-side helper to prevent drift.These blocks re-implement the environment compatibility rule inline. Since the PR introduced
test_environment_and_group_pair(envA, envB, groupA, groupB)for kernels, reuse it here to keep CPU-side prefiltering in lockstep with GPU semantics.Minimal change:
+from ..geometry.broad_phase_common import test_environment_and_group_pair @@ - # Check environment groups - env1 = self.shape_group[s1] if s1 < len(self.shape_group) else -1 - env2 = self.shape_group[s2] if s2 < len(self.shape_group) else -1 - - # Skip shapes from different environments (unless one is global) - if env1 != -1 and env2 != -1 and env1 != env2: - continue + env1 = self.shape_group[s1] if s1 < len(self.shape_group) else -1 + env2 = self.shape_group[s2] if s2 < len(self.shape_group) else -1 + grp1 = self.shape_collision_group[s1] + grp2 = self.shape_collision_group[s2] + if not test_environment_and_group_pair(env1, env2, grp1, grp2): + continue @@ - # Check environment groups - env1 = self.shape_group[s1] if s1 < len(self.shape_group) else -1 - env2 = self.shape_group[s2] if s2 < len(self.shape_group) else -1 - - # Skip shapes from different environments (unless one is global) - if env1 != -1 and env2 != -1 and env1 != env2: - continue + env1 = self.shape_group[s1] if s1 < len(self.shape_group) else -1 + env2 = self.shape_group[s2] if s2 < len(self.shape_group) else -1 + grp1 = self.shape_collision_group[s1] + grp2 = self.shape_collision_group[s2] + if not test_environment_and_group_pair(env1, env2, grp1, grp2): + continueNote: keep the cross-product with collision-group -1; the helper only answers “allowed?”, not “which sets to iterate.”
Also applies to: 4209-4216
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
newton/_src/sim/builder.py(4 hunks)newton/_src/sim/collide.py(1 hunks)newton/_src/sim/style3d/builder_style3d.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- newton/_src/sim/collide.py
- newton/_src/sim/style3d/builder_style3d.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.042Z
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.
📚 Learning: 2025-08-20T18:02:36.042Z
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.042Z
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/_src/sim/builder.py
🧬 Code graph analysis (1)
newton/_src/sim/builder.py (1)
newton/_src/geometry/flags.py (1)
ShapeFlags(30-42)
⏰ 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 Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
…ion-detection-system
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
newton/_src/sim/builder.py (1)
807-811: Canonicalize filter pairs on builder merge to avoid missed filters
find_shape_contact_pairs()canonicalizes pairs as (min, max) before lookup. Merging non-canonical pairs here means filters won’t match and undesired contact pairs can slip through, especially after replication/merging multiple builders.Apply canonicalization during insertion:
- shape_count_offset = self.shape_count - for i, j in builder.shape_collision_filter_pairs: - self.shape_collision_filter_pairs.add((i + shape_count_offset, j + shape_count_offset)) + shape_count_offset = self.shape_count + for i, j in builder.shape_collision_filter_pairs: + a = i + shape_count_offset + b = j + shape_count_offset + if a > b: + a, b = b, a + self.shape_collision_filter_pairs.add((a, b))Also consider normalizing other insertion sites (e.g., same-body and parent–child insertions) for consistency; see note in the follow-up comment below.
Run this quick scan to find other non-canonical insertions of
shape_collision_filter_pairs.add:#!/bin/bash # Show all call sites with context and highlight obvious canonicalization. rg -nC3 -P '\bshape_collision_filter_pairs\.add\s*\(' newton/_src/sim/builder.py # Heuristic search for min/max normalization near these sites. rg -nC1 -P '\b(min|max)\s*\(' newton/_src/sim/builder.py
🧹 Nitpick comments (2)
newton/_src/sim/builder.py (2)
4190-4198: Environment filtering in contact-pair generation: logic is correct; consider small cleanup
- The predicate “skip when env1 != -1 and env2 != -1 and env1 != env2” implements “same env or either is global,” which matches the PR’s spec.
- Minor: the defensive indexing defaulting to -1 will silently treat absent env-tags as global. If that’s not expected, consider asserting lengths match
shape_countduring finalize.Optional micro-refactor to reduce duplication in both loops:
- Extract a tiny predicate:
same_env_or_global = (env1 == -1) or (env2 == -1) or (env1 == env2)Then use
if not same_env_or_global: continue.Also, leveraging the retrieved learning that world-attached shapes (body == -1) don’t collide with each other automatically, you can save work by pruning world–world pairs here:
- Early-continue when both
self.shape_body[s1] == -1 and self.shape_body[s2] == -1.This can reduce contact-pair list size and downstream kernel work without changing behavior.
If you want, I can draft a follow-up diff that adds a local
_same_env_or_global(env1, env2)predicate and a world–world pruning check in both loops.
4205-4225: Global collision group (-1) cross-pairing looks right; consider DRY-ing the predicateThe special-case loop for
group != -1vs-1is correct and respects env rules. To reduce duplication and the risk of drift, consider factoring the shared flag/shape/env checks into a local helper used by both loops.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
newton/_src/sim/builder.py(5 hunks)newton/tests/test_environment_group_collision.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/tests/test_environment_group_collision.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-20T18:02:36.042Z
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.042Z
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/_src/sim/builder.py
🧬 Code graph analysis (1)
newton/_src/sim/builder.py (1)
newton/_src/geometry/flags.py (1)
ShapeFlags(30-42)
⏰ 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 Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (4)
newton/_src/sim/builder.py (4)
665-669: Docs: environment-group collision semantics look correctThe bullets accurately capture the intended behavior: cross-environment collisions are disabled except when either side is in env group -1. No changes requested.
804-806: Preserving shape collision groups as-is is appropriateCarrying over group IDs without reindexing aligns with the “per-env filtering + group inside env” model. This keeps intra-env collision control intact.
1113-1117: Nice: canonical order for parent–child filter pairsThis prevents lookup mismatches and is consistent with the consumer logic. Good fix.
2169-2199: No normalization needed for same-body and parent–child filter insertionsThe review suggests canonicalizing
(u, v)pairs so that u ≤ v, but in this context that condition is already guaranteed:
- Each new shape’s index is
self.shape_count, which monotonically increases as shapes are added.- In the "same body" loop,
same_body_shapealways refers to an existing shape ID (< new shape).- In the "parent body" loop,
parent_shapelikewise is from an already-added body, so its index is < new shape.- Downstream lookups (e.g., in
solver_mujoco.py) even check both(i, j)and(j, i), ensuring no misses.Since the tuples are inherently inserted in ascending order and lookups handle either ordering, no change is required here.
Likely an incorrect or invalid review comment.
…ion-detection-system
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
newton/_src/sim/builder.py (1)
1056-1060: Canonicalize merged filter pairs to avoid missed filtersWhen importing
builder.shape_collision_filter_pairs, pairs may be stored non-canonically upstream. Sincefind_shape_contact_pairs()looks up canonicalized pairs(min, max), a non-canonical insertion here can reintroduce contacts that were meant to be filtered. Canonicalize before insertion.Apply:
- for i, j in builder.shape_collision_filter_pairs: - self.shape_collision_filter_pairs.add((i + shape_count_offset, j + shape_count_offset)) + for i, j in builder.shape_collision_filter_pairs: + a = i + shape_count_offset + b = j + shape_count_offset + if a > b: + a, b = b, a + self.shape_collision_filter_pairs.add((a, b))#!/bin/bash # Verify all insertions into shape_collision_filter_pairs are canonicalized. rg -nC2 -P '\bshape_collision_filter_pairs\.add\s*\('
🧹 Nitpick comments (4)
newton/_src/sim/builder.py (4)
914-918: Doc: clarify world-attached (-1 body) non-collision and cross-reference behaviorNice summary. Consider adding a one-liner that shapes attached to the world (body == -1) never collide with each other, so users don’t need to add manual filters for those. This aligns expectations with runtime behavior. Based on retrieved learnings from PR #587.
1061-1066: Optional: guard against accidental duplicates in group mapIf the source builder ever contained duplicates (or future code paths append the same shape twice),
itertools.combinations(shapes, 2)will emit degenerate pairs and extra work. Not critical today, but you can defensively deduplicate during merge.Example (keep list type, preserve order of first occurrences):
- self.shape_collision_group_map[group].extend([s + shape_count_offset for s in shapes]) + seen = set(self.shape_collision_group_map[group]) + for s in shapes: + idx = s + shape_count_offset + if idx not in seen: + self.shape_collision_group_map[group].append(idx) + seen.add(idx)
4440-4447: Env filtering logic matches the stated semanticsThe check
env1 != -1 and env2 != -1 and env1 != env2: continueenforces “same env or either global (-1)” correctly before adding pairs.Minor maintainability: factor this into a tiny helper to keep parity with the kernel-side predicate and reduce duplication:
+def _envs_collide(e1: int, e2: int) -> bool: + return e1 == -1 or e2 == -1 or e1 == e2 ... - if env1 != -1 and env2 != -1 and env1 != env2: + if not _envs_collide(env1, env2): continue
4463-4470: Consistent env filtering for group -1 cross-pairsSame comment as above; logic is correct and consistent with the doc. Consider reusing the same helper here to avoid divergence over time.
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
newton/_src/geometry/kernels.py(2 hunks)newton/_src/sim/builder.py(5 hunks)newton/_src/sim/style3d/builder_style3d.py(1 hunks)newton/examples/example_robot_manipulating_cloth.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- newton/_src/sim/style3d/builder_style3d.py
- newton/_src/geometry/kernels.py
- newton/examples/example_robot_manipulating_cloth.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-20T18:02:36.042Z
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.042Z
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/_src/sim/builder.py
🧬 Code graph analysis (1)
newton/_src/sim/builder.py (1)
newton/_src/geometry/flags.py (1)
ShapeFlags(30-42)
⏰ 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 Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (1)
newton/_src/sim/builder.py (1)
1362-1367: Good: canonical order for parent–child shape filtersNormalizing to
(min, max)avoids lookup mismatches later. This prevents subtle filter misses.
…ion-detection-system
…ion-detection-system
…ion-detection-system
…ion-detection-system
Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
newton/_src/geometry/broad_phase_nxn.py (1)
189-201: Fix Warp launch argument mapping; don’t place scalar in outputsPassing
max_candidate_pair(an int) viaoutputs=is incorrect and mixes styles with the precomputed-pairs path. Standardize to pass all kernel params viainputsin signature order (as done below), or put only array outputs inoutputswhile keeping positional mapping intact. This version mirrors the precomputed path and avoids confusion.- wp.launch( - _nxn_broadphase_kernel, - dim=num_lower_tri_elements, - inputs=[ - geom_lower, - geom_upper, - geom_count, - geom_cutoffs, - geom_collision_group, - geom_shape_group, - ], - outputs=[candidate_pair, num_candidate_pair, max_candidate_pair], - ) + wp.launch( + _nxn_broadphase_kernel, + dim=num_lower_tri_elements, + inputs=[ + geom_lower, + geom_upper, + geom_count, + geom_cutoffs, + geom_collision_group, + geom_shape_group, + candidate_pair, + num_candidate_pair, + max_candidate_pair, + ], + )
🧹 Nitpick comments (1)
newton/_src/geometry/broad_phase_nxn.py (1)
133-140: Docstring is outdated; mention environment groups and new predicateUpdate to reflect env-group semantics and the combined predicate.
- The collision checks take into account per-geometry cutoff distances and collision groups. Two geometries - will only be considered as a candidate pair if: - 1. Their AABBs overlap when expanded by their cutoff distances - 2. Their collision groups allow interaction (determined by test_group_pair()) + The collision checks take into account per-geometry cutoff distances, environment groups, and collision + groups. Two geometries will only be considered as a candidate pair if: + 1. Their AABBs overlap when expanded by their cutoff distances + 2. Their environment groups are compatible (same environment or at least one is global, -1) + 3. Their collision groups allow interaction (see test_environment_and_group_pair())
📜 Review details
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
newton/_src/geometry/broad_phase_nxn.py(6 hunks)newton/_src/geometry/broad_phase_sap.py(10 hunks)newton/tests/test_broad_phase.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- newton/tests/test_broad_phase.py
- newton/_src/geometry/broad_phase_sap.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-12T17:51:37.474Z
Learnt from: nvlukasz
PR: newton-physics/newton#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/geometry/broad_phase_nxn.py
🧬 Code graph analysis (1)
newton/_src/geometry/broad_phase_nxn.py (1)
newton/_src/geometry/broad_phase_common.py (3)
check_aabb_overlap(22-38)test_environment_and_group_pair(91-119)write_pair(54-65)
⏰ 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 (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
🔇 Additional comments (4)
newton/_src/geometry/broad_phase_nxn.py (4)
85-86: Kernel now takes env-group input — LGTMAdding
shape_group: wp.array(dtype=int, ndim=1)matches the -1/global semantics and keeps dtype compatible with negatives.
98-106: Early environment/group rejection is correctFiltering by
test_environment_and_group_pair(...)before AABB overlap avoids unnecessary box loads/comparisons.
167-172: Launch docstring additions — LGTMCollision-group semantics and env-group (-1 global) notes are clear; compatibility criteria spelled out.
Also applies to: 176-180
18-18: No action needed: predicates are annotated as @wp.funcBoth
test_environment_and_group_pairandcheck_aabb_overlapare correctly decorated with@wp.func.
…ion-detection-system Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
…ion-detection-system
…ion-detection-system
…hysics#605 (newton-physics#615) Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
…hysics#605 (newton-physics#615) Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
…hysics#605 (newton-physics#615) Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
Description
This PR integrates environment groups with the collision detection system in Newton, enabling efficient multi-environment simulations where entities from different environments don't collide with each other.
Key Changes:
Unified Environment and Collision Group Filtering
test_environment_and_group_pair()function inbroad_phase_common.pythat handles both environment and collision group filteringUpdated Collision Detection Pipeline
ModelBuilder.find_shape_contact_pairs()to filter shape pairs based on environment groups_nxn_broadphase_kerneland_sap_broadphase_kernel) to pass and utilize environment group informationcreate_soft_contactskernel to check particle vs shape environment groupsAPI Changes
separate_collision_groupparameter fromModelBuilder.add_builder()Limitations:
particle_groupenvironment boundaries (deferred due to complexity)Example Usage:
Newton Migration Guide
Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this MR.
docs/migration.rstis up-to dateBefore your PR is "Ready for review"
newton/tests/test_examples.py)newton/tests/test_environment_group_collision.pyModelBuilderdocstring to explain environment groupingpre-commit run -aSummary by CodeRabbit
New Features
Refactor
Tests
Chores