Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 :
19 :
20 : // Local includes
21 : #include "libmesh/fe.h"
22 : #include "libmesh/inf_fe.h"
23 : #include "libmesh/libmesh_logging.h"
24 :
25 : // For projection code:
26 : #include "libmesh/boundary_info.h"
27 : #include "libmesh/mesh_base.h"
28 : #include "libmesh/dense_matrix.h"
29 : #include "libmesh/dense_vector.h"
30 : #include "libmesh/dof_map.h"
31 : #include "libmesh/elem.h"
32 : #include "libmesh/fe_interface.h"
33 : #include "libmesh/int_range.h"
34 : #include "libmesh/numeric_vector.h"
35 : #include "libmesh/periodic_boundary_base.h"
36 : #include "libmesh/periodic_boundaries.h"
37 : #include "libmesh/quadrature.h"
38 : #include "libmesh/quadrature_gauss.h"
39 : #include "libmesh/tensor_value.h"
40 : #include "libmesh/threads.h"
41 : #include "libmesh/fe_type.h"
42 : #include "libmesh/enum_to_string.h"
43 :
44 : // C++ Includes
45 : #include <memory>
46 :
47 : // Anonymous namespace, for a helper function for periodic boundary
48 : // constraint calculations
49 : namespace
50 : {
51 : using namespace libMesh;
52 :
53 : #ifdef LIBMESH_ENABLE_PERIODIC
54 :
55 : // Find the "primary" element around a boundary point:
56 57362 : const Elem * primary_boundary_point_neighbor(const Elem * elem,
57 : const Point & p,
58 : const BoundaryInfo & boundary_info,
59 : const std::set<boundary_id_type> & boundary_ids)
60 : {
61 : // If we don't find a better alternative, the user will have
62 : // provided the primary element
63 9289 : const Elem * primary = elem;
64 :
65 : // Container to catch boundary IDs passed back by BoundaryInfo.
66 18578 : std::vector<boundary_id_type> bc_ids;
67 :
68 : // Pull object out of the loop to reduce heap operations
69 57362 : std::unique_ptr<const Elem> periodic_side;
70 :
71 9289 : std::set<const Elem *> point_neighbors;
72 57362 : elem->find_point_neighbors(p, point_neighbors);
73 218932 : for (const auto & pt_neighbor : point_neighbors)
74 : {
75 : // If this point neighbor isn't at least
76 : // as coarse as the current primary elem, or if it is at
77 : // the same level but has a lower id, then
78 : // we won't defer to it.
79 320577 : if ((pt_neighbor->level() > primary->level()) ||
80 275838 : (pt_neighbor->level() == primary->level() &&
81 157873 : pt_neighbor->id() < primary->id()))
82 62457 : continue;
83 :
84 : // Otherwise, we will defer to the point neighbor, but only if
85 : // one of its sides is on a relevant boundary and that side
86 : // contains this vertex
87 14081 : bool vertex_on_periodic_side = false;
88 245272 : for (auto ns : pt_neighbor->side_index_range())
89 : {
90 230572 : boundary_info.boundary_ids (pt_neighbor, ns, bc_ids);
91 :
92 33631 : bool on_relevant_boundary = false;
93 461144 : for (const auto & id : boundary_ids)
94 230572 : if (std::find(bc_ids.begin(), bc_ids.end(), id) != bc_ids.end())
95 14035 : on_relevant_boundary = true;
96 :
97 230572 : if (!on_relevant_boundary)
98 112278 : continue;
99 :
100 98694 : pt_neighbor->build_side_ptr(periodic_side, ns);
101 98694 : if (!periodic_side->contains_point(p))
102 208 : continue;
103 :
104 14024 : vertex_on_periodic_side = true;
105 14024 : break;
106 : }
107 :
108 99113 : if (vertex_on_periodic_side)
109 98477 : primary = pt_neighbor;
110 : }
111 :
112 66651 : return primary;
113 38784 : }
114 :
115 : // Find the "primary" element around a boundary edge:
116 480 : const Elem * primary_boundary_edge_neighbor(const Elem * elem,
117 : const Point & p1,
118 : const Point & p2,
119 : const BoundaryInfo & boundary_info,
120 : const std::set<boundary_id_type> & boundary_ids)
121 : {
122 : // If we don't find a better alternative, the user will have
123 : // provided the primary element
124 40 : const Elem * primary = elem;
125 :
126 80 : std::set<const Elem *> edge_neighbors;
127 480 : elem->find_edge_neighbors(p1, p2, edge_neighbors);
128 :
129 : // Container to catch boundary IDs handed back by BoundaryInfo
130 80 : std::vector<boundary_id_type> bc_ids;
131 :
132 : // Pull object out of the loop to reduce heap operations
133 440 : std::unique_ptr<const Elem> periodic_side;
134 :
135 960 : for (const auto & e_neighbor : edge_neighbors)
136 : {
137 : // If this edge neighbor isn't at least
138 : // as coarse as the current primary elem, or if it is at
139 : // the same level but has a lower id, then
140 : // we won't defer to it.
141 960 : if ((e_neighbor->level() > primary->level()) ||
142 880 : (e_neighbor->level() == primary->level() &&
143 480 : e_neighbor->id() < primary->id()))
144 0 : continue;
145 :
146 : // Otherwise, we will defer to the edge neighbor, but only if
147 : // one of its sides is on this periodic boundary and that
148 : // side contains this edge
149 40 : bool vertex_on_periodic_side = false;
150 808 : for (auto ns : e_neighbor->side_index_range())
151 : {
152 768 : boundary_info.boundary_ids (e_neighbor, ns, bc_ids);
153 :
154 64 : bool on_relevant_boundary = false;
155 1536 : for (const auto & id : boundary_ids)
156 768 : if (std::find(bc_ids.begin(), bc_ids.end(), id) != bc_ids.end())
157 58 : on_relevant_boundary = true;
158 :
159 768 : if (!on_relevant_boundary)
160 66 : continue;
161 :
162 696 : e_neighbor->build_side_ptr(periodic_side, ns);
163 1284 : if (!(periodic_side->contains_point(p1) &&
164 588 : periodic_side->contains_point(p2)))
165 216 : continue;
166 :
167 40 : vertex_on_periodic_side = true;
168 40 : break;
169 : }
170 :
171 480 : if (vertex_on_periodic_side)
172 480 : primary = e_neighbor;
173 : }
174 :
175 520 : return primary;
176 400 : }
177 :
178 : #endif // LIBMESH_ENABLE_PERIODIC
179 :
180 : }
181 :
182 : namespace libMesh
183 : {
184 :
185 :
186 :
187 : // ------------------------------------------------------------
188 : // FEBase class members
189 : template <>
190 : std::unique_ptr<FEGenericBase<Real>>
191 5261973 : FEGenericBase<Real>::build (const unsigned int dim,
192 : const FEType & fet)
193 : {
194 5261973 : switch (dim)
195 : {
196 : // 0D
197 12 : case 0:
198 : {
199 12 : switch (fet.family)
200 : {
201 0 : case CLOUGH:
202 0 : return std::make_unique<FE<0,CLOUGH>>(fet);
203 :
204 0 : case HERMITE:
205 0 : return std::make_unique<FE<0,HERMITE>>(fet);
206 :
207 12 : case LAGRANGE:
208 12 : return std::make_unique<FE<0,LAGRANGE>>(fet);
209 :
210 0 : case L2_LAGRANGE:
211 0 : return std::make_unique<FE<0,L2_LAGRANGE>>(fet);
212 :
213 0 : case HIERARCHIC:
214 0 : return std::make_unique<FE<0,HIERARCHIC>>(fet);
215 :
216 0 : case L2_HIERARCHIC:
217 0 : return std::make_unique<FE<0,L2_HIERARCHIC>>(fet);
218 :
219 0 : case SIDE_HIERARCHIC:
220 0 : return std::make_unique<FE<0,SIDE_HIERARCHIC>>(fet);
221 :
222 0 : case MONOMIAL:
223 0 : return std::make_unique<FE<0,MONOMIAL>>(fet);
224 :
225 : #ifdef LIBMESH_ENABLE_HIGHER_ORDER_SHAPES
226 0 : case SZABAB:
227 0 : return std::make_unique<FE<0,SZABAB>>(fet);
228 :
229 0 : case BERNSTEIN:
230 0 : return std::make_unique<FE<0,BERNSTEIN>>(fet);
231 :
232 0 : case RATIONAL_BERNSTEIN:
233 0 : return std::make_unique<FE<0,RATIONAL_BERNSTEIN>>(fet);
234 : #endif
235 :
236 0 : case XYZ:
237 0 : return std::make_unique<FEXYZ<0>>(fet);
238 :
239 0 : case SCALAR:
240 0 : return std::make_unique<FEScalar<0>>(fet);
241 :
242 0 : default:
243 0 : libmesh_error_msg("ERROR: Bad FEType.family == " << Utility::enum_to_string(fet.family));
244 : }
245 : }
246 : // 1D
247 436575 : case 1:
248 : {
249 436575 : switch (fet.family)
250 : {
251 0 : case CLOUGH:
252 0 : return std::make_unique<FE<1,CLOUGH>>(fet);
253 :
254 398546 : case HERMITE:
255 398546 : return std::make_unique<FE<1,HERMITE>>(fet);
256 :
257 3670 : case LAGRANGE:
258 3670 : return std::make_unique<FE<1,LAGRANGE>>(fet);
259 :
260 2130 : case L2_LAGRANGE:
261 2130 : return std::make_unique<FE<1,L2_LAGRANGE>>(fet);
262 :
263 11217 : case HIERARCHIC:
264 11217 : return std::make_unique<FE<1,HIERARCHIC>>(fet);
265 :
266 3550 : case L2_HIERARCHIC:
267 3550 : return std::make_unique<FE<1,L2_HIERARCHIC>>(fet);
268 :
269 3550 : case SIDE_HIERARCHIC:
270 3550 : return std::make_unique<FE<1,SIDE_HIERARCHIC>>(fet);
271 :
272 3592 : case MONOMIAL:
273 3592 : return std::make_unique<FE<1,MONOMIAL>>(fet);
274 :
275 : #ifdef LIBMESH_ENABLE_HIGHER_ORDER_SHAPES
276 2840 : case SZABAB:
277 2840 : return std::make_unique<FE<1,SZABAB>>(fet);
278 :
279 2840 : case BERNSTEIN:
280 2840 : return std::make_unique<FE<1,BERNSTEIN>>(fet);
281 :
282 1800 : case RATIONAL_BERNSTEIN:
283 1800 : return std::make_unique<FE<1,RATIONAL_BERNSTEIN>>(fet);
284 : #endif
285 :
286 2840 : case XYZ:
287 2840 : return std::make_unique<FEXYZ<1>>(fet);
288 :
289 0 : case SCALAR:
290 0 : return std::make_unique<FEScalar<1>>(fet);
291 :
292 0 : default:
293 0 : libmesh_error_msg("ERROR: Bad FEType.family == " << Utility::enum_to_string(fet.family));
294 : }
295 : }
296 :
297 :
298 : // 2D
299 1674217 : case 2:
300 : {
301 1674217 : switch (fet.family)
302 : {
303 69732 : case CLOUGH:
304 69732 : return std::make_unique<FE<2,CLOUGH>>(fet);
305 :
306 35278 : case HERMITE:
307 35278 : return std::make_unique<FE<2,HERMITE>>(fet);
308 :
309 535424 : case LAGRANGE:
310 535424 : return std::make_unique<FE<2,LAGRANGE>>(fet);
311 :
312 53964 : case L2_LAGRANGE:
313 53964 : return std::make_unique<FE<2,L2_LAGRANGE>>(fet);
314 :
315 538441 : case HIERARCHIC:
316 538441 : return std::make_unique<FE<2,HIERARCHIC>>(fet);
317 :
318 100174 : case L2_HIERARCHIC:
319 100174 : return std::make_unique<FE<2,L2_HIERARCHIC>>(fet);
320 :
321 147652 : case SIDE_HIERARCHIC:
322 147652 : return std::make_unique<FE<2,SIDE_HIERARCHIC>>(fet);
323 :
324 18420 : case MONOMIAL:
325 18420 : return std::make_unique<FE<2,MONOMIAL>>(fet);
326 :
327 : #ifdef LIBMESH_ENABLE_HIGHER_ORDER_SHAPES
328 12082 : case SZABAB:
329 12082 : return std::make_unique<FE<2,SZABAB>>(fet);
330 :
331 11698 : case BERNSTEIN:
332 11698 : return std::make_unique<FE<2,BERNSTEIN>>(fet);
333 :
334 117469 : case RATIONAL_BERNSTEIN:
335 117469 : return std::make_unique<FE<2,RATIONAL_BERNSTEIN>>(fet);
336 : #endif
337 :
338 33812 : case XYZ:
339 33812 : return std::make_unique<FEXYZ<2>>(fet);
340 :
341 0 : case SCALAR:
342 0 : return std::make_unique<FEScalar<2>>(fet);
343 :
344 71 : case SUBDIVISION:
345 71 : return std::make_unique<FESubdivision>(fet);
346 :
347 0 : default:
348 0 : libmesh_error_msg("ERROR: Bad FEType.family == " << Utility::enum_to_string(fet.family));
349 : }
350 : }
351 :
352 :
353 : // 3D
354 3151169 : case 3:
355 : {
356 3151169 : switch (fet.family)
357 : {
358 0 : case CLOUGH:
359 0 : libmesh_error_msg("ERROR: Clough-Tocher elements currently only support 1D and 2D");
360 :
361 926 : case HERMITE:
362 926 : return std::make_unique<FE<3,HERMITE>>(fet);
363 :
364 2462361 : case LAGRANGE:
365 2462361 : return std::make_unique<FE<3,LAGRANGE>>(fet);
366 :
367 85159 : case L2_LAGRANGE:
368 85159 : return std::make_unique<FE<3,L2_LAGRANGE>>(fet);
369 :
370 56485 : case HIERARCHIC:
371 56485 : return std::make_unique<FE<3,HIERARCHIC>>(fet);
372 :
373 89064 : case L2_HIERARCHIC:
374 89064 : return std::make_unique<FE<3,L2_HIERARCHIC>>(fet);
375 :
376 305448 : case SIDE_HIERARCHIC:
377 305448 : return std::make_unique<FE<3,SIDE_HIERARCHIC>>(fet);
378 :
379 25591 : case MONOMIAL:
380 25591 : return std::make_unique<FE<3,MONOMIAL>>(fet);
381 :
382 : #ifdef LIBMESH_ENABLE_HIGHER_ORDER_SHAPES
383 0 : case SZABAB:
384 0 : return std::make_unique<FE<3,SZABAB>>(fet);
385 :
386 21386 : case BERNSTEIN:
387 21386 : return std::make_unique<FE<3,BERNSTEIN>>(fet);
388 :
389 12419 : case RATIONAL_BERNSTEIN:
390 12419 : return std::make_unique<FE<3,RATIONAL_BERNSTEIN>>(fet);
391 : #endif
392 :
393 92330 : case XYZ:
394 92330 : return std::make_unique<FEXYZ<3>>(fet);
395 :
396 0 : case SCALAR:
397 0 : return std::make_unique<FEScalar<3>>(fet);
398 :
399 0 : default:
400 0 : libmesh_error_msg("ERROR: Bad FEType.family == " << Utility::enum_to_string(fet.family));
401 : }
402 : }
403 :
404 0 : default:
405 0 : libmesh_error_msg("Invalid dimension dim = " << dim);
406 : }
407 : }
408 :
409 :
410 :
411 : template <>
412 : std::unique_ptr<FEGenericBase<RealGradient>>
413 574530 : FEGenericBase<RealGradient>::build (const unsigned int dim,
414 : const FEType & fet)
415 : {
416 574530 : switch (dim)
417 : {
418 : // 0D
419 0 : case 0:
420 : {
421 0 : switch (fet.family)
422 : {
423 0 : case HIERARCHIC_VEC:
424 0 : return std::make_unique<FEHierarchicVec<0>>(fet);
425 :
426 0 : case L2_HIERARCHIC_VEC:
427 0 : return std::make_unique<FEL2HierarchicVec<0>>(fet);
428 :
429 0 : case LAGRANGE_VEC:
430 0 : return std::make_unique<FELagrangeVec<0>>(fet);
431 :
432 0 : case L2_LAGRANGE_VEC:
433 0 : return std::make_unique<FEL2LagrangeVec<0>>(fet);
434 :
435 0 : case MONOMIAL_VEC:
436 0 : return std::make_unique<FEMonomialVec<0>>(fet);
437 :
438 0 : default:
439 0 : libmesh_error_msg("ERROR: Bad FEType.family == " << Utility::enum_to_string(fet.family));
440 : }
441 : }
442 0 : case 1:
443 : {
444 0 : switch (fet.family)
445 : {
446 0 : case HIERARCHIC_VEC:
447 0 : return std::make_unique<FEHierarchicVec<1>>(fet);
448 :
449 0 : case L2_HIERARCHIC_VEC:
450 0 : return std::make_unique<FEL2HierarchicVec<1>>(fet);
451 :
452 0 : case LAGRANGE_VEC:
453 0 : return std::make_unique<FELagrangeVec<1>>(fet);
454 :
455 0 : case L2_LAGRANGE_VEC:
456 0 : return std::make_unique<FEL2LagrangeVec<1>>(fet);
457 :
458 0 : case MONOMIAL_VEC:
459 0 : return std::make_unique<FEMonomialVec<1>>(fet);
460 :
461 0 : default:
462 0 : libmesh_error_msg("ERROR: Bad FEType.family == " << Utility::enum_to_string(fet.family));
463 : }
464 : }
465 212008 : case 2:
466 : {
467 212008 : switch (fet.family)
468 : {
469 16572 : case HIERARCHIC_VEC:
470 16572 : return std::make_unique<FEHierarchicVec<2>>(fet);
471 :
472 2840 : case L2_HIERARCHIC_VEC:
473 2840 : return std::make_unique<FEL2HierarchicVec<2>>(fet);
474 :
475 15977 : case LAGRANGE_VEC:
476 15977 : return std::make_unique<FELagrangeVec<2>>(fet);
477 :
478 3250 : case L2_LAGRANGE_VEC:
479 3250 : return std::make_unique<FEL2LagrangeVec<2>>(fet);
480 :
481 1470 : case MONOMIAL_VEC:
482 1470 : return std::make_unique<FEMonomialVec<2>>(fet);
483 :
484 55937 : case NEDELEC_ONE:
485 55937 : return std::make_unique<FENedelecOne<2>>(fet);
486 :
487 114542 : case RAVIART_THOMAS:
488 114542 : return std::make_unique<FERaviartThomas<2>>(fet);
489 :
490 1420 : case L2_RAVIART_THOMAS:
491 1420 : return std::make_unique<FEL2RaviartThomas<2>>(fet);
492 :
493 0 : default:
494 0 : libmesh_error_msg("ERROR: Bad FEType.family == " << Utility::enum_to_string(fet.family));
495 : }
496 : }
497 362522 : case 3:
498 : {
499 362522 : switch (fet.family)
500 : {
501 0 : case HIERARCHIC_VEC:
502 0 : return std::make_unique<FEHierarchicVec<3>>(fet);
503 :
504 710 : case L2_HIERARCHIC_VEC:
505 710 : return std::make_unique<FEL2HierarchicVec<3>>(fet);
506 :
507 22913 : case LAGRANGE_VEC:
508 22913 : return std::make_unique<FELagrangeVec<3>>(fet);
509 :
510 1420 : case L2_LAGRANGE_VEC:
511 1420 : return std::make_unique<FEL2LagrangeVec<3>>(fet);
512 :
513 0 : case MONOMIAL_VEC:
514 0 : return std::make_unique<FEMonomialVec<3>>(fet);
515 :
516 33874 : case NEDELEC_ONE:
517 33874 : return std::make_unique<FENedelecOne<3>>(fet);
518 :
519 302895 : case RAVIART_THOMAS:
520 302895 : return std::make_unique<FERaviartThomas<3>>(fet);
521 :
522 710 : case L2_RAVIART_THOMAS:
523 710 : return std::make_unique<FEL2RaviartThomas<3>>(fet);
524 :
525 0 : default:
526 0 : libmesh_error_msg("ERROR: Bad FEType.family == " << Utility::enum_to_string(fet.family));
527 : }
528 : }
529 :
530 0 : default:
531 0 : libmesh_error_msg("Invalid dimension dim = " << dim);
532 : } // switch(dim)
533 : }
534 :
535 :
536 :
537 :
538 :
539 :
540 :
541 : #ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
542 :
543 :
544 : template <>
545 : std::unique_ptr<FEGenericBase<Real>>
546 446 : FEGenericBase<Real>::build_InfFE (const unsigned int dim,
547 : const FEType & fet)
548 : {
549 446 : switch (dim)
550 : {
551 :
552 : // 1D
553 0 : case 1:
554 : {
555 0 : switch (fet.radial_family)
556 : {
557 0 : case INFINITE_MAP:
558 0 : libmesh_error_msg("ERROR: Can't build an infinite element with FEFamily = " << Utility::enum_to_string(fet.radial_family));
559 :
560 0 : case JACOBI_20_00:
561 : {
562 0 : switch (fet.inf_map)
563 : {
564 0 : case CARTESIAN:
565 0 : return std::make_unique<InfFE<1,JACOBI_20_00,CARTESIAN>>(fet);
566 :
567 0 : default:
568 0 : libmesh_error_msg("ERROR: Can't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
569 : }
570 : }
571 :
572 0 : case JACOBI_30_00:
573 : {
574 0 : switch (fet.inf_map)
575 : {
576 0 : case CARTESIAN:
577 0 : return std::make_unique<InfFE<1,JACOBI_30_00,CARTESIAN>>(fet);
578 :
579 0 : default:
580 0 : libmesh_error_msg("ERROR: Can't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
581 : }
582 : }
583 :
584 0 : case LEGENDRE:
585 : {
586 0 : switch (fet.inf_map)
587 : {
588 0 : case CARTESIAN:
589 0 : return std::make_unique<InfFE<1,LEGENDRE,CARTESIAN>>(fet);
590 :
591 0 : default:
592 0 : libmesh_error_msg("ERROR: Can't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
593 : }
594 : }
595 :
596 0 : case LAGRANGE:
597 : {
598 0 : switch (fet.inf_map)
599 : {
600 0 : case CARTESIAN:
601 0 : return std::make_unique<InfFE<1,LAGRANGE,CARTESIAN>>(fet);
602 :
603 0 : default:
604 0 : libmesh_error_msg("ERROR: Can't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
605 : }
606 : }
607 :
608 0 : default:
609 0 : libmesh_error_msg("ERROR: Bad FEType.radial_family= " << Utility::enum_to_string(fet.radial_family));
610 : }
611 : }
612 :
613 :
614 :
615 :
616 : // 2D
617 0 : case 2:
618 : {
619 0 : switch (fet.radial_family)
620 : {
621 0 : case INFINITE_MAP:
622 0 : libmesh_error_msg("ERROR: Can't build an infinite element with FEFamily = " << Utility::enum_to_string(fet.radial_family));
623 :
624 0 : case JACOBI_20_00:
625 : {
626 0 : switch (fet.inf_map)
627 : {
628 0 : case CARTESIAN:
629 0 : return std::make_unique<InfFE<2,JACOBI_20_00,CARTESIAN>>(fet);
630 :
631 0 : default:
632 0 : libmesh_error_msg("ERROR: Don't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
633 : }
634 : }
635 :
636 0 : case JACOBI_30_00:
637 : {
638 0 : switch (fet.inf_map)
639 : {
640 0 : case CARTESIAN:
641 0 : return std::make_unique<InfFE<2,JACOBI_30_00,CARTESIAN>>(fet);
642 :
643 0 : default:
644 0 : libmesh_error_msg("ERROR: Don't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
645 : }
646 : }
647 :
648 0 : case LEGENDRE:
649 : {
650 0 : switch (fet.inf_map)
651 : {
652 0 : case CARTESIAN:
653 0 : return std::make_unique<InfFE<2,LEGENDRE,CARTESIAN>>(fet);
654 :
655 0 : default:
656 0 : libmesh_error_msg("ERROR: Don't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
657 : }
658 : }
659 :
660 0 : case LAGRANGE:
661 : {
662 0 : switch (fet.inf_map)
663 : {
664 0 : case CARTESIAN:
665 0 : return std::make_unique<InfFE<2,LAGRANGE,CARTESIAN>>(fet);
666 :
667 0 : default:
668 0 : libmesh_error_msg("ERROR: Don't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
669 : }
670 : }
671 :
672 0 : default:
673 0 : libmesh_error_msg("ERROR: Bad FEType.radial_family= " << Utility::enum_to_string(fet.radial_family));
674 : }
675 : }
676 :
677 :
678 :
679 :
680 : // 3D
681 446 : case 3:
682 : {
683 446 : switch (fet.radial_family)
684 : {
685 0 : case INFINITE_MAP:
686 0 : libmesh_error_msg("ERROR: Don't build an infinite element with FEFamily = " << Utility::enum_to_string(fet.radial_family));
687 :
688 391 : case JACOBI_20_00:
689 : {
690 391 : switch (fet.inf_map)
691 : {
692 391 : case CARTESIAN:
693 391 : return std::make_unique<InfFE<3,JACOBI_20_00,CARTESIAN>>(fet);
694 :
695 0 : default:
696 0 : libmesh_error_msg("ERROR: Don't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
697 : }
698 : }
699 :
700 20 : case JACOBI_30_00:
701 : {
702 20 : switch (fet.inf_map)
703 : {
704 20 : case CARTESIAN:
705 20 : return std::make_unique<InfFE<3,JACOBI_30_00,CARTESIAN>>(fet);
706 :
707 0 : default:
708 0 : libmesh_error_msg("ERROR: Don't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
709 : }
710 : }
711 :
712 20 : case LEGENDRE:
713 : {
714 20 : switch (fet.inf_map)
715 : {
716 20 : case CARTESIAN:
717 20 : return std::make_unique<InfFE<3,LEGENDRE,CARTESIAN>>(fet);
718 :
719 0 : default:
720 0 : libmesh_error_msg("ERROR: Don't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
721 : }
722 : }
723 :
724 15 : case LAGRANGE:
725 : {
726 15 : switch (fet.inf_map)
727 : {
728 15 : case CARTESIAN:
729 15 : return std::make_unique<InfFE<3,LAGRANGE,CARTESIAN>>(fet);
730 :
731 0 : default:
732 0 : libmesh_error_msg("ERROR: Don't build an infinite element with InfMapType = " << Utility::enum_to_string(fet.inf_map));
733 : }
734 : }
735 :
736 0 : default:
737 0 : libmesh_error_msg("ERROR: Bad FEType.radial_family= " << Utility::enum_to_string(fet.radial_family));
738 : }
739 : }
740 :
741 0 : default:
742 0 : libmesh_error_msg("Invalid dimension dim = " << dim);
743 : }
744 : }
745 :
746 :
747 :
748 : template <>
749 : std::unique_ptr<FEGenericBase<RealGradient>>
750 0 : FEGenericBase<RealGradient>::build_InfFE (const unsigned int,
751 : const FEType & )
752 : {
753 : // No vector types defined... YET.
754 0 : libmesh_not_implemented();
755 : return std::unique_ptr<FEVectorBase>();
756 : }
757 :
758 : #endif // ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS
759 :
760 :
761 : template <typename OutputType>
762 122845000 : void FEGenericBase<OutputType>::compute_shape_functions (const Elem * elem,
763 : const std::vector<Point> & qp)
764 : {
765 : //-------------------------------------------------------------------------
766 : // Compute the shape function values (and derivatives)
767 : // at the Quadrature points. Note that the actual values
768 : // have already been computed via init_shape_functions
769 :
770 : // Start logging the shape function computation
771 22018988 : LOG_SCOPE("compute_shape_functions()", "FE");
772 :
773 122845000 : this->determine_calculations();
774 :
775 122845000 : if (calculate_phi)
776 111412256 : this->_fe_trans->map_phi(this->dim, elem, qp, (*this), this->phi, this->_add_p_level_in_reinit);
777 :
778 122845000 : if (calculate_dphi)
779 88586178 : this->_fe_trans->map_dphi(this->dim, elem, qp, (*this), this->dphi,
780 81129878 : this->dphidx, this->dphidy, this->dphidz);
781 :
782 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
783 122845000 : if (calculate_d2phi)
784 7132646 : this->_fe_trans->map_d2phi(this->dim, qp, (*this), this->d2phi,
785 6608234 : this->d2phidx2, this->d2phidxdy, this->d2phidxdz,
786 6608234 : this->d2phidy2, this->d2phidydz, this->d2phidz2);
787 : #endif //LIBMESH_ENABLE_SECOND_DERIVATIVES
788 :
789 : // Only compute curl for vector-valued elements
790 9947971 : if (calculate_curl_phi && TypesEqual<OutputType,RealGradient>::value)
791 859569 : this->_fe_trans->map_curl(this->dim, elem, qp, (*this), this->curl_phi);
792 :
793 : // Only compute div for vector-valued elements
794 9947971 : if (calculate_div_phi && TypesEqual<OutputType,RealGradient>::value)
795 1476125 : this->_fe_trans->map_div(this->dim, elem, qp, (*this), this->div_phi);
796 122845000 : }
797 :
798 :
799 : // Here, we rely on the input \p phi_vals for accurate integration of the mass matrix.
800 : // This is because in contact, we often have customized qrule for mortar segments
801 : // due to deformation of the element, and the size of \p phi_vals for the secondary
802 : // element changes accordingly.
803 : template <>
804 6418 : void FEGenericBase<Real>::compute_dual_shape_coeffs (const std::vector<Real> & JxW, const std::vector<std::vector<OutputShape>> & phi_vals)
805 : {
806 : // Start logging the dual coeff computation
807 862 : LOG_SCOPE("compute_dual_shape_coeffs()", "FE");
808 :
809 6418 : const unsigned int sz=phi_vals.size();
810 6418 : libmesh_error_msg_if(!sz, "ERROR: cannot compute dual shape coefficients with empty phi values");
811 :
812 : //compute dual basis coefficient (dual_coeff)
813 5987 : dual_coeff.resize(sz, sz);
814 7280 : DenseMatrix<Real> A(sz, sz), D(sz, sz);
815 :
816 108576 : for (const auto i : index_range(phi_vals))
817 6046176 : for (const auto qp : index_range(phi_vals[i]))
818 : {
819 7201123 : D(i,i) += JxW[qp]*phi_vals[i][qp];
820 306307372 : for (const auto j : index_range(phi_vals))
821 368500299 : A(i,j) += JxW[qp]*phi_vals[i][qp]*phi_vals[j][qp];
822 : }
823 :
824 : // D(k,k) = \int N_k is not positive on the serendipity quadratic faces: 0 at a TRI6 vertex, which
825 : // leaves those dual shape functions identically zero, and -1/3 at a QUAD8 corner. Biorthogonalize
826 : // instead against the locally quadratic transformed basis Ntilde = T N of Popp et al., SIAM J. Sci.
827 : // Comput. 34(4):B421-B446, 2012, Sec. 4.4.1, in which each vertex absorbs a fraction alpha of its
828 : // adjacent mid-edge shapes. That replaces D by T^-1 diag(T d), which for this T is the sparse
829 : // update below. alpha = 1/5 is recommended there, and makes the weights strictly positive (QUAD8
830 : // 1/5 and 4/5, TRI6 1/15 and 1/10) while preserving the partition of unity. T = I elsewhere.
831 6477 : if (_elem && (_elem->type() == TRI6 || _elem->type() == QUAD8) &&
832 7041 : get_family() == LAGRANGE && sz == _elem->n_nodes())
833 : {
834 6 : const Real alpha = Real(1)/5;
835 : // Mid-edge nodes are the trailing indices, and only vertex entries are written, so D(m,m) here
836 : // is never a value an earlier iteration modified.
837 352 : for (const auto m : make_range(_elem->n_vertices(), sz))
838 872 : for (const auto v : make_range(_elem->n_second_order_adjacent_vertices(m)))
839 : {
840 456 : const auto vertex = _elem->second_order_adjacent_vertex(m, v);
841 456 : D(vertex, vertex) += alpha*D(m,m);
842 496 : D(vertex, m) -= alpha*D(m,m);
843 : }
844 : }
845 :
846 : // dual_coeff = A^-1*D
847 108576 : for (const auto j : index_range(phi_vals))
848 : {
849 102158 : DenseVector<Real> Dcol(sz), coeffcol(sz);
850 3507468 : for (const auto i : index_range(phi_vals))
851 3904102 : Dcol(i) = D(i, j);
852 102158 : A.cholesky_solve(Dcol, coeffcol);
853 :
854 3507468 : for (const auto row : index_range(phi_vals))
855 3904102 : dual_coeff(row, j)=coeffcol(row);
856 : }
857 6418 : }
858 :
859 : template <>
860 6418 : void FEGenericBase<Real>::compute_dual_shape_functions ()
861 : {
862 : // Start logging the shape function computation
863 862 : LOG_SCOPE("compute_dual_shape_functions()", "FE");
864 :
865 : // The dual coeffs matrix should have the same size as phi
866 431 : libmesh_assert(dual_coeff.m() == phi.size());
867 431 : libmesh_assert(dual_coeff.n() == phi.size());
868 :
869 : // initialize dual basis
870 108576 : for (const auto j : index_range(phi))
871 6046176 : for (const auto qp : index_range(phi[j]))
872 : {
873 6363053 : dual_phi[j][qp] = 0;
874 5944018 : if (calculate_dphi)
875 1256625 : dual_dphi[j][qp] = 0;
876 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
877 5944018 : if (calculate_d2phi)
878 1248093 : dual_d2phi[j][qp] = 0;
879 : #endif
880 : }
881 :
882 : // compute dual basis
883 108576 : for (const auto j : index_range(phi))
884 3507468 : for (const auto i : index_range(phi))
885 303768664 : for (const auto qp : index_range(phi[j]))
886 : {
887 391212614 : dual_phi[j][qp] += dual_coeff(i, j) * phi[i][qp];
888 300363354 : if (calculate_dphi)
889 136267362 : dual_dphi[j][qp] += dual_coeff(i, j) * dphi[i][qp];
890 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
891 300363354 : if (calculate_d2phi)
892 667700859 : dual_d2phi[j][qp] += dual_coeff(i, j) * d2phi[i][qp];
893 : #endif
894 : }
895 6418 : }
896 :
897 : template <typename OutputType>
898 0 : void FEGenericBase<OutputType>::print_phi(std::ostream & os) const
899 : {
900 0 : for (auto i : index_range(phi))
901 0 : for (auto j : index_range(phi[i]))
902 0 : os << " phi[" << i << "][" << j << "]=" << phi[i][j] << std::endl;
903 0 : }
904 :
905 : template <typename OutputType>
906 0 : void FEGenericBase<OutputType>::print_dual_phi(std::ostream & os) const
907 : {
908 0 : for (auto i : index_range(dual_phi))
909 0 : for (auto j : index_range(dual_phi[i]))
910 0 : os << " dual_phi[" << i << "][" << j << "]=" << dual_phi[i][j] << std::endl;
911 0 : }
912 :
913 :
914 :
915 :
916 : template <typename OutputType>
917 0 : void FEGenericBase<OutputType>::print_dphi(std::ostream & os) const
918 : {
919 0 : for (auto i : index_range(dphi))
920 0 : for (auto j : index_range(dphi[i]))
921 0 : os << " dphi[" << i << "][" << j << "]=" << dphi[i][j];
922 0 : }
923 :
924 : template <typename OutputType>
925 0 : void FEGenericBase<OutputType>::print_dual_dphi(std::ostream & os) const
926 : {
927 0 : for (auto i : index_range(dphi))
928 0 : for (auto j : index_range(dphi[i]))
929 0 : os << " dual_dphi[" << i << "][" << j << "]=" << dual_dphi[i][j];
930 0 : }
931 :
932 :
933 :
934 : template <typename OutputType>
935 344071416 : void FEGenericBase<OutputType>::determine_calculations()
936 : {
937 344071416 : this->calculations_started = true;
938 :
939 : // If the user did not explicitly pre-request something (or nothing)
940 : // to be computed, then we throw an error here.
941 29877779 : bool requested_ok =
942 334730401 : this->calculate_nothing || this->calculate_phi || this->calculate_dphi ||
943 375695149 : this->calculate_dphiref || this->calculate_curl_phi || this->calculate_div_phi ||
944 366629 : this->calculate_map;
945 :
946 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
947 29877779 : requested_ok = requested_ok || this->calculate_d2phi;
948 : #endif
949 :
950 29877779 : libmesh_error_msg_if(
951 : !requested_ok,
952 : "You must call one or more of the FE accessors "
953 : "(e.g. get_phi(), get_dphi(), get_nothing()) "
954 : "_before_ calling reinit()!");
955 :
956 : // Request whichever terms are necessary from the FEMap
957 344071416 : if (this->calculate_phi)
958 320364111 : this->_fe_trans->init_map_phi(*this);
959 :
960 344071416 : if (this->calculate_dphiref)
961 225565061 : this->_fe_trans->init_map_dphi(*this);
962 :
963 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
964 344071416 : if (this->calculate_d2phi)
965 58343733 : this->_fe_trans->init_map_d2phi(*this);
966 : #endif //LIBMESH_ENABLE_SECOND_DERIVATIVES
967 344071416 : }
968 :
969 :
970 :
971 : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
972 :
973 :
974 : template <typename OutputType>
975 0 : void FEGenericBase<OutputType>::print_d2phi(std::ostream & os) const
976 : {
977 0 : for (auto i : index_range(dphi))
978 0 : for (auto j : index_range(dphi[i]))
979 0 : os << " d2phi[" << i << "][" << j << "]=" << d2phi[i][j];
980 0 : }
981 :
982 : template <typename OutputType>
983 0 : void FEGenericBase<OutputType>::print_dual_d2phi(std::ostream & os) const
984 : {
985 0 : for (auto i : index_range(dual_d2phi))
986 0 : for (auto j : index_range(dual_d2phi[i]))
987 0 : os << " dual_d2phi[" << i << "][" << j << "]=" << dual_d2phi[i][j];
988 0 : }
989 :
990 : #endif
991 :
992 :
993 :
994 : #ifdef LIBMESH_ENABLE_AMR
995 :
996 : template <typename OutputType>
997 : void
998 0 : FEGenericBase<OutputType>::coarsened_dof_values(const NumericVector<Number> & old_vector,
999 : const DofMap & dof_map,
1000 : const Elem * elem,
1001 : DenseVector<Number> & Ue,
1002 : const unsigned int var,
1003 : const bool use_old_dof_indices)
1004 : {
1005 : // Side/edge local DOF indices
1006 0 : std::vector<unsigned int> new_side_dofs, old_side_dofs;
1007 :
1008 : // FIXME: what about 2D shells in 3D space?
1009 0 : unsigned int dim = elem->dim();
1010 :
1011 : // Cache n_children(); it's a virtual call but it's const.
1012 0 : const unsigned int n_children = elem->n_children();
1013 :
1014 : // We use local FE objects for now
1015 : // FIXME: we should use more, external objects instead for efficiency
1016 0 : const FEType & base_fe_type = dof_map.variable_type(var);
1017 0 : std::unique_ptr<FEGenericBase<OutputShape>> fe
1018 : (FEGenericBase<OutputShape>::build(dim, base_fe_type));
1019 0 : std::unique_ptr<FEGenericBase<OutputShape>> fe_coarse
1020 : (FEGenericBase<OutputShape>::build(dim, base_fe_type));
1021 :
1022 0 : std::unique_ptr<QBase> qrule (base_fe_type.default_quadrature_rule(dim));
1023 0 : std::unique_ptr<QBase> qedgerule (base_fe_type.default_quadrature_rule(1));
1024 0 : std::unique_ptr<QBase> qsiderule (base_fe_type.default_quadrature_rule(dim-1));
1025 0 : std::vector<Point> coarse_qpoints;
1026 :
1027 : // The values of the shape functions at the quadrature
1028 : // points
1029 0 : const std::vector<std::vector<OutputShape>> & phi_values =
1030 : fe->get_phi();
1031 0 : const std::vector<std::vector<OutputShape>> & phi_coarse =
1032 : fe_coarse->get_phi();
1033 :
1034 : // The gradients of the shape functions at the quadrature
1035 : // points on the child element.
1036 0 : const std::vector<std::vector<OutputGradient>> * dphi_values =
1037 : nullptr;
1038 0 : const std::vector<std::vector<OutputGradient>> * dphi_coarse =
1039 : nullptr;
1040 :
1041 0 : const FEContinuity cont = fe->get_continuity();
1042 :
1043 0 : if (cont == C_ONE)
1044 : {
1045 : const std::vector<std::vector<OutputGradient>> &
1046 0 : ref_dphi_values = fe->get_dphi();
1047 0 : dphi_values = &ref_dphi_values;
1048 : const std::vector<std::vector<OutputGradient>> &
1049 0 : ref_dphi_coarse = fe_coarse->get_dphi();
1050 0 : dphi_coarse = &ref_dphi_coarse;
1051 : }
1052 :
1053 : // The Jacobian * quadrature weight at the quadrature points
1054 0 : const std::vector<Real> & JxW =
1055 0 : fe->get_JxW();
1056 :
1057 : // The XYZ locations of the quadrature points on the
1058 : // child element
1059 0 : const std::vector<Point> & xyz_values =
1060 0 : fe->get_xyz();
1061 :
1062 : // Number of nodes on parent element
1063 0 : const unsigned int n_nodes = elem->n_nodes();
1064 :
1065 : // Number of dofs on parent element
1066 : const unsigned int new_n_dofs =
1067 0 : FEInterface::n_dofs(base_fe_type, elem->max_descendant_p_level(), elem);
1068 :
1069 : // Fixed vs. free DoFs on edge/face projections
1070 0 : std::vector<char> dof_is_fixed(new_n_dofs, false); // bools
1071 0 : std::vector<int> free_dof(new_n_dofs, 0);
1072 :
1073 0 : DenseMatrix<Real> Ke;
1074 0 : DenseVector<Number> Fe;
1075 0 : Ue.resize(new_n_dofs); Ue.zero();
1076 :
1077 :
1078 : // When coarsening, in general, we need a series of
1079 : // projections to ensure a unique and continuous
1080 : // solution. We start by interpolating nodes, then
1081 : // hold those fixed and project edges, then
1082 : // hold those fixed and project faces, then
1083 : // hold those fixed and project interiors
1084 :
1085 : // Copy node values first
1086 : {
1087 0 : std::vector<dof_id_type> node_dof_indices;
1088 0 : if (use_old_dof_indices)
1089 0 : dof_map.old_dof_indices (elem, node_dof_indices, var);
1090 : else
1091 0 : dof_map.dof_indices (elem, node_dof_indices, var);
1092 :
1093 0 : unsigned int current_dof = 0;
1094 0 : for (unsigned int n=0; n!= n_nodes; ++n)
1095 : {
1096 : // FIXME: this should go through the DofMap,
1097 : // not duplicate dof_indices code badly!
1098 : const unsigned int my_nc =
1099 0 : FEInterface::n_dofs_at_node (base_fe_type, elem->max_descendant_p_level(), elem, n);
1100 0 : if (!elem->is_vertex(n))
1101 : {
1102 0 : current_dof += my_nc;
1103 0 : continue;
1104 : }
1105 :
1106 : // We're assuming here that child n shares vertex n,
1107 : // which is wrong on non-simplices right now
1108 : // ... but this code isn't necessary except on elements
1109 : // where p refinement creates more vertex dofs; we have
1110 : // no such elements yet.
1111 0 : int extra_order = 0;
1112 : // if (elem->child_ptr(n)->p_level() < elem->p_level())
1113 : // extra_order = elem->child_ptr(n)->p_level();
1114 : const unsigned int nc =
1115 0 : FEInterface::n_dofs_at_node (base_fe_type, extra_order, elem, n);
1116 0 : for (unsigned int i=0; i!= nc; ++i)
1117 : {
1118 0 : Ue(current_dof) =
1119 0 : old_vector(node_dof_indices[current_dof]);
1120 0 : dof_is_fixed[current_dof] = true;
1121 0 : current_dof++;
1122 : }
1123 : }
1124 : }
1125 :
1126 0 : FEType fe_type = base_fe_type, temp_fe_type;
1127 0 : fe_type.order = fe_type.order + elem->max_descendant_p_level();
1128 :
1129 : // In 3D, project any edge values next
1130 0 : if (dim > 2 && cont != DISCONTINUOUS)
1131 0 : for (auto e : elem->edge_index_range())
1132 : {
1133 0 : FEInterface::dofs_on_edge(elem, dim, fe_type,
1134 : e, new_side_dofs);
1135 :
1136 : const unsigned int n_new_side_dofs =
1137 0 : cast_int<unsigned int>(new_side_dofs.size());
1138 :
1139 : // Some edge dofs are on nodes and already
1140 : // fixed, others are free to calculate
1141 0 : unsigned int free_dofs = 0;
1142 0 : for (unsigned int i=0; i != n_new_side_dofs; ++i)
1143 0 : if (!dof_is_fixed[new_side_dofs[i]])
1144 0 : free_dof[free_dofs++] = i;
1145 0 : Ke.resize (free_dofs, free_dofs); Ke.zero();
1146 0 : Fe.resize (free_dofs); Fe.zero();
1147 : // The new edge coefficients
1148 0 : DenseVector<Number> Uedge(free_dofs);
1149 :
1150 : // Add projection terms from each child sharing
1151 : // this edge
1152 0 : for (unsigned int c=0; c != n_children; ++c)
1153 : {
1154 0 : if (!elem->is_child_on_edge(c,e))
1155 0 : continue;
1156 0 : const Elem * child = elem->child_ptr(c);
1157 :
1158 0 : std::vector<dof_id_type> child_dof_indices;
1159 0 : if (use_old_dof_indices)
1160 0 : dof_map.old_dof_indices (child,
1161 : child_dof_indices, var);
1162 : else
1163 0 : dof_map.dof_indices (child,
1164 : child_dof_indices, var);
1165 : const unsigned int child_n_dofs =
1166 : cast_int<unsigned int>
1167 0 : (child_dof_indices.size());
1168 :
1169 0 : temp_fe_type = base_fe_type;
1170 0 : temp_fe_type.order = temp_fe_type.order + child->p_level();
1171 :
1172 0 : FEInterface::dofs_on_edge(child, dim,
1173 : temp_fe_type, e, old_side_dofs);
1174 :
1175 : // Initialize both child and parent FE data
1176 : // on the child's edge
1177 0 : fe->attach_quadrature_rule (qedgerule.get());
1178 0 : fe->edge_reinit (child, e);
1179 0 : const unsigned int n_qp = qedgerule->n_points();
1180 :
1181 0 : FEMap::inverse_map (dim, elem, xyz_values,
1182 : coarse_qpoints);
1183 :
1184 0 : fe_coarse->reinit(elem, &coarse_qpoints);
1185 :
1186 : // Loop over the quadrature points
1187 0 : for (unsigned int qp=0; qp<n_qp; qp++)
1188 : {
1189 : // solution value at the quadrature point
1190 0 : OutputNumber fineval = libMesh::zero;
1191 : // solution grad at the quadrature point
1192 0 : OutputNumberGradient finegrad;
1193 :
1194 : // Sum the solution values * the DOF
1195 : // values at the quadrature point to
1196 : // get the solution value and gradient.
1197 0 : for (unsigned int i=0; i<child_n_dofs;
1198 : i++)
1199 : {
1200 0 : fineval +=
1201 0 : (old_vector(child_dof_indices[i])*
1202 0 : phi_values[i][qp]);
1203 0 : if (cont == C_ONE)
1204 0 : finegrad += (*dphi_values)[i][qp] *
1205 0 : old_vector(child_dof_indices[i]);
1206 : }
1207 :
1208 : // Form edge projection matrix
1209 0 : for (unsigned int sidei=0, freei=0; sidei != n_new_side_dofs; ++sidei)
1210 : {
1211 0 : unsigned int i = new_side_dofs[sidei];
1212 : // fixed DoFs aren't test functions
1213 0 : if (dof_is_fixed[i])
1214 0 : continue;
1215 0 : for (unsigned int sidej=0, freej=0; sidej != n_new_side_dofs; ++sidej)
1216 : {
1217 0 : unsigned int j =
1218 : new_side_dofs[sidej];
1219 0 : if (dof_is_fixed[j])
1220 0 : Fe(freei) -=
1221 0 : TensorTools::inner_product(phi_coarse[i][qp],
1222 0 : phi_coarse[j][qp]) *
1223 0 : JxW[qp] * Ue(j);
1224 : else
1225 0 : Ke(freei,freej) +=
1226 0 : TensorTools::inner_product(phi_coarse[i][qp],
1227 0 : phi_coarse[j][qp]) *
1228 : JxW[qp];
1229 0 : if (cont == C_ONE)
1230 : {
1231 0 : if (dof_is_fixed[j])
1232 0 : Fe(freei) -=
1233 0 : TensorTools::inner_product((*dphi_coarse)[i][qp],
1234 0 : (*dphi_coarse)[j][qp]) *
1235 0 : JxW[qp] * Ue(j);
1236 : else
1237 0 : Ke(freei,freej) +=
1238 0 : TensorTools::inner_product((*dphi_coarse)[i][qp],
1239 0 : (*dphi_coarse)[j][qp]) *
1240 : JxW[qp];
1241 : }
1242 0 : if (!dof_is_fixed[j])
1243 0 : freej++;
1244 : }
1245 0 : Fe(freei) += TensorTools::inner_product(phi_coarse[i][qp],
1246 0 : fineval) * JxW[qp];
1247 0 : if (cont == C_ONE)
1248 0 : Fe(freei) +=
1249 0 : TensorTools::inner_product(finegrad, (*dphi_coarse)[i][qp]) * JxW[qp];
1250 0 : freei++;
1251 : }
1252 : }
1253 : }
1254 0 : Ke.cholesky_solve(Fe, Uedge);
1255 :
1256 : // Transfer new edge solutions to element
1257 0 : for (unsigned int i=0; i != free_dofs; ++i)
1258 : {
1259 0 : Number & ui = Ue(new_side_dofs[free_dof[i]]);
1260 0 : libmesh_assert(std::abs(ui) < TOLERANCE ||
1261 : std::abs(ui - Uedge(i)) < TOLERANCE);
1262 0 : ui = Uedge(i);
1263 0 : dof_is_fixed[new_side_dofs[free_dof[i]]] = true;
1264 : }
1265 : }
1266 :
1267 : // Project any side values (edges in 2D, faces in 3D)
1268 0 : if (dim > 1 && cont != DISCONTINUOUS)
1269 0 : for (auto s : elem->side_index_range())
1270 : {
1271 0 : FEInterface::dofs_on_side(elem, dim, fe_type,
1272 : s, new_side_dofs);
1273 :
1274 : const unsigned int n_new_side_dofs =
1275 0 : cast_int<unsigned int>(new_side_dofs.size());
1276 :
1277 : // Some side dofs are on nodes/edges and already
1278 : // fixed, others are free to calculate
1279 0 : unsigned int free_dofs = 0;
1280 0 : for (unsigned int i=0; i != n_new_side_dofs; ++i)
1281 0 : if (!dof_is_fixed[new_side_dofs[i]])
1282 0 : free_dof[free_dofs++] = i;
1283 0 : Ke.resize (free_dofs, free_dofs); Ke.zero();
1284 0 : Fe.resize (free_dofs); Fe.zero();
1285 : // The new side coefficients
1286 0 : DenseVector<Number> Uside(free_dofs);
1287 :
1288 : // Add projection terms from each child sharing
1289 : // this side
1290 0 : for (unsigned int c=0; c != n_children; ++c)
1291 : {
1292 0 : if (!elem->is_child_on_side(c,s))
1293 0 : continue;
1294 0 : const Elem * child = elem->child_ptr(c);
1295 :
1296 0 : std::vector<dof_id_type> child_dof_indices;
1297 0 : if (use_old_dof_indices)
1298 0 : dof_map.old_dof_indices (child,
1299 : child_dof_indices, var);
1300 : else
1301 0 : dof_map.dof_indices (child,
1302 : child_dof_indices, var);
1303 : const unsigned int child_n_dofs =
1304 : cast_int<unsigned int>
1305 0 : (child_dof_indices.size());
1306 :
1307 0 : temp_fe_type = base_fe_type;
1308 0 : temp_fe_type.order = temp_fe_type.order + child->p_level();
1309 :
1310 0 : FEInterface::dofs_on_side(child, dim,
1311 : temp_fe_type, s, old_side_dofs);
1312 :
1313 : // Initialize both child and parent FE data
1314 : // on the child's side
1315 0 : fe->attach_quadrature_rule (qsiderule.get());
1316 0 : fe->reinit (child, s);
1317 0 : const unsigned int n_qp = qsiderule->n_points();
1318 :
1319 0 : FEMap::inverse_map (dim, elem, xyz_values,
1320 : coarse_qpoints);
1321 :
1322 0 : fe_coarse->reinit(elem, &coarse_qpoints);
1323 :
1324 : // Loop over the quadrature points
1325 0 : for (unsigned int qp=0; qp<n_qp; qp++)
1326 : {
1327 : // solution value at the quadrature point
1328 0 : OutputNumber fineval = libMesh::zero;
1329 : // solution grad at the quadrature point
1330 0 : OutputNumberGradient finegrad;
1331 :
1332 : // Sum the solution values * the DOF
1333 : // values at the quadrature point to
1334 : // get the solution value and gradient.
1335 0 : for (unsigned int i=0; i<child_n_dofs;
1336 : i++)
1337 : {
1338 0 : fineval +=
1339 0 : old_vector(child_dof_indices[i]) *
1340 0 : phi_values[i][qp];
1341 0 : if (cont == C_ONE)
1342 0 : finegrad += (*dphi_values)[i][qp] *
1343 0 : old_vector(child_dof_indices[i]);
1344 : }
1345 :
1346 : // Form side projection matrix
1347 0 : for (unsigned int sidei=0, freei=0; sidei != n_new_side_dofs; ++sidei)
1348 : {
1349 0 : unsigned int i = new_side_dofs[sidei];
1350 : // fixed DoFs aren't test functions
1351 0 : if (dof_is_fixed[i])
1352 0 : continue;
1353 0 : for (unsigned int sidej=0, freej=0; sidej != n_new_side_dofs; ++sidej)
1354 : {
1355 0 : unsigned int j =
1356 : new_side_dofs[sidej];
1357 0 : if (dof_is_fixed[j])
1358 0 : Fe(freei) -=
1359 0 : TensorTools::inner_product(phi_coarse[i][qp],
1360 0 : phi_coarse[j][qp]) *
1361 0 : JxW[qp] * Ue(j);
1362 : else
1363 0 : Ke(freei,freej) +=
1364 0 : TensorTools::inner_product(phi_coarse[i][qp],
1365 0 : phi_coarse[j][qp]) *
1366 : JxW[qp];
1367 0 : if (cont == C_ONE)
1368 : {
1369 0 : if (dof_is_fixed[j])
1370 0 : Fe(freei) -=
1371 0 : TensorTools::inner_product((*dphi_coarse)[i][qp],
1372 0 : (*dphi_coarse)[j][qp]) *
1373 0 : JxW[qp] * Ue(j);
1374 : else
1375 0 : Ke(freei,freej) +=
1376 0 : TensorTools::inner_product((*dphi_coarse)[i][qp],
1377 0 : (*dphi_coarse)[j][qp]) *
1378 : JxW[qp];
1379 : }
1380 0 : if (!dof_is_fixed[j])
1381 0 : freej++;
1382 : }
1383 0 : Fe(freei) += TensorTools::inner_product(fineval, phi_coarse[i][qp]) * JxW[qp];
1384 0 : if (cont == C_ONE)
1385 0 : Fe(freei) +=
1386 0 : TensorTools::inner_product(finegrad, (*dphi_coarse)[i][qp]) * JxW[qp];
1387 0 : freei++;
1388 : }
1389 : }
1390 : }
1391 0 : Ke.cholesky_solve(Fe, Uside);
1392 :
1393 : // Transfer new side solutions to element
1394 0 : for (unsigned int i=0; i != free_dofs; ++i)
1395 : {
1396 0 : Number & ui = Ue(new_side_dofs[free_dof[i]]);
1397 0 : libmesh_assert(std::abs(ui) < TOLERANCE ||
1398 : std::abs(ui - Uside(i)) < TOLERANCE);
1399 0 : ui = Uside(i);
1400 0 : dof_is_fixed[new_side_dofs[free_dof[i]]] = true;
1401 : }
1402 : }
1403 :
1404 : // Project the interior values, finally
1405 :
1406 : // Some interior dofs are on nodes/edges/sides and
1407 : // already fixed, others are free to calculate
1408 0 : unsigned int free_dofs = 0;
1409 0 : for (unsigned int i=0; i != new_n_dofs; ++i)
1410 0 : if (!dof_is_fixed[i])
1411 0 : free_dof[free_dofs++] = i;
1412 0 : Ke.resize (free_dofs, free_dofs); Ke.zero();
1413 0 : Fe.resize (free_dofs); Fe.zero();
1414 : // The new interior coefficients
1415 0 : DenseVector<Number> Uint(free_dofs);
1416 :
1417 : // Add projection terms from each child
1418 0 : for (auto & child : elem->child_ref_range())
1419 : {
1420 0 : std::vector<dof_id_type> child_dof_indices;
1421 0 : if (use_old_dof_indices)
1422 0 : dof_map.old_dof_indices (&child,
1423 : child_dof_indices, var);
1424 : else
1425 0 : dof_map.dof_indices (&child,
1426 : child_dof_indices, var);
1427 : const unsigned int child_n_dofs =
1428 : cast_int<unsigned int>
1429 0 : (child_dof_indices.size());
1430 :
1431 : // Initialize both child and parent FE data
1432 : // on the child's quadrature points
1433 0 : fe->attach_quadrature_rule (qrule.get());
1434 0 : fe->reinit (&child);
1435 0 : const unsigned int n_qp = qrule->n_points();
1436 :
1437 0 : FEMap::inverse_map (dim, elem, xyz_values, coarse_qpoints);
1438 :
1439 0 : fe_coarse->reinit(elem, &coarse_qpoints);
1440 :
1441 : // Loop over the quadrature points
1442 0 : for (unsigned int qp=0; qp<n_qp; qp++)
1443 : {
1444 : // solution value at the quadrature point
1445 0 : OutputNumber fineval = libMesh::zero;
1446 : // solution grad at the quadrature point
1447 0 : OutputNumberGradient finegrad;
1448 :
1449 : // Sum the solution values * the DOF
1450 : // values at the quadrature point to
1451 : // get the solution value and gradient.
1452 0 : for (unsigned int i=0; i<child_n_dofs; i++)
1453 : {
1454 0 : fineval +=
1455 0 : (old_vector(child_dof_indices[i]) *
1456 0 : phi_values[i][qp]);
1457 0 : if (cont == C_ONE)
1458 0 : finegrad += (*dphi_values)[i][qp] *
1459 0 : old_vector(child_dof_indices[i]);
1460 : }
1461 :
1462 : // Form interior projection matrix
1463 0 : for (unsigned int i=0, freei=0;
1464 0 : i != new_n_dofs; ++i)
1465 : {
1466 : // fixed DoFs aren't test functions
1467 0 : if (dof_is_fixed[i])
1468 0 : continue;
1469 0 : for (unsigned int j=0, freej=0; j !=
1470 : new_n_dofs; ++j)
1471 : {
1472 0 : if (dof_is_fixed[j])
1473 0 : Fe(freei) -=
1474 0 : TensorTools::inner_product(phi_coarse[i][qp],
1475 0 : phi_coarse[j][qp]) *
1476 0 : JxW[qp] * Ue(j);
1477 : else
1478 0 : Ke(freei,freej) +=
1479 0 : TensorTools::inner_product(phi_coarse[i][qp],
1480 0 : phi_coarse[j][qp]) *
1481 : JxW[qp];
1482 0 : if (cont == C_ONE)
1483 : {
1484 0 : if (dof_is_fixed[j])
1485 0 : Fe(freei) -=
1486 0 : TensorTools::inner_product((*dphi_coarse)[i][qp],
1487 0 : (*dphi_coarse)[j][qp]) *
1488 0 : JxW[qp] * Ue(j);
1489 : else
1490 0 : Ke(freei,freej) +=
1491 0 : TensorTools::inner_product((*dphi_coarse)[i][qp],
1492 0 : (*dphi_coarse)[j][qp]) *
1493 : JxW[qp];
1494 : }
1495 0 : if (!dof_is_fixed[j])
1496 0 : freej++;
1497 : }
1498 0 : Fe(freei) += TensorTools::inner_product(phi_coarse[i][qp], fineval) *
1499 : JxW[qp];
1500 0 : if (cont == C_ONE)
1501 0 : Fe(freei) += TensorTools::inner_product(finegrad, (*dphi_coarse)[i][qp]) * JxW[qp];
1502 0 : freei++;
1503 : }
1504 : }
1505 : }
1506 0 : Ke.cholesky_solve(Fe, Uint);
1507 :
1508 : // Transfer new interior solutions to element
1509 0 : for (unsigned int i=0; i != free_dofs; ++i)
1510 : {
1511 0 : Number & ui = Ue(free_dof[i]);
1512 0 : libmesh_assert(std::abs(ui) < TOLERANCE ||
1513 : std::abs(ui - Uint(i)) < TOLERANCE);
1514 0 : ui = Uint(i);
1515 : // We should be fixing all dofs by now; no need to keep track of
1516 : // that unless we're debugging
1517 : #ifndef NDEBUG
1518 0 : dof_is_fixed[free_dof[i]] = true;
1519 : #endif
1520 : }
1521 :
1522 : #ifndef NDEBUG
1523 : // Make sure every DoF got reached!
1524 0 : for (unsigned int i=0; i != new_n_dofs; ++i)
1525 0 : libmesh_assert(dof_is_fixed[i]);
1526 : #endif
1527 0 : }
1528 :
1529 :
1530 :
1531 : template <typename OutputType>
1532 : void
1533 0 : FEGenericBase<OutputType>::coarsened_dof_values(const NumericVector<Number> & old_vector,
1534 : const DofMap & dof_map,
1535 : const Elem * elem,
1536 : DenseVector<Number> & Ue,
1537 : const bool use_old_dof_indices)
1538 : {
1539 0 : Ue.resize(0);
1540 :
1541 0 : for (auto v : make_range(dof_map.n_variables()))
1542 : {
1543 0 : DenseVector<Number> Usub;
1544 :
1545 0 : coarsened_dof_values(old_vector, dof_map, elem, Usub,
1546 : v, use_old_dof_indices);
1547 :
1548 0 : Ue.append (Usub);
1549 : }
1550 0 : }
1551 :
1552 :
1553 :
1554 : template <typename OutputType>
1555 : void
1556 671882 : FEGenericBase<OutputType>::compute_proj_constraints (DofConstraints & constraints,
1557 : DofMap & dof_map,
1558 : const unsigned int variable_number,
1559 : const Elem * elem)
1560 : {
1561 56462 : libmesh_assert(elem);
1562 :
1563 671882 : const unsigned int Dim = elem->dim();
1564 :
1565 : // Only constrain elements in 2,3D.
1566 671882 : if (Dim == 1)
1567 108039 : return;
1568 :
1569 : // Only constrain active elements with this method
1570 56462 : if (!elem->active())
1571 9226 : return;
1572 :
1573 563843 : const Variable & var = dof_map.variable(variable_number);
1574 47236 : const FEType & base_fe_type = var.type();
1575 563843 : const bool add_p_level = base_fe_type.p_refinement;
1576 :
1577 : // Construct FE objects for this element and its neighbors.
1578 563843 : std::unique_ptr<FEGenericBase<OutputShape>> my_fe
1579 : (FEGenericBase<OutputShape>::build(Dim, base_fe_type));
1580 47236 : my_fe->add_p_level_in_reinit(add_p_level);
1581 563843 : const FEContinuity cont = my_fe->get_continuity();
1582 :
1583 : // We don't need to constrain discontinuous elements
1584 563843 : if (cont == DISCONTINUOUS)
1585 0 : return;
1586 47236 : libmesh_assert (cont == C_ZERO || cont == C_ONE ||
1587 : cont == SIDE_DISCONTINUOUS);
1588 :
1589 : // this would require some generalisation:
1590 : // - e.g. the 'my_fe'-object needs generalisation
1591 : // - due to lack of one-to-one correspondence of DOFs and nodes,
1592 : // this doesn't work easily.
1593 126075 : if (elem->infinite())
1594 0 : libmesh_not_implemented();
1595 :
1596 611079 : std::unique_ptr<FEGenericBase<OutputShape>> neigh_fe
1597 : (FEGenericBase<OutputShape>::build(Dim, base_fe_type));
1598 47236 : neigh_fe->add_p_level_in_reinit(add_p_level);
1599 :
1600 611079 : QGauss my_qface(Dim-1, base_fe_type.default_quadrature_order());
1601 563843 : my_fe->attach_quadrature_rule (&my_qface);
1602 94472 : std::vector<Point> neigh_qface;
1603 :
1604 126075 : const std::vector<Real> & JxW = my_fe->get_JxW();
1605 126075 : const std::vector<Point> & q_point = my_fe->get_xyz();
1606 47236 : const std::vector<std::vector<OutputShape>> & phi = my_fe->get_phi();
1607 47236 : const std::vector<std::vector<OutputShape>> & neigh_phi =
1608 : neigh_fe->get_phi();
1609 47236 : const std::vector<Point> * face_normals = nullptr;
1610 47236 : const std::vector<std::vector<OutputGradient>> * dphi = nullptr;
1611 47236 : const std::vector<std::vector<OutputGradient>> * neigh_dphi = nullptr;
1612 :
1613 94472 : std::vector<dof_id_type> my_dof_indices, neigh_dof_indices;
1614 94472 : std::vector<unsigned int> my_side_dofs, neigh_side_dofs;
1615 :
1616 563843 : if (cont == C_ONE)
1617 : {
1618 7218 : const std::vector<Point> & ref_face_normals =
1619 7434 : my_fe->get_normals();
1620 3609 : face_normals = &ref_face_normals;
1621 3609 : const std::vector<std::vector<OutputGradient>> & ref_dphi =
1622 : my_fe->get_dphi();
1623 3609 : dphi = &ref_dphi;
1624 3609 : const std::vector<std::vector<OutputGradient>> & ref_neigh_dphi =
1625 : neigh_fe->get_dphi();
1626 3609 : neigh_dphi = &ref_neigh_dphi;
1627 : }
1628 :
1629 658315 : DenseMatrix<Real> Ke;
1630 563843 : DenseVector<Real> Fe;
1631 141708 : std::vector<DenseVector<Real>> Ue;
1632 :
1633 : // Look at the element faces. Check to see if we need to
1634 : // build constraints.
1635 2683416 : for (auto s : elem->side_index_range())
1636 : {
1637 : // Get pointers to the element's neighbor.
1638 2119573 : const Elem * neigh = elem->neighbor_ptr(s);
1639 :
1640 2119573 : if (!neigh)
1641 202457 : continue;
1642 :
1643 1898594 : if (!var.active_on_subdomain(neigh->subdomain_id()))
1644 1636 : continue;
1645 :
1646 : // h refinement constraints:
1647 : // constrain dofs shared between
1648 : // this element and ones coarser
1649 : // than this element.
1650 1896814 : if (neigh->level() < elem->level())
1651 : {
1652 119138 : unsigned int s_neigh = neigh->which_neighbor_am_i(elem);
1653 10120 : libmesh_assert_less (s_neigh, neigh->n_neighbors());
1654 :
1655 : // Find the minimum p level; we build the h constraint
1656 : // matrix with this and then constrain away all higher p
1657 : // DoFs.
1658 10120 : libmesh_assert(neigh->active());
1659 139378 : const unsigned int min_p_level = add_p_level *
1660 139378 : std::min(elem->p_level(), neigh->p_level());
1661 : // we may need to make the FE objects reinit with the
1662 : // minimum shared p_level
1663 119138 : const unsigned int old_elem_level = add_p_level * elem->p_level();
1664 119138 : if (old_elem_level != min_p_level)
1665 360 : my_fe->set_fe_order(my_fe->get_fe_type().order.get_order() + min_p_level - old_elem_level);
1666 119138 : const unsigned int old_neigh_level = add_p_level * neigh->p_level();
1667 119138 : if (old_neigh_level != min_p_level)
1668 0 : neigh_fe->set_fe_order(neigh_fe->get_fe_type().order.get_order() + min_p_level - old_neigh_level);
1669 :
1670 119138 : my_fe->reinit(elem, s);
1671 :
1672 : // This function gets called element-by-element, so there
1673 : // will be a lot of memory allocation going on. We can
1674 : // at least minimize this for the case of the dof indices
1675 : // by efficiently preallocating the requisite storage.
1676 : // n_nodes is not necessarily n_dofs, but it is better
1677 : // than nothing!
1678 119138 : my_dof_indices.reserve (elem->n_nodes());
1679 119138 : neigh_dof_indices.reserve (neigh->n_nodes());
1680 :
1681 119138 : dof_map.dof_indices (elem, my_dof_indices,
1682 : variable_number,
1683 : min_p_level);
1684 119138 : dof_map.dof_indices (neigh, neigh_dof_indices,
1685 : variable_number,
1686 : min_p_level);
1687 :
1688 10120 : const unsigned int n_qp = my_qface.n_points();
1689 :
1690 119138 : FEMap::inverse_map (Dim, neigh, q_point, neigh_qface);
1691 :
1692 119138 : neigh_fe->reinit(neigh, &neigh_qface);
1693 :
1694 : // We're only concerned with DOFs whose values (and/or first
1695 : // derivatives for C1 elements) are supported on side nodes
1696 119138 : FEType elem_fe_type = base_fe_type;
1697 119138 : if (old_elem_level != min_p_level)
1698 360 : elem_fe_type.order = base_fe_type.order.get_order() + min_p_level - old_elem_level;
1699 119138 : FEType neigh_fe_type = base_fe_type;
1700 119138 : if (old_neigh_level != min_p_level)
1701 0 : neigh_fe_type.order = base_fe_type.order.get_order() + min_p_level - old_neigh_level;
1702 119138 : FEInterface::dofs_on_side(elem, Dim, elem_fe_type, s, my_side_dofs);
1703 119138 : FEInterface::dofs_on_side(neigh, Dim, neigh_fe_type, s_neigh, neigh_side_dofs);
1704 :
1705 10120 : const unsigned int n_side_dofs =
1706 20240 : cast_int<unsigned int>(my_side_dofs.size());
1707 10120 : libmesh_assert_equal_to (n_side_dofs, neigh_side_dofs.size());
1708 :
1709 : #ifndef NDEBUG
1710 55500 : for (auto i : my_side_dofs)
1711 45380 : libmesh_assert_less(i, my_dof_indices.size());
1712 55500 : for (auto i : neigh_side_dofs)
1713 45380 : libmesh_assert_less(i, neigh_dof_indices.size());
1714 : #endif
1715 :
1716 109018 : Ke.resize (n_side_dofs, n_side_dofs);
1717 119138 : Ue.resize(n_side_dofs);
1718 :
1719 : // Form the projection matrix, (inner product of fine basis
1720 : // functions against fine test functions)
1721 655688 : for (unsigned int is = 0; is != n_side_dofs; ++is)
1722 : {
1723 581930 : const unsigned int i = my_side_dofs[is];
1724 3219776 : for (unsigned int js = 0; js != n_side_dofs; ++js)
1725 : {
1726 2908110 : const unsigned int j = my_side_dofs[js];
1727 14927746 : for (unsigned int qp = 0; qp != n_qp; ++qp)
1728 : {
1729 18280808 : Ke(is,js) += JxW[qp] * TensorTools::inner_product(phi[i][qp], phi[j][qp]);
1730 12244520 : if (cont == C_ONE)
1731 4220200 : Ke(is,js) += JxW[qp] *
1732 646400 : TensorTools::inner_product((*dphi)[i][qp] *
1733 : (*face_normals)[qp],
1734 1616000 : (*dphi)[j][qp] *
1735 : (*face_normals)[qp]);
1736 : }
1737 : }
1738 : }
1739 :
1740 : // Form the right hand sides, (inner product of coarse basis
1741 : // functions against fine test functions)
1742 655688 : for (unsigned int is = 0; is != n_side_dofs; ++is)
1743 : {
1744 581930 : const unsigned int i = neigh_side_dofs[is];
1745 491170 : Fe.resize (n_side_dofs);
1746 3219776 : for (unsigned int js = 0; js != n_side_dofs; ++js)
1747 : {
1748 2908110 : const unsigned int j = my_side_dofs[js];
1749 14927746 : for (unsigned int qp = 0; qp != n_qp; ++qp)
1750 : {
1751 14256616 : Fe(js) += JxW[qp] *
1752 14256616 : TensorTools::inner_product(neigh_phi[i][qp],
1753 13250568 : phi[j][qp]);
1754 12244520 : if (cont == C_ONE)
1755 4220200 : Fe(js) += JxW[qp] *
1756 969600 : TensorTools::inner_product((*neigh_dphi)[i][qp] *
1757 : (*face_normals)[qp],
1758 1616000 : (*dphi)[j][qp] *
1759 : (*face_normals)[qp]);
1760 : }
1761 : }
1762 581930 : Ke.cholesky_solve(Fe, Ue[is]);
1763 : }
1764 :
1765 655688 : for (unsigned int js = 0; js != n_side_dofs; ++js)
1766 : {
1767 536550 : const unsigned int j = my_side_dofs[js];
1768 581930 : const dof_id_type my_dof_g = my_dof_indices[j];
1769 45380 : libmesh_assert_not_equal_to (my_dof_g, DofObject::invalid_id);
1770 :
1771 : // Hunt for "constraining against myself" cases before
1772 : // we bother creating a constraint row
1773 45380 : bool self_constraint = false;
1774 2591921 : for (unsigned int is = 0; is != n_side_dofs; ++is)
1775 : {
1776 2217761 : const unsigned int i = neigh_side_dofs[is];
1777 2217761 : const dof_id_type their_dof_g = neigh_dof_indices[i];
1778 185750 : libmesh_assert_not_equal_to (their_dof_g, DofObject::invalid_id);
1779 :
1780 2217761 : if (their_dof_g == my_dof_g)
1781 : {
1782 : #ifndef NDEBUG
1783 13708 : const Real their_dof_value = Ue[is](js);
1784 13708 : libmesh_assert_less (std::abs(their_dof_value-1.),
1785 : 10*TOLERANCE);
1786 :
1787 86448 : for (unsigned int k = 0; k != n_side_dofs; ++k)
1788 72740 : libmesh_assert(k == is ||
1789 : std::abs(Ue[k](js)) <
1790 : 10*TOLERANCE);
1791 : #endif
1792 :
1793 13708 : self_constraint = true;
1794 13708 : break;
1795 : }
1796 : }
1797 :
1798 536550 : if (self_constraint)
1799 239220 : continue;
1800 :
1801 : DofConstraintRow * constraint_row;
1802 :
1803 : // we may be running constraint methods concurrently
1804 : // on multiple threads, so we need a lock to
1805 : // ensure that this constraint is "ours"
1806 : {
1807 31672 : Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
1808 :
1809 374160 : if (dof_map.is_constrained_dof(my_dof_g))
1810 6863 : continue;
1811 :
1812 297330 : constraint_row = &(constraints[my_dof_g]);
1813 24809 : libmesh_assert(constraint_row->empty());
1814 : }
1815 :
1816 1685196 : for (unsigned int is = 0; is != n_side_dofs; ++is)
1817 : {
1818 1387866 : const unsigned int i = neigh_side_dofs[is];
1819 1387866 : const dof_id_type their_dof_g = neigh_dof_indices[i];
1820 114227 : libmesh_assert_not_equal_to (their_dof_g, DofObject::invalid_id);
1821 114227 : libmesh_assert_not_equal_to (their_dof_g, my_dof_g);
1822 :
1823 1502093 : const Real their_dof_value = Ue[is](js);
1824 :
1825 1387866 : if (std::abs(their_dof_value) < 10*TOLERANCE)
1826 760320 : continue;
1827 :
1828 51566 : constraint_row->emplace(their_dof_g, their_dof_value);
1829 : }
1830 : }
1831 :
1832 119138 : my_fe->set_fe_order(my_fe->get_fe_type().order.get_order() + old_elem_level - min_p_level);
1833 119138 : neigh_fe->set_fe_order(neigh_fe->get_fe_type().order.get_order() + old_neigh_level - min_p_level);
1834 : }
1835 :
1836 1896814 : if (add_p_level)
1837 : {
1838 : // p refinement constraints:
1839 : // constrain dofs shared between
1840 : // active elements and neighbors with
1841 : // lower polynomial degrees
1842 : const unsigned int min_p_level =
1843 2055899 : neigh->min_p_level_by_neighbor(elem, elem->p_level());
1844 2055899 : if (min_p_level < elem->p_level())
1845 : {
1846 : // Adaptive p refinement of non-hierarchic bases will
1847 : // require more coding
1848 48 : libmesh_assert(my_fe->is_hierarchic());
1849 576 : dof_map.constrain_p_dofs(variable_number, elem,
1850 : s, min_p_level);
1851 : }
1852 : }
1853 : }
1854 1408113 : }
1855 :
1856 : #endif // #ifdef LIBMESH_ENABLE_AMR
1857 :
1858 :
1859 :
1860 : #ifdef LIBMESH_ENABLE_PERIODIC
1861 : template <typename OutputType>
1862 : void
1863 264164 : FEGenericBase<OutputType>::
1864 : compute_periodic_constraints (DofConstraints & constraints,
1865 : DofMap & dof_map,
1866 : const PeriodicBoundaries & boundaries,
1867 : const MeshBase & mesh,
1868 : const PointLocatorBase * point_locator,
1869 : const unsigned int variable_number,
1870 : const Elem * elem)
1871 : {
1872 : // Only bother if we truly have periodic boundaries
1873 264164 : if (boundaries.empty())
1874 58473 : return;
1875 :
1876 67082 : libmesh_assert(elem);
1877 :
1878 : // Only constrain active elements with this method
1879 67082 : if (!elem->active())
1880 19491 : return;
1881 :
1882 138677 : if (elem->infinite())
1883 0 : libmesh_not_implemented();
1884 :
1885 205691 : const unsigned int Dim = elem->dim();
1886 :
1887 : // We need sys_number and variable_number for DofObject methods
1888 : // later
1889 95182 : const unsigned int sys_number = dof_map.sys_number();
1890 :
1891 47591 : const FEType & base_fe_type = dof_map.variable_type(variable_number);
1892 :
1893 : // Construct FE objects for this element and its pseudo-neighbors.
1894 205691 : std::unique_ptr<FEGenericBase<OutputShape>> my_fe
1895 : (FEGenericBase<OutputShape>::build(Dim, base_fe_type));
1896 205691 : const FEContinuity cont = my_fe->get_continuity();
1897 :
1898 : // We don't need to constrain discontinuous elements
1899 205691 : if (cont == DISCONTINUOUS)
1900 0 : return;
1901 47591 : libmesh_assert (cont == C_ZERO || cont == C_ONE);
1902 :
1903 : // We'll use element size to generate relative tolerances later
1904 205691 : const Real primary_hmin = elem->hmin();
1905 :
1906 253282 : std::unique_ptr<FEGenericBase<OutputShape>> neigh_fe
1907 : (FEGenericBase<OutputShape>::build(Dim, base_fe_type));
1908 :
1909 253282 : QGauss my_qface(Dim-1, base_fe_type.default_quadrature_order());
1910 205691 : my_fe->attach_quadrature_rule (&my_qface);
1911 95182 : std::vector<Point> neigh_qface;
1912 :
1913 138677 : const std::vector<Real> & JxW = my_fe->get_JxW();
1914 138677 : const std::vector<Point> & q_point = my_fe->get_xyz();
1915 47591 : const std::vector<std::vector<OutputShape>> & phi = my_fe->get_phi();
1916 47591 : const std::vector<std::vector<OutputShape>> & neigh_phi =
1917 : neigh_fe->get_phi();
1918 47591 : const std::vector<Point> * face_normals = nullptr;
1919 47591 : const std::vector<std::vector<OutputGradient>> * dphi = nullptr;
1920 47591 : const std::vector<std::vector<OutputGradient>> * neigh_dphi = nullptr;
1921 95182 : std::vector<dof_id_type> my_dof_indices, neigh_dof_indices;
1922 95182 : std::vector<unsigned int> my_side_dofs, neigh_side_dofs;
1923 :
1924 205691 : if (cont != C_ZERO)
1925 : {
1926 8192 : const std::vector<Point> & ref_face_normals =
1927 4096 : my_fe->get_normals();
1928 4096 : face_normals = &ref_face_normals;
1929 4096 : const std::vector<std::vector<OutputGradient>> & ref_dphi =
1930 : my_fe->get_dphi();
1931 4096 : dphi = &ref_dphi;
1932 4096 : const std::vector<std::vector<OutputGradient>> & ref_neigh_dphi =
1933 : neigh_fe->get_dphi();
1934 4096 : neigh_dphi = &ref_neigh_dphi;
1935 : }
1936 :
1937 300873 : DenseMatrix<Real> Ke;
1938 205691 : DenseVector<Real> Fe;
1939 142773 : std::vector<DenseVector<Real>> Ue;
1940 :
1941 : // Container to catch the boundary ids that BoundaryInfo hands us.
1942 95182 : std::vector<boundary_id_type> bc_ids;
1943 :
1944 : // Look at the element faces. Check to see if we need to
1945 : // build constraints.
1946 205691 : const unsigned short int max_ns = elem->n_sides();
1947 1018023 : for (unsigned short int s = 0; s != max_ns; ++s)
1948 : {
1949 1001144 : if (elem->neighbor_ptr(s))
1950 589194 : continue;
1951 :
1952 39424 : mesh.get_boundary_info().boundary_ids (elem, s, bc_ids);
1953 :
1954 59984 : for (const auto & boundary_id : bc_ids)
1955 : {
1956 20560 : const PeriodicBoundaryBase * periodic = boundaries.boundary(boundary_id);
1957 20560 : if (!periodic || !periodic->is_my_variable(variable_number))
1958 5784 : continue;
1959 :
1960 3044 : libmesh_assert(point_locator);
1961 :
1962 : // Get pointers to the element's neighbor.
1963 : unsigned int s_neigh;
1964 14776 : const Elem * neigh = boundaries.neighbor(boundary_id, *point_locator, elem, s, &s_neigh);
1965 :
1966 14776 : libmesh_error_msg_if(neigh == nullptr,
1967 : "PeriodicBoundaries point locator object returned nullptr!");
1968 :
1969 : // periodic (and possibly h refinement) constraints:
1970 : // constrain dofs shared between
1971 : // this element and ones as coarse
1972 : // as or coarser than this element.
1973 14776 : if (neigh->level() <= elem->level())
1974 : {
1975 : #ifdef LIBMESH_ENABLE_AMR
1976 : // Find the minimum p level; we build the h constraint
1977 : // matrix with this and then constrain away all higher p
1978 : // DoFs.
1979 2596 : libmesh_assert(neigh->active());
1980 13432 : const unsigned int min_p_level =
1981 18624 : std::min(elem->p_level(), neigh->p_level());
1982 :
1983 : // we may need to make the FE objects reinit with the
1984 : // minimum shared p_level
1985 : // FIXME - I hate using const_cast<> and avoiding
1986 : // accessor functions; there's got to be a
1987 : // better way to do this!
1988 2596 : const unsigned int old_elem_level = elem->p_level();
1989 13432 : if (old_elem_level != min_p_level)
1990 0 : (const_cast<Elem *>(elem))->hack_p_level(min_p_level);
1991 5192 : const unsigned int old_neigh_level = neigh->p_level();
1992 13432 : if (old_neigh_level != min_p_level)
1993 0 : (const_cast<Elem *>(neigh))->hack_p_level(min_p_level);
1994 : #endif // #ifdef LIBMESH_ENABLE_AMR
1995 :
1996 : // We can do a projection with a single integration,
1997 : // due to the assumption of nested finite element
1998 : // subspaces.
1999 : // FIXME: it might be more efficient to do nodes,
2000 : // then edges, then side, to reduce the size of the
2001 : // Cholesky factorization(s)
2002 13432 : my_fe->reinit(elem, s);
2003 :
2004 13432 : dof_map.dof_indices (elem, my_dof_indices,
2005 : variable_number);
2006 13432 : dof_map.dof_indices (neigh, neigh_dof_indices,
2007 : variable_number);
2008 :
2009 : // We use neigh_dof_indices_all_variables in the case that the
2010 : // periodic boundary condition involves mappings between multiple
2011 : // variables.
2012 7788 : std::vector<std::vector<dof_id_type>> neigh_dof_indices_all_variables;
2013 13432 : if(periodic->has_transformation_matrix())
2014 : {
2015 7200 : const std::set<unsigned int> & variables = periodic->get_variables();
2016 7200 : neigh_dof_indices_all_variables.resize(variables.size());
2017 600 : unsigned int index = 0;
2018 28800 : for(unsigned int var : variables)
2019 : {
2020 23400 : dof_map.dof_indices (neigh, neigh_dof_indices_all_variables[index],
2021 : var);
2022 21600 : index++;
2023 : }
2024 : }
2025 :
2026 2596 : const unsigned int n_qp = my_qface.n_points();
2027 :
2028 : // Translate the quadrature points over to the
2029 : // neighbor's boundary
2030 18624 : std::vector<Point> neigh_point(q_point.size());
2031 54848 : for (auto i : index_range(neigh_point))
2032 47820 : neigh_point[i] = periodic->get_corresponding_pos(q_point[i]);
2033 :
2034 13432 : FEMap::inverse_map (Dim, neigh, neigh_point,
2035 : neigh_qface);
2036 :
2037 13432 : neigh_fe->reinit(neigh, &neigh_qface);
2038 :
2039 : // We're only concerned with DOFs whose values (and/or first
2040 : // derivatives for C1 elements) are supported on side nodes
2041 13432 : FEInterface::dofs_on_side(elem, Dim, base_fe_type, s, my_side_dofs);
2042 13432 : FEInterface::dofs_on_side(neigh, Dim, base_fe_type, s_neigh, neigh_side_dofs);
2043 :
2044 : // We're done with functions that examine Elem::p_level(),
2045 : // so let's unhack those levels
2046 : #ifdef LIBMESH_ENABLE_AMR
2047 16028 : if (elem->p_level() != old_elem_level)
2048 0 : (const_cast<Elem *>(elem))->hack_p_level(old_elem_level);
2049 16028 : if (neigh->p_level() != old_neigh_level)
2050 0 : (const_cast<Elem *>(neigh))->hack_p_level(old_neigh_level);
2051 : #endif // #ifdef LIBMESH_ENABLE_AMR
2052 :
2053 2596 : const unsigned int n_side_dofs =
2054 : cast_int<unsigned int>
2055 5192 : (my_side_dofs.size());
2056 2596 : libmesh_assert_equal_to (n_side_dofs, neigh_side_dofs.size());
2057 :
2058 10836 : Ke.resize (n_side_dofs, n_side_dofs);
2059 13432 : Ue.resize(n_side_dofs);
2060 :
2061 : // Form the projection matrix, (inner product of fine basis
2062 : // functions against fine test functions)
2063 54936 : for (unsigned int is = 0; is != n_side_dofs; ++is)
2064 : {
2065 47916 : const unsigned int i = my_side_dofs[is];
2066 182832 : for (unsigned int js = 0; js != n_side_dofs; ++js)
2067 : {
2068 159012 : const unsigned int j = my_side_dofs[js];
2069 656192 : for (unsigned int qp = 0; qp != n_qp; ++qp)
2070 : {
2071 624296 : Ke(is,js) += JxW[qp] *
2072 624296 : TensorTools::inner_product(phi[i][qp],
2073 569580 : phi[j][qp]);
2074 514864 : if (cont != C_ZERO)
2075 384 : Ke(is,js) += JxW[qp] *
2076 64 : TensorTools::inner_product((*dphi)[i][qp] *
2077 : (*face_normals)[qp],
2078 160 : (*dphi)[j][qp] *
2079 : (*face_normals)[qp]);
2080 : }
2081 : }
2082 : }
2083 :
2084 : // Form the right hand sides, (inner product of coarse basis
2085 : // functions against fine test functions)
2086 54936 : for (unsigned int is = 0; is != n_side_dofs; ++is)
2087 : {
2088 47916 : const unsigned int i = neigh_side_dofs[is];
2089 35092 : Fe.resize (n_side_dofs);
2090 182832 : for (unsigned int js = 0; js != n_side_dofs; ++js)
2091 : {
2092 159012 : const unsigned int j = my_side_dofs[js];
2093 656192 : for (unsigned int qp = 0; qp != n_qp; ++qp)
2094 : {
2095 624296 : Fe(js) += JxW[qp] *
2096 624296 : TensorTools::inner_product(neigh_phi[i][qp],
2097 569580 : phi[j][qp]);
2098 514864 : if (cont != C_ZERO)
2099 384 : Fe(js) += JxW[qp] *
2100 96 : TensorTools::inner_product((*neigh_dphi)[i][qp] *
2101 : (*face_normals)[qp],
2102 160 : (*dphi)[j][qp] *
2103 : (*face_normals)[qp]);
2104 : }
2105 : }
2106 47916 : Ke.cholesky_solve(Fe, Ue[is]);
2107 : }
2108 :
2109 : // Make sure we're not adding recursive constraints
2110 : // due to the redundancy in the way we add periodic
2111 : // boundary constraints
2112 : //
2113 : // In order for this to work while threaded or on
2114 : // distributed meshes, we need a rigorous way to
2115 : // avoid recursive constraints. Here it is:
2116 : //
2117 : // For vertex DoFs, if there is a "prior" element
2118 : // (i.e. a coarser element or an equally refined
2119 : // element with a lower id) on this boundary which
2120 : // contains the vertex point, then we will avoid
2121 : // generating constraints; the prior element (or
2122 : // something prior to it) may do so. If we are the
2123 : // most prior (or "primary") element on this
2124 : // boundary sharing this point, then we look at the
2125 : // boundary periodic to us, we find the primary
2126 : // element there, and if that primary is coarser or
2127 : // equal-but-lower-id, then our vertex dofs are
2128 : // constrained in terms of that element.
2129 : //
2130 : // For edge DoFs, if there is a coarser element
2131 : // on this boundary sharing this edge, then we will
2132 : // avoid generating constraints (we will be
2133 : // constrained indirectly via AMR constraints
2134 : // connecting us to the coarser element's DoFs). If
2135 : // we are the coarsest element sharing this edge,
2136 : // then we generate constraints if and only if we
2137 : // are finer than the coarsest element on the
2138 : // boundary periodic to us sharing the corresponding
2139 : // periodic edge, or if we are at equal level but
2140 : // our edge nodes have higher ids than the periodic
2141 : // edge nodes (sorted from highest to lowest, then
2142 : // compared lexicographically)
2143 : //
2144 : // For face DoFs, we generate constraints if we are
2145 : // finer than our periodic neighbor, or if we are at
2146 : // equal level but our element id is higher than its
2147 : // element id.
2148 : //
2149 : // If the primary neighbor is also the current elem
2150 : // (a 1-element-thick mesh) then we choose which
2151 : // vertex dofs to constrain via lexicographic
2152 : // ordering on point locations
2153 :
2154 : // FIXME: This code doesn't yet properly handle
2155 : // cases where multiple different periodic BCs
2156 : // intersect.
2157 5192 : std::set<dof_id_type> my_constrained_dofs;
2158 :
2159 : // Container to catch boundary IDs handed back by BoundaryInfo.
2160 5192 : std::vector<boundary_id_type> new_bc_ids;
2161 :
2162 96264 : for (auto n : elem->node_index_range())
2163 : {
2164 82832 : if (!elem->is_node_on_side(n,s))
2165 35012 : continue;
2166 :
2167 6404 : const Node & my_node = elem->node_ref(n);
2168 :
2169 41416 : if (elem->is_vertex(n))
2170 : {
2171 : // Find all boundary ids that include this
2172 : // point and have periodic boundary
2173 : // conditions for this variable
2174 6384 : std::set<boundary_id_type> point_bcids;
2175 :
2176 256440 : for (unsigned int new_s = 0;
2177 262824 : new_s != max_ns; ++new_s)
2178 : {
2179 221648 : if (!elem->is_node_on_side(n,new_s))
2180 95464 : continue;
2181 :
2182 111064 : mesh.get_boundary_info().boundary_ids (elem, s, new_bc_ids);
2183 :
2184 222128 : for (const auto & new_boundary_id : new_bc_ids)
2185 : {
2186 111064 : const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
2187 111064 : if (new_periodic && new_periodic->is_my_variable(variable_number))
2188 95904 : point_bcids.insert(new_boundary_id);
2189 : }
2190 : }
2191 :
2192 : // See if this vertex has point neighbors to
2193 : // defer to
2194 44655 : if (primary_boundary_point_neighbor
2195 41176 : (elem, my_node, mesh.get_boundary_info(), point_bcids)
2196 6384 : != elem)
2197 21511 : continue;
2198 :
2199 : // Find the complementary boundary id set
2200 2905 : std::set<boundary_id_type> point_pairedids;
2201 32372 : for (const auto & new_boundary_id : point_bcids)
2202 : {
2203 16186 : const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
2204 16186 : point_pairedids.insert(new_periodic->pairedboundary);
2205 : }
2206 :
2207 : // What do we want to constrain against?
2208 2905 : const Elem * primary_elem = nullptr;
2209 2905 : const Elem * main_neigh = nullptr;
2210 16186 : Point main_pt = my_node,
2211 16186 : primary_pt = my_node;
2212 :
2213 32372 : for (const auto & new_boundary_id : point_bcids)
2214 : {
2215 : // Find the corresponding periodic point and
2216 : // its primary neighbor
2217 16186 : const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
2218 :
2219 : const Point neigh_pt =
2220 16186 : new_periodic->get_corresponding_pos(my_node);
2221 :
2222 : // If the point is getting constrained
2223 : // to itself by this PBC then we don't
2224 : // generate any constraints
2225 16186 : if (neigh_pt.absolute_fuzzy_equals
2226 16186 : (my_node, primary_hmin*TOLERANCE))
2227 6828 : continue;
2228 :
2229 : // Otherwise we'll have a constraint in
2230 : // one direction or another
2231 16186 : if (!primary_elem)
2232 2905 : primary_elem = elem;
2233 :
2234 2905 : const Elem * primary_neigh =
2235 16186 : primary_boundary_point_neighbor(neigh, neigh_pt,
2236 : mesh.get_boundary_info(),
2237 : point_pairedids);
2238 :
2239 2905 : libmesh_assert(primary_neigh);
2240 :
2241 16186 : if (new_boundary_id == boundary_id)
2242 : {
2243 2905 : main_neigh = primary_neigh;
2244 16186 : main_pt = neigh_pt;
2245 : }
2246 :
2247 : // Finer elements will get constrained in
2248 : // terms of coarser neighbors, not the
2249 : // other way around
2250 29467 : if ((primary_neigh->level() > primary_elem->level()) ||
2251 :
2252 : // For equal-level elements, the one with
2253 : // higher id gets constrained in terms of
2254 : // the one with lower id
2255 25646 : (primary_neigh->level() == primary_elem->level() &&
2256 28558 : primary_neigh->id() > primary_elem->id()) ||
2257 :
2258 : // On a one-element-thick mesh, we compare
2259 : // points to see what side gets constrained
2260 1880 : (primary_neigh == primary_elem &&
2261 0 : (neigh_pt > primary_pt)))
2262 6828 : continue;
2263 :
2264 1880 : primary_elem = primary_neigh;
2265 9358 : primary_pt = neigh_pt;
2266 : }
2267 :
2268 13493 : if (!primary_elem ||
2269 19091 : primary_elem != main_neigh ||
2270 1880 : primary_pt != main_pt)
2271 1025 : continue;
2272 : }
2273 240 : else if (elem->is_edge(n))
2274 : {
2275 : // Find which edge we're on
2276 240 : unsigned int e=0, ne = elem->n_edges();
2277 384 : for (; e != ne; ++e)
2278 : {
2279 384 : if (elem->is_node_on_edge(n,e))
2280 20 : break;
2281 : }
2282 20 : libmesh_assert_less (e, elem->n_edges());
2283 :
2284 : // Find the edge end nodes
2285 : const Node
2286 20 : * e1 = nullptr,
2287 20 : * e2 = nullptr;
2288 612 : for (auto nn : elem->node_index_range())
2289 : {
2290 612 : if (nn == n)
2291 0 : continue;
2292 :
2293 612 : if (elem->is_node_on_edge(nn, e))
2294 : {
2295 480 : if (e1 == nullptr)
2296 : {
2297 40 : e1 = elem->node_ptr(nn);
2298 : }
2299 : else
2300 : {
2301 40 : e2 = elem->node_ptr(nn);
2302 240 : break;
2303 : }
2304 : }
2305 : }
2306 20 : libmesh_assert (e1 && e2);
2307 :
2308 : // Find all boundary ids that include this
2309 : // edge and have periodic boundary
2310 : // conditions for this variable
2311 20 : std::set<boundary_id_type> edge_bcids;
2312 :
2313 940 : for (unsigned int new_s = 0;
2314 960 : new_s != max_ns; ++new_s)
2315 : {
2316 720 : if (!elem->is_node_on_side(n,new_s))
2317 440 : continue;
2318 :
2319 : // We're reusing the new_bc_ids vector created outside the loop over nodes.
2320 240 : mesh.get_boundary_info().boundary_ids (elem, s, new_bc_ids);
2321 :
2322 480 : for (const auto & new_boundary_id : new_bc_ids)
2323 : {
2324 240 : const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
2325 240 : if (new_periodic && new_periodic->is_my_variable(variable_number))
2326 220 : edge_bcids.insert(new_boundary_id);
2327 : }
2328 : }
2329 :
2330 :
2331 : // See if this edge has neighbors to defer to
2332 240 : if (primary_boundary_edge_neighbor
2333 240 : (elem, *e1, *e2, mesh.get_boundary_info(), edge_bcids)
2334 20 : != elem)
2335 0 : continue;
2336 :
2337 : // Find the complementary boundary id set
2338 20 : std::set<boundary_id_type> edge_pairedids;
2339 480 : for (const auto & new_boundary_id : edge_bcids)
2340 : {
2341 240 : const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
2342 240 : edge_pairedids.insert(new_periodic->pairedboundary);
2343 : }
2344 :
2345 : // What do we want to constrain against?
2346 20 : const Elem * primary_elem = nullptr;
2347 20 : const Elem * main_neigh = nullptr;
2348 240 : Point main_pt1 = *e1,
2349 240 : main_pt2 = *e2,
2350 240 : primary_pt1 = *e1,
2351 240 : primary_pt2 = *e2;
2352 :
2353 480 : for (const auto & new_boundary_id : edge_bcids)
2354 : {
2355 : // Find the corresponding periodic edge and
2356 : // its primary neighbor
2357 240 : const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
2358 :
2359 240 : Point neigh_pt1 = new_periodic->get_corresponding_pos(*e1),
2360 240 : neigh_pt2 = new_periodic->get_corresponding_pos(*e2);
2361 :
2362 : // If the edge is getting constrained
2363 : // to itself by this PBC then we don't
2364 : // generate any constraints
2365 20 : if (neigh_pt1.absolute_fuzzy_equals
2366 260 : (*e1, primary_hmin*TOLERANCE) &&
2367 : neigh_pt2.absolute_fuzzy_equals
2368 0 : (*e2, primary_hmin*TOLERANCE))
2369 120 : continue;
2370 :
2371 : // Otherwise we'll have a constraint in
2372 : // one direction or another
2373 240 : if (!primary_elem)
2374 20 : primary_elem = elem;
2375 :
2376 20 : const Elem * primary_neigh = primary_boundary_edge_neighbor
2377 240 : (neigh, neigh_pt1, neigh_pt2,
2378 : mesh.get_boundary_info(), edge_pairedids);
2379 :
2380 20 : libmesh_assert(primary_neigh);
2381 :
2382 240 : if (new_boundary_id == boundary_id)
2383 : {
2384 20 : main_neigh = primary_neigh;
2385 240 : main_pt1 = neigh_pt1;
2386 240 : main_pt2 = neigh_pt2;
2387 : }
2388 :
2389 : // If we have a one-element thick mesh,
2390 : // we'll need to sort our points to get a
2391 : // consistent ordering rule
2392 : //
2393 : // Use >= in this test to make sure that,
2394 : // for angular constraints, no node gets
2395 : // constrained to itself.
2396 240 : if (primary_neigh == primary_elem)
2397 : {
2398 0 : if (primary_pt1 > primary_pt2)
2399 0 : std::swap(primary_pt1, primary_pt2);
2400 0 : if (neigh_pt1 > neigh_pt2)
2401 0 : std::swap(neigh_pt1, neigh_pt2);
2402 :
2403 0 : if (neigh_pt2 >= primary_pt2)
2404 0 : continue;
2405 : }
2406 :
2407 : // Otherwise:
2408 : // Finer elements will get constrained in
2409 : // terms of coarser ones, not the other way
2410 : // around
2411 480 : if ((primary_neigh->level() > primary_elem->level()) ||
2412 :
2413 : // For equal-level elements, the one with
2414 : // higher id gets constrained in terms of
2415 : // the one with lower id
2416 440 : (primary_neigh->level() == primary_elem->level() &&
2417 40 : primary_neigh->id() > primary_elem->id()))
2418 120 : continue;
2419 :
2420 10 : primary_elem = primary_neigh;
2421 120 : primary_pt1 = neigh_pt1;
2422 120 : primary_pt2 = neigh_pt2;
2423 : }
2424 :
2425 160 : if (!primary_elem ||
2426 230 : primary_elem != main_neigh ||
2427 270 : primary_pt1 != main_pt1 ||
2428 10 : primary_pt2 != main_pt2)
2429 10 : continue;
2430 : }
2431 0 : else if (elem->is_face(n))
2432 : {
2433 : // If we have a one-element thick mesh,
2434 : // use the ordering of the face node and its
2435 : // periodic counterpart to determine what
2436 : // gets constrained
2437 0 : if (neigh == elem)
2438 : {
2439 : const Point neigh_pt =
2440 0 : periodic->get_corresponding_pos(my_node);
2441 0 : if (neigh_pt > my_node)
2442 0 : continue;
2443 : }
2444 :
2445 : // Otherwise:
2446 : // Finer elements will get constrained in
2447 : // terms of coarser ones, not the other way
2448 : // around
2449 0 : if ((neigh->level() > elem->level()) ||
2450 :
2451 : // For equal-level elements, the one with
2452 : // higher id gets constrained in terms of
2453 : // the one with lower id
2454 0 : (neigh->level() == elem->level() &&
2455 0 : neigh->id() > elem->id()))
2456 0 : continue;
2457 : }
2458 :
2459 : // If we made it here without hitting a continue
2460 : // statement, then we're at a node whose dofs
2461 : // should be constrained by this element's
2462 : // calculations.
2463 : const unsigned int n_comp =
2464 9478 : my_node.n_comp(sys_number, variable_number);
2465 :
2466 19000 : for (unsigned int i=0; i != n_comp; ++i)
2467 : my_constrained_dofs.insert
2468 7628 : (my_node.dof_number
2469 9522 : (sys_number, variable_number, i));
2470 : }
2471 :
2472 : // FIXME: old code for disambiguating periodic BCs:
2473 : // this is not threadsafe nor safe to run on a
2474 : // non-serialized mesh.
2475 : /*
2476 : std::vector<bool> recursive_constraint(n_side_dofs, false);
2477 :
2478 : for (unsigned int is = 0; is != n_side_dofs; ++is)
2479 : {
2480 : const unsigned int i = neigh_side_dofs[is];
2481 : const dof_id_type their_dof_g = neigh_dof_indices[i];
2482 : libmesh_assert_not_equal_to (their_dof_g, DofObject::invalid_id);
2483 :
2484 : {
2485 : Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
2486 :
2487 : if (!dof_map.is_constrained_dof(their_dof_g))
2488 : continue;
2489 : }
2490 :
2491 : DofConstraintRow & their_constraint_row =
2492 : constraints[their_dof_g].first;
2493 :
2494 : for (unsigned int js = 0; js != n_side_dofs; ++js)
2495 : {
2496 : const unsigned int j = my_side_dofs[js];
2497 : const dof_id_type my_dof_g = my_dof_indices[j];
2498 : libmesh_assert_not_equal_to (my_dof_g, DofObject::invalid_id);
2499 :
2500 : if (their_constraint_row.count(my_dof_g))
2501 : recursive_constraint[js] = true;
2502 : }
2503 : }
2504 : */
2505 :
2506 54936 : for (unsigned int js = 0; js != n_side_dofs; ++js)
2507 : {
2508 : // FIXME: old code path
2509 : // if (recursive_constraint[js])
2510 : // continue;
2511 :
2512 41504 : const unsigned int j = my_side_dofs[js];
2513 47916 : const dof_id_type my_dof_g = my_dof_indices[j];
2514 6412 : libmesh_assert_not_equal_to (my_dof_g, DofObject::invalid_id);
2515 :
2516 : // FIXME: new code path
2517 29358 : if (!my_constrained_dofs.count(my_dof_g))
2518 32254 : continue;
2519 :
2520 : DofConstraintRow * constraint_row;
2521 :
2522 : // we may be running constraint methods concurrently
2523 : // on multiple threads, so we need a lock to
2524 : // ensure that this constraint is "ours"
2525 : {
2526 1894 : Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
2527 :
2528 9522 : if (dof_map.is_constrained_dof(my_dof_g))
2529 81 : continue;
2530 :
2531 9250 : constraint_row = &(constraints[my_dof_g]);
2532 1813 : libmesh_assert(constraint_row->empty());
2533 : }
2534 :
2535 37506 : for (unsigned int is = 0; is != n_side_dofs; ++is)
2536 : {
2537 28256 : const unsigned int i = neigh_side_dofs[is];
2538 28256 : const dof_id_type their_dof_g = neigh_dof_indices[i];
2539 4439 : libmesh_assert_not_equal_to (their_dof_g, DofObject::invalid_id);
2540 :
2541 : // Periodic constraints should never be
2542 : // self-constraints
2543 : // libmesh_assert_not_equal_to (their_dof_g, my_dof_g);
2544 :
2545 28256 : const Real their_dof_value = Ue[is](js);
2546 :
2547 28256 : if (their_dof_g == my_dof_g)
2548 : {
2549 0 : libmesh_assert_less (std::abs(their_dof_value-1.), 1.e-5);
2550 0 : for (unsigned int k = 0; k != n_side_dofs; ++k)
2551 0 : libmesh_assert(k == is || std::abs(Ue[k](js)) < 1.e-5);
2552 17662 : continue;
2553 0 : }
2554 :
2555 28256 : if (std::abs(their_dof_value) < 10*TOLERANCE)
2556 15484 : continue;
2557 :
2558 10594 : if(!periodic->has_transformation_matrix())
2559 : {
2560 1865 : constraint_row->emplace(their_dof_g, their_dof_value);
2561 : }
2562 : else
2563 : {
2564 : // In this case the current variable is constrained in terms of other variables.
2565 : // We assume that all variables in this constraint have the same FE type (this
2566 : // is asserted below), and hence we can create the constraint row contribution
2567 : // by multiplying their_dof_value by the corresponding row of the transformation
2568 : // matrix.
2569 :
2570 4752 : const std::set<unsigned int> & variables = periodic->get_variables();
2571 4752 : neigh_dof_indices_all_variables.resize(variables.size());
2572 396 : unsigned int index = 0;
2573 19008 : for(unsigned int other_var : variables)
2574 : {
2575 1188 : libmesh_assert_msg(base_fe_type == dof_map.variable_type(other_var), "FE types must match for all variables involved in constraint");
2576 :
2577 14256 : Real var_weighting = periodic->get_transformation_matrix()(variable_number, other_var);
2578 14256 : constraint_row->emplace(neigh_dof_indices_all_variables[index][i],
2579 14256 : var_weighting*their_dof_value);
2580 14256 : index++;
2581 : }
2582 : }
2583 :
2584 : }
2585 : }
2586 8240 : }
2587 : // p refinement constraints:
2588 : // constrain dofs shared between
2589 : // active elements and neighbors with
2590 : // lower polynomial degrees
2591 : #ifdef LIBMESH_ENABLE_AMR
2592 : const unsigned int min_p_level =
2593 17820 : neigh->min_p_level_by_neighbor(elem, elem->p_level());
2594 17820 : if (min_p_level < elem->p_level())
2595 : {
2596 : // Adaptive p refinement of non-hierarchic bases will
2597 : // require more coding
2598 0 : libmesh_assert(my_fe->is_hierarchic());
2599 0 : dof_map.constrain_p_dofs(variable_number, elem,
2600 : s, min_p_level);
2601 : }
2602 : #endif // #ifdef LIBMESH_ENABLE_AMR
2603 : }
2604 : }
2605 331527 : }
2606 :
2607 : #endif // LIBMESH_ENABLE_PERIODIC
2608 :
2609 : // ------------------------------------------------------------
2610 : // Explicit instantiations
2611 : template class LIBMESH_EXPORT FEGenericBase<Real>;
2612 : template class LIBMESH_EXPORT FEGenericBase<RealGradient>;
2613 :
2614 : } // namespace libMesh
|