VBD Tuning#

This page explains how SolverVBD reacts to its solver and model parameters, what typically goes wrong and how to fix it, and which parameter combinations are known to work. It covers cloth (triangle meshes), soft bodies (tetrahedral meshes), rigid bodies, and cables, including their contacts. See Solver Tuning Reference for the parameter reference and VBD for setup and API explanations. Simulation Tuning holds the diagnostic workflow that this page assumes. Check Joint Feature Support and Contact Material Support before tuning your model.

Important

The specific values, mode names, and formulas on this page reflect the code at a point in time and can drift. Treat them as starting points and verify any you rely on against the cited source (for example SolverVBD and its kernels). See Simulation Tuning for the full guidance.

Mental Model#

VBD treats every time step as one minimization and solves it one block at a time. A block is one particle (3 degrees of freedom) or one rigid body (6). Each block solves a small local problem: with every other block held fixed, it takes one Newton step that balances its inertia against the forces acting on it. One pass over all blocks is one iteration, and the solver runs a fixed number of iterations per step. The material parameters on this page enter through the energy terms of that minimization, and contacts are energy terms too; the other parameters control detection, truncation and the iteration. How VBD Works on the backend page gives the step objective, the block solve and its call tree, the formula of every energy term, and the Divide and Truncate scheme (DAT) for particle self-contact and optional rigid-soft contact.

The consequences for tuning are:

  • Stiff terms win the local solve. One term far stiffer than the others that share its vertices makes the local step serve that term and neglect the rest. See Stiffness ratio.

  • Information moves one ring per iteration. A vertex sees only the vertices it shares an element with, its one ring, so a change travels one ring of neighbours per iteration. A fine mesh needs more iterations to reach the same state. See A fine mesh converges slowly.

  • An unfinished solve becomes velocity. Corrections that did not settle inside the step come back as motion in the next step.

  • DAT caps motion between detections. DAT keeps contacts penetration-free by limiting how far a vertex may move between two contact detections. That limit, the motion budget, is a fraction of the distance out to which contact pairs are detected, the query radius. A small query radius means a small budget and visible DAT damping. See DAT damping: slow, hovering or locked motion.

  • Float32 sets a floor on the time step. See A smaller time step made things worse.

Parameter Effects#

Four contact distances appear in the tables below; Contact Handling has the figure, the sums per contact type and the arguments that set them. The interaction distance is where the contact force starts to act. For particle contacts, it is the self-contact margin, or the particle radius plus the shape margin for contact with shapes. The detection slack is the extra reach of detection beyond it, the gap, so that pairs are known before they interact. The query radius is their sum. The motion budget is how far one side of a pair may move between two detections under DAT: half of dat_conservative_bound_relaxation times the query radius.

VBD-Specific Parameters#

The constructor arguments of SolverVBD that matter most for tuning. Listed values are built-in fallbacks; the class docstring is the source of truth. Omitted iterations, rigid_contact_history, and non-rod joint gains (rigid_joint_*_ke and rigid_joint_*_kd) can inherit model.vbd settings.

Parameter

What it controls

Raise or enable when

Cost or risk

iterations (10)

Block-descent sweeps per substep; how far information travels and how far the solve converges.

The object looks too soft or lags; a finer mesh. For jitter only as the last resort, see Jitter: where to look first.

Runtime grows linearly. Does not fix a stiffness-ratio problem.

friction_epsilon (1e-2)

Threshold for small sliding speeds [m/s]. Sets particle friction smoothing length to friction_epsilon x dt; also regularizes the compliant-ALM body-body friction solve.

Particle friction chatters at rest.

Slow particle creep under tangential load when large; affects finite-iteration convergence for compliant-ALM body-body contacts.

dat_conservative_bound_relaxation (0.85)

Scale of both truncations: how close to its division plane a vertex may go, and the radius cap of 0.5 x this x query radius.

Rarely; DAT damping with a query radius you cannot raise.

Values near 1 leave no safety for detection error.

particle_enable_self_contact (False)

Vertex-triangle and edge-edge self-contact with DAT.

Cloth folds or stacks; soft bodies touch themselves.

Detection cost; requires a non-NONE self-contact schedule.

particle_self_contact_margin (0.2)

Interaction distance: the surface offset at which self-contact forces start. Always pass it together with the gap.

Visible gaps between layers are too small; see Setting margin and gap.

Rest neighbours beyond the topological filter push each other when it exceeds their rest distance.

particle_self_contact_gap (0)

Detection slack: pairs are found out to margin plus gap, and the motion budget scales with that sum.

Motion looks capped or sticky (DAT damping); fast relative motion.

More candidate pairs, larger buffers.

particle_vertex_contact_buffer_size (32), particle_edge_contact_buffer_size (64)

Candidate pairs stored per vertex and per edge. Pairs beyond the capacity are dropped silently.

Many layers or a large gap; tunneling. See Self-contact tunneling.

Memory, detection time.

particle_topological_contact_filter_threshold (2)

Ring distance under which candidate pairs are discarded.

A coarse mesh with a large interaction distance.

Values above 3 raise the cost significantly (docstring).

particle_rest_shape_contact_exclusion_radius (0)

Pairs closer than this in the rest shape are never contacts.

Seams, layered cloth, or thin regions that are close by design.

Real contacts in those regions are missed too.

particle_enable_tile_solve (True)

Tile-based elasticity kernel specialized at construction.

Keep on. Rebuild the solver after changing stiffness values.

None in normal use.

collision_frequency_type, collision_frequency (SOFT_SELF_CONTACT slot)

When self-contact detection runs. AUTO resolves to before and after the inertial step. ITERATIONS with frequency \(k\) detects before the inertial step and then before iterations \(k, 2k, \ldots\); the pass after the inertial step does not run, so a \(k\) above the iteration count detects less often than AUTO.

Fast motion with a small gap; for jitter only as the last resort.

One detection pass per refresh.

rigid_soft_contact_use_log_barrier (False)

Rigid-soft normal law: quadratic penalty, or the self-contact barrier.

Rigid-soft contacts that sink deep under load with the quadratic penalty: the barrier force grows like \(1/d\) as the gap closes. It does not stop a crossing; that is rigid_soft_enable_dat.

Stiffer local systems, more iterations.

rigid_soft_enable_dat (False)

DAT truncation of rigid pose updates and particles against rigid-soft contact planes.

Thin cloth between fast rigid parts.

Rigid motion is capped by the rigid-soft query radius.

rigid_body_particle_contact_buffer_size (256)

