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
25namespace 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.
KOKKOS_INLINE_FUNCTION void shiftedLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x)
Evaluate shifted Legendre polynomials up to order n at a specified point in [0,1].
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues_dx(OutputValueViewType outputValues, double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
x derivative of integrated Jacobi polynomials L_i for i>=1, defined for x in [0,1].
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues(OutputValueViewType outputValues, const OutputValueViewType jacobiValues, double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
Integrated Jacobi values, defined for x in [0,1].
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues_dt(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
t derivative of shifted, scaled integrated Legendre polynomials L_i for i>=1, defined for x in [0,...
KOKKOS_INLINE_FUNCTION void legendreDerivativeValues(OutputValueViewType outputValues, const OutputValueViewType legendreValues, Intrepid2::ordinal_type n, ScalarType x)
Evaluate first derivatives of Legendre polynomials up to order n at a specified point,...
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
Integrated Legendre polynomials L_i for i>=1, defined for x in [0,1].
KOKKOS_INLINE_FUNCTION void shiftedScaledLegendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
Evaluate shifted, scaled Legendre polynomials up to order n at a specified point in [0,...
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedLegendreValues_dx(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
x derivative of shifted, scaled integrated Legendre polynomials L_i for i>=1, defined for x in [0,...
KOKKOS_INLINE_FUNCTION void shiftedScaledIntegratedJacobiValues_dt(OutputValueViewType outputValues, const OutputValueViewType jacobiValues, double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
t derivative of shifted, scaled integrated Jacobi polynomials L_i for i>=1, defined for x in [0,...
KOKKOS_INLINE_FUNCTION void shiftedScaledJacobiValues(OutputValueViewType outputValues, double alpha, Intrepid2::ordinal_type n, ScalarType x, ScalarTypeForScaling t)
Shifted, scaled Jacobi values, defined for x in [0,1].
KOKKOS_INLINE_FUNCTION void legendreValues(OutputValueViewType outputValues, Intrepid2::ordinal_type n, ScalarType x)
Evaluate Legendre polynomials up to order n at a specified point.
Contains definitions of custom data types in Intrepid2.
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, .