Skip to content

Add DART 7 body deactivation#2939

Open
jslee02 wants to merge 16 commits into
mainfrom
feature/dart7-deactivation-sleeping
Open

Add DART 7 body deactivation#2939
jslee02 wants to merge 16 commits into
mainfrom
feature/dart7-deactivation-sleeping

Conversation

@jslee02

@jslee02 jslee02 commented Jun 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds opt-in DART 7 body deactivation ("sleeping") for supported rigid bodies and semi-implicit multibodies, disabled by default.
  • Exposes DeactivationOptions, World.deactivation_options, World.deactivation_enabled, and body sleep-state/group accessors in C++ and dartpy.
  • Adds a deactivation_sleeping Python GUI demo in py-demos with sleep-state diagnostics and headless capture coverage.

Motivation / Problem

  • DART 7 needed the DART 6 sleeping/deactivation workflow without changing default forward results for existing simulations.
  • Downstream users need a public, solver-aware way to opt in, inspect whether bodies are sleeping, and wake sleeping bodies through ordinary disturbances or active contact.
  • The feature should be visible in the maintained Python-first GUI example surface, not only in unit tests.

Before / After

Area Before After
Default behavior Every supported body stayed active every step. Unchanged unless DeactivationOptions.enabled is set.
Public API No DART 7 sleep-state options or body accessors. C++ and dartpy expose deactivation options plus sleep/group queries.
Step behavior Sleeping candidates could not be skipped or woken as contact islands. Quiet supported bodies freeze as islands and wake on active contact or explicit disturbance.
Python examples No GUI example showed sleeping state. pixi run py-demos -- --scene deactivation_sleeping shows sleep/wake diagnostics.

Changes / Key Changes

  • Adds DeactivationOptions to WorldOptions and World setters/getters, with deactivation disabled by default.
  • Stores deactivation state as an ECS component, clears it when the feature is disabled, and serializes it through replay/binary IO.
  • Filters sleeping bodies out of rigid and multibody step stages while preserving wake behavior for forces, commands, and active contacts.
  • Exposes sleep-state APIs through dartpy and generated stubs.
  • Adds C++ and Python regression coverage plus the new py-demo scene and README entry.

Testing

  • pixi run build
  • pixi run lint
  • cmake --build build/default/cpp/Release --target test_deactivation
  • ctest --test-dir build/default/cpp/Release -R '^test_deactivation$' --output-on-failure
  • PYTHONPATH=build/default/cpp/Release/python pixi run pytest python/tests/unit/simulation/test_world.py -q
  • cmake --build build/default/cpp/Release --target test_world
  • build/default/cpp/Release/bin/test_world --gtest_filter=World.ReservedRegistryStorageReusesComponentCapacity:World.BakedRigidBodyContactStepsDoNotAllocateGlobalHeap:World.BakedArticulatedContactStepsDoNotAllocateGlobalHeap
  • PYTHONPATH=build/default/cpp/Release/python:python pixi run pytest python/tests/unit/test_py_demo_panels.py::test_deactivation_sleeping_scene_exposes_sleep_state_panel python/tests/unit/test_py_demo_panels.py::test_high_value_world_scenes_expose_custom_panels -q
  • PYTHONPATH=build/default/cpp/Release/python:python pixi run pytest python/tests/integration/test_demos_cycle.py::test_world_scenes_use_solver_focused_categories python/tests/integration/test_demos_cycle.py::test_deactivation_sleeping_demo_sleeps_then_wakes_contact_target -q
  • PYTHONPATH=build/default/cpp/Release/python:python pixi run py-demos -- --list
  • LIBGL_ALWAYS_SOFTWARE=1 MESA_LOADER_DRIVER_OVERRIDE=llvmpipe PYTHONPATH=build/default/cpp/Release-docking/python:python pixi run py-demo-capture -- --scene deactivation_sleeping --show-ui --frames 150 --width 1280 --height 720
  • CI follow-up after merging latest main:
    • pixi run lint
    • cmake --build build/default/cpp/Debug --target test_world test_serialization test_deactivation -j2
    • build/default/cpp/Debug/bin/test_serialization --gtest_filter=Serialization.RejectsInvalidWorldSolverOptionTail:Serialization.LegacyWorldMetadataDoesNotConsumeTrailingBytesAsGravity:Serialization.SimulationModeReloadReservesRegistryStorageBeforeStep
    • build/default/cpp/Debug/bin/test_world --gtest_filter=World.EnterSimulationModeReservesRegistryStorageForKinematicIpcSteps:World.EnterSimulationModeReservesRegistryStorageForMultibodySteps:World.SetMultibodyOptionsReservesVariationalStateAfterBake:World.SolverMethodSwitchesAfterBakeSharePreparationPath:World.EnterSimulationModeReservesRegistryStorageForDeformableSteps:World.BakedKinematicIpcStepsDoNotAllocateGlobalHeap:World.BakedRigidBodyContactStepsDoNotAllocateGlobalHeap:World.BakedArticulatedContactStepsDoNotAllocateGlobalHeap:World.BakedMultibodyAndDeformableStepsDoNotAllocateGlobalHeap:World.SetRigidBodySolverPreparesIpcScratchAfterSimulationBake:World.StepWithCustomStageReusesPreparedSolverStages
    • build/default/cpp/Debug/bin/test_deactivation
    • cmake --build build/default/cpp/Release --target test_world test_serialization test_deactivation -j2
    • build/default/cpp/Release/bin/test_serialization --gtest_filter=Serialization.RejectsInvalidWorldSolverOptionTail:Serialization.LegacyWorldMetadataDoesNotConsumeTrailingBytesAsGravity:Serialization.SimulationModeReloadReservesRegistryStorageBeforeStep
    • build/default/cpp/Release/bin/test_world --gtest_filter=World.EnterSimulationModeReservesRegistryStorageForKinematicIpcSteps:World.EnterSimulationModeReservesRegistryStorageForMultibodySteps:World.SetMultibodyOptionsReservesVariationalStateAfterBake:World.SolverMethodSwitchesAfterBakeSharePreparationPath:World.EnterSimulationModeReservesRegistryStorageForDeformableSteps:World.BakedKinematicIpcStepsDoNotAllocateGlobalHeap:World.BakedRigidBodyContactStepsDoNotAllocateGlobalHeap:World.BakedArticulatedContactStepsDoNotAllocateGlobalHeap:World.BakedMultibodyAndDeformableStepsDoNotAllocateGlobalHeap:World.SetRigidBodySolverPreparesIpcScratchAfterSimulationBake:World.StepWithCustomStageReusesPreparedSolverStages
    • build/default/cpp/Release/bin/test_deactivation

