libMesh
Loading...
Searching...
No Matches
Public Member Functions | Protected Member Functions | List of all members
VolumeTest Class Reference
Inheritance diagram for VolumeTest:
[legend]

Public Member Functions

 LIBMESH_CPPUNIT_TEST_SUITE (VolumeTest)
 
 CPPUNIT_TEST (testTwistedVolume)
 
 CPPUNIT_TEST (testEdge3Volume)
 
 CPPUNIT_TEST (testEdge3Invertible)
 
 CPPUNIT_TEST (testEdge4Invertible)
 
 CPPUNIT_TEST (testQuad4Invertible)
 
 CPPUNIT_TEST (testTri3TrueCentroid)
 
 CPPUNIT_TEST (testQuad4TrueCentroid)
 
 CPPUNIT_TEST (testPyramid5TrueCentroid)
 
 CPPUNIT_TEST (testHex8TrueCentroid)
 
 CPPUNIT_TEST (testPrism6TrueCentroid)
 
 CPPUNIT_TEST (testHex20PLevelTrueCentroid)
 
 CPPUNIT_TEST (testQuad4AspectRatio)
 
 CPPUNIT_TEST (testQuad4Warpage)
 
 CPPUNIT_TEST (testQuad4MinMaxAngle)
 
 CPPUNIT_TEST (testQuad4Jacobian)
 
 CPPUNIT_TEST (testTri3AspectRatio)
 
 CPPUNIT_TEST (testTet4DihedralAngle)
 
 CPPUNIT_TEST (testTet4Jacobian)
 
 CPPUNIT_TEST (testC0PolygonSquare)
 
 CPPUNIT_TEST (testC0PolygonQuad)
 
 CPPUNIT_TEST (testC0PolygonPentagon)
 
 CPPUNIT_TEST (testC0PolygonHexagon)
 
 CPPUNIT_TEST (testC0PolyhedronCube)
 
 CPPUNIT_TEST (testC0PolyhedronHexagonalPrism)
 
 CPPUNIT_TEST_SUITE_END ()
 
void setUp ()
 
void tearDown ()
 
void testTri3TrueCentroid ()
 
void testQuad4TrueCentroid ()
 
void testPyramid5TrueCentroid ()
 
void testHex8TrueCentroid ()
 
void testPrism6TrueCentroid ()
 
void testHex20PLevelTrueCentroid ()
 
void testTwistedVolume ()
 
void testEdge3Volume ()
 
void testEdge3Invertible ()
 
void testEdge4Invertible ()
 
void testQuad4Invertible ()
 
void testQuad4AspectRatio ()
 
void testTri3AspectRatio ()
 
void testQuad4Warpage ()
 
void testQuad4MinMaxAngle ()
 
void testQuad4Jacobian ()
 
void testTet4DihedralAngle ()
 
void testTet4Jacobian ()
 
void testC0Polygon (const std::vector< Point > &points, Real expected_volume)
 
void testC0PolygonSquare ()
 
void testC0PolygonQuad ()
 
void testC0PolygonPentagon ()
 
void testC0PolygonHexagon ()
 

Protected Member Functions

std::pair< std::unique_ptr< Elem >, std::vector< std::unique_ptr< Node > > > construct_elem (const std::vector< Point > &pts, ElemType elem_type)
 
ElembuildC0Polyhedron (std::vector< std::shared_ptr< Polygon > > sides, MeshBase &mesh)
 
void testC0PolygonMethods (MeshBase &mesh, unsigned int n_sides)
 
void testC0PolyhedronMethods (MeshBase &mesh, bool has_midnode)
 
void testElemVolume (const Elem *elem, Real expected_volume)
 
void testC0PolyhedronCube ()
 
void testC0PolyhedronHexagonalPrism ()
 
bool test_elem_invertible (const std::vector< Point > &pts, ElemType elem_type)
 
void test_true_centroid_and_volume (ElemType elem_type)
 

Detailed Description

Definition at line 25 of file volume_test.C.

Member Function Documentation

◆ buildC0Polyhedron()

Elem * VolumeTest::buildC0Polyhedron ( std::vector< std::shared_ptr< Polygon > >  sides,
MeshBase mesh 
)
inlineprotected

Definition at line 997 of file volume_test.C.

