iMSTK
Interactive Medical Simulation Toolkit
Docs/Dynamical_Models/PbdModel.md
1 # Position Based Dynamics (PBD)
2 
3 Position based dynamics [[pbd]](#pbd) is a first order, particle & constraint based dynamical model. It simulates the dynamics of objects through direct manipulation of particle positions with velocities computed afterwards. This has pro's and cons, a major pro is the stability of such method. During the solve step position can be reprojected implicitly, there is no need for an integratoin step.
4 
5 Pbd in iMSTK is primarily used for deformable & rigid bodies but can also be used for fluids (both gas and liquid). Discussed here are its medical applications.
6 
7 The particular implementation of PBD used in iMSTK is Extended PBD [[xpbd]](#xpbd) and Rigid Body PBD [rpbd](#rpbd).
8 
9 The general pbd pipeline is as follows:
10 
11 - **Integrate Position**: Given the current velocity of every particle, compute the new/tentative position.
12 - **Solve Internal Constraints**: Solve all internal constraints (directly changing positions). Resolving any violations.
13 - **Update Velocities**: Compute new velocities from the displacements produced.
14 
15 ## Surgical Tools
16 
17 To setup a rigid pbd body with capsule geometry:
18 
19 ```cpp
20 // Create the geometry
21 auto rigidGeom = std::make_shared<Capsule>(Vec3d(0.0, 0.0, 0.0), 0.005, 0.015);
22 
23 // Setup the objects visual, collision, and physics geometry
24 auto capsuleObj = std::make_shared<PbdObject>("capsule0");
25 capsuleObj->setVisualGeometry(rigidGeom);
26 capsuleObj->setCollidingGeometry(rigidGeom);
27 capsuleObj->setPhysicsGeometry(rigidGeom);
28 
29 // Setup the model used
30 capsuleObj->setDynamicalModel(pbdModel);
31 
32 // Setup the body
33 capsuleObj->getPbdBody()->setRigid(
34  Vec3d(0.0, 0.0, 0.0), // Position
35  1.0, // Mass
36  Quatd::Identity(), // Orientation
37  Mat3d::Identity() * 0.1); // Inertia
38 
39 scene->addSceneObject(capsuleObj);
40 ```
41 
42 Any geometry will work. Except those that can't be rigidly transformed such as `SignedDistanceField` and `ImageData`.
43 
44 A rigid PBD simulated body dropped into a PBD simulated thin tissue.
45 
46 <p align="center">
47  <img src="../media/rigidCapsuleInThinTissue.png" width="500"/>
48 </p>
49 
50 ## Deformable Membranes
51 
52 For thin deformable membraneous tissues (cloth like) a SurfaceMesh can be used. For this simulation one may use **PbdDistanceConstraint**, **PbdDihedralConstraint**, and/or **PbdAreaConstraint**. The most common setup is just distance and dihedral constraints though. The distance constraints keep the cloth together, whilst the dihedral constraint controls how resistent to bending the membrane is. It's useful to note that tissues can stretch but most cloths cannot (only bend). Hollow organs can also be modelled like this however they have poor recovery from inversion and may fold over. For this reason a deformable can better approximate a hollow organ.
53 
54 2D Tissue Membrane | 2D Cloth
55 :-------------------------:|:-------------------------:
56 ![](../media/PbdModel/tissue1Gif.gif) | ![](../media/PbdModel/cloth2.png)
57 
58 To setup a thin deformabl membrane:
59 
60 ```cpp
61 // Setup the Geometry
62 std::shared_ptr<SurfaceMesh> mesh =
63  GeometryUtils::toTriangleGrid(Vec3d::Zero(),
64  Vec2d(width, height), Vec2i(rowCount, colCount),
65  Quatd::Identity(), 2.0);
66 
67 // Setup the Parameters
68 auto pbdParams = std::make_shared<PbdModelConfig>();
69 pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, 10000.0);
70 pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Dihedral, 0.1);
71 pbdParams->m_gravity = Vec3d(0.0, -0.01, 0.0);
72 pbdParams->m_dt = 0.005;
73 pbdParams->m_iterations = 4;
74 pbdParams->m_linearDampingCoeff = 0.01;
75 
76 // Setup the Model
77 auto pbdModel = std::make_shared<PbdModel>();
78 pbdModel->configure(pbdParams);
79 
80 // Setup the VisualModel
81 auto material = std::make_shared<RenderMaterial>();
82 material->setBackFaceCulling(false);
83 
84 // Setup the Object
85 auto tissueObj = std::make_shared<PbdObject>(name);
86 tissueObj->setVisualGeometry(mesh);
87 tissueObj->getVisualModel(0)->setRenderMaterial(material);
88 tissueObj->setPhysicsGeometry(mesh);
89 tissueObj->setCollidingGeometry(mesh);
90 tissueObj->setDynamicalModel(pbdModel);
91 
92 // Fix the edges
93 for (int x = 0; x < rowCount; x++)
94 {
95  for (int y = 0; y < colCount; y++)
96  {
97  if (x == 0 || y == 0 || x == rowCount - 1 || y == colCount - 1)
98  {
99  tissueObj->getPbdBody()->fixedNodeIds.push_back(x * colCount + y);
100  }
101  }
102 }
103 tissueObj->getPbdBody()->uniformMassValue = 1.0;
104 ```
105 
106 ## Deformable Volumetric Tissue
107 
108 For volumetric deformable tissues discretized with tetrahedrons may be used. With the tetrahedrons one may either use (a) **PbdVolumeConstraint** & **PbdDistanceConstraint** constraints Or (b) use **PbdFemConstraint** constraints. The FEM constraints are more accurate than the volume+distance. However, they are much slower in that one may not be able to achieve the target element count or timestep, iteration count, & stiffness. The volume constraints behave well with sign inversion, recovering well from inverted tetrahedrons quickly. **PbdFemConstraint** can recover from inversion even better than the volume ones but it is costly. Best if you only have a couple tetrahedrons inverting for a short period.
109 
110 Tetrahedral Needle Puncture | Endoscope Tissue Press
111 :-------------------------:|:-------------------------:
112 ![](../media/needleTissue.gif) | ![](../media/PbdModel/tissue3.gif)
113 
114 PBD FEM simulation can be setup with NeoHookean, StVK, Linear, or CoRotation models. NeoHookean is recommended for large deformation but slightly more costly than StVK.
115 
116 ```cpp
117 // Setup the Model
118 auto pbdModel = std::make_shared<PbdModel>();
119 pbdModel->getConfig()->m_dt = 0.005;
120 pbdModel->getConfig()->m_iterations = 4;
121 pbdModel->getConfig()->m_linearDampingCoeff = 0.01;
122 pbdModel->getConfig()->m_gravity = Vec3d(0.0, -0.01, 0.0);
123 
124 // Setup the Geometry
125 std::shared_ptr<TetrahedralMesh> tissueMesh = GeometryUtils::toTetGrid(center, size, dim);
126 
127 // Setup the Object
128 auto tissueObj = std::make_shared<PbdObject>(name);
129 
130 // Setup the visuals
131 auto visuals = tissueObj->addComponent<VisualModel>();
132 visuals->setGeometry(tissueMesh);
133 auto material = std::make_shared<RenderMaterial>();
134 material->setShadingModel(RenderMaterial::ShadingModel::PBR);
135 visuals->setRenderMaterial(material);
136 
137 // Setup the physics
138 tissueObj->setPhysicsGeometry(tissueMesh);
139 tissueObj->setDynamicalModel(model);
140 tissueObj->getPbdBody()->uniformMassValue = 0.05;
141 
142 // Fix/hold in place the borders of the simulated geometry
143 for (int z = 0; z < dim[2]; z++)
144 {
145  for (int y = 0; y < dim[1]; y++)
146  {
147  for (int x = 0; x < dim[0]; x++)
148  {
149  if (x == 0 || /*z == 0 ||*/ x == dim[0] - 1 /*|| z == dim[2] - 1*/)
150  {
151  tissueObj->getPbdBody()->fixedNodeIds.push_back(x + dim[0] * (y + dim[1] * z));
152  }
153  }
154  }
155 }
156 model->getConfig()->m_femParams->m_YoungModulus = 4000.0;
157 model->getConfig()->m_femParams->m_PoissonRatio = 0.45;
158 model->getConfig()->enableFemConstraint(PbdFemConstraint::MaterialType::NeoHookean);
159 model->getConfig()->setBodyDamping(tissueObj->getPbdBody()->bodyHandle, 0.001);
160 ```
161 
162 ## Deformable Threads
163 
164 Surture threads are very common in surgical scenarios. For threads one may use **PbdDistanceConstraint** & **PbdBendConstraint** constraints. The distance constraints keep the particles of the thread together, whilst the bend controls the rigidity of the the thread. The bend constraints may also be generated between multiple sets of particles to reduce iteration count. It's useful to note that a LineMesh threads **PbdDistanceConstraint**'s will be solved in fewer iterations if the lines are ordered from end effector to tail.
165 
166 <p align="center">
167  <img src="../media/PbdModel/thread_wrap.gif"/>
168 </p>
169 
170 To setup a `LineMesh` that is simulated with PBD:
171 
172 ```cpp
173 // Setup the Geometry
174 std::shared_ptr<LineMesh> stringMesh = GeometryUtils::toLineGrid(start, dir, length, dim);
175 
176 const int numVerts = stringMesh->getNumVertices();
177 
178 // Setup the Parameters
179 auto pbdParams = std::make_shared<PbdModelConfig>();
180 pbdParams->m_gravity = Vec3d(0.0, -9.8, 0.0);
181 pbdParams->m_dt = 0.0005;
182 pbdParams->m_iterations = 1;
183 pbdParams->m_linearDampingCoeff = 0.03;
184 
185 // Setup the Model
186 auto pbdModel = std::make_shared<PbdModel>();
187 pbdModel->configure(pbdParams);
188 
189 // Setup the VisualModel
190 auto material = std::make_shared<RenderMaterial>();
191 material->setBackFaceCulling(false);
192 material->setLineWidth(4.0);
193 material->setDisplayMode(RenderMaterial::DisplayMode::Wireframe);
194 
195 // Setup the Object
196 auto stringObj = std::make_shared<PbdObject>(name);
197 stringObj->setVisualGeometry(stringMesh);
198 stringObj->getVisualModel(0)->setRenderMaterial(material);
199 stringObj->setPhysicsGeometry(stringMesh);
200 stringObj->setCollidingGeometry(stringMesh);
201 stringObj->setDynamicalModel(pbdModel);
202 
203 stringObj->getPbdBody()->uniformMassValue = 0.0001 / numVerts; // grams
204 stringObj->getPbdBody()->fixedNodeIds = { 0, }; // Fix the first vertex
205 pbdParams->enableConstraint(PbdModelConfig::ConstraintGenType::Distance, 200.0);
206 pbdParams->enableBendConstraint(0.01, 1);
207 ```
208 
209 ## Liquids
210 
211 Liquids can be modeled with pbd using **PbdConstantDensityConstraint**. Generally, the stiffness is kept as high as possible as liquids are incompressible. If not, you may observe "bouncey" behaviour. Liquids in iMSTK are most useful for bleeding simulation.
212 
213 <p align="center">
214  <img src="../media/PbdModel/blood.png" width="500"/>
215 </p>
216 
217 To setup a PBD fluid:
218 
219 ```cpp
220 // Load a sample mesh
221 std::shared_ptr<PointSet> fluidMesh = MeshIO::read("myPointSet.vtk");
222 
223 const double particleRadius = 0.5;
224 auto material = std::make_shared<RenderMaterial>();
225 material->setDisplayMode(RenderMaterial::DisplayMode::Fluid);
226 material->setVertexColor(Color::Red);
227 material->setPointSize(particleRadius); // Control visual particle size
228 
229 auto fluidObj = std::make_shared<PbdObject>();
230 fluidObj->setVisualGeometry(fluidMesh);
231 fluidVisualModel->getVisualModel(0)->setRenderMaterial(material);
232 fluidObj->setCollidingGeometry(fluidMesh);
233 fluidObj->setPhysicsGeometry(fluidMesh);
234 
235 auto pbdModel = std::make_shared<PbdModel>();
236 std::shared_ptr<PbdModelConfig> pbdParams = pbdModel->getConfig();
237 pbdParams->enableConstantDensityConstraint(1.0, particleRadius);
238 pbdParams->m_gravity = Vec3d(0.0, -9.8, 0.0);
239 pbdParams->m_dt = 0.005;
240 pbdParams->m_iterations = 2;
241 
242 fluidObj->setDynamicalModel(pbdModel);
243 fluidObj->getPbdBody()->uniformMassValue = 1.0;
244 ```
245 
246 ## Gasses
247 
248 The primary usage for gas is particles during electrocautery. Often these would be billboarded smoke images on particles that fade fairly quickly. There are currently no examples for gas in iMSTK. It is a fluid though, so its approach is not much different than liquids. The **PbdConstantDensityConstraint** may be used. I would suggest using a lower stiffness as liquids tend to be incompressible (constant density) whereas gasses are compressible. The other issue is the lack of proper boundary conditions. Often we are modeling a gas suspended in air. This air must be modeled too if you want accuracy. There do exist some solutions with "ghost particles" to approximate air without adding air particles, but iMSTK does not have such solutions yet. If this is for visual purposes I might suggest lowering gravity, fiddling with mass, etc to get believable behaviour without being suspended in anything.
249 
250 ## Constraints and Parameters
251 
252 To use a `PbdModel`, create a `PbdBody` from it. In the above code when a `PbdObject` is created this is automatically done for you. The `PbdBody` represents the `PbdObject` in the `PbdModel`. Multiple `PbdModel`'s could be used but normally only one large system is shared among all simulated objects. The PbdBody can be rigid or deformable. If rigid, geometry doesn't matter. If deformable, it can only be supplied one `Geometry` for constraint generation of its particles. So this means it can only be SurfaceMesh deformable, a LineMesh deformable, or a TetrahedralMesh deformable. But one is able to attach constraints between `PbdBody`'s should you need to connect geometries of differing types (ex: or even connect a rigid body needle to a deformable suture thread).
253 
254 The PbdModel has a bunch of constraints and some global parameters.
255 
256 **Constraints**: Constraints of varying types may be used via ``PbdModelConfig::enableConstraint``, internally this uses PbdConstraintFunctor's which defines how to generate constraints. If one needs more hands on with constraints you may write your own ``PbdConstraintFunctor``. Implemented by subclassing PbdConstraintFunctor and overriding the operator() function. See existing functors in ``imstkPbdConstraintFunctor.h``.
257 
258 ```cpp
259 auto myCustomFunctor = std::make_shared<MySuperCustomFunctor>();
260 myCustomFunctor->setStiffness(0.95);
261 pbdModel->addPbdConstraintFunctor(myCustomFunctor);
262 ```
263 
264 **dt**: The timestep is used during integration to move the particles. Small timesteps are preferable for stability. Real time steps may be used by varying dt every update of the simulation.
265 
266 ```cpp
267 connect<Event>(sceneManager, &SceneManager::postUpdate, [&](Event*)
268 {
269  pbdConfig->m_dt = sceneManager->getDt();
270 });
271 ```
272 
273 **Iterations**: The iterations of the solver used in the internal constraints. More iterations give changes more time to percolate through the body. For example, a really long thread with numerous segments may have a really high stiffness but if it doesn't have enough iterations it will never be able to reach maximum stiffness. In the original PBD paper stiffness varied with the number of iterations. In xPBD (default) it does not.
274 
275 **linearDampingCoeff** & **angularDampingCoeff**: Multiplied with velocity to slow any particles. Globally applied. There are also per `PbdBody` coefficients.
276 
277 ## Constraints
278 
279 Pbd is a constraint based model. Constraints are made for particles in pbd. This constrains the movement of a particle. Constraints are given via a constraint function q, and the gradient of the function q. To solve a constraint is to reduce the scalar, q, to 0. The gradient gives the direction to move the particle to do this. A pbd particle may also optionally have an orientation, similarly constraints can be defined for orientations.
280 
281 There are four types of pbd constraints in iMSTK.
282  - PbdConstraint: Only linear constraints, not used for collision at all.
283  - PbdCollisionConstraints: Used for deformable-deformable linear only collisions
284  - PbdContactConstraints: Used for rigid-deformable and rigid-rigid collisions, applies force and torque. Has a support point, to define torque through that axes.
285  - PbdAngularConstraint: Used only for changing the orientation of a body.
286 
287 Among the PbdConstraints are:
288 - **PbdDistanceConstraint**: Constraints two points by the initial distance between them.
289 - **PbdDihedralConstraint**: Constrains two triangles by the initial angle between their planes.
290 - **PbdVolumeConstraint**: Constrains all points of a tetrahedron by the initial volume.
291 - **PbdFETetConstraint**:
292 - **PbdBendConstraint**: Constrains 3 points by the angle between the two lines. (like a 1d Dihedral)
293 - **PbdConstantDensityConstraint**: Constrains all points of a system to achieve constant density using SPH kernels (ie: repels particles in a spherical manner).
294 - **PbdAreaConstraint**: Constrains 3 points of a triangle by the initial area of that triangle.
295 - **PbdAngularDistanceConstraint**: Constraints two bodies by an angle, or one body to a fixed angle.
296 - **PbdBodyToBodyDistanceConstraint**: Constraints two rigid pbd bodies by a distance defined between two points local to each body (off center of mass).
297 
298 This does not include all of them, a number of collision constraints were omitted.
299 
300 To implement a custom constraint, subclass a `PbdConstraint`, construct or initialize it appropriately, then implement/override ``computeValueAndGradient`` which should fill out the constraint value C and constraint gradient dC/dx. This constraint would then need to be added to an existing system or solved in its own computational block.
301 
302 ## Bibliography
303 
304 <a id="pbd">[pbd]</a>
305 Matthias Müller, Bruno Heidelberger, Marcus Hennix, and John Ratcliff. 2007.
306 Position based dynamics.
307 J. Vis. Comun. Image Represent. 18, 2 (April 2007), 109-118
308 
309 <a id="xpbd">[pbd]</a>
310 Miles Macklin, Matthias Müller, and Nuttapong Chentanez.
311 XPBD: position-based simulation of compliant constrained dynamics.
312 In Proc. of Motion in Games. 49–54
313 
314 <a id="rpbd">[rpbd]</a>
315 Matthias Mullerm, Miles Macklin, Nuttapong Chentanez, Stefan Jeschke, and Tae-Yong Kim. 2020.
316 Detailed Rigid Body Simulation with Extended Position Based Dynamics
317 Computer Graphics Forum Vol 39, Issue 8, 101-112
318 
319 <a id="spbd">[spbd]</a>
320 Jan Bender, Matthias Muller, Miles Macklin. 2017.
321 A Survey on Position Based Dynamics.
322 Proc. of European Association for Computer Graphics. No. 6, 1-31