282 if (multiple_subdomains && tangle_mesh)
283 libmesh_not_implemented_msg(
284 "Arbitrary mesh tangling with multiple subdomains is not supported.");
288 const auto dim = ref_elem->dim();
294 unsigned int n_elems_per_side = 4 / Elem::type_to_default_order_map[type];
303 mesh, n_elems_per_side, n_elems_per_side, 0., 1., 0., 1., type);
321 libmesh_error_msg(
"Unsupported dimension " <<
dim);
325 DistortHyperCube dh(
dim);
339 const Real dr = 1. / (n_elems_per_side * Elem::type_to_default_order_map[type]);
345 Real dist1_closest = std::numeric_limits<Real>::max();
347 Real dist2_closest = std::numeric_limits<Real>::max();
349 for (
const auto * node :
mesh.local_node_ptr_range())
351 const auto dist1 = (*node - p1).
norm();
352 if (dist1 < dist1_closest)
354 dist1_closest = dist1;
355 node1_id = node->id();
358 const auto dist2 = (*node - p2).
norm();
359 if (dist2 < dist2_closest)
361 dist2_closest = dist2;
362 node2_id = node->id();
367 unsigned int node1_rank;
371 unsigned int node2_rank;
376 if ((node1_id != DofObject::invalid_id) && (node2_id != DofObject::invalid_id))
383 const Point diff = node1 - node2;
387 node1 -= tangle_damping_factor * diff;
388 node2 += tangle_damping_factor * diff;
397 CPPUNIT_ASSERT(unsmoothed_info.mesh_is_tangled);
401 std::unordered_map<subdomain_id_type, Real> distorted_subdomain_volumes;
402 if (multiple_subdomains)
405 for (
auto * elem :
mesh.active_element_ptr_range())
407 unsigned int subdomain_id = 0;
409 if (elem->vertex_average()(d) > 0.5)
411 elem->subdomain_id() += subdomain_id;
417 for (
auto * elem :
mesh.active_element_ptr_range())
418 distorted_subdomain_volumes[elem->subdomain_id()] += elem->volume();
423 libmesh_error_msg_if(elem_orders.size() != 1,
424 "The variational smoother cannot be used for mixed-order meshes!");
428 const auto scale_factor = *elem_orders.begin() * (type_is_pyramid ? 2 * 4 : 1);
431 auto node_distortion_is = [&n_elems_per_side, &
dim, &boundary_info, &scale_factor](
434 std::vector<boundary_id_type> boundary_ids;
435 boundary_info.boundary_ids(&node, boundary_ids);
438 const auto num_dofs =
dim - boundary_ids.size();
458 std::size_t num_zero_or_one = 0;
460 bool distorted =
false;
463 const Real r = node(d);
464 const Real R = r * n_elems_per_side * scale_factor;
465 CPPUNIT_ASSERT_GREATER(-distortion_tol * distortion_tol, r);
466 CPPUNIT_ASSERT_GREATER(-distortion_tol * distortion_tol, 1 - r);
468 const bool d_distorted = std::abs(R - std::round(R)) > distortion_tol;
469 distorted |= d_distorted;
473 CPPUNIT_ASSERT_GREATEREQUAL(
dim - num_dofs, num_zero_or_one);
479 return distorted == distortion;
483 for (
auto node :
mesh.node_ptr_range())
484 CPPUNIT_ASSERT(node_distortion_is(*node,
true));
491 SquareToParallelogram stp;
503 ParallelogramToSquare pts;
507 if (multiple_subdomains)
512 std::unordered_map<subdomain_id_type, Real> smoothed_subdomain_volumes;
513 for (
auto * elem :
mesh.active_element_ptr_range())
514 smoothed_subdomain_volumes[elem->subdomain_id()] += elem->volume();
516 for (
const auto & pair : distorted_subdomain_volumes)
518 const auto & subdomain_id = pair.first;
521 libmesh_map_find(smoothed_subdomain_volumes, subdomain_id),
528 std::set<dof_id_type> nodes_checked;
529 for (
const auto * elem :
mesh.active_element_ptr_range())
531 for (
const auto local_node_id :
make_range(elem->n_nodes()))
533 const auto & node = elem->node_ref(local_node_id);
534 if (nodes_checked.find(node.id()) != nodes_checked.end())
537 nodes_checked.insert(node.id());
543 if (local_node_id > 8 && local_node_id < 13)
555 const auto & base = elem->node_ref(local_node_id - 9);
556 const auto & apex = elem->node_ref(4);
559 CPPUNIT_ASSERT(node.relative_fuzzy_equals(base + x * (apex - base), 1e-3));
562 else if (local_node_id > 13)
572 const auto & base1 = elem->node_ref(local_node_id - 14);
573 const auto & base2 = elem->node_ref((local_node_id - 13) % 4);
574 const auto & apex = elem->node_ref(4);
576 const auto node_approx = (0.3141064847 * base1 +
577 0.3141064847 * base2 +
578 0.3717870306 * apex);
579 CPPUNIT_ASSERT(node.relative_fuzzy_equals(node_approx, 1e-3));
584 CPPUNIT_ASSERT(node_distortion_is(node,
false, 1e-2));
const std::set< Order > & elem_default_orders() const
A Node is like a Point, but with more information.
void minloc(T &r, unsigned int &min_id) const
static constexpr Real TOLERANCE
const MeshQualityInfo & get_mesh_info() const
Getter for the _system's _mesh_info attribute.
const Parallel::Communicator & comm() const
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
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.
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
std::string enum_to_string(const T e)
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.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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...
virtual const Node & node_ref(const dof_id_type i) const
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.
virtual void setup()
Setup method that creates equation systems, system, and constraints, to be called just prior to smoot...
A Point defines a location in LIBMESH_DIM dimensional Real space.
const Elem & get(const ElemType type_in)
virtual void smooth() override
Redefinition of the smooth function from the base class.