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_SUITE_END ()
 
void setUp ()
 
void tearDown ()
 
void testLaplaceSmoother (ReplicatedMesh &mesh, MeshSmoother &smoother, ElemType type)
 
std::pair< unsigned int, unsigned intnumCenteredAndFacedDimensions (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 testVariationalMixed3D ()
 

Detailed Description

Definition at line 221 of file mesh_smoother_test.C.

Member Function Documentation

◆ CPPUNIT_TEST() [1/36]

MeshSmootherTest::CPPUNIT_TEST ( testLaplaceQuad  )

◆ CPPUNIT_TEST() [2/36]

MeshSmootherTest::CPPUNIT_TEST ( testLaplaceTri  )

◆ CPPUNIT_TEST() [3/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalEdge2  )

◆ CPPUNIT_TEST() [4/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalEdge3  )

◆ CPPUNIT_TEST() [5/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalEdge3MultipleSubdomains  )

◆ CPPUNIT_TEST() [6/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex20  )

◆ CPPUNIT_TEST() [7/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex27  )

◆ CPPUNIT_TEST() [8/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex27MultipleSubdomains  )

◆ CPPUNIT_TEST() [9/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex27Tangled  )

◆ CPPUNIT_TEST() [10/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalHex8  )

◆ CPPUNIT_TEST() [11/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalMixed2D  )

◆ CPPUNIT_TEST() [12/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalMixed3D  )

◆ CPPUNIT_TEST() [13/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism15  )

◆ CPPUNIT_TEST() [14/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism18  )

◆ CPPUNIT_TEST() [15/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism20  )

◆ CPPUNIT_TEST() [16/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism21  )

◆ CPPUNIT_TEST() [17/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism21MultipleSubdomains  )

◆ CPPUNIT_TEST() [18/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPrism6  )

◆ CPPUNIT_TEST() [19/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid13  )

◆ CPPUNIT_TEST() [20/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid14  )

◆ CPPUNIT_TEST() [21/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid18  )

◆ CPPUNIT_TEST() [22/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid18MultipleSubdomains  )

◆ CPPUNIT_TEST() [23/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalPyramid5  )

◆ CPPUNIT_TEST() [24/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad4  )

◆ CPPUNIT_TEST() [25/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad4MultipleSubdomains  )

◆ CPPUNIT_TEST() [26/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad4Tangled  )

◆ CPPUNIT_TEST() [27/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad8  )

◆ CPPUNIT_TEST() [28/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad9  )

◆ CPPUNIT_TEST() [29/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalQuad9MultipleSubdomains  )

◆ CPPUNIT_TEST() [30/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTet10  )

◆ CPPUNIT_TEST() [31/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTet14  )

◆ CPPUNIT_TEST() [32/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTet14MultipleSubdomains  )

◆ CPPUNIT_TEST() [33/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTet4  )

◆ CPPUNIT_TEST() [34/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTri3  )

◆ CPPUNIT_TEST() [35/36]

MeshSmootherTest::CPPUNIT_TEST ( testVariationalTri6  )

◆ CPPUNIT_TEST() [36/36]

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 357 of file mesh_smoother_test.C.

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

383 {
384 return numCenteredAndFacedDimensions(point, side_length, tol).first == 3;
385 }
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 396 of file mesh_smoother_test.C.

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

References numCenteredAndFacedDimensions().

Referenced by testVariationalSmoother().

◆ pointIsCubeVertex()

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

Definition at line 389 of file mesh_smoother_test.C.

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

References numCenteredAndFacedDimensions().

Referenced by testVariationalSmoother().

◆ setUp()

void MeshSmootherTest::setUp ( )
inline

Definition at line 284 of file mesh_smoother_test.C.

284{}

◆ tearDown()

void MeshSmootherTest::tearDown ( )
inline

Definition at line 286 of file mesh_smoother_test.C.

286{}

◆ testLaplaceQuad()

void MeshSmootherTest::testLaplaceQuad ( )
inline

Definition at line 1066 of file mesh_smoother_test.C.

1067 {
1068 ReplicatedMesh mesh(*TestCommWorld);
1069 LaplaceMeshSmoother laplace(mesh, 8);
1070
1071 testLaplaceSmoother(mesh, laplace, QUAD4);
1072 }
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 288 of file mesh_smoother_test.C.

289 {
290 LOG_UNIT_TEST;
291
292 constexpr unsigned int n_elems_per_side = 4;
293
295 mesh, n_elems_per_side, n_elems_per_side, 0., 1., 0., 1., type);
296
297 // Move it around so we have something that needs smoothing
298 DistortSquare ds;
300
301 // Assert the distortion is as expected
302 auto center_distortion_is =
303 [](const Node & node, int d, bool distortion, Real distortion_tol = TOLERANCE) {
304 const Real r = node(d);
305 const Real R = r * n_elems_per_side;
306 CPPUNIT_ASSERT_GREATER(-TOLERANCE * TOLERANCE, r);
307 CPPUNIT_ASSERT_GREATER(-TOLERANCE * TOLERANCE, 1 - r);
308
309 // If we're at the boundaries we should *never* be distorted
310 if (std::abs(node(0)) < TOLERANCE * TOLERANCE ||
311 std::abs(node(0) - 1) < TOLERANCE * TOLERANCE)
312 {
313 const Real R1 = node(1) * n_elems_per_side;
314 CPPUNIT_ASSERT_LESS(TOLERANCE * TOLERANCE, std::abs(R1 - std::round(R1)));
315 return true;
316 }
317
318 if (std::abs(node(1)) < TOLERANCE * TOLERANCE ||
319 std::abs(node(1) - 1) < TOLERANCE * TOLERANCE)
320 {
321 const Real R0 = node(0) * n_elems_per_side;
322 CPPUNIT_ASSERT_LESS(TOLERANCE * TOLERANCE, std::abs(R0 - std::round(R0)));
323
324 return true;
325 }
326
327 // If we're at the center we're fine
328 if (std::abs(r - 0.5) < TOLERANCE * TOLERANCE)
329 return true;
330
331 return ((std::abs(R - std::round(R)) > distortion_tol) == distortion);
332 };
333
334 for (auto node : mesh.node_ptr_range())
335 {
336 CPPUNIT_ASSERT(center_distortion_is(*node, 0, true));
337 CPPUNIT_ASSERT(center_distortion_is(*node, 1, true));
338 }
339
340 // Enough iterations to mostly fix us up. Laplace seems to be at 1e-3
341 // tolerance by iteration 6, so hopefully everything is there on any
342 // system by 8.
343 for (unsigned int i = 0; i != 8; ++i)
344 smoother.smooth();
345
346 // Make sure we're not too distorted anymore.
347 for (auto node : mesh.node_ptr_range())
348 {
349 CPPUNIT_ASSERT(center_distortion_is(*node, 0, false, 1e-3));
350 CPPUNIT_ASSERT(center_distortion_is(*node, 1, false, 1e-3));
351 }
352 }
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 1074 of file mesh_smoother_test.C.

