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 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
35  using FamilyVectorArray = Kokkos::Array< VectorArray, Parameters::MaxTensorComponents>;
36 
37  FamilyVectorArray vectorComponents_; // outer: family ordinal; inner: component/spatial dimension ordinal
38  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_.
39 
40  int totalDimension_;
41  Kokkos::Array<int, Parameters::MaxVectorComponents> dimToComponent_;
42  Kokkos::Array<int, Parameters::MaxVectorComponents> dimToComponentDim_;
43  Kokkos::Array<int, Parameters::MaxVectorComponents> numDimsForComponent_;
44 
45  Kokkos::Array<int,Parameters::MaxTensorComponents> familyFieldUpperBound_; // one higher than the end of family indicated
46 
47  unsigned numFamilies_; // number of valid entries in vectorComponents_
48  unsigned numComponents_; // number of valid entries in each entry of vectorComponents_
49  unsigned numPoints_; // the second dimension of each (valid) TensorData entry
50 
54  void initialize()
55  {
56  int lastFieldUpperBound = 0;
57  int numPoints = 0;
58  axialComponents_ = true; // will set to false if we find any valid entries that are not on the "diagonal" (like position for family/component)
59  for (unsigned i=0; i<numFamilies_; i++)
60  {
61  bool validEntryFoundForFamily = false;
62  int numFieldsInFamily = 0;
63  for (unsigned j=0; j<numComponents_; j++)
64  {
65  if (vectorComponents_[i][j].isValid())
66  {
67  if (!validEntryFoundForFamily)
68  {
69  numFieldsInFamily = vectorComponents_[i][j].extent_int(0); // (F,P[,D])
70  validEntryFoundForFamily = true;
71  }
72  else
73  {
74  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");
75  }
76  if (numPoints == 0)
77  {
78  numPoints = vectorComponents_[i][j].extent_int(1); // (F,P[,D])
79  }
80  else
81  {
82  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");
83  }
84  if (i != j)
85  {
86  // valid entry found that is not on the "diagonal": axialComponents is false
87  axialComponents_ = false;
88  }
89  }
90  }
91  lastFieldUpperBound += numFieldsInFamily;
92  familyFieldUpperBound_[i] = lastFieldUpperBound;
93  INTREPID2_TEST_FOR_EXCEPTION(!validEntryFoundForFamily, std::invalid_argument, "Each family must have at least one valid TensorData entry");
94  }
95 
96  // do a pass through components to determine total component dim (totalDimension_) and size lookups appropriately
97  int currentDim = 0;
98  for (unsigned j=0; j<numComponents_; j++)
99  {
100  bool validEntryFoundForComponent = false;
101  int numDimsForComponent = 0;
102  for (unsigned i=0; i<numFamilies_; i++)
103  {
104  if (vectorComponents_[i][j].isValid())
105  {
106  if (!validEntryFoundForComponent)
107  {
108  validEntryFoundForComponent = true;
109  numDimsForComponent = vectorComponents_[i][j].extent_int(2); // (F,P,D) container or (F,P) container
110  }
111  else
112  {
113  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");
114  }
115  }
116  }
117  if (!validEntryFoundForComponent)
118  {
119  // assume that the component takes up exactly one space dim
120  numDimsForComponent = 1;
121  }
122 
123  numDimsForComponent_[j] = numDimsForComponent;
124 
125  currentDim += numDimsForComponent;
126  }
127  totalDimension_ = currentDim;
128 
129  currentDim = 0;
130  for (unsigned j=0; j<numComponents_; j++)
131  {
132  int numDimsForComponent = numDimsForComponent_[j];
133  for (int dim=0; dim<numDimsForComponent; dim++)
134  {
135  dimToComponent_[currentDim+dim] = j;
136  dimToComponentDim_[currentDim+dim] = dim;
137  }
138  currentDim += numDimsForComponent;
139  }
140  numPoints_ = numPoints;
141  }
142  public:
149  template<size_t numFamilies, size_t numComponents>
150  VectorData(Kokkos::Array< Kokkos::Array<TensorData<Scalar,DeviceType>, numComponents>, numFamilies> vectorComponents)
151  :
152  numFamilies_(numFamilies),
153  numComponents_(numComponents)
154  {
155  static_assert(numFamilies <= Parameters::MaxTensorComponents, "numFamilies must be less than Parameters::MaxTensorComponents");
156  static_assert(numComponents <= Parameters::MaxVectorComponents, "numComponents must be less than Parameters::MaxVectorComponents");
157  for (unsigned i=0; i<numFamilies; i++)
158  {
159  for (unsigned j=0; j<numComponents; j++)
160  {
161  vectorComponents_[i][j] = vectorComponents[i][j];
162  }
163  }
164  initialize();
165  }
166 
173  VectorData(const std::vector< std::vector<TensorData<Scalar,DeviceType> > > &vectorComponents)
174  {
175  numFamilies_ = vectorComponents.size();
176  INTREPID2_TEST_FOR_EXCEPTION(numFamilies_ <= 0, std::invalid_argument, "numFamilies must be at least 1");
177  numComponents_ = vectorComponents[0].size();
178  for (unsigned i=1; i<numFamilies_; i++)
179  {
180  INTREPID2_TEST_FOR_EXCEPTION(vectorComponents[i].size() != numComponents_, std::invalid_argument, "each family must have the same number of components");
181  }
182 
183  INTREPID2_TEST_FOR_EXCEPTION(numFamilies_ > Parameters::MaxTensorComponents, std::invalid_argument, "numFamilies must be at most Parameters::MaxTensorComponents");
184  INTREPID2_TEST_FOR_EXCEPTION(numComponents_ > Parameters::MaxVectorComponents, std::invalid_argument, "numComponents must be at most Parameters::MaxVectorComponents");
185  for (unsigned i=0; i<numFamilies_; i++)
186  {
187  for (unsigned j=0; j<numComponents_; j++)
188  {
189  vectorComponents_[i][j] = vectorComponents[i][j];
190  }
191  }
192  initialize();
193  }
194 
202  template<size_t numComponents>
204  {
205  if (axialComponents)
206  {
207  numFamilies_ = numComponents;
208  numComponents_ = numComponents;
209  for (unsigned d=0; d<numComponents_; d++)
210  {
211  vectorComponents_[d][d] = vectorComponents[d];
212  }
213  }
214  else
215  {
216  numFamilies_ = 1;
217  numComponents_ = numComponents;
218  for (unsigned d=0; d<numComponents_; d++)
219  {
220  vectorComponents_[0][d] = vectorComponents[d];
221  }
222  }
223  initialize();
224  }
225 
233  VectorData(std::vector< TensorData<Scalar,DeviceType> > vectorComponents, bool axialComponents)
234  : numComponents_(vectorComponents.size())
235  {
236  if (axialComponents)
237  {
238  numFamilies_ = numComponents_;
239  for (unsigned d=0; d<numComponents_; d++)
240  {
241  vectorComponents_[d][d] = vectorComponents[d];
242  }
243  }
244  else
245  {
246  numFamilies_ = 1;
247  for (unsigned d=0; d<numComponents_; d++)
248  {
249  vectorComponents_[0][d] = vectorComponents[d];
250  }
251  }
252  initialize();
253  }
254 
256  template<typename OtherDeviceType, class = typename std::enable_if< std::is_same<typename DeviceType::memory_space, typename OtherDeviceType::memory_space>::value>::type,
257  class = typename std::enable_if<!std::is_same<DeviceType,OtherDeviceType>::value>::type>
258  VectorData(const VectorData<Scalar,OtherDeviceType> &vectorData)
259  :
260  numFamilies_(vectorData.numFamilies()),
261  numComponents_(vectorData.numComponents())
262  {
263  if (vectorData.isValid())
264  {
265  for (unsigned i=0; i<numFamilies_; i++)
266  {
267  for (unsigned j=0; j<numComponents_; j++)
268  {
269  vectorComponents_[i][j] = vectorData.getComponent(i, j);
270  }
271  }
272  initialize();
273  }
274  }
275 
277  template<typename OtherDeviceType, class = typename std::enable_if<!std::is_same<typename DeviceType::memory_space, typename OtherDeviceType::memory_space>::value>::type>
279  :
280  numFamilies_(vectorData.numFamilies()),
281  numComponents_(vectorData.numComponents())
282  {
283  if (vectorData.isValid())
284  {
285  for (unsigned i=0; i<numFamilies_; i++)
286  {
287  for (unsigned j=0; j<numComponents_; j++)
288  {
289  vectorComponents_[i][j] = vectorData.getComponent(i, j);
290  }
291  }
292  initialize();
293  }
294  }
295 
298  :
299  VectorData(Kokkos::Array< TensorData<Scalar,DeviceType>, 1>(data), true)
300  {}
301 
304  :
305  VectorData(Kokkos::Array< TensorData<Scalar,DeviceType>, 1>({TensorData<Scalar,DeviceType>(data)}), true)
306  {}
307 
309  VectorData()
310  :
311  numFamilies_(0), numComponents_(0)
312  {}
313 
315  KOKKOS_INLINE_FUNCTION
316  bool axialComponents() const
317  {
318  return axialComponents_;
319  }
320 
322  KOKKOS_INLINE_FUNCTION
323  int numDimsForComponent(int &componentOrdinal) const
324  {
325  return numDimsForComponent_[componentOrdinal];
326  }
327 
329  KOKKOS_INLINE_FUNCTION
330  int numFields() const
331  {
332  return familyFieldUpperBound_[numFamilies_-1];
333  }
334 
336  KOKKOS_INLINE_FUNCTION
337  int familyFieldOrdinalOffset(const int &familyOrdinal) const
338  {
339  return (familyOrdinal == 0) ? 0 : familyFieldUpperBound_[familyOrdinal-1];
340  }
341 
343  KOKKOS_INLINE_FUNCTION
344  int numPoints() const
345  {
346  return numPoints_;
347  }
348 
350  KOKKOS_INLINE_FUNCTION
351  int spaceDim() const
352  {
353  return totalDimension_;
354  }
355 
357  KOKKOS_INLINE_FUNCTION
358  Scalar operator()(const int &fieldOrdinal, const int &pointOrdinal, const int &dim) const
359  {
360  int fieldOrdinalInFamily = fieldOrdinal;
361  int familyForField = 0;
362  if (numFamilies_ > 1)
363  {
364  familyForField = -1;
365  int previousFamilyEnd = -1;
366  int fieldAdjustment = 0;
367  // this loop is written in such a way as to avoid branching for CUDA performance
368  for (unsigned family=0; family<numFamilies_; family++)
369  {
370  const bool fieldInRange = (fieldOrdinal > previousFamilyEnd) && (fieldOrdinal < familyFieldUpperBound_[family]);
371  familyForField = fieldInRange ? family : familyForField;
372  fieldAdjustment = fieldInRange ? previousFamilyEnd + 1 : fieldAdjustment;
373  previousFamilyEnd = familyFieldUpperBound_[family] - 1;
374  }
375 #ifdef HAVE_INTREPID2_DEBUG
376  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(familyForField == -1, std::invalid_argument, "family for field not found");
377 #endif
378 
379  fieldOrdinalInFamily = fieldOrdinal - fieldAdjustment;
380  }
381 
382  const int componentOrdinal = dimToComponent_[dim];
383 
384  const auto &component = vectorComponents_[familyForField][componentOrdinal];
385  if (component.isValid())
386  {
387  const int componentRank = component.rank();
388  if (componentRank == 2) // (F,P) container
389  {
390  return component(fieldOrdinalInFamily,pointOrdinal);
391  }
392  else if (componentRank == 3) // (F,P,D)
393  {
394  return component(fieldOrdinalInFamily,pointOrdinal,dimToComponentDim_[dim]);
395  }
396  else
397  {
398  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::logic_error, "Unsupported component rank");
399  return -1; // unreachable, but compilers complain otherwise...
400  }
401  }
402  else // invalid component: placeholder means 0
403  {
404  return 0;
405  }
406  }
407 
412  KOKKOS_INLINE_FUNCTION
413  const TensorData<Scalar,DeviceType> & getComponent(const int &componentOrdinal) const
414  {
415  if (axialComponents_)
416  {
417  return vectorComponents_[componentOrdinal][componentOrdinal];
418  }
419  else if (numFamilies_ == 1)
420  {
421  return vectorComponents_[0][componentOrdinal];
422  }
423  else
424  {
425  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::invalid_argument, "Ambiguous component request; use the two-argument getComponent()");
426  }
427  // nvcc warns here about a missing return.
428  return vectorComponents_[6][6]; // likely this is an empty container, but anyway it's an unreachable line...
429  }
430 
436  KOKKOS_INLINE_FUNCTION
437  const TensorData<Scalar,DeviceType> & getComponent(const int &familyOrdinal, const int &componentOrdinal) const
438  {
439  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(familyOrdinal < 0, std::invalid_argument, "familyOrdinal must be non-negative");
440  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(static_cast<unsigned>(familyOrdinal) >= numFamilies_, std::invalid_argument, "familyOrdinal out of bounds");
441  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(componentOrdinal < 0, std::invalid_argument, "componentOrdinal must be non-negative");
442  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(static_cast<unsigned>(componentOrdinal) >= numComponents_, std::invalid_argument, "componentOrdinal out of bounds");
443 
444  return vectorComponents_[familyOrdinal][componentOrdinal];
445  }
446 
448  KOKKOS_INLINE_FUNCTION
449  int extent_int(const int &r) const
450  {
451  // logically (F,P,D) container
452  if (r == 0) return numFields();
453  else if (r == 1) return numPoints();
454  else if (r == 2) return totalDimension_;
455  else if (r > 2) return 1;
456 
457  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(true, std::invalid_argument, "Unsupported rank");
458  return -1; // unreachable; return here included to avoid compiler warnings.
459  }
460 
462  KOKKOS_INLINE_FUNCTION
463  unsigned rank() const
464  {
465  // logically (F,P,D) container
466  return 3;
467  }
468 
470  KOKKOS_INLINE_FUNCTION int numComponents() const
471  {
472  return numComponents_;
473  }
474 
476  KOKKOS_INLINE_FUNCTION int numFamilies() const
477  {
478  return numFamilies_;
479  }
480 
482  KOKKOS_INLINE_FUNCTION int familyForFieldOrdinal(const int &fieldOrdinal) const
483  {
484  int matchingFamily = -1;
485  int fieldsSoFar = 0;
486  // logic here is a little bit more complex to avoid branch divergence
487  for (int i=0; i<numFamilies_; i++)
488  {
489  const bool fieldIsBeyondPreviousFamily = (fieldOrdinal >= fieldsSoFar);
490  fieldsSoFar += numFieldsInFamily(i);
491  const bool fieldIsBeforeCurrentFamily = (fieldOrdinal < fieldsSoFar);
492  const bool fieldMatchesFamily = fieldIsBeyondPreviousFamily && fieldIsBeforeCurrentFamily;
493  matchingFamily = fieldMatchesFamily ? i : matchingFamily;
494  }
495  return matchingFamily;
496  }
497 
499  KOKKOS_INLINE_FUNCTION int numFieldsInFamily(const unsigned &familyOrdinal) const
500  {
501  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(familyOrdinal >= numFamilies_, std::invalid_argument, "familyOrdinal out of bounds");
502  int numFields = -1;
503  for (unsigned componentOrdinal=0; componentOrdinal<numComponents_; componentOrdinal++)
504  {
505  numFields = vectorComponents_[familyOrdinal][componentOrdinal].isValid() ? vectorComponents_[familyOrdinal][componentOrdinal].extent_int(0) : numFields;
506  }
507  INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(numFields < 0, std::logic_error, "numFields was not properly initialized");
508  return numFields;
509  }
510 
512  KOKKOS_INLINE_FUNCTION constexpr bool isValid() const
513  {
514  return numComponents_ > 0;
515  }
516  };
517 }
518 
519 #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...