deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
symmetric_tensor.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2005 - 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13#ifndef dealii_symmetric_tensor_h
14#define dealii_symmetric_tensor_h
15
16
17#include <deal.II/base/config.h>
18
24#include <deal.II/base/tensor.h>
25#include <deal.II/base/types.h>
26
27#if DEAL_II_KOKKOS_VERSION_GTE(4, 3, 0)
28# include <Kokkos_Swap.hpp>
29#endif
30
31#include <array>
32
34
35// Forward declaration
36#ifndef DOXYGEN
37template <int rank, int dim, typename Number = double>
38class SymmetricTensor;
39#endif
40
51template <int dim, typename Number = double>
55
112template <int dim, typename Number = double>
116
154template <int dim, typename Number = double>
158
159template <int dim, typename Number>
163
164template <int dim, typename Number>
168
178template <int dim2, typename Number>
179DEAL_II_HOST_DEVICE constexpr inline DEAL_II_ALWAYS_INLINE Number
181
220template <int dim, typename Number>
224
238template <int dim, typename Number>
241
242
243
244namespace internal
245{
246 // Workaround: The following 4 overloads are necessary to be able to
247 // compile the library with Apple Clang 8 and older. We should remove
248 // these overloads again when we bump the minimal required version to
249 // something later than clang-3.6 / Apple Clang 6.3.
250 template <int rank, int dim, typename T, typename U>
251 struct ProductTypeImpl<SymmetricTensor<rank, dim, T>, std::complex<U>>
252 {
253 using type =
254 SymmetricTensor<rank,
255 dim,
256 std::complex<typename ProductType<T, U>::type>>;
257 };
258
259 template <int rank, int dim, typename T, typename U>
260 struct ProductTypeImpl<SymmetricTensor<rank, dim, std::complex<T>>,
261 std::complex<U>>
262 {
263 using type =
264 SymmetricTensor<rank,
265 dim,
266 std::complex<typename ProductType<T, U>::type>>;
267 };
268
269 template <typename T, int rank, int dim, typename U>
270 struct ProductTypeImpl<std::complex<T>, SymmetricTensor<rank, dim, U>>
271 {
272 using type =
273 SymmetricTensor<rank,
274 dim,
275 std::complex<typename ProductType<T, U>::type>>;
276 };
277
278 template <int rank, int dim, typename T, typename U>
279 struct ProductTypeImpl<std::complex<T>,
280 SymmetricTensor<rank, dim, std::complex<U>>>
281 {
282 using type =
283 SymmetricTensor<rank,
284 dim,
285 std::complex<typename ProductType<T, U>::type>>;
286 };
287 // end workaround
288
293 namespace SymmetricTensorImplementation
294 {
299 template <int rank, int dim, typename Number>
300 struct Inverse;
301 } // namespace SymmetricTensorImplementation
302
307 namespace SymmetricTensorAccessors
308 {
317 merge(const TableIndices<2> &previous_indices,
318 const unsigned int new_index,
319 const unsigned int position)
320 {
321 AssertIndexRange(position, 2);
322
323 if (position == 0)
324 return {new_index, numbers::invalid_unsigned_int};
325 else
326 return {previous_indices[0], new_index};
327 }
328
329
330
339 merge(const TableIndices<4> &previous_indices,
340 const unsigned int new_index,
341 const unsigned int position)
342 {
343 AssertIndexRange(position, 4);
344
345 switch (position)
346 {
347 case 0:
348 return {new_index,
352 case 1:
353 return {previous_indices[0],
354 new_index,
357 case 2:
358 return {previous_indices[0],
359 previous_indices[1],
360 new_index,
362 case 3:
363 return {previous_indices[0],
364 previous_indices[1],
365 previous_indices[2],
366 new_index};
367 default:
369 return {};
370 }
371 }
372
373
380 template <int rank1,
381 int rank2,
382 int dim,
383 typename Number,
384 typename OtherNumber = Number>
386 {
388 using type =
389 ::SymmetricTensor<rank1 + rank2 - 4, dim, value_type>;
390 };
391
392
399 template <int dim, typename Number, typename OtherNumber>
400 struct double_contraction_result<2, 2, dim, Number, OtherNumber>
401 {
403 };
404
405
406
419 template <int rank, int dim, typename Number>
421
425 template <int dim, typename Number>
426 struct StorageType<2, dim, Number>
427 {
432 static const unsigned int n_independent_components =
433 (dim * dim + dim) / 2;
434
439 };
440
441
442
446 template <int dim, typename Number>
447 struct StorageType<4, dim, Number>
448 {
454 static const unsigned int n_rank2_components = (dim * dim + dim) / 2;
455
459 static const unsigned int n_independent_components =
460 (n_rank2_components *
462
470 };
471
472
473
478 template <int rank, int dim, bool constness, typename Number>
480
487 template <int rank, int dim, typename Number>
488 struct AccessorTypes<rank, dim, true, Number>
489 {
490 using tensor_type = const ::SymmetricTensor<rank, dim, Number>;
491
492 using reference = const Number &;
493 };
494
501 template <int rank, int dim, typename Number>
502 struct AccessorTypes<rank, dim, false, Number>
503 {
505
506 using reference = Number &;
507 };
508
509
542 template <int rank, int dim, bool constness, int P, typename Number>
544 {
545 public:
549 using reference =
553
554 private:
576
581 constexpr DEAL_II_ALWAYS_INLINE
582 Accessor(const Accessor &) = default;
583
584 public:
589 constexpr Accessor<rank, dim, constness, P - 1, Number>
590 operator[](const unsigned int i);
591
596 constexpr Accessor<rank, dim, constness, P - 1, Number>
597 operator[](const unsigned int i) const;
598
599 private:
605
606 // Declare some other classes as friends. Make sure to work around bugs
607 // in some compilers:
608 template <int, int, typename>
609 friend class ::SymmetricTensor;
610 template <int, int, bool, int, typename>
611 friend class Accessor;
612 friend class ::SymmetricTensor<rank, dim, Number>;
613 friend class Accessor<rank, dim, constness, P + 1, Number>;
614 };
615
616
617
625 template <int rank, int dim, bool constness, typename Number>
626 class Accessor<rank, dim, constness, 1, Number>
627 {
628 public:
632 using reference =
636
637 private:
662
667 constexpr DEAL_II_ALWAYS_INLINE
668 Accessor(const Accessor &) = default;
669
670 public:
675 constexpr reference
676 operator[](const unsigned int);
677
682 constexpr reference
683 operator[](const unsigned int) const;
684
685 private:
691
692 // Declare some other classes as friends. Make sure to work around bugs
693 // in some compilers:
694 template <int, int, typename>
695 friend class ::SymmetricTensor;
696 template <int, int, bool, int, typename>
698 friend class ::SymmetricTensor<rank, dim, Number>;
699 friend class SymmetricTensorAccessors::
700 Accessor<rank, dim, constness, 2, Number>;
701 };
702 } // namespace SymmetricTensorAccessors
703} // namespace internal
704
705
706
779template <int rank_, int dim, typename Number>
781{
782public:
783 static_assert(rank_ % 2 == 0, "A SymmetricTensor must have even rank!");
784
793 static constexpr unsigned int dimension = dim;
794
798 static const unsigned int rank = rank_;
799
805 static constexpr unsigned int n_independent_components =
807 n_independent_components;
808
813 constexpr DEAL_II_ALWAYS_INLINE
814 SymmetricTensor() = default;
815
829 template <typename OtherNumber>
830 explicit DEAL_II_HOST_DEVICE
832
849 constexpr SymmetricTensor(const Number (&array)[n_independent_components]);
850
856 template <typename OtherNumber>
859
866 template <typename OtherNumber>
869
877 constexpr SymmetricTensor &
878 operator=(const Number &d);
879
885 constexpr operator Tensor<rank_, dim, Number>() const;
886
891 constexpr bool
893
898 constexpr bool
900
904 template <typename OtherNumber>
907
911 template <typename OtherNumber>
914
919 template <typename OtherNumber>
921 operator*=(const OtherNumber &factor);
922
926 template <typename OtherNumber>
928 operator/=(const OtherNumber &factor);
929
934 constexpr SymmetricTensor
935 operator-() const;
936
989 template <typename OtherNumber>
991 typename internal::SymmetricTensorAccessors::
992 double_contraction_result<rank_, 2, dim, Number, OtherNumber>::type
994
999 template <typename OtherNumber>
1001 typename internal::SymmetricTensorAccessors::
1002 double_contraction_result<rank_, 4, dim, Number, OtherNumber>::type
1004
1009 constexpr Number &
1011
1016 constexpr const Number &
1017 operator()(const TableIndices<rank_> &indices) const;
1018
1024 constexpr internal::SymmetricTensorAccessors::
1025 Accessor<rank_, dim, true, rank_ - 1, Number>
1026 operator[](const unsigned int row) const;
1027
1033 constexpr internal::SymmetricTensorAccessors::
1034 Accessor<rank_, dim, false, rank_ - 1, Number>
1035 operator[](const unsigned int row);
1036
1043 constexpr const Number &
1044 operator[](const TableIndices<rank_> &indices) const;
1045
1052 constexpr Number &
1054
1062 constexpr const Number &
1063 access_raw_entry(const unsigned int unrolled_index) const;
1064
1072 constexpr Number &
1073 access_raw_entry(const unsigned int unrolled_index);
1074
1086 norm() const;
1087
1095 static DEAL_II_HOST_DEVICE constexpr unsigned int
1097
1104 unrolled_to_component_indices(const unsigned int i);
1105
1119 constexpr void
1121
1126 static DEAL_II_HOST_DEVICE constexpr std::size_t
1128
1134 template <class Archive>
1135 void
1136 serialize(Archive &ar, const unsigned int version);
1137
1138private:
1144
1148 using base_tensor_type = typename base_tensor_descriptor::base_tensor_type;
1149
1154
1155#ifndef DOXYGEN
1156
1157 // Make all other symmetric tensors friends.
1158 template <int, int, typename>
1159 friend class SymmetricTensor;
1160
1161 // Make a few more functions friends.
1162 template <int dim2, typename Number2>
1163 friend DEAL_II_HOST_DEVICE constexpr Number2
1165
1166 template <int dim2, typename Number2>
1169
1170 template <int dim2, typename Number2>
1173
1174 template <int dim2, typename Number2>
1177
1178 template <int dim2, typename Number2>
1181
1182 template <int dim2, typename Number2>
1185
1186
1187 // Make a few helper classes friends as well.
1189 Inverse<2, dim, Number>;
1190
1192 Inverse<4, dim, Number>;
1193#endif
1194};
1195
1196
1197
1198// ------------------------- inline functions ------------------------
1199
1200#ifndef DOXYGEN
1201
1202// provide declarations for static members
1203template <int rank, int dim, typename Number>
1204const unsigned int SymmetricTensor<rank, dim, Number>::dimension;
1205
1206template <int rank_, int dim, typename Number>
1207constexpr unsigned int
1208 SymmetricTensor<rank_, dim, Number>::n_independent_components;
1209
1210namespace internal
1211{
1212 namespace SymmetricTensorAccessors
1213 {
1214 template <int rank_, int dim, bool constness, int P, typename Number>
1216 Accessor<rank_, dim, constness, P, Number>::Accessor(
1217 tensor_type &tensor,
1218 const TableIndices<rank_> &previous_indices)
1219 : tensor(tensor)
1220 , previous_indices(previous_indices)
1221 {}
1222
1223
1224
1225 template <int rank_, int dim, bool constness, int P, typename Number>
1227 Accessor<rank_, dim, constness, P - 1, Number>
1228 Accessor<rank_, dim, constness, P, Number>::operator[](
1229 const unsigned int i)
1230 {
1231 return Accessor<rank_, dim, constness, P - 1, Number>(
1232 tensor, merge(previous_indices, i, rank_ - P));
1233 }
1234
1235
1236
1237 template <int rank_, int dim, bool constness, int P, typename Number>
1239 Accessor<rank_, dim, constness, P - 1, Number>
1240 Accessor<rank_, dim, constness, P, Number>::operator[](
1241 const unsigned int i) const
1242 {
1243 return Accessor<rank_, dim, constness, P - 1, Number>(
1244 tensor, merge(previous_indices, i, rank_ - P));
1245 }
1246
1247
1248
1249 template <int rank_, int dim, bool constness, typename Number>
1251 Accessor<rank_, dim, constness, 1, Number>::Accessor(
1252 tensor_type &tensor,
1253 const TableIndices<rank_> &previous_indices)
1254 : tensor(tensor)
1255 , previous_indices(previous_indices)
1256 {}
1257
1258
1259
1260 template <int rank_, int dim, bool constness, typename Number>
1262 typename Accessor<rank_, dim, constness, 1, Number>::reference
1263 Accessor<rank_, dim, constness, 1, Number>::operator[](
1264 const unsigned int i)
1265 {
1266 return tensor(merge(previous_indices, i, rank_ - 1));
1267 }
1268
1269
1270 template <int rank_, int dim, bool constness, typename Number>
1272 typename Accessor<rank_, dim, constness, 1, Number>::reference
1273 Accessor<rank_, dim, constness, 1, Number>::operator[](
1274 const unsigned int i) const
1275 {
1276 return tensor(merge(previous_indices, i, rank_ - 1));
1277 }
1278 } // namespace SymmetricTensorAccessors
1279} // namespace internal
1280
1281
1282
1283template <int rank_, int dim, typename Number>
1284template <typename OtherNumber>
1288{
1289 static_assert(rank == 2, "This function is only implemented for rank==2");
1290 for (unsigned int d = 0; d < dim; ++d)
1291 for (unsigned int e = 0; e < d; ++e)
1292 Assert(t[d][e] == t[e][d],
1293 ExcMessage("The incoming Tensor must be exactly symmetric."));
1294
1295 for (unsigned int d = 0; d < dim; ++d)
1296 data[d] = t[d][d];
1297
1298 for (unsigned int d = 0, c = 0; d < dim; ++d)
1299 for (unsigned int e = d + 1; e < dim; ++e, ++c)
1300 data[dim + c] = t[d][e];
1301}
1302
1303
1304
1305template <int rank_, int dim, typename Number>
1306template <typename OtherNumber>
1310 : data(initializer.data)
1311{}
1312
1313
1314
1315template <int rank_, int dim, typename Number>
1318 const Number (&array)[n_independent_components])
1319 : data(
1320 *reinterpret_cast<const typename base_tensor_type::array_type *>(array))
1321{
1322 // ensure that the reinterpret_cast above actually works
1323 static_assert(sizeof(typename base_tensor_type::array_type) == sizeof(array));
1324}
1325
1326
1327
1328template <int rank_, int dim, typename Number>
1329template <typename OtherNumber>
1334{
1335 data = t.data;
1336 return *this;
1337}
1338
1339
1340
1341template <int rank_, int dim, typename Number>
1345{
1347 ExcMessage("Only assignment with zero is allowed"));
1348 (void)d;
1349
1351
1352 return *this;
1353}
1354
1355
1356namespace internal
1357{
1358 namespace SymmetricTensorImplementation
1359 {
1360 template <int dim, typename Number>
1361 constexpr inline DEAL_II_ALWAYS_INLINE ::Tensor<2, dim, Number>
1362 convert_to_tensor(const ::SymmetricTensor<2, dim, Number> &s)
1363 {
1365
1366 // diagonal entries are stored first
1367 for (unsigned int d = 0; d < dim; ++d)
1368 t[d][d] = s.access_raw_entry(d);
1369
1370 // off-diagonal entries come next, row by row
1371 for (unsigned int d = 0, c = 0; d < dim; ++d)
1372 for (unsigned int e = d + 1; e < dim; ++e, ++c)
1373 {
1374 t[d][e] = s.access_raw_entry(dim + c);
1375 t[e][d] = s.access_raw_entry(dim + c);
1376 }
1377 return t;
1378 }
1379
1380
1381 template <int dim, typename Number>
1382 constexpr ::Tensor<4, dim, Number>
1383 convert_to_tensor(const ::SymmetricTensor<4, dim, Number> &st)
1384 {
1385 // utilize the symmetry properties of SymmetricTensor<4,dim>
1386 // discussed in the class documentation to avoid accessing all
1387 // independent elements of the input tensor more than once
1389
1390 for (unsigned int i = 0; i < dim; ++i)
1391 for (unsigned int j = i; j < dim; ++j)
1392 for (unsigned int k = 0; k < dim; ++k)
1393 for (unsigned int l = k; l < dim; ++l)
1394 t[TableIndices<4>(i, j, k, l)] = t[TableIndices<4>(i, j, l, k)] =
1395 t[TableIndices<4>(j, i, k, l)] =
1396 t[TableIndices<4>(j, i, l, k)] =
1397 st[TableIndices<4>(i, j, k, l)];
1398
1399 return t;
1400 }
1401
1402
1403 template <typename Number>
1404 struct Inverse<2, 1, Number>
1405 {
1406 DEAL_II_HOST_DEVICE constexpr static inline DEAL_II_ALWAYS_INLINE
1407 ::SymmetricTensor<2, 1, Number>
1408 value(const ::SymmetricTensor<2, 1, Number> &t)
1409 {
1411
1412 tmp[0][0] = 1.0 / t[0][0];
1413
1414 return tmp;
1415 }
1416 };
1417
1418
1419 template <typename Number>
1420 struct Inverse<2, 2, Number>
1421 {
1422 DEAL_II_HOST_DEVICE constexpr static inline DEAL_II_ALWAYS_INLINE
1423 ::SymmetricTensor<2, 2, Number>
1424 value(const ::SymmetricTensor<2, 2, Number> &t)
1425 {
1427
1428 // Sympy result: ([
1429 // [ t11/(t00*t11 - t01**2), -t01/(t00*t11 - t01**2)],
1430 // [-t01/(t00*t11 - t01**2), t00/(t00*t11 - t01**2)] ])
1431 const TableIndices<2> idx_00(0, 0);
1432 const TableIndices<2> idx_01(0, 1);
1433 const TableIndices<2> idx_11(1, 1);
1434 const Number inv_det_t =
1435 1.0 / (t[idx_00] * t[idx_11] - t[idx_01] * t[idx_01]);
1436 tmp[idx_00] = t[idx_11];
1437 tmp[idx_01] = -t[idx_01];
1438 tmp[idx_11] = t[idx_00];
1439 tmp *= inv_det_t;
1440
1441 return tmp;
1442 }
1443 };
1444
1445
1446 template <typename Number>
1447 struct Inverse<2, 3, Number>
1448 {
1449 DEAL_II_HOST_DEVICE constexpr static ::SymmetricTensor<2, 3, Number>
1450 value(const ::SymmetricTensor<2, 3, Number> &t)
1451 {
1453
1454 // Sympy result: ([
1455 // [ (t11*t22 - t12**2)/(t00*t11*t22 - t00*t12**2 - t01**2*t22 +
1456 // 2*t01*t02*t12 - t02**2*t11),
1457 // (-t01*t22 + t02*t12)/(t00*t11*t22 - t00*t12**2 - t01**2*t22 +
1458 // 2*t01*t02*t12 - t02**2*t11),
1459 // (t01*t12 - t02*t11)/(t00*t11*t22 - t00*t12**2 - t01**2*t22 +
1460 // 2*t01*t02*t12 - t02**2*t11)],
1461 // [ (-t01*t22 + t02*t12)/(t00*t11*t22 - t00*t12**2 - t01**2*t22 +
1462 // 2*t01*t02*t12 - t02**2*t11),
1463 // (t00*t22 - t02**2)/(t00*t11*t22 - t00*t12**2 - t01**2*t22 +
1464 // 2*t01*t02*t12 - t02**2*t11),
1465 // (t00*t12 - t01*t02)/(-t00*t11*t22 + t00*t12**2 + t01**2*t22 -
1466 // 2*t01*t02*t12 + t02**2*t11)],
1467 // [ (t01*t12 - t02*t11)/(t00*t11*t22 - t00*t12**2 - t01**2*t22 +
1468 // 2*t01*t02*t12 - t02**2*t11),
1469 // (t00*t12 - t01*t02)/(-t00*t11*t22 + t00*t12**2 + t01**2*t22 -
1470 // 2*t01*t02*t12 + t02**2*t11),
1471 // (-t00*t11 + t01**2)/(-t00*t11*t22 + t00*t12**2 + t01**2*t22 -
1472 // 2*t01*t02*t12 + t02**2*t11)] ])
1473 //
1474 // =
1475 //
1476 // [ (t11*t22 - t12**2)/det_t,
1477 // (-t01*t22 + t02*t12)/det_t,
1478 // (t01*t12 - t02*t11)/det_t],
1479 // [ (-t01*t22 + t02*t12)/det_t,
1480 // (t00*t22 - t02**2)/det_t,
1481 // (-t00*t12 + t01*t02)/det_t],
1482 // [ (t01*t12 - t02*t11)/det_t,
1483 // (-t00*t12 + t01*t02)/det_t,
1484 // (t00*t11 - t01**2)/det_t] ])
1485 //
1486 // with det_t = (t00*t11*t22 - t00*t12**2 - t01**2*t22 +
1487 // 2*t01*t02*t12 - t02**2*t11)
1488 const TableIndices<2> idx_00(0, 0);
1489 const TableIndices<2> idx_01(0, 1);
1490 const TableIndices<2> idx_02(0, 2);
1491 const TableIndices<2> idx_11(1, 1);
1492 const TableIndices<2> idx_12(1, 2);
1493 const TableIndices<2> idx_22(2, 2);
1494 const Number inv_det_t =
1495 1.0 / (t[idx_00] * t[idx_11] * t[idx_22] -
1496 t[idx_00] * t[idx_12] * t[idx_12] -
1497 t[idx_01] * t[idx_01] * t[idx_22] +
1498 2.0 * t[idx_01] * t[idx_02] * t[idx_12] -
1499 t[idx_02] * t[idx_02] * t[idx_11]);
1500 tmp[idx_00] = t[idx_11] * t[idx_22] - t[idx_12] * t[idx_12];
1501 tmp[idx_01] = -t[idx_01] * t[idx_22] + t[idx_02] * t[idx_12];
1502 tmp[idx_02] = t[idx_01] * t[idx_12] - t[idx_02] * t[idx_11];
1503 tmp[idx_11] = t[idx_00] * t[idx_22] - t[idx_02] * t[idx_02];
1504 tmp[idx_12] = -t[idx_00] * t[idx_12] + t[idx_01] * t[idx_02];
1505 tmp[idx_22] = t[idx_00] * t[idx_11] - t[idx_01] * t[idx_01];
1506 tmp *= inv_det_t;
1507
1508 return tmp;
1509 }
1510 };
1511
1512
1513 template <typename Number>
1514 struct Inverse<4, 1, Number>
1515 {
1516 DEAL_II_HOST_DEVICE constexpr static inline ::
1517 SymmetricTensor<4, 1, Number>
1518 value(const ::SymmetricTensor<4, 1, Number> &t)
1519 {
1521 tmp.data[0][0] = 1.0 / t.data[0][0];
1522 return tmp;
1523 }
1524 };
1525
1526
1527 template <typename Number>
1528 struct Inverse<4, 2, Number>
1529 {
1530 DEAL_II_HOST_DEVICE constexpr static inline ::
1531 SymmetricTensor<4, 2, Number>
1532 value(const ::SymmetricTensor<4, 2, Number> &t)
1533 {
1535
1536 // Inverting this tensor is a little more complicated than necessary,
1537 // since we store the data of 't' as a 3x3 matrix t.data, but the
1538 // product between a rank-4 and a rank-2 tensor is really not the
1539 // product between this matrix and the 3-vector of a rhs, but rather
1540 //
1541 // B.vec = t.data * mult * A.vec
1542 //
1543 // where mult is a 3x3 matrix with entries [[1,0,0],[0,1,0],[0,0,2]] to
1544 // capture the fact that we need to add up both the c_ij12*a_12 and the
1545 // c_ij21*a_21 terms.
1546 //
1547 // In addition, in this scheme, the identity tensor has the matrix
1548 // representation mult^-1.
1549 //
1550 // The inverse of 't' therefore has the matrix representation
1551 //
1552 // inv.data = mult^-1 * t.data^-1 * mult^-1
1553 //
1554 // in order to compute it, let's first compute the inverse of t.data and
1555 // put it into tmp.data; at the end of the function we then scale the
1556 // last row and column of the inverse by 1/2, corresponding to the left
1557 // and right multiplication with mult^-1.
1558 const Number t4 = t.data[0][0] * t.data[1][1],
1559 t6 = t.data[0][0] * t.data[1][2],
1560 t8 = t.data[0][1] * t.data[1][0],
1561 t00 = t.data[0][2] * t.data[1][0],
1562 t01 = t.data[0][1] * t.data[2][0],
1563 t04 = t.data[0][2] * t.data[2][0],
1564 t07 = 1.0 / (t4 * t.data[2][2] - t6 * t.data[2][1] -
1565 t8 * t.data[2][2] + t00 * t.data[2][1] +
1566 t01 * t.data[1][2] - t04 * t.data[1][1]);
1567 tmp.data[0][0] =
1568 (t.data[1][1] * t.data[2][2] - t.data[1][2] * t.data[2][1]) * t07;
1569 tmp.data[0][1] =
1570 -(t.data[0][1] * t.data[2][2] - t.data[0][2] * t.data[2][1]) * t07;
1571 tmp.data[0][2] =
1572 -(-t.data[0][1] * t.data[1][2] + t.data[0][2] * t.data[1][1]) * t07;
1573 tmp.data[1][0] =
1574 -(t.data[1][0] * t.data[2][2] - t.data[1][2] * t.data[2][0]) * t07;
1575 tmp.data[1][1] = (t.data[0][0] * t.data[2][2] - t04) * t07;
1576 tmp.data[1][2] = -(t6 - t00) * t07;
1577 tmp.data[2][0] =
1578 -(-t.data[1][0] * t.data[2][1] + t.data[1][1] * t.data[2][0]) * t07;
1579 tmp.data[2][1] = -(t.data[0][0] * t.data[2][1] - t01) * t07;
1580 tmp.data[2][2] = (t4 - t8) * t07;
1581
1582 // scale last row and column as mentioned
1583 // above
1584 tmp.data[2][0] /= 2;
1585 tmp.data[2][1] /= 2;
1586 tmp.data[0][2] /= 2;
1587 tmp.data[1][2] /= 2;
1588 tmp.data[2][2] /= 4;
1589
1590 return tmp;
1591 }
1592 };
1593
1594
1595
1600 template <class NumberType>
1602 device_swap(NumberType &x, NumberType &y) noexcept
1603 {
1604 if constexpr (std::is_same_v<NumberType, double> ||
1605 std::is_same_v<NumberType, float> ||
1606 std::is_same_v<NumberType, unsigned int>)
1607 {
1608# if DEAL_II_KOKKOS_VERSION_GTE(4, 3, 0)
1609 Kokkos::kokkos_swap(x, y);
1610# else
1611 NumberType tmp = x;
1612 x = y;
1613 y = tmp;
1614# endif
1615 }
1616 else
1618 }
1619
1620
1621
1622 template <typename Number>
1623 struct Inverse<4, 3, Number>
1624 {
1625 DEAL_II_HOST_DEVICE static ::SymmetricTensor<4, 3, Number>
1626 value(const ::SymmetricTensor<4, 3, Number> &t)
1627 {
1629
1630 // This function follows the exact same scheme as the 2d case, except
1631 // that hardcoding the inverse of a 6x6 matrix is pretty wasteful.
1632 // Instead, we use the Gauss-Jordan algorithm implemented for
1633 // FullMatrix. For historical reasons the following code is copied from
1634 // there, with the tangential benefit that we do not need to copy the
1635 // tensor entries to and from the FullMatrix.
1636 const unsigned int N = 6;
1637
1638 // First get an estimate of the size of the elements of this matrix,
1639 // for later checks whether the pivot element is large enough, or
1640 // whether we have to fear that the matrix is not regular.
1641 Number diagonal_sum = internal::NumberType<Number>::value(0.0);
1642 for (unsigned int i = 0; i < N; ++i)
1643 diagonal_sum += numbers::NumberTraits<Number>::abs(tmp.data[i][i]);
1644 const Number typical_diagonal_element =
1645 diagonal_sum / static_cast<double>(N);
1646 (void)typical_diagonal_element;
1647
1648 unsigned int p[N];
1649 for (unsigned int i = 0; i < N; ++i)
1650 p[i] = i;
1651
1652 for (unsigned int j = 0; j < N; ++j)
1653 {
1654 // Pivot search: search that part of the line on and right of the
1655 // diagonal for the largest element.
1657 unsigned int r = j;
1658 for (unsigned int i = j + 1; i < N; ++i)
1659 if (numbers::NumberTraits<Number>::abs(tmp.data[i][j]) > max)
1660 {
1662 r = i;
1663 }
1664
1665 // Check whether the pivot is too small
1666 Assert(max > 1.e-16 * typical_diagonal_element,
1667 ExcMessage("This tensor seems to be noninvertible"));
1668
1669 // Row interchange
1670 if (r > j)
1671 {
1672 for (unsigned int k = 0; k < N; ++k)
1673 device_swap(tmp.data[j][k], tmp.data[r][k]);
1674
1675 device_swap(p[j], p[r]);
1676 }
1677
1678 // Transformation
1679 const Number hr = 1. / tmp.data[j][j];
1680 tmp.data[j][j] = hr;
1681 for (unsigned int k = 0; k < N; ++k)
1682 {
1683 if (k == j)
1684 continue;
1685 for (unsigned int i = 0; i < N; ++i)
1686 {
1687 if (i == j)
1688 continue;
1689 tmp.data[i][k] -= tmp.data[i][j] * tmp.data[j][k] * hr;
1690 }
1691 }
1692 for (unsigned int i = 0; i < N; ++i)
1693 {
1694 tmp.data[i][j] *= hr;
1695 tmp.data[j][i] *= -hr;
1696 }
1697 tmp.data[j][j] = hr;
1698 }
1699
1700 // Column interchange
1701 Number hv[N];
1702 for (unsigned int i = 0; i < N; ++i)
1703 {
1704 for (unsigned int k = 0; k < N; ++k)
1705 hv[p[k]] = tmp.data[i][k];
1706 for (unsigned int k = 0; k < N; ++k)
1707 tmp.data[i][k] = hv[k];
1708 }
1709
1710 // Scale rows and columns. The mult matrix
1711 // here is diag[1, 1, 1, 1/2, 1/2, 1/2].
1712 for (unsigned int i = 3; i < 6; ++i)
1713 for (unsigned int j = 0; j < 3; ++j)
1714 tmp.data[i][j] /= 2;
1715
1716 for (unsigned int i = 0; i < 3; ++i)
1717 for (unsigned int j = 3; j < 6; ++j)
1718 tmp.data[i][j] /= 2;
1719
1720 for (unsigned int i = 3; i < 6; ++i)
1721 for (unsigned int j = 3; j < 6; ++j)
1722 tmp.data[i][j] /= 4;
1723
1724 return tmp;
1725 }
1726 };
1727
1728 } // namespace SymmetricTensorImplementation
1729} // namespace internal
1730
1731
1732
1733template <int rank_, int dim, typename Number>
1736 const
1737{
1738 return internal::SymmetricTensorImplementation::convert_to_tensor(*this);
1739}
1740
1741
1742
1743template <int rank_, int dim, typename Number>
1744DEAL_II_HOST_DEVICE constexpr bool
1747{
1748 return data == t.data;
1749}
1750
1751
1752
1753template <int rank_, int dim, typename Number>
1754DEAL_II_HOST_DEVICE constexpr bool
1757{
1758 return data != t.data;
1759}
1760
1761
1762
1763template <int rank_, int dim, typename Number>
1764template <typename OtherNumber>
1769{
1770 data += t.data;
1771 return *this;
1772}
1773
1774
1775
1776template <int rank_, int dim, typename Number>
1777template <typename OtherNumber>
1782{
1783 data -= t.data;
1784 return *this;
1785}
1786
1787
1788
1789template <int rank_, int dim, typename Number>
1790template <typename OtherNumber>
1794{
1795 data *= d;
1796 return *this;
1797}
1798
1799
1800
1801template <int rank_, int dim, typename Number>
1802template <typename OtherNumber>
1806{
1807 data /= d;
1808 return *this;
1809}
1810
1811
1812
1813template <int rank_, int dim, typename Number>
1817{
1818 SymmetricTensor tmp = *this;
1819 tmp.data = -tmp.data;
1820 return tmp;
1821}
1822
1823
1824
1825template <int rank_, int dim, typename Number>
1826DEAL_II_HOST_DEVICE constexpr inline DEAL_II_ALWAYS_INLINE void
1828{
1829 data.clear();
1830}
1831
1832
1833
1834template <int rank_, int dim, typename Number>
1835DEAL_II_HOST_DEVICE constexpr std::size_t
1837{
1838 // all memory consists of statically allocated memory of the current
1839 // object, no pointers
1841}
1842
1843
1844
1845namespace internal
1846{
1850 template <int dim, typename Number, typename OtherNumber = Number>
1852 typename SymmetricTensorAccessors::
1853 double_contraction_result<2, 2, dim, Number, OtherNumber>::type
1854 perform_double_contraction(
1855 const typename SymmetricTensorAccessors::StorageType<2, dim, Number>::
1856 base_tensor_type &data,
1857 const typename SymmetricTensorAccessors::
1858 StorageType<2, dim, OtherNumber>::base_tensor_type &sdata)
1859 {
1860 using result_type = typename SymmetricTensorAccessors::
1861 double_contraction_result<2, 2, dim, Number, OtherNumber>::type;
1862
1863 switch (dim)
1864 {
1865 case 1:
1866 return data[0] * sdata[0];
1867
1868 default:
1869 // Start with the non-diagonal part. These values appear
1870 // twice in the matrix, but are only stored once. So we can
1871 // get the double-contraction sum for these elements using
1872 // only one multiplication each, and at the end multiplying
1873 // things by 2.
1874 result_type sum = data[dim] * sdata[dim];
1875 for (unsigned int d = dim + 1; d < (dim * (dim + 1) / 2); ++d)
1876 sum += data[d] * sdata[d];
1877 sum += sum; // sum *= 2
1878
1879 // Now add the contributions from the diagonal
1880 for (unsigned int d = 0; d < dim; ++d)
1881 sum += data[d] * sdata[d];
1882 return sum;
1883 }
1884 }
1885
1886
1887
1892 template <int dim, typename Number, typename OtherNumber = Number>
1894 typename SymmetricTensorAccessors::
1895 double_contraction_result<4, 2, dim, Number, OtherNumber>::type
1896 perform_double_contraction(
1897 const typename SymmetricTensorAccessors::StorageType<4, dim, Number>::
1898 base_tensor_type &data,
1899 const typename SymmetricTensorAccessors::
1900 StorageType<2, dim, OtherNumber>::base_tensor_type &sdata)
1901 {
1902 using result_type = typename SymmetricTensorAccessors::
1903 double_contraction_result<4, 2, dim, Number, OtherNumber>::type;
1904 using value_type = typename SymmetricTensorAccessors::
1905 double_contraction_result<4, 2, dim, Number, OtherNumber>::value_type;
1906
1907 const unsigned int data_dim = SymmetricTensorAccessors::
1908 StorageType<2, dim, value_type>::n_independent_components;
1909 value_type tmp[data_dim]{};
1910 for (unsigned int i = 0; i < data_dim; ++i)
1911 tmp[i] =
1912 perform_double_contraction<dim, Number, OtherNumber>(data[i], sdata);
1913 return result_type(tmp);
1914 }
1915
1916
1917
1922 template <int dim, typename Number, typename OtherNumber = Number>
1924 typename SymmetricTensorAccessors::StorageType<
1925 2,
1926 dim,
1927 typename SymmetricTensorAccessors::
1928 double_contraction_result<2, 4, dim, Number, OtherNumber>::value_type>::
1929 base_tensor_type
1930 perform_double_contraction(
1931 const typename SymmetricTensorAccessors::StorageType<2, dim, Number>::
1932 base_tensor_type &data,
1933 const typename SymmetricTensorAccessors::
1934 StorageType<4, dim, OtherNumber>::base_tensor_type &sdata)
1935 {
1936 using value_type = typename SymmetricTensorAccessors::
1937 double_contraction_result<2, 4, dim, Number, OtherNumber>::value_type;
1938 using base_tensor_type = typename SymmetricTensorAccessors::
1939 StorageType<2, dim, value_type>::base_tensor_type;
1940
1941 base_tensor_type tmp;
1942 for (unsigned int i = 0; i < tmp.dimension; ++i)
1943 {
1944 // Start with the non-diagonal part. These values appear
1945 // twice in the matrix, but are only stored once. So we can
1946 // get the double-contraction sum for these elements using
1947 // only one multiplication each, and at the end multiplying
1948 // things by 2.
1949 value_type sum = data[dim] * sdata[dim][i];
1950 for (unsigned int d = dim + 1; d < (dim * (dim + 1) / 2); ++d)
1951 sum += data[d] * sdata[d][i];
1952 sum += sum; // sum *= 2
1953
1954 // Now add the contributions from the diagonal
1955 for (unsigned int d = 0; d < dim; ++d)
1956 sum += data[d] * sdata[d][i];
1957 tmp[i] = sum;
1958 }
1959 return tmp;
1960 }
1961
1962
1963
1967 template <int dim, typename Number, typename OtherNumber = Number>
1969 typename SymmetricTensorAccessors::StorageType<
1970 4,
1971 dim,
1972 typename SymmetricTensorAccessors::
1973 double_contraction_result<4, 4, dim, Number, OtherNumber>::value_type>::
1974 base_tensor_type
1975 perform_double_contraction(
1976 const typename SymmetricTensorAccessors::StorageType<4, dim, Number>::
1977 base_tensor_type &data,
1978 const typename SymmetricTensorAccessors::
1979 StorageType<4, dim, OtherNumber>::base_tensor_type &sdata)
1980 {
1981 using value_type = typename SymmetricTensorAccessors::
1982 double_contraction_result<4, 4, dim, Number, OtherNumber>::value_type;
1983 using base_tensor_type = typename SymmetricTensorAccessors::
1984 StorageType<4, dim, value_type>::base_tensor_type;
1985
1986 const unsigned int data_dim = SymmetricTensorAccessors::
1987 StorageType<2, dim, value_type>::n_independent_components;
1988 base_tensor_type tmp;
1989 for (unsigned int i = 0; i < data_dim; ++i)
1990 for (unsigned int j = 0; j < data_dim; ++j)
1991 {
1992 // Start with the non-diagonal part
1993 for (unsigned int d = dim; d < (dim * (dim + 1) / 2); ++d)
1994 tmp[i][j] += data[i][d] * sdata[d][j];
1995 tmp[i][j] += tmp[i][j]; // tmp[i][j] *= 2;
1996
1997 // Now add the contributions from the diagonal
1998 for (unsigned int d = 0; d < dim; ++d)
1999 tmp[i][j] += data[i][d] * sdata[d][j];
2000 }
2001 return tmp;
2002 }
2003
2004} // end of namespace internal
2005
2006
2007
2008template <int rank_, int dim, typename Number>
2009template <typename OtherNumber>
2011 typename internal::SymmetricTensorAccessors::
2012 double_contraction_result<rank_, 2, dim, Number, OtherNumber>::type
2015{
2016 // Dispatch to functions that know the types of the involved
2017 // arguments via overloads.
2018 return internal::perform_double_contraction<dim, Number, OtherNumber>(data,
2019 s.data);
2020}
2021
2022
2023
2024template <int rank_, int dim, typename Number>
2025template <typename OtherNumber>
2027 typename internal::SymmetricTensorAccessors::
2028 double_contraction_result<rank_, 4, dim, Number, OtherNumber>::type
2031{
2032 typename internal::SymmetricTensorAccessors::
2033 double_contraction_result<rank_, 4, dim, Number, OtherNumber>::type tmp;
2034 tmp.data =
2035 internal::perform_double_contraction<dim, Number, OtherNumber>(data,
2036 s.data);
2037 return tmp;
2038}
2039
2040
2041
2042// internal namespace to switch between the
2043// access of different tensors. There used to
2044// be explicit instantiations before for
2045// different ranks and dimensions, but since
2046// we now allow for templates on the data
2047// type, and since we cannot partially
2048// specialize the implementation, this got
2049// into a separate namespace
2050namespace internal
2051{
2052 namespace SymmetricTensorImplementation
2053 {
2054 // a function to do the unrolling from a set of indices to a
2055 // scalar index into the array in which we store the elements of
2056 // a symmetric tensor
2057 //
2058 // this function is for rank-2 tensors
2059 template <int dim>
2061 DEAL_II_CONSTEXPR inline DEAL_II_ALWAYS_INLINE unsigned int
2063 {
2064 AssertIndexRange(indices[0], dim);
2065 AssertIndexRange(indices[1], dim);
2066
2067 switch (dim)
2068 {
2069 case 1:
2070 {
2071 return 0;
2072 }
2073 case 2:
2074 {
2075 constexpr unsigned int table[2][2] = {{0, 2}, {2, 1}};
2076 return table[indices[0]][indices[1]];
2077 }
2078 case 3:
2079 {
2080 // Split the following table to avoid compiler warnings about
2081 // uninitialized values:
2082 // constexpr unsigned int table[3][3] = {{0, 3, 4},
2083 // {3, 1, 5},
2084 // {4, 5, 2}};
2085
2086 const unsigned int i = indices[0];
2087 const unsigned int j = indices[1];
2088 if (i == 0)
2089 {
2090 constexpr unsigned int table[3] = {0, 3, 4};
2091 return table[j];
2092 }
2093 else if (i == 1)
2094 {
2095 constexpr unsigned int table[3] = {3, 1, 5};
2096 return table[j];
2097 }
2098 else
2099 {
2100 constexpr unsigned int table[3] = {4, 5, 2};
2101 return table[j];
2102 }
2103 }
2104 case 4:
2105 {
2106 constexpr unsigned int table[4][4] = {{0, 4, 5, 6},
2107 {4, 1, 7, 8},
2108 {5, 7, 2, 9},
2109 {6, 8, 9, 3}};
2110 return table[indices[0]][indices[1]];
2111 }
2112 default:
2113 // for the remainder, manually figure out the numbering
2114 {
2115 if (indices[0] == indices[1])
2116 return indices[0];
2117
2118 const TableIndices<2> sorted_indices(
2119 std::min(indices[0], indices[1]),
2120 std::max(indices[0], indices[1]));
2121
2122 // Here (d, e) are the row and column of the symmetric matrix and
2123 // 'dim + c' is the index into the Tensor<1, dim> actually used
2124 // for storage.
2125 unsigned int c = 0;
2126 for (unsigned int d = 0; d < dim; ++d)
2127 for (unsigned int e = d + 1; e < dim; ++e, ++c)
2128 if ((sorted_indices[0] == d) && (sorted_indices[1] == e))
2129 return dim + c;
2130
2131 // should never get here:
2133 return 0;
2134 }
2135 }
2136 }
2137
2138 // a function to do the unrolling from a set of indices to a
2139 // scalar index into the array in which we store the elements of
2140 // a symmetric tensor
2141 //
2142 // this function is for tensors of ranks not already handled
2143 // above
2144 template <int dim, int rank_>
2145 DEAL_II_HOST_DEVICE DEAL_II_CONSTEXPR inline unsigned int
2147 {
2148 (void)indices;
2151 }
2152 } // namespace SymmetricTensorImplementation
2153
2154 template <int dim, typename Number>
2156 symmetric_tensor_access(const TableIndices<2> &indices,
2157 typename SymmetricTensorAccessors::
2158 StorageType<2, dim, Number>::base_tensor_type &data)
2159 {
2160 return data[SymmetricTensorImplementation::component_to_unrolled_index<dim>(
2161 indices)];
2162 }
2163
2164
2165
2166 template <int dim, typename Number>
2168 Number &
2169 symmetric_tensor_access(
2170 const TableIndices<2> &indices,
2171 const typename SymmetricTensorAccessors::StorageType<2, dim, Number>::
2172 base_tensor_type &data)
2173 {
2174 return data[SymmetricTensorImplementation::component_to_unrolled_index<dim>(
2175 indices)];
2176 }
2177
2178
2179
2180 template <int dim, typename Number>
2182 symmetric_tensor_access(const TableIndices<4> &indices,
2183 typename SymmetricTensorAccessors::
2184 StorageType<4, dim, Number>::base_tensor_type &data)
2185 {
2186 switch (dim)
2187 {
2188 case 1:
2189 return data[0][0];
2190
2191 case 2:
2192 // each entry of the tensor can be thought of as an entry in a
2193 // matrix that maps the rolled-out rank-2 tensors into rolled-out
2194 // rank-2 tensors. this is the format in which we store rank-4
2195 // tensors. determine which position the present entry is
2196 // stored in
2197 {
2198 constexpr std::size_t base_index[2][2] = {{0, 2}, {2, 1}};
2199 return data[base_index[indices[0]][indices[1]]]
2200 [base_index[indices[2]][indices[3]]];
2201 }
2202 case 3:
2203 // each entry of the tensor can be thought of as an entry in a
2204 // matrix that maps the rolled-out rank-2 tensors into rolled-out
2205 // rank-2 tensors. this is the format in which we store rank-4
2206 // tensors. determine which position the present entry is
2207 // stored in
2208 {
2209 constexpr std::size_t base_index[3][3] = {{0, 3, 4},
2210 {3, 1, 5},
2211 {4, 5, 2}};
2212 return data[base_index[indices[0]][indices[1]]]
2213 [base_index[indices[2]][indices[3]]];
2214 }
2215
2216 default:
2218 }
2219
2220 // The code should never reach here.
2221 // We cannot return a static variable, as this class must support number
2222 // types that require no instances of the number type to be in scope during
2223 // a reinitialization procedure (e.g. ADOL-C adtl::adouble).
2224 return data[0][0];
2225 }
2226
2227
2228 template <int dim, typename Number>
2230 Number &
2231 symmetric_tensor_access(
2232 const TableIndices<4> &indices,
2233 const typename SymmetricTensorAccessors::StorageType<4, dim, Number>::
2234 base_tensor_type &data)
2235 {
2236 switch (dim)
2237 {
2238 case 1:
2239 return data[0][0];
2240
2241 case 2:
2242 // each entry of the tensor can be thought of as an entry in a
2243 // matrix that maps the rolled-out rank-2 tensors into rolled-out
2244 // rank-2 tensors. this is the format in which we store rank-4
2245 // tensors. determine which position the present entry is
2246 // stored in
2247 {
2248 constexpr std::size_t base_index[2][2] = {{0, 2}, {2, 1}};
2249 return data[base_index[indices[0]][indices[1]]]
2250 [base_index[indices[2]][indices[3]]];
2251 }
2252 case 3:
2253 // each entry of the tensor can be thought of as an entry in a
2254 // matrix that maps the rolled-out rank-2 tensors into rolled-out
2255 // rank-2 tensors. this is the format in which we store rank-4
2256 // tensors. determine which position the present entry is
2257 // stored in
2258 {
2259 constexpr std::size_t base_index[3][3] = {{0, 3, 4},
2260 {3, 1, 5},
2261 {4, 5, 2}};
2262 return data[base_index[indices[0]][indices[1]]]
2263 [base_index[indices[2]][indices[3]]];
2264 }
2265
2266 default:
2268 }
2269
2270 // The code should never reach here.
2271 // We cannot return a static variable, as this class must support number
2272 // types that require no instances of the number type to be in scope during
2273 // a reinitialization procedure (e.g. ADOL-C adtl::adouble).
2274 return data[0][0];
2275 }
2276
2277} // end of namespace internal
2278
2279
2280
2281template <int rank_, int dim, typename Number>
2284 const TableIndices<rank_> &indices)
2285{
2286 for (unsigned int r = 0; r < rank; ++r)
2287 AssertIndexRange(indices[r], dim);
2288 return internal::symmetric_tensor_access<dim, Number>(indices, data);
2289}
2290
2291
2292
2293template <int rank_, int dim, typename Number>
2294DEAL_II_HOST_DEVICE constexpr inline DEAL_II_ALWAYS_INLINE const Number &
2296 const TableIndices<rank_> &indices) const
2297{
2298 for (unsigned int r = 0; r < rank; ++r)
2299 AssertIndexRange(indices[r], dim);
2300 return internal::symmetric_tensor_access<dim, Number>(indices, data);
2301}
2302
2303
2304
2305namespace internal
2306{
2307 namespace SymmetricTensorImplementation
2308 {
2309 template <int rank_>
2310 constexpr TableIndices<rank_>
2311 get_partially_filled_indices(const unsigned int row,
2312 const std::integral_constant<int, 2> &)
2313 {
2315 }
2316
2317
2318 template <int rank_>
2319 constexpr TableIndices<rank_>
2320 get_partially_filled_indices(const unsigned int row,
2321 const std::integral_constant<int, 4> &)
2322 {
2323 return TableIndices<rank_>(row,
2327 }
2328 } // namespace SymmetricTensorImplementation
2329} // namespace internal
2330
2331
2332template <int rank_, int dim, typename Number>
2333DEAL_II_HOST_DEVICE constexpr DEAL_II_ALWAYS_INLINE internal::
2334 SymmetricTensorAccessors::Accessor<rank_, dim, true, rank_ - 1, Number>
2335 SymmetricTensor<rank_, dim, Number>::operator[](const unsigned int row) const
2336{
2337 return internal::SymmetricTensorAccessors::
2338 Accessor<rank_, dim, true, rank_ - 1, Number>(
2339 *this,
2340 internal::SymmetricTensorImplementation::get_partially_filled_indices<
2341 rank_>(row, std::integral_constant<int, rank_>()));
2342}
2343
2344
2345
2346template <int rank_, int dim, typename Number>
2347DEAL_II_HOST_DEVICE constexpr inline DEAL_II_ALWAYS_INLINE internal::
2348 SymmetricTensorAccessors::Accessor<rank_, dim, false, rank_ - 1, Number>
2350{
2351 return internal::SymmetricTensorAccessors::
2352 Accessor<rank_, dim, false, rank_ - 1, Number>(
2353 *this,
2354 internal::SymmetricTensorImplementation::get_partially_filled_indices<
2355 rank_>(row, std::integral_constant<int, rank_>()));
2356}
2357
2358
2359
2360template <int rank_, int dim, typename Number>
2363 const TableIndices<rank_> &indices) const
2364{
2365 return operator()(indices);
2366}
2367
2368
2369
2370template <int rank_, int dim, typename Number>
2373 const TableIndices<rank_> &indices)
2374{
2375 return operator()(indices);
2376}
2377
2378
2379
2380template <int rank_, int dim, typename Number>
2381DEAL_II_HOST_DEVICE constexpr inline const Number &
2383 const unsigned int index) const
2384{
2385 // make nvcc happy:
2386 constexpr unsigned int my_n_independent_components = n_independent_components;
2387 AssertIndexRange(index, my_n_independent_components);
2388
2389 if constexpr (rank == 2)
2390 return data[index];
2391 else
2392 return data[decltype(data)::unrolled_to_component_indices(index)];
2393}
2394
2395
2396
2397template <int rank_, int dim, typename Number>
2398DEAL_II_HOST_DEVICE constexpr inline Number &
2400{
2401 // make nvcc happy:
2402 constexpr unsigned int my_n_independent_components = n_independent_components;
2403 AssertIndexRange(index, my_n_independent_components);
2404
2405 if constexpr (rank == 2)
2406 return data[index];
2407 else
2408 return data[decltype(data)::unrolled_to_component_indices(index)];
2409}
2410
2411
2412
2413namespace internal
2414{
2415 template <int dim, typename Number>
2416 constexpr inline typename numbers::NumberTraits<Number>::real_type
2417 compute_norm(const typename SymmetricTensorAccessors::
2418 StorageType<2, dim, Number>::base_tensor_type &data)
2419 {
2420 // Make things work with AD types
2421 using std::sqrt;
2422 switch (dim)
2423 {
2424 case 1:
2426
2427 case 2:
2431
2432 case 3:
2439
2440 default:
2441 {
2442 typename numbers::NumberTraits<Number>::real_type return_value =
2444
2445 for (unsigned int d = 0; d < dim; ++d)
2446 return_value +=
2448 for (unsigned int d = dim; d < (dim * dim + dim) / 2; ++d)
2449 return_value +=
2451
2452 return sqrt(return_value);
2453 }
2454 }
2455 }
2456
2457
2458
2459 template <int dim, typename Number>
2460 constexpr inline typename numbers::NumberTraits<Number>::real_type
2461 compute_norm(const typename SymmetricTensorAccessors::
2462 StorageType<4, dim, Number>::base_tensor_type &data)
2463 {
2464 // Make things work with AD types
2465 using std::sqrt;
2466 switch (dim)
2467 {
2468 case 1:
2470
2471 default:
2472 {
2473 typename numbers::NumberTraits<Number>::real_type return_value =
2475
2476 const unsigned int n_independent_components = data.dimension;
2477
2478 for (unsigned int i = 0; i < dim; ++i)
2479 for (unsigned int j = 0; j < dim; ++j)
2480 return_value +=
2482 for (unsigned int i = 0; i < dim; ++i)
2483 for (unsigned int j = dim; j < n_independent_components; ++j)
2484 return_value +=
2486 for (unsigned int i = dim; i < n_independent_components; ++i)
2487 for (unsigned int j = 0; j < dim; ++j)
2488 return_value +=
2490 for (unsigned int i = dim; i < n_independent_components; ++i)
2491 for (unsigned int j = dim; j < n_independent_components; ++j)
2492 return_value +=
2494
2495 return sqrt(return_value);
2496 }
2497 }
2498 }
2499
2500} // end of namespace internal
2501
2502
2503
2504template <int rank_, int dim, typename Number>
2507{
2508 return internal::compute_norm<dim, Number>(data);
2509}
2510
2511
2512
2513template <int rank_, int dim, typename Number>
2514DEAL_II_HOST_DEVICE constexpr unsigned int
2516 const TableIndices<rank_> &indices)
2517{
2518 return internal::SymmetricTensorImplementation::component_to_unrolled_index<
2519 dim>(indices);
2520}
2521
2522
2523
2524namespace internal
2525{
2526 namespace SymmetricTensorImplementation
2527 {
2528 // a function to do the inverse of the unrolling from a set of
2529 // indices to a scalar index into the array in which we store
2530 // the elements of a symmetric tensor. in other words, it goes
2531 // from the scalar index into the array to a set of indices of
2532 // the tensor
2533 //
2534 // this function is for rank-2 tensors
2535 template <int dim>
2536 constexpr inline DEAL_II_ALWAYS_INLINE TableIndices<2>
2537 unrolled_to_component_indices(const unsigned int i,
2538 const std::integral_constant<int, 2> &)
2539 {
2540 Assert(
2543 i,
2544 0,
2546 switch (dim)
2547 {
2548 case 1:
2549 {
2550 return {0, 0};
2551 }
2552
2553 case 2:
2554 {
2555 const TableIndices<2> table[3] = {TableIndices<2>(0, 0),
2556 TableIndices<2>(1, 1),
2557 TableIndices<2>(0, 1)};
2558 return table[i];
2559 }
2560
2561 case 3:
2562 {
2563 const TableIndices<2> table[6] = {TableIndices<2>(0, 0),
2564 TableIndices<2>(1, 1),
2565 TableIndices<2>(2, 2),
2566 TableIndices<2>(0, 1),
2567 TableIndices<2>(0, 2),
2568 TableIndices<2>(1, 2)};
2569 return table[i];
2570 }
2571
2572 default:
2573 if (i < dim)
2574 return {i, i};
2575
2576 for (unsigned int d = 0, c = dim; d < dim; ++d)
2577 for (unsigned int e = d + 1; e < dim; ++e, ++c)
2578 if (c == i)
2579 return {d, e};
2580
2581 // should never get here:
2583 return {0, 0};
2584 }
2585 }
2586
2587 // a function to do the inverse of the unrolling from a set of
2588 // indices to a scalar index into the array in which we store
2589 // the elements of a symmetric tensor. in other words, it goes
2590 // from the scalar index into the array to a set of indices of
2591 // the tensor
2592 //
2593 // this function is for tensors of a rank not already handled
2594 // above
2595 template <int dim, int rank_>
2596 constexpr inline std::enable_if_t<rank_ != 2, TableIndices<rank_>>
2597 unrolled_to_component_indices(const unsigned int i,
2598 const std::integral_constant<int, rank_> &)
2599 {
2600 (void)i;
2601 Assert(
2602 (i <
2604 ExcIndexRange(i,
2605 0,
2607 n_independent_components));
2609 return TableIndices<rank_>();
2610 }
2611
2612 } // namespace SymmetricTensorImplementation
2613} // namespace internal
2614
2615template <int rank_, int dim, typename Number>
2618 const unsigned int i)
2619{
2620 return internal::SymmetricTensorImplementation::unrolled_to_component_indices<
2621 dim>(i, std::integral_constant<int, rank_>());
2622}
2623
2624
2625
2626template <int rank_, int dim, typename Number>
2627template <class Archive>
2628inline void
2629SymmetricTensor<rank_, dim, Number>::serialize(Archive &ar, const unsigned int)
2630{
2631 ar &data;
2632}
2633
2634
2635#endif // DOXYGEN
2636
2637/* ----------------- Non-member functions operating on tensors. ------------ */
2638
2639
2652template <int rank_, int dim, typename Number, typename OtherNumber>
2663
2664
2677template <int rank_, int dim, typename Number, typename OtherNumber>
2688
2689
2697template <int rank_, int dim, typename Number, typename OtherNumber>
2702{
2703 return Tensor<rank_, dim, Number>(left) + right;
2704}
2705
2706
2714template <int rank_, int dim, typename Number, typename OtherNumber>
2719{
2720 return left + Tensor<rank_, dim, OtherNumber>(right);
2721}
2722
2723
2731template <int rank_, int dim, typename Number, typename OtherNumber>
2736{
2737 return Tensor<rank_, dim, Number>(left) - right;
2738}
2739
2740
2748template <int rank_, int dim, typename Number, typename OtherNumber>
2753{
2754 return left - Tensor<rank_, dim, OtherNumber>(right);
2755}
2756
2757
2758
2759template <int dim, typename Number>
2762{
2763 switch (dim)
2764 {
2765 case 1:
2766 return t.data[0];
2767 case 2:
2768 return (t.data[0] * t.data[1] - t.data[2] * t.data[2]);
2769 case 3:
2770 {
2771 // in analogy to general tensors, but
2772 // there's something to be simplified for
2773 // the present case
2774 const Number tmp = t.data[3] * t.data[4] * t.data[5];
2775 return (tmp + tmp + t.data[0] * t.data[1] * t.data[2] -
2776 t.data[0] * t.data[5] * t.data[5] -
2777 t.data[1] * t.data[4] * t.data[4] -
2778 t.data[2] * t.data[3] * t.data[3]);
2779 }
2780 default:
2783 }
2784}
2785
2786
2787
2799template <int dim, typename Number>
2805
2806
2807
2808template <int dim, typename Number>
2809DEAL_II_HOST_DEVICE constexpr inline DEAL_II_ALWAYS_INLINE Number
2811{
2812 Number t = d.data[0];
2813 for (unsigned int i = 1; i < dim; ++i)
2814 t += d.data[i];
2815 return t;
2816}
2817
2818
2830template <int dim, typename Number>
2833{
2834 return trace(t);
2835}
2836
2837
2849template <typename Number>
2855
2856
2857
2876template <typename Number>
2879{
2880 return t[0][0] * t[1][1] - t[0][1] * t[0][1];
2881}
2882
2883
2884
2893template <typename Number>
2896{
2897 return (t[0][0] * t[1][1] + t[1][1] * t[2][2] + t[2][2] * t[0][0] -
2898 t[0][1] * t[0][1] - t[0][2] * t[0][2] - t[1][2] * t[1][2]);
2899}
2900
2901
2902
2910template <typename Number>
2911std::array<Number, 1>
2913
2914
2915
2938template <typename Number>
2939std::array<Number, 2>
2941
2942
2943
2966template <typename Number>
2967std::array<Number, 3>
2969
2970
2971
2972namespace internal
2973{
2974 namespace SymmetricTensorImplementation
2975 {
2987 template <int dim, typename Number>
2988 void
2989 tridiagonalize(const ::SymmetricTensor<2, dim, Number> &A,
2991 std::array<Number, dim> &d,
2992 std::array<Number, dim - 1> &e);
2993
2994
2995
3009 template <int dim, typename Number>
3010 std::array<std::pair<Number, Tensor<1, dim, Number>>, dim>
3011 ql_implicit_shifts(const ::SymmetricTensor<2, dim, Number> &A);
3012
3013
3014
3028 template <int dim, typename Number>
3029 std::array<std::pair<Number, Tensor<1, dim, Number>>, dim>
3031
3032
3033
3047 template <typename Number>
3048 std::array<std::pair<Number, Tensor<1, 2, Number>>, 2>
3049 hybrid(const ::SymmetricTensor<2, 2, Number> &A);
3050
3051
3052
3067 template <typename Number>
3068 std::array<std::pair<Number, Tensor<1, 3, Number>>, 3>
3069 hybrid(const ::SymmetricTensor<2, 3, Number> &A);
3070
3075 template <int dim, typename Number>
3077 {
3078 using EigValsVecs = std::pair<Number, Tensor<1, dim, Number>>;
3079 bool
3080 operator()(const EigValsVecs &lhs, const EigValsVecs &rhs)
3081 {
3082 return lhs.first > rhs.first;
3083 }
3084 };
3085
3086 } // namespace SymmetricTensorImplementation
3087
3088} // namespace internal
3089
3090
3091
3092// The line below is to ensure that doxygen puts the full description
3093// of this global enumeration into the documentation
3094// See https://stackoverflow.com/a/1717984
3106{
3116 hybrid,
3134 jacobi
3135};
3136
3137
3138
3149template <int dim, typename Number>
3150std::array<std::pair<Number, Tensor<1, dim, Number>>, dim>
3154
3155
3156
3165template <int rank_, int dim, typename Number>
3169{
3170 return t;
3171}
3172
3173
3174
3175template <int dim, typename Number>
3179{
3181
3182 // subtract scaled trace from the diagonal
3183 const Number tr = trace(t) * internal::NumberType<Number>::value(1.0 / dim);
3184 for (unsigned int i = 0; i < dim; ++i)
3185 tmp.data[i] -= tr;
3186
3187 return tmp;
3188}
3189
3190
3191
3192template <int dim, typename Number>
3196{
3197 // create a default constructed matrix filled with
3198 // zeros, then set the diagonal elements to one
3200 switch (dim)
3201 {
3202 case 1:
3204 break;
3205 case 2:
3206 tmp.data[0] = tmp.data[1] = internal::NumberType<Number>::value(1.);
3207 break;
3208 case 3:
3209 tmp.data[0] = tmp.data[1] = tmp.data[2] =
3211 break;
3212 default:
3213 for (unsigned int d = 0; d < dim; ++d)
3215 }
3216 return tmp;
3217}
3218
3219
3220
3221template <int dim, typename Number>
3224{
3226
3227 // fill the elements treating the diagonal
3228 for (unsigned int i = 0; i < dim; ++i)
3229 for (unsigned int j = 0; j < dim; ++j)
3230 tmp.data[i][j] =
3231 internal::NumberType<Number>::value((i == j ? 1. : 0.) - 1. / dim);
3232
3233 // then fill the ones that copy over the
3234 // non-diagonal elements. note that during
3235 // the double-contraction, we handle the
3236 // off-diagonal elements twice, so simply
3237 // copying requires a weight of 1/2
3238 for (unsigned int i = dim;
3239 i < internal::SymmetricTensorAccessors::StorageType<4, dim, Number>::
3240 n_rank2_components;
3241 ++i)
3243
3244 return tmp;
3245}
3246
3247
3248
3249template <int dim, typename Number>
3253{
3255
3256 // fill the elements treating the diagonal
3257 for (unsigned int i = 0; i < dim; ++i)
3259
3260 // then fill the ones that copy over the
3261 // non-diagonal elements. note that during
3262 // the double-contraction, we handle the
3263 // off-diagonal elements twice, so simply
3264 // copying requires a weight of 1/2
3265 for (unsigned int i = dim;
3266 i < internal::SymmetricTensorAccessors::StorageType<4, dim, Number>::
3267 n_rank2_components;
3268 ++i)
3270
3271 return tmp;
3272}
3273
3274
3275
3285template <int dim, typename Number>
3293
3294
3295
3306template <int dim, typename Number>
3313
3314
3315
3337template <int dim, typename Number>
3341{
3343
3344 // fill only the elements really needed
3345 for (unsigned int i = 0; i < dim; ++i)
3346 for (unsigned int j = i; j < dim; ++j)
3347 for (unsigned int k = 0; k < dim; ++k)
3348 for (unsigned int l = k; l < dim; ++l)
3349 tmp[i][j][k][l] = t1[i][j] * t2[k][l];
3350
3351 return tmp;
3352}
3353
3379template <int dim, typename Number>
3380std::pair<SymmetricTensor<2, dim, Number>, SymmetricTensor<2, dim, Number>>
3382{
3383 Assert(dim <= 3, ExcNotImplemented());
3384
3385 const std::array<std::pair<Number, Tensor<1, dim, Number>>, dim>
3386 eigen_system = eigenvectors(original_tensor);
3387
3388 std::pair<SymmetricTensor<2, dim, Number>, SymmetricTensor<2, dim, Number>>
3389 positive_negative_tensors;
3390
3391 auto &[positive_part_tensor, negative_part_tensor] =
3392 positive_negative_tensors;
3393
3394 positive_part_tensor = 0;
3395 for (unsigned int i = 0; i < dim; ++i)
3396 if (eigen_system[i].first > 0)
3397 positive_part_tensor += eigen_system[i].first *
3398 symmetrize(outer_product(eigen_system[i].second,
3399 eigen_system[i].second));
3400
3401 negative_part_tensor = 0;
3402 for (unsigned int i = 0; i < dim; ++i)
3403 if (eigen_system[i].first < 0)
3404 negative_part_tensor += eigen_system[i].first *
3405 symmetrize(outer_product(eigen_system[i].second,
3406 eigen_system[i].second));
3407
3408 return positive_negative_tensors;
3409}
3410
3443template <int dim, typename Number>
3444std::tuple<SymmetricTensor<2, dim, Number>,
3449 const SymmetricTensor<2, dim, Number> &original_tensor)
3450{
3451 Assert(dim <= 3, ExcNotImplemented());
3452
3453 auto heaviside_function{[](const double x) {
3454 if (std::fabs(x) < 1.0e-16)
3455 return 0.5;
3456 if (x > 0)
3457 return 1.0;
3458 else
3459 return 0.0;
3460 }};
3461
3462 std::tuple<SymmetricTensor<2, dim, Number>,
3466 positive_negative_tensors_projectors;
3467
3468 auto &[positive_part_tensor,
3469 negative_part_tensor,
3470 positive_projector,
3471 negative_projector] = positive_negative_tensors_projectors;
3472
3473 const std::array<std::pair<Number, Tensor<1, dim, Number>>, dim>
3474 eigen_system = eigenvectors(original_tensor);
3475
3476 positive_part_tensor = 0;
3477 for (unsigned int i = 0; i < dim; ++i)
3478 if (eigen_system[i].first > 0)
3479 positive_part_tensor += eigen_system[i].first *
3480 symmetrize(outer_product(eigen_system[i].second,
3481 eigen_system[i].second));
3482
3483 negative_part_tensor = 0;
3484 for (unsigned int i = 0; i < dim; ++i)
3485 if (eigen_system[i].first < 0)
3486 negative_part_tensor += eigen_system[i].first *
3487 symmetrize(outer_product(eigen_system[i].second,
3488 eigen_system[i].second));
3489
3490 std::array<SymmetricTensor<2, dim, Number>, dim> M;
3491 for (unsigned int a = 0; a < dim; ++a)
3492 M[a] =
3493 symmetrize(outer_product(eigen_system[a].second, eigen_system[a].second));
3494
3495 std::array<SymmetricTensor<4, dim, Number>, dim> Q;
3496 for (unsigned int a = 0; a < dim; ++a)
3497 Q[a] = outer_product(M[a], M[a]);
3498
3499 std::array<std::array<SymmetricTensor<4, dim, Number>, dim>, dim> G;
3500 for (unsigned int a = 0; a < dim; ++a)
3501 for (unsigned int b = 0; b < dim; ++b)
3502 for (unsigned int i = 0; i < dim; ++i)
3503 for (unsigned int j = 0; j < dim; ++j)
3504 for (unsigned int k = 0; k < dim; ++k)
3505 for (unsigned int l = 0; l < dim; ++l)
3506 G[a][b][i][j][k][l] =
3507 M[a][i][k] * M[b][j][l] + M[a][i][l] * M[b][j][k];
3508
3509 // positive P
3510 positive_projector = 0;
3511 for (unsigned int a = 0; a < dim; ++a)
3512 {
3513 double lambda_a = eigen_system[a].first;
3514 positive_projector += heaviside_function(lambda_a) * Q[a];
3515 for (unsigned int b = 0; b < dim; ++b)
3516 {
3517 if (b != a)
3518 {
3519 double lambda_b = eigen_system[b].first;
3520
3521 double v_ab = 0.0;
3522 if (std::fabs(lambda_a - lambda_b) > 1.0e-12)
3523 v_ab = (std::fmax(lambda_a, 0.0) - std::fmax(lambda_b, 0.0)) /
3524 (lambda_a - lambda_b);
3525 else
3526 v_ab = 0.5 * (heaviside_function(lambda_a) +
3527 heaviside_function(lambda_b));
3528
3529 positive_projector += 0.5 * v_ab * 0.5 * (G[a][b] + G[b][a]);
3530 }
3531 }
3532 }
3533
3534 // negative P
3535 negative_projector = 0;
3536 for (unsigned int a = 0; a < dim; ++a)
3537 {
3538 double lambda_a = eigen_system[a].first;
3539 negative_projector += heaviside_function(-lambda_a) * Q[a];
3540 for (unsigned int b = 0; b < dim; ++b)
3541 {
3542 if (b != a)
3543 {
3544 double lambda_b = eigen_system[b].first;
3545
3546 double v_ab = 0.0;
3547 if (std::fabs(lambda_a - lambda_b) > 1.0e-12)
3548 v_ab = (std::fmin(lambda_a, 0.0) - std::fmin(lambda_b, 0.0)) /
3549 (lambda_a - lambda_b);
3550 else
3551 v_ab = 0.5 * (heaviside_function(-lambda_a) +
3552 heaviside_function(-lambda_b));
3553
3554 negative_projector += 0.5 * v_ab * 0.5 * (G[a][b] + G[b][a]);
3555 }
3556 }
3557 }
3558
3559 return positive_negative_tensors_projectors;
3560}
3561
3569template <int dim, typename Number>
3573{
3575 for (unsigned int d = 0; d < dim; ++d)
3576 result[d][d] = t[d][d];
3577
3578 const Number half = internal::NumberType<Number>::value(0.5);
3579 for (unsigned int d = 0; d < dim; ++d)
3580 for (unsigned int e = d + 1; e < dim; ++e)
3581 result[d][e] = (t[d][e] + t[e][d]) * half;
3582 return result;
3583}
3584
3585
3586
3598template <int dim, typename Number>
3601 symmetrize(const Tensor<4, dim, Number> &t, const bool major_symmetry)
3602{
3604
3605 const Number half = internal::NumberType<Number>::value(0.5);
3606
3607 // minor symmetry - A_{ijkl}=A_{jikl}=A_{ijlk}=A_{jilk}
3608 for (unsigned int i = 0; i < dim; ++i)
3609 for (unsigned int j = 0; j < dim; ++j)
3610 for (unsigned int k = 0; k < dim; ++k)
3611 for (unsigned int l = 0; l < dim; ++l)
3612 {
3613 if (i != j && k == l)
3614 {
3615 // A_{ijkk}=A_{jikk}
3616 result[i][j][k][k] = (t[i][j][k][k] + t[j][i][k][k]) * half;
3617 }
3618 else if (i == j && k != l)
3619 {
3620 // A_{iikl}=A_{iilk}
3621 result[i][i][k][l] = (t[i][i][k][l] + t[i][i][l][k]) * half;
3622 }
3623 else if (i != j && k != l)
3624 {
3625 // A_{ijkl}=A_{jilk}
3626 result[i][j][k][l] = (t[i][j][k][l] + t[j][i][k][l] +
3627 t[i][j][l][k] + t[j][i][l][k]) *
3628 half * half;
3629 }
3630 else
3631 {
3632 // A_{iijj} and A_{iiii} unchanged
3633 result[i][j][k][l] = t[i][j][k][l];
3634 }
3635 }
3636
3637 // in case major symmetry is also required
3638 if (major_symmetry)
3639 {
3640 // major symmetry - A_{ijkl}=A_{klij}
3641 for (unsigned int i = 0; i < dim; ++i)
3642 for (unsigned int j = i; j < dim; ++j)
3643 for (unsigned int k = 0; k < dim; ++k)
3644 for (unsigned int l = k; l < dim; ++l)
3645 result[i][j][k][l] = (t[i][j][k][l] + t[k][l][i][j]) * half;
3646 }
3647 return result;
3648}
3649
3650
3651
3659template <int rank_, int dim, typename Number>
3662 operator*(const SymmetricTensor<rank_, dim, Number> &t, const Number &factor)
3663{
3665 tt *= factor;
3666 return tt;
3667}
3668
3669
3670
3678template <int rank_, int dim, typename Number>
3681 operator*(const Number &factor, const SymmetricTensor<rank_, dim, Number> &t)
3682{
3683 // simply forward to the other operator
3684 return t * factor;
3685}
3686
3687
3688
3713template <int rank_, int dim, typename Number, typename OtherNumber>
3715 rank_,
3716 dim,
3717 typename ProductType<Number,
3718 typename EnableIfScalar<OtherNumber>::type>::type>
3720 const OtherNumber &factor)
3721{
3722 // form the product. we have to convert the two factors into the final
3723 // type via explicit casts because, for awkward reasons, the C++
3724 // standard committee saw it fit to not define an
3725 // operator*(float,std::complex<double>)
3726 // (as well as with switched arguments and double<->float).
3727 using product_type = typename ProductType<Number, OtherNumber>::type;
3730 return tt;
3731}
3732
3733
3734
3742template <int rank_, int dim, typename Number, typename OtherNumber>
3744 rank_,
3745 dim,
3746 typename ProductType<OtherNumber,
3747 typename EnableIfScalar<Number>::type>::type>
3748operator*(const Number &factor,
3750{
3751 // simply forward to the other operator with switched arguments
3752 return (t * factor);
3753}
3754
3755
3756
3762template <int rank_, int dim, typename Number, typename OtherNumber>
3763DEAL_II_HOST_DEVICE constexpr inline SymmetricTensor<
3764 rank_,
3765 dim,
3766 typename ProductType<Number,
3767 typename EnableIfScalar<OtherNumber>::type>::type>
3769 const OtherNumber &factor)
3770{
3771 using product_type = typename ProductType<Number, OtherNumber>::type;
3774 return tt;
3775}
3776
3777
3778
3785template <int rank_, int dim>
3788 operator*(const SymmetricTensor<rank_, dim> &t, const double factor)
3789{
3791 tt *= factor;
3792 return tt;
3793}
3794
3795
3796
3803template <int rank_, int dim>
3806 operator*(const double factor, const SymmetricTensor<rank_, dim> &t)
3807{
3809 tt *= factor;
3810 return tt;
3811}
3812
3813
3814
3820template <int rank_, int dim>
3822operator/(const SymmetricTensor<rank_, dim> &t, const double factor)
3823{
3825 tt /= factor;
3826 return tt;
3827}
3828
3838template <int dim, typename Number, typename OtherNumber>
3843{
3844 return (t1 * t2);
3845}
3846
3847
3860template <int dim, typename Number, typename OtherNumber>
3865{
3867 typename ProductType<Number, OtherNumber>::type>::value(0.0);
3868 for (unsigned int i = 0; i < dim; ++i)
3869 for (unsigned int j = 0; j < dim; ++j)
3870 s += t1[i][j] * t2[i][j];
3871 return s;
3872}
3873
3874
3887template <int dim, typename Number, typename OtherNumber>
3892{
3893 return scalar_product(t2, t1);
3894}
3895
3896
3911template <typename Number, typename OtherNumber>
3912DEAL_II_HOST_DEVICE constexpr inline DEAL_II_ALWAYS_INLINE void
3917{
3918 tmp[0][0] = t[0][0][0][0] * s[0][0];
3919}
3920
3921
3922
3937template <typename Number, typename OtherNumber>
3938DEAL_II_HOST_DEVICE constexpr inline void
3943{
3944 tmp[0][0] = t[0][0][0][0] * s[0][0];
3945}
3946
3947
3948
3963template <typename Number, typename OtherNumber>
3964DEAL_II_HOST_DEVICE constexpr inline void
3969{
3970 const unsigned int dim = 2;
3971
3972 for (unsigned int i = 0; i < dim; ++i)
3973 for (unsigned int j = i; j < dim; ++j)
3974 tmp[i][j] = t[i][j][0][0] * s[0][0] + t[i][j][1][1] * s[1][1] +
3975 2 * t[i][j][0][1] * s[0][1];
3976}
3977
3978
3979
3994template <typename Number, typename OtherNumber>
3995DEAL_II_HOST_DEVICE constexpr inline void
4000{
4001 const unsigned int dim = 2;
4002
4003 for (unsigned int i = 0; i < dim; ++i)
4004 for (unsigned int j = i; j < dim; ++j)
4005 tmp[i][j] = s[0][0] * t[0][0][i][j] * +s[1][1] * t[1][1][i][j] +
4006 2 * s[0][1] * t[0][1][i][j];
4007}
4008
4009
4010
4025template <typename Number, typename OtherNumber>
4026DEAL_II_HOST_DEVICE constexpr inline void
4031{
4032 const unsigned int dim = 3;
4033
4034 for (unsigned int i = 0; i < dim; ++i)
4035 for (unsigned int j = i; j < dim; ++j)
4036 tmp[i][j] = t[i][j][0][0] * s[0][0] + t[i][j][1][1] * s[1][1] +
4037 t[i][j][2][2] * s[2][2] + 2 * t[i][j][0][1] * s[0][1] +
4038 2 * t[i][j][0][2] * s[0][2] + 2 * t[i][j][1][2] * s[1][2];
4039}
4040
4041
4042
4057template <typename Number, typename OtherNumber>
4058DEAL_II_HOST_DEVICE constexpr inline void
4063{
4064 const unsigned int dim = 3;
4065
4066 for (unsigned int i = 0; i < dim; ++i)
4067 for (unsigned int j = i; j < dim; ++j)
4068 tmp[i][j] = s[0][0] * t[0][0][i][j] + s[1][1] * t[1][1][i][j] +
4069 s[2][2] * t[2][2][i][j] + 2 * s[0][1] * t[0][1][i][j] +
4070 2 * s[0][2] * t[0][2][i][j] + 2 * s[1][2] * t[1][2][i][j];
4071}
4072
4073
4074
4081template <int dim, typename Number, typename OtherNumber>
4082DEAL_II_HOST_DEVICE constexpr Tensor<
4083 1,
4084 dim,
4087 const Tensor<1, dim, OtherNumber> &src2)
4088{
4090 for (unsigned int i = 0; i < dim; ++i)
4091 {
4092 dest[i] = src1[i][0] * src2[0];
4093 for (unsigned int j = 1; j < dim; ++j)
4094 dest[i] += src1[i][j] * src2[j];
4095 }
4096 return dest;
4097}
4098
4099
4106template <int dim, typename Number, typename OtherNumber>
4107DEAL_II_HOST_DEVICE constexpr Tensor<
4108 1,
4109 dim,
4113{
4114 // this is easy for symmetric tensors:
4115 return src2 * src1;
4116}
4117
4118
4119
4139template <int rank_1,
4140 int rank_2,
4141 int dim,
4142 typename Number,
4143 typename OtherNumber>
4145 typename Tensor<rank_1 + rank_2 - 2,
4146 dim,
4147 typename ProductType<Number, OtherNumber>::type>::tensor_type
4153
4154
4155
4175template <int rank_1,
4176 int rank_2,
4177 int dim,
4178 typename Number,
4179 typename OtherNumber>
4181 typename Tensor<rank_1 + rank_2 - 2,
4182 dim,
4183 typename ProductType<Number, OtherNumber>::type>::tensor_type
4186{
4187 return Tensor<rank_1, dim, Number>(src1) * src2;
4188}
4189
4190
4191
4201template <int dim, typename Number>
4202inline std::ostream &
4203operator<<(std::ostream &out, const SymmetricTensor<2, dim, Number> &t)
4204{
4205 // make our lives a bit simpler by outputting
4206 // the tensor through the operator for the
4207 // general Tensor class
4209
4210 for (unsigned int i = 0; i < dim; ++i)
4211 for (unsigned int j = 0; j < dim; ++j)
4212 tt[i][j] = t[i][j];
4213
4214 return out << tt;
4215}
4216
4217
4218
4228template <int dim, typename Number>
4229inline std::ostream &
4230operator<<(std::ostream &out, const SymmetricTensor<4, dim, Number> &t)
4231{
4232 // make our lives a bit simpler by outputting
4233 // the tensor through the operator for the
4234 // general Tensor class
4236
4237 for (unsigned int i = 0; i < dim; ++i)
4238 for (unsigned int j = 0; j < dim; ++j)
4239 for (unsigned int k = 0; k < dim; ++k)
4240 for (unsigned int l = 0; l < dim; ++l)
4241 tt[i][j][k][l] = t[i][j][k][l];
4242
4243 return out << tt;
4244}
4245
4246
4248
4249#endif
*  *  Point< dim > operator()(const Point< dim > &p) const * 
constexpr SymmetricTensor< 2, dim, Number > deviator(const SymmetricTensor< 2, dim, Number > &)
constexpr bool operator==(const SymmetricTensor &) const
constexpr Number first_invariant(const SymmetricTensor< 2, dim, Number > &t)
constexpr void double_contract(SymmetricTensor< 2, 2, typename ProductType< Number, OtherNumber >::type > &tmp, const SymmetricTensor< 2, 2, Number > &s, const SymmetricTensor< 4, 2, OtherNumber > &t)
constexpr void double_contract(SymmetricTensor< 2, 1, typename ProductType< Number, OtherNumber >::type > &tmp, const SymmetricTensor< 4, 1, Number > &t, const SymmetricTensor< 2, 1, OtherNumber > &s)
constexpr ProductType< Number, OtherNumber >::type scalar_product(const Tensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, OtherNumber > &t2)
std::pair< SymmetricTensor< 2, dim, Number >, SymmetricTensor< 2, dim, Number > > positive_negative_split(const SymmetricTensor< 2, dim, Number > &original_tensor)
constexpr SymmetricTensor< 4, dim, Number > invert(const SymmetricTensor< 4, dim, Number > &t)
constexpr SymmetricTensor< rank_, dim > operator*(const SymmetricTensor< rank_, dim > &t, const double factor)
constexpr Tensor< 1, dim, typename ProductType< Number, OtherNumber >::type > operator*(const SymmetricTensor< 2, dim, Number > &src1, const Tensor< 1, dim, OtherNumber > &src2)
constexpr SymmetricTensor< 2, dim, Number > symmetrize(const Tensor< 2, dim, Number > &t)
constexpr SymmetricTensor(const Number(&array)[n_independent_components])
constexpr Tensor< rank_, dim, typename ProductType< Number, OtherNumber >::type > operator-(const SymmetricTensor< rank_, dim, Number > &left, const Tensor< rank_, dim, OtherNumber > &right)
constexpr void double_contract(SymmetricTensor< 2, 2, typename ProductType< Number, OtherNumber >::type > &tmp, const SymmetricTensor< 4, 2, Number > &t, const SymmetricTensor< 2, 2, OtherNumber > &s)
static constexpr unsigned int component_to_unrolled_index(const TableIndices< rank_ > &indices)
constexpr Number determinant(const SymmetricTensor< 2, dim, Number > &)
constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &t)
constexpr Tensor< 1, dim, typename ProductType< Number, OtherNumber >::type > operator*(const Tensor< 1, dim, Number > &src1, const SymmetricTensor< 2, dim, OtherNumber > &src2)
std::array< Number, 2 > eigenvalues(const SymmetricTensor< 2, 2, Number > &T)
void serialize(Archive &ar, const unsigned int version)
constexpr Tensor< rank_, dim, typename ProductType< Number, OtherNumber >::type > operator-(const Tensor< rank_, dim, Number > &left, const SymmetricTensor< rank_, dim, OtherNumber > &right)
constexpr Number trace(const SymmetricTensor< 2, dim2, Number > &)
std::array< Number, 1 > eigenvalues(const SymmetricTensor< 2, 1, Number > &T)
std::tuple< SymmetricTensor< 2, dim, Number >, SymmetricTensor< 2, dim, Number >, SymmetricTensor< 4, dim, Number >, SymmetricTensor< 4, dim, Number > > positive_negative_projectors(const SymmetricTensor< 2, dim, Number > &original_tensor)
std::array< std::pair< Number, Tensor< 1, dim, Number > >, dim > eigenvectors(const SymmetricTensor< 2, dim, Number > &T, const SymmetricTensorEigenvectorMethod method=SymmetricTensorEigenvectorMethod::ql_implicit_shifts)
constexpr internal::SymmetricTensorAccessors::Accessor< rank_, dim, false, rank_ - 1, Number > operator[](const unsigned int row)
typename base_tensor_descriptor::base_tensor_type base_tensor_type
constexpr const Number & operator()(const TableIndices< rank_ > &indices) const
constexpr SymmetricTensor< 4, dim, Number > deviator_tensor()
constexpr bool operator!=(const SymmetricTensor &) const
constexpr Number & operator[](const TableIndices< rank_ > &indices)
constexpr void double_contract(SymmetricTensor< 2, 1, typename ProductType< Number, OtherNumber >::type > &tmp, const SymmetricTensor< 2, 1, Number > &s, const SymmetricTensor< 4, 1, OtherNumber > &t)
constexpr SymmetricTensor< rank_, dim, typename ProductType< OtherNumber, typename EnableIfScalar< Number >::type >::type > operator*(const Number &factor, const SymmetricTensor< rank_, dim, OtherNumber > &t)
constexpr SymmetricTensor< rank_, dim, typename ProductType< Number, typename EnableIfScalar< OtherNumber >::type >::type > operator/(const SymmetricTensor< rank_, dim, Number > &t, const OtherNumber &factor)
static constexpr std::size_t memory_consumption()
constexpr ProductType< Number, OtherNumber >::type scalar_product(const SymmetricTensor< 2, dim, Number > &t1, const Tensor< 2, dim, OtherNumber > &t2)
constexpr Tensor< rank_, dim, typename ProductType< Number, OtherNumber >::type > operator+(const SymmetricTensor< rank_, dim, Number > &left, const Tensor< rank_, dim, OtherNumber > &right)
base_tensor_type data
constexpr const Number & operator[](const TableIndices< rank_ > &indices) const
constexpr SymmetricTensor & operator=(const Number &d)
SymmetricTensor(const Tensor< 2, dim, OtherNumber > &t)
static constexpr TableIndices< rank_ > unrolled_to_component_indices(const unsigned int i)
constexpr SymmetricTensor< 4, dim, Number > outer_product(const SymmetricTensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, Number > &t2)
constexpr Number & access_raw_entry(const unsigned int unrolled_index)
constexpr numbers::NumberTraits< Number >::real_type norm() const
constexpr SymmetricTensor operator-() const
constexpr internal::SymmetricTensorAccessors::double_contraction_result< rank_, 2, dim, Number, OtherNumber >::type operator*(const SymmetricTensor< 2, dim, OtherNumber > &s) const
constexpr SymmetricTensor()=default
constexpr Number second_invariant(const SymmetricTensor< 2, 1, Number > &)
constexpr Number third_invariant(const SymmetricTensor< 2, dim, Number > &t)
constexpr SymmetricTensor(const SymmetricTensor< rank_, dim, OtherNumber > &initializer)
constexpr SymmetricTensor & operator-=(const SymmetricTensor< rank_, dim, OtherNumber > &)
constexpr SymmetricTensor< rank_, dim, Number > operator*(const Number &factor, const SymmetricTensor< rank_, dim, Number > &t)
constexpr Tensor< rank_1+rank_2-2, dim, typenameProductType< Number, OtherNumber >::type >::tensor_type operator*(const Tensor< rank_1, dim, Number > &src1, const SymmetricTensor< rank_2, dim, OtherNumber > &src2)
constexpr SymmetricTensor< rank_, dim, typename ProductType< Number, typename EnableIfScalar< OtherNumber >::type >::type > operator*(const SymmetricTensor< rank_, dim, Number > &t, const OtherNumber &factor)
constexpr Tensor< rank_1+rank_2-2, dim, typenameProductType< Number, OtherNumber >::type >::tensor_type operator*(const SymmetricTensor< rank_1, dim, Number > &src1, const Tensor< rank_2, dim, OtherNumber > &src2)
constexpr ProductType< Number, OtherNumber >::type scalar_product(const SymmetricTensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, OtherNumber > &t2)
constexpr SymmetricTensor< 4, dim, Number > identity_tensor()
constexpr void double_contract(SymmetricTensor< 2, 3, typename ProductType< Number, OtherNumber >::type > &tmp, const SymmetricTensor< 4, 3, Number > &t, const SymmetricTensor< 2, 3, OtherNumber > &s)
constexpr SymmetricTensor< rank_, dim, typename ProductType< Number, OtherNumber >::type > operator-(const SymmetricTensor< rank_, dim, Number > &left, const SymmetricTensor< rank_, dim, OtherNumber > &right)
constexpr SymmetricTensor< rank_, dim, typename ProductType< Number, OtherNumber >::type > operator+(const SymmetricTensor< rank_, dim, Number > &left, const SymmetricTensor< rank_, dim, OtherNumber > &right)
constexpr Number second_invariant(const SymmetricTensor< 2, 3, Number > &t)
constexpr SymmetricTensor< rank_, dim, Number > operator*(const SymmetricTensor< rank_, dim, Number > &t, const Number &factor)
constexpr const Number & access_raw_entry(const unsigned int unrolled_index) const
constexpr SymmetricTensor & operator=(const SymmetricTensor< rank_, dim, OtherNumber > &rhs)
constexpr SymmetricTensor< rank_, dim, Number > transpose(const SymmetricTensor< rank_, dim, Number > &t)
constexpr SymmetricTensor< 4, dim, Number > symmetrize(const Tensor< 4, dim, Number > &t, const bool major_symmetry)
constexpr SymmetricTensor< rank_, dim > operator*(const double factor, const SymmetricTensor< rank_, dim > &t)
constexpr SymmetricTensor & operator/=(const OtherNumber &factor)
constexpr void clear()
constexpr Tensor< rank_, dim, typename ProductType< Number, OtherNumber >::type > operator+(const Tensor< rank_, dim, Number > &left, const SymmetricTensor< rank_, dim, OtherNumber > &right)
constexpr Number second_invariant(const SymmetricTensor< 2, 2, Number > &t)
constexpr SymmetricTensor & operator+=(const SymmetricTensor< rank_, dim, OtherNumber > &)
constexpr SymmetricTensor< 2, dim, Number > unit_symmetric_tensor()
constexpr internal::SymmetricTensorAccessors::double_contraction_result< rank_, 4, dim, Number, OtherNumber >::type operator*(const SymmetricTensor< 4, dim, OtherNumber > &s) const
constexpr SymmetricTensor< rank_, dim > operator/(const SymmetricTensor< rank_, dim > &t, const double factor)
std::array< Number, 3 > eigenvalues(const SymmetricTensor< 2, 3, Number > &T)
constexpr internal::SymmetricTensorAccessors::Accessor< rank_, dim, true, rank_ - 1, Number > operator[](const unsigned int row) const
constexpr void double_contract(SymmetricTensor< 2, 3, typename ProductType< Number, OtherNumber >::type > &tmp, const SymmetricTensor< 2, 3, Number > &s, const SymmetricTensor< 4, 3, OtherNumber > &t)
constexpr SymmetricTensor & operator*=(const OtherNumber &factor)
constexpr Number & operator()(const TableIndices< rank_ > &indices)
typename AccessorTypes< rank, dim, constness, Number >::reference reference
constexpr Accessor(tensor_type &tensor, const TableIndices< rank > &previous_indices)
typename AccessorTypes< rank, dim, constness, Number >::tensor_type tensor_type
typename AccessorTypes< rank, dim, constness, Number >::tensor_type tensor_type
constexpr Accessor< rank, dim, constness, P - 1, Number > operator[](const unsigned int i)
constexpr Accessor(tensor_type &tensor, const TableIndices< rank > &previous_indices)
typename AccessorTypes< rank, dim, constness, Number >::reference reference
constexpr Accessor< rank, dim, constness, P - 1, Number > operator[](const unsigned int i) const
DEAL_II_HOST constexpr Accessor(const Accessor &)=default
#define DEAL_II_ALWAYS_INLINE
Definition config.h:166
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_HOST_DEVICE
Definition config.h:171
#define DEAL_II_CONSTEXPR
Definition config.h:282
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_HOST
Definition config.h:182
std::ostream & operator<<(std::ostream &out, const DerivativeForm< order, dim, spacedim, Number > &df)
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
static ::ExceptionBase & ExcMessage(std::string arg1)
std::vector< index_type > data
Definition mpi.cc:734
constexpr char N
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
Tensor< 2, dim, Number > l(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
*  *  *  RotationFunction< dim, Number >::RotationFunction Number(dim)
*  *  *  *  std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters   const
T sum(const T &t, const MPI_Comm mpi_communicator)
constexpr TableIndices< 2 > merge(const TableIndices< 2 > &previous_indices, const unsigned int new_index, const unsigned int position)
void tridiagonalize(const ::SymmetricTensor< 2, dim, Number > &A, ::Tensor< 2, dim, Number > &Q, std::array< Number, dim > &d, std::array< Number, dim - 1 > &e)
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
constexpr bool value_is_zero(const Number &value)
Definition numbers.h:874
STL namespace.
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
typename internal::ProductTypeImpl< std::decay_t< T >, std::decay_t< U > >::type type
static constexpr const T & value(const T &t)
Definition numbers.h:662
typename ProductType< Number, OtherNumber >::type value_type
std::pair< Number, Tensor< 1, dim, Number > > EigValsVecs
bool operator()(const EigValsVecs &lhs, const EigValsVecs &rhs)
static real_type abs(const number &x)
Definition numbers.h:560
constexpr SymmetricTensor< 2, dim, Number > deviator(const SymmetricTensor< 2, dim, Number > &)
constexpr SymmetricTensor< 2, dim, Number > symmetrize(const Tensor< 2, dim, Number > &t)
constexpr Number determinant(const SymmetricTensor< 2, dim, Number > &)
constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)
constexpr Number trace(const SymmetricTensor< 2, dim2, Number > &)
std::array< std::pair< Number, Tensor< 1, dim, Number > >, dim > eigenvectors(const SymmetricTensor< 2, dim, Number > &T, const SymmetricTensorEigenvectorMethod method=SymmetricTensorEigenvectorMethod::ql_implicit_shifts)
constexpr SymmetricTensor< 4, dim, Number > deviator_tensor()
constexpr SymmetricTensor< 4, dim, Number > outer_product(const SymmetricTensor< 2, dim, Number > &t1, const SymmetricTensor< 2, dim, Number > &t2)
constexpr SymmetricTensor< 4, dim, Number > identity_tensor()
SymmetricTensorEigenvectorMethod
constexpr SymmetricTensor< 2, dim, Number > unit_symmetric_tensor()