Skip to content

Add Newton implicit MPM manager#5697

Closed
maxkra15 wants to merge 7 commits into
isaac-sim:developfrom
maxkra15:max/newton-mpm-manager
Closed

Add Newton implicit MPM manager#5697
maxkra15 wants to merge 7 commits into
isaac-sim:developfrom
maxkra15:max/newton-mpm-manager

Conversation

@maxkra15
Copy link
Copy Markdown

Description

Adds initial Isaac Lab support for Newton implicit MPM simulations.

This PR adds:

  • MPMSolverCfg, an Isaac Lab config wrapper for Newton's SolverImplicitMPM.Config.
  • NewtonMPMManager, a physics manager specialization for standalone implicit MPM simulations.
  • MPM custom-attribute registration in NewtonManager before builder/model finalization.
  • A standalone teapot-pour MPM demo.
  • Test coverage for the MPM manager path in the Newton manager abstraction tests.
  • A changelog fragment for isaaclab_newton.

The MPM manager does not use Isaac Lab's rigid-body external collision pipeline because Newton's implicit MPM solver handles particle/collider
interaction internally.

Dependencies:

  • No new package dependencies.
  • Uses the existing pinned Newton dependency, newton[sim] @ git+https://github.com/newton-physics/newton.git@v1.2.0.

Fixes # N/A

Type of change

  • New feature (non-breaking change which adds functionality)

GIF

Pour2

Validation

Ran formatting checks:

./isaaclab.sh -f

Ran focused Newton manager tests:

./isaaclab.sh -p -m pytest source/isaaclab_newton/test/physics/test_newton_manager_abstraction.py

Result:

33 passed

