407 const bool multiple_subdomains =
false,
408 const bool tangle_mesh =
false,
409 const Real tangle_damping_factor = 1.0)
413 if (multiple_subdomains && tangle_mesh)
414 libmesh_not_implemented_msg(
415 "Arbitrary mesh tangling with multiple subdomains is not supported.");
419 const auto dim = ref_elem->dim();
428 const auto side_length = 1.0 / n_elems_per_side;
437 mesh, n_elems_per_side, n_elems_per_side, 0., 1., 0., 1., type);
455 libmesh_error_msg(
"Unsupported dimension " <<
dim);
459 DistortHyperCube dh(
dim);
482 std::map<dof_id_type, Real> dist1_map;
484 std::map<dof_id_type, Real> dist2_map;
486 for (
const auto * node :
mesh.local_node_ptr_range())
488 dist1_map[node->id()] = (*node - p1).norm();
489 dist2_map[node->id()] = (*node - p2).norm();
495 auto get_closet_point_accross_all_procs =
496 [&
mesh, &proc_id](
const std::map<dof_id_type, Real> &dist_map) {
499 Real d_min_local = std::numeric_limits<Real>::max();
508 std::min_element(dist_map.begin(), dist_map.end(),
509 [](
const auto &a,
const auto &
b) {
510 return a.second < b.second;
513 node_id = min_it->first;
514 d_min_local = min_it->second;
518 auto d_min_global = d_min_local;
524 if (d_min_local == d_min_global) {
525 broadcasting_proc = proc_id;
534 return std::pair(node_id, node);
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);
543 const auto displacement = tangle_damping_factor * (node1 - node2);
551 mesh.nodes_end(), sync_object);
557 CPPUNIT_ASSERT(unsmoothed_info.mesh_is_tangled);
561 std::unordered_map<subdomain_id_type, Real> distorted_subdomain_volumes;
563 if (multiple_subdomains)
566 for (
auto * elem :
mesh.active_element_ptr_range())
568 unsigned int subdomain_id = 0;
570 if (elem->vertex_average()(d) > 0.5)
572 elem->subdomain_id() += subdomain_id;
578 for (
auto *elem :
mesh.active_element_ptr_range()) {
579 const auto sub_id = elem->subdomain_id();
581 if (elem->processor_id() != proc_id)
583 distorted_subdomain_volumes[sub_id] += elem->volume();
584 highest_subdomain_id = std::max(sub_id, highest_subdomain_id);
589 for (
const auto sub_id :
make_range(highest_subdomain_id + 1)) {
591 if (distorted_subdomain_volumes.find(sub_id) ==
592 distorted_subdomain_volumes.end())
593 distorted_subdomain_volumes[sub_id] = 0.;
595 mesh.
comm().
sum(distorted_subdomain_volumes[sub_id]);
608 libmesh_error_msg_if(elem_orders.size() != 1,
609 "The variational smoother cannot be used for mixed-order meshes!");
614 const auto scale_factor = *elem_orders.begin() * ((type_is_pyramid || type_is_tet) ? 2 * 4 : 1);
617 auto node_distortion_is = [&n_elems_per_side, &
dim, &boundary_info, &scale_factor, &type_is_prism](
620 std::vector<boundary_id_type> boundary_ids;
621 boundary_info.boundary_ids(&node, boundary_ids);
624 const auto num_dofs =
dim - boundary_ids.size();
644 std::size_t num_zero_or_one = 0;
646 bool distorted =
false;
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);
654 bool d_distorted = std::abs(R - std::round(R)) > distortion_tol;
655 if (type_is_prism && (scale_factor == 3))
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;
665 distorted |= d_distorted;
669 CPPUNIT_ASSERT_GREATEREQUAL(
dim - num_dofs, num_zero_or_one);
675 return distorted == distortion;
679 for (
auto node :
mesh.node_ptr_range())
680 CPPUNIT_ASSERT(node_distortion_is(*node,
true));
690 SquareToParallelogram stp;
693 else if (type_is_prism)
698 CubeToParallelepiped ctp;
709 ParallelogramToSquare pts;
712 else if (type_is_prism)
717 ParallelepipedToCube ptc;
721 if (multiple_subdomains)
726 std::unordered_map<subdomain_id_type, Real> smoothed_subdomain_volumes;
727 for (
auto *elem :
mesh.active_element_ptr_range()) {
729 if (elem->processor_id() != proc_id)
731 smoothed_subdomain_volumes[elem->subdomain_id()] += elem->volume();
735 for (
const auto sub_id :
make_range(highest_subdomain_id + 1)) {
737 if (smoothed_subdomain_volumes.find(sub_id) ==
738 smoothed_subdomain_volumes.end())
739 smoothed_subdomain_volumes[sub_id] = 0.;
741 mesh.
comm().
sum(smoothed_subdomain_volumes[sub_id]);
744 for (
const auto sub_id :
make_range(highest_subdomain_id + 1))
746 libmesh_map_find(distorted_subdomain_volumes, sub_id),
747 libmesh_map_find(smoothed_subdomain_volumes, sub_id),
TOLERANCE));
751 std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
756 std::set<dof_id_type> nodes_checked;
759 for (
const auto * elem :
mesh.active_element_ptr_range())
761 for (
const auto local_node_id :
make_range(elem->n_nodes()))
763 const auto & node = elem->node_ref(local_node_id);
764 if (nodes_checked.find(node.id()) != nodes_checked.end())
767 nodes_checked.insert(node.id());
773 if (local_node_id > 8 && local_node_id < 13)
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;
789 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(base + x * (apex - base), tol));
792 else if (local_node_id > 13)
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);
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));
815 else if (type_is_tet && !elem->is_vertex(local_node_id))
820 std::vector<const Node *> neighbors;
822 mesh, node, nodes_to_elem_map, neighbors);
824 switch (neighbors.size())
831 const auto is_0_cube_center =
833 const auto is_1_cube_center =
837 if (is_0_cube_center || is_1_cube_center)
839 const auto & cube_center =
840 is_0_cube_center ? *neighbors[0] : *neighbors[1];
842 is_0_cube_center ? *neighbors[1] : *neighbors[0];
849 const Real x = (type ==
TET10) ? 0.42895041 : 0.41486385;
850 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(
851 other + x * (cube_center - other), tol));
856 const Real x = (type ==
TET10) ? 0.55388920 : 0.58093516;
857 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(
858 other + x * (cube_center - other), tol));
867 const auto is_0_cube_vertex =
869 const auto is_1_cube_vertex =
871 const auto is_0_cube_face_center =
873 const auto is_1_cube_face_center =
876 if (is_0_cube_vertex && is_1_cube_vertex)
880 CPPUNIT_ASSERT(node_distortion_is(node,
false));
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));
905 neighbors.erase(std::remove_if(neighbors.begin(),
907 [&elem](
const Node * n) {
908 return !elem->is_vertex(
909 elem->local_node(n->id()));
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)
932 num_vertices_at_cube_center += 1;
934 num_vertices_at_cube_vertices += 1;
936 num_vertices_at_cube_face_centers += 1;
944 if (num_vertices_at_cube_vertices == 2)
947 if (num_vertices_at_cube_center)
948 for (
const auto * neighbor : neighbors)
956 libmesh_error_msg(
"We should never get here!");
958 node_approx += weight * (*neighbor);
962 else if (num_vertices_at_cube_face_centers)
963 for (
const auto * neighbor : neighbors)
971 libmesh_error_msg(
"We should never get here!");
973 node_approx += weight * (*neighbor);
977 libmesh_error_msg(
"We should never get here!");
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)
993 libmesh_error_msg(
"We should never get here!");
995 node_approx += weight * (*neighbor);
999 libmesh_error_msg(
"We should never get here!");
1001 CPPUNIT_ASSERT(node.absolute_fuzzy_equals(node_approx, tol));
1007 libmesh_error_msg(node <<
" has unexpected number of neighbors ("
1008 << neighbors.size() <<
")");
1014 CPPUNIT_ASSERT(node_distortion_is(node,
false));