MFEM  v4.1.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
ttensor.hpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #ifndef MFEM_TEMPLATE_TENSOR
13 #define MFEM_TEMPLATE_TENSOR
14 
15 #include "../config/tconfig.hpp"
16 #include "../general/tassign.hpp"
17 #include "tlayout.hpp"
18 #include "tmatrix.hpp"
19 #include "../general/cuda.hpp"
20 #include "../general/hip.hpp"
21 
22 // Templated tensor implementation (up to order 4)
23 
24 namespace mfem
25 {
26 
27 // Element-wise tensor operations
28 
29 namespace internal
30 {
31 
32 template <int Rank>
33 struct TensorOps;
34 
35 template <>
36 struct TensorOps<1> // rank = 1
37 {
38  // Assign: A {=,+=,*=} scalar_value
39  template <AssignOp::Type Op, typename A_layout_t, typename A_data_t,
40  typename scalar_t>
41  MFEM_HOST_DEVICE
42  static void Assign(const A_layout_t &A_layout, A_data_t &A_data,
43  scalar_t value)
44  {
45  MFEM_STATIC_ASSERT(A_layout_t::rank == 1, "invalid rank");
46  for (int i1 = 0; i1 < A_layout_t::dim_1; i1++)
47  {
48  mfem::Assign<Op>(A_data[A_layout.ind(i1)], value);
49  }
50  }
51 
52  // Assign: A {=,+=,*=} B
53  template <AssignOp::Type Op,
54  typename A_layout_t, typename A_data_t,
55  typename B_layout_t, typename B_data_t>
56  static void Assign(const A_layout_t &A_layout, A_data_t &A_data,
57  const B_layout_t &B_layout, const B_data_t &B_data)
58  {
59  MFEM_STATIC_ASSERT(A_layout_t::rank == 1 && B_layout_t::rank == 1,
60  "invalid ranks");
61  MFEM_STATIC_ASSERT(A_layout_t::dim_1 == B_layout_t::dim_1,
62  "invalid dimensions");
63  for (int i1 = 0; i1 < A_layout_t::dim_1; i1++)
64  {
65  mfem::Assign<Op>(A_data[A_layout.ind(i1)], B_data[B_layout.ind(i1)]);
66  }
67  }
68 };
69 
70 template <>
71 struct TensorOps<2> // rank = 2
72 {
73  // Assign: A {=,+=,*=} scalar_value
74  template <AssignOp::Type Op, typename A_layout_t, typename A_data_t,
75  typename scalar_t>
76  MFEM_HOST_DEVICE
77  static void Assign(const A_layout_t &A_layout, A_data_t &A_data,
78  scalar_t value)
79  {
80  MFEM_STATIC_ASSERT(A_layout_t::rank == 2, "invalid rank");
81  for (int i2 = 0; i2 < A_layout_t::dim_2; i2++)
82  {
83  for (int i1 = 0; i1 < A_layout_t::dim_1; i1++)
84  {
85  mfem::Assign<Op>(A_data[A_layout.ind(i1,i2)], value);
86  }
87  }
88  }
89 
90  // Assign: A {=,+=,*=} B
91  template <AssignOp::Type Op,
92  typename A_layout_t, typename A_data_t,
93  typename B_layout_t, typename B_data_t>
94  static void Assign(const A_layout_t &A_layout, A_data_t &A_data,
95  const B_layout_t &B_layout, const B_data_t &B_data)
96  {
97  MFEM_STATIC_ASSERT(A_layout_t::rank == 2 && B_layout_t::rank == 2,
98  "invalid ranks");
99  MFEM_STATIC_ASSERT(A_layout_t::dim_1 == B_layout_t::dim_1 &&
100  A_layout_t::dim_2 == B_layout_t::dim_2,
101  "invalid dimensions");
102  for (int i2 = 0; i2 < A_layout_t::dim_2; i2++)
103  {
104  for (int i1 = 0; i1 < A_layout_t::dim_1; i1++)
105  {
106  mfem::Assign<Op>(A_data[A_layout.ind(i1,i2)],
107  B_data[B_layout.ind(i1,i2)]);
108  }
109  }
110  }
111 };
112 
113 template <>
114 struct TensorOps<3> // rank = 3
115 {
116  // Assign: A {=,+=,*=} scalar_value
117  template <AssignOp::Type Op, typename A_layout_t, typename A_data_t,
118  typename scalar_t>
119  static void Assign(const A_layout_t &A_layout, A_data_t &A_data,
120  scalar_t value)
121  {
122  MFEM_STATIC_ASSERT(A_layout_t::rank == 3, "invalid rank");
123  for (int i3 = 0; i3 < A_layout_t::dim_3; i3++)
124  {
125  for (int i2 = 0; i2 < A_layout_t::dim_2; i2++)
126  {
127  for (int i1 = 0; i1 < A_layout_t::dim_1; i1++)
128  {
129  mfem::Assign<Op>(A_data[A_layout.ind(i1,i2,i3)], value);
130  }
131  }
132  }
133  }
134 
135  // Assign: A {=,+=,*=} B
136  template <AssignOp::Type Op,
137  typename A_layout_t, typename A_data_t,
138  typename B_layout_t, typename B_data_t>
139  static void Assign(const A_layout_t &A_layout, A_data_t &A_data,
140  const B_layout_t &B_layout, const B_data_t &B_data)
141  {
142  MFEM_STATIC_ASSERT(A_layout_t::rank == 3 && B_layout_t::rank == 3,
143  "invalid ranks");
144  MFEM_STATIC_ASSERT(A_layout_t::dim_1 == B_layout_t::dim_1 &&
145  A_layout_t::dim_2 == B_layout_t::dim_2 &&
146  A_layout_t::dim_3 == B_layout_t::dim_3,
147  "invalid dimensions");
148  for (int i3 = 0; i3 < A_layout_t::dim_3; i3++)
149  {
150  for (int i2 = 0; i2 < A_layout_t::dim_2; i2++)
151  {
152  for (int i1 = 0; i1 < A_layout_t::dim_1; i1++)
153  {
154  mfem::Assign<Op>(A_data[A_layout.ind(i1,i2,i3)],
155  B_data[B_layout.ind(i1,i2,i3)]);
156  }
157  }
158  }
159  }
160 };
161 
162 template <>
163 struct TensorOps<4> // rank = 4
164 {
165  // Assign: A {=,+=,*=} scalar_value
166  template <AssignOp::Type Op, typename A_layout_t, typename A_data_t,
167  typename scalar_t>
168  static void Assign(const A_layout_t &A_layout, A_data_t &A_data,
169  scalar_t value)
170  {
171  MFEM_STATIC_ASSERT(A_layout_t::rank == 4, "invalid rank");
172  for (int i4 = 0; i4 < A_layout_t::dim_4; i4++)
173  {
174  for (int i3 = 0; i3 < A_layout_t::dim_3; i3++)
175  {
176  for (int i2 = 0; i2 < A_layout_t::dim_2; i2++)
177  {
178  for (int i1 = 0; i1 < A_layout_t::dim_1; i1++)
179  {
180  mfem::Assign<Op>(A_data[A_layout.ind(i1,i2,i3,i4)], value);
181  }
182  }
183  }
184  }
185  }
186 
187  // Assign: A {=,+=,*=} B
188  template <AssignOp::Type Op,
189  typename A_layout_t, typename A_data_t,
190  typename B_layout_t, typename B_data_t>
191  static void Assign(const A_layout_t &A_layout, A_data_t &A_data,
192  const B_layout_t &B_layout, const B_data_t &B_data)
193  {
194  MFEM_STATIC_ASSERT(A_layout_t::rank == 4 && B_layout_t::rank == 4,
195  "invalid ranks");
196  MFEM_STATIC_ASSERT(A_layout_t::dim_1 == B_layout_t::dim_1 &&
197  A_layout_t::dim_2 == B_layout_t::dim_2 &&
198  A_layout_t::dim_3 == B_layout_t::dim_3 &&
199  A_layout_t::dim_4 == B_layout_t::dim_4,
200  "invalid dimensions");
201  for (int i4 = 0; i4 < A_layout_t::dim_4; i4++)
202  {
203  for (int i3 = 0; i3 < A_layout_t::dim_3; i3++)
204  {
205  for (int i2 = 0; i2 < A_layout_t::dim_2; i2++)
206  {
207  for (int i1 = 0; i1 < A_layout_t::dim_1; i1++)
208  {
209  mfem::Assign<Op>(A_data[A_layout.ind(i1,i2,i3,i4)],
210  B_data[B_layout.ind(i1,i2,i3,i4)]);
211  }
212  }
213  }
214  }
215  }
216 };
217 
218 } // namespace mfem::internal
219 
220 // Tensor or sub-tensor assign function: A {=,+=,*=} scalar_value.
221 template <AssignOp::Type Op, typename A_layout_t, typename A_data_t,
222  typename scalar_t>
223 MFEM_HOST_DEVICE
224 inline void TAssign(const A_layout_t &A_layout, A_data_t &A_data,
225  scalar_t value)
226 {
227  internal::TensorOps<A_layout_t::rank>::
228  template Assign<Op>(A_layout, A_data, value);
229 }
230 
231 // Tensor assign function: A {=,+=,*=} B that allows different input and output
232 // layouts. With suitable layouts this function can be used to permute
233 // (transpose) tensors, extract sub-tensors, etc.
234 template <AssignOp::Type Op,
235  typename A_layout_t, typename A_data_t,
236  typename B_layout_t, typename B_data_t>
237 inline void TAssign(const A_layout_t &A_layout, A_data_t &A_data,
238  const B_layout_t &B_layout, const B_data_t &B_data)
239 {
240  internal::TensorOps<A_layout_t::rank>::
241  template Assign<Op>(A_layout, A_data, B_layout, B_data);
242 }
243 
244 
245 // classes TVector, TMatrix, TTensor3, TTensor4
246 
247 template <int S, typename data_t = double, bool align = false>
248 struct TVector
249 {
250 public:
251  static const int size = S;
252  static const int aligned_size = align ? MFEM_ALIGN_SIZE(S,data_t) : size;
253  typedef data_t data_type;
255 
257  static const layout_type layout;
258 
259  MFEM_HOST_DEVICE data_t &operator[](int i) { return data[i]; }
260  MFEM_HOST_DEVICE const data_t &operator[](int i) const { return data[i]; }
261 
262  template <AssignOp::Type Op>
263  void Assign(const data_t d)
264  {
265  TAssign<Op>(layout, data, d);
266  }
267 
268  template <AssignOp::Type Op, typename src_data_t>
269  void Assign(const src_data_t &src)
270  {
271  TAssign<Op>(layout, data, layout, src);
272  }
273 
274  template <AssignOp::Type Op, typename dest_data_t>
275  void AssignTo(dest_data_t &dest)
276  {
277  TAssign<Op>(layout, dest, layout, data);
278  }
279 
280  void Set(const data_t d)
281  {
282  Assign<AssignOp::Set>(d);
283  }
284 
285  template <typename src_data_t>
286  void Set(const src_data_t &src)
287  {
288  Assign<AssignOp::Set>(src);
289  }
290 
291  template <typename dest_data_t>
292  void Assemble(dest_data_t &dest) const
293  {
294  AssignTo<AssignOp::Add>(dest);
295  }
296 
297  void Scale(const data_t scale)
298  {
299  Assign<AssignOp::Mult>(scale);
300  }
301 };
302 
303 template <int S, typename data_t, bool align>
304 const typename TVector<S,data_t,align>::layout_type
305 TVector<S,data_t,align>::layout = layout_type();
306 
307 
308 template <int N1, int N2, typename data_t = double, bool align = false>
309 struct TMatrix : public TVector<N1*N2,data_t,align>
310 {
312  using base_class::size;
313  using base_class::data;
314 
316  static const layout_type layout;
317  static inline int ind(int i1, int i2) { return layout.ind(i1,i2); }
318 
319  data_t &operator()(int i, int j) { return data[ind(i,j)]; }
320  const data_t &operator()(int i, int j) const { return data[ind(i,j)]; }
321 
322  inline data_t Det() const
323  {
324  return TDet<data_t>(layout, data);
325  }
326 
327  inline void Adjugate(TMatrix<N1,N2,data_t> &adj) const
328  {
329  TAdjugate<data_t>(layout, data, layout, adj.data);
330  }
331 
332  // Compute the adjugate and the determinant of a (small) matrix.
333  inline data_t AdjDet(TMatrix<N2,N1,data_t> &adj) const
334  {
335  return TAdjDet<data_t>(layout, data, layout, adj.data);
336  }
337 };
338 
339 template <int N1, int N2, typename data_t, bool align>
340 const typename TMatrix<N1,N2,data_t,align>::layout_type
341 TMatrix<N1,N2,data_t,align>::layout = layout_type();
342 
343 
344 template <int N1, int N2, int N3, typename data_t = double, bool align = false>
345 struct TTensor3 : TVector<N1*N2*N3,data_t,align>
346 {
348  using base_class::size;
349  using base_class::data;
350 
352  static const layout_type layout;
353  static inline int ind(int i1, int i2, int i3)
354  { return layout.ind(i1,i2,i3); }
355 
356  data_t &operator()(int i, int j, int k) { return data[ind(i,j,k)]; }
357  const data_t &operator()(int i, int j, int k) const
358  { return data[ind(i,j,k)]; }
359 };
360 
361 template <int N1, int N2, int N3, typename data_t, bool align>
362 const typename TTensor3<N1,N2,N3,data_t,align>::layout_type
363 TTensor3<N1,N2,N3,data_t,align>::layout = layout_type();
364 
365 template <int N1, int N2, int N3, int N4, typename data_t = double,
366  bool align = false>
367 struct TTensor4 : TVector<N1*N2*N3*N4,data_t,align>
368 {
370  using base_class::size;
371  using base_class::data;
372 
374  static const layout_type layout;
375  static inline int ind(int i1, int i2, int i3, int i4)
376  { return layout.ind(i1,i2,i3,i4); }
377 
378  data_t &operator()(int i, int j, int k, int l)
379  { return data[ind(i,j,k,l)]; }
380  const data_t &operator()(int i, int j, int k, int l) const
381  { return data[ind(i,j,k,l)]; }
382 };
383 
384 template <int N1, int N2, int N3, int N4, typename data_t, bool align>
385 const typename TTensor4<N1,N2,N3,N4,data_t,align>::layout_type
386 TTensor4<N1,N2,N3,N4,data_t,align>::layout = layout_type();
387 
388 
389 // Tensor products
390 
391 // C_{i,j,k} {=|+=} \sum_s A_{s,j} B_{i,s,k}
392 template <bool Add,
393  typename A_layout_t, typename A_data_t,
394  typename B_layout_t, typename B_data_t,
395  typename C_layout_t, typename C_data_t>
396 MFEM_ALWAYS_INLINE inline
397 void Mult_1_2(const A_layout_t &A_layout, const A_data_t &A_data,
398  const B_layout_t &B_layout, const B_data_t &B_data,
399  const C_layout_t &C_layout, C_data_t &C_data)
400 {
401  MFEM_STATIC_ASSERT(A_layout_t::rank == 2 && B_layout_t::rank == 3 &&
402  C_layout_t::rank == 3, "invalid ranks");
403  const int B3 = B_layout_t::dim_3;
404  const int C3 = C_layout_t::dim_3;
405  MFEM_STATIC_ASSERT(B3 == C3, "invalid dimentions");
406  for (int k = 0; k < B3; k++)
407  {
408  Mult_AB<Add>(B_layout.ind3(k), B_data,
409  A_layout, A_data,
410  C_layout.ind3(k), C_data);
411  }
412 }
413 
414 // C_{i,j,k} {=|+=} \sum_s A_{i,s} B_{s,j,k}
415 template <bool Add,
416  typename A_layout_t, typename A_data_t,
417  typename B_layout_t, typename B_data_t,
418  typename C_layout_t, typename C_data_t>
419 MFEM_ALWAYS_INLINE inline
420 void Mult_2_1(const A_layout_t &A_layout, const A_data_t &A_data,
421  const B_layout_t &B_layout, const B_data_t &B_data,
422  const C_layout_t &C_layout, C_data_t &C_data)
423 {
424  MFEM_STATIC_ASSERT(A_layout_t::rank == 2 && B_layout_t::rank == 3 &&
425  C_layout_t::rank == 3, "invalid ranks");
426  Mult_AB<Add>(A_layout, A_data,
427  B_layout.merge_23(), B_data,
428  C_layout.merge_23(), C_data);
429 }
430 
431 // C_{i,k,j,l} {=|+=} \sum_s A_{s,i} A_{s,j} B_{k,s,l}
432 template <bool Add,
433  typename A_layout_t, typename A_data_t,
434  typename B_layout_t, typename B_data_t,
435  typename C_layout_t, typename C_data_t>
436 MFEM_ALWAYS_INLINE inline
437 void TensorAssemble(const A_layout_t &A_layout, const A_data_t &A_data,
438  const B_layout_t &B_layout, const B_data_t &B_data,
439  const C_layout_t &C_layout, C_data_t &C_data)
440 {
441  MFEM_STATIC_ASSERT(A_layout_t::rank == 2 && B_layout_t::rank == 3 &&
442  C_layout_t::rank == 4, "invalid ranks");
443  const int A1 = A_layout_t::dim_1;
444  const int A2 = A_layout_t::dim_2;
445  const int B1 = B_layout_t::dim_1;
446  const int B2 = B_layout_t::dim_2;
447  const int B3 = B_layout_t::dim_3;
448  const int C1 = C_layout_t::dim_1;
449  const int C2 = C_layout_t::dim_2;
450  const int C3 = C_layout_t::dim_3;
451  const int C4 = C_layout_t::dim_4;
452  MFEM_STATIC_ASSERT(A1 == B2 && A2 == C1 && A2 == C3 && B1 == C2 && B3 == C4,
453  "invalid dimensions");
454 
455 #if 1
456  // Impl == 3
457  MFEM_FLOPS_ADD(3*A1*A2*A2*B1*B3);
458  if (!Add) { TAssign<AssignOp::Set>(C_layout, C_data, 0.0); }
459  for (int j = 0; j < A2; j++)
460  {
461  for (int i = 0; i < A2; i++)
462  {
463  for (int l = 0; l < B3; l++)
464  {
465  for (int k = 0; k < B1; k++)
466  {
467  for (int s = 0; s < A1; s++)
468  {
469  // C(i,k,j,l) += A(s,i) * A(s,j) * B(k,s,l);
470  C_data[C_layout.ind(i,k,j,l)] +=
471  A_data[A_layout.ind(s,i)] *
472  A_data[A_layout.ind(s,j)] *
473  B_data[B_layout.ind(k,s,l)];
474  }
475  }
476  }
477  }
478  }
479 #else
480  // Impl == 1
481  if (!Add) { TAssign<AssignOp::Set>(C_layout, C_data, 0.0); }
482  for (int s = 0; s < A1; s++)
483  {
484  for (int i = 0; i < A2; i++)
485  {
486  for (int k = 0; k < B1; k++)
487  {
488  for (int j = 0; j < A2; j++)
489  {
490  for (int l = 0; l < B3; l++)
491  {
492  // C(i,k,j,l) += A(s,i) * A(s,j) * B(k,s,l);
493  C_data[C_layout.ind(i,k,j,l)] +=
494  A_data[A_layout.ind(s,i)] *
495  A_data[A_layout.ind(s,j)] *
496  B_data[B_layout.ind(k,s,l)];
497  }
498  }
499  }
500  }
501  }
502 #endif
503 }
504 
505 // D_{i,k,j,l} {=|+=} \sum_s A_{i,s} B_{s,j} C_{k,s,l}
506 template <bool Add,
507  typename A_layout_t, typename A_data_t,
508  typename B_layout_t, typename B_data_t,
509  typename C_layout_t, typename C_data_t,
510  typename D_layout_t, typename D_data_t>
511 MFEM_ALWAYS_INLINE inline
512 void TensorAssemble(const A_layout_t &A_layout, const A_data_t &A_data,
513  const B_layout_t &B_layout, const B_data_t &B_data,
514  const C_layout_t &C_layout, const C_data_t &C_data,
515  const D_layout_t &D_layout, D_data_t &D_data)
516 {
517  MFEM_STATIC_ASSERT(A_layout_t::rank == 2 && B_layout_t::rank == 2 &&
518  C_layout_t::rank == 3 && D_layout_t::rank == 4,
519  "invalid ranks");
520  const int A1 = A_layout_t::dim_1;
521  const int A2 = A_layout_t::dim_2;
522  const int B1 = B_layout_t::dim_1;
523  const int B2 = B_layout_t::dim_2;
524  const int C1 = C_layout_t::dim_1;
525  const int C2 = C_layout_t::dim_2;
526  const int C3 = C_layout_t::dim_3;
527  const int D1 = D_layout_t::dim_1;
528  const int D2 = D_layout_t::dim_2;
529  const int D3 = D_layout_t::dim_3;
530  const int D4 = D_layout_t::dim_4;
531  MFEM_STATIC_ASSERT(A2 == B1 && A2 == C2 && A1 == D1 && B2 == D3 &&
532  C1 == D2 && C3 == D4, "invalid dimensions");
533 
534 #if 0
536  // H_{i,k,s,l} = A_{i,s} C_{k,s,l}
537  for (int l = 0; l < C3; l++)
538  {
539  for (int s = 0; s < B1; s++)
540  {
541  for (int k = 0; k < C1; k++)
542  {
543  for (int i = 0; i < A1; i++)
544  {
545  H(i,k,s,l) = A_data[A_layout.ind(i,s)]*
546  C_data[C_layout.ind(k,s,l)];
547  }
548  }
549  }
550  }
551  // D_{(i,k),j,l} = \sum_s B_{s,j} H_{(i,k),s,l}
552  Mult_1_2<Add>(B_layout, B_data, H.layout.merge_12(), H,
553  D_layout.merge_12(), D_data);
554 #elif 1
555  MFEM_FLOPS_ADD(A1*B1*C1*C3); // computation of H(l)
556  for (int l = 0; l < C3; l++)
557  {
559  // H(l)_{i,k,s} = A_{i,s} C_{k,s,l}
560  for (int s = 0; s < B1; s++)
561  {
562  for (int k = 0; k < C1; k++)
563  {
564  for (int i = 0; i < A1; i++)
565  {
566  H(i,k,s) = A_data[A_layout.ind(i,s)]*
567  C_data[C_layout.ind(k,s,l)];
568  }
569  }
570  }
571  // D_{(i,k),j,l} = \sum_s H(l)_{(i,k),s} B_{s,j}
572  Mult_AB<Add>(H.layout.merge_12(), H, B_layout, B_data,
573  D_layout.merge_12().ind3(l), D_data);
574  }
575 #else
577  for (int l = 0; l < C3; l++)
578  {
579  for (int j = 0; j < B2; j++)
580  {
581  for (int k = 0; k < C1; k++)
582  {
583  for (int s = 0; s < B1; s++)
584  {
585  F(s,k,j,l) = B_data[B_layout.ind(s,j)]*
586  C_data[C_layout.ind(k,s,l)];
587  }
588  }
589  }
590  }
591  Mult_AB<Add>(A_layout, A_data, F.layout.merge_34().merge_23(), F,
592  D_layout.merge_34().merge_23(), D_data);
593 #endif
594 }
595 
596 
597 // C_{i,j,k,l} {=|+=} A_{i,j,k} B_{j,l}
598 template <AssignOp::Type Op,
599  typename A_layout_t, typename A_data_t,
600  typename B_layout_t, typename B_data_t,
601  typename C_layout_t, typename C_data_t>
602 MFEM_ALWAYS_INLINE inline
603 void TensorProduct(const A_layout_t &a, const A_data_t &A,
604  const B_layout_t &b, const B_data_t &B,
605  const C_layout_t &c, C_data_t &C)
606 {
607  const int A1 = A_layout_t::dim_1;
608  const int A2 = A_layout_t::dim_2;
609  const int A3 = A_layout_t::dim_3;
610  const int B1 = B_layout_t::dim_1;
611  const int B2 = B_layout_t::dim_2;
612  const int C1 = C_layout_t::dim_1;
613  const int C2 = C_layout_t::dim_2;
614  const int C3 = C_layout_t::dim_3;
615  const int C4 = C_layout_t::dim_4;
616  MFEM_STATIC_ASSERT(A1 == C1 && A2 == B1 && A2 == C2 && A3 == C3 && B2 == C4,
617  "invalid dimensions");
618 
619  MFEM_FLOPS_ADD(A1*A2*A3*B2);
620  for (int l = 0; l < B2; l++)
621  {
622  for (int k = 0; k < A3; k++)
623  {
624  for (int j = 0; j < A2; j++)
625  {
626  for (int i = 0; i < A1; i++)
627  {
628  mfem::Assign<Op>(C[c.ind(i,j,k,l)],
629  A[a.ind(i,j,k)]*B[b.ind(j,l)]);
630  }
631  }
632  }
633  }
634 }
635 
636 } // namespace mfem
637 
638 #endif // MFEM_TEMPLATE_TENSOR
void Assemble(dest_data_t &dest) const
Definition: ttensor.hpp:292
ColumnMajorLayout3D< N1, N2, N3 > layout_type
Definition: ttensor.hpp:351
ColumnMajorLayout4D< N1, N2, N3, N4 > layout_type
Definition: ttensor.hpp:373
static const layout_type layout
Definition: ttensor.hpp:316
void Adjugate(TMatrix< N1, N2, data_t > &adj) const
Definition: ttensor.hpp:327
void Assign(const src_data_t &src)
Definition: ttensor.hpp:269
void AssignTo(dest_data_t &dest)
Definition: ttensor.hpp:275
const data_t & operator()(int i, int j, int k) const
Definition: ttensor.hpp:357
MFEM_HOST_DEVICE void TAssign(const A_layout_t &A_layout, A_data_t &A_data, scalar_t value)
Definition: ttensor.hpp:224
void Assign(const data_t d)
Definition: ttensor.hpp:263
static const layout_type layout
Definition: ttensor.hpp:352
static int ind(int i1, int i2, int i3)
Definition: ttensor.hpp:353
const data_t & operator()(int i, int j) const
Definition: ttensor.hpp:320
data_t & operator()(int i, int j)
Definition: ttensor.hpp:319
data_t Det() const
Definition: ttensor.hpp:322
MFEM_ALWAYS_INLINE void TensorAssemble(const A_layout_t &A_layout, const A_data_t &A_data, const B_layout_t &B_layout, const B_data_t &B_data, const C_layout_t &C_layout, C_data_t &C_data)
Definition: ttensor.hpp:437
void Add(const DenseMatrix &A, const DenseMatrix &B, double alpha, DenseMatrix &C)
C = A + alpha*B.
Definition: densemat.cpp:1926
static const layout_type layout
Definition: ttensor.hpp:374
static int ind(int i1, int i2, int i3, int i4)
Definition: tlayout.hpp:397
static int ind(int i1, int i2, int i3, int i4)
Definition: ttensor.hpp:375
double b
Definition: lissajous.cpp:42
MFEM_HOST_DEVICE data_t & operator[](int i)
Definition: ttensor.hpp:259
const data_t & operator()(int i, int j, int k, int l) const
Definition: ttensor.hpp:380
static int ind(int i1, int i2, int i3)
Definition: tlayout.hpp:250
TVector< N1 *N2, data_t, align > base_class
Definition: ttensor.hpp:311
MFEM_ALWAYS_INLINE void Mult_2_1(const A_layout_t &A_layout, const A_data_t &A_data, const B_layout_t &B_layout, const B_data_t &B_data, const C_layout_t &C_layout, C_data_t &C_data)
Definition: ttensor.hpp:420
ColumnMajorLayout2D< N1, N2 > layout_type
Definition: ttensor.hpp:315
MFEM_ALWAYS_INLINE void TensorProduct(const A_layout_t &a, const A_data_t &A, const B_layout_t &b, const B_data_t &B, const C_layout_t &c, C_data_t &C)
Definition: ttensor.hpp:603
static const layout_type layout
Definition: ttensor.hpp:257
double a
Definition: lissajous.cpp:41
MFEM_HOST_DEVICE lvalue_t & Assign(lvalue_t &a, const rvalue_t &b)
Definition: tassign.hpp:105
data_t data_type
Definition: ttensor.hpp:253
TVector< N1 *N2 *N3 *N4, data_t, align > base_class
Definition: ttensor.hpp:369
static int ind(int i1, int i2)
Definition: ttensor.hpp:317
data_t AdjDet(TMatrix< N2, N1, data_t > &adj) const
Definition: ttensor.hpp:333
MFEM_ALWAYS_INLINE void Mult_1_2(const A_layout_t &A_layout, const A_data_t &A_data, const B_layout_t &B_layout, const B_data_t &B_data, const C_layout_t &C_layout, C_data_t &C_data)
Definition: ttensor.hpp:397
static const int size
Definition: ttensor.hpp:251
TVector< N1 *N2 *N3, data_t, align > base_class
Definition: ttensor.hpp:347
StridedLayout1D< S, 1 > layout_type
Definition: ttensor.hpp:256
static MFEM_HOST_DEVICE int ind(int i1, int i2)
Definition: tlayout.hpp:108
data_t data[aligned_size >0?aligned_size:1]
Definition: ttensor.hpp:254
void Set(const src_data_t &src)
Definition: ttensor.hpp:286
static const int aligned_size
Definition: ttensor.hpp:252
void Set(const data_t d)
Definition: ttensor.hpp:280
MFEM_HOST_DEVICE const data_t & operator[](int i) const
Definition: ttensor.hpp:260
void Scale(const data_t scale)
Definition: ttensor.hpp:297
data_t & operator()(int i, int j, int k, int l)
Definition: ttensor.hpp:378
data_t & operator()(int i, int j, int k)
Definition: ttensor.hpp:356