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

Public Member Functions

 LIBMESH_CPPUNIT_TEST_SUITE (MeshSmootherTest)
 The goal of this test is to verify proper operation of the MeshSmoother subclasses.
 
 CPPUNIT_TEST (testLaplaceQuad)
 
 CPPUNIT_TEST (testLaplaceTri)
 
 CPPUNIT_TEST (testVariationalEdge2)
 
 CPPUNIT_TEST (testVariationalEdge3)
 
 CPPUNIT_TEST (testVariationalEdge3MultipleSubdomains)
 
 CPPUNIT_TEST (testVariationalQuad4)
 
 CPPUNIT_TEST (testVariationalQuad4MultipleSubdomains)
 
 CPPUNIT_TEST (testVariationalQuad8)
 
 CPPUNIT_TEST (testVariationalQuad9)
 
 CPPUNIT_TEST (testVariationalQuad9MultipleSubdomains)
 
 CPPUNIT_TEST (testVariationalQuad4Tangled)
 
 CPPUNIT_TEST (testVariationalTri3)
 
 CPPUNIT_TEST (testVariationalTri6)
 
 CPPUNIT_TEST (testVariationalTri6MultipleSubdomains)
 
 CPPUNIT_TEST (testVariationalHex8)
 
 CPPUNIT_TEST (testVariationalHex20)
 
 CPPUNIT_TEST (testVariationalHex27)
 
 CPPUNIT_TEST (testVariationalHex27Tangled)
 
 CPPUNIT_TEST (testVariationalHex27MultipleSubdomains)
 
 CPPUNIT_TEST (testVariationalPyramid5)
 
 CPPUNIT_TEST (testVariationalPyramid13)
 
 CPPUNIT_TEST (testVariationalPyramid14)
 
 CPPUNIT_TEST (testVariationalPyramid18)
 
 CPPUNIT_TEST (testVariationalPyramid18MultipleSubdomains)
 
 CPPUNIT_TEST (testVariationalPrism6)
 
 CPPUNIT_TEST (testVariationalPrism15)
 
 CPPUNIT_TEST (testVariationalPrism18)
 
 CPPUNIT_TEST (testVariationalPrism20)
 
 CPPUNIT_TEST (testVariationalPrism21)
 
 CPPUNIT_TEST (testVariationalPrism21MultipleSubdomains)
 
 CPPUNIT_TEST (testVariationalTet4)
 
 CPPUNIT_TEST (testVariationalTet10)
 
 CPPUNIT_TEST (testVariationalTet14)
 
 CPPUNIT_TEST (testVariationalTet14MultipleSubdomains)
 
 CPPUNIT_TEST (testVariationalMixed2D)
 
 CPPUNIT_TEST (testVariationalMixed3D)
 
 CPPUNIT_TEST (testVariationalQuadratureHourglass)
 
 CPPUNIT_TEST_SUITE_END ()
 
void setUp ()
 
void tearDown ()
 
void testLaplaceSmoother (ReplicatedMesh &mesh, MeshSmoother &smoother, ElemType type)
 
std::pair< unsigned int, unsigned int > numCenteredAndFacedDimensions (const Point &point, const Real &side_length, const Real &tol)
 
bool pointIsCubeCenter (const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
 
bool pointIsCubeVertex (const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
 
bool pointIsCubeFaceCenter (const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
 
void testVariationalSmoother (Mesh &mesh, VariationalMeshSmoother &smoother, const ElemType type, const bool multiple_subdomains=false, const bool tangle_mesh=false, const Real tangle_damping_factor=1.0)
 
void testVariationalSmootherRegression (const ReplicatedMesh &gold_mesh)
 
void testLaplaceQuad ()
 
void testLaplaceTri ()
 
void testVariationalEdge2 ()
 
void testVariationalEdge3 ()
 
void testVariationalEdge3MultipleSubdomains ()
 
void testVariationalQuad4 ()
 
void testVariationalQuad4MultipleSubdomains ()
 
void testVariationalQuad8 ()
 
void testVariationalQuad9 ()
 
void testVariationalQuad9MultipleSubdomains ()
 
void testVariationalQuad4Tangled ()
 
void testVariationalTri3 ()
 
void testVariationalTri6 ()
 
void testVariationalTri6MultipleSubdomains ()
 
void testVariationalHex8 ()
 
void testVariationalHex20 ()
 
void testVariationalHex27 ()
 
void testVariationalHex27MultipleSubdomains ()
 
void testVariationalHex27Tangled ()
 
void testVariationalPyramid5 ()
 
void testVariationalPyramid13 ()
 
void testVariationalPyramid14 ()
 
void testVariationalPyramid18 ()
 
void testVariationalPyramid18MultipleSubdomains ()
 
void testVariationalPrism6 ()
 
void testVariationalPrism15 ()
 
void testVariationalPrism18 ()
 
void testVariationalPrism20 ()
 
void testVariationalPrism21 ()
 
void testVariationalPrism21MultipleSubdomains ()
 
void testVariationalTet4 ()
 
void testVariationalTet10 ()
 
void testVariationalTet14 ()
 
void testVariationalTet14MultipleSubdomains ()
 
void testVariationalMixed2D ()
 
void testVariationalQuadratureHourglass ()
 
void testVariationalMixed3D ()
 

Detailed Description

Definition at line 222 of file mesh_smoother_test.C.

Member Function Documentation

◆ CPPUNIT_TEST() [1/37]

MeshSmootherTest::CPPUNIT_TEST ( testLaplaceQuad  )

◆ CPPUNIT_TEST() [2/37]

MeshSmootherTest::CPPUNIT_TEST ( testLaplaceTri  )

◆ CPPUNIT_TEST() [3/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalEdge2  )

◆ CPPUNIT_TEST() [4/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalEdge3  )

◆ CPPUNIT_TEST() [5/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalEdge3MultipleSubdomains  )

◆ CPPUNIT_TEST() [6/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex20  )

◆ CPPUNIT_TEST() [7/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex27  )

◆ CPPUNIT_TEST() [8/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex27MultipleSubdomains  )

◆ CPPUNIT_TEST() [9/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex27Tangled  )

◆ CPPUNIT_TEST() [10/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex8  )

◆ CPPUNIT_TEST() [11/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalMixed2D  )

◆ CPPUNIT_TEST() [12/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalMixed3D  )

◆ CPPUNIT_TEST() [13/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism15  )

◆ CPPUNIT_TEST() [14/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism18  )

◆ CPPUNIT_TEST() [15/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism20  )

◆ CPPUNIT_TEST() [16/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism21  )

◆ CPPUNIT_TEST() [17/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism21MultipleSubdomains  )

◆ CPPUNIT_TEST() [18/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism6  )

◆ CPPUNIT_TEST() [19/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid13  )

◆ CPPUNIT_TEST() [20/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid14  )

◆ CPPUNIT_TEST() [21/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid18  )

◆ CPPUNIT_TEST() [22/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid18MultipleSubdomains  )

◆ CPPUNIT_TEST() [23/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid5  )

◆ CPPUNIT_TEST() [24/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad4  )

◆ CPPUNIT_TEST() [25/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad4MultipleSubdomains  )

◆ CPPUNIT_TEST() [26/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad4Tangled  )

◆ CPPUNIT_TEST() [27/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad8  )

◆ CPPUNIT_TEST() [28/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad9  )

◆ CPPUNIT_TEST() [29/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad9MultipleSubdomains  )

◆ CPPUNIT_TEST() [30/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuadratureHourglass  )

◆ CPPUNIT_TEST() [31/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTet10  )

◆ CPPUNIT_TEST() [32/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTet14  )

◆ CPPUNIT_TEST() [33/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTet14MultipleSubdomains  )

◆ CPPUNIT_TEST() [34/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTet4  )

◆ CPPUNIT_TEST() [35/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTri3  )

◆ CPPUNIT_TEST() [36/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTri6  )

◆ CPPUNIT_TEST() [37/37]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTri6MultipleSubdomains  )

◆ CPPUNIT_TEST_SUITE_END()

MeshSmootherTest::CPPUNIT_TEST_SUITE_END ( )

◆ LIBMESH_CPPUNIT_TEST_SUITE()

MeshSmootherTest::LIBMESH_CPPUNIT_TEST_SUITE ( MeshSmootherTest  )

The goal of this test is to verify proper operation of the MeshSmoother subclasses.

◆ numCenteredAndFacedDimensions()

std::pair< unsigned int, unsigned int > MeshSmootherTest::numCenteredAndFacedDimensions ( const Point &  point,
const Real &  side_length,
const Real &  tol 
)
inline

Definition at line 359 of file mesh_smoother_test.C.

360 {
361 const auto half_length = 0.5 * side_length;
362 unsigned int num_centered = 0;
363 unsigned int num_face = 0;
364 for (const auto d : make_range(3))
365 {
366 const auto num_half_lengths = point(d) / half_length;
367 if (std::abs(num_half_lengths - std::round(num_half_lengths)) > tol)
368 // This coordinante does not occur at a sub-cube face or center
369 continue;
370
371 if (std::lround(num_half_lengths) % 2)
372 // An odd number of half lengths means point(d) is at a sub-cube center
373 ++num_centered;
374 else
375 // An odd number of half lengths means point(d) is on a sub-cube face
376 ++num_face;
377 }
378
379 return std::make_pair(num_centered, num_face);
380 }
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::make_range().

Referenced by pointIsCubeCenter(), pointIsCubeFaceCenter(), and pointIsCubeVertex().

◆ pointIsCubeCenter()

bool MeshSmootherTest::pointIsCubeCenter ( const Point &  point,
const Real &  side_length,
const Real &  tol = TOLERANCE 
)
inline

Definition at line 384 of file mesh_smoother_test.C.

385 {
386 return numCenteredAndFacedDimensions(point, side_length, tol).first == 3;
387 }
std::pair< unsigned int, unsigned int > numCenteredAndFacedDimensions(const Point &point, const Real &side_length, const Real &tol)

References numCenteredAndFacedDimensions().

Referenced by testVariationalSmoother().

◆ pointIsCubeFaceCenter()

bool MeshSmootherTest::pointIsCubeFaceCenter ( const Point &  point,
const Real &  side_length,
const Real &  tol = TOLERANCE 
)
inline

Definition at line 398 of file mesh_smoother_test.C.

399 {
400 const auto result = numCenteredAndFacedDimensions(point, side_length, tol);
401 return (result.first == 2) && (result.second == 1);
402 }

References numCenteredAndFacedDimensions().

Referenced by testVariationalSmoother().

◆ pointIsCubeVertex()

bool MeshSmootherTest::pointIsCubeVertex ( const Point &  point,
const Real &  side_length,
const Real &  tol = TOLERANCE 
)
inline

Definition at line 391 of file mesh_smoother_test.C.

392 {
393 return numCenteredAndFacedDimensions(point, side_length, tol).second == 3;
394 }

References numCenteredAndFacedDimensions().

Referenced by testVariationalSmoother().

◆ setUp()

void MeshSmootherTest::setUp ( )
inline

Definition at line 286 of file mesh_smoother_test.C.

286{}

◆ tearDown()

void MeshSmootherTest::tearDown ( )
inline

Definition at line 288 of file mesh_smoother_test.C.

288{}

◆ testLaplaceQuad()

void MeshSmootherTest::testLaplaceQuad ( )
inline

Definition at line 1071 of file mesh_smoother_test.C.

1072 {
1073 ReplicatedMesh mesh(*TestCommWorld);
1074 LaplaceMeshSmoother laplace(mesh, 8);
1075
1076 testLaplaceSmoother(mesh, laplace, QUAD4);
1077 }
void testLaplaceSmoother(ReplicatedMesh &mesh, MeshSmoother &smoother, ElemType type)
This class defines the data structures necessary for Laplace smoothing.
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
MeshBase & mesh

References mesh, libMesh::QUAD4, TestCommWorld, and testLaplaceSmoother().

◆ testLaplaceSmoother()

void MeshSmootherTest::testLaplaceSmoother ( ReplicatedMesh &  mesh,
MeshSmoother &  smoother,
ElemType  type 
)
inline

Definition at line 290 of file mesh_smoother_test.C.

291 {
292 LOG_UNIT_TEST;
293
294 constexpr unsigned int n_elems_per_side = 4;
295
297 mesh, n_elems_per_side, n_elems_per_side, 0., 1., 0., 1., type);
298
299 // Move it around so we have something that needs smoothing
300 DistortSquare ds;
302
303 // Assert the distortion is as expected
304 auto center_distortion_is =
305 [](const Node & node, int d, bool distortion, Real distortion_tol = TOLERANCE) {
306 const Real r = node(d);
307 const Real R = r * n_elems_per_side;
308 CPPUNIT_ASSERT_GREATER(-TOLERANCE * TOLERANCE, r);
309 CPPUNIT_ASSERT_GREATER(-TOLERANCE * TOLERANCE, 1 - r);
310
311 // If we're at the boundaries we should *never* be distorted
312 if (std::abs(node(0)) < TOLERANCE * TOLERANCE ||
313 std::abs(node(0) - 1) < TOLERANCE * TOLERANCE)
314 {
315 const Real R1 = node(1) * n_elems_per_side;
316 CPPUNIT_ASSERT_LESS(TOLERANCE * TOLERANCE, std::abs(R1 - std::round(R1)));
317 return true;
318 }
319
320 if (std::abs(node(1)) < TOLERANCE * TOLERANCE ||
321 std::abs(node(1) - 1) < TOLERANCE * TOLERANCE)
322 {
323 const Real R0 = node(0) * n_elems_per_side;
324 CPPUNIT_ASSERT_LESS(TOLERANCE * TOLERANCE, std::abs(R0 - std::round(R0)));
325
326 return true;
327 }
328
329 // If we're at the center we're fine
330 if (std::abs(r - 0.5) < TOLERANCE * TOLERANCE)
331 return true;
332
333 return ((std::abs(R - std::round(R)) > distortion_tol) == distortion);
334 };
335
336 for (auto node : mesh.node_ptr_range())
337 {
338 CPPUNIT_ASSERT(center_distortion_is(*node, 0, true));
339 CPPUNIT_ASSERT(center_distortion_is(*node, 1, true));
340 }
341
342 // Enough iterations to mostly fix us up. Laplace seems to be at 1e-3
343 // tolerance by iteration 6, so hopefully everything is there on any
344 // system by 8.
345 for (unsigned int i = 0; i != 8; ++i)
346 smoother.smooth();
347
348 // Make sure we're not too distorted anymore.
349 for (auto node : mesh.node_ptr_range())
350 {
351 CPPUNIT_ASSERT(center_distortion_is(*node, 0, false, 1e-3));
352 CPPUNIT_ASSERT(center_distortion_is(*node, 1, false, 1e-3));
353 }
354 }
virtual void smooth()=0
Function which actually performs the smoothing operations.
A Node is like a Point, but with more information.
Definition node.h:55
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.
void redistribute(MeshBase &mesh, const FunctionBase< Real > &mapfunc)
Deterministically perturb the nodal locations.
static constexpr Real TOLERANCE
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

References libMesh::MeshTools::Generation::build_square(), mesh, libMesh::Real, libMesh::MeshTools::Modification::redistribute(), libMesh::MeshSmoother::smooth(), and libMesh::TOLERANCE.

Referenced by testLaplaceQuad(), and testLaplaceTri().

◆ testLaplaceTri()

void MeshSmootherTest::testLaplaceTri ( )
inline

Definition at line 1079 of file mesh_smoother_test.C.

1080 {
1081 ReplicatedMesh mesh(*TestCommWorld);
1082 LaplaceMeshSmoother laplace(mesh, 8);
1083
1084 testLaplaceSmoother(mesh, laplace, TRI3);
1085 }

References mesh, TestCommWorld, testLaplaceSmoother(), and libMesh::TRI3.

◆ testVariationalEdge2()

void MeshSmootherTest::testVariationalEdge2 ( )
inline

Definition at line 1088 of file mesh_smoother_test.C.

1089 {
1090 Mesh mesh(*TestCommWorld);
1091 VariationalMeshSmoother variational(mesh);
1092
1093 testVariationalSmoother(mesh, variational, EDGE2);
1094 }
void testVariationalSmoother(Mesh &mesh, VariationalMeshSmoother &smoother, const ElemType type, const bool multiple_subdomains=false, const bool tangle_mesh=false, const Real tangle_damping_factor=1.0)
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
This is an implementation of Larisa Branets' smoothing algorithms.

References libMesh::EDGE2, mesh, TestCommWorld, and testVariationalSmoother().

◆ testVariationalEdge3()

void MeshSmootherTest::testVariationalEdge3 ( )
inline

Definition at line 1096 of file mesh_smoother_test.C.

1097 {
1098 Mesh mesh(*TestCommWorld);
1099 VariationalMeshSmoother variational(mesh);
1100
1101 testVariationalSmoother(mesh, variational, EDGE3);
1102 }

References libMesh::EDGE3, mesh, TestCommWorld, and testVariationalSmoother().

◆ testVariationalEdge3MultipleSubdomains()

void MeshSmootherTest::testVariationalEdge3MultipleSubdomains ( )
inline

Definition at line 1104 of file mesh_smoother_test.C.

1105 {
1106 Mesh mesh(*TestCommWorld);
1107 VariationalMeshSmoother variational(mesh);
1108
1109 testVariationalSmoother(mesh, variational, EDGE3, true);
1110 }

References libMesh::EDGE3, mesh, TestCommWorld, and testVariationalSmoother().

◆ testVariationalHex20()

void MeshSmootherTest::testVariationalHex20 ( )
inline

Definition at line 1200 of file mesh_smoother_test.C.

1201 {
1202 Mesh mesh(*TestCommWorld);
1203 VariationalMeshSmoother variational(mesh);
1204
1205 testVariationalSmoother(mesh, variational, HEX20);
1206 }

References libMesh::HEX20, mesh, TestCommWorld, and testVariationalSmoother().

◆ testVariationalHex27()

void MeshSmootherTest::testVariationalHex27 ( )
inline

Definition at line 1208 of file mesh_smoother_test.C.

1209 {
1210 Mesh mesh(*TestCommWorld);
1211 VariationalMeshSmoother variational(mesh);
1212
1213 testVariationalSmoother(mesh, variational, HEX27);
1214 }

References libMesh::HEX27, mesh, TestCommWorld, and testVariationalSmoother().

◆ testVariationalHex27MultipleSubdomains()

void MeshSmootherTest::testVariationalHex27MultipleSubdomains ( )
inline

Definition at line 1216 of file mesh_smoother_test.C.

1217 {
1218 Mesh mesh(*TestCommWorld);
1219 VariationalMeshSmoother variational(mesh);
1220
1221 testVariationalSmoother(mesh, variational, HEX27, true);
1222 }

References libMesh::HEX27, mesh, TestCommWorld, and testVariationalSmoother().

◆ testVariationalHex27Tangled()

void MeshSmootherTest::testVariationalHex27Tangled ( )
inline

Definition at line 1224 of file mesh_smoother_test.C.

1225 {
1226 Mesh mesh(*TestCommWorld);
1227 VariationalMeshSmoother variational(mesh);
1228
1229 testVariationalSmoother(mesh, variational, HEX27, false, true, 0.55);
1230 }

References libMesh::HEX27, mesh, TestCommWorld, and testVariationalSmoother().

◆ testVariationalHex8()

void MeshSmootherTest::testVariationalHex8 ( )
inline

Definition at line 1192 of file mesh_smoother_test.C.

1193 {
1194 Mesh mesh(*TestCommWorld);
1195 VariationalMeshSmoother variational(mesh);
1196
1197 testVariationalSmoother(mesh, variational, HEX8);
1198 }

References libMesh::HEX8, mesh, TestCommWorld, and testVariationalSmoother().

◆ testVariationalMixed2D()

void MeshSmootherTest::testVariationalMixed2D ( )
inline

Definition at line 1364 of file mesh_smoother_test.C.

1365 {
1366 ReplicatedMesh mesh(*TestCommWorld);
1367 mesh.read("meshes/quad4_tri3_smoothed.xda.gz");
1368
1370 }
void testVariationalSmootherRegression(const ReplicatedMesh &gold_mesh)
virtual void read(const std::string &name, void *mesh_data=nullptr, bool skip_renumber_nodes_and_elements=false, bool skip_find_neighbors=false, bool skip_detect_interior_parents=false)=0
Interfaces for reading/writing a mesh to/from a file.

References mesh, libMesh::MeshBase::read(), TestCommWorld, and testVariationalSmootherRegression().

◆ testVariationalMixed3D()

void MeshSmootherTest::testVariationalMixed3D ( )
inline

Definition at line 1428 of file mesh_smoother_test.C.

1429 {
1430 ReplicatedMesh mesh(*TestCommWorld);
1431 mesh.read("meshes/hex8_prism6_smoothed.xda.gz");
1432
1434 }

References mesh, libMesh::MeshBase::read(), TestCommWorld, and testVariationalSmootherRegression().

◆ testVariationalPrism15()

void MeshSmootherTest::testVariationalPrism15 ( )
inline

Definition at line 1280 of file mesh_smoother_test.C.

1281 {
1282 Mesh mesh(*TestCommWorld);
1283 VariationalMeshSmoother variational(mesh);
1284
1285 testVariationalSmoother(mesh, variational, PRISM15);
1286 }

References mesh, libMesh::PRISM15, TestCommWorld, and testVariationalSmoother().

◆ testVariationalPrism18()

void MeshSmootherTest::testVariationalPrism18 ( )
inline

Definition at line 1288 of file mesh_smoother_test.C.

1289 {
1290 Mesh mesh(*TestCommWorld);
1291 VariationalMeshSmoother variational(
1292 mesh, 0.5, true, TOLERANCE * TOLERANCE, 10 * TOLERANCE * TOLERANCE);
1293
1294 testVariationalSmoother(mesh, variational, PRISM18);
1295 }

References mesh, libMesh::PRISM18, TestCommWorld, testVariationalSmoother(), and libMesh::TOLERANCE.

◆ testVariationalPrism20()

void MeshSmootherTest::testVariationalPrism20 ( )
inline

Definition at line 1297 of file mesh_smoother_test.C.

1298 {
1299 Mesh mesh(*TestCommWorld);
1300 VariationalMeshSmoother variational(
1301 mesh, 0.5, true, TOLERANCE * TOLERANCE, 10 * TOLERANCE * TOLERANCE);
1302
1303 testVariationalSmoother(mesh, variational, PRISM20);
1304 }

References mesh, libMesh::PRISM20, TestCommWorld, testVariationalSmoother(), and libMesh::TOLERANCE.

◆ testVariationalPrism21()

void MeshSmootherTest::testVariationalPrism21 ( )
inline

Definition at line 1306 of file mesh_smoother_test.C.

1307 {
1308 Mesh mesh(*TestCommWorld);
1309 VariationalMeshSmoother variational(
1310 mesh, 0.5, true, TOLERANCE * TOLERANCE, 10 * TOLERANCE * TOLERANCE);
1311
1312 testVariationalSmoother(mesh, variational, PRISM21);
1313 }

References mesh, libMesh::PRISM21, TestCommWorld, testVariationalSmoother(), and libMesh::TOLERANCE.

◆ testVariationalPrism21MultipleSubdomains()

void MeshSmootherTest::testVariationalPrism21MultipleSubdomains ( )
inline

Definition at line 1315 of file mesh_smoother_test.C.

1316 {
1317 Mesh mesh(*TestCommWorld);
1318 mesh.allow_renumbering(false);
1319 VariationalMeshSmoother variational(mesh);
1320
1321 testVariationalSmoother(mesh, variational, PRISM21, true);
1322 }
void allow_renumbering(bool allow)
If false is passed in then this mesh will no longer be renumbered when being prepared for use.
Definition mesh_base.h:1359

References libMesh::MeshBase::allow_renumbering(), mesh, libMesh::PRISM21, TestCommWorld, and testVariationalSmoother().

◆ testVariationalPrism6()

void MeshSmootherTest::testVariationalPrism6 ( )
inline

Definition at line 1272 of file mesh_smoother_test.C.

1273 {
1274 Mesh mesh(*TestCommWorld);
1275 VariationalMeshSmoother variational(mesh);
1276
1277 testVariationalSmoother(mesh, variational, PRISM6);
1278 }

References mesh, libMesh::PRISM6, TestCommWorld, and testVariationalSmoother().

◆ testVariationalPyramid13()

void MeshSmootherTest::testVariationalPyramid13 ( )
inline

Definition at line 1240 of file mesh_smoother_test.C.

1241 {
1242 Mesh mesh(*TestCommWorld);
1243 VariationalMeshSmoother variational(mesh);
1244
1245 testVariationalSmoother(mesh, variational, PYRAMID13);
1246 }

References mesh, libMesh::PYRAMID13, TestCommWorld, and testVariationalSmoother().

◆ testVariationalPyramid14()

void MeshSmootherTest::testVariationalPyramid14 ( )
inline

Definition at line 1248 of file mesh_smoother_test.C.

1249 {
1250 Mesh mesh(*TestCommWorld);
1251 VariationalMeshSmoother variational(mesh);
1252
1253 testVariationalSmoother(mesh, variational, PYRAMID14);
1254 }

References mesh, libMesh::PYRAMID14, TestCommWorld, and testVariationalSmoother().

◆ testVariationalPyramid18()

void MeshSmootherTest::testVariationalPyramid18 ( )
inline

Definition at line 1256 of file mesh_smoother_test.C.

1257 {
1258 Mesh mesh(*TestCommWorld);
1259 VariationalMeshSmoother variational(mesh);
1260
1261 testVariationalSmoother(mesh, variational, PYRAMID18);
1262 }

References mesh, libMesh::PYRAMID18, TestCommWorld, and testVariationalSmoother().

◆ testVariationalPyramid18MultipleSubdomains()

void MeshSmootherTest::testVariationalPyramid18MultipleSubdomains ( )
inline

Definition at line 1264 of file mesh_smoother_test.C.

1265 {
1266 Mesh mesh(*TestCommWorld);
1267 VariationalMeshSmoother variational(mesh, 0.0);
1268
1269 testVariationalSmoother(mesh, variational, PYRAMID18, true);
1270 }

References mesh, libMesh::PYRAMID18, TestCommWorld, and testVariationalSmoother().

◆ testVariationalPyramid5()

void MeshSmootherTest::testVariationalPyramid5 ( )
inline

Definition at line 1232 of file mesh_smoother_test.C.

1233 {
1234 Mesh mesh(*TestCommWorld);
1235 VariationalMeshSmoother variational(mesh);
1236
1237 testVariationalSmoother(mesh, variational, PYRAMID5);
1238 }

References mesh, libMesh::PYRAMID5, TestCommWorld, and testVariationalSmoother().

◆ testVariationalQuad4()

void MeshSmootherTest::testVariationalQuad4 ( )
inline

Definition at line 1112 of file mesh_smoother_test.C.

1113 {
1114 Mesh mesh(*TestCommWorld);
1115 VariationalMeshSmoother variational(mesh, 0.5, true, TOLERANCE * TOLERANCE, TOLERANCE * TOLERANCE, 100);
1116
1117 // High verbosity for a 3D mesh to increase code coverage, but silence the output
1118 // If we want to save the output for processing later, send it somewhere
1119 // besides nullptr
1120 std::streambuf * out_buf = libMesh::out.rdbuf(nullptr);
1121
1122 testVariationalSmoother(mesh, variational, QUAD4);
1123
1124 // Reset stdout
1125 libMesh::out.rdbuf(out_buf);
1126 }
streambufT * rdbuf() const
Get the associated stream buffer.
OStreamProxy out

References mesh, libMesh::out, libMesh::QUAD4, libMesh::BasicOStreamProxy< charT, traits >::rdbuf(), TestCommWorld, testVariationalSmoother(), and libMesh::TOLERANCE.

◆ testVariationalQuad4MultipleSubdomains()

void MeshSmootherTest::testVariationalQuad4MultipleSubdomains ( )
inline

Definition at line 1128 of file mesh_smoother_test.C.

1129 {
1130 Mesh mesh(*TestCommWorld);
1131 VariationalMeshSmoother variational(mesh);
1132
1133 testVariationalSmoother(mesh, variational, QUAD4, true);
1134 }

References mesh, libMesh::QUAD4, TestCommWorld, and testVariationalSmoother().

◆ testVariationalQuad4Tangled()

void MeshSmootherTest::testVariationalQuad4Tangled ( )
inline

Definition at line 1160 of file mesh_smoother_test.C.

1161 {
1162 Mesh mesh(*TestCommWorld);
1163 VariationalMeshSmoother variational(mesh);
1164
1165 testVariationalSmoother(mesh, variational, QUAD4, false, true, 0.65);
1166 }

References mesh, libMesh::QUAD4, TestCommWorld, and testVariationalSmoother().

◆ testVariationalQuad8()

void MeshSmootherTest::testVariationalQuad8 ( )
inline

Definition at line 1136 of file mesh_smoother_test.C.

1137 {
1138 Mesh mesh(*TestCommWorld);
1139 VariationalMeshSmoother variational(mesh);
1140
1141 testVariationalSmoother(mesh, variational, QUAD8);
1142 }

References mesh, libMesh::QUAD8, TestCommWorld, and testVariationalSmoother().

◆ testVariationalQuad9()

void MeshSmootherTest::testVariationalQuad9 ( )
inline

Definition at line 1144 of file mesh_smoother_test.C.

1145 {
1146 Mesh mesh(*TestCommWorld);
1147 VariationalMeshSmoother variational(mesh);
1148
1149 testVariationalSmoother(mesh, variational, QUAD9);
1150 }

References mesh, libMesh::QUAD9, TestCommWorld, and testVariationalSmoother().

◆ testVariationalQuad9MultipleSubdomains()

void MeshSmootherTest::testVariationalQuad9MultipleSubdomains ( )
inline

Definition at line 1152 of file mesh_smoother_test.C.

1153 {
1154 Mesh mesh(*TestCommWorld);
1155 VariationalMeshSmoother variational(mesh);
1156
1157 testVariationalSmoother(mesh, variational, QUAD9, true);
1158 }

References mesh, libMesh::QUAD9, TestCommWorld, and testVariationalSmoother().

◆ testVariationalQuadratureHourglass()

void MeshSmootherTest::testVariationalQuadratureHourglass ( )
inline

Definition at line 1389 of file mesh_smoother_test.C.

1390 {
1391 LOG_UNIT_TEST;
1392
1393 auto count_non_invertible = [](const MeshBase & m) {
1394 unsigned int n_bad = 0;
1395 for (const auto * elem : m.active_element_ptr_range())
1396 if (!elem->has_invertible_map())
1397 ++n_bad;
1398 return n_bad;
1399 };
1400
1401 // (1) Default Gauss rule: characterizes the bug. At least one element folds
1402 // into an hourglass and loses its invertible map. (If a future change to the
1403 // default smoother fixes this on its own, this assertion should be revisited.)
1404 {
1405 ReplicatedMesh mesh(*TestCommWorld);
1406 mesh.read("meshes/quad4_tri3_hourglass.xda.gz");
1407
1408 VariationalMeshSmoother smoother(mesh, 0.5, true);
1409 smoother.smooth();
1410
1411 CPPUNIT_ASSERT(count_non_invertible(mesh) > 0);
1412 }
1413
1414 // (2) Vertex-sampling rule (QNODAL): the metric now "sees" the corners, so
1415 // every element keeps an invertible map.
1416 {
1417 ReplicatedMesh mesh(*TestCommWorld);
1418 mesh.read("meshes/quad4_tri3_hourglass.xda.gz");
1419
1420 VariationalMeshSmoother smoother(mesh, 0.5, true);
1421 smoother.set_quadrature_type(QNODAL);
1422 smoother.smooth();
1423
1424 CPPUNIT_ASSERT_EQUAL(0u, count_non_invertible(mesh));
1425 }
1426 }
This is the MeshBase class.
Definition mesh_base.h:81

References mesh, libMesh::QNODAL, libMesh::MeshBase::read(), libMesh::VariationalMeshSmoother::set_quadrature_type(), libMesh::VariationalMeshSmoother::smooth(), and TestCommWorld.

◆ testVariationalSmoother()

void MeshSmootherTest::testVariationalSmoother ( Mesh &  mesh,
VariationalMeshSmoother &  smoother,
const ElemType  type,
const bool  multiple_subdomains = false,
const bool  tangle_mesh = false,
const Real  tangle_damping_factor = 1.0 
)
inline

Definition at line 404 of file mesh_smoother_test.C.

410 {
411 LOG_UNIT_TEST;
412
413 if (multiple_subdomains && tangle_mesh)
414 libmesh_not_implemented_msg(
415 "Arbitrary mesh tangling with multiple subdomains is not supported.");
416
417 // Get mesh dimension, determine whether type is triangular
418 const auto * ref_elem = &(ReferenceElem::get(type));
419 const auto dim = ref_elem->dim();
420 const bool type_is_tri = Utility::enum_to_string(type).compare(0, 3, "TRI") == 0;
421 const bool type_is_pyramid = Utility::enum_to_string(type).compare(0, 7, "PYRAMID") == 0;
422 const bool type_is_prism = Utility::enum_to_string(type).compare(0, 5, "PRISM") == 0;
423 const bool type_is_tet = Utility::enum_to_string(type).compare(0, 3, "TET") == 0;
424
425 // Used fewer elems for higher order types, as extra midpoint nodes will add
426 // enough complexity
427 unsigned int n_elems_per_side = 4 / Elem::type_to_default_order_map[type];
428 const auto side_length = 1.0 / n_elems_per_side;
429
430 switch (dim)
431 {
432 case 1:
433 MeshTools::Generation::build_line(mesh, n_elems_per_side, 0., 1., type);
434 break;
435 case 2:
437 mesh, n_elems_per_side, n_elems_per_side, 0., 1., 0., 1., type);
438 break;
439
440 case 3:
442 n_elems_per_side,
443 n_elems_per_side,
444 n_elems_per_side,
445 0.,
446 1.,
447 0.,
448 1.,
449 0.,
450 1.,
451 type);
452 break;
453
454 default:
455 libmesh_error_msg("Unsupported dimension " << dim);
456 }
457
458 // Move it around so we have something that needs smoothing
459 DistortHyperCube dh(dim);
461
462 const auto & boundary_info = mesh.get_boundary_info();
463 const auto & proc_id = mesh.processor_id();
464
465 if (tangle_mesh)
466 {
467 // We tangle the mesh by (partially) swapping the locations of 2 nodes.
468 // If the n-dimentional hypercube is represented as a grid with
469 // (undistorted) divisions occuring at integer numbers, we will
470 // (partially) swap the nodes nearest p1 = (1,1,1) and p2 = (2,1,2).
471
472 // Define p1 and p2 given the integer indices
473 // Undistorted elem side length
474 const Real dr = 1. / (n_elems_per_side * Elem::type_to_default_order_map[type]);
475 const Point p1 = Point(dr, dim > 1 ? dr : 0, dim > 2 ? dr : 0);
476 const Point p2 = Point(2. * dr, dim > 1 ? dr : 0, dim > 2 ? 2. * dr : 0);
477
478 // We need to find the nodes of the mesh that are closest to p1 and p2.
479 // This will require some parallel communication for distributed meshes.
480
481 // Distances between local mesh nodes and p1
482 std::map<dof_id_type, Real> dist1_map;
483 // Distances between local mesh nodes and p2
484 std::map<dof_id_type, Real> dist2_map;
485
486 for (const auto * node : mesh.local_node_ptr_range())
487 {
488 dist1_map[node->id()] = (*node - p1).norm();
489 dist2_map[node->id()] = (*node - p2).norm();
490 }
491
492 // Helper function to analyze a dist_map accross all processors and
493 // return the node id and spatial coordinates for the node with the
494 // smallest distance
495 auto get_closet_point_accross_all_procs =
496 [&mesh, &proc_id](const std::map<dof_id_type, Real> &dist_map) {
497
498 processor_id_type broadcasting_proc = 0;
499 Real d_min_local = std::numeric_limits<Real>::max();
501
502 // Only execute this if this proc owns any nodes. Otherwise,
503 // use default values defined above
504 if (dist_map.size())
505 {
506 // Iterator to the map entry with the smallest distance
507 auto min_it =
508 std::min_element(dist_map.begin(), dist_map.end(),
509 [](const auto &a, const auto &b) {
510 return a.second < b.second;
511 });
512
513 node_id = min_it->first;
514 d_min_local = min_it->second;
515 }
516
517 // Get the smallest distance accross all procs
518 auto d_min_global = d_min_local;
519 mesh.comm().min(d_min_global);
520
521 // Find the proc id, node_id, and spatial coordinantes for the
522 // node with the smallest distance
523 Point node;
524 if (d_min_local == d_min_global) {
525 broadcasting_proc = proc_id;
526 node = mesh.node_ref(node_id);
527 }
528
529 mesh.comm().max(broadcasting_proc);
530 // Broadcast information about this node to all procs
531 mesh.comm().broadcast(node_id, broadcasting_proc);
532 mesh.comm().broadcast(node, broadcasting_proc);
533
534 return std::pair(node_id, node);
535 };
536
537 const auto [node_id1, node1] =
538 get_closet_point_accross_all_procs(dist1_map);
539 const auto [node_id2, node2] =
540 get_closet_point_accross_all_procs(dist2_map);
541
542 // modify the nodes if they are on this proc
543 const auto displacement = tangle_damping_factor * (node1 - node2);
544 if (mesh.query_node_ptr(node_id1))
545 mesh.node_ref(node_id1) -= displacement;
546 if (mesh.query_node_ptr(node_id2))
547 mesh.node_ref(node_id2) += displacement;
548
549 SyncNodalPositions sync_object(mesh);
551 mesh.nodes_end(), sync_object);
552
553 // Make sure the mesh is tangled
554 // Need this to create the system we are about to access
555 smoother.setup();
556 const auto &unsmoothed_info = smoother.get_mesh_info();
557 CPPUNIT_ASSERT(unsmoothed_info.mesh_is_tangled);
558 }
559
560 // Add multiple subdomains if requested
561 std::unordered_map<subdomain_id_type, Real> distorted_subdomain_volumes;
562 subdomain_id_type highest_subdomain_id = 0;
563 if (multiple_subdomains)
564 {
565 // Modify the subdomain ids in an interesting way
566 for (auto * elem : mesh.active_element_ptr_range())
567 {
568 unsigned int subdomain_id = 0;
569 for (const auto d : make_range(dim))
570 if (elem->vertex_average()(d) > 0.5)
571 ++subdomain_id;
572 elem->subdomain_id() += subdomain_id;
573 }
574
575 // This loop should NOT be combined with the one above because we need to
576 // finish checking and updating subdomain ids for all elements before
577 // recording the final subdomain volumes.
578 for (auto *elem : mesh.active_element_ptr_range()) {
579 const auto sub_id = elem->subdomain_id();
580 // Don't double count an element's volume on multiple procs
581 if (elem->processor_id() != proc_id)
582 continue;
583 distorted_subdomain_volumes[sub_id] += elem->volume();
584 highest_subdomain_id = std::max(sub_id, highest_subdomain_id);
585 }
586
587 // Parallel communication
588 mesh.comm().max(highest_subdomain_id);
589 for (const auto sub_id : make_range(highest_subdomain_id + 1)) {
590 // Make sure sub_id exists in the map on this proc
591 if (distorted_subdomain_volumes.find(sub_id) ==
592 distorted_subdomain_volumes.end())
593 distorted_subdomain_volumes[sub_id] = 0.;
594
595 mesh.comm().sum(distorted_subdomain_volumes[sub_id]);
596 }
597 }
598
599 // We may have just invalidated the get_mesh_subdomains() cache by
600 // adding a new one, and libMesh marked our spatial_dimension()
601 // cache as invalidated in redistribute(). Just rebuild our
602 // caches before we start asking for anything like
603 // elem_default_orders().
605
606 // Get the mesh order
607 const auto & elem_orders = mesh.elem_default_orders();
608 libmesh_error_msg_if(elem_orders.size() != 1,
609 "The variational smoother cannot be used for mixed-order meshes!");
610 // For pyramids, the factor 2 accounts for the account that cubes of side
611 // length s are divided into pyramids of base side length s and height s/2.
612 // The factor 4 lets us catch triangular face midpoint nodes for PYRAMID18 elements.
613 // Similar reasoning for tets.
614 const auto scale_factor = *elem_orders.begin() * ((type_is_pyramid || type_is_tet) ? 2 * 4 : 1);
615
616 // Function to assert the node distortion is as expected
617 auto node_distortion_is = [&n_elems_per_side, &dim, &boundary_info, &scale_factor, &type_is_prism](
618 const Node & node, bool distortion, Real distortion_tol = TOLERANCE) {
619 // Get boundary ids associated with the node
620 std::vector<boundary_id_type> boundary_ids;
621 boundary_info.boundary_ids(&node, boundary_ids);
622
623 // This tells us what type of node we are: internal, sliding, or fixed
624 const auto num_dofs = dim - boundary_ids.size();
625 /*
626 * The following cases of num_dofs are possible, ASSUMING all boundaries
627 * are non-overlapping
628 * 3D: 3-0, 3-1, 3-2, 3-3
629 * = 3 2 1 0
630 * internal sliding, sliding, fixed
631 * 2D: 2-0, 2-1, 2-2
632 * = 2 1 0
633 * internal sliding, fixed
634 * 1D: 1-0, 1-1
635 * = 1 0
636 * internal fixed
637 *
638 * We expect that R is an integer in [0, n_elems_per_side] for
639 * num_dofs of the node's cooridinantes, while the remaining coordinates
640 * are fixed to the boundary with value 0 or 1. In other words, at LEAST
641 * dim - num_dofs coordinantes should be 0 or 1.
642 */
643
644 std::size_t num_zero_or_one = 0;
645
646 bool distorted = false;
647 for (const auto d : make_range(dim))
648 {
649 const Real r = node(d);
650 const Real R = r * n_elems_per_side * scale_factor;
651 CPPUNIT_ASSERT_GREATER(-distortion_tol * distortion_tol, r);
652 CPPUNIT_ASSERT_GREATER(-distortion_tol * distortion_tol, 1 - r);
653
654 bool d_distorted = std::abs(R - std::round(R)) > distortion_tol;
655 if (type_is_prism && (scale_factor == 3))
656 {
657 // Adjustment required for triangular face nodes of PRISM20/21 elements.
658 // These elements have fe_order 3. This takes care of nodes occuring at
659 // thirds, but not halves.
660 const Real R_prism = R / scale_factor * 2;
661 const bool d_distorted_prism =
662 std::abs(R_prism - std::round(R_prism)) > distortion_tol;
663 d_distorted &= d_distorted_prism;
664 }
665 distorted |= d_distorted;
666 num_zero_or_one += (absolute_fuzzy_equals(r, 0.) || absolute_fuzzy_equals(r, 1.));
667 }
668
669 CPPUNIT_ASSERT_GREATEREQUAL(dim - num_dofs, num_zero_or_one);
670
671 // We can never expect a fixed node to be distorted
672 if (num_dofs == 0)
673 // if (num_dofs < dim)
674 return true;
675 return distorted == distortion;
676 };
677
678 // Make sure our DistortHyperCube transformation has distorted the mesh
679 for (auto node : mesh.node_ptr_range())
680 CPPUNIT_ASSERT(node_distortion_is(*node, true));
681
682 // Transform the square mesh of triangles to a parallelogram mesh of
683 // triangles. This will allow the Variational Smoother to smooth the mesh
684 // to the optimal case of equilateral triangles
685 if (type_is_tri)
686 {
687 // Transform the square mesh of triangles to a parallelogram mesh of
688 // triangles. This will allow the Variational Smoother to smooth the mesh
689 // to the optimal case of equilateral triangles
690 SquareToParallelogram stp;
692 }
693 else if (type_is_prism)
694 {
695 // Transform the cube mesh of prisms to a parallelepiped mesh of
696 // prisms. This will allow the Variational Smoother to smooth the mesh
697 // to the optimal case of equilateral triangular prisms
698 CubeToParallelepiped ctp;
700 }
701
702 smoother.smooth();
703
704 if (type_is_tri)
705 {
706 // Transform the parallelogram mesh back to a square mesh. In the case of
707 // the Variational Smoother, equilateral triangules will be transformed
708 // into right triangules that align with the original undistorted mesh.
709 ParallelogramToSquare pts;
711 }
712 else if (type_is_prism)
713 {
714 // Transform the parallelepiped mesh back to a cube mesh. In the case of
715 // the Variational Smoother, equilateral triangular prisms will be transformed
716 // into right triangular prisms that align with the original undistorted mesh.
717 ParallelepipedToCube ptc;
719 }
720
721 if (multiple_subdomains)
722 {
723 // Make sure the subdomain volumes didn't change. Although this doesn't
724 // guarantee that the subdomain didn't change, it is a good indicator
725 // and is friedly to sliding subdomain boundary nodes.
726 std::unordered_map<subdomain_id_type, Real> smoothed_subdomain_volumes;
727 for (auto *elem : mesh.active_element_ptr_range()) {
728 // Don't double count an element's volume on multiple procs
729 if (elem->processor_id() != proc_id)
730 continue;
731 smoothed_subdomain_volumes[elem->subdomain_id()] += elem->volume();
732 }
733
734 // Parallel communication
735 for (const auto sub_id : make_range(highest_subdomain_id + 1)) {
736 // Make sure sub_id exists in the map on this proc
737 if (smoothed_subdomain_volumes.find(sub_id) ==
738 smoothed_subdomain_volumes.end())
739 smoothed_subdomain_volumes[sub_id] = 0.;
740
741 mesh.comm().sum(smoothed_subdomain_volumes[sub_id]);
742 }
743
744 for (const auto sub_id : make_range(highest_subdomain_id + 1))
745 CPPUNIT_ASSERT(relative_fuzzy_equals(
746 libmesh_map_find(distorted_subdomain_volumes, sub_id),
747 libmesh_map_find(smoothed_subdomain_volumes, sub_id), TOLERANCE));
748 }
749 else
750 {
751 std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
752 MeshTools::build_nodes_to_elem_map(mesh, nodes_to_elem_map);
753
754 // Make sure we're not too distorted anymore, using the given
755 // tolerance.
756 std::set<dof_id_type> nodes_checked;
757 const Real tol = TOLERANCE;
758
759 for (const auto * elem : mesh.active_element_ptr_range())
760 {
761 for (const auto local_node_id : make_range(elem->n_nodes()))
762 {
763 const auto & node = elem->node_ref(local_node_id);
764 if (nodes_checked.find(node.id()) != nodes_checked.end())
765 continue;
766
767 nodes_checked.insert(node.id());
768
769 // Check for special case where pyramidal base-to-apex midpoint
770 // nodes are not smoothed to the actual midpoints.
771 if (type_is_pyramid)
772 {
773 if (local_node_id > 8 && local_node_id < 13)
774 {
775 // Base-Apex midpoint nodes
776 //
777 // Due to the nature of the pyramidal target element and the
778 // cubic nature of the test mesh, for a dilation weight o
779 // 0.5, smoothed base-apex midpoint nodes are smoothed to
780 // the point base + x * (apex - base) instead of
781 // base + 0.5 (apex - base), where x is in (0,1) and
782 // depends on the number of nodes in the element.
783 // We hard-code this check below.
784
785 const auto & base = elem->node_ref(local_node_id - 9);
786 const auto & apex = elem->node_ref(4);
787 const Real x = (type == PYRAMID18) ? 0.56646084 : 0.54985875;
788
789 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(base + x * (apex - base), tol));
790 continue;
791 }
792 else if (local_node_id > 13)
793 {
794 // Triangular face midpoint nodes
795 //
796 // Due to the nature of the pyramidal target element and the
797 // cubic nature of the test mesh, for a dilation weight o
798 // 0.5, smoothed triangular face midpoint nodes are
799 // smoothed a weighted average of the constituent
800 // vertices instead of an unweighted average.
801 // We hard-code this check below.
802 const auto & base1 = elem->node_ref(local_node_id - 14);
803 const auto & base2 = elem->node_ref((local_node_id - 13) % 4);
804 const auto & apex = elem->node_ref(4);
805
806 const auto node_approx =
807 (0.31401599 * base1 + 0.31401599 * base2 + 0.37196802 * apex);
808 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(node_approx, tol));
809 continue;
810 }
811 }
812
813 // Check for special case where some tet midpoint nodes are not
814 // smoothed to the actual midpoints.
815 else if (type_is_tet && !elem->is_vertex(local_node_id))
816 {
817 // We have a non-vertex node. Determine what "type" of
818 // midpoint node with respect to the mesh geometry.
819 // First, get the nodes that neighbor this node
820 std::vector<const Node *> neighbors;
822 mesh, node, nodes_to_elem_map, neighbors);
823
824 switch (neighbors.size())
825 {
826 case 2: {
827 // Midpoint node
828 // Determine what "type" of midpoint node
829
830 // Is one of the neighbors at the center of a sub-cube?
831 const auto is_0_cube_center =
832 pointIsCubeCenter(*neighbors[0], side_length);
833 const auto is_1_cube_center =
834 pointIsCubeCenter(*neighbors[1], side_length);
835 libmesh_assert(!(is_0_cube_center && is_1_cube_center));
836
837 if (is_0_cube_center || is_1_cube_center)
838 {
839 const auto & cube_center =
840 is_0_cube_center ? *neighbors[0] : *neighbors[1];
841 const auto & other =
842 is_0_cube_center ? *neighbors[1] : *neighbors[0];
843
844 // Since one neighbor is a sub-cube center, the
845 // other will either be a sub-cube face center or
846 // a sub-cube vertex. Determine which one.
847 if (pointIsCubeFaceCenter(other, side_length))
848 {
849 const Real x = (type == TET10) ? 0.42895041 : 0.41486385;
850 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(
851 other + x * (cube_center - other), tol));
852 }
853
854 else if (pointIsCubeVertex(other, side_length))
855 {
856 const Real x = (type == TET10) ? 0.55388920 : 0.58093516;
857 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(
858 other + x * (cube_center - other), tol));
859 }
860 }
861
862 else
863 {
864 // The remaining possibilities are
865 // cube-vertex-to-cube-vertex midpoints and
866 // cube-vertex-to-cube-face-center midpoints.
867 const auto is_0_cube_vertex =
868 pointIsCubeVertex(*neighbors[0], side_length);
869 const auto is_1_cube_vertex =
870 pointIsCubeVertex(*neighbors[1], side_length);
871 const auto is_0_cube_face_center =
872 pointIsCubeFaceCenter(*neighbors[0], side_length);
873 const auto is_1_cube_face_center =
874 pointIsCubeFaceCenter(*neighbors[1], side_length);
875
876 if (is_0_cube_vertex && is_1_cube_vertex)
877 // Due to symmetry, this type of midpoint is
878 // the geometric midpoint. Let the
879 // node_distortion_is function check it
880 CPPUNIT_ASSERT(node_distortion_is(node, false));
881
882 else
883 {
884 libmesh_error_msg_if(
885 (is_0_cube_center || is_0_cube_face_center) &&
886 (is_1_cube_center || is_1_cube_face_center),
887 "We should never get here!");
888 const auto & cube_vertex =
889 is_0_cube_vertex ? *neighbors[0] : *neighbors[1];
890 const auto & cube_face_center =
891 is_0_cube_face_center ? *neighbors[0] : *neighbors[1];
892 const Real x = (type == TET10) ? 0.61299101 : 0.65125580;
893 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(
894 cube_vertex + x * (cube_face_center - cube_vertex), tol));
895 }
896 }
897
898 continue;
899 break;
900 }
901
902 case 6: {
903 // Face node
904 // Only keep the face vertex nodes
905 neighbors.erase(std::remove_if(neighbors.begin(),
906 neighbors.end(),
907 [&elem](const Node * n) {
908 return !elem->is_vertex(
909 elem->local_node(n->id()));
910 }),
911 neighbors.end());
912
913 // There are three types of faces:
914 //
915 // 1) Isoscelese triangle with vertices at two
916 // sub-cube vertices and the sub-cube center,
917 // 2) Isosceles triangle with vertices at two
918 // sub-cube vertices and a sub-cube face center,
919 // 3) Scalene triangle with vertices at a sub-cube
920 // center, a sub-cube vertex, and a sub-cube face
921 // center.
922 //
923 // Determine which kind of face this node belongs to based on the vertex
924 // neighbors
925
926 unsigned int num_vertices_at_cube_center = 0;
927 unsigned int num_vertices_at_cube_vertices = 0;
928 unsigned int num_vertices_at_cube_face_centers = 0;
929 for (const auto * neighbor : neighbors)
930 {
931 if (pointIsCubeCenter(*neighbor, side_length))
932 num_vertices_at_cube_center += 1;
933 else if (pointIsCubeVertex(*neighbor, side_length))
934 num_vertices_at_cube_vertices += 1;
935 else if (pointIsCubeFaceCenter(*neighbor, side_length))
936 num_vertices_at_cube_face_centers += 1;
937 }
938
939 // We will express the face node at a linear
940 // combination of the face vertices
941 Node node_approx = Node(0, 0, 0);
942
943 // Isosceles triangular face
944 if (num_vertices_at_cube_vertices == 2)
945 {
946 // Isosceles triangular face (type 1)
947 if (num_vertices_at_cube_center)
948 for (const auto * neighbor : neighbors)
949 {
950 Real weight;
951 if (pointIsCubeVertex(*neighbor, side_length))
952 weight = 0.30600747;
953 else if (pointIsCubeCenter(*neighbor, side_length))
954 weight = 0.38798506;
955 else
956 libmesh_error_msg("We should never get here!");
957
958 node_approx += weight * (*neighbor);
959 }
960
961 // Isosceles triangular face (type 2)
962 else if (num_vertices_at_cube_face_centers)
963 for (const auto * neighbor : neighbors)
964 {
965 Real weight;
966 if (pointIsCubeVertex(*neighbor, side_length))
967 weight = 0.28078090;
968 else if (pointIsCubeFaceCenter(*neighbor, side_length))
969 weight = 0.43843820;
970 else
971 libmesh_error_msg("We should never get here!");
972
973 node_approx += weight * (*neighbor);
974 }
975
976 else
977 libmesh_error_msg("We should never get here!");
978 }
979
980 // Scalene triangular face (type 3)
981 else if (num_vertices_at_cube_center && num_vertices_at_cube_vertices &&
982 num_vertices_at_cube_face_centers)
983 for (const auto * neighbor : neighbors)
984 {
985 Real weight;
986 if (pointIsCubeCenter(*neighbor, side_length))
987 weight = 0.33102438;
988 else if (pointIsCubeVertex(*neighbor, side_length))
989 weight = 0.27147230;
990 else if (pointIsCubeFaceCenter(*neighbor, side_length))
991 weight = 0.39750332;
992 else
993 libmesh_error_msg("We should never get here!");
994
995 node_approx += weight * (*neighbor);
996 }
997
998 else
999 libmesh_error_msg("We should never get here!");
1000
1001 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(node_approx, tol));
1002
1003 continue;
1004 break;
1005 }
1006 default: {
1007 libmesh_error_msg(node << " has unexpected number of neighbors ("
1008 << neighbors.size() << ")");
1009 break;
1010 }
1011 } // switch (neighbors.size())
1012 } // if (type_is_tet)
1013
1014 CPPUNIT_ASSERT(node_distortion_is(node, false));
1015
1016 }
1017 }
1018 }
1019 }
unsigned int dim
bool pointIsCubeFaceCenter(const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
bool pointIsCubeCenter(const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
bool pointIsCubeVertex(const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
void max(const T &r, T &o, Request &req) const
void min(const T &r, T &o, Request &req) const
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
static const Order type_to_default_order_map[INVALID_ELEM]
This array maps the integer representation of the ElemType enum to the default approximation order of...
Definition elem.h:1089
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:742
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual const Node * query_node_ptr(const dof_id_type i) const =0
const std::set< Order > & elem_default_orders() const
Definition mesh_base.h:2658
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:41
virtual void smooth() override
Redefinition of the smooth function from the base class.
const MeshQualityInfo & get_mesh_info() const
Getter for the _system's _mesh_info attribute.
virtual void setup()
Setup method that creates equation systems, system, and constraints, to be called just prior to smoot...
static const Real b
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.
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.
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
Definition mesh_tools.C:444
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type > > &nodes_to_elem_map)
After calling this function the input vector nodes_to_elem_map will contain the node to element conne...
Definition mesh_tools.C:456
void find_nodal_or_face_neighbors(const MeshBase &mesh, const Node &node, const std::unordered_map< dof_id_type, std::vector< const Elem * > > &nodes_to_elem_map, std::vector< const Node * > &neighbors)
Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to t...
void sync_dofobject_data_by_id(const Communicator &comm, const Iterator &range_begin, const Iterator &range_end, SyncFunctor &sync)
Request data about a range of ghost dofobjects uniquely identified by their id.
const Elem & get(const ElemType type_in)
LIBMESH_DEVICE_INLINE auto norm(const T &a)
std::string enum_to_string(const T e)
bool relative_fuzzy_equals(const T &var1, const T2 &var2, const Real tol=TOLERANCE *TOLERANCE)
Function to check whether two variables are equal within a relative tolerance.
bool absolute_fuzzy_equals(const T &var1, const T2 &var2, const Real tol=TOLERANCE *TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
libmesh_assert(ctx)
uint8_t dof_id_type
Definition id_types.h:67
uint8_t processor_id_type
Definition id_types.h:104
const dof_id_type n_nodes
Definition tecplot_io.C:67

References libMesh::absolute_fuzzy_equals(), b, libMesh::Parallel::Communicator::broadcast(), libMesh::MeshTools::Generation::build_cube(), libMesh::MeshTools::Generation::build_line(), libMesh::MeshTools::build_nodes_to_elem_map(), libMesh::MeshTools::Generation::build_square(), libMesh::MeshBase::cache_elem_data(), libMesh::ParallelObject::comm(), dim, libMesh::MeshBase::elem_default_orders(), libMesh::Utility::enum_to_string(), libMesh::MeshTools::find_nodal_or_face_neighbors(), libMesh::ReferenceElem::get(), libMesh::MeshBase::get_boundary_info(), libMesh::VariationalMeshSmoother::get_mesh_info(), libMesh::DofObject::invalid_id, libMesh::libmesh_assert(), libMesh::make_range(), libMesh::Parallel::Communicator::max(), mesh, libMesh::Parallel::Communicator::min(), libMesh::MeshBase::node_ref(), pointIsCubeCenter(), pointIsCubeFaceCenter(), pointIsCubeVertex(), libMesh::ParallelObject::processor_id(), libMesh::PYRAMID18, libMesh::MeshBase::query_node_ptr(), libMesh::Real, libMesh::MeshTools::Modification::redistribute(), libMesh::relative_fuzzy_equals(), libMesh::VariationalMeshSmoother::setup(), libMesh::VariationalMeshSmoother::smooth(), libMesh::Parallel::Communicator::sum(), libMesh::Parallel::sync_dofobject_data_by_id(), libMesh::TET10, libMesh::TOLERANCE, and libMesh::Elem::type_to_default_order_map.

Referenced by testVariationalEdge2(), testVariationalEdge3(), testVariationalEdge3MultipleSubdomains(), testVariationalHex20(), testVariationalHex27(), testVariationalHex27MultipleSubdomains(), testVariationalHex27Tangled(), testVariationalHex8(), testVariationalPrism15(), testVariationalPrism18(), testVariationalPrism20(), testVariationalPrism21(), testVariationalPrism21MultipleSubdomains(), testVariationalPrism6(), testVariationalPyramid13(), testVariationalPyramid14(), testVariationalPyramid18(), testVariationalPyramid18MultipleSubdomains(), testVariationalPyramid5(), testVariationalQuad4(), testVariationalQuad4MultipleSubdomains(), testVariationalQuad4Tangled(), testVariationalQuad8(), testVariationalQuad9(), testVariationalQuad9MultipleSubdomains(), testVariationalTet10(), testVariationalTet14(), testVariationalTet14MultipleSubdomains(), testVariationalTet4(), testVariationalTri3(), testVariationalTri6(), and testVariationalTri6MultipleSubdomains().

◆ testVariationalSmootherRegression()

void MeshSmootherTest::testVariationalSmootherRegression ( const ReplicatedMesh &  gold_mesh)
inline

Definition at line 1023 of file mesh_smoother_test.C.

1024 {
1025 // Make a copy of the gold mesh to distort and then smooth
1026 ReplicatedMesh mesh(gold_mesh);
1027
1028 // Move it around so we have something that needs smoothing
1029 DistortHyperCube dh(mesh.mesh_dimension());
1031
1032 // Function to assert the distortion is as expected
1033 auto mesh_distortion_is = [](const ReplicatedMesh & gold_mesh,
1034 const ReplicatedMesh & mesh,
1035 const bool distortion,
1036 Real distortion_tol = TOLERANCE) {
1037
1038 CPPUNIT_ASSERT(gold_mesh.n_nodes() == mesh.n_nodes());
1039 for (const auto node_id : make_range(gold_mesh.n_nodes()))
1040 {
1041 const auto & gold_node = gold_mesh.node_ref(node_id);
1042 const auto & node = mesh.node_ref(node_id);
1043 for (const auto d : make_range(gold_mesh.mesh_dimension()))
1044 {
1045 const bool d_distorted = std::abs(gold_node(d) - node(d)) > distortion_tol;
1046 if (d_distorted)
1047 // mesh is distorted from gold_mesh
1048 return distortion;
1049 }
1050 }
1051
1052 // mesh is not distorted from gold_mesh
1053 return !distortion;
1054 };
1055
1056 // Make sure the mesh has been distorted
1057 CPPUNIT_ASSERT(mesh_distortion_is(gold_mesh, mesh, true));
1058
1059 // Turn off subdomain boundary preservation because DistortHyperCube
1060 // does not preserve subdomain boundaries
1061 // Also set dilation coefficient to 0 because the reference volume of the
1062 // distorted mesh won't be equal to the reference volume of the smoothed
1063 // mesh and will smooth to a slightly different solution.
1064 VariationalMeshSmoother smoother(mesh, 0.0, false);
1065 smoother.smooth();
1066
1067 // Make sure the mesh has been smoothed to the gold mesh
1068 CPPUNIT_ASSERT(mesh_distortion_is(gold_mesh, mesh, false));
1069 }
unsigned int mesh_dimension() const
Definition mesh_base.C:435
virtual dof_id_type n_nodes() const =0
virtual dof_id_type n_nodes() const override final

References libMesh::make_range(), mesh, libMesh::MeshBase::mesh_dimension(), libMesh::MeshBase::n_nodes(), libMesh::ReplicatedMesh::n_nodes(), libMesh::MeshBase::node_ref(), libMesh::Real, libMesh::MeshTools::Modification::redistribute(), libMesh::VariationalMeshSmoother::smooth(), and libMesh::TOLERANCE.

Referenced by testVariationalMixed2D(), and testVariationalMixed3D().

◆ testVariationalTet10()

void MeshSmootherTest::testVariationalTet10 ( )
inline

Definition at line 1340 of file mesh_smoother_test.C.

1341 {
1342 Mesh mesh(*TestCommWorld);
1343 VariationalMeshSmoother variational(mesh);
1344
1345 testVariationalSmoother(mesh, variational, TET10);
1346 }

References mesh, TestCommWorld, testVariationalSmoother(), and libMesh::TET10.

◆ testVariationalTet14()

void MeshSmootherTest::testVariationalTet14 ( )
inline

Definition at line 1348 of file mesh_smoother_test.C.

1349 {
1350 Mesh mesh(*TestCommWorld);
1351 VariationalMeshSmoother variational(mesh);
1352
1353 testVariationalSmoother(mesh, variational, TET14);
1354 }

References mesh, TestCommWorld, testVariationalSmoother(), and libMesh::TET14.

◆ testVariationalTet14MultipleSubdomains()

void MeshSmootherTest::testVariationalTet14MultipleSubdomains ( )
inline

Definition at line 1356 of file mesh_smoother_test.C.

1357 {
1358 Mesh mesh(*TestCommWorld);
1359 VariationalMeshSmoother variational(mesh);
1360
1361 testVariationalSmoother(mesh, variational, TET14, true);
1362 }

References mesh, TestCommWorld, testVariationalSmoother(), and libMesh::TET14.

◆ testVariationalTet4()

void MeshSmootherTest::testVariationalTet4 ( )
inline

Definition at line 1324 of file mesh_smoother_test.C.

1325 {
1326 Mesh mesh(*TestCommWorld);
1327 VariationalMeshSmoother variational(mesh, 0.5, true, TOLERANCE * TOLERANCE, TOLERANCE * TOLERANCE, 100);
1328
1329 // High verbosity for a 3D mesh to increase code coverage, but silence the output
1330 // If we want to save the output for processing later, send it somewhere
1331 // besides nullptr
1332 std::streambuf * out_buf = libMesh::out.rdbuf(nullptr);
1333
1334 testVariationalSmoother(mesh, variational, TET4);
1335
1336 // Reset stdout
1337 libMesh::out.rdbuf(out_buf);
1338 }

References mesh, libMesh::out, libMesh::BasicOStreamProxy< charT, traits >::rdbuf(), TestCommWorld, testVariationalSmoother(), libMesh::TET4, and libMesh::TOLERANCE.

◆ testVariationalTri3()

void MeshSmootherTest::testVariationalTri3 ( )
inline

Definition at line 1168 of file mesh_smoother_test.C.

1169 {
1170 Mesh mesh(*TestCommWorld);
1171 VariationalMeshSmoother variational(mesh);
1172
1173 testVariationalSmoother(mesh, variational, TRI3);
1174 }

References mesh, TestCommWorld, testVariationalSmoother(), and libMesh::TRI3.

◆ testVariationalTri6()

void MeshSmootherTest::testVariationalTri6 ( )
inline

Definition at line 1176 of file mesh_smoother_test.C.

1177 {
1178 Mesh mesh(*TestCommWorld);
1179 VariationalMeshSmoother variational(mesh);
1180
1181 testVariationalSmoother(mesh, variational, TRI6);
1182 }

References mesh, TestCommWorld, testVariationalSmoother(), and libMesh::TRI6.

◆ testVariationalTri6MultipleSubdomains()

void MeshSmootherTest::testVariationalTri6MultipleSubdomains ( )
inline

Definition at line 1184 of file mesh_smoother_test.C.

1185 {
1186 Mesh mesh(*TestCommWorld);
1187 VariationalMeshSmoother variational(mesh);
1188
1189 testVariationalSmoother(mesh, variational, TRI3, true);
1190 }

References mesh, TestCommWorld, testVariationalSmoother(), and libMesh::TRI3.


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