Skip to content

Update selection examples to new Viewer#614

Merged
eric-heiden merged 13 commits into
newton-physics:mainfrom
mmacklin:update-selection-examples
Aug 22, 2025
Merged

Update selection examples to new Viewer#614
eric-heiden merged 13 commits into
newton-physics:mainfrom
mmacklin:update-selection-examples

Conversation

@mmacklin

@mmacklin mmacklin commented Aug 22, 2025

Copy link
Copy Markdown
Member

Description

  • Update selection examples to use new viewer and guidelines.
  • Fixes an unnecessary GPU/CPU sync. point with the picking code and improves some GL interop perf.

Newton Migration Guide

Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this MR.

  • The migration guide in docs/migration.rst is up-to date

Before your PR is "Ready for review"

  • All commits are signed-off to indicate that your contribution adheres to the Developer Certificate of Origin requirements
  • Necessary tests have been added and new examples are tested (see newton/tests/test_examples.py)
  • Documentation is up-to-date
  • Code passes formatting and linting checks with pre-commit run -a

Summary by CodeRabbit

  • New Features

    • Three selection examples (Cartpole, Materials, Articulations) added to the CLI and now run via a viewer-driven runtime.
    • Optional CUDA-graph capture support added for selection examples.
  • Improvements

    • Viewer-driven multi-environment replication for simpler multi-instance workflows.
    • More reliable picking state via an explicit active flag.
    • Faster instance rendering via dynamic transform uploads.
  • Documentation

    • README updated with a Selection Examples gallery and run commands.
  • Chores

    • Tests updated to run in viewer mode; kernel-cache rebuild disabled.

Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
@mmacklin mmacklin requested a review from nvlukasz August 22, 2025 03:02
@coderabbitai

coderabbitai Bot commented Aug 22, 2025

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Refactors three selection examples to a viewer-based runtime with CUDA graph capture, updates tests and CLI example registry accordingly, adds a picking_active flag to picking state, and changes OpenGL mesh instancing buffers to use dynamic draw with CUDA‑GL WRITE_DISCARD interop.

Changes

Cohort / File(s) Summary
OpenGL viewer buffers
newton/_src/viewer/gl/opengl.py
Change per-instance transform VBO from GL_STATIC_DRAW to GL_DYNAMIC_DRAW; register CUDA‑GL interop buffer with WRITE_DISCARD; update host upload path to use dynamic draw.
Picking state management
newton/_src/viewer/picking.py
Add picking_active attribute; is_picking() returns this flag; pick() sets picking_active based on pick_body; release() resets pick state and picking_active.
Examples registry
newton/examples/__init__.py
Register three selection examples: selection_articulations, selection_cartpole, selection_materialsnewton.examples.selection.*.
Viewer-based selection examples
newton/examples/selection/example_selection_articulations.py, newton/examples/selection/example_selection_cartpole.py, newton/examples/selection/example_selection_materials.py
Migrate examples to viewer-driven API Example(viewer, num_envs); build one env via ModelBuilder, replicate with scene.replicate, finalize a single model; attach model to viewer; render via viewer (begin_frame/log_state/end_frame); add capture() for CUDA graph capture and use capture launch when available; remove ScopedDevice; add time-stepping fields and viewer-based force application; minor kernel param rename in materials example.
Viewer base API
newton/_src/viewer/viewer.py
Remove @abstractmethod decorator from ViewerBase.apply_forces (method now concrete with same signature and empty body).
Tests update for viewer flow
newton/tests/test_examples.py
Switch three selection tests to viewer execution (use_viewer=True), drop stage_path from test options, shorten CPU test frames; disable explicit kernel-cache rebuild in __main__.
Docs / examples index
README.md
Add "Selection Examples" gallery section with three example thumbnails and run commands.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as newton.examples CLI
  participant Init as examples.init()
  participant Viewer as Viewer
  participant Ex as Example(viewer,num_envs)
  participant Model as ModelBuilder/Scene
  participant Solver as Solver
  participant Capt as CUDA Capture

  User->>CLI: run selection_* example
  CLI->>Init: create_parser() / parse args
  Init-->>CLI: viewer, args
  CLI->>Ex: construct Example(viewer, num_envs)
  Ex->>Model: build env via ModelBuilder
  Ex->>Model: scene.replicate(num_envs)
  Ex->>Viewer: set_model(model)
  Ex->>Capt: capture() (optional)
  Capt-->>Ex: graph (optional)

  loop each frame
    CLI->>Viewer: begin_frame()
    CLI->>Ex: step()
    alt graph available
      Ex->>Capt: capture_launch(graph)
    else
      Ex->>Viewer: apply_forces(state_0)
      Ex->>Solver: evaluate FK / contacts (if needed)
      Ex->>Solver: step(state_0 -> state_1)
    end
    Ex->>Viewer: log_state(state_1)
    CLI->>Viewer: end_frame()
  end
