v0.16.3
Loading...
Searching...
No Matches
LogarithmicDirectionalDerivatives.hpp
Go to the documentation of this file.
1/**
2 * \file LogarithmicDirectionalDerivatives.hpp
3 * \brief Mixed directional derivatives of matrix exponentials for the
4 * reduced Hessian of the logarithmic plastic parametrisation.
5 *
6 * A jet, Matrix<N>, holds exp(A) together with its derivatives in up to N = 3
7 * directions. Block `mask` is the mixed derivative in the directions whose
8 * bits are set, block 0 is the value; for N = 3 with directions (p, s, e),
9 * block 3 is D^2 exp(A)[p, s] and block 7 is D^3 exp(A)[p, s, e]. Jets form
10 * an algebra: product() of the jets of two factors is the jet of their
11 * product, which gives the mixed derivatives of F = R U F^p from the jets of
12 * the three exponentials without differentiating the product by hand.
13 *
14 * The exponentials themselves are closed-form:
15 * - SymmetricExponential, for symmetric arguments (stretches), uses the
16 * eigen-decomposition. The value is EigenMatrix::getMat
17 * (MatrixFunction.hpp), the spectral form of isotropic tensor functions
18 * used in computational plasticity \cite miehe1998comparison
19 * \cite miehe2001algorithms. The derivatives are the divided-difference
20 * (Daleckii--Krein) representation of the Fr\'echet derivatives in the
21 * eigenbasis \cite daleckii1965integration, \cite bhatia1997matrix
22 * (Chapter V), \cite higham2008functions (Chapter 3), extended to second
23 * and third order as in \cite higham2014higher. The divided differences of
24 * exp at the eigenvalues are evaluated from the series in the complete
25 * homogeneous symmetric polynomials of the shifts from their mean when the
26 * spread is small, and recursively otherwise, which is the stable
27 * treatment of nearly coincident points \cite mccurdy1984accurate,
28 * \cite deboor2005divided, \cite opitz1964steigungsmatrizen.
29 * - RotationExponential, for exp(skew(w)), uses Rodrigues' formula and its
30 * derivatives from LieGroups::SO3 (Lie.hpp) \cite sola2018micro, with the
31 * small-angle series for the coefficient functions
32 * \cite grassia1998practical.
33 * No series is used for the matrix exponential itself; for the background
34 * on why series and scaling-and-squaring are a poor fit for derivatives see
35 * \cite moler2003nineteen and \cite higham2008functions (Chapter 10).
36 *
37 * Usage. Directions are written into the single-bit blocks of an argument
38 * jet, the base into block 0, and fill() writes the complete jet:
39 * \code
40 * using namespace LogarithmicDirectionalDerivatives;
41 * FTENSOR_INDEXES(3, i, j);
42 *
43 * Matrix<3> u, f, r; // arguments
44 * u[0](i, j) = t_H(i, j); // base: total log stretch
45 * u[1](i, j) = t_ph(i, j); // direction p (adjoint)
46 * u[2](i, j) = t_sh(i, j); // direction s
47 * u[4](i, j) = t_e(i, j); // direction e (basis function)
48 * f[0](i, j) = t_Hp(i, j); // plastic log stretch
49 * f[2](i, j) = t_vh(i, j); // only a direction s
50 * r[0](i, j) = skew(t_omega)(i, j); // rotation vector as skew block
51 * r[1](i, j) = skew(t_pr)(i, j);
52 *
53 * const SymmetricExponential stretch(u[0]), plastic(f[0]);
54 * const RotationExponential rotation(axial(r[0]));
55 * Matrix<3> U, Fp, R;
56 * stretch.fill(u, U); // U[7] = D^3 exp(H)[ph, sh, e]
57 * plastic.fill(f, Fp); // blocks with bit 1 or 4 are zero
58 * rotation.fill(r, R); // derivatives in the axial vectors
59 *
60 * const auto F = product(product(R, U), Fp); // jet of F = R U Fp
61 * double d2 = t_P(i, j) * F[3](i, j); // P : D^2 F[p, s]
62 * double d3 = t_P(i, j) * F[7](i, j); // P : D^3 F[p, s, e]
63 *
64 * // exp(-Hp) from the same eigenvectors, and a stand-alone derivative
65 * const auto inverse = plastic.negated();
66 * const auto dexp = stretch.first(t_e); // D exp(H)[e]
67 *
68 * // Neo-Hookean deviatoric curvature, f = C10 tr exp(2 dev H):
69 * const auto c = deviatorCurvature(c10, t_H, t_ph, t_sh);
70 * double D2f = c.second(i, j) * t_e(i, j); // D^2 f[s, e]
71 * double D3f = c.third(i, j) * t_e(i, j); // D^3 f[p, s, e]
72 * \endcode
73 * A zero direction block gives zero derivatives, so the same argument jet
74 * can be reused with one direction changed. A base object is constructed
75 * once per quadrature point and its fill() is called for every direction
76 * set; the cost of fill() is a few hundred flops.
77 */
78
79#ifndef ESHELBIAN_LOGARITHMIC_DIRECTIONAL_DERIVATIVES_HPP
80#define ESHELBIAN_LOGARITHMIC_DIRECTIONAL_DERIVATIVES_HPP
81
82#include <Lie.hpp>
83#include <MatrixFunction.hpp>
84
85namespace EshelbianPlasticity {
86
87/** Local directions for D(J_z - R_z^T p)[s,v], with p held fixed. */
93
94namespace LogarithmicDirectionalDerivatives {
98
99/** Cursor onto one 3x3 block of a jet; ++ moves to the next block. */
101
102/** Mixed derivatives in independent nilpotent directions. Bit masks denote
103 * subsets of directions; block `mask` is the derivative in the directions
104 * whose bits are set and block 0 is the value. All 2^N blocks live in one
105 * contiguous array, block `mask` at doubles [9 mask, 9 mask + 9), accessed
106 * through FTensor cursor views, so no block is copied and a loop over masks
107 * is a cursor increment. */
108template <int N> struct Matrix {
109 static constexpr int count = 1 << N;
110 static constexpr int blockSize = 9;
111 static constexpr int size = count * blockSize;
112 std::array<double, size> data;
113
114 Matrix() { data.fill(0.); }
115
116 /** View of block `mask`. Views are handles, so a const jet returns the
117 * same view type, as FTensor pointer tensors do. The view is built directly
118 * from the pointer: getFTensor2FromPtr goes through a ublas shallow array
119 * adaptor whose shared_array allocates, which is prohibitive here. */
120 View operator[](int mask) const {
121 return View(const_cast<double *>(data.data()) + blockSize * mask);
122 }
123 /** Cursor at block 0; ++ advances by one block. */
124 View begin() const { return (*this)[0]; }
125};
126
127/** result = a b over the jet algebra; result must be distinct from a and b,
128 * which may alias each other. */
129template <int N>
130inline void product(const Matrix<N> &a, const Matrix<N> &b, Matrix<N> &result) {
131 FTENSOR_INDEXES(3, i, j, k);
132 auto t_r = result.begin();
133 for (int mask = 0; mask != Matrix<N>::count; ++mask, ++t_r) {
134 // Accumulate in a local block so the compiler need not assume that the
135 // result aliases a or b. Subsets run from the mask itself down to the
136 // empty set.
137 Tensor block;
138 block(i, j) = a[mask](i, k) * b[0](k, j);
139 for (int s = (mask - 1) & mask; mask != 0; s = (s - 1) & mask) {
140 block(i, j) += a[s](i, k) * b[mask ^ s](k, j);
141 if (!s)
142 break;
143 }
144 t_r(i, j) = block(i, j);
145 }
146}
147
148template <int N> Matrix<N> product(const Matrix<N> &a, const Matrix<N> &b) {
149 Matrix<N> result;
150 product(a, b, result);
151 return result;
152}
153
154template <class V> Tensor skew(const V &v) {
155 FTENSOR_INDEXES(3, i, j, k);
157 Tensor result;
158 result(i, j) = epsilon(i, j, k) * v(k);
159 return result;
160}
161
162/** Axial vector of a skew block, skew(v)(i,j) = epsilon(i,j,k) v(k). */
163inline Vector axial(View block) {
164 FTENSOR_INDEXES(3, i, j, k);
166 Vector result;
167 result(k) = 0.5 * (epsilon(i, j, k) * block(i, j));
168 return result;
169}
170
171namespace detail {
172constexpr int factorialTableSize = 32;
173constexpr std::array<double, factorialTableSize> inverseFactorials() {
174 std::array<double, factorialTableSize> table{};
175 double f = 1.;
176 for (int n = 0; n != factorialTableSize; ++n) {
177 table[n] = 1. / f;
178 f *= n + 1;
179 }
180 return table;
181}
182inline constexpr auto inverseFactorial = inverseFactorials();
183
184/** Spread below which divided differences of exp use the series. */
185constexpr double closeSpread = 0.1;
186
187/** Divided difference exp[p_0, ..., p_{K-1}] of close points as the series
188 * e^mean sum_n h_n(shifts) / (n + K - 1)! in the complete homogeneous
189 * symmetric polynomials h_n of the shifts from the mean; with spread 0.1 the
190 * term n is below 1e-17 for n >= 11. Exact for coincident points. */
191template <std::size_t K>
192double expDividedDifferenceSeries(const std::array<double, K> &points) {
193 double mean = 0.;
194 for (auto p : points)
195 mean += p;
196 mean /= double(K);
197 constexpr int terms = 12;
198 std::array<double, terms> h{};
199 h[0] = 1.;
200 for (auto p : points) {
201 const double d = p - mean;
202 for (int n = 1; n != terms; ++n)
203 h[n] += d * h[n - 1];
204 }
205 double sum = 0.;
206 for (int n = terms - 1; n >= 0; --n)
207 sum += h[n] * inverseFactorial[n + K - 1];
208 return std::exp(mean) * sum;
209}
210} // namespace detail
211
212/** Exponential of a symmetric matrix with its directional derivatives up to
213 * third order from the eigen-decomposition. The value is
214 * EigenMatrix::getMat (MatrixFunction.hpp). The derivatives use the
215 * divided-difference expansion in the eigenbasis (components X_ab of a
216 * direction, exp[...] divided differences of exp at the eigenvalues):
217 * (D exp[E])_ab = exp[l_a,l_b] E_ab,
218 * (D^2 exp[E,F])_ab = sum_c exp[l_a,l_c,l_b] (E_ac F_cb + F_ac E_cb),
219 * (D^3 exp[E,F,G])_ab = sum_{c,d} exp[l_a,l_c,l_d,l_b]
220 * sum_{perm (X,Y,Z) of (E,F,G)} X_ac Y_cd Z_db.
221 * The divided differences are evaluated as a short series when eigenvalues
222 * are close, so coincident and nearly coincident eigenvalues are exact; the
223 * confluent formulas of EigenMatrix::getDiffMat and getDiffDiffMat apply only
224 * to eigenvalues equal to 1e-14 and lose about eps/gap^2 in between. The
225 * decomposition and the difference tables are formed once per base and
226 * reused for every direction set. */
228 template <class A> explicit SymmetricExponential(const A &a) {
229 FTENSOR_INDEXES(3, i, j);
230 vectors(i, j) = a(i, j);
232 "Cannot diagonalise symmetric exponential argument");
235 }
236
237 /** exp(-A) from the same eigenvectors, without a second diagonalisation. */
239 SymmetricExponential result(*this);
240 FTENSOR_INDEXES(3, i);
241 result.values(i) = -values(i);
242 result.value = EigenMatrix::getMat(result.values, result.vectors, expFun());
243 result.prepareDifferences();
244 return result;
245 }
246
247 /** D exp(A)[e] */
248 Symmetric first(const Symmetric &e) const {
250 }
251
252 /** D^2 exp(A)[e1, e2] */
253 Symmetric second(const Symmetric &e1, const Symmetric &e2) const {
255 }
256
257 /** D^3 exp(A)[e1, e2, e3] */
258 Symmetric third(const Symmetric &e1, const Symmetric &e2,
259 const Symmetric &e3) const {
260 return fromEigenbasis(
262 }
263
264 /** Symmetric part of a jet block. */
265 static Symmetric symmetric(View block) {
266 FTENSOR_INDEXES(3, i, j);
267 Symmetric result;
268 result(i, j) = (block(i, j) || block(j, i)) / 2.;
269 return result;
270 }
271
272 /** Fill the jet of exp at this base: block 0 the value, single-bit blocks
273 * the first derivatives in the (symmetric) direction blocks of `argument`,
274 * two-bit blocks the second and the three-bit block the third derivative.
275 * A zero direction block gives zero derivatives. */
276 template <int N>
277 void fill(const Matrix<N> &argument, Matrix<N> &result) const {
278 FTENSOR_INDEXES(3, i, j);
279 result.data.fill(0.);
280 result[0](i, j) = value(i, j);
281 std::array<Tensor, N> e;
282 std::array<bool, N> active{};
283 for (int d = 0; d != N; ++d) {
284 const auto block = symmetric(argument[1 << d]);
285 active[d] = block(i, j) * block(i, j) > 0.;
286 if (active[d]) {
287 e[d] = toEigenbasis(block);
288 result[1 << d](i, j) = fromEigenbasis(firstInBasis(e[d]))(i, j);
289 }
290 }
291 for (int d1 = 0; d1 != N; ++d1)
292 for (int d2 = d1 + 1; d2 != N; ++d2)
293 if (active[d1] && active[d2])
294 result[(1 << d1) | (1 << d2)](i, j) =
295 fromEigenbasis(secondInBasis(e[d1], e[d2]))(i, j);
296 if constexpr (N == 3)
297 if (active[0] && active[1] && active[2])
298 result[7](i, j) = fromEigenbasis(thirdInBasis(e[0], e[1], e[2]))(i, j);
299 }
300
302 /** Eigenvectors in rows (MoFEM convention). */
305
306private:
308 static const EigenMatrix::Fun<double> f = [](double x) {
309 return std::exp(x);
310 };
311 return f;
312 }
313
314 Tensor toEigenbasis(const Symmetric &e) const {
315 FTENSOR_INDEXES(3, i, j);
316 FTensor::Index<'a', 3> a;
317 FTensor::Index<'b', 3> b;
318 Tensor c;
319 c(a, b) = vectors(a, i) * (e(i, j) * vectors(b, j));
320 return c;
321 }
322
324 FTENSOR_INDEXES(3, i, j);
325 FTensor::Index<'a', 3> a;
326 FTensor::Index<'b', 3> b;
327 Tensor full;
328 full(i, j) = vectors(a, i) * (t(a, b) * vectors(b, j));
329 Symmetric result;
330 result(i, j) = (full(i, j) || full(j, i)) / 2.;
331 return result;
332 }
333
334 Tensor firstInBasis(const Tensor &x) const {
335 Tensor t;
336 for (int a = 0; a != 3; ++a)
337 for (int b = 0; b != 3; ++b)
338 t(a, b) = second_[a][b] * x(a, b);
339 return t;
340 }
341
342 Tensor secondInBasis(const Tensor &x, const Tensor &y) const {
343 Tensor t;
344 for (int a = 0; a != 3; ++a)
345 for (int b = 0; b != 3; ++b) {
346 double sum = 0.;
347 for (int c = 0; c != 3; ++c)
348 sum += third_[a][c][b] * (x(a, c) * y(c, b) + y(a, c) * x(c, b));
349 t(a, b) = sum;
350 }
351 return t;
352 }
353
354 Tensor thirdInBasis(const Tensor &x, const Tensor &y, const Tensor &z) const {
355 Tensor t;
356 for (int a = 0; a != 3; ++a)
357 for (int b = 0; b != 3; ++b) {
358 double sum = 0.;
359 for (int c = 0; c != 3; ++c)
360 for (int d = 0; d != 3; ++d)
361 sum += fourth_[a][c][d][b] *
362 (x(a, c) * y(c, d) * z(d, b) + x(a, c) * z(c, d) * y(d, b) +
363 y(a, c) * x(c, d) * z(d, b) + y(a, c) * z(c, d) * x(d, b) +
364 z(a, c) * x(c, d) * y(d, b) + z(a, c) * y(c, d) * x(d, b));
365 t(a, b) = sum;
366 }
367 return t;
368 }
369
370 /** Divided differences of exp at the eigenvalues for every index tuple.
371 * The eigenvalues are ascending (LAPACK dsyev), so for a sorted tuple the
372 * extreme points are its first and last entry and the recursion
373 * exp[p_0..p_k] = (exp[p_1..p_k] - exp[p_0..p_{k-1}]) / (p_k - p_0) divides
374 * by the largest spread; tuples with a small spread use the series. */
376#ifndef NDEBUG
377 if (values(0) > values(1) || values(1) > values(2))
378 THROW_MESSAGE("Eigenvalues are expected in ascending order");
379#endif
380 const auto l = [&](int a) { return values(a); };
381 const auto close = [](double spread) {
382 return spread <= detail::closeSpread;
383 };
384 const double e[3] = {std::exp(l(0)), std::exp(l(1)), std::exp(l(2))};
385 for (int a = 0; a != 3; ++a)
386 for (int b = a; b != 3; ++b)
387 second_[a][b] = second_[b][a] =
389 std::array<double, 2>{l(a), l(b)})
390 : (e[b] - e[a]) / (l(b) - l(a));
391 for (int a = 0; a != 3; ++a)
392 for (int b = a; b != 3; ++b)
393 for (int c = b; c != 3; ++c)
394 third_[a][b][c] =
395 close(l(c) - l(a))
397 std::array<double, 3>{l(a), l(b), l(c)})
398 : (second_[b][c] - second_[a][b]) / (l(c) - l(a));
399 for (int a = 0; a != 3; ++a)
400 for (int b = 0; b != 3; ++b)
401 for (int c = 0; c != 3; ++c) {
402 std::array<int, 3> s{a, b, c};
403 std::sort(s.begin(), s.end());
404 third_[a][b][c] = third_[s[0]][s[1]][s[2]];
405 }
406 for (int a = 0; a != 3; ++a)
407 for (int b = a; b != 3; ++b)
408 for (int c = b; c != 3; ++c)
409 for (int d = c; d != 3; ++d)
410 fourth_[a][b][c][d] =
411 close(l(d) - l(a))
413 std::array<double, 4>{l(a), l(b), l(c), l(d)})
414 : (third_[b][c][d] - third_[a][b][c]) / (l(d) - l(a));
415 for (int a = 0; a != 3; ++a)
416 for (int b = 0; b != 3; ++b)
417 for (int c = 0; c != 3; ++c)
418 for (int d = 0; d != 3; ++d) {
419 std::array<int, 4> s{a, b, c, d};
420 std::sort(s.begin(), s.end());
421 fourth_[a][b][c][d] = fourth_[s[0]][s[1]][s[2]][s[3]];
422 }
423 }
424
425 /** exp[l_a,l_b], exp[l_a,l_b,l_c], exp[l_a,l_b,l_c,l_d]. */
426 double second_[3][3];
427 double third_[3][3][3];
428 double fourth_[3][3][3][3];
429};
430
431/** Rotation exp(skew(w)) and its directional derivatives up to third order
432 * from LieGroups::SO3 (Lie.hpp): exp, diffExp, diffDiffExp and
433 * diffDiffDiffExp contracted with the axial direction vectors. The value and
434 * the first two derivative tensors are formed once per base and reused for
435 * every direction set. */
437 explicit RotationExponential(const Vector &w) : omega(w) {
438 FTENSOR_INDEXES(3, i, j, k, l);
439 const double theta = std::sqrt(omega(i) * omega(i));
440 R(i, j) = LieGroups::SO3::exp(omega, theta)(i, j);
441 dR(i, j, k) = LieGroups::SO3::diffExp(omega, theta)(i, j, k);
442 ddR(i, j, k, l) = LieGroups::SO3::diffDiffExp(omega, theta)(i, j, k, l);
443 }
444
445 Tensor value() const { return R; }
446
447 Tensor first(const Vector &a) const {
448 FTENSOR_INDEXES(3, i, j, k);
449 Tensor r;
450 r(i, j) = dR(i, j, k) * a(k);
451 return r;
452 }
453
454 Tensor second(const Vector &a, const Vector &b) const {
455 FTENSOR_INDEXES(3, i, j, k, l);
456 Tensor r;
457 r(i, j) = ddR(i, j, k, l) * a(k) * b(l);
458 return r;
459 }
460
461 Tensor third(const Vector &a, const Vector &b, const Vector &c) const {
462 FTENSOR_INDEXES(3, i, j, k, l);
463 const double theta = std::sqrt(omega(i) * omega(i));
464 const auto dddR = LieGroups::SO3::diffDiffDiffExp(omega, theta, c);
465 Tensor r;
466 r(i, j) = dddR(i, j, k, l) * a(k) * b(l);
467 return r;
468 }
469
470 /** Fill the jet of exp(skew(w)): block 0 the value and the other blocks
471 * the derivatives in the axial vectors of the skew direction blocks of
472 * `argument`. A zero direction block gives zero derivatives. */
473 template <int N>
474 void fill(const Matrix<N> &argument, Matrix<N> &result) const {
475 FTENSOR_INDEXES(3, i, j);
476 result.data.fill(0.);
477 result[0](i, j) = R(i, j);
478 std::array<Vector, N> v;
479 std::array<bool, N> active{};
480 for (int d = 0; d != N; ++d) {
481 v[d] = axial(argument[1 << d]);
482 active[d] = v[d](i) * v[d](i) > 0.;
483 if (active[d])
484 result[1 << d](i, j) = first(v[d])(i, j);
485 }
486 for (int d1 = 0; d1 != N; ++d1)
487 for (int d2 = d1 + 1; d2 != N; ++d2)
488 if (active[d1] && active[d2])
489 result[(1 << d1) | (1 << d2)](i, j) = second(v[d1], v[d2])(i, j);
490 if constexpr (N == 3)
491 if (active[0] && active[1] && active[2])
492 result[7](i, j) = third(v[0], v[1], v[2])(i, j);
493 }
494
499};
500
501/** Product interpolation exp(skew(a)) exp(skew(q)).
502 * Used by L2 rotation kinematics and reduced curvature. Constant and higher
503 * directions stay distinct; the jet product supplies every mixed derivative.
504 */
506 SplitRotation(const Vector &a, const Vector &q) : constant(a), higher(q) {}
507
508 Tensor value() const {
509 FTENSOR_INDEXES(3, i, j, k);
510 Tensor result;
511 result(i, j) = constant.R(i, k) * higher.R(k, j);
512 return result;
513 }
514
515 Tensor first(const Vector &da, const Vector &dq) const {
516 FTENSOR_INDEXES(3, i, j, k);
517 const auto dA = constant.first(da), dQ = higher.first(dq);
518 Tensor result;
519 result(i, j) = dA(i, k) * higher.R(k, j) + constant.R(i, k) * dQ(k, j);
520 return result;
521 }
522
523 /** Coordinate derivative tensor for one of the two three-vector factors. */
524 auto firstTensor(const int mode) const {
525 FTENSOR_INDEXES(3, i, j, k, m);
527 if (!mode)
528 result(i, j, k) = constant.dR(i, m, k) * higher.R(m, j);
529 else
530 result(i, j, k) = constant.R(i, m) * higher.dR(m, j, k);
531 return result;
532 }
533
534 /** Constant/constant, constant/higher, higher/constant or higher/higher. */
535 auto secondTensor(const int row_mode, const int col_mode) const {
536 FTENSOR_INDEXES(3, i, j, k, l, m);
538 if (!row_mode && !col_mode)
539 result(i, j, k, l) = constant.ddR(i, m, k, l) * higher.R(m, j);
540 else if (!row_mode)
541 result(i, j, k, l) = constant.dR(i, m, k) * higher.dR(m, j, l);
542 else if (!col_mode)
543 result(i, j, k, l) = constant.dR(i, m, l) * higher.dR(m, j, k);
544 else
545 result(i, j, k, l) = constant.R(i, m) * higher.ddR(m, j, k, l);
546 return result;
547 }
548
549 template <int N>
550 void fill(const Matrix<N> &a, const Matrix<N> &q, Matrix<N> &result) const {
551 Matrix<N> A, Q;
552 constant.fill(a, A);
553 higher.fill(q, Q);
554 result = product(A, Q);
555 }
556
558};
559
560/** Deviatoric curvature of f = C10 tr exp(2 dev H) as symmetric tensors to be
561 * contracted with a stretch direction e: second(i,j) e(i,j) = D^2 f[s, e] and
562 * third(i,j) e(i,j) = D^3 f[p, s, e]. */
566
568 const Symmetric &p,
569 const Symmetric &s) {
570 FTENSOR_INDEXES(3, i, j, k, l);
571 constexpr auto t_diff_deviator = FTensor::diff_deviator<int, 3>();
572 const auto deviator = [&](const Symmetric &t) {
573 Symmetric result;
574 result(i, j) = 2. * (t_diff_deviator(i, j, k, l) * t(k, l));
575 return result;
576 };
577 const auto a = deviator(h), dp = deviator(p), ds = deviator(s);
578 const SymmetricExponential exponential(a);
579 // D^k tr exp(A)[..., E] = D^(k-1) exp(A)[...] : E with E = 2 dev e, so the
580 // tensors to contract with e are twice the deviators of the derivatives.
581 DeviatorCurvature result;
582 result.second(i, j) = c10 * deviator(exponential.first(ds))(i, j);
583 result.third(i, j) = c10 * deviator(exponential.second(dp, ds))(i, j);
584 return result;
585}
586} // namespace LogarithmicDirectionalDerivatives
587} // namespace EshelbianPlasticity
588
589#endif // ESHELBIAN_LOGARITHMIC_DIRECTIONAL_DERIVATIVES_HPP
bool active
Lie algebra implementation.
#define FTENSOR_INDEXES(DIM,...)
constexpr double a
Levi_Civita Classes.
#define CHK_THROW_MESSAGE(err, msg)
Check and throw MoFEM exception.
#define THROW_MESSAGE(msg)
Throw MoFEM exception.
FTensor::Index< 'i', SPACE_DIM > i
const double c
speed of light (cm/ns)
const double v
phase velocity of light in medium (cm/ns)
const double n
refractive index of diffusive medium
FTensor::Index< 'l', 3 > l
FTensor::Index< 'j', 3 > j
FTensor::Index< 'k', 3 > k
boost::function< T(const T)> Fun
auto getMat(A &&t_val, B &&t_vec, Fun< double > f)
Get the Mat object.
DeviatorCurvature deviatorCurvature(double c10, const Symmetric &h, const Symmetric &p, const Symmetric &s)
FTensor::Tensor2< FTensor::CursorPtr< double *, 9 >, 3, 3 > View
void product(const Matrix< N > &a, const Matrix< N > &b, Matrix< N > &result)
MoFEMErrorCode computeEigenValuesSymmetric(const MatrixDouble &mat, VectorDouble &eig, MatrixDouble &eigen_vec)
compute eigenvalues of a symmetric matrix using lapack dsyev
constexpr AssemblyType A
double h
constexpr double t
plate stiffness
Definition plate.cpp:58
double q
FTensor::Index< 'm', 3 > m
const int N
Definition speed_test.cpp:3
void fill(const Matrix< N > &a, const Matrix< N > &q, Matrix< N > &result) const
Symmetric third(const Symmetric &e1, const Symmetric &e2, const Symmetric &e3) const
static auto diffDiffDiffExp(A &&t_w_vee, B &&theta, C &&t_delta_w_vee)
Definition Lie.hpp:117
static auto diffDiffExp(A &&t_w_vee, B &&theta)
Definition Lie.hpp:110
static auto diffExp(A &&t_w_vee, B &&theta)
Definition Lie.hpp:105
static auto exp(A &&t_w_vee, B &&theta)
Definition Lie.hpp:69