15 #include "libmesh/boundary_info.h" 16 #include "libmesh/mesh_triangle_holes.h" 17 #include "libmesh/mesh_modification.h" 18 #include "libmesh/parallel_algebra.h" 19 #include "libmesh/poly2tri_triangulator.h" 20 #include "libmesh/unstructured_mesh.h" 22 #include "libmesh/mesh_base.h" 23 #include "libmesh/replicated_mesh.h" 35 "Mesh generator that re-meshes a 2D surface mesh given as one or more subdomains into " 36 "a 2D surface mesh using Delaunay triangulation.");
39 params.
addRequiredParam<MeshGeneratorName>(
"input",
"The mesh we want to modify");
40 params.
addParam<std::vector<std::vector<SubdomainName>>>(
41 "subdomain_names", {},
"The surface mesh subdomains to be re-meshed");
42 params.
addParam<std::vector<SubdomainName>>(
43 "exclude_subdomain_names", {},
"The surface mesh subdomains that should not be re-meshed");
49 "Maximum length of an edge in the 1D meshes around each subdomain. Only a single value is " 50 "currently allowed as the boundary refinement must be consistent between all subdomains.");
54 params.
addParam<std::vector<unsigned int>>(
55 "interpolate_boundaries",
57 "Ratio of points to add to the boundaries. Can be set to a single value for all groups at " 58 "once, or specified individually. A single value is recommended as the boundary refinement " 59 "must be consistent between all subdomains.");
63 "Target element size when triangulating projection of the subdomain group. Can be set to a " 64 "single value for all groups at once, or specified individually. Default of 0 means no " 67 "Delaunay triangulation");
70 params.
addParam<
bool>(
"avoid_merging_subdomains",
72 "Whether to prevent merging subdomains by offsetting ids. The first mesh " 73 "in the input will keep the same subdomains ids, the others will have " 74 "offsets. All subdomain names will remain valid");
75 params.
addParam<
bool>(
"clear_stitching_boundaries",
77 "Whether to clear the boundaries between the subdomains being stitched " 78 "together after they were re-meshed with triangles");
79 MooseEnum algorithm(
"BINARY EXHAUSTIVE",
"BINARY");
81 "stitching_algorithm",
83 "Control the use of binary search for the nodes of the stitched surfaces.");
85 "verbose_stitching",
false,
"Whether mesh stitching should have verbose output.");
87 "avoid_merging_subdomains clear_stitching_boundaries stitching_algorithm verbose_stitching",
88 "Stitching triangularized meshes");
97 _input(getMesh(
"input")),
98 _subdomain_names(getParam<
std::vector<
std::vector<SubdomainName>>>(
"subdomain_names")),
99 _interpolate_boundaries(getParam<
std::vector<unsigned
int>>(
"interpolate_boundaries")),
100 _desired_areas(getParam<
std::vector<
Real>>(
"desired_areas"))
104 "Excluding subdomain names is only to be set when 'subdomain_names' is not set");
107 std::unique_ptr<MeshBase>
110 std::unique_ptr<MeshBase> mesh_3d = std::move(
_input);
120 std::set<subdomain_id_type> sub_ids;
121 mesh_3d->subdomain_ids(sub_ids);
124 const auto & excluded = getParam<std::vector<SubdomainName>>(
"exclude_subdomain_names");
125 for (
const auto & sub_id : sub_ids)
127 const auto & sn = mesh_3d->subdomain_name(sub_id) ==
"" ? std::to_string(sub_id)
128 : mesh_3d->subdomain_name(sub_id);
129 if (excluded.empty() ||
std::find(excluded.begin(), excluded.end(), sn) == excluded.end())
140 "Should be the same size as 'subdomain_names' or of size 1");
142 paramError(
"desired_areas",
"Should be the same size as 'subdomain_names' or of size 1");
145 std::vector<std::unique_ptr<UnstructuredMesh>> remeshed_2d;
154 std::vector<std::unique_ptr<UnstructuredMesh>> no_holes = {};
158 remeshed_2d.push_back(std::move(new_mesh));
162 const auto primary_bcid = 1;
166 auto paired_bcid = starting_stitching_id;
167 const auto verbose_stitching = getParam<bool>(
"verbose_stitching");
168 const auto use_binary_search = getParam<MooseEnum>(
"stitching_algorithm") ==
"BINARY";
171 std::unique_ptr<UnstructuredMesh> full_mesh;
172 full_mesh = std::move(remeshed_2d[0]);
175 auto & main_subdomain_map = full_mesh->set_subdomain_name_map();
182 UnstructuredMesh & remeshed =
dynamic_cast<UnstructuredMesh &
>(*remeshed_2d[remesh_i]);
191 bool rem_has_external_bdy =
false;
193 mooseAssert(rem_has_external_bdy,
"Subdomain mesh should have an external boundary");
197 bool base_has_external_bdy =
false;
199 if (!base_has_external_bdy)
201 "Full mesh should have an external boundary. This could occur if the surface mesh is " 202 "disconnected in several parts and a part's surface mesh has just been fully remeshed");
206 const auto & increment_subdomain_map = remeshed.get_subdomain_name_map();
207 main_subdomain_map.insert(increment_subdomain_map.begin(), increment_subdomain_map.end());
209 full_mesh->stitch_meshes(remeshed,
213 getParam<bool>(
"clear_stitching_boundaries"),
218 !getParam<bool>(
"avoid_merging_subdomains"));
228 if (getParam<bool>(
"clear_stitching_boundaries"))
229 for (
const auto bcid :
make_range(starting_stitching_id, paired_bcid))
230 full_mesh->get_boundary_info().remove_id(bcid);
232 full_mesh->unset_is_prepared();
237 std::unique_ptr<UnstructuredMesh>
239 UnstructuredMesh & mesh_2d,
240 std::vector<std::unique_ptr<UnstructuredMesh>> & hole_meshes_2d,
241 unsigned int group_i)
245 if (!mesh_2d.preparation().has_cached_elem_data)
246 mesh_2d.cache_elem_data();
247 _console <<
"Re-meshing mesh\n " << mesh_2d << std::endl;
250 if (hole_meshes_2d.size())
251 _console <<
"With " << hole_meshes_2d.size() <<
" holes:" << std::endl;
252 for (
const auto & hole_m : hole_meshes_2d)
254 if (!hole_m->preparation().has_cached_elem_data)
255 hole_m->cache_elem_data();
265 for (
const auto & node : mesh_2d.node_ptr_range())
270 "The level set function does not match the nodes in the given boundary of the " 271 "input mesh. Level set evaluates at: " +
280 mesh_2d.prepare_for_use();
281 bool has_external_side =
false;
282 MeshTools::Modification::all_tri(mesh_2d);
284 for (
const auto elem : mesh_2d.active_element_ptr_range())
285 for (
const auto i_side : elem->side_index_range())
286 if (elem->neighbor_ptr(i_side) ==
nullptr)
288 mesh_2d.get_boundary_info().add_side(elem, i_side, mesh_2d_ext_bdry);
289 has_external_side =
true;
293 auto mesh_2d_dummy = mesh_2d.clone();
297 if (has_external_side)
299 {std::to_string(mesh_2d_ext_bdry)},
306 if (has_external_side)
308 mesh_2d_dummy->clear();
319 mesh_2d.get_boundary_info().build_node_list_from_side_list({mesh_2d_ext_bdry});
322 std::vector<Point> mesh_1d_points;
323 mesh_1d_points.reserve(boundary_1d->n_nodes());
324 for (
const auto & node : boundary_1d->node_ptr_range())
325 mesh_1d_points.push_back(*node);
331 *mesh_1d, mesh_1d_points,
true,
"",
"", getParam<Real>(
"max_edge_length") + 1e-8);
341 "The normal vector of some elements in the 2D mesh deviates too much from the " 342 "global average normal vector. The maximum deviation found / allowed is " +
345 ". Consider dividing the boundary into several parts to " 346 "reduce the angle deviation.");
349 MeshTools::Modification::translate(*mesh_1d, -centroid(0), -centroid(1), -centroid(2));
350 MeshTools::Modification::translate(mesh_2d, -centroid(0), -centroid(1), -centroid(2));
354 const Real theta = std::acos(mesh_norm(2)) / M_PI * 180.0;
356 (MooseUtils::absoluteFuzzyLessThan(mesh_norm(2), 1.0) ? std::atan2(mesh_norm(1), mesh_norm(0))
359 MeshTools::Modification::rotate(*mesh_1d, 90.0 - phi, theta, 0.0);
360 MeshTools::Modification::rotate(mesh_2d, 90.0 - phi, theta, 0.0);
366 for (
const auto & node : mesh_2d.node_ptr_range())
369 for (
const auto & node : mesh_1d->node_ptr_range())
373 std::unique_ptr<UnstructuredMesh>
mesh =
376 Poly2TriTriangulator poly2tri(*
mesh);
377 poly2tri.triangulation_type() = TriangulatorInterface::PSLG;
380 poly2tri.set_verify_hole_boundaries(
false);
382 poly2tri.minimum_angle() = 0;
383 poly2tri.smooth_after_generating() =
false;
386 poly2tri.set_auto_area_function(this->
comm(),
391 poly2tri.triangulate();
394 for (
const auto & node :
mesh->node_ptr_range())
396 bool node_mod =
false;
398 for (
const auto & elem : mesh_2d.active_element_ptr_range())
400 if (elem->contains_point(Point((*node)(0), (*node)(1), 0.0)))
403 const auto elem_id = elem->id();
405 const Elem & elem_xyz = *mesh_2d_xyz->elem_ptr(elem_id);
407 const Point elem_normal =
elemNormal(elem_xyz);
408 const auto & elem_p = mesh_2d_xyz->elem_ptr(elem_id)->point(0);
412 if (MooseUtils::absoluteFuzzyEqual((*node)(0), elem_p(0)) &&
413 MooseUtils::absoluteFuzzyEqual((*node)(1), elem_p(1)))
415 (*node)(2) = elem_p(2);
421 (*node)(2) = elem_p(2) - (((*node)(0) - elem_p(0)) * elem_normal(0) +
422 ((*node)(1) - elem_p(1)) * elem_normal(1)) /
433 MeshTools::Modification::rotate(*
mesh, 0.0, -theta, phi - 90.0);
435 MeshTools::Modification::translate(*
mesh, centroid(0), centroid(1), centroid(2));
440 for (
const auto & node :
mesh->node_ptr_range())
442 unsigned int iter_ct = 0;
452 const auto common_id = mesh_2d.get_mesh_subdomains().begin();
453 for (
auto & elem :
mesh->active_element_ptr_range())
454 elem->subdomain_id() = *common_id;
457 mesh->set_subdomain_name_map() = mesh_2d.get_subdomain_name_map();
459 mesh->get_boundary_info().remove_id(mesh_2d_ext_bdry);
461 mesh->unset_is_prepared();
void buildPolyLineMesh(MeshBase &mesh, const std::vector< Point > &points, const bool loop, const BoundaryName &start_boundary, const BoundaryName &end_boundary, const std::vector< unsigned int > &nums_edges_between_points)
Generates meshes from edges connecting a list of points.
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.
Real meshNormalDeviation2D(const MeshBase &mesh, const Point &global_norm)
Calculate the maximum deviation of the normal vectors in a given mesh from a global average normal ve...
std::unique_ptr< ReplicatedMesh > buildReplicatedMesh(unsigned int dim=libMesh::invalid_uint)
Build a replicated 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 ...
void convertBlockToMesh(MeshBase &source_mesh, MeshBase &target_mesh, const std::vector< SubdomainName > &target_blocks)
Convert a list of blocks in a given mesh to a standalone new mesh.
static constexpr Real TOLERANCE
Base class for Delaunay mesh generators applied to a surface.
virtual std::unique_ptr< MeshBase > generate() override
Generate / modify the mesh.
std::unique_ptr< UnstructuredMesh > General2DDelaunay(UnstructuredMesh &mesh_2d, std::vector< std::unique_ptr< UnstructuredMesh >> &hole_meshes_2d, unsigned int group_i)
Generate a 2D mesh using Delaunay triangulation based on the input 2D surface mesh and the 2D hole me...
static InputParameters validParams()
const Parallel::Communicator & comm() const
Point meshNormal2D(const MeshBase &mesh)
Calculate the average normal vector of a 2D mesh based on the normal vectors of its elements using th...
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.
Helper class to define, parameterize and create a level set function used in meshing, often to correct the position of nodes on a surface.
unsigned int _num_groups
Number of groups of subdomains, also number of times we will call the Delaunay generator.
std::unique_ptr< ReplicatedMesh > buildLoopBoundaryOf2DMesh(const MeshBase &input_mesh, const boundary_id_type boundary_id)
Build a loop mesh of edges from the contiguous 2D boundary of 2D input mesh Note: The lower-dimension...
static InputParameters validParams()
static InputParameters validParams()
void mooseWarning(Args &&... args) const
const bool _use_auto_area_func
Whether to use automatic desired area function.
registerMooseObject("MooseApp", SurfaceSubdomainsDelaunayRemesher)
const bool _verbose
Whether the generator should be verbose.
std::vector< Real > _desired_areas
Target areas for the triangulation for each group of surface subdomains.
const std::string & type() const
Get the type of this class.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
const unsigned int _auto_area_function_num_points
Maximum number of points to use for the inverse distance interpolation for automatic area function...
std::string stringify(const T &t)
conversion to string
SymFunctionPtr _func_level_set
function parser object describing the level set
void levelSetCorrection(Node &node)
Correct the position of a node based on the level set function.
SurfaceSubdomainsDelaunayRemesher(const InputParameters ¶meters)
Real levelSetEvaluator(const Point &point)
Evaluate the level set function at a given point.
const Real _auto_area_func_default_size_dist
Background size's effective distance for automatic desired area function.
std::vector< std::vector< SubdomainName > > _subdomain_names
The subdomains to be converted to a 2D mesh.
void createSubdomainFromSidesets(MeshBase &mesh, std::vector< BoundaryName > boundary_names, const SubdomainID new_subdomain_id, const SubdomainName new_subdomain_name, const std::string type_name)
Create a new subdomain by generating new side elements from a list of sidesets in a given mesh...
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const Real _max_angle_deviation
Max angle deviation from the global average normal vector in the input mesh.
std::vector< unsigned int > _interpolate_boundaries
Number of points added to boundaries around each group of surface subdomains.
const Real _auto_area_function_power
Power of the polynomial used in the inverse distance interpolation for automatic area function...
const unsigned int _max_level_set_correction_iterations
Maximum number of iterations to correct the nodes based on the level set function.
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...
bool isParamValid(const std::string &name) const
Test if the supplied parameter is valid.
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
Point meshCentroidCalculator(const MeshBase &mesh)
Calculates the centroid of a MeshBase.
std::unique_ptr< MeshBase > buildMeshBaseObject(unsigned int dim=libMesh::invalid_uint)
Build a MeshBase object whose underlying type will be determined by the Mesh input file block...
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...
std::unique_ptr< MeshBase > & _input
The input mesh name.
Class to remesh surface subdomains using a triangle mesh inside the subdomain boundaries.
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)
Point elemNormal(const Elem &elem)
Calculate the normal vector of a 2D element based the first three vertices.
void addExternalBoundary(MeshBase &mesh, const BoundaryID extern_bid, bool &has_external_bid)
Adds a sideset for the external boundary of the mesh (e.g.