LCOV - code coverage report
Current view: top level - src/fe - fe_base.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4541 (8e443e) with base 41ae1d Lines: 690 1188 58.1 %
Date: 2026-09-07 22:42:35 Functions: 14 36 38.9 %
Legend: Lines: hit not hit

          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      320587 :       if ((pt_neighbor->level() > primary->level()) ||
      80      275864 :           (pt_neighbor->level() == primary->level() &&
      81      157883 :            pt_neighbor->id() < primary->id()))
      82       62259 :         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       14098 :       bool vertex_on_periodic_side = false;
      88      245832 :       for (auto ns : pt_neighbor->side_index_range())
      89             :         {
      90      231079 :           boundary_info.boundary_ids (pt_neighbor, ns, bc_ids);
      91             : 
      92       33679 :           bool on_relevant_boundary = false;
      93      462158 :           for (const auto & id : boundary_ids)
      94      231079 :             if (std::find(bc_ids.begin(), bc_ids.end(), id) != bc_ids.end())
      95       14051 :               on_relevant_boundary = true;
      96             : 
      97      231079 :           if (!on_relevant_boundary)
      98      112540 :             continue;
      99             : 
     100       98898 :           pt_neighbor->build_side_ptr(periodic_side, ns);
     101       98898 :           if (!periodic_side->contains_point(p))
     102         205 :             continue;
     103             : 
     104       14037 :           vertex_on_periodic_side = true;
     105       14037 :           break;
     106             :         }
     107             : 
     108       99311 :       if (vertex_on_periodic_side)
     109       98682 :         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     5288384 : FEGenericBase<Real>::build (const unsigned int dim,
     192             :                             const FEType & fet)
     193             : {
     194     5288384 :   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      439024 :     case 1:
     248             :       {
     249      439024 :         switch (fet.family)
     250             :           {
     251           0 :           case CLOUGH:
     252           0 :             return std::make_unique<FE<1,CLOUGH>>(fet);
     253             : 
     254      398658 :           case HERMITE:
     255      398658 :             return std::make_unique<FE<1,HERMITE>>(fet);
     256             : 
     257        3812 :           case LAGRANGE:
     258        3812 :             return std::make_unique<FE<1,LAGRANGE>>(fet);
     259             : 
     260        2343 :           case L2_LAGRANGE:
     261        2343 :             return std::make_unique<FE<1,L2_LAGRANGE>>(fet);
     262             : 
     263       11495 :           case HIERARCHIC:
     264       11495 :             return std::make_unique<FE<1,HIERARCHIC>>(fet);
     265             : 
     266        3905 :           case L2_HIERARCHIC:
     267        3905 :             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        3947 :           case MONOMIAL:
     273        3947 :             return std::make_unique<FE<1,MONOMIAL>>(fet);
     274             : 
     275             : #ifdef LIBMESH_ENABLE_HIGHER_ORDER_SHAPES
     276        3124 :           case SZABAB:
     277        3124 :             return std::make_unique<FE<1,SZABAB>>(fet);
     278             : 
     279        3124 :           case BERNSTEIN:
     280        3124 :             return std::make_unique<FE<1,BERNSTEIN>>(fet);
     281             : 
     282        1942 :           case RATIONAL_BERNSTEIN:
     283        1942 :             return std::make_unique<FE<1,RATIONAL_BERNSTEIN>>(fet);
     284             : #endif
     285             : 
     286        3124 :           case XYZ:
     287        3124 :             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     1683412 :     case 2:
     300             :       {
     301     1683412 :         switch (fet.family)
     302             :           {
     303       69954 :           case CLOUGH:
     304       69954 :             return std::make_unique<FE<2,CLOUGH>>(fet);
     305             : 
     306       35468 :           case HERMITE:
     307       35468 :             return std::make_unique<FE<2,HERMITE>>(fet);
     308             : 
     309      536170 :           case LAGRANGE:
     310      536170 :             return std::make_unique<FE<2,LAGRANGE>>(fet);
     311             : 
     312       54674 :           case L2_LAGRANGE:
     313       54674 :             return std::make_unique<FE<2,L2_LAGRANGE>>(fet);
     314             : 
     315      539625 :           case HIERARCHIC:
     316      539625 :             return std::make_unique<FE<2,HIERARCHIC>>(fet);
     317             : 
     318      101097 :           case L2_HIERARCHIC:
     319      101097 :             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       19769 :           case MONOMIAL:
     325       19769 :             return std::make_unique<FE<2,MONOMIAL>>(fet);
     326             : 
     327             : #ifdef LIBMESH_ENABLE_HIGHER_ORDER_SHAPES
     328       13295 :           case SZABAB:
     329       13295 :             return std::make_unique<FE<2,SZABAB>>(fet);
     330             : 
     331       12911 :           case BERNSTEIN:
     332       12911 :             return std::make_unique<FE<2,BERNSTEIN>>(fet);
     333             : 
     334      117778 :           case RATIONAL_BERNSTEIN:
     335      117778 :             return std::make_unique<FE<2,RATIONAL_BERNSTEIN>>(fet);
     336             : #endif
     337             : 
     338       34948 :           case XYZ:
     339       34948 :             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     3165936 :     case 3:
     355             :       {
     356     3165936 :         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        1021 :           case HERMITE:
     362        1021 :             return std::make_unique<FE<3,HERMITE>>(fet);
     363             : 
     364     2463668 :           case LAGRANGE:
     365     2463668 :             return std::make_unique<FE<3,LAGRANGE>>(fet);
     366             : 
     367       86153 :           case L2_LAGRANGE:
     368       86153 :             return std::make_unique<FE<3,L2_LAGRANGE>>(fet);
     369             : 
     370       60515 :           case HIERARCHIC:
     371       60515 :             return std::make_unique<FE<3,HIERARCHIC>>(fet);
     372             : 
     373       90413 :           case L2_HIERARCHIC:
     374       90413 :             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       27650 :           case MONOMIAL:
     380       27650 :             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       23707 :           case BERNSTEIN:
     387       23707 :             return std::make_unique<FE<3,BERNSTEIN>>(fet);
     388             : 
     389       13256 :           case RATIONAL_BERNSTEIN:
     390       13256 :             return std::make_unique<FE<3,RATIONAL_BERNSTEIN>>(fet);
     391             : #endif
     392             : 
     393       94105 :           case XYZ:
     394       94105 :             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      574758 : FEGenericBase<RealGradient>::build (const unsigned int dim,
     414             :                                     const FEType & fet)
     415             : {
     416      574758 :   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      212076 :     case 2:
     466             :       {
     467      212076 :         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      114610 :           case RAVIART_THOMAS:
     488      114610 :             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      362682 :     case 3:
     498             :       {
     499      362682 :         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      303055 :           case RAVIART_THOMAS:
     520      303055 :             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         452 : FEGenericBase<Real>::build_InfFE (const unsigned int dim,
     547             :                                   const FEType & fet)
     548             : {
     549         452 :   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         452 :     case 3:
     682             :       {
     683         452 :         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         397 :           case JACOBI_20_00:
     689             :             {
     690         397 :               switch (fet.inf_map)
     691             :                 {
     692         397 :                 case CARTESIAN:
     693         397 :                   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   122871846 : 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    22022682 :   LOG_SCOPE("compute_shape_functions()", "FE");
     772             : 
     773   122871846 :   this->determine_calculations();
     774             : 
     775   122871846 :   if (calculate_phi)
     776   111439216 :     this->_fe_trans->map_phi(this->dim, elem, qp, (*this), this->phi, this->_add_p_level_in_reinit);
     777             : 
     778   122871846 :   if (calculate_dphi)
     779    88594246 :     this->_fe_trans->map_dphi(this->dim, elem, qp, (*this), this->dphi,
     780    81137604 :                               this->dphidx, this->dphidy, this->dphidz);
     781             : 
     782             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     783   122871846 :   if (calculate_d2phi)
     784     7132647 :     this->_fe_trans->map_d2phi(this->dim, qp, (*this), this->d2phi,
     785     6608235 :                                this->d2phidx2, this->d2phidxdy, this->d2phidxdz,
     786     6608235 :                                this->d2phidy2, this->d2phidydz, this->d2phidz2);
     787             : #endif //LIBMESH_ENABLE_SECOND_DERIVATIVES
     788             : 
     789             :   // Only compute curl for vector-valued elements
     790     9954519 :   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     9954519 :   if (calculate_div_phi && TypesEqual<OutputType,RealGradient>::value)
     795     1476125 :     this->_fe_trans->map_div(this->dim, elem, qp, (*this), this->div_phi);
     796   122871846 : }
     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             :   // The integral positivity condition on the dual basis (Popp, Wohlmuth, Gee and Wall, SIAM J. Sci.
     825             :   // Comput. 34(4):B421-B446, 2012, Eq. (4.2)) fails on QUAD8 and TRI6: D(k,k) = \int N_k is -1/3 at a
     826             :   // QUAD8 corner and exactly 0 at a TRI6 vertex (their Eqs. (4.3) and (4.4)), the latter leaving those
     827             :   // dual shape functions identically zero. Biorthogonalize instead against the locally quadratic
     828             :   // transformed basis Ntilde = T N of their Sec. 4.4.1, in which each vertex absorbs a fraction alpha
     829             :   // of its adjacent mid-edge shapes. That replaces D by T^-1 diag(T d), which for this T is the sparse
     830             :   // update below. alpha = 1/5 is recommended there, and makes the weights strictly positive (QUAD8
     831             :   // 1/5 and 4/5, TRI6 1/15 and 1/10) while preserving the partition of unity. T = I elsewhere.
     832        6477 :   if (_elem && (_elem->type() == TRI6 || _elem->type() == QUAD8) &&
     833        7041 :       get_family() == LAGRANGE && sz == _elem->n_nodes())
     834             :     {
     835           6 :       const Real alpha = Real(1)/5;
     836             :       // Mid-edge nodes are the trailing indices, and only vertex entries are written, so D(m,m) here
     837             :       // is never a value an earlier iteration modified.
     838         352 :       for (const auto m : make_range(_elem->n_vertices(), sz))
     839         872 :         for (const auto v : make_range(_elem->n_second_order_adjacent_vertices(m)))
     840             :           {
     841         456 :             const auto vertex = _elem->second_order_adjacent_vertex(m, v);
     842         456 :             D(vertex, vertex) += alpha*D(m,m);
     843         496 :             D(vertex, m)      -= alpha*D(m,m);
     844             :           }
     845             :     }
     846             : 
     847             :   // dual_coeff = A^-1*D
     848      108576 :   for (const auto j : index_range(phi_vals))
     849             :   {
     850      102158 :     DenseVector<Real> Dcol(sz), coeffcol(sz);
     851     3507468 :     for (const auto i : index_range(phi_vals))
     852     3904102 :       Dcol(i) = D(i, j);
     853      102158 :     A.cholesky_solve(Dcol, coeffcol);
     854             : 
     855     3507468 :     for (const auto row : index_range(phi_vals))
     856     3904102 :       dual_coeff(row, j)=coeffcol(row);
     857             :   }
     858        6418 : }
     859             : 
     860             : template <>
     861        6418 : void FEGenericBase<Real>::compute_dual_shape_functions ()
     862             : {
     863             :   // Start logging the shape function computation
     864         862 :   LOG_SCOPE("compute_dual_shape_functions()", "FE");
     865             : 
     866             :   // The dual coeffs matrix should have the same size as phi
     867         431 :   libmesh_assert(dual_coeff.m() == phi.size());
     868         431 :   libmesh_assert(dual_coeff.n() == phi.size());
     869             : 
     870             :   // initialize dual basis
     871      108576 :   for (const auto j : index_range(phi))
     872     6046176 :     for (const auto qp : index_range(phi[j]))
     873             :     {
     874     6363053 :       dual_phi[j][qp] = 0;
     875     5944018 :       if (calculate_dphi)
     876     1256625 :         dual_dphi[j][qp] = 0;
     877             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     878     5944018 :       if (calculate_d2phi)
     879     1248093 :         dual_d2phi[j][qp] = 0;
     880             : #endif
     881             :     }
     882             : 
     883             :   // compute dual basis
     884      108576 :   for (const auto j : index_range(phi))
     885     3507468 :     for (const auto i : index_range(phi))
     886   303768664 :       for (const auto qp : index_range(phi[j]))
     887             :       {
     888   391212614 :         dual_phi[j][qp] += dual_coeff(i, j) * phi[i][qp];
     889   300363354 :         if (calculate_dphi)
     890   136267362 :           dual_dphi[j][qp] += dual_coeff(i, j) * dphi[i][qp];
     891             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     892   300363354 :         if (calculate_d2phi)
     893   667700859 :           dual_d2phi[j][qp] += dual_coeff(i, j) * d2phi[i][qp];
     894             : #endif
     895             :       }
     896        6418 : }
     897             : 
     898             : template <typename OutputType>
     899           0 : void FEGenericBase<OutputType>::print_phi(std::ostream & os) const
     900             : {
     901           0 :   for (auto i : index_range(phi))
     902           0 :     for (auto j : index_range(phi[i]))
     903           0 :       os << " phi[" << i << "][" << j << "]=" << phi[i][j] << std::endl;
     904           0 : }
     905             : 
     906             : template <typename OutputType>
     907           0 : void FEGenericBase<OutputType>::print_dual_phi(std::ostream & os) const
     908             : {
     909           0 :   for (auto i : index_range(dual_phi))
     910           0 :     for (auto j : index_range(dual_phi[i]))
     911           0 :       os << " dual_phi[" << i << "][" << j << "]=" << dual_phi[i][j] << std::endl;
     912           0 : }
     913             : 
     914             : 
     915             : 
     916             : 
     917             : template <typename OutputType>
     918           0 : void FEGenericBase<OutputType>::print_dphi(std::ostream & os) const
     919             : {
     920           0 :   for (auto i : index_range(dphi))
     921           0 :     for (auto j : index_range(dphi[i]))
     922           0 :       os << " dphi[" << i << "][" << j << "]=" << dphi[i][j];
     923           0 : }
     924             : 
     925             : template <typename OutputType>
     926           0 : void FEGenericBase<OutputType>::print_dual_dphi(std::ostream & os) const
     927             : {
     928           0 :   for (auto i : index_range(dphi))
     929           0 :     for (auto j : index_range(dphi[i]))
     930           0 :       os << " dual_dphi[" << i << "][" << j << "]=" << dual_dphi[i][j];
     931           0 : }
     932             : 
     933             : 
     934             : 
     935             : template <typename OutputType>
     936   344153713 : void FEGenericBase<OutputType>::determine_calculations()
     937             : {
     938   344153713 :   this->calculations_started = true;
     939             : 
     940             :   // If the user did not explicitly pre-request something (or nothing)
     941             :   // to be computed, then we throw an error here.
     942    29883673 :   bool requested_ok =
     943   334794103 :     this->calculate_nothing || this->calculate_phi || this->calculate_dphi ||
     944   375781790 :     this->calculate_dphiref || this->calculate_curl_phi || this->calculate_div_phi ||
     945      366629 :     this->calculate_map;
     946             : 
     947             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     948    29883673 :   requested_ok = requested_ok || this->calculate_d2phi;
     949             : #endif
     950             : 
     951    29883673 :   libmesh_error_msg_if(
     952             :     !requested_ok,
     953             :     "You must call one or more of the FE accessors "
     954             :     "(e.g. get_phi(), get_dphi(), get_nothing()) "
     955             :     "_before_ calling reinit()!");
     956             : 
     957             :   // Request whichever terms are necessary from the FEMap
     958   344153713 :   if (this->calculate_phi)
     959   320446643 :     this->_fe_trans->init_map_phi(*this);
     960             : 
     961   344153713 :   if (this->calculate_dphiref)
     962   225580817 :     this->_fe_trans->init_map_dphi(*this);
     963             : 
     964             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     965   344153713 :   if (this->calculate_d2phi)
     966    58343728 :     this->_fe_trans->init_map_d2phi(*this);
     967             : #endif //LIBMESH_ENABLE_SECOND_DERIVATIVES
     968   344153713 : }
     969             : 
     970             : 
     971             : 
     972             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     973             : 
     974             : 
     975             : template <typename OutputType>
     976           0 : void FEGenericBase<OutputType>::print_d2phi(std::ostream & os) const
     977             : {
     978           0 :   for (auto i : index_range(dphi))
     979           0 :     for (auto j : index_range(dphi[i]))
     980           0 :       os << " d2phi[" << i << "][" << j << "]=" << d2phi[i][j];
     981           0 : }
     982             : 
     983             : template <typename OutputType>
     984           0 : void FEGenericBase<OutputType>::print_dual_d2phi(std::ostream & os) const
     985             : {
     986           0 :   for (auto i : index_range(dual_d2phi))
     987           0 :     for (auto j : index_range(dual_d2phi[i]))
     988           0 :       os << " dual_d2phi[" << i << "][" << j << "]=" << dual_d2phi[i][j];
     989           0 : }
     990             : 
     991             : #endif
     992             : 
     993             : 
     994             : 
     995             : #ifdef LIBMESH_ENABLE_AMR
     996             : 
     997             : template <typename OutputType>
     998             : void
     999           0 : FEGenericBase<OutputType>::coarsened_dof_values(const NumericVector<Number> & old_vector,
    1000             :                                                 const DofMap & dof_map,
    1001             :                                                 const Elem * elem,
    1002             :                                                 DenseVector<Number> & Ue,
    1003             :                                                 const unsigned int var,
    1004             :                                                 const bool use_old_dof_indices)
    1005             : {
    1006             :   // Side/edge local DOF indices
    1007           0 :   std::vector<unsigned int> new_side_dofs, old_side_dofs;
    1008             : 
    1009             :   // FIXME: what about 2D shells in 3D space?
    1010           0 :   unsigned int dim = elem->dim();
    1011             : 
    1012             :   // Cache n_children(); it's a virtual call but it's const.
    1013           0 :   const unsigned int n_children = elem->n_children();
    1014             : 
    1015             :   // We use local FE objects for now
    1016             :   // FIXME: we should use more, external objects instead for efficiency
    1017           0 :   const FEType & base_fe_type = dof_map.variable_type(var);
    1018           0 :   std::unique_ptr<FEGenericBase<OutputShape>> fe
    1019             :     (FEGenericBase<OutputShape>::build(dim, base_fe_type));
    1020           0 :   std::unique_ptr<FEGenericBase<OutputShape>> fe_coarse
    1021             :     (FEGenericBase<OutputShape>::build(dim, base_fe_type));
    1022             : 
    1023           0 :   std::unique_ptr<QBase> qrule     (base_fe_type.default_quadrature_rule(dim));
    1024           0 :   std::unique_ptr<QBase> qedgerule (base_fe_type.default_quadrature_rule(1));
    1025           0 :   std::unique_ptr<QBase> qsiderule (base_fe_type.default_quadrature_rule(dim-1));
    1026           0 :   std::vector<Point> coarse_qpoints;
    1027             : 
    1028             :   // The values of the shape functions at the quadrature
    1029             :   // points
    1030           0 :   const std::vector<std::vector<OutputShape>> & phi_values =
    1031             :     fe->get_phi();
    1032           0 :   const std::vector<std::vector<OutputShape>> & phi_coarse =
    1033             :     fe_coarse->get_phi();
    1034             : 
    1035             :   // The gradients of the shape functions at the quadrature
    1036             :   // points on the child element.
    1037           0 :   const std::vector<std::vector<OutputGradient>> * dphi_values =
    1038             :     nullptr;
    1039           0 :   const std::vector<std::vector<OutputGradient>> * dphi_coarse =
    1040             :     nullptr;
    1041             : 
    1042           0 :   const FEContinuity cont = fe->get_continuity();
    1043             : 
    1044           0 :   if (cont == C_ONE)
    1045             :     {
    1046             :       const std::vector<std::vector<OutputGradient>> &
    1047           0 :         ref_dphi_values = fe->get_dphi();
    1048           0 :       dphi_values = &ref_dphi_values;
    1049             :       const std::vector<std::vector<OutputGradient>> &
    1050           0 :         ref_dphi_coarse = fe_coarse->get_dphi();
    1051           0 :       dphi_coarse = &ref_dphi_coarse;
    1052             :     }
    1053             : 
    1054             :   // The Jacobian * quadrature weight at the quadrature points
    1055           0 :   const std::vector<Real> & JxW =
    1056           0 :     fe->get_JxW();
    1057             : 
    1058             :   // The XYZ locations of the quadrature points on the
    1059             :   // child element
    1060           0 :   const std::vector<Point> & xyz_values =
    1061           0 :     fe->get_xyz();
    1062             : 
    1063             :   // Number of nodes on parent element
    1064           0 :   const unsigned int n_nodes = elem->n_nodes();
    1065             : 
    1066             :   // Number of dofs on parent element
    1067             :   const unsigned int new_n_dofs =
    1068           0 :     FEInterface::n_dofs(base_fe_type, elem->max_descendant_p_level(), elem);
    1069             : 
    1070             :   // Fixed vs. free DoFs on edge/face projections
    1071           0 :   std::vector<char> dof_is_fixed(new_n_dofs, false); // bools
    1072           0 :   std::vector<int> free_dof(new_n_dofs, 0);
    1073             : 
    1074           0 :   DenseMatrix<Real> Ke;
    1075           0 :   DenseVector<Number> Fe;
    1076           0 :   Ue.resize(new_n_dofs); Ue.zero();
    1077             : 
    1078             : 
    1079             :   // When coarsening, in general, we need a series of
    1080             :   // projections to ensure a unique and continuous
    1081             :   // solution.  We start by interpolating nodes, then
    1082             :   // hold those fixed and project edges, then
    1083             :   // hold those fixed and project faces, then
    1084             :   // hold those fixed and project interiors
    1085             : 
    1086             :   // Copy node values first
    1087             :   {
    1088           0 :     std::vector<dof_id_type> node_dof_indices;
    1089           0 :     if (use_old_dof_indices)
    1090           0 :       dof_map.old_dof_indices (elem, node_dof_indices, var);
    1091             :     else
    1092           0 :       dof_map.dof_indices (elem, node_dof_indices, var);
    1093             : 
    1094           0 :     unsigned int current_dof = 0;
    1095           0 :     for (unsigned int n=0; n!= n_nodes; ++n)
    1096             :       {
    1097             :         // FIXME: this should go through the DofMap,
    1098             :         // not duplicate dof_indices code badly!
    1099             :         const unsigned int my_nc =
    1100           0 :           FEInterface::n_dofs_at_node (base_fe_type, elem->max_descendant_p_level(), elem, n);
    1101           0 :         if (!elem->is_vertex(n))
    1102             :           {
    1103           0 :             current_dof += my_nc;
    1104           0 :             continue;
    1105             :           }
    1106             : 
    1107             :         // We're assuming here that child n shares vertex n,
    1108             :         // which is wrong on non-simplices right now
    1109             :         // ... but this code isn't necessary except on elements
    1110             :         // where p refinement creates more vertex dofs; we have
    1111             :         // no such elements yet.
    1112           0 :         int extra_order = 0;
    1113             :         // if (elem->child_ptr(n)->p_level() < elem->p_level())
    1114             :         //   extra_order = elem->child_ptr(n)->p_level();
    1115             :         const unsigned int nc =
    1116           0 :           FEInterface::n_dofs_at_node (base_fe_type, extra_order, elem, n);
    1117           0 :         for (unsigned int i=0; i!= nc; ++i)
    1118             :           {
    1119           0 :             Ue(current_dof) =
    1120           0 :               old_vector(node_dof_indices[current_dof]);
    1121           0 :             dof_is_fixed[current_dof] = true;
    1122           0 :             current_dof++;
    1123             :           }
    1124             :       }
    1125             :   }
    1126             : 
    1127           0 :   FEType fe_type = base_fe_type, temp_fe_type;
    1128           0 :   fe_type.order = fe_type.order + elem->max_descendant_p_level();
    1129             : 
    1130             :   // In 3D, project any edge values next
    1131           0 :   if (dim > 2 && cont != DISCONTINUOUS)
    1132           0 :     for (auto e : elem->edge_index_range())
    1133             :       {
    1134           0 :         FEInterface::dofs_on_edge(elem, dim, fe_type,
    1135             :                                   e, new_side_dofs);
    1136             : 
    1137             :         const unsigned int n_new_side_dofs =
    1138           0 :           cast_int<unsigned int>(new_side_dofs.size());
    1139             : 
    1140             :         // Some edge dofs are on nodes and already
    1141             :         // fixed, others are free to calculate
    1142           0 :         unsigned int free_dofs = 0;
    1143           0 :         for (unsigned int i=0; i != n_new_side_dofs; ++i)
    1144           0 :           if (!dof_is_fixed[new_side_dofs[i]])
    1145           0 :             free_dof[free_dofs++] = i;
    1146           0 :         Ke.resize (free_dofs, free_dofs); Ke.zero();
    1147           0 :         Fe.resize (free_dofs); Fe.zero();
    1148             :         // The new edge coefficients
    1149           0 :         DenseVector<Number> Uedge(free_dofs);
    1150             : 
    1151             :         // Add projection terms from each child sharing
    1152             :         // this edge
    1153           0 :         for (unsigned int c=0; c != n_children; ++c)
    1154             :           {
    1155           0 :             if (!elem->is_child_on_edge(c,e))
    1156           0 :               continue;
    1157           0 :             const Elem * child = elem->child_ptr(c);
    1158             : 
    1159           0 :             std::vector<dof_id_type> child_dof_indices;
    1160           0 :             if (use_old_dof_indices)
    1161           0 :               dof_map.old_dof_indices (child,
    1162             :                                        child_dof_indices, var);
    1163             :             else
    1164           0 :               dof_map.dof_indices (child,
    1165             :                                    child_dof_indices, var);
    1166             :             const unsigned int child_n_dofs =
    1167             :               cast_int<unsigned int>
    1168           0 :               (child_dof_indices.size());
    1169             : 
    1170           0 :             temp_fe_type = base_fe_type;
    1171           0 :             temp_fe_type.order = temp_fe_type.order + child->p_level();
    1172             : 
    1173           0 :             FEInterface::dofs_on_edge(child, dim,
    1174             :                                       temp_fe_type, e, old_side_dofs);
    1175             : 
    1176             :             // Initialize both child and parent FE data
    1177             :             // on the child's edge
    1178           0 :             fe->attach_quadrature_rule (qedgerule.get());
    1179           0 :             fe->edge_reinit (child, e);
    1180           0 :             const unsigned int n_qp = qedgerule->n_points();
    1181             : 
    1182           0 :             FEMap::inverse_map (dim, elem, xyz_values,
    1183             :                                 coarse_qpoints);
    1184             : 
    1185           0 :             fe_coarse->reinit(elem, &coarse_qpoints);
    1186             : 
    1187             :             // Loop over the quadrature points
    1188           0 :             for (unsigned int qp=0; qp<n_qp; qp++)
    1189             :               {
    1190             :                 // solution value at the quadrature point
    1191           0 :                 OutputNumber fineval = libMesh::zero;
    1192             :                 // solution grad at the quadrature point
    1193           0 :                 OutputNumberGradient finegrad;
    1194             : 
    1195             :                 // Sum the solution values * the DOF
    1196             :                 // values at the quadrature point to
    1197             :                 // get the solution value and gradient.
    1198           0 :                 for (unsigned int i=0; i<child_n_dofs;
    1199             :                      i++)
    1200             :                   {
    1201           0 :                     fineval +=
    1202           0 :                       (old_vector(child_dof_indices[i])*
    1203           0 :                        phi_values[i][qp]);
    1204           0 :                     if (cont == C_ONE)
    1205           0 :                       finegrad += (*dphi_values)[i][qp] *
    1206           0 :                         old_vector(child_dof_indices[i]);
    1207             :                   }
    1208             : 
    1209             :                 // Form edge projection matrix
    1210           0 :                 for (unsigned int sidei=0, freei=0; sidei != n_new_side_dofs; ++sidei)
    1211             :                   {
    1212           0 :                     unsigned int i = new_side_dofs[sidei];
    1213             :                     // fixed DoFs aren't test functions
    1214           0 :                     if (dof_is_fixed[i])
    1215           0 :                       continue;
    1216           0 :                     for (unsigned int sidej=0, freej=0; sidej != n_new_side_dofs; ++sidej)
    1217             :                       {
    1218           0 :                         unsigned int j =
    1219             :                           new_side_dofs[sidej];
    1220           0 :                         if (dof_is_fixed[j])
    1221           0 :                           Fe(freei) -=
    1222           0 :                             TensorTools::inner_product(phi_coarse[i][qp],
    1223           0 :                                                        phi_coarse[j][qp]) *
    1224           0 :                             JxW[qp] * Ue(j);
    1225             :                         else
    1226           0 :                           Ke(freei,freej) +=
    1227           0 :                             TensorTools::inner_product(phi_coarse[i][qp],
    1228           0 :                                                        phi_coarse[j][qp]) *
    1229             :                             JxW[qp];
    1230           0 :                         if (cont == C_ONE)
    1231             :                           {
    1232           0 :                             if (dof_is_fixed[j])
    1233           0 :                               Fe(freei) -=
    1234           0 :                                 TensorTools::inner_product((*dphi_coarse)[i][qp],
    1235           0 :                                                            (*dphi_coarse)[j][qp]) *
    1236           0 :                                 JxW[qp] * Ue(j);
    1237             :                             else
    1238           0 :                               Ke(freei,freej) +=
    1239           0 :                                 TensorTools::inner_product((*dphi_coarse)[i][qp],
    1240           0 :                                                            (*dphi_coarse)[j][qp]) *
    1241             :                                 JxW[qp];
    1242             :                           }
    1243           0 :                         if (!dof_is_fixed[j])
    1244           0 :                           freej++;
    1245             :                       }
    1246           0 :                     Fe(freei) += TensorTools::inner_product(phi_coarse[i][qp],
    1247           0 :                                                             fineval) * JxW[qp];
    1248           0 :                     if (cont == C_ONE)
    1249           0 :                       Fe(freei) +=
    1250           0 :                         TensorTools::inner_product(finegrad, (*dphi_coarse)[i][qp]) * JxW[qp];
    1251           0 :                     freei++;
    1252             :                   }
    1253             :               }
    1254             :           }
    1255           0 :         Ke.cholesky_solve(Fe, Uedge);
    1256             : 
    1257             :         // Transfer new edge solutions to element
    1258           0 :         for (unsigned int i=0; i != free_dofs; ++i)
    1259             :           {
    1260           0 :             Number & ui = Ue(new_side_dofs[free_dof[i]]);
    1261           0 :             libmesh_assert(std::abs(ui) < TOLERANCE ||
    1262             :                            std::abs(ui - Uedge(i)) < TOLERANCE);
    1263           0 :             ui = Uedge(i);
    1264           0 :             dof_is_fixed[new_side_dofs[free_dof[i]]] = true;
    1265             :           }
    1266             :       }
    1267             : 
    1268             :   // Project any side values (edges in 2D, faces in 3D)
    1269           0 :   if (dim > 1 && cont != DISCONTINUOUS)
    1270           0 :     for (auto s : elem->side_index_range())
    1271             :       {
    1272           0 :         FEInterface::dofs_on_side(elem, dim, fe_type,
    1273             :                                   s, new_side_dofs);
    1274             : 
    1275             :         const unsigned int n_new_side_dofs =
    1276           0 :           cast_int<unsigned int>(new_side_dofs.size());
    1277             : 
    1278             :         // Some side dofs are on nodes/edges and already
    1279             :         // fixed, others are free to calculate
    1280           0 :         unsigned int free_dofs = 0;
    1281           0 :         for (unsigned int i=0; i != n_new_side_dofs; ++i)
    1282           0 :           if (!dof_is_fixed[new_side_dofs[i]])
    1283           0 :             free_dof[free_dofs++] = i;
    1284           0 :         Ke.resize (free_dofs, free_dofs); Ke.zero();
    1285           0 :         Fe.resize (free_dofs); Fe.zero();
    1286             :         // The new side coefficients
    1287           0 :         DenseVector<Number> Uside(free_dofs);
    1288             : 
    1289             :         // Add projection terms from each child sharing
    1290             :         // this side
    1291           0 :         for (unsigned int c=0; c != n_children; ++c)
    1292             :           {
    1293           0 :             if (!elem->is_child_on_side(c,s))
    1294           0 :               continue;
    1295           0 :             const Elem * child = elem->child_ptr(c);
    1296             : 
    1297           0 :             std::vector<dof_id_type> child_dof_indices;
    1298           0 :             if (use_old_dof_indices)
    1299           0 :               dof_map.old_dof_indices (child,
    1300             :                                        child_dof_indices, var);
    1301             :             else
    1302           0 :               dof_map.dof_indices (child,
    1303             :                                    child_dof_indices, var);
    1304             :             const unsigned int child_n_dofs =
    1305             :               cast_int<unsigned int>
    1306           0 :               (child_dof_indices.size());
    1307             : 
    1308           0 :             temp_fe_type = base_fe_type;
    1309           0 :             temp_fe_type.order = temp_fe_type.order + child->p_level();
    1310             : 
    1311           0 :             FEInterface::dofs_on_side(child, dim,
    1312             :                                       temp_fe_type, s, old_side_dofs);
    1313             : 
    1314             :             // Initialize both child and parent FE data
    1315             :             // on the child's side
    1316           0 :             fe->attach_quadrature_rule (qsiderule.get());
    1317           0 :             fe->reinit (child, s);
    1318           0 :             const unsigned int n_qp = qsiderule->n_points();
    1319             : 
    1320           0 :             FEMap::inverse_map (dim, elem, xyz_values,
    1321             :                                 coarse_qpoints);
    1322             : 
    1323           0 :             fe_coarse->reinit(elem, &coarse_qpoints);
    1324             : 
    1325             :             // Loop over the quadrature points
    1326           0 :             for (unsigned int qp=0; qp<n_qp; qp++)
    1327             :               {
    1328             :                 // solution value at the quadrature point
    1329           0 :                 OutputNumber fineval = libMesh::zero;
    1330             :                 // solution grad at the quadrature point
    1331           0 :                 OutputNumberGradient finegrad;
    1332             : 
    1333             :                 // Sum the solution values * the DOF
    1334             :                 // values at the quadrature point to
    1335             :                 // get the solution value and gradient.
    1336           0 :                 for (unsigned int i=0; i<child_n_dofs;
    1337             :                      i++)
    1338             :                   {
    1339           0 :                     fineval +=
    1340           0 :                       old_vector(child_dof_indices[i]) *
    1341           0 :                       phi_values[i][qp];
    1342           0 :                     if (cont == C_ONE)
    1343           0 :                       finegrad += (*dphi_values)[i][qp] *
    1344           0 :                         old_vector(child_dof_indices[i]);
    1345             :                   }
    1346             : 
    1347             :                 // Form side projection matrix
    1348           0 :                 for (unsigned int sidei=0, freei=0; sidei != n_new_side_dofs; ++sidei)
    1349             :                   {
    1350           0 :                     unsigned int i = new_side_dofs[sidei];
    1351             :                     // fixed DoFs aren't test functions
    1352           0 :                     if (dof_is_fixed[i])
    1353           0 :                       continue;
    1354           0 :                     for (unsigned int sidej=0, freej=0; sidej != n_new_side_dofs; ++sidej)
    1355             :                       {
    1356           0 :                         unsigned int j =
    1357             :                           new_side_dofs[sidej];
    1358           0 :                         if (dof_is_fixed[j])
    1359           0 :                           Fe(freei) -=
    1360           0 :                             TensorTools::inner_product(phi_coarse[i][qp],
    1361           0 :                                                        phi_coarse[j][qp]) *
    1362           0 :                             JxW[qp] * Ue(j);
    1363             :                         else
    1364           0 :                           Ke(freei,freej) +=
    1365           0 :                             TensorTools::inner_product(phi_coarse[i][qp],
    1366           0 :                                                        phi_coarse[j][qp]) *
    1367             :                             JxW[qp];
    1368           0 :                         if (cont == C_ONE)
    1369             :                           {
    1370           0 :                             if (dof_is_fixed[j])
    1371           0 :                               Fe(freei) -=
    1372           0 :                                 TensorTools::inner_product((*dphi_coarse)[i][qp],
    1373           0 :                                                            (*dphi_coarse)[j][qp]) *
    1374           0 :                                 JxW[qp] * Ue(j);
    1375             :                             else
    1376           0 :                               Ke(freei,freej) +=
    1377           0 :                                 TensorTools::inner_product((*dphi_coarse)[i][qp],
    1378           0 :                                                            (*dphi_coarse)[j][qp]) *
    1379             :                                 JxW[qp];
    1380             :                           }
    1381           0 :                         if (!dof_is_fixed[j])
    1382           0 :                           freej++;
    1383             :                       }
    1384           0 :                     Fe(freei) += TensorTools::inner_product(fineval, phi_coarse[i][qp]) * JxW[qp];
    1385           0 :                     if (cont == C_ONE)
    1386           0 :                       Fe(freei) +=
    1387           0 :                         TensorTools::inner_product(finegrad, (*dphi_coarse)[i][qp]) * JxW[qp];
    1388           0 :                     freei++;
    1389             :                   }
    1390             :               }
    1391             :           }
    1392           0 :         Ke.cholesky_solve(Fe, Uside);
    1393             : 
    1394             :         // Transfer new side solutions to element
    1395           0 :         for (unsigned int i=0; i != free_dofs; ++i)
    1396             :           {
    1397           0 :             Number & ui = Ue(new_side_dofs[free_dof[i]]);
    1398           0 :             libmesh_assert(std::abs(ui) < TOLERANCE ||
    1399             :                            std::abs(ui - Uside(i)) < TOLERANCE);
    1400           0 :             ui = Uside(i);
    1401           0 :             dof_is_fixed[new_side_dofs[free_dof[i]]] = true;
    1402             :           }
    1403             :       }
    1404             : 
    1405             :   // Project the interior values, finally
    1406             : 
    1407             :   // Some interior dofs are on nodes/edges/sides and
    1408             :   // already fixed, others are free to calculate
    1409           0 :   unsigned int free_dofs = 0;
    1410           0 :   for (unsigned int i=0; i != new_n_dofs; ++i)
    1411           0 :     if (!dof_is_fixed[i])
    1412           0 :       free_dof[free_dofs++] = i;
    1413           0 :   Ke.resize (free_dofs, free_dofs); Ke.zero();
    1414           0 :   Fe.resize (free_dofs); Fe.zero();
    1415             :   // The new interior coefficients
    1416           0 :   DenseVector<Number> Uint(free_dofs);
    1417             : 
    1418             :   // Add projection terms from each child
    1419           0 :   for (auto & child : elem->child_ref_range())
    1420             :     {
    1421           0 :       std::vector<dof_id_type> child_dof_indices;
    1422           0 :       if (use_old_dof_indices)
    1423           0 :         dof_map.old_dof_indices (&child,
    1424             :                                  child_dof_indices, var);
    1425             :       else
    1426           0 :         dof_map.dof_indices (&child,
    1427             :                              child_dof_indices, var);
    1428             :       const unsigned int child_n_dofs =
    1429             :         cast_int<unsigned int>
    1430           0 :         (child_dof_indices.size());
    1431             : 
    1432             :       // Initialize both child and parent FE data
    1433             :       // on the child's quadrature points
    1434           0 :       fe->attach_quadrature_rule (qrule.get());
    1435           0 :       fe->reinit (&child);
    1436           0 :       const unsigned int n_qp = qrule->n_points();
    1437             : 
    1438           0 :       FEMap::inverse_map (dim, elem, xyz_values, coarse_qpoints);
    1439             : 
    1440           0 :       fe_coarse->reinit(elem, &coarse_qpoints);
    1441             : 
    1442             :       // Loop over the quadrature points
    1443           0 :       for (unsigned int qp=0; qp<n_qp; qp++)
    1444             :         {
    1445             :           // solution value at the quadrature point
    1446           0 :           OutputNumber fineval = libMesh::zero;
    1447             :           // solution grad at the quadrature point
    1448           0 :           OutputNumberGradient finegrad;
    1449             : 
    1450             :           // Sum the solution values * the DOF
    1451             :           // values at the quadrature point to
    1452             :           // get the solution value and gradient.
    1453           0 :           for (unsigned int i=0; i<child_n_dofs; i++)
    1454             :             {
    1455           0 :               fineval +=
    1456           0 :                 (old_vector(child_dof_indices[i]) *
    1457           0 :                  phi_values[i][qp]);
    1458           0 :               if (cont == C_ONE)
    1459           0 :                 finegrad += (*dphi_values)[i][qp] *
    1460           0 :                   old_vector(child_dof_indices[i]);
    1461             :             }
    1462             : 
    1463             :           // Form interior projection matrix
    1464           0 :           for (unsigned int i=0, freei=0;
    1465           0 :                i != new_n_dofs; ++i)
    1466             :             {
    1467             :               // fixed DoFs aren't test functions
    1468           0 :               if (dof_is_fixed[i])
    1469           0 :                 continue;
    1470           0 :               for (unsigned int j=0, freej=0; j !=
    1471             :                      new_n_dofs; ++j)
    1472             :                 {
    1473           0 :                   if (dof_is_fixed[j])
    1474           0 :                     Fe(freei) -=
    1475           0 :                       TensorTools::inner_product(phi_coarse[i][qp],
    1476           0 :                                                  phi_coarse[j][qp]) *
    1477           0 :                       JxW[qp] * Ue(j);
    1478             :                   else
    1479           0 :                     Ke(freei,freej) +=
    1480           0 :                       TensorTools::inner_product(phi_coarse[i][qp],
    1481           0 :                                                  phi_coarse[j][qp]) *
    1482             :                       JxW[qp];
    1483           0 :                   if (cont == C_ONE)
    1484             :                     {
    1485           0 :                       if (dof_is_fixed[j])
    1486           0 :                         Fe(freei) -=
    1487           0 :                           TensorTools::inner_product((*dphi_coarse)[i][qp],
    1488           0 :                                                      (*dphi_coarse)[j][qp]) *
    1489           0 :                           JxW[qp] * Ue(j);
    1490             :                       else
    1491           0 :                         Ke(freei,freej) +=
    1492           0 :                           TensorTools::inner_product((*dphi_coarse)[i][qp],
    1493           0 :                                                      (*dphi_coarse)[j][qp]) *
    1494             :                           JxW[qp];
    1495             :                     }
    1496           0 :                   if (!dof_is_fixed[j])
    1497           0 :                     freej++;
    1498             :                 }
    1499           0 :               Fe(freei) += TensorTools::inner_product(phi_coarse[i][qp], fineval) *
    1500             :                 JxW[qp];
    1501           0 :               if (cont == C_ONE)
    1502           0 :                 Fe(freei) += TensorTools::inner_product(finegrad, (*dphi_coarse)[i][qp]) * JxW[qp];
    1503           0 :               freei++;
    1504             :             }
    1505             :         }
    1506             :     }
    1507           0 :   Ke.cholesky_solve(Fe, Uint);
    1508             : 
    1509             :   // Transfer new interior solutions to element
    1510           0 :   for (unsigned int i=0; i != free_dofs; ++i)
    1511             :     {
    1512           0 :       Number & ui = Ue(free_dof[i]);
    1513           0 :       libmesh_assert(std::abs(ui) < TOLERANCE ||
    1514             :                      std::abs(ui - Uint(i)) < TOLERANCE);
    1515           0 :       ui = Uint(i);
    1516             :       // We should be fixing all dofs by now; no need to keep track of
    1517             :       // that unless we're debugging
    1518             : #ifndef NDEBUG
    1519           0 :       dof_is_fixed[free_dof[i]] = true;
    1520             : #endif
    1521             :     }
    1522             : 
    1523             : #ifndef NDEBUG
    1524             :   // Make sure every DoF got reached!
    1525           0 :   for (unsigned int i=0; i != new_n_dofs; ++i)
    1526           0 :     libmesh_assert(dof_is_fixed[i]);
    1527             : #endif
    1528           0 : }
    1529             : 
    1530             : 
    1531             : 
    1532             : template <typename OutputType>
    1533             : void
    1534           0 : FEGenericBase<OutputType>::coarsened_dof_values(const NumericVector<Number> & old_vector,
    1535             :                                                 const DofMap & dof_map,
    1536             :                                                 const Elem * elem,
    1537             :                                                 DenseVector<Number> & Ue,
    1538             :                                                 const bool use_old_dof_indices)
    1539             : {
    1540           0 :   Ue.resize(0);
    1541             : 
    1542           0 :   for (auto v : make_range(dof_map.n_variables()))
    1543             :     {
    1544           0 :       DenseVector<Number> Usub;
    1545             : 
    1546           0 :       coarsened_dof_values(old_vector, dof_map, elem, Usub,
    1547             :                            v, use_old_dof_indices);
    1548             : 
    1549           0 :       Ue.append (Usub);
    1550             :     }
    1551           0 : }
    1552             : 
    1553             : 
    1554             : 
    1555             : template <typename OutputType>
    1556             : void
    1557      675294 : FEGenericBase<OutputType>::compute_proj_constraints (DofConstraints & constraints,
    1558             :                                                      DofMap & dof_map,
    1559             :                                                      const unsigned int variable_number,
    1560             :                                                      const Elem * elem)
    1561             : {
    1562       56747 :   libmesh_assert(elem);
    1563             : 
    1564      675294 :   const unsigned int Dim = elem->dim();
    1565             : 
    1566             :   // Only constrain elements in 2,3D.
    1567      675294 :   if (Dim == 1)
    1568      108037 :     return;
    1569             : 
    1570             :   // Only constrain active elements with this method
    1571       56747 :   if (!elem->active())
    1572        9226 :     return;
    1573             : 
    1574      567257 :   const Variable & var = dof_map.variable(variable_number);
    1575       47521 :   const FEType & base_fe_type = var.type();
    1576      567257 :   const bool add_p_level = base_fe_type.p_refinement;
    1577             : 
    1578             :   // Construct FE objects for this element and its neighbors.
    1579      567257 :   std::unique_ptr<FEGenericBase<OutputShape>> my_fe
    1580             :     (FEGenericBase<OutputShape>::build(Dim, base_fe_type));
    1581       47521 :   my_fe->add_p_level_in_reinit(add_p_level);
    1582      567257 :   const FEContinuity cont = my_fe->get_continuity();
    1583             : 
    1584             :   // We don't need to constrain discontinuous elements
    1585      567257 :   if (cont == DISCONTINUOUS)
    1586           0 :     return;
    1587       47521 :   libmesh_assert (cont == C_ZERO || cont == C_ONE ||
    1588             :                   cont == SIDE_DISCONTINUOUS);
    1589             : 
    1590             :   // this would require some generalisation:
    1591             :   //  - e.g. the 'my_fe'-object needs generalisation
    1592             :   //  - due to lack of one-to-one correspondence of DOFs and nodes,
    1593             :   //    this doesn't work easily.
    1594      126930 :   if (elem->infinite())
    1595           0 :     libmesh_not_implemented();
    1596             : 
    1597      614778 :   std::unique_ptr<FEGenericBase<OutputShape>> neigh_fe
    1598             :     (FEGenericBase<OutputShape>::build(Dim, base_fe_type));
    1599       47521 :   neigh_fe->add_p_level_in_reinit(add_p_level);
    1600             : 
    1601      614778 :   QGauss my_qface(Dim-1, base_fe_type.default_quadrature_order());
    1602      567257 :   my_fe->attach_quadrature_rule (&my_qface);
    1603       95042 :   std::vector<Point> neigh_qface;
    1604             : 
    1605      126930 :   const std::vector<Real> & JxW = my_fe->get_JxW();
    1606      126930 :   const std::vector<Point> & q_point = my_fe->get_xyz();
    1607       47521 :   const std::vector<std::vector<OutputShape>> & phi = my_fe->get_phi();
    1608       47521 :   const std::vector<std::vector<OutputShape>> & neigh_phi =
    1609             :     neigh_fe->get_phi();
    1610       47521 :   const std::vector<Point> * face_normals = nullptr;
    1611       47521 :   const std::vector<std::vector<OutputGradient>> * dphi = nullptr;
    1612       47521 :   const std::vector<std::vector<OutputGradient>> * neigh_dphi = nullptr;
    1613             : 
    1614       95042 :   std::vector<dof_id_type> my_dof_indices, neigh_dof_indices;
    1615       95042 :   std::vector<unsigned int> my_side_dofs, neigh_side_dofs;
    1616             : 
    1617      567257 :   if (cont == C_ONE)
    1618             :     {
    1619        7232 :       const std::vector<Point> & ref_face_normals =
    1620        7448 :         my_fe->get_normals();
    1621        3616 :       face_normals = &ref_face_normals;
    1622        3616 :       const std::vector<std::vector<OutputGradient>> & ref_dphi =
    1623             :         my_fe->get_dphi();
    1624        3616 :       dphi = &ref_dphi;
    1625        3616 :       const std::vector<std::vector<OutputGradient>> & ref_neigh_dphi =
    1626             :         neigh_fe->get_dphi();
    1627        3616 :       neigh_dphi = &ref_neigh_dphi;
    1628             :     }
    1629             : 
    1630      662299 :   DenseMatrix<Real> Ke;
    1631      567257 :   DenseVector<Real> Fe;
    1632      142563 :   std::vector<DenseVector<Real>> Ue;
    1633             : 
    1634             :   // Look at the element faces.  Check to see if we need to
    1635             :   // build constraints.
    1636     2700396 :   for (auto s : elem->side_index_range())
    1637             :     {
    1638             :       // Get pointers to the element's neighbor.
    1639     2133139 :       const Elem * neigh = elem->neighbor_ptr(s);
    1640             : 
    1641     2133139 :       if (!neigh)
    1642      207847 :         continue;
    1643             : 
    1644     1906280 :       if (!var.active_on_subdomain(neigh->subdomain_id()))
    1645        1636 :         continue;
    1646             : 
    1647             :       // h refinement constraints:
    1648             :       // constrain dofs shared between
    1649             :       // this element and ones coarser
    1650             :       // than this element.
    1651     1904500 :       if (neigh->level() < elem->level())
    1652             :         {
    1653      119126 :           unsigned int s_neigh = neigh->which_neighbor_am_i(elem);
    1654       10120 :           libmesh_assert_less (s_neigh, neigh->n_neighbors());
    1655             : 
    1656             :           // Find the minimum p level; we build the h constraint
    1657             :           // matrix with this and then constrain away all higher p
    1658             :           // DoFs.
    1659       10120 :           libmesh_assert(neigh->active());
    1660      139366 :           const unsigned int min_p_level = add_p_level *
    1661      139366 :             std::min(elem->p_level(), neigh->p_level());
    1662             :           // we may need to make the FE objects reinit with the
    1663             :           // minimum shared p_level
    1664      119126 :           const unsigned int old_elem_level = add_p_level * elem->p_level();
    1665      119126 :           if (old_elem_level != min_p_level)
    1666         360 :             my_fe->set_fe_order(my_fe->get_fe_type().order.get_order() + min_p_level - old_elem_level);
    1667      119126 :           const unsigned int old_neigh_level = add_p_level * neigh->p_level();
    1668      119126 :           if (old_neigh_level != min_p_level)
    1669           0 :             neigh_fe->set_fe_order(neigh_fe->get_fe_type().order.get_order() + min_p_level - old_neigh_level);
    1670             : 
    1671      119126 :           my_fe->reinit(elem, s);
    1672             : 
    1673             :           // This function gets called element-by-element, so there
    1674             :           // will be a lot of memory allocation going on.  We can
    1675             :           // at least minimize this for the case of the dof indices
    1676             :           // by efficiently preallocating the requisite storage.
    1677             :           // n_nodes is not necessarily n_dofs, but it is better
    1678             :           // than nothing!
    1679      119126 :           my_dof_indices.reserve    (elem->n_nodes());
    1680      119126 :           neigh_dof_indices.reserve (neigh->n_nodes());
    1681             : 
    1682      119126 :           dof_map.dof_indices (elem, my_dof_indices,
    1683             :                                variable_number,
    1684             :                                min_p_level);
    1685      119126 :           dof_map.dof_indices (neigh, neigh_dof_indices,
    1686             :                                variable_number,
    1687             :                                min_p_level);
    1688             : 
    1689       10120 :           const unsigned int n_qp = my_qface.n_points();
    1690             : 
    1691      119126 :           FEMap::inverse_map (Dim, neigh, q_point, neigh_qface);
    1692             : 
    1693      119126 :           neigh_fe->reinit(neigh, &neigh_qface);
    1694             : 
    1695             :           // We're only concerned with DOFs whose values (and/or first
    1696             :           // derivatives for C1 elements) are supported on side nodes
    1697      119126 :           FEType elem_fe_type = base_fe_type;
    1698      119126 :           if (old_elem_level != min_p_level)
    1699         360 :             elem_fe_type.order = base_fe_type.order.get_order() + min_p_level - old_elem_level;
    1700      119126 :           FEType neigh_fe_type = base_fe_type;
    1701      119126 :           if (old_neigh_level != min_p_level)
    1702           0 :             neigh_fe_type.order = base_fe_type.order.get_order() + min_p_level - old_neigh_level;
    1703      119126 :           FEInterface::dofs_on_side(elem,  Dim, elem_fe_type,  s,       my_side_dofs);
    1704      119126 :           FEInterface::dofs_on_side(neigh, Dim, neigh_fe_type, s_neigh, neigh_side_dofs);
    1705             : 
    1706       10120 :           const unsigned int n_side_dofs =
    1707       20240 :             cast_int<unsigned int>(my_side_dofs.size());
    1708       10120 :           libmesh_assert_equal_to (n_side_dofs, neigh_side_dofs.size());
    1709             : 
    1710             : #ifndef NDEBUG
    1711       55500 :           for (auto i : my_side_dofs)
    1712       45380 :             libmesh_assert_less(i, my_dof_indices.size());
    1713       55500 :           for (auto i : neigh_side_dofs)
    1714       45380 :             libmesh_assert_less(i, neigh_dof_indices.size());
    1715             : #endif
    1716             : 
    1717      109006 :           Ke.resize (n_side_dofs, n_side_dofs);
    1718      119126 :           Ue.resize(n_side_dofs);
    1719             : 
    1720             :           // Form the projection matrix, (inner product of fine basis
    1721             :           // functions against fine test functions)
    1722      655592 :           for (unsigned int is = 0; is != n_side_dofs; ++is)
    1723             :             {
    1724      581846 :               const unsigned int i = my_side_dofs[is];
    1725     3219104 :               for (unsigned int js = 0; js != n_side_dofs; ++js)
    1726             :                 {
    1727     2907522 :                   const unsigned int j = my_side_dofs[js];
    1728    14924806 :                   for (unsigned int qp = 0; qp != n_qp; ++qp)
    1729             :                     {
    1730    18278456 :                       Ke(is,js) += JxW[qp] * TensorTools::inner_product(phi[i][qp], phi[j][qp]);
    1731    12242168 :                       if (cont == C_ONE)
    1732     4217848 :                         Ke(is,js) += JxW[qp] *
    1733      646400 :                           TensorTools::inner_product((*dphi)[i][qp] *
    1734             :                                                      (*face_normals)[qp],
    1735     1616000 :                                                      (*dphi)[j][qp] *
    1736             :                                                      (*face_normals)[qp]);
    1737             :                     }
    1738             :                 }
    1739             :             }
    1740             : 
    1741             :           // Form the right hand sides, (inner product of coarse basis
    1742             :           // functions against fine test functions)
    1743      655592 :           for (unsigned int is = 0; is != n_side_dofs; ++is)
    1744             :             {
    1745      581846 :               const unsigned int i = neigh_side_dofs[is];
    1746      491086 :               Fe.resize (n_side_dofs);
    1747     3219104 :               for (unsigned int js = 0; js != n_side_dofs; ++js)
    1748             :                 {
    1749     2907522 :                   const unsigned int j = my_side_dofs[js];
    1750    14924806 :                   for (unsigned int qp = 0; qp != n_qp; ++qp)
    1751             :                     {
    1752    14254264 :                       Fe(js) += JxW[qp] *
    1753    14254264 :                         TensorTools::inner_product(neigh_phi[i][qp],
    1754    13248216 :                                                    phi[j][qp]);
    1755    12242168 :                       if (cont == C_ONE)
    1756     4217848 :                         Fe(js) += JxW[qp] *
    1757      969600 :                           TensorTools::inner_product((*neigh_dphi)[i][qp] *
    1758             :                                                      (*face_normals)[qp],
    1759     1616000 :                                                      (*dphi)[j][qp] *
    1760             :                                                      (*face_normals)[qp]);
    1761             :                     }
    1762             :                 }
    1763      581846 :               Ke.cholesky_solve(Fe, Ue[is]);
    1764             :             }
    1765             : 
    1766      655592 :           for (unsigned int js = 0; js != n_side_dofs; ++js)
    1767             :             {
    1768      536466 :               const unsigned int j = my_side_dofs[js];
    1769      581846 :               const dof_id_type my_dof_g = my_dof_indices[j];
    1770       45380 :               libmesh_assert_not_equal_to (my_dof_g, DofObject::invalid_id);
    1771             : 
    1772             :               // Hunt for "constraining against myself" cases before
    1773             :               // we bother creating a constraint row
    1774       45380 :               bool self_constraint = false;
    1775     2591411 :               for (unsigned int is = 0; is != n_side_dofs; ++is)
    1776             :                 {
    1777     2217299 :                   const unsigned int i = neigh_side_dofs[is];
    1778     2217299 :                   const dof_id_type their_dof_g = neigh_dof_indices[i];
    1779      185750 :                   libmesh_assert_not_equal_to (their_dof_g, DofObject::invalid_id);
    1780             : 
    1781     2217299 :                   if (their_dof_g == my_dof_g)
    1782             :                     {
    1783             : #ifndef NDEBUG
    1784       13708 :                       const Real their_dof_value = Ue[is](js);
    1785       13708 :                       libmesh_assert_less (std::abs(their_dof_value-1.),
    1786             :                                            10*TOLERANCE);
    1787             : 
    1788       86448 :                       for (unsigned int k = 0; k != n_side_dofs; ++k)
    1789       72740 :                         libmesh_assert(k == is ||
    1790             :                                        std::abs(Ue[k](js)) <
    1791             :                                        10*TOLERANCE);
    1792             : #endif
    1793             : 
    1794       13708 :                       self_constraint = true;
    1795       13708 :                       break;
    1796             :                     }
    1797             :                 }
    1798             : 
    1799      536466 :               if (self_constraint)
    1800      239175 :                 continue;
    1801             : 
    1802             :               DofConstraintRow * constraint_row;
    1803             : 
    1804             :               // we may be running constraint methods concurrently
    1805             :               // on multiple threads, so we need a lock to
    1806             :               // ensure that this constraint is "ours"
    1807             :               {
    1808       31672 :                 Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    1809             : 
    1810      374112 :                 if (dof_map.is_constrained_dof(my_dof_g))
    1811        6863 :                   continue;
    1812             : 
    1813      297291 :                 constraint_row = &(constraints[my_dof_g]);
    1814       24809 :                 libmesh_assert(constraint_row->empty());
    1815             :               }
    1816             : 
    1817     1684884 :               for (unsigned int is = 0; is != n_side_dofs; ++is)
    1818             :                 {
    1819     1387593 :                   const unsigned int i = neigh_side_dofs[is];
    1820     1387593 :                   const dof_id_type their_dof_g = neigh_dof_indices[i];
    1821      114227 :                   libmesh_assert_not_equal_to (their_dof_g, DofObject::invalid_id);
    1822      114227 :                   libmesh_assert_not_equal_to (their_dof_g, my_dof_g);
    1823             : 
    1824     1501820 :                   const Real their_dof_value = Ue[is](js);
    1825             : 
    1826     1387593 :                   if (std::abs(their_dof_value) < 10*TOLERANCE)
    1827      760194 :                     continue;
    1828             : 
    1829       51566 :                   constraint_row->emplace(their_dof_g, their_dof_value);
    1830             :                 }
    1831             :             }
    1832             : 
    1833      119126 :           my_fe->set_fe_order(my_fe->get_fe_type().order.get_order() + old_elem_level - min_p_level);
    1834      119126 :           neigh_fe->set_fe_order(neigh_fe->get_fe_type().order.get_order() + old_neigh_level - min_p_level);
    1835             :         }
    1836             : 
    1837     1904500 :       if (add_p_level)
    1838             :       {
    1839             :         // p refinement constraints:
    1840             :         // constrain dofs shared between
    1841             :         // active elements and neighbors with
    1842             :         // lower polynomial degrees
    1843             :         const unsigned int min_p_level =
    1844     2064227 :           neigh->min_p_level_by_neighbor(elem, elem->p_level());
    1845     2064227 :         if (min_p_level < elem->p_level())
    1846             :         {
    1847             :           // Adaptive p refinement of non-hierarchic bases will
    1848             :           // require more coding
    1849          48 :           libmesh_assert(my_fe->is_hierarchic());
    1850         576 :           dof_map.constrain_p_dofs(variable_number, elem,
    1851             :                                    s, min_p_level);
    1852             :         }
    1853             :       }
    1854             :     }
    1855     1416645 : }
    1856             : 
    1857             : #endif // #ifdef LIBMESH_ENABLE_AMR
    1858             : 
    1859             : 
    1860             : 
    1861             : #ifdef LIBMESH_ENABLE_PERIODIC
    1862             : template <typename OutputType>
    1863             : void
    1864      264164 : FEGenericBase<OutputType>::
    1865             : compute_periodic_constraints (DofConstraints & constraints,
    1866             :                               DofMap & dof_map,
    1867             :                               const PeriodicBoundaries & boundaries,
    1868             :                               const MeshBase & mesh,
    1869             :                               const PointLocatorBase * point_locator,
    1870             :                               const unsigned int variable_number,
    1871             :                               const Elem * elem)
    1872             : {
    1873             :   // Only bother if we truly have periodic boundaries
    1874      264164 :   if (boundaries.empty())
    1875       58473 :     return;
    1876             : 
    1877       67082 :   libmesh_assert(elem);
    1878             : 
    1879             :   // Only constrain active elements with this method
    1880       67082 :   if (!elem->active())
    1881       19491 :     return;
    1882             : 
    1883      138677 :   if (elem->infinite())
    1884           0 :     libmesh_not_implemented();
    1885             : 
    1886      205691 :   const unsigned int Dim = elem->dim();
    1887             : 
    1888             :   // We need sys_number and variable_number for DofObject methods
    1889             :   // later
    1890       95182 :   const unsigned int sys_number = dof_map.sys_number();
    1891             : 
    1892       47591 :   const FEType & base_fe_type = dof_map.variable_type(variable_number);
    1893             : 
    1894             :   // Construct FE objects for this element and its pseudo-neighbors.
    1895      205691 :   std::unique_ptr<FEGenericBase<OutputShape>> my_fe
    1896             :     (FEGenericBase<OutputShape>::build(Dim, base_fe_type));
    1897      205691 :   const FEContinuity cont = my_fe->get_continuity();
    1898             : 
    1899             :   // We don't need to constrain discontinuous elements
    1900      205691 :   if (cont == DISCONTINUOUS)
    1901           0 :     return;
    1902       47591 :   libmesh_assert (cont == C_ZERO || cont == C_ONE);
    1903             : 
    1904             :   // We'll use element size to generate relative tolerances later
    1905      205691 :   const Real primary_hmin = elem->hmin();
    1906             : 
    1907      253282 :   std::unique_ptr<FEGenericBase<OutputShape>> neigh_fe
    1908             :     (FEGenericBase<OutputShape>::build(Dim, base_fe_type));
    1909             : 
    1910      253282 :   QGauss my_qface(Dim-1, base_fe_type.default_quadrature_order());
    1911      205691 :   my_fe->attach_quadrature_rule (&my_qface);
    1912       95182 :   std::vector<Point> neigh_qface;
    1913             : 
    1914      138677 :   const std::vector<Real> & JxW = my_fe->get_JxW();
    1915      138677 :   const std::vector<Point> & q_point = my_fe->get_xyz();
    1916       47591 :   const std::vector<std::vector<OutputShape>> & phi = my_fe->get_phi();
    1917       47591 :   const std::vector<std::vector<OutputShape>> & neigh_phi =
    1918             :     neigh_fe->get_phi();
    1919       47591 :   const std::vector<Point> * face_normals = nullptr;
    1920       47591 :   const std::vector<std::vector<OutputGradient>> * dphi = nullptr;
    1921       47591 :   const std::vector<std::vector<OutputGradient>> * neigh_dphi = nullptr;
    1922       95182 :   std::vector<dof_id_type> my_dof_indices, neigh_dof_indices;
    1923       95182 :   std::vector<unsigned int> my_side_dofs, neigh_side_dofs;
    1924             : 
    1925      205691 :   if (cont != C_ZERO)
    1926             :     {
    1927        8192 :       const std::vector<Point> & ref_face_normals =
    1928        4096 :         my_fe->get_normals();
    1929        4096 :       face_normals = &ref_face_normals;
    1930        4096 :       const std::vector<std::vector<OutputGradient>> & ref_dphi =
    1931             :         my_fe->get_dphi();
    1932        4096 :       dphi = &ref_dphi;
    1933        4096 :       const std::vector<std::vector<OutputGradient>> & ref_neigh_dphi =
    1934             :         neigh_fe->get_dphi();
    1935        4096 :       neigh_dphi = &ref_neigh_dphi;
    1936             :     }
    1937             : 
    1938      300873 :   DenseMatrix<Real> Ke;
    1939      205691 :   DenseVector<Real> Fe;
    1940      142773 :   std::vector<DenseVector<Real>> Ue;
    1941             : 
    1942             :   // Container to catch the boundary ids that BoundaryInfo hands us.
    1943       95182 :   std::vector<boundary_id_type> bc_ids;
    1944             : 
    1945             :   // Look at the element faces.  Check to see if we need to
    1946             :   // build constraints.
    1947      205691 :   const unsigned short int max_ns = elem->n_sides();
    1948     1018023 :   for (unsigned short int s = 0; s != max_ns; ++s)
    1949             :     {
    1950     1001144 :       if (elem->neighbor_ptr(s))
    1951      589194 :         continue;
    1952             : 
    1953       39424 :       mesh.get_boundary_info().boundary_ids (elem, s, bc_ids);
    1954             : 
    1955       59984 :       for (const auto & boundary_id : bc_ids)
    1956             :         {
    1957       20560 :           const PeriodicBoundaryBase * periodic = boundaries.boundary(boundary_id);
    1958       20560 :           if (!periodic || !periodic->is_my_variable(variable_number))
    1959        5784 :             continue;
    1960             : 
    1961        3044 :           libmesh_assert(point_locator);
    1962             : 
    1963             :           // Get pointers to the element's neighbor.
    1964             :           unsigned int s_neigh;
    1965       14776 :           const Elem * neigh = boundaries.neighbor(boundary_id, *point_locator, elem, s, &s_neigh);
    1966             : 
    1967       14776 :           libmesh_error_msg_if(neigh == nullptr,
    1968             :                                "PeriodicBoundaries point locator object returned nullptr!");
    1969             : 
    1970             :           // periodic (and possibly h refinement) constraints:
    1971             :           // constrain dofs shared between
    1972             :           // this element and ones as coarse
    1973             :           // as or coarser than this element.
    1974       14776 :           if (neigh->level() <= elem->level())
    1975             :             {
    1976             : #ifdef LIBMESH_ENABLE_AMR
    1977             :               // Find the minimum p level; we build the h constraint
    1978             :               // matrix with this and then constrain away all higher p
    1979             :               // DoFs.
    1980        2596 :               libmesh_assert(neigh->active());
    1981       13432 :               const unsigned int min_p_level =
    1982       18624 :                 std::min(elem->p_level(), neigh->p_level());
    1983             : 
    1984             :               // we may need to make the FE objects reinit with the
    1985             :               // minimum shared p_level
    1986             :               // FIXME - I hate using const_cast<> and avoiding
    1987             :               // accessor functions; there's got to be a
    1988             :               // better way to do this!
    1989        2596 :               const unsigned int old_elem_level = elem->p_level();
    1990       13432 :               if (old_elem_level != min_p_level)
    1991           0 :                 (const_cast<Elem *>(elem))->hack_p_level(min_p_level);
    1992        5192 :               const unsigned int old_neigh_level = neigh->p_level();
    1993       13432 :               if (old_neigh_level != min_p_level)
    1994           0 :                 (const_cast<Elem *>(neigh))->hack_p_level(min_p_level);
    1995             : #endif // #ifdef LIBMESH_ENABLE_AMR
    1996             : 
    1997             :               // We can do a projection with a single integration,
    1998             :               // due to the assumption of nested finite element
    1999             :               // subspaces.
    2000             :               // FIXME: it might be more efficient to do nodes,
    2001             :               // then edges, then side, to reduce the size of the
    2002             :               // Cholesky factorization(s)
    2003       13432 :               my_fe->reinit(elem, s);
    2004             : 
    2005       13432 :               dof_map.dof_indices (elem, my_dof_indices,
    2006             :                                    variable_number);
    2007       13432 :               dof_map.dof_indices (neigh, neigh_dof_indices,
    2008             :                                    variable_number);
    2009             : 
    2010             :               // We use neigh_dof_indices_all_variables in the case that the
    2011             :               // periodic boundary condition involves mappings between multiple
    2012             :               // variables.
    2013        7788 :               std::vector<std::vector<dof_id_type>> neigh_dof_indices_all_variables;
    2014       13432 :               if(periodic->has_transformation_matrix())
    2015             :                 {
    2016        7200 :                   const std::set<unsigned int> & variables = periodic->get_variables();
    2017        7200 :                   neigh_dof_indices_all_variables.resize(variables.size());
    2018         600 :                   unsigned int index = 0;
    2019       28800 :                   for(unsigned int var : variables)
    2020             :                     {
    2021       23400 :                       dof_map.dof_indices (neigh, neigh_dof_indices_all_variables[index],
    2022             :                                            var);
    2023       21600 :                       index++;
    2024             :                     }
    2025             :                 }
    2026             : 
    2027        2596 :               const unsigned int n_qp = my_qface.n_points();
    2028             : 
    2029             :               // Translate the quadrature points over to the
    2030             :               // neighbor's boundary
    2031       18624 :               std::vector<Point> neigh_point(q_point.size());
    2032       54848 :               for (auto i : index_range(neigh_point))
    2033       47820 :                 neigh_point[i] = periodic->get_corresponding_pos(q_point[i]);
    2034             : 
    2035       13432 :               FEMap::inverse_map (Dim, neigh, neigh_point,
    2036             :                                   neigh_qface);
    2037             : 
    2038       13432 :               neigh_fe->reinit(neigh, &neigh_qface);
    2039             : 
    2040             :               // We're only concerned with DOFs whose values (and/or first
    2041             :               // derivatives for C1 elements) are supported on side nodes
    2042       13432 :               FEInterface::dofs_on_side(elem, Dim, base_fe_type, s, my_side_dofs);
    2043       13432 :               FEInterface::dofs_on_side(neigh, Dim, base_fe_type, s_neigh, neigh_side_dofs);
    2044             : 
    2045             :               // We're done with functions that examine Elem::p_level(),
    2046             :               // so let's unhack those levels
    2047             : #ifdef LIBMESH_ENABLE_AMR
    2048       16028 :               if (elem->p_level() != old_elem_level)
    2049           0 :                 (const_cast<Elem *>(elem))->hack_p_level(old_elem_level);
    2050       16028 :               if (neigh->p_level() != old_neigh_level)
    2051           0 :                 (const_cast<Elem *>(neigh))->hack_p_level(old_neigh_level);
    2052             : #endif // #ifdef LIBMESH_ENABLE_AMR
    2053             : 
    2054        2596 :               const unsigned int n_side_dofs =
    2055             :                 cast_int<unsigned int>
    2056        5192 :                 (my_side_dofs.size());
    2057        2596 :               libmesh_assert_equal_to (n_side_dofs, neigh_side_dofs.size());
    2058             : 
    2059       10836 :               Ke.resize (n_side_dofs, n_side_dofs);
    2060       13432 :               Ue.resize(n_side_dofs);
    2061             : 
    2062             :               // Form the projection matrix, (inner product of fine basis
    2063             :               // functions against fine test functions)
    2064       54936 :               for (unsigned int is = 0; is != n_side_dofs; ++is)
    2065             :                 {
    2066       47916 :                   const unsigned int i = my_side_dofs[is];
    2067      182832 :                   for (unsigned int js = 0; js != n_side_dofs; ++js)
    2068             :                     {
    2069      159012 :                       const unsigned int j = my_side_dofs[js];
    2070      656192 :                       for (unsigned int qp = 0; qp != n_qp; ++qp)
    2071             :                         {
    2072      624296 :                           Ke(is,js) += JxW[qp] *
    2073      624296 :                             TensorTools::inner_product(phi[i][qp],
    2074      569580 :                                                        phi[j][qp]);
    2075      514864 :                           if (cont != C_ZERO)
    2076         384 :                             Ke(is,js) += JxW[qp] *
    2077          64 :                               TensorTools::inner_product((*dphi)[i][qp] *
    2078             :                                                          (*face_normals)[qp],
    2079         160 :                                                          (*dphi)[j][qp] *
    2080             :                                                          (*face_normals)[qp]);
    2081             :                         }
    2082             :                     }
    2083             :                 }
    2084             : 
    2085             :               // Form the right hand sides, (inner product of coarse basis
    2086             :               // functions against fine test functions)
    2087       54936 :               for (unsigned int is = 0; is != n_side_dofs; ++is)
    2088             :                 {
    2089       47916 :                   const unsigned int i = neigh_side_dofs[is];
    2090       35092 :                   Fe.resize (n_side_dofs);
    2091      182832 :                   for (unsigned int js = 0; js != n_side_dofs; ++js)
    2092             :                     {
    2093      159012 :                       const unsigned int j = my_side_dofs[js];
    2094      656192 :                       for (unsigned int qp = 0; qp != n_qp; ++qp)
    2095             :                         {
    2096      624296 :                           Fe(js) += JxW[qp] *
    2097      624296 :                             TensorTools::inner_product(neigh_phi[i][qp],
    2098      569580 :                                                        phi[j][qp]);
    2099      514864 :                           if (cont != C_ZERO)
    2100         384 :                             Fe(js) += JxW[qp] *
    2101          96 :                               TensorTools::inner_product((*neigh_dphi)[i][qp] *
    2102             :                                                          (*face_normals)[qp],
    2103         160 :                                                          (*dphi)[j][qp] *
    2104             :                                                          (*face_normals)[qp]);
    2105             :                         }
    2106             :                     }
    2107       47916 :                   Ke.cholesky_solve(Fe, Ue[is]);
    2108             :                 }
    2109             : 
    2110             :               // Make sure we're not adding recursive constraints
    2111             :               // due to the redundancy in the way we add periodic
    2112             :               // boundary constraints
    2113             :               //
    2114             :               // In order for this to work while threaded or on
    2115             :               // distributed meshes, we need a rigorous way to
    2116             :               // avoid recursive constraints.  Here it is:
    2117             :               //
    2118             :               // For vertex DoFs, if there is a "prior" element
    2119             :               // (i.e. a coarser element or an equally refined
    2120             :               // element with a lower id) on this boundary which
    2121             :               // contains the vertex point, then we will avoid
    2122             :               // generating constraints; the prior element (or
    2123             :               // something prior to it) may do so.  If we are the
    2124             :               // most prior (or "primary") element on this
    2125             :               // boundary sharing this point, then we look at the
    2126             :               // boundary periodic to us, we find the primary
    2127             :               // element there, and if that primary is coarser or
    2128             :               // equal-but-lower-id, then our vertex dofs are
    2129             :               // constrained in terms of that element.
    2130             :               //
    2131             :               // For edge DoFs, if there is a coarser element
    2132             :               // on this boundary sharing this edge, then we will
    2133             :               // avoid generating constraints (we will be
    2134             :               // constrained indirectly via AMR constraints
    2135             :               // connecting us to the coarser element's DoFs).  If
    2136             :               // we are the coarsest element sharing this edge,
    2137             :               // then we generate constraints if and only if we
    2138             :               // are finer than the coarsest element on the
    2139             :               // boundary periodic to us sharing the corresponding
    2140             :               // periodic edge, or if we are at equal level but
    2141             :               // our edge nodes have higher ids than the periodic
    2142             :               // edge nodes (sorted from highest to lowest, then
    2143             :               // compared lexicographically)
    2144             :               //
    2145             :               // For face DoFs, we generate constraints if we are
    2146             :               // finer than our periodic neighbor, or if we are at
    2147             :               // equal level but our element id is higher than its
    2148             :               // element id.
    2149             :               //
    2150             :               // If the primary neighbor is also the current elem
    2151             :               // (a 1-element-thick mesh) then we choose which
    2152             :               // vertex dofs to constrain via lexicographic
    2153             :               // ordering on point locations
    2154             : 
    2155             :               // FIXME: This code doesn't yet properly handle
    2156             :               // cases where multiple different periodic BCs
    2157             :               // intersect.
    2158        5192 :               std::set<dof_id_type> my_constrained_dofs;
    2159             : 
    2160             :               // Container to catch boundary IDs handed back by BoundaryInfo.
    2161        5192 :               std::vector<boundary_id_type> new_bc_ids;
    2162             : 
    2163       96264 :               for (auto n : elem->node_index_range())
    2164             :                 {
    2165       82832 :                   if (!elem->is_node_on_side(n,s))
    2166       35012 :                     continue;
    2167             : 
    2168        6404 :                   const Node & my_node = elem->node_ref(n);
    2169             : 
    2170       41416 :                   if (elem->is_vertex(n))
    2171             :                     {
    2172             :                       // Find all boundary ids that include this
    2173             :                       // point and have periodic boundary
    2174             :                       // conditions for this variable
    2175        6384 :                       std::set<boundary_id_type> point_bcids;
    2176             : 
    2177      256440 :                       for (unsigned int new_s = 0;
    2178      262824 :                            new_s != max_ns; ++new_s)
    2179             :                         {
    2180      221648 :                           if (!elem->is_node_on_side(n,new_s))
    2181       95464 :                             continue;
    2182             : 
    2183      111064 :                           mesh.get_boundary_info().boundary_ids (elem, s, new_bc_ids);
    2184             : 
    2185      222128 :                           for (const auto & new_boundary_id : new_bc_ids)
    2186             :                             {
    2187      111064 :                               const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
    2188      111064 :                               if (new_periodic && new_periodic->is_my_variable(variable_number))
    2189       95904 :                             point_bcids.insert(new_boundary_id);
    2190             :                             }
    2191             :                         }
    2192             : 
    2193             :                       // See if this vertex has point neighbors to
    2194             :                       // defer to
    2195       44655 :                       if (primary_boundary_point_neighbor
    2196       41176 :                           (elem, my_node, mesh.get_boundary_info(), point_bcids)
    2197        6384 :                           != elem)
    2198       21511 :                         continue;
    2199             : 
    2200             :                       // Find the complementary boundary id set
    2201        2905 :                       std::set<boundary_id_type> point_pairedids;
    2202       32372 :                       for (const auto & new_boundary_id : point_bcids)
    2203             :                         {
    2204       16186 :                           const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
    2205       16186 :                           point_pairedids.insert(new_periodic->pairedboundary);
    2206             :                         }
    2207             : 
    2208             :                       // What do we want to constrain against?
    2209        2905 :                       const Elem * primary_elem = nullptr;
    2210        2905 :                       const Elem * main_neigh = nullptr;
    2211       16186 :                       Point main_pt = my_node,
    2212       16186 :                         primary_pt = my_node;
    2213             : 
    2214       32372 :                       for (const auto & new_boundary_id : point_bcids)
    2215             :                         {
    2216             :                           // Find the corresponding periodic point and
    2217             :                           // its primary neighbor
    2218       16186 :                           const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
    2219             : 
    2220             :                           const Point neigh_pt =
    2221       16186 :                             new_periodic->get_corresponding_pos(my_node);
    2222             : 
    2223             :                           // If the point is getting constrained
    2224             :                           // to itself by this PBC then we don't
    2225             :                           // generate any constraints
    2226       16186 :                           if (neigh_pt.absolute_fuzzy_equals
    2227       16186 :                               (my_node, primary_hmin*TOLERANCE))
    2228        6828 :                             continue;
    2229             : 
    2230             :                           // Otherwise we'll have a constraint in
    2231             :                           // one direction or another
    2232       16186 :                           if (!primary_elem)
    2233        2905 :                             primary_elem = elem;
    2234             : 
    2235        2905 :                           const Elem * primary_neigh =
    2236       16186 :                             primary_boundary_point_neighbor(neigh, neigh_pt,
    2237             :                                                             mesh.get_boundary_info(),
    2238             :                                                             point_pairedids);
    2239             : 
    2240        2905 :                           libmesh_assert(primary_neigh);
    2241             : 
    2242       16186 :                           if (new_boundary_id == boundary_id)
    2243             :                             {
    2244        2905 :                               main_neigh = primary_neigh;
    2245       16186 :                               main_pt = neigh_pt;
    2246             :                             }
    2247             : 
    2248             :                           // Finer elements will get constrained in
    2249             :                           // terms of coarser neighbors, not the
    2250             :                           // other way around
    2251       29467 :                           if ((primary_neigh->level() > primary_elem->level()) ||
    2252             : 
    2253             :                               // For equal-level elements, the one with
    2254             :                               // higher id gets constrained in terms of
    2255             :                               // the one with lower id
    2256       25646 :                               (primary_neigh->level() == primary_elem->level() &&
    2257       28558 :                                primary_neigh->id() > primary_elem->id()) ||
    2258             : 
    2259             :                               // On a one-element-thick mesh, we compare
    2260             :                               // points to see what side gets constrained
    2261        1880 :                               (primary_neigh == primary_elem &&
    2262           0 :                                (neigh_pt > primary_pt)))
    2263        6828 :                             continue;
    2264             : 
    2265        1880 :                           primary_elem = primary_neigh;
    2266        9358 :                           primary_pt = neigh_pt;
    2267             :                         }
    2268             : 
    2269       13493 :                       if (!primary_elem ||
    2270       19091 :                           primary_elem != main_neigh ||
    2271        1880 :                           primary_pt != main_pt)
    2272        1025 :                         continue;
    2273             :                     }
    2274         240 :                   else if (elem->is_edge(n))
    2275             :                     {
    2276             :                       // Find which edge we're on
    2277         240 :                       unsigned int e=0, ne = elem->n_edges();
    2278         384 :                       for (; e != ne; ++e)
    2279             :                         {
    2280         384 :                           if (elem->is_node_on_edge(n,e))
    2281          20 :                             break;
    2282             :                         }
    2283          20 :                       libmesh_assert_less (e, elem->n_edges());
    2284             : 
    2285             :                       // Find the edge end nodes
    2286             :                       const Node
    2287          20 :                         * e1 = nullptr,
    2288          20 :                         * e2 = nullptr;
    2289         612 :                       for (auto nn : elem->node_index_range())
    2290             :                         {
    2291         612 :                           if (nn == n)
    2292           0 :                             continue;
    2293             : 
    2294         612 :                           if (elem->is_node_on_edge(nn, e))
    2295             :                             {
    2296         480 :                               if (e1 == nullptr)
    2297             :                                 {
    2298          40 :                                   e1 = elem->node_ptr(nn);
    2299             :                                 }
    2300             :                               else
    2301             :                                 {
    2302          40 :                                   e2 = elem->node_ptr(nn);
    2303         240 :                                   break;
    2304             :                                 }
    2305             :                             }
    2306             :                         }
    2307          20 :                       libmesh_assert (e1 && e2);
    2308             : 
    2309             :                       // Find all boundary ids that include this
    2310             :                       // edge and have periodic boundary
    2311             :                       // conditions for this variable
    2312          20 :                       std::set<boundary_id_type> edge_bcids;
    2313             : 
    2314         940 :                       for (unsigned int new_s = 0;
    2315         960 :                            new_s != max_ns; ++new_s)
    2316             :                         {
    2317         720 :                           if (!elem->is_node_on_side(n,new_s))
    2318         440 :                             continue;
    2319             : 
    2320             :                           // We're reusing the new_bc_ids vector created outside the loop over nodes.
    2321         240 :                           mesh.get_boundary_info().boundary_ids (elem, s, new_bc_ids);
    2322             : 
    2323         480 :                           for (const auto & new_boundary_id : new_bc_ids)
    2324             :                             {
    2325         240 :                               const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
    2326         240 :                               if (new_periodic && new_periodic->is_my_variable(variable_number))
    2327         220 :                                 edge_bcids.insert(new_boundary_id);
    2328             :                             }
    2329             :                         }
    2330             : 
    2331             : 
    2332             :                       // See if this edge has neighbors to defer to
    2333         240 :                       if (primary_boundary_edge_neighbor
    2334         240 :                           (elem, *e1, *e2, mesh.get_boundary_info(), edge_bcids)
    2335          20 :                           != elem)
    2336           0 :                         continue;
    2337             : 
    2338             :                       // Find the complementary boundary id set
    2339          20 :                       std::set<boundary_id_type> edge_pairedids;
    2340         480 :                       for (const auto & new_boundary_id : edge_bcids)
    2341             :                         {
    2342         240 :                           const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
    2343         240 :                           edge_pairedids.insert(new_periodic->pairedboundary);
    2344             :                         }
    2345             : 
    2346             :                       // What do we want to constrain against?
    2347          20 :                       const Elem * primary_elem = nullptr;
    2348          20 :                       const Elem * main_neigh = nullptr;
    2349         240 :                       Point main_pt1 = *e1,
    2350         240 :                         main_pt2 = *e2,
    2351         240 :                         primary_pt1 = *e1,
    2352         240 :                         primary_pt2 = *e2;
    2353             : 
    2354         480 :                       for (const auto & new_boundary_id : edge_bcids)
    2355             :                         {
    2356             :                           // Find the corresponding periodic edge and
    2357             :                           // its primary neighbor
    2358         240 :                           const PeriodicBoundaryBase * new_periodic = boundaries.boundary(new_boundary_id);
    2359             : 
    2360         240 :                           Point neigh_pt1 = new_periodic->get_corresponding_pos(*e1),
    2361         240 :                             neigh_pt2 = new_periodic->get_corresponding_pos(*e2);
    2362             : 
    2363             :                           // If the edge is getting constrained
    2364             :                           // to itself by this PBC then we don't
    2365             :                           // generate any constraints
    2366          20 :                           if (neigh_pt1.absolute_fuzzy_equals
    2367         260 :                               (*e1, primary_hmin*TOLERANCE) &&
    2368             :                               neigh_pt2.absolute_fuzzy_equals
    2369           0 :                               (*e2, primary_hmin*TOLERANCE))
    2370         120 :                             continue;
    2371             : 
    2372             :                           // Otherwise we'll have a constraint in
    2373             :                           // one direction or another
    2374         240 :                           if (!primary_elem)
    2375          20 :                             primary_elem = elem;
    2376             : 
    2377          20 :                           const Elem * primary_neigh = primary_boundary_edge_neighbor
    2378         240 :                             (neigh, neigh_pt1, neigh_pt2,
    2379             :                              mesh.get_boundary_info(), edge_pairedids);
    2380             : 
    2381          20 :                           libmesh_assert(primary_neigh);
    2382             : 
    2383         240 :                           if (new_boundary_id == boundary_id)
    2384             :                             {
    2385          20 :                               main_neigh = primary_neigh;
    2386         240 :                               main_pt1 = neigh_pt1;
    2387         240 :                               main_pt2 = neigh_pt2;
    2388             :                             }
    2389             : 
    2390             :                           // If we have a one-element thick mesh,
    2391             :                           // we'll need to sort our points to get a
    2392             :                           // consistent ordering rule
    2393             :                           //
    2394             :                           // Use >= in this test to make sure that,
    2395             :                           // for angular constraints, no node gets
    2396             :                           // constrained to itself.
    2397         240 :                           if (primary_neigh == primary_elem)
    2398             :                             {
    2399           0 :                               if (primary_pt1 > primary_pt2)
    2400           0 :                                 std::swap(primary_pt1, primary_pt2);
    2401           0 :                               if (neigh_pt1 > neigh_pt2)
    2402           0 :                                 std::swap(neigh_pt1, neigh_pt2);
    2403             : 
    2404           0 :                               if (neigh_pt2 >= primary_pt2)
    2405           0 :                                 continue;
    2406             :                             }
    2407             : 
    2408             :                           // Otherwise:
    2409             :                           // Finer elements will get constrained in
    2410             :                           // terms of coarser ones, not the other way
    2411             :                           // around
    2412         480 :                           if ((primary_neigh->level() > primary_elem->level()) ||
    2413             : 
    2414             :                               // For equal-level elements, the one with
    2415             :                               // higher id gets constrained in terms of
    2416             :                               // the one with lower id
    2417         440 :                               (primary_neigh->level() == primary_elem->level() &&
    2418          40 :                                primary_neigh->id() > primary_elem->id()))
    2419         120 :                             continue;
    2420             : 
    2421          10 :                           primary_elem = primary_neigh;
    2422         120 :                           primary_pt1 = neigh_pt1;
    2423         120 :                           primary_pt2 = neigh_pt2;
    2424             :                         }
    2425             : 
    2426         160 :                       if (!primary_elem ||
    2427         230 :                           primary_elem != main_neigh ||
    2428         270 :                           primary_pt1 != main_pt1 ||
    2429          10 :                           primary_pt2 != main_pt2)
    2430          10 :                         continue;
    2431             :                     }
    2432           0 :                   else if (elem->is_face(n))
    2433             :                     {
    2434             :                       // If we have a one-element thick mesh,
    2435             :                       // use the ordering of the face node and its
    2436             :                       // periodic counterpart to determine what
    2437             :                       // gets constrained
    2438           0 :                       if (neigh == elem)
    2439             :                         {
    2440             :                           const Point neigh_pt =
    2441           0 :                             periodic->get_corresponding_pos(my_node);
    2442           0 :                           if (neigh_pt > my_node)
    2443           0 :                             continue;
    2444             :                         }
    2445             : 
    2446             :                       // Otherwise:
    2447             :                       // Finer elements will get constrained in
    2448             :                       // terms of coarser ones, not the other way
    2449             :                       // around
    2450           0 :                       if ((neigh->level() > elem->level()) ||
    2451             : 
    2452             :                           // For equal-level elements, the one with
    2453             :                           // higher id gets constrained in terms of
    2454             :                           // the one with lower id
    2455           0 :                           (neigh->level() == elem->level() &&
    2456           0 :                            neigh->id() > elem->id()))
    2457           0 :                         continue;
    2458             :                     }
    2459             : 
    2460             :                   // If we made it here without hitting a continue
    2461             :                   // statement, then we're at a node whose dofs
    2462             :                   // should be constrained by this element's
    2463             :                   // calculations.
    2464             :                   const unsigned int n_comp =
    2465        9478 :                     my_node.n_comp(sys_number, variable_number);
    2466             : 
    2467       19000 :                   for (unsigned int i=0; i != n_comp; ++i)
    2468             :                     my_constrained_dofs.insert
    2469        7628 :                       (my_node.dof_number
    2470        9522 :                        (sys_number, variable_number, i));
    2471             :                 }
    2472             : 
    2473             :               // FIXME: old code for disambiguating periodic BCs:
    2474             :               // this is not threadsafe nor safe to run on a
    2475             :               // non-serialized mesh.
    2476             :               /*
    2477             :                 std::vector<bool> recursive_constraint(n_side_dofs, false);
    2478             : 
    2479             :                 for (unsigned int is = 0; is != n_side_dofs; ++is)
    2480             :                 {
    2481             :                 const unsigned int i = neigh_side_dofs[is];
    2482             :                 const dof_id_type their_dof_g = neigh_dof_indices[i];
    2483             :                 libmesh_assert_not_equal_to (their_dof_g, DofObject::invalid_id);
    2484             : 
    2485             :                 {
    2486             :                 Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    2487             : 
    2488             :                 if (!dof_map.is_constrained_dof(their_dof_g))
    2489             :                 continue;
    2490             :                 }
    2491             : 
    2492             :                 DofConstraintRow & their_constraint_row =
    2493             :                 constraints[their_dof_g].first;
    2494             : 
    2495             :                 for (unsigned int js = 0; js != n_side_dofs; ++js)
    2496             :                 {
    2497             :                 const unsigned int j = my_side_dofs[js];
    2498             :                 const dof_id_type my_dof_g = my_dof_indices[j];
    2499             :                 libmesh_assert_not_equal_to (my_dof_g, DofObject::invalid_id);
    2500             : 
    2501             :                 if (their_constraint_row.count(my_dof_g))
    2502             :                 recursive_constraint[js] = true;
    2503             :                 }
    2504             :                 }
    2505             :               */
    2506             : 
    2507       54936 :               for (unsigned int js = 0; js != n_side_dofs; ++js)
    2508             :                 {
    2509             :                   // FIXME: old code path
    2510             :                   // if (recursive_constraint[js])
    2511             :                   //  continue;
    2512             : 
    2513       41504 :                   const unsigned int j = my_side_dofs[js];
    2514       47916 :                   const dof_id_type my_dof_g = my_dof_indices[j];
    2515        6412 :                   libmesh_assert_not_equal_to (my_dof_g, DofObject::invalid_id);
    2516             : 
    2517             :                   // FIXME: new code path
    2518       29358 :                   if (!my_constrained_dofs.count(my_dof_g))
    2519       32254 :                     continue;
    2520             : 
    2521             :                   DofConstraintRow * constraint_row;
    2522             : 
    2523             :                   // we may be running constraint methods concurrently
    2524             :                   // on multiple threads, so we need a lock to
    2525             :                   // ensure that this constraint is "ours"
    2526             :                   {
    2527        1894 :                     Threads::spin_mutex::scoped_lock lock(Threads::spin_mtx);
    2528             : 
    2529        9522 :                     if (dof_map.is_constrained_dof(my_dof_g))
    2530          81 :                       continue;
    2531             : 
    2532        9250 :                     constraint_row = &(constraints[my_dof_g]);
    2533        1813 :                     libmesh_assert(constraint_row->empty());
    2534             :                   }
    2535             : 
    2536       37506 :                   for (unsigned int is = 0; is != n_side_dofs; ++is)
    2537             :                     {
    2538       28256 :                       const unsigned int i = neigh_side_dofs[is];
    2539       28256 :                       const dof_id_type their_dof_g = neigh_dof_indices[i];
    2540        4439 :                       libmesh_assert_not_equal_to (their_dof_g, DofObject::invalid_id);
    2541             : 
    2542             :                       // Periodic constraints should never be
    2543             :                       // self-constraints
    2544             :                       // libmesh_assert_not_equal_to (their_dof_g, my_dof_g);
    2545             : 
    2546       28256 :                       const Real their_dof_value = Ue[is](js);
    2547             : 
    2548       28256 :                       if (their_dof_g == my_dof_g)
    2549             :                         {
    2550           0 :                           libmesh_assert_less (std::abs(their_dof_value-1.), 1.e-5);
    2551           0 :                           for (unsigned int k = 0; k != n_side_dofs; ++k)
    2552           0 :                             libmesh_assert(k == is || std::abs(Ue[k](js)) < 1.e-5);
    2553       17662 :                           continue;
    2554           0 :                         }
    2555             : 
    2556       28256 :                       if (std::abs(their_dof_value) < 10*TOLERANCE)
    2557       15484 :                         continue;
    2558             : 
    2559       10594 :                       if(!periodic->has_transformation_matrix())
    2560             :                         {
    2561        1865 :                           constraint_row->emplace(their_dof_g, their_dof_value);
    2562             :                         }
    2563             :                       else
    2564             :                         {
    2565             :                           // In this case the current variable is constrained in terms of other variables.
    2566             :                           // We assume that all variables in this constraint have the same FE type (this
    2567             :                           // is asserted below), and hence we can create the constraint row contribution
    2568             :                           // by multiplying their_dof_value by the corresponding row of the transformation
    2569             :                           // matrix.
    2570             : 
    2571        4752 :                           const std::set<unsigned int> & variables = periodic->get_variables();
    2572        4752 :                           neigh_dof_indices_all_variables.resize(variables.size());
    2573         396 :                           unsigned int index = 0;
    2574       19008 :                           for(unsigned int other_var : variables)
    2575             :                             {
    2576        1188 :                               libmesh_assert_msg(base_fe_type == dof_map.variable_type(other_var), "FE types must match for all variables involved in constraint");
    2577             : 
    2578       14256 :                               Real var_weighting = periodic->get_transformation_matrix()(variable_number, other_var);
    2579       14256 :                               constraint_row->emplace(neigh_dof_indices_all_variables[index][i],
    2580       14256 :                                                       var_weighting*their_dof_value);
    2581       14256 :                               index++;
    2582             :                             }
    2583             :                         }
    2584             : 
    2585             :                     }
    2586             :                 }
    2587        8240 :             }
    2588             :           // p refinement constraints:
    2589             :           // constrain dofs shared between
    2590             :           // active elements and neighbors with
    2591             :           // lower polynomial degrees
    2592             : #ifdef LIBMESH_ENABLE_AMR
    2593             :           const unsigned int min_p_level =
    2594       17820 :             neigh->min_p_level_by_neighbor(elem, elem->p_level());
    2595       17820 :           if (min_p_level < elem->p_level())
    2596             :             {
    2597             :               // Adaptive p refinement of non-hierarchic bases will
    2598             :               // require more coding
    2599           0 :               libmesh_assert(my_fe->is_hierarchic());
    2600           0 :               dof_map.constrain_p_dofs(variable_number, elem,
    2601             :                                        s, min_p_level);
    2602             :             }
    2603             : #endif // #ifdef LIBMESH_ENABLE_AMR
    2604             :         }
    2605             :     }
    2606      331527 : }
    2607             : 
    2608             : #endif // LIBMESH_ENABLE_PERIODIC
    2609             : 
    2610             : // ------------------------------------------------------------
    2611             : // Explicit instantiations
    2612             : template class LIBMESH_EXPORT FEGenericBase<Real>;
    2613             : template class LIBMESH_EXPORT FEGenericBase<RealGradient>;
    2614             : 
    2615             : } // namespace libMesh

Generated by: LCOV version 1.14