Skip to content

skip usd cloning in pure newton path#5743

Merged
StafaH merged 11 commits into
isaac-sim:developfrom
ooctipus:zhengyuz/newton-clone-plan
May 29, 2026
Merged

skip usd cloning in pure newton path#5743
StafaH merged 11 commits into
isaac-sim:developfrom
ooctipus:zhengyuz/newton-clone-plan

Conversation

@ooctipus
Copy link
Copy Markdown
Collaborator

Description

Fixes Newton replicated-scene cloning so clone-plan source paths are available before sensor construction and asset USD replication is skipped when Newton handles physics replication.

This also updates Newton frame views, ray caster and joint wrench sensor resolution, Newton camera preparation, deformable managers, and DexSuite point-cloud sampling to resolve against clone-plan source prims and Newton model labels instead of cloned destination USD prims.

Fixes # N/A

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Screenshots

N/A

Validation

  • ./isaaclab.sh -f was attempted. It passed ruff, formatting, whitespace, YAML/TOML, merge-conflict, private-key, debug-statement, codespell, license, and RST checks before failing in end-of-file-fixer with PermissionError on root-owned files in this checkout. The working tree remained clean after the attempt.
  • git diff --check upstream/develop...HEAD passed.

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks 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 added a changelog fragment under source/<pkg>/changelog.d/ for every touched package (do not edit CHANGELOG.rst or bump extension.toml -- CI handles that)
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label May 22, 2026
@ooctipus ooctipus changed the title Fix Newton clone-plan source path handling skip usd cloning in pure newton path May 22, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 22, 2026

Greptile Summary

This PR fixes Newton replicated-scene cloning by publishing the clone plan to SimulationContext before sensor construction, skipping USD asset replication when Newton handles physics replication, and routing sensor/view resolution through clone-plan source prims rather than cloned destination USD prims.

  • Clone-plan-first resolution: new resolve_matching_prims_from_source (queries.py), path_source_path and iter_clone_plan_matches (cloner_utils.py) utilities let sensors, frame views, ray casters, cameras, deformable managers, and DexSuite point-cloud sampling resolve against source prims and Newton model labels instead of potentially-absent cloned USD prims.
  • Newton site frame view refactor: NewtonSiteFrameView is heavily reworked to register sites during pre-physics init via cl_register_site (with a new per_world mode for env-frame sites), then populate GPU arrays from the injected site map on physics-ready; scale and local-pose kernels are consolidated from indexed/non-indexed pairs into single indexed variants.
  • Sensor base and asset init updates: sensor_base._resolve_rigid_body_ancestor_expr, and the Newton/PhysX articulation, rigid-object, and rigid-object-collection asset initializers all switch from find_first_matching_prim + regex reconstruction to resolve_matching_prims_from_source + clone-plan-aware suffix mapping.

Confidence Score: 3/5

Not safe to merge as-is: two newly introduced code paths crash with AttributeError when SimulationContext.instance() returns None, and the public cloner stub exports a symbol (cfg_source_path) that does not exist in the implementation.

The new resolve_matching_prims_from_source utility is called from every sensor and asset initializer added in this PR; without the instance() null check it will crash in unit-test environments and any headless setup that exercises sensors before a SimulationContext is created. The same unguarded pattern is present in NewtonSiteFrameView._resolve_site_specs. Additionally, cfg_source_path is declared in the public stub and wired through lazy_export, but the function body is absent from cloner_utils.py, so any consumer of the published API who tries to use it will hit an ImportError.

source/isaaclab/isaaclab/sim/utils/queries.py (missing None guard on line 394), source/isaaclab/isaaclab/cloner/init.pyi (undefined cfg_source_path export), and source/isaaclab_newton/isaaclab_newton/sim/views/newton_site_frame_view.py (same missing None guard in _resolve_site_specs).

Important Files Changed

