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 
28 namespace 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 
687  using typename BasisBase::OrdinalTypeArray1DHost;
688  using typename BasisBase::OrdinalTypeArray2DHost;
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
860  this->setOrdinalTagData(this->tagToOrdinal_,
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.)
888  using BasisBase::getValues;
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 
934  BasisPtr<DeviceType,OutputScalar,PointScalar>
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 
954  virtual BasisPtr<typename Kokkos::HostSpace::device_type, OutputScalar, PointScalar>
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 */
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 > 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...
const char * getName() const override
Returns basis name.
An abstract base class that defines interface for concrete basis implementations for Finite Element (...
Kokkos::DynRankView< scalarType, Kokkos::LayoutStride, DeviceType > ScalarViewType
View type for scalars.
Free functions, callable from device code, that implement various polynomials useful in basis definit...
virtual bool requireOrientation() const override
True if orientation is required.
H(curl) basis on the triangle using a construction involving Legendre and integrated Jacobi polynomia...
For mathematical details of the construction, see:
EFunctionSpace functionSpace_
The function space in which the basis is defined.
ordinal_type basisDegree_
Degree of the largest complete polynomial space that can be represented by the basis.
virtual KOKKOS_INLINE_FUNCTION void getValues(OutputViewType, const PointViewType, const EOperator, const typename Kokkos::TeamPolicy< ExecutionSpace >::member_type &teamMember, const typename ExecutionSpace::scratch_memory_space &scratchStorage, const ordinal_type subcellDim=-1, const ordinal_type subcellOrdinal=-1) const
Team-level evaluation of basis functions on a reference cell.
Header function for Intrepid2::Util class and other utility functions.
For mathematical details of the construction, see:
Kokkos::View< ordinal_type **, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray2DHost
View type for 2d host array.
Basis defining Legendre basis on the line, a polynomial subspace of L^2 (a.k.a. H(vol)) on the line...
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::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
H(vol) basis on the line based on Legendre polynomials.
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.
OrdinalTypeArray2DHost ordinalToTag_
&quot;true&quot; if tagToOrdinal_ and ordinalToTag_ have been initialized
ordinal_type getDegree() const
Returns the degree of the basis.
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
ECoordinates basisCoordinates_
The coordinate system for which the basis is defined.
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...
OrdinalTypeArray2DHost fieldOrdinalPolynomialDegree_
Polynomial degree for each degree of freedom. Only defined for hierarchical bases right now...
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.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
void setOrdinalTagData(OrdinalTypeView3D &tagToOrdinal, OrdinalTypeView2D &ordinalToTag, const OrdinalTypeView1D tags, const ordinal_type basisCard, const ordinal_type tagSize, const ordinal_type posScDim, const ordinal_type posScOrd, const ordinal_type posDfOrd)
Fills ordinalToTag_ and tagToOrdinal_ by basis-specific tag data.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
Functor for computing values for the HierarchicalBasis_HCURL_TET class.
Header file for the abstract base class Intrepid2::Basis.
typename DeviceType::execution_space ExecutionSpace
(Kokkos) Execution space for basis.