iMSTK
Interactive Medical Simulation Toolkit
imstkRigidBodyModel2.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkRigidBodyModel2.h"
8 #include "imstkParallelFor.h"
9 #include "imstkProjectedGaussSeidelSolver.h"
10 #include "imstkRbdConstraint.h"
11 #include "imstkTaskGraph.h"
12 #include "imstkLogger.h"
13 
14 namespace imstk
15 {
16 RigidBodyModel2::RigidBodyModel2() :
17  m_config(std::make_shared<RigidBodyModel2Config>()),
18  m_pgsSolver(std::make_shared<ProjectedGaussSeidelSolver<double>>())
19 {
20  m_computeTentativeVelocities = std::make_shared<TaskNode>(
21  std::bind(&RigidBodyModel2::computeTentativeVelocities, this), "RigidBodyModel_ComputeTentativeVelocities");
22  m_solveNode = std::make_shared<TaskNode>(std::bind(&RigidBodyModel2::solveConstraints, this), "RigidBodyModel_Solve");
23  m_integrateNode = std::make_shared<TaskNode>(std::bind(&RigidBodyModel2::integrate, this), "RigidBodyModel_Integrate");
24 
25  m_taskGraph->addNode(m_computeTentativeVelocities);
26  m_taskGraph->addNode(m_solveNode);
27  m_taskGraph->addNode(m_integrateNode);
28 }
29 
30 std::shared_ptr<RigidBody>
32 {
33  m_bodies.push_back(std::make_shared<RigidBody>());
34  m_modified = true;
35  return m_bodies.back();
36 }
37 
38 void
39 RigidBodyModel2::removeRigidBody(std::shared_ptr<RigidBody> rbd)
40 {
41  std::vector<std::shared_ptr<RigidBody>>::iterator bodyIter = std::find(m_bodies.begin(), m_bodies.end(), rbd);
42  if (bodyIter != m_bodies.end())
43  {
44  m_bodies.erase(bodyIter);
45  }
46  m_modified = true;
47 }
48 
49 bool
51 {
52  // Only run initialize if a body has been added/removed
53  if (!m_modified)
54  {
55  return true;
56  }
57 
58  // Compute the initial state
59  std::shared_ptr<RigidBodyState2> state = std::make_shared<RigidBodyState2>();
60  state->resize(m_bodies.size());
61  std::vector<bool>& isStatic = state->getIsStatic();
62  std::vector<double>& invMasses = state->getInvMasses();
63  StdVectorOfMat3d& invInteriaTensors = state->getInvIntertiaTensors();
64  StdVectorOfVec3d& positions = state->getPositions();
65  StdVectorOfQuatd& orientations = state->getOrientations();
66  StdVectorOfVec3d& velocities = state->getVelocities();
67  StdVectorOfVec3d& angularVelocities = state->getAngularVelocities();
68  StdVectorOfVec3d& tentativeVelocities = state->getTentatveVelocities();
69  StdVectorOfVec3d& tentativeAngularVelocities = state->getTentativeAngularVelocities();
70  StdVectorOfVec3d& forces = state->getForces();
71  StdVectorOfVec3d& torques = state->getTorques();
72 
73  m_Minv = Eigen::SparseMatrix<double>(m_bodies.size() * 6, m_bodies.size() * 6);
74  std::vector<Eigen::Triplet<double>> mInvTriplets;
75  mInvTriplets.reserve((9 + 3) * m_bodies.size());
76  for (size_t i = 0; i < m_bodies.size(); i++)
77  {
78  RigidBody& body = *m_bodies[i];
79 
80  // Set the intial state
81  isStatic[i] = body.m_isStatic;
82  invMasses[i] = (body.m_mass == 0.0) ? 0.0 : 1.0 / body.m_mass;
83  if (body.m_intertiaTensor.determinant() == 0.0)
84  {
85  LOG(WARNING) << "Inertia tensor provided is not invertible, check that it makes sense";
86  return false;
87  }
88  invInteriaTensors[i] = body.m_intertiaTensor.inverse();
89  positions[i] = body.m_initPos;
90  orientations[i] = body.m_initOrientation;
91  velocities[i] = body.m_initVelocity;
92  angularVelocities[i] = body.m_initAngularVelocity;
93  tentativeVelocities[i] = body.m_initVelocity;
94  tentativeAngularVelocities[i] = body.m_initAngularVelocity;
95  forces[i] = body.m_initForce;
96  torques[i] = body.m_initTorque;
97 
98  // Link it up with the state
99  body.m_pos = &positions[i];
100  body.m_orientation = &orientations[i];
101  body.m_velocity = &velocities[i];
102  body.m_angularVelocity = &angularVelocities[i];
103  body.m_force = &forces[i];
104  body.m_torque = &torques[i];
105  m_locations[m_bodies[i].get()] = static_cast<StorageIndex>(i);
106 
107  if (!body.m_isStatic)
108  {
109  // invMass expanded to 3x3 matrix
110  const double invMass = invMasses[i];
111  const Mat3d& invInvertia = invInteriaTensors[i];
112  int index = static_cast<int>(i * 6);
113  mInvTriplets.push_back(Eigen::Triplet<double>(index, index, invMass));
114  index++;
115  mInvTriplets.push_back(Eigen::Triplet<double>(index, index, invMass));
116  index++;
117  mInvTriplets.push_back(Eigen::Triplet<double>(index, index, invMass));
118  index++;
119  for (unsigned int c = 0; c < 3; c++)
120  {
121  for (unsigned int r = 0; r < 3; r++)
122  {
123  mInvTriplets.push_back(Eigen::Triplet<double>(index + r, index + c, invInvertia(c, r)));
124  }
125  }
126  }
127  }
128  m_Minv.setFromTriplets(mInvTriplets.begin(), mInvTriplets.end());
129 
130  // Copy to initial state
131  m_initialState = std::make_shared<RigidBodyState2>(*state);
132  m_currentState = state;
133  m_previousState = std::make_shared<RigidBodyState2>(*state);
134  m_modified = false;
135 
136  return true;
137 }
138 
139 void
141 {
142  // Compute the initial state
143  std::shared_ptr<RigidBodyState2> state = m_currentState;
144 
145  std::vector<double>& invMasses = state->getInvMasses();
146  StdVectorOfMat3d& invInteriaTensors = state->getInvIntertiaTensors();
147 
148  m_Minv = Eigen::SparseMatrix<double>(m_bodies.size() * 6, m_bodies.size() * 6);
149  std::vector<Eigen::Triplet<double>> mInvTriplets;
150  mInvTriplets.reserve((9 + 3) * m_bodies.size());
151  for (size_t i = 0; i < m_bodies.size(); i++)
152  {
153  RigidBody& body = *m_bodies[i];
154 
155  invMasses[i] = (body.m_mass == 0.0) ? 0.0 : 1.0 / body.m_mass;
156  if (body.m_intertiaTensor.determinant() == 0.0)
157  {
158  LOG(WARNING) << "Inertia tensor provided is not invertible, check that it makes sense";
159  }
160  else
161  {
162  invInteriaTensors[i] = body.m_intertiaTensor.inverse();
163  }
164 
165  if (!body.m_isStatic)
166  {
167  // invMass expanded to 3x3 matrix
168  const double invMass = invMasses[i];
169  const Mat3d& invInvertia = invInteriaTensors[i];
170  int index = static_cast<int>(i * 6);
171  mInvTriplets.push_back(Eigen::Triplet<double>(index, index, invMass));
172  index++;
173  mInvTriplets.push_back(Eigen::Triplet<double>(index, index, invMass));
174  index++;
175  mInvTriplets.push_back(Eigen::Triplet<double>(index, index, invMass));
176  index++;
177  for (unsigned int c = 0; c < 3; c++)
178  {
179  for (unsigned int r = 0; r < 3; r++)
180  {
181  mInvTriplets.push_back(Eigen::Triplet<double>(index + r, index + c, invInvertia(c, r)));
182  }
183  }
184  }
185  }
186  m_Minv.setFromTriplets(mInvTriplets.begin(), mInvTriplets.end());
187 }
188 
189 void
190 RigidBodyModel2::configure(std::shared_ptr<RigidBodyModel2Config> config)
191 {
192  m_config = config;
193 }
194 
195 void
197 {
198  const double dt = m_config->m_dt;
199  const std::vector<double>& invMasses = getCurrentState()->getInvMasses();
200  const StdVectorOfMat3d& invInteriaTensors = getCurrentState()->getInvIntertiaTensors();
201  StdVectorOfVec3d& tentativeVelocities = getCurrentState()->getTentatveVelocities();
202  StdVectorOfVec3d& tentativeAngularVelocities = getCurrentState()->getTentativeAngularVelocities();
203  StdVectorOfVec3d& forces = getCurrentState()->getForces();
204  StdVectorOfVec3d& torques = getCurrentState()->getTorques();
205  const Vec3d& fG = m_config->m_gravity;
206 
207  // Compute the desired velocites, later we will solve for the proper velocities,
208  // adjusted for the constraints
209  ParallelUtils::parallelFor(tentativeVelocities.size(), [&](const size_t& i)
210  {
211  forces[i] += fG * (1.0 / invMasses[i]);
212  tentativeVelocities[i] += forces[i] * invMasses[i] * dt;
213  tentativeAngularVelocities[i] += invInteriaTensors[i] * torques[i] * dt;
214  }, tentativeVelocities.size() > m_maxBodiesParallel);
215 }
216 
217 void
219 {
220  // Clear
221  F = Eigen::VectorXd();
222 
223  // Solves the current constraints of the system, then discards them
224  if (m_constraints.size() == 0)
225  {
226  return;
227  }
228  if (m_config->m_maxNumConstraints != -1 && static_cast<int>(m_constraints.size()) > m_config->m_maxNumConstraints * 2)
229  {
230  m_constraints.resize(m_config->m_maxNumConstraints * 2);
231  }
232 
233  //printf("solving\n");
234 
235  std::shared_ptr<RigidBodyState2> state = getCurrentState();
236  const std::vector<bool>& isStatic = state->getIsStatic();
237  const StdVectorOfVec3d& tentativeVelocities = state->getTentatveVelocities();
238  const StdVectorOfVec3d& tentativeAngularVelocities = state->getTentativeAngularVelocities();
239  StdVectorOfVec3d& forces = state->getForces();
240  StdVectorOfVec3d& torques = state->getTorques();
241 
242  Eigen::VectorXd V = Eigen::VectorXd(state->size() * 6);
243  Eigen::VectorXd Fext = Eigen::VectorXd(state->size() * 6);
244 
245  // Fill the forces and tenative velocities vectors
246  StorageIndex j = 0;
247  for (size_t i = 0; i < state->size(); i++)
248  {
249  if (!isStatic[i])
250  {
251  const Vec3d& velocity = tentativeVelocities[i];
252  const Vec3d& angularVelocity = tentativeAngularVelocities[i];
253  const Vec3d& force = forces[i];
254  const Vec3d& torque = torques[i];
255  Fext(j) = force[0];
256  V(j) = velocity[0];
257  j++;
258  Fext(j) = force[1];
259  V(j) = velocity[1];
260  j++;
261  Fext(j) = force[2];
262  V(j) = velocity[2];
263  j++;
264  Fext(j) = torque[0];
265  V(j) = angularVelocity[0];
266  j++;
267  Fext(j) = torque[1];
268  V(j) = angularVelocity[1];
269  j++;
270  Fext(j) = torque[2];
271  V(j) = angularVelocity[2];
272  j++;
273  }
274  else
275  {
276  for (StorageIndex k = 0; k < 6; k++)
277  {
278  Fext(j + k) = 0.0;
279  V(j + k) = 0.0;
280  }
281  j += 6;
282  }
283  }
284 
285  // Now construct the sparse jacobian matrix for every constraint (object vs constraint)
286  Eigen::SparseMatrix<double> J(m_constraints.size(), state->size() * 6);
287  Eigen::VectorXd Vu(m_constraints.size()); // Push Factor
288  Eigen::MatrixXd cu(m_constraints.size(), 2); // Mins and maxes
289  std::vector<Eigen::Triplet<double>> JTriplets;
290  JTriplets.reserve(m_constraints.size() * 12);
291  j = 0;
292  for (std::list<std::shared_ptr<RbdConstraint>>::iterator iter = m_constraints.begin(); iter != m_constraints.end(); iter++)
293  {
294  std::shared_ptr<RbdConstraint> constraint = *iter;
295  Vu(j) = constraint->vu;
296 
297  // Object 1
298  StorageIndex k = 0;
299  if (constraint->m_obj1 != nullptr)
300  {
301  const StorageIndex obj1Location = m_locations[constraint->m_obj1.get()];
302  const StorageIndex start1 = obj1Location * 6;
303  for (StorageIndex c = 0; c < 2; c++)
304  {
305  for (StorageIndex r = 0; r < 3; r++)
306  {
307  JTriplets.push_back(Eigen::Triplet<double>(j, start1 + k, constraint->J(r, c)));
308  k++;
309  }
310  }
311  }
312 
313  // Object 2
314  if (constraint->m_obj2 != nullptr)
315  {
316  const StorageIndex obj2Location = m_locations[constraint->m_obj2.get()];
317  const StorageIndex start2 = obj2Location * 6;
318  k = 0;
319  for (StorageIndex c = 2; c < 4; c++)
320  {
321  for (StorageIndex r = 0; r < 3; r++)
322  {
323  JTriplets.push_back(Eigen::Triplet<double>(j, start2 + k, constraint->J(r, c)));
324  k++;
325  }
326  }
327  }
328 
329  cu(j, 0) = constraint->range[0];
330  cu(j, 1) = constraint->range[1];
331  j++;
332  }
333  J.setFromTriplets(JTriplets.begin(), JTriplets.end());
334 
335  const double dt = m_config->m_dt;
336  Eigen::SparseMatrix<double> A = J * m_Minv * J.transpose();
337  Eigen::VectorXd b = Vu / dt - J * (V / dt + m_Minv * Fext);
338 
339  /*std::cout << "Minv: " << std::endl << m_Minv.toDense() << std::endl;
340  std::cout << "J: " << std::endl << J.toDense() << std::endl;
341  std::cout << "A: " << std::endl << A.toDense() << std::endl;
342  std::cout << std::endl;
343  std::cout << "Vu: " << std::endl << Vu << std::endl;
344  std::cout << "V: " << std::endl << V << std::endl;
345  std::cout << "b: " << std::endl << b << std::endl;*/
346 
347  m_pgsSolver->setA(&A);
348  //pgsSolver.setGuess(F); // Not using warm starting
349  m_pgsSolver->setMaxIterations(m_config->m_maxNumIterations);
350  m_pgsSolver->setEpsilon(m_config->m_epsilon);
351  F = J.transpose() * m_pgsSolver->solve(b, cu); // Reaction force,torque
352 
353  // Apply reaction impulse
354  j = 0;
355  for (size_t i = 0; i < state->size(); i++, j += 6)
356  {
357  forces[i] += Vec3d(F(j), F(j + 1), F(j + 2));
358  torques[i] += Vec3d(F(j + 3), F(j + 4), F(j + 5));
359  }
360 
361  //std::cout << std::endl << "Forces/Torques: " << F << std::endl;
362 
363  m_constraints.clear();
364 }
365 
366 void
368 {
369  // Just a basic symplectic euler
370  const double dt = m_config->m_dt;
371  const double velocityDamping = m_config->m_velocityDamping;
372  const double angularVelocityDamping = m_config->m_angularVelocityDamping;
373 
374  const std::vector<bool>& isStatic = getCurrentState()->getIsStatic();
375 
376  const std::vector<double>& invMasses = getCurrentState()->getInvMasses();
377  const StdVectorOfMat3d& invInteriaTensors = getCurrentState()->getInvIntertiaTensors();
378 
379  StdVectorOfVec3d& positions = getCurrentState()->getPositions();
380  StdVectorOfQuatd& orientations = getCurrentState()->getOrientations();
381 
382  StdVectorOfVec3d& velocities = getCurrentState()->getVelocities();
383  StdVectorOfVec3d& angularVelocities = getCurrentState()->getAngularVelocities();
384 
385  StdVectorOfVec3d& tentativeVelocities = getCurrentState()->getTentatveVelocities();
386  StdVectorOfVec3d& tentativeAngularVelocities = getCurrentState()->getTentativeAngularVelocities();
387 
388  StdVectorOfVec3d& forces = getCurrentState()->getForces();
389  StdVectorOfVec3d& torques = getCurrentState()->getTorques();
390 
391  ParallelUtils::parallelFor(positions.size(), [&](const size_t& i)
392  {
393  if (!isStatic[i])
394  {
395  velocities[i] += forces[i] * invMasses[i] * dt;
396  velocities[i] *= velocityDamping;
397  angularVelocities[i] += invInteriaTensors[i] * torques[i] * dt;
398  angularVelocities[i] *= angularVelocityDamping;
399  positions[i] += velocities[i] * dt;
400  {
401  const Quatd q = Quatd(0.0,
402  angularVelocities[i][0],
403  angularVelocities[i][1],
404  angularVelocities[i][2]) * orientations[i];
405  orientations[i].x() += q.x() * dt;
406  orientations[i].y() += q.y() * dt;
407  orientations[i].z() += q.z() * dt;
408  orientations[i].w() += q.w() * dt;
409  orientations[i].normalize();
410  }
411  }
412 
413  // Reset
414  m_bodies[i]->m_prevForce = forces[i];
415  forces[i] = Vec3d(0.0, 0.0, 0.0);
416  torques[i] = Vec3d(0.0, 0.0, 0.0);
417  tentativeVelocities[i] = velocities[i];
418  tentativeAngularVelocities[i] = angularVelocities[i];
419  }, positions.size() > m_maxBodiesParallel);
420 }
421 
422 void
423 RigidBodyModel2::initGraphEdges(std::shared_ptr<TaskNode> source, std::shared_ptr<TaskNode> sink)
424 {
425  m_taskGraph->addEdge(source, m_computeTentativeVelocities);
426  m_taskGraph->addEdge(m_computeTentativeVelocities, m_solveNode);
427  m_taskGraph->addEdge(m_solveNode, m_integrateNode);
428  m_taskGraph->addEdge(m_integrateNode, sink);
429 }
430 } // namespace imstk
std::shared_ptr< FeDeformBodyState > m_initialState
Initial state.
void solveConstraints()
Solve the current constraints of the model, then discards.
void integrate()
Integrate the model state.
Compound Geometry.
std::shared_ptr< FeDeformBodyState > m_previousState
Previous state.
bool initialize() override
Initialize the RigidBody model to the initial state.
void updateMass()
Updates mass and inertia matrices to those provided by the bodies. Not often needed unless mass/inert...
void configure(std::shared_ptr< RigidBodyModel2Config > config)
Configure the model.
Serves as a handle to the body.
void computeTentativeVelocities()
Computes the velocities.
std::shared_ptr< RigidBody > addRigidBody()
Adds a body to the system, must call initialize for changes to effect returns its reference...
std::shared_ptr< FeDeformBodyState > m_currentState
Current state.
void removeRigidBody(std::shared_ptr< RigidBody > body)
Removes a body from the system, must call initialize for changes to effect.
std::shared_ptr< FeDeformBodyState > getCurrentState() const
Return the current state of the problem.
void initGraphEdges()
Initializes the edges of the graph.