2222#include "libmesh/number_lookups.h"
2323#include "libmesh/enum_to_string.h"
2424#include "libmesh/cell_tet4.h" // We need edge_nodes_map + side_nodes_map
25+ #include "libmesh/cell_prism6.h"
2526#include "libmesh/face_tri3.h" // Faster to construct these on the stack
27+ #include "libmesh/face_quad4.h"
2628
2729// Anonymous namespace for functions shared by HIERARCHIC and
2830// L2_HIERARCHIC implementations. Implementations appear at the bottom
@@ -35,6 +37,9 @@ unsigned int cube_side(const Point & p);
3537
3638Point cube_side_point (unsigned int sidenum , const Point & interior_point );
3739
40+ std ::array < unsigned int , 4 > oriented_prism_nodes (const Elem & elem ,
41+ unsigned int face_num );
42+
3843std ::array < unsigned int , 3 > oriented_tet_nodes (const Elem & elem ,
3944 unsigned int face_num );
4045
@@ -944,7 +949,8 @@ Real FE<3,SIDE_HIERARCHIC>::shape(const Elem * elem,
944949 const std ::array < unsigned int , 3 > face_vertex =
945950 oriented_tet_nodes (* elem , face_num );
946951
947- // We only need a Tri3 to evaluate L2_HIERARCHIC
952+ // We only need a Tri3 to evaluate L2_HIERARCHIC on the affine
953+ // master element
948954 Tri3 side ;
949955
950956 // We pinky swear not to modify these nodes
@@ -961,6 +967,136 @@ Real FE<3,SIDE_HIERARCHIC>::shape(const Elem * elem,
961967 basisnum , sidep , false);
962968 }
963969
970+ case PRISM20 :
971+ case PRISM21 :
972+ {
973+ const unsigned int dofs_per_quad = (totalorder + 1u )* (totalorder + 1u );
974+ const unsigned int dofs_per_tri = (totalorder + 1u )* (totalorder + 2u )/2u ;
975+ libmesh_assert_less (i , 3 * dofs_per_quad + 2 * dofs_per_tri );
976+
977+ // We only need a Tri3 or Quad4 to evaluate L2_HIERARCHIC on
978+ // the affine master element
979+ Tri3 tri ;
980+ Quad4 quad ;
981+ Elem * side = & quad ;
982+ unsigned int dofs_on_side = dofs_per_quad ;
983+
984+ // We pinky swear not to modify the nodes we'll point to
985+ Elem & e = const_cast < Elem & > (* elem );
986+
987+ Point sidep ;
988+
989+ // Face number calculation is tricky - the ordering of side
990+ // nodes on Prisms does *not* match the ordering of sides!
991+ // (the mid-triangle side nodes were added "later")
992+ // Here face_num will be the numbering that matches the side
993+ // number, but i_offset will have to consider the nodal
994+ // ordering.
995+ unsigned int face_num = 0 ;
996+ unsigned int i_offset = 0 ;
997+
998+ // Triangular coordinates
999+ const Real zeta [3 ] = { 1. - p (0 ) - p (1 ), p (0 ), p (1 ) };
1000+
1001+ // Closeness to midplane
1002+ const Real zmid = 1 - std ::abs (p (2 ));
1003+
1004+ if (zeta [1 ] > zeta [2 ] && zeta [0 ] > zeta [2 ] &&
1005+ zmid > 3 * zeta [2 ]) // face 1, quad
1006+ {
1007+ face_num = 1 ;
1008+ i_offset = 0 ;
1009+ }
1010+ else if (zeta [1 ] > zeta [0 ] && zeta [2 ] > zeta [0 ] &&
1011+ zmid > 3 * zeta [0 ]) // face 2, quad
1012+ {
1013+ face_num = 2 ;
1014+ i_offset = dofs_per_quad ;
1015+ }
1016+ else if (zeta [0 ] > zeta [1 ] && zeta [2 ] > zeta [1 ] &&
1017+ zmid > 3 * zeta [1 ]) // face 3, quad
1018+ {
1019+ face_num = 3 ;
1020+ i_offset = 2 * dofs_per_quad ;
1021+ }
1022+ else if (p (2 ) + 1 < 3 * zeta [0 ] &&
1023+ p (2 ) + 1 < 3 * zeta [1 ] &&
1024+ p (2 ) + 1 < 3 * zeta [2 ]) // face 0, tri
1025+ {
1026+ face_num = 0 ;
1027+ i_offset = 3 * dofs_per_quad ;
1028+ dofs_on_side = dofs_per_tri ;
1029+ side = & tri ;
1030+ }
1031+ else if (1 - p (2 ) < 3 * zeta [0 ] &&
1032+ 1 - p (2 ) < 3 * zeta [1 ] &&
1033+ 1 - p (2 ) < 3 * zeta [2 ]) // face 4, tri
1034+ {
1035+ face_num = 4 ;
1036+ i_offset = dofs_per_tri + 3 * dofs_per_quad ;
1037+ dofs_on_side = dofs_per_tri ;
1038+ side = & tri ;
1039+ }
1040+ else
1041+ {
1042+ libmesh_error_msg ("Evaluating SIDE_HIERARCHIC right between two Prism faces?" );
1043+ }
1044+
1045+ if (i < i_offset ||
1046+ i >= i_offset + dofs_on_side )
1047+ return 0 ;
1048+
1049+ if (totalorder == 0 )
1050+ return 1 ;
1051+
1052+ const std ::array < unsigned int , 4 > face_vertex =
1053+ oriented_prism_nodes (* elem , face_num );
1054+
1055+ side -> set_node (0 ) = e .node_ptr (face_vertex [0 ]);
1056+ side -> set_node (1 ) = e .node_ptr (face_vertex [1 ]);
1057+ side -> set_node (2 ) = e .node_ptr (face_vertex [2 ]);
1058+ if (face_vertex [3 ] < 21 )
1059+ side -> set_node (3 ) = e .node_ptr (face_vertex [3 ]);
1060+
1061+ if (face_num == 0 || face_num == 4 )
1062+ sidep = {zeta [face_vertex [1 ]%3 ], zeta [face_vertex [2 ]%3 ]};
1063+ else
1064+ {
1065+ // Transform a coordinate from the master prism to the
1066+ // master quad, based on two vertex indices defining the
1067+ // coordinate's direction
1068+ auto coord_val = [p ](int v1 , int v2 ){
1069+ if (v2 - v1 == 3 )
1070+ return p (2 );
1071+ else if (v2 - v1 == -3 )
1072+ return - p (2 );
1073+ else if (v1 %3 == 0 && v2 %3 == 1 )
1074+ return 2 * p (0 )- 1 ;
1075+ else if (v2 %3 == 0 && v1 %3 == 1 )
1076+ return 1 - 2 * p (0 );
1077+ else if (v1 %3 == 1 && v2 %3 == 2 )
1078+ return p (1 )- p (0 );
1079+ else if (v2 %3 == 1 && v1 %3 == 2 )
1080+ return p (0 )- p (1 );
1081+ else if (v1 %3 == 2 && v2 %3 == 0 )
1082+ return 1 - 2 * p (1 );
1083+ else if (v2 %3 == 2 && v1 %3 == 0 )
1084+ return 2 * p (1 )- 1 ;
1085+ else
1086+ libmesh_error ();
1087+ };
1088+
1089+ sidep = {coord_val (face_vertex [0 ], face_vertex [1 ]),
1090+ coord_val (face_vertex [0 ], face_vertex [3 ])};
1091+ }
1092+
1093+ const unsigned int basisnum = i - i_offset ;
1094+
1095+ return FE < 2 ,L2_HIERARCHIC > ::shape (side , totalorder ,
1096+ basisnum , sidep , false);
1097+ }
1098+
1099+
9641100 default :
9651101 libmesh_error_msg ("Invalid element type = " << Utility ::enum_to_string (type ));
9661102 }
@@ -1208,6 +1344,8 @@ Real FE<3,SIDE_HIERARCHIC>::shape_deriv(const Elem * elem,
12081344 }
12091345
12101346 case TET14 :
1347+ case PRISM20 :
1348+ case PRISM21 :
12111349 {
12121350 return fe_fdm_deriv (elem , order , i , j , p , add_p_level , FE < 3 ,SIDE_HIERARCHIC > ::shape );
12131351 }
@@ -1527,6 +1665,8 @@ Real FE<3,SIDE_HIERARCHIC>::shape_second_deriv(const Elem * elem,
15271665 }
15281666
15291667 case TET14 :
1668+ case PRISM20 :
1669+ case PRISM21 :
15301670 {
15311671 return fe_fdm_second_deriv (elem , order , i , j , p , add_p_level ,
15321672 FE < 3 ,SIDE_HIERARCHIC > ::shape_deriv );
@@ -1633,18 +1773,38 @@ Point cube_side_point(unsigned int sidenum, const Point & p)
16331773}
16341774
16351775
1636- std ::array < unsigned int , 3 > oriented_tet_nodes (const Elem & elem ,
1637- unsigned int face_num )
1776+ void orient_quad (const Elem & elem ,
1777+ std ::array < unsigned int , 4 > & face_vertex )
1778+ {
1779+ // Sort the minimum point into face_vertex[0], the minimum of its
1780+ // neighbors into face_vertex[1]. Keep the other two consistent; we
1781+ // want to rotate or flip the quad but not to twist it.
1782+
1783+ const unsigned int min_pt =
1784+ std ::min_element (face_vertex .begin (), face_vertex .end (),
1785+ [& elem ](auto v1 , auto v2 )
1786+ {return elem .point (v1 )< elem .point (v2 );}) -
1787+ face_vertex .begin ();
1788+
1789+ // Do we flip the quad?
1790+ if (elem .point (face_vertex [(min_pt + 3 )%4 ]) <
1791+ elem .point (face_vertex [(min_pt + 1 )%4 ]))
1792+ face_vertex = { face_vertex [min_pt ], face_vertex [(min_pt + 3 )%4 ],
1793+ face_vertex [(min_pt + 2 )%4 ], face_vertex [(min_pt + 1 )%4 ] };
1794+ else
1795+ face_vertex = { face_vertex [min_pt ], face_vertex [(min_pt + 1 )%4 ],
1796+ face_vertex [(min_pt + 2 )%4 ], face_vertex [(min_pt + 3 )%4 ] };
1797+ }
1798+
1799+
1800+ void orient_triangle (const Elem & elem ,
1801+ unsigned int * face_vertex )
16381802{
16391803 // Reorient nodes to account for flipping and rotation.
16401804 // We could try to identify indices with symmetric shape
16411805 // functions, to skip this in those cases, if we really
16421806 // need to optimize later.
1643- std ::array < unsigned int , 3 > face_vertex
1644- { Tet4 ::side_nodes_map [face_num ][0 ],
1645- Tet4 ::side_nodes_map [face_num ][1 ],
1646- Tet4 ::side_nodes_map [face_num ][2 ] };
1647-
1807+ //
16481808 // With only 3 items, we should bubble sort!
16491809 // Programming-for-MechE's class pays off!
16501810 bool lastcheck = true;
@@ -1657,6 +1817,36 @@ std::array<unsigned int, 3> oriented_tet_nodes(const Elem & elem,
16571817 std ::swap (face_vertex [1 ], face_vertex [2 ]);
16581818 if (lastcheck && elem .point (face_vertex [0 ]) > elem .point (face_vertex [1 ]))
16591819 std ::swap (face_vertex [0 ], face_vertex [1 ]);
1820+ }
1821+
1822+
1823+ std ::array < unsigned int , 4 > oriented_prism_nodes (const Elem & elem ,
1824+ unsigned int face_num )
1825+ {
1826+ std ::array < unsigned int , 4 > face_vertex
1827+ { Prism6 ::side_nodes_map [face_num ][0 ],
1828+ Prism6 ::side_nodes_map [face_num ][1 ],
1829+ Prism6 ::side_nodes_map [face_num ][2 ],
1830+ Prism6 ::side_nodes_map [face_num ][3 ] };
1831+
1832+ if (face_num > 0 && face_num < 4 )
1833+ orient_quad (elem , face_vertex );
1834+ else
1835+ orient_triangle (elem , face_vertex .data ());
1836+
1837+ return face_vertex ;
1838+ }
1839+
1840+
1841+ std ::array < unsigned int , 3 > oriented_tet_nodes (const Elem & elem ,
1842+ unsigned int face_num )
1843+ {
1844+ std ::array < unsigned int , 3 > face_vertex
1845+ { Tet4 ::side_nodes_map [face_num ][0 ],
1846+ Tet4 ::side_nodes_map [face_num ][1 ],
1847+ Tet4 ::side_nodes_map [face_num ][2 ] };
1848+
1849+ orient_triangle (elem , face_vertex .data ());
16601850
16611851 return face_vertex ;
16621852}
0 commit comments