Skip to content

Add dedicated gravity for global world -1 - #3724

Open
eric-heiden wants to merge 6 commits into
newton-physics:mainfrom
eric-heiden:eric-heiden/global-world-gravity
Open

Add dedicated gravity for global world -1#3724
eric-heiden wants to merge 6 commits into
newton-physics:mainfrom
eric-heiden:eric-heiden/global-world-gravity

Conversation

@eric-heiden

@eric-heiden eric-heiden commented Jul 29, 2026

Copy link
Copy Markdown
Member

Description

Closes #3723.
Closes #3692.
Closes #3715.

Give Model.gravity a dedicated final entry for global world -1, so global entities no longer inherit world 0 gravity. Entity-indexed gravity reads now use their world indices directly across the core solvers, VBD, Style3D, implicit MPM, MuJoCo coupling, sensors, and inverse dynamics.

This fully incorporates the VBD fix from #3695 and the IMU fix from #3716 as separate commits, preserving Junseo Oh's (@pietis) authorship:

Runtime gravity updates remain backward compatible with arrays containing only local-world values, while world=-1 and full arrays can update the global value explicitly.

Checklist

  • New or existing tests cover these changes
  • The documentation is up to date with these changes
  • CHANGELOG.md has been updated (if user-facing change)

Test plan

uv run --extra dev python -m unittest newton.tests.test_runtime_gravity.TestRuntimeGravity.test_per_world_gravity_particles_vbd_cpu newton.tests.test_runtime_gravity.TestRuntimeGravity.test_per_world_gravity_particles_vbd_cuda_0
uv run --extra dev python -m unittest newton.tests.test_sensor_imu.TestSensorIMU.test_sensor_world_frame_sites_use_per_world_gravity
uv run --extra dev python -m unittest newton.tests.test_runtime_gravity
uv run --extra dev python -m unittest newton.tests.test_sensor_imu
uv run --extra dev -m newton.tests -k test_gravity_acceleration_kernel_uses_global_gravity
uv run --extra dev -m newton.tests -k test_global_particles_use_global_gravity
uv run --extra dev -m newton.tests -k test_single_world_global_particles_supported
uv run --extra dev -m newton.tests -k test_gravity_three_worlds_different_axes
uvx pre-commit run -a

Bug fix

Steps to reproduce:

  1. Create local worlds with different gravity values and add VBD particles or body-less IMU sites to each world.
  2. Add a particle, body, or IMU site to global world -1 with gravity distinct from world 0.
  3. Advance VBD or update the IMU sensor.
  4. Without this change, local VBD particles and body-less IMUs can incorrectly use world 0 gravity, while global entities have no dedicated gravity value.

Minimal reproduction:

import newton

builder = newton.ModelBuilder()
builder.set_gravity((0.0, 0.0, -9.81), world=-1)
model = builder.finalize()

# The final gravity entry belongs to global world -1.
assert model.gravity.shape[0] == model.world_count + 1

New feature / API change

# Update only global world -1 gravity at runtime.
model.set_gravity((0.0, -9.81, 0.0), world=-1)

Summary by CodeRabbit

  • New Features

    • Added dedicated global gravity alongside per-world gravity.
    • Gravity can be updated independently for local worlds and global entities.
    • Reset operations now support global-world masks with validation and compatibility handling.
    • Viewer gravity information displays both local and global values.
  • Bug Fixes

    • Corrected gravity application for particles, bodies, IMU sensors, coupling, and physics solvers.
    • Improved MuJoCo gravity compensation across multiple worlds.
    • Improved single-world compatibility for model conversion.
  • Documentation

    • Updated world and gravity documentation with defaults, configuration details, and examples.

pietis and others added 3 commits July 29, 2026 10:57
The VBD particle forward step indexes gravity[0], so world-local
particles in every environment use world 0's gravity.

Pass particle_world to the kernel and select the corresponding gravity,
preserving world 0 as the fallback for global particles. Add a
regression test with distinct gravity values across two worlds.

Fixes newton-physics#3692
Select gravity from shape_world for each world-frame site that is
not attached to a body. Preserve world 0 as the fallback for global
sites.
Append the builder default to Model.gravity so world -1 has an
independent gravity vector and entity world indices can be used directly.

Keep local-only runtime gravity arrays compatible, and cover global
particles, bodies, sensors, coupling, and inverse dynamics.
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0d3cc156-0800-408b-adbf-265115303ebd

📥 Commits

Reviewing files that changed from the base of the PR and between 62b56bb and ded6b63.

📒 Files selected for processing (1)
  • newton/_src/solvers/kamino/_src/core/conversions.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • newton/_src/solvers/kamino/_src/core/conversions.py

📝 Walkthrough

Walkthrough

