14 #include "libmesh/elem.h" 15 #include "libmesh/boundary_info.h" 16 #include "libmesh/mesh_base.h" 17 #include "libmesh/parallel.h" 18 #include "libmesh/parallel_algebra.h" 19 #include "libmesh/cell_tet4.h" 30 params.
addRequiredParam<MeshGeneratorName>(
"input",
"The input mesh that needs to be trimmed.");
33 "generate_transition_layer",
35 "Whether to generate a transition layer for the cut mesh. " 36 "If false, the entire input mesh will be converted to TET4 elements to facilitate the " 37 "cutting; if true, only the elements near the cut face will be converted with a transition " 38 "layer to maintain compatibility with the original mesh.");
41 "The boundary id of the face generated by the cut. An " 42 "id will be automatically assigned if not provided.");
44 "cut_face_name", BoundaryName(),
"The boundary name of the face generated by the cut.");
45 params.
addParam<SubdomainName>(
"converted_tet_element_subdomain_name_suffix",
47 "The suffix to be added to the original subdomain name for the " 48 "subdomains containing the elements converted to TET4. This is " 49 "only applicable when transition layer is generated.");
51 "converted_pyramid_element_subdomain_name_suffix",
53 "The suffix to be added to the original subdomain name for the subdomains containing the " 54 "elements converted to PYRAMID5. This is only applicable when transition layer is " 58 "This CutMeshByLevelSetGeneratorBase object is designed to be the base class of mesh " 59 "generator that cuts a 3D mesh based on an analytic level set function. The level set " 60 "function could be provided explicitly or indirectly.");
68 _input_name(getParam<MeshGeneratorName>(
"input")),
69 _generate_transition_layer(getParam<bool>(
"generate_transition_layer")),
70 _cut_face_name(getParam<BoundaryName>(
"cut_face_name")),
71 _converted_tet_element_subdomain_name_suffix(
72 getParam<SubdomainName>(
"converted_tet_element_subdomain_name_suffix")),
73 _converted_pyramid_element_subdomain_name_suffix(
74 getParam<SubdomainName>(
"converted_pyramid_element_subdomain_name_suffix")),
75 _input(getMeshByName(_input_name))
80 std::unique_ptr<MeshBase>
84 if (!
_input->preparation().has_cached_elem_data)
87 auto replicated_mesh_ptr =
dynamic_cast<ReplicatedMesh *
>(
_input.get());
88 if (!replicated_mesh_ptr)
89 paramError(
"input",
"Input is not a replicated mesh, which is required");
90 if (*(replicated_mesh_ptr->elem_dimensions().begin()) != 3 ||
91 *(replicated_mesh_ptr->elem_dimensions().rbegin()) != 3)
94 "Only 3D meshes are supported. Use XYMeshLineCutter for 2D meshes if applicable. Mixed " 95 "dimensional meshes are not supported at the moment.");
97 ReplicatedMesh &
mesh = *replicated_mesh_ptr;
107 "The cut face boundary name and id are both provided, but they are inconsistent " 108 "with an existing boundary name which has a different id.");
127 const auto block_id_to_remove = sid_shift_base * 3;
131 std::vector<std::pair<dof_id_type, bool>> cross_and_remained_elems_pre_convert;
134 std::set<subdomain_id_type> original_subdomain_ids;
135 for (
auto elem_it =
mesh.active_elements_begin(); elem_it !=
mesh.active_elements_end();
138 original_subdomain_ids.emplace((*elem_it)->subdomain_id());
139 const unsigned int & n_vertices = (*elem_it)->n_vertices();
140 unsigned short elem_vertices_counter(0);
141 for (
unsigned int i = 0; i < n_vertices; i++)
147 elem_vertices_counter++;
149 if (elem_vertices_counter == n_vertices)
150 (*elem_it)->subdomain_id() = block_id_to_remove;
154 if ((*elem_it)->default_order() != Order::FIRST)
155 mooseError(
"Only first order elements are supported for cutting.");
159 cross_and_remained_elems_pre_convert.push_back(std::make_pair((*elem_it)->id(),
true));
162 if ((*elem_it)->type() !=
TET4)
163 (*elem_it)->subdomain_id() += sid_shift_base;
166 cross_and_remained_elems_pre_convert.push_back(
167 std::make_pair((*elem_it)->id(), elem_vertices_counter > 0));
172 std::vector<dof_id_type> transition_elems_list;
173 std::vector<std::vector<unsigned int>> transition_elems_sides_list;
176 if (!
mesh.is_prepared())
177 mesh.find_neighbors();
180 for (
const auto & elem_info : cross_and_remained_elems_pre_convert)
182 for (
const auto & i_side :
make_range(
mesh.elem_ptr(elem_info.first)->n_sides()))
185 if (
mesh.elem_ptr(elem_info.first)->side_ptr(i_side)->type() ==
QUAD4)
187 if (
mesh.elem_ptr(elem_info.first)->neighbor_ptr(i_side) !=
nullptr &&
188 mesh.elem_ptr(elem_info.first)->neighbor_ptr(i_side)->subdomain_id() !=
189 block_id_to_remove &&
190 std::find(cross_and_remained_elems_pre_convert.begin(),
191 cross_and_remained_elems_pre_convert.end(),
192 std::make_pair(
mesh.elem_ptr(elem_info.first)->neighbor_ptr(i_side)->id(),
193 true)) == cross_and_remained_elems_pre_convert.end())
195 const auto & neighbor_elem_id =
196 mesh.elem_ptr(elem_info.first)->neighbor_ptr(i_side)->id();
197 const auto & neighbor_elem_side_index =
198 mesh.elem_ptr(elem_info.first)
199 ->neighbor_ptr(i_side)
200 ->which_neighbor_am_i(
mesh.elem_ptr(elem_info.first));
202 transition_elems_list.begin(), transition_elems_list.end(), neighbor_elem_id);
203 if (id_found == transition_elems_list.end())
205 transition_elems_list.push_back(neighbor_elem_id);
206 transition_elems_sides_list.push_back(
207 std::vector<unsigned int>({neighbor_elem_side_index}));
212 const auto index = std::distance(transition_elems_list.begin(), id_found);
213 transition_elems_sides_list[index].push_back(neighbor_elem_side_index);
221 std::vector<dof_id_type> converted_elems_ids_to_cut;
224 cross_and_remained_elems_pre_convert,
225 converted_elems_ids_to_cut,
230 auto & sideset_map =
mesh.get_boundary_info().get_sideset_map();
231 for (
const auto & i_elem :
index_range(transition_elems_list))
233 const auto & elem_id = transition_elems_list[i_elem];
234 const auto & side_indices = transition_elems_sides_list[i_elem];
236 std::vector<std::vector<boundary_id_type>> elem_side_info(
mesh.elem_ptr(elem_id)->n_sides());
237 auto side_range = sideset_map.equal_range(
mesh.elem_ptr(elem_id));
238 for (
auto i = side_range.first; i != side_range.second; ++i)
239 elem_side_info[i->second.first].push_back(i->second.second);
242 mesh, elem_id, side_indices, elem_side_info, sid_shift_base);
245 std::vector<const Node *> new_on_plane_nodes;
248 BoundaryInfo & boundary_info =
mesh.get_boundary_info();
249 const auto bdry_side_list = boundary_info.build_side_list();
251 for (
const auto & converted_elems_id_to_cut : converted_elems_ids_to_cut)
254 mesh, bdry_side_list, converted_elems_id_to_cut, block_id_to_remove, new_on_plane_nodes);
264 original_subdomain_ids,
269 catch (
const std::exception & e)
271 if (((std::string)e.what()).compare(26, 4,
"TET4") == 0)
272 paramError(
"converted_tet_element_subdomain_name_suffix", e.what());
274 paramError(
"converted_pyramid_element_subdomain_name_suffix", e.what());
279 for (
const auto & elem_id : transition_elems_list)
280 mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
282 for (
auto elem_it =
mesh.active_subdomain_elements_begin(block_id_to_remove);
283 elem_it !=
mesh.active_subdomain_elements_end(block_id_to_remove);
285 mesh.delete_elem(*elem_it);
288 mesh.unset_is_prepared();
296 if (MooseUtils::absoluteFuzzyLessThan(level_set_value, 0.0))
298 else if (MooseUtils::absoluteFuzzyGreaterThan(level_set_value, 0.0))
306 const Point & point2)
311 if (MooseUtils::absoluteFuzzyEqual(dist1, 0.0) || MooseUtils::absoluteFuzzyEqual(dist2, 0.0))
312 mooseError(
"At least one of the two points are on the plane.");
313 if (MooseUtils::absoluteFuzzyGreaterThan(dist1 * dist2, 0.0))
314 mooseError(
"The two points are on the same side of the plane.");
322 while (MooseUtils::absoluteFuzzyGreaterThan(dist, 0.0))
324 mid_point = 0.5 * (p1 + p2);
329 else if (dist_mid * dist1 < 0.0)
333 dist =
abs(dist1) +
abs(dist2);
339 dist =
abs(dist1) +
abs(dist2);
347 ReplicatedMesh & mesh,
348 std::vector<const Node *> & new_on_plane_nodes,
349 const Point & new_point)
const 351 for (
const auto & new_on_plane_node : new_on_plane_nodes)
353 if (MooseUtils::absoluteFuzzyEqual((*new_on_plane_node - new_point).
norm(), 0.0))
354 return new_on_plane_node;
356 new_on_plane_nodes.push_back(
mesh.add_point(new_point));
357 return new_on_plane_nodes.back();
362 ReplicatedMesh & mesh,
363 const std::vector<libMesh::BoundaryInfo::BCTuple> & bdry_side_list,
366 std::vector<const Node *> & new_on_plane_nodes)
369 BoundaryInfo & boundary_info =
mesh.get_boundary_info();
375 std::vector<Point> elem_side_normal_list;
376 elem_side_normal_list.resize(4);
379 auto elem_side =
mesh.elem_ptr(elem_id)->side_ptr(i);
380 elem_side_normal_list[i] = (*elem_side->node_ptr(1) - *elem_side->node_ptr(0))
381 .cross(*elem_side->node_ptr(2) - *elem_side->node_ptr(1))
385 std::vector<std::vector<boundary_id_type>> elem_side_list;
387 bdry_side_list, elem_id, 4, elem_side_list);
389 std::vector<PointLevelSetRelationIndex> node_plane_relation(4);
390 std::vector<const Node *> tet4_nodes(4);
391 std::vector<const Node *> tet4_nodes_on_plane;
392 std::vector<const Node *> tet4_nodes_outside_plane;
393 std::vector<const Node *> tet4_nodes_inside_plane;
395 for (
unsigned int i = 0; i < 4; i++)
397 tet4_nodes[i] =
mesh.elem_ptr(elem_id)->node_ptr(i);
400 tet4_nodes_on_plane.push_back(tet4_nodes[i]);
402 tet4_nodes_outside_plane.push_back(tet4_nodes[i]);
404 tet4_nodes_inside_plane.push_back(tet4_nodes[i]);
406 std::vector<Elem *> elems_tet4;
407 bool new_elements_created(
false);
409 if (tet4_nodes_outside_plane.size() == 0)
411 if (tet4_nodes_on_plane.size() == 3)
414 elems_tet4.push_back(
mesh.elem_ptr(elem_id));
418 else if (tet4_nodes_inside_plane.size() == 0)
420 mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
421 if (tet4_nodes_on_plane.size() == 3)
430 new_elements_created =
true;
431 if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 3)
433 std::vector<const Node *> new_plane_nodes;
435 for (
const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
442 auto new_elem_tet4 = std::make_unique<Tet4>();
443 new_elem_tet4->set_node(0, const_cast<Node *>(tet4_nodes_inside_plane[0]));
444 new_elem_tet4->set_node(1, const_cast<Node *>(new_plane_nodes[0]));
445 new_elem_tet4->set_node(2, const_cast<Node *>(new_plane_nodes[1]));
446 new_elem_tet4->set_node(3, const_cast<Node *>(new_plane_nodes[2]));
447 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
448 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
450 else if (tet4_nodes_inside_plane.size() == 2 && tet4_nodes_outside_plane.size() == 2)
452 std::vector<const Node *> new_plane_nodes;
454 for (
const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
456 for (
const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
464 std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[1],
467 tet4_nodes_inside_plane[0],
470 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
471 std::vector<std::vector<const Node *>> optimized_node_list;
473 new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
475 for (
unsigned int i = 0; i < optimized_node_list.size(); i++)
477 auto new_elem_tet4 = std::make_unique<Tet4>();
478 new_elem_tet4->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
479 new_elem_tet4->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
480 new_elem_tet4->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
481 new_elem_tet4->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
482 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
483 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
486 else if (tet4_nodes_inside_plane.size() == 3 && tet4_nodes_outside_plane.size() == 1)
488 std::vector<const Node *> new_plane_nodes;
490 for (
const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
497 std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[0],
498 tet4_nodes_inside_plane[1],
499 tet4_nodes_inside_plane[2],
503 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
504 std::vector<std::vector<const Node *>> optimized_node_list;
506 new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
508 for (
unsigned int i = 0; i < optimized_node_list.size(); i++)
510 auto new_elem_tet4 = std::make_unique<Tet4>();
511 new_elem_tet4->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
512 new_elem_tet4->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
513 new_elem_tet4->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
514 new_elem_tet4->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
515 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
516 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
519 else if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 1)
526 auto new_elem_tet4 = std::make_unique<Tet4>();
527 new_elem_tet4->set_node(0, const_cast<Node *>(new_plane_node));
528 new_elem_tet4->set_node(1, const_cast<Node *>(tet4_nodes_on_plane[0]));
529 new_elem_tet4->set_node(2, const_cast<Node *>(tet4_nodes_on_plane[1]));
530 new_elem_tet4->set_node(3, const_cast<Node *>(tet4_nodes_inside_plane[0]));
531 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
532 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
534 else if (tet4_nodes_inside_plane.size() == 1 && tet4_nodes_outside_plane.size() == 2)
536 std::vector<const Node *> new_plane_nodes;
538 for (
const auto & tet4_node_outside_plane : tet4_nodes_outside_plane)
545 auto new_elem_tet4 = std::make_unique<Tet4>();
546 new_elem_tet4->set_node(0, const_cast<Node *>(new_plane_nodes[0]));
547 new_elem_tet4->set_node(1, const_cast<Node *>(new_plane_nodes[1]));
548 new_elem_tet4->set_node(2, const_cast<Node *>(tet4_nodes_on_plane[0]));
549 new_elem_tet4->set_node(3, const_cast<Node *>(tet4_nodes_inside_plane[0]));
550 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
551 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
553 else if (tet4_nodes_inside_plane.size() == 2 && tet4_nodes_outside_plane.size() == 1)
555 std::vector<const Node *> new_plane_nodes;
557 for (
const auto & tet4_node_inside_plane : tet4_nodes_inside_plane)
564 std::vector<const Node *> new_elems_nodes = {tet4_nodes_inside_plane[0],
565 tet4_nodes_inside_plane[1],
568 tet4_nodes_on_plane[0]};
569 std::vector<std::vector<unsigned int>> rotated_tet_face_indices;
570 std::vector<std::vector<const Node *>> optimized_node_list;
572 new_elems_nodes, rotated_tet_face_indices, optimized_node_list);
574 for (
unsigned int i = 0; i < optimized_node_list.size(); i++)
576 auto new_elem_tet4 = std::make_unique<Tet4>();
577 new_elem_tet4->set_node(0, const_cast<Node *>(optimized_node_list[i][0]));
578 new_elem_tet4->set_node(1, const_cast<Node *>(optimized_node_list[i][1]));
579 new_elem_tet4->set_node(2, const_cast<Node *>(optimized_node_list[i][2]));
580 new_elem_tet4->set_node(3, const_cast<Node *>(optimized_node_list[i][3]));
581 new_elem_tet4->subdomain_id() =
mesh.elem_ptr(elem_id)->subdomain_id();
582 elems_tet4.push_back(
mesh.add_elem(std::move(new_elem_tet4)));
588 mesh.elem_ptr(elem_id)->subdomain_id() = block_id_to_remove;
591 for (
auto & elem_tet4 : elems_tet4)
593 if (new_elements_created)
595 if (elem_tet4->volume() < 0.0)
597 Node * temp = elem_tet4->node_ptr(0);
598 elem_tet4->set_node(0, elem_tet4->node_ptr(1));
599 elem_tet4->set_node(1, temp);
603 for (
unsigned int i = 0; i < 4; i++)
605 const Point & side_pt_0 = *elem_tet4->side_ptr(i)->node_ptr(0);
606 const Point & side_pt_1 = *elem_tet4->side_ptr(i)->node_ptr(1);
607 const Point & side_pt_2 = *elem_tet4->side_ptr(i)->node_ptr(2);
609 const Point side_normal = (side_pt_1 - side_pt_0).cross(side_pt_2 - side_pt_1).unit();
610 for (
unsigned int j = 0; j < 4; j++)
612 if (new_elements_created)
614 if (MooseUtils::absoluteFuzzyEqual(side_normal * elem_side_normal_list[j], 1.0))
616 for (
const auto & side_info : elem_side_list[j])
618 boundary_info.add_side(elem_tet4, i, side_info);
void assignConvertedElementsSubdomainNameSuffix(MeshBase &mesh, const std::set< subdomain_id_type > &original_subdomain_ids, const subdomain_id_type sid_shift_base, const SubdomainName &tet_suffix, const SubdomainName &pyramid_suffix)
Assign a subdomain name suffix to the converted elements created during transition layer generation...
std::unique_ptr< MeshBase > & _input
Reference to input mesh pointer.
const SubdomainName _converted_pyramid_element_subdomain_name_suffix
The suffix to be added to the original subdomain name for the subdomains containing the elements conv...
GenericReal< is_ad > evaluate(SymFunctionPtr &, const std::string &object_name="")
Evaluate FParser object and check EvalError.
MetaPhysicL::DualNumber< V, D, asd > abs(const MetaPhysicL::DualNumber< V, D, asd > &a)
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
Find a value in an array.
bool hasBoundaryName(const MeshBase &input_mesh, const BoundaryName &name)
Whether a particular boundary name exists in the mesh.
void paramError(const std::string ¶m, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
SymFunctionPtr _func_level_set
function parser object describing the level set
void elementBoundaryInfoCollector(const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, const unsigned short n_elem_sides, std::vector< std::vector< boundary_id_type >> &elem_side_list)
Collect the boundary information of the given element in a mesh.
PointLevelSetRelationIndex pointLevelSetRelation(const Point &point)
Evaluate whether a point is on the level set, inside or outside the level set.
const bool _generate_transition_layer
Whether to generate a transition layer for the cut mesh.
const BoundaryName _cut_face_name
The boundary name of the surface generated by the cut.
Real levelSetEvaluator(const Point &point)
Evaluate the level set function at a given point.
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
const SubdomainName _converted_tet_element_subdomain_name_suffix
The suffix to be added to the original subdomain name for the subdomains containing the elements conv...
void convert3DMeshToAllTet4(MeshBase &mesh, const std::vector< std::pair< dof_id_type, bool >> &elems_to_process, std::vector< dof_id_type > &converted_elems_ids_to_track, const subdomain_id_type block_id_to_remove, const bool delete_block_to_remove)
Convert all the elements in a 3D mesh, consisting of only linear elements, into TET4 elements...
PointLevelSetRelationIndex
An enum class for style of input polygon size.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
Point pointPairLevelSetInterception(const Point &point1, const Point &point2)
Calculate the intersection point of a level set and a line segment defined by two points separated by...
static InputParameters validParams()
const Node * nonDuplicateNodeCreator(ReplicatedMesh &mesh, std::vector< const Node *> &new_on_plane_nodes, const Point &new_point) const
Check if a position on a plane has already been used as a node in the mesh.
CutMeshByLevelSetGeneratorBase(const InputParameters ¶meters)
boundary_id_type _cut_face_id
The boundary id of the surface generated by the cut.
void convertElem(MeshBase &mesh, const dof_id_type &elem_id, const std::vector< unsigned int > &side_indices, const std::vector< std::vector< boundary_id_type >> &elem_side_info, const SubdomainID &subdomain_id_shift_base)
Convert the element to an element with TRI3 side-elements on the user-specified sides by modifying th...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void pyramidNodesToTetNodesDeterminer(std::vector< const Node *> &pyramid_nodes, std::vector< std::vector< unsigned int >> &rotated_tet_face_indices, std::vector< std::vector< const Node *>> &tet_nodes_list)
For a rotated nodes that can form a PYRAMID5 element, create a series of four-node set that can form ...
std::vector< GenericReal< is_ad > > _func_params
Array to stage the parameters passed to the functions when calling Eval.
IntRange< T > make_range(T beg, T end)
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
void tet4ElemCutter(ReplicatedMesh &mesh, const std::vector< libMesh::BoundaryInfo::BCTuple > &bdry_side_list, const dof_id_type elem_id, const subdomain_id_type &block_id_to_remove, std::vector< const Node *> &new_on_plane_nodes)
For a TET4 elements crossed by the level set, keep the part of the element on the retaining side of t...
static InputParameters validParams()
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
static InputParameters validParams()
SubdomainID getNextFreeSubdomainID(MeshBase &input_mesh)
Checks input mesh and returns max(block ID) + 1, which represents a block ID that is not currently in...
void prismNodesToTetNodesDeterminer(std::vector< const Node *> &prism_nodes, std::vector< std::vector< unsigned int >> &rotated_tet_face_indices, std::vector< std::vector< const Node *>> &tet_nodes_list)
For a rotated nodes that can form a PRISM6 element, create a series of four-node set that can form TE...
BoundaryID getNextFreeBoundaryID(MeshBase &input_mesh)
Checks input mesh and returns the largest boundary ID in the mesh plus one, which is a boundary ID in...
MeshGenerators are objects that can modify or add to an existing mesh.
auto index_range(const T &sizable)