newton.solvers.SolverVBD#

class newton.solvers.SolverVBD(model, iterations=10, friction_epsilon=1e-2, integrate_with_external_rigid_solver=False, particle_enable_self_contact=False, particle_self_contact_radius=0.2, particle_self_contact_margin=0.2, particle_conservative_bound_relaxation=0.85, particle_vertex_contact_buffer_size=32, particle_edge_contact_buffer_size=64, particle_collision_detection_interval=0, particle_edge_parallel_epsilon=1e-5, particle_enable_tile_solve=True, particle_topological_contact_filter_threshold=2, particle_rest_shape_contact_exclusion_radius=0.0, particle_external_vertex_contact_filtering_map=None, particle_external_edge_contact_filtering_map=None, rigid_avbd_alpha=0.95, rigid_avbd_joint_alpha=None, rigid_avbd_contact_alpha=None, rigid_avbd_beta=0.0, rigid_avbd_linear_beta=None, rigid_avbd_angular_beta=None, rigid_avbd_gamma=0.999, rigid_contact_hard=True, rigid_contact_history=False, rigid_contact_stick_motion_eps=1.0e-4, rigid_contact_stick_freeze_translation_eps=1.0e-4, rigid_contact_stick_freeze_angular_eps=1.0e-4, rigid_contact_k_start=1.0e2, rigid_body_contact_buffer_size=64, rigid_body_particle_contact_buffer_size=256, rigid_joint_linear_ke=1.0e5, rigid_joint_angular_ke=1.0e5, rigid_joint_linear_k_start=1.0e2, rigid_joint_angular_k_start=1.0e1, rigid_joint_linear_kd=0.0, rigid_joint_angular_kd=0.0, rigid_enable_dahl_friction=None)[source]#

Bases: SolverBase

An implicit solver using Vertex Block Descent (VBD) for particles and Augmented VBD (AVBD) for rigid bodies.

This unified solver supports:
  • Particle simulation (cloth, soft bodies) using the VBD algorithm

  • Rigid body simulation (joints, contacts) using the AVBD algorithm

  • Coupled particle-rigid body systems

For rigid bodies, the AVBD algorithm uses penalty stiffness that is fixed by default (rigid_avbd_beta=0) or ramped per iteration from k_start seeds by the AVBD beta parameters. Hard contacts and hard joint slots additionally use augmented-Lagrangian state.

Non-cable structural joint slots default to hard mode (augmented Lagrangian with persistent lambda and C0 stabilization). Cable stretch and bend default to soft mode. The hard/soft mode can be changed per slot via set_joint_constraint_mode().

Joint limitations:

See Joint Feature Support for the full comparison across solvers.

Buffer sizing:

SolverVBD pre-allocates contact state from capacities populated by CollisionPipeline when available; otherwise, the first step() lazily sizes buffers from Contacts. During CUDA graph recording, lazy resize is supported only when Warp mempool is enabled; otherwise the solver raises with guidance to pre-size before capture.

References

  • Anka He Chen, Ziheng Liu, Yin Yang, and Cem Yuksel. 2024. Vertex Block Descent. ACM Trans. Graph. 43, 4, Article 116 (July 2024), 16 pages. https://doi.org/10.1145/3658179

  • Chris Giles, Elie Diaz, and Cem Yuksel. 2025. Augmented Vertex Block Descent. ACM Trans. Graph. 44, 4, Article 90 (August 2025), 12 pages. https://doi.org/10.1145/3731195

Note

SolverVBD requires coloring for each system it solves:

Call newton.ModelBuilder.color() to automatically color both particles and rigid bodies.

VBD uses model.body_q as the structural rest pose and reads model.joint_q for drive/limit rest-angle offsets. The body transforms must match the joint angles at solver creation time (see example below).

Example

# Automatically color both particles and rigid bodies
builder.color()

model = builder.finalize()

solver = newton.solvers.SolverVBD(model)

# Initialize states and contacts
state_in = model.state()
state_out = model.state()
control = model.control()
contacts = model.contacts()

# Simulation loop
for i in range(100):
    model.collide(state_in, contacts)  # Update contacts
    solver.step(state_in, state_out, control, contacts, dt)
    state_in, state_out = state_out, state_in
