Intrepid2
Intrepid2_Data.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Intrepid2 Package
4 //
5 // Copyright 2007 NTESS and the Intrepid2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 //
10 // Intrepid2_Data.hpp
11 // QuadraturePerformance
12 //
13 // Created by Roberts, Nathan V on 8/24/20.
14 //
15 
16 #ifndef Intrepid2_Data_h
17 #define Intrepid2_Data_h
18 
23 #include "Intrepid2_ScalarView.hpp"
24 #include "Intrepid2_Utils.hpp"
25 
31 namespace Intrepid2 {
32 
41 template<class DataScalar,typename DeviceType>
42 class ZeroView {
43 public:
44  static ScalarView<DataScalar,DeviceType> zeroView()
45  {
46  static ScalarView<DataScalar,DeviceType> zeroView = ScalarView<DataScalar,DeviceType>("zero",1);
47  static bool havePushedFinalizeHook = false;
48  if (!havePushedFinalizeHook)
49  {
50  Kokkos::push_finalize_hook( [=] {
51  zeroView = ScalarView<DataScalar,DeviceType>();
52  });
53  havePushedFinalizeHook = true;
54  }
55  return zeroView;
56  }
57 };
58 
76  template<class DataScalar,typename DeviceType>
77  class Data {
78  public:
79  using value_type = DataScalar;
80  using execution_space = typename DeviceType::execution_space;
81 
82  using reference_type = typename ScalarView<DataScalar,DeviceType>::reference_type;
83  using const_reference_type = typename ScalarView<const DataScalar,DeviceType>::reference_type;
84  private:
85  ordinal_type dataRank_;
86  Kokkos::View<DataScalar*, DeviceType> data1_; // the rank 1 data that is explicitly stored
87  Kokkos::View<DataScalar**, DeviceType> data2_; // the rank 2 data that is explicitly stored
88  Kokkos::View<DataScalar***, DeviceType> data3_; // the rank 3 data that is explicitly stored
89  Kokkos::View<DataScalar****, DeviceType> data4_; // the rank 4 data that is explicitly stored
90  Kokkos::View<DataScalar*****, DeviceType> data5_; // the rank 5 data that is explicitly stored
91  Kokkos::View<DataScalar******, DeviceType> data6_; // the rank 6 data that is explicitly stored
92  Kokkos::View<DataScalar*******, DeviceType> data7_; // the rank 7 data that is explicitly stored
93  Kokkos::Array<int,7> extents_; // logical extents in each dimension
94  Kokkos::Array<DataVariationType,7> variationType_; // for each dimension, whether the data varies in that dimension
95  Kokkos::Array<int,7> variationModulus_; // for each dimension, a value by which indices should be modulused (only used when variationType_ is MODULAR)
96  int blockPlusDiagonalLastNonDiagonal_ = -1; // last row/column that is part of the non-diagonal part of the matrix indicated by BLOCK_PLUS_DIAGONAL (if any dimensions are thus marked)
97 
98  bool hasNontrivialModulusUNUSED_; // this is a little nutty, but having this UNUSED member variable improves performance, probably by shifting the alignment of underlyingMatchesLogical_. This is true with nvcc; it may also be true with Apple clang
99  bool underlyingMatchesLogical_; // if true, this Data object has the same rank and extent as the underlying view
100  Kokkos::Array<ordinal_type,7> activeDims_;
101  int numActiveDims_; // how many of the 7 entries are actually filled in
102 
103  ordinal_type rank_;
104 
105  // we use (const_)reference_type as the return for operator() for performance reasons, especially significant when using Sacado types
106  using return_type = const_reference_type;
107 
108  ScalarView<DataScalar,DeviceType> zeroView_; // one-entry (zero); used to allow getEntry() to return 0 for off-diagonal entries in BLOCK_PLUS_DIAGONAL
109 
111  KOKKOS_INLINE_FUNCTION
112  static int blockPlusDiagonalNumNondiagonalEntries(const int &lastNondiagonal)
113  {
114  return (lastNondiagonal + 1) * (lastNondiagonal + 1);
115  }
116 
118  KOKKOS_INLINE_FUNCTION
119  static int blockPlusDiagonalBlockEntryIndex(const int &lastNondiagonal, const int &numNondiagonalEntries, const int &i, const int &j)
120  {
121  return i * (lastNondiagonal + 1) + j;
122  }
123 
125  KOKKOS_INLINE_FUNCTION
126  static int blockPlusDiagonalDiagonalEntryIndex(const int &lastNondiagonal, const int &numNondiagonalEntries, const int &i)
127  {
128  return i - (lastNondiagonal + 1) + numNondiagonalEntries;
129  }
130 
132  KOKKOS_INLINE_FUNCTION
133  int getUnderlyingViewExtent(const int &dim) const
134  {
135  switch (dataRank_)
136  {
137  case 1: return data1_.extent_int(dim);
138  case 2: return data2_.extent_int(dim);
139  case 3: return data3_.extent_int(dim);
140  case 4: return data4_.extent_int(dim);
141  case 5: return data5_.extent_int(dim);
142  case 6: return data6_.extent_int(dim);
143  case 7: return data7_.extent_int(dim);
144  default: return -1;
145  }
146  }
147 
150  {
151  zeroView_ = ZeroView<DataScalar,DeviceType>::zeroView(); // one-entry (zero); used to allow getEntry() to return 0 for off-diagonal entries in BLOCK_PLUS_DIAGONAL
152  // check that rank is compatible with the claimed extents:
153  for (int d=rank_; d<7; d++)
154  {
155  INTREPID2_TEST_FOR_EXCEPTION(extents_[d] > 1, std::invalid_argument, "Nominal extents may not be > 1 in dimensions beyond the rank of the container");
156  }
157 
158  numActiveDims_ = 0;
159  int blockPlusDiagonalCount = 0;
160  underlyingMatchesLogical_ = true;
161  for (ordinal_type i=0; i<7; i++)
162  {
163  if (variationType_[i] == GENERAL)
164  {
165  if (extents_[i] != 0)
166  {
167  variationModulus_[i] = extents_[i];
168  }
169  else
170  {
171  variationModulus_[i] = 1;
172  }
173  activeDims_[numActiveDims_] = i;
174  numActiveDims_++;
175  }
176  else if (variationType_[i] == MODULAR)
177  {
178  underlyingMatchesLogical_ = false;
179  if (extents_[i] != getUnderlyingViewExtent(numActiveDims_))
180  {
181  const int dataExtent = getUnderlyingViewExtent(numActiveDims_);
182  const int logicalExtent = extents_[i];
183  const int modulus = dataExtent;
184 
185  INTREPID2_TEST_FOR_EXCEPTION( dataExtent * (logicalExtent / dataExtent) != logicalExtent, std::invalid_argument, "data extent must evenly divide logical extent");
186 
187  variationModulus_[i] = modulus;
188  }
189  else
190  {
191  variationModulus_[i] = extents_[i];
192  }
193  activeDims_[numActiveDims_] = i;
194  numActiveDims_++;
195  }
196  else if (variationType_[i] == BLOCK_PLUS_DIAGONAL)
197  {
198  underlyingMatchesLogical_ = false;
199  blockPlusDiagonalCount++;
200  if (blockPlusDiagonalCount == 1) // first dimension thus marked --> active
201  {
202 
203 #ifdef HAVE_INTREPID2_DEBUG
204  const int numNondiagonalEntries = blockPlusDiagonalNumNondiagonalEntries(blockPlusDiagonalLastNonDiagonal_);
205  const int dataExtent = getUnderlyingViewExtent(numActiveDims_); // flat storage of all matrix entries
206  const int logicalExtent = extents_[i];
207  const int numDiagonalEntries = logicalExtent - (blockPlusDiagonalLastNonDiagonal_ + 1);
208  const int expectedDataExtent = numNondiagonalEntries + numDiagonalEntries;
209  INTREPID2_TEST_FOR_EXCEPTION(dataExtent != expectedDataExtent, std::invalid_argument, ("BLOCK_PLUS_DIAGONAL data extent of " + std::to_string(dataExtent) + " does not match expected based on blockPlusDiagonalLastNonDiagonal setting of " + std::to_string(blockPlusDiagonalLastNonDiagonal_)).c_str());
210 #endif
211 
212  activeDims_[numActiveDims_] = i;
213  numActiveDims_++;
214  }
215  variationModulus_[i] = getUnderlyingViewExtent(numActiveDims_);
216  INTREPID2_TEST_FOR_EXCEPTION(variationType_[i+1] != BLOCK_PLUS_DIAGONAL, std::invalid_argument, "BLOCK_PLUS_DIAGONAL ranks must be contiguous");
217  i++; // skip over the next BLOCK_PLUS_DIAGONAL
218  variationModulus_[i] = 1; // trivial modulus (should not ever be used)
219  INTREPID2_TEST_FOR_EXCEPTION(blockPlusDiagonalCount > 1, std::invalid_argument, "BLOCK_PLUS_DIAGONAL can only apply to two ranks");
220  }
221  else // CONSTANT
222  {
223  if (i < rank_)
224  {
225  underlyingMatchesLogical_ = false;
226  }
227  variationModulus_[i] = 1; // trivial modulus
228  }
229  }
230 
231  if (rank_ != dataRank_)
232  {
233  underlyingMatchesLogical_ = false;
234  }
235 
236  for (int d=numActiveDims_; d<7; d++)
237  {
238  // for *inactive* dims, the activeDims_ map just is the identity
239  // (this allows getEntry() to work even when the logical rank of the Data object is lower than that of the underlying View. This can happen for gradients in 1D.)
240  activeDims_[d] = d;
241  }
242  for (int d=0; d<7; d++)
243  {
244  INTREPID2_TEST_FOR_EXCEPTION(variationModulus_[d] == 0, std::logic_error, "variationModulus should not ever be 0");
245  }
246  }
247 
248  public:
250  template<class UnaryOperator>
251  void applyOperator(UnaryOperator unaryOperator)
252  {
253  using ExecutionSpace = typename DeviceType::execution_space;
254 
255  switch (dataRank_)
256  {
257  case 1:
258  {
259  const int dataRank = 1;
260  auto view = getUnderlyingView<dataRank>();
261 
262  const int dataExtent = this->getDataExtent(0);
263  Kokkos::RangePolicy<ExecutionSpace> policy(ExecutionSpace(),0,dataExtent);
264  Kokkos::parallel_for("apply operator in-place", policy,
265  KOKKOS_LAMBDA (const int &i0) {
266  view(i0) = unaryOperator(view(i0));
267  });
268 
269  }
270  break;
271  case 2:
272  {
273  const int dataRank = 2;
274  auto policy = dataExtentRangePolicy<dataRank>();
275  auto view = getUnderlyingView<dataRank>();
276 
277  Kokkos::parallel_for("apply operator in-place", policy,
278  KOKKOS_LAMBDA (const int &i0, const int &i1) {
279  view(i0,i1) = unaryOperator(view(i0,i1));
280  });
281  }
282  break;
283  case 3:
284  {
285  const int dataRank = 3;
286  auto policy = dataExtentRangePolicy<dataRank>();
287  auto view = getUnderlyingView<dataRank>();
288 
289  Kokkos::parallel_for("apply operator in-place", policy,
290  KOKKOS_LAMBDA (const int &i0, const int &i1, const int &i2) {
291  view(i0,i1,i2) = unaryOperator(view(i0,i1,i2));
292  });
293  }
294  break;
295  case 4:
296  {
297  const int dataRank = 4;
298  auto policy = dataExtentRangePolicy<dataRank>();
299  auto view = getUnderlyingView<dataRank>();
300 
301  Kokkos::parallel_for("apply operator in-place", policy,
302  KOKKOS_LAMBDA (const int &i0, const int &i1, const int &i2, const int &i3) {
303  view(i0,i1,i2,i3) = unaryOperator(view(i0,i1,i2,i3));
304  });
305  }
306  break;
307  case 5:
308  {
309  const int dataRank = 5;
310  auto policy = dataExtentRangePolicy<dataRank>();
311  auto view = getUnderlyingView<dataRank>();
312 
313  Kokkos::parallel_for("apply operator in-place", policy,
314  KOKKOS_LAMBDA (const int &i0, const int &i1, const int &i2, const int &i3, const int &i4) {
315  view(i0,i1,i2,i3,i4) = unaryOperator(view(i0,i1,i2,i3,i4));
316  });
317  }
318  break;
319  case 6:
320  {
321  const int dataRank = 6;
322  auto policy = dataExtentRangePolicy<dataRank>();
323  auto view = getUnderlyingView<dataRank>();
324 
325  Kokkos::parallel_for("apply operator in-place", policy,
326  KOKKOS_LAMBDA (const int &i0, const int &i1, const int &i2, const int &i3, const int &i4, const int &i5) {
327  view(i0,i1,i2,i3,i4,i5) = unaryOperator(view(i0,i1,i2,i3,i4,i5));
328  });
329  }
330  break;
331  case 7:
332  {
333  const int dataRank = 7;
334  auto policy6 = dataExtentRangePolicy<6>();
335  auto view = getUnderlyingView<dataRank>();
336 
337  const int dim_i6 = view.extent_int(6);
338 
339  Kokkos::parallel_for("apply operator in-place", policy6,
340  KOKKOS_LAMBDA (const int &i0, const int &i1, const int &i2, const int &i3, const int &i4, const int &i5) {
341  for (int i6=0; i6<dim_i6; i6++)
342  {
343  view(i0,i1,i2,i3,i4,i5,i6) = unaryOperator(view(i0,i1,i2,i3,i4,i5,i6));
344  }
345  });
346  }
347  break;
348  default:
349  INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Unsupported data rank");
350  }
351  }
352 
354  template<class ...IntArgs>
355  KOKKOS_INLINE_FUNCTION
356  reference_type getWritableEntryWithPassThroughOption(const bool &passThroughBlockDiagonalArgs, const IntArgs... intArgs) const
357  {
358  #ifdef INTREPID2_HAVE_DEBUG
359  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(numArgs != rank_, std::invalid_argument, "getWritableEntry() should have the same number of arguments as the logical rank.");
360  #endif
361  constexpr int numArgs = sizeof...(intArgs);
362  if (underlyingMatchesLogical_)
363  {
364  // in this case, we require that numArgs == dataRank_
365  return getUnderlyingView<numArgs>()(intArgs...);
366  }
367 
368  // extract the type of the first argument; use that for the arrays below
369  using int_type = std::tuple_element_t<0, std::tuple<IntArgs...>>;
370 
371  const Kokkos::Array<int_type, numArgs+1> args {intArgs...,0}; // we pad with one extra entry (0) to avoid gcc compiler warnings about references beyond the bounds of the array (the [d+1]'s below)
372  Kokkos::Array<int_type, 7> refEntry;
373  for (int d=0; d<numArgs; d++)
374  {
375  switch (variationType_[d])
376  {
377  case CONSTANT: refEntry[d] = 0; break;
378  case GENERAL: refEntry[d] = args[d]; break;
379  case MODULAR: refEntry[d] = args[d] % variationModulus_[d]; break;
380  case BLOCK_PLUS_DIAGONAL:
381  {
382  if (passThroughBlockDiagonalArgs)
383  {
384  refEntry[d] = args[d];
385  refEntry[d+1] = args[d+1]; // this had better be == 0
386  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(args[d+1] != 0, std::invalid_argument, "getWritableEntry() called with passThroughBlockDiagonalArgs = true, but nonzero second matrix argument.");
387  }
388  else
389  {
390  const int numNondiagonalEntries = blockPlusDiagonalNumNondiagonalEntries(blockPlusDiagonalLastNonDiagonal_);
391 
392  const int_type &i = args[d];
393  if (d+1 >= numArgs)
394  {
395  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::invalid_argument, "BLOCK_PLUS_DIAGONAL must be present for two dimensions; here, encountered only one");
396  }
397  else
398  {
399  const int_type &j = args[d+1];
400 
401  if ((i > static_cast<int_type>(blockPlusDiagonalLastNonDiagonal_)) || (j > static_cast<int_type>(blockPlusDiagonalLastNonDiagonal_)))
402  {
403  if (i != j)
404  {
405  // off diagonal: zero
406  return zeroView_(0); // NOTE: this branches in an argument-dependent way; this is not great for CUDA performance. When using BLOCK_PLUS_DIAGONAL, should generally avoid calls to this getEntry() method. (Use methods that directly take advantage of the data packing instead.)
407  }
408  else
409  {
410  refEntry[d] = blockPlusDiagonalDiagonalEntryIndex(blockPlusDiagonalLastNonDiagonal_, numNondiagonalEntries, i);
411  }
412  }
413  else
414  {
415  refEntry[d] = blockPlusDiagonalBlockEntryIndex(blockPlusDiagonalLastNonDiagonal_, numNondiagonalEntries, i, j);
416  }
417 
418  // skip next d (this is required also to be BLOCK_PLUS_DIAGONAL, and we've consumed its arg as j above)
419  refEntry[d+1] = 0;
420  }
421  }
422  d++;
423  }
424  }
425  }
426  // refEntry should be zero-filled beyond numArgs, for cases when rank_ < dataRank_ (this only is allowed if the extra dimensions each has extent 1).
427  for (int d=numArgs; d<7; d++)
428  {
429  refEntry[d] = 0;
430  }
431 
432  if (dataRank_ == 1)
433  {
434  return data1_(refEntry[activeDims_[0]]);
435  }
436  else if (dataRank_ == 2)
437  {
438  return data2_(refEntry[activeDims_[0]],refEntry[activeDims_[1]]);
439  }
440  else if (dataRank_ == 3)
441  {
442  return data3_(refEntry[activeDims_[0]],refEntry[activeDims_[1]],refEntry[activeDims_[2]]);
443  }
444  else if (dataRank_ == 4)
445  {
446  return data4_(refEntry[activeDims_[0]],refEntry[activeDims_[1]],refEntry[activeDims_[2]],refEntry[activeDims_[3]]);
447  }
448  else if (dataRank_ == 5)
449  {
450  return data5_(refEntry[activeDims_[0]],refEntry[activeDims_[1]],refEntry[activeDims_[2]],refEntry[activeDims_[3]],
451  refEntry[activeDims_[4]]);
452  }
453  else if (dataRank_ == 6)
454  {
455  return data6_(refEntry[activeDims_[0]],refEntry[activeDims_[1]],refEntry[activeDims_[2]],refEntry[activeDims_[3]],
456  refEntry[activeDims_[4]],refEntry[activeDims_[5]]);
457  }
458  else // dataRank_ == 7
459  {
460  return data7_(refEntry[activeDims_[0]],refEntry[activeDims_[1]],refEntry[activeDims_[2]],refEntry[activeDims_[3]],
461  refEntry[activeDims_[4]],refEntry[activeDims_[5]],refEntry[activeDims_[6]]);
462  }
463 
464  }
465 
467  template<class ...IntArgs>
468  KOKKOS_INLINE_FUNCTION
469  reference_type getWritableEntry(const IntArgs... intArgs) const
470  {
471  return getWritableEntryWithPassThroughOption(false, intArgs...);
472  }
473  public:
475  template<class ToContainer, class FromContainer>
476  static void copyContainer(ToContainer to, FromContainer from)
477  {
478 // std::cout << "Entered copyContainer().\n";
479  auto policy = Kokkos::MDRangePolicy<execution_space,Kokkos::Rank<6>>({0,0,0,0,0,0},{from.extent_int(0),from.extent_int(1),from.extent_int(2), from.extent_int(3), from.extent_int(4), from.extent_int(5)});
480 
481  Kokkos::parallel_for("copyContainer", policy,
482  KOKKOS_LAMBDA (const int &i0, const int &i1, const int &i2, const int &i3, const int &i4, const int &i5) {
483  for (int i6=0; i6<from.extent_int(6); i6++)
484  {
485  to.access(i0,i1,i2,i3,i4,i5,i6) = from.access(i0,i1,i2,i3,i4,i5,i6);
486  }
487  });
488  }
489 
491  void allocateAndCopyFromDynRankView(ScalarView<DataScalar,DeviceType> data)
492  {
493 // std::cout << "Entered allocateAndCopyFromDynRankView().\n";
494  switch (dataRank_)
495  {
496  case 1: data1_ = Kokkos::View<DataScalar*, DeviceType>("Intrepid2 Data", data.extent_int(0)); break;
497  case 2: data2_ = Kokkos::View<DataScalar**, DeviceType>("Intrepid2 Data", data.extent_int(0), data.extent_int(1)); break;
498  case 3: data3_ = Kokkos::View<DataScalar***, DeviceType>("Intrepid2 Data", data.extent_int(0), data.extent_int(1), data.extent_int(2)); break;
499  case 4: data4_ = Kokkos::View<DataScalar****, DeviceType>("Intrepid2 Data", data.extent_int(0), data.extent_int(1), data.extent_int(2), data.extent_int(3)); break;
500  case 5: data5_ = Kokkos::View<DataScalar*****, DeviceType>("Intrepid2 Data", data.extent_int(0), data.extent_int(1), data.extent_int(2), data.extent_int(3), data.extent_int(4)); break;
501  case 6: data6_ = Kokkos::View<DataScalar******, DeviceType>("Intrepid2 Data", data.extent_int(0), data.extent_int(1), data.extent_int(2), data.extent_int(3), data.extent_int(4), data.extent_int(5)); break;
502  case 7: data7_ = Kokkos::View<DataScalar*******, DeviceType>("Intrepid2 Data", data.extent_int(0), data.extent_int(1), data.extent_int(2), data.extent_int(3), data.extent_int(4), data.extent_int(5), data.extent_int(6)); break;
503  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
504  }
505 
506  switch (dataRank_)
507  {
508  case 1: copyContainer(data1_,data); break;
509  case 2: copyContainer(data2_,data); break;
510  case 3: copyContainer(data3_,data); break;
511  case 4: copyContainer(data4_,data); break;
512  case 5: copyContainer(data5_,data); break;
513  case 6: copyContainer(data6_,data); break;
514  case 7: copyContainer(data7_,data); break;
515  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
516  }
517  }
518 
520  Data(std::vector<DimensionInfo> dimInfoVector)
521  :
522  // initialize member variables as if default constructor; if dimInfoVector is empty, we want default constructor behavior.
523  dataRank_(0), extents_({0,0,0,0,0,0,0}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(-1), rank_(dimInfoVector.size())
524  {
525  // If dimInfoVector is empty, the member initialization above is correct; otherwise, we set as below.
526  // Either way, once members are initialized, we must call setActiveDims().
527  if (dimInfoVector.size() != 0)
528  {
529  std::vector<int> dataExtents;
530 
531  bool blockPlusDiagonalEncountered = false;
532  for (int d=0; d<rank_; d++)
533  {
534  const DimensionInfo & dimInfo = dimInfoVector[d];
535  extents_[d] = dimInfo.logicalExtent;
536  variationType_[d] = dimInfo.variationType;
537  const bool isBlockPlusDiagonal = (variationType_[d] == BLOCK_PLUS_DIAGONAL);
538  const bool isSecondBlockPlusDiagonal = isBlockPlusDiagonal && blockPlusDiagonalEncountered;
539  if (isBlockPlusDiagonal)
540  {
541  blockPlusDiagonalEncountered = true;
542  blockPlusDiagonalLastNonDiagonal_ = dimInfo.blockPlusDiagonalLastNonDiagonal;
543  }
544  if ((variationType_[d] != CONSTANT) && (!isSecondBlockPlusDiagonal))
545  {
546  dataExtents.push_back(dimInfo.dataExtent);
547  }
548  }
549  if (dataExtents.size() == 0)
550  {
551  // constant data
552  dataExtents.push_back(1);
553  }
554  dataRank_ = dataExtents.size();
555  switch (dataRank_)
556  {
557  case 1: data1_ = Kokkos::View<DataScalar*, DeviceType>("Intrepid2 Data", dataExtents[0]); break;
558  case 2: data2_ = Kokkos::View<DataScalar**, DeviceType>("Intrepid2 Data", dataExtents[0], dataExtents[1]); break;
559  case 3: data3_ = Kokkos::View<DataScalar***, DeviceType>("Intrepid2 Data", dataExtents[0], dataExtents[1], dataExtents[2]); break;
560  case 4: data4_ = Kokkos::View<DataScalar****, DeviceType>("Intrepid2 Data", dataExtents[0], dataExtents[1], dataExtents[2], dataExtents[3]); break;
561  case 5: data5_ = Kokkos::View<DataScalar*****, DeviceType>("Intrepid2 Data", dataExtents[0], dataExtents[1], dataExtents[2], dataExtents[3], dataExtents[4]); break;
562  case 6: data6_ = Kokkos::View<DataScalar******, DeviceType>("Intrepid2 Data", dataExtents[0], dataExtents[1], dataExtents[2], dataExtents[3], dataExtents[4], dataExtents[5]); break;
563  case 7: data7_ = Kokkos::View<DataScalar*******, DeviceType>("Intrepid2 Data", dataExtents[0], dataExtents[1], dataExtents[2], dataExtents[3], dataExtents[4], dataExtents[5], dataExtents[6]); break;
564  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
565  }
566  }
567  setActiveDims();
568  }
569 
571  Data(const ScalarView<DataScalar,DeviceType> &data, int rank, Kokkos::Array<int,7> extents, Kokkos::Array<DataVariationType,7> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
572  :
573  dataRank_(data.rank()), extents_(extents), variationType_(variationType), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
574  {
576  setActiveDims();
577  }
578 
580  template<typename OtherDeviceType, class = typename std::enable_if< std::is_same<typename DeviceType::memory_space, typename OtherDeviceType::memory_space>::value>::type,
581  class = typename std::enable_if<!std::is_same<DeviceType,OtherDeviceType>::value>::type>
582  Data(const Data<DataScalar,OtherDeviceType> &data)
583  :
584  dataRank_(data.getUnderlyingViewRank()), extents_(data.getExtents()), variationType_(data.getVariationTypes()), blockPlusDiagonalLastNonDiagonal_(data.blockPlusDiagonalLastNonDiagonal()), rank_(data.rank())
585  {
586 // std::cout << "Entered copy-like Data constructor.\n";
587  if (dataRank_ != 0) // dataRank_ == 0 indicates an invalid Data object (a placeholder, can indicate zero value)
588  {
589  const auto view = data.getUnderlyingView();
590  switch (dataRank_)
591  {
592  case 1: data1_ = data.getUnderlyingView1(); break;
593  case 2: data2_ = data.getUnderlyingView2(); break;
594  case 3: data3_ = data.getUnderlyingView3(); break;
595  case 4: data4_ = data.getUnderlyingView4(); break;
596  case 5: data5_ = data.getUnderlyingView5(); break;
597  case 6: data6_ = data.getUnderlyingView6(); break;
598  case 7: data7_ = data.getUnderlyingView7(); break;
599  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
600  }
601  }
602  setActiveDims();
603  }
604 
606  template<typename OtherDeviceType, class = typename std::enable_if<!std::is_same<typename DeviceType::memory_space, typename OtherDeviceType::memory_space>::value>::type>
608  :
609  dataRank_(data.getUnderlyingViewRank()), extents_(data.getExtents()), variationType_(data.getVariationTypes()), blockPlusDiagonalLastNonDiagonal_(data.blockPlusDiagonalLastNonDiagonal()), rank_(data.rank())
610  {
611 // std::cout << "Entered copy-like Data constructor.\n";
612  if (dataRank_ != 0) // dataRank_ == 0 indicates an invalid Data object (a placeholder, can indicate zero value)
613  {
614  const auto view = data.getUnderlyingView();
615  switch (dataRank_)
616  {
617  case 1: data1_ = Kokkos::View<DataScalar*, DeviceType>("Intrepid2 Data", view.extent_int(0)); break;
618  case 2: data2_ = Kokkos::View<DataScalar**, DeviceType>("Intrepid2 Data", view.extent_int(0), view.extent_int(1)); break;
619  case 3: data3_ = Kokkos::View<DataScalar***, DeviceType>("Intrepid2 Data", view.extent_int(0), view.extent_int(1), view.extent_int(2)); break;
620  case 4: data4_ = Kokkos::View<DataScalar****, DeviceType>("Intrepid2 Data", view.extent_int(0), view.extent_int(1), view.extent_int(2), view.extent_int(3)); break;
621  case 5: data5_ = Kokkos::View<DataScalar*****, DeviceType>("Intrepid2 Data", view.extent_int(0), view.extent_int(1), view.extent_int(2), view.extent_int(3), view.extent_int(4)); break;
622  case 6: data6_ = Kokkos::View<DataScalar******, DeviceType>("Intrepid2 Data", view.extent_int(0), view.extent_int(1), view.extent_int(2), view.extent_int(3), view.extent_int(4), view.extent_int(5)); break;
623  case 7: data7_ = Kokkos::View<DataScalar*******, DeviceType>("Intrepid2 Data", view.extent_int(0), view.extent_int(1), view.extent_int(2), view.extent_int(3), view.extent_int(4), view.extent_int(5), view.extent_int(6)); break;
624  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
625  }
626 
627  // copy
628  // (Note: Kokkos::deep_copy() will not generally work if the layouts are different; that's why we do a manual copy here once we have the data on the host):
629  // first, mirror and copy dataView; then copy to the appropriate data_ member
630  using MemorySpace = typename DeviceType::memory_space;
631  switch (dataRank_)
632  {
633  case 1: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView1()); copyContainer(data1_, dataViewMirror);} break;
634  case 2: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView2()); copyContainer(data2_, dataViewMirror);} break;
635  case 3: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView3()); copyContainer(data3_, dataViewMirror);} break;
636  case 4: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView4()); copyContainer(data4_, dataViewMirror);} break;
637  case 5: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView5()); copyContainer(data5_, dataViewMirror);} break;
638  case 6: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView6()); copyContainer(data6_, dataViewMirror);} break;
639  case 7: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView7()); copyContainer(data7_, dataViewMirror);} break;
640  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
641  }
642  }
643  setActiveDims();
644  }
645 
647 // Data(const Data<DataScalar,ExecSpaceType> &data)
648 // :
649 // dataRank_(data.getUnderlyingViewRank()), extents_(data.getExtents()), variationType_(data.getVariationTypes()), blockPlusDiagonalLastNonDiagonal_(data.blockPlusDiagonalLastNonDiagonal()), rank_(data.rank())
650 // {
651 // std::cout << "Entered Data copy constructor.\n";
652 // if (dataRank_ != 0) // dataRank_ == 0 indicates an invalid Data object (a placeholder, can indicate zero value)
653 // {
654 // const auto view = data.getUnderlyingView();
655 // switch (dataRank_)
656 // {
657 // case 1: data1_ = Kokkos::View<DataScalar*, DeviceType>("Intrepid2 Data - explicit copy constructor(for debugging)", view.extent_int(0)); break;
658 // case 2: data2_ = Kokkos::View<DataScalar**, DeviceType>("Intrepid2 Data - explicit copy constructor(for debugging)", view.extent_int(0), view.extent_int(1)); break;
659 // case 3: data3_ = Kokkos::View<DataScalar***, DeviceType>("Intrepid2 Data - explicit copy constructor(for debugging)", view.extent_int(0), view.extent_int(1), view.extent_int(2)); break;
660 // case 4: data4_ = Kokkos::View<DataScalar****, DeviceType>("Intrepid2 Data - explicit copy constructor(for debugging)", view.extent_int(0), view.extent_int(1), view.extent_int(2), view.extent_int(3)); break;
661 // case 5: data5_ = Kokkos::View<DataScalar*****, DeviceType>("Intrepid2 Data - explicit copy constructor(for debugging)", view.extent_int(0), view.extent_int(1), view.extent_int(2), view.extent_int(3), view.extent_int(4)); break;
662 // case 6: data6_ = Kokkos::View<DataScalar******, DeviceType>("Intrepid2 Data - explicit copy constructor(for debugging)", view.extent_int(0), view.extent_int(1), view.extent_int(2), view.extent_int(3), view.extent_int(4), view.extent_int(5)); break;
663 // case 7: data7_ = Kokkos::View<DataScalar*******, DeviceType>("Intrepid2 Data - explicit copy constructor(for debugging)", view.extent_int(0), view.extent_int(1), view.extent_int(2), view.extent_int(3), view.extent_int(4), view.extent_int(5), view.extent_int(6)); break;
664 // default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
665 // }
666 //
667 // // copy
668 // // (Note: Kokkos::deep_copy() will not generally work if the layouts are different; that's why we do a manual copy here once we have the data on the host):
669 // // first, mirror and copy dataView; then copy to the appropriate data_ member
670 // using MemorySpace = typename DeviceType::memory_space;
671 // switch (dataRank_)
672 // {
673 // case 1: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView1()); copyContainer(data1_, dataViewMirror);} break;
674 // case 2: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView2()); copyContainer(data2_, dataViewMirror);} break;
675 // case 3: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView3()); copyContainer(data3_, dataViewMirror);} break;
676 // case 4: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView4()); copyContainer(data4_, dataViewMirror);} break;
677 // case 5: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView5()); copyContainer(data5_, dataViewMirror);} break;
678 // case 6: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView6()); copyContainer(data6_, dataViewMirror);} break;
679 // case 7: {auto dataViewMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), data.getUnderlyingView7()); copyContainer(data7_, dataViewMirror);} break;
680 // default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
681 // }
682 // }
683 //
684 // setActiveDims();
685 // }
686 
688  Data(ScalarView<DataScalar,DeviceType> data)
689  :
690  Data(data,
691  data.rank(),
692  Kokkos::Array<int,7> {data.extent_int(0),data.extent_int(1),data.extent_int(2),data.extent_int(3),data.extent_int(4),data.extent_int(5),data.extent_int(6)},
693  Kokkos::Array<DataVariationType,7> {GENERAL,GENERAL,GENERAL,GENERAL,GENERAL,GENERAL,GENERAL}, -1)
694  {}
695 
697  template<size_t rank, class ...DynRankViewProperties>
698  Data(const Kokkos::DynRankView<DataScalar,DeviceType, DynRankViewProperties...> &data, Kokkos::Array<int,rank> extents, Kokkos::Array<DataVariationType,rank> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
699  :
700  dataRank_(data.rank()), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
701  {
702 // std::cout << "Entered a DynRankView Data() constructor.\n";
704  for (unsigned d=0; d<rank; d++)
705  {
706  extents_[d] = extents[d];
707  variationType_[d] = variationType[d];
708  }
709  setActiveDims();
710  }
711 
712  template<size_t rank, class ...ViewProperties>
713  Data(Kokkos::View<DataScalar*,DeviceType, ViewProperties...> data, Kokkos::Array<int,rank> extents, Kokkos::Array<DataVariationType,rank> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
714  :
715  dataRank_(data.rank), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
716  {
717  data1_ = data;
718  for (unsigned d=0; d<rank; d++)
719  {
720  extents_[d] = extents[d];
721  variationType_[d] = variationType[d];
722  }
723  setActiveDims();
724  }
725 
726  template<size_t rank, class ...ViewProperties>
727  Data(Kokkos::View<DataScalar**,DeviceType, ViewProperties...> data, Kokkos::Array<int,rank> extents, Kokkos::Array<DataVariationType,rank> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
728  :
729  dataRank_(data.rank), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
730  {
731  data2_ = data;
732  for (unsigned d=0; d<rank; d++)
733  {
734  extents_[d] = extents[d];
735  variationType_[d] = variationType[d];
736  }
737  setActiveDims();
738  }
739 
740  template<size_t rank, class ...ViewProperties>
741  Data(Kokkos::View<DataScalar***,DeviceType, ViewProperties...> data, Kokkos::Array<int,rank> extents, Kokkos::Array<DataVariationType,rank> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
742  :
743  dataRank_(data.rank), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
744  {
745  data3_ = data;
746  for (unsigned d=0; d<rank; d++)
747  {
748  extents_[d] = extents[d];
749  variationType_[d] = variationType[d];
750  }
751  setActiveDims();
752  }
753 
754  template<size_t rank, class ...ViewProperties>
755  Data(Kokkos::View<DataScalar****,DeviceType, ViewProperties...> data, Kokkos::Array<int,rank> extents, Kokkos::Array<DataVariationType,rank> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
756  :
757  dataRank_(data.rank), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
758  {
759  data4_ = data;
760  for (unsigned d=0; d<rank; d++)
761  {
762  extents_[d] = extents[d];
763  variationType_[d] = variationType[d];
764  }
765  setActiveDims();
766  }
767 
768  template<size_t rank, class ...ViewProperties>
769  Data(Kokkos::View<DataScalar*****,DeviceType, ViewProperties...> data, Kokkos::Array<int,rank> extents, Kokkos::Array<DataVariationType,rank> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
770  :
771  dataRank_(data.rank), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
772  {
773  data5_ = data;
774  for (unsigned d=0; d<rank; d++)
775  {
776  extents_[d] = extents[d];
777  variationType_[d] = variationType[d];
778  }
779  setActiveDims();
780  }
781 
782  template<size_t rank, class ...ViewProperties>
783  Data(Kokkos::View<DataScalar******,DeviceType, ViewProperties...> data, Kokkos::Array<int,rank> extents, Kokkos::Array<DataVariationType,rank> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
784  :
785  dataRank_(data.rank), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
786  {
787  data6_ = data;
788  for (unsigned d=0; d<rank; d++)
789  {
790  extents_[d] = extents[d];
791  variationType_[d] = variationType[d];
792  }
793  setActiveDims();
794  }
795 
796  template<size_t rank, class ...ViewProperties>
797  Data(Kokkos::View<DataScalar*******,DeviceType, ViewProperties...> data, Kokkos::Array<int,rank> extents, Kokkos::Array<DataVariationType,rank> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
798  :
799  dataRank_(data.rank), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
800  {
801  data7_ = data;
802  for (unsigned d=0; d<rank; d++)
803  {
804  extents_[d] = extents[d];
805  variationType_[d] = variationType[d];
806  }
807  setActiveDims();
808  }
809 
811  template<class ViewScalar, class ...ViewProperties>
812  Data(const unsigned rank, Kokkos::View<ViewScalar,DeviceType, ViewProperties...> data, Kokkos::Array<int,7> extents, Kokkos::Array<DataVariationType,7> variationType, const int blockPlusDiagonalLastNonDiagonal = -1)
813  :
814  dataRank_(data.rank), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(blockPlusDiagonalLastNonDiagonal), rank_(rank)
815  {
816  setUnderlyingView<data.rank>(data);
817  for (unsigned d=0; d<rank; d++)
818  {
819  extents_[d] = extents[d];
820  variationType_[d] = variationType[d];
821  }
822  setActiveDims();
823  }
824 
826  template<size_t rank>
827  Data(DataScalar constantValue, Kokkos::Array<int,rank> extents)
828  :
829  dataRank_(1), extents_({1,1,1,1,1,1,1}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(-1), rank_(rank)
830  {
831  data1_ = Kokkos::View<DataScalar*,DeviceType>("Constant Data",1);
832  Kokkos::deep_copy(data1_, constantValue);
833  for (unsigned d=0; d<rank; d++)
834  {
835  extents_[d] = extents[d];
836  }
837  setActiveDims();
838  }
839 
842  :
843  dataRank_(0), extents_({0,0,0,0,0,0,0}), variationType_({CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT,CONSTANT}), blockPlusDiagonalLastNonDiagonal_(-1), rank_(0)
844  {
845  setActiveDims();
846  }
847 
849  KOKKOS_INLINE_FUNCTION
851  {
852  return blockPlusDiagonalLastNonDiagonal_;
853  }
854 
856  KOKKOS_INLINE_FUNCTION
857  Kokkos::Array<int,7> getExtents() const
858  {
859  return extents_;
860  }
861 
863  KOKKOS_INLINE_FUNCTION
864  DimensionInfo getDimensionInfo(const int &dim) const
865  {
866  DimensionInfo dimInfo;
867 
868  dimInfo.logicalExtent = extent_int(dim);
869  dimInfo.variationType = variationType_[dim];
870  dimInfo.dataExtent = getDataExtent(dim);
871  dimInfo.variationModulus = variationModulus_[dim];
872 
873  if (dimInfo.variationType == BLOCK_PLUS_DIAGONAL)
874  {
875  dimInfo.blockPlusDiagonalLastNonDiagonal = blockPlusDiagonalLastNonDiagonal_;
876  }
877  return dimInfo;
878  }
879 
881  KOKKOS_INLINE_FUNCTION
882  DimensionInfo combinedDataDimensionInfo(const Data &otherData, const int &dim) const
883  {
884  const DimensionInfo myDimInfo = getDimensionInfo(dim);
885  const DimensionInfo otherDimInfo = otherData.getDimensionInfo(dim);
886 
887  return combinedDimensionInfo(myDimInfo, otherDimInfo);
888  }
889 
891  template<int rank>
892  KOKKOS_INLINE_FUNCTION
893  enable_if_t<rank==1, const Kokkos::View<typename RankExpander<DataScalar, rank>::value_type, DeviceType> &>
895  {
896  #ifdef HAVE_INTREPID2_DEBUG
897  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(dataRank_ != rank, std::invalid_argument, "getUnderlyingView() called for rank that does not match dataRank_");
898  #endif
899  return data1_;
900  }
901 
903  template<int rank>
904  KOKKOS_INLINE_FUNCTION
905  enable_if_t<rank==2, const Kokkos::View<typename RankExpander<DataScalar, rank>::value_type, DeviceType> &>
907  {
908  #ifdef HAVE_INTREPID2_DEBUG
909  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(dataRank_ != rank, std::invalid_argument, "getUnderlyingView() called for rank that does not match dataRank_");
910  #endif
911  return data2_;
912  }
913 
915  template<int rank>
916  KOKKOS_INLINE_FUNCTION
917  enable_if_t<rank==3, const Kokkos::View<typename RankExpander<DataScalar, rank>::value_type, DeviceType> &>
919  {
920  #ifdef HAVE_INTREPID2_DEBUG
921  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(dataRank_ != rank, std::invalid_argument, "getUnderlyingView() called for rank that does not match dataRank_");
922  #endif
923  return data3_;
924  }
925 
927  template<int rank>
928  KOKKOS_INLINE_FUNCTION
929  enable_if_t<rank==4, const Kokkos::View<typename RankExpander<DataScalar, rank>::value_type, DeviceType> &>
931  {
932  #ifdef HAVE_INTREPID2_DEBUG
933  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(dataRank_ != rank, std::invalid_argument, "getUnderlyingView() called for rank that does not match dataRank_");
934  #endif
935  return data4_;
936  }
937 
939  template<int rank>
940  KOKKOS_INLINE_FUNCTION
941  enable_if_t<rank==5, const Kokkos::View<typename RankExpander<DataScalar, rank>::value_type, DeviceType> &>
943  {
944  #ifdef HAVE_INTREPID2_DEBUG
945  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(dataRank_ != rank, std::invalid_argument, "getUnderlyingView() called for rank that does not match dataRank_");
946  #endif
947  return data5_;
948  }
949 
951  template<int rank>
952  KOKKOS_INLINE_FUNCTION
953  enable_if_t<rank==6, const Kokkos::View<typename RankExpander<DataScalar, rank>::value_type, DeviceType> &>
955  {
956  #ifdef HAVE_INTREPID2_DEBUG
957  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(dataRank_ != rank, std::invalid_argument, "getUnderlyingView() called for rank that does not match dataRank_");
958  #endif
959  return data6_;
960  }
961 
963  template<int rank>
964  KOKKOS_INLINE_FUNCTION
965  enable_if_t<rank==7, const Kokkos::View<typename RankExpander<DataScalar, rank>::value_type, DeviceType> &>
967  {
968  #ifdef HAVE_INTREPID2_DEBUG
969  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(dataRank_ != rank, std::invalid_argument, "getUnderlyingView() called for rank that does not match dataRank_");
970  #endif
971  return data7_;
972  }
973 
975  KOKKOS_INLINE_FUNCTION
976  const Kokkos::View<DataScalar*, DeviceType> & getUnderlyingView1() const
977  {
978  return getUnderlyingView<1>();
979  }
980 
982  KOKKOS_INLINE_FUNCTION
983  const Kokkos::View<DataScalar**, DeviceType> & getUnderlyingView2() const
984  {
985  return getUnderlyingView<2>();
986  }
987 
989  KOKKOS_INLINE_FUNCTION
990  const Kokkos::View<DataScalar***, DeviceType> & getUnderlyingView3() const
991  {
992  return getUnderlyingView<3>();
993  }
994 
996  KOKKOS_INLINE_FUNCTION
997  const Kokkos::View<DataScalar****, DeviceType> & getUnderlyingView4() const
998  {
999  return getUnderlyingView<4>();
1000  }
1001 
1003  KOKKOS_INLINE_FUNCTION
1004  const Kokkos::View<DataScalar*****, DeviceType> & getUnderlyingView5() const
1005  {
1006  return getUnderlyingView<5>();
1007  }
1008 
1010  KOKKOS_INLINE_FUNCTION
1011  const Kokkos::View<DataScalar******, DeviceType> & getUnderlyingView6() const
1012  {
1013  return getUnderlyingView<6>();
1014  }
1015 
1017  KOKKOS_INLINE_FUNCTION
1018  const Kokkos::View<DataScalar*******, DeviceType> & getUnderlyingView7() const
1019  {
1020  return getUnderlyingView<7>();
1021  }
1022 
1024  KOKKOS_INLINE_FUNCTION
1025  void setUnderlyingView1(const Kokkos::View<DataScalar*, DeviceType> & view)
1026  {
1027  data1_ = view;
1028  }
1029 
1031  KOKKOS_INLINE_FUNCTION
1032  void setUnderlyingView2(const Kokkos::View<DataScalar**, DeviceType> & view)
1033  {
1034  data2_ = view;
1035  }
1036 
1038  KOKKOS_INLINE_FUNCTION
1039  void setUnderlyingView3(const Kokkos::View<DataScalar***, DeviceType> & view)
1040  {
1041  data3_ = view;
1042  }
1043 
1045  KOKKOS_INLINE_FUNCTION
1046  void setUnderlyingView4(const Kokkos::View<DataScalar****, DeviceType> & view)
1047  {
1048  data4_ = view;
1049  }
1050 
1052  KOKKOS_INLINE_FUNCTION
1053  void setUnderlyingView5(const Kokkos::View<DataScalar*****, DeviceType> & view)
1054  {
1055  data5_ = view;
1056  }
1057 
1059  KOKKOS_INLINE_FUNCTION
1060  void setUnderlyingView6(const Kokkos::View<DataScalar******, DeviceType> & view)
1061  {
1062  data6_ = view;
1063  }
1064 
1066  KOKKOS_INLINE_FUNCTION
1067  void setUnderlyingView7(const Kokkos::View<DataScalar*******, DeviceType> & view)
1068  {
1069  data7_ = view;
1070  }
1071 
1072  template<int underlyingRank, class ViewScalar>
1073  KOKKOS_INLINE_FUNCTION
1074  void setUnderlyingView(const Kokkos::View<ViewScalar, DeviceType> & view)
1075  {
1076  if constexpr (underlyingRank == 1)
1077  {
1078  setUnderlyingView1(view);
1079  }
1080  else if constexpr (underlyingRank == 2)
1081  {
1082  setUnderlyingView2(view);
1083  }
1084  else if constexpr (underlyingRank == 3)
1085  {
1086  setUnderlyingView3(view);
1087  }
1088  else if constexpr (underlyingRank == 4)
1089  {
1090  setUnderlyingView4(view);
1091  }
1092  else if constexpr (underlyingRank == 5)
1093  {
1094  setUnderlyingView5(view);
1095  }
1096  else if constexpr (underlyingRank == 6)
1097  {
1098  setUnderlyingView6(view);
1099  }
1100  else if constexpr (underlyingRank == 7)
1101  {
1102  setUnderlyingView7(view);
1103  }
1104  else
1105  {
1106  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::invalid_argument, "implementation for specialization missing");
1107  }
1108  }
1109 
1111  ScalarView<DataScalar,DeviceType> getUnderlyingView() const
1112  {
1113  switch (dataRank_)
1114  {
1115  case 1: return data1_;
1116  case 2: return data2_;
1117  case 3: return data3_;
1118  case 4: return data4_;
1119  case 5: return data5_;
1120  case 6: return data6_;
1121  case 7: return data7_;
1122  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
1123  }
1124  }
1125 
1127  KOKKOS_INLINE_FUNCTION
1128  ordinal_type getUnderlyingViewRank() const
1129  {
1130  return dataRank_;
1131  }
1132 
1134  KOKKOS_INLINE_FUNCTION
1135  ordinal_type getUnderlyingViewSize() const
1136  {
1137  ordinal_type size = 1;
1138  for (ordinal_type r=0; r<dataRank_; r++)
1139  {
1140  size *= getUnderlyingViewExtent(r);
1141  }
1142  return size;
1143  }
1144 
1146  ScalarView<DataScalar,DeviceType> allocateDynRankViewMatchingUnderlying() const
1147  {
1148  switch (dataRank_)
1149  {
1150  case 1: return getMatchingViewWithLabel(data1_, "Intrepid2 Data", data1_.extent_int(0));
1151  case 2: return getMatchingViewWithLabel(data2_, "Intrepid2 Data", data2_.extent_int(0), data2_.extent_int(1));
1152  case 3: return getMatchingViewWithLabel(data3_, "Intrepid2 Data", data3_.extent_int(0), data3_.extent_int(1), data3_.extent_int(2));
1153  case 4: return getMatchingViewWithLabel(data4_, "Intrepid2 Data", data4_.extent_int(0), data4_.extent_int(1), data4_.extent_int(2), data4_.extent_int(3));
1154  case 5: return getMatchingViewWithLabel(data5_, "Intrepid2 Data", data5_.extent_int(0), data5_.extent_int(1), data5_.extent_int(2), data5_.extent_int(3), data5_.extent_int(4));
1155  case 6: return getMatchingViewWithLabel(data6_, "Intrepid2 Data", data6_.extent_int(0), data6_.extent_int(1), data6_.extent_int(2), data6_.extent_int(3), data6_.extent_int(4), data6_.extent_int(5));
1156  case 7: return getMatchingViewWithLabel(data7_, "Intrepid2 Data", data7_.extent_int(0), data7_.extent_int(1), data7_.extent_int(2), data7_.extent_int(3), data7_.extent_int(4), data7_.extent_int(5), data7_.extent_int(6));
1157  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
1158  }
1159  }
1160 
1162  template<class ... DimArgs>
1163  ScalarView<DataScalar,DeviceType> allocateDynRankViewMatchingUnderlying(DimArgs... dims) const
1164  {
1165  switch (dataRank_)
1166  {
1167  case 1: return getMatchingViewWithLabel(data1_, "Intrepid2 Data", dims...);
1168  case 2: return getMatchingViewWithLabel(data2_, "Intrepid2 Data", dims...);
1169  case 3: return getMatchingViewWithLabel(data3_, "Intrepid2 Data", dims...);
1170  case 4: return getMatchingViewWithLabel(data4_, "Intrepid2 Data", dims...);
1171  case 5: return getMatchingViewWithLabel(data5_, "Intrepid2 Data", dims...);
1172  case 6: return getMatchingViewWithLabel(data6_, "Intrepid2 Data", dims...);
1173  case 7: return getMatchingViewWithLabel(data7_, "Intrepid2 Data", dims...);
1174  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
1175  }
1176  }
1177 
1179  void clear() const
1180  {
1181  switch (dataRank_)
1182  {
1183  case 1: Kokkos::deep_copy(data1_, 0.0); break;
1184  case 2: Kokkos::deep_copy(data2_, 0.0); break;
1185  case 3: Kokkos::deep_copy(data3_, 0.0); break;
1186  case 4: Kokkos::deep_copy(data4_, 0.0); break;
1187  case 5: Kokkos::deep_copy(data5_, 0.0); break;
1188  case 6: Kokkos::deep_copy(data6_, 0.0); break;
1189  case 7: Kokkos::deep_copy(data7_, 0.0); break;
1190  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
1191  }
1192  }
1193 
1195  void copyDataFromDynRankViewMatchingUnderlying(const ScalarView<DataScalar,DeviceType> &dynRankView) const
1196  {
1197 // std::cout << "Entered copyDataFromDynRankViewMatchingUnderlying().\n";
1198  switch (dataRank_)
1199  {
1200  case 1: copyContainer(data1_,dynRankView); break;
1201  case 2: copyContainer(data2_,dynRankView); break;
1202  case 3: copyContainer(data3_,dynRankView); break;
1203  case 4: copyContainer(data4_,dynRankView); break;
1204  case 5: copyContainer(data5_,dynRankView); break;
1205  case 6: copyContainer(data6_,dynRankView); break;
1206  case 7: copyContainer(data7_,dynRankView); break;
1207  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Invalid data rank");
1208  }
1209  }
1210 
1212  KOKKOS_INLINE_FUNCTION int getDataExtent(const ordinal_type &d) const
1213  {
1214  for (int i=0; i<numActiveDims_; i++)
1215  {
1216  if (activeDims_[i] == d)
1217  {
1218  return getUnderlyingViewExtent(i);
1219  }
1220  else if (activeDims_[i] > d)
1221  {
1222  return 1; // data does not vary in the specified dimension
1223  }
1224  }
1225  return 1; // data does not vary in the specified dimension
1226  }
1227 
1239  KOKKOS_INLINE_FUNCTION
1240  int getVariationModulus(const int &d) const
1241  {
1242  return variationModulus_[d];
1243  }
1244 
1246  KOKKOS_INLINE_FUNCTION
1247  const Kokkos::Array<DataVariationType,7> & getVariationTypes() const
1248  {
1249  return variationType_;
1250  }
1251 
1253  template<class ...IntArgs>
1254  KOKKOS_INLINE_FUNCTION
1255  return_type getEntryWithPassThroughOption(const bool &passThroughBlockDiagonalArgs, const IntArgs&... intArgs) const
1256  {
1257  return getWritableEntryWithPassThroughOption(passThroughBlockDiagonalArgs, intArgs...);
1258  }
1259 
1261  template<class ...IntArgs>
1262  KOKKOS_INLINE_FUNCTION
1263  return_type getEntry(const IntArgs&... intArgs) const
1264  {
1265  return getEntryWithPassThroughOption(false, intArgs...);
1266  }
1267 
1268  template <bool...> struct bool_pack;
1269 
1270  template <bool... v>
1271  using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;
1272 
1273  template <class ...IntArgs>
1274  using valid_args = all_true<std::is_integral<IntArgs>{}...>;
1275 
1276  static_assert(valid_args<int,long,unsigned>::value, "valid args works");
1277 
1279  template <class ...IntArgs>
1280  KOKKOS_INLINE_FUNCTION
1281 #ifndef __INTEL_COMPILER
1282  // icc has a bug that prevents compilation with this enable_if_t
1283  // (possibly the same as https://community.intel.com/t5/Intel-C-Compiler/Intel-Compiler-bug-while-deducing-template-arguments-inside/m-p/1164358)
1284  // so with icc we'll just skip the argument type/count check
1285  enable_if_t<valid_args<IntArgs...>::value && (sizeof...(IntArgs) <= 7),return_type>
1286 #else
1287  return_type
1288 #endif
1289  operator()(const IntArgs&... intArgs) const {
1290  return getEntry(intArgs...);
1291  }
1292 
1294  KOKKOS_INLINE_FUNCTION
1295  int extent_int(const int& r) const
1296  {
1297  return extents_[r];
1298  }
1299 
1300  template <typename iType>
1301  KOKKOS_INLINE_FUNCTION constexpr
1302  typename std::enable_if<std::is_integral<iType>::value, size_t>::type
1303  extent(const iType& r) const {
1304  return extents_[r];
1305  }
1306 
1308  KOKKOS_INLINE_FUNCTION bool isDiagonal() const
1309  {
1310  if (blockPlusDiagonalLastNonDiagonal_ >= 1) return false;
1311  int numBlockPlusDiagonalTypes = 0;
1312  for (unsigned r = 0; r<variationType_.size(); r++)
1313  {
1314  const auto &entryType = variationType_[r];
1315  if (entryType == BLOCK_PLUS_DIAGONAL) numBlockPlusDiagonalTypes++;
1316  }
1317  // 2 BLOCK_PLUS_DIAGONAL entries, combined with blockPlusDiagonalLastNonDiagonal being -1 or 0 indicates diagonal
1318  if (numBlockPlusDiagonalTypes == 2) return true;
1319  else if (numBlockPlusDiagonalTypes == 0) return false; // no BLOCK_PLUS_DIAGONAL --> not a diagonal matrix
1320  else INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::invalid_argument, "Unexpected number of ranks marked as BLOCK_PLUS_DIAGONAL (should be 0 or 2)");
1321  return false; // statement should be unreachable; included because compilers don't necessarily recognize that fact...
1322  }
1323 
1328  {
1329  return Data<DataScalar,DeviceType>(value, this->getExtents());
1330  }
1331 
1338  {
1339  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A.rank() != B.rank(), std::invalid_argument, "A and B must have the same logical shape");
1340  const int rank = A.rank();
1341  std::vector<DimensionInfo> dimInfo(rank);
1342  for (int d=0; d<rank; d++)
1343  {
1344  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A.extent_int(d) != B.extent_int(d), std::invalid_argument, "A and B must have the same logical shape");
1345  dimInfo[d] = A.combinedDataDimensionInfo(B, d);
1346  }
1347  Data<DataScalar,DeviceType> result(dimInfo);
1348  return result;
1349  }
1350 
1357  static Data<DataScalar,DeviceType> allocateMatMatResult( const bool transposeA, const Data<DataScalar,DeviceType> &A_MatData, const bool transposeB, const Data<DataScalar,DeviceType> &B_MatData )
1358  {
1359  // we treat last two logical dimensions of matData as the matrix; last dimension of vecData as the vector
1360  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A_MatData.rank() != B_MatData.rank(), std::invalid_argument, "AmatData and BmatData have incompatible ranks");
1361 
1362  const int D1_DIM = A_MatData.rank() - 2;
1363  const int D2_DIM = A_MatData.rank() - 1;
1364 
1365  const int A_rows = A_MatData.extent_int(D1_DIM);
1366  const int A_cols = A_MatData.extent_int(D2_DIM);
1367  const int B_rows = B_MatData.extent_int(D1_DIM);
1368  const int B_cols = B_MatData.extent_int(D2_DIM);
1369 
1370  const int leftRows = transposeA ? A_cols : A_rows;
1371  const int leftCols = transposeA ? A_rows : A_cols;
1372  const int rightRows = transposeB ? B_cols : B_rows;
1373  const int rightCols = transposeB ? B_rows : B_cols;
1374 
1375  INTREPID2_TEST_FOR_EXCEPTION(leftCols != rightRows, std::invalid_argument, "incompatible matrix dimensions");
1376 
1377  Kokkos::Array<int,7> resultExtents; // logical extents
1378  Kokkos::Array<DataVariationType,7> resultVariationTypes; // for each dimension, whether the data varies in that dimension
1379 
1380  resultExtents[D1_DIM] = leftRows;
1381  resultExtents[D2_DIM] = rightCols;
1382  int resultBlockPlusDiagonalLastNonDiagonal = -1;
1383  if ( (A_MatData.getVariationTypes()[D1_DIM] == BLOCK_PLUS_DIAGONAL) && (B_MatData.getVariationTypes()[D1_DIM] == BLOCK_PLUS_DIAGONAL) )
1384  {
1385  // diagonal times diagonal is diagonal; the result will have the maximum of A and B's non-diagonal block size
1386  resultVariationTypes[D1_DIM] = BLOCK_PLUS_DIAGONAL;
1387  resultVariationTypes[D2_DIM] = BLOCK_PLUS_DIAGONAL;
1388  resultBlockPlusDiagonalLastNonDiagonal = std::max(A_MatData.blockPlusDiagonalLastNonDiagonal(), B_MatData.blockPlusDiagonalLastNonDiagonal());
1389  }
1390 
1391  const int resultRank = A_MatData.rank();
1392 
1393  auto A_VariationTypes = A_MatData.getVariationTypes();
1394  auto B_VariationTypes = B_MatData.getVariationTypes();
1395 
1396  Kokkos::Array<int,7> resultActiveDims;
1397  Kokkos::Array<int,7> resultDataDims;
1398  int resultNumActiveDims = 0; // how many of the 7 entries are actually filled in
1399  // the following loop is over the dimensions *prior* to matrix dimensions
1400  for (int i=0; i<resultRank-2; i++)
1401  {
1402  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A_MatData.extent_int(i) != B_MatData.extent_int(i), std::invalid_argument, "A and B extents must match in each non-matrix dimension");
1403 
1404  resultExtents[i] = A_MatData.extent_int(i);
1405 
1406  const DataVariationType &A_VariationType = A_VariationTypes[i];
1407  const DataVariationType &B_VariationType = B_VariationTypes[i];
1408 
1409  // BLOCK_PLUS_DIAGONAL should only occur in matData, and only in the matrix (final) dimensions
1410  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A_VariationType == BLOCK_PLUS_DIAGONAL, std::invalid_argument, "unsupported variationType");
1411  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(B_VariationType == BLOCK_PLUS_DIAGONAL, std::invalid_argument, "unsupported variationType");
1412 
1413  int dataSize = 0;
1414  DataVariationType resultVariationType;
1415  if ((A_VariationType == GENERAL) || (B_VariationType == GENERAL))
1416  {
1417  resultVariationType = GENERAL;
1418  dataSize = resultExtents[i];
1419  }
1420  else if ((B_VariationType == CONSTANT) && (A_VariationType == CONSTANT))
1421  {
1422  resultVariationType = CONSTANT;
1423  dataSize = 1;
1424  }
1425  else if ((B_VariationType == MODULAR) && (A_VariationType == CONSTANT))
1426  {
1427  resultVariationType = MODULAR;
1428  dataSize = B_MatData.getVariationModulus(i);
1429  }
1430  else if ((B_VariationType == CONSTANT) && (A_VariationType == MODULAR))
1431  {
1432  resultVariationType = MODULAR;
1433  dataSize = A_MatData.getVariationModulus(i);
1434  }
1435  else
1436  {
1437  // both are modular. We allow this if they agree on the modulus
1438  auto A_Modulus = A_MatData.getVariationModulus(i);
1439  auto B_Modulus = B_MatData.getVariationModulus(i);
1440  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A_Modulus != B_Modulus, std::invalid_argument, "If both matrices have variation type MODULAR, they must agree on the modulus");
1441  resultVariationType = MODULAR;
1442  dataSize = A_Modulus;
1443  }
1444  resultVariationTypes[i] = resultVariationType;
1445 
1446  if (resultVariationType != CONSTANT)
1447  {
1448  resultActiveDims[resultNumActiveDims] = i;
1449  resultDataDims[resultNumActiveDims] = dataSize;
1450  resultNumActiveDims++;
1451  }
1452  }
1453 
1454  // set things for final dimensions:
1455  resultExtents[D1_DIM] = leftRows;
1456  resultExtents[D2_DIM] = rightCols;
1457 
1458  if ( (A_MatData.getVariationTypes()[D1_DIM] == BLOCK_PLUS_DIAGONAL) && (B_MatData.getVariationTypes()[D1_DIM] == BLOCK_PLUS_DIAGONAL) )
1459  {
1460  // diagonal times diagonal is diagonal; the result will have the maximum of A and B's non-diagonal block size
1461  resultVariationTypes[D1_DIM] = BLOCK_PLUS_DIAGONAL;
1462  resultVariationTypes[D2_DIM] = BLOCK_PLUS_DIAGONAL;
1463  resultBlockPlusDiagonalLastNonDiagonal = std::max(A_MatData.blockPlusDiagonalLastNonDiagonal(), B_MatData.blockPlusDiagonalLastNonDiagonal());
1464 
1465  resultActiveDims[resultNumActiveDims] = resultRank - 2;
1466 
1467  const int numDiagonalEntries = leftRows - (resultBlockPlusDiagonalLastNonDiagonal + 1);
1468  const int numNondiagonalEntries = (resultBlockPlusDiagonalLastNonDiagonal + 1) * (resultBlockPlusDiagonalLastNonDiagonal + 1);
1469 
1470  resultDataDims[resultNumActiveDims] = numDiagonalEntries + numNondiagonalEntries;
1471  resultNumActiveDims++;
1472  }
1473  else
1474  {
1475  // pretty much the only variation types that make sense for matrix dims are GENERAL and BLOCK_PLUS_DIAGONAL
1476  resultVariationTypes[D1_DIM] = GENERAL;
1477  resultVariationTypes[D2_DIM] = GENERAL;
1478 
1479  resultActiveDims[resultNumActiveDims] = resultRank - 2;
1480  resultActiveDims[resultNumActiveDims+1] = resultRank - 1;
1481 
1482  resultDataDims[resultNumActiveDims] = leftRows;
1483  resultDataDims[resultNumActiveDims+1] = rightCols;
1484  resultNumActiveDims += 2;
1485  }
1486 
1487  for (int i=resultRank; i<7; i++)
1488  {
1489  resultVariationTypes[i] = CONSTANT;
1490  resultExtents[i] = 1;
1491  }
1492 
1493  ScalarView<DataScalar,DeviceType> data; // new view will match this one in layout and fad dimension, if any
1494  auto viewToMatch = A_MatData.getUnderlyingView();
1495  if (resultNumActiveDims == 1)
1496  {
1497  data = getMatchingViewWithLabel(viewToMatch, "Data mat-mat result", resultDataDims[0]);
1498  }
1499  else if (resultNumActiveDims == 2)
1500  {
1501  data = getMatchingViewWithLabel(viewToMatch, "Data mat-mat result", resultDataDims[0], resultDataDims[1]);
1502  }
1503  else if (resultNumActiveDims == 3)
1504  {
1505  data = getMatchingViewWithLabel(viewToMatch, "Data mat-mat result", resultDataDims[0], resultDataDims[1], resultDataDims[2]);
1506  }
1507  else if (resultNumActiveDims == 4)
1508  {
1509  data = getMatchingViewWithLabel(viewToMatch, "Data mat-mat result", resultDataDims[0], resultDataDims[1], resultDataDims[2],
1510  resultDataDims[3]);
1511  }
1512  else if (resultNumActiveDims == 5)
1513  {
1514  data = getMatchingViewWithLabel(viewToMatch, "Data mat-mat result", resultDataDims[0], resultDataDims[1], resultDataDims[2],
1515  resultDataDims[3], resultDataDims[4]);
1516  }
1517  else if (resultNumActiveDims == 6)
1518  {
1519  data = getMatchingViewWithLabel(viewToMatch, "Data mat-mat result", resultDataDims[0], resultDataDims[1], resultDataDims[2],
1520  resultDataDims[3], resultDataDims[4], resultDataDims[5]);
1521  }
1522  else // resultNumActiveDims == 7
1523  {
1524  data = getMatchingViewWithLabel(viewToMatch, "Data mat-mat result", resultDataDims[0], resultDataDims[1], resultDataDims[2],
1525  resultDataDims[3], resultDataDims[4], resultDataDims[5], resultDataDims[6]);
1526  }
1527 
1528  return Data<DataScalar,DeviceType>(data,resultRank,resultExtents,resultVariationTypes,resultBlockPlusDiagonalLastNonDiagonal);
1529  }
1530 
1538  {
1539  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A.rank() != B.rank(), std::invalid_argument, "A and B must have the same logical shape");
1540  const int rank = A.rank();
1541  const int resultRank = rank - numContractionDims;
1542  std::vector<DimensionInfo> dimInfo(resultRank);
1543  for (int d=0; d<resultRank; d++)
1544  {
1545  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A.extent_int(d) != B.extent_int(d), std::invalid_argument, "A and B must have the same logical shape");
1546  dimInfo[d] = A.combinedDataDimensionInfo(B, d);
1547  }
1548  Data<DataScalar,DeviceType> result(dimInfo);
1549  return result;
1550  }
1551 
1558  {
1559  return allocateContractionResult(A, B, 1);
1560  }
1561 
1564  static Data<DataScalar,DeviceType> allocateMatVecResult( const Data<DataScalar,DeviceType> &matData, const Data<DataScalar,DeviceType> &vecData, const bool transposeMatrix = false )
1565  {
1566  // we treat last two logical dimensions of matData as the matrix; last dimension of vecData as the vector
1567  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(matData.rank() != vecData.rank() + 1, std::invalid_argument, "matData and vecData have incompatible ranks");
1568  const int vecDim = vecData.extent_int(vecData.rank() - 1);
1569 
1570  const int D1_DIM = matData.rank() - 2;
1571  const int D2_DIM = matData.rank() - 1;
1572 
1573  const int matRows = matData.extent_int(D1_DIM);
1574  const int matCols = matData.extent_int(D2_DIM);
1575 
1576  const int rows = transposeMatrix ? matCols : matRows;
1577  const int cols = transposeMatrix ? matRows : matCols;
1578 
1579  const int resultRank = vecData.rank();
1580 
1581  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(cols != vecDim, std::invalid_argument, "matData column count != vecData dimension");
1582 
1583  Kokkos::Array<int,7> resultExtents; // logical extents
1584  Kokkos::Array<DataVariationType,7> resultVariationTypes; // for each dimension, whether the data varies in that dimension
1585  auto vecVariationTypes = vecData.getVariationTypes();
1586  auto matVariationTypes = matData.getVariationTypes();
1587 
1588  Kokkos::Array<int,7> resultActiveDims;
1589  Kokkos::Array<int,7> resultDataDims;
1590  int resultNumActiveDims = 0; // how many of the 7 entries are actually filled in
1591  // the following loop is over the dimensions *prior* to matrix/vector dimensions
1592  for (int i=0; i<resultRank-1; i++)
1593  {
1594  resultExtents[i] = vecData.extent_int(i);
1595  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(vecData.extent_int(i) != matData.extent_int(i), std::invalid_argument, "matData and vecData extents must match in each non-matrix/vector dimension");
1596 
1597  const DataVariationType &vecVariationType = vecVariationTypes[i];
1598  const DataVariationType &matVariationType = matVariationTypes[i];
1599 
1600  // BLOCK_PLUS_DIAGONAL should only occur in matData, and only in the matrix (final) dimensions
1601  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(vecVariationType == BLOCK_PLUS_DIAGONAL, std::invalid_argument, "unsupported variationType");
1602  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(matVariationType == BLOCK_PLUS_DIAGONAL, std::invalid_argument, "unsupported variationType");
1603 
1604  int dataSize = 0;
1605  DataVariationType resultVariationType;
1606  if ((vecVariationType == GENERAL) || (matVariationType == GENERAL))
1607  {
1608  resultVariationType = GENERAL;
1609  dataSize = resultExtents[i];
1610  }
1611  else if ((matVariationType == CONSTANT) && (vecVariationType == CONSTANT))
1612  {
1613  resultVariationType = CONSTANT;
1614  dataSize = 1;
1615  }
1616  else if ((matVariationType == MODULAR) && (vecVariationType == CONSTANT))
1617  {
1618  resultVariationType = MODULAR;
1619  dataSize = matData.getVariationModulus(i);
1620  }
1621  else if ((matVariationType == CONSTANT) && (vecVariationType == MODULAR))
1622  {
1623  resultVariationType = MODULAR;
1624  dataSize = matData.getVariationModulus(i);
1625  }
1626  else
1627  {
1628  // both are modular. We allow this if they agree on the modulus
1629  auto matModulus = matData.getVariationModulus(i);
1630  auto vecModulus = vecData.getVariationModulus(i);
1631  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(matModulus != vecModulus, std::invalid_argument, "If matrix and vector both have variation type MODULAR, they must agree on the modulus");
1632  resultVariationType = MODULAR;
1633  dataSize = matModulus;
1634  }
1635  resultVariationTypes[i] = resultVariationType;
1636 
1637  if (resultVariationType != CONSTANT)
1638  {
1639  resultActiveDims[resultNumActiveDims] = i;
1640  resultDataDims[resultNumActiveDims] = dataSize;
1641  resultNumActiveDims++;
1642  }
1643  }
1644  // for the final dimension, the variation type is always GENERAL
1645  // (Some combinations, e.g. CONSTANT/CONSTANT *would* generate a CONSTANT result, but constant matrices don't make a lot of sense beyond 1x1 matrices…)
1646  resultActiveDims[resultNumActiveDims] = resultRank - 1;
1647  resultDataDims[resultNumActiveDims] = rows;
1648  resultNumActiveDims++;
1649 
1650  for (int i=resultRank; i<7; i++)
1651  {
1652  resultVariationTypes[i] = CONSTANT;
1653  resultExtents[i] = 1;
1654  }
1655  resultVariationTypes[resultRank-1] = GENERAL;
1656  resultExtents[resultRank-1] = rows;
1657 
1658  ScalarView<DataScalar,DeviceType> data;
1659  if (resultNumActiveDims == 1)
1660  {
1661  data = matData.allocateDynRankViewMatchingUnderlying(resultDataDims[0]);
1662  }
1663  else if (resultNumActiveDims == 2)
1664  {
1665  data = matData.allocateDynRankViewMatchingUnderlying(resultDataDims[0], resultDataDims[1]);
1666  }
1667  else if (resultNumActiveDims == 3)
1668  {
1669  data = matData.allocateDynRankViewMatchingUnderlying(resultDataDims[0], resultDataDims[1], resultDataDims[2]);
1670  }
1671  else if (resultNumActiveDims == 4)
1672  {
1673  data = matData.allocateDynRankViewMatchingUnderlying(resultDataDims[0], resultDataDims[1], resultDataDims[2],
1674  resultDataDims[3]);
1675  }
1676  else if (resultNumActiveDims == 5)
1677  {
1678  data = matData.allocateDynRankViewMatchingUnderlying(resultDataDims[0], resultDataDims[1], resultDataDims[2],
1679  resultDataDims[3], resultDataDims[4]);
1680  }
1681  else if (resultNumActiveDims == 6)
1682  {
1683  data = matData.allocateDynRankViewMatchingUnderlying(resultDataDims[0], resultDataDims[1], resultDataDims[2],
1684  resultDataDims[3], resultDataDims[4], resultDataDims[5]);
1685  }
1686  else // resultNumActiveDims == 7
1687  {
1688  data = matData.allocateDynRankViewMatchingUnderlying(resultDataDims[0], resultDataDims[1], resultDataDims[2],
1689  resultDataDims[3], resultDataDims[4], resultDataDims[5], resultDataDims[6]);
1690  }
1691 
1692  return Data<DataScalar,DeviceType>(data,resultRank,resultExtents,resultVariationTypes);
1693  }
1694 
1696  template<int rank>
1697  enable_if_t<(rank!=1) && (rank!=7), Kokkos::MDRangePolicy<typename DeviceType::execution_space,Kokkos::Rank<rank>> >
1699  {
1700  using ExecutionSpace = typename DeviceType::execution_space;
1701  Kokkos::Array<int,rank> startingOrdinals;
1702  Kokkos::Array<int,rank> extents;
1703 
1704  for (int d=0; d<rank; d++)
1705  {
1706  startingOrdinals[d] = 0;
1707  extents[d] = getDataExtent(d);
1708  }
1709  auto policy = Kokkos::MDRangePolicy<ExecutionSpace,Kokkos::Rank<rank>>(startingOrdinals,extents);
1710  return policy;
1711  }
1712 
1714  template<int rank>
1715  enable_if_t<rank==7, Kokkos::MDRangePolicy<typename DeviceType::execution_space,Kokkos::Rank<6>> >
1717  {
1718  using ExecutionSpace = typename DeviceType::execution_space;
1719  Kokkos::Array<int,6> startingOrdinals;
1720  Kokkos::Array<int,6> extents;
1721 
1722  for (int d=0; d<6; d++)
1723  {
1724  startingOrdinals[d] = 0;
1725  extents[d] = getDataExtent(d);
1726  }
1727  auto policy = Kokkos::MDRangePolicy<ExecutionSpace,Kokkos::Rank<6>>(startingOrdinals,extents);
1728  return policy;
1729  }
1730 
1731  template<int rank>
1732  inline
1733  enable_if_t<rank==1, Kokkos::RangePolicy<typename DeviceType::execution_space> >
1735  {
1736  using ExecutionSpace = typename DeviceType::execution_space;
1737  Kokkos::RangePolicy<ExecutionSpace> policy(ExecutionSpace(),0,getDataExtent(0));
1738  return policy;
1739  }
1740 
1742  Data shallowCopy(const int rank, const Kokkos::Array<int,7> &extents, const Kokkos::Array<DataVariationType,7> &variationTypes) const
1743  {
1744  switch (dataRank_)
1745  {
1746  case 1: return Data(rank, data1_, extents, variationTypes);
1747  case 2: return Data(rank, data2_, extents, variationTypes);
1748  case 3: return Data(rank, data3_, extents, variationTypes);
1749  case 4: return Data(rank, data4_, extents, variationTypes);
1750  case 5: return Data(rank, data5_, extents, variationTypes);
1751  case 6: return Data(rank, data6_, extents, variationTypes);
1752  case 7: return Data(rank, data7_, extents, variationTypes);
1753  default:
1754  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unhandled dataRank_");
1755  }
1756  }
1757 
1760  {
1761  const int D_DIM = A.rank() - 1;
1762  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A.extent_int(D_DIM) != B.extent_int(D_DIM), std::invalid_argument, "A and B have different extents");
1763  const int vectorComponents = A.extent_int(D_DIM);
1764 
1765  // shallow copy of this to avoid implicit references to this in call to getWritableEntry() below
1766  Data<DataScalar,DeviceType> thisData = *this;
1767 
1768  using ExecutionSpace = typename DeviceType::execution_space;
1769  // note the use of getDataExtent() below: we only range over the possibly-distinct entries
1770  if (rank_ == 1) // contraction result rank; e.g., (P)
1771  {
1772  Kokkos::parallel_for("compute dot product", getDataExtent(0),
1773  KOKKOS_LAMBDA (const int &pointOrdinal) {
1774  auto & val = thisData.getWritableEntry(pointOrdinal);
1775  val = 0;
1776  for (int i=0; i<vectorComponents; i++)
1777  {
1778  val += A(pointOrdinal,i) * B(pointOrdinal,i);
1779  }
1780  });
1781  }
1782  else if (rank_ == 2) // contraction result rank; e.g., (C,P)
1783  {
1784  // typical case for e.g. gradient data: (C,P,D)
1785  auto policy = Kokkos::MDRangePolicy<ExecutionSpace,Kokkos::Rank<2>>({0,0},{getDataExtent(0),getDataExtent(1)});
1786  Kokkos::parallel_for("compute dot product", policy,
1787  KOKKOS_LAMBDA (const int &cellOrdinal, const int &pointOrdinal) {
1788  auto & val = thisData.getWritableEntry(cellOrdinal, pointOrdinal);
1789  val = 0;
1790  for (int i=0; i<vectorComponents; i++)
1791  {
1792  val += A(cellOrdinal,pointOrdinal,i) * B(cellOrdinal,pointOrdinal,i);
1793  }
1794  });
1795  }
1796  else if (rank_ == 3)
1797  {
1798  auto policy = Kokkos::MDRangePolicy<ExecutionSpace,Kokkos::Rank<3>>({0,0,0},{getDataExtent(0),getDataExtent(1),getDataExtent(2)});
1799  Kokkos::parallel_for("compute dot product", policy,
1800  KOKKOS_LAMBDA (const int &cellOrdinal, const int &pointOrdinal, const int &d) {
1801  auto & val = thisData.getWritableEntry(cellOrdinal, pointOrdinal,d);
1802  val = 0;
1803  for (int i=0; i<vectorComponents; i++)
1804  {
1805  val += A(cellOrdinal,pointOrdinal,d,i) * B(cellOrdinal,pointOrdinal,d,i);
1806  }
1807  });
1808  }
1809  else
1810  {
1811  // TODO: handle other cases
1812  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::logic_error, "rank not yet supported");
1813  }
1814  }
1815 
1817  template<class BinaryOperator>
1818  void storeInPlaceCombination(const Data<DataScalar,DeviceType> &A, const Data<DataScalar,DeviceType> &B, BinaryOperator binaryOperator);
1819 
1822  {
1824  storeInPlaceCombination(A, B, sum);
1825  }
1826 
1829  {
1831  storeInPlaceCombination(A, B, product);
1832  }
1833 
1836  {
1838  storeInPlaceCombination(A, B, difference);
1839  }
1840 
1843  {
1845  storeInPlaceCombination(A, B, quotient);
1846  }
1847 
1849  void storeMatVec( const Data<DataScalar,DeviceType> &matData, const Data<DataScalar,DeviceType> &vecData, const bool transposeMatrix = false )
1850  {
1851  // TODO: add a compile-time (SFINAE-type) guard against DataScalar types that do not support arithmetic operations. (We support Orientation as a DataScalar type; it might suffice just to compare DataScalar to Orientation, and eliminate this method for that case.)
1852  // TODO: check for invalidly shaped containers.
1853 
1854  const int D1_DIM = matData.rank() - 2;
1855  const int D2_DIM = matData.rank() - 1;
1856 
1857  const int matRows = matData.extent_int(D1_DIM);
1858  const int matCols = matData.extent_int(D2_DIM);
1859 
1860  const int rows = transposeMatrix ? matCols : matRows;
1861  const int cols = transposeMatrix ? matRows : matCols;
1862 
1863  // shallow copy of this to avoid implicit references to this in call to getWritableEntry() below
1864  Data<DataScalar,DeviceType> thisData = *this;
1865 
1866  using ExecutionSpace = typename DeviceType::execution_space;
1867  // note the use of getDataExtent() below: we only range over the possibly-distinct entries
1868  if (rank_ == 3)
1869  {
1870  // typical case for e.g. gradient data: (C,P,D)
1871  auto policy = Kokkos::MDRangePolicy<ExecutionSpace,Kokkos::Rank<3>>({0,0,0},{getDataExtent(0),getDataExtent(1),rows});
1872  Kokkos::parallel_for("compute mat-vec", policy,
1873  KOKKOS_LAMBDA (const int &cellOrdinal, const int &pointOrdinal, const int &i) {
1874  auto & val_i = thisData.getWritableEntry(cellOrdinal, pointOrdinal, i);
1875  val_i = 0;
1876  for (int j=0; j<cols; j++)
1877  {
1878  const auto & mat_ij = transposeMatrix ? matData(cellOrdinal,pointOrdinal,j,i) : matData(cellOrdinal,pointOrdinal,i,j);
1879  val_i += mat_ij * vecData(cellOrdinal,pointOrdinal,j);
1880  }
1881  });
1882  }
1883  else if (rank_ == 2)
1884  {
1885  //
1886  auto policy = Kokkos::MDRangePolicy<ExecutionSpace,Kokkos::Rank<2>>({0,0},{getDataExtent(0),rows});
1887  Kokkos::parallel_for("compute mat-vec", policy,
1888  KOKKOS_LAMBDA (const int &vectorOrdinal, const int &i) {
1889  auto & val_i = thisData.getWritableEntry(vectorOrdinal, i);
1890  val_i = 0;
1891  for (int j=0; j<cols; j++)
1892  {
1893  const auto & mat_ij = transposeMatrix ? matData(vectorOrdinal,j,i) : matData(vectorOrdinal,i,j);
1894  val_i += mat_ij * vecData(vectorOrdinal,j);
1895  }
1896  });
1897  }
1898  else if (rank_ == 1)
1899  {
1900  // single-vector case
1901  Kokkos::RangePolicy<ExecutionSpace> policy(0,rows);
1902  Kokkos::parallel_for("compute mat-vec", policy,
1903  KOKKOS_LAMBDA (const int &i) {
1904  auto & val_i = thisData.getWritableEntry(i);
1905  val_i = 0;
1906  for (int j=0; j<cols; j++)
1907  {
1908  const auto & mat_ij = transposeMatrix ? matData(j,i) : matData(i,j);
1909  val_i += mat_ij * vecData(j);
1910  }
1911  });
1912  }
1913  else
1914  {
1915  // TODO: handle other cases
1916  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::logic_error, "rank not yet supported");
1917  }
1918  }
1919 
1926  void storeMatMat( const bool transposeA, const Data<DataScalar,DeviceType> &A_MatData, const bool transposeB, const Data<DataScalar,DeviceType> &B_MatData )
1927  {
1928  // TODO: add a compile-time (SFINAE-type) guard against DataScalar types that do not support arithmetic operations. (We support Orientation as a DataScalar type; it might suffice just to compare DataScalar to Orientation, and eliminate this method for that case.)
1929  // TODO: check for invalidly shaped containers.
1930 
1931  // we treat last two logical dimensions of matData as the matrix; last dimension of vecData as the vector
1932  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(A_MatData.rank() != B_MatData.rank(), std::invalid_argument, "AmatData and BmatData have incompatible ranks");
1933 
1934  const int D1_DIM = A_MatData.rank() - 2;
1935  const int D2_DIM = A_MatData.rank() - 1;
1936 
1937  const int A_rows = A_MatData.extent_int(D1_DIM);
1938  const int A_cols = A_MatData.extent_int(D2_DIM);
1939  const int B_rows = B_MatData.extent_int(D1_DIM);
1940  const int B_cols = B_MatData.extent_int(D2_DIM);
1941 
1942  const int leftRows = transposeA ? A_cols : A_rows;
1943  const int leftCols = transposeA ? A_rows : A_cols;
1944  const int rightCols = transposeB ? B_rows : B_cols;
1945 
1946 #ifdef INTREPID2_HAVE_DEBUG
1947  const int rightRows = transposeB ? B_cols : B_rows;
1948  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(leftCols != rightRows, std::invalid_argument, "inner dimensions do not match");
1949 #endif
1950 
1951  // shallow copy of this to avoid implicit references to this in call to getWritableEntry() below
1952  Data<DataScalar,DeviceType> thisData = *this;
1953 
1954  using ExecutionSpace = typename DeviceType::execution_space;
1955 
1956  const int diagonalStart = (variationType_[D1_DIM] == BLOCK_PLUS_DIAGONAL) ? blockPlusDiagonalLastNonDiagonal_ + 1 : leftRows;
1957  // note the use of getDataExtent() below: we only range over the possibly-distinct entries
1958  if (rank_ == 3)
1959  {
1960  // (C,D,D), say
1961  auto policy = Kokkos::RangePolicy<ExecutionSpace>(0,getDataExtent(0));
1962  Kokkos::parallel_for("compute mat-mat", policy,
1963  KOKKOS_LAMBDA (const int &matrixOrdinal) {
1964  for (int i=0; i<diagonalStart; i++)
1965  {
1966  for (int j=0; j<rightCols; j++)
1967  {
1968  auto & val_ij = thisData.getWritableEntry(matrixOrdinal, i, j);
1969  val_ij = 0;
1970  for (int k=0; k<leftCols; k++)
1971  {
1972  const auto & left = transposeA ? A_MatData(matrixOrdinal,k,i) : A_MatData(matrixOrdinal,i,k);
1973  const auto & right = transposeB ? B_MatData(matrixOrdinal,j,k) : B_MatData(matrixOrdinal,k,j);
1974  val_ij += left * right;
1975  }
1976  }
1977  }
1978  for (int i=diagonalStart; i<leftRows; i++)
1979  {
1980  auto & val_ii = thisData.getWritableEntry(matrixOrdinal, i, i);
1981  const auto & left = A_MatData(matrixOrdinal,i,i);
1982  const auto & right = B_MatData(matrixOrdinal,i,i);
1983  val_ii = left * right;
1984  }
1985  });
1986  }
1987  else if (rank_ == 4)
1988  {
1989  // (C,P,D,D), perhaps
1990  auto policy = Kokkos::MDRangePolicy<ExecutionSpace, Kokkos::Rank<2> >({0,0},{getDataExtent(0),getDataExtent(1)});
1991  if (underlyingMatchesLogical_) // receiving data object is completely expanded
1992  {
1993  Kokkos::parallel_for("compute mat-mat", policy,
1994  KOKKOS_LAMBDA (const int &cellOrdinal, const int &pointOrdinal) {
1995  for (int i=0; i<leftRows; i++)
1996  {
1997  for (int j=0; j<rightCols; j++)
1998  {
1999  auto & val_ij = thisData.getUnderlyingView4()(cellOrdinal,pointOrdinal, i, j);
2000  val_ij = 0;
2001  for (int k=0; k<leftCols; k++)
2002  {
2003  const auto & left = transposeA ? A_MatData(cellOrdinal,pointOrdinal,k,i) : A_MatData(cellOrdinal,pointOrdinal,i,k);
2004  const auto & right = transposeB ? B_MatData(cellOrdinal,pointOrdinal,j,k) : B_MatData(cellOrdinal,pointOrdinal,k,j);
2005  val_ij += left * right;
2006  }
2007  }
2008  }
2009  });
2010  }
2011  else
2012  {
2013  Kokkos::parallel_for("compute mat-mat", policy,
2014  KOKKOS_LAMBDA (const int &cellOrdinal, const int &pointOrdinal) {
2015  for (int i=0; i<diagonalStart; i++)
2016  {
2017  for (int j=0; j<rightCols; j++)
2018  {
2019  auto & val_ij = thisData.getWritableEntry(cellOrdinal,pointOrdinal, i, j);
2020  val_ij = 0;
2021  for (int k=0; k<leftCols; k++)
2022  {
2023  const auto & left = transposeA ? A_MatData(cellOrdinal,pointOrdinal,k,i) : A_MatData(cellOrdinal,pointOrdinal,i,k);
2024  const auto & right = transposeB ? B_MatData(cellOrdinal,pointOrdinal,j,k) : B_MatData(cellOrdinal,pointOrdinal,k,j);
2025  val_ij += left * right;
2026  }
2027  }
2028  }
2029  for (int i=diagonalStart; i<leftRows; i++)
2030  {
2031  auto & val_ii = thisData.getWritableEntry(cellOrdinal,pointOrdinal, i, i);
2032  const auto & left = A_MatData(cellOrdinal,pointOrdinal,i,i);
2033  const auto & right = B_MatData(cellOrdinal,pointOrdinal,i,i);
2034  val_ii = left * right;
2035  }
2036  });
2037  }
2038  }
2039  else
2040  {
2041  // TODO: handle other cases
2042  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::logic_error, "rank not yet supported");
2043  }
2044  }
2045 
2047  KOKKOS_INLINE_FUNCTION constexpr bool isValid() const
2048  {
2049  return extents_[0] > 0;
2050  }
2051 
2053  KOKKOS_INLINE_FUNCTION
2054  unsigned rank() const
2055  {
2056  return rank_;
2057  }
2058 
2065  void setExtent(const ordinal_type &d, const ordinal_type &newExtent)
2066  {
2067  INTREPID2_TEST_FOR_EXCEPTION(variationType_[d] == BLOCK_PLUS_DIAGONAL, std::invalid_argument, "setExtent is not supported for BLOCK_PLUS_DIAGONAL dimensions");
2068 
2069  if (variationType_[d] == MODULAR)
2070  {
2071  bool dividesEvenly = ((newExtent / variationModulus_[d]) * variationModulus_[d] == newExtent);
2072  INTREPID2_TEST_FOR_EXCEPTION(!dividesEvenly, std::invalid_argument, "when setExtent is called on dimenisions with MODULAR variation, the modulus must divide the new extent evenly");
2073  }
2074 
2075  if ((newExtent != extents_[d]) && (variationType_[d] == GENERAL))
2076  {
2077  // then we need to resize; let's determine the full set of new extents
2078  std::vector<ordinal_type> newExtents(dataRank_,-1);
2079  for (int r=0; r<dataRank_; r++)
2080  {
2081  if (activeDims_[r] == d)
2082  {
2083  // this is the changed dimension
2084  newExtents[r] = newExtent;
2085  }
2086  else
2087  {
2088  // unchanged; copy from existing
2089  newExtents[r] = getUnderlyingViewExtent(r);
2090  }
2091  }
2092 
2093  switch (dataRank_)
2094  {
2095  case 1: Kokkos::resize(data1_,newExtents[0]);
2096  break;
2097  case 2: Kokkos::resize(data2_,newExtents[0],newExtents[1]);
2098  break;
2099  case 3: Kokkos::resize(data3_,newExtents[0],newExtents[1],newExtents[2]);
2100  break;
2101  case 4: Kokkos::resize(data4_,newExtents[0],newExtents[1],newExtents[2],newExtents[3]);
2102  break;
2103  case 5: Kokkos::resize(data5_,newExtents[0],newExtents[1],newExtents[2],newExtents[3],newExtents[4]);
2104  break;
2105  case 6: Kokkos::resize(data6_,newExtents[0],newExtents[1],newExtents[2],newExtents[3],newExtents[4],newExtents[5]);
2106  break;
2107  case 7: Kokkos::resize(data7_,newExtents[0],newExtents[1],newExtents[2],newExtents[3],newExtents[4],newExtents[5],newExtents[6]);
2108  break;
2109  default: INTREPID2_TEST_FOR_EXCEPTION(true, std::logic_error, "Unexpected dataRank_ value");
2110  }
2111  }
2112 
2113  extents_[d] = newExtent;
2114  }
2115 
2117  KOKKOS_INLINE_FUNCTION
2119  {
2120  return underlyingMatchesLogical_;
2121  }
2122  };
2123 
2124  template<class DataScalar, typename DeviceType>
2125  KOKKOS_INLINE_FUNCTION constexpr unsigned rank(const Data<DataScalar, DeviceType>& D) {
2126  return D.rank();
2127  }
2128 }
2129 
2130 // we do ETI for doubles and default ExecutionSpace's device_type
2132 
2133 #include "Intrepid2_DataDef.hpp"
2134 
2135 #endif /* Intrepid2_Data_h */
enable_if_t< rank==7, Kokkos::MDRangePolicy< typename DeviceType::execution_space, Kokkos::Rank< 6 > > > dataExtentRangePolicy()
returns an MDRangePolicy over the first six underlying data extents (but with the logical shape)...
void clear() const
Copies 0.0 to the underlying View.
void copyDataFromDynRankViewMatchingUnderlying(const ScalarView< DataScalar, DeviceType > &dynRankView) const
Copies from the provided DynRankView into the underlying Kokkos::View container storing the unique da...
KOKKOS_INLINE_FUNCTION reference_type getWritableEntry(const IntArgs...intArgs) const
Returns an l-value reference to the specified logical entry in the underlying view. Note that for variation types other than GENERAL, multiple valid argument sets will refer to the same memory location. Intended for Intrepid2 developers and expert users only. If passThroughBlockDiagonalArgs is TRUE, the corresponding arguments are interpreted as entries in the 1D packed matrix rather than as logical 2D matrix row and column.
KOKKOS_INLINE_FUNCTION ordinal_type getUnderlyingViewRank() const
returns the rank of the View that stores the unique data
Header file with various static argument-extractor classes. These are useful for writing efficient...
KOKKOS_INLINE_FUNCTION const Kokkos::View< DataScalar *******, DeviceType > & getUnderlyingView7() const
returns the View that stores the unique data. For rank-7 underlying containers.
ScalarView< DataScalar, DeviceType > allocateDynRankViewMatchingUnderlying(DimArgs...dims) const
Returns a DynRankView that matches the underlying Kokkos::View object value_type and layout...
void applyOperator(UnaryOperator unaryOperator)
applies the specified unary operator to each entry
static KOKKOS_INLINE_FUNCTION int blockPlusDiagonalDiagonalEntryIndex(const int &lastNondiagonal, const int &numNondiagonalEntries, const int &i)
Returns flattened index of the specified (i,i) matrix entry, assuming that i &gt; lastNondiagonal. Only applicable for BLOCK_PLUS_DIAGONAL DataVariationType.
void storeDotProduct(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B)
Places the result of a contraction along the final dimension of A and B into this data container...
static KOKKOS_INLINE_FUNCTION int blockPlusDiagonalBlockEntryIndex(const int &lastNondiagonal, const int &numNondiagonalEntries, const int &i, const int &j)
//! Returns flattened index of the specified (i,j) matrix entry, assuming that i,j ≤ lastNondiagonal...
static Data< DataScalar, DeviceType > allocateInPlaceCombinationResult(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B)
static Data< DataScalar, DeviceType > allocateMatVecResult(const Data< DataScalar, DeviceType > &matData, const Data< DataScalar, DeviceType > &vecData, const bool transposeMatrix=false)
void storeMatVec(const Data< DataScalar, DeviceType > &matData, const Data< DataScalar, DeviceType > &vecData, const bool transposeMatrix=false)
Places the result of a matrix-vector multiply corresponding to the two provided containers into this ...
Data(const unsigned rank, Kokkos::View< ViewScalar, DeviceType, ViewProperties...> data, Kokkos::Array< int, 7 > extents, Kokkos::Array< DataVariationType, 7 > variationType, const int blockPlusDiagonalLastNonDiagonal=-1)
constructor with run-time rank (requires full-length extents, variationType inputs; those beyond the ...
KOKKOS_INLINE_FUNCTION enable_if_t< rank==6, const Kokkos::View< typename RankExpander< DataScalar, rank >::value_type, DeviceType > & > getUnderlyingView() const
Returns the underlying view. Throws an exception if the underlying view is not rank 6...
KOKKOS_INLINE_FUNCTION enable_if_t< rank==5, const Kokkos::View< typename RankExpander< DataScalar, rank >::value_type, DeviceType > & > getUnderlyingView() const
Returns the underlying view. Throws an exception if the underlying view is not rank 5...
static void copyContainer(ToContainer to, FromContainer from)
Generic data copying method to allow construction of Data object from DynRankViews for which deep_cop...
KOKKOS_INLINE_FUNCTION void setUnderlyingView3(const Kokkos::View< DataScalar ***, DeviceType > &view)
sets the View that stores the unique data. For rank-3 underlying containers.
#define INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(test, x, msg)
KOKKOS_INLINE_FUNCTION int getVariationModulus(const int &d) const
Variation modulus accessor.
KOKKOS_INLINE_FUNCTION enable_if_t< rank==7, const Kokkos::View< typename RankExpander< DataScalar, rank >::value_type, DeviceType > & > getUnderlyingView() const
Returns the underlying view. Throws an exception if the underlying view is not rank 7...
KOKKOS_INLINE_FUNCTION const Kokkos::View< DataScalar *****, DeviceType > & getUnderlyingView5() const
returns the View that stores the unique data. For rank-5 underlying containers.
KOKKOS_INLINE_FUNCTION DimensionInfo combinedDataDimensionInfo(const Data &otherData, const int &dim) const
Returns (DataVariationType, data extent) in the specified dimension for a Data container that combine...
ScalarView< DataScalar, DeviceType > getUnderlyingView() const
Returns a DynRankView constructed atop the same underlying data as the fixed-rank Kokkos::View used i...
KOKKOS_INLINE_FUNCTION Kokkos::Array< int, 7 > getExtents() const
Returns an array containing the logical extents in each dimension.
KOKKOS_INLINE_FUNCTION const Kokkos::View< DataScalar *, DeviceType > & getUnderlyingView1() const
returns the View that stores the unique data. For rank-1 underlying containers.
KOKKOS_INLINE_FUNCTION const Kokkos::Array< DataVariationType, 7 > & getVariationTypes() const
Returns an array with the variation types in each logical dimension.
KOKKOS_INLINE_FUNCTION const Kokkos::View< DataScalar ******, DeviceType > & getUnderlyingView6() const
returns the View that stores the unique data. For rank-6 underlying containers.
KOKKOS_INLINE_FUNCTION enable_if_t< rank==1, const Kokkos::View< typename RankExpander< DataScalar, rank >::value_type, DeviceType > & > getUnderlyingView() const
Returns the underlying view. Throws an exception if the underlying view is not rank 1...
Defines implementations for the Data class that are not present in the declaration.
KOKKOS_INLINE_FUNCTION bool underlyingMatchesLogical() const
Returns true if the underlying container has exactly the same rank and extents as the logical contain...
Wrapper around a Kokkos::View that allows data that is constant or repeating in various logical dimen...
void storeInPlaceCombination(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B, BinaryOperator binaryOperator)
Places the result of an in-place combination (e.g., entrywise sum) into this data container...
Header function for Intrepid2::Util class and other utility functions.
KOKKOS_INLINE_FUNCTION bool isDiagonal() const
returns true for containers that have two dimensions marked as BLOCK_PLUS_DIAGONAL for which the non-...
KOKKOS_INLINE_FUNCTION const Kokkos::View< DataScalar ****, DeviceType > & getUnderlyingView4() const
returns the View that stores the unique data. For rank-4 underlying containers.
KOKKOS_INLINE_FUNCTION void setUnderlyingView5(const Kokkos::View< DataScalar *****, DeviceType > &view)
sets the View that stores the unique data. For rank-5 underlying containers.
Data(std::vector< DimensionInfo > dimInfoVector)
Constructor in terms of DimensionInfo for each logical dimension; does not require a View to be speci...
Struct expressing all variation information about a Data object in a single dimension, including its logical extent and storage extent.
KOKKOS_INLINE_FUNCTION return_type getEntryWithPassThroughOption(const bool &passThroughBlockDiagonalArgs, const IntArgs &...intArgs) const
Returns a (read-only) value corresponding to the specified logical data location. If passThroughBlock...
Data shallowCopy(const int rank, const Kokkos::Array< int, 7 > &extents, const Kokkos::Array< DataVariationType, 7 > &variationTypes) const
Creates a new Data object with the same underlying view, but with the specified logical rank...
void storeInPlaceDifference(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B)
stores the in-place (entrywise) difference, A .- B, into this container.
Defines functors for use with Data objects: so far, we include simple arithmetical functors for sum...
KOKKOS_INLINE_FUNCTION DimensionInfo getDimensionInfo(const int &dim) const
Returns an object fully specifying the indicated dimension. This is used in determining appropriate s...
KOKKOS_INLINE_FUNCTION const Kokkos::View< DataScalar ***, DeviceType > & getUnderlyingView3() const
returns the View that stores the unique data. For rank-3 underlying containers.
Data(DataScalar constantValue, Kokkos::Array< int, rank > extents)
constructor for everywhere-constant data
enable_if_t<(rank!=1)&&(rank!=7), Kokkos::MDRangePolicy< typename DeviceType::execution_space, Kokkos::Rank< rank > > > dataExtentRangePolicy()
returns an MDRangePolicy over the underlying data extents (but with the logical shape).
KOKKOS_INLINE_FUNCTION reference_type getWritableEntryWithPassThroughOption(const bool &passThroughBlockDiagonalArgs, const IntArgs...intArgs) const
Returns an l-value reference to the specified logical entry in the underlying view. Note that for variation types other than GENERAL, multiple valid argument sets will refer to the same memory location. Intended for Intrepid2 developers and expert users only. If passThroughBlockDiagonalArgs is TRUE, the corresponding arguments are interpreted as entries in the 1D packed matrix rather than as logical 2D matrix row and column.
Data< DataScalar, DeviceType > allocateConstantData(const DataScalar &value)
KOKKOS_INLINE_FUNCTION constexpr bool isValid() const
returns true for containers that have data; false for those that don&#39;t (namely, those that have been ...
KOKKOS_INLINE_FUNCTION enable_if_t< valid_args< IntArgs...>::value &&(sizeof...(IntArgs)<=7), return_type > operator()(const IntArgs &...intArgs) const
Returns a value corresponding to the specified logical data location.
KOKKOS_INLINE_FUNCTION void setUnderlyingView6(const Kokkos::View< DataScalar ******, DeviceType > &view)
sets the View that stores the unique data. For rank-6 underlying containers.
KOKKOS_INLINE_FUNCTION void setUnderlyingView2(const Kokkos::View< DataScalar **, DeviceType > &view)
sets the View that stores the unique data. For rank-2 underlying containers.
ScalarView< DataScalar, DeviceType > allocateDynRankViewMatchingUnderlying() const
Returns a DynRankView that matches the underlying Kokkos::View object in value_type, layout, and dimension.
void storeInPlaceQuotient(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B)
stores the in-place (entrywise) quotient, A ./ B, into this container.
static Data< DataScalar, DeviceType > allocateDotProductResult(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B)
void storeInPlaceSum(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B)
stores the in-place (entrywise) sum, A .+ B, into this container.
Data(const ScalarView< DataScalar, DeviceType > &data, int rank, Kokkos::Array< int, 7 > extents, Kokkos::Array< DataVariationType, 7 > variationType, const int blockPlusDiagonalLastNonDiagonal=-1)
DynRankView constructor. Will copy to a View of appropriate rank.
void storeMatMat(const bool transposeA, const Data< DataScalar, DeviceType > &A_MatData, const bool transposeB, const Data< DataScalar, DeviceType > &B_MatData)
KOKKOS_INLINE_FUNCTION ordinal_type getUnderlyingViewSize() const
returns the number of entries in the View that stores the unique data
void allocateAndCopyFromDynRankView(ScalarView< DataScalar, DeviceType > data)
allocate an underlying View that matches the provided DynRankView in dimensions, and copy...
static KOKKOS_INLINE_FUNCTION int blockPlusDiagonalNumNondiagonalEntries(const int &lastNondiagonal)
Returns the number of non-diagonal entries based on the last non-diagonal. Only applicable for BLOCK_...
KOKKOS_INLINE_FUNCTION enable_if_t< rank==3, const Kokkos::View< typename RankExpander< DataScalar, rank >::value_type, DeviceType > & > getUnderlyingView() const
Returns the underlying view. Throws an exception if the underlying view is not rank 3...
void setActiveDims()
class initialization method. Called by constructors.
Data()
default constructor (empty data)
void setExtent(const ordinal_type &d, const ordinal_type &newExtent)
sets the logical extent in the specified dimension. If needed, the underlying data container is resiz...
A singleton class for a DynRankView containing exactly one zero entry. (Technically, the entry is DataScalar(), the default value for the scalar type.) This allows View-wrapping classes to return a reference to zero, even when that zero is not explicitly stored in the wrapped views.
void storeInPlaceProduct(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B)
stores the in-place (entrywise) product, A .* B, into this container.
KOKKOS_INLINE_FUNCTION enable_if_t< rank==4, const Kokkos::View< typename RankExpander< DataScalar, rank >::value_type, DeviceType > & > getUnderlyingView() const
Returns the underlying view. Throws an exception if the underlying view is not rank 4...
KOKKOS_INLINE_FUNCTION void setUnderlyingView1(const Kokkos::View< DataScalar *, DeviceType > &view)
sets the View that stores the unique data. For rank-1 underlying containers.
KOKKOS_INLINE_FUNCTION unsigned rank() const
Returns the logical rank of the Data container.
KOKKOS_INLINE_FUNCTION int extent_int(const int &r) const
Returns the logical extent in the specified dimension.
Defines DimensionInfo struct that allows specification of a dimension within a Data object...
Defines DataVariationType enum that specifies the types of variation possible within a Data object...
KOKKOS_INLINE_FUNCTION enable_if_t< rank==2, const Kokkos::View< typename RankExpander< DataScalar, rank >::value_type, DeviceType > & > getUnderlyingView() const
Returns the underlying view. Throws an exception if the underlying view is not rank 2...
KOKKOS_INLINE_FUNCTION int getDataExtent(const ordinal_type &d) const
returns the true extent of the data corresponding to the logical dimension provided; if the data does...
KOKKOS_INLINE_FUNCTION int getUnderlyingViewExtent(const int &dim) const
Returns the extent of the underlying view in the specified dimension.
Data(ScalarView< DataScalar, DeviceType > data)
copy constructor modeled after the copy-like constructor above. Not as efficient as the implicit copy...
KOKKOS_INLINE_FUNCTION void setUnderlyingView4(const Kokkos::View< DataScalar ****, DeviceType > &view)
sets the View that stores the unique data. For rank-4 underlying containers.
static Data< DataScalar, DeviceType > allocateContractionResult(const Data< DataScalar, DeviceType > &A, const Data< DataScalar, DeviceType > &B, const int &numContractionDims)
KOKKOS_INLINE_FUNCTION const int & blockPlusDiagonalLastNonDiagonal() const
For a Data object containing data with variation type BLOCK_PLUS_DIAGONAL, returns the row and column...
KOKKOS_INLINE_FUNCTION const Kokkos::View< DataScalar **, DeviceType > & getUnderlyingView2() const
returns the View that stores the unique data. For rank-2 underlying containers.
KOKKOS_INLINE_FUNCTION void setUnderlyingView7(const Kokkos::View< DataScalar *******, DeviceType > &view)
sets the View that stores the unique data. For rank-7 underlying containers.
KOKKOS_INLINE_FUNCTION return_type getEntry(const IntArgs &...intArgs) const
Returns a (read-only) value corresponding to the specified logical data location. ...
static Data< DataScalar, DeviceType > allocateMatMatResult(const bool transposeA, const Data< DataScalar, DeviceType > &A_MatData, const bool transposeB, const Data< DataScalar, DeviceType > &B_MatData)
Data(const Data< DataScalar, OtherDeviceType > &data)
copy-like constructor for differing execution spaces. This does a deep_copy of the underlying view...