Filename Overview
source/isaaclab/isaaclab/cloner/init.pyi Exports cfg_source_path which does not exist in cloner_utils.py, causing a runtime ImportError for any caller that uses this symbol via the lazy_export mechanism.
source/isaaclab/isaaclab/sim/utils/queries.py Adds resolve_matching_prims_from_source — a broad utility used by nearly every sensor init path — that calls SimulationContext.instance().get_clone_plan() without a None guard, crashing in tests or headless contexts without a live simulation.
source/isaaclab/isaaclab/cloner/cloner_utils.py Adds path_source_path and iter_clone_plan_matches helpers for clone-plan lookups; changes make_clone_plan return type from ClonePlan to a raw tuple — a breaking API change for external callers not updated in this PR.
source/isaaclab/isaaclab/scene/interactive_scene.py Publishes the clone plan to SimulationContext before sensor construction, enabling source-prim resolution; skips USD cloning for Newton-replicated scenes via clone_usd=not is_newton_replicated_scene or has_kit().
source/isaaclab/isaaclab/sensors/sensor_base.py Replaces stage-scan env-count with clone-plan-aware _num_envs resolution; refactors _resolve_and_spawn into _resolve_rigid_body_ancestor_expr that resolves physics body ancestors from source prims rather than cloned USD prims.
source/isaaclab_newton/isaaclab_newton/sim/views/newton_site_frame_view.py Major refactor of NewtonSiteFrameView to resolve frames from clone-plan source prims and Newton model labels rather than cloned USD prims; introduces _resolve_site_specs which calls SimulationContext.instance().get_clone_plan() without a None guard.
source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py Adds per_world site registration for world-frame sites that are instantiated once per cloned environment; records _world_xforms to support NewtonSiteFrameView world-id lookups.
source/isaaclab_newton/isaaclab_newton/cloner/newton_replicate.py Adds default zero-positions/identity-quaternion fallback for None inputs; injects world sites per cloned env and stores _world_xforms for downstream view lookups.
source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/utils.py Switches point-cloud sampling from per-env USD prim iteration to clone-plan source prim lookups; iter_clone_plan_matches is called with a potentially-None plan, and if the plan has no matching entries points remains all-zeros.
source/isaaclab/isaaclab/sensors/ray_caster/base_multi_mesh_ray_caster.py Migrates clone-plan mesh-target resolution to use iter_clone_plan_matches; handles articulation roots as top-level bodies and enumerates rigid-body descendants for compound assets.

Sequence Diagram

sequenceDiagram
    participant IS as InteractiveScene
    participant SC as SimulationContext
    participant S as Sensor / Asset
    participant CU as cloner_utils
    participant NM as NewtonManager
    participant QU as queries.py

    IS->>IS: _build_clone_plan_from_cfg()
    IS->>SC: set_clone_plan(plan)
    Note over IS,SC: plan published BEFORE sensor construction

    IS->>S: _add_entities_from_cfg()
    S->>QU: resolve_matching_prims_from_source(prim_path)
    QU->>SC: instance().get_clone_plan()
    QU->>CU: path_source_path(prim_path, plan)
    CU-->>QU: (source_path, dest_glob, suffix)
    QU-->>S: [(source_prim, dest_expr), ...]

    alt "Newton backend and clone_usd=False"
        IS->>NM: newton_physics_replicate(...)
        NM->>NM: _cl_inject_sites(builder, protos)
        NM->>NM: store _world_xforms
        NM->>NM: _cl_site_index_map populated
        S->>NM: _on_physics_ready then _initialize_from_site_map()
    else USD/PhysX backend
        IS->>IS: usd_replicate(stage, ...)
        S->>S: _initialize_from_specs(model)
    end
Loading

Comments Outside Diff (1)

  1. source/isaaclab_newton/isaaclab_newton/sim/views/newton_site_frame_view.py, line 875-876 (link)

    P1 SimulationContext.instance() not null-checked in _resolve_site_specs

    sim_utils.SimulationContext.instance() can return None in any environment that creates a NewtonSiteFrameView before the simulation context is alive (common in unit tests that exercise views in isolation). The direct .get_clone_plan() call on a None return will raise AttributeError, preventing view construction entirely. A guard identical to the fix needed in queries.py is required: sim_ctx = sim_utils.SimulationContext.instance(); plan = sim_ctx.get_clone_plan() if sim_ctx is not None else None.

Reviews (2): Last reviewed commit: "simplify clone plan" | Re-trigger Greptile

Comment on lines 44 to +47
# Obtain stage handle
stage = sim_utils.get_current_stage()

for i in range(num_envs):
# Resolve prim path
obj_path = prim_path.replace(".*", str(i))
sample_targets: list[tuple[str, tuple[int, ...]]] = []
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Crash when clone_plan is None or simulation context is missing