class JointSlot#

Bases: object

Named constraint slot indices for set_joint_constraint_mode().

The first two solver constraint slots are structural where present:
  • CABLE: LINEAR/STRETCH -> stretch, ANGULAR/BEND -> bend

  • BALL: LINEAR only

  • FIXED/REVOLUTE/PRISMATIC/D6: LINEAR and ANGULAR

Drive/limit slots start at slot 2 and are not represented here. STRETCH and BEND are cable-only aliases for LINEAR and ANGULAR.

ANGULAR = 1#
BEND = 1#
LINEAR = 0#
STRETCH = 0#
classmethod register_custom_attributes(builder)#

Register solver-specific custom Model attributes for SolverVBD.

Currently used for:
  • Cable bending plasticity/hysteresis (Dahl friction model)

  • Per-joint structural constraint mode (hard/soft)

Attributes are declared in the vbd namespace so they can be authored in scenes and in USD as newton:vbd:<attr>.

__init__(model, iterations=10, friction_epsilon=1e-2, integrate_with_external_rigid_solver=False, particle_enable_self_contact=False, particle_self_contact_radius=0.2, particle_self_contact_margin=0.2, particle_conservative_bound_relaxation=0.85, particle_vertex_contact_buffer_size=32, particle_edge_contact_buffer_size=64, particle_collision_detection_interval=0, particle_edge_parallel_epsilon=1e-5, particle_enable_tile_solve=True, particle_topological_contact_filter_threshold=2, particle_rest_shape_contact_exclusion_radius=0.0, particle_external_vertex_contact_filtering_map=None, particle_external_edge_contact_filtering_map=None, rigid_avbd_alpha=0.95, rigid_avbd_joint_alpha=None, rigid_avbd_contact_alpha=None, rigid_avbd_beta=0.0, rigid_avbd_linear_beta=None, rigid_avbd_angular_beta=None, rigid_avbd_gamma=0.999, rigid_contact_hard=True, rigid_contact_history=False, rigid_contact_stick_motion_eps=1.0e-4, rigid_contact_stick_freeze_translation_eps=1.0e-4, rigid_contact_stick_freeze_angular_eps=1.0e-4, rigid_contact_k_start=1.0e2, rigid_body_contact_buffer_size=64, rigid_body_particle_contact_buffer_size=256, rigid_joint_linear_ke=1.0e5, rigid_joint_angular_ke=1.0e5, rigid_joint_linear_k_start=1.0e2, rigid_joint_angular_k_start=1.0e1, rigid_joint_linear_kd=0.0, rigid_joint_angular_kd=0.0, rigid_enable_dahl_friction=None)#
Parameters:
  • model (Model) – The Model object used to initialize the integrator. Must be identical to the Model object passed to the step function.

  • parameters (Rigid body)

  • iterations (int) – Number of VBD iterations per step.

  • friction_epsilon (float) – Threshold to smooth small relative velocities in friction computation (used for both particle and rigid body contacts).

  • integrate_with_external_rigid_solver (bool) – Indicator for coupled rigid body-cloth simulation. When set to True, the solver assumes rigid bodies are integrated by an external solver (one-way coupling).

  • parameters

  • particle_enable_self_contact (bool) – Whether to enable self-contact detection for particles.

  • particle_self_contact_radius (float) – The radius used for self-contact detection. This is the distance at which vertex-triangle pairs and edge-edge pairs will start to interact with each other.

  • particle_self_contact_margin (float) – The margin used for self-contact detection. This is the distance at which vertex-triangle pairs and edge-edge will be considered in contact generation. It should be larger than particle_self_contact_radius to avoid missing contacts.

  • particle_conservative_bound_relaxation (float) – Relaxation factor for conservative penetration-free projection.

  • particle_vertex_contact_buffer_size (int) – Preallocation size for each vertex’s vertex-triangle collision buffer.

  • particle_edge_contact_buffer_size (int) – Preallocation size for edge’s edge-edge collision buffer.

  • particle_collision_detection_interval (int) – Controls how frequently particle self-contact detection is applied during the simulation. If set to a value < 0, collision detection is only performed once before the initialization step. If set to 0, collision detection is applied twice: once before and once immediately after initialization. If set to a value n >= 1, collision detection is applied before every n VBD iterations.

  • particle_edge_parallel_epsilon (float) – Threshold to detect near-parallel edges in edge-edge collision handling.

  • particle_enable_tile_solve (bool) – Whether to accelerate the particle solver using tile API.

  • particle_topological_contact_filter_threshold (int) – Maximum topological distance (measured in rings) under which candidate self-contacts are discarded. Set to a higher value to tolerate contacts between more closely connected mesh elements. Only used when particle_enable_self_contact is True. Note that setting this to a value larger than 3 will result in a significant increase in computation time.

  • particle_rest_shape_contact_exclusion_radius (float) – Additional world-space distance threshold for filtering topologically close primitives. Candidate contacts with a rest separation shorter than this value are ignored. The distance is evaluated in the rest configuration conveyed by model.particle_q. Only used when particle_enable_self_contact is True.

  • particle_external_vertex_contact_filtering_map (dict | None) – Optional dictionary used to exclude additional vertex-triangle pairs during contact generation. Keys must be vertex primitive ids (integers), and each value must be a list or set containing the triangle primitives to be filtered out. Only used when particle_enable_self_contact is True.

  • particle_external_edge_contact_filtering_map (dict | None) – Optional dictionary used to exclude additional edge-edge pairs during contact generation. Keys must be edge primitive ids (integers), and each value must be a list or set containing the edges to be filtered out. Only used when particle_enable_self_contact is True.

  • parameters

  • rigid_avbd_alpha (float) – C0 stabilization strength (C_stab = C - alpha * C0). Range: [0, 1]. Used as the default alpha for joints and body-body contacts.

  • rigid_avbd_joint_alpha (float | None) – Joint-specific alpha override. None (default) uses rigid_avbd_alpha.

  • rigid_avbd_contact_alpha (float | None) – Body-body contact alpha override. None (default) uses rigid_avbd_alpha. For hard contacts, lower values (e.g., 0.0) correct more current penetration each step and can give stronger repulsion when iteration count is low or contact history is disabled. Larger values can improve stability with enough iterations or contact history, but may feel weak with few iterations and no history.

  • rigid_avbd_beta (float) – Penalty ramp rate per AVBD iteration. 0 (default) disables ramping (fixed-k). Set to e.g. 1e5 for ramping. Used for both linear and angular constraints unless overridden. Note: linear (meters) and angular (radians) constraints have different units, so the overrides should be used for production tuning.

  • rigid_avbd_linear_beta (float | None) – Linear beta override for linear constraints (meters). None (default) uses rigid_avbd_beta.

  • rigid_avbd_angular_beta (float | None) – Angular beta override for angular constraints (radians). None (default) uses rigid_avbd_beta.

  • rigid_avbd_gamma (float) – Per-step decay factor for penalty k and persisted hard-mode lambda. Hard joint/contact lambda is additionally scaled by the corresponding alpha during warm-starting, following the AVBD reference scheme. Lower values decay faster, improving stability at the cost of slower convergence.

  • rigid_contact_hard (bool) – Whether body-body rigid contacts use hard mode (augmented Lagrangian with persistent lambda and C0 stabilization) or soft mode (penalty only).

  • rigid_contact_history (bool) – Whether to persist body-body contact state across steps using Contacts.rigid_contact_match_index from the collision pipeline. For hard contacts, restores lambda, penalty k, and sticky contact anchors; C0 is recomputed each step. For soft contacts, only restored penalty k affects the solve (useful with ramping). Requires contacts with rigid_contact_match_index populated; use CollisionPipeline(contact_matching="latest") for VBD warm-starting. Ignored when integrate_with_external_rigid_solver=True or model.body_count == 0.

  • rigid_contact_stick_motion_eps (float) – Tangential contact residual threshold for marking hard body-body contacts as sticking. Sticking contacts may replay contact points when rigid_contact_history=True; dynamic-dynamic sticking contacts may also use the body-level deadzone snap. Set to 0.0 to disable sticky flags while preserving lambda and penalty warm-starting.

  • rigid_contact_stick_freeze_translation_eps (float) – World-space translation threshold for the body-level deadzone snap on dynamic-dynamic sticking contacts. Set to 0.0 to disable translation snapping.

  • rigid_contact_stick_freeze_angular_eps (float) – Angular threshold [rad] for the body-level deadzone snap on dynamic-dynamic sticking contacts. Set to 0.0 to disable angular snapping.

  • rigid_contact_k_start (float) – Body-body and body-particle contact penalty seed for AVBD ramping. Used when rigid_avbd_linear_beta (or rigid_avbd_beta fallback) is greater than zero. When the linear beta is 0, k is fixed at the contact stiffness regardless of this value.

  • rigid_body_contact_buffer_size (int) – Max body-body contacts per rigid body for per-body contact lists.

  • rigid_body_particle_contact_buffer_size (int) – Max body-particle contacts tracked per rigid body.

  • rigid_joint_linear_ke (float) – Penalty stiffness ceiling for non-cable structural linear joint slots.

  • rigid_joint_angular_ke (float) – Penalty stiffness ceiling for non-cable structural angular joint slots.

  • rigid_joint_linear_k_start (float) – Linear penalty seed for AVBD ramping. Used when rigid_avbd_linear_beta (or rigid_avbd_beta fallback) is greater than zero. When the linear beta is 0, k is fixed at the joint stiffness regardless of this value.

  • rigid_joint_angular_k_start (float) – Angular penalty seed for AVBD ramping. Used when rigid_avbd_angular_beta (or rigid_avbd_beta fallback) is greater than zero. When the angular beta is 0, k is fixed at the joint stiffness regardless of this value.

  • rigid_joint_linear_kd (float) – Rayleigh damping coefficient for non-cable linear joint constraints. Negative values are clamped to 0.

  • rigid_joint_angular_kd (float) – Rayleigh damping coefficient for non-cable angular joint constraints. Negative values are clamped to 0.

  • rigid_enable_dahl_friction (bool | None) – Deprecated and ignored. Dahl friction is auto-detected from model.vbd.dahl_eps_max / model.vbd.dahl_tau.

