Skip to content

Commit 1d70f3b

Browse files
committed
Support SIDE_HIERARCHIC on PRISM2*
1 parent 63ad414 commit 1d70f3b

3 files changed

Lines changed: 212 additions & 8 deletions

File tree

src/fe/fe_hierarchic_shape_3D.C

Lines changed: 198 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
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

3638
Point 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+
3843
std::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
}

src/fe/fe_interface.C

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2681,8 +2681,10 @@ unsigned int FEInterface::max_order(const FEType & fe_t,
26812681
case PRISM6:
26822682
case PRISM15:
26832683
case PRISM18:
2684+
return 0;
26842685
case PRISM20:
26852686
case PRISM21:
2687+
return unlimited;
26862688
case PYRAMID5:
26872689
case PYRAMID13:
26882690
case PYRAMID14:

src/fe/fe_side_hierarchic.C

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,15 @@ unsigned int side_hierarchic_n_dofs_at_node(const ElemType t,
123123
return (o+1)*(o+2)/2;
124124
else
125125
return 0;
126+
case PRISM20:
127+
case PRISM21:
128+
if (n > 19)
129+
return 0;
130+
if (n > 17)
131+
return (o+1)*(o+2)/2;
132+
if (n > 14)
133+
return (o+1)*(o+1);
134+
return 0;
126135
case INVALID_ELEM:
127136
return 0;
128137
// Without side nodes on all sides we can't support side elements
@@ -154,6 +163,9 @@ unsigned int side_hierarchic_n_dofs(const ElemType t, const Order o)
154163
return ((o+1)*3); // o+1 per side
155164
case TET14:
156165
return (o+1)*(o+2)*2; // 4 sides, each (o+1)(o+2)/2
166+
case PRISM20:
167+
case PRISM21:
168+
return (o+1)*(o+1)*3+(o+1)*(o+2); // 2 tris, (o+1)(o+2)/2; 3 quads
157169
case INVALID_ELEM:
158170
return 0;
159171
// Without side nodes on all sides we can't support side elements

0 commit comments

Comments
 (0)