Loading
sequenceDiagram
  autonumber
  participant Input as Mouse/HitTest
  participant Pick as Picking

  Input->>Pick: pick(event)
  alt valid pick_body (>=0)
    Pick->>Pick: set pick_body >= 0\npicking_active = True
  else
    Pick->>Pick: set pick_body = -1\npicking_active = False
  end
  Input->>Pick: release()
  Pick->>Pick: reset pick state\npicking_active = False
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Viewer refactor #522: Modifies OpenGL viewer and picking subsystems; likely overlaps with opengl.py and picking.py edits.
  • Example cleanup #580: Touches OpenGL viewer implementation and CUDA‑GL interop; relevant to VBO/interop changes here.

Suggested reviewers

  • shi-eric

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

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

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@mmacklin mmacklin requested a review from eric-heiden August 22, 2025 03:03

@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: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
newton/examples/selection/example_selection_articulations.py (1)

229-236: Random forces kernel uses the wrong stride in RNG seeding.

dof_forces has shape (num_envs, dof_count). The kernel seeds with i*num_envs + j, which collides across rows. Use dof_count for the stride and pass it as a kernel argument.

Apply this refactor:

-@wp.kernel
-def random_forces_kernel(dof_forces: wp.array2d(dtype=float), seed: int, num_envs: int):
+@wp.kernel
+def random_forces_kernel(dof_forces: wp.array2d(dtype=float), seed: int, dof_count: int):
     i, j = wp.tid()
-    rng = wp.rand_init(seed, i * num_envs + j)
+    rng = wp.rand_init(seed, i * dof_count + j)
     dof_forces[i, j] = 5.0 - 10.0 * wp.randf(rng)
@@
-    dof_forces = self.ants.get_dof_forces(self.control)
-    wp.launch(random_forces_kernel, dim=dof_forces.shape, inputs=[dof_forces, self.step_count, self.num_envs])
+    dof_forces = self.ants.get_dof_forces(self.control)
+    dof_count = dof_forces.shape[1]
+    wp.launch(random_forces_kernel, dim=dof_forces.shape, inputs=[dof_forces, self.step_count, dof_count])
🧹 Nitpick comments (8)
newton/_src/viewer/picking.py (2)

82-84: Double‑check capture invariance of update().

update() early‑returns when not picking. If update() is part of a captured CUDA graph in any viewer path, this conditional could break capture determinism. If applicable, consider always launching update_pick_target_kernel with a no‑op ray (e.g., zero dir) and gating in the kernel. Otherwise, ignore.


162-163: Host sync to read pick_body is acceptable given pick() frequency.

Reading pick_body back to host once per pick is fine. If pick() is called per-mouse‑move and profiling shows stalls on CUDA, consider keeping a small host mirror updated via a trivial 1‑element memcpy kernel or using wp.copy to a CPU scalar. Not required unless measured.

newton/_src/viewer/gl/opengl.py (1)

731-732: Consider buffer orphaning or SubData for CPU path to reduce reallocations.

glBufferData with DYNAMIC_DRAW each frame can cause reallocations. Two lighter options:

  • Orphaning: glBufferData(GL_ARRAY_BUFFER, size, None, GL_DYNAMIC_DRAW) followed by glBufferSubData with data.
  • Or persistent mapping with glMapBufferRange if available.

Optional unless profiling shows overhead.

newton/examples/selection/example_selection_articulations.py (1)

291-297: Avoid double parsing of CLI args.

You parse args before and after init(). Keep the init() path only to avoid divergence.

Apply:

-args = parser.parse_known_args()[0]
-
-viewer, args = newton.examples.init(parser)
+viewer, args = newton.examples.init(parser)
newton/examples/selection/example_selection_materials.py (1)

262-273: Avoid double parsing of CLI args.

Same as articulations: remove the pre-init parse to rely on init() as the single source of truth.

-args = parser.parse_known_args()[0]
-
-viewer, args = newton.examples.init(parser)
+viewer, args = newton.examples.init(parser)
newton/examples/selection/example_selection_cartpole.py (3)

118-125: Unnecessary/mis-scoped FK evaluation; remove or use state arrays

The comment says this is for non‑MuJoCo solvers, but the code always calls newton.eval_fk using model.joint_q/joint_qd while you’ve randomized state_0’s joint_q. This is redundant (you already eval FK via ArticulationView for the non‑MuJoCo case) and potentially inconsistent with the state.

Two options (prefer removing to avoid duplication):

Apply this diff to remove the block:

-        # Ensure FK evaluation (for non-MuJoCo solvers):
-        newton.eval_fk(
-            self.model,
-            self.model.joint_q,
-            self.model.joint_qd,
-            self.state_0,
-        )

If you keep it, at least use the state arrays and guard by solver type:

