libMesh
Loading...
Searching...
No Matches
kokkos_tensor_ops.h
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18#ifndef LIBMESH_KOKKOS_TENSOR_OPS_H
19#define LIBMESH_KOKKOS_TENSOR_OPS_H
20
21// libMesh Kokkos generic tensor operations.
22//
23// These free functions build tensor algebra on top of the primitive
24// access/materialization layer in kokkos_linalg_base.h. They are written
25// against tensor-like and vector-like inputs so both libMesh owning types and
26// storage-backed refs can participate in the same math.
27//
28// The header is organized like kokkos_vector_ops.h:
29//
30// - construction/materialization helpers (tensor_identity, copy_tensor),
31// - detail:: componentwise kernels shared by everything below,
32// - libMesh-like wrappers (contract, transpose, det, inverse, ...), and
33// - the out-of-line tensor_ref definitions and the operator overloads.
34//
35// As in the vector case, the operator overloads are constrained (via
36// is_tensor_ref_v / is_vector_ref_v) to require at least one
37// storage-backed ref operand, so expressions between owning types keep
38// using the operators those types already define.
39//
40// Functions taking an optional ResultTensor/ResultVector template
41// parameter pick a default owning result type - the input's semantic
42// type for copy_tensor/transpose/inverse, a plain TypeTensor or
43// TypeVector of the input's component type for outer_product/row/
44// column - but a caller can request a different owning type
45// explicitly, e.g. inverse<TensorValue<Real>>(T).
46
47#include "libmesh/kokkos_linalg_base.h"
48#include "libmesh/kokkos_vector_ops.h"
49
50#include "libmesh/tensor_tools.h"
51
52#include <cmath>
53
54namespace libMesh::Kokkos
55{
56
57// Construction and materialization
58
63template <typename ResultTensor>
64LIBMESH_DEVICE_INLINE
65ResultTensor tensor_identity(const unsigned int dim = LIBMESH_DIM)
66{
67 ResultTensor out;
68 out.zero();
69
70 for (unsigned int i = 0; i < dim; ++i)
71 out(i, i) = 1;
72
73 return out;
74}
75
82template <typename ResultTensor = void, typename TensorLike>
83LIBMESH_DEVICE_INLINE
84auto copy_tensor(const TensorLike & T_in)
85 -> std::conditional_t<std::is_void<ResultTensor>::value,
87 ResultTensor>
88{
89 using output_type = std::conditional_t<std::is_void<ResultTensor>::value,
91 ResultTensor>;
92 return materialize_tensor<output_type>(T_in);
93}
94
95namespace detail
96{
97
98// These helpers are shared by the public functions and ref operators so
99// Kokkos-backed refs use direct component access without extra materialization.
100
106template <typename TensorLike>
107LIBMESH_DEVICE_INLINE
108typename TensorLike::value_type
109leading_determinant(const TensorLike & T_in, const unsigned int dim = LIBMESH_DIM)
110{
111 static_assert(is_tensor_like_v<TensorLike>,
112 "detail::leading_determinant() requires a tensor-like input");
113
114 if (dim == 0)
115 return 1;
116
117 if (dim == 1)
118 return T_in(0, 0);
119
120 if (dim == 2)
121 return T_in(0, 0) * T_in(1, 1) -
122 T_in(0, 1) * T_in(1, 0);
123
124#if LIBMESH_DIM > 2
125 const auto a00 = T_in(0, 0);
126 const auto a01 = T_in(0, 1);
127 const auto a02 = T_in(0, 2);
128 const auto a10 = T_in(1, 0);
129 const auto a11 = T_in(1, 1);
130 const auto a12 = T_in(1, 2);
131 const auto a20 = T_in(2, 0);
132 const auto a21 = T_in(2, 1);
133 const auto a22 = T_in(2, 2);
134
135 return a00 * (a11 * a22 - a12 * a21) -
136 a01 * (a10 * a22 - a12 * a20) +
137 a02 * (a10 * a21 - a11 * a20);
138#else
139 libmesh_ignore(T_in);
140 return 0;
141#endif
142}
143
149template <typename ResultTensor, typename LeftVector, typename RightVector>
150LIBMESH_DEVICE_INLINE
151ResultTensor outer_product(const LeftVector & left, const RightVector & right)
152{
153 ResultTensor out;
154 out.zero();
155
156 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
157 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
158 out(row, col) = left(row) * libmesh_conj(right(col));
159
160 return out;
161}
162
168template <typename ResultTensor, typename TensorLike>
169LIBMESH_DEVICE_INLINE
170ResultTensor inverse(const TensorLike & T_in, const unsigned int dim = LIBMESH_DIM)
171{
172 static_assert(is_tensor_like_v<TensorLike>, "detail::inverse() requires a tensor-like input");
173
174 ResultTensor out;
175 out.zero();
176
177 if (dim == 0)
178 return out;
179
180 if (dim == 1)
181 {
182 using value_type = typename ResultTensor::value_type;
183 out(0, 0) = value_type(1) / T_in(0, 0);
184 return out;
185 }
186
187 const auto det = leading_determinant(T_in, dim);
188
189 if (dim == 2)
190 {
191 out(0, 0) = T_in(1, 1) / det;
192 out(0, 1) = -T_in(0, 1) / det;
193 out(1, 0) = -T_in(1, 0) / det;
194 out(1, 1) = T_in(0, 0) / det;
195 return out;
196 }
197
198#if LIBMESH_DIM > 2
199 const auto a00 = T_in(0, 0);
200 const auto a01 = T_in(0, 1);
201 const auto a02 = T_in(0, 2);
202 const auto a10 = T_in(1, 0);
203 const auto a11 = T_in(1, 1);
204 const auto a12 = T_in(1, 2);
205 const auto a20 = T_in(2, 0);
206 const auto a21 = T_in(2, 1);
207 const auto a22 = T_in(2, 2);
208
209 out(0, 0) = (a11 * a22 - a12 * a21) / det;
210 out(0, 1) = (a02 * a21 - a01 * a22) / det;
211 out(0, 2) = (a01 * a12 - a02 * a11) / det;
212 out(1, 0) = (a12 * a20 - a10 * a22) / det;
213 out(1, 1) = (a00 * a22 - a02 * a20) / det;
214 out(1, 2) = (a02 * a10 - a00 * a12) / det;
215 out(2, 0) = (a10 * a21 - a11 * a20) / det;
216 out(2, 1) = (a01 * a20 - a00 * a21) / det;
217 out(2, 2) = (a00 * a11 - a01 * a10) / det;
218#else
219 libmesh_ignore(T_in);
220#endif
221
222 return out;
223}
224
228template <typename ResultTensor, typename TensorLike>
229LIBMESH_DEVICE_INLINE
230ResultTensor transpose(const TensorLike & T_in)
231{
232 ResultTensor out;
233 out.zero();
234
235 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
236 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
237 out(row, col) = T_in(col, row);
238
239 return out;
240}
241
242// Tensor/tensor product operators delegate here for the same direct-access path.
243
248template <typename ResultTensor, typename LeftTensor, typename RightTensor>
249LIBMESH_DEVICE_INLINE
250ResultTensor multiply_tensors(const LeftTensor & left, const RightTensor & right)
251{
252 ResultTensor out;
253 out.zero();
254
255 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
256 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
257 {
258 auto value = left(row, 0) * right(0, col);
259 for (unsigned int k = 1; k < LIBMESH_DIM; ++k)
260 value += left(row, k) * right(k, col);
261 out(row, col) = value;
262 }
263
264 return out;
265}
266
270template <typename ResultVector, typename TensorLike>
271LIBMESH_DEVICE_INLINE
272ResultVector row(const TensorLike & T_in, const unsigned int row_index)
273{
274 ResultVector out;
275 out.zero();
276
277 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
278 out(col) = T_in(row_index, col);
279
280 return out;
281}
282
287template <typename ResultVector, typename TensorLike>
288LIBMESH_DEVICE_INLINE
289ResultVector column(const TensorLike & T_in, const unsigned int col_index)
290{
291 ResultVector out;
292 out.zero();
293
294 for (unsigned int row_index = 0; row_index < LIBMESH_DIM; ++row_index)
295 out(row_index) = T_in(row_index, col_index);
296
297 return out;
298}
299
300// Tensor/vector and vector/tensor product operators keep the direct-access path too.
301
306template <typename ResultVector, typename TensorLike, typename VectorLike>
307LIBMESH_DEVICE_INLINE
308ResultVector multiply_tensor_vector(const TensorLike & T_in, const VectorLike & v)
309{
310 ResultVector out;
311 out.zero();
312
313 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
314 {
315 auto value = T_in(row, 0) * v(0);
316 for (unsigned int col = 1; col < LIBMESH_DIM; ++col)
317 value += T_in(row, col) * v(col);
318 out(row) = value;
319 }
320
321 return out;
322}
323
328template <typename ResultVector, typename VectorLike, typename TensorLike>
329LIBMESH_DEVICE_INLINE
330ResultVector multiply_vector_tensor(const VectorLike & v, const TensorLike & T_in)
331{
332 ResultVector out;
333 out.zero();
334
335 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
336 {
337 auto value = v(0) * T_in(0, col);
338 for (unsigned int row = 1; row < LIBMESH_DIM; ++row)
339 value += v(row) * T_in(row, col);
340 out(col) = value;
341 }
342
343 return out;
344}
345
349template <typename LeftTensor, typename RightTensor>
350LIBMESH_DEVICE_INLINE
351void assign_tensor_components(LeftTensor & left, const RightTensor & right)
352{
353 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
354 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
355 left(row, col) = right(row, col);
356}
357
361template <typename TensorLike, typename Scalar>
362LIBMESH_DEVICE_INLINE
363void fill_tensor_components(TensorLike & T_in, const Scalar & value)
364{
365 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
366 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
367 T_in(row, col) = value;
368}
369
373template <typename LeftTensor, typename RightTensor, typename Scalar>
374LIBMESH_DEVICE_INLINE
375void update_tensor_components(LeftTensor & left, const RightTensor & right, const Scalar & factor)
376{
377 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
378 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
379 left(row, col) = left(row, col) + factor * right(row, col);
380}
381
386template <typename OutputTensor, typename InputTensor, typename TransformOp>
387LIBMESH_DEVICE_INLINE
388void transform_tensor_components(OutputTensor & out, const InputTensor & in, const TransformOp & op)
389{
390 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
391 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
392 out(row, col) = op(in(row, col));
393}
394
398template <typename ResultTensor, typename TensorLike, typename TransformOp>
399LIBMESH_DEVICE_INLINE
400ResultTensor transformed_tensor(const TensorLike & T_in, const TransformOp & op)
401{
402 ResultTensor out;
403 out.zero();
405 return out;
406}
407
412template <typename LeftTensor, typename RightTensor>
413LIBMESH_DEVICE_INLINE
414bool tensor_equal_impl(const LeftTensor & left, const RightTensor & right)
415{
416 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
417 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
418 if (left(row, col) != right(row, col))
419 return false;
420
421 return true;
422}
423
424// Tensor reductions
425
430template <typename LeftTensor, typename RightTensor>
431LIBMESH_DEVICE_INLINE
432auto tensor_contract(const LeftTensor & left, const RightTensor & right)
433{
434 static_assert(is_tensor_like_v<LeftTensor>, "tensor_contract() requires a tensor-like left input");
435 static_assert(is_tensor_like_v<RightTensor>, "tensor_contract() requires a tensor-like right input");
436
437 using sum_type =
438 detail::remove_cvref_t<decltype(left(0, 0) * right(0, 0))>;
439
440 sum_type sum = sum_type(0);
441 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
442 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
443 sum += left(row, col) * right(row, col);
444
445 return sum;
446}
447
448
452template <typename TensorLike>
453LIBMESH_DEVICE_INLINE
454auto tensor_trace(const TensorLike & T_in)
455{
456 static_assert(is_tensor_like_v<TensorLike>, "tensor_trace() requires a tensor-like input");
457
458 using trace_type = detail::remove_cvref_t<decltype(T_in(0, 0))>;
459 trace_type sum = trace_type(0);
460 for (unsigned int i = 0; i < LIBMESH_DIM; ++i)
461 sum += T_in(i, i);
462
463 return sum;
464}
465
466} // namespace detail
467
468// libMesh-like convenience wrappers
469
474template <typename LeftTensor,
475 typename RightTensor,
476 typename std::enable_if<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor>,
477 int>::type = 0>
478LIBMESH_DEVICE_INLINE
479auto contract(const LeftTensor & left, const RightTensor & right)
480{
481 return detail::tensor_contract(left, right);
482}
483
484
490template <typename ResultTensor = void, typename LeftVector, typename RightVector>
491LIBMESH_DEVICE_INLINE
492auto outer_product(const LeftVector & left, const RightVector & right)
493 -> std::enable_if_t<is_vector_like_v<LeftVector> && is_vector_like_v<RightVector>,
494 std::conditional_t<std::is_void<ResultTensor>::value,
496 ResultTensor>>
497{
498 using output_type = std::conditional_t<std::is_void<ResultTensor>::value,
500 ResultTensor>;
501 return detail::outer_product<output_type>(left, right);
502}
503
508template <typename ResultTensor = void, typename TensorLike>
509LIBMESH_DEVICE_INLINE
510auto transpose(const TensorLike & T_in)
511 -> std::enable_if_t<is_tensor_like_v<TensorLike>,
512 std::conditional_t<std::is_void<ResultTensor>::value,
514 ResultTensor>>
515{
516 using output_type = std::conditional_t<std::is_void<ResultTensor>::value,
518 ResultTensor>;
519 return detail::transpose<output_type>(T_in);
520}
521
527template <typename TensorLike>
528LIBMESH_DEVICE_INLINE
529auto det(const TensorLike & T_in)
530 -> std::enable_if_t<is_tensor_like_v<TensorLike>, decltype(T_in.det())>
531{
532 return T_in.det();
533}
534
539template <typename ResultTensor = void, typename TensorLike>
540LIBMESH_DEVICE_INLINE
541auto inverse(const TensorLike & T_in, const unsigned int dim = LIBMESH_DIM)
542 -> std::enable_if_t<is_tensor_like_v<TensorLike>,
543 std::conditional_t<std::is_void<ResultTensor>::value,
545 ResultTensor>>
546{
547 using output_type = std::conditional_t<std::is_void<ResultTensor>::value,
549 ResultTensor>;
550 return detail::inverse<output_type>(T_in, dim);
551}
552
558template <typename ResultVector = void, typename TensorLike>
559LIBMESH_DEVICE_INLINE
560auto row(const TensorLike & T_in, const unsigned int i)
561 -> std::enable_if_t<is_tensor_like_v<TensorLike>,
562 std::conditional_t<std::is_void<ResultVector>::value,
564 ResultVector>>
565{
566 using output_type = std::conditional_t<std::is_void<ResultVector>::value,
568 ResultVector>;
569 return detail::row<output_type>(T_in, i);
570}
571
576template <typename ResultVector = void, typename TensorLike>
577LIBMESH_DEVICE_INLINE
578auto column(const TensorLike & T_in, const unsigned int i)
579 -> std::enable_if_t<is_tensor_like_v<TensorLike>,
580 std::conditional_t<std::is_void<ResultVector>::value,
582 ResultVector>>
583{
584 using output_type = std::conditional_t<std::is_void<ResultVector>::value,
586 ResultVector>;
587 return detail::column<output_type>(T_in, i);
588}
589
590// Forward declarations of the compound assignment operators, which the
591// tensor_ref member definitions below delegate to.
592
593template <typename LeftTensor, typename RightTensor>
594LIBMESH_DEVICE_INLINE
595auto operator+=(LeftTensor & left, const RightTensor & right)
596 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
597 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
598 LeftTensor &>;
599
600template <typename LeftTensor, typename RightTensor>
601LIBMESH_DEVICE_INLINE
602auto operator-=(LeftTensor & left, const RightTensor & right)
603 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
604 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
605 LeftTensor &>;
606
607// Out-of-line tensor_ref member definitions. Several delegate to the
608// generic operations above or the operators below, so the whole group
609// lives here rather than in kokkos_linalg_base.h; see the class
610// definition for their documentation.
611
612template <typename ViewType>
613template <typename RightTensor>
614LIBMESH_DEVICE_INLINE
615void tensor_ref<ViewType>::assign(const RightTensor & right)
616{
618}
619
620template <typename ViewType>
621template <typename RightTensor>
622LIBMESH_DEVICE_INLINE
623void tensor_ref<ViewType>::add(const RightTensor & right)
624{
625 libMesh::Kokkos::operator+=(*this, right);
626}
627
628template <typename ViewType>
629template <typename RightTensor>
630LIBMESH_DEVICE_INLINE
631void tensor_ref<ViewType>::add_scaled(const RightTensor & right, const value_type & factor)
632{
633 detail::update_tensor_components(*this, right, factor);
634}
635
636template <typename ViewType>
637template <typename RightTensor>
638LIBMESH_DEVICE_INLINE
639void tensor_ref<ViewType>::subtract(const RightTensor & right)
640{
641 libMesh::Kokkos::operator-=(*this, right);
642}
643
644template <typename ViewType>
645template <typename RightTensor>
646LIBMESH_DEVICE_INLINE
647void tensor_ref<ViewType>::subtract_scaled(const RightTensor & right, const value_type & factor)
648{
649 detail::update_tensor_components(*this, right, -factor);
650}
651
652template <typename ViewType>
653LIBMESH_DEVICE_INLINE
658
659template <typename ViewType>
660template <typename RightTensor>
661LIBMESH_DEVICE_INLINE
662auto tensor_ref<ViewType>::contract(const RightTensor & right) const
663{
664 return libMesh::Kokkos::contract(*this, right);
665}
666
667template <typename ViewType>
668LIBMESH_DEVICE_INLINE
670{
671 using std::sqrt;
672 return sqrt(this->norm_sq());
673}
674
675template <typename ViewType>
676LIBMESH_DEVICE_INLINE
678{
679 // TensorTools::norm_sq handles complex-valued components correctly.
680 using norm_type = detail::remove_cvref_t<decltype(libMesh::TensorTools::norm_sq((*this)(0, 0)))>;
681
682 norm_type sum = norm_type(0);
683 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
684 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
685 sum += libMesh::TensorTools::norm_sq((*this)(row, col));
686
687 return sum;
688}
689
690template <typename ViewType>
691LIBMESH_DEVICE_INLINE
693{
694 for (unsigned int row = 0; row < LIBMESH_DIM; ++row)
695 for (unsigned int col = 0; col < LIBMESH_DIM; ++col)
696 if ((*this)(row, col) != value_type(0))
697 return false;
698
699 return true;
700}
701
702template <typename ViewType>
703LIBMESH_DEVICE_INLINE
705{
706 return libMesh::Kokkos::transpose(*this);
707}
708
709template <typename ViewType>
710LIBMESH_DEVICE_INLINE
711auto tensor_ref<ViewType>::det(const unsigned int dim) const
712{
713 return detail::leading_determinant(*this, dim);
714}
715
716template <typename ViewType>
717LIBMESH_DEVICE_INLINE
719{
720 return detail::tensor_trace(*this);
721}
722
723template <typename ViewType>
724LIBMESH_DEVICE_INLINE
725auto tensor_ref<ViewType>::inverse(const unsigned int dim) const
726{
727 return libMesh::Kokkos::inverse(*this, dim);
728}
729
730template <typename ViewType>
731template <typename VectorLike, typename ResultVector>
732LIBMESH_DEVICE_INLINE
733void tensor_ref<ViewType>::solve(const VectorLike & b, ResultVector & x) const
734{
735 const auto solution =
736 detail::multiply_tensor_vector<vector_semantic_type_t<ResultVector>>(this->inverse(), b);
737 for (unsigned int component = 0; component < LIBMESH_DIM; ++component)
738 x(component) = solution(component);
739}
740
741template <typename ViewType>
742LIBMESH_DEVICE_INLINE
743auto tensor_ref<ViewType>::row(const unsigned int i) const
744{
745 return libMesh::Kokkos::row(*this, i);
746}
747
748template <typename ViewType>
749LIBMESH_DEVICE_INLINE
750auto tensor_ref<ViewType>::column(const unsigned int i) const
751{
752 return libMesh::Kokkos::column(*this, i);
753}
754
755template <typename ViewType>
756template <typename VectorLike>
757LIBMESH_DEVICE_INLINE
758auto tensor_ref<ViewType>::left_multiply(const VectorLike & v) const
759{
760 return v * *this;
761}
762
763// Operator-compatible wrappers for storage-backed refs and mixed ref/owning math.
764//
765// Every overload requires at least one tensor_ref or vector_ref
766// operand, so these never compete with the operators the owning types
767// define for themselves. Tensor- and vector-valued results are
768// returned as owning values; compound assignments update the left
769// operand in place, writing through to storage when it is a ref.
770
775template <typename TensorLike>
776LIBMESH_DEVICE_INLINE
777auto operator-(const TensorLike & T_in)
778 -> std::enable_if_t<is_tensor_like_v<TensorLike> && is_tensor_ref_v<TensorLike>,
780{
781 return detail::transformed_tensor<tensor_semantic_type_t<TensorLike>>(
782 T_in,
784}
785
789template <typename LeftTensor, typename RightTensor>
790LIBMESH_DEVICE_INLINE
791auto operator+(const LeftTensor & left, const RightTensor & right)
792 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
793 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
795{
796 auto out = copy_tensor<tensor_semantic_type_t<LeftTensor>>(left);
797 out += right;
798 return out;
799}
800
805template <typename LeftTensor, typename RightTensor>
806LIBMESH_DEVICE_INLINE
807auto operator-(const LeftTensor & left, const RightTensor & right)
808 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
809 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
811{
812 auto out = copy_tensor<tensor_semantic_type_t<LeftTensor>>(left);
813 out -= right;
814 return out;
815}
816
821template <typename Scalar,
822 typename TensorLike,
823 typename std::enable_if<!is_vector_like_v<Scalar> && !is_tensor_like_v<Scalar> &&
824 is_tensor_like_v<TensorLike> && is_tensor_ref_v<TensorLike>,
825 int>::type = 0>
826LIBMESH_DEVICE_INLINE
827auto operator*(const Scalar & alpha, const TensorLike & T_in)
828{
829 return T_in * alpha;
830}
831
832template <typename TensorLike,
833 typename Scalar,
834 typename std::enable_if<is_tensor_like_v<TensorLike> && is_tensor_ref_v<TensorLike> &&
835 !is_vector_like_v<Scalar> && !is_tensor_like_v<Scalar>,
836 int>::type = 0>
837LIBMESH_DEVICE_INLINE
838auto operator*(const TensorLike & T_in, const Scalar & alpha)
839{
840 return detail::transformed_tensor<tensor_semantic_type_t<TensorLike>>(
841 T_in,
843}
844
849template <typename TensorLike, typename Scalar>
850LIBMESH_DEVICE_INLINE
851auto operator/(const TensorLike & T_in, const Scalar & alpha)
852 -> std::enable_if_t<is_tensor_like_v<TensorLike> && is_tensor_ref_v<TensorLike> &&
853 !is_vector_like_v<Scalar> && !is_tensor_like_v<Scalar>,
855{
856 return detail::transformed_tensor<tensor_semantic_type_t<TensorLike>>(
857 T_in,
859}
860
865template <typename LeftTensor,
866 typename RightTensor,
867 typename std::enable_if<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
868 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
869 int>::type = 0>
870LIBMESH_DEVICE_INLINE
871auto operator*(const LeftTensor & left, const RightTensor & right)
872{
873 return detail::multiply_tensors<tensor_semantic_type_t<LeftTensor>>(left, right);
874}
875
879template <typename TensorLike,
880 typename VectorLike,
881 typename std::enable_if<is_tensor_like_v<TensorLike> && is_vector_like_v<VectorLike> &&
882 (is_tensor_ref_v<TensorLike> || is_vector_ref_v<VectorLike>),
883 int>::type = 0>
884LIBMESH_DEVICE_INLINE
885auto operator*(const TensorLike & T_in, const VectorLike & v)
886{
887 return detail::multiply_tensor_vector<vector_semantic_type_t<VectorLike>>(T_in, v);
888}
889
894template <typename VectorLike,
895 typename TensorLike,
896 typename std::enable_if<is_vector_like_v<VectorLike> && is_tensor_like_v<TensorLike> &&
897 (is_vector_ref_v<VectorLike> || is_tensor_ref_v<TensorLike>),
898 int>::type = 0>
899LIBMESH_DEVICE_INLINE
900auto operator*(const VectorLike & v, const TensorLike & T_in)
901{
902 return detail::multiply_vector_tensor<vector_semantic_type_t<VectorLike>>(v, T_in);
903}
904
913template <typename LeftTensor, typename RightTensor>
914LIBMESH_DEVICE_INLINE
915auto operator==(const LeftTensor & left, const RightTensor & right)
916 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
917 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
918 bool>
919{
920 return detail::tensor_equal_impl(left, right);
921}
922
927template <typename LeftTensor, typename RightTensor>
928LIBMESH_DEVICE_INLINE
929auto operator!=(const LeftTensor & left, const RightTensor & right)
930 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
931 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
932 bool>
933{
934 return !(left == right);
935}
936
941template <typename LeftTensor, typename RightTensor>
942LIBMESH_DEVICE_INLINE
943auto operator+=(LeftTensor & left, const RightTensor & right)
944 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
945 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
946 LeftTensor &>
947{
948 detail::update_tensor_components(left, right, 1);
949 return left;
950}
951
955template <typename LeftTensor, typename RightTensor>
956LIBMESH_DEVICE_INLINE
957auto operator-=(LeftTensor & left, const RightTensor & right)
958 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_like_v<RightTensor> &&
959 (is_tensor_ref_v<LeftTensor> || is_tensor_ref_v<RightTensor>),
960 LeftTensor &>
961{
962 detail::update_tensor_components(left, right, -1);
963 return left;
964}
965
969template <typename LeftTensor, typename Scalar>
970LIBMESH_DEVICE_INLINE
971auto operator*=(LeftTensor & left, const Scalar & alpha)
972 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_ref_v<LeftTensor> &&
973 !is_vector_like_v<Scalar> && !is_tensor_like_v<Scalar>,
974 LeftTensor &>
975{
977 return left;
978}
979
983template <typename LeftTensor, typename Scalar>
984LIBMESH_DEVICE_INLINE
985auto operator/=(LeftTensor & left, const Scalar & alpha)
986 -> std::enable_if_t<is_tensor_like_v<LeftTensor> && is_tensor_ref_v<LeftTensor> &&
987 !is_vector_like_v<Scalar> && !is_tensor_like_v<Scalar>,
988 LeftTensor &>
989{
991 return left;
992}
993
994} // namespace libMesh::Kokkos
995
996#endif // LIBMESH_KOKKOS_TENSOR_OPS_H
unsigned int dim
LIBMESH_DEVICE_INLINE auto left_multiply(const VectorLike &v) const
LIBMESH_DEVICE_INLINE void add(const RightTensor &right)
Adds right to the referenced entry, in place.
LIBMESH_DEVICE_INLINE auto transpose() const
LIBMESH_DEVICE_INLINE void zero()
Sets every component of the referenced entry to zero.
LIBMESH_DEVICE_INLINE void subtract_scaled(const RightTensor &right, const value_type &factor)
Subtracts factor times right from the referenced entry, in place.
LIBMESH_DEVICE_INLINE void solve(const VectorLike &b, ResultVector &x) const
Solves the linear system (*this)x = b for x, via the explicit inverse.
LIBMESH_DEVICE_INLINE auto norm() const
LIBMESH_DEVICE_INLINE void add_scaled(const RightTensor &right, const value_type &factor)
Adds factor times right to the referenced entry, in place.
LIBMESH_DEVICE_INLINE auto norm_sq() const
LIBMESH_DEVICE_INLINE void subtract(const RightTensor &right)
Subtracts right from the referenced entry, in place.
LIBMESH_DEVICE_INLINE auto tr() const
LIBMESH_DEVICE_INLINE void assign(const RightTensor &right)
Assigns right 's components to the referenced entry.
LIBMESH_DEVICE_INLINE auto row(const unsigned int i) const
detail::tensor_view_value_t< ViewType > value_type
LIBMESH_DEVICE_INLINE auto contract(const RightTensor &right) const
LIBMESH_DEVICE_INLINE auto inverse(const unsigned int dim=LIBMESH_DIM) const
LIBMESH_DEVICE_INLINE auto det(const unsigned int dim=LIBMESH_DIM) const
LIBMESH_DEVICE_INLINE auto column(const unsigned int i) const
LIBMESH_DEVICE_INLINE bool is_zero() const
This class defines a tensor in LIBMESH_DIM dimensional space of type T.
Definition type_tensor.h:54
This class defines a vector in LIBMESH_DIM dimensional space of type T.
Definition type_vector.h:64
static const Real b
LIBMESH_DEVICE_INLINE bool tensor_equal_impl(const LeftTensor &left, const RightTensor &right)
LIBMESH_DEVICE_INLINE void fill_tensor_components(TensorLike &T_in, const Scalar &value)
Sets every component of T_in to value.
LIBMESH_DEVICE_INLINE void transform_tensor_components(OutputTensor &out, const InputTensor &in, const TransformOp &op)
Componentwise transform: out(i,j) = op(in(i,j)).
LIBMESH_DEVICE_INLINE ResultTensor transformed_tensor(const TensorLike &T_in, const TransformOp &op)
LIBMESH_DEVICE_INLINE auto tensor_trace(const TensorLike &T_in)
LIBMESH_DEVICE_INLINE ResultTensor transpose(const TensorLike &T_in)
LIBMESH_DEVICE_INLINE ResultTensor inverse(const TensorLike &T_in, const unsigned int dim=LIBMESH_DIM)
LIBMESH_DEVICE_INLINE void assign_tensor_components(LeftTensor &left, const RightTensor &right)
Componentwise copy: left = right.
LIBMESH_DEVICE_INLINE ResultTensor outer_product(const LeftVector &left, const RightVector &right)
LIBMESH_DEVICE_INLINE void update_tensor_components(LeftTensor &left, const RightTensor &right, const Scalar &factor)
Componentwise axpy-style update: left += factor * right.
typename std::remove_cv< typename std::remove_reference< T >::type >::type remove_cvref_t
Strips reference and const/volatile qualifiers from T, turning e.g.
LIBMESH_DEVICE_INLINE ResultVector column(const TensorLike &T_in, const unsigned int col_index)
LIBMESH_DEVICE_INLINE ResultVector row(const TensorLike &T_in, const unsigned int row_index)
LIBMESH_DEVICE_INLINE ResultVector multiply_vector_tensor(const VectorLike &v, const TensorLike &T_in)
LIBMESH_DEVICE_INLINE auto tensor_contract(const LeftTensor &left, const RightTensor &right)
LIBMESH_DEVICE_INLINE ResultVector multiply_tensor_vector(const TensorLike &T_in, const VectorLike &v)
LIBMESH_DEVICE_INLINE ResultTensor multiply_tensors(const LeftTensor &left, const RightTensor &right)
LIBMESH_DEVICE_INLINE TensorLike::value_type leading_determinant(const TensorLike &T_in, const unsigned int dim=LIBMESH_DIM)
LIBMESH_DEVICE_INLINE auto operator*(const Scalar &alpha, const TensorLike &T_in)
LIBMESH_DEVICE_INLINE auto inverse(const TensorLike &T_in, const unsigned int dim=LIBMESH_DIM) -> std::enable_if_t< is_tensor_like_v< TensorLike >, std::conditional_t< std::is_void< ResultTensor >::value, tensor_semantic_type_t< TensorLike >, ResultTensor > >
typename tensor_traits< detail::remove_cvref_t< T > >::semantic_type tensor_semantic_type_t
The owning libMesh type a tensor-like T materializes to, with any cv/reference qualification on T str...
LIBMESH_DEVICE_INLINE auto copy_tensor(const TensorLike &T_in) -> std::conditional_t< std::is_void< ResultTensor >::value, tensor_semantic_type_t< TensorLike >, ResultTensor >
LIBMESH_DEVICE_INLINE auto operator/=(LeftTensor &left, const Scalar &alpha) -> std::enable_if_t< is_tensor_like_v< LeftTensor > &&is_tensor_ref_v< LeftTensor > &&!is_vector_like_v< Scalar > &&!is_tensor_like_v< Scalar >, LeftTensor & >
Divides left by alpha in place.
LIBMESH_DEVICE_INLINE auto column(const TensorLike &T_in, const unsigned int i) -> std::enable_if_t< is_tensor_like_v< TensorLike >, std::conditional_t< std::is_void< ResultVector >::value, libMesh::TypeVector< typename TensorLike::value_type >, ResultVector > >
LIBMESH_DEVICE_INLINE auto operator+(const LeftTensor &left, const RightTensor &right) -> std::enable_if_t< is_tensor_like_v< LeftTensor > &&is_tensor_like_v< RightTensor > &&(is_tensor_ref_v< LeftTensor >||is_tensor_ref_v< RightTensor >), tensor_semantic_type_t< LeftTensor > >
LIBMESH_DEVICE_INLINE auto contract(const LeftTensor &left, const RightTensor &right)
LIBMESH_DEVICE_INLINE auto operator==(const LeftTensor &left, const RightTensor &right) -> std::enable_if_t< is_tensor_like_v< LeftTensor > &&is_tensor_like_v< RightTensor > &&(is_tensor_ref_v< LeftTensor >||is_tensor_ref_v< RightTensor >), bool >
LIBMESH_DEVICE_INLINE auto operator-(const TensorLike &T_in) -> std::enable_if_t< is_tensor_like_v< TensorLike > &&is_tensor_ref_v< TensorLike >, tensor_semantic_type_t< TensorLike > >
LIBMESH_DEVICE_INLINE ResultTensor tensor_identity(const unsigned int dim=LIBMESH_DIM)
LIBMESH_DEVICE_INLINE auto operator!=(const LeftTensor &left, const RightTensor &right) -> std::enable_if_t< is_tensor_like_v< LeftTensor > &&is_tensor_like_v< RightTensor > &&(is_tensor_ref_v< LeftTensor >||is_tensor_ref_v< RightTensor >), bool >
LIBMESH_DEVICE_INLINE auto row(const TensorLike &T_in, const unsigned int i) -> std::enable_if_t< is_tensor_like_v< TensorLike >, std::conditional_t< std::is_void< ResultVector >::value, libMesh::TypeVector< typename TensorLike::value_type >, ResultVector > >
LIBMESH_DEVICE_INLINE auto transpose(const TensorLike &T_in) -> std::enable_if_t< is_tensor_like_v< TensorLike >, std::conditional_t< std::is_void< ResultTensor >::value, tensor_semantic_type_t< TensorLike >, ResultTensor > >
LIBMESH_DEVICE_INLINE auto operator-=(LeftTensor &left, const RightTensor &right) -> std::enable_if_t< is_tensor_like_v< LeftTensor > &&is_tensor_like_v< RightTensor > &&(is_tensor_ref_v< LeftTensor >||is_tensor_ref_v< RightTensor >), LeftTensor & >
Subtracts right from left in place.
LIBMESH_DEVICE_INLINE auto outer_product(const LeftVector &left, const RightVector &right) -> std::enable_if_t< is_vector_like_v< LeftVector > &&is_vector_like_v< RightVector >, std::conditional_t< std::is_void< ResultTensor >::value, libMesh::TypeTensor< typename LeftVector::value_type >, ResultTensor > >
LIBMESH_DEVICE_INLINE auto operator/(const TensorLike &T_in, const Scalar &alpha) -> std::enable_if_t< is_tensor_like_v< TensorLike > &&is_tensor_ref_v< TensorLike > &&!is_vector_like_v< Scalar > &&!is_tensor_like_v< Scalar >, tensor_semantic_type_t< TensorLike > >
LIBMESH_DEVICE_INLINE auto operator+=(LeftTensor &left, const RightTensor &right) -> std::enable_if_t< is_tensor_like_v< LeftTensor > &&is_tensor_like_v< RightTensor > &&(is_tensor_ref_v< LeftTensor >||is_tensor_ref_v< RightTensor >), LeftTensor & >
Adds right to left in place; when left is a ref, the update writes through to the underlying storage.
LIBMESH_DEVICE_INLINE auto operator*=(LeftTensor &left, const Scalar &alpha) -> std::enable_if_t< is_tensor_like_v< LeftTensor > &&is_tensor_ref_v< LeftTensor > &&!is_vector_like_v< Scalar > &&!is_tensor_like_v< Scalar >, LeftTensor & >
Scales left by alpha in place.
LIBMESH_DEVICE_INLINE auto det(const TensorLike &T_in) -> std::enable_if_t< is_tensor_like_v< TensorLike >, decltype(T_in.det())>
LIBMESH_DEVICE_INLINE auto norm_sq(const T &a)
void libmesh_ignore(const Args &...)
OStreamProxy out
LIBMESH_DEVICE_INLINE T libmesh_conj(T a)
Componentwise transform functors, shared by the vector helpers here and the tensor helpers in kokkos_...
static const bool value
Definition xdr_io.C:55