152#ifdef LIBMESH_HAVE_NETGEN
154 std::unique_ptr<UnstructuredMesh>
mesh =
155 dynamic_pointer_cast<UnstructuredMesh>(std::move(
_bdy_ptr));
160 mesh->get_boundary_info().clear_boundary_node_ids();
173 for (
const auto hole_i : index_range(
_hole_ptrs))
175 UnstructuredMesh & hole_mesh =
dynamic_cast<UnstructuredMesh &
>(**
_hole_ptrs[hole_i]);
179 std::set<ElemType> hole_elem_types;
180 std::set<unsigned short> hole_elem_dims;
181 std::vector<std::pair<dof_id_type, unsigned int>> hole_elem_external_sides;
182 for (
auto elem : hole_mesh.element_ptr_range())
184 hole_elem_dims.emplace(elem->dim());
188 if (elem->dim() == 3)
189 for (
auto s : make_range(elem->n_sides()))
192 if (!elem->neighbor_ptr(s))
194 hole_elem_types.emplace(elem->side_ptr(s)->type());
195 hole_elem_external_sides.emplace_back(elem->id(), s);
200 hole_elem_types.emplace(elem->type());
202 if (hole_elem_dims.size() != 1 || *hole_elem_dims.begin() < 2)
205 "All elements in a hole mesh must have the same dimension that is either 2D or 3D.");
206 else if (*hole_elem_dims.begin() == 3)
212 if (*hole_elem_types.begin() != ElemType::TRI3 || hole_elem_types.size() > 1)
216 "3D hole meshes with non-TRI3 surface elements cannot be stitched without "
217 "converting them to TET4. Consider setting convert_holes_for_stitching=true.");
222 for (
const auto & hees : hole_elem_external_sides)
223 hole_mesh.get_boundary_info().add_side(hees.first, hees.second, temp_ext_bid);
225 hole_mesh, std::vector<BoundaryName>({std::to_string(temp_ext_bid)}), 1,
false);
226 hole_mesh.get_boundary_info().remove_id(temp_ext_bid);
231 hole_mesh.prepare_for_use();
234 MeshTools::Modification::all_tri(**
_hole_ptrs[hole_i]);
242 "the hole mesh with index " + std::to_string(hole_i) +
243 " is a 2D mesh, for which stitching onto a 3D mesh does not make sense.");
247 std::unique_ptr<std::vector<std::unique_ptr<UnstructuredMesh>>> ngholes =
248 std::make_unique<std::vector<std::unique_ptr<UnstructuredMesh>>>();
252 for (std::unique_ptr<MeshBase> * hole_ptr :
_hole_ptrs)
256 const UnstructuredMesh & hole =
dynamic_cast<UnstructuredMesh &
>(**hole_ptr);
257 ngholes->push_back(std::make_unique<ReplicatedMesh>(hole));
267 auto output_subdomain_name = getParam<SubdomainName>(
"output_subdomain_name");
280 if (possible_sbdid != Elem::invalid_subdomain_id)
294 for (
auto elem :
mesh->element_ptr_range())
303 if (elem->is_flipped())
306 mooseError(
"Inverted element found in triangulation.\n"
307 "Laplacian smoothing can create these at reentrant corners; disable it?");
309 mooseError(
"Unexplained inverted element found in triangulation.\n");
313 const bool use_binary_search = (
_algorithm ==
"BINARY");
330 const boundary_id_type end_bcid =
_hole_ptrs.size() + 1;
354 boundary_id_type new_hole_bcid = end_bcid + free_boundary_id;
357 ? getParam<std::vector<BoundaryName>>(
"hole_boundaries")
358 : std::vector<BoundaryName>();
359 const BoundaryName output_boundary =
360 isParamValid(
"output_boundary") ? getParam<BoundaryName>(
"output_boundary") : BoundaryName();
364 const std::vector<BoundaryID> output_boundary_id =
366 ? (MooseUtils::isDigits(output_boundary)
367 ? std::vector<BoundaryID>(
368 1, MooseMeshUtils::getIDFromName<BoundaryName, BoundaryID>(output_boundary))
369 : std::vector<BoundaryID>(1, free_boundary_id))
370 : std::vector<BoundaryID>();
372 std::vector<BoundaryID> hole_boundary_ids;
374 for (
auto h : index_range(hole_boundaries))
375 hole_boundary_ids.push_back(
376 MooseUtils::isDigits(hole_boundaries[h])
377 ? MooseMeshUtils::getIDFromName<BoundaryName, BoundaryID>(hole_boundaries[h])
378 : h + 1 + free_boundary_id);
382 if (hole_boundary_ids.size())
384 new_hole_bcid = std::max(new_hole_bcid, boundary_id_type(hole_boundary_ids[h] + 1));
385 if (output_boundary_id.size())
386 new_hole_bcid = std::max(new_hole_bcid, boundary_id_type(output_boundary_id[0] + 1));
388 bool doing_stitching =
false;
392 const MeshBase & hole_mesh = **
_hole_ptrs[hole_i];
393 auto & hole_boundary_info = hole_mesh.get_boundary_info();
394 const std::set<boundary_id_type> & local_hole_bcids = hole_boundary_info.get_boundary_ids();
396 if (!local_hole_bcids.empty())
397 new_hole_bcid = std::max(new_hole_bcid, boundary_id_type(*local_hole_bcids.rbegin() + 1));
398 hole_mesh.comm().max(new_hole_bcid);
401 doing_stitching =
true;
404 const boundary_id_type inner_bcid = new_hole_bcid + 1;
419 std::unordered_map<std::tuple<Point, Point, Point>,
420 std::pair<std::pair<Elem *, unsigned int>,
bool>>
423 auto sorted_point_tuple = [](Elem & elem,
unsigned int side)
425 std::vector<unsigned int> nodes_on_side = elem.nodes_on_side(side);
426 libmesh_assert_equal_to(nodes_on_side.size(), 3);
427 std::vector<Point> p(3);
428 for (
auto i : index_range(p))
429 p[i] = elem.point(nodes_on_side[i]);
433 return std::make_tuple(p[0], p[1], p[2]);
434 else if (p[0] < p[2])
435 return std::make_tuple(p[0], p[2], p[1]);
437 return std::make_tuple(p[2], p[0], p[1]);
442 return std::make_tuple(p[1], p[0], p[2]);
443 else if (p[1] < p[2])
444 return std::make_tuple(p[1], p[2], p[0]);
446 return std::make_tuple(p[2], p[1], p[0]);
451 for (
auto elem :
mesh->element_ptr_range())
452 for (
auto s : make_range(elem->n_sides()))
453 if (!elem->neighbor_ptr(s))
455 auto points = sorted_point_tuple(*elem, s);
456 libmesh_assert(!mesh_faces.count(points));
457 mesh_faces.emplace(points, std::make_pair(std::make_pair(elem, s),
false));
460 auto & mesh_boundary_info =
mesh->get_boundary_info();
463 auto & main_subdomain_map =
mesh->set_subdomain_name_map();
466 UnstructuredMesh & hole_mesh =
dynamic_cast<UnstructuredMesh &
>(**
_hole_ptrs[hole_i]);
467 auto & hole_boundary_info = hole_mesh.get_boundary_info();
477 for (
auto elem : hole_mesh.element_ptr_range())
478 for (
auto s : make_range(elem->n_sides()))
479 if (!elem->neighbor_ptr(s))
481 auto points = sorted_point_tuple(*elem, s);
482 auto it = mesh_faces.find(points);
486 if (it != mesh_faces.end())
488 auto [main_elem, main_side] = it->second.first;
491 hole_boundary_info.add_side(elem, s, new_hole_bcid);
492 mesh_boundary_info.add_side(main_elem, main_side, inner_bcid);
496 mesh_boundary_info.add_side(main_elem, main_side, hole_i + 1);
497 it->second.second =
true;
504 for (
auto & [points, elem_side] : mesh_faces)
505 if (!elem_side.second)
507 auto [main_elem, main_side] = elem_side.first;
508 mesh_boundary_info.add_side(main_elem, main_side, 0);
524 *
mesh, 0, temp_bcid_shift + free_boundary_id);
528 *
mesh, hole_i + 1, hole_i + 1 + temp_bcid_shift + free_boundary_id);
533 if (output_boundary_id.size())
535 *
mesh, temp_bcid_shift + free_boundary_id, output_boundary_id[0]);
538 *
mesh, temp_bcid_shift + free_boundary_id, free_boundary_id);
540 if (hole_boundary_ids.size())
543 *
mesh, hole_i + 1 + temp_bcid_shift + free_boundary_id, hole_boundary_ids[hole_i]);
547 *
mesh, hole_i + 1 + temp_bcid_shift + free_boundary_id, hole_i + 1 + free_boundary_id);
551 UnstructuredMesh & hole_mesh =
dynamic_cast<UnstructuredMesh &
>(**
_hole_ptrs[hole_i]);
557 const auto & increment_subdomain_map = hole_mesh.get_subdomain_name_map();
558 main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end());
570 std::size_t n_nodes_stitched =
mesh->stitch_meshes(hole_mesh,
578 if (!n_nodes_stitched)
579 mooseError(
"Failed to stitch hole mesh ", hole_i,
" to new tetrahedralization.");
585 std::size_t n_nodes_stitched =
mesh->stitch_surfaces(inner_bcid,
591 if (!n_nodes_stitched)
592 mooseError(
"Failed to stitch combined hole meshes to new tetrahedralization.");
596 if (hole_boundary_ids.size())
598 mesh->get_boundary_info().sideset_name(hole_boundary_ids[h]) = hole_boundaries[h];
599 if (output_boundary_id.size())
600 mesh->get_boundary_info().sideset_name(output_boundary_id[0]) = output_boundary;
603 std::set<SubdomainName> main_subdomain_map_name_list;
604 for (
auto const & id_name_pair : main_subdomain_map)
605 main_subdomain_map_name_list.emplace(id_name_pair.second);
606 if (main_subdomain_map.size() != main_subdomain_map_name_list.size())
607 paramError(
"holes",
"The hole meshes contain subdomain name maps with conflicts.");
614 mesh->unset_is_prepared();
617 mooseError(
"Cannot use XYZDelaunayGenerator without NetGen-enabled libMesh.");
618 return std::unique_ptr<MeshBase>();