LCOV - code coverage report
Current view: top level - src/fe - fe_subdivision_2D.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4232 (290bfc) with base 82cc40 Lines: 460 556 82.7 %
Date: 2025-08-27 15:46:53 Functions: 20 41 48.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // The libMesh Finite Element Library.
       2             : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
       3             : 
       4             : // This library is free software; you can redistribute it and/or
       5             : // modify it under the terms of the GNU Lesser General Public
       6             : // License as published by the Free Software Foundation; either
       7             : // version 2.1 of the License, or (at your option) any later version.
       8             : 
       9             : // This library is distributed in the hope that it will be useful,
      10             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             : // Lesser General Public License for more details.
      13             : 
      14             : // You should have received a copy of the GNU Lesser General Public
      15             : // License along with this library; if not, write to the Free Software
      16             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      17             : 
      18             : 
      19             : // Local includes
      20             : #include "libmesh/fe.h"
      21             : #include "libmesh/libmesh_logging.h"
      22             : #include "libmesh/fe_type.h"
      23             : #include "libmesh/quadrature.h"
      24             : #include "libmesh/face_tri3_subdivision.h"
      25             : #include "libmesh/fe_macro.h"
      26             : #include "libmesh/dense_matrix.h"
      27             : #include "libmesh/utility.h"
      28             : #include "libmesh/enum_to_string.h"
      29             : 
      30             : namespace libMesh
      31             : {
      32             : 
      33             : 
      34           0 : LIBMESH_DEFAULT_VECTORIZED_FE(2,SUBDIVISION)
      35             : 
      36             : 
      37          71 : FESubdivision::FESubdivision(const FEType & fet) :
      38          71 :   FE<2,SUBDIVISION>(fet)
      39             : {
      40             :   // Only 2D meshes in 3D space are supported
      41             :   libmesh_assert_equal_to(LIBMESH_DIM, 3);
      42          71 : }
      43             : 
      44             : 
      45             : 
      46          48 : void FESubdivision::init_subdivision_matrix(DenseMatrix<Real> & A,
      47             :                                             unsigned int valence)
      48             : {
      49          48 :   A.resize(valence + 12, valence + 12);
      50             : 
      51             :   // A = (S11 0; S21 S22), see Cirak et al.,
      52             :   // Int. J. Numer. Meth. Engng. 2000; 47:2039-2072, Appendix A.2.
      53             : 
      54             :   // First, set the static S21 part
      55          52 :   A(valence+ 1,0        ) = 0.125;
      56          48 :   A(valence+ 1,1        ) = 0.375;
      57          48 :   A(valence+ 1,valence  ) = 0.375;
      58          52 :   A(valence+ 2,0        ) = 0.0625;
      59          48 :   A(valence+ 2,1        ) = 0.625;
      60          48 :   A(valence+ 2,2        ) = 0.0625;
      61          48 :   A(valence+ 2,valence  ) = 0.0625;
      62          52 :   A(valence+ 3,0        ) = 0.125;
      63          48 :   A(valence+ 3,1        ) = 0.375;
      64          48 :   A(valence+ 3,2        ) = 0.375;
      65          52 :   A(valence+ 4,0        ) = 0.0625;
      66          48 :   A(valence+ 4,1        ) = 0.0625;
      67          52 :   A(valence+ 4,valence-1) = 0.0625;
      68          48 :   A(valence+ 4,valence  ) = 0.625;
      69          52 :   A(valence+ 5,0        ) = 0.125;
      70          48 :   A(valence+ 5,valence-1) = 0.375;
      71          48 :   A(valence+ 5,valence  ) = 0.375;
      72          52 :   A(valence+ 6,1        ) = 0.375;
      73          48 :   A(valence+ 6,valence  ) = 0.125;
      74          48 :   A(valence+ 7,1        ) = 0.375;
      75          52 :   A(valence+ 8,1        ) = 0.375;
      76          48 :   A(valence+ 8,2        ) = 0.125;
      77          52 :   A(valence+ 9,1        ) = 0.125;
      78          48 :   A(valence+ 9,valence  ) = 0.375;
      79          48 :   A(valence+10,valence  ) = 0.375;
      80          52 :   A(valence+11,valence-1) = 0.125;
      81          48 :   A(valence+11,valence  ) = 0.375;
      82             : 
      83             :   // Next, set the static S22 part
      84          48 :   A(valence+ 1,valence+1) = 0.125;
      85          48 :   A(valence+ 2,valence+1) = 0.0625;
      86          48 :   A(valence+ 2,valence+2) = 0.0625;
      87          48 :   A(valence+ 2,valence+3) = 0.0625;
      88          48 :   A(valence+ 3,valence+3) = 0.125;
      89          48 :   A(valence+ 4,valence+1) = 0.0625;
      90          48 :   A(valence+ 4,valence+4) = 0.0625;
      91          48 :   A(valence+ 4,valence+5) = 0.0625;
      92          48 :   A(valence+ 5,valence+5) = 0.125;
      93          48 :   A(valence+ 6,valence+1) = 0.375;
      94          48 :   A(valence+ 6,valence+2) = 0.125;
      95          48 :   A(valence+ 7,valence+1) = 0.125;
      96          48 :   A(valence+ 7,valence+2) = 0.375;
      97          48 :   A(valence+ 7,valence+3) = 0.125;
      98          48 :   A(valence+ 8,valence+2) = 0.125;
      99          48 :   A(valence+ 8,valence+3) = 0.375;
     100          48 :   A(valence+ 9,valence+1) = 0.375;
     101          48 :   A(valence+ 9,valence+4) = 0.125;
     102          48 :   A(valence+10,valence+1) = 0.125;
     103          48 :   A(valence+10,valence+4) = 0.375;
     104          48 :   A(valence+10,valence+5) = 0.125;
     105          48 :   A(valence+11,valence+4) = 0.125;
     106          48 :   A(valence+11,valence+5) = 0.375;
     107             : 
     108             :   // Last, set the S11 part: first row
     109           4 :   std::vector<Real> weights;
     110          48 :   loop_subdivision_mask(weights, valence);
     111         288 :   for (unsigned int i = 0; i <= valence; ++i)
     112         280 :     A(0,i) = weights[i];
     113             : 
     114             :   // second row
     115          48 :   A(1,0) = 0.375;
     116          48 :   A(1,1) = 0.375;
     117          48 :   A(1,2) = 0.125;
     118          48 :   A(1,valence) = 0.125;
     119             : 
     120             :   // third to second-to-last rows
     121         144 :   for (unsigned int i = 2; i < valence; ++i)
     122             :     {
     123          96 :       A(i,0  ) = 0.375;
     124         104 :       A(i,i-1) = 0.125;
     125          96 :       A(i,i  ) = 0.375;
     126         104 :       A(i,i+1) = 0.125;
     127             :     }
     128             : 
     129             :   // last row
     130          48 :   A(valence,0) = 0.375;
     131          48 :   A(valence,1) = 0.125;
     132          48 :   A(valence,valence-1) = 0.125;
     133          48 :   A(valence,valence  ) = 0.375;
     134          48 : }
     135             : 
     136             : 
     137             : 
     138       36864 : Real FESubdivision::regular_shape(const unsigned int i,
     139             :                                   const Real v,
     140             :                                   const Real w)
     141             : {
     142             :   // These are the 12 quartic box splines, see Cirak et al.,
     143             :   // Int. J. Numer. Meth. Engng. 2000; 47:2039-2072, Appendix A.1.
     144             : 
     145       36864 :   const Real u = 1 - v - w;
     146        3072 :   libmesh_assert_less_equal(0, v);
     147        3072 :   libmesh_assert_less_equal(0, w);
     148        3072 :   libmesh_assert_less_equal(0, u);
     149             : 
     150             :   using Utility::pow;
     151        3072 :   const Real factor = 1. / 12;
     152             : 
     153       36864 :   switch (i)
     154             :     {
     155         512 :     case 0:
     156        3072 :       return factor*(pow<4>(u) + 2*u*u*u*v);
     157         512 :     case 1:
     158        3072 :       return factor*(pow<4>(u) + 2*u*u*u*w);
     159         512 :     case 2:
     160        3328 :       return factor*(pow<4>(u) + 2*u*u*u*w + 6*u*u*u*v + 6*u*u*v*w + 12*u*u*v*v + 6*u*v*v*w + 6*u*v*v*v +
     161        3328 :                      2*v*v*v*w + pow<4>(v));
     162         512 :     case 3:
     163        3072 :       return factor*(6*pow<4>(u) + 24*u*u*u*w + 24*u*u*w*w + 8*u*w*w*w + pow<4>(w) + 24*u*u*u*v +
     164        4096 :                      60*u*u*v*w + 36*u*v*w*w + 6*v*w*w*w + 24*u*u*v*v + 36*u*v*v*w + 12*v*v*w*w + 8*u*v*v*v +
     165        3328 :                      6*v*v*v*w + pow<4>(v));
     166         512 :     case 4:
     167        3072 :       return factor*(pow<4>(u) + 6*u*u*u*w + 12*u*u*w*w + 6*u*w*w*w + pow<4>(w) + 2*u*u*u*v + 6*u*u*v*w +
     168        3072 :                      6*u*v*w*w + 2*v*w*w*w);
     169        3072 :     case 5:
     170        3328 :       return factor*(2*u*v*v*v + pow<4>(v));
     171         512 :     case 6:
     172        3072 :       return factor*(pow<4>(u) + 6*u*u*u*w + 12*u*u*w*w + 6*u*w*w*w + pow<4>(w) + 8*u*u*u*v + 36*u*u*v*w +
     173        3328 :                      36*u*v*w*w + 8*v*w*w*w + 24*u*u*v*v + 60*u*v*v*w + 24*v*v*w*w + 24*u*v*v*v + 24*v*v*v*w + 6*pow<4>(v));
     174         512 :     case 7:
     175        3072 :       return factor*(pow<4>(u) + 8*u*u*u*w + 24*u*u*w*w + 24*u*w*w*w + 6*pow<4>(w) + 6*u*u*u*v + 36*u*u*v*w +
     176        3328 :                      60*u*v*w*w + 24*v*w*w*w + 12*u*u*v*v + 36*u*v*v*w + 24*v*v*w*w + 6*u*v*v*v + 8*v*v*v*w + pow<4>(v));
     177        3072 :     case 8:
     178        3328 :       return factor*(2*u*w*w*w + pow<4>(w));
     179        3072 :     case 9:
     180        3328 :       return factor*(2*v*v*v*w + pow<4>(v));
     181        3072 :     case 10:
     182        3072 :       return factor*(2*u*w*w*w + pow<4>(w) + 6*u*v*w*w + 6*v*w*w*w + 6*u*v*v*w + 12*v*v*w*w + 2*u*v*v*v +
     183        3328 :                      6*v*v*v*w + pow<4>(v));
     184         512 :     case 11:
     185        3072 :       return factor*(pow<4>(w) + 2*v*w*w*w);
     186             : 
     187           0 :     default:
     188           0 :       libmesh_error_msg("Invalid i = " << i);
     189             :     }
     190             : }
     191             : 
     192             : 
     193             : 
     194       73728 : Real FESubdivision::regular_shape_deriv(const unsigned int i,
     195             :                                         const unsigned int j,
     196             :                                         const Real v,
     197             :                                         const Real w)
     198             : {
     199       73728 :   const Real u = 1 - v - w;
     200        6144 :   const Real factor = 1. / 12;
     201             : 
     202       73728 :   switch (j) // j=0: xi-directional derivative, j=1: eta-directional derivative
     203             :     {
     204       36864 :     case 0: // xi derivatives
     205             :       {
     206       36864 :         switch (i) // shape function number
     207             :           {
     208        3072 :           case 0:
     209        3072 :             return factor*(-6*v*u*u - 2*u*u*u);
     210        3072 :           case 1:
     211        3072 :             return factor*(-4*u*u*u - 6*u*u*w);
     212        3072 :           case 2:
     213        3072 :             return factor*(-2*v*v*v - 6*v*v*u + 6*v*u*u + 2*u*u*u);
     214        3072 :           case 3:
     215        3584 :             return factor*(-4*v*v*v - 24*v*v*u - 24*v*u*u - 18*v*v*w - 48*v*u*w - 12*u*u*w -
     216        3072 :                            12*v*w*w - 12*u*w*w - 2*w*w*w);
     217        3072 :           case 4:
     218        3072 :             return factor*(-6*v*u*u - 2*u*u*u - 12*v*u*w-12*u*u*w - 6*v*w*w - 18*u*w*w - 4*w*w*w);
     219        3072 :           case 5:
     220        3072 :             return factor*(2*v*v*v + 6*v*v*u);
     221        3072 :           case 6:
     222        3584 :             return factor*(24*v*v*u + 24*v*u*u + 4*u*u*u + 12*v*v*w + 48*v*u*w + 18*u*u*w +
     223        3072 :                            12*v*w*w + 12*u*w*w + 2*w*w*w);
     224        3072 :           case 7:
     225        3584 :             return factor*(-2*v*v*v - 6*v*v*u + 6*v*u*u + 2*u*u*u - 12*v*v*w + 12*u*u*w -
     226        3072 :                            12*v*w*w + 12*u*w*w);
     227        3072 :           case 8:
     228        3072 :             return -w*w*w/6;
     229        3072 :           case 9:
     230        3072 :             return factor*(4*v*v*v + 6*v*v*w);
     231        3072 :           case 10:
     232        3072 :             return factor*(2*v*v*v + 6*v*v*u + 12*v*v*w + 12*v*u*w + 18*v*w*w + 6*u*w*w + 4*w*w*w);
     233        3072 :           case 11:
     234        3072 :             return w*w*w/6;
     235           0 :           default:
     236           0 :             libmesh_error_msg("Invalid i = " << i);
     237             :           }
     238             :       }
     239       36864 :     case 1: // eta derivatives
     240             :       {
     241       36864 :         switch (i) // shape function number
     242             :           {
     243        3072 :           case 0:
     244        3072 :             return factor*(-6*v*u*u - 4*u*u*u);
     245        3072 :           case 1:
     246        3072 :             return factor*(-2*u*u*u - 6*u*u*w);
     247        3072 :           case 2:
     248        3584 :             return factor*(-4*v*v*v - 18*v*v*u - 12*v*u*u - 2*u*u*u - 6*v*v*w - 12*v*u*w -
     249        3072 :                            6*u*u*w);
     250        3072 :           case 3:
     251        3584 :             return factor*(-2*v*v*v-12*v*v*u - 12*v*u*u - 12*v*v*w - 48*v*u*w - 24*u*u*w -
     252        3072 :                            18*v*w*w - 24*u*w*w - 4*w*w*w);
     253        3072 :           case 4:
     254        3072 :             return factor*(2*u*u*u + 6*u*u*w - 6*u*w*w - 2*w*w*w);
     255        3072 :           case 5:
     256        3072 :             return -v*v*v/6;
     257        3072 :           case 6:
     258        3584 :             return factor*(12*v*v*u + 12*v*u*u + 2*u*u*u - 12*v*v*w + 6*u*u*w - 12*v*w*w -
     259        3072 :                            6*u*w*w - 2*w*w*w);
     260        3072 :           case 7:
     261        3584 :             return factor*(2*v*v*v + 12*v*v*u + 18*v*u*u + 4*u*u*u + 12*v*v*w + 48*v*u*w +
     262        3072 :                            24*u*u*w + 12*v*w*w + 24*u*w*w);
     263        3072 :           case 8:
     264        3072 :             return factor*(6*u*w*w + 2*w*w*w);
     265        3072 :           case 9:
     266        3072 :             return v*v*v/6;
     267        3072 :           case 10:
     268        3584 :             return factor*(4*v*v*v + 6*v*v*u + 18*v*v*w + 12*v*u*w + 12*v*w*w + 6*u*w*w +
     269        3072 :                            2*w*w*w);
     270        3072 :           case 11:
     271        3072 :             return factor*(6*v*w*w + 4*w*w*w);
     272           0 :           default:
     273           0 :             libmesh_error_msg("Invalid i = " << i);
     274             :           }
     275             :       }
     276           0 :     default:
     277           0 :       libmesh_error_msg("Invalid j = " << j);
     278             :     }
     279             : }
     280             : 
     281             : 
     282             : 
     283             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     284      110592 : Real FESubdivision::regular_shape_second_deriv(const unsigned int i,
     285             :                                                const unsigned int j,
     286             :                                                const Real v,
     287             :                                                const Real w)
     288             : {
     289      110592 :   const Real u = 1 - v - w;
     290        9216 :   const Real factor = 1. / 12;
     291             : 
     292      110592 :   switch (j)
     293             :     {
     294       36864 :     case 0: // xi-xi derivative
     295             :       {
     296       36864 :         switch (i) // shape function number
     297             :           {
     298        3072 :           case 0:
     299        3072 :             return v*u;
     300        3072 :           case 1:
     301        3072 :             return u*u + u*w;
     302        3072 :           case 2:
     303        3072 :             return -2*v*u;
     304        3072 :           case 3:
     305        3072 :             return v*v - 2*u*u + v*w - 2*u*w;
     306        3072 :           case 4:
     307        3072 :             return v*u + v*w + u*w + w*w;
     308        3072 :           case 5:
     309        3072 :             return v*u;
     310        3072 :           case 6:
     311        3072 :             return factor*(-24*v*v + 12*u*u - 24*v*w + 12*u*w);
     312        3072 :           case 7:
     313        3072 :             return -2*v*u - 2*v*w - 2*u*w - 2*w*w;
     314         256 :           case 8:
     315         256 :             return 0.;
     316        3072 :           case 9:
     317        3072 :             return v*v + v*w;
     318        3072 :           case 10:
     319        3072 :             return v*u + v*w + u*w + w*w;
     320         256 :           case 11:
     321         256 :             return 0.;
     322           0 :           default:
     323           0 :             libmesh_error_msg("Invalid i = " << i);
     324             :           }
     325             :       }
     326       36864 :     case 1: //eta-xi derivative
     327             :       {
     328       36864 :         switch (i)
     329             :           {
     330        3072 :           case 0:
     331        3072 :             return factor*(12*v*u + 6*u*u);
     332        3072 :           case 1:
     333        3072 :             return factor*(6*u*u + 12*u*w);
     334        3072 :           case 2:
     335        3072 :             return factor*(6*v*v - 12*v*u - 6*u*u);
     336        3072 :           case 3:
     337        3072 :             return factor*(6*v*v - 12*u*u + 24*v*w + 6*w*w);
     338        3072 :           case 4:
     339        3072 :             return factor*(-6*u*u - 12*u*w + 6*w*w);
     340        3072 :           case 5:
     341        3072 :             return -v*v/2.;
     342        3072 :           case 6:
     343        3072 :             return factor*(-12*v*v + 6*u*u - 24*v*w - 12*u*w - 6*w*w);
     344        3072 :           case 7:
     345        3072 :             return factor*(-6*v*v - 12*v*u + 6*u*u - 24*v*w - 12*w*w);
     346        3072 :           case 8:
     347        3072 :             return -w*w/2.;
     348        3072 :           case 9:
     349        3072 :             return v*v/2.;
     350        3072 :           case 10:
     351        3072 :             return factor*(6*v*v + 12*v*u + 24*v*w + 12*u*w + 6*w*w);
     352        3072 :           case 11:
     353        3072 :             return w*w/2.;
     354           0 :           default:
     355           0 :             libmesh_error_msg("Invalid i = " << i);
     356             :           }
     357             :       }
     358       36864 :     case 2: // eta-eta derivative
     359             :       {
     360       36864 :         switch (i)
     361             :           {
     362        3072 :           case 0:
     363        3072 :             return v*u + u*u;
     364        3072 :           case 1:
     365        3072 :             return u*w;
     366        3072 :           case 2:
     367        3072 :             return v*v + v*u + v*w + u*w;
     368        3072 :           case 3:
     369        3072 :             return -2*v*u - 2*u*u + v*w + w*w;
     370        3072 :           case 4:
     371        3072 :             return -2*u*w;
     372         256 :           case 5:
     373         256 :             return 0.;
     374        3072 :           case 6:
     375        3072 :             return -2*v*v - 2*v*u - 2*v*w - 2*u*w;
     376        3072 :           case 7:
     377        3072 :             return v*u + u*u - 2*v*w - 2*w*w;
     378        3072 :           case 8:
     379        3072 :             return u*w;
     380         256 :           case 9:
     381         256 :             return 0.;
     382        3072 :           case 10:
     383        3072 :             return v*v + v*u + v*w + u*w;
     384        3072 :           case 11:
     385        3072 :             return v*w + w*w;
     386           0 :           default:
     387           0 :             libmesh_error_msg("Invalid i = " << i);
     388             :           }
     389             :       }
     390           0 :     default:
     391           0 :       libmesh_error_msg("Invalid j = " << j);
     392             :     }
     393             : }
     394             : 
     395             : #endif //LIBMESH_ENABLE_SECOND_DERIVATIVES
     396             : 
     397             : 
     398          48 : void FESubdivision::loop_subdivision_mask(std::vector<Real> & weights,
     399             :                                           const unsigned int valence)
     400             : {
     401           4 :   libmesh_assert_greater(valence, 0);
     402          48 :   const Real cs = std::cos(2 * libMesh::pi / valence);
     403          48 :   const Real nb_weight = (0.625 - Utility::pow<2>(0.375 + 0.25 * cs)) / valence;
     404          48 :   weights.resize(1 + valence, nb_weight);
     405          48 :   weights[0] = 1.0 - valence * nb_weight;
     406          48 : }
     407             : 
     408             : 
     409             : 
     410        3072 : void FESubdivision::init_shape_functions(const std::vector<Point> & qp,
     411             :                                          const Elem * elem)
     412             : {
     413         256 :   libmesh_assert(elem);
     414         256 :   libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION);
     415         256 :   const Tri3Subdivision * sd_elem = static_cast<const Tri3Subdivision *>(elem);
     416             : 
     417         512 :   LOG_SCOPE("init_shape_functions()", "FESubdivision");
     418             : 
     419        3072 :   calculations_started = true;
     420             : 
     421        3072 :   const unsigned int valence = sd_elem->get_ordered_valence(0);
     422         512 :   const unsigned int n_qp = cast_int<unsigned int>(qp.size());
     423        3072 :   const unsigned int n_approx_shape_functions = valence + 6;
     424             : 
     425             :   // resize the vectors to hold current data
     426        3072 :   phi.resize         (n_approx_shape_functions);
     427        3072 :   dphi.resize        (n_approx_shape_functions);
     428        3072 :   dphidxi.resize     (n_approx_shape_functions);
     429        3072 :   dphideta.resize    (n_approx_shape_functions);
     430             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     431        3072 :   d2phi.resize       (n_approx_shape_functions);
     432        3072 :   d2phidxi2.resize   (n_approx_shape_functions);
     433        3072 :   d2phidxideta.resize(n_approx_shape_functions);
     434        3072 :   d2phideta2.resize  (n_approx_shape_functions);
     435             : #endif
     436             : 
     437       39840 :   for (unsigned int i = 0; i < n_approx_shape_functions; ++i)
     438             :     {
     439       39832 :       phi[i].resize         (n_qp);
     440       39832 :       dphi[i].resize        (n_qp);
     441       39832 :       dphidxi[i].resize     (n_qp);
     442       39832 :       dphideta[i].resize    (n_qp);
     443             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     444       39832 :       d2phi[i].resize       (n_qp);
     445       39832 :       d2phidxi2[i].resize   (n_qp);
     446       39832 :       d2phidxideta[i].resize(n_qp);
     447       39832 :       d2phideta2[i].resize  (n_qp);
     448             : #endif
     449             :     }
     450             : 
     451             :   // Renumbering of the shape functions
     452             :   static const unsigned int cvi[12] = {3,6,2,0,1,4,7,10,9,5,11,8};
     453             : 
     454        3072 :   if (valence == 6) // This means that all vertices are regular, i.e. we have 12 shape functions
     455             :     {
     456       39312 :       for (unsigned int i = 0; i < n_approx_shape_functions; ++i)
     457             :         {
     458       72576 :           for (unsigned int p = 0; p < n_qp; ++p)
     459             :             {
     460       39312 :               phi[i][p]          = FE<2,SUBDIVISION>::shape             (elem, fe_type.order, cvi[i],    qp[p]);
     461       39312 :               dphidxi[i][p]      = FE<2,SUBDIVISION>::shape_deriv       (elem, fe_type.order, cvi[i], 0, qp[p]);
     462       39312 :               dphideta[i][p]     = FE<2,SUBDIVISION>::shape_deriv       (elem, fe_type.order, cvi[i], 1, qp[p]);
     463       45360 :               dphi[i][p](0)      = dphidxi[i][p];
     464       36288 :               dphi[i][p](1)      = dphideta[i][p];
     465             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     466       39312 :               d2phidxi2[i][p]    = FE<2,SUBDIVISION>::shape_second_deriv(elem, fe_type.order, cvi[i], 0, qp[p]);
     467       39312 :               d2phidxideta[i][p] = FE<2,SUBDIVISION>::shape_second_deriv(elem, fe_type.order, cvi[i], 1, qp[p]);
     468       39312 :               d2phideta2[i][p]   = FE<2,SUBDIVISION>::shape_second_deriv(elem, fe_type.order, cvi[i], 2, qp[p]);
     469       45360 :               d2phi[i][p](0,0)   = d2phidxi2[i][p];
     470       39312 :               d2phi[i][p](0,1)   = d2phi[i][p](1,0) = d2phidxideta[i][p];
     471       36288 :               d2phi[i][p](1,1)   = d2phideta2[i][p];
     472             : #endif
     473             :             }
     474             :         }
     475             :     }
     476             :   else // vertex 0 is irregular by construction of the mesh
     477             :     {
     478             :       static const Real eps = 1e-10;
     479             : 
     480             :       // temporary values
     481          52 :       std::vector<Real> tphi(12);
     482          52 :       std::vector<Real> tdphidxi(12);
     483          52 :       std::vector<Real> tdphideta(12);
     484             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     485          52 :       std::vector<Real> td2phidxi2(12);
     486          52 :       std::vector<Real> td2phidxideta(12);
     487          52 :       std::vector<Real> td2phideta2(12);
     488             : #endif
     489             : 
     490          96 :       for (unsigned int p = 0; p < n_qp; ++p)
     491             :         {
     492             :           // evaluate the number of the required subdivisions
     493          48 :           Real v = qp[p](0);
     494          48 :           Real w = qp[p](1);
     495          48 :           Real u = 1 - v - w;
     496           4 :           Real min = 0, max = 0.5;
     497           4 :           int n = 0;
     498          48 :           while (!(u > min-eps && u < max+eps))
     499             :             {
     500           0 :               ++n;
     501           0 :               min = max;
     502           0 :               max += std::pow((Real)(2), -n-1);
     503             :             }
     504             : 
     505             :           // transform u, v and w according to the number of subdivisions required.
     506           4 :           const Real pow2 = std::pow((Real)(2), n);
     507          48 :           v *= pow2;
     508          48 :           w *= pow2;
     509           4 :           u = 1 - v - w;
     510           4 :           libmesh_assert_less(u, 0.5 + eps);
     511           4 :           libmesh_assert_greater(u, -eps);
     512             : 
     513             :           // find out in which subdivided patch we are and setup the "selection matrix" P and the transformation Jacobian
     514             :           // (see Int. J. Numer. Meth. Engng. 2000; 47:2039-2072, Appendix A.2.)
     515          48 :           const int k = n+1;
     516             :           Real jfac; // the additional factor per derivative order
     517          56 :           DenseMatrix<Real> P(12, valence+12);
     518          48 :           if (v > 0.5 - eps)
     519             :             {
     520           0 :               v = 2*v - 1;
     521           0 :               w = 2*w;
     522           0 :               jfac = std::pow((Real)(2), k);
     523           0 :               P( 0,2        ) = 1;
     524           0 :               P( 1,0        ) = 1;
     525           0 :               P( 2,valence+3) = 1;
     526           0 :               P( 3,1        ) = 1;
     527           0 :               P( 4,valence  ) = 1;
     528           0 :               P( 5,valence+8) = 1;
     529           0 :               P( 6,valence+2) = 1;
     530           0 :               P( 7,valence+1) = 1;
     531           0 :               P( 8,valence+4) = 1;
     532           0 :               P( 9,valence+7) = 1;
     533           0 :               P(10,valence+6) = 1;
     534           0 :               P(11,valence+9) = 1;
     535             :             }
     536          48 :           else if (w > 0.5 - eps)
     537             :             {
     538           0 :               v = 2*v;
     539           0 :               w = 2*w - 1;
     540           0 :               jfac = std::pow((Real)(2), k);
     541           0 :               P( 0,0         ) = 1;
     542           0 :               P( 1,valence- 1) = 1;
     543           0 :               P( 2,1         ) = 1;
     544           0 :               P( 3,valence   ) = 1;
     545           0 :               P( 4,valence+ 5) = 1;
     546           0 :               P( 5,valence+ 2) = 1;
     547           0 :               P( 6,valence+ 1) = 1;
     548           0 :               P( 7,valence+ 4) = 1;
     549           0 :               P( 8,valence+11) = 1;
     550           0 :               P( 9,valence+ 6) = 1;
     551           0 :               P(10,valence+ 9) = 1;
     552           0 :               P(11,valence+10) = 1;
     553             :             }
     554             :           else
     555             :             {
     556          48 :               v = 1 - 2*v;
     557          48 :               w = 1 - 2*w;
     558           4 :               jfac = std::pow((Real)(-2), k);
     559          52 :               P( 0,valence+9) = 1;
     560          48 :               P( 1,valence+6) = 1;
     561          48 :               P( 2,valence+4) = 1;
     562          48 :               P( 3,valence+1) = 1;
     563          48 :               P( 4,valence+2) = 1;
     564          52 :               P( 5,valence+5) = 1;
     565          48 :               P( 6,valence  ) = 1;
     566          48 :               P( 7,1        ) = 1;
     567          48 :               P( 8,valence+3) = 1;
     568          52 :               P( 9,valence-1) = 1;
     569          48 :               P(10,0        ) = 1;
     570          48 :               P(11,2        ) = 1;
     571             :             }
     572             : 
     573          48 :           u = 1 - v - w;
     574          48 :           libmesh_error_msg_if((u > 1 + eps) || (u < -eps), "SUBDIVISION irregular patch: u is outside valid range!");
     575             : 
     576          56 :           DenseMatrix<Real> A;
     577          48 :           init_subdivision_matrix(A, valence);
     578             : 
     579             :           // compute P*A^k
     580          48 :           if (k > 1)
     581             :             {
     582           0 :               DenseMatrix<Real> Acopy(A);
     583           0 :               for (int e = 1; e < k; ++e)
     584           0 :                 A.right_multiply(Acopy);
     585           0 :             }
     586          48 :           P.right_multiply(A);
     587             : 
     588           4 :           const Point transformed_p(v,w);
     589             : 
     590         624 :           for (unsigned int i = 0; i < 12; ++i)
     591             :             {
     592         576 :               tphi[i]          = FE<2,SUBDIVISION>::shape             (elem, fe_type.order, i,    transformed_p);
     593         576 :               tdphidxi[i]      = FE<2,SUBDIVISION>::shape_deriv       (elem, fe_type.order, i, 0, transformed_p);
     594         576 :               tdphideta[i]     = FE<2,SUBDIVISION>::shape_deriv       (elem, fe_type.order, i, 1, transformed_p);
     595             : 
     596             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     597         576 :               td2phidxi2[i]    = FE<2,SUBDIVISION>::shape_second_deriv(elem, fe_type.order, i, 0, transformed_p);
     598         576 :               td2phidxideta[i] = FE<2,SUBDIVISION>::shape_second_deriv(elem, fe_type.order, i, 1, transformed_p);
     599         576 :               td2phideta2[i]   = FE<2,SUBDIVISION>::shape_second_deriv(elem, fe_type.order, i, 2, transformed_p);
     600             : #endif
     601             :             }
     602             : 
     603             :           // Finally, we can compute the irregular shape functions as the product of P
     604             :           // and the regular shape functions:
     605             :           Real sum1, sum2, sum3, sum4, sum5, sum6;
     606         528 :           for (unsigned int j = 0; j < n_approx_shape_functions; ++j)
     607             :             {
     608          40 :               sum1 = sum2 = sum3 = sum4 = sum5 = sum6 = 0;
     609        6240 :               for (unsigned int i = 0; i < 12; ++i)
     610             :                 {
     611        5760 :                   sum1 += P(i,j) * tphi[i];
     612        5760 :                   sum2 += P(i,j) * tdphidxi[i];
     613        5760 :                   sum3 += P(i,j) * tdphideta[i];
     614             : 
     615             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     616        5760 :                   sum4 += P(i,j) * td2phidxi2[i];
     617        5760 :                   sum5 += P(i,j) * td2phidxideta[i];
     618        6240 :                   sum6 += P(i,j) * td2phideta2[i];
     619             : #endif
     620             :                 }
     621         520 :               phi[j][p]          = sum1;
     622         520 :               dphidxi[j][p]      = sum2 * jfac;
     623         520 :               dphideta[j][p]     = sum3 * jfac;
     624         520 :               dphi[j][p](0)      = dphidxi[j][p];
     625         480 :               dphi[j][p](1)      = dphideta[j][p];
     626             : 
     627             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     628         520 :               d2phidxi2[j][p]    = sum4 * jfac * jfac;
     629         520 :               d2phidxideta[j][p] = sum5 * jfac * jfac;
     630         520 :               d2phideta2[j][p]   = sum6 * jfac * jfac;
     631         520 :               d2phi[j][p](0,0)   = d2phidxi2[j][p];
     632         480 :               d2phi[j][p](0,1)   = d2phi[j][p](1,0) = d2phidxideta[j][p];
     633         480 :               d2phi[j][p](1,1)   = d2phideta2[j][p];
     634             : #endif
     635             :             }
     636          40 :         } // end quadrature loop
     637             :     } // end irregular vertex
     638             : 
     639             :   // Let the FEMap use the same initialized shape functions
     640        3072 :   this->_fe_map->get_phi_map()          = phi;
     641        3072 :   this->_fe_map->get_dphidxi_map()      = dphidxi;
     642        3072 :   this->_fe_map->get_dphideta_map()     = dphideta;
     643             : 
     644             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     645        3072 :   this->_fe_map->get_d2phidxi2_map()    = d2phidxi2;
     646        3072 :   this->_fe_map->get_d2phideta2_map()   = d2phideta2;
     647        3072 :   this->_fe_map->get_d2phidxideta_map() = d2phidxideta;
     648             : #endif
     649             : 
     650        3072 :   if (this->calculate_dual)
     651           0 :     this->init_dual_shape_functions(n_approx_shape_functions, n_qp);
     652        3072 : }
     653             : 
     654             : 
     655             : 
     656          71 : void FESubdivision::attach_quadrature_rule(QBase * q)
     657             : {
     658           2 :   libmesh_assert(q);
     659             : 
     660          71 :   qrule = q;
     661             :   // make sure we don't cache results from a previous quadrature rule
     662          71 :   this->_elem = nullptr;
     663          71 :   this->_elem_type = INVALID_ELEM;
     664          71 :   return;
     665             : }
     666             : 
     667             : 
     668             : 
     669        3072 : void FESubdivision::reinit(const Elem * elem,
     670             :                            const std::vector<Point> * const libmesh_dbg_var(pts),
     671             :                            const std::vector<Real> * const)
     672             : {
     673         256 :   libmesh_assert(elem);
     674         256 :   libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION);
     675             : #ifndef NDEBUG
     676         256 :   const Tri3Subdivision * sd_elem = static_cast<const Tri3Subdivision *>(elem);
     677             : #endif
     678             : 
     679         512 :   LOG_SCOPE("reinit()", "FESubdivision");
     680             : 
     681         256 :   libmesh_assert(!sd_elem->is_ghost());
     682         256 :   libmesh_assert(sd_elem->is_subdivision_updated());
     683             : 
     684             :   // check if vertices 1 and 2 are regular
     685         256 :   libmesh_assert_equal_to(sd_elem->get_ordered_valence(1), 6);
     686         256 :   libmesh_assert_equal_to(sd_elem->get_ordered_valence(2), 6);
     687             : 
     688             :   // We're calculating now!  Time to determine what.
     689        3072 :   this->determine_calculations();
     690             : 
     691             :   // no custom quadrature support
     692         256 :   libmesh_assert(pts == nullptr);
     693         256 :   libmesh_assert(qrule);
     694        3072 :   qrule->init(*elem);
     695             : 
     696             :   // Initialize the shape functions
     697        3328 :   this->init_shape_functions(this->qrule->get_points(), elem);
     698             : 
     699             :   // The shape functions correspond to the qrule
     700        3072 :   shapes_on_quadrature = true;
     701             : 
     702             :   // Compute the map for this element.
     703        3328 :   this->_fe_map->compute_map (this->dim, this->qrule->get_weights(), elem, this->calculate_d2phi);
     704        3072 : }
     705             : 
     706             : 
     707             : 
     708             : template <>
     709       36864 : Real FE<2,SUBDIVISION>::shape(const ElemType type,
     710             :                               const Order order,
     711             :                               const unsigned int i,
     712             :                               const Point & p)
     713             : {
     714       36864 :   switch (order)
     715             :     {
     716       36864 :     case FOURTH:
     717             :       {
     718       36864 :         switch (type)
     719             :           {
     720       36864 :           case TRI3SUBDIVISION:
     721        3072 :             libmesh_assert_less(i, 12);
     722       36864 :             return FESubdivision::regular_shape(i,p(0),p(1));
     723           0 :           default:
     724           0 :             libmesh_error_msg("ERROR: Unsupported element type == " << Utility::enum_to_string(type));
     725             :           }
     726             :       }
     727           0 :     default:
     728           0 :       libmesh_error_msg("ERROR: Unsupported polynomial order == " << order);
     729             :     }
     730             : }
     731             : 
     732             : 
     733             : 
     734             : template <>
     735       36864 : Real FE<2,SUBDIVISION>::shape(const Elem * elem,
     736             :                               const Order order,
     737             :                               const unsigned int i,
     738             :                               const Point & p,
     739             :                               const bool add_p_level)
     740             : {
     741        3072 :   libmesh_assert(elem);
     742       39936 :   const Order totalorder = order + add_p_level*elem->p_level();
     743       36864 :   return FE<2,SUBDIVISION>::shape(elem->type(), totalorder, i, p);
     744             : }
     745             : 
     746             : 
     747             : template <>
     748           0 : Real FE<2,SUBDIVISION>::shape(const FEType fet,
     749             :                               const Elem * elem,
     750             :                               const unsigned int i,
     751             :                               const Point & p,
     752             :                               const bool add_p_level)
     753             : {
     754           0 :   libmesh_assert(elem);
     755           0 :   const Order totalorder = fet.order + add_p_level*elem->p_level();
     756           0 :   return FE<2,SUBDIVISION>::shape(elem->type(), totalorder, i, p);
     757             : }
     758             : 
     759             : 
     760             : 
     761             : template <>
     762       73728 : Real FE<2,SUBDIVISION>::shape_deriv(const ElemType type,
     763             :                                     const Order order,
     764             :                                     const unsigned int i,
     765             :                                     const unsigned int j,
     766             :                                     const Point & p)
     767             : {
     768       73728 :   switch (order)
     769             :     {
     770       73728 :     case FOURTH:
     771             :       {
     772       73728 :         switch (type)
     773             :           {
     774       73728 :           case TRI3SUBDIVISION:
     775        6144 :             libmesh_assert_less(i, 12);
     776       73728 :             return FESubdivision::regular_shape_deriv(i,j,p(0),p(1));
     777           0 :           default:
     778           0 :             libmesh_error_msg("ERROR: Unsupported element type == " << Utility::enum_to_string(type));
     779             :           }
     780             :       }
     781           0 :     default:
     782           0 :       libmesh_error_msg("ERROR: Unsupported polynomial order == " << order);
     783             :     }
     784             : }
     785             : 
     786             : 
     787             : 
     788             : template <>
     789       73728 : Real FE<2,SUBDIVISION>::shape_deriv(const Elem * elem,
     790             :                                     const Order order,
     791             :                                     const unsigned int i,
     792             :                                     const unsigned int j,
     793             :                                     const Point & p,
     794             :                                     const bool add_p_level)
     795             : {
     796        6144 :   libmesh_assert(elem);
     797       79872 :   const Order totalorder = order + add_p_level*elem->p_level();
     798       73728 :   return FE<2,SUBDIVISION>::shape_deriv(elem->type(), totalorder, i, j, p);
     799             : }
     800             : 
     801             : 
     802             : template <>
     803           0 : Real FE<2,SUBDIVISION>::shape_deriv(const FEType fet,
     804             :                                     const Elem * elem,
     805             :                                     const unsigned int i,
     806             :                                     const unsigned int j,
     807             :                                     const Point & p,
     808             :                                     const bool add_p_level)
     809             : {
     810           0 :   libmesh_assert(elem);
     811           0 :   const Order totalorder = fet.order + add_p_level*elem->p_level();
     812           0 :   return FE<2,SUBDIVISION>::shape_deriv(elem->type(), totalorder, i, j, p);
     813             : }
     814             : 
     815             : 
     816             : #ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
     817             : 
     818             : template <>
     819      110592 : Real FE<2,SUBDIVISION>::shape_second_deriv(const ElemType type,
     820             :                                            const Order order,
     821             :                                            const unsigned int i,
     822             :                                            const unsigned int j,
     823             :                                            const Point & p)
     824             : {
     825      110592 :   switch (order)
     826             :     {
     827      110592 :     case FOURTH:
     828             :       {
     829      110592 :         switch (type)
     830             :           {
     831      110592 :           case TRI3SUBDIVISION:
     832        9216 :             libmesh_assert_less(i, 12);
     833      110592 :             return FESubdivision::regular_shape_second_deriv(i,j,p(0),p(1));
     834           0 :           default:
     835           0 :             libmesh_error_msg("ERROR: Unsupported element type == " << Utility::enum_to_string(type));
     836             :           }
     837             :       }
     838           0 :     default:
     839           0 :       libmesh_error_msg("ERROR: Unsupported polynomial order == " << order);
     840             :     }
     841             : }
     842             : 
     843             : 
     844             : 
     845             : template <>
     846      110592 : Real FE<2,SUBDIVISION>::shape_second_deriv(const Elem * elem,
     847             :                                            const Order order,
     848             :                                            const unsigned int i,
     849             :                                            const unsigned int j,
     850             :                                            const Point & p,
     851             :                                            const bool add_p_level)
     852             : {
     853        9216 :   libmesh_assert(elem);
     854      119808 :   const Order totalorder = order + add_p_level*elem->p_level();
     855      110592 :   return FE<2,SUBDIVISION>::shape_second_deriv(elem->type(), totalorder, i, j, p);
     856             : }
     857             : 
     858             : 
     859             : 
     860             : template <>
     861           0 : Real FE<2,SUBDIVISION>::shape_second_deriv(const FEType fet,
     862             :                                            const Elem * elem,
     863             :                                            const unsigned int i,
     864             :                                            const unsigned int j,
     865             :                                            const Point & p,
     866             :                                            const bool add_p_level)
     867             : {
     868           0 :   libmesh_assert(elem);
     869           0 :   const Order totalorder = fet.order + add_p_level*elem->p_level();
     870           0 :   return FE<2,SUBDIVISION>::shape_second_deriv(elem->type(), totalorder, i, j, p);
     871             : }
     872             : 
     873             : 
     874             : #endif // LIBMESH_ENABLE_SECOND_DERIVATIVES
     875             : 
     876             : template <>
     877       11664 : void FE<2,SUBDIVISION>::nodal_soln(const Elem * elem,
     878             :                                    const Order,
     879             :                                    const std::vector<Number> & elem_soln,
     880             :                                    std::vector<Number> & nodal_soln,
     881             :                                    const bool /*add_p_level*/,
     882             :                                    const unsigned)
     883             : {
     884         972 :   libmesh_assert(elem);
     885         972 :   libmesh_assert_equal_to(elem->type(), TRI3SUBDIVISION);
     886         972 :   const Tri3Subdivision * sd_elem = static_cast<const Tri3Subdivision *>(elem);
     887             : 
     888       11664 :   nodal_soln.resize(3); // three nodes per element
     889             : 
     890             :   // Ghost nodes are auxiliary.
     891       11664 :   if (sd_elem->is_ghost())
     892             :     {
     893        2244 :       nodal_soln[0] = 0;
     894        2244 :       nodal_soln[1] = 0;
     895        2244 :       nodal_soln[2] = 0;
     896        2448 :       return;
     897             :     }
     898             : 
     899             :   // First node (node 0 in the element patch):
     900        9216 :   unsigned int j = sd_elem->local_node_number(sd_elem->get_ordered_node(0)->id());
     901        9216 :   nodal_soln[j] = elem_soln[0];
     902             : 
     903             :   // Second node (node 1 in the element patch):
     904        9216 :   j = sd_elem->local_node_number(sd_elem->get_ordered_node(1)->id());
     905        9216 :   nodal_soln[j] = elem_soln[1];
     906             : 
     907             :   // Third node (node 'valence' in the element patch):
     908        9216 :   j = sd_elem->local_node_number(sd_elem->get_ordered_node(2)->id());
     909        9216 :   nodal_soln[j] = elem_soln[sd_elem->get_ordered_valence(0)];
     910             : }
     911             : 
     912             : 
     913             : 
     914             : // the empty template specializations below are needed to avoid
     915             : // linker reference errors, but should never get called
     916             : template <>
     917           0 : void FE<2,SUBDIVISION>::side_map(const Elem *,
     918             :                                  const Elem *,
     919             :                                  const unsigned int,
     920             :                                  const std::vector<Point> &,
     921             :                                  std::vector<Point> &)
     922             : {
     923           0 :   libmesh_not_implemented();
     924             : }
     925             : 
     926             : template <>
     927           0 : void FE<2,SUBDIVISION>::edge_reinit(Elem const *,
     928             :                                     unsigned int,
     929             :                                     Real,
     930             :                                     const std::vector<Point> * const,
     931             :                                     const std::vector<Real> * const)
     932             : {
     933           0 :   libmesh_not_implemented();
     934             : }
     935             : 
     936             : template <>
     937           0 : Point FE<2,SUBDIVISION>::inverse_map(const Elem *,
     938             :                                      const Point &,
     939             :                                      const Real,
     940             :                                      const bool)
     941             : {
     942           0 :   libmesh_not_implemented();
     943             : }
     944             : 
     945             : template <>
     946           0 : void FE<2,SUBDIVISION>::inverse_map(const Elem *,
     947             :                                     const std::vector<Point> &,
     948             :                                     std::vector<Point> &,
     949             :                                     Real,
     950             :                                     bool)
     951             : {
     952           0 :   libmesh_not_implemented();
     953             : }
     954             : 
     955             : 
     956             : 
     957             : // The number of dofs per subdivision element depends on the valence of its nodes, so it is non-static
     958           0 : template <> unsigned int FE<2,SUBDIVISION>::n_dofs(const ElemType, const Order) { libmesh_not_implemented(); return 0; }
     959           0 : template <> unsigned int FE<2,SUBDIVISION>::n_dofs(const Elem *, const Order) { libmesh_not_implemented(); return 0; }
     960             : 
     961             : // Loop subdivision elements have only a single dof per node
     962      562734 : template <> unsigned int FE<2,SUBDIVISION>::n_dofs_at_node(const ElemType, const Order, const unsigned int) { return 1; }
     963      138024 : template <> unsigned int FE<2,SUBDIVISION>::n_dofs_at_node(const Elem &, const Order, const unsigned int) { return 1; }
     964             : 
     965             : // Subdivision FEMs have dofs only at the nodes
     966           0 : template <> unsigned int FE<2,SUBDIVISION>::n_dofs_per_elem(const ElemType, const Order) { return 0; }
     967       70023 : template <> unsigned int FE<2,SUBDIVISION>::n_dofs_per_elem(const Elem &, const Order) { return 0; }
     968             : 
     969             : // Subdivision FEMs have dofs only at the nodes
     970           0 : template <> void FE<2,SUBDIVISION>::dofs_on_side(const Elem * const, const Order, unsigned int, std::vector<unsigned int> & di, bool) { di.resize(0); }
     971           0 : template <> void FE<2,SUBDIVISION>::dofs_on_edge(const Elem * const, const Order, unsigned int, std::vector<unsigned int> & di, bool) { di.resize(0); }
     972             : 
     973             : // Subdivision FEMs are C^1 continuous
     974           0 : template <> FEContinuity FE<2,SUBDIVISION>::get_continuity() const { return C_ONE; }
     975             : 
     976             : // Subdivision FEMs are not hierarchic
     977           0 : template <> bool FE<2,SUBDIVISION>::is_hierarchic() const { return false; }
     978             : 
     979             : // Subdivision FEM shapes need reinit
     980           0 : template <> bool FE<2,SUBDIVISION>::shapes_need_reinit() const { return true; }
     981             : 
     982           0 : LIBMESH_FE_SIDE_NODAL_SOLN_DIM(SUBDIVISION, 2)
     983             : 
     984             : } // namespace libMesh

Generated by: LCOV version 1.14