411 if (multiple_subdomains && tangle_mesh)
412 libmesh_not_implemented_msg(
413 "Arbitrary mesh tangling with multiple subdomains is not supported.");
417 const auto dim = ref_elem->dim();
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;
435 mesh, n_elems_per_side, n_elems_per_side, 0., 1., 0., 1., type);
453 libmesh_error_msg(
"Unsupported dimension " <<
dim);
457 DistortHyperCube dh(
dim);
472 const Real dr = 1. / (n_elems_per_side * Elem::type_to_default_order_map[type]);
480 std::map<dof_id_type, Real> dist1_map;
482 std::map<dof_id_type, Real> dist2_map;
484 for (
const auto * node :
mesh.local_node_ptr_range())
486 dist1_map[node->id()] = (*node - p1).
norm();
487 dist2_map[node->id()] = (*node - p2).
norm();
493 auto get_closet_point_accross_all_procs =
494 [&
mesh, &proc_id](
const std::map<dof_id_type, Real> &dist_map) {
497 Real d_min_local = std::numeric_limits<Real>::max();
506 std::min_element(dist_map.begin(), dist_map.end(),
507 [](
const auto &a,
const auto &
b) {
508 return a.second <
b.second;
511 node_id = min_it->first;
512 d_min_local = min_it->second;
516 auto d_min_global = d_min_local;
522 if (d_min_local == d_min_global) {
523 broadcasting_proc = proc_id;
532 return std::pair(node_id, node);
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);
541 const auto displacement = tangle_damping_factor * (node1 - node2);
549 mesh.nodes_end(), sync_object);
555 CPPUNIT_ASSERT(unsmoothed_info.mesh_is_tangled);
559 std::unordered_map<subdomain_id_type, Real> distorted_subdomain_volumes;
561 if (multiple_subdomains)
564 for (
auto * elem :
mesh.active_element_ptr_range())
566 unsigned int subdomain_id = 0;
568 if (elem->vertex_average()(d) > 0.5)
570 elem->subdomain_id() += subdomain_id;
576 for (
auto *elem :
mesh.active_element_ptr_range()) {
577 const auto sub_id = elem->subdomain_id();
579 if (elem->processor_id() != proc_id)
581 distorted_subdomain_volumes[sub_id] += elem->volume();
582 highest_subdomain_id = std::max(sub_id, highest_subdomain_id);
587 for (
const auto sub_id :
make_range(highest_subdomain_id + 1)) {
589 if (distorted_subdomain_volumes.find(sub_id) ==
590 distorted_subdomain_volumes.end())
591 distorted_subdomain_volumes[sub_id] = 0.;
593 mesh.
comm().
sum(distorted_subdomain_volumes[sub_id]);
603 libmesh_error_msg_if(elem_orders.size() != 1,
604 "The variational smoother cannot be used for mixed-order meshes!");
609 const auto scale_factor = *elem_orders.begin() * ((type_is_pyramid || type_is_tet) ? 2 * 4 : 1);
612 auto node_distortion_is = [&n_elems_per_side, &
dim, &boundary_info, &scale_factor, &type_is_prism](
615 std::vector<boundary_id_type> boundary_ids;
616 boundary_info.boundary_ids(&node, boundary_ids);
619 const auto num_dofs =
dim - boundary_ids.size();
639 std::size_t num_zero_or_one = 0;
641 bool distorted =
false;
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);
649 bool d_distorted = std::abs(R - std::round(R)) > distortion_tol;
650 if (type_is_prism && (scale_factor == 3))
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;
660 distorted |= d_distorted;
664 CPPUNIT_ASSERT_GREATEREQUAL(
dim - num_dofs, num_zero_or_one);
670 return distorted == distortion;
674 for (
auto node :
mesh.node_ptr_range())
675 CPPUNIT_ASSERT(node_distortion_is(*node,
true));
685 SquareToParallelogram stp;
688 else if (type_is_prism)
693 CubeToParallelepiped ctp;
704 ParallelogramToSquare pts;
707 else if (type_is_prism)
712 ParallelepipedToCube ptc;
716 if (multiple_subdomains)
721 std::unordered_map<subdomain_id_type, Real> smoothed_subdomain_volumes;
722 for (
auto *elem :
mesh.active_element_ptr_range()) {
724 if (elem->processor_id() != proc_id)
726 smoothed_subdomain_volumes[elem->subdomain_id()] += elem->volume();
730 for (
const auto sub_id :
make_range(highest_subdomain_id + 1)) {
732 if (smoothed_subdomain_volumes.find(sub_id) ==
733 smoothed_subdomain_volumes.end())
734 smoothed_subdomain_volumes[sub_id] = 0.;
736 mesh.
comm().
sum(smoothed_subdomain_volumes[sub_id]);
739 for (
const auto sub_id :
make_range(highest_subdomain_id + 1))
741 libmesh_map_find(distorted_subdomain_volumes, sub_id),
742 libmesh_map_find(smoothed_subdomain_volumes, sub_id),
TOLERANCE));
746 std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
751 std::set<dof_id_type> nodes_checked;
754 for (
const auto * elem :
mesh.active_element_ptr_range())
756 for (
const auto local_node_id :
make_range(elem->n_nodes()))
758 const auto & node = elem->node_ref(local_node_id);
759 if (nodes_checked.find(node.id()) != nodes_checked.end())
762 nodes_checked.insert(node.id());
768 if (local_node_id > 8 && local_node_id < 13)
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;
784 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(base + x * (apex - base), tol));
787 else if (local_node_id > 13)
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);
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));
810 else if (type_is_tet && !elem->is_vertex(local_node_id))
815 std::vector<const Node *> neighbors;
817 mesh, node, nodes_to_elem_map, neighbors);
819 switch (neighbors.size())
826 const auto is_0_cube_center =
828 const auto is_1_cube_center =
832 if (is_0_cube_center || is_1_cube_center)
834 const auto & cube_center =
835 is_0_cube_center ? *neighbors[0] : *neighbors[1];
837 is_0_cube_center ? *neighbors[1] : *neighbors[0];
844 const Real x = (type ==
TET10) ? 0.42895041 : 0.41486385;
845 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(
846 other + x * (cube_center - other), tol));
851 const Real x = (type ==
TET10) ? 0.55388920 : 0.58093516;
852 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(
853 other + x * (cube_center - other), tol));
862 const auto is_0_cube_vertex =
864 const auto is_1_cube_vertex =
866 const auto is_0_cube_face_center =
868 const auto is_1_cube_face_center =
871 if (is_0_cube_vertex && is_1_cube_vertex)
875 CPPUNIT_ASSERT(node_distortion_is(node,
false));
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));
900 neighbors.erase(std::remove_if(neighbors.begin(),
902 [&elem](
const Node * n) {
903 return !elem->is_vertex(
904 elem->local_node(n->id()));
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)
927 num_vertices_at_cube_center += 1;
929 num_vertices_at_cube_vertices += 1;
931 num_vertices_at_cube_face_centers += 1;
939 if (num_vertices_at_cube_vertices == 2)
942 if (num_vertices_at_cube_center)
943 for (
const auto * neighbor : neighbors)
951 libmesh_error_msg(
"We should never get here!");
953 node_approx +=
weight * (*neighbor);
957 else if (num_vertices_at_cube_face_centers)
958 for (
const auto * neighbor : neighbors)
966 libmesh_error_msg(
"We should never get here!");
968 node_approx +=
weight * (*neighbor);
972 libmesh_error_msg(
"We should never get here!");
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)
988 libmesh_error_msg(
"We should never get here!");
990 node_approx +=
weight * (*neighbor);
994 libmesh_error_msg(
"We should never get here!");
996 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(node_approx, tol));
1002 libmesh_error_msg(node <<
" has unexpected number of neighbors (" 1003 << neighbors.size() <<
")");
1009 CPPUNIT_ASSERT(node_distortion_is(node,
false));
const std::set< Order > & elem_default_orders() const
A Node is like a Point, but with more information.
static constexpr Real TOLERANCE
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.
bool pointIsCubeVertex(const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
const Parallel::Communicator & comm() const
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
uint8_t processor_id_type
void min(const T &r, T &o, Request &req) const
bool pointIsCubeFaceCenter(const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
virtual const Node * query_node_ptr(const dof_id_type i) const =0
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
void max(const T &r, T &o, Request &req) const
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 pointIsCubeCenter(const Point &point, const Real &side_length, const Real &tol=TOLERANCE)
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...
processor_id_type processor_id() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
const Elem & get(const ElemType type_in)