16 #include "libmesh/elem.h" 17 #include "libmesh/enum_to_string.h" 18 #include "libmesh/int_range.h" 19 #include "libmesh/mesh_modification.h" 20 #include "libmesh/mesh_serializer.h" 21 #include "libmesh/mesh_triangle_holes.h" 22 #include "libmesh/parsed_function.h" 23 #include "libmesh/poly2tri_triangulator.h" 24 #include "libmesh/unstructured_mesh.h" 34 MooseEnum algorithm(
"BINARY EXHAUSTIVE",
"BINARY");
35 MooseEnum tri_elem_type(
"TRI3 TRI6 TRI7 DEFAULT",
"DEFAULT");
39 "The input MeshGenerator defining the output outer boundary and required Steiner points.");
40 params.
addParam<std::vector<BoundaryName>>(
41 "input_boundary_names",
"2D-input-mesh boundaries defining the output mesh outer boundary");
42 params.
addParam<std::vector<SubdomainName>>(
43 "input_subdomain_names",
"1D-input-mesh subdomains defining the output mesh outer boundary");
44 params.
addParam<
unsigned int>(
"add_nodes_per_boundary_segment",
46 "How many more nodes to add in each outer boundary segment.");
48 "refine_boundary",
true,
"Whether to allow automatically refining the outer boundary.");
50 params.
addParam<SubdomainName>(
"output_subdomain_name",
51 "Subdomain name to set on new triangles.");
52 params.
addParam<
SubdomainID>(
"output_subdomain_id",
"Subdomain id to set on new triangles.");
56 "Boundary name to set on new outer boundary. Default ID: 0 if no hole meshes are stitched; " 57 "or maximum boundary ID of all the stitched hole meshes + 1.");
58 params.
addParam<std::vector<BoundaryName>>(
60 "Boundary names to set on holes. Default IDs are numbered up from 1 if no hole meshes are " 61 "stitched; or from maximum boundary ID of all the stitched hole meshes + 2.");
66 "Verify holes do not intersect boundary or each other. Asymptotically costly.");
68 params.
addParam<
bool>(
"smooth_triangulation",
70 "Whether to do Laplacian mesh smoothing on the generated triangles.");
71 params.
addParam<std::vector<MeshGeneratorName>>(
72 "holes", std::vector<MeshGeneratorName>(),
"The MeshGenerators that define mesh holes.");
74 "stitch_holes", std::vector<bool>(),
"Whether to stitch to the mesh defining each hole.");
75 params.
addParam<std::vector<bool>>(
"refine_holes",
77 "Whether to allow automatically refining each hole boundary.");
82 "Desired (maximum) triangle area, or 0 to skip uniform refinement");
86 "Desired area as a function of x,y; omit to skip non-uniform refinement");
91 "Control the use of binary search for the nodes of the stitched surfaces.");
93 "tri_element_type", tri_elem_type,
"Type of the triangular elements to be generated.");
95 "verbose_stitching",
false,
"Whether mesh stitching should have verbose output.");
96 params.
addParam<std::vector<Point>>(
"interior_points",
98 "Interior node locations, if no smoothing is used. Any point " 99 "outside the surface will not be meshed.");
100 params.
addParam<std::vector<FileName>>(
101 "interior_point_files", {},
"Text file(s) with the interior points, one per line");
102 params.
addClassDescription(
"Triangulates meshes within boundaries defined by input meshes.");
105 "use_auto_area_func auto_area_func_default_size auto_area_func_default_size_dist",
106 "Automatic triangle meshing area control");
108 "Mandatory mesh interior nodes");
115 _bdy_ptr(getMesh(
"boundary")),
116 _add_nodes_per_boundary_segment(getParam<unsigned
int>(
"add_nodes_per_boundary_segment")),
117 _refine_bdy(getParam<bool>(
"refine_boundary")),
118 _output_subdomain_id(0),
119 _smooth_tri(getParam<bool>(
"smooth_triangulation")),
120 _verify_holes(getParam<bool>(
"verify_holes")),
121 _hole_ptrs(getMeshes(
"holes")),
122 _stitch_holes(getParam<
std::vector<bool>>(
"stitch_holes")),
123 _refine_holes(getParam<
std::vector<bool>>(
"refine_holes")),
124 _desired_area(getParam<
Real>(
"desired_area")),
125 _desired_area_func(getParam<
std::string>(
"desired_area_func")),
127 _tri_elem_type(parameters.
get<
MooseEnum>(
"tri_element_type")),
128 _verbose_stitching(parameters.
get<bool>(
"verbose_stitching")),
129 _interior_points(getParam<
std::vector<Point>>(
"interior_points"))
135 "Only one of the three methods ('desired_area', 'desired_area_func', and " 136 "'_use_auto_area_func') to set element area limit should be used.");
144 "If this parameter is set to false, the following parameters should not be set: " 145 "'auto_area_func_default_size', 'auto_area_func_default_size_dist', " 146 "'auto_area_function_num_points', 'auto_area_function_power'.");
149 paramError(
"stitch_holes",
"Need one stitch_holes entry per hole, if specified.");
153 paramError(
"refine_holes",
"Disable auto refine of any hole boundary to be stitched.");
157 auto & hole_boundaries = getParam<std::vector<BoundaryName>>(
"hole_boundaries");
158 if (hole_boundaries.size() !=
_hole_ptrs.size())
159 paramError(
"hole_boundaries",
"Need one hole_boundaries entry per hole, if specified.");
162 const auto & positions_files = getParam<std::vector<FileName>>(
"interior_point_files");
163 for (
const auto p_file_it :
index_range(positions_files))
165 const std::string positions_file = positions_files[p_file_it];
171 for (
const auto & d : data)
174 bool has_duplicates =
180 paramError(
"interior_points",
"Duplicate points were found in the provided interior points.");
183 std::unique_ptr<MeshBase>
187 std::unique_ptr<UnstructuredMesh>
mesh =
196 std::set<std::size_t> bdy_ids;
202 "input_subdomain_names",
203 "input_boundary_names and input_subdomain_names cannot both specify an outer boundary.");
205 const auto & boundary_names = getParam<std::vector<BoundaryName>>(
"input_boundary_names");
206 for (
const auto &
name : boundary_names)
209 if (bcid == BoundaryInfo::invalid_id)
210 paramError(
"input_boundary_names",
name,
" is not a boundary name in the input mesh");
212 bdy_ids.insert(bcid);
218 const auto & subdomain_names = getParam<std::vector<SubdomainName>>(
"input_subdomain_names");
223 std::set<SubdomainID> subdomains;
224 mesh->subdomain_ids(subdomains);
230 "input_subdomain_names", subdomain_names[i],
" was not found in the boundary mesh");
232 bdy_ids.insert(subdomain_ids[i]);
236 if (!bdy_ids.empty())
237 poly2tri.set_outer_boundary_ids(bdy_ids);
244 poly2tri.minimum_angle() = 0;
247 std::vector<libMesh::TriangulatorInterface::MeshedHole> meshed_holes;
248 std::vector<libMesh::TriangulatorInterface::Hole *> triangulator_hole_ptrs(
_hole_ptrs.size());
249 std::vector<std::unique_ptr<MeshBase>> hole_ptrs(
_hole_ptrs.size());
252 std::vector<bool> holes_with_midpoints(
_hole_ptrs.size());
253 bool stitch_second_order_holes(
false);
259 hole_ptrs[hole_i] = std::move(*
_hole_ptrs[hole_i]);
260 if (!hole_ptrs[hole_i]->is_prepared())
261 hole_ptrs[hole_i]->prepare_for_use();
262 meshed_holes.emplace_back(*hole_ptrs[hole_i]);
263 holes_with_midpoints[hole_i] = meshed_holes.back().n_midpoints();
266 : ((holes_with_midpoints[hole_i] &&
_stitch_holes[hole_i]) ||
267 stitch_second_order_holes);
269 meshed_holes.back().set_refine_boundary_allowed(
_refine_holes[hole_i]);
271 triangulator_hole_ptrs[hole_i] = &meshed_holes.back();
276 "Cannot use first order elements with stitched quadratic element holes. Please try " 277 "to specify a higher-order tri_element_type or reduce the order of the hole inputs.");
279 if (!triangulator_hole_ptrs.empty())
280 poly2tri.attach_hole_list(&triangulator_hole_ptrs);
286 poly2tri.set_desired_area_function(&area_func);
290 poly2tri.set_auto_area_function(
299 poly2tri.elem_type() = libMesh::ElemType::TRI6;
301 poly2tri.elem_type() = libMesh::ElemType::TRI7;
307 poly2tri.triangulate();
314 auto output_subdomain_name = getParam<SubdomainName>(
"output_subdomain_name");
317 if (
id == Elem::invalid_subdomain_id)
325 for (
auto & hole_ptr : hole_ptrs)
329 if (possible_sbdid != Elem::invalid_subdomain_id)
345 "name has been used by the input meshes and the corresponding id is not equal " 346 "to 'output_subdomain_id'");
352 if (output_subdomain_name.size())
357 for (
auto elem :
mesh->element_ptr_range())
359 mooseAssert(elem->type() ==
362 <<
" found in triangulation");
373 auto cross_prod = (elem->point(1) - elem->point(0)).cross(elem->point(2) - elem->point(0));
375 if (cross_prod(2) <= 0)
376 mooseError(
"Inverted element found in triangulation.\n" 377 "Laplacian smoothing can create these at reentrant corners; disable it?");
381 const bool use_binary_search = (
_algorithm ==
"BINARY");
408 hole_ptrs[hole_i]->comm().max(free_boundary_id);
421 *
mesh, 0, (
isParamValid(
"output_boundary") ? end_bcid : 0) + free_boundary_id);
425 auto hole_boundaries = getParam<std::vector<BoundaryName>>(
"hole_boundaries");
430 *
mesh, h + 1 + free_boundary_id, h + 1 + free_boundary_id + end_bcid);
435 *
mesh, h + 1 + free_boundary_id + end_bcid, hole_boundary_ids[h]);
436 mesh->get_boundary_info().sideset_name(hole_boundary_ids[h]) = hole_boundaries[h];
443 const BoundaryName output_boundary = getParam<BoundaryName>(
"output_boundary");
444 const std::vector<BoundaryID> output_boundary_id =
448 *
mesh, end_bcid + free_boundary_id, output_boundary_id[0]);
449 mesh->get_boundary_info().sideset_name(output_boundary_id[0]) = output_boundary;
454 bool doing_stitching =
false;
458 const MeshBase & hole_mesh = *hole_ptrs[hole_i];
459 auto & hole_boundary_info = hole_mesh.get_boundary_info();
460 const std::set<boundary_id_type> & local_hole_bcids = hole_boundary_info.get_boundary_ids();
462 if (!local_hole_bcids.empty())
464 hole_mesh.comm().max(new_hole_bcid);
467 doing_stitching =
true;
477 auto & main_subdomain_map =
mesh->set_subdomain_name_map();
482 UnstructuredMesh & hole_mesh =
dynamic_cast<UnstructuredMesh &
>(*hole_ptrs[hole_i]);
484 if (!holes_with_midpoints[hole_i])
487 hole_mesh.all_second_order();
489 hole_mesh.all_complete_order();
491 auto & hole_boundary_info = hole_mesh.get_boundary_info();
508 std::unordered_map<Point, Point> next_hole_boundary_point;
511 next_hole_boundary_point[mh.point(
pi - 1)] = mh.point(
pi);
512 next_hole_boundary_point[mh.point(np - 1)] = mh.point(0);
515 int found_hole_sides = 0;
517 for (
auto elem : hole_mesh.element_ptr_range())
519 if (elem->dim() != 2)
520 mooseError(
"Non 2-D element found in hole; stitching is not supported.");
522 auto ns = elem->n_sides();
525 auto it_s = next_hole_boundary_point.find(elem->point(s));
526 if (it_s != next_hole_boundary_point.end())
527 if (it_s->second == elem->point((s + 1) % ns))
529 hole_boundary_info.add_side(elem, s, new_hole_bcid);
536 mooseAssert(found_hole_sides == np,
"Failed to find full outer boundary of meshed hole");
538 auto & mesh_boundary_info =
mesh->get_boundary_info();
540 int found_inner_sides = 0;
542 for (
auto elem :
mesh->element_ptr_range())
544 auto ns = elem->n_sides();
547 auto it_s = next_hole_boundary_point.find(elem->point((s + 1) % ns));
548 if (it_s != next_hole_boundary_point.end())
549 if (it_s->second == elem->point(s))
551 mesh_boundary_info.add_side(elem, s, inner_bcid);
558 mooseAssert(found_inner_sides == np,
"Failed to find full boundary around meshed hole");
562 const auto & increment_subdomain_map = hole_mesh.get_subdomain_name_map();
563 main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end());
565 mesh->stitch_meshes(hole_mesh,
575 std::set<SubdomainName> main_subdomain_map_name_list;
576 for (
auto const & id_name_pair : main_subdomain_map)
577 main_subdomain_map_name_list.emplace(id_name_pair.second);
578 if (main_subdomain_map.size() != main_subdomain_map_name_list.size())
579 paramError(
"holes",
"The hole meshes contain subdomain name maps with conflicts.");
581 mesh->set_isnt_prepared();
const std::string _desired_area_func
Desired triangle area as a (fparser-compatible) function of x,y.
const bool _verbose_stitching
Whether mesh stitching should have verbose output.
virtual unsigned int n_points() const override
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 ...
const bool _verify_holes
Whether to verify holes do not intersect boundary or each other.
const std::vector< Point > getDataAsPoints() const
Get the data in Point format.
Base class for Delaunay mesh generators applied to a surface.
T * get(const std::unique_ptr< T > &u)
The MooseUtils::get() specializations are used to support making forwards-compatible code changes fro...
const std::vector< bool > _refine_holes
Whether to allow automatically refining each hole boundary.
registerMooseObject("MooseApp", XYDelaunayGenerator)
std::vector< Point > _interior_points
Desired interior node locations.
const bool _smooth_tri
Whether to do Laplacian mesh smoothing on the generated triangles.
const Parallel::Communicator & comm() const
std::unique_ptr< T_DEST, T_DELETER > dynamic_pointer_cast(std::unique_ptr< T_SRC, T_DELETER > &src)
These are reworked from https://stackoverflow.com/a/11003103.
const Parallel::Communicator & _communicator
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.
static InputParameters validParams()
std::unique_ptr< MeshBase > & _bdy_ptr
Input mesh defining the boundary to triangulate within.
const bool _refine_bdy
Whether to allow automatically refining the outer boundary.
SubdomainID getSubdomainID(const SubdomainName &subdomain_name, const MeshBase &mesh)
Gets the subdomain ID associated with the given SubdomainName.
auto max(const L &left, const R &right)
const SubdomainID INVALID_BLOCK_ID
BoundaryID getBoundaryID(const BoundaryName &boundary_name, const MeshBase &mesh)
Gets the boundary ID associated with the given BoundaryName.
const std::vector< bool > _stitch_holes
Whether to stitch to the mesh defining each hole.
Generates a triangulation in the XY plane, based on an input mesh defining the outer boundary (as wel...
const bool _use_auto_area_func
Whether to use automatic desired area function.
const std::string & name() const
Get the name of the class.
std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
boundary_id_type BoundaryID
void setFormatFlag(FormatFlag value)
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
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.
const unsigned int _auto_area_function_num_points
Maximum number of points to use for the inverse distance interpolation for automatic area function...
void read()
Perform the actual data reading.
XYDelaunayGenerator(const InputParameters ¶meters)
static InputParameters validParams()
const unsigned int _add_nodes_per_boundary_segment
How many more nodes to add in each outer boundary segment.
std::string enum_to_string(const T e)
const Real _auto_area_func_default_size_dist
Background size's effective distance for automatic desired area function.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Utility class for reading delimited data (e.g., CSV data).
const MooseEnum _algorithm
Type of algorithm used to find matching nodes (binary or exhaustive)
const Real _auto_area_function_power
Power of the polynomial used in the inverse distance interpolation for automatic area function...
const std::vector< std::unique_ptr< MeshBase > * > _hole_ptrs
Holds pointers to the pointers to input meshes defining holes.
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...
const MooseEnum _tri_elem_type
Type of triangular elements to be generated.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
const Real _auto_area_func_default_size
Background size for automatic desired area function.
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 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 _output_subdomain_id
What subdomain_id to set on the generated triangles.
bool isParamSetByUser(const std::string &name) const
Test if the supplied parameter is set by a user, as opposed to not set or set to default.
void ErrorVector unsigned int
auto index_range(const T &sizable)
const Real _desired_area
Desired (maximum) triangle area.