libMesh
Loading...
Searching...
No Matches
fe_lagrange_gll_shape_1D.h
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
19#ifndef LIBMESH_FE_LAGRANGE_GLL_SHAPE_1D_H
20#define LIBMESH_FE_LAGRANGE_GLL_SHAPE_1D_H
21
22// Local includes
23#include "libmesh/libmesh_common.h"
24#include "libmesh/enum_order.h"
25#include "libmesh/int_range.h"
26#include "libmesh/quadrature_gauss_lobatto.h"
27
28// C++ includes
29#include <algorithm>
30#include <array>
31#include <iterator>
32#include <vector>
33
34// The one-dimensional nodal basis on the Gauss-Lobatto points. The tensor product element
35// types build their shape functions from it.
36//
37// The n points are -1, 1, and the roots of the derivative of the Legendre polynomial of
38// degree n-1, which is why they are also called the Gauss-Lobatto-Legendre (GLL) points.
39// The basis reads them from QGaussLobatto, so it and the rule with the same number of
40// points use identical values. A basis and a quadrature rule that share their points are
41// collocated: each shape function is one at its own point and zero at the others. The mass
42// matrix assembled with a collocated rule is therefore diagonal for any mapping and element
43// shape. That requires the points to match exactly, which is why the basis takes them from
44// the rule.
45
46namespace libMesh
47{
48
53{
55 std::vector<Real> nodes;
56
58 std::vector<Real> weights;
59
61 std::vector<std::vector<Real>> deriv;
62
63#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
65 std::vector<std::vector<Real>> second;
66#endif
67};
68
69
75constexpr unsigned int fe_lagrange_gll_max_points_1D = 23;
76
77
81inline GLLBasis1D build_gll_basis_1D(const unsigned int n)
82{
84
85 // QGaussLobatto chooses its number of points from the order: a rule of n points is exact
86 // through degree 2n-3
87 QGaussLobatto rule(1, static_cast<Order>(2*n - 3));
88 rule.init(EDGE2);
89 libmesh_assert_equal_to(rule.n_points(), n);
90
91 b.nodes.resize(n);
92 for (const auto i : make_range(n))
93 b.nodes[i] = rule.qp(i)(0);
94
95 b.weights.resize(n);
96 for (const auto i : make_range(n))
97 {
98 Real product = 1.;
99 for (const auto j : make_range(n))
100 if (j != i)
101 product *= b.nodes[i] - b.nodes[j];
102 b.weights[i] = 1. / product;
103 }
104
105 // Entry (k,i) is the derivative of shape function i at point k. Off the diagonal it is
106 // (w_i/w_k)/(x_k - x_i). The shape functions sum to one, so each row sums to zero, and
107 // the diagonal entry is minus the sum of the rest of its row. That also gives the
108 // endpoint entries -p(p+1)/4 and p(p+1)/4, so they don't need a special case.
109 b.deriv.assign(n, std::vector<Real>(n, 0.));
110 for (const auto k : make_range(n))
111 {
112 Real row_sum = 0.;
113 for (const auto i : make_range(n))
114 if (i != k)
115 {
116 b.deriv[k][i] = (b.weights[i] / b.weights[k]) / (b.nodes[k] - b.nodes[i]);
117 row_sum += b.deriv[k][i];
118 }
119 b.deriv[k][k] = -row_sum;
120 }
121
122#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
123 // Off the diagonal, entry (k,i) of the second differentiation matrix is
124 // 2 D_ki (D_kk - 1/(x_k - x_i)), with D the first differentiation matrix. Each row sums to
125 // zero for the same reason as in D, which gives the diagonal.
126 b.second.assign(n, std::vector<Real>(n, 0.));
127 for (const auto k : make_range(n))
128 {
129 Real row_sum = 0.;
130 for (const auto i : make_range(n))
131 if (i != k)
132 {
133 b.second[k][i] =
134 2. * b.deriv[k][i] * (b.deriv[k][k] - 1. / (b.nodes[k] - b.nodes[i]));
135 row_sum += b.second[k][i];
136 }
137 b.second[k][k] = -row_sum;
138 }
139#endif
140
141 return b;
142}
143
152inline const GLLBasis1D & fe_lagrange_gll_1D_basis(const Order order)
153{
154 static const std::array<GLLBasis1D, fe_lagrange_gll_max_points_1D + 1> bases = []()
155 {
156 std::array<GLLBasis1D, fe_lagrange_gll_max_points_1D + 1> b;
157 for (const auto n : make_range(2u, fe_lagrange_gll_max_points_1D + 1))
158 b[n] = build_gll_basis_1D(n);
159 return b;
160 }();
161
162 const unsigned int n = static_cast<unsigned int>(order) + 1;
163
164 libmesh_error_msg_if(n < 2 || n > fe_lagrange_gll_max_points_1D,
165 "The Gauss-Lobatto nodal basis supports orders 1 through "
166 << fe_lagrange_gll_max_points_1D - 1 << ", but order " << order
167 << " was requested.");
168
169 return bases[n];
170}
171
172
181inline unsigned int fe_lagrange_gll_1D_point(const GLLBasis1D & b, const Real xi)
182{
183 const auto it = std::lower_bound(b.nodes.begin(), b.nodes.end(), xi);
184
185 if (it != b.nodes.end() && *it == xi)
186 return cast_int<unsigned int>(std::distance(b.nodes.begin(), it));
187
188 return invalid_uint;
189}
190
200 const unsigned int i,
201 const Real xi)
202{
203 const GLLBasis1D & b = fe_lagrange_gll_1D_basis(order);
204
205 libmesh_assert_less (i, b.nodes.size());
206
207 const unsigned int k = fe_lagrange_gll_1D_point(b, xi);
208 if (k != invalid_uint)
209 return (i == k) ? 1. : 0.;
210
211 Real numerator = 0., denominator = 0.;
212
213 for (const auto j : index_range(b.nodes))
214 {
215 const Real term = b.weights[j] / (xi - b.nodes[j]);
216
217 denominator += term;
218
219 if (j == i)
220 numerator = term;
221 }
222
223 return numerator / denominator;
224}
225
233template <typename Column>
235 const Real xi,
236 const Column & f)
237{
238 Real numerator = 0., denominator = 0.;
239
240 for (const auto k : index_range(b.nodes))
241 {
242 const Real term = b.weights[k] / (xi - b.nodes[k]);
243
244 numerator += term * f(k);
245 denominator += term;
246 }
247
248 return numerator / denominator;
249}
250
261 const unsigned int i,
262 const unsigned int libmesh_dbg_var(j),
263 const Real xi)
264{
265 // only d()/dxi in 1D!
266 libmesh_assert_equal_to (j, 0);
267
268 const GLLBasis1D & b = fe_lagrange_gll_1D_basis(order);
269
270 libmesh_assert_less (i, b.nodes.size());
271
272 const unsigned int k = fe_lagrange_gll_1D_point(b, xi);
273 if (k != invalid_uint)
274 return b.deriv[k][i];
275
276 return fe_lagrange_gll_1D_interpolate(b, xi, [&b, i](const std::size_t m)
277 { return b.deriv[m][i]; });
278}
279
280#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
289 const unsigned int i,
290 const unsigned int libmesh_dbg_var(j),
291 const Real xi)
292{
293 // only d^2()/dxi^2 in 1D!
294 libmesh_assert_equal_to (j, 0);
295
296 const GLLBasis1D & b = fe_lagrange_gll_1D_basis(order);
297
298 libmesh_assert_less (i, b.nodes.size());
299
300 const unsigned int k = fe_lagrange_gll_1D_point(b, xi);
301 if (k != invalid_uint)
302 return b.second[k][i];
303
304 return fe_lagrange_gll_1D_interpolate(b, xi, [&b, i](const std::size_t m)
305 { return b.second[m][i]; });
306}
307#endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
308
309} // namespace libMesh
310
311#endif // LIBMESH_FE_LAGRANGE_GLL_SHAPE_1D_H
unsigned int n_points() const
Definition quadrature.h:131
Point qp(const unsigned int i) const
Definition quadrature.h:179
virtual void init(const Elem &e, unsigned int p_level=invalid_uint)
Initializes the data structures for a quadrature rule for the element e.
Definition quadrature.C:65
This class implements Gauss-Lobatto quadrature for 1D elements and 2D/3D tensor product elements.
LIBMESH_DEVICE_INLINE void assign(const TypeVector< T2 > &)
Assign to this vector without creating a temporary.
static const Real b
The libMesh namespace provides an interface to certain functionality in the library.
unsigned int fe_lagrange_gll_1D_point(const GLLBasis1D &b, const Real xi)
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition libmesh.h:303
const GLLBasis1D & fe_lagrange_gll_1D_basis(const Order order)
Real fe_lagrange_gll_1D_shape_deriv(const Order order, const unsigned int i, const unsigned int libmesh_dbg_var(j), const Real xi)
constexpr unsigned int fe_lagrange_gll_max_points_1D
The number of points in the largest Gauss-Lobatto rule that QGaussLobatto tabulates,...
Real fe_lagrange_gll_1D_interpolate(const GLLBasis1D &b, const Real xi, const Column &f)
GLLBasis1D build_gll_basis_1D(const unsigned int n)
Real fe_lagrange_gll_1D_shape_second_deriv(const Order order, const unsigned int i, const unsigned int libmesh_dbg_var(j), const Real xi)
Real fe_lagrange_gll_1D_shape(const Order order, const unsigned int i, const Real xi)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176
The one-dimensional nodal basis of degree n-1 on n Gauss-Lobatto points.
std::vector< std::vector< Real > > deriv
deriv[k][i] is the derivative of the i'th shape function at the k'th point
std::vector< Real > nodes
The interpolation points in ascending order. The first is -1 and the last is 1.
std::vector< std::vector< Real > > second
second[k][i] is the second derivative of the i'th shape function at the k'th point
std::vector< Real > weights
The barycentric weights. The i'th is the reciprocal of .