Intrepid2
Intrepid2_VectorData.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 
16 #ifndef Intrepid2_VectorData_h
17 #define Intrepid2_VectorData_h
18 
19 namespace Intrepid2 {
30  template<class Scalar, typename DeviceType>
31  class VectorData
32  {
33  public:
34  using value_type = Scalar;
35  using VectorArray = Kokkos::Array< TensorData<Scalar,DeviceType>, Parameters::MaxVectorComponents >; // for axis-aligned case, these correspond entry-wise to the axis with which the vector values align
36  using FamilyVectorArray = Kokkos::Array< VectorArray, Parameters::MaxTensorComponents>;
37 
38  FamilyVectorArray vectorComponents_; // outer: family ordinal; inner: component/spatial dimension ordinal
39  bool axialComponents_; // if true, each entry in vectorComponents_ is an axial component vector; for 3D: (f1,0,0); (0,f2,0); (0,0,f3). The 0s are represented by trivial/invalid TensorData objects. In this case, numComponents_ == numFamilies_.
40 
41  int totalDimension_;
42  Kokkos::Array<int, Parameters::MaxVectorComponents> dimToComponent_;
43  Kokkos::Array<int, Parameters::MaxVectorComponents> dimToComponentDim_;
44  Kokkos::Array<int, Parameters::MaxVectorComponents> numDimsForComponent_;
45 
46  Kokkos::Array<int,Parameters::MaxTensorComponents> familyFieldUpperBound_; // one higher than the end of family indicated
47 
48  unsigned numFamilies_; // number of valid entries in vectorComponents_
49  unsigned numComponents_; // number of valid entries in each entry of vectorComponents_
50  unsigned numPoints_; // the second dimension of each (valid) TensorData entry
51 
55  void initialize()
56  {
57  int lastFieldUpperBound = 0;
58  int numPoints = 0;
59  axialComponents_ = true; // will set to false if we find any valid entries that are not on the "diagonal" (like position for family/component)
60  for (unsigned i=0; i<numFamilies_; i++)
61  {
62  bool validEntryFoundForFamily = false;
63  int numFieldsInFamily = 0;
64  for (unsigned j=0; j<numComponents_; j++)
65  {
66  if (vectorComponents_[i][j].isValid())
67  {
68  if (!validEntryFoundForFamily)
69  {
70  numFieldsInFamily = vectorComponents_[i][j].extent_int(0); // (F,P[,D])
71  validEntryFoundForFamily = true;
72  }
73  else
74  {
75  INTREPID2_TEST_FOR_EXCEPTION(numFieldsInFamily != vectorComponents_[i][j].extent_int(0), std::invalid_argument, "Each valid TensorData entry within a family must agree with the others on the number of fields in the family");
76  }
77  if (numPoints == 0)
78  {
79  numPoints = vectorComponents_[i][j].extent_int(1); // (F,P[,D])
80  }
81  else
82  {
83  INTREPID2_TEST_FOR_EXCEPTION(numPoints != vectorComponents_[i][j].extent_int(1), std::invalid_argument, "Each valid TensorData entry must agree with the others on the number of points");
84  }
85  if (i != j)
86  {
87  // valid entry found that is not on the "diagonal": axialComponents is false
88  axialComponents_ = false;
89  }
90  }
91  }
92  lastFieldUpperBound += numFieldsInFamily;
93  familyFieldUpperBound_[i] = lastFieldUpperBound;
94  INTREPID2_TEST_FOR_EXCEPTION(!validEntryFoundForFamily, std::invalid_argument, "Each family must have at least one valid TensorData entry");
95  }
96 
97  // do a pass through components to determine total component dim (totalDimension_) and size lookups appropriately
98  int currentDim = 0;
99  for (unsigned j=0; j<numComponents_; j++)
100  {
101  bool validEntryFoundForComponent = false;
102  int numDimsForComponent = 0;
103  for (unsigned i=0; i<numFamilies_; i++)
104  {
105  if (vectorComponents_[i][j].isValid())
106  {
107  if (!validEntryFoundForComponent)
108  {
109  validEntryFoundForComponent = true;
110  numDimsForComponent = vectorComponents_[i][j].extent_int(2); // (F,P,D) container or (F,P) container
111  }
112  else
113  {
114  INTREPID2_TEST_FOR_EXCEPTION(numDimsForComponent != vectorComponents_[i][j].extent_int(2), std::invalid_argument, "Components in like positions must agree across families on the number of dimensions spanned by that component position");
115  }
116  }
117  }
118  if (!validEntryFoundForComponent)
119  {
120  // assume that the component takes up exactly one space dim
121  numDimsForComponent = 1;
122  }
123 
124  numDimsForComponent_[j] = numDimsForComponent;
125 
126  currentDim += numDimsForComponent;
127  }
128  totalDimension_ = currentDim;
129 
130  currentDim = 0;
131  for (unsigned j=0; j<numComponents_; j++)
132  {
133  int numDimsForComponent = numDimsForComponent_[j];
134  for (int dim=0; dim<numDimsForComponent; dim++)
135  {
136  dimToComponent_[currentDim+dim] = j;
137  dimToComponentDim_[currentDim+dim] = dim;
138  }
139  currentDim += numDimsForComponent;
140  }
141  numPoints_ = numPoints;
142  }
143  public:
150  template<size_t numFamilies, size_t numComponents>
151  VectorData(Kokkos::Array< Kokkos::Array<TensorData<Scalar,DeviceType>, numComponents>, numFamilies> vectorComponents)
152  :
153  numFamilies_(numFamilies),
154  numComponents_(numComponents)
155  {
156  static_assert(numFamilies <= Parameters::MaxTensorComponents, "numFamilies must be less than Parameters::MaxTensorComponents");
157  static_assert(numComponents <= Parameters::MaxVectorComponents, "numComponents must be less than Parameters::MaxVectorComponents");
158  for (unsigned i=0; i<numFamilies; i++)
159  {
160  for (unsigned j=0; j<numComponents; j++)
161  {
162  vectorComponents_[i][j] = vectorComponents[i][j];
163  }
164  }
165  initialize();
166  }
167 
174  VectorData(const std::vector< std::vector<TensorData<Scalar,DeviceType> > > &vectorComponents)
175  {
176  numFamilies_ = vectorComponents.size();
177  INTREPID2_TEST_FOR_EXCEPTION(numFamilies_ <= 0, std::invalid_argument, "numFamilies must be at least 1");
178  numComponents_ = vectorComponents[0].size();
179  for (unsigned i=1; i<numFamilies_; i++)
180  {
181  INTREPID2_TEST_FOR_EXCEPTION(vectorComponents[i].size() != numComponents_, std::invalid_argument, "each family must have the same number of components");
182  }
183 
184  INTREPID2_TEST_FOR_EXCEPTION(numFamilies_ > Parameters::MaxTensorComponents, std::invalid_argument, "numFamilies must be at most Parameters::MaxTensorComponents");
185  INTREPID2_TEST_FOR_EXCEPTION(numComponents_ > Parameters::MaxVectorComponents, std::invalid_argument, "numComponents must be at most Parameters::MaxVectorComponents");
186  for (unsigned i=0; i<numFamilies_; i++)
187  {
188  for (unsigned j=0; j<numComponents_; j++)
189  {
190  vectorComponents_[i][j] = vectorComponents[i][j];
191  }
192  }
193  initialize();
194  }
195 
203  template<size_t numComponents>
205  {
206  if (axialComponents)
207  {
208  numFamilies_ = numComponents;
209  numComponents_ = numComponents;
210  for (unsigned d=0; d<numComponents_; d++)
211  {
212  vectorComponents_[d][d] = vectorComponents[d];
213  }
214  }
215  else
216  {
217  numFamilies_ = 1;
218  numComponents_ = numComponents;
219  for (unsigned d=0; d<numComponents_; d++)
220  {
221  vectorComponents_[0][d] = vectorComponents[d];
222  }
223  }
224  initialize();
225  }
226 
234  VectorData(std::vector< TensorData<Scalar,DeviceType> > vectorComponents, bool axialComponents)
235  : numComponents_(vectorComponents.size())
236  {
237  if (axialComponents)
238  {
239  numFamilies_ = numComponents_;
240  for (unsigned d=0; d<numComponents_; d++)
241  {
242  vectorComponents_[d][d] = vectorComponents[d];
243  }
244  }
245  else
246  {
247  numFamilies_ = 1;
248  for (unsigned d=0; d<numComponents_; d++)
249  {
250  vectorComponents_[0][d] = vectorComponents[d];
251  }
252  }
253  initialize();
254  }
255 
257  template<typename OtherDeviceType, class = typename std::enable_if< std::is_same<typename DeviceType::memory_space, typename OtherDeviceType::memory_space>::value>::type,
258  class = typename std::enable_if<!std::is_same<DeviceType,OtherDeviceType>::value>::type>
259  VectorData(const VectorData<Scalar,OtherDeviceType> &vectorData)
260  :
261  numFamilies_(vectorData.numFamilies()),
262  numComponents_(vectorData.numComponents())
263  {
264  if (vectorData.isValid())
265  {
266  for (unsigned i=0; i<numFamilies_; i++)
267  {
268  for (unsigned j=0; j<numComponents_; j++)
269  {
270  vectorComponents_[i][j] = vectorData.getComponent(i, j);
271  }
272  }
273  initialize();
274  }
275  }
276 
278  template<typename OtherDeviceType, class = typename std::enable_if<!std::is_same<typename DeviceType::memory_space, typename OtherDeviceType::memory_space>::value>::type>
280  :
281  numFamilies_(vectorData.numFamilies()),
282  numComponents_(vectorData.numComponents())
283  {
284  if (vectorData.isValid())
285  {
286  for (unsigned i=0; i<numFamilies_; i++)
287  {
288  for (unsigned j=0; j<numComponents_; j++)
289  {
290  vectorComponents_[i][j] = vectorData.getComponent(i, j);
291  }
292  }
293  initialize();
294  }
295  }
296 
299  :
300  VectorData(Kokkos::Array< TensorData<Scalar,DeviceType>, 1>(data), true)
301  {}
302 
305  :
306  VectorData(Kokkos::Array< TensorData<Scalar,DeviceType>, 1>({TensorData<Scalar,DeviceType>(data)}), true)
307  {}
308 
310  VectorData()
311  :
312  numFamilies_(0), numComponents_(0)
313  {}
314 
316  KOKKOS_INLINE_FUNCTION
317  bool axialComponents() const
318  {
319  return axialComponents_;
320  }
321 
323  KOKKOS_INLINE_FUNCTION
324  int numDimsForComponent(int &componentOrdinal) const
325  {
326  return numDimsForComponent_[componentOrdinal];
327  }
328 
330  KOKKOS_INLINE_FUNCTION
331  int numFields() const
332  {
333  return familyFieldUpperBound_[numFamilies_-1];
334  }
335 
337  KOKKOS_INLINE_FUNCTION
338  int familyFieldOrdinalOffset(const int &familyOrdinal) const
339  {
340  return (familyOrdinal == 0) ? 0 : familyFieldUpperBound_[familyOrdinal-1];
341  }
342 
344  KOKKOS_INLINE_FUNCTION
345  int numPoints() const
346  {
347  return numPoints_;
348  }
349 
351  KOKKOS_INLINE_FUNCTION
352  int spaceDim() const
353  {
354  return totalDimension_;
355  }
356 
358  KOKKOS_INLINE_FUNCTION
359  Scalar operator()(const int &fieldOrdinal, const int &pointOrdinal, const int &dim) const
360  {
361  int fieldOrdinalInFamily = fieldOrdinal;
362  int familyForField = 0;
363  if (numFamilies_ > 1)
364  {
365  familyForField = -1;
366  int previousFamilyEnd = -1;
367  int fieldAdjustment = 0;
368  // this loop is written in such a way as to avoid branching for CUDA performance
369  for (unsigned family=0; family<numFamilies_; family++)
370  {
371  const bool fieldInRange = (fieldOrdinal > previousFamilyEnd) && (fieldOrdinal < familyFieldUpperBound_[family]);
372  familyForField = fieldInRange ? family : familyForField;
373  fieldAdjustment = fieldInRange ? previousFamilyEnd + 1 : fieldAdjustment;
374  previousFamilyEnd = familyFieldUpperBound_[family] - 1;
375  }
376 #ifdef HAVE_INTREPID2_DEBUG
377  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(familyForField == -1, std::invalid_argument, "family for field not found");
378 #endif
379 
380  fieldOrdinalInFamily = fieldOrdinal - fieldAdjustment;
381  }
382 
383  const int componentOrdinal = dimToComponent_[dim];
384 
385  const auto &component = vectorComponents_[familyForField][componentOrdinal];
386  if (component.isValid())
387  {
388  const int componentRank = component.rank();
389  if (componentRank == 2) // (F,P) container
390  {
391  return component(fieldOrdinalInFamily,pointOrdinal);
392  }
393  else if (componentRank == 3) // (F,P,D)
394  {
395  return component(fieldOrdinalInFamily,pointOrdinal,dimToComponentDim_[dim]);
396  }
397  else
398  {
399  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::logic_error, "Unsupported component rank");
400  return -1; // unreachable, but compilers complain otherwise...
401  }
402  }
403  else // invalid component: placeholder means 0
404  {
405  return 0;
406  }
407  }
408 
413  KOKKOS_INLINE_FUNCTION
414  const TensorData<Scalar,DeviceType> & getComponent(const int &componentOrdinal) const
415  {
416  if (axialComponents_)
417  {
418  return vectorComponents_[componentOrdinal][componentOrdinal];
419  }
420  else if (numFamilies_ == 1)
421  {
422  return vectorComponents_[0][componentOrdinal];
423  }
424  else
425  {
426  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::invalid_argument, "Ambiguous component request; use the two-argument getComponent()");
427  }
428  // nvcc warns here about a missing return.
429  return vectorComponents_[6][6]; // likely this is an empty container, but anyway it's an unreachable line...
430  }
431 
437  KOKKOS_INLINE_FUNCTION
438  const TensorData<Scalar,DeviceType> & getComponent(const int &familyOrdinal, const int &componentOrdinal) const
439  {
440  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(familyOrdinal < 0, std::invalid_argument, "familyOrdinal must be non-negative");
441  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(static_cast<unsigned>(familyOrdinal) >= numFamilies_, std::invalid_argument, "familyOrdinal out of bounds");
442  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(componentOrdinal < 0, std::invalid_argument, "componentOrdinal must be non-negative");
443  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(static_cast<unsigned>(componentOrdinal) >= numComponents_, std::invalid_argument, "componentOrdinal out of bounds");
444 
445  return vectorComponents_[familyOrdinal][componentOrdinal];
446  }
447 
449  KOKKOS_INLINE_FUNCTION
450  int extent_int(const int &r) const
451  {
452  // logically (F,P,D) container
453  if (r == 0) return numFields();
454  else if (r == 1) return numPoints();
455  else if (r == 2) return totalDimension_;
456  else if (r > 2) return 1;
457 
458  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::invalid_argument, "Unsupported rank");
459  return -1; // unreachable; return here included to avoid compiler warnings.
460  }
461 
463  KOKKOS_INLINE_FUNCTION
464  unsigned rank() const
465  {
466  // logically (F,P,D) container
467  return 3;
468  }
469 
471  KOKKOS_INLINE_FUNCTION int numComponents() const
472  {
473  return numComponents_;
474  }
475 
477  KOKKOS_INLINE_FUNCTION int numFamilies() const
478  {
479  return numFamilies_;
480  }
481 
483  KOKKOS_INLINE_FUNCTION int familyForFieldOrdinal(const int &fieldOrdinal) const
484  {
485  int matchingFamily = -1;
486  int fieldsSoFar = 0;
487  // logic here is a little bit more complex to avoid branch divergence
488  for (int i=0; i<numFamilies_; i++)
489  {
490  const bool fieldIsBeyondPreviousFamily = (fieldOrdinal >= fieldsSoFar);
491  fieldsSoFar += numFieldsInFamily(i);
492  const bool fieldIsBeforeCurrentFamily = (fieldOrdinal < fieldsSoFar);
493  const bool fieldMatchesFamily = fieldIsBeyondPreviousFamily && fieldIsBeforeCurrentFamily;
494  matchingFamily = fieldMatchesFamily ? i : matchingFamily;
495  }
496  return matchingFamily;
497  }
498 
500  KOKKOS_INLINE_FUNCTION int numFieldsInFamily(const unsigned &familyOrdinal) const
501  {
502  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(familyOrdinal >= numFamilies_, std::invalid_argument, "familyOrdinal out of bounds");
503  int numFields = -1;
504  for (unsigned componentOrdinal=0; componentOrdinal<numComponents_; componentOrdinal++)
505  {
506  numFields = vectorComponents_[familyOrdinal][componentOrdinal].isValid() ? vectorComponents_[familyOrdinal][componentOrdinal].extent_int(0) : numFields;
507  }
508  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(numFields < 0, std::logic_error, "numFields was not properly initialized");
509  return numFields;
510  }
511 
513  KOKKOS_INLINE_FUNCTION constexpr bool isValid() const
514  {
515  return numComponents_ > 0;
516  }
517  };
518 }
519 
520 #endif /* Intrepid2_VectorData_h */
KOKKOS_INLINE_FUNCTION Scalar operator()(const int &fieldOrdinal, const int &pointOrdinal, const int &dim) const
Accessor for the container, which has shape (F,P,D).
KOKKOS_INLINE_FUNCTION int numFields() const
Returns the total number of fields; corresponds to the first dimension of this container.
KOKKOS_INLINE_FUNCTION int numFieldsInFamily(const unsigned &familyOrdinal) const
returns the number of fields in the specified family
VectorData(Kokkos::Array< Kokkos::Array< TensorData< Scalar, DeviceType >, numComponents >, numFamilies > vectorComponents)
Standard constructor for the arbitrary case, accepting a fixed-length array argument.
#define INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(test, x, msg)
VectorData(const std::vector< std::vector< TensorData< Scalar, DeviceType > > > &vectorComponents)
Standard constructor for the arbitrary case, accepting a variable-length std::vector argument...
VectorData(Kokkos::Array< TensorData< Scalar, DeviceType >, numComponents > vectorComponents, bool axialComponents)
Simplified constructor for gradients of HGRAD, and values of HDIV and HCURL vector bases...
void initialize()
Initialize members based on constructor parameters; all constructors should call this after populatin...
VectorData(Data< Scalar, DeviceType > data)
Simple 1-argument constructor for the case of trivial tensor product structure. The argument should h...
KOKKOS_INLINE_FUNCTION int numFamilies() const
returns the number of families
KOKKOS_INLINE_FUNCTION int numDimsForComponent(int &componentOrdinal) const
Returns the number of dimensions corresponding to the specified component.
KOKKOS_INLINE_FUNCTION const TensorData< Scalar, DeviceType > & getComponent(const int &familyOrdinal, const int &componentOrdinal) const
General component accessor.
VectorData(const VectorData< Scalar, OtherDeviceType > &vectorData)
copy-like constructor for differing execution spaces. This does a deep copy of underlying views...
KOKKOS_INLINE_FUNCTION const TensorData< Scalar, DeviceType > & getComponent(const int &componentOrdinal) const
Single-argument component accessor for the axial-component or the single-family case; in this case...
KOKKOS_INLINE_FUNCTION int extent_int(const int &r) const
Returns the extent in the specified dimension as an int.
static constexpr ordinal_type MaxVectorComponents
Maximum number of components that a VectorData object will store – 66 corresponds to OPERATOR_D10 on ...
KOKKOS_INLINE_FUNCTION constexpr bool isValid() const
returns true for containers that have data; false for those that don&#39;t (e.g., those that have been co...
VectorData(TensorData< Scalar, DeviceType > data)
Simple 1-argument constructor for the case of trivial tensor product structure. The argument should h...
KOKKOS_INLINE_FUNCTION int spaceDim() const
Returns the spatial dimension; corresponds to the third dimension of this container.
KOKKOS_INLINE_FUNCTION int familyFieldOrdinalOffset(const int &familyOrdinal) const
Returns the field ordinal offset for the specified family.
KOKKOS_INLINE_FUNCTION unsigned rank() const
Returns the rank of this container, which is 3.
VectorData(std::vector< TensorData< Scalar, DeviceType > > vectorComponents, bool axialComponents)
Simplified constructor for gradients of HGRAD, and values of HDIV and HCURL vector bases...
KOKKOS_INLINE_FUNCTION int familyForFieldOrdinal(const int &fieldOrdinal) const
Returns the family ordinal corresponding to the indicated field ordinal.
View-like interface to tensor data; tensor components are stored separately and multiplied together a...
KOKKOS_INLINE_FUNCTION bool axialComponents() const
Returns true only if the families are so structured that the first family has nonzeros only in the x ...
KOKKOS_INLINE_FUNCTION int numPoints() const
Returns the number of points; corresponds to the second dimension of this container.
KOKKOS_INLINE_FUNCTION int numComponents() const
returns the number of components
Reference-space field values for a basis, designed to support typical vector-valued bases...
static constexpr ordinal_type MaxTensorComponents
Maximum number of tensor/Cartesian products that can be taken: this allows hypercube basis in 7D to b...