+        if not isinstance(self.solver, newton.solvers.SolverMuJoCo):
+            newton.eval_fk(self.model, self.state_0.joint_q, self.state_0.joint_qd, self.state_0)

196-201: Torch device setup is likely incorrect API

torch.set_device(...) isn’t a public PyTorch API. Use torch.cuda.set_device for CUDA and torch.set_default_device for CPU (if desired), guarding for availability.

Apply this diff:

-    if USE_TORCH:
-        import torch
-
-        torch.set_device(args.device)
+    if USE_TORCH:
+        import torch
+        # Set device only when using CUDA; otherwise default CPU is fine
+        if args.device and isinstance(args.device, str) and args.device.startswith("cuda"):
+            idx = int(args.device.split(":")[1]) if ":" in args.device else 0
+            torch.cuda.set_device(idx)
+        elif hasattr(torch, "set_default_device"):
+            # Optional: explicitly pin to CPU if needed
+            try:
+                torch.set_default_device("cpu")
+            except Exception:
+                pass

16-26: Update usage hint to reflect viewer-based CLI (nit)

The “Command” line doesn’t reflect that this example expects a viewer argument now. Consider showing a concrete invocation that works with both GL and USD:

  • python -m newton.examples selection_cartpole --viewer gl
  • python -m newton.examples selection_cartpole --viewer usd --output-path out.usd
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5e55801 and 6947f64.

