@@ -923,6 +923,7 @@ VariationalSmootherSystem::get_target_elem(const ElemType & type)
923923  const  auto ref_vol =  target_elem -> reference_elem ()-> volume ();
924924
925925  // Update the nodes of the target element, depending on type 
926+   const  Real  sqrt_2  =  std ::sqrt (Real (2 ));
926927  const  Real  sqrt_3  =  std ::sqrt (Real (3 ));
927928  std ::vector < std ::unique_ptr < Node >> owned_nodes ;
928929
@@ -1055,7 +1056,6 @@ VariationalSmootherSystem::get_target_elem(const ElemType & type)
10551056      // Solving for s: s = (3 sqrt(2) v)^(1/3), where v is the volume of the 
10561057      // non-optimal reference element. 
10571058
1058-       const  Real  sqrt_2  =  std ::sqrt (Real (2 ));
10591059      // Side length that preserves the volume of the reference element 
10601060      const  auto side_length  =  std ::pow (3.  *  sqrt_2  *  ref_vol , 1.  / 3. );
10611061      // Pyramid height with the property that all faces are equilateral triangles 
@@ -1110,6 +1110,67 @@ VariationalSmootherSystem::get_target_elem(const ElemType & type)
11101110
11111111    } // if Pyramid 
11121112
1113+   // Elems deriving from Tet 
1114+   else  if  (type_str .compare (0 , 3 , "TET" ) ==  0 )
1115+     {
1116+ 
1117+       // The ideal target element is a a regular tet with equilateral 
1118+       // triangles for all faces, with volume equal to the volume of the 
1119+       // reference element. 
1120+ 
1121+       // The volume of a tet is given by v = b * h / 3, where b is the area of 
1122+       // the base face and h is the height of the apex node. The area of an 
1123+       // equilateral triangle with side length s is b = sqrt(3) s^2 / 4. 
1124+       // For all faces to have side length s, the height of the apex node is 
1125+       // h = sqrt(2/3) * s. Then the volume is v = sqrt(2) * s^3 / 12. 
1126+       // Solving for s, the side length that will preserve the volume of the 
1127+       // reference element is s = (6 * sqrt(2) * v)^(1/3), where v is the volume 
1128+       // of the non-optimal reference element (i.e., a right tet). 
1129+ 
1130+       // Side length that preserves the volume of the reference element 
1131+       const  auto side_length  =  std ::cbrt (6.  *  sqrt_2  *  ref_vol );
1132+       // tet height with the property that all faces are equilateral triangles 
1133+       const  auto target_height =  sqrt_2  / sqrt_3  *  side_length ;
1134+ 
1135+       const  auto &  s  =  side_length ;
1136+       const  auto &  h  =  target_height ;
1137+ 
1138+       // For regular tet 
1139+       //                                         x        y                z     node_id 
1140+       owned_nodes .emplace_back (Node ::build (Point (0. ,      0. ,               0. ), 0 ));
1141+       owned_nodes .emplace_back (Node ::build (Point (s ,       0. ,               0. ), 1 ));
1142+       owned_nodes .emplace_back (Node ::build (Point (0.5  *  s , 0.5  *  sqrt_3  *  s , 0. ), 2 ));
1143+       owned_nodes .emplace_back (Node ::build (Point (0.5  *  s , sqrt_3  / 6.  *  s ,  h ),  3 ));
1144+ 
1145+       if  (type  ==  TET10  ||  type  ==  TET14 )
1146+         {
1147+           const  auto &  on  =  owned_nodes ;
1148+           // Define the edge midpoint nodes of the tet 
1149+ 
1150+           // Base node to base node midpoint nodes 
1151+           owned_nodes .emplace_back (Node ::build (Point ((* on [0 ] +  * on [1 ]) / 2. ), 4 ));
1152+           owned_nodes .emplace_back (Node ::build (Point ((* on [1 ] +  * on [2 ]) / 2. ), 5 ));
1153+           owned_nodes .emplace_back (Node ::build (Point ((* on [2 ] +  * on [0 ]) / 2. ), 6 ));
1154+           // Base node to apex node midpoint nodes 
1155+           owned_nodes .emplace_back (Node ::build (Point ((* on [0 ] +  * on [3 ]) / 2. ), 7 ));
1156+           owned_nodes .emplace_back (Node ::build (Point ((* on [1 ] +  * on [3 ]) / 2. ), 8 ));
1157+           owned_nodes .emplace_back (Node ::build (Point ((* on [2 ] +  * on [3 ]) / 2. ), 9 ));
1158+ 
1159+           if  (type  ==  TET14 )
1160+             {
1161+               // Define the face midpoint nodes of the tet 
1162+               owned_nodes .emplace_back (Node ::build (Point ((* on [0 ] +  * on [1 ] +  * on [2 ]) / 3. ), 10 ));
1163+               owned_nodes .emplace_back (Node ::build (Point ((* on [0 ] +  * on [1 ] +  * on [3 ]) / 3. ), 11 ));
1164+               owned_nodes .emplace_back (Node ::build (Point ((* on [1 ] +  * on [2 ] +  * on [3 ]) / 3. ), 12 ));
1165+               owned_nodes .emplace_back (Node ::build (Point ((* on [0 ] +  * on [2 ] +  * on [3 ]) / 3. ), 13 ));
1166+             }
1167+         }
1168+ 
1169+       else  if  (type  !=  TET4 )
1170+         libmesh_error_msg ("Unsupported tet element: "  << type_str );
1171+ 
1172+     } // if Tet 
1173+ 
11131174  // Set the target_elem equal to the reference elem 
11141175  else 
11151176    for  (const  auto &  node  : target_elem -> reference_elem ()-> node_ref_range ())
0 commit comments