1 #include <libmesh/boundary_info.h> 2 #include <libmesh/elem.h> 3 #include <libmesh/enum_elem_type.h> 4 #include <libmesh/function_base.h> 5 #include <libmesh/libmesh.h> 6 #include <libmesh/mesh_generation.h> 7 #include <libmesh/mesh_modification.h> 8 #include <libmesh/mesh_smoother_laplace.h> 9 #include <libmesh/mesh_smoother_vsmoother.h> 10 #include <libmesh/mesh_tools.h> 11 #include <libmesh/node.h> 12 #include <libmesh/reference_elem.h> 13 #include <libmesh/replicated_mesh.h> 14 #include <libmesh/system.h> 15 #include "libmesh/face_tri.h" 16 #include "libmesh/utility.h" 17 #include "libmesh/enum_to_string.h" 31 std::unique_ptr<FunctionBase<Real>> clone()
const override 33 return std::make_unique<DistortSquare>();
38 libmesh_not_implemented();
45 const Real eta = 2 * p(0) - 1;
46 const Real zeta = 2 * p(1) - 1;
47 output(0) = p(0) + (pow<3>(eta) - eta) * p(1) * (1 - p(1));
48 output(1) = p(1) + (pow<3>(zeta) - zeta) * p(0) * (1 - p(0));
58 DistortHyperCube(
const unsigned int dim) : _dim(
dim) {}
61 std::unique_ptr<FunctionBase<Real>> clone()
const override 63 return std::make_unique<DistortHyperCube>(_dim);
66 Real operator()(
const Point &,
const Real = 0.)
override { libmesh_not_implemented(); }
74 unsigned int boundary_dims = 0;
75 std::array<bool, 3> is_on_boundary = {
false,
false,
false};
76 for (
unsigned int i = 0; i < _dim; ++i)
81 is_on_boundary[i] =
true;
86 if (boundary_dims == _dim)
88 for (
unsigned int i = 0; i < _dim; ++i)
94 for (
unsigned int i = 0; i < _dim; ++i)
96 if (!is_on_boundary[i])
98 Real xi = 2. * p(i) - 1.;
100 Real modulation = 0.3;
101 for (
unsigned int j = 0; j < _dim; ++j)
105 Real pj = std::clamp(p(j), 0., 1.);
106 modulation *= (pj - 0.5) * (pj - 0.5) * 4.;
109 const auto delta = (pow<3>(xi) - xi) * modulation;
111 output(i) = (delta >
TOLERANCE) ? p(i) + delta : 1.05 * p(i);
118 const unsigned int _dim;
123 std::unique_ptr<FunctionBase<Real>> clone()
const override 125 return std::make_unique<SquareToParallelogram>();
130 libmesh_not_implemented();
139 output(0) = p(0) - 0.5 * p(1);
140 output(1) = 0.5 * std::sqrt(3) * p(1);
147 std::unique_ptr<FunctionBase<Real>> clone()
const override 149 return std::make_unique<ParallelogramToSquare>();
154 libmesh_not_implemented();
163 output(0) = p(0) + p(1) / std::sqrt(3.);
164 output(1) = (2. / std::sqrt(3)) * p(1);
183 CPPUNIT_TEST(testLaplaceQuad);
184 CPPUNIT_TEST(testLaplaceTri);
185 #if defined(LIBMESH_ENABLE_VSMOOTHER) && defined(LIBMESH_HAVE_SOLVER) 186 CPPUNIT_TEST(testVariationalEdge2);
187 CPPUNIT_TEST(testVariationalEdge3);
188 CPPUNIT_TEST(testVariationalEdge3MultipleSubdomains);
190 CPPUNIT_TEST(testVariationalQuad);
191 CPPUNIT_TEST(testVariationalQuadMultipleSubdomains);
193 CPPUNIT_TEST(testVariationalTri3);
194 CPPUNIT_TEST(testVariationalTri6);
195 CPPUNIT_TEST(testVariationalTri6MultipleSubdomains);
197 CPPUNIT_TEST(testVariationalHex8);
198 CPPUNIT_TEST(testVariationalHex20);
199 CPPUNIT_TEST(testVariationalHex27);
200 CPPUNIT_TEST(testVariationalHex27MultipleSubdomains);
201 #endif // LIBMESH_ENABLE_VSMOOTHER 204 CPPUNIT_TEST_SUITE_END();
215 constexpr
unsigned int n_elems_per_side = 4;
218 mesh, n_elems_per_side, n_elems_per_side, 0., 1., 0., 1., type);
225 auto center_distortion_is =
227 const Real r = node(d);
228 const Real R = r * n_elems_per_side;
236 const Real R1 = node(1) * n_elems_per_side;
244 const Real R0 = node(0) * n_elems_per_side;
254 return ((std::abs(R - std::round(R)) > distortion_tol) == distortion);
257 for (
auto node :
mesh.node_ptr_range())
259 CPPUNIT_ASSERT(center_distortion_is(*node, 0,
true));
260 CPPUNIT_ASSERT(center_distortion_is(*node, 1,
true));
266 for (
unsigned int i = 0; i != 8; ++i)
270 for (
auto node :
mesh.node_ptr_range())
272 CPPUNIT_ASSERT(center_distortion_is(*node, 0,
false, 1e-3));
273 CPPUNIT_ASSERT(center_distortion_is(*node, 1,
false, 1e-3));
280 const bool multiple_subdomains =
false)
286 const auto dim = ref_elem->dim();
289 unsigned int n_elems_per_side = 5;
301 libmesh_error_msg_if(n_elems_per_side % 2 != 1,
302 "n_elems_per_side should be odd.");
311 mesh, n_elems_per_side, n_elems_per_side, 0., 1., 0., 1., type);
329 libmesh_error_msg(
"Unsupported dimension " <<
dim);
333 DistortHyperCube dh(
dim);
337 std::unordered_map<dof_id_type, Point> subdomain_boundary_node_id_to_point;
338 if (multiple_subdomains)
341 for (
auto * elem :
mesh.active_element_ptr_range())
343 unsigned int subdomain_id = 0;
345 if (elem->vertex_average()(d) > 0.5)
347 elem->subdomain_id() += subdomain_id;
353 for (
auto * elem :
mesh.active_element_ptr_range())
354 for (
const auto & s : elem->side_index_range())
356 const auto * neighbor = elem->neighbor_ptr(s);
357 if (neighbor ==
nullptr)
360 if (elem->subdomain_id() != neighbor->subdomain_id())
363 for (
const auto & n : elem->nodes_on_side(s))
364 subdomain_boundary_node_id_to_point[elem->node_id(n)] =
365 Point(*(elem->get_nodes()[n]));
372 libmesh_error_msg_if(
373 elem_orders.size() != 1,
374 "The variational smoother cannot be used for mixed-order meshes!");
375 const auto fe_order = *elem_orders.begin();
379 auto distortion_is = [
380 &n_elems_per_side, &
dim, &boundary_info, &fe_order
384 std::vector<boundary_id_type> boundary_ids;
388 const auto num_dofs =
dim - boundary_ids.size();
408 std::size_t num_zero_or_one = 0;
410 bool distorted =
false;
413 const Real r = node(d);
414 const Real R = r * n_elems_per_side * fe_order;
415 CPPUNIT_ASSERT_GREATER(-distortion_tol * distortion_tol, r);
416 CPPUNIT_ASSERT_GREATER(-distortion_tol * distortion_tol, 1 - r);
418 const bool d_distorted = std::abs(R - std::round(R)) > distortion_tol;
419 distorted |= d_distorted;
424 CPPUNIT_ASSERT_GREATEREQUAL(
dim - num_dofs, num_zero_or_one);
430 return distorted == distortion;
434 auto is_subdomain_boundary_node_the_same = [&subdomain_boundary_node_id_to_point](
436 auto it = subdomain_boundary_node_id_to_point.find(node.id());
437 if (it != subdomain_boundary_node_id_to_point.end())
445 for (
auto node :
mesh.node_ptr_range())
446 CPPUNIT_ASSERT(distortion_is(*node,
true));
453 SquareToParallelogram stp;
465 ParallelogramToSquare pts;
471 for (
auto node :
mesh.node_ptr_range())
473 if (multiple_subdomains)
474 CPPUNIT_ASSERT(is_subdomain_boundary_node_the_same(*node));
476 CPPUNIT_ASSERT(distortion_is(*node,
false, 1e-3));
485 testLaplaceSmoother(
mesh, laplace,
QUAD4);
493 testLaplaceSmoother(
mesh, laplace,
TRI3);
496 #ifdef LIBMESH_ENABLE_VSMOOTHER 502 testVariationalSmoother(
mesh, variational,
EDGE2);
510 testVariationalSmoother(
mesh, variational,
EDGE3);
518 testVariationalSmoother(
mesh, variational,
EDGE3,
true);
526 testVariationalSmoother(
mesh, variational,
QUAD4);
534 testVariationalSmoother(
mesh, variational,
QUAD4,
true);
542 testVariationalSmoother(
mesh, variational,
TRI3);
550 testVariationalSmoother(
mesh, variational,
TRI6);
558 testVariationalSmoother(
mesh, variational,
TRI3,
true);
566 testVariationalSmoother(
mesh, variational,
HEX8);
574 testVariationalSmoother(
mesh, variational,
HEX20);
582 testVariationalSmoother(
mesh, variational,
HEX27);
590 testVariationalSmoother(
mesh, variational,
HEX27,
true);
592 #endif // LIBMESH_ENABLE_VSMOOTHER ElemType
Defines an enum for geometric element types.
const std::set< Order > & elem_default_orders() const
The ReplicatedMesh class is derived from the MeshBase class, and is used to store identical copies of...
A Node is like a Point, but with more information.
virtual void zero() override final
Set every element in the vector to 0.
void testVariationalTri3()
void testLaplaceSmoother(ReplicatedMesh &mesh, MeshSmoother &smoother, ElemType type)
libMesh::Parallel::Communicator * TestCommWorld
static constexpr Real TOLERANCE
void resize(const unsigned int n)
Resize the vector.
void testVariationalQuad()
void testVariationalEdge2()
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
The libMesh namespace provides an interface to certain functionality in the library.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
virtual void smooth()=0
Function which actually performs the smoothing operations.
void testVariationalHex27()
This class defines the data structures necessary for Laplace smoothing.
void testVariationalEdge3()
void testVariationalHex8()
void testVariationalSmoother(ReplicatedMesh &mesh, MeshSmoother &smoother, const ElemType type, const bool multiple_subdomains=false)
CPPUNIT_TEST_SUITE_REGISTRATION(MeshSmootherTest)
std::string enum_to_string(const T e)
void testVariationalHex20()
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.
void testVariationalHex27MultipleSubdomains()
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
This is an implementation of Larisa Branets' smoothing algorithms.
void testVariationalQuadMultipleSubdomains()
void testVariationalTri6MultipleSubdomains()
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...
void testVariationalTri6()
This class provides the necessary interface for mesh smoothing.
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.
Defines a dense vector for use in Finite Element-type computations.
Base class for functors that can be evaluated at a point and (optionally) time.
A Point defines a location in LIBMESH_DIM dimensional Real space.
const Elem & get(const ElemType type_in)
void testVariationalEdge3MultipleSubdomains()