Note

  • The integrate_with_external_rigid_solver argument enables one-way coupling between rigid body and soft body solvers. If set to True, the rigid states should be integrated externally, with state_in passed to step representing the previous rigid state and state_out representing the current one. Frictional forces are computed accordingly.

  • particle_vertex_contact_buffer_size, particle_edge_contact_buffer_size, rigid_body_contact_buffer_size, and rigid_body_particle_contact_buffer_size are fixed and will not be dynamically resized during runtime. Setting them too small may result in undetected collisions (particles) or contact overflow (rigid body contacts). Setting them excessively large may increase memory usage and degrade performance.

  • Dahl hysteresis friction for cable bending is auto-detected from custom model attributes model.vbd.dahl_eps_max and model.vbd.dahl_tau (set via SolverVBD.register_custom_attributes).

collect_rigid_contact_forces(body_q, body_q_prev, contacts, dt)#

Collect per-contact rigid contact forces and world-space application points.

Parameters:
  • body_q (wp.array[wp.transform]) – Current body transforms (world frame), typically state_out.body_q after a step() call.

  • body_q_prev (wp.array[wp.transform]) – Previous body transforms (world frame) corresponding to the start of the step. The caller must snapshot solver.body_q_prev before calling step(), because step() advances the solver’s internal body_q_prev to the end-of-step pose.

  • contacts (Optional[Contacts]) – Contact data buffers containing rigid contact geometry/material references. If None, the function returns default zero/sentinel outputs.

  • dt (float) – Time step size [s].