1075 {
1076 ReplicatedMesh mesh(*TestCommWorld);
1077 LaplaceMeshSmoother laplace(mesh, 8);
1078
1079 testLaplaceSmoother(mesh, laplace, TRI3);
1080 }

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

◆ testVariationalEdge2()

void MeshSmootherTest::testVariationalEdge2 ( )
inline

Definition at line 1083 of file mesh_smoother_test.C.

1084 {
1085 Mesh mesh(*TestCommWorld);
1086 VariationalMeshSmoother variational(mesh);
1087
1088 testVariationalSmoother(mesh, variational, EDGE2);
1089 }
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 1091 of file mesh_smoother_test.C.

1092 {
1093 Mesh mesh(*TestCommWorld);
1094 VariationalMeshSmoother variational(mesh);
1095
1096 testVariationalSmoother(mesh, variational, EDGE3);
1097 }

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

◆ testVariationalEdge3MultipleSubdomains()

void MeshSmootherTest::testVariationalEdge3MultipleSubdomains ( )
inline

Definition at line 1099 of file mesh_smoother_test.C.

1100 {
1101 Mesh mesh(*TestCommWorld);
1102 VariationalMeshSmoother variational(mesh);
1103
1104 testVariationalSmoother(mesh, variational, EDGE3, true);
1105 }

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