Rigid-soft contact records per body.

Dense cloth on a large body; grippers with many vertices in contact.

Memory.

rigid_body_contact_buffer_size (64)

Body-body contact records per body.

Per-body overflow warnings during impacts or dense packing (see Contact capacity).

Memory; contacts beyond the limit are omitted from that body’s solve.

rigid_contact_history (False)

Warm-start body-body contact state between steps; requires pipeline contact matching (see Contact history).

Persistent contacts jitter or slip; compare against a history-off baseline.

Extra computation and memory for matching and history.

rigid_joint_linear_ke (1e5), rigid_joint_angular_ke (1e5)

Non-rod structural joint stiffness in constrained directions [N/m, N·m/rad]; separate from drive gains.

Joint displacement or rotation under load remains too large after checking setup and convergence.

Very large values can impede convergence. Rebuild the solver after changes.

rigid_joint_linear_kd (0), rigid_joint_angular_kd (0)

Non-rod structural joint damping in constrained directions [N·s/m, N·m·s/rad].

Constraint-error oscillations persist after release; tune after stiffness on an isolated joint.

Excessive damping can slow settling after a disturbance. Rebuild the solver after changes.

Deprecated aliases remain for one release cycle: particle_self_contact_radius (legacy radius and margin pair), particle_collision_detection_interval (now the self-contact slot of the collision schedule) and particle_conservative_bound_relaxation (now dat_conservative_bound_relaxation). New code should not use them. Passing a margin without a gap also triggers the legacy meaning of the margin and a deprecation warning, so always pass both.

Compliant ALM is the default for rigid constraints. Initially keep rigid_avbd_alpha and rigid_avbd_gamma at their defaults. Legacy AVBD is deprecated and will be removed in a future release. Revalidate scenes when migrating; the same settings can produce different behavior.

Finite stiffness permits some stretch, joint displacement, or contact penetration even after convergence. If deformation is larger than expected, check loads, material values, rest geometry, attachments, and contact setup. Also check time step and iteration sensitivity rather than relying only on higher stiffness; see Simulation Tuning.

Choose non-rod structural stiffness for acceptable constrained displacement or rotation on an isolated joint under load. Then tune damping from constraint-error decay after release. For cables, see materials and attachments.

Model Parameters#

Cloth and soft bodies#

Model parameters are set when the geometry is added, through add_cloth_mesh(), add_cloth_grid(), add_soft_mesh() and add_soft_grid(), under the names below. After finalize() they live on the Model as the arrays tri_materials, edge_bending_properties, tet_materials, particle_radius and particle_mass, and as the scalars soft_contact_ke, soft_contact_kd and soft_contact_mu. The formulas they enter are in The energy terms.

Parameter

What it controls

Raise when

Cost or risk

tri_ke, tri_ka [N/m]

Membrane stretch and area stiffness (\(\mu\) and \(\lambda\) per unit area of the stable Neo-Hookean membrane).

Cloth stretches visibly under its own weight.

Above about 1e6, slow convergence and jitter under contact; stiffening only the membrane moves deformation into bending (see Example 2: stiffening only the membrane does not stiffen the object).

tri_kd

Membrane damping.

In-plane ringing after impacts.

Overdamped, slow drape.

edge_ke [N]

Bending stiffness of the dihedral hinge.

Cloth folds too sharply or looks too limp.

Stiff hinges need more iterations; raise membrane and bending together for a stiffer object.

edge_kd

Bending damping.

Bending jitter; first knob of the jitter pipeline for cloth without contacts.

Slow unfolding.

k_mu, k_lambda [Pa]

Volumetric Neo-Hookean shear and volume stiffness.

A soft body sags or inverts too easily.

Same ceiling as the membrane; k_lambda far above k_mu makes a stiff local system.

k_damp [Pa s]

Volumetric viscous damping.

Soft-body jitter; the soft-body knob of the jitter pipeline.

Slow recovery.

soft_contact_ke [N/m]

Contact stiffness for self-contact; averaged with the shape ke for rigid-soft contact.

Penetration between layers or into shapes that the fix below does not cure.

Keep within about 10x the elastic stiffness; see Example 1: contact stiffness too high compared to the elasticity.

soft_contact_kd

Contact damping on the gap rate, active only while approaching.

Contact jitter; second knob of the jitter pipeline.

Shrinks each correction by enlarging the local matrix; too much slows settling.

soft_contact_mu

Friction coefficient (geometric mean with the shape mu for rigid-soft contact).

Cloth slides off a gripper.

Sticking, slow sliding.

particle_radius [m]

The particle’s half of the rigid-soft interaction distance; also the resting offset from shapes.

Cloth sinks into shapes.

Visible floating; a large radius with a small mesh spacing distorts contact geometry.

density, mass

Particle masses (per area for cloth, per volume for soft meshes).

Never as a fix; set it from the real object.

Too small a mass is an explosion cause; see Explosion: what to check, in this order.

shape ke, kd, mu, margin (ShapeConfig)

The shape’s half of the rigid-soft material and distance.

Set once per object; the shape margin adds to the interaction distance.

ke and kd are averaged with the particle side; mu is the geometric mean, so a zero on either side removes friction. The shape gap is a rigid-rigid broad-phase setting and does not enter rigid-soft detection, which uses soft_contact_gap.

CollisionPipeline(soft_contact_gap=...) (0.01)

Rigid-soft detection slack, added on top of particle radius plus shape margin.

Fast relative motion between cloth and shapes.

More rigid-soft records; rigid-soft DAT budget grows with it.

vel

Initial velocity of the added geometry.

Never as a fix; a large initial velocity is an explosion cause.

See Explosion: what to check, in this order.

Cables#

Create a Rod and pass it to ModelBuilder.add_rod(); each segment becomes a capsule body connected to its neighbors by rod joints (see Rod joints). For capsule contacts, see body-body contacts.

Elastic material inputs and section rigidities are two mutually exclusive ways to specify the material on Rod.

Parameter

What it controls

When to use or adjust

Cost or risk

youngs_modulus [Pa] and radius [m], plus either poissons_ratio (dimensionless) or shear_modulus [Pa] (Rod)

Elastic material inputs used to compute section rigidities.

Resolution studies at fixed material.

Supply exactly one of poissons_ratio or shear_modulus.

stretch_rigidity (\(EA\)), shear_rigidity (\(kGA\)) [N], bend_rigidity (\(EI\)), twist_rigidity (\(GJ\)) [N·m²] (Rod)

