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..d1a9effc177e 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/vbd_manager_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/vbd_manager_cfg.py @@ -40,11 +40,7 @@ class VBDSolverCfg(NewtonSolverCfg): """Self-contact detection margin [m].""" particle_collision_detection_interval: int = -1 - """How often particle self-contact detection is applied. - - ``< 0``: once before initialization. ``0``: once before and once after - initialization. ``k >= 1``: before every ``k`` VBD iterations. - """ + """Self-contact detection: <0 before init, 0 before and after init, k>=1 before every k VBD iterations.""" particle_vertex_contact_buffer_size: int = 32 """Preallocation size for each vertex contact buffer.""" @@ -58,12 +54,14 @@ 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 contacts; ``None`` preserves Newton's default.""" + rigid_contact_k_start: float = 1.0e2 """Initial stiffness seed for rigid-body contacts [N/m].""" - rigid_body_particle_contact_buffer_size: int = 256 - """Per-body capacity of the particle, edge, and face soft-contact list. + rigid_body_contact_buffer_size: int = 64 + """Per-body body-body contact capacity when VBD integrates rigid bodies.""" - Increase this value when Newton reports a per-body particle contact buffer overflow. - Only used when :attr:`integrate_with_external_rigid_solver` is ``False``. - """ + rigid_body_particle_contact_buffer_size: int = 256 + """Per-body particle, edge, and face soft-contact capacity when VBD integrates rigid bodies.""" diff --git a/source/isaaclab_newton/test/physics/test_vbd_core.py b/source/isaaclab_newton/test/physics/test_vbd_core.py index 407e6eaae8e0..3fd73bd439e8 100644 --- a/source/isaaclab_newton/test/physics/test_vbd_core.py +++ b/source/isaaclab_newton/test/physics/test_vbd_core.py @@ -13,6 +13,7 @@ import pytest from isaaclab_newton.physics import NewtonBackendCfg, NewtonManager, NewtonSoftContactCfg from newton import ModelBuilder +from newton.solvers import SolverVBD from isaaclab.sim import SimulationContext @@ -113,6 +114,52 @@ def test_vbd_solver_force_input_capability(monkeypatch): assert NewtonManager._supports_rigid_body_force_input is False +@pytest.mark.parametrize("overrides", [{}, {"rigid_compliant_alm": False}], ids=["defaults", "legacy"]) +def test_vbd_rigid_solver_controls(overrides): + """VBD preserves the default controls and explicit legacy-mode selection.""" + physics = importlib.import_module("isaaclab_newton.physics") + kwargs = NewtonManager._filter_solver_kwargs(SolverVBD, physics.VBDSolverCfg(**overrides)) + assert kwargs["rigid_compliant_alm"] is overrides.get("rigid_compliant_alm") + assert kwargs["rigid_body_contact_buffer_size"] == 64 + + +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 + assert solver.body_body_contact_indices.size == model.body_count * 256 + + 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")