Boundaries and rigid-body coupling
The RbdCoupling argument of state.insert_rigid_body lets MPM particles interact with a
rigid-body collider. This is typically used for a container
holding sand, or a moving blade pushing an elastic object. Use BoundaryCondition to choose how the material
moves wrt. the collider's surface.
Coupling modes
There are three RbdCoupling modes:
RbdCoupling::NoneRbdCoupling.NONEselects ordinary rigid-body dynamics, without an MPM boundary.RbdCoupling::MpmOneWay(boundary)RbdCoupling.mpm_one_way(boundary)lets the body affect particles without receiving their reaction. This is typically used for fixed walls or prescribed motion.RbdCoupling::MpmTwoWay(boundary)RbdCoupling.mpm_two_way(boundary)also transfers particle impulses to the body.
With RbdCoupling::MpmTwoWayRbdCoupling.mpm_two_way, pipeline.simulate runs the
rigid-body step, then MPM integrates the coupled body's pose and writes it back. These separate updates don't resolve
all interactions simultaneously. Keep this limitation in mind when combining coupled bodies with
articulated mechanisms or ordinary rigid-body contacts.
Boundary conditions
Import BoundaryCondition from nexus2d::mpm::solver or nexus3d::mpm::solver.
Import BoundaryCondition from nexus3d. Its constructors select the surface response:
BoundaryCondition::stick()BoundaryCondition.stick()removes relative velocity at the surface. The material follows the surface without sliding.BoundaryCondition::slip()BoundaryCondition.slip()removes the normal component of relative velocity and preserves the tangential component.BoundaryCondition::separate(friction)BoundaryCondition.separate(friction)prevents motion into the surface while allowing the material to move away. Thefrictionargument opposes sliding.
- Example 2D
- Example 3D
let sticky = BoundaryCondition::stick();
let sliding = BoundaryCondition::slip();
let separating = BoundaryCondition::separate(0.5);
let sticky = BoundaryCondition::stick();
let sliding = BoundaryCondition::slip();
let separating = BoundaryCondition::separate(0.5);
sticky = BoundaryCondition.stick()
sliding = BoundaryCondition.slip()
separating = BoundaryCondition.separate(0.5)
The BoundaryCondition response is independent of
ColliderBuilder::frictionColliderBuilder.friction and
ColliderBuilder::restitutionColliderBuilder.restitution, which configure
ordinary rigid-body contacts. Changing those coefficients won't change BoundaryCondition.
Creating a ground
The following passes BoundaryCondition::separate(0.5)BoundaryCondition.separate(0.5)
to one-way coupling. It uses
RigidBodyBuilder::fixed and ColliderBuilder::cuboidRigidBodyBuilder.fixed and ColliderBuilder.cuboid
to describe the ground:
- Example 2D
- Example 3D
let coupling = RbdCoupling::MpmOneWay(BoundaryCondition::separate(0.5));
let body = RigidBodyBuilder::fixed().build();
let collider = ColliderBuilder::cuboid(5.0, 0.1).build();
state.insert_rigid_body(body, collider, coupling);
let coupling = RbdCoupling::MpmOneWay(BoundaryCondition::separate(0.5));
let body = RigidBodyBuilder::fixed().build();
let collider = ColliderBuilder::cuboid(5.0, 0.1, 5.0).build();
state.insert_rigid_body(body, collider, coupling);
coupling = RbdCoupling.mpm_one_way(BoundaryCondition.separate(0.5))
body = RigidBodyBuilder.fixed().build()
collider = ColliderBuilder.cuboid(5.0, 0.1, 5.0).build()
state.insert_rigid_body(body, collider, coupling)
Call state.insert_rigid_body during scene construction, before state.finalize.
Also call state.add_particles, so the scene contains the material to simulate.
The viewer.insert_shape method can display the ground, but registering graphics alone doesn't create a boundary.
Compatible particle-in-cell
Nexus uses CPIC (Compatible Particle-In-Cell) by default. This lets transfers account for boundaries separating
particles from grid nodes. Call state.set_mpm_use_cpic(enabled) to change this setting. For a first simulation,
it is recommended to keep enabled = trueenabled = True.
Environment and timestep
The state.finalize method collects coupled boundaries from rigid-body environment 0.
Calling state.add_environment creates more rigid-body environments, without duplicating the MPM material or its
boundaries. Insert an MPM boundary with state.insert_rigid_body, or with state.insert_rigid_body_in(0, ...).
The rigid-body and MPM solvers have independent timesteps. The MPM SimulationParams.dt must match the total
rigid-body time advanced by pipeline.simulate if the results need to stay synchronized.
For example, RbdSimParams.dt = 1 / 120 with state.set_rbd_steps_per_frame(2) matches
SimulationParams.dt = 1 / 60.
The default rigid-body timestep is 1 / 60, so leave state.set_rbd_steps_per_frame(1) when using
SimulationParams(..., 1 / 60).
The state.set_mpm_substeps method subdivides that MPM time; it doesn't change the total SimulationParams.dt.
Call state.set_rbd_gravity after state.finalize. For the material, supply gravity in
SimulationParamsSimulationParams(gravity, dt), or call state.set_mpm_gravity. Keep the two values consistent when gravity should affect both parts of the scene equally.
If particles pass through the ground, check its RbdCoupling value and environment index first. Then check cell_width,
SimulationParams.dt, and state.set_mpm_substeps as described in
MPM timestep and gravity. A visible shape from viewer.insert_shape isn't
evidence that state.finalize registered an MPM boundary.