Note

Call after collision generation and step() with the same Contacts buffer. If rigid contact state is absent or undersized, this returns sentinel/zero outputs without growing output buffers. Output buffers persist and grow on demand; they do not shrink, so iterate up to the returned rigid_contact_count rather than the array length.

Returns:

tuple[

wp.array[wp.int32], wp.array[wp.int32], wp.array[wp.vec3], wp.array[wp.vec3], wp.array[wp.vec3], wp.array[wp.int32],

]: Tuple of per-contact outputs:
  • body0: Body index for shape0, int32.

  • body1: Body index for shape1, int32.

  • point0_world: World-space contact point on body0, wp.vec3 [m].

  • point1_world: World-space contact point on body1, wp.vec3 [m].

  • force_on_body1: Contact force applied to body1 in world frame, wp.vec3 [N].

  • rigid_contact_count: Length-1 active rigid-contact count, int32.

Return type:

tuple[array, array, array, array, array, array]

notify_model_changed(flags)#
rebuild_bvh(state)#

This function will rebuild the BVHs used for detecting self-contacts using the input state.

When the simulated object deforms significantly, simply refitting the BVH can lead to deterioration of the BVH’s quality. In these cases, rebuilding the entire tree is necessary to achieve better querying efficiency.

Parameters:

state (newton.State) – The state whose particle positions (particle_q) will be used for rebuilding the BVHs.

