Chaste Commit::9e4a273f0754a391514ab12ed9d4a38fc9933db2
CompressibleNonlinearElasticitySolver.cpp
1/*
2
3Copyright (c) 2005-2026, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36/*
37 * NOTE ON COMPILATION ERRORS:
38 *
39 * (The following applies to IncompressibleNonlinearElasticityAssembler; possibly/probably holds for this class too).
40 *
41 * This file won't compile with Intel icpc version 9.1.039, with error message:
42 * "Terminate with:
43 (0): internal error: backend signals"
44 *
45 * Try recompiling with icpc version 10.0.025.
46 */
47
48#include "CompressibleNonlinearElasticitySolver.hpp"
49#include "LinearBasisFunction.hpp"
50#include "QuadraticBasisFunction.hpp"
51#include <algorithm>
52
53template<size_t DIM>
55 bool assembleJacobian)
56{
57 // Check we've actually been asked to do something!
58 assert(assembleResidual || assembleJacobian);
59 assert(this->mCurrentSolution.size()==this->mNumDofs);
60
61 // Zero the matrix/vector if it is to be assembled
62 if (assembleResidual)
63 {
64 PetscVecTools::Finalise(this->mResidualVector);
65 PetscVecTools::Zero(this->mResidualVector);
66 }
67 if (assembleJacobian)
68 {
69 PetscMatTools::Zero(this->mrJacobianMatrix);
70 PetscMatTools::Zero(this->mPreconditionMatrix);
71 }
72
73 c_matrix<double, STENCIL_SIZE, STENCIL_SIZE> a_elem;
74 // The (element) preconditioner matrix: this is the same as the jacobian, but
75 // with the mass matrix (ie \intgl phi_i phi_j) in the pressure-pressure block.
76 c_matrix<double, STENCIL_SIZE, STENCIL_SIZE> a_elem_precond;
77 c_vector<double, STENCIL_SIZE> b_elem;
78
79 // Loop over elements
80 for (typename AbstractTetrahedralMesh<DIM, DIM>::ElementIterator iter = this->mrQuadMesh.GetElementIteratorBegin();
81 iter != this->mrQuadMesh.GetElementIteratorEnd();
82 ++iter)
83 {
84 Element<DIM, DIM>& element = *iter;
85
86 if (element.GetOwnership() == true)
87 {
88 // LCOV_EXCL_START
89 // note: if assembleJacobian only
90 if (CommandLineArguments::Instance()->OptionExists("-mech_very_verbose") && assembleJacobian)
91 {
92 std::cout << "\r[" << PetscTools::GetMyRank() << "]: Element " << (*iter).GetIndex() << " of " << this->mrQuadMesh.GetNumElements() << std::flush;
93 }
94 // LCOV_EXCL_STOP
95
96 AssembleOnElement(element, a_elem, a_elem_precond, b_elem, assembleResidual, assembleJacobian);
97
101 //for (unsigned i=0; i<STENCIL_SIZE; i++)
102 //{
103 // for (unsigned j=0; j<STENCIL_SIZE; j++)
104 // {
105 // a_elem(i,j)=1.0;
106 // }
107 //}
108
109 unsigned p_indices[STENCIL_SIZE];
110 for (unsigned i=0; i<NUM_NODES_PER_ELEMENT; i++)
111 {
112 for (unsigned j=0; j<DIM; j++)
113 {
114 p_indices[DIM*i+j] = DIM*element.GetNodeGlobalIndex(i) + j;
115 }
116 }
117
118 if (assembleJacobian)
119 {
120 PetscMatTools::AddMultipleValues<STENCIL_SIZE>(this->mrJacobianMatrix, p_indices, a_elem);
121 PetscMatTools::AddMultipleValues<STENCIL_SIZE>(this->mPreconditionMatrix, p_indices, a_elem_precond);
122 }
123
124 if (assembleResidual)
125 {
126 PetscVecTools::AddMultipleValues<STENCIL_SIZE>(this->mResidualVector, p_indices, b_elem);
127 }
128 }
129 }
130
131 // Loop over specified boundary elements and compute surface traction terms
132 c_vector<double, BOUNDARY_STENCIL_SIZE> b_boundary_elem;
133 c_matrix<double, BOUNDARY_STENCIL_SIZE, BOUNDARY_STENCIL_SIZE> a_boundary_elem;
134 if (this->mrProblemDefinition.GetTractionBoundaryConditionType() != NO_TRACTIONS)
135 {
136 for (unsigned bc_index=0; bc_index<this->mrProblemDefinition.rGetTractionBoundaryElements().size(); bc_index++)
137 {
138 BoundaryElement<DIM-1,DIM>& r_boundary_element = *(this->mrProblemDefinition.rGetTractionBoundaryElements()[bc_index]);
139
140 // If the BCs are tractions applied on a given surface, the boundary integral is independent of u,
141 // so a_boundary_elem will be zero (no contribution to jacobian).
142 // If the BCs are normal pressure applied to the deformed body, the boundary depends on the deformation,
143 // so there is a contribution to the jacobian, and a_boundary_elem is non-zero. Note however that
144 // the AssembleOnBoundaryElement() method might decide not to include this, as it can actually
145 // cause divergence if the current guess is not close to the true solution
146
147 this->AssembleOnBoundaryElement(r_boundary_element, a_boundary_elem, b_boundary_elem, assembleResidual, assembleJacobian, bc_index);
148
149 unsigned p_indices[BOUNDARY_STENCIL_SIZE];
150 for (unsigned i=0; i<NUM_NODES_PER_BOUNDARY_ELEMENT; i++)
151 {
152 for (unsigned j=0; j<DIM; j++)
153 {
154 p_indices[DIM*i+j] = DIM*r_boundary_element.GetNodeGlobalIndex(i) + j;
155 }
156 }
157
158 if (assembleJacobian)
159 {
160 PetscMatTools::AddMultipleValues<BOUNDARY_STENCIL_SIZE>(this->mrJacobianMatrix, p_indices, a_boundary_elem);
161 PetscMatTools::AddMultipleValues<BOUNDARY_STENCIL_SIZE>(this->mPreconditionMatrix, p_indices, a_boundary_elem);
162 }
163
164 if (assembleResidual)
165 {
166 PetscVecTools::AddMultipleValues<BOUNDARY_STENCIL_SIZE>(this->mResidualVector, p_indices, b_boundary_elem);
167 }
168 }
169 }
170
171 this->FinishAssembleSystem(assembleResidual, assembleJacobian);
172}
173
174template<size_t DIM>
176 Element<DIM, DIM>& rElement,
177 c_matrix<double, STENCIL_SIZE, STENCIL_SIZE >& rAElem,
178 c_matrix<double, STENCIL_SIZE, STENCIL_SIZE >& rAElemPrecond,
179 c_vector<double, STENCIL_SIZE>& rBElem,
180 bool assembleResidual,
181 bool assembleJacobian)
182{
183 static c_matrix<double,DIM,DIM> jacobian;
184 static c_matrix<double,DIM,DIM> inverse_jacobian;
185 double jacobian_determinant;
186
187 this->mrQuadMesh.GetInverseJacobianForElement(rElement.GetIndex(), jacobian, jacobian_determinant, inverse_jacobian);
188
189 if (assembleJacobian)
190 {
191 rAElem.clear();
192 rAElemPrecond.clear();
193 }
194
195 if (assembleResidual)
196 {
197 rBElem.clear();
198 }
199
200 // Get the current displacement at the nodes
201 static c_matrix<double,DIM,NUM_NODES_PER_ELEMENT> element_current_displacements;
202 for (unsigned II=0; II<NUM_NODES_PER_ELEMENT; II++)
203 {
204 for (unsigned JJ=0; JJ<DIM; JJ++)
205 {
206 element_current_displacements(JJ,II) = this->mCurrentSolution[DIM*rElement.GetNodeGlobalIndex(II) + JJ];
207 }
208 }
209
210 // Allocate memory for the basis functions values and derivative values
211 static c_vector<double, NUM_VERTICES_PER_ELEMENT> linear_phi;
212 static c_vector<double, NUM_NODES_PER_ELEMENT> quad_phi;
213 static c_matrix<double, DIM, NUM_NODES_PER_ELEMENT> grad_quad_phi;
214 static c_matrix<double, NUM_NODES_PER_ELEMENT, DIM> trans_grad_quad_phi;
215
216 // Get the material law
217 AbstractCompressibleMaterialLaw<DIM>* p_material_law = this->mrProblemDefinition.GetCompressibleMaterialLaw(rElement.GetIndex());
218
219
220 static c_matrix<double,DIM,DIM> grad_u; // grad_u = (du_i/dX_M)
221
222 static c_matrix<double,DIM,DIM> F; // the deformation gradient, F = dx/dX, F_{iM} = dx_i/dX_M
223 static c_matrix<double,DIM,DIM> C; // Green deformation tensor, C = F^T F
224 static c_matrix<double,DIM,DIM> inv_C; // inverse(C)
225 static c_matrix<double,DIM,DIM> inv_F; // inverse(F)
226 static c_matrix<double,DIM,DIM> T; // Second Piola-Kirchoff stress tensor (= dW/dE = 2dW/dC)
227
228 static c_matrix<double,DIM,DIM> F_T; // F*T
229 static c_matrix<double,DIM,NUM_NODES_PER_ELEMENT> F_T_grad_quad_phi; // F*T*grad_quad_phi
230
231 c_vector<double,DIM> body_force;
232
233 static FourthOrderTensor<DIM,DIM,DIM,DIM> dTdE; // dTdE(M,N,P,Q) = dT_{MN}/dE_{PQ}
234 static FourthOrderTensor<DIM,DIM,DIM,DIM> dSdF; // dSdF(M,i,N,j) = dS_{Mi}/dF_{jN}
235
238
239 static c_matrix<double, DIM, NUM_NODES_PER_ELEMENT> temp_matrix;
240 static c_matrix<double,NUM_NODES_PER_ELEMENT,DIM> grad_quad_phi_times_invF;
241
242 if (this->mSetComputeAverageStressPerElement)
243 {
244 this->mAverageStressesPerElement[rElement.GetIndex()] = zero_vector<double>(DIM*(DIM+1)/2);
245 }
246
247 // Loop over Gauss points
248 for (unsigned quadrature_index=0; quadrature_index < this->mpQuadratureRule->GetNumQuadPoints(); quadrature_index++)
249 {
250 // This is needed by the cardiac mechanics solver
251 unsigned current_quad_point_global_index = rElement.GetIndex()*this->mpQuadratureRule->GetNumQuadPoints()
252 + quadrature_index;
253
254 double wJ = jacobian_determinant * this->mpQuadratureRule->GetWeight(quadrature_index);
255
256 const ChastePoint<DIM>& quadrature_point = this->mpQuadratureRule->rGetQuadPoint(quadrature_index);
257
258 // Set up basis function information
259 LinearBasisFunction<DIM>::ComputeBasisFunctions(quadrature_point, linear_phi);
260 QuadraticBasisFunction<DIM>::ComputeBasisFunctions(quadrature_point, quad_phi);
261 QuadraticBasisFunction<DIM>::ComputeTransformedBasisFunctionDerivatives(quadrature_point, inverse_jacobian, grad_quad_phi);
262 trans_grad_quad_phi = trans(grad_quad_phi);
263
264 // Get the body force, interpolating X if necessary
265 if (assembleResidual)
266 {
267 switch (this->mrProblemDefinition.GetBodyForceType())
268 {
269 case FUNCTIONAL_BODY_FORCE:
270 {
271 c_vector<double,DIM> X = zero_vector<double>(DIM);
272 // interpolate X (using the vertices and the /linear/ bases, as no curvilinear elements
273 for (unsigned node_index=0; node_index<NUM_VERTICES_PER_ELEMENT; node_index++)
274 {
275 X += linear_phi(node_index)*this->mrQuadMesh.GetNode( rElement.GetNodeGlobalIndex(node_index) )->rGetLocation();
276 }
277 body_force = this->mrProblemDefinition.EvaluateBodyForceFunction(X, this->mCurrentTime);
278 break;
279 }
280 case CONSTANT_BODY_FORCE:
281 {
282 body_force = this->mrProblemDefinition.GetConstantBodyForce();
283 break;
284 }
285 default:
287 }
288 }
289
290 // Interpolate grad_u
291 grad_u = zero_matrix<double>(DIM,DIM);
292 for (unsigned node_index=0; node_index<NUM_NODES_PER_ELEMENT; node_index++)
293 {
294 for (unsigned i=0; i<DIM; i++)
295 {
296 for (unsigned M=0; M<DIM; M++)
297 {
298 grad_u(i,M) += grad_quad_phi(M,node_index)*element_current_displacements(i,node_index);
299 }
300 }
301 }
302
303 // Calculate C, inv(C) and T
304 for (unsigned i=0; i<DIM; i++)
305 {
306 for (unsigned M=0; M<DIM; M++)
307 {
308 F(i,M) = (i==M?1:0) + grad_u(i,M);
309 }
310 }
311
312 C = prod(trans(F),F);
313 inv_C = Inverse(C);
314 inv_F = Inverse(F);
315
316 // Compute the passive stress, and dTdE corresponding to passive stress
317 this->SetupChangeOfBasisMatrix(rElement.GetIndex(), current_quad_point_global_index);
318 p_material_law->SetChangeOfBasisMatrix(this->mChangeOfBasisMatrix);
319 p_material_law->ComputeStressAndStressDerivative(C, inv_C, 0.0, T, dTdE, assembleJacobian);
320
321 if (this->mIncludeActiveTension)
322 {
323 // Add any active stresses, if there are any. Requires subclasses to overload this method,
324 // see for example the cardiac mechanics assemblers.
325 this->AddActiveStressAndStressDerivative(C, rElement.GetIndex(), current_quad_point_global_index,
326 T, dTdE, assembleJacobian);
327 }
328
329 if (this->mSetComputeAverageStressPerElement)
330 {
331 this->AddStressToAverageStressPerElement(T,rElement.GetIndex());
332 }
333
334 // Residual vector
335 if (assembleResidual)
336 {
337 F_T = prod(F,T);
338 F_T_grad_quad_phi = prod(F_T, grad_quad_phi);
339
340 for (unsigned index=0; index<NUM_NODES_PER_ELEMENT*DIM; index++)
341 {
342 unsigned spatial_dim = index%DIM;
343 unsigned node_index = (index-spatial_dim)/DIM;
344
345 rBElem(index) += - this->mrProblemDefinition.GetDensity()
346 * body_force(spatial_dim)
347 * quad_phi(node_index)
348 * wJ;
349
350 // The T(M,N)*F(spatial_dim,M)*grad_quad_phi(N,node_index) term
351 rBElem(index) += F_T_grad_quad_phi(spatial_dim,node_index)
352 * wJ;
353 }
354 }
355
356 // Jacobian matrix
357 if (assembleJacobian)
358 {
359 // Save trans(grad_quad_phi) * invF
360 grad_quad_phi_times_invF = prod(trans_grad_quad_phi, inv_F);
361
363 // Set up the tensor dSdF
364 //
365 // dSdF as a function of T and dTdE (which is what the material law returns) is given by:
366 //
367 // dS_{Mi}/dF_{jN} = (dT_{MN}/dC_{PQ}+dT_{MN}/dC_{PQ}) F{iP} F_{jQ} + T_{MN} delta_{ij}
368 //
369 // todo1: this should probably move into the material law (but need to make sure
370 // memory is handled efficiently
371 // todo2: get material law to return this immediately, not dTdE
373
374 // Set up the tensor 0.5(dTdE(M,N,P,Q) + dTdE(M,N,Q,P))
375 for (unsigned M=0; M<DIM; M++)
376 {
377 for (unsigned N=0; N<DIM; N++)
378 {
379 for (unsigned P=0; P<DIM; P++)
380 {
381 for (unsigned Q=0; Q<DIM; Q++)
382 {
383 // this is NOT dSdF, just using this as storage space
384 dSdF(M,N,P,Q) = 0.5*(dTdE(M,N,P,Q) + dTdE(M,N,Q,P));
385 }
386 }
387 }
388 }
389
390 // This is NOT dTdE, just reusing memory. A^{MdPQ} = F^d_N * dTdE_sym^{MNPQ}
391 dTdE.template SetAsContractionOnSecondDimension<DIM>(F, dSdF);
392
393 // dSdF{MdPe} := F^d_N * F^e_Q * dTdE_sym^{MNPQ}
394 dSdF.template SetAsContractionOnFourthDimension<DIM>(F, dTdE);
395
396 // Now add the T_{MN} delta_{ij} term
397 for (unsigned M=0; M<DIM; M++)
398 {
399 for (unsigned N=0; N<DIM; N++)
400 {
401 for (unsigned i=0; i<DIM; i++)
402 {
403 dSdF(M,i,N,i) += T(M,N);
404 }
405 }
406 }
407
409 // Set up the tensor
410 // dSdF_quad_quad(node_index1, spatial_dim1, node_index2, spatial_dim2)
411 // = dS_{M,spatial_dim1}/d_F{spatial_dim2,N}
412 // * grad_quad_phi(M,node_index1)
413 // * grad_quad_phi(P,node_index2)
414 //
415 // = dSdF(M,spatial_index1,N,spatial_index2)
416 // * grad_quad_phi(M,node_index1)
417 // * grad_quad_phi(P,node_index2)
418 //
420 temp_tensor.template SetAsContractionOnFirstDimension<DIM>(trans_grad_quad_phi, dSdF);
421 dSdF_quad_quad.template SetAsContractionOnThirdDimension<DIM>(trans_grad_quad_phi, temp_tensor);
422
423 for (unsigned index1=0; index1<NUM_NODES_PER_ELEMENT*DIM; index1++)
424 {
425 unsigned spatial_dim1 = index1%DIM;
426 unsigned node_index1 = (index1-spatial_dim1)/DIM;
427
428 for (unsigned index2=0; index2<NUM_NODES_PER_ELEMENT*DIM; index2++)
429 {
430 unsigned spatial_dim2 = index2%DIM;
431 unsigned node_index2 = (index2-spatial_dim2)/DIM;
432
433 // The dSdF*grad_quad_phi*grad_quad_phi term
434 rAElem(index1,index2) += dSdF_quad_quad(node_index1,spatial_dim1,node_index2,spatial_dim2)
435 * wJ;
436 }
437 }
438 }
439 }
440
441 rAElemPrecond.clear();
442 if (assembleJacobian)
443 {
444 rAElemPrecond = rAElem;
445 }
446
447 if (this->mSetComputeAverageStressPerElement)
448 {
449 for (unsigned i=0; i<DIM*(DIM+1)/2; i++)
450 {
451 this->mAverageStressesPerElement[rElement.GetIndex()](i) /= this->mpQuadratureRule->GetNumQuadPoints();
452 }
453 }
454}
455
456template<size_t DIM>
458 SolidMechanicsProblemDefinition<DIM>& rProblemDefinition,
459 std::string outputDirectory)
460 : AbstractNonlinearElasticitySolver<DIM>(rQuadMesh,
461 rProblemDefinition,
462 outputDirectory,
463 COMPRESSIBLE)
464{
465 if (rProblemDefinition.GetCompressibilityType() != COMPRESSIBLE)
466 {
467 EXCEPTION("SolidMechanicsProblemDefinition object contains incompressible material laws");
468 }
469}
470
471template<size_t DIM>
475
476// Explicit instantiation
#define EXCEPTION(message)
#define NEVER_REACHED
boost::numeric::ublas::c_matrix< T, 1, 1 > Inverse(const boost::numeric::ublas::c_matrix< T, 1, 1 > &rM)
unsigned GetNodeGlobalIndex(unsigned localIndex) const
bool GetOwnership() const
unsigned GetIndex() const
virtual void ComputeStressAndStressDerivative(c_matrix< double, DIM, DIM > &rC, c_matrix< double, DIM, DIM > &rInvC, double pressure, c_matrix< double, DIM, DIM > &rT, FourthOrderTensor< DIM, DIM, DIM, DIM > &rDTdE, bool computeDTdE)=0
void SetChangeOfBasisMatrix(c_matrix< double, DIM, DIM > &rChangeOfBasisMatrix)
bool OptionExists(const std::string &rOption)
static CommandLineArguments * Instance()
CompressibleNonlinearElasticitySolver(AbstractTetrahedralMesh< DIM, DIM > &rQuadMesh, SolidMechanicsProblemDefinition< DIM > &rProblemDefinition, std::string outputDirectory)
virtual void AssembleOnElement(Element< DIM, DIM > &rElement, c_matrix< double, STENCIL_SIZE, STENCIL_SIZE > &rAElem, c_matrix< double, STENCIL_SIZE, STENCIL_SIZE > &rAElemPrecond, c_vector< double, STENCIL_SIZE > &rBElem, bool assembleResidual, bool assembleJacobian)
void AssembleSystem(bool assembleResidual, bool assembleJacobian)
static void ComputeBasisFunctions(const ChastePoint< ELEMENT_DIM > &rPoint, c_vector< double, ELEMENT_DIM+1 > &rReturnValue)
static void Zero(Mat matrix)
static unsigned GetMyRank()
static void Finalise(Vec vector)
static void Zero(Vec vector)
static void ComputeBasisFunctions(const ChastePoint< ELEMENT_DIM > &rPoint, c_vector< double,(ELEMENT_DIM+1) *(ELEMENT_DIM+2)/2 > &rReturnValue)
static void ComputeTransformedBasisFunctionDerivatives(const ChastePoint< ELEMENT_DIM > &rPoint, const c_matrix< double, ELEMENT_DIM, ELEMENT_DIM > &rInverseJacobian, c_matrix< double, ELEMENT_DIM,(ELEMENT_DIM+1) *(ELEMENT_DIM+2)/2 > &rReturnValue)