Intrepid2
Intrepid2_LegendreBasis_HVOL_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_LegendreBasis_HVOL_TET_h
16 #define Intrepid2_LegendreBasis_HVOL_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 
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  int polyOrder_;
53  int numFields_, numPoints_;
54 
55  size_t fad_size_output_;
56 
57  Hierarchical_HVOL_TET_Functor(EOperator opType, OutputFieldType output, InputPointsType inputPoints, int polyOrder)
58  : opType_(opType), output_(output), inputPoints_(inputPoints),
59  polyOrder_(polyOrder),
60  fad_size_output_(getScalarDimensionForView(output))
61  {
62  numFields_ = output.extent_int(0);
63  numPoints_ = output.extent_int(1);
64  INTREPID2_TEST_FOR_EXCEPTION(numPoints_ != inputPoints.extent_int(0), std::invalid_argument, "point counts need to match!");
65  INTREPID2_TEST_FOR_EXCEPTION(numFields_ != (polyOrder_+1)*(polyOrder_+2)*(polyOrder_+3)/6, std::invalid_argument, "output field size does not match basis cardinality");
66  }
67 
68  KOKKOS_INLINE_FUNCTION
69  void operator()( const TeamMember & teamMember ) const
70  {
71  // values are product of [P_i](lambda_0,lambda_1), [P^{2i+1}_j](lambda_0 + lambda_1, lambda_2), and [P^{2*(i+j+1)}_k](1-lambda_3,lambda_3),
72  // times ((grad lambda_1) x (grad lambda_2)) \cdot (grad lambda_3).
73  // For the canonical orientation (all we support), the last term evaluates to 1, and
74  // lambda_0 = 1 - x - y - z
75  // lambda_1 = x
76  // lambda_2 = y
77  // lambda_3 = z
78  // [P_i](lambda_0, lambda_1) = P_i(lambda_1; lambda_0 + lambda_1) = P_i(x; 1 - y - z) -- a shifted, scaled Legendre function
79  // [P^{2i+1}_j](lambda_0 + lambda_1, lambda_2) = P^{2i+1}_j(lambda_2; lambda_0 + lambda_1 + lambda_2) = P^{2i+1}_j(y; 1 - z) -- a shifted, scaled Jacobi function
80  // [P^{2*(i+j+1)}_k](1-lambda_3,lambda_3) = P^{2*(i+j+1)}_k(lambda_3; 1) = P^{2*(i+j+1)}_k(z; 1) -- another shifted, scaled Jacobi function
81  auto pointOrdinal = teamMember.league_rank();
82  OutputScratchView P, P_2p1, P_2ipjp1;
83  if (fad_size_output_ > 0) {
84  P = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
85  P_2p1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
86  P_2ipjp1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1, fad_size_output_);
87  }
88  else {
89  P = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
90  P_2p1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
91  P_2ipjp1 = OutputScratchView(teamMember.team_shmem(), polyOrder_ + 1);
92  }
93 
94  const auto & x = inputPoints_(pointOrdinal,0);
95  const auto & y = inputPoints_(pointOrdinal,1);
96  const auto & z = inputPoints_(pointOrdinal,2);
97 
98  // write as barycentric coordinates:
99  const PointScalar lambda[4] = {1. - x - y - z, x, y, z};
100 
101  switch (opType_)
102  {
103  case OPERATOR_VALUE:
104  {
105  // face functions
106  {
107  const PointScalar tLegendre = lambda[0] + lambda[1];
108  Polynomials::shiftedScaledLegendreValues(P, polyOrder_, lambda[1], tLegendre);
109 
110  int fieldOrdinalOffset = 0;
111 
112  const int min_i = 0;
113  const int min_j = 0;
114  const int min_k = 0;
115  const int min_ij = min_i + min_j;
116  const int min_ijk = min_ij + min_k;
117  for (int totalPolyOrder_ijk=min_ijk; totalPolyOrder_ijk <= polyOrder_; totalPolyOrder_ijk++)
118  {
119  for (int totalPolyOrder_ij=min_ij; totalPolyOrder_ij <= totalPolyOrder_ijk-min_j; totalPolyOrder_ij++)
120  {
121  for (int i=min_i; i <= totalPolyOrder_ij-min_j; i++)
122  {
123  const int j = totalPolyOrder_ij - i;
124  const int k = totalPolyOrder_ijk - totalPolyOrder_ij;
125 
126  const double alpha1 = i * 2.0 + 1.;
127  const PointScalar tJacobi1 = lambda[0] + lambda[1] + lambda[2];
128  const PointScalar & xJacobi1 = lambda[2];
129  Polynomials::shiftedScaledJacobiValues(P_2p1, alpha1, polyOrder_, xJacobi1, tJacobi1);
130 
131  const double alpha2 = 2. * (i + j + 1.);
132  const PointScalar tJacobi2 = 1.0; // 1 - lambda[3] + lambda[3]
133  const PointScalar & xJacobi2 = lambda[3];
134  Polynomials::shiftedScaledJacobiValues(P_2ipjp1, alpha2, polyOrder_, xJacobi2, tJacobi2);
135 
136  const auto & P_i = P(i);
137  const auto & P_2p1_j = P_2p1(j);
138  const auto & P_2ipjp1_k = P_2ipjp1(k);
139 
140  output_(fieldOrdinalOffset,pointOrdinal) = P_i * P_2p1_j * P_2ipjp1_k;
141  fieldOrdinalOffset++;
142  }
143  }
144  }
145  }
146  } // end OPERATOR_VALUE
147  break;
148  default:
149  // unsupported operator type
150  device_assert(false);
151  }
152  }
153 
154  // Provide the shared memory capacity.
155  // This function takes the team_size as an argument,
156  // which allows team_size-dependent allocations.
157  size_t team_shmem_size (int team_size) const
158  {
159  // we will use shared memory to create a fast buffer for basis computations
160  size_t shmem_size = 0;
161  if (fad_size_output_ > 0)
162  shmem_size += 3 * OutputScratchView::shmem_size(polyOrder_ + 1, fad_size_output_);
163  else
164  shmem_size += 3 * OutputScratchView::shmem_size(polyOrder_ + 1);
165 
166  return shmem_size;
167  }
168  };
169 
180  template<typename DeviceType,
181  typename OutputScalar = double,
182  typename PointScalar = double>
184  : public Basis<DeviceType,OutputScalar,PointScalar>
185  {
186  public:
188 
189  using typename BasisBase::OrdinalTypeArray1DHost;
190  using typename BasisBase::OrdinalTypeArray2DHost;
191 
192  using typename BasisBase::OutputViewType;
193  using typename BasisBase::PointViewType;
194  using typename BasisBase::ScalarViewType;
195 
196  using typename BasisBase::ExecutionSpace;
197 
198  protected:
199  int polyOrder_; // the maximum order of the polynomial
200  EPointType pointType_;
201  public:
208  LegendreBasis_HVOL_TET(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
209  :
210  polyOrder_(polyOrder),
211  pointType_(pointType)
212  {
213  INTREPID2_TEST_FOR_EXCEPTION(pointType!=POINTTYPE_DEFAULT,std::invalid_argument,"PointType not supported");
214 
215  this->basisCardinality_ = ((polyOrder+3) * (polyOrder+2) * (polyOrder+1)) / 6;
216  this->basisDegree_ = polyOrder;
217  this->basisCellTopologyKey_ = shards::Tetrahedron<>::key;
218  this->basisType_ = BASIS_FEM_HIERARCHICAL;
219  this->basisCoordinates_ = COORDINATES_CARTESIAN;
220  this->functionSpace_ = FUNCTION_SPACE_HVOL;
221 
222  const int degreeLength = 1;
223  this->fieldOrdinalPolynomialDegree_ = OrdinalTypeArray2DHost("Integrated Legendre H(vol) triangle polynomial degree lookup", this->basisCardinality_, degreeLength);
224 
225  int fieldOrdinalOffset = 0;
226  // **** volume/interior functions **** //
227  const int min_i = 0;
228  const int min_j = 0;
229  const int min_k = 0;
230  const int min_ij = min_i + min_j;
231  const int min_ijk = min_ij + min_k;
232  for (int totalPolyOrder_ijk=min_ijk; totalPolyOrder_ijk <= polyOrder_; totalPolyOrder_ijk++)
233  {
234  for (int totalPolyOrder_ij=min_ij; totalPolyOrder_ij <= totalPolyOrder_ijk-min_j; totalPolyOrder_ij++)
235  {
236  for (int i=min_i; i <= totalPolyOrder_ij-min_j; i++)
237  {
238  const int j = totalPolyOrder_ij - i;
239  const int k = totalPolyOrder_ijk - totalPolyOrder_ij;
240 
241  this->fieldOrdinalPolynomialDegree_(fieldOrdinalOffset,0) = i+j+k;
242  fieldOrdinalOffset++;
243  }
244  }
245  }
246  INTREPID2_TEST_FOR_EXCEPTION(fieldOrdinalOffset != this->basisCardinality_, std::invalid_argument, "Internal error: basis enumeration is incorrect");
247 
248  // initialize tags
249  {
250  const auto & cardinality = this->basisCardinality_;
251 
252  // Basis-dependent initializations
253  const ordinal_type tagSize = 4; // size of DoF tag, i.e., number of fields in the tag
254  const ordinal_type posScDim = 0; // position in the tag, counting from 0, of the subcell dim
255  const ordinal_type posScOrd = 1; // position in the tag, counting from 0, of the subcell ordinal
256  const ordinal_type posDfOrd = 2; // position in the tag, counting from 0, of DoF ordinal relative to the subcell
257 
258  OrdinalTypeArray1DHost tagView("tag view", cardinality*tagSize);
259  const int volumeDim = 3;
260 
261  for (ordinal_type i=0;i<cardinality;++i) {
262  tagView(i*tagSize+0) = volumeDim; // volume dimension
263  tagView(i*tagSize+1) = 0; // volume id
264  tagView(i*tagSize+2) = i; // local dof id
265  tagView(i*tagSize+3) = cardinality; // total number of dofs on this face
266  }
267 
268  // Basis-independent function sets tag and enum data in tagToOrdinal_ and ordinalToTag_ arrays:
269  // tags are constructed on host
270  this->setOrdinalTagData(this->tagToOrdinal_,
271  this->ordinalToTag_,
272  tagView,
273  this->basisCardinality_,
274  tagSize,
275  posScDim,
276  posScOrd,
277  posDfOrd);
278  }
279  }
280 
285  const char* getName() const override {
286  return "Intrepid2_LegendreBasis_HVOL_TET";
287  }
288 
291  virtual bool requireOrientation() const override {
292  return (this->getDegree() > 2);
293  }
294 
295  // since the getValues() below only overrides the FEM variant, we specify that
296  // we use the base class's getValues(), which implements the FVD variant by throwing an exception.
297  // (It's an error to use the FVD variant on this basis.)
298  using BasisBase::getValues;
299 
318  virtual void getValues( OutputViewType outputValues, const PointViewType inputPoints,
319  const EOperator operatorType = OPERATOR_VALUE ) const override
320  {
321  auto numPoints = inputPoints.extent_int(0);
322 
324 
325  FunctorType functor(operatorType, outputValues, inputPoints, polyOrder_);
326 
327  const int outputVectorSize = getVectorSizeForHierarchicalParallelism<OutputScalar>();
328  const int pointVectorSize = getVectorSizeForHierarchicalParallelism<PointScalar>();
329  const int vectorSize = std::max(outputVectorSize,pointVectorSize);
330  const int teamSize = 1; // because of the way the basis functions are computed, we don't have a second level of parallelism...
331 
332  auto policy = Kokkos::TeamPolicy<ExecutionSpace>(numPoints,teamSize,vectorSize);
333  Kokkos::parallel_for("Hierarchical_HVOL_TET_Functor", policy , functor);
334  }
335 
340  virtual BasisPtr<typename Kokkos::HostSpace::device_type, OutputScalar, PointScalar>
341  getHostBasis() const override {
342  using HostDeviceType = typename Kokkos::HostSpace::device_type;
344  return Teuchos::rcp( new HostBasisType(polyOrder_, pointType_) );
345  }
346  };
347 } // end namespace Intrepid2
348 
349 #endif /* Intrepid2_LegendreBasis_HVOL_TET_h */
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
const char * getName() const override
Returns basis name.
LegendreBasis_HVOL_TET(int polyOrder, const EPointType pointType=POINTTYPE_DEFAULT)
Constructor.
unsigned basisCellTopologyKey_
Identifier of the base topology of the cells for which the basis is defined. See the Shards package f...
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...
An abstract base class that defines interface for concrete basis implementations for Finite Element (...
Kokkos::View< ordinal_type *, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray1DHost
View type for 1d host array.
Kokkos::DynRankView< scalarType, Kokkos::LayoutStride, DeviceType > ScalarViewType
View type for scalars.
virtual bool requireOrientation() const override
True if orientation is required.
Functor for computing values for the LegendreBasis_HVOL_TET class.
Free functions, callable from device code, that implement various polynomials useful in basis definit...
EFunctionSpace functionSpace_
The function space in which the basis is defined.
ordinal_type basisDegree_
Degree of the largest complete polynomial space that can be represented by the basis.
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.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
Kokkos::View< ordinal_type **, typename ExecutionSpace::array_layout, Kokkos::HostSpace > OrdinalTypeArray2DHost
View type for 2d host array.
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.
Basis defining Legendre basis on the line, a polynomial subspace of H(vol) on the line: extension to ...
virtual void getValues(OutputViewType outputValues, const PointViewType inputPoints, const EOperator operatorType=OPERATOR_VALUE) const override
Evaluation of a FEM basis on a reference cell.
OrdinalTypeArray2DHost ordinalToTag_
&quot;true&quot; if tagToOrdinal_ and ordinalToTag_ have been initialized
ordinal_type getDegree() const
Returns the degree of the basis.
ECoordinates basisCoordinates_
The coordinate system for which the basis is defined.
OrdinalTypeArray2DHost fieldOrdinalPolynomialDegree_
Polynomial degree for each degree of freedom. Only defined for hierarchical bases right now...
Kokkos::DynRankView< OutputValueType, Kokkos::LayoutStride, DeviceType > OutputViewType
View type for basis value output.
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.
H(vol) basis on the triangle based on integrated Legendre polynomials.
Kokkos::DynRankView< PointValueType, Kokkos::LayoutStride, DeviceType > PointViewType
View type for input points.
Header file for the abstract base class Intrepid2::Basis.
typename DeviceType::execution_space ExecutionSpace
(Kokkos) Execution space for basis.