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");
88 params.
addParam<
bool>(
"use_auto_area_func",
90 "Use the automatic area function for the triangle meshing region.");
92 "auto_area_func_default_size",
94 "Background size for automatic area function, or 0 to use non background size");
95 params.
addParam<
Real>(
"auto_area_func_default_size_dist",
97 "Effective distance of background size for automatic area " 98 "function, or negative to use non background size");
99 params.
addParam<
unsigned int>(
"auto_area_function_num_points",
101 "Maximum number of nearest points used for the inverse distance " 102 "interpolation algorithm for automatic area function calculation.");
104 "auto_area_function_power",
106 "auto_area_function_power>0",
107 "Polynomial power of the inverse distance interpolation algorithm for automatic area " 108 "function calculation.");
113 "Control the use of binary search for the nodes of the stitched surfaces.");
115 "tri_element_type", tri_elem_type,
"Type of the triangular elements to be generated.");
117 "verbose_stitching",
false,
"Whether mesh stitching should have verbose output.");
118 params.
addParam<std::vector<Point>>(
"interior_points",
120 "Interior node locations, if no smoothing is used. Any point " 121 "outside the surface will not be meshed.");
122 params.
addParam<std::vector<FileName>>(
123 "interior_point_files", {},
"Text file(s) with the interior points, one per line");
124 params.
addClassDescription(
"Triangulates meshes within boundaries defined by input meshes.");
127 "use_auto_area_func auto_area_func_default_size auto_area_func_default_size_dist",
128 "Automatic triangle meshing area control");
130 "Mandatory mesh interior nodes");
137 _bdy_ptr(getMesh(
"boundary")),
138 _add_nodes_per_boundary_segment(getParam<unsigned
int>(
"add_nodes_per_boundary_segment")),
139 _refine_bdy(getParam<bool>(
"refine_boundary")),
140 _output_subdomain_id(0),
141 _smooth_tri(getParam<bool>(
"smooth_triangulation")),
142 _verify_holes(getParam<bool>(
"verify_holes")),
143 _hole_ptrs(getMeshes(
"holes")),
144 _stitch_holes(getParam<
std::vector<bool>>(
"stitch_holes")),
145 _refine_holes(getParam<
std::vector<bool>>(
"refine_holes")),
146 _desired_area(getParam<
Real>(
"desired_area")),
147 _desired_area_func(getParam<
std::string>(
"desired_area_func")),
148 _use_auto_area_func(getParam<bool>(
"use_auto_area_func")),
149 _auto_area_func_default_size(getParam<
Real>(
"auto_area_func_default_size")),
150 _auto_area_func_default_size_dist(getParam<
Real>(
"auto_area_func_default_size_dist")),
151 _auto_area_function_num_points(getParam<unsigned
int>(
"auto_area_function_num_points")),
152 _auto_area_function_power(getParam<
Real>(
"auto_area_function_power")),
154 _tri_elem_type(parameters.
get<
MooseEnum>(
"tri_element_type")),
155 _verbose_stitching(parameters.
get<bool>(
"verbose_stitching")),
156 _interior_points(getParam<
std::vector<Point>>(
"interior_points"))
162 "Only one of the three methods ('desired_area', 'desired_area_func', and " 163 "'_use_auto_area_func') to set element area limit should be used.");
171 "If this parameter is set to false, the following parameters should not be set: " 172 "'auto_area_func_default_size', 'auto_area_func_default_size_dist', " 173 "'auto_area_function_num_points', 'auto_area_function_power'.");
176 paramError(
"stitch_holes",
"Need one stitch_holes entry per hole, if specified.");
180 paramError(
"refine_holes",
"Disable auto refine of any hole boundary to be stitched.");
184 auto & hole_boundaries = getParam<std::vector<BoundaryName>>(
"hole_boundaries");
185 if (hole_boundaries.size() !=
_hole_ptrs.size())
186 paramError(
"hole_boundaries",
"Need one hole_boundaries entry per hole, if specified.");
189 const auto & positions_files = getParam<std::vector<FileName>>(
"interior_point_files");
190 for (
const auto p_file_it :
index_range(positions_files))
192 const std::string positions_file = positions_files[p_file_it];
198 for (
const auto & d : data)
201 bool has_duplicates =
207 paramError(
"interior_points",
"Duplicate points were found in the provided interior points.");
210 std::unique_ptr<MeshBase>
214 std::unique_ptr<UnstructuredMesh>
mesh =
223 std::set<std::size_t> bdy_ids;
229 "input_subdomain_names",
230 "input_boundary_names and input_subdomain_names cannot both specify an outer boundary.");
232 const auto & boundary_names = getParam<std::vector<BoundaryName>>(
"input_boundary_names");
233 for (
const auto &
name : boundary_names)
236 if (bcid == BoundaryInfo::invalid_id)
237 paramError(
"input_boundary_names",
name,
" is not a boundary name in the input mesh");
239 bdy_ids.insert(bcid);
245 const auto & subdomain_names = getParam<std::vector<SubdomainName>>(
"input_subdomain_names");
250 std::set<SubdomainID> subdomains;
251 mesh->subdomain_ids(subdomains);
257 "input_subdomain_names", subdomain_names[i],
" was not found in the boundary mesh");
259 bdy_ids.insert(subdomain_ids[i]);
263 if (!bdy_ids.empty())
264 poly2tri.set_outer_boundary_ids(bdy_ids);
271 poly2tri.minimum_angle() = 0;
274 std::vector<libMesh::TriangulatorInterface::MeshedHole> meshed_holes;
275 std::vector<libMesh::TriangulatorInterface::Hole *> triangulator_hole_ptrs(
_hole_ptrs.size());
276 std::vector<std::unique_ptr<MeshBase>> hole_ptrs(
_hole_ptrs.size());
279 std::vector<bool> holes_with_midpoints(
_hole_ptrs.size());
280 bool stitch_second_order_holes(
false);
286 hole_ptrs[hole_i] = std::move(*
_hole_ptrs[hole_i]);
287 if (!hole_ptrs[hole_i]->is_prepared())
288 hole_ptrs[hole_i]->prepare_for_use();
289 meshed_holes.emplace_back(*hole_ptrs[hole_i]);
290 holes_with_midpoints[hole_i] = meshed_holes.back().n_midpoints();
293 : ((holes_with_midpoints[hole_i] &&
_stitch_holes[hole_i]) ||
294 stitch_second_order_holes);
296 meshed_holes.back().set_refine_boundary_allowed(
_refine_holes[hole_i]);
298 triangulator_hole_ptrs[hole_i] = &meshed_holes.back();
303 "Cannot use first order elements with stitched quadratic element holes. Please try " 304 "to specify a higher-order tri_element_type or reduce the order of the hole inputs.");
306 if (!triangulator_hole_ptrs.empty())
307 poly2tri.attach_hole_list(&triangulator_hole_ptrs);
313 poly2tri.set_desired_area_function(&area_func);
317 poly2tri.set_auto_area_function(
326 poly2tri.elem_type() = libMesh::ElemType::TRI6;
328 poly2tri.elem_type() = libMesh::ElemType::TRI7;
332 mesh->add_point(point);
334 poly2tri.triangulate();
341 auto output_subdomain_name = getParam<SubdomainName>(
"output_subdomain_name");
344 if (
id == Elem::invalid_subdomain_id)
352 for (
auto & hole_ptr : hole_ptrs)
356 if (possible_sbdid != Elem::invalid_subdomain_id)
372 "name has been used by the input meshes and the corresponding id is not equal " 373 "to 'output_subdomain_id'");
379 if (output_subdomain_name.size())
384 for (
auto elem :
mesh->element_ptr_range())
386 mooseAssert(elem->type() ==
389 <<
" found in triangulation");
400 auto cross_prod = (elem->point(1) - elem->point(0)).cross(elem->point(2) - elem->point(0));
402 if (cross_prod(2) <= 0)
403 mooseError(
"Inverted element found in triangulation.\n" 404 "Laplacian smoothing can create these at reentrant corners; disable it?");
408 const bool use_binary_search = (
_algorithm ==
"BINARY");
435 hole_ptrs[hole_i]->comm().max(free_boundary_id);
448 *
mesh, 0, (
isParamValid(
"output_boundary") ? end_bcid : 0) + free_boundary_id);
452 auto hole_boundaries = getParam<std::vector<BoundaryName>>(
"hole_boundaries");
457 *
mesh, h + 1 + free_boundary_id, h + 1 + free_boundary_id + end_bcid);
462 *
mesh, h + 1 + free_boundary_id + end_bcid, hole_boundary_ids[h]);
463 mesh->get_boundary_info().sideset_name(hole_boundary_ids[h]) = hole_boundaries[h];
470 const BoundaryName output_boundary = getParam<BoundaryName>(
"output_boundary");
471 const std::vector<BoundaryID> output_boundary_id =
475 *
mesh, end_bcid + free_boundary_id, output_boundary_id[0]);
476 mesh->get_boundary_info().sideset_name(output_boundary_id[0]) = output_boundary;
481 bool doing_stitching =
false;
485 const MeshBase & hole_mesh = *hole_ptrs[hole_i];
486 auto & hole_boundary_info = hole_mesh.get_boundary_info();
487 const std::set<boundary_id_type> & local_hole_bcids = hole_boundary_info.get_boundary_ids();
489 if (!local_hole_bcids.empty())
491 hole_mesh.comm().max(new_hole_bcid);
494 doing_stitching =
true;
504 auto & main_subdomain_map =
mesh->set_subdomain_name_map();
509 UnstructuredMesh & hole_mesh =
dynamic_cast<UnstructuredMesh &
>(*hole_ptrs[hole_i]);
511 if (!holes_with_midpoints[hole_i])
514 hole_mesh.all_second_order();
516 hole_mesh.all_complete_order();
518 auto & hole_boundary_info = hole_mesh.get_boundary_info();
535 std::unordered_map<Point, Point> next_hole_boundary_point;
538 next_hole_boundary_point[mh.point(
pi - 1)] = mh.point(
pi);
539 next_hole_boundary_point[mh.point(np - 1)] = mh.point(0);
542 int found_hole_sides = 0;
544 for (
auto elem : hole_mesh.element_ptr_range())
546 if (elem->dim() != 2)
547 mooseError(
"Non 2-D element found in hole; stitching is not supported.");
549 auto ns = elem->n_sides();
552 auto it_s = next_hole_boundary_point.find(elem->point(s));
553 if (it_s != next_hole_boundary_point.end())
554 if (it_s->second == elem->point((s + 1) % ns))
556 hole_boundary_info.add_side(elem, s, new_hole_bcid);
563 mooseAssert(found_hole_sides == np,
"Failed to find full outer boundary of meshed hole");
565 auto & mesh_boundary_info =
mesh->get_boundary_info();
567 int found_inner_sides = 0;
569 for (
auto elem :
mesh->element_ptr_range())
571 auto ns = elem->n_sides();
574 auto it_s = next_hole_boundary_point.find(elem->point((s + 1) % ns));
575 if (it_s != next_hole_boundary_point.end())
576 if (it_s->second == elem->point(s))
578 mesh_boundary_info.add_side(elem, s, inner_bcid);
585 mooseAssert(found_inner_sides == np,
"Failed to find full boundary around meshed hole");
589 const auto & increment_subdomain_map = hole_mesh.get_subdomain_name_map();
590 main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end());
592 mesh->stitch_meshes(hole_mesh,
602 std::set<SubdomainName> main_subdomain_map_name_list;
603 for (
auto const & id_name_pair : main_subdomain_map)
604 main_subdomain_map_name_list.emplace(id_name_pair.second);
605 if (main_subdomain_map.size() != main_subdomain_map_name_list.size())
606 paramError(
"holes",
"The hole meshes contain subdomain name maps with conflicts.");
608 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.
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)
const Real _auto_area_func_default_size
Background size for automatic desired area function.
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.
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 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.
void read()
Perform the actual data reading.
static InputParameters validParams()
XYDelaunayGenerator(const InputParameters ¶meters)
const Real _auto_area_function_power
Power of the polynomial used in the inverse distance interpolation for automatic area function...
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 bool _use_auto_area_func
Whether to use 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 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.
const Real _auto_area_func_default_size_dist
Background size's effective distance for automatic desired area function.
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
const unsigned int _auto_area_function_num_points
Maximum number of points to use for the inverse distance interpolation for automatic 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.
MeshGenerators are objects that can modify or add to an existing mesh.
void ErrorVector unsigned int
auto index_range(const T &sizable)
const Real _desired_area
Desired (maximum) triangle area.