Intrepid2
Intrepid2_LegendreBasis_HVOL_PYR.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 
21 #ifndef Intrepid2_LegendreBasis_HVOL_PYR_h
22 #define Intrepid2_LegendreBasis_HVOL_PYR_h
23 
24 #include <Kokkos_DynRankView.hpp>
25 
26 #include <Intrepid2_config.h>
27 
28 #include "Intrepid2_Basis.hpp"
31 #include "Intrepid2_Utils.hpp"
32 
33 #include "Teuchos_RCP.hpp"
34 
35 namespace Intrepid2
36 {
42  template<class DeviceType, class OutputScalar, class PointScalar,
43  class OutputFieldType, class InputPointsType>
45  {
46  using ExecutionSpace = typename DeviceType::execution_space;
47  using ScratchSpace = typename ExecutionSpace::scratch_memory_space;
48  using OutputScratchView = Kokkos::View<OutputScalar*,ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
49  using OutputScratchView2D = Kokkos::View<OutputScalar**,ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
50  using PointScratchView = Kokkos::View<PointScalar*, ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
51 
52  using TeamPolicy = Kokkos::TeamPolicy<ExecutionSpace>;
53  using TeamMember = typename TeamPolicy::member_type;
54 
55  EOperator opType_;
56 
57  OutputFieldType output_; // F,P
58  InputPointsType inputPoints_; // P,D
59 
60  int polyOrder_;
61  int numFields_, numPoints_;
62 
63  size_t fad_size_output_;
64 
65  static const int numVertices = 5;
66  static const int numMixedEdges = 4;
67  static const int numTriEdges = 4;
68  static const int numEdges = 8;
69  // the following ordering of the edges matches that used by ESEAS
70  // (it *looks* like this is what ESEAS uses; the basis comparison tests will clarify whether I've read their code correctly)
71  // see also PyramidEdgeNodeMap in Shards_BasicTopologies.hpp
72  const int edge_start_[numEdges] = {0,1,2,3,0,1,2,3}; // edge i is from edge_start_[i] to edge_end_[i]
73  const int edge_end_[numEdges] = {1,2,3,0,4,4,4,4}; // edge i is from edge_start_[i] to edge_end_[i]
74 
75  // quadrilateral face comes first
76  static const int numQuadFaces = 1;
77  static const int numTriFaces = 4;
78 
79  // face ordering matches ESEAS. (See BlendProjectPyraTF in ESEAS.)
80  const int tri_face_vertex_0[numTriFaces] = {0,1,3,0}; // faces are abc where 0 ≤ a < b < c ≤ 3
81  const int tri_face_vertex_1[numTriFaces] = {1,2,2,3};
82  const int tri_face_vertex_2[numTriFaces] = {4,4,4,4};
83 
84  Hierarchical_HVOL_PYR_Functor(EOperator opType, OutputFieldType output, InputPointsType inputPoints,
85  int polyOrder)
86  : opType_(opType), output_(output), inputPoints_(inputPoints),
87  polyOrder_(polyOrder),
88  fad_size_output_(getScalarDimensionForView(output))
89  {
90  numFields_ = output.extent_int(0);
91  numPoints_ = output.extent_int(1);
92  const auto & p = polyOrder;
93  const auto p_plus_one_cubed = (p+1) * (p+1) * (p+1);
94  INTREPID2_TEST_FOR_EXCEPTION(numPoints_ != inputPoints.extent_int(0), std::invalid_argument, "point counts need to match!");
95  INTREPID2_TEST_FOR_EXCEPTION(numFields_ != p_plus_one_cubed, std::invalid_argument, "output field size does not match basis cardinality");
96  }
97 
98  KOKKOS_INLINE_FUNCTION
99  void operator()( const TeamMember & teamMember ) const
100  {
101  auto pointOrdinal = teamMember.league_rank();
102  OutputScratchView scratch1D_1, scratch1D_2, scratch1D_3;
103  OutputScratchView scratch1D_4, scratch1D_5, scratch1D_6;
104  OutputScratchView scratch1D_7, scratch1D_8, scratch1D_9;
105  OutputScratchView2D scratch2D_1, scratch2D_2, scratch2D_3;
106  const int numAlphaValues = (polyOrder_-1 > 1) ? (polyOrder_-1) : 1; // make numAlphaValues at least 1 so we can avoid zero-extent allocations…
107  if (fad_size_output_ > 0) {
108  scratch1D_1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
109  scratch1D_2 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
110  scratch1D_3 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
111  scratch1D_4 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
112  scratch1D_5 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
113  scratch1D_6 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
114  scratch1D_7 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
115  scratch1D_8 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
116  scratch1D_9 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
117  scratch2D_1 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
118  scratch2D_2 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
119  scratch2D_3 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
120  }
121  else {
122  scratch1D_1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
123  scratch1D_2 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
124  scratch1D_3 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
125  scratch1D_4 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
126  scratch1D_5 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
127  scratch1D_6 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
128  scratch1D_7 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
129  scratch1D_8 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
130  scratch1D_9 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
131  scratch2D_1 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
132  scratch2D_2 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
133  scratch2D_3 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
134  }
135 
136  const auto & x = inputPoints_(pointOrdinal,0);
137  const auto & y = inputPoints_(pointOrdinal,1);
138  const auto & z = inputPoints_(pointOrdinal,2);
139 
140  // Intrepid2 uses (-1,1)^2 for x,y
141  // ESEAS uses (0,1)^2
142  // (Can look at what we do on the HGRAD_LINE for reference; there's a similar difference for line topology.)
143 
144  Kokkos::Array<PointScalar,3> coords;
145  transformToESEASPyramid<>(coords[0], coords[1], coords[2], x, y, z); // map x,y coordinates from (-z,z)^2 to (0,z)^2
146 
147  // pyramid "affine" coordinates and gradients get stored in lambda, lambdaGrad:
148  using Kokkos::Array;
149  Array<PointScalar,5> lambda;
150  Array<Kokkos::Array<PointScalar,3>,5> lambdaGrad;
151 
152  Array<Array<PointScalar,3>,2> mu; // first index is subscript; second is superscript: 0 --> (\zeta, xi_1), 1 --> (\zeta, xi_2), 2 --> (\zeta)
153  Array<Array<Array<PointScalar,3>,3>,2> muGrad;
154 
155  Array<Array<PointScalar,2>,3> nu;
156  Array<Array<Array<PointScalar,3>,2>,3> nuGrad;
157 
158  affinePyramid(lambda, lambdaGrad, mu, muGrad, nu, nuGrad, coords);
159 
160  switch (opType_)
161  {
162  case OPERATOR_VALUE:
163  {
164  // interior functions
165  // rename scratch
166  ordinal_type fieldOrdinalOffset = 0;
167  auto & Pi = scratch1D_1;
168  auto & Pj = scratch1D_2;
169  auto & Pk = scratch1D_3;
170  // [P_i](mu_01^{\zeta,\xi_1})
171  Polynomials::shiftedScaledLegendreValues(Pi, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
172  // [P_j](mu_01^{\zeta,\xi_2})
173  Polynomials::shiftedScaledLegendreValues(Pj, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
174  // [P_k](mu_01^{\zeta})
175  Polynomials::shiftedScaledLegendreValues(Pk, polyOrder_, mu[1][2], mu[0][2] + mu[1][2]);
176  // (grad nu_1^{\zeta,\xi_1} x grad nu_1^{\zeta,\xi_2}) \cdot grad mu_1^zeta:
177  PointScalar grad_weight =
178  (nuGrad[1][0][1] * nuGrad[1][1][2] - nuGrad[1][0][2] * nuGrad[1][1][1]) * muGrad[1][2][0]
179  + (nuGrad[1][0][2] * nuGrad[1][1][0] - nuGrad[1][0][0] * nuGrad[1][1][2]) * muGrad[1][2][1]
180  + (nuGrad[1][0][0] * nuGrad[1][1][1] - nuGrad[1][0][1] * nuGrad[1][1][0]) * muGrad[1][2][2];
181 
182  // following the ESEAS ordering: k increments first
183  for (int k=0; k<=polyOrder_; k++)
184  {
185  for (int j=0; j<=polyOrder_; j++)
186  {
187  for (int i=0; i<=polyOrder_; i++)
188  {
189  output_(fieldOrdinalOffset,pointOrdinal) = Pk(k) * Pi(i) * Pj(j) * grad_weight;
190  fieldOrdinalOffset++;
191  }
192  }
193  }
194  } // end OPERATOR_VALUE
195  break;
196  case OPERATOR_GRAD:
197  case OPERATOR_D1:
198  case OPERATOR_D2:
199  case OPERATOR_D3:
200  case OPERATOR_D4:
201  case OPERATOR_D5:
202  case OPERATOR_D6:
203  case OPERATOR_D7:
204  case OPERATOR_D8:
205  case OPERATOR_D9:
206  case OPERATOR_D10:
207  INTREPID2_TEST_FOR_ABORT( true,
208  ">>> ERROR: (Intrepid2::Hierarchical_HVOL_PYR_Functor) Computing of derivatives is not supported");
209  default:
210  // unsupported operator type
211  device_assert(false);
212  }
213  }
214 
215  // Provide the shared memory capacity.
216  // This function takes the team_size as an argument,
217  // which allows team_size-dependent allocations.
218  size_t team_shmem_size (int team_size) const
219  {
220  // we use shared memory to create a fast buffer for basis computations
221  // for the (integrated) Legendre computations, we just need p+1 values stored. For interior functions on the pyramid, we have up to 3 scratch arrays with (integrated) Legendre values stored, for each of the 3 directions (i,j,k indices): a total of 9.
222  // for the (integrated) Jacobi computations, though, we want (p+1)*(# alpha values)
223  // alpha is either 2i or 2(i+j), where i=2,…,p or i+j=3,…,p. So there are at most (p-1) alpha values needed.
224  // We can have up to 3 of the (integrated) Jacobi values needed at once.
225  const int numAlphaValues = std::max(polyOrder_-1, 1); // make it at least 1 so we can avoid zero-extent ranks…
226  size_t shmem_size = 0;
227  if (fad_size_output_ > 0)
228  {
229  // Legendre:
230  shmem_size += 9 * OutputScratchView::shmem_size(polyOrder_ + 1, fad_size_output_);
231  // Jacobi:
232  shmem_size += 3 * OutputScratchView2D::shmem_size(numAlphaValues, polyOrder_ + 1, fad_size_output_);
233  }
234  else
235  {
236  // Legendre:
237  shmem_size += 9 * OutputScratchView::shmem_size(polyOrder_ + 1);
238  // Jacobi:
239  shmem_size += 3 * OutputScratchView2D::shmem_size(numAlphaValues, polyOrder_ + 1);
240  }
241 
242  return shmem_size;
243  }
244  };
245 
257  template<typename DeviceType,
258  typename OutputScalar = double,
259  typename PointScalar = double>
261  : public Basis<DeviceType,OutputScalar,PointScalar>
262  {
263  public:
265 
266  using typename BasisBase::OrdinalTypeArray1DHost;
267  using typename BasisBase::OrdinalTypeArray2DHost;
268 
269  using typename BasisBase::OutputViewType;
270  using typename BasisBase::PointViewType;
271  using typename BasisBase::ScalarViewType;
272 
273  using typename BasisBase::ExecutionSpace;
274 
275  protected:
276  int polyOrder_; // the maximum order of the polynomial
277  EPointType pointType_;
278  public:
284  LegendreBasis_HVOL_PYR(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
285  :
286  polyOrder_(polyOrder),
287  pointType_(pointType)
288  {
289  INTREPID2_TEST_FOR_EXCEPTION(pointType!=POINTTYPE_DEFAULT,std::invalid_argument,"PointType not supported");
290  this->basisCardinality_ = (polyOrder + 1) * (polyOrder + 1) * (polyOrder + 1);
291  this->basisDegree_ = polyOrder;
292  this->basisCellTopologyKey_ = shards::Pyramid<>::key;
293  this->basisType_ = BASIS_FEM_HIERARCHICAL;
294  this->basisCoordinates_ = COORDINATES_CARTESIAN;
295  this->functionSpace_ = FUNCTION_SPACE_HVOL;
296 
297  const int degreeLength = 1;
298  this->fieldOrdinalPolynomialDegree_ = OrdinalTypeArray2DHost("Legendre H(vol) pyramid polynomial degree lookup", this->basisCardinality_, degreeLength);
299  this->fieldOrdinalH1PolynomialDegree_ = OrdinalTypeArray2DHost("Legendre H(vol) pyramid polynomial H^1 degree lookup", this->basisCardinality_, degreeLength);
300 
301  int fieldOrdinalOffset = 0;
302 
303  // **** interior functions **** //
304  const int numVolumes = 1; // interior
305  for (int volumeOrdinal=0; volumeOrdinal<numVolumes; volumeOrdinal++)
306  {
307  // following the ESEAS ordering: k increments first
308  for (int k=0; k<=polyOrder_; k++)
309  {
310  for (int j=0; j<=polyOrder_; j++)
311  {
312  for (int i=0; i<=polyOrder_; i++)
313  {
314  const int max_ij = std::max(i,j);
315  const int max_ijk = std::max(max_ij,k);
316  this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = max_ijk; // L^2 degree
317  this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = max_ijk + 1; // H^1 degree
318  fieldOrdinalOffset++;
319  }
320  }
321  }
322  }
323 
324  INTREPID2_TEST_FOR_EXCEPTION(fieldOrdinalOffset != this->basisCardinality_, std::invalid_argument, "Internal error: basis enumeration is incorrect");
325 
326  // initialize tags
327  {
328  const auto & cardinality = this->basisCardinality_;
329 
330  // Basis-dependent initializations
331  const ordinal_type tagSize = 4; // size of DoF tag, i.e., number of fields in the tag
332  const ordinal_type posScDim = 0; // position in the tag, counting from 0, of the subcell dim
333  const ordinal_type posScOrd = 1; // position in the tag, counting from 0, of the subcell ordinal
334  const ordinal_type posDfOrd = 2; // position in the tag, counting from 0, of DoF ordinal relative to the subcell
335 
336  OrdinalTypeArray1DHost tagView("tag view", cardinality*tagSize);
337  const ordinal_type volumeDim = 3;
338 
339  for (ordinal_type i=0;i<cardinality;++i) {
340  tagView(i*tagSize+0) = volumeDim; // volume dimension
341  tagView(i*tagSize+1) = 0; // volume ordinal
342  tagView(i*tagSize+2) = i; // local dof id
343  tagView(i*tagSize+3) = cardinality; // total number of dofs on this volume
344  }
345 
346  // Basis-independent function sets tag and enum data in tagToOrdinal_ and ordinalToTag_ arrays:
347  // tags are constructed on host
348  this->setOrdinalTagData(this->tagToOrdinal_,
349  this->ordinalToTag_,
350  tagView,
351  this->basisCardinality_,
352  tagSize,
353  posScDim,
354  posScOrd,
355  posDfOrd);
356  }
357  }
358 
363  const char* getName() const override {
364  return "Intrepid2_LegendreBasis_HVOL_PYR";
365  }
366 
369  virtual bool requireOrientation() const override {
370  return false;
371  }
372 
373  // since the getValues() below only overrides the FEM variant, we specify that
374  // we use the base class's getValues(), which implements the FVD variant by throwing an exception.
375  // (It's an error to use the FVD variant on this basis.)
376  using BasisBase::getValues;
377 
396  virtual void getValues( OutputViewType outputValues, const PointViewType inputPoints,
397  const EOperator operatorType = OPERATOR_VALUE ) const override
398  {
399  auto numPoints = inputPoints.extent_int(0);
400 
402 
403  FunctorType functor(operatorType, outputValues, inputPoints, polyOrder_);
404 
405  const int outputVectorSize = getVectorSizeForHierarchicalParallelism<OutputScalar>();
406  const int pointVectorSize = getVectorSizeForHierarchicalParallelism<PointScalar>();
407  const int vectorSize = std::max(outputVectorSize,pointVectorSize);
408  const int teamSize = 1; // because of the way the basis functions are computed, we don't have a second level of parallelism...
409 
410  auto policy = Kokkos::TeamPolicy<ExecutionSpace>(numPoints,teamSize,vectorSize);
411  Kokkos::parallel_for("Hierarchical_HVOL_PYR_Functor", policy, functor);
412  }
413 
422  BasisPtr<DeviceType,OutputScalar,PointScalar>
423  getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override{
424  // no subcell ref basis for HVOL
425  INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Input parameters out of bounds");
426  }
427 
432  virtual BasisPtr<typename Kokkos::HostSpace::device_type, OutputScalar, PointScalar>
433  getHostBasis() const override {
434  using HostDeviceType = typename Kokkos::HostSpace::device_type;
436  return Teuchos::rcp( new HostBasisType(polyOrder_, pointType_) );
437  }
438  };
439 } // end namespace Intrepid2
440 
441 // do ETI with default (double) type
443 
444 #endif /* Intrepid2_LegendreBasis_HVOL_PYR_h */
Functor for computing values for the LegendreBasis_HVOL_PYR class.
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
LegendreBasis_HVOL_PYR(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Constructor.
unsigned basisCellTopologyKey_
Identifier of the base topology of the cells for which the basis is defined. See the Shards package f...
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
Defines several coordinates and their gradients on the pyramid; maps from Intrepid2 (shards) pyramid ...
An abstract base class that defines interface for concrete basis implementations for Finite Element (...
Kokkos::DynRankView< scalarType, Kokkos::LayoutStride, DeviceType > ScalarViewType
View type for scalars.
Free functions, callable from device code, that implement various polynomials useful in basis definit...
EFunctionSpace functionSpace_
The function space in which the basis is defined.
ordinal_type basisDegree_
Degree of the largest complete polynomial space that can be represented by the basis.
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line...
virtual KOKKOS_INLINE_FUNCTION void getValues(OutputViewType, const PointViewType, const EOperator, const typename Kokkos::TeamPolicy< ExecutionSpace >::member_type &teamMember, const typename ExecutionSpace::scratch_memory_space &scratchStorage, const ordinal_type subcellDim=-1, const ordinal_type subcellOrdinal=-1) const
Team-level evaluation of basis functions on a reference cell.
Header function for Intrepid2::Util class and other utility functions.
virtual BasisPtr< typename Kokkos::HostSpace::device_type, OutputScalar, PointScalar > getHostBasis() const override
Creates and returns a Basis object whose DeviceType template argument is Kokkos::HostSpace::device_ty...
Kokkos::View< ordinal_type **, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray2DHost
View type for 2d host array.
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
const char * getName() const override
Returns basis name.
ordinal_type basisCardinality_
Cardinality of the basis, i.e., the number of basis functions/degrees-of-freedom. ...
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
OrdinalTypeArray3DHost tagToOrdinal_
DoF tag to ordinal lookup table.
virtual void getValues(OutputViewType outputValues, const PointViewType inputPoints, const EOperator operatorType=OPERATOR_VALUE) const override
Evaluation of a FEM basis on a reference cell.
OrdinalTypeArray2DHost ordinalToTag_
&quot;true&quot; if tagToOrdinal_ and ordinalToTag_ have been initialized
ECoordinates basisCoordinates_
The coordinate system for which the basis is defined.
OrdinalTypeArray2DHost fieldOrdinalH1PolynomialDegree_
H^1 polynomial degree for each degree of freedom. Only defined for hierarchical bases right now...
OrdinalTypeArray2DHost fieldOrdinalPolynomialDegree_
Polynomial degree for each degree of freedom. Only defined for hierarchical bases right now...
void setOrdinalTagData(OrdinalTypeView3D &tagToOrdinal, OrdinalTypeView2D &ordinalToTag, const OrdinalTypeView1D tags, const ordinal_type basisCard, const ordinal_type tagSize, const ordinal_type posScDim, const ordinal_type posScOrd, const ordinal_type posDfOrd)
Fills ordinalToTag_ and tagToOrdinal_ by basis-specific tag data.
BasisPtr< DeviceType, OutputScalar, PointScalar > getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override
returns the basis associated to a subCell.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
Header file for the abstract base class Intrepid2::Basis.
typename DeviceType::execution_space ExecutionSpace
(Kokkos) Execution space for basis.
virtual bool requireOrientation() const override
True if orientation is required.