◆ testVariationalHex20()

void MeshSmootherTest::testVariationalHex20 ( )
inline

Definition at line 1195 of file mesh_smoother_test.C.

1196 {
1197 Mesh mesh(*TestCommWorld);
1198 VariationalMeshSmoother variational(mesh);
1199
1200 testVariationalSmoother(mesh, variational, HEX20);
1201 }

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

◆ testVariationalHex27()

void MeshSmootherTest::testVariationalHex27 ( )
inline

Definition at line 1203 of file mesh_smoother_test.C.

1204 {
1205 Mesh mesh(*TestCommWorld);
1206 VariationalMeshSmoother variational(mesh);
1207
1208 testVariationalSmoother(mesh, variational, HEX27);
1209 }

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

◆ testVariationalHex27MultipleSubdomains()

void MeshSmootherTest::testVariationalHex27MultipleSubdomains ( )
inline

Definition at line 1211 of file mesh_smoother_test.C.

1212 {
1213 Mesh mesh(*TestCommWorld);
1214 VariationalMeshSmoother variational(mesh);
1215
1216 testVariationalSmoother(mesh, variational, HEX27, true);
1217 }

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

◆ testVariationalHex27Tangled()

void MeshSmootherTest::testVariationalHex27Tangled ( )
inline

Definition at line 1219 of file mesh_smoother_test.C.

1220 {
1221 Mesh mesh(*TestCommWorld);
1222 VariationalMeshSmoother variational(mesh);
1223
1224 testVariationalSmoother(mesh, variational, HEX27, false, true, 0.55);
1225 }

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

◆ testVariationalHex8()

void MeshSmootherTest::testVariationalHex8 ( )
inline

Definition at line 1187 of file mesh_smoother_test.C.

1188 {
1189 Mesh mesh(*TestCommWorld);
1190 VariationalMeshSmoother variational(mesh);
1191
1192 testVariationalSmoother(mesh, variational, HEX8);
1193 }

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

◆ testVariationalMixed2D()

void MeshSmootherTest::testVariationalMixed2D ( )
inline

Definition at line 1359 of file mesh_smoother_test.C.

1360 {
1361 ReplicatedMesh mesh(*TestCommWorld);
1362 mesh.read("meshes/quad4_tri3_smoothed.xda.gz");
1363
1365 }
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 1367 of file mesh_smoother_test.C.

1368 {
1369 ReplicatedMesh mesh(*TestCommWorld);
1370 mesh.read("meshes/hex8_prism6_smoothed.xda.gz");
1371
1373 }

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

◆ testVariationalPrism15()

void MeshSmootherTest::testVariationalPrism15 ( )
inline

Definition at line 1275 of file mesh_smoother_test.C.

1276 {
1277 Mesh mesh(*TestCommWorld);
1278 VariationalMeshSmoother variational(mesh);
1279
1280 testVariationalSmoother(mesh, variational, PRISM15);
1281 }

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

◆ testVariationalPrism18()

void MeshSmootherTest::testVariationalPrism18 ( )
inline

Definition at line 1283 of file mesh_smoother_test.C.

1284 {
1285 Mesh mesh(*TestCommWorld);
1286 VariationalMeshSmoother variational(
1287 mesh, 0.5, true, TOLERANCE * TOLERANCE, 10 * TOLERANCE * TOLERANCE);
1288
1289 testVariationalSmoother(mesh, variational, PRISM18);
1290 }

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

◆ testVariationalPrism20()

void MeshSmootherTest::testVariationalPrism20 ( )
inline

Definition at line 1292 of file mesh_smoother_test.C.

1293 {
1294 Mesh mesh(*TestCommWorld);
1295 VariationalMeshSmoother variational(
1296 mesh, 0.5, true, TOLERANCE * TOLERANCE, 10 * TOLERANCE * TOLERANCE);
1297
1298 testVariationalSmoother(mesh, variational, PRISM20);
1299 }

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

◆ testVariationalPrism21()

void MeshSmootherTest::testVariationalPrism21 ( )
inline

Definition at line 1301 of file mesh_smoother_test.C.

1302 {
1303 Mesh mesh(*TestCommWorld);
1304 VariationalMeshSmoother variational(
1305 mesh, 0.5, true, TOLERANCE * TOLERANCE, 10 * TOLERANCE * TOLERANCE);
1306
1307 testVariationalSmoother(mesh, variational, PRISM21);
1308 }

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

◆ testVariationalPrism21MultipleSubdomains()

void MeshSmootherTest::testVariationalPrism21MultipleSubdomains ( )
inline

Definition at line 1310 of file mesh_smoother_test.C.

1311 {
1312 Mesh mesh(*TestCommWorld);
1313 mesh.allow_renumbering(false);
1314 VariationalMeshSmoother variational(mesh);
1315
1316 testVariationalSmoother(mesh, variational, PRISM21, true);
1317 }
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:1355

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

◆ testVariationalPrism6()

void MeshSmootherTest::testVariationalPrism6 ( )
inline

Definition at line 1267 of file mesh_smoother_test.C.

1268 {
1269 Mesh mesh(*TestCommWorld);
1270 VariationalMeshSmoother variational(mesh);
1271
1272 testVariationalSmoother(mesh, variational, PRISM6);
1273 }

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

◆ testVariationalPyramid13()

void MeshSmootherTest::testVariationalPyramid13 ( )
inline

Definition at line 1235 of file mesh_smoother_test.C.

1236 {
1237 Mesh mesh(*TestCommWorld);
1238 VariationalMeshSmoother variational(mesh);
1239
1240 testVariationalSmoother(mesh, variational, PYRAMID13);
1241 }

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

◆ testVariationalPyramid14()

void MeshSmootherTest::testVariationalPyramid14 ( )
inline

Definition at line 1243 of file mesh_smoother_test.C.

1244 {
1245 Mesh mesh(*TestCommWorld);
1246 VariationalMeshSmoother variational(mesh);
1247
1248 testVariationalSmoother(mesh, variational, PYRAMID14);
1249 }

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

◆ testVariationalPyramid18()

void MeshSmootherTest::testVariationalPyramid18 ( )
inline

Definition at line 1251 of file mesh_smoother_test.C.

1252 {
1253 Mesh mesh(*TestCommWorld);
1254 VariationalMeshSmoother variational(mesh);
1255
1256 testVariationalSmoother(mesh, variational, PYRAMID18);
1257 }

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

◆ testVariationalPyramid18MultipleSubdomains()

void MeshSmootherTest::testVariationalPyramid18MultipleSubdomains ( )
inline

Definition at line 1259 of file mesh_smoother_test.C.

1260 {
1261 Mesh mesh(*TestCommWorld);
1262 VariationalMeshSmoother variational(mesh, 0.0);
1263
1264 testVariationalSmoother(mesh, variational, PYRAMID18, true);
1265 }

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

◆ testVariationalPyramid5()

void MeshSmootherTest::testVariationalPyramid5 ( )
inline

Definition at line 1227 of file mesh_smoother_test.C.

1228 {
1229 Mesh mesh(*TestCommWorld);
1230 VariationalMeshSmoother variational(mesh);
1231
1232 testVariationalSmoother(mesh, variational, PYRAMID5);
1233 }

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

◆ testVariationalQuad4()

void MeshSmootherTest::testVariationalQuad4 ( )
inline

Definition at line 1107 of file mesh_smoother_test.C.

1108 {
1109 Mesh mesh(*TestCommWorld);
1110 VariationalMeshSmoother variational(mesh, 0.5, true, TOLERANCE * TOLERANCE, TOLERANCE * TOLERANCE, 100);
1111
1112 // High verbosity for a 3D mesh to increase code coverage, but silence the output
1113 // If we want to save the output for processing later, send it somewhere
1114 // besides nullptr
1115 std::streambuf * out_buf = libMesh::out.rdbuf(nullptr);
1116
1117 testVariationalSmoother(mesh, variational, QUAD4);
1118
1119 // Reset stdout
1120 libMesh::out.rdbuf(out_buf);
1121 }
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 1123 of file mesh_smoother_test.C.

1124 {
1125 Mesh mesh(*TestCommWorld);
1126 VariationalMeshSmoother variational(mesh);
1127
1128 testVariationalSmoother(mesh, variational, QUAD4, true);
1129 }

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

◆ testVariationalQuad4Tangled()

void MeshSmootherTest::testVariationalQuad4Tangled ( )
inline

Definition at line 1155 of file mesh_smoother_test.C.

1156 {
1157 Mesh mesh(*TestCommWorld);
1158 VariationalMeshSmoother variational(mesh);
1159
1160 testVariationalSmoother(mesh, variational, QUAD4, false, true, 0.65);
1161 }

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

◆ testVariationalQuad8()

void MeshSmootherTest::testVariationalQuad8 ( )
inline

Definition at line 1131 of file mesh_smoother_test.C.

1132 {
1133 Mesh mesh(*TestCommWorld);
1134 VariationalMeshSmoother variational(mesh);
1135
1136 testVariationalSmoother(mesh, variational, QUAD8);
1137 }

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

◆ testVariationalQuad9()

void MeshSmootherTest::testVariationalQuad9 ( )
inline

Definition at line 1139 of file mesh_smoother_test.C.

1140 {
1141 Mesh mesh(*TestCommWorld);
1142 VariationalMeshSmoother variational(mesh);
1143
1144 testVariationalSmoother(mesh, variational, QUAD9);
1145 }

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

◆ testVariationalQuad9MultipleSubdomains()

void MeshSmootherTest::testVariationalQuad9MultipleSubdomains ( )
inline

Definition at line 1147 of file mesh_smoother_test.C.

1148 {
1149 Mesh mesh(*TestCommWorld);
1150 VariationalMeshSmoother variational(mesh);
1151
1152 testVariationalSmoother(mesh, variational, QUAD9, true);
1153 }

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

◆ 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 402 of file mesh_smoother_test.C.

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

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

1336 {
1337 Mesh mesh(*TestCommWorld);
1338 VariationalMeshSmoother variational(mesh);
1339
1340 testVariationalSmoother(mesh, variational, TET10);
1341 }

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

◆ testVariationalTet14()

void MeshSmootherTest::testVariationalTet14 ( )
inline

Definition at line 1343 of file mesh_smoother_test.C.

1344 {
1345 Mesh mesh(*TestCommWorld);
1346 VariationalMeshSmoother variational(mesh);
1347
1348 testVariationalSmoother(mesh, variational, TET14);
1349 }

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

◆ testVariationalTet14MultipleSubdomains()

void MeshSmootherTest::testVariationalTet14MultipleSubdomains ( )
inline

Definition at line 1351 of file mesh_smoother_test.C.

1352 {
1353 Mesh mesh(*TestCommWorld);
1354 VariationalMeshSmoother variational(mesh);
1355
1356 testVariationalSmoother(mesh, variational, TET14, true);
1357 }

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

◆ testVariationalTet4()

void MeshSmootherTest::testVariationalTet4 ( )
inline

Definition at line 1319 of file mesh_smoother_test.C.

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

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

◆ testVariationalTri3()

void MeshSmootherTest::testVariationalTri3 ( )
inline

Definition at line 1163 of file mesh_smoother_test.C.

1164 {
1165 Mesh mesh(*TestCommWorld);
1166 VariationalMeshSmoother variational(mesh);
1167
1168 testVariationalSmoother(mesh, variational, TRI3);
1169 }

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

◆ testVariationalTri6()

void MeshSmootherTest::testVariationalTri6 ( )
inline

Definition at line 1171 of file mesh_smoother_test.C.

1172 {
1173 Mesh mesh(*TestCommWorld);
1174 VariationalMeshSmoother variational(mesh);
1175
1176 testVariationalSmoother(mesh, variational, TRI6);
1177 }

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

◆ testVariationalTri6MultipleSubdomains()

void MeshSmootherTest::testVariationalTri6MultipleSubdomains ( )
inline

Definition at line 1179 of file mesh_smoother_test.C.

1180 {
1181 Mesh mesh(*TestCommWorld);
1182 VariationalMeshSmoother variational(mesh);
1183
1184 testVariationalSmoother(mesh, variational, TRI3, true);
1185 }

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


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