Intrepid2
Intrepid2_IntegratedLegendreBasis_HGRAD_TET.hpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// Intrepid2 Package
4//
5// Copyright 2007 NTESS and the Intrepid2 contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
15#ifndef Intrepid2_IntegratedLegendreBasis_HGRAD_TET_h
16#define Intrepid2_IntegratedLegendreBasis_HGRAD_TET_h
17
18#include <Kokkos_DynRankView.hpp>
19
20#include <Intrepid2_config.h>
21
22#include "Intrepid2_Basis.hpp"
26#include "Intrepid2_Utils.hpp"
27
28namespace Intrepid2
29{
35 template<class DeviceType, class OutputScalar, class PointScalar,
36 class OutputFieldType, class InputPointsType>
38 {
39 using ExecutionSpace = typename DeviceType::execution_space;
40 using ScratchSpace = typename ExecutionSpace::scratch_memory_space;
41 using OutputScratchView = Kokkos::View<OutputScalar*,ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
42 using OutputScratchView2D = Kokkos::View<OutputScalar**,ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
43 using PointScratchView = Kokkos::View<PointScalar*, ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
44
45 using TeamPolicy = Kokkos::TeamPolicy<ExecutionSpace>;
46 using TeamMember = typename TeamPolicy::member_type;
47
48 EOperator opType_;
49
50 OutputFieldType output_; // F,P
51 InputPointsType inputPoints_; // P,D
52
53 int polyOrder_;
54 bool defineVertexFunctions_;
55 int numFields_, numPoints_;
56
57 size_t fad_size_output_;
58
59 static const int numVertices = 4;
60 static const int numEdges = 6;
61 // the following ordering of the edges matches that used by ESEAS
62 const int edge_start_[numEdges] = {0,1,0,0,1,2}; // edge i is from edge_start_[i] to edge_end_[i]
63 const int edge_end_[numEdges] = {1,2,2,3,3,3}; // edge i is from edge_start_[i] to edge_end_[i]
64
65 static const int numFaces = 4;
66 const int face_vertex_0[numFaces] = {0,0,1,0}; // faces are abc where 0 ≤ a < b < c ≤ 3
67 const int face_vertex_1[numFaces] = {1,1,2,2};
68 const int face_vertex_2[numFaces] = {2,3,3,3};
69
70 // this allows us to look up the edge ordinal of the first edge of a face
71 // this is useful because face functions are defined using edge basis functions of the first edge of the face
72 const int face_ordinal_of_first_edge[numFaces] = {0,0,1,2};
73
74 Hierarchical_HGRAD_TET_Functor(EOperator opType, OutputFieldType output, InputPointsType inputPoints,
75 int polyOrder, bool defineVertexFunctions)
76 : opType_(opType), output_(output), inputPoints_(inputPoints),
77 polyOrder_(polyOrder), defineVertexFunctions_(defineVertexFunctions),
78 fad_size_output_(getScalarDimensionForView(output))
79 {
80 numFields_ = output.extent_int(0);
81 numPoints_ = output.extent_int(1);
82 INTREPID2_TEST_FOR_EXCEPTION(numPoints_ != inputPoints.extent_int(0), std::invalid_argument, "point counts need to match!");
83 INTREPID2_TEST_FOR_EXCEPTION(numFields_ != (polyOrder_+1)*(polyOrder_+2)*(polyOrder_+3)/6, std::invalid_argument, "output field size does not match basis cardinality");
84 }
85
86 KOKKOS_INLINE_FUNCTION
87 void operator()( const TeamMember & teamMember ) const
88 {
89 const int numFaceBasisFunctionsPerFace = (polyOrder_-2) * (polyOrder_-1) / 2;
90 const int numInteriorBasisFunctions = (polyOrder_-3) * (polyOrder_-2) * (polyOrder_-1) / 6;
91
92 auto pointOrdinal = teamMember.league_rank();
93 OutputScratchView legendre_values1_at_point, legendre_values2_at_point;
94 OutputScratchView2D jacobi_values1_at_point, jacobi_values2_at_point, jacobi_values3_at_point;
95 const int numAlphaValues = (polyOrder_-1 > 1) ? (polyOrder_-1) : 1; // make numAlphaValues at least 1 so we can avoid zero-extent allocations…
96 if (fad_size_output_ > 0) {
97 legendre_values1_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
98 legendre_values2_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
99 jacobi_values1_at_point = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
100 jacobi_values2_at_point = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
101 jacobi_values3_at_point = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1, fad_size_output_);
102 }
103 else {
104 legendre_values1_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
105 legendre_values2_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
106 jacobi_values1_at_point = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
107 jacobi_values2_at_point = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
108 jacobi_values3_at_point = OutputScratchView2D(teamMember.team_shmem(), numAlphaValues, polyOrder_ + 1);
109 }
110
111 const auto & x = inputPoints_(pointOrdinal,0);
112 const auto & y = inputPoints_(pointOrdinal,1);
113 const auto & z = inputPoints_(pointOrdinal,2);
114
115 // write as barycentric coordinates:
116 const PointScalar lambda[numVertices] = {1. - x - y - z, x, y, z};
117 const PointScalar lambda_dx[numVertices] = {-1., 1., 0., 0.};
118 const PointScalar lambda_dy[numVertices] = {-1., 0., 1., 0.};
119 const PointScalar lambda_dz[numVertices] = {-1., 0., 0., 1.};
120
121 const int num1DEdgeFunctions = polyOrder_ - 1;
122
123 switch (opType_)
124 {
125 case OPERATOR_VALUE:
126 {
127 // vertex functions come first, according to vertex ordering: (0,0,0), (1,0,0), (0,1,0), (0,0,1)
128 for (int vertexOrdinal=0; vertexOrdinal<numVertices; vertexOrdinal++)
129 {
130 output_(vertexOrdinal,pointOrdinal) = lambda[vertexOrdinal];
131 }
132 if (!defineVertexFunctions_)
133 {
134 // "DG" basis case
135 // here, we overwrite the first vertex function with 1:
136 output_(0,pointOrdinal) = 1.0;
137 }
138
139 // edge functions
140 int fieldOrdinalOffset = numVertices;
141 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
142 {
143 const auto & s0 = lambda[edge_start_[edgeOrdinal]];
144 const auto & s1 = lambda[ edge_end_[edgeOrdinal]];
145
146 Polynomials::shiftedScaledIntegratedLegendreValues(legendre_values1_at_point, polyOrder_, PointScalar(s1), PointScalar(s0+s1));
147 for (int edgeFunctionOrdinal=0; edgeFunctionOrdinal<num1DEdgeFunctions; edgeFunctionOrdinal++)
148 {
149 // the first two integrated legendre functions are essentially the vertex functions; hence the +2 on on the RHS here:
150 output_(edgeFunctionOrdinal+fieldOrdinalOffset,pointOrdinal) = legendre_values1_at_point(edgeFunctionOrdinal+2);
151 }
152 fieldOrdinalOffset += num1DEdgeFunctions;
153 }
154 /*
155 Face functions for face abc are the product of edge functions on their ab edge
156 and a Jacobi polynomial [L^2i_j](s0+s1,s2) = L^2i_j(s2;s0+s1+s2)
157 */
158 for (int faceOrdinal=0; faceOrdinal<numFaces; faceOrdinal++)
159 {
160 const auto & s0 = lambda[face_vertex_0[faceOrdinal]];
161 const auto & s1 = lambda[face_vertex_1[faceOrdinal]];
162 const auto & s2 = lambda[face_vertex_2[faceOrdinal]];
163 const PointScalar jacobiScaling = s0 + s1 + s2;
164
165 // compute integrated Jacobi values for each desired value of alpha
166 for (int n=2; n<=polyOrder_; n++)
167 {
168 const double alpha = n*2;
169 const int alphaOrdinal = n-2;
170 using Kokkos::subview;
171 using Kokkos::ALL;
172 auto jacobi_alpha = subview(jacobi_values1_at_point, alphaOrdinal, ALL);
173 Polynomials::shiftedScaledIntegratedJacobiValues(jacobi_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
174 }
175
176 const int edgeOrdinal = face_ordinal_of_first_edge[faceOrdinal];
177 int localFaceBasisOrdinal = 0;
178 for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
179 {
180 for (int i=2; i<totalPolyOrder; i++)
181 {
182 const int edgeBasisOrdinal = edgeOrdinal*num1DEdgeFunctions + i-2 + numVertices;
183 const auto & edgeValue = output_(edgeBasisOrdinal,pointOrdinal);
184 const int alphaOrdinal = i-2;
185
186 const int j = totalPolyOrder - i;
187 const auto & jacobiValue = jacobi_values1_at_point(alphaOrdinal,j);
188 const int fieldOrdinal = fieldOrdinalOffset + localFaceBasisOrdinal;
189 output_(fieldOrdinal,pointOrdinal) = edgeValue * jacobiValue;
190
191 localFaceBasisOrdinal++;
192 }
193 }
194 fieldOrdinalOffset += numFaceBasisFunctionsPerFace;
195 }
196 // interior functions
197 // compute integrated Jacobi values for each desired value of alpha
198 for (int n=3; n<=polyOrder_; n++)
199 {
200 const double alpha = n*2;
201 const double jacobiScaling = 1.0;
202 const int alphaOrdinal = n-3;
203 using Kokkos::subview;
204 using Kokkos::ALL;
205 auto jacobi_alpha = subview(jacobi_values1_at_point, alphaOrdinal, ALL);
206 Polynomials::shiftedScaledIntegratedJacobiValues(jacobi_alpha, alpha, polyOrder_-3, lambda[3], jacobiScaling);
207 }
208 const int min_i = 2;
209 const int min_j = 1;
210 const int min_k = 1;
211 const int min_ij = min_i + min_j;
212 const int min_ijk = min_ij + min_k;
213 int localInteriorBasisOrdinal = 0;
214 for (int totalPolyOrder_ijk=min_ijk; totalPolyOrder_ijk <= polyOrder_; totalPolyOrder_ijk++)
215 {
216 int localFaceBasisOrdinal = 0;
217 for (int totalPolyOrder_ij=min_ij; totalPolyOrder_ij <= totalPolyOrder_ijk-min_j; totalPolyOrder_ij++)
218 {
219 for (int i=2; i <= totalPolyOrder_ij-min_j; i++)
220 {
221 const int j = totalPolyOrder_ij - i;
222 const int k = totalPolyOrder_ijk - totalPolyOrder_ij;
223 const int faceBasisOrdinal = numEdges*num1DEdgeFunctions + numVertices + localFaceBasisOrdinal;
224 const auto & faceValue = output_(faceBasisOrdinal,pointOrdinal);
225 const int alphaOrdinal = (i+j)-3;
226 localFaceBasisOrdinal++;
227
228 const int fieldOrdinal = fieldOrdinalOffset + localInteriorBasisOrdinal;
229 const auto & jacobiValue = jacobi_values1_at_point(alphaOrdinal,k);
230 output_(fieldOrdinal,pointOrdinal) = faceValue * jacobiValue;
231 localInteriorBasisOrdinal++;
232 } // end i loop
233 } // end totalPolyOrder_ij loop
234 } // end totalPolyOrder_ijk loop
235 fieldOrdinalOffset += numInteriorBasisFunctions;
236 } // end OPERATOR_VALUE
237 break;
238 case OPERATOR_GRAD:
239 case OPERATOR_D1:
240 {
241 // vertex functions
242 if (defineVertexFunctions_)
243 {
244 // standard, "CG" basis case
245 // first vertex function is 1-x-y-z
246 output_(0,pointOrdinal,0) = -1.0;
247 output_(0,pointOrdinal,1) = -1.0;
248 output_(0,pointOrdinal,2) = -1.0;
249 }
250 else
251 {
252 // "DG" basis case
253 // here, the first "vertex" function is 1, so the derivative is 0:
254 output_(0,pointOrdinal,0) = 0.0;
255 output_(0,pointOrdinal,1) = 0.0;
256 output_(0,pointOrdinal,2) = 0.0;
257 }
258 // second vertex function is x
259 output_(1,pointOrdinal,0) = 1.0;
260 output_(1,pointOrdinal,1) = 0.0;
261 output_(1,pointOrdinal,2) = 0.0;
262 // third vertex function is y
263 output_(2,pointOrdinal,0) = 0.0;
264 output_(2,pointOrdinal,1) = 1.0;
265 output_(2,pointOrdinal,2) = 0.0;
266 // fourth vertex function is z
267 output_(3,pointOrdinal,0) = 0.0;
268 output_(3,pointOrdinal,1) = 0.0;
269 output_(3,pointOrdinal,2) = 1.0;
270
271 // edge functions
272 int fieldOrdinalOffset = numVertices;
273 /*
274 Per Fuentes et al. (see Appendix E.1, E.2), the edge functions, defined for i ≥ 2, are
275 [L_i](s0,s1) = L_i(s1; s0+s1)
276 and have gradients:
277 grad [L_i](s0,s1) = [P_{i-1}](s0,s1) grad s1 + [R_{i-1}](s0,s1) grad (s0 + s1)
278 where
279 [R_{i-1}](s0,s1) = R_{i-1}(s1; s0+s1) = d/dt L_{i}(s0; s0+s1)
280 The P_i we have implemented in shiftedScaledLegendreValues, while d/dt L_{i+1} is
281 implemented in shiftedScaledIntegratedLegendreValues_dt.
282 */
283 // rename the scratch memory to match our usage here:
284 auto & P_i_minus_1 = legendre_values1_at_point;
285 auto & L_i_dt = legendre_values2_at_point;
286 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
287 {
288 const auto & s0 = lambda[edge_start_[edgeOrdinal]];
289 const auto & s1 = lambda[ edge_end_[edgeOrdinal]];
290
291 const auto & s0_dx = lambda_dx[edge_start_[edgeOrdinal]];
292 const auto & s0_dy = lambda_dy[edge_start_[edgeOrdinal]];
293 const auto & s0_dz = lambda_dz[edge_start_[edgeOrdinal]];
294 const auto & s1_dx = lambda_dx[ edge_end_[edgeOrdinal]];
295 const auto & s1_dy = lambda_dy[ edge_end_[edgeOrdinal]];
296 const auto & s1_dz = lambda_dz[ edge_end_[edgeOrdinal]];
297
298 Polynomials::shiftedScaledLegendreValues (P_i_minus_1, polyOrder_-1, PointScalar(s1), PointScalar(s0+s1));
299 Polynomials::shiftedScaledIntegratedLegendreValues_dt(L_i_dt, polyOrder_, PointScalar(s1), PointScalar(s0+s1));
300 for (int edgeFunctionOrdinal=0; edgeFunctionOrdinal<num1DEdgeFunctions; edgeFunctionOrdinal++)
301 {
302 // the first two (integrated) Legendre functions are essentially the vertex functions; hence the +2 here:
303 const int i = edgeFunctionOrdinal+2;
304 output_(edgeFunctionOrdinal+fieldOrdinalOffset,pointOrdinal,0) = P_i_minus_1(i-1) * s1_dx + L_i_dt(i) * (s1_dx + s0_dx);
305 output_(edgeFunctionOrdinal+fieldOrdinalOffset,pointOrdinal,1) = P_i_minus_1(i-1) * s1_dy + L_i_dt(i) * (s1_dy + s0_dy);
306 output_(edgeFunctionOrdinal+fieldOrdinalOffset,pointOrdinal,2) = P_i_minus_1(i-1) * s1_dz + L_i_dt(i) * (s1_dz + s0_dz);
307 }
308 fieldOrdinalOffset += num1DEdgeFunctions;
309 }
310
311 /*
312 Fuentes et al give the face functions as phi_{ij}, with gradient:
313 grad phi_{ij}(s0,s1,s2) = [L^{2i}_j](s0+s1,s2) grad [L_i](s0,s1) + [L_i](s0,s1) grad [L^{2i}_j](s0+s1,s2)
314 where:
315 - grad [L_i](s0,s1) is the edge function gradient we computed above
316 - [L_i](s0,s1) is the edge function which we have implemented above (in OPERATOR_VALUE)
317 - L^{2i}_j is a Jacobi polynomial with:
318 [L^{2i}_j](s0,s1) = L^{2i}_j(s1;s0+s1)
319 and the gradient for j ≥ 1 is
320 grad [L^{2i}_j](s0,s1) = [P^{2i}_{j-1}](s0,s1) grad s1 + [R^{2i}_{j-1}(s0,s1)] grad (s0 + s1)
321 Here,
322 [P^{2i}_{j-1}](s0,s1) = P^{2i}_{j-1}(s1,s0+s1)
323 and
324 [R^{2i}_{j-1}(s0,s1)] = d/dt L^{2i}_j(s1,s0+s1)
325 We have implemented P^{alpha}_{j} as shiftedScaledJacobiValues,
326 and d/dt L^{alpha}_{j} as shiftedScaledIntegratedJacobiValues_dt.
327 */
328 // rename the scratch memory to match our usage here:
329 auto & L_i = legendre_values2_at_point;
330 auto & L_2i_j_dt = jacobi_values1_at_point;
331 auto & L_2i_j = jacobi_values2_at_point;
332 auto & P_2i_j_minus_1 = jacobi_values3_at_point;
333
334 for (int faceOrdinal=0; faceOrdinal<numFaces; faceOrdinal++)
335 {
336 const auto & s0 = lambda[face_vertex_0[faceOrdinal]];
337 const auto & s1 = lambda[face_vertex_1[faceOrdinal]];
338 const auto & s2 = lambda[face_vertex_2[faceOrdinal]];
339 Polynomials::shiftedScaledIntegratedLegendreValues(L_i, polyOrder_, s1, s0+s1);
340
341 const PointScalar jacobiScaling = s0 + s1 + s2;
342
343 // compute integrated Jacobi values for each desired value of alpha
344 for (int n=2; n<=polyOrder_; n++)
345 {
346 const double alpha = n*2;
347 const int alphaOrdinal = n-2;
348 using Kokkos::subview;
349 using Kokkos::ALL;
350 auto L_2i_j_dt_alpha = subview(L_2i_j_dt, alphaOrdinal, ALL);
351 auto L_2i_j_alpha = subview(L_2i_j, alphaOrdinal, ALL);
352 auto P_2i_j_minus_1_alpha = subview(P_2i_j_minus_1, alphaOrdinal, ALL);
353 Polynomials::shiftedScaledIntegratedJacobiValues_dt(L_2i_j_dt_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
354 Polynomials::shiftedScaledIntegratedJacobiValues (L_2i_j_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
355 Polynomials::shiftedScaledJacobiValues(P_2i_j_minus_1_alpha, alpha, polyOrder_-1, s2, jacobiScaling);
356 }
357
358 const int edgeOrdinal = face_ordinal_of_first_edge[faceOrdinal];
359 int localFaceOrdinal = 0;
360 for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
361 {
362 for (int i=2; i<totalPolyOrder; i++)
363 {
364 const int edgeBasisOrdinal = edgeOrdinal*num1DEdgeFunctions + i-2 + numVertices;
365 const auto & grad_L_i_dx = output_(edgeBasisOrdinal,pointOrdinal,0);
366 const auto & grad_L_i_dy = output_(edgeBasisOrdinal,pointOrdinal,1);
367 const auto & grad_L_i_dz = output_(edgeBasisOrdinal,pointOrdinal,2);
368
369 const int alphaOrdinal = i-2;
370
371 const auto & s0_dx = lambda_dx[face_vertex_0[faceOrdinal]];
372 const auto & s0_dy = lambda_dy[face_vertex_0[faceOrdinal]];
373 const auto & s0_dz = lambda_dz[face_vertex_0[faceOrdinal]];
374 const auto & s1_dx = lambda_dx[face_vertex_1[faceOrdinal]];
375 const auto & s1_dy = lambda_dy[face_vertex_1[faceOrdinal]];
376 const auto & s1_dz = lambda_dz[face_vertex_1[faceOrdinal]];
377 const auto & s2_dx = lambda_dx[face_vertex_2[faceOrdinal]];
378 const auto & s2_dy = lambda_dy[face_vertex_2[faceOrdinal]];
379 const auto & s2_dz = lambda_dz[face_vertex_2[faceOrdinal]];
380
381 int j = totalPolyOrder - i;
382
383 // put references to the entries of interest in like-named variables with lowercase first letters
384 auto & l_2i_j = L_2i_j(alphaOrdinal,j);
385 auto & l_i = L_i(i);
386 auto & l_2i_j_dt = L_2i_j_dt(alphaOrdinal,j);
387 auto & p_2i_j_minus_1 = P_2i_j_minus_1(alphaOrdinal,j-1);
388
389 const OutputScalar basisValue_dx = l_2i_j * grad_L_i_dx + l_i * (p_2i_j_minus_1 * s2_dx + l_2i_j_dt * (s0_dx + s1_dx + s2_dx));
390 const OutputScalar basisValue_dy = l_2i_j * grad_L_i_dy + l_i * (p_2i_j_minus_1 * s2_dy + l_2i_j_dt * (s0_dy + s1_dy + s2_dy));
391 const OutputScalar basisValue_dz = l_2i_j * grad_L_i_dz + l_i * (p_2i_j_minus_1 * s2_dz + l_2i_j_dt * (s0_dz + s1_dz + s2_dz));
392
393 const int fieldOrdinal = fieldOrdinalOffset + localFaceOrdinal;
394
395 output_(fieldOrdinal,pointOrdinal,0) = basisValue_dx;
396 output_(fieldOrdinal,pointOrdinal,1) = basisValue_dy;
397 output_(fieldOrdinal,pointOrdinal,2) = basisValue_dz;
398
399 localFaceOrdinal++;
400 }
401 }
402 fieldOrdinalOffset += numFaceBasisFunctionsPerFace;
403 }
404 // interior functions
405 /*
406 Per Fuentes et al. (see Appendix E.1, E.2), the interior functions, defined for i ≥ 2, are
407 phi_ij(lambda_012) [L^{2(i+j)}_k](1-lambda_3,lambda_3) = phi_ij(lambda_012) L^{2(i+j)}_k (lambda_3; 1)
408 and have gradients:
409 L^{2(i+j)}_k (lambda_3; 1) grad (phi_ij(lambda_012)) + phi_ij(lambda_012) grad (L^{2(i+j)}_k (lambda_3; 1))
410 where:
411 - phi_ij(lambda_012) is the (i,j) basis function on face 012,
412 - L^{alpha}_j(t0; t1) is the jth integrated Jacobi polynomial
413 and the gradient of the integrated Jacobi polynomial can be computed:
414 - grad L^{alpha}_j(t0; t1) = P^{alpha}_{j-1} (t0;t1) grad t0 + R^{alpha}_{j-1}(t0,t1) grad t1
415 Here, t1=1, so this simplifies to:
416 - grad L^{alpha}_j(t0; t1) = P^{alpha}_{j-1} (t0;t1) grad t0
417
418 The P_i we have implemented in shiftedScaledJacobiValues.
419 */
420 // rename the scratch memory to match our usage here:
421 auto & L_alpha = jacobi_values1_at_point;
422 auto & P_alpha = jacobi_values2_at_point;
423
424 // precompute values used in face ordinal 0:
425 {
426 const auto & s0 = lambda[0];
427 const auto & s1 = lambda[1];
428 const auto & s2 = lambda[2];
429 // Legendre:
430 Polynomials::shiftedScaledIntegratedLegendreValues(legendre_values1_at_point, polyOrder_, PointScalar(s1), PointScalar(s0+s1));
431
432 // Jacobi for each desired alpha value:
433 const PointScalar jacobiScaling = s0 + s1 + s2;
434 for (int n=2; n<=polyOrder_; n++)
435 {
436 const double alpha = n*2;
437 const int alphaOrdinal = n-2;
438 using Kokkos::subview;
439 using Kokkos::ALL;
440 auto jacobi_alpha = subview(jacobi_values3_at_point, alphaOrdinal, ALL);
441 Polynomials::shiftedScaledIntegratedJacobiValues(jacobi_alpha, alpha, polyOrder_-2, s2, jacobiScaling);
442 }
443 }
444
445 // interior
446 for (int n=3; n<=polyOrder_; n++)
447 {
448 const double alpha = n*2;
449 const double jacobiScaling = 1.0;
450 const int alphaOrdinal = n-3;
451 using Kokkos::subview;
452 using Kokkos::ALL;
453
454 // values for interior functions:
455 auto L = subview(L_alpha, alphaOrdinal, ALL);
456 auto P = subview(P_alpha, alphaOrdinal, ALL);
457 Polynomials::shiftedScaledIntegratedJacobiValues(L, alpha, polyOrder_-3, lambda[3], jacobiScaling);
458 Polynomials::shiftedScaledJacobiValues (P, alpha, polyOrder_-3, lambda[3], jacobiScaling);
459 }
460
461 const int min_i = 2;
462 const int min_j = 1;
463 const int min_k = 1;
464 const int min_ij = min_i + min_j;
465 const int min_ijk = min_ij + min_k;
466 int localInteriorBasisOrdinal = 0;
467 for (int totalPolyOrder_ijk=min_ijk; totalPolyOrder_ijk <= polyOrder_; totalPolyOrder_ijk++)
468 {
469 int localFaceBasisOrdinal = 0;
470 for (int totalPolyOrder_ij=min_ij; totalPolyOrder_ij <= totalPolyOrder_ijk-min_j; totalPolyOrder_ij++)
471 {
472 for (int i=2; i <= totalPolyOrder_ij-min_j; i++)
473 {
474 const int j = totalPolyOrder_ij - i;
475 const int k = totalPolyOrder_ijk - totalPolyOrder_ij;
476 // interior functions use basis values belonging to the first face, 012
477 const int faceBasisOrdinal = numEdges*num1DEdgeFunctions + numVertices + localFaceBasisOrdinal;
478
479 const auto & faceValue_dx = output_(faceBasisOrdinal,pointOrdinal,0);
480 const auto & faceValue_dy = output_(faceBasisOrdinal,pointOrdinal,1);
481 const auto & faceValue_dz = output_(faceBasisOrdinal,pointOrdinal,2);
482
483 // determine faceValue (on face 0)
484 OutputScalar faceValue;
485 {
486 const auto & edgeValue = legendre_values1_at_point(i);
487 const int alphaOrdinal = i-2;
488 const auto & jacobiValue = jacobi_values3_at_point(alphaOrdinal,j);
489 faceValue = edgeValue * jacobiValue;
490 }
491 localFaceBasisOrdinal++;
492
493 const int alphaOrdinal = (i+j)-3;
494
495 const int fieldOrdinal = fieldOrdinalOffset + localInteriorBasisOrdinal;
496 const auto & integratedJacobiValue = L_alpha(alphaOrdinal,k);
497 const auto & jacobiValue = P_alpha(alphaOrdinal,k-1);
498 output_(fieldOrdinal,pointOrdinal,0) = integratedJacobiValue * faceValue_dx + faceValue * jacobiValue * lambda_dx[3];
499 output_(fieldOrdinal,pointOrdinal,1) = integratedJacobiValue * faceValue_dy + faceValue * jacobiValue * lambda_dy[3];
500 output_(fieldOrdinal,pointOrdinal,2) = integratedJacobiValue * faceValue_dz + faceValue * jacobiValue * lambda_dz[3];
501
502 localInteriorBasisOrdinal++;
503 } // end i loop
504 } // end totalPolyOrder_ij loop
505 } // end totalPolyOrder_ijk loop
506 fieldOrdinalOffset += numInteriorBasisFunctions;
507 }
508 break;
509 case OPERATOR_D2:
510 case OPERATOR_D3:
511 case OPERATOR_D4:
512 case OPERATOR_D5:
513 case OPERATOR_D6:
514 case OPERATOR_D7:
515 case OPERATOR_D8:
516 case OPERATOR_D9:
517 case OPERATOR_D10:
518 INTREPID2_TEST_FOR_ABORT( true,
519 ">>> ERROR: (Intrepid2::Basis_HGRAD_TET_Cn_FEM_ORTH::OrthPolynomialTri) Computing of second and higher-order derivatives is not currently supported");
520 default:
521 // unsupported operator type
522 device_assert(false);
523 }
524 }
525
526 // Provide the shared memory capacity.
527 // This function takes the team_size as an argument,
528 // which allows team_size-dependent allocations.
529 size_t team_shmem_size (int team_size) const
530 {
531 // we will use shared memory to create a fast buffer for basis computations
532 // for the (integrated) Legendre computations, we just need p+1 values stored
533 // for the (integrated) Jacobi computations, though, we want (p+1)*(# alpha values)
534 // 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.
535 // We can have up to 3 of the (integrated) Jacobi values needed at once.
536 const int numAlphaValues = std::max(polyOrder_-1, 1); // make it at least 1 so we can avoid zero-extent ranks…
537 size_t shmem_size = 0;
538 if (fad_size_output_ > 0)
539 {
540 // Legendre:
541 shmem_size += 2 * OutputScratchView::shmem_size(polyOrder_ + 1, fad_size_output_);
542 // Jacobi:
543 shmem_size += 3 * OutputScratchView2D::shmem_size(numAlphaValues, polyOrder_ + 1, fad_size_output_);
544 }
545 else
546 {
547 // Legendre:
548 shmem_size += 2 * OutputScratchView::shmem_size(polyOrder_ + 1);
549 // Jacobi:
550 shmem_size += 3 * OutputScratchView2D::shmem_size(numAlphaValues, polyOrder_ + 1);
551 }
552
553 return shmem_size;
554 }
555 };
556
574 template<typename DeviceType,
575 typename OutputScalar = double,
576 typename PointScalar = double,
577 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.
579 : public Basis<DeviceType,OutputScalar,PointScalar>
580 {
581 public:
583
586
587 using typename BasisBase::OutputViewType;
588 using typename BasisBase::PointViewType;
589 using typename BasisBase::ScalarViewType;
590
591 using typename BasisBase::ExecutionSpace;
592
593 protected:
594 int polyOrder_; // the maximum order of the polynomial
595 EPointType pointType_;
596 public:
607 IntegratedLegendreBasis_HGRAD_TET(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
608 :
609 polyOrder_(polyOrder),
610 pointType_(pointType)
611 {
612 INTREPID2_TEST_FOR_EXCEPTION(pointType!=POINTTYPE_DEFAULT,std::invalid_argument,"PointType not supported");
613 this->basisCardinality_ = ((polyOrder+1) * (polyOrder+2) * (polyOrder+3)) / 6;
614 this->basisDegree_ = polyOrder;
615 this->basisCellTopologyKey_ = shards::Tetrahedron<>::key;
616 this->basisType_ = BASIS_FEM_HIERARCHICAL;
617 this->basisCoordinates_ = COORDINATES_CARTESIAN;
618 this->functionSpace_ = FUNCTION_SPACE_HGRAD;
619
620 const int degreeLength = 1;
621 this->fieldOrdinalPolynomialDegree_ = OrdinalTypeArray2DHost("Integrated Legendre H(grad) tetrahedron polynomial degree lookup", this->basisCardinality_, degreeLength);
622 this->fieldOrdinalH1PolynomialDegree_ = OrdinalTypeArray2DHost("Integrated Legendre H(grad) tetrahedron polynomial H^1 degree lookup", this->basisCardinality_, degreeLength);
623
624 int fieldOrdinalOffset = 0;
625 // **** vertex functions **** //
626 const int numVertices = this->getBaseCellTopology().getVertexCount();
627 const int numFunctionsPerVertex = 1;
628 const int numVertexFunctions = numVertices * numFunctionsPerVertex;
629 for (int i=0; i<numVertexFunctions; i++)
630 {
631 // for H(grad) on tetrahedron, if defineVertexFunctions is false, first four basis members are linear
632 // if not, then the only difference is that the first member is constant
633 this->fieldOrdinalPolynomialDegree_ (i,0) = 1;
634 this->fieldOrdinalH1PolynomialDegree_(i,0) = 1;
635 }
636 if (!defineVertexFunctions)
637 {
638 this->fieldOrdinalPolynomialDegree_ (0,0) = 0;
639 this->fieldOrdinalH1PolynomialDegree_(0,0) = 0;
640 }
641 fieldOrdinalOffset += numVertexFunctions;
642
643 // **** edge functions **** //
644 const int numFunctionsPerEdge = polyOrder - 1; // bubble functions: all but the vertices
645 const int numEdges = this->getBaseCellTopology().getEdgeCount();
646 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
647 {
648 for (int i=0; i<numFunctionsPerEdge; i++)
649 {
650 this->fieldOrdinalPolynomialDegree_(i+fieldOrdinalOffset,0) = i+2; // vertex functions are 1st order; edge functions start at order 2
651 this->fieldOrdinalH1PolynomialDegree_(i+fieldOrdinalOffset,0) = i+2; // vertex functions are 1st order; edge functions start at order 2
652 }
653 fieldOrdinalOffset += numFunctionsPerEdge;
654 }
655
656 // **** face functions **** //
657 const int numFunctionsPerFace = ((polyOrder-1)*(polyOrder-2))/2;
658 const int numFaces = 4;
659 for (int faceOrdinal=0; faceOrdinal<numFaces; faceOrdinal++)
660 {
661 for (int totalPolyOrder=3; totalPolyOrder<=polyOrder_; totalPolyOrder++)
662 {
663 const int totalFaceDofs = (totalPolyOrder-2)*(totalPolyOrder-1)/2;
664 const int totalFaceDofsPrevious = (totalPolyOrder-3)*(totalPolyOrder-2)/2;
665 const int faceDofsForPolyOrder = totalFaceDofs - totalFaceDofsPrevious;
666 for (int i=0; i<faceDofsForPolyOrder; i++)
667 {
668 this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = totalPolyOrder;
669 this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = totalPolyOrder;
670 fieldOrdinalOffset++;
671 }
672 }
673 }
674
675 // **** interior functions **** //
676 const int numFunctionsPerVolume = ((polyOrder-1)*(polyOrder-2)*(polyOrder-3))/6;
677 const int numVolumes = 1; // interior
678 for (int volumeOrdinal=0; volumeOrdinal<numVolumes; volumeOrdinal++)
679 {
680 for (int totalPolyOrder=4; totalPolyOrder<=polyOrder_; totalPolyOrder++)
681 {
682 const int totalInteriorDofs = (totalPolyOrder-3)*(totalPolyOrder-2)*(totalPolyOrder-1)/6;
683 const int totalInteriorDofsPrevious = (totalPolyOrder-4)*(totalPolyOrder-3)*(totalPolyOrder-2)/6;
684 const int interiorDofsForPolyOrder = totalInteriorDofs - totalInteriorDofsPrevious;
685
686 for (int i=0; i<interiorDofsForPolyOrder; i++)
687 {
688 this->fieldOrdinalPolynomialDegree_ (fieldOrdinalOffset,0) = totalPolyOrder;
689 this->fieldOrdinalH1PolynomialDegree_(fieldOrdinalOffset,0) = totalPolyOrder;
690 fieldOrdinalOffset++;
691 }
692 }
693 }
694
695 INTREPID2_TEST_FOR_EXCEPTION(fieldOrdinalOffset != this->basisCardinality_, std::invalid_argument, "Internal error: basis enumeration is incorrect");
696
697 // initialize tags
698 {
699 // ESEAS numbers tetrahedron faces differently from Intrepid2
700 // ESEAS: 012, 013, 123, 023
701 // Intrepid2: 013, 123, 032, 021
702 const int intrepid2FaceOrdinals[4] {3,0,1,2}; // index is the ESEAS face ordinal; value is the intrepid2 ordinal
703
704 const auto & cardinality = this->basisCardinality_;
705
706 // Basis-dependent initializations
707 const ordinal_type tagSize = 4; // size of DoF tag, i.e., number of fields in the tag
708 const ordinal_type posScDim = 0; // position in the tag, counting from 0, of the subcell dim
709 const ordinal_type posScOrd = 1; // position in the tag, counting from 0, of the subcell ordinal
710 const ordinal_type posDfOrd = 2; // position in the tag, counting from 0, of DoF ordinal relative to the subcell
711
712 OrdinalTypeArray1DHost tagView("tag view", cardinality*tagSize);
713 const int vertexDim = 0, edgeDim = 1, faceDim = 2, volumeDim = 3;
714
715 if (defineVertexFunctions) {
716 {
717 int tagNumber = 0;
718 for (int vertexOrdinal=0; vertexOrdinal<numVertices; vertexOrdinal++)
719 {
720 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerVertex; functionOrdinal++)
721 {
722 tagView(tagNumber*tagSize+0) = vertexDim; // vertex dimension
723 tagView(tagNumber*tagSize+1) = vertexOrdinal; // vertex id
724 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
725 tagView(tagNumber*tagSize+3) = numFunctionsPerVertex; // total number of dofs in this vertex
726 tagNumber++;
727 }
728 }
729 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
730 {
731 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerEdge; functionOrdinal++)
732 {
733 tagView(tagNumber*tagSize+0) = edgeDim; // edge dimension
734 tagView(tagNumber*tagSize+1) = edgeOrdinal; // edge id
735 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
736 tagView(tagNumber*tagSize+3) = numFunctionsPerEdge; // total number of dofs on this edge
737 tagNumber++;
738 }
739 }
740 for (int faceOrdinalESEAS=0; faceOrdinalESEAS<numFaces; faceOrdinalESEAS++)
741 {
742 int faceOrdinalIntrepid2 = intrepid2FaceOrdinals[faceOrdinalESEAS];
743 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerFace; functionOrdinal++)
744 {
745 tagView(tagNumber*tagSize+0) = faceDim; // face dimension
746 tagView(tagNumber*tagSize+1) = faceOrdinalIntrepid2; // face id
747 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
748 tagView(tagNumber*tagSize+3) = numFunctionsPerFace; // total number of dofs on this face
749 tagNumber++;
750 }
751 }
752 for (int volumeOrdinal=0; volumeOrdinal<numVolumes; volumeOrdinal++)
753 {
754 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerVolume; functionOrdinal++)
755 {
756 tagView(tagNumber*tagSize+0) = volumeDim; // volume dimension
757 tagView(tagNumber*tagSize+1) = volumeOrdinal; // volume id
758 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
759 tagView(tagNumber*tagSize+3) = numFunctionsPerVolume; // total number of dofs in this volume
760 tagNumber++;
761 }
762 }
763 INTREPID2_TEST_FOR_EXCEPTION(tagNumber != this->basisCardinality_, std::invalid_argument, "Internal error: basis tag enumeration is incorrect");
764 }
765 } else {
766 for (ordinal_type i=0;i<cardinality;++i) {
767 tagView(i*tagSize+0) = volumeDim; // volume dimension
768 tagView(i*tagSize+1) = 0; // volume ordinal
769 tagView(i*tagSize+2) = i; // local dof id
770 tagView(i*tagSize+3) = cardinality; // total number of dofs on this face
771 }
772 }
773
774 // Basis-independent function sets tag and enum data in tagToOrdinal_ and ordinalToTag_ arrays:
775 // tags are constructed on host
777 this->ordinalToTag_,
778 tagView,
779 this->basisCardinality_,
780 tagSize,
781 posScDim,
782 posScOrd,
783 posDfOrd);
784 }
785 }
786
791 const char* getName() const override {
792 return "Intrepid2_IntegratedLegendreBasis_HGRAD_TET";
793 }
794
797 virtual bool requireOrientation() const override {
798 return (this->getDegree() > 2);
799 }
800
801 // since the getValues() below only overrides the FEM variant, we specify that
802 // we use the base class's getValues(), which implements the FVD variant by throwing an exception.
803 // (It's an error to use the FVD variant on this basis.)
805
824 virtual void getValues( OutputViewType outputValues, const PointViewType inputPoints,
825 const EOperator operatorType = OPERATOR_VALUE ) const override
826 {
827 auto numPoints = inputPoints.extent_int(0);
828
830
831 FunctorType functor(operatorType, outputValues, inputPoints, polyOrder_, defineVertexFunctions);
832
833 const int outputVectorSize = getVectorSizeForHierarchicalParallelism<OutputScalar>();
834 const int pointVectorSize = getVectorSizeForHierarchicalParallelism<PointScalar>();
835 const int vectorSize = std::max(outputVectorSize,pointVectorSize);
836 const int teamSize = 1; // because of the way the basis functions are computed, we don't have a second level of parallelism...
837
838 auto policy = Kokkos::TeamPolicy<ExecutionSpace>(numPoints,teamSize,vectorSize);
839 Kokkos::parallel_for("Hierarchical_HGRAD_TET_Functor", policy, functor);
840 }
841
851 getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override{
852 if(subCellDim == 1) {
853 return Teuchos::rcp(new
855 (this->basisDegree_));
856 } else if(subCellDim == 2) {
857 return Teuchos::rcp(new
859 (this->basisDegree_));
860 }
861 INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Input parameters out of bounds");
862 }
863
869 getHostBasis() const override {
870 using HostDeviceType = typename Kokkos::HostSpace::device_type;
872 return Teuchos::rcp( new HostBasisType(polyOrder_, pointType_) );
873 }
874 };
875} // end namespace Intrepid2
876
877#endif /* Intrepid2_IntegratedLegendreBasis_HGRAD_TET_h */
Header file for the abstract base class Intrepid2::Basis.
Teuchos::RCP< Basis< DeviceType, OutputType, PointType > > BasisPtr
Basis Pointer.
KOKKOS_INLINE_FUNCTION void device_assert(bool val)
H(grad) basis on the line based on integrated Legendre polynomials.
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...
Header function for Intrepid2::Util class and other utility functions.
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....
An abstract base class that defines interface for concrete basis implementations for Finite Element (...
ECoordinates basisCoordinates_
The coordinate system for which the basis is defined.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
virtual KOKKOS_INLINE_FUNCTION void getValues(OutputViewType, const PointViewType, const EOperator, const typename Kokkos::TeamPolicy< ExecutionSpace >::member_type &teamMember, const typename ExecutionSpace::scratch_memory_space &scratchStorage, const ordinal_type subcellDim=-1, const ordinal_type subcellOrdinal=-1) const
Team-level evaluation of basis functions on a reference cell.
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
EBasis basisType_
Type of the basis.
ordinal_type basisDegree_
Degree of the largest complete polynomial space that can be represented by the basis.
ordinal_type getDegree() const
Returns the degree of the basis.
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< scalarType, Kokkos::LayoutStride, DeviceType > ScalarViewType
View type for scalars.
OrdinalTypeArray2DHost ordinalToTag_
"true" if tagToOrdinal_ and ordinalToTag_ have been initialized
OrdinalTypeArray2DHost fieldOrdinalH1PolynomialDegree_
H^1 polynomial degree for each degree of freedom. Only defined for hierarchical bases right now....
Kokkos::View< ordinal_type **, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray2DHost
View type for 2d host array.
ordinal_type basisCardinality_
Cardinality of the basis, i.e., the number of basis functions/degrees-of-freedom.
OrdinalTypeArray3DHost tagToOrdinal_
DoF tag to ordinal lookup table.
shards::CellTopology getBaseCellTopology() const
Returns the base cell topology for which the basis is defined. See Shards documentation https://trili...
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
unsigned basisCellTopologyKey_
Identifier of the base topology of the cells for which the basis is defined. See the Shards package f...
typename DeviceType::execution_space ExecutionSpace
(Kokkos) Execution space for basis.
OrdinalTypeArray2DHost fieldOrdinalPolynomialDegree_
Polynomial degree for each degree of freedom. Only defined for hierarchical bases right now....
EFunctionSpace functionSpace_
The function space in which the basis is defined.
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.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
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.
Kokkos::View< ordinal_type **, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray2DHost
View type for 2d host array.
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
virtual bool requireOrientation() const override
True if orientation is required.
IntegratedLegendreBasis_HGRAD_TET(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_TET class.