iter_clone_plan_matches unconditionally dereferences plan.sources, so if SimulationContext.instance() returns None or get_clone_plan() returns None (both are possible outside a Newton-replicated scene), the call at line 45 raises AttributeError. Additionally, if the clone plan has no matching entries, sample_targets is empty and points is returned without being filled for any environment — a silent data-corruption path that would produce zero-filled tensors for every env rather than a diagnostic error.

pos, quat = sim_utils.resolve_prim_pose(prim, body_prim)
body_path = body_prim.GetPath().pathString
if source_root is not None and destination_template is not None:
assert env_ids is not None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 assert in production code is silently stripped by -O

assert env_ids is not None is evaluated only in debug mode; running Python with -O or -OO silently skips this guard and proceeds with env_ids = None, which would later cause a crash inside the loop that iterates over env_ids. A proper if env_ids is None: raise ValueError(...) guard is needed here.

Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

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

🔬 Code Review Update: skip usd cloning in pure newton path

Re-review of commits 87c949f2...f49038c4 — This increment addresses several important issues and includes significant modernization of the build system.


📋 Incremental Changes Summary

1. Issue #4970 Fix: Stale sensor data after scene.reset(env_ids)

The core fix across joint_wrench_sensor.py, pva.py, and their respective kernels adds timestamp guards:

# Skip envs that have not been stepped since their last reset: PhysX's incoming joint
# wrench still holds pre-reset values, so reading it now would inject stale data (#4970).
if timestamp[env] == 0.0:
    return

Well-implemented — The timestamp guard at the Warp kernel level is the correct place to catch stale buffers before they propagate. The new regression tests in test_contact_sensor.py, test_imu.py, test_joint_wrench_sensor.py, and test_pva.py thoroughly validate the fix.

2. Build System Modernization: setup.pypyproject.toml

Migrated 8 packages to pure pyproject.toml:

  • isaaclab_physx
  • isaaclab_ppisp
  • isaaclab_rl
  • isaaclab_tasks
  • isaaclab_tasks_experimental
  • isaaclab_teleop
  • isaaclab_visualizers

Clean modernization — Removes toml build dependency, uses importlib.metadata for version retrieval. The isaaclab_rl changelog note about normalized extra names (rsl_rlrsl-rl, rl_gamesrl-games) is appropriate PEP 685 compliance.

3. Runtime Compatibility Validation

sim_launcher.py now validates incompatible backend combinations:

def validate_runtime_compatibility(env_cfg, launcher_args):
    """Validate that the resolved physics, renderer, and visualizer combination is supported.
    
    The OVRTX renderer is a kitless renderer that cannot run together with Isaac Sim / Kit
    runtimes (PhysxCfg physics or the Kit visualizer).
    """

Good UX improvement — Clear error messages guide users to the correct preset (isaacsim_rtx_renderer) instead of failing with opaque runtime errors. The new test_runtime_compatibility.py covers all valid/invalid combinations.

4. DexSuite Point-Cloud Sampling Fix

In isaaclab_tasks/.../mdp/utils.py:

from isaaclab.cloner.cloner_utils import iter_clone_plan_matches

sample_targets: list[tuple[str, tuple[int, ...]]] = []
clone_plan = sim_utils.SimulationContext.instance().get_clone_plan()
for _, _, source_path, env_ids in iter_clone_plan_matches(clone_plan, prim_path):
    sample_targets.append((source_path, env_ids))

Correctly addresses Newton replicated scenes — Instead of iterating num_envs and string-replacing .* with indices (which fails for Newton's source-only USD), this now properly resolves against ClonePlan sources and broadcasts samples to all destination env_ids.

5. Sensor Resolution Refactoring

Multiple sensors (joint_wrench_sensor.py, pva.py, ray_caster.py) now use:

  • resolve_matching_prims_from_source() instead of find_first_matching_prim()
  • Proper ClonePlan-aware source/destination resolution

Consistent pattern — All sensors now follow the same ClonePlan-aware resolution pattern established earlier in the PR.

6. Visualizer Test Refactoring

The monolithic test_visualizer_cartpole_integration.py is split into:

  • test_visualizer_integration_newton.py
  • test_visualizer_integration_physx.py
  • test_visualizer_tiled_integration_newton.py
  • test_visualizer_tiled_integration_physx.py
  • visualizer_integration_utils.py (shared utilities)

Good test hygiene — Separating by physics backend improves CI parallelization and failure isolation.


🟡 Minor Observations

1. source/isaaclab_tasks/isaaclab_tasks/manager_based/manipulation/dexsuite/mdp/observations.py:237-238

Deprecation warning fix for matplotlib colormap access:

import matplotlib
turbo = matplotlib.colormaps["turbo"]

✅ Good fix, though unrelated to the main PR topic.

2. setup.py still exists in isaaclab_visualizers/

The file was not deleted despite the new pyproject.toml. This appears intentional (the setup.py has different extras), but worth confirming if both are needed.


✅ Previous Findings Status

Finding Status
Interactive scene logic bug ✅ Fixed in 9e95f98f
iter_clone_plan_matches sorting optimization ⚠️ Still present (non-blocking)
Camera empty prims fallback test ⚠️ Still needs explicit test
Newton site frame view docstring expansion ⚠️ Optional improvement

📊 Test Coverage in This Increment

Component Coverage
#4970 stale data fix ✅ 4 new regression tests
Runtime compatibility ✅ 7 new tests
Visualizer integration ✅ Refactored, same coverage
DexSuite point-cloud ⚠️ Covered by existing integration tests

📋 Verdict

LGTM with minor notes — This increment successfully:

  1. Fixes a meaningful bug (#4970 stale sensor data after partial reset)
  2. Modernizes the build system to pure pyproject.toml
  3. Adds user-friendly validation for incompatible runtime combinations
  4. Ensures DexSuite point-cloud sampling works with Newton replicated scenes

The previous critical findings have been addressed. No new blocking issues found.


Reviewed commits: 87c949f2...f49038c4
Previous review SHA: 87c949f27ae97cc7fff8f7b922d42ccc9169b530

@ooctipus ooctipus force-pushed the zhengyuz/newton-clone-plan branch 2 times, most recently from 4639a8e to 690943d Compare May 23, 2026 01:56
@ooctipus ooctipus requested a review from jtigue-bdai as a code owner May 23, 2026 01:56
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label May 23, 2026
@ooctipus ooctipus force-pushed the zhengyuz/newton-clone-plan branch 5 times, most recently from e8da390 to ca54ca2 Compare May 26, 2026 23:44
@kellyguo11 kellyguo11 moved this to In review in Isaac Lab May 27, 2026
@ooctipus ooctipus force-pushed the zhengyuz/newton-clone-plan branch 2 times, most recently from 6c8a9fb to 87c949f Compare May 28, 2026 08:12
Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

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


Update (8374e25): This commit batch includes lint fixes and several merged PRs since the last review.

Changes in This Update

1. Newton Clone Plan Skip USD Cloning — The core feature continues to evolve:

  • newton_manager.py: Added world_sites tracking via _cl_inject_sites returning a third element and storing _world_xforms per cloned world
  • newton_site_frame_view.py: Major refactoring to use clone-plan-aware site resolution with _resolve_site_specs and _initialize_from_specs
  • ray_caster.py: Refactored to use clone plan for site registration with per_world flag support
  • joint_wrench_sensor.py: Switched to resolve_matching_prims_from_source for articulation root discovery

2. Fixed tendons support (PR #5522) — Added passive fixed tendon support for Newton/mjwarp:

  • New robot_tendon_properties event term for tendon parameter randomization
  • Shadow hand Newton scene uses MJCF version with fixed tendons

3. Various Fixes — GearAssembly env count, skrl MARL observation spaces, doc build, changelog tooling, OvPhysX Kit visualizer guard.

Assessment

Good progress — The clone plan integration is more complete with proper world-site tracking for per-environment transforms.

Tests updated — The test_site_injection.py and test_views_xform_prim_newton.py tests have been updated to cover the new functionality.

Outstanding Items (from previous review)

Still Open:

  1. utils.py:48clone_plan None handling still needed:

    clone_plan = sim_utils.SimulationContext.instance().get_clone_plan()
    for _, _, source_path, env_ids in iter_clone_plan_matches(clone_plan, prim_path):

    If clone_plan is None, this will crash. Should guard with if clone_plan is not None: or use the pattern from other call sites.

  2. newton_site_frame_view.py:290assert should be proper if guard:

    assert env_ids is not None

    Production code should use explicit if checks instead of assert (which can be disabled with -O).

No new blocking issues identified in this commit batch.

@ooctipus ooctipus force-pushed the zhengyuz/newton-clone-plan branch 2 times, most recently from 7e3381f to f49038c Compare May 28, 2026 21:48
@ooctipus
Copy link
Copy Markdown
Collaborator Author

@greptile-apps review again

from isaaclab.cloner.cloner_utils import path_source_path # noqa: PLC0415
from isaaclab.sim.simulation_context import SimulationContext # noqa: PLC0415

plan = SimulationContext.instance().get_clone_plan()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 SimulationContext.instance() not null-checked before attribute access

SimulationContext.instance() returns None when no simulation has been created (unit tests, headless scripts, or any code path that calls a sensor's init before the simulation context is alive). Chaining .get_clone_plan() directly on the result will raise AttributeError: 'NoneType' object has no attribute 'get_clone_plan'. Because resolve_matching_prims_from_source is called from every sensor's initialization path in this PR (Articulation, RigidObjectCollection, JointWrenchSensor, Camera, etc.), this is a broad regression affecting all those callers.

@ooctipus ooctipus force-pushed the zhengyuz/newton-clone-plan branch from 29f145f to 9eb3175 Compare May 29, 2026 04:19
ooctipus added 8 commits May 29, 2026 02:27
The camera spawn block was nested under the physics-body probe check, so a
camera whose prim_path has no existing prim yet (the common case) hit
probe_prim is None and skipped spawning entirely, leaving the camera view
with zero prims (and the verify that would have raised was skipped too).
Move the spawn out of the probe conditional so it always runs when a
spawner is configured; only the body-redirect stays gated on the probe.
@ooctipus ooctipus force-pushed the zhengyuz/newton-clone-plan branch from a30d973 to 74c43eb Compare May 29, 2026 09:27
@isaaclab-review-bot
Copy link
Copy Markdown

🤖 Follow-up Review (74c43eb)

Reviewed the incremental changes from 8374e2574c43eb.

Previously reported issues still present:

  • ⚠️ P1 in queries.py:392: SimulationContext.instance() not null-checked before .get_clone_plan()
  • ⚠️ P1 in utils.py:47: Same null-check issue with SimulationContext.instance()
  • ⚠️ P2 in newton_site_frame_view.py:291: assert env_ids is not None still used instead of proper runtime guard

New changes look good:

  • ✅ Gravity vector binding cleanup (now shares storage with Newton's per-env gravity)
  • ✅ Mid-tick collision decimation feature properly gated
  • ✅ Per-world site registration added with appropriate fallback handling
  • ✅ Test coverage added for gravity tracking and collision decimation
  • ✅ NewtonSiteFrameView refactored for clone-plan awareness

No new issues introduced in this commit.

# holds in practice; revert to ``not startswith("ovphysx")`` if
# ``physx.clone()`` errors on already-populated targets.
clone_usd=True,
clone_usd=not is_newton_replicated_scene or has_kit(),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can this just be not is_newton_replicated_scene? Since non-newton implies kit will be there?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we could also have ovphysx path which won't use kit

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

removed in follow up decouple cloner pr

first_env_root_prims = get_all_matching_child_prims(
first_env_matching_prim_path,
predicate=lambda prim: prim.HasAPI(UsdPhysics.ArticulationRootAPI)
and prim.GetAttribute("physxArticulation:articulationEnabled").Get() is not False,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

the and check for articulation enabled is removed completely, is this no longer needed?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

UsdPhysics.ArticulationRootAPI needs to exist but "physxArticulation:articulationEnabled" is currently a bit too strict for some usd, we can fix those usd and turn them on if thats better long run. but enable now will break them

def has_rigid_body_api(prim) -> bool:
return bool(prim.HasAPI(UsdPhysics.RigidBodyAPI))

matches = resolve_matching_prims_from_source(self.cfg.prim_path)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I see this block of code in many places, can we make a clean helper function like:

resolve_single_descendant_expr(
    cfg_path,
    predicate=...,
    api_name="RigidBodyAPI",
)

and use it everywhere

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ill clean up in follow up pr


"""

def _first_RigidObject_child_path(self):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Similar to other comment made, something similar to this helper could be used almost everywhere in the code

if suffix and sensor_expr.endswith(suffix):
return sensor_expr[: -len(suffix)]
return body_path
def _has_rigid_body_api(prim) -> bool:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This function is defined in too many places!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

will clean up in follow up pr

@StafaH StafaH merged commit e792b0b into isaac-sim:develop May 29, 2026
37 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in Isaac Lab May 29, 2026
@ooctipus ooctipus deleted the zhengyuz/newton-clone-plan branch May 30, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants