deal.II version 9.7.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
fe_nedelec.cc
Go to the documentation of this file.
1// ------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 2013 - 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Part of the source code is dual licensed under Apache-2.0 WITH
9// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10// governing the source code and code contributions can be found in
11// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12//
13// ------------------------------------------------------------------------
14
20
22
25#include <deal.II/fe/fe_tools.h>
27#include <deal.II/fe/mapping.h>
28
29#include <deal.II/grid/tria.h>
31
33#include <deal.II/lac/vector.h>
34
35#include <iostream>
36#include <memory>
37#include <sstream>
38
40
41// #define DEBUG_NEDELEC
42
43namespace internal
44{
45 namespace FE_Nedelec
46 {
47 namespace
48 {
49 double
50 get_embedding_computation_tolerance(const unsigned int p)
51 {
52 // This heuristic was computed by monitoring the worst residual
53 // resulting from the least squares computation when computing
54 // the face embedding matrices in the FE_Nedelec constructor.
55 // The residual growth is exponential, but is bounded by this
56 // function up to degree 12.
57 return 1.e-15 * std::exp(std::pow(p, 1.075));
58 }
59 } // namespace
60 } // namespace FE_Nedelec
61} // namespace internal
62
63
64// TODO: implement the adjust_quad_dof_index_for_face_orientation_table and
65// adjust_line_dof_index_for_line_orientation_table fields, and write tests
66// similar to bits/face_orientation_and_fe_q_*
67
68template <int dim>
69FE_Nedelec<dim>::FE_Nedelec(const unsigned int order)
70 : FE_PolyTensor<dim>(
71 PolynomialsNedelec<dim>(order),
73 dim,
74 order + 1,
76 std::vector<bool>(PolynomialsNedelec<dim>::n_polynomials(order), true),
77 std::vector<ComponentMask>(PolynomialsNedelec<dim>::n_polynomials(order),
78 ComponentMask(std::vector<bool>(dim, true))))
79{
80#ifdef DEBUG_NEDELEC
81 deallog << get_name() << std::endl;
82#endif
83
84 Assert(dim >= 2, ExcImpossibleInDim(dim));
85
87 // First, initialize the
88 // generalized support points and
89 // quadrature weights, since they
90 // are required for interpolation.
92
93 // We already use the correct basis, so no basis transformation is required
94 // from the polynomial space we have described above to the one that is dual
95 // to the node functionals. As documented in the base class, this is
96 // expressed by setting the inverse node matrix to the empty matrix.
97 this->inverse_node_matrix.clear();
98
99 // do not initialize embedding and restriction here. these matrices are
100 // initialized on demand in get_restriction_matrix and
101 // get_prolongation_matrix
102
103#ifdef DEBUG_NEDELEC
104 deallog << "Face Embedding" << std::endl;
105#endif
107
108 // TODO: the implementation makes the assumption that all faces have the
109 // same number of dofs
111 const unsigned int face_no = 0;
112
113 for (unsigned int i = 0; i < GeometryInfo<dim>::max_children_per_face; ++i)
114 face_embeddings[i].reinit(this->n_dofs_per_face(face_no),
115 this->n_dofs_per_face(face_no));
116
118 *this,
119 face_embeddings,
120 0,
121 0,
122 internal::FE_Nedelec::get_embedding_computation_tolerance(order));
123
124 switch (dim)
125 {
126 case 1:
127 {
128 this->interface_constraints.reinit(0, 0);
129 break;
130 }
131
132 case 2:
133 {
134 this->interface_constraints.reinit(2 * this->n_dofs_per_face(face_no),
135 this->n_dofs_per_face(face_no));
136
137 for (unsigned int i = 0; i < GeometryInfo<2>::max_children_per_face;
138 ++i)
139 for (unsigned int j = 0; j < this->n_dofs_per_face(face_no); ++j)
140 for (unsigned int k = 0; k < this->n_dofs_per_face(face_no); ++k)
141 this->interface_constraints(i * this->n_dofs_per_face(face_no) +
142 j,
143 k) = face_embeddings[i](j, k);
144
145 break;
146 }
147
148 case 3:
149 {
150 this->interface_constraints.reinit(
151 4 * (this->n_dofs_per_face(face_no) - this->degree),
152 this->n_dofs_per_face(face_no));
153
154 unsigned int target_row = 0;
155
156 for (unsigned int i = 0; i < 2; ++i)
157 for (unsigned int j = this->degree; j < 2 * this->degree;
158 ++j, ++target_row)
159 for (unsigned int k = 0; k < this->n_dofs_per_face(face_no); ++k)
160 this->interface_constraints(target_row, k) =
161 face_embeddings[2 * i](j, k);
162
163 for (unsigned int i = 0; i < 2; ++i)
164 for (unsigned int j = 3 * this->degree;
165 j < GeometryInfo<3>::lines_per_face * this->degree;
166 ++j, ++target_row)
167 for (unsigned int k = 0; k < this->n_dofs_per_face(face_no); ++k)
168 this->interface_constraints(target_row, k) =
169 face_embeddings[i](j, k);
170
171 for (unsigned int i = 0; i < 2; ++i)
172 for (unsigned int j = 0; j < 2; ++j)
173 for (unsigned int k = i * this->degree;
174 k < (i + 1) * this->degree;
175 ++k, ++target_row)
176 for (unsigned int l = 0; l < this->n_dofs_per_face(face_no);
177 ++l)
178 this->interface_constraints(target_row, l) =
179 face_embeddings[i + 2 * j](k, l);
180
181 for (unsigned int i = 0; i < 2; ++i)
182 for (unsigned int j = 0; j < 2; ++j)
183 for (unsigned int k = (i + 2) * this->degree;
184 k < (i + 3) * this->degree;
185 ++k, ++target_row)
186 for (unsigned int l = 0; l < this->n_dofs_per_face(face_no);
187 ++l)
188 this->interface_constraints(target_row, l) =
189 face_embeddings[2 * i + j](k, l);
190
191 for (unsigned int i = 0; i < GeometryInfo<3>::max_children_per_face;
192 ++i)
193 for (unsigned int j =
194 GeometryInfo<3>::lines_per_face * this->degree;
195 j < this->n_dofs_per_face(face_no);
196 ++j, ++target_row)
197 for (unsigned int k = 0; k < this->n_dofs_per_face(face_no); ++k)
198 this->interface_constraints(target_row, k) =
199 face_embeddings[i](j, k);
200
201 break;
202 }
203
204 default:
206 }
207
208 // We need to initialize the dof permutation table and the one for the sign
209 // change.
211}
212
213
214template <int dim>
215void
217{
218 // The order of the Nedelec elements equals the tensor degree minus one,
219 // k = n - 1. In the three-dimensional space the Nedelec elements of the
220 // lowermost order, k = 0, have only 12 line (edge) dofs. The Nedelec
221 // elements of the higher orders, k > 0, have 3*(k+1)*(k+2)^2 dofs in
222 // total if dim=3. The dofs in a cell are distributed between lines
223 // (edges), quads (faces), and the hex (the interior of the cell) as the
224 // following:
225 //
226 // 12*(k+1) line dofs; (k+1) dofs per line.
227 // 2*6*k*(k+1) quad dofs; 2*k*(k+1) dofs per quad.
228 // 3*(k+2)^2*(k+1) hex dofs;
229 //
230 // The dofs are indexed in the following order: first all line dofs,
231 // then all quad dofs, and then all hex dofs.
232 //
233 // The line dofs need only sign adjustments. No permutation of line
234 // dofs is needed. The line dofs are treated by
235 // internal::FE_PolyTensor::get_dof_sign_change_nedelec(...)
236 // in fe_poly_tensor.cc.
237 //
238 // The hex dofs need no adjustments: they are not shared between
239 // neighbouring mesh cells.
240 //
241 // The two-dimensional Nedelec finite elements share no quad dofs between
242 // neighbouring mesh cells. The zero-order three-dimensional Nedelec
243 // finite elements have no quad dofs. Consequently, here we treat only
244 // quad dofs of the three-dimensional Nedelec finite elements of the
245 // higher orders, k>0. The questions how the curl looks like in the
246 // higher-dimensional spaces and what does it mean to be curl-conforming
247 // if dim>3 we leave unanswered.
248 //
249 // In this function we need to change some entries in the following two
250 // vectors of tables:
251 // adjust_quad_dof_index_for_face_orientation_table
252 // and
253 // adjust_quad_dof_sign_for_face_orientation_table.
254 // These tables specify the permutations and sign adjustments of the quad
255 // dofs only. The tables are already filled with zeros meaning no
256 // permutations or sign change are required. We need to change some
257 // entries of the tables such that the shape functions that correspond to
258 // the quad dofs and are shared between neighbouring cells have consistent
259 // orientations.
260 //
261 // The swap tables below describe the dof permutations and sign changes
262 // that need to be done. The function
263 // FE_Nedelec<dim>::initialize_quad_dof_index_permutation_and_sign_change()
264 // simply reads the information in the swap tables below and puts it into
265 // tables
266 // adjust_quad_dof_index_for_face_orientation_table
267 // and
268 // adjust_quad_dof_sign_for_face_orientation_table.
269 // A good question is: why don't we put the information into the tables of
270 // deal.II right away? The answer is the following. The information on the
271 // necessary dof permutations and sign changes is derived by plotting the
272 // shape functions and observing them on faces of different orientations.
273 // It is convenient to put the observations first in the format of the
274 // swap tables below and then convert the swap tables into the format used
275 // by deal.II.
276 //
277 // The dofs on a quad are indexed as the following:
278 //
279 // | x0, x1, x2, x3, ..., xk | y0, y1, y2, y3 ..., yk |
280 // | | |
281 // |<------ k*(k+1) --------->|<------ k*(k+1) -------->|
282 // | |
283 // |<------------------- 2*k*(k+1) -------------------->|
284 //
285 // Only one type of dof permutation is needed: swap between two dofs; one
286 // dof being xi, another yj. That is, if x4 is replaced with y7,
287 // then y7 must be replaced with x4. Such swaps can be ordered as
288 // illustrated by the following example:
289 //
290 // *
291 // y0, y9, y1, y2, ..., yk
292 // --------------------------- (swap)
293 // x0, x1, x2, x3, ..., xk
294 // *
295 //
296 // An x-dof below the line is swapped with the corresponding y-dof above
297 // the line. A dof marked by the asterisk must change its sign before the
298 // swap.
299 //
300 // The x-dofs are assumed to have the normal order. There is no need to
301 // encode it. Therefore, the swap tables need to encode the following
302 // information: indices of the y-dofs, the sign change of the x-dofs, and
303 // sign change of the y-dofs. The swap above is encoded as the following:
304 //
305 // swap = { 0, 9, 1, 2, ...., yk, // indices of the y-dofs
306 // 1, 0, 0, 0, ...., 0, // sign change of the x-dofs,
307 // 0, 1, 0, 0, ...., 0}; // sign change of the y-dofs.
308 //
309 // If no swap is needed, -1 is placed instead of the y-dof index.
310 //
311 // Such swaps are assembled into the swap table:
312 //
313 // swap_table = {swap_0, swap_1, ... swap_7};
314 //
315 // Each swap table contains eight swaps - one swap for each possible quad
316 // orientation. These are encoded in the standard way (i.e., orientation,
317 // rotation, flip). See the orientation module for more information.
318 //
319 // Nedelec elements of order k have their own swap table, swap_table_k.
320 // Recall, the swap_table_0 is empty as the Nedelec finite elements of the
321 // lowermost order have no quad dofs.
322
323 static const int c_swap_table_0 = 0;
324
325 static const int c_swap_table_1[8][3][2] = { // 0 1
326 {{-1, -1}, // 0
327 {0, 0},
328 {0, 0}},
329 {{0, 1}, // 1
330 {0, 0},
331 {0, 0}},
332 {{0, 1}, // 2
333 {1, 0},
334 {0, 0}},
335 {{-1, -1}, // 3
336 {0, 0},
337 {1, 0}},
338 {{-1, -1}, // 4
339 {1, 0},
340 {1, 0}},
341 {{0, 1}, // 5
342 {1, 0},
343 {1, 0}},
344 {{0, 1}, // 6
345 {0, 0},
346 {1, 0}},
347 {{-1, -1}, // 7
348 {1, 0},
349 {0, 0}}};
350
351 static const int c_swap_table_2[8][3][6] = {// 0 1 2 3 4 5
352 {{-1, -1, -1, -1, -1, -1}, // 0
353 {0, 0, 0, 0, 0, 0},
354 {0, 0, 0, 0, 0, 0}},
355 {{0, 3, 1, 4, 2, 5}, // 1
356 {0, 0, 0, 0, 0, 0},
357 {0, 0, 0, 0, 0, 0}},
358 {{0, 3, 1, 4, 2, 5}, // 2
359 {1, 1, 0, 0, 1, 1},
360 {0, 0, 0, 1, 1, 1}},
361 {{-1, -1, -1, -1, -1, -1}, // 3
362 {0, 1, 0, 1, 0, 1},
363 {1, 0, 1, 1, 0, 1}},
364 {{-1, -1, -1, -1, -1, -1}, // 4
365 {1, 0, 0, 1, 1, 0},
366 {1, 0, 1, 0, 1, 0}},
367 {{0, 3, 1, 4, 2, 5}, // 5
368 {1, 0, 0, 1, 1, 0},
369 {1, 0, 1, 0, 1, 0}},
370 {{0, 3, 1, 4, 2, 5}, // 6
371 {0, 1, 0, 1, 0, 1},
372 {1, 0, 1, 1, 0, 1}},
373 {{-1, -1, -1, -1, -1, -1}, // 7
374 {1, 1, 0, 0, 1, 1},
375 {0, 0, 0, 1, 1, 1}}};
376
377 static const int c_swap_table_3[8][3][12] = {
378 // 0 1 2 3 4 5 6 7 8 9 10 11
379 {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 0
380 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
381 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
382 {{0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11}, // 1
383 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
384 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
385 {{0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11}, // 2
386 {1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0},
387 {0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}},
388 {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 3
389 {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0},
390 {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}},
391 {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 4
392 {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
393 {1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0}},
394 {{0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11}, // 5
395 {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
396 {1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0}},
397 {{0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11}, // 6
398 {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0},
399 {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}},
400 {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 7
401 {1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0},
402 {0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0}}};
403
404 static const int c_swap_table_4[8][3][20] = {
405 // Swap sign_X and sign_Y rows if k=4. Why?...
406 // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
407 // 19
408 {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
409 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 0
410 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
411 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
412 {{0, 5, 10, 15, 1, 6, 11, 16, 2, 7,
413 12, 17, 3, 8, 13, 18, 4, 9, 14, 19}, // 1
414 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
415 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
416 {{0, 5, 10, 15, 1, 6, 11, 16, 2, 7,
417 12, 17, 3, 8, 13, 18, 4, 9, 14, 19}, // 2
418 {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1},
419 {1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1}},
420 {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
421 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 3
422 {1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1},
423 {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}},
424 {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
425 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 4
426 {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
427 {1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0}},
428 {{0, 5, 10, 15, 1, 6, 11, 16, 2, 7,
429 12, 17, 3, 8, 13, 18, 4, 9, 14, 19}, // 5
430 {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
431 {1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0}},
432 {{0, 5, 10, 15, 1, 6, 11, 16, 2, 7,
433 12, 17, 3, 8, 13, 18, 4, 9, 14, 19}, // 6
434 {1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1},
435 {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}},
436 {{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
437 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // 7
438 {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1},
439 {1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1}}};
440
441 static const int *swap_table_array[5] = {&c_swap_table_0,
442 &c_swap_table_1[0][0][0],
443 &c_swap_table_2[0][0][0],
444 &c_swap_table_3[0][0][0],
445 &c_swap_table_4[0][0][0]};
446
447 static const int row_length[5] = {0, 2, 6, 12, 20};
448 static const int table_size[5] = {
449 0, 8 * 3 * 2, 8 * 3 * 6, 8 * 3 * 12, 8 * 3 * 20};
450
451 // Only three-dimensional Nedelec finite elements are treated. The
452 // two-dimensional Nedelec finite elements only need sign adjustments of the
453 // line dofs. These adjustments are done by
454 // internal::FE_PolyTensor::get_dof_sign_change_nedelec(...)
455 // in fe_poly_tensor.cc. The notions of curl and curl-conforming finite
456 // elements in higher-dimensional spaces, dim >3, are somewhat unclear as
457 // curl, strictly peaking, exists only in the three-dimensional space.
458 if (dim != 3)
459 return;
460
461 const unsigned int k = this->tensor_degree() - 1;
462
463 // The Nedelec finite elements of the lowermost order have no quad dofs.
464 if (k == 0)
465 return;
466
467 // The finite element orders > 4 are not implemented.
469
470 // TODO: the implementation makes the assumption that all quads have the
471 // same number of dofs
473 const unsigned int face_no = 0;
474
475 Assert(
477 this->reference_cell().n_face_orientations(face_no) *
478 this->n_dofs_per_quad(face_no),
480
481 Assert(
483 this->reference_cell().n_face_orientations(face_no) *
484 this->n_dofs_per_quad(face_no),
486
487 // The 3D Nedelec finite elements have 2*k*(k+1) dofs per each quad.
488 Assert(2 * k * (k + 1) == this->n_dofs_per_quad(face_no), ExcInternalError());
489
490 const int *swap_table = swap_table_array[k];
491
492 const unsigned int half_dofs = k * (k + 1); // see below;
493
494 const int rl = row_length[k];
495 for (types::geometric_orientation combined_orientation = 0;
496 combined_orientation <
497 this->reference_cell().n_face_orientations(face_no);
498 ++combined_orientation)
499 {
500 // The dofs on a quad are indexed as the following:
501 //
502 // | x0, x1, x2, x3, ..., xk | y0, y1, y2, y3 ..., yk |
503 // | | |
504 // |-- half_ dofs = k*(k+1) --|-- half_dofs = k*(k+1) --|
505 // | |
506 // |-------------------- 2*k*(k+1) ---------------------|
507
508 for (unsigned int indx_x = 0; indx_x < half_dofs; indx_x++)
509 {
510 int offset = 3 * rl * combined_orientation + 0 * rl + indx_x;
511 Assert(offset < table_size[k], ExcInternalError());
512 int value = *(swap_table + offset);
513 Assert(value < table_size[k], ExcInternalError());
515
516 if (value != -1)
517 {
518 const unsigned int indx_y =
519 half_dofs + static_cast<unsigned int>(value);
520
521 // dofs swap
523 indx_x, combined_orientation) = indx_y - indx_x;
524
526 indx_y, combined_orientation) = indx_x - indx_y;
527 }
528
529 // dof sign change
530 offset = 3 * rl * combined_orientation + 1 * rl + indx_x;
531 Assert(offset < table_size[k], ExcInternalError());
532 value = *(swap_table + offset);
533 Assert((value == 0) || (value == 1), ExcInternalError());
534
536 indx_x, combined_orientation) = static_cast<bool>(value);
537
538
539 offset = 3 * rl * combined_orientation + 2 * rl + indx_x;
540 Assert(offset < table_size[k], ExcInternalError());
541 value = *(swap_table + offset);
542 Assert((value == 0) || (value == 1), ExcInternalError());
543
545 indx_x + half_dofs, combined_orientation) =
546 static_cast<bool>(value);
547 }
548 }
549
550 return;
551}
552
553
554template <int dim>
555std::string
557{
558 // note that the
559 // FETools::get_fe_by_name
560 // function depends on the
561 // particular format of the string
562 // this function returns, so they
563 // have to be kept in synch
564
565 std::ostringstream namebuf;
566 namebuf << "FE_Nedelec<" << dim << ">(" << this->degree - 1 << ")";
567
568 return namebuf.str();
569}
570
571
572template <int dim>
573std::unique_ptr<FiniteElement<dim, dim>>
575{
576 return std::make_unique<FE_Nedelec<dim>>(*this);
577}
578
579//---------------------------------------------------------------------------
580// Auxiliary and internal functions
581//---------------------------------------------------------------------------
582
583
584
585// Set the generalized support
586// points and precompute the
587// parts of the projection-based
588// interpolation, which does
589// not depend on the interpolated
590// function.
591template <>
592void
597
598
599
600template <>
601void
603{
604 const int dim = 2;
605
606 // TODO: the implementation makes the assumption that all faces have the
607 // same number of dofs
609 const unsigned int face_no = 0;
610
611 // Create polynomial basis.
612 const std::vector<Polynomials::Polynomial<double>> &lobatto_polynomials =
614 std::vector<Polynomials::Polynomial<double>> lobatto_polynomials_grad(order +
615 1);
616
617 for (unsigned int i = 0; i < lobatto_polynomials_grad.size(); ++i)
618 lobatto_polynomials_grad[i] = lobatto_polynomials[i + 1].derivative();
619
620 // Initialize quadratures to obtain
621 // quadrature points later on.
622 const QGauss<dim - 1> reference_edge_quadrature(order + 1);
623 const unsigned int n_edge_points = reference_edge_quadrature.size();
624 const unsigned int n_boundary_points =
625 GeometryInfo<dim>::lines_per_cell * n_edge_points;
626 const Quadrature<dim> edge_quadrature =
628 reference_edge_quadrature);
629
630 this->generalized_face_support_points[face_no].resize(n_edge_points);
631
632 // Create face support points.
633 for (unsigned int q_point = 0; q_point < n_edge_points; ++q_point)
634 this->generalized_face_support_points[face_no][q_point] =
635 reference_edge_quadrature.point(q_point);
636
637 if (order > 0)
638 {
639 // If the polynomial degree is positive
640 // we have support points on the faces
641 // and in the interior of a cell.
642 const QGauss<dim> quadrature(order + 1);
643 const unsigned int n_interior_points = quadrature.size();
644
645 this->generalized_support_points.resize(n_boundary_points +
646 n_interior_points);
647 boundary_weights.reinit(n_edge_points, order);
648
649 for (unsigned int q_point = 0; q_point < n_edge_points; ++q_point)
650 {
651 for (unsigned int line = 0; line < GeometryInfo<dim>::lines_per_cell;
652 ++line)
653 this->generalized_support_points[line * n_edge_points + q_point] =
655 this->reference_cell(),
656 line,
658 n_edge_points) +
659 q_point);
660
661 for (unsigned int i = 0; i < order; ++i)
662 boundary_weights(q_point, i) =
663 reference_edge_quadrature.weight(q_point) *
664 lobatto_polynomials_grad[i + 1].value(
665 this->generalized_face_support_points[face_no][q_point][0]);
666 }
667
668 for (unsigned int q_point = 0; q_point < n_interior_points; ++q_point)
669 this->generalized_support_points[q_point + n_boundary_points] =
670 quadrature.point(q_point);
671 }
672
673 else
674 {
675 // In this case we only need support points
676 // on the faces of a cell.
677 this->generalized_support_points.resize(n_boundary_points);
678
679 for (unsigned int line = 0; line < GeometryInfo<dim>::lines_per_cell;
680 ++line)
681 for (unsigned int q_point = 0; q_point < n_edge_points; ++q_point)
682 this->generalized_support_points[line * n_edge_points + q_point] =
684 this->reference_cell(),
685 line,
687 n_edge_points) +
688 q_point);
689 }
690}
691
692
693
694template <>
695void
697{
698 const int dim = 3;
699
700 // TODO: the implementation makes the assumption that all faces have the
701 // same number of dofs
703 const unsigned int face_no = 0;
704
705 // Create polynomial basis.
706 const std::vector<Polynomials::Polynomial<double>> &lobatto_polynomials =
708 std::vector<Polynomials::Polynomial<double>> lobatto_polynomials_grad(order +
709 1);
710
711 for (unsigned int i = 0; i < lobatto_polynomials_grad.size(); ++i)
712 lobatto_polynomials_grad[i] = lobatto_polynomials[i + 1].derivative();
713
714 // Initialize quadratures to obtain
715 // quadrature points later on.
716 const QGauss<1> reference_edge_quadrature(order + 1);
717 const unsigned int n_edge_points = reference_edge_quadrature.size();
718 const Quadrature<dim - 1> &edge_quadrature =
720 ReferenceCells::get_hypercube<dim - 1>(), reference_edge_quadrature);
721
722 if (order > 0)
723 {
724 // If the polynomial order is positive
725 // we have support points on the edges,
726 // faces and in the interior of a cell.
727 const QGauss<dim - 1> reference_face_quadrature(order + 1);
728 const unsigned int n_face_points = reference_face_quadrature.size();
729 const unsigned int n_boundary_points =
730 GeometryInfo<dim>::lines_per_cell * n_edge_points +
731 GeometryInfo<dim>::faces_per_cell * n_face_points;
732 const QGauss<dim> quadrature(order + 1);
733 const unsigned int n_interior_points = quadrature.size();
734
735 boundary_weights.reinit(n_edge_points + n_face_points,
736 2 * (order + 1) * order);
737 this->generalized_face_support_points[face_no].resize(4 * n_edge_points +
738 n_face_points);
739 this->generalized_support_points.resize(n_boundary_points +
740 n_interior_points);
741
742 // Create support points on edges.
743 for (unsigned int q_point = 0; q_point < n_edge_points; ++q_point)
744 {
745 for (unsigned int line = 0;
746 line < GeometryInfo<dim - 1>::lines_per_cell;
747 ++line)
748 this
749 ->generalized_face_support_points[face_no][line * n_edge_points +
750 q_point] =
751 edge_quadrature.point(
754 line,
756 n_edge_points) +
757 q_point);
758
759 for (unsigned int i = 0; i < 2; ++i)
760 for (unsigned int j = 0; j < 2; ++j)
761 {
762 this->generalized_support_points[q_point +
763 (i + 4 * j) * n_edge_points] =
764 Point<dim>(i, reference_edge_quadrature.point(q_point)[0], j);
765 this->generalized_support_points[q_point + (i + 4 * j + 2) *
766 n_edge_points] =
767 Point<dim>(reference_edge_quadrature.point(q_point)[0], i, j);
768 this->generalized_support_points[q_point + (i + 2 * (j + 4)) *
769 n_edge_points] =
770 Point<dim>(i, j, reference_edge_quadrature.point(q_point)[0]);
771 }
772
773 for (unsigned int i = 0; i < order; ++i)
774 boundary_weights(q_point, i) =
775 reference_edge_quadrature.weight(q_point) *
776 lobatto_polynomials_grad[i + 1].value(
777 this->generalized_face_support_points[face_no][q_point][1]);
778 }
779
780 // Create support points on faces.
781 for (unsigned int q_point = 0; q_point < n_face_points; ++q_point)
782 {
784 [q_point + 4 * n_edge_points] =
785 reference_face_quadrature.point(q_point);
786
787 for (unsigned int i = 0; i <= order; ++i)
788 for (unsigned int j = 0; j < order; ++j)
789 {
790 boundary_weights(q_point + n_edge_points, 2 * (i * order + j)) =
791 reference_face_quadrature.weight(q_point) *
792 lobatto_polynomials_grad[i].value(
794 [face_no][q_point + 4 * n_edge_points][0]) *
795 lobatto_polynomials[j + 2].value(
797 [face_no][q_point + 4 * n_edge_points][1]);
798 boundary_weights(q_point + n_edge_points,
799 2 * (i * order + j) + 1) =
800 reference_face_quadrature.weight(q_point) *
801 lobatto_polynomials_grad[i].value(
803 [face_no][q_point + 4 * n_edge_points][1]) *
804 lobatto_polynomials[j + 2].value(
806 [face_no][q_point + 4 * n_edge_points][0]);
807 }
808 }
809
810 const Quadrature<dim> &face_quadrature =
812 reference_face_quadrature);
813
814 for (const unsigned int face : GeometryInfo<dim>::face_indices())
815 for (unsigned int q_point = 0; q_point < n_face_points; ++q_point)
816 {
817 this->generalized_support_points[face * n_face_points + q_point +
819 n_edge_points] =
821 this->reference_cell(),
822 face,
824 n_face_points) +
825 q_point);
826 }
827
828 // Create support points in the interior.
829 for (unsigned int q_point = 0; q_point < n_interior_points; ++q_point)
830 this->generalized_support_points[q_point + n_boundary_points] =
831 quadrature.point(q_point);
832 }
833
834 else
835 {
836 this->generalized_face_support_points[face_no].resize(4 * n_edge_points);
837 this->generalized_support_points.resize(
838 GeometryInfo<dim>::lines_per_cell * n_edge_points);
839
840 for (unsigned int q_point = 0; q_point < n_edge_points; ++q_point)
841 {
842 for (unsigned int line = 0;
843 line < GeometryInfo<dim - 1>::lines_per_cell;
844 ++line)
845 this
846 ->generalized_face_support_points[face_no][line * n_edge_points +
847 q_point] =
848 edge_quadrature.point(
851 line,
853 n_edge_points) +
854 q_point);
855
856 for (unsigned int i = 0; i < 2; ++i)
857 for (unsigned int j = 0; j < 2; ++j)
858 {
859 this->generalized_support_points[q_point +
860 (i + 4 * j) * n_edge_points] =
861 Point<dim>(i, reference_edge_quadrature.point(q_point)[0], j);
862 this->generalized_support_points[q_point + (i + 4 * j + 2) *
863 n_edge_points] =
864 Point<dim>(reference_edge_quadrature.point(q_point)[0], i, j);
865 this->generalized_support_points[q_point + (i + 2 * (j + 4)) *
866 n_edge_points] =
867 Point<dim>(i, j, reference_edge_quadrature.point(q_point)[0]);
868 }
869 }
870 }
871}
872
873
874
875// Set the restriction matrices.
876template <>
877void
879{
880 // there is only one refinement case in 1d,
881 // which is the isotropic one
882 for (unsigned int i = 0; i < GeometryInfo<1>::max_children_per_cell; ++i)
883 this->restriction[0][i].reinit(0, 0);
884}
885
886
887
888// Restriction operator
889template <int dim>
890void
892{
893 // This function does the same as the
894 // function interpolate further below.
895 // But since the functions, which we
896 // interpolate here, are discontinuous
897 // we have to use more quadrature
898 // points as in interpolate.
899 const QGauss<1> edge_quadrature(2 * this->degree);
900 const std::vector<Point<1>> &edge_quadrature_points =
901 edge_quadrature.get_points();
902 const unsigned int n_edge_quadrature_points = edge_quadrature.size();
903 const unsigned int index = RefinementCase<dim>::isotropic_refinement - 1;
904
905 switch (dim)
906 {
907 case 2:
908 {
909 // First interpolate the shape
910 // functions of the child cells
911 // to the lowest order shape
912 // functions of the parent cell.
913 for (unsigned int dof = 0; dof < this->n_dofs_per_cell(); ++dof)
914 for (unsigned int q_point = 0; q_point < n_edge_quadrature_points;
915 ++q_point)
916 {
917 const double weight = 2.0 * edge_quadrature.weight(q_point);
918
919 if (edge_quadrature_points[q_point][0] < 0.5)
920 {
921 Point<dim> quadrature_point(
922 0.0, 2.0 * edge_quadrature_points[q_point][0]);
923
924 this->restriction[index][0](0, dof) +=
925 weight *
926 this->shape_value_component(dof, quadrature_point, 1);
927 quadrature_point[0] = 1.0;
928 this->restriction[index][1](this->degree, dof) +=
929 weight *
930 this->shape_value_component(dof, quadrature_point, 1);
931 quadrature_point[0] = quadrature_point[1];
932 quadrature_point[1] = 0.0;
933 this->restriction[index][0](2 * this->degree, dof) +=
934 weight *
935 this->shape_value_component(dof, quadrature_point, 0);
936 quadrature_point[1] = 1.0;
937 this->restriction[index][2](3 * this->degree, dof) +=
938 weight *
939 this->shape_value_component(dof, quadrature_point, 0);
940 }
941
942 else
943 {
944 Point<dim> quadrature_point(
945 0.0, 2.0 * edge_quadrature_points[q_point][0] - 1.0);
946
947 this->restriction[index][2](0, dof) +=
948 weight *
949 this->shape_value_component(dof, quadrature_point, 1);
950 quadrature_point[0] = 1.0;
951 this->restriction[index][3](this->degree, dof) +=
952 weight *
953 this->shape_value_component(dof, quadrature_point, 1);
954 quadrature_point[0] = quadrature_point[1];
955 quadrature_point[1] = 0.0;
956 this->restriction[index][1](2 * this->degree, dof) +=
957 weight *
958 this->shape_value_component(dof, quadrature_point, 0);
959 quadrature_point[1] = 1.0;
960 this->restriction[index][3](3 * this->degree, dof) +=
961 weight *
962 this->shape_value_component(dof, quadrature_point, 0);
963 }
964 }
965
966 // Then project the shape functions
967 // of the child cells to the higher
968 // order shape functions of the
969 // parent cell.
970 if (this->degree > 1)
971 {
972 const unsigned int deg = this->degree - 1;
973 const std::vector<Polynomials::Polynomial<double>>
974 &legendre_polynomials =
976 FullMatrix<double> system_matrix_inv(deg, deg);
977
978 {
979 FullMatrix<double> assembling_matrix(deg,
980 n_edge_quadrature_points);
981
982 for (unsigned int q_point = 0;
983 q_point < n_edge_quadrature_points;
984 ++q_point)
985 {
986 const double weight =
987 std::sqrt(edge_quadrature.weight(q_point));
988
989 for (unsigned int i = 0; i < deg; ++i)
990 assembling_matrix(i, q_point) =
991 weight * legendre_polynomials[i + 1].value(
992 edge_quadrature_points[q_point][0]);
993 }
994
995 FullMatrix<double> system_matrix(deg, deg);
996
997 assembling_matrix.mTmult(system_matrix, assembling_matrix);
998 system_matrix_inv.invert(system_matrix);
999 }
1000
1001 FullMatrix<double> solution(this->degree - 1, 4);
1002 FullMatrix<double> system_rhs(this->degree - 1, 4);
1003 Vector<double> tmp(4);
1004
1005 for (unsigned int dof = 0; dof < this->n_dofs_per_cell(); ++dof)
1006 for (unsigned int i = 0; i < 2; ++i)
1007 {
1008 system_rhs = 0.0;
1009
1010 for (unsigned int q_point = 0;
1011 q_point < n_edge_quadrature_points;
1012 ++q_point)
1013 {
1014 const double weight = edge_quadrature.weight(q_point);
1015 const Point<dim> quadrature_point_0(
1016 i, edge_quadrature_points[q_point][0]);
1017 const Point<dim> quadrature_point_1(
1018 edge_quadrature_points[q_point][0], i);
1019
1020 if (edge_quadrature_points[q_point][0] < 0.5)
1021 {
1022 Point<dim> quadrature_point_2(
1023 i, 2.0 * edge_quadrature_points[q_point][0]);
1024
1025 tmp(0) =
1026 weight *
1027 (2.0 * this->shape_value_component(
1028 dof, quadrature_point_2, 1) -
1029 this->restriction[index][i](i * this->degree,
1030 dof) *
1031 this->shape_value_component(i * this->degree,
1032 quadrature_point_0,
1033 1));
1034 tmp(1) =
1035 -1.0 * weight *
1036 this->restriction[index][i + 2](i * this->degree,
1037 dof) *
1038 this->shape_value_component(i * this->degree,
1039 quadrature_point_0,
1040 1);
1041 quadrature_point_2 = Point<dim>(
1042 2.0 * edge_quadrature_points[q_point][0], i);
1043 tmp(2) =
1044 weight *
1045 (2.0 * this->shape_value_component(
1046 dof, quadrature_point_2, 0) -
1047 this->restriction[index][2 * i]((i + 2) *
1048 this->degree,
1049 dof) *
1050 this->shape_value_component((i + 2) *
1051 this->degree,
1052 quadrature_point_1,
1053 0));
1054 tmp(3) =
1055 -1.0 * weight *
1056 this->restriction[index][2 * i + 1](
1057 (i + 2) * this->degree, dof) *
1059 (i + 2) * this->degree, quadrature_point_1, 0);
1060 }
1061
1062 else
1063 {
1064 tmp(0) =
1065 -1.0 * weight *
1066 this->restriction[index][i](i * this->degree,
1067 dof) *
1068 this->shape_value_component(i * this->degree,
1069 quadrature_point_0,
1070 1);
1071
1072 Point<dim> quadrature_point_2(
1073 i,
1074 2.0 * edge_quadrature_points[q_point][0] - 1.0);
1075
1076 tmp(1) =
1077 weight *
1078 (2.0 * this->shape_value_component(
1079 dof, quadrature_point_2, 1) -
1080 this->restriction[index][i + 2](i * this->degree,
1081 dof) *
1082 this->shape_value_component(i * this->degree,
1083 quadrature_point_0,
1084 1));
1085 tmp(2) =
1086 -1.0 * weight *
1087 this->restriction[index][2 * i]((i + 2) *
1088 this->degree,
1089 dof) *
1091 (i + 2) * this->degree, quadrature_point_1, 0);
1092 quadrature_point_2 = Point<dim>(
1093 2.0 * edge_quadrature_points[q_point][0] - 1.0,
1094 i);
1095 tmp(3) =
1096 weight *
1097 (2.0 * this->shape_value_component(
1098 dof, quadrature_point_2, 0) -
1099 this->restriction[index][2 * i + 1](
1100 (i + 2) * this->degree, dof) *
1101 this->shape_value_component((i + 2) *
1102 this->degree,
1103 quadrature_point_1,
1104 0));
1105 }
1106
1107 for (unsigned int j = 0; j < this->degree - 1; ++j)
1108 {
1109 const double L_j =
1110 legendre_polynomials[j + 1].value(
1111 edge_quadrature_points[q_point][0]);
1112
1113 for (unsigned int k = 0; k < tmp.size(); ++k)
1114 system_rhs(j, k) += tmp(k) * L_j;
1115 }
1116 }
1117
1118 system_matrix_inv.mmult(solution, system_rhs);
1119
1120 for (unsigned int j = 0; j < this->degree - 1; ++j)
1121 for (unsigned int k = 0; k < 2; ++k)
1122 {
1123 if (std::abs(solution(j, k)) > 1e-14)
1124 this->restriction[index][i + 2 * k](
1125 i * this->degree + j + 1, dof) = solution(j, k);
1126
1127 if (std::abs(solution(j, k + 2)) > 1e-14)
1128 this->restriction[index][2 * i + k](
1129 (i + 2) * this->degree + j + 1, dof) =
1130 solution(j, k + 2);
1131 }
1132 }
1133
1134 const QGauss<dim> quadrature(2 * this->degree);
1135 const std::vector<Point<dim>> &quadrature_points =
1136 quadrature.get_points();
1137 const std::vector<Polynomials::Polynomial<double>>
1138 &lobatto_polynomials =
1140 const unsigned int n_boundary_dofs =
1142 const unsigned int n_quadrature_points = quadrature.size();
1143
1144 {
1145 FullMatrix<double> assembling_matrix((this->degree - 1) *
1146 this->degree,
1147 n_quadrature_points);
1148
1149 for (unsigned int q_point = 0; q_point < n_quadrature_points;
1150 ++q_point)
1151 {
1152 const double weight = std::sqrt(quadrature.weight(q_point));
1153
1154 for (unsigned int i = 0; i < this->degree; ++i)
1155 {
1156 const double L_i =
1157 weight * legendre_polynomials[i].value(
1158 quadrature_points[q_point][0]);
1159
1160 for (unsigned int j = 0; j < this->degree - 1; ++j)
1161 assembling_matrix(i * (this->degree - 1) + j,
1162 q_point) =
1163 L_i * lobatto_polynomials[j + 2].value(
1164 quadrature_points[q_point][1]);
1165 }
1166 }
1167
1168 FullMatrix<double> system_matrix(assembling_matrix.m(),
1169 assembling_matrix.m());
1170
1171 assembling_matrix.mTmult(system_matrix, assembling_matrix);
1172 system_matrix_inv.reinit(system_matrix.m(), system_matrix.m());
1173 system_matrix_inv.invert(system_matrix);
1174 }
1175
1176 solution.reinit(system_matrix_inv.m(), 8);
1177 system_rhs.reinit(system_matrix_inv.m(), 8);
1178 tmp.reinit(8);
1179
1180 for (unsigned int dof = 0; dof < this->n_dofs_per_cell(); ++dof)
1181 {
1182 system_rhs = 0.0;
1183
1184 for (unsigned int q_point = 0; q_point < n_quadrature_points;
1185 ++q_point)
1186 {
1187 tmp = 0.0;
1188
1189 if (quadrature_points[q_point][0] < 0.5)
1190 {
1191 if (quadrature_points[q_point][1] < 0.5)
1192 {
1193 const Point<dim> quadrature_point(
1194 2.0 * quadrature_points[q_point][0],
1195 2.0 * quadrature_points[q_point][1]);
1196
1197 tmp(0) += 2.0 * this->shape_value_component(
1198 dof, quadrature_point, 0);
1199 tmp(1) += 2.0 * this->shape_value_component(
1200 dof, quadrature_point, 1);
1201 }
1202
1203 else
1204 {
1205 const Point<dim> quadrature_point(
1206 2.0 * quadrature_points[q_point][0],
1207 2.0 * quadrature_points[q_point][1] - 1.0);
1208
1209 tmp(4) += 2.0 * this->shape_value_component(
1210 dof, quadrature_point, 0);
1211 tmp(5) += 2.0 * this->shape_value_component(
1212 dof, quadrature_point, 1);
1213 }
1214 }
1215
1216 else if (quadrature_points[q_point][1] < 0.5)
1217 {
1218 const Point<dim> quadrature_point(
1219 2.0 * quadrature_points[q_point][0] - 1.0,
1220 2.0 * quadrature_points[q_point][1]);
1221
1222 tmp(2) +=
1223 2.0 * this->shape_value_component(dof,
1224 quadrature_point,
1225 0);
1226 tmp(3) +=
1227 2.0 * this->shape_value_component(dof,
1228 quadrature_point,
1229 1);
1230 }
1231
1232 else
1233 {
1234 const Point<dim> quadrature_point(
1235 2.0 * quadrature_points[q_point][0] - 1.0,
1236 2.0 * quadrature_points[q_point][1] - 1.0);
1237
1238 tmp(6) +=
1239 2.0 * this->shape_value_component(dof,
1240 quadrature_point,
1241 0);
1242 tmp(7) +=
1243 2.0 * this->shape_value_component(dof,
1244 quadrature_point,
1245 1);
1246 }
1247
1248 for (unsigned int i = 0; i < 2; ++i)
1249 for (unsigned int j = 0; j < this->degree; ++j)
1250 {
1251 tmp(2 * i) -=
1252 this->restriction[index][i](j + 2 * this->degree,
1253 dof) *
1255 j + 2 * this->degree,
1256 quadrature_points[q_point],
1257 0);
1258 tmp(2 * i + 1) -=
1259 this->restriction[index][i](i * this->degree + j,
1260 dof) *
1262 i * this->degree + j,
1263 quadrature_points[q_point],
1264 1);
1265 tmp(2 * (i + 2)) -= this->restriction[index][i + 2](
1266 j + 3 * this->degree, dof) *
1268 j + 3 * this->degree,
1269 quadrature_points[q_point],
1270 0);
1271 tmp(2 * i + 5) -= this->restriction[index][i + 2](
1272 i * this->degree + j, dof) *
1274 i * this->degree + j,
1275 quadrature_points[q_point],
1276 1);
1277 }
1278
1279 tmp *= quadrature.weight(q_point);
1280
1281 for (unsigned int i = 0; i < this->degree; ++i)
1282 {
1283 const double L_i_0 = legendre_polynomials[i].value(
1284 quadrature_points[q_point][0]);
1285 const double L_i_1 = legendre_polynomials[i].value(
1286 quadrature_points[q_point][1]);
1287
1288 for (unsigned int j = 0; j < this->degree - 1; ++j)
1289 {
1290 const double l_j_0 =
1291 L_i_0 * lobatto_polynomials[j + 2].value(
1292 quadrature_points[q_point][1]);
1293 const double l_j_1 =
1294 L_i_1 * lobatto_polynomials[j + 2].value(
1295 quadrature_points[q_point][0]);
1296
1297 for (unsigned int k = 0; k < 4; ++k)
1298 {
1299 system_rhs(i * (this->degree - 1) + j,
1300 2 * k) += tmp(2 * k) * l_j_0;
1301 system_rhs(i * (this->degree - 1) + j,
1302 2 * k + 1) +=
1303 tmp(2 * k + 1) * l_j_1;
1304 }
1305 }
1306 }
1307 }
1308
1309 system_matrix_inv.mmult(solution, system_rhs);
1310
1311 for (unsigned int i = 0; i < this->degree; ++i)
1312 for (unsigned int j = 0; j < this->degree - 1; ++j)
1313 for (unsigned int k = 0; k < 4; ++k)
1314 {
1315 if (std::abs(solution(i * (this->degree - 1) + j,
1316 2 * k)) > 1e-14)
1317 this->restriction[index][k](i * (this->degree - 1) +
1318 j + n_boundary_dofs,
1319 dof) =
1320 solution(i * (this->degree - 1) + j, 2 * k);
1321
1322 if (std::abs(solution(i * (this->degree - 1) + j,
1323 2 * k + 1)) > 1e-14)
1324 this->restriction[index][k](
1325 i + (this->degree - 1 + j) * this->degree +
1326 n_boundary_dofs,
1327 dof) =
1328 solution(i * (this->degree - 1) + j, 2 * k + 1);
1329 }
1330 }
1331 }
1332
1333 break;
1334 }
1335
1336 case 3:
1337 {
1338 // First interpolate the shape
1339 // functions of the child cells
1340 // to the lowest order shape
1341 // functions of the parent cell.
1342 for (unsigned int dof = 0; dof < this->n_dofs_per_cell(); ++dof)
1343 for (unsigned int q_point = 0; q_point < n_edge_quadrature_points;
1344 ++q_point)
1345 {
1346 const double weight = 2.0 * edge_quadrature.weight(q_point);
1347
1348 if (edge_quadrature_points[q_point][0] < 0.5)
1349 for (unsigned int i = 0; i < 2; ++i)
1350 for (unsigned int j = 0; j < 2; ++j)
1351 {
1352 Point<dim> quadrature_point(
1353 i, 2.0 * edge_quadrature_points[q_point][0], j);
1354
1355 this->restriction[index][i + 4 * j]((i + 4 * j) *
1356 this->degree,
1357 dof) +=
1358 weight *
1359 this->shape_value_component(dof, quadrature_point, 1);
1360 quadrature_point =
1361 Point<dim>(2.0 * edge_quadrature_points[q_point][0],
1362 i,
1363 j);
1364 this->restriction[index][2 * (i + 2 * j)](
1365 (i + 4 * j + 2) * this->degree, dof) +=
1366 weight *
1367 this->shape_value_component(dof, quadrature_point, 0);
1368 quadrature_point =
1369 Point<dim>(i,
1370 j,
1371 2.0 * edge_quadrature_points[q_point][0]);
1372 this->restriction[index][i + 2 * j]((i + 2 * (j + 4)) *
1373 this->degree,
1374 dof) +=
1375 weight *
1376 this->shape_value_component(dof, quadrature_point, 2);
1377 }
1378
1379 else
1380 for (unsigned int i = 0; i < 2; ++i)
1381 for (unsigned int j = 0; j < 2; ++j)
1382 {
1383 Point<dim> quadrature_point(
1384 i, 2.0 * edge_quadrature_points[q_point][0] - 1.0, j);
1385
1386 this->restriction[index][i + 4 * j + 2]((i + 4 * j) *
1387 this->degree,
1388 dof) +=
1389 weight *
1390 this->shape_value_component(dof, quadrature_point, 1);
1391 quadrature_point = Point<dim>(
1392 2.0 * edge_quadrature_points[q_point][0] - 1.0, i, j);
1393 this->restriction[index][2 * (i + 2 * j) + 1](
1394 (i + 4 * j + 2) * this->degree, dof) +=
1395 weight *
1396 this->shape_value_component(dof, quadrature_point, 0);
1397 quadrature_point = Point<dim>(
1398 i, j, 2.0 * edge_quadrature_points[q_point][0] - 1.0);
1399 this->restriction[index][i + 2 * (j + 2)](
1400 (i + 2 * (j + 4)) * this->degree, dof) +=
1401 weight *
1402 this->shape_value_component(dof, quadrature_point, 2);
1403 }
1404 }
1405
1406 // Then project the shape functions
1407 // of the child cells to the higher
1408 // order shape functions of the
1409 // parent cell.
1410 if (this->degree > 1)
1411 {
1412 const unsigned int deg = this->degree - 1;
1413 const std::vector<Polynomials::Polynomial<double>>
1414 &legendre_polynomials =
1416 FullMatrix<double> system_matrix_inv(deg, deg);
1417
1418 {
1419 FullMatrix<double> assembling_matrix(deg,
1420 n_edge_quadrature_points);
1421
1422 for (unsigned int q_point = 0;
1423 q_point < n_edge_quadrature_points;
1424 ++q_point)
1425 {
1426 const double weight =
1427 std::sqrt(edge_quadrature.weight(q_point));
1428
1429 for (unsigned int i = 0; i < deg; ++i)
1430 assembling_matrix(i, q_point) =
1431 weight * legendre_polynomials[i + 1].value(
1432 edge_quadrature_points[q_point][0]);
1433 }
1434
1435 FullMatrix<double> system_matrix(deg, deg);
1436
1437 assembling_matrix.mTmult(system_matrix, assembling_matrix);
1438 system_matrix_inv.invert(system_matrix);
1439 }
1440
1441 FullMatrix<double> solution(deg, 6);
1442 FullMatrix<double> system_rhs(deg, 6);
1443 Vector<double> tmp(6);
1444
1445 for (unsigned int i = 0; i < 2; ++i)
1446 for (unsigned int j = 0; j < 2; ++j)
1447 for (unsigned int dof = 0; dof < this->n_dofs_per_cell();
1448 ++dof)
1449 {
1450 system_rhs = 0.0;
1451
1452 for (unsigned int q_point = 0;
1453 q_point < n_edge_quadrature_points;
1454 ++q_point)
1455 {
1456 const double weight = edge_quadrature.weight(q_point);
1457 const Point<dim> quadrature_point_0(
1458 i, edge_quadrature_points[q_point][0], j);
1459 const Point<dim> quadrature_point_1(
1460 edge_quadrature_points[q_point][0], i, j);
1461 const Point<dim> quadrature_point_2(
1462 i, j, edge_quadrature_points[q_point][0]);
1463
1464 if (edge_quadrature_points[q_point][0] < 0.5)
1465 {
1466 Point<dim> quadrature_point_3(
1467 i, 2.0 * edge_quadrature_points[q_point][0], j);
1468
1469 tmp(0) =
1470 weight * (2.0 * this->shape_value_component(
1471 dof, quadrature_point_3, 1) -
1472 this->restriction[index][i + 4 * j](
1473 (i + 4 * j) * this->degree, dof) *
1475 (i + 4 * j) * this->degree,
1476 quadrature_point_0,
1477 1));
1478 tmp(1) =
1479 -1.0 * weight *
1480 this->restriction[index][i + 4 * j + 2](
1481 (i + 4 * j) * this->degree, dof) *
1482 this->shape_value_component((i + 4 * j) *
1483 this->degree,
1484 quadrature_point_0,
1485 1);
1486 quadrature_point_3 = Point<dim>(
1487 2.0 * edge_quadrature_points[q_point][0], i, j);
1488 tmp(2) =
1489 weight *
1490 (2.0 * this->shape_value_component(
1491 dof, quadrature_point_3, 0) -
1492 this->restriction[index][2 * (i + 2 * j)](
1493 (i + 4 * j + 2) * this->degree, dof) *
1495 (i + 4 * j + 2) * this->degree,
1496 quadrature_point_1,
1497 0));
1498 tmp(3) =
1499 -1.0 * weight *
1500 this->restriction[index][2 * (i + 2 * j) + 1](
1501 (i + 4 * j + 2) * this->degree, dof) *
1502 this->shape_value_component((i + 4 * j + 2) *
1503 this->degree,
1504 quadrature_point_1,
1505 0);
1506 quadrature_point_3 = Point<dim>(
1507 i, j, 2.0 * edge_quadrature_points[q_point][0]);
1508 tmp(4) =
1509 weight *
1510 (2.0 * this->shape_value_component(
1511 dof, quadrature_point_3, 2) -
1512 this->restriction[index][i + 2 * j](
1513 (i + 2 * (j + 4)) * this->degree, dof) *
1514 this->shape_value_component(
1515 (i + 2 * (j + 4)) * this->degree,
1516 quadrature_point_2,
1517 2));
1518 tmp(5) =
1519 -1.0 * weight *
1520 this->restriction[index][i + 2 * (j + 2)](
1521 (i + 2 * (j + 4)) * this->degree, dof) *
1522 this->shape_value_component((i + 2 * (j + 4)) *
1523 this->degree,
1524 quadrature_point_2,
1525 2);
1526 }
1527
1528 else
1529 {
1530 tmp(0) =
1531 -1.0 * weight *
1532 this->restriction[index][i + 4 * j](
1533 (i + 4 * j) * this->degree, dof) *
1534 this->shape_value_component((i + 4 * j) *
1535 this->degree,
1536 quadrature_point_0,
1537 1);
1538
1539 Point<dim> quadrature_point_3(
1540 i,
1541 2.0 * edge_quadrature_points[q_point][0] - 1.0,
1542 j);
1543
1544 tmp(1) = weight *
1545 (2.0 * this->shape_value_component(
1546 dof, quadrature_point_3, 1) -
1547 this->restriction[index][i + 4 * j + 2](
1548 (i + 4 * j) * this->degree, dof) *
1550 (i + 4 * j) * this->degree,
1551 quadrature_point_0,
1552 1));
1553 tmp(2) =
1554 -1.0 * weight *
1555 this->restriction[index][2 * (i + 2 * j)](
1556 (i + 4 * j + 2) * this->degree, dof) *
1557 this->shape_value_component((i + 4 * j + 2) *
1558 this->degree,
1559 quadrature_point_1,
1560 0);
1561 quadrature_point_3 = Point<dim>(
1562 2.0 * edge_quadrature_points[q_point][0] - 1.0,
1563 i,
1564 j);
1565 tmp(3) =
1566 weight *
1567 (2.0 * this->shape_value_component(
1568 dof, quadrature_point_3, 0) -
1569 this->restriction[index][2 * (i + 2 * j) + 1](
1570 (i + 4 * j + 2) * this->degree, dof) *
1571 this->shape_value_component(
1572 (i + 4 * j + 2) * this->degree,
1573 quadrature_point_1,
1574 0));
1575 tmp(4) =
1576 -1.0 * weight *
1577 this->restriction[index][i + 2 * j](
1578 (i + 2 * (j + 4)) * this->degree, dof) *
1579 this->shape_value_component((i + 2 * (j + 4)) *
1580 this->degree,
1581 quadrature_point_2,
1582 2);
1583 quadrature_point_3 = Point<dim>(
1584 i,
1585 j,
1586 2.0 * edge_quadrature_points[q_point][0] - 1.0);
1587 tmp(5) =
1588 weight *
1589 (2.0 * this->shape_value_component(
1590 dof, quadrature_point_3, 2) -
1591 this->restriction[index][i + 2 * (j + 2)](
1592 (i + 2 * (j + 4)) * this->degree, dof) *
1593 this->shape_value_component(
1594 (i + 2 * (j + 4)) * this->degree,
1595 quadrature_point_2,
1596 2));
1597 }
1598
1599 for (unsigned int k = 0; k < deg; ++k)
1600 {
1601 const double L_k =
1602 legendre_polynomials[k + 1].value(
1603 edge_quadrature_points[q_point][0]);
1604
1605 for (unsigned int l = 0; l < tmp.size(); ++l)
1606 system_rhs(k, l) += tmp(l) * L_k;
1607 }
1608 }
1609
1610 system_matrix_inv.mmult(solution, system_rhs);
1611
1612 for (unsigned int k = 0; k < 2; ++k)
1613 for (unsigned int l = 0; l < deg; ++l)
1614 {
1615 if (std::abs(solution(l, k)) > 1e-14)
1616 this->restriction[index][i + 2 * (2 * j + k)](
1617 (i + 4 * j) * this->degree + l + 1, dof) =
1618 solution(l, k);
1619
1620 if (std::abs(solution(l, k + 2)) > 1e-14)
1621 this->restriction[index][2 * (i + 2 * j) + k](
1622 (i + 4 * j + 2) * this->degree + l + 1, dof) =
1623 solution(l, k + 2);
1624
1625 if (std::abs(solution(l, k + 4)) > 1e-14)
1626 this->restriction[index][i + 2 * (j + 2 * k)](
1627 (i + 2 * (j + 4)) * this->degree + l + 1, dof) =
1628 solution(l, k + 4);
1629 }
1630 }
1631
1632 const QGauss<2> face_quadrature(2 * this->degree);
1633 const std::vector<Point<2>> &face_quadrature_points =
1634 face_quadrature.get_points();
1635 const std::vector<Polynomials::Polynomial<double>>
1636 &lobatto_polynomials =
1638 const unsigned int n_edge_dofs =
1640 const unsigned int n_face_quadrature_points =
1641 face_quadrature.size();
1642
1643 {
1644 FullMatrix<double> assembling_matrix(deg * this->degree,
1645 n_face_quadrature_points);
1646
1647 for (unsigned int q_point = 0;
1648 q_point < n_face_quadrature_points;
1649 ++q_point)
1650 {
1651 const double weight =
1652 std::sqrt(face_quadrature.weight(q_point));
1653
1654 for (unsigned int i = 0; i <= deg; ++i)
1655 {
1656 const double L_i =
1657 weight * legendre_polynomials[i].value(
1658 face_quadrature_points[q_point][0]);
1659
1660 for (unsigned int j = 0; j < deg; ++j)
1661 assembling_matrix(i * deg + j, q_point) =
1662 L_i * lobatto_polynomials[j + 2].value(
1663 face_quadrature_points[q_point][1]);
1664 }
1665 }
1666
1667 FullMatrix<double> system_matrix(assembling_matrix.m(),
1668 assembling_matrix.m());
1669
1670 assembling_matrix.mTmult(system_matrix, assembling_matrix);
1671 system_matrix_inv.reinit(system_matrix.m(), system_matrix.m());
1672 system_matrix_inv.invert(system_matrix);
1673 }
1674
1675 solution.reinit(system_matrix_inv.m(), 24);
1676 system_rhs.reinit(system_matrix_inv.m(), 24);
1677 tmp.reinit(24);
1678
1679 for (unsigned int i = 0; i < 2; ++i)
1680 for (unsigned int dof = 0; dof < this->n_dofs_per_cell(); ++dof)
1681 {
1682 system_rhs = 0.0;
1683
1684 for (unsigned int q_point = 0;
1685 q_point < n_face_quadrature_points;
1686 ++q_point)
1687 {
1688 tmp = 0.0;
1689
1690 if (face_quadrature_points[q_point][0] < 0.5)
1691 {
1692 if (face_quadrature_points[q_point][1] < 0.5)
1693 {
1694 Point<dim> quadrature_point_0(
1695 i,
1696 2.0 * face_quadrature_points[q_point][0],
1697 2.0 * face_quadrature_points[q_point][1]);
1698
1699 tmp(0) += 2.0 * this->shape_value_component(
1700 dof, quadrature_point_0, 1);
1701 tmp(1) += 2.0 * this->shape_value_component(
1702 dof, quadrature_point_0, 2);
1703 quadrature_point_0 = Point<dim>(
1704 2.0 * face_quadrature_points[q_point][0],
1705 i,
1706 2.0 * face_quadrature_points[q_point][1]);
1707 tmp(8) += 2.0 * this->shape_value_component(
1708 dof, quadrature_point_0, 2);
1709 tmp(9) += 2.0 * this->shape_value_component(
1710 dof, quadrature_point_0, 0);
1711 quadrature_point_0 = Point<dim>(
1712 2.0 * face_quadrature_points[q_point][0],
1713 2.0 * face_quadrature_points[q_point][1],
1714 i);
1715 tmp(16) += 2.0 * this->shape_value_component(
1716 dof, quadrature_point_0, 0);
1717 tmp(17) += 2.0 * this->shape_value_component(
1718 dof, quadrature_point_0, 1);
1719 }
1720
1721 else
1722 {
1723 Point<dim> quadrature_point_0(
1724 i,
1725 2.0 * face_quadrature_points[q_point][0],
1726 2.0 * face_quadrature_points[q_point][1] -
1727 1.0);
1728
1729 tmp(2) += 2.0 * this->shape_value_component(
1730 dof, quadrature_point_0, 1);
1731 tmp(3) += 2.0 * this->shape_value_component(
1732 dof, quadrature_point_0, 2);
1733 quadrature_point_0 = Point<dim>(
1734 2.0 * face_quadrature_points[q_point][0],
1735 i,
1736 2.0 * face_quadrature_points[q_point][1] -
1737 1.0);
1738 tmp(10) += 2.0 * this->shape_value_component(
1739 dof, quadrature_point_0, 2);
1740 tmp(11) += 2.0 * this->shape_value_component(
1741 dof, quadrature_point_0, 0);
1742 quadrature_point_0 = Point<dim>(
1743 2.0 * face_quadrature_points[q_point][0],
1744 2.0 * face_quadrature_points[q_point][1] -
1745 1.0,
1746 i);
1747 tmp(18) += 2.0 * this->shape_value_component(
1748 dof, quadrature_point_0, 0);
1749 tmp(19) += 2.0 * this->shape_value_component(
1750 dof, quadrature_point_0, 1);
1751 }
1752 }
1753
1754 else if (face_quadrature_points[q_point][1] < 0.5)
1755 {
1756 Point<dim> quadrature_point_0(
1757 i,
1758 2.0 * face_quadrature_points[q_point][0] - 1.0,
1759 2.0 * face_quadrature_points[q_point][1]);
1760
1761 tmp(4) += 2.0 * this->shape_value_component(
1762 dof, quadrature_point_0, 1);
1763 tmp(5) += 2.0 * this->shape_value_component(
1764 dof, quadrature_point_0, 2);
1765 quadrature_point_0 = Point<dim>(
1766 2.0 * face_quadrature_points[q_point][0] - 1.0,
1767 i,
1768 2.0 * face_quadrature_points[q_point][1]);
1769 tmp(12) += 2.0 * this->shape_value_component(
1770 dof, quadrature_point_0, 2);
1771 tmp(13) += 2.0 * this->shape_value_component(
1772 dof, quadrature_point_0, 0);
1773 quadrature_point_0 = Point<dim>(
1774 2.0 * face_quadrature_points[q_point][0] - 1.0,
1775 2.0 * face_quadrature_points[q_point][1],
1776 i);
1777 tmp(20) += 2.0 * this->shape_value_component(
1778 dof, quadrature_point_0, 0);
1779 tmp(21) += 2.0 * this->shape_value_component(
1780 dof, quadrature_point_0, 1);
1781 }
1782
1783 else
1784 {
1785 Point<dim> quadrature_point_0(
1786 i,
1787 2.0 * face_quadrature_points[q_point][0] - 1.0,
1788 2.0 * face_quadrature_points[q_point][1] - 1.0);
1789
1790 tmp(6) += 2.0 * this->shape_value_component(
1791 dof, quadrature_point_0, 1);
1792 tmp(7) += 2.0 * this->shape_value_component(
1793 dof, quadrature_point_0, 2);
1794 quadrature_point_0 = Point<dim>(
1795 2.0 * face_quadrature_points[q_point][0] - 1.0,
1796 i,
1797 2.0 * face_quadrature_points[q_point][1] - 1.0);
1798 tmp(14) += 2.0 * this->shape_value_component(
1799 dof, quadrature_point_0, 2);
1800 tmp(15) += 2.0 * this->shape_value_component(
1801 dof, quadrature_point_0, 0);
1802 quadrature_point_0 = Point<dim>(
1803 2.0 * face_quadrature_points[q_point][0] - 1.0,
1804 2.0 * face_quadrature_points[q_point][1] - 1.0,
1805 i);
1806 tmp(22) += 2.0 * this->shape_value_component(
1807 dof, quadrature_point_0, 0);
1808 tmp(23) += 2.0 * this->shape_value_component(
1809 dof, quadrature_point_0, 1);
1810 }
1811
1812 const Point<dim> quadrature_point_0(
1813 i,
1814 face_quadrature_points[q_point][0],
1815 face_quadrature_points[q_point][1]);
1816 const Point<dim> quadrature_point_1(
1817 face_quadrature_points[q_point][0],
1818 i,
1819 face_quadrature_points[q_point][1]);
1820 const Point<dim> quadrature_point_2(
1821 face_quadrature_points[q_point][0],
1822 face_quadrature_points[q_point][1],
1823 i);
1824
1825 for (unsigned int j = 0; j < 2; ++j)
1826 for (unsigned int k = 0; k < 2; ++k)
1827 for (unsigned int l = 0; l <= deg; ++l)
1828 {
1829 tmp(2 * (j + 2 * k)) -=
1830 this->restriction[index][i + 2 * (2 * j + k)](
1831 (i + 4 * j) * this->degree + l, dof) *
1833 (i + 4 * j) * this->degree + l,
1834 quadrature_point_0,
1835 1);
1836 tmp(2 * (j + 2 * k) + 1) -=
1837 this->restriction[index][i + 2 * (2 * j + k)](
1838 (i + 2 * (k + 4)) * this->degree + l, dof) *
1840 (i + 2 * (k + 4)) * this->degree + l,
1841 quadrature_point_0,
1842 2);
1843 tmp(2 * (j + 2 * (k + 2))) -=
1844 this->restriction[index][2 * (i + 2 * j) + k](
1845 (2 * (i + 4) + k) * this->degree + l, dof) *
1847 (2 * (i + 4) + k) * this->degree + l,
1848 quadrature_point_1,
1849 2);
1850 tmp(2 * (j + 2 * k) + 9) -=
1851 this->restriction[index][2 * (i + 2 * j) + k](
1852 (i + 4 * j + 2) * this->degree + l, dof) *
1854 (i + 4 * j + 2) * this->degree + l,
1855 quadrature_point_1,
1856 0);
1857 tmp(2 * (j + 2 * (k + 4))) -=
1858 this->restriction[index][2 * (2 * i + j) + k](
1859 (4 * i + j + 2) * this->degree + l, dof) *
1861 (4 * i + j + 2) * this->degree + l,
1862 quadrature_point_2,
1863 0);
1864 tmp(2 * (j + 2 * k) + 17) -=
1865 this->restriction[index][2 * (2 * i + j) + k](
1866 (4 * i + k) * this->degree + l, dof) *
1868 (4 * i + k) * this->degree + l,
1869 quadrature_point_2,
1870 1);
1871 }
1872
1873 tmp *= face_quadrature.weight(q_point);
1874
1875 for (unsigned int j = 0; j <= deg; ++j)
1876 {
1877 const double L_j_0 = legendre_polynomials[j].value(
1878 face_quadrature_points[q_point][0]);
1879 const double L_j_1 = legendre_polynomials[j].value(
1880 face_quadrature_points[q_point][1]);
1881
1882 for (unsigned int k = 0; k < deg; ++k)
1883 {
1884 const double l_k_0 =
1885 L_j_0 * lobatto_polynomials[k + 2].value(
1886 face_quadrature_points[q_point][1]);
1887 const double l_k_1 =
1888 L_j_1 * lobatto_polynomials[k + 2].value(
1889 face_quadrature_points[q_point][0]);
1890
1891 for (unsigned int l = 0; l < 4; ++l)
1892 {
1893 system_rhs(j * deg + k, 2 * l) +=
1894 tmp(2 * l) * l_k_0;
1895 system_rhs(j * deg + k, 2 * l + 1) +=
1896 tmp(2 * l + 1) * l_k_1;
1897 system_rhs(j * deg + k, 2 * (l + 4)) +=
1898 tmp(2 * (l + 4)) * l_k_1;
1899 system_rhs(j * deg + k, 2 * l + 9) +=
1900 tmp(2 * l + 9) * l_k_0;
1901 system_rhs(j * deg + k, 2 * (l + 8)) +=
1902 tmp(2 * (l + 8)) * l_k_0;
1903 system_rhs(j * deg + k, 2 * l + 17) +=
1904 tmp(2 * l + 17) * l_k_1;
1905 }
1906 }
1907 }
1908 }
1909
1910 system_matrix_inv.mmult(solution, system_rhs);
1911
1912 for (unsigned int j = 0; j < 2; ++j)
1913 for (unsigned int k = 0; k < 2; ++k)
1914 for (unsigned int l = 0; l <= deg; ++l)
1915 for (unsigned int m = 0; m < deg; ++m)
1916 {
1917 if (std::abs(solution(l * deg + m,
1918 2 * (j + 2 * k))) > 1e-14)
1919 this->restriction[index][i + 2 * (2 * j + k)](
1920 (2 * i * this->degree + l) * deg + m +
1921 n_edge_dofs,
1922 dof) = solution(l * deg + m, 2 * (j + 2 * k));
1923
1924 if (std::abs(solution(l * deg + m,
1925 2 * (j + 2 * k) + 1)) >
1926 1e-14)
1927 this->restriction[index][i + 2 * (2 * j + k)](
1928 ((2 * i + 1) * deg + m) * this->degree + l +
1929 n_edge_dofs,
1930 dof) =
1931 solution(l * deg + m, 2 * (j + 2 * k) + 1);
1932
1933 if (std::abs(solution(l * deg + m,
1934 2 * (j + 2 * (k + 2)))) >
1935 1e-14)
1936 this->restriction[index][2 * (i + 2 * j) + k](
1937 (2 * (i + 2) * this->degree + l) * deg + m +
1938 n_edge_dofs,
1939 dof) =
1940 solution(l * deg + m, 2 * (j + 2 * (k + 2)));
1941
1942 if (std::abs(solution(l * deg + m,
1943 2 * (j + 2 * k) + 9)) >
1944 1e-14)
1945 this->restriction[index][2 * (i + 2 * j) + k](
1946 ((2 * i + 5) * deg + m) * this->degree + l +
1947 n_edge_dofs,
1948 dof) =
1949 solution(l * deg + m, 2 * (j + 2 * k) + 9);
1950
1951 if (std::abs(solution(l * deg + m,
1952 2 * (j + 2 * (k + 4)))) >
1953 1e-14)
1954 this->restriction[index][2 * (2 * i + j) + k](
1955 (2 * (i + 4) * this->degree + l) * deg + m +
1956 n_edge_dofs,
1957 dof) =
1958 solution(l * deg + m, 2 * (j + 2 * (k + 4)));
1959
1960 if (std::abs(solution(l * deg + m,
1961 2 * (j + 2 * k) + 17)) >
1962 1e-14)
1963 this->restriction[index][2 * (2 * i + j) + k](
1964 ((2 * i + 9) * deg + m) * this->degree + l +
1965 n_edge_dofs,
1966 dof) =
1967 solution(l * deg + m, 2 * (j + 2 * k) + 17);
1968 }
1969 }
1970
1971 const QGauss<dim> quadrature(2 * this->degree);
1972 const std::vector<Point<dim>> &quadrature_points =
1973 quadrature.get_points();
1974 const unsigned int n_boundary_dofs =
1975 2 * GeometryInfo<dim>::faces_per_cell * deg * this->degree +
1976 n_edge_dofs;
1977 const unsigned int n_quadrature_points = quadrature.size();
1978
1979 {
1980 FullMatrix<double> assembling_matrix(deg * deg * this->degree,
1981 n_quadrature_points);
1982
1983 for (unsigned int q_point = 0; q_point < n_quadrature_points;
1984 ++q_point)
1985 {
1986 const double weight = std::sqrt(quadrature.weight(q_point));
1987
1988 for (unsigned int i = 0; i <= deg; ++i)
1989 {
1990 const double L_i =
1991 weight * legendre_polynomials[i].value(
1992 quadrature_points[q_point][0]);
1993
1994 for (unsigned int j = 0; j < deg; ++j)
1995 {
1996 const double l_j =
1997 L_i * lobatto_polynomials[j + 2].value(
1998 quadrature_points[q_point][1]);
1999
2000 for (unsigned int k = 0; k < deg; ++k)
2001 assembling_matrix((i * deg + j) * deg + k,
2002 q_point) =
2003 l_j * lobatto_polynomials[k + 2].value(
2004 quadrature_points[q_point][2]);
2005 }
2006 }
2007 }
2008
2009 FullMatrix<double> system_matrix(assembling_matrix.m(),
2010 assembling_matrix.m());
2011
2012 assembling_matrix.mTmult(system_matrix, assembling_matrix);
2013 system_matrix_inv.reinit(system_matrix.m(), system_matrix.m());
2014 system_matrix_inv.invert(system_matrix);
2015 }
2016
2017 solution.reinit(system_matrix_inv.m(), 24);
2018 system_rhs.reinit(system_matrix_inv.m(), 24);
2019 tmp.reinit(24);
2020
2021 for (unsigned int dof = 0; dof < this->n_dofs_per_cell(); ++dof)
2022 {
2023 system_rhs = 0.0;
2024
2025 for (unsigned int q_point = 0; q_point < n_quadrature_points;
2026 ++q_point)
2027 {
2028 tmp = 0.0;
2029
2030 if (quadrature_points[q_point][0] < 0.5)
2031 {
2032 if (quadrature_points[q_point][1] < 0.5)
2033 {
2034 if (quadrature_points[q_point][2] < 0.5)
2035 {
2036 const Point<dim> quadrature_point(
2037 2.0 * quadrature_points[q_point][0],
2038 2.0 * quadrature_points[q_point][1],
2039 2.0 * quadrature_points[q_point][2]);
2040
2041 tmp(0) += 2.0 * this->shape_value_component(
2042 dof, quadrature_point, 0);
2043 tmp(1) += 2.0 * this->shape_value_component(
2044 dof, quadrature_point, 1);
2045 tmp(2) += 2.0 * this->shape_value_component(
2046 dof, quadrature_point, 2);
2047 }
2048
2049 else
2050 {
2051 const Point<dim> quadrature_point(
2052 2.0 * quadrature_points[q_point][0],
2053 2.0 * quadrature_points[q_point][1],
2054 2.0 * quadrature_points[q_point][2] - 1.0);
2055
2056 tmp(3) += 2.0 * this->shape_value_component(
2057 dof, quadrature_point, 0);
2058 tmp(4) += 2.0 * this->shape_value_component(
2059 dof, quadrature_point, 1);
2060 tmp(5) += 2.0 * this->shape_value_component(
2061 dof, quadrature_point, 2);
2062 }
2063 }
2064
2065 else if (quadrature_points[q_point][2] < 0.5)
2066 {
2067 const Point<dim> quadrature_point(
2068 2.0 * quadrature_points[q_point][0],
2069 2.0 * quadrature_points[q_point][1] - 1.0,
2070 2.0 * quadrature_points[q_point][2]);
2071
2072 tmp(6) += 2.0 * this->shape_value_component(
2073 dof, quadrature_point, 0);
2074 tmp(7) += 2.0 * this->shape_value_component(
2075 dof, quadrature_point, 1);
2076 tmp(8) += 2.0 * this->shape_value_component(
2077 dof, quadrature_point, 2);
2078 }
2079
2080 else
2081 {
2082 const Point<dim> quadrature_point(
2083 2.0 * quadrature_points[q_point][0],
2084 2.0 * quadrature_points[q_point][1] - 1.0,
2085 2.0 * quadrature_points[q_point][2] - 1.0);
2086
2087 tmp(9) += 2.0 * this->shape_value_component(
2088 dof, quadrature_point, 0);
2089 tmp(10) += 2.0 * this->shape_value_component(
2090 dof, quadrature_point, 1);
2091 tmp(11) += 2.0 * this->shape_value_component(
2092 dof, quadrature_point, 2);
2093 }
2094 }
2095
2096 else if (quadrature_points[q_point][1] < 0.5)
2097 {
2098 if (quadrature_points[q_point][2] < 0.5)
2099 {
2100 const Point<dim> quadrature_point(
2101 2.0 * quadrature_points[q_point][0] - 1.0,
2102 2.0 * quadrature_points[q_point][1],
2103 2.0 * quadrature_points[q_point][2]);
2104
2105 tmp(12) += 2.0 * this->shape_value_component(
2106 dof, quadrature_point, 0);
2107 tmp(13) += 2.0 * this->shape_value_component(
2108 dof, quadrature_point, 1);
2109 tmp(14) += 2.0 * this->shape_value_component(
2110 dof, quadrature_point, 2);
2111 }
2112
2113 else
2114 {
2115 const Point<dim> quadrature_point(
2116 2.0 * quadrature_points[q_point][0] - 1.0,
2117 2.0 * quadrature_points[q_point][1],
2118 2.0 * quadrature_points[q_point][2] - 1.0);
2119
2120 tmp(15) += 2.0 * this->shape_value_component(
2121 dof, quadrature_point, 0);
2122 tmp(16) += 2.0 * this->shape_value_component(
2123 dof, quadrature_point, 1);
2124 tmp(17) += 2.0 * this->shape_value_component(
2125 dof, quadrature_point, 2);
2126 }
2127 }
2128
2129 else if (quadrature_points[q_point][2] < 0.5)
2130 {
2131 const Point<dim> quadrature_point(
2132 2.0 * quadrature_points[q_point][0] - 1.0,
2133 2.0 * quadrature_points[q_point][1] - 1.0,
2134 2.0 * quadrature_points[q_point][2]);
2135
2136 tmp(18) +=
2137 2.0 * this->shape_value_component(dof,
2138 quadrature_point,
2139 0);
2140 tmp(19) +=
2141 2.0 * this->shape_value_component(dof,
2142 quadrature_point,
2143 1);
2144 tmp(20) +=
2145 2.0 * this->shape_value_component(dof,
2146 quadrature_point,
2147 2);
2148 }
2149
2150 else
2151 {
2152 const Point<dim> quadrature_point(
2153 2.0 * quadrature_points[q_point][0] - 1.0,
2154 2.0 * quadrature_points[q_point][1] - 1.0,
2155 2.0 * quadrature_points[q_point][2] - 1.0);
2156
2157 tmp(21) +=
2158 2.0 * this->shape_value_component(dof,
2159 quadrature_point,
2160 0);
2161 tmp(22) +=
2162 2.0 * this->shape_value_component(dof,
2163 quadrature_point,
2164 1);
2165 tmp(23) +=
2166 2.0 * this->shape_value_component(dof,
2167 quadrature_point,
2168 2);
2169 }
2170
2171 for (unsigned int i = 0; i < 2; ++i)
2172 for (unsigned int j = 0; j < 2; ++j)
2173 for (unsigned int k = 0; k < 2; ++k)
2174 for (unsigned int l = 0; l <= deg; ++l)
2175 {
2176 tmp(3 * (i + 2 * (j + 2 * k))) -=
2177 this->restriction[index][2 * (2 * i + j) + k](
2178 (4 * i + j + 2) * this->degree + l, dof) *
2180 (4 * i + j + 2) * this->degree + l,
2181 quadrature_points[q_point],
2182 0);
2183 tmp(3 * (i + 2 * (j + 2 * k)) + 1) -=
2184 this->restriction[index][2 * (2 * i + j) + k](
2185 (4 * i + k) * this->degree + l, dof) *
2187 (4 * i + k) * this->degree + l,
2188 quadrature_points[q_point],
2189 1);
2190 tmp(3 * (i + 2 * (j + 2 * k)) + 2) -=
2191 this->restriction[index][2 * (2 * i + j) + k](
2192 (2 * (j + 4) + k) * this->degree + l, dof) *
2194 (2 * (j + 4) + k) * this->degree + l,
2195 quadrature_points[q_point],
2196 2);
2197
2198 for (unsigned int m = 0; m < deg; ++m)
2199 {
2200 tmp(3 * (i + 2 * (j + 2 * k))) -=
2201 this->restriction[index][2 * (2 * i + j) +
2202 k](
2203 ((2 * j + 5) * deg + m) * this->degree +
2204 l + n_edge_dofs,
2205 dof) *
2207 ((2 * j + 5) * deg + m) * this->degree +
2208 l + n_edge_dofs,
2209 quadrature_points[q_point],
2210 0);
2211 tmp(3 * (i + 2 * (j + 2 * k))) -=
2212 this->restriction[index][2 * (2 * i + j) +
2213 k](
2214 (2 * (i + 4) * this->degree + l) * deg +
2215 m + n_edge_dofs,
2216 dof) *
2218 (2 * (i + 4) * this->degree + l) * deg +
2219 m + n_edge_dofs,
2220 quadrature_points[q_point],
2221 0);
2222 tmp(3 * (i + 2 * (j + 2 * k)) + 1) -=
2223 this->restriction[index][2 * (2 * i + j) +
2224 k](
2225 (2 * k * this->degree + l) * deg + m +
2226 n_edge_dofs,
2227 dof) *
2229 (2 * k * this->degree + l) * deg + m +
2230 n_edge_dofs,
2231 quadrature_points[q_point],
2232 1);
2233 tmp(3 * (i + 2 * (j + 2 * k)) + 1) -=
2234 this->restriction[index][2 * (2 * i + j) +
2235 k](
2236 ((2 * i + 9) * deg + m) * this->degree +
2237 l + n_edge_dofs,
2238 dof) *
2240 ((2 * i + 9) * deg + m) * this->degree +
2241 l + n_edge_dofs,
2242 quadrature_points[q_point],
2243 1);
2244 tmp(3 * (i + 2 * (j + 2 * k)) + 2) -=
2245 this->restriction[index][2 * (2 * i + j) +
2246 k](
2247 ((2 * k + 1) * deg + m) * this->degree +
2248 l + n_edge_dofs,
2249 dof) *
2251 ((2 * k + 1) * deg + m) * this->degree +
2252 l + n_edge_dofs,
2253 quadrature_points[q_point],
2254 2);
2255 tmp(3 * (i + 2 * (j + 2 * k)) + 2) -=
2256 this->restriction[index][2 * (2 * i + j) +
2257 k](
2258 (2 * (j + 2) * this->degree + l) * deg +
2259 m + n_edge_dofs,
2260 dof) *
2262 (2 * (j + 2) * this->degree + l) * deg +
2263 m + n_edge_dofs,
2264 quadrature_points[q_point],
2265 2);
2266 }
2267 }
2268
2269 tmp *= quadrature.weight(q_point);
2270
2271 for (unsigned int i = 0; i <= deg; ++i)
2272 {
2273 const double L_i_0 = legendre_polynomials[i].value(
2274 quadrature_points[q_point][0]);
2275 const double L_i_1 = legendre_polynomials[i].value(
2276 quadrature_points[q_point][1]);
2277 const double L_i_2 = legendre_polynomials[i].value(
2278 quadrature_points[q_point][2]);
2279
2280 for (unsigned int j = 0; j < deg; ++j)
2281 {
2282 const double l_j_0 =
2283 L_i_0 * lobatto_polynomials[j + 2].value(
2284 quadrature_points[q_point][1]);
2285 const double l_j_1 =
2286 L_i_1 * lobatto_polynomials[j + 2].value(
2287 quadrature_points[q_point][0]);
2288 const double l_j_2 =
2289 L_i_2 * lobatto_polynomials[j + 2].value(
2290 quadrature_points[q_point][0]);
2291
2292 for (unsigned int k = 0; k < deg; ++k)
2293 {
2294 const double l_k_0 =
2295 l_j_0 * lobatto_polynomials[k + 2].value(
2296 quadrature_points[q_point][2]);
2297 const double l_k_1 =
2298 l_j_1 * lobatto_polynomials[k + 2].value(
2299 quadrature_points[q_point][2]);
2300 const double l_k_2 =
2301 l_j_2 * lobatto_polynomials[k + 2].value(
2302 quadrature_points[q_point][1]);
2303
2304 for (unsigned int l = 0; l < 8; ++l)
2305 {
2306 system_rhs((i * deg + j) * deg + k,
2307 3 * l) += tmp(3 * l) * l_k_0;
2308 system_rhs((i * deg + j) * deg + k,
2309 3 * l + 1) +=
2310 tmp(3 * l + 1) * l_k_1;
2311 system_rhs((i * deg + j) * deg + k,
2312 3 * l + 2) +=
2313 tmp(3 * l + 2) * l_k_2;
2314 }
2315 }
2316 }
2317 }
2318 }
2319
2320 system_matrix_inv.mmult(solution, system_rhs);
2321
2322 for (unsigned int i = 0; i < 2; ++i)
2323 for (unsigned int j = 0; j < 2; ++j)
2324 for (unsigned int k = 0; k < 2; ++k)
2325 for (unsigned int l = 0; l <= deg; ++l)
2326 for (unsigned int m = 0; m < deg; ++m)
2327 for (unsigned int n = 0; n < deg; ++n)
2328 {
2329 if (std::abs(
2330 solution((l * deg + m) * deg + n,
2331 3 * (i + 2 * (j + 2 * k)))) >
2332 1e-14)
2333 this->restriction[index][2 * (2 * i + j) + k](
2334 (l * deg + m) * deg + n + n_boundary_dofs,
2335 dof) = solution((l * deg + m) * deg + n,
2336 3 * (i + 2 * (j + 2 * k)));
2337
2338 if (std::abs(
2339 solution((l * deg + m) * deg + n,
2340 3 * (i + 2 * (j + 2 * k)) + 1)) >
2341 1e-14)
2342 this->restriction[index][2 * (2 * i + j) + k](
2343 (l + (m + deg) * this->degree) * deg + n +
2344 n_boundary_dofs,
2345 dof) =
2346 solution((l * deg + m) * deg + n,
2347 3 * (i + 2 * (j + 2 * k)) + 1);
2348
2349 if (std::abs(
2350 solution((l * deg + m) * deg + n,
2351 3 * (i + 2 * (j + 2 * k)) + 2)) >
2352 1e-14)
2353 this->restriction[index][2 * (2 * i + j) + k](
2354 l +
2355 ((m + 2 * deg) * deg + n) * this->degree +
2356 n_boundary_dofs,
2357 dof) =
2358 solution((l * deg + m) * deg + n,
2359 3 * (i + 2 * (j + 2 * k)) + 2);
2360 }
2361 }
2362 }
2363
2364 break;
2365 }
2366
2367 default:
2369 }
2370}
2371
2372
2373
2374template <int dim>
2375std::vector<unsigned int>
2376FE_Nedelec<dim>::get_dpo_vector(const unsigned int degree, bool dg)
2377{
2378 std::vector<unsigned int> dpo;
2379
2380 if (dg)
2381 {
2382 dpo.resize(dim + 1);
2384 }
2385 else
2386 {
2387 dpo.push_back(0);
2388 dpo.push_back(degree + 1);
2389 if (dim > 1)
2390 dpo.push_back(2 * degree * (degree + 1));
2391 if (dim > 2)
2392 dpo.push_back(3 * degree * degree * (degree + 1));
2393 }
2394
2395 return dpo;
2396}
2397
2398//---------------------------------------------------------------------------
2399// Data field initialization
2400//---------------------------------------------------------------------------
2401
2402// Check whether a given shape
2403// function has support on a
2404// given face.
2405
2406// We just switch through the
2407// faces of the cell and return
2408// true, if the shape function
2409// has support on the face
2410// and false otherwise.
2411template <int dim>
2412bool
2413FE_Nedelec<dim>::has_support_on_face(const unsigned int shape_index,
2414 const unsigned int face_index) const
2415{
2416 AssertIndexRange(shape_index, this->n_dofs_per_cell());
2418
2419 const unsigned int deg = this->degree - 1;
2420 switch (dim)
2421 {
2422 case 2:
2423 switch (face_index)
2424 {
2425 case 0:
2426 if (!((shape_index > deg) && (shape_index < 2 * this->degree)))
2427 return true;
2428
2429 else
2430 return false;
2431
2432 case 1:
2433 if ((shape_index > deg) &&
2434 (shape_index <
2435 GeometryInfo<2>::lines_per_cell * this->degree))
2436 return true;
2437
2438 else
2439 return false;
2440
2441 case 2:
2442 if (shape_index < 3 * this->degree)
2443 return true;
2444
2445 else
2446 return false;
2447
2448 case 3:
2449 if (!((shape_index >= 2 * this->degree) &&
2450 (shape_index < 3 * this->degree)))
2451 return true;
2452
2453 else
2454 return false;
2455
2456 default:
2457 {
2459 return false;
2460 }
2461 }
2462
2463 case 3:
2464 switch (face_index)
2465 {
2466 case 0:
2467 if (((shape_index > deg) && (shape_index < 2 * this->degree)) ||
2468 ((shape_index >= 5 * this->degree) &&
2469 (shape_index < 6 * this->degree)) ||
2470 ((shape_index >= 9 * this->degree) &&
2471 (shape_index < 10 * this->degree)) ||
2472 ((shape_index >= 11 * this->degree) &&
2473 (shape_index <
2474 GeometryInfo<3>::lines_per_cell * this->degree)) ||
2475 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 2 * deg) *
2476 this->degree) &&
2477 (shape_index < (GeometryInfo<3>::lines_per_cell + 4 * deg) *
2478 this->degree)) ||
2479 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 5 * deg) *
2480 this->degree) &&
2481 (shape_index < (GeometryInfo<3>::lines_per_cell + 6 * deg) *
2482 this->degree)) ||
2483 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 7 * deg) *
2484 this->degree) &&
2485 (shape_index < (GeometryInfo<3>::lines_per_cell + 9 * deg) *
2486 this->degree)) ||
2487 ((shape_index >=
2488 (GeometryInfo<3>::lines_per_cell + 10 * deg) *
2489 this->degree) &&
2490 (shape_index < (GeometryInfo<3>::lines_per_cell + 11 * deg) *
2491 this->degree)))
2492 return false;
2493
2494 else
2495 return true;
2496
2497 case 1:
2498 if (((shape_index > deg) && (shape_index < 4 * this->degree)) ||
2499 ((shape_index >= 5 * this->degree) &&
2500 (shape_index < 8 * this->degree)) ||
2501 ((shape_index >= 9 * this->degree) &&
2502 (shape_index < 10 * this->degree)) ||
2503 ((shape_index >= 11 * this->degree) &&
2504 (shape_index <
2505 GeometryInfo<3>::lines_per_cell * this->degree)) ||
2506 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 2 * deg) *
2507 this->degree) &&
2508 (shape_index < (GeometryInfo<3>::lines_per_cell + 5 * deg) *
2509 this->degree)) ||
2510 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 6 * deg) *
2511 this->degree) &&
2512 (shape_index < (GeometryInfo<3>::lines_per_cell + 7 * deg) *
2513 this->degree)) ||
2514 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 9 * deg) *
2515 this->degree) &&
2516 (shape_index < (GeometryInfo<3>::lines_per_cell + 10 * deg) *
2517 this->degree)) ||
2518 ((shape_index >=
2519 (GeometryInfo<3>::lines_per_cell + 11 * deg) *
2520 this->degree) &&
2521 (shape_index < (GeometryInfo<3>::lines_per_cell + 12 * deg) *
2522 this->degree)))
2523 return true;
2524
2525 else
2526 return false;
2527
2528 case 2:
2529 if ((shape_index < 3 * this->degree) ||
2530 ((shape_index >= 4 * this->degree) &&
2531 (shape_index < 7 * this->degree)) ||
2532 ((shape_index >= 8 * this->degree) &&
2533 (shape_index < 10 * this->degree)) ||
2534 ((shape_index >=
2535 (GeometryInfo<3>::lines_per_cell + deg) * this->degree) &&
2536 (shape_index < (GeometryInfo<3>::lines_per_cell + 2 * deg) *
2537 this->degree)) ||
2538 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 3 * deg) *
2539 this->degree) &&
2540 (shape_index < (GeometryInfo<3>::lines_per_cell + 6 * deg) *
2541 this->degree)) ||
2542 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 8 * deg) *
2543 this->degree) &&
2544 (shape_index < (GeometryInfo<3>::lines_per_cell + 9 * deg) *
2545 this->degree)) ||
2546 ((shape_index >=
2547 (GeometryInfo<3>::lines_per_cell + 10 * deg) *
2548 this->degree) &&
2549 (shape_index < (GeometryInfo<3>::lines_per_cell + 11 * deg) *
2550 this->degree)))
2551 return true;
2552
2553 else
2554 return false;
2555
2556 case 3:
2557 if ((shape_index < 2 * this->degree) ||
2558 ((shape_index >= 3 * this->degree) &&
2559 (shape_index < 6 * this->degree)) ||
2560 ((shape_index >= 7 * this->degree) &&
2561 (shape_index < 8 * this->degree)) ||
2562 ((shape_index >= 10 * this->degree) &&
2563 (shape_index <
2564 GeometryInfo<3>::lines_per_cell * this->degree)) ||
2565 ((shape_index >=
2566 (GeometryInfo<3>::lines_per_cell + deg) * this->degree) &&
2567 (shape_index < (GeometryInfo<3>::lines_per_cell + 2 * deg) *
2568 this->degree)) ||
2569 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 3 * deg) *
2570 this->degree) &&
2571 (shape_index < (GeometryInfo<3>::lines_per_cell + 4 * deg) *
2572 this->degree)) ||
2573 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 6 * deg) *
2574 this->degree) &&
2575 (shape_index < (GeometryInfo<3>::lines_per_cell + 9 * deg) *
2576 this->degree)) ||
2577 ((shape_index >=
2578 (GeometryInfo<3>::lines_per_cell + 10 * deg) *
2579 this->degree) &&
2580 (shape_index < (GeometryInfo<3>::lines_per_cell + 11 * deg) *
2581 this->degree)))
2582 return true;
2583
2584 else
2585 return false;
2586
2587 case 4:
2588 if ((shape_index < 4 * this->degree) ||
2589 ((shape_index >= 8 * this->degree) &&
2590 (shape_index <
2591 (GeometryInfo<3>::lines_per_cell + deg) * this->degree)) ||
2592 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 2 * deg) *
2593 this->degree) &&
2594 (shape_index < (GeometryInfo<3>::lines_per_cell + 3 * deg) *
2595 this->degree)) ||
2596 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 5 * deg) *
2597 this->degree) &&
2598 (shape_index < (GeometryInfo<3>::lines_per_cell + 6 * deg) *
2599 this->degree)) ||
2600 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 7 * deg) *
2601 this->degree) &&
2602 (shape_index < (GeometryInfo<3>::lines_per_cell + 10 * deg) *
2603 this->degree)))
2604 return true;
2605
2606 else
2607 return false;
2608
2609 case 5:
2610 if (((shape_index >= 4 * this->degree) &&
2611 (shape_index <
2612 (GeometryInfo<3>::lines_per_cell + deg) * this->degree)) ||
2613 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 2 * deg) *
2614 this->degree) &&
2615 (shape_index < (GeometryInfo<3>::lines_per_cell + 3 * deg) *
2616 this->degree)) ||
2617 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 5 * deg) *
2618 this->degree) &&
2619 (shape_index < (GeometryInfo<3>::lines_per_cell + 6 * deg) *
2620 this->degree)) ||
2621 ((shape_index >= (GeometryInfo<3>::lines_per_cell + 7 * deg) *
2622 this->degree) &&
2623 (shape_index < (GeometryInfo<3>::lines_per_cell + 8 * deg) *
2624 this->degree)) ||
2625 ((shape_index >=
2626 (GeometryInfo<3>::lines_per_cell + 10 * deg) *
2627 this->degree) &&
2628 (shape_index < (GeometryInfo<3>::lines_per_cell + 12 * deg) *
2629 this->degree)))
2630 return true;
2631
2632 else
2633 return false;
2634
2635 default:
2636 {
2638 return false;
2639 }
2640 }
2641
2642 default:
2643 {
2645 return false;
2646 }
2647 }
2648}
2649
2650template <int dim>
2653 const unsigned int codim) const
2654{
2655 Assert(codim <= dim, ExcImpossibleInDim(dim));
2656 (void)codim;
2657
2658 // vertex/line/face/cell domination
2659 // --------------------------------
2660 if (const FE_Nedelec<dim> *fe_nedelec_other =
2661 dynamic_cast<const FE_Nedelec<dim> *>(&fe_other))
2662 {
2663 if (this->degree < fe_nedelec_other->degree)
2665 else if (this->degree == fe_nedelec_other->degree)
2667 else
2669 }
2670 else if (const FE_Nothing<dim> *fe_nothing =
2671 dynamic_cast<const FE_Nothing<dim> *>(&fe_other))
2672 {
2673 if (fe_nothing->is_dominating())
2675 else
2676 // the FE_Nothing has no degrees of freedom and it is typically used
2677 // in a context where we don't require any continuity along the
2678 // interface
2680 }
2681
2684}
2685
2686template <int dim>
2687bool
2689{
2690 return true;
2691}
2692
2693template <int dim>
2694std::vector<std::pair<unsigned int, unsigned int>>
2696{
2697 // Nedelec elements do not have any dofs
2698 // on vertices, hence return an empty vector.
2699 return std::vector<std::pair<unsigned int, unsigned int>>();
2700}
2701
2702template <int dim>
2703std::vector<std::pair<unsigned int, unsigned int>>
2705 const FiniteElement<dim> &fe_other) const
2706{
2707 // we can presently only compute these
2708 // identities if both FEs are
2709 // FE_Nedelec or if the other one is an
2710 // FE_Nothing
2711 if (const FE_Nedelec<dim> *fe_nedelec_other =
2712 dynamic_cast<const FE_Nedelec<dim> *>(&fe_other))
2713 {
2714 // dofs are located on lines, so
2715 // two dofs are identical, if their
2716 // edge shape functions have the
2717 // same polynomial degree.
2718 std::vector<std::pair<unsigned int, unsigned int>> identities;
2719
2720 identities.reserve(std::min(fe_nedelec_other->degree, this->degree));
2721 for (unsigned int i = 0;
2722 i < std::min(fe_nedelec_other->degree, this->degree);
2723 ++i)
2724 identities.emplace_back(i, i);
2725
2726 return identities;
2727 }
2728
2729 else if (dynamic_cast<const FE_Nothing<dim> *>(&fe_other) != nullptr)
2730 {
2731 // the FE_Nothing has no
2732 // degrees of freedom, so there
2733 // are no equivalencies to be
2734 // recorded
2735 return std::vector<std::pair<unsigned int, unsigned int>>();
2736 }
2737
2738 else
2739 {
2741 return std::vector<std::pair<unsigned int, unsigned int>>();
2742 }
2743}
2744
2745template <int dim>
2746std::vector<std::pair<unsigned int, unsigned int>>
2748 const unsigned int) const
2749{
2750 // we can presently only compute
2751 // these identities if both FEs are
2752 // FE_Nedelec or if the other one is an
2753 // FE_Nothing
2754 if (const FE_Nedelec<dim> *fe_nedelec_other =
2755 dynamic_cast<const FE_Nedelec<dim> *>(&fe_other))
2756 {
2757 // dofs are located on the interior
2758 // of faces, so two dofs are identical,
2759 // if their face shape functions have
2760 // the same polynomial degree.
2761 const unsigned int p = fe_nedelec_other->degree;
2762 const unsigned int q = this->degree;
2763 const unsigned int p_min = std::min(p, q);
2764 std::vector<std::pair<unsigned int, unsigned int>> identities;
2765
2766 for (unsigned int i = 0; i < p_min; ++i)
2767 for (unsigned int j = 0; j < p_min - 1; ++j)
2768 {
2769 identities.emplace_back(i * (q - 1) + j, i * (p - 1) + j);
2770 identities.emplace_back(i + (j + q - 1) * q, i + (j + p - 1) * p);
2771 }
2772
2773 return identities;
2774 }
2775
2776 else if (dynamic_cast<const FE_Nothing<dim> *>(&fe_other) != nullptr)
2777 {
2778 // the FE_Nothing has no
2779 // degrees of freedom, so there
2780 // are no equivalencies to be
2781 // recorded
2782 return std::vector<std::pair<unsigned int, unsigned int>>();
2783 }
2784
2785 else
2786 {
2788 return std::vector<std::pair<unsigned int, unsigned int>>();
2789 }
2790}
2791
2792// In this function we compute the face
2793// interpolation matrix. This is usually
2794// done by projection-based interpolation,
2795// but, since one can compute the entries
2796// easy per hand, we save some computation
2797// time at this point and just fill in the
2798// correct values.
2799template <int dim>
2800void
2802 const FiniteElement<dim> &source,
2803 FullMatrix<double> &interpolation_matrix,
2804 const unsigned int face_no) const
2805{
2806 (void)face_no;
2807 // this is only implemented, if the
2808 // source FE is also a
2809 // Nedelec element
2810 AssertThrow((source.get_name().find("FE_Nedelec<") == 0) ||
2811 (dynamic_cast<const FE_Nedelec<dim> *>(&source) != nullptr),
2813 Assert(interpolation_matrix.m() == source.n_dofs_per_face(face_no),
2814 ExcDimensionMismatch(interpolation_matrix.m(),
2815 source.n_dofs_per_face(face_no)));
2816 Assert(interpolation_matrix.n() == this->n_dofs_per_face(face_no),
2817 ExcDimensionMismatch(interpolation_matrix.n(),
2818 this->n_dofs_per_face(face_no)));
2819
2820 // ok, source is a Nedelec element, so
2821 // we will be able to do the work
2822 const FE_Nedelec<dim> &source_fe =
2823 dynamic_cast<const FE_Nedelec<dim> &>(source);
2824
2825 // Make sure, that the element,
2826 // for which the DoFs should be
2827 // constrained is the one with
2828 // the higher polynomial degree.
2829 // Actually the procedure will work
2830 // also if this assertion is not
2831 // satisfied. But the matrices
2832 // produced in that case might
2833 // lead to problems in the
2834 // hp-procedures, which use this
2835 // method.
2836 Assert(this->n_dofs_per_face(face_no) <= source_fe.n_dofs_per_face(face_no),
2838 interpolation_matrix = 0;
2839
2840 // On lines we can just identify
2841 // all degrees of freedom.
2842 for (unsigned int i = 0; i < this->degree; ++i)
2843 interpolation_matrix(i, i) = 1.0;
2844
2845 // In 3d we have some lines more
2846 // and a face. The procedure stays
2847 // the same as above, but we have
2848 // to take a bit more care of the
2849 // indices of the degrees of
2850 // freedom.
2851 if (dim == 3)
2852 {
2853 const unsigned int p = source_fe.degree;
2854 const unsigned int q = this->degree;
2855
2856 for (unsigned int i = 0; i < q; ++i)
2857 {
2858 for (unsigned int j = 1; j < GeometryInfo<dim>::lines_per_face; ++j)
2859 interpolation_matrix(j * p + i, j * q + i) = 1.0;
2860
2861 for (unsigned int j = 0; j < q - 1; ++j)
2862 {
2863 interpolation_matrix(GeometryInfo<dim>::lines_per_face * p +
2864 i * (p - 1) + j,
2866 i * (q - 1) + j) = 1.0;
2867 interpolation_matrix(GeometryInfo<dim>::lines_per_face * p + i +
2868 (j + p - 1) * p,
2870 (j + q - 1) * q) = 1.0;
2871 }
2872 }
2873 }
2874}
2875
2876
2877
2878template <>
2879void
2881 const unsigned int,
2883 const unsigned int) const
2884{
2886}
2887
2888
2889
2890// In this function we compute the
2891// subface interpolation matrix.
2892// This is done by a projection-
2893// based interpolation. Therefore
2894// we first interpolate the
2895// shape functions of the higher
2896// order element on the lowest
2897// order edge shape functions.
2898// Then the remaining part of
2899// the interpolated shape
2900// functions is projected on the
2901// higher order edge shape
2902// functions, the face shape
2903// functions and the interior
2904// shape functions (if they all
2905// exist).
2906template <int dim>
2907void
2909 const FiniteElement<dim> &source,
2910 const unsigned int subface,
2911 FullMatrix<double> &interpolation_matrix,
2912 const unsigned int face_no) const
2913{
2914 // this is only implemented, if the
2915 // source FE is also a
2916 // Nedelec element
2917 AssertThrow((source.get_name().find("FE_Nedelec<") == 0) ||
2918 (dynamic_cast<const FE_Nedelec<dim> *>(&source) != nullptr),
2920 Assert(interpolation_matrix.m() == source.n_dofs_per_face(face_no),
2921 ExcDimensionMismatch(interpolation_matrix.m(),
2922 source.n_dofs_per_face(face_no)));
2923 Assert(interpolation_matrix.n() == this->n_dofs_per_face(face_no),
2924 ExcDimensionMismatch(interpolation_matrix.n(),
2925 this->n_dofs_per_face(face_no)));
2926
2927 // ok, source is a Nedelec element, so
2928 // we will be able to do the work
2929 const FE_Nedelec<dim> &source_fe =
2930 dynamic_cast<const FE_Nedelec<dim> &>(source);
2931
2932 // Make sure, that the element,
2933 // for which the DoFs should be
2934 // constrained is the one with
2935 // the higher polynomial degree.
2936 // Actually the procedure will work
2937 // also if this assertion is not
2938 // satisfied. But the matrices
2939 // produced in that case might
2940 // lead to problems in the
2941 // hp-procedures, which use this
2942 // method.
2943 Assert(this->n_dofs_per_face(face_no) <= source_fe.n_dofs_per_face(face_no),
2945 interpolation_matrix = 0.0;
2946 // Perform projection-based interpolation
2947 // as usual.
2948 const QGauss<1> edge_quadrature(source_fe.degree);
2949 const std::vector<Point<1>> &edge_quadrature_points =
2950 edge_quadrature.get_points();
2951 const unsigned int n_edge_quadrature_points = edge_quadrature.size();
2952
2953 switch (dim)
2954 {
2955 case 2:
2956 {
2957 for (unsigned int dof = 0; dof < this->n_dofs_per_face(face_no);
2958 ++dof)
2959 for (unsigned int q_point = 0; q_point < n_edge_quadrature_points;
2960 ++q_point)
2961 {
2962 const Point<dim> quadrature_point(
2963 0.0, 0.5 * (edge_quadrature_points[q_point][0] + subface));
2964
2965 interpolation_matrix(0, dof) +=
2966 0.5 * edge_quadrature.weight(q_point) *
2967 this->shape_value_component(dof, quadrature_point, 1);
2968 }
2969
2970 if (source_fe.degree > 1)
2971 {
2972 const std::vector<Polynomials::Polynomial<double>>
2973 &legendre_polynomials =
2975 source_fe.degree - 1);
2976 FullMatrix<double> system_matrix_inv(source_fe.degree - 1,
2977 source_fe.degree - 1);
2978
2979 {
2980 FullMatrix<double> assembling_matrix(source_fe.degree - 1,
2981 n_edge_quadrature_points);
2982
2983 for (unsigned int q_point = 0;
2984 q_point < n_edge_quadrature_points;
2985 ++q_point)
2986 {
2987 const double weight =
2988 std::sqrt(edge_quadrature.weight(q_point));
2989
2990 for (unsigned int i = 0; i < source_fe.degree - 1; ++i)
2991 assembling_matrix(i, q_point) =
2992 weight * legendre_polynomials[i + 1].value(
2993 edge_quadrature_points[q_point][0]);
2994 }
2995
2996 FullMatrix<double> system_matrix(source_fe.degree - 1,
2997 source_fe.degree - 1);
2998
2999 assembling_matrix.mTmult(system_matrix, assembling_matrix);
3000 system_matrix_inv.invert(system_matrix);
3001 }
3002
3003 Vector<double> solution(source_fe.degree - 1);
3004 Vector<double> system_rhs(source_fe.degree - 1);
3005
3006 for (unsigned int dof = 0; dof < this->n_dofs_per_face(face_no);
3007 ++dof)
3008 {
3009 system_rhs = 0.0;
3010
3011 for (unsigned int q_point = 0;
3012 q_point < n_edge_quadrature_points;
3013 ++q_point)
3014 {
3015 const Point<dim> quadrature_point_0(
3016 0.0,
3017 0.5 * (edge_quadrature_points[q_point][0] + subface));
3018 const Point<dim> quadrature_point_1(
3019 0.0, edge_quadrature_points[q_point][0]);
3020 const double tmp =
3021 edge_quadrature.weight(q_point) *
3022 (0.5 * this->shape_value_component(dof,
3023 quadrature_point_0,
3024 1) -
3025 interpolation_matrix(0, dof) *
3026 source_fe.shape_value_component(0,
3027 quadrature_point_1,
3028 1));
3029
3030 for (unsigned int i = 0; i < source_fe.degree - 1; ++i)
3031 system_rhs(i) +=
3032 tmp * legendre_polynomials[i + 1].value(
3033 edge_quadrature_points[q_point][0]);
3034 }
3035
3036 system_matrix_inv.vmult(solution, system_rhs);
3037
3038 for (unsigned int i = 0; i < source_fe.degree - 1; ++i)
3039 if (std::abs(solution(i)) > 1e-14)
3040 interpolation_matrix(i + 1, dof) = solution(i);
3041 }
3042 }
3043
3044 break;
3045 }
3046
3047 case 3:
3048 {
3049 const double shifts[4][2] = {{0.0, 0.0},
3050 {1.0, 0.0},
3051 {0.0, 1.0},
3052 {1.0, 1.0}};
3053
3054 for (unsigned int dof = 0; dof < this->n_dofs_per_face(face_no);
3055 ++dof)
3056 for (unsigned int q_point = 0; q_point < n_edge_quadrature_points;
3057 ++q_point)
3058 {
3059 const double weight = 0.5 * edge_quadrature.weight(q_point);
3060
3061 for (unsigned int i = 0; i < 2; ++i)
3062 {
3063 Point<dim> quadrature_point(
3064 0.5 * (i + shifts[subface][0]),
3065 0.5 * (edge_quadrature_points[q_point][0] +
3066 shifts[subface][1]),
3067 0.0);
3068
3069 interpolation_matrix(i * source_fe.degree, dof) +=
3070 weight *
3072 this->face_to_cell_index(dof, 4), quadrature_point, 1);
3073 quadrature_point =
3074 Point<dim>(0.5 * (edge_quadrature_points[q_point][0] +
3075 shifts[subface][0]),
3076 0.5 * (i + shifts[subface][1]),
3077 0.0);
3078 interpolation_matrix((i + 2) * source_fe.degree, dof) +=
3079 weight *
3080 this->shape_value_component(
3081 this->face_to_cell_index(dof, 4), quadrature_point, 0);
3082 }
3083 }
3084
3085 if (source_fe.degree > 1)
3086 {
3087 const std::vector<Polynomials::Polynomial<double>>
3088 &legendre_polynomials =
3090 source_fe.degree - 1);
3091 FullMatrix<double> system_matrix_inv(source_fe.degree - 1,
3092 source_fe.degree - 1);
3093
3094 {
3095 FullMatrix<double> assembling_matrix(source_fe.degree - 1,
3096 n_edge_quadrature_points);
3097
3098 for (unsigned int q_point = 0;
3099 q_point < n_edge_quadrature_points;
3100 ++q_point)
3101 {
3102 const double weight =
3103 std::sqrt(edge_quadrature.weight(q_point));
3104
3105 for (unsigned int i = 0; i < source_fe.degree - 1; ++i)
3106 assembling_matrix(i, q_point) =
3107 weight * legendre_polynomials[i + 1].value(
3108 edge_quadrature_points[q_point][0]);
3109 }
3110
3111 FullMatrix<double> system_matrix(source_fe.degree - 1,
3112 source_fe.degree - 1);
3113
3114 assembling_matrix.mTmult(system_matrix, assembling_matrix);
3115 system_matrix_inv.invert(system_matrix);
3116 }
3117
3118 FullMatrix<double> solution(source_fe.degree - 1,
3120 FullMatrix<double> system_rhs(source_fe.degree - 1,
3123
3124 for (unsigned int dof = 0; dof < this->n_dofs_per_face(face_no);
3125 ++dof)
3126 {
3127 system_rhs = 0.0;
3128
3129 for (unsigned int q_point = 0;
3130 q_point < n_edge_quadrature_points;
3131 ++q_point)
3132 {
3133 const double weight = edge_quadrature.weight(q_point);
3134
3135 for (unsigned int i = 0; i < 2; ++i)
3136 {
3137 Point<dim> quadrature_point_0(
3138 0.5 * (i + shifts[subface][0]),
3139 0.5 * (edge_quadrature_points[q_point][0] +
3140 shifts[subface][1]),
3141 0.0);
3142 Point<dim> quadrature_point_1(
3143 i, edge_quadrature_points[q_point][0], 0.0);
3144
3145 tmp(i) =
3146 weight *
3147 (0.5 * this->shape_value_component(
3148 this->face_to_cell_index(dof, 4),
3149 quadrature_point_0,
3150 1) -
3151 interpolation_matrix(i * source_fe.degree, dof) *
3152 source_fe.shape_value_component(
3153 i * source_fe.degree, quadrature_point_1, 1));
3154 quadrature_point_0 =
3155 Point<dim>(0.5 *
3156 (edge_quadrature_points[q_point][0] +
3157 shifts[subface][0]),
3158 0.5 * (i + shifts[subface][1]),
3159 0.0);
3160 quadrature_point_1 =
3161 Point<dim>(edge_quadrature_points[q_point][0],
3162 i,
3163 0.0);
3164 tmp(i + 2) =
3165 weight *
3166 (0.5 * this->shape_value_component(
3167 this->face_to_cell_index(dof, 4),
3168 quadrature_point_0,
3169 0) -
3170 interpolation_matrix((i + 2) * source_fe.degree,
3171 dof) *
3172 source_fe.shape_value_component(
3173 (i + 2) * source_fe.degree,
3174 quadrature_point_1,
3175 0));
3176 }
3177
3178 for (unsigned int i = 0; i < source_fe.degree - 1; ++i)
3179 {
3180 const double L_i = legendre_polynomials[i + 1].value(
3181 edge_quadrature_points[q_point][0]);
3182
3183 for (unsigned int j = 0;
3184 j < GeometryInfo<dim>::lines_per_face;
3185 ++j)
3186 system_rhs(i, j) += tmp(j) * L_i;
3187 }
3188 }
3189
3190 system_matrix_inv.mmult(solution, system_rhs);
3191
3192 for (unsigned int i = 0;
3193 i < GeometryInfo<dim>::lines_per_face;
3194 ++i)
3195 for (unsigned int j = 0; j < source_fe.degree - 1; ++j)
3196 if (std::abs(solution(j, i)) > 1e-14)
3197 interpolation_matrix(i * source_fe.degree + j + 1,
3198 dof) = solution(j, i);
3199 }
3200
3201 const QGauss<2> quadrature(source_fe.degree);
3202 const std::vector<Point<2>> &quadrature_points =
3203 quadrature.get_points();
3204 const std::vector<Polynomials::Polynomial<double>>
3205 &lobatto_polynomials =
3207 source_fe.degree);
3208 const unsigned int n_boundary_dofs =
3210 const unsigned int n_quadrature_points = quadrature.size();
3211
3212 {
3213 FullMatrix<double> assembling_matrix(source_fe.degree *
3214 (source_fe.degree - 1),
3215 n_quadrature_points);
3216
3217 for (unsigned int q_point = 0; q_point < n_quadrature_points;
3218 ++q_point)
3219 {
3220 const double weight = std::sqrt(quadrature.weight(q_point));
3221
3222 for (unsigned int i = 0; i < source_fe.degree; ++i)
3223 {
3224 const double L_i =
3225 weight * legendre_polynomials[i].value(
3226 quadrature_points[q_point][0]);
3227
3228 for (unsigned int j = 0; j < source_fe.degree - 1; ++j)
3229 assembling_matrix(i * (source_fe.degree - 1) + j,
3230 q_point) =
3231 L_i * lobatto_polynomials[j + 2].value(
3232 quadrature_points[q_point][1]);
3233 }
3234 }
3235
3236 FullMatrix<double> system_matrix(assembling_matrix.m(),
3237 assembling_matrix.m());
3238
3239 assembling_matrix.mTmult(system_matrix, assembling_matrix);
3240 system_matrix_inv.reinit(system_matrix.m(), system_matrix.m());
3241 system_matrix_inv.invert(system_matrix);
3242 }
3243
3244 solution.reinit(system_matrix_inv.m(), 2);
3245 system_rhs.reinit(system_matrix_inv.m(), 2);
3246 tmp.reinit(2);
3247
3248 for (unsigned int dof = 0; dof < this->n_dofs_per_face(face_no);
3249 ++dof)
3250 {
3251 system_rhs = 0.0;
3252
3253 for (unsigned int q_point = 0; q_point < n_quadrature_points;
3254 ++q_point)
3255 {
3256 Point<dim> quadrature_point(
3257 0.5 *
3258 (quadrature_points[q_point][0] + shifts[subface][0]),
3259 0.5 *
3260 (quadrature_points[q_point][1] + shifts[subface][1]),
3261 0.0);
3262 tmp(0) = 0.5 * this->shape_value_component(
3263 this->face_to_cell_index(dof, 4),
3264 quadrature_point,
3265 0);
3266 tmp(1) = 0.5 * this->shape_value_component(
3267 this->face_to_cell_index(dof, 4),
3268 quadrature_point,
3269 1);
3270 quadrature_point =
3271 Point<dim>(quadrature_points[q_point][0],
3272 quadrature_points[q_point][1],
3273 0.0);
3274
3275 for (unsigned int i = 0; i < 2; ++i)
3276 for (unsigned int j = 0; j < source_fe.degree; ++j)
3277 {
3278 tmp(0) -= interpolation_matrix(
3279 (i + 2) * source_fe.degree + j, dof) *
3280 source_fe.shape_value_component(
3281 (i + 2) * source_fe.degree + j,
3282 quadrature_point,
3283 0);
3284 tmp(1) -=
3285 interpolation_matrix(i * source_fe.degree + j,
3286 dof) *
3287 source_fe.shape_value_component(
3288 i * source_fe.degree + j, quadrature_point, 1);
3289 }
3290
3291 tmp *= quadrature.weight(q_point);
3292
3293 for (unsigned int i = 0; i < source_fe.degree; ++i)
3294 {
3295 const double L_i_0 = legendre_polynomials[i].value(
3296 quadrature_points[q_point][0]);
3297 const double L_i_1 = legendre_polynomials[i].value(
3298 quadrature_points[q_point][1]);
3299
3300 for (unsigned int j = 0; j < source_fe.degree - 1;
3301 ++j)
3302 {
3303 system_rhs(i * (source_fe.degree - 1) + j, 0) +=
3304 tmp(0) * L_i_0 *
3305 lobatto_polynomials[j + 2].value(
3306 quadrature_points[q_point][1]);
3307 system_rhs(i * (source_fe.degree - 1) + j, 1) +=
3308 tmp(1) * L_i_1 *
3309 lobatto_polynomials[j + 2].value(
3310 quadrature_points[q_point][0]);
3311 }
3312 }
3313 }
3314
3315 system_matrix_inv.mmult(solution, system_rhs);
3316
3317 for (unsigned int i = 0; i < source_fe.degree; ++i)
3318 for (unsigned int j = 0; j < source_fe.degree - 1; ++j)
3319 {
3320 if (std::abs(solution(i * (source_fe.degree - 1) + j,
3321 0)) > 1e-14)
3322 interpolation_matrix(i * (source_fe.degree - 1) + j +
3323 n_boundary_dofs,
3324 dof) =
3325 solution(i * (source_fe.degree - 1) + j, 0);
3326
3327 if (std::abs(solution(i * (source_fe.degree - 1) + j,
3328 1)) > 1e-14)
3329 interpolation_matrix(
3330 i + (j + source_fe.degree - 1) * source_fe.degree +
3331 n_boundary_dofs,
3332 dof) = solution(i * (source_fe.degree - 1) + j, 1);
3333 }
3334 }
3335 }
3336
3337 break;
3338 }
3339
3340 default:
3342 }
3343}
3344
3345template <int dim>
3346const FullMatrix<double> &
3348 const unsigned int child,
3349 const RefinementCase<dim> &refinement_case) const
3350{
3351 AssertIndexRange(refinement_case,
3353 Assert(refinement_case != RefinementCase<dim>::no_refinement,
3354 ExcMessage(
3355 "Prolongation matrices are only available for refined cells!"));
3357 child, this->reference_cell().template n_children<dim>(refinement_case));
3358
3359 // initialization upon first request
3360 if (this->prolongation[refinement_case - 1][child].n() == 0)
3361 {
3362 std::lock_guard<std::mutex> lock(prolongation_matrix_mutex);
3363
3364 // if matrix got updated while waiting for the lock
3365 if (this->prolongation[refinement_case - 1][child].n() ==
3366 this->n_dofs_per_cell())
3367 return this->prolongation[refinement_case - 1][child];
3368
3369 // now do the work. need to get a non-const version of data in order to
3370 // be able to modify them inside a const function
3371 FE_Nedelec<dim> &this_nonconst = const_cast<FE_Nedelec<dim> &>(*this);
3372
3373 // Reinit the vectors of
3374 // restriction and prolongation
3375 // matrices to the right sizes.
3376 // Restriction only for isotropic
3377 // refinement
3378#ifdef DEBUG_NEDELEC
3379 deallog << "Embedding" << std::endl;
3380#endif
3382 // Fill prolongation matrices with embedding operators
3384 this_nonconst,
3385 this_nonconst.prolongation,
3386 true,
3387 internal::FE_Nedelec::get_embedding_computation_tolerance(
3388 this->degree));
3389#ifdef DEBUG_NEDELEC
3390 deallog << "Restriction" << std::endl;
3391#endif
3392 this_nonconst.initialize_restriction();
3393 }
3394
3395 // we use refinement_case-1 here. the -1 takes care of the origin of the
3396 // vector, as for RefinementCase<dim>::no_refinement (=0) there is no data
3397 // available and so the vector indices are shifted
3398 return this->prolongation[refinement_case - 1][child];
3399}
3400
3401template <int dim>
3402const FullMatrix<double> &
3404 const unsigned int child,
3405 const RefinementCase<dim> &refinement_case) const
3406{
3407 AssertIndexRange(refinement_case,
3409 Assert(refinement_case != RefinementCase<dim>::no_refinement,
3410 ExcMessage(
3411 "Restriction matrices are only available for refined cells!"));
3412 AssertIndexRange(child,
3413 this->reference_cell().template n_children<dim>(
3414 RefinementCase<dim>(refinement_case)));
3415
3416 // initialization upon first request
3417 if (this->restriction[refinement_case - 1][child].n() == 0)
3418 {
3419 std::lock_guard<std::mutex> lock(restriction_matrix_mutex);
3420
3421 // if matrix got updated while waiting for the lock...
3422 if (this->restriction[refinement_case - 1][child].n() ==
3423 this->n_dofs_per_cell())
3424 return this->restriction[refinement_case - 1][child];
3425
3426 // now do the work. need to get a non-const version of data in order to
3427 // be able to modify them inside a const function
3428 FE_Nedelec<dim> &this_nonconst = const_cast<FE_Nedelec<dim> &>(*this);
3429
3430 // Reinit the vectors of
3431 // restriction and prolongation
3432 // matrices to the right sizes.
3433 // Restriction only for isotropic
3434 // refinement
3435#ifdef DEBUG_NEDELEC
3436 deallog << "Embedding" << std::endl;
3437#endif
3439 // Fill prolongation matrices with embedding operators
3441 this_nonconst,
3442 this_nonconst.prolongation,
3443 true,
3444 internal::FE_Nedelec::get_embedding_computation_tolerance(
3445 this->degree));
3446#ifdef DEBUG_NEDELEC
3447 deallog << "Restriction" << std::endl;
3448#endif
3449 this_nonconst.initialize_restriction();
3450 }
3451
3452 // we use refinement_case-1 here. the -1 takes care of the origin of the
3453 // vector, as for RefinementCase<dim>::no_refinement (=0) there is no data
3454 // available and so the vector indices are shifted
3455 return this->restriction[refinement_case - 1][child];
3456}
3457
3458
3459// Interpolate a function, which is given by
3460// its values at the generalized support
3461// points in the finite element space on the
3462// reference cell.
3463// This is done as usual by projection-based
3464// interpolation.
3465template <int dim>
3466void
3468 const std::vector<Vector<double>> &support_point_values,
3469 std::vector<double> &nodal_values) const
3470{
3471 // TODO: the implementation makes the assumption that all faces have the
3472 // same number of dofs
3473 AssertDimension(this->n_unique_faces(), 1);
3474 const unsigned int face_no = 0;
3475
3476 const unsigned int deg = this->degree - 1;
3477 Assert(support_point_values.size() == this->generalized_support_points.size(),
3478 ExcDimensionMismatch(support_point_values.size(),
3479 this->generalized_support_points.size()));
3480 Assert(support_point_values[0].size() == this->n_components(),
3481 ExcDimensionMismatch(support_point_values[0].size(),
3482 this->n_components()));
3483 Assert(nodal_values.size() == this->n_dofs_per_cell(),
3484 ExcDimensionMismatch(nodal_values.size(), this->n_dofs_per_cell()));
3485 std::fill(nodal_values.begin(), nodal_values.end(), 0.0);
3486
3487 switch (dim)
3488 {
3489 case 2:
3490 {
3491 // Let us begin with the
3492 // interpolation part.
3493 const QGauss<1> reference_edge_quadrature(this->degree);
3494 const unsigned int n_edge_points = reference_edge_quadrature.size();
3495
3496 for (unsigned int i = 0; i < 2; ++i)
3497 for (unsigned int j = 0; j < 2; ++j)
3498 {
3499 for (unsigned int q_point = 0; q_point < n_edge_points;
3500 ++q_point)
3501 nodal_values[(i + 2 * j) * this->degree] +=
3502 reference_edge_quadrature.weight(q_point) *
3503 support_point_values[q_point + (i + 2 * j) * n_edge_points]
3504 [1 - j];
3505
3506 // Add the computed support_point_values to the resulting vector
3507 // only, if they are not
3508 // too small.
3509 if (std::abs(nodal_values[(i + 2 * j) * this->degree]) < 1e-14)
3510 nodal_values[(i + 2 * j) * this->degree] = 0.0;
3511 }
3512
3513 // If the Nedelec element degree is greater
3514 // than 0 (i.e., the polynomial degree is greater than 1),
3515 // then we have still some higher order edge
3516 // shape functions to consider.
3517 // Note that this->degree returns the polynomial
3518 // degree.
3519 // Here the projection part starts.
3520 // The dof support_point_values are obtained by solving
3521 // a linear system of
3522 // equations.
3523 if (this->degree > 1)
3524 {
3525 // We start with projection
3526 // on the higher order edge
3527 // shape function.
3528 const std::vector<Polynomials::Polynomial<double>>
3529 &lobatto_polynomials =
3531 FullMatrix<double> system_matrix(this->degree - 1,
3532 this->degree - 1);
3533 std::vector<Polynomials::Polynomial<double>>
3534 lobatto_polynomials_grad(this->degree);
3535
3536 for (unsigned int i = 0; i < lobatto_polynomials_grad.size(); ++i)
3537 lobatto_polynomials_grad[i] =
3538 lobatto_polynomials[i + 1].derivative();
3539
3540 // Set up the system matrix.
3541 // This can be used for all
3542 // edges.
3543 for (unsigned int i = 0; i < system_matrix.m(); ++i)
3544 for (unsigned int j = 0; j < system_matrix.n(); ++j)
3545 for (unsigned int q_point = 0; q_point < n_edge_points;
3546 ++q_point)
3547 system_matrix(i, j) +=
3548 boundary_weights(q_point, j) *
3549 lobatto_polynomials_grad[i + 1].value(
3550 this->generalized_face_support_points[face_no][q_point]
3551 [0]);
3552
3553 FullMatrix<double> system_matrix_inv(this->degree - 1,
3554 this->degree - 1);
3555
3556 system_matrix_inv.invert(system_matrix);
3557
3558 const unsigned int
3559 line_coordinate[GeometryInfo<2>::lines_per_cell] = {1, 1, 0, 0};
3560 Vector<double> system_rhs(system_matrix.m());
3561 Vector<double> solution(system_rhs.size());
3562
3563 for (unsigned int line = 0;
3564 line < GeometryInfo<dim>::lines_per_cell;
3565 ++line)
3566 {
3567 // Set up the right hand side.
3568 system_rhs = 0;
3569
3570 for (unsigned int q_point = 0; q_point < n_edge_points;
3571 ++q_point)
3572 {
3573 const double tmp =
3574 support_point_values[line * n_edge_points + q_point]
3575 [line_coordinate[line]] -
3576 nodal_values[line * this->degree] *
3578 line * this->degree,
3579 this->generalized_support_points[line *
3580 n_edge_points +
3581 q_point],
3582 line_coordinate[line]);
3583
3584 for (unsigned int i = 0; i < system_rhs.size(); ++i)
3585 system_rhs(i) += boundary_weights(q_point, i) * tmp;
3586 }
3587
3588 system_matrix_inv.vmult(solution, system_rhs);
3589
3590 // Add the computed support_point_values
3591 // to the resulting vector
3592 // only, if they are not
3593 // too small.
3594 for (unsigned int i = 0; i < solution.size(); ++i)
3595 if (std::abs(solution(i)) > 1e-14)
3596 nodal_values[line * this->degree + i + 1] = solution(i);
3597 }
3598
3599 // Then we go on to the
3600 // interior shape
3601 // functions. Again we
3602 // set up the system
3603 // matrix and use it
3604 // for both, the
3605 // horizontal and the
3606 // vertical, interior
3607 // shape functions.
3608 const QGauss<dim> reference_quadrature(this->degree);
3609 const unsigned int n_interior_points =
3610 reference_quadrature.size();
3611 const std::vector<Polynomials::Polynomial<double>>
3612 &legendre_polynomials =
3614 1);
3615
3616 system_matrix.reinit((this->degree - 1) * this->degree,
3617 (this->degree - 1) * this->degree);
3618 system_matrix = 0;
3619
3620 for (unsigned int i = 0; i < this->degree; ++i)
3621 for (unsigned int j = 0; j < this->degree - 1; ++j)
3622 for (unsigned int k = 0; k < this->degree; ++k)
3623 for (unsigned int l = 0; l < this->degree - 1; ++l)
3624 for (unsigned int q_point = 0;
3625 q_point < n_interior_points;
3626 ++q_point)
3627 system_matrix(i * (this->degree - 1) + j,
3628 k * (this->degree - 1) + l) +=
3629 reference_quadrature.weight(q_point) *
3630 legendre_polynomials[i].value(
3633 n_edge_points][0]) *
3634 lobatto_polynomials[j + 2].value(
3637 n_edge_points][1]) *
3638 lobatto_polynomials_grad[k].value(
3641 n_edge_points][0]) *
3642 lobatto_polynomials[l + 2].value(
3645 n_edge_points][1]);
3646
3647 system_matrix_inv.reinit(system_matrix.m(), system_matrix.m());
3648 system_matrix_inv.invert(system_matrix);
3649 // Set up the right hand side
3650 // for the horizontal shape
3651 // functions.
3652 system_rhs.reinit(system_matrix_inv.m());
3653 system_rhs = 0;
3654
3655 for (unsigned int q_point = 0; q_point < n_interior_points;
3656 ++q_point)
3657 {
3658 double tmp =
3659 support_point_values[q_point +
3661 n_edge_points][0];
3662
3663 for (unsigned int i = 0; i < 2; ++i)
3664 for (unsigned int j = 0; j <= deg; ++j)
3665 tmp -= nodal_values[(i + 2) * this->degree + j] *
3667 (i + 2) * this->degree + j,
3670 n_edge_points],
3671 0);
3672
3673 for (unsigned int i = 0; i <= deg; ++i)
3674 for (unsigned int j = 0; j < deg; ++j)
3675 system_rhs(i * deg + j) +=
3676 reference_quadrature.weight(q_point) * tmp *
3677 lobatto_polynomials_grad[i].value(
3680 n_edge_points][0]) *
3681 lobatto_polynomials[j + 2].value(
3684 n_edge_points][1]);
3685 }
3686
3687 solution.reinit(system_matrix.m());
3688 system_matrix_inv.vmult(solution, system_rhs);
3689
3690 // Add the computed support_point_values
3691 // to the resulting vector
3692 // only, if they are not
3693 // too small.
3694 for (unsigned int i = 0; i <= deg; ++i)
3695 for (unsigned int j = 0; j < deg; ++j)
3696 if (std::abs(solution(i * deg + j)) > 1e-14)
3697 nodal_values[(i + GeometryInfo<dim>::lines_per_cell) * deg +
3699 solution(i * deg + j);
3700
3701 system_rhs = 0;
3702 // Set up the right hand side
3703 // for the vertical shape
3704 // functions.
3705
3706 for (unsigned int q_point = 0; q_point < n_interior_points;
3707 ++q_point)
3708 {
3709 double tmp =
3710 support_point_values[q_point +
3712 n_edge_points][1];
3713
3714 for (unsigned int i = 0; i < 2; ++i)
3715 for (unsigned int j = 0; j <= deg; ++j)
3716 tmp -= nodal_values[i * this->degree + j] *
3718 i * this->degree + j,
3721 n_edge_points],
3722 1);
3723
3724 for (unsigned int i = 0; i <= deg; ++i)
3725 for (unsigned int j = 0; j < deg; ++j)
3726 system_rhs(i * deg + j) +=
3727 reference_quadrature.weight(q_point) * tmp *
3728 lobatto_polynomials_grad[i].value(
3731 n_edge_points][1]) *
3732 lobatto_polynomials[j + 2].value(
3735 n_edge_points][0]);
3736 }
3737
3738 system_matrix_inv.vmult(solution, system_rhs);
3739
3740 // Add the computed support_point_values
3741 // to the resulting vector
3742 // only, if they are not
3743 // too small.
3744 for (unsigned int i = 0; i <= deg; ++i)
3745 for (unsigned int j = 0; j < deg; ++j)
3746 if (std::abs(solution(i * deg + j)) > 1e-14)
3747 nodal_values[i +
3749 this->degree] = solution(i * deg + j);
3750 }
3751
3752 break;
3753 }
3754
3755 case 3:
3756 {
3757 // Let us begin with the
3758 // interpolation part.
3759 const QGauss<1> reference_edge_quadrature(this->degree);
3760 const unsigned int n_edge_points = reference_edge_quadrature.size();
3761
3762 for (unsigned int q_point = 0; q_point < n_edge_points; ++q_point)
3763 {
3764 for (unsigned int i = 0; i < 4; ++i)
3765 nodal_values[(i + 8) * this->degree] +=
3766 reference_edge_quadrature.weight(q_point) *
3767 support_point_values[q_point + (i + 8) * n_edge_points][2];
3768
3769 for (unsigned int i = 0; i < 2; ++i)
3770 for (unsigned int j = 0; j < 2; ++j)
3771 for (unsigned int k = 0; k < 2; ++k)
3772 nodal_values[(i + 2 * (2 * j + k)) * this->degree] +=
3773 reference_edge_quadrature.weight(q_point) *
3774 support_point_values[q_point + (i + 2 * (2 * j + k)) *
3775 n_edge_points][1 - k];
3776 }
3777
3778 // Add the computed support_point_values
3779 // to the resulting vector
3780 // only, if they are not
3781 // too small.
3782 for (unsigned int i = 0; i < 4; ++i)
3783 if (std::abs(nodal_values[(i + 8) * this->degree]) < 1e-14)
3784 nodal_values[(i + 8) * this->degree] = 0.0;
3785
3786 for (unsigned int i = 0; i < 2; ++i)
3787 for (unsigned int j = 0; j < 2; ++j)
3788 for (unsigned int k = 0; k < 2; ++k)
3789 if (std::abs(
3790 nodal_values[(i + 2 * (2 * j + k)) * this->degree]) <
3791 1e-14)
3792 nodal_values[(i + 2 * (2 * j + k)) * this->degree] = 0.0;
3793
3794 // If the degree is greater
3795 // than 0, then we have still
3796 // some higher order shape
3797 // functions to consider.
3798 // Here the projection part
3799 // starts. The dof support_point_values
3800 // are obtained by solving
3801 // a linear system of
3802 // equations.
3803 if (this->degree > 1)
3804 {
3805 // We start with projection
3806 // on the higher order edge
3807 // shape function.
3808 const std::vector<Polynomials::Polynomial<double>>
3809 &lobatto_polynomials =
3811 FullMatrix<double> system_matrix(this->degree - 1,
3812 this->degree - 1);
3813 std::vector<Polynomials::Polynomial<double>>
3814 lobatto_polynomials_grad(this->degree);
3815
3816 for (unsigned int i = 0; i < lobatto_polynomials_grad.size(); ++i)
3817 lobatto_polynomials_grad[i] =
3818 lobatto_polynomials[i + 1].derivative();
3819
3820 // Set up the system matrix.
3821 // This can be used for all
3822 // edges.
3823 for (unsigned int i = 0; i < system_matrix.m(); ++i)
3824 for (unsigned int j = 0; j < system_matrix.n(); ++j)
3825 for (unsigned int q_point = 0; q_point < n_edge_points;
3826 ++q_point)
3827 system_matrix(i, j) +=
3828 boundary_weights(q_point, j) *
3829 lobatto_polynomials_grad[i + 1].value(
3830 this->generalized_face_support_points[face_no][q_point]
3831 [1]);
3832
3833 FullMatrix<double> system_matrix_inv(this->degree - 1,
3834 this->degree - 1);
3835
3836 system_matrix_inv.invert(system_matrix);
3837
3838 const unsigned int
3839 line_coordinate[GeometryInfo<3>::lines_per_cell] = {
3840 1, 1, 0, 0, 1, 1, 0, 0, 2, 2, 2, 2};
3841 Vector<double> system_rhs(system_matrix.m());
3842 Vector<double> solution(system_rhs.size());
3843
3844 for (unsigned int line = 0;
3845 line < GeometryInfo<dim>::lines_per_cell;
3846 ++line)
3847 {
3848 // Set up the right hand side.
3849 system_rhs = 0;
3850
3851 for (unsigned int q_point = 0; q_point < this->degree;
3852 ++q_point)
3853 {
3854 const double tmp =
3855 support_point_values[line * this->degree + q_point]
3856 [line_coordinate[line]] -
3857 nodal_values[line * this->degree] *
3859 line * this->degree,
3860 this
3861 ->generalized_support_points[line * this->degree +
3862 q_point],
3863 line_coordinate[line]);
3864
3865 for (unsigned int i = 0; i < system_rhs.size(); ++i)
3866 system_rhs(i) += boundary_weights(q_point, i) * tmp;
3867 }
3868
3869 system_matrix_inv.vmult(solution, system_rhs);
3870
3871 // Add the computed values
3872 // to the resulting vector
3873 // only, if they are not
3874 // too small.
3875 for (unsigned int i = 0; i < solution.size(); ++i)
3876 if (std::abs(solution(i)) > 1e-14)
3877 nodal_values[line * this->degree + i + 1] = solution(i);
3878 }
3879
3880 // Then we go on to the
3881 // face shape functions.
3882 // Again we set up the
3883 // system matrix and
3884 // use it for both, the
3885 // horizontal and the
3886 // vertical, shape
3887 // functions.
3888 const std::vector<Polynomials::Polynomial<double>>
3889 &legendre_polynomials =
3891 1);
3892 const unsigned int n_face_points = n_edge_points * n_edge_points;
3893
3894 system_matrix.reinit((this->degree - 1) * this->degree,
3895 (this->degree - 1) * this->degree);
3896 system_matrix = 0;
3897
3898 for (unsigned int i = 0; i < this->degree; ++i)
3899 for (unsigned int j = 0; j < this->degree - 1; ++j)
3900 for (unsigned int k = 0; k < this->degree; ++k)
3901 for (unsigned int l = 0; l < this->degree - 1; ++l)
3902 for (unsigned int q_point = 0; q_point < n_face_points;
3903 ++q_point)
3904 system_matrix(i * (this->degree - 1) + j,
3905 k * (this->degree - 1) + l) +=
3906 boundary_weights(q_point + n_edge_points,
3907 2 * (k * (this->degree - 1) + l)) *
3908 legendre_polynomials[i].value(
3910 [face_no][q_point + 4 * n_edge_points][0]) *
3911 lobatto_polynomials[j + 2].value(
3913 [face_no][q_point + 4 * n_edge_points][1]);
3914
3915 system_matrix_inv.reinit(system_matrix.m(), system_matrix.m());
3916 system_matrix_inv.invert(system_matrix);
3917 solution.reinit(system_matrix.m());
3918 system_rhs.reinit(system_matrix.m());
3919
3920 const unsigned int
3921 face_coordinates[GeometryInfo<3>::faces_per_cell][2] = {
3922 {1, 2}, {1, 2}, {2, 0}, {2, 0}, {0, 1}, {0, 1}};
3923 const unsigned int edge_indices[GeometryInfo<3>::faces_per_cell]
3925 {{0, 4, 8, 10},
3926 {1, 5, 9, 11},
3927 {8, 9, 2, 6},
3928 {10, 11, 3, 7},
3929 {2, 3, 0, 1},
3930 {6, 7, 4, 5}};
3931
3932 for (const unsigned int face : GeometryInfo<dim>::face_indices())
3933 {
3934 // Set up the right hand side
3935 // for the horizontal shape
3936 // functions.
3937 system_rhs = 0;
3938
3939 for (unsigned int q_point = 0; q_point < n_face_points;
3940 ++q_point)
3941 {
3942 double tmp =
3943 support_point_values[q_point +
3945 n_edge_points]
3946 [face_coordinates[face][0]];
3947
3948 for (unsigned int i = 0; i < 2; ++i)
3949 for (unsigned int j = 0; j <= deg; ++j)
3950 tmp -=
3951 nodal_values[edge_indices[face][i] * this->degree +
3952 j] *
3954 edge_indices[face][i] * this->degree + j,
3957 n_edge_points],
3958 face_coordinates[face][0]);
3959
3960 for (unsigned int i = 0; i <= deg; ++i)
3961 for (unsigned int j = 0; j < deg; ++j)
3962 system_rhs(i * deg + j) +=
3963 boundary_weights(q_point + n_edge_points,
3964 2 * (i * deg + j)) *
3965 tmp;
3966 }
3967
3968 system_matrix_inv.vmult(solution, system_rhs);
3969
3970 // Add the computed support_point_values
3971 // to the resulting vector
3972 // only, if they are not
3973 // too small.
3974 for (unsigned int i = 0; i <= deg; ++i)
3975 for (unsigned int j = 0; j < deg; ++j)
3976 if (std::abs(solution(i * deg + j)) > 1e-14)
3977 nodal_values[(2 * face * this->degree + i +
3979 deg +
3981 solution(i * deg + j);
3982
3983 // Set up the right hand side
3984 // for the vertical shape
3985 // functions.
3986 system_rhs = 0;
3987
3988 for (unsigned int q_point = 0; q_point < n_face_points;
3989 ++q_point)
3990 {
3991 double tmp =
3992 support_point_values[q_point +
3994 n_edge_points]
3995 [face_coordinates[face][1]];
3996
3997 for (unsigned int i = 2;
3998 i < GeometryInfo<dim>::lines_per_face;
3999 ++i)
4000 for (unsigned int j = 0; j <= deg; ++j)
4001 tmp -=
4002 nodal_values[edge_indices[face][i] * this->degree +
4003 j] *
4005 edge_indices[face][i] * this->degree + j,
4008 n_edge_points],
4009 face_coordinates[face][1]);
4010
4011 for (unsigned int i = 0; i <= deg; ++i)
4012 for (unsigned int j = 0; j < deg; ++j)
4013 system_rhs(i * deg + j) +=
4014 boundary_weights(q_point + n_edge_points,
4015 2 * (i * deg + j) + 1) *
4016 tmp;
4017 }
4018
4019 system_matrix_inv.vmult(solution, system_rhs);
4020
4021 // Add the computed support_point_values
4022 // to the resulting vector
4023 // only, if they are not
4024 // too small.
4025 for (unsigned int i = 0; i <= deg; ++i)
4026 for (unsigned int j = 0; j < deg; ++j)
4027 if (std::abs(solution(i * deg + j)) > 1e-14)
4028 nodal_values[((2 * face + 1) * deg + j +
4030 this->degree +
4031 i] = solution(i * deg + j);
4032 }
4033
4034 // Finally we project
4035 // the remaining parts
4036 // of the function on
4037 // the interior shape
4038 // functions.
4039 const QGauss<dim> reference_quadrature(this->degree);
4040 const unsigned int n_interior_points =
4041 reference_quadrature.size();
4042
4043 // We create the
4044 // system matrix.
4045 system_matrix.reinit(this->degree * deg * deg,
4046 this->degree * deg * deg);
4047 system_matrix = 0;
4048
4049 for (unsigned int i = 0; i <= deg; ++i)
4050 for (unsigned int j = 0; j < deg; ++j)
4051 for (unsigned int k = 0; k < deg; ++k)
4052 for (unsigned int l = 0; l <= deg; ++l)
4053 for (unsigned int m = 0; m < deg; ++m)
4054 for (unsigned int n = 0; n < deg; ++n)
4055 for (unsigned int q_point = 0;
4056 q_point < n_interior_points;
4057 ++q_point)
4058 system_matrix((i * deg + j) * deg + k,
4059 (l * deg + m) * deg + n) +=
4060 reference_quadrature.weight(q_point) *
4061 legendre_polynomials[i].value(
4063 [q_point +
4065 n_edge_points +
4067 n_face_points][0]) *
4068 lobatto_polynomials[j + 2].value(
4070 [q_point +
4072 n_edge_points +
4074 n_face_points][1]) *
4075 lobatto_polynomials[k + 2].value(
4077 [q_point +
4079 n_edge_points +
4081 n_face_points][2]) *
4082 lobatto_polynomials_grad[l].value(
4084 [q_point +
4086 n_edge_points +
4088 n_face_points][0]) *
4089 lobatto_polynomials[m + 2].value(
4091 [q_point +
4093 n_edge_points +
4095 n_face_points][1]) *
4096 lobatto_polynomials[n + 2].value(
4098 [q_point +
4100 n_edge_points +
4102 n_face_points][2]);
4103
4104 system_matrix_inv.reinit(system_matrix.m(), system_matrix.m());
4105 system_matrix_inv.invert(system_matrix);
4106 // Set up the right hand side.
4107 system_rhs.reinit(system_matrix.m());
4108 system_rhs = 0;
4109
4110 for (unsigned int q_point = 0; q_point < n_interior_points;
4111 ++q_point)
4112 {
4113 double tmp =
4114 support_point_values[q_point +
4116 n_edge_points +
4118 n_face_points][0];
4119
4120 for (unsigned int i = 0; i <= deg; ++i)
4121 {
4122 for (unsigned int j = 0; j < 2; ++j)
4123 for (unsigned int k = 0; k < 2; ++k)
4124 tmp -=
4125 nodal_values[i + (j + 4 * k + 2) * this->degree] *
4127 i + (j + 4 * k + 2) * this->degree,
4129 [q_point +
4131 n_edge_points +
4133 n_face_points],
4134 0);
4135
4136 for (unsigned int j = 0; j < deg; ++j)
4137 for (unsigned int k = 0; k < 4; ++k)
4138 tmp -=
4139 nodal_values[(i + 2 * (k + 2) * this->degree +
4141 deg +
4142 j +
4144 this->shape_value_component(
4145 (i + 2 * (k + 2) * this->degree +
4147 deg +
4150 [q_point +
4152 n_edge_points +
4154 n_face_points],
4155 0);
4156 }
4157
4158 for (unsigned int i = 0; i <= deg; ++i)
4159 for (unsigned int j = 0; j < deg; ++j)
4160 for (unsigned int k = 0; k < deg; ++k)
4161 system_rhs((i * deg + j) * deg + k) +=
4162 reference_quadrature.weight(q_point) * tmp *
4163 lobatto_polynomials_grad[i].value(
4165 [q_point +
4167 n_edge_points +
4169 n_face_points][0]) *
4170 lobatto_polynomials[j + 2].value(
4172 [q_point +
4174 n_edge_points +
4176 n_face_points][1]) *
4177 lobatto_polynomials[k + 2].value(
4179 [q_point +
4181 n_edge_points +
4183 n_face_points][2]);
4184 }
4185
4186 solution.reinit(system_rhs.size());
4187 system_matrix_inv.vmult(solution, system_rhs);
4188
4189 // Add the computed values
4190 // to the resulting vector
4191 // only, if they are not
4192 // too small.
4193 for (unsigned int i = 0; i <= deg; ++i)
4194 for (unsigned int j = 0; j < deg; ++j)
4195 for (unsigned int k = 0; k < deg; ++k)
4196 if (std::abs(solution((i * deg + j) * deg + k)) > 1e-14)
4197 nodal_values
4198 [((i + 2 * GeometryInfo<dim>::faces_per_cell) * deg +
4201 deg +
4203 solution((i * deg + j) * deg + k);
4204
4205 // Set up the right hand side.
4206 system_rhs = 0;
4207
4208 for (unsigned int q_point = 0; q_point < n_interior_points;
4209 ++q_point)
4210 {
4211 double tmp =
4212 support_point_values[q_point +
4214 n_edge_points +
4216 n_face_points][1];
4217
4218 for (unsigned int i = 0; i <= deg; ++i)
4219 for (unsigned int j = 0; j < 2; ++j)
4220 {
4221 for (unsigned int k = 0; k < 2; ++k)
4222 tmp -= nodal_values[i + (4 * j + k) * this->degree] *
4224 i + (4 * j + k) * this->degree,
4226 [q_point +
4228 n_edge_points +
4230 n_face_points],
4231 1);
4232
4233 for (unsigned int k = 0; k < deg; ++k)
4234 tmp -=
4235 nodal_values[(i + 2 * j * this->degree +
4237 deg +
4238 k +
4240 this->shape_value_component(
4241 (i + 2 * j * this->degree +
4243 deg +
4246 [q_point +
4248 n_edge_points +
4250 n_face_points],
4251 1) +
4252 nodal_values[i +
4253 ((2 * j + 9) * deg + k +
4255 this->degree] *
4256 this->shape_value_component(
4257 i + ((2 * j + 9) * deg + k +
4259 this->degree,
4261 [q_point +
4263 n_edge_points +
4265 n_face_points],
4266 1);
4267 }
4268
4269 for (unsigned int i = 0; i <= deg; ++i)
4270 for (unsigned int j = 0; j < deg; ++j)
4271 for (unsigned int k = 0; k < deg; ++k)
4272 system_rhs((i * deg + j) * deg + k) +=
4273 reference_quadrature.weight(q_point) * tmp *
4274 lobatto_polynomials_grad[i].value(
4276 [q_point +
4278 n_edge_points +
4280 n_face_points][1]) *
4281 lobatto_polynomials[j + 2].value(
4283 [q_point +
4285 n_edge_points +
4287 n_face_points][0]) *
4288 lobatto_polynomials[k + 2].value(
4290 [q_point +
4292 n_edge_points +
4294 n_face_points][2]);
4295 }
4296
4297 system_matrix_inv.vmult(solution, system_rhs);
4298
4299 // Add the computed support_point_values
4300 // to the resulting vector
4301 // only, if they are not
4302 // too small.
4303 for (unsigned int i = 0; i <= deg; ++i)
4304 for (unsigned int j = 0; j < deg; ++j)
4305 for (unsigned int k = 0; k < deg; ++k)
4306 if (std::abs(solution((i * deg + j) * deg + k)) > 1e-14)
4307 nodal_values[((i + this->degree +
4309 deg +
4312 deg +
4314 solution((i * deg + j) * deg + k);
4315
4316 // Set up the right hand side.
4317 system_rhs = 0;
4318
4319 for (unsigned int q_point = 0; q_point < n_interior_points;
4320 ++q_point)
4321 {
4322 double tmp =
4323 support_point_values[q_point +
4325 n_edge_points +
4327 n_face_points][2];
4328
4329 for (unsigned int i = 0; i <= deg; ++i)
4330 for (unsigned int j = 0; j < 4; ++j)
4331 {
4332 tmp -= nodal_values[i + (j + 8) * this->degree] *
4334 i + (j + 8) * this->degree,
4336 [q_point +
4338 n_edge_points +
4340 n_face_points],
4341 2);
4342
4343 for (unsigned int k = 0; k < deg; ++k)
4344 tmp -=
4345 nodal_values[i +
4346 ((2 * j + 1) * deg + k +
4348 this->degree] *
4350 i + ((2 * j + 1) * deg + k +
4352 this->degree,
4354 [q_point +
4356 n_edge_points +
4358 n_face_points],
4359 2);
4360 }
4361
4362 for (unsigned int i = 0; i <= deg; ++i)
4363 for (unsigned int j = 0; j < deg; ++j)
4364 for (unsigned int k = 0; k < deg; ++k)
4365 system_rhs((i * deg + j) * deg + k) +=
4366 reference_quadrature.weight(q_point) * tmp *
4367 lobatto_polynomials_grad[i].value(
4369 [q_point +
4371 n_edge_points +
4373 n_face_points][2]) *
4374 lobatto_polynomials[j + 2].value(
4376 [q_point +
4378 n_edge_points +
4380 n_face_points][0]) *
4381 lobatto_polynomials[k + 2].value(
4383 [q_point +
4385 n_edge_points +
4387 n_face_points][1]);
4388 }
4389
4390 system_matrix_inv.vmult(solution, system_rhs);
4391
4392 // Add the computed support_point_values
4393 // to the resulting vector
4394 // only, if they are not
4395 // too small.
4396 for (unsigned int i = 0; i <= deg; ++i)
4397 for (unsigned int j = 0; j < deg; ++j)
4398 for (unsigned int k = 0; k < deg; ++k)
4399 if (std::abs(solution((i * deg + j) * deg + k)) > 1e-14)
4400 nodal_values
4401 [i +
4402 ((j + 2 * (deg + GeometryInfo<dim>::faces_per_cell)) *
4403 deg +
4405 this->degree] = solution((i * deg + j) * deg + k);
4406 }
4407
4408 break;
4409 }
4410
4411 default:
4413 }
4414}
4415
4416
4417
4418template <int dim>
4419std::pair<Table<2, bool>, std::vector<unsigned int>>
4421{
4422 Table<2, bool> constant_modes(dim, this->n_dofs_per_cell());
4423 for (unsigned int d = 0; d < dim; ++d)
4424 for (unsigned int i = 0; i < this->n_dofs_per_cell(); ++i)
4425 constant_modes(d, i) = true;
4426 std::vector<unsigned int> components;
4427 components.reserve(dim);
4428 for (unsigned int d = 0; d < dim; ++d)
4429 components.push_back(d);
4430 return std::pair<Table<2, bool>, std::vector<unsigned int>>(constant_modes,
4431 components);
4432}
4433
4434
4435template <int dim>
4436std::size_t
4438{
4440 return 0;
4441}
4442
4443template <int dim>
4444std::vector<unsigned int>
4445FE_Nedelec<dim>::get_embedding_dofs(const unsigned int sub_degree) const
4446{
4447 Assert((sub_degree > 0) && (sub_degree <= this->degree),
4448 ExcIndexRange(sub_degree, 1, this->degree));
4449
4450 switch (dim)
4451 {
4452 case 2:
4453 {
4454 // The Nedelec cell has only Face (Line) and Cell DoFs...
4455 const unsigned int n_face_dofs_sub =
4457 const unsigned int n_cell_dofs_sub =
4458 2 * (sub_degree - 1) * sub_degree;
4459
4460 std::vector<unsigned int> embedding_dofs(n_face_dofs_sub +
4461 n_cell_dofs_sub);
4462
4463 unsigned int i = 0;
4464
4465 // Identify the Face/Line DoFs
4466 while (i < n_face_dofs_sub)
4467 {
4468 const unsigned int face_index = i / sub_degree;
4469 embedding_dofs[i] = i % sub_degree + face_index * this->degree;
4470 ++i;
4471 }
4472
4473 // Identify the Cell DoFs
4474 if (sub_degree >= 2)
4475 {
4476 const unsigned int n_face_dofs =
4477 GeometryInfo<dim>::lines_per_cell * this->degree;
4478
4479 // For the first component
4480 for (unsigned ku = 0; ku < sub_degree; ++ku)
4481 for (unsigned kv = 2; kv <= sub_degree; ++kv)
4482 embedding_dofs[i++] =
4483 n_face_dofs + ku * (this->degree - 1) + (kv - 2);
4484
4485 // For the second component
4486 for (unsigned ku = 2; ku <= sub_degree; ++ku)
4487 for (unsigned kv = 0; kv < sub_degree; ++kv)
4488 embedding_dofs[i++] = n_face_dofs +
4489 this->degree * (this->degree - 1) +
4490 (ku - 2) * (this->degree) + kv;
4491 }
4492 Assert(i == (n_face_dofs_sub + n_cell_dofs_sub), ExcInternalError());
4493 return embedding_dofs;
4494 }
4495 default:
4497 return std::vector<unsigned int>();
4498 }
4499}
4500//----------------------------------------------------------------------//
4501
4502
4503// explicit instantiations
4504#include "fe/fe_nedelec.inst"
4505
4506
void initialize_quad_dof_index_permutation_and_sign_change()
std::vector< unsigned int > get_embedding_dofs(const unsigned int sub_degree) const
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_line_dof_identities(const FiniteElement< dim > &fe_other) const override
virtual bool hp_constraints_are_implemented() const override
virtual void get_subface_interpolation_matrix(const FiniteElement< dim > &source, const unsigned int subface, FullMatrix< double > &matrix, const unsigned int face_no=0) const override
void initialize_restriction()
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_quad_dof_identities(const FiniteElement< dim > &fe_other, const unsigned int face_no=0) const override
static std::vector< unsigned int > get_dpo_vector(const unsigned int degree, bool dg=false)
virtual const FullMatrix< double > & get_restriction_matrix(const unsigned int child, const RefinementCase< dim > &refinement_case=RefinementCase< dim >::isotropic_refinement) const override
virtual void get_face_interpolation_matrix(const FiniteElement< dim > &source, FullMatrix< double > &matrix, const unsigned int face_no=0) const override
virtual std::unique_ptr< FiniteElement< dim, dim > > clone() const override
virtual std::pair< Table< 2, bool >, std::vector< unsigned int > > get_constant_modes() const override
virtual void convert_generalized_support_point_values_to_dof_values(const std::vector< Vector< double > > &support_point_values, std::vector< double > &nodal_values) const override
Threads::Mutex restriction_matrix_mutex
Definition fe_nedelec.h:641
virtual const FullMatrix< double > & get_prolongation_matrix(const unsigned int child, const RefinementCase< dim > &refinement_case=RefinementCase< dim >::isotropic_refinement) const override
virtual std::size_t memory_consumption() const override
virtual bool has_support_on_face(const unsigned int shape_index, const unsigned int face_index) const override
virtual std::string get_name() const override
friend class FE_Nedelec
Definition fe_nedelec.h:655
virtual FiniteElementDomination::Domination compare_for_domination(const FiniteElement< dim > &fe_other, const unsigned int codim=0) const override final
Threads::Mutex prolongation_matrix_mutex
Definition fe_nedelec.h:642
Table< 2, double > boundary_weights
Definition fe_nedelec.h:635
void initialize_support_points(const unsigned int order)
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_vertex_dof_identities(const FiniteElement< dim > &fe_other) const override
std::vector< Table< 2, bool > > adjust_quad_dof_sign_for_face_orientation_table
FullMatrix< double > inverse_node_matrix
virtual double shape_value_component(const unsigned int i, const Point< dim > &p, const unsigned int component) const override
std::vector< MappingKind > mapping_kind
FE_PolyTensor(const TensorPolynomialsBase< dim > &polynomials, const FiniteElementData< dim > &fe_data, const std::vector< bool > &restriction_is_additive_flags, const std::vector< ComponentMask > &nonzero_components)
const unsigned int components
Definition fe_data.h:446
const unsigned int degree
Definition fe_data.h:452
unsigned int n_dofs_per_cell() const
unsigned int n_dofs_per_face(unsigned int face_no=0, unsigned int child=0) const
unsigned int tensor_degree() const
unsigned int n_components() const
ReferenceCell reference_cell() const
unsigned int n_unique_faces() const
unsigned int n_dofs_per_quad(unsigned int face_no=0) const
FiniteElementData(const std::vector< unsigned int > &dofs_per_object, const unsigned int n_components, const unsigned int degree, const Conformity conformity=unknown, const BlockIndices &block_indices=BlockIndices())
Definition fe_data.cc:114
virtual std::string get_name() const =0
std::vector< std::vector< FullMatrix< double > > > restriction
Definition fe.h:2524
std::vector< Table< 2, int > > adjust_quad_dof_index_for_face_orientation_table
Definition fe.h:2598
void reinit_restriction_and_prolongation_matrices(const bool isotropic_restriction_only=false, const bool isotropic_prolongation_only=false)
std::vector< std::vector< Point< dim - 1 > > > generalized_face_support_points
Definition fe.h:2581
FiniteElement(const FiniteElementData< dim > &fe_data, const std::vector< bool > &restriction_is_additive_flags, const std::vector< ComponentMask > &nonzero_components)
virtual unsigned int face_to_cell_index(const unsigned int face_dof_index, const unsigned int face, const types::geometric_orientation combined_orientation=numbers::default_geometric_orientation) const
FullMatrix< double > interface_constraints
Definition fe.h:2550
std::vector< Point< dim > > generalized_support_points
Definition fe.h:2575
std::vector< std::vector< FullMatrix< double > > > prolongation
Definition fe.h:2538
void mmult(FullMatrix< number2 > &C, const FullMatrix< number2 > &B, const bool adding=false) const
size_type n() const
void vmult(Vector< number2 > &w, const Vector< number2 > &v, const bool adding=false) const
void invert(const FullMatrix< number2 > &M)
void mTmult(FullMatrix< number2 > &C, const FullMatrix< number2 > &B, const bool adding=false) const
size_type m() const
Definition point.h:113
static unsigned int n_polynomials(const unsigned int degree)
static std::vector< Polynomial< double > > generate_complete_basis(const unsigned int degree)
static std::vector< Polynomial< double > > generate_complete_basis(const unsigned int p)
static DataSetDescriptor face(const ReferenceCell &reference_cell, const unsigned int face_no, const bool face_orientation, const bool face_flip, const bool face_rotation, const unsigned int n_quadrature_points)
static Quadrature< dim > project_to_all_faces(const ReferenceCell &reference_cell, const hp::QCollection< dim - 1 > &quadrature)
const Point< dim > & point(const unsigned int i) const
virtual size_type size() const override
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:40
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:41
#define DEAL_II_NOT_IMPLEMENTED()
constexpr unsigned int GeometryInfo< dim >::lines_per_cell
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
static ::ExceptionBase & ExcImpossibleInDim(int arg1)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
static ::ExceptionBase & ExcInterpolationNotImplemented()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
const bool IsBlockVector< VectorType >::value
@ mapping_nedelec
Definition mapping.h:131
LogStream deallog
Definition logstream.cc:38
void compute_embedding_matrices(const FiniteElement< dim, spacedim > &fe, std::vector< std::vector< FullMatrix< number > > > &matrices, const bool isotropic_only=false, const double threshold=1.e-12)
void compute_face_embedding_matrices(const FiniteElement< dim, spacedim > &fe, const ArrayView< FullMatrix< number > > &matrices, const unsigned int face_coarse, const unsigned int face_fine, const double threshold=1.e-12)
constexpr const ReferenceCell & get_hypercube()
constexpr types::geometric_orientation default_geometric_orientation
Definition types.h:352
STL namespace.
::VectorizedArray< Number, width > exp(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned char geometric_orientation
Definition types.h:40
static constexpr unsigned int max_children_per_face
static constexpr unsigned int lines_per_cell
static constexpr unsigned int faces_per_cell
static std_cxx20::ranges::iota_view< unsigned int, unsigned int > face_indices()
static constexpr unsigned int lines_per_face