Intrepid2
Intrepid2_IntegratedLegendreBasis_HGRAD_PYR.hpp
Go to the documentation of this file.
1 // @HEADER
2 // ************************************************************************
3 //
4 // Intrepid2 Package
5 // Copyright (2007) Sandia Corporation
6 //
7 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
8 // license for use of this work by or on behalf of the U.S. Government.
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are
12 // met:
13 //
14 // 1. Redistributions of source code must retain the above copyright
15 // notice, this list of conditions and the following disclaimer.
16 //
17 // 2. Redistributions in binary form must reproduce the above copyright
18 // notice, this list of conditions and the following disclaimer in the
19 // documentation and/or other materials provided with the distribution.
20 //
21 // 3. Neither the name of the Corporation nor the names of the
22 // contributors may be used to endorse or promote products derived from
23 // this software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
26 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
29 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 //
37 // Questions? Contact Mauro Perego (mperego@sandia.gov) or
38 // Nate Roberts (nvrober@sandia.gov)
39 //
40 // ************************************************************************
41 // @HEADER
42 
54 #ifndef Intrepid2_IntegratedLegendreBasis_HGRAD_PYR_h
55 #define Intrepid2_IntegratedLegendreBasis_HGRAD_PYR_h
56 
57 #include <Kokkos_DynRankView.hpp>
58 
59 #include <Intrepid2_config.h>
60 
61 #include "Intrepid2_Basis.hpp"
67 #include "Intrepid2_Utils.hpp"
68 
69 #include "Teuchos_RCP.hpp"
70 
71 namespace Intrepid2
72 {
78  template<class DeviceType, class OutputScalar, class PointScalar,
79  class OutputFieldType, class InputPointsType>
81  {
82  using ExecutionSpace = typename DeviceType::execution_space;
83  using ScratchSpace = typename ExecutionSpace::scratch_memory_space;
84  using OutputScratchView = Kokkos::View<OutputScalar*,ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
85  using OutputScratchView2D = Kokkos::View<OutputScalar**,ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
86  using PointScratchView = Kokkos::View<PointScalar*, ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
87 
88  using TeamPolicy = Kokkos::TeamPolicy<ExecutionSpace>;
89  using TeamMember = typename TeamPolicy::member_type;
90 
91  EOperator opType_;
92 
93  OutputFieldType output_; // F,P
94  InputPointsType inputPoints_; // P,D
95 
96  int polyOrder_;
97  bool defineVertexFunctions_;
98  int numFields_, numPoints_;
99 
100  size_t fad_size_output_;
101 
102  static const int numVertices = 5;
103  static const int numMixedEdges = 4;
104  static const int numTriEdges = 4;
105  static const int numEdges = 8;
106  // the following ordering of the edges matches that used by ESEAS
107  // (it *looks* like this is what ESEAS uses; the basis comparison tests will clarify whether I've read their code correctly)
108  // see also PyramidEdgeNodeMap in Shards_BasicTopologies.hpp
109  const int edge_start_[numEdges] = {0,1,2,3,0,1,2,3}; // edge i is from edge_start_[i] to edge_end_[i]
110  const int edge_end_[numEdges] = {1,2,3,0,4,4,4,4}; // edge i is from edge_start_[i] to edge_end_[i]
111 
112  // quadrilateral face comes first
113  static const int numQuadFaces = 1;
114  static const int numTriFaces = 4;
115 
116  // face ordering matches ESEAS. (See BlendProjectPyraTF in ESEAS.)
117  const int tri_face_vertex_0[numTriFaces] = {0,1,3,0}; // faces are abc where 0 ≤ a < b < c ≤ 3
118  const int tri_face_vertex_1[numTriFaces] = {1,2,2,3};
119  const int tri_face_vertex_2[numTriFaces] = {4,4,4,4};
120 
121  Hierarchical_HGRAD_PYR_Functor(EOperator opType, OutputFieldType output, InputPointsType inputPoints,
122  int polyOrder, bool defineVertexFunctions)
123  : opType_(opType), output_(output), inputPoints_(inputPoints),
124  polyOrder_(polyOrder), defineVertexFunctions_(defineVertexFunctions),
125  fad_size_output_(getScalarDimensionForView(output))
126  {
127  numFields_ = output.extent_int(0);
128  numPoints_ = output.extent_int(1);
129  const auto & p = polyOrder;
130  const auto p3 = p * p * p;
131  INTREPID2_TEST_FOR_EXCEPTION(numPoints_ != inputPoints.extent_int(0), std::invalid_argument, "point counts need to match!");
132  INTREPID2_TEST_FOR_EXCEPTION(numFields_ != p3 + 3 * p + 1, std::invalid_argument, "output field size does not match basis cardinality");
133  }
134 
135  KOKKOS_INLINE_FUNCTION
136  void operator()( const TeamMember & teamMember ) const
137  {
138  auto pointOrdinal = teamMember.league_rank();
139  OutputScratchView scratch1D_1, scratch1D_2, scratch1D_3;
140  OutputScratchView scratch1D_4, scratch1D_5, scratch1D_6;
141  OutputScratchView scratch1D_7, scratch1D_8, scratch1D_9;
142  OutputScratchView2D scratch2D_1, scratch2D_2, scratch2D_3;
143  const int numAlphaValues = (polyOrder_-1 > 1) ? (polyOrder_-1) : 1; // make numAlphaValues at least 1 so we can avoid zero-extent allocations…
144  if (fad_size_output_ > 0) {
145  scratch1D_1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
146  scratch1D_2 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
147  scratch1D_3 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
148  scratch1D_4 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
149  scratch1D_5 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
150  scratch1D_6 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
151  scratch1D_7 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
152  scratch1D_8 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
153  scratch1D_9 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
154  scratch2D_1 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
155  scratch2D_2 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
156  scratch2D_3 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
157  }
158  else {
159  scratch1D_1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
160  scratch1D_2 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
161  scratch1D_3 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
162  scratch1D_4 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
163  scratch1D_5 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
164  scratch1D_6 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
165  scratch1D_7 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
166  scratch1D_8 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
167  scratch1D_9 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
168  scratch2D_1 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
169  scratch2D_2 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
170  scratch2D_3 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
171  }
172 
173  const auto & x = inputPoints_(pointOrdinal,0);
174  const auto & y = inputPoints_(pointOrdinal,1);
175  const auto & z = inputPoints_(pointOrdinal,2);
176 
177  // Intrepid2 uses (-1,1)^2 for x,y
178  // ESEAS uses (0,1)^2
179  // (Can look at what we do on the HGRAD_LINE for reference; there's a similar difference for line topology.)
180 
181  Kokkos::Array<PointScalar,3> coords;
182  transformToESEASPyramid<>(coords[0], coords[1], coords[2], x, y, z); // map x,y coordinates from (-z,z)^2 to (0,z)^2
183 
184  // pyramid "affine" coordinates and gradients get stored in lambda, lambdaGrad:
185  using Kokkos::Array;
186  Array<PointScalar,5> lambda;
187  Array<Kokkos::Array<PointScalar,3>,5> lambdaGrad;
188 
189  Array<Array<PointScalar,3>,2> mu;
190  Array<Array<Array<PointScalar,3>,3>,2> muGrad;
191 
192  Array<Array<PointScalar,2>,3> nu;
193  Array<Array<Array<PointScalar,3>,2>,3> nuGrad;
194 
195  affinePyramid(lambda, lambdaGrad, mu, muGrad, nu, nuGrad, coords);
196 
197  switch (opType_)
198  {
199  case OPERATOR_VALUE:
200  {
201  // vertex functions come first, according to vertex ordering: (0,0,0), (1,0,0), (1,1,0), (0,1,0), (0,0,1)
202  for (int vertexOrdinal=0; vertexOrdinal<numVertices; vertexOrdinal++)
203  {
204  output_(vertexOrdinal,pointOrdinal) = lambda[vertexOrdinal];
205  }
206  if (!defineVertexFunctions_)
207  {
208  // "DG" basis case
209  // here, we overwrite the first vertex function with 1:
210  output_(0,pointOrdinal) = 1.0;
211  }
212 
213  // rename scratch1, scratch2
214  auto & Li_a1 = scratch1D_1;
215  auto & Li_a2 = scratch1D_2;
216 
217  Polynomials::shiftedScaledIntegratedLegendreValues(Li_a1, polyOrder_, nu[1][0], nu[0][0] + nu[1][0]);
218  Polynomials::shiftedScaledIntegratedLegendreValues(Li_a2, polyOrder_, nu[1][1], nu[0][1] + nu[1][1]);
219 
220  // edge functions
221  // "mixed" edges (those shared between the quad and some tri face) first
222  int fieldOrdinalOffset = numVertices;
223  for (int edgeOrdinal=0; edgeOrdinal<numMixedEdges; edgeOrdinal++)
224  {
225  // edge 0,2 --> a=1, b=2
226  // edge 1,3 --> a=2, b=1
227  int a = (edgeOrdinal % 2 == 0) ? 1 : 2;
228  int b = 3 - a;
229  auto & Li = (a == 1) ? Li_a1 : Li_a2;
230  // edge 0,3 --> c=0
231  // edge 1,2 --> c=1
232  int c = ((edgeOrdinal == 0) || (edgeOrdinal == 3)) ? 0 : 1;
233  for (int i=2; i<=polyOrder_; i++)
234  {
235  output_(fieldOrdinalOffset,pointOrdinal) = mu[c][b-1] * Li(i);
236  fieldOrdinalOffset++;
237  }
238  }
239 
240  // triangle edges next
241  // rename scratch1
242  auto & Li = scratch1D_1;
243  for (int edgeOrdinal=0; edgeOrdinal<numMixedEdges; edgeOrdinal++)
244  {
245  const auto & lambda_a = lambda[edgeOrdinal];
246  Polynomials::shiftedScaledIntegratedLegendreValues(Li, polyOrder_, lambda[4], lambda_a + lambda[4]);
247  for (int i=2; i<=polyOrder_; i++)
248  {
249  output_(fieldOrdinalOffset,pointOrdinal) = Li(i);
250  fieldOrdinalOffset++;
251  }
252  }
253 
254  // quadrilateral face
255  // mu_0 * phi_i(mu_0^{xi_1},mu_1^{xi_1}) * phi_j(mu_0^{xi_2},mu_1^{xi_2})
256 
257  // rename scratch
258  Li = scratch1D_1;
259  auto & Lj = scratch1D_2;
260  Polynomials::shiftedScaledIntegratedLegendreValues(Li, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
261  Polynomials::shiftedScaledIntegratedLegendreValues(Lj, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
262  // following the ESEAS ordering: j increments first
263  for (int j=2; j<=polyOrder_; j++)
264  {
265  for (int i=2; i<=polyOrder_; i++)
266  {
267  output_(fieldOrdinalOffset,pointOrdinal) = mu[0][2] * Li(i) * Lj(j);
268  fieldOrdinalOffset++;
269  }
270  }
271 
272  /*
273  Face functions for triangular face (d,e,f) are the product of:
274  mu_c^{zeta,xi_b},
275  edge functions on their (d,e) edge,
276  and a Jacobi polynomial [L^2i_j](s0+s1,s2) = L^2i_j(s2;s0+s1+s2),
277 
278  where s0, s1, s2 are nu[0][a-1],nu[1][a-1],nu[2][a-1],
279  and (a,b) = (1,2) for faces 0,2; (a,b) = (2,1) for others;
280  c = 0 for faces 0,3; c = 1 for others
281  */
282  for (int faceOrdinal=0; faceOrdinal<numTriFaces; faceOrdinal++)
283  {
284  // face 0,2 --> a=1, b=2
285  // face 1,3 --> a=2, b=1
286  int a = (faceOrdinal % 2 == 0) ? 1 : 2;
287  int b = 3 - a;
288  // face 0,3 --> c=0
289  // face 1,2 --> c=1
290  int c = ((faceOrdinal == 0) || (faceOrdinal == 3)) ? 0 : 1;
291 
292  const auto & s0 = nu[0][a-1];
293  const auto & s1 = nu[1][a-1];
294  const auto & s2 = nu[2][a-1];
295  const PointScalar jacobiScaling = s0 + s1 + s2;
296 
297  // compute integrated Jacobi values for each desired value of alpha
298  // relabel scratch2D_1
299  auto & jacobi = scratch2D_1;
300  for (int n=2; n<=polyOrder_; n++)
301  {
302  const double alpha = n*2;
303  const int alphaOrdinal = n-2;
304  using Kokkos::subview;
305  using Kokkos::ALL;
306  auto jacobi_alpha = subview(jacobi, alphaOrdinal, ALL);
307  Polynomials::shiftedScaledIntegratedJacobiValues(jacobi_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
308  }
309  // relabel scratch1D_1
310  auto & edge_s0s1 = scratch1D_1;
311  Polynomials::shiftedScaledIntegratedLegendreValues(edge_s0s1, polyOrder_, s1, s0 + s1);
312 
313  for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
314  {
315  for (int i=2; i<totalPolyOrder; i++)
316  {
317  const auto & edgeValue = edge_s0s1(i);
318  const int alphaOrdinal = i-2;
319 
320  const int j = totalPolyOrder - i;
321  const auto & jacobiValue = jacobi(alphaOrdinal,j);
322  output_(fieldOrdinalOffset,pointOrdinal) = mu[c][b-1] * edgeValue * jacobiValue;
323 
324  fieldOrdinalOffset++;
325  }
326  }
327  }
328 
329  // interior functions
330  // these are the product of the same quadrilateral function that we blended for the quadrilateral face and
331  // [L_k](mu_{0}^zeta, mu_1^zeta)
332 
333  // rename scratch
334  Li = scratch1D_1;
335  Lj = scratch1D_2;
336  auto & Lk = scratch1D_3;
337  Polynomials::shiftedScaledIntegratedLegendreValues(Li, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
338  Polynomials::shiftedScaledIntegratedLegendreValues(Lj, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
339  Polynomials::shiftedScaledIntegratedLegendreValues(Lk, polyOrder_, mu[1][2], mu[0][2] + mu[1][2]);
340  // following the ESEAS ordering: k increments first
341  for (int k=2; k<=polyOrder_; k++)
342  {
343  for (int j=2; j<=polyOrder_; j++)
344  {
345  for (int i=2; i<=polyOrder_; i++)
346  {
347  output_(fieldOrdinalOffset,pointOrdinal) = Lk(k) * Li(i) * Lj(j);
348  fieldOrdinalOffset++;
349  }
350  }
351  }
352  } // end OPERATOR_VALUE
353  break;
354  case OPERATOR_GRAD:
355  case OPERATOR_D1:
356  {
357  // vertex functions
358 
359  for (int vertexOrdinal=0; vertexOrdinal<numVertices; vertexOrdinal++)
360  {
361  for (int d=0; d<3; d++)
362  {
363  output_(vertexOrdinal,pointOrdinal,d) = lambdaGrad[vertexOrdinal][d];
364  }
365  }
366 
367  if (!defineVertexFunctions_)
368  {
369  // "DG" basis case
370  // here, the first "vertex" function is 1, so the derivative is 0:
371  output_(0,pointOrdinal,0) = 0.0;
372  output_(0,pointOrdinal,1) = 0.0;
373  output_(0,pointOrdinal,2) = 0.0;
374  }
375 
376  // edge functions
377  int fieldOrdinalOffset = numVertices;
378 
379  // mixed edges first
380  auto & P_i_minus_1 = scratch1D_1;
381  auto & L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
382  auto & L_i = scratch1D_3;
383 
384  for (int edgeOrdinal=0; edgeOrdinal<numMixedEdges; edgeOrdinal++)
385  {
386  // edge 0,2 --> a=1, b=2
387  // edge 1,3 --> a=2, b=1
388  int a = (edgeOrdinal % 2 == 0) ? 1 : 2;
389  int b = 3 - a;
390 
391  Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, nu[1][a-1], nu[0][a-1] + nu[1][a-1]);
392  Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, nu[1][a-1], nu[0][a-1] + nu[1][a-1]);
393  Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, nu[1][a-1], nu[0][a-1] + nu[1][a-1]);
394 
395  // edge 0,3 --> c=0
396  // edge 1,2 --> c=1
397  int c = ((edgeOrdinal == 0) || (edgeOrdinal == 3)) ? 0 : 1;
398  for (int i=2; i<=polyOrder_; i++)
399  {
400  // grad (mu[c][b-1] * Li(i)) = grad (mu[c][b-1]) * Li(i) + mu[c][b-1] * grad Li(i)
401  const auto & R_i_minus_1 = L_i_dt(i);
402 
403  for (int d=0; d<3; d++)
404  {
405  // grad [L_i](nu_0,nu_1) = [P_{i-1}](nu_0,nu_1) * grad nu_1 + [R_{i-1}](nu_0,nu_1) * grad (nu_0 + nu_1)
406 
407  OutputScalar grad_Li_d = P_i_minus_1(i-1) * nuGrad[1][a-1][d] + R_i_minus_1 * (nuGrad[0][a-1][d] + nuGrad[1][a-1][d]);
408  output_(fieldOrdinalOffset,pointOrdinal,d) = muGrad[c][b-1][d] * L_i(i) + mu[c][b-1] * grad_Li_d;
409  }
410  fieldOrdinalOffset++;
411  }
412  }
413 
414  // triangle edges next
415  P_i_minus_1 = scratch1D_1;
416  L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
417  L_i = scratch1D_3;
418  for (int edgeOrdinal=0; edgeOrdinal<numMixedEdges; edgeOrdinal++)
419  {
420  const auto & lambda_a = lambda [edgeOrdinal];
421  const auto & lambdaGrad_a = lambdaGrad[edgeOrdinal];
422  Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, lambda[4], lambda_a + lambda[4]);
423  Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, lambda[4], lambda_a + lambda[4]);
424  Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, lambda[4], lambda_a + lambda[4]);
425 
426  for (int i=2; i<=polyOrder_; i++)
427  {
428  const auto & R_i_minus_1 = L_i_dt(i);
429  for (int d=0; d<3; d++)
430  {
431  // grad [L_i](s0,s1) = [P_{i-1}](s0,s1) * grad s1 + [R_{i-1}](s0,s1) * grad (s0 + s1)
432  // here, s1 = lambda[4], s0 = lambda_a
433  OutputScalar grad_Li_d = P_i_minus_1(i-1) * lambdaGrad[4][d] + R_i_minus_1 * (lambdaGrad_a[d] + lambdaGrad[4][d]);
434  output_(fieldOrdinalOffset,pointOrdinal,d) = grad_Li_d;
435  }
436  fieldOrdinalOffset++;
437  }
438  }
439 
440  // quadrilateral faces
441  // rename scratch
442  P_i_minus_1 = scratch1D_1;
443  L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
444  L_i = scratch1D_3;
445  auto & P_j_minus_1 = scratch1D_4;
446  auto & L_j_dt = scratch1D_5; // R_{j-1} = d/dt L_j
447  auto & L_j = scratch1D_6;
448  Polynomials::shiftedScaledIntegratedLegendreValues(L_i, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
449  Polynomials::shiftedScaledIntegratedLegendreValues(L_j, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
450 
451  Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, mu[1][0], mu[0][0] + mu[1][0]);
452  Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
453  Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
454 
455  Polynomials::shiftedScaledLegendreValues (P_j_minus_1, polyOrder_-1, mu[1][1], mu[0][1] + mu[1][1]);
456  Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_j_dt, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
457  Polynomials::shiftedScaledIntegratedLegendreValues (L_j, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
458 
459  // following the ESEAS ordering: j increments first
460  for (int j=2; j<=polyOrder_; j++)
461  {
462  const auto & R_j_minus_1 = L_j_dt(j);
463 
464  for (int i=2; i<=polyOrder_; i++)
465  {
466  const auto & R_i_minus_1 = L_i_dt(i);
467 
468  OutputScalar phi_quad = L_i(i) * L_j(j);
469 
470  for (int d=0; d<3; d++)
471  {
472  // grad [L_j](s0,s1) = [P_{j-1}](s0,s1) * grad s1 + [R_{j-1}](s0,s1) * grad (s0 + s1)
473  // here, s1 = mu[1][1], s0 = mu[0][1]
474  OutputScalar grad_Lj_d = P_j_minus_1(j-1) * muGrad[1][1][d] + R_j_minus_1 * (muGrad[0][1][d] + muGrad[1][1][d]);
475  // for L_i, s1 = mu[1][0], s0 = mu[0][0]
476  OutputScalar grad_Li_d = P_i_minus_1(i-1) * muGrad[1][0][d] + R_i_minus_1 * (muGrad[0][0][d] + muGrad[1][0][d]);
477 
478  OutputScalar grad_phi_quad_d = L_i(i) * grad_Lj_d + L_j(j) * grad_Li_d;
479 
480  output_(fieldOrdinalOffset,pointOrdinal,d) = mu[0][2] * grad_phi_quad_d + phi_quad * muGrad[0][2][d];
481  }
482 
483  fieldOrdinalOffset++;
484  }
485  }
486 
487  // triangle faces
488  for (int faceOrdinal=0; faceOrdinal<numTriFaces; faceOrdinal++)
489  {
490  // face 0,2 --> a=1, b=2
491  // face 1,3 --> a=2, b=1
492  int a = (faceOrdinal % 2 == 0) ? 1 : 2;
493  int b = 3 - a;
494  // face 0,3 --> c=0
495  // face 1,2 --> c=1
496  int c = ((faceOrdinal == 0) || (faceOrdinal == 3)) ? 0 : 1;
497 
498  const auto & s0 = nu[0][a-1];
499  const auto & s1 = nu[1][a-1];
500  const auto & s2 = nu[2][a-1];
501 
502  const auto & s0Grad = nuGrad[0][a-1];
503  const auto & s1Grad = nuGrad[1][a-1];
504  const auto & s2Grad = nuGrad[2][a-1];
505 
506  const PointScalar jacobiScaling = s0 + s1 + s2;
507 
508  // compute integrated Jacobi values for each desired value of alpha
509  // relabel storage:
510  // 1D containers:
511  P_i_minus_1 = scratch1D_1;
512  L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
513  L_i = scratch1D_3;
514  // 2D containers:
515  auto & L_2i_j_dt = scratch2D_1;
516  auto & L_2i_j = scratch2D_2;
517  auto & P_2i_j_minus_1 = scratch2D_3;
518  for (int n=2; n<=polyOrder_; n++)
519  {
520  const double alpha = n*2;
521  const int alphaOrdinal = n-2;
522  using Kokkos::subview;
523  using Kokkos::ALL;
524  auto L_2i_j_dt_alpha = subview(L_2i_j_dt, alphaOrdinal, ALL);
525  auto L_2i_j_alpha = subview(L_2i_j, alphaOrdinal, ALL);
526  auto P_2i_j_minus_1_alpha = subview(P_2i_j_minus_1, alphaOrdinal, ALL);
527  Polynomials::shiftedScaledIntegratedJacobiValues_dt(L_2i_j_dt_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
528  Polynomials::shiftedScaledIntegratedJacobiValues ( L_2i_j_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
529  Polynomials::shiftedScaledJacobiValues (P_2i_j_minus_1_alpha, alpha, polyOrder_-1, s2, jacobiScaling);
530  }
531  Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, s1, s0 + s1);
532  Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, s1, s0 + s1);
533  Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, s1, s0 + s1);
534 
535  for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
536  {
537  for (int i=2; i<totalPolyOrder; i++)
538  {
539  const int alphaOrdinal = i-2;
540  const int j = totalPolyOrder - i;
541 
542  const auto & R_i_minus_1 = L_i_dt(i);
543  OutputScalar phi_tri = L_2i_j(alphaOrdinal,j) * L_i(i);
544 
545  for (int d=0; d<3; d++)
546  {
547  // grad [L_i](s0,s1) = [P_{i-1}](s0,s1) * grad s1 + [R_{i-1}](s0,s1) * grad (s0 + s1)
548  OutputScalar grad_Li_d = P_i_minus_1(i-1) * s1Grad[d] + R_i_minus_1 * (s0Grad[d] + s1Grad[d]);
549  OutputScalar grad_L2i_j_d = P_2i_j_minus_1(alphaOrdinal,j-1) * s2Grad[d] + L_2i_j_dt(alphaOrdinal,j) * (s0Grad[d] + s1Grad[d] + s2Grad[d]);
550  OutputScalar grad_phi_tri_d = L_i(i) * grad_L2i_j_d + L_2i_j(alphaOrdinal,j) * grad_Li_d;
551 
552  output_(fieldOrdinalOffset,pointOrdinal,d) = mu[c][b-1] * grad_phi_tri_d + phi_tri * muGrad[c][b-1][d];
553  }
554  fieldOrdinalOffset++;
555  }
556  }
557  }
558 
559  // interior functions
560  P_i_minus_1 = scratch1D_1;
561  L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
562  L_i = scratch1D_3;
563  P_j_minus_1 = scratch1D_4;
564  L_j_dt = scratch1D_5; // R_{j-1} = d/dt L_j
565  L_j = scratch1D_6;
566  auto & P_k_minus_1 = scratch1D_7;
567  auto & L_k_dt = scratch1D_8; // R_{k-1} = d/dt L_k
568  auto & L_k = scratch1D_9;
569 
570  Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, mu[1][0], mu[0][0] + mu[1][0]);
571  Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
572  Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
573 
574  Polynomials::shiftedScaledLegendreValues (P_j_minus_1, polyOrder_-1, mu[1][1], mu[0][1] + mu[1][1]);
575  Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_j_dt, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
576  Polynomials::shiftedScaledIntegratedLegendreValues (L_j, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
577 
578  Polynomials::shiftedScaledLegendreValues (P_k_minus_1, polyOrder_-1, mu[1][2], mu[0][2] + mu[1][2]);
579  Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_k_dt, polyOrder_, mu[1][2], mu[0][2] + mu[1][2]);
580  Polynomials::shiftedScaledIntegratedLegendreValues (L_k, polyOrder_, mu[1][2], mu[0][2] + mu[1][2]);
581 
582  // following the ESEAS ordering: k increments first
583  for (int k=2; k<=polyOrder_; k++)
584  {
585  const auto & R_k_minus_1 = L_k_dt(k);
586 
587  for (int j=2; j<=polyOrder_; j++)
588  {
589  const auto & R_j_minus_1 = L_j_dt(j);
590 
591  for (int i=2; i<=polyOrder_; i++)
592  {
593  const auto & R_i_minus_1 = L_i_dt(i);
594 
595  OutputScalar phi_quad = L_i(i) * L_j(j);
596 
597  for (int d=0; d<3; d++)
598  {
599  // grad [L_i](s0,s1) = [P_{i-1}](s0,s1) * grad s1 + [R_{i-1}](s0,s1) * grad (s0 + s1)
600  // for L_i, s1 = mu[1][0], s0 = mu[0][0]
601  OutputScalar grad_Li_d = P_i_minus_1(i-1) * muGrad[1][0][d] + R_i_minus_1 * (muGrad[0][0][d] + muGrad[1][0][d]);
602  // for L_j, s1 = mu[1][1], s0 = mu[0][1]
603  OutputScalar grad_Lj_d = P_j_minus_1(j-1) * muGrad[1][1][d] + R_j_minus_1 * (muGrad[0][1][d] + muGrad[1][1][d]);
604  // for L_k, s1 = mu[1][2], s0 = mu[0][2]
605  OutputScalar grad_Lk_d = P_k_minus_1(k-1) * muGrad[1][2][d] + R_k_minus_1 * (muGrad[0][2][d] + muGrad[1][2][d]);
606 
607  OutputScalar grad_phi_quad_d = L_i(i) * grad_Lj_d + L_j(j) * grad_Li_d;
608 
609  output_(fieldOrdinalOffset,pointOrdinal,d) = L_k(k) * grad_phi_quad_d + phi_quad * grad_Lk_d;
610  }
611 
612  fieldOrdinalOffset++;
613  }
614  }
615  }
616 
617  for (int basisOrdinal=0; basisOrdinal<numFields_; basisOrdinal++)
618  {
619  // transform derivatives to account for the ref space transformation: Intrepid2 uses (-z,z)^2; ESEAS uses (0,z)^2
620  const auto dx_eseas = output_(basisOrdinal,pointOrdinal,0);
621  const auto dy_eseas = output_(basisOrdinal,pointOrdinal,1);
622  const auto dz_eseas = output_(basisOrdinal,pointOrdinal,2);
623 
624  auto &dx_int2 = output_(basisOrdinal,pointOrdinal,0);
625  auto &dy_int2 = output_(basisOrdinal,pointOrdinal,1);
626  auto &dz_int2 = output_(basisOrdinal,pointOrdinal,2);
627 
628  transformFromESEASPyramidGradient(dx_int2, dy_int2, dz_int2, dx_eseas, dy_eseas, dz_eseas);
629  }
630 
631  } // end OPERATOR_GRAD block
632  break;
633  case OPERATOR_D2:
634  case OPERATOR_D3:
635  case OPERATOR_D4:
636  case OPERATOR_D5:
637  case OPERATOR_D6:
638  case OPERATOR_D7:
639  case OPERATOR_D8:
640  case OPERATOR_D9:
641  case OPERATOR_D10:
642  INTREPID2_TEST_FOR_ABORT( true,
643  ">>> ERROR: (Intrepid2::Hierarchical_HGRAD_PYR_Functor) Computing of second and higher-order derivatives is not currently supported");
644  default:
645  // unsupported operator type
646  device_assert(false);
647  }
648  }
649 
650  // Provide the shared memory capacity.
651  // This function takes the team_size as an argument,
652  // which allows team_size-dependent allocations.
653  size_t team_shmem_size (int team_size) const
654  {
655  // we use shared memory to create a fast buffer for basis computations
656  // 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.
657  // for the (integrated) Jacobi computations, though, we want (p+1)*(# alpha values)
658  // 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.
659  // We can have up to 3 of the (integrated) Jacobi values needed at once.
660  const int numAlphaValues = std::max(polyOrder_-1, 1); // make it at least 1 so we can avoid zero-extent ranks…
661  size_t shmem_size = 0;
662  if (fad_size_output_ > 0)
663  {
664  // Legendre:
665  shmem_size += 9 * OutputScratchView::shmem_size(polyOrder_ + 1, fad_size_output_);
666  // Jacobi:
667  shmem_size += 3 * OutputScratchView2D::shmem_size(numAlphaValues, polyOrder_ + 1, fad_size_output_);
668  }
669  else
670  {
671  // Legendre:
672  shmem_size += 9 * OutputScratchView::shmem_size(polyOrder_ + 1);
673  // Jacobi:
674  shmem_size += 3 * OutputScratchView2D::shmem_size(numAlphaValues, polyOrder_ + 1);
675  }
676 
677  return shmem_size;
678  }
679  };
680 
698  template<typename DeviceType,
699  typename OutputScalar = double,
700  typename PointScalar = double,
701  bool defineVertexFunctions = true> // if defineVertexFunctions is true, first four basis functions are 1-x-y-z, x, y, and z. Otherwise, they are 1, x, y, and z.
703  : public Basis<DeviceType,OutputScalar,PointScalar>
704  {
705  public:
707 
708  using typename BasisBase::OrdinalTypeArray1DHost;
709  using typename BasisBase::OrdinalTypeArray2DHost;
710 
711  using typename BasisBase::OutputViewType;
712  using typename BasisBase::PointViewType;
713  using typename BasisBase::ScalarViewType;
714 
715  using typename BasisBase::ExecutionSpace;
716 
717  protected:
718  int polyOrder_; // the maximum order of the polynomial
719  EPointType pointType_;
720  public:
731  IntegratedLegendreBasis_HGRAD_PYR(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
732  :
733  polyOrder_(polyOrder),
734  pointType_(pointType)
735  {
736  INTREPID2_TEST_FOR_EXCEPTION(pointType!=POINTTYPE_DEFAULT,std::invalid_argument,"PointType not supported");
737  this->basisCardinality_ = polyOrder * polyOrder * polyOrder + 3 * polyOrder + 1;
738  this->basisDegree_ = polyOrder;
739  this->basisCellTopology_ = shards::CellTopology(shards::getCellTopologyData<shards::Pyramid<> >() );
740  this->basisType_ = BASIS_FEM_HIERARCHICAL;
741  this->basisCoordinates_ = COORDINATES_CARTESIAN;
742  this->functionSpace_ = FUNCTION_SPACE_HGRAD;
743 
744  const int degreeLength = 1;
745  this->fieldOrdinalPolynomialDegree_ = OrdinalTypeArray2DHost("Integrated Legendre H(grad) pyramid polynomial degree lookup", this->basisCardinality_, degreeLength);
746  this->fieldOrdinalH1PolynomialDegree_ = OrdinalTypeArray2DHost("Integrated Legendre H(grad) pyramid polynomial H^1 degree lookup", this->basisCardinality_, degreeLength);
747 
748  int fieldOrdinalOffset = 0;
749  // **** vertex functions **** //
750  const int numVertices = this->basisCellTopology_.getVertexCount();
751  for (int i=0; i<numVertices; i++)
752  {
753  // for H(grad) on Pyramid, if defineVertexFunctions is false, first five basis members are linear
754  // if not, then the only difference is that the first member is constant
755  this->fieldOrdinalPolynomialDegree_ (i,0) = 1;
756  this->fieldOrdinalH1PolynomialDegree_(i,0) = 1;
757  }
758  if (!defineVertexFunctions)
759  {
760  this->fieldOrdinalPolynomialDegree_ (0,0) = 0;
761  this->fieldOrdinalH1PolynomialDegree_(0,0) = 0;
762  }
763  fieldOrdinalOffset += numVertices;
764 
765  // **** edge functions **** //
766  const int numFunctionsPerEdge = polyOrder - 1; // bubble functions: all but the vertices
767  const int numEdges = this->basisCellTopology_.getEdgeCount();
768  for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
769  {
770  for (int i=0; i<numFunctionsPerEdge; i++)
771  {
772  this->fieldOrdinalPolynomialDegree_(i+fieldOrdinalOffset,0) = i+2; // vertex functions are 1st order; edge functions start at order 2
773  this->fieldOrdinalH1PolynomialDegree_(i+fieldOrdinalOffset,0) = i+2; // vertex functions are 1st order; edge functions start at order 2
774  }
775  fieldOrdinalOffset += numFunctionsPerEdge;
776  }
777 
778  // **** face functions **** //
779  // one quad face
780  const int numFunctionsPerQuadFace = (polyOrder-1)*(polyOrder-1);
781 
782  // following the ESEAS ordering: j increments first
783  for (int j=2; j<=polyOrder_; j++)
784  {
785  for (int i=2; i<=polyOrder_; i++)
786  {
787  this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = std::max(i,j);
788  this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = std::max(i,j);
789  fieldOrdinalOffset++;
790  }
791  }
792 
793  const int numFunctionsPerTriFace = ((polyOrder-1)*(polyOrder-2))/2;
794  const int numTriFaces = 4;
795  for (int faceOrdinal=0; faceOrdinal<numTriFaces; faceOrdinal++)
796  {
797  for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
798  {
799  const int totalFaceDofs = (totalPolyOrder-2)*(totalPolyOrder-1)/2;
800  const int totalFaceDofsPrevious = (totalPolyOrder-3)*(totalPolyOrder-2)/2;
801  const int faceDofsForPolyOrder = totalFaceDofs - totalFaceDofsPrevious;
802  for (int i=0; i<faceDofsForPolyOrder; i++)
803  {
804  this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = totalPolyOrder;
805  this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = totalPolyOrder;
806  fieldOrdinalOffset++;
807  }
808  }
809  }
810 
811  // **** interior functions **** //
812  const int numFunctionsPerVolume = (polyOrder-1)*(polyOrder-1)*(polyOrder-1);
813  const int numVolumes = 1; // interior
814  for (int volumeOrdinal=0; volumeOrdinal<numVolumes; volumeOrdinal++)
815  {
816  // following the ESEAS ordering: k increments first
817  for (int k=2; k<=polyOrder_; k++)
818  {
819  for (int j=2; j<=polyOrder_; j++)
820  {
821  for (int i=2; i<=polyOrder_; i++)
822  {
823  const int max_ij = std::max(i,j);
824  const int max_ijk = std::max(max_ij,k);
825  this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = max_ijk;
826  this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = max_ijk;
827  fieldOrdinalOffset++;
828  }
829  }
830  }
831  }
832 
833  INTREPID2_TEST_FOR_EXCEPTION(fieldOrdinalOffset != this->basisCardinality_, std::invalid_argument, "Internal error: basis enumeration is incorrect");
834 
835  // initialize tags
836  {
837  // Intrepid2 vertices:
838  // 0: (-1,-1, 0)
839  // 1: ( 1,-1, 0)
840  // 2: ( 1, 1, 0)
841  // 3: (-1, 1, 0)
842  // 4: ( 0, 0, 1)
843 
844  // ESEAS vertices:
845  // 0: ( 0, 0, 0)
846  // 1: ( 1, 0, 0)
847  // 2: ( 1, 1, 0)
848  // 3: ( 0, 1, 0)
849  // 4: ( 0, 0, 1)
850 
851  // The edge numbering appears to match between ESEAS and Intrepid2
852 
853  // ESEAS numbers pyramid faces differently from Intrepid2
854  // See BlendProjectPyraTF in ESEAS.
855  // See PyramidFaceNodeMap in Shards_BasicTopologies
856  // ESEAS: 0123, 014, 124, 324, 034
857  // Intrepid2: 014, 124, 234, 304, 0321
858 
859  const int intrepid2FaceOrdinals[5] {4,0,1,2,3}; // index is the ESEAS face ordinal; value is the intrepid2 ordinal
860 
861  const auto & cardinality = this->basisCardinality_;
862 
863  // Basis-dependent initializations
864  const ordinal_type tagSize = 4; // size of DoF tag, i.e., number of fields in the tag
865  const ordinal_type posScDim = 0; // position in the tag, counting from 0, of the subcell dim
866  const ordinal_type posScOrd = 1; // position in the tag, counting from 0, of the subcell ordinal
867  const ordinal_type posDfOrd = 2; // position in the tag, counting from 0, of DoF ordinal relative to the subcell
868 
869  OrdinalTypeArray1DHost tagView("tag view", cardinality*tagSize);
870  const int vertexDim = 0, edgeDim = 1, faceDim = 2, volumeDim = 3;
871 
872  if (defineVertexFunctions) {
873  {
874  int tagNumber = 0;
875  for (int vertexOrdinal=0; vertexOrdinal<numVertices; vertexOrdinal++)
876  {
877  tagView(tagNumber*tagSize+0) = vertexDim; // vertex dimension
878  tagView(tagNumber*tagSize+1) = vertexOrdinal; // vertex id
879  tagView(tagNumber*tagSize+2) = 0; // local dof id
880  tagView(tagNumber*tagSize+3) = 1; // total number of dofs at this vertex
881  tagNumber++;
882  }
883  for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
884  {
885  for (int functionOrdinal=0; functionOrdinal<numFunctionsPerEdge; functionOrdinal++)
886  {
887  tagView(tagNumber*tagSize+0) = edgeDim; // edge dimension
888  tagView(tagNumber*tagSize+1) = edgeOrdinal; // edge id
889  tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
890  tagView(tagNumber*tagSize+3) = numFunctionsPerEdge; // total number of dofs on this edge
891  tagNumber++;
892  }
893  }
894  {
895  // quad face
896  const int faceOrdinalESEAS = 0;
897  const int faceOrdinalIntrepid2 = intrepid2FaceOrdinals[faceOrdinalESEAS];
898 
899  for (int functionOrdinal=0; functionOrdinal<numFunctionsPerQuadFace; functionOrdinal++)
900  {
901  tagView(tagNumber*tagSize+0) = faceDim; // face dimension
902  tagView(tagNumber*tagSize+1) = faceOrdinalIntrepid2; // face id
903  tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
904  tagView(tagNumber*tagSize+3) = numFunctionsPerQuadFace; // total number of dofs on this face
905  tagNumber++;
906  }
907  }
908  for (int triFaceOrdinalESEAS=0; triFaceOrdinalESEAS<numTriFaces; triFaceOrdinalESEAS++)
909  {
910  const int faceOrdinalESEAS = triFaceOrdinalESEAS + 1;
911  const int faceOrdinalIntrepid2 = intrepid2FaceOrdinals[faceOrdinalESEAS];
912  for (int functionOrdinal=0; functionOrdinal<numFunctionsPerTriFace; functionOrdinal++)
913  {
914  tagView(tagNumber*tagSize+0) = faceDim; // face dimension
915  tagView(tagNumber*tagSize+1) = faceOrdinalIntrepid2; // face id
916  tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
917  tagView(tagNumber*tagSize+3) = numFunctionsPerTriFace; // total number of dofs on this face
918  tagNumber++;
919  }
920  }
921  for (int volumeOrdinal=0; volumeOrdinal<numVolumes; volumeOrdinal++)
922  {
923  for (int functionOrdinal=0; functionOrdinal<numFunctionsPerVolume; functionOrdinal++)
924  {
925  tagView(tagNumber*tagSize+0) = volumeDim; // volume dimension
926  tagView(tagNumber*tagSize+1) = volumeOrdinal; // volume id
927  tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
928  tagView(tagNumber*tagSize+3) = numFunctionsPerVolume; // total number of dofs in this volume
929  tagNumber++;
930  }
931  }
932  INTREPID2_TEST_FOR_EXCEPTION(tagNumber != this->basisCardinality_, std::invalid_argument, "Internal error: basis tag enumeration is incorrect");
933  }
934  } else {
935  for (ordinal_type i=0;i<cardinality;++i) {
936  tagView(i*tagSize+0) = volumeDim; // volume dimension
937  tagView(i*tagSize+1) = 0; // volume ordinal
938  tagView(i*tagSize+2) = i; // local dof id
939  tagView(i*tagSize+3) = cardinality; // total number of dofs on this face
940  }
941  }
942 
943  // Basis-independent function sets tag and enum data in tagToOrdinal_ and ordinalToTag_ arrays:
944  // tags are constructed on host
945  this->setOrdinalTagData(this->tagToOrdinal_,
946  this->ordinalToTag_,
947  tagView,
948  this->basisCardinality_,
949  tagSize,
950  posScDim,
951  posScOrd,
952  posDfOrd);
953  }
954  }
955 
960  const char* getName() const override {
961  return "Intrepid2_IntegratedLegendreBasis_HGRAD_PYR";
962  }
963 
966  virtual bool requireOrientation() const override {
967  return (this->getDegree() > 2);
968  }
969 
970  // since the getValues() below only overrides the FEM variant, we specify that
971  // we use the base class's getValues(), which implements the FVD variant by throwing an exception.
972  // (It's an error to use the FVD variant on this basis.)
973  using BasisBase::getValues;
974 
993  virtual void getValues( OutputViewType outputValues, const PointViewType inputPoints,
994  const EOperator operatorType = OPERATOR_VALUE ) const override
995  {
996  auto numPoints = inputPoints.extent_int(0);
997 
999 
1000  FunctorType functor(operatorType, outputValues, inputPoints, polyOrder_, defineVertexFunctions);
1001 
1002  const int outputVectorSize = getVectorSizeForHierarchicalParallelism<OutputScalar>();
1003  const int pointVectorSize = getVectorSizeForHierarchicalParallelism<PointScalar>();
1004  const int vectorSize = std::max(outputVectorSize,pointVectorSize);
1005  const int teamSize = 1; // because of the way the basis functions are computed, we don't have a second level of parallelism...
1006 
1007  auto policy = Kokkos::TeamPolicy<ExecutionSpace>(numPoints,teamSize,vectorSize);
1008  Kokkos::parallel_for("Hierarchical_HGRAD_PYR_Functor", policy, functor);
1009  }
1010 
1019  BasisPtr<DeviceType,OutputScalar,PointScalar>
1020  getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override{
1023  using HGRAD_QUAD = Basis_Derived_HGRAD_QUAD<HGRAD_LINE>;
1024  const auto & p = this->basisDegree_;
1025  if(subCellDim == 1) // line basis
1026  {
1027  return Teuchos::rcp(new HGRAD_LINE(p));
1028  }
1029  else if (subCellDim == 2)
1030  {
1031  if (subCellOrd == 4) // quad basis
1032  {
1033  return Teuchos::rcp(new HGRAD_QUAD(p));
1034  }
1035  else // tri basis
1036  {
1037  return Teuchos::rcp(new HGRAD_TRI(p));
1038  }
1039  }
1040  INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Input parameters out of bounds");
1041  }
1042 
1047  virtual BasisPtr<typename Kokkos::HostSpace::device_type, OutputScalar, PointScalar>
1048  getHostBasis() const override {
1049  using HostDeviceType = typename Kokkos::HostSpace::device_type;
1051  return Teuchos::rcp( new HostBasisType(polyOrder_, pointType_) );
1052  }
1053  };
1054 } // end namespace Intrepid2
1055 
1056 // do ETI with default (double) type
1058 
1059 #endif /* Intrepid2_IntegratedLegendreBasis_HGRAD_PYR_h */
H(grad) basis on the line based on integrated Legendre polynomials.
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
virtual void getValues(const ExecutionSpace &, OutputViewType, const PointViewType, const EOperator=OPERATOR_VALUE) const
Evaluation of a FEM basis on a reference cell.
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line...
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::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
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.
Header function for Intrepid2::Util class and other utility functions.
Implementation of H(grad) basis on the quadrilateral that is templated on H(grad) on the line...
Kokkos::View< ordinal_type **, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray2DHost
View type for 2d host array.
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...
IntegratedLegendreBasis_HGRAD_PYR(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Constructor.
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line: e...
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line...
ordinal_type basisCardinality_
Cardinality of the basis, i.e., the number of basis functions/degrees-of-freedom. ...
BasisPtr< DeviceType, OutputScalar, PointScalar > getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override
returns the basis associated to a subCell.
H(grad) basis on the triangle based on integrated Legendre polynomials.
OrdinalTypeArray3DHost tagToOrdinal_
DoF tag to ordinal lookup table.
OrdinalTypeArray2DHost ordinalToTag_
&quot;true&quot; if tagToOrdinal_ and ordinalToTag_ have been initialized
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
ordinal_type getDegree() const
Returns the degree of the basis.
ECoordinates basisCoordinates_
The coordinate system for which the basis is defined.
shards::CellTopology basisCellTopology_
Base topology of the cells for which the basis is defined. See the Shards package for definition of b...
OrdinalTypeArray2DHost fieldOrdinalH1PolynomialDegree_
H^1 polynomial degree for each degree of freedom. Only defined for hierarchical bases right now...
Functor for computing values for the IntegratedLegendreBasis_HGRAD_PYR class.
OrdinalTypeArray2DHost fieldOrdinalPolynomialDegree_
Polynomial degree for each degree of freedom. Only defined for hierarchical bases right now...
virtual void getValues(OutputViewType outputValues, const PointViewType inputPoints, const EOperator operatorType=OPERATOR_VALUE) const override
Evaluation of a FEM basis on a reference cell.
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.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
Header file for the abstract base class Intrepid2::Basis.
virtual bool requireOrientation() const override
True if orientation is required.
typename DeviceType::execution_space ExecutionSpace
(Kokkos) Execution space for basis.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.