Visual capture note: the capture helper produced a nonblank screenshot and 149 PNG frames locally. The CLI path here cannot upload GitHub user-attachment images; a maintainer can upload the local artifact through the PR editor if inline visual evidence is required.

Breaking Changes

  • None

Related Issues / PRs (backports)


Checklist

  • Milestone set (DART 7.0 for main, DART 6.17.1 for release-6.17)
  • CHANGELOG.md updated if required
  • Add unit tests for new functionality
  • Document new methods and classes
  • Add Python bindings (dartpy) if applicable

@jslee02 jslee02 added this to the DART 7.0 milestone Jun 7, 2026
@jslee02

jslee02 commented Jun 7, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@jslee02 jslee02 marked this pull request as ready for review June 8, 2026 00:27
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@codecov

codecov Bot commented Jun 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.63180% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.08%. Comparing base (c6792aa) to head (3e70b9c).
⚠️ Report is 22 commits behind head on main.

Files with missing lines Patch % Lines
dart/simulation/world.cpp 91.64% 34 Missing ⚠️
dart/simulation/compute/multibody_dynamics.cpp 71.42% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2939      +/-   ##
==========================================
+ Coverage   84.81%   85.08%   +0.26%     
==========================================
  Files         692      702      +10     
  Lines       87571    92823    +5252     
