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
15
16#ifndef Intrepid2_VectorData_h
17#define Intrepid2_VectorData_h
18
19namespace Intrepid2 {
30 template<class Scalar, typename DeviceType>
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
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
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
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 */
#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 ...
View-like interface to tensor data; tensor components are stored separately and multiplied together a...
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.