📒 Files selected for processing (7)
  • newton/_src/viewer/gl/opengl.py (3 hunks)
  • newton/_src/viewer/picking.py (3 hunks)
  • newton/examples/__init__.py (1 hunks)
  • newton/examples/selection/example_selection_articulations.py (5 hunks)
  • newton/examples/selection/example_selection_cartpole.py (4 hunks)
  • newton/examples/selection/example_selection_materials.py (7 hunks)
  • newton/tests/test_examples.py (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-12T17:51:37.474Z
Learnt from: nvlukasz
PR: newton-physics/newton#519
File: newton/geometry.py:16-22
Timestamp: 2025-08-12T17:51:37.474Z
Learning: In the Newton public API refactor, common geometry symbols like ParticleFlags and ShapeFlags are now exposed at the top level in newton/__init__.py rather than through newton.geometry. The newton/geometry.py module is intentionally focused only on broad-phase collision detection classes (BroadPhaseAllPairs, BroadPhaseExplicit, BroadPhaseSAP).

Applied to files:

  • newton/examples/__init__.py
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
PR: newton-physics/newton#579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, there's a distinction between solver parameters and Example class attributes. The Example class can have its own use_mujoco attribute for controlling example-level behavior (like CUDA graphs, rendering logic), while the solver uses use_mujoco_cpu for backend selection. These serve different purposes and should not be conflated during API renames.

Applied to files:

  • newton/examples/selection/example_selection_materials.py
  • newton/examples/selection/example_selection_cartpole.py
🧬 Code graph analysis (3)
newton/examples/selection/example_selection_materials.py (5)
newton/_src/sim/builder.py (3)
  • ModelBuilder (67-4099)
  • collapse_fixed_joints (1739-2036)
  • replicate (574-580)
newton/_src/utils/import_mjcf.py (1)
  • parse_mjcf (32-931)
newton/examples/__init__.py (3)
  • create_parser (97-123)
  • init (126-167)
  • run (36-44)
newton/_src/sim/model.py (3)
  • state (441-474)
  • control (476-508)
  • collide (510-564)
newton/examples/selection/example_selection_articulations.py (4)
  • reset (247-282)
  • capture (200-205)
  • step (220-245)
  • simulate (207-218)
newton/examples/selection/example_selection_cartpole.py (6)
newton/_src/sim/builder.py (3)
  • ModelBuilder (67-4099)
  • collapse_fixed_joints (1739-2036)
  • replicate (574-580)
newton/_src/utils/import_usd.py (1)
  • parse_usd (34-1297)
newton/examples/__init__.py (3)
  • create_parser (97-123)
  • init (126-167)
  • run (36-44)
newton/examples/selection/example_selection_articulations.py (3)
  • capture (200-205)
  • simulate (207-218)
  • render (284-287)
newton/examples/selection/example_selection_materials.py (4)
  • capture (162-167)
  • simulate (169-183)
  • render (252-255)
  • test (257-258)
newton/_src/sim/model.py (1)
  • control (476-508)
newton/examples/selection/example_selection_articulations.py (2)
newton/_src/sim/builder.py (2)
  • ModelBuilder (67-4099)
  • replicate (574-580)
newton/examples/__init__.py (3)
  • create_parser (97-123)
  • init (126-167)
  • run (36-44)
🔇 Additional comments (16)
newton/_src/viewer/picking.py (2)

56-57: Explicit picking_active flag is a clean separation from pick_body.

Good addition. It avoids device reads in hot paths that only need a boolean and makes the intent clearer.


85-88: Release path resets both device and host picking state.

Setting pick_body = -1 and picking_active = False keeps CPU/GPU state consistent. Looks good.

newton/_src/viewer/gl/opengl.py (1)

630-631: Use of GL_DYNAMIC_DRAW for per‑instance transforms is appropriate.

This matches the per‑frame update pattern for instance transforms and avoids STATIC allocation thrash.

newton/examples/selection/example_selection_articulations.py (4)

86-96: Viewer-based constructor and timing fields look good.

Clear separation of sim_dt vs frame_dt and support for multi-env is consistent with other examples.


97-104: Scene replication pattern is correct; minor verification on ignore_names.

Building one env then replicating via scene.replicate is the right approach. Verify that "floor"/"ground" filtering in both MJCFs is sufficient to avoid duplicate planes across envs.

Also applies to: 105-111, 113-117


192-193: Explicit viewer.set_model(model) is necessary here.

Good to see this wired up to enable viewer-driven logging and picking.


200-206: CUDA graph capture is scoped correctly.

Capturing simulate() once and replaying in step() is the right balance. Ensure no host-side allocations happen inside simulate() to keep capture reusable.

newton/examples/selection/example_selection_materials.py (3)

71-81: Viewer-based constructor and sim timing look consistent.

Matches the articulations example; good for test harness uniformity.


151-152: viewer.set_model(model) is correctly placed.

Critical for picking/forces to bind to the active model.


153-155: FK precompute for non-MuJoCo solvers is a good safety.

This avoids stale transforms when switching solvers.

newton/examples/__init__.py (1)

186-189: Selection examples importability failed locally

We attempted to run the provided import-verification script but encountered a ModuleNotFoundError for the warp dependency when loading the new example modules:

ModuleNotFoundError: No module named 'warp'

Please ensure that:

  • The warp package is declared and installed in your CI environments.
  • All three modules under newton.examples.selection import cleanly once warp is available.

Once warp is installed, rerun:

python - <<'PY'
import importlib
for m in [
  "newton.examples.selection.example_selection_articulations",
  "newton.examples.selection.example_selection_cartpole",
  "newton.examples.selection.example_selection_materials",
]:
    importlib.import_module(m)
    print("OK:", m)
PY

to confirm that each prints “OK: ”.

newton/tests/test_examples.py (3)

333-336: Selection articulations: migrated test wiring to viewer path looks good

  • use_viewer=True correctly routes through the new viewer initialization in newton.examples.init().
  • Explicit num_frames keeps runtime bounded; CPU override at Line 334 is reasonable for CI speed.

341-344: Selection cartpole: viewer-based execution enabled

Switching this example to use_viewer=True aligns it with the refactored viewer-driven runtime and matches the example’s new CLI. No issues spotted.


349-352: Selection materials: consistent test options and viewer flow

Consistent with the other selection tests; the viewer mode integration and frame budgeting look appropriate.

newton/examples/selection/example_selection_cartpole.py (2)

128-134: CUDA graph capture includes viewer.apply_forces; confirm it remains interactive

simulate() calls viewer.apply_forces(self.state_0), and capture() records simulate() inside a CUDA graph. If viewer.apply_forces depends on per-frame host state (e.g., picking), ensure that the data it writes is fed via device buffers updated between launches so that interaction isn’t “frozen” at capture time.

Would you like me to add a minimal toggle to re-capture on interaction state changes, or document why capture is safe here?


139-141: Nice: forces routed through viewer for picking/wind

Integrating viewer.apply_forces keeps interaction logic consolidated and makes the example consistent with the other selection demos.

Comment thread newton/_src/viewer/gl/opengl.py
Comment thread newton/examples/selection/example_selection_articulations.py
Comment thread newton/examples/selection/example_selection_cartpole.py
Comment thread newton/examples/selection/example_selection_materials.py
Comment thread newton/examples/selection/example_selection_materials.py
Comment thread newton/examples/selection/example_selection_materials.py

@nvlukasz nvlukasz 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.

Looks good! The new viewer and example utilities make the code simpler and cleaner. Made a few minor comments, but none of them are blockers.

help="Path to the output USD file.",
)
parser.add_argument("--num-frames", type=int, default=1200, help="Total number of frames.")
parser = newton.examples.create_parser()

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.

Minor nitpick, but at first glance I wasn't sure what kind of parser this is creating. Maybe we can be more explicit, e.g., create_argument_parser() or just argument_parser()?

example = Example(stage_path=args.stage_path, num_envs=args.num_envs, use_cuda_graph=args.use_cuda_graph)
viewer, args = newton.examples.init(parser)

if USE_TORCH:

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.

Should we get rid of this now to keep these examples simpler? We could create one or two Torch examples (e.g., example_selection_torch.py), but the rest could be pure Warp.

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.

I could also do it at a later time.

