From 549da0c49443b7edd472a6f354533b3ed657d779 Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Wed, 16 Sep 2026 13:56:47 -0600 Subject: [PATCH 01/10] Make the element node maps constexpr Move the side_nodes_map and edge_nodes_map initializers from the .C files into the class definitions as static constexpr members, so the reference-element topology is available at compile time to anything that includes the element header. C++17 makes these inline, so the out-of-line definitions go away; the members' names, types and public visibility are unchanged. --- include/geom/cell_hex20.h | 26 ++++++++++++++++++++++++-- include/geom/cell_hex27.h | 26 ++++++++++++++++++++++++-- include/geom/cell_hex8.h | 26 ++++++++++++++++++++++++-- include/geom/cell_prism15.h | 22 ++++++++++++++++++++-- include/geom/cell_prism18.h | 22 ++++++++++++++++++++-- include/geom/cell_prism20.h | 22 ++++++++++++++++++++-- include/geom/cell_prism21.h | 22 ++++++++++++++++++++-- include/geom/cell_prism6.h | 22 ++++++++++++++++++++-- include/geom/cell_pyramid13.h | 21 +++++++++++++++++++-- include/geom/cell_pyramid14.h | 21 +++++++++++++++++++-- include/geom/cell_pyramid18.h | 21 +++++++++++++++++++-- include/geom/cell_pyramid5.h | 21 +++++++++++++++++++-- include/geom/cell_tet10.h | 18 ++++++++++++++++-- include/geom/cell_tet14.h | 18 ++++++++++++++++-- include/geom/cell_tet4.h | 18 ++++++++++++++++-- include/geom/face_quad4.h | 8 +++++++- include/geom/face_quad8.h | 8 +++++++- include/geom/face_quad9.h | 8 +++++++- include/geom/face_tri3.h | 7 ++++++- include/geom/face_tri6.h | 7 ++++++- include/geom/face_tri7.h | 7 ++++++- src/geom/cell_hex20.C | 26 -------------------------- src/geom/cell_hex27.C | 26 -------------------------- src/geom/cell_hex8.C | 26 -------------------------- src/geom/cell_prism15.C | 22 ---------------------- src/geom/cell_prism18.C | 22 ---------------------- src/geom/cell_prism20.C | 22 ---------------------- src/geom/cell_prism21.C | 22 ---------------------- src/geom/cell_prism6.C | 22 ---------------------- src/geom/cell_pyramid13.C | 21 --------------------- src/geom/cell_pyramid14.C | 21 --------------------- src/geom/cell_pyramid18.C | 21 --------------------- src/geom/cell_pyramid5.C | 21 --------------------- src/geom/cell_tet10.C | 18 ------------------ src/geom/cell_tet14.C | 18 ------------------ src/geom/cell_tet4.C | 18 ------------------ src/geom/face_quad4.C | 8 -------- src/geom/face_quad8.C | 8 -------- src/geom/face_quad9.C | 8 -------- src/geom/face_tri3.C | 7 ------- src/geom/face_tri6.C | 7 ------- src/geom/face_tri7.C | 7 ------- 42 files changed, 335 insertions(+), 407 deletions(-) diff --git a/include/geom/cell_hex20.h b/include/geom/cell_hex20.h index 70c37c23f70..b4bd6afd013 100644 --- a/include/geom/cell_hex20.h +++ b/include/geom/cell_hex20.h @@ -220,13 +220,35 @@ class Hex20 final : public Hex * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 3, 2, 1, 11, 10, 9, 8}, // Side 0 + {0, 1, 5, 4, 8, 13, 16, 12}, // Side 1 + {1, 2, 6, 5, 9, 14, 17, 13}, // Side 2 + {2, 3, 7, 6, 10, 15, 18, 14}, // Side 3 + {3, 0, 4, 7, 11, 12, 19, 15}, // Side 4 + {4, 5, 6, 7, 16, 17, 18, 19} // Side 5 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 8}, // Edge 0 + {1, 2, 9}, // Edge 1 + {2, 3, 10}, // Edge 2 + {0, 3, 11}, // Edge 3 + {0, 4, 12}, // Edge 4 + {1, 5, 13}, // Edge 5 + {2, 6, 14}, // Edge 6 + {3, 7, 15}, // Edge 7 + {4, 5, 16}, // Edge 8 + {5, 6, 17}, // Edge 9 + {6, 7, 18}, // Edge 10 + {4, 7, 19} // Edge 11 + }; /** * A specialization for computing the volume of a Hex20. diff --git a/include/geom/cell_hex27.h b/include/geom/cell_hex27.h index 0777540a303..8ad8230297d 100644 --- a/include/geom/cell_hex27.h +++ b/include/geom/cell_hex27.h @@ -235,13 +235,35 @@ class Hex27 final : public Hex * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 3, 2, 1, 11, 10, 9, 8, 20}, // Side 0 + {0, 1, 5, 4, 8, 13, 16, 12, 21}, // Side 1 + {1, 2, 6, 5, 9, 14, 17, 13, 22}, // Side 2 + {2, 3, 7, 6, 10, 15, 18, 14, 23}, // Side 3 + {3, 0, 4, 7, 11, 12, 19, 15, 24}, // Side 4 + {4, 5, 6, 7, 16, 17, 18, 19, 25} // Side 5 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 8}, // Edge 0 + {1, 2, 9}, // Edge 1 + {2, 3, 10}, // Edge 2 + {0, 3, 11}, // Edge 3 + {0, 4, 12}, // Edge 4 + {1, 5, 13}, // Edge 5 + {2, 6, 14}, // Edge 6 + {3, 7, 15}, // Edge 7 + {4, 5, 16}, // Edge 8 + {5, 6, 17}, // Edge 9 + {6, 7, 18}, // Edge 10 + {4, 7, 19} // Edge 11 + }; /** * A specialization for computing the volume of a Hex27. diff --git a/include/geom/cell_hex8.h b/include/geom/cell_hex8.h index 9abee931719..cf77eb45943 100644 --- a/include/geom/cell_hex8.h +++ b/include/geom/cell_hex8.h @@ -170,13 +170,35 @@ class Hex8 final : public Hex * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 3, 2, 1}, // Side 0 + {0, 1, 5, 4}, // Side 1 + {1, 2, 6, 5}, // Side 2 + {2, 3, 7, 6}, // Side 3 + {3, 0, 4, 7}, // Side 4 + {4, 5, 6, 7} // Side 5 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1}, // Edge 0 + {1, 2}, // Edge 1 + {2, 3}, // Edge 2 + {0, 3}, // Edge 3 + {0, 4}, // Edge 4 + {1, 5}, // Edge 5 + {2, 6}, // Edge 6 + {3, 7}, // Edge 7 + {4, 5}, // Edge 8 + {5, 6}, // Edge 9 + {6, 7}, // Edge 10 + {4, 7} // Edge 11 + }; /** * Class static helper function that computes the centroid of a diff --git a/include/geom/cell_prism15.h b/include/geom/cell_prism15.h index 68374b06f6f..6e0a6304cf3 100644 --- a/include/geom/cell_prism15.h +++ b/include/geom/cell_prism15.h @@ -225,13 +225,31 @@ class Prism15 final : public Prism * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 2, 1, 8, 7, 6, 99, 99}, // Side 0 + {0, 1, 4, 3, 6, 10, 12, 9}, // Side 1 + {1, 2, 5, 4, 7, 11, 13, 10}, // Side 2 + {2, 0, 3, 5, 8, 9, 14, 11}, // Side 3 + {3, 4, 5, 12, 13, 14, 99, 99} // Side 4 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 6}, // Edge 0 + {1, 2, 7}, // Edge 1 + {0, 2, 8}, // Edge 2 + {0, 3, 9}, // Edge 3 + {1, 4, 10}, // Edge 4 + {2, 5, 11}, // Edge 5 + {3, 4, 12}, // Edge 6 + {4, 5, 13}, // Edge 7 + {3, 5, 14} // Edge 8 + }; /** * A specialization for computing the volume of a Prism15. diff --git a/include/geom/cell_prism18.h b/include/geom/cell_prism18.h index 530f6f37970..12e46858fcb 100644 --- a/include/geom/cell_prism18.h +++ b/include/geom/cell_prism18.h @@ -240,13 +240,31 @@ class Prism18 final : public Prism * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 2, 1, 8, 7, 6, 99, 99, 99}, // Side 0 + {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 + {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 + {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 + {3, 4, 5, 12, 13, 14, 99, 99, 99} // Side 4 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 6}, // Edge 0 + {1, 2, 7}, // Edge 1 + {0, 2, 8}, // Edge 2 + {0, 3, 9}, // Edge 3 + {1, 4, 10}, // Edge 4 + {2, 5, 11}, // Edge 5 + {3, 4, 12}, // Edge 6 + {4, 5, 13}, // Edge 7 + {3, 5, 14} // Edge 8 + }; /** * A specialization for computing the volume of a Prism18. diff --git a/include/geom/cell_prism20.h b/include/geom/cell_prism20.h index af1bedf2634..7b7ab1975b6 100644 --- a/include/geom/cell_prism20.h +++ b/include/geom/cell_prism20.h @@ -245,13 +245,31 @@ class Prism20 final : public Prism * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 2, 1, 8, 7, 6, 18, 99, 99}, // Side 0 + {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 + {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 + {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 + {3, 4, 5, 12, 13, 14, 19, 99, 99} // Side 4 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 6}, // Edge 0 + {1, 2, 7}, // Edge 1 + {0, 2, 8}, // Edge 2 + {0, 3, 9}, // Edge 3 + {1, 4, 10}, // Edge 4 + {2, 5, 11}, // Edge 5 + {3, 4, 12}, // Edge 6 + {4, 5, 13}, // Edge 7 + {3, 5, 14} // Edge 8 + }; virtual void permute(unsigned int perm_num) override final; diff --git a/include/geom/cell_prism21.h b/include/geom/cell_prism21.h index 894f86789fd..4cd906e369d 100644 --- a/include/geom/cell_prism21.h +++ b/include/geom/cell_prism21.h @@ -248,13 +248,31 @@ class Prism21 final : public Prism * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 2, 1, 8, 7, 6, 18, 99, 99}, // Side 0 + {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 + {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 + {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 + {3, 4, 5, 12, 13, 14, 19, 99, 99} // Side 4 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 6}, // Edge 0 + {1, 2, 7}, // Edge 1 + {0, 2, 8}, // Edge 2 + {0, 3, 9}, // Edge 3 + {1, 4, 10}, // Edge 4 + {2, 5, 11}, // Edge 5 + {3, 4, 12}, // Edge 6 + {4, 5, 13}, // Edge 7 + {3, 5, 14} // Edge 8 + }; virtual void permute(unsigned int perm_num) override final; diff --git a/include/geom/cell_prism6.h b/include/geom/cell_prism6.h index 27cdd174dc5..c172c12ce71 100644 --- a/include/geom/cell_prism6.h +++ b/include/geom/cell_prism6.h @@ -169,7 +169,14 @@ class Prism6 final : public Prism * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 2, 1, 99}, // Side 0 + {0, 1, 4, 3}, // Side 1 + {1, 2, 5, 4}, // Side 2 + {2, 0, 3, 5}, // Side 3 + {3, 4, 5, 99} // Side 4 + }; /** * This maps the child elements with the associated side of the parent element @@ -180,7 +187,18 @@ class Prism6 final : public Prism * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1}, // Edge 0 + {1, 2}, // Edge 1 + {0, 2}, // Edge 2 + {0, 3}, // Edge 3 + {1, 4}, // Edge 4 + {2, 5}, // Edge 5 + {3, 4}, // Edge 6 + {4, 5}, // Edge 7 + {3, 5} // Edge 8 + }; /** * An Optimized numerical quadrature approach for computing the diff --git a/include/geom/cell_pyramid13.h b/include/geom/cell_pyramid13.h index f0d2819fb2e..637eb5d1255 100644 --- a/include/geom/cell_pyramid13.h +++ b/include/geom/cell_pyramid13.h @@ -213,13 +213,30 @@ class Pyramid13 final : public Pyramid * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1, 4, 5, 10, 9, 99, 99}, // Side 0 (front) + {1, 2, 4, 6, 11, 10, 99, 99}, // Side 1 (right) + {2, 3, 4, 7, 12, 11, 99, 99}, // Side 2 (back) + {3, 0, 4, 8, 9, 12, 99, 99}, // Side 3 (left) + {0, 3, 2, 1, 8, 7, 6, 5} // Side 4 (base) + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 5}, // Edge 0 + {1, 2, 6}, // Edge 1 + {2, 3, 7}, // Edge 2 + {0, 3, 8}, // Edge 3 + {0, 4, 9}, // Edge 4 + {1, 4, 10}, // Edge 5 + {2, 4, 11}, // Edge 6 + {3, 4, 12} // Edge 7 + }; /** * Specialization for computing the volume of a Pyramid13. diff --git a/include/geom/cell_pyramid14.h b/include/geom/cell_pyramid14.h index 87547dc63e4..8c96baf15ff 100644 --- a/include/geom/cell_pyramid14.h +++ b/include/geom/cell_pyramid14.h @@ -231,13 +231,30 @@ class Pyramid14 final : public Pyramid * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1, 4, 5, 10, 9, 99, 99, 99}, // Side 0 (front) + {1, 2, 4, 6, 11, 10, 99, 99, 99}, // Side 1 (right) + {2, 3, 4, 7, 12, 11, 99, 99, 99}, // Side 2 (back) + {3, 0, 4, 8, 9, 12, 99, 99, 99}, // Side 3 (left) + {0, 3, 2, 1, 8, 7, 6, 5, 13} // Side 4 (base) + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 5}, // Edge 0 + {1, 2, 6}, // Edge 1 + {2, 3, 7}, // Edge 2 + {0, 3, 8}, // Edge 3 + {0, 4, 9}, // Edge 4 + {1, 4, 10}, // Edge 5 + {2, 4, 11}, // Edge 6 + {3, 4, 12} // Edge 7 + }; /** * Specialization for computing the volume of a Pyramid14. diff --git a/include/geom/cell_pyramid18.h b/include/geom/cell_pyramid18.h index 33f5c21e707..b4b18438b49 100644 --- a/include/geom/cell_pyramid18.h +++ b/include/geom/cell_pyramid18.h @@ -238,13 +238,30 @@ class Pyramid18 final : public Pyramid * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1, 4, 5, 10, 9, 14, 99, 99}, // Side 0 (front) + {1, 2, 4, 6, 11, 10, 15, 99, 99}, // Side 1 (right) + {2, 3, 4, 7, 12, 11, 16, 99, 99}, // Side 2 (back) + {3, 0, 4, 8, 9, 12, 17, 99, 99}, // Side 3 (left) + {0, 3, 2, 1, 8, 7, 6, 5, 13} // Side 4 (base) + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 5}, // Edge 0 + {1, 2, 6}, // Edge 1 + {2, 3, 7}, // Edge 2 + {0, 3, 8}, // Edge 3 + {0, 4, 9}, // Edge 4 + {1, 4, 10}, // Edge 5 + {2, 4, 11}, // Edge 6 + {3, 4, 12} // Edge 7 + }; virtual void permute(unsigned int perm_num) override final; diff --git a/include/geom/cell_pyramid5.h b/include/geom/cell_pyramid5.h index 835a09b850d..4edd0083515 100644 --- a/include/geom/cell_pyramid5.h +++ b/include/geom/cell_pyramid5.h @@ -167,13 +167,30 @@ class Pyramid5 final : public Pyramid * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1, 4, 99}, // Side 0 + {1, 2, 4, 99}, // Side 1 + {2, 3, 4, 99}, // Side 2 + {3, 0, 4, 99}, // Side 3 + {0, 3, 2, 1} // Side 4 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1}, // Edge 0 + {1, 2}, // Edge 1 + {2, 3}, // Edge 2 + {0, 3}, // Edge 3 + {0, 4}, // Edge 4 + {1, 4}, // Edge 5 + {2, 4}, // Edge 6 + {3, 4} // Edge 7 + }; /** * We compute the centroid of the Pyramid by treating it as a diff --git a/include/geom/cell_tet10.h b/include/geom/cell_tet10.h index 5f454fe755c..a3e90044a95 100644 --- a/include/geom/cell_tet10.h +++ b/include/geom/cell_tet10.h @@ -221,13 +221,27 @@ class Tet10 final : public Tet * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 2, 1, 6, 5, 4}, // Side 0 + {0, 1, 3, 4, 8, 7}, // Side 1 + {1, 2, 3, 5, 9, 8}, // Side 2 + {2, 0, 3, 6, 7, 9} // Side 3 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 4}, // Edge 0 + {1, 2, 5}, // Edge 1 + {0, 2, 6}, // Edge 2 + {0, 3, 7}, // Edge 3 + {1, 3, 8}, // Edge 4 + {2, 3, 9} // Edge 5 + }; /** * A specialization for computing the volume of a Tet10. diff --git a/include/geom/cell_tet14.h b/include/geom/cell_tet14.h index 43245751eec..6abcf5873e4 100644 --- a/include/geom/cell_tet14.h +++ b/include/geom/cell_tet14.h @@ -226,13 +226,27 @@ class Tet14 final : public Tet * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 2, 1, 6, 5, 4, 10}, // Side 0 + {0, 1, 3, 4, 8, 7, 11}, // Side 1 + {1, 2, 3, 5, 9, 8, 12}, // Side 2 + {2, 0, 3, 6, 7, 9, 13} // Side 3 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1, 4}, // Edge 0 + {1, 2, 5}, // Edge 1 + {0, 2, 6}, // Edge 2 + {0, 3, 7}, // Edge 3 + {1, 3, 8}, // Edge 4 + {2, 3, 9} // Edge 5 + }; virtual void permute(unsigned int perm_num) override final; diff --git a/include/geom/cell_tet4.h b/include/geom/cell_tet4.h index c6efd46cf38..f31e259a7c4 100644 --- a/include/geom/cell_tet4.h +++ b/include/geom/cell_tet4.h @@ -192,13 +192,27 @@ class Tet4 final : public Tet * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 2, 1}, // Side 0 + {0, 1, 3}, // Side 1 + {1, 2, 3}, // Side 2 + {2, 0, 3} // Side 3 + }; /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to * element node numbers. */ - static const unsigned int edge_nodes_map[num_edges][nodes_per_edge]; + static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = + { + {0, 1}, // Edge 0 + {1, 2}, // Edge 1 + {0, 2}, // Edge 2 + {0, 3}, // Edge 3 + {1, 3}, // Edge 4 + {2, 3} // Edge 5 + }; /** * The centroid of a 4-node tetrahedron is simply given by the diff --git a/include/geom/face_quad4.h b/include/geom/face_quad4.h index 2bc20fa0738..0fa0ae6a198 100644 --- a/include/geom/face_quad4.h +++ b/include/geom/face_quad4.h @@ -152,7 +152,13 @@ class Quad4 : public Quad * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1}, // Side 0 + {1, 2}, // Side 1 + {2, 3}, // Side 2 + {3, 0} // Side 3 + }; /** * An optimized method for computing the centroid of a diff --git a/include/geom/face_quad8.h b/include/geom/face_quad8.h index 4bcdaa39433..53262260612 100644 --- a/include/geom/face_quad8.h +++ b/include/geom/face_quad8.h @@ -194,7 +194,13 @@ class Quad8 : public Quad * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1, 4}, // Side 0 + {1, 2, 5}, // Side 1 + {2, 3, 6}, // Side 2 + {3, 0, 7} // Side 3 + }; /** * An optimized method for approximating the area of a diff --git a/include/geom/face_quad9.h b/include/geom/face_quad9.h index e065d8bafa5..7ac983a8839 100644 --- a/include/geom/face_quad9.h +++ b/include/geom/face_quad9.h @@ -201,7 +201,13 @@ class Quad9 : public Quad * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1, 4}, // Side 0 + {1, 2, 5}, // Side 1 + {2, 3, 6}, // Side 2 + {3, 0, 7} // Side 3 + }; /** * An optimized method for approximating the area of a diff --git a/include/geom/face_tri3.h b/include/geom/face_tri3.h index 583b80c8ab2..71f993a8ee0 100644 --- a/include/geom/face_tri3.h +++ b/include/geom/face_tri3.h @@ -166,7 +166,12 @@ class Tri3 : public Tri * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1}, // Side 0 + {1, 2}, // Side 1 + {2, 0} // Side 2 + }; /** * The centroid of a 3-node triangle is simply given by the diff --git a/include/geom/face_tri6.h b/include/geom/face_tri6.h index 6417999e9fd..4c53b5a5d3b 100644 --- a/include/geom/face_tri6.h +++ b/include/geom/face_tri6.h @@ -204,7 +204,12 @@ class Tri6 : public Tri * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1, 3}, // Side 0 + {1, 2, 4}, // Side 1 + {2, 0, 5} // Side 2 + }; /** * An optimized method for approximating the area of a diff --git a/include/geom/face_tri7.h b/include/geom/face_tri7.h index 833bb3ac3b6..22055d91f80 100644 --- a/include/geom/face_tri7.h +++ b/include/geom/face_tri7.h @@ -208,7 +208,12 @@ class Tri7 : public Tri * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to * element node numbers. */ - static const unsigned int side_nodes_map[num_sides][nodes_per_side]; + static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = + { + {0, 1, 3}, // Side 0 + {1, 2, 4}, // Side 1 + {2, 0, 5} // Side 2 + }; /** * \returns A bounding box (not necessarily the minimal bounding box) diff --git a/src/geom/cell_hex20.C b/src/geom/cell_hex20.C index f8351801d7d..077d01b5f16 100644 --- a/src/geom/cell_hex20.C +++ b/src/geom/cell_hex20.C @@ -34,32 +34,6 @@ const int Hex20::num_nodes; const int Hex20::nodes_per_side; const int Hex20::nodes_per_edge; -const unsigned int Hex20::side_nodes_map[Hex20::num_sides][Hex20::nodes_per_side] = - { - {0, 3, 2, 1, 11, 10, 9, 8}, // Side 0 - {0, 1, 5, 4, 8, 13, 16, 12}, // Side 1 - {1, 2, 6, 5, 9, 14, 17, 13}, // Side 2 - {2, 3, 7, 6, 10, 15, 18, 14}, // Side 3 - {3, 0, 4, 7, 11, 12, 19, 15}, // Side 4 - {4, 5, 6, 7, 16, 17, 18, 19} // Side 5 - }; - -const unsigned int Hex20::edge_nodes_map[Hex20::num_edges][Hex20::nodes_per_edge] = - { - {0, 1, 8}, // Edge 0 - {1, 2, 9}, // Edge 1 - {2, 3, 10}, // Edge 2 - {0, 3, 11}, // Edge 3 - {0, 4, 12}, // Edge 4 - {1, 5, 13}, // Edge 5 - {2, 6, 14}, // Edge 6 - {3, 7, 15}, // Edge 7 - {4, 5, 16}, // Edge 8 - {5, 6, 17}, // Edge 9 - {6, 7, 18}, // Edge 10 - {4, 7, 19} // Edge 11 - }; - // ------------------------------------------------------------ // Hex20 class member functions diff --git a/src/geom/cell_hex27.C b/src/geom/cell_hex27.C index 93528738895..4877a1fce6e 100644 --- a/src/geom/cell_hex27.C +++ b/src/geom/cell_hex27.C @@ -34,32 +34,6 @@ const int Hex27::num_nodes; const int Hex27::nodes_per_side; const int Hex27::nodes_per_edge; -const unsigned int Hex27::side_nodes_map[Hex27::num_sides][Hex27::nodes_per_side] = - { - {0, 3, 2, 1, 11, 10, 9, 8, 20}, // Side 0 - {0, 1, 5, 4, 8, 13, 16, 12, 21}, // Side 1 - {1, 2, 6, 5, 9, 14, 17, 13, 22}, // Side 2 - {2, 3, 7, 6, 10, 15, 18, 14, 23}, // Side 3 - {3, 0, 4, 7, 11, 12, 19, 15, 24}, // Side 4 - {4, 5, 6, 7, 16, 17, 18, 19, 25} // Side 5 - }; - -const unsigned int Hex27::edge_nodes_map[Hex27::num_edges][Hex27::nodes_per_edge] = - { - {0, 1, 8}, // Edge 0 - {1, 2, 9}, // Edge 1 - {2, 3, 10}, // Edge 2 - {0, 3, 11}, // Edge 3 - {0, 4, 12}, // Edge 4 - {1, 5, 13}, // Edge 5 - {2, 6, 14}, // Edge 6 - {3, 7, 15}, // Edge 7 - {4, 5, 16}, // Edge 8 - {5, 6, 17}, // Edge 9 - {6, 7, 18}, // Edge 10 - {4, 7, 19} // Edge 11 - }; - // ------------------------------------------------------------ // Hex27 class member functions diff --git a/src/geom/cell_hex8.C b/src/geom/cell_hex8.C index e3fb308739c..f157c7b9551 100644 --- a/src/geom/cell_hex8.C +++ b/src/geom/cell_hex8.C @@ -39,32 +39,6 @@ const int Hex8::num_nodes; const int Hex8::nodes_per_side; const int Hex8::nodes_per_edge; -const unsigned int Hex8::side_nodes_map[Hex8::num_sides][Hex8::nodes_per_side] = - { - {0, 3, 2, 1}, // Side 0 - {0, 1, 5, 4}, // Side 1 - {1, 2, 6, 5}, // Side 2 - {2, 3, 7, 6}, // Side 3 - {3, 0, 4, 7}, // Side 4 - {4, 5, 6, 7} // Side 5 - }; - -const unsigned int Hex8::edge_nodes_map[Hex8::num_edges][Hex8::nodes_per_edge] = - { - {0, 1}, // Edge 0 - {1, 2}, // Edge 1 - {2, 3}, // Edge 2 - {0, 3}, // Edge 3 - {0, 4}, // Edge 4 - {1, 5}, // Edge 5 - {2, 6}, // Edge 6 - {3, 7}, // Edge 7 - {4, 5}, // Edge 8 - {5, 6}, // Edge 9 - {6, 7}, // Edge 10 - {4, 7} // Edge 11 - }; - // ------------------------------------------------------------ // Hex8 class member functions diff --git a/src/geom/cell_prism15.C b/src/geom/cell_prism15.C index 1fdeded2e33..a89e280263d 100644 --- a/src/geom/cell_prism15.C +++ b/src/geom/cell_prism15.C @@ -35,28 +35,6 @@ const int Prism15::num_nodes; const int Prism15::nodes_per_side; const int Prism15::nodes_per_edge; -const unsigned int Prism15::side_nodes_map[Prism15::num_sides][Prism15::nodes_per_side] = - { - {0, 2, 1, 8, 7, 6, 99, 99}, // Side 0 - {0, 1, 4, 3, 6, 10, 12, 9}, // Side 1 - {1, 2, 5, 4, 7, 11, 13, 10}, // Side 2 - {2, 0, 3, 5, 8, 9, 14, 11}, // Side 3 - {3, 4, 5, 12, 13, 14, 99, 99} // Side 4 - }; - -const unsigned int Prism15::edge_nodes_map[Prism15::num_edges][Prism15::nodes_per_edge] = - { - {0, 1, 6}, // Edge 0 - {1, 2, 7}, // Edge 1 - {0, 2, 8}, // Edge 2 - {0, 3, 9}, // Edge 3 - {1, 4, 10}, // Edge 4 - {2, 5, 11}, // Edge 5 - {3, 4, 12}, // Edge 6 - {4, 5, 13}, // Edge 7 - {3, 5, 14} // Edge 8 - }; - // ------------------------------------------------------------ // Prism15 class member functions diff --git a/src/geom/cell_prism18.C b/src/geom/cell_prism18.C index 8b708d6d05a..373ec1e19e6 100644 --- a/src/geom/cell_prism18.C +++ b/src/geom/cell_prism18.C @@ -36,28 +36,6 @@ const int Prism18::num_nodes; const int Prism18::nodes_per_side; const int Prism18::nodes_per_edge; -const unsigned int Prism18::side_nodes_map[Prism18::num_sides][Prism18::nodes_per_side] = - { - {0, 2, 1, 8, 7, 6, 99, 99, 99}, // Side 0 - {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 - {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 - {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 - {3, 4, 5, 12, 13, 14, 99, 99, 99} // Side 4 - }; - -const unsigned int Prism18::edge_nodes_map[Prism18::num_edges][Prism18::nodes_per_edge] = - { - {0, 1, 6}, // Edge 0 - {1, 2, 7}, // Edge 1 - {0, 2, 8}, // Edge 2 - {0, 3, 9}, // Edge 3 - {1, 4, 10}, // Edge 4 - {2, 5, 11}, // Edge 5 - {3, 4, 12}, // Edge 6 - {4, 5, 13}, // Edge 7 - {3, 5, 14} // Edge 8 - }; - // ------------------------------------------------------------ // Prism18 class member functions diff --git a/src/geom/cell_prism20.C b/src/geom/cell_prism20.C index c1cab408568..8cd5de73064 100644 --- a/src/geom/cell_prism20.C +++ b/src/geom/cell_prism20.C @@ -36,28 +36,6 @@ const int Prism20::num_nodes; const int Prism20::nodes_per_side; const int Prism20::nodes_per_edge; -const unsigned int Prism20::side_nodes_map[Prism20::num_sides][Prism20::nodes_per_side] = - { - {0, 2, 1, 8, 7, 6, 18, 99, 99}, // Side 0 - {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 - {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 - {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 - {3, 4, 5, 12, 13, 14, 19, 99, 99} // Side 4 - }; - -const unsigned int Prism20::edge_nodes_map[Prism20::num_edges][Prism20::nodes_per_edge] = - { - {0, 1, 6}, // Edge 0 - {1, 2, 7}, // Edge 1 - {0, 2, 8}, // Edge 2 - {0, 3, 9}, // Edge 3 - {1, 4, 10}, // Edge 4 - {2, 5, 11}, // Edge 5 - {3, 4, 12}, // Edge 6 - {4, 5, 13}, // Edge 7 - {3, 5, 14} // Edge 8 - }; - // ------------------------------------------------------------ // Prism20 class member functions diff --git a/src/geom/cell_prism21.C b/src/geom/cell_prism21.C index 2e6a5777849..807f74cf409 100644 --- a/src/geom/cell_prism21.C +++ b/src/geom/cell_prism21.C @@ -51,28 +51,6 @@ const int Prism21::num_nodes; const int Prism21::nodes_per_side; const int Prism21::nodes_per_edge; -const unsigned int Prism21::side_nodes_map[Prism21::num_sides][Prism21::nodes_per_side] = - { - {0, 2, 1, 8, 7, 6, 18, 99, 99}, // Side 0 - {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 - {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 - {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 - {3, 4, 5, 12, 13, 14, 19, 99, 99} // Side 4 - }; - -const unsigned int Prism21::edge_nodes_map[Prism21::num_edges][Prism21::nodes_per_edge] = - { - {0, 1, 6}, // Edge 0 - {1, 2, 7}, // Edge 1 - {0, 2, 8}, // Edge 2 - {0, 3, 9}, // Edge 3 - {1, 4, 10}, // Edge 4 - {2, 5, 11}, // Edge 5 - {3, 4, 12}, // Edge 6 - {4, 5, 13}, // Edge 7 - {3, 5, 14} // Edge 8 - }; - // ------------------------------------------------------------ // Prism21 class member functions diff --git a/src/geom/cell_prism6.C b/src/geom/cell_prism6.C index a520b137cc6..6a972dc5c44 100644 --- a/src/geom/cell_prism6.C +++ b/src/geom/cell_prism6.C @@ -110,15 +110,6 @@ const int Prism6::num_nodes; const int Prism6::nodes_per_side; const int Prism6::nodes_per_edge; -const unsigned int Prism6::side_nodes_map[Prism6::num_sides][Prism6::nodes_per_side] = - { - {0, 2, 1, 99}, // Side 0 - {0, 1, 4, 3}, // Side 1 - {1, 2, 5, 4}, // Side 2 - {2, 0, 3, 5}, // Side 3 - {3, 4, 5, 99} // Side 4 - }; - const unsigned int Prism6::side_elems_map[Prism6::num_sides][Prism6::nodes_per_side] = { {0, 1, 2, 3}, // Side 0 @@ -128,19 +119,6 @@ const unsigned int Prism6::side_elems_map[Prism6::num_sides][Prism6::nodes_per_s {4, 5, 6, 7} // Side 4 }; -const unsigned int Prism6::edge_nodes_map[Prism6::num_edges][Prism6::nodes_per_edge] = - { - {0, 1}, // Edge 0 - {1, 2}, // Edge 1 - {0, 2}, // Edge 2 - {0, 3}, // Edge 3 - {1, 4}, // Edge 4 - {2, 5}, // Edge 5 - {3, 4}, // Edge 6 - {4, 5}, // Edge 7 - {3, 5} // Edge 8 - }; - // ------------------------------------------------------------ // Prism6 class member functions diff --git a/src/geom/cell_pyramid13.C b/src/geom/cell_pyramid13.C index 2f1e878af01..068252f088d 100644 --- a/src/geom/cell_pyramid13.C +++ b/src/geom/cell_pyramid13.C @@ -36,27 +36,6 @@ const int Pyramid13::num_nodes; const int Pyramid13::nodes_per_side; const int Pyramid13::nodes_per_edge; -const unsigned int Pyramid13::side_nodes_map[Pyramid13::num_sides][Pyramid13::nodes_per_side] = - { - {0, 1, 4, 5, 10, 9, 99, 99}, // Side 0 (front) - {1, 2, 4, 6, 11, 10, 99, 99}, // Side 1 (right) - {2, 3, 4, 7, 12, 11, 99, 99}, // Side 2 (back) - {3, 0, 4, 8, 9, 12, 99, 99}, // Side 3 (left) - {0, 3, 2, 1, 8, 7, 6, 5} // Side 4 (base) - }; - -const unsigned int Pyramid13::edge_nodes_map[Pyramid13::num_edges][Pyramid13::nodes_per_edge] = - { - {0, 1, 5}, // Edge 0 - {1, 2, 6}, // Edge 1 - {2, 3, 7}, // Edge 2 - {0, 3, 8}, // Edge 3 - {0, 4, 9}, // Edge 4 - {1, 4, 10}, // Edge 5 - {2, 4, 11}, // Edge 6 - {3, 4, 12} // Edge 7 - }; - // ------------------------------------------------------------ // Pyramid13 class member functions diff --git a/src/geom/cell_pyramid14.C b/src/geom/cell_pyramid14.C index 73b056e0e29..769ca89862d 100644 --- a/src/geom/cell_pyramid14.C +++ b/src/geom/cell_pyramid14.C @@ -36,27 +36,6 @@ const int Pyramid14::num_nodes; const int Pyramid14::nodes_per_side; const int Pyramid14::nodes_per_edge; -const unsigned int Pyramid14::side_nodes_map[Pyramid14::num_sides][Pyramid14::nodes_per_side] = - { - {0, 1, 4, 5, 10, 9, 99, 99, 99}, // Side 0 (front) - {1, 2, 4, 6, 11, 10, 99, 99, 99}, // Side 1 (right) - {2, 3, 4, 7, 12, 11, 99, 99, 99}, // Side 2 (back) - {3, 0, 4, 8, 9, 12, 99, 99, 99}, // Side 3 (left) - {0, 3, 2, 1, 8, 7, 6, 5, 13} // Side 4 (base) - }; - -const unsigned int Pyramid14::edge_nodes_map[Pyramid14::num_edges][Pyramid14::nodes_per_edge] = - { - {0, 1, 5}, // Edge 0 - {1, 2, 6}, // Edge 1 - {2, 3, 7}, // Edge 2 - {0, 3, 8}, // Edge 3 - {0, 4, 9}, // Edge 4 - {1, 4, 10}, // Edge 5 - {2, 4, 11}, // Edge 6 - {3, 4, 12} // Edge 7 - }; - // ------------------------------------------------------------ // Pyramid14 class member functions diff --git a/src/geom/cell_pyramid18.C b/src/geom/cell_pyramid18.C index 12f7ad69f5f..5a4d89af1e9 100644 --- a/src/geom/cell_pyramid18.C +++ b/src/geom/cell_pyramid18.C @@ -36,27 +36,6 @@ const int Pyramid18::num_nodes; const int Pyramid18::nodes_per_side; const int Pyramid18::nodes_per_edge; -const unsigned int Pyramid18::side_nodes_map[Pyramid18::num_sides][Pyramid18::nodes_per_side] = - { - {0, 1, 4, 5, 10, 9, 14, 99, 99}, // Side 0 (front) - {1, 2, 4, 6, 11, 10, 15, 99, 99}, // Side 1 (right) - {2, 3, 4, 7, 12, 11, 16, 99, 99}, // Side 2 (back) - {3, 0, 4, 8, 9, 12, 17, 99, 99}, // Side 3 (left) - {0, 3, 2, 1, 8, 7, 6, 5, 13} // Side 4 (base) - }; - -const unsigned int Pyramid18::edge_nodes_map[Pyramid18::num_edges][Pyramid18::nodes_per_edge] = - { - {0, 1, 5}, // Edge 0 - {1, 2, 6}, // Edge 1 - {2, 3, 7}, // Edge 2 - {0, 3, 8}, // Edge 3 - {0, 4, 9}, // Edge 4 - {1, 4, 10}, // Edge 5 - {2, 4, 11}, // Edge 6 - {3, 4, 12} // Edge 7 - }; - // ------------------------------------------------------------ // Pyramid18 class member functions diff --git a/src/geom/cell_pyramid5.C b/src/geom/cell_pyramid5.C index eb539aa591a..c8c997ba954 100644 --- a/src/geom/cell_pyramid5.C +++ b/src/geom/cell_pyramid5.C @@ -37,27 +37,6 @@ const int Pyramid5::num_nodes; const int Pyramid5::nodes_per_side; const int Pyramid5::nodes_per_edge; -const unsigned int Pyramid5::side_nodes_map[Pyramid5::num_sides][Pyramid5::nodes_per_side] = - { - {0, 1, 4, 99}, // Side 0 - {1, 2, 4, 99}, // Side 1 - {2, 3, 4, 99}, // Side 2 - {3, 0, 4, 99}, // Side 3 - {0, 3, 2, 1} // Side 4 - }; - -const unsigned int Pyramid5::edge_nodes_map[Pyramid5::num_edges][Pyramid5::nodes_per_edge] = - { - {0, 1}, // Edge 0 - {1, 2}, // Edge 1 - {2, 3}, // Edge 2 - {0, 3}, // Edge 3 - {0, 4}, // Edge 4 - {1, 4}, // Edge 5 - {2, 4}, // Edge 6 - {3, 4} // Edge 7 - }; - // ------------------------------------------------------------ // Pyramid5 class member functions diff --git a/src/geom/cell_tet10.C b/src/geom/cell_tet10.C index 511eba251c0..9896a24e413 100644 --- a/src/geom/cell_tet10.C +++ b/src/geom/cell_tet10.C @@ -34,24 +34,6 @@ const int Tet10::num_nodes; const int Tet10::nodes_per_side; const int Tet10::nodes_per_edge; -const unsigned int Tet10::side_nodes_map[Tet10::num_sides][Tet10::nodes_per_side] = - { - {0, 2, 1, 6, 5, 4}, // Side 0 - {0, 1, 3, 4, 8, 7}, // Side 1 - {1, 2, 3, 5, 9, 8}, // Side 2 - {2, 0, 3, 6, 7, 9} // Side 3 - }; - -const unsigned int Tet10::edge_nodes_map[Tet10::num_edges][Tet10::nodes_per_edge] = - { - {0, 1, 4}, // Edge 0 - {1, 2, 5}, // Edge 1 - {0, 2, 6}, // Edge 2 - {0, 3, 7}, // Edge 3 - {1, 3, 8}, // Edge 4 - {2, 3, 9} // Edge 5 - }; - // ------------------------------------------------------------ // Tet10 class member functions diff --git a/src/geom/cell_tet14.C b/src/geom/cell_tet14.C index b214ee1c36b..91d3a9c6538 100644 --- a/src/geom/cell_tet14.C +++ b/src/geom/cell_tet14.C @@ -42,24 +42,6 @@ const int Tet14::num_nodes; const int Tet14::nodes_per_side; const int Tet14::nodes_per_edge; -const unsigned int Tet14::side_nodes_map[Tet14::num_sides][Tet14::nodes_per_side] = - { - {0, 2, 1, 6, 5, 4, 10}, // Side 0 - {0, 1, 3, 4, 8, 7, 11}, // Side 1 - {1, 2, 3, 5, 9, 8, 12}, // Side 2 - {2, 0, 3, 6, 7, 9, 13} // Side 3 - }; - -const unsigned int Tet14::edge_nodes_map[Tet14::num_edges][Tet14::nodes_per_edge] = - { - {0, 1, 4}, // Edge 0 - {1, 2, 5}, // Edge 1 - {0, 2, 6}, // Edge 2 - {0, 3, 7}, // Edge 3 - {1, 3, 8}, // Edge 4 - {2, 3, 9} // Edge 5 - }; - // ------------------------------------------------------------ // Tet14 class member functions diff --git a/src/geom/cell_tet4.C b/src/geom/cell_tet4.C index fafde46173b..63b8216efab 100644 --- a/src/geom/cell_tet4.C +++ b/src/geom/cell_tet4.C @@ -35,24 +35,6 @@ const int Tet4::num_nodes; const int Tet4::nodes_per_side; const int Tet4::nodes_per_edge; -const unsigned int Tet4::side_nodes_map[Tet4::num_sides][Tet4::nodes_per_side] = - { - {0, 2, 1}, // Side 0 - {0, 1, 3}, // Side 1 - {1, 2, 3}, // Side 2 - {2, 0, 3} // Side 3 - }; - -const unsigned int Tet4::edge_nodes_map[Tet4::num_edges][Tet4::nodes_per_edge] = - { - {0, 1}, // Edge 0 - {1, 2}, // Edge 1 - {0, 2}, // Edge 2 - {0, 3}, // Edge 3 - {1, 3}, // Edge 4 - {2, 3} // Edge 5 - }; - // ------------------------------------------------------------ // Tet4 class member functions diff --git a/src/geom/face_quad4.C b/src/geom/face_quad4.C index 4a1d2356f8a..5cf7955b33d 100644 --- a/src/geom/face_quad4.C +++ b/src/geom/face_quad4.C @@ -32,14 +32,6 @@ namespace libMesh const int Quad4::num_nodes; const int Quad4::nodes_per_side; -const unsigned int Quad4::side_nodes_map[Quad4::num_sides][Quad4::nodes_per_side] = - { - {0, 1}, // Side 0 - {1, 2}, // Side 1 - {2, 3}, // Side 2 - {3, 0} // Side 3 - }; - #ifdef LIBMESH_ENABLE_AMR const Real Quad4::_embedding_matrix[Quad4::num_children][Quad4::num_nodes][Quad4::num_nodes] = diff --git a/src/geom/face_quad8.C b/src/geom/face_quad8.C index 720f77dd4d1..7a7cbfd2a85 100644 --- a/src/geom/face_quad8.C +++ b/src/geom/face_quad8.C @@ -32,14 +32,6 @@ namespace libMesh const int Quad8::num_nodes; const int Quad8::nodes_per_side; -const unsigned int Quad8::side_nodes_map[Quad8::num_sides][Quad8::nodes_per_side] = - { - {0, 1, 4}, // Side 0 - {1, 2, 5}, // Side 1 - {2, 3, 6}, // Side 2 - {3, 0, 7} // Side 3 - }; - #ifdef LIBMESH_ENABLE_AMR diff --git a/src/geom/face_quad9.C b/src/geom/face_quad9.C index 7182b023a43..ddc153d8320 100644 --- a/src/geom/face_quad9.C +++ b/src/geom/face_quad9.C @@ -32,14 +32,6 @@ namespace libMesh const int Quad9::num_nodes; const int Quad9::nodes_per_side; -const unsigned int Quad9::side_nodes_map[Quad9::num_sides][Quad9::nodes_per_side] = - { - {0, 1, 4}, // Side 0 - {1, 2, 5}, // Side 1 - {2, 3, 6}, // Side 2 - {3, 0, 7} // Side 3 - }; - #ifdef LIBMESH_ENABLE_AMR diff --git a/src/geom/face_tri3.C b/src/geom/face_tri3.C index 39909e62147..fc7faa3440f 100644 --- a/src/geom/face_tri3.C +++ b/src/geom/face_tri3.C @@ -31,13 +31,6 @@ namespace libMesh const int Tri3::num_nodes; const int Tri3::nodes_per_side; -const unsigned int Tri3::side_nodes_map[Tri3::num_sides][Tri3::nodes_per_side] = - { - {0, 1}, // Side 0 - {1, 2}, // Side 1 - {2, 0} // Side 2 - }; - #ifdef LIBMESH_ENABLE_AMR const Real Tri3::_embedding_matrix[Tri3::num_children][Tri3::num_nodes][Tri3::num_nodes] = diff --git a/src/geom/face_tri6.C b/src/geom/face_tri6.C index fd035d7e90a..99858591379 100644 --- a/src/geom/face_tri6.C +++ b/src/geom/face_tri6.C @@ -32,13 +32,6 @@ namespace libMesh const int Tri6::num_nodes; const int Tri6::nodes_per_side; -const unsigned int Tri6::side_nodes_map[Tri6::num_sides][Tri6::nodes_per_side] = - { - {0, 1, 3}, // Side 0 - {1, 2, 4}, // Side 1 - {2, 0, 5} // Side 2 - }; - #ifdef LIBMESH_ENABLE_AMR diff --git a/src/geom/face_tri7.C b/src/geom/face_tri7.C index e30c72ced52..c150153787b 100644 --- a/src/geom/face_tri7.C +++ b/src/geom/face_tri7.C @@ -38,13 +38,6 @@ namespace libMesh const int Tri7::num_nodes; const int Tri7::nodes_per_side; -const unsigned int Tri7::side_nodes_map[Tri7::num_sides][Tri7::nodes_per_side] = - { - {0, 1, 3}, // Side 0 - {1, 2, 4}, // Side 1 - {2, 0, 5} // Side 2 - }; - #ifdef LIBMESH_ENABLE_AMR From 9c1c2d4eeacc558be877caba4158aed607e32a6a Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Wed, 16 Sep 2026 13:57:29 -0600 Subject: [PATCH 02/10] Make Elem::type_to_n_nodes_map constexpr Move its initializer into the class definition so node counts can be composed with other compile-time lookups, e.g. type_to_n_nodes_map[side_type(t, s)] for the number of nodes on a side. --- include/geom/elem.h | 62 ++++++++++++++++++++++++++++++++++++++++++++- src/geom/elem.C | 61 -------------------------------------------- 2 files changed, 61 insertions(+), 62 deletions(-) diff --git a/include/geom/elem.h b/include/geom/elem.h index 75611008ce7..114e813811a 100644 --- a/include/geom/elem.h +++ b/include/geom/elem.h @@ -640,7 +640,67 @@ class Elem : public ReferenceCountedObject, * is fixed; for more general types like Polygon subclasses an actual * instantiated Elem must be queried. */ - static const unsigned int type_to_n_nodes_map[INVALID_ELEM]; + static constexpr unsigned int type_to_n_nodes_map[INVALID_ELEM] = + { + 2, // EDGE2 + 3, // EDGE3 + 4, // EDGE4 + + 3, // TRI3 + 6, // TRI6 + + 4, // QUAD4 + 8, // QUAD8 + 9, // QUAD9 + + 4, // TET4 + 10, // TET10 + + 8, // HEX8 + 20, // HEX20 + 27, // HEX27 + + 6, // PRISM6 + 15, // PRISM15 + 18, // PRISM18 + + 5, // PYRAMID5 + 13, // PYRAMID13 + 14, // PYRAMID14 + + 2, // INFEDGE2 + + 4, // INFQUAD4 + 6, // INFQUAD6 + + 8, // INFHEX8 + 16, // INFHEX16 + 18, // INFHEX18 + + 6, // INFPRISM6 + 12, // INFPRISM12 + + 1, // NODEELEM + + 0, // REMOTEELEM + + 3, // TRI3SUBDIVISION + 3, // TRISHELL3 + 4, // QUADSHELL4 + 8, // QUADSHELL8 + + 7, // TRI7 + 14, // TET14 + 20, // PRISM20 + 21, // PRISM21 + 18, // PYRAMID18 + + 9, // QUADSHELL9 + + invalid_uint, // C0POLYGON + invalid_uint, // C0POLYHEDRON + + }; /** * \returns The number of nodes this element contains. diff --git a/src/geom/elem.C b/src/geom/elem.C index 9440ddd9307..6ca301ba3e8 100644 --- a/src/geom/elem.C +++ b/src/geom/elem.C @@ -161,67 +161,6 @@ const unsigned int Elem::type_to_dim_map [] = const unsigned int Elem::max_n_nodes; -const unsigned int Elem::type_to_n_nodes_map [] = - { - 2, // EDGE2 - 3, // EDGE3 - 4, // EDGE4 - - 3, // TRI3 - 6, // TRI6 - - 4, // QUAD4 - 8, // QUAD8 - 9, // QUAD9 - - 4, // TET4 - 10, // TET10 - - 8, // HEX8 - 20, // HEX20 - 27, // HEX27 - - 6, // PRISM6 - 15, // PRISM15 - 18, // PRISM18 - - 5, // PYRAMID5 - 13, // PYRAMID13 - 14, // PYRAMID14 - - 2, // INFEDGE2 - - 4, // INFQUAD4 - 6, // INFQUAD6 - - 8, // INFHEX8 - 16, // INFHEX16 - 18, // INFHEX18 - - 6, // INFPRISM6 - 12, // INFPRISM12 - - 1, // NODEELEM - - 0, // REMOTEELEM - - 3, // TRI3SUBDIVISION - 3, // TRISHELL3 - 4, // QUADSHELL4 - 8, // QUADSHELL8 - - 7, // TRI7 - 14, // TET14 - 20, // PRISM20 - 21, // PRISM21 - 18, // PYRAMID18 - - 9, // QUADSHELL9 - - invalid_uint, // C0POLYGON - invalid_uint, // C0POLYHEDRON - }; - const unsigned int Elem::type_to_n_sides_map [] = { 2, // EDGE2 From faa52fd0888051d2c18f021d50183df2e021acba Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Wed, 16 Sep 2026 14:00:33 -0600 Subject: [PATCH 03/10] Static ElemType overloads of side_type/local_*_node Add Elem::side_type(ElemType, s), Elem::local_side_node(ElemType, s, n) and Elem::local_edge_node(ElemType, e, n), giving the reference-element topology of any fixed-topology type without an instantiated Elem to call the virtual versions on. They read the same static node maps the element classes use, so there is still one copy of each table. Out of range input asserts; types without a static topology error out. --- include/geom/elem.h | 30 +++++ src/geom/elem.C | 262 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 292 insertions(+) diff --git a/include/geom/elem.h b/include/geom/elem.h index 114e813811a..13f21c472bf 100644 --- a/include/geom/elem.h +++ b/include/geom/elem.h @@ -468,6 +468,27 @@ class Elem : public ReferenceCountedObject, virtual unsigned int local_edge_node(unsigned int edge, unsigned int edge_node) const = 0; + /** + * \returns The local node id for node \p side_node on side \p side of + * an element of type \p t, without needing an instantiated Elem. + * The Polygon and Polyhedron subclasses have no such map, and the + * infinite elements' maps are not read here, so those must be queried + * through an actual Elem. + */ + static unsigned int local_side_node(ElemType t, + unsigned int side, + unsigned int side_node); + + /** + * \returns The local node id for node \p edge_node on edge \p edge of + * an element of type \p t, without needing an instantiated Elem. For + * 2D types this is local_side_node(); 1D types have no edges. The + * same types are unsupported here as in local_side_node(). + */ + static unsigned int local_edge_node(ElemType t, + unsigned int edge, + unsigned int edge_node); + /** * \returns \p true if a vertex of \p e is contained * in this element. If \p mesh_connection is true, looks @@ -749,6 +770,15 @@ class Elem : public ReferenceCountedObject, */ virtual ElemType side_type (const unsigned int s) const = 0; + /** + * \returns The type of side \p s of an element of type \p t, without + * needing an instantiated Elem. The Polygon and Polyhedron subclasses + * have one side type but no fixed number of sides, so \p s goes + * unchecked for them; query an actual Elem when you have one. + */ + static ElemType side_type (const ElemType t, + const unsigned int s); + /** * \returns the normal (outwards-facing) of the side of the element at the vertex-average of the side * @param s the side of interest diff --git a/src/geom/elem.C b/src/geom/elem.C index 6ca301ba3e8..cc7b2b570fc 100644 --- a/src/geom/elem.C +++ b/src/geom/elem.C @@ -3103,6 +3103,268 @@ ElemType Elem::first_order_equivalent_type (const ElemType et) } +// Most of our elements have the same topology on every side, but the +// prisms and pyramids have both triangular and quadrilateral faces, and +// a 2D or 3D infinite element's side 0 is its finite base while the rest +// are infinite. The polygons and polyhedra have one side type but no +// fixed side count, so they can answer here too while their side index +// goes unchecked; ask an actual Elem when you have one. +ElemType Elem::side_type (const ElemType t, + const unsigned int s) +{ + libmesh_assert_less (s, type_to_n_sides_map[t]); + + switch (t) + { + case EDGE2: + case EDGE3: + case EDGE4: + return NODEELEM; + case TRI3: + case TRISHELL3: + case QUAD4: + case QUADSHELL4: + // A polygon's sides are all edges and a polyhedron's are all + // polygons, however many a given element turns out to have + case C0POLYGON: + return EDGE2; + case TRI6: + case TRI7: + case QUAD8: + case QUADSHELL8: + case QUAD9: + case QUADSHELL9: + return EDGE3; + case TET4: + return TRI3; + case TET10: + return TRI6; + case TET14: + return TRI7; + case HEX8: + return QUAD4; + case HEX20: + return QUAD8; + case HEX27: + return QUAD9; + // Sides 0 and 4 of a prism are its triangles + case PRISM6: + return (s == 0 || s == 4) ? TRI3 : QUAD4; + case PRISM15: + return (s == 0 || s == 4) ? TRI6 : QUAD8; + case PRISM18: + return (s == 0 || s == 4) ? TRI6 : QUAD9; + case PRISM20: + case PRISM21: + return (s == 0 || s == 4) ? TRI7 : QUAD9; + // Side 4 of a pyramid is its quadrilateral base + case PYRAMID5: + return (s < 4) ? TRI3 : QUAD4; + case PYRAMID13: + return (s < 4) ? TRI6 : QUAD8; + case PYRAMID14: + return (s < 4) ? TRI6 : QUAD9; + case PYRAMID18: + return (s < 4) ? TRI7 : QUAD9; + case C0POLYHEDRON: + return C0POLYGON; +#ifdef LIBMESH_ENABLE_INFINITE_ELEMENTS + // An InfEdge2's sides are its end nodes, like any other 1D element's + case INFEDGE2: + return NODEELEM; + // A 2D or 3D infinite element's side 0 is the finite base it was + // built from; its remaining sides run out to infinity with it + case INFQUAD4: + return (s == 0) ? EDGE2 : INFEDGE2; + case INFQUAD6: + return (s == 0) ? EDGE3 : INFEDGE2; + case INFHEX8: + return (s == 0) ? QUAD4 : INFQUAD4; + case INFHEX16: + return (s == 0) ? QUAD8 : INFQUAD6; + case INFHEX18: + return (s == 0) ? QUAD9 : INFQUAD6; + case INFPRISM6: + return (s == 0) ? TRI3 : INFQUAD4; + case INFPRISM12: + return (s == 0) ? TRI6 : INFQUAD6; +#endif + default: + libmesh_error_msg("No side type for element type " << Utility::enum_to_string(t)); + } + + return INVALID_ELEM; +} + + + +// This reads the same constexpr side_nodes_map tables the element +// classes use for their virtual local_side_node(), so that code which +// knows an element type but has no Elem to call a virtual function on -- +// building a side of an element that doesn't exist yet, or a device +// kernel that has only the type -- can still get at the reference +// element topology. +// +// The polygons and polyhedra have no such table to read, and the +// infinite elements' maps have no caller here yet, so both error out: +// asking for a topology we can't answer is a programming error rather +// than something to signal with a return value. +unsigned int Elem::local_side_node (const ElemType t, + const unsigned int side, + const unsigned int side_node) +{ + // The nodes on a side are the nodes of the side's own element type, so + // we compose the two lookups here rather than tabulating side node + // counts a second time. That also keeps us inside the meaningful part + // of a row: the prisms' and pyramids' triangular sides carry fewer + // nodes than their quadrilateral ones, and their rows are padded out + // to the longer length + libmesh_assert_less (side, type_to_n_sides_map[t]); + libmesh_assert_less (side_node, type_to_n_nodes_map[side_type(t, side)]); + + switch (t) + { + // A 1D element's sides are its end nodes + case EDGE2: + case EDGE3: + case EDGE4: + return side; + // The shell elements are numbered like the elements they shadow + case TRI3: + case TRISHELL3: + return Tri3::side_nodes_map[side][side_node]; + case TRI6: + return Tri6::side_nodes_map[side][side_node]; + case TRI7: + return Tri7::side_nodes_map[side][side_node]; + case QUAD4: + case QUADSHELL4: + return Quad4::side_nodes_map[side][side_node]; + case QUAD8: + case QUADSHELL8: + return Quad8::side_nodes_map[side][side_node]; + case QUAD9: + case QUADSHELL9: + return Quad9::side_nodes_map[side][side_node]; + case TET4: + return Tet4::side_nodes_map[side][side_node]; + case TET10: + return Tet10::side_nodes_map[side][side_node]; + case TET14: + return Tet14::side_nodes_map[side][side_node]; + case HEX8: + return Hex8::side_nodes_map[side][side_node]; + case HEX20: + return Hex20::side_nodes_map[side][side_node]; + case HEX27: + return Hex27::side_nodes_map[side][side_node]; + case PRISM6: + return Prism6::side_nodes_map[side][side_node]; + case PRISM15: + return Prism15::side_nodes_map[side][side_node]; + case PRISM18: + return Prism18::side_nodes_map[side][side_node]; + case PRISM20: + return Prism20::side_nodes_map[side][side_node]; + case PRISM21: + return Prism21::side_nodes_map[side][side_node]; + case PYRAMID5: + return Pyramid5::side_nodes_map[side][side_node]; + case PYRAMID13: + return Pyramid13::side_nodes_map[side][side_node]; + case PYRAMID14: + return Pyramid14::side_nodes_map[side][side_node]; + case PYRAMID18: + return Pyramid18::side_nodes_map[side][side_node]; + default: + libmesh_error_msg("No static side node map for element type " << Utility::enum_to_string(t)); + } + + return invalid_uint; +} + + + +// A 2D element's edges are its sides -- Face::local_edge_node() defines +// the two to be the same thing -- so we answer those from the side map +// and keep one copy of the 2D numbering. 1D elements have no edges to +// ask about, and like local_side_node() above we error rather than +// return a flag for a type we have no table for. +unsigned int Elem::local_edge_node (const ElemType t, + const unsigned int edge, + const unsigned int edge_node) +{ + libmesh_assert_less (edge, type_to_n_edges_map[t]); + + switch (t) + { + case TRI3: + case TRISHELL3: + case TRI6: + case TRI7: + case QUAD4: + case QUADSHELL4: + case QUAD8: + case QUADSHELL8: + case QUAD9: + case QUADSHELL9: + return local_side_node(t, edge, edge_node); + // A 3D element's edge holds its two vertices, plus a midpoint for + // the second-order types, so each case checks the node index + // against its own class's nodes_per_edge + case TET4: + libmesh_assert_less (edge_node, Tet4::nodes_per_edge); + return Tet4::edge_nodes_map[edge][edge_node]; + case TET10: + libmesh_assert_less (edge_node, Tet10::nodes_per_edge); + return Tet10::edge_nodes_map[edge][edge_node]; + case TET14: + libmesh_assert_less (edge_node, Tet14::nodes_per_edge); + return Tet14::edge_nodes_map[edge][edge_node]; + case HEX8: + libmesh_assert_less (edge_node, Hex8::nodes_per_edge); + return Hex8::edge_nodes_map[edge][edge_node]; + case HEX20: + libmesh_assert_less (edge_node, Hex20::nodes_per_edge); + return Hex20::edge_nodes_map[edge][edge_node]; + case HEX27: + libmesh_assert_less (edge_node, Hex27::nodes_per_edge); + return Hex27::edge_nodes_map[edge][edge_node]; + case PRISM6: + libmesh_assert_less (edge_node, Prism6::nodes_per_edge); + return Prism6::edge_nodes_map[edge][edge_node]; + case PRISM15: + libmesh_assert_less (edge_node, Prism15::nodes_per_edge); + return Prism15::edge_nodes_map[edge][edge_node]; + case PRISM18: + libmesh_assert_less (edge_node, Prism18::nodes_per_edge); + return Prism18::edge_nodes_map[edge][edge_node]; + case PRISM20: + libmesh_assert_less (edge_node, Prism20::nodes_per_edge); + return Prism20::edge_nodes_map[edge][edge_node]; + case PRISM21: + libmesh_assert_less (edge_node, Prism21::nodes_per_edge); + return Prism21::edge_nodes_map[edge][edge_node]; + case PYRAMID5: + libmesh_assert_less (edge_node, Pyramid5::nodes_per_edge); + return Pyramid5::edge_nodes_map[edge][edge_node]; + case PYRAMID13: + libmesh_assert_less (edge_node, Pyramid13::nodes_per_edge); + return Pyramid13::edge_nodes_map[edge][edge_node]; + case PYRAMID14: + libmesh_assert_less (edge_node, Pyramid14::nodes_per_edge); + return Pyramid14::edge_nodes_map[edge][edge_node]; + case PYRAMID18: + libmesh_assert_less (edge_node, Pyramid18::nodes_per_edge); + return Pyramid18::edge_nodes_map[edge][edge_node]; + default: + libmesh_error_msg("No static edge node map for element type " << Utility::enum_to_string(t)); + } + + return invalid_uint; +} + + ElemType Elem::second_order_equivalent_type (const ElemType et, const bool full_ordered) From 5256ae22bb1663e201645801940513fceeffdf3f Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Wed, 16 Sep 2026 15:39:07 -0600 Subject: [PATCH 04/10] Derive the second-order node maps from the first-order ones A second-order element's edge_nodes_map is its first-order equivalent's edge_nodes_map with the mid-edge node appended, numbered after the vertices in edge order; its side_nodes_map row is the first-order vertex row, then the mid-edge node of each consecutive vertex pair, then the face node if the side has one. Compute both at compile time from the first-order tables (fe_reference_element_traits.h) instead of writing them out, so the 15 second-order classes store no node tables of their own and a side map can no longer disagree with an edge map. The classes keep their side_nodes_map / edge_nodes_map members, as references to the derived arrays, so every existing use -- indexing, std::begin()/std::end() on a row, sizeof, MOOSE's ray tracing -- compiles unchanged. The only hand-written facts left are the first-order tables and the face-node rule for the seven types that have face nodes, which stay in their element headers. --- include/fe/fe_reference_element_traits.h | 170 +++++++++++++++++++++++ include/geom/cell_hex20.h | 44 ++---- include/geom/cell_hex27.h | 44 ++---- include/geom/cell_prism15.h | 40 ++---- include/geom/cell_prism18.h | 40 ++---- include/geom/cell_prism20.h | 40 ++---- include/geom/cell_prism21.h | 40 ++---- include/geom/cell_pyramid13.h | 39 ++---- include/geom/cell_pyramid14.h | 39 ++---- include/geom/cell_pyramid18.h | 39 ++---- include/geom/cell_tet10.h | 36 ++--- include/geom/cell_tet14.h | 36 ++--- include/geom/face_quad8.h | 15 +- include/geom/face_quad9.h | 15 +- include/geom/face_tri6.h | 14 +- include/geom/face_tri7.h | 14 +- include/include_HEADERS | 1 + include/libmesh/Makefile.am | 4 + 18 files changed, 346 insertions(+), 324 deletions(-) create mode 100644 include/fe/fe_reference_element_traits.h diff --git a/include/fe/fe_reference_element_traits.h b/include/fe/fe_reference_element_traits.h new file mode 100644 index 00000000000..bb7cb3d55d2 --- /dev/null +++ b/include/fe/fe_reference_element_traits.h @@ -0,0 +1,170 @@ +// The libMesh Finite Element Library. +// Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner + +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef LIBMESH_FE_REFERENCE_ELEMENT_TRAITS_H +#define LIBMESH_FE_REFERENCE_ELEMENT_TRAITS_H + +#include "libmesh/libmesh.h" // invalid_uint + +namespace libMesh +{ + +/** + * The second-order elements' node maps are not independent facts. + * Their edge_nodes_map is the first-order equivalent's edge_nodes_map + * with the mid-edge node appended, and mid-edge nodes are numbered + * after the vertices in edge order. Their side_nodes_map row is the + * first-order equivalent's vertex row, then the mid-edge node of each + * consecutive vertex pair, then the face node if the side has one. + * + * The functions here compute those tables at compile time from the + * first-order tables, so a second-order element class stores neither: + * it holds the derived tables as static constexpr members and exposes + * them through its usual side_nodes_map / edge_nodes_map names as + * references to the underlying arrays. Only the first-order tables + * and the face-node rules are written by hand. + */ + +/** + * The value the element classes pad short side_nodes_map rows with, + * e.g. the triangular sides of a Prism. + */ +static constexpr unsigned int unused_side_node = 99; + +/** + * A fixed-size table that constexpr functions can return. Element + * classes bind their side_nodes_map / edge_nodes_map references to + * \p values. + */ +template +struct ReferenceElementTable +{ + unsigned int values[Rows][Cols]; +}; + +/** + * The face-node rule for elements with no face nodes. + */ +constexpr unsigned int no_face_node (const unsigned int) +{ + return invalid_uint; +} + +/** + * \returns The edge_nodes_map of the second-order element whose + * first-order equivalent is \p Linear: each edge's two vertices, then + * its mid-edge node, numbered after the vertices in edge order. + */ +template +constexpr ReferenceElementTable +derived_edge_nodes () +{ + ReferenceElementTable t {}; + for (unsigned int e = 0; e != Edges; ++e) + { + t.values[e][0] = Linear::edge_nodes_map[e][0]; + t.values[e][1] = Linear::edge_nodes_map[e][1]; + t.values[e][2] = Linear::num_nodes + e; + } + return t; +} + +/** + * \returns The number of vertices on side \p s of a first-order + * element, i.e. the entries of its side_nodes_map row that aren't + * padding. + */ +template +constexpr unsigned int n_side_vertices (const unsigned int s) +{ + unsigned int n = 0; + for (unsigned int k = 0; k != Linear::nodes_per_side; ++k) + if (Linear::side_nodes_map[s][k] != unused_side_node) + ++n; + return n; +} + +/** + * \returns The third entry of the row of \p edges joining vertices \p a + * and \p b, i.e. that edge's mid-edge node. + */ +template +constexpr unsigned int mid_edge_node (const unsigned int (&edges)[Edges][3], + const unsigned int a, + const unsigned int b) +{ + for (unsigned int e = 0; e != Edges; ++e) + if ((edges[e][0] == a && edges[e][1] == b) || + (edges[e][0] == b && edges[e][1] == a)) + return edges[e][2]; + return invalid_uint; +} + +/** + * \returns The side_nodes_map of a 3D second-order element with + * \p Sides sides of up to \p Cols nodes each, whose first-order + * equivalent is \p Linear, whose edge_nodes_map is \p edges, and whose + * \p face_node(s) is the node at the center of side \p s (or + * \p invalid_uint if there is none). + */ +template +constexpr ReferenceElementTable +derived_side_nodes (const unsigned int (&edges)[Edges][3], + FaceNode face_node) +{ + ReferenceElementTable t {}; + for (unsigned int s = 0; s != Sides; ++s) + { + const unsigned int nv = n_side_vertices(s); + unsigned int n = 0; + for (unsigned int k = 0; k != nv; ++k) + t.values[s][n++] = Linear::side_nodes_map[s][k]; + for (unsigned int k = 0; k != nv; ++k) + t.values[s][n++] = mid_edge_node(edges, + Linear::side_nodes_map[s][k], + Linear::side_nodes_map[s][(k+1) % nv]); + if (face_node(s) != invalid_uint) + t.values[s][n++] = face_node(s); + for (; n != Cols; ++n) + t.values[s][n] = unused_side_node; + } + return t; +} + +/** + * \returns The side_nodes_map of a 2D second-order element whose + * first-order equivalent is \p Linear: each side's two vertices, then + * its mid-side node, numbered after the vertices in side order. + */ +template +constexpr ReferenceElementTable +derived_side_nodes () +{ + ReferenceElementTable t {}; + for (unsigned int s = 0; s != Sides; ++s) + { + t.values[s][0] = Linear::side_nodes_map[s][0]; + t.values[s][1] = Linear::side_nodes_map[s][1]; + t.values[s][2] = Linear::num_nodes + s; + } + return t; +} + +} // namespace libMesh + +#endif // LIBMESH_FE_REFERENCE_ELEMENT_TRAITS_H diff --git a/include/geom/cell_hex20.h b/include/geom/cell_hex20.h index b4bd6afd013..88aaefbe082 100644 --- a/include/geom/cell_hex20.h +++ b/include/geom/cell_hex20.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_hex.h" +#include "libmesh/cell_hex8.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -217,38 +219,18 @@ class Hex20 final : public Hex static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Hex8 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 3, 2, 1, 11, 10, 9, 8}, // Side 0 - {0, 1, 5, 4, 8, 13, 16, 12}, // Side 1 - {1, 2, 6, 5, 9, 14, 17, 13}, // Side 2 - {2, 3, 7, 6, 10, 15, 18, 14}, // Side 3 - {3, 0, 4, 7, 11, 12, 19, 15}, // Side 4 - {4, 5, 6, 7, 16, 17, 18, 19} // Side 5 - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 8}, // Edge 0 - {1, 2, 9}, // Edge 1 - {2, 3, 10}, // Edge 2 - {0, 3, 11}, // Edge 3 - {0, 4, 12}, // Edge 4 - {1, 5, 13}, // Edge 5 - {2, 6, 14}, // Edge 6 - {3, 7, 15}, // Edge 7 - {4, 5, 16}, // Edge 8 - {5, 6, 17}, // Edge 9 - {6, 7, 18}, // Edge 10 - {4, 7, 19} // Edge 11 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, no_face_node); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * A specialization for computing the volume of a Hex20. diff --git a/include/geom/cell_hex27.h b/include/geom/cell_hex27.h index 8ad8230297d..716f2135f87 100644 --- a/include/geom/cell_hex27.h +++ b/include/geom/cell_hex27.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_hex.h" +#include "libmesh/cell_hex8.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -232,38 +234,18 @@ class Hex27 final : public Hex static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Hex8 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 3, 2, 1, 11, 10, 9, 8, 20}, // Side 0 - {0, 1, 5, 4, 8, 13, 16, 12, 21}, // Side 1 - {1, 2, 6, 5, 9, 14, 17, 13, 22}, // Side 2 - {2, 3, 7, 6, 10, 15, 18, 14, 23}, // Side 3 - {3, 0, 4, 7, 11, 12, 19, 15, 24}, // Side 4 - {4, 5, 6, 7, 16, 17, 18, 19, 25} // Side 5 - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 8}, // Edge 0 - {1, 2, 9}, // Edge 1 - {2, 3, 10}, // Edge 2 - {0, 3, 11}, // Edge 3 - {0, 4, 12}, // Edge 4 - {1, 5, 13}, // Edge 5 - {2, 6, 14}, // Edge 6 - {3, 7, 15}, // Edge 7 - {4, 5, 16}, // Edge 8 - {5, 6, 17}, // Edge 9 - {6, 7, 18}, // Edge 10 - {4, 7, 19} // Edge 11 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, [](unsigned int s) { return 20 + s; }); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * A specialization for computing the volume of a Hex27. diff --git a/include/geom/cell_prism15.h b/include/geom/cell_prism15.h index 6e0a6304cf3..aaea6828d4c 100644 --- a/include/geom/cell_prism15.h +++ b/include/geom/cell_prism15.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_prism.h" +#include "libmesh/cell_prism6.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -222,34 +224,18 @@ class Prism15 final : public Prism static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Prism6 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 2, 1, 8, 7, 6, 99, 99}, // Side 0 - {0, 1, 4, 3, 6, 10, 12, 9}, // Side 1 - {1, 2, 5, 4, 7, 11, 13, 10}, // Side 2 - {2, 0, 3, 5, 8, 9, 14, 11}, // Side 3 - {3, 4, 5, 12, 13, 14, 99, 99} // Side 4 - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 6}, // Edge 0 - {1, 2, 7}, // Edge 1 - {0, 2, 8}, // Edge 2 - {0, 3, 9}, // Edge 3 - {1, 4, 10}, // Edge 4 - {2, 5, 11}, // Edge 5 - {3, 4, 12}, // Edge 6 - {4, 5, 13}, // Edge 7 - {3, 5, 14} // Edge 8 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, no_face_node); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * A specialization for computing the volume of a Prism15. diff --git a/include/geom/cell_prism18.h b/include/geom/cell_prism18.h index 12e46858fcb..70ad3cf69f2 100644 --- a/include/geom/cell_prism18.h +++ b/include/geom/cell_prism18.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_prism.h" +#include "libmesh/cell_prism6.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -237,34 +239,18 @@ class Prism18 final : public Prism static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Prism6 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 2, 1, 8, 7, 6, 99, 99, 99}, // Side 0 - {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 - {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 - {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 - {3, 4, 5, 12, 13, 14, 99, 99, 99} // Side 4 - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 6}, // Edge 0 - {1, 2, 7}, // Edge 1 - {0, 2, 8}, // Edge 2 - {0, 3, 9}, // Edge 3 - {1, 4, 10}, // Edge 4 - {2, 5, 11}, // Edge 5 - {3, 4, 12}, // Edge 6 - {4, 5, 13}, // Edge 7 - {3, 5, 14} // Edge 8 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, [](unsigned int s) { return (s >= 1 && s <= 3) ? 14 + s : invalid_uint; }); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * A specialization for computing the volume of a Prism18. diff --git a/include/geom/cell_prism20.h b/include/geom/cell_prism20.h index 7b7ab1975b6..0962451eb68 100644 --- a/include/geom/cell_prism20.h +++ b/include/geom/cell_prism20.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_prism.h" +#include "libmesh/cell_prism6.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -242,34 +244,18 @@ class Prism20 final : public Prism static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Prism6 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 2, 1, 8, 7, 6, 18, 99, 99}, // Side 0 - {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 - {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 - {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 - {3, 4, 5, 12, 13, 14, 19, 99, 99} // Side 4 - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 6}, // Edge 0 - {1, 2, 7}, // Edge 1 - {0, 2, 8}, // Edge 2 - {0, 3, 9}, // Edge 3 - {1, 4, 10}, // Edge 4 - {2, 5, 11}, // Edge 5 - {3, 4, 12}, // Edge 6 - {4, 5, 13}, // Edge 7 - {3, 5, 14} // Edge 8 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, [](unsigned int s) { return s == 0 ? 18u : s == 4 ? 19u : 14 + s; }); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; virtual void permute(unsigned int perm_num) override final; diff --git a/include/geom/cell_prism21.h b/include/geom/cell_prism21.h index 4cd906e369d..684de823b46 100644 --- a/include/geom/cell_prism21.h +++ b/include/geom/cell_prism21.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_prism.h" +#include "libmesh/cell_prism6.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -245,34 +247,18 @@ class Prism21 final : public Prism static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Prism6 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 2, 1, 8, 7, 6, 18, 99, 99}, // Side 0 - {0, 1, 4, 3, 6, 10, 12, 9, 15}, // Side 1 - {1, 2, 5, 4, 7, 11, 13, 10, 16}, // Side 2 - {2, 0, 3, 5, 8, 9, 14, 11, 17}, // Side 3 - {3, 4, 5, 12, 13, 14, 19, 99, 99} // Side 4 - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 6}, // Edge 0 - {1, 2, 7}, // Edge 1 - {0, 2, 8}, // Edge 2 - {0, 3, 9}, // Edge 3 - {1, 4, 10}, // Edge 4 - {2, 5, 11}, // Edge 5 - {3, 4, 12}, // Edge 6 - {4, 5, 13}, // Edge 7 - {3, 5, 14} // Edge 8 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, [](unsigned int s) { return s == 0 ? 18u : s == 4 ? 19u : 14 + s; }); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; virtual void permute(unsigned int perm_num) override final; diff --git a/include/geom/cell_pyramid13.h b/include/geom/cell_pyramid13.h index 637eb5d1255..1ab36eb3c82 100644 --- a/include/geom/cell_pyramid13.h +++ b/include/geom/cell_pyramid13.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_pyramid.h" +#include "libmesh/cell_pyramid5.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -210,33 +212,18 @@ class Pyramid13 final : public Pyramid static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Pyramid5 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 1, 4, 5, 10, 9, 99, 99}, // Side 0 (front) - {1, 2, 4, 6, 11, 10, 99, 99}, // Side 1 (right) - {2, 3, 4, 7, 12, 11, 99, 99}, // Side 2 (back) - {3, 0, 4, 8, 9, 12, 99, 99}, // Side 3 (left) - {0, 3, 2, 1, 8, 7, 6, 5} // Side 4 (base) - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 5}, // Edge 0 - {1, 2, 6}, // Edge 1 - {2, 3, 7}, // Edge 2 - {0, 3, 8}, // Edge 3 - {0, 4, 9}, // Edge 4 - {1, 4, 10}, // Edge 5 - {2, 4, 11}, // Edge 6 - {3, 4, 12} // Edge 7 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, no_face_node); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * Specialization for computing the volume of a Pyramid13. diff --git a/include/geom/cell_pyramid14.h b/include/geom/cell_pyramid14.h index 8c96baf15ff..203e8ddeeda 100644 --- a/include/geom/cell_pyramid14.h +++ b/include/geom/cell_pyramid14.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_pyramid.h" +#include "libmesh/cell_pyramid5.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -228,33 +230,18 @@ class Pyramid14 final : public Pyramid static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Pyramid5 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 1, 4, 5, 10, 9, 99, 99, 99}, // Side 0 (front) - {1, 2, 4, 6, 11, 10, 99, 99, 99}, // Side 1 (right) - {2, 3, 4, 7, 12, 11, 99, 99, 99}, // Side 2 (back) - {3, 0, 4, 8, 9, 12, 99, 99, 99}, // Side 3 (left) - {0, 3, 2, 1, 8, 7, 6, 5, 13} // Side 4 (base) - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 5}, // Edge 0 - {1, 2, 6}, // Edge 1 - {2, 3, 7}, // Edge 2 - {0, 3, 8}, // Edge 3 - {0, 4, 9}, // Edge 4 - {1, 4, 10}, // Edge 5 - {2, 4, 11}, // Edge 6 - {3, 4, 12} // Edge 7 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, [](unsigned int s) { return s == 4 ? 13 : invalid_uint; }); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * Specialization for computing the volume of a Pyramid14. diff --git a/include/geom/cell_pyramid18.h b/include/geom/cell_pyramid18.h index b4b18438b49..2cb71253b5b 100644 --- a/include/geom/cell_pyramid18.h +++ b/include/geom/cell_pyramid18.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_pyramid.h" +#include "libmesh/cell_pyramid5.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -235,33 +237,18 @@ class Pyramid18 final : public Pyramid static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Pyramid5 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 1, 4, 5, 10, 9, 14, 99, 99}, // Side 0 (front) - {1, 2, 4, 6, 11, 10, 15, 99, 99}, // Side 1 (right) - {2, 3, 4, 7, 12, 11, 16, 99, 99}, // Side 2 (back) - {3, 0, 4, 8, 9, 12, 17, 99, 99}, // Side 3 (left) - {0, 3, 2, 1, 8, 7, 6, 5, 13} // Side 4 (base) - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 5}, // Edge 0 - {1, 2, 6}, // Edge 1 - {2, 3, 7}, // Edge 2 - {0, 3, 8}, // Edge 3 - {0, 4, 9}, // Edge 4 - {1, 4, 10}, // Edge 5 - {2, 4, 11}, // Edge 6 - {3, 4, 12} // Edge 7 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, [](unsigned int s) { return s == 4 ? 13 : 14 + s; }); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; virtual void permute(unsigned int perm_num) override final; diff --git a/include/geom/cell_tet10.h b/include/geom/cell_tet10.h index a3e90044a95..2ffc12b410d 100644 --- a/include/geom/cell_tet10.h +++ b/include/geom/cell_tet10.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_tet.h" +#include "libmesh/cell_tet4.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -218,30 +220,18 @@ class Tet10 final : public Tet static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Tet4 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 2, 1, 6, 5, 4}, // Side 0 - {0, 1, 3, 4, 8, 7}, // Side 1 - {1, 2, 3, 5, 9, 8}, // Side 2 - {2, 0, 3, 6, 7, 9} // Side 3 - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 4}, // Edge 0 - {1, 2, 5}, // Edge 1 - {0, 2, 6}, // Edge 2 - {0, 3, 7}, // Edge 3 - {1, 3, 8}, // Edge 4 - {2, 3, 9} // Edge 5 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, no_face_node); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * A specialization for computing the volume of a Tet10. diff --git a/include/geom/cell_tet14.h b/include/geom/cell_tet14.h index 6abcf5873e4..bf2f4e08f97 100644 --- a/include/geom/cell_tet14.h +++ b/include/geom/cell_tet14.h @@ -22,6 +22,8 @@ // Local includes #include "libmesh/cell_tet.h" +#include "libmesh/cell_tet4.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -223,30 +225,18 @@ class Tet14 final : public Tet static const int nodes_per_edge = 3; /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * These map the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge or + * side to element node numbers. They are derived from the + * first-order Tet4 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 2, 1, 6, 5, 4, 10}, // Side 0 - {0, 1, 3, 4, 8, 7, 11}, // Side 1 - {1, 2, 3, 5, 9, 8, 12}, // Side 2 - {2, 0, 3, 6, 7, 9, 13} // Side 3 - }; - - /** - * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ edge to - * element node numbers. - */ - static constexpr unsigned int edge_nodes_map[num_edges][nodes_per_edge] = - { - {0, 1, 4}, // Edge 0 - {1, 2, 5}, // Edge 1 - {0, 2, 6}, // Edge 2 - {0, 3, 7}, // Edge 3 - {1, 3, 8}, // Edge 4 - {2, 3, 9} // Edge 5 - }; + static constexpr ReferenceElementTable + _edge_nodes = derived_edge_nodes(); + static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; + + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes + (_edge_nodes.values, [](unsigned int s) { return 10 + s; }); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; virtual void permute(unsigned int perm_num) override final; diff --git a/include/geom/face_quad8.h b/include/geom/face_quad8.h index 53262260612..9773974aef4 100644 --- a/include/geom/face_quad8.h +++ b/include/geom/face_quad8.h @@ -23,6 +23,8 @@ // Local includes #include "libmesh/libmesh_common.h" #include "libmesh/face_quad.h" +#include "libmesh/face_quad4.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -192,15 +194,12 @@ class Quad8 : public Quad /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * element node numbers. It is derived from the first-order + * Quad4 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 1, 4}, // Side 0 - {1, 2, 5}, // Side 1 - {2, 3, 6}, // Side 2 - {3, 0, 7} // Side 3 - }; + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes(); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * An optimized method for approximating the area of a diff --git a/include/geom/face_quad9.h b/include/geom/face_quad9.h index 7ac983a8839..0f9c25c13a6 100644 --- a/include/geom/face_quad9.h +++ b/include/geom/face_quad9.h @@ -23,6 +23,8 @@ // Local includes #include "libmesh/libmesh_common.h" #include "libmesh/face_quad.h" +#include "libmesh/face_quad4.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -199,15 +201,12 @@ class Quad9 : public Quad /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * element node numbers. It is derived from the first-order + * Quad4 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 1, 4}, // Side 0 - {1, 2, 5}, // Side 1 - {2, 3, 6}, // Side 2 - {3, 0, 7} // Side 3 - }; + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes(); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * An optimized method for approximating the area of a diff --git a/include/geom/face_tri6.h b/include/geom/face_tri6.h index 4c53b5a5d3b..acb62e22848 100644 --- a/include/geom/face_tri6.h +++ b/include/geom/face_tri6.h @@ -23,6 +23,8 @@ // Local includes #include "libmesh/libmesh_common.h" #include "libmesh/face_tri.h" +#include "libmesh/face_tri3.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -202,14 +204,12 @@ class Tri6 : public Tri /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * element node numbers. It is derived from the first-order + * Tri3 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 1, 3}, // Side 0 - {1, 2, 4}, // Side 1 - {2, 0, 5} // Side 2 - }; + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes(); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * An optimized method for approximating the area of a diff --git a/include/geom/face_tri7.h b/include/geom/face_tri7.h index 22055d91f80..3875b55c7df 100644 --- a/include/geom/face_tri7.h +++ b/include/geom/face_tri7.h @@ -23,6 +23,8 @@ // Local includes #include "libmesh/libmesh_common.h" #include "libmesh/face_tri.h" +#include "libmesh/face_tri3.h" +#include "libmesh/fe_reference_element_traits.h" namespace libMesh { @@ -206,14 +208,12 @@ class Tri7 : public Tri /** * This maps the \f$ j^{th} \f$ node of the \f$ i^{th} \f$ side to - * element node numbers. + * element node numbers. It is derived from the first-order + * Tri3 tables; see fe_reference_element_traits.h. */ - static constexpr unsigned int side_nodes_map[num_sides][nodes_per_side] = - { - {0, 1, 3}, // Side 0 - {1, 2, 4}, // Side 1 - {2, 0, 5} // Side 2 - }; + static constexpr ReferenceElementTable + _side_nodes = derived_side_nodes(); + static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; /** * \returns A bounding box (not necessarily the minimal bounding box) diff --git a/include/include_HEADERS b/include/include_HEADERS index a7ddd59ad09..e19ebb70d4c 100644 --- a/include/include_HEADERS +++ b/include/include_HEADERS @@ -90,6 +90,7 @@ include_HEADERS = \ fe/fe_lagrange_shape_1D.h \ fe/fe_macro.h \ fe/fe_map.h \ + fe/fe_reference_element_traits.h \ fe/fe_transformation_base.h \ fe/fe_type.h \ fe/fe_xyz_map.h \ diff --git a/include/libmesh/Makefile.am b/include/libmesh/Makefile.am index b3eb689f91c..0083c34c372 100644 --- a/include/libmesh/Makefile.am +++ b/include/libmesh/Makefile.am @@ -81,6 +81,7 @@ BUILT_SOURCES = \ fe_lagrange_shape_1D.h \ fe_macro.h \ fe_map.h \ + fe_reference_element_traits.h \ fe_transformation_base.h \ fe_type.h \ fe_xyz_map.h \ @@ -852,6 +853,9 @@ fe_macro.h: $(top_srcdir)/include/fe/fe_macro.h fe_map.h: $(top_srcdir)/include/fe/fe_map.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ +fe_reference_element_traits.h: $(top_srcdir)/include/fe/fe_reference_element_traits.h + $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ + fe_transformation_base.h: $(top_srcdir)/include/fe/fe_transformation_base.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ From 3b013ae31a468de405eeaaa39f11d299d1460f7a Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Wed, 16 Sep 2026 15:24:02 -0600 Subject: [PATCH 05/10] Unit test the static topology and node placement test_static_topology checks the new Elem::side_type/local_side_node/ local_edge_node(ElemType, ...) overloads against the virtual versions on an actual element of every type. test_higher_order_node_placement checks the one relationship the static_asserts can't: every non-vertex reference node sits at the centroid of the vertices of its edge, face or element, except for EDGE4's trisection nodes. This is the check that flagged the Pyramid reference-point bugs fixed in #4539. --- tests/geom/elem_test.C | 125 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/tests/geom/elem_test.C b/tests/geom/elem_test.C index 943e4f8554e..8204c47383f 100644 --- a/tests/geom/elem_test.C +++ b/tests/geom/elem_test.C @@ -75,6 +75,129 @@ public: } } + // The Elem::side_type/local_side_node/local_edge_node overloads that + // take an ElemType have to agree with the virtual versions they shadow; + // an element of each type is the only honest way to check that. + void test_static_topology() + { + LOG_UNIT_TEST; + + for (const auto & elem : this->_mesh->active_local_element_ptr_range()) + { + // Polytopes have no compile-time topology to compare against + if (elem->runtime_topology()) + continue; + + + const ElemType type = elem->type(); + + for (const auto s : elem->side_index_range()) + { + const ElemType side_type = Elem::side_type(type, s); + CPPUNIT_ASSERT_EQUAL(elem->side_type(s), side_type); + + // The infinite elements' node maps haven't been shared with + // the static lookups, so only their side types are checked + if (elem->infinite()) + continue; + + const auto nodes = elem->nodes_on_side(s); + CPPUNIT_ASSERT_EQUAL(std::size_t(Elem::type_to_n_nodes_map[side_type]), + nodes.size()); + for (auto n : index_range(nodes)) + { + CPPUNIT_ASSERT_EQUAL(elem->local_side_node(s, n), + Elem::local_side_node(type, s, n)); + CPPUNIT_ASSERT_EQUAL(nodes[n], + Elem::local_side_node(type, s, n)); + } + } + + // 1D elements have no edges, and Face::local_edge_node() makes a + // 2D element's edges its sides, which the loop above covered + if (elem->infinite() || elem->dim() < 2) + continue; + + for (const auto e : elem->edge_index_range()) + { + const auto nodes = elem->nodes_on_edge(e); + for (auto n : index_range(nodes)) + { + CPPUNIT_ASSERT_EQUAL(elem->local_edge_node(e, n), + Elem::local_edge_node(type, e, n)); + CPPUNIT_ASSERT_EQUAL(nodes[n], + Elem::local_edge_node(type, e, n)); + } + } + } + } + + void test_higher_order_node_placement() + { + LOG_UNIT_TEST; + + // The side/edge map consistency is a static_assert in each element + // header now; what's left to check at runtime is where the + // higher-order nodes sit. Every non-vertex reference node is at + // the centroid of the vertices of its edge, its face, or the whole + // element, except for EDGE4's nodes, which trisect it. + for (const auto & elem : this->_mesh->active_local_element_ptr_range()) + { + if (elem->infinite() || elem->runtime_topology()) + continue; + + const ElemType type = elem->type(); + + for (const auto i : elem->node_index_range()) + { + if (elem->is_vertex(i)) + continue; + + // EDGE4's interior nodes trisect it instead + if (type == EDGE4) + { + LIBMESH_ASSERT_REALVEC_EQUAL(Point(i == 2 ? Real(-1)/3 : Real(1)/3), + elem->master_point(i), + TOLERANCE*TOLERANCE); + continue; + } + + // Find the smallest subentity the node belongs to: an edge + // if it sits on one, else a face, else the element itself. + // Its vertices are what the node should be the centroid of. + std::vector subentity_vertices; + if (elem->dim() > 1) + for (const auto e : elem->edge_index_range()) + { + const auto nodes = elem->nodes_on_edge(e); + if (std::find(nodes.begin(), nodes.end(), i) != nodes.end()) + subentity_vertices = {nodes[0], nodes[1]}; + } + if (subentity_vertices.empty()) + for (const auto s : elem->side_index_range()) + { + const auto nodes = elem->nodes_on_side(s); + if (std::find(nodes.begin(), nodes.end(), i) != nodes.end()) + for (auto n : nodes) + if (elem->is_vertex(n)) + subentity_vertices.push_back(n); + } + if (subentity_vertices.empty()) + for (const auto n : elem->node_index_range()) + if (elem->is_vertex(n)) + subentity_vertices.push_back(n); + + Point centroid; + for (auto v : subentity_vertices) + centroid += elem->master_point(v); + centroid /= Real(subentity_vertices.size()); + + LIBMESH_ASSERT_REALVEC_EQUAL(centroid, elem->master_point(i), + TOLERANCE*TOLERANCE); + } + } + } + void test_quality() { LOG_UNIT_TEST; @@ -970,6 +1093,8 @@ public: #define ELEMTEST \ CPPUNIT_TEST( test_bounding_box ); \ CPPUNIT_TEST( test_ref_elem ); \ + CPPUNIT_TEST( test_static_topology ); \ + CPPUNIT_TEST( test_higher_order_node_placement ); \ CPPUNIT_TEST( test_quality ); \ CPPUNIT_TEST( test_node_edge_map_consistency ); \ CPPUNIT_TEST( test_maps ); \ From 07d1757e845e944720ffb8feb2bedb75149455f7 Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Wed, 16 Sep 2026 15:39:21 -0600 Subject: [PATCH 06/10] Re-bootstrap --- include/Makefile.in | 1 + include/libmesh/Makefile.in | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/Makefile.in b/include/Makefile.in index 00edd929c89..140ebb4df71 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -715,6 +715,7 @@ include_HEADERS = \ fe/fe_lagrange_shape_1D.h \ fe/fe_macro.h \ fe/fe_map.h \ + fe/fe_reference_element_traits.h \ fe/fe_transformation_base.h \ fe/fe_type.h \ fe/fe_xyz_map.h \ diff --git a/include/libmesh/Makefile.in b/include/libmesh/Makefile.in index b2f773c1b6c..98e72774ce4 100644 --- a/include/libmesh/Makefile.in +++ b/include/libmesh/Makefile.in @@ -574,7 +574,8 @@ BUILT_SOURCES = dirichlet_boundaries.h dof_map.h dof_map_base.h \ weighted_patch_recovery_error_estimator.h fe.h fe_abstract.h \ fe_base.h fe_compute_data.h fe_interface.h \ fe_interface_macros.h fe_lagrange_shape_1D.h fe_macro.h \ - fe_map.h fe_transformation_base.h fe_type.h fe_xyz_map.h \ + fe_map.h fe_reference_element_traits.h \ + fe_transformation_base.h fe_type.h fe_xyz_map.h \ h1_fe_transformation.h hcurl_fe_transformation.h \ hdiv_fe_transformation.h inf_fe.h inf_fe_instantiate_1D.h \ inf_fe_instantiate_2D.h inf_fe_instantiate_3D.h inf_fe_macro.h \ @@ -1200,6 +1201,9 @@ fe_macro.h: $(top_srcdir)/include/fe/fe_macro.h fe_map.h: $(top_srcdir)/include/fe/fe_map.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ +fe_reference_element_traits.h: $(top_srcdir)/include/fe/fe_reference_element_traits.h + $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ + fe_transformation_base.h: $(top_srcdir)/include/fe/fe_transformation_base.h $(AM_V_GEN)rm -f $@ && $(LN_S) -f $< $@ From 5ae6719bd761f38f435f83c9329332996a3240a9 Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Fri, 25 Sep 2026 10:28:59 -0600 Subject: [PATCH 07/10] Add Elem::edge_type() local_edge_node() was checking its node index against each element class's own nodes_per_edge, fifteen copies of a fact we can compose from lookups we already have. A finite element's edges all have the same type, unlike its sides, so this needs no edge index, and the check becomes type_to_n_nodes_map[edge_type(t)] to match the side version. There is no virtual edge_type() to test the new one against, but the element classes state the same fact when they build an edge, so the unit test compares with build_edge_ptr(e)->type(). --- include/geom/elem.h | 9 +++++ src/geom/elem.C | 75 ++++++++++++++++++++++++++++++++---------- tests/geom/elem_test.C | 8 +++++ 3 files changed, 74 insertions(+), 18 deletions(-) diff --git a/include/geom/elem.h b/include/geom/elem.h index 13f21c472bf..43ec15d5cc3 100644 --- a/include/geom/elem.h +++ b/include/geom/elem.h @@ -779,6 +779,15 @@ class Elem : public ReferenceCountedObject, static ElemType side_type (const ElemType t, const unsigned int s); + /** + * \returns The type of every edge of an element of type \p t, or + * \p INVALID_ELEM for the 1D types, which have no edges. Unlike its + * sides, a finite element's edges all have the same type, so no edge + * index is needed; the infinite elements, whose finite and infinite + * edges differ, are not answered here. + */ + static ElemType edge_type (const ElemType t); + /** * \returns the normal (outwards-facing) of the side of the element at the vertex-average of the side * @param s the side of interest diff --git a/src/geom/elem.C b/src/geom/elem.C index cc7b2b570fc..85a4896549c 100644 --- a/src/geom/elem.C +++ b/src/geom/elem.C @@ -3209,6 +3209,62 @@ ElemType Elem::side_type (const ElemType t, // infinite elements' maps have no caller here yet, so both error out: // asking for a topology we can't answer is a programming error rather // than something to signal with a return value. +// A finite element's edges all have the same type, unlike its sides, +// which differ on the prisms and pyramids, so this takes no edge index. +// The infinite elements are the exception -- the edges of their finite +// base are not the ones running out to infinity -- and like the rest of +// the static topology lookups they are left to their virtual overrides. +ElemType Elem::edge_type (const ElemType t) +{ + switch (t) + { + // 1D elements have no edges + case EDGE2: + case EDGE3: + case EDGE4: + return INVALID_ELEM; + // A 2D element's edges are its sides + case TRI3: + case TRISHELL3: + case TRI6: + case TRI7: + case QUAD4: + case QUADSHELL4: + case QUAD8: + case QUADSHELL8: + case QUAD9: + case QUADSHELL9: + case C0POLYGON: + return side_type(t, 0); + // A first-order 3D element's edges hold their two vertices + case TET4: + case HEX8: + case PRISM6: + case PYRAMID5: + case C0POLYHEDRON: + return EDGE2; + // and a second-order element's add the midpoint + case TET10: + case TET14: + case HEX20: + case HEX27: + case PRISM15: + case PRISM18: + case PRISM20: + case PRISM21: + case PYRAMID13: + case PYRAMID14: + case PYRAMID18: + return EDGE3; + default: + libmesh_error_msg("No edge type for element type " << Utility::enum_to_string(t)); + } + + return INVALID_ELEM; +} + + + unsigned int Elem::local_side_node (const ElemType t, const unsigned int side, const unsigned int side_node) @@ -3295,6 +3351,7 @@ unsigned int Elem::local_edge_node (const ElemType t, const unsigned int edge_node) { libmesh_assert_less (edge, type_to_n_edges_map[t]); + libmesh_assert_less (edge_node, type_to_n_nodes_map[edge_type(t)]); switch (t) { @@ -3309,53 +3366,35 @@ unsigned int Elem::local_edge_node (const ElemType t, case QUAD9: case QUADSHELL9: return local_side_node(t, edge, edge_node); - // A 3D element's edge holds its two vertices, plus a midpoint for - // the second-order types, so each case checks the node index - // against its own class's nodes_per_edge case TET4: - libmesh_assert_less (edge_node, Tet4::nodes_per_edge); return Tet4::edge_nodes_map[edge][edge_node]; case TET10: - libmesh_assert_less (edge_node, Tet10::nodes_per_edge); return Tet10::edge_nodes_map[edge][edge_node]; case TET14: - libmesh_assert_less (edge_node, Tet14::nodes_per_edge); return Tet14::edge_nodes_map[edge][edge_node]; case HEX8: - libmesh_assert_less (edge_node, Hex8::nodes_per_edge); return Hex8::edge_nodes_map[edge][edge_node]; case HEX20: - libmesh_assert_less (edge_node, Hex20::nodes_per_edge); return Hex20::edge_nodes_map[edge][edge_node]; case HEX27: - libmesh_assert_less (edge_node, Hex27::nodes_per_edge); return Hex27::edge_nodes_map[edge][edge_node]; case PRISM6: - libmesh_assert_less (edge_node, Prism6::nodes_per_edge); return Prism6::edge_nodes_map[edge][edge_node]; case PRISM15: - libmesh_assert_less (edge_node, Prism15::nodes_per_edge); return Prism15::edge_nodes_map[edge][edge_node]; case PRISM18: - libmesh_assert_less (edge_node, Prism18::nodes_per_edge); return Prism18::edge_nodes_map[edge][edge_node]; case PRISM20: - libmesh_assert_less (edge_node, Prism20::nodes_per_edge); return Prism20::edge_nodes_map[edge][edge_node]; case PRISM21: - libmesh_assert_less (edge_node, Prism21::nodes_per_edge); return Prism21::edge_nodes_map[edge][edge_node]; case PYRAMID5: - libmesh_assert_less (edge_node, Pyramid5::nodes_per_edge); return Pyramid5::edge_nodes_map[edge][edge_node]; case PYRAMID13: - libmesh_assert_less (edge_node, Pyramid13::nodes_per_edge); return Pyramid13::edge_nodes_map[edge][edge_node]; case PYRAMID14: - libmesh_assert_less (edge_node, Pyramid14::nodes_per_edge); return Pyramid14::edge_nodes_map[edge][edge_node]; case PYRAMID18: - libmesh_assert_less (edge_node, Pyramid18::nodes_per_edge); return Pyramid18::edge_nodes_map[edge][edge_node]; default: libmesh_error_msg("No static edge node map for element type " << Utility::enum_to_string(t)); diff --git a/tests/geom/elem_test.C b/tests/geom/elem_test.C index 8204c47383f..ef472054839 100644 --- a/tests/geom/elem_test.C +++ b/tests/geom/elem_test.C @@ -120,7 +120,15 @@ public: for (const auto e : elem->edge_index_range()) { + // There is no virtual edge_type() to check against, but the + // element classes state the same fact when they build an + // edge, so compare with that instead + CPPUNIT_ASSERT_EQUAL(elem->build_edge_ptr(e)->type(), + Elem::edge_type(type)); + const auto nodes = elem->nodes_on_edge(e); + CPPUNIT_ASSERT_EQUAL(std::size_t(Elem::type_to_n_nodes_map[Elem::edge_type(type)]), + nodes.size()); for (auto n : index_range(nodes)) { CPPUNIT_ASSERT_EQUAL(elem->local_edge_node(e, n), From 5280c985aaf62bdeae81310000010ece390bb8bb Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Fri, 25 Sep 2026 10:28:59 -0600 Subject: [PATCH 08/10] Rename Linear and ReferenceElementTable "Linear" means linear algebra here -- LinearSolver, LinearImplicitSystem, LinearPartitioner -- and our word for this is first order, as in first_order_equivalent_type(), so the derivation's template parameter is FirstOrder. ReferenceElem is already the namespace handing out singleton reference Elems, so what these functions return is a NodeMapTable. --- include/fe/fe_reference_element_traits.h | 52 ++++++++++++------------ include/geom/cell_hex20.h | 4 +- include/geom/cell_hex27.h | 4 +- include/geom/cell_prism15.h | 4 +- include/geom/cell_prism18.h | 4 +- include/geom/cell_prism20.h | 4 +- include/geom/cell_prism21.h | 4 +- include/geom/cell_pyramid13.h | 4 +- include/geom/cell_pyramid14.h | 4 +- include/geom/cell_pyramid18.h | 4 +- include/geom/cell_tet10.h | 4 +- include/geom/cell_tet14.h | 4 +- include/geom/face_quad8.h | 2 +- include/geom/face_quad9.h | 2 +- include/geom/face_tri6.h | 2 +- include/geom/face_tri7.h | 2 +- 16 files changed, 52 insertions(+), 52 deletions(-) diff --git a/include/fe/fe_reference_element_traits.h b/include/fe/fe_reference_element_traits.h index bb7cb3d55d2..1af7be5bc9c 100644 --- a/include/fe/fe_reference_element_traits.h +++ b/include/fe/fe_reference_element_traits.h @@ -51,7 +51,7 @@ static constexpr unsigned int unused_side_node = 99; * \p values. */ template -struct ReferenceElementTable +struct NodeMapTable { unsigned int values[Rows][Cols]; }; @@ -66,19 +66,19 @@ constexpr unsigned int no_face_node (const unsigned int) /** * \returns The edge_nodes_map of the second-order element whose - * first-order equivalent is \p Linear: each edge's two vertices, then + * first-order equivalent is \p FirstOrder: each edge's two vertices, then * its mid-edge node, numbered after the vertices in edge order. */ -template -constexpr ReferenceElementTable +template +constexpr NodeMapTable derived_edge_nodes () { - ReferenceElementTable t {}; + NodeMapTable t {}; for (unsigned int e = 0; e != Edges; ++e) { - t.values[e][0] = Linear::edge_nodes_map[e][0]; - t.values[e][1] = Linear::edge_nodes_map[e][1]; - t.values[e][2] = Linear::num_nodes + e; + t.values[e][0] = FirstOrder::edge_nodes_map[e][0]; + t.values[e][1] = FirstOrder::edge_nodes_map[e][1]; + t.values[e][2] = FirstOrder::num_nodes + e; } return t; } @@ -88,12 +88,12 @@ derived_edge_nodes () * element, i.e. the entries of its side_nodes_map row that aren't * padding. */ -template +template constexpr unsigned int n_side_vertices (const unsigned int s) { unsigned int n = 0; - for (unsigned int k = 0; k != Linear::nodes_per_side; ++k) - if (Linear::side_nodes_map[s][k] != unused_side_node) + for (unsigned int k = 0; k != FirstOrder::nodes_per_side; ++k) + if (FirstOrder::side_nodes_map[s][k] != unused_side_node) ++n; return n; } @@ -117,27 +117,27 @@ constexpr unsigned int mid_edge_node (const unsigned int (&edges)[Edges][3], /** * \returns The side_nodes_map of a 3D second-order element with * \p Sides sides of up to \p Cols nodes each, whose first-order - * equivalent is \p Linear, whose edge_nodes_map is \p edges, and whose + * equivalent is \p FirstOrder, whose edge_nodes_map is \p edges, and whose * \p face_node(s) is the node at the center of side \p s (or * \p invalid_uint if there is none). */ -template -constexpr ReferenceElementTable +constexpr NodeMapTable derived_side_nodes (const unsigned int (&edges)[Edges][3], FaceNode face_node) { - ReferenceElementTable t {}; + NodeMapTable t {}; for (unsigned int s = 0; s != Sides; ++s) { - const unsigned int nv = n_side_vertices(s); + const unsigned int nv = n_side_vertices(s); unsigned int n = 0; for (unsigned int k = 0; k != nv; ++k) - t.values[s][n++] = Linear::side_nodes_map[s][k]; + t.values[s][n++] = FirstOrder::side_nodes_map[s][k]; for (unsigned int k = 0; k != nv; ++k) t.values[s][n++] = mid_edge_node(edges, - Linear::side_nodes_map[s][k], - Linear::side_nodes_map[s][(k+1) % nv]); + FirstOrder::side_nodes_map[s][k], + FirstOrder::side_nodes_map[s][(k+1) % nv]); if (face_node(s) != invalid_uint) t.values[s][n++] = face_node(s); for (; n != Cols; ++n) @@ -148,19 +148,19 @@ derived_side_nodes (const unsigned int (&edges)[Edges][3], /** * \returns The side_nodes_map of a 2D second-order element whose - * first-order equivalent is \p Linear: each side's two vertices, then + * first-order equivalent is \p FirstOrder: each side's two vertices, then * its mid-side node, numbered after the vertices in side order. */ -template -constexpr ReferenceElementTable +template +constexpr NodeMapTable derived_side_nodes () { - ReferenceElementTable t {}; + NodeMapTable t {}; for (unsigned int s = 0; s != Sides; ++s) { - t.values[s][0] = Linear::side_nodes_map[s][0]; - t.values[s][1] = Linear::side_nodes_map[s][1]; - t.values[s][2] = Linear::num_nodes + s; + t.values[s][0] = FirstOrder::side_nodes_map[s][0]; + t.values[s][1] = FirstOrder::side_nodes_map[s][1]; + t.values[s][2] = FirstOrder::num_nodes + s; } return t; } diff --git a/include/geom/cell_hex20.h b/include/geom/cell_hex20.h index 88aaefbe082..f857d3e0184 100644 --- a/include/geom/cell_hex20.h +++ b/include/geom/cell_hex20.h @@ -223,11 +223,11 @@ class Hex20 final : public Hex * side to element node numbers. They are derived from the * first-order Hex8 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, no_face_node); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_hex27.h b/include/geom/cell_hex27.h index 716f2135f87..1dd50c3079f 100644 --- a/include/geom/cell_hex27.h +++ b/include/geom/cell_hex27.h @@ -238,11 +238,11 @@ class Hex27 final : public Hex * side to element node numbers. They are derived from the * first-order Hex8 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, [](unsigned int s) { return 20 + s; }); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_prism15.h b/include/geom/cell_prism15.h index aaea6828d4c..8227ca142cf 100644 --- a/include/geom/cell_prism15.h +++ b/include/geom/cell_prism15.h @@ -228,11 +228,11 @@ class Prism15 final : public Prism * side to element node numbers. They are derived from the * first-order Prism6 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, no_face_node); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_prism18.h b/include/geom/cell_prism18.h index 70ad3cf69f2..646a76675ad 100644 --- a/include/geom/cell_prism18.h +++ b/include/geom/cell_prism18.h @@ -243,11 +243,11 @@ class Prism18 final : public Prism * side to element node numbers. They are derived from the * first-order Prism6 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, [](unsigned int s) { return (s >= 1 && s <= 3) ? 14 + s : invalid_uint; }); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_prism20.h b/include/geom/cell_prism20.h index 0962451eb68..b9a2eb335db 100644 --- a/include/geom/cell_prism20.h +++ b/include/geom/cell_prism20.h @@ -248,11 +248,11 @@ class Prism20 final : public Prism * side to element node numbers. They are derived from the * first-order Prism6 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, [](unsigned int s) { return s == 0 ? 18u : s == 4 ? 19u : 14 + s; }); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_prism21.h b/include/geom/cell_prism21.h index 684de823b46..2ceb2567753 100644 --- a/include/geom/cell_prism21.h +++ b/include/geom/cell_prism21.h @@ -251,11 +251,11 @@ class Prism21 final : public Prism * side to element node numbers. They are derived from the * first-order Prism6 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, [](unsigned int s) { return s == 0 ? 18u : s == 4 ? 19u : 14 + s; }); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_pyramid13.h b/include/geom/cell_pyramid13.h index 1ab36eb3c82..8208f1f84c3 100644 --- a/include/geom/cell_pyramid13.h +++ b/include/geom/cell_pyramid13.h @@ -216,11 +216,11 @@ class Pyramid13 final : public Pyramid * side to element node numbers. They are derived from the * first-order Pyramid5 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, no_face_node); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_pyramid14.h b/include/geom/cell_pyramid14.h index 203e8ddeeda..4329d6a896a 100644 --- a/include/geom/cell_pyramid14.h +++ b/include/geom/cell_pyramid14.h @@ -234,11 +234,11 @@ class Pyramid14 final : public Pyramid * side to element node numbers. They are derived from the * first-order Pyramid5 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, [](unsigned int s) { return s == 4 ? 13 : invalid_uint; }); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_pyramid18.h b/include/geom/cell_pyramid18.h index 2cb71253b5b..0df26624e65 100644 --- a/include/geom/cell_pyramid18.h +++ b/include/geom/cell_pyramid18.h @@ -241,11 +241,11 @@ class Pyramid18 final : public Pyramid * side to element node numbers. They are derived from the * first-order Pyramid5 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, [](unsigned int s) { return s == 4 ? 13 : 14 + s; }); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_tet10.h b/include/geom/cell_tet10.h index 2ffc12b410d..12b5b7c20bd 100644 --- a/include/geom/cell_tet10.h +++ b/include/geom/cell_tet10.h @@ -224,11 +224,11 @@ class Tet10 final : public Tet * side to element node numbers. They are derived from the * first-order Tet4 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, no_face_node); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/cell_tet14.h b/include/geom/cell_tet14.h index bf2f4e08f97..42362fc8f21 100644 --- a/include/geom/cell_tet14.h +++ b/include/geom/cell_tet14.h @@ -229,11 +229,11 @@ class Tet14 final : public Tet * side to element node numbers. They are derived from the * first-order Tet4 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _edge_nodes = derived_edge_nodes(); static constexpr const unsigned int (&edge_nodes_map)[num_edges][nodes_per_edge] = _edge_nodes.values; - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes (_edge_nodes.values, [](unsigned int s) { return 10 + s; }); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/face_quad8.h b/include/geom/face_quad8.h index 9773974aef4..6e5ad55600a 100644 --- a/include/geom/face_quad8.h +++ b/include/geom/face_quad8.h @@ -197,7 +197,7 @@ class Quad8 : public Quad * element node numbers. It is derived from the first-order * Quad4 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes(); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/face_quad9.h b/include/geom/face_quad9.h index 0f9c25c13a6..28b3aff481f 100644 --- a/include/geom/face_quad9.h +++ b/include/geom/face_quad9.h @@ -204,7 +204,7 @@ class Quad9 : public Quad * element node numbers. It is derived from the first-order * Quad4 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes(); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/face_tri6.h b/include/geom/face_tri6.h index acb62e22848..38494ce86f2 100644 --- a/include/geom/face_tri6.h +++ b/include/geom/face_tri6.h @@ -207,7 +207,7 @@ class Tri6 : public Tri * element node numbers. It is derived from the first-order * Tri3 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes(); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; diff --git a/include/geom/face_tri7.h b/include/geom/face_tri7.h index 3875b55c7df..70fb3522772 100644 --- a/include/geom/face_tri7.h +++ b/include/geom/face_tri7.h @@ -211,7 +211,7 @@ class Tri7 : public Tri * element node numbers. It is derived from the first-order * Tri3 tables; see fe_reference_element_traits.h. */ - static constexpr ReferenceElementTable + static constexpr NodeMapTable _side_nodes = derived_side_nodes(); static constexpr const unsigned int (&side_nodes_map)[num_sides][nodes_per_side] = _side_nodes.values; From 05df3c37f8c8e8341b21dc4a4f9faf0d10944cb6 Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Fri, 25 Sep 2026 10:28:59 -0600 Subject: [PATCH 09/10] Fix whitespace and clarify the 2D edge comment The side loop covers a 2D element's edge nodes in spirit, but it does not exercise nodes_on_edge() or local_edge_node(), so the edge loop still earns its 2D coverage; the comment implied otherwise. --- tests/geom/elem_test.C | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/geom/elem_test.C b/tests/geom/elem_test.C index ef472054839..424d2662e70 100644 --- a/tests/geom/elem_test.C +++ b/tests/geom/elem_test.C @@ -88,7 +88,6 @@ public: if (elem->runtime_topology()) continue; - const ElemType type = elem->type(); for (const auto s : elem->side_index_range()) @@ -113,8 +112,9 @@ public: } } - // 1D elements have no edges, and Face::local_edge_node() makes a - // 2D element's edges its sides, which the loop above covered + // 1D elements have no edges. A 2D element's edges are its sides, + // but nodes_on_edge() and local_edge_node() are not the APIs the + // loop above exercised, so those are checked here too. if (elem->infinite() || elem->dim() < 2) continue; From 3bd68905ad6f12c367cc42504dd0b49d8bd78031 Mon Sep 17 00:00:00 2001 From: Rochishnu Chowdhury Date: Fri, 25 Sep 2026 10:28:59 -0600 Subject: [PATCH 10/10] Test the polytopes' side and edge types A polygon's sides are all edges and a polyhedron's are all polygons, however many of them a given element has, so side_type() and edge_type() do answer for them, and the test can check those answers against the virtual side_type() and against build_edge_ptr(). Only the node map assertions have to stop, since the runtime-topology elements have no static maps. --- tests/geom/elem_test.C | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/geom/elem_test.C b/tests/geom/elem_test.C index 424d2662e70..fc3ceeef706 100644 --- a/tests/geom/elem_test.C +++ b/tests/geom/elem_test.C @@ -84,10 +84,6 @@ public: for (const auto & elem : this->_mesh->active_local_element_ptr_range()) { - // Polytopes have no compile-time topology to compare against - if (elem->runtime_topology()) - continue; - const ElemType type = elem->type(); for (const auto s : elem->side_index_range()) @@ -95,9 +91,11 @@ public: const ElemType side_type = Elem::side_type(type, s); CPPUNIT_ASSERT_EQUAL(elem->side_type(s), side_type); - // The infinite elements' node maps haven't been shared with - // the static lookups, so only their side types are checked - if (elem->infinite()) + // A polytope's side type follows from its element type even + // though its side count does not, but it has no static node + // map to check; neither are the infinite elements' maps read + // by these lookups, so both stop at the side type. + if (elem->runtime_topology() || elem->infinite()) continue; const auto nodes = elem->nodes_on_side(s); @@ -126,6 +124,9 @@ public: CPPUNIT_ASSERT_EQUAL(elem->build_edge_ptr(e)->type(), Elem::edge_type(type)); + if (elem->runtime_topology()) + continue; + const auto nodes = elem->nodes_on_edge(e); CPPUNIT_ASSERT_EQUAL(std::size_t(Elem::type_to_n_nodes_map[Elem::edge_type(type)]), nodes.size());