Extending Newton Solvers
This page is for contributors adding a Newton solver to Isaac Lab or building a
custom coupled solver. It describes the
~isaaclab_newton.physics.NewtonManager extension contract: what the
manager owns, when its hooks run, and what a subclass must provide.
If you only need to select and configure a shipped solver, use the user-facing pages instead: /source/concepts/backends_and_presets for backend and preset selection, the solver tuning guides for shipped solvers, and Coupled Solvers for choosing a coupling approach.
When a Solver Manager Is Needed
Each Newton solver is exposed as a
~isaaclab_newton.physics.NewtonManager subclass. Write a new one when:
- a Newton solver has no Isaac Lab manager yet;
- the solver needs its own contact allocation, builder attributes, or reset handling;
- several solvers must advance one shared model and the substep order is part of the algorithm.
Do not write one when an existing solver can advance the whole model, or when
the scene can be partitioned into named solver entries. Partitioning is already
covered by ~isaaclab_contrib.coupling.CouplerProxyCfg and
~isaaclab_contrib.coupling.CouplerAdmmCfg, which
~isaaclab_contrib.coupling.NewtonCouplerManager resolves into entry
views over a shared model. Prefer that path for mixed rigid and deformable
scenes. Write a coupled manager only when contact detection is shared but each
solver consumes the contacts differently, or when the exchange between solvers
is a custom force, impulse, or state transfer.
Responsibilities and Boundaries
~isaaclab_newton.physics.NewtonManager is a class-level singleton: all
state lives on the base class and there are no instance methods, so exactly one
manager subclass is active per simulation.
~isaaclab_newton.physics.NewtonCfg copies solver_cfg.class_type
onto its own isaaclab_newton.physics.NewtonCfg.class_type, so task
configuration never names the manager directly.
Lifecycle
The public entry points run in this order. Subclass hooks are private; the base class invokes them.
_build_solver() runs after the model is finalized, so it may size solver
resources from the real model. _register_builder_attributes() runs before
particles are added and before finalize(), so it is the only place to
register Newton custom attributes.
step() takes one of two paths, selected by
~isaaclab_newton.physics.NewtonManager.handles_decimation. When every
actuator is on the graph-safe Newton fast path, actuators and substeps run
together inside one graph and step() runs the whole decimation loop, so
_step_solver() runs decimation x num_substeps times per call. Otherwise
actuators run eagerly, only the substeps are graphed through
_simulate_physics_only(), and the environment drives decimation by calling
step() repeatedly. Both paths reach _step_solver() through the same
substep loop.
Graph capture happens at one of several points. initialize_solver() captures
unless the graph-safe path is active; in that case
~isaaclab_newton.physics.NewtonManager.set_decimation captures once the
decimation is known, and the RTX path defers capture to the first step().
All routes check _supports_cuda_graph_capture(). The non-RTX route
additionally checks _requires_initial_reset_before_graph_capture().
With _use_single_state = False the base manager ping-pongs
NewtonManager._state_0 and NewtonManager._state_1 after each substep,
except on the final substep of an odd count, where it copies instead. Never
cache a State reference in _build_solver(); read the current state
through the class attribute on each use.
Extension Contract
A subclass must implement _build_solver() and assign four slots on
NewtonManager itself, not on cls.
~isaaclab_newton.physics.NewtonManager.initialize_solver raises
RuntimeError if _solver is still unset. The other three are not
validated and keep their defaults, so a subclass that forgets them runs with
double-buffered states, no collision pipeline, and no visualizer force input.
Override anything else only when the solver needs it:
_create_solver(): construct a solver without mutating manager state, so a coupler can nest this solver._initialize_contacts(): allocate custom contact buffers._step_solver(state_0, state_1, control, contacts, substep_dt): change one substep while keeping the base simulation loop._simulate_physics_only(): add per-step work around the substep loop._reset_solver_internals(): clear solver-owned state for reset worlds._register_builder_attributes(): register Newton custom particle, shape, or body attributes on the builder._prepare_builder_for_finalize(): normalize imported or replicated builder data immediately beforefinalize()._supports_cuda_graph_capture(): returnFalseto fall back to eager execution._requires_initial_reset_before_graph_capture(): delay headless capture until the first post-reset step._solver_specific_clear(): release class-level state the subclass owns._check_solver_status()and_log_solver_debug(): run after stepping.
~isaaclab_newton.physics.NewtonMPMManager overrides both builder hooks.
Raise from _build_solver() on an unsupported configuration rather than
silently degrading. Name the manager Newton<Solver>Manager.
Coupling Paths
The four architectures differ in what drives the substep loop:
A custom shared-model manager bypasses entry ownership resolution, so it cannot reuse the coupler’s selectors or validation. For the proxy and ADMM trade-offs, see Coupled Solvers.
Related Documentation
- add_physics_backend: adding a whole physics backend.
- /source/api/lab_newton/isaaclab_newton.physics: manager and solver configuration API reference.