eric-heiden and others added 5 commits August 22, 2025 10:48
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>

@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

♻️ Duplicate comments (1)
newton/examples/selection/example_selection_cartpole.py (1)

158-162: Fix: kernel launch dimension must be 1D integer (not a tuple).

apply_forces_kernel uses a 1D thread id (wp.tid()), but the launch passes dim=joint_f.shape (a tuple). This will compile/launch incorrectly. Use the batch size only.

Apply this diff:

-            wp.launch(
-                apply_forces_kernel,
-                dim=joint_f.shape,
-                inputs=[joint_q, joint_f],
-            )
+            wp.launch(
+                apply_forces_kernel,
+                dim=joint_f.shape[0],
+                inputs=[joint_q, joint_f],
+            )
🧹 Nitpick comments (4)
newton/examples/selection/example_selection_cartpole.py (4)

118-125: Redundant FK evaluation; remove or guard to non-MuJoCo solvers.

You already call self.cartpoles.eval_fk(self.state_0) for non-MuJoCo solvers. The unconditional newton.eval_fk block duplicates work and is unnecessary for MuJoCo (and double-computes for other solvers).

Apply one of these:

Option A — remove redundant block:

-        # Ensure FK evaluation (for non-MuJoCo solvers):
-        newton.eval_fk(
-            self.model,
-            self.model.joint_q,
-            self.model.joint_qd,
-            self.state_0,
-        )

Option B — guard consistently:

-        # Ensure FK evaluation (for non-MuJoCo solvers):
-        newton.eval_fk(
-            self.model,
-            self.model.joint_q,
-            self.model.joint_qd,
-            self.state_0,
-        )
+        if not isinstance(self.solver, newton.solvers.SolverMuJoCo):
+            newton.eval_fk(self.model, self.model.joint_q, self.model.joint_qd, self.state_0)

192-192: Remove dead code: duplicate args parsing.

args is parsed here and immediately overwritten by init(parser). Safe to delete this line.

-    args = parser.parse_known_args()[0]

103-111: Consistency: prefer self.num_envs over local num_envs.

Keeps initialization consistent and resilient to signature refactors.

-            cart_positions = 2.0 - 4.0 * torch.rand(num_envs)
-            pole1_angles = torch.pi / 8.0 - torch.pi / 4.0 * torch.rand(num_envs)
-            pole2_angles = torch.pi / 8.0 - torch.pi / 4.0 * torch.rand(num_envs)
+            cart_positions = 2.0 - 4.0 * torch.rand(self.num_envs)
+            pole1_angles = torch.pi / 8.0 - torch.pi / 4.0 * torch.rand(self.num_envs)
+            pole2_angles = torch.pi / 8.0 - torch.pi / 4.0 * torch.rand(self.num_envs)
             joint_q = torch.stack([cart_positions, pole1_angles, pole2_angles], dim=1)
 ...
-            wp.launch(randomize_states_kernel, dim=num_envs, inputs=[joint_q, 42])
+            wp.launch(randomize_states_kernel, dim=self.num_envs, inputs=[joint_q, 42])

60-66: Optional: honor USD fps if available.

parse_usd returns metadata including fps. Consider reading it to set self.fps/frame_dt so the sim time aligns with the asset’s timing.

Example (conceptual):

ret = newton.utils.parse_usd(...)

fps = ret.get("fps")
if fps:
    self.fps = fps
    self.frame_dt = 1.0 / self.fps
    self.sim_dt = self.frame_dt / self.sim_substeps

Also applies to: 69-83

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6947f64 and f73077d.

📒 Files selected for processing (2)
  • newton/examples/selection/example_selection_cartpole.py (4 hunks)
  • newton/tests/test_examples.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • newton/tests/test_examples.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
PR: newton-physics/newton#579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, there's a distinction between solver parameters and Example class attributes. The Example class can have its own use_mujoco attribute for controlling example-level behavior (like CUDA graphs, rendering logic), while the solver uses use_mujoco_cpu for backend selection. These serve different purposes and should not be conflated during API renames.

Applied to files:

  • newton/examples/selection/example_selection_cartpole.py
🧬 Code graph analysis (1)
newton/examples/selection/example_selection_cartpole.py (5)
newton/_src/sim/builder.py (3)
  • ModelBuilder (67-4099)
  • collapse_fixed_joints (1739-2036)
  • replicate (574-580)
newton/_src/utils/import_usd.py (1)
  • parse_usd (34-1297)
newton/examples/__init__.py (3)
  • create_parser (97-123)
  • init (126-167)
  • run (36-44)
newton/examples/selection/example_selection_articulations.py (3)
  • capture (200-205)
  • simulate (207-218)
  • render (284-287)
newton/examples/selection/example_selection_materials.py (3)
  • capture (162-167)
  • simulate (169-183)
  • render (252-255)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run GPU Benchmarks (Pull Request)
  • GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
  • GitHub Check: run-newton-tests / newton-unittests (windows-latest)
  • GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