Axial, shear, bending, and torsional section rigidities.

Specify each section response directly while keeping it fixed across segment resolutions.

All four rigidities must be supplied together.

stretch_stiffness, shear_stiffness [N/m], bend_stiffness, twist_stiffness [N·m/rad] (ModelBuilder.add_rod())

Direct per-joint stiffness for each mode (not scaled by segment length).

Set joint response directly at a fixed segment count.

Overrides material-derived values; rescale with segment length when changing resolution to preserve the material.

stretch_damping, shear_damping [N·s/m], bend_damping, twist_damping [N·m·s/rad] (ModelBuilder.add_rod())

Direct per-joint damping for each mode (not scaled by segment length).

Tune oscillation decay after release, after fitting stiffness.

Not a damping ratio. Excessive damping makes motion sluggish.

Stiffness and resolution. The builder maps each rigidity to joint stiffness as \(R / L_{\mathrm{dual}}\), where \(L_{\mathrm{dual}}\) is the mean rest length of the two adjacent segments. This lets resolution studies keep the intended material fixed.

Without material or section-rigidity inputs on Rod or explicit overrides, add_rod defaults stretch and shear stiffness to 1.0e5 N/m and bend and twist stiffness to 0.0 N·m/rad. See ModelBuilder.add_rod() for topology restrictions.

Refine segment count separately from time step and iterations: the new joint graph, masses, inertias, and contact workload require another convergence check. Recheck the damping transient at the final resolution too.

Initial and rest geometry. Initial geometry (points, quaternions) and rest geometry (rest_points, rest_quaternions) are distinct; rest defaults to the initial shape. For an ordered chain, rest_straight=True sets zero intrinsic bend and twist without changing the initial geometry or segment lengths. Do not supply explicit rest inputs with it. With nonzero bending stiffness, the cable then tends toward straight. The default body_frame_origin="com" places the body origin at the segment midpoint; use the corresponding local offset for an endpoint attachment.

Attachments and fitting. Before tuning stiffness, check the initial and rest orientations and how the cable ends are attached. Fixing only an endpoint’s position allows it to rotate; fixing its orientation as well makes the attachment clamped.

At a cable attachment, the non-rod joint and adjacent rod joint contribute to the endpoint body’s local solve. Stiffening or refining the cable can increase attachment error at a fixed time step and iteration count, even with unchanged attachment gains. Check attachment errors and cable deformation together when tuning rigid_joint_*_ke. Assess time step and iteration sensitivity with those gains held fixed.

Fit stretch, shear, bend, and twist stiffness separately under static loads, then tune damping from oscillation decay after release. Lower stretch stiffness changes the material; use it only if the extra extension is acceptable.

Dahl hysteresis. Use for history-dependent cable bending and twisting. Call register_custom_attributes() on the builder before building the model, then set positive model.vbd.dahl_eps_max and model.vbd.dahl_tau for the intended rod joints before constructing the solver. Both default to zero; the affected bend or twist mode also needs positive stiffness. Tune the Dahl parameters under cyclic loading and unloading. dahl_eps_max scales the hysteretic moment relative to stiffness; dahl_tau sets the deformation scale over which it evolves. See the cable bundle hysteresis example.

Time step, substeps and iterations#

Most shipped examples render at 60 frames per second with 10 substeps, so the solver runs at dt = 1/600 s, with 4 to 20 iterations. Start there with 10 iterations. A smaller dt improves stiffness and convergence, because every substep adds a full set of iterations and a detection pass. It also costs the same factor in runtime. Do not go much below 1/3000 s: positions are stored in float32, and when the per-step update becomes small compared to the position value, part of each update is rounded away (one float32 unit at 1 m is about 1.2e-7 m). Before that floor, reducing dt does more for stiffness than adding iterations. See Rules of Thumb for the order of operations.

Contact Handling#

Particle contacts#

Four distances govern a contact. The interaction distance is where the contact energy starts to act. The detection slack is the extra reach of detection, so pairs are known before they interact. The query radius is their sum. The motion budget is how far one side of a pair may move between two detections under the Divide and Truncate scheme (DAT), the solver’s penetration-free truncation (Penetration-free truncation (DAT)); it scales with the query radius. Self-contact pairs are vertex-triangle and edge-edge pairs of the same particle system; rigid-soft pairs are particles, edges or faces against a body or static shape.

Interaction distance, detection slack, query radius and motion budget for self-contact and rigid-soft contact

Left: self-contact, shown for a vertex-triangle pair; edge-edge pairs use the same distances. particle_self_contact_margin is the interaction distance and particle_self_contact_gap is added on top of it: the two stacked arrows sum to the query radius, drawn once more as the horizontal arrow. Right: rigid-soft contact, with the particle resting at the interaction distance particle_radius plus the shape margin; the pipeline’s soft_contact_gap is a detection band beyond that.#

In the names of the SolverVBD constructor arguments, the CollisionPipeline constructor arguments and the model arrays, the four distances are:

Distance

Self-contact

Rigid-soft contact

Interaction distance

particle_self_contact_margin (solver)

particle_radius + shape margin

Detection slack

particle_self_contact_gap (solver)

soft_contact_gap (pipeline)

Query radius

particle_self_contact_margin + particle_self_contact_gap

particle_radius + margin + soft_contact_gap

Motion budget per side

0.5 x dat_conservative_bound_relaxation x query radius

0.5 x dat_conservative_bound_relaxation x the smallest rigid-soft query radius in the model, with rigid_soft_enable_dat

The force kernels act below the interaction distance; detection finds pairs out to the query radius. The default self-contact schedule detects before and after the inertial step of every substep, so the motion budget is refreshed twice per substep.

What DAT does to motion. DAT works on the displacement each vertex has accumulated since the last detection, and it has two distinct parts. Its knobs are SolverVBD constructor arguments (VBD-Specific Parameters): dat_conservative_bound_relaxation is the relaxation factor used below (default 0.85), particle_self_contact_margin and particle_self_contact_gap set the self-contact query radius, rigid_soft_enable_dat turns on the rigid-soft part, and collision_frequency_type with collision_frequency sets how often detection refreshes the budget.

Planar truncation acts per detected pair. At the detection-time positions DAT places a division plane between the two primitives. The plane splits the gap in proportion to how far each side wants to move toward the other, and each side keeps at least 5 percent of the gap. Each vertex of the pair may then move only up to its own side of the plane, scaled by the relaxation factor: motion toward the plane is cut at the plane, motion away from it or along it passes untouched. A pair whose primitives already intersect at detection time has no plane and is left unconstrained in that pass.

