2 Written by Xuchen Han <xuchenhan2015@u.northwestern.edu>
4 Bullet Continuous Collision Detection and Physics Library
5 Copyright (c) 2019 Google Inc. http://bulletphysics.org
6 This software is provided 'as-is', without any express or implied warranty.
7 In no event will the authors be held liable for any damages arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it freely,
10 subject to the following restrictions:
11 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
12 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
13 3. This notice may not be removed or altered from any source distribution.
16 #ifndef BT_LINEAR_ELASTICITY_H
17 #define BT_LINEAR_ELASTICITY_H
19 #include "btDeformableLagrangianForce.h"
20 #include "LinearMath/btQuickprof.h"
21 #include "btSoftBodyInternals.h"
22 #define TETRA_FLAT_THRESHOLD 0.01
23 class btDeformableLinearElasticityForce : public btDeformableLagrangianForce
26 typedef btAlignedObjectArray<btVector3> TVStack;
27 btScalar m_mu, m_lambda;
28 btScalar m_E, m_nu; // Young's modulus and Poisson ratio
29 btScalar m_damping_alpha, m_damping_beta;
30 btDeformableLinearElasticityForce() : m_mu(1), m_lambda(1), m_damping_alpha(0.01), m_damping_beta(0.01)
32 updateYoungsModulusAndPoissonRatio();
35 btDeformableLinearElasticityForce(btScalar mu, btScalar lambda, btScalar damping_alpha = 0.01, btScalar damping_beta = 0.01) : m_mu(mu), m_lambda(lambda), m_damping_alpha(damping_alpha), m_damping_beta(damping_beta)
37 updateYoungsModulusAndPoissonRatio();
40 void updateYoungsModulusAndPoissonRatio()
42 // conversion from Lame Parameters to Young's modulus and Poisson ratio
43 // https://en.wikipedia.org/wiki/Lam%C3%A9_parameters
44 m_E = m_mu * (3 * m_lambda + 2 * m_mu) / (m_lambda + m_mu);
45 m_nu = m_lambda * 0.5 / (m_mu + m_lambda);
48 void updateLameParameters()
50 // conversion from Young's modulus and Poisson ratio to Lame Parameters
51 // https://en.wikipedia.org/wiki/Lam%C3%A9_parameters
52 m_mu = m_E * 0.5 / (1 + m_nu);
53 m_lambda = m_E * m_nu / ((1 + m_nu) * (1 - 2 * m_nu));
56 void setYoungsModulus(btScalar E)
59 updateLameParameters();
62 void setPoissonRatio(btScalar nu)
65 updateLameParameters();
68 void setDamping(btScalar damping_alpha, btScalar damping_beta)
70 m_damping_alpha = damping_alpha;
71 m_damping_beta = damping_beta;
74 void setLameParameters(btScalar mu, btScalar lambda)
78 updateYoungsModulusAndPoissonRatio();
81 virtual void addScaledForces(btScalar scale, TVStack& force)
83 addScaledDampingForce(scale, force);
84 addScaledElasticForce(scale, force);
87 virtual void addScaledExplicitForce(btScalar scale, TVStack& force)
89 addScaledElasticForce(scale, force);
92 // The damping matrix is calculated using the time n state as described in https://www.math.ucla.edu/~jteran/papers/GSSJT15.pdf to allow line search
93 virtual void addScaledDampingForce(btScalar scale, TVStack& force)
95 if (m_damping_alpha == 0 && m_damping_beta == 0)
97 btScalar mu_damp = m_damping_beta * m_mu;
98 btScalar lambda_damp = m_damping_beta * m_lambda;
99 int numNodes = getNumNodes();
100 btAssert(numNodes <= force.size());
101 btVector3 grad_N_hat_1st_col = btVector3(-1, -1, -1);
102 for (int i = 0; i < m_softBodies.size(); ++i)
104 btSoftBody* psb = m_softBodies[i];
105 if (!psb->isActive())
109 for (int j = 0; j < psb->m_tetras.size(); ++j)
111 bool close_to_flat = (psb->m_tetraScratches[j].m_J < TETRA_FLAT_THRESHOLD);
112 btSoftBody::Tetra& tetra = psb->m_tetras[j];
113 btSoftBody::Node* node0 = tetra.m_n[0];
114 btSoftBody::Node* node1 = tetra.m_n[1];
115 btSoftBody::Node* node2 = tetra.m_n[2];
116 btSoftBody::Node* node3 = tetra.m_n[3];
117 size_t id0 = node0->index;
118 size_t id1 = node1->index;
119 size_t id2 = node2->index;
120 size_t id3 = node3->index;
121 btMatrix3x3 dF = DsFromVelocity(node0, node1, node2, node3) * tetra.m_Dm_inverse;
124 dF = psb->m_tetraScratches[j].m_corotation.transpose() * dF;
128 btMatrix3x3 dP = (dF + dF.transpose()) * mu_damp + I * ((dF[0][0] + dF[1][1] + dF[2][2]) * lambda_damp);
129 btMatrix3x3 df_on_node123 = dP * tetra.m_Dm_inverse.transpose();
132 df_on_node123 = psb->m_tetraScratches[j].m_corotation * df_on_node123;
134 btVector3 df_on_node0 = df_on_node123 * grad_N_hat_1st_col;
135 // damping force differential
136 btScalar scale1 = scale * tetra.m_element_measure;
137 force[id0] -= scale1 * df_on_node0;
138 force[id1] -= scale1 * df_on_node123.getColumn(0);
139 force[id2] -= scale1 * df_on_node123.getColumn(1);
140 force[id3] -= scale1 * df_on_node123.getColumn(2);
142 for (int j = 0; j < psb->m_nodes.size(); ++j)
144 const btSoftBody::Node& node = psb->m_nodes[j];
145 size_t id = node.index;
148 force[id] -= scale * node.m_v / node.m_im * m_damping_alpha;
154 virtual double totalElasticEnergy(btScalar dt)
157 for (int i = 0; i < m_softBodies.size(); ++i)
159 btSoftBody* psb = m_softBodies[i];
160 if (!psb->isActive())
164 for (int j = 0; j < psb->m_tetraScratches.size(); ++j)
166 btSoftBody::Tetra& tetra = psb->m_tetras[j];
167 btSoftBody::TetraScratch& s = psb->m_tetraScratches[j];
168 energy += tetra.m_element_measure * elasticEnergyDensity(s);
174 // The damping energy is formulated as in https://www.math.ucla.edu/~jteran/papers/GSSJT15.pdf to allow line search
175 virtual double totalDampingEnergy(btScalar dt)
179 for (int i = 0; i < m_softBodies.size(); ++i)
181 btSoftBody* psb = m_softBodies[i];
182 if (!psb->isActive())
186 for (int j = 0; j < psb->m_nodes.size(); ++j)
188 sz = btMax(sz, psb->m_nodes[j].index);
191 TVStack dampingForce;
192 dampingForce.resize(sz + 1);
193 for (int i = 0; i < dampingForce.size(); ++i)
194 dampingForce[i].setZero();
195 addScaledDampingForce(0.5, dampingForce);
196 for (int i = 0; i < m_softBodies.size(); ++i)
198 btSoftBody* psb = m_softBodies[i];
199 for (int j = 0; j < psb->m_nodes.size(); ++j)
201 const btSoftBody::Node& node = psb->m_nodes[j];
202 energy -= dampingForce[node.index].dot(node.m_v) / dt;
208 double elasticEnergyDensity(const btSoftBody::TetraScratch& s)
211 btMatrix3x3 epsilon = (s.m_F + s.m_F.transpose()) * 0.5 - btMatrix3x3::getIdentity();
212 btScalar trace = epsilon[0][0] + epsilon[1][1] + epsilon[2][2];
213 density += m_mu * (epsilon[0].length2() + epsilon[1].length2() + epsilon[2].length2());
214 density += m_lambda * trace * trace * 0.5;
218 virtual void addScaledElasticForce(btScalar scale, TVStack& force)
220 int numNodes = getNumNodes();
221 btAssert(numNodes <= force.size());
222 btVector3 grad_N_hat_1st_col = btVector3(-1, -1, -1);
223 for (int i = 0; i < m_softBodies.size(); ++i)
225 btSoftBody* psb = m_softBodies[i];
226 if (!psb->isActive())
230 btScalar max_p = psb->m_cfg.m_maxStress;
231 for (int j = 0; j < psb->m_tetras.size(); ++j)
233 btSoftBody::Tetra& tetra = psb->m_tetras[j];
235 firstPiola(psb->m_tetraScratches[j], P);
239 // since we want to clamp the principal stress to max_p, we only need to
240 // calculate SVD when sigma_0^2 + sigma_1^2 + sigma_2^2 > max_p * max_p
241 btScalar trPTP = (P[0].length2() + P[1].length2() + P[2].length2());
242 if (trPTP > max_p * max_p)
246 singularValueDecomposition(P, U, sigma, V);
247 sigma[0] = btMin(sigma[0], max_p);
248 sigma[1] = btMin(sigma[1], max_p);
249 sigma[2] = btMin(sigma[2], max_p);
250 sigma[0] = btMax(sigma[0], -max_p);
251 sigma[1] = btMax(sigma[1], -max_p);
252 sigma[2] = btMax(sigma[2], -max_p);
255 Sigma[0][0] = sigma[0];
256 Sigma[1][1] = sigma[1];
257 Sigma[2][2] = sigma[2];
258 P = U * Sigma * V.transpose();
262 // btVector3 force_on_node0 = P * (tetra.m_Dm_inverse.transpose()*grad_N_hat_1st_col);
263 btMatrix3x3 force_on_node123 = psb->m_tetraScratches[j].m_corotation * P * tetra.m_Dm_inverse.transpose();
264 btVector3 force_on_node0 = force_on_node123 * grad_N_hat_1st_col;
266 btSoftBody::Node* node0 = tetra.m_n[0];
267 btSoftBody::Node* node1 = tetra.m_n[1];
268 btSoftBody::Node* node2 = tetra.m_n[2];
269 btSoftBody::Node* node3 = tetra.m_n[3];
270 size_t id0 = node0->index;
271 size_t id1 = node1->index;
272 size_t id2 = node2->index;
273 size_t id3 = node3->index;
276 btScalar scale1 = scale * tetra.m_element_measure;
277 force[id0] -= scale1 * force_on_node0;
278 force[id1] -= scale1 * force_on_node123.getColumn(0);
279 force[id2] -= scale1 * force_on_node123.getColumn(1);
280 force[id3] -= scale1 * force_on_node123.getColumn(2);
285 virtual void buildDampingForceDifferentialDiagonal(btScalar scale, TVStack& diagA) {}
287 // The damping matrix is calculated using the time n state as described in https://www.math.ucla.edu/~jteran/papers/GSSJT15.pdf to allow line search
288 virtual void addScaledDampingForceDifferential(btScalar scale, const TVStack& dv, TVStack& df)
290 if (m_damping_alpha == 0 && m_damping_beta == 0)
292 btScalar mu_damp = m_damping_beta * m_mu;
293 btScalar lambda_damp = m_damping_beta * m_lambda;
294 int numNodes = getNumNodes();
295 btAssert(numNodes <= df.size());
296 btVector3 grad_N_hat_1st_col = btVector3(-1, -1, -1);
297 for (int i = 0; i < m_softBodies.size(); ++i)
299 btSoftBody* psb = m_softBodies[i];
300 if (!psb->isActive())
304 for (int j = 0; j < psb->m_tetras.size(); ++j)
306 bool close_to_flat = (psb->m_tetraScratches[j].m_J < TETRA_FLAT_THRESHOLD);
307 btSoftBody::Tetra& tetra = psb->m_tetras[j];
308 btSoftBody::Node* node0 = tetra.m_n[0];
309 btSoftBody::Node* node1 = tetra.m_n[1];
310 btSoftBody::Node* node2 = tetra.m_n[2];
311 btSoftBody::Node* node3 = tetra.m_n[3];
312 size_t id0 = node0->index;
313 size_t id1 = node1->index;
314 size_t id2 = node2->index;
315 size_t id3 = node3->index;
316 btMatrix3x3 dF = Ds(id0, id1, id2, id3, dv) * tetra.m_Dm_inverse;
319 dF = psb->m_tetraScratches[j].m_corotation.transpose() * dF;
323 btMatrix3x3 dP = (dF + dF.transpose()) * mu_damp + I * ((dF[0][0] + dF[1][1] + dF[2][2]) * lambda_damp);
324 btMatrix3x3 df_on_node123 = dP * tetra.m_Dm_inverse.transpose();
327 df_on_node123 = psb->m_tetraScratches[j].m_corotation * df_on_node123;
329 btVector3 df_on_node0 = df_on_node123 * grad_N_hat_1st_col;
331 // damping force differential
332 btScalar scale1 = scale * tetra.m_element_measure;
333 df[id0] -= scale1 * df_on_node0;
334 df[id1] -= scale1 * df_on_node123.getColumn(0);
335 df[id2] -= scale1 * df_on_node123.getColumn(1);
336 df[id3] -= scale1 * df_on_node123.getColumn(2);
338 for (int j = 0; j < psb->m_nodes.size(); ++j)
340 const btSoftBody::Node& node = psb->m_nodes[j];
341 size_t id = node.index;
344 df[id] -= scale * dv[id] / node.m_im * m_damping_alpha;
350 virtual void addScaledElasticForceDifferential(btScalar scale, const TVStack& dx, TVStack& df)
352 int numNodes = getNumNodes();
353 btAssert(numNodes <= df.size());
354 btVector3 grad_N_hat_1st_col = btVector3(-1, -1, -1);
355 for (int i = 0; i < m_softBodies.size(); ++i)
357 btSoftBody* psb = m_softBodies[i];
358 if (!psb->isActive())
362 for (int j = 0; j < psb->m_tetras.size(); ++j)
364 btSoftBody::Tetra& tetra = psb->m_tetras[j];
365 btSoftBody::Node* node0 = tetra.m_n[0];
366 btSoftBody::Node* node1 = tetra.m_n[1];
367 btSoftBody::Node* node2 = tetra.m_n[2];
368 btSoftBody::Node* node3 = tetra.m_n[3];
369 size_t id0 = node0->index;
370 size_t id1 = node1->index;
371 size_t id2 = node2->index;
372 size_t id3 = node3->index;
373 btMatrix3x3 dF = psb->m_tetraScratches[j].m_corotation.transpose() * Ds(id0, id1, id2, id3, dx) * tetra.m_Dm_inverse;
375 firstPiolaDifferential(psb->m_tetraScratches[j], dF, dP);
376 // btVector3 df_on_node0 = dP * (tetra.m_Dm_inverse.transpose()*grad_N_hat_1st_col);
377 btMatrix3x3 df_on_node123 = psb->m_tetraScratches[j].m_corotation * dP * tetra.m_Dm_inverse.transpose();
378 btVector3 df_on_node0 = df_on_node123 * grad_N_hat_1st_col;
380 // elastic force differential
381 btScalar scale1 = scale * tetra.m_element_measure;
382 df[id0] -= scale1 * df_on_node0;
383 df[id1] -= scale1 * df_on_node123.getColumn(0);
384 df[id2] -= scale1 * df_on_node123.getColumn(1);
385 df[id3] -= scale1 * df_on_node123.getColumn(2);
390 void firstPiola(const btSoftBody::TetraScratch& s, btMatrix3x3& P)
392 btMatrix3x3 corotated_F = s.m_corotation.transpose() * s.m_F;
394 btMatrix3x3 epsilon = (corotated_F + corotated_F.transpose()) * 0.5 - btMatrix3x3::getIdentity();
395 btScalar trace = epsilon[0][0] + epsilon[1][1] + epsilon[2][2];
396 P = epsilon * btScalar(2) * m_mu + btMatrix3x3::getIdentity() * m_lambda * trace;
399 // Let P be the first piola stress.
400 // This function calculates the dP = dP/dF * dF
401 void firstPiolaDifferential(const btSoftBody::TetraScratch& s, const btMatrix3x3& dF, btMatrix3x3& dP)
403 btScalar trace = (dF[0][0] + dF[1][1] + dF[2][2]);
404 dP = (dF + dF.transpose()) * m_mu + btMatrix3x3::getIdentity() * m_lambda * trace;
407 // Let Q be the damping stress.
408 // This function calculates the dP = dQ/dF * dF
409 void firstPiolaDampingDifferential(const btSoftBody::TetraScratch& s, const btMatrix3x3& dF, btMatrix3x3& dP)
411 btScalar mu_damp = m_damping_beta * m_mu;
412 btScalar lambda_damp = m_damping_beta * m_lambda;
413 btScalar trace = (dF[0][0] + dF[1][1] + dF[2][2]);
414 dP = (dF + dF.transpose()) * mu_damp + btMatrix3x3::getIdentity() * lambda_damp * trace;
417 virtual void addScaledHessian(btScalar scale)
419 btVector3 grad_N_hat_1st_col = btVector3(-1, -1, -1);
420 for (int i = 0; i < m_softBodies.size(); ++i)
422 btSoftBody* psb = m_softBodies[i];
423 if (!psb->isActive())
427 for (int j = 0; j < psb->m_tetras.size(); ++j)
429 btSoftBody::Tetra& tetra = psb->m_tetras[j];
431 firstPiola(psb->m_tetraScratches[j], P); // make sure scratch is evaluated at x_n + dt * vn
432 btMatrix3x3 force_on_node123 = psb->m_tetraScratches[j].m_corotation * P * tetra.m_Dm_inverse.transpose();
433 btVector3 force_on_node0 = force_on_node123 * grad_N_hat_1st_col;
434 btSoftBody::Node* node0 = tetra.m_n[0];
435 btSoftBody::Node* node1 = tetra.m_n[1];
436 btSoftBody::Node* node2 = tetra.m_n[2];
437 btSoftBody::Node* node3 = tetra.m_n[3];
438 btScalar scale1 = scale * (scale + m_damping_beta) * tetra.m_element_measure; // stiff and stiffness-damping terms;
439 node0->m_effectiveMass += OuterProduct(force_on_node0, force_on_node0) * scale1;
440 node1->m_effectiveMass += OuterProduct(force_on_node123.getColumn(0), force_on_node123.getColumn(0)) * scale1;
441 node2->m_effectiveMass += OuterProduct(force_on_node123.getColumn(1), force_on_node123.getColumn(1)) * scale1;
442 node3->m_effectiveMass += OuterProduct(force_on_node123.getColumn(2), force_on_node123.getColumn(2)) * scale1;
444 for (int j = 0; j < psb->m_nodes.size(); ++j)
446 btSoftBody::Node& node = psb->m_nodes[j];
451 node.m_effectiveMass += I * (scale * (1.0 / node.m_im) * m_damping_alpha);
457 virtual btDeformableLagrangianForceType getForceType()
459 return BT_LINEAR_ELASTICITY_FORCE;
462 #endif /* BT_LINEAR_ELASTICITY_H */