🔇 Additional comments (5)
newton/examples/selection/example_selection_cartpole.py (5)

116-117: Viewer migration looks correct.

set_model + the render path (begin_frame/log_state/end_frame) follow the new viewer guidelines and are consistent with other updated examples.


78-83: Good use of builder.replicate for multi-env instancing.

Replicating a single env builder into a scene builder keeps USD parsing simple and aligns with the new replication API.


84-85: Solver choice is explicit and not conflated with example-level flags.

Using SolverMuJoCo(disable_contacts=True) directly aligns with the “solver param vs example attribute” distinction from prior learnings. No issues.


126-134: Verify CUDA graph capture safety with dynamic viewer forces.

You capture simulate(), which calls viewer.apply_forces(self.state_0) each substep. Ensure this function uses GPU-side writes or mapped buffers that are compatible with CUDA graph replay (e.g., GL interop with WRITE_DISCARD and no implicit host sync), otherwise graph replay may bake in stale inputs or incur sync.

Would you confirm that viewer.apply_forces avoids CPU-GPU sync and is implemented with CUDA-GL interop suitable for capture (matching the changes mentioned in this PR)?

Also applies to: 139-141, 166-171


23-23: Update “Command” doc string to the new runner.

The usage hint may be outdated. If the new entrypoint is via the example runner registry, it’s likely either:

  • python -m newton.examples selection.example_selection_cartpole
    or
  • python -m newton.examples selection_cartpole

Please update to the correct, supported form.

Comment thread newton/examples/selection/example_selection_cartpole.py

@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: 2

♻️ Duplicate comments (1)
newton/examples/selection/example_selection_cartpole.py (1)

158-162: Kernel launch dimension is incorrect (tuple passed to 1D kernel)

apply_forces_kernel uses a 1D thread id (wp.tid()), but the launch passes dim=joint_f.shape (a tuple). Launch with the batch size only.

Apply this diff:

-            wp.launch(
-                apply_forces_kernel,
-                dim=joint_f.shape,
-                inputs=[joint_q, joint_f],
-            )
+            wp.launch(
+                apply_forces_kernel,
+                dim=joint_f.shape[0],
+                inputs=[joint_q, joint_f],
+            )
🧹 Nitpick comments (3)
newton/examples/selection/example_selection_cartpole.py (3)

118-124: Unconditional FK evaluation contradicts the comment and is redundant

The comment says “for non-MuJoCo solvers,” but the code runs unconditionally and duplicates the earlier FK update via ArticulationView for that branch. Either guard this block with the same condition or remove it to avoid extra work.

Apply this diff to remove the redundant call (preferred, since the initial FK is already handled above for non-MuJoCo):

-        # Ensure FK evaluation (for non-MuJoCo solvers):
-        newton.eval_fk(
-            self.model,
-            self.model.joint_q,
-            self.model.joint_qd,
-            self.state_0,
-        )

If you want full-model FK instead of the articulation-only call, keep this block but move it under the existing if not isinstance(self.solver, newton.solvers.SolverMuJoCo): and drop the earlier self.cartpoles.eval_fk(...).


192-192: Redundant parse — args are parsed again in init(parser)

This parse result is immediately overwritten by the subsequent init(parser) call. It’s safe to drop to avoid confusion.

-    args = parser.parse_known_args()[0]
+    # args are parsed within newton.examples.init(parser)

79-79: Spacing between replicated envs might be tight for longer poles

Minor: with spacing=(2.0, 0.0, 0.0), neighboring carts could overlap visuals if assets change. Not a blocker, just a heads-up.

If you anticipate longer links, consider a slightly larger x-spacing (e.g., 2.5–3.0).

📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6947f64 and f73077d.

📒 Files selected for processing (2)
  • newton/examples/selection/example_selection_cartpole.py (4 hunks)
  • newton/tests/test_examples.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • newton/tests/test_examples.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
PR: newton-physics/newton#579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, there's a distinction between solver parameters and Example class attributes. The Example class can have its own use_mujoco attribute for controlling example-level behavior (like CUDA graphs, rendering logic), while the solver uses use_mujoco_cpu for backend selection. These serve different purposes and should not be conflated during API renames.

Applied to files:

  • newton/examples/selection/example_selection_cartpole.py
🧬 Code graph analysis (1)
newton/examples/selection/example_selection_cartpole.py (6)
newton/_src/sim/builder.py (3)
  • ModelBuilder (67-4099)
  • collapse_fixed_joints (1739-2036)
  • replicate (574-580)
newton/_src/utils/import_usd.py (1)
  • parse_usd (34-1297)
newton/examples/__init__.py (3)
  • create_parser (97-123)
  • init (126-167)
  • run (36-44)
