From e4c40d2ce47f3e1bbc0131e1330ca6794d8d8c52 Mon Sep 17 00:00:00 2001 From: rebeccazhang0707 Date: Sun, 20 Sep 2026 11:06:35 +0800 Subject: [PATCH 1/3] Expose VBD rigid solver controls and compliant ALM selection --- .../concepts/solver-tuning/tune_vbd.rst | 36 ++++++++++++++++ .../vbd-rigid-solver-controls.minor.rst | 8 ++++ .../physics/vbd_manager_cfg.py | 34 +++++++++++++++ .../test/physics/test_vbd_core.py | 43 +++++++++++++++++++ 4 files changed, 121 insertions(+) create mode 100644 source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst diff --git a/docs/source/concepts/solver-tuning/tune_vbd.rst b/docs/source/concepts/solver-tuning/tune_vbd.rst index 543f8243d578..387e3e442e9f 100644 --- a/docs/source/concepts/solver-tuning/tune_vbd.rst +++ b/docs/source/concepts/solver-tuning/tune_vbd.rst @@ -187,6 +187,42 @@ Core Solve - 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 Joints and Body-Body Contacts +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These controls apply when VBD integrates rigid bodies, including rigid cable +segments in a coupled solver entry. They use the same configuration for standalone +and coupled VBD solvers. + +.. list-table:: + :header-rows: 1 + :widths: 30 70 + + * - Parameter + - Description + * - ``rigid_compliant_alm`` + - Default: ``None``. Preserves Newton's mode default. Newton 1.6 selects the legacy path and emits a deprecation warning when VBD integrates rigid bodies. Set ``True`` to adopt compliant ALM, or ``False`` to retain legacy behavior explicitly during migration. + * - ``rigid_avbd_alpha`` + - Default: ``None``. Shared C0 stabilization strength in ``[0, 1]`` for rigid joints and body-body contacts. Newton uses ``0.95`` on the legacy path and ``0.0`` with compliant ALM when unset. Joint-specific or contact-specific alpha overrides take precedence if a config subclass supplies them. + * - ``rigid_contact_hard`` + - Default: ``True``. Selects hard contacts on the legacy path; ``False`` selects penalty-only contacts. Deprecated by Newton 1.6. With compliant ALM enabled, contacts use the ALM path regardless of this setting. + * - ``rigid_body_contact_buffer_size`` + - Default: ``64``. Maximum body-body contact entries per rigid body. Increase it for per-body body-body contact buffer overflow warnings. This is separate from ``rigid_body_particle_contact_buffer_size`` and collision-pipeline capacities. + +Existing configurations keep their previous solver behavior. To migrate to +compliant ALM, set ``rigid_compliant_alm=True`` and author finite contact and joint +stiffness. Recheck deformation and contact response: values tuned for legacy hard +constraints may need retuning. Leaving ``rigid_avbd_alpha=None`` allows each mode +to select its own stabilization default. For example: + +.. code-block:: python + + vbd_cfg = VBDSolverCfg( + rigid_compliant_alm=True, + rigid_body_contact_buffer_size=256, + ) + + 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..ff4f59766d2f --- /dev/null +++ b/source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst @@ -0,0 +1,8 @@ +Added +^^^^^ + +* Exposed ``rigid_avbd_alpha``, ``rigid_contact_hard``, and + ``rigid_body_contact_buffer_size`` on ``VBDSolverCfg`` while preserving Newton's + existing defaults. Added ``rigid_compliant_alm`` for explicit migration from + Newton 1.6's deprecated legacy rigid-contact path; users can opt in with + ``rigid_compliant_alm=True`` and retune finite material stiffness as needed. 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..d70b5bbfc3eb 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,43 @@ 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 contacts, joints, drives, and limits. + + ``None`` preserves Newton's default, which selects the legacy path in Newton 1.6 + and emits a deprecation warning when VBD integrates rigid bodies. Set ``True`` to + adopt compliant ALM with finite authored stiffness, or ``False`` to retain the legacy + path explicitly during migration. Material parameters may need retuning. + """ + + rigid_avbd_alpha: float | None = None + """Shared C0 stabilization strength for rigid joints and body-body contacts. + + Values must be in ``[0, 1]``. ``None`` preserves Newton's mode defaults: ``0.95`` + for the legacy path and ``0.0`` for compliant ALM. Newton's joint-specific and + contact-specific alpha overrides take precedence when supplied by a config subclass. + """ + + rigid_contact_hard: bool = True + """Whether to use hard body-body contacts on the legacy VBD path. + + ``False`` selects legacy penalty-only contacts. This setting does not select the + contact mode when :attr:`rigid_compliant_alm` is ``True``. + + .. deprecated:: Newton 1.6 + Set :attr:`rigid_compliant_alm` to ``True`` and author finite contact stiffness. + """ + 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..b519fd40faa4 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,46 @@ 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": False, "rigid_avbd_alpha": 0.0, "rigid_body_contact_buffer_size": 256}, + id="legacy-cable-controls", + ), + pytest.param({"rigid_compliant_alm": False, "rigid_contact_hard": False}, id="legacy-penalty-contacts"), + pytest.param({"rigid_compliant_alm": True}, id="compliant-mode-defaults"), + pytest.param({"rigid_compliant_alm": True, "rigid_avbd_alpha": 0.25}, id="compliant-alpha-override"), + ], +) +def test_vbd_rigid_solver_controls(overrides): + """Public VBD controls preserve Newton defaults and reach the actual solver.""" + physics = importlib.import_module("isaaclab_newton.physics") + solver_cfg = physics.VBDSolverCfg(**overrides) + parameters = inspect.signature(SolverVBD).parameters + for name in ("rigid_compliant_alm", "rigid_avbd_alpha", "rigid_contact_hard", "rigid_body_contact_buffer_size"): + assert getattr(solver_cfg, name) == overrides.get(name, parameters[name].default) + + builder = ModelBuilder() + body = builder.add_body() + builder.add_shape_sphere(body=body, radius=0.1) + builder.color() + model = builder.finalize(device="cpu") + + reference = SolverVBD(model, **overrides) + solver = physics.NewtonVBDManager._create_solver(model, solver_cfg) + for name in ( + "rigid_compliant_alm", + "rigid_joint_alpha", + "rigid_contact_alpha", + "rigid_contact_hard", + "body_body_contact_buffer_pre_alloc", + ): + assert getattr(solver, name) == getattr(reference, name) + assert solver.body_body_contact_indices.shape == reference.body_body_contact_indices.shape + + 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") From 1209acbcdb3f531ed15b93d7862ab236cd5f8601 Mon Sep 17 00:00:00 2001 From: rebeccazhang0707 Date: Sun, 20 Sep 2026 11:38:52 +0800 Subject: [PATCH 2/3] Limit VBD controls to cable stabilization and capacity --- .../concepts/solver-tuning/tune_vbd.rst | 40 ++----------------- .../vbd-rigid-solver-controls.minor.rst | 8 ++-- .../physics/vbd_manager_cfg.py | 23 +---------- .../test/physics/test_vbd_core.py | 9 ++--- 4 files changed, 11 insertions(+), 69 deletions(-) diff --git a/docs/source/concepts/solver-tuning/tune_vbd.rst b/docs/source/concepts/solver-tuning/tune_vbd.rst index 387e3e442e9f..7a0f0b069858 100644 --- a/docs/source/concepts/solver-tuning/tune_vbd.rst +++ b/docs/source/concepts/solver-tuning/tune_vbd.rst @@ -181,48 +181,16 @@ 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_avbd_alpha`` + - Default: ``None``. Shared C0 stabilization strength in ``[0, 1]`` for rigid joints and body-body contacts. Leaving it unset preserves Newton's mode-dependent default. + * - ``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 Joints and Body-Body Contacts -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -These controls apply when VBD integrates rigid bodies, including rigid cable -segments in a coupled solver entry. They use the same configuration for standalone -and coupled VBD solvers. - -.. list-table:: - :header-rows: 1 - :widths: 30 70 - - * - Parameter - - Description - * - ``rigid_compliant_alm`` - - Default: ``None``. Preserves Newton's mode default. Newton 1.6 selects the legacy path and emits a deprecation warning when VBD integrates rigid bodies. Set ``True`` to adopt compliant ALM, or ``False`` to retain legacy behavior explicitly during migration. - * - ``rigid_avbd_alpha`` - - Default: ``None``. Shared C0 stabilization strength in ``[0, 1]`` for rigid joints and body-body contacts. Newton uses ``0.95`` on the legacy path and ``0.0`` with compliant ALM when unset. Joint-specific or contact-specific alpha overrides take precedence if a config subclass supplies them. - * - ``rigid_contact_hard`` - - Default: ``True``. Selects hard contacts on the legacy path; ``False`` selects penalty-only contacts. Deprecated by Newton 1.6. With compliant ALM enabled, contacts use the ALM path regardless of this setting. - * - ``rigid_body_contact_buffer_size`` - - Default: ``64``. Maximum body-body contact entries per rigid body. Increase it for per-body body-body contact buffer overflow warnings. This is separate from ``rigid_body_particle_contact_buffer_size`` and collision-pipeline capacities. - -Existing configurations keep their previous solver behavior. To migrate to -compliant ALM, set ``rigid_compliant_alm=True`` and author finite contact and joint -stiffness. Recheck deformation and contact response: values tuned for legacy hard -constraints may need retuning. Leaving ``rigid_avbd_alpha=None`` allows each mode -to select its own stabilization default. For example: - -.. code-block:: python - - vbd_cfg = VBDSolverCfg( - rigid_compliant_alm=True, - rigid_body_contact_buffer_size=256, - ) - - 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 index ff4f59766d2f..5e597049aa26 100644 --- a/source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst +++ b/source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst @@ -1,8 +1,6 @@ Added ^^^^^ -* Exposed ``rigid_avbd_alpha``, ``rigid_contact_hard``, and - ``rigid_body_contact_buffer_size`` on ``VBDSolverCfg`` while preserving Newton's - existing defaults. Added ``rigid_compliant_alm`` for explicit migration from - Newton 1.6's deprecated legacy rigid-contact path; users can opt in with - ``rigid_compliant_alm=True`` and retune finite material stiffness as needed. +* Exposed ``rigid_avbd_alpha`` and ``rigid_body_contact_buffer_size`` on + ``VBDSolverCfg`` for rigid cable stabilization 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 d70b5bbfc3eb..50c858bee7c4 100644 --- a/source/isaaclab_newton/isaaclab_newton/physics/vbd_manager_cfg.py +++ b/source/isaaclab_newton/isaaclab_newton/physics/vbd_manager_cfg.py @@ -58,31 +58,10 @@ 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 contacts, joints, drives, and limits. - - ``None`` preserves Newton's default, which selects the legacy path in Newton 1.6 - and emits a deprecation warning when VBD integrates rigid bodies. Set ``True`` to - adopt compliant ALM with finite authored stiffness, or ``False`` to retain the legacy - path explicitly during migration. Material parameters may need retuning. - """ - rigid_avbd_alpha: float | None = None """Shared C0 stabilization strength for rigid joints and body-body contacts. - Values must be in ``[0, 1]``. ``None`` preserves Newton's mode defaults: ``0.95`` - for the legacy path and ``0.0`` for compliant ALM. Newton's joint-specific and - contact-specific alpha overrides take precedence when supplied by a config subclass. - """ - - rigid_contact_hard: bool = True - """Whether to use hard body-body contacts on the legacy VBD path. - - ``False`` selects legacy penalty-only contacts. This setting does not select the - contact mode when :attr:`rigid_compliant_alm` is ``True``. - - .. deprecated:: Newton 1.6 - Set :attr:`rigid_compliant_alm` to ``True`` and author finite contact stiffness. + Values must be in ``[0, 1]``. ``None`` preserves Newton's mode-dependent default. """ rigid_contact_k_start: float = 1.0e2 diff --git a/source/isaaclab_newton/test/physics/test_vbd_core.py b/source/isaaclab_newton/test/physics/test_vbd_core.py index b519fd40faa4..2ae16e9989fb 100644 --- a/source/isaaclab_newton/test/physics/test_vbd_core.py +++ b/source/isaaclab_newton/test/physics/test_vbd_core.py @@ -213,12 +213,9 @@ def test_vbd_solver_force_input_capability(monkeypatch, external_rigid_solver): [ pytest.param({}, id="newton-defaults"), pytest.param( - {"rigid_compliant_alm": False, "rigid_avbd_alpha": 0.0, "rigid_body_contact_buffer_size": 256}, - id="legacy-cable-controls", + {"rigid_avbd_alpha": 0.0, "rigid_body_contact_buffer_size": 256}, + id="shoelace-controls", ), - pytest.param({"rigid_compliant_alm": False, "rigid_contact_hard": False}, id="legacy-penalty-contacts"), - pytest.param({"rigid_compliant_alm": True}, id="compliant-mode-defaults"), - pytest.param({"rigid_compliant_alm": True, "rigid_avbd_alpha": 0.25}, id="compliant-alpha-override"), ], ) def test_vbd_rigid_solver_controls(overrides): @@ -226,7 +223,7 @@ def test_vbd_rigid_solver_controls(overrides): physics = importlib.import_module("isaaclab_newton.physics") solver_cfg = physics.VBDSolverCfg(**overrides) parameters = inspect.signature(SolverVBD).parameters - for name in ("rigid_compliant_alm", "rigid_avbd_alpha", "rigid_contact_hard", "rigid_body_contact_buffer_size"): + for name in ("rigid_avbd_alpha", "rigid_body_contact_buffer_size"): assert getattr(solver_cfg, name) == overrides.get(name, parameters[name].default) builder = ModelBuilder() From 71a913e6f21097ea193666c7b06662b0d90349da Mon Sep 17 00:00:00 2001 From: rebeccazhang0707 Date: Thu, 24 Sep 2026 10:51:28 +0800 Subject: [PATCH 3/3] Expose compliant ALM selection for VBD rigid cables --- .../concepts/solver-tuning/tune_vbd.rst | 29 +++++++++- .../vbd-rigid-solver-controls.minor.rst | 4 +- .../physics/vbd_manager_cfg.py | 8 ++- .../test/physics/test_vbd_core.py | 58 +++++++++++++------ 4 files changed, 73 insertions(+), 26 deletions(-) diff --git a/docs/source/concepts/solver-tuning/tune_vbd.rst b/docs/source/concepts/solver-tuning/tune_vbd.rst index 7a0f0b069858..046245bb32cb 100644 --- a/docs/source/concepts/solver-tuning/tune_vbd.rst +++ b/docs/source/concepts/solver-tuning/tune_vbd.rst @@ -181,8 +181,8 @@ 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_avbd_alpha`` - - Default: ``None``. Shared C0 stabilization strength in ``[0, 1]`` for rigid joints and body-body contacts. Leaving it unset preserves Newton's mode-dependent default. + * - ``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`` @@ -191,6 +191,31 @@ Core Solve - 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 index 5e597049aa26..0f733529dcb9 100644 --- a/source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst +++ b/source/isaaclab_newton/changelog.d/vbd-rigid-solver-controls.minor.rst @@ -1,6 +1,6 @@ Added ^^^^^ -* Exposed ``rigid_avbd_alpha`` and ``rigid_body_contact_buffer_size`` on - ``VBDSolverCfg`` for rigid cable stabilization and per-body contact capacity, +* 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 50c858bee7c4..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,10 +58,12 @@ class VBDSolverCfg(NewtonSolverCfg): particle_rest_shape_contact_exclusion_radius: float = 0.0 """Rest-shape separation threshold for filtering contacts [m].""" - rigid_avbd_alpha: float | None = None - """Shared C0 stabilization strength for rigid joints and body-body contacts. + rigid_compliant_alm: bool | None = None + """Whether to use compliant ALM for rigid joints and body-body contacts. - Values must be in ``[0, 1]``. ``None`` preserves Newton's mode-dependent default. + ``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 diff --git a/source/isaaclab_newton/test/physics/test_vbd_core.py b/source/isaaclab_newton/test/physics/test_vbd_core.py index 2ae16e9989fb..e509b6943eb4 100644 --- a/source/isaaclab_newton/test/physics/test_vbd_core.py +++ b/source/isaaclab_newton/test/physics/test_vbd_core.py @@ -213,36 +213,56 @@ def test_vbd_solver_force_input_capability(monkeypatch, external_rigid_solver): [ pytest.param({}, id="newton-defaults"), pytest.param( - {"rigid_avbd_alpha": 0.0, "rigid_body_contact_buffer_size": 256}, - id="shoelace-controls", + {"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 reach the actual solver.""" + """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_avbd_alpha", "rigid_body_contact_buffer_size"): - assert getattr(solver_cfg, name) == overrides.get(name, parameters[name].default) + for name in ("rigid_compliant_alm", "rigid_body_contact_buffer_size"): + assert kwargs[name] == overrides.get(name, parameters[name].default) - builder = ModelBuilder() - body = builder.add_body() - builder.add_shape_sphere(body=body, radius=0.1) + +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") - - reference = SolverVBD(model, **overrides) + solver_cfg = physics.VBDSolverCfg(rigid_compliant_alm=True, rigid_body_contact_buffer_size=256) solver = physics.NewtonVBDManager._create_solver(model, solver_cfg) - for name in ( - "rigid_compliant_alm", - "rigid_joint_alpha", - "rigid_contact_alpha", - "rigid_contact_hard", - "body_body_contact_buffer_pre_alloc", - ): - assert getattr(solver, name) == getattr(reference, name) - assert solver.body_body_contact_indices.shape == reference.body_body_contact_indices.shape + 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):