https://mooseframework.inl.gov
Loading...
Searching...
No Matches
RankFourTensorImplementation.h
Go to the documentation of this file.
1//* This file is part of the MOOSE framework
2//* https://mooseframework.inl.gov
3//*
4//* All rights reserved, see COPYRIGHT for full restrictions
5//* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6//*
7//* Licensed under LGPL 2.1, please see LICENSE for details
8//* https://www.gnu.org/licenses/lgpl-2.1.html
9
10#pragma once
11
12#include "RankFourTensor.h"
13
14// MOOSE includes
15#include "RankTwoTensor.h"
16#include "RankThreeTensor.h"
17#include "MooseEnum.h"
18#include "MooseException.h"
19#include "MooseUtils.h"
20#include "MatrixTools.h"
21#include "PermutationTensor.h"
22
23#include "libmesh/utility.h"
24#include "libmesh/tensor_value.h"
25#include "libmesh/vector_value.h"
26
27// Eigen needs LU
28#include "Eigen/LU"
29
30// C++ includes
31#include <iomanip>
32#include <ostream>
33
34namespace MathUtils
35{
36template <>
37void mooseSetToZero<RankFourTensorTempl<Real>>(RankFourTensorTempl<Real> & v);
38template <>
39void mooseSetToZero<RankFourTensorTempl<ADReal>>(RankFourTensorTempl<ADReal> & v);
40}
41
42template <typename T>
45{
46 return MooseEnum("antisymmetric symmetric9 symmetric21 general_isotropic symmetric_isotropic "
47 "symmetric_isotropic_E_nu antisymmetric_isotropic axisymmetric_rz general "
48 "principal orthotropic");
49}
50
51template <typename T>
53{
54 mooseAssert(N == 3, "RankFourTensorTempl<T> is currently only tested for 3 dimensions.");
55
56 for (auto i : make_range(N4))
57 _vals[i] = 0.0;
58}
59
60template <typename T>
62{
63 unsigned int index = 0;
64 switch (init)
65 {
66 case initNone:
67 break;
68
69 case initIdentity:
70 zero();
71 for (auto i : make_range(N))
72 (*this)(i, i, i, i) = 1.0;
73 break;
74
75 case initIdentityFour:
76 for (auto i : make_range(N))
77 for (auto j : make_range(N))
78 for (auto k : make_range(N))
79 for (auto l : make_range(N))
80 _vals[index++] = Real(i == k && j == l);
81 break;
82
83 case initIdentitySymmetricFour:
84 for (auto i : make_range(N))
85 for (auto j : make_range(N))
86 for (auto k : make_range(N))
87 for (auto l : make_range(N))
88 _vals[index++] = 0.5 * Real(i == k && j == l) + 0.5 * Real(i == l && j == k);
89 break;
90
91 case initIdentityDeviatoric:
92 for (unsigned int i = 0; i < N; ++i)
93 for (unsigned int j = 0; j < N; ++j)
94 for (unsigned int k = 0; k < N; ++k)
95 for (unsigned int l = 0; l < N; ++l)
96 {
97 _vals[index] = Real(i == k && j == l);
98 if ((i == j) && (k == l))
99 _vals[index] -= 1.0 / 3.0;
100 index++;
101 }
102 break;
103
104 default:
105 mooseError("Unknown RankFourTensorTempl<T> initialization pattern.");
106 }
107}
108
109template <typename T>
110RankFourTensorTempl<T>::RankFourTensorTempl(const std::vector<T> & input, FillMethod fill_method)
111{
112 fillFromInputVector(input, fill_method);
113}
114
115template <typename T>
116void
118{
119 for (auto i : make_range(N4))
120 _vals[i] = 0.0;
121}
122
123template <typename T>
124template <template <typename> class Tensor, typename T2>
125auto
126RankFourTensorTempl<T>::operator*(const Tensor<T2> & b) const ->
127 typename std::enable_if<TwoTensorMultTraits<Tensor, T2>::value,
128 RankTwoTensorTempl<decltype(T() * T2())>>::type
129{
130 typedef decltype(T() * T2()) ValueType;
132
133 if constexpr (std::is_same_v<T, Real> && std::is_same_v<T2, Real>)
134 {
135 // result_ij = A_ijkl b_kl: an (N2 x N2) . (N2) matvec over the flat storage. Gather b into a
136 // contiguous vector once (b's storage type is generic here), then use Eigen's kernel -- the
137 // scalar loop below re-derives b's index with a div/mod on every inner iteration.
138 Eigen::Matrix<Real, N2, 1> bvec;
139 for (unsigned int kl = 0; kl < N2; ++kl)
140 bvec(kl) = b(kl / N, kl % N);
141 const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> a(_vals);
142 Eigen::Map<Eigen::Matrix<Real, N2, 1>> r(result._coords);
143 r.noalias() = a * bvec;
144 return result;
145 }
146 else
147 {
148 unsigned int index = 0;
149 for (unsigned int ij = 0; ij < N2; ++ij)
150 {
151 ValueType tmp = 0;
152 for (unsigned int kl = 0; kl < N2; ++kl)
153 tmp += _vals[index++] * b(kl / LIBMESH_DIM, kl % LIBMESH_DIM);
154 result._coords[ij] = tmp;
155 }
156
157 return result;
158 }
159}
160
161template <typename T>
164{
165 for (auto i : make_range(N4))
166 _vals[i] *= a;
167 return *this;
168}
169
170template <typename T>
173{
174 for (auto i : make_range(N4))
175 _vals[i] /= a;
176 return *this;
177}
178
179template <typename T>
182{
183 for (auto i : make_range(N4))
184 _vals[i] += a._vals[i];
185 return *this;
186}
187
188template <typename T>
189template <typename T2>
190auto
192 -> RankFourTensorTempl<decltype(T() + T2())>
193{
194 RankFourTensorTempl<decltype(T() + T2())> result;
195 for (auto i : make_range(N4))
196 result._vals[i] = _vals[i] + b._vals[i];
197 return result;
198}
199
200template <typename T>
203{
204 for (auto i : make_range(N4))
205 _vals[i] -= a._vals[i];
206 return *this;
207}
208
209template <typename T>
210template <typename T2>
211auto
213 -> RankFourTensorTempl<decltype(T() - T2())>
214{
215 RankFourTensorTempl<decltype(T() - T2())> result;
216 for (auto i : make_range(N4))
217 result._vals[i] = _vals[i] - b._vals[i];
218 return result;
219}
220
221template <typename T>
224{
226 for (auto i : make_range(N4))
227 result._vals[i] = -_vals[i];
228 return result;
229}
230
231template <typename T>
232template <typename T2>
233auto
235 -> RankFourTensorTempl<decltype(T() * T2())>
236{
237 typedef decltype(T() * T2()) ValueType;
239
240 if constexpr (std::is_same_v<T, Real> && std::is_same_v<T2, Real>)
241 {
242 // C_ijkl = A_ijpq B_pqkl. With the row-major flat storage (linear index ij*N2 + kl, where
243 // ij = i*N + j and kl = k*N + l) this is exactly an (N2 x N2) matrix product over the
244 // flattened tensors. Dispatch to Eigen's vectorized kernel instead of the six-deep loop,
245 // which recomputes the flat index via operator() ~3x per innermost iteration.
246 const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> a(_vals);
247 const Eigen::Map<const Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> bmat(b._vals);
248 Eigen::Map<Eigen::Matrix<Real, N2, N2, Eigen::RowMajor>> r(result._vals);
249 r.noalias() = a * bmat;
250 return result;
251 }
252 else
253 {
254 for (auto i : make_range(N))
255 for (auto j : make_range(N))
256 for (auto k : make_range(N))
257 for (auto l : make_range(N))
258 for (auto p : make_range(N))
259 for (auto q : make_range(N))
260 result(i, j, k, l) += (*this)(i, j, p, q) * b(p, q, k, l);
261
262 return result;
263 }
264}
265
266template <typename T>
267T
269{
270 T l2 = 0;
271
272 for (auto i : make_range(N4))
273 l2 += Utility::pow<2>(_vals[i]);
274
275 using std::sqrt;
276 return sqrt(l2);
277}
278
279template <typename T>
282{
283 mooseError("The invSymm operation calls to LAPACK and only supports plain Real type tensors.");
284}
285
286template <>
289{
290 unsigned int ntens = N * (N + 1) / 2;
291 int nskip = N - 1;
292
294 std::vector<PetscScalar> mat;
295 mat.assign(ntens * ntens, 0);
296
297 // We use the LAPACK matrix inversion routine here. Form the matrix
298 //
299 // mat[0] mat[1] mat[2] mat[3] mat[4] mat[5]
300 // mat[6] mat[7] mat[8] mat[9] mat[10] mat[11]
301 // mat[12] mat[13] mat[14] mat[15] mat[16] mat[17]
302 // mat[18] mat[19] mat[20] mat[21] mat[22] mat[23]
303 // mat[24] mat[25] mat[26] mat[27] mat[28] mat[29]
304 // mat[30] mat[31] mat[32] mat[33] mat[34] mat[35]
305 //
306 // This is filled from the indpendent components of C assuming
307 // the symmetry C_ijkl = C_ijlk = C_jikl.
308 //
309 // If there are two rank-four tensors X and Y then the reason for
310 // this filling becomes apparent if we want to calculate
311 // X_ijkl*Y_klmn = Z_ijmn
312 // For denote the "mat" versions of X, Y and Z by x, y and z.
313 // Then
314 // z_ab = x_ac*y_cb
315 // Eg
316 // z_00 = Z_0000 = X_0000*Y_0000 + X_0011*Y_1111 + X_0022*Y_2200 + 2*X_0001*Y_0100 +
317 // 2*X_0002*Y_0200 + 2*X_0012*Y_1200 (the factors of 2 come from the assumed symmetries)
318 // z_03 = 2*Z_0001 = X_0000*2*Y_0001 + X_0011*2*Y_1101 + X_0022*2*Y_2201 + 2*X_0001*2*Y_0101 +
319 // 2*X_0002*2*Y_0201 + 2*X_0012*2*Y_1201
320 // z_22 = 2*Z_0102 = X_0100*2*Y_0002 + X_0111*2*X_1102 + X_0122*2*Y_2202 + 2*X_0101*2*Y_0102 +
321 // 2*X_0102*2*Y_0202 + 2*X_0112*2*Y_1202
322 // Finally, we use LAPACK to find x^-1, and put it back into rank-4 tensor form
323 //
324 // mat[0] = C(0,0,0,0)
325 // mat[1] = C(0,0,1,1)
326 // mat[2] = C(0,0,2,2)
327 // mat[3] = C(0,0,0,1)*2
328 // mat[4] = C(0,0,0,2)*2
329 // mat[5] = C(0,0,1,2)*2
330
331 // mat[6] = C(1,1,0,0)
332 // mat[7] = C(1,1,1,1)
333 // mat[8] = C(1,1,2,2)
334 // mat[9] = C(1,1,0,1)*2
335 // mat[10] = C(1,1,0,2)*2
336 // mat[11] = C(1,1,1,2)*2
337
338 // mat[12] = C(2,2,0,0)
339 // mat[13] = C(2,2,1,1)
340 // mat[14] = C(2,2,2,2)
341 // mat[15] = C(2,2,0,1)*2
342 // mat[16] = C(2,2,0,2)*2
343 // mat[17] = C(2,2,1,2)*2
344
345 // mat[18] = C(0,1,0,0)
346 // mat[19] = C(0,1,1,1)
347 // mat[20] = C(0,1,2,2)
348 // mat[21] = C(0,1,0,1)*2
349 // mat[22] = C(0,1,0,2)*2
350 // mat[23] = C(0,1,1,2)*2
351
352 // mat[24] = C(0,2,0,0)
353 // mat[25] = C(0,2,1,1)
354 // mat[26] = C(0,2,2,2)
355 // mat[27] = C(0,2,0,1)*2
356 // mat[28] = C(0,2,0,2)*2
357 // mat[29] = C(0,2,1,2)*2
358
359 // mat[30] = C(1,2,0,0)
360 // mat[31] = C(1,2,1,1)
361 // mat[32] = C(1,2,2,2)
362 // mat[33] = C(1,2,0,1)*2
363 // mat[34] = C(1,2,0,2)*2
364 // mat[35] = C(1,2,1,2)*2
365
366 unsigned int index = 0;
367 for (auto i : make_range(N))
368 for (auto j : make_range(N))
369 for (auto k : make_range(N))
370 for (auto l : make_range(N))
371 {
372 if (i == j)
373 mat[k == l ? i * ntens + k : i * ntens + k + nskip + l] += _vals[index];
374 else
375 // i!=j
376 mat[k == l ? (nskip + i + j) * ntens + k : (nskip + i + j) * ntens + k + nskip + l] +=
377 _vals[index]; // note the +=, which results in double-counting and is rectified
378 // below
379 index++;
380 }
381
382 for (unsigned int i = 3; i < ntens; ++i)
383 for (auto j : make_range(ntens))
384 mat[i * ntens + j] /= 2.0; // because of double-counting above
385
386 // use LAPACK to find the inverse
387 MatrixTools::inverse(mat, ntens);
388
389 // build the resulting rank-four tensor
390 // using the inverse of the above algorithm
391 index = 0;
392 for (auto i : make_range(N))
393 for (auto j : make_range(N))
394 for (auto k : make_range(N))
395 for (auto l : make_range(N))
396 {
397 if (i == j)
398 result._vals[index] =
399 k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
400 else
401 // i!=j
402 result._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
403 : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
404 index++;
405 }
406
407 return result;
408}
409
410template <typename T>
411void
412RankFourTensorTempl<T>::rotate(const TypeTensor<T> & R)
413{
414 RankFourTensorTempl<T> old = *this;
415
416 unsigned int index = 0;
417 for (auto i : make_range(N))
418 for (auto j : make_range(N))
419 for (auto k : make_range(N))
420 for (auto l : make_range(N))
421 {
422 unsigned int index2 = 0;
423 T sum = 0.0;
424 for (auto m : make_range(N))
425 {
426 const T & a = R(i, m);
427 for (auto n : make_range(N))
428 {
429 const T & ab = a * R(j, n);
430 for (auto o : make_range(N))
431 {
432 const T & abc = ab * R(k, o);
433 for (auto p : make_range(N))
434 sum += abc * R(l, p) * old._vals[index2++];
435 }
436 }
437 }
438 _vals[index++] = sum;
439 }
440}
441
442template <typename T>
443void
444RankFourTensorTempl<T>::print(std::ostream & stm) const
445{
446 for (auto i : make_range(N))
447 for (auto j : make_range(N))
448 {
449 stm << "i = " << i << " j = " << j << '\n';
450 for (auto k : make_range(N))
451 {
452 for (auto l : make_range(N))
453 stm << std::setw(15) << (*this)(i, j, k, l) << " ";
454
455 stm << '\n';
456 }
457 }
458
459 stm << std::flush;
460}
461
462template <typename T>
463void
464RankFourTensorTempl<T>::printReal(std::ostream & stm) const
465{
466 for (unsigned int i = 0; i < N; ++i)
467 for (unsigned int j = 0; j < N; ++j)
468 {
469 stm << "i = " << i << " j = " << j << '\n';
470 for (unsigned int k = 0; k < N; ++k)
471 {
472 for (unsigned int l = 0; l < N; ++l)
473 stm << std::setw(15) << MetaPhysicL::raw_value((*this)(i, j, k, l)) << " ";
474
475 stm << '\n';
476 }
477 }
478
479 stm << std::flush;
480}
481
482template <typename T>
485{
487
488 unsigned int index = 0;
489 for (auto i : make_range(N))
490 for (auto j : make_range(N))
491 for (auto k : make_range(N))
492 for (auto l : make_range(N))
493 result._vals[index++] = _vals[k * N3 + i * N + j + l * N2];
494
495 return result;
496}
497
498template <typename T>
501{
503
504 for (auto i : make_range(N))
505 for (auto j : make_range(N))
506 for (auto k : make_range(N))
507 for (auto l : make_range(N))
508 result(i, j, k, l) = (*this)(j, i, k, l);
509
510 return result;
511}
512
513template <typename T>
516{
518
519 for (auto i : make_range(N))
520 for (auto j : make_range(N))
521 for (auto k : make_range(N))
522 for (auto l : make_range(N))
523 result(i, j, k, l) = (*this)(i, j, l, k);
524
525 return result;
526}
527
528template <typename T>
529void
531{
532 zero();
533
534 if (input.size() == 9)
535 {
536 // then fill from vector C_1111, C_1112, C_1122, C_1212, C_1222, C_1211, C_2211, C_2212, C_2222
537 (*this)(0, 0, 0, 0) = input[0];
538 (*this)(0, 0, 0, 1) = input[1];
539 (*this)(0, 0, 1, 1) = input[2];
540 (*this)(0, 1, 0, 1) = input[3];
541 (*this)(0, 1, 1, 1) = input[4];
542 (*this)(0, 1, 0, 0) = input[5];
543 (*this)(1, 1, 0, 0) = input[6];
544 (*this)(1, 1, 0, 1) = input[7];
545 (*this)(1, 1, 1, 1) = input[8];
546
547 // fill in remainders from C_ijkl = C_ijlk = C_jikl
548 (*this)(0, 0, 1, 0) = (*this)(0, 0, 0, 1);
549 (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
550 (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
551 (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
552 (*this)(1, 0, 1, 1) = (*this)(0, 1, 1, 1);
553 (*this)(1, 0, 0, 0) = (*this)(0, 1, 0, 0);
554 (*this)(1, 1, 1, 0) = (*this)(1, 1, 0, 1);
555 }
556 else if (input.size() == 2)
557 {
558 // only two independent constants, C_1111 and C_1122
559 (*this)(0, 0, 0, 0) = input[0];
560 (*this)(0, 0, 1, 1) = input[1];
561 // use symmetries
562 (*this)(1, 1, 1, 1) = (*this)(0, 0, 0, 0);
563 (*this)(1, 1, 0, 0) = (*this)(0, 0, 1, 1);
564 (*this)(0, 1, 0, 1) = 0.5 * ((*this)(0, 0, 0, 0) - (*this)(0, 0, 1, 1));
565 (*this)(1, 0, 0, 1) = (*this)(0, 1, 0, 1);
566 (*this)(0, 1, 1, 0) = (*this)(0, 1, 0, 1);
567 (*this)(1, 0, 1, 0) = (*this)(0, 1, 0, 1);
568 }
569 else
570 mooseError("Please provide correct number of inputs for surface RankFourTensorTempl<T> "
571 "initialization.");
572}
573
574template <typename T>
575void
576RankFourTensorTempl<T>::fillFromInputVector(const std::vector<T> & input, FillMethod fill_method)
577{
578
579 switch (fill_method)
580 {
581 case antisymmetric:
582 fillAntisymmetricFromInputVector(input);
583 break;
584 case symmetric9:
585 fillSymmetric9FromInputVector(input);
586 break;
587 case symmetric21:
588 fillSymmetric21FromInputVector(input);
589 break;
590 case general_isotropic:
591 fillGeneralIsotropicFromInputVector(input);
592 break;
593 case symmetric_isotropic:
594 fillSymmetricIsotropicFromInputVector(input);
595 break;
596 case symmetric_isotropic_E_nu:
597 fillSymmetricIsotropicEandNuFromInputVector(input);
598 break;
599 case antisymmetric_isotropic:
600 fillAntisymmetricIsotropicFromInputVector(input);
601 break;
602 case axisymmetric_rz:
603 fillAxisymmetricRZFromInputVector(input);
604 break;
605 case general:
606 fillGeneralFromInputVector(input);
607 break;
608 case principal:
609 fillPrincipalFromInputVector(input);
610 break;
611 case orthotropic:
612 fillGeneralOrthotropicFromInputVector(input);
613 break;
614 default:
615 mooseError("fillFromInputVector called with unknown fill_method of ", fill_method);
616 }
617}
618
619template <typename T>
620void
622{
623 if (input.size() != 6)
625 "To use fillAntisymmetricFromInputVector, your input must have size 6. Yours has size ",
626 input.size());
627
628 zero();
629
630 (*this)(0, 1, 0, 1) = input[0]; // B1212
631 (*this)(0, 1, 0, 2) = input[1]; // B1213
632 (*this)(0, 1, 1, 2) = input[2]; // B1223
633
634 (*this)(0, 2, 0, 2) = input[3]; // B1313
635 (*this)(0, 2, 1, 2) = input[4]; // B1323
636
637 (*this)(1, 2, 1, 2) = input[5]; // B2323
638
639 // symmetry on the two pairs
640 (*this)(0, 2, 0, 1) = (*this)(0, 1, 0, 2);
641 (*this)(1, 2, 0, 1) = (*this)(0, 1, 1, 2);
642 (*this)(1, 2, 0, 2) = (*this)(0, 2, 1, 2);
643 // have now got the upper parts of vals[0][1], vals[0][2] and vals[1][2]
644
645 // fill in from antisymmetry relations
646 for (auto i : make_range(N))
647 for (auto j : make_range(N))
648 {
649 (*this)(0, 1, j, i) = -(*this)(0, 1, i, j);
650 (*this)(0, 2, j, i) = -(*this)(0, 2, i, j);
651 (*this)(1, 2, j, i) = -(*this)(1, 2, i, j);
652 }
653 // have now got all of vals[0][1], vals[0][2] and vals[1][2]
654
655 // fill in from antisymmetry relations
656 for (auto i : make_range(N))
657 for (auto j : make_range(N))
658 {
659 (*this)(1, 0, i, j) = -(*this)(0, 1, i, j);
660 (*this)(2, 0, i, j) = -(*this)(0, 2, i, j);
661 (*this)(2, 1, i, j) = -(*this)(1, 2, i, j);
662 }
663}
664
665template <typename T>
666void
668{
669 if (input.size() != 3)
670 mooseError("To use fillGeneralIsotropicFromInputVector, your input must have size 3. Yours "
671 "has size ",
672 input.size());
673
674 fillGeneralIsotropic(input[0], input[1], input[2]);
675}
676
677template <typename T>
678void
679RankFourTensorTempl<T>::fillGeneralIsotropic(const T & i0, const T & i1, const T & i2)
680{
681 for (auto i : make_range(N))
682 for (auto j : make_range(N))
683 for (auto k : make_range(N))
684 for (auto l : make_range(N))
685 {
686 (*this)(i, j, k, l) = i0 * Real(i == j) * Real(k == l) +
687 i1 * Real(i == k) * Real(j == l) + i1 * Real(i == l) * Real(j == k);
688 for (auto m : make_range(N))
689 (*this)(i, j, k, l) +=
690 i2 * Real(PermutationTensor::eps(i, j, m)) * Real(PermutationTensor::eps(k, l, m));
691 }
692}
693
694template <typename T>
695void
697{
698 if (input.size() != 1)
699 mooseError("To use fillAntisymmetricIsotropicFromInputVector, your input must have size 1. "
700 "Yours has size ",
701 input.size());
702
703 fillGeneralIsotropic(0.0, 0.0, input[0]);
704}
705
706template <typename T>
707void
709{
710 fillGeneralIsotropic(0.0, 0.0, i0);
711}
712
713template <typename T>
714void
716{
717 mooseAssert(input.size() == 2,
718 "To use fillSymmetricIsotropicFromInputVector, your input must have size 2.");
719 fillSymmetricIsotropic(input[0], input[1]);
720}
721
722template <typename T>
723void
725{
726 // clang-format off
727 fillSymmetric21FromInputVector(std::array<T,21>
728 {{lambda + 2.0 * G, lambda, lambda, 0.0, 0.0, 0.0,
729 lambda + 2.0 * G, lambda, 0.0, 0.0, 0.0,
730 lambda + 2.0 * G, 0.0, 0.0, 0.0,
731 G, 0.0, 0.0,
732 G, 0.0,
733 G}});
734 // clang-format on
735}
736
737template <typename T>
738void
740{
741 if (input.size() != 2)
743 "To use fillSymmetricIsotropicEandNuFromInputVector, your input must have size 2. Yours "
744 "has size ",
745 input.size());
746
747 fillSymmetricIsotropicEandNu(input[0], input[1]);
748}
749
750template <typename T>
751void
753{
754 // Calculate lambda and the shear modulus from the given young's modulus and poisson's ratio
755 const T & lambda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu));
756 const T & G = E / (2.0 * (1.0 + nu));
757
758 fillSymmetricIsotropic(lambda, G);
759}
760
761template <typename T>
762void
764{
765 mooseAssert(input.size() == 5,
766 "To use fillAxisymmetricRZFromInputVector, your input must have size 5.");
767
768 // C1111 C1122 C1133 0 0 0
769 // C2222 C2233=C1133 0 0 0
770 // C3333 0 0 0
771 // C2323 0 0
772 // C3131=C2323 0
773 // C1212
774 // clang-format off
775 fillSymmetric21FromInputVector(std::array<T,21>
776 {{input[0],input[1],input[2], 0.0, 0.0, 0.0,
777 input[0],input[2], 0.0, 0.0, 0.0,
778 input[3], 0.0, 0.0, 0.0,
779 input[4], 0.0, 0.0,
780 input[4], 0.0,
781 (input[0] - input[1]) * 0.5}});
782 // clang-format on
783}
784
785template <typename T>
786void
788{
789 if (input.size() != 81)
790 mooseError("To use fillGeneralFromInputVector, your input must have size 81. Yours has size ",
791 input.size());
792
793 for (auto i : make_range(N4))
794 _vals[i] = input[i];
795}
796
797template <typename T>
798void
800{
801 if (input.size() != 9)
802 mooseError("To use fillPrincipalFromInputVector, your input must have size 9. Yours has size ",
803 input.size());
804
805 zero();
806
807 (*this)(0, 0, 0, 0) = input[0];
808 (*this)(0, 0, 1, 1) = input[1];
809 (*this)(0, 0, 2, 2) = input[2];
810 (*this)(1, 1, 0, 0) = input[3];
811 (*this)(1, 1, 1, 1) = input[4];
812 (*this)(1, 1, 2, 2) = input[5];
813 (*this)(2, 2, 0, 0) = input[6];
814 (*this)(2, 2, 1, 1) = input[7];
815 (*this)(2, 2, 2, 2) = input[8];
816}
817
818template <typename T>
819void
821{
822 if (input.size() != 12)
823 mooseError("To use fillGeneralOrhotropicFromInputVector, your input must have size 12. Yours "
824 "has size ",
825 input.size());
826
827 const T & Ea = input[0];
828 const T & Eb = input[1];
829 const T & Ec = input[2];
830 const T & Gab = input[3];
831 const T & Gbc = input[4];
832 const T & Gca = input[5];
833 const T & nuba = input[6];
834 const T & nuca = input[7];
835 const T & nucb = input[8];
836 const T & nuab = input[9];
837 const T & nuac = input[10];
838 const T & nubc = input[11];
839
840 // Input must satisfy constraints.
841 bool preserve_symmetry = MooseUtils::relativeFuzzyEqual(nuab * Eb, nuba * Ea) &&
842 MooseUtils::relativeFuzzyEqual(nuca * Ea, nuac * Ec) &&
843 MooseUtils::relativeFuzzyEqual(nubc * Ec, nucb * Eb);
844
845 if (!preserve_symmetry)
846 mooseError("Orthotropic elasticity tensor input is not consistent with symmetry requirements. "
847 "Check input for accuracy");
848
849 unsigned int ntens = N * (N + 1) / 2;
850
851 std::vector<T> mat;
852 mat.assign(ntens * ntens, 0);
853
854 T k = 1 - nuab * nuba - nubc * nucb - nuca * nuac - nuab * nubc * nuca - nuba * nucb * nuac;
855
856 bool is_positive_definite =
857 (k > 0) && (1 - nubc * nucb) > 0 && (1 - nuac * nuca) > 0 && (1 - nuab * nuba) > 0;
858 if (!is_positive_definite)
859 mooseError("Orthotropic elasticity tensor input is not positive definite. Check input for "
860 "accuracy");
861
862 mat[0] = Ea * (1 - nubc * nucb) / k;
863 mat[1] = Ea * (nubc * nuca + nuba) / k;
864 mat[2] = Ea * (nuba * nucb + nuca) / k;
865
866 mat[6] = Eb * (nuac * nucb + nuab) / k;
867 mat[7] = Eb * (1 - nuac * nuca) / k;
868 mat[8] = Eb * (nuab * nuca + nucb) / k;
869
870 mat[12] = Ec * (nuab * nubc + nuac) / k;
871 mat[13] = Ec * (nuac * nuba + nubc) / k;
872 mat[14] = Ec * (1 - nuab * nuba) / k;
873
874 mat[21] = 2 * Gab;
875 mat[28] = 2 * Gca;
876 mat[35] = 2 * Gbc;
877
878 // Switching from Voigt to fourth order tensor
879 // Copied from existing code (invSymm)
880 int nskip = N - 1;
881
882 unsigned int index = 0;
883 for (auto i : make_range(N))
884 for (auto j : make_range(N))
885 for (auto k : make_range(N))
886 for (auto l : make_range(N))
887 {
888 if (i == j)
889 (*this)._vals[index] =
890 k == l ? mat[i * ntens + k] : mat[i * ntens + k + nskip + l] / 2.0;
891 else
892 (*this)._vals[index] = k == l ? mat[(nskip + i + j) * ntens + k]
893 : mat[(nskip + i + j) * ntens + k + nskip + l] / 2.0;
894 index++;
895 }
896}
897
898template <typename T>
901{
903
904 unsigned int index = 0;
905 for (unsigned int ij = 0; ij < N2; ++ij)
906 {
907 T bb = b._coords[ij];
908 for (unsigned int kl = 0; kl < N2; ++kl)
909 result._coords[kl] += _vals[index++] * bb;
910 }
911
912 return result;
913}
914
915template <typename T>
916T
918 unsigned int j,
919 const RankTwoTensorTempl<T> & M) const
920{
921 T val = 0;
922 for (unsigned int k = 0; k < N; k++)
923 for (unsigned int l = 0; l < N; l++)
924 val += (*this)(i, j, k, l) * M(k, l);
925
926 return val;
927}
928
929template <typename T>
930T
932 unsigned int l,
933 const RankTwoTensorTempl<T> & M) const
934{
935 T val = 0;
936 for (unsigned int i = 0; i < N; i++)
937 for (unsigned int j = 0; j < N; j++)
938 val += (*this)(i, j, k, l) * M(i, j);
939
940 return val;
941}
942
943template <typename T>
944T
946{
947 // used in the volumetric locking correction
948 T sum = 0;
949 for (auto i : make_range(N))
950 for (auto j : make_range(N))
951 sum += (*this)(i, i, j, j);
952 return sum;
953}
954
955template <typename T>
958{
959 // used for volumetric locking correction
961 a(0) = (*this)(0, 0, 0, 0) + (*this)(0, 0, 1, 1) + (*this)(0, 0, 2, 2); // C0000 + C0011 + C0022
962 a(1) = (*this)(1, 1, 0, 0) + (*this)(1, 1, 1, 1) + (*this)(1, 1, 2, 2); // C1100 + C1111 + C1122
963 a(2) = (*this)(2, 2, 0, 0) + (*this)(2, 2, 1, 1) + (*this)(2, 2, 2, 2); // C2200 + C2211 + C2222
964 return a;
965}
966
967template <typename T>
970 const RankTwoTensorTempl<T> & B,
971 const RankTwoTensorTempl<T> & C) const
972{
974 for (unsigned int i = 0; i < N; i++)
975 for (unsigned int j = 0; j < N; j++)
976 for (unsigned int k = 0; k < N; k++)
977 for (unsigned int l = 0; l < N; l++)
978 for (unsigned int m = 0; m < N; m++)
979 for (unsigned int n = 0; n < N; n++)
980 for (unsigned int t = 0; t < N; t++)
981 R(i, j, k, l) += (*this)(i, m, n, t) * A(j, m) * B(k, n) * C(l, t);
982
983 return R;
984}
985
986template <typename T>
989 const RankTwoTensorTempl<T> & B,
990 const RankTwoTensorTempl<T> & C) const
991{
993 for (unsigned int i = 0; i < N; i++)
994 for (unsigned int j = 0; j < N; j++)
995 for (unsigned int k = 0; k < N; k++)
996 for (unsigned int l = 0; l < N; l++)
997 for (unsigned int m = 0; m < N; m++)
998 for (unsigned int n = 0; n < N; n++)
999 for (unsigned int t = 0; t < N; t++)
1000 R(i, j, k, l) += (*this)(m, j, n, t) * A(i, m) * B(k, n) * C(l, t);
1001
1002 return R;
1003}
1004
1005template <typename T>
1008 const RankTwoTensorTempl<T> & B,
1009 const RankTwoTensorTempl<T> & C) const
1010{
1012 for (unsigned int i = 0; i < N; i++)
1013 for (unsigned int j = 0; j < N; j++)
1014 for (unsigned int k = 0; k < N; k++)
1015 for (unsigned int l = 0; l < N; l++)
1016 for (unsigned int m = 0; m < N; m++)
1017 for (unsigned int n = 0; n < N; n++)
1018 for (unsigned int t = 0; t < N; t++)
1019 R(i, j, k, l) += (*this)(m, n, k, t) * A(i, m) * B(j, n) * C(l, t);
1020
1021 return R;
1022}
1023
1024template <typename T>
1027 const RankTwoTensorTempl<T> & B,
1028 const RankTwoTensorTempl<T> & C) const
1029{
1031 for (unsigned int i = 0; i < N; i++)
1032 for (unsigned int j = 0; j < N; j++)
1033 for (unsigned int k = 0; k < N; k++)
1034 for (unsigned int l = 0; l < N; l++)
1035 for (unsigned int m = 0; m < N; m++)
1036 for (unsigned int n = 0; n < N; n++)
1037 for (unsigned int t = 0; t < N; t++)
1038 R(i, j, k, l) += (*this)(m, n, t, l) * A(i, m) * B(j, n) * C(k, t);
1039
1040 return R;
1041}
1042
1043template <typename T>
1046{
1048
1049 for (unsigned int i = 0; i < N; i++)
1050 for (unsigned int j = 0; j < N; j++)
1051 for (unsigned int k = 0; k < N; k++)
1052 for (unsigned int l = 0; l < N; l++)
1053 for (unsigned int m = 0; m < N; m++)
1054 R(i, j, k, l) += (*this)(m, j, k, l) * A(i, m);
1055
1056 return R;
1057}
1058
1059template <typename T>
1062{
1064
1065 for (unsigned int i = 0; i < N; i++)
1066 for (unsigned int j = 0; j < N; j++)
1067 for (unsigned int k = 0; k < N; k++)
1068 for (unsigned int l = 0; l < N; l++)
1069 for (unsigned int m = 0; m < N; m++)
1070 R(i, j, k, l) += (*this)(i, m, k, l) * A(j, m);
1071
1072 return R;
1073}
1074
1075template <typename T>
1078{
1080
1081 for (unsigned int i = 0; i < N; i++)
1082 for (unsigned int j = 0; j < N; j++)
1083 for (unsigned int k = 0; k < N; k++)
1084 for (unsigned int l = 0; l < N; l++)
1085 for (unsigned int m = 0; m < N; m++)
1086 R(i, j, k, l) += (*this)(i, j, m, l) * A(k, m);
1087
1088 return R;
1089}
1090
1091template <typename T>
1094{
1096
1097 for (unsigned int i = 0; i < N; i++)
1098 for (unsigned int j = 0; j < N; j++)
1099 for (unsigned int k = 0; k < N; k++)
1100 for (unsigned int l = 0; l < N; l++)
1101 for (unsigned int m = 0; m < N; m++)
1102 R(i, j, k, l) += (*this)(i, j, k, m) * A(l, m);
1103
1104 return R;
1105}
1106
1107template <typename T>
1108bool
1110{
1111 for (auto i : make_range(1u, N))
1112 for (auto j : make_range(i))
1113 for (auto k : make_range(1u, N))
1114 for (auto l : make_range(k))
1115 {
1116 // minor symmetries
1117 if ((*this)(i, j, k, l) != (*this)(j, i, k, l) ||
1118 (*this)(i, j, k, l) != (*this)(i, j, l, k))
1119 return false;
1120
1121 // major symmetry
1122 if ((*this)(i, j, k, l) != (*this)(k, l, i, j))
1123 return false;
1124 }
1125 return true;
1126}
1127
1128template <typename T>
1129bool
1131{
1132 // prerequisite is symmetry
1133 if (!isSymmetric())
1134 return false;
1135
1136 // inspect shear components
1137 const T & mu = (*this)(0, 1, 0, 1);
1138 // ...diagonal
1139 if ((*this)(1, 2, 1, 2) != mu || (*this)(2, 0, 2, 0) != mu)
1140 return false;
1141 // ...off-diagonal
1142 if ((*this)(2, 0, 1, 2) != 0.0 || (*this)(0, 1, 1, 2) != 0.0 || (*this)(0, 1, 2, 0) != 0.0)
1143 return false;
1144
1145 // off diagonal blocks in Voigt
1146 for (auto i : make_range(N))
1147 for (auto j : make_range(N))
1148 if (_vals[i * (N3 + N2) + ((j + 1) % N) * N + (j + 2) % N] != 0.0)
1149 return false;
1150
1151 // top left block
1152 const T & K1 = (*this)(0, 0, 0, 0);
1153 const T & K2 = (*this)(0, 0, 1, 1);
1154 if (!MooseUtils::relativeFuzzyEqual(K1 - 4.0 * mu / 3.0, K2 + 2.0 * mu / 3.0))
1155 return false;
1156 if ((*this)(1, 1, 1, 1) != K1 || (*this)(2, 2, 2, 2) != K1)
1157 return false;
1158 for (auto i : make_range(1u, N))
1159 for (auto j : make_range(i))
1160 if ((*this)(i, i, j, j) != K2)
1161 return false;
1162
1163 return true;
1164}
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition MooseEnum.h:55
RankFourTensorTempl is designed to handle any N-dimensional fourth order tensor, C.
void fillSymmetricIsotropic(const T &i0, const T &i1)
void fillFromInputVector(const std::vector< T > &input, FillMethod fill_method)
fillFromInputVector takes some number of inputs to fill the Rank-4 tensor.
void fillGeneralOrthotropicFromInputVector(const std::vector< T > &input)
fillGeneralOrhotropicFromInputVector takes 10 inputs to fill the Rank-4 tensor It defines a general o...
RankFourTensorTempl< T > singleProductL(const RankTwoTensorTempl< T > &) const
Calculates C_ijkm A_lm.
RankFourTensorTempl< T > & operator+=(const RankFourTensorTempl< T > &a)
C_ijkl += a_ijkl for all i, j, k, l.
T sum3x3() const
Calculates the sum of Ciijj for i and j varying from 0 to 2.
void fillGeneralIsotropicFromInputVector(const std::vector< T > &input)
fillGeneralIsotropicFromInputVector takes 3 inputs to fill the Rank-4 tensor with symmetries C_ijkl =...
bool isIsotropic() const
checks if the tensor is isotropic
T contractionIj(unsigned int, unsigned int, const RankTwoTensorTempl< T > &) const
Sum C_ijkl M_kl for a given i,j.
RankFourTensorTempl< T > & operator/=(const T &a)
C_ijkl /= a for all i, j, k, l.
RankFourTensorTempl< T > invSymm() const
This returns A_ijkl such that C_ijkl*A_klmn = 0.5*(de_im de_jn + de_in de_jm) This routine assumes th...
void rotate(const TypeTensor< T > &R)
Rotate the tensor using C_ijkl = R_im R_jn R_ko R_lp C_mnop.
RankFourTensorTempl< T > tripleProductIjk(const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &) const
Calculates C_mntl A_im B_jn C_kt.
static MooseEnum fillMethodEnum()
Static method for use in validParams for getting the "fill_method".
RankFourTensorTempl< T > singleProductK(const RankTwoTensorTempl< T > &) const
Calculates C_ijml A_km.
RankFourTensorTempl< T > transposeMajor() const
Transpose the tensor by swapping the first pair with the second pair of indices.
auto operator+(const RankFourTensorTempl< T2 > &a) const -> RankFourTensorTempl< decltype(T()+T2())>
C_ijkl + a_ijkl.
RankFourTensorTempl< T > transposeIj() const
Transpose the tensor by swapping the first two indeces.
void fillAntisymmetricIsotropicFromInputVector(const std::vector< T > &input)
fillAntisymmetricIsotropicFromInputVector takes 1 input to fill the the antisymmetric Rank-4 tensor w...
libMesh::VectorValue< T > sum3x1() const
Calculates the vector a[i] = sum over j Ciijj for i and j varying from 0 to 2.
RankFourTensorTempl< T > & operator*=(const T &a)
C_ijkl *= a.
InitMethod
Initialization method.
RankFourTensorTempl< T > operator-() const
-C_ijkl
void fillPrincipalFromInputVector(const std::vector< T > &input)
fillPrincipalFromInputVector takes 9 inputs to fill a Rank-4 tensor C1111 = input0 C1122 = input1 C11...
T contractionKl(unsigned int, unsigned int, const RankTwoTensorTempl< T > &) const
Sum M_ij C_ijkl for a given k,l.
void fillSymmetricIsotropicEandNu(const T &E, const T &nu)
RankFourTensorTempl< T > & operator-=(const RankFourTensorTempl< T > &a)
C_ijkl -= a_ijkl.
void fillGeneralIsotropic(const T &i0, const T &i1, const T &i2)
Vector-less fill API functions. See docs of the corresponding ...FromInputVector methods.
void fillSymmetricIsotropicEandNuFromInputVector(const std::vector< T > &input)
fillSymmetricIsotropicEandNuFromInputVector is a variation of the fillSymmetricIsotropicFromInputVect...
RankFourTensorTempl< T > tripleProductIjl(const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &) const
Calculates C_mnkt A_im B_jn C_lt.
RankFourTensorTempl< T > transposeKl() const
Transpose the tensor by swapping the last two indeces.
void fillSymmetricIsotropicFromInputVector(const std::vector< T > &input)
fillSymmetricIsotropicFromInputVector takes 2 inputs to fill the the symmetric Rank-4 tensor with the...
void zero()
Zeros out the tensor.
T _vals[N4]
The values of the rank-four tensor stored by index=(((i * LIBMESH_DIM + j) * LIBMESH_DIM + k) * LIBME...
RankFourTensorTempl< T > singleProductJ(const RankTwoTensorTempl< T > &) const
Calculates C_imkl A_jm.
RankFourTensorTempl< T > singleProductI(const RankTwoTensorTempl< T > &) const
Calculates C_mjkl A_im.
RankFourTensorTempl< T > tripleProductJkl(const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &) const
Calculates C_imnt A_jm B_kn C_lt.
void fillGeneralFromInputVector(const std::vector< T > &input)
fillGeneralFromInputVector takes 81 inputs to fill the Rank-4 tensor No symmetries are explicitly mai...
bool isSymmetric() const
checks if the tensor is symmetric
void fillAxisymmetricRZFromInputVector(const std::vector< T > &input)
fillAxisymmetricRZFromInputVector takes 5 inputs to fill the axisymmetric Rank-4 tensor with the appr...
RankFourTensorTempl< T > tripleProductIkl(const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &, const RankTwoTensorTempl< T > &) const
Calculates C_mjnt A_im B_kn C_lt.
void printReal(std::ostream &stm=Moose::out) const
Print the values of the rank four tensor.
void fillAntisymmetricFromInputVector(const std::vector< T > &input)
fillAntisymmetricFromInputVector takes 6 inputs to fill the the antisymmetric Rank-4 tensor with the ...
RankTwoTensorTempl< T > innerProductTranspose(const RankTwoTensorTempl< T > &) const
Inner product of the major transposed tensor with a rank two tensor.
friend class RankFourTensorTempl
FillMethod
To fill up the 81 entries in the 4th-order tensor, fillFromInputVector is called with one of the foll...
T L2norm() const
sqrt(C_ijkl*C_ijkl)
auto operator*(const Tensor< T2 > &a) const -> typename std::enable_if< TwoTensorMultTraits< Tensor, T2 >::value, RankTwoTensorTempl< decltype(T() *T2())> >::type
C_ijkl*a_kl.
void print(std::ostream &stm=Moose::out) const
Print the rank four tensor.
void fillAntisymmetricIsotropic(const T &i0)
void surfaceFillFromInputVector(const std::vector< T > &input)
Fills the tensor entries ignoring the last dimension (ie, C_ijkl=0 if any of i, j,...
RankTwoTensorTempl is designed to handle the Stress or Strain Tensor for a fully anisotropic material...
T _coords[LIBMESH_DIM *LIBMESH_DIM]
void inverse(const std::vector< std::vector< Real > > &m, std::vector< std::vector< Real > > &m_inv)
Inverse the dense square matrix m using LAPACK routines.
Definition MatrixTools.C:23
auto raw_value(const Eigen::Map< T > &in)
int eps(unsigned int i, unsigned int j)
2D version