Intrepid2
Intrepid2_HierarchicalBasis_HCURL_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_HierarchicalBasis_HCURL_TET_h
16#define Intrepid2_HierarchicalBasis_HCURL_TET_h
17
18#include <Intrepid2_config.h>
19
20#include <Kokkos_DynRankView.hpp>
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 PointScratchView = Kokkos::View<PointScalar*, ScratchSpace,Kokkos::MemoryTraits<Kokkos::Unmanaged>>;
43
44 using TeamPolicy = Kokkos::TeamPolicy<ExecutionSpace>;
45 using TeamMember = typename TeamPolicy::member_type;
46
47 EOperator opType_;
48
49 OutputFieldType output_; // F,P
50 InputPointsType inputPoints_; // P,D
51
52 ordinal_type polyOrder_;
53 ordinal_type numFields_, numPoints_;
54
55 size_t fad_size_output_;
56
57 static constexpr ordinal_type numVertices = 4;
58 static constexpr ordinal_type numEdges = 6;
59 static constexpr ordinal_type numEdgesPerFace = 3;
60 static constexpr ordinal_type numFaceFamilies = 2;
61 static constexpr ordinal_type numFaces = 4;
62 static constexpr ordinal_type numVerticesPerFace = 3;
63 static constexpr ordinal_type numInteriorFamilies = 3;
64
65 // index into face_vertices with faceOrdinal * numVerticesPerFace + vertexNumber
66 const ordinal_type face_vertices[numFaces*numVerticesPerFace] = {0,1,2, // face 0
67 0,1,3, // face 1
68 1,2,3, // face 2
69 0,2,3 // face 3
70 };
71
72 // index into face_edges with faceOrdinal * numEdgesPerFace + faceEdgeNumber
73 // (entries are the edge numbers in the tetrahedron)
74 // note that the orientation of each face's third edge is reversed relative to the orientation in the volume
75 const ordinal_type face_edges[numFaces * numEdgesPerFace] = {0,1,2, // face 0
76 0,4,3, // face 1
77 1,5,4, // face 2
78 2,5,3}; // face 3
79
80 // the following ordering of the edges matches that used by ESEAS
81 const ordinal_type edge_start_[numEdges] = {0,1,0,0,1,2}; // edge i is from edge_start_[i] to edge_end_[i]
82 const ordinal_type edge_end_[numEdges] = {1,2,2,3,3,3}; // edge i is from edge_start_[i] to edge_end_[i]
83 const ordinal_type face_family_start_ [numFaceFamilies] = {0,1};
84 const ordinal_type face_family_middle_[numFaceFamilies] = {1,2};
85 const ordinal_type face_family_end_ [numFaceFamilies] = {2,0};
86
87 const ordinal_type numEdgeFunctions_;
88 const ordinal_type numFaceFunctionsPerFace_;
89 const ordinal_type numFaceFunctions_;
90 const ordinal_type numInteriorFunctionsPerFamily_;
91 const ordinal_type numInteriorFunctions_;
92
93 // interior basis functions are computed in terms of certain face basis functions.
94 const ordinal_type faceOrdinalForInterior_[numInteriorFamilies] = {0,2,3};
95 const ordinal_type faceFamilyForInterior_[numInteriorFamilies] = {0,0,1};
96 const ordinal_type interiorCoordinateOrdinal_[numInteriorFamilies] = {3,0,1}; // m, where E^b_{ijk} is computed in terms of [L^{2(i+j)}_k](1-lambda_m, lambda_m)
97
98 KOKKOS_INLINE_FUNCTION
99 ordinal_type dofOrdinalForFace(const ordinal_type &faceOrdinal,
100 const ordinal_type &zeroBasedFaceFamily,
101 const ordinal_type &i,
102 const ordinal_type &j) const
103 {
104 // determine where the functions for this face start
105 const ordinal_type faceDofOffset = numEdgeFunctions_ + faceOrdinal * numFaceFunctionsPerFace_;
106
107 // rather than deriving a closed formula in terms of i and j (which is potentially error-prone),
108 // we simply step through a for loop much as we do in the basis computations themselves. (This method
109 // is not expected to be called so much as to be worth optimizing.)
110
111 const ordinal_type max_ij_sum = polyOrder_ - 1;
112
113 ordinal_type fieldOrdinal = faceDofOffset + zeroBasedFaceFamily; // families are interleaved on the face.
114
115 for (ordinal_type ij_sum=1; ij_sum <= max_ij_sum; ij_sum++)
116 {
117 for (ordinal_type ii=0; ii<ij_sum; ii++)
118 {
119 // j will be ij_sum - i; j >= 1.
120 const ordinal_type jj = ij_sum - ii; // jj >= 1
121 if ( (ii == i) && (jj == j))
122 {
123 // have reached the (i,j) we're looking for
124 return fieldOrdinal;
125 }
126 fieldOrdinal += numFaceFamilies; // increment for the interleaving of face families.
127 }
128 }
129 return -1; // error: not found.
130 }
131
132 Hierarchical_HCURL_TET_Functor(EOperator opType, OutputFieldType output, InputPointsType inputPoints, int polyOrder)
133 : opType_(opType), output_(output), inputPoints_(inputPoints),
134 polyOrder_(polyOrder),
135 fad_size_output_(getScalarDimensionForView(output)),
136 numEdgeFunctions_(polyOrder * numEdges), // 6 edges
137 numFaceFunctionsPerFace_(polyOrder * (polyOrder-1)), // 2 families, each with p*(p-1)/2 functions per face
138 numFaceFunctions_(numFaceFunctionsPerFace_*numFaces), // 4 faces
139 numInteriorFunctionsPerFamily_((polyOrder > 2) ? (polyOrder-2)*(polyOrder-1)*polyOrder/6 : 0), // p choose 3
140 numInteriorFunctions_(numInteriorFunctionsPerFamily_ * numInteriorFamilies) // 3 families of interior functions
141 {
142 numFields_ = output.extent_int(0);
143 numPoints_ = output.extent_int(1);
144
145 const ordinal_type expectedCardinality = numEdgeFunctions_ + numFaceFunctions_ + numInteriorFunctions_;
146
147 // interior family I: computed in terms of face 012 (face ordinal 0), ordinal 0 in face family I. First interior family is computed in terms of the first set of face functions (note that both sets of families are interleaved, so basis ordinal increments are by numInteriorFamilies and numFaceFamilies, respectively).
148 // interior family II: computed in terms of face 123 (face ordinal 2), ordinal 2 in face family I.
149 // interior family III: computed in terms of face 230 (face ordinal 3), ordinal 3 in face family II.
150
151 INTREPID2_TEST_FOR_EXCEPTION(numPoints_ != inputPoints.extent_int(0), std::invalid_argument, "point counts need to match!");
152 INTREPID2_TEST_FOR_EXCEPTION(numFields_ != expectedCardinality, std::invalid_argument, "output field size does not match basis cardinality");
153 }
154
155 KOKKOS_INLINE_FUNCTION
156 void computeEdgeLegendre(OutputScratchView &P,
157 const ordinal_type &edgeOrdinal,
158 const PointScalar* lambda) const
159 {
160 const auto & s0 = lambda[edge_start_[edgeOrdinal]];
161 const auto & s1 = lambda[ edge_end_[edgeOrdinal]];
162
163 Polynomials::shiftedScaledLegendreValues(P, polyOrder_-1, PointScalar(s1), PointScalar(s0+s1));
164 }
165
166 KOKKOS_INLINE_FUNCTION
167 void edgeFunctionValue(OutputScalar &edgeValue_x,
168 OutputScalar &edgeValue_y,
169 OutputScalar &edgeValue_z,
170 const ordinal_type &edgeOrdinal,
171 OutputScratchView &P,
172 const ordinal_type &i,
173 const PointScalar* lambda,
174 const PointScalar* lambda_dx,
175 const PointScalar* lambda_dy,
176 const PointScalar* lambda_dz
177 ) const
178 {
179 const auto & s0 = lambda [edge_start_[edgeOrdinal]];
180 const auto & s0_dx = lambda_dx[edge_start_[edgeOrdinal]];
181 const auto & s0_dy = lambda_dy[edge_start_[edgeOrdinal]];
182 const auto & s0_dz = lambda_dz[edge_start_[edgeOrdinal]];
183
184 const auto & s1 = lambda [ edge_end_[edgeOrdinal]];
185 const auto & s1_dx = lambda_dx[ edge_end_[edgeOrdinal]];
186 const auto & s1_dy = lambda_dy[ edge_end_[edgeOrdinal]];
187 const auto & s1_dz = lambda_dz[ edge_end_[edgeOrdinal]];
188
189 const auto & P_i = P(i);
190 const PointScalar xWeight = s0 * s1_dx - s1 * s0_dx;
191 const PointScalar yWeight = s0 * s1_dy - s1 * s0_dy;
192 const PointScalar zWeight = s0 * s1_dz - s1 * s0_dz;
193 edgeValue_x = P_i * xWeight;
194 edgeValue_y = P_i * yWeight;
195 edgeValue_z = P_i * zWeight;
196 }
197
198 KOKKOS_INLINE_FUNCTION
199 void computeFaceIntegratedJacobi(OutputScratchView &L_2ip1,
200 const ordinal_type &zeroBasedFaceOrdinal,
201 const ordinal_type &zeroBasedFamilyOrdinal,
202 const ordinal_type &i,
203 const PointScalar* lambda) const
204 {
205 const auto &s0_vertex_number = face_family_start_ [zeroBasedFamilyOrdinal];
206 const auto &s1_vertex_number = face_family_middle_[zeroBasedFamilyOrdinal];
207 const auto &s2_vertex_number = face_family_end_ [zeroBasedFamilyOrdinal];
208
209 // index into face_vertices with faceOrdinal * numVerticesPerFace + vertexNumber
210 const auto &s0_index = face_vertices[zeroBasedFaceOrdinal * numVerticesPerFace + s0_vertex_number];
211 const auto &s1_index = face_vertices[zeroBasedFaceOrdinal * numVerticesPerFace + s1_vertex_number];
212 const auto &s2_index = face_vertices[zeroBasedFaceOrdinal * numVerticesPerFace + s2_vertex_number];
213
214 const auto & s0 = lambda[s0_index];
215 const auto & s1 = lambda[s1_index];
216 const auto & s2 = lambda[s2_index];
217 const PointScalar jacobiScaling = s0 + s1 + s2;
218
219 const double alpha = i*2.0 + 1;
220 Polynomials::shiftedScaledIntegratedJacobiValues(L_2ip1, alpha, polyOrder_-1, s2, jacobiScaling);
221 }
222
223 KOKKOS_INLINE_FUNCTION
224 void faceFunctionValue(OutputScalar &value_x,
225 OutputScalar &value_y,
226 OutputScalar &value_z,
227 const ordinal_type &j, // j >= 1
228 const OutputScratchView &L_2ip1, // container in which shiftedScaledIntegratedJacobiValues have been computed for (2i+1) for appropriate face and family
229 const OutputScalar &edgeValue_x,
230 const OutputScalar &edgeValue_y,
231 const OutputScalar &edgeValue_z,
232 const PointScalar* lambda) const
233 {
234 const auto & L_2ip1_j = L_2ip1(j);
235 value_x = edgeValue_x * L_2ip1_j;
236 value_y = edgeValue_y * L_2ip1_j;
237 value_z = edgeValue_z * L_2ip1_j;
238 }
239
240 KOKKOS_INLINE_FUNCTION
241 void operator()( const TeamMember & teamMember ) const
242 {
243 const ordinal_type numFunctionsPerFace = polyOrder_ * (polyOrder_ - 1);
244 auto pointOrdinal = teamMember.league_rank();
245 OutputScratchView edge_field_values_at_point, jacobi_values_at_point, other_values_at_point, other_values2_at_point;
246 if (fad_size_output_ > 0) {
247 edge_field_values_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_, fad_size_output_);
248 jacobi_values_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_, fad_size_output_);
249 other_values_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_, fad_size_output_);
250 other_values2_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_, fad_size_output_);
251 }
252 else {
253 edge_field_values_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_);
254 jacobi_values_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_);
255 other_values_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_);
256 other_values2_at_point = OutputScratchView(teamMember.team_shmem(), polyOrder_);
257 }
258
259 const auto & x = inputPoints_(pointOrdinal,0);
260 const auto & y = inputPoints_(pointOrdinal,1);
261 const auto & z = inputPoints_(pointOrdinal,2);
262
263 // write as barycentric coordinates:
264 const PointScalar lambda[4] = {1. - x - y - z, x, y, z};
265 const PointScalar lambda_dx[4] = {-1., 1., 0., 0.};
266 const PointScalar lambda_dy[4] = {-1., 0., 1., 0.};
267 const PointScalar lambda_dz[4] = {-1., 0., 0., 1.};
268
269 const int num1DEdgeFunctions = polyOrder_; // per edge
270
271 switch (opType_)
272 {
273 case OPERATOR_VALUE:
274 {
275 // edge functions
276
277 // relabel scratch view
278 auto & P = edge_field_values_at_point;
279
280 int fieldOrdinalOffset = 0;
281 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
282 {
283 computeEdgeLegendre(P, edgeOrdinal, lambda);
284
285 for (int i=0; i<num1DEdgeFunctions; i++)
286 {
287 auto &output_x = output_(i+fieldOrdinalOffset,pointOrdinal,0);
288 auto &output_y = output_(i+fieldOrdinalOffset,pointOrdinal,1);
289 auto &output_z = output_(i+fieldOrdinalOffset,pointOrdinal,2);
290
291 edgeFunctionValue(output_x, output_y, output_z,
292 edgeOrdinal, P, i,
293 lambda, lambda_dx, lambda_dy, lambda_dz);
294 }
295 fieldOrdinalOffset += num1DEdgeFunctions;
296 }
297
298 // face functions
299 {
300 // relabel scratch view
301 auto & L_2ip1 = jacobi_values_at_point;
302
303 // these functions multiply the edge functions from the 01 edge by integrated Jacobi functions, appropriately scaled
304 const int max_ij_sum = polyOrder_ - 1;
305
306 // following ESEAS, we interleave the face families. This groups all the face dofs of a given degree together.
307 int faceFieldOrdinalOffset = fieldOrdinalOffset;
308 for (int faceOrdinal = 0; faceOrdinal < numFaces; faceOrdinal++) {
309 for (int familyOrdinal=1; familyOrdinal<=numFaceFamilies; familyOrdinal++)
310 {
311 int fieldOrdinal = faceFieldOrdinalOffset + familyOrdinal - 1;
312
313 for (int ij_sum=1; ij_sum <= max_ij_sum; ij_sum++)
314 {
315 for (int i=0; i<ij_sum; i++)
316 {
317 computeFaceIntegratedJacobi(L_2ip1, faceOrdinal, familyOrdinal-1, i, lambda);
318
319 const int j = ij_sum - i; // j >= 1
320 // family 1 involves edge functions from edge (s0,s1) (edgeOrdinal 0 in the face); family 2 involves functions from edge (s1,s2) (edgeOrdinal 1 in the face)
321 const int faceEdgeOrdinal = familyOrdinal-1; // family I: use first edge from face; family II: use second edge
322 const int volumeEdgeOrdinal = face_edges[faceOrdinal * numEdgesPerFace + faceEdgeOrdinal];
323 const int edgeBasisOrdinal = i + volumeEdgeOrdinal*num1DEdgeFunctions;
324 const auto & edgeValue_x = output_(edgeBasisOrdinal,pointOrdinal,0);
325 const auto & edgeValue_y = output_(edgeBasisOrdinal,pointOrdinal,1);
326 const auto & edgeValue_z = output_(edgeBasisOrdinal,pointOrdinal,2);
327
328 auto & output_x = output_(fieldOrdinal,pointOrdinal,0);
329 auto & output_y = output_(fieldOrdinal,pointOrdinal,1);
330 auto & output_z = output_(fieldOrdinal,pointOrdinal,2);
331
332 faceFunctionValue(output_x, output_y, output_z, j, L_2ip1, edgeValue_x, edgeValue_y, edgeValue_z, lambda);
333
334 fieldOrdinal += numFaceFamilies; // increment due to the interleaving
335 } // i
336 } // ij_sum
337 fieldOrdinalOffset = fieldOrdinal - numFaceFamilies + 1; // due to the interleaving increment, we've gone numFaceFamilies past the last face ordinal. Set offset to be one past.
338 } // familyOrdinal
339 faceFieldOrdinalOffset += numFunctionsPerFace;
340 } // faceOrdinal
341 } // face functions block
342
343 // interior functions
344 {
345 const int interiorFieldOrdinalOffset = fieldOrdinalOffset;
346 const int min_ijk_sum = 2;
347 const int max_ijk_sum = polyOrder_-1;
348
349 // relabel Jacobi values container:
350 const auto & L_2ipj = jacobi_values_at_point;
351 for (int interiorFamilyOrdinal=1; interiorFamilyOrdinal<=numInteriorFamilies; interiorFamilyOrdinal++)
352 {
353 // following ESEAS, we interleave the interior families. This groups all the interior dofs of a given degree together.
354
355 // lambda_m is used to compute the appropriate weight in terms of Jacobi functions of order k below.
356 const auto & lambda_m = lambda[interiorCoordinateOrdinal_[interiorFamilyOrdinal-1]];
357 const PointScalar jacobiScaling = 1.0;
358
359 ordinal_type fieldOrdinal = interiorFieldOrdinalOffset + interiorFamilyOrdinal - 1;
360
361 const ordinal_type relatedFaceOrdinal = faceOrdinalForInterior_[interiorFamilyOrdinal-1];
362 const ordinal_type relatedFaceFamily = faceFamilyForInterior_ [interiorFamilyOrdinal-1]; // zero-based
363
364 for (int ijk_sum=min_ijk_sum; ijk_sum <= max_ijk_sum; ijk_sum++)
365 {
366 for (int i=0; i<ijk_sum-1; i++)
367 {
368 for (int j=1; j<ijk_sum-i; j++)
369 {
370 // the interior functions are blended face functions. This dof ordinal corresponds to the face function which we blend.
371 const ordinal_type faceDofOrdinal = dofOrdinalForFace(relatedFaceOrdinal, relatedFaceFamily, i, j);
372
373 const double alpha = 2 * (i + j);
374
375 Polynomials::shiftedScaledIntegratedJacobiValues(L_2ipj, alpha, polyOrder_-1, lambda_m, jacobiScaling);
376
377 const int k = ijk_sum - i - j;
378 const auto & L_k = L_2ipj(k);
379 for (int d=0; d<3; d++)
380 {
381 const auto & E_face_d = output_(faceDofOrdinal,pointOrdinal,d);
382 output_(fieldOrdinal,pointOrdinal,d) = L_k * E_face_d;
383 }
384 fieldOrdinal += numInteriorFamilies; // increment due to the interleaving.
385 }
386 }
387 }
388 fieldOrdinalOffset = fieldOrdinal - numInteriorFamilies + 1; // due to the interleaving increment, we've gone numInteriorFamilies past the last interior ordinal. Set offset to be one past.
389 }
390 } // interior functions block
391
392 } // end OPERATOR_VALUE
393 break;
394 case OPERATOR_CURL:
395 {
396 // edge functions
397 int fieldOrdinalOffset = 0;
398 /*
399 Per Fuentes et al. (see Appendix E.1, E.2), the curls of the edge functions, are
400 (i+2) * [P_i](s0,s1) * (grad s0 \times grad s1)
401 The P_i we have implemented in shiftedScaledLegendreValues.
402 */
403 // rename the scratch memory to match our usage here:
404 auto & P_i = edge_field_values_at_point;
405 auto & L_2ip1_j = jacobi_values_at_point;
406 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
407 {
408 const auto & s0 = lambda[edge_start_[edgeOrdinal]];
409 const auto & s1 = lambda[ edge_end_[edgeOrdinal]];
410
411 const auto & s0_dx = lambda_dx[edge_start_[edgeOrdinal]];
412 const auto & s0_dy = lambda_dy[edge_start_[edgeOrdinal]];
413 const auto & s0_dz = lambda_dz[edge_start_[edgeOrdinal]];
414 const auto & s1_dx = lambda_dx[ edge_end_[edgeOrdinal]];
415 const auto & s1_dy = lambda_dy[ edge_end_[edgeOrdinal]];
416 const auto & s1_dz = lambda_dz[ edge_end_[edgeOrdinal]];
417
418 const OutputScalar grad_s0_cross_grad_s1[3] = {s0_dy * s1_dz - s0_dz * s1_dy,
419 s0_dz * s1_dx - s0_dx * s1_dz,
420 s0_dx * s1_dy - s0_dy * s1_dx};
421
422 Polynomials::shiftedScaledLegendreValues(P_i, polyOrder_-1, PointScalar(s1), PointScalar(s0+s1));
423 for (int i=0; i<num1DEdgeFunctions; i++)
424 {
425 for (int d=0; d<3; d++)
426 {
427 output_(i+fieldOrdinalOffset,pointOrdinal,d) = (i+2) * P_i(i) * grad_s0_cross_grad_s1[d];
428 }
429 }
430 fieldOrdinalOffset += num1DEdgeFunctions;
431 }
432
433 /*
434 Fuentes et al give the face functions as E^f_{ij}, with curl:
435 [L^{2i+1}_j](s0+s1,s2) curl(E^E_i(s0,s1)) + grad[L^(2i+1)_j](s0+s1,s2) \times E^E_i(s0,s1)
436 where:
437 - E^E_i is the ith edge function on the edge s0 to s1
438 - L^{2i+1}_j is an shifted, scaled integrated Jacobi polynomial.
439 - For family 1, s0s1s2 = 012
440 - For family 2, s0s1s2 = 120
441 - Note that grad[L^(2i+1)_j](s0+s1,s2) is computed as [P^{2i+1}_{j-1}](s0+s1,s2) (grad s2) + [R^{2i+1}_{j-1}] grad (s0+s1+s2),
442 but for triangles (s0+s1+s2) is always 1, so that the grad (s0+s1+s2) is 0.
443 - Here,
444 [P^{2i+1}_{j-1}](s0,s1) = P^{2i+1}_{j-1}(s1,s0+s1)
445 and
446 [R^{2i+1}_{j-1}(s0,s1)] = d/dt L^{2i+1}_j(s1,s0+s1)
447 We have implemented P^{alpha}_{j} as shiftedScaledJacobiValues,
448 and d/dt L^{alpha}_{j} as shiftedScaledIntegratedJacobiValues_dt.
449 */
450 // rename the scratch memory to match our usage here:
451 auto & P_2ip1_j = other_values_at_point;
452 auto & L_2ip1_j_dt = other_values2_at_point;
453
454 // following ESEAS, we interleave the face families. This groups all the face dofs of a given degree together.
455 int faceFieldOrdinalOffset = fieldOrdinalOffset;
456 for (int faceOrdinal=0; faceOrdinal<numFaces; faceOrdinal++)
457 {
458 for (int familyOrdinal=1; familyOrdinal<=numFaceFamilies; familyOrdinal++)
459 {
460 int fieldOrdinal = faceFieldOrdinalOffset + familyOrdinal - 1;
461
462 const auto &s0_vertex_number = face_family_start_ [familyOrdinal-1];
463 const auto &s1_vertex_number = face_family_middle_[familyOrdinal-1];
464 const auto &s2_vertex_number = face_family_end_ [familyOrdinal-1];
465
466 // index into face_vertices with faceOrdinal * numVerticesPerFace + vertexNumber
467 const auto &s0_index = face_vertices[faceOrdinal * numVerticesPerFace + s0_vertex_number];
468 const auto &s1_index = face_vertices[faceOrdinal * numVerticesPerFace + s1_vertex_number];
469 const auto &s2_index = face_vertices[faceOrdinal * numVerticesPerFace + s2_vertex_number];
470
471 const auto & s0 = lambda[s0_index];
472 const auto & s1 = lambda[s1_index];
473 const auto & s2 = lambda[s2_index];
474 const PointScalar jacobiScaling = s0 + s1 + s2;
475
476 const auto & s0_dx = lambda_dx[s0_index];
477 const auto & s0_dy = lambda_dy[s0_index];
478 const auto & s0_dz = lambda_dz[s0_index];
479 const auto & s1_dx = lambda_dx[s1_index];
480 const auto & s1_dy = lambda_dy[s1_index];
481 const auto & s1_dz = lambda_dz[s1_index];
482 const auto & s2_dx = lambda_dx[s2_index];
483 const auto & s2_dy = lambda_dy[s2_index];
484 const auto & s2_dz = lambda_dz[s2_index];
485
486 const PointScalar grad_s2[3] = {s2_dx, s2_dy, s2_dz};
487 const PointScalar gradJacobiScaling[3] = {s0_dx + s1_dx + s2_dx,
488 s0_dy + s1_dy + s2_dy,
489 s0_dz + s1_dz + s2_dz};
490
491 const PointScalar grad_s0_cross_grad_s1[3] = {s0_dy * s1_dz - s0_dz * s1_dy,
492 s0_dz * s1_dx - s0_dx * s1_dz,
493 s0_dx * s1_dy - s0_dy * s1_dx};
494
495 const PointScalar s0_grad_s1_minus_s1_grad_s0[3] = {s0 * s1_dx - s1 * s0_dx,
496 s0 * s1_dy - s1 * s0_dy,
497 s0 * s1_dz - s1 * s0_dz};
498
499 Polynomials::shiftedScaledLegendreValues (P_i, polyOrder_-1, PointScalar(s1), PointScalar(s0+s1));
500 // [L^{2i+1}_j](s0+s1,s2) curl(E^E_i(s0,s1)) + grad[L^(2i+1)_j](s0+s1,s2) \times E^E_i(s0,s1)
501 // - Note that grad[L^(2i+1)_j](s0+s1,s2) is computed as [P^{2i+1}_{j-1}](s0+s1,s2) (grad s2) + [R^{2i+1}_{j-1}](s0+s1,s2) grad (s0+s1+s2),
502 // - R^{2i+1}_{j-1}(s0+s1;s0+s1+s2) = d/dt L^{2i+1}_j(s0+s1;s0+s1+s2)
503 // - We have implemented d/dt L^{alpha}_{j} as shiftedScaledIntegratedJacobiValues_dt.
504 // - E^E_i(s0,s1) = [P_i](s0,s1) (s0 grad s1 - s1 grad s0)
505
506 const int max_ij_sum = polyOrder_ - 1;
507 for (int ij_sum=1; ij_sum <= max_ij_sum; ij_sum++)
508 {
509 for (int i=0; i<ij_sum; i++)
510 {
511 const int j = ij_sum - i; // j >= 1
512
513 const double alpha = i*2.0 + 1;
514
515 Polynomials::shiftedScaledJacobiValues (P_2ip1_j, alpha, polyOrder_-1, PointScalar(s2), jacobiScaling);
516 Polynomials::shiftedScaledIntegratedJacobiValues (L_2ip1_j, alpha, polyOrder_-1, PointScalar(s2), jacobiScaling);
517 Polynomials::shiftedScaledIntegratedJacobiValues_dt(L_2ip1_j_dt, alpha, polyOrder_-1, PointScalar(s2), jacobiScaling);
518
519 const PointScalar & edgeValue = P_i(i);
520
521 PointScalar grad_L_2ip1_j[3];
522 for (int d=0; d<3; d++)
523 {
524 grad_L_2ip1_j[d] = P_2ip1_j(j-1) * grad_s2[d] // [P^{2i+1}_{j-1}](s0+s1,s2) (grad s2)
525 + L_2ip1_j_dt(j) * gradJacobiScaling[d]; // [R^{2i+1}_{j-1}](s0+s1,s2) grad (s0+s1+s2)
526 }
527
528 const PointScalar grad_L_2ip1_j_cross_E_i[3] = { grad_L_2ip1_j[1] * edgeValue * s0_grad_s1_minus_s1_grad_s0[2] - grad_L_2ip1_j[2] * edgeValue * s0_grad_s1_minus_s1_grad_s0[1],
529 grad_L_2ip1_j[2] * edgeValue * s0_grad_s1_minus_s1_grad_s0[0] - grad_L_2ip1_j[0] * edgeValue * s0_grad_s1_minus_s1_grad_s0[2],
530 grad_L_2ip1_j[0] * edgeValue * s0_grad_s1_minus_s1_grad_s0[1] - grad_L_2ip1_j[1] * edgeValue * s0_grad_s1_minus_s1_grad_s0[0] };
531
532 for (int d=0; d<3; d++)
533 {
534 const OutputScalar edgeCurl_d = (i+2.) * P_i(i) * grad_s0_cross_grad_s1[d];
535 output_(fieldOrdinal,pointOrdinal,d) = L_2ip1_j(j) * edgeCurl_d // [L^{2i+1}_j](s0+s1,s2) curl(E^E_i(s0,s1))
536 + grad_L_2ip1_j_cross_E_i[d];
537 }
538
539 fieldOrdinal += numFaceFamilies; // increment due to the interleaving
540 } // i
541 } // ij_sum
542 fieldOrdinalOffset = fieldOrdinal - numFaceFamilies + 1; // due to the interleaving increment, we've gone numFaceFamilies past the last face ordinal. Set offset to be one past.
543 } // familyOrdinal
544 faceFieldOrdinalOffset += numFunctionsPerFace;
545 } // faceOrdinal
546
547 // interior functions
548 {
549 // relabel values containers:
550 auto & L_2ipj = jacobi_values_at_point;
551 auto & P_2ipj = other_values_at_point;
552 auto & L_2ip1 = edge_field_values_at_point;
553 auto & P = other_values2_at_point;
554
555 const int interiorFieldOrdinalOffset = fieldOrdinalOffset;
556 const int min_ijk_sum = 2;
557 const int max_ijk_sum = polyOrder_-1;
558 for (int interiorFamilyOrdinal=1; interiorFamilyOrdinal<=numInteriorFamilies; interiorFamilyOrdinal++)
559 {
560 // following ESEAS, we interleave the interior families. This groups all the interior dofs of a given degree together.
561
562 const ordinal_type relatedFaceOrdinal = faceOrdinalForInterior_[interiorFamilyOrdinal-1];
563 const ordinal_type relatedFaceFamily = faceFamilyForInterior_ [interiorFamilyOrdinal-1]; // zero-based
564
565 // lambda_m is used to compute the appropriate weight in terms of Jacobi functions of order k below.
566 const auto & m = interiorCoordinateOrdinal_[interiorFamilyOrdinal-1];
567 const auto & lambda_m = lambda[m];
568 const PointScalar jacobiScaling = 1.0;
569
570 ordinal_type fieldOrdinal = interiorFieldOrdinalOffset + interiorFamilyOrdinal - 1;
571
572 for (int ijk_sum=min_ijk_sum; ijk_sum <= max_ijk_sum; ijk_sum++)
573 {
574 for (int i=0; i<ijk_sum-1; i++)
575 {
576 computeFaceIntegratedJacobi(L_2ip1, relatedFaceOrdinal, relatedFaceFamily, i, lambda);
577 // face family 1 involves edge functions from edge (0,1) (edgeOrdinal 0); family 2 involves functions from edge (1,2) (edgeOrdinal 1)
578 const ordinal_type faceEdgeOrdinal = relatedFaceFamily;
579 const int volumeEdgeOrdinal = face_edges[relatedFaceOrdinal * numEdgesPerFace + faceEdgeOrdinal];
580 computeEdgeLegendre(P, volumeEdgeOrdinal, lambda);
581
582 OutputScalar edgeValue[3];
583 edgeFunctionValue(edgeValue[0], edgeValue[1], edgeValue[2], volumeEdgeOrdinal, P, i, lambda, lambda_dx, lambda_dy, lambda_dz);
584
585 for (int j=1; j<ijk_sum-i; j++)
586 {
587 // the interior functions are blended face functions. This dof ordinal corresponds to the face function which we blend.
588 const ordinal_type faceDofOrdinal = dofOrdinalForFace(relatedFaceOrdinal, relatedFaceFamily, i, j);
589
590 const double alpha = 2 * (i + j);
591
592 Polynomials::shiftedScaledIntegratedJacobiValues(L_2ipj, alpha, polyOrder_-1, lambda_m, jacobiScaling);
593 Polynomials::shiftedScaledJacobiValues (P_2ipj, alpha, polyOrder_-1, lambda_m, jacobiScaling);
594
595 // gradient of [L^{2(i+j)}_k](t0,t1) = [P^{2(i+j)}_{k-1}](t0,t1) grad t1 + [R^{2(i+j}_k](t0,t1) grad (t0+t1).
596 // we have t0 = lambda_m, t1 = 1 - lambda_m, so grad (t0 + t1) = 0.
597
598 const int k = ijk_sum - i - j;
599 const auto & L_k = L_2ipj(k);
600 const auto & P_km1 = P_2ipj(k-1);
601
602 const PointScalar grad_L_k[3] = {P_km1 * lambda_dx[m],
603 P_km1 * lambda_dy[m],
604 P_km1 * lambda_dz[m]};
605
606 // compute E_face -- OPERATOR_VALUE for the face function corresponding to this interior function
607 OutputScalar E_face[3];
608 faceFunctionValue(E_face[0], E_face[1], E_face[2], j, L_2ip1, edgeValue[0], edgeValue[1], edgeValue[2], lambda);
609
610 PointScalar grad_L_k_cross_E_face[3] = {grad_L_k[1] * E_face[2] - grad_L_k[2] * E_face[1],
611 grad_L_k[2] * E_face[0] - grad_L_k[0] * E_face[2],
612 grad_L_k[0] * E_face[1] - grad_L_k[1] * E_face[0]};
613 for (int d=0; d<3; d++)
614 {
615 const auto & curl_E_face_d = output_(faceDofOrdinal,pointOrdinal,d);
616 output_(fieldOrdinal,pointOrdinal,d) = L_k * curl_E_face_d + grad_L_k_cross_E_face[d];
617 }
618
619 fieldOrdinal += numInteriorFamilies; // increment due to the interleaving.
620 }
621 }
622 }
623 fieldOrdinalOffset = fieldOrdinal - numInteriorFamilies + 1; // due to the interleaving increment, we've gone numInteriorFamilies past the last interior ordinal. Set offset to be one past.
624 }
625 } // interior functions block
626 } // OPERATOR_CURL
627 break;
628 case OPERATOR_GRAD:
629 case OPERATOR_D1:
630 case OPERATOR_D2:
631 case OPERATOR_D3:
632 case OPERATOR_D4:
633 case OPERATOR_D5:
634 case OPERATOR_D6:
635 case OPERATOR_D7:
636 case OPERATOR_D8:
637 case OPERATOR_D9:
638 case OPERATOR_D10:
639 INTREPID2_TEST_FOR_ABORT( true,
640 ">>> ERROR: (Intrepid2::Hierarchical_HCURL_TET_Functor) Unsupported differential operator");
641 default:
642 // unsupported operator type
643 device_assert(false);
644 }
645 }
646
647 // Provide the shared memory capacity.
648 // This function takes the team_size as an argument,
649 // which allows team_size-dependent allocations.
650 size_t team_shmem_size (int team_size) const
651 {
652 // we will use shared memory to create a fast buffer for basis computations
653 size_t shmem_size = 0;
654 if (fad_size_output_ > 0)
655 shmem_size += 4 * OutputScratchView::shmem_size(polyOrder_ + 1, fad_size_output_);
656 else
657 shmem_size += 4 * OutputScratchView::shmem_size(polyOrder_ + 1);
658
659 return shmem_size;
660 }
661 };
662
677 template<typename DeviceType,
678 typename OutputScalar = double,
679 typename PointScalar = double,
680 bool useCGBasis = true> // if useCGBasis is false, all basis functions will be associated with the interior
682 : public Basis<DeviceType,OutputScalar,PointScalar>
683 {
684 public:
686
689
690 using typename BasisBase::OutputViewType;
691 using typename BasisBase::PointViewType;
692 using typename BasisBase::ScalarViewType;
693
694 using typename BasisBase::ExecutionSpace;
695
696 protected:
697 int polyOrder_; // the maximum order of the polynomial
698 public:
703 HierarchicalBasis_HCURL_TET(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
704 :
705 polyOrder_(polyOrder)
706 {
707 this->basisCellTopologyKey_ = shards::Tetrahedron<>::key;
708 const shards::CellTopology cellTopo(shards::getCellTopologyData<shards::Tetrahedron<>>());
709 const int numEdges = cellTopo.getEdgeCount();
710 const int numFaces = cellTopo.getFaceCount();
711
712 const int numEdgeFunctions = polyOrder * numEdges;
713 const int numFaceFunctions = polyOrder * (polyOrder-1) * numFaces; // 4 faces; 2 families, each with p*(p-1)/2 functions per face
714 const int numInteriorFunctionsPerFamily = (polyOrder > 2) ? (polyOrder-2)*(polyOrder-1)*polyOrder/6 : 0; // (p choose 3)
715 const int numInteriorFunctions = numInteriorFunctionsPerFamily * 3; // 3 families of interior functions
716 this->basisCardinality_ = numEdgeFunctions + numFaceFunctions + numInteriorFunctions;
717 this->basisDegree_ = polyOrder;
718
719 this->basisType_ = BASIS_FEM_HIERARCHICAL;
720 this->basisCoordinates_ = COORDINATES_CARTESIAN;
721 this->functionSpace_ = FUNCTION_SPACE_HCURL;
722
723 const int degreeLength = 1;
724 this->fieldOrdinalPolynomialDegree_ = OrdinalTypeArray2DHost("Hierarchical H(curl) triangle polynomial degree lookup", this->basisCardinality_, degreeLength);
725
726 int fieldOrdinalOffset = 0;
727 // **** vertex functions **** //
728 // no vertex functions in H(curl)
729
730 // **** edge functions **** //
731 const int numFunctionsPerEdge = polyOrder; // p functions associated with each edge
732 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
733 {
734 for (int i=0; i<numFunctionsPerEdge; i++)
735 {
736 this->fieldOrdinalPolynomialDegree_(i+fieldOrdinalOffset,0) = i+1; // the multiplicands involving the gradients of the vertex functions are first degree polynomials; hence the +1 (the remaining multiplicands are order i = 0,…,p-1).
737 }
738 fieldOrdinalOffset += numFunctionsPerEdge;
739 }
740 INTREPID2_TEST_FOR_EXCEPTION(fieldOrdinalOffset != numEdgeFunctions, std::invalid_argument, "Internal error: basis enumeration is incorrect");
741
742 // **** face functions **** //
743 const int max_ij_sum = polyOrder-1;
744 int faceFieldOrdinalOffset = fieldOrdinalOffset;
745 const int numFaceFamilies = 2;
746 for (int faceOrdinal=0; faceOrdinal<numFaces; faceOrdinal++)
747 {
748 for (int faceFamilyOrdinal=1; faceFamilyOrdinal<=numFaceFamilies; faceFamilyOrdinal++)
749 {
750 // following ESEAS, we interleave the face families. This groups all the face dofs of a given degree together.
751 int fieldOrdinal = faceFieldOrdinalOffset + faceFamilyOrdinal - 1;
752 for (int ij_sum=1; ij_sum <= max_ij_sum; ij_sum++)
753 {
754 for (int i=0; i<ij_sum; i++)
755 {
756 this->fieldOrdinalPolynomialDegree_(fieldOrdinal,0) = ij_sum+1;
757 fieldOrdinal += numFaceFamilies; // increment due to the interleaving.
758 }
759 }
760 fieldOrdinalOffset = fieldOrdinal - numFaceFamilies + 1; // due to the interleaving increment, we've gone numFaceFamilies past the last face ordinal. Set offset to be one past.
761 }
762 faceFieldOrdinalOffset += numFaceFunctions / numFaces;
763 }
764 INTREPID2_TEST_FOR_EXCEPTION(fieldOrdinalOffset != numEdgeFunctions + numFaceFunctions, std::invalid_argument, "Internal error: basis enumeration is incorrect");
765
766 const int numInteriorFamilies = 3;
767 const int interiorFieldOrdinalOffset = fieldOrdinalOffset;
768 const int min_ijk_sum = 2;
769 const int max_ijk_sum = polyOrder-1;
770 for (int interiorFamilyOrdinal=1; interiorFamilyOrdinal<=numInteriorFamilies; interiorFamilyOrdinal++)
771 {
772 // following ESEAS, we interleave the interior families. This groups all the interior dofs of a given degree together.
773 int fieldOrdinal = interiorFieldOrdinalOffset + interiorFamilyOrdinal - 1;
774 for (int ijk_sum=min_ijk_sum; ijk_sum <= max_ijk_sum; ijk_sum++)
775 {
776 for (int i=0; i<ijk_sum-1; i++)
777 {
778 for (int j=1; j<ijk_sum-i; j++)
779 {
780 this->fieldOrdinalPolynomialDegree_(fieldOrdinal,0) = ijk_sum+1;
781 fieldOrdinal += numInteriorFamilies; // increment due to the interleaving.
782 }
783 }
784 }
785 fieldOrdinalOffset = fieldOrdinal - numInteriorFamilies + 1; // due to the interleaving increment, we've gone numFaceFamilies past the last face ordinal. Set offset to be one past.
786 }
787
788 INTREPID2_TEST_FOR_EXCEPTION(fieldOrdinalOffset != this->basisCardinality_, std::invalid_argument, "Internal error: basis enumeration is incorrect");
789
790 // initialize tags
791 {
792 // ESEAS numbers tetrahedron faces differently from Intrepid2
793 // ESEAS: 012, 013, 123, 023
794 // Intrepid2: 013, 123, 032, 021
795 const int intrepid2FaceOrdinals[4] {3,0,1,2}; // index is the ESEAS face ordinal; value is the intrepid2 ordinal
796
797 const auto & cardinality = this->basisCardinality_;
798
799 // Basis-dependent initializations
800 const ordinal_type tagSize = 4; // size of DoF tag, i.e., number of fields in the tag
801 const ordinal_type posScDim = 0; // position in the tag, counting from 0, of the subcell dim
802 const ordinal_type posScOrd = 1; // position in the tag, counting from 0, of the subcell ordinal
803 const ordinal_type posDfOrd = 2; // position in the tag, counting from 0, of DoF ordinal relative to the subcell
804
805 OrdinalTypeArray1DHost tagView("tag view", cardinality*tagSize);
806 const ordinal_type edgeDim = 1, faceDim = 2, volumeDim = 3;
807
808 if (useCGBasis) {
809 {
810 int tagNumber = 0;
811 for (int edgeOrdinal=0; edgeOrdinal<numEdges; edgeOrdinal++)
812 {
813 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerEdge; functionOrdinal++)
814 {
815 tagView(tagNumber*tagSize+0) = edgeDim; // edge dimension
816 tagView(tagNumber*tagSize+1) = edgeOrdinal; // edge id
817 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
818 tagView(tagNumber*tagSize+3) = numFunctionsPerEdge; // total number of dofs on this edge
819 tagNumber++;
820 }
821 }
822 const int numFunctionsPerFace = numFaceFunctions / numFaces;
823 for (int faceOrdinalESEAS=0; faceOrdinalESEAS<numFaces; faceOrdinalESEAS++)
824 {
825 int faceOrdinalIntrepid2 = intrepid2FaceOrdinals[faceOrdinalESEAS];
826 for (int functionOrdinal=0; functionOrdinal<numFunctionsPerFace; functionOrdinal++)
827 {
828 tagView(tagNumber*tagSize+0) = faceDim; // face dimension
829 tagView(tagNumber*tagSize+1) = faceOrdinalIntrepid2; // face id
830 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
831 tagView(tagNumber*tagSize+3) = numFunctionsPerFace; // total number of dofs on this face
832 tagNumber++;
833 }
834 }
835
836 // interior
837 for (int functionOrdinal=0; functionOrdinal<numInteriorFunctions; functionOrdinal++)
838 {
839 tagView(tagNumber*tagSize+0) = volumeDim; // interior dimension
840 tagView(tagNumber*tagSize+1) = 0; // volume id
841 tagView(tagNumber*tagSize+2) = functionOrdinal; // local dof id
842 tagView(tagNumber*tagSize+3) = numInteriorFunctions; // total number of interior dofs
843 tagNumber++;
844 }
845 }
846 }
847 else
848 {
849 // DG basis: all functions are associated with interior
850 for (ordinal_type i=0;i<cardinality;++i) {
851 tagView(i*tagSize+0) = volumeDim; // face dimension
852 tagView(i*tagSize+1) = 0; // interior/volume id
853 tagView(i*tagSize+2) = i; // local dof id
854 tagView(i*tagSize+3) = cardinality; // total number of dofs on this face
855 }
856 }
857
858 // Basis-independent function sets tag and enum data in tagToOrdinal_ and ordinalToTag_ arrays:
859 // tags are constructed on host
861 this->ordinalToTag_,
862 tagView,
863 this->basisCardinality_,
864 tagSize,
865 posScDim,
866 posScOrd,
867 posDfOrd);
868 }
869 }
870
875 const char* getName() const override {
876 return "Intrepid2_HierarchicalBasis_HCURL_TET";
877 }
878
881 virtual bool requireOrientation() const override {
882 return (this->getDegree() > 2);
883 }
884
885 // since the getValues() below only overrides the FEM variant, we specify that
886 // we use the base class's getValues(), which implements the FVD variant by throwing an exception.
887 // (It's an error to use the FVD variant on this basis.)
889
908 virtual void getValues( OutputViewType outputValues, const PointViewType inputPoints,
909 const EOperator operatorType = OPERATOR_VALUE ) const override
910 {
911 auto numPoints = inputPoints.extent_int(0);
912
914
915 FunctorType functor(operatorType, outputValues, inputPoints, polyOrder_);
916
917 const int outputVectorSize = getVectorSizeForHierarchicalParallelism<OutputScalar>();
918 const int pointVectorSize = getVectorSizeForHierarchicalParallelism<PointScalar>();
919 const int vectorSize = std::max(outputVectorSize,pointVectorSize);
920 const int teamSize = 1; // because of the way the basis functions are computed, we don't have a second level of parallelism...
921
922 auto policy = Kokkos::TeamPolicy<ExecutionSpace>(numPoints,teamSize,vectorSize);
923 Kokkos::parallel_for("Hierarchical_HCURL_TET_Functor", policy , functor);
924 }
925
935 getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override
936 {
939 if (subCellDim == 1)
940 {
941 return Teuchos::rcp(new HVOL_Line(this->basisDegree_-1));
942 }
943 else if (subCellDim == 2)
944 {
945 return Teuchos::rcp(new HCURL_Tri(this->basisDegree_));
946 }
947 INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Input parameters out of bounds");
948 }
949
955 getHostBasis() const override {
956 using HostDeviceType = typename Kokkos::HostSpace::device_type;
958 return Teuchos::rcp( new HostBasisType(polyOrder_) );
959 }
960 };
961} // end namespace Intrepid2
962
963#endif /* Intrepid2_HierarchicalBasis_HCURL_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(curl) basis on the triangle using a construction involving Legendre and integrated Jacobi polynomia...
H(vol) basis on the line based on 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
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.
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.
For mathematical details of the construction, see:
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 void getValues(OutputViewType outputValues, const PointViewType inputPoints, const EOperator operatorType=OPERATOR_VALUE) const override
Evaluation of a FEM basis on a reference cell.
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 bool requireOrientation() const override
True if orientation is required.
BasisPtr< DeviceType, OutputScalar, PointScalar > getSubCellRefBasis(const ordinal_type subCellDim, const ordinal_type subCellOrd) const override
returns the basis associated to a subCell.
Kokkos::View< ordinal_type **, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray2DHost
View type for 2d host array.
const char * getName() const override
Returns basis name.
HierarchicalBasis_HCURL_TET(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Constructor.
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
For mathematical details of the construction, see:
Basis defining Legendre basis on the line, a polynomial subspace of L^2 (a.k.a. H(vol)) on the line.
Functor for computing values for the HierarchicalBasis_HCURL_TET class.