newton/examples/selection/example_selection_articulations.py (3)
  • capture (200-205)
  • simulate (207-218)
  • render (284-287)
newton/examples/selection/example_selection_materials.py (4)
  • capture (162-167)
  • simulate (169-183)
  • render (252-255)
  • test (257-258)
newton/_src/sim/model.py (1)
  • control (476-508)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run GPU Benchmarks (Pull Request)
  • GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
  • GitHub Check: run-newton-tests / newton-unittests (windows-latest)
  • GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
🔇 Additional comments (6)
newton/examples/selection/example_selection_cartpole.py (6)

16-26: Clear example header and CLI guidance — looks good

The header block is concise and the command matches the new viewer-based entrypoint pattern.


69-83: Viewer-driven pipeline and scene replication are correctly applied

  • Using a per-env builder (env) and replicating into scene before finalize() follows the new guidelines.
  • Assigning the viewer via constructor injection and calling viewer.set_model(self.model) at init time is the right ordering for allocating renderer/interop resources.

Also applies to: 86-88, 116-116


128-134: CUDA graph capture placement is fine; double-check capture safety of viewer.apply_forces

Capturing simulate() once in init mirrors the other selection examples. Assuming viewer.apply_forces is purely GPU-side (no implicit host sync), the graph should be safe to replay each frame with updated inputs.

Would you like me to run a quick repo scan to confirm viewer.apply_forces launches kernels only and doesn’t issue host-side syncs?


139-141: Using viewer.apply_forces removes CPU/GPU sync in picking path

Good integration point; this aligns with the PR goal to avoid unnecessary synchronization while keeping per-frame forces on-GPU.


167-171: Graph replay fallback is correct

Cleanly falls back to simulate() when graphs aren’t available. Matches patterns in the sibling examples.


58-66: Time-step configuration is clear and conventional

fps/frame_dt with sim_substeps/sim_dt is straightforward and consistent with other examples.

Comment thread newton/examples/selection/example_selection_cartpole.py
Comment thread newton/examples/selection/example_selection_cartpole.py
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
@codecov

codecov Bot commented Aug 22, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% 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/picking.py 0.00% 4 Missing ⚠️
newton/_src/viewer/gl/opengl.py 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>

@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: 0

♻️ Duplicate comments (3)
newton/examples/selection/example_selection_materials.py (3)

60-67: RNG stride fix (shape_count) is correct and resolves prior collision issue.

Switching the kernel arg from num_envs to shape_count and using i*shape_count + j prevents per-shape RNG collisions within an environment. Call site passes self.ants.shape_count accordingly.

Also applies to: 224-228


173-175: Ensure apply_forces is implemented (or a no-op) for all viewers.

Given apply_forces() is called unconditionally, confirm the base viewer provides a concrete no-op so non-GL viewers don’t error.

#!/bin/bash
set -euo pipefail
echo "✓ Confirm apply_forces exists on viewer base/backends and is not abstract"
rg -nP 'class\s+Viewer\w+' newton/_src/viewer -C2
rg -nP 'def\s+apply_forces\s*\(' newton/_src/viewer -C3
rg -nP '@abstractmethod.*apply_forces' newton/_src/viewer || echo "No abstract apply_forces found (good)"

274-279: Fix PyTorch device selection (unguarded, wrong API).

torch.set_device(...) is not a public API and this call is unguarded. Use set_default_device when available or cuda.set_device for CUDA, and only when args.device is provided and valid.

-    import torch
-
-    torch.set_device(args.device)
+    import torch
+    if getattr(args, "device", None):
+        # Prefer modern API when available
+        if hasattr(torch, "set_default_device"):
+            torch.set_default_device(args.device)
+        elif isinstance(args.device, str) and args.device.startswith("cuda"):
+            import re
+            m = re.search(r"cuda:(\d+)", args.device)
+            if m and torch.cuda.is_available():
+                torch.cuda.set_device(int(m.group(1)))
🧹 Nitpick comments (3)
README.md (1)

188-188: Nit: “line arguments” → “command-line arguments”.

Small wording tweak for clarity.

- The examples support the following common line arguments:
+ The examples support the following common command-line arguments:
newton/examples/selection/example_selection_materials.py (2)

72-81: Viewer-centric refactor looks good; drop redundant initial collide and keep FK note scoped.

  • Good shift to a viewer-driven runtime with set_model() and capture().
  • Minor: self.contacts = self.model.collide(self.state_0) (Line 106) is redundant since the solver is MuJoCo by default and simulate() resets contacts each step. Dropping it avoids a needless compute at startup.
-        self.contacts = self.model.collide(self.state_0)
+        self.contacts = None
  • The FK evaluation note is helpful; consider gating it to non-MuJoCo solvers if you ever switch the default solver here to avoid extra work, but leaving it unconditional is acceptable.

Also applies to: 91-98, 101-107, 114-120, 151-159


