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
53
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"
66#include "Intrepid2_Utils.hpp"
67
68#include "Teuchos_RCP.hpp"
69
70namespace Intrepid2
71{
73 template<class PointScalar>
74 KOKKOS_INLINE_FUNCTION
75 void affinePyramid(Kokkos::Array<PointScalar,5> &lambda,
76 Kokkos::Array<Kokkos::Array<PointScalar,3>,5> &lambdaGrad,
77 Kokkos::Array<Kokkos::Array<PointScalar,3>,2> &mu,
78 Kokkos::Array<Kokkos::Array<Kokkos::Array<PointScalar,3>,3>,2> &muGrad,
79 Kokkos::Array<Kokkos::Array<PointScalar,2>,3> &nu,
80 Kokkos::Array<Kokkos::Array<Kokkos::Array<PointScalar,3>,2>,3> &nuGrad,
81 Kokkos::Array<PointScalar,3> &coords)
82 {
83 const auto & x = coords[0];
84 const auto & y = coords[1];
85 const auto & z = coords[2];
86 nu[0][0] = 1. - x - z; // nu_0^{\zeta,\xi_1}
87 nu[0][1] = 1. - y - z; // nu_0^{\zeta,\xi_2}
88 nu[1][0] = x ; // nu_1^{\zeta,\xi_1}
89 nu[1][1] = y ; // nu_1^{\zeta,\xi_2}
90 nu[2][0] = z ; // nu_2^{\zeta,\xi_1}
91 nu[2][1] = z ; // nu_2^{\zeta,\xi_2}
92
93 nuGrad[0][0][0] = -1. ; // nu_0^{\zeta,\xi_1}_dxi_1
94 nuGrad[0][0][1] = 0. ; // nu_0^{\zeta,\xi_1}_dxi_2
95 nuGrad[0][0][2] = -1. ; // nu_0^{\zeta,\xi_1}_dzeta
96
97 nuGrad[0][1][0] = 0. ; // nu_0^{\zeta,\xi_2}_dxi_1
98 nuGrad[0][1][1] = -1. ; // nu_0^{\zeta,\xi_2}_dxi_2
99 nuGrad[0][1][2] = -1. ; // nu_0^{\zeta,\xi_2}_dzeta
100
101 nuGrad[1][0][0] = 1. ; // nu_1^{\zeta,\xi_1}_dxi_1
102 nuGrad[1][0][1] = 0. ; // nu_1^{\zeta,\xi_1}_dxi_2
103 nuGrad[1][0][2] = 0. ; // nu_1^{\zeta,\xi_1}_dzeta
104
105 nuGrad[1][1][0] = 0. ; // nu_1^{\zeta,\xi_2}_dxi_1
106 nuGrad[1][1][1] = 1. ; // nu_1^{\zeta,\xi_2}_dxi_2
107 nuGrad[1][1][2] = 0. ; // nu_1^{\zeta,\xi_2}_dzeta
108
109 nuGrad[2][0][0] = 0. ; // nu_2^{\zeta,\xi_1}_dxi_1
110 nuGrad[2][0][1] = 0. ; // nu_2^{\zeta,\xi_1}_dxi_2
111 nuGrad[2][0][2] = 1. ; // nu_2^{\zeta,\xi_1}_dzeta
112
113 nuGrad[2][1][0] = 0. ; // nu_2^{\zeta,\xi_2}_dxi_1
114 nuGrad[2][1][1] = 0. ; // nu_2^{\zeta,\xi_2}_dxi_2
115 nuGrad[2][1][2] = 1. ; // nu_2^{\zeta,\xi_2}_dzeta
116
117 // (1 - z) goes in denominator -- so we check for 1-z=0
118 auto & muZ_0 = mu[0][2];
119 auto & muZ_1 = mu[1][2];
120 const double epsilon = 1e-12;
121 muZ_0 = (fabs(1.-z) > epsilon) ? 1. - z : epsilon;
122 muZ_1 = (fabs(1.-z) > epsilon) ? z : 1. - epsilon;
123 PointScalar scaling = 1. / muZ_0;
124 mu[0][0] = 1. - x * scaling;
125 mu[0][1] = 1. - y * scaling;
126 mu[1][0] = x * scaling;
127 mu[1][1] = y * scaling;
128
129 PointScalar scaling2 = scaling * scaling;
130 muGrad[0][0][0] = -scaling ;
131 muGrad[0][0][1] = 0. ;
132 muGrad[0][0][2] = - x * scaling2;
133
134 muGrad[0][1][0] = 0. ;
135 muGrad[0][1][1] = -scaling ;
136 muGrad[0][1][2] = -y * scaling2;
137
138 muGrad[0][2][0] = 0. ;
139 muGrad[0][2][1] = 0. ;
140 muGrad[0][2][2] = -1. ;
141
142 muGrad[1][0][0] = scaling ;
143 muGrad[1][0][1] = 0. ;
144 muGrad[1][0][2] = x * scaling2;
145
146 muGrad[1][1][0] = 0. ;
147 muGrad[1][1][1] = scaling ;
148 muGrad[1][1][2] = y * scaling2;
149
150 muGrad[1][2][0] = 0. ;
151 muGrad[1][2][1] = 0. ;
152 muGrad[1][2][2] = 1. ;
153
154 lambda[0] = nu[0][0] * mu[0][1];
155 lambda[1] = nu[0][1] * mu[1][0];
156 lambda[2] = nu[1][0] * mu[1][1];
157 lambda[3] = nu[1][1] * mu[0][0];
158 lambda[4] = z;
159
160 for (int d=0; d<3; d++)
161 {
162 lambdaGrad[0][d] = nu[0][0] * muGrad[0][1][d] + nuGrad[0][0][d] * mu[0][1];
163 lambdaGrad[1][d] = nu[0][1] * muGrad[1][0][d] + nuGrad[0][1][d] * mu[1][0];
164 lambdaGrad[2][d] = nu[1][0] * muGrad[1][1][d] + nuGrad[1][0][d] * mu[1][1];
165 lambdaGrad[3][d] = nu[1][1] * muGrad[0][0][d] + nuGrad[1][1][d] * mu[0][0];
166 }
167 lambdaGrad[4][0] = 0;
168 lambdaGrad[4][1] = 0;
169 lambdaGrad[4][2] = 1;
170 }
171
173 template<class PointScalar>
174 KOKKOS_INLINE_FUNCTION
175 void transformToESEASPyramid( PointScalar &x_eseas, PointScalar &y_eseas, PointScalar &z_eseas,
176 const PointScalar &x_int2, const PointScalar &y_int2, const PointScalar &z_int2)
177 {
178 x_eseas = (x_int2 + 1. - z_int2) / 2.;
179 y_eseas = (y_int2 + 1. - z_int2) / 2.;
180 z_eseas = z_int2;
181 }
182
184 template<class OutputScalar>
185 KOKKOS_INLINE_FUNCTION
186 void transformFromESEASPyramidGradient( OutputScalar &dx_int2, OutputScalar &dy_int2, OutputScalar &dz_int2,
187 const OutputScalar &dx_eseas, const OutputScalar &dy_eseas, const OutputScalar &dz_eseas)
188 {
189 dx_int2 = dx_eseas / 2.;
190 dy_int2 = dy_eseas / 2.;
191 dz_int2 = dz_eseas - dx_int2 - dy_int2;
192 }
193
199 template<class DeviceType, class OutputScalar, class PointScalar,
200 class OutputFieldType, class InputPointsType>
201 struct Hierarchical_HGRAD_PYR_Functor
202 {
203 using ExecutionSpace = typename DeviceType::execution_space;
204 using ScratchSpace = typename ExecutionSpace::scratch_memory_space;
205 using OutputScratchView = Kokkos::View<OutputScalar*,ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
206 using OutputScratchView2D = Kokkos::View<OutputScalar**,ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
207 using PointScratchView = Kokkos::View<PointScalar*, ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
208
209 using TeamPolicy = Kokkos::TeamPolicy<ExecutionSpace>;
210 using TeamMember = typename TeamPolicy::member_type;
211
212 EOperator opType_;
213
214 OutputFieldType output_; // F,P
215 InputPointsType inputPoints_; // P,D
216
217 int polyOrder_;
218 bool defineVertexFunctions_;
219 int numFields_, numPoints_;
220
221 size_t fad_size_output_;
222
223 static const int numVertices = 5;
224 static const int numMixedEdges = 4;
225 static const int numTriEdges = 4;
226 static const int numEdges = 8;
227 // the following ordering of the edges matches that used by ESEAS
228 // (it *looks* like this is what ESEAS uses; the basis comparison tests will clarify whether I've read their code correctly)
229 // see also PyramidEdgeNodeMap in Shards_BasicTopologies.hpp
230 const int edge_start_[numEdges] = {0,1,2,3,0,1,2,3}; // edge i is from edge_start_[i] to edge_end_[i]
231 const int edge_end_[numEdges] = {1,2,3,0,4,4,4,4}; // edge i is from edge_start_[i] to edge_end_[i]
232
233 // quadrilateral face comes first
234 static const int numQuadFaces = 1;
235 static const int numTriFaces = 4;
236
237 // face ordering matches ESEAS. (See BlendProjectPyraTF in ESEAS.)
238 const int tri_face_vertex_0[numTriFaces] = {0,1,3,0}; // faces are abc where 0 ≤ a < b < c ≤ 3
239 const int tri_face_vertex_1[numTriFaces] = {1,2,2,3};
240 const int tri_face_vertex_2[numTriFaces] = {4,4,4,4};
241
242 Hierarchical_HGRAD_PYR_Functor(EOperator opType, OutputFieldType output, InputPointsType inputPoints,
243 int polyOrder, bool defineVertexFunctions)
244 : opType_(opType), output_(output), inputPoints_(inputPoints),
245 polyOrder_(polyOrder), defineVertexFunctions_(defineVertexFunctions),
246 fad_size_output_(getScalarDimensionForView(output))
247 {
248 numFields_ = output.extent_int(0);
249 numPoints_ = output.extent_int(1);
250 const auto & p = polyOrder;
251 const auto p3 = p * p * p;
252 INTREPID2_TEST_FOR_EXCEPTION(numPoints_ != inputPoints.extent_int(0), std::invalid_argument, "point counts need to match!");
253 INTREPID2_TEST_FOR_EXCEPTION(numFields_ != p3 + 3 * p + 1, std::invalid_argument, "output field size does not match basis cardinality");
254 }
255
256 KOKKOS_INLINE_FUNCTION
257 void operator()( const TeamMember & teamMember ) const
258 {
259 auto pointOrdinal = teamMember.league_rank();
260 OutputScratchView scratch1D_1, scratch1D_2, scratch1D_3;
261 OutputScratchView scratch1D_4, scratch1D_5, scratch1D_6;
262 OutputScratchView scratch1D_7, scratch1D_8, scratch1D_9;
263 OutputScratchView2D scratch2D_1, scratch2D_2, scratch2D_3;
264 const int numAlphaValues = (polyOrder_-1 > 1) ? (polyOrder_-1) : 1; // make numAlphaValues at least 1 so we can avoid zero-extent allocations…
265 if (fad_size_output_ > 0) {
266 scratch1D_1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
267 scratch1D_2 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
268 scratch1D_3 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
269 scratch1D_4 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
270 scratch1D_5 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
271 scratch1D_6 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
272 scratch1D_7 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
273 scratch1D_8 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
274 scratch1D_9 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
275 scratch2D_1 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
276 scratch2D_2 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
277 scratch2D_3 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
278 }
279 else {
280 scratch1D_1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
281 scratch1D_2 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
282 scratch1D_3 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
283 scratch1D_4 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
284 scratch1D_5 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
285 scratch1D_6 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
286 scratch1D_7 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
287 scratch1D_8 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
288 scratch1D_9 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
289 scratch2D_1 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
290 scratch2D_2 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
291 scratch2D_3 = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
292 }
293
294 const auto & x = inputPoints_(pointOrdinal,0);
295 const auto & y = inputPoints_(pointOrdinal,1);
296 const auto & z = inputPoints_(pointOrdinal,2);
297
298 // Intrepid2 uses (-1,1)^2 for x,y
299 // ESEAS uses (0,1)^2
300 // (Can look at what we do on the HGRAD_LINE for reference; there's a similar difference for line topology.)
301
302 Kokkos::Array<PointScalar,3> coords;
303 transformToESEASPyramid<>(coords[0], coords[1], coords[2], x, y, z); // map x,y coordinates from (-z,z)^2 to (0,z)^2
304
305 // pyramid "affine" coordinates and gradients get stored in lambda, lambdaGrad:
306 using Kokkos::Array;
307 Array<PointScalar,5> lambda;
308 Array<Kokkos::Array<PointScalar,3>,5> lambdaGrad;
309
310 Array<Array<PointScalar,3>,2> mu;
311 Array<Array<Array<PointScalar,3>,3>,2> muGrad;
312
313 Array<Array<PointScalar,2>,3> nu;
314 Array<Array<Array<PointScalar,3>,2>,3> nuGrad;
315
316 affinePyramid(lambda, lambdaGrad, mu, muGrad, nu, nuGrad, coords);
317
318 switch (opType_)
319 {
320 case OPERATOR_VALUE:
321 {
322 // vertex functions come first, according to vertex ordering: (0,0,0), (1,0,0), (1,1,0), (0,1,0), (0,0,1)
323 for (int vertexOrdinal=0; vertexOrdinal<numVertices; vertexOrdinal++)
324 {
325 output_(vertexOrdinal,pointOrdinal) = lambda[vertexOrdinal];
326 }
327 if (!defineVertexFunctions_)
328 {
329 // "DG" basis case
330 // here, we overwrite the first vertex function with 1:
331 output_(0,pointOrdinal) = 1.0;
332 }
333
334 // rename scratch1, scratch2
335 auto & Li_a1 = scratch1D_1;
336 auto & Li_a2 = scratch1D_2;
337
338 Polynomials::shiftedScaledIntegratedLegendreValues(Li_a1, polyOrder_, nu[1][0], nu[0][0] + nu[1][0]);
339 Polynomials::shiftedScaledIntegratedLegendreValues(Li_a2, polyOrder_, nu[1][1], nu[0][1] + nu[1][1]);
340
341 // edge functions
342 // "mixed" edges (those shared between the quad and some tri face) first
343 int fieldOrdinalOffset = numVertices;
344 for (int edgeOrdinal=0; edgeOrdinal<numMixedEdges; edgeOrdinal++)
345 {
346 // edge 0,2 --> a=1, b=2
347 // edge 1,3 --> a=2, b=1
348 int a = (edgeOrdinal % 2 == 0) ? 1 : 2;
349 int b = 3 - a;
350 auto & Li = (a == 1) ? Li_a1 : Li_a2;
351 // edge 0,3 --> c=0
352 // edge 1,2 --> c=1
353 int c = ((edgeOrdinal == 0) || (edgeOrdinal == 3)) ? 0 : 1;
354 for (int i=2; i<=polyOrder_; i++)
355 {
356 output_(fieldOrdinalOffset,pointOrdinal) = mu[c][b-1] * Li(i);
357 fieldOrdinalOffset++;
358 }
359 }
360
361 // triangle edges next
362 // rename scratch1
363 auto & Li = scratch1D_1;
364 for (int edgeOrdinal=0; edgeOrdinal<numMixedEdges; edgeOrdinal++)
365 {
366 const auto & lambda_a = lambda[edgeOrdinal];
367 Polynomials::shiftedScaledIntegratedLegendreValues(Li, polyOrder_, lambda[4], lambda_a + lambda[4]);
368 for (int i=2; i<=polyOrder_; i++)
369 {
370 output_(fieldOrdinalOffset,pointOrdinal) = Li(i);
371 fieldOrdinalOffset++;
372 }
373 }
374
375 // quadrilateral face
376 // mu_0 * phi_i(mu_0^{xi_1},mu_1^{xi_1}) * phi_j(mu_0^{xi_2},mu_1^{xi_2})
377
378 // rename scratch
379 Li = scratch1D_1;
380 auto & Lj = scratch1D_2;
381 Polynomials::shiftedScaledIntegratedLegendreValues(Li, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
382 Polynomials::shiftedScaledIntegratedLegendreValues(Lj, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
383 // following the ESEAS ordering: j increments first
384 for (int j=2; j<=polyOrder_; j++)
385 {
386 for (int i=2; i<=polyOrder_; i++)
387 {
388 output_(fieldOrdinalOffset,pointOrdinal) = mu[0][2] * Li(i) * Lj(j);
389 fieldOrdinalOffset++;
390 }
391 }
392
393 /*
394 Face functions for triangular face (d,e,f) are the product of:
395 mu_c^{zeta,xi_b},
396 edge functions on their (d,e) edge,
397 and a Jacobi polynomial [L^2i_j](s0+s1,s2) = L^2i_j(s2;s0+s1+s2),
398
399 where s0, s1, s2 are nu[0][a-1],nu[1][a-1],nu[2][a-1],
400 and (a,b) = (1,2) for faces 0,2; (a,b) = (2,1) for others;
401 c = 0 for faces 0,3; c = 1 for others
402 */
403 for (int faceOrdinal=0; faceOrdinal<numTriFaces; faceOrdinal++)
404 {
405 // face 0,2 --> a=1, b=2
406 // face 1,3 --> a=2, b=1
407 int a = (faceOrdinal % 2 == 0) ? 1 : 2;
408 int b = 3 - a;
409 // face 0,3 --> c=0
410 // face 1,2 --> c=1
411 int c = ((faceOrdinal == 0) || (faceOrdinal == 3)) ? 0 : 1;
412
413 const auto & s0 = nu[0][a-1];
414 const auto & s1 = nu[1][a-1];
415 const auto & s2 = nu[2][a-1];
416 const PointScalar jacobiScaling = s0 + s1 + s2;
417
418 // compute integrated Jacobi values for each desired value of alpha
419 // relabel scratch2D_1
420 auto & jacobi = scratch2D_1;
421 for (int n=2; n<=polyOrder_; n++)
422 {
423 const double alpha = n*2;
424 const int alphaOrdinal = n-2;
425 using Kokkos::subview;
426 using Kokkos::ALL;
427 auto jacobi_alpha = subview(jacobi, alphaOrdinal, ALL);
428 Polynomials::shiftedScaledIntegratedJacobiValues(jacobi_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
429 }
430 // relabel scratch1D_1
431 auto & edge_s0s1 = scratch1D_1;
432 Polynomials::shiftedScaledIntegratedLegendreValues(edge_s0s1, polyOrder_, s1, s0 + s1);
433
434 for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
435 {
436 for (int i=2; i<totalPolyOrder; i++)
437 {
438 const auto & edgeValue = edge_s0s1(i);
439 const int alphaOrdinal = i-2;
440
441 const int j = totalPolyOrder - i;
442 const auto & jacobiValue = jacobi(alphaOrdinal,j);
443 output_(fieldOrdinalOffset,pointOrdinal) = mu[c][b-1] * edgeValue * jacobiValue;
444
445 fieldOrdinalOffset++;
446 }
447 }
448 }
449
450 // interior functions
451 // these are the product of the same quadrilateral function that we blended for the quadrilateral face and
452 // [L_k](mu_{0}^zeta, mu_1^zeta)
453
454 // rename scratch
455 Li = scratch1D_1;
456 Lj = scratch1D_2;
457 auto & Lk = scratch1D_3;
458 Polynomials::shiftedScaledIntegratedLegendreValues(Li, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
459 Polynomials::shiftedScaledIntegratedLegendreValues(Lj, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
460 Polynomials::shiftedScaledIntegratedLegendreValues(Lk, polyOrder_, mu[1][2], mu[0][2] + mu[1][2]);
461 // following the ESEAS ordering: k increments first
462 for (int k=2; k<=polyOrder_; k++)
463 {
464 for (int j=2; j<=polyOrder_; j++)
465 {
466 for (int i=2; i<=polyOrder_; i++)
467 {
468 output_(fieldOrdinalOffset,pointOrdinal) = Lk(k) * Li(i) * Lj(j);
469 fieldOrdinalOffset++;
470 }
471 }
472 }
473 } // end OPERATOR_VALUE
474 break;
475 case OPERATOR_GRAD:
476 case OPERATOR_D1:
477 {
478 // vertex functions
479
480 for (int vertexOrdinal=0; vertexOrdinal<numVertices; vertexOrdinal++)
481 {
482 for (int d=0; d<3; d++)
483 {
484 output_(vertexOrdinal,pointOrdinal,d) = lambdaGrad[vertexOrdinal][d];
485 }
486 }
487
488 if (!defineVertexFunctions_)
489 {
490 // "DG" basis case
491 // here, the first "vertex" function is 1, so the derivative is 0:
492 output_(0,pointOrdinal,0) = 0.0;
493 output_(0,pointOrdinal,1) = 0.0;
494 output_(0,pointOrdinal,2) = 0.0;
495 }
496
497 // edge functions
498 int fieldOrdinalOffset = numVertices;
499
500 // mixed edges first
501 auto & P_i_minus_1 = scratch1D_1;
502 auto & L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
503 auto & L_i = scratch1D_3;
504
505 for (int edgeOrdinal=0; edgeOrdinal<numMixedEdges; edgeOrdinal++)
506 {
507 // edge 0,2 --> a=1, b=2
508 // edge 1,3 --> a=2, b=1
509 int a = (edgeOrdinal % 2 == 0) ? 1 : 2;
510 int b = 3 - a;
511
512 Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, nu[1][a-1], nu[0][a-1] + nu[1][a-1]);
513 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, nu[1][a-1], nu[0][a-1] + nu[1][a-1]);
514 Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, nu[1][a-1], nu[0][a-1] + nu[1][a-1]);
515
516 // edge 0,3 --> c=0
517 // edge 1,2 --> c=1
518 int c = ((edgeOrdinal == 0) || (edgeOrdinal == 3)) ? 0 : 1;
519 for (int i=2; i<=polyOrder_; i++)
520 {
521 // grad (mu[c][b-1] * Li(i)) = grad (mu[c][b-1]) * Li(i) + mu[c][b-1] * grad Li(i)
522 const auto & R_i_minus_1 = L_i_dt(i);
523
524 for (int d=0; d<3; d++)
525 {
526 // 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)
527
528 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]);
529 output_(fieldOrdinalOffset,pointOrdinal,d) = muGrad[c][b-1][d] * L_i(i) + mu[c][b-1] * grad_Li_d;
530 }
531 fieldOrdinalOffset++;
532 }
533 }
534
535 // triangle edges next
536 P_i_minus_1 = scratch1D_1;
537 L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
538 L_i = scratch1D_3;
539 for (int edgeOrdinal=0; edgeOrdinal<numMixedEdges; edgeOrdinal++)
540 {
541 const auto & lambda_a = lambda [edgeOrdinal];
542 const auto & lambdaGrad_a = lambdaGrad[edgeOrdinal];
543 Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, lambda[4], lambda_a + lambda[4]);
544 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, lambda[4], lambda_a + lambda[4]);
545 Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, lambda[4], lambda_a + lambda[4]);
546
547 for (int i=2; i<=polyOrder_; i++)
548 {
549 const auto & R_i_minus_1 = L_i_dt(i);
550 for (int d=0; d<3; d++)
551 {
552 // grad [L_i](s0,s1) = [P_{i-1}](s0,s1) * grad s1 + [R_{i-1}](s0,s1) * grad (s0 + s1)
553 // here, s1 = lambda[4], s0 = lambda_a
554 OutputScalar grad_Li_d = P_i_minus_1(i-1) * lambdaGrad[4][d] + R_i_minus_1 * (lambdaGrad_a[d] + lambdaGrad[4][d]);
555 output_(fieldOrdinalOffset,pointOrdinal,d) = grad_Li_d;
556 }
557 fieldOrdinalOffset++;
558 }
559 }
560
561 // quadrilateral faces
562 // rename scratch
563 P_i_minus_1 = scratch1D_1;
564 L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
565 L_i = scratch1D_3;
566 auto & P_j_minus_1 = scratch1D_4;
567 auto & L_j_dt = scratch1D_5; // R_{j-1} = d/dt L_j
568 auto & L_j = scratch1D_6;
569 Polynomials::shiftedScaledIntegratedLegendreValues(L_i, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
570 Polynomials::shiftedScaledIntegratedLegendreValues(L_j, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
571
572 Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, mu[1][0], mu[0][0] + mu[1][0]);
573 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
574 Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
575
576 Polynomials::shiftedScaledLegendreValues (P_j_minus_1, polyOrder_-1, mu[1][1], mu[0][1] + mu[1][1]);
577 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_j_dt, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
578 Polynomials::shiftedScaledIntegratedLegendreValues (L_j, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
579
580 // following the ESEAS ordering: j increments first
581 for (int j=2; j<=polyOrder_; j++)
582 {
583 const auto & R_j_minus_1 = L_j_dt(j);
584
585 for (int i=2; i<=polyOrder_; i++)
586 {
587 const auto & R_i_minus_1 = L_i_dt(i);
588
589 OutputScalar phi_quad = L_i(i) * L_j(j);
590
591 for (int d=0; d<3; d++)
592 {
593 // grad [L_j](s0,s1) = [P_{j-1}](s0,s1) * grad s1 + [R_{j-1}](s0,s1) * grad (s0 + s1)
594 // here, s1 = mu[1][1], s0 = mu[0][1]
595 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]);
596 // for L_i, s1 = mu[1][0], s0 = mu[0][0]
597 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]);
598
599 OutputScalar grad_phi_quad_d = L_i(i) * grad_Lj_d + L_j(j) * grad_Li_d;
600
601 output_(fieldOrdinalOffset,pointOrdinal,d) = mu[0][2] * grad_phi_quad_d + phi_quad * muGrad[0][2][d];
602 }
603
604 fieldOrdinalOffset++;
605 }
606 }
607
608 // triangle faces
609 for (int faceOrdinal=0; faceOrdinal<numTriFaces; faceOrdinal++)
610 {
611 // face 0,2 --> a=1, b=2
612 // face 1,3 --> a=2, b=1
613 int a = (faceOrdinal % 2 == 0) ? 1 : 2;
614 int b = 3 - a;
615 // face 0,3 --> c=0
616 // face 1,2 --> c=1
617 int c = ((faceOrdinal == 0) || (faceOrdinal == 3)) ? 0 : 1;
618
619 const auto & s0 = nu[0][a-1];
620 const auto & s1 = nu[1][a-1];
621 const auto & s2 = nu[2][a-1];
622
623 const auto & s0Grad = nuGrad[0][a-1];
624 const auto & s1Grad = nuGrad[1][a-1];
625 const auto & s2Grad = nuGrad[2][a-1];
626
627 const PointScalar jacobiScaling = s0 + s1 + s2;
628
629 // compute integrated Jacobi values for each desired value of alpha
630 // relabel storage:
631 // 1D containers:
632 auto & P_i_minus_1 = scratch1D_1;
633 auto & L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
634 auto & L_i = scratch1D_3;
635 // 2D containers:
636 auto & L_2i_j_dt = scratch2D_1;
637 auto & L_2i_j = scratch2D_2;
638 auto & P_2i_j_minus_1 = scratch2D_3;
639 for (int n=2; n<=polyOrder_; n++)
640 {
641 const double alpha = n*2;
642 const int alphaOrdinal = n-2;
643 using Kokkos::subview;
644 using Kokkos::ALL;
645 auto L_2i_j_dt_alpha = subview(L_2i_j_dt, alphaOrdinal, ALL);
646 auto L_2i_j_alpha = subview(L_2i_j, alphaOrdinal, ALL);
647 auto P_2i_j_minus_1_alpha = subview(P_2i_j_minus_1, alphaOrdinal, ALL);
648 Polynomials::shiftedScaledIntegratedJacobiValues_dt(L_2i_j_dt_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
649 Polynomials::shiftedScaledIntegratedJacobiValues ( L_2i_j_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
650 Polynomials::shiftedScaledJacobiValues (P_2i_j_minus_1_alpha, alpha, polyOrder_-1, s2, jacobiScaling);
651 }
652 Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, s1, s0 + s1);
653 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, s1, s0 + s1);
654 Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, s1, s0 + s1);
655
656 for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
657 {
658 for (int i=2; i<totalPolyOrder; i++)
659 {
660 const int alphaOrdinal = i-2;
661 const int j = totalPolyOrder - i;
662
663 const auto & R_i_minus_1 = L_i_dt(i);
664 OutputScalar phi_tri = L_2i_j(alphaOrdinal,j) * L_i(i);
665
666 for (int d=0; d<3; d++)
667 {
668 // grad [L_i](s0,s1) = [P_{i-1}](s0,s1) * grad s1 + [R_{i-1}](s0,s1) * grad (s0 + s1)
669 OutputScalar grad_Li_d = P_i_minus_1(i-1) * s1Grad[d] + R_i_minus_1 * (s0Grad[d] + s1Grad[d]);
670 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]);
671 OutputScalar grad_phi_tri_d = L_i(i) * grad_L2i_j_d + L_2i_j(alphaOrdinal,j) * grad_Li_d;
672
673 output_(fieldOrdinalOffset,pointOrdinal,d) = mu[c][b-1] * grad_phi_tri_d + phi_tri * muGrad[c][b-1][d];
674 }
675 fieldOrdinalOffset++;
676 }
677 }
678 }
679
680 // interior functions
681 P_i_minus_1 = scratch1D_1;
682 L_i_dt = scratch1D_2; // R_{i-1} = d/dt L_i
683 L_i = scratch1D_3;
684 P_j_minus_1 = scratch1D_4;
685 L_j_dt = scratch1D_5; // R_{j-1} = d/dt L_j
686 L_j = scratch1D_6;
687 auto & P_k_minus_1 = scratch1D_7;
688 auto & L_k_dt = scratch1D_8; // R_{k-1} = d/dt L_k
689 auto & L_k = scratch1D_9;
690
691 Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, mu[1][0], mu[0][0] + mu[1][0]);
692 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
693 Polynomials::shiftedScaledIntegratedLegendreValues (L_i, polyOrder_, mu[1][0], mu[0][0] + mu[1][0]);
694
695 Polynomials::shiftedScaledLegendreValues (P_j_minus_1, polyOrder_-1, mu[1][1], mu[0][1] + mu[1][1]);
696 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_j_dt, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
697 Polynomials::shiftedScaledIntegratedLegendreValues (L_j, polyOrder_, mu[1][1], mu[0][1] + mu[1][1]);
698
699 Polynomials::shiftedScaledLegendreValues (P_k_minus_1, polyOrder_-1, mu[1][2], mu[0][2] + mu[1][2]);
700 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_k_dt, polyOrder_, mu[1][2], mu[0][2] + mu[1][2]);
701 Polynomials::shiftedScaledIntegratedLegendreValues (L_k, polyOrder_, mu[1][2], mu[0][2] + mu[1][2]);
702
703 // following the ESEAS ordering: k increments first
704 for (int k=2; k<=polyOrder_; k++)
705 {
706 const auto & R_k_minus_1 = L_k_dt(k);
707
708 for (int j=2; j<=polyOrder_; j++)
709 {
710 const auto & R_j_minus_1 = L_j_dt(j);
711
712 for (int i=2; i<=polyOrder_; i++)
713 {
714 const auto & R_i_minus_1 = L_i_dt(i);
715
716 OutputScalar phi_quad = L_i(i) * L_j(j);
717
718 for (int d=0; d<3; d++)
719 {
720 // grad [L_i](s0,s1) = [P_{i-1}](s0,s1) * grad s1 + [R_{i-1}](s0,s1) * grad (s0 + s1)
721 // for L_i, s1 = mu[1][0], s0 = mu[0][0]
722 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]);
723 // for L_j, s1 = mu[1][1], s0 = mu[0][1]
724 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]);
725 // for L_k, s1 = mu[1][2], s0 = mu[0][2]
726 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]);
727
728 OutputScalar grad_phi_quad_d = L_i(i) * grad_Lj_d + L_j(j) * grad_Li_d;
729
730 output_(fieldOrdinalOffset,pointOrdinal,d) = L_k(k) * grad_phi_quad_d + phi_quad * grad_Lk_d;
731 }
732
733 fieldOrdinalOffset++;
734 }
735 }
736 }
737
738 for (int basisOrdinal=0; basisOrdinal<numFields_; basisOrdinal++)
739 {
740 // transform derivatives to account for the ref space transformation: Intrepid2 uses (-z,z)^2; ESEAS uses (0,z)^2
741 const auto dx_eseas = output_(basisOrdinal,pointOrdinal,0);
742 const auto dy_eseas = output_(basisOrdinal,pointOrdinal,1);
743 const auto dz_eseas = output_(basisOrdinal,pointOrdinal,2);
744
745 auto &dx_int2 = output_(basisOrdinal,pointOrdinal,0);
746 auto &dy_int2 = output_(basisOrdinal,pointOrdinal,1);
747 auto &dz_int2 = output_(basisOrdinal,pointOrdinal,2);
748
749 transformFromESEASPyramidGradient(dx_int2, dy_int2, dz_int2, dx_eseas, dy_eseas, dz_eseas);
750 }
751
752 } // end OPERATOR_GRAD block
753 break;
754 case OPERATOR_D2:
755 case OPERATOR_D3:
756 case OPERATOR_D4:
757 case OPERATOR_D5:
758 case OPERATOR_D6:
759 case OPERATOR_D7:
760 case OPERATOR_D8:
761 case OPERATOR_D9:
762 case OPERATOR_D10:
763 INTREPID2_TEST_FOR_ABORT( true,
764 ">>> ERROR: (Intrepid2::Hierarchical_HGRAD_PYR_Functor) Computing of second and higher-order derivatives is not currently supported");
765 default:
766 // unsupported operator type
767 device_assert(false);
768 }
769 }
770
771 // Provide the shared memory capacity.
772 // This function takes the team_size as an argument,
773 // which allows team_size-dependent allocations.
774 size_t team_shmem_size (int team_size) const
775 {
776 // we use shared memory to create a fast buffer for basis computations
777 // 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.
778 // for the (integrated) Jacobi computations, though, we want (p+1)*(# alpha values)
779 // 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.
780 // We can have up to 3 of the (integrated) Jacobi values needed at once.
781 const int numAlphaValues = std::max(polyOrder_-1, 1); // make it at least 1 so we can avoid zero-extent ranks…
782 size_t shmem_size = 0;
783 if (fad_size_output_ > 0)
784 {
785 // Legendre:
786 shmem_size += 9 * OutputScratchView::shmem_size(polyOrder_ + 1, fad_size_output_);
787 // Jacobi:
788 shmem_size += 3 * OutputScratchView2D::shmem_size(numAlphaValues, polyOrder_ + 1, fad_size_output_);
789 }
790 else
791 {
792 // Legendre:
793 shmem_size += 9 * OutputScratchView::shmem_size(polyOrder_ + 1);
794 // Jacobi:
795 shmem_size += 3 * OutputScratchView2D::shmem_size(numAlphaValues, polyOrder_ + 1);
796 }
797
798 return shmem_size;
799 }
800 };
801
819 template<typename DeviceType,
820 typename OutputScalar = double,
821 typename PointScalar = double,
822 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.
824 : public Basis<DeviceType,OutputScalar,PointScalar>
825 {
826 public:
827 using BasisBase = Basis<DeviceType,OutputScalar,PointScalar>;
828
831
832 using typename BasisBase::OutputViewType;
833 using typename BasisBase::PointViewType;
834 using typename BasisBase::ScalarViewType;
835
836 using typename BasisBase::ExecutionSpace;
837
838 protected:
839 int polyOrder_; // the maximum order of the polynomial
840 EPointType pointType_;
841 public:
852 IntegratedLegendreBasis_HGRAD_PYR(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
853 :
854 polyOrder_(polyOrder),
855 pointType_(pointType)
856 {
857 INTREPID2_TEST_FOR_EXCEPTION(pointType!=POINTTYPE_DEFAULT,std::invalid_argument,"PointType not supported");
858 this->basisCardinality_ = polyOrder * polyOrder * polyOrder + 3 * polyOrder + 1;
859 this->basisDegree_ = polyOrder;
860 this->basisCellTopology_ = shards::CellTopology(shards::getCellTopologyData<shards::Pyramid<> >() );
861 this->basisType_ = BASIS_FEM_HIERARCHICAL;
862 this->basisCoordinates_ = COORDINATES_CARTESIAN;
863 this->functionSpace_ = FUNCTION_SPACE_HGRAD;
864
865 const int degreeLength = 1;
866 this->fieldOrdinalPolynomialDegree_ = OrdinalTypeArray2DHost("Integrated Legendre H(grad) tetrahedron polynomial degree lookup", this->basisCardinality_, degreeLength);
867 this->fieldOrdinalH1PolynomialDegree_ = OrdinalTypeArray2DHost("Integrated Legendre H(grad) tetrahedron polynomial H^1 degree lookup", this->basisCardinality_, degreeLength);
868
869 int fieldOrdinalOffset = 0;
870 // **** vertex functions **** //
871 const int numVertices = this->basisCellTopology_.getVertexCount();
872 for (int i=0; i<numVertices; i++)
873 {
874 // for H(grad) on Pyramid, if defineVertexFunctions is false, first five basis members are linear
875 // if not, then the only difference is that the first member is constant
876 this->fieldOrdinalPolynomialDegree_ (i,0) = 1;
877 this->fieldOrdinalH1PolynomialDegree_(i,0) = 1;
878 }
879 if (!defineVertexFunctions)
880 {
881 this->fieldOrdinalPolynomialDegree_ (0,0) = 0;
882 this->fieldOrdinalH1PolynomialDegree_(0,0) = 0;
883 }
884 fieldOrdinalOffset += numVertices;
885
886 // **** edge functions **** //
887 const int numFunctionsPerEdge = polyOrder - 1; // bubble functions: all but the vertices
888 const int numEdges = this->basisCellTopology_.getEdgeCount();
889 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
890 {
891 for (int i=0; i<numFunctionsPerEdge; i++)
892 {
893 this->fieldOrdinalPolynomialDegree_(i+fieldOrdinalOffset,0) = i+2; // vertex functions are 1st order; edge functions start at order 2
894 this->fieldOrdinalH1PolynomialDegree_(i+fieldOrdinalOffset,0) = i+2; // vertex functions are 1st order; edge functions start at order 2
895 }
896 fieldOrdinalOffset += numFunctionsPerEdge;
897 }
898
899 // **** face functions **** //
900 // one quad face
901 const int numFunctionsPerQuadFace = (polyOrder-1)*(polyOrder-1);
902
903 // following the ESEAS ordering: j increments first
904 for (int j=2; j<=polyOrder_; j++)
905 {
906 for (int i=2; i<=polyOrder_; i++)
907 {
908 this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = std::max(i,j);
909 this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = std::max(i,j);
910 fieldOrdinalOffset++;
911 }
912 }
913
914 const int numFunctionsPerTriFace = ((polyOrder-1)*(polyOrder-2))/2;
915 const int numTriFaces = 4;
916 for (int faceOrdinal=0; faceOrdinal<numTriFaces; faceOrdinal++)
917 {
918 for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
919 {
920 const int totalFaceDofs = (totalPolyOrder-2)*(totalPolyOrder-1)/2;
921 const int totalFaceDofsPrevious = (totalPolyOrder-3)*(totalPolyOrder-2)/2;
922 const int faceDofsForPolyOrder = totalFaceDofs - totalFaceDofsPrevious;
923 for (int i=0; i<faceDofsForPolyOrder; i++)
924 {
925 this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = totalPolyOrder;
926 this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = totalPolyOrder;
927 fieldOrdinalOffset++;
928 }
929 }
930 }
931
932 // **** interior functions **** //
933 const int numFunctionsPerVolume = (polyOrder-1)*(polyOrder-1)*(polyOrder-1);
934 const int numVolumes = 1; // interior
935 for (int volumeOrdinal=0; volumeOrdinal<numVolumes; volumeOrdinal++)
936 {
937 // following the ESEAS ordering: k increments first
938 for (int k=2; k<=polyOrder_; k++)
939 {
940 for (int j=2; j<=polyOrder_; j++)
941 {
942 for (int i=2; i<=polyOrder_; i++)
943 {
944 const int max_ij = std::max(i,j);
945 const int max_ijk = std::max(max_ij,k);
946 this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = max_ijk;
947 this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = max_ijk;
948 fieldOrdinalOffset++;
949 }
950 }
951 }
952 }
953
954 INTREPID2_TEST_FOR_EXCEPTION(fieldOrdinalOffset != this->basisCardinality_, std::invalid_argument, "Internal error: basis enumeration is incorrect");
955
956 // initialize tags
957 {
958 // Intrepid2 vertices:
959 // 0: (-1,-1, 0)
960 // 1: ( 1,-1, 0)
961 // 2: ( 1, 1, 0)
962 // 3: (-1, 1, 0)
963 // 4: ( 0, 0, 1)
964
965 // ESEAS vertices:
966 // 0: ( 0, 0, 0)
967 // 1: ( 1, 0, 0)
968 // 2: ( 1, 1, 0)
969 // 3: ( 0, 1, 0)
970 // 4: ( 0, 0, 1)
971
972 // The edge numbering appears to match between ESEAS and Intrepid2
973
974 // ESEAS numbers pyramid faces differently from Intrepid2
975 // See BlendProjectPyraTF in ESEAS.
976 // See PyramidFaceNodeMap in Shards_BasicTopologies
977 // ESEAS: 0123, 014, 124, 324, 034
978 // Intrepid2: 014, 124, 234, 304, 0321
979
980 const int intrepid2FaceOrdinals[5] {4,0,1,2,3}; // index is the ESEAS face ordinal; value is the intrepid2 ordinal
981
982 const auto & cardinality = this->basisCardinality_;
983
984 // Basis-dependent initializations
985 const ordinal_type tagSize = 4; // size of DoF tag, i.e., number of fields in the tag
986 const ordinal_type posScDim = 0; // position in the tag, counting from 0, of the subcell dim
987 const ordinal_type posScOrd = 1; // position in the tag, counting from 0, of the subcell ordinal
988 const ordinal_type posDfOrd = 2; // position in the tag, counting from 0, of DoF ordinal relative to the subcell
989
990 OrdinalTypeArray1DHost tagView("tag view", cardinality*tagSize);
991 const int vertexDim = 0, edgeDim = 1, faceDim = 2, volumeDim = 3;
992
993 if (defineVertexFunctions) {
994 {
995 int tagNumber = 0;
996 for (int vertexOrdinal=0; vertexOrdinal<numVertices; vertexOrdinal++)
997 {
998 tagView(tagNumber*tagSize+0) = vertexDim; // vertex dimension
999 tagView(tagNumber*tagSize+1) = vertexOrdinal; // vertex id
1000 tagView(tagNumber*tagSize+2) = 0; // local dof id
1001 tagView(tagNumber*tagSize+3) = 1; // total number of dofs at this vertex
1002 tagNumber++;
1003 }
1004 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
1005 {
1006 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerEdge; functionOrdinal++)
1007 {
1008 tagView(tagNumber*tagSize+0) = edgeDim; // edge dimension
1009 tagView(tagNumber*tagSize+1) = edgeOrdinal; // edge id
1010 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
1011 tagView(tagNumber*tagSize+3) = numFunctionsPerEdge; // total number of dofs on this edge
1012 tagNumber++;
1013 }
1014 }
1015 {
1016 // quad face
1017 const int faceOrdinalESEAS = 0;
1018 const int faceOrdinalIntrepid2 = intrepid2FaceOrdinals[faceOrdinalESEAS];
1019
1020 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerQuadFace; functionOrdinal++)
1021 {
1022 tagView(tagNumber*tagSize+0) = faceDim; // face dimension
1023 tagView(tagNumber*tagSize+1) = faceOrdinalIntrepid2; // face id
1024 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
1025 tagView(tagNumber*tagSize+3) = numFunctionsPerQuadFace; // total number of dofs on this face
1026 tagNumber++;
1027 }
1028 }
1029 for (int triFaceOrdinalESEAS=0; triFaceOrdinalESEAS<numTriFaces; triFaceOrdinalESEAS++)
1030 {
1031 const int faceOrdinalESEAS = triFaceOrdinalESEAS + 1;
1032 const int faceOrdinalIntrepid2 = intrepid2FaceOrdinals[faceOrdinalESEAS];
1033 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerTriFace; functionOrdinal++)
1034 {
1035 tagView(tagNumber*tagSize+0) = faceDim; // face dimension
1036 tagView(tagNumber*tagSize+1) = faceOrdinalIntrepid2; // face id
1037 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
1038 tagView(tagNumber*tagSize+3) = numFunctionsPerTriFace; // total number of dofs on this face
1039 tagNumber++;
1040 }
1041 }
1042 for (int volumeOrdinal=0; volumeOrdinal<numVolumes; volumeOrdinal++)
1043 {
1044 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerVolume; functionOrdinal++)
1045 {
1046 tagView(tagNumber*tagSize+0) = volumeDim; // volume dimension
1047 tagView(tagNumber*tagSize+1) = volumeOrdinal; // volume id
1048 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
1049 tagView(tagNumber*tagSize+3) = numFunctionsPerVolume; // total number of dofs in this volume
1050 tagNumber++;
1051 }
1052 }
1053 INTREPID2_TEST_FOR_EXCEPTION(tagNumber != this->basisCardinality_, std::invalid_argument, "Internal error: basis tag enumeration is incorrect");
1054 }
1055 } else {
1056 for (ordinal_type i=0;i<cardinality;++i) {
1057 tagView(i*tagSize+0) = volumeDim; // volume dimension
1058 tagView(i*tagSize+1) = 0; // volume ordinal
1059 tagView(i*tagSize+2) = i; // local dof id
1060 tagView(i*tagSize+3) = cardinality; // total number of dofs on this face
1061 }
1062 }
1063
1064 // Basis-independent function sets tag and enum data in tagToOrdinal_ and ordinalToTag_ arrays:
1065 // tags are constructed on host
1066 this->setOrdinalTagData(this->tagToOrdinal_,
1067 this->ordinalToTag_,
1068 tagView,
1069 this->basisCardinality_,
1070 tagSize,
1071 posScDim,
1072 posScOrd,
1073 posDfOrd);
1074 }
1075 }
1076
1081 const char* getName() const override {
1082 return "Intrepid2_IntegratedLegendreBasis_HGRAD_PYR";
1083 }
1084
1087 virtual bool requireOrientation() const override {
1088 return (this->getDegree() > 2);
1089 }
1090
1091 // since the getValues() below only overrides the FEM variant, we specify that
1092 // we use the base class's getValues(), which implements the FVD variant by throwing an exception.
1093 // (It's an error to use the FVD variant on this basis.)
1095
1114 virtual void getValues( OutputViewType outputValues, const PointViewType inputPoints,
1115 const EOperator operatorType = OPERATOR_VALUE ) const override
1116 {
1117 auto numPoints = inputPoints.extent_int(0);
1118
1120
1121 FunctorType functor(operatorType, outputValues, inputPoints, polyOrder_, defineVertexFunctions);
1122
1123 const int outputVectorSize = getVectorSizeForHierarchicalParallelism<OutputScalar>();
1124 const int pointVectorSize = getVectorSizeForHierarchicalParallelism<PointScalar>();
1125 const int vectorSize = std::max(outputVectorSize,pointVectorSize);
1126 const int teamSize = 1; // because of the way the basis functions are computed, we don't have a second level of parallelism...
1127
1128 auto policy = Kokkos::TeamPolicy<ExecutionSpace>(numPoints,teamSize,vectorSize);
1129 Kokkos::parallel_for("Hierarchical_HGRAD_PYR_Functor", policy, functor);
1130 }
1131
1141 getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override{
1144 using HGRAD_QUAD = Basis_Derived_HGRAD_QUAD<HGRAD_LINE>;
1145 const auto & p = this->basisDegree_;
1146 if(subCellDim == 1) // line basis
1147 {
1148 return Teuchos::rcp(new HGRAD_LINE(p));
1149 }
1150 else if (subCellDim == 2)
1151 {
1152 if (subCellOrd == 0) // quad basis
1153 {
1154 return Teuchos::rcp(new HGRAD_QUAD(p));
1155 }
1156 else // tri basis
1157 {
1158 return Teuchos::rcp(new HGRAD_TRI(p));
1159 }
1160 }
1161 INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Input parameters out of bounds");
1162 }
1163
1169 getHostBasis() const override {
1170 using HostDeviceType = typename Kokkos::HostSpace::device_type;
1172 return Teuchos::rcp( new HostBasisType(polyOrder_, pointType_) );
1173 }
1174 };
1175} // end namespace Intrepid2
1176
1177// do ETI with default (double) type
1179
1180#endif /* Intrepid2_IntegratedLegendreBasis_HGRAD_PYR_h */
Header file for the abstract base class Intrepid2::Basis.
Teuchos::RCP< Basis< DeviceType, OutputType, PointType > > BasisPtr
Basis Pointer.
Implementation of H(grad) basis on the quadrilateral that is templated on H(grad) on the line.
KOKKOS_INLINE_FUNCTION void device_assert(bool val)
H(grad) basis on the line based on integrated Legendre polynomials.
KOKKOS_INLINE_FUNCTION void affinePyramid(Kokkos::Array< PointScalar, 5 > &lambda, Kokkos::Array< Kokkos::Array< PointScalar, 3 >, 5 > &lambdaGrad, Kokkos::Array< Kokkos::Array< PointScalar, 3 >, 2 > &mu, Kokkos::Array< Kokkos::Array< Kokkos::Array< PointScalar, 3 >, 3 >, 2 > &muGrad, Kokkos::Array< Kokkos::Array< PointScalar, 2 >, 3 > &nu, Kokkos::Array< Kokkos::Array< Kokkos::Array< PointScalar, 3 >, 2 >, 3 > &nuGrad, Kokkos::Array< PointScalar, 3 > &coords)
Compute various affine-like coordinates on the pyramid. See Fuentes et al, Appendix E....
KOKKOS_INLINE_FUNCTION void transformFromESEASPyramidGradient(OutputScalar &dx_int2, OutputScalar &dy_int2, OutputScalar &dz_int2, const OutputScalar &dx_eseas, const OutputScalar &dy_eseas, const OutputScalar &dz_eseas)
Transforms gradients computed on the ESEAS pyramid to gradients on the Intrepid2 pyramid.
KOKKOS_INLINE_FUNCTION void transformToESEASPyramid(PointScalar &x_eseas, PointScalar &y_eseas, PointScalar &z_eseas, const PointScalar &x_int2, const PointScalar &y_int2, const PointScalar &z_int2)
Transforms from the Intrepid2 pyramid, centered at the origin with base [-1,1]^2 and height 1,...
H(grad) basis on the triangle based on integrated Legendre polynomials.
Free functions, callable from device code, that implement various polynomials useful in basis definit...
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues(OutputValueViewType outputValues, const OutputValueViewType jacobiValues, double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
Integrated Jacobi values, defined for x in [0,1].
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues_dt(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
t derivative of shifted, scaled integrated Legendre polynomials L_i for i>=1, defined for x in [0,...
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
Integrated Legendre polynomials L_i for i>=1, defined for x in [0,1].
KOKKOS_INLINE_FUNCTION void shiftedScaledLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
Evaluate shifted, scaled Legendre polynomials up to order n at a specified point in [0,...
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues_dt(OutputValueViewType outputValues, const OutputValueViewType jacobiValues, double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
t derivative of shifted, scaled integrated Jacobi polynomials L_i for i>=1, defined for x in [0,...
KOKKOS_INLINE_FUNCTION void shiftedScaledJacobiValues(OutputValueViewType outputValues, double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
Shifted, scaled Jacobi values, defined for x in [0,1].
Header function for Intrepid2::Util class and other utility functions.
constexpr int getVectorSizeForHierarchicalParallelism()
Returns a vector size to be used for the provided Scalar type in the context of hierarchically-parall...
KOKKOS_INLINE_FUNCTION constexpr unsigned getScalarDimensionForView(const ViewType &view)
Returns the size of the Scalar dimension for the View. This is 0 for non-AD types....
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
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)
Kokkos::DynRankView< scalarType, Kokkos::LayoutStride, DeviceType > ScalarViewType
Kokkos::View< ordinal_type **, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray2DHost
virtual void getValues(OutputViewType, const PointViewType, const EOperator=OPERATOR_VALUE) const
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line.
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line.
virtual bool requireOrientation() const override
True if orientation is required.
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...
virtual void getValues(OutputViewType outputValues, const PointViewType inputPoints, const EOperator operatorType=OPERATOR_VALUE) const override
Evaluation of a FEM basis on a reference cell.
IntegratedLegendreBasis_HGRAD_PYR(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Constructor.
BasisPtr< DeviceType, OutputScalar, PointScalar > getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override
returns the basis associated to a subCell.
Basis defining integrated Legendre basis on the line, a polynomial subspace of H(grad) on the line: e...
Functor for computing values for the IntegratedLegendreBasis_HGRAD_PYR class.