LCOV - code coverage report
Current view: top level - src/fe - fe_base.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 681 1183 57.6 %
Date: 2025-08-19 19:27:09 Functions: 14 36 38.9 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14