270-273: Avoid double-parsing CLI args; let init() own parsing.

args = parser.parse_known_args()[0] (Line 270) is redundant since init(parser) parses again and returns args. Removing the first parse reduces confusion.

-    args = parser.parse_known_args()[0]
-
     viewer, args = newton.examples.init(parser)
📜 Review details

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f73077d and 7062766.

⛔ Files ignored due to path filters (3)
  • docs/images/examples/example_selection_articulations.jpg is excluded by !**/*.jpg
  • docs/images/examples/example_selection_cartpole.jpg is excluded by !**/*.jpg
  • docs/images/examples/example_selection_materials.jpg is excluded by !**/*.jpg
📒 Files selected for processing (3)
  • README.md (1 hunks)
  • newton/_src/viewer/viewer.py (0 hunks)
  • newton/examples/selection/example_selection_materials.py (6 hunks)
💤 Files with no reviewable changes (1)
  • newton/_src/viewer/viewer.py
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-19T12:27:30.629Z
Learnt from: preist-nvidia
PR: newton-physics/newton#579
File: newton/examples/example_mujoco.py:350-354
Timestamp: 2025-08-19T12:27:30.629Z
Learning: In Newton examples, there's a distinction between solver parameters and Example class attributes. The Example class can have its own use_mujoco attribute for controlling example-level behavior (like CUDA graphs, rendering logic), while the solver uses use_mujoco_cpu for backend selection. These serve different purposes and should not be conflated during API renames.

Applied to files:

  • newton/examples/selection/example_selection_materials.py
🧬 Code graph analysis (1)
newton/examples/selection/example_selection_materials.py (4)
newton/_src/sim/builder.py (4)
  • shape_count (494-495)
  • ModelBuilder (67-4099)
  • add_ground_plane (2205-2225)
  • replicate (574-580)
newton/examples/selection/example_selection_articulations.py (5)
  • Example (86-287)
  • reset (247-282)
  • capture (200-205)
  • step (220-245)
  • simulate (207-218)
newton/examples/selection/example_selection_cartpole.py (4)
  • Example (58-180)
  • capture (128-133)
  • step (145-172)
  • simulate (135-143)
newton/examples/__init__.py (3)
  • create_parser (97-123)
  • init (126-167)
  • run (36-44)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run GPU Unit Tests on AWS EC2 (Pull Request)
  • GitHub Check: Run GPU Benchmarks (Pull Request)
  • GitHub Check: run-newton-tests / newton-unittests (ubuntu-latest)
  • GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (5)
README.md (1)

152-185: Selection Examples gallery verified
All example scripts, image assets, and registry entries are present and correctly referenced:

  • Script files in newton/examples/selection/
    • example_selection_cartpole.py
    • example_selection_materials.py
    • example_selection_articulations.py

  • Image assets in docs/images/examples/
    • example_selection_cartpole.jpg
    • example_selection_materials.jpg
    • example_selection_articulations.jpg

  • Registry entries in newton/examples/__init__.py (lines 186–188)
    "selection_articulations": "newton.examples.selection.example_selection_articulations"
    "selection_cartpole": "newton.examples.selection.example_selection_cartpole"
    "selection_materials": "newton.examples.selection.example_selection_materials"

All checks passed — ready to merge.

newton/examples/selection/example_selection_materials.py (4)

16-29: Helpful example header and usage hint.

The banner and command are clear and match the README section.


162-168: CUDA graph capture is set up correctly; verify invariants.

Capturing simulate() once at init is fine as long as launch shapes, solver, and sim_substeps remain constant. If any of these change at runtime (e.g., toggling solver or substeps), recapture will be required.

Would you like me to add a small guard that re-captures when sim_substeps changes or when a different solver is selected?


190-196: Graph launch fallback logic is clean.

The step() control flow between capture_launch and simulate is straightforward and correct.


253-255: Render path integrates properly with the new Viewer API.

begin_frame/log_state/end_frame usage is consistent with other updated examples.

@eric-heiden eric-heiden merged commit a11c534 into newton-physics:main Aug 22, 2025
8 of 10 checks passed
@shi-eric shi-eric mentioned this pull request Aug 25, 2025
4 tasks
eric-heiden added a commit to eric-heiden/newton that referenced this pull request Jan 28, 2026
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Co-authored-by: Eric Heiden <eheiden@nvidia.com>
Co-authored-by: Eric Heiden <eric-heiden@outlook.com>
This was referenced Feb 11, 2026
mmacklin added a commit to mmacklin/newton that referenced this pull request Apr 7, 2026
Signed-off-by: Miles Macklin <mmacklin@nvidia.com>
Signed-off-by: Eric Heiden <eric-heiden@outlook.com>
Co-authored-by: Eric Heiden <eheiden@nvidia.com>
Co-authored-by: Eric Heiden <eric-heiden@outlook.com>
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.

3 participants