998 {
999 std::unique_ptr<libMesh::Node> mid_elem_node;
1000 std::unique_ptr<Elem> polyhedron = std::make_unique<C0Polyhedron>(sides, mid_elem_node);
1001 if (mid_elem_node)
1002 mesh.add_node(std::move(mid_elem_node));
1003 polyhedron->set_id() = 0;
1004 Elem * elem = mesh.add_elem(std::move(polyhedron));
1005 libmesh_assert(dynamic_cast<C0Polyhedron *>(elem));
1007
1008 return elem;
1009 }
The C0Polyhedron is an element in 3D with an arbitrary (but fixed) number of polygonal first-order (C...
dof_id_type & set_id()
Definition dof_object.h:827
This is the base class from which all geometric element types are derived.
Definition elem.h:96
void prepare_for_use(const bool skip_renumber_nodes_and_elements, const bool skip_find_neighbors)
Prepare a newly created (or read) mesh for use.
Definition mesh_base.C:824
virtual Node * add_node(Node *n)=0
Add Node n to the end of the vertex array.
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
MeshBase & mesh
libmesh_assert(ctx)

References libMesh::MeshBase::add_elem(), libMesh::MeshBase::add_node(), libMesh::libmesh_assert(), mesh, libMesh::MeshBase::prepare_for_use(), and libMesh::DofObject::set_id().

Referenced by testC0PolyhedronCube(), and testC0PolyhedronHexagonalPrism().

◆ construct_elem()

std::pair< std::unique_ptr< Elem >, std::vector< std::unique_ptr< Node > > > VolumeTest::construct_elem ( const std::vector< Point > &  pts,
ElemType  elem_type 
)
inlineprotected

Definition at line 968 of file volume_test.C.

970 {
971 const unsigned int n_points = pts.size();
972
973 // Create Nodes
974 std::vector<std::unique_ptr<Node>> nodes(n_points);
975 for (unsigned int i=0; i<n_points; i++)
976 nodes[i] = Node::build(pts[i], /*id*/ i);
977
978 // Create Elem, assign nodes
979 std::unique_ptr<Elem> elem = Elem::build(elem_type, /*parent*/ nullptr);
980
981 // Make sure we were passed consistent input to build this type of Elem
982 libmesh_error_msg_if(elem->n_nodes() != n_points,
983 "Wrong number of points "
984 << n_points
985 << " provided to build a "
986 << Utility::enum_to_string(elem_type));
987
988 for (unsigned int i=0; i<n_points; i++)
989 elem->set_node(i, nodes[i].get());
990
991 // Return Elem and Nodes we created
992 return std::make_pair(std::move(elem), std::move(nodes));
993 }
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
static std::unique_ptr< Node > build(const Node &n)
Definition node.h:315
std::string enum_to_string(const T e)

References libMesh::Elem::build(), libMesh::Node::build(), and libMesh::Utility::enum_to_string().

Referenced by test_elem_invertible(), testQuad4AspectRatio(), testQuad4Jacobian(), testQuad4MinMaxAngle(), testQuad4Warpage(), testTet4DihedralAngle(), testTet4Jacobian(), and testTri3AspectRatio().

◆ CPPUNIT_TEST() [1/24]

VolumeTest::CPPUNIT_TEST ( testC0PolygonHexagon  )

◆ CPPUNIT_TEST() [2/24]

VolumeTest::CPPUNIT_TEST ( testC0PolygonPentagon  )

◆ CPPUNIT_TEST() [3/24]

VolumeTest::CPPUNIT_TEST ( testC0PolygonQuad  )

◆ CPPUNIT_TEST() [4/24]

VolumeTest::CPPUNIT_TEST ( testC0PolygonSquare  )

◆ CPPUNIT_TEST() [5/24]

VolumeTest::CPPUNIT_TEST ( testC0PolyhedronCube  )

◆ CPPUNIT_TEST() [6/24]

VolumeTest::CPPUNIT_TEST ( testC0PolyhedronHexagonalPrism  )

◆ CPPUNIT_TEST() [7/24]

VolumeTest::CPPUNIT_TEST ( testEdge3Invertible  )

◆ CPPUNIT_TEST() [8/24]

VolumeTest::CPPUNIT_TEST ( testEdge3Volume  )

◆ CPPUNIT_TEST() [9/24]

VolumeTest::CPPUNIT_TEST ( testEdge4Invertible  )

◆ CPPUNIT_TEST() [10/24]

VolumeTest::CPPUNIT_TEST ( testHex20PLevelTrueCentroid  )

◆ CPPUNIT_TEST() [11/24]

VolumeTest::CPPUNIT_TEST ( testHex8TrueCentroid  )

◆ CPPUNIT_TEST() [12/24]

VolumeTest::CPPUNIT_TEST ( testPrism6TrueCentroid  )

◆ CPPUNIT_TEST() [13/24]

VolumeTest::CPPUNIT_TEST ( testPyramid5TrueCentroid  )

◆ CPPUNIT_TEST() [14/24]

VolumeTest::CPPUNIT_TEST ( testQuad4AspectRatio  )

◆ CPPUNIT_TEST() [15/24]

VolumeTest::CPPUNIT_TEST ( testQuad4Invertible  )

◆ CPPUNIT_TEST() [16/24]

VolumeTest::CPPUNIT_TEST ( testQuad4Jacobian  )

◆ CPPUNIT_TEST() [17/24]

VolumeTest::CPPUNIT_TEST ( testQuad4MinMaxAngle  )

◆ CPPUNIT_TEST() [18/24]

VolumeTest::CPPUNIT_TEST ( testQuad4TrueCentroid  )

◆ CPPUNIT_TEST() [19/24]

VolumeTest::CPPUNIT_TEST ( testQuad4Warpage  )

◆ CPPUNIT_TEST() [20/24]

VolumeTest::CPPUNIT_TEST ( testTet4DihedralAngle  )

◆ CPPUNIT_TEST() [21/24]

VolumeTest::CPPUNIT_TEST ( testTet4Jacobian  )

◆ CPPUNIT_TEST() [22/24]

VolumeTest::CPPUNIT_TEST ( testTri3AspectRatio  )

◆ CPPUNIT_TEST() [23/24]

VolumeTest::CPPUNIT_TEST ( testTri3TrueCentroid  )

◆ CPPUNIT_TEST() [24/24]

VolumeTest::CPPUNIT_TEST ( testTwistedVolume  )

◆ CPPUNIT_TEST_SUITE_END()

VolumeTest::CPPUNIT_TEST_SUITE_END ( )

◆ LIBMESH_CPPUNIT_TEST_SUITE()

VolumeTest::LIBMESH_CPPUNIT_TEST_SUITE ( VolumeTest  )

◆ setUp()

void VolumeTest::setUp ( )
inline

Definition at line 57 of file volume_test.C.

58 {
59 }

◆ tearDown()

void VolumeTest::tearDown ( )
inline

Definition at line 61 of file volume_test.C.

62 {
63 }

◆ test_elem_invertible()

bool VolumeTest::test_elem_invertible ( const std::vector< Point > &  pts,
ElemType  elem_type 
)
inlineprotected

Definition at line 1271 of file volume_test.C.

1273 {
1274 // Construct Elem of desired type
1275 auto [elem, nodes] = this->construct_elem(pts, elem_type);
1276 libmesh_ignore(nodes);
1277
1278 // Return whether or not this Elem has an invertible map
1279 return elem->has_invertible_map();
1280 }
std::pair< std::unique_ptr< Elem >, std::vector< std::unique_ptr< Node > > > construct_elem(const std::vector< Point > &pts, ElemType elem_type)
void libmesh_ignore(const Args &...)

References construct_elem(), and libMesh::libmesh_ignore().

Referenced by testEdge3Invertible(), testEdge4Invertible(), and testQuad4Invertible().

◆ test_true_centroid_and_volume()

void VolumeTest::test_true_centroid_and_volume ( ElemType  elem_type)
inlineprotected

Definition at line 1284 of file volume_test.C.

1285 {
1286 // Check that derived class true_centroid() gives same result as
1287 // the base class Elem::true_centroid() on a 2x2x2 mesh of 10%
1288 // distorted elements.
1289 {
1290 ReplicatedMesh mesh(*TestCommWorld);
1291
1293 /*nelem=*/2, /*nelem=*/2, /*nelem=*/2,
1294 /*xmin=*/-1, /*xmax=*/1,
1295 /*ymin=*/-1, /*ymax=*/1,
1296 /*zmin=*/-1, /*zmax=*/1,
1297 elem_type);
1298
1300 /*factor=*/0.1,
1301 /*perturb_boundary=*/false);
1302
1303 for (const auto & elem : mesh.element_ptr_range())
1304 {
1305 Point derived_centroid = elem->true_centroid();
1306 Point base_centroid = elem->Elem::true_centroid();
1307
1308 // Debugging: check results in detail
1309 // auto flags = libMesh::out.flags();
1310 // libMesh::out << std::scientific << std::setprecision(16);
1311 // libMesh::out << "derived_centroid = " << derived_centroid << std::endl;
1312 // libMesh::out << "base_centroid = " << base_centroid << std::endl;
1313 // libMesh::out.flags(flags);
1314
1315 CPPUNIT_ASSERT(derived_centroid.absolute_fuzzy_equals(base_centroid, 50*TOLERANCE*TOLERANCE));
1316
1317 // Make sure that base class and "optimized" routines for computing the cell volume agree
1318 Real derived_volume = elem->volume();
1319 Real base_volume = elem->Elem::volume();
1320 LIBMESH_ASSERT_FP_EQUAL(base_volume, derived_volume, 50*TOLERANCE*TOLERANCE);
1321 }
1322 }
1323 }
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
bool absolute_fuzzy_equals(const TypeVector< T > &rhs, Real tol=TOLERANCE) const
void build_cube(UnstructuredMesh &mesh, const unsigned int nx=0, const unsigned int ny=0, const unsigned int nz=0, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const Real zmin=0., const Real zmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
Builds a (elements) cube.
void distort(MeshBase &mesh, const Real factor, const bool perturb_boundary=false)
Randomly perturb the nodal locations.
static constexpr Real TOLERANCE
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

References libMesh::TypeVector< T >::absolute_fuzzy_equals(), libMesh::MeshTools::Generation::build_cube(), libMesh::MeshTools::Modification::distort(), mesh, libMesh::Real, TestCommWorld, and libMesh::TOLERANCE.

Referenced by testHex8TrueCentroid(), testPrism6TrueCentroid(), and testPyramid5TrueCentroid().

◆ testC0Polygon()

void VolumeTest::testC0Polygon ( const std::vector< Point > &  points,
Real  expected_volume 
)
inline

Definition at line 913 of file volume_test.C.

915 {
916 Mesh mesh(*TestCommWorld);
917
918 const auto np = points.size();
919 std::unique_ptr<Elem> polygon = std::make_unique<C0Polygon>(np);
920
921 for (auto p : make_range(np))
922 polygon->set_node(p, mesh.add_point(points[p], p));
923
924 polygon->set_id() = 0;
925 Elem * elem = mesh.add_elem(std::move(polygon));
926
927 const Real derived_volume = elem->volume();
928 const Real base_volume = elem->Elem::volume();
929 LIBMESH_ASSERT_FP_EQUAL(base_volume, derived_volume, TOLERANCE*TOLERANCE);
930 LIBMESH_ASSERT_FP_EQUAL(derived_volume, expected_volume, TOLERANCE*TOLERANCE);
931
932 this->testC0PolygonMethods(mesh, np);
933 }
void testC0PolygonMethods(MeshBase &mesh, unsigned int n_sides)
virtual Real volume() const
Definition elem.C:3462
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176

References libMesh::MeshBase::add_elem(), libMesh::MeshBase::add_point(), libMesh::make_range(), mesh, libMesh::Real, testC0PolygonMethods(), TestCommWorld, libMesh::TOLERANCE, and libMesh::Elem::volume().

Referenced by testC0PolygonHexagon(), testC0PolygonPentagon(), testC0PolygonQuad(), and testC0PolygonSquare().

◆ testC0PolygonHexagon()

void VolumeTest::testC0PolygonHexagon ( )
inline

Definition at line 955 of file volume_test.C.

956 {
957 LOG_UNIT_TEST;
958 testC0Polygon({{0,0}, {1,0}, {1.5,0.5}, {1,1}, {0,1}, {-0.5, 0.5}}, 1.5);
959 }
void testC0Polygon(const std::vector< Point > &points, Real expected_volume)

References testC0Polygon().

◆ testC0PolygonMethods()

void VolumeTest::testC0PolygonMethods ( MeshBase mesh,
unsigned int  n_sides 
)
inlineprotected

Definition at line 1012 of file volume_test.C.

1014 {
1015 Elem * elem = mesh.query_elem_ptr(0);
1016 bool found_elem = elem;
1017 mesh.comm().max(found_elem);
1018 CPPUNIT_ASSERT(found_elem);
1019
1020 if (!elem)
1021 return;
1022
1023 CPPUNIT_ASSERT_EQUAL(elem->type(), C0POLYGON);
1024 CPPUNIT_ASSERT_EQUAL(elem->n_nodes(), n_sides);
1025 CPPUNIT_ASSERT_EQUAL(elem->n_sub_elem(), n_sides);
1026 CPPUNIT_ASSERT_EQUAL(elem->n_sides(), n_sides);
1027 CPPUNIT_ASSERT_EQUAL(elem->n_vertices(), n_sides);
1028 CPPUNIT_ASSERT_EQUAL(elem->n_edges(), n_sides);
1029
1030 // Even number of sides
1031 if (!(n_sides%2))
1032 {
1033 const unsigned int nsover2 = n_sides/2;
1034 for (auto i : make_range(nsover2))
1035 {
1036 CPPUNIT_ASSERT_EQUAL(elem->opposite_side(i), i+nsover2);
1037 CPPUNIT_ASSERT_EQUAL(elem->opposite_side(i+nsover2), i);
1038 }
1039 }
1040
1041 for (unsigned int i : make_range(n_sides))
1042 {
1043 CPPUNIT_ASSERT(elem->is_vertex(i));
1044 CPPUNIT_ASSERT(!elem->is_edge(i));
1045 CPPUNIT_ASSERT(!elem->is_face(i));
1046 CPPUNIT_ASSERT(elem->is_node_on_side(i,i));
1047 CPPUNIT_ASSERT(elem->is_node_on_edge(i,i));
1048 CPPUNIT_ASSERT(elem->is_node_on_side((i+1)%n_sides,i));
1049 CPPUNIT_ASSERT(elem->is_node_on_edge((i+1)%n_sides,i));
1050 std::vector<unsigned int> nodes = elem->nodes_on_side(i);
1051 CPPUNIT_ASSERT_EQUAL(nodes.size(), std::size_t(2));
1052 CPPUNIT_ASSERT(nodes[0] == i ||
1053 nodes[1] == i);
1054 CPPUNIT_ASSERT(nodes[0] == (i+1)%n_sides ||
1055 nodes[1] == (i+1)%n_sides);
1056 std::vector<unsigned int> edge_nodes = elem->nodes_on_edge(i);
1057 CPPUNIT_ASSERT(nodes == edge_nodes);
1058
1059
1060 CPPUNIT_ASSERT_EQUAL(elem->local_side_node(i,0), i);
1061 CPPUNIT_ASSERT_EQUAL(elem->local_side_node(i,1), (i+1)%n_sides);
1062 CPPUNIT_ASSERT_EQUAL(elem->local_edge_node(i,0), i);
1063 CPPUNIT_ASSERT_EQUAL(elem->local_edge_node(i,1), (i+1)%n_sides);
1064
1065 auto edge = elem->side_ptr(i);
1066 CPPUNIT_ASSERT_EQUAL(edge->type(), EDGE2);
1067 CPPUNIT_ASSERT_EQUAL(edge->node_ptr(0), mesh.node_ptr(i));
1068 CPPUNIT_ASSERT_EQUAL(edge->node_ptr(1), mesh.node_ptr((i+1)%n_sides));
1069 }
1070
1071 CPPUNIT_ASSERT(!elem->is_flipped());
1072 elem->flip(&mesh.get_boundary_info());
1073 CPPUNIT_ASSERT(elem->is_flipped());
1074 elem->flip(&mesh.get_boundary_info());
1075 }
void max(const T &r, T &o, Request &req) const
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const =0
virtual unsigned int n_vertices() const =0
virtual unsigned int local_side_node(unsigned int side, unsigned int side_node) const =0
virtual std::unique_ptr< Elem > side_ptr(unsigned int i)=0
virtual unsigned int n_nodes() const =0
virtual bool is_flipped() const =0
virtual std::vector< unsigned int > nodes_on_side(const unsigned int) const =0
virtual bool is_face(const unsigned int i) const =0
virtual std::vector< unsigned int > nodes_on_edge(const unsigned int) const =0
virtual void flip(BoundaryInfo *boundary_info)=0
Flips the element (by swapping node and neighbor pointers) to have a mapping Jacobian of opposite sig...
virtual unsigned int local_edge_node(unsigned int edge, unsigned int edge_node) const =0
Similar to Elem::local_side_node(), but instead of a side id, takes an edge id and a node id on that ...
virtual unsigned int n_sub_elem() const =0
virtual bool is_edge(const unsigned int i) const =0
virtual unsigned int opposite_side(const unsigned int s) const
Definition elem.C:3556
virtual bool is_vertex(const unsigned int i) const =0
virtual ElemType type() const =0
virtual bool is_node_on_edge(const unsigned int n, const unsigned int e) const =0
virtual unsigned int n_edges() const =0
virtual unsigned int n_sides() const =0
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual const Node * node_ptr(const dof_id_type i) const =0
virtual const Elem * query_elem_ptr(const dof_id_type i) const =0
const Parallel::Communicator & comm() const

References libMesh::C0POLYGON, libMesh::ParallelObject::comm(), libMesh::EDGE2, libMesh::Elem::flip(), libMesh::MeshBase::get_boundary_info(), libMesh::Elem::is_edge(), libMesh::Elem::is_face(), libMesh::Elem::is_flipped(), libMesh::Elem::is_node_on_edge(), libMesh::Elem::is_node_on_side(), libMesh::Elem::is_vertex(), libMesh::Elem::local_edge_node(), libMesh::Elem::local_side_node(), libMesh::make_range(), libMesh::Parallel::Communicator::max(), mesh, libMesh::Elem::n_edges(), libMesh::Elem::n_nodes(), libMesh::Elem::n_sides(), libMesh::Elem::n_sub_elem(), libMesh::Elem::n_vertices(), libMesh::MeshBase::node_ptr(), libMesh::Elem::nodes_on_edge(), libMesh::Elem::nodes_on_side(), libMesh::Elem::opposite_side(), libMesh::MeshBase::query_elem_ptr(), libMesh::Elem::side_ptr(), and libMesh::Elem::type().

Referenced by testC0Polygon().

◆ testC0PolygonPentagon()

void VolumeTest::testC0PolygonPentagon ( )
inline

Definition at line 949 of file volume_test.C.

950 {
951 LOG_UNIT_TEST;
952 testC0Polygon({{0,0}, {1,0}, {1.5,0.5}, {1,1}, {0,1}}, 1.25);
953 }

References testC0Polygon().

◆ testC0PolygonQuad()

void VolumeTest::testC0PolygonQuad ( )
inline

Definition at line 943 of file volume_test.C.

944 {
945 LOG_UNIT_TEST;
946 testC0Polygon({{0,0}, {1,0}, {1,2}, {-1,1}}, 2.5);
947 }

References testC0Polygon().

◆ testC0PolygonSquare()

void VolumeTest::testC0PolygonSquare ( )
inline

Definition at line 937 of file volume_test.C.

938 {
939 LOG_UNIT_TEST;
940 testC0Polygon({{0,0}, {1,0}, {1,1}, {0,1}}, 1);
941 }

References testC0Polygon().

◆ testC0PolyhedronCube()

void VolumeTest::testC0PolyhedronCube ( )
inlineprotected

Definition at line 1161 of file volume_test.C.

1162 {
1163 ReplicatedMesh mesh(*TestCommWorld);
1164
1165 mesh.add_point(Point(0, 0, 0), 0);
1166 mesh.add_point(Point(1, 0, 0), 1);
1167 mesh.add_point(Point(1, 1, 0), 2);
1168 mesh.add_point(Point(0, 1, 0), 3);
1169 mesh.add_point(Point(0, 0, 1), 4);
1170 mesh.add_point(Point(1, 0, 1), 5);
1171 mesh.add_point(Point(1, 1, 1), 6);
1172 mesh.add_point(Point(0, 1, 1), 7);
1173
1174 // See notes in elem_test.h
1175 const std::vector<std::vector<unsigned int>> nodes_on_side =
1176 { {0, 1, 2, 3}, // min z
1177 {0, 1, 5, 4}, // min y
1178 {2, 6, 5, 1}, // max x
1179 {2, 3, 7, 6}, // max y
1180 {0, 4, 7, 3}, // min x
1181 {5, 6, 7, 4} }; // max z
1182
1183 // Build all the sides.
1184 std::vector<std::shared_ptr<Polygon>> sides(nodes_on_side.size());
1185
1186 for (auto s : index_range(nodes_on_side))
1187 {
1188 const auto & nodes_on_s = nodes_on_side[s];
1189 sides[s] = std::make_shared<C0Polygon>(nodes_on_s.size());
1190 for (auto i : index_range(nodes_on_s))
1191 sides[s]->set_node(i, mesh.node_ptr(nodes_on_s[i]));
1192 }
1193
1194 const auto poly = dynamic_cast<C0Polyhedron *>(buildC0Polyhedron(sides, mesh));
1195 testElemVolume(poly, 1);
1196 #ifdef LIBMESH_ENABLE_EXCEPTIONS
1197 testC0PolyhedronMethods(mesh, /*midnode*/false);
1198
1199 // Check routine for subtet side to poly side mapping
1200 // NOTE: this test is hard coded to the elements used right now (to be reworked)
1201 const auto subtet0_sides_to_poly_sides = poly->subelement_sides_to_poly_sides(0);
1202 CPPUNIT_ASSERT_EQUAL(libMesh::invalid_int, subtet0_sides_to_poly_sides[0]);
1203 CPPUNIT_ASSERT_EQUAL(1, subtet0_sides_to_poly_sides[1]);
1204 CPPUNIT_ASSERT_EQUAL(0, subtet0_sides_to_poly_sides[2]);
1205 CPPUNIT_ASSERT_EQUAL(2, subtet0_sides_to_poly_sides[3]);
1206 #else
1207 testC0PolyhedronMethods(mesh, /*midnode*/true);
1208 #endif
1209 }
void testElemVolume(const Elem *elem, Real expected_volume)
Elem * buildC0Polyhedron(std::vector< std::shared_ptr< Polygon > > sides, MeshBase &mesh)
void testC0PolyhedronMethods(MeshBase &mesh, bool has_midnode)
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
const int invalid_int
A number which is used quite often to represent an invalid or uninitialized value for an integer.
Definition libmesh.h:309

References libMesh::MeshBase::add_point(), buildC0Polyhedron(), libMesh::index_range(), libMesh::invalid_int, mesh, libMesh::MeshBase::node_ptr(), testC0PolyhedronMethods(), TestCommWorld, and testElemVolume().

◆ testC0PolyhedronHexagonalPrism()

void VolumeTest::testC0PolyhedronHexagonalPrism ( )
inlineprotected

Definition at line 1213 of file volume_test.C.

1214 {
1215 ReplicatedMesh mesh(*TestCommWorld);
1216
1217 mesh.add_point(Point(0, -2, 0), 0);
1218 mesh.add_point(Point(-1, -1, 0), 1);
1219 mesh.add_point(Point(-1, 1, 0), 2);
1220 mesh.add_point(Point(0, 2, 0), 3);
1221 mesh.add_point(Point(1, 1, 0), 4);
1222 mesh.add_point(Point(1, -1, 0), 5);
1223 mesh.add_point(Point(0, -2, 1), 6);
1224 mesh.add_point(Point(-1, -1, 1), 7);
1225 mesh.add_point(Point(-1, 1, 1), 8);
1226 mesh.add_point(Point(0, 2, 1), 9);
1227 mesh.add_point(Point(1, 1, 1), 10);
1228 mesh.add_point(Point(1, -1, 1), 11);
1229
1230 // See notes in elem_test.h
1231 // With this ordering, we'll need to use a mid-node to tetrahedralize it
1232 const std::vector<std::vector<unsigned int>> nodes_on_side =
1233 { {0, 1, 2, 3, 4, 5},
1234 {0, 1, 7, 6},
1235 {1, 2, 8, 7},
1236 {2, 3, 9, 8},
1237 {3, 4, 10, 9},
1238 {4, 5, 11, 10},
1239 {5, 0, 6, 11},
1240 {6, 7, 8, 9, 10, 11} };
1241
1242 // Build all the sides.
1243 std::vector<std::shared_ptr<Polygon>> sides(nodes_on_side.size());
1244
1245 for (auto s : index_range(nodes_on_side))
1246 {
1247 const auto & nodes_on_s = nodes_on_side[s];
1248 sides[s] = std::make_shared<C0Polygon>(nodes_on_s.size());
1249 for (auto i : index_range(nodes_on_s))
1250 sides[s]->set_node(i, mesh.node_ptr(nodes_on_s[i]));
1251 }
1252
1253 const auto poly = dynamic_cast<C0Polyhedron *>(buildC0Polyhedron(sides, mesh));
1254 CPPUNIT_ASSERT_EQUAL((unsigned int)13, poly->n_nodes()); // we should have a mid node
1255 testElemVolume(poly, 6);
1256 testC0PolyhedronMethods(mesh, /*midnode*/true);
1257
1258 // Check routine for subtet side to poly side mapping
1259 const auto subtet0_sides_to_poly_sides = poly->subelement_sides_to_poly_sides(0);
1260 CPPUNIT_ASSERT_EQUAL(0, subtet0_sides_to_poly_sides[0]);
1261 CPPUNIT_ASSERT_EQUAL(libMesh::invalid_int, subtet0_sides_to_poly_sides[1]);
1262 CPPUNIT_ASSERT_EQUAL(libMesh::invalid_int, subtet0_sides_to_poly_sides[2]);
1263 CPPUNIT_ASSERT_EQUAL(libMesh::invalid_int, subtet0_sides_to_poly_sides[3]);
1264 }

References libMesh::MeshBase::add_point(), buildC0Polyhedron(), libMesh::index_range(), libMesh::invalid_int, mesh, libMesh::MeshBase::node_ptr(), testC0PolyhedronMethods(), TestCommWorld, and testElemVolume().

◆ testC0PolyhedronMethods()

void VolumeTest::testC0PolyhedronMethods ( MeshBase mesh,
bool  has_midnode 
)
inlineprotected

Definition at line 1079 of file volume_test.C.

1080 {
1081 Elem * elem = mesh.query_elem_ptr(0);
1082 bool found_elem = elem;
1083 mesh.comm().max(found_elem);
1084 CPPUNIT_ASSERT(found_elem);
1085
1086 if (!elem)
1087 return;
1088
1089 CPPUNIT_ASSERT_EQUAL(elem->type(), C0POLYHEDRON);
1090
1091 const int V = elem->n_vertices();
1092 const int E = elem->n_edges();
1093 const int F = elem->n_faces();
1094
1095 if (!has_midnode)
1096 CPPUNIT_ASSERT_EQUAL(int(elem->n_nodes()), V);
1097 else
1098 CPPUNIT_ASSERT_EQUAL(int(elem->n_nodes()), V + 1);
1099 CPPUNIT_ASSERT_EQUAL(int(elem->n_sides()), F);
1100
1101 // Euler characteristic for polygons homeomorphic to spheres is 2
1102 CPPUNIT_ASSERT_EQUAL(V-E+F, 2);
1103
1104 int FE = 0;
1105 int FV = 0;
1106
1107 std::vector<int> sides_on_vertex(V, 0);
1108 for (auto s : make_range(F))
1109 {
1110 auto side = elem->build_side_ptr(s);
1111 CPPUNIT_ASSERT_EQUAL(side->type(), C0POLYGON);
1112 FE += side->n_edges();
1113 const int SV = side->n_vertices();
1114 FV += SV;
1115
1116 auto nos = elem->nodes_on_side(s);
1117 CPPUNIT_ASSERT_EQUAL(nos.size(), std::size_t(SV));
1118
1119 for (auto v : make_range(SV))
1120 {
1121 Node * sidenode = side->node_ptr(v);
1122 const unsigned int n = elem->get_node_index(sidenode);
1123 CPPUNIT_ASSERT(n != invalid_uint);
1124 ++sides_on_vertex[n];
1125
1126 // We're just looking at first order so far
1127 CPPUNIT_ASSERT(side->is_vertex(v));
1128 CPPUNIT_ASSERT(!side->is_edge(v));
1129 CPPUNIT_ASSERT(elem->is_vertex(n));
1130 CPPUNIT_ASSERT(!elem->is_edge(n));
1131 CPPUNIT_ASSERT(!elem->is_face(n));
1132 CPPUNIT_ASSERT(elem->is_node_on_side(n, s));
1133 CPPUNIT_ASSERT(std::find(nos.begin(), nos.end(), n) != nos.end());
1134 }
1135 }
1136
1137 // We hit every edge from 2 faces
1138 CPPUNIT_ASSERT_EQUAL(E*2, FE);
1139
1140 // We hit every vertex from at least 3 faces
1141 CPPUNIT_ASSERT_LESSEQUAL(FV, V*3); // V*3 <= FV
1142 for (auto sov : sides_on_vertex)
1143 CPPUNIT_ASSERT_LESSEQUAL(sov, 3); // 3 <= sov
1144
1145 CPPUNIT_ASSERT(!elem->is_flipped());
1146 }
virtual unsigned int n_faces() const =0
unsigned int get_node_index(const Node *node_ptr) const
Definition elem.h:2554
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i)=0
A specific instantiation of the FEBase class.
Definition fe.h:128
A Node is like a Point, but with more information.
Definition node.h:55
const unsigned int invalid_uint
A number which is used quite often to represent an invalid or uninitialized value for an unsigned int...
Definition libmesh.h:303