The PR adds dedicated gravity for global world -1, updates gravity consumers and runtime APIs, standardizes reset masks with a global slot, fixes VBD and IMU world selection, and expands documentation, examples, UI, and regression coverage.

Changes

Global gravity and world-index semantics

Layer / File(s) Summary
Gravity storage and runtime contract
newton/_src/sim/builder.py, newton/_src/sim/model.py, newton/_src/sim/inverse_dynamics.py, docs/concepts/worlds.rst, newton/_src/viewer/viewer_gui.py
Model.gravity stores local gravity entries followed by a global entry. Model.set_gravity supports local and global updates while preserving legacy forms. Documentation and UI output describe the expanded layout.
Shared reset-mask normalization
newton/_src/solvers/solver.py, newton/_src/solvers/vbd/*, newton/tests/test_coupled_solver.py
Reset masks validate against the expanded world layout, normalize deprecated local-only masks, and map global selection to the final slot.
Direct world-index gravity consumers
newton/_src/solvers/*
Solver, coupling, contact, MPM, Style3D, Featherstone, and VBD kernels use assigned world indices instead of clamping them to world 0.
Sensor, MuJoCo, and adapter mappings
newton/_src/sensors/sensor_imu.py, newton/_src/solvers/mujoco/*, newton/_src/solvers/kamino/*
IMU sites, MuJoCo coupling, and Kamino conversion paths use or preserve world mappings for global and local entities.
Regression coverage and supporting integrations
newton/tests/*, newton/examples/*, CHANGELOG.md
Tests and examples cover global gravity, per-world gravity, gravity-array sizing, reset behavior, IMU readings, coupling, and compatibility behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ModelBuilder
  participant Model
  participant Solver
  participant SensorIMU
  ModelBuilder->>Model: finalize local and global gravity entries
  Model->>Solver: provide gravity and entity world mappings
  Solver->>Solver: apply gravity[world_idx]
  Model->>SensorIMU: provide gravity and shape_world
  SensorIMU->>SensorIMU: apply gravity[shape_world[site_idx]]
Loading

Possibly related PRs

Suggested reviewers: adenzler-nvidia

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The Kamino ModelView conversion changes are unrelated to the linked gravity objectives. Remove the unrelated Kamino conversion changes or link an issue that requires ModelView support.
Docstring Coverage ⚠️ Warning Docstring coverage is 71.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: dedicated gravity for global world -1.
Linked Issues check ✅ Passed The changes satisfy the gravity layout, runtime API, solver, VBD, IMU, builder, indexing, and regression-test objectives in [#3723], [#3692], and [#3715].
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
newton/_src/sim/model.py (1)

1970-1975: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the gravity units in set_gravity.

Add [m/s²] to the gravity argument description, consistent with Model.gravity, so callers know the expected SI units.

As per coding guidelines, public API docstrings under newton/**/*.py must include SI units for physical quantities.
[details]

Proposed documentation fix
-            gravity: Gravity vector with shape ``(3,)``, a local-world array with shape
+            gravity: Gravity vector [m/s²] with shape ``(3,)``, a local-world array with shape
[/details]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/_src/sim/model.py` around lines 1970 - 1975, Update the gravity
argument description in set_gravity to state that gravity is measured in SI
units of [m/s²], matching the Model.gravity documentation while preserving the
existing shape and behavior descriptions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@newton/_src/sim/model.py`:
- Around line 1970-1975: Update the gravity argument description in set_gravity
to state that gravity is measured in SI units of [m/s²], matching the
Model.gravity documentation while preserving the existing shape and behavior
descriptions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 68de413c-69e5-4b9a-a6e2-1e7a0377522d

📥 Commits

Reviewing files that changed from the base of the PR and between 1f675f2 and b809688.

📒 Files selected for processing (28)
  • CHANGELOG.md
  • docs/concepts/worlds.rst
  • newton/_src/sensors/sensor_imu.py
  • newton/_src/sim/builder.py
  • newton/_src/sim/inverse_dynamics.py
  • newton/_src/sim/model.py
  • newton/_src/solvers/coupled/interface.py
  • newton/_src/solvers/featherstone/kernels.py
  • newton/_src/solvers/implicit_mpm/implicit_mpm_solver_kernels.py
  • newton/_src/solvers/mujoco/kernels.py
  • newton/_src/solvers/solver.py
  • newton/_src/solvers/style3d/kernels.py
  • newton/_src/solvers/vbd/particle_vbd_kernels.py
  • newton/_src/solvers/vbd/rigid_vbd_kernels.py
  • newton/_src/solvers/vbd/solver_vbd.py
  • newton/_src/solvers/xpbd/kernels.py
  • newton/_src/viewer/viewer_gui.py
  • newton/examples/cloth/example_cloth_franka.py
  • newton/examples/contacts/example_contacts_rj45_plug.py
  • newton/examples/sensors/example_sensor_imu.py
  • newton/examples/softbody/example_softbody_franka.py
  • newton/tests/test_cloth.py
  • newton/tests/test_coupled_solver.py
  • newton/tests/test_implicit_mpm.py
  • newton/tests/test_inverse_dynamics.py
  • newton/tests/test_runtime_gravity.py
  • newton/tests/test_sensor_imu.py
  • newton/tests/test_solver_style3d.py

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.00000% with 6 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
newton/_src/viewer/viewer_gui.py 0.00% 5 Missing ⚠️
...olvers/implicit_mpm/implicit_mpm_solver_kernels.py 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@eric-heiden eric-heiden changed the title Add dedicated gravity for global world -1 Add global world gravity and reset masks Jul 29, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
newton/_src/solvers/coupled/solver_coupled_proxy.py (1)

