Skip to content

Fix OVPhysX device routing for CPU-resident tensor types - #7563

Merged
kellyguo11 merged 3 commits into
developfrom
antoiner/fix-ovphysx-cpu-only-types
Sep 4, 2026
Merged

Fix OVPhysX device routing for CPU-resident tensor types#7563
kellyguo11 merged 3 commits into
developfrom
antoiner/fix-ovphysx-cpu-only-types

Conversation

@AntoineRichard

@AntoineRichard AntoineRichard commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Description

_CPU_ONLY_TYPES in isaaclab_ov is the single constant deciding whether an OvPhysxView tensor buffer is allocated on, and validated against, host or device memory. It omitted eight tensor types that are CPU-resident even on a GPU simulation:

tensor type alias shape
ARTICULATION_CONTACT_OFFSET CONTACT_OFFSET [N, S]
ARTICULATION_REST_OFFSET REST_OFFSET [N, S]
RIGID_BODY_CONTACT_OFFSET RIGID_BODY_CONTACT_OFFSET [N, S]
RIGID_BODY_REST_OFFSET RIGID_BODY_REST_OFFSET [N, S]
ARTICULATION_BODY_DISABLE_GRAVITY BODY_DISABLE_GRAVITY [N, L]
RIGID_BODY_DISABLE_GRAVITY RIGID_BODY_DISABLE_GRAVITY [N]
ARTICULATION_DOF_DRIVE_TYPE DOF_DRIVE_TYPE [N, D]
ARTICULATION_DOF_DRIVE_MODEL DOF_DRIVE_MODEL [N, D, 3]

Because _native_device consults this set, it returned the simulation device for host-resident data. Two consequences:

  1. A hidden per-call host-to-device staging copy on every access, on a path the view believed was device-native. Silent, and not attributable to Isaac Lab in a profile. Values are still correct — ovphysx stages the mismatch transparently, so this is not a data-integrity bug.
  2. _check_device rejected a correctly placed buffer: allocating one of these on the host, where the data actually lives, raised OvPhysxView.DeviceMismatch, whose message advises moving the buffer yourself — precisely the advice that fails here.

No shipped Isaac Lab workflow reads these eight through the tensor path today, so this is latent rather than a live outage. It is not an obscure corner: Isaac Lab already authors these same properties through USD schema writes at scene construction, and the tensor path exists to change such values per-env at runtime.

Each of the eight is also exposed as a public alias carrying its shape, dtype and units, taken from the wheel's own type table. This module is the alias layer over the ovphysx enum, so referencing them raw would have left the candidate tuple a mix of aliases and raw enum members with no stated reason why these eight differed. Naming follows the existing convention: the ARTICULATION_ prefix is dropped for articulation types, as with DOF_STIFFNESS and BODY_MASS, and retained for rigid-body ones, as with RIGID_BODY_MASS.

A second, orthogonal gap surfaced during the audit: ovphysx documents ARTICULATION_DOF_DRIVE_TYPE as read-only, but it was missing from _READ_ONLY_NAMES, so set_attribute accepted writes and forwarded them. An audit of the full classification against the wheel's type table found this to be the only such gap — of the 21 types ovphysx documents read-only, 20 were already classified, with no misclassified members.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change — writes to articulation_dof_drive_type now raise OvPhysxView.ReadOnlyAttribute instead of being silently forwarded. Migration: remove the write; drive type is authored through the USD drive schema, not the tensor path.

Release backport

  • Backport this pull request to the active release branch after it merges into develop

Screenshots

Not applicable.

Checklist

  • 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 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

How it was tested

ovphysx exposes no residency query (ovphysx_tensor_spec_t carries dtype/ndim/shape and no device field), so residency was measured, not assumed: CUDA memcpys were counted via CUPTI around each binding read into a host buffer and into a device buffer, under the DirectGPU configuration isaaclab_ov itself sets ("/physics/suppressReadback": True). A host read showing DtoH means GPU-resident; no memcpy on the host read plus HtoD on the device read means CPU-resident.

Measured across every GPU sample scene in the ovphysx 0.5.11 wheel:

scene before after
two_articulations_gpu agree=40 MISMATCH=8 agree=48 MISMATCH=0
links_chain_sample_gpu agree=40 MISMATCH=8 agree=48 MISMATCH=0
mixed_base_articulations_gpu agree=36 MISMATCH=8 agree=44 MISMATCH=0
boxes_falling_on_groundplane_gpu agree=9 MISMATCH=3 agree=12 MISMATCH=0
volume_deformable_multi MISMATCH=0 MISMATCH=0
surface_deformable_material MISMATCH=0 MISMATCH=0

Every previously classified member measured CPU-resident as declared in all scenes, so the set was incomplete rather than wrong — there were no false positives to correct.

The guarding test was a tautology: it compared the view's derived set against the canonical set it is derived from, so it could not fail for any contents. It now asserts against an independent inventory of measured residency, mirroring the existing _EXPECTED_READ_ONLY_NAMES pattern. Both new assertions were confirmed to fail before the fix and pass after it; the read-only case fails with DID NOT RAISE ReadOnlyAttribute without the fix.

  • pytest source/isaaclab_ov/test/sim/test_ovphysx_view.py — 81 passed
  • test_articulation_helpers, test_rigid_object_helpers, test_deformable_object_helpers, test_ovphysx_scene_data_backend, test_randomize_rigid_body_material_mdp — all pass
  • uv run isaaclab -f — passes
  • tools/changelog/cli.py check develop — passes

test_views_xform_prim_ovphysx.py reports 23 failed / 28 passed both with and without this change; every failure is the process-global device lock covered by the device_split marker, which requires per-device re-invocation. Baselined on a clean tree to confirm it is unrelated.

Environment: ovphysx 0.5.11, Linux x86_64, Python 3.12, NVIDIA RTX 5000 Ada Generation Laptop GPU.

_CPU_ONLY_TYPES omitted eight tensor types that are CPU-resident even on a
GPU simulation: the articulation and rigid-body collision-shape contact and
rest offsets, both gravity-disable flags, and the DOF drive type and drive
model.

Because _native_device consults this set, it returned the simulation device
for host-resident data. Every access staged a hidden host-to-device copy on a
path the view believed was device-native, and _check_device rejected a buffer
placed on the host, where the data actually lives, with DeviceMismatch.

Residency was measured rather than assumed: ovphysx exposes no residency
query, so each type was classified by counting CUDA memcpys around a binding
read into a host buffer and into a device buffer, under the DirectGPU
configuration isaaclab_ov itself sets. All eight measured CPU-resident across
every GPU sample scene in the ovphysx wheel, and all previously classified
members measured as declared.

The guarding test compared the view's derived set against the canonical set it
is derived from, so it could not fail for any contents. It now asserts against
an independent inventory of measured residency, mirroring the existing
_EXPECTED_READ_ONLY_NAMES pattern, so an incomplete set is caught.
ovphysx documents ARTICULATION_DOF_DRIVE_TYPE as read-only, but the attribute
was missing from _READ_ONLY_NAMES, so set_attribute accepted writes to it and
forwarded them to the binding instead of raising.

An audit of the whole classification against the wheel's type table found this
to be the only gap: of the twenty-one types ovphysx documents as read-only,
twenty were already classified, and no member was misclassified.

This is a behavior change. Writes to articulation_dof_drive_type that were
previously accepted now raise OvPhysxView.ReadOnlyAttribute. Drive type is
authored through the USD drive schema, not the tensor path.

The existing read-only behavior test is parameterized over the expected
inventory, so adding the name there also asserts that a write raises and
leaves no binding behind.
@AntoineRichard
AntoineRichard requested a review from a team September 4, 2026 07:42
@github-actions github-actions Bot added bug Something isn't working isaac-lab Related to Isaac Lab team labels Sep 4, 2026
@greptile-apps

greptile-apps Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR corrects OVPhysX tensor routing for eight CPU-resident types and classifies articulation DOF drive type as read-only.

  • Adds the measured CPU-resident tensor types to the canonical CPU-only classification.
  • Rejects unsupported writes to articulation_dof_drive_type.
  • Replaces a tautological classification test with independent expected read-only and residency inventories.
  • Documents the routing fix and intentional compatibility change.

Confidence Score: 5/5

The PR appears safe to merge with no actionable correctness, security, or maintainability issues identified.

The new classifications consistently feed the existing device-routing and read-only checks, are covered by independent inventory assertions, and the intentional behavior change is documented with migration guidance.

Important Files Changed

Filename Overview
source/isaaclab_ov/isaaclab_ov/tensor_types.py Adds eight measured host-resident OVPhysX tensor types to the CPU-only routing set; no actionable defect was found.
source/isaaclab_ov/isaaclab_ov/sim/views/ovphysx_view.py Marks articulation DOF drive type read-only so unsupported writes fail before reaching OVPhysX.
source/isaaclab_ov/test/sim/test_ovphysx_view.py Adds independent inventories that verify the complete CPU-only and read-only classifications.
source/isaaclab_ov/changelog.d/ovphysx-cpu-only-tensor-types.minor.rst Clearly documents the corrected routing and migration required for the newly rejected write.

Reviews (1): Last reviewed commit: "Reject writes to the read-only DOF drive..." | Re-trigger Greptile

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

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.

Isaac Lab Review Bot

The change updates the canonical CPU-residency classification for eight OVPhysX tensor types and marks articulation_dof_drive_type read-only, with focused inventory tests and migration guidance. The proposed changelog-version finding is not supported by a trusted rule establishing that this repository requires a major fragment for this contract change.

  • Design and architecture: The reviewed design keeps device routing centralized in _CPU_ONLY_TYPES and derives the view’s name classification from that set, avoiding duplicate production tables. Using private _TT members avoids unnecessarily expanding the public tensor-type alias surface.
  • API: No public symbols are added or removed. Writes to articulation_dof_drive_type now deterministically raise ReadOnlyAttribute, while correctly host-resident buffers for the eight added types pass device validation. The behavior change and migration path are explicitly documented.
  • Implementation: The added classifications propagate through the existing read-only validation and native-device routing paths. The tests replace the prior self-derived CPU-only assertion with an independent measured inventory and extend the read-only inventory. The hard-coded inventory remains coupled to the supported OVPhysX wheel’s available enum members, which is a non-blocking compatibility tradeoff.

No blocking issues. No inline issue met the actionable-evidence threshold; the assessment above records the review feedback.

Automated review; human maintainers own approval decisions.

The eight types added to the CPU-only classification were referenced through
the raw ``_TT`` enum, leaving the candidate tuple as a mix of module aliases
and raw enum members and giving a future reader no reason why these eight were
different.

This module is the alias layer over the ovphysx enum, so a type worth
classifying is worth naming. Each alias carries the shape, dtype and units
taken from the wheel's own type table, which the raw references did not.

Naming follows the established convention: the ``ARTICULATION_`` prefix is
dropped for articulation types, as with DOF_STIFFNESS and BODY_MASS, and
retained for rigid-body ones, as with RIGID_BODY_MASS.
@kellyguo11

Copy link
Copy Markdown
Contributor

run-ci

@isaaclab-bot isaaclab-bot Bot added ci:run-docker Trigger the on-demand Docker and GPU CI workflow and removed ci:run-docker Trigger the on-demand Docker and GPU CI workflow labels Sep 4, 2026
@kellyguo11
kellyguo11 merged commit ec58631 into develop Sep 4, 2026
83 of 84 checks passed
@kellyguo11
kellyguo11 deleted the antoiner/fix-ovphysx-cpu-only-types branch September 4, 2026 22:10
@isaaclab-bot

isaaclab-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Backported to release/3.0.0 as f1f9576.

isaaclab-bot Bot pushed a commit that referenced this pull request Sep 4, 2026
# Description

`_CPU_ONLY_TYPES` in `isaaclab_ov` is the single constant deciding
whether an `OvPhysxView` tensor buffer is allocated on, and validated
against, host or device memory. It omitted eight tensor types that are
CPU-resident even on a GPU simulation:

| tensor type | alias | shape |
|---|---|---|
| `ARTICULATION_CONTACT_OFFSET` | `CONTACT_OFFSET` | `[N, S]` |
| `ARTICULATION_REST_OFFSET` | `REST_OFFSET` | `[N, S]` |
| `RIGID_BODY_CONTACT_OFFSET` | `RIGID_BODY_CONTACT_OFFSET` | `[N, S]` |
| `RIGID_BODY_REST_OFFSET` | `RIGID_BODY_REST_OFFSET` | `[N, S]` |
| `ARTICULATION_BODY_DISABLE_GRAVITY` | `BODY_DISABLE_GRAVITY` | `[N,
L]` |
| `RIGID_BODY_DISABLE_GRAVITY` | `RIGID_BODY_DISABLE_GRAVITY` | `[N]` |
| `ARTICULATION_DOF_DRIVE_TYPE` | `DOF_DRIVE_TYPE` | `[N, D]` |
| `ARTICULATION_DOF_DRIVE_MODEL` | `DOF_DRIVE_MODEL` | `[N, D, 3]` |

Because `_native_device` consults this set, it returned the simulation
device for host-resident data. Two consequences:

1. A hidden per-call host-to-device staging copy on every access, on a
path the view believed was device-native. Silent, and not attributable
to Isaac Lab in a profile. Values are still correct — ovphysx stages the
mismatch transparently, so this is not a data-integrity bug.
2. `_check_device` rejected a **correctly** placed buffer: allocating
one of these on the host, where the data actually lives, raised
`OvPhysxView.DeviceMismatch`, whose message advises moving the buffer
yourself — precisely the advice that fails here.

No shipped Isaac Lab workflow reads these eight through the tensor path
today, so this is latent rather than a live outage. It is not an obscure
corner: Isaac Lab already authors these same properties through USD
schema writes at scene construction, and the tensor path exists to
change such values per-env at runtime.

Each of the eight is also exposed as a public alias carrying its shape,
dtype and units, taken from the wheel's own type table. This module is
the alias layer over the ovphysx enum, so referencing them raw would
have left the candidate tuple a mix of aliases and raw enum members with
no stated reason why these eight differed. Naming follows the existing
convention: the `ARTICULATION_` prefix is dropped for articulation
types, as with `DOF_STIFFNESS` and `BODY_MASS`, and retained for
rigid-body ones, as with `RIGID_BODY_MASS`.

A second, orthogonal gap surfaced during the audit: ovphysx documents
`ARTICULATION_DOF_DRIVE_TYPE` as read-only, but it was missing from
`_READ_ONLY_NAMES`, so `set_attribute` accepted writes and forwarded
them. An audit of the full classification against the wheel's type table
found this to be the only such gap — of the 21 types ovphysx documents
read-only, 20 were already classified, with no misclassified members.

## Type of change

- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- **Breaking change** — writes to `articulation_dof_drive_type` now
raise `OvPhysxView.ReadOnlyAttribute` instead of being silently
forwarded. Migration: remove the write; drive type is authored through
the USD drive schema, not the tensor path.

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Screenshots

Not applicable.

## Checklist

- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the changelog and the corresponding version in the
extension's `config/extension.toml` file
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

## How it was tested

ovphysx exposes no residency query (`ovphysx_tensor_spec_t` carries
dtype/ndim/shape and no device field), so residency was **measured**,
not assumed: CUDA memcpys were counted via CUPTI around each binding
read into a host buffer and into a device buffer, under the DirectGPU
configuration `isaaclab_ov` itself sets (`"/physics/suppressReadback":
True`). A host read showing `DtoH` means GPU-resident; no memcpy on the
host read plus `HtoD` on the device read means CPU-resident.

Measured across every GPU sample scene in the ovphysx 0.5.11 wheel:

| scene | before | after |
|---|---|---|
| `two_articulations_gpu` | `agree=40 MISMATCH=8` | `agree=48
MISMATCH=0` |
| `links_chain_sample_gpu` | `agree=40 MISMATCH=8` | `agree=48
MISMATCH=0` |
| `mixed_base_articulations_gpu` | `agree=36 MISMATCH=8` | `agree=44
MISMATCH=0` |
| `boxes_falling_on_groundplane_gpu` | `agree=9 MISMATCH=3` | `agree=12
MISMATCH=0` |
| `volume_deformable_multi` | `MISMATCH=0` | `MISMATCH=0` |
| `surface_deformable_material` | `MISMATCH=0` | `MISMATCH=0` |

Every previously classified member measured CPU-resident as declared in
all scenes, so the set was incomplete rather than wrong — there were no
false positives to correct.

The guarding test was a tautology: it compared the view's derived set
against the canonical set it is derived from, so it could not fail for
any contents. It now asserts against an independent inventory of
measured residency, mirroring the existing `_EXPECTED_READ_ONLY_NAMES`
pattern. Both new assertions were confirmed to fail before the fix and
pass after it; the read-only case fails with `DID NOT RAISE
ReadOnlyAttribute` without the fix.

- `pytest source/isaaclab_ov/test/sim/test_ovphysx_view.py` — 81 passed
- `test_articulation_helpers`, `test_rigid_object_helpers`,
`test_deformable_object_helpers`, `test_ovphysx_scene_data_backend`,
`test_randomize_rigid_body_material_mdp` — all pass
- `uv run isaaclab -f` — passes
- `tools/changelog/cli.py check develop` — passes

`test_views_xform_prim_ovphysx.py` reports 23 failed / 28 passed both
with and without this change; every failure is the process-global device
lock covered by the `device_split` marker, which requires per-device
re-invocation. Baselined on a clean tree to confirm it is unrelated.

Environment: ovphysx 0.5.11, Linux x86_64, Python 3.12, NVIDIA RTX 5000
Ada Generation Laptop GPU.

(cherry picked from commit ec58631)
kellyguo11 added a commit that referenced this pull request Sep 5, 2026
…7569)

# Description

> **Stacked on #7563.** This branch needs the `REST_OFFSET` /
`CONTACT_OFFSET` / `RIGID_BODY_*_OFFSET` aliases and the CPU-only
routing for those tensor types that #7563 adds. Review/merge that first;
this diff collapses to a single commit once it lands, at which point the
base should be retargeted to `develop`.

`randomize_rigid_body_collider_offsets` had no OVPhysX implementation.
Its backend dispatch tested only for `"newton"` and fell through a bare
`else` to `_RandomizeRigidBodyColliderOffsetsPhysx`, which calls
`get_rest_offsets` / `get_contact_offsets` / `set_rest_offsets` /
`set_contact_offsets` on `asset.root_view`. On OVPhysX that view is an
`OvPhysxView`, which defines none of them, so adding the term to a
config raised `AttributeError` during `OvPhysxManager.reset()` (via
`PHYSICS_READY`) and the environment never built. The error surfaced
from inside the PhysX implementation, so it did not name the unsupported
backend either.

Found by the OVQA agent harness on Isaac-Cartpole (release/3.0.0,
ovphysx 0.5.11). No shipped task uses the term today, so this is latent
rather than a live regression.

**Fix**

- New `_RandomizeRigidBodyColliderOffsetsOvPhysx`, mirroring the PhysX
variant. OVPhysX runs the PhysX solver, so rest/contact offsets are
written directly, per collision shape, through the asset's
`OvPhysxView`: `REST_OFFSET` / `CONTACT_OFFSET` for articulations,
`RIGID_BODY_REST_OFFSET` / `RIGID_BODY_CONTACT_OFFSET` for rigid
objects. Both bindings are CPU-resident `[N, S]` buffers, so the full
tensor is read-modify-written on the host with the selected envs as
write indices.
- Dispatch now matches `randomize_rigid_body_material` in the same file:
`ovphysxmanager` first (it contains the substring `physx`), then Newton,
then PhysX, and a `ValueError` naming the manager for anything else
instead of silently selecting PhysX.
- Docstring lists OVPhysX as a supported backend.

**Note on write order**

PhysX enforces `restOffset < contactOffset` and on OVPhysX a violating
`setRestOffset` is only logged, not raised. The term writes rest before
contact, same as the PhysX variant, so raising both above a small
authored contact offset can silently skip the rest write. Kept for PhysX
parity; flagging in case we want to reorder in both backends.

**Verification**

New
`source/isaaclab_ov/test/test_randomize_rigid_body_collider_offsets_mdp.py`
drives the public term (stubbed `cfg` / `env` / `asset_cfg`) against a
real OVPhysX `RigidObject` and `Articulation`, so the dispatch itself is
covered. It reproduced the reported `AttributeError` before the fix.
Assertions: selected envs land in the sampled range, unselected envs are
untouched, and omitting one distribution leaves that offset alone.
Passes on `cuda:0` and `cpu` (separate processes, ovphysx device lock).
PhysX and Newton paths are unchanged.

Dependencies: #7563.

Fixes # (no tracking issue; reported by the OVQA agent harness)

## Type of change

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

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Screenshots

Not applicable: no visual change.

## Checklist

Docker and GPU tests run on demand. Push the commits you want tested,
then
comment `run-ci` on the pull request.

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] 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)
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>
isaaclab-bot Bot pushed a commit that referenced this pull request Sep 5, 2026
…7569)

# Description

> **Stacked on #7563.** This branch needs the `REST_OFFSET` /
`CONTACT_OFFSET` / `RIGID_BODY_*_OFFSET` aliases and the CPU-only
routing for those tensor types that #7563 adds. Review/merge that first;
this diff collapses to a single commit once it lands, at which point the
base should be retargeted to `develop`.

`randomize_rigid_body_collider_offsets` had no OVPhysX implementation.
Its backend dispatch tested only for `"newton"` and fell through a bare
`else` to `_RandomizeRigidBodyColliderOffsetsPhysx`, which calls
`get_rest_offsets` / `get_contact_offsets` / `set_rest_offsets` /
`set_contact_offsets` on `asset.root_view`. On OVPhysX that view is an
`OvPhysxView`, which defines none of them, so adding the term to a
config raised `AttributeError` during `OvPhysxManager.reset()` (via
`PHYSICS_READY`) and the environment never built. The error surfaced
from inside the PhysX implementation, so it did not name the unsupported
backend either.

Found by the OVQA agent harness on Isaac-Cartpole (release/3.0.0,
ovphysx 0.5.11). No shipped task uses the term today, so this is latent
rather than a live regression.

**Fix**

- New `_RandomizeRigidBodyColliderOffsetsOvPhysx`, mirroring the PhysX
variant. OVPhysX runs the PhysX solver, so rest/contact offsets are
written directly, per collision shape, through the asset's
`OvPhysxView`: `REST_OFFSET` / `CONTACT_OFFSET` for articulations,
`RIGID_BODY_REST_OFFSET` / `RIGID_BODY_CONTACT_OFFSET` for rigid
objects. Both bindings are CPU-resident `[N, S]` buffers, so the full
tensor is read-modify-written on the host with the selected envs as
write indices.
- Dispatch now matches `randomize_rigid_body_material` in the same file:
`ovphysxmanager` first (it contains the substring `physx`), then Newton,
then PhysX, and a `ValueError` naming the manager for anything else
instead of silently selecting PhysX.
- Docstring lists OVPhysX as a supported backend.

**Note on write order**

PhysX enforces `restOffset < contactOffset` and on OVPhysX a violating
`setRestOffset` is only logged, not raised. The term writes rest before
contact, same as the PhysX variant, so raising both above a small
authored contact offset can silently skip the rest write. Kept for PhysX
parity; flagging in case we want to reorder in both backends.

**Verification**

New
`source/isaaclab_ov/test/test_randomize_rigid_body_collider_offsets_mdp.py`
drives the public term (stubbed `cfg` / `env` / `asset_cfg`) against a
real OVPhysX `RigidObject` and `Articulation`, so the dispatch itself is
covered. It reproduced the reported `AttributeError` before the fix.
Assertions: selected envs land in the sampled range, unselected envs are
untouched, and omitting one distribution leaves that offset alone.
Passes on `cuda:0` and `cpu` (separate processes, ovphysx device lock).
PhysX and Newton paths are unchanged.

Dependencies: #7563.

Fixes # (no tracking issue; reported by the OVQA agent harness)

## Type of change

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

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

## Screenshots

Not applicable: no visual change.

## Checklist

Docker and GPU tests run on demand. Push the commits you want tested,
then
comment `run-ci` on the pull request.

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] 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)
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>

(cherry picked from commit 1b182e7)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants