Description
NewtonManager._initialize_contacts() can grow the contact buffer without growing the collision pipeline's deterministic sorting buffers.
Newton enables deterministic sorting implicitly when contact matching is enabled. Isaac Lab's capacity-growth branch checks the global deterministic mode instead of the constructed pipeline's actual deterministic flag. With global determinism set to NOT_GUARANTEED, contact matching enabled, and a solver requesting more contacts than the initial pipeline capacity, the next collision call fails with a contact/sort capacity mismatch.
No application-specific scene or policy is required.
Minimal regression reproducer
Run this with pytest in an Isaac Lab environment with the Newton backend installed. It uses a real CPU Newton model and collision pipeline; only the solver's capacity request is stubbed to isolate allocation behavior. No GPU or simulator application launch is required by this test.
from types import SimpleNamespace
import newton
import warp as wp
from isaaclab.physics import PhysicsManager
from isaaclab_newton.physics import NewtonManager
def test_contact_matching_capacity_growth(monkeypatch):
builder = newton.ModelBuilder()
builder.add_ground_plane()
body = builder.add_body(
xform=wp.transform((0, 0, 0.5), wp.quat_identity())
)
builder.add_shape_sphere(body, radius=0.5)
model = builder.finalize(device="cpu")
fields = {
"_needs_collision_pipeline": True,
"_collision_pipeline": None,
"_contacts": None,
"_model": model,
"_deterministic_mode": wp.DeterministicMode.NOT_GUARANTEED,
"_solver": SimpleNamespace(get_max_contact_count=lambda: 80),
"_collision_cfg": SimpleNamespace(to_pipeline_args=lambda: {
"rigid_contact_max": 32,
"contact_matching": "latest",
}),
}
for name, value in fields.items():
monkeypatch.setattr(NewtonManager, name, value)
monkeypatch.setattr(PhysicsManager, "_device", "cpu")
NewtonManager._initialize_contacts()
pipeline = NewtonManager._collision_pipeline
contacts = NewtonManager._contacts
assert contacts.rigid_contact_max == 80
pipeline.collide(model.state(), contacts)
assert pipeline._sort_key_array.shape[0] == contacts.rigid_contact_max
Expected behavior
The contact and deterministic sort buffers both grow from 32 to 80, and collision detection succeeds.
Actual behavior
The contact buffer grows while the pipeline retains its original sort capacity. Collision detection raises ValueError because the capacities differ. The error explains that deterministic sorting uses fixed-capacity buffers for CUDA graph capture and requires an exact capacity match.
Affected source
The issue was observed with Isaac Lab revision c5c102380df23c206c53157bb54e9eb00c5ec0cc. The same allocation condition was also present in upstream develop at a8b4da3c29ae528b39d4b3c9444d782ce58d886d (source inspection; that upstream revision was not separately executed).
Proposed fix and validation
In the capacity-growth branch, replace:
if cls._deterministic_mode != wp.DeterministicMode.NOT_GUARANTEED:
with:
if cls._collision_pipeline.deterministic:
This selects the existing pipeline-rebuild path whenever sorting is actually enabled, preserving matching contact/sort capacity. The unsorted allocation path remains unchanged.
Implementation and regression tests: lgulich#2
Three real CPU collision-pipeline regression cases passed with the fix, each growing capacity from 32 to 80 and executing collision detection:
- Global
NOT_GUARANTEED, contact matching latest (implicit sorting).
- Global
GPU_TO_GPU, contact matching disabled (explicit determinism).
- Global
NOT_GUARANTEED, contact matching disabled (unsorted).
This is specifically an allocation-consistency fix; it does not claim to address other numerical instability or nonfinite-state failures.
Description
NewtonManager._initialize_contacts()can grow the contact buffer without growing the collision pipeline's deterministic sorting buffers.Newton enables deterministic sorting implicitly when contact matching is enabled. Isaac Lab's capacity-growth branch checks the global deterministic mode instead of the constructed pipeline's actual
deterministicflag. With global determinism set toNOT_GUARANTEED, contact matching enabled, and a solver requesting more contacts than the initial pipeline capacity, the next collision call fails with a contact/sort capacity mismatch.No application-specific scene or policy is required.
Minimal regression reproducer
Run this with pytest in an Isaac Lab environment with the Newton backend installed. It uses a real CPU Newton model and collision pipeline; only the solver's capacity request is stubbed to isolate allocation behavior. No GPU or simulator application launch is required by this test.
Expected behavior
The contact and deterministic sort buffers both grow from 32 to 80, and collision detection succeeds.
Actual behavior
The contact buffer grows while the pipeline retains its original sort capacity. Collision detection raises
ValueErrorbecause the capacities differ. The error explains that deterministic sorting uses fixed-capacity buffers for CUDA graph capture and requires an exact capacity match.Affected source
The issue was observed with Isaac Lab revision
c5c102380df23c206c53157bb54e9eb00c5ec0cc. The same allocation condition was also present in upstreamdevelopata8b4da3c29ae528b39d4b3c9444d782ce58d886d(source inspection; that upstream revision was not separately executed).Proposed fix and validation
In the capacity-growth branch, replace:
with:
This selects the existing pipeline-rebuild path whenever sorting is actually enabled, preserving matching contact/sort capacity. The unsorted allocation path remains unchanged.
Implementation and regression tests: lgulich#2
Three real CPU collision-pipeline regression cases passed with the fix, each growing capacity from 32 to 80 and executing collision detection:
NOT_GUARANTEED, contact matchinglatest(implicit sorting).GPU_TO_GPU, contact matchingdisabled(explicit determinism).NOT_GUARANTEED, contact matchingdisabled(unsorted).This is specifically an allocation-consistency fix; it does not claim to address other numerical instability or nonfinite-state failures.