Radius truncation acts on every particle, pair or no pair. The length of the accumulated displacement is capped at half of the relaxation factor (dat_conservative_bound_relaxation) times the query radius. It is isotropic: a vertex that wants to move further is slowed down in every direction, not only along a contact normal. This cap is what makes the scheme safe between detections: two primitives farther apart than the query radius at detection cannot meet before the next detection, because each side moves at most half of the relaxation factor times that radius.

Rigid-soft DAT (rigid_soft_enable_dat, off by default) applies both parts to contacts between soft geometry and shapes. Both sides of a contact are truncated against the same plane: the soft vertices as above, the rigid body by scaling its whole pose update, translation and rotation together. The budget is half of the relaxation factor times the smallest rigid-soft query radius (the pipeline’s soft_contact_gap plus particle radius plus shape margin). It is fixed when the solver is built, and it caps every dynamic body and particle in the model while the option is on. Kinematic bodies are not truncated. A pair without a certifiable separating plane freezes. The option needs a solver-owned collision pipeline; for fast bodies raise the pipeline gap first, then the detection frequency (a RIGID slot on ITERATIONS needs contact matching in the pipeline; see contact matching on the solver page). When self-contact is enabled too, the RIGID and SOFT_SELF_CONTACT slots must use the same schedule type, and the same frequency for ITERATIONS; the solver raises otherwise.

Both parts remove motion. A cloth that falls fast onto a shape, or a sheet pressed between fingers, can therefore move slower than physics would predict, hover at a fixed offset, or stop. This is DAT damping. The radius cap grows with the query radius, so the cure is more room: a larger gap first, then a larger margin.

Planar division for a vertex-triangle pair in four displacement scenarios

Planar truncation for a vertex-triangle pair, redrawn after the DAT paper. Top left: vertex \(v\), triangle \((v_a, v_b, v_c)\) and the normal from the closest point on the triangle to \(v\). The dashed line is the division plane. Top right: both sides approach, \(v\) faster, so the plane shifts toward the triangle and gives \(v\) the larger share of the gap; the dashed parts of the displacements, which would cross the plane, are truncated. Bottom left: \(v\) moves away and the triangle approaches, so the plane sits close to \(v\) and the triangle gets almost the whole gap. Bottom right: motion that stays on its own side of the plane, along it or away from it, passes untouched.#

Setting margin and gap#

There is no single right value. The choice trades physical fidelity against cost and DAT damping.

Fidelity first. The interaction distance is where the contact force starts, so it is the separation you see between unloaded surfaces. Two cloth layers rest about one margin apart; a particle rests at about its radius plus the shape margin from a shape; under load the separation shrinks by the load over the contact stiffness. Pick the interaction distance from the thickness you want to show. Pairs more than two rings apart are contact candidates, so an interaction distance larger than their rest distance creates contact forces at rest. Where a mesh is close to itself by design, such as seams or layered panels, use particle_rest_shape_contact_exclusion_radius instead of a smaller margin.

Change

Cost

Separation

DAT damping

Other effects

Raise the margin

More active pairs

Larger visible gaps

Less (larger budget)

Rest neighbours beyond the topological filter can push each other

Raise the gap

More candidate pairs, larger buffers, more expensive collision detection

None

Less (larger budget)

Dropped pairs when the buffers overflow

Lower the margin

Less

Tighter contact

More (smaller budget)

Without DAT (rigid-soft default) a step that moves further than the interaction distance can pass through. With DAT on, tunneling stays possible in theory; see Self-contact tunneling

Lower the gap

Less

None

More (smaller budget)

Fewer candidates; without DAT, pairs that close by more than the query radius between two detections are missed

Starting point and adjustments. Take the values of the nearest example in the cloth and soft-body example table; they span 1 mm to 40 mm depending on the scene scale. Then adjust by symptom: a visible gap between layers means a smaller margin; slow, hovering or locked motion means a larger gap first and a larger margin second; dropped pairs (see Self-contact tunneling) mean larger buffers. Keep the gap at about half to one margin unless speed asks for more.

Stiffness, damping and friction together. The contact stiffness sets how far a contact penetrates under load and how stiff the local system becomes. Keep it within about 10x the elastic stiffness of the material it presses on. Contact damping only acts while the gap closes and mostly shrinks the per iteration correction, so it is a second-order knob. Friction scales with the normal force; a contact that is too soft also has too little friction, which shows as slipping in grasps.

Body-body contacts#

Detection first. Shape geometry, such as a capsule radius, describes the body; margin shifts the contact surface; gap extends the detection distance. See margin and gap semantics. The builder’s rigid_gap defaults to 0.1 m for shapes without an explicit gap, which can be large beside a centimeter-scale cable. Set builder.default_shape_cfg.gap or a shape’s ShapeConfig.gap before adding shapes, then check fast motion between collision passes. Changing the builder default later does not update existing shapes. Choose a gap that detects the required contacts reliably. If those contacts exceed the available capacity, increase the relevant contact buffer.

Collision filtering. Directly jointed body pairs are filtered by default (collision_filter_parent), but other nearby cable segments can still overlap, especially with short segments or thick capsules. Unfiltered overlaps can produce unwanted contact forces from the first step. Inspect the initial geometry and exclude intentionally overlapping neighboring segments whose collision response is unnecessary throughout the expected motion, using ModelBuilder.add_shape_collision_filter_pair() before finalize(). Correct unintended initial overlaps, and preserve self-contact needed for folding and tight bends. Filtering additional neighboring pairs whose collision response is unnecessary can also reduce contact processing and buffer demand.

Normal support before friction. Check penetration and normal-load support before increasing mu. VBD mixes body-body ke and kd with an arithmetic mean and mu with a geometric mean, so one stiff shape can dominate normal stiffness while a shape with mu=0 makes its pairs frictionless. Unexpected slip can also come from missing contacts or an incomplete solve. Leave friction_epsilon at its default initially; it does not change the Coulomb limit set by mu.

Body coloring. VBD updates bodies in the same color group in parallel. Automatic coloring uses joint and tendon connections, but does not include contacts. Contacting bodies may share a color, which can slow convergence. For small scenes with predictable contacts, consider splitting existing groups in builder.body_color_groups to separate frequently contacting bodies. Make these edits after builder.color() and before builder.finalize(); calling color() again replaces them.

Split existing groups without dropping or duplicating bodies; this preserves the original joint/tendon separation. Custom groups are not fully validated. More colors reduce parallelism, so compare convergence and runtime.

Contact history#

Set contact_matching on CollisionPipeline and rigid_contact_history on SolverVBD. Enabling history does not enable matching automatically.

Matching identifies corresponding contacts across collision passes. With rigid_contact_history=True, VBD uses matches to warm-start contacts between steps. When a solver-owned pipeline re-detects rigid contacts during a step (ITERATIONS), matches preserve ALM state within that step, even with rigid_contact_history=False.

For graph capture with contact history, construct the collision pipeline before SolverVBD so history buffers are preallocated, or run one uncaptured solver step with the intended contact buffers before capture.

With history enabled, passing a pipeline with matching disabled to SolverVBD raises ValueError at construction. With externally supplied contacts, VBD raises RuntimeError during rigid-contact refresh if the buffer lacks match-index data or reports neither "latest" nor "sticky" as its matching mode.

With compliant ALM and rigid_contact_history=True, the matching modes behave as follows:

contact_matching

Behavior

"disabled"

Incompatible with history. Use rigid_contact_history=False.

"latest"

Keep fresh geometry. Restore normal and projected tangential multipliers for valid matches.

"sticky"

Replay eligible contact geometry. Restore normal multipliers; tangential memory comes from the replayed material anchor. This matching mode is experimental.

For a baseline, set rigid_contact_history=False explicitly. Use contact_matching="disabled" unless rigid detection uses ITERATIONS, which requires "latest" or "sticky" matching.

Contact history can improve convergence and reduce jitter or unintended slip for persistent contacts; it does not change the authored friction coefficient. Compare latest matching plus history against the baseline, measuring drift, penetration, and total collision plus solver time. Treat sticky matching as a separate choice: test sliding, separation, and changing contact normals as well as rest. Increasing contact_matching_pos_threshold allows matching across larger position changes; decreasing contact_matching_normal_dot_threshold allows matching across larger changes in contact normal. See Contact Matching. Neither mode guarantees improvement or replaces convergence work.

Contact matching and solver history add computation and memory costs. Contact matching does not guarantee a deterministic GPU rigid-contact solve.

Warm starts require correspondence to the contact set actually solved in the previous step. New or unmatched contacts start cold; this is normal. Alternating contact buffers or colliding twice between steps can cold-start contacts even with history enabled; see SolverVBD for the contact-generation contract.

When a substep reuses contacts without rerunning collision detection, you can call set_rigid_history_update() with False to skip rebuilding contact lists and reinitializing contact records. Reuse the last refreshed contact buffers unchanged, with body mass and kinematic status unchanged. Per-step contact/joint updates, solving, and enabled history snapshots still run. Keep refresh enabled after detection or reset. The flag resets to True after each step and is independent of rigid_contact_history.

Contact capacity#

Global storage and per-body adjacency have independent capacities. Increasing one does not increase the other. CollisionPipeline exposes:

  • rigid_contact_max: global rigid contact storage.

  • max_triangle_pairs: intermediate triangle pairs for mesh/heightfield collision and global contact reduction.

  • contact_reduction_hashtable_size_factor: global contact-reduction hash-table capacity relative to max_triangle_pairs. Increase it for hash-table fill/failure warnings.

VBD’s rigid_body_contact_buffer_size (default 64) controls body-body contact entries per rigid body. Overflow drops contributions from that body’s solve. Keep CollisionPipeline(verify_buffers=True) diagnostics enabled while sizing collision buffers and check VBD’s per-body overflow warnings separately. Measure peak demand during impacts, dense packing, and resets, then allocate headroom; a count at capacity may already be clipped. Check filters and an unnecessarily large detection gap before allocating more memory.

For rigid-soft contacts, rigid_body_particle_contact_buffer_size (default 256) is a separate per-body budget. Its overflow falls back to scanning all stored soft contacts, preserving reactions at extra cost. It cannot recover contacts lost from the global CollisionPipeline.soft_contact_max storage.

Particle self-contact uses particle_vertex_contact_buffer_size and particle_edge_contact_buffer_size; see Self-contact tunneling for overflow checks.

Best Practices#

What Can Go Wrong and How to Fix It#

Symptom

Likely cause

Entry

Cloth or a soft body jitters at a few vertices

Stiffness ratio: contact far above the elasticity

Example 1: contact stiffness too high compared to the elasticity

Raising one stiffness does not make the object stiffer

Stiffness ratio: the deformation moves into the terms that stayed soft

Example 2: stiffening only the membrane does not stiffen the object

A fine mesh is soft, laggy or slow to settle

About one ring of information per iteration

A fine mesh converges slowly

Motion is slower than expected, hovers or locks near contact

DAT damping, small query radius

DAT damping: slow, hovering or locked motion

Explosion or NaN at a few vertices

Bad elements in the asset

Bad asset

A smaller dt made things worse

Compute budget, float32 precision

A smaller time step made things worse

Cloth passes through itself

Candidate buffers overflow, or an exclusion filter

Self-contact tunneling

Unexpected rigid-body or cable motion at startup

Initial overlaps, rest geometry, or joint attachment frames

Startup checks

Excessive cable stretch or joint displacement

Loads, stiffness, rest geometry, attachments, or incomplete convergence

Deformation checks

Excessive body-body penetration, slip, or jitter

Detection, capacity, normal support, friction, or incomplete convergence

Contact checks

Rigid-body or cable behavior changes unexpectedly after edits or reset

Stale solver caches or contact history

Update and reset checks

Cloth and soft bodies#

Jitter: where to look first#

Work through these steps in order and stop at the first one that fixes it.

  1. If the scene has self-contacts, check whether the self-contact stiffness is disproportionately high. Keep the collision stiffness within 10x of the elasticity stiffness: soft_contact_ke against tri_ke for cloth or k_mu for soft bodies. This is the stiffness ratio (Stiffness ratio).

  2. If not fixed, tune the self-contact damping, soft_contact_kd.

  3. If it is still not fixed, or there is no contact, increase the elasticity damping: bending damping edge_kd for cloth, tet damping k_damp for soft bodies.

  4. Increasing the iterations or the collision detection frequency also helps, but both increase the computational cost. Only use them as the last resort. Iterations: iterations. Detection frequency: set the self-contact slot to the ITERATIONS schedule and give it a frequency,

    solver = newton.solvers.SolverVBD(
        model,
        particle_enable_self_contact=True,  # plus your margin and gap
        collision_frequency_type={
            newton.solvers.SolverBase.CollisionSlot.SOFT_SELF_CONTACT:
                newton.solvers.SolverBase.CollisionFrequencyType.ITERATIONS
        },
        collision_frequency={newton.solvers.SolverBase.CollisionSlot.SOFT_SELF_CONTACT: 2},
    )
    

    which detects before the inertial step and then before every second iteration, in place of the pass after the inertial step. With rigid_soft_enable_dat on, give the RIGID slot the same type and frequency; the solver raises in step otherwise, because the two DAT families share one motion reference. A RIGID slot on ITERATIONS re-detects between iterations, and the solver carries each contact’s multipliers onto the new contact set through the pipeline’s contact matching. Build the pipeline with contact_matching="latest" (or "sticky"); with the default "disabled" the constructor raises ValueError. The solver page explains the mechanism (contact matching).

Explosion: what to check, in this order#
  1. Asset quality. Add the mesh with validate_mesh=True and read the warnings. See Bad asset.

  2. Density too small? A light vertex next to a stiff element or contact receives a large acceleration. Check density or mass against the real object; do not lower them to speed things up.

  3. Stiffness too high? Compare the elastic and contact stiffness with the examples in the cloth and soft-body example table. Above about 1e6 expect trouble.

  4. Initially penetrating? The validator does not detect intersections; it checks element quality and manifoldness only. Inspect the start pose with the viewer debugging tools. Layers that start inside the interaction distance receive a large separating push in the first steps.

  5. Initial stretch or velocity too high? Compare the start positions with the rest shape, and check vel and any prescribed motion in the first frame.

Stiffness ratio#

Symptom. One stiffness stands far above the others, and raising it made things worse instead of better: a few vertices jitter and do not settle with more iterations, or the object does not get stiffer and even wobbles or buckles more.

Cause. The stiffness ratio. Every term that acts on a vertex enters the same local system, so one stiffness far above the others impedes the overall convergence and makes some of the elasticity look less stiff. The pair depends on the scene: contact against the elasticity of the material it presses on, membrane against bending, volume against shear (the k_mu, k_lambda row of the model table), and in general any two terms that share vertices.

Fix. Keep the stiffnesses of terms that share vertices within about 10x of each other: lower the one that stands out, or raise the others with it. The rule is a starting point, not a universal one; after the change, check the behaviour the stiff term was there for, such as a grasp that must hold or a sheet that must not stretch. The two cases below are the common ones.

Example 1: contact stiffness too high compared to the elasticity#

Symptom. Cloth layers or a grasped sheet jitter at a few vertices. The motion is fast, local, and does not settle when you add iterations.

Cause. The stiffness ratio: the contact stiffness is too high compared to the elasticity.

Fix. Bring soft_contact_ke to within about 10x of the membrane stiffness (tri_ke) or the volumetric stiffness (k_mu), then check that grasps still hold. Lower contact stiffness also means lower friction force, so a grasp can slip; in the pouch scene below, lowering the contact stiffness to 2x the membrane stiffness lost the grasp.

Animation of five stacked cloth layers with contact stiffness 30,000 N/m (jittering) and 4,000 N/m (still)

Five stacked cloth layers, the last two seconds of a seven-second rest, same time in both runs. Membrane stiffness 2,000 N/m, self-contact margin 8 mm, gap 8 mm, dt = 1/300 s, 20 iterations. Left: contact stiffness 30,000 N/m. Right: 4,000 N/m. The scene is a test scene, not a shipped example.#

Local motion of free vertices, 5 to 50 Hz band, 2 to 7 s

Contact stiffness 30,000 N/m

Contact stiffness 4,000 N/m

Mean amplitude (RMS over vertices) [mm]

0.93

0.016

95th percentile vertex [mm]

1.93

0.020

Worst vertex [mm]

4.79

0.024

“Local motion” is the motion of a vertex relative to the mean of its mesh neighbours, so a rigid shift of the whole stack does not count.

More iterations did not remove this kind of jitter. On a pinched pouch (membrane 3,000 N/m, contact 50,000 N/m, dt = 1/300 s) the mean mesh speed over 0.1 s was 0.123 m/s at 20 iterations, 0.023 m/s at 40, 0.141 m/s at 100 and 0.205 m/s at 200.

Reproduce. A similar setup is example_vbd_soft_rigid_contact with soft_contact_ke raised from 1e5 to 1e6 while cloth_tri_ke stays at 1e5.

Example 2: stiffening only the membrane does not stiffen the object#

Symptom. You raise tri_ke and tri_ka by orders of magnitude and the object still wobbles; at the highest values it buckles and wrinkles more.

Cause. The stiffness ratio: the membrane is far stiffer than bending. Once the membrane is near inextensible, the deformation energy moves into bending, which stayed soft.

Fix. Scale edge_ke together with tri_ke and tri_ka. Stiffening both gives a monotonic stiffening with diminishing returns past the baseline.

Animation of a pinned bag at five membrane stiffness values from 1e3 to 1e7

A bag with rigid contents, pinned at the rim, rim driven sideways at 0.85 Hz, seconds 3 to 6 of the run. Only the membrane stiffness changes, edge_ke stays at 200 N. dt = 1/600 s, 10 iterations. The x100 bag buckles at the bottom. The scene is a test scene, not a shipped example.#

Multiplier

Stretch score, membrane only

Bend score, membrane only

Stretch score, both scaled

Bend score, both scaled

x0.01

0.114

0.012

0.012

0.033

x0.1

0.015

0.008

0.007

0.018

x1

0.006

0.015

0.006

0.015

x10

0.006

0.045

0.006

0.015

x100

0.006

0.179

0.006

0.015

Baseline x1: tri_ke 1e5, tri_ka 2e4, edge_ke 200. Scores are mean deformation measures over the last second of each run; lower means stiffer. Scaling only the membrane leaves the stretch score flat from x1 on and raises the bend score twelve-fold. Scaling both keeps every score flat.

Reproduce. A similar setup is example_cloth_hanging with tri_ke and tri_ka multiplied by 100 and edge_ke unchanged, compared with all three multiplied.

A fine mesh converges slowly#

Symptom. After refining the mesh, the object looks softer, lags behind contacts, or needs many more frames to settle, with unchanged material values.

Cause. Information moves about one ring per iteration. A block is solved from the current values of its neighbours, colour by colour, so a change reaches the next ring within one iteration and crosses \(N\) rings in about \(N\) iterations. At fixed iterations a finer mesh is therefore less converged at the end of every step.

A triangle mesh patch with rings around one vertex coloured by their ring distance from it

A change at one vertex reaches ring \(k\) after about \(k\) iterations. Refining the mesh by a factor of two doubles the number of rings across the object and so, roughly, the iterations needed for the same convergence.#

Fix. Use the lowest resolution that expresses the deformation you need. When you refine, raise iterations roughly in proportion to the number of rings across the object.

DAT damping: slow, hovering or locked motion#

Symptom. Near contacts, cloth falls slower than gravity, hovers at a fixed offset, or a pinched region stops moving.

Cause. The motion budget between detections is too small for the motion the scene asks for. The radius truncation caps the displacement since the last detection at 0.5 x relaxation x query radius, and the planar truncation stops a vertex at its division plane, so a small margin and gap cap the speed.

Fix. Raise particle_self_contact_gap first, then particle_self_contact_margin, following Setting margin and gap. For rigid-soft contact raise the pipeline’s soft_contact_gap. More frequent detection also refreshes the budget more often, at a cost.

Bad asset#

Symptom. NaN or an explosion at a few vertices from the first frames, jitter at rest in one region, or warnings when the mesh is added.

Cause. Degenerate or sliver elements make the local solve ill-conditioned.

Fix. Run the built-in checker, then apply isotropic explicit remeshing. The checker is opt-in:

builder.add_cloth_mesh(
    pos=pos, rot=rot, scale=1.0, vel=wp.vec3(0.0),
    vertices=vertices, indices=indices, density=0.1,
    validate_mesh=True, label="shirt",
)
builder.add_soft_mesh(
    pos=pos, rot=rot, scale=1.0, vel=wp.vec3(0.0),
    vertices=vertices, indices=tets, density=100.0,
    k_mu=1.0e5, k_lambda=1.0e5, k_damp=1.0,
    validate_mesh=True, label="sponge",
)

It never raises; it warns and adds the mesh anyway. The same checks are available directly as newton.utils.validate_triangle_mesh() and newton.utils.validate_tet_mesh(). The checker inspects element quality and manifoldness; it does not detect intersections between or inside meshes. Thresholds at the time of writing:

Mesh

Check

Threshold

Triangles

Small area

below 1 mm² (1e-6 m²)

Triangles

Sliver, longest edge over shortest altitude

above 20

Triangles

Minimum interior angle

below 5°

Triangles

Non-manifold edge (always checked by the adjacency builder)

edge shared by more than two faces

Tetrahedra

Inverted element

signed volume below 0

Tetrahedra

Small volume

below 1 mm³ (1e-9 m³)

Tetrahedra

Sliver, shape quality \(\eta\) (1 for a regular tetrahedron)

below 0.01

Tetrahedra

Non-manifold face

face shared by more than two tetrahedra

A warning has this form (the numbers are illustrative):

NewtonWarning: Mesh quality warning [bag] (873 vertices, 1544 triangles):
  - 4 sliver triangle(s) with aspect ratio > 20.0 (worst: 27.3)
  - 1 triangle(s) with minimum angle < 5.0° (smallest: 4.0°)
Consider remeshing the input geometry.

Apply isotropic explicit remeshing, for example MeshLab’s filter Remeshing: Isotropic Explicit Remeshing, with a target edge length that is uniform over the asset and larger than the interaction distance. Then add the mesh with validate_mesh=True again. Remeshing at a coarse target can still leave slivers; the checker tells you.

A smaller time step made things worse#

Symptom. After halving dt the simulation is slower, and either nothing improved or the object drifts and loses detail over long runs.

Cause. Two effects. First, a smaller dt costs more substeps per second, each with collision detection; at a fixed compute budget it takes iterations away from each substep. Second, positions are float32. A small dt gives a small per-step update compared to the position value, so part of each update is rounded away, and the loss accumulates over time.

Fix. Start at dt = 1/600 s with 10 iterations. Reduce dt while it visibly helps, but not below about 1/3000 s. Beyond that, spend the budget on iterations, or move the scene closer to the origin so that positions are small numbers.

Self-contact tunneling#

Symptom. Cloth passes through itself, or one layer of a stack drops through another, although self-contact is enabled.

Cause. The candidate buffers overflow, or a filter excludes the pair. Each vertex stores at most particle_vertex_contact_buffer_size vertex-triangle candidates (default 32) and each edge at most particle_edge_contact_buffer_size edge-edge candidates (default 64). Pairs beyond the capacity are dropped silently, and a dropped pair has no force and no division plane; only the radius cap still limits its vertices. The topological filter and the rest-shape exclusion radius also remove pairs, by design.

Fix. Raise the two buffer sizes, and lower the gap if it is far above the margin. The detector records the number of pairs found per vertex and per edge, including the dropped ones, next to the buffer sizes. A count above the buffer size is a measured overflow. The result struct is reachable as solver.trimesh_collision_info (fields vertex_colliding_triangles_count, vertex_colliding_triangles_buffer_sizes, edge_colliding_edges_count and edge_colliding_edges_buffer_sizes); it is an experimental type and can change. If a filter is the cause, lower particle_rest_shape_contact_exclusion_radius or check the external filter maps you passed.

Rigid bodies and cables#

Unexpected motion at startup#

Check initial overlaps, cable rest geometry, and joint attachment frames. Adjust the initial geometry to remove unintended overlaps. Exclude intentionally overlapping neighboring segments only if their collision response is unnecessary throughout the expected motion. See cable inputs and rigid contact setup.

Excessive stretch or joint displacement#

Check loads, stiffness inputs, rest geometry, and attachments. Also check time step and iteration sensitivity with those inputs held fixed. Finite stiffness permits some deformation; see cable materials and attachments and non-rod joint gains.

Excessive penetration, unexpected slip, or jitter#

Check contact detection, including fast motion between collision passes, buffer overflow, and normal-load support. For unexpected slip, check both shapes’ mu values and joint/contact convergence. For persistent contacts, compare contact history against a baseline. See rigid contact setup and contact capacity.

Unexpected behavior after edits or reset#

Check whether a model edit requires notify_model_changed() or rebuilding the solver. For resets, clear solver and collision-pipeline history and regenerate contacts at the final pose. See model changes and reset.

Model Changes and Reset#

Create a new SolverVBD after changing construction-only settings. When using the tiled particle solve (particle_enable_tile_solve), also rebuild after changing triangle or edge stiffness. For rigid bodies and cables, rebuild after changing joint topology or DOF layout, or the non-rod reference configuration used to initialize drive/limit angle offsets.

Rod stiffness and damping can be updated during simulation. VBD caches the rod gains and requires equal X/Y shear and X/Y bend coefficients on each rod joint. After editing model properties, call notify_model_changed() with the matching flag before the next step:

When editing mass or inertia directly, keep model.body_inv_mass and model.body_inv_inertia consistent with them. The notification refreshes solver caches; it does not recompute these model arrays.

When VBD integrates rigid bodies, it advances state.body_q and state.body_qd directly. step() does not synchronize state.joint_q or state.joint_qd; call eval_ik() explicitly when joint coordinates or velocities are needed from body state.

For an episode reset, call reset() to clear rigid solver history. By default, it also restores particle positions and velocities and VBD-integrated body poses and velocities from the model defaults. For a custom reset, assign state.body_q and state.body_qd and use flags=0 to preserve them. Set velocities to zero for a reset to rest. If authoring the reset in joint coordinates, run eval_fk() after reset to populate the body poses and velocities. Finish all state edits before the next step.

Also call CollisionPipeline.reset_contact_matching() with the same world mask to discard correspondence and sticky geometry. Regenerate contacts at the final pose and leave rigid-history refresh enabled for the next contact-bearing step. See reset() for partial-world and coupled-solver behavior.

Examples#

Cloth and soft bodies#

Start from an example, not from material data. Stiffness values derived from measured material properties (Young’s modulus, Poisson ratio, thickness) are usually far too high for VBD to converge at practical iteration counts. Each shipped example below is a working combination of material, contact and time-step values for its scenario. Open the example file for the complete configuration; the examples are maintained with the solver, so their values are the ones to copy. The resemblance given for each scenario is visual, at the example’s scale, not a material measurement.

Scenario

Example

What it demonstrates

Cloth drape

cloth_hanging (run with --solver vbd)

A planar cloth fixed on one side hangs under gravity and settles on the ground. Resembles a heavy cotton sheet at a coarse scale.

Cloth with a robot

cloth_franka

A Franka arm folds a shirt: VBD cloth coupled with a Featherstone robot, in centimetre scale. Resembles cotton cloth handled by a gripper.

Stiff sheets

cloth_poker_cards

52 cards stack on a cube and are knocked off by a sphere; high bending stiffness keeps their shape. Resembles playing cards, cardboard.

Inextensible sheet

cloth_stiff_material_hanging

A curtain pinned along one edge swings and settles with very stiff membrane values. Resembles a plastic membrane that drapes but does not stretch.

Soft-body beams

softbody_hanging

Four tetrahedral beams hang with damping from 1e4 down to 1e1, showing what damping does to Neo-Hookean behaviour. Resembles rubber to foam beams, depending on the damping.

Soft box with ribbon

softbody_gift

Four soft blocks tied by cloth straps drop and bounce while the straps hold the stack together. Resembles a foam box with a thin ribbon.

Soft-body grasp

softbody_franka

A Franka arm grasps, lifts and drops a tetrahedral rubber duck. Resembles a firm rubber block in a gripper.

Thin bags with contents

vbd_soft_rigid_contact

Six cloth bags each hold a different rigid shape type; the bags settle, then lift their contents, so rigid-soft contact can be inspected per shape type. Resembles plastic bags holding rigid objects.

Tarp under fast impacts

vbd_dat_rigid_soft

Heavy, fast projectiles hit sheets that catch them; rigid-soft DAT keeps the cloth outside the bodies where contact stiffness alone cannot. Resembles a tarp struck by thrown objects.

Gripper on a soft pad

vbd_gripper_soft_grid

A parallel-jaw gripper pinches a two-triangle grid on its interior edge and lifts it, which needs full-surface rigid-soft contact. Resembles a foam pad pinched and lifted.

Cables#

These examples demonstrate cable setup and behavior:

Scenario

Example

What it demonstrates

Initial and rest shapes

cable_initial_rest_pose

Cables relax toward rest shapes that differ from their initial geometry.

Driven twist

cable_twist

Twist propagates through bent cables with different angular stiffnesses.

Bending hysteresis

cable_bundle_hysteresis

Cyclic bending leaves residual curvature; compare with --no-dahl for the elastic response.

Cable-driven table

cable_cross_slide_table

Driven pulleys move two carriages through a cable loop with wrapping contact.

Dense cable pile

cable_pile

A cable pile settles under gravity with cable-cable contact and friction.

Run, for example, python -m newton.examples cable_initial_rest_pose. Example settings are specific to each scene; check convergence before reusing them.

Rules of Thumb#

The following rules apply to cloth and soft bodies:

  • Use the lowest resolution that expresses the deformation you need. Raise it only when the degrees of freedom are not enough.

  • Use a uniform triangulation with good element quality. Check it with validate_mesh=True, and apply isotropic explicit remeshing when it warns.

  • Start at dt = 1/600 s and 10 iterations, and adjust from there. If you start from a shipped example, keep its values.

  • Keep a copy of the last working configuration and change one parameter at a time, so you know what each change did and can go back. The Simulation Tuning page explains the one-knob rule.

  • Start from the paradigm example closest to your task: cloth_hanging for cloth simulation, cloth_franka for cloth manipulation, softbody_hanging and softbody_franka for soft bodies, vbd_gripper_soft_grid for grippers.

  • Keep the collision stiffness within 10x of the elasticity stiffness. A contact that is far stiffer than the material it presses on jitters.

  • Always pass the self-contact margin and gap together, and keep the gap at about half to one margin. Raise the gap before the margin when motion looks damped near contacts.

  • Scale membrane and bending stiffness together. Stiffening only the membrane moves the deformation into bending.

  • Do not blindly crank up stiffness. It can be challenging for VBD to simulate a stiffness higher than about 1e6; a static drape can still work at higher values, as cloth_stiff_material_hanging shows at 1e8. If your stiffness is already very high, cranking it up further is unlikely to help and only creates more artifacts from the stiffness ratio. Look into, in this order: the resolution (is such a high resolution necessary? reducing it immediately improves convergence and so increases stiffness); the stiffness ratio (one stiffness far above the others impedes the overall convergence and makes some of the elasticity look less stiff, see Stiffness ratio); then the time step (reducing it always helps, but not below about 1/3000 s; more iterations also help, and before dt gets too small, reducing dt improves stiffness more than adding iterations).

  • Start from an example’s values, not from measured material data. Derived stiffness values are usually too high for VBD to simulate.

  • Increasing the iterations or the collision detection frequency helps, but both increase the computational cost. Use them as the last resort.