==========================================
+ Hits        74272    78975    +4703     
- Misses      13299    13848     +549     
Flag Coverage Δ
unittests 85.08% <91.63%> (+0.26%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dart/simulation/body/rigid_body.cpp 90.63% <100.00%> (+0.17%) ⬆️
dart/simulation/comps/dynamics.hpp 100.00% <100.00%> (ø)
dart/simulation/compute/world_step_stage.cpp 93.43% <100.00%> (+0.28%) ⬆️
dart/simulation/io/binary_io.hpp 100.00% <ø> (ø)
dart/simulation/io/serializer.cpp 74.33% <100.00%> (-2.22%) ⬇️
dart/simulation/multibody/multibody.cpp 97.36% <100.00%> (-1.68%) ⬇️
dart/simulation/multibody/multibody.hpp 50.00% <ø> (ø)
dart/simulation/world.hpp 100.00% <ø> (ø)
dart/simulation/compute/multibody_dynamics.cpp 88.51% <71.42%> (+5.26%) ⬆️
dart/simulation/world.cpp 94.18% <91.64%> (+0.40%) ⬆️

... and 47 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

jslee02 and others added 9 commits June 13, 2026 06:35
…ation-sleeping

# Conflicts:
#	dart/simulation/compute/world_step_stage.cpp
#	dart/simulation/io/binary_io.hpp
#	dart/simulation/world.cpp
#	python/tests/integration/test_demos_cycle.py
#	tests/unit/simulation/world/test_serialization.cpp
Resolve conflicts from main's snapshot/profiling refactor (#2996) and the
contact-query/guard changes landing alongside it:

- world.cpp: adopt SnapshotVector/ComponentSnapshot replay storage and keep the
  new DeactivationState snapshot; order m_deactivationOptions correctly in the
  constructor init list; thread replayAllocator through the deactivation replay
  capture and restore calls to match the new helper signatures.
- multibody_dynamics.cpp / world_step_stage.cpp / world.hpp: adapt the
  deactivation contact filter to main's std::span<const Contact> queryContacts
  API while preserving main's new hasAnyCollisionShapes early-out and
  mayUseAvbdContactDetails query.
- registry.py / README.md / test_demos_cycle.py / test_py_demo_panels.py: keep
  main's reorganized demo catalog and re-apply the deactivation_sleeping scene
  entries, import, panel, and tests.
- test_world.py: keep both sides' forbidden camelCase binding names.
- api-boundary-inventory.md: regenerated.
… tests

Bring the DART 7 deactivation port to full parity with #2920 and broaden test
coverage of the new sleeping paths:

- world.cpp: a contact island may sleep only once every contact's penetration
  has converged below 1e-3 (including penetration against static geometry), so
  quiet bodies are never frozen mid-interpenetration. Matches the DART 6
  resting-scene gate.
- test_deactivation.cpp: add coverage for the penetration gate
  (PenetratingContactDefersSleepUntilConverged), gravity-loaded resting-contact
  sleeping (RestingRigidBodyOnGroundSleepsUnderGravity), and semi-implicit
  multibody sleeping/waking (RestingMultibodySleepsAndFreezes,
  LinkForceWakesSleepingMultibody, ContactWithActiveBodyWakesSleepingMultibody),
  covering the previously-untested multibody deactivation paths.
Pick up the C++23 bump (#3005), AVBD spring/collision-pair work (#3004),
allocator-backed Dantzig scratch change (#3011), and Python 3.14 minimum
(#3010). Conflicts:

- api-boundary-inventory.md: accept main's deletion; #3005 made the inventory an
  untracked, gitignored on-demand report (`report-api-boundary-inventory`) and
  dropped the `check-api-boundary-inventory` gate.

world_step_stage.cpp, test_demos_cycle.py, test_py_demo_panels.py, and
CHANGELOG.md auto-merged cleanly.
Pick up the PLAN-091 stage-contract refactor (#3003) and the string-component-ID
serialization change. Conflicts:

- world_options.hpp / world.hpp / world.cpp: keep both the deactivation options
  (DeactivationOptions / m_deactivationOptions) and main's new
  strictSolverResolution member; order m_deactivationOptions before
  m_strictSolverResolution to match the constructor init list.
- binary_io.hpp: main took format version 20 (AvbdJointStiffness component) and
  21 (stable string component IDs); renumber the deactivation-options payload to
  version 22 and bump kBinaryFormatVersion to 22.
- world.cpp: gate the deactivation-options read on formatVersion >= 22.
- comps/dynamics.hpp: give DeactivationState an explicit "comps.DeactivationState"
  stable ID for the new string-ID component identity scheme.
clang-format wraps the two-argument DART_SIMULATION_PROPERTY_COMPONENT call
that gained the stable string ID in the main merge.
… allocation-free

RigidIpcContactStage::prepare pre-warmed only the assembly scratch, so the
projected-Newton solve's motion-dependent CCD and line-search scratch (and the
FreeListAllocator pool blocks backing them) were materialized lazily on the
first World::step. That grew the World base allocator after the bake boundary
and tripped World.BakedDynamicRigidIpcStepsDoNotGrowWorldBaseAllocator (a
pre-existing failure on main, surfaced here by merging main into this branch).

Run a one-shot warm-up solve in prepare(), driven by the real dynamics terms so
the bodies approach contact and the full assembly/line-search/friction scratch
is reserved at bake. The warm-up writes only to discarded scratch and result --
it never advances a body or mutates the World -- so it is side-effect free.

Validated: the allocator guard tests, test_rigid_ipc_barrier (55),
test_rigid_ipc_paper_experiments (45), test_boxed_lcp_contact (122),
test_deactivation (10), and full test_world (372) all pass.
@jslee02

jslee02 commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant