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 
21 namespace Intrepid2 {
25  template<class PointScalar, typename DeviceType>
26  class TensorPoints {
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 
60  void initialize()
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 */
TensorPoints(ScalarView< PointScalar, DeviceType > points)
Constructor for point set with trivial tensor structure.
KOKKOS_INLINE_FUNCTION bool isValid() const
Returns true for containers that have data; false for those that don&#39;t (e.g., those that have been co...
View-like interface to tensor points; point components are stored separately; the appropriate coordin...
void initialize()
Initialize members based on constructor parameters.
TensorPoints(const TensorPoints< PointScalar, OtherDeviceType > &tensorPoints)
copy-like constructor for differing memory spaces. This does a deep_copy of the underlying view...
void copyPointsContainer(ScalarView< PointScalar, DeviceType > toPoints, OtherPointsContainer fromPoints)
Copy from one points container, which may be an arbitrary functor, to a DynRankView.
ScalarView< PointScalar, DeviceType > allocateAndFillExpandedRawPointView() const
This method is for compatibility with existing methods that take raw point views. Note that in genera...
KOKKOS_INLINE_FUNCTION constexpr ordinal_type rank() const
Return the rank of the container, which is 2.
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.
TensorPoints(TensorPoints otherPointsContainer, std::vector< int > whichDims)
Constructor that takes a subset of the tensorial components of another points container.
TensorPoints(Kokkos::Array< ScalarView< PointScalar, DeviceType >, numTensorComponents > pointTensorComponents)
Constructor with fixed-length Kokkos::Array argument.
KOKKOS_INLINE_FUNCTION ordinal_type numTensorComponents() const
Returns the number of tensorial components.
TensorPoints()
Default constructor. TensorPoints::isValid() will return false.
ordinal_type componentPointCount(const ordinal_type &tensorComponentOrdinal) const
Returns the number of points in the indicated component.
KOKKOS_INLINE_FUNCTION ScalarView< PointScalar, DeviceType > getTensorComponent(const ordinal_type &r) const
Returns the requested tensor component.
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(std::vector< ScalarView< PointScalar, DeviceType >> pointTensorComponents)
Constructor with variable-length std::vector argument.
static constexpr ordinal_type MaxTensorComponents
Maximum number of tensor/Cartesian products that can be taken: this allows hypercube basis in 7D to b...