diff --git a/docs/source/concepts/solver-tuning/tune_vbd.rst b/docs/source/concepts/solver-tuning/tune_vbd.rst index 543f8243d578..046245bb32cb 100644 --- a/docs/source/concepts/solver-tuning/tune_vbd.rst +++ b/docs/source/concepts/solver-tuning/tune_vbd.rst @@ -181,12 +181,41 @@ Core Solve - Description * - ``iterations`` - Default: ``10``. Number of VBD iterations per substep. Increasing this value improves deformation and contact convergence, especially for stiff materials or rigid gripper contacts, but increases runtime. + * - ``rigid_compliant_alm`` + - Default: ``None``. Preserves Newton's rigid solver mode. In Newton 1.6, ``None`` selects deprecated legacy AVBD. Set to ``True`` to use compliant ALM for rigid joints and body-body contacts, or ``False`` to explicitly retain legacy AVBD. + * - ``rigid_body_contact_buffer_size`` + - Default: ``64``. Per-body capacity for body-body contacts when VBD integrates rigid bodies. Increase it if Newton reports a per-body body-body contact buffer overflow. * - ``rigid_body_particle_contact_buffer_size`` - Default: ``256``. Per-body capacity for particle, edge, and face soft contacts. Increase it if Newton reports a per-body contact buffer overflow. * - ``integrate_with_external_rigid_solver`` - Default: ``False``. Set to ``True`` only when a manual manager integrates rigid bodies in the shared model. Proxy-coupled entries use partitioned model views and leave this ``False``. +Rigid Cables +^^^^^^^^^^^^ + +For new rigid-cable configurations, explicitly enable compliant ALM: + +.. code-block:: python + + from isaaclab_newton.physics import VBDSolverCfg + + cable_solver_cfg = VBDSolverCfg( + rigid_compliant_alm=True, + rigid_body_contact_buffer_size=256, + ) + +Compliant ALM uses finite material stiffness for rigid joints and body-body +contacts. Validate the cable's stretch, bend, and contact stiffness under the +intended loads and timestep when switching from legacy AVBD. Increasing contact +capacity only increases the available storage; it does not change stiffness. + +``VBDSolverCfg`` leaves Newton's C0 stabilization parameter ``rigid_avbd_alpha`` +unset. Newton 1.6 defaults it to ``0.0`` for compliant ALM and ``0.95`` for legacy +AVBD, for both rigid joints and body-body contacts. Setting alpha to zero alone +does not enable ALM. + + Self-Contact ^^^^^^^^^^^^ diff --git a/source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst b/source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst new file mode 100644 index 000000000000..0f733529dcb9 --- /dev/null +++ b/source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst @@ -0,0 +1,6 @@ +Added +^^^^^ + +* Exposed ``rigid_compliant_alm`` and ``rigid_body_contact_buffer_size`` on + ``VBDSolverCfg`` for compliant ALM selection and per-body contact capacity, + while preserving Newton's existing defaults. diff --git a/source/isaaclab_newton/isaaclab_newton/physics/vbd_manager_cfg.py b/source/isaaclab_newton/isaaclab_newton/physics/vbd_manager_cfg.py index 70306c861f35..dbbba6bb4550 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/vbd_manager_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/vbd_manager_cfg.py @@ -58,9 +58,24 @@ class VBDSolverCfg(NewtonSolverCfg): particle_rest_shape_contact_exclusion_radius: float = 0.0 """Rest-shape separation threshold for filtering contacts [m].""" + rigid_compliant_alm: bool | None = None + """Whether to use compliant ALM for rigid joints and body-body contacts. + + ``None`` preserves Newton's default, which selects deprecated legacy AVBD in Newton 1.6. + Set to ``True`` for new rigid-cable configurations and validate their finite material stiffnesses. + Newton's default C0 stabilization strength is ``0.0`` with compliant ALM and ``0.95`` with legacy AVBD. + """ + rigid_contact_k_start: float = 1.0e2 """Initial stiffness seed for rigid-body contacts [N/m].""" + rigid_body_contact_buffer_size: int = 64 + """Per-body capacity of the body-body contact list. + + Increase this value when Newton reports a per-body body-body contact buffer overflow. + Only used when :attr:`integrate_with_external_rigid_solver` is ``False``. + """ + rigid_body_particle_contact_buffer_size: int = 256 """Per-body capacity of the particle, edge, and face soft-contact list. diff --git a/source/isaaclab_newton/test/physics/test_vbd_core.py b/source/isaaclab_newton/test/physics/test_vbd_core.py index fc598952e137..e509b6943eb4 100644 --- a/source/isaaclab_newton/test/physics/test_vbd_core.py +++ b/source/isaaclab_newton/test/physics/test_vbd_core.py @@ -8,10 +8,13 @@ from __future__ import annotations import importlib +import inspect from types import SimpleNamespace import pytest from isaaclab_newton.physics import NewtonBackendCfg, NewtonManager, NewtonSoftContactCfg +from newton import ModelBuilder +from newton.solvers import SolverVBD from isaaclab.sim import SimulationContext @@ -205,6 +208,63 @@ def test_vbd_solver_force_input_capability(monkeypatch, external_rigid_solver): assert NewtonManager._supports_rigid_body_force_input is not external_rigid_solver +@pytest.mark.parametrize( + "overrides", + [ + pytest.param({}, id="newton-defaults"), + pytest.param( + {"rigid_compliant_alm": True, "rigid_body_contact_buffer_size": 256}, + id="compliant-alm", + ), + pytest.param({"rigid_compliant_alm": False}, id="legacy-mode"), + ], +) +def test_vbd_rigid_solver_controls(overrides): + """Public VBD controls preserve Newton defaults and survive kwargs filtering.""" + physics = importlib.import_module("isaaclab_newton.physics") + solver_cfg = physics.VBDSolverCfg(**overrides) + kwargs = NewtonManager._filter_solver_kwargs(SolverVBD, solver_cfg) + parameters = inspect.signature(SolverVBD).parameters + for name in ("rigid_compliant_alm", "rigid_body_contact_buffer_size"): + assert kwargs[name] == overrides.get(name, parameters[name].default) + + +def test_vbd_compliant_alm_cable_stiffness(): + """The manager's ALM solver retains finite cable stiffness under gravity.""" + physics = importlib.import_module("isaaclab_newton.physics") + gravity = 9.81 + stretch_stiffness = 1.0e3 + builder = ModelBuilder(gravity=(0.0, 0.0, -gravity)) + body = builder.add_link() + builder.add_shape_capsule(body=body, radius=0.01, half_height=0.1) + mass = builder.body_mass[body] + joint = builder.add_joint_rod( + parent=-1, + child=body, + stretch_stiffness=stretch_stiffness, + stretch_damping=2.0 * (mass * stretch_stiffness) ** 0.5, + bend_stiffness=5.0, + bend_damping=1.0, + ) + builder.add_articulation([joint]) + builder.color() + model = builder.finalize(device="cpu") + solver_cfg = physics.VBDSolverCfg(rigid_compliant_alm=True, rigid_body_contact_buffer_size=256) + solver = physics.NewtonVBDManager._create_solver(model, solver_cfg) + assert solver.rigid_compliant_alm is True + + state_0, state_1 = model.state(), model.state() + control = model.control() + for _ in range(120): + state_0.clear_forces() + solver.step(state_0, state_1, control, None, 1.0 / 240.0) + state_0, state_1 = state_1, state_0 + + # At equilibrium the spring force balances the weight: k * extension = m * g. + expected_extension = mass * gravity / stretch_stiffness + assert state_0.body_q.numpy()[body, 2] == pytest.approx(-expected_extension, rel=0.01) + + def test_vbd_rebuilds_particle_bvh_before_physics_step(monkeypatch): """VBD rebuilds its particle BVH before the base physics step.""" physics = importlib.import_module("isaaclab_newton.physics")