https://mooseframework.inl.gov
Loading...
Searching...
No Matches
Classes | Functions
MeshTriangulationUtils Namespace Reference

Classes

struct  XYDelaunayOptions
 Bundle of inputs for triangulateWithDelaunay. More...
 

Functions

std::set< std::size_t > outerBoundaryIds (MeshGenerator &mg, MeshBase &boundary_mesh, const XYDelaunayOptions &opts)
 Resolves the outer-boundary selection of the options into the set of ids that define it: the ids of 'input_boundary_names' or of 'input_subdomain_names' in the boundary mesh.
 
std::unique_ptr< MeshBase > triangulateWithDelaunay (MeshGenerator &mg, std::unique_ptr< MeshBase > boundary_mesh, std::vector< std::unique_ptr< MeshBase > > hole_meshes, const XYDelaunayOptions &xyd_opts)
 Performs a 2D Delaunay triangulation (via libMesh::Poly2TriTriangulator) inside a closed boundary mesh with optional holes, optionally stitches the resulting triangulation to each hole mesh that requests it, and applies subdomain / boundary renumbering.
 
void finalizeTriangulation (MeshGenerator &mg, UnstructuredMesh &mesh, std::vector< std::unique_ptr< MeshBase > > &holes, const std::vector< bool > &holes_with_midpoints, const XYDelaunayOptions &opts)
 Performs the subdomain and boundary naming, the boundary id remapping (outer boundary to 0 and hole i to i + 1), and the hole stitching on a mesh that has already been triangulated.
 

Function Documentation

◆ finalizeTriangulation()

void MeshTriangulationUtils::finalizeTriangulation ( MeshGenerator mg,
UnstructuredMesh &  mesh,
std::vector< std::unique_ptr< MeshBase > > &  holes,
const std::vector< bool > &  holes_with_midpoints,
const XYDelaunayOptions opts 
)

Performs the subdomain and boundary naming, the boundary id remapping (outer boundary to 0 and hole i to i + 1), and the hole stitching on a mesh that has already been triangulated.

This is shared by the Poly2Tri-backed and the frontal triangulators.

Parameters
mgThe calling mesh generator (used for paramError reporting)
meshThe triangulated mesh, modified in place
holesThe hole meshes used by the triangulation (empty vector if no holes)
holes_with_midpointsWhether the boundary of each hole carries midpoints, as reported by the libMesh MeshedHole built from that hole before the triangulation
optsTriangulation options (see XYDelaunayOptions)

Definition at line 186 of file MeshTriangulationUtils.C.

191{
192 SubdomainID output_subdomain_id = opts.has_output_subdomain_id ? opts.output_subdomain_id : 0;
193
195 {
197
198 if (id == Elem::invalid_subdomain_id)
199 {
200 if (!opts.has_output_subdomain_id)
201 {
202 // We'll probably need to make a new ID, then
203 output_subdomain_id = MooseMeshUtils::getNextFreeSubdomainID(mesh);
204
205 // But check the hole meshes for our output subdomain name too
206 for (auto & hole_ptr : holes)
207 {
208 auto possible_sbdid =
210 // Huh, it was in one of them
211 if (possible_sbdid != Elem::invalid_subdomain_id)
212 {
213 output_subdomain_id = possible_sbdid;
214 break;
215 }
216 output_subdomain_id =
217 std::max(output_subdomain_id, MooseMeshUtils::getNextFreeSubdomainID(*hole_ptr));
218 }
219 }
220 }
221 else
222 {
224 {
225 if (id != output_subdomain_id)
226 mg.paramError("output_subdomain_name",
227 "name has been used by the input meshes and the corresponding id is not "
228 "equal to 'output_subdomain_id'");
229 }
230 else
231 output_subdomain_id = id;
232 }
233 // We do not want to set an empty subdomain name
234 if (opts.output_subdomain_name.size())
235 mesh.set_subdomain_name(output_subdomain_id, opts.output_subdomain_name);
236 }
237
238 if (opts.smooth_tri || output_subdomain_id)
239 for (auto elem : mesh.element_ptr_range())
240 {
241 mooseAssert(
242 elem->type() ==
243 (opts.tri_elem_type == "TRI6" ? TRI6 : (opts.tri_elem_type == "TRI7" ? TRI7 : TRI3)),
244 "Unexpected element type " << libMesh::Utility::enum_to_string(elem->type())
245 << " found in triangulation");
246
247 elem->subdomain_id() = output_subdomain_id;
248
249 // I do not trust Laplacian mesh smoothing not to invert
250 // elements near reentrant corners. Eventually we'll add better
251 // smoothing options, but even those might have failure cases.
252 // Better to always do extra tests here than to ever let users
253 // try to run on a degenerate mesh.
254 if (opts.smooth_tri)
255 {
256 auto cross_prod = (elem->point(1) - elem->point(0)).cross(elem->point(2) - elem->point(0));
257
258 if (cross_prod(2) <= 0)
259 mooseError("Inverted element found in triangulation.\n"
260 "Laplacian smoothing can create these at reentrant corners; disable it?");
261 }
262 }
263
264 // The hole meshes are specified by the user, so they could have any
265 // BCID or no BCID or any combination of BCIDs on their outer
266 // boundary, so we'll have to set our own BCID to use for stitching
267 // there. We'll need to check all the holes for used BCIDs, if we
268 // want to pick a new ID on hole N that doesn't conflict with any
269 // IDs on hole M < N (or with the IDs on the new triangulation)
270
271 // The new triangulation by default assigns BCID i+1 to hole i ...
272 // but we can't even use this for mesh stitching, because we can't
273 // be sure it isn't also already in use on the hole's mesh and so we
274 // won't be able to safely clear it afterwards.
275 const boundary_id_type end_bcid = holes.size() + 1;
276
277 // If a hole has its boundary layer mesh, we need to move the hole bcid to the "real" hole
278 // boundary in the boundary layer mesh. So we need to record them here.
279 std::vector<BoundaryID> hole_boundary_rec(holes.size());
280 std::iota(hole_boundary_rec.begin(), hole_boundary_rec.end(), 1);
281
282 // For the hole meshes that need to be stitched, we would like to make sure the hole boundary ids
283 // and output boundary id are not conflicting with the existing boundary ids of the hole meshes to
284 // be stitched.
285 BoundaryID free_boundary_id = 0;
286 if (opts.stitch_holes.size())
287 {
288 for (auto hole_i : index_range(holes))
289 {
290 if (opts.stitch_holes[hole_i])
291 {
292 free_boundary_id =
293 std::max(free_boundary_id, MooseMeshUtils::getNextFreeBoundaryID(*holes[hole_i]));
294 holes[hole_i]->comm().max(free_boundary_id);
295 }
296 }
297 for (auto h : index_range(holes))
298 {
299 libMesh::MeshTools::Modification::change_boundary_id(mesh, h + 1, h + 1 + free_boundary_id);
300 hole_boundary_rec[h] = h + 1 + free_boundary_id;
301 }
302 }
303 boundary_id_type new_hole_bcid = end_bcid + free_boundary_id;
304
305 // We might be overriding the default bcid numbers. We have to be
306 // careful about how we renumber, though. We pick unused temporary
307 // numbers because e.g. "0->2, 2->0" is impossible to do
308 // sequentially, but "0->N, 2->N+2, N->2, N+2->0" works.
310 mesh, 0, (opts.has_output_boundary ? end_bcid : 0) + free_boundary_id);
311
312 if (!opts.hole_boundaries.empty())
313 {
314 auto hole_boundary_ids = MooseMeshUtils::getBoundaryIDs(mesh, opts.hole_boundaries, true);
315
316 for (auto h : index_range(holes))
317 libMesh::MeshTools::Modification::change_boundary_id(
318 mesh, h + 1 + free_boundary_id, h + 1 + free_boundary_id + end_bcid);
319
320 for (auto h : index_range(holes))
321 {
323 mesh, h + 1 + free_boundary_id + end_bcid, hole_boundary_ids[h]);
324 hole_boundary_rec[h] = hole_boundary_ids[h];
325 mesh.get_boundary_info().sideset_name(hole_boundary_ids[h]) = opts.hole_boundaries[h];
326 new_hole_bcid = std::max(new_hole_bcid, boundary_id_type(hole_boundary_ids[h] + 1));
327 }
328 }
329
330 if (opts.has_output_boundary)
331 {
332 const std::vector<BoundaryID> output_boundary_id =
334
336 mesh, end_bcid + free_boundary_id, output_boundary_id[0]);
337 mesh.get_boundary_info().sideset_name(output_boundary_id[0]) = opts.output_boundary;
338
339 new_hole_bcid = std::max(new_hole_bcid, boundary_id_type(output_boundary_id[0] + 1));
340 }
341
342 bool doing_stitching = false;
343
344 for (auto hole_i : index_range(holes))
345 {
346 const MeshBase & hole_mesh = *holes[hole_i];
347 auto & hole_boundary_info = hole_mesh.get_boundary_info();
348 const std::set<boundary_id_type> & local_hole_bcids = hole_boundary_info.get_boundary_ids();
349
350 if (!local_hole_bcids.empty())
351 new_hole_bcid = std::max(new_hole_bcid, boundary_id_type(*local_hole_bcids.rbegin() + 1));
352 hole_mesh.comm().max(new_hole_bcid);
353
354 if (hole_i < opts.stitch_holes.size() && opts.stitch_holes[hole_i])
355 doing_stitching = true;
356 }
357
358 const boundary_id_type inner_bcid = new_hole_bcid + 1;
359
360 // libMesh mesh stitching still requires a serialized mesh, and it's
361 // cheaper to do that once than to do it once-per-hole
362 libMesh::MeshSerializer serial(mesh, doing_stitching);
363
364 // Define a reference map variable for subdomain map
365 auto & main_subdomain_map = mesh.set_subdomain_name_map();
366 for (auto hole_i : index_range(holes))
367 {
368 if (hole_i < opts.stitch_holes.size() && opts.stitch_holes[hole_i])
369 {
370 UnstructuredMesh & hole_mesh = dynamic_cast<UnstructuredMesh &>(*holes[hole_i]);
371 // increase hole mesh order if the triangulation mesh has higher order
372 if (!holes_with_midpoints[hole_i])
373 {
374 if (opts.tri_elem_type == "TRI6")
375 hole_mesh.all_second_order();
376 else if (opts.tri_elem_type == "TRI7")
377 hole_mesh.all_complete_order();
378 }
379 auto & hole_boundary_info = hole_mesh.get_boundary_info();
380
381 // Our algorithm here requires a serialized Mesh. To avoid
382 // redundant serialization and deserialization (libMesh
383 // MeshedHole and stitch_meshes still also require
384 // serialization) we'll do the serialization up front.
385 libMesh::MeshSerializer serial_hole(hole_mesh);
386
387 // It would have been nicer for MeshedHole to add the BCID
388 // itself, but we want MeshedHole to work with a const mesh.
389 // We'll still use MeshedHole, for its code distinguishing
390 // outer boundaries from inner boundaries on a
391 // hole-with-holes.
392 const auto & hole_bdy_id_filter = (hole_i < opts.hole_boundary_id_filters.size())
393 ? opts.hole_boundary_id_filters[hole_i]
394 : std::set<std::size_t>();
395 libMesh::TriangulatorInterface::MeshedHole mh{hole_mesh, hole_bdy_id_filter};
396
397 // We have to translate from MeshedHole points to mesh
398 // sides.
399 std::unordered_map<Point, Point> next_hole_boundary_point;
400 const int np = mh.n_points();
401 for (auto pi : make_range(1, np))
402 next_hole_boundary_point[mh.point(pi - 1)] = mh.point(pi);
403 next_hole_boundary_point[mh.point(np - 1)] = mh.point(0);
404
405#ifndef NDEBUG
406 int found_hole_sides = 0;
407#endif
408 for (auto elem : hole_mesh.element_ptr_range())
409 {
410 if (elem->dim() != 2)
411 mooseError("Non 2-D element found in hole; stitching is not supported.");
412
413 auto ns = elem->n_sides();
414 for (auto s : make_range(ns))
415 {
416 auto it_s = next_hole_boundary_point.find(elem->point(s));
417 if (it_s != next_hole_boundary_point.end())
418 if (it_s->second == elem->point((s + 1) % ns))
419 {
420 hole_boundary_info.add_side(elem, s, new_hole_bcid);
421#ifndef NDEBUG
422 ++found_hole_sides;
423#endif
424 }
425 }
426 }
427 mooseAssert(found_hole_sides == np, "Failed to find full outer boundary of meshed hole");
428
429 auto & mesh_boundary_info = mesh.get_boundary_info();
430#ifndef NDEBUG
431 int found_inner_sides = 0;
432#endif
433 for (auto elem : mesh.element_ptr_range())
434 {
435 auto ns = elem->n_sides();
436 for (auto s : make_range(ns))
437 {
438 auto it_s = next_hole_boundary_point.find(elem->point((s + 1) % ns));
439 if (it_s != next_hole_boundary_point.end())
440 if (it_s->second == elem->point(s))
441 {
442 mesh_boundary_info.add_side(elem, s, inner_bcid);
443#ifndef NDEBUG
444 ++found_inner_sides;
445#endif
446 }
447 }
448 }
449 mooseAssert(found_inner_sides == np, "Failed to find full boundary around meshed hole");
450
451 // Retrieve subdomain name map from the mesh to be stitched and insert it into the main
452 // subdomain map
453 const auto & increment_subdomain_map = hole_mesh.get_subdomain_name_map();
454 main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end());
455
456 // We do not need the hole_bdy_id_filter anymore
457 for (const auto & bcid : hole_bdy_id_filter)
458 hole_boundary_info.remove_id(bcid);
459 // If we are stitching a hole boundary layer mesh, we need to reassign the bcid
460 if (hole_bdy_id_filter.size())
461 {
463 hole_mesh,
464 opts.hole_boundary_inner_id_defaults[hole_i].empty()
465 ? 1
466 : *opts.hole_boundary_inner_id_defaults[hole_i].begin(),
467 hole_boundary_rec[hole_i],
468 true);
469 hole_mesh.get_boundary_info().sideset_name(hole_boundary_rec[hole_i]) =
470 mesh.get_boundary_info().sideset_name(hole_boundary_rec[hole_i]);
471 mesh.get_boundary_info().remove_id(hole_boundary_rec[hole_i]);
472 }
473
474 mesh.stitch_meshes(hole_mesh,
475 inner_bcid,
476 new_hole_bcid,
477 TOLERANCE,
478 /*clear_stitched_bcids*/ true,
480 opts.use_binary_search);
481 }
482 }
483 // Check if one SubdomainName is shared by more than one subdomain ids
484 std::set<SubdomainName> main_subdomain_map_name_list;
485 for (auto const & id_name_pair : main_subdomain_map)
486 main_subdomain_map_name_list.emplace(id_name_pair.second);
487 if (main_subdomain_map.size() != main_subdomain_map_name_list.size())
488 mg.paramError("holes", "The hole meshes contain subdomain name maps with conflicts.");
489
490 mesh.unset_is_prepared();
491}
boundary_id_type BoundaryID
subdomain_id_type SubdomainID
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application.
Definition MooseError.h:311
void paramError(const std::string &param, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
Definition MooseBase.h:457
virtual unsigned int n_points() const override
MeshBase & mesh
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< 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...
SubdomainID getSubdomainID(const SubdomainName &subdomain_name, const MeshBase &mesh)
Gets the subdomain ID associated with the given SubdomainName.
void change_boundary_id(MeshBase &mesh, const boundary_id_type old_id, const boundary_id_type new_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)
int8_t boundary_id_type
const Real pi
IntRange< T > make_range(T beg, T end)
std::vector< std::set< std::size_t > > hole_boundary_id_filters
std::vector< std::set< BoundaryID > > hole_boundary_inner_id_defaults

Referenced by triangulateWithDelaunay().

◆ outerBoundaryIds()

std::set< std::size_t > MeshTriangulationUtils::outerBoundaryIds ( MeshGenerator mg,
MeshBase &  boundary_mesh,
const XYDelaunayOptions opts 
)

Resolves the outer-boundary selection of the options into the set of ids that define it: the ids of 'input_boundary_names' or of 'input_subdomain_names' in the boundary mesh.

This is shared by the Poly2Tri-backed and the frontal triangulators, so that both select the outer boundary the same way.

Parameters
mgThe calling mesh generator (used for paramError reporting)
boundary_meshThe mesh the names are resolved against
optsTriangulation options carrying the names (see XYDelaunayOptions)
Returns
The resolved ids, empty when neither parameter selects a boundary

Definition at line 29 of file MeshTriangulationUtils.C.

30{
31 std::set<std::size_t> bdy_ids;
32
33 if (!opts.input_boundary_names.empty())
34 {
35 if (!opts.input_subdomain_names.empty())
36 mg.paramError(
37 "input_subdomain_names",
38 "input_boundary_names and input_subdomain_names cannot both specify an outer boundary.");
39
40 for (const auto & name : opts.input_boundary_names)
41 {
42 auto bcid = MooseMeshUtils::getBoundaryID(name, boundary_mesh);
43 if (bcid == BoundaryInfo::invalid_id)
44 mg.paramError("input_boundary_names", name, " is not a boundary name in the input mesh");
45
46 bdy_ids.insert(bcid);
47 }
48 }
49
50 if (!opts.input_subdomain_names.empty())
51 {
52 // Make sure subdomain info caches are up to date
53 if (!boundary_mesh.preparation().has_cached_elem_data)
54 boundary_mesh.cache_elem_data();
55
56 const auto subdomain_ids =
58
59 // Check that the requested subdomains exist in the mesh
60 std::set<SubdomainID> subdomains;
61 boundary_mesh.subdomain_ids(subdomains);
62
63 for (auto i : index_range(subdomain_ids))
64 {
65 if (subdomain_ids[i] == Moose::INVALID_BLOCK_ID || !subdomains.count(subdomain_ids[i]))
66 mg.paramError("input_subdomain_names",
68 " was not found in the boundary mesh");
69
70 bdy_ids.insert(subdomain_ids[i]);
71 }
72 }
73
74 return bdy_ids;
75}
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.
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
const SubdomainID INVALID_BLOCK_ID
Definition MooseTypes.C:20
std::vector< SubdomainName > input_subdomain_names

Referenced by XYFrontalDelaunayGenerator::generate(), and triangulateWithDelaunay().

◆ triangulateWithDelaunay()

std::unique_ptr< MeshBase > MeshTriangulationUtils::triangulateWithDelaunay ( MeshGenerator mg,
std::unique_ptr< MeshBase >  boundary_mesh,
std::vector< std::unique_ptr< MeshBase > >  hole_meshes,
const XYDelaunayOptions xyd_opts 
)

Performs a 2D Delaunay triangulation (via libMesh::Poly2TriTriangulator) inside a closed boundary mesh with optional holes, optionally stitches the resulting triangulation to each hole mesh that requests it, and applies subdomain / boundary renumbering.

This is the core algorithm shared by XYDelaunayGenerator and XYTriangleBoundaryLayerGenerator.

Parameters
mgThe calling mesh generator (used for paramError reporting and communicator)
boundary_meshThe closed-loop boundary mesh
hole_meshesOptional hole meshes (one mesh per hole; empty vector if no holes)
xyd_optsTriangulation options (see XYDelaunayOptions)
Returns
The triangulated mesh with optionally stitched holes

Definition at line 78 of file MeshTriangulationUtils.C.

82{
83 // Put the boundary mesh in a local pointer
84 std::unique_ptr<UnstructuredMesh> mesh =
85 dynamic_pointer_cast<UnstructuredMesh>(std::move(boundary_mesh));
86
87 // Get ready to triangulate the line segments we extract from it
88 libMesh::Poly2TriTriangulator poly2tri(*mesh);
89 poly2tri.triangulation_type() = libMesh::TriangulatorInterface::PSLG;
90
91 // If we're using a user-requested subset of boundaries on that
92 // mesh, get their ids.
93 const std::set<std::size_t> bdy_ids = outerBoundaryIds(mg, *mesh, xyd_opts);
94
95 if (!bdy_ids.empty())
96 poly2tri.set_outer_boundary_ids(bdy_ids);
97
98 poly2tri.set_interpolate_boundary_points(xyd_opts.add_nodes_per_boundary_segment);
99 poly2tri.set_refine_boundary_allowed(xyd_opts.refine_bdy);
100 poly2tri.set_verify_hole_boundaries(xyd_opts.verify_holes);
101
102 poly2tri.desired_area() = xyd_opts.desired_area;
103 poly2tri.minimum_angle() = 0; // Not yet supported
104 poly2tri.smooth_after_generating() = xyd_opts.smooth_tri;
105
106 std::vector<libMesh::TriangulatorInterface::MeshedHole> meshed_holes;
107 std::vector<libMesh::TriangulatorInterface::Hole *> triangulator_hole_ptrs(hole_meshes.size());
108 // This tells us the element orders of the hole meshes
109 // For the boundary meshes, it can be access through poly2tri.segment_midpoints.
110 std::vector<bool> holes_with_midpoints(hole_meshes.size());
111 bool stitch_second_order_holes(false);
112
113 // Make sure pointers here aren't invalidated by a resize
114 meshed_holes.reserve(hole_meshes.size());
115 for (auto hole_i : index_range(hole_meshes))
116 {
117 if (!hole_meshes[hole_i]->is_prepared())
118 hole_meshes[hole_i]->prepare_for_use();
119 if (hole_i < xyd_opts.hole_boundary_id_filters.size() &&
120 !xyd_opts.hole_boundary_id_filters[hole_i].empty())
121 meshed_holes.emplace_back(*hole_meshes[hole_i], xyd_opts.hole_boundary_id_filters[hole_i]);
122 else
123 meshed_holes.emplace_back(*hole_meshes[hole_i]);
124 holes_with_midpoints[hole_i] = meshed_holes.back().n_midpoints();
125 stitch_second_order_holes =
126 xyd_opts.stitch_holes.empty()
127 ? false
128 : ((holes_with_midpoints[hole_i] && xyd_opts.stitch_holes[hole_i]) ||
129 stitch_second_order_holes);
130 if (hole_i < xyd_opts.refine_holes.size())
131 meshed_holes.back().set_refine_boundary_allowed(xyd_opts.refine_holes[hole_i]);
132
133 triangulator_hole_ptrs[hole_i] = &meshed_holes.back();
134 }
135 if (stitch_second_order_holes &&
136 (xyd_opts.tri_elem_type == "TRI3" || xyd_opts.tri_elem_type == "DEFAULT"))
137 mg.paramError(
138 "tri_element_type",
139 "Cannot use first order elements with stitched quadratic element holes. Please try "
140 "to specify a higher-order tri_element_type or reduce the order of the hole inputs.");
141
142 if (!triangulator_hole_ptrs.empty())
143 poly2tri.attach_hole_list(&triangulator_hole_ptrs);
144
145 if (xyd_opts.desired_area_func != "")
146 {
147 // poly2tri will clone this so it's fine going out of scope
149 poly2tri.set_desired_area_function(&area_func);
150 }
151 else if (xyd_opts.use_auto_area_func)
152 {
153 poly2tri.set_auto_area_function(
154 mg.comm(),
157 xyd_opts.auto_area_func_default_size > 0.0 ? xyd_opts.auto_area_func_default_size : 0.0,
159 : -1.0);
160 }
161
162 if (xyd_opts.tri_elem_type == "TRI6")
163 poly2tri.elem_type() = libMesh::ElemType::TRI6;
164 else if (xyd_opts.tri_elem_type == "TRI7")
165 {
166 poly2tri.elem_type() = libMesh::ElemType::TRI7;
167 // Snapping boundary mid-edge nodes onto a curved input boundary leaves the
168 // TRI7 interior node at the stale straight-edge centroid. Ask libMesh to
169 // move it to the curved-mapping centroid; this is also what makes
170 // libMesh's Tri6-based tangling check valid for TRI7.
171 poly2tri.set_fixup_tri7_center_nodes(true);
172 }
173 // Add interior points before triangulating. Only points inside the boundaries
174 // will be meshed.
175 for (const auto & point : xyd_opts.interior_points)
176 mesh->add_point(point);
177
178 poly2tri.triangulate();
179
180 finalizeTriangulation(mg, *mesh, hole_meshes, holes_with_midpoints, xyd_opts);
181
182 return mesh;
183}
const Parallel::Communicator & comm() const
void finalizeTriangulation(MeshGenerator &mg, UnstructuredMesh &mesh, std::vector< std::unique_ptr< MeshBase > > &holes, const std::vector< bool > &holes_with_midpoints, const XYDelaunayOptions &opts)
Performs the subdomain and boundary naming, the boundary id remapping (outer boundary to 0 and hole i...
std::set< std::size_t > outerBoundaryIds(MeshGenerator &mg, MeshBase &boundary_mesh, const XYDelaunayOptions &opts)
Resolves the outer-boundary selection of the options into the set of ids that define it: the ids of '...

Referenced by XYFrontalDelaunayGenerator::buildBackgroundMesh(), BoundaryLayerUtils::buildBoundaryLayerRing(), and XYDelaunayGenerator::generate().