116-120: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Correct fix for global-slot selection.

The rewritten _reset_world_selected correctly derives the global slot as world_mask.shape[0] - 1 and returns it for world == -1, fixing the prior bug where global selection silently returned False for masks longer than 1 element. Verified boundary cases (world_count == 0, world >= 0 in-range/out-of-range) are all handled correctly via short-circuit evaluation.

A one-line comment noting that global_world_index is the reserved trailing slot for world -1 would help future readers, since the indexing convention isn't otherwise self-evident from this snippet alone.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/_src/solvers/coupled/solver_coupled_proxy.py` around lines 116 - 120,
Add a concise comment in _reset_world_selected explaining that
global_world_index is the reserved trailing slot corresponding to world -1.
Leave the existing selection logic unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@newton/tests/test_coupled_solver.py`:
- Around line 2444-2478: Add a local proxy mapping alongside the existing
global-only mapping in the coupled reset test, initialize its history arrays
with distinct sentinel values, and assert those values remain unchanged after
reset with only the final global-mask entry selected. Keep the existing global
mapping assertions to verify only targeted global entities are cleared.

---

Nitpick comments:
In `@newton/_src/solvers/coupled/solver_coupled_proxy.py`:
- Around line 116-120: Add a concise comment in _reset_world_selected explaining
that global_world_index is the reserved trailing slot corresponding to world -1.
Leave the existing selection logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: cc743d55-52ff-4ebc-ae4d-dd5359df7402

📥 Commits

Reviewing files that changed from the base of the PR and between b809688 and aefe5b0.

📒 Files selected for processing (16)
  • CHANGELOG.md
  • docs/concepts/worlds.rst
  • newton/_src/sim/model.py
  • newton/_src/solvers/coupled/solver_coupled.py
  • newton/_src/solvers/coupled/solver_coupled_proxy.py
  • newton/_src/solvers/kamino/solver_kamino.py
  • newton/_src/solvers/kamino/tests/test_solvers_dvi.py
  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/_src/solvers/solver.py
  • newton/_src/solvers/vbd/rigid_vbd_kernels.py
  • newton/_src/solvers/vbd/solver_vbd.py
  • newton/tests/test_coupled_solver.py
  • newton/tests/test_custom_solver.py
  • newton/tests/test_mujoco_reset.py
  • newton/tests/test_solver_kamino_dvi.py
  • newton/tests/test_solver_vbd.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • newton/_src/sim/model.py

Comment thread newton/tests/test_coupled_solver.py Outdated
@eric-heiden
eric-heiden force-pushed the eric-heiden/global-world-gravity branch from aefe5b0 to 04e0d20 Compare July 29, 2026 19:38
@eric-heiden eric-heiden changed the title Add global world gravity and reset masks Add dedicated gravity for global world -1 Jul 29, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jul 29, 2026
3 tasks
@eric-heiden
eric-heiden marked this pull request as ready for review July 29, 2026 22:18
@eric-heiden
eric-heiden requested a review from a team as a code owner July 29, 2026 22:18
@eric-heiden eric-heiden added this to the 1.5 Release milestone Jul 29, 2026
@eric-heiden eric-heiden self-assigned this Jul 29, 2026
@eric-heiden
eric-heiden requested a review from vreutskyy July 29, 2026 22:18

@vreutskyy vreutskyy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three findings from local reproductions where the dedicated global gravity behavior does not carry through existing update paths or solver backends.

Comment thread newton/_src/sim/model.py
full_shape = (self.world_count + 1, 3)
if gravity_np.shape == local_shape:
current = self.gravity.numpy()
current[: self.world_count] = gravity_np

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Legacy gravity updates no longer affect global-only entities

ModelBuilder.finalize() reports world_count == 1 even when all entities belong to world -1. Consequently, set_gravity(..., world=0) and (world_count, 3) updates now modify gravity[0], while those entities read gravity[-1]. In a SemiImplicit reproduction, both calls succeeded but the particle continued accelerating at -9.81. The new Model.gravity shape also makes existing direct assignments with (world_count, 3) arrays incompatible.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Implicit single-world models keep the legacy one-entry Model.gravity, so world 0 updates, local arrays, and direct assignments still affect global-only entities.

if self.world_gravity:
# Use per-world gravity from world_gravity list
gravity_vecs = self.world_gravity
gravity_vecs = [*self.world_gravity, global_gravity]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Kamino maps global entities to world 0

ModelKamino.from_newton() rewrites every -1 world index to 0 whenever world_count == 1, including models containing both global and world-0 entities. With global gravity -2 and world-0 gravity -5, constructing Kamino changed body_world from [-1, 0] to [0, 0]; one step produced Z velocities [-0.5, -0.5] instead of [-0.2, -0.5].

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Kamino now uses a conversion-only world mapping and leaves model.body_world unchanged. Global bodies keep world -1 and use global gravity.

Comment thread newton/_src/solvers/mujoco/kernels.py Outdated

g = wp.vec3(0.0, 0.0, 0.0)
if world >= 0 and world < gravity.shape[0]:
if world >= -1 and world < gravity.shape[0]:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] MuJoCo uses world-0 gravity for global bodies

MuJoCo continues to initialize its gravity from model.gravity[0] (solver_mujoco.py:4998). This coupling kernel also reads body_world only when more than one MuJoCo world exists. With global gravity -2 and world-0 gravity -5, both bodies received Z velocity -0.5 after one step.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. MuJoCo now corrects gravity per body when global and world-0 bodies share one MuJoCo world. The coupling hook also reports each body's gravity.

Preserve legacy gravity storage for implicit single-world models and avoid
mutating Newton world assignments during Kamino conversion.

Apply per-body gravity corrections for mixed global/local MuJoCo models and
cover the update paths across solvers.
@eric-heiden
eric-heiden requested a review from a team as a code owner August 3, 2026 08:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
newton/_src/solvers/mujoco/kernels.py (1)

241-266: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a brief comment explaining the mujoco_world selection logic.

The branch that keeps mujoco_world = 0 unless the model uses separate MuJoCo worlds is not obvious. apply_mjc_body_f_kernel, right below, documents an equivalent invariant in its docstring. Add a short comment here that explains why body_gravcomp.shape[0] > 1 selects the body's own world, and why it stays 0 otherwise (single shared MuJoCo world, where global and local bodies coexist).

As per coding guidelines: "Keep comments brief and explain why non-obvious code exists rather than restating what the code does."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@newton/_src/solvers/mujoco/kernels.py` around lines 241 - 266, Add a brief
rationale comment immediately above the mujoco_world selection branch in
eval_mujoco_coupling_gravity_acceleration_kernel: when body_gravcomp.shape[0] >
1, use gravity_world for separate MuJoCo worlds; otherwise retain world 0
because global and local bodies share one MuJoCo world. Keep the comment concise
and explain the invariant rather than restating the assignments.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@newton/_src/solvers/kamino/_src/core/model.py`:
- Around line 741-749: Update the model parameter type hints on convert_joints
and convert_geometries in conversions.py from Model to Model | ModelView,
matching convert_rigid_bodies and the ModelView value passed by this call site.
Preserve their existing behavior and imports, adding the ModelView type
reference if needed.

---

Nitpick comments:
In `@newton/_src/solvers/mujoco/kernels.py`:
- Around line 241-266: Add a brief rationale comment immediately above the
mujoco_world selection branch in
eval_mujoco_coupling_gravity_acceleration_kernel: when body_gravcomp.shape[0] >
1, use gravity_world for separate MuJoCo worlds; otherwise retain world 0
because global and local bodies share one MuJoCo world. Keep the comment concise
and explain the invariant rather than restating the assignments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 40e6b54c-0f78-4a8c-8d2a-f9c1d036bfaf

📥 Commits

Reviewing files that changed from the base of the PR and between 04e0d20 and 62b56bb.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • docs/concepts/worlds.rst
  • newton/_src/sim/builder.py
  • newton/_src/sim/model.py
  • newton/_src/solvers/kamino/_src/core/conversions.py
  • newton/_src/solvers/kamino/_src/core/model.py
  • newton/_src/solvers/mujoco/kernels.py
  • newton/_src/solvers/mujoco/solver_mujoco.py
  • newton/tests/test_runtime_gravity.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • newton/_src/sim/model.py
  • CHANGELOG.md
  • newton/_src/sim/builder.py

Comment thread newton/_src/solvers/kamino/_src/core/model.py
Allow joint and geometry conversion helpers to accept ModelView, matching their call sites and the rigid body conversion helper.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

3 participants