References libMesh::Elem::build_side_ptr(), libMesh::C0POLYGON, libMesh::C0POLYHEDRON, libMesh::ParallelObject::comm(), libMesh::Elem::get_node_index(), libMesh::invalid_uint, libMesh::Elem::is_edge(), libMesh::Elem::is_face(), libMesh::Elem::is_flipped(), libMesh::Elem::is_node_on_side(), libMesh::Elem::is_vertex(), libMesh::make_range(), libMesh::Parallel::Communicator::max(), mesh, libMesh::Elem::n_edges(), libMesh::Elem::n_faces(), libMesh::Elem::n_nodes(), libMesh::Elem::n_sides(), libMesh::Elem::n_vertices(), libMesh::Elem::nodes_on_side(), libMesh::MeshBase::query_elem_ptr(), and libMesh::Elem::type().

Referenced by testC0PolyhedronCube(), and testC0PolyhedronHexagonalPrism().

◆ testEdge3Invertible()

void VolumeTest::testEdge3Invertible ( )
inline

Definition at line 245 of file volume_test.C.

246 {
247 LOG_UNIT_TEST;
248
249 // 1.) This is the original test which started the investigation
250 // of determining invertibility. In this test, the actual
251 // midpoint of nodes 0 and 1 is 0.5*(1.100328e2 + 1.176528e2) =
252 // 113.8428, so we can see that the middle node is closer to the
253 // left endpoint. In this case, it is too close and the element is
254 // not invertible.
255 bool invertible = test_elem_invertible({
256 Point(-3.566160e1, -6.690970e-1, 1.100328e2),
257 Point(-3.566160e1, -6.690970e-1, 1.176528e2),
258 Point(-3.566160e1, -6.690970e-1, 1.115568e2)}, EDGE3);
259 CPPUNIT_ASSERT(!invertible);
260
261 // 2.) Just like case 1, but now node 2 is at the midpoint, so
262 // this case is invertible.
263 invertible = test_elem_invertible({
264 Point(-3.566160e1, -6.690970e-1, 1.100328e2),
265 Point(-3.566160e1, -6.690970e-1, 1.176528e2),
266 Point(-3.566160e1, -6.690970e-1, 113.8428)}, EDGE3);
267 CPPUNIT_ASSERT(invertible);
268
269 // 3.) Non-collinear case where the mid-edge node is "above" and "way
270 // past" the right endpoint. This case is not invertible
271 invertible = test_elem_invertible({Point(0, 0, 0), Point(1, 0, 0), Point(3.5, 1.5, 0)}, EDGE3);
272 CPPUNIT_ASSERT(!invertible);
273 }
bool test_elem_invertible(const std::vector< Point > &pts, ElemType elem_type)

