35{
36
37 std::unique_ptr<UnstructuredMesh>
mesh =
38 dynamic_pointer_cast<UnstructuredMesh>(std::move(boundary_mesh));
39
40
42 poly2tri.triangulation_type() = libMesh::TriangulatorInterface::PSLG;
43
44
45
46 std::set<std::size_t> bdy_ids;
47
49 {
52 "input_subdomain_names",
53 "input_boundary_names and input_subdomain_names cannot both specify an outer boundary.");
54
55 for (const auto & name : xyd_opts.input_boundary_names)
56 {
59 mg.
paramError(
"input_boundary_names", name,
" is not a boundary name in the input mesh");
60
61 bdy_ids.insert(bcid);
62 }
63 }
64
66 {
67
70
71 const auto subdomain_ids =
73
74
75 std::set<SubdomainID> subdomains;
77
79 {
83 " was not found in the boundary mesh");
84
85 bdy_ids.insert(subdomain_ids[i]);
86 }
87 }
88
89 if (!bdy_ids.empty())
90 poly2tri.set_outer_boundary_ids(bdy_ids);
91
93 poly2tri.set_refine_boundary_allowed(xyd_opts.
refine_bdy);
94 poly2tri.set_verify_hole_boundaries(xyd_opts.
verify_holes);
95
97 poly2tri.minimum_angle() = 0;
98 poly2tri.smooth_after_generating() = xyd_opts.
smooth_tri;
99
100 std::vector<libMesh::TriangulatorInterface::MeshedHole> meshed_holes;
101 std::vector<libMesh::TriangulatorInterface::Hole *> triangulator_hole_ptrs(hole_meshes.size());
102
103
104 std::vector<bool> holes_with_midpoints(hole_meshes.size());
105 bool stitch_second_order_holes(false);
106
107
108 meshed_holes.reserve(hole_meshes.size());
110 {
111 if (!hole_meshes[hole_i]->is_prepared())
112 hole_meshes[hole_i]->prepare_for_use();
116 else
117 meshed_holes.emplace_back(*hole_meshes[hole_i]);
118 holes_with_midpoints[hole_i] = meshed_holes.back().n_midpoints();
119 stitch_second_order_holes =
121 ? false
122 : ((holes_with_midpoints[hole_i] && xyd_opts.
stitch_holes[hole_i]) ||
123 stitch_second_order_holes);
125 meshed_holes.back().set_refine_boundary_allowed(xyd_opts.
refine_holes[hole_i]);
126
127 triangulator_hole_ptrs[hole_i] = &meshed_holes.back();
128 }
129 if (stitch_second_order_holes &&
132 "tri_element_type",
133 "Cannot use first order elements with stitched quadratic element holes. Please try "
134 "to specify a higher-order tri_element_type or reduce the order of the hole inputs.");
135
136 if (!triangulator_hole_ptrs.empty())
137 poly2tri.attach_hole_list(&triangulator_hole_ptrs);
138
140 {
141
143 poly2tri.set_desired_area_function(&area_func);
144 }
146 {
147 poly2tri.set_auto_area_function(
153 : -1.0);
154 }
155
157 poly2tri.elem_type() = libMesh::ElemType::TRI6;
159 poly2tri.elem_type() = libMesh::ElemType::TRI7;
160
161
162 for (const auto & point : xyd_opts.interior_points)
163 mesh->add_point(point);
164
165 poly2tri.triangulate();
166
169
171 {
173
174 if (id == Elem::invalid_subdomain_id)
175 {
177 {
178
180
181
182 for (auto & hole_ptr : hole_meshes)
183 {
184 auto possible_sbdid =
186
187 if (possible_sbdid != Elem::invalid_subdomain_id)
188 {
189 output_subdomain_id = possible_sbdid;
190 break;
191 }
192 output_subdomain_id =
194 }
195 }
196 }
197 else
198 {
200 {
201 if (id != output_subdomain_id)
203 "name has been used by the input meshes and the corresponding id is not "
204 "equal to 'output_subdomain_id'");
205 }
206 else
207 output_subdomain_id = id;
208 }
209
212 }
213
214 if (xyd_opts.
smooth_tri || output_subdomain_id)
215 for (
auto elem :
mesh->element_ptr_range())
216 {
217 mooseAssert(elem->type() == (xyd_opts.
tri_elem_type ==
"TRI6"
219 : (xyd_opts.tri_elem_type ==
"TRI7" ?
TRI7 :
TRI3)),
221 << " found in triangulation");
222
223 elem->subdomain_id() = output_subdomain_id;
224
225
226
227
228
229
231 {
232 auto cross_prod = (elem->point(1) - elem->point(0)).cross(elem->point(2) - elem->point(0));
233
234 if (cross_prod(2) <= 0)
235 mooseError(
"Inverted element found in triangulation.\n"
236 "Laplacian smoothing can create these at reentrant corners; disable it?");
237 }
238 }
239
240
241
242
243
244
245
246
247
248
249
250
252
253
254
255 std::vector<BoundaryID> hole_boundary_rec(hole_meshes.size());
256 std::iota(hole_boundary_rec.begin(), hole_boundary_rec.end(), 1);
257
258
259
260
263 {
265 {
267 {
268 free_boundary_id =
270 hole_meshes[hole_i]->comm().max(free_boundary_id);
271 }
272 }
274 {
276 hole_boundary_rec[h] = h + 1 + free_boundary_id;
277 }
278 }
280
281
282
283
284
287
289 {
291
294 *
mesh, h + 1 + free_boundary_id, h + 1 + free_boundary_id + end_bcid);
295
297 {
299 *mesh, h + 1 + free_boundary_id + end_bcid, hole_boundary_ids[h]);
300 hole_boundary_rec[h] = hole_boundary_ids[h];
302 new_hole_bcid = std::max(new_hole_bcid,
boundary_id_type(hole_boundary_ids[h] + 1));
303 }
304 }
305
307 {
308 const std::vector<BoundaryID> output_boundary_id =
310
312 *mesh, end_bcid + free_boundary_id, output_boundary_id[0]);
314
315 new_hole_bcid = std::max(new_hole_bcid,
boundary_id_type(output_boundary_id[0] + 1));
316 }
317
318 bool doing_stitching = false;
319
321 {
322 const MeshBase & hole_mesh = *hole_meshes[hole_i];
324 const std::set<boundary_id_type> & local_hole_bcids = hole_boundary_info.
get_boundary_ids();
325
326 if (!local_hole_bcids.empty())
327 new_hole_bcid = std::max(new_hole_bcid,
boundary_id_type(*local_hole_bcids.rbegin() + 1));
328 hole_mesh.
comm().
max(new_hole_bcid);
329
331 doing_stitching = true;
332 }
333
335
336
337
339
340
343 {
345 {
347
348 if (!holes_with_midpoints[hole_i])
349 {
354 }
356
357
358
359
360
362
363
364
365
366
367
370 :
std::set<
std::size_t>();
372
373
374
375 std::unordered_map<Point, Point> next_hole_boundary_point;
376 const int np = mh.n_points();
378 next_hole_boundary_point[mh.point(
pi - 1)] = mh.point(
pi);
379 next_hole_boundary_point[mh.point(np - 1)] = mh.
point(0);
380
381#ifndef NDEBUG
382 int found_hole_sides = 0;
383#endif
384 for (auto elem : hole_mesh.element_ptr_range())
385 {
386 if (elem->dim() != 2)
387 mooseError(
"Non 2-D element found in hole; stitching is not supported.");
388
389 auto ns = elem->n_sides();
391 {
392 auto it_s = next_hole_boundary_point.find(elem->point(s));
393 if (it_s != next_hole_boundary_point.end())
394 if (it_s->second == elem->point((s + 1) % ns))
395 {
396 hole_boundary_info.add_side(elem, s, new_hole_bcid);
397#ifndef NDEBUG
398 ++found_hole_sides;
399#endif
400 }
401 }
402 }
403 mooseAssert(found_hole_sides == np, "Failed to find full outer boundary of meshed hole");
404
406#ifndef NDEBUG
407 int found_inner_sides = 0;
408#endif
409 for (
auto elem :
mesh->element_ptr_range())
410 {
411 auto ns = elem->n_sides();
413 {
414 auto it_s = next_hole_boundary_point.find(elem->point((s + 1) % ns));
415 if (it_s != next_hole_boundary_point.end())
416 if (it_s->second == elem->point(s))
417 {
418 mesh_boundary_info.
add_side(elem, s, inner_bcid);
419#ifndef NDEBUG
420 ++found_inner_sides;
421#endif
422 }
423 }
424 }
425 mooseAssert(found_inner_sides == np, "Failed to find full boundary around meshed hole");
426
427
428
430 main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end());
431
432
433 for (const auto & bcid : hole_bdy_id_filter)
434 hole_boundary_info.remove_id(bcid);
435
436 if (hole_bdy_id_filter.size())
437 {
439 hole_mesh,
441 ? 1
442 : *xyd_opts.hole_boundary_inner_id_defaults[hole_i].begin(),
443 hole_boundary_rec[hole_i],
444 true);
448 }
449
450 mesh->stitch_meshes(hole_mesh,
451 inner_bcid,
452 new_hole_bcid,
454 true,
457 }
458 }
459
460 std::set<SubdomainName> main_subdomain_map_name_list;
461 for (auto const & id_name_pair : main_subdomain_map)
462 main_subdomain_map_name_list.emplace(id_name_pair.second);
463 if (main_subdomain_map.size() != main_subdomain_map_name_list.size())
464 mg.
paramError(
"holes",
"The hole meshes contain subdomain name maps with conflicts.");
465
468}
boundary_id_type BoundaryID
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
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 ...
void max(const T &r, T &o, Request &req) const
std::string & sideset_name(boundary_id_type id)
void remove_id(boundary_id_type id, bool global=false)
const std::set< boundary_id_type > & get_boundary_ids() const
static const boundary_id_type invalid_id
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
void set_subdomain_name(subdomain_id_type id, const std::string &name, bool synchronous=false)
virtual const Point & point(const dof_id_type i) const=0
const BoundaryInfo & get_boundary_info() const
Preparation preparation() const
void subdomain_ids(std::set< subdomain_id_type > &ids, const bool global=true) const
const std::map< subdomain_id_type, std::string > & get_subdomain_name_map() const
void all_second_order(const bool full_ordered=true)
virtual void all_complete_order()
std::map< subdomain_id_type, std::string > & set_subdomain_name_map()
const Parallel::Communicator & comm() const
void changeBoundaryId(MeshBase &mesh, const libMesh::boundary_id_type old_id, const libMesh::boundary_id_type new_id, bool delete_prev)
Changes the old boundary ID to a new ID in the mesh.
std::vector< subdomain_id_type > getSubdomainIDs(const libMesh::MeshBase &mesh, const std::vector< SubdomainName > &subdomain_name)
Get the associated subdomainIDs for the subdomain names that are passed in.
std::vector< BoundaryID > getBoundaryIDs(const libMesh::MeshBase &mesh, const std::vector< BoundaryName > &boundary_name, bool generate_unknown, const std::set< BoundaryID > &mesh_boundary_ids)
Gets the boundary IDs with their names.
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...
SubdomainID getNextFreeSubdomainID(MeshBase &input_mesh)
Checks input mesh and returns max(block ID) + 1, which represents a block ID that is not currently in...
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
SubdomainID getSubdomainID(const SubdomainName &subdomain_name, const MeshBase &mesh)
Gets the subdomain ID associated with the given SubdomainName.
const SubdomainID INVALID_BLOCK_ID
std::string enum_to_string(const T e)
The following methods are specializations for using the libMesh::Parallel::packed_range_* routines fo...
auto index_range(const T &sizable)
static constexpr Real TOLERANCE
IntRange< T > make_range(T beg, T end)
Real auto_area_func_default_size
SubdomainName output_subdomain_name
std::vector< SubdomainName > input_subdomain_names
unsigned int auto_area_function_num_points
std::vector< BoundaryName > hole_boundaries
std::vector< std::set< std::size_t > > hole_boundary_id_filters
std::vector< std::set< BoundaryID > > hole_boundary_inner_id_defaults
std::string tri_elem_type
Real auto_area_func_default_size_dist
std::vector< bool > stitch_holes
BoundaryName output_boundary
Real auto_area_function_power
std::vector< BoundaryName > input_boundary_names
std::vector< bool > refine_holes
bool has_output_subdomain_name
bool has_output_subdomain_id
SubdomainID output_subdomain_id
std::string desired_area_func
unsigned int add_nodes_per_boundary_segment