API documentation (#544)#613
Conversation
📝 WalkthroughWalkthroughAdds six mesh-creation functions to the public API and docs; introduces a device-backed Contacts buffer class and a CollisionPipeline; precomputes shape-contact pairs during ModelBuilder.finalize; renames ContactSensor.init parameters; adds IK MIXED mode and per-problem target setters; extends Mesh/SDF APIs and many docstrings; adds ViewerNull/ViewerRerun/ViewerUSD runtime/documentation tweaks. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant User
participant ModelBuilder
participant Finder as find_shape_contact_pairs
participant Model
User->>ModelBuilder: finalize(...)
activate ModelBuilder
ModelBuilder->>Finder: compute pairs(groups, filters)
Finder-->>ModelBuilder: shape_contact_pairs, pair_count
ModelBuilder->>Model: attach pairs -> model.shape_contact_pairs
deactivate ModelBuilder
Model-->>User: Model (with precomputed pairs)
sequenceDiagram
autonumber
participant User
participant Model
participant Pipeline as CollisionPipeline
participant Kernels as CollisionKernels
participant Contacts
User->>Model: collide(state)
Model->>Pipeline: from_model(model, ...)
Pipeline-->>Model: pipeline
Model->>Pipeline: collide(model, state)
activate Pipeline
Pipeline->>Kernels: run broad/narrowphase kernels
Kernels-->>Pipeline: fill contact buffers
Pipeline->>Contacts: allocate/populate Contacts (device-scoped)
Pipeline-->>Model: return Contacts
Model-->>User: Contacts
deactivate Pipeline
sequenceDiagram
autonumber
participant User
participant IKPos as IKPositionObjective
User->>IKPos: set_target_position(i, pos)
IKPos->>IKPos: update per-problem buffer
User->>IKPos: set_target_positions(positions[])
IKPos->>IKPos: bulk update buffers
sequenceDiagram
autonumber
participant User
participant ContactSensor
participant Model
participant Contacts
User->>ContactSensor: __init__(model, sensing_obj_bodies, sensing_obj_shapes, counterpart_bodies, counterpart_shapes, ...)
ContactSensor->>Model: resolve names -> indices/maps
ContactSensor-->>User: configured sensor
User->>ContactSensor: eval(contacts)
ContactSensor->>Contacts: read contact buffers
Contacts-->>ContactSensor: per-shape/particle contact data
ContactSensor-->>User: aggregated sensor outputs
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
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 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
newton/_src/sim/articulation.py (1)
340-347: Clarify None-handling semantics for articulation_maskThe kernel signature doesn’t mark
articulation_maskas optional, yet the implementation branches on its presence. Add a short note in the kernel doc (or a comment near the param) thatNoneis supported and that the mask must reside onmodel.device.Proposed comment near the param:
- articulation_mask: wp.array( - dtype=bool - ), # used to enable / disable FK for an articulation, if None then treat all as enabled + articulation_mask: wp.array(dtype=bool), # Optional: pass None to enable all. Must be on the same device as model.Also applies to: 365-371
newton/_src/sim/joints.py (2)
21-45: Tighten wording for DISTANCE and D6 to avoid confusionMinor clarity tweak: DISTANCE is represented with FREE-like coordinates (6 DoF velocities, 7 position coords) but semantically enforces a distance constraint. Adding that sentence helps users reconcile the representation with the name.
Suggested doc tweak:
- """Distance constraint: maintains a fixed distance between two points (6 DoF, 7 coordinates).""" + """Distance constraint: maintains a fixed distance between two points. + Represented with FREE-like coordinates (6 DoF velocities, 7 position coordinates)."""
48-66: Validate num_axes for joint types or document expectations explicitly
get_joint_dof_countforwardsnum_axesfor most types; callers passingnum_axes != 1for PRISMATIC/REVOLUTE (or unexpected values for others) will get surprising results. Either add guards or make the expectations explicit in the docstring.Option A (doc-only):
- num_axes (int): The number of axes for the joint (typically 1 for prismatic/revolute, 3 for D6, etc). + num_axes (int): The number of translational/rotational axes considered for this joint. + Expected values by type: + - PRISMATIC/REVOLUTE: 1 + - D6: 3 (per-translation and per-rotation groups handled elsewhere) + - Others: ignoredOption B (light validation):
def get_joint_dof_count(joint_type: int, num_axes: int) -> tuple[int, int]: + if joint_type in (JointType.PRISMATIC, JointType.REVOLUTE) and num_axes != 1: + raise ValueError(f"Expected num_axes=1 for {JointType(joint_type).name}, got {num_axes}")newton/_src/sim/style3d/builder_style3d.py (1)
122-122: Minor typo in docstring.The docstring should maintain consistency by referring to "another Style3DModelBuilder" instead of starting with "another".
- """Copies the data from another `Style3DModelBuilder` to this `Style3DModelBuilder`. + """Copies the data from another Style3DModelBuilder to this Style3DModelBuilder.
📜 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 c367f67ce0ee379310a0dac1dbeee6e4e47bf327.
⛔ Files ignored due to path filters (10)
docs/api/_generated/newton.utils.create_box_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_capsule_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_cone_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_cylinder_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_plane_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_sphere_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.viewer.ViewerGL.rstis excluded by!**/_generated/**docs/api/_generated/newton.viewer.ViewerNull.rstis excluded by!**/_generated/**docs/api/_generated/newton.viewer.ViewerRerun.rstis excluded by!**/_generated/**docs/api/_generated/newton.viewer.ViewerUSD.rstis excluded by!**/_generated/**
📒 Files selected for processing (18)
docs/api/newton_utils.rst(1 hunks)docs/api/newton_viewer.rst(1 hunks)newton/_src/core/types.py(4 hunks)newton/_src/geometry/flags.py(1 hunks)newton/_src/geometry/types.py(10 hunks)newton/_src/sim/articulation.py(1 hunks)newton/_src/sim/builder.py(9 hunks)newton/_src/sim/collide.py(7 hunks)newton/_src/sim/contacts.py(3 hunks)newton/_src/sim/ik.py(24 hunks)newton/_src/sim/joints.py(2 hunks)newton/_src/sim/model.py(15 hunks)newton/_src/sim/state.py(1 hunks)newton/_src/sim/style3d/builder_style3d.py(2 hunks)newton/_src/sim/style3d/model_style3d.py(1 hunks)newton/_src/utils/__init__.py(2 hunks)newton/_src/utils/contact_sensor.py(3 hunks)newton/_src/utils/selection.py(2 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-08-12T18:04:06.287Z
Learnt from: nvlukasz
PR: newton-physics/newton#519
File: newton/_src/utils/import_usd.py:364-365
Timestamp: 2025-08-12T18:04:06.287Z
Learning: The quat_between_axes function in Newton accepts AxisType parameters, which includes both Axis enum values and strings. The function internally handles string-to-Axis conversion using Axis.from_any(), so passing strings directly to quat_between_axes is valid and correct.
Applied to files:
newton/_src/core/types.py
📚 Learning: 2025-08-12T18:04:06.287Z
Learnt from: nvlukasz
PR: newton-physics/newton#519
File: newton/_src/utils/import_usd.py:364-365
Timestamp: 2025-08-12T18:04:06.287Z
Learning: The quat_between_axes function in Newton accepts AxisType parameters, which includes strings like "X", "Y", "Z" as well as Axis enum values and integers. The function internally handles conversion using Axis.from_any(), so passing strings directly to quat_between_axes is valid and correct behavior.
Applied to files:
newton/_src/core/types.py
📚 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/_src/geometry/flags.pynewton/_src/geometry/types.py
📚 Learning: 2025-08-20T18:02:36.042Z
Learnt from: eric-heiden
PR: newton-physics/newton#587
File: newton/_src/sim/builder.py:656-661
Timestamp: 2025-08-20T18:02:36.042Z
Learning: In Newton physics, collisions between shapes with body index -1 (world-attached/global shapes) are automatically skipped by the collision system, so no manual collision filter pairs need to be added between global shapes from different builders.
Applied to files:
newton/_src/sim/model.py
📚 Learning: 2025-07-21T19:11:04.077Z
Learnt from: dylanturpin
PR: newton-physics/newton#450
File: newton/sim/ik.py:1286-1296
Timestamp: 2025-07-21T19:11:04.077Z
Learning: In Newton's IK system, the JointLimitObjective class intentionally only handles joints where DOF count equals coordinate count (1-1 mapping). This is appropriate because joint limits are typically only meaningful for simple joint types like revolute and prismatic joints, not complex joints like ball or free joints where DOF count != coordinate count.
Applied to files:
newton/_src/sim/ik.py
📚 Learning: 2025-08-12T05:17:34.423Z
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Applied to files:
newton/_src/utils/__init__.py
🧬 Code graph analysis (5)
newton/_src/sim/builder.py (1)
newton/_src/sim/state.py (2)
body_count(100-104)particle_count(107-111)
newton/_src/sim/model.py (4)
newton/_src/sim/collide.py (1)
device(347-354)newton/_src/sim/contacts.py (1)
device(116-120)newton/_src/sim/state.py (3)
requires_grad(91-97)joint_coord_count(114-118)body_count(100-104)newton/_src/sim/builder.py (1)
body_count(675-679)
newton/_src/sim/ik.py (2)
newton/_src/sim/collide.py (1)
device(347-354)newton/_src/sim/contacts.py (1)
device(116-120)
newton/_src/sim/collide.py (1)
newton/_src/sim/model.py (1)
Model(29-630)
newton/_src/utils/contact_sensor.py (1)
newton/_src/solvers/mujoco/solver_mujoco.py (1)
update_contacts(1612-1654)
⏰ 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). (3)
- 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 (60)
docs/api/newton_viewer.rst (1)
16-19: Verified: public exports for Viewer classes are correctThe four classes—
ViewerGL,ViewerNull,ViewerRerun, andViewerUSD—are indeed imported from the internal module and re-exported innewton/viewer.py(see thefrom ._src.viewer import …at line 21 and the matching entries in__all__on lines 28–31). They therefore exist as public attributes ofnewton.viewer, and the Sphinx autosummary targets will resolve successfully. No changes needed.newton/_src/geometry/flags.py (1)
21-23: Good incremental doc improvements on public enumsEnum-level docstrings read clearly and match existing member-level docs. No API or behavior change introduced.
Also applies to: 31-33
newton/_src/core/types.py (4)
65-72: LGTM! Clear and consistent documentation improvements.The enhanced class docstring and per-member docstrings provide better clarity for API users. The documentation follows a consistent pattern across all axis members.
76-87: LGTM! Comprehensive docstring with proper error documentation.The docstring clearly explains the function's behavior, including case-insensitive handling and the specific ValueError that can be raised.
99-111: LGTM! Well-documented type conversion method.The docstring properly documents all accepted input types and potential exceptions, making the API contract clear for users.
141-161: LGTM! Clear documentation for vector conversion methods.Both
to_vector()andto_vec3()methods now have clear docstrings explaining their return types and purpose.newton/_src/sim/ik.py (4)
26-41: LGTM! Enhanced enum documentation for IK Jacobian modes.The expanded docstrings clearly explain the different Jacobian computation strategies, helping users understand when to use each mode.
1262-1279: LGTM! New target position setter method is well-implemented.The
set_target_positionmethod provides a clean API for updating individual problem targets. The implementation correctly uses a Warp kernel to update the target array on the specified device.
1281-1296: LGTM! Batch target setter complements the single-problem setter.The
set_target_positionsmethod efficiently updates all targets in batch using the appropriate Warp kernel dimension.
919-957: LGTM! Comprehensive IKObjective base class documentation.The enhanced docstring clearly describes the purpose and interface of IK objectives, with detailed method descriptions that will help developers implement custom objectives.
newton/_src/sim/state.py (3)
22-43: LGTM! Excellent State class documentation with clear attribute descriptions.The enhanced docstring provides a comprehensive overview of the State class and its role in simulations. The Attributes section clearly documents each array's shape and dtype, making the API contract explicit.
77-82: LGTM! Clear documentation for force clearing behavior.The expanded docstring explicitly states the method's behavior, making it clear which arrays are affected.
63-69: Documentation verification complete: no inconsistencies found.I’ve confirmed in the code that:
- The Euler and XPBD solvers apply torques using the body’s center of mass (they compute moments with cross(r_com, f) in their kernels).
- The Featherstone solver treats input wrenches at the world origin (it uses cross(r_world, f) when assembling body forces).
- The state attribute docstring in
newton/_src/sim/state.pyaccurately describes this distinction.No updates are needed to the existing documentation.
newton/_src/utils/__init__.py (4)
29-47: LGTM! Excellent technical documentation for transform_inertia.The enhanced docstring provides both the mathematical formulation and a proper reference to Frank & Park's textbook, making it valuable for users who need to understand the underlying theory.
66-84: LGTM! Clear documentation for mathematical utility functions.The Boltzmann function documentation clearly explains the interpolation behavior and the role of the alpha parameter.
88-159: LGTM! Consistent documentation pattern for smooth approximation functions.All smooth/leaky min/max functions now have clear docstrings explaining their differentiable nature and the role of smoothing parameters. The relationship between leaky and smooth variants (with eps=1e-5) is properly documented.
162-236: LGTM! Complete documentation for vector utility functions.All vector operations now have clear docstrings following a consistent pattern, making the API intuitive to use.
newton/_src/geometry/types.py (6)
26-64: LGTM! Comprehensive GeoType enum documentation.Each geometry type now has a clear description, making it easier for users to understand the available shape primitives.
72-106: LGTM! Improved SDF class with proper initialization and documentation.The SDF class now properly initializes the
comattribute with a default zero vector when not provided, and the documentation clearly describes all attributes and their defaults.
122-156: LGTM! Excellent Mesh class documentation with practical example.The enhanced docstring provides a clear description of the Mesh class purpose and includes a practical example using OpenMesh. The Attributes section comprehensively documents all mesh properties.
158-205: LGTM! Well-documented Mesh constructor with flexible data handling.The constructor now supports optional per-vertex normals and UVs, with clear documentation of all parameters and their defaults. The inertia computation behavior is properly controlled and documented.
206-222: LGTM! Useful copy method with clear documentation.The copy method provides flexibility for creating mesh variants, with optional vertex/index replacement and inertia recomputation.
259-268: LGTM! Clear documentation for finalize method.The updated docstring clearly explains that the method creates a Warp Mesh and returns its ID.
newton/_src/sim/style3d/builder_style3d.py (1)
95-106: LGTM! Clear and comprehensive documentation added.The expanded docstring properly documents the initialization parameters and public-facing attributes that are specific to Style3D models. This improves the API documentation clarity.
newton/_src/sim/style3d/model_style3d.py (1)
49-67: Well-documented factory method implementation.The
from_modelmethod now has clear documentation describing its purpose and behavior. The implementation correctly usescls.__new__to avoid calling__init__, copies all attributes from the base Model, and initializes Style3D-specific attributes to None.newton/_src/sim/contacts.py (3)
24-60: Excellent comprehensive documentation for the Contacts class.The expanded docstring provides clear descriptions of all buffer attributes, their shapes, and data types. The documentation properly explains the purpose of each field and notes that this is a temporary solution whose interface may change.
100-103: LGTM! Clear method documentation added.The docstring clearly describes the clearing behavior.
115-120: LGTM! Device property properly documented.The docstring clearly explains what the property returns.
newton/_src/sim/model.py (6)
30-53: Excellent Model class documentation overhaul.The expanded docstring provides a comprehensive overview of the Model class, its purpose, key features, and the recommended construction workflow via ModelBuilder. The documentation clearly explains the environment grouping concept which is crucial for understanding the simulation architecture.
443-455: Clear documentation for state creation method.The docstring properly documents the method's purpose, the optional
requires_gradparameter, and clarifies that the returned state is initialized from the model description.
479-492: Well-documented control object creation.The docstring clearly explains the method's purpose, parameters including the
clone_variablesoption, and return value.
526-548: Comprehensive collision documentation.The docstring thoroughly documents all parameters and clearly explains that this method creates a Contacts object for use in contact-dynamics kernels. The collision pipeline creation behavior is well explained.
576-605: Robust input validation added to add_attribute method.The method now properly validates that:
- The attribute doesn't already exist
- The attribute is a
wp.array- The attribute is on the same device as the Model
This prevents common errors and ensures consistency. The documentation clearly lists the supported frequency values.
607-630: Clear documentation for attribute frequency retrieval.The docstring properly documents the supported frequency values and the error condition when the frequency is unknown.
newton/_src/utils/contact_sensor.py (6)
113-126: Clear documentation for populate_contacts function.The docstring properly describes the function's purpose, parameters, and in-place update behavior.
131-149: Comprehensive ContactSensor class documentation.The class docstring clearly explains the sensor's purpose, configuration options, and key attributes. This provides users with a good understanding of how to use the sensor.
151-181: Improved parameter naming and documentation in ContactSensor.init.The parameter names have been updated to be more explicit and clear:
sensor_body→sensing_obj_bodiescontact_partners_shape→counterpart_shapescontact_partners_body→counterpart_bodiesThe documentation clearly explains the mutual exclusivity constraints and the purpose of each parameter.
236-246: Clear documentation for eval method.The docstring properly describes the method's purpose and behavior.
248-255: Well-documented get_total_force method.The docstring clearly describes the return value and its type.
183-227: Backward-compatibility check completedI searched the entire codebase—including all Python files, tests, and examples—and found no internal references to the old
ContactSensorparameters (sensor_body,contact_partners_shape, orcontact_partners_body). This means the rename is self-contained and won’t break anything in our own examples or tests. External users will still need to update their instantiations to the new parameter names; please be sure to document this change in the changelog or an upgrade guide before the next release.newton/_src/sim/collide.py (4)
33-45: LGTM! Well-documented function signature with return type annotation.The addition of the return type annotation and comprehensive docstring improves API clarity.
65-90: Excellent documentation for the CollisionPipeline class!The comprehensive docstring clearly describes the class purpose, responsibilities, and all attributes. This will greatly help developers understand and use the collision system.
168-184: Clear documentation for the from_model class method.The docstring properly explains all parameters and their defaults, making the API intuitive to use.
347-354: Good addition of the device property with clear documentation.The property provides convenient access to the device information with a well-documented return value.
newton/_src/utils/selection.py (6)
126-163: Comprehensive docstring for ArticulationView class!The detailed docstring with clear descriptions of arguments and attributes will significantly improve the usability of this complex class.
693-703: Consistent and clear method documentation.All the new method docstrings follow a consistent pattern with clear descriptions of parameters and return values. The use of forward-referenced types in signatures is appropriate for avoiding circular imports.
705-715: Well-documented get_link_velocities method.Clear description of the world-space spatial velocities return value.
717-738: Clear documentation for DoF position methods.Both getter and setter methods have consistent documentation explaining their purpose and parameters.
740-761: Consistent documentation for DoF velocity methods.Maintains the same documentation pattern as the position methods.
763-784: Complete documentation for DoF force methods.The force-related methods are properly documented with appropriate forward-referenced Control type.
newton/_src/sim/builder.py (10)
268-433: Exceptional documentation for ModelBuilder.init!This comprehensive docstring documents all 150+ attributes, providing invaluable reference for developers. The organization by category (particles, shapes, joints, etc.) makes it easy to navigate.
647-658: Clear documentation for the up_vector property.The read-only property is well-documented with its behavior clearly explained.
668-736: Concise property documentation for counts.Each count property has a brief but clear docstring describing what it counts.
778-811: Comprehensive documentation for the replicate method.The detailed docstring with example usage and notes about environment grouping and spatial arrangement helps users understand this complex functionality.
1787-1787: Clear specification of allowed joint types for equality constraints.The explicit mention of "prismatic and revolute" as the only allowed scalar joint types prevents user confusion.
3921-3939: Good documentation for add_free_joints_to_floating_bodies.The expanded docstring clearly explains the method's behavior and the expected use of the new_bodies parameter.
3622-3646: Well-documented add_particle_grid method.Comprehensive parameter descriptions with clear explanations of optional parameters and their defaults.
4004-4023: Excellent documentation for the finalize method.The expanded docstring clearly describes the finalization process, validation steps, and the purpose of the returned Model object.
4335-4336: Integration of collision pair computation during finalization.The addition of
find_shape_contact_pairsandcount_rigid_contact_pointsproperly integrates the new collision system into the model finalization process.
4348-4395: Well-implemented and documented find_shape_contact_pairs method.The implementation correctly:
- Handles collision groups and filters
- Ensures canonical ordering of pairs
- Handles the special -1 group for global collisions
- Documents side effects clearly
The logic for computing collision pairs based on groups and filters is sound.
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
c367f67 to
d33496c
Compare
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
newton/_src/viewer/viewer_rerun.py (2)
71-77: Critical: server_uri may be used before assignment; address is ignored; use connect_grpc/serve_grpc correctly
- If server=False and launch_viewer=True (the defaults differ only by server), server_uri is undefined when passed to rr.serve_web_viewer(connect_to=server_uri) → NameError.
- The address parameter is never used. For remote viewers or non-default ports you should either:
- start a local gRPC server with rr.serve_grpc(grpc_port=…) and pass its URI to the web viewer, or
- connect to an existing server via rr.connect_grpc(url=…).
- Rerun’s Python API indicates rr.serve_grpc returns a server URI string (default port 9876) and rr.connect_grpc expects a fully qualified rerun+http URL ending in /proxy. rr.disconnect() is available for cleanup. Please wire these up accordingly. (ref.rerun.io)
Apply this minimal, safe refactor to initialize server_uri, honor address, and avoid NameError:
@@ - # Set up connection based on mode - if self.server: - server_uri = rr.serve_grpc() - - # Optionally launch viewer client - if self.launch_viewer: - rr.serve_web_viewer(connect_to=server_uri) + # Set up connection based on mode + server_uri = None + if self.server: + # Try to parse a port from "host:port" or accept plain "port" + grpc_port = None + try: + if ":" in self.address: + _, port_str = self.address.split(":", 1) + grpc_port = int(port_str) + else: + grpc_port = int(self.address) + except Exception: + # Fall back to default (9876) if parsing fails + grpc_port = None + server_uri = rr.serve_grpc(grpc_port=grpc_port) + else: + # Connect to an existing viewer/server if an address is provided + if self.address: + rr.connect_grpc(url=f"rerun+http://{self.address}/proxy") + + # Optionally launch viewer client + if self.launch_viewer: + connect_to = server_uri or (f"rerun+http://{self.address}/proxy" if self.address else None) + # Returns immediately; no process handle is returned + rr.serve_web_viewer(connect_to=connect_to)If you prefer a one-liner that serves both the gRPC server and a web viewer, consider rr.serve_web(open_browser=True, grpc_port=…, web_port=…), which the docs describe as equivalent to serve_grpc + serve_web_viewer. (ref.rerun.io)
Also applies to: 46-54
154-171: Bug: vertex_colors referenced before assignment when colors is NoneWhen colors is None, vertex_colors is never defined but still passed to rr.Mesh3D(..., vertex_colors=vertex_colors) → UnboundLocalError. Also, when colors are floats in 0–1 you correctly scale by 255; consider supporting both float [0–1] and integer [0–255] inputs as allowed by Rerun’s Mesh3D docs. (rerun.io)
Apply this fix to default vertex_colors to None and handle both float and integer inputs robustly:
@@ - # Handle colors - ReRun doesn't support per-instance colors - # so we just use the first instance's color for all instances - if colors is not None: - colors_np = colors.numpy().astype(np.float32) - # Take the first instance's color and apply to all vertices - first_color = colors_np[0] - color_rgb = np.array(first_color * 255, dtype=np.uint8) - num_vertices = len(mesh_data["points"]) - vertex_colors = np.tile(color_rgb, (num_vertices, 1)) + # Handle colors - Rerun doesn't support per-instance colors. + # Use the first instance's color for all vertices of the base mesh. + vertex_colors = None + if colors is not None: + colors_np = colors.numpy() + first = colors_np[0] + if np.issubdtype(colors_np.dtype, np.floating): + color_rgb = np.clip(first, 0.0, 1.0) * 255.0 + else: + color_rgb = np.clip(first, 0, 255) + color_rgb = np.asarray(color_rgb, dtype=np.uint8) + num_vertices = len(mesh_data["points"]) + vertex_colors = np.tile(color_rgb, (num_vertices, 1)) @@ - mesh_3d = rr.Mesh3D( + mesh_3d = rr.Mesh3D( vertex_positions=mesh_data["points"], triangle_indices=mesh_data["indices"], vertex_normals=mesh_data["normals"], vertex_colors=vertex_colors, )Optional: Document that only the first color is used for all instances to match Rerun’s per-entity color behavior; consider later emitting per-instance colors via separate entities if needed. (rerun.io)
newton/_src/sim/builder.py (1)
3436-3439: Fix mass/density computation for cloth grid (uses dim_x twice)
total_mass = mass * (dim_x + 1) * (dim_x + 1)should use(dim_y + 1)for the second factor.Apply this diff:
- total_mass = mass * (dim_x + 1) * (dim_x + 1) + total_mass = mass * (dim_x + 1) * (dim_y + 1)
🧹 Nitpick comments (13)
newton/_src/viewer/viewer_rerun.py (6)
228-237: Viewer process tracking is ineffective; simplify or capture a real handleself._viewer_process is never assigned (rr.serve_web_viewer doesn’t return a Popen-like handle), so is_running() always falls back to self._running and close()’s terminate/kill block is dead code. The Rerun API exposes rr.spawn() for launching a native viewer process or rr.disconnect() to close connections/servers; if you want process tracking, use rr.spawn() and store the returned handle. Otherwise, simplify the code to rely on _running only and rr.disconnect(). (ref.rerun.io)
If you don’t plan to use rr.spawn(), apply this simplification:
@@ - # Check if viewer process is still alive - if self._viewer_process is not None: - return self._viewer_process.poll() is None - return self._running + return self._running @@ - # Close viewer process if we spawned one - if self._viewer_process is not None: - try: - self._viewer_process.terminate() - self._viewer_process.wait(timeout=5) - except subprocess.TimeoutExpired: - self._viewer_process.kill() - except Exception: - pass - self._viewer_process = None + # If we ever start managing an external process via rr.spawn(), handle it here.If you want to support an external viewer, capture and manage the handle returned by rr.spawn(). (ref.rerun.io)
Also applies to: 247-257
104-108: Prefer explicit exceptions over asserts for argument validationassert can be disabled with Python -O, removing your runtime checks. Raise TypeError/ValueError explicitly to validate inputs.
- assert isinstance(points, wp.array) - assert isinstance(indices, wp.array) - assert normals is None or isinstance(normals, wp.array) - assert uvs is None or isinstance(uvs, wp.array) + if not isinstance(points, wp.array): + raise TypeError("points must be a wp.array") + if not isinstance(indices, wp.array): + raise TypeError("indices must be a wp.array") + if normals is not None and not isinstance(normals, wp.array): + raise TypeError("normals must be a wp.array or None") + if uvs is not None and not isinstance(uvs, wp.array): + raise TypeError("uvs must be a wp.array or None")
113-116: Validate flattened index buffers are trianglesIf indices is 1D, ensure its length is divisible by 3 before reshaping to (-1, 3) to avoid silent shape errors.
# Rerun expects indices as (N, 3) for triangles if indices_np.ndim == 1: + if (indices_np.size % 3) != 0: + raise ValueError("indices length must be divisible by 3") indices_np = indices_np.reshape(-1, 3)
92-103: Docstring is clear; note unused params or wire them throughDoc is solid. Consider either removing hidden/backface_culling from the signature (if unsupported by Rerun) or documenting that they are currently ignored to avoid confusion.
240-244: Cleanup path is sound; consider flushing before disconnectrr.disconnect() will close connections and servers. Optionally call rr.flush(blocking=True) first to ensure pending batches are sent before teardown (micro-batching is enabled by default). (ref.rerun.io)
# Disconnect from rerun try: - rr.disconnect() + try: + rr.flush(blocking=True) + except Exception: + pass + rr.disconnect() except Exception: passAlso applies to: 258-263
135-145: Clarify color range and materials behavior in the docstring
- Colors: specify accepted ranges (float 0–1 or uint 0–255) to align with Rerun’s expectations. (ref.rerun.io)
- materials is currently unused; call it out in the docstring to avoid implying support.
- colors (wp.array): Instance colors (wp.vec3). - materials (wp.array): Instance materials (wp.vec4). + colors (wp.array): Instance colors (wp.vec3). Floats in [0,1] or uints in [0,255]; first color is applied to all vertices. + materials (wp.array): Instance materials (wp.vec4). Currently unused.newton/_src/sim/ik.py (2)
1163-1193: Minor documentation enhancement suggestion.The class docstring could be slightly more explicit about the coordinate system conventions.
Consider adding a note about coordinate system conventions:
class IKPositionObjective(IKObjective): """ Inverse kinematics objective for enforcing a positional target on a single end-effector link. This objective constrains the world-space position of a specified link (optionally offset in the link's local frame) to match a target position for each problem in a batch. The objective supports both analytic and autodiff Jacobian - computation. + computation. + + All positions are specified in world coordinates, with the link_offset applied in the link's local frame + before transformation to world space.
1803-1837: Minor documentation enhancement suggestion.The rotation objective documentation could clarify the quaternion representation and error computation.
Consider adding clarity about quaternion conventions:
This objective constrains the world-space orientation of a specified link (optionally offset by a local rotation) to match a target quaternion for each problem in a batch. The objective supports both analytic and autodiff Jacobian - computation. + computation. + + Quaternions are represented as vec4 (x, y, z, w) and rotation errors are computed in axis-angle form. + The canonicalize_quat_err parameter controls whether to use the shortest rotation path.newton/_src/sim/collide.py (1)
220-221: Consider clarifying the gradient-related allocation behaviorThe comment mentions allocating new contact memory "for gradients", but the condition also checks if contacts is None. This might be clearer if separated.
Consider clarifying the logic:
- # Allocate new contact memory for contacts if needed (e.g., for gradients) - if self.contacts is None or self.requires_grad: + # Allocate new contact memory if not yet allocated or if gradients are required + # (gradient computation needs fresh buffers each time) + if self.contacts is None or self.requires_grad:newton/_src/geometry/types.py (2)
212-235: Mesh.copy should preserve optional attributes (normals/uvs/color) by defaultCurrent copy drops
normals,uvs, andcolor. That’s surprising given the expanded API/docs. Preserve them unless explicitly overridden.Apply this diff:
- m = Mesh( - vertices, indices, compute_inertia=recompute_inertia, is_solid=self.is_solid, maxhullvert=self.maxhullvert - ) + m = Mesh( + vertices, + indices, + normals=self._normals, + uvs=self._uvs, + compute_inertia=recompute_inertia, + is_solid=self.is_solid, + maxhullvert=self.maxhullvert, + color=self._color, + )
309-321: Hash stability and cost on large meshesThe hash flattens arrays into Python tuples, which is O(N) memory and time. For large meshes this is heavy and may spike memory.
Consider hashing on immutable bytes with dtype enforced:
- self._cached_hash = hash( - (tuple(np.array(self.vertices).flatten()), tuple(np.array(self.indices).flatten()), self.is_solid) - ) + v = np.asarray(self.vertices, dtype=np.float32).tobytes() + i = np.asarray(self.indices, dtype=np.int32).tobytes() + self._cached_hash = hash((v, i, bool(self.is_solid)))newton/_src/sim/builder.py (2)
1786-1786: Clarify “scalar joint types” phrasing“Only scalar joint types (prismatic and revolute) can be used.” is correct; consider linking to these types or referring to
JointType.PRISMATIC/JointType.REVOLUTEfor consistency across docs.
266-432: Suppress duplicate Sphinx index entries for ModelBuilder attributesCI is reporting duplicate-object warnings for the per-attribute docs in
ModelBuilder. To avoid this, update the generated stub atdocs/api/_generated/newton.ModelBuilder.rstby either:• Excluding those attributes from the auto-member list:
.. autoclass:: newton.ModelBuilder :members: :undoc-members: :show-inheritance: :member-order: groupwise + :exclude-members: balance_inertia, bound_mass, bound_inertia, validate_inertia_detailed• Or disabling index entries altogether on this directive:
.. autoclass:: newton.ModelBuilder :members: :undoc-members: :show-inheritance: :member-order: groupwise + :noindex:Either approach prevents Sphinx from generating duplicate index entries for those attributes. Let me know if you’d like a PR to apply one of these patches.
📜 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 c367f67ce0ee379310a0dac1dbeee6e4e47bf327 and b8835e2.
⛔ Files ignored due to path filters (10)
docs/api/_generated/newton.utils.create_box_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_capsule_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_cone_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_cylinder_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_plane_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.utils.create_sphere_mesh.rstis excluded by!**/_generated/**docs/api/_generated/newton.viewer.ViewerGL.rstis excluded by!**/_generated/**docs/api/_generated/newton.viewer.ViewerNull.rstis excluded by!**/_generated/**docs/api/_generated/newton.viewer.ViewerRerun.rstis excluded by!**/_generated/**docs/api/_generated/newton.viewer.ViewerUSD.rstis excluded by!**/_generated/**
📒 Files selected for processing (24)
docs/api/newton_utils.rst(1 hunks)docs/api/newton_viewer.rst(1 hunks)newton/_src/core/types.py(4 hunks)newton/_src/geometry/flags.py(1 hunks)newton/_src/geometry/types.py(10 hunks)newton/_src/sim/articulation.py(1 hunks)newton/_src/sim/builder.py(10 hunks)newton/_src/sim/collide.py(7 hunks)newton/_src/sim/contacts.py(3 hunks)newton/_src/sim/ik.py(24 hunks)newton/_src/sim/joints.py(2 hunks)newton/_src/sim/model.py(15 hunks)newton/_src/sim/state.py(1 hunks)newton/_src/sim/style3d/builder_style3d.py(2 hunks)newton/_src/sim/style3d/model_style3d.py(1 hunks)newton/_src/utils/__init__.py(2 hunks)newton/_src/utils/contact_sensor.py(3 hunks)newton/_src/utils/gizmo.py(11 hunks)newton/_src/utils/recorder_gui.py(3 hunks)newton/_src/utils/selection.py(2 hunks)newton/_src/viewer/viewer_gl.py(26 hunks)newton/_src/viewer/viewer_null.py(2 hunks)newton/_src/viewer/viewer_rerun.py(5 hunks)newton/_src/viewer/viewer_usd.py(5 hunks)
✅ Files skipped from review due to trivial changes (3)
- newton/_src/utils/gizmo.py
- newton/_src/viewer/viewer_gl.py
- docs/api/newton_viewer.rst
🚧 Files skipped from review as they are similar to previous changes (12)
- docs/api/newton_utils.rst
- newton/_src/sim/articulation.py
- newton/_src/geometry/flags.py
- newton/_src/sim/style3d/model_style3d.py
- newton/_src/sim/model.py
- newton/_src/utils/selection.py
- newton/_src/sim/style3d/builder_style3d.py
- newton/_src/sim/joints.py
- newton/_src/utils/init.py
- newton/_src/utils/contact_sensor.py
- newton/_src/sim/contacts.py
- newton/_src/core/types.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-07-21T19:11:04.077Z
Learnt from: dylanturpin
PR: newton-physics/newton#450
File: newton/sim/ik.py:1286-1296
Timestamp: 2025-07-21T19:11:04.077Z
Learning: In Newton's IK system, the JointLimitObjective class intentionally only handles joints where DOF count equals coordinate count (1-1 mapping). This is appropriate because joint limits are typically only meaningful for simple joint types like revolute and prismatic joints, not complex joints like ball or free joints where DOF count != coordinate count.
Applied to files:
newton/_src/sim/ik.py
🧬 Code graph analysis (5)
newton/_src/viewer/viewer_null.py (2)
newton/_src/viewer/viewer_usd.py (9)
log_instances(130-258)begin_frame(260-274)end_frame(276-282)is_running(284-293)close(295-302)log_lines(305-311)log_points(313-319)log_array(321-327)log_scalar(329-335)newton/_src/viewer/viewer.py (8)
log_instances(346-347)begin_frame(74-75)end_frame(370-371)close(374-375)log_lines(350-351)log_points(354-355)log_array(358-359)log_scalar(362-363)
newton/_src/sim/collide.py (1)
newton/_src/sim/model.py (1)
Model(29-630)
newton/_src/sim/builder.py (1)
newton/_src/sim/state.py (2)
body_count(100-104)particle_count(107-111)
newton/_src/viewer/viewer_rerun.py (4)
newton/_src/viewer/viewer_gl.py (6)
end_frame(382-397)is_running(446-453)close(464-468)log_points(289-307)log_array(309-313)log_scalar(315-319)newton/_src/viewer/viewer_null.py (6)
end_frame(95-99)is_running(101-108)close(110-114)log_points(130-141)log_array(143-151)log_scalar(153-161)newton/_src/viewer/viewer_usd.py (6)
end_frame(276-282)is_running(284-293)close(295-302)log_points(313-319)log_array(321-327)log_scalar(329-335)newton/_src/viewer/viewer.py (5)
end_frame(370-371)close(374-375)log_points(354-355)log_array(358-359)log_scalar(362-363)
newton/_src/viewer/viewer_usd.py (4)
newton/_src/viewer/viewer_gl.py (6)
is_running(446-453)close(464-468)log_lines(234-287)log_points(289-307)log_array(309-313)log_scalar(315-319)newton/_src/viewer/viewer_null.py (6)
is_running(101-108)close(110-114)log_lines(117-128)log_points(130-141)log_array(143-151)log_scalar(153-161)newton/_src/viewer/viewer_rerun.py (6)
is_running(227-237)close(239-262)log_lines(265-276)log_points(278-289)log_array(291-299)log_scalar(301-309)newton/_src/viewer/viewer.py (5)
close(374-375)log_lines(350-351)log_points(354-355)log_array(358-359)log_scalar(362-363)
🪛 GitHub Actions: Pull Request
newton/_src/sim/state.py
[warning] 1-1: docstring of newton.State.body_f:1: WARNING: duplicate object description of newton.State.body_f, other instance in api/_generated/newton.State, use :no-index: for one of them
[warning] 1-1: docstring of newton.State.body_q:1: WARNING: duplicate object description of newton.State.body_q, other instance in api/_generated/newton.State, use :no-index: for one of them
[warning] 1-1: docstring of newton.State.body_qd:1: WARNING: duplicate object description of newton.State.body_qd, other instance in api/_generated/newton.State, use :no-index: for one of them
[warning] 1-1: docstring of newton.State.joint_q:1: WARNING: duplicate object description of newton.State.joint_q, other instance in api/_generated/newton.State, use :no-index: for one of them
[warning] 1-1: docstring of newton.State.joint_qd:1: WARNING: duplicate object description of newton.State.joint_qd, other instance in api/_generated/newton.State, use :no-index: for one of them
[warning] 1-1: docstring of newton.State.particle_f:1: WARNING: duplicate object description of newton.State.particle_f, other instance in api/_generated/newton.State, use :no-index: for one of them
[warning] 1-1: docstring of newton.State.particle_q:1: WARNING: duplicate object description of newton.State.particle_q, other instance in api/_generated/newton.State, use :no-index: for one of them
[warning] 1-1: docstring of newton.State.particle_qd:1: WARNING: duplicate object description of newton.State.particle_qd, other instance in api/_generated/newton.State, use :no-index: for one of them
[warning] 1-1: docstring of newton.State.q:1: WARNING: duplicate object description of newton.State.q, other instance in api/_generated/newton.State, use :no-index: for one of them
newton/_src/geometry/types.py
[warning] 1-1: docstring of newton.Mesh.indices:1: WARNING: duplicate object description of newton.Mesh.indices, other instance in api/_generated/newton.Mesh, use :no-index: for one of them
[warning] 1-1: docstring of newton.Mesh.vertices:1: WARNING: duplicate object description of newton.Mesh.vertices, other instance in api/_generated/newton.Mesh, use :no-index: for one of them
newton/_src/sim/ik.py
[warning] 1-1: docstring of newton._src.sim.ik.IKObjective.bind_device:1: WARNING: duplicate object description of newton.ik.IKObjective.bind_device, other instance in api/_generated/newton.ik.IKObjective, use :no-index: for one of them
[warning] 1-1: docstring of newton._src.sim.ik.IKObjective.compute_jacobian_analytic:1: WARNING: duplicate object description of newton.ik.IKObjective.compute_jacobian_analytic, other instance in api/_generated/newton.ik.IKObjective, use :no-index: for one of them
[warning] 1-1: docstring of newton._src.sim.ik.IKObjective.compute_jacobian_autodiff:1: WARNING: duplicate object description of newton.ik.IKObjective.compute_jacobian_autodiff, other instance in api/_generated/newton.ik.IKObjective, use :no-index: for one of them
[warning] 1-1: docstring of newton._src.sim.ik.IKObjective.compute_residuals:1: WARNING: duplicate object description of newton.ik.IKObjective.compute_residuals, other instance in api/_generated/newton.ik.IKObjective, use :no-index: for one of them
[warning] 1-1: docstring of newton._src.sim.ik.IKObjective.init_buffers:1: WARNING: duplicate object description of newton.ik.IKObjective.init_buffers, other instance in api/_generated/newton.ik.IKObjective, use :no-index: for one of them
[warning] 1-1: docstring of newton._src.sim.ik.IKObjective.residual_dim:1: WARNING: duplicate object description of newton.ik.IKObjective.residual_dim, other instance in api/_generated/newton.ik.IKObjective, use :no-index: for one of them
[warning] 1-1: docstring of newton._src.sim.ik.IKObjective.supports_analytic:1: WARNING: duplicate object description of newton.ik.IKObjective.supports_analytic, other instance in api/_generated/newton.ik.IKObjective, use :no-index: for one of them
newton/_src/sim/builder.py
[warning] 1-1: docstring of newton.ModelBuilder.balance_inertia:1: WARNING: duplicate object description of newton.ModelBuilder.balance_inertia, other instance in api/_generated/newton.ModelBuilder, use :no-index: for one of them
[warning] 1-1: docstring of newton.ModelBuilder.bound_inertia:1: WARNING: duplicate object description of newton.ModelBuilder.bound_inertia, other instance in api/_generated/newton.ModelBuilder, use :no-index: for one of them
[warning] 1-1: docstring of newton.ModelBuilder.bound_mass:1: WARNING: duplicate object description of newton.ModelBuilder.bound_mass, other instance in api/_generated/newton.ModelBuilder, use :no-index: for one of them
[warning] 1-1: docstring of newton.ModelBuilder.validate_inertia_detailed:1: WARNING: duplicate object description of newton.ModelBuilder.validate_inertia_detailed, other instance in api/_generated/newton.ModelBuilder, use :no-index: for one of them
🔇 Additional comments (44)
newton/_src/viewer/viewer_rerun.py (2)
206-216: Good: rr.set_time usage matches the 0.23+ APIUsing rr.set_time("time", timestamp=time) is the recommended unified API after 0.23 (replacing set_time_* variants). This is consistent with current docs. (rerun.io)
31-37: Nice: clear, high-level class docstringThe overview reads well and matches the intended capabilities. No action needed.
newton/_src/sim/ik.py (10)
25-41: LGTM - Clear documentation for new MIXED mode.The addition of the MIXED mode to the IKJacobianMode enum is well-documented and provides a sensible hybrid approach combining analytic Jacobians where available with autodiff fallback. The docstrings clearly explain the behavior of each mode.
107-150: LGTM - Comprehensive parameter documentation.The init method documentation has been significantly improved with detailed parameter descriptions and clear notes about the batch structure. The documentation style is consistent and informative.
201-222: LGTM - Proper objective initialization with mode-aware buffer allocation.The
_init_objectivesmethod correctly handles the new MIXED mode by determining the appropriate Jacobian mode for each objective based on whether it supports analytic computation. The logic is sound and the implementation is clean.
276-313: LGTM - Well-documented compute_residuals method.The enhanced documentation clearly explains the parameters, return values, and behavior of the method. The implementation correctly handles both autodiff/mixed and analytic modes with appropriate forward kinematics computation.
315-388: LGTM - Robust MIXED mode implementation in compute_jacobian.The MIXED mode implementation is well-structured:
- Efficiently checks which objectives need each type of computation
- Properly sets up autodiff tape only when needed
- Correctly computes motion subspace only when needed
- Iterates through objectives using the appropriate method for each
The logic handles all three modes (AUTODIFF, ANALYTIC, MIXED) correctly.
919-1063: LGTM - Comprehensive IKObjective base class documentation.The extensive documentation for the IKObjective base class is excellent:
- Clearly describes the purpose and role of IK objectives
- Documents all required and optional methods
- Provides detailed parameter descriptions
- Explains the contract that subclasses must implement
This greatly improves the API usability for developers creating custom objectives.
1262-1296: LGTM - Valuable per-problem target update APIs.The addition of
set_target_positionandset_target_positionsmethods provides essential runtime flexibility for IK problems. The implementation is efficient using GPU kernels and the documentation clearly explains the usage.
1918-1952: LGTM - Consistent rotation target update APIs.The
set_target_rotationandset_target_rotationsmethods mirror the position objective implementation perfectly, providing consistent API design across objective types.
1209-1254: LGTM - Efficient buffer initialization with kinematic analysis.The
init_buffersmethod in IKPositionObjective demonstrates sophisticated implementation:
- For analytic mode: performs kinematic tree traversal to determine which DoFs affect the end-effector
- For autodiff mode: pre-allocates one-hot vectors for efficient gradient extraction
- Uses numpy for CPU computations then transfers to GPU arrays
The kinematic analysis logic correctly traces ancestors in the joint hierarchy.
1545-1573: LGTM - Proper DoF-to-coordinate mapping for joint limits.The
init_buffersmethod in IKJointLimitObjective correctly handles the mapping between DoF indices and coordinate indices, which is essential for joint types where these don't align 1:1 (as noted in the retrieved learnings about the Newton IK system design).newton/_src/utils/recorder_gui.py (5)
22-29: Documentation looks good and comprehensiveThe expanded class docstring clearly describes the purpose and functionality of the RecorderImGuiManager, including its role in visualization and control of simulation playback.
31-41: Detailed initialization documentationThe docstring now provides clear parameter descriptions with proper type hints and defaults, making the API easy to understand.
53-59: Clear documentation for internal methodThe docstring appropriately documents this private method's purpose and behavior for future maintainers.
65-85: Functional improvements with proper documentationThe expanded logic for handling paused state with transforms and point clouds is well-documented. The method now properly consumes recorder playback results when paused, applying transforms and rendering contact points as needed.
86-91: Well-documented public interface methodThe docstring clearly describes the UI drawing functionality, making it easy for users to understand what this method does.
newton/_src/viewer/viewer_null.py (6)
22-34: Comprehensive class documentationThe new docstring clearly explains the purpose of ViewerNull as a headless, non-rendering viewer for automated environments, with well-documented attributes.
36-42: Clear initialization documentationThe docstring properly documents the parameter and its purpose.
46-46: Frame tracking implementation looks correctThe addition of
frame_countinitialization enables proper frame-limited operation as described in the documentation.
95-99: Proper frame counting implementationThe
end_framemethod correctly increments the frame counter, enabling the frame-limited behavior.
101-108: Correct frame limit checkThe
is_runningmethod properly checks against the frame limit, consistent with other viewer implementations.
48-161: Comprehensive documentation for all no-op methodsAll logging and lifecycle methods now have clear docstrings explaining their no-op behavior and parameter meanings, which is helpful for API consistency.
newton/_src/sim/state.py (3)
21-43: Excellent high-level class documentationThe expanded docstring provides a clear overview of the State class's role, proper usage via Model.state(), and comprehensive attribute documentation with shapes and dtypes.
46-75: Consistent and precise attribute documentationAll attribute docstrings now follow a consistent format with shape and dtype information. The descriptions are more precise, especially for body transforms (7-DOF) and spatial vectors with explicit angular/linear component ordering.
77-82: Clear documentation for clear_forces methodThe expanded docstring explicitly states the behavior of clearing particle_f and body_f arrays.
newton/_src/viewer/viewer_usd.py (8)
14-21: Well-structured class documentationThe docstring clearly explains the USD viewer's role in creating USD stages with instanced rendering and time-sampled transforms.
23-35: Comprehensive initialization documentationThe docstring provides clear parameter descriptions and properly documents the ImportError that may be raised.
67-91: Improved type hints and documentation for log_meshThe addition of type hints for arrays and the comprehensive docstring with parameter descriptions and return value documentation improves API clarity.
260-275: Proper frame counting implementationThe begin_frame method now correctly increments
_frame_countand updates the USD stage end time as needed, enabling frame-limited recording.
284-293: Correct frame limit check for USD viewerThe is_running method properly checks the frame count against the limit, consistent with other viewer implementations.
130-144: Clear error handling documentationThe docstring properly documents the RuntimeError that may be raised if the mesh prototype is not found.
305-335: Consistent documentation for unimplemented methodsAll placeholder methods have clear docstrings indicating they are not implemented for the USD backend.
338-349: Well-documented utility methodThe _ensure_scopes_for_path static method has comprehensive documentation explaining its purpose and behavior.
newton/_src/sim/collide.py (8)
33-45: Clear function signature and documentationThe addition of explicit return type annotation and comprehensive docstring improves API clarity.
65-90: Comprehensive CollisionPipeline class documentationThe new class docstring clearly describes the responsibilities and attributes of the CollisionPipeline, making it easy to understand its role in the simulation.
92-125: Detailed initialization documentationThe docstring provides comprehensive parameter descriptions with defaults and types, making the API clear and easy to use.
139-139: Comment improvementThe updated comment "Allocate buffers for broadphase collision handling" is more descriptive than the previous version.
156-184: Well-documented factory methodThe from_model classmethod has clear documentation explaining how it constructs a CollisionPipeline from a Model with appropriate parameter descriptions.
206-219: Clear collide method documentationThe docstring explains the method's purpose, behavior regarding buffer allocation, and return value.
346-354: Clear property documentationThe device property has appropriate documentation explaining what it returns.
357-360: Proper module exportsThe all list correctly exports the new CollisionPipeline class alongside the existing function.
newton/_src/sim/builder.py (2)
777-810: Nice addition: clear usage docs for replicate()Documentation upgrades are thorough and actionable. No code issues here.
648-656: LGTM on up_vector docs and read-only enforcementClearer docs and explicit setter error improve UX.
Signed-off-by: camevor <camevor@nvidia.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
newton/_src/sim/builder.py (1)
3921-3937: new_bodies=None raises TypeError; doc says optionalThe code calls set(new_bodies) unconditionally. Passing None (the default) will raise. Either require it or accept None. Since the doc says optional, normalize None to “consider all bodies”.
def add_free_joints_to_floating_bodies(self, new_bodies: Iterable[int] | None = None): @@ - # set(self.joint_child) is connected_bodies - floating_bodies = set(new_bodies) - set(self.joint_child) + # If not provided, consider all current bodies + if new_bodies is None: + new_bodies = range(self.body_count) + # set(self.joint_child) is connected_bodies + floating_bodies = set(new_bodies) - set(self.joint_child)
♻️ Duplicate comments (4)
newton/_src/sim/contacts.py (1)
24-60: Docstring expansion looks good; keeping Sphinx notes is appropriateThe richer attribute documentation and the explicit “.. note::” block read well and are consistent with our docs style.
newton/_src/sim/builder.py (2)
3621-3645: Jitter distribution is biased; radius can go negative under Gaussian noise
- rng.random(3) ∈ [0,1) yields one-sided jitter; positions are biased.
- radius_mean + N(0,σ) can become negative.
If you want to keep this PR doc-only, at least document that jitter is one-sided and radii are unclamped. Otherwise, consider this minimal fix:
- rng = np.random.default_rng(42) + rng = np.random.default_rng(42) @@ - p = wp.quat_rotate(rot, v) + pos + wp.vec3(rng.random(3) * jitter) + # symmetric jitter in [-jitter, +jitter] + jitter_vec = (rng.random(3) * 2.0 - 1.0) * jitter + p = wp.quat_rotate(rot, v) + pos + wp.vec3(*jitter_vec) @@ - if radius_std > 0.0: - r = radius_mean + rng.standard_normal() * radius_std - else: - r = radius_mean + if radius_std > 0.0: + r = float(radius_mean + rng.standard_normal() * radius_std) + r = max(0.0, r) # clamp + else: + r = float(radius_mean)Also applies to: 3648-3661
4341-4387: Empty contact-pairs edge case: wp.vec2i requires Nx2 inputWhen no pairs exist, np.array(contact_pairs) has shape (0,), which doesn’t map to vec2i. Create a shape-(0,2) array before wrapping.
- model.shape_contact_pairs = wp.array(np.array(contact_pairs), dtype=wp.vec2i, device=model.device) - model.shape_contact_pair_count = len(contact_pairs) + if contact_pairs: + arr = np.asarray(contact_pairs, dtype=np.int32).reshape(-1, 2) + else: + arr = np.empty((0, 2), dtype=np.int32) + model.shape_contact_pairs = wp.array(arr, dtype=wp.vec2i, device=model.device) + model.shape_contact_pair_count = int(arr.shape[0])newton/_src/sim/ik.py (1)
120-150: Type in constructor docs: use newton.Model consistentlySome places say “Articulation” and others “newton.Model”. The code expects a Model with articulation data. Please unify to newton.Model here to match the class-level docs.
- model : Articulation - The shared articulation model for all IK problems in the batch. + model : newton.Model + The shared model/articulation for all IK problems in the batch.
🧹 Nitpick comments (8)
newton/_src/sim/contacts.py (2)
100-114: clear() doc/code mismatch; some indices aren’t reset to -1Doc says “resetting counts and filling indices with -1,” but rigid_contact_point_id isn’t filled, and soft/rigid position/normal buffers aren’t zeroed consistently. Either narrow the docstring or reset additional fields.
Suggested minimal code change to align with the doc (safe and cheap):
def clear(self): """ Clear all contact data, resetting counts and filling indices with -1. """ self.rigid_contact_count.zero_() + self.rigid_contact_point_id.fill_(-1) self.rigid_contact_shape0.fill_(-1) self.rigid_contact_shape1.fill_(-1) self.rigid_contact_tids.fill_(-1) self.rigid_contact_force.zero_() self.soft_contact_count.zero_() self.soft_contact_particle.fill_(-1) self.soft_contact_shape.fill_(-1) self.soft_contact_tids.fill_(-1)
86-94: Nit: dtype consistency for soft-contact index arrayssoft_contact_particle/shape/tids use dtype=int, while rigid equivalents use wp.int32. Consider standardizing on wp.int32 for cross-device consistency and to match the docs style used elsewhere.
newton/_src/sim/builder.py (5)
3398-3412: Parameter name typo in docs: dim_x_ → dim_xThe docstring lists “dim_x_” but the signature uses “dim_x”. This can confuse users and Sphinx/autodoc cross-refs.
- dim_x_: The number of rectangular cells along the x-axis + dim_x: The number of rectangular cells along the x-axis
3434-3438: Bug: total_mass formula uses dim_x twice; should include dim_yThe cloth grid total particle count is (dim_x + 1) * (dim_y + 1). Using dim_x twice underestimates/overestimates density.
- total_mass = mass * (dim_x + 1) * (dim_x + 1) + total_mass = mass * (dim_x + 1) * (dim_y + 1)
2416-2433: Plane-from-equation sign bug: position should be -d·nFor plane a·x + b·y + c·z + d = 0, a point on the plane is x0 = -d·n̂, not d·n̂.
- pos = plane[3] * normal + pos = -plane[3] * normal
3818-3827: Density unit in soft mesh docs should be per-volume, not per-areaTetrahedral mass accumulation uses volume. Please correct the parameter description:
- density: The density per-area of the mesh + density: The density per-volume of the mesh
3962-3973: Parameter name mismatches in color() docDoc lists include_bending_energy, target_max_min_color_ratio “the color balancing algorithm…”, and “algorithm”, while the signature is include_bending, target_max_min_color_ratio, coloring_algorithm. Align names to signature to avoid confusion.
- include_bending_energy: Whether to consider bending energy ... + include_bending: Whether to consider bending energy ... @@ - algorithm: Value should be an enum type of ColoringAlgorithm, ... + coloring_algorithm: Value should be an enum type of ColoringAlgorithm, ...newton/_src/sim/ik.py (1)
94-103: _cache type annotation doesn’t match the actual keyKey includes (n_dofs, n_residuals, arch), but the type is dict[tuple[int, int], type]. Update the annotation to avoid static check noise.
- _cache: ClassVar[dict[tuple[int, int], type]] = {} + _cache: ClassVar[dict[tuple[int, int, object], type]] = {}
📜 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.
📒 Files selected for processing (5)
newton/_src/sim/articulation.py(1 hunks)newton/_src/sim/builder.py(10 hunks)newton/_src/sim/contacts.py(3 hunks)newton/_src/sim/ik.py(24 hunks)newton/_src/sim/joints.py(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- newton/_src/sim/articulation.py
- newton/_src/sim/joints.py
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-07-21T19:11:04.077Z
Learnt from: dylanturpin
PR: newton-physics/newton#450
File: newton/sim/ik.py:1286-1296
Timestamp: 2025-07-21T19:11:04.077Z
Learning: In Newton's IK system, the JointLimitObjective class intentionally only handles joints where DOF count equals coordinate count (1-1 mapping). This is appropriate because joint limits are typically only meaningful for simple joint types like revolute and prismatic joints, not complex joints like ball or free joints where DOF count != coordinate count.
Applied to files:
newton/_src/sim/ik.py
📚 Learning: 2025-08-12T05:17:34.423Z
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Applied to files:
newton/_src/sim/builder.py
🧬 Code graph analysis (2)
newton/_src/sim/ik.py (2)
newton/_src/sim/contacts.py (1)
device(116-120)newton/_src/sim/collide.py (1)
device(347-354)
newton/_src/sim/builder.py (1)
newton/_src/sim/state.py (2)
body_count(100-104)particle_count(107-111)
⏰ 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/_src/sim/builder.py (2)
777-810: Replicate() doc improvements read wellThe updated description, example, and notes are clear and match the implementation.
1785-1797: Clarify allowed “scalar” joint types in equality constraint docsUpdating this to “prismatic and revolute” improves accuracy for scalar joints. Looks good.
newton/_src/sim/ik.py (3)
315-333: Compute Jacobian docs are clear and match the three backendsNo issues spotted with the description of AUTODIFF/ANALYTIC/MIXED behavior.
1162-1223: IKPositionObjective buffer-init docs and logic look solidAnalytic path precomputes affects_dof; autodiff path initializes per-component e-arrays. This aligns with the solver’s usage.
1963-1995: Rotation residuals and canonicalization docs match the implementationAxis-angle residual behavior and canonicalization flag are clearly documented and implemented as described.
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
newton/_src/sim/ik.py (1)
232-233: Fix potential bug withoroperator on arrays.Using
orwith Warp/NumPy arrays will raise an ambiguous truth value error. Use explicit None checks instead.- joint_q = joint_q or self.joint_q - output_residuals = output_residuals or self.residuals + if joint_q is None: + joint_q = self.joint_q + if output_residuals is None: + output_residuals = self.residuals
🧹 Nitpick comments (2)
newton/_src/sim/ik.py (2)
134-135: Consider validating jacobian_mode parameter.The code assumes
jacobian_modeis one of the valid enum values. Consider adding validation to provide a clearer error message if an invalid mode is passed.grad = jacobian_mode in (IKJacobianMode.AUTODIFF, IKJacobianMode.MIXED) + +# Validate jacobian_mode +if not isinstance(jacobian_mode, IKJacobianMode): + raise ValueError(f"jacobian_mode must be an IKJacobianMode enum value, got {type(jacobian_mode)}")
1057-1091: Complex buffer initialization could benefit from helper methods.The buffer initialization logic for computing affected DOFs is duplicated between
IKPositionObjectiveandIKRotationObjective. Consider extracting this into a shared helper method.def _compute_affected_dofs(model, link_index, device): """Helper to compute which DOFs affect a given link.""" joint_qd_start_np = model.joint_qd_start.numpy() dof_to_joint_np = np.empty(joint_qd_start_np[-1], dtype=np.int32) for j in range(len(joint_qd_start_np) - 1): dof_to_joint_np[joint_qd_start_np[j] : joint_qd_start_np[j + 1]] = j # ... rest of the logic ... return wp.array(affects_dof_np.astype(np.uint8), device=device)
📜 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.
📒 Files selected for processing (1)
newton/_src/sim/ik.py(7 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
⏰ 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). (3)
- 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/_src/sim/ik.py (6)
25-38: LGTM! Well-structured enum with clear documentation.The
IKJacobianModeenum is properly designed with descriptive docstrings for each mode. TheMIXEDmode provides a nice hybrid approach that can optimize performance while maintaining flexibility.
51-74: Documentation is comprehensive and well-formatted.The Args section clearly documents all parameters with types and default values. The batch structure explanation is particularly helpful for understanding the solver's parallelization approach.
836-930: Excellent abstract base class design for IK objectives.The
IKObjectiveclass provides a well-thought-out interface with clear separation between required and optional methods. The documentation thoroughly explains the purpose of each method and the batching paradigm.
1095-1112: Well-designed per-problem target update API.The new methods
set_target_positionandset_target_positionsprovide a clean interface for updating targets either individually or in batch, which is essential for dynamic IK scenarios.
1554-1571: Consistent API design for rotation target updates.The
set_target_rotationandset_target_rotationsmethods mirror the position objective's API, maintaining consistency across objective types.
280-304: MIXED mode implementation is elegant and efficient.The MIXED mode cleverly combines both autodiff and analytic approaches, only computing what's needed for each objective type. This provides optimal performance while maintaining flexibility.
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
newton/_src/sim/builder.py (5)
1316-1336: Doc refers to non-existent defaults; point todefault_joint_cfg.*instead.
default_joint_control_mode,default_joint_limit_*, anddefault_joint_armaturearen’t defined. The implementation pulls all these fromdefault_joint_cfg. Update the docs to match the code.Doc-only diff:
- mode: The control mode of the joint. If None, the default value from :attr:`default_joint_control_mode` is used. - limit_lower: The lower limit of the joint. If None, the default value from :attr:`default_joint_limit_lower` is used. - limit_upper: The upper limit of the joint. If None, the default value from :attr:`default_joint_limit_upper` is used. - limit_ke: The stiffness of the joint limit. If None, the default value from :attr:`default_joint_limit_ke` is used. - limit_kd: The damping of the joint limit. If None, the default value from :attr:`default_joint_limit_kd` is used. - armature: Artificial inertia added around the joint axis. If None, the default value from :attr:`default_joint_armature` is used. + mode: The control mode of the joint. If None, defaults to :attr:`default_joint_cfg.mode`. + limit_lower: The lower limit of the joint. If None, defaults to :attr:`default_joint_cfg.limit_lower`. + limit_upper: The upper limit of the joint. If None, defaults to :attr:`default_joint_cfg.limit_upper`. + limit_ke: The stiffness of the joint limit. If None, defaults to :attr:`default_joint_cfg.limit_ke`. + limit_kd: The damping of the joint limit. If None, defaults to :attr:`default_joint_cfg.limit_kd`. + armature: Artificial inertia added around the joint axis. If None, defaults to :attr:`default_joint_cfg.armature`.
1404-1417: Same doc issue as revolute: referencedefault_joint_cfg.*.The prismatic joint doc references undefined attributes and should mirror the revolute fix.
Doc-only diff:
- mode: The control mode of the joint. If None, the default value from :attr:`default_joint_control_mode` is used. - limit_lower: The lower limit of the joint. If None, the default value from :attr:`default_joint_limit_lower` is used. - limit_upper: The upper limit of the joint. If None, the default value from :attr:`default_joint_limit_upper` is used. - limit_ke: The stiffness of the joint limit. If None, the default value from :attr:`default_joint_limit_ke` is used. - limit_kd: The damping of the joint limit. If None, the default value from :attr:`default_joint_limit_kd` is used. - armature: Artificial inertia added around the joint axis. If None, the default value from :attr:`default_joint_armature` is used. + mode: The control mode of the joint. If None, defaults to :attr:`default_joint_cfg.mode`. + limit_lower: The lower limit of the joint. If None, defaults to :attr:`default_joint_cfg.limit_lower`. + limit_upper: The upper limit of the joint. If None, defaults to :attr:`default_joint_cfg.limit_upper`. + limit_ke: The stiffness of the joint limit. If None, defaults to :attr:`default_joint_cfg.limit_ke`. + limit_kd: The damping of the joint limit. If None, defaults to :attr:`default_joint_cfg.limit_kd`. + armature: Artificial inertia added around the joint axis. If None, defaults to :attr:`default_joint_cfg.armature`.
1664-1665: Remove “armature” from D6 doc (not in signature).
add_joint_d6()does not accept anarmatureparameter; the doc lists one. Remove to avoid confusion.Doc-only diff:
- armature: Artificial inertia added around the joint axes. If None, the default value from :attr:`default_joint_armature` is used.
3393-3393: Parameter name typos and density meaning in cloth/soft grid docs.
dim_x_should bedim_x(both places).- In
add_soft_grid(), “density of each particle” is misleading; code uses volumetric density to derive per-node mass ascell_x*cell_y*cell_z*density.Doc-only diffs:
- dim_x_: The number of rectangular cells along the x-axis + dim_x: The number of rectangular cells along the x-axis- density: The density of each particle + density: Volumetric density (mass per unit volume). Per-node mass is computed as cell_x * cell_y * cell_z * density.Also applies to: 3689-3696
3954-3963: Parameter name mismatch:include_bendingvs.include_bending_energy.The function parameter is
include_bending; the doc usesinclude_bending_energy.Doc-only diff:
- include_bending_energy: Whether to consider bending energy for trimeshes in the coloring process. If set to `True`, the generated + include_bending: Whether to consider bending energy for trimeshes in the coloring process. If set to `True`, the generated
♻️ Duplicate comments (3)
newton/_src/sim/builder.py (3)
3612-3636: Clarify jitter distribution and radius sampling behavior in docs (no code change).
- Jitter is sampled per-axis as uniform in [0, jitter) and added in world space; it’s not symmetric.
- Radii sampled with
radius_std > 0are not clamped; negative values are possible with large std.Doc-only diff:
- jitter (float): Maximum random offset to apply to each particle position. + jitter (float): Per-axis uniform random offset added in world space; sampled in [0, jitter). + Set to 0.0 for a perfect lattice. @@ - radius_std (float, optional): Standard deviation for particle radii. If > 0, radii are sampled from a normal distribution. + radius_std (float, optional): Standard deviation for particle radii. If > 0, radii are sampled + from a normal distribution without clamping; ensure chosen mean/std produce positive radii.
4376-4378: Handle empty contact-pair list to avoid shape mismatch withwp.vec2i.When no pairs exist,
np.array(contact_pairs)has shape(0,), which won’t map cleanly towp.vec2i. Create an empty(0, 2)array.Apply this code diff:
- model.shape_contact_pairs = wp.array(np.array(contact_pairs), dtype=wp.vec2i, device=model.device) - model.shape_contact_pair_count = len(contact_pairs) + if contact_pairs: + arr = np.asarray(contact_pairs, dtype=np.int32).reshape(-1, 2) + else: + arr = np.empty((0, 2), dtype=np.int32) + model.shape_contact_pairs = wp.array(arr, dtype=wp.vec2i, device=model.device) + model.shape_contact_pair_count = int(arr.shape[0])
3913-3921: Doc/code mismatch foradd_free_joints_to_floating_bodies(new_bodies).Doc advertises
Iterable[int] | None, but passingNonewill raise aTypeErroratset(new_bodies). Since this PR is docs-only, tighten the doc to require an iterable, or (in a follow-up PR) explicitly handle/raise onNone.Doc-only fix now:
- new_bodies (Iterable[int] or None, optional): The set of body indices to consider for adding free joints. + new_bodies (Iterable[int]): The set of body indices to consider for adding free joints.Option for a future code change (if desired later):
def add_free_joints_to_floating_bodies(self, new_bodies: Iterable[int] | None = None): @@ - floating_bodies = set(new_bodies) - set(self.joint_child) + if new_bodies is None: + raise ValueError("new_bodies must be provided; pass the set of newly added body indices.") + floating_bodies = set(new_bodies) - set(self.joint_child)
🧹 Nitpick comments (11)
newton/_src/geometry/types.py (7)
89-97: Resolve potentially unresolved type name in docs (“Mat33”).The docstring refers to “Mat33”, which isn’t imported here. Use a resolvable symbol (e.g., wp.mat33) so intersphinx/autodoc can link it.
- I (Mat33, optional): 3x3 inertia matrix. Defaults to identity. + I (wp.mat33, optional): 3x3 inertia matrix. Defaults to identity.
108-113: Document the precondition for finalize() when volume is None.Right now this will error if volume is None. Make that explicit so users aren’t surprised. (Implementation guard can come in a separate PR.)
Returns: wp.uint64: The unique identifier of the SDF volume. + + Note: + Requires a non-None ``volume``. If ``volume`` is None, this method will error.
122-156: Tighten Mesh class docs: “Examples” section name, attribute shapes, and linkable types.
- Use “Examples:” header for Napoleon.
- Clarify index array shape as (T*3,) rather than “(3 per triangle)”.
- Prefer wp.mat33 for linkable type.
- Example: + Examples: @@ - Attributes: + Attributes: vertices (np.ndarray): Mesh 3D vertex positions, shape (N, 3). - indices (np.ndarray): Flattened triangle indices, shape (3 per triangle). + indices (np.ndarray): Flattened triangle indices, shape (T*3,) where T is the number of triangles. normals (np.ndarray or None): Optional per-vertex normals, shape (N, 3). uvs (np.ndarray or None): Optional per-vertex UV coordinates, shape (N, 2). - I (Mat33): 3x3 inertia matrix of the mesh (about the center of mass, assuming density 1.0). + I (wp.mat33): 3x3 inertia matrix of the mesh (about the center of mass, assuming density 1.0).
169-185: Make default for maxhullvert reference the constant to avoid drift.Docs currently hard-code “64”. Referencing the constant is more robust if the value changes.
- maxhullvert (int, optional): Max vertices for convex hull approximation (default: 64). + maxhullvert (int, optional): Max vertices for convex hull approximation (default: MESH_MAXHULLVERT).
258-266: Clarify finalize() side effects and defaults.
- Mention that self.mesh is populated.
- Call out default device behavior when device is None.
- Clarify that requires_grad applies to points/velocities only.
- Construct a simulation-ready Warp Mesh object from the mesh data and return its ID. + Construct a simulation-ready Warp Mesh object from the mesh data, store it in ``self.mesh``, and return its ID. @@ - device (Devicelike, optional): Device on which to allocate mesh buffers. - requires_grad (bool, optional): If True, mesh points and velocities are allocated with gradient tracking. + device (Devicelike, optional): Device on which to allocate mesh buffers. If ``None``, uses the current Warp device. + requires_grad (bool, optional): If True, mesh points and velocities are allocated with gradient tracking (indices remain integer).
277-285: Reference maxhullvert in convex hull docs.Readers will appreciate knowing how the hull complexity is controlled.
- Compute and return the convex hull of this mesh. + Compute and return the convex hull of this mesh. The number of hull vertices is capped by ``self.maxhullvert``.
26-31: Consolidate GeoType member docs into the class docstring or enable enum-member autodocChecked the current Sphinx setup and discovered that:
- No enum-aware extension (e.g.
autoenum,enum_tools,sphinxcontrib.enum) is enabled indocs/conf.py- The generated RST (
docs/api/_generated/newton.GeoType.rst) useswithout a.. autoclass:: newton.GeoType:members:option, so bare string literals after each enum member will never be renderedTo ensure your per-member documentation isn’t lost, you can choose one of:
• Fold descriptions into the class docstring (simplest) and remove all no-op literals:
class GeoType(enum.IntEnum): - """ - Enumeration of geometric shape types supported in Newton. - - Each member represents a different primitive or mesh-based geometry - that can be used for collision, rendering, or simulation. - """ + """ + Enumeration of geometric shape types supported in Newton. + + Each member represents a different primitive or mesh-based geometry + that can be used for collision, rendering, or simulation. + + Members: + - PLANE: Infinite plane + - HFIELD: Height field (terrain) + - SPHERE: Sphere + - CAPSULE: Capsule (cylinder with hemispherical ends) + - ELLIPSOID: Ellipsoid + - CYLINDER: Cylinder + - BOX: Axis-aligned box + - MESH: Triangle mesh + - SDF: Signed distance field + - CONE: Cone + - NONE: No geometry (placeholder) + """ PLANE = 0 - """Infinite plane.""" HFIELD = 1 - """Height field (terrain).""" SPHERE = 2 - """Sphere.""" CAPSULE = 3 - """Capsule (cylinder with hemispherical ends).""" ELLIPSOID = 4 - """Ellipsoid.""" CYLINDER = 5 - """Cylinder.""" BOX = 6 - """Axis-aligned box.""" MESH = 7 - """Triangle mesh.""" SDF = 8 - """Signed distance field.""" CONE = 9 - """Cone.""" NONE = 10 - """No geometry (placeholder)."""• Enable enum-member autodoc by adding an enum extension and/or members flags:
– In
docs/conf.py, add toextensions:
python extensions += [ "sphinxcontrib.enum", # or another enum-autodoc extension ]
– Either set inconf.py:
python autodoc_default_options = { 'members': True, 'undoc-members': True, }
or update the generated RST:
diff .. autoclass:: newton.GeoType + :members: + :undoc-members:Choose the approach that fits your doc-build workflow.
newton/_src/sim/builder.py (4)
313-315: Fix doc mismatch:shape_material_kais adhesion distance, not “area stiffness”.The ShapeConfig docstring correctly defines
kaas “contact adhesion distance”, but the init Attributes section lists “area stiffness,” which is confusing.Apply this doc-only diff:
- shape_material_ka (list): Shape material area stiffness. + shape_material_ka (list): Contact adhesion distance for shape contacts.
363-364: Clarifyjoint_parentsmapping semantics.This maps from “child body -> list of parent bodies,” not “joint -> parent bodies.”
Doc-only fix:
- joint_parents (dict): Mapping from joint to parent bodies. + joint_parents (dict): Mapping from child body index to its immediate parent body indices (used for collision-filter inheritance).
1776-1789: Document enforcement of “scalar joints only” or relax the claim.The doc says only revolute/prismatic are allowed, but the function does not validate this. If enforcement happens downstream, say so; otherwise, consider softening the claim.
Doc-only tweak (if enforcement is downstream):
- Constrains the position or angle of one joint to be a quartic polynomial of another joint. Only scalar joint types (prismatic and revolute) can be used. + Constrains the position or angle of one joint to be a quartic polynomial of another joint. + This constraint is intended for scalar joints (revolute or prismatic). The builder does not validate types; + solvers may raise or ignore invalid combinations.
4332-4347: Document environment-group semantics of contact pairs.If cross-environment collisions are intentionally included (group -1 vs. per-env), spell this out here. If not, consider filtering by
shape_groupto avoid cross-env pairs. For this docs-only PR, at least clarify intent.Doc-only addition example:
- The resulting contact pairs are stored in the model as a 2D array of shape indices. + The resulting contact pairs are stored in the model as a 2D array of shape indices. + Note: Contact pairs are generated per collision group and include interactions with the global group (-1). + Environment groups are not filtered here; downstream collision stages may apply per-environment filtering.
📜 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.
📒 Files selected for processing (3)
newton/_src/geometry/types.py(8 hunks)newton/_src/sim/builder.py(10 hunks)newton/_src/sim/state.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- newton/_src/sim/state.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-08-12T05:17:34.423Z
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Applied to files:
newton/_src/geometry/types.pynewton/_src/sim/builder.py
🔇 Additional comments (3)
newton/_src/geometry/types.py (3)
72-86: LGTM: SDF class docs are clear and aligned with current behavior.
212-222: LGTM: copy() docstring reads well and matches behavior.
307-314: LGTM: Hashing docs accurately describe the cache and inputs.
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
newton/_src/sim/builder.py (2)
3898-3908: Doc/code mismatch:new_bodiescannot be None as implementedDocstring says “Iterable[int] or None”, but
set(new_bodies)will raise whennew_bodies is None. Either:
- Update the doc to require a non-None iterable (quick fix for this docs-only PR), or
- Add a small guard and raise a clear
ValueError(preferred long-term).Doc-only fix (recommended for this PR):
- new_bodies (Iterable[int] or None, optional): The set of body indices to consider for adding free joints. + new_bodies (Iterable[int]): The set of body indices to consider for adding free joints. Must not be None.Optional future code tweak for clarity:
def add_free_joints_to_floating_bodies(self, new_bodies: Iterable[int] | None = None): @@ - # set(self.joint_child) is connected_bodies - floating_bodies = set(new_bodies) - set(self.joint_child) + if new_bodies is None: + raise ValueError("new_bodies must be provided; pass the set of newly added body indices.") + # set(self.joint_child) is connected_bodies + floating_bodies = set(new_bodies) - set(self.joint_child)
4318-4333: Empty-contact-pairs edge case: ensure(0, 2)shape forwp.vec2iarrayWhen no shape pairs are found,
np.array(contact_pairs)yields shape(0,), which does not map cleanly towp.vec2i. Create an empty(0, 2)array to avoid runtime issues. The docs mention storing a 2D array; this change aligns code with that contract.Apply this diff:
- model.shape_contact_pairs = wp.array(np.array(contact_pairs), dtype=wp.vec2i, device=model.device) - model.shape_contact_pair_count = len(contact_pairs) + if contact_pairs: + arr = np.asarray(contact_pairs, dtype=np.int32).reshape(-1, 2) + else: + arr = np.empty((0, 2), dtype=np.int32) + model.shape_contact_pairs = wp.array(arr, dtype=wp.vec2i, device=model.device) + model.shape_contact_pair_count = int(arr.shape[0])Also applies to: 4362-4363
🧹 Nitpick comments (4)
newton/_src/sim/builder.py (4)
271-275: Clarify gravity semantics (it’s a signed scalar, not a magnitude)As written, “magnitude … Defaults to -9.81” is contradictory. Suggest tightening wording to reflect the signed scalar applied along
up_vector.Apply this diff:
- up_axis (AxisType, optional): The axis to use as the "up" direction in the simulation. - Defaults to Axis.Z. - gravity (float, optional): The magnitude of gravity to apply along the up axis. - Defaults to -9.81. + up_axis (AxisType, optional): Axis used as the "up" direction in the simulation. Defaults to Axis.Z. + gravity (float, optional): Scalar acceleration applied along the up axis; use a negative value + for downward gravity relative to `up_axis`. Defaults to -9.81.
768-789: Replicate docs: note env/collision grouping and centering behaviorTwo small clarifications will save reader surprises:
- Each copy goes into a distinct environment group (because
add_builder(..., environment=None)advancesnum_envs).- Copies are centered around the non-up axes (ground-plane preserved along the up-axis).
Apply this diff:
""" Replicates the given builder multiple times, offsetting each copy according to the supplied spacing. This method is useful for creating multiple instances of a sub-model (e.g., robots, environments) arranged in a regular grid or along a line. Each copy is offset in space by a multiple of the - specified spacing vector, and all entities from each copy are assigned to a new environment group. + specified spacing vector. + + Notes + ----- + - Environment grouping: each replicated copy is assigned to a new environment group by default. + - Collision grouping: shapes from the replicated builder will also be placed in a new collision + group (mirrors `add_builder(..., separate_collision_group=True)` defaults). + - Centering: offsets are recentred so the set of copies is centred in non-up directions; the + up-axis component is preserved to avoid shifting below the ground plane.
1763-1763: Equality-constraint docs: make intent and support explicitDocument that only scalar joints are intended here, and that other joint types are unsupported.
Apply this diff:
- Constrains the position or angle of one joint to be a quartic polynomial of another joint. Only scalar joint types (prismatic and revolute) can be used. + Constrains the position or angle of one joint to be a quartic polynomial of another joint. + Only scalar joint types (prismatic, revolute) are intended. Passing non‑scalar joints is + unsupported and may be ignored by solvers.
3598-3622:add_particle_griddocs: specify jitter distribution and radius sampling behaviorCurrent implementation samples:
- Jitter per-axis uniformly in [0,
jitter) (positive-only bias).- Radii from a normal distribution with no lower bound clamp.
Make that explicit to prevent incorrect assumptions.
Apply this diff:
- jitter (float): Maximum random offset to apply to each particle position. - radius_mean (float, optional): Mean radius for particles. If None, uses the builder's default. - radius_std (float, optional): Standard deviation for particle radii. If > 0, radii are sampled from a normal distribution. + jitter (float): Per-axis random offset applied uniformly in the range [0, jitter). Set to 0 to disable. + radius_mean (float, optional): Mean radius for particles. If None, uses the builder's default. + radius_std (float, optional): Standard deviation for particle radii. If > 0, radii are sampled + from a normal distribution around `radius_mean` (no lower clamp is applied in code). + Choose values to keep radii positive.
📜 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.
📒 Files selected for processing (1)
newton/_src/sim/builder.py(10 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-08-12T05:17:34.423Z
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Applied to files:
newton/_src/sim/builder.py
🧬 Code graph analysis (1)
newton/_src/sim/builder.py (1)
newton/_src/sim/state.py (2)
body_count(87-91)particle_count(94-98)
🔇 Additional comments (4)
newton/_src/sim/builder.py (4)
71-74: Nice clarity upgrade on the class overview and finalize call pathThe added narrative around “lists unsuitable for simulation” and explicitly calling
finalize()is helpful for users scanning the API.
639-647: Read-onlyup_vectordoc is crispGood, the getter explains behavior and the property is explicitly documented as read-only.
659-725: Count properties: concise and consistentThe short docstrings on the various
*_countproperties improve discoverability without noise.
3973-3992: Finalize docs: improved end-to-end descriptionThe expanded “Args/Returns/Notes” explains the lifecycle and what
finalize()prepares on device memory. This is a solid step for API readers.
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
docs/generate_api.py (2)
99-101: Broaden “constant” detection to include common Python scalars and None.Right now plain Python ints/floats/bools/bytes and None may still fall through to Functions depending on wp.types behavior. Consider folding them into Constants for more predictable docs.
Apply this minimal change to the predicate:
- if wp.types.type_is_value(type(attr)) or isinstance(attr, str): + if ( + wp.types.type_is_value(type(attr)) + or isinstance(attr, (str, int, float, bool, complex, bytes, bytearray)) + or attr is None + ): constants.append(name) continueOptional (if you want Enum values to show under Constants too), add this small extension which requires an import outside this hunk:
# at top-level imports import enum # stdlib- if ( + if ( wp.types.type_is_value(type(attr)) - or isinstance(attr, (str, int, float, bool, complex, bytes, bytearray)) + or isinstance(attr, (str, int, float, bool, complex, bytes, bytearray, enum.Enum)) or attr is None ):
185-196: Escape/quote constant values to avoid reST markup surprises and multiline table breaks.Rendering raw strings (and other objects) can trigger unintended reST formatting (e.g., trailing underscores, backticks) or break list-tables on newlines. Wrap strings as literals and use repr() for non-scalar objects.
for const in constants: value = getattr(module, const, "?") # unpack the warp scalar value, we can remove this # when the warp.types.scalar_base supports __str__() if type(value) in wp.types.scalar_types: value = value.value - lines.extend( - [ - f" * - {const}", - f" - {value}", - ] - ) + # Format the cell safely for reST list-table: + # - Strings as literals, escape backticks/newlines + # - Plain Python scalars as-is + # - Everything else via repr() to avoid unintended markup + if isinstance(value, str): + safe = value.replace("`", "\\`").replace("\n", "\\n") + display_value = f"``{safe}``" + elif isinstance(value, (int, float, bool, complex)): + display_value = value + else: + display_value = repr(value) + + lines.extend( + [ + f" * - {const}", + f" - {display_value}", + ] + )
📜 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.
⛔ Files ignored due to path filters (1)
docs/api/_generated/newton.__version__.rstis excluded by!**/_generated/**
📒 Files selected for processing (3)
docs/api/newton.rst(1 hunks)docs/generate_api.py(1 hunks)newton/_src/geometry/utils.py(1 hunks)
✅ Files skipped from review due to trivial changes (2)
- docs/api/newton.rst
- newton/_src/geometry/utils.py
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
🔇 Additional comments (2)
docs/generate_api.py (2)
99-101: Good call: treating strings as constants fixes misclassification (e.g., version).This aligns the generated docs with expectations by moving string literals out of Functions. Looks good.
211-217: Ensure docs generation prerequisites are in placeBefore running the API generator, make sure your environment has the
warpmodule (and any other developer dependencies) installed. For example:# In your project’s virtualenv: pip install -e . pip install -r requirements-dev.txtThen regenerate the API pages and build the docs with warnings-as-errors to confirm Constants render correctly and no links break:
# Regenerate API pages python docs/generate_api.py # Verify that string constants (e.g. __version__) appear under “Constants” rg -n '__version__' docs/api/newton.rst || { echo "Missing __version__ row"; exit 1; } # Build the HTML docs, treating warnings as errors sphinx-build -nW -b html docs docs/_build/htmlOnce you’ve verified the above steps complete without errors, you can be confident that the Constants section renders cleanly and that no broken references have been introduced.
…odoc Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com>
6651829 to
a8a896f
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
newton/_src/sim/contacts.py (1)
61-67: Fix dtype inconsistencies for soft-contact integer buffersThree soft-contact arrays use Python int instead of an explicit Warp integer dtype. This can cause device/type mismatches in kernels that expect wp.int32. Please standardize to wp.int32 to match the rigid-contact buffers and avoid subtle bugs.
Apply this diff:
- self.soft_contact_particle = wp.full(soft_contact_max, -1, dtype=int) - self.soft_contact_shape = wp.full(soft_contact_max, -1, dtype=int) + self.soft_contact_particle = wp.full(soft_contact_max, -1, dtype=wp.int32) + self.soft_contact_shape = wp.full(soft_contact_max, -1, dtype=wp.int32) self.soft_contact_body_pos = wp.zeros(soft_contact_max, dtype=wp.vec3, requires_grad=requires_grad) self.soft_contact_body_vel = wp.zeros(soft_contact_max, dtype=wp.vec3, requires_grad=requires_grad) self.soft_contact_normal = wp.zeros(soft_contact_max, dtype=wp.vec3, requires_grad=requires_grad) - self.soft_contact_tids = wp.full(soft_contact_max, -1, dtype=int) + self.soft_contact_tids = wp.full(soft_contact_max, -1, dtype=wp.int32)newton/_src/sim/ik.py (1)
679-679: Align Warp integer array dtypes towp.int32Several Warp kernel signatures and helper functions currently declare integer arrays using the bare Python
inttype, which may lead to JIT/type errors. Please update all of these to use the explicit Warp dtypewp.int32for consistency and correctness.Affected locations (replace
dtype=int→dtype=wp.int32):
- newton/_src/sim/ik.py
- Line 678:
joint_dof_dim: wp.array2d(dtype=int)- newton/_src/solvers/style3d/builder.py
- add_connection_wp signature:
counts: wp.array(dtype=int),neighbors: wp.array2d(dtype=int)- Function params at lines 39–43:
edge_inds,neighbors,neighbor_counts- tri_indices at lines 67–68, neighbor_counts at lines 71–73
- assemble_nz_ell_kernel neighbor arrays at lines 256–258
- newton/_src/solvers/xpbd/kernels.py
- Line 365:
indices: wp.array2d(dtype=int)- newton/_src/solvers/implicit_mpm/solver_implicit_mpm.py
- Lines 724–725 and 737–738:
voxels: wp.array2d(dtype=int),colors: wp.array(dtype=int)- newton/_src/solvers/mujoco/solver_mujoco.py
- Line 519:
mj_contact_efc_address: wp.array2d(dtype=int)- newton/_src/solvers/euler/kernels.py
- Lines 87–88, 207–208, 378–379, 453–454: all
indices: wp.array2d(dtype=int)- newton/tests/test_collision.py
- Line 40:
indices: wp.array2d(dtype=int)Example patch snippet:
- joint_dof_dim: wp.array2d(dtype=int), # (n_joints, 2) + joint_dof_dim: wp.array2d(dtype=wp.int32), # (n_joints, 2)newton/_src/viewer/viewer_gl.py (2)
365-381: Paused loop can spin indefinitely after window close; break on exit and avoid busy-wait.If the window is closed while paused, _update() early-outs but the while loop never terminates and will hot-spin. This impacts apps that call end_frame() within their main loop.
Apply this diff:
- # continue running the viewer update while the simulation is paused - while self.is_paused(): - self._update() + # continue updating while paused, but stop if the window is closed + while self.is_paused() and not self.renderer.has_exit(): + self._update() + # small sleep to avoid busy-spin when paused/minimized + time.sleep(0.001)
217-270: Switch clearing logic to use empty arrays rather than NoneThe
LinesGL.updatemethod innewton/_src/viewer/gl/opengl.pyis defined as:def update(self, line_begins, line_ends, line_colors): """Update line data in the VBO.""" ...It does not handle
Nonevalues for its arguments, so callingupdate(None, None, None)will raise a runtime error when uploading to the GPU. To safely clear a line batch, supply zero-length buffers and hide the batch instead:• File: newton/_src/viewer/viewer_gl.py
Location:log_lines, around line 238Suggested diff:
- # Handle empty logs by resetting the LinesGL object - if line_begins is None or line_ends is None or line_colors is None: - if name in self.lines: - self.lines[name].update(None, None, None) - return + # Handle empty logs by resetting with zero-sized arrays and hiding + if line_begins is None or line_ends is None or line_colors is None: + if name in self.lines: + empty = wp.array([], dtype=wp.vec3, device=self.device) + self.lines[name].update(empty, empty, empty) + self.lines[name].hidden = True + returnThis ensures no invalid
Noneuploads and keeps the batch hidden after clearing.
♻️ Duplicate comments (2)
newton/_src/sim/contacts.py (1)
31-33: Keeping Sphinx note directive is aligned with prior feedbackThanks for retaining the .. note:: admonition; this matches the earlier review preference to have such notes stand out in the docs.
newton/_src/sim/ik.py (1)
225-228: Bug: usingorwith Warp/NumPy arrays raises “ambiguous truth value”If callers pass arrays for
joint_q/output_residuals, these lines will error because arrays cannot be used in boolean contexts. Use explicit None checks.Apply this change:
- joint_q = joint_q or self.joint_q - output_residuals = output_residuals or self.residuals + if joint_q is None: + joint_q = self.joint_q + if output_residuals is None: + output_residuals = self.residualsNote: A prior review already flagged this; queuing the fix for a follow-up PR is fine if this one is doc-only.
🧹 Nitpick comments (19)
docs/_templates/class.rst (1)
8-8: De-duplicate:member-order:overrides in class templates
Centralize member ordering in docs/conf.py by removing the:member-order: groupwiselines from all class templates.Affected files:
- docs/_templates/class.rst (line 8)
- docs/_templates/autosummary/class.rst (line 7)
Apply these diffs:
--- docs/_templates/class.rst +++ docs/_templates/class.rst @@ -6,7 +6,6 @@ .. autoclass:: {{ objname }} :members: :inherited-members: - :member-order: groupwise--- docs/_templates/autosummary/class.rst +++ docs/_templates/autosummary/class.rst @@ -5,7 +5,6 @@ .. autoclass:: {{ objname }} :members: :inherited-members: - :member-order: groupwiseWith those overrides removed, the single
member-ordersetting in docs/conf.py will govern all classes, avoiding drift and making future global changes trivial.docs/conf.py (2)
80-80: Nit: fix PyTorch intersphinx URLThe canonical URL is under pytorch.org. Using the non-canonical host can intermittently break cross-refs.
Apply:
- "pytorch": ("https://docs.pytorch.org/docs/stable", None), + "pytorch": ("https://pytorch.org/docs/stable", None),
73-75: Consolidate duplicate exclude_patterns definitionsexclude_patterns is assigned twice; the latter overrides the former. Merge them to a single list to avoid confusion.
Apply:
templates_path = ["_templates"] -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + "sphinx-env/**", + "sphinx-env", + "**/site-packages/**", + "**/lib/**", +] @@ -exclude_patterns = [ - "sphinx-env/**", - "sphinx-env", - "**/site-packages/**", - "**/lib/**", -] +# (moved into the single exclude_patterns definition above)Also applies to: 167-173
newton/_src/sim/contacts.py (4)
35-41: Validate maxima and surface clearer errors on constructionGuard against negative maxima early to prevent silent allocation of zero-length buffers or downstream index errors.
def __init__( self, rigid_contact_max: int, soft_contact_max: int, requires_grad: bool = False, device: Devicelike = None, ): + if rigid_contact_max < 0 or soft_contact_max < 0: + raise ValueError( + f"Contact maxima must be non-negative, got rigid_contact_max={rigid_contact_max}, soft_contact_max={soft_contact_max}" + ) with wp.ScopedDevice(device):
77-87: Consider resetting all per-contact vectors when clearing, or document why stale values are OKclear() resets counts and index-like arrays, but leaves vectors/scalars (points, offsets, normals, thicknesses, body_pos/vel) unchanged. If any consumer assumes zeroed vectors after clear, stale values may leak into computations.
If intentional, add a brief note to the docstring clarifying that only indices are reset and that consumers must gate by contact_count. Otherwise, extend clear() to zero vectors:
self.rigid_contact_force.zero_() + self.rigid_contact_point0.zero_() + self.rigid_contact_point1.zero_() + self.rigid_contact_offset0.zero_() + self.rigid_contact_offset1.zero_() + self.rigid_contact_normal.zero_() + self.rigid_contact_thickness0.zero_() + self.rigid_contact_thickness1.zero_() @@ self.soft_contact_tids.fill_(-1) + self.soft_contact_body_pos.zero_() + self.soft_contact_body_vel.zero_() + self.soft_contact_normal.zero_()
19-21: Unify Devicelike import to project alias for consistencyModel uses Devicelike from ..core.types; Contacts imports from warp.context. Recommend using the same alias source across the codebase.
-import warp as wp -from warp.context import Devicelike +import warp as wp +from ..core.types import Devicelike
88-93: Device property is fine; optional: store device once for readabilityReturning device from a representative array works. For readability and future-proofing, consider caching wp.get_device(device) into self._device during init and returning that here.
newton/_src/sim/model.py (2)
511-523: Collision pipeline lifecycle: verify requires_grad and device assumptionsTwo potential gotchas:
- If an existing self._collision_pipeline was built with a different requires_grad than the current call/model, the code reuses it without adjustment.
- If an external collision_pipeline is passed on a different device than the model/state, this could misallocate Contacts buffers.
Consider rebuilding the pipeline (or error) when requires_grad or device mismatches occur.
Example approach:
if collision_pipeline is not None: - self._collision_pipeline = collision_pipeline + self._collision_pipeline = collision_pipeline + # Optional guard: + if getattr(self._collision_pipeline, "device", None) not in (None, self.device): + raise RuntimeError("CollisionPipeline device mismatch with Model.device") elif not hasattr(self, "_collision_pipeline"): self._collision_pipeline = CollisionPipeline.from_model( self, rigid_contact_max_per_pair, rigid_contact_margin, soft_contact_max, soft_contact_margin, edge_sdf_iter, iterate_mesh_vertices, requires_grad, ) + # If pipeline exists but requires_grad changed, rebuild it (or add a setter if supported). + if getattr(self._collision_pipeline, "requires_grad", requires_grad) != requires_grad: + self._collision_pipeline = CollisionPipeline.from_model( + self, + rigid_contact_max_per_pair, + rigid_contact_margin, + soft_contact_max, + soft_contact_margin, + edge_sdf_iter, + iterate_mesh_vertices, + requires_grad, + )Also applies to: 524-545, 548-570
573-589: Validate frequency argument and error types in add_attributeDocs state the allowed set for frequency, but it’s not enforced. Add a check to prevent invalid values from entering attribute_frequency. Also consider ValueError for invalid inputs (AttributeError is fine for “already exists”).
def add_attribute(self, name: str, attrib: wp.array, frequency: str): @@ - if not isinstance(attrib, wp.array): - raise AttributeError(f"Attribute '{name}' must be an array, got {type(attrib)}") + if not isinstance(attrib, wp.array): + raise ValueError(f"Attribute '{name}' must be a wp.array, got {type(attrib)}") if attrib.device != self.device: - raise AttributeError( + raise ValueError( f"Attribute '{name}' must be on the same device as the Model, expected {self.device}, got {attrib.device}" ) + allowed = {"body", "joint", "joint_coord", "joint_dof", "shape"} + if frequency not in allowed: + raise ValueError(f"Invalid frequency '{frequency}'; expected one of {sorted(allowed)}") @@ setattr(self, name, attrib) self.attribute_frequency[name] = frequencyAlso applies to: 593-599, 600-603
newton/_src/sim/ik.py (6)
47-68: Docstrings: great rewrite; minor dtype/shape notation nits for consistencyTo match conventions used elsewhere in the file and the rest of Newton, suggest using explicit Warp dtypes and dimensionality in the Args section.
Apply this doc-only diff within the class docstring:
- joint_q (wp.array2d(dtype=float)): Shape (n_problems, model.joint_coord_count). Initial joint coordinates, one row per problem. Modified in place. - objectives (Sequence[IKObjective]): Ordered list of objectives shared by all problems. Each `IKObjective` instance can carry arrays of per-problem parameters (e.g., an array of target positions of length `n_problems`). + joint_q (wp.array2d(dtype=wp.float32)): Shape (n_problems, model.joint_coord_count). Initial joint coordinates, one row per problem. Modified in place. + objectives (Sequence[IKObjective]): Ordered list of objectives shared by all problems. Each `IKObjective` instance can carry arrays of per-problem parameters (e.g., a wp.array1d of target positions of length `n_problems`).
864-869: Doc nit: use explicit Warp dtypesUse
wp.float32and explicit dimensionality in the interface docs for consistency.- body_q (wp.array2d(dtype=wp.transform)): Array of body transforms for each problem. - joint_q (wp.array2d(dtype=wp.float)): Array of joint coordinates for each problem. + body_q (wp.array2d(dtype=wp.transform)): Array of body transforms for each problem. + joint_q (wp.array2d(dtype=wp.float32)): Array of joint coordinates for each problem. - residuals (wp.array2d(dtype=wp.float)): Output array for residuals. + residuals (wp.array2d(dtype=wp.float32)): Output array for residuals.
879-882: Doc nit: clarify Jacobian and dq_dof array typesAlign with the rest of the file’s dtype notation.
- jacobian (wp.array3d(dtype=wp.float)): Output array for the Jacobian. + jacobian (wp.array3d(dtype=wp.float32)): Output array for the Jacobian. - dq_dof (wp.array2d(dtype=wp.float)): Derivative of q with respect to DoF. + dq_dof (wp.array2d(dtype=wp.float32)): Derivative of q with respect to DoF.
916-922: Doc nit: analytic path dtype/shapeTiny consistency fix.
- joint_q (wp.array2d(dtype=wp.float)): Array of joint coordinates for each problem. - jacobian (wp.array3d(dtype=wp.float)): Output array for the Jacobian. + joint_q (wp.array2d(dtype=wp.float32)): Array of joint coordinates for each problem. + jacobian (wp.array3d(dtype=wp.float32)): Output array for the Jacobian.
1027-1035: Doc nit: target_positions should be 1DReflect that targets are one per problem.
- target_positions (wp.array(dtype=wp.vec3)): One target position per problem. + target_positions (wp.array1d(dtype=wp.vec3)): One target position per problem.
1474-1485: Doc nit: clarify target_rotations dimensionalityKeep array dimensionality explicit and consistent with position objective.
- target_rotations (wp.array(dtype=wp.vec4)): One target quaternion per problem (stored as vec4). + target_rotations (wp.array1d(dtype=wp.vec4)): One target quaternion per problem (stored as vec4).newton/_src/viewer/viewer_gl.py (4)
318-354: Avoid per-frame allocations in _render_picking_line by reusing tiny buffers.Allocating three new wp.arrays every frame while picking is active adds avoidable GPU/host churn. Cache 1-element arrays and just write values.
Apply this localized diff:
- # Create line data - line_begins = wp.array([com_position], dtype=wp.vec3, device=self.device) - line_ends = wp.array([pick_target], dtype=wp.vec3, device=self.device) - line_colors = wp.array([wp.vec3(0.0, 1.0, 1.0)], dtype=wp.vec3, device=self.device) + # Create or reuse 1-element buffers + if not hasattr(self, "_picking_line_buffers"): + self._picking_line_buffers = { + "beg": wp.empty(1, dtype=wp.vec3, device=self.device), + "end": wp.empty(1, dtype=wp.vec3, device=self.device), + "col": wp.empty(1, dtype=wp.vec3, device=self.device), + } + self._picking_line_buffers["col"].fill_(wp.vec3(0.0, 1.0, 1.0)) + line_begins = self._picking_line_buffers["beg"] + line_ends = self._picking_line_buffers["end"] + line_begins[0] = com_position + line_ends[0] = pick_target
272-291: Normalize log_points inputs to wp.arrays and validate shapes.log_points assumes points/widths/colors are already wp.arrays with compatible lengths. Add light input normalization to reduce caller footguns.
Apply this diff:
if self._point_mesh is None: self._create_point_mesh() if name not in self.objects: self.objects[name] = MeshInstancerGL(len(points), self._point_mesh) - self.objects[name].update_from_points(points, widths, colors) + # Normalize inputs + if not isinstance(points, wp.array): + points = wp.array(points, dtype=wp.vec3, device=self.device) + if not isinstance(widths, wp.array): + widths = wp.array(widths, dtype=float, device=self.device) + if not isinstance(colors, wp.array): + colors = wp.array(colors, dtype=wp.vec3, device=self.device) + assert len(points) == len(widths) == len(colors), "points/widths/colors must have same length" + + self.objects[name].update_from_points(points, widths, colors) self.objects[name].hidden = hidden
473-529: Key mapping covers left modifiers only; consider RSHIFT/RCTRL/RALT for completeness.Optional for parity with common input schemes; not blocking.
142-156: Remove redundant device init and normalize up-axis
- The
deviceattribute is already initialized inViewerBase.__init__(newton/_src/viewer/viewer.py line 36), so reassigning it inset_model()is unnecessary and may introduce confusion.- Ensure that the
up_axispassed toCamerais always one of the strings"X","Y", or"Z", even ifmodel.up_axisis stored as an integer.Apply this diff:
def set_model(self, model): """ Set the Newton model to visualize. Args: model: The Newton model instance. """ - super().set_model(model) + super().set_model(model) self.picking = Picking(model, pick_stiffness=10000.0, pick_damping=1000.0) self.wind = Wind(model) fb_w, fb_h = self.renderer.window.get_framebuffer_size() - self.camera = Camera(width=fb_w, height=fb_h, up_axis=model.up_axis if model else "Z") + # Normalize up_axis to ensure Camera always receives "X"/"Y"/"Z" + if model and isinstance(model.up_axis, int): + up_str = ["X", "Y", "Z"][model.up_axis] + else: + up_str = model.up_axis if model and isinstance(model.up_axis, str) else "Z" + self.camera = Camera(width=fb_w, height=fb_h, up_axis=up_str)
📜 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.
📒 Files selected for processing (14)
docs/_templates/class.rst(1 hunks)docs/conf.py(1 hunks)newton/_src/geometry/types.py(8 hunks)newton/_src/sim/builder.py(10 hunks)newton/_src/sim/collide.py(7 hunks)newton/_src/sim/contacts.py(3 hunks)newton/_src/sim/ik.py(7 hunks)newton/_src/sim/model.py(15 hunks)newton/_src/sim/style3d/builder_style3d.py(2 hunks)newton/_src/utils/contact_sensor.py(3 hunks)newton/_src/utils/gizmo.py(11 hunks)newton/_src/utils/selection.py(2 hunks)newton/_src/viewer/viewer_gl.py(26 hunks)newton/_src/viewer/viewer_null.py(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- newton/_src/utils/gizmo.py
🚧 Files skipped from review as they are similar to previous changes (7)
- newton/_src/viewer/viewer_null.py
- newton/_src/sim/style3d/builder_style3d.py
- newton/_src/utils/selection.py
- newton/_src/sim/collide.py
- newton/_src/sim/builder.py
- newton/_src/utils/contact_sensor.py
- newton/_src/geometry/types.py
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
📚 Learning: 2025-08-12T05:17:34.423Z
Learnt from: shi-eric
PR: newton-physics/newton#521
File: newton/examples/example_cloth_hanging.py:36-36
Timestamp: 2025-08-12T05:17:34.423Z
Learning: The Newton migration guide (docs/migration.rst) is specifically for documenting how to migrate existing warp.sim functionality to Newton equivalents. New Newton-only features that didn't exist in warp.sim do not need migration documentation.
Applied to files:
newton/_src/sim/ik.py
🧬 Code graph analysis (2)
newton/_src/viewer/viewer_gl.py (6)
newton/_src/viewer/viewer_null.py (4)
log_scalar(149-157)begin_frame(82-89)end_frame(91-95)close(106-110)newton/_src/viewer/viewer.py (5)
log_scalar(362-363)log_state(77-99)begin_frame(74-75)end_frame(370-371)close(374-375)newton/_src/viewer/gl/gui.py (3)
begin_frame(233-244)end_frame(246-251)resize(265-269)newton/_src/viewer/wind.py (3)
time(142-144)time(147-149)_apply_wind_force(117-132)newton/_src/viewer/picking.py (2)
_apply_picking_force(58-78)release(83-84)newton/_src/viewer/gl/opengl.py (6)
has_exit(1036-1037)close(1039-1046)get_vsync(1028-1034)set_vsync(1020-1026)is_key_down(1183-1184)resize(1013-1015)
newton/_src/sim/model.py (2)
newton/_src/sim/contacts.py (1)
device(89-93)newton/_src/sim/state.py (3)
requires_grad(78-84)joint_coord_count(101-105)body_count(87-91)
⏰ 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 (ubuntu-latest)
- GitHub Check: run-newton-tests / newton-unittests (windows-latest)
🔇 Additional comments (14)
docs/conf.py (1)
119-119: LGTM: Global autodoc member-order switched to groupwiseThis aligns the docs to grouped member ordering. Once the template override is removed, conf.py becomes the single source of truth.
newton/_src/sim/model.py (5)
30-50: Docstring overhaul reads well and clarifies construction/usageClear guidance on static model semantics, grouping, and ModelBuilder recommendation. Good improvement in discoverability.
76-96: API docs polish across core attributes looks consistentThe added “used by …” hints and group-index explanations will help users navigate solver expectations. No functional issues noted.
Also applies to: 145-152, 184-185, 208-212, 227-233, 279-287, 291-296, 297-304, 313-316, 362-366, 371-374
440-474: state(): cloning semantics are appropriate and coherent with requires_gradCloning only when counts are non-zero and propagating requires_grad from the model by default looks correct.
476-509: control(): clone_variables behavior is well-documented and implementedUsing counts to gate cloning and allowing reference sharing is sensible. No issues.
605-627: get_attribute_frequency(): interface and errors are clearGood, explicit error when unknown; aligns with add_attribute behavior.
newton/_src/sim/ik.py (5)
26-38: Nice: clear IKJacobianMode docs and explicit MIXED semanticsThe added enum and per-member docstrings are crisp and remove ambiguity about back-end selection. No issues.
128-128: Correct requires_grad gating for AD/MIXEDUsing
grad = jacobian_mode in (IKJacobianMode.AUTODIFF, IKJacobianMode.MIXED)correctly scopes tape-enabled allocations to only when autodiff is needed. Looks good.
154-157: Good: allocate motion subspace only when analytically neededConditionally creating
joint_S_swhen not AUTODIFF and at least one objective supports analytic keeps memory use in check. LGTM.
167-176: Per-objective Jacobian mode selection is appropriateIn MIXED mode, passing ANALYTIC vs AUTODIFF to each objective’s
init_buffers()based onsupports_analytic()is the right call and avoids over-allocating buffers. Nice.
212-220: Helpful: solve() doc clarifies side effectsThe Side Effects section calling out in-place updates of
self.joint_qimproves usability. No issues.newton/_src/viewer/viewer_gl.py (3)
36-53: Solid class-level docstring — clear feature inventory and intent.Good high-level overview that aligns with the public viewer docs and helps new users understand capabilities.
128-141: create_sphere_mesh always returns 8-column vertices; no guard neededThe
create_sphere_meshhelper is defined to emit a float32 array of shape (N, 8) ―[x,y,z, nx,ny,nz, u,v]― in both its docstring and implementation, so indexing intovertices[:,3:6]andvertices[:,6:8]is safe as-is. Since there’s no code path that omits normals or UVs, the suggested defensive branches aren’t required at this time. If we later introduce variants that drop normals/UVs, we can revisit and add guards then.
744-816: No change needed for up-axis display
The viewer’s use ofself.model.up_axissafely handles only integer indices because:
- In
ModelBuilder.__init__,self.up_axisis always converted viaAxis.from_any(up_axis)into anAxisIntEnum, even if the user passed in a string.- In
finalize(), that enum value is assigned directly to theModel.up_axisfield.- An
AxisIntEnum is a subclass ofint, so indexing["X","Y","Z"][self.model.up_axis]will never raise anIndexError.No refactoring is required.
Likely an incorrect or invalid review comment.
|
|
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
Signed-off-by: Lukasz Wawrzyniak <lwawrzyniak@nvidia.com> Co-authored-by: Eric Heiden <eheiden@nvidia.com>
Description
Newton Migration Guide
Please ensure the migration guide for warp.sim users is up-to-date with the changes made in this MR.
docs/migration.rstis up-to dateBefore your PR is "Ready for review"
newton/tests/test_examples.py)pre-commit run -aSummary by CodeRabbit
New Features
Improvements
Refactor