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
19namespace Intrepid2 {
30 template<class Scalar, typename DeviceType>
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::MaxBasisFamilies>;
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
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
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
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_[Parameters::MaxBasisFamilies-1][Parameters::MaxTensorComponents-1]; // 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 */
#define INTREPID2_TEST_FOR_EXCEPTION_DEVICE_SAFE(test, x, msg)
Wrapper around a Kokkos::View that allows data that is constant or repeating in various logical dimen...
static constexpr ordinal_type MaxTensorComponents
Maximum number of tensor/Cartesian products that can be taken: this allows hypercube basis in 7D to b...
static constexpr ordinal_type MaxVectorComponents
Maximum number of components that a VectorData object will store – 66 corresponds to OPERATOR_D10 on ...
static constexpr ordinal_type MaxBasisFamilies
Maximum number of families that a VectorData object will store – 3 corresponds to H(div) and H(curl) ...
View-like interface to tensor data; tensor components are stored separately and multiplied together a...
Reference-space field values for a basis, designed to support typical vector-valued bases.
KOKKOS_INLINE_FUNCTION int numPoints() const
Returns the number of points; corresponds to the second dimension of this container.
VectorData(const VectorData< Scalar, OtherDeviceType > &vectorData)
copy-like constructor for differing execution spaces. This does a deep copy of underlying views.
VectorData(Kokkos::Array< Kokkos::Array< TensorData< Scalar, DeviceType >, numComponents >, numFamilies > vectorComponents)
Standard constructor for the arbitrary case, accepting a fixed-length array argument.
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 ...
VectorData()
default constructor; results in an invalid container.
void initialize()
Initialize members based on constructor parameters; all constructors should call this after populatin...
KOKKOS_INLINE_FUNCTION int numFieldsInFamily(const unsigned &familyOrdinal) const
returns the number of fields in the specified family
VectorData(std::vector< TensorData< Scalar, DeviceType > > vectorComponents, bool axialComponents)
Simplified constructor for gradients of HGRAD, and values of HDIV and HCURL vector bases.
VectorData(const std::vector< std::vector< TensorData< Scalar, DeviceType > > > &vectorComponents)
Standard constructor for the arbitrary case, accepting a variable-length std::vector argument.
KOKKOS_INLINE_FUNCTION int familyForFieldOrdinal(const int &fieldOrdinal) const
Returns the family ordinal corresponding to the indicated field ordinal.
KOKKOS_INLINE_FUNCTION unsigned rank() const
Returns the rank of this container, which is 3.
KOKKOS_INLINE_FUNCTION const TensorData< Scalar, DeviceType > & getComponent(const int &familyOrdinal, const int &componentOrdinal) const
General component accessor.
VectorData(Data< Scalar, DeviceType > data)
Simple 1-argument constructor for the case of trivial tensor product structure. The argument should h...
VectorData(TensorData< Scalar, DeviceType > data)
Simple 1-argument constructor for the case of trivial tensor product structure. The argument should 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 numComponents() const
returns the number of components
KOKKOS_INLINE_FUNCTION int familyFieldOrdinalOffset(const int &familyOrdinal) const
Returns the field ordinal offset for the specified family.
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 constexpr bool isValid() const
returns true for containers that have data; false for those that don't (e.g., those that have been co...
KOKKOS_INLINE_FUNCTION int numFamilies() const
returns the number of families
KOKKOS_INLINE_FUNCTION int spaceDim() const
Returns the spatial dimension; corresponds to the third dimension of this container.
KOKKOS_INLINE_FUNCTION int numDimsForComponent(int &componentOrdinal) const
Returns the number of dimensions corresponding to the specified component.
KOKKOS_INLINE_FUNCTION int numFields() const
Returns the total number of fields; corresponds to the first dimension of this container.
KOKKOS_INLINE_FUNCTION int extent_int(const int &r) const
Returns the extent in the specified dimension as an int.
VectorData(Kokkos::Array< TensorData< Scalar, DeviceType >, numComponents > vectorComponents, bool axialComponents)
Simplified constructor for gradients of HGRAD, and values of HDIV and HCURL vector bases.