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

Generated by: LCOV version 1.14