Intrepid2
Intrepid2_TensorPoints.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_TensorPoints_h
17#define Intrepid2_TensorPoints_h
18
19#include <Kokkos_Core.hpp>
20
21namespace Intrepid2 {
25 template<class PointScalar, typename DeviceType>
27 public:
28 using value_type = PointScalar;
29 protected:
30 Kokkos::Array< ScalarView<PointScalar,DeviceType>, Parameters::MaxTensorComponents> pointTensorComponents_; // each component has dimensions (P,D)
31 ordinal_type numTensorComponents_;
32 ordinal_type totalPointCount_;
33 ordinal_type totalDimension_;
34 Kokkos::View<ordinal_type*, DeviceType> dimToComponent_;
35 Kokkos::View<ordinal_type*, DeviceType> dimToComponentDim_;
36 Kokkos::Array<ordinal_type, Parameters::MaxTensorComponents> pointModulus_;
37 Kokkos::Array<ordinal_type, Parameters::MaxTensorComponents> pointDivisor_;
38
39 bool isValid_;
40 using reference_type = typename ScalarView<PointScalar,DeviceType>::reference_type;
41
42 void TEST_VALID_POINT_COMPONENTS()
43 {
44#ifdef HAVE_INTREPID2_DEBUG
45 if (isValid_)
46 {
47 for (ordinal_type r=0; r<numTensorComponents_; r++)
48 {
49 const auto & pointComponent = pointTensorComponents_[r];
50 INTREPID2_TEST_FOR_EXCEPTION(2 != pointComponent.rank(), std::invalid_argument, "Each component must have shape (P,D)");
51 INTREPID2_TEST_FOR_EXCEPTION(pointComponent.extent_int(0) <= 0, std::invalid_argument, "Each component must have at least one point");
52 }
53 }
54#endif
55 }
56
61 {
62 TEST_VALID_POINT_COMPONENTS();
63
64 totalPointCount_ = 1;
65 totalDimension_ = 0;
66 for (ordinal_type r=0; r<numTensorComponents_; r++)
67 {
68 totalPointCount_ *= pointTensorComponents_[r].extent_int(0);
69 totalDimension_ += pointTensorComponents_[r].extent_int(1);
70 }
71 ordinal_type pointDivisor = 1;
72 for (ordinal_type r=0; r<numTensorComponents_; r++)
73 {
74 pointModulus_[r] = pointTensorComponents_[r].extent_int(0);
75 pointDivisor_[r] = pointDivisor;
76 pointDivisor *= pointTensorComponents_[r].extent_int(0);
77 }
78 dimToComponent_ = Kokkos::View<ordinal_type*, DeviceType>("dimToComponent_",totalDimension_);
79 dimToComponentDim_ = Kokkos::View<ordinal_type*, DeviceType>("dimToComponentDim_",totalDimension_);
80 ordinal_type d=0;
81 ordinal_type dimsSoFar = 0;
82
83 auto dimToComponentHost = Kokkos::create_mirror_view(dimToComponent_);
84 auto dimToComponentDimHost = Kokkos::create_mirror_view(dimToComponentDim_);
85 for (ordinal_type r=0; r<numTensorComponents_; r++)
86 {
87 const int componentDim = pointTensorComponents_[r].extent_int(1);
88 for (int i=0; i<componentDim; i++)
89 {
90 dimToComponentHost[d] = r;
91 dimToComponentDimHost[d] = d - dimsSoFar;
92 d++;
93 }
94 dimsSoFar += componentDim;
95 }
96 Kokkos::deep_copy(dimToComponent_,dimToComponentHost);
97 Kokkos::deep_copy(dimToComponentDim_,dimToComponentDimHost);
98 }
99 public:
106 template<size_t numTensorComponents>
107 TensorPoints(Kokkos::Array< ScalarView<PointScalar,DeviceType>, numTensorComponents> pointTensorComponents)
108 :
109 numTensorComponents_(numTensorComponents),
110 isValid_(true)
111 {
112 for (unsigned r=0; r<numTensorComponents; r++)
113 {
114 pointTensorComponents_[r] = pointTensorComponents[r];
115 }
116
117 initialize();
118 }
119
127 TensorPoints(TensorPoints otherPointsContainer, std::vector<int> whichDims)
128 :
129 numTensorComponents_(whichDims.size()),
130 isValid_(true)
131 {
132 int r = 0;
133 for (const auto & componentOrdinal : whichDims)
134 {
135 pointTensorComponents_[r++] = otherPointsContainer.getTensorComponent(componentOrdinal);
136 }
137
138 initialize();
139 }
140
147 TensorPoints(std::vector< ScalarView<PointScalar,DeviceType>> pointTensorComponents)
148 :
149 numTensorComponents_(pointTensorComponents.size()),
150 isValid_(true)
151 {
152 for (ordinal_type r=0; r<numTensorComponents_; r++)
153 {
154 pointTensorComponents_[r] = pointTensorComponents[r];
155 }
156
157 initialize();
158 }
159
166 TensorPoints(ScalarView<PointScalar,DeviceType> points)
167 :
168 numTensorComponents_(1),
169 isValid_(true)
170 {
171 pointTensorComponents_[0] = points;
172 initialize();
173 }
174
180 template<class OtherPointsContainer>
181 void copyPointsContainer(ScalarView<PointScalar,DeviceType> toPoints, OtherPointsContainer fromPoints)
182 {
183 const int numPoints = fromPoints.extent_int(0);
184 const int numDims = fromPoints.extent_int(1);
185 using ExecutionSpace = typename DeviceType::execution_space;
186 auto policy = Kokkos::MDRangePolicy<ExecutionSpace,Kokkos::Rank<2>>({0,0},{numPoints,numDims});
187 Kokkos::parallel_for("copy points", policy,
188 KOKKOS_LAMBDA (const int &i0, const int &i1) {
189 toPoints(i0,i1) = fromPoints(i0,i1);
190 });
191 }
192
194 template<typename OtherDeviceType, class = typename std::enable_if< std::is_same<typename DeviceType::memory_space, typename OtherDeviceType::memory_space>::value>::type,
195 class = typename std::enable_if<!std::is_same<DeviceType,OtherDeviceType>::value>::type>
196 TensorPoints(const TensorPoints<PointScalar,OtherDeviceType> &tensorPoints)
197 :
198 numTensorComponents_(tensorPoints.numTensorComponents()),
199 isValid_(tensorPoints.isValid())
200 {
201 if (isValid_)
202 {
203 for (ordinal_type r=0; r<numTensorComponents_; r++)
204 {
205 pointTensorComponents_[r] = tensorPoints.getTensorComponent(r);
206 }
207 initialize();
208 }
209 }
210
212 template<typename OtherDeviceType, class = typename std::enable_if<!std::is_same<typename DeviceType::memory_space, typename OtherDeviceType::memory_space>::value>::type>
214 :
215 numTensorComponents_(tensorPoints.numTensorComponents()),
216 isValid_(tensorPoints.isValid())
217 {
218 if (isValid_)
219 {
220 for (ordinal_type r=0; r<numTensorComponents_; r++)
221 {
222 ScalarView<PointScalar,OtherDeviceType> otherPointComponent = tensorPoints.getTensorComponent(r);
223 const int numPoints = otherPointComponent.extent_int(0);
224 const int numDims = otherPointComponent.extent_int(1);
225 pointTensorComponents_[r] = ScalarView<PointScalar,DeviceType>("Intrepid2 point component", numPoints, numDims);
226
227 using MemorySpace = typename DeviceType::memory_space;
228 auto pointComponentMirror = Kokkos::create_mirror_view_and_copy(MemorySpace(), otherPointComponent);
229
230 copyPointsContainer(pointTensorComponents_[r], pointComponentMirror);
231 }
232 initialize();
233 }
234 }
235
238 isValid_(false)
239 // when constructed with default arguments, TensorPoints should not be used…
240 // default constructor is only provided for things like CellGeometry, which has TensorPoints as a member,
241 // but only uses it in certain modes.
242 {}
243
245 ordinal_type componentPointCount(const ordinal_type &tensorComponentOrdinal) const
246 {
247 return pointTensorComponents_[tensorComponentOrdinal].extent_int(0);
248 }
249
258 template <typename iType0, typename iType1>
259 KOKKOS_INLINE_FUNCTION typename std::enable_if<
260 (std::is_integral<iType0>::value && std::is_integral<iType1>::value),
261 reference_type>::type
262 operator()(const iType0& tensorPointIndex, const iType1& dim) const {
263 const ordinal_type component = dimToComponent_[dim];
264 const ordinal_type d = dimToComponentDim_[dim];
265 const ordinal_type componentPointOrdinal = (tensorPointIndex / pointDivisor_[component]) % pointModulus_[component];
266 return pointTensorComponents_[component](componentPointOrdinal,d);
267 }
268
277 template <typename iType0, typename iType1, size_t numTensorComponents>
278 KOKKOS_INLINE_FUNCTION typename std::enable_if<
279 (std::is_integral<iType0>::value && std::is_integral<iType1>::value),
280 reference_type>::type
281 operator()(const Kokkos::Array<iType0,numTensorComponents>& pointOrdinalComponents, const iType1& dim) const {
282 const ordinal_type component = dimToComponent_[dim];
283 const ordinal_type d = dimToComponentDim_[dim];
284 const ordinal_type componentPointOrdinal = pointOrdinalComponents[component];
285 return pointTensorComponents_[component](componentPointOrdinal,d);
286 }
287
293 template <typename iType>
294 KOKKOS_INLINE_FUNCTION
295 typename std::enable_if<std::is_integral<iType>::value, int>::type
296 extent_int(const iType& r) const {
297 if (r == static_cast<iType>(0))
298 {
299 return totalPointCount_;
300 }
301 else if (r == static_cast<iType>(1))
302 {
303 return totalDimension_;
304 }
305 else
306 {
307 return 1;
308 }
309 }
310
316 template <typename iType>
317 KOKKOS_INLINE_FUNCTION constexpr
318 typename std::enable_if<std::is_integral<iType>::value, size_t>::type
319 extent(const iType& r) const {
320 // C++ prior to 14 doesn't allow constexpr if statements; the compound ternary is here to keep things constexpr
321 return (r == static_cast<iType>(0)) ? totalPointCount_
322 :
323 (r == static_cast<iType>(1)) ? totalDimension_ : 1;
324 }
325
327 ScalarView<PointScalar,DeviceType> allocateAndFillExpandedRawPointView() const
328 {
329 const int numPoints = this->extent_int(0);
330 const int spaceDim = this->extent_int(1);
331 ScalarView<PointScalar,DeviceType> expandedRawPoints("expanded raw points from TensorPoints", numPoints, spaceDim);
332 TensorPoints<PointScalar,DeviceType> tensorPoints(*this); // (shallow) copy for lambda capture
333 using ExecutionSpace = typename DeviceType::execution_space;
334 Kokkos::parallel_for(
335 Kokkos::MDRangePolicy<ExecutionSpace,Kokkos::Rank<2>>({0,0},{numPoints,spaceDim}),
336 KOKKOS_LAMBDA (const int &pointOrdinal, const int &d) {
337 expandedRawPoints(pointOrdinal,d) = tensorPoints(pointOrdinal,d);
338 });
339 return expandedRawPoints;
340 }
341
347 KOKKOS_INLINE_FUNCTION
348 ScalarView<PointScalar,DeviceType> getTensorComponent(const ordinal_type &r) const
349 {
350 return pointTensorComponents_[r];
351 }
352
354 KOKKOS_INLINE_FUNCTION
355 bool isValid() const
356 {
357 return isValid_;
358 }
359
361 KOKKOS_INLINE_FUNCTION
362 ordinal_type numTensorComponents() const
363 {
364 return numTensorComponents_;
365 }
366
368 KOKKOS_INLINE_FUNCTION
369 constexpr ordinal_type rank() const
370 {
371 return 2; // shape is (P,D)
372 }
373
374 // NOTE: the extractTensorPoints() code commented out below appears not to work. We don't have tests against it, though.
375 // TODO: either delete this, or re-enable, add tests, and fix.
376// template<class ViewType>
377// static TensorPoints<PointScalar,DeviceType> extractTensorPoints( ViewType expandedPoints, const std::vector<ordinal_type> &dimensionExtents )
378// {
379// const ordinal_type numComponents = dimensionExtents.size();
380// const ordinal_type numPoints = expandedPoints.extent_int(0);
381// Kokkos::Array<ordinal_type,Parameters::MaxTensorComponents> componentPointCounts;
382// Kokkos::Array<ordinal_type,Parameters::MaxTensorComponents> componentPointTensorStride;
383// Kokkos::Array<ordinal_type,Parameters::MaxTensorComponents> componentPointDimOffsets;
384//
385// // for simplicity of implementation, we copy to host:
386// auto hostExpandedPoints = Kokkos::create_mirror_view_and_copy(typename Kokkos::HostSpace::memory_space(), expandedPoints);
387//
388// ordinal_type dimOffset = 0;
389// ordinal_type tensorPointStride = 1; // increases with componentOrdinal
390//
391// TensorPoints<PointScalar,DeviceType> invalidTensorData; // will be returned if extraction does not succeed.
392//
393// for (ordinal_type componentOrdinal=0; componentOrdinal<numComponents; componentOrdinal++)
394// {
395// componentPointDimOffsets[componentOrdinal] = dimOffset;
396// componentPointTensorStride[componentOrdinal] = tensorPointStride;
397// const ordinal_type numDimsForComponent = dimensionExtents[componentOrdinal];
398// std::vector<PointScalar> firstPoint(numDimsForComponent);
399// for (ordinal_type d=0; d<numDimsForComponent; d++)
400// {
401// firstPoint[d] = hostExpandedPoints(0,d+dimOffset);
402// }
403//
404// // we assume that once we see the same point twice, we've found the cycle length:
405// componentPointCounts[componentOrdinal] = -1;
406// for (ordinal_type pointOrdinal=1; pointOrdinal<numPoints; pointOrdinal += tensorPointStride)
407// {
408// bool matches = true;
409// for (ordinal_type d=0; d<numDimsForComponent; d++)
410// {
411// matches = matches && (firstPoint[d] == hostExpandedPoints(pointOrdinal,d+dimOffset));
412// }
413// if (matches)
414// {
415// componentPointCounts[componentOrdinal] = pointOrdinal;
416// break;
417// }
418// }
419// if (componentPointCounts[componentOrdinal] == -1)
420// {
421// // no matches found -> no tensor decomposition available
422// return invalidTensorData;
423// }
424//
425// // check that we got the cycle length correct:
426// for (ordinal_type pointOrdinal=0; pointOrdinal<componentPointCounts[componentOrdinal]; pointOrdinal += tensorPointStride)
427// {
428// std::vector<PointScalar> point(numDimsForComponent);
429// for (ordinal_type d=0; d<numDimsForComponent; d++)
430// {
431// point[d] = hostExpandedPoints(pointOrdinal,d+dimOffset);
432// }
433// // each of the following points should match:
434// for (ordinal_type secondPointOrdinal=0; secondPointOrdinal<numPoints; secondPointOrdinal += tensorPointStride*componentPointCounts[componentOrdinal])
435// {
436// bool matches = true;
437// for (ordinal_type d=0; d<numDimsForComponent; d++)
438// {
439// matches = matches && (point[d] == hostExpandedPoints(secondPointOrdinal,d+dimOffset));
440// }
441// if (!matches)
442// {
443// // fail:
444// return invalidTensorData;
445// }
446// }
447// }
448//
449// dimOffset += numDimsForComponent;
450// tensorPointStride *= componentPointCounts[componentOrdinal];
451// }
452//
453// std::vector< ScalarView<PointScalar,DeviceType> > componentPoints(numComponents);
454// std::vector< ScalarView<PointScalar,Kokkos::HostSpace> > componentPointsHost(numComponents);
455// for (ordinal_type componentOrdinal=0; componentOrdinal<numComponents; componentOrdinal++)
456// {
457// const ordinal_type numPointsForComponent = componentPointCounts[componentOrdinal];
458// const ordinal_type dimForComponent = dimensionExtents[componentOrdinal];
459// componentPoints[componentOrdinal] = ScalarView<PointScalar,DeviceType>("extracted tensor components", numPointsForComponent, dimForComponent);
460// componentPointsHost[componentOrdinal] = Kokkos::create_mirror(componentPoints[componentOrdinal]);
461// }
462//
463// for (ordinal_type componentOrdinal=0; componentOrdinal<numComponents; componentOrdinal++)
464// {
465// const ordinal_type numComponentPoints = componentPointCounts[componentOrdinal];
466//
467// auto hostView = componentPointsHost[componentOrdinal];
468// auto deviceView = componentPoints[componentOrdinal];
469// const ordinal_type tensorPointStride = componentPointTensorStride[componentOrdinal];
470// const ordinal_type dimOffset = componentPointDimOffsets[componentOrdinal];
471// const ordinal_type numDimsForComponent = dimensionExtents[componentOrdinal];
472// for (ordinal_type componentPointOrdinal=0; componentPointOrdinal<numComponentPoints; componentPointOrdinal++)
473// {
474// const ordinal_type expandedPointOrdinal = componentPointOrdinal*tensorPointStride;
475// for (ordinal_type d=0; d<numDimsForComponent; d++)
476// {
477// hostView(componentPointOrdinal,d) = hostExpandedPoints(expandedPointOrdinal,d+dimOffset);
478// }
479// }
480// Kokkos::deep_copy(deviceView, hostView);
481// }
482//
483// // prior to return, check all points agree in all dimensions with the input points
484// // for convenience, we do this check on host, too
485// TensorPoints<PointScalar,Kokkos::HostSpace> hostTensorPoints(componentPointsHost);
486// const ordinal_type totalDim = expandedPoints.extent_int(1);
487// bool matches = true;
488// for (ordinal_type pointOrdinal=0; pointOrdinal<numPoints; pointOrdinal++)
489// {
490// for (ordinal_type d=0; d<totalDim; d++)
491// {
492// const auto &originalCoord = hostExpandedPoints(pointOrdinal,d);
493// const auto &tensorCoord = hostTensorPoints(pointOrdinal,d);
494// if (originalCoord != tensorCoord)
495// {
496// matches = false;
497// }
498// }
499// }
500// if (!matches)
501// {
502// return invalidTensorData;
503// }
504//
505// return TensorPoints<PointScalar,DeviceType>(componentPoints);
506// }
507 };
508
509}
510
511#endif /* Intrepid2_TensorPoints_h */
static constexpr ordinal_type MaxTensorComponents
Maximum number of tensor/Cartesian products that can be taken: this allows hypercube basis in 7D to b...
View-like interface to tensor points; point components are stored separately; the appropriate coordin...
KOKKOS_INLINE_FUNCTION constexpr ordinal_type rank() const
Return the rank of the container, which is 2.
KOKKOS_INLINE_FUNCTION ordinal_type numTensorComponents() const
Returns the number of tensorial components.
TensorPoints(const TensorPoints< PointScalar, OtherDeviceType > &tensorPoints)
copy-like constructor for differing memory spaces. This does a deep_copy of the underlying view.
KOKKOS_INLINE_FUNCTION ScalarView< PointScalar, DeviceType > getTensorComponent(const ordinal_type &r) const
Returns the requested tensor component.
KOKKOS_INLINE_FUNCTION bool isValid() const
Returns true for containers that have data; false for those that don't (e.g., those that have been co...
TensorPoints()
Default constructor. TensorPoints::isValid() will return false.
void initialize()
Initialize members based on constructor parameters.
TensorPoints(ScalarView< PointScalar, DeviceType > points)
Constructor for point set with trivial tensor structure.
KOKKOS_INLINE_FUNCTION std::enable_if< std::is_integral< iType >::value, int >::type extent_int(const iType &r) const
Returns the logical extent in the requested dimension.
TensorPoints(TensorPoints otherPointsContainer, std::vector< int > whichDims)
Constructor that takes a subset of the tensorial components of another points container.
void copyPointsContainer(ScalarView< PointScalar, DeviceType > toPoints, OtherPointsContainer fromPoints)
Copy from one points container, which may be an arbitrary functor, to a DynRankView.
TensorPoints(std::vector< ScalarView< PointScalar, DeviceType > > pointTensorComponents)
Constructor with variable-length std::vector argument.
TensorPoints(Kokkos::Array< ScalarView< PointScalar, DeviceType >, numTensorComponents > pointTensorComponents)
Constructor with fixed-length Kokkos::Array argument.
ScalarView< PointScalar, DeviceType > allocateAndFillExpandedRawPointView() const
This method is for compatibility with existing methods that take raw point views. Note that in genera...
ordinal_type componentPointCount(const ordinal_type &tensorComponentOrdinal) const
Returns the number of points in the indicated component.
KOKKOS_INLINE_FUNCTION constexpr std::enable_if< std::is_integral< iType >::value, size_t >::type extent(const iType &r) const
Returns the logical extent in the requested dimension.