References libMesh::EDGE3, and test_elem_invertible().

◆ testEdge3Volume()

void VolumeTest::testEdge3Volume ( )
inline

Definition at line 201 of file volume_test.C.

202 {
203 LOG_UNIT_TEST;
204
205 Mesh mesh(*TestCommWorld);
206 MeshTools::Generation::build_line (mesh, /*nelem=*/1, 0., 1., EDGE3);
207 CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(3), mesh.n_nodes());
208
209 auto edge3 = mesh.query_elem_ptr(0);
210 if (!edge3) // We may be on a distributed mesh
211 return;
212
213 // Check unperturbed, straight edge case
214 LIBMESH_ASSERT_FP_EQUAL(1.0, edge3->volume(), TOLERANCE*TOLERANCE);
215
216 // Get references to the individual Edge3 nodes
217 auto & middle_node = edge3->node_ref(2);
218 auto & right_node = edge3->node_ref(1);
219
220 // Check middle node perturbed in +x direction case. This should
221 // not change the volume because it's still a straight line
222 // element.
223 middle_node = Point(0.5 + 1.e-3, 0., 0.);
224 LIBMESH_ASSERT_FP_EQUAL(1.0, edge3->volume(), TOLERANCE*TOLERANCE);
225
226 // Check middle node perturbed in -x direction case. This should
227 // not change the volume because it's still a straight line
228 // element.
229 middle_node = Point(0.5 - 1.e-3, 0., 0.);
230 LIBMESH_ASSERT_FP_EQUAL(1.0, edge3->volume(), TOLERANCE*TOLERANCE);
231
232 // Check volume of actual curved element against pre-computed value.
233 middle_node = Point(0.5, 0.25, 0.);
234 right_node = Point(1., 1., 0.);
235 LIBMESH_ASSERT_FP_EQUAL(1.4789428575446, edge3->volume(), TOLERANCE);
236
237 // Compare with volume computed by base class Elem::volume() call
238 // which uses quadrature. We don't expect this to have full
239 // floating point accuracy.
240 middle_node = Point(0.5, 0.1, 0.);
241 right_node = Point(1., 0., 0.);
242 LIBMESH_ASSERT_FP_EQUAL(edge3->Elem::volume(), edge3->volume(), 1e-4);
243 }
virtual dof_id_type n_nodes() const =0
void build_line(UnstructuredMesh &mesh, const unsigned int nx, const Real xmin=0., const Real xmax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 1D meshes.
uint8_t dof_id_type
Definition id_types.h:67

References libMesh::MeshTools::Generation::build_line(), libMesh::EDGE3, mesh, libMesh::MeshBase::n_nodes(), libMesh::MeshBase::query_elem_ptr(), TestCommWorld, and libMesh::TOLERANCE.

◆ testEdge4Invertible()

void VolumeTest::testEdge4Invertible ( )
inline

Definition at line 275 of file volume_test.C.

276 {
277 LOG_UNIT_TEST;
278
279 // Reference Elem should be invertible
280 {
281 const Elem & edge4 = ReferenceElem::get(EDGE4);
282 CPPUNIT_ASSERT(edge4.has_invertible_map());
283 }
284
285 // If node 2 goes to the left past -5/9 = -.555, the element becomes non-invertible
286 {
287 // x2 > -5/9, the map is still invertible
288 bool invertible =
289 test_elem_invertible({Point(-1, 0, 0), Point(1, 0, 0), Point(-0.5, 0, 0), Point(Real(1)/3, 0, 0)},
290 EDGE4);
291 CPPUNIT_ASSERT(invertible);
292
293 // x2 < -5/9, it is too close to x0 now
294 invertible =
295 test_elem_invertible({Point(-1, 0, 0), Point(1, 0, 0), Point(-0.57, 0, 0), Point(Real(1)/3, 0, 0)},
296 EDGE4);
297 CPPUNIT_ASSERT(!invertible);
298 }
299
300 // If node 2 goes to the right past 5/21 ~ 0.2381, the element becomes non-invertible
301 {
302 // x2 < 5/21, the map should still be invertible
303 bool invertible =
304 test_elem_invertible({Point(-1, 0, 0), Point(1, 0, 0), Point(Real(3)/21, 0, 0), Point(Real(1)/3, 0, 0)},
305 EDGE4);
306 CPPUNIT_ASSERT(invertible);
307
308 // x2 > 5/21, x2 is too close to x3 now
309 invertible =
310 test_elem_invertible({Point(-1, 0, 0), Point(1, 0, 0), Point(Real(6)/21, 0, 0), Point(Real(1)/3, 0, 0)},
311 EDGE4);
312 CPPUNIT_ASSERT(!invertible);
313 }
314 }
virtual bool has_invertible_map(Real tol=TOLERANCE *TOLERANCE) const
Definition elem.C:2914
const Elem & get(const ElemType type_in)

References libMesh::EDGE4, libMesh::ReferenceElem::get(), libMesh::Elem::has_invertible_map(), libMesh::Real, and test_elem_invertible().

◆ testElemVolume()

void VolumeTest::testElemVolume ( const Elem elem,
Real  expected_volume 
)
inlineprotected

Definition at line 1150 of file volume_test.C.

1152 {
1153 const Real derived_volume = elem->volume();
1154 const Real base_volume = elem->Elem::volume();
1155 LIBMESH_ASSERT_FP_EQUAL(base_volume, derived_volume, TOLERANCE*TOLERANCE);
1156 LIBMESH_ASSERT_FP_EQUAL(derived_volume, expected_volume, TOLERANCE*TOLERANCE);
1157 }

References libMesh::Real, libMesh::TOLERANCE, and libMesh::Elem::volume().

Referenced by testC0PolyhedronCube(), and testC0PolyhedronHexagonalPrism().

◆ testHex20PLevelTrueCentroid()

void VolumeTest::testHex20PLevelTrueCentroid ( )
inline

Definition at line 150 of file volume_test.C.

151 {
152 LOG_UNIT_TEST;
153
154 // Test that Elem base class true_centroid() implementation works
155 // for an elevated p_level HEX20
156 {
157#ifdef LIBMESH_ENABLE_AMR
158 ReplicatedMesh mesh(*TestCommWorld);
160 /*nelem=*/1, /*nelem=*/1, /*nelem=*/1,
161 /*xmin=*/-1, /*xmax=*/1,
162 /*ymin=*/-1, /*ymax=*/1,
163 /*zmin=*/-1, /*zmax=*/1,
164 HEX20);
165 Elem * hex20 = mesh.elem_ptr(0);
166 hex20->set_p_level(1);
167 Point true_centroid = hex20->true_centroid();
168 LIBMESH_ASSERT_FP_EQUAL(0, true_centroid(0), TOLERANCE*TOLERANCE);
169 LIBMESH_ASSERT_FP_EQUAL(0, true_centroid(1), TOLERANCE*TOLERANCE);
170 LIBMESH_ASSERT_FP_EQUAL(0, true_centroid(2), TOLERANCE*TOLERANCE);
171#endif // LIBMESH_ENABLE_AMR
172 }
173 }
virtual Point true_centroid() const
Definition elem.C:575
void set_p_level(const unsigned int p)
Sets the value of the p-refinement level for the element.
virtual const Elem * elem_ptr(const dof_id_type i) const =0