Checklist

  • I have read and understood the contribution guidelines (https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
  • I have run the pre-commit checks (https://pre-commit.com/) with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the changelog. A fragment was added under source/isaaclab_newton/changelog.d/; config/extension.toml was not edited directly.
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@maxkra15 maxkra15 requested a review from kellyguo11 May 20, 2026 01:35
@maxkra15 maxkra15 requested a review from ooctipus as a code owner May 20, 2026 01:35
@github-actions github-actions Bot added the isaac-lab Related to Isaac Lab team label May 20, 2026
Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

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

🔍 Code Review: Add Newton implicit MPM manager

Thank you for this well-structured addition of implicit MPM support to Isaac Lab! The implementation follows the established manager pattern cleanly. Here are my findings:


✅ Strengths

  1. Clean Architecture: The NewtonMPMManager / MPMSolverCfg pattern mirrors existing managers (Kamino, MJWarp, etc.) and integrates naturally with the base NewtonManager class.

  2. Thorough Test Coverage: Adding MPM to the existing test_newton_manager_abstraction.py ensures the new solver path is validated alongside the others.

  3. Well-Documented Demo: The teapot-pour demo (newton_cup_pour_mpm.py) is extensive and includes helpful CLI parameters for experimentation.

  4. Correct Custom Attribute Registration: The _register_solver_custom_attributes hook ensures MPM-specific particle attributes (mpm:viscosity, mpm:friction, etc.) are registered before particles are added.


🔸 Suggestions

1. Hardcoded Local Path in Demo ✅ RESOLVED

File: scripts/demos/newton_cup_pour_mpm.py

This absolute path will not work for other contributors.

Update: This has been addressed in commit 7c3f4c9. The demo now uses ISAACLAB_ASSETS_DATA_DIR for proper asset resolution, and the teapot USD has been added to isaaclab_assets.

2. Missing NaN/Divergence Checks in MPM Manager (Medium)

File: source/isaaclab_newton/isaaclab_newton/physics/mpm_manager.py

Implicit MPM solvers can silently diverge under certain conditions (e.g., extreme velocities, bad voxel sizing). Consider adding:

  • A diagnostic check for NaN or Inf values in particle positions/velocities after stepping
  • Optional logging when iteration limits are hit without convergence

This could be a follow-up enhancement rather than a blocker.

3. Config Attribute Mapping Silently Ignores Unknown Fields (Low)

File: source/isaaclab_newton/isaaclab_newton/physics/mpm_manager_cfg.py, lines 99-105

def to_solver_config(self) -> SolverImplicitMPM.Config:
    cfg = SolverImplicitMPM.Config()
    for key, value in self.to_dict().items():
        if hasattr(cfg, key):
            setattr(cfg, key, value)
    return cfg

Fields that exist in MPMSolverCfg but not in SolverImplicitMPM.Config are silently ignored. This could mask typos or version mismatches. Consider logging a warning for unmapped fields, or at minimum documenting this behavior.

4. Demo Particle Seeding Magic Numbers (Low)

File: scripts/demos/newton_cup_pour_mpm.py, lines 595-598

z_lo = mesh_lo[2] + 0.22 * mesh_extent[2] + bottom_clearance
z_hi = mesh_lo[2] + 0.72 * mesh_extent[2] - top_clearance

The 0.22 and 0.72 fractions appear to be empirically determined for this specific teapot mesh. A brief comment explaining these values (e.g., "skip bottom 22% for teapot base, top 28% for lid/spout area") would help future maintainers.


❓ Questions

  1. Test coverage for particle count validation: The NewtonMPMManager._build_solver correctly raises if model.particle_count == 0. Is this exercised by a negative test case, or just covered implicitly via the valid particle test?

  2. CUDA graph compatibility: Is the implicit MPM solver compatible with CUDA graph capture when use_cuda_graph=True? The demo defaults to --disable-cuda-graph being False, suggesting it should work, but this might warrant a note in the docstring.


📋 Summary

This is a solid contribution that follows Isaac Lab conventions well. The main actionable item is the hardcoded teapot asset path, which should be addressed before merge. The other suggestions are improvements that can be tracked separately.

Recommendation: Approve after addressing the hardcoded path issue.


🔄 Update (7c3f4c9)

The author has addressed the main blocking issue:

Changes in this push:

  • ✅ Replaced hardcoded teapot path with proper asset resolution via ISAACLAB_ASSETS_DATA_DIR
  • ✅ Added the teapot USD asset (teapot_hollow_separate_lid.usdc) to isaaclab_assets/data/Props/Teapot/
  • ✅ Added .usdc to .gitattributes for LFS tracking
  • ✅ Added changelog entry for the new asset

The demo now properly resolves the teapot asset from the package data directory, making it work for all contributors without manual path configuration.

Updated Recommendation: ✅ Approve — The blocking issue has been resolved. Remaining suggestions (NaN checks, unmapped field warnings, magic number comments) are minor improvements that can be addressed in follow-up PRs.


🔄 Update (bee2da2)

The author has pivoted the asset resolution strategy:

Changes in this push:

  • 🔄 Asset moved to Nucleus: The teapot USD is now fetched from ISAACLAB_NUCLEUS_DIR/Mimic/nut_pour_task/nut_pour_assets/ instead of being bundled in the repo
  • 🔄 Uses retrieve_file_path(): The demo now uses Isaac Lab's asset retrieval utility for proper caching/download
  • Removed LFS-tracked asset: The .usdc file and LFS entry have been removed from the repo
  • Removed changelog entry: Since no new asset is being added to isaaclab_assets, the changelog entry was removed

Assessment:

This is a cleaner approach — using existing Nucleus-hosted assets avoids bloating the repo with large binary files. The retrieve_file_path() utility handles caching transparently, and the error message now guides users to either upload to Nucleus or provide a local path via --teapot-usd.

The error handling with the explicit from exc chain is a nice touch for debugging.

Updated Recommendation: ✅ Approve — The asset resolution is now properly integrated with Isaac Lab's Nucleus infrastructure. This is the preferred pattern for large assets.


🔄 Update (3d8218a)

No substantive changes since the previous review. The author rebased onto the latest develop branch, which incorporated commits from other PRs. The MPM-specific code remains unchanged and continues to use the Nucleus-hosted teapot asset approach reviewed previously.

Status: ✅ Ready to merge — no additional review findings.


🔄 Update (d3355bc)

Excellent improvements in this push! The author has addressed one of my previous suggestions:

✅ Fixed: Config Attribute Mapping (Suggestion #3)

The to_solver_config() method now explicitly iterates over a whitelist of valid Newton solver fields (_SOLVER_CONFIG_FIELDS) rather than using to_dict(). This is a cleaner solution than logging warnings — it simply ensures only the relevant fields are mapped, and Isaac Lab metadata fields (like class_type, solver_type) are never accidentally forwarded.

Additional improvements:

  • 🐛 Fixed variable shadowing: solver_cfgsolver_cfg_item in the MPM detection loop
  • ✨ Added idempotency guard: has_custom_attribute(\"mpm:young_modulus\") prevents duplicate attribute registration
  • 🧪 Added two targeted unit tests covering both the field filtering and registration idempotency

Status: ✅ Ready to merge — this is a well-polished contribution.


🔄 Update (684dc9f)

Minor cleanup in the demo script:

  • ➖ Removed --teapot-scale and --teapot-roll-correction-deg CLI arguments
  • ➖ Removed _bake_mesh_roll_correction() function and associated logic
  • 🧹 Simplified open_teapot_stage() by removing dynamic scale operations

This is a sensible cleanup — the Nucleus-hosted teapot asset is apparently already in the correct orientation and scale, so the manual transformation code is no longer needed. This reduces code complexity without affecting functionality.

Status: ✅ Ready to merge — no new concerns.


🔄 Update (5022487)

Further simplification of the demo's visual scene setup:

Changes in this push:

  • ➖ Removed --table-usd, --bowl-usd, --table-visual-z, and --asset-scale CLI arguments
  • ➖ Removed AssetPaths class and resolve_asset_paths() function
  • 🔄 Changed teapot asset path from teapot_hollow_separate_lid.usdc to teapot.usdc
  • ✨ Rewrote setup_kit_scene() to generate table/bowl visuals procedurally via _spawn_collision_shape_visuals() using primitive shapes and mesh generation

Assessment:

This is a nice cleanup that removes external USD dependencies for the table and bowl. By constructing these visuals directly (CuboidCfg for table, generated mesh for bowl), the demo becomes more self-contained and avoids potential Nucleus availability issues for secondary assets. The collision geometry and visual geometry now match exactly, which is cleaner for a physics demo.

The only external asset requirement is now the teapot itself (renamed to teapot.usdc), which is appropriate since that's the centerpiece of the demo.

Status: ✅ Ready to merge — cleaner and more self-contained.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 20, 2026

Greptile Summary

This PR adds Newton implicit MPM simulation support to Isaac Lab, including an MPMSolverCfg config wrapper, a NewtonMPMManager specialization, custom-attribute registration hooks in NewtonManager, a teapot-pour demo, and test coverage for the new manager path.

  • NewtonMPMManager follows the existing manager pattern correctly, wiring SolverImplicitMPM, setting _use_single_state = True, and bypassing the collision pipeline that MPM handles internally.
  • The demo ships with TEAPOT_USD_PATH pointing to a developer-local absolute path that will raise FileNotFoundError for every other user who runs it without --teapot-usd.

Confidence Score: 3/5

The core manager and config classes are well-structured, but the demo ships with a hardcoded developer-local asset path that makes it unusable out of the box for anyone else.

The NewtonMPMManager, MPMSolverCfg, and test additions are clean and follow existing patterns. The demo TEAPOT_USD_PATH points to /home/maximiliank/Work/IsaacLab/... — anyone running it without --teapot-usd will hit a FileNotFoundError with no guidance on obtaining the asset. The library code itself is safe; the demo needs the asset path resolved before it can be used by anyone other than the author.

scripts/demos/newton_cup_pour_mpm.py — the hardcoded default teapot asset path must be replaced before the demo is usable by anyone other than the author.

Important Files Changed

Filename Overview
scripts/demos/newton_cup_pour_mpm.py 1237-line teapot-pour MPM demo; contains a hardcoded developer-local asset path that will fail at startup for every other user.
source/isaaclab_newton/isaaclab_newton/physics/mpm_manager.py Minimal NewtonMPMManager subclass; correctly sets _solver, _use_single_state, and _needs_collision_pipeline on the base class following the same pattern as other managers.
source/isaaclab_newton/isaaclab_newton/physics/mpm_manager_cfg.py MPMSolverCfg wraps SolverImplicitMPM.Config via hasattr-based forwarding; safe today but could silently forward Isaac Lab-only fields if Newton adds matching names.
source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py Adds _register_solver_custom_attributes at three call sites; variable shadowing and potential double-registration are minor robustness concerns.
source/isaaclab_newton/test/physics/test_newton_manager_abstraction.py Test coverage added for MPM manager path; correctly seeds particles and registers custom attributes before asserting solver population.
source/isaaclab_newton/isaaclab_newton/physics/newton_manager_cfg.py Doc-only addition clarifying that MPMSolverCfg bypasses the collision pipeline; no functional change.
source/isaaclab_newton/isaaclab_newton/physics/init.pyi Stub updated to export NewtonMPMManager and MPMSolverCfg correctly.

Sequence Diagram

sequenceDiagram
    participant User
    participant SimContext as SimulationContext
    participant NM as NewtonManager
    participant NMPM as NewtonMPMManager
    participant Builder as ModelBuilder
    participant Solver as SolverImplicitMPM

    User->>NM: create_builder()
    NM->>NM: _register_solver_custom_attributes(builder)
    NM-->>User: builder
    User->>Builder: add_particles(...)
    User->>NM: set_builder(builder)
    User->>SimContext: reset()
    SimContext->>NM: start_simulation()
    NM->>NM: _register_solver_custom_attributes(_builder)
    NM->>Builder: finalize(device)
    Builder-->>NM: model
    NM->>NMPM: _build_solver(model, solver_cfg)
    NMPM->>Solver: SolverImplicitMPM(model, config)
    Solver-->>NMPM: solver
    NMPM->>NM: _solver, _use_single_state, _needs_collision_pipeline
    User->>SimContext: step()
    SimContext->>NM: step(dt)
    NM->>Solver: step(model, state_0, state_0, dt)
Loading

Reviews (1): Last reviewed commit: "Add Newton implicit MPM manager" | Re-trigger Greptile

Comment on lines +141 to +143
BOWL_BASE_POS = (0.22, 0.0, TABLE_TOP_Z + 0.02)
BOWL_HEIGHT = 0.13
BOWL_RIM_Z = BOWL_BASE_POS[2] + BOWL_HEIGHT
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Hardcoded developer-local asset path

TEAPOT_USD_PATH is set to /home/maximiliank/Work/IsaacLab/... — a path that exists only on the author's machine. Every other user who runs the demo without --teapot-usd will immediately hit a FileNotFoundError with no guidance on where to obtain the asset. Either publish the asset to a Nucleus path and update this constant, or make --teapot-usd a required argument.

Comment on lines +96 to +108
def to_solver_config(self) -> SolverImplicitMPM.Config:
"""Build a :class:`SolverImplicitMPM.Config` from this configuration.

Returns:
A ``SolverImplicitMPM.Config`` instance ready for solver construction.
"""
from newton.solvers import SolverImplicitMPM

cfg = SolverImplicitMPM.Config()
for key, value in self.to_dict().items():
if hasattr(cfg, key):
setattr(cfg, key, value)
return cfg
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Silent forwarding of Isaac Lab-specific config fields to Newton config

to_solver_config iterates over self.to_dict() — which includes class_type, solver_type, and entries inherited from NewtonSolverCfg — and silently sets any field that also exists on SolverImplicitMPM.Config. Currently safe because those names don't appear in Newton's config, but a future Newton Config field with a colliding name would be silently injected with an Isaac Lab value. An explicit exclusion set would make the mapping robust against upstream Newton changes.

Comment on lines +641 to +644
if any(isinstance(solver_cfg, MPMSolverCfg) for solver_cfg in solver_cfgs):
from newton.solvers import SolverImplicitMPM

SolverImplicitMPM.register_custom_attributes(builder)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 register_custom_attributes called multiple times on the same builder

_register_solver_custom_attributes is invoked at three distinct call sites (create_builder, start_simulation, and both builder branches of instantiate_builder_from_stage), so the same ModelBuilder can receive SolverImplicitMPM.register_custom_attributes twice. Correctness currently depends on that call being idempotent. A guard like if not builder.has_custom_attribute("mpm:young_modulus") would make the invocation explicitly safe.

Suggested change
if any(isinstance(solver_cfg, MPMSolverCfg) for solver_cfg in solver_cfgs):
from newton.solvers import SolverImplicitMPM
SolverImplicitMPM.register_custom_attributes(builder)
if any(isinstance(solver_cfg, MPMSolverCfg) for solver_cfg in solver_cfgs):
from newton.solvers import SolverImplicitMPM
if not builder.has_custom_attribute("mpm:young_modulus"):
SolverImplicitMPM.register_custom_attributes(builder)

Comment on lines +636 to +641
solver_cfg = getattr(cfg, "solver_cfg", None)
solver_cfgs = [solver_cfg]
solver_cfgs.extend(getattr(entry, "solver_cfg", entry) for entry in getattr(solver_cfg, "entries", ()) or ())
from .mpm_manager_cfg import MPMSolverCfg

if any(isinstance(solver_cfg, MPMSolverCfg) for solver_cfg in solver_cfgs):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Variable name shadows outer binding in generator expression

solver_cfg is used both as the outer local variable and as the iteration variable inside the any(...) generator expression. Python 3 generator expressions carry their own scope so this is not a runtime bug, but a reader skimming the function may expect the isinstance check to test only the single top-level solver_cfg rather than every element of solver_cfgs.

Suggested change
solver_cfg = getattr(cfg, "solver_cfg", None)
solver_cfgs = [solver_cfg]
solver_cfgs.extend(getattr(entry, "solver_cfg", entry) for entry in getattr(solver_cfg, "entries", ()) or ())
from .mpm_manager_cfg import MPMSolverCfg
if any(isinstance(solver_cfg, MPMSolverCfg) for solver_cfg in solver_cfgs):
top_solver_cfg = getattr(cfg, "solver_cfg", None)
solver_cfgs = [top_solver_cfg]
solver_cfgs.extend(getattr(entry, "solver_cfg", entry) for entry in getattr(top_solver_cfg, "entries", ()) or ())
from .mpm_manager_cfg import MPMSolverCfg
if any(isinstance(sc, MPMSolverCfg) for sc in solver_cfgs):

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@github-actions github-actions Bot added the asset New asset feature or request label May 20, 2026
@maxkra15 maxkra15 marked this pull request as draft May 20, 2026 02:02
@maxkra15
Copy link
Copy Markdown
Author

Need to add USD asset to Nucleus

@maxkra15 maxkra15 force-pushed the max/newton-mpm-manager branch from bee2da2 to 3d8218a Compare May 21, 2026 23:01
@maxkra15 maxkra15 closed this May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

asset New asset feature or request isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant