20#include "libmesh/fe.h"
21#include "libmesh/elem.h"
22#include "libmesh/number_lookups.h"
23#include "libmesh/enum_to_string.h"
24#include "libmesh/cell_tet4.h"
25#include "libmesh/face_tri3.h"
26#include "libmesh/face_quad4.h"
35unsigned int cube_side(
const Point & p);
37Point cube_side_point(
unsigned int sidenum,
const Point & interior_point);
40Real fe_hierarchic_3D_shape(
const Elem * elem,
44 const bool add_p_level);
47Real fe_hierarchic_3D_shape_deriv(
const Elem * elem,
52 const bool add_p_level);
54#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
57Real fe_hierarchic_3D_shape_second_deriv(
const Elem * elem,
62 const bool add_p_level);
68template <
unsigned int N_nodes>
69unsigned int remap_node(
unsigned int n,
71 unsigned int nodebegin)
73 std::array<const Point *, N_nodes> points;
76 points[i] = &elem.point(nodebegin+i);
78 std::sort(points.begin(), points.end(),
82 const Point * pn = points[n-nodebegin];
84 for (
auto i :
IntRange<unsigned
int>(nodebegin, nodebegin+N_nodes))
85 if (pn == &elem.point(i))
93void cube_remap(
unsigned int & side_i,
97 unsigned int totalorder,
103 side_i = remap_node<4>(side_i, side, 0);
107 else if (side_i < 4u*totalorder)
109 unsigned int side_node = (side_i - 4)/(totalorder-1)+4;
110 side_node = remap_node<4>(side_node, side, 4);
111 side_i = ((side_i - 4) % (totalorder - 1))
112 + 4 + (side_node-4)*(totalorder-1);
124 const unsigned int min_side_node = orientation / 2;
125 const bool flip = orientation % 2;
127 switch (min_side_node) {
130 std::swap(sidep(0), sidep(1));
133 sidep(0) = -sidep(0);
135 std::swap(sidep(0), sidep(1));
138 sidep(0) = -sidep(0);
139 sidep(1) = -sidep(1);
141 std::swap(sidep(0), sidep(1));
144 sidep(1) = -sidep(1);
146 std::swap(sidep(0), sidep(1));
155void cube_indices(
const Elem * elem,
156 const unsigned int totalorder,
157 const unsigned int i,
176 const unsigned int e = totalorder - 1u;
178 libmesh_assert_less (i, (totalorder+1u)*(totalorder+1u)*(totalorder+1u));
180 Real xi_saved = xi, eta_saved = eta, zeta_saved = zeta;
241 else if (i < 8 + 2*e)
250 else if (i < 8 + 3*e)
259 else if (i < 8 + 4*e)
268 else if (i < 8 + 5*e)
277 else if (i < 8 + 6*e)
286 else if (i < 8 + 7*e)
295 else if (i < 8 + 8*e)
304 else if (i < 8 + 9*e)
313 else if (i < 8 + 10*e)
322 else if (i < 8 + 11*e)
331 else if (i < 8 + 12*e)
340 else if (i < 8 + 12*e + 6*e*e)
342 const unsigned int face = (i - 8 - 12*e) / (e*e);
343 const unsigned int basisnum = (i - 8 - 12*e) % (e*e);
349 const std::vector<unsigned int> v =
354 const Point p_saved(xi_saved, eta_saved, zeta_saved);
356 Real *
const coord[3] = {&xi, &eta, &zeta};
357 unsigned int *
const index[3] = {&i0, &i1, &i2};
363 *coord[d] = p_saved * first;
365 else if (second(d) != 0)
368 *coord[d] = p_saved * second;
372 *index[d] = (origin(d) > 0);
378 unsigned int basisnum = i - 8 - 12*e - 6*e*e;
389void orient_triangle_coords(
const Elem & elem,
390 const unsigned int face_num,
391 const Point & xi_eta_saved,
394 const std::vector<unsigned int> face_vertex =
397 const Real barycentric[3] = {1 - xi_eta_saved(0) - xi_eta_saved(1),
404 xi_eta(0) = barycentric[face_vertex[1] % 3];
405 xi_eta(1) = barycentric[face_vertex[2] % 3];
409void prism_indices(
const Elem * elem,
410 const unsigned int totalorder,
411 const unsigned int i,
417 const unsigned int e = totalorder - 1u;
419 libmesh_assert_less (i, (totalorder+1u)*(totalorder+1u)*(totalorder+2u)/2u);
421 Point xi_eta_saved = xi_eta;
422 Real zeta_saved = zeta;
456 else if (i < 6 + 3*e)
463 else if (i < 6 + 6*e)
465 i01 = (i - 6 - 3*e)/e;
466 i2 = (i - 6 - 3*e)%e+2;
472 else if (i < 6 + 9*e)
479 else if (i < 6 + 9*e + 3*e*e)
481 const unsigned int face = (i - 6 - 9*e) / (e*e) + 1;
482 const unsigned int basisnum = (i - 6 - 9*e) % (e*e);
495 const std::vector<unsigned int> v =
499 const bool swap = (v[0] < 3) != (v[1] < 3);
500 i01 = (swap ? s1 : s0) + 1 + (face-1)*e;
509 if (v[0] % 3 == n1 % 3)
511 Real barycentric[3] = {1 - xi_eta_saved(0) - xi_eta_saved(1),
514 std::swap(barycentric[n0 % 3], barycentric[n1 % 3]);
515 xi_eta(0) = barycentric[1];
516 xi_eta(1) = barycentric[2];
520 else if (i < 6 + 9*e + 3*e*e + e*(e-1)/2)
522 i01 = i - 3 - 6*e - 3*e*e;
524 orient_triangle_coords(*elem, 0, xi_eta_saved, xi_eta);
527 else if (i < 6 + 9*e + 3*e*e + e*(e-1))
529 i01 = i - 3 - 6*e - 3*e*e - e*(e-1)/2;
531 orient_triangle_coords(*elem, 4, xi_eta_saved, xi_eta);
538 unsigned int basisnum = i - 6 - 9*e - 3*e*e - e*(e-1);
565 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
577 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
589 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
598 const unsigned int i,
600 const bool add_p_level)
602 return fe_hierarchic_3D_shape<HIERARCHIC>(elem, order, i, p, add_p_level);
609 const unsigned int i,
611 const bool add_p_level)
613 return fe_hierarchic_3D_shape<HIERARCHIC>(elem, fet.
order, i, p, add_p_level);
622 const unsigned int i,
624 const bool add_p_level)
626 return fe_hierarchic_3D_shape<L2_HIERARCHIC>(elem, order, i, p, add_p_level);
633 const unsigned int i,
635 const bool add_p_level)
637 return fe_hierarchic_3D_shape<L2_HIERARCHIC>(elem, fet.
order, i, p, add_p_level);
645 const unsigned int i,
647 const bool add_p_level)
653 const Order totalorder = order + add_p_level*elem->
p_level();
659 const unsigned int dofs_per_side = (totalorder+1u)*(totalorder+1u);
660 libmesh_assert_less(i, 6*dofs_per_side);
662 const unsigned int sidenum = cube_side(p);
664 return std::numeric_limits<Real>::quiet_NaN();
666 const unsigned int dof_offset = sidenum * dofs_per_side;
671 if (i >= dof_offset + dofs_per_side)
677 unsigned int side_i = i - dof_offset;
681 Point sidep = cube_side_point(sidenum, p);
683 cube_remap(side_i, *elem, sidenum, *side, totalorder, sidep);
690 const unsigned int dofs_per_side = (totalorder+1u)*(totalorder+2u)/2u;
691 libmesh_assert_less(i, 4*dofs_per_side);
693 const Real zeta[4] = {
Real(1.) - p(0) - p(1) - p(2), p(0), p(1), p(2) };
695 unsigned int face_num = 0;
696 if (zeta[0] > zeta[3] &&
702 else if (zeta[0] > zeta[2] &&
708 else if (zeta[1] > zeta[0] &&
723 if (i < face_num * dofs_per_side ||
724 i >= (face_num+1) * dofs_per_side)
730 const std::vector<unsigned int> face_vertex =
738 Elem & e =
const_cast<Elem &
>(*elem);
743 const unsigned int basisnum = i - face_num*dofs_per_side;
745 Point sidep {zeta[face_vertex[1]], zeta[face_vertex[2]]};
748 basisnum, sidep,
false);
754 const unsigned int dofs_per_quad = (totalorder+1u)*(totalorder+1u);
755 const unsigned int dofs_per_tri = (totalorder+1u)*(totalorder+2u)/2u;
756 libmesh_assert_less(i, 3*dofs_per_quad + 2*dofs_per_tri);
763 unsigned int dofs_on_side = dofs_per_quad;
766 Elem & e =
const_cast<Elem &
>(*elem);
776 unsigned int face_num = 0;
777 unsigned int i_offset = 0;
780 const Real zeta[3] = {
Real(1.) - p(0) - p(1), p(0), p(1) };
783 const Real zmid = 1 - std::abs(p(2));
785 if (zeta[1] > zeta[2] && zeta[0] > zeta[2] &&
791 else if (zeta[1] > zeta[0] && zeta[2] > zeta[0] &&
795 i_offset = dofs_per_quad;
797 else if (zeta[0] > zeta[1] && zeta[2] > zeta[1] &&
801 i_offset = 2*dofs_per_quad;
803 else if (p(2) + 1 < 3*zeta[0] &&
804 p(2) + 1 < 3*zeta[1] &&
805 p(2) + 1 < 3*zeta[2])
808 i_offset = 3*dofs_per_quad;
809 dofs_on_side = dofs_per_tri;
812 else if (1 - p(2) < 3*zeta[0] &&
813 1 - p(2) < 3*zeta[1] &&
814 1 - p(2) < 3*zeta[2])
817 i_offset = dofs_per_tri + 3*dofs_per_quad;
818 dofs_on_side = dofs_per_tri;
823 libmesh_error_msg(
"Evaluating SIDE_HIERARCHIC right between two Prism faces?");
827 i >= i_offset + dofs_on_side)
833 const std::vector<unsigned int> face_vertex =
839 if (face_vertex.size() > 3)
842 if (face_num == 0 || face_num == 4)
843 sidep = {zeta[face_vertex[1]%3], zeta[face_vertex[2]%3]};
849 auto coord_val = [p](
int v1,
int v2){
852 else if (v2-v1 == -3)
854 else if (v1%3 == 0 && v2%3 == 1)
856 else if (v2%3 == 0 && v1%3 == 1)
858 else if (v1%3 == 1 && v2%3 == 2)
860 else if (v2%3 == 1 && v1%3 == 2)
862 else if (v1%3 == 2 && v2%3 == 0)
864 else if (v2%3 == 2 && v1%3 == 0)
870 sidep = {coord_val(face_vertex[0], face_vertex[1]),
871 coord_val(face_vertex[0], face_vertex[3])};
874 const unsigned int basisnum = i - i_offset;
877 basisnum, sidep,
false);
887 libmesh_not_implemented();
895 const unsigned int i,
897 const bool add_p_level)
910 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
923 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
936 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
945 const unsigned int i,
946 const unsigned int j,
948 const bool add_p_level)
950 return fe_hierarchic_3D_shape_deriv<HIERARCHIC>(elem, order, i, j, p, add_p_level);
957 const unsigned int i,
958 const unsigned int j,
960 const bool add_p_level)
962 return fe_hierarchic_3D_shape_deriv<HIERARCHIC>(elem, fet.
order, i, j, p, add_p_level);
970 const unsigned int i,
971 const unsigned int j,
973 const bool add_p_level)
975 return fe_hierarchic_3D_shape_deriv<L2_HIERARCHIC>(elem, order, i, j, p, add_p_level);
982 const unsigned int i,
983 const unsigned int j,
985 const bool add_p_level)
987 return fe_hierarchic_3D_shape_deriv<L2_HIERARCHIC>(elem, fet.
order, i, j, p, add_p_level);
995 const unsigned int i,
996 const unsigned int j,
998 const bool add_p_level)
1004 const Order totalorder = order + add_p_level*elem->
p_level();
1006 if (totalorder == 0)
1017 const unsigned int dofs_per_side = (totalorder+1u)*(totalorder+1u);
1018 libmesh_assert_less(i, 6*dofs_per_side);
1020 const unsigned int sidenum = cube_side(p);
1022 return std::numeric_limits<Real>::quiet_NaN();
1024 const unsigned int dof_offset = sidenum * dofs_per_side;
1029 if (i >= dof_offset + dofs_per_side)
1032 unsigned int side_i = i - dof_offset;
1034 std::unique_ptr<const Elem> side = elem->
build_side_ptr(sidenum);
1036 Point sidep = cube_side_point(sidenum, p);
1038 cube_remap(side_i, *elem, sidenum, *side, totalorder, sidep);
1042 unsigned int sidej = 100;
1122 libmesh_error_msg(
"Invalid derivative index j = " << j);
1126 side_i, sidej, sidep,
1143 libmesh_not_implemented();
1151 const unsigned int i,
1152 const unsigned int j,
1154 const bool add_p_level)
1160#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
1169 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
1182 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
1195 libmesh_error_msg(
"Hierarchic shape functions require an Elem for edge/face orientation.");
1204 const unsigned int i,
1205 const unsigned int j,
1207 const bool add_p_level)
1209 return fe_hierarchic_3D_shape_second_deriv<HIERARCHIC>(elem, order, i, j, p, add_p_level);
1217 const unsigned int i,
1218 const unsigned int j,
1220 const bool add_p_level)
1222 return fe_hierarchic_3D_shape_second_deriv<HIERARCHIC>(elem, fet.
order, i, j, p, add_p_level);
1230 const unsigned int i,
1231 const unsigned int j,
1233 const bool add_p_level)
1235 return fe_hierarchic_3D_shape_second_deriv<L2_HIERARCHIC>(elem, order, i, j, p, add_p_level);
1242 const unsigned int i,
1243 const unsigned int j,
1245 const bool add_p_level)
1247 return fe_hierarchic_3D_shape_second_deriv<L2_HIERARCHIC>(elem, fet.
order, i, j, p, add_p_level);
1254 const unsigned int i,
1255 const unsigned int j,
1257 const bool add_p_level)
1263 const Order totalorder = order + add_p_level*elem->
p_level();
1265 if (totalorder == 0)
1277 const unsigned int dofs_per_side = (totalorder+1u)*(totalorder+1u);
1278 libmesh_assert_less(i, 6*dofs_per_side);
1280 const unsigned int sidenum = cube_side(p);
1282 return std::numeric_limits<Real>::quiet_NaN();
1284 const unsigned int dof_offset = sidenum * dofs_per_side;
1289 if (i >= dof_offset + dofs_per_side)
1292 unsigned int side_i = i - dof_offset;
1294 std::unique_ptr<const Elem> side = elem->
build_side_ptr(sidenum);
1296 Point sidep = cube_side_point(sidenum, p);
1298 cube_remap(side_i, *elem, sidenum, *side, totalorder, sidep);
1302 unsigned int sidej = 100;
1446 libmesh_error_msg(
"Invalid derivative index j = " << j);
1469 libmesh_not_implemented();
1477 const unsigned int i,
1478 const unsigned int j,
1480 const bool add_p_level)
1496unsigned int cube_side (
const Point & p)
1498 const Real xi = p(0), eta = p(1), zeta = p(2);
1499 const Real absxi = std::abs(xi),
1500 abseta = std::abs(eta),
1501 abszeta = std::abs(zeta);
1502 const Real maxabs_xi_eta = std::max(absxi, abseta),
1503 maxabs_xi_zeta = std::max(absxi, abszeta),
1504 maxabs_eta_zeta = std::max(abseta, abszeta);
1506 if (zeta < -maxabs_xi_eta)
1508 else if (eta < -maxabs_xi_zeta)
1510 else if (xi > maxabs_eta_zeta)
1512 else if (eta > maxabs_xi_zeta)
1514 else if (xi < -maxabs_eta_zeta)
1516 else if (zeta > maxabs_xi_eta)
1526Point cube_side_point(
unsigned int sidenum,
const Point & p)
1565template <FEFamily T>
1566Real fe_hierarchic_3D_shape(
const Elem * elem,
1568 const unsigned int i,
1570 const bool add_p_level)
1577 const Order totalorder = order + add_p_level*elem->
p_level();
1584 libmesh_fallthrough();
1587 libmesh_assert_less (i, (totalorder+1u)*(totalorder+1u)*(totalorder+1u));
1594 unsigned int i0, i1, i2;
1596 cube_indices(elem, totalorder, i, xi, eta, zeta, i0, i1, i2);
1606 libmesh_fallthrough();
1609 libmesh_fallthrough();
1613 libmesh_assert_less (i, (totalorder+1u)*(totalorder+1u)*(totalorder+2u)/2u);
1618 Point xi_eta {p(0),p(1)};
1621 unsigned int i01, i2;
1623 prism_indices(elem, totalorder, i, xi_eta, zeta, i01, i2);
1630 Elem & e =
const_cast<Elem &
>(*elem);
1655 if (i01 > 2 && i01 < 3u*totalorder)
1658 const bool odd_basis = ((i01-3)%(totalorder-1))%2;
1661 const int tri_edge = (i01-3)/(totalorder-1);
1664 if (tri.
point(tri_edge) > tri.
point((tri_edge+1)%3))
1680 libmesh_fallthrough();
1683 libmesh_fallthrough();
1686 const Real zeta[4] = { 1 - p(0) - p(1) - p(2), p(0), p(1), p(2) };
1693 else if (i < 6u*totalorder - 2u)
1695 const unsigned int edge_num = (i - 4) / (totalorder - 1u);
1697 const unsigned int basisorder = i - 2 - ((totalorder - 1u) * edge_num);
1708 const Real crossval = zeta[edgevertex0] + zeta[edgevertex1];
1709 const Real edgenumerator = zeta[edgevertex1] - zeta[edgevertex0];
1713 unsigned int basisfactorial = 1.;
1714 for (
unsigned int n=2; n <= basisorder; ++n)
1715 basisfactorial *= n;
1717 return flip * std::pow(edgenumerator, basisorder) / basisfactorial;
1720 const Real edgeval = edgenumerator / crossval;
1721 const Real crossfunc = std::pow(crossval, basisorder);
1723 return flip * crossfunc *
1725 basisorder, edgeval);
1729 else if (i < 2u*totalorder*totalorder + 2u)
1731 const int dofs_per_face = (totalorder - 1u) * (totalorder - 2u) / 2;
1732 const int face_num = (i - (6u*totalorder - 2u)) / dofs_per_face;
1734 const std::vector<unsigned int> face_vertex =
1736 const Real zeta0 = zeta[face_vertex[0]],
1737 zeta1 = zeta[face_vertex[1]],
1738 zeta2 = zeta[face_vertex[2]];
1740 const unsigned int basisnum =
1742 (totalorder - 1u) * 6 -
1743 (dofs_per_face * face_num);
1750 for (
unsigned int n = 0; n != exp0; ++n)
1752 for (
unsigned int n = 0; n != exp1; ++n)
1761 const unsigned int basisnum = i - 2u*totalorder*totalorder - 2u;
1769 for (
unsigned int n = 0; n != exp0; ++n)
1770 returnval *= zeta[0];
1771 for (
unsigned int n = 0; n != exp1; ++n)
1772 returnval *= zeta[1];
1773 for (
unsigned int n = 0; n != exp2; ++n)
1774 returnval *= zeta[2];
1775 returnval *= zeta[3];
1786 libmesh_not_implemented();
1792template <FEFamily T>
1793Real fe_hierarchic_3D_shape_deriv(
const Elem * elem,
1795 const unsigned int i,
1796 const unsigned int j,
1798 const bool add_p_level)
1805#ifdef LIBMESH_ENABLE_SECOND_DERIVATIVES
1807template <FEFamily T>
1808Real fe_hierarchic_3D_shape_second_deriv(
const Elem * elem,
1810 const unsigned int i,
1811 const unsigned int j,
1813 const bool add_p_level)
void ErrorVector unsigned int
This is the base class from which all geometric element types are derived.
virtual Node *& set_node(const unsigned int i)
const Point & point(const unsigned int i) const
virtual unsigned int local_side_node(unsigned int side, unsigned int side_node) const =0
bool positive_edge_orientation(const unsigned int i) const
std::vector< unsigned int > oriented_face_vertices(const unsigned int i) const
const Node * node_ptr(const unsigned int i) const
virtual ElemType type() const =0
virtual Point master_point(const unsigned int i) const =0
unsigned int p_level() const
unsigned int face_orientation(const unsigned int i) const
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
OrderWrapper order
The approximation order of the element (at 0 p-refinement level).
A specific instantiation of the FEBase class.
static OutputShape shape(const ElemType t, const Order o, const unsigned int i, const Point &p)
static OutputShape shape_second_deriv(const ElemType t, const Order o, const unsigned int i, const unsigned int j, const Point &p)
static OutputShape shape_deriv(const ElemType t, const Order o, const unsigned int i, const unsigned int j, const Point &p)
The IntRange templated class is intended to make it easy to loop over integers which are indices of a...
A Node is like a Point, but with more information.
A Point defines a location in LIBMESH_DIM dimensional Real space.
The QUAD4 is an element in 2D composed of 4 nodes.
static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge]
This maps the node of the edge to element node numbers.
The Tri3 is an element in 2D composed of 3 nodes.
std::string enum_to_string(const T e)
The libMesh namespace provides an interface to certain functionality in the library.
const unsigned char tetrahedral_number_column[]
const unsigned char tetrahedral_number_row[]
const unsigned char prism_number_triangle[]
LIBMESH_DEFAULT_VECTORIZED_FE(template<>Real FE< 0, BERNSTEIN)
const unsigned char prism_number_page[]
OutputShape fe_fdm_deriv(const Elem *elem, const Order order, const unsigned int i, const unsigned int j, const Point &p, const bool add_p_level, OutputShape(*shape_func)(const Elem *, const Order, const unsigned int, const Point &, const bool))
Helper functions for finite differenced derivatives in cases where analytical calculations haven't be...
ElemType
Defines an enum for geometric element types.
void libmesh_ignore(const Args &...)
OutputShape fe_fdm_second_deriv(const Elem *elem, const Order order, const unsigned int i, const unsigned int j, const Point &p, const bool add_p_level, OutputShape(*deriv_func)(const Elem *, const Order, const unsigned int, const unsigned int, const Point &, const bool))
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
const unsigned char triangular_number_row[]
const unsigned char cube_number_row[]
const unsigned char cube_number_page[]
const unsigned char cube_number_column[]
const unsigned char square_number_row[]
const unsigned char square_number_column[]
const unsigned char triangular_number_column[]
const unsigned char tetrahedral_number_page[]
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...