set_joint_constraint_mode(joint_index, hard, slot=None)#

Set hard or soft constraint mode for a joint’s structural slots at runtime.

Hard mode (augmented Lagrangian): uses persistent lambda + C0 stabilization to drive constraint violation toward zero across iterations. Soft mode (penalty-only): uses penalty stiffness only (no lambda or C0 state).

Structural slots are LINEAR (slot 0) and ANGULAR (slot 1). Drive/limit slots (slot 2+) are always soft and cannot be set to hard.

By default, cable stretch and bend slots are soft, while non-cable structural slots are hard.

For bulk initialization of non-cable joints at build time (avoids per-joint roundtrips), use the joint_is_hard model custom attribute:

SolverVBD.register_custom_attributes(builder)  # before adding joints
...
model = builder.finalize()
model.vbd.joint_is_hard.numpy()[j] = 0  # set joint j to soft
solver = SolverVBD(model, ...)
Parameters:
  • joint_index (int) – Index of the joint to modify.

  • hard (bool) – True for hard mode (AL), False for soft mode (penalty-only).

  • slot (int | None) – Specific slot index to set. If None, sets all structural slots. Use JointSlot.LINEAR / JointSlot.ANGULAR (equivalently JointSlot.STRETCH / JointSlot.BEND for cables).

Raises:

ValueError – If the joint index is out of range, or the slot is a drive/limit slot (>= 2), or the slot exceeds the joint’s constraint dimension.

set_rigid_history_update(update)#

Set whether the next step() should update rigid solver history.

When True (default), the step refreshes rigid contact state from the provided Contacts buffer: rebuilds per-body contact lists, initializes penalty_k/lambda/C0, and restores warm-start state from Contacts.rigid_contact_match_index when contact history is enabled. When False, the step reuses the current rigid contact lists and contact state. In that mode, the caller must pass the same contact result/buffers used by the previous refresh; do not run collision into the contacts buffer between refreshes. Passing newly collided contacts while update is disabled can mismatch stale per-body contact lists with current contact rows.

Joint AVBD maintenance (C0 snapshot, lambda decay) runs every step regardless of this flag via step_joint_C0_lambda(). Rigid contact history snapshotting also runs every step when enabled.

This setting applies only to the next call to step() and is then reset to True. Useful for substepping where collision detection frequency differs from the simulation step frequency.

Parameters:

update (bool) – If True, update rigid solver state. If False, reuse previous.

step(state_in, state_out, control, contacts, dt)#

Execute one simulation timestep using VBD (particles) and AVBD (rigid bodies).

The solver follows a 3-phase structure: 1. Initialize: Forward integrate particles and rigid bodies, detect collisions, initialize contact state 2. Iterate: Interleave particle VBD iterations and rigid body AVBD iterations 3. Finalize: Update velocities and persistent state (Dahl friction)

To control rigid body substepping behavior, call set_rigid_history_update(). When True (default), the step rebuilds rigid contact lists, re-initializes rigid contact state (penalty_k, lambda, C0), and restores from history if enabled. When False, reuses previous rigid contact state. The flag is reset to True when consumed.

Parameters:
  • state_in (newton.State) – Input state.

  • state_out (newton.State) – Output state.

  • control (Control) – Control inputs.

  • contacts (Contacts | None) – Contact data produced by collide() (rigid-rigid and rigid-particle contacts). If None, rigid contact handling is skipped. Note that particle self-contact (if enabled) does not depend on this argument.

  • dt (float) – Time step size.