References libMesh::MeshTools::Generation::build_cube(), libMesh::MeshBase::elem_ptr(), libMesh::HEX20, mesh, libMesh::Elem::set_p_level(), TestCommWorld, libMesh::TOLERANCE, and libMesh::Elem::true_centroid().

◆ testHex8TrueCentroid()

void VolumeTest::testHex8TrueCentroid ( )
inline

Definition at line 147 of file volume_test.C.

147{ LOG_UNIT_TEST; test_true_centroid_and_volume(HEX8); }
void test_true_centroid_and_volume(ElemType elem_type)

References libMesh::HEX8, and test_true_centroid_and_volume().

◆ testPrism6TrueCentroid()

void VolumeTest::testPrism6TrueCentroid ( )
inline

Definition at line 148 of file volume_test.C.

148{ LOG_UNIT_TEST; test_true_centroid_and_volume(PRISM6); }

References libMesh::PRISM6, and test_true_centroid_and_volume().

◆ testPyramid5TrueCentroid()

void VolumeTest::testPyramid5TrueCentroid ( )
inline

Definition at line 130 of file volume_test.C.

131 {
132 LOG_UNIT_TEST;
133
134 // Test Pyramid5::true_centroid() gives the correct result for a reference element
135 {
136 const Elem & pyr5 = ReferenceElem::get(PYRAMID5);
137 Point true_centroid = pyr5.true_centroid();
138 LIBMESH_ASSERT_FP_EQUAL(0, true_centroid(0), TOLERANCE*TOLERANCE);
139 LIBMESH_ASSERT_FP_EQUAL(0, true_centroid(1), TOLERANCE*TOLERANCE);
140 LIBMESH_ASSERT_FP_EQUAL(0.25, true_centroid(2), TOLERANCE*TOLERANCE);
141 }
142
143 // Currently there is not an optimized Pyramid5::true_centroid() to compare against
145 }

References libMesh::ReferenceElem::get(), libMesh::PYRAMID5, test_true_centroid_and_volume(), libMesh::TOLERANCE, and libMesh::Elem::true_centroid().

◆ testQuad4AspectRatio()

void VolumeTest::testQuad4AspectRatio ( )
inline

Definition at line 407 of file volume_test.C.

408 {
409 LOG_UNIT_TEST;
410
411 // Case 1: Test that rigid body rotations of a unit square
412 // quadrilateral that have no effect on the quality of the
413 // element.
414 {
415 // Construct unit square QUAD4
416 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(1, 1, 0), Point(0, 1, 0)};
417 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
418 libmesh_ignore(nodes);
419
420 // 1a) Unit square aspect ratio should be == 1
421 Real aspect_ratio = elem->quality(ASPECT_RATIO);
422 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0, /*actual=*/aspect_ratio, TOLERANCE);
423
424 // 1b) Rotate all points about x-axis by 90 degrees
425 Real cost = std::cos(.5*libMesh::pi);
426 Real sint = std::sin(.5*libMesh::pi);
427 RealTensorValue Rx(1, 0, 0,
428 0, cost, sint,
429 0, sint, cost);
430
431 for (auto & pt : pts)
432 pt = Rx * pt;
433
434 aspect_ratio = elem->quality(ASPECT_RATIO);
435 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0, /*actual=*/aspect_ratio, TOLERANCE);
436
437 // 1c) Rotate all points about z-axis by 90 degrees
438 RealTensorValue Rz(cost, -sint, 0,
439 sint, cost, 0,
440 0, 0, 1);
441
442 for (auto & pt : pts)
443 pt = Rz * pt;
444
445 aspect_ratio = elem->quality(ASPECT_RATIO);
446 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0, /*actual=*/aspect_ratio, TOLERANCE);
447
448 // 1d) Rotate all points about y-axis by 270 degrees
449 RealTensorValue Ry(cost, 0, sint,
450 0, 1, 0,
451 -sint, 0, cost);
452
453 for (int cnt=0; cnt<3; ++cnt)
454 for (auto & pt : pts)
455 pt = Ry * pt;
456
457 aspect_ratio = elem->quality(ASPECT_RATIO);
458 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0, /*actual=*/aspect_ratio, TOLERANCE);
459 }
460
461 // Case 2: Rhombus QUAD4. This case should have an aspect ratio of
462 // 1/sin(theta), where theta is the acute interior angle of the
463 // rhombus.
464 {
465 // Helper lambda function that constructs a rhombus quad with
466 // interior acute angle theta.
467 auto test_rhombus_quad = [this](Real theta)
468 {
469 Real ct = std::cos(theta);
470 Real st = std::sin(theta);
471 std::vector<Point> pts = {
472 Point(0, 0, 0),
473 Point(1, 0, 0),
474 Point(1. + ct, st, 0),
475 Point( ct, st, 0)};
476 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
477 libmesh_ignore(nodes);
478
479 // The expected aspect ratio for the rhombus is 1/sin(theta)
480 Real aspect_ratio = elem->quality(ASPECT_RATIO);
481 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0/st, /*actual=*/aspect_ratio, TOLERANCE);
482 };
483
484 // 2a) Rhombus with interior angle theta=pi/6. The expected
485 // aspect ratio in this case is 1/std::sin(pi/6) = 2
486 test_rhombus_quad(libMesh::pi / 6);
487
488 // 2b) Rhombus with interior angle theta=pi/3. The expected
489 // aspect ratio in this case is 1/std::sin(pi/3) = 2/sqrt(3) = 1.155
490 test_rhombus_quad(libMesh::pi / 3);
491 }
492
493 // Case 3) Rectangle QUAD4. The "old" and "new" aspect ratio metrics
494 // return the same result for this case, whih is simply the ratio of
495 // the longest to shortest side.
496 {
497 auto test_rectangle_quad = [this](Real a)
498 {
499 std::vector<Point> pts = {Point(0, 0, 0), Point(a, 0, 0), Point(a, 1, 0), Point(0, 1, 0)};
500 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
501 libmesh_ignore(nodes);
502
503 // The expected aspect ratio for the rectangle is "a"
504 Real aspect_ratio = elem->quality(ASPECT_RATIO);
505 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/a, /*actual=*/aspect_ratio, TOLERANCE);
506 };
507
508 // 3a) Test case twice as long as it is tall
509 test_rectangle_quad(2.0);
510
511 // 3b) Test no funny numerical stuff with extremely stretched case
512 test_rectangle_quad(1e6);
513 }
514
515 // Case 4) Degenerate QUAD with zero length side. In the "old"
516 // aspect ratio metric we'd just get zero (as a stand-in for
517 // infinity) for this case since the minimum side length is zero,
518 // but using the new metric we get a non-zero value of 2.5. Thus,
519 // in the new metric it is possible to compare the aspect ratios
520 // of different degenerate QUAD elements rather than simply
521 // assigning all such elements a quality value of 0. Degenerate
522 // quadrilaterals are sometimes used as a "hack" to avoid having a
523 // separate subdomain of TRIs, and (surprisingly) many finite
524 // element operations just "work" on such elements.
525 {
526 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(1, 0, 0), Point(0, 1, 0)};
527 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
528 libmesh_ignore(nodes);
529
530 Real aspect_ratio = elem->quality(ASPECT_RATIO);
531 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/2.5, /*actual=*/aspect_ratio, TOLERANCE);
532 }
533
534 // Case 5) Trapezoid QUAD
535 {
536 auto test_trapezoid_quad = [this](Real a)
537 {
538 // 0 <= a <= 1/2
539 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(1-a, 1, 0), Point(a, 1, 0)};
540 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
541 libmesh_ignore(nodes);
542
543 Real aspect_ratio = elem->quality(ASPECT_RATIO);
544 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1./(1. - a), /*actual=*/aspect_ratio, TOLERANCE);
545 };
546
547 // 3a) Test "simple" trapezoid with expected aspect ratio 1.5
548 test_trapezoid_quad(1./3);
549
550 // 3b) Test "degenerate" trapezoid with one zero length base
551 test_trapezoid_quad(0.5);
552 }
553 }
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
const Real pi
.
Definition libmesh.h:292

References libMesh::ASPECT_RATIO, construct_elem(), libMesh::libmesh_ignore(), libMesh::pi, libMesh::QUAD4, libMesh::Real, and libMesh::TOLERANCE.

◆ testQuad4Invertible()

void VolumeTest::testQuad4Invertible ( )
inline

Definition at line 316 of file volume_test.C.

317 {
318 LOG_UNIT_TEST;
319
320 // Case 1: Test that rigid body rotations have no effect on the
321 // invertibility of the reference element
322 {
323 // 1a) The reference element rotated into various different different planes.
324 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(1, 1, 0), Point(0, 1, 0)};
325 bool invertible = test_elem_invertible(pts, QUAD4);
326 CPPUNIT_ASSERT(invertible);
327
328 // 1b) Rotate all points about x-axis by 90 degrees
329 Real cost = std::cos(.5*libMesh::pi);
330 Real sint = std::sin(.5*libMesh::pi);
331 RealTensorValue Rx(1, 0, 0,
332 0, cost, sint,
333 0, sint, cost);
334
335 for (auto & pt : pts)
336 pt = Rx * pt;
337
338 invertible = test_elem_invertible(pts, QUAD4);
339 CPPUNIT_ASSERT(invertible);
340
341 // 1c) Rotate all points about z-axis by 90 degrees
342 RealTensorValue Rz(cost, -sint, 0,
343 sint, cost, 0,
344 0, 0, 1);
345
346 for (auto & pt : pts)
347 pt = Rz * pt;
348
349 invertible = test_elem_invertible(pts, QUAD4);
350 CPPUNIT_ASSERT(invertible);
351
352 // 1d) Rotate all points about y-axis by 270 degrees
353 RealTensorValue Ry(cost, 0, sint,
354 0, 1, 0,
355 -sint, 0, cost);
356
357 for (int cnt=0; cnt<3; ++cnt)
358 for (auto & pt : pts)
359 pt = Ry * pt;
360
361 invertible = test_elem_invertible(pts, QUAD4);
362 CPPUNIT_ASSERT(invertible);
363 }
364
365 // Case 2: Planar quad with top right vertex displaced to the position
366 // (alpha, alpha). Some different cases are described below.
367 // .) alpha==1: affine case, always invertible
368 // .) 1/2 < alpha < 1: planar case, invertible
369 // .) alpha<=1/2: planar case but node is now at center of the
370 // element, should give a zero/negative Jacobian on the displaced
371 // Node -> not invertible.
372 {
373 const Real alpha = .5;
374
375 bool invertible =
376 test_elem_invertible({Point(0, 0, 0), Point(1, 0, 0), Point(alpha, alpha, 0), Point(0, 1, 0)}, QUAD4);
377
378 CPPUNIT_ASSERT(!invertible);
379 }
380
381 // Case 3) Top right corner is moved to (alpha, 1, 0). Element
382 // becomes non-invertible when alpha < 0.
383 {
384 const Real alpha = -0.25;
385
386 bool invertible =
387 test_elem_invertible({Point(0, 0, 0), Point(1, 0, 0), Point(alpha, 1, 0), Point(0, 1, 0)}, QUAD4);
388
389 CPPUNIT_ASSERT(!invertible);
390 }
391
392 // Case 4) Degenerate case - all 4 points at same location. This
393 // zero-volume element does not have an invertible map.
394 {
395 const Real alpha = std::log(2);
396
397 bool invertible =
398 test_elem_invertible({Point(alpha, alpha, alpha),
399 Point(alpha, alpha, alpha),
400 Point(alpha, alpha, alpha),
401 Point(alpha, alpha, alpha)}, QUAD4);
402
403 CPPUNIT_ASSERT(!invertible);
404 }
405 }

References libMesh::pi, libMesh::QUAD4, libMesh::Real, and test_elem_invertible().

◆ testQuad4Jacobian()

void VolumeTest::testQuad4Jacobian ( )
inline

Definition at line 783 of file volume_test.C.

784 {
785 LOG_UNIT_TEST;
786
787 // Rhombus QUAD4. This case should have a JACOBIAN and
788 // SCALED_JACOBIAN value of sin(theta), where "theta" is the
789 // specified amount that we "sheared" the element by on creation.
790 // The JACOBIAN and SCALED_JACOBIAN are the same for this element
791 // because the edge lengths are all = 1.
792 {
793 // Helper lambda function that constructs a rhombus quad with
794 // interior acute angle theta.
795 auto test_rhombus_quad = [this](Real theta)
796 {
797 Real ct = std::cos(theta);
798 Real st = std::sin(theta);
799 std::vector<Point> pts = {
800 Point(0, 0, 0),
801 Point(1, 0, 0),
802 Point(1. + ct, st, 0),
803 Point( ct, st, 0)};
804 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
805 libmesh_ignore(nodes);
806
807 Real jac = elem->quality(JACOBIAN);
808 Real scaled_jac = elem->quality(SCALED_JACOBIAN);
809
810 // Debugging
811 // libMesh::out << "jac = " << jac << std::endl;
812 // libMesh::out << "scaled_jac = " << scaled_jac << std::endl;
813
814 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/std::abs(std::sin(theta)), /*actual=*/jac, TOLERANCE);
815 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/std::abs(std::sin(theta)), /*actual=*/scaled_jac, TOLERANCE);
816 };
817
818 // 2a) Rhombus with interior angle theta=pi/6.
819 test_rhombus_quad(libMesh::pi / 6);
820
821 // 2b) Rhombus with interior angle theta=pi/3.
822 test_rhombus_quad(libMesh::pi / 3);
823 }
824 }

References construct_elem(), libMesh::JACOBIAN, libMesh::libmesh_ignore(), libMesh::pi, libMesh::QUAD4, libMesh::Real, libMesh::SCALED_JACOBIAN, and libMesh::TOLERANCE.

◆ testQuad4MinMaxAngle()

void VolumeTest::testQuad4MinMaxAngle ( )
inline

Definition at line 680 of file volume_test.C.

681 {
682 LOG_UNIT_TEST;
683
684 // Case 1: Test that rigid body rotations of a unit square
685 // quadrilateral that have no effect on the quality of the
686 // element.
687 {
688 // Construct unit square QUAD4
689 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(1, 1, 0), Point(0, 1, 0)};
690 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
691 libmesh_ignore(nodes);
692
693 // 1a) Reference Elem should have min angle == max angle == pi/2
694 {
695 Real min_angle = elem->quality(MIN_ANGLE);
696 Real max_angle = elem->quality(MAX_ANGLE);
697 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/min_angle, TOLERANCE);
698 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/max_angle, TOLERANCE);
699 }
700
701 // 1b) Rotate all points about x-axis by 90 degrees
702 Real cost = std::cos(.5*libMesh::pi);
703 Real sint = std::sin(.5*libMesh::pi);
704 RealTensorValue Rx(1, 0, 0,
705 0, cost, sint,
706 0, sint, cost);
707
708 for (auto & pt : pts)
709 pt = Rx * pt;
710
711 {
712 Real min_angle = elem->quality(MIN_ANGLE);
713 Real max_angle = elem->quality(MAX_ANGLE);
714 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/min_angle, TOLERANCE);
715 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/max_angle, TOLERANCE);
716 }
717
718 // 1c) Rotate all points about z-axis by 90 degrees
719 RealTensorValue Rz(cost, -sint, 0,
720 sint, cost, 0,
721 0, 0, 1);
722
723 for (auto & pt : pts)
724 pt = Rz * pt;
725
726 {
727 Real min_angle = elem->quality(MIN_ANGLE);
728 Real max_angle = elem->quality(MAX_ANGLE);
729 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/min_angle, TOLERANCE);
730 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/max_angle, TOLERANCE);
731 }
732
733 // 1d) Rotate all points about y-axis by 270 degrees
734 RealTensorValue Ry(cost, 0, sint,
735 0, 1, 0,
736 -sint, 0, cost);
737
738 for (int cnt=0; cnt<3; ++cnt)
739 for (auto & pt : pts)
740 pt = Ry * pt;
741
742 {
743 Real min_angle = elem->quality(MIN_ANGLE);
744 Real max_angle = elem->quality(MAX_ANGLE);
745 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/min_angle, TOLERANCE);
746 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/max_angle, TOLERANCE);
747 }
748 }
749
750 // Case 2: Rhombus QUAD4. This case should have an interior min
751 // angle of theta and an interior max angle of pi-theta, where
752 // "theta" is the specified amount that we "sheared" the element
753 // by on creation
754 {
755 // Helper lambda function that constructs a rhombus quad with
756 // interior acute angle theta.
757 auto test_rhombus_quad = [this](Real theta)
758 {
759 Real ct = std::cos(theta);
760 Real st = std::sin(theta);
761 std::vector<Point> pts = {
762 Point(0, 0, 0),
763 Point(1, 0, 0),
764 Point(1. + ct, st, 0),
765 Point( ct, st, 0)};
766 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
767 libmesh_ignore(nodes);
768
769 Real min_angle = elem->quality(MIN_ANGLE);
770 Real max_angle = elem->quality(MAX_ANGLE);
771 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/Real(180) / libMesh::pi * theta, /*actual=*/min_angle, TOLERANCE);
772 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/Real(180) / libMesh::pi * (libMesh::pi - theta), /*actual=*/max_angle, TOLERANCE);
773 };
774
775 // 2a) Rhombus with interior angle theta=pi/6.
776 test_rhombus_quad(libMesh::pi / 6);
777
778 // 2b) Rhombus with interior angle theta=pi/3.
779 test_rhombus_quad(libMesh::pi / 3);
780 }
781 }

References construct_elem(), libMesh::libmesh_ignore(), libMesh::MAX_ANGLE, libMesh::MIN_ANGLE, libMesh::pi, libMesh::QUAD4, libMesh::Real, and libMesh::TOLERANCE.

◆ testQuad4TrueCentroid()

void VolumeTest::testQuad4TrueCentroid ( )
inline

Definition at line 78 of file volume_test.C.

79 {
80 LOG_UNIT_TEST;
81
82 // Test Quad4::true_centroid() override
83 {
84 const Elem & quad4 = ReferenceElem::get(QUAD4);
85 Point true_centroid = quad4.true_centroid();
86 LIBMESH_ASSERT_FP_EQUAL(0, true_centroid(0), TOLERANCE*TOLERANCE);
87 LIBMESH_ASSERT_FP_EQUAL(0, true_centroid(1), TOLERANCE*TOLERANCE);
88
89 // Compare to centroid computed via generic base class implementation
90 Point base_class_centroid = quad4.Elem::true_centroid();
91 CPPUNIT_ASSERT(true_centroid.absolute_fuzzy_equals(base_class_centroid, TOLERANCE*TOLERANCE));
92 }
93
94 // Check that "optimized" Quad4::true_centroid() gives same result
95 // as "generic" Elem::true_centroid() on a mesh of 10% distorted elements.
96 {
97 ReplicatedMesh mesh(*TestCommWorld);
98
100 /*nx=*/3, /*ny=*/3,
101 /*xmin=*/0., /*xmax=*/1.,
102 /*ymin=*/0., /*ymax=*/1.,
103 QUAD4);
104
106 /*factor=*/0.1,
107 /*perturb_boundary=*/false);
108
109 for (const auto & elem : mesh.element_ptr_range())
110 {
111 Point derived_centroid = elem->true_centroid();
112 Point base_centroid = elem->Elem::true_centroid();
113
114 // Debugging: check results in detail
115 // auto flags = libMesh::out.flags();
116 // libMesh::out << std::scientific << std::setprecision(16);
117 // libMesh::out << "derived_centroid = " << derived_centroid << std::endl;
118 // libMesh::out << "base_centroid = " << base_centroid << std::endl;
119 // libMesh::out.flags(flags);
120
121 CPPUNIT_ASSERT(derived_centroid.absolute_fuzzy_equals(base_centroid, TOLERANCE*TOLERANCE));
122
123 Real derived_volume = elem->volume();
124 Real base_volume = elem->Elem::volume();
125 LIBMESH_ASSERT_FP_EQUAL(base_volume, derived_volume, TOLERANCE*TOLERANCE);
126 }
127 }
128 }
void build_square(UnstructuredMesh &mesh, const unsigned int nx, const unsigned int ny, const Real xmin=0., const Real xmax=1., const Real ymin=0., const Real ymax=1., const ElemType type=INVALID_ELEM, const bool gauss_lobatto_grid=false)
A specialized build_cube() for 2D meshes.

References libMesh::TypeVector< T >::absolute_fuzzy_equals(), libMesh::MeshTools::Generation::build_square(), libMesh::MeshTools::Modification::distort(), libMesh::ReferenceElem::get(), mesh, libMesh::QUAD4, libMesh::Real, TestCommWorld, libMesh::TOLERANCE, and libMesh::Elem::true_centroid().

◆ testQuad4Warpage()

void VolumeTest::testQuad4Warpage ( )
inline

Definition at line 600 of file volume_test.C.

601 {
602 LOG_UNIT_TEST;
603
604 // Case 1: Test that rigid body rotations of a unit square
605 // quadrilateral that have no effect on the quality of the
606 // element.
607 {
608 // Construct unit square QUAD4
609 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(1, 1, 0), Point(0, 1, 0)};
610 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
611 libmesh_ignore(nodes);
612
613 // 1a) Any flat element should have warp == 1
614 Real warpage = elem->quality(WARP);
615 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0, /*actual=*/warpage, TOLERANCE);
616
617 // 1b) Rotate all points about x-axis by 90 degrees
618 Real cost = std::cos(.5*libMesh::pi);
619 Real sint = std::sin(.5*libMesh::pi);
620 RealTensorValue Rx(1, 0, 0,
621 0, cost, sint,
622 0, sint, cost);
623
624 for (auto & pt : pts)
625 pt = Rx * pt;
626
627 warpage = elem->quality(WARP);
628 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0, /*actual=*/warpage, TOLERANCE);
629
630 // 1c) Rotate all points about z-axis by 90 degrees
631 RealTensorValue Rz(cost, -sint, 0,
632 sint, cost, 0,
633 0, 0, 1);
634
635 for (auto & pt : pts)
636 pt = Rz * pt;
637
638 warpage = elem->quality(WARP);
639 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0, /*actual=*/warpage, TOLERANCE);
640
641 // 1d) Rotate all points about y-axis by 270 degrees
642 RealTensorValue Ry(cost, 0, sint,
643 0, 1, 0,
644 -sint, 0, cost);
645
646 for (int cnt=0; cnt<3; ++cnt)
647 for (auto & pt : pts)
648 pt = Ry * pt;
649
650 warpage = elem->quality(WARP);
651 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/1.0, /*actual=*/warpage, TOLERANCE);
652 }
653
654 // Case 2: Unit square quadrilateral with Node 2 displaced by a distance h in the z-direction.
655 {
656 auto test_warped_quad = [this](Real h)
657 {
658 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(1, 1, h), Point(0, 1, 0)};
659 auto [elem, nodes] = this->construct_elem(pts, QUAD4);
660 libmesh_ignore(nodes);
661
662 Real warpage = elem->quality(WARP);
663 // libMesh::out << "QUAD with node 3 displaced by h = "
664 // << h << " in the z-direction , warpage = " << warpage
665 // << std::endl;
666 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/Real(1) / (h*h + 1), /*actual=*/warpage, TOLERANCE);
667 };
668
669 // For h = 0.1, the expected warpage value is ~ 0.991, so not
670 // much different than the value of 1.0 for a flat element.
671 test_warped_quad(0.1);
672
673 // For h = 0.3, the expected warpage value is ~ 0.917431, which
674 // is pretty close to the lower bound (0.9) of "acceptable"
675 // warpage, as suggested in the Cubit manual.
676 test_warped_quad(0.3);
677 }
678 }

References construct_elem(), libMesh::libmesh_ignore(), libMesh::pi, libMesh::QUAD4, libMesh::Real, libMesh::TOLERANCE, and libMesh::WARP.

◆ testTet4DihedralAngle()

void VolumeTest::testTet4DihedralAngle ( )
inline

Definition at line 826 of file volume_test.C.

827 {
828 LOG_UNIT_TEST;
829
830 // Construct a tetrahedron whose projection into the x-y plane looks like the unit square,
831 // and where the apex node is just slightly out of the x-y plane. This element should have
832 // reasonable MIN,MAX_ANGLE values but MIN,MAX_DIHEDRAL angles of nearly 0. This is an
833 // example that demonstrates one should not solely use edge angles to determine Elem quality
834 // in 3D.
835 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(0, 1, 0), Point(1, 1, 0.01)};
836 auto [elem, nodes] = this->construct_elem(pts, TET4);
837 libmesh_ignore(nodes);
838
839 Real min_angle = elem->quality(MIN_ANGLE);
840 Real max_angle = elem->quality(MAX_ANGLE);
841 Real min_dihedral_angle = elem->quality(MIN_DIHEDRAL_ANGLE);
842 Real max_dihedral_angle = elem->quality(MAX_DIHEDRAL_ANGLE);
843
844 // Debugging
845 // libMesh::out << "Squashed Tet4 min_angle = " << min_angle << " degrees" << std::endl;
846 // libMesh::out << "Squashed Tet4 max_angle = " << max_angle << " degrees" << std::endl;
847 // libMesh::out << "Squashed Tet4 min_dihedral_angle = " << min_dihedral_angle << " degrees" << std::endl;
848 // libMesh::out << "Squashed Tet4 max_dihedral_angle = " << max_dihedral_angle << " degrees" << std::endl;
849
850 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/44.9985676771277, /*actual=*/min_angle, TOLERANCE);
851 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/90, /*actual=*/max_angle, TOLERANCE);
852
853 // Assert that both min and max dihedral angles are less than 1 degree (a very low quality element)
854 CPPUNIT_ASSERT_LESS(Real(1), min_dihedral_angle);
855 CPPUNIT_ASSERT_LESS(Real(1), max_dihedral_angle);
856 }

References construct_elem(), libMesh::libmesh_ignore(), libMesh::MAX_ANGLE, libMesh::MAX_DIHEDRAL_ANGLE, libMesh::MIN_ANGLE, libMesh::MIN_DIHEDRAL_ANGLE, libMesh::Real, libMesh::TET4, and libMesh::TOLERANCE.

◆ testTet4Jacobian()

void VolumeTest::testTet4Jacobian ( )
inline

Definition at line 858 of file volume_test.C.

859 {
860 LOG_UNIT_TEST;
861
862 {
863 // Same element as in testTet4DihedralAngle(). Here we verify that
864 // the element is low quality since the SCALED_JACOBIAN is < O(h)
865 // as h -> 0, and h can be arbitrarily small in the squashed element.
866 Real h = Real(1)/100;
867 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(0, 1, 0), Point(1, 1, h)};
868 auto [elem, nodes] = this->construct_elem(pts, TET4);
869 libmesh_ignore(nodes);
870
871 Real jac = elem->quality(JACOBIAN);
872 Real scaled_jac = elem->quality(SCALED_JACOBIAN);
873
874 // Debugging
875 // libMesh::out << "Squashed Tet4 jac = " << jac << std::endl;
876 // libMesh::out << "Squashed Tet4 scaled_jac = " << scaled_jac << std::endl;
877
878 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/h, /*actual=*/jac, TOLERANCE);
879 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/h/(h*h+1)/std::sqrt(h*h+2),
880 /*actual=*/scaled_jac, TOLERANCE * TOLERANCE);
881 }
882
883 {
884 // A representative Tet generated by calling build_cube(). This
885 // element has minimum interior edge angle of approximately
886 // 35.26 degrees, so it it is not a simple refinement of the
887 // reference element. It is not located at the origin, but still
888 // has a simple to compute value for the JACOBIAN and
889 // SCALED_JACOBIAN quality metrics.
890 std::vector<Point> pts = {
891 Point(2.5, 2.5, 4.5),
892 Point(3.5, 1.5, 5.5),
893 Point(1.5, 1.5, 5.5),
894 Point(2.5, 2.5, 5.5)
895 };
896 auto [elem, nodes] = this->construct_elem(pts, TET4);
897 libmesh_ignore(nodes);
898
899 Real jac = elem->quality(JACOBIAN);
900 Real scaled_jac = elem->quality(SCALED_JACOBIAN);
901
902 // Debugging
903 // libMesh::out << "build_cube() Tet4 jac = " << jac << std::endl;
904 // libMesh::out << "build_cube() Tet4 scaled_jac = " << scaled_jac << std::endl;
905
906 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/2, /*actual=*/jac, TOLERANCE);
907 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/Real(1)/std::sqrt(Real(6)), /*actual=*/scaled_jac, TOLERANCE);
908 }
909 }

References construct_elem(), libMesh::JACOBIAN, libMesh::libmesh_ignore(), libMesh::Real, libMesh::SCALED_JACOBIAN, libMesh::TET4, and libMesh::TOLERANCE.

◆ testTri3AspectRatio()

void VolumeTest::testTri3AspectRatio ( )
inline

Definition at line 555 of file volume_test.C.

556 {
557 LOG_UNIT_TEST;
558
559 // Case 1) Reference TRI3
560 {
561 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(0, 1, 0)};
562 auto [elem, nodes] = this->construct_elem(pts, TRI3);
563 libmesh_ignore(nodes);
564
565 // Compute the aspect ratio for the reference Tri
566 // The expected value is ~ 1.44338
567 Real aspect_ratio = elem->quality(ASPECT_RATIO);
568 // libMesh::out << "Unit TRI3 aspect ratio = " << aspect_ratio << std::endl;
569 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/Real(5)/2/std::sqrt(Real(3)), /*actual=*/aspect_ratio, TOLERANCE);
570 }
571
572 // Case 2) Equilateral TRI3
573 {
574 std::vector<Point> pts = {Point(0, 0, 0), Point(1, 0, 0), Point(0.5, 0.5*std::sqrt(3), 0)};
575 auto [elem, nodes] = this->construct_elem(pts, TRI3);
576 libmesh_ignore(nodes);
577
578 // Compute the aspect ratio for the reference Tri
579 // The expected value is 1.0
580 Real aspect_ratio = elem->quality(ASPECT_RATIO);
581 // libMesh::out << "Equilateral TRI3 aspect ratio = " << aspect_ratio << std::endl;
582 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/Real(1), /*actual=*/aspect_ratio, TOLERANCE);
583 }
584
585 // Case 3) Reference TRI3 with one leg length = L >> 1
586 {
587 Real L = 10.;
588 std::vector<Point> pts = {Point(0, 0, 0), Point(L, 0, 0), Point(0, 1, 0)};
589 auto [elem, nodes] = this->construct_elem(pts, TRI3);
590 libmesh_ignore(nodes);
591
592 // Compute the aspect ratio for the reference Tri
593 // The expected value is ~ 11.5759
594 Real aspect_ratio = elem->quality(ASPECT_RATIO);
595 // libMesh::out << "TRI3 with leg length L = " << L << ", aspect ratio = " << aspect_ratio << std::endl;
596 LIBMESH_ASSERT_FP_EQUAL(/*expected=*/Real(1)/std::sqrt(Real(3)) * (Real(2)*L + Real(1)/(2*L)), /*actual=*/aspect_ratio, TOLERANCE);
597 }
598 }

References libMesh::ASPECT_RATIO, construct_elem(), libMesh::libmesh_ignore(), libMesh::Real, libMesh::TOLERANCE, and libMesh::TRI3.

◆ testTri3TrueCentroid()

void VolumeTest::testTri3TrueCentroid ( )
inline

Definition at line 65 of file volume_test.C.

66 {
67 LOG_UNIT_TEST;
68
69 // The true_centroid() == vertex_average() == (1/3, 1/3) for reference Tri3
70 {
71 const Elem & tri3 = ReferenceElem::get(TRI3);
72 Point true_centroid = tri3.true_centroid();
73 LIBMESH_ASSERT_FP_EQUAL(Real(1)/3, true_centroid(0), TOLERANCE*TOLERANCE);
74 LIBMESH_ASSERT_FP_EQUAL(Real(1)/3, true_centroid(1), TOLERANCE*TOLERANCE);
75 }
76 }

References libMesh::ReferenceElem::get(), libMesh::Real, libMesh::TOLERANCE, libMesh::TRI3, and libMesh::Elem::true_centroid().

◆ testTwistedVolume()

void VolumeTest::testTwistedVolume ( )
inline

Definition at line 175 of file volume_test.C.

176 {
177 LOG_UNIT_TEST;
178
179 ReplicatedMesh mesh(*TestCommWorld);
180
181 // Build an element type that will fall back on our generic
182 // quadrature-based Elem::volume()
184 /*nelem=*/1, /*nelem=*/1, /*nelem=*/1,
185 /*xmin=*/-1, /*xmax=*/1,
186 /*ymin=*/-1, /*ymax=*/1,
187 /*zmin=*/-1, /*zmax=*/1,
188 PRISM21);
189
190 // Pick an element and twist it
191 Elem * prism6 = mesh.elem_ptr(0);
192 prism6->point(1) *= -1;
193 prism6->point(1) += 2*prism6->point(0);
194
195 // The real test here is that volume() doesn't throw
196 const Real vol = prism6->volume();
197 const Real gold_vol = 3+Real(5)/9;
198 CPPUNIT_ASSERT_LESS(TOLERANCE, std::abs(vol-gold_vol));
199 }
const Point & point(const unsigned int i) const
Definition elem.h:2462

References libMesh::MeshTools::Generation::build_cube(), libMesh::MeshBase::elem_ptr(), mesh, libMesh::Elem::point(), libMesh::PRISM21, libMesh::Real, TestCommWorld, libMesh::TOLERANCE, and libMesh::Elem::volume().


The documentation for this class was generated from the following file: