120 unsigned int num_fixed_nodes = 0;
121 auto pl =
mesh->sub_point_locator();
124 std::unordered_set<dof_id_type> nodes_removed;
126 for (
auto & node :
mesh->node_ptr_range())
129 if (nodes_removed.count(node->id()))
133 std::set<const Elem *> elements;
134 (*pl)(*node, elements);
136 for (
auto & elem : elements)
139 for (
auto & elem_node : elem->node_ref_range())
141 if (node->id() == elem_node.id())
149 for (
auto & elem_node : elem->node_ref_range())
151 if (elem_node.id() == node->id())
155 const auto x_node = (*node)(0);
156 const auto x_elem_node = elem_node(0);
157 const auto y_node = (*node)(1);
158 const auto y_elem_node = elem_node(1);
159 const auto z_node = (*node)(2);
160 const auto z_elem_node = elem_node(2);
162 if (MooseUtils::absoluteFuzzyEqual(x_node, x_elem_node, tol) &&
163 MooseUtils::absoluteFuzzyEqual(y_node, y_elem_node, tol) &&
164 MooseUtils::absoluteFuzzyEqual(z_node, z_elem_node, tol))
169 _console <<
"Two overlapping nodes in element " << elem->id() <<
" right by "
170 << elem->vertex_average() <<
".\n They will not be stitched" << std::endl;
176 const_cast<Elem *
>(elem)->set_node(elem->get_node_index(&elem_node), node);
177 nodes_removed.insert(elem_node.id());
180 if (num_fixed_nodes < 10)
181 _console <<
"Stitching nodes " << *node <<
" and " << elem_node
183 else if (num_fixed_nodes == 10)
184 _console <<
"Node stitching will now proceed silently." << std::endl;
190 _console <<
"Number of overlapping nodes which got merged: " << num_fixed_nodes << std::endl;
191 if (
mesh->allow_renumbering())
192 mesh->renumber_nodes_and_elements();
195 mesh->remove_orphaned_nodes();
196 mesh->update_parallel_id_counts();
240 unsigned int num_polygons = 0;
241 unsigned int num_nonconvex = 0;
242 unsigned int num_triangulated = 0;
243 std::vector<Elem *> elems_to_delete;
244 std::vector<std::unique_ptr<libMesh::C0Polygon>> elements_to_add_to_mesh;
247 if (!
mesh->is_serial())
248 mooseError(
"MeshRepairGenerator requires a serial mesh for this operation. "
249 "The mesh should not be distributed.");
251 for (
auto elem :
mesh->element_ptr_range())
259 auto is_convex = [](
const Elem * elem,
260 std::vector<std::pair<int, Real>> & obtuse_angle_nodes) ->
bool
262 mooseAssert(elem,
"Should have an elem");
263 mooseAssert(obtuse_angle_nodes.empty(),
"Should not be empty");
266 const auto elem_centroid = elem->vertex_average();
267 const auto n_nodes = elem->n_nodes();
270 for (
const auto i : make_range(
n_nodes))
272 const auto v1 = elem_centroid - elem->point(i);
273 const auto v2 = elem_centroid - elem->point((i + 1) %
n_nodes);
274 if (!MooseUtils::absoluteFuzzyEqual((v1.cross(v2)).norm_sq(), 0))
276 plane_normal = v1.cross(v2).unit();
283 for (
const auto i : make_range(
n_nodes))
285 const auto n1 = elem->point(i);
286 const auto n2 = elem->point((i + 1) %
n_nodes);
287 const auto n3 = elem->point((i + 2) %
n_nodes);
288 const auto top_dir = (n2 - n1).cross(n3 - n2);
290 if (plane_normal * top_dir <= 0)
293 if (!MooseUtils::absoluteFuzzyEqual(plane_normal * top_dir, 0))
294 obtuse_angle_nodes.push_back(
295 std::make_pair<int, Real>(i, plane_normal * top_dir / top_dir.norm()));
298 obtuse_angle_nodes.push_back(std::make_pair<int, Real>(i, -1));
304 std::vector<std::pair<int, Real>> parent_obtuse_angles;
305 const auto convex = is_convex(elem, parent_obtuse_angles);
315 auto cut_polygon = [&
mesh](
const Elem * elem,
unsigned int node_i_cut1,
unsigned node_i_cut2)
316 -> std::pair<std::unique_ptr<libMesh::C0Polygon>, std::unique_ptr<libMesh::C0Polygon>>
318 const auto cut1 = std::min(node_i_cut1, node_i_cut2);
319 const auto cut2 = std::max(node_i_cut1, node_i_cut2);
322 const auto ns1 = cut2 - cut1 + 1;
323 const auto ns2 = elem->n_nodes() - ns1 + 2;
324 mooseAssert(ns1 >= 3,
"Should cut at least a triangle");
325 mooseAssert(ns2 >= 3,
"Should cut at least a triangle");
328 auto child1 = std::make_unique<libMesh::C0Polygon>(ns1);
329 for (
const auto i_n : make_range(cut1, cut2 + 1))
330 child1->set_node(i_n - cut1,
mesh->node_ptr(elem->node_ptr(i_n)->id()));
332 auto child2 = std::make_unique<libMesh::C0Polygon>(ns2);
333 for (
const auto i_n : make_range(cut2, elem->n_nodes() + cut1 + 1))
334 child2->set_node((i_n - cut2) % child2->n_nodes(),
335 mesh->node_ptr(elem->node_ptr(i_n % elem->n_nodes())->id()));
337 return std::make_pair(std::move(child1), std::move(child2));
345 std::vector<std::unique_ptr<libMesh::C0Polygon>> elements_to_add_to_mesh_temp;
346 std::vector<std::pair<std::unique_ptr<Elem>, std::vector<std::pair<int, Real>>>> elems_to_cut;
349 std::unique_ptr<libMesh::C0Polygon> base_elem =
350 std::make_unique<libMesh::C0Polygon>(elem->n_nodes());
351 for (
const auto i_n : make_range(elem->n_nodes()))
352 base_elem->set_node(i_n, elem->node_ptr(i_n));
353 elems_to_cut.push_back(std::make_pair(std::move(base_elem), parent_obtuse_angles));
355 while (!failed && !elems_to_cut.empty())
357 auto & [current_elem, large_angles] = elems_to_cut.back();
361 auto worst_angle_it =
362 std::min_element(large_angles.begin(),
364 [](std::pair<int, Real> lhs, std::pair<int, Real> rhs) ->
bool
365 { return lhs.second < rhs.second; });
366 unsigned int worst_angle_i = (*worst_angle_it).first;
367 const auto n_nodes = current_elem->n_nodes();
375 auto [child1, child2] = cut_polygon(current_elem.get(), worst_angle_i, opposite);
378 std::vector<std::pair<int, Real>> child1_large_angles;
379 bool is_convex1 = is_convex(child1.get(), child1_large_angles);
380 std::vector<std::pair<int, Real>> child2_large_angles;
381 bool is_convex2 = is_convex(child2.get(), child2_large_angles);
384 if (child1->n_nodes() < 4 && !is_convex1)
386 if (child2->n_nodes() < 4 && !is_convex2)
391 if (is_convex1 && child1->volume() <= 0)
393 if (is_convex2 && child2->volume() <= 0)
399 large_angles.erase(worst_angle_it);
400 elems_to_cut.pop_back();
404 elems_to_cut.push_back(std::make_pair(std::move(child1), child1_large_angles));
406 elems_to_cut.push_back(std::make_pair(std::move(child2), child2_large_angles));
411 elements_to_add_to_mesh_temp.push_back(std::move(child1));
413 elements_to_add_to_mesh_temp.push_back(std::move(child2));
415 bool fixed_it = !failed;
419 for (
auto & elem_uptr : elements_to_add_to_mesh_temp)
421 elem_uptr->inherit_data_from(*elem);
422 elements_to_add_to_mesh.push_back(std::move(elem_uptr));
429 const auto centroid_node =
mesh->add_point(elem->true_centroid());
432 const auto n_sides = poly->n_sides();
433 for (
const auto i_tr : make_range(n_sides))
435 auto new_elem = std::make_unique<libMesh::C0Polygon>(3);
436 new_elem->set_node(0,
mesh->node_ptr(elem->node_ptr(i_tr)->id()));
437 new_elem->set_node(1, centroid_node);
438 new_elem->set_node(2,
mesh->node_ptr(elem->node_ptr((i_tr + 1) % n_sides)->id()));
441 if (MooseUtils::absoluteFuzzyEqual(
442 (*(Point *)centroid_node - *(Point *)elem->node_ptr(i_tr))
443 .cross(*(Point *)centroid_node -
444 *(Point *)elem->node_ptr((i_tr + 1) % n_sides))
447 mooseError(
"Manual triangulation failed as two consecutive nodes are aligned with the "
450 new_elem->inherit_data_from(*elem);
451 elements_to_add_to_mesh.push_back(std::move(new_elem));
455 elems_to_delete.push_back(elem);
459 for (
auto elem : elems_to_delete)
460 mesh->delete_elem(elem);
462 for (
auto & new_elem_ptr : elements_to_add_to_mesh)
463 mesh->add_elem(std::move(new_elem_ptr));
465 _console <<
"Number of non-convex polygons which got split into convex polygons: "
466 << num_nonconvex << std::endl;
467 if (num_triangulated)
468 _console <<
"Number of non-convex polygons split using a triangulation: " << num_triangulated
469 <<
", using heuristic: " << num_nonconvex - num_triangulated << std::endl;
471 mooseWarning(
"No C0 polygons in mesh: the polyon convexity fix did nothing");