xref: /libCEED/examples/solids/qfunctions/finite-strain-neo-hookean.h (revision c8565611f4f88586c9ab8f49f4be6e8b5d8096a7)
1*c8565611SJeremy L Thompson // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors.
2*c8565611SJeremy L Thompson // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3*c8565611SJeremy L Thompson //
4*c8565611SJeremy L Thompson // SPDX-License-Identifier: BSD-2-Clause
5*c8565611SJeremy L Thompson //
6*c8565611SJeremy L Thompson // This file is part of CEED:  http://github.com/ceed
7*c8565611SJeremy L Thompson 
8*c8565611SJeremy L Thompson /// @file
9*c8565611SJeremy L Thompson /// Hyperelasticity, finite strain for solid mechanics example using PETSc
10*c8565611SJeremy L Thompson 
11*c8565611SJeremy L Thompson #include <ceed.h>
12*c8565611SJeremy L Thompson #include <math.h>
13*c8565611SJeremy L Thompson 
14*c8565611SJeremy L Thompson #ifndef PHYSICS_STRUCT
15*c8565611SJeremy L Thompson #define PHYSICS_STRUCT
16*c8565611SJeremy L Thompson typedef struct Physics_private *Physics;
17*c8565611SJeremy L Thompson struct Physics_private {
18*c8565611SJeremy L Thompson   CeedScalar nu;  // Poisson's ratio
19*c8565611SJeremy L Thompson   CeedScalar E;   // Young's Modulus
20*c8565611SJeremy L Thompson };
21*c8565611SJeremy L Thompson #endif
22*c8565611SJeremy L Thompson 
23*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
24*c8565611SJeremy L Thompson // Series approximation of log1p()
25*c8565611SJeremy L Thompson //  log1p() is not vectorized in libc
26*c8565611SJeremy L Thompson //
27*c8565611SJeremy L Thompson //  The series expansion is accurate to 1e-7 in the range sqrt(2)/2 < J < sqrt(2), with machine precision accuracy near J=1.
28*c8565611SJeremy L Thompson //  The initialization extends this range to 0.35 ~= sqrt(2)/4 < J < sqrt(2)*2 ~= 2.83, which should be sufficient for applications of the Neo-Hookean
29*c8565611SJeremy L Thompson //  model.
30*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
31*c8565611SJeremy L Thompson #ifndef LOG1P_SERIES_SHIFTED
32*c8565611SJeremy L Thompson #define LOG1P_SERIES_SHIFTED
33*c8565611SJeremy L Thompson CEED_QFUNCTION_HELPER CeedScalar log1p_series_shifted(CeedScalar x) {
34*c8565611SJeremy L Thompson   const CeedScalar left = sqrt(2.) / 2 - 1, right = sqrt(2.) - 1;
35*c8565611SJeremy L Thompson   CeedScalar       sum = 0;
36*c8565611SJeremy L Thompson   if (1) {           // Disable if the smaller range sqrt(2)/2 < J < sqrt(2) is sufficient
37*c8565611SJeremy L Thompson     if (x < left) {  // Replace if with while for arbitrary range (may hurt vectorization)
38*c8565611SJeremy L Thompson       sum -= log(2.) / 2;
39*c8565611SJeremy L Thompson       x = 1 + 2 * x;
40*c8565611SJeremy L Thompson     } else if (right < x) {
41*c8565611SJeremy L Thompson       sum += log(2.) / 2;
42*c8565611SJeremy L Thompson       x = (x - 1) / 2;
43*c8565611SJeremy L Thompson     }
44*c8565611SJeremy L Thompson   }
45*c8565611SJeremy L Thompson   CeedScalar       y  = x / (2. + x);
46*c8565611SJeremy L Thompson   const CeedScalar y2 = y * y;
47*c8565611SJeremy L Thompson   sum += y;
48*c8565611SJeremy L Thompson   y *= y2;
49*c8565611SJeremy L Thompson   sum += y / 3;
50*c8565611SJeremy L Thompson   y *= y2;
51*c8565611SJeremy L Thompson   sum += y / 5;
52*c8565611SJeremy L Thompson   y *= y2;
53*c8565611SJeremy L Thompson   sum += y / 7;
54*c8565611SJeremy L Thompson   return 2 * sum;
55*c8565611SJeremy L Thompson }
56*c8565611SJeremy L Thompson #endif
57*c8565611SJeremy L Thompson 
58*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
59*c8565611SJeremy L Thompson // Compute det F - 1
60*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
61*c8565611SJeremy L Thompson #ifndef DETJM1
62*c8565611SJeremy L Thompson #define DETJM1
63*c8565611SJeremy L Thompson CEED_QFUNCTION_HELPER CeedScalar computeJM1(const CeedScalar grad_u[3][3]) {
64*c8565611SJeremy L Thompson   return grad_u[0][0] * (grad_u[1][1] * grad_u[2][2] - grad_u[1][2] * grad_u[2][1]) +
65*c8565611SJeremy L Thompson          grad_u[0][1] * (grad_u[1][2] * grad_u[2][0] - grad_u[1][0] * grad_u[2][2]) +
66*c8565611SJeremy L Thompson          grad_u[0][2] * (grad_u[1][0] * grad_u[2][1] - grad_u[2][0] * grad_u[1][1]) + grad_u[0][0] + grad_u[1][1] + grad_u[2][2] +
67*c8565611SJeremy L Thompson          grad_u[0][0] * grad_u[1][1] + grad_u[0][0] * grad_u[2][2] + grad_u[1][1] * grad_u[2][2] - grad_u[0][1] * grad_u[1][0] -
68*c8565611SJeremy L Thompson          grad_u[0][2] * grad_u[2][0] - grad_u[1][2] * grad_u[2][1];
69*c8565611SJeremy L Thompson }
70*c8565611SJeremy L Thompson #endif
71*c8565611SJeremy L Thompson 
72*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
73*c8565611SJeremy L Thompson // Compute matrix^(-1), where matrix is symetric, returns array of 6
74*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
75*c8565611SJeremy L Thompson #ifndef MatinvSym
76*c8565611SJeremy L Thompson #define MatinvSym
77*c8565611SJeremy L Thompson CEED_QFUNCTION_HELPER int computeMatinvSym(const CeedScalar A[3][3], const CeedScalar detA, CeedScalar Ainv[6]) {
78*c8565611SJeremy L Thompson   // Compute A^(-1) : A-Inverse
79*c8565611SJeremy L Thompson   CeedScalar B[6] = {
80*c8565611SJeremy L Thompson       A[1][1] * A[2][2] - A[1][2] * A[2][1], /* *NOPAD* */
81*c8565611SJeremy L Thompson       A[0][0] * A[2][2] - A[0][2] * A[2][0], /* *NOPAD* */
82*c8565611SJeremy L Thompson       A[0][0] * A[1][1] - A[0][1] * A[1][0], /* *NOPAD* */
83*c8565611SJeremy L Thompson       A[0][2] * A[1][0] - A[0][0] * A[1][2], /* *NOPAD* */
84*c8565611SJeremy L Thompson       A[0][1] * A[1][2] - A[0][2] * A[1][1], /* *NOPAD* */
85*c8565611SJeremy L Thompson       A[0][2] * A[2][1] - A[0][1] * A[2][2]  /* *NOPAD* */
86*c8565611SJeremy L Thompson   };
87*c8565611SJeremy L Thompson   for (CeedInt m = 0; m < 6; m++) Ainv[m] = B[m] / (detA);
88*c8565611SJeremy L Thompson 
89*c8565611SJeremy L Thompson   return CEED_ERROR_SUCCESS;
90*c8565611SJeremy L Thompson }
91*c8565611SJeremy L Thompson #endif
92*c8565611SJeremy L Thompson 
93*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
94*c8565611SJeremy L Thompson // Common computations between FS and dFS
95*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
96*c8565611SJeremy L Thompson CEED_QFUNCTION_HELPER int commonFS(const CeedScalar lambda, const CeedScalar mu, const CeedScalar grad_u[3][3], CeedScalar Swork[6],
97*c8565611SJeremy L Thompson                                    CeedScalar Cinvwork[6], CeedScalar *logJ) {
98*c8565611SJeremy L Thompson   // E - Green-Lagrange strain tensor
99*c8565611SJeremy L Thompson   //     E = 1/2 (grad_u + grad_u^T + grad_u^T*grad_u)
100*c8565611SJeremy L Thompson   const CeedInt indj[6] = {0, 1, 2, 1, 0, 0}, indk[6] = {0, 1, 2, 2, 2, 1};
101*c8565611SJeremy L Thompson   CeedScalar    E2work[6];
102*c8565611SJeremy L Thompson   for (CeedInt m = 0; m < 6; m++) {
103*c8565611SJeremy L Thompson     E2work[m] = grad_u[indj[m]][indk[m]] + grad_u[indk[m]][indj[m]];
104*c8565611SJeremy L Thompson     for (CeedInt n = 0; n < 3; n++) E2work[m] += grad_u[n][indj[m]] * grad_u[n][indk[m]];
105*c8565611SJeremy L Thompson   }
106*c8565611SJeremy L Thompson   CeedScalar E2[3][3] = {
107*c8565611SJeremy L Thompson       {E2work[0], E2work[5], E2work[4]},
108*c8565611SJeremy L Thompson       {E2work[5], E2work[1], E2work[3]},
109*c8565611SJeremy L Thompson       {E2work[4], E2work[3], E2work[2]}
110*c8565611SJeremy L Thompson   };
111*c8565611SJeremy L Thompson   // J-1
112*c8565611SJeremy L Thompson   const CeedScalar Jm1 = computeJM1(grad_u);
113*c8565611SJeremy L Thompson 
114*c8565611SJeremy L Thompson   // C : right Cauchy-Green tensor
115*c8565611SJeremy L Thompson   // C = I + 2E
116*c8565611SJeremy L Thompson   const CeedScalar C[3][3] = {
117*c8565611SJeremy L Thompson       {1 + E2[0][0], E2[0][1],     E2[0][2]    },
118*c8565611SJeremy L Thompson       {E2[0][1],     1 + E2[1][1], E2[1][2]    },
119*c8565611SJeremy L Thompson       {E2[0][2],     E2[1][2],     1 + E2[2][2]}
120*c8565611SJeremy L Thompson   };
121*c8565611SJeremy L Thompson 
122*c8565611SJeremy L Thompson   // Compute C^(-1) : C-Inverse
123*c8565611SJeremy L Thompson   const CeedScalar detC = (Jm1 + 1.) * (Jm1 + 1.);
124*c8565611SJeremy L Thompson   computeMatinvSym(C, detC, Cinvwork);
125*c8565611SJeremy L Thompson 
126*c8565611SJeremy L Thompson   const CeedScalar C_inv[3][3] = {
127*c8565611SJeremy L Thompson       {Cinvwork[0], Cinvwork[5], Cinvwork[4]},
128*c8565611SJeremy L Thompson       {Cinvwork[5], Cinvwork[1], Cinvwork[3]},
129*c8565611SJeremy L Thompson       {Cinvwork[4], Cinvwork[3], Cinvwork[2]}
130*c8565611SJeremy L Thompson   };
131*c8565611SJeremy L Thompson 
132*c8565611SJeremy L Thompson   // Compute the Second Piola-Kirchhoff (S)
133*c8565611SJeremy L Thompson   *logJ = log1p_series_shifted(Jm1);
134*c8565611SJeremy L Thompson   for (CeedInt m = 0; m < 6; m++) {
135*c8565611SJeremy L Thompson     Swork[m] = (lambda * (*logJ)) * Cinvwork[m];
136*c8565611SJeremy L Thompson     for (CeedInt n = 0; n < 3; n++) Swork[m] += mu * C_inv[indj[m]][n] * E2[n][indk[m]];
137*c8565611SJeremy L Thompson   }
138*c8565611SJeremy L Thompson 
139*c8565611SJeremy L Thompson   return CEED_ERROR_SUCCESS;
140*c8565611SJeremy L Thompson }
141*c8565611SJeremy L Thompson 
142*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
143*c8565611SJeremy L Thompson // Residual evaluation for hyperelasticity, finite strain
144*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
145*c8565611SJeremy L Thompson CEED_QFUNCTION(ElasFSResidual_NH)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
146*c8565611SJeremy L Thompson   // Inputs
147*c8565611SJeremy L Thompson   const CeedScalar(*ug)[3][CEED_Q_VLA] = (const CeedScalar(*)[3][CEED_Q_VLA])in[0], (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[1];
148*c8565611SJeremy L Thompson 
149*c8565611SJeremy L Thompson   // Outputs
150*c8565611SJeremy L Thompson   CeedScalar(*dvdX)[3][CEED_Q_VLA] = (CeedScalar(*)[3][CEED_Q_VLA])out[0];
151*c8565611SJeremy L Thompson   // Store grad_u for HyperFSdF (Jacobian of HyperFSF)
152*c8565611SJeremy L Thompson   CeedScalar(*grad_u)[3][CEED_Q_VLA] = (CeedScalar(*)[3][CEED_Q_VLA])out[1];
153*c8565611SJeremy L Thompson 
154*c8565611SJeremy L Thompson   // Context
155*c8565611SJeremy L Thompson   const Physics    context = (Physics)ctx;
156*c8565611SJeremy L Thompson   const CeedScalar E       = context->E;
157*c8565611SJeremy L Thompson   const CeedScalar nu      = context->nu;
158*c8565611SJeremy L Thompson   const CeedScalar TwoMu   = E / (1 + nu);
159*c8565611SJeremy L Thompson   const CeedScalar mu      = TwoMu / 2;
160*c8565611SJeremy L Thompson   const CeedScalar Kbulk   = E / (3 * (1 - 2 * nu));  // Bulk Modulus
161*c8565611SJeremy L Thompson   const CeedScalar lambda  = (3 * Kbulk - TwoMu) / 3;
162*c8565611SJeremy L Thompson 
163*c8565611SJeremy L Thompson   // Formulation Terminology:
164*c8565611SJeremy L Thompson   //  I3    : 3x3 Identity matrix
165*c8565611SJeremy L Thompson   //  C     : right Cauchy-Green tensor
166*c8565611SJeremy L Thompson   //  C_inv : inverse of C
167*c8565611SJeremy L Thompson   //  F     : deformation gradient
168*c8565611SJeremy L Thompson   //  S     : 2nd Piola-Kirchhoff (in current config)
169*c8565611SJeremy L Thompson   //  P     : 1st Piola-Kirchhoff (in referential config)
170*c8565611SJeremy L Thompson   // Formulation:
171*c8565611SJeremy L Thompson   //  F =  I3 + grad_ue
172*c8565611SJeremy L Thompson   //  J = det(F)
173*c8565611SJeremy L Thompson   //  C = F(^T)*F
174*c8565611SJeremy L Thompson   //  S = mu*I3 + (lambda*log(J)-mu)*C_inv;
175*c8565611SJeremy L Thompson   //  P = F*S
176*c8565611SJeremy L Thompson 
177*c8565611SJeremy L Thompson   // Quadrature Point Loop
178*c8565611SJeremy L Thompson   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
179*c8565611SJeremy L Thompson     // Read spatial derivatives of u
180*c8565611SJeremy L Thompson     const CeedScalar du[3][3] = {
181*c8565611SJeremy L Thompson         {ug[0][0][i], ug[1][0][i], ug[2][0][i]},
182*c8565611SJeremy L Thompson         {ug[0][1][i], ug[1][1][i], ug[2][1][i]},
183*c8565611SJeremy L Thompson         {ug[0][2][i], ug[1][2][i], ug[2][2][i]}
184*c8565611SJeremy L Thompson     };
185*c8565611SJeremy L Thompson     // -- Qdata
186*c8565611SJeremy L Thompson     const CeedScalar wdetJ      = q_data[0][i];
187*c8565611SJeremy L Thompson     const CeedScalar dXdx[3][3] = {
188*c8565611SJeremy L Thompson         {q_data[1][i], q_data[2][i], q_data[3][i]},
189*c8565611SJeremy L Thompson         {q_data[4][i], q_data[5][i], q_data[6][i]},
190*c8565611SJeremy L Thompson         {q_data[7][i], q_data[8][i], q_data[9][i]}
191*c8565611SJeremy L Thompson     };
192*c8565611SJeremy L Thompson 
193*c8565611SJeremy L Thompson     // Compute grad_u
194*c8565611SJeremy L Thompson     //   dXdx = (dx/dX)^(-1)
195*c8565611SJeremy L Thompson     // Apply dXdx to du = grad_u
196*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {    // Component
197*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) {  // Derivative
198*c8565611SJeremy L Thompson         grad_u[j][k][i] = 0;
199*c8565611SJeremy L Thompson         for (CeedInt m = 0; m < 3; m++) grad_u[j][k][i] += dXdx[m][k] * du[j][m];
200*c8565611SJeremy L Thompson       }
201*c8565611SJeremy L Thompson     }
202*c8565611SJeremy L Thompson 
203*c8565611SJeremy L Thompson     // I3 : 3x3 Identity matrix
204*c8565611SJeremy L Thompson     // Compute The Deformation Gradient : F = I3 + grad_u
205*c8565611SJeremy L Thompson     const CeedScalar F[3][3] = {
206*c8565611SJeremy L Thompson         {grad_u[0][0][i] + 1, grad_u[0][1][i],     grad_u[0][2][i]    },
207*c8565611SJeremy L Thompson         {grad_u[1][0][i],     grad_u[1][1][i] + 1, grad_u[1][2][i]    },
208*c8565611SJeremy L Thompson         {grad_u[2][0][i],     grad_u[2][1][i],     grad_u[2][2][i] + 1}
209*c8565611SJeremy L Thompson     };
210*c8565611SJeremy L Thompson 
211*c8565611SJeremy L Thompson     // Common components of finite strain calculations
212*c8565611SJeremy L Thompson     CeedScalar       Swork[6], Cinvwork[6], logJ;
213*c8565611SJeremy L Thompson     const CeedScalar tempgradu[3][3] = {
214*c8565611SJeremy L Thompson         {grad_u[0][0][i], grad_u[0][1][i], grad_u[0][2][i]},
215*c8565611SJeremy L Thompson         {grad_u[1][0][i], grad_u[1][1][i], grad_u[1][2][i]},
216*c8565611SJeremy L Thompson         {grad_u[2][0][i], grad_u[2][1][i], grad_u[2][2][i]}
217*c8565611SJeremy L Thompson     };
218*c8565611SJeremy L Thompson     commonFS(lambda, mu, tempgradu, Swork, Cinvwork, &logJ);
219*c8565611SJeremy L Thompson 
220*c8565611SJeremy L Thompson     // Second Piola-Kirchhoff (S)
221*c8565611SJeremy L Thompson     const CeedScalar S[3][3] = {
222*c8565611SJeremy L Thompson         {Swork[0], Swork[5], Swork[4]},
223*c8565611SJeremy L Thompson         {Swork[5], Swork[1], Swork[3]},
224*c8565611SJeremy L Thompson         {Swork[4], Swork[3], Swork[2]}
225*c8565611SJeremy L Thompson     };
226*c8565611SJeremy L Thompson 
227*c8565611SJeremy L Thompson     // Compute the First Piola-Kirchhoff : P = F*S
228*c8565611SJeremy L Thompson     CeedScalar P[3][3];
229*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
230*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) {
231*c8565611SJeremy L Thompson         P[j][k] = 0;
232*c8565611SJeremy L Thompson         for (CeedInt m = 0; m < 3; m++) P[j][k] += F[j][m] * S[m][k];
233*c8565611SJeremy L Thompson       }
234*c8565611SJeremy L Thompson     }
235*c8565611SJeremy L Thompson 
236*c8565611SJeremy L Thompson     // Apply dXdx^T and weight to P (First Piola-Kirchhoff)
237*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {    // Component
238*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) {  // Derivative
239*c8565611SJeremy L Thompson         dvdX[k][j][i] = 0;
240*c8565611SJeremy L Thompson         for (CeedInt m = 0; m < 3; m++) dvdX[k][j][i] += dXdx[k][m] * P[j][m] * wdetJ;
241*c8565611SJeremy L Thompson       }
242*c8565611SJeremy L Thompson     }
243*c8565611SJeremy L Thompson   }  // End of Quadrature Point Loop
244*c8565611SJeremy L Thompson 
245*c8565611SJeremy L Thompson   return CEED_ERROR_SUCCESS;
246*c8565611SJeremy L Thompson }
247*c8565611SJeremy L Thompson 
248*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
249*c8565611SJeremy L Thompson // Jacobian evaluation for hyperelasticity, finite strain
250*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
251*c8565611SJeremy L Thompson CEED_QFUNCTION(ElasFSJacobian_NH)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
252*c8565611SJeremy L Thompson   // Inputs
253*c8565611SJeremy L Thompson   const CeedScalar(*deltaug)[3][CEED_Q_VLA] = (const CeedScalar(*)[3][CEED_Q_VLA])in[0],
254*c8565611SJeremy L Thompson         (*q_data)[CEED_Q_VLA]               = (const CeedScalar(*)[CEED_Q_VLA])in[1];
255*c8565611SJeremy L Thompson   // grad_u is used for hyperelasticity (non-linear)
256*c8565611SJeremy L Thompson   const CeedScalar(*grad_u)[3][CEED_Q_VLA] = (const CeedScalar(*)[3][CEED_Q_VLA])in[2];
257*c8565611SJeremy L Thompson 
258*c8565611SJeremy L Thompson   // Outputs
259*c8565611SJeremy L Thompson   CeedScalar(*deltadvdX)[3][CEED_Q_VLA] = (CeedScalar(*)[3][CEED_Q_VLA])out[0];
260*c8565611SJeremy L Thompson 
261*c8565611SJeremy L Thompson   // Context
262*c8565611SJeremy L Thompson   const Physics    context = (Physics)ctx;
263*c8565611SJeremy L Thompson   const CeedScalar E       = context->E;
264*c8565611SJeremy L Thompson   const CeedScalar nu      = context->nu;
265*c8565611SJeremy L Thompson 
266*c8565611SJeremy L Thompson   // Constants
267*c8565611SJeremy L Thompson   const CeedScalar TwoMu  = E / (1 + nu);
268*c8565611SJeremy L Thompson   const CeedScalar mu     = TwoMu / 2;
269*c8565611SJeremy L Thompson   const CeedScalar Kbulk  = E / (3 * (1 - 2 * nu));  // Bulk Modulus
270*c8565611SJeremy L Thompson   const CeedScalar lambda = (3 * Kbulk - TwoMu) / 3;
271*c8565611SJeremy L Thompson 
272*c8565611SJeremy L Thompson   // Quadrature Point Loop
273*c8565611SJeremy L Thompson   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
274*c8565611SJeremy L Thompson     // Read spatial derivatives of delta_u
275*c8565611SJeremy L Thompson     const CeedScalar deltadu[3][3] = {
276*c8565611SJeremy L Thompson         {deltaug[0][0][i], deltaug[1][0][i], deltaug[2][0][i]},
277*c8565611SJeremy L Thompson         {deltaug[0][1][i], deltaug[1][1][i], deltaug[2][1][i]},
278*c8565611SJeremy L Thompson         {deltaug[0][2][i], deltaug[1][2][i], deltaug[2][2][i]}
279*c8565611SJeremy L Thompson     };
280*c8565611SJeremy L Thompson     // -- Qdata
281*c8565611SJeremy L Thompson     const CeedScalar wdetJ      = q_data[0][i];
282*c8565611SJeremy L Thompson     const CeedScalar dXdx[3][3] = {
283*c8565611SJeremy L Thompson         {q_data[1][i], q_data[2][i], q_data[3][i]},
284*c8565611SJeremy L Thompson         {q_data[4][i], q_data[5][i], q_data[6][i]},
285*c8565611SJeremy L Thompson         {q_data[7][i], q_data[8][i], q_data[9][i]}
286*c8565611SJeremy L Thompson     };
287*c8565611SJeremy L Thompson 
288*c8565611SJeremy L Thompson     // Compute graddeltau
289*c8565611SJeremy L Thompson     //   dXdx = (dx/dX)^(-1)
290*c8565611SJeremy L Thompson     // Apply dXdx to deltadu = graddelta
291*c8565611SJeremy L Thompson     CeedScalar graddeltau[3][3];
292*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {    // Component
293*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) {  // Derivative
294*c8565611SJeremy L Thompson         graddeltau[j][k] = 0;
295*c8565611SJeremy L Thompson         for (CeedInt m = 0; m < 3; m++) graddeltau[j][k] += dXdx[m][k] * deltadu[j][m];
296*c8565611SJeremy L Thompson       }
297*c8565611SJeremy L Thompson     }
298*c8565611SJeremy L Thompson 
299*c8565611SJeremy L Thompson     // I3 : 3x3 Identity matrix
300*c8565611SJeremy L Thompson     // Deformation Gradient : F = I3 + grad_u
301*c8565611SJeremy L Thompson     const CeedScalar F[3][3] = {
302*c8565611SJeremy L Thompson         {grad_u[0][0][i] + 1, grad_u[0][1][i],     grad_u[0][2][i]    },
303*c8565611SJeremy L Thompson         {grad_u[1][0][i],     grad_u[1][1][i] + 1, grad_u[1][2][i]    },
304*c8565611SJeremy L Thompson         {grad_u[2][0][i],     grad_u[2][1][i],     grad_u[2][2][i] + 1}
305*c8565611SJeremy L Thompson     };
306*c8565611SJeremy L Thompson 
307*c8565611SJeremy L Thompson     // Common components of finite strain calculations
308*c8565611SJeremy L Thompson     CeedScalar       Swork[6], Cinvwork[6], logJ;
309*c8565611SJeremy L Thompson     const CeedScalar tempgradu[3][3] = {
310*c8565611SJeremy L Thompson         {grad_u[0][0][i], grad_u[0][1][i], grad_u[0][2][i]},
311*c8565611SJeremy L Thompson         {grad_u[1][0][i], grad_u[1][1][i], grad_u[1][2][i]},
312*c8565611SJeremy L Thompson         {grad_u[2][0][i], grad_u[2][1][i], grad_u[2][2][i]}
313*c8565611SJeremy L Thompson     };
314*c8565611SJeremy L Thompson     commonFS(lambda, mu, tempgradu, Swork, Cinvwork, &logJ);
315*c8565611SJeremy L Thompson 
316*c8565611SJeremy L Thompson     // deltaE - Green-Lagrange strain tensor
317*c8565611SJeremy L Thompson     const CeedInt indj[6] = {0, 1, 2, 1, 0, 0}, indk[6] = {0, 1, 2, 2, 2, 1};
318*c8565611SJeremy L Thompson     CeedScalar    deltaEwork[6];
319*c8565611SJeremy L Thompson     for (CeedInt m = 0; m < 6; m++) {
320*c8565611SJeremy L Thompson       deltaEwork[m] = 0;
321*c8565611SJeremy L Thompson       for (CeedInt n = 0; n < 3; n++) deltaEwork[m] += (graddeltau[n][indj[m]] * F[n][indk[m]] + F[n][indj[m]] * graddeltau[n][indk[m]]) / 2.;
322*c8565611SJeremy L Thompson     }
323*c8565611SJeremy L Thompson     CeedScalar deltaE[3][3] = {
324*c8565611SJeremy L Thompson         {deltaEwork[0], deltaEwork[5], deltaEwork[4]},
325*c8565611SJeremy L Thompson         {deltaEwork[5], deltaEwork[1], deltaEwork[3]},
326*c8565611SJeremy L Thompson         {deltaEwork[4], deltaEwork[3], deltaEwork[2]}
327*c8565611SJeremy L Thompson     };
328*c8565611SJeremy L Thompson 
329*c8565611SJeremy L Thompson     // C : right Cauchy-Green tensor
330*c8565611SJeremy L Thompson     // C^(-1) : C-Inverse
331*c8565611SJeremy L Thompson     const CeedScalar C_inv[3][3] = {
332*c8565611SJeremy L Thompson         {Cinvwork[0], Cinvwork[5], Cinvwork[4]},
333*c8565611SJeremy L Thompson         {Cinvwork[5], Cinvwork[1], Cinvwork[3]},
334*c8565611SJeremy L Thompson         {Cinvwork[4], Cinvwork[3], Cinvwork[2]}
335*c8565611SJeremy L Thompson     };
336*c8565611SJeremy L Thompson 
337*c8565611SJeremy L Thompson     // Second Piola-Kirchhoff (S)
338*c8565611SJeremy L Thompson     const CeedScalar S[3][3] = {
339*c8565611SJeremy L Thompson         {Swork[0], Swork[5], Swork[4]},
340*c8565611SJeremy L Thompson         {Swork[5], Swork[1], Swork[3]},
341*c8565611SJeremy L Thompson         {Swork[4], Swork[3], Swork[2]}
342*c8565611SJeremy L Thompson     };
343*c8565611SJeremy L Thompson 
344*c8565611SJeremy L Thompson     // deltaS = dSdE:deltaE
345*c8565611SJeremy L Thompson     //      = lambda(C_inv:deltaE)C_inv + 2(mu-lambda*log(J))C_inv*deltaE*C_inv
346*c8565611SJeremy L Thompson     // -- C_inv:deltaE
347*c8565611SJeremy L Thompson     CeedScalar Cinv_contract_E = 0;
348*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
349*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) Cinv_contract_E += C_inv[j][k] * deltaE[j][k];
350*c8565611SJeremy L Thompson     }
351*c8565611SJeremy L Thompson     // -- deltaE*C_inv
352*c8565611SJeremy L Thompson     CeedScalar deltaECinv[3][3];
353*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
354*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) {
355*c8565611SJeremy L Thompson         deltaECinv[j][k] = 0;
356*c8565611SJeremy L Thompson         for (CeedInt m = 0; m < 3; m++) deltaECinv[j][k] += deltaE[j][m] * C_inv[m][k];
357*c8565611SJeremy L Thompson       }
358*c8565611SJeremy L Thompson     }
359*c8565611SJeremy L Thompson     // -- intermediate deltaS = C_inv*deltaE*C_inv
360*c8565611SJeremy L Thompson     CeedScalar deltaS[3][3];
361*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
362*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) {
363*c8565611SJeremy L Thompson         deltaS[j][k] = 0;
364*c8565611SJeremy L Thompson         for (CeedInt m = 0; m < 3; m++) deltaS[j][k] += C_inv[j][m] * deltaECinv[m][k];
365*c8565611SJeremy L Thompson       }
366*c8565611SJeremy L Thompson     }
367*c8565611SJeremy L Thompson     // -- deltaS = lambda(C_inv:deltaE)C_inv - 2(lambda*log(J)-mu)*(intermediate)
368*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
369*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) deltaS[j][k] = lambda * Cinv_contract_E * C_inv[j][k] - 2. * (lambda * logJ - mu) * deltaS[j][k];
370*c8565611SJeremy L Thompson     }
371*c8565611SJeremy L Thompson 
372*c8565611SJeremy L Thompson     // deltaP = dPdF:deltaF = deltaF*S + F*deltaS
373*c8565611SJeremy L Thompson     CeedScalar deltaP[3][3];
374*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
375*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) {
376*c8565611SJeremy L Thompson         deltaP[j][k] = 0;
377*c8565611SJeremy L Thompson         for (CeedInt m = 0; m < 3; m++) deltaP[j][k] += graddeltau[j][m] * S[m][k] + F[j][m] * deltaS[m][k];
378*c8565611SJeremy L Thompson       }
379*c8565611SJeremy L Thompson     }
380*c8565611SJeremy L Thompson 
381*c8565611SJeremy L Thompson     // Apply dXdx^T and weight
382*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {    // Component
383*c8565611SJeremy L Thompson       for (CeedInt k = 0; k < 3; k++) {  // Derivative
384*c8565611SJeremy L Thompson         deltadvdX[k][j][i] = 0;
385*c8565611SJeremy L Thompson         for (CeedInt m = 0; m < 3; m++) deltadvdX[k][j][i] += dXdx[k][m] * deltaP[j][m] * wdetJ;
386*c8565611SJeremy L Thompson       }
387*c8565611SJeremy L Thompson     }
388*c8565611SJeremy L Thompson   }  // End of Quadrature Point Loop
389*c8565611SJeremy L Thompson 
390*c8565611SJeremy L Thompson   return CEED_ERROR_SUCCESS;
391*c8565611SJeremy L Thompson }
392*c8565611SJeremy L Thompson 
393*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
394*c8565611SJeremy L Thompson // Strain energy computation for hyperelasticity, finite strain
395*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
396*c8565611SJeremy L Thompson CEED_QFUNCTION(ElasFSEnergy_NH)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
397*c8565611SJeremy L Thompson   // Inputs
398*c8565611SJeremy L Thompson   const CeedScalar(*ug)[3][CEED_Q_VLA] = (const CeedScalar(*)[3][CEED_Q_VLA])in[0], (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[1];
399*c8565611SJeremy L Thompson 
400*c8565611SJeremy L Thompson   // Outputs
401*c8565611SJeremy L Thompson   CeedScalar(*energy) = (CeedScalar(*))out[0];
402*c8565611SJeremy L Thompson 
403*c8565611SJeremy L Thompson   // Context
404*c8565611SJeremy L Thompson   const Physics    context = (Physics)ctx;
405*c8565611SJeremy L Thompson   const CeedScalar E       = context->E;
406*c8565611SJeremy L Thompson   const CeedScalar nu      = context->nu;
407*c8565611SJeremy L Thompson   const CeedScalar TwoMu   = E / (1 + nu);
408*c8565611SJeremy L Thompson   const CeedScalar mu      = TwoMu / 2;
409*c8565611SJeremy L Thompson   const CeedScalar Kbulk   = E / (3 * (1 - 2 * nu));  // Bulk Modulus
410*c8565611SJeremy L Thompson   const CeedScalar lambda  = (3 * Kbulk - TwoMu) / 3;
411*c8565611SJeremy L Thompson 
412*c8565611SJeremy L Thompson   // Quadrature Point Loop
413*c8565611SJeremy L Thompson   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
414*c8565611SJeremy L Thompson     // Read spatial derivatives of u
415*c8565611SJeremy L Thompson     const CeedScalar du[3][3] = {
416*c8565611SJeremy L Thompson         {ug[0][0][i], ug[1][0][i], ug[2][0][i]},
417*c8565611SJeremy L Thompson         {ug[0][1][i], ug[1][1][i], ug[2][1][i]},
418*c8565611SJeremy L Thompson         {ug[0][2][i], ug[1][2][i], ug[2][2][i]}
419*c8565611SJeremy L Thompson     };
420*c8565611SJeremy L Thompson     // -- Qdata
421*c8565611SJeremy L Thompson     const CeedScalar wdetJ      = q_data[0][i];
422*c8565611SJeremy L Thompson     const CeedScalar dXdx[3][3] = {
423*c8565611SJeremy L Thompson         {q_data[1][i], q_data[2][i], q_data[3][i]},
424*c8565611SJeremy L Thompson         {q_data[4][i], q_data[5][i], q_data[6][i]},
425*c8565611SJeremy L Thompson         {q_data[7][i], q_data[8][i], q_data[9][i]}
426*c8565611SJeremy L Thompson     };
427*c8565611SJeremy L Thompson 
428*c8565611SJeremy L Thompson     // Compute grad_u
429*c8565611SJeremy L Thompson     //   dXdx = (dx/dX)^(-1)
430*c8565611SJeremy L Thompson     // Apply dXdx to du = grad_u
431*c8565611SJeremy L Thompson     CeedScalar grad_u[3][3];
432*c8565611SJeremy L Thompson     for (int j = 0; j < 3; j++) {    // Component
433*c8565611SJeremy L Thompson       for (int k = 0; k < 3; k++) {  // Derivative
434*c8565611SJeremy L Thompson         grad_u[j][k] = 0;
435*c8565611SJeremy L Thompson         for (int m = 0; m < 3; m++) grad_u[j][k] += dXdx[m][k] * du[j][m];
436*c8565611SJeremy L Thompson       }
437*c8565611SJeremy L Thompson     }
438*c8565611SJeremy L Thompson 
439*c8565611SJeremy L Thompson     // E - Green-Lagrange strain tensor
440*c8565611SJeremy L Thompson     //     E = 1/2 (grad_u + grad_u^T + grad_u^T*grad_u)
441*c8565611SJeremy L Thompson     const CeedInt indj[6] = {0, 1, 2, 1, 0, 0}, indk[6] = {0, 1, 2, 2, 2, 1};
442*c8565611SJeremy L Thompson     CeedScalar    E2work[6];
443*c8565611SJeremy L Thompson     for (CeedInt m = 0; m < 6; m++) {
444*c8565611SJeremy L Thompson       E2work[m] = grad_u[indj[m]][indk[m]] + grad_u[indk[m]][indj[m]];
445*c8565611SJeremy L Thompson       for (CeedInt n = 0; n < 3; n++) E2work[m] += grad_u[n][indj[m]] * grad_u[n][indk[m]];
446*c8565611SJeremy L Thompson     }
447*c8565611SJeremy L Thompson     CeedScalar E2[3][3] = {
448*c8565611SJeremy L Thompson         {E2work[0], E2work[5], E2work[4]},
449*c8565611SJeremy L Thompson         {E2work[5], E2work[1], E2work[3]},
450*c8565611SJeremy L Thompson         {E2work[4], E2work[3], E2work[2]}
451*c8565611SJeremy L Thompson     };
452*c8565611SJeremy L Thompson     const CeedScalar Jm1  = computeJM1(grad_u);
453*c8565611SJeremy L Thompson     const CeedScalar logJ = log1p_series_shifted(Jm1);
454*c8565611SJeremy L Thompson 
455*c8565611SJeremy L Thompson     // Strain energy Phi(E) for compressible Neo-Hookean
456*c8565611SJeremy L Thompson     energy[i] = (lambda * logJ * logJ / 2. - mu * logJ + mu * (E2[0][0] + E2[1][1] + E2[2][2]) / 2.) * wdetJ;
457*c8565611SJeremy L Thompson 
458*c8565611SJeremy L Thompson   }  // End of Quadrature Point Loop
459*c8565611SJeremy L Thompson 
460*c8565611SJeremy L Thompson   return CEED_ERROR_SUCCESS;
461*c8565611SJeremy L Thompson }
462*c8565611SJeremy L Thompson 
463*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
464*c8565611SJeremy L Thompson // Nodal diagnostic quantities for hyperelasticity, finite strain
465*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
466*c8565611SJeremy L Thompson CEED_QFUNCTION(ElasFSDiagnostic_NH)(void *ctx, CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) {
467*c8565611SJeremy L Thompson   // Inputs
468*c8565611SJeremy L Thompson   const CeedScalar(*u)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[0], (*ug)[3][CEED_Q_VLA] = (const CeedScalar(*)[3][CEED_Q_VLA])in[1],
469*c8565611SJeremy L Thompson         (*q_data)[CEED_Q_VLA] = (const CeedScalar(*)[CEED_Q_VLA])in[2];
470*c8565611SJeremy L Thompson 
471*c8565611SJeremy L Thompson   // Outputs
472*c8565611SJeremy L Thompson   CeedScalar(*diagnostic)[CEED_Q_VLA] = (CeedScalar(*)[CEED_Q_VLA])out[0];
473*c8565611SJeremy L Thompson 
474*c8565611SJeremy L Thompson   // Context
475*c8565611SJeremy L Thompson   const Physics    context = (Physics)ctx;
476*c8565611SJeremy L Thompson   const CeedScalar E       = context->E;
477*c8565611SJeremy L Thompson   const CeedScalar nu      = context->nu;
478*c8565611SJeremy L Thompson   const CeedScalar TwoMu   = E / (1 + nu);
479*c8565611SJeremy L Thompson   const CeedScalar mu      = TwoMu / 2;
480*c8565611SJeremy L Thompson   const CeedScalar Kbulk   = E / (3 * (1 - 2 * nu));  // Bulk Modulus
481*c8565611SJeremy L Thompson   const CeedScalar lambda  = (3 * Kbulk - TwoMu) / 3;
482*c8565611SJeremy L Thompson 
483*c8565611SJeremy L Thompson   // Quadrature Point Loop
484*c8565611SJeremy L Thompson   CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) {
485*c8565611SJeremy L Thompson     // Read spatial derivatives of u
486*c8565611SJeremy L Thompson     const CeedScalar du[3][3] = {
487*c8565611SJeremy L Thompson         {ug[0][0][i], ug[1][0][i], ug[2][0][i]},
488*c8565611SJeremy L Thompson         {ug[0][1][i], ug[1][1][i], ug[2][1][i]},
489*c8565611SJeremy L Thompson         {ug[0][2][i], ug[1][2][i], ug[2][2][i]}
490*c8565611SJeremy L Thompson     };
491*c8565611SJeremy L Thompson     // -- Qdata
492*c8565611SJeremy L Thompson     const CeedScalar dXdx[3][3] = {
493*c8565611SJeremy L Thompson         {q_data[1][i], q_data[2][i], q_data[3][i]},
494*c8565611SJeremy L Thompson         {q_data[4][i], q_data[5][i], q_data[6][i]},
495*c8565611SJeremy L Thompson         {q_data[7][i], q_data[8][i], q_data[9][i]}
496*c8565611SJeremy L Thompson     };
497*c8565611SJeremy L Thompson 
498*c8565611SJeremy L Thompson     // Compute grad_u
499*c8565611SJeremy L Thompson     //   dXdx = (dx/dX)^(-1)
500*c8565611SJeremy L Thompson     // Apply dXdx to du = grad_u
501*c8565611SJeremy L Thompson     CeedScalar grad_u[3][3];
502*c8565611SJeremy L Thompson     for (int j = 0; j < 3; j++) {    // Component
503*c8565611SJeremy L Thompson       for (int k = 0; k < 3; k++) {  // Derivative
504*c8565611SJeremy L Thompson         grad_u[j][k] = 0;
505*c8565611SJeremy L Thompson         for (int m = 0; m < 3; m++) grad_u[j][k] += dXdx[m][k] * du[j][m];
506*c8565611SJeremy L Thompson       }
507*c8565611SJeremy L Thompson     }
508*c8565611SJeremy L Thompson 
509*c8565611SJeremy L Thompson     // E - Green-Lagrange strain tensor
510*c8565611SJeremy L Thompson     //     E = 1/2 (grad_u + grad_u^T + grad_u^T*grad_u)
511*c8565611SJeremy L Thompson     const CeedInt indj[6] = {0, 1, 2, 1, 0, 0}, indk[6] = {0, 1, 2, 2, 2, 1};
512*c8565611SJeremy L Thompson     CeedScalar    E2work[6];
513*c8565611SJeremy L Thompson     for (CeedInt m = 0; m < 6; m++) {
514*c8565611SJeremy L Thompson       E2work[m] = grad_u[indj[m]][indk[m]] + grad_u[indk[m]][indj[m]];
515*c8565611SJeremy L Thompson       for (CeedInt n = 0; n < 3; n++) E2work[m] += grad_u[n][indj[m]] * grad_u[n][indk[m]];
516*c8565611SJeremy L Thompson     }
517*c8565611SJeremy L Thompson     CeedScalar E2[3][3] = {
518*c8565611SJeremy L Thompson         {E2work[0], E2work[5], E2work[4]},
519*c8565611SJeremy L Thompson         {E2work[5], E2work[1], E2work[3]},
520*c8565611SJeremy L Thompson         {E2work[4], E2work[3], E2work[2]}
521*c8565611SJeremy L Thompson     };
522*c8565611SJeremy L Thompson 
523*c8565611SJeremy L Thompson     // Displacement
524*c8565611SJeremy L Thompson     diagnostic[0][i] = u[0][i];
525*c8565611SJeremy L Thompson     diagnostic[1][i] = u[1][i];
526*c8565611SJeremy L Thompson     diagnostic[2][i] = u[2][i];
527*c8565611SJeremy L Thompson 
528*c8565611SJeremy L Thompson     // Pressure
529*c8565611SJeremy L Thompson     const CeedScalar Jm1  = computeJM1(grad_u);
530*c8565611SJeremy L Thompson     const CeedScalar logJ = log1p_series_shifted(Jm1);
531*c8565611SJeremy L Thompson     diagnostic[3][i]      = -lambda * logJ;
532*c8565611SJeremy L Thompson 
533*c8565611SJeremy L Thompson     // Stress tensor invariants
534*c8565611SJeremy L Thompson     diagnostic[4][i] = (E2[0][0] + E2[1][1] + E2[2][2]) / 2.;
535*c8565611SJeremy L Thompson     diagnostic[5][i] = 0.;
536*c8565611SJeremy L Thompson     for (CeedInt j = 0; j < 3; j++) {
537*c8565611SJeremy L Thompson       for (CeedInt m = 0; m < 3; m++) diagnostic[5][i] += E2[j][m] * E2[m][j] / 4.;
538*c8565611SJeremy L Thompson     }
539*c8565611SJeremy L Thompson     diagnostic[6][i] = Jm1 + 1.;
540*c8565611SJeremy L Thompson 
541*c8565611SJeremy L Thompson     // Strain energy
542*c8565611SJeremy L Thompson     diagnostic[7][i] = (lambda * logJ * logJ / 2. - mu * logJ + mu * (E2[0][0] + E2[1][1] + E2[2][2]) / 2.);
543*c8565611SJeremy L Thompson   }  // End of Quadrature Point Loop
544*c8565611SJeremy L Thompson 
545*c8565611SJeremy L Thompson   return CEED_ERROR_SUCCESS;
546*c8565611SJeremy L Thompson }
547*c8565611SJeremy L Thompson // -----------------------------------------------------------------------------
548