Intrepid2
Intrepid2_Polynomials.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_Polynomials_h
16 #define Intrepid2_Polynomials_h
17 
18 #include "Intrepid2_Polylib.hpp"
19 #include "Intrepid2_Types.hpp"
20 #include "Intrepid2_Utils.hpp"
21 #if defined(HAVE_INTREPID2_SACADO) && !defined(KOKKOS_ENABLE_IMPL_VIEW_LEGACY)
22 #include <Sacado.hpp>
23 #endif
24 
25 namespace Intrepid2
26 {
27  namespace Polynomials
28  {
29  /*
30  These polynomials are supplemental to those defined in the Polylib class; there is some overlap.
31  We actually take advantage of the overlap in our verification tests, using the Polylib functions
32  to verify the ones defined here. Our interface here is a little simpler, and the functions are a little less
33  general than those in Polylib.
34 
35  We define some polynomial functions that are useful in a variety of contexts.
36  In particular, the (integrated) Legendre and Jacobi polynomials are useful in defining
37  hierarchical bases. See in particular:
38 
39  Federico Fuentes, Brendan Keith, Leszek Demkowicz, Sriram Nagaraj.
40  "Orientation embedded high order shape functions for the exact sequence elements of all shapes."
41  Computers & Mathematics with Applications, Volume 70, Issue 4, 2015, Pages 353-458, ISSN 0898-1221.
42  https://doi.org/10.1016/j.camwa.2015.04.027.
43 
44  In this implementation, we take care to make minimal assumptions on both the containers
45  and the scalar type. The containers need to support a one-argument operator() for assignment and/or
46  lookup (as appropriate). The scalar type needs to support a cast from Intrepid2::ordinal_type, as well
47  as standard arithmetic operations. In particular, both 1-rank Kokkos::View and Kokkos::DynRankView are
48  supported, as are C++ floating point types and Sacado scalar types.
49  */
50 
57  template<typename OutputValueViewType, typename ScalarType>
58  KOKKOS_INLINE_FUNCTION void legendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x)
59  {
60  if (n >= 0) outputValues(0) = 1.0;
61  if (n >= 1) outputValues(1) = x;
62  for (int i=2; i<=n; i++)
63  {
64  const ScalarType i_scalar = ScalarType(i);
65  outputValues(i) = (2. - 1. / i_scalar) * x * outputValues(i-1) - (1. - 1. / i_scalar) * outputValues(i-2);
66  }
67  }
68 
76  template<typename OutputValueViewType, typename ScalarType>
77  KOKKOS_INLINE_FUNCTION void legendreDerivativeValues(OutputValueViewType outputValues, const OutputValueViewType legendreValues, Intrepid2::ordinal_type n, ScalarType x)
78  {
79  if (n >= 0) outputValues(0) = 0.0;
80  if (n >= 1) outputValues(1) = 1.0;
81  for (int i=2; i<=n; i++)
82  {
83  const ScalarType i_scalar = ScalarType(i);
84  outputValues(i) = outputValues(i-2) + (2. * i_scalar - 1.) * legendreValues(i-1);
85  }
86  }
87 
88  // derivative values can be computed using the Legendre values
89  // n: number of Legendre polynomials' derivative values to compute. outputValues must have at least n+1 entries
90  // x: value in [-1, 1]
91  // dn: number of derivatives to take. Must be >= 1.
99  template<typename OutputValueViewType, typename PointScalarType>
100  KOKKOS_INLINE_FUNCTION void legendreDerivativeValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, PointScalarType x, Intrepid2::ordinal_type dn)
101  {
102  const OutputValueViewType nullOutputScalarView;
103  const double alpha = 0;
104  const double beta = 0;
105  const int numPoints = 1;
106 
107  using Layout = typename NaturalLayoutForType<PointScalarType>::layout;
108 
109  using UnmanagedPointScalarView = Kokkos::View<PointScalarType*, Layout, Kokkos::MemoryTraits<Kokkos::Unmanaged> >;
110  UnmanagedPointScalarView pointView;
111 #if defined(HAVE_INTREPID2_SACADO) && defined(SACADO_HAS_NEW_KOKKOS_VIEW_IMPL)
112  if constexpr (Sacado::is_view_fad<UnmanagedPointScalarView>::value) {
113  // This is super weird and doesn't actually wrap the Sacado derivatives
114  pointView = UnmanagedPointScalarView(&x.val(), numPoints, 1);
115  } else
116 #endif
117  pointView = UnmanagedPointScalarView(&x, numPoints);
118  for (int i=0; i<=n; i++)
119  {
120  auto jacobiValue = Kokkos::subview(outputValues,Kokkos::pair<Intrepid2::ordinal_type,Intrepid2::ordinal_type>(i,i+1));
121  jacobiValue(0) = 0.0;
122  Intrepid2::Polylib::Serial::JacobiPolynomial(numPoints, pointView, jacobiValue, nullOutputScalarView, i-dn, alpha+dn, beta+dn);
123 
124  double scaleFactor = 1.0;
125  for (int j=1; j<=dn; j++)
126  {
127  scaleFactor *= 0.5 * (j+alpha+beta+i);
128  }
129 
130  outputValues(i) = jacobiValue(0) * scaleFactor;
131  }
132  }
133 
143  template<typename OutputValueViewType, typename ScalarType>
144  KOKKOS_INLINE_FUNCTION void shiftedLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x)
145  {
146  legendreValues(outputValues, n, 2.*x-1.);
147  }
148 
159  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
160  KOKKOS_INLINE_FUNCTION void shiftedScaledLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
161  {
162  using OutputScalar = typename OutputValueViewType::value_type;
163  OutputScalar two_x_minus_t = 2. * x - t;
164  OutputScalar t_squared = t * t;
165  if (n >= 0) outputValues(0) = 1.0;
166  if (n >= 1) outputValues(1) = two_x_minus_t;
167  for (int i=2; i<=n; i++)
168  {
169  const ScalarType one_over_i = 1.0 / ScalarType(i);
170  outputValues(i) = one_over_i * ( (2. *i - 1. ) * two_x_minus_t * outputValues(i-1) - (i - 1.) * t_squared * outputValues(i-2));
171  }
172  }
173 
187  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
188  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
189  {
190  // reduced memory version: compute P_i in outputValues
191  shiftedScaledLegendreValues(outputValues,n,x,t);
192  // keep a copy of the last two P_i values around; update these before overwriting in outputValues
193  ScalarType P_i_minus_two, P_i_minus_one;
194  if (n >= 0) P_i_minus_two = outputValues(0);
195  if (n >= 1) P_i_minus_one = outputValues(1);
196 
197  if (n >= 0) outputValues(0) = 1.0;
198  if (n >= 1) outputValues(1) = x;
199  for (int i=2; i<=n; i++)
200  {
201  const ScalarType & P_i = outputValues(i); // define as P_i just for clarity of the code below
202  const ScalarType i_scalar = ScalarType(i);
203  ScalarType L_i = (P_i - t * t * P_i_minus_two) /( 2. * (2. * i_scalar - 1.));
204 
205  // get the next values of P_{i-1} and P_{i-2} before overwriting the P_i value
206  P_i_minus_two = P_i_minus_one;
207  P_i_minus_one = P_i;
208 
209  // overwrite P_i value
210  outputValues(i) = L_i;
211  }
212  }
213 
228  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
229  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues(OutputValueViewType outputValues, const OutputValueViewType shiftedScaledLegendreValues,
230  Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
231  {
232  // reduced flops version: rely on previously computed P_i
233  if (n >= 0) outputValues(0) = 1.0;
234  if (n >= 1) outputValues(1) = x;
235  for (int i=2; i<=n; i++)
236  {
237  const ScalarType & P_i = shiftedScaledLegendreValues(i); // define as P_i just for clarity of the code below
238  const ScalarType & P_i_minus_two = shiftedScaledLegendreValues(i-2);
239  const ScalarType i_scalar = ScalarType(i);
240  outputValues(i) = (P_i - t * t * P_i_minus_two) /( 2. * (2. * i_scalar - 1.));
241  }
242  }
243 
244  // the below implementation is commented out for now to guard a certain confusion.
245  // the integratedLegendreValues() implementation below is implemented in such a way as to agree, modulo a coordinate transformation, with the
246  // shiftedScaledIntegratedLegendreValues() above. Since the latter really is an integral of shiftedScaledLegendreValues(), the former isn't
247  // actually an integral of the (unshifted, unscaled) Legendre polynomials.
248 // template<typename OutputValueViewType, typename ScalarType>
249 // KOKKOS_INLINE_FUNCTION void integratedLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x)
250 // {
251 // // to reduce memory requirements, compute P_i in outputValues
252 // legendreValues(outputValues,n,x);
253 // // keep a copy of the last two P_i values around; update these before overwriting in outputValues
254 // ScalarType P_i_minus_two, P_i_minus_one;
255 // if (n >= 0) P_i_minus_two = outputValues(0);
256 // if (n >= 1) P_i_minus_one = outputValues(1);
257 //
258 // if (n >= 0) outputValues(0) = 1.0;
259 // if (n >= 1) outputValues(1) = (x + 1.0) / 2.0;
260 // for (int i=2; i<=n; i++)
261 // {
262 // const ScalarType & P_i = outputValues(i); // define as P_i just for clarity of the code below
263 // const ScalarType i_scalar = ScalarType(i);
264 // ScalarType L_i = (P_i - P_i_minus_two) /( 2. * (2. * i_scalar - 1.));
265 //
266 // // get the next values of P_{i-1} and P_{i-2} before overwriting the P_i value
267 // P_i_minus_two = P_i_minus_one;
268 // P_i_minus_one = P_i;
269 //
270 // // overwrite P_i value
271 // outputValues(i) = L_i;
272 // }
273 // }
274 
282  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
283  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues_dx(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
284  {
285  if (n >= 0) outputValues(0) = 0.0;
286  if (n >= 1) outputValues(1) = 1.0;
287  if (n >= 2) outputValues(2) = 2. * x - t;
288  for (int i=2; i<=n-1; i++)
289  {
290  const ScalarType one_over_i = 1.0 / ScalarType(i);
291  outputValues(i+1) = one_over_i * (2. * i - 1.) * (2. * x - t) * outputValues(i) - one_over_i * (i - 1.0) * t * t * outputValues(i-1);
292  }
293  }
294 
304  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
305  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues_dt(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
306  {
307  // memory-conserving version -- place the Legendre values in the final output container
308  shiftedScaledLegendreValues(outputValues, n, x, t);
309 
310  ScalarType P_i_minus_2 = outputValues(0);
311  ScalarType P_i_minus_1 = outputValues(1);
312 
313  if (n >= 0) outputValues(0) = 0.0;
314  if (n >= 1) outputValues(1) = 0.0;
315 
316  for (int i=2; i<=n; i++)
317  {
318  const ScalarType L_i_dt = -0.5 * (P_i_minus_1 + t * P_i_minus_2);
319 
320  P_i_minus_2 = P_i_minus_1;
321  P_i_minus_1 = outputValues(i);
322 
323  outputValues(i) = L_i_dt;
324  }
325  }
326 
337  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
338  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues_dt(OutputValueViewType outputValues, const OutputValueViewType shiftedScaledLegendreValues,
339  Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
340  {
341  // reduced flops version: rely on previously computed P_i
342  if (n >= 0) outputValues(0) = 0.0;
343  if (n >= 1) outputValues(1) = 0.0;
344  for (int i=2; i<=n; i++)
345  {
346  const ScalarType & P_i_minus_1 = shiftedScaledLegendreValues(i-1); // define as P_i just for clarity of the code below
347  const ScalarType & P_i_minus_2 = shiftedScaledLegendreValues(i-2);
348  outputValues(i) = -0.5 * (P_i_minus_1 + t * P_i_minus_2);
349  }
350  }
351 
366  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
367  KOKKOS_INLINE_FUNCTION void shiftedScaledJacobiValues(OutputValueViewType outputValues, double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
368  {
369  ScalarType two_x_minus_t = 2. * x - t;
370  ScalarTypeForScaling alpha_squared_t = alpha * alpha * t;
371 
372  if (n >= 0) outputValues(0) = 1.0;
373  if (n >= 1) outputValues(1) = two_x_minus_t + alpha * x;
374 
375  for (int i=2; i<=n; i++)
376  {
377  const ScalarType & P_i_minus_one = outputValues(i-1); // define as P_i just for clarity of the code below
378  const ScalarType & P_i_minus_two = outputValues(i-2);
379 
380  double a_i = (2. * i) * (i + alpha) * (2. * i + alpha - 2.);
381  double b_i = 2. * i + alpha - 1.;
382  double c_i = (2. * i + alpha) * (2. * i + alpha - 2.);
383  double d_i = 2. * (i + alpha - 1.) * (i - 1.) * (2. * i + alpha);
384 
385  outputValues(i) = (b_i / a_i) * (c_i * two_x_minus_t + alpha_squared_t) * P_i_minus_one - (d_i / a_i) * t * t * P_i_minus_two;
386  }
387  }
388 
406  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
407  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues(OutputValueViewType outputValues, const OutputValueViewType jacobiValues,
408  double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
409  {
410  // reduced flops version: rely on previously computed P_i
411  if (n >= 0) outputValues(0) = 1.0;
412  if (n >= 1) outputValues(1) = x;
413 
414  ScalarType t_squared = t * t;
415  for (int i=2; i<=n; i++)
416  {
417  const ScalarType & P_i = jacobiValues(i); // define as P_i just for clarity of the code below
418  const ScalarType & P_i_minus_1 = jacobiValues(i-1);
419  const ScalarType & P_i_minus_2 = jacobiValues(i-2);
420 
421  double a_i = (i + alpha) / ((2. * i + alpha - 1.) * (2. * i + alpha ));
422  double b_i = alpha / ((2. * i + alpha - 2.) * (2. * i + alpha ));
423  double c_i = (i - 1.) / ((2. * i + alpha - 2.) * (2. * i + alpha - 1.));
424 
425  outputValues(i) = a_i * P_i + b_i * t * P_i_minus_1 - c_i * t_squared * P_i_minus_2;
426  }
427  }
428 
446  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
447  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues(OutputValueViewType outputValues,
448  double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
449  {
450  // memory-conserving version -- place the Jacobi values in the final output container
451  shiftedScaledJacobiValues(outputValues, alpha, n, x, t);
452 
453  ScalarType P_i_minus_2 = outputValues(0);
454  ScalarType P_i_minus_1 = outputValues(1);
455 
456  if (n >= 0) outputValues(0) = 1.0;
457  if (n >= 1) outputValues(1) = x;
458 
459  ScalarType t_squared = t * t;
460  for (int i=2; i<=n; i++)
461  {
462  const ScalarType & P_i = outputValues(i);
463 
464  double a_i = (i + alpha) / ((2. * i + alpha - 1.) * (2. * i + alpha ));
465  double b_i = alpha / ((2. * i + alpha - 2.) * (2. * i + alpha ));
466  double c_i = (i - 1.) / ((2. * i + alpha - 2.) * (2. * i + alpha - 1.));
467 
468  ScalarType L_i = a_i * P_i + b_i * t * P_i_minus_1 - c_i * t_squared * P_i_minus_2;
469 
470  P_i_minus_2 = P_i_minus_1;
471  P_i_minus_1 = P_i;
472 
473  outputValues(i) = L_i;
474  }
475  }
476 
477  // x derivative of integrated Jacobi is just Jacobi
478  // only distinction is in the index -- outputValues indices are shifted by 1 relative to jacobiValues, above
486  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
487  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues_dx(OutputValueViewType outputValues,
488  double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
489  {
490  // rather than repeating the somewhat involved implementation of jacobiValues here,
491  // call with (n-1), and then move values accordingly
492  shiftedScaledJacobiValues(outputValues, alpha, n-1, x, t);
493 
494  // forward implementation
495  ScalarType nextValue = 0.0;
496  ScalarType nextNextValue = 0.0;
497  for (int i=0; i<=n-1; i++)
498  {
499  nextNextValue = outputValues(i);
500  outputValues(i) = nextValue;
501  nextValue = nextNextValue;
502  }
503  outputValues(n-1) = nextValue;
504  }
505 
516  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
517  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues_dt(OutputValueViewType outputValues, const OutputValueViewType jacobiValues,
518  double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
519  {
520  // reduced flops version: rely on previously computed P_i
521  if (n >= 0) outputValues(0) = 0.0;
522  if (n >= 1) outputValues(1) = 0.0;
523  for (int i=2; i<=n; i++)
524  {
525  const ScalarType & P_i_minus_1 = jacobiValues(i-1); // define as P_i just for clarity of the code below
526  const ScalarType & P_i_minus_2 = jacobiValues(i-2);
527  outputValues(i) = - (i-1.) / (2. * i - 2. + alpha) * (P_i_minus_1 + t * P_i_minus_2);
528  }
529  }
530 
541  template<typename OutputValueViewType, typename ScalarType, typename ScalarTypeForScaling>
542  KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues_dt(OutputValueViewType outputValues,
543  double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
544  {
545  // memory-conserving version -- place the Jacobi values in the final output container
546  shiftedScaledJacobiValues(outputValues, alpha, n, x, t);
547 
548  ScalarType P_i_minus_2 = outputValues(0);
549  ScalarType P_i_minus_1 = outputValues(1);
550 
551  if (n >= 0) outputValues(0) = 0.0;
552  if (n >= 1) outputValues(1) = 0.0;
553 
554  for (int i=2; i<=n; i++)
555  {
556  const ScalarType L_i_dt = - (i-1.) / (2. * i - 2. + alpha) * (P_i_minus_1 + t * P_i_minus_2);
557 
558  P_i_minus_2 = P_i_minus_1;
559  P_i_minus_1 = outputValues(i);
560 
561  outputValues(i) = L_i_dt;
562  }
563  }
564  } // namespace Polynomials
565 } // namespace Intrepid2
566 
567 #endif /* Intrepid2_Polynomials_h */
Header file for Intrepid2::Polylib class providing orthogonal polynomial calculus and interpolation...
Header function for Intrepid2::Util class and other utility functions.
static KOKKOS_INLINE_FUNCTION void JacobiPolynomial(const ordinal_type np, const zViewType z, polyiViewType poly_in, polydViewType polyd, const ordinal_type n, const double alpha, const double beta)
Routine to calculate Jacobi polynomials, , and their first derivative, .
Contains definitions of custom data types in Intrepid2.