๐Ÿงฎ Non-rigid Coupling#

Genesis allows you to combine multiple continuum and rigid-body solvers in the same scene โ€“ e.g. MPM snow interacting with SPH water, deformable FEM tissue colliding with surgical tools, or rigid props splashing into a granular bed. All cross-solver interactions are orchestrated by the gs.engine.Coupler class.

This page explains:

  • the architecture of the Coupler and how it decides which solver pairs are active;

  • the impulse-based collision response that governs momentum exchange;

  • the meaning of friction, restitution, softness and other coupling parameters;

  • a quick reference table of currently supported solver pairs; and

  • usage examples showing how to enable/disable specific interactions.


1. Architecture overview#

Internally the simulator owns one Coupler instance which keeps pointers to every solver. During each sub-step the simulator executes:

  1. coupler.preprocess(f) ย ย  โ€“ e.g. surfacing operations for CPIC.

  2. solver.substep_pre_coupling(f) โ€“ advance each individual solver.

  3. coupler.couple(f) โ€“ exchange momentum between solvers.

  4. solver.substep_post_coupling(f) โ€“ solver postprocessing after collision.

Because all solver fields live on Taichi data-structures the Coupler can call Taichi @kernels that touch the memory of several solvers without data copies.

1.1 Activating a coupling pair#

Whether a pair is active is determined statically once when Coupler.build() is called:

self._rigid_mpm = rigid.is_active() and mpm.is_active() and options.rigid_mpm

2. Impulse-based collision response#

2.1 Signed distance & influence weight#

For every candidate contact the Coupler queries the signed distance function sdf(p) of the rigid geometry. The softness parameter produces a smooth blending weight

\[ \text{influence} = \min\bigl( \exp\!\left(-\dfrac{\;d\;}{\epsilon}\right) ,\;1 \bigr) \]

where d is the signed distance and ฮต = coup_softness. Large softness values make the contact zone thicker and produce gentler impulses.

2.2 Relative velocity decomposition#

For a particle/grid node with world velocity v and a rigid body velocity vแตฃ, the relative velocity is

\[ \mathbf r = \mathbf v - \mathbf v_{\text{rigid}}. \]

Split r into its normal and tangential components

\[ r_n = (\mathbf r \cdot \mathbf n)\,\mathbf n, \quad r_t = \mathbf r - r_n \]

with n the outward surface normal.

2.3 Normal impulse (restitution)#

If the normal component is inward (\(r_n<0\)) an impulse is applied so that after the collision

\[ r_n' = -e\,r_n, \quad 0 \le e \le 1, \]

where e = coup_restitution is the restitution coefficient. e=0 is perfectly inelastic, e=1 perfectly elastic.

2.4 Tangential impulse (Coulomb friction)#

Friction is implemented by scaling the tangential component:

\[ r_t' = \max\!\bigl( 0,\;|r_t| + \mu \, r_n\bigr) \; \dfrac{r_t}{|r_t|}\,, \]

with ฮผ = coup_friction. This is an impulse-based variant of Coulomb friction that ensures the post-collision tangential speed never exceeds the sticking limit.

2.5 Velocity update and momentum transfer#

The new particle/node velocity is then

\[ \mathbf v' = \mathbf v_{\text{rigid}} + (r_t' + r_n') \times \text{influence} + \mathbf r\,(1-\text{influence}). \]

The change of momentum

\[ \Delta\mathbf p = m\,(\mathbf v' - \mathbf v) \]

is applied as an external force on the rigid body

\[ \mathbf F_{\text{rigid}} = -\dfrac{\Delta\mathbf p}{\Delta t}. \]

Thus Newtonโ€™s third law is satisfied and the rigid body responds to fluid impacts.


3. Supported solver pairs#

Pair

Direction

Notes

MPM โ†” Rigid

impulse based on grid nodes (supports CPIC)

MPM โ†” SPH

averages SPH particle velocities within an MPM cell

MPM โ†” PBD

similar to SPH but skips pinned PBD particles

FEM โ†” Rigid

collision on surface vertices only

FEM โ†” MPM

uses MPM P2G/G2P weights to exchange momentum

FEM โ†” SPH

experimental โ€“ normal projection only

SPH โ†” Rigid

robust side-flip handling of normals

PBD โ†” Rigid

positional correction then velocity projection

Tool โ†” MPM

delegated to each Tool entityโ€™s collide()

If a combination is not in the table it is currently unsupported.