Add environment grouping functionality to Model entities #375#504
Conversation
…cs#375 Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
📝 WalkthroughWalkthroughAdds environment-group support: ModelBuilder tracks a current_env_group and per-entity group arrays; add_builder gains an environment parameter and propagates groups during copies; Model now exposes *_group arrays; four new tests validate grouping, num_envs tracking, and collapse-fixed-joints semantics. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Builder as ModelBuilder
participant Sub as Sub-Builder
participant Model
User->>Builder: set current_env_group (e.g. -1 global)
User->>Builder: add global entities (add_particle/add_body/add_shape)
User->>Sub: construct env-local sub-builder
User->>Builder: add_builder(Sub, environment=0)
User->>Builder: add_builder(Sub, environment=1)
User->>Builder: add_builder(Sub) (auto/next env or use current_env_group)
User->>Builder: finalize()
Builder-->>Model: returns Model with particle_group, shape_group, body_group, joint_group, articulation_group
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Assessment against linked issues
Out-of-scope changes(No out-of-scope functional code changes detected related to the linked issue objectives.) Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ 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
|
adenzler-nvidia
left a comment
There was a problem hiding this comment.
Thanks - looks good in general. Some questions about the modelbuilder parts.
Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 6
🔭 Outside diff range comments (1)
newton/sim/builder.py (1)
3703-3713: Fix required: add_particles must update particle_group to match particle_countShort: add_particles() extends particle arrays but does not update self.particle_group, so finalize()'s conversion (m.particle_group = wp.array(self.particle_group, ...)) can produce a length mismatch.
Places to fix:
- newton/sim/builder.py
- add_particles (def at line ~2673): after self.particle_flags.extend(flags) add:
self.particle_group.extend([self.current_env_group] * len(pos))- finalize (m.particle_group assignment at line ~3710): add a pre-finalize sanity check:
assert len(self.particle_group) == self.particle_count,
f"particle_group length {len(self.particle_group)} != particle_count {self.particle_count}"Suggested diff (minimal):
self.particle_flags.extend(flags)self.particle_group.extend([self.current_env_group] * len(pos))before converting to Warp arrays in finalize:
assert len(self.particle_group) == self.particle_count, \
f"particle_group length {len(self.particle_group)} != particle_count {self.particle_count}"Tag:
♻️ Duplicate comments (3)
newton/sim/builder.py (3)
120-146: Docs clarify env-grouping semantics; add one note about scope and explicit env behaviorThe “Environment Grouping” section is clear and directly addresses previous confusion about add_builder overriding sub-builder groups.
Two small doc nits to prevent confusion:
- Explicitly call out that grouping applies only to core entities: particles, bodies, shapes, joints, articulations (not springs/constraints/etc.).
- Clarify what happens to num_envs when a specific environment is provided to add_builder(environment=...).
Proposed wording tweak (insert after Line 128):
Applies to: particles, bodies, shapes, joints, articulations. Other elements (e.g., springs, constraints) are currently ungrouped. When environment is specified in add_builder(environment=...), the builder will use that index for all copied entities. num_envs will be updated to cover the highest environment index seen (environment + 1).
415-416: Body group state added — LGTMInline comment is helpful and resolves past request to document.
565-592: Docs explicitly stating override-on-merge behavior — LGTMThis addresses prior confusion called out in earlier review threads. Thanks for spelling out the two usage patterns and examples.
🧹 Nitpick comments (2)
newton/sim/builder.py (2)
561-562: add_builder gains environment parameter — consider validating inputSignature extension makes sense. Consider validating environment >= -1 to catch accidental negative values other than -1.
Example:
if environment is not None and environment < -1: raise ValueError(f"Invalid environment index {environment}; expected -1 or >= 0.")
597-607: Optional: validate environment index earlyDefensive check to avoid unexpected negative indices beyond -1.
Diff (within this block):
# Set the environment group for entities being added - if environment is None: + if environment is None: # Use the current environment count as the group index if not specified group_idx = self.num_envs if update_num_env_count else self.current_env_group else: + if environment < -1: + raise ValueError(f"Invalid environment index {environment}; expected -1 or >= 0.") group_idx = environment
📜 Review details
Configuration used: .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 settings in your CodeRabbit configuration.
📒 Files selected for processing (2)
newton/sim/builder.py(17 hunks)newton/sim/model.py(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/sim/model.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
newton/sim/builder.py (2)
newton/sim/state.py (4)
joint_dof_count(105-109)joint_coord_count(98-102)particle_count(91-95)body_count(84-88)newton/sim/style3d/builder_style3d.py (1)
add_builder(106-136)
⏰ 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 (11)
newton/sim/builder.py (11)
340-341: Particle group state added — LGTMThis keeps particle-group metadata alongside existing particle state. Good default (-1) via current_env_group.
366-368: Shape group state added — LGTMConsistent with particles; aligns with finalize.
451-456: Joint and articulation group state added — LGTMBoth tracked explicitly. This matches PR scope.
460-464: current_env_group default and override behavior documented — LGTMDefaulting to -1 (global) is aligned with the stated semantics.
553-553: Articulation grouping on creation — LGTMConsistent with the other entity adders.
597-607: Group selection logic — OK; beware num_envs accounting with explicit environmentThe temporary override of current_env_group is correct. However, num_envs accounting later (Lines 828-830) doesn’t handle environment being explicitly set to a higher index (e.g., 10). See fix below.
Would you confirm whether num_envs should always be max(max_group_index + 1, current value) when environment is explicitly provided? The diff below assumes yes.
721-747: Override copied entity groups on merge — LGTMAll five categories (particles, bodies, shapes, joints, articulations) are covered consistently.
895-896: Body grouping on creation — LGTMGroup assignment mirrors add_particle/add_shape/add_joint. Good consistency.
960-961: Joint grouping on creation — LGTMConsistent with other entities.
2063-2064: Shape grouping on creation — LGTMNo concerns.
3753-3754: Finalize: shape_group wired — LGTMAlignment with shape_count is correct.
adenzler-nvidia
left a comment
There was a problem hiding this comment.
Approcing, thanks. Can you make sure that all the follow-up tasks are captured in github issues:
here's what's on top of my mind:
- collision groups
- better modelbuilder documentation
- BVH support
- actually taking advantage of this in the MuJoCo solver, maybe other solvers.
…o-model-entities Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🔭 Outside diff range comments (3)
newton/_src/sim/model.py (1)
3698-3699: Fix required: ModelBuilder.add_particles doesn't populate particle_group — causes length mismatch at finalizeConfirmed: ModelBuilder.add_particles (list form) extends particle arrays but does not add entries to self.particle_group. finalize builds the model from self.particle_group (so a shorter particle_group will produce shape/length mismatches at runtime).
Places to fix / verify:
- newton/_src/sim/builder.py — add_particles (def at ~line 2661): missing particle_group extension.
- newton/_src/sim/builder.py — add_particle (single, ~lines 2628–2656): correctly does self.particle_group.append(self.current_env_group).
- newton/_src/sim/builder.py — add_builder merge path (~lines 708–714): correctly extends particle_group for merged builders.
- newton/_src/sim/builder.py — finalize (def at ~line 3655; m.particle_group assigned ~lines 3696–3699): consumes self.particle_group to create the model array.
Suggested minimal fix (apply inside ModelBuilder.add_particles, after self.particle_flags.extend(flags)):
# ensure environment group index is set for each new particle self.particle_group.extend([self.current_env_group] * len(pos))Optional fail-fast sanity check to add in finalize (before creating model arrays):
assert len(self.particle_group) == len(self.particle_q), "particle_group length mismatch"Please apply the small change above in add_particles and add the assertion in finalize to prevent regressions.
newton/_src/sim/builder.py (2)
1674-1949: collapse_fixed_joints must remap group arrays (body_group, joint_group, articulation_group) — fix requiredVerified: collapse_fixed_joints (newton/_src/sim/builder.py, def at ~line 1674) rebuilds bodies/joints and updates articulation_start but does not update self.body_group, self.joint_group or self.articulation_group. finalize() later emits these arrays to the Model (m.body_group @ ~3896, m.joint_group @ ~3909, m.articulation_group @ ~3949), so the groups become stale/misaligned. Tests and import paths call collapse_fixed_joints (e.g. newton/tests/test_model.py lines ~162–166), so this is a correctness/regression risk.
Locations to change
- newton/_src/sim/builder.py
- collapse_fixed_joints definition (starts ~1674)
- body list clearing & repopulation (clears at ~1839; retained_bodies loop ~1852–1875)
- retained_joints handling and joint repopulation (~1877–1917)
- articulation_start update and the problematic list(set(...)) usage (~1883–1892)
- finalize() uses of group arrays (~3896, ~3909, ~3949)
Suggested minimal fix
- Snapshot old group arrays before clearing:
- old_body_group = list(self.body_group)
- old_joint_group = list(self.joint_group)
- old_articulation_group = list(self.articulation_group)
- old_articulation_start = list(self.articulation_start)
- Remap body_group while rebuilding bodies:
- In the retained_bodies loop, alongside body_remap/new_id append new_body_group.append(old_body_group[body["original_id"]]).
- Remap joint_group while rebuilding joints:
- When iterating retained_joints (in original order), append new_joint_group.append(old_joint_group[joint["original_id"]]).
- Rebuild articulation_start and articulation_group together (do NOT use list(set(...))):
- Map each old articulation start via joint_remap (or keep start if unmapped).
- Build list of (mapped_start, old_articulation_index, old_group), sort by mapped_start, remove duplicates where mapped_start collapses with previous articulation, then set self.articulation_start = [mapped_start ...] and self.articulation_group = [corresponding old_group ...].
- Replace the current articulation_start = list(set(...)) line with the deterministic remapping above to preserve ordering and group alignment.
Would you like me to prepare a targeted patch/PR that implements the above remapping and replaces the list(set(...)) use with a deterministic reconstruction of articulation_group?
2681-2690: Fix required: add_particles must populate particle_group to preserve particle_count invariantadd_particles extends particle arrays but does not add entries to self.particle_group; add_particle (single) does. This causes len(particle_group) != particle_count and can break finalization.
Files / locations to change:
- newton/_src/sim/builder.py — add_particles (around lines 2661–2691): after self.particle_flags.extend(flags) add group entries.
- Note: add_particle (around lines 2625–2660) already does self.particle_group.append(self.current_env_group) — use the same behavior for vectorized creation.
Suggested diff:
self.particle_radius.extend(radius) self.particle_flags.extend(flags) + # Populate environment group indices for vectorized particle creation + self.particle_group.extend([self.current_env_group] * len(pos))
🧹 Nitpick comments (3)
newton/_src/sim/model.py (2)
94-96: New per-entity group fields: API shape looks goodAdding nullable int32 arrays per-entity type is consistent and non-breaking. One optional improvement: consider registering these fields in attribute_frequency for introspection parity (e.g., "particle", "shape", "body", "joint", "articulation").
If you want, I can add the attribute_frequency mappings in the right section for you.
Also applies to: 150-152, 224-226, 285-287, 291-293
382-441: Consider exposing group fields in attribute_frequency for consistencyDownstream utilities that rely on attribute_frequency might benefit from knowing the frequency of the new group attributes (e.g., for generic attribute iteration/validation).
Happy to add:
- "particle_group": "particle"
- "shape_group": "shape"
- "body_group": "body"
- "joint_group": "joint"
- "articulation_group": "articulation"
newton/_src/sim/builder.py (1)
816-818: Avoid incrementing num_envs for global (-1) “environment”Only increment when assigning to a non-negative environment. This also aligns with the doc intent and typical per-env allocations downstream.
Apply this diff:
- if update_num_env_count: - self.num_envs += 1 + if update_num_env_count and group_idx >= 0: + self.num_envs += 1
📜 Review details
Configuration used: .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(17 hunks)newton/_src/sim/model.py(5 hunks)newton/tests/test_model.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/tests/test_model.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 Benchmarks (Pull Request)
- GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
🔇 Additional comments (8)
newton/_src/sim/model.py (1)
35-45: Docblock addition reads well and matches PR scopeClear explanation of -1 (global) vs non-negative environment indices and intended downstream uses. No issues.
newton/_src/sim/builder.py (7)
108-134: Docs for Environment Grouping are clear and actionableExamples are helpful; they match the implemented semantics (explicit override via environment argument vs inheriting current_env_group).
328-329: State additions and current_env_group default look right
- Storing per-entity group indices in parallel arrays is consistent with existing storage.
- Defaulting current_env_group to -1 (global) is sensible and aligns with the doc.
Also applies to: 355-356, 403-404, 439-440, 443-444, 449-452
541-542: Propagating group on articulation creationAppending articulation_group here ensures per-articulation grouping is captured even when users build by hand. Looks good.
553-572: Clarify interaction with update_num_env_count for global (-1) environmentDocs say “Use -1 for global entities,” but code currently increments num_envs unconditionally (see Lines 816-818). That will count a global builder as an “environment,” which is surprising and can inflate per-env allocations.
I recommend conditioning the increment on environment >= 0 (see diff below).
585-596: Environment group selection is sound
- environment=None → auto-assign to self.num_envs (pre-increment), matches expectations.
- Explicit environment overrides the source builder’s groups as documented.
709-735: Overriding sub-builder groups upon merge: correct and simpleThis guarantees all imported entities land in a single target environment, which is the desired behavior.
883-884: Per-entity group propagation on direct adds is correctBodies, joints, shapes, and single particles inherit current_env_group, as advertised.
Also applies to: 948-949, 2051-2052, 2655-2656
Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (8)
newton/_src/sim/builder.py (8)
329-330: Introduce and maintain invariants for group arraysDefining particle_group is good. Please ensure a strict invariant throughout the builder: for every entity type, len(*_group) must always equal the corresponding count. A later comment flags a concrete violation in add_particles.
449-453: Default current_env_group (-1) is sensibleThe default aligns with “global entities.” Consider documenting that users who set current_env_group directly should also ensure num_envs is large enough (see finalize comment).
589-599: Exception safety: restore current_env_group via try/finallycurrent_env_group is saved and restored at the end. If an exception occurs in-between (e.g., in transform_mul or deep copies), current_env_group will remain set to the temporary group. Wrap the body of add_builder in a try/finally to guarantee restoration.
Apply this minimal restructuring (pseudo-diff, indentation omitted for brevity):
- prev_env_group = self.current_env_group - self.current_env_group = group_idx + prev_env_group = self.current_env_group + self.current_env_group = group_idx + try: # ... existing add_builder logic ... - # Restore the previous environment group - self.current_env_group = prev_env_group + finally: + self.current_env_group = prev_env_group
823-825: State restoration acknowledgedRestoring current_env_group is good; see earlier note about using try/finally for exception safety.
3675-3691: Make num_envs consistent with assigned group indicesCurrently, finalize forces num_envs = max(1, self.num_envs). If users set current_env_group manually (e.g., to 1 or higher) without using add_builder(update_num_env_count=True), num_envs may be too small. Suggest ensuring num_envs is at least max(nonnegative group index) + 1 across all entity types.
Apply this refinement:
- # ensure the env count is set correctly - self.num_envs = max(1, self.num_envs) + # ensure the env count covers all non-negative group indices + def _max_nonneg(xs): + return max((g for g in xs if g >= 0), default=-1) + max_group = max( + _max_nonneg(self.particle_group), + _max_nonneg(self.shape_group), + _max_nonneg(self.body_group), + _max_nonneg(self.joint_group), + _max_nonneg(self.articulation_group), + ) + self.num_envs = max(1, self.num_envs, max_group + 1)This keeps backward compatibility and hardens finalize for manually assigned groups.
3703-3703: Serialize particle_group — ensure size matches particle_countThis will error or misbehave if add_particles hasn’t updated particle_group accordingly. After fixing add_particles, consider a lightweight runtime check before creating arrays:
assert len(self.particle_group) == self.particle_count(Or raise a ValueError with a clear message.)
3952-3954: Serialize articulation_group — potential mismatch with articulation_start updatescollapse_fixed_joints mutates articulation_start (including a non-stable set() dedup), but articulation_group is not updated. Consider keeping articulation_start order stable and updating articulation_group in lockstep.
Would you like a concrete patch to keep articulation_start stable and rebuild articulation_group appropriately?
544-584: Optional: Instrumentation to detect grouping mismatches earlyGiven the new per-entity group arrays, adding lightweight validation at the end of add_builder could save debugging time:
- particle_count == len(particle_group) - pre and post
- shape_count == len(shape_group)
- body_count == len(body_group)
- joint_count == len(joint_group)
- articulation_count == len(articulation_group)
This can be gated behind a debug flag or wp.config.verbose.
Also applies to: 589-599, 713-739
📜 Review details
Configuration used: .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 (1)
newton/_src/sim/builder.py(17 hunks)
⏰ 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 (windows-latest)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
🔇 Additional comments (13)
newton/_src/sim/builder.py (13)
108-135: Clear, actionable documentation for environment groupingThe Environment Grouping section is concise and useful. It makes the global (-1) vs environment-scoped grouping semantics obvious and shows both direct and add_builder flows.
355-357: Shape grouping array added — goodshape_group complements the other per-entity arrays. No issues here.
404-405: Body grouping array added — goodbody_group is wired consistently with add_body and finalize. LGTM.
440-441: Joint grouping array added — goodThe array is propagated at creation and serialized in finalize. LGTM.
444-445: Articulation grouping array added — goodThe creation path and finalize serialization are consistent. LGTM.
542-543: Propagate env group on articulation creationAppending current_env_group here is correct. LGTM.
550-551: add_builder(environment=...) API extensionExtends flexibility without breaking existing callers. LGTM.
554-576: Docstring for add_builder environment behavior is clearCalling out the override semantics is important; thanks for being explicit. LGTM.
713-739: Group overriding on add_builder is correct but ensure all entity types stay alignedOverriding source-builder group indices with the computed self.current_env_group meets the PR objective. Note that any bulk-add paths outside add_builder must also update their *_group arrays to preserve length equality with counts (see add_particles comment).
887-888: Propagate env group on body creationAppending current_env_group on body creation is correct. LGTM.
952-953: Propagate env group on joint creationCorrect and consistent with other entities. LGTM.
2055-2056: Propagate env group on shape creationCorrect; shapes added via helpers will inherit this as well. LGTM.
3746-3746: Serialize shape_group — size sanityIf there are code paths that add shapes without touching shape_group (none observed), this would mismatch. Looks fine given add_shape/add_builder paths.
Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🔭 Outside diff range comments (1)
newton/_src/sim/builder.py (1)
3618-3623: Free-joint auto-add assigns joint_group based on current_env_group, not the body’s envWhen adding free joints for floating bodies, the joint’s group should inherit the child body’s environment (so world->child joints remain in the same env). Today it uses whatever current_env_group happens to be, which can mislabel joints in mixed-env scenes.
Apply this change:
def add_free_joints_to_floating_bodies(self, new_bodies: Iterable[int] | None = None): """ Adds a free joint to every body that is not a child in any joint and has mass > 0. Should be called after all other joints have been added. """ # set(self.joint_child) is connected_bodies floating_bodies = set(new_bodies) - set(self.joint_child) for body_id in floating_bodies: if self.body_mass[body_id] > 0: - self.add_joint_free(child=body_id) + prev_env = self.current_env_group + try: + # Inherit the child's environment for the world->child free joint + body_env = self.body_group[body_id] if body_id < len(self.body_group) else -1 + self.current_env_group = body_env + self.add_joint_free(child=body_id) + finally: + self.current_env_group = prev_envThis aligns joint_group with the body’s env group.
♻️ Duplicate comments (1)
newton/_src/sim/builder.py (1)
2714-2716: Bulk particle add now maintains particle_group — thanks for fixing the invariantThis resolves length mismatches between particle_group and particle_* arrays reported earlier.
🧹 Nitpick comments (5)
newton/tests/test_model.py (3)
270-291: add_particles grouping test covers the core path; consider tightening assertionsThis validates default (-1) and non-default (0) environments and exercises finalize->Model propagation. Optionally assert particle_count == 5 and that dtype is int32 to catch serialization regressions early.
Apply this small augmentation:
model = builder.finalize() particle_groups = model.particle_group.numpy() # First 3 particles should be in group -1 self.assertTrue(np.all(particle_groups[0:3] == -1)) # Next 2 particles should be in group 0 self.assertTrue(np.all(particle_groups[3:5] == 0)) + self.assertEqual(model.particle_count, 5) + self.assertEqual(particle_groups.dtype, np.int32)
292-396: Environment grouping test is comprehensive; make indexing assertions less brittleGreat coverage across particles, bodies, shapes, joints, articulations and auto-assign (env=2). The slice-based assertions assume insertion order; future refactors might change ordering without breaking grouping semantics. Prefer checking counts per group to reduce brittleness.
For example:
- if len(body_groups) > 0: - self.assertEqual(body_groups[0], -1) # ground body - self.assertTrue(np.all(body_groups[1:3] == 0)) - self.assertTrue(np.all(body_groups[3:5] == 1)) - self.assertTrue(np.all(body_groups[5:7] == 2)) + if len(body_groups) > 0: + # counts by group + unique, counts = np.unique(body_groups, return_counts=True) + counts_by_group = dict(zip(unique.tolist(), counts.tolist())) + self.assertEqual(counts_by_group.get(-1, 0), 1) + self.assertEqual(counts_by_group.get(0, 0), 2) + self.assertEqual(counts_by_group.get(1, 0), 2) + self.assertEqual(counts_by_group.get(2, 0), 2)Do similarly for particle_groups and shape_groups.
397-494: collapse_fixed_joints grouping checks: solid; consider asserting articulation grouping tooThe pre-/post-collapse assertions for body_group and joint_group are spot on and validate the rebuild logic. As an extra guardrail, you could assert articulation_count and articulation_group remain consistent with the retained joint segments.
For example:
model = builder.finalize() body_groups = model.body_group.numpy() joint_groups = model.joint_group.numpy() + if model.articulation_count > 0 and model.articulation_group is not None: + art_groups = model.articulation_group.numpy() + self.assertTrue(np.all((art_groups == 0) | (art_groups == 1)))newton/_src/sim/builder.py (2)
544-585: add_builder: docs correctly state group override behavior; add an explicit note on env=-1 countingThe behavior “ALL entities from the source builder are assigned to the specified group” is clearly documented. Consider adding that passing environment=-1 produces global entities and should not increase num_envs (see code suggestion below).
3724-3725: *Model serialization of _group arrays — LGTM; optionally validate lengths before writingSerializing group arrays as wp.int32 (or None when empty) matches the public API. To catch regressions earlier, consider validating lengths before serialization.
Add a lightweight validation helper:
def finalize(self, device: Devicelike | None = None, requires_grad: bool = False) -> Model: + # Sanity checks for group array lengths + def _check_group_lengths(): + assert len(self.particle_group) == self.particle_count or self.particle_count == 0 + assert len(self.shape_group) == self.shape_count or self.shape_count == 0 + assert len(self.body_group) == self.body_count or self.body_count == 0 + assert len(self.joint_group) == self.joint_count or self.joint_count == 0 + assert len(self.articulation_group) == self.articulation_count or self.articulation_count == 0 + _check_group_lengths()If asserts are too strict for end-users, log warnings instead of asserting.
Also applies to: 3767-3768, 3922-3923, 3935-3936, 3975-3976
📜 Review details
Configuration used: .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(24 hunks)newton/tests/test_model.py(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
newton/tests/test_model.py (1)
newton/_src/sim/builder.py (12)
ModelBuilder(68-4051)add_particles(2685-2715)finalize(3681-4019)add_body(826-888)add_shape_box(2196-2233)add_particle(2649-2683)add_articulation(536-542)add_joint_revolute(1005-1087)add_shape_sphere(2163-2194)add_builder(544-824)add_joint_fixed(1210-1246)collapse_fixed_joints(1678-1972)
🔇 Additional comments (12)
newton/_src/sim/builder.py (12)
108-135: Clear, actionable Environment Grouping documentationThis section concisely explains semantics (-1 global, non-negative envs) and usage patterns (current_env_group, add_builder). Good addition.
329-357: *Initialization of _group arrays and current_env_group is correct and consistentIntroducing particle_group, shape_group, body_group, joint_group, articulation_group and defaulting current_env_group to -1 aligns with the API and tests.
Also applies to: 405-405, 440-445, 449-453
542-543: Articulation group tagging added at creation — LGTMThis mirrors the other entity creators and keeps grouping in sync.
589-599: Group selection logic balances explicit env vs. auto-assignment — LGTMWhen environment is None, using num_envs (or current_env_group if not updating) yields deterministic auto-assignment.
713-739: Uniform group overrides for copied entities are correctExtending particle/body/shape/joint/articulation groups with the target group guarantees a single env grouping per sub-builder instance — exactly as specified.
823-825: Restoring previous current_env_group after add_builder — LGTMPrevents unexpected leakage of sub-builder env into subsequent direct adds.
887-887: Body creation tags body_group with current_env_group — LGTMKeeps body_group in-step with body arrays.
952-953: Joint creation tags joint_group with current_env_group — LGTMConsistent with other entity creators.
2075-2076: Shape creation tags shape_group with current_env_group — LGTMMatches the body’s env at creation and test expectations.
2679-2680: Particle creation tags particle_group with current_env_group — LGTMSingle-item add path is aligned with the bulk path.
1843-1855: collapse_fixed_joints: body_group is correctly rebuilt for retained bodiesSaving original_body_group, clearing, then reassigning per retained body via original_id preserves grouping through structural edits. Defaulting to -1 when missing is sensible.
Also applies to: 1885-1890
1908-1954: collapse_fixed_joints: joint_group is correctly rebuilt for retained jointsReconstructing joint_group from original_joint_group by original_id maintains group semantics after fixed-joint removal and reordering.
Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
newton/_src/sim/builder.py (3)
823-832: Num envs update logic fixed (no increment for globals; reconcile explicit env indices)This addresses the previously raised mismatch and ensures num_envs reflects the highest non-negative group used.
2724-2726: Bulk particle creation now updates particle_groupThis resolves the earlier mismatch risk for add_particles.
1853-1900: Rebuild body_group in collapse_fixed_joints — nice catchSaving the original mapping and rebuilding body_group for retained bodies prevents post-collapse misalignment.
🧹 Nitpick comments (2)
newton/_src/sim/builder.py (2)
1918-1964: Rebuild joint_group in collapse_fixed_joints — correct; consider doing the same for articulationsReconstructing joint_group to match retained_joints resolves joint-group desyncs. Consider verifying whether articulation_start changes (set/dedup) can desynchronize articulation_group. If collapse affects articulations or changes their ordering/count, rebuild articulation_group accordingly to keep it 1:1 with articulation_start.
Would you like a patch sketch to rebuild articulation_group based on the new articulation starts?
3734-3735: Serialize and Guard Per-Entity Group Arrays — ApprovedAll per-entity group arrays (
particle_group,shape_group,body_group,joint_group,articulation_group) are correctly exported to theModeland guarded for empty counts in newton/_src/sim/builder.py at:
- Lines 3734–3735 (
particle_group)- Lines 3777–3778 (
shape_group)- Lines 3932–3933 (
body_group)- Lines 3945–3946 (
joint_group)- Lines 3984–3986 (
articulation_group)Optional Safety Hardening: To prevent future regressions, you can reconcile
self.num_envswith the highest non-negative group index in use. Replace the existing- # ensure the env count is set correctly - self.num_envs = max(1, self.num_envs)with:
+ # ensure the env count is set correctly + # Optional safety: reconcile with the highest non-negative group index in use + groups_max = max( + max(self.particle_group) if self.particle_group else -1, + max(self.shape_group) if self.shape_group else -1, + max(self.body_group) if self.body_group else -1, + max(self.joint_group) if self.joint_group else -1, + max(self.articulation_group) if self.articulation_group else -1, + ) + computed_envs = groups_max + 1 if groups_max >= 0 else 0 + self.num_envs = max(1, max(self.num_envs, computed_envs))Apply this change at the location where
self.num_envsis currently set (e.g., at the end ofadd_builderor in your finalize routine).Group-invariant verification (already confirmed):
rg -n -C2 'self\.(particle|shape|body|joint|articulation)_group\.(append|extend)\(' newton/_src/sim/builder.py rg -n -C2 'm\.(particle|shape|body|joint|articulation)_group\s*=' newton/_src/sim/builder.py
📜 Review details
Configuration used: .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(24 hunks)newton/tests/test_model.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/tests/test_model.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 Unit Tests on AWS EC2 (Pull Request)
- GitHub Check: Run GPU Benchmarks (Pull Request)
- GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (17)
newton/_src/sim/builder.py (17)
108-135: Environment grouping docs are clear and actionableThe new section concisely explains -1 vs non-negative env groups and how to use add_builder and current_env_group. Good guidance for users.
329-329: Initialize particle_group — goodEstablishing a per-particle env group list aligns with the PR’s objectives.
355-356: Initialize shape_group — goodThis mirrors other entity group lists and keeps data in lockstep with shapes.
405-405: Initialize body_group — goodConsistent with per-entity env grouping.
440-441: Initialize joint_group — goodRequired for per-joint grouping and aligns with tests.
444-445: Initialize articulation_group — goodKeeps articulation grouping explicit for serialization and downstream use.
449-453: current_env_group default and semantics look rightDefaulting to -1 (global) with temporary override during add_builder matches the intended workflow.
542-543: Record articulation group on creation — goodNew articulations pick up the current_env_group. Matches the model’s grouping API.
544-551: add_builder(environment=...) signature addition is sensibleThe parameter is well-placed and typed; defaulting to None enables the auto-assignment behavior.
554-587: Docstring for add_builder environment behavior is clearExplicitly stating that sub-builder groups are overridden removes ambiguity. The examples are helpful.
592-602: Group selection logic covers all modes
- environment=None uses next env (or current group when update_num_env_count=False).
- Explicit environment uses that index.
- The temporary override of current_env_group is restored later.
716-742: Override group arrays for copied entities — correct and consistentPopulating particle/body/shape/joint/articulation groups with the chosen env id guarantees alignment with the “override all entities” rule.
834-835: Restore current_env_group — goodPrevents add_builder from leaking group state into subsequent direct additions.
897-898: Add body_group on body creation — correctKeeps body_group length in sync with bodies.
962-963: Add joint_group on joint creation — correctJoint group per addition keeps ordering and length aligned with joints.
2085-2086: Add shape_group on shape creation — correctShape grouping is consistently tracked.
2689-2690: Add particle_group on single-particle creation — correctMaintains grouping invariants.
…cs#375 (newton-physics#504) Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com> Signed-off-by: Miguel Zamora <mazamoramora@nvidia.com>
…cs#375 (newton-physics#504) Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
…on (newton-physics#504) # Description When running headless, with no cameras enabled, optimize init time by only doing minimal fabric population, by turning off FSD. ## Type of change <!-- As you go through the list, delete the ones that are not applicable. --> - Bug fix (non-breaking change which fixes an issue) ## Checklist - [ ] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have updated the changelog and the corresponding version in the extension's `config/extension.toml` file - [ ] I have added my name to the `CONTRIBUTORS.md` or my name already exists there <!-- As you go through the checklist above, you can mark something as done by putting an x character in it For example, - [x] I have done this task - [ ] I have not done this task --> --------- Co-authored-by: Kelly Guo <kellyg@nvidia.com>
…cs#375 (newton-physics#504) Signed-off-by: Viktor Reutskyy <vreutskyy@nvidia.com>
Description
This PR implements environment grouping functionality for Model entities. Closes #375
Changes:
Added group index properties to Model class for all entity types:
particle_group,body_group,shape_group,joint_group,articulation_groupUpdated ModelBuilder to track and assign group indices:
current_env_groupproperty (defaults to -1 for global entities)Enhanced
add_builder()method with anenvironmentparameter:Added test coverage with
test_environment_groupingintest_model.pyLimitations:
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)pre-commit run -aSummary